diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json index 4313a21698..83f75ae302 100644 --- a/application/src/main/data/json/system/widget_bundles/charts.json +++ b/application/src/main/data/json/system/widget_bundles/charts.json @@ -146,9 +146,10 @@ "resources": [], "templateHtml": "", "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true,\n hasAdditionalLatestDataKeys: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.getLatestDataKeySettingsSchema = function() {\n return TbFlot.latestDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true,\n hasAdditionalLatestDataKeys: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.getLatestDataKeySettingsSchema = function() {\n return TbFlot.latestDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", "settingsSchema": "{}", "dataKeySettingsSchema": "{}", + "settingsDirective": "tb-flot-line-widget-settings", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" } }, @@ -164,10 +165,11 @@ "resources": [], "templateHtml": "", "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.getLatestDataKeySettingsSchema = function() {\n return TbFlot.latestDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true\n };\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.getLatestDataKeySettingsSchema = function() {\n return TbFlot.latestDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true\n };\n}\n", "settingsSchema": "{}", "dataKeySettingsSchema": "{}", "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-flot-line-widget-settings", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"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;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"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\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Timeseries Line Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}" } }, @@ -183,9 +185,10 @@ "resources": [], "templateHtml": "", "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('bar');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false, 'bar');\n}\n\nself.getLatestDataKeySettingsSchema = function() {\n return TbFlot.latestDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true\n };\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false, 'bar');\n}\n\nself.getLatestDataKeySettingsSchema = function() {\n return TbFlot.latestDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true\n };\n}\n", "settingsSchema": "{}", "dataKeySettingsSchema": "{}", + "settingsDirective": "tb-flot-bar-widget-settings", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":true,\"tooltipIndividual\":false,\"defaultBarWidth\":600},\"title\":\"Timeseries Bar Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{}}" } } diff --git a/ui-ngx/.editorconfig b/ui-ngx/.editorconfig index a1a4fbe770..6279ea642d 100644 --- a/ui-ngx/.editorconfig +++ b/ui-ngx/.editorconfig @@ -24,6 +24,9 @@ indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true +[*.json] +indent_size = 4 + [*.md] max_line_length = off trim_trailing_whitespace = false diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts index f790393dad..66317bf596 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts @@ -100,8 +100,8 @@ export interface TbFlotGridSettings { outlineWidth: number; verticalLines: boolean; horizontalLines: boolean; - minBorderMargin: number; - margin: number; + minBorderMargin?: number; + margin?: number; } export interface TbFlotXAxisSettings { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-widget-settings.component.html new file mode 100644 index 0000000000..b91b716ce3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-widget-settings.component.html @@ -0,0 +1,23 @@ + + +
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-widget-settings.component.ts new file mode 100644 index 0000000000..305780dc5c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-widget-settings.component.ts @@ -0,0 +1,55 @@ +/// +/// 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'; +import { flotDefaultSettings } from '@home/components/widget/lib/settings/chart/flot-widget-settings.component'; + +@Component({ + selector: 'tb-flot-bar-widget-settings', + templateUrl: './flot-bar-widget-settings.component.html', + styleUrls: [] +}) +export class FlotBarWidgetSettingsComponent extends WidgetSettingsComponent { + + flotBarWidgetSettingsForm: FormGroup; + + constructor(protected store: Store, + private fb: FormBuilder) { + super(store); + } + + protected settingsForm(): FormGroup { + return this.flotBarWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return flotDefaultSettings('bar'); + } + + protected onSettingsSet(settings: WidgetSettings) { + this.flotBarWidgetSettingsForm = this.fb.group({ + flotSettings: [settings, []] + }); + } + + protected prepareOutputSettings(settings: any): WidgetSettings { + return settings.flotSettings; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-line-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-line-widget-settings.component.html new file mode 100644 index 0000000000..299627f9e2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-line-widget-settings.component.html @@ -0,0 +1,23 @@ + + +
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-line-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-line-widget-settings.component.ts new file mode 100644 index 0000000000..0ce8dbe2a1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-line-widget-settings.component.ts @@ -0,0 +1,55 @@ +/// +/// 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'; +import { flotDefaultSettings } from '@home/components/widget/lib/settings/chart/flot-widget-settings.component'; + +@Component({ + selector: 'tb-flot-line-widget-settings', + templateUrl: './flot-line-widget-settings.component.html', + styleUrls: [] +}) +export class FlotLineWidgetSettingsComponent extends WidgetSettingsComponent { + + flotLineWidgetSettingsForm: FormGroup; + + constructor(protected store: Store, + private fb: FormBuilder) { + super(store); + } + + protected settingsForm(): FormGroup { + return this.flotLineWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return flotDefaultSettings('graph'); + } + + protected onSettingsSet(settings: WidgetSettings) { + this.flotLineWidgetSettingsForm = this.fb.group({ + flotSettings: [settings, []] + }); + } + + protected prepareOutputSettings(settings: any): WidgetSettings { + return settings.flotSettings; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.html new file mode 100644 index 0000000000..9f375368f7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.html @@ -0,0 +1,327 @@ + +
+
+ widgets.chart.common-settings + + {{ 'widgets.chart.enable-stacking-mode' | translate }} + +
+ + widgets.chart.line-shadow-size + + + + {{ 'widgets.chart.display-smooth-lines' | translate }} + +
+
+ + widgets.chart.default-bar-width + + + + widgets.chart.bar-alignment + + + {{ 'widgets.chart.bar-alignment-left' | translate }} + + + {{ 'widgets.chart.bar-alignment-right' | translate }} + + + {{ 'widgets.chart.bar-alignment-center' | translate }} + + + +
+
+ + widgets.chart.default-font-size + + + + +
+ + widgets.chart.thresholds-line-width + + +
+
+ widgets.chart.tooltip-settings + + + + + {{ 'widgets.chart.show-tooltip' | translate }} + + + + widget-config.advanced-settings + + + +
+ + {{ 'widgets.chart.hover-individual-points' | translate }} + + + {{ 'widgets.chart.show-cumulative-values' | translate }} + + + {{ 'widgets.chart.hide-zero-false-values' | translate }} + + + +
+
+
+
+
+ widgets.chart.grid-settings + + {{ 'widgets.chart.show-vertical-lines' | translate }} + + + {{ 'widgets.chart.show-horizontal-lines' | translate }} + + + widgets.chart.grid-outline-border-width + + + + + + + + +
+
+ widgets.chart.xaxis-settings + + widgets.chart.axis-title + + +
+ widgets.chart.xaxis-tick-labels-settings + + + + + {{ 'widgets.chart.show-tick-labels' | translate }} + + + + widget-config.advanced-settings + + + + + + + +
+
+
+ widgets.chart.yaxis-settings + + widgets.chart.axis-title + + +
+ + widgets.chart.min-scale-value + + + + widgets.chart.max-scale-value + + +
+
+ widgets.chart.yaxis-tick-labels-settings + + + + + {{ 'widgets.chart.show-tick-labels' | translate }} + + + + widget-config.advanced-settings + + + + + +
+ + widgets.chart.tick-step-size + + + + widgets.chart.number-of-decimals + + +
+ + +
+
+
+
+
+ widgets.chart.comparison-settings + + + + + {{ 'widgets.chart.enable-comparison' | translate }} + + + + widget-config.advanced-settings + + + +
+ + widgets.chart.time-for-comparison + + + {{ 'widgets.chart.time-for-comparison-previous-interval' | translate }} + + + {{ 'widgets.chart.time-for-comparison-days' | translate }} + + + {{ 'widgets.chart.time-for-comparison-weeks' | translate }} + + + {{ 'widgets.chart.time-for-comparison-months' | translate }} + + + {{ 'widgets.chart.time-for-comparison-years' | translate }} + + + {{ 'widgets.chart.time-for-comparison-custom-interval' | translate }} + + + + + widgets.chart.custom-interval-value + + +
+ widgets.chart.comparison-x-axis-settings + + widgets.chart.axis-title + + + + widgets.chart.axis-position + + + {{ 'widgets.chart.axis-position-top' | translate }} + + + {{ 'widgets.chart.axis-position-bottom' | translate }} + + + + + {{ 'widgets.chart.show-tick-labels' | translate }} + +
+
+
+
+
+
+ widgets.chart.custom-legend-settings + + + + + {{ 'widgets.chart.enable-custom-legend' | translate }} + + + + widget-config.advanced-settings + + + +
+ widgets.chart.label-keys-list +
+
+
+ + +
+
+
+ widgets.chart.no-label-keys +
+
+ +
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts new file mode 100644 index 0000000000..8accd374a6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts @@ -0,0 +1,411 @@ +/// +/// 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, forwardRef, Input, OnInit } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormArray, + FormBuilder, + FormControl, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + Validator, + Validators +} from '@angular/forms'; +import { PageComponent } from '@shared/components/page.component'; +import { ChartType, TbFlotSettings } from '@home/components/widget/lib/flot-widget.models'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { TranslateService } from '@ngx-translate/core'; +import { ComparisonDuration } from '@shared/models/time/time.models'; +import { WidgetService } from '@core/http/widget.service'; +import { fixedColorLevelValidator } from '@home/components/widget/lib/settings/gauge/fixed-color-level.component'; +import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { + LabelDataKey, + labelDataKeyValidator +} from '@home/components/widget/lib/settings/chart/label-data-key.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; + +export function flotDefaultSettings(chartType: ChartType): Partial { + const settings: Partial = { + stack: false, + fontColor: '#545454', + fontSize: 10, + showTooltip: true, + tooltipIndividual: false, + tooltipCumulative: false, + hideZeros: false, + tooltipValueFormatter: '', + grid: { + verticalLines: true, + horizontalLines: true, + outlineWidth: 1, + color: '#545454', + backgroundColor: null, + tickColor: '#DDDDDD' + }, + xaxis: { + title: null, + showLabels: true, + color: null + }, + yaxis: { + min: null, + max: null, + title: null, + showLabels: true, + color: null, + tickSize: null, + tickDecimals: 0, + ticksFormatter: '' + } + }; + if (chartType === 'graph') { + settings.smoothLines = false; + settings.shadowSize = 4; + } + if (chartType === 'bar') { + settings.defaultBarWidth = 600; + settings.barAlignment = 'left'; + } + if (chartType === 'graph' || chartType === 'bar') { + settings.thresholdsLineWidth = null; + settings.comparisonEnabled = false; + settings.timeForComparison = 'previousInterval'; + settings.comparisonCustomIntervalValue = 7200000; + settings.xaxisSecond = { + title: null, + axisPosition: 'top', + showLabels: true + }; + settings.customLegendEnabled = false; + settings.dataKeysListForLabels = []; + } + return settings; +} + +@Component({ + selector: 'tb-flot-widget-settings', + templateUrl: './flot-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => FlotWidgetSettingsComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => FlotWidgetSettingsComponent), + multi: true, + } + ] +}) +export class FlotWidgetSettingsComponent extends PageComponent implements OnInit, ControlValueAccessor, Validator { + + @Input() + disabled: boolean; + + @Input() + chartType: ChartType; + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + private modelValue: TbFlotSettings; + + private propagateChange = null; + + public flotSettingsFormGroup: FormGroup; + + constructor(protected store: Store, + private translate: TranslateService, + private widgetService: WidgetService, + private fb: FormBuilder) { + super(store); + } + + ngOnInit(): void { + this.flotSettingsFormGroup = this.fb.group({ + + // Common settings + + stack: [false, []], + fontSize: [10, [Validators.min(0)]], + fontColor: ['#545454', []], + + // Tooltip settings + + showTooltip: [true, []], + tooltipIndividual: [false, []], + tooltipCumulative: [false, []], + hideZeros: [false, []], + tooltipValueFormatter: ['', []], + + // Grid settings + + grid: this.fb.group({ + verticalLines: [true, []], + horizontalLines: [true, []], + outlineWidth: [1, [Validators.min(0)]], + color: ['#545454', []], + backgroundColor: [null, []], + tickColor: ['#DDDDDD', []] + }), + + // X axis settings + + xaxis: this.fb.group({ + title: [null, []], + + // --> X axis tick labels settings + + showLabels: [true, []], + color: [null, []], + }), + + // Y axis settings + + yaxis: this.fb.group({ + min: [null, []], + max: [null, []], + title: [null, []], + + // --> Y axis tick labels settings + + showLabels: [true, []], + color: [null, []], + tickSize: [null, [Validators.min(0)]], + tickDecimals: [0, [Validators.min(0)]], + ticksFormatter: ['', []] + }) + }); + if (this.chartType === 'graph') { + // Common settings + this.flotSettingsFormGroup.addControl('shadowSize', this.fb.control(4, [Validators.min(0)])); + this.flotSettingsFormGroup.addControl('smoothLines', this.fb.control(false, [])); + } else if (this.chartType === 'bar') { + // Common settings + this.flotSettingsFormGroup.addControl('defaultBarWidth', this.fb.control(600, [Validators.min(0)])); + this.flotSettingsFormGroup.addControl('barAlignment', this.fb.control('left', [])); + } + if (this.chartType === 'graph' || this.chartType === 'bar') { + // Common settings + this.flotSettingsFormGroup.addControl('thresholdsLineWidth', this.fb.control(null, [Validators.min(0)])); + + // Comparison settings + + this.flotSettingsFormGroup.addControl('comparisonEnabled', this.fb.control(false, [])); + this.flotSettingsFormGroup.addControl('timeForComparison', this.fb.control('previousInterval', [])); + this.flotSettingsFormGroup.addControl('comparisonCustomIntervalValue', this.fb.control(7200000, [Validators.min(0)])); + + // --> Comparison X axis settings + + this.flotSettingsFormGroup.addControl('xaxisSecond', this.fb.group({ + axisPosition: ['top', []], + title: [null, []], + showLabels: [true, []] + })); + + // Custom legend settings + + this.flotSettingsFormGroup.addControl('customLegendEnabled', this.fb.control(false, [])); + this.flotSettingsFormGroup.addControl('dataKeysListForLabels', this.fb.control(this.fb.array([]), [])); + } + + this.flotSettingsFormGroup.get('showTooltip').valueChanges.subscribe(() => { + this.updateValidators(true); + }); + + this.flotSettingsFormGroup.get('xaxis.showLabels').valueChanges.subscribe(() => { + this.updateValidators(true); + }); + + this.flotSettingsFormGroup.get('yaxis.showLabels').valueChanges.subscribe(() => { + this.updateValidators(true); + }); + + if (this.chartType === 'graph' || this.chartType === 'bar') { + this.flotSettingsFormGroup.get('comparisonEnabled').valueChanges.subscribe(() => { + this.updateValidators(true); + }); + this.flotSettingsFormGroup.get('timeForComparison').valueChanges.subscribe(() => { + this.updateValidators(true); + }); + this.flotSettingsFormGroup.get('customLegendEnabled').valueChanges.subscribe(() => { + this.updateValidators(true); + }); + } + + this.flotSettingsFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + + this.updateValidators(false); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.flotSettingsFormGroup.disable({emitEvent: false}); + } else { + this.flotSettingsFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: TbFlotSettings): void { + const dataKeysListForLabels = value?.dataKeysListForLabels; + this.modelValue = value; + this.flotSettingsFormGroup.patchValue( + value, {emitEvent: false} + ); + const dataKeysListForLabelsControls: Array = []; + if (dataKeysListForLabels && dataKeysListForLabels.length) { + dataKeysListForLabels.forEach((labelDataKey) => { + dataKeysListForLabelsControls.push(this.fb.control(labelDataKey, [labelDataKeyValidator])); + }); + } + this.flotSettingsFormGroup.setControl('dataKeysListForLabels', this.fb.array(dataKeysListForLabelsControls), {emitEvent: false}); + this.updateValidators(false); + } + + validate(c: FormControl) { + return (this.flotSettingsFormGroup.valid) ? null : { + flotSettings: { + valid: false, + }, + }; + } + + private updateModel() { + const value: TbFlotSettings = this.flotSettingsFormGroup.value; + this.modelValue = value; + this.propagateChange(this.modelValue); + } + + private updateValidators(emitEvent?: boolean): void { + const showTooltip: boolean = this.flotSettingsFormGroup.get('showTooltip').value; + const xaxisShowLabels: boolean = this.flotSettingsFormGroup.get('xaxis.showLabels').value; + const yaxisShowLabels: boolean = this.flotSettingsFormGroup.get('yaxis.showLabels').value; + + if (showTooltip) { + this.flotSettingsFormGroup.get('tooltipIndividual').enable({emitEvent}); + this.flotSettingsFormGroup.get('tooltipCumulative').enable({emitEvent}); + this.flotSettingsFormGroup.get('hideZeros').enable({emitEvent}); + this.flotSettingsFormGroup.get('tooltipValueFormatter').enable({emitEvent}); + } else { + this.flotSettingsFormGroup.get('tooltipIndividual').disable({emitEvent}); + this.flotSettingsFormGroup.get('tooltipCumulative').disable({emitEvent}); + this.flotSettingsFormGroup.get('hideZeros').disable({emitEvent}); + this.flotSettingsFormGroup.get('tooltipValueFormatter').disable({emitEvent}); + } + + if (xaxisShowLabels) { + this.flotSettingsFormGroup.get('xaxis.color').enable({emitEvent}); + } else { + this.flotSettingsFormGroup.get('xaxis.color').disable({emitEvent}); + } + + if (yaxisShowLabels) { + this.flotSettingsFormGroup.get('yaxis.color').enable({emitEvent}); + this.flotSettingsFormGroup.get('yaxis.tickSize').enable({emitEvent}); + this.flotSettingsFormGroup.get('yaxis.tickDecimals').enable({emitEvent}); + this.flotSettingsFormGroup.get('yaxis.ticksFormatter').enable({emitEvent}); + } else { + this.flotSettingsFormGroup.get('yaxis.color').disable({emitEvent}); + this.flotSettingsFormGroup.get('yaxis.tickSize').disable({emitEvent}); + this.flotSettingsFormGroup.get('yaxis.tickDecimals').disable({emitEvent}); + this.flotSettingsFormGroup.get('yaxis.ticksFormatter').disable({emitEvent}); + } + + this.flotSettingsFormGroup.get('tooltipIndividual').updateValueAndValidity({emitEvent: false}); + this.flotSettingsFormGroup.get('tooltipCumulative').updateValueAndValidity({emitEvent: false}); + this.flotSettingsFormGroup.get('hideZeros').updateValueAndValidity({emitEvent: false}); + this.flotSettingsFormGroup.get('tooltipValueFormatter').updateValueAndValidity({emitEvent: false}); + this.flotSettingsFormGroup.get('xaxis.color').updateValueAndValidity({emitEvent: false}); + this.flotSettingsFormGroup.get('yaxis.color').updateValueAndValidity({emitEvent: false}); + this.flotSettingsFormGroup.get('yaxis.tickSize').updateValueAndValidity({emitEvent: false}); + this.flotSettingsFormGroup.get('yaxis.tickDecimals').updateValueAndValidity({emitEvent: false}); + this.flotSettingsFormGroup.get('yaxis.ticksFormatter').updateValueAndValidity({emitEvent: false}); + + if (this.chartType === 'graph' || this.chartType === 'bar') { + const comparisonEnabled: boolean = this.flotSettingsFormGroup.get('comparisonEnabled').value; + const timeForComparison: ComparisonDuration = this.flotSettingsFormGroup.get('timeForComparison').value; + const customLegendEnabled: boolean = this.flotSettingsFormGroup.get('customLegendEnabled').value; + if (comparisonEnabled) { + this.flotSettingsFormGroup.get('timeForComparison').enable({emitEvent: false}); + if (timeForComparison === 'customInterval') { + this.flotSettingsFormGroup.get('comparisonCustomIntervalValue').enable({emitEvent}); + } else { + this.flotSettingsFormGroup.get('comparisonCustomIntervalValue').disable({emitEvent}); + } + } else { + this.flotSettingsFormGroup.get('timeForComparison').disable({emitEvent: false}); + this.flotSettingsFormGroup.get('comparisonCustomIntervalValue').disable({emitEvent}); + } + if (customLegendEnabled) { + this.flotSettingsFormGroup.get('dataKeysListForLabels').enable({emitEvent}); + } else { + this.flotSettingsFormGroup.get('dataKeysListForLabels').disable({emitEvent}); + } + + this.flotSettingsFormGroup.get('timeForComparison').updateValueAndValidity({emitEvent: false}); + this.flotSettingsFormGroup.get('comparisonCustomIntervalValue').updateValueAndValidity({emitEvent: false}); + this.flotSettingsFormGroup.get('dataKeysListForLabels').updateValueAndValidity({emitEvent: false}); + } + } + + dataKeysListForLabelsFormArray(): FormArray { + return this.flotSettingsFormGroup.get('dataKeysListForLabels') as FormArray; + } + + public trackByLabelDataKey(index: number, labelDataKeyControl: AbstractControl): any { + return labelDataKeyControl; + } + + public removeLabelDataKey(index: number) { + (this.flotSettingsFormGroup.get('dataKeysListForLabels') as FormArray).removeAt(index); + } + + public addLabelDataKey() { + const labelDataKey: LabelDataKey = { + name: null, + type: DataKeyType.attribute + }; + const dataKeysListForLabelsArray = this.flotSettingsFormGroup.get('dataKeysListForLabels') as FormArray; + const labelDataKeyControl = this.fb.control(labelDataKey, [labelDataKeyValidator]); + (labelDataKeyControl as any).new = true; + dataKeysListForLabelsArray.push(labelDataKeyControl); + this.flotSettingsFormGroup.updateValueAndValidity(); + } + + labelDataKeyDrop(event: CdkDragDrop) { + const dataKeysListForLabelsArray = this.flotSettingsFormGroup.get('dataKeysListForLabels') as FormArray; + const labelDataKey = dataKeysListForLabelsArray.at(event.previousIndex); + dataKeysListForLabelsArray.removeAt(event.previousIndex); + dataKeysListForLabelsArray.insert(event.currentIndex, labelDataKey); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.html new file mode 100644 index 0000000000..50dc16d6fb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.html @@ -0,0 +1,63 @@ + + + +
+ +
+ {{ labelDataKeyText() }} +
+
+ + +
+
+ +
+ +
+
+ + widgets.chart.key-name + + + {{ 'widgets.chart.key-name-required' | translate }} + + + + widgets.chart.key-type + + + {{ 'widgets.chart.key-type-attribute' | translate }} + + + {{ 'widgets.chart.key-type-timeseries' | translate }} + + + +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.scss new file mode 100644 index 0000000000..d82e4b34b9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.scss @@ -0,0 +1,40 @@ +/** + * 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 { + display: block; + .mat-expansion-panel { + box-shadow: none; + &.label-data-key { + border: 1px groove rgba(0, 0, 0, .25); + .mat-expansion-panel-header { + padding: 0 24px 0 8px; + &.mat-expanded { + height: 48px; + } + } + } + } +} + +:host ::ng-deep { + .mat-expansion-panel { + &.label-data-key { + .mat-expansion-panel-body { + padding: 0 8px 8px; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.ts new file mode 100644 index 0000000000..940c135c1e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.ts @@ -0,0 +1,132 @@ +/// +/// 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, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALUE_ACCESSOR, ValidationErrors, + Validators +} from '@angular/forms'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { TranslateService } from '@ngx-translate/core'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; + +export interface LabelDataKey { + name: string; + type: DataKeyType; +} + +export function labelDataKeyValidator(control: AbstractControl): ValidationErrors | null { + const labelDataKey: LabelDataKey = control.value; + if (!labelDataKey || !labelDataKey.name) { + return { + labelDataKey: true + }; + } + return null; +} + +@Component({ + selector: 'tb-label-data-key', + templateUrl: './label-data-key.component.html', + styleUrls: ['./label-data-key.component.scss', './../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => LabelDataKeyComponent), + multi: true + } + ] +}) +export class LabelDataKeyComponent extends PageComponent implements OnInit, ControlValueAccessor { + + @Input() + disabled: boolean; + + @Input() + expanded = false; + + @Output() + removeLabelDataKey = new EventEmitter(); + + private modelValue: LabelDataKey; + + private propagateChange = null; + + public labelDataKeyFormGroup: FormGroup; + + constructor(protected store: Store, + private translate: TranslateService, + private fb: FormBuilder) { + super(store); + } + + ngOnInit(): void { + this.labelDataKeyFormGroup = this.fb.group({ + name: [null, [Validators.required]], + type: [DataKeyType.attribute, [Validators.required]] + }); + this.labelDataKeyFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.labelDataKeyFormGroup.disable({emitEvent: false}); + } else { + this.labelDataKeyFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: LabelDataKey): void { + this.modelValue = value; + this.labelDataKeyFormGroup.patchValue( + value, {emitEvent: false} + ); + } + + labelDataKeyText(): string { + const name: string = this.labelDataKeyFormGroup.get('name').value || ''; + const type: DataKeyType = this.labelDataKeyFormGroup.get('type').value; + let typeStr: string; + if (type === DataKeyType.attribute) { + typeStr = this.translate.instant('widgets.chart.key-type-attribute'); + } else if (type === DataKeyType.timeseries) { + typeStr = this.translate.instant('widgets.chart.key-type-timeseries'); + } + return `${name} (${typeStr})`; + } + + private updateModel() { + const value: LabelDataKey = this.labelDataKeyFormGroup.value; + this.modelValue = value; + this.propagateChange(this.modelValue); + } +} 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 a90bc9edf4..8ee227e253 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 @@ -75,6 +75,14 @@ import { import { ValueSourceComponent } from '@home/components/widget/lib/settings/common/value-source.component'; import { FixedColorLevelComponent } from '@home/components/widget/lib/settings/gauge/fixed-color-level.component'; import { TickValueComponent } from '@home/components/widget/lib/settings/gauge/tick-value.component'; +import { FlotWidgetSettingsComponent } from '@home/components/widget/lib/settings/chart/flot-widget-settings.component'; +import { + FlotLineWidgetSettingsComponent +} from '@home/components/widget/lib/settings/chart/flot-line-widget-settings.component'; +import { LabelDataKeyComponent } from '@home/components/widget/lib/settings/chart/label-data-key.component'; +import { + FlotBarWidgetSettingsComponent +} from '@home/components/widget/lib/settings/chart/flot-bar-widget-settings.component'; @NgModule({ declarations: [ @@ -101,7 +109,11 @@ import { TickValueComponent } from '@home/components/widget/lib/settings/gauge/t DigitalGaugeWidgetSettingsComponent, ValueSourceComponent, FixedColorLevelComponent, - TickValueComponent + TickValueComponent, + FlotWidgetSettingsComponent, + LabelDataKeyComponent, + FlotLineWidgetSettingsComponent, + FlotBarWidgetSettingsComponent ], imports: [ CommonModule, @@ -132,7 +144,11 @@ import { TickValueComponent } from '@home/components/widget/lib/settings/gauge/t DigitalGaugeWidgetSettingsComponent, ValueSourceComponent, FixedColorLevelComponent, - TickValueComponent + TickValueComponent, + FlotWidgetSettingsComponent, + LabelDataKeyComponent, + FlotLineWidgetSettingsComponent, + FlotBarWidgetSettingsComponent ] }) export class WidgetSettingsModule { @@ -156,5 +172,7 @@ export const widgetSettingsComponentsMap: {[key: string]: Type { - this.onSettingsChanged(updated); + this.settingsForm().valueChanges.subscribe((updated: any) => { + this.onSettingsChanged(this.prepareOutputSettings(updated)); }); } @@ -713,7 +713,7 @@ export abstract class WidgetSettingsComponent extends PageComponent implements protected onSettingsChanged(updated: WidgetSettings) { this.settingsValue = updated; if (this.validateSettings()) { - this.settingsChangedEmitter.emit(this.prepareOutputSettings(updated)); + this.settingsChangedEmitter.emit(updated); } else { this.settingsChangedEmitter.emit(null); } @@ -723,7 +723,7 @@ export abstract class WidgetSettingsComponent extends PageComponent implements return settings; } - protected prepareOutputSettings(settings: WidgetSettings): WidgetSettings { + protected prepareOutputSettings(settings: any): WidgetSettings { return settings; } 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 c0e6c3b801..d4d47f3bfd 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3182,13 +3182,75 @@ "invalid-widget-type-file-error": "Unable to import widget type: Invalid widget type data structure." }, "widgets": { + "chart": { + "common-settings": "Common settings", + "enable-stacking-mode": "Enable stacking mode", + "line-shadow-size": "Line shadow size", + "display-smooth-lines": "Display smooth (curved) lines", + "default-bar-width": "Default bar width for non-aggregated data (milliseconds)", + "bar-alignment": "Bar alignment", + "bar-alignment-left": "Left", + "bar-alignment-right": "Right", + "bar-alignment-center": "Center", + "default-font-size": "Default font size", + "default-font-color": "Default font color", + "thresholds-line-width": "Default line width for all thresholds", + "tooltip-settings": "Tooltip settings", + "show-tooltip": "Show tooltip", + "hover-individual-points": "Hover individual points", + "show-cumulative-values": "Show cumulative values in stacking mode", + "hide-zero-false-values": "Hide zero/false values from tooltip", + "tooltip-value-format-function": "Tooltip value format function", + "grid-settings": "Grid settings", + "show-vertical-lines": "Show vertical lines", + "show-horizontal-lines": "Show horizontal lines", + "grid-outline-border-width": "Grid outline/border width (px)", + "primary-color": "Primary color", + "background-color": "Background color", + "ticks-color": "Ticks color", + "xaxis-settings": "X axis settings", + "axis-title": "Axis title", + "xaxis-tick-labels-settings": "X axis tick labels settings", + "show-tick-labels": "Show axis tick labels", + "yaxis-settings": "Y axis settings", + "min-scale-value": "Minimum value on the scale", + "max-scale-value": "Maximum value on the scale", + "yaxis-tick-labels-settings": "Y axis tick labels settings", + "tick-step-size": "Step size between ticks", + "number-of-decimals": "The number of decimals to display", + "ticks-formatter-function": "Ticks formatter function", + "comparison-settings": "Comparison settings", + "enable-comparison": "Enable comparison", + "time-for-comparison": "Comparison period", + "time-for-comparison-previous-interval": "Previous interval (default)", + "time-for-comparison-days": "Day ago", + "time-for-comparison-weeks": "Week ago", + "time-for-comparison-months": "Month ago", + "time-for-comparison-years": "Year ago", + "time-for-comparison-custom-interval": "Custom interval", + "custom-interval-value": "Custom interval value (ms)", + "comparison-x-axis-settings": "Comparison X axis settings", + "axis-position": "Axis position", + "axis-position-top": "Top (default)", + "axis-position-bottom": "Bottom", + "custom-legend-settings": "Custom legend settings", + "enable-custom-legend": "Enable custom legend (this will allow you to use attribute/timeseries values in key labels)", + "key-name": "Key name", + "key-name-required": "Key name is required", + "key-type": "Key type", + "key-type-attribute": "Attribute", + "key-type-timeseries": "Timeseries", + "label-keys-list": "Keys list to use in labels", + "no-label-keys": "No keys configured", + "add-label-key": "Add new key" + }, "dashboard-state": { - "dashboard-state-settings": "Dashboard state settings", - "dashboard-state": "Dashboard state id", - "autofill-state-layout": "Autofill state layout height by default", - "default-margin": "Default widgets margin", - "default-background-color": "Default background color", - "sync-parent-state-params": "Sync state params with parent dashboard" + "dashboard-state-settings": "Dashboard state settings", + "dashboard-state": "Dashboard state id", + "autofill-state-layout": "Autofill state layout height by default", + "default-margin": "Default widgets margin", + "default-background-color": "Default background color", + "sync-parent-state-params": "Sync state params with parent dashboard" }, "date-range-navigator": { "localizationMap": { @@ -3248,137 +3310,137 @@ } }, "entities-hierarchy": { - "hierarchy-data-settings": "Hierarchy data settings", - "relations-query-function": "Node relations query function", - "has-children-function": "Node has children function", - "node-state-settings": "Node state settings", - "node-opened-function": "Default node opened function", - "node-disabled-function": "Node disabled function", - "display-settings": "Display settings", - "node-icon-function": "Node icon function", - "node-text-function": "Node text function", - "sort-settings": "Sort settings", - "nodes-sort-function": "Nodes sort function" + "hierarchy-data-settings": "Hierarchy data settings", + "relations-query-function": "Node relations query function", + "has-children-function": "Node has children function", + "node-state-settings": "Node state settings", + "node-opened-function": "Default node opened function", + "node-disabled-function": "Node disabled function", + "display-settings": "Display settings", + "node-icon-function": "Node icon function", + "node-text-function": "Node text function", + "sort-settings": "Sort settings", + "nodes-sort-function": "Nodes sort function" }, "gauge": { - "default-color": "Default color", - "radial-gauge-settings": "Radial gauge settings", - "ticks-settings": "Ticks settings", - "min-value": "Minimum value", - "max-value": "Maximum value", - "start-ticks-angle": "Start ticks angle", - "ticks-angle": "Ticks angle", - "major-ticks-count": "Major ticks count", - "major-ticks-color": "Major ticks color", - "minor-ticks-count": "Minor ticks count", - "minor-ticks-color": "Minor ticks color", - "tick-numbers-font": "Tick numbers font", - "unit-title-settings": "Unit title settings", - "show-unit-title": "Show unit title", - "unit-title": "Unit title", - "title-font": "Title text font", - "units-settings": "Units settings", - "units-font": "Units text font", - "value-box-settings": "Value box settings", - "show-value-box": "Show value box", - "value-int": "Digits count for integer part of value", - "value-font": "Value text font", - "value-box-rect-stroke-color": "Value box rectangle stroke color", - "value-box-rect-stroke-color-end": "Value box rectangle stroke color - end gradient", - "value-box-background-color": "Value box background color", - "value-box-shadow-color": "Value box shadow color", - "plate-settings": "Plate settings", - "show-plate-border": "Show plate border", - "plate-color": "Plate color", - "needle-settings": "Needle settings", - "needle-circle-size": "Needle circle size", - "needle-color": "Needle color", - "needle-color-end": "Needle color - end gradient", - "needle-color-shadow-up": "Upper half of the needle shadow color", - "needle-color-shadow-down": "Drop shadow needle color", - "highlights-settings": "Highlights settings", - "highlights-width": "Highlights width", - "highlights": "Highlights", - "highlight-from": "From", - "highlight-to": "To", - "highlight-color": "Color", - "no-highlights": "No highlights configured", - "add-highlight": "Add highlight", - "animation-settings": "Animation settings", - "enable-animation": "Enable animation", - "animation-duration": "Animation duration", - "animation-rule": "Animation rule", - "animation-linear": "Linear", - "animation-quad": "Quad", - "animation-quint": "Quint", - "animation-cycle": "Cycle", - "animation-bounce": "Bounce", - "animation-elastic": "Elastic", - "animation-dequad": "Dequad", - "animation-dequint": "Dequint", - "animation-decycle": "Decycle", - "animation-debounce": "Debounce", - "animation-delastic": "Delastic", - "linear-gauge-settings": "Linear gauge settings", - "bar-stroke-width": "Bar stroke width", - "bar-stroke-color": "Bar stroke color", - "bar-background-color": "Gauge bar background color", - "bar-background-color-end": "Bar background color - end gradient", - "progress-bar-color": "Progress bar color", - "progress-bar-color-end": "Progress bar color - end gradient", - "major-ticks-names": "Major ticks names", - "show-stroke-ticks": "Show ticks stroke", - "major-ticks-font": "Major ticks font", - "border-color": "Border color", - "border-width": "Border width", - "needle-circle-color": "Needle circle color", - "animation-target": "Animation target", - "animation-target-needle": "Needle", - "animation-target-plate": "Plate", - "common-settings": "Common gauge settings", - "gauge-type": "Gauge type", - "gauge-type-arc": "Arc", - "gauge-type-donut": "Donut", - "gauge-type-horizontal-bar": "Horizontal bar", - "gauge-type-vertical-bar": "Vertical bar", - "donut-start-angle": "Angle to start from", - "bar-settings": "Gauge bar settings", - "relative-bar-width": "Relative bar width", - "neon-glow-brightness": "Neon glow effect brightness, (0-100), 0 - disable effect", - "stripes-thickness": "Thickness of the stripes, 0 - no stripes", - "rounded-line-cap": "Display rounded line cap", - "bar-color-settings": "Bar color settings", - "use-precise-level-color-values": "Use precise color levels", - "bar-colors": "Bar colors, from lower to upper", - "color": "Color", - "no-bar-colors": "No bar colors configured", - "add-bar-color": "Add bar color", - "from": "From", - "to": "To", - "fixed-level-colors": "Bar colors using boundary values", - "gauge-title-settings": "Gauge title settings", - "show-gauge-title": "Show gauge title", - "gauge-title": "Gauge title", - "gauge-title-font": "Gauge title font", - "unit-title-and-timestamp-settings": "Unit title and timestamp settings", - "show-timestamp": "Show value timestamp", - "timestamp-format": "Timestamp format", - "label-font": "Font of label showing under value", - "value-settings": "Value settings", - "show-value": "Show value text", - "min-max-settings": "Minimum/maximum labels settings", - "show-min-max": "Show min and max values", - "min-max-font": "Font of minimum and maximum labels", - "show-ticks": "Show ticks", - "tick-width": "Tick width", - "tick-color": "Tick color", - "tick-values": "Tick values", - "no-tick-values": "No tick values configured", - "add-tick-value": "Add tick value" + "default-color": "Default color", + "radial-gauge-settings": "Radial gauge settings", + "ticks-settings": "Ticks settings", + "min-value": "Minimum value", + "max-value": "Maximum value", + "start-ticks-angle": "Start ticks angle", + "ticks-angle": "Ticks angle", + "major-ticks-count": "Major ticks count", + "major-ticks-color": "Major ticks color", + "minor-ticks-count": "Minor ticks count", + "minor-ticks-color": "Minor ticks color", + "tick-numbers-font": "Tick numbers font", + "unit-title-settings": "Unit title settings", + "show-unit-title": "Show unit title", + "unit-title": "Unit title", + "title-font": "Title text font", + "units-settings": "Units settings", + "units-font": "Units text font", + "value-box-settings": "Value box settings", + "show-value-box": "Show value box", + "value-int": "Digits count for integer part of value", + "value-font": "Value text font", + "value-box-rect-stroke-color": "Value box rectangle stroke color", + "value-box-rect-stroke-color-end": "Value box rectangle stroke color - end gradient", + "value-box-background-color": "Value box background color", + "value-box-shadow-color": "Value box shadow color", + "plate-settings": "Plate settings", + "show-plate-border": "Show plate border", + "plate-color": "Plate color", + "needle-settings": "Needle settings", + "needle-circle-size": "Needle circle size", + "needle-color": "Needle color", + "needle-color-end": "Needle color - end gradient", + "needle-color-shadow-up": "Upper half of the needle shadow color", + "needle-color-shadow-down": "Drop shadow needle color", + "highlights-settings": "Highlights settings", + "highlights-width": "Highlights width", + "highlights": "Highlights", + "highlight-from": "From", + "highlight-to": "To", + "highlight-color": "Color", + "no-highlights": "No highlights configured", + "add-highlight": "Add highlight", + "animation-settings": "Animation settings", + "enable-animation": "Enable animation", + "animation-duration": "Animation duration", + "animation-rule": "Animation rule", + "animation-linear": "Linear", + "animation-quad": "Quad", + "animation-quint": "Quint", + "animation-cycle": "Cycle", + "animation-bounce": "Bounce", + "animation-elastic": "Elastic", + "animation-dequad": "Dequad", + "animation-dequint": "Dequint", + "animation-decycle": "Decycle", + "animation-debounce": "Debounce", + "animation-delastic": "Delastic", + "linear-gauge-settings": "Linear gauge settings", + "bar-stroke-width": "Bar stroke width", + "bar-stroke-color": "Bar stroke color", + "bar-background-color": "Gauge bar background color", + "bar-background-color-end": "Bar background color - end gradient", + "progress-bar-color": "Progress bar color", + "progress-bar-color-end": "Progress bar color - end gradient", + "major-ticks-names": "Major ticks names", + "show-stroke-ticks": "Show ticks stroke", + "major-ticks-font": "Major ticks font", + "border-color": "Border color", + "border-width": "Border width", + "needle-circle-color": "Needle circle color", + "animation-target": "Animation target", + "animation-target-needle": "Needle", + "animation-target-plate": "Plate", + "common-settings": "Common gauge settings", + "gauge-type": "Gauge type", + "gauge-type-arc": "Arc", + "gauge-type-donut": "Donut", + "gauge-type-horizontal-bar": "Horizontal bar", + "gauge-type-vertical-bar": "Vertical bar", + "donut-start-angle": "Angle to start from", + "bar-settings": "Gauge bar settings", + "relative-bar-width": "Relative bar width", + "neon-glow-brightness": "Neon glow effect brightness, (0-100), 0 - disable effect", + "stripes-thickness": "Thickness of the stripes, 0 - no stripes", + "rounded-line-cap": "Display rounded line cap", + "bar-color-settings": "Bar color settings", + "use-precise-level-color-values": "Use precise color levels", + "bar-colors": "Bar colors, from lower to upper", + "color": "Color", + "no-bar-colors": "No bar colors configured", + "add-bar-color": "Add bar color", + "from": "From", + "to": "To", + "fixed-level-colors": "Bar colors using boundary values", + "gauge-title-settings": "Gauge title settings", + "show-gauge-title": "Show gauge title", + "gauge-title": "Gauge title", + "gauge-title-font": "Gauge title font", + "unit-title-and-timestamp-settings": "Unit title and timestamp settings", + "show-timestamp": "Show value timestamp", + "timestamp-format": "Timestamp format", + "label-font": "Font of label showing under value", + "value-settings": "Value settings", + "show-value": "Show value text", + "min-max-settings": "Minimum/maximum labels settings", + "show-min-max": "Show min and max values", + "min-max-font": "Font of minimum and maximum labels", + "show-ticks": "Show ticks", + "tick-width": "Tick width", + "tick-color": "Tick color", + "tick-values": "Tick values", + "no-tick-values": "No tick values configured", + "add-tick-value": "Add tick value" }, "html-card": { - "html": "HTML", - "css": "CSS" + "html": "HTML", + "css": "CSS" }, "input-widgets": { "attribute-not-allowed": "Attribute parameter cannot be used in this widget", @@ -3434,18 +3496,18 @@ "qr-code-text-function": "QR code text function" }, "label-widget": { - "label-pattern": "Pattern", - "label-pattern-hint": "Hint: for ex. 'Text ${keyName} units.' or ${#<key index>} units'", - "label-pattern-required": "Pattern is required", - "label-position": "Position (Percentage relative to background)", - "x-pos": "X", - "y-pos": "Y", - "background-color": "Background color", - "font-settings": "Font settings", - "background-image": "Background image", - "labels": "Labels", - "no-labels": "No labels configured", - "add-label": "Add label" + "label-pattern": "Pattern", + "label-pattern-hint": "Hint: for ex. 'Text ${keyName} units.' or ${#<key index>} units'", + "label-pattern-required": "Pattern is required", + "label-position": "Position (Percentage relative to background)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "Background color", + "font-settings": "Font settings", + "background-image": "Background image", + "labels": "Labels", + "no-labels": "No labels configured", + "add-label": "Add label" }, "persistent-table": { "rpc-id": "RPC ID", @@ -3526,87 +3588,87 @@ } }, "markdown": { - "use-markdown-text-function": "Use markdown/HTML value function", - "markdown-text-function": "Markdown/HTML value function", - "markdown-text-pattern": "Markdown/HTML pattern (markdown or HTML with variables, for ex. '${entityName} or ${keyName} - some text.')", - "markdown-css": "Markdown/HTML CSS" + "use-markdown-text-function": "Use markdown/HTML value function", + "markdown-text-function": "Markdown/HTML value function", + "markdown-text-pattern": "Markdown/HTML pattern (markdown or HTML with variables, for ex. '${entityName} or ${keyName} - some text.')", + "markdown-css": "Markdown/HTML CSS" }, "simple-card": { - "label-position": "Label position", - "label-position-left": "Left", - "label-position-top": "Top" + "label-position": "Label position", + "label-position-left": "Left", + "label-position-top": "Top" }, "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", - "entities-table-title": "Entities table title", - "enable-select-column-display": "Enable select columns to display", - "display-entity-name": "Display entity name column", - "entity-name-column-title": "Entity name column title", - "display-entity-label": "Display entity label column", - "entity-label-column-title": "Entity label column title", - "display-entity-type": "Display entity type column", - "default-sort-order": "Default sort order", - "column-width": "Column width (px or %)", - "default-column-visibility": "Default column visibility", - "column-visibility-visible": "Visible", - "column-visibility-hidden": "Hidden", - "column-selection-to-display": "Column selection in 'Columns to Display'", - "column-selection-to-display-enabled": "Enabled", - "column-selection-to-display-disabled": "Disabled", - "alarms-table-title": "Alarms table title", - "enable-alarms-selection": "Enable alarms selection", - "enable-alarms-search": "Enable alarms search", - "enable-alarm-filter": "Enable alarm filter", - "display-alarm-details": "Display alarm details", - "allow-alarms-ack": "Allow alarms acknowledgment", - "allow-alarms-clear": "Allow alarms clear" + "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", + "entities-table-title": "Entities table title", + "enable-select-column-display": "Enable select columns to display", + "display-entity-name": "Display entity name column", + "entity-name-column-title": "Entity name column title", + "display-entity-label": "Display entity label column", + "entity-label-column-title": "Entity label column title", + "display-entity-type": "Display entity type column", + "default-sort-order": "Default sort order", + "column-width": "Column width (px or %)", + "default-column-visibility": "Default column visibility", + "column-visibility-visible": "Visible", + "column-visibility-hidden": "Hidden", + "column-selection-to-display": "Column selection in 'Columns to Display'", + "column-selection-to-display-enabled": "Enabled", + "column-selection-to-display-disabled": "Disabled", + "alarms-table-title": "Alarms table title", + "enable-alarms-selection": "Enable alarms selection", + "enable-alarms-search": "Enable alarms search", + "enable-alarm-filter": "Enable alarm filter", + "display-alarm-details": "Display alarm details", + "allow-alarms-ack": "Allow alarms acknowledgment", + "allow-alarms-clear": "Allow alarms clear" }, "value-source": { - "value-source": "Value source", - "predefined-value": "Predefined value", - "entity-attribute": "Value taken from entity attribute", - "value": "Value", - "source-entity-alias": "Source entity alias", - "source-entity-attribute": "Source entity attribute" + "value-source": "Value source", + "predefined-value": "Predefined value", + "entity-attribute": "Value taken from entity attribute", + "value": "Value", + "source-entity-alias": "Source entity alias", + "source-entity-attribute": "Source entity attribute" }, "widget-font": { - "font-family": "Font family", - "size": "Size", - "relative-font-size": "Relative font size (percents)", - "font-style": "Style", - "font-style-normal": "Normal", - "font-style-italic": "Italic", - "font-style-oblique": "Oblique", - "font-weight": "Weight", - "font-weight-normal": "Normal", - "font-weight-bold": "Bold", - "font-weight-bolder": "Bolder", - "font-weight-lighter": "Lighter", - "color": "Color", - "shadow-color": "Shadow color" + "font-family": "Font family", + "size": "Size", + "relative-font-size": "Relative font size (percents)", + "font-style": "Style", + "font-style-normal": "Normal", + "font-style-italic": "Italic", + "font-style-oblique": "Oblique", + "font-weight": "Weight", + "font-weight-normal": "Normal", + "font-weight-bold": "Bold", + "font-weight-bolder": "Bolder", + "font-weight-lighter": "Lighter", + "color": "Color", + "shadow-color": "Shadow color" } }, "icon": {