UI: Implement basic config form for Entities table widget.
This commit is contained in:
parent
c71d29c507
commit
d992355a3c
@ -104,7 +104,7 @@
|
||||
"settingsDirective": "tb-simple-card-widget-settings",
|
||||
"hasBasicMode": true,
|
||||
"basicModeDirective": "tb-simple-card-basic-config",
|
||||
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ff5722\",\"color\":\"rgba(255, 255, 255, 0.87)\",\"padding\":\"16px\",\"settings\":{\"labelPosition\":\"top\"},\"title\":\"Simple card\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\"}"
|
||||
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ff5722\",\"color\":\"rgba(255, 255, 255, 0.87)\",\"padding\":\"16px\",\"settings\":{\"labelPosition\":\"top\"},\"title\":\"Simple card\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true}"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -143,7 +143,9 @@
|
||||
"dataKeySettingsSchema": "",
|
||||
"settingsDirective": "tb-entities-table-widget-settings",
|
||||
"dataKeySettingsDirective": "tb-entities-table-key-settings",
|
||||
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"entitiesTitle\":\"\",\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"reserveSpaceForHiddenAction\":\"true\",\"displayEntityName\":true,\"entityNameColumnTitle\":\"\",\"displayEntityLabel\":false,\"displayEntityType\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"useRowStyleFunction\":false},\"title\":\"Entities table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"useCellContentFunction\":false,\"defaultColumnVisibility\":\"visible\",\"columnSelectionToDisplay\":\"enabled\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}]}"
|
||||
"hasBasicMode": true,
|
||||
"basicModeDirective": "tb-entities-table-basic-config",
|
||||
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"reserveSpaceForHiddenAction\":\"true\",\"displayEntityName\":false,\"displayEntityLabel\":false,\"displayEntityType\":false,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"name\",\"useRowStyleFunction\":false,\"entitiesTitle\":\"Entities\"},\"title\":\"Entities table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity name\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return 'Simulated';\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity type\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.782057645776538,\"funcBody\":\"return 'Device';\",\"decimals\":null,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.904797781901171,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\",\"decimals\":0},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.1961430898042078,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\",\"decimals\":0},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.7678057538205878,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"decimals\":2}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"displayTimewindow\":false,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"list\",\"iconColor\":null}"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@ -816,7 +816,8 @@ export class EntityService {
|
||||
);
|
||||
}
|
||||
|
||||
public getEntityKeysByEntityFilter(filter: EntityFilter, types: DataKeyType[], config?: RequestConfig): Observable<Array<DataKey>> {
|
||||
public getEntityKeysByEntityFilter(filter: EntityFilter, types: DataKeyType[],
|
||||
entityTypes?: EntityType[], config?: RequestConfig): Observable<Array<DataKey>> {
|
||||
if (!types.length) {
|
||||
return of([]);
|
||||
}
|
||||
@ -832,7 +833,7 @@ export class EntityService {
|
||||
entitiesKeysByQuery$ = of({
|
||||
attribute: [],
|
||||
timeseries: [],
|
||||
entityTypes: [],
|
||||
entityTypes: entityTypes || [],
|
||||
});
|
||||
}
|
||||
return entitiesKeysByQuery$.pipe(
|
||||
|
||||
@ -366,25 +366,18 @@ export class DashboardUtilsService {
|
||||
}
|
||||
const dataKeys = newDatasource.dataKeys;
|
||||
newDatasource.dataKeys = [];
|
||||
dataKeys.forEach(dataKey => {
|
||||
newDatasource.dataKeys.push(this.convertDataKeyFromWidgetType(widgetTypeDescriptor, config, dataKey));
|
||||
});
|
||||
if (widgetTypeDescriptor.type === widgetType.alarm) {
|
||||
dataKeys.forEach(dataKey => {
|
||||
const newDataKey = deepClone(dataKey);
|
||||
newDataKey.funcBody = null;
|
||||
newDataKey.type = DataKeyType.alarm;
|
||||
newDatasource.dataKeys.push(newDataKey);
|
||||
});
|
||||
}
|
||||
}
|
||||
return newDatasource;
|
||||
}
|
||||
|
||||
private convertDataKeyFromWidgetType(widgetTypeDescriptor: WidgetTypeDescriptor, config: WidgetConfig, dataKey: DataKey): DataKey {
|
||||
const newDataKey = deepClone(dataKey);
|
||||
newDataKey.funcBody = null;
|
||||
if (widgetTypeDescriptor.type === widgetType.alarm) {
|
||||
newDataKey.type = DataKeyType.alarm;
|
||||
} else {
|
||||
newDataKey.type = DataKeyType.timeseries;
|
||||
newDataKey.name = newDataKey.label;
|
||||
}
|
||||
return newDataKey;
|
||||
}
|
||||
|
||||
private validateAndUpdateState(state: DashboardState) {
|
||||
if (!state.layouts) {
|
||||
state.layouts = this.createDefaultLayouts();
|
||||
|
||||
@ -51,6 +51,7 @@
|
||||
[widget]="widget"
|
||||
[widgetConfigMode]="widgetConfigMode"
|
||||
[hideHeader]="widgetConfigMode === widgetConfigModes.basic"
|
||||
isAdd
|
||||
formControlName="widgetConfig">
|
||||
</tb-widget-config>
|
||||
<tb-widget-preview *ngIf="previewMode" class="tb-absolute-fill"
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
addKeyTitle="{{ 'widgets.table.add-column' | translate }}"
|
||||
removeKeyTitle="{{ 'widgets.table.remove-column' | translate }}"
|
||||
noKeysText="{{ 'widgets.table.no-columns' | translate }}"
|
||||
hideDataKeyColor
|
||||
[datasourceType]="datasource?.type"
|
||||
[deviceId]="datasource?.deviceId"
|
||||
[entityAliasId]="datasource?.entityAliasId"
|
||||
@ -37,6 +38,29 @@
|
||||
</tb-data-keys-panel>
|
||||
<div class="tb-widget-config-panel">
|
||||
<div class="tb-widget-config-panel-title" translate>widget-config.appearance</div>
|
||||
<div class="tb-widget-config-row">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="showTitle">
|
||||
{{ 'widget-config.card-title' | translate }}
|
||||
</mat-slide-toggle>
|
||||
<mat-form-field fxFlex appearance="outline" subscriptSizing="dynamic">
|
||||
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="tb-widget-config-row space-between same-padding">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="showTitleIcon">
|
||||
{{ 'widget-config.card-icon' | translate }}
|
||||
</mat-slide-toggle>
|
||||
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tb-widget-config-row space-between same-padding">
|
||||
<div>{{ 'widget-config.text-color' | translate }}</div>
|
||||
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
|
||||
|
||||
@ -26,11 +26,13 @@ import {
|
||||
datasourcesHasAggregation,
|
||||
datasourcesHasOnlyComparisonAggregation
|
||||
} from '@shared/models/widget.models';
|
||||
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
|
||||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-entities-table-basic-config',
|
||||
templateUrl: './entities-table-basic-config.component.html',
|
||||
styleUrls: ['../basic-config.scss', '../../widget-config.scss']
|
||||
styleUrls: ['../basic-config.scss']
|
||||
})
|
||||
export class EntitiesTableBasicConfigComponent extends BasicWidgetConfigComponent {
|
||||
|
||||
@ -56,14 +58,19 @@ export class EntitiesTableBasicConfigComponent extends BasicWidgetConfigComponen
|
||||
entitiesTableWidgetConfigForm: UntypedFormGroup;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
protected widgetConfigComponent: WidgetConfigComponent,
|
||||
private fb: UntypedFormBuilder) {
|
||||
super(store);
|
||||
super(store, widgetConfigComponent);
|
||||
}
|
||||
|
||||
protected configForm(): UntypedFormGroup {
|
||||
return this.entitiesTableWidgetConfigForm;
|
||||
}
|
||||
|
||||
protected setupDefaults(configData: WidgetConfigComponentData) {
|
||||
this.setupDefaultDatasource(configData, [{ name: 'name', type: DataKeyType.entityField }]);
|
||||
}
|
||||
|
||||
protected onConfigSet(configData: WidgetConfigComponentData) {
|
||||
this.entitiesTableWidgetConfigForm = this.fb.group({
|
||||
timewindowConfig: [{
|
||||
@ -73,6 +80,11 @@ export class EntitiesTableBasicConfigComponent extends BasicWidgetConfigComponen
|
||||
}, []],
|
||||
datasources: [configData.config.datasources, []],
|
||||
columns: [this.getColumns(configData.config.datasources), []],
|
||||
showTitle: [configData.config.showTitle, []],
|
||||
title: [configData.config.settings?.entitiesTitle, []],
|
||||
showTitleIcon: [configData.config.showTitleIcon, []],
|
||||
titleIcon: [configData.config.titleIcon, []],
|
||||
iconColor: [configData.config.iconColor, []],
|
||||
color: [configData.config.color, []],
|
||||
backgroundColor: [configData.config.backgroundColor, []],
|
||||
actions: [configData.config.actions || {}, []]
|
||||
@ -86,11 +98,46 @@ export class EntitiesTableBasicConfigComponent extends BasicWidgetConfigComponen
|
||||
this.widgetConfig.config.datasources = config.datasources;
|
||||
this.setColumns(config.columns, this.widgetConfig.config.datasources);
|
||||
this.widgetConfig.config.actions = config.actions;
|
||||
this.widgetConfig.config.showTitle = config.showTitle;
|
||||
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {};
|
||||
this.widgetConfig.config.settings.entitiesTitle = config.title;
|
||||
this.widgetConfig.config.showTitleIcon = config.showTitleIcon;
|
||||
this.widgetConfig.config.titleIcon = config.titleIcon;
|
||||
this.widgetConfig.config.iconColor = config.iconColor;
|
||||
this.widgetConfig.config.color = config.color;
|
||||
this.widgetConfig.config.backgroundColor = config.backgroundColor;
|
||||
return this.widgetConfig;
|
||||
}
|
||||
|
||||
protected validatorTriggers(): string[] {
|
||||
return ['showTitle', 'showTitleIcon'];
|
||||
}
|
||||
|
||||
protected updateValidators(emitEvent: boolean, trigger?: string) {
|
||||
const showTitle: boolean = this.entitiesTableWidgetConfigForm.get('showTitle').value;
|
||||
const showTitleIcon: boolean = this.entitiesTableWidgetConfigForm.get('showTitleIcon').value;
|
||||
if (showTitle) {
|
||||
this.entitiesTableWidgetConfigForm.get('title').enable();
|
||||
this.entitiesTableWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false});
|
||||
if (showTitleIcon) {
|
||||
this.entitiesTableWidgetConfigForm.get('titleIcon').enable();
|
||||
this.entitiesTableWidgetConfigForm.get('iconColor').enable();
|
||||
} else {
|
||||
this.entitiesTableWidgetConfigForm.get('titleIcon').disable();
|
||||
this.entitiesTableWidgetConfigForm.get('iconColor').disable();
|
||||
}
|
||||
} else {
|
||||
this.entitiesTableWidgetConfigForm.get('title').disable();
|
||||
this.entitiesTableWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false});
|
||||
this.entitiesTableWidgetConfigForm.get('titleIcon').disable();
|
||||
this.entitiesTableWidgetConfigForm.get('iconColor').disable();
|
||||
}
|
||||
this.entitiesTableWidgetConfigForm.get('title').updateValueAndValidity({emitEvent});
|
||||
this.entitiesTableWidgetConfigForm.get('showTitleIcon').updateValueAndValidity({emitEvent: false});
|
||||
this.entitiesTableWidgetConfigForm.get('titleIcon').updateValueAndValidity({emitEvent});
|
||||
this.entitiesTableWidgetConfigForm.get('iconColor').updateValueAndValidity({emitEvent});
|
||||
}
|
||||
|
||||
private getColumns(datasources?: Datasource[]): DataKey[] {
|
||||
if (datasources && datasources.length) {
|
||||
return datasources[0].dataKeys || [];
|
||||
|
||||
@ -23,13 +23,15 @@ import { WidgetConfigComponentData } from '@home/models/widget-component.models'
|
||||
import {
|
||||
Datasource,
|
||||
datasourcesHasAggregation,
|
||||
datasourcesHasOnlyComparisonAggregation
|
||||
datasourcesHasOnlyComparisonAggregation,
|
||||
} from '@shared/models/widget.models';
|
||||
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
|
||||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-simple-card-basic-config',
|
||||
templateUrl: './simple-card-basic-config.component.html',
|
||||
styleUrls: ['../basic-config.scss', '../../widget-config.scss']
|
||||
styleUrls: ['../basic-config.scss']
|
||||
})
|
||||
export class SimpleCardBasicConfigComponent extends BasicWidgetConfigComponent {
|
||||
|
||||
@ -46,14 +48,19 @@ export class SimpleCardBasicConfigComponent extends BasicWidgetConfigComponent {
|
||||
simpleCardWidgetConfigForm: UntypedFormGroup;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
protected widgetConfigComponent: WidgetConfigComponent,
|
||||
private fb: UntypedFormBuilder) {
|
||||
super(store);
|
||||
super(store, widgetConfigComponent);
|
||||
}
|
||||
|
||||
protected configForm(): UntypedFormGroup {
|
||||
return this.simpleCardWidgetConfigForm;
|
||||
}
|
||||
|
||||
protected setupDefaults(configData: WidgetConfigComponentData) {
|
||||
this.setupDefaultDatasource(configData, [{ name: 'temperature', label: 'Temperature', type: DataKeyType.timeseries }]);
|
||||
}
|
||||
|
||||
protected onConfigSet(configData: WidgetConfigComponentData) {
|
||||
this.simpleCardWidgetConfigForm = this.fb.group({
|
||||
timewindowConfig: [{
|
||||
|
||||
@ -136,6 +136,21 @@
|
||||
<mat-form-field fxFlex class="tb-inline-field" appearance="outline" subscriptSizing="dynamic">
|
||||
<input matInput formControlName="label" placeholder="{{ 'widget-config.set' | translate }}">
|
||||
</mat-form-field>
|
||||
<div *ngIf="!hideDataKeyColor" class="tb-color-field">
|
||||
<tb-color-input asBoxInput
|
||||
formControlName="color">
|
||||
</tb-color-input>
|
||||
</div>
|
||||
<div class="tb-units-field">
|
||||
<tb-widget-units *ngIf="displayUnitsOrDigits"
|
||||
formControlName="units">
|
||||
</tb-widget-units>
|
||||
</div>
|
||||
<div class="tb-decimals-field">
|
||||
<mat-form-field *ngIf="displayUnitsOrDigits" appearance="outline" class="tb-inline-field center number" subscriptSizing="dynamic">
|
||||
<input matInput formControlName="decimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #keyName>
|
||||
<ng-container *ngIf="dataKeyHasPostprocessing(); else keyName">
|
||||
|
||||
@ -42,4 +42,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tb-color-field, .tb-units-field, .tb-decimals-field {
|
||||
width: 60px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
place-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ import {
|
||||
} from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
|
||||
import { DataKey, DatasourceType, JsonSettingsSchema, widgetType } from '@shared/models/widget.models';
|
||||
import { DataKey, DatasourceType, JsonSettingsSchema, Widget, widgetType } from '@shared/models/widget.models';
|
||||
import { DataKeysPanelComponent } from '@home/components/widget/config/basic/common/data-keys-panel.component';
|
||||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||
import { AggregationType } from '@shared/models/time/time.models';
|
||||
@ -49,6 +49,13 @@ import { Observable, of } from 'rxjs';
|
||||
import { filter, map, mergeMap, publishReplay, refCount, share, tap } from 'rxjs/operators';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { TruncatePipe } from '@shared/pipe/truncate.pipe';
|
||||
import {
|
||||
DataKeyConfigDialogComponent,
|
||||
DataKeyConfigDialogData
|
||||
} from '@home/components/widget/config/data-key-config-dialog.component';
|
||||
import { deepClone } from '@core/utils';
|
||||
import { Dashboard } from '@shared/models/dashboard.models';
|
||||
import { IAliasController } from '@core/api/widget-api.models';
|
||||
|
||||
export const dataKeyRowValidator = (control: AbstractControl): ValidationErrors | null => {
|
||||
const dataKey: DataKey = control.value;
|
||||
@ -63,7 +70,7 @@ export const dataKeyRowValidator = (control: AbstractControl): ValidationErrors
|
||||
@Component({
|
||||
selector: 'tb-data-key-row',
|
||||
templateUrl: './data-key-row.component.html',
|
||||
styleUrls: ['./data-key-row.component.scss', '../../data-keys.component.scss', '../../widget-config.scss'],
|
||||
styleUrls: ['./data-key-row.component.scss', '../../data-keys.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
@ -122,6 +129,10 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan
|
||||
return this.dataKeysPanelComponent.functionTypeKeys;
|
||||
}
|
||||
|
||||
get hideDataKeyColor(): boolean {
|
||||
return this.dataKeysPanelComponent.hideDataKeyColor;
|
||||
}
|
||||
|
||||
get widgetType(): widgetType {
|
||||
return this.widgetConfigComponent.widgetType;
|
||||
}
|
||||
@ -130,14 +141,34 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan
|
||||
return this.widgetConfigComponent.widgetConfigCallbacks;
|
||||
}
|
||||
|
||||
get widget(): Widget {
|
||||
return this.widgetConfigComponent.widget;
|
||||
}
|
||||
|
||||
get dashboard(): Dashboard {
|
||||
return this.widgetConfigComponent.dashboard;
|
||||
}
|
||||
|
||||
get aliasController(): IAliasController {
|
||||
return this.widgetConfigComponent.aliasController;
|
||||
}
|
||||
|
||||
get datakeySettingsSchema(): JsonSettingsSchema {
|
||||
return this.widgetConfigComponent.modelValue?.dataKeySettingsSchema;
|
||||
}
|
||||
|
||||
get dataKeySettingsDirective(): string {
|
||||
return this.widgetConfigComponent.modelValue?.dataKeySettingsDirective;
|
||||
}
|
||||
|
||||
get isEntityDatasource(): boolean {
|
||||
return [DatasourceType.device, DatasourceType.entity].includes(this.datasourceType);
|
||||
}
|
||||
|
||||
get displayUnitsOrDigits() {
|
||||
return this.modelValue.type && ![ DataKeyType.alarm, DataKeyType.entityField, DataKeyType.count ].includes(this.modelValue.type);
|
||||
}
|
||||
|
||||
private propagateChange = (_val: any) => {};
|
||||
|
||||
constructor(private fb: UntypedFormBuilder,
|
||||
@ -261,7 +292,37 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan
|
||||
}
|
||||
|
||||
editKey() {
|
||||
|
||||
this.dialog.open<DataKeyConfigDialogComponent, DataKeyConfigDialogData, DataKey>(DataKeyConfigDialogComponent,
|
||||
{
|
||||
disableClose: true,
|
||||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
||||
data: {
|
||||
dataKey: deepClone(this.modelValue),
|
||||
dataKeySettingsSchema: this.datakeySettingsSchema,
|
||||
dataKeySettingsDirective: this.dataKeySettingsDirective,
|
||||
dashboard: this.dashboard,
|
||||
aliasController: this.aliasController,
|
||||
widget: this.widget,
|
||||
widgetType: this.widgetType,
|
||||
deviceId: this.deviceId,
|
||||
entityAliasId: this.entityAliasId,
|
||||
showPostProcessing: this.widgetType !== widgetType.alarm,
|
||||
callbacks: this.callbacks,
|
||||
hideDataKeyLabel: false,
|
||||
hideDataKeyColor: this.hideDataKeyColor,
|
||||
hideDataKeyUnits: !this.displayUnitsOrDigits,
|
||||
hideDataKeyDecimals: !this.displayUnitsOrDigits
|
||||
}
|
||||
}).afterClosed().subscribe((updatedDataKey) => {
|
||||
if (updatedDataKey) {
|
||||
this.modelValue = updatedDataKey;
|
||||
this.keyRowFormGroup.get('label').patchValue(this.modelValue.label, {emitEvent: false});
|
||||
this.keyRowFormGroup.get('color').patchValue(this.modelValue.color, {emitEvent: false});
|
||||
this.keyRowFormGroup.get('units').patchValue(this.modelValue.units, {emitEvent: false});
|
||||
this.keyRowFormGroup.get('decimals').patchValue(this.modelValue.decimals, {emitEvent: false});
|
||||
this.updateModel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeKey() {
|
||||
|
||||
@ -21,9 +21,10 @@
|
||||
<div class="tb-data-keys-header">
|
||||
<div class="tb-data-keys-header-cell" fxFlex translate>datakey.key</div>
|
||||
<div class="tb-data-keys-header-cell" fxFlex translate>datakey.label</div>
|
||||
<div class="tb-data-keys-header-cell" translate>datakey.color</div>
|
||||
<div class="tb-data-keys-header-cell" translate>widget-config.units-short</div>
|
||||
<div class="tb-data-keys-header-cell" translate>widget-config.decimals-short</div>
|
||||
<div *ngIf="!hideDataKeyColor" class="tb-data-keys-header-cell tb-color-header" translate>datakey.color</div>
|
||||
<div class="tb-data-keys-header-cell tb-units-header" translate>widget-config.units-short</div>
|
||||
<div class="tb-data-keys-header-cell tb-decimals-header" translate>widget-config.decimals-short</div>
|
||||
<div class="tb-data-keys-header-cell tb-actions-header"></div>
|
||||
</div>
|
||||
<div *ngIf="keysFormArray().controls.length; else noKeys" class="tb-data-keys-body tb-drop-list" cdkDropList cdkDropListOrientation="vertical"
|
||||
(cdkDropListDropped)="keyDrop($event)">
|
||||
|
||||
@ -29,13 +29,18 @@
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
.tb-data-keys-header-cell {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.2px;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
&.tb-color-header, &.tb-units-header, &.tb-decimals-header {
|
||||
width: 60px;
|
||||
}
|
||||
&.tb-actions-header {
|
||||
width: 76px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tb-data-keys-body {
|
||||
|
||||
@ -44,11 +44,12 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||
import { alarmFields } from '@shared/models/alarm.models';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models';
|
||||
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-data-keys-panel',
|
||||
templateUrl: './data-keys-panel.component.html',
|
||||
styleUrls: ['./data-keys-panel.component.scss', '../../widget-config.scss'],
|
||||
styleUrls: ['./data-keys-panel.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
@ -89,6 +90,10 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC
|
||||
@Input()
|
||||
deviceId: string;
|
||||
|
||||
@Input()
|
||||
@coerceBoolean()
|
||||
hideDataKeyColor = false;
|
||||
|
||||
dataKeyType: DataKeyType;
|
||||
alarmKeys: Array<DataKey>;
|
||||
functionTypeKeys: Array<DataKey>;
|
||||
@ -212,13 +217,11 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC
|
||||
|
||||
addKey() {
|
||||
const dataKey = this.callbacks.generateDataKey('', null, this.datakeySettingsSchema);
|
||||
dataKey.label = '';
|
||||
dataKey.decimals = 0;
|
||||
const keysArray = this.keysListFormGroup.get('keys') as UntypedFormArray;
|
||||
const keyControl = this.fb.control(dataKey, [dataKeyRowValidator]);
|
||||
keysArray.push(keyControl);
|
||||
this.keysListFormGroup.updateValueAndValidity();
|
||||
if (!this.keysListFormGroup.valid) {
|
||||
this.propagateChange(this.keysListFormGroup.get('keys').value);
|
||||
}
|
||||
}
|
||||
|
||||
private prepareKeysFormArray(keys: DataKey[] | undefined): UntypedFormArray {
|
||||
|
||||
@ -29,7 +29,7 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
@Component({
|
||||
selector: 'tb-widget-actions-panel',
|
||||
templateUrl: './widget-actions-panel.component.html',
|
||||
styleUrls: ['../../widget-config.scss'],
|
||||
styleUrls: [],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
|
||||
@ -155,6 +155,7 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
|
||||
private dataKeySettingsData: JsonFormComponentData;
|
||||
|
||||
private alarmKeys: Array<DataKey>;
|
||||
private functionTypeKeys: Array<DataKey>;
|
||||
|
||||
filteredKeys: Observable<Array<string>>;
|
||||
private latestKeySearchResult: Array<string> = null;
|
||||
@ -183,6 +184,13 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
|
||||
type: DataKeyType.alarm
|
||||
});
|
||||
}
|
||||
this.functionTypeKeys = [];
|
||||
for (const type of this.utils.getPredefinedFunctionsList()) {
|
||||
this.functionTypeKeys.push({
|
||||
name: type,
|
||||
type: DataKeyType.function
|
||||
});
|
||||
}
|
||||
if (this.dataKeySettingsSchema && this.dataKeySettingsSchema.schema ||
|
||||
this.dataKeySettingsDirective && this.dataKeySettingsDirective.length) {
|
||||
this.displayAdvanced = true;
|
||||
@ -396,6 +404,8 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
|
||||
let fetchObservable: Observable<Array<DataKey>>;
|
||||
if (this.modelValue.type === DataKeyType.alarm) {
|
||||
fetchObservable = of(this.alarmKeys);
|
||||
} else if (this.modelValue.type === DataKeyType.function) {
|
||||
fetchObservable = of(this.functionTypeKeys);
|
||||
} else {
|
||||
if (this.deviceId || this.entityAliasId) {
|
||||
const dataKeyTypes = [this.modelValue.type];
|
||||
|
||||
@ -46,7 +46,7 @@ import { coerceBoolean } from '@shared/decorators/coercion';
|
||||
@Component({
|
||||
selector: 'tb-datasources',
|
||||
templateUrl: './datasources.component.html',
|
||||
styleUrls: ['./datasources.component.scss', './widget-config.scss'],
|
||||
styleUrls: ['./datasources.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
|
||||
@ -31,7 +31,7 @@ export interface TimewindowConfigData {
|
||||
@Component({
|
||||
selector: 'tb-timewindow-config-panel',
|
||||
templateUrl: './timewindow-config-panel.component.html',
|
||||
styleUrls: ['./widget-config.scss'],
|
||||
styleUrls: [],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
|
||||
@ -23,12 +23,15 @@ import { PageComponent } from '@shared/components/page.component';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { WidgetConfigMode } from '@shared/models/widget.models';
|
||||
import { DataKey, DatasourceType, KeyInfo, WidgetConfigMode } from '@shared/models/widget.models';
|
||||
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
|
||||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||
import { isDefinedAndNotNull } from '@core/utils';
|
||||
|
||||
export type WidgetConfigCallbacks = DatasourceCallbacks & WidgetActionCallbacks;
|
||||
|
||||
export interface IBasicWidgetConfigComponent {
|
||||
|
||||
isAdd: boolean;
|
||||
widgetConfig: WidgetConfigComponentData;
|
||||
widgetConfigChanged: Observable<WidgetConfigComponentData>;
|
||||
validateConfig(): boolean;
|
||||
@ -40,6 +43,8 @@ export interface IBasicWidgetConfigComponent {
|
||||
export abstract class BasicWidgetConfigComponent extends PageComponent implements
|
||||
IBasicWidgetConfigComponent, OnInit, AfterViewInit {
|
||||
|
||||
isAdd = false;
|
||||
|
||||
basicMode = WidgetConfigMode.basic;
|
||||
|
||||
widgetConfigValue: WidgetConfigComponentData;
|
||||
@ -56,7 +61,8 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement
|
||||
widgetConfigChangedEmitter = new EventEmitter<WidgetConfigComponentData>();
|
||||
widgetConfigChanged = this.widgetConfigChangedEmitter.asObservable();
|
||||
|
||||
protected constructor(@Inject(Store) protected store: Store<AppState>) {
|
||||
protected constructor(@Inject(Store) protected store: Store<AppState>,
|
||||
protected widgetConfigComponent: WidgetConfigComponent) {
|
||||
super(store);
|
||||
}
|
||||
|
||||
@ -65,12 +71,15 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement
|
||||
ngAfterViewInit(): void {
|
||||
setTimeout(() => {
|
||||
if (!this.validateConfig()) {
|
||||
this.onConfigChanged(this.prepareOutputConfig(this.configForm().value));
|
||||
this.onConfigChanged(this.prepareOutputConfig(this.configForm().getRawValue()));
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
protected setupConfig(widgetConfig: WidgetConfigComponentData) {
|
||||
if (this.isAdd) {
|
||||
this.setupDefaults(widgetConfig);
|
||||
}
|
||||
this.onConfigSet(widgetConfig);
|
||||
this.updateValidators(false);
|
||||
for (const trigger of this.validatorTriggers()) {
|
||||
@ -83,11 +92,13 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement
|
||||
this.updateValidators(true, trigger);
|
||||
});
|
||||
}
|
||||
this.configForm().valueChanges.subscribe((updated: any) => {
|
||||
this.onConfigChanged(this.prepareOutputConfig(updated));
|
||||
this.configForm().valueChanges.subscribe(() => {
|
||||
this.onConfigChanged(this.prepareOutputConfig(this.configForm().getRawValue()));
|
||||
});
|
||||
}
|
||||
|
||||
protected setupDefaults(configData: WidgetConfigComponentData) {}
|
||||
|
||||
protected updateValidators(emitEvent: boolean, trigger?: string) {
|
||||
}
|
||||
|
||||
@ -108,6 +119,41 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement
|
||||
return this.configForm().valid;
|
||||
}
|
||||
|
||||
protected setupDefaultDatasource(configData: WidgetConfigComponentData, keys?: DataKey[]) {
|
||||
let datasources = configData.config.datasources;
|
||||
if (!datasources || !datasources.length) {
|
||||
datasources = [
|
||||
{
|
||||
type: DatasourceType.device,
|
||||
dataKeys: []
|
||||
}
|
||||
];
|
||||
configData.config.datasources = datasources;
|
||||
}
|
||||
let dataKeys = datasources[0].dataKeys;
|
||||
if (!dataKeys) {
|
||||
dataKeys = [];
|
||||
datasources[0].dataKeys = dataKeys;
|
||||
}
|
||||
if (keys && keys.length) {
|
||||
dataKeys.length = 0;
|
||||
keys.forEach(key => {
|
||||
const dataKey =
|
||||
this.widgetConfigComponent.widgetConfigCallbacks.generateDataKey(key.name, key.type, configData.dataKeySettingsSchema);
|
||||
if (key.label) {
|
||||
dataKey.label = key.label;
|
||||
}
|
||||
if (key.units) {
|
||||
dataKey.units = key.units;
|
||||
}
|
||||
if (isDefinedAndNotNull(key.decimals)) {
|
||||
dataKey.decimals = key.decimals;
|
||||
}
|
||||
dataKeys.push(dataKey);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract configForm(): UntypedFormGroup;
|
||||
|
||||
protected abstract onConfigSet(configData: WidgetConfigComponentData);
|
||||
|
||||
@ -1,175 +0,0 @@
|
||||
/**
|
||||
* 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-widget-config-panel {
|
||||
box-shadow: 0 0 10px 6px rgba(11, 17, 51, 0.04);
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
gap: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
letter-spacing: 0.15px;
|
||||
position: relative;
|
||||
&.no-padding-bottom {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
&.stroked {
|
||||
box-shadow: none;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
.tb-widget-config-panel-title {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
.tb-widget-config-panel-hint {
|
||||
font-size: 12px;
|
||||
color: #808080;
|
||||
}
|
||||
.tb-widget-config-row {
|
||||
height: 56px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding-left: 16px;
|
||||
padding-right: 12px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 6px;
|
||||
&.same-padding {
|
||||
padding-right: 16px;
|
||||
}
|
||||
&.space-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.mat-divider-vertical {
|
||||
height: 56px;
|
||||
}
|
||||
}
|
||||
|
||||
.tb-widget-config-row .mat-mdc-form-field, .mat-mdc-form-field.tb-inline-field {
|
||||
&.mat-form-field-appearance-fill {
|
||||
.mdc-text-field--filled:not(.mdc-text-field--disabled):before {
|
||||
opacity: 0;
|
||||
}
|
||||
.mat-mdc-form-field-focus-overlay {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.mat-mdc-text-field-wrapper {
|
||||
&.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
|
||||
padding-right: 12px;
|
||||
padding-left: 12px;
|
||||
&:not(.mdc-text-field--focused):not(.mdc-text-field--disabled):not(:hover) {
|
||||
.mdc-notched-outline__leading, .mdc-notched-outline__trailing {
|
||||
border-color: rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
.mat-mdc-form-field-infix {
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
min-height: 38px;
|
||||
width: 72px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.center {
|
||||
.mat-mdc-text-field-wrapper {
|
||||
.mat-mdc-form-field-infix {
|
||||
.mdc-text-field__input {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.number {
|
||||
.mat-mdc-text-field-wrapper {
|
||||
padding-right: 4px;
|
||||
.mat-mdc-form-field-infix {
|
||||
width: 80px;
|
||||
input.mdc-text-field__input[type=number]::-webkit-inner-spin-button,
|
||||
input.mdc-text-field__input[type=number]::-webkit-outer-spin-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
:host ::ng-deep {
|
||||
|
||||
.mat-slide {
|
||||
margin: 8px 0;
|
||||
.mdc-form-field>label {
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.slide-block {
|
||||
display: block;
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.tb-widget-config-panel {
|
||||
.mat-expansion-panel {
|
||||
&.tb-settings {
|
||||
box-shadow: none;
|
||||
.mat-content {
|
||||
overflow: visible;
|
||||
}
|
||||
.mat-expansion-panel-header {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0.25px;
|
||||
padding: 0;
|
||||
.mat-content {
|
||||
flex: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
.mat-expansion-indicator {
|
||||
height: 32px;
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
.mat-expansion-panel-header-description {
|
||||
align-items: center;
|
||||
}
|
||||
> .mat-expansion-panel-content {
|
||||
> .mat-expansion-panel-body {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.tb-json-object-panel, .tb-css-content-panel {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
}
|
||||
.mat-expansion-panel-content {
|
||||
font: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,6 @@
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<mat-form-field appearance="outline" class="center" subscriptSizing="dynamic">
|
||||
<mat-form-field appearance="outline" class="tb-inline-field center" subscriptSizing="dynamic">
|
||||
<input matInput [formControl]="unitsFormControl" placeholder="{{ 'widget-config.set' | translate }}">
|
||||
</mat-form-field>
|
||||
|
||||
@ -20,7 +20,7 @@ import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, UntypedFormBuilde
|
||||
@Component({
|
||||
selector: 'tb-widget-units',
|
||||
templateUrl: './widget-units.component.html',
|
||||
styleUrls: ['./widget-config.scss'],
|
||||
styleUrls: [],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
@ -43,6 +43,7 @@ export class WidgetUnitsComponent implements ControlValueAccessor, OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.unitsFormControl = this.fb.control('', []);
|
||||
this.unitsFormControl.valueChanges.subscribe(val => this.propagateChange(val));
|
||||
}
|
||||
|
||||
writeValue(units?: string): void {
|
||||
|
||||
@ -23,7 +23,7 @@ import { AppState } from '@core/core.state';
|
||||
@Component({
|
||||
selector: 'tb-simple-card-widget-settings',
|
||||
templateUrl: './simple-card-widget-settings.component.html',
|
||||
styleUrls: ['../../../config/widget-config.scss']
|
||||
styleUrls: []
|
||||
})
|
||||
export class SimpleCardWidgetSettingsComponent extends WidgetSettingsComponent {
|
||||
|
||||
|
||||
@ -215,7 +215,7 @@ export class ValueSourceComponent extends PageComponent implements OnInit, Contr
|
||||
mergeMap((aliasInfo) => {
|
||||
return this.entityService.getEntityKeysByEntityFilter(
|
||||
aliasInfo.entityFilter,
|
||||
dataKeyTypes,
|
||||
dataKeyTypes, [],
|
||||
{ignoreLoading: true, ignoreErrors: true}
|
||||
).pipe(
|
||||
catchError(() => of([]))
|
||||
|
||||
@ -15,7 +15,13 @@
|
||||
///
|
||||
|
||||
import { Component, ElementRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
|
||||
import {
|
||||
ControlValueAccessor,
|
||||
NG_VALUE_ACCESSOR,
|
||||
UntypedFormBuilder,
|
||||
UntypedFormGroup,
|
||||
Validators
|
||||
} from '@angular/forms';
|
||||
import { PageComponent } from '@shared/components/page.component';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
@ -27,6 +33,7 @@ import { catchError, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/op
|
||||
import { DataKey } from '@shared/models/widget.models';
|
||||
import { EntityService } from '@core/http/entity.service';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
import { EntityType } from '@shared/models/entity-type.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-device-key-autocomplete',
|
||||
@ -199,7 +206,7 @@ export class DeviceKeyAutocompleteComponent extends PageComponent implements OnI
|
||||
mergeMap((aliasInfo) => {
|
||||
return this.entityService.getEntityKeysByEntityFilter(
|
||||
aliasInfo.entityFilter,
|
||||
dataKeyTypes,
|
||||
dataKeyTypes, [EntityType.DEVICE],
|
||||
{ignoreLoading: true, ignoreErrors: true}
|
||||
).pipe(
|
||||
catchError(() => of([]))
|
||||
|
||||
@ -236,7 +236,7 @@ export class ImageMapProviderSettingsComponent extends PageComponent implements
|
||||
mergeMap((aliasInfo) => {
|
||||
return this.entityService.getEntityKeysByEntityFilter(
|
||||
aliasInfo.entityFilter,
|
||||
dataKeyTypes,
|
||||
dataKeyTypes, [],
|
||||
{ignoreLoading: true, ignoreErrors: true}
|
||||
).pipe(
|
||||
catchError(() => of([]))
|
||||
|
||||
@ -81,8 +81,8 @@ import { FilterDialogComponent, FilterDialogData } from '@home/components/filter
|
||||
import { ToggleHeaderOption } from '@shared/components/toggle-header.component';
|
||||
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||
import { basicWidgetConfigComponentsMap } from '@home/components/widget/config/basic/basic-widget-config.module';
|
||||
import Timeout = NodeJS.Timeout;
|
||||
import { TimewindowConfigData } from '@home/components/widget/config/timewindow-config-panel.component';
|
||||
import Timeout = NodeJS.Timeout;
|
||||
|
||||
const emptySettingsSchema: JsonSchema = {
|
||||
type: 'object',
|
||||
@ -96,7 +96,7 @@ const defaultSettingsForm = [
|
||||
@Component({
|
||||
selector: 'tb-widget-config',
|
||||
templateUrl: './widget-config.component.html',
|
||||
styleUrls: ['./widget-config.component.scss', './config/widget-config.scss'],
|
||||
styleUrls: ['./widget-config.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
@ -143,6 +143,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
|
||||
@coerceBoolean()
|
||||
hideToggleHeader = false;
|
||||
|
||||
@Input()
|
||||
@coerceBoolean()
|
||||
isAdd = false;
|
||||
|
||||
@Input() disabled: boolean;
|
||||
|
||||
@Input()
|
||||
@ -399,22 +403,22 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
|
||||
writeValue(value: WidgetConfigComponentData): void {
|
||||
this.modelValue = value;
|
||||
this.widgetType = this.modelValue?.widgetType;
|
||||
this.setupConfig();
|
||||
this.setupConfig(this.isAdd);
|
||||
}
|
||||
|
||||
private setupConfig() {
|
||||
private setupConfig(isAdd = false) {
|
||||
if (this.modelValue) {
|
||||
this.destroyBasicModeComponent();
|
||||
this.removeChangeSubscriptions();
|
||||
if (this.hasBasicModeDirective && this.widgetConfigMode === WidgetConfigMode.basic) {
|
||||
this.setupBasicModeConfig();
|
||||
this.setupBasicModeConfig(isAdd);
|
||||
} else {
|
||||
this.setupDefaultConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setupBasicModeConfig() {
|
||||
private setupBasicModeConfig(isAdd = false) {
|
||||
const componentType = basicWidgetConfigComponentsMap[this.modelValue.basicModeDirective];
|
||||
if (!componentType) {
|
||||
this.basicModeDirectiveError = this.translate.instant('widget-config.settings-component-not-found',
|
||||
@ -425,6 +429,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
|
||||
this.createBasicModeComponentTimeout = null;
|
||||
this.basicModeComponentRef = this.basicModeContainer.createComponent(factory);
|
||||
this.basicModeComponent = this.basicModeComponentRef.instance;
|
||||
this.basicModeComponent.isAdd = isAdd;
|
||||
this.basicModeComponent.widgetConfig = this.modelValue;
|
||||
this.basicModeComponentChangeSubscription = this.basicModeComponent.widgetConfigChanged.subscribe((data) => {
|
||||
this.modelValue = data;
|
||||
@ -826,7 +831,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
|
||||
const entityFilter = singleEntityFilterFromDeviceId(deviceId);
|
||||
return this.entityService.getEntityKeysByEntityFilter(
|
||||
entityFilter,
|
||||
dataKeyTypes,
|
||||
dataKeyTypes, [EntityType.DEVICE],
|
||||
{ignoreLoading: true, ignoreErrors: true}
|
||||
).pipe(
|
||||
catchError(() => of([]))
|
||||
@ -837,7 +842,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
|
||||
return this.aliasController.getAliasInfo(entityAliasId).pipe(
|
||||
mergeMap((aliasInfo) => this.entityService.getEntityKeysByEntityFilter(
|
||||
aliasInfo.entityFilter,
|
||||
dataKeyTypes,
|
||||
dataKeyTypes, [],
|
||||
{ignoreLoading: true, ignoreErrors: true}
|
||||
).pipe(
|
||||
catchError(() => of([]))
|
||||
|
||||
@ -4125,6 +4125,7 @@
|
||||
"title-tooltip": "Title Tooltip",
|
||||
"general-settings": "General settings",
|
||||
"display-title": "Display widget title",
|
||||
"card-title": "Card title",
|
||||
"drop-shadow": "Drop shadow",
|
||||
"enable-fullscreen": "Enable fullscreen",
|
||||
"background-color": "Background color",
|
||||
@ -4179,6 +4180,7 @@
|
||||
"delete-action-text": "Are you sure you want delete widget action with name '{{actionName}}'?",
|
||||
"title-icon": "Title icon",
|
||||
"display-icon": "Display title icon",
|
||||
"card-icon": "Card icon",
|
||||
"icon-color": "Icon color",
|
||||
"icon-size": "Icon size",
|
||||
"advanced-settings": "Advanced settings",
|
||||
|
||||
@ -1183,4 +1183,166 @@ mat-label {
|
||||
.mat-expansion-panel {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
// Widget config
|
||||
|
||||
.tb-widget-config-panel {
|
||||
box-shadow: 0 0 10px 6px rgba(11, 17, 51, 0.04);
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
gap: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
letter-spacing: 0.15px;
|
||||
position: relative;
|
||||
&.no-padding-bottom {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
&.stroked {
|
||||
box-shadow: none;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 6px;
|
||||
}
|
||||
.mat-expansion-panel {
|
||||
&.tb-settings {
|
||||
box-shadow: none;
|
||||
.mat-content {
|
||||
overflow: visible;
|
||||
}
|
||||
.mat-expansion-panel-header {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0.25px;
|
||||
padding: 0;
|
||||
.mat-content {
|
||||
flex: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
.mat-expansion-indicator {
|
||||
height: 32px;
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
.mat-expansion-panel-header-description {
|
||||
align-items: center;
|
||||
}
|
||||
> .mat-expansion-panel-content {
|
||||
> .mat-expansion-panel-body {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.tb-json-object-panel, .tb-css-content-panel {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
}
|
||||
.mat-expansion-panel-content {
|
||||
font: inherit;
|
||||
}
|
||||
}
|
||||
.mat-slide {
|
||||
margin: 8px 0;
|
||||
.mdc-form-field>label {
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tb-widget-config-panel-title {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
.tb-widget-config-panel-hint {
|
||||
font-size: 12px;
|
||||
color: #808080;
|
||||
}
|
||||
.tb-widget-config-row {
|
||||
height: 56px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding-left: 16px;
|
||||
padding-right: 12px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 6px;
|
||||
&.same-padding {
|
||||
padding-right: 16px;
|
||||
}
|
||||
&.space-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.mat-divider-vertical {
|
||||
height: 56px;
|
||||
}
|
||||
.mat-mdc-form-field {
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
.tb-widget-config-row .mat-mdc-form-field, .mat-mdc-form-field.tb-inline-field {
|
||||
&.mat-form-field-appearance-fill {
|
||||
.mdc-text-field--filled:not(.mdc-text-field--disabled) {
|
||||
&:before {
|
||||
opacity: 0;
|
||||
}
|
||||
.mdc-line-ripple::before {
|
||||
border-bottom-color: rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
.mat-mdc-form-field-focus-overlay {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.mat-mdc-text-field-wrapper {
|
||||
&.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
|
||||
padding-right: 12px;
|
||||
padding-left: 12px;
|
||||
&:not(.mdc-text-field--focused):not(.mdc-text-field--disabled):not(:hover) {
|
||||
.mdc-notched-outline__leading, .mdc-notched-outline__trailing {
|
||||
border-color: rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
.mat-mdc-form-field-infix {
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
min-height: 38px;
|
||||
width: auto;
|
||||
.mdc-text-field__input, .mat-mdc-select {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.center {
|
||||
.mat-mdc-text-field-wrapper {
|
||||
.mat-mdc-form-field-infix {
|
||||
.mdc-text-field__input {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.number {
|
||||
.mat-mdc-text-field-wrapper {
|
||||
padding-right: 4px;
|
||||
.mat-mdc-form-field-infix {
|
||||
input.mdc-text-field__input[type=number]::-webkit-inner-spin-button,
|
||||
input.mdc-text-field__input[type=number]::-webkit-outer-spin-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user