From ac56d06a55ba71681f26f5f32e40c0f3c1575a34 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 1 Apr 2022 17:03:57 +0300 Subject: [PATCH] UI: Settings forms for Timeseries table widget --- .../json/system/widget_bundles/cards.json | 15 ++- .../widget/data-key-config.component.ts | 2 +- .../qrcode-widget-settings.component.html | 16 +-- .../qrcode-widget-settings.component.ts | 23 ++-- ...meseries-table-key-settings.component.html | 67 +++++++++++ ...timeseries-table-key-settings.component.ts | 80 +++++++++++++ ...s-table-latest-key-settings.component.html | 74 ++++++++++++ ...ies-table-latest-key-settings.component.ts | 96 +++++++++++++++ ...eries-table-widget-settings.component.html | 95 +++++++++++++++ ...eseries-table-widget-settings.component.ts | 98 +++++++++++++++ .../lib/settings/widget-settings.module.ts | 24 +++- .../widget/lib/settings/widget-settings.scss | 113 ++++++++++++++++++ .../widget/widget-settings.component.ts | 4 + .../pages/widget/widget-editor.component.html | 6 + .../shared/components/js-func.component.html | 7 +- .../shared/components/js-func.component.scss | 5 +- .../shared/components/js-func.component.ts | 2 + ui-ngx/src/app/shared/models/widget.models.ts | 9 +- .../assets/locale/locale.constant-en_US.json | 27 +++++ 19 files changed, 726 insertions(+), 37 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-key-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-key-settings.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-latest-key-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-latest-key-settings.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-widget-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-widget-settings.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.scss 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 23b30f017f..dafd87b14b 100644 --- a/application/src/main/data/json/system/widget_bundles/cards.json +++ b/application/src/main/data/json/system/widget_bundles/cards.json @@ -55,10 +55,13 @@ "templateHtml": "\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onLatestDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"enableSearch\": {\n \"title\": \"Enable search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyHeader\": {\n \"title\": \"Always display header\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyAction\": {\n \"title\": \"Always display actions column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"reserveSpaceForHiddenAction\": {\n \"title\": \"Hidden cell button actions display mode\",\n \"type\": \"string\",\n \"default\": \"true\"\n },\n \"showTimestamp\": {\n \"title\": \"Display timestamp column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showMilliseconds\": {\n \"title\": \"Display timestamp milliseconds\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n }, \n \"useEntityLabel\": {\n \"title\": \"Use entity label in tab name\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"hideEmptyLines\": {\n \"title\": \"Hide empty lines\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"disableStickyHeader\": {\n \"title\": \"Disable sticky header\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"useRowStyleFunction\": {\n \"title\": \"Use row style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"rowStyleFunction\": {\n \"title\": \"Row style function: f(rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"enableSearch\",\n \"enableStickyHeader\",\n \"enableStickyAction\",\n {\n \"key\": \"reserveSpaceForHiddenAction\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"true\",\n \"label\": \"Show empty space instead of hidden cell button action\"\n },\n {\n \"value\": \"false\",\n \"label\": \"Don't reserve space for hidden action buttons\"\n }\n ]\n },\n \"showTimestamp\",\n \"showMilliseconds\",\n \"displayPagination\",\n \"useEntityLabel\",\n \"defaultPageSize\",\n \"hideEmptyLines\",\n \"useRowStyleFunction\",\n {\n \"key\": \"rowStyleFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/timeseries/row_style_fn\",\n \"condition\": \"model.useRowStyleFunction === true\"\n }\n ]\n}", - "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value, rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/timeseries/cell_style_fn\",\n \"condition\": \"model.useCellStyleFunction === true\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/timeseries/cell_content_fn\",\n \"condition\": \"model.useCellContentFunction === true\"\n }\n ]\n}", - "latestDataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"LatestDataKeySettings\",\n \"properties\": {\n \"show\": {\n \"title\": \"Show latest data column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"order\": {\n \"title\": \"Latest data column order\",\n \"type\": \"number\"\n },\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value, rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"show\",\n {\n \"key\": \"order\",\n \"condition\": \"model.show === true\"\n },\n {\n \"key\": \"useCellStyleFunction\",\n \"condition\": \"model.show === true\"\n },\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/timeseries/cell_style_fn\",\n \"condition\": \"model.show === true && model.useCellStyleFunction === true\"\n },\n {\n \"key\": \"useCellContentFunction\",\n \"condition\": \"model.show === true\"\n },\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/timeseries/cell_content_fn\",\n \"condition\": \"model.show === true && model.useCellContentFunction === true\"\n }\n ]\n}", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', amount = percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\"}" + "settingsSchema": "", + "dataKeySettingsSchema": "", + "latestDataKeySettingsSchema": "", + "settingsDirective": "tb-timeseries-table-widget-settings", + "dataKeySettingsDirective": "tb-timeseries-table-key-settings", + "latestDataKeySettingsDirective": "tb-timeseries-table-latest-key-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}],\"latestDataKeys\":null}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"displayTimewindow\":true}" } }, { @@ -164,8 +167,8 @@ "templateHtml": "\n", "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.qrCodeWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n", - "settingsSchema": "{}\n", - "dataKeySettingsSchema": "{}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", "settingsDirective": "tb-qrcode-widget-settings", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7036904308224163,\"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;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"qrCodeTextPattern\":\"${entityName}\",\"useQrCodeTextFunction\":false,\"qrCodeTextFunction\":\"return data[0] ? data[0]['entityName'] : '';\"},\"title\":\"QR Code\"}" } diff --git a/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts index 9c23be8144..b0d8847c1b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts @@ -287,7 +287,7 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con } }; } - if (this.displayAdvanced && !this.dataKeySettingsFormGroup.valid) { + if (this.displayAdvanced && (!this.dataKeySettingsFormGroup.valid || !this.modelValue.settings)) { return { dataKeySettings: { valid: false diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.html index 29a10e1d58..93d3b8086b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.html @@ -15,22 +15,22 @@ limitations under the License. --> -
- +
+ {{ 'widgets.qr-code.use-qr-code-text-function' | translate }} - - + + widgets.qr-code.qr-code-text-pattern {{ 'widgets.qr-code.qr-code-text-pattern-required' | translate }} -
- - +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.ts index 06e8d7d079..c9c43d2951 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.ts @@ -14,22 +14,19 @@ /// limitations under the License. /// -import { Component, ViewChild } from '@angular/core'; +import { Component } from '@angular/core'; import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; -import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { JsFuncComponent } from '@shared/components/js-func.component'; @Component({ selector: 'tb-qrcode-widget-settings', templateUrl: './qrcode-widget-settings.component.html', - styleUrls: [] + styleUrls: ['./widget-settings.scss'] }) export class QrCodeWidgetSettingsComponent extends WidgetSettingsComponent { - @ViewChild('qrCodeTextFunctionComponent', {static: true}) qrCodeTextFunctionComponent: JsFuncComponent; - qrCodeWidgetSettingsForm: FormGroup; constructor(protected store: Store, @@ -51,8 +48,8 @@ export class QrCodeWidgetSettingsComponent extends WidgetSettingsComponent { protected onSettingsSet(settings: WidgetSettings) { this.qrCodeWidgetSettingsForm = this.fb.group({ - qrCodeTextPattern: [settings.qrCodeTextPattern, []], - useQrCodeTextFunction: [settings.useQrCodeTextFunction, []], + qrCodeTextPattern: [settings.qrCodeTextPattern, [Validators.required]], + useQrCodeTextFunction: [settings.useQrCodeTextFunction, [Validators.required]], qrCodeTextFunction: [settings.qrCodeTextFunction, []] }); } @@ -64,13 +61,11 @@ export class QrCodeWidgetSettingsComponent extends WidgetSettingsComponent { protected updateValidators(emitEvent: boolean) { const useQrCodeTextFunction: boolean = this.qrCodeWidgetSettingsForm.get('useQrCodeTextFunction').value; if (useQrCodeTextFunction) { - this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').setValidators([]); - this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').setValidators([Validators.required, - (control: AbstractControl) => this.qrCodeTextFunctionComponent.validate(control as FormControl) - ]); + this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').disable(); + this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').enable(); } else { - this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').setValidators([Validators.required]); - this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').setValidators([]); + this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').enable(); + this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').disable(); } this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').updateValueAndValidity({emitEvent}); this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').updateValueAndValidity({emitEvent}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-key-settings.component.html new file mode 100644 index 0000000000..bf568fc3eb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-key-settings.component.html @@ -0,0 +1,67 @@ + +
+
+ widgets.table.cell-style + + + + + {{ 'widgets.table.use-cell-style-function' | translate }} + + + + widget-config.advanced-settings + + + + + + + +
+
+ widgets.table.cell-content + + + + + {{ 'widgets.table.use-cell-content-function' | translate }} + + + + widget-config.advanced-settings + + + + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-key-settings.component.ts new file mode 100644 index 0000000000..b2433dfde0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-key-settings.component.ts @@ -0,0 +1,80 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; + +@Component({ + selector: 'tb-timeseries-table-key-settings', + templateUrl: './timeseries-table-key-settings.component.html', + styleUrls: ['./widget-settings.scss'] +}) +export class TimeseriesTableKeySettingsComponent extends WidgetSettingsComponent { + + timeseriesTableKeySettingsForm: FormGroup; + + constructor(protected store: Store, + private fb: FormBuilder) { + super(store); + } + + protected settingsForm(): FormGroup { + return this.timeseriesTableKeySettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return { + useCellStyleFunction: false, + cellStyleFunction: '', + useCellContentFunction: false, + cellContentFunction: '' + }; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.timeseriesTableKeySettingsForm = this.fb.group({ + useCellStyleFunction: [settings.useCellStyleFunction, []], + cellStyleFunction: [settings.cellStyleFunction, [Validators.required]], + useCellContentFunction: [settings.useCellContentFunction, []], + cellContentFunction: [settings.cellContentFunction, [Validators.required]], + }); + } + + protected validatorTriggers(): string[] { + return ['useCellStyleFunction', 'useCellContentFunction']; + } + + protected updateValidators(emitEvent: boolean) { + const useCellStyleFunction: boolean = this.timeseriesTableKeySettingsForm.get('useCellStyleFunction').value; + const useCellContentFunction: boolean = this.timeseriesTableKeySettingsForm.get('useCellContentFunction').value; + if (useCellStyleFunction) { + this.timeseriesTableKeySettingsForm.get('cellStyleFunction').enable(); + } else { + this.timeseriesTableKeySettingsForm.get('cellStyleFunction').disable(); + } + if (useCellContentFunction) { + this.timeseriesTableKeySettingsForm.get('cellContentFunction').enable(); + } else { + this.timeseriesTableKeySettingsForm.get('cellContentFunction').disable(); + } + this.timeseriesTableKeySettingsForm.get('cellStyleFunction').updateValueAndValidity({emitEvent}); + this.timeseriesTableKeySettingsForm.get('cellContentFunction').updateValueAndValidity({emitEvent}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-latest-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-latest-key-settings.component.html new file mode 100644 index 0000000000..b005b45ce8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-latest-key-settings.component.html @@ -0,0 +1,74 @@ + +
+ + {{ 'widgets.table.show-latest-data-column' | translate }} + + + widgets.table.latest-data-column-order + + +
+ widgets.table.cell-style + + + + + {{ 'widgets.table.use-cell-style-function' | translate }} + + + + widget-config.advanced-settings + + + + + + + +
+
+ widgets.table.cell-content + + + + + {{ 'widgets.table.use-cell-content-function' | translate }} + + + + widget-config.advanced-settings + + + + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-latest-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-latest-key-settings.component.ts new file mode 100644 index 0000000000..1d8d26ead3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-latest-key-settings.component.ts @@ -0,0 +1,96 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; + +@Component({ + selector: 'tb-timeseries-table-latest-key-settings', + templateUrl: './timeseries-table-latest-key-settings.component.html', + styleUrls: ['./widget-settings.scss'] +}) +export class TimeseriesTableLatestKeySettingsComponent extends WidgetSettingsComponent { + + timeseriesTableLatestKeySettingsForm: FormGroup; + + constructor(protected store: Store, + private fb: FormBuilder) { + super(store); + } + + protected settingsForm(): FormGroup { + return this.timeseriesTableLatestKeySettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return { + show: true, + useCellStyleFunction: false, + cellStyleFunction: '', + useCellContentFunction: false, + cellContentFunction: '' + }; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.timeseriesTableLatestKeySettingsForm = this.fb.group({ + show: [settings.show, []], + order: [settings.order, []], + useCellStyleFunction: [settings.useCellStyleFunction, []], + cellStyleFunction: [settings.cellStyleFunction, [Validators.required]], + useCellContentFunction: [settings.useCellContentFunction, []], + cellContentFunction: [settings.cellContentFunction, [Validators.required]], + }); + } + + protected validatorTriggers(): string[] { + return ['show', 'useCellStyleFunction', 'useCellContentFunction']; + } + + protected updateValidators(emitEvent: boolean) { + const show: boolean = this.timeseriesTableLatestKeySettingsForm.get('show').value; + if (show) { + this.timeseriesTableLatestKeySettingsForm.get('order').enable(); + this.timeseriesTableLatestKeySettingsForm.get('useCellStyleFunction').enable({emitEvent: false}); + this.timeseriesTableLatestKeySettingsForm.get('useCellContentFunction').enable({emitEvent: false}); + const useCellStyleFunction: boolean = this.timeseriesTableLatestKeySettingsForm.get('useCellStyleFunction').value; + const useCellContentFunction: boolean = this.timeseriesTableLatestKeySettingsForm.get('useCellContentFunction').value; + if (useCellStyleFunction) { + this.timeseriesTableLatestKeySettingsForm.get('cellStyleFunction').enable(); + } else { + this.timeseriesTableLatestKeySettingsForm.get('cellStyleFunction').disable(); + } + if (useCellContentFunction) { + this.timeseriesTableLatestKeySettingsForm.get('cellContentFunction').enable(); + } else { + this.timeseriesTableLatestKeySettingsForm.get('cellContentFunction').disable(); + } + } else { + this.timeseriesTableLatestKeySettingsForm.get('order').disable(); + this.timeseriesTableLatestKeySettingsForm.get('useCellStyleFunction').disable({emitEvent: false}); + this.timeseriesTableLatestKeySettingsForm.get('cellStyleFunction').disable(); + this.timeseriesTableLatestKeySettingsForm.get('useCellContentFunction').disable({emitEvent: false}); + this.timeseriesTableLatestKeySettingsForm.get('cellContentFunction').disable(); + } + this.timeseriesTableLatestKeySettingsForm.get('order').updateValueAndValidity({emitEvent}); + this.timeseriesTableLatestKeySettingsForm.get('cellStyleFunction').updateValueAndValidity({emitEvent}); + this.timeseriesTableLatestKeySettingsForm.get('cellContentFunction').updateValueAndValidity({emitEvent}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-widget-settings.component.html new file mode 100644 index 0000000000..5f90fd6aed --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-widget-settings.component.html @@ -0,0 +1,95 @@ + +
+
+ widgets.table.common-table-settings +
+
+ + {{ 'widgets.table.enable-search' | translate }} + +
+
+ + {{ 'widgets.table.enable-sticky-header' | translate }} + + + {{ 'widgets.table.enable-sticky-action' | translate }} + +
+
+ + widgets.table.hidden-cell-button-display-mode + + + {{ 'widgets.table.show-empty-space-hidden-action' | translate }} + + + {{ 'widgets.table.dont-reserve-space-hidden-action' | translate }} + + + +
+ + {{ 'widgets.table.display-timestamp' | translate }} + + + {{ 'widgets.table.display-milliseconds' | translate }} + +
+ + {{ 'widgets.table.display-pagination' | translate }} + + + widgets.table.default-page-size + + +
+ + {{ 'widgets.table.use-entity-label-tab-name' | translate }} + + + {{ 'widgets.table.hide-empty-lines' | translate }} + +
+
+
+ widgets.table.row-style + + + + + {{ 'widgets.table.use-row-style-function' | translate }} + + + + widget-config.advanced-settings + + + + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-widget-settings.component.ts new file mode 100644 index 0000000000..d28b4f081a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/timeseries-table-widget-settings.component.ts @@ -0,0 +1,98 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; + +@Component({ + selector: 'tb-timeseries-table-widget-settings', + templateUrl: './timeseries-table-widget-settings.component.html', + styleUrls: ['./widget-settings.scss'] +}) +export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsComponent { + + timeseriesTableWidgetSettingsForm: FormGroup; + + constructor(protected store: Store, + private fb: FormBuilder) { + super(store); + } + + protected settingsForm(): FormGroup { + return this.timeseriesTableWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return { + enableSearch: true, + enableStickyHeader: true, + enableStickyAction: true, + reserveSpaceForHiddenAction: 'true', + showTimestamp: true, + showMilliseconds: false, + displayPagination: true, + useEntityLabel: false, + defaultPageSize: 10, + hideEmptyLines: false, + disableStickyHeader: false, + useRowStyleFunction: false, + rowStyleFunction: '' + }; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.timeseriesTableWidgetSettingsForm = this.fb.group({ + enableSearch: [settings.enableSearch, []], + enableStickyHeader: [settings.enableStickyHeader, []], + enableStickyAction: [settings.enableStickyAction, []], + reserveSpaceForHiddenAction: [settings.reserveSpaceForHiddenAction, []], + showTimestamp: [settings.showTimestamp, []], + showMilliseconds: [settings.showMilliseconds, []], + displayPagination: [settings.displayPagination, []], + useEntityLabel: [settings.useEntityLabel, []], + defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]], + hideEmptyLines: [settings.hideEmptyLines, []], + disableStickyHeader: [settings.disableStickyHeader, []], + useRowStyleFunction: [settings.useRowStyleFunction, []], + rowStyleFunction: [settings.rowStyleFunction, [Validators.required]] + }); + } + + protected validatorTriggers(): string[] { + return ['useRowStyleFunction', 'displayPagination']; + } + + protected updateValidators(emitEvent: boolean) { + const useRowStyleFunction: boolean = this.timeseriesTableWidgetSettingsForm.get('useRowStyleFunction').value; + const displayPagination: boolean = this.timeseriesTableWidgetSettingsForm.get('displayPagination').value; + if (useRowStyleFunction) { + this.timeseriesTableWidgetSettingsForm.get('rowStyleFunction').enable(); + } else { + this.timeseriesTableWidgetSettingsForm.get('rowStyleFunction').disable(); + } + if (displayPagination) { + this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').enable(); + } else { + this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').disable(); + } + this.timeseriesTableWidgetSettingsForm.get('rowStyleFunction').updateValueAndValidity({emitEvent}); + this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').updateValueAndValidity({emitEvent}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 7a10d51465..e31eef1f87 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -20,10 +20,22 @@ import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module'; import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module'; import { IWidgetSettingsComponent } from '@shared/models/widget.models'; +import { + TimeseriesTableWidgetSettingsComponent +} from '@home/components/widget/lib/settings/timeseries-table-widget-settings.component'; +import { + TimeseriesTableKeySettingsComponent +} from '@home/components/widget/lib/settings/timeseries-table-key-settings.component'; +import { + TimeseriesTableLatestKeySettingsComponent +} from '@home/components/widget/lib/settings/timeseries-table-latest-key-settings.component'; @NgModule({ declarations: [ - QrCodeWidgetSettingsComponent + QrCodeWidgetSettingsComponent, + TimeseriesTableWidgetSettingsComponent, + TimeseriesTableKeySettingsComponent, + TimeseriesTableLatestKeySettingsComponent ], imports: [ CommonModule, @@ -31,12 +43,18 @@ import { IWidgetSettingsComponent } from '@shared/models/widget.models'; SharedHomeComponentsModule ], exports: [ - QrCodeWidgetSettingsComponent + QrCodeWidgetSettingsComponent, + TimeseriesTableWidgetSettingsComponent, + TimeseriesTableKeySettingsComponent, + TimeseriesTableLatestKeySettingsComponent ] }) export class WidgetSettingsModule { } export const widgetSettingsComponentsMap: {[key: string]: Type} = { - 'tb-qrcode-widget-settings': QrCodeWidgetSettingsComponent + 'tb-qrcode-widget-settings': QrCodeWidgetSettingsComponent, + 'tb-timeseries-table-widget-settings': TimeseriesTableWidgetSettingsComponent, + 'tb-timeseries-table-key-settings': TimeseriesTableKeySettingsComponent, + 'tb-timeseries-table-latest-key-settings': TimeseriesTableLatestKeySettingsComponent }; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.scss new file mode 100644 index 0000000000..4b9e0edd97 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.scss @@ -0,0 +1,113 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .tb-widget-settings { + .fields-group { + padding: 0 16px 8px; + margin-bottom: 10px; + border: 1px groove rgba(0, 0, 0, .25); + border-radius: 4px; + + legend { + color: rgba(0, 0, 0, .7); + width: fit-content; + } + + legend + * { + margin-top: 16px; + } + + &.fields-group-slider { + padding: 0; + + legend { + margin-left: 16px; + } + + .tb-settings { + margin-top: 0; + padding: 0 16px 8px; + } + } + } + } +} + +:host ::ng-deep { + .tb-widget-settings { + .mat-checkbox-label { + white-space: normal; + } + + .mat-expansion-panel { + &.tb-settings { + box-shadow: none; + + .mat-content { + overflow: visible; + } + + .mat-expansion-panel-header { + padding: 0; + + &:hover { + background: none; + } + + .mat-expansion-indicator { + padding: 2px; + } + } + + .mat-expansion-panel-header-description { + align-items: center; + } + + .mat-expansion-panel-body { + padding: 0; + } + + .mat-checkbox-layout { + margin: 5px 0; + } + + .mat-checkbox-inner-container { + margin-right: 12px; + } + } + + .mat-expansion-panel-content { + font: inherit; + } + } + + .mat-slide { + margin: 8px 0; + } + + .slide-block { + display: block; + + &:not(:last-child) { + margin-bottom: 8px; + } + } + + .mat-slide-toggle-content { + white-space: normal; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-settings.component.ts index 9226c4e1f1..15919447ea 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-settings.component.ts @@ -43,6 +43,7 @@ import { JsonFormComponentData } from '@shared/components/json-form/json-form-co import { IWidgetSettingsComponent, Widget, WidgetSettings } from '@shared/models/widget.models'; import { widgetSettingsComponentsMap } from '@home/components/widget/lib/settings/widget-settings.module'; import { Dashboard } from '@shared/models/dashboard.models'; +import { WidgetService } from '@core/http/widget.service'; @Component({ selector: 'tb-widget-settings', @@ -100,6 +101,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On constructor(private translate: TranslateService, private cfr: ComponentFactoryResolver, + private widgetService: WidgetService, private fb: FormBuilder) { this.widgetSettingsFormGroup = this.fb.group({ settings: [null, Validators.required] @@ -143,6 +145,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On if (this.definedSettingsComponent) { this.definedSettingsComponent.dashboard = this.dashboard; this.definedSettingsComponent.widget = this.widget; + this.definedSettingsComponent.functionScopeVariables = this.widgetService.getWidgetScopeVariables(); this.definedSettingsComponent.settings = this.widgetSettingsFormData.model; this.changeSubscription = this.definedSettingsComponent.settingsChanged.subscribe((settings) => { this.updateModel(settings); @@ -196,6 +199,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On this.definedSettingsComponent = this.definedSettingsComponentRef.instance; this.definedSettingsComponent.dashboard = this.dashboard; this.definedSettingsComponent.widget = this.widget; + this.definedSettingsComponent.functionScopeVariables = this.widgetService.getWidgetScopeVariables(); this.changeSubscription = this.definedSettingsComponent.settingsChanged.subscribe((settings) => { this.updateModel(settings); }); diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html index 549230d6a3..13c03b7945 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html @@ -265,6 +265,12 @@ [(ngModel)]="widget.dataKeySettingsDirective" (ngModelChange)="isDirty = true"/> + + widget.latest-data-key-settings-form-selector + + diff --git a/ui-ngx/src/app/shared/components/js-func.component.html b/ui-ngx/src/app/shared/components/js-func.component.html index 3769e1ff45..a5f4040af1 100644 --- a/ui-ngx/src/app/shared/components/js-func.component.html +++ b/ui-ngx/src/app/shared/components/js-func.component.html @@ -19,7 +19,8 @@ tb-fullscreen [fullscreen]="fullscreen" fxLayout="column">
- + +
-
+
- +
diff --git a/ui-ngx/src/app/shared/components/js-func.component.scss b/ui-ngx/src/app/shared/components/js-func.component.scss index 6cb0154c88..3a7ddc3d09 100644 --- a/ui-ngx/src/app/shared/components/js-func.component.scss +++ b/ui-ngx/src/app/shared/components/js-func.component.scss @@ -26,9 +26,12 @@ .tb-js-func-panel { height: calc(100% - 80px); - margin-left: 15px; border: 1px solid #c0c0c0; + &:not(.tb-js-func-title) { + margin-left: 15px; + } + #tb-javascript-input { width: 100%; min-width: 200px; diff --git a/ui-ngx/src/app/shared/components/js-func.component.ts b/ui-ngx/src/app/shared/components/js-func.component.ts index 439677ffe5..be78c7480c 100644 --- a/ui-ngx/src/app/shared/components/js-func.component.ts +++ b/ui-ngx/src/app/shared/components/js-func.component.ts @@ -69,6 +69,8 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, toastTargetId = `jsFuncEditor-${guid()}`; + @Input() functionTitle: string; + @Input() functionName: string; @Input() functionArgs: Array; diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index bbb2ea56d2..1f3cece5cb 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -615,6 +615,7 @@ export interface WidgetSize { export interface IWidgetSettingsComponent { dashboard: Dashboard; widget: Widget; + functionScopeVariables: string[]; settings: WidgetSettings; settingsChanged: Observable; validate(); @@ -630,12 +631,18 @@ export abstract class WidgetSettingsComponent extends PageComponent implements widget: Widget; + functionScopeVariables: string[]; + settingsValue: WidgetSettings; private settingsSet = false; set settings(value: WidgetSettings) { - this.settingsValue = value || this.defaultSettings(); + if (!value) { + this.settingsValue = this.defaultSettings(); + } else { + this.settingsValue = {...this.defaultSettings(), ...value}; + } if (!this.settingsSet) { this.settingsSet = true; this.setupSettings(this.settingsValue); 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 f904f10930..47d22d9b3f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3001,6 +3001,7 @@ "image-preview": "Image preview", "settings-form-selector": "Settings form selector", "data-key-settings-form-selector": "Data key settings form selector", + "latest-data-key-settings-form-selector": "Latest data key settings form selector", "javascript": "Javascript", "js": "JS", "remove-widget-type-title": "Are you sure you want to remove the widget type '{{widgetName}}'?", @@ -3366,6 +3367,32 @@ "drawCircleMarkerButton": "Create circle marker", "rotateButton": "Rotate polygon" } + }, + "table": { + "common-table-settings": "Common Table Settings", + "enable-search": "Enable search", + "enable-sticky-header": "Always display header", + "enable-sticky-action": "Always display actions column", + "hidden-cell-button-display-mode": "Hidden cell button actions display mode", + "show-empty-space-hidden-action": "Show empty space instead of hidden cell button action", + "dont-reserve-space-hidden-action": "Don't reserve space for hidden action buttons", + "display-timestamp": "Display timestamp column", + "display-milliseconds": "Display timestamp milliseconds", + "display-pagination": "Display pagination", + "default-page-size": "Default page size", + "use-entity-label-tab-name": "Use entity label in tab name", + "hide-empty-lines": "Hide empty lines", + "row-style": "Row style", + "use-row-style-function": "Use row style function", + "row-style-function": "Row style function", + "cell-style": "Cell style", + "use-cell-style-function": "Use cell style function", + "cell-style-function": "Cell style function", + "cell-content": "Cell content", + "use-cell-content-function": "Use cell content function", + "cell-content-function": "Cell content function", + "show-latest-data-column": "Show latest data column", + "latest-data-column-order": "Latest data column order" } }, "icon": {