diff --git a/src/Squidex/app/framework/angular/forms/checkbox-group.component.html b/src/Squidex/app/framework/angular/forms/checkbox-group.component.html index de5b90311..f3245e6f2 100644 --- a/src/Squidex/app/framework/angular/forms/checkbox-group.component.html +++ b/src/Squidex/app/framework/angular/forms/checkbox-group.component.html @@ -1,9 +1,9 @@ - - + \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/forms/checkbox-group.component.ts b/src/Squidex/app/framework/angular/forms/checkbox-group.component.ts index 90dc09607..a426f93cc 100644 --- a/src/Squidex/app/framework/angular/forms/checkbox-group.component.ts +++ b/src/Squidex/app/framework/angular/forms/checkbox-group.component.ts @@ -6,11 +6,11 @@ */ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, Input } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { MathHelper, - StatefulComponent, + StatefulControlComponent, Types } from '@app/framework/internal'; @@ -20,8 +20,6 @@ export const SQX_CHECKBOX_GROUP_CONTROL_VALUE_ACCESSOR: any = { interface State { checkedValues: string[]; - controlId: string; - isDisabled: boolean; } @Component({ @@ -31,39 +29,22 @@ interface State { providers: [SQX_CHECKBOX_GROUP_CONTROL_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush }) -export class CheckboxGroupComponent extends StatefulComponent implements ControlValueAccessor { - private callChange = (v: any) => { /* NOOP */ }; - private callTouched = () => { /* NOOP */ }; +export class CheckboxGroupComponent extends StatefulControlComponent { + public readonly controlId = MathHelper.guid(); @Input() public values: string[] = []; constructor(changeDetector: ChangeDetectorRef) { super(changeDetector, { - controlId: MathHelper.guid(), - checkedValues: [], - isDisabled: false + checkedValues: [] }); } public writeValue(obj: any) { - this.next({ checkedValues: Types.isArrayOfString(obj) ? obj.filter(x => this.values.indexOf(x) >= 0) : [] }); - } - - public setDisabledState(isDisabled: boolean): void { - this.next({ isDisabled }); - } - - public registerOnChange(fn: any) { - this.callChange = fn; - } + const checkedValues = Types.isArrayOfString(obj) ? obj.filter(x => this.values.indexOf(x) >= 0) : []; - public registerOnTouched(fn: any) { - this.callTouched = fn; - } - - public blur() { - this.callTouched(); + this.next({ checkedValues }); } public check(isChecked: boolean, value: string) { @@ -78,7 +59,7 @@ export class CheckboxGroupComponent extends StatefulComponent implements this.next({ checkedValues }); this.callChange(checkedValues); - } + } public isChecked(value: string) { return this.snapshot.checkedValues.indexOf(value) >= 0; diff --git a/src/Squidex/app/framework/angular/forms/code-editor.component.ts b/src/Squidex/app/framework/angular/forms/code-editor.component.ts index 7af142c50..056ebfe55 100644 --- a/src/Squidex/app/framework/angular/forms/code-editor.component.ts +++ b/src/Squidex/app/framework/angular/forms/code-editor.component.ts @@ -6,12 +6,12 @@ */ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, ViewChild } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { Subject } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { - PureComponent, + ExternalControlComponent, ResourceLoaderService, Types } from '@app/framework/internal'; @@ -29,9 +29,7 @@ export const SQX_JSCRIPT_EDITOR_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_JSCRIPT_EDITOR_CONTROL_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush }) -export class CodeEditorComponent extends PureComponent implements ControlValueAccessor, AfterViewInit { - private callChange = (v: any) => { /* NOOP */ }; - private callTouched = () => { /* NOOP */ }; +export class CodeEditorComponent extends ExternalControlComponent implements AfterViewInit { private valueChanged = new Subject(); private aceEditor: any; private value: string; @@ -48,8 +46,6 @@ export class CodeEditorComponent extends PureComponent implements ControlValueAc private readonly resourceLoader: ResourceLoaderService ) { super(changeDetector); - - changeDetector.detach(); } public writeValue(obj: any) { @@ -68,14 +64,6 @@ export class CodeEditorComponent extends PureComponent implements ControlValueAc } } - public registerOnChange(fn: any) { - this.callChange = fn; - } - - public registerOnTouched(fn: any) { - this.callTouched = fn; - } - public ngAfterViewInit() { this.valueChanged.pipe( debounceTime(500)) diff --git a/src/Squidex/app/framework/angular/forms/date-time-editor.component.html b/src/Squidex/app/framework/angular/forms/date-time-editor.component.html index 730e2f679..0bca5eb55 100644 --- a/src/Squidex/app/framework/angular/forms/date-time-editor.component.html +++ b/src/Squidex/app/framework/angular/forms/date-time-editor.component.html @@ -1,19 +1,19 @@
- +
- +
- +
- +
- +
diff --git a/src/Squidex/app/framework/angular/forms/date-time-editor.component.ts b/src/Squidex/app/framework/angular/forms/date-time-editor.component.ts index eee5c1890..cca58a42b 100644 --- a/src/Squidex/app/framework/angular/forms/date-time-editor.component.ts +++ b/src/Squidex/app/framework/angular/forms/date-time-editor.component.ts @@ -5,12 +5,11 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; import * as moment from 'moment'; -import { Subscription } from 'rxjs'; -import { Types } from '@app/framework/internal'; +import { StatefulControlComponent, Types } from '@app/framework/internal'; declare module 'pikaday/pikaday'; @@ -27,15 +26,11 @@ export const SQX_DATE_TIME_EDITOR_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_DATE_TIME_EDITOR_CONTROL_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush }) -export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, OnInit, AfterViewInit { - private timeSubscription: Subscription; - private dateSubscription: Subscription; +export class DateTimeEditorComponent extends StatefulControlComponent<{}, string | null> implements OnInit, AfterViewInit { private picker: any; private timeValue: any | null = null; private dateValue: any | null = null; private suppressEvents = false; - private callChange = (v: any) => { /* NOOP */ }; - private callTouched = () => { /* NOOP */ }; @Input() public mode: string; @@ -49,8 +44,6 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, @ViewChild('dateInput') public dateInput: ElementRef; - public isDisabled = false; - public timeControl = new FormControl(); public dateControl = new FormControl(); @@ -62,18 +55,12 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, return !!this.dateValue; } - constructor( - private readonly changeDetector: ChangeDetectorRef - ) { - } - - public ngOnDestroy() { - this.dateSubscription.unsubscribe(); - this.timeSubscription.unsubscribe(); + constructor(changeDetector: ChangeDetectorRef) { + super(changeDetector, {}); } public ngOnInit() { - this.timeSubscription = + this.observe( this.timeControl.valueChanges.subscribe(value => { if (!value || value.length === 0) { this.timeValue = null; @@ -82,9 +69,9 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, } this.updateValue(); - }); + })); - this.dateSubscription = + this.observe( this.dateControl.valueChanges.subscribe(value => { if (!value || value.length === 0) { this.dateValue = null; @@ -93,7 +80,7 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, } this.updateValue(); - }); + })); } public writeValue(obj: any) { @@ -114,7 +101,7 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, } public setDisabledState(isDisabled: boolean): void { - this.isDisabled = isDisabled; + super.setDisabledState(isDisabled); if (isDisabled) { this.dateControl.disable({ emitEvent: false }); @@ -123,8 +110,6 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, this.dateControl.enable({ emitEvent: false }); this.timeControl.enable({ emitEvent: false }); } - - this.changeDetector.markForCheck(); } public registerOnChange(fn: any) { @@ -144,25 +129,19 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, this.dateValue = this.picker.getMoment(); this.updateValue(); - this.touched(); - - this.changeDetector.markForCheck(); + this.callTouched(); } }); this.updateControls(); } - public touched() { - this.callTouched(); - } - public writeNow() { this.writeValue(new Date().toUTCString()); this.updateControls(); this.updateValue(); - this.touched(); + this.callTouched(); return false; } diff --git a/src/Squidex/app/framework/angular/forms/iframe-editor.component.ts b/src/Squidex/app/framework/angular/forms/iframe-editor.component.ts index 528eb663b..11146a73c 100644 --- a/src/Squidex/app/framework/angular/forms/iframe-editor.component.ts +++ b/src/Squidex/app/framework/angular/forms/iframe-editor.component.ts @@ -5,11 +5,11 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, Renderer2, ViewChild } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnInit, Renderer2, ViewChild } from '@angular/core'; +import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; -import { Types } from '@app/framework/internal'; +import { ExternalControlComponent, Types } from '@app/framework/internal'; export const SQX_IFRAME_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => IFrameEditorComponent), multi: true @@ -22,10 +22,7 @@ export const SQX_IFRAME_EDITOR_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_IFRAME_EDITOR_CONTROL_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush }) -export class IFrameEditorComponent implements ControlValueAccessor, AfterViewInit, OnInit, OnDestroy { - private windowMessageListener: Function; - private callChange = (v: any) => { /* NOOP */ }; - private callTouched = () => { /* NOOP */ }; +export class IFrameEditorComponent extends ExternalControlComponent implements AfterViewInit, OnInit { private value: any; private isDisabled = false; private isInitialized = false; @@ -41,14 +38,11 @@ export class IFrameEditorComponent implements ControlValueAccessor, AfterViewIni public sanitizedUrl: SafeResourceUrl; - constructor( + constructor(changeDetector: ChangeDetectorRef, private readonly sanitizer: DomSanitizer, private readonly renderer: Renderer2 ) { - } - - public ngOnDestroy() { - this.windowMessageListener(); + super(changeDetector); } public ngAfterViewInit() { @@ -56,7 +50,7 @@ export class IFrameEditorComponent implements ControlValueAccessor, AfterViewIni } public ngOnInit(): void { - this.windowMessageListener = + this.observe( this.renderer.listen('window', 'message', (event: MessageEvent) => { if (event.source === this.plugin.contentWindow) { const { type } = event.data; @@ -84,7 +78,7 @@ export class IFrameEditorComponent implements ControlValueAccessor, AfterViewIni this.callTouched(); } } - }); + })); } public writeValue(obj: any) { @@ -102,12 +96,4 @@ export class IFrameEditorComponent implements ControlValueAccessor, AfterViewIni this.plugin.contentWindow.postMessage({ type: 'disabled', isDisabled: this.isDisabled }, '*'); } } - - public registerOnChange(fn: any) { - this.callChange = fn; - } - - public registerOnTouched(fn: any) { - this.callTouched = fn; - } } \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/forms/json-editor.component.ts b/src/Squidex/app/framework/angular/forms/json-editor.component.ts index 77e3adf1d..143fd095a 100644 --- a/src/Squidex/app/framework/angular/forms/json-editor.component.ts +++ b/src/Squidex/app/framework/angular/forms/json-editor.component.ts @@ -6,11 +6,11 @@ */ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, ViewChild } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { Subject } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; -import { ResourceLoaderService } from '@app/framework/internal'; +import { ExternalControlComponent, ResourceLoaderService } from '@app/framework/internal'; declare var ace: any; @@ -25,9 +25,7 @@ export const SQX_JSON_EDITOR_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_JSON_EDITOR_CONTROL_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush }) -export class JsonEditorComponent implements ControlValueAccessor, AfterViewInit { - private callChange = (v: any) => { /* NOOP */ }; - private callTouched = () => { /* NOOP */ }; +export class JsonEditorComponent extends ExternalControlComponent implements AfterViewInit { private valueChanged = new Subject(); private aceEditor: any; private value: any; @@ -35,12 +33,12 @@ export class JsonEditorComponent implements ControlValueAccessor, AfterViewInit private isDisabled = false; @ViewChild('editor') - public editor: ElementRef; + public editor: ElementRef; constructor(changeDetector: ChangeDetectorRef, private readonly resourceLoader: ResourceLoaderService ) { - changeDetector.detach(); + super(changeDetector); } public writeValue(obj: any) { @@ -65,14 +63,6 @@ export class JsonEditorComponent implements ControlValueAccessor, AfterViewInit } } - public registerOnChange(fn: any) { - this.callChange = fn; - } - - public registerOnTouched(fn: any) { - this.callTouched = fn; - } - public ngAfterViewInit() { this.valueChanged.pipe( debounceTime(500)) diff --git a/src/Squidex/app/framework/angular/forms/slider.component.html b/src/Squidex/app/framework/angular/forms/slider.component.html deleted file mode 100644 index ab37c52ff..000000000 --- a/src/Squidex/app/framework/angular/forms/slider.component.html +++ /dev/null @@ -1,3 +0,0 @@ -
-
-
\ No newline at end of file diff --git a/src/Squidex/app/framework/angular/forms/slider.component.scss b/src/Squidex/app/framework/angular/forms/slider.component.scss deleted file mode 100644 index da277a647..000000000 --- a/src/Squidex/app/framework/angular/forms/slider.component.scss +++ /dev/null @@ -1,51 +0,0 @@ -@import '_mixins'; -@import '_vars'; - -$bar-height: .8rem; - -$thumb-size: 1.25rem; -$thumb-margin: ($thumb-size - $bar-height) * .5; - -.slider { - &-bar { - & { - @include border-radius($bar-height * .5); - position: relative; - border: 1px solid $color-input-border; - margin-bottom: 1.25rem; - margin-top: .25rem; - margin-right: $thumb-size * .5; - background: $color-dark-foreground; - height: $bar-height; - } - - &.disabled { - background: lighten($color-border, 5%); - } - } - - &-thumb { - & { - @include border-radius($thumb-size * .5); - position: absolute; - width: $thumb-size; - height: $thumb-size; - border: 1px solid $color-input-border; - background: $color-dark-foreground; - margin-top: -$thumb-margin; - margin-left: -$thumb-size * .5; - } - - &.disabled { - background: lighten($color-border, 5%); - } - - &.focused { - border-color: $color-theme-blue; - } - } -} - -.disabled { - pointer-events: none; -} \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/forms/slider.component.ts b/src/Squidex/app/framework/angular/forms/slider.component.ts deleted file mode 100644 index 2dcb73854..000000000 --- a/src/Squidex/app/framework/angular/forms/slider.component.ts +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, Renderer2, ViewChild } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; - -import { Types } from '@app/framework/internal'; - -export const SQX_SLIDER_CONTROL_VALUE_ACCESSOR: any = { - provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SliderComponent), multi: true -}; - -@Component({ - selector: 'sqx-slider', - styleUrls: ['./slider.component.scss'], - templateUrl: './slider.component.html', - providers: [SQX_SLIDER_CONTROL_VALUE_ACCESSOR], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class SliderComponent implements ControlValueAccessor { - private callChange = (v: any) => { /* NOOP */ }; - private callTouched = () => { /* NOOP */ }; - private windowMouseMoveListener: Function | null = null; - private windowMouseUpListener: Function | null = null; - private centerStartOffset = 0; - private lastValue: number; - private value: number; - private isDragging = false; - - @ViewChild('bar') - public bar: ElementRef; - - @ViewChild('thumb') - public thumb: ElementRef; - - @Input() - public min = 0; - - @Input() - public max = 100; - - @Input() - public step = 1; - - public isDisabled = false; - - constructor( - private readonly changeDetector: ChangeDetectorRef, - private readonly renderer: Renderer2 - ) { - } - - public writeValue(obj: any) { - this.lastValue = this.value = Types.isNumber(obj) ? obj : 0; - - this.updateThumbPosition(); - - this.changeDetector.markForCheck(); - } - - public setDisabledState(isDisabled: boolean): void { - this.isDisabled = isDisabled; - - this.changeDetector.markForCheck(); - } - - public registerOnChange(fn: any) { - this.callChange = fn; - } - - public registerOnTouched(fn: any) { - this.callTouched = fn; - } - - public onBarMouseClick(event: MouseEvent): boolean { - if (this.windowMouseMoveListener) { - return true; - } - - const relativeValue = this.getRelativeX(event); - - this.value = Math.round((relativeValue * (this.max - this.min) + this.min) / this.step) * this.step; - - this.updateThumbPosition(); - this.updateTouched(); - this.updateValue(); - - return false; - } - - public onThumbMouseDown(event: MouseEvent): boolean { - this.centerStartOffset = event.offsetX - this.thumb.nativeElement.clientWidth * 0.5; - - this.windowMouseMoveListener = - this.renderer.listen('window', 'mousemove', (e: MouseEvent) => { - this.onMouseMove(e); - }); - - this.windowMouseUpListener = - this.renderer.listen('window', 'mouseup', () => { - this.onMouseUp(); - }); - - this.renderer.addClass(this.thumb.nativeElement, 'focused'); - - this.isDragging = true; - - return false; - } - - private onMouseMove(event: MouseEvent): boolean { - if (!this.isDragging) { - return true; - } - - const relativeValue = this.getRelativeX(event); - - this.value = Math.round((relativeValue * (this.max - this.min) + this.min) / this.step) * this.step; - - this.updateThumbPosition(); - this.updateTouched(); - - return false; - } - - private onMouseUp(): boolean { - this.updateValue(); - - setTimeout(() => { - this.releaseMouseHandlers(); - this.renderer.removeClass(this.thumb.nativeElement, 'focused'); - }, 10); - - this.centerStartOffset = 0; - - this.isDragging = false; - - return false; - } - - private getRelativeX(event: MouseEvent): number { - const parentOffsetX = this.getParentX(event, this.bar.nativeElement) - this.centerStartOffset; - const parentWidth = this.bar.nativeElement.clientWidth; - - const relativeValue = Math.min(1, Math.max(0, (parentOffsetX - this.centerStartOffset) / parentWidth)); - - return relativeValue; - } - - private getParentX(e: any, container: any): number { - const rect = container.getBoundingClientRect(); - - const x = - !!e.touches ? - e.touches[0].pageX : - e.pageX; - - return x - rect.left; - } - - private updateTouched() { - this.callTouched(); - } - - private updateValue() { - if (this.lastValue !== this.value) { - this.lastValue = this.value; - - this.callChange(this.value); - } - } - - private updateThumbPosition() { - const relativeValue = Math.min(1, Math.max(0, (this.value - this.min) / (this.max - this.min))); - - this.renderer.setStyle(this.thumb.nativeElement, 'left', relativeValue * 100 + '%'); - } - - private releaseMouseHandlers() { - if (this.windowMouseMoveListener) { - this.windowMouseMoveListener(); - this.windowMouseMoveListener = null; - } - - if (this.windowMouseUpListener) { - this.windowMouseUpListener(); - this.windowMouseUpListener = null; - } - - this.isDragging = false; - } -} \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/forms/stars.component.ts b/src/Squidex/app/framework/angular/forms/stars.component.ts index 3a9772f9b..75761a388 100644 --- a/src/Squidex/app/framework/angular/forms/stars.component.ts +++ b/src/Squidex/app/framework/angular/forms/stars.component.ts @@ -8,15 +8,13 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, Input } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { StatefulComponent, Types } from '@app/framework/internal'; +import { StatefulControlComponent, Types } from '@app/framework/internal'; export const SQX_STARS_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => StarsComponent), multi: true }; interface State { - isDisabled: boolean; - stars: number; starsArray: number[]; @@ -30,9 +28,7 @@ interface State { providers: [SQX_STARS_CONTROL_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush }) -export class StarsComponent extends StatefulComponent implements ControlValueAccessor { - private callChange = (v: any) => { /* NOOP */ }; - private callTouched = () => { /* NOOP */ }; +export class StarsComponent extends StatefulControlComponent implements ControlValueAccessor { private maximumStarsValue = 5; @Input() @@ -58,7 +54,6 @@ export class StarsComponent extends StatefulComponent implements ControlV constructor(changeDetector: ChangeDetectorRef) { super(changeDetector, { - isDisabled: false, stars: -1, starsArray: [1, 2, 3, 4, 5], value: 1 @@ -71,18 +66,6 @@ export class StarsComponent extends StatefulComponent implements ControlV this.next({ stars: value, value }); } - public setDisabledState(isDisabled: boolean): void { - this.next({ isDisabled }); - } - - public registerOnChange(fn: any) { - this.callChange = fn; - } - - public registerOnTouched(fn: any) { - this.callTouched = fn; - } - public setPreview(stars: number) { if (this.snapshot.isDisabled) { return; diff --git a/src/Squidex/app/framework/angular/forms/tag-editor.component.ts b/src/Squidex/app/framework/angular/forms/tag-editor.component.ts index 0fa053881..32e0b96c4 100644 --- a/src/Squidex/app/framework/angular/forms/tag-editor.component.ts +++ b/src/Squidex/app/framework/angular/forms/tag-editor.component.ts @@ -5,12 +5,11 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Subscription } from 'rxjs'; import { distinctUntilChanged, map, tap } from 'rxjs/operators'; -import { Types } from '@app/framework/internal'; +import { StatefulControlComponent, Types } from '@app/framework/internal'; const KEY_COMMA = 188; const KEY_DELETE = 8; @@ -75,6 +74,15 @@ const CACHED_SIZES: { [key: string]: number } = {}; let CACHED_FONT: string; +interface State { + hasFocus: boolean; + + suggestedItems: string[]; + suggestedIndex: number; + + items: any[]; +} + @Component({ selector: 'sqx-tag-editor', styleUrls: ['./tag-editor.component.scss'], @@ -82,11 +90,7 @@ let CACHED_FONT: string; providers: [SQX_TAG_EDITOR_CONTROL_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush }) -export class TagEditorComponent implements AfterViewInit, ControlValueAccessor, OnDestroy, OnInit { - private subscription: Subscription; - private callChange = (v: any) => { /* NOOP */ }; - private callTouched = () => { /* NOOP */ }; - +export class TagEditorComponent extends StatefulControlComponent implements AfterViewInit, ControlValueAccessor, OnInit { @Input() public converter: Converter = new StringConverter(); @@ -115,27 +119,20 @@ export class TagEditorComponent implements AfterViewInit, ControlValueAccessor, public inputName = 'tag-editor'; @ViewChild('form') - public formElement: ElementRef; + public formElement: ElementRef; @ViewChild('input') public inputElement: ElementRef; - public hasFocus = false; - - public suggestedItems: string[] = []; - public suggestedIndex = 0; - - public items: any[] = []; - public addInput = new FormControl(); - constructor( - private readonly changeDetector: ChangeDetectorRef - ) { - } - - public ngOnDestroy() { - this.subscription.unsubscribe(); + constructor(changeDetector: ChangeDetectorRef) { + super(changeDetector, { + hasFocus: false, + suggestedItems: [], + suggestedIndex: 0, + items: [] + }); } public ngAfterViewInit() { @@ -149,7 +146,7 @@ export class TagEditorComponent implements AfterViewInit, ControlValueAccessor, } public ngOnInit() { - this.subscription = + this.observe( this.addInput.valueChanges.pipe( tap(() => { this.resetSize(); @@ -164,7 +161,7 @@ export class TagEditorComponent implements AfterViewInit, ControlValueAccessor, distinctUntilChanged(), map(query => { if (Types.isArray(this.suggestions) && query && query.length > 0) { - return this.suggestions.filter(s => s.indexOf(query) >= 0 && this.items.indexOf(s) < 0); + return this.suggestions.filter(s => s.indexOf(query) >= 0 && this.snapshot.items.indexOf(s) < 0); } else { return []; } @@ -172,7 +169,7 @@ export class TagEditorComponent implements AfterViewInit, ControlValueAccessor, .subscribe(items => { this.suggestedIndex = -1; this.suggestedItems = items || []; - }); + })); } public writeValue(obj: any) { @@ -189,6 +186,8 @@ export class TagEditorComponent implements AfterViewInit, ControlValueAccessor, } public setDisabledState(isDisabled: boolean): void { + super.setDisabledState(isDisabled); + if (isDisabled) { this.addInput.disable(); } else { @@ -196,17 +195,9 @@ export class TagEditorComponent implements AfterViewInit, ControlValueAccessor, } } - public registerOnChange(fn: any) { - this.callChange = fn; - } - - public registerOnTouched(fn: any) { - this.callTouched = fn; - } - public focus() { if (this.addInput.enabled) { - this.hasFocus = true; + this.next({ hasFocus: true }); } } @@ -396,7 +387,7 @@ export class TagEditorComponent implements AfterViewInit, ControlValueAccessor, } private updateItems(items: any[]) { - this.items = items; + const items = items; if (items.length === 0 && this.undefinedWhenEmpty) { this.callChange(undefined); diff --git a/src/Squidex/app/framework/angular/forms/toggle.component.ts b/src/Squidex/app/framework/angular/forms/toggle.component.ts index 94052ac3c..2fb6142dd 100644 --- a/src/Squidex/app/framework/angular/forms/toggle.component.ts +++ b/src/Squidex/app/framework/angular/forms/toggle.component.ts @@ -10,14 +10,13 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Types } from '@app/framework/internal'; -import { StatefulComponent } from '../stateful.component'; +import { StatefulControlComponent } from '../stateful.component'; export const SQX_TOGGLE_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ToggleComponent), multi: true }; interface State { - isDisabled: boolean; isChecked: boolean | null; } @@ -27,17 +26,13 @@ interface State { templateUrl: './toggle.component.html', providers: [SQX_TOGGLE_CONTROL_VALUE_ACCESSOR] }) -export class ToggleComponent extends StatefulComponent implements ControlValueAccessor { - private callChange = (v: any) => { /* NOOP */ }; - private callTouched = () => { /* NOOP */ }; - +export class ToggleComponent extends StatefulControlComponent implements ControlValueAccessor { @Input() public threeStates = false; constructor(changeDetector: ChangeDetectorRef) { super(changeDetector, { - isChecked: null, - isDisabled: false + isChecked: null }); } @@ -45,18 +40,6 @@ export class ToggleComponent extends StatefulComponent implements Control this.next({ isChecked: Types.isBoolean(obj) ? obj : null }); } - public setDisabledState(isDisabled: boolean): void { - this.next({ isDisabled }); - } - - public registerOnChange(fn: any) { - this.callChange = fn; - } - - public registerOnTouched(fn: any) { - this.callTouched = fn; - } - public changeState(event: MouseEvent) { let { isDisabled, isChecked } = this.snapshot; diff --git a/src/Squidex/app/framework/angular/stateful.component.ts b/src/Squidex/app/framework/angular/stateful.component.ts index 330ecaa34..4ce595620 100644 --- a/src/Squidex/app/framework/angular/stateful.component.ts +++ b/src/Squidex/app/framework/angular/stateful.component.ts @@ -6,6 +6,7 @@ */ import { ChangeDetectorRef, OnDestroy, OnInit } from '@angular/core'; +import { ControlValueAccessor } from '@angular/forms'; import { Subscription } from 'rxjs'; import { Types } from './../utils/types'; @@ -51,8 +52,74 @@ export abstract class StatefulComponent extends State implements OnDestroy } } +export interface FormControlState { + isDisabled: boolean; +} + +export abstract class StatefulControlComponent extends StatefulComponent implements ControlValueAccessor { + private fnChanged = (v: any) => { /* NOOP */ }; + private fnTouched = () => { /* NOOP */ }; + + constructor(changeDetector: ChangeDetectorRef, state: T) { + super(changeDetector, { ...state, isDisabled: false }); + } + + public registerOnChange(fn: any) { + this.fnChanged = fn; + } + + public registerOnTouched(fn: any) { + this.fnTouched = fn; + } + + public callTouched() { + this.fnTouched(); + } + + public callChange(value: TValue | null | undefined) { + this.fnChanged(value); + } + + public setDisabledState(isDisabled: boolean): void { + this.next(state => { state.isDisabled = isDisabled; }); + } + + public abstract writeValue(obj: any): void; +} + export abstract class PureComponent extends StatefulComponent { constructor(changeDetector: ChangeDetectorRef) { super(changeDetector, {}); } +} + +export abstract class ExternalControlComponent extends PureComponent implements ControlValueAccessor { + private fnChanged = (v: any) => { /* NOOP */ }; + private fnTouched = () => { /* NOOP */ }; + + constructor(changeDetector: ChangeDetectorRef) { + super(changeDetector); + + changeDetector.detach(); + } + + public registerOnChange(fn: any) { + this.fnChanged = fn; + } + + public registerOnTouched(fn: any) { + this.fnTouched = fn; + } + + protected callTouched() { + this.fnTouched(); + } + + protected callChange(value: TValue) { + this.fnChanged(value); + } + + public abstract setDisabledState(isDisabled: boolean): void; + + public abstract writeValue(obj: any): void; } \ No newline at end of file diff --git a/src/Squidex/app/framework/declarations.ts b/src/Squidex/app/framework/declarations.ts index f8ebdfc58..443df2412 100644 --- a/src/Squidex/app/framework/declarations.ts +++ b/src/Squidex/app/framework/declarations.ts @@ -20,7 +20,6 @@ export * from './angular/forms/iframe-editor.component'; export * from './angular/forms/indeterminate-value.directive'; export * from './angular/forms/json-editor.component'; export * from './angular/forms/progress-bar.component'; -export * from './angular/forms/slider.component'; export * from './angular/forms/stars.component'; export * from './angular/forms/tag-editor.component'; export * from './angular/forms/toggle.component'; diff --git a/src/Squidex/app/framework/module.ts b/src/Squidex/app/framework/module.ts index 823abd164..0e65813a3 100644 --- a/src/Squidex/app/framework/module.ts +++ b/src/Squidex/app/framework/module.ts @@ -73,7 +73,6 @@ import { ShortcutService, ShortDatePipe, ShortTimePipe, - SliderComponent, SortedDirective, StarsComponent, TagEditorComponent, @@ -143,7 +142,6 @@ import { ShortcutComponent, ShortDatePipe, ShortTimePipe, - SliderComponent, SortedDirective, StarsComponent, TagEditorComponent, @@ -208,7 +206,6 @@ import { ShortcutComponent, ShortDatePipe, ShortTimePipe, - SliderComponent, SortedDirective, StarsComponent, TagEditorComponent,