Browse Source

UI: Add settings forms for flot chart widgets.

pull/6545/head
Igor Kulikov 5 years ago
parent
commit
83da5520fc
  1. 9
      application/src/main/data/json/system/widget_bundles/charts.json
  2. 3
      ui-ngx/.editorconfig
  3. 4
      ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts
  4. 23
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-widget-settings.component.html
  5. 55
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-widget-settings.component.ts
  6. 23
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-line-widget-settings.component.html
  7. 55
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-line-widget-settings.component.ts
  8. 327
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.html
  9. 411
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts
  10. 63
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.html
  11. 40
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.scss
  12. 132
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.ts
  13. 24
      ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts
  14. 8
      ui-ngx/src/app/shared/models/widget.models.ts
  15. 498
      ui-ngx/src/assets/locale/locale.constant-en_US.json

9
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\":{}}"
}
}

3
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

4
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 {

23
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-widget-settings.component.html

@ -0,0 +1,23 @@
<!--
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.
-->
<section [formGroup]="flotBarWidgetSettingsForm" fxLayout="column">
<tb-flot-widget-settings formControlName="flotSettings"
[chartType]="'bar'">
</tb-flot-widget-settings>
</section>

55
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<AppState>,
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;
}
}

23
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-line-widget-settings.component.html

@ -0,0 +1,23 @@
<!--
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.
-->
<section [formGroup]="flotLineWidgetSettingsForm" fxLayout="column">
<tb-flot-widget-settings formControlName="flotSettings"
[chartType]="'graph'">
</tb-flot-widget-settings>
</section>

55
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<AppState>,
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;
}
}

327
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.html

@ -0,0 +1,327 @@
<!--
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.
-->
<section class="tb-widget-settings" [formGroup]="flotSettingsFormGroup" fxLayout="column">
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.chart.common-settings</legend>
<mat-checkbox formControlName="stack" style="margin-bottom: 16px;">
{{ 'widgets.chart.enable-stacking-mode' | translate }}
</mat-checkbox>
<section *ngIf="chartType === 'graph'" fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.line-shadow-size</mat-label>
<input matInput type="number" min="0" formControlName="shadowSize">
</mat-form-field>
<mat-checkbox fxFlex formControlName="smoothLines" style="margin-bottom: 16px;">
{{ 'widgets.chart.display-smooth-lines' | translate }}
</mat-checkbox>
</section>
<section *ngIf="chartType === 'bar'" fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.default-bar-width</mat-label>
<input matInput type="number" min="0" formControlName="defaultBarWidth">
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.bar-alignment</mat-label>
<mat-select formControlName="barAlignment">
<mat-option [value]="'left'">
{{ 'widgets.chart.bar-alignment-left' | translate }}
</mat-option>
<mat-option [value]="'right'">
{{ 'widgets.chart.bar-alignment-right' | translate }}
</mat-option>
<mat-option [value]="'center'">
{{ 'widgets.chart.bar-alignment-center' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</section>
<section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.default-font-size</mat-label>
<input matInput type="number" min="0" formControlName="fontSize">
</mat-form-field>
<tb-color-input fxFlex
formControlName="fontColor"
label="{{ 'widgets.chart.default-font-color' | translate }}" openOnInput colorClearButton>
</tb-color-input>
</section>
<mat-form-field *ngIf="chartType === 'graph' || chartType === 'bar'" fxFlex class="mat-block">
<mat-label translate>widgets.chart.thresholds-line-width</mat-label>
<input matInput type="number" min="0" formControlName="thresholdsLineWidth">
</mat-form-field>
</fieldset>
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.chart.tooltip-settings</legend>
<mat-expansion-panel class="tb-settings" [expanded]="flotSettingsFormGroup.get('showTooltip').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="showTooltip" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.chart.show-tooltip' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<section fxLayout="column">
<mat-slide-toggle formControlName="tooltipIndividual" class="slide-block">
{{ 'widgets.chart.hover-individual-points' | translate }}
</mat-slide-toggle>
<mat-slide-toggle formControlName="tooltipCumulative" class="slide-block">
{{ 'widgets.chart.show-cumulative-values' | translate }}
</mat-slide-toggle>
<mat-slide-toggle formControlName="hideZeros" class="slide-block">
{{ 'widgets.chart.hide-zero-false-values' | translate }}
</mat-slide-toggle>
<tb-js-func
formControlName="tooltipValueFormatter"
[globalVariables]="functionScopeVariables"
[functionArgs]="['value']"
functionTitle="{{ 'widgets.chart.tooltip-value-format-function' | translate }}"
helpId="widget/lib/flot/tooltip_value_format_fn">
</tb-js-func>
</section>
</ng-template>
</mat-expansion-panel>
</fieldset>
<fieldset class="fields-group" formGroupName="grid">
<legend class="group-title" translate>widgets.chart.grid-settings</legend>
<mat-checkbox formControlName="verticalLines" style="margin-bottom: 16px;">
{{ 'widgets.chart.show-vertical-lines' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="horizontalLines" style="margin-bottom: 16px;">
{{ 'widgets.chart.show-horizontal-lines' | translate }}
</mat-checkbox>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.grid-outline-border-width</mat-label>
<input matInput type="number" min="0" formControlName="outlineWidth">
</mat-form-field>
<tb-color-input fxFlex
formControlName="color"
label="{{ 'widgets.chart.primary-color' | translate }}" openOnInput colorClearButton>
</tb-color-input>
<tb-color-input fxFlex
formControlName="backgroundColor"
label="{{ 'widgets.chart.background-color' | translate }}" openOnInput colorClearButton>
</tb-color-input>
<tb-color-input fxFlex
formControlName="tickColor"
label="{{ 'widgets.chart.ticks-color' | translate }}" openOnInput colorClearButton>
</tb-color-input>
</fieldset>
<fieldset class="fields-group" formGroupName="xaxis">
<legend class="group-title" translate>widgets.chart.xaxis-settings</legend>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.axis-title</mat-label>
<input matInput formControlName="title">
</mat-form-field>
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.chart.xaxis-tick-labels-settings</legend>
<mat-expansion-panel class="tb-settings" [expanded]="flotSettingsFormGroup.get('xaxis.showLabels').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="showLabels" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.chart.show-tick-labels' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-color-input fxFlex
formControlName="color"
label="{{ 'widgets.chart.ticks-color' | translate }}" openOnInput colorClearButton>
</tb-color-input>
</ng-template>
</mat-expansion-panel>
</fieldset>
</fieldset>
<fieldset class="fields-group" formGroupName="yaxis">
<legend class="group-title" translate>widgets.chart.yaxis-settings</legend>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.axis-title</mat-label>
<input matInput formControlName="title">
</mat-form-field>
<section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.min-scale-value</mat-label>
<input matInput type="number" formControlName="min">
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.max-scale-value</mat-label>
<input matInput type="number" formControlName="max">
</mat-form-field>
</section>
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.chart.yaxis-tick-labels-settings</legend>
<mat-expansion-panel class="tb-settings" [expanded]="flotSettingsFormGroup.get('yaxis.showLabels').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="showLabels" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.chart.show-tick-labels' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-color-input fxFlex
formControlName="color"
label="{{ 'widgets.chart.ticks-color' | translate }}" openOnInput colorClearButton>
</tb-color-input>
<section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.tick-step-size</mat-label>
<input matInput type="number" formControlName="tickSize">
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.number-of-decimals</mat-label>
<input matInput type="number" min="0" formControlName="tickDecimals">
</mat-form-field>
</section>
<tb-js-func
formControlName="ticksFormatter"
[globalVariables]="functionScopeVariables"
[functionArgs]="['value']"
functionTitle="{{ 'widgets.chart.ticks-formatter-function' | translate }}"
helpId="widget/lib/flot/ticks_formatter_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
</fieldset>
</fieldset>
<fieldset *ngIf="chartType === 'graph' || chartType === 'bar'" class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.chart.comparison-settings</legend>
<mat-expansion-panel class="tb-settings" [expanded]="flotSettingsFormGroup.get('comparisonEnabled').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="comparisonEnabled" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.chart.enable-comparison' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<section fxLayout="column">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.time-for-comparison</mat-label>
<mat-select formControlName="timeForComparison">
<mat-option [value]="'previousInterval'">
{{ 'widgets.chart.time-for-comparison-previous-interval' | translate }}
</mat-option>
<mat-option [value]="'days'">
{{ 'widgets.chart.time-for-comparison-days' | translate }}
</mat-option>
<mat-option [value]="'weeks'">
{{ 'widgets.chart.time-for-comparison-weeks' | translate }}
</mat-option>
<mat-option [value]="'months'">
{{ 'widgets.chart.time-for-comparison-months' | translate }}
</mat-option>
<mat-option [value]="'years'">
{{ 'widgets.chart.time-for-comparison-years' | translate }}
</mat-option>
<mat-option [value]="'customInterval'">
{{ 'widgets.chart.time-for-comparison-custom-interval' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field [fxShow]="flotSettingsFormGroup.get('timeForComparison').value === 'customInterval'" fxFlex class="mat-block">
<mat-label translate>widgets.chart.custom-interval-value</mat-label>
<input matInput type="number" min="0" formControlName="comparisonCustomIntervalValue">
</mat-form-field>
<fieldset class="fields-group" formGroupName="xaxisSecond">
<legend class="group-title" translate>widgets.chart.comparison-x-axis-settings</legend>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.axis-title</mat-label>
<input matInput formControlName="title">
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.axis-position</mat-label>
<mat-select formControlName="axisPosition">
<mat-option [value]="'top'">
{{ 'widgets.chart.axis-position-top' | translate }}
</mat-option>
<mat-option [value]="'bottom'">
{{ 'widgets.chart.axis-position-bottom' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-slide-toggle formControlName="showLabels" class="slide-block">
{{ 'widgets.chart.show-tick-labels' | translate }}
</mat-slide-toggle>
</fieldset>
</section>
</ng-template>
</mat-expansion-panel>
</fieldset>
<fieldset *ngIf="chartType === 'graph' || chartType === 'bar'" class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.chart.custom-legend-settings</legend>
<mat-expansion-panel class="tb-settings" [expanded]="flotSettingsFormGroup.get('customLegendEnabled').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="customLegendEnabled" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.chart.enable-custom-legend' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<fieldset [fxShow]="flotSettingsFormGroup.get('customLegendEnabled').value" class="fields-group">
<legend class="group-title" translate>widgets.chart.label-keys-list</legend>
<div fxLayout="column">
<div class="tb-control-list tb-drop-list" cdkDropList cdkDropListOrientation="vertical"
(cdkDropListDropped)="labelDataKeyDrop($event)">
<div cdkDrag class="tb-draggable" *ngFor="let labelDataKeyControl of dataKeysListForLabelsFormArray().controls; trackBy: trackByLabelDataKey;
let $index = index; last as isLast;"
fxLayout="column" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}">
<tb-label-data-key [formControl]="labelDataKeyControl"
[expanded]="labelDataKeyControl.new"
(removeLabelDataKey)="removeLabelDataKey($index)">
</tb-label-data-key>
</div>
</div>
<div *ngIf="!dataKeysListForLabelsFormArray().controls.length">
<span translate fxLayoutAlign="center center"
class="tb-prompt">widgets.chart.no-label-keys</span>
</div>
<div style="padding-top: 16px;">
<button mat-raised-button color="primary"
type="button"
(click)="addLabelDataKey()">
<span translate>widgets.chart.add-label-key</span>
</button>
</div>
</div>
</fieldset>
</ng-template>
</mat-expansion-panel>
</fieldset>
</section>

411
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<TbFlotSettings> {
const settings: Partial<TbFlotSettings> = {
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<AppState>,
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<AbstractControl> = [];
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<string[]>) {
const dataKeysListForLabelsArray = this.flotSettingsFormGroup.get('dataKeysListForLabels') as FormArray;
const labelDataKey = dataKeysListForLabelsArray.at(event.previousIndex);
dataKeysListForLabelsArray.removeAt(event.previousIndex);
dataKeysListForLabelsArray.insert(event.currentIndex, labelDataKey);
}
}

63
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.html

@ -0,0 +1,63 @@
<!--
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.
-->
<mat-expansion-panel class="label-data-key" fxFlex [formGroup]="labelDataKeyFormGroup" [(expanded)]="expanded">
<mat-expansion-panel-header>
<div fxFlex fxLayout="row" fxLayoutAlign="start center">
<mat-panel-title>
<div fxLayout="row" fxFlex fxLayoutAlign="start center">
{{ labelDataKeyText() }}
</div>
</mat-panel-title>
<span fxFlex></span>
<button *ngIf="!disabled" mat-icon-button style="min-width: 40px;"
type="button"
(click)="removeLabelDataKey.emit()"
matTooltip="{{ 'action.remove' | translate }}"
matTooltipPosition="above">
<mat-icon>delete</mat-icon>
</button>
</div>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div fxLayout="column" fxLayoutGap="0.5em">
<mat-divider></mat-divider>
<section class="tb-widget-settings" fxLayout="column">
<section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.key-name</mat-label>
<input required matInput formControlName="name">
<mat-error *ngIf="labelDataKeyFormGroup.get('name').hasError('required')">
{{ 'widgets.chart.key-name-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.key-type</mat-label>
<mat-select formControlName="type">
<mat-option [value]="'attribute'">
{{ 'widgets.chart.key-type-attribute' | translate }}
</mat-option>
<mat-option [value]="'timeseries'">
{{ 'widgets.chart.key-type-timeseries' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</section>
</section>
</div>
</ng-template>
</mat-expansion-panel>

40
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;
}
}
}
}

132
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<AppState>,
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);
}
}

24
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<IWidgetSettingsCo
'tb-analogue-radial-gauge-widget-settings': AnalogueRadialGaugeWidgetSettingsComponent,
'tb-analogue-linear-gauge-widget-settings': AnalogueLinearGaugeWidgetSettingsComponent,
'tb-analogue-compass-widget-settings': AnalogueCompassWidgetSettingsComponent,
'tb-digital-gauge-widget-settings': DigitalGaugeWidgetSettingsComponent
'tb-digital-gauge-widget-settings': DigitalGaugeWidgetSettingsComponent,
'tb-flot-line-widget-settings': FlotLineWidgetSettingsComponent,
'tb-flot-bar-widget-settings': FlotBarWidgetSettingsComponent
};

8
ui-ngx/src/app/shared/models/widget.models.ts

@ -693,8 +693,8 @@ export abstract class WidgetSettingsComponent extends PageComponent implements
this.updateValidators(true, trigger);
});
}
this.settingsForm().valueChanges.subscribe((updated: WidgetSettings) => {
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;
}

498
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 <code><span style=\"color: #000;\">$&#123;</span>keyName<span style=\"color: #000;\">&#125;</span></code> units.' or <code><span style=\"color: #000;\">$&#123;</span>#&lt;key index&gt;<span style=\"color: #000;\">&#125;</span></code> 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 <code><span style=\"color: #000;\">$&#123;</span>keyName<span style=\"color: #000;\">&#125;</span></code> units.' or <code><span style=\"color: #000;\">$&#123;</span>#&lt;key index&gt;<span style=\"color: #000;\">&#125;</span></code> 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": {

Loading…
Cancel
Save