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'],
data: {
color
}
},
autoFocus: false
}).afterClosed();
}

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

@ -45,9 +45,3 @@
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 }}">
</mat-form-field>
</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">
{{ 'widget-config.card-icon' | translate }}
</mat-slide-toggle>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-material-icon-select asBoxInput
[color]="alarmsTableWidgetConfigForm.get('iconColor').value"
formControlName="titleIcon">
</tb-material-icon-select>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="iconColor">
</tb-color-input>
@ -82,23 +81,17 @@
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
</div>
<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 }}">
</mat-form-field>
</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">
{{ 'widget-config.card-icon' | translate }}
</mat-slide-toggle>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-material-icon-select asBoxInput
[color]="entitiesTableWidgetConfigForm.get('iconColor').value"
formControlName="titleIcon">
</tb-material-icon-select>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="iconColor">
</tb-color-input>
@ -70,23 +69,17 @@
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
</div>
<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-listbox>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<div class="tb-form-row space-between same-padding">
<div class="tb-form-row space-between">
<div>{{ 'widget-config.background' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
</div>
<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 }}">
</mat-form-field>
</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">
{{ 'widget-config.card-icon' | translate }}
</mat-slide-toggle>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-material-icon-select asBoxInput
[color]="timeseriesTableWidgetConfigForm.get('iconColor').value"
formControlName="titleIcon">
</tb-material-icon-select>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="iconColor">
</tb-color-input>
@ -70,23 +69,17 @@
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
</div>
<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 }}">
</mat-form-field>
</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">
{{ 'widget-config.card-icon' | translate }}
</mat-slide-toggle>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-material-icon-select asBoxInput
[color]="flotWidgetConfigForm.get('iconColor').value"
formControlName="titleIcon">
</tb-material-icon-select>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="iconColor">
</tb-color-input>
@ -68,23 +67,17 @@
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
</div>
<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>
</div>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<ng-container *ngIf="widgetType === widgetTypes.latest && modelValue.type === dataKeyTypes.timeseries">
<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 *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>
</div>

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

@ -27,6 +27,7 @@ import {
SimpleChanges,
SkipSelf,
ViewChild,
ViewContainerRef,
ViewEncapsulation
} from '@angular/core';
import {
@ -56,7 +57,6 @@ import { alarmFields } from '@shared/models/alarm.models';
import { UtilsService } from '@core/services/utils.service';
import { ErrorStateMatcher } from '@angular/material/core';
import { TruncatePipe } from '@shared/pipe/truncate.pipe';
import { DialogService } from '@core/services/dialog.service';
import { MatDialog } from '@angular/material/dialog';
import {
DataKeyConfigDialogComponent,
@ -69,6 +69,8 @@ import { DndDropEvent } from 'ngx-drag-drop/lib/dnd-dropzone.directive';
import { moveItemInArray } from '@angular/cdk/drag-drop';
import { coerceBoolean } from '@shared/decorators/coercion';
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({
selector: 'tb-data-keys',
@ -208,10 +210,11 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
private datasourceComponent: DatasourceComponent,
public translate: TranslateService,
private utils: UtilsService,
private dialogs: DialogService,
private dialog: MatDialog,
private fb: UntypedFormBuilder,
private cd: ChangeDetectorRef,
private popoverService: TbPopoverService,
private viewContainerRef: ViewContainerRef,
private renderer: Renderer2,
public truncate: TruncatePipe) {
}
@ -471,15 +474,30 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
this.propagateChange(this.modelValue);
}
showColorPicker(key: DataKey) {
this.dialogs.colorPicker(key.color).subscribe(
(color) => {
openColorPickerPopup(key: DataKey, $event: Event, keyColorButton: HTMLDivElement) {
if ($event) {
$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) {
key.color = color;
this.propagateChange(this.modelValue);
}
}
);
});
}
}
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 }}">
</mat-form-field>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
</ng-template>
</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>
</mat-form-field>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="thresholdColor">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="thresholdColor">
</tb-color-input>
</div>
</ng-template>
</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 }}">
</mat-form-field>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="fontSize" type="number" min="0" placeholder="{{ 'widget-config.set' | translate }}">
<span matSuffix>px</span>
</mat-form-field>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="fontColor">
</tb-color-input>
@ -132,14 +131,11 @@
</mat-panel-description>
</mat-expansion-panel-header>
<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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<div class="tb-form-row space-between">
<div translate>widget-config.decimals-short</div>
@ -187,14 +183,11 @@
</mat-panel-description>
</mat-expansion-panel-header>
<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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
</ng-template>
</mat-expansion-panel>
@ -213,36 +206,29 @@
{{ 'widgets.chart.horizontal-grid-lines' | translate }}
</mat-slide-toggle>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="tickColor">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="tickColor">
</tb-color-input>
</div>
<div class="tb-form-row space-between same-padding">
<div class="tb-form-row space-between">
<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">
<input matInput formControlName="outlineWidth" type="number" min="0" placeholder="{{ 'widget-config.set' | translate }}">
<span matSuffix>px</span>
</mat-form-field>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
</div>
<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 }}">
</mat-form-field>
</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">
{{ 'widget-config.display-icon' | translate }}
</mat-slide-toggle>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-material-icon-select asBoxInput
[color]="widgetSettings.get('iconColor').value"
formControlName="titleIcon">
@ -60,7 +60,6 @@
<mat-form-field appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="iconSize">
</mat-form-field>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="iconColor">
</tb-color-input>
@ -84,23 +83,17 @@
</div>
<div class="tb-form-panel">
<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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</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 fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
<div class="tb-form-row space-between">
<div>{{ 'widget-config.padding' | translate }}</div>

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

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

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
* limitations under the License.
*/
@import './../../../scss/mixins';
:host {
.mat-mdc-form-field {
width: 100%;
@ -29,4 +32,10 @@
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.
///
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 { Store } from '@ngrx/store';
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 { coerceBooleanProperty } from '@angular/cdk/coercion';
import { DialogService } from '@core/services/dialog.service';
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({
selector: 'tb-color-input',
@ -100,6 +109,9 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro
constructor(protected store: Store<AppState>,
private dialogs: DialogService,
private translate: TranslateService,
private popoverService: TbPopoverService,
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef,
private fb: UntypedFormBuilder,
private cd: ChangeDetectorRef) {
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() {
this.colorFormGroup.get('color').patchValue(null, {emitEvent: true});
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">
<indicator-component class="indicator-component"
[colorType]="presentations[selectedPresentation]"
[colorType]="presentations[presentationControl.value]"
[color]="control.value">
</indicator-component>
<div class="hue-alpha-range">
@ -29,7 +29,12 @@
</div>
<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
[(color)]="control.value" [(hue)]="control.hue"></rgba-input-component>
<hsla-input-component *ngSwitchCase="'hsla'" label
@ -37,5 +42,8 @@
<hex-input-component *ngSwitchCase="'hex'" label prefix="#" [(color)]="control.value"
[(hue)]="control.hue"></hex-input-component>
</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>

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

@ -17,12 +17,10 @@
width: 100%;
display: flex;
flex-direction: column;
gap: 16px;
gap: 32px;
.saturation-component {
height: 100%;
min-height: 200px;
max-height: 300px;
height: 238px;
border-radius: 8px;
}
@ -55,6 +53,12 @@
.color-input-block {
display: flex;
gap: 20px;
.presentation-select {
font-size: 14px;
width: 56px;
}
.color-input {
flex: 1;
@ -63,16 +67,13 @@
color: initial;
}
}
}
.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;
}
.color-presets-block {
.color-presets-component {
display: flex;
flex-direction: column;
gap: 12px;
}
}
}
@ -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 { Color, ColorPickerControl } from '@iplab/ngx-color-picker';
import { Subscription } from 'rxjs';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms';
export enum ColorType {
hex = 'hex',
@ -29,6 +29,10 @@ export enum ColorType {
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({
selector: `tb-color-picker`,
templateUrl: `./color-picker.component.html`,
@ -43,10 +47,13 @@ export enum ColorType {
})
export class ColorPickerComponent implements ControlValueAccessor, OnDestroy {
selectedPresentation = 0;
presentations = [ColorType.hex, ColorType.rgba, ColorType.hsla];
control = new ColorPickerControl();
presentationControl = new UntypedFormControl(0);
colorPresets: Color[] = colorPresetsHex.map(c => Color.from(c));
private modelValue: string;
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 {
@ -86,12 +98,11 @@ export class ColorPickerComponent implements ControlValueAccessor, OnDestroy {
} else if (this.control.initType === ColorType.hsl) {
this.control.initType = ColorType.hsla;
}
this.selectedPresentation = this.presentations.indexOf(this.control.initType);
this.presentationControl.patchValue(this.presentations.indexOf(this.control.initType), {emitEvent: false});
}
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) {
this.modelValue = color;
this.propagateChange(color);
@ -103,12 +114,6 @@ export class ColorPickerComponent implements ControlValueAccessor, OnDestroy {
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:

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

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

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.
///
import { Component, Inject, OnInit } from '@angular/core';
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { DialogComponent } from '@shared/components/dialog.component';
@ -29,33 +28,26 @@ export interface ColorPickerDialogData {
@Component({
selector: 'tb-color-picker-dialog',
templateUrl: './color-picker-dialog.component.html',
styleUrls: []
styleUrls: ['./color-picker-dialog.component.scss']
})
export class ColorPickerDialogComponent extends DialogComponent<ColorPickerDialogComponent, string>
implements OnInit {
export class ColorPickerDialogComponent extends DialogComponent<ColorPickerDialogComponent, string> {
colorPickerFormGroup: FormGroup;
color: string;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: ColorPickerDialogData,
public dialogRef: MatDialogRef<ColorPickerDialogComponent, string>,
public fb: FormBuilder) {
public dialogRef: MatDialogRef<ColorPickerDialogComponent, string>) {
super(store, router, dialogRef);
this.color = data.color;
}
ngOnInit(): void {
this.colorPickerFormGroup = this.fb.group({
color: [this.data.color, [Validators.required]]
});
selectColor(color: string) {
this.dialogRef.close(color);
}
cancel(): void {
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 { UnitInputComponent } from '@shared/components/unit-input.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) {
return markedOptionsService;
@ -365,6 +366,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
GtMdLgLayoutGapDirective,
GtMdLgShowHideDirective,
ColorPickerComponent,
ColorPickerPanelComponent,
ResourceAutocompleteComponent,
ToggleHeaderComponent,
ToggleOption,
@ -597,6 +599,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
GtMdLgLayoutGapDirective,
GtMdLgShowHideDirective,
ColorPickerComponent,
ColorPickerPanelComponent,
ResourceAutocompleteComponent,
ToggleHeaderComponent,
ToggleOption,

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

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

53
ui-ngx/src/form.scss

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

Loading…
Cancel
Save