diff --git a/ui-ngx/package.json b/ui-ngx/package.json index f23e3f0b9a..2c29990466 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -31,6 +31,7 @@ "@flowjs/flow.js": "^2.14.1", "@flowjs/ngx-flow": "~0.6.0", "@geoman-io/leaflet-geoman-free": "^2.13.0", + "@iplab/ngx-color-picker": "^15.0.2", "@juggle/resize-observer": "^3.4.0", "@mat-datetimepicker/core": "~11.0.3", "@material-ui/core": "4.12.3", @@ -73,7 +74,6 @@ "moment": "^2.29.4", "moment-timezone": "^0.5.42", "ngx-clipboard": "^15.1.0", - "ngx-color-picker": "^14.0.0", "ngx-daterangepicker-material": "^6.0.4", "ngx-drag-drop": "^15.0.1", "ngx-flowchart": "https://github.com/thingsboard/ngx-flowchart.git#release/2.0.0", diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.html b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.html new file mode 100644 index 0000000000..d1432d27f5 --- /dev/null +++ b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.html @@ -0,0 +1,41 @@ + + + +
+ + +
+ + +
+
+ +
+
+ + + +
+
+
diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss new file mode 100644 index 0000000000..f71707195a --- /dev/null +++ b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss @@ -0,0 +1,104 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + width: 100%; + display: flex; + flex-direction: column; + gap: 16px; + + .saturation-component { + height: 100%; + min-height: 200px; + max-height: 300px; + border-radius: 8px; + } + + .control-component { + max-height: 48px; + display: flex; + gap: 16px; + + .indicator-component { + height: 48px; + width: 48px; + border-radius: 8px; + background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAAAAAClZ7nPAAAAAnRSTlMUAFg9Gm0AAAAPSURBVAjXY2D4jxXhEgYAfr8P8QhChVEAAAAASUVORK5CYII=') repeat; + } + + .hue-alpha-range { + display: flex; + justify-content: space-between; + flex-direction: column; + flex: 1; + + > * { + height: 18px; + overflow: hidden; + border-radius: 9px; + border: 1px solid rgba(0, 0, 0, 0.1); + } + } + } + + .color-input-block { + display: flex; + + .color-input { + flex: 1; + } + + .type-btn { + height: 26px; + width: 20px; + background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAgCAMAAAAootjDAAAAM1BMVEUAAAAzMzMzMzMzMzMzMzM0NDQzMzMzMzM0NDQzMzMzMzM0NDQzMzMzMzMyMjIrKyszMzPF8UZlAAAAEHRSTlMA1fHr4ZxxSRP45sG+sCkGH2+Z6QAAAHJJREFUKM+9kkkSgCAQA0FEVLb5/2tViqgQvNrHviSzKGCt6nDGuNass8i8NsrLiX+bZbrUtDwm7VLYE0zWUtEZ+RvUZpEvN8YhH9QmQRoC8kFpEnVHVP/DJUZVeSAem5fDKxwtms/BR+PT8gN8vwk/0wE1gQzNVYryIwAAAABJRU5ErkJggg==') no-repeat center; + background-size: 6px 12px; + + &:hover { + background-color: #eee; + } + } + } +} + +:host ::ng-deep { + .saturation-component { + .pointer { + border-width: 2px; + width: 16px; + height: 16px; + } + } + + indicator-component { + svg { + vertical-align: -70% !important; + } + } + + .hue-alpha-range { + alpha-component, hue-component { + .pointer { + height: 18px; + width: 18px; + background: none; + border: 2px solid #fff; + } + .gradient-color { + border-radius: 9px; + } + } + } +} diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.ts b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.ts new file mode 100644 index 0000000000..ce49310b03 --- /dev/null +++ b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.ts @@ -0,0 +1,124 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, OnDestroy } from '@angular/core'; +import { Color, ColorPickerControl } from '@iplab/ngx-color-picker'; +import { Subscription } from 'rxjs'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +export enum ColorType { + hex = 'hex', + hexa = 'hexa', + rgba = 'rgba', + rgb = 'rgb', + hsla = 'hsla', + hsl = 'hsl', + cmyk = 'cmyk' +} + +@Component({ + selector: `tb-color-picker`, + templateUrl: `./color-picker.component.html`, + styleUrls: [`./color-picker.component.scss`], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ColorPickerComponent), + multi: true + } + ] +}) +export class ColorPickerComponent implements ControlValueAccessor, OnDestroy { + + selectedPresentation = 0; + presentations = [ColorType.hex, ColorType.rgba, ColorType.hsla]; + control = new ColorPickerControl(); + + private modelValue: string; + + private subscriptions: Array = []; + + private propagateChange = (v: any) => {}; + + private setValue = false; + + constructor() { + this.subscriptions.push( + this.control.valueChanges.subscribe(() => { + if (!this.setValue) { + this.updateModel(); + } else { + this.setValue = false; + } + }) + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + writeValue(value: string): void { + this.setValue = !!value; + this.control.setValueFrom(value || '#fff'); + this.modelValue = value; + + if (this.control.initType === ColorType.hexa) { + this.control.initType = ColorType.hex; + } else if (this.control.initType === ColorType.rgb) { + this.control.initType = ColorType.rgba; + } else if (this.control.initType === ColorType.hsl) { + this.control.initType = ColorType.hsla; + } + + this.selectedPresentation = this.presentations.indexOf(this.control.initType); + } + + private updateModel() { + const color: string = this.getValueByType(this.control.value, this.presentations[this.selectedPresentation]); + if (this.modelValue !== color) { + this.modelValue = color; + this.propagateChange(color); + } + } + + public ngOnDestroy(): void { + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); + this.subscriptions.length = 0; + } + + public changePresentation(): void { + this.selectedPresentation = + this.selectedPresentation === this.presentations.length - 1 ? 0 : this.selectedPresentation + 1; + this.updateModel(); + } + + getValueByType(color: Color, type: ColorType): string { + switch (type) { + case ColorType.hex: + return color.toHexString(this.control.value.getRgba().getAlpha() !== 1); + case ColorType.rgba: + return this.control.value.getRgba().getAlpha() !== 1 ? color.toRgbaString() : color.toRgbString(); + case ColorType.hsla: + return this.control.value.getRgba().getAlpha() !== 1 ? color.toHslaString() : color.toHslString(); + default: + return color.toRgbaString(); + } + } +} diff --git a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.html b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.html index a7d4835547..eaec4c0b5e 100644 --- a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.html +++ b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.html @@ -15,14 +15,9 @@ limitations under the License. --> -
-
- - + +
+
diff --git a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts index e986ddc331..44959023f4 100644 --- a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts @@ -14,12 +14,11 @@ /// limitations under the License. /// -import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; -import { ErrorStateMatcher } from '@angular/material/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { DialogComponent } from '@shared/components/dialog.component'; @@ -30,22 +29,18 @@ export interface ColorPickerDialogData { @Component({ selector: 'tb-color-picker-dialog', templateUrl: './color-picker-dialog.component.html', - providers: [{provide: ErrorStateMatcher, useExisting: ColorPickerDialogComponent}], styleUrls: [] }) export class ColorPickerDialogComponent extends DialogComponent - implements OnInit, ErrorStateMatcher { + implements OnInit { - colorPickerFormGroup: UntypedFormGroup; - - submitted = false; + colorPickerFormGroup: FormGroup; constructor(protected store: Store, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: ColorPickerDialogData, - @SkipSelf() private errorStateMatcher: ErrorStateMatcher, public dialogRef: MatDialogRef, - public fb: UntypedFormBuilder) { + public fb: FormBuilder) { super(store, router, dialogRef); } @@ -55,23 +50,11 @@ export class ColorPickerDialogComponent extends DialogComponent=6.0.0" tslib "^2.0.0" -ngx-color-picker@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/ngx-color-picker/-/ngx-color-picker-14.0.0.tgz#4587f517ac5683a705d4e55cd0939afa91faa853" - integrity sha512-w28zx2DyVpIJeNsTB3T2LUI4Ed/Ujf5Uhxuh0dllputfpxXwZG9ocSJM/0L67+fxA3UnfvvXVZNUX1Ny5nZIIw== - dependencies: - tslib "^2.3.0" - ngx-daterangepicker-material@^6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/ngx-daterangepicker-material/-/ngx-daterangepicker-material-6.0.4.tgz#4e2432698ded28021a5fc0b0025dd1e0a3a13412"