67 changed files with 3738 additions and 1247 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,29 @@ |
|||||
|
{ |
||||
|
"fqn": "charts.bars", |
||||
|
"name": "Bars", |
||||
|
"deprecated": true, |
||||
|
"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAA8FBMVEUhlvNMr1Bqamp5eXl7e3t8fHx9fX1+fn5/f3+AgICCgoKDg4OEhISGhoaHh4eKioqMjIyNjY2Ojo6QkJCRkZGSkpKWlpaXl5ebm5udnZ2enp6goKChoaGkpKSnp6epqamsrKyurq6xsbGzs7O1tbW2tra3t7e4uLi7u7u9vb3BwcHCwsLDw8PGxsbKysrNzc3Ozs7R0dHS0tLT09PZ2dna2trc3Nzd3d3e3t7g4ODh4eHj4+Pk5OTm5ubn5+fo6Ojp6enu7u7w8PDz8/P0Qzb09PT29vb39/f5+fn6+vr7+/v8/Pz9/f3+/v7/wQf///+dc+aLAAAAAWJLR0RPbmZBSQAAAcFJREFUeNrt3ds2AgEYhuHsaSOZbAvZi0r2YYjCJOW7/7txZhkcDNbM6h/vdwfPmlX/ybtmEorJErGCeJLadz3rkKPpZamaLTp925DHdFvSpKelU9uQ/cLKhtcdk7YqtiHruevtojch7ZZtQ0o1dcdfRqXNqm1IbVU3OaVamm/YhvQW5zIXOknnC5JUt7qEpE5fUv/5HVePy2UHAgQIECBAgAABAgQIECBxgrwGHBAgQIAAAQIECBAgQIAAAQIECJC/QRIBN0iQ+66voDMLuRp2fQWdVUhvNun6CjqrkJ0Dx/UVdEYhzXzfcX0FnVFIrlSZ2mx/LOiMQuqHh6k972NBZ/fv13G/KeiCQkIu4358EL8UdBafSGwuOxAgQIAAAQIECJDYQB4CDggQIECAAAECBAgQIECAAAECBAgQIECA/BrSufn0DjqjkEZmLXkWaUEXEmThXMeFSAu68H4j5b1IC7rQILfZTqQFXViQ1nRTL1EWdCFBnmYuJUVZ0IUEWR1xHKcXZUEXFDLwBR2XHQgQIECAAAEC5H9ChgIOCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgxiBmv+L6Bl9pkxYph15gAAAAAElFTkSuQmCC", |
||||
|
"description": "Displays latest values of the attributes or time-series data for multiple entities as separate bars.", |
||||
|
"descriptor": { |
||||
|
"type": "latest", |
||||
|
"sizeX": 7, |
||||
|
"sizeY": 5, |
||||
|
"resources": [ |
||||
|
{ |
||||
|
"url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js" |
||||
|
} |
||||
|
], |
||||
|
"templateHtml": "<canvas id=\"barChart\"></canvas>\n", |
||||
|
"templateCss": "", |
||||
|
"controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var barData = {\n labels: [],\n datasets: []\n };\n \n for (var i = 0; i < self.ctx.datasources.length; i++) {\n var datasource = self.ctx.datasources[i];\n for (var d = 0; d < datasource.dataKeys.length; d++) {\n var dataKey = datasource.dataKeys[d];\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n var dataset = {\n label: dataKey.label + units,\n data: [0],\n backgroundColor: [dataKey.color],\n borderColor: [dataKey.color],\n borderWidth: 1\n }\n barData.datasets.push(dataset);\n }\n }\n\n var ctx = $('#barChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'bar',\n data: barData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scales: {\n yAxes: [{\n ticks: {\n beginAtZero:true\n }\n }]\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var c = 0;\n for (var i = 0; i < self.ctx.chart.data.datasets.length; i++) {\n var dataset = self.ctx.chart.data.datasets[i];\n var cellData = self.ctx.data[i]; \n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n dataset.data[0] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n", |
||||
|
"settingsSchema": "", |
||||
|
"dataKeySettingsSchema": "{}\n", |
||||
|
"settingsDirective": "tb-chart-widget-settings", |
||||
|
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Bars\"}" |
||||
|
}, |
||||
|
"externalId": null, |
||||
|
"tags": [ |
||||
|
"bar", |
||||
|
"bar chart" |
||||
|
] |
||||
|
} |
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,261 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2024 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. |
||||
|
|
||||
|
--> |
||||
|
<ng-container [formGroup]="barChartWidgetConfigForm"> |
||||
|
<tb-timewindow-config-panel *ngIf="displayTimewindowConfig" |
||||
|
[onlyHistoryTimewindow]="onlyHistoryTimewindow()" |
||||
|
formControlName="timewindowConfig"> |
||||
|
</tb-timewindow-config-panel> |
||||
|
<tb-datasources |
||||
|
[configMode]="basicMode" |
||||
|
hideDatasourceLabel |
||||
|
hideDataKeys |
||||
|
forceSingleDatasource |
||||
|
formControlName="datasources"> |
||||
|
</tb-datasources> |
||||
|
<tb-data-keys-panel |
||||
|
panelTitle="{{ 'widgets.chart.series' | translate }}" |
||||
|
addKeyTitle="{{ 'widgets.chart.add-series' | translate }}" |
||||
|
keySettingsTitle="{{ 'widgets.chart.series-settings' | translate }}" |
||||
|
removeKeyTitle="{{ 'widgets.chart.remove-series' | translate }}" |
||||
|
noKeysText="{{ 'widgets.chart.no-series' | translate }}" |
||||
|
requiredKeysText="{{ 'widgets.chart.no-series-error' | translate }}" |
||||
|
hideUnits |
||||
|
hideDecimals |
||||
|
hideDataKeyUnits |
||||
|
hideDataKeyDecimals |
||||
|
[datasourceType]="datasource?.type" |
||||
|
[deviceId]="datasource?.deviceId" |
||||
|
[entityAliasId]="datasource?.entityAliasId" |
||||
|
formControlName="series"> |
||||
|
</tb-data-keys-panel> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widget-config.card-appearance</div> |
||||
|
<div class="tb-form-row column-xs"> |
||||
|
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitle"> |
||||
|
{{ 'widget-config.card-title' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<mat-form-field fxFlex appearance="outline" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
<tb-font-settings formControlName="titleFont" |
||||
|
clearButton |
||||
|
[previewText]="barChartWidgetConfigForm.get('title').value" |
||||
|
[initialPreviewStyle]="widgetConfig.config.titleStyle"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="titleColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="showTitleIcon"> |
||||
|
{{ 'widget-config.card-icon' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<tb-material-icon-select asBoxInput |
||||
|
iconClearButton |
||||
|
[color]="barChartWidgetConfigForm.get('iconColor').value" |
||||
|
formControlName="titleIcon"> |
||||
|
</tb-material-icon-select> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="iconColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="sortSeries"> |
||||
|
{{ 'widgets.latest-chart.sort-series' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div translate>widget-config.units-short</div> |
||||
|
<tb-unit-input |
||||
|
formControlName="units"> |
||||
|
</tb-unit-input> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div translate>widget-config.decimals-short</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="decimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-appearance</div> |
||||
|
<tb-chart-bar-settings |
||||
|
formControlName="barSettings" |
||||
|
[series]="false"> |
||||
|
</tb-chart-bar-settings> |
||||
|
</div> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-axis</div> |
||||
|
<div class="tb-form-row space-between column-xs"> |
||||
|
<div translate>widgets.chart.chart-axis.scale</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-min</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="axisMin" |
||||
|
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-max</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="axisMax" |
||||
|
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
<tb-font-settings formControlName="axisTickLabelFont" |
||||
|
clearButton |
||||
|
disabledLineHeight |
||||
|
forceSizeUnit="px" |
||||
|
previewText="100"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="axisTickLabelColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-panel tb-slide-toggle"> |
||||
|
<mat-expansion-panel class="tb-settings" [expanded]="barChartWidgetConfigForm.get('showLegend').value" [disabled]="!barChartWidgetConfigForm.get('showLegend').value"> |
||||
|
<mat-expansion-panel-header fxLayout="row wrap"> |
||||
|
<mat-panel-title> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="showLegend" (click)="$event.stopPropagation()" |
||||
|
fxLayoutAlign="center"> |
||||
|
{{ 'widget-config.legend' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<ng-template matExpansionPanelContent> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'legend.position' | translate }}</div> |
||||
|
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic"> |
||||
|
<mat-select formControlName="legendPosition"> |
||||
|
<mat-option *ngFor="let pos of legendPositions" [value]="pos"> |
||||
|
{{ legendPositionTranslationMap.get(pos) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'legend.label' | translate }}</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<tb-font-settings formControlName="legendLabelFont" |
||||
|
previewText="Wind"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="legendLabelColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'legend.value' | translate }}</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<tb-font-settings formControlName="legendValueFont" |
||||
|
[previewText]="valuePreviewFn"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="legendValueColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</mat-expansion-panel> |
||||
|
</div> |
||||
|
<div class="tb-form-panel tb-slide-toggle"> |
||||
|
<mat-expansion-panel class="tb-settings" [expanded]="barChartWidgetConfigForm.get('showTooltip').value" [disabled]="!barChartWidgetConfigForm.get('showTooltip').value"> |
||||
|
<mat-expansion-panel-header fxLayout="row wrap"> |
||||
|
<mat-panel-title> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="showTooltip" (click)="$event.stopPropagation()" |
||||
|
fxLayoutAlign="center"> |
||||
|
{{ 'widget-config.tooltip' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<ng-template matExpansionPanelContent> |
||||
|
<div class="tb-form-row space-between column-xs"> |
||||
|
<div>{{ 'tooltip.value' | translate }}</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic"> |
||||
|
<mat-select formControlName="tooltipValueType"> |
||||
|
<mat-option *ngFor="let type of latestChartTooltipValueTypes" [value]="type"> |
||||
|
{{ latestChartTooltipValueTypeTranslationMap.get(type) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="tooltipValueDecimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
<div matSuffix fxHide.lt-md translate>widget-config.decimals-suffix</div> |
||||
|
</mat-form-field> |
||||
|
<tb-font-settings formControlName="tooltipValueFont" |
||||
|
[previewText]="tooltipValuePreviewFn"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="tooltipValueColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'tooltip.background-color' | translate }}</div> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="tooltipBackgroundColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'tooltip.background-blur' | translate }}</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="tooltipBackgroundBlur" type="number" min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
<div matSuffix>px</div> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</mat-expansion-panel> |
||||
|
</div> |
||||
|
<tb-chart-animation-settings |
||||
|
formControlName="animation"> |
||||
|
</tb-chart-animation-settings> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widget-config.card-appearance</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'widgets.background.background' | translate }}</div> |
||||
|
<tb-background-settings formControlName="background"> |
||||
|
</tb-background-settings> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between column-lt-md"> |
||||
|
<div translate>widget-config.show-card-buttons</div> |
||||
|
<mat-chip-listbox multiple formControlName="cardButtons"> |
||||
|
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option> |
||||
|
</mat-chip-listbox> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'widget-config.card-border-radius' | translate }}</div> |
||||
|
<mat-form-field appearance="outline" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="borderRadius" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
<tb-widget-actions-panel |
||||
|
formControlName="actions"> |
||||
|
</tb-widget-actions-panel> |
||||
|
</ng-container> |
||||
@ -0,0 +1,316 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 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 { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { AppState } from '@core/core.state'; |
||||
|
import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; |
||||
|
import { WidgetConfigComponentData } from '@home/models/widget-component.models'; |
||||
|
import { |
||||
|
DataKey, |
||||
|
Datasource, |
||||
|
datasourcesHasAggregation, |
||||
|
datasourcesHasOnlyComparisonAggregation, |
||||
|
legendPositions, |
||||
|
legendPositionTranslationMap, |
||||
|
WidgetConfig |
||||
|
} from '@shared/models/widget.models'; |
||||
|
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; |
||||
|
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
||||
|
import { formatValue, isUndefined, mergeDeep } from '@core/utils'; |
||||
|
import { |
||||
|
getTimewindowConfig, |
||||
|
setTimewindowConfig |
||||
|
} from '@home/components/widget/config/timewindow-config-panel.component'; |
||||
|
import { |
||||
|
LatestChartTooltipValueType, |
||||
|
latestChartTooltipValueTypes, |
||||
|
latestChartTooltipValueTypeTranslations |
||||
|
} from '@home/components/widget/lib/chart/latest-chart.models'; |
||||
|
import { |
||||
|
barChartWidgetDefaultSettings, |
||||
|
BarChartWidgetSettings |
||||
|
} from '@home/components/widget/lib/chart/bar-chart-widget.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-bar-chart-basic-config', |
||||
|
templateUrl: './bar-chart-basic-config.component.html', |
||||
|
styleUrls: ['../basic-config.scss'] |
||||
|
}) |
||||
|
export class BarChartBasicConfigComponent extends BasicWidgetConfigComponent { |
||||
|
|
||||
|
public get datasource(): Datasource { |
||||
|
const datasources: Datasource[] = this.barChartWidgetConfigForm.get('datasources').value; |
||||
|
if (datasources && datasources.length) { |
||||
|
return datasources[0]; |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public get displayTimewindowConfig(): boolean { |
||||
|
const datasources = this.barChartWidgetConfigForm.get('datasources').value; |
||||
|
return datasourcesHasAggregation(datasources); |
||||
|
} |
||||
|
|
||||
|
public onlyHistoryTimewindow(): boolean { |
||||
|
const datasources = this.barChartWidgetConfigForm.get('datasources').value; |
||||
|
return datasourcesHasOnlyComparisonAggregation(datasources); |
||||
|
} |
||||
|
|
||||
|
legendPositions = legendPositions; |
||||
|
|
||||
|
legendPositionTranslationMap = legendPositionTranslationMap; |
||||
|
|
||||
|
latestChartTooltipValueTypes = latestChartTooltipValueTypes; |
||||
|
|
||||
|
latestChartTooltipValueTypeTranslationMap = latestChartTooltipValueTypeTranslations; |
||||
|
|
||||
|
barChartWidgetConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
valuePreviewFn = this._valuePreviewFn.bind(this); |
||||
|
|
||||
|
tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
protected widgetConfigComponent: WidgetConfigComponent, |
||||
|
private fb: UntypedFormBuilder) { |
||||
|
super(store, widgetConfigComponent); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.barChartWidgetConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { |
||||
|
return [{ name: 'windPower', label: 'Wind', type: DataKeyType.timeseries, units: '', decimals: 0, color: '#08872B' }, |
||||
|
{ name: 'solarPower', label: 'Solar', type: DataKeyType.timeseries, units: '', decimals: 0, color: '#FF4D5A' }, |
||||
|
{ name: 'hydroelectricPower', label: 'Hydroelectric', type: DataKeyType.timeseries, units: '', decimals: 0, color: '#FFDE30' }]; |
||||
|
} |
||||
|
|
||||
|
protected onConfigSet(configData: WidgetConfigComponentData) { |
||||
|
const settings: BarChartWidgetSettings = mergeDeep<BarChartWidgetSettings>({} as BarChartWidgetSettings, |
||||
|
barChartWidgetDefaultSettings, configData.config.settings as BarChartWidgetSettings); |
||||
|
this.barChartWidgetConfigForm = this.fb.group({ |
||||
|
timewindowConfig: [getTimewindowConfig(configData.config), []], |
||||
|
datasources: [configData.config.datasources, []], |
||||
|
|
||||
|
series: [this.getSeries(configData.config.datasources), []], |
||||
|
|
||||
|
showTitle: [configData.config.showTitle, []], |
||||
|
title: [configData.config.title, []], |
||||
|
titleFont: [configData.config.titleFont, []], |
||||
|
titleColor: [configData.config.titleColor, []], |
||||
|
showTitleIcon: [configData.config.showTitleIcon, []], |
||||
|
titleIcon: [configData.config.titleIcon, []], |
||||
|
iconColor: [configData.config.iconColor, []], |
||||
|
|
||||
|
sortSeries: [settings.sortSeries, []], |
||||
|
|
||||
|
units: [configData.config.units, []], |
||||
|
decimals: [configData.config.decimals, []], |
||||
|
|
||||
|
barSettings: [settings.barSettings, []], |
||||
|
|
||||
|
axisMin: [settings.axisMin, []], |
||||
|
axisMax: [settings.axisMax, []], |
||||
|
axisTickLabelFont: [settings.axisTickLabelFont, []], |
||||
|
axisTickLabelColor: [settings.axisTickLabelColor, []], |
||||
|
|
||||
|
animation: [settings.animation, []], |
||||
|
|
||||
|
showLegend: [settings.showLegend, []], |
||||
|
legendPosition: [settings.legendPosition, []], |
||||
|
legendLabelFont: [settings.legendLabelFont, []], |
||||
|
legendLabelColor: [settings.legendLabelColor, []], |
||||
|
legendValueFont: [settings.legendValueFont, []], |
||||
|
legendValueColor: [settings.legendValueColor, []], |
||||
|
|
||||
|
showTooltip: [settings.showTooltip, []], |
||||
|
tooltipValueType: [settings.tooltipValueType, []], |
||||
|
tooltipValueDecimals: [settings.tooltipValueDecimals, []], |
||||
|
tooltipValueFont: [settings.tooltipValueFont, []], |
||||
|
tooltipValueColor: [settings.tooltipValueColor, []], |
||||
|
tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], |
||||
|
tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], |
||||
|
|
||||
|
background: [settings.background, []], |
||||
|
|
||||
|
cardButtons: [this.getCardButtons(configData.config), []], |
||||
|
borderRadius: [configData.config.borderRadius, []], |
||||
|
|
||||
|
actions: [configData.config.actions || {}, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputConfig(config: any): WidgetConfigComponentData { |
||||
|
setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); |
||||
|
this.widgetConfig.config.datasources = config.datasources; |
||||
|
this.setSeries(config.series, this.widgetConfig.config.datasources); |
||||
|
|
||||
|
this.widgetConfig.config.showTitle = config.showTitle; |
||||
|
this.widgetConfig.config.title = config.title; |
||||
|
this.widgetConfig.config.titleFont = config.titleFont; |
||||
|
this.widgetConfig.config.titleColor = config.titleColor; |
||||
|
|
||||
|
this.widgetConfig.config.showTitleIcon = config.showTitleIcon; |
||||
|
this.widgetConfig.config.titleIcon = config.titleIcon; |
||||
|
this.widgetConfig.config.iconColor = config.iconColor; |
||||
|
|
||||
|
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; |
||||
|
|
||||
|
this.widgetConfig.config.settings.sortSeries = config.sortSeries; |
||||
|
|
||||
|
this.widgetConfig.config.units = config.units; |
||||
|
this.widgetConfig.config.decimals = config.decimals; |
||||
|
|
||||
|
this.widgetConfig.config.settings.barSettings = config.barSettings; |
||||
|
|
||||
|
this.widgetConfig.config.settings.axisMin = config.axisMin; |
||||
|
this.widgetConfig.config.settings.axisMax = config.axisMax; |
||||
|
this.widgetConfig.config.settings.axisTickLabelFont = config.axisTickLabelFont; |
||||
|
this.widgetConfig.config.settings.axisTickLabelColor = config.axisTickLabelColor; |
||||
|
|
||||
|
this.widgetConfig.config.settings.animation = config.animation; |
||||
|
|
||||
|
this.widgetConfig.config.settings.showLegend = config.showLegend; |
||||
|
this.widgetConfig.config.settings.legendPosition = config.legendPosition; |
||||
|
this.widgetConfig.config.settings.legendLabelFont = config.legendLabelFont; |
||||
|
this.widgetConfig.config.settings.legendLabelColor = config.legendLabelColor; |
||||
|
this.widgetConfig.config.settings.legendValueFont = config.legendValueFont; |
||||
|
this.widgetConfig.config.settings.legendValueColor = config.legendValueColor; |
||||
|
|
||||
|
this.widgetConfig.config.settings.showTooltip = config.showTooltip; |
||||
|
this.widgetConfig.config.settings.tooltipValueType = config.tooltipValueType; |
||||
|
this.widgetConfig.config.settings.tooltipValueDecimals = config.tooltipValueDecimals; |
||||
|
this.widgetConfig.config.settings.tooltipValueFont = config.tooltipValueFont; |
||||
|
this.widgetConfig.config.settings.tooltipValueColor = config.tooltipValueColor; |
||||
|
this.widgetConfig.config.settings.tooltipBackgroundColor = config.tooltipBackgroundColor; |
||||
|
this.widgetConfig.config.settings.tooltipBackgroundBlur = config.tooltipBackgroundBlur; |
||||
|
|
||||
|
this.widgetConfig.config.settings.background = config.background; |
||||
|
|
||||
|
this.setCardButtons(config.cardButtons, this.widgetConfig.config); |
||||
|
this.widgetConfig.config.borderRadius = config.borderRadius; |
||||
|
|
||||
|
this.widgetConfig.config.actions = config.actions; |
||||
|
|
||||
|
return this.widgetConfig; |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['showTitle', 'showTitleIcon', 'showLegend', 'showTooltip']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean, trigger?: string) { |
||||
|
const showTitle: boolean = this.barChartWidgetConfigForm.get('showTitle').value; |
||||
|
const showTitleIcon: boolean = this.barChartWidgetConfigForm.get('showTitleIcon').value; |
||||
|
const showLegend: boolean = this.barChartWidgetConfigForm.get('showLegend').value; |
||||
|
const showTooltip: boolean = this.barChartWidgetConfigForm.get('showTooltip').value; |
||||
|
|
||||
|
if (showTitle) { |
||||
|
this.barChartWidgetConfigForm.get('title').enable(); |
||||
|
this.barChartWidgetConfigForm.get('titleFont').enable(); |
||||
|
this.barChartWidgetConfigForm.get('titleColor').enable(); |
||||
|
this.barChartWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false}); |
||||
|
if (showTitleIcon) { |
||||
|
this.barChartWidgetConfigForm.get('titleIcon').enable(); |
||||
|
this.barChartWidgetConfigForm.get('iconColor').enable(); |
||||
|
} else { |
||||
|
this.barChartWidgetConfigForm.get('titleIcon').disable(); |
||||
|
this.barChartWidgetConfigForm.get('iconColor').disable(); |
||||
|
} |
||||
|
} else { |
||||
|
this.barChartWidgetConfigForm.get('title').disable(); |
||||
|
this.barChartWidgetConfigForm.get('titleFont').disable(); |
||||
|
this.barChartWidgetConfigForm.get('titleColor').disable(); |
||||
|
this.barChartWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false}); |
||||
|
this.barChartWidgetConfigForm.get('titleIcon').disable(); |
||||
|
this.barChartWidgetConfigForm.get('iconColor').disable(); |
||||
|
} |
||||
|
if (showLegend) { |
||||
|
this.barChartWidgetConfigForm.get('legendPosition').enable(); |
||||
|
this.barChartWidgetConfigForm.get('legendLabelFont').enable(); |
||||
|
this.barChartWidgetConfigForm.get('legendLabelColor').enable(); |
||||
|
this.barChartWidgetConfigForm.get('legendValueFont').enable(); |
||||
|
this.barChartWidgetConfigForm.get('legendValueColor').enable(); |
||||
|
} else { |
||||
|
this.barChartWidgetConfigForm.get('legendPosition').disable(); |
||||
|
this.barChartWidgetConfigForm.get('legendLabelFont').disable(); |
||||
|
this.barChartWidgetConfigForm.get('legendLabelColor').disable(); |
||||
|
this.barChartWidgetConfigForm.get('legendValueFont').disable(); |
||||
|
this.barChartWidgetConfigForm.get('legendValueColor').disable(); |
||||
|
} |
||||
|
if (showTooltip) { |
||||
|
this.barChartWidgetConfigForm.get('tooltipValueType').enable(); |
||||
|
this.barChartWidgetConfigForm.get('tooltipValueDecimals').enable(); |
||||
|
this.barChartWidgetConfigForm.get('tooltipValueFont').enable(); |
||||
|
this.barChartWidgetConfigForm.get('tooltipValueColor').enable(); |
||||
|
this.barChartWidgetConfigForm.get('tooltipBackgroundColor').enable(); |
||||
|
this.barChartWidgetConfigForm.get('tooltipBackgroundBlur').enable(); |
||||
|
} else { |
||||
|
this.barChartWidgetConfigForm.get('tooltipValueType').disable(); |
||||
|
this.barChartWidgetConfigForm.get('tooltipValueDecimals').disable(); |
||||
|
this.barChartWidgetConfigForm.get('tooltipValueFont').disable(); |
||||
|
this.barChartWidgetConfigForm.get('tooltipValueColor').disable(); |
||||
|
this.barChartWidgetConfigForm.get('tooltipBackgroundColor').disable(); |
||||
|
this.barChartWidgetConfigForm.get('tooltipBackgroundBlur').disable(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private getSeries(datasources?: Datasource[]): DataKey[] { |
||||
|
if (datasources && datasources.length) { |
||||
|
return datasources[0].dataKeys || []; |
||||
|
} |
||||
|
return []; |
||||
|
} |
||||
|
|
||||
|
private setSeries(series: DataKey[], datasources?: Datasource[]) { |
||||
|
if (datasources && datasources.length) { |
||||
|
datasources[0].dataKeys = series; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private getCardButtons(config: WidgetConfig): string[] { |
||||
|
const buttons: string[] = []; |
||||
|
if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { |
||||
|
buttons.push('fullscreen'); |
||||
|
} |
||||
|
return buttons; |
||||
|
} |
||||
|
|
||||
|
private setCardButtons(buttons: string[], config: WidgetConfig) { |
||||
|
config.enableFullscreen = buttons.includes('fullscreen'); |
||||
|
} |
||||
|
|
||||
|
private _valuePreviewFn(): string { |
||||
|
const units: string = this.barChartWidgetConfigForm.get('units').value; |
||||
|
const decimals: number = this.barChartWidgetConfigForm.get('decimals').value; |
||||
|
return formatValue(110, decimals, units, false); |
||||
|
} |
||||
|
|
||||
|
private _tooltipValuePreviewFn(): string { |
||||
|
const tooltipValueType: LatestChartTooltipValueType = this.barChartWidgetConfigForm.get('tooltipValueType').value; |
||||
|
const decimals: number = this.barChartWidgetConfigForm.get('tooltipValueDecimals').value; |
||||
|
if (tooltipValueType === LatestChartTooltipValueType.percentage) { |
||||
|
return formatValue(35, decimals, '%', false); |
||||
|
} else { |
||||
|
const units: string = this.barChartWidgetConfigForm.get('units').value; |
||||
|
return formatValue(110, decimals, units, false); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,268 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2024 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. |
||||
|
|
||||
|
--> |
||||
|
<ng-container [formGroup]="polarAreaChartWidgetConfigForm"> |
||||
|
<tb-timewindow-config-panel *ngIf="displayTimewindowConfig" |
||||
|
[onlyHistoryTimewindow]="onlyHistoryTimewindow()" |
||||
|
formControlName="timewindowConfig"> |
||||
|
</tb-timewindow-config-panel> |
||||
|
<tb-datasources |
||||
|
[configMode]="basicMode" |
||||
|
hideDatasourceLabel |
||||
|
hideDataKeys |
||||
|
forceSingleDatasource |
||||
|
formControlName="datasources"> |
||||
|
</tb-datasources> |
||||
|
<tb-data-keys-panel |
||||
|
panelTitle="{{ 'widgets.chart.series' | translate }}" |
||||
|
addKeyTitle="{{ 'widgets.chart.add-series' | translate }}" |
||||
|
keySettingsTitle="{{ 'widgets.chart.series-settings' | translate }}" |
||||
|
removeKeyTitle="{{ 'widgets.chart.remove-series' | translate }}" |
||||
|
noKeysText="{{ 'widgets.chart.no-series' | translate }}" |
||||
|
requiredKeysText="{{ 'widgets.chart.no-series-error' | translate }}" |
||||
|
hideUnits |
||||
|
hideDecimals |
||||
|
hideDataKeyUnits |
||||
|
hideDataKeyDecimals |
||||
|
[datasourceType]="datasource?.type" |
||||
|
[deviceId]="datasource?.deviceId" |
||||
|
[entityAliasId]="datasource?.entityAliasId" |
||||
|
formControlName="series"> |
||||
|
</tb-data-keys-panel> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widget-config.card-appearance</div> |
||||
|
<div class="tb-form-row column-xs"> |
||||
|
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitle"> |
||||
|
{{ 'widget-config.card-title' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<mat-form-field fxFlex appearance="outline" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
<tb-font-settings formControlName="titleFont" |
||||
|
clearButton |
||||
|
[previewText]="polarAreaChartWidgetConfigForm.get('title').value" |
||||
|
[initialPreviewStyle]="widgetConfig.config.titleStyle"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="titleColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="showTitleIcon"> |
||||
|
{{ 'widget-config.card-icon' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<tb-material-icon-select asBoxInput |
||||
|
iconClearButton |
||||
|
[color]="polarAreaChartWidgetConfigForm.get('iconColor').value" |
||||
|
formControlName="titleIcon"> |
||||
|
</tb-material-icon-select> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="iconColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="sortSeries"> |
||||
|
{{ 'widgets.latest-chart.sort-series' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div translate>widget-config.units-short</div> |
||||
|
<tb-unit-input |
||||
|
formControlName="units"> |
||||
|
</tb-unit-input> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div translate>widget-config.decimals-short</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="decimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-appearance</div> |
||||
|
<tb-chart-bar-settings |
||||
|
formControlName="barSettings" |
||||
|
pieLabelPosition |
||||
|
[series]="false"> |
||||
|
</tb-chart-bar-settings> |
||||
|
</div> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widgets.polar-area-chart.polar-axis</div> |
||||
|
<div class="tb-form-row space-between column-xs"> |
||||
|
<div translate>widgets.chart.chart-axis.scale</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-min</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="axisMin" |
||||
|
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-max</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="axisMax" |
||||
|
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
<tb-font-settings formControlName="axisTickLabelFont" |
||||
|
clearButton |
||||
|
disabledLineHeight |
||||
|
forceSizeUnit="px" |
||||
|
previewText="100"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="axisTickLabelColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div translate>widgets.polar-area-chart.start-angle</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="angleAxisStartAngle" type="number" min="0" max="360" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-panel tb-slide-toggle"> |
||||
|
<mat-expansion-panel class="tb-settings" [expanded]="polarAreaChartWidgetConfigForm.get('showLegend').value" [disabled]="!polarAreaChartWidgetConfigForm.get('showLegend').value"> |
||||
|
<mat-expansion-panel-header fxLayout="row wrap"> |
||||
|
<mat-panel-title> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="showLegend" (click)="$event.stopPropagation()" |
||||
|
fxLayoutAlign="center"> |
||||
|
{{ 'widget-config.legend' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<ng-template matExpansionPanelContent> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'legend.position' | translate }}</div> |
||||
|
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic"> |
||||
|
<mat-select formControlName="legendPosition"> |
||||
|
<mat-option *ngFor="let pos of legendPositions" [value]="pos"> |
||||
|
{{ legendPositionTranslationMap.get(pos) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'legend.label' | translate }}</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<tb-font-settings formControlName="legendLabelFont" |
||||
|
previewText="Wind"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="legendLabelColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'legend.value' | translate }}</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<tb-font-settings formControlName="legendValueFont" |
||||
|
[previewText]="valuePreviewFn"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="legendValueColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</mat-expansion-panel> |
||||
|
</div> |
||||
|
<div class="tb-form-panel tb-slide-toggle"> |
||||
|
<mat-expansion-panel class="tb-settings" [expanded]="polarAreaChartWidgetConfigForm.get('showTooltip').value" [disabled]="!polarAreaChartWidgetConfigForm.get('showTooltip').value"> |
||||
|
<mat-expansion-panel-header fxLayout="row wrap"> |
||||
|
<mat-panel-title> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="showTooltip" (click)="$event.stopPropagation()" |
||||
|
fxLayoutAlign="center"> |
||||
|
{{ 'widget-config.tooltip' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<ng-template matExpansionPanelContent> |
||||
|
<div class="tb-form-row space-between column-xs"> |
||||
|
<div>{{ 'tooltip.value' | translate }}</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic"> |
||||
|
<mat-select formControlName="tooltipValueType"> |
||||
|
<mat-option *ngFor="let type of latestChartTooltipValueTypes" [value]="type"> |
||||
|
{{ latestChartTooltipValueTypeTranslationMap.get(type) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="tooltipValueDecimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
<div matSuffix fxHide.lt-md translate>widget-config.decimals-suffix</div> |
||||
|
</mat-form-field> |
||||
|
<tb-font-settings formControlName="tooltipValueFont" |
||||
|
[previewText]="tooltipValuePreviewFn"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="tooltipValueColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'tooltip.background-color' | translate }}</div> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="tooltipBackgroundColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'tooltip.background-blur' | translate }}</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="tooltipBackgroundBlur" type="number" min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
<div matSuffix>px</div> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</mat-expansion-panel> |
||||
|
</div> |
||||
|
<tb-chart-animation-settings |
||||
|
formControlName="animation"> |
||||
|
</tb-chart-animation-settings> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widget-config.card-appearance</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'widgets.background.background' | translate }}</div> |
||||
|
<tb-background-settings formControlName="background"> |
||||
|
</tb-background-settings> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between column-lt-md"> |
||||
|
<div translate>widget-config.show-card-buttons</div> |
||||
|
<mat-chip-listbox multiple formControlName="cardButtons"> |
||||
|
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option> |
||||
|
</mat-chip-listbox> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'widget-config.card-border-radius' | translate }}</div> |
||||
|
<mat-form-field appearance="outline" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="borderRadius" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
<tb-widget-actions-panel |
||||
|
formControlName="actions"> |
||||
|
</tb-widget-actions-panel> |
||||
|
</ng-container> |
||||
@ -0,0 +1,318 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 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 { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { AppState } from '@core/core.state'; |
||||
|
import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; |
||||
|
import { WidgetConfigComponentData } from '@home/models/widget-component.models'; |
||||
|
import { |
||||
|
DataKey, |
||||
|
Datasource, |
||||
|
datasourcesHasAggregation, |
||||
|
datasourcesHasOnlyComparisonAggregation, |
||||
|
legendPositions, |
||||
|
legendPositionTranslationMap, |
||||
|
WidgetConfig |
||||
|
} from '@shared/models/widget.models'; |
||||
|
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; |
||||
|
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
||||
|
import { formatValue, isUndefined, mergeDeep } from '@core/utils'; |
||||
|
import { |
||||
|
getTimewindowConfig, |
||||
|
setTimewindowConfig |
||||
|
} from '@home/components/widget/config/timewindow-config-panel.component'; |
||||
|
import { |
||||
|
LatestChartTooltipValueType, |
||||
|
latestChartTooltipValueTypes, |
||||
|
latestChartTooltipValueTypeTranslations |
||||
|
} from '@home/components/widget/lib/chart/latest-chart.models'; |
||||
|
import { |
||||
|
polarAreaChartWidgetDefaultSettings, |
||||
|
PolarAreaChartWidgetSettings |
||||
|
} from '@home/components/widget/lib/chart/polar-area-widget.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-polar-area-chart-basic-config', |
||||
|
templateUrl: './polar-area-chart-basic-config.component.html', |
||||
|
styleUrls: ['../basic-config.scss'] |
||||
|
}) |
||||
|
export class PolarAreaChartBasicConfigComponent extends BasicWidgetConfigComponent { |
||||
|
|
||||
|
public get datasource(): Datasource { |
||||
|
const datasources: Datasource[] = this.polarAreaChartWidgetConfigForm.get('datasources').value; |
||||
|
if (datasources && datasources.length) { |
||||
|
return datasources[0]; |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public get displayTimewindowConfig(): boolean { |
||||
|
const datasources = this.polarAreaChartWidgetConfigForm.get('datasources').value; |
||||
|
return datasourcesHasAggregation(datasources); |
||||
|
} |
||||
|
|
||||
|
public onlyHistoryTimewindow(): boolean { |
||||
|
const datasources = this.polarAreaChartWidgetConfigForm.get('datasources').value; |
||||
|
return datasourcesHasOnlyComparisonAggregation(datasources); |
||||
|
} |
||||
|
|
||||
|
legendPositions = legendPositions; |
||||
|
|
||||
|
legendPositionTranslationMap = legendPositionTranslationMap; |
||||
|
|
||||
|
latestChartTooltipValueTypes = latestChartTooltipValueTypes; |
||||
|
|
||||
|
latestChartTooltipValueTypeTranslationMap = latestChartTooltipValueTypeTranslations; |
||||
|
|
||||
|
polarAreaChartWidgetConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
valuePreviewFn = this._valuePreviewFn.bind(this); |
||||
|
|
||||
|
tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
protected widgetConfigComponent: WidgetConfigComponent, |
||||
|
private fb: UntypedFormBuilder) { |
||||
|
super(store, widgetConfigComponent); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.polarAreaChartWidgetConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { |
||||
|
return [{ name: 'windPower', label: 'Wind', type: DataKeyType.timeseries, units: '', decimals: 0, color: '#08872B' }, |
||||
|
{ name: 'solarPower', label: 'Solar', type: DataKeyType.timeseries, units: '', decimals: 0, color: '#FF4D5A' }, |
||||
|
{ name: 'hydroelectricPower', label: 'Hydroelectric', type: DataKeyType.timeseries, units: '', decimals: 0, color: '#FFDE30' }]; |
||||
|
} |
||||
|
|
||||
|
protected onConfigSet(configData: WidgetConfigComponentData) { |
||||
|
const settings: PolarAreaChartWidgetSettings = mergeDeep<PolarAreaChartWidgetSettings>({} as PolarAreaChartWidgetSettings, |
||||
|
polarAreaChartWidgetDefaultSettings, configData.config.settings as PolarAreaChartWidgetSettings); |
||||
|
this.polarAreaChartWidgetConfigForm = this.fb.group({ |
||||
|
timewindowConfig: [getTimewindowConfig(configData.config), []], |
||||
|
datasources: [configData.config.datasources, []], |
||||
|
|
||||
|
series: [this.getSeries(configData.config.datasources), []], |
||||
|
|
||||
|
showTitle: [configData.config.showTitle, []], |
||||
|
title: [configData.config.title, []], |
||||
|
titleFont: [configData.config.titleFont, []], |
||||
|
titleColor: [configData.config.titleColor, []], |
||||
|
showTitleIcon: [configData.config.showTitleIcon, []], |
||||
|
titleIcon: [configData.config.titleIcon, []], |
||||
|
iconColor: [configData.config.iconColor, []], |
||||
|
|
||||
|
sortSeries: [settings.sortSeries, []], |
||||
|
|
||||
|
units: [configData.config.units, []], |
||||
|
decimals: [configData.config.decimals, []], |
||||
|
|
||||
|
barSettings: [settings.barSettings, []], |
||||
|
|
||||
|
axisMin: [settings.axisMin, []], |
||||
|
axisMax: [settings.axisMax, []], |
||||
|
axisTickLabelFont: [settings.axisTickLabelFont, []], |
||||
|
axisTickLabelColor: [settings.axisTickLabelColor, []], |
||||
|
angleAxisStartAngle: [settings.angleAxisStartAngle, [Validators.min(0), Validators.max(360)]], |
||||
|
|
||||
|
animation: [settings.animation, []], |
||||
|
|
||||
|
showLegend: [settings.showLegend, []], |
||||
|
legendPosition: [settings.legendPosition, []], |
||||
|
legendLabelFont: [settings.legendLabelFont, []], |
||||
|
legendLabelColor: [settings.legendLabelColor, []], |
||||
|
legendValueFont: [settings.legendValueFont, []], |
||||
|
legendValueColor: [settings.legendValueColor, []], |
||||
|
|
||||
|
showTooltip: [settings.showTooltip, []], |
||||
|
tooltipValueType: [settings.tooltipValueType, []], |
||||
|
tooltipValueDecimals: [settings.tooltipValueDecimals, []], |
||||
|
tooltipValueFont: [settings.tooltipValueFont, []], |
||||
|
tooltipValueColor: [settings.tooltipValueColor, []], |
||||
|
tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], |
||||
|
tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], |
||||
|
|
||||
|
background: [settings.background, []], |
||||
|
|
||||
|
cardButtons: [this.getCardButtons(configData.config), []], |
||||
|
borderRadius: [configData.config.borderRadius, []], |
||||
|
|
||||
|
actions: [configData.config.actions || {}, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputConfig(config: any): WidgetConfigComponentData { |
||||
|
setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); |
||||
|
this.widgetConfig.config.datasources = config.datasources; |
||||
|
this.setSeries(config.series, this.widgetConfig.config.datasources); |
||||
|
|
||||
|
this.widgetConfig.config.showTitle = config.showTitle; |
||||
|
this.widgetConfig.config.title = config.title; |
||||
|
this.widgetConfig.config.titleFont = config.titleFont; |
||||
|
this.widgetConfig.config.titleColor = config.titleColor; |
||||
|
|
||||
|
this.widgetConfig.config.showTitleIcon = config.showTitleIcon; |
||||
|
this.widgetConfig.config.titleIcon = config.titleIcon; |
||||
|
this.widgetConfig.config.iconColor = config.iconColor; |
||||
|
|
||||
|
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; |
||||
|
|
||||
|
this.widgetConfig.config.settings.sortSeries = config.sortSeries; |
||||
|
|
||||
|
this.widgetConfig.config.units = config.units; |
||||
|
this.widgetConfig.config.decimals = config.decimals; |
||||
|
|
||||
|
this.widgetConfig.config.settings.barSettings = config.barSettings; |
||||
|
|
||||
|
this.widgetConfig.config.settings.axisMin = config.axisMin; |
||||
|
this.widgetConfig.config.settings.axisMax = config.axisMax; |
||||
|
this.widgetConfig.config.settings.axisTickLabelFont = config.axisTickLabelFont; |
||||
|
this.widgetConfig.config.settings.axisTickLabelColor = config.axisTickLabelColor; |
||||
|
this.widgetConfig.config.settings.angleAxisStartAngle = config.angleAxisStartAngle; |
||||
|
|
||||
|
this.widgetConfig.config.settings.animation = config.animation; |
||||
|
|
||||
|
this.widgetConfig.config.settings.showLegend = config.showLegend; |
||||
|
this.widgetConfig.config.settings.legendPosition = config.legendPosition; |
||||
|
this.widgetConfig.config.settings.legendLabelFont = config.legendLabelFont; |
||||
|
this.widgetConfig.config.settings.legendLabelColor = config.legendLabelColor; |
||||
|
this.widgetConfig.config.settings.legendValueFont = config.legendValueFont; |
||||
|
this.widgetConfig.config.settings.legendValueColor = config.legendValueColor; |
||||
|
|
||||
|
this.widgetConfig.config.settings.showTooltip = config.showTooltip; |
||||
|
this.widgetConfig.config.settings.tooltipValueType = config.tooltipValueType; |
||||
|
this.widgetConfig.config.settings.tooltipValueDecimals = config.tooltipValueDecimals; |
||||
|
this.widgetConfig.config.settings.tooltipValueFont = config.tooltipValueFont; |
||||
|
this.widgetConfig.config.settings.tooltipValueColor = config.tooltipValueColor; |
||||
|
this.widgetConfig.config.settings.tooltipBackgroundColor = config.tooltipBackgroundColor; |
||||
|
this.widgetConfig.config.settings.tooltipBackgroundBlur = config.tooltipBackgroundBlur; |
||||
|
|
||||
|
this.widgetConfig.config.settings.background = config.background; |
||||
|
|
||||
|
this.setCardButtons(config.cardButtons, this.widgetConfig.config); |
||||
|
this.widgetConfig.config.borderRadius = config.borderRadius; |
||||
|
|
||||
|
this.widgetConfig.config.actions = config.actions; |
||||
|
|
||||
|
return this.widgetConfig; |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['showTitle', 'showTitleIcon', 'showLegend', 'showTooltip']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean, trigger?: string) { |
||||
|
const showTitle: boolean = this.polarAreaChartWidgetConfigForm.get('showTitle').value; |
||||
|
const showTitleIcon: boolean = this.polarAreaChartWidgetConfigForm.get('showTitleIcon').value; |
||||
|
const showLegend: boolean = this.polarAreaChartWidgetConfigForm.get('showLegend').value; |
||||
|
const showTooltip: boolean = this.polarAreaChartWidgetConfigForm.get('showTooltip').value; |
||||
|
|
||||
|
if (showTitle) { |
||||
|
this.polarAreaChartWidgetConfigForm.get('title').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('titleFont').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('titleColor').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false}); |
||||
|
if (showTitleIcon) { |
||||
|
this.polarAreaChartWidgetConfigForm.get('titleIcon').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('iconColor').enable(); |
||||
|
} else { |
||||
|
this.polarAreaChartWidgetConfigForm.get('titleIcon').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('iconColor').disable(); |
||||
|
} |
||||
|
} else { |
||||
|
this.polarAreaChartWidgetConfigForm.get('title').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('titleFont').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('titleColor').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false}); |
||||
|
this.polarAreaChartWidgetConfigForm.get('titleIcon').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('iconColor').disable(); |
||||
|
} |
||||
|
if (showLegend) { |
||||
|
this.polarAreaChartWidgetConfigForm.get('legendPosition').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('legendLabelFont').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('legendLabelColor').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('legendValueFont').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('legendValueColor').enable(); |
||||
|
} else { |
||||
|
this.polarAreaChartWidgetConfigForm.get('legendPosition').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('legendLabelFont').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('legendLabelColor').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('legendValueFont').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('legendValueColor').disable(); |
||||
|
} |
||||
|
if (showTooltip) { |
||||
|
this.polarAreaChartWidgetConfigForm.get('tooltipValueType').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('tooltipValueDecimals').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('tooltipValueFont').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('tooltipValueColor').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('tooltipBackgroundColor').enable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('tooltipBackgroundBlur').enable(); |
||||
|
} else { |
||||
|
this.polarAreaChartWidgetConfigForm.get('tooltipValueType').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('tooltipValueDecimals').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('tooltipValueFont').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('tooltipValueColor').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('tooltipBackgroundColor').disable(); |
||||
|
this.polarAreaChartWidgetConfigForm.get('tooltipBackgroundBlur').disable(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private getSeries(datasources?: Datasource[]): DataKey[] { |
||||
|
if (datasources && datasources.length) { |
||||
|
return datasources[0].dataKeys || []; |
||||
|
} |
||||
|
return []; |
||||
|
} |
||||
|
|
||||
|
private setSeries(series: DataKey[], datasources?: Datasource[]) { |
||||
|
if (datasources && datasources.length) { |
||||
|
datasources[0].dataKeys = series; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private getCardButtons(config: WidgetConfig): string[] { |
||||
|
const buttons: string[] = []; |
||||
|
if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { |
||||
|
buttons.push('fullscreen'); |
||||
|
} |
||||
|
return buttons; |
||||
|
} |
||||
|
|
||||
|
private setCardButtons(buttons: string[], config: WidgetConfig) { |
||||
|
config.enableFullscreen = buttons.includes('fullscreen'); |
||||
|
} |
||||
|
|
||||
|
private _valuePreviewFn(): string { |
||||
|
const units: string = this.polarAreaChartWidgetConfigForm.get('units').value; |
||||
|
const decimals: number = this.polarAreaChartWidgetConfigForm.get('decimals').value; |
||||
|
return formatValue(110, decimals, units, false); |
||||
|
} |
||||
|
|
||||
|
private _tooltipValuePreviewFn(): string { |
||||
|
const tooltipValueType: LatestChartTooltipValueType = this.polarAreaChartWidgetConfigForm.get('tooltipValueType').value; |
||||
|
const decimals: number = this.polarAreaChartWidgetConfigForm.get('tooltipValueDecimals').value; |
||||
|
if (tooltipValueType === LatestChartTooltipValueType.percentage) { |
||||
|
return formatValue(35, decimals, '%', false); |
||||
|
} else { |
||||
|
const units: string = this.polarAreaChartWidgetConfigForm.get('units').value; |
||||
|
return formatValue(110, decimals, units, false); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,75 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 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, Input, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; |
||||
|
import { WidgetContext } from '@home/models/widget-component.models'; |
||||
|
import { WidgetComponent } from '@home/components/widget/widget.component'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { |
||||
|
LatestChartComponent, |
||||
|
LatestChartComponentCallbacks |
||||
|
} from '@home/components/widget/lib/chart/latest-chart.component'; |
||||
|
import { |
||||
|
barChartWidgetBarsChartSettings, |
||||
|
barChartWidgetDefaultSettings, |
||||
|
BarChartWidgetSettings |
||||
|
} from '@home/components/widget/lib/chart/bar-chart-widget.models'; |
||||
|
import { TbBarsChart } from '@home/components/widget/lib/chart/bars-chart'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-bar-chart-widget', |
||||
|
templateUrl: './latest-chart-widget.component.html', |
||||
|
styleUrls: [], |
||||
|
encapsulation: ViewEncapsulation.None |
||||
|
}) |
||||
|
export class BarChartWidgetComponent implements OnInit { |
||||
|
|
||||
|
@ViewChild('latestChart') |
||||
|
latestChart: LatestChartComponent; |
||||
|
|
||||
|
@Input() |
||||
|
ctx: WidgetContext; |
||||
|
|
||||
|
@Input() |
||||
|
widgetTitlePanel: TemplateRef<any>; |
||||
|
|
||||
|
settings: BarChartWidgetSettings; |
||||
|
|
||||
|
callbacks: LatestChartComponentCallbacks; |
||||
|
|
||||
|
constructor(private widgetComponent: WidgetComponent, |
||||
|
private translate: TranslateService) { |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.ctx.$scope.barChartWidget = this; |
||||
|
this.settings = {...barChartWidgetDefaultSettings, ...this.ctx.settings}; |
||||
|
this.callbacks = { |
||||
|
createChart: (chartShape, renderer) => { |
||||
|
const settings = barChartWidgetBarsChartSettings(this.settings); |
||||
|
return new TbBarsChart(this.ctx, settings, chartShape.nativeElement, renderer, this.translate, true); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public onInit() { |
||||
|
this.latestChart?.onInit(); |
||||
|
} |
||||
|
|
||||
|
public onDataUpdated() { |
||||
|
this.latestChart?.onDataUpdated(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,78 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 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 { |
||||
|
latestChartWidgetDefaultSettings, |
||||
|
LatestChartWidgetSettings |
||||
|
} from '@home/components/widget/lib/chart/latest-chart.models'; |
||||
|
import { mergeDeep } from '@core/utils'; |
||||
|
import { |
||||
|
barsChartAnimationDefaultSettings, |
||||
|
BarsChartSettings |
||||
|
} from '@home/components/widget/lib/chart/bars-chart.models'; |
||||
|
import { Font } from '@shared/models/widget-settings.models'; |
||||
|
import { DeepPartial } from '@shared/models/common'; |
||||
|
import { |
||||
|
ChartAnimationSettings, |
||||
|
chartBarDefaultSettings, |
||||
|
ChartBarSettings, |
||||
|
chartColorScheme |
||||
|
} from '@home/components/widget/lib/chart/chart.models'; |
||||
|
|
||||
|
export interface BarChartWidgetSettings extends LatestChartWidgetSettings { |
||||
|
axisMin?: number; |
||||
|
axisMax?: number; |
||||
|
axisTickLabelFont: Font; |
||||
|
axisTickLabelColor: string; |
||||
|
barSettings: ChartBarSettings; |
||||
|
} |
||||
|
|
||||
|
export const barChartWidgetDefaultSettings: BarChartWidgetSettings = { |
||||
|
...latestChartWidgetDefaultSettings, |
||||
|
animation: mergeDeep({} as ChartAnimationSettings, |
||||
|
barsChartAnimationDefaultSettings), |
||||
|
axisTickLabelFont: { |
||||
|
family: 'Roboto', |
||||
|
size: 12, |
||||
|
sizeUnit: 'px', |
||||
|
style: 'normal', |
||||
|
weight: '400', |
||||
|
lineHeight: '1' |
||||
|
}, |
||||
|
axisTickLabelColor: chartColorScheme['axis.tickLabel'].light, |
||||
|
barSettings: mergeDeep({} as ChartBarSettings, chartBarDefaultSettings, |
||||
|
{barWidth: 80, showLabel: true} as ChartBarSettings) |
||||
|
}; |
||||
|
|
||||
|
export const barChartWidgetBarsChartSettings = (settings: BarChartWidgetSettings): DeepPartial<BarsChartSettings> => ({ |
||||
|
polar: false, |
||||
|
axisMin: settings.axisMin, |
||||
|
axisMax: settings.axisMax, |
||||
|
axisTickLabelFont: settings.axisTickLabelFont, |
||||
|
axisTickLabelColor: settings.axisTickLabelColor, |
||||
|
barSettings: settings.barSettings, |
||||
|
sortSeries: settings.sortSeries, |
||||
|
showTotal: false, |
||||
|
animation: settings.animation, |
||||
|
showLegend: settings.showLegend, |
||||
|
showTooltip: settings.showTooltip, |
||||
|
tooltipValueType: settings.tooltipValueType, |
||||
|
tooltipValueDecimals: settings.tooltipValueDecimals, |
||||
|
tooltipValueFont: settings.tooltipValueFont, |
||||
|
tooltipValueColor: settings.tooltipValueColor, |
||||
|
tooltipBackgroundColor: settings.tooltipBackgroundColor, |
||||
|
tooltipBackgroundBlur: settings.tooltipBackgroundBlur |
||||
|
}); |
||||
@ -0,0 +1,61 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 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 { latestChartDefaultSettings, LatestChartSettings } from '@home/components/widget/lib/chart/latest-chart.models'; |
||||
|
import { mergeDeep } from '@core/utils'; |
||||
|
import { |
||||
|
chartAnimationDefaultSettings, |
||||
|
ChartAnimationSettings, |
||||
|
chartBarDefaultSettings, |
||||
|
ChartBarSettings, |
||||
|
chartColorScheme |
||||
|
} from '@home/components/widget/lib/chart/chart.models'; |
||||
|
import { Font } from '@shared/models/widget-settings.models'; |
||||
|
|
||||
|
export interface BarsChartSettings extends LatestChartSettings { |
||||
|
polar: boolean; |
||||
|
axisMin?: number | string; |
||||
|
axisMax?: number | string; |
||||
|
axisTickLabelFont: Font; |
||||
|
axisTickLabelColor: string; |
||||
|
angleAxisStartAngle?: number; |
||||
|
barSettings: ChartBarSettings; |
||||
|
} |
||||
|
|
||||
|
export const barsChartAnimationDefaultSettings: ChartAnimationSettings = |
||||
|
mergeDeep({} as ChartAnimationSettings, chartAnimationDefaultSettings, { |
||||
|
animationDuration: 1000, |
||||
|
animationDurationUpdate: 500 |
||||
|
} as ChartAnimationSettings); |
||||
|
|
||||
|
export const barsChartDefaultSettings: BarsChartSettings = { |
||||
|
...latestChartDefaultSettings, |
||||
|
animation: mergeDeep({} as ChartAnimationSettings, |
||||
|
barsChartAnimationDefaultSettings), |
||||
|
polar: false, |
||||
|
axisTickLabelFont: { |
||||
|
family: 'Roboto', |
||||
|
size: 12, |
||||
|
sizeUnit: 'px', |
||||
|
style: 'normal', |
||||
|
weight: '400', |
||||
|
lineHeight: '1' |
||||
|
}, |
||||
|
axisTickLabelColor: chartColorScheme['axis.tickLabel'].light, |
||||
|
angleAxisStartAngle: 0, |
||||
|
barSettings: mergeDeep({} as ChartBarSettings, chartBarDefaultSettings, |
||||
|
{barWidth: 80, showLabel: true} as ChartBarSettings) |
||||
|
}; |
||||
@ -0,0 +1,173 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 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 { TbLatestChart } from '@home/components/widget/lib/chart/latest-chart'; |
||||
|
import { barsChartDefaultSettings, BarsChartSettings } from '@home/components/widget/lib/chart/bars-chart.models'; |
||||
|
import { WidgetContext } from '@home/models/widget-component.models'; |
||||
|
import { DeepPartial } from '@shared/models/common'; |
||||
|
import { PieChartSettings } from '@home/components/widget/lib/chart/pie-chart.models'; |
||||
|
import { Renderer2 } from '@angular/core'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { ComponentStyle } from '@shared/models/widget-settings.models'; |
||||
|
import { LinearGradientObject } from 'zrender/lib/graphic/LinearGradient'; |
||||
|
import tinycolor from 'tinycolor2'; |
||||
|
import { BarDataItemOption, BarSeriesLabelOption } from 'echarts/types/src/chart/bar/BarSeries'; |
||||
|
import { formatValue, isDefinedAndNotNull } from '@core/utils'; |
||||
|
import { |
||||
|
ChartFillType, |
||||
|
ChartLabelPosition, |
||||
|
createChartTextStyle, |
||||
|
createLinearOpacityGradient |
||||
|
} from '@home/components/widget/lib/chart/chart.models'; |
||||
|
import { ValueAxisBaseOption } from 'echarts/types/src/coord/axisCommonTypes'; |
||||
|
import { RadiusAxisOption, YAXisOption } from 'echarts/types/dist/shared'; |
||||
|
|
||||
|
export class TbBarsChart extends TbLatestChart<BarsChartSettings> { |
||||
|
|
||||
|
constructor(ctx: WidgetContext, |
||||
|
inputSettings: DeepPartial<PieChartSettings>, |
||||
|
chartElement: HTMLElement, |
||||
|
renderer: Renderer2, |
||||
|
translate: TranslateService, |
||||
|
autoResize = true) { |
||||
|
|
||||
|
super(ctx, inputSettings, chartElement, renderer, translate, autoResize); |
||||
|
} |
||||
|
|
||||
|
protected defaultSettings(): BarsChartSettings { |
||||
|
return barsChartDefaultSettings; |
||||
|
} |
||||
|
|
||||
|
protected prepareLatestChartOption() { |
||||
|
let labelStyle: ComponentStyle = {}; |
||||
|
if (this.settings.barSettings.showLabel) { |
||||
|
labelStyle = createChartTextStyle(this.settings.barSettings.labelFont, |
||||
|
this.settings.barSettings.labelColor, false, 'series.label', false); |
||||
|
} |
||||
|
const labelOption: BarSeriesLabelOption = { |
||||
|
show: this.settings.barSettings.showLabel, |
||||
|
position: this.settings.barSettings.labelPosition, |
||||
|
formatter: (params) => `{label|${params.name}}`, |
||||
|
rich: { |
||||
|
label: labelStyle |
||||
|
} |
||||
|
}; |
||||
|
if (this.settings.barSettings.enableLabelBackground) { |
||||
|
labelOption.backgroundColor = this.settings.barSettings.labelBackground; |
||||
|
labelOption.padding = [4, 5]; |
||||
|
labelOption.borderRadius = 4; |
||||
|
} |
||||
|
this.latestChartOption.series = [ |
||||
|
{ |
||||
|
type: 'bar', |
||||
|
barWidth: isDefinedAndNotNull(this.settings.barSettings.barWidth) ? this.settings.barSettings.barWidth + '%' : undefined, |
||||
|
itemStyle: { |
||||
|
borderWidth: this.settings.barSettings.showBorder ? this.settings.barSettings.borderWidth : 0 |
||||
|
}, |
||||
|
emphasis: { |
||||
|
focus: 'self' |
||||
|
}, |
||||
|
coordinateSystem: this.settings.polar ? 'polar' : 'cartesian2d', |
||||
|
label: labelOption, |
||||
|
animation: this.settings.animation.animation, |
||||
|
animationThreshold: this.settings.animation.animationThreshold, |
||||
|
animationDuration: this.settings.animation.animationDuration, |
||||
|
animationEasing: this.settings.animation.animationEasing, |
||||
|
animationDelay: this.settings.animation.animationDelay, |
||||
|
animationDurationUpdate: this.settings.animation.animationDurationUpdate, |
||||
|
animationEasingUpdate: this.settings.animation.animationEasingUpdate, |
||||
|
animationDelayUpdate: this.settings.animation.animationDelayUpdate |
||||
|
} |
||||
|
]; |
||||
|
|
||||
|
const axisTickLabelStyle = createChartTextStyle(this.settings.axisTickLabelFont, |
||||
|
this.settings.axisTickLabelColor, false, 'axis.tickLabel'); |
||||
|
const valueAxis: ValueAxisBaseOption = { |
||||
|
type: 'value', |
||||
|
min: this.settings.axisMin, |
||||
|
max: this.settings.axisMax, |
||||
|
axisLabel: { |
||||
|
color: axisTickLabelStyle.color, |
||||
|
fontStyle: axisTickLabelStyle.fontStyle, |
||||
|
fontWeight: axisTickLabelStyle.fontWeight, |
||||
|
fontFamily: axisTickLabelStyle.fontFamily, |
||||
|
fontSize: axisTickLabelStyle.fontSize, |
||||
|
formatter: (value: any) => formatValue(value, this.decimals, this.units, false) |
||||
|
} |
||||
|
}; |
||||
|
if (this.settings.polar) { |
||||
|
this.latestChartOption.polar = { |
||||
|
radius: '100%' |
||||
|
}; |
||||
|
this.latestChartOption.radiusAxis = valueAxis as RadiusAxisOption; |
||||
|
this.latestChartOption.angleAxis = { |
||||
|
type: 'category', |
||||
|
data: [], |
||||
|
startAngle: this.settings.angleAxisStartAngle |
||||
|
}; |
||||
|
} else { |
||||
|
let minTop = 0; |
||||
|
let minBottom = 0; |
||||
|
if (this.settings.barSettings.showLabel) { |
||||
|
if (this.settings.barSettings.labelPosition === ChartLabelPosition.top) { |
||||
|
minTop = this.settings.barSettings.labelFont.size; |
||||
|
} else if (this.settings.barSettings.labelPosition === ChartLabelPosition.bottom) { |
||||
|
minBottom = this.settings.barSettings.labelFont.size; |
||||
|
} |
||||
|
} |
||||
|
this.latestChartOption.grid = [{ |
||||
|
containLabel: true, |
||||
|
top: minTop, |
||||
|
bottom: minBottom, |
||||
|
left: 0, |
||||
|
right: 0 |
||||
|
}]; |
||||
|
this.latestChartOption.xAxis = { |
||||
|
type: 'category', |
||||
|
data: [] |
||||
|
}; |
||||
|
this.latestChartOption.yAxis = valueAxis as YAXisOption; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected doUpdateSeriesData() { |
||||
|
const seriesData: BarDataItemOption[] = []; |
||||
|
for (const dataItem of this.dataItems) { |
||||
|
if (dataItem.enabled && dataItem.hasValue) { |
||||
|
const barSettings = this.settings.barSettings; |
||||
|
let borderRadius: number[]; |
||||
|
if (dataItem.value < 0) { |
||||
|
borderRadius = [0, 0, barSettings.borderRadius, barSettings.borderRadius]; |
||||
|
} else { |
||||
|
borderRadius = [barSettings.borderRadius, barSettings.borderRadius, 0, 0]; |
||||
|
} |
||||
|
let barColor: string | LinearGradientObject; |
||||
|
if (barSettings.backgroundSettings.type === ChartFillType.none) { |
||||
|
barColor = dataItem.dataKey.color; |
||||
|
} else if (barSettings.backgroundSettings.type === ChartFillType.opacity) { |
||||
|
barColor = tinycolor(dataItem.dataKey.color).setAlpha(barSettings.backgroundSettings.opacity).toRgbString(); |
||||
|
} else { |
||||
|
barColor = createLinearOpacityGradient(dataItem.dataKey.color, barSettings.backgroundSettings.gradient); |
||||
|
} |
||||
|
seriesData.push( |
||||
|
{id: dataItem.id, value: dataItem.value, name: dataItem.dataKey.label, |
||||
|
itemStyle: {color: barColor, borderColor: dataItem.dataKey.color, borderRadius}} |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
this.latestChartOption.series[0].data = seriesData; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,316 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 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 { isNumber } from '@core/utils'; |
||||
|
import { TbColorScheme } from '@shared/models/color.models'; |
||||
|
import { LinearGradientObject } from 'zrender/lib/graphic/LinearGradient'; |
||||
|
import tinycolor from 'tinycolor2'; |
||||
|
import { ComponentStyle, Font, textStyle } from '@shared/models/widget-settings.models'; |
||||
|
import { LabelFormatterCallback } from 'echarts'; |
||||
|
import { LabelLayoutOption } from 'echarts/types/src/util/types'; |
||||
|
import { LabelLayoutOptionCallback } from 'echarts/types/dist/shared'; |
||||
|
import { BuiltinTextPosition } from 'zrender/src/core/types'; |
||||
|
|
||||
|
export const chartColorScheme: TbColorScheme = { |
||||
|
'threshold.line': { |
||||
|
light: 'rgba(0, 0, 0, 0.76)', |
||||
|
dark: '#eee' |
||||
|
}, |
||||
|
'threshold.label': { |
||||
|
light: 'rgba(0, 0, 0, 0.76)', |
||||
|
dark: '#eee' |
||||
|
}, |
||||
|
'axis.line': { |
||||
|
light: 'rgba(0, 0, 0, 0.54)', |
||||
|
dark: '#B9B8CE' |
||||
|
}, |
||||
|
'axis.label': { |
||||
|
light: 'rgba(0, 0, 0, 0.54)', |
||||
|
dark: '#B9B8CE' |
||||
|
}, |
||||
|
'axis.ticks': { |
||||
|
light: 'rgba(0, 0, 0, 0.54)', |
||||
|
dark: '#B9B8CE' |
||||
|
}, |
||||
|
'axis.tickLabel': { |
||||
|
light: 'rgba(0, 0, 0, 0.54)', |
||||
|
dark: '#B9B8CE' |
||||
|
}, |
||||
|
'axis.splitLine': { |
||||
|
light: 'rgba(0, 0, 0, 0.12)', |
||||
|
dark: '#484753' |
||||
|
}, |
||||
|
'series.label': { |
||||
|
light: 'rgba(0, 0, 0, 0.76)', |
||||
|
dark: '#eee' |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export enum ChartShape { |
||||
|
emptyCircle = 'emptyCircle', |
||||
|
circle = 'circle', |
||||
|
rect = 'rect', |
||||
|
roundRect = 'roundRect', |
||||
|
triangle = 'triangle', |
||||
|
diamond = 'diamond', |
||||
|
pin = 'pin', |
||||
|
arrow = 'arrow', |
||||
|
none = 'none' |
||||
|
} |
||||
|
|
||||
|
export const chartShapes = Object.keys(ChartShape) as ChartShape[]; |
||||
|
|
||||
|
export const chartShapeTranslations = new Map<ChartShape, string>( |
||||
|
[ |
||||
|
[ChartShape.emptyCircle, 'widgets.chart.shape-empty-circle'], |
||||
|
[ChartShape.circle, 'widgets.chart.shape-circle'], |
||||
|
[ChartShape.rect, 'widgets.chart.shape-rect'], |
||||
|
[ChartShape.roundRect, 'widgets.chart.shape-round-rect'], |
||||
|
[ChartShape.triangle, 'widgets.chart.shape-triangle'], |
||||
|
[ChartShape.diamond, 'widgets.chart.shape-diamond'], |
||||
|
[ChartShape.pin, 'widgets.chart.shape-pin'], |
||||
|
[ChartShape.arrow, 'widgets.chart.shape-arrow'], |
||||
|
[ChartShape.none, 'widgets.chart.shape-none'] |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
export enum ChartAnimationEasing { |
||||
|
linear = 'linear', |
||||
|
quadraticIn = 'quadraticIn', |
||||
|
quadraticOut = 'quadraticOut', |
||||
|
quadraticInOut = 'quadraticInOut', |
||||
|
cubicIn = 'cubicIn', |
||||
|
cubicOut = 'cubicOut', |
||||
|
cubicInOut = 'cubicInOut', |
||||
|
quarticIn = 'quarticIn', |
||||
|
quarticOut = 'quarticOut', |
||||
|
quarticInOut = 'quarticInOut', |
||||
|
quinticIn = 'quinticIn', |
||||
|
quinticOut = 'quinticOut', |
||||
|
quinticInOut = 'quinticInOut', |
||||
|
sinusoidalIn = 'sinusoidalIn', |
||||
|
sinusoidalOut = 'sinusoidalOut', |
||||
|
sinusoidalInOut = 'sinusoidalInOut', |
||||
|
exponentialIn = 'exponentialIn', |
||||
|
exponentialOut = 'exponentialOut', |
||||
|
exponentialInOut = 'exponentialInOut', |
||||
|
circularIn = 'circularIn', |
||||
|
circularOut = 'circularOut', |
||||
|
circularInOut = 'circularInOut', |
||||
|
elasticIn = 'elasticIn', |
||||
|
elasticOut = 'elasticOut', |
||||
|
elasticInOut = 'elasticInOut', |
||||
|
backIn = 'backIn', |
||||
|
backOut = 'backOut', |
||||
|
backInOut = 'backInOut', |
||||
|
bounceIn = 'bounceIn', |
||||
|
bounceOut = 'bounceOut', |
||||
|
bounceInOut = 'bounceInOut' |
||||
|
} |
||||
|
|
||||
|
export const chartAnimationEasings = Object.keys(ChartAnimationEasing) as ChartAnimationEasing[]; |
||||
|
|
||||
|
export enum ChartFillType { |
||||
|
none = 'none', |
||||
|
opacity = 'opacity', |
||||
|
gradient = 'gradient' |
||||
|
} |
||||
|
|
||||
|
export const chartFillTypes = Object.keys(ChartFillType) as ChartFillType[]; |
||||
|
|
||||
|
export const chartFillTypeTranslations = new Map<ChartFillType, string>( |
||||
|
[ |
||||
|
[ChartFillType.none, 'widgets.chart.fill-type-none'], |
||||
|
[ChartFillType.opacity, 'widgets.chart.fill-type-opacity'], |
||||
|
[ChartFillType.gradient, 'widgets.chart.fill-type-gradient'] |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
export enum ChartLabelPosition { |
||||
|
top = 'top', |
||||
|
bottom = 'bottom' |
||||
|
} |
||||
|
|
||||
|
export const chartLabelPositions = Object.keys(ChartLabelPosition) as ChartLabelPosition[]; |
||||
|
|
||||
|
export const chartLabelPositionTranslations = new Map<ChartLabelPosition, string>( |
||||
|
[ |
||||
|
[ChartLabelPosition.top, 'widgets.chart.label-position-top'], |
||||
|
[ChartLabelPosition.bottom, 'widgets.chart.label-position-bottom'] |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
export enum PieChartLabelPosition { |
||||
|
inside = 'inside', |
||||
|
outside = 'outside' |
||||
|
} |
||||
|
|
||||
|
export const pieChartLabelPositions = Object.keys(PieChartLabelPosition) as PieChartLabelPosition[]; |
||||
|
|
||||
|
export const pieChartLabelPositionTranslations = new Map<PieChartLabelPosition, string>( |
||||
|
[ |
||||
|
[PieChartLabelPosition.inside, 'widgets.chart.label-position-inside'], |
||||
|
[PieChartLabelPosition.outside, 'widgets.chart.label-position-outside'] |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
export interface ChartAnimationSettings { |
||||
|
animation: boolean; |
||||
|
animationThreshold: number; |
||||
|
animationDuration: number; |
||||
|
animationEasing: ChartAnimationEasing; |
||||
|
animationDelay: number; |
||||
|
animationDurationUpdate: number; |
||||
|
animationEasingUpdate: ChartAnimationEasing; |
||||
|
animationDelayUpdate: number; |
||||
|
} |
||||
|
|
||||
|
export const chartAnimationDefaultSettings: ChartAnimationSettings = { |
||||
|
animation: true, |
||||
|
animationThreshold: 2000, |
||||
|
animationDuration: 500, |
||||
|
animationEasing: ChartAnimationEasing.cubicOut, |
||||
|
animationDelay: 0, |
||||
|
animationDurationUpdate: 300, |
||||
|
animationEasingUpdate: ChartAnimationEasing.cubicOut, |
||||
|
animationDelayUpdate: 0 |
||||
|
}; |
||||
|
|
||||
|
export interface ChartFillSettings { |
||||
|
type: ChartFillType; |
||||
|
opacity: number; |
||||
|
gradient: { |
||||
|
start: number; |
||||
|
end: number; |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export interface ChartBarSettings { |
||||
|
showBorder: boolean; |
||||
|
borderWidth: number; |
||||
|
borderRadius: number; |
||||
|
barWidth?: number; |
||||
|
showLabel: boolean; |
||||
|
labelPosition: ChartLabelPosition | PieChartLabelPosition | BuiltinTextPosition; |
||||
|
labelFont: Font; |
||||
|
labelColor: string; |
||||
|
enableLabelBackground: boolean; |
||||
|
labelBackground: string; |
||||
|
labelFormatter?: string | LabelFormatterCallback; |
||||
|
labelLayout?: LabelLayoutOption | LabelLayoutOptionCallback; |
||||
|
additionalLabelOption?: {[key: string]: any}; |
||||
|
backgroundSettings: ChartFillSettings; |
||||
|
} |
||||
|
|
||||
|
export const chartBarDefaultSettings: ChartBarSettings = { |
||||
|
showBorder: false, |
||||
|
borderWidth: 2, |
||||
|
borderRadius: 0, |
||||
|
showLabel: false, |
||||
|
labelPosition: ChartLabelPosition.top, |
||||
|
labelFont: { |
||||
|
family: 'Roboto', |
||||
|
size: 11, |
||||
|
sizeUnit: 'px', |
||||
|
style: 'normal', |
||||
|
weight: '400', |
||||
|
lineHeight: '1' |
||||
|
}, |
||||
|
labelColor: chartColorScheme['series.label'].light, |
||||
|
enableLabelBackground: false, |
||||
|
labelBackground: 'rgba(255,255,255,0.56)', |
||||
|
backgroundSettings: { |
||||
|
type: ChartFillType.none, |
||||
|
opacity: 0.4, |
||||
|
gradient: { |
||||
|
start: 100, |
||||
|
end: 0 |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
type ChartShapeOffsetFunction = (size: number) => number; |
||||
|
|
||||
|
const chartShapeOffsetFunctions = new Map<ChartShape, ChartShapeOffsetFunction>( |
||||
|
[ |
||||
|
[ChartShape.emptyCircle, size => size / 2 + 1], |
||||
|
[ChartShape.circle, size => size / 2], |
||||
|
[ChartShape.rect, size => size / 2], |
||||
|
[ChartShape.roundRect, size => size / 2], |
||||
|
[ChartShape.triangle, size => size / 2], |
||||
|
[ChartShape.diamond, size => size / 2], |
||||
|
[ChartShape.pin, size => size], |
||||
|
[ChartShape.arrow, () => 0], |
||||
|
[ChartShape.none, () => 0], |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
export const measureSymbolOffset = (symbol: string, symbolSize: any): number => { |
||||
|
if (isNumber(symbolSize)) { |
||||
|
if (symbol) { |
||||
|
const offsetFunction = chartShapeOffsetFunctions.get(symbol as ChartShape); |
||||
|
if (offsetFunction) { |
||||
|
return offsetFunction(symbolSize); |
||||
|
} else { |
||||
|
return symbolSize / 2; |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
return 0; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export const createLinearOpacityGradient = (color: string, gradient: {start: number; end: number}): LinearGradientObject => ({ |
||||
|
type: 'linear', |
||||
|
x: 0, |
||||
|
y: 0, |
||||
|
x2: 0, |
||||
|
y2: 1, |
||||
|
colorStops: [{ |
||||
|
offset: 0, color: tinycolor(color).setAlpha(gradient.start / 100).toRgbString() // color at 0%
|
||||
|
}, { |
||||
|
offset: 1, color: tinycolor(color).setAlpha(gradient.end / 100).toRgbString() // color at 100%
|
||||
|
}], |
||||
|
global: false |
||||
|
}); |
||||
|
|
||||
|
export const createChartTextStyle = (font: Font, color: string, darkMode: boolean, colorKey?: string, fill = false): ComponentStyle => { |
||||
|
const style = textStyle(font); |
||||
|
delete style.lineHeight; |
||||
|
style.fontSize = font.size; |
||||
|
if (fill) { |
||||
|
style.fill = prepareChartThemeColor(color, darkMode, colorKey); |
||||
|
} else { |
||||
|
style.color = prepareChartThemeColor(color, darkMode, colorKey); |
||||
|
} |
||||
|
return style; |
||||
|
}; |
||||
|
|
||||
|
export const prepareChartThemeColor = (color: string, darkMode: boolean, colorKey?: string): string => { |
||||
|
if (darkMode) { |
||||
|
let colorInstance = tinycolor(color); |
||||
|
if (colorInstance.isDark()) { |
||||
|
if (colorKey && chartColorScheme[colorKey]) { |
||||
|
return chartColorScheme[colorKey].dark; |
||||
|
} else { |
||||
|
const rgb = colorInstance.toRgb(); |
||||
|
colorInstance = tinycolor({r: 255 - rgb.r, g: 255 - rgb.g, b: 255 - rgb.b, a: rgb.a}); |
||||
|
return colorInstance.toRgbString(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return color; |
||||
|
}; |
||||
@ -0,0 +1,75 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 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, Input, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; |
||||
|
import { WidgetContext } from '@home/models/widget-component.models'; |
||||
|
import { WidgetComponent } from '@home/components/widget/widget.component'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { |
||||
|
LatestChartComponent, |
||||
|
LatestChartComponentCallbacks |
||||
|
} from '@home/components/widget/lib/chart/latest-chart.component'; |
||||
|
import { TbBarsChart } from '@home/components/widget/lib/chart/bars-chart'; |
||||
|
import { |
||||
|
polarAreaChartWidgetBarsChartSettings, |
||||
|
polarAreaChartWidgetDefaultSettings, |
||||
|
PolarAreaChartWidgetSettings |
||||
|
} from '@home/components/widget/lib/chart/polar-area-widget.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-polar-area-chart-widget', |
||||
|
templateUrl: './latest-chart-widget.component.html', |
||||
|
styleUrls: [], |
||||
|
encapsulation: ViewEncapsulation.None |
||||
|
}) |
||||
|
export class PolarAreaWidgetComponent implements OnInit { |
||||
|
|
||||
|
@ViewChild('latestChart') |
||||
|
latestChart: LatestChartComponent; |
||||
|
|
||||
|
@Input() |
||||
|
ctx: WidgetContext; |
||||
|
|
||||
|
@Input() |
||||
|
widgetTitlePanel: TemplateRef<any>; |
||||
|
|
||||
|
settings: PolarAreaChartWidgetSettings; |
||||
|
|
||||
|
callbacks: LatestChartComponentCallbacks; |
||||
|
|
||||
|
constructor(private widgetComponent: WidgetComponent, |
||||
|
private translate: TranslateService) { |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.ctx.$scope.polarAreaChartWidget = this; |
||||
|
this.settings = {...polarAreaChartWidgetDefaultSettings, ...this.ctx.settings}; |
||||
|
this.callbacks = { |
||||
|
createChart: (chartShape, renderer) => { |
||||
|
const settings = polarAreaChartWidgetBarsChartSettings(this.settings); |
||||
|
return new TbBarsChart(this.ctx, settings, chartShape.nativeElement, renderer, this.translate, true); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public onInit() { |
||||
|
this.latestChart?.onInit(); |
||||
|
} |
||||
|
|
||||
|
public onDataUpdated() { |
||||
|
this.latestChart?.onDataUpdated(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,82 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 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 { |
||||
|
latestChartWidgetDefaultSettings, |
||||
|
LatestChartWidgetSettings |
||||
|
} from '@home/components/widget/lib/chart/latest-chart.models'; |
||||
|
import { Font } from '@shared/models/widget-settings.models'; |
||||
|
import { |
||||
|
ChartAnimationSettings, |
||||
|
chartBarDefaultSettings, |
||||
|
ChartBarSettings, |
||||
|
chartColorScheme, |
||||
|
PieChartLabelPosition |
||||
|
} from '@home/components/widget/lib/chart/chart.models'; |
||||
|
import { mergeDeep } from '@core/utils'; |
||||
|
import { |
||||
|
barsChartAnimationDefaultSettings, |
||||
|
BarsChartSettings |
||||
|
} from '@home/components/widget/lib/chart/bars-chart.models'; |
||||
|
import { DeepPartial } from '@shared/models/common'; |
||||
|
|
||||
|
export interface PolarAreaChartWidgetSettings extends LatestChartWidgetSettings { |
||||
|
axisMin?: number; |
||||
|
axisMax?: number; |
||||
|
axisTickLabelFont: Font; |
||||
|
axisTickLabelColor: string; |
||||
|
angleAxisStartAngle?: number; |
||||
|
barSettings: ChartBarSettings; |
||||
|
} |
||||
|
|
||||
|
export const polarAreaChartWidgetDefaultSettings: PolarAreaChartWidgetSettings = { |
||||
|
...latestChartWidgetDefaultSettings, |
||||
|
animation: mergeDeep({} as ChartAnimationSettings, |
||||
|
barsChartAnimationDefaultSettings), |
||||
|
axisTickLabelFont: { |
||||
|
family: 'Roboto', |
||||
|
size: 12, |
||||
|
sizeUnit: 'px', |
||||
|
style: 'normal', |
||||
|
weight: '400', |
||||
|
lineHeight: '1' |
||||
|
}, |
||||
|
axisTickLabelColor: chartColorScheme['axis.tickLabel'].light, |
||||
|
angleAxisStartAngle: 90, |
||||
|
barSettings: mergeDeep({} as ChartBarSettings, chartBarDefaultSettings, |
||||
|
{barWidth: 100, showLabel: true, labelPosition: PieChartLabelPosition.outside} as ChartBarSettings) |
||||
|
}; |
||||
|
|
||||
|
export const polarAreaChartWidgetBarsChartSettings = (settings: PolarAreaChartWidgetSettings): DeepPartial<BarsChartSettings> => ({ |
||||
|
polar: true, |
||||
|
axisMin: settings.axisMin, |
||||
|
axisMax: settings.axisMax, |
||||
|
axisTickLabelFont: settings.axisTickLabelFont, |
||||
|
axisTickLabelColor: settings.axisTickLabelColor, |
||||
|
angleAxisStartAngle: settings.angleAxisStartAngle, |
||||
|
barSettings: settings.barSettings, |
||||
|
sortSeries: settings.sortSeries, |
||||
|
showTotal: false, |
||||
|
animation: settings.animation, |
||||
|
showLegend: settings.showLegend, |
||||
|
showTooltip: settings.showTooltip, |
||||
|
tooltipValueType: settings.tooltipValueType, |
||||
|
tooltipValueDecimals: settings.tooltipValueDecimals, |
||||
|
tooltipValueFont: settings.tooltipValueFont, |
||||
|
tooltipValueColor: settings.tooltipValueColor, |
||||
|
tooltipBackgroundColor: settings.tooltipBackgroundColor, |
||||
|
tooltipBackgroundBlur: settings.tooltipBackgroundBlur |
||||
|
}); |
||||
@ -0,0 +1,169 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2024 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. |
||||
|
|
||||
|
--> |
||||
|
<ng-container [formGroup]="barChartWidgetSettingsForm"> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-chart-style</div> |
||||
|
<div class="tb-form-row"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="sortSeries"> |
||||
|
{{ 'widgets.latest-chart.sort-series' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-appearance</div> |
||||
|
<tb-chart-bar-settings |
||||
|
formControlName="barSettings" |
||||
|
[series]="false"> |
||||
|
</tb-chart-bar-settings> |
||||
|
</div> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-axis</div> |
||||
|
<div class="tb-form-row space-between column-xs"> |
||||
|
<div translate>widgets.chart.chart-axis.scale</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-min</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="axisMin" |
||||
|
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-max</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="axisMax" |
||||
|
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
<tb-font-settings formControlName="axisTickLabelFont" |
||||
|
clearButton |
||||
|
disabledLineHeight |
||||
|
forceSizeUnit="px" |
||||
|
previewText="100"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="axisTickLabelColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-panel tb-slide-toggle"> |
||||
|
<mat-expansion-panel class="tb-settings" [expanded]="barChartWidgetSettingsForm.get('showLegend').value" [disabled]="!barChartWidgetSettingsForm.get('showLegend').value"> |
||||
|
<mat-expansion-panel-header fxLayout="row wrap"> |
||||
|
<mat-panel-title> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="showLegend" (click)="$event.stopPropagation()" |
||||
|
fxLayoutAlign="center"> |
||||
|
{{ 'widget-config.legend' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<ng-template matExpansionPanelContent> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'legend.position' | translate }}</div> |
||||
|
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic"> |
||||
|
<mat-select formControlName="legendPosition"> |
||||
|
<mat-option *ngFor="let pos of legendPositions" [value]="pos"> |
||||
|
{{ legendPositionTranslationMap.get(pos) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'legend.label' | translate }}</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<tb-font-settings formControlName="legendLabelFont" |
||||
|
previewText="Wind power"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="legendLabelColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'legend.value' | translate }}</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<tb-font-settings formControlName="legendValueFont" |
||||
|
[previewText]="valuePreviewFn"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="legendValueColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</mat-expansion-panel> |
||||
|
</div> |
||||
|
<div class="tb-form-panel tb-slide-toggle"> |
||||
|
<mat-expansion-panel class="tb-settings" [expanded]="barChartWidgetSettingsForm.get('showTooltip').value" [disabled]="!barChartWidgetSettingsForm.get('showTooltip').value"> |
||||
|
<mat-expansion-panel-header fxLayout="row wrap"> |
||||
|
<mat-panel-title> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="showTooltip" (click)="$event.stopPropagation()" |
||||
|
fxLayoutAlign="center"> |
||||
|
{{ 'widget-config.tooltip' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<ng-template matExpansionPanelContent> |
||||
|
<div class="tb-form-row space-between column-xs"> |
||||
|
<div>{{ 'tooltip.value' | translate }}</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic"> |
||||
|
<mat-select formControlName="tooltipValueType"> |
||||
|
<mat-option *ngFor="let type of latestChartTooltipValueTypes" [value]="type"> |
||||
|
{{ latestChartTooltipValueTypeTranslationMap.get(type) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="tooltipValueDecimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
<div matSuffix fxHide.lt-md translate>widget-config.decimals-suffix</div> |
||||
|
</mat-form-field> |
||||
|
<tb-font-settings formControlName="tooltipValueFont" |
||||
|
[previewText]="tooltipValuePreviewFn"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="tooltipValueColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'tooltip.background-color' | translate }}</div> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="tooltipBackgroundColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'tooltip.background-blur' | translate }}</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="tooltipBackgroundBlur" type="number" min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
<div matSuffix>px</div> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</mat-expansion-panel> |
||||
|
</div> |
||||
|
<tb-chart-animation-settings |
||||
|
formControlName="animation"> |
||||
|
</tb-chart-animation-settings> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'widgets.background.background' | translate }}</div> |
||||
|
<tb-background-settings formControlName="background"> |
||||
|
</tb-background-settings> |
||||
|
</div> |
||||
|
</div> |
||||
|
</ng-container> |
||||
@ -0,0 +1,160 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 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 { |
||||
|
legendPositions, |
||||
|
legendPositionTranslationMap, |
||||
|
WidgetSettings, |
||||
|
WidgetSettingsComponent |
||||
|
} from '@shared/models/widget.models'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { AppState } from '@core/core.state'; |
||||
|
import { formatValue, mergeDeep } from '@core/utils'; |
||||
|
import { |
||||
|
LatestChartTooltipValueType, |
||||
|
latestChartTooltipValueTypes, |
||||
|
latestChartTooltipValueTypeTranslations |
||||
|
} from '@home/components/widget/lib/chart/latest-chart.models'; |
||||
|
import { |
||||
|
barChartWidgetDefaultSettings, |
||||
|
BarChartWidgetSettings |
||||
|
} from '@home/components/widget/lib/chart/bar-chart-widget.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-bar-chart-widget-settings', |
||||
|
templateUrl: './bar-chart-widget-settings.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class BarChartWidgetSettingsComponent extends WidgetSettingsComponent { |
||||
|
|
||||
|
legendPositions = legendPositions; |
||||
|
|
||||
|
legendPositionTranslationMap = legendPositionTranslationMap; |
||||
|
|
||||
|
latestChartTooltipValueTypes = latestChartTooltipValueTypes; |
||||
|
|
||||
|
latestChartTooltipValueTypeTranslationMap = latestChartTooltipValueTypeTranslations; |
||||
|
|
||||
|
barChartWidgetSettingsForm: UntypedFormGroup; |
||||
|
|
||||
|
valuePreviewFn = this._valuePreviewFn.bind(this); |
||||
|
|
||||
|
tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private fb: UntypedFormBuilder) { |
||||
|
super(store); |
||||
|
} |
||||
|
|
||||
|
protected settingsForm(): UntypedFormGroup { |
||||
|
return this.barChartWidgetSettingsForm; |
||||
|
} |
||||
|
|
||||
|
protected defaultSettings(): WidgetSettings { |
||||
|
return mergeDeep<BarChartWidgetSettings>({} as BarChartWidgetSettings, barChartWidgetDefaultSettings); |
||||
|
} |
||||
|
|
||||
|
protected onSettingsSet(settings: WidgetSettings) { |
||||
|
this.barChartWidgetSettingsForm = this.fb.group({ |
||||
|
|
||||
|
sortSeries: [settings.sortSeries, []], |
||||
|
|
||||
|
barSettings: [settings.barSettings, []], |
||||
|
|
||||
|
axisMin: [settings.axisMin, []], |
||||
|
axisMax: [settings.axisMax, []], |
||||
|
axisTickLabelFont: [settings.axisTickLabelFont, []], |
||||
|
axisTickLabelColor: [settings.axisTickLabelColor, []], |
||||
|
|
||||
|
animation: [settings.animation, []], |
||||
|
|
||||
|
showLegend: [settings.showLegend, []], |
||||
|
legendPosition: [settings.legendPosition, []], |
||||
|
legendLabelFont: [settings.legendLabelFont, []], |
||||
|
legendLabelColor: [settings.legendLabelColor, []], |
||||
|
legendValueFont: [settings.legendValueFont, []], |
||||
|
legendValueColor: [settings.legendValueColor, []], |
||||
|
|
||||
|
showTooltip: [settings.showTooltip, []], |
||||
|
tooltipValueType: [settings.tooltipValueType, []], |
||||
|
tooltipValueDecimals: [settings.tooltipValueDecimals, []], |
||||
|
tooltipValueFont: [settings.tooltipValueFont, []], |
||||
|
tooltipValueColor: [settings.tooltipValueColor, []], |
||||
|
tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], |
||||
|
tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], |
||||
|
|
||||
|
background: [settings.background, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['showLegend', 'showTooltip']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
const showLegend: boolean = this.barChartWidgetSettingsForm.get('showLegend').value; |
||||
|
const showTooltip: boolean = this.barChartWidgetSettingsForm.get('showTooltip').value; |
||||
|
|
||||
|
if (showLegend) { |
||||
|
this.barChartWidgetSettingsForm.get('legendPosition').enable(); |
||||
|
this.barChartWidgetSettingsForm.get('legendLabelFont').enable(); |
||||
|
this.barChartWidgetSettingsForm.get('legendLabelColor').enable(); |
||||
|
this.barChartWidgetSettingsForm.get('legendValueFont').enable(); |
||||
|
this.barChartWidgetSettingsForm.get('legendValueColor').enable(); |
||||
|
} else { |
||||
|
this.barChartWidgetSettingsForm.get('legendPosition').disable(); |
||||
|
this.barChartWidgetSettingsForm.get('legendLabelFont').disable(); |
||||
|
this.barChartWidgetSettingsForm.get('legendLabelColor').disable(); |
||||
|
this.barChartWidgetSettingsForm.get('legendValueFont').disable(); |
||||
|
this.barChartWidgetSettingsForm.get('legendValueColor').disable(); |
||||
|
} |
||||
|
if (showTooltip) { |
||||
|
this.barChartWidgetSettingsForm.get('tooltipValueType').enable(); |
||||
|
this.barChartWidgetSettingsForm.get('tooltipValueDecimals').enable(); |
||||
|
this.barChartWidgetSettingsForm.get('tooltipValueFont').enable(); |
||||
|
this.barChartWidgetSettingsForm.get('tooltipValueColor').enable(); |
||||
|
this.barChartWidgetSettingsForm.get('tooltipBackgroundColor').enable(); |
||||
|
this.barChartWidgetSettingsForm.get('tooltipBackgroundBlur').enable(); |
||||
|
} else { |
||||
|
this.barChartWidgetSettingsForm.get('tooltipValueType').disable(); |
||||
|
this.barChartWidgetSettingsForm.get('tooltipValueDecimals').disable(); |
||||
|
this.barChartWidgetSettingsForm.get('tooltipValueFont').disable(); |
||||
|
this.barChartWidgetSettingsForm.get('tooltipValueColor').disable(); |
||||
|
this.barChartWidgetSettingsForm.get('tooltipBackgroundColor').disable(); |
||||
|
this.barChartWidgetSettingsForm.get('tooltipBackgroundBlur').disable(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private _valuePreviewFn(): string { |
||||
|
const units: string = this.widgetConfig.config.units; |
||||
|
const decimals: number = this.widgetConfig.config.decimals; |
||||
|
return formatValue(110, decimals, units, false); |
||||
|
} |
||||
|
|
||||
|
private _tooltipValuePreviewFn(): string { |
||||
|
const tooltipValueType: LatestChartTooltipValueType = this.barChartWidgetSettingsForm.get('tooltipValueType').value; |
||||
|
const decimals: number = this.barChartWidgetSettingsForm.get('tooltipValueDecimals').value; |
||||
|
if (tooltipValueType === LatestChartTooltipValueType.percentage) { |
||||
|
return formatValue(35, decimals, '%', false); |
||||
|
} else { |
||||
|
const units: string = this.widgetConfig.config.units; |
||||
|
return formatValue(110, decimals, units, false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,176 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2024 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. |
||||
|
|
||||
|
--> |
||||
|
<ng-container [formGroup]="polarAreaChartWidgetSettingsForm"> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widgets.polar-area-chart.polar-area-chart-style</div> |
||||
|
<div class="tb-form-row"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="sortSeries"> |
||||
|
{{ 'widgets.latest-chart.sort-series' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-appearance</div> |
||||
|
<tb-chart-bar-settings |
||||
|
formControlName="barSettings" |
||||
|
pieLabelPosition |
||||
|
[series]="false"> |
||||
|
</tb-chart-bar-settings> |
||||
|
</div> |
||||
|
<div class="tb-form-panel"> |
||||
|
<div class="tb-form-panel-title" translate>widgets.polar-area-chart.polar-axis</div> |
||||
|
<div class="tb-form-row space-between column-xs"> |
||||
|
<div translate>widgets.chart.chart-axis.scale</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-min</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="axisMin" |
||||
|
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-max</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="axisMax" |
||||
|
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
<tb-font-settings formControlName="axisTickLabelFont" |
||||
|
clearButton |
||||
|
disabledLineHeight |
||||
|
forceSizeUnit="px" |
||||
|
previewText="100"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="axisTickLabelColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div translate>widgets.polar-area-chart.start-angle</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="angleAxisStartAngle" type="number" min="0" max="360" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-panel tb-slide-toggle"> |
||||
|
<mat-expansion-panel class="tb-settings" [expanded]="polarAreaChartWidgetSettingsForm.get('showLegend').value" [disabled]="!polarAreaChartWidgetSettingsForm.get('showLegend').value"> |
||||
|
<mat-expansion-panel-header fxLayout="row wrap"> |
||||
|
<mat-panel-title> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="showLegend" (click)="$event.stopPropagation()" |
||||
|
fxLayoutAlign="center"> |
||||
|
{{ 'widget-config.legend' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<ng-template matExpansionPanelContent> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'legend.position' | translate }}</div> |
||||
|
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic"> |
||||
|
<mat-select formControlName="legendPosition"> |
||||
|
<mat-option *ngFor="let pos of legendPositions" [value]="pos"> |
||||
|
{{ legendPositionTranslationMap.get(pos) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'legend.label' | translate }}</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<tb-font-settings formControlName="legendLabelFont" |
||||
|
previewText="Wind power"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="legendLabelColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'legend.value' | translate }}</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<tb-font-settings formControlName="legendValueFont" |
||||
|
[previewText]="valuePreviewFn"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="legendValueColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</mat-expansion-panel> |
||||
|
</div> |
||||
|
<div class="tb-form-panel tb-slide-toggle"> |
||||
|
<mat-expansion-panel class="tb-settings" [expanded]="polarAreaChartWidgetSettingsForm.get('showTooltip').value" [disabled]="!polarAreaChartWidgetSettingsForm.get('showTooltip').value"> |
||||
|
<mat-expansion-panel-header fxLayout="row wrap"> |
||||
|
<mat-panel-title> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="showTooltip" (click)="$event.stopPropagation()" |
||||
|
fxLayoutAlign="center"> |
||||
|
{{ 'widget-config.tooltip' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<ng-template matExpansionPanelContent> |
||||
|
<div class="tb-form-row space-between column-xs"> |
||||
|
<div>{{ 'tooltip.value' | translate }}</div> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic"> |
||||
|
<mat-select formControlName="tooltipValueType"> |
||||
|
<mat-option *ngFor="let type of latestChartTooltipValueTypes" [value]="type"> |
||||
|
{{ latestChartTooltipValueTypeTranslationMap.get(type) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="tooltipValueDecimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
<div matSuffix fxHide.lt-md translate>widget-config.decimals-suffix</div> |
||||
|
</mat-form-field> |
||||
|
<tb-font-settings formControlName="tooltipValueFont" |
||||
|
[previewText]="tooltipValuePreviewFn"> |
||||
|
</tb-font-settings> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="tooltipValueColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'tooltip.background-color' | translate }}</div> |
||||
|
<tb-color-input asBoxInput |
||||
|
colorClearButton |
||||
|
formControlName="tooltipBackgroundColor"> |
||||
|
</tb-color-input> |
||||
|
</div> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'tooltip.background-blur' | translate }}</div> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="tooltipBackgroundBlur" type="number" min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
<div matSuffix>px</div> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</mat-expansion-panel> |
||||
|
</div> |
||||
|
<tb-chart-animation-settings |
||||
|
formControlName="animation"> |
||||
|
</tb-chart-animation-settings> |
||||
|
<div class="tb-form-row space-between"> |
||||
|
<div>{{ 'widgets.background.background' | translate }}</div> |
||||
|
<tb-background-settings formControlName="background"> |
||||
|
</tb-background-settings> |
||||
|
</div> |
||||
|
</div> |
||||
|
</ng-container> |
||||
@ -0,0 +1,161 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 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 { |
||||
|
legendPositions, |
||||
|
legendPositionTranslationMap, |
||||
|
WidgetSettings, |
||||
|
WidgetSettingsComponent |
||||
|
} from '@shared/models/widget.models'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { AppState } from '@core/core.state'; |
||||
|
import { formatValue, mergeDeep } from '@core/utils'; |
||||
|
import { |
||||
|
LatestChartTooltipValueType, |
||||
|
latestChartTooltipValueTypes, |
||||
|
latestChartTooltipValueTypeTranslations |
||||
|
} from '@home/components/widget/lib/chart/latest-chart.models'; |
||||
|
import { |
||||
|
polarAreaChartWidgetDefaultSettings, |
||||
|
PolarAreaChartWidgetSettings |
||||
|
} from '@home/components/widget/lib/chart/polar-area-widget.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-polar-area-chart-widget-settings', |
||||
|
templateUrl: './polar-area-chart-widget-settings.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class PolarAreaChartWidgetSettingsComponent extends WidgetSettingsComponent { |
||||
|
|
||||
|
legendPositions = legendPositions; |
||||
|
|
||||
|
legendPositionTranslationMap = legendPositionTranslationMap; |
||||
|
|
||||
|
latestChartTooltipValueTypes = latestChartTooltipValueTypes; |
||||
|
|
||||
|
latestChartTooltipValueTypeTranslationMap = latestChartTooltipValueTypeTranslations; |
||||
|
|
||||
|
polarAreaChartWidgetSettingsForm: UntypedFormGroup; |
||||
|
|
||||
|
valuePreviewFn = this._valuePreviewFn.bind(this); |
||||
|
|
||||
|
tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private fb: UntypedFormBuilder) { |
||||
|
super(store); |
||||
|
} |
||||
|
|
||||
|
protected settingsForm(): UntypedFormGroup { |
||||
|
return this.polarAreaChartWidgetSettingsForm; |
||||
|
} |
||||
|
|
||||
|
protected defaultSettings(): WidgetSettings { |
||||
|
return mergeDeep<PolarAreaChartWidgetSettings>({} as PolarAreaChartWidgetSettings, polarAreaChartWidgetDefaultSettings); |
||||
|
} |
||||
|
|
||||
|
protected onSettingsSet(settings: WidgetSettings) { |
||||
|
this.polarAreaChartWidgetSettingsForm = this.fb.group({ |
||||
|
|
||||
|
sortSeries: [settings.sortSeries, []], |
||||
|
|
||||
|
barSettings: [settings.barSettings, []], |
||||
|
|
||||
|
axisMin: [settings.axisMin, []], |
||||
|
axisMax: [settings.axisMax, []], |
||||
|
axisTickLabelFont: [settings.axisTickLabelFont, []], |
||||
|
axisTickLabelColor: [settings.axisTickLabelColor, []], |
||||
|
angleAxisStartAngle: [settings.angleAxisStartAngle, [Validators.min(0), Validators.max(360)]], |
||||
|
|
||||
|
animation: [settings.animation, []], |
||||
|
|
||||
|
showLegend: [settings.showLegend, []], |
||||
|
legendPosition: [settings.legendPosition, []], |
||||
|
legendLabelFont: [settings.legendLabelFont, []], |
||||
|
legendLabelColor: [settings.legendLabelColor, []], |
||||
|
legendValueFont: [settings.legendValueFont, []], |
||||
|
legendValueColor: [settings.legendValueColor, []], |
||||
|
|
||||
|
showTooltip: [settings.showTooltip, []], |
||||
|
tooltipValueType: [settings.tooltipValueType, []], |
||||
|
tooltipValueDecimals: [settings.tooltipValueDecimals, []], |
||||
|
tooltipValueFont: [settings.tooltipValueFont, []], |
||||
|
tooltipValueColor: [settings.tooltipValueColor, []], |
||||
|
tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], |
||||
|
tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], |
||||
|
|
||||
|
background: [settings.background, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['showLegend', 'showTooltip']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
const showLegend: boolean = this.polarAreaChartWidgetSettingsForm.get('showLegend').value; |
||||
|
const showTooltip: boolean = this.polarAreaChartWidgetSettingsForm.get('showTooltip').value; |
||||
|
|
||||
|
if (showLegend) { |
||||
|
this.polarAreaChartWidgetSettingsForm.get('legendPosition').enable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('legendLabelFont').enable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('legendLabelColor').enable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('legendValueFont').enable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('legendValueColor').enable(); |
||||
|
} else { |
||||
|
this.polarAreaChartWidgetSettingsForm.get('legendPosition').disable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('legendLabelFont').disable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('legendLabelColor').disable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('legendValueFont').disable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('legendValueColor').disable(); |
||||
|
} |
||||
|
if (showTooltip) { |
||||
|
this.polarAreaChartWidgetSettingsForm.get('tooltipValueType').enable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('tooltipValueDecimals').enable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('tooltipValueFont').enable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('tooltipValueColor').enable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('tooltipBackgroundColor').enable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('tooltipBackgroundBlur').enable(); |
||||
|
} else { |
||||
|
this.polarAreaChartWidgetSettingsForm.get('tooltipValueType').disable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('tooltipValueDecimals').disable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('tooltipValueFont').disable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('tooltipValueColor').disable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('tooltipBackgroundColor').disable(); |
||||
|
this.polarAreaChartWidgetSettingsForm.get('tooltipBackgroundBlur').disable(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private _valuePreviewFn(): string { |
||||
|
const units: string = this.widgetConfig.config.units; |
||||
|
const decimals: number = this.widgetConfig.config.decimals; |
||||
|
return formatValue(110, decimals, units, false); |
||||
|
} |
||||
|
|
||||
|
private _tooltipValuePreviewFn(): string { |
||||
|
const tooltipValueType: LatestChartTooltipValueType = this.polarAreaChartWidgetSettingsForm.get('tooltipValueType').value; |
||||
|
const decimals: number = this.polarAreaChartWidgetSettingsForm.get('tooltipValueDecimals').value; |
||||
|
if (tooltipValueType === LatestChartTooltipValueType.percentage) { |
||||
|
return formatValue(35, decimals, '%', false); |
||||
|
} else { |
||||
|
const units: string = this.widgetConfig.config.units; |
||||
|
return formatValue(110, decimals, units, false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue