Browse Source

UI: Improve color picker

pull/8946/head
Igor Kulikov 3 years ago
parent
commit
b74e52d14a
  1. 3
      ui-ngx/src/app/core/services/dialog.service.ts
  2. 6
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss
  3. 27
      ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.html
  4. 27
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.html
  5. 22
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.html
  6. 27
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.html
  7. 27
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.html
  8. 11
      ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.html
  9. 2
      ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.html
  10. 32
      ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts
  11. 11
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.html
  12. 11
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-latest-key-settings.component.html
  13. 54
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.html
  14. 27
      ui-ngx/src/app/modules/home/components/widget/widget-config.component.html
  15. 13
      ui-ngx/src/app/shared/components/color-input.component.html
  16. 9
      ui-ngx/src/app/shared/components/color-input.component.scss
  17. 42
      ui-ngx/src/app/shared/components/color-input.component.ts
  18. 30
      ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.html
  19. 36
      ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.scss
  20. 55
      ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.ts
  21. 14
      ui-ngx/src/app/shared/components/color-picker/color-picker.component.html
  22. 39
      ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss
  23. 27
      ui-ngx/src/app/shared/components/color-picker/color-picker.component.ts
  24. 30
      ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.html
  25. 22
      ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.scss
  26. 24
      ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts
  27. 3
      ui-ngx/src/app/shared/shared.module.ts
  28. 3
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  29. 53
      ui-ngx/src/form.scss

3
ui-ngx/src/app/core/services/dialog.service.ts

@ -103,7 +103,8 @@ export class DialogService {
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: { data: {
color color
} },
autoFocus: false
}).afterClosed(); }).afterClosed();
} }

6
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss

@ -45,9 +45,3 @@
margin-right: 8px; margin-right: 8px;
} }
} }
.drop-down-icon {
&.inline {
margin-right: -12px;
}
}

27
ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.html

@ -58,16 +58,15 @@
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}"> <input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="showTitleIcon"> <mat-slide-toggle class="mat-slide" formControlName="showTitleIcon">
{{ 'widget-config.card-icon' | translate }} {{ 'widget-config.card-icon' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-material-icon-select asBoxInput <tb-material-icon-select asBoxInput
[color]="alarmsTableWidgetConfigForm.get('iconColor').value" [color]="alarmsTableWidgetConfigForm.get('iconColor').value"
formControlName="titleIcon"> formControlName="titleIcon">
</tb-material-icon-select> </tb-material-icon-select>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput <tb-color-input asBoxInput
formControlName="iconColor"> formControlName="iconColor">
</tb-color-input> </tb-color-input>
@ -82,23 +81,17 @@
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option> <mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox> </mat-chip-listbox>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.text-color' | translate }}</div> <div>{{ 'widget-config.text-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="color">
<tb-color-input asBoxInput </tb-color-input>
formControlName="color">
</tb-color-input>
</div>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.background-color' | translate }}</div> <div>{{ 'widget-config.background-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="backgroundColor">
<tb-color-input asBoxInput </tb-color-input>
formControlName="backgroundColor">
</tb-color-input>
</div>
</div> </div>
</div> </div>
<tb-widget-actions-panel <tb-widget-actions-panel

27
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.html

@ -47,16 +47,15 @@
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}"> <input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="showTitleIcon"> <mat-slide-toggle class="mat-slide" formControlName="showTitleIcon">
{{ 'widget-config.card-icon' | translate }} {{ 'widget-config.card-icon' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-material-icon-select asBoxInput <tb-material-icon-select asBoxInput
[color]="entitiesTableWidgetConfigForm.get('iconColor').value" [color]="entitiesTableWidgetConfigForm.get('iconColor').value"
formControlName="titleIcon"> formControlName="titleIcon">
</tb-material-icon-select> </tb-material-icon-select>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput <tb-color-input asBoxInput
formControlName="iconColor"> formControlName="iconColor">
</tb-color-input> </tb-color-input>
@ -70,23 +69,17 @@
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option> <mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox> </mat-chip-listbox>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.text-color' | translate }}</div> <div>{{ 'widget-config.text-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="color">
<tb-color-input asBoxInput </tb-color-input>
formControlName="color">
</tb-color-input>
</div>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.background-color' | translate }}</div> <div>{{ 'widget-config.background-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="backgroundColor">
<tb-color-input asBoxInput </tb-color-input>
formControlName="backgroundColor">
</tb-color-input>
</div>
</div> </div>
</div> </div>
<tb-widget-actions-panel <tb-widget-actions-panel

22
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.html

@ -67,23 +67,17 @@
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option> <mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox> </mat-chip-listbox>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.text-color' | translate }}</div> <div>{{ 'widget-config.text-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="color">
<tb-color-input asBoxInput </tb-color-input>
formControlName="color">
</tb-color-input>
</div>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.background' | translate }}</div> <div>{{ 'widget-config.background' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="backgroundColor">
<tb-color-input asBoxInput </tb-color-input>
formControlName="backgroundColor">
</tb-color-input>
</div>
</div> </div>
</div> </div>
<tb-widget-actions-panel <tb-widget-actions-panel

27
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.html

@ -47,16 +47,15 @@
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}"> <input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="showTitleIcon"> <mat-slide-toggle class="mat-slide" formControlName="showTitleIcon">
{{ 'widget-config.card-icon' | translate }} {{ 'widget-config.card-icon' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-material-icon-select asBoxInput <tb-material-icon-select asBoxInput
[color]="timeseriesTableWidgetConfigForm.get('iconColor').value" [color]="timeseriesTableWidgetConfigForm.get('iconColor').value"
formControlName="titleIcon"> formControlName="titleIcon">
</tb-material-icon-select> </tb-material-icon-select>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput <tb-color-input asBoxInput
formControlName="iconColor"> formControlName="iconColor">
</tb-color-input> </tb-color-input>
@ -70,23 +69,17 @@
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option> <mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox> </mat-chip-listbox>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.text-color' | translate }}</div> <div>{{ 'widget-config.text-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="color">
<tb-color-input asBoxInput </tb-color-input>
formControlName="color">
</tb-color-input>
</div>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.background-color' | translate }}</div> <div>{{ 'widget-config.background-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="backgroundColor">
<tb-color-input asBoxInput </tb-color-input>
formControlName="backgroundColor">
</tb-color-input>
</div>
</div> </div>
</div> </div>
<tb-widget-actions-panel <tb-widget-actions-panel

27
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.html

@ -47,16 +47,15 @@
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}"> <input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="showTitleIcon"> <mat-slide-toggle class="mat-slide" formControlName="showTitleIcon">
{{ 'widget-config.card-icon' | translate }} {{ 'widget-config.card-icon' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-material-icon-select asBoxInput <tb-material-icon-select asBoxInput
[color]="flotWidgetConfigForm.get('iconColor').value" [color]="flotWidgetConfigForm.get('iconColor').value"
formControlName="titleIcon"> formControlName="titleIcon">
</tb-material-icon-select> </tb-material-icon-select>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput <tb-color-input asBoxInput
formControlName="iconColor"> formControlName="iconColor">
</tb-color-input> </tb-color-input>
@ -68,23 +67,17 @@
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option> <mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox> </mat-chip-listbox>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.text-color' | translate }}</div> <div>{{ 'widget-config.text-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="color">
<tb-color-input asBoxInput </tb-color-input>
formControlName="color">
</tb-color-input>
</div>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.background-color' | translate }}</div> <div>{{ 'widget-config.background-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="backgroundColor">
<tb-color-input asBoxInput </tb-color-input>
formControlName="backgroundColor">
</tb-color-input>
</div>
</div> </div>
</div> </div>
<div class="tb-form-panel"> <div class="tb-form-panel">

11
ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.html

@ -59,14 +59,11 @@
</mat-form-field> </mat-form-field>
</div> </div>
</ng-container> </ng-container>
<div class="tb-form-row space-between same-padding" *ngIf="!hideDataKeyColor"> <div class="tb-form-row space-between" *ngIf="!hideDataKeyColor">
<div>{{ 'datakey.color' | translate }}</div> <div>{{ 'datakey.color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="color">
<tb-color-input asBoxInput </tb-color-input>
formControlName="color">
</tb-color-input>
</div>
</div> </div>
<ng-container *ngIf="widgetType === widgetTypes.latest && modelValue.type === dataKeyTypes.timeseries"> <ng-container *ngIf="widgetType === widgetTypes.latest && modelValue.type === dataKeyTypes.timeseries">
<mat-form-field subscriptSizing="dynamic"> <mat-form-field subscriptSizing="dynamic">

2
ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.html

@ -72,7 +72,7 @@
</div> </div>
</div> </div>
<div *ngIf="!hideDataKeyColor" style="padding: 3px;"> <div *ngIf="!hideDataKeyColor" style="padding: 3px;">
<div class="tb-color-preview small box" (click)="showColorPicker(key)"> <div #keyColorButton class="tb-color-preview small box" (click)="openColorPickerPopup(key, $event, keyColorButton)">
<div class="tb-color-result" [ngStyle]="{background: key.color}"></div> <div class="tb-color-result" [ngStyle]="{background: key.color}"></div>
</div> </div>
</div> </div>

32
ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts

@ -27,6 +27,7 @@ import {
SimpleChanges, SimpleChanges,
SkipSelf, SkipSelf,
ViewChild, ViewChild,
ViewContainerRef,
ViewEncapsulation ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { import {
@ -56,7 +57,6 @@ import { alarmFields } from '@shared/models/alarm.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { ErrorStateMatcher } from '@angular/material/core'; import { ErrorStateMatcher } from '@angular/material/core';
import { TruncatePipe } from '@shared/pipe/truncate.pipe'; import { TruncatePipe } from '@shared/pipe/truncate.pipe';
import { DialogService } from '@core/services/dialog.service';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { import {
DataKeyConfigDialogComponent, DataKeyConfigDialogComponent,
@ -69,6 +69,8 @@ import { DndDropEvent } from 'ngx-drag-drop/lib/dnd-dropzone.directive';
import { moveItemInArray } from '@angular/cdk/drag-drop'; import { moveItemInArray } from '@angular/cdk/drag-drop';
import { coerceBoolean } from '@shared/decorators/coercion'; import { coerceBoolean } from '@shared/decorators/coercion';
import { DatasourceComponent } from '@home/components/widget/config/datasource.component'; import { DatasourceComponent } from '@home/components/widget/config/datasource.component';
import { ColorPickerPanelComponent } from '@shared/components/color-picker/color-picker-panel.component';
import { TbPopoverService } from '@shared/components/popover.service';
@Component({ @Component({
selector: 'tb-data-keys', selector: 'tb-data-keys',
@ -208,10 +210,11 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
private datasourceComponent: DatasourceComponent, private datasourceComponent: DatasourceComponent,
public translate: TranslateService, public translate: TranslateService,
private utils: UtilsService, private utils: UtilsService,
private dialogs: DialogService,
private dialog: MatDialog, private dialog: MatDialog,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private popoverService: TbPopoverService,
private viewContainerRef: ViewContainerRef,
private renderer: Renderer2, private renderer: Renderer2,
public truncate: TruncatePipe) { public truncate: TruncatePipe) {
} }
@ -471,15 +474,30 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
this.propagateChange(this.modelValue); this.propagateChange(this.modelValue);
} }
showColorPicker(key: DataKey) { openColorPickerPopup(key: DataKey, $event: Event, keyColorButton: HTMLDivElement) {
this.dialogs.colorPicker(key.color).subscribe( if ($event) {
(color) => { $event.stopPropagation();
}
const trigger = keyColorButton;
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const colorPickerPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, ColorPickerPanelComponent, 'left', true, null,
{
color: key.color
},
{},
{}, {}, true);
colorPickerPopover.tbComponentRef.instance.popover = colorPickerPopover;
colorPickerPopover.tbComponentRef.instance.colorSelected.subscribe((color) => {
colorPickerPopover.hide();
if (color && key.color !== color) { if (color && key.color !== color) {
key.color = color; key.color = color;
this.propagateChange(this.modelValue); this.propagateChange(this.modelValue);
} }
} });
); }
} }
editDataKey(key: DataKey, index: number) { editDataKey(key: DataKey, index: number) {

11
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.html

@ -235,14 +235,11 @@
<input matInput formControlName="comparisonValuesLabel" placeholder="{{ 'widget-config.set' | translate }}"> <input matInput formControlName="comparisonValuesLabel" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widgets.chart.comparison-line-color' | translate }}</div> <div>{{ 'widgets.chart.comparison-line-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="color">
<tb-color-input asBoxInput </tb-color-input>
formControlName="color">
</tb-color-input>
</div>
</div> </div>
</ng-template> </ng-template>
</mat-expansion-panel> </mat-expansion-panel>

11
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-latest-key-settings.component.html

@ -36,14 +36,11 @@
<span matSuffix>px</span> <span matSuffix>px</span>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widgets.chart.threshold-color' | translate }}</div> <div>{{ 'widgets.chart.threshold-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="thresholdColor">
<tb-color-input asBoxInput </tb-color-input>
formControlName="thresholdColor">
</tb-color-input>
</div>
</div> </div>
</ng-template> </ng-template>
</mat-expansion-panel> </mat-expansion-panel>

54
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.html

@ -61,14 +61,13 @@
<input matInput formControlName="thresholdsLineWidth" type="number" min="0" placeholder="{{ 'widget-config.set' | translate }}"> <input matInput formControlName="thresholdsLineWidth" type="number" min="0" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widgets.chart.default-font' | translate }}</div> <div>{{ 'widgets.chart.default-font' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> <mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="fontSize" type="number" min="0" placeholder="{{ 'widget-config.set' | translate }}"> <input matInput formControlName="fontSize" type="number" min="0" placeholder="{{ 'widget-config.set' | translate }}">
<span matSuffix>px</span> <span matSuffix>px</span>
</mat-form-field> </mat-form-field>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput <tb-color-input asBoxInput
formControlName="fontColor"> formControlName="fontColor">
</tb-color-input> </tb-color-input>
@ -132,14 +131,11 @@
</mat-panel-description> </mat-panel-description>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.color' | translate }}</div> <div>{{ 'widget-config.color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="color">
<tb-color-input asBoxInput </tb-color-input>
formControlName="color">
</tb-color-input>
</div>
</div> </div>
<div class="tb-form-row space-between"> <div class="tb-form-row space-between">
<div translate>widget-config.decimals-short</div> <div translate>widget-config.decimals-short</div>
@ -187,14 +183,11 @@
</mat-panel-description> </mat-panel-description>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.color' | translate }}</div> <div>{{ 'widget-config.color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="color">
<tb-color-input asBoxInput </tb-color-input>
formControlName="color">
</tb-color-input>
</div>
</div> </div>
</ng-template> </ng-template>
</mat-expansion-panel> </mat-expansion-panel>
@ -213,36 +206,29 @@
{{ 'widgets.chart.horizontal-grid-lines' | translate }} {{ 'widgets.chart.horizontal-grid-lines' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widgets.chart.grid-lines-color' | translate }}</div> <div>{{ 'widgets.chart.grid-lines-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="tickColor">
<tb-color-input asBoxInput </tb-color-input>
formControlName="tickColor">
</tb-color-input>
</div>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widgets.chart.border' | translate }}</div> <div>{{ 'widgets.chart.border' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> <mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="outlineWidth" type="number" min="0" placeholder="{{ 'widget-config.set' | translate }}"> <input matInput formControlName="outlineWidth" type="number" min="0" placeholder="{{ 'widget-config.set' | translate }}">
<span matSuffix>px</span> <span matSuffix>px</span>
</mat-form-field> </mat-form-field>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput <tb-color-input asBoxInput
formControlName="color"> formControlName="color">
</tb-color-input> </tb-color-input>
</div> </div>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widgets.chart.background-color' | translate }}</div> <div>{{ 'widgets.chart.background-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="backgroundColor">
<tb-color-input asBoxInput </tb-color-input>
formControlName="backgroundColor">
</tb-color-input>
</div>
</div> </div>
</div> </div>
<div class="tb-form-panel tb-slide-toggle"> <div class="tb-form-panel tb-slide-toggle">

27
ui-ngx/src/app/modules/home/components/widget/widget-config.component.html

@ -48,11 +48,11 @@
<input matInput formControlName="titleTooltip" placeholder="{{ 'widget-config.set' | translate }}"> <input matInput formControlName="titleTooltip" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="showTitleIcon"> <mat-slide-toggle class="mat-slide" formControlName="showTitleIcon">
{{ 'widget-config.display-icon' | translate }} {{ 'widget-config.display-icon' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-material-icon-select asBoxInput <tb-material-icon-select asBoxInput
[color]="widgetSettings.get('iconColor').value" [color]="widgetSettings.get('iconColor').value"
formControlName="titleIcon"> formControlName="titleIcon">
@ -60,7 +60,6 @@
<mat-form-field appearance="outline" subscriptSizing="dynamic"> <mat-form-field appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="iconSize"> <input matInput formControlName="iconSize">
</mat-form-field> </mat-form-field>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput <tb-color-input asBoxInput
formControlName="iconColor"> formControlName="iconColor">
</tb-color-input> </tb-color-input>
@ -84,23 +83,17 @@
</div> </div>
<div class="tb-form-panel"> <div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widget-config.card-style</div> <div class="tb-form-panel-title" translate>widget-config.card-style</div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.text-color' | translate }}</div> <div>{{ 'widget-config.text-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="color">
<tb-color-input asBoxInput </tb-color-input>
formControlName="color">
</tb-color-input>
</div>
</div> </div>
<div class="tb-form-row space-between same-padding"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.background-color' | translate }}</div> <div>{{ 'widget-config.background-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <tb-color-input asBoxInput
<mat-divider vertical></mat-divider> formControlName="backgroundColor">
<tb-color-input asBoxInput </tb-color-input>
formControlName="backgroundColor">
</tb-color-input>
</div>
</div> </div>
<div class="tb-form-row space-between"> <div class="tb-form-row space-between">
<div>{{ 'widget-config.padding' | translate }}</div> <div>{{ 'widget-config.padding' | translate }}</div>

13
ui-ngx/src/app/shared/components/color-input.component.html

@ -35,7 +35,14 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<ng-template #boxInput> <ng-template #boxInput>
<div class="tb-color-preview no-margin box" [ngClass]="{'disabled': disabled}" (click)="!disabled && showColorPicker($event)"> <button type="button"
<div class="tb-color-result" [ngStyle]="!disabled ? {background: colorFormGroup.get('color').value} : {}"></div> mat-stroked-button
</div> class="color-box"
[disabled]="disabled"
#matButton
(click)="openColorPickerPopup($event, matButton)">
<div class="tb-color-preview no-margin box" [ngClass]="{'disabled': disabled}">
<div class="tb-color-result" [ngStyle]="!disabled ? {background: colorFormGroup.get('color').value} : {}"></div>
</div>
</button>
</ng-template> </ng-template>

9
ui-ngx/src/app/shared/components/color-input.component.scss

@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
@import './../../../scss/mixins';
:host { :host {
.mat-mdc-form-field { .mat-mdc-form-field {
width: 100%; width: 100%;
@ -29,4 +32,10 @@
margin: 0; margin: 0;
} }
} }
button.mat-mdc-button-base.color-box {
width: 40px;
min-width: 40px;
height: 40px;
padding: 7px;
}
} }

42
ui-ngx/src/app/shared/components/color-input.component.ts

@ -14,15 +14,24 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { ChangeDetectorRef, Component, forwardRef, Input, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import {
ControlValueAccessor,
NG_VALUE_ACCESSOR,
UntypedFormBuilder,
UntypedFormGroup,
Validators
} from '@angular/forms';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { coerceBoolean } from '@shared/decorators/coercion'; import { coerceBoolean } from '@shared/decorators/coercion';
import { TbPopoverService } from '@shared/components/popover.service';
import { ColorPickerPanelComponent } from '@shared/components/color-picker/color-picker-panel.component';
import { MatButton } from '@angular/material/button';
@Component({ @Component({
selector: 'tb-color-input', selector: 'tb-color-input',
@ -100,6 +109,9 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private dialogs: DialogService, private dialogs: DialogService,
private translate: TranslateService, private translate: TranslateService,
private popoverService: TbPopoverService,
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private cd: ChangeDetectorRef) { private cd: ChangeDetectorRef) {
super(store); super(store);
@ -167,6 +179,32 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro
); );
} }
openColorPickerPopup($event: Event, matButton: MatButton) {
if ($event) {
$event.stopPropagation();
}
const trigger = matButton._elementRef.nativeElement;
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const colorPickerPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, ColorPickerPanelComponent, 'left', true, null,
{
color: this.colorFormGroup.get('color').value
},
{},
{}, {}, true);
colorPickerPopover.tbComponentRef.instance.popover = colorPickerPopover;
colorPickerPopover.tbComponentRef.instance.colorSelected.subscribe((color) => {
colorPickerPopover.hide();
this.colorFormGroup.patchValue(
{color}, {emitEvent: true}
);
this.cd.markForCheck();
});
}
}
clear() { clear() {
this.colorFormGroup.get('color').patchValue(null, {emitEvent: true}); this.colorFormGroup.get('color').patchValue(null, {emitEvent: true});
this.cd.markForCheck(); this.cd.markForCheck();

30
ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.html

@ -0,0 +1,30 @@
<!--
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.
-->
<div class="tb-color-picker-panel">
<div class="tb-color-picker-title" translate>color.color</div>
<tb-color-picker [formControl]="colorPickerControl"></tb-color-picker>
<div class="tb-color-picker-panel-buttons">
<button mat-raised-button
color="primary"
type="button"
(click)="selectColor()"
[disabled]="colorPickerControl.invalid || !colorPickerControl.dirty">
{{ 'action.select' | translate }}
</button>
</div>
</div>

36
ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.scss

@ -0,0 +1,36 @@
/**
* 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.
*/
.tb-color-picker-panel {
width: 328px;
display: flex;
flex-direction: column;
gap: 16px;
.tb-color-picker-title {
font-size: 16px;
font-weight: 500;
line-height: 24px;
letter-spacing: 0.25px;
color: rgba(0, 0, 0, 0.87);
}
.tb-color-picker-panel-buttons {
height: 60px;
display: flex;
flex-direction: row;
gap: 16px;
justify-content: flex-end;
align-items: flex-end;
}
}

55
ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.ts

@ -0,0 +1,55 @@
///
/// 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 { PageComponent } from '@shared/components/page.component';
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { UntypedFormControl } from '@angular/forms';
import { TbPopoverComponent } from '@shared/components/popover.component';
@Component({
selector: 'tb-color-picker-panel',
templateUrl: './color-picker-panel.component.html',
providers: [],
styleUrls: ['./color-picker-panel.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ColorPickerPanelComponent extends PageComponent implements OnInit {
@Input()
color: string;
@Input()
popover: TbPopoverComponent<ColorPickerPanelComponent>;
@Output()
colorSelected = new EventEmitter<string>();
colorPickerControl: UntypedFormControl;
constructor(protected store: Store<AppState>) {
super(store);
}
ngOnInit(): void {
this.colorPickerControl = new UntypedFormControl(this.color);
}
selectColor() {
this.colorSelected.emit(this.colorPickerControl.value);
}
}

14
ui-ngx/src/app/shared/components/color-picker/color-picker.component.html

@ -19,7 +19,7 @@
<div class="control-component"> <div class="control-component">
<indicator-component class="indicator-component" <indicator-component class="indicator-component"
[colorType]="presentations[selectedPresentation]" [colorType]="presentations[presentationControl.value]"
[color]="control.value"> [color]="control.value">
</indicator-component> </indicator-component>
<div class="hue-alpha-range"> <div class="hue-alpha-range">
@ -29,7 +29,12 @@
</div> </div>
<div class="color-input-block"> <div class="color-input-block">
<div class="color-input" [ngSwitch]="presentations[selectedPresentation]"> <mat-select class="presentation-select" [formControl]="presentationControl">
<mat-option [value]="0">HEX</mat-option>
<mat-option [value]="1">RGBA</mat-option>
<mat-option [value]="2">HSLA</mat-option>
</mat-select>
<div class="color-input" [ngSwitch]="presentations[presentationControl.value]">
<rgba-input-component *ngSwitchCase="'rgba'" label <rgba-input-component *ngSwitchCase="'rgba'" label
[(color)]="control.value" [(hue)]="control.hue"></rgba-input-component> [(color)]="control.value" [(hue)]="control.hue"></rgba-input-component>
<hsla-input-component *ngSwitchCase="'hsla'" label <hsla-input-component *ngSwitchCase="'hsla'" label
@ -37,5 +42,8 @@
<hex-input-component *ngSwitchCase="'hex'" label prefix="#" [(color)]="control.value" <hex-input-component *ngSwitchCase="'hex'" label prefix="#" [(color)]="control.value"
[(hue)]="control.hue"></hex-input-component> [(hue)]="control.hue"></hex-input-component>
</div> </div>
<div class="type-btn" (click)="changePresentation()"></div> </div>
<div class="color-presets-block">
<color-presets-component class="color-presets-component" columns="11" [colorPresets]="colorPresets" [(color)]="control.value" [(hue)]="control.hue"></color-presets-component>
</div> </div>

39
ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss

@ -17,12 +17,10 @@
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 32px;
.saturation-component { .saturation-component {
height: 100%; height: 238px;
min-height: 200px;
max-height: 300px;
border-radius: 8px; border-radius: 8px;
} }
@ -55,6 +53,12 @@
.color-input-block { .color-input-block {
display: flex; display: flex;
gap: 20px;
.presentation-select {
font-size: 14px;
width: 56px;
}
.color-input { .color-input {
flex: 1; flex: 1;
@ -63,16 +67,13 @@
color: initial; color: initial;
} }
} }
}
.type-btn { .color-presets-block {
height: 26px; .color-presets-component {
width: 20px; display: flex;
background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAgCAMAAAAootjDAAAAM1BMVEUAAAAzMzMzMzMzMzMzMzM0NDQzMzMzMzM0NDQzMzMzMzM0NDQzMzMzMzMyMjIrKyszMzPF8UZlAAAAEHRSTlMA1fHr4ZxxSRP45sG+sCkGH2+Z6QAAAHJJREFUKM+9kkkSgCAQA0FEVLb5/2tViqgQvNrHviSzKGCt6nDGuNass8i8NsrLiX+bZbrUtDwm7VLYE0zWUtEZ+RvUZpEvN8YhH9QmQRoC8kFpEnVHVP/DJUZVeSAem5fDKxwtms/BR+PT8gN8vwk/0wE1gQzNVYryIwAAAABJRU5ErkJggg==') no-repeat center; flex-direction: column;
background-size: 6px 12px; gap: 12px;
&:hover {
background-color: #eee;
}
} }
} }
} }
@ -105,4 +106,16 @@
} }
} }
} }
.color-presets-component {
.presets-row {
gap: 10px;
justify-content: space-between;
}
color-preset {
height: 20px;
width: 20px;
border-radius: 4px;
}
}
} }

27
ui-ngx/src/app/shared/components/color-picker/color-picker.component.ts

@ -17,7 +17,7 @@
import { Component, forwardRef, OnDestroy } from '@angular/core'; import { Component, forwardRef, OnDestroy } from '@angular/core';
import { Color, ColorPickerControl } from '@iplab/ngx-color-picker'; import { Color, ColorPickerControl } from '@iplab/ngx-color-picker';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms';
export enum ColorType { export enum ColorType {
hex = 'hex', hex = 'hex',
@ -29,6 +29,10 @@ export enum ColorType {
cmyk = 'cmyk' cmyk = 'cmyk'
} }
const colorPresetsHex =
['#435B63', '#F44336', '#E89623', '#F5DD00', '#8BC34A', '#4CAF50', '#009688', '#048AD3', '#673AB7', '#9C27B0', '#E91E63',
'#A1ADB1', '#F9A19B', '#FFD190', '#FFF59D', '#C5E1A4', '#A5D7A7', '#80CBC3', '#81C4E9', '#B39CDB', '#CD93D7', '#F48FB1'];
@Component({ @Component({
selector: `tb-color-picker`, selector: `tb-color-picker`,
templateUrl: `./color-picker.component.html`, templateUrl: `./color-picker.component.html`,
@ -43,10 +47,13 @@ export enum ColorType {
}) })
export class ColorPickerComponent implements ControlValueAccessor, OnDestroy { export class ColorPickerComponent implements ControlValueAccessor, OnDestroy {
selectedPresentation = 0;
presentations = [ColorType.hex, ColorType.rgba, ColorType.hsla]; presentations = [ColorType.hex, ColorType.rgba, ColorType.hsla];
control = new ColorPickerControl(); control = new ColorPickerControl();
presentationControl = new UntypedFormControl(0);
colorPresets: Color[] = colorPresetsHex.map(c => Color.from(c));
private modelValue: string; private modelValue: string;
private subscriptions: Array<Subscription> = []; private subscriptions: Array<Subscription> = [];
@ -65,6 +72,11 @@ export class ColorPickerComponent implements ControlValueAccessor, OnDestroy {
} }
}) })
); );
this.subscriptions.push(
this.presentationControl.valueChanges.subscribe(() => {
this.updateModel();
})
);
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -86,12 +98,11 @@ export class ColorPickerComponent implements ControlValueAccessor, OnDestroy {
} else if (this.control.initType === ColorType.hsl) { } else if (this.control.initType === ColorType.hsl) {
this.control.initType = ColorType.hsla; this.control.initType = ColorType.hsla;
} }
this.presentationControl.patchValue(this.presentations.indexOf(this.control.initType), {emitEvent: false});
this.selectedPresentation = this.presentations.indexOf(this.control.initType);
} }
private updateModel() { private updateModel() {
const color: string = this.getValueByType(this.control.value, this.presentations[this.selectedPresentation]); const color: string = this.getValueByType(this.control.value, this.presentations[this.presentationControl.value]);
if (this.modelValue !== color) { if (this.modelValue !== color) {
this.modelValue = color; this.modelValue = color;
this.propagateChange(color); this.propagateChange(color);
@ -103,12 +114,6 @@ export class ColorPickerComponent implements ControlValueAccessor, OnDestroy {
this.subscriptions.length = 0; 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 { getValueByType(color: Color, type: ColorType): string {
switch (type) { switch (type) {
case ColorType.hex: case ColorType.hex:

30
ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.html

@ -15,22 +15,14 @@
limitations under the License. limitations under the License.
--> -->
<form [formGroup]="colorPickerFormGroup" (ngSubmit)="select()" style="width: 320px"> <div mat-dialog-content>
<div mat-dialog-content style="padding: 16px; display: flex"> <button class="tb-close-button"
<tb-color-picker formControlName="color"></tb-color-picker> mat-icon-button
</div> (click)="cancel()"
<div mat-dialog-actions fxLayout="row"> type="button">
<span fxFlex></span> <mat-icon>close</mat-icon>
<button mat-button </button>
type="button" <tb-color-picker-panel [color]="color"
[disabled]="(isLoading$ | async)" (colorSelected)="selectColor($event)">
(click)="cancel()"> </tb-color-picker-panel>
{{ 'action.cancel' | translate }} </div>
</button>
<button mat-button
type="submit"
[disabled]="(isLoading$ | async) || colorPickerFormGroup.invalid || !colorPickerFormGroup.dirty">
{{ 'action.select' | translate }}
</button>
</div>
</form>

22
ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.scss

@ -0,0 +1,22 @@
/**
* 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 {
.tb-close-button {
position: absolute;
top: 6px;
right: 6px;
}
}

24
ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts

@ -14,11 +14,10 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Inject, OnInit } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
@ -29,33 +28,26 @@ export interface ColorPickerDialogData {
@Component({ @Component({
selector: 'tb-color-picker-dialog', selector: 'tb-color-picker-dialog',
templateUrl: './color-picker-dialog.component.html', templateUrl: './color-picker-dialog.component.html',
styleUrls: [] styleUrls: ['./color-picker-dialog.component.scss']
}) })
export class ColorPickerDialogComponent extends DialogComponent<ColorPickerDialogComponent, string> export class ColorPickerDialogComponent extends DialogComponent<ColorPickerDialogComponent, string> {
implements OnInit {
colorPickerFormGroup: FormGroup; color: string;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected router: Router, protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: ColorPickerDialogData, @Inject(MAT_DIALOG_DATA) public data: ColorPickerDialogData,
public dialogRef: MatDialogRef<ColorPickerDialogComponent, string>, public dialogRef: MatDialogRef<ColorPickerDialogComponent, string>) {
public fb: FormBuilder) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.color = data.color;
} }
ngOnInit(): void { selectColor(color: string) {
this.colorPickerFormGroup = this.fb.group({ this.dialogRef.close(color);
color: [this.data.color, [Validators.required]]
});
} }
cancel(): void { cancel(): void {
this.dialogRef.close(null); this.dialogRef.close(null);
} }
select(): void {
const color: string = this.colorPickerFormGroup.get('color').value;
this.dialogRef.close(color);
}
} }

3
ui-ngx/src/app/shared/shared.module.ts

@ -196,6 +196,7 @@ import { RuleChainSelectComponent } from '@shared/components/rule-chain/rule-cha
import { ToggleSelectComponent } from '@shared/components/toggle-select.component'; import { ToggleSelectComponent } from '@shared/components/toggle-select.component';
import { UnitInputComponent } from '@shared/components/unit-input.component'; import { UnitInputComponent } from '@shared/components/unit-input.component';
import { MaterialIconsComponent } from '@shared/components/material-icons.component'; import { MaterialIconsComponent } from '@shared/components/material-icons.component';
import { ColorPickerPanelComponent } from '@shared/components/color-picker/color-picker-panel.component';
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) { export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
return markedOptionsService; return markedOptionsService;
@ -365,6 +366,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
GtMdLgLayoutGapDirective, GtMdLgLayoutGapDirective,
GtMdLgShowHideDirective, GtMdLgShowHideDirective,
ColorPickerComponent, ColorPickerComponent,
ColorPickerPanelComponent,
ResourceAutocompleteComponent, ResourceAutocompleteComponent,
ToggleHeaderComponent, ToggleHeaderComponent,
ToggleOption, ToggleOption,
@ -597,6 +599,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
GtMdLgLayoutGapDirective, GtMdLgLayoutGapDirective,
GtMdLgShowHideDirective, GtMdLgShowHideDirective,
ColorPickerComponent, ColorPickerComponent,
ColorPickerPanelComponent,
ResourceAutocompleteComponent, ResourceAutocompleteComponent,
ToggleHeaderComponent, ToggleHeaderComponent,
ToggleOption, ToggleOption,

3
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -5500,6 +5500,9 @@
} }
} }
}, },
"color": {
"color": "Color"
},
"icon": { "icon": {
"icon": "Icon", "icon": "Icon",
"icons": "Icons", "icons": "Icons",

53
ui-ngx/src/form.scss

@ -131,14 +131,11 @@
} }
.tb-form-row { .tb-form-row {
height: 100%; height: 100%;
padding-top: 7px;
padding-bottom: 7px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
gap: 16px; gap: 16px;
padding-left: 16px; padding: 7px 7px 7px 16px;
padding-right: 12px;
border: 1px solid rgba(0, 0, 0, 0.12); border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 6px; border-radius: 6px;
&.same-padding { &.same-padding {
@ -314,33 +311,33 @@
.tb-prompt { .tb-prompt {
height: 38px; height: 38px;
} }
}
.tb-form-table-row { .tb-form-table-row {
height: 38px; height: 38px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
gap: 12px; gap: 12px;
padding-left: 12px; padding-left: 12px;
&.tb-draggable { &.tb-draggable {
gap: 0; gap: 0;
padding-left: 0; padding-left: 0;
background: #fff; background: #fff;
} }
&-cell-buttons { &-cell-buttons {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
button.mat-mdc-icon-button.mat-mdc-button-base { button.mat-mdc-icon-button.mat-mdc-button-base {
padding: 7px; padding: 7px;
width: 38px; width: 38px;
height: 38px; height: 38px;
.mat-icon { .mat-icon {
color: rgba(0, 0, 0, 0.38); color: rgba(0, 0, 0, 0.38);
} }
&.tb-hidden { &.tb-hidden {
visibility: hidden; visibility: hidden;
}
} }
} }
} }

Loading…
Cancel
Save