diff --git a/frontend/app/features/content/pages/calendar/calendar-page.component.ts b/frontend/app/features/content/pages/calendar/calendar-page.component.ts index dd9090c6b..d34b595dd 100644 --- a/frontend/app/features/content/pages/calendar/calendar-page.component.ts +++ b/frontend/app/features/content/pages/calendar/calendar-page.component.ts @@ -60,10 +60,10 @@ export class CalendarPageComponent implements AfterViewInit, OnDestroy { const Calendar = tui.Calendar; this.calendar = new Calendar(this.calendarContainer.nativeElement, { - taskView: false, - scheduleView: ['time'], defaultView: 'month', isReadOnly: true, + scheduleView: ['time'], + taskView: false, ...getLocalizationSettings(), }); diff --git a/frontend/app/features/content/pages/content/editor/content-field.component.ts b/frontend/app/features/content/pages/content/editor/content-field.component.ts index 393285954..6abda7516 100644 --- a/frontend/app/features/content/pages/content/editor/content-field.component.ts +++ b/frontend/app/features/content/pages/content/editor/content-field.component.ts @@ -6,9 +6,8 @@ */ import { Component, EventEmitter, HostBinding, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; -import { AppLanguageDto, AppsState, EditContentForm, FieldForm, invalid$, LocalStoreService, SchemaDto, Settings, TranslationsService, Types, value$ } from '@app/shared'; -import { combineLatest, Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { AppLanguageDto, AppsState, changed$, EditContentForm, FieldForm, invalid$, LocalStoreService, SchemaDto, Settings, TranslationsService } from '@app/shared'; +import { Observable } from 'rxjs'; @Component({ selector: 'sqx-content-field[form][formContext][formModel][language][languages][schema]', @@ -79,11 +78,7 @@ export class ContentFieldComponent implements OnChanges { } if ((changes['formModel'] || changes['formModelCompare']) && this.formModelCompare) { - this.isDifferent = - combineLatest([ - value$(this.formModel.form), - value$(this.formModelCompare!.form), - ]).pipe(map(([lhs, rhs]) => !Types.equals(lhs, rhs, true))); + this.isDifferent = changed$(this.formModel.form, this.formModelCompare.form); } } @@ -110,41 +105,44 @@ export class ContentFieldComponent implements OnChanges { public translate() { const master = this.languages.find(x => x.isMaster); - if (master) { - const masterCode = master.iso2Code; - const masterValue = this.formModel.get(masterCode)!.form.value; - - if (masterValue) { - if (this.showAllControls) { - for (const language of this.languages) { - if (!language.isMaster) { - this.translateValue(masterValue, masterCode, language.iso2Code); - } - } - } else { - this.translateValue(masterValue, masterCode, this.language.iso2Code); - } + if (!master) { + return; + } + const masterCode = master.iso2Code; + const masterValue = this.formModel.get(masterCode)!.form.value; + + if (!masterValue) { + return; + } + + if (this.showAllControls) { + for (const language of this.languages.filter(x => !x.isMaster)) { + this.translateValue(masterValue, masterCode, language.iso2Code); } + } else { + this.translateValue(masterValue, masterCode, this.language.iso2Code); } } private translateValue(text: string, sourceLanguage: string, targetLanguage: string) { const control = this.formModel.get(targetLanguage); - if (control) { - const value = control.form.value; - - if (!value) { - const request = { text, sourceLanguage, targetLanguage }; + if (!control) { + return; + } - this.translations.translate(this.appsState.appName, request) - .subscribe(result => { - if (result.text) { - control.form.setValue(result.text); - } - }); - } + if (control.form.value) { + return; } + + const request = { text, sourceLanguage, targetLanguage }; + + this.translations.translate(this.appsState.appName, request) + .subscribe(result => { + if (result.text) { + control.form.setValue(result.text); + } + }); } public prefix(language: AppLanguageDto) { diff --git a/frontend/app/features/content/shared/content-extension.component.ts b/frontend/app/features/content/shared/content-extension.component.ts index d27679032..e5e94d9e3 100644 --- a/frontend/app/features/content/shared/content-extension.component.ts +++ b/frontend/app/features/content/shared/content-extension.component.ts @@ -20,15 +20,15 @@ export class ContentExtensionComponent extends ResourceOwner implements OnChange private readonly context: any; private isInitialized = false; + @ViewChild('iframe', { static: false }) + public iframe: ElementRef; + @Input() public content?: ContentDto | null; @Input() public contentSchema: SchemaDto; - @ViewChild('iframe', { static: false }) - public iframe: ElementRef; - @Input() public set url(value: string | undefined | null) { this.computedUrl = computeEditorUrl(value, this.appsState.snapshot.selectedSettings); diff --git a/frontend/app/features/content/shared/forms/array-item.component.ts b/frontend/app/features/content/shared/forms/array-item.component.ts index d5e2a0af3..1d3a27fb8 100644 --- a/frontend/app/features/content/shared/forms/array-item.component.ts +++ b/frontend/app/features/content/shared/forms/array-item.component.ts @@ -6,9 +6,8 @@ */ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnInit, Output, QueryList, SimpleChanges, ViewChildren } from '@angular/core'; -import { AppLanguageDto, ComponentForm, EditContentForm, FieldDto, FieldFormatter, FieldSection, invalid$, ObjectForm, RootFieldDto, StatefulComponent, Types, value$ } from '@app/shared'; +import { AppLanguageDto, ComponentForm, EditContentForm, FieldDto, FieldFormatter, FieldSection, invalid$, ObjectForm, RootFieldDto, StatefulComponent, Types, valueProjection$ } from '@app/shared'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; import { ComponentSectionComponent } from './component-section.component'; interface State { @@ -88,7 +87,7 @@ export class ArrayItemComponent extends StatefulComponent implements OnCh if (changes['formModel']) { this.isInvalid = invalid$(this.formModel.form); - this.title = value$(this.formModel.form).pipe(map(x => this.getTitle(x))); + this.title = valueProjection$(this.formModel.form, x => this.getTitle(x)); } } diff --git a/frontend/app/features/content/shared/forms/field-editor.component.html b/frontend/app/features/content/shared/forms/field-editor.component.html index 09c2a2089..967307480 100644 --- a/frontend/app/features/content/shared/forms/field-editor.component.html +++ b/frontend/app/features/content/shared/forms/field-editor.component.html @@ -5,13 +5,13 @@ Disabled - +
@@ -31,16 +31,16 @@ - + - +
- +
@@ -67,31 +67,31 @@
- + - + - + - + - + -
- + @@ -103,7 +103,7 @@ @@ -138,55 +138,55 @@ - + - + - + - + - + - + - + -
- +
- +
- + - + - diff --git a/frontend/app/features/content/shared/forms/field-editor.component.ts b/frontend/app/features/content/shared/forms/field-editor.component.ts index 8032afa64..f2598c806 100644 --- a/frontend/app/features/content/shared/forms/field-editor.component.ts +++ b/frontend/app/features/content/shared/forms/field-editor.component.ts @@ -6,10 +6,9 @@ */ import { Component, ElementRef, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core'; -import { AbstractControl, FormControl } from '@angular/forms'; -import { AbstractContentForm, AppLanguageDto, EditContentForm, FieldDto, MathHelper, RootFieldDto, Types, value$ } from '@app/shared'; +import { AbstractControl } from '@angular/forms'; +import { AbstractContentForm, AppLanguageDto, EditContentForm, FieldDto, hasNoValue$, MathHelper, Types } from '@app/shared'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; @Component({ selector: 'sqx-field-editor[form][formContext][formModel][language][languages]', @@ -17,6 +16,8 @@ import { map } from 'rxjs/operators'; templateUrl: './field-editor.component.html', }) export class FieldEditorComponent implements OnChanges { + public readonly uniqueId = MathHelper.guid(); + @Input() public form: EditContentForm; @@ -50,25 +51,13 @@ export class FieldEditorComponent implements OnChanges { return this.formModel.field; } - public get editorControl() { - return this.formModel.form as FormControl; - } - - public get rootField() { - return this.formModel.field as RootFieldDto; + public get fieldForm() { + return this.formModel.form; } - public uniqueId = MathHelper.guid(); - public ngOnChanges(changes: SimpleChanges) { if (changes['formModel']) { - const previousControl: AbstractContentForm = changes['formModel'].previousValue; - - if (previousControl && Types.isFunction(previousControl.form['_clearChangeFns'])) { - previousControl.form['_clearChangeFns'](); - } - - this.isEmpty = value$(this.formModel.form).pipe(map(x => Types.isUndefined(x) || Types.isNull(x))); + this.isEmpty = hasNoValue$(this.formModel.form); } } diff --git a/frontend/app/features/content/shared/forms/stock-photo-editor.component.ts b/frontend/app/features/content/shared/forms/stock-photo-editor.component.ts index 4fd03d626..c47bd4ab1 100644 --- a/frontend/app/features/content/shared/forms/stock-photo-editor.component.ts +++ b/frontend/app/features/content/shared/forms/stock-photo-editor.component.ts @@ -7,9 +7,9 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, Input, OnInit } from '@angular/core'; import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { StatefulControlComponent, StockPhotoDto, StockPhotoService, thumbnail, Types, value$ } from '@app/shared'; +import { StatefulControlComponent, StockPhotoDto, StockPhotoService, thumbnail, Types, value$, valueProjection$ } from '@app/shared'; import { of } from 'rxjs'; -import { debounceTime, map, switchMap, tap } from 'rxjs/operators'; +import { debounceTime, switchMap, tap } from 'rxjs/operators'; export const SQX_STOCK_PHOTO_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => StockPhotoEditorComponent), multi: true, @@ -40,7 +40,7 @@ export class StockPhotoEditorComponent extends StatefulControlComponent thumbnail(v, 400) || v)); + public stockPhotoThumbnail = valueProjection$(this.valueControl, x => thumbnail(x, 400) || x); public stockPhotoSearch = new FormControl(''); public stockPhotos = diff --git a/frontend/app/features/schemas/pages/schema/fields/types/number-ui.component.ts b/frontend/app/features/schemas/pages/schema/fields/types/number-ui.component.ts index ffd27743c..2034e38b6 100644 --- a/frontend/app/features/schemas/pages/schema/fields/types/number-ui.component.ts +++ b/frontend/app/features/schemas/pages/schema/fields/types/number-ui.component.ts @@ -7,9 +7,8 @@ import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { FormGroup } from '@angular/forms'; -import { FieldDto, FloatConverter, NumberFieldPropertiesDto, NUMBER_FIELD_EDITORS, ResourceOwner, value$ } from '@app/shared'; +import { FieldDto, FloatConverter, NumberFieldPropertiesDto, NUMBER_FIELD_EDITORS, ResourceOwner, valueProjection$ } from '@app/shared'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; @Component({ selector: 'sqx-number-ui[field][fieldForm][properties]', @@ -36,11 +35,13 @@ export class NumberUIComponent extends ResourceOwner implements OnChanges { if (changes['fieldForm']) { this.unsubscribeAll(); + const editor = this.fieldForm.controls['editor']; + this.hideAllowedValues = - value$(this.fieldForm.controls['editor']).pipe(map(x => !(x && (x === 'Radio' || x === 'Dropdown')))); + valueProjection$(editor, x => !(x && (x === 'Radio' || x === 'Dropdown'))); this.hideInlineEditable = - value$(this.fieldForm.controls['editor']).pipe(map(x => x === 'Radio')); + valueProjection$(editor, x => x === 'Radio'); this.own( this.hideAllowedValues.subscribe(isSelection => { diff --git a/frontend/app/features/schemas/pages/schema/fields/types/string-ui.component.ts b/frontend/app/features/schemas/pages/schema/fields/types/string-ui.component.ts index de5545e24..33ca8ad37 100644 --- a/frontend/app/features/schemas/pages/schema/fields/types/string-ui.component.ts +++ b/frontend/app/features/schemas/pages/schema/fields/types/string-ui.component.ts @@ -7,9 +7,8 @@ import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { FormGroup } from '@angular/forms'; -import { FieldDto, ResourceOwner, StringFieldPropertiesDto, STRING_FIELD_EDITORS, value$ } from '@app/shared'; +import { FieldDto, ResourceOwner, StringFieldPropertiesDto, STRING_FIELD_EDITORS, valueProjection$ } from '@app/shared'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; @Component({ selector: 'sqx-string-ui[field][fieldForm][properties]', @@ -35,11 +34,13 @@ export class StringUIComponent extends ResourceOwner implements OnChanges { if (changes['fieldForm']) { this.unsubscribeAll(); + const editor = this.fieldForm.controls['editor']; + this.hideAllowedValues = - value$(this.fieldForm.controls['editor']).pipe(map(x => !(x && (x === 'Radio' || x === 'Dropdown')))); + valueProjection$(editor, x => !(x && (x === 'Radio' || x === 'Dropdown'))); this.hideInlineEditable = - value$(this.fieldForm.controls['editor']).pipe(map(x => !(x && (x === 'Input' || x === 'Dropdown' || x === 'Slug')))); + valueProjection$(editor, x => !(x && (x === 'Input' || x === 'Dropdown' || x === 'Slug'))); this.own( this.hideAllowedValues.subscribe(isSelection => { diff --git a/frontend/app/framework/angular/forms/control-errors.component.ts b/frontend/app/framework/angular/forms/control-errors.component.ts index cb100be50..4cd45a692 100644 --- a/frontend/app/framework/angular/forms/control-errors.component.ts +++ b/frontend/app/framework/angular/forms/control-errors.component.ts @@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Host, Input, OnC import { AbstractControl, FormArray, FormGroupDirective } from '@angular/forms'; import { fadeAnimation, LocalizerService, StatefulComponent, Types } from '@app/framework/internal'; import { merge } from 'rxjs'; +import { touchedChange$ } from './forms-helper'; import { formatError } from './error-formatting'; interface State { @@ -27,8 +28,7 @@ interface State { }) export class ControlErrorsComponent extends StatefulComponent implements OnChanges, OnDestroy { private displayFieldName: string; - private control: AbstractControl; - private controlOriginalMarkAsTouched: any; + private control: AbstractControl | null = null; @Input() public for: string | AbstractControl; @@ -37,7 +37,7 @@ export class ControlErrorsComponent extends StatefulComponent implements public fieldName: string | null | undefined; public get isTouched() { - return this.control.touched || Types.is(this.control, FormArray); + return this.control?.touched || Types.is(this.control, FormArray); } constructor(changeDetector: ChangeDetectorRef, @@ -49,13 +49,9 @@ export class ControlErrorsComponent extends StatefulComponent implements }); } - public ngOnDestroy() { - super.ngOnDestroy(); - - this.unsetCustomMarkAsTouchedFunction(); - } - public ngOnChanges() { + const previousControl = this.control; + if (this.fieldName) { this.displayFieldName = this.fieldName; } else if (this.for) { @@ -72,53 +68,34 @@ export class ControlErrorsComponent extends StatefulComponent implements } } - let control: AbstractControl | null = null; - if (Types.isString(this.for)) { if (this.formGroupDirective && this.formGroupDirective.form) { - control = this.formGroupDirective.form.controls[this.for]; + this.control = this.formGroupDirective.form.controls[this.for]; + } else { + this.control = null; } } else { - control = this.for; + this.control = this.for; } - if (this.control !== control && control) { + if (this.control !== previousControl) { this.unsubscribeAll(); - this.unsetCustomMarkAsTouchedFunction(); - - this.control = control; - if (control) { + if (this.control) { this.own( - merge(control.valueChanges, control.statusChanges) - .subscribe(() => { - this.createMessages(); - })); - - this.controlOriginalMarkAsTouched = this.control.markAsTouched; - - // eslint-disable-next-line @typescript-eslint/no-this-alias - const self = this; - - // eslint-disable-next-line func-names - this.control['markAsTouched'] = function () { - // eslint-disable-next-line prefer-rest-params - self.controlOriginalMarkAsTouched.apply(this, arguments); - - self.createMessages(); - }; + merge( + this.control.valueChanges, + this.control.statusChanges, + touchedChange$(this.control), + ).subscribe(() => { + this.createMessages(); + })); } } this.createMessages(); } - private unsetCustomMarkAsTouchedFunction() { - if (this.control && this.controlOriginalMarkAsTouched) { - this.control['markAsTouched'] = this.controlOriginalMarkAsTouched; - } - } - private createMessages() { const errorMessages: string[] = []; diff --git a/frontend/app/framework/angular/forms/forms-helper.spec.ts b/frontend/app/framework/angular/forms/forms-helper.spec.ts index e389d023f..92b315e7d 100644 --- a/frontend/app/framework/angular/forms/forms-helper.spec.ts +++ b/frontend/app/framework/angular/forms/forms-helper.spec.ts @@ -6,7 +6,7 @@ */ import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms'; -import { getControlPath, value$ } from './forms-helper'; +import { getControlPath, hasNoValue$, hasValue$, touchedChange$, value$ } from './forms-helper'; describe('FormHelpers', () => { describe('value$', () => { @@ -44,6 +44,54 @@ describe('FormHelpers', () => { }); }); + it('should provide touched changes', () => { + const form = new FormControl('1', Validators.required); + + const values: any[] = []; + + touchedChange$(form).subscribe(x => { + values.push(x); + }); + + form.markAsTouched(); + form.markAsUntouched(); + form.markAsTouched(); + + expect(values).toEqual([false, true, false, true]); + }); + + it('should provide value when defined', () => { + const form = new FormControl('1', Validators.required); + + const values: any[] = []; + + hasValue$(form).subscribe(x => { + values.push(x); + }); + + form.setValue(undefined); + form.setValue('1'); + form.setValue(null); + + expect(values).toEqual([true, false, true, false]); + }); + + it('should provide value when defined', () => { + const form = new FormControl('1', Validators.required); + + const values: any[] = []; + + hasNoValue$(form).subscribe(x => { + values.push(x); + }); + + form.setValue(undefined); + form.setValue('1'); + form.setValue(null); + + expect(values).toEqual([false, true, false, true]); + }); + describe('getControlPath', () => { it('should calculate path for standalone control', () => { const control = new FormControl(); diff --git a/frontend/app/framework/angular/forms/forms-helper.ts b/frontend/app/framework/angular/forms/forms-helper.ts index fdeb61ba9..2d6c67345 100644 --- a/frontend/app/framework/angular/forms/forms-helper.ts +++ b/frontend/app/framework/angular/forms/forms-helper.ts @@ -6,7 +6,7 @@ */ import { AbstractControl, FormArray, FormGroup, ValidatorFn } from '@angular/forms'; -import { Observable } from 'rxjs'; +import { combineLatest, Observable } from 'rxjs'; import { distinctUntilChanged, map, startWith } from 'rxjs/operators'; import { Types } from './../../utils/types'; @@ -96,19 +96,67 @@ export function invalid$(form: AbstractControl): Observable { } export function value$(form: AbstractControl): Observable { - return form.valueChanges.pipe(startWith(form.value), distinctUntilChanged()); + return form.valueChanges.pipe(map(() => getRawValue(form)), startWith(getRawValue(form)), distinctUntilChanged()); } -export function valueAll$(form: AbstractControl): Observable { - return form.valueChanges.pipe(map(() => getRawValue(form)), startWith(getRawValue(form)), distinctUntilChanged()); +export function valueProjection$(form: AbstractControl, projection: (value: any) => T): Observable { + return value$(form).pipe(map(projection), distinctUntilChanged()); } export function hasValue$(form: AbstractControl): Observable { - return value$(form).pipe(map(v => !!v)); + return valueProjection$(form, v => isValid(v)); } export function hasNoValue$(form: AbstractControl): Observable { - return value$(form).pipe(map(v => !v)); + return valueProjection$(form, v => !isValid(v)); +} + +export function changed$(lhs: AbstractControl, rhs: AbstractControl) { + return combineLatest([ + value$(lhs), + value$(rhs), + ]).pipe(map(([lhs, rhs]) => !Types.equals(lhs, rhs, true)), + distinctUntilChanged()); +} + +export function touchedChange$(form: AbstractControl) { + return new Observable(subscriber => { + let previousTouched = form.touched; + + const updateTouched = (touched: boolean) => { + if (touched !== previousTouched) { + subscriber.next(touched); + + previousTouched = touched; + } + }; + + subscriber.next(form.touched); + + const previousMarkedAsTouched = form.markAsTouched; + const previousMarkedAsUntouched = form.markAsUntouched; + + form['markAsTouched'] = function markAsTouched(...args: any[]) { + previousMarkedAsTouched.apply(this, args); + + updateTouched(form.touched); + }; + + form['markAsUntouched'] = function markAsTouched(...args: any[]) { + previousMarkedAsUntouched.apply(this, args); + + updateTouched(form.touched); + }; + + return () => { + form['markAsTouched'] = previousMarkedAsTouched; + form['markAsUntouched'] = previousMarkedAsUntouched; + }; + }); +} + +function isValid(value: any) { + return !Types.isNull(value) && !Types.isUndefined(value); } export function getRawValue(form: AbstractControl): any { diff --git a/frontend/app/framework/angular/stop-drag.directive.ts b/frontend/app/framework/angular/stop-drag.directive.ts new file mode 100644 index 000000000..3a0a1569f --- /dev/null +++ b/frontend/app/framework/angular/stop-drag.directive.ts @@ -0,0 +1,25 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Directive, HostListener, Input } from '@angular/core'; + +@Directive({ + selector: '[sqxStopDrag]', +}) +export class StopDragDirective { + @Input('sqxStopDrag') + public shouldStop: any = true; + + @HostListener('dragstart', ['$event']) + public onDragStart(event: Event) { + const shouldStop: any = this.shouldStop; + + if (shouldStop || shouldStop === '') { + event.preventDefault(); + } + } +} diff --git a/frontend/app/framework/declarations.ts b/frontend/app/framework/declarations.ts index 69ec14ac3..08fb49255 100644 --- a/frontend/app/framework/declarations.ts +++ b/frontend/app/framework/declarations.ts @@ -72,6 +72,7 @@ export * from './angular/shortcut.component'; export * from './angular/shortcut.directive'; export * from './angular/status-icon.component'; export * from './angular/stop-click.directive'; +export * from './angular/stop-drag.directive'; export * from './angular/sync-scrolling.directive'; export * from './angular/sync-width.directive'; export * from './angular/tab-router-link.directive'; diff --git a/frontend/app/framework/module.ts b/frontend/app/framework/module.ts index 150baffc7..91d3bd6f9 100644 --- a/frontend/app/framework/module.ts +++ b/frontend/app/framework/module.ts @@ -11,7 +11,7 @@ import { ModuleWithProviders, NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { ColorPickerModule } from 'ngx-color-picker'; -import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, ConfirmClickDirective, ControlErrorsComponent, CopyDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, HighlightPipe, HoverBackgroundDirective, ImageSourceDirective, IndeterminateValueDirective, ISODatePipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MoneyPipe, MonthPipe, OnboardingService, OnboardingTooltipComponent, PagerComponent, ParentLinkDirective, PopupLinkDirective, ProgressBarComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, StarsComponent, StatusIconComponent, StopClickDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations'; +import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterceptor, CanDeactivateGuard, CheckboxGroupComponent, ClipboardService, CodeComponent, CodeEditorComponent, ColorPickerComponent, ConfirmClickDirective, ControlErrorsComponent, CopyDirective, DarkenPipe, DatePipe, DateTimeEditorComponent, DayOfWeekPipe, DayPipe, DialogRendererComponent, DialogService, DisplayNamePipe, DropdownComponent, DurationPipe, EditableTitleComponent, ExternalLinkDirective, FileDropDirective, FileSizePipe, FocusOnInitDirective, FormAlertComponent, FormErrorComponent, FormHintComponent, FromNowPipe, FullDateTimePipe, HighlightPipe, HoverBackgroundDirective, ImageSourceDirective, IndeterminateValueDirective, ISODatePipe, KeysPipe, KNumberPipe, LanguageSelectorComponent, LayoutComponent, LayoutContainerDirective, LightenPipe, ListViewComponent, LoadingInterceptor, LoadingService, LocalizedInputComponent, LocalStoreService, MarkdownInlinePipe, MarkdownPipe, MessageBus, ModalDialogComponent, ModalDirective, ModalPlacementDirective, MoneyPipe, MonthPipe, OnboardingService, OnboardingTooltipComponent, PagerComponent, ParentLinkDirective, PopupLinkDirective, ProgressBarComponent, ResizedDirective, ResizeService, ResourceLoaderService, RootViewComponent, SafeHtmlPipe, SafeResourceUrlPipe, SafeUrlPipe, ScrollActiveDirective, ShortcutComponent, ShortcutDirective, ShortcutService, ShortDatePipe, ShortTimePipe, StarsComponent, StatusIconComponent, StopClickDirective, StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, TagEditorComponent, TemplateWrapperDirective, TempService, TitleComponent, TitleService, ToggleComponent, ToolbarComponent, TooltipDirective, TransformInputDirective, TranslatePipe, VideoPlayerComponent } from './declarations'; @NgModule({ imports: [ @@ -88,6 +88,7 @@ import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterc StarsComponent, StatusIconComponent, StopClickDirective, + StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, @@ -171,6 +172,7 @@ import { AnalyticsService, AutocompleteComponent, AvatarComponent, CachingInterc StarsComponent, StatusIconComponent, StopClickDirective, + StopDragDirective, SyncScollingDirective, SyncWidthDirective, TabRouterlinkDirective, diff --git a/frontend/app/shared/components/schema-category.component.html b/frontend/app/shared/components/schema-category.component.html index ff4f909ac..88a20ac38 100644 --- a/frontend/app/shared/components/schema-category.component.html +++ b/frontend/app/shared/components/schema-category.component.html @@ -1,4 +1,4 @@ -