diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json index 1cf107b801..9ab33665e6 100644 --- a/application/src/main/data/json/system/widget_bundles/cards.json +++ b/application/src/main/data/json/system/widget_bundles/cards.json @@ -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}" } }, { diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index ee3e232bba..12f6300b93 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -816,7 +816,8 @@ export class EntityService { ); } - public getEntityKeysByEntityFilter(filter: EntityFilter, types: DataKeyType[], config?: RequestConfig): Observable> { + public getEntityKeysByEntityFilter(filter: EntityFilter, types: DataKeyType[], + entityTypes?: EntityType[], config?: RequestConfig): Observable> { if (!types.length) { return of([]); } @@ -832,7 +833,7 @@ export class EntityService { entitiesKeysByQuery$ = of({ attribute: [], timeseries: [], - entityTypes: [], + entityTypes: entityTypes || [], }); } return entitiesKeysByQuery$.pipe( diff --git a/ui-ngx/src/app/core/services/dashboard-utils.service.ts b/ui-ngx/src/app/core/services/dashboard-utils.service.ts index 243c5e369b..8355ce2f61 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -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(); diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html index fb5336de66..5af51031bf 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html @@ -51,6 +51,7 @@ [widget]="widget" [widgetConfigMode]="widgetConfigMode" [hideHeader]="widgetConfigMode === widgetConfigModes.basic" + isAdd formControlName="widgetConfig">
widget-config.appearance
+
+ + {{ 'widget-config.card-title' | translate }} + + + + +
+
+ + {{ 'widget-config.card-icon' | translate }} + +
+ + + + + +
+
{{ 'widget-config.text-color' | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.ts index 6ec1d6c5c4..4ddd068bb4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.ts @@ -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, + 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 || []; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts index 62d25249e9..b59170fb89 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts @@ -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, + 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: [{ diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html index cf7b380650..0ae6ffb05e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html @@ -136,6 +136,21 @@ +
+ + +
+
+ + +
+
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.scss index a7165af95a..5b3d4f1a80 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.scss @@ -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; + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts index 5664ec8d29..d434ef74e3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts @@ -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, + { + 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() { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html index b4e8795f8f..754f0052c9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html @@ -21,9 +21,10 @@
datakey.key
datakey.label
-
datakey.color
-
widget-config.units-short
-
widget-config.decimals-short
+
datakey.color
+
widget-config.units-short
+
widget-config.decimals-short
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.scss index 944181f085..df8b187535 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.scss @@ -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 { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts index c27de8549b..3d8b80c1df 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts @@ -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; functionTypeKeys: Array; @@ -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 { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts index 68dfca5858..469ff191ed 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts @@ -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, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts index 9d9201e88a..a7cf923c44 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts @@ -155,6 +155,7 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con private dataKeySettingsData: JsonFormComponentData; private alarmKeys: Array; + private functionTypeKeys: Array; filteredKeys: Observable>; private latestKeySearchResult: Array = 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>; 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]; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts index e94a3062e7..ae674a4d55 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts @@ -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, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts index ec7e8d0854..31436c9415 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts @@ -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, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts index 39b3c2a741..9bda9345dc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts @@ -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; 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(); widgetConfigChanged = this.widgetConfigChangedEmitter.asObservable(); - protected constructor(@Inject(Store) protected store: Store) { + protected constructor(@Inject(Store) protected store: Store, + 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); diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.scss b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.scss deleted file mode 100644 index 754afcf1d1..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.scss +++ /dev/null @@ -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; - } - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.html b/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.html index d573e65183..a2ed480388 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.html @@ -15,6 +15,6 @@ limitations under the License. --> - + diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.ts index 4a1665b5b2..e61511698d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.ts @@ -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 { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/simple-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/simple-card-widget-settings.component.ts index b9080b93ff..4ecd21ad1d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/simple-card-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/simple-card-widget-settings.component.ts @@ -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 { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts index d3858b89f6..6417ae4bcb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts @@ -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([])) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/device-key-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/device-key-autocomplete.component.ts index 2ada7e6cfb..22f8bf8e75 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/device-key-autocomplete.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/device-key-autocomplete.component.ts @@ -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([])) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.ts index 7e96d6ba0a..e79735e70d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.ts @@ -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([])) diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts index 744747a50a..999e7e55ff 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts @@ -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([])) diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index bc14e58e98..b6f6bd8386 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -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", diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index 3d4029d564..760ffe88c6 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -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; + } + } + } + } + } + }