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,