diff --git a/frontend/src/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.ts b/frontend/src/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.ts
index 5c9fd04ff..98bbb7e6b 100644
--- a/frontend/src/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.ts
+++ b/frontend/src/app/features/schemas/pages/schema/rules/schema-field-rules-form.component.ts
@@ -5,15 +5,16 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { Component, Input, OnChanges } from '@angular/core';
-import { ConfigureFieldRulesForm, FIELD_RULE_ACTIONS, SchemaDto, SchemasState } from '@app/shared';
+import { Component, Input, OnInit } from '@angular/core';
+import { EMPTY, Observable, shareReplay } from 'rxjs';
+import { ConfigureFieldRulesForm, FIELD_RULE_ACTIONS, SchemaCompletions, SchemaDto, SchemasService, SchemasState } from '@app/shared';
@Component({
selector: 'sqx-schema-field-rules-form',
styleUrls: ['./schema-field-rules-form.component.scss'],
templateUrl: './schema-field-rules-form.component.html',
})
-export class SchemaFieldRulesFormComponent implements OnChanges {
+export class SchemaFieldRulesFormComponent implements OnInit {
@Input()
public schema!: SchemaDto;
@@ -21,14 +22,20 @@ export class SchemaFieldRulesFormComponent implements OnChanges {
public fieldNames!: ReadonlyArray
;
public fieldActions = FIELD_RULE_ACTIONS;
+ public fieldCompletions: Observable = EMPTY;
public isEditable = false;
constructor(
private readonly schemasState: SchemasState,
+ private readonly schemasService: SchemasService,
) {
}
+ public ngOnInit() {
+ this.fieldCompletions = this.schemasService.getFieldRulesCompletion(this.schemasState.appName, this.schema.name).pipe(shareReplay(1));
+ }
+
public ngOnChanges() {
const fieldNames: string[] = [];
diff --git a/frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.ts b/frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.ts
index 4760773a8..d86d3a627 100644
--- a/frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.ts
+++ b/frontend/src/app/features/schemas/pages/schema/scripts/schema-scripts-form.component.ts
@@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { Component, Input, OnChanges } from '@angular/core';
+import { Component, Input } from '@angular/core';
import { EMPTY, Observable, shareReplay } from 'rxjs';
import { AppsState, EditSchemaScriptsForm, SchemaCompletions, SchemaDto, SchemasService, SchemasState } from '@app/shared';
@@ -14,7 +14,7 @@ import { AppsState, EditSchemaScriptsForm, SchemaCompletions, SchemaDto, Schemas
styleUrls: ['./schema-scripts-form.component.scss'],
templateUrl: './schema-scripts-form.component.html',
})
-export class SchemaScriptsFormComponent implements OnChanges {
+export class SchemaScriptsFormComponent {
@Input()
public schema!: SchemaDto;
@@ -38,7 +38,7 @@ export class SchemaScriptsFormComponent implements OnChanges {
this.editForm.load(this.schema.scripts);
this.editForm.setEnabled(this.isEditable);
- this.schemaCompletions = this.schemasService.getCompletions(this.appsState.appName, this.schema.name).pipe(shareReplay(1));
+ this.schemaCompletions = this.schemasService.getContentScriptsCompletion(this.appsState.appName, this.schema.name).pipe(shareReplay(1));
}
public selectField(field: string) {
diff --git a/frontend/src/app/features/schemas/pages/schema/ui/field-list.component.ts b/frontend/src/app/features/schemas/pages/schema/ui/field-list.component.ts
index d0799f528..594f00027 100644
--- a/frontend/src/app/features/schemas/pages/schema/ui/field-list.component.ts
+++ b/frontend/src/app/features/schemas/pages/schema/ui/field-list.component.ts
@@ -6,7 +6,7 @@
*/
import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
-import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { META_FIELDS, SchemaDto, TableField } from '@app/shared';
const META_FIELD_NAMES = Object.values(META_FIELDS).filter(x => x !== META_FIELDS.empty);
@@ -17,7 +17,7 @@ const META_FIELD_NAMES = Object.values(META_FIELDS).filter(x => x !== META_FIELD
templateUrl: './field-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class FieldListComponent implements OnChanges {
+export class FieldListComponent {
@Input()
public emptyText = '';
diff --git a/frontend/src/app/features/schemas/pages/schema/ui/schema-ui-form.component.ts b/frontend/src/app/features/schemas/pages/schema/ui/schema-ui-form.component.ts
index 1dc7ef4c0..f352bfaf4 100644
--- a/frontend/src/app/features/schemas/pages/schema/ui/schema-ui-form.component.ts
+++ b/frontend/src/app/features/schemas/pages/schema/ui/schema-ui-form.component.ts
@@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { Component, Input, OnChanges } from '@angular/core';
+import { Component, Input } from '@angular/core';
import { SchemaDto, SchemasState } from '@app/shared';
@Component({
@@ -13,7 +13,7 @@ import { SchemaDto, SchemasState } from '@app/shared';
styleUrls: ['./schema-ui-form.component.scss'],
templateUrl: './schema-ui-form.component.html',
})
-export class SchemaUIFormComponent implements OnChanges {
+export class SchemaUIFormComponent {
@Input()
public schema!: SchemaDto;
diff --git a/frontend/src/app/features/settings/pages/backups/backup.component.ts b/frontend/src/app/features/settings/pages/backups/backup.component.ts
index d2e509e72..d9188cbe0 100644
--- a/frontend/src/app/features/settings/pages/backups/backup.component.ts
+++ b/frontend/src/app/features/settings/pages/backups/backup.component.ts
@@ -5,8 +5,8 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
-import { ApiUrlConfig, BackupDto, BackupsState, Duration } from '@app/shared';
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { ApiUrlConfig, BackupDto, BackupsState, Duration, TypedSimpleChanges } from '@app/shared';
@Component({
selector: 'sqx-backup[backup]',
@@ -14,7 +14,7 @@ import { ApiUrlConfig, BackupDto, BackupsState, Duration } from '@app/shared';
templateUrl: './backup.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class BackupComponent implements OnChanges {
+export class BackupComponent {
@Input()
public backup!: BackupDto;
@@ -26,8 +26,8 @@ export class BackupComponent implements OnChanges {
) {
}
- public ngOnChanges(changes: SimpleChanges) {
- if (changes['backup']) {
+ public ngOnChanges(changes: TypedSimpleChanges) {
+ if (changes.backup) {
this.duration = Duration.create(this.backup.started, this.backup.stopped!).toString();
}
}
diff --git a/frontend/src/app/features/settings/pages/clients/client.component.ts b/frontend/src/app/features/settings/pages/clients/client.component.ts
index 7b230f85b..435c6be94 100644
--- a/frontend/src/app/features/settings/pages/clients/client.component.ts
+++ b/frontend/src/app/features/settings/pages/clients/client.component.ts
@@ -5,8 +5,8 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
-import { AppsState, ClientDto, ClientsState, DialogModel, RoleDto } from '@app/shared';
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { AppsState, ClientDto, ClientsState, DialogModel, RoleDto, TypedSimpleChanges } from '@app/shared';
@Component({
selector: 'sqx-client[client][clientRoles]',
@@ -14,7 +14,7 @@ import { AppsState, ClientDto, ClientsState, DialogModel, RoleDto } from '@app/s
templateUrl: './client.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class ClientComponent implements OnChanges {
+export class ClientComponent {
@Input()
public client!: ClientDto;
@@ -31,8 +31,8 @@ export class ClientComponent implements OnChanges {
) {
}
- public ngOnChanges(changes: SimpleChanges) {
- if (changes['client']) {
+ public ngOnChanges(changes: TypedSimpleChanges) {
+ if (changes.client) {
this.apiCallsLimit = this.client.apiCallsLimit;
}
}
diff --git a/frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.ts b/frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.ts
index 668de7a99..facbff706 100644
--- a/frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.ts
+++ b/frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.ts
@@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { Component, Injectable, Input, OnChanges } from '@angular/core';
+import { Component, Injectable, Input } from '@angular/core';
import { Observable, of } from 'rxjs';
import { withLatestFrom } from 'rxjs/operators';
import { AssignContributorForm, AutocompleteSource, ContributorsState, DialogModel, DialogService, RoleDto, UsersService } from '@app/shared';
@@ -45,7 +45,7 @@ export class UsersDataSource implements AutocompleteSource {
UsersDataSource,
],
})
-export class ContributorAddFormComponent implements OnChanges {
+export class ContributorAddFormComponent {
private defaultValue: any;
@Input()
diff --git a/frontend/src/app/features/settings/pages/languages/language.component.ts b/frontend/src/app/features/settings/pages/languages/language.component.ts
index 36436eaca..a4507b2d6 100644
--- a/frontend/src/app/features/settings/pages/languages/language.component.ts
+++ b/frontend/src/app/features/settings/pages/languages/language.component.ts
@@ -6,7 +6,7 @@
*/
import { CdkDragDrop } from '@angular/cdk/drag-drop';
-import { Component, Input, OnChanges } from '@angular/core';
+import { Component, Input } from '@angular/core';
import { AppLanguageDto, EditLanguageForm, LanguageDto, LanguagesState, sorted } from '@app/shared';
@Component({
@@ -14,7 +14,7 @@ import { AppLanguageDto, EditLanguageForm, LanguageDto, LanguagesState, sorted }
styleUrls: ['./language.component.scss'],
templateUrl: './language.component.html',
})
-export class LanguageComponent implements OnChanges {
+export class LanguageComponent {
@Input()
public language!: AppLanguageDto;
diff --git a/frontend/src/app/features/settings/pages/roles/role.component.ts b/frontend/src/app/features/settings/pages/roles/role.component.ts
index d6652d212..cbcd14d82 100644
--- a/frontend/src/app/features/settings/pages/roles/role.component.ts
+++ b/frontend/src/app/features/settings/pages/roles/role.component.ts
@@ -5,8 +5,8 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
-import { AutocompleteComponent, AutocompleteSource, EditRoleForm, RoleDto, RolesState, SchemaDto, Settings } from '@app/shared';
+import { ChangeDetectionStrategy, Component, Input, ViewChild } from '@angular/core';
+import { AutocompleteComponent, AutocompleteSource, EditRoleForm, RoleDto, RolesState, SchemaDto, Settings, TypedSimpleChanges } from '@app/shared';
const DESCRIPTIONS = {
Developer: 'i18n:roles.defaults.developer',
@@ -37,7 +37,7 @@ const SIMPLE_PROPERTIES: ReadonlyArray = [{
templateUrl: './role.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class RoleComponent implements OnChanges {
+export class RoleComponent {
@Input()
public role!: RoleDto;
@@ -70,8 +70,8 @@ export class RoleComponent implements OnChanges {
) {
}
- public ngOnChanges(changes: SimpleChanges) {
- if (changes['role']) {
+ public ngOnChanges(changes: TypedSimpleChanges) {
+ if (changes.role) {
this.isEditable = this.role.canUpdate;
this.properties = this.role.properties;
diff --git a/frontend/src/app/features/settings/pages/templates/template.component.ts b/frontend/src/app/features/settings/pages/templates/template.component.ts
index 46d4d0600..b3d0c06b3 100644
--- a/frontend/src/app/features/settings/pages/templates/template.component.ts
+++ b/frontend/src/app/features/settings/pages/templates/template.component.ts
@@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { map, Observable, shareReplay } from 'rxjs';
import { AppsState, ClientsState, TemplateDetailsDto, TemplateDto, TemplatesService } from '@app/shared';
@@ -15,7 +15,7 @@ import { AppsState, ClientsState, TemplateDetailsDto, TemplateDto, TemplatesServ
templateUrl: './template.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class TemplateComponent implements OnChanges {
+export class TemplateComponent {
@Input()
public template!: TemplateDto;
diff --git a/frontend/src/app/features/settings/pages/workflows/workflow-diagram.component.ts b/frontend/src/app/features/settings/pages/workflows/workflow-diagram.component.ts
index 57a7e4964..c7e387569 100644
--- a/frontend/src/app/features/settings/pages/workflows/workflow-diagram.component.ts
+++ b/frontend/src/app/features/settings/pages/workflows/workflow-diagram.component.ts
@@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnDestroy, ViewChild } from '@angular/core';
+import { AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core';
import { ResourceLoaderService, WorkflowDto } from '@app/shared';
declare const vis: any;
@@ -15,7 +15,7 @@ declare const vis: any;
styleUrls: ['./workflow-diagram.component.scss'],
templateUrl: './workflow-diagram.component.html',
})
-export class WorkflowDiagramComponent implements AfterViewInit, OnDestroy, OnChanges {
+export class WorkflowDiagramComponent implements AfterViewInit, OnDestroy {
private network: any;
@ViewChild('chartContainer', { static: false })
diff --git a/frontend/src/app/features/settings/pages/workflows/workflow-step.component.ts b/frontend/src/app/features/settings/pages/workflows/workflow-step.component.ts
index 15d83ce67..9ff9a3f16 100644
--- a/frontend/src/app/features/settings/pages/workflows/workflow-step.component.ts
+++ b/frontend/src/app/features/settings/pages/workflows/workflow-step.component.ts
@@ -5,15 +5,15 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
-import { WorkflowDto, WorkflowStep, WorkflowStepValues, WorkflowTransition, WorkflowTransitionValues, WorkflowTransitionView } from '@app/shared';
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { TypedSimpleChanges, WorkflowDto, WorkflowStep, WorkflowStepValues, WorkflowTransition, WorkflowTransitionValues, WorkflowTransitionView } from '@app/shared';
@Component({
selector: 'sqx-workflow-step[roles][step][workflow]',
styleUrls: ['./workflow-step.component.scss'],
templateUrl: './workflow-step.component.html',
})
-export class WorkflowStepComponent implements OnChanges {
+export class WorkflowStepComponent {
public readonly onBlur: { updateOn: 'blur' } = { updateOn: 'blur' };
@Output()
@@ -54,8 +54,8 @@ export class WorkflowStepComponent implements OnChanges {
public transitions!: ReadonlyArray;
- public ngOnChanges(changes: SimpleChanges) {
- if (changes['workflow'] || changes['step']) {
+ public ngOnChanges(changes: TypedSimpleChanges) {
+ if (changes.workflow || changes.step) {
this.openSteps = this.workflow.getOpenSteps(this.step);
this.openStep = this.openSteps[0];
diff --git a/frontend/src/app/features/settings/pages/workflows/workflow.component.ts b/frontend/src/app/features/settings/pages/workflows/workflow.component.ts
index f07e27b9f..a763c580a 100644
--- a/frontend/src/app/features/settings/pages/workflows/workflow.component.ts
+++ b/frontend/src/app/features/settings/pages/workflows/workflow.component.ts
@@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { Component, Input, OnChanges } from '@angular/core';
+import { Component, Input } from '@angular/core';
import { ErrorDto, MathHelper, SchemaTagSource, WorkflowDto, WorkflowsState, WorkflowStep, WorkflowStepValues, WorkflowTransition, WorkflowTransitionValues } from '@app/shared';
@Component({
@@ -13,7 +13,7 @@ import { ErrorDto, MathHelper, SchemaTagSource, WorkflowDto, WorkflowsState, Wor
styleUrls: ['./workflow.component.scss'],
templateUrl: './workflow.component.html',
})
-export class WorkflowComponent implements OnChanges {
+export class WorkflowComponent {
public readonly onBlur: { updateOn: 'blur' } = { updateOn: 'blur' };
@Input()
diff --git a/frontend/src/app/framework/angular/avatar.component.ts b/frontend/src/app/framework/angular/avatar.component.ts
index a0e132841..a54cd30db 100644
--- a/frontend/src/app/framework/angular/avatar.component.ts
+++ b/frontend/src/app/framework/angular/avatar.component.ts
@@ -5,8 +5,9 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { picasso } from '@app/framework/internal';
+import { TypedSimpleChanges } from './helpers';
@Component({
selector: 'sqx-avatar',
@@ -14,7 +15,7 @@ import { picasso } from '@app/framework/internal';
templateUrl: './avatar.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class AvatarComponent implements OnChanges {
+export class AvatarComponent {
@Input()
public identifier: string | undefined | null;
@@ -29,16 +30,16 @@ export class AvatarComponent implements OnChanges {
public actualImage: string | undefined | null;
- public ngOnChanges(changes: SimpleChanges) {
- if (changes['image']) {
+ public ngOnChanges(changes: TypedSimpleChanges) {
+ if (changes.image) {
this.actualImage = this.image;
}
- if (changes['image'] || changes['identifier']) {
+ if (changes.image || changes.identifier) {
this.updateSource();
}
- if (changes['size']) {
+ if (changes.size) {
this.imageSize = `${this.size}px`;
}
}
diff --git a/frontend/src/app/framework/angular/forms/control-errors.component.ts b/frontend/src/app/framework/angular/forms/control-errors.component.ts
index 34aa7bbbd..6106bf244 100644
--- a/frontend/src/app/framework/angular/forms/control-errors.component.ts
+++ b/frontend/src/app/framework/angular/forms/control-errors.component.ts
@@ -5,7 +5,7 @@
* Copyright (c) Sebastian Stehle. All rights r vbeserved
*/
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Host, Input, OnChanges, OnDestroy, Optional } from '@angular/core';
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Host, Input, OnDestroy, Optional } from '@angular/core';
import { AbstractControl, FormGroupDirective, UntypedFormArray } from '@angular/forms';
import { merge } from 'rxjs';
import { LocalizerService, StatefulComponent, Types } from '@app/framework/internal';
@@ -23,7 +23,7 @@ interface State {
templateUrl: './control-errors.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class ControlErrorsComponent extends StatefulComponent implements OnChanges, OnDestroy {
+export class ControlErrorsComponent extends StatefulComponent implements OnDestroy {
private controlDisplayName = '';
private control: AbstractControl | null = null;
diff --git a/frontend/src/app/framework/angular/forms/editors/checkbox-group.component.ts b/frontend/src/app/framework/angular/forms/editors/checkbox-group.component.ts
index 4cd859cf5..d14fc372b 100644
--- a/frontend/src/app/framework/angular/forms/editors/checkbox-group.component.ts
+++ b/frontend/src/app/framework/angular/forms/editors/checkbox-group.component.ts
@@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnChanges, ViewChild } from '@angular/core';
+import { AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { getTagValues, MathHelper, StatefulControlComponent, TagValue, TextMeasurer, Types } from '@app/framework/internal';
@@ -30,7 +30,7 @@ interface State {
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class CheckboxGroupComponent extends StatefulControlComponent implements AfterViewInit, AfterViewChecked, OnChanges {
+export class CheckboxGroupComponent extends StatefulControlComponent implements AfterViewInit, AfterViewChecked {
private readonly textMeasurer: TextMeasurer;
private childrenWidth = 0;
private checkedValuesRaw: any;
diff --git a/frontend/src/app/framework/angular/forms/editors/code-editor.component.html b/frontend/src/app/framework/angular/forms/editors/code-editor.component.html
index 57212f4d4..89365df41 100644
--- a/frontend/src/app/framework/angular/forms/editors/code-editor.component.html
+++ b/frontend/src/app/framework/angular/forms/editors/code-editor.component.html
@@ -1 +1,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/frontend/src/app/framework/angular/forms/editors/code-editor.component.scss b/frontend/src/app/framework/angular/forms/editors/code-editor.component.scss
index a8b8e1941..2f40df19f 100644
--- a/frontend/src/app/framework/angular/forms/editors/code-editor.component.scss
+++ b/frontend/src/app/framework/angular/forms/editors/code-editor.component.scss
@@ -32,4 +32,27 @@
.ace_gutter {
background: $color-border !important;
}
+
+ .ace_scroller {
+ box-shadow: none !important;
+ }
+
+ .form-control {
+ display: flex;
+ flex-direction: row;
+ flex-grow: 1;
+ align-items: center;
+
+ .editor {
+ flex-grow: 1;
+ }
+
+ span {
+ height: 1.5rem;
+ }
+
+ .ace_hidden-cursors {
+ display: none;
+ }
+ }
}
\ No newline at end of file
diff --git a/frontend/src/app/framework/angular/forms/editors/code-editor.component.ts b/frontend/src/app/framework/angular/forms/editors/code-editor.component.ts
index 23b578851..8cc993b6f 100644
--- a/frontend/src/app/framework/angular/forms/editors/code-editor.component.ts
+++ b/frontend/src/app/framework/angular/forms/editors/code-editor.component.ts
@@ -5,10 +5,11 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
+import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { debounceTime, Subject } from 'rxjs';
import { ResourceLoaderService, StatefulControlComponent, Types } from '@app/framework/internal';
+import { TypedSimpleChanges } from './../../helpers';
import { FocusComponent } from './../forms-helper';
declare const ace: any;
@@ -26,8 +27,9 @@ export const SQX_CODE_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class CodeEditorComponent extends StatefulControlComponent<{}, string> implements AfterViewInit, FocusComponent, OnChanges {
+export class CodeEditorComponent extends StatefulControlComponent<{}, string> implements AfterViewInit, FocusComponent {
private aceEditor: any;
+ private aceTools: any;
private valueChanged = new Subject();
private value = '';
private modelist: any;
@@ -51,6 +53,9 @@ export class CodeEditorComponent extends StatefulControlComponent<{}, string> im
@Input()
public maxLines: number | undefined;
+ @Input()
+ public singleLine = false;
+
@Input()
public wordWrap = false;
@@ -63,9 +68,9 @@ export class CodeEditorComponent extends StatefulControlComponent<{}, string> im
}
@Input()
- public set completion(value: ReadonlyArray<{ path: string; description: string }> | undefined | null) {
+ public set completion(value: ReadonlyArray<{ path: string; description: string; type: string; allowedValues?: string[] }> | undefined | null) {
if (value) {
- this.completions = value.map(({ path, description }) => ({ value: path, name: path, meta: 'context', description }));
+ this.completions = value.map(({ path, description, type, allowedValues }) => ({ value: path, name: path, description, meta: type?.toLowerCase(), path, allowedValues }));
} else {
this.completions = [];
}
@@ -77,16 +82,16 @@ export class CodeEditorComponent extends StatefulControlComponent<{}, string> im
super(changeDetector, {});
}
- public ngOnChanges(changes: SimpleChanges) {
- if (changes['valueFile'] || changes['mode']) {
+ public ngOnChanges(changes: TypedSimpleChanges) {
+ if (changes.valueFile || changes.mode) {
this.setMode();
}
- if (changes['height'] || changes['maxLines']) {
- this.setHeight();
+ if (changes.height || changes.maxLines || changes.singleLine) {
+ this.setOptions();
}
- if (changes['wordWrap']) {
+ if (changes.wordWrap) {
this.setWordWrap();
}
}
@@ -114,15 +119,19 @@ export class CodeEditorComponent extends StatefulControlComponent<{}, string> im
}
public onDisabled(isDisabled: boolean) {
- if (this.aceEditor) {
- this.aceEditor.setReadOnly(isDisabled);
+ if (!this.aceEditor) {
+ return;
}
+
+ this.aceEditor.setReadOnly(isDisabled);
}
public focus() {
- if (this.aceEditor) {
- this.aceEditor.focus();
+ if (!this.aceEditor) {
+ return;
}
+
+ this.aceEditor.focus();
}
public ngAfterViewInit() {
@@ -136,39 +145,39 @@ export class CodeEditorComponent extends StatefulControlComponent<{}, string> im
this.resourceLoader.loadLocalScript('dependencies/ace/ext/modelist.js'),
this.resourceLoader.loadLocalScript('dependencies/ace/ext/language_tools.js'),
]).then(() => {
- this.aceEditor = ace.edit(this.editor.nativeElement);
-
this.modelist = ace.require('ace/ext/modelist');
- this.aceEditor.setReadOnly(this.snapshot.isDisabled);
+ this.aceEditor = ace.edit(this.editor.nativeElement);
this.aceEditor.setFontSize(14);
-
- this.onDisabled(this.snapshot.isDisabled);
+ this.aceTools = ace.require('ace/ext/language_tools');
this.setValue(this.value);
this.setMode();
- this.setHeight();
+ this.setOptions();
this.setWordWrap();
+ this.onDisabled(this.snapshot.isDisabled);
- const langTools = ace.require('ace/ext/language_tools');
-
- if (langTools) {
- this.aceEditor.setOptions({
- enableBasicAutocompletion: true,
- enableSnippets: true,
- enableLiveAutocompletion: true,
- });
-
+ if (this.aceTools) {
const previous = this.aceEditor.completers;
this.aceEditor.completers = [
- previous[0], {
+ previous?.[0], {
getCompletions: (editor: any, session: any, pos: any, prefix: any, callback: any) => {
callback(null, this.completions);
},
getDocTooltip: (item: any) => {
- if (item.meta === 'context' && item.description) {
+ if (item.path && item.description) {
item.docHTML = `${item.value}
${item.description}`;
+
+ if (item.allowedValues) {
+ item.docHTML += 'Allowed Values:
';
+
+ for (const value of item.allowedValues) {
+ item.docHTML += `${value} `;
+ }
+
+ item.docHTML += '
';
+ }
}
},
// eslint-disable-next-line no-useless-escape
@@ -176,7 +185,6 @@ export class CodeEditorComponent extends StatefulControlComponent<{}, string> im
},
];
}
-
this.aceEditor.on('blur', () => {
this.changeValue();
@@ -187,6 +195,12 @@ export class CodeEditorComponent extends StatefulControlComponent<{}, string> im
this.valueChanged.next(true);
});
+ this.aceEditor.on('paste', (event: any) => {
+ if (this.singleLine) {
+ event.text = event.text.replace(/[\r\n]+/g, ' ');
+ }
+ });
+
this.detach();
});
}
@@ -219,39 +233,61 @@ export class CodeEditorComponent extends StatefulControlComponent<{}, string> im
}
private setWordWrap() {
- if (this.aceEditor) {
- this.aceEditor.getSession().setUseWrapMode(this.wordWrap);
+ if (!this.aceEditor || this.singleLine) {
+ return;
}
+
+ this.aceEditor.getSession().setUseWrapMode(this.wordWrap);
}
private setMode() {
- if (this.aceEditor) {
- if (this.valueFile && this.modelist) {
- const mode = this.modelist.getModeForPath(this.valueFile).mode;
+ if (!this.aceEditor) {
+ return;
+ }
- this.aceEditor.getSession().setMode(mode);
- } else {
- this.aceEditor.getSession().setMode(this.mode);
- }
+ if (this.valueFile && this.modelist) {
+ const mode = this.modelist.getModeForPath(this.valueFile).mode;
+
+ this.aceEditor.getSession().setMode(mode);
+ } else {
+ this.aceEditor.getSession().setMode(this.mode);
}
}
- private setHeight() {
- if (this.aceEditor && this.editor?.nativeElement) {
+ private setOptions() {
+ if (!this.aceEditor) {
+ return;
+ }
+
+ let maxLines = undefined;
+ let minLines = undefined;
+
+ if (!this.singleLine) {
if (Types.isNumber(this.height)) {
const lines = this.height / 15;
- this.aceEditor.setOptions({
- minLines: lines,
- maxLines: lines,
- });
+ maxLines = lines;
+ minLines = lines;
} else if (this.height === 'auto') {
- this.aceEditor.setOptions({
- minLines: 3,
- maxLines: this.maxLines || 500,
- });
+ maxLines = this.maxLines || 500;
+ minLines = Math.min(3, maxLines);
}
+ } else {
+ maxLines = 1;
+ minLines = 1;
}
+
+ this.aceEditor.setOptions({
+ autoScrollEditorIntoView: this.singleLine,
+ enableBasicAutocompletion: !!this.aceTools,
+ enableLiveAutocompletion: !!this.aceTools,
+ enableSnippets: !!this.aceTools && !this.singleLine,
+ highlightActiveLine: !this.singleLine,
+ maxLines,
+ minLines,
+ printMargin: !this.singleLine,
+ showGutter: !this.singleLine,
+ });
}
private setValue(value: string) {
diff --git a/frontend/src/app/framework/angular/forms/editors/code-editor.stories.ts b/frontend/src/app/framework/angular/forms/editors/code-editor.stories.ts
new file mode 100644
index 000000000..b94a4d0b3
--- /dev/null
+++ b/frontend/src/app/framework/angular/forms/editors/code-editor.stories.ts
@@ -0,0 +1,99 @@
+/*
+ * Squidex Headless CMS
+ *
+ * @license
+ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
+ */
+
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { moduleMetadata } from '@storybook/angular';
+import { Meta, Story } from '@storybook/angular/types-6-0';
+import { CodeEditorComponent, SqxFrameworkModule } from '@app/framework';
+
+export default {
+ title: 'Framework/CodeEditor',
+ component: CodeEditorComponent,
+ argTypes: {
+ disabled: {
+ control: 'boolean',
+ },
+ dropdownFullWidth: {
+ control: 'boolean',
+ },
+ },
+ decorators: [
+ moduleMetadata({
+ imports: [
+ BrowserAnimationsModule,
+ SqxFrameworkModule,
+ SqxFrameworkModule.forRoot(),
+ ],
+ }),
+ ],
+} as Meta;
+
+const Template: Story = (args: CodeEditorComponent) => ({
+ props: args,
+ template: `
+
+
+
+
+ `,
+});
+
+const SingleLineTemplate: Story = (args: CodeEditorComponent) => ({
+ props: args,
+ template: `
+
+ `,
+});
+
+const COMPLETIONS = [{
+ path: 'path1',
+ description: 'Test1 Path',
+}, {
+ path: 'path2',
+ description: 'Test2 Path',
+}, {
+ path: 'path3',
+ description: 'Test3 Path',
+}];
+
+export const Default = Template.bind({});
+
+Default.args = {
+ height: 'auto',
+};
+
+export const SingleLine = SingleLineTemplate.bind({});
+
+SingleLine.args = {
+ completion: COMPLETIONS,
+ singleLine: true,
+};
\ No newline at end of file
diff --git a/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts b/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts
index 04a38afc8..b2262ca42 100644
--- a/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts
+++ b/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts
@@ -5,10 +5,11 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChildren, EventEmitter, forwardRef, Input, OnChanges, OnInit, Output, QueryList, SimpleChanges, TemplateRef } from '@angular/core';
+import { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChildren, EventEmitter, forwardRef, Input, OnInit, Output, QueryList, TemplateRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms';
import { map } from 'rxjs/operators';
import { Keys, ModalModel, RelativePosition, StatefulControlComponent, Types } from '@app/framework/internal';
+import { TypedSimpleChanges } from './../../helpers';
export const SQX_DROPDOWN_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DropdownComponent), multi: true,
@@ -37,7 +38,7 @@ interface State {
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class DropdownComponent extends StatefulControlComponent> implements AfterContentInit, OnChanges, OnInit {
+export class DropdownComponent extends StatefulControlComponent> implements AfterContentInit, OnInit {
private value: any;
@Output()
@@ -127,8 +128,8 @@ export class DropdownComponent extends StatefulControlComponent) {
+ if (changes.items) {
this.items = this.items || [];
this.next({ suggestedItems: this.items });
diff --git a/frontend/src/app/framework/angular/forms/editors/radio-group.component.ts b/frontend/src/app/framework/angular/forms/editors/radio-group.component.ts
index c3be42332..6359ff92e 100644
--- a/frontend/src/app/framework/angular/forms/editors/radio-group.component.ts
+++ b/frontend/src/app/framework/angular/forms/editors/radio-group.component.ts
@@ -5,7 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
-import { AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnChanges, ViewChild } from '@angular/core';
+import { AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { getTagValues, MathHelper, StatefulControlComponent, TagValue, TextMeasurer } from '@app/framework/internal';
@@ -27,7 +27,7 @@ interface State {
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class RadioGroupComponent extends StatefulControlComponent implements AfterViewInit, AfterViewChecked, OnChanges {
+export class RadioGroupComponent extends StatefulControlComponent implements AfterViewInit, AfterViewChecked {
private readonly textMeasurer: TextMeasurer;
private childrenWidth = 0;
private containerWidth = 0;
diff --git a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html
index 1ec34342a..c59a585df 100644
--- a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html
+++ b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html
@@ -27,7 +27,7 @@