Browse Source

UI: Implement time-series chart key settings form.

pull/10315/head
Igor Kulikov 2 years ago
parent
commit
fc06a77943
  1. 55
      ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts
  2. 3
      ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts
  3. 65
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.html
  4. 148
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.ts
  5. 53
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.html
  6. 143
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.ts
  7. 14
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.html
  8. 26
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.ts
  9. 111
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html
  10. 187
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts
  11. 19
      ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts
  12. 52
      ui-ngx/src/assets/locale/locale.constant-en_US.json

55
ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts

@ -96,12 +96,38 @@ export enum TimeSeriesChartShape {
none = 'none'
}
export const timeSeriesChartShapes = Object.keys(TimeSeriesChartShape) as TimeSeriesChartShape[];
export const timeSeriesChartShapeTranslations = new Map<TimeSeriesChartShape, string>(
[
[TimeSeriesChartShape.emptyCircle, 'widgets.time-series-chart.shape-empty-circle'],
[TimeSeriesChartShape.circle, 'widgets.time-series-chart.shape-circle'],
[TimeSeriesChartShape.rect, 'widgets.time-series-chart.shape-rect'],
[TimeSeriesChartShape.roundRect, 'widgets.time-series-chart.shape-round-rect'],
[TimeSeriesChartShape.triangle, 'widgets.time-series-chart.shape-triangle'],
[TimeSeriesChartShape.diamond, 'widgets.time-series-chart.shape-diamond'],
[TimeSeriesChartShape.pin, 'widgets.time-series-chart.shape-pin'],
[TimeSeriesChartShape.arrow, 'widgets.time-series-chart.shape-arrow'],
[TimeSeriesChartShape.none, 'widgets.time-series-chart.shape-none']
]
);
export enum TimeSeriesChartLineType {
solid = 'solid',
dashed = 'dashed',
dotted = 'dotted'
}
export const timeSeriesLineTypes = Object.keys(TimeSeriesChartLineType) as TimeSeriesChartLineType[];
export const timeSeriesLineTypeTranslations = new Map<TimeSeriesChartLineType, string>(
[
[TimeSeriesChartLineType.solid, 'widgets.time-series-chart.line-type-solid'],
[TimeSeriesChartLineType.dashed, 'widgets.time-series-chart.line-type-dashed'],
[TimeSeriesChartLineType.dotted, 'widgets.time-series-chart.line-type-dotted']
]
);
export enum ThresholdLabelPosition {
start = 'start',
middle = 'middle',
@ -129,17 +155,46 @@ export enum SeriesFillType {
gradient = 'gradient'
}
export const seriesFillTypes = Object.keys(SeriesFillType) as SeriesFillType[];
export const seriesFillTypeTranslations = new Map<SeriesFillType, string>(
[
[SeriesFillType.none, 'widgets.time-series-chart.series.fill-type-none'],
[SeriesFillType.opacity, 'widgets.time-series-chart.series.fill-type-opacity'],
[SeriesFillType.gradient, 'widgets.time-series-chart.series.fill-type-gradient']
]
);
export enum SeriesLabelPosition {
top = 'top',
bottom = 'bottom'
}
export const seriesLabelPositions = Object.keys(SeriesLabelPosition) as SeriesLabelPosition[];
export const seriesLabelPositionTranslations = new Map<SeriesLabelPosition, string>(
[
[SeriesLabelPosition.top, 'widgets.time-series-chart.series.label-position-top'],
[SeriesLabelPosition.bottom, 'widgets.time-series-chart.series.label-position-bottom']
]
);
export enum LineSeriesStepType {
start = 'start',
middle = 'middle',
end = 'end'
}
export const lineSeriesStepTypes = Object.keys(LineSeriesStepType) as LineSeriesStepType[];
export const lineSeriesStepTypeTranslations = new Map<LineSeriesStepType, string>(
[
[LineSeriesStepType.start, 'widgets.time-series-chart.series.line.step-type-start'],
[LineSeriesStepType.middle, 'widgets.time-series-chart.series.line.step-type-middle'],
[LineSeriesStepType.end, 'widgets.time-series-chart.series.line.step-type-end']
]
);
export enum TimeSeriesChartSeriesType {
line = 'line',
bar = 'bar'

3
ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts

@ -259,8 +259,7 @@ export class TbTimeSeriesChart {
timeSeriesChartKeyDefaultSettings, dataKey.settings);
if ((keySettings.type === TimeSeriesChartSeriesType.line && keySettings.lineSettings.showPointLabel &&
keySettings.lineSettings.pointLabelPosition === SeriesLabelPosition.top) ||
(keySettings.type === TimeSeriesChartSeriesType.bar && keySettings.barSettings.showLabel &&
keySettings.barSettings.labelPosition === SeriesLabelPosition.top)) {
(keySettings.type === TimeSeriesChartSeriesType.bar && keySettings.barSettings.showLabel)) {
this.topPointLabels = true;
}
dataKey.settings = keySettings;

65
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.html

@ -0,0 +1,65 @@
<!--
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]="barSettingsFormGroup">
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="showBorder">
{{ 'widgets.time-series-chart.series.bar.show-border' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-row space-between column-xs">
<div translate>widgets.time-series-chart.series.bar.border-width</div>
<mat-form-field class="medium-width number" appearance="outline" subscriptSizing="dynamic">
<input matInput type="number" formControlName="borderWidth" min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}"/>
</mat-form-field>
</div>
<div class="tb-form-row space-between column-xs">
<div translate>widgets.time-series-chart.series.bar.border-radius</div>
<mat-form-field class="medium-width number" appearance="outline" subscriptSizing="dynamic">
<input matInput type="number" formControlName="borderRadius" min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}"/>
</mat-form-field>
</div>
<div class="tb-form-row space-between column-lt-md">
<mat-slide-toggle class="mat-slide" formControlName="showLabel">
<div tb-hint-tooltip-icon="{{'widgets.time-series-chart.series.bar.label-hint' | translate}}">
{{ 'widgets.time-series-chart.series.bar.label' | translate }}
</div>
</mat-slide-toggle>
<div fxLayout="row" fxFlex.lt-md fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field class="medium-width" fxFlex.lt-md appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="labelPosition">
<mat-option *ngFor="let position of seriesLabelPositions" [value]="position">
{{ seriesLabelPositionTranslations.get(position) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<tb-font-settings formControlName="labelFont"
clearButton
[previewText]="labelPreviewFn">
</tb-font-settings>
<tb-color-input asBoxInput
colorClearButton
formControlName="labelColor">
</tb-color-input>
</div>
</div>
<tb-time-series-chart-fill-settings
formControlName="backgroundSettings"
title="widgets.time-series-chart.series.background"
fillNoneTitle="widgets.time-series-chart.series.fill-type-solid">
</tb-time-series-chart-fill-settings>
</ng-container>

148
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.ts

@ -0,0 +1,148 @@
///
/// 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, forwardRef, Input, OnInit } from '@angular/core';
import {
ControlValueAccessor,
NG_VALUE_ACCESSOR,
UntypedFormBuilder,
UntypedFormGroup,
Validators
} from '@angular/forms';
import {
BarSeriesSettings,
seriesLabelPositions,
seriesLabelPositionTranslations
} from '@home/components/widget/lib/chart/time-series-chart.models';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { merge } from 'rxjs';
import { formatValue, isDefinedAndNotNull } from '@core/utils';
import { DataKeyConfigComponent } from '@home/components/widget/config/data-key-config.component';
@Component({
selector: 'tb-time-series-chart-bar-settings',
templateUrl: './time-series-chart-bar-settings.component.html',
styleUrls: ['./../widget-settings.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TimeSeriesChartBarSettingsComponent),
multi: true
}
]
})
export class TimeSeriesChartBarSettingsComponent implements OnInit, ControlValueAccessor {
seriesLabelPositions = seriesLabelPositions;
seriesLabelPositionTranslations = seriesLabelPositionTranslations;
labelPreviewFn = this._labelPreviewFn.bind(this);
@Input()
disabled: boolean;
private modelValue: BarSeriesSettings;
private propagateChange = null;
public barSettingsFormGroup: UntypedFormGroup;
constructor(protected store: Store<AppState>,
private dataKeyConfigComponent: DataKeyConfigComponent,
private fb: UntypedFormBuilder) {
}
ngOnInit(): void {
this.barSettingsFormGroup = this.fb.group({
showBorder: [null, []],
borderWidth: [null, [Validators.min(0)]],
borderRadius: [null, [Validators.min(0)]],
showLabel: [null, []],
labelPosition: [null, []],
labelFont: [null, []],
labelColor: [null, []],
backgroundSettings: [null, []]
});
this.barSettingsFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
merge(this.barSettingsFormGroup.get('showBorder').valueChanges,
this.barSettingsFormGroup.get('showLabel').valueChanges)
.subscribe(() => {
this.updateValidators();
});
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(_fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (isDisabled) {
this.barSettingsFormGroup.disable({emitEvent: false});
} else {
this.barSettingsFormGroup.enable({emitEvent: false});
this.updateValidators();
}
}
writeValue(value: BarSeriesSettings): void {
this.modelValue = value;
this.barSettingsFormGroup.patchValue(
value, {emitEvent: false}
);
this.updateValidators();
}
private updateValidators() {
const showBorder: boolean = this.barSettingsFormGroup.get('showBorder').value;
const showLabel: boolean = this.barSettingsFormGroup.get('showLabel').value;
if (showBorder) {
this.barSettingsFormGroup.get('borderWidth').enable({emitEvent: false});
} else {
this.barSettingsFormGroup.get('borderWidth').disable({emitEvent: false});
}
if (showLabel) {
this.barSettingsFormGroup.get('labelPosition').enable({emitEvent: false});
this.barSettingsFormGroup.get('labelFont').enable({emitEvent: false});
this.barSettingsFormGroup.get('labelColor').enable({emitEvent: false});
} else {
this.barSettingsFormGroup.get('labelPosition').disable({emitEvent: false});
this.barSettingsFormGroup.get('labelFont').disable({emitEvent: false});
this.barSettingsFormGroup.get('labelColor').disable({emitEvent: false});
}
}
private updateModel() {
this.modelValue = this.barSettingsFormGroup.getRawValue();
this.propagateChange(this.modelValue);
}
private _labelPreviewFn(): string {
const dataKey = this.dataKeyConfigComponent.modelValue;
const widgetConfig = this.dataKeyConfigComponent.widgetConfig;
const units = dataKey.units && dataKey.units.length ? dataKey.units : widgetConfig.config.units;
const decimals = isDefinedAndNotNull(dataKey.decimals) ? dataKey.decimals :
(isDefinedAndNotNull(widgetConfig.config.decimals) ? widgetConfig.config.decimals : 2);
return formatValue(22, decimals, units, false);
}
}

53
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.html

@ -0,0 +1,53 @@
<!--
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]="fillSettingsFormGroup">
<div class="tb-form-row column">
<div class="tb-form-row no-border no-padding space-between">
<div>{{ title | translate }}</div>
<tb-toggle-select formControlName="type">
<tb-toggle-option *ngFor="let type of seriesFillTypes" [value]="type">{{ seriesFillTypeTranslationMap.get(type) | translate }}</tb-toggle-option>
</tb-toggle-select>
</div>
<ng-container *ngIf="fillSettingsFormGroup.get('type').value === SeriesFillType.opacity">
<div class="tb-form-row space-between column-xs">
<div translate>widgets.time-series-chart.series.opacity</div>
<mat-form-field class="medium-width number" appearance="outline" subscriptSizing="dynamic">
<input matInput type="number" formControlName="opacity" min="0" max="100"
step="1" placeholder="{{ 'widget-config.set' | translate }}"/>
</mat-form-field>
</div>
</ng-container>
<ng-container *ngIf="fillSettingsFormGroup.get('type').value === SeriesFillType.gradient" formGroupName="gradient">
<div class="tb-form-row space-between column-xs">
<div translate>widgets.time-series-chart.series.gradient-stops</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<div class="tb-small-label" translate>widgets.time-series-chart.series.gradient-start</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="start" min="0" max="100" step="1"
type="number" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<div class="tb-small-label" translate>widgets.time-series-chart.series.gradient-end</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="end" min="0" max="100" step="1"
type="number" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
</div>
</ng-container>
</div>
</ng-container>

143
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.ts

@ -0,0 +1,143 @@
///
/// 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, forwardRef, Input, OnInit } from '@angular/core';
import {
ControlValueAccessor,
NG_VALUE_ACCESSOR,
UntypedFormBuilder,
UntypedFormGroup,
Validators
} from '@angular/forms';
import {
SeriesFillSettings,
SeriesFillType,
seriesFillTypes,
seriesFillTypeTranslations
} from '@home/components/widget/lib/chart/time-series-chart.models';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-time-series-chart-fill-settings',
templateUrl: './time-series-chart-fill-settings.component.html',
styleUrls: ['./../widget-settings.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TimeSeriesChartFillSettingsComponent),
multi: true
}
]
})
export class TimeSeriesChartFillSettingsComponent implements OnInit, ControlValueAccessor {
seriesFillTypes = seriesFillTypes;
seriesFillTypeTranslationMap: Map<SeriesFillType, string> = new Map<SeriesFillType, string>([]);
SeriesFillType = SeriesFillType;
@Input()
disabled: boolean;
@Input()
title = 'widgets.time-series-chart.series.fill';
@Input()
fillNoneTitle = 'widgets.time-series-chart.series.fill-type-none';
private modelValue: SeriesFillSettings;
private propagateChange = null;
public fillSettingsFormGroup: UntypedFormGroup;
constructor(protected store: Store<AppState>,
private fb: UntypedFormBuilder) {
}
ngOnInit(): void {
this.fillSettingsFormGroup = this.fb.group({
type: [null, []],
opacity: [null, [Validators.min(0), Validators.max(100)]],
gradient: this.fb.group({
start: [null, [Validators.min(0), Validators.max(100)]],
end: [null, [Validators.min(0), Validators.max(100)]]
})
});
this.fillSettingsFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
this.fillSettingsFormGroup.get('type').valueChanges.subscribe(() => {
this.updateValidators();
});
for (const type of seriesFillTypes) {
let translation: string;
if (type === SeriesFillType.none) {
translation = this.fillNoneTitle;
} else {
translation = seriesFillTypeTranslations.get(type);
}
this.seriesFillTypeTranslationMap.set(type, translation);
}
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(_fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (isDisabled) {
this.fillSettingsFormGroup.disable({emitEvent: false});
} else {
this.fillSettingsFormGroup.enable({emitEvent: false});
this.updateValidators();
}
}
writeValue(value: SeriesFillSettings): void {
this.modelValue = value;
this.fillSettingsFormGroup.patchValue(
value, {emitEvent: false}
);
this.updateValidators();
}
private updateValidators() {
const type: SeriesFillType = this.fillSettingsFormGroup.get('type').value;
if (type === SeriesFillType.none) {
this.fillSettingsFormGroup.get('opacity').disable({emitEvent: false});
this.fillSettingsFormGroup.get('gradient').disable({emitEvent: false});
} else if (type === SeriesFillType.opacity) {
this.fillSettingsFormGroup.get('opacity').enable({emitEvent: false});
this.fillSettingsFormGroup.get('gradient').disable({emitEvent: false});
} else if (type === SeriesFillType.gradient) {
this.fillSettingsFormGroup.get('opacity').disable({emitEvent: false});
this.fillSettingsFormGroup.get('gradient').enable({emitEvent: false});
}
}
private updateModel() {
const value: SeriesFillSettings = this.fillSettingsFormGroup.getRawValue();
this.modelValue = value;
this.propagateChange(this.modelValue);
}
}

14
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.html

@ -30,7 +30,17 @@
</mat-slide-toggle>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.time-series-chart.series.series-type</div>
TODO:
<div fxLayout="row" fxLayoutAlign="space-between center">
<div class="tb-form-panel-title" translate>widgets.time-series-chart.series.series-type</div>
<tb-toggle-select formControlName="type">
<tb-toggle-option *ngFor="let type of timeSeriesChartSeriesTypes" [value]="type">{{ timeSeriesChartSeriesTypeTranslations.get(type) | translate }}</tb-toggle-option>
</tb-toggle-select>
</div>
<tb-time-series-chart-line-settings [fxShow]="timeSeriesChartKeySettingsForm.get('type').value === TimeSeriesChartSeriesType.line"
formControlName="lineSettings">
</tb-time-series-chart-line-settings>
<tb-time-series-chart-bar-settings [fxShow]="timeSeriesChartKeySettingsForm.get('type').value === TimeSeriesChartSeriesType.bar"
formControlName="barSettings">
</tb-time-series-chart-bar-settings>
</div>
</ng-container>

26
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.ts

@ -22,7 +22,10 @@ import { AppState } from '@core/core.state';
import { mergeDeep } from '@core/utils';
import {
timeSeriesChartKeyDefaultSettings,
TimeSeriesChartKeySettings
TimeSeriesChartKeySettings,
TimeSeriesChartSeriesType,
timeSeriesChartSeriesTypes,
timeSeriesChartSeriesTypeTranslations
} from '@home/components/widget/lib/chart/time-series-chart.models';
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
@ -33,6 +36,12 @@ import { WidgetConfigComponentData } from '@home/models/widget-component.models'
})
export class TimeSeriesChartKeySettingsComponent extends WidgetSettingsComponent {
TimeSeriesChartSeriesType = TimeSeriesChartSeriesType;
timeSeriesChartSeriesTypes = timeSeriesChartSeriesTypes;
timeSeriesChartSeriesTypeTranslations = timeSeriesChartSeriesTypeTranslations;
timeSeriesChartKeySettingsForm: UntypedFormGroup;
constructor(protected store: Store<AppState>,
@ -60,23 +69,30 @@ export class TimeSeriesChartKeySettingsComponent extends WidgetSettingsComponent
showInLegend: [seriesSettings.showInLegend, []],
dataHiddenByDefault: [seriesSettings.dataHiddenByDefault, []],
type: [seriesSettings.type, []],
lineSettings: [settings.lineSettings, []],
barSettings: [settings.barSettings, []]
lineSettings: [seriesSettings.lineSettings, []],
barSettings: [seriesSettings.barSettings, []]
});
}
protected validatorTriggers(): string[] {
return ['showInLegend'];
return ['showInLegend', 'type'];
}
protected updateValidators(_emitEvent: boolean) {
const showInLegend: boolean = this.timeSeriesChartKeySettingsForm.get('showInLegend').value;
const type: TimeSeriesChartSeriesType = this.timeSeriesChartKeySettingsForm.get('type').value;
if (showInLegend) {
this.timeSeriesChartKeySettingsForm.get('dataHiddenByDefault').enable();
} else {
this.timeSeriesChartKeySettingsForm.get('dataHiddenByDefault').patchValue(false, {emitEvent: false});
this.timeSeriesChartKeySettingsForm.get('dataHiddenByDefault').disable();
}
if (type === TimeSeriesChartSeriesType.line) {
this.timeSeriesChartKeySettingsForm.get('lineSettings').enable();
this.timeSeriesChartKeySettingsForm.get('barSettings').disable();
} else if (type === TimeSeriesChartSeriesType.bar) {
this.timeSeriesChartKeySettingsForm.get('lineSettings').disable();
this.timeSeriesChartKeySettingsForm.get('barSettings').enable();
}
}
}

111
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html

@ -0,0 +1,111 @@
<!--
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]="lineSettingsFormGroup">
<div class="tb-form-panel stroked">
<div class="tb-form-panel-title" translate>widgets.time-series-chart.series.line.line</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="showLine">
{{ 'widgets.time-series-chart.series.line.show-line' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-row space-between column-xs">
<mat-slide-toggle class="mat-slide" formControlName="step">
{{ 'widgets.time-series-chart.series.line.step-line' | translate }}
</mat-slide-toggle>
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="stepType">
<mat-option *ngFor="let stepType of lineSeriesStepTypes" [value]="stepType">
{{ lineSeriesStepTypeTranslations.get(stepType) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="smooth">
{{ 'widgets.time-series-chart.series.line.smooth-line' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-row space-between column-xs">
<div translate>widgets.time-series-chart.line-type</div>
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="lineType">
<mat-option *ngFor="let lineType of timeSeriesLineTypes" [value]="lineType">
{{ timeSeriesLineTypeTranslations.get(lineType) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="tb-form-row space-between column-xs">
<div translate>widgets.time-series-chart.line-width</div>
<mat-form-field class="medium-width number" appearance="outline" subscriptSizing="dynamic">
<input matInput type="number" formControlName="lineWidth" min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}"/>
</mat-form-field>
</div>
</div>
<div class="tb-form-panel stroked">
<div class="tb-form-panel-title" translate>widgets.time-series-chart.series.point.points</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="showPoints">
{{ 'widgets.time-series-chart.series.point.show-points' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-row space-between column-lt-md">
<mat-slide-toggle class="mat-slide" formControlName="showPointLabel">
<div tb-hint-tooltip-icon="{{'widgets.time-series-chart.series.point.point-label-hint' | translate}}">
{{ 'widgets.time-series-chart.series.point.point-label' | translate }}
</div>
</mat-slide-toggle>
<div fxLayout="row" fxFlex.lt-md fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field class="medium-width" fxFlex.lt-md appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="pointLabelPosition">
<mat-option *ngFor="let position of seriesLabelPositions" [value]="position">
{{ seriesLabelPositionTranslations.get(position) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<tb-font-settings formControlName="pointLabelFont"
clearButton
[previewText]="pointLabelPreviewFn">
</tb-font-settings>
<tb-color-input asBoxInput
colorClearButton
formControlName="pointLabelColor">
</tb-color-input>
</div>
</div>
<div class="tb-form-row space-between column-xs">
<div translate>widgets.time-series-chart.series.point.point-shape</div>
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="pointShape">
<mat-option *ngFor="let shape of timeSeriesChartShapes" [value]="shape">
{{ timeSeriesChartShapeTranslations.get(shape) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="tb-form-row space-between column-xs">
<div translate>widgets.time-series-chart.series.point.point-size</div>
<mat-form-field class="medium-width number" appearance="outline" subscriptSizing="dynamic">
<input matInput type="number" formControlName="pointSize" min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}"/>
</mat-form-field>
</div>
</div>
<tb-time-series-chart-fill-settings
formControlName="fillAreaSettings">
</tb-time-series-chart-fill-settings>
</ng-container>

187
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts

@ -0,0 +1,187 @@
///
/// 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, forwardRef, Input, OnInit } from '@angular/core';
import {
ControlValueAccessor,
NG_VALUE_ACCESSOR,
UntypedFormBuilder,
UntypedFormGroup,
Validators
} from '@angular/forms';
import {
LineSeriesSettings,
lineSeriesStepTypes,
lineSeriesStepTypeTranslations,
seriesLabelPositions,
seriesLabelPositionTranslations,
timeSeriesChartShapes,
timeSeriesChartShapeTranslations,
timeSeriesLineTypes,
timeSeriesLineTypeTranslations
} from '@home/components/widget/lib/chart/time-series-chart.models';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { merge } from 'rxjs';
import { formatValue, isDefinedAndNotNull } from '@core/utils';
import { DataKeyConfigComponent } from '@home/components/widget/config/data-key-config.component';
@Component({
selector: 'tb-time-series-chart-line-settings',
templateUrl: './time-series-chart-line-settings.component.html',
styleUrls: ['./../widget-settings.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TimeSeriesChartLineSettingsComponent),
multi: true
}
]
})
export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValueAccessor {
lineSeriesStepTypes = lineSeriesStepTypes;
lineSeriesStepTypeTranslations = lineSeriesStepTypeTranslations;
timeSeriesLineTypes = timeSeriesLineTypes;
timeSeriesLineTypeTranslations = timeSeriesLineTypeTranslations;
seriesLabelPositions = seriesLabelPositions;
seriesLabelPositionTranslations = seriesLabelPositionTranslations;
timeSeriesChartShapes = timeSeriesChartShapes;
timeSeriesChartShapeTranslations = timeSeriesChartShapeTranslations;
pointLabelPreviewFn = this._pointLabelPreviewFn.bind(this);
@Input()
disabled: boolean;
private modelValue: LineSeriesSettings;
private propagateChange = null;
public lineSettingsFormGroup: UntypedFormGroup;
constructor(protected store: Store<AppState>,
private dataKeyConfigComponent: DataKeyConfigComponent,
private fb: UntypedFormBuilder) {
}
ngOnInit(): void {
this.lineSettingsFormGroup = this.fb.group({
showLine: [null, []],
step: [null, []],
stepType: [null, []],
smooth: [null, []],
lineType: [null, []],
lineWidth: [null, [Validators.min(0)]],
showPoints: [null, []],
showPointLabel: [null, []],
pointLabelPosition: [null, []],
pointLabelFont: [null, []],
pointLabelColor: [null, []],
pointShape: [null, []],
pointSize: [null, [Validators.min(0)]],
fillAreaSettings: [null, []]
});
this.lineSettingsFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
merge(this.lineSettingsFormGroup.get('showLine').valueChanges,
this.lineSettingsFormGroup.get('step').valueChanges,
this.lineSettingsFormGroup.get('showPointLabel').valueChanges)
.subscribe(() => {
this.updateValidators();
});
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(_fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (isDisabled) {
this.lineSettingsFormGroup.disable({emitEvent: false});
} else {
this.lineSettingsFormGroup.enable({emitEvent: false});
this.updateValidators();
}
}
writeValue(value: LineSeriesSettings): void {
this.modelValue = value;
this.lineSettingsFormGroup.patchValue(
value, {emitEvent: false}
);
this.updateValidators();
}
private updateValidators() {
const showLine: boolean = this.lineSettingsFormGroup.get('showLine').value;
const step: boolean = this.lineSettingsFormGroup.get('step').value;
const showPointLabel: boolean = this.lineSettingsFormGroup.get('showPointLabel').value;
if (showLine) {
this.lineSettingsFormGroup.get('step').enable({emitEvent: false});
if (step) {
this.lineSettingsFormGroup.get('stepType').enable({emitEvent: false});
this.lineSettingsFormGroup.get('smooth').disable({emitEvent: false});
} else {
this.lineSettingsFormGroup.get('stepType').disable({emitEvent: false});
this.lineSettingsFormGroup.get('smooth').enable({emitEvent: false});
}
this.lineSettingsFormGroup.get('lineType').enable({emitEvent: false});
this.lineSettingsFormGroup.get('lineWidth').enable({emitEvent: false});
} else {
this.lineSettingsFormGroup.get('step').disable({emitEvent: false});
this.lineSettingsFormGroup.get('stepType').disable({emitEvent: false});
this.lineSettingsFormGroup.get('smooth').disable({emitEvent: false});
this.lineSettingsFormGroup.get('lineType').disable({emitEvent: false});
this.lineSettingsFormGroup.get('lineWidth').disable({emitEvent: false});
}
if (showPointLabel) {
this.lineSettingsFormGroup.get('pointLabelPosition').enable({emitEvent: false});
this.lineSettingsFormGroup.get('pointLabelFont').enable({emitEvent: false});
this.lineSettingsFormGroup.get('pointLabelColor').enable({emitEvent: false});
} else {
this.lineSettingsFormGroup.get('pointLabelPosition').disable({emitEvent: false});
this.lineSettingsFormGroup.get('pointLabelFont').disable({emitEvent: false});
this.lineSettingsFormGroup.get('pointLabelColor').disable({emitEvent: false});
}
}
private updateModel() {
this.modelValue = this.lineSettingsFormGroup.getRawValue();
this.propagateChange(this.modelValue);
}
private _pointLabelPreviewFn(): string {
const dataKey = this.dataKeyConfigComponent.modelValue;
const widgetConfig = this.dataKeyConfigComponent.widgetConfig;
const units = dataKey.units && dataKey.units.length ? dataKey.units : widgetConfig.config.units;
const decimals = isDefinedAndNotNull(dataKey.decimals) ? dataKey.decimals :
(isDefinedAndNotNull(widgetConfig.config.decimals) ? widgetConfig.config.decimals : 2);
return formatValue(22, decimals, units, false);
}
}

19
ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts

@ -333,6 +333,15 @@ import {
import {
TimeSeriesChartKeySettingsComponent
} from '@home/components/widget/lib/settings/chart/time-series-chart-key-settings.component';
import {
TimeSeriesChartLineSettingsComponent
} from '@home/components/widget/lib/settings/chart/time-series-chart-line-settings.component';
import {
TimeSeriesChartFillSettingsComponent
} from '@home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component';
import {
TimeSeriesChartBarSettingsComponent
} from '@home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component';
@NgModule({
declarations: [
@ -452,7 +461,10 @@ import {
PowerButtonWidgetSettingsComponent,
SliderWidgetSettingsComponent,
ToggleButtonWidgetSettingsComponent,
TimeSeriesChartKeySettingsComponent
TimeSeriesChartKeySettingsComponent,
TimeSeriesChartLineSettingsComponent,
TimeSeriesChartBarSettingsComponent,
TimeSeriesChartFillSettingsComponent
],
imports: [
CommonModule,
@ -577,7 +589,10 @@ import {
PowerButtonWidgetSettingsComponent,
SliderWidgetSettingsComponent,
ToggleButtonWidgetSettingsComponent,
TimeSeriesChartKeySettingsComponent
TimeSeriesChartKeySettingsComponent,
TimeSeriesChartLineSettingsComponent,
TimeSeriesChartBarSettingsComponent,
TimeSeriesChartFillSettingsComponent
]
})
export class WidgetSettingsModule {

52
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -6601,6 +6601,20 @@
"stack-mode": "Stack mode",
"stack-mode-hint": "Stacks series on the chart. The series with the same unit would be put on top of each other.",
"axes": "Axes",
"line-type": "Line type",
"line-type-solid": "Solid",
"line-type-dashed": "Dashed",
"line-type-dotted": "Dotted",
"line-width": "Line width",
"shape-empty-circle": "Empty circle",
"shape-circle": "Circle",
"shape-rect": "Rectangle",
"shape-round-rect": "Rounded rectangle",
"shape-triangle": "Triangle",
"shape-diamond": "Diamond",
"shape-pin": "Pin",
"shape-arrow": "Arrow",
"shape-none": "None",
"series": {
"legend-settings": "Legend settings",
"show-in-legend": "Show in legend",
@ -6610,7 +6624,43 @@
"series-type": "Series type",
"type": "Type",
"type-line": "Line",
"type-bar": "Bar"
"type-bar": "Bar",
"label-position-top": "Top",
"label-position-bottom": "Bottom",
"fill": "Fill",
"background": "Background",
"fill-type-none": "None",
"fill-type-solid": "Solid",
"fill-type-opacity": "Opacity",
"fill-type-gradient": "Gradient",
"opacity": "Opacity",
"gradient-stops": "Gradient stops",
"gradient-start": "start",
"gradient-end": "end",
"line": {
"line": "Line",
"show-line": "Show line",
"step-line": "Step line",
"step-type-start": "Start",
"step-type-middle": "Middle",
"step-type-end": "End",
"smooth-line": "Smooth line"
},
"point": {
"points": "Points",
"show-points": "Show points",
"point-label": "Point label",
"point-label-hint": "Display label with value over the series point.",
"point-shape": "Point shape",
"point-size": "Point size"
},
"bar": {
"show-border": "Show border",
"border-width": "Border width",
"border-radius": "Border radius",
"label": "Label",
"label-hint": "Display label with value over the bar."
}
}
},
"wind-speed-direction": {

Loading…
Cancel
Save