1;
}
@@ -256,7 +264,8 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC
}
addKey() {
- const dataKey = this.callbacks.generateDataKey('', null, this.datakeySettingsSchema);
+ const dataKey = this.callbacks.generateDataKey('', null, this.datakeySettingsSchema,
+ false, this.dataKeySettingsFunction);
dataKey.label = '';
dataKey.decimals = 0;
if (this.hasAdditionalLatestDataKeys) {
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.html
index fe6c347649..37c85e173d 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.html
@@ -197,6 +197,7 @@
[dashboard]="dashboard"
[aliasController]="aliasController"
[widget]="widget"
+ [widgetConfig]="widgetConfig"
formControlName="settings">
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts
index b120a60809..9ebe8821cf 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts
@@ -40,7 +40,7 @@ import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog';
import { EntityService } from '@core/http/entity.service';
-import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models';
+import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { Observable, of } from 'rxjs';
import { map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators';
@@ -51,8 +51,10 @@ import { WidgetService } from '@core/http/widget.service';
import { Dashboard } from '@shared/models/dashboard.models';
import { IAliasController } from '@core/api/widget-api.models';
import { aggregationTranslations, AggregationType, ComparisonDuration } from '@shared/models/time/time.models';
-import { genNextLabel } from '@core/utils';
+import { genNextLabel, isDefinedAndNotNull } from '@core/utils';
import { coerceBoolean } from '@shared/decorators/coercion';
+import { WidgetConfigComponentData } from '@home/models/widget-component.models';
+import { WidgetComponentService } from '@home/components/widget/widget-component.service';
@Component({
selector: 'tb-data-key-config',
@@ -155,6 +157,8 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
modelValue: DataKey;
+ widgetConfig: WidgetConfigComponentData;
+
private propagateChange = null;
public dataKeyFormGroup: UntypedFormGroup;
@@ -180,12 +184,31 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
private dialog: MatDialog,
private translate: TranslateService,
private widgetService: WidgetService,
+ private widgetComponentService: WidgetComponentService,
private fb: UntypedFormBuilder) {
super(store);
this.functionScopeVariables = this.widgetService.getWidgetScopeVariables();
}
ngOnInit(): void {
+
+ const widgetInfo = this.widgetComponentService.getInstantWidgetInfo(this.widget);
+ const typeParameters = widgetInfo.typeParameters;
+ const dataKeySettingsFunction: DataKeySettingsFunction = typeParameters?.dataKeySettingsFunction;
+
+ this.widgetConfig = {
+ widgetName: widgetInfo.widgetName,
+ config: this.widget.config,
+ widgetType: this.widget.type,
+ typeParameters,
+ dataKeySettingsFunction,
+ settingsDirective: widgetInfo.settingsDirective,
+ dataKeySettingsDirective: widgetInfo.dataKeySettingsDirective,
+ latestDataKeySettingsDirective: widgetInfo.latestDataKeySettingsDirective,
+ hasBasicMode: isDefinedAndNotNull(widgetInfo.hasBasicMode) ? widgetInfo.hasBasicMode : false,
+ basicModeDirective: widgetInfo.basicModeDirective
+ } as WidgetConfigComponentData;
+
this.alarmKeys = [];
for (const name of Object.keys(alarmFields)) {
this.alarmKeys.push({
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts
index 893631f57b..b0e80973f0 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts
@@ -18,8 +18,11 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { DataKey, JsonSettingsSchema } from '@shared/models/widget.models';
import { Observable } from 'rxjs';
+export type DataKeySettingsFunction = (key: DataKey, isLatestDataKey: boolean) => any;
+
export interface DataKeysCallbacks {
- generateDataKey: (chip: any, type: DataKeyType, datakeySettingsSchema: JsonSettingsSchema) => DataKey;
+ generateDataKey: (chip: any, type: DataKeyType, datakeySettingsSchema: JsonSettingsSchema,
+ isLatestDataKey: boolean, dataKeySettingsFunction: DataKeySettingsFunction) => DataKey;
fetchEntityKeys: (entityAliasId: string, types: Array
) => Observable>;
fetchEntityKeysForDevice: (deviceId: string, types: Array) => Observable>;
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts
index 800c831b67..8c2df629de 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts
@@ -52,7 +52,7 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { DataKey, DatasourceType, JsonSettingsSchema, Widget, widgetType } from '@shared/models/widget.models';
import { IAliasController } from '@core/api/widget-api.models';
-import { DataKeysCallbacks } from './data-keys.component.models';
+import { DataKeysCallbacks, DataKeySettingsFunction } from './data-keys.component.models';
import { alarmFields } from '@shared/models/alarm.models';
import { UtilsService } from '@core/services/utils.service';
import { ErrorStateMatcher } from '@angular/material/core';
@@ -139,6 +139,10 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
@Input()
optDataKeys: boolean;
+ @Input()
+ @coerceBoolean()
+ latestDataKeys = false;
+
@Input()
@coerceBoolean()
simpleDataKeysLabel = false;
@@ -149,6 +153,9 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
@Input()
datakeySettingsSchema: JsonSettingsSchema;
+ @Input()
+ datakeySettingsFunction: DataKeySettingsFunction;
+
@Input()
dataKeySettingsDirective: string;
@@ -361,7 +368,8 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
if (this.widgetType === widgetType.alarm) {
this.keys = this.utils.getDefaultAlarmDataKeys();
} else if (this.isCountDatasource) {
- this.keys = [this.callbacks.generateDataKey('count', DataKeyType.count, this.datakeySettingsSchema)];
+ this.keys = [this.callbacks.generateDataKey('count', DataKeyType.count, this.datakeySettingsSchema,
+ this.latestDataKeys, this.datakeySettingsFunction)];
} else {
this.keys = [];
}
@@ -447,7 +455,8 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
}
private addFromChipValue(chip: DataKey) {
- const key = this.callbacks.generateDataKey(chip.name, chip.type, this.datakeySettingsSchema);
+ const key = this.callbacks.generateDataKey(chip.name, chip.type, this.datakeySettingsSchema, this.latestDataKeys,
+ this.datakeySettingsFunction);
this.addKey(key);
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html
index b72f9f0e5d..13a52a295b 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html
@@ -72,6 +72,7 @@
[aliasController]="aliasController"
[datakeySettingsSchema]="dataKeySettingsSchema"
[dataKeySettingsDirective]="dataKeySettingsDirective"
+ [datakeySettingsFunction]="dataKeySettingsFunction"
[dashboard]="dashboard"
[widget]="widget"
[callbacks]="dataKeysCallbacks"
@@ -82,10 +83,12 @@
{
- const dataKey = this.constructDataKey(configData, key);
+ const dataKey = this.constructDataKey(configData, key, false);
dataKeys.push(dataKey);
});
}
if (latestKeys && latestKeys.length) {
latestDataKeys.length = 0;
latestKeys.forEach(key => {
- const dataKey = this.constructDataKey(configData, key);
+ const dataKey = this.constructDataKey(configData, key, true);
latestDataKeys.push(dataKey);
});
}
}
- protected constructDataKey(configData: WidgetConfigComponentData, key: DataKey): DataKey {
+ protected constructDataKey(configData: WidgetConfigComponentData, key: DataKey, isLatestKey: boolean): DataKey {
const dataKey =
- this.widgetConfigComponent.widgetConfigCallbacks.generateDataKey(key.name, key.type, configData.dataKeySettingsSchema);
+ this.widgetConfigComponent.widgetConfigCallbacks.generateDataKey(key.name, key.type,
+ configData.dataKeySettingsSchema, isLatestKey, configData.dataKeySettingsFunction);
if (key.label) {
dataKey.label = key.label;
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts
index ae7c3c5eee..3edce958f0 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts
@@ -148,13 +148,14 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit
if (this.ctx.defaultSubscription.firstDatasource?.dataKeys?.length) {
this.lineChartDataKey = this.ctx.defaultSubscription.firstDatasource?.dataKeys[0];
this.lineChartDataKey.settings = {
- showPointLabel: false,
type: TimeSeriesChartSeriesType.line,
lineSettings: {
- smooth: false,
showLine: true,
+ step: false,
+ smooth: false,
lineWidth: 2,
- showPoints: false
+ showPoints: false,
+ showPointLabel: false
}
} as TimeSeriesChartKeySettings;
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts
index b59098de17..f5a305a247 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts
@@ -148,13 +148,14 @@ export class ValueChartCardWidgetComponent implements OnInit, AfterViewInit, OnD
if (this.ctx.defaultSubscription.firstDatasource?.dataKeys?.length) {
this.lineChartDataKey = this.ctx.defaultSubscription.firstDatasource?.dataKeys[0];
this.lineChartDataKey.settings = {
- showPointLabel: false,
type: TimeSeriesChartSeriesType.line,
lineSettings: {
- smooth: true,
showLine: true,
+ step: false,
+ smooth: true,
lineWidth: 2,
- showPoints: false
+ showPoints: false,
+ showPointLabel: false
}
} as TimeSeriesChartKeySettings;
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts
index 7564bd9cab..06e8e98fd0 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts
@@ -44,7 +44,7 @@ import {
} from 'echarts/charts';
import { LabelLayout } from 'echarts/features';
import { CanvasRenderer, SVGRenderer } from 'echarts/renderers';
-import { DataEntry, DataKey, DataSet } from '@shared/models/widget.models';
+import { DataEntry, DataKey, DataSet, LegendDirection } from '@shared/models/widget.models';
import {
calculateAggIntervalWithWidgetTimeWindow,
IntervalMath,
@@ -304,6 +304,13 @@ export enum EChartsTooltipTrigger {
axis = 'axis'
}
+export const tooltipTriggerTranslationMap = new Map(
+ [
+ [ EChartsTooltipTrigger.point, 'tooltip.trigger-point' ],
+ [ EChartsTooltipTrigger.axis, 'tooltip.trigger-axis' ]
+ ]
+);
+
export interface EChartsTooltipWidgetSettings {
showTooltip: boolean;
tooltipTrigger?: EChartsTooltipTrigger;
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts
index ef8b432e46..3965f69000 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts
@@ -40,11 +40,42 @@ import {
import { DataKey } from '@shared/models/widget.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { TbColorScheme } from '@shared/models/color.models';
+import { DoughnutLayout } from '@home/components/widget/lib/chart/doughnut-widget.models';
-export enum PointLabelPosition {
- top = 'top',
- bottom = 'bottom'
-}
+const timeSeriesChartColorScheme: 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 AxisPosition {
left = 'left',
@@ -86,19 +117,63 @@ export enum ThresholdLabelPosition {
insideEndBottom = 'insideEndBottom'
}
+export enum TimeSeriesChartThresholdType {
+ constant = 'constant',
+ latestKey = 'latestKey',
+ entity = 'entity'
+}
+
+export enum SeriesFillType {
+ none = 'none',
+ opacity = 'opacity',
+ gradient = 'gradient'
+}
+
+export enum SeriesLabelPosition {
+ top = 'top',
+ bottom = 'bottom'
+}
+
+export enum LineSeriesStepType {
+ start = 'start',
+ middle = 'middle',
+ end = 'end'
+}
+
+export enum TimeSeriesChartSeriesType {
+ line = 'line',
+ bar = 'bar'
+}
+
+export const timeSeriesChartSeriesTypes = Object.keys(TimeSeriesChartSeriesType) as TimeSeriesChartSeriesType[];
+
+export const timeSeriesChartSeriesTypeTranslations = new Map(
+ [
+ [TimeSeriesChartSeriesType.line, 'widgets.time-series-chart.series.type-line'],
+ [TimeSeriesChartSeriesType.bar, 'widgets.time-series-chart.series.type-bar']
+ ]
+);
+
+export const timeSeriesChartSeriesTypeIcons = new Map(
+ [
+ [TimeSeriesChartSeriesType.line, 'mdi:chart-line'],
+ [TimeSeriesChartSeriesType.bar, 'mdi:chart-bar']
+ ]
+);
+
export interface TimeSeriesChartAxisSettings {
show: boolean;
- position: AxisPosition;
label?: string;
labelFont?: Font;
labelColor?: string;
- showLine: boolean;
- lineColor: string;
- showTicks: boolean;
- ticksColor: string;
+ position: AxisPosition;
showTickLabels: boolean;
tickLabelFont: Font;
tickLabelColor: string;
+ showTicks: boolean;
+ ticksColor: string;
+ showLine: boolean;
+ lineColor: string;
showSplitLines: boolean;
splitLinesColor: string;
}
@@ -109,24 +184,19 @@ export interface TimeSeriesChartYAxisSettings extends TimeSeriesChartAxisSetting
intervalCalculator?: string;
}
-export enum TimeSeriesChartThresholdType {
- constant = 'constant',
- latestKey = 'latestKey',
- entity = 'entity'
-}
-
export interface TimeSeriesChartThreshold {
type: TimeSeriesChartThresholdType;
value?: number;
- latestKeyName?: string;
+ latestKey?: string;
+ latestKeyType?: DataKeyType.attribute | DataKeyType.timeseries;
entityAlias?: string;
- entityKeyType?: DataKeyType.attribute | DataKeyType.timeseries;
entityKey?: string;
+ entityKeyType?: DataKeyType.attribute | DataKeyType.timeseries;
units?: string;
decimals?: number;
- lineWidth: number;
- lineType: TimeSeriesChartLineType;
lineColor: string;
+ lineType: TimeSeriesChartLineType;
+ lineWidth: number;
startSymbol: TimeSeriesChartShape;
startSymbolSize: number;
endSymbol: TimeSeriesChartShape;
@@ -137,48 +207,13 @@ export interface TimeSeriesChartThreshold {
labelColor: string;
}
-const timeSeriesChartColorScheme: 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.pointLabel': {
- light: 'rgba(0, 0, 0, 0.76)',
- dark: '#eee'
- }
-};
-
export const timeSeriesChartThresholdDefaultSettings: TimeSeriesChartThreshold = {
type: TimeSeriesChartThresholdType.constant,
units: '',
decimals: 0,
- lineWidth: 1,
- lineType: TimeSeriesChartLineType.solid,
lineColor: timeSeriesChartColorScheme['threshold.line'].light,
+ lineType: TimeSeriesChartLineType.solid,
+ lineWidth: 1,
startSymbol: TimeSeriesChartShape.none,
startSymbolSize: 5,
endSymbol: TimeSeriesChartShape.arrow,
@@ -197,22 +232,21 @@ export const timeSeriesChartThresholdDefaultSettings: TimeSeriesChartThreshold =
};
export interface TimeSeriesChartSettings extends EChartsTooltipWidgetSettings {
+ thresholds: TimeSeriesChartThreshold[];
darkMode: boolean;
dataZoom: boolean;
stack: boolean;
- thresholds: TimeSeriesChartThreshold[];
- xAxis: TimeSeriesChartAxisSettings;
yAxis: TimeSeriesChartYAxisSettings;
+ xAxis: TimeSeriesChartAxisSettings;
}
export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = {
+ thresholds: [],
darkMode: false,
dataZoom: true,
stack: false,
- thresholds: [],
- xAxis: {
+ yAxis: {
show: true,
- position: AxisPosition.bottom,
label: '',
labelFont: {
family: 'Roboto',
@@ -223,26 +257,26 @@ export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = {
lineHeight: '1'
},
labelColor: timeSeriesChartColorScheme['axis.label'].light,
- showLine: true,
- lineColor: timeSeriesChartColorScheme['axis.line'].light,
- showTicks: true,
- ticksColor: timeSeriesChartColorScheme['axis.ticks'].light,
+ position: AxisPosition.left,
showTickLabels: true,
tickLabelFont: {
family: 'Roboto',
- size: 10,
+ size: 12,
sizeUnit: 'px',
style: 'normal',
weight: '400',
lineHeight: '1'
},
tickLabelColor: timeSeriesChartColorScheme['axis.tickLabel'].light,
+ showTicks: true,
+ ticksColor: timeSeriesChartColorScheme['axis.ticks'].light,
+ showLine: true,
+ lineColor: timeSeriesChartColorScheme['axis.line'].light,
showSplitLines: true,
splitLinesColor: timeSeriesChartColorScheme['axis.splitLine'].light
},
- yAxis: {
+ xAxis: {
show: true,
- position: AxisPosition.left,
label: '',
labelFont: {
family: 'Roboto',
@@ -253,20 +287,21 @@ export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = {
lineHeight: '1'
},
labelColor: timeSeriesChartColorScheme['axis.label'].light,
- showLine: true,
- lineColor: timeSeriesChartColorScheme['axis.line'].light,
- showTicks: true,
- ticksColor: timeSeriesChartColorScheme['axis.ticks'].light,
+ position: AxisPosition.bottom,
showTickLabels: true,
tickLabelFont: {
family: 'Roboto',
- size: 12,
+ size: 10,
sizeUnit: 'px',
style: 'normal',
weight: '400',
lineHeight: '1'
},
tickLabelColor: timeSeriesChartColorScheme['axis.tickLabel'].light,
+ showTicks: true,
+ ticksColor: timeSeriesChartColorScheme['axis.ticks'].light,
+ showLine: true,
+ lineColor: timeSeriesChartColorScheme['axis.line'].light,
showSplitLines: true,
splitLinesColor: timeSeriesChartColorScheme['axis.splitLine'].light
},
@@ -282,7 +317,6 @@ export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = {
},
tooltipValueColor: 'rgba(0, 0, 0, 0.76)',
tooltipShowDate: true,
- tooltipDateInterval: true,
tooltipDateFormat: simpleDateFormat('dd MMM yyyy HH:mm:ss'),
tooltipDateFont: {
family: 'Roboto',
@@ -293,16 +327,11 @@ export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = {
lineHeight: '16px'
},
tooltipDateColor: 'rgba(0, 0, 0, 0.76)',
+ tooltipDateInterval: true,
tooltipBackgroundColor: 'rgba(255, 255, 255, 0.76)',
tooltipBackgroundBlur: 4
};
-export enum SeriesFillType {
- none = 'none',
- opacity = 'opacity',
- gradient = 'gradient'
-}
-
export interface SeriesFillSettings {
type: SeriesFillType;
opacity: number;
@@ -313,36 +342,36 @@ export interface SeriesFillSettings {
}
export interface LineSeriesSettings {
- step: false | 'start' | 'end' | 'middle';
- smooth: boolean;
showLine: boolean;
- lineWidth: number;
+ step: boolean;
+ stepType: LineSeriesStepType;
+ smooth: boolean;
lineType: TimeSeriesChartLineType;
- fillAreaSettings: SeriesFillSettings;
+ lineWidth: number;
showPoints: boolean;
+ showPointLabel: boolean;
+ pointLabelPosition: SeriesLabelPosition;
+ pointLabelFont: Font;
+ pointLabelColor: string;
pointShape: TimeSeriesChartShape;
pointSize: number;
+ fillAreaSettings: SeriesFillSettings;
}
export interface BarSeriesSettings {
showBorder: boolean;
borderWidth: number;
borderRadius: number;
+ showLabel: boolean;
+ labelPosition: SeriesLabelPosition;
+ labelFont: Font;
+ labelColor: string;
backgroundSettings: SeriesFillSettings;
}
-export enum TimeSeriesChartSeriesType {
- line = 'line',
- bar = 'bar'
-}
-
export interface TimeSeriesChartKeySettings {
- dataHiddenByDefault: boolean;
showInLegend: boolean;
- showPointLabel: boolean;
- pointLabelPosition: PointLabelPosition;
- pointLabelFont: Font;
- pointLabelColor: string;
+ dataHiddenByDefault: boolean;
type: TimeSeriesChartSeriesType;
lineSettings: LineSeriesSettings;
barSettings: BarSeriesSettings;
@@ -351,24 +380,28 @@ export interface TimeSeriesChartKeySettings {
export const timeSeriesChartKeyDefaultSettings: TimeSeriesChartKeySettings = {
showInLegend: true,
dataHiddenByDefault: false,
- showPointLabel: false,
- pointLabelPosition: PointLabelPosition.top,
- pointLabelFont: {
- family: 'Roboto',
- size: 11,
- sizeUnit: 'px',
- style: 'normal',
- weight: '400',
- lineHeight: '1'
- },
- pointLabelColor: timeSeriesChartColorScheme['series.pointLabel'].light,
type: TimeSeriesChartSeriesType.line,
lineSettings: {
+ showLine: true,
step: false,
+ stepType: LineSeriesStepType.start,
smooth: false,
- showLine: true,
- lineWidth: 2,
lineType: TimeSeriesChartLineType.solid,
+ lineWidth: 2,
+ showPoints: false,
+ showPointLabel: false,
+ pointLabelPosition: SeriesLabelPosition.top,
+ pointLabelFont: {
+ family: 'Roboto',
+ size: 11,
+ sizeUnit: 'px',
+ style: 'normal',
+ weight: '400',
+ lineHeight: '1'
+ },
+ pointLabelColor: timeSeriesChartColorScheme['series.label'].light,
+ pointShape: TimeSeriesChartShape.emptyCircle,
+ pointSize: 4,
fillAreaSettings: {
type: SeriesFillType.none,
opacity: 0.4,
@@ -376,15 +409,23 @@ export const timeSeriesChartKeyDefaultSettings: TimeSeriesChartKeySettings = {
start: 100,
end: 0
}
- },
- showPoints: false,
- pointShape: TimeSeriesChartShape.emptyCircle,
- pointSize: 4
+ }
},
barSettings: {
showBorder: false,
borderWidth: 2,
borderRadius: 0,
+ showLabel: false,
+ labelPosition: SeriesLabelPosition.top,
+ labelFont: {
+ family: 'Roboto',
+ size: 11,
+ sizeUnit: 'px',
+ style: 'normal',
+ weight: '400',
+ lineHeight: '1'
+ },
+ labelColor: timeSeriesChartColorScheme['series.label'].light,
backgroundSettings: {
type: SeriesFillType.none,
opacity: 0.4,
@@ -707,21 +748,23 @@ export const updateDarkMode = (options: EChartsOption, settings: TimeSeriesChart
}
for (const item of dataItems) {
if (item.dataKey.settings.type === TimeSeriesChartSeriesType.line) {
+ const lineSettings = item.dataKey.settings as LineSeriesSettings;
if (item.option.label?.show) {
- item.option.label.rich.value.color = prepareChartThemeColor(item.dataKey.settings.pointLabelColor, darkMode, 'series.pointLabel');
+ item.option.label.rich.value.color = prepareChartThemeColor(lineSettings.pointLabelColor, darkMode, 'series.label');
}
if (Array.isArray(options.series)) {
const series = options.series.find(s => s.id === item.id);
if (series) {
if (series.label?.show) {
- series.label.rich.value.color = prepareChartThemeColor(item.dataKey.settings.pointLabelColor, darkMode, 'series.pointLabel');
+ series.label.rich.value.color = prepareChartThemeColor(lineSettings.pointLabelColor, darkMode, 'series.label');
}
}
}
} else {
if (item.barRenderContext?.labelOption?.show) {
- item.barRenderContext.labelOption.rich.value.color = prepareChartThemeColor(item.dataKey.settings.pointLabelColor,
- darkMode, 'series.pointLabel');
+ const barSettings = item.dataKey.settings as BarSeriesSettings;
+ item.barRenderContext.labelOption.rich.value.color = prepareChartThemeColor(barSettings.labelColor,
+ darkMode, 'series.label');
}
}
}
@@ -747,21 +790,6 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem,
const dataKey = item.dataKey;
const settings: TimeSeriesChartKeySettings = dataKey.settings;
const seriesColor = item.dataKey.color;
- let pointLabelStyle: ComponentStyle = {};
- if (settings.showPointLabel) {
- pointLabelStyle = createChartTextStyle(settings.pointLabelFont, settings.pointLabelColor, darkMode, 'series.pointLabel');
- }
- const label: SeriesLabelOption = {
- show: settings.showPointLabel,
- position: settings.pointLabelPosition,
- formatter: (params): string => {
- const value = formatValue(params.value[1], item.decimals, item.units, false);
- return `{value|${value}}`;
- },
- rich: {
- value: pointLabelStyle
- }
- };
seriesOption = {
id: item.id,
dataGroupId: item.id,
@@ -788,8 +816,9 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem,
const lineSettings = settings.lineSettings;
const lineSeriesOption = seriesOption as LineSeriesOption;
lineSeriesOption.type = 'line';
- lineSeriesOption.label = label;
- lineSeriesOption.step = lineSettings.step;
+ lineSeriesOption.label = createSeriesLabelOption(item, lineSettings.showPointLabel,
+ lineSettings.pointLabelFont, lineSettings.pointLabelColor, lineSettings.pointLabelPosition, darkMode);
+ lineSeriesOption.step = lineSettings.step ? lineSettings.stepType : false;
lineSeriesOption.smooth = lineSettings.smooth;
lineSeriesOption.lineStyle = {
width: lineSettings.showLine ? lineSettings.lineWidth : 0,
@@ -825,7 +854,8 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem,
barVisualSettings.color = createLinearOpacityGradient(seriesColor, barSettings.backgroundSettings.gradient);
}
item.barRenderContext.visualSettings = barVisualSettings;
- item.barRenderContext.labelOption = label;
+ item.barRenderContext.labelOption = createSeriesLabelOption(item, barSettings.showLabel,
+ barSettings.labelFont, barSettings.labelColor, barSettings.labelPosition, darkMode);
barSeriesOption.renderItem = (params, api) =>
renderTimeSeriesBar(params, api, item.barRenderContext);
}
@@ -834,6 +864,26 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem,
return seriesOption;
};
+const createSeriesLabelOption = (item: TimeSeriesChartDataItem, show: boolean,
+ labelFont: Font, labelColor: string, position: SeriesLabelPosition,
+ darkMode: boolean): SeriesLabelOption => {
+ let labelStyle: ComponentStyle = {};
+ if (show) {
+ labelStyle = createChartTextStyle(labelFont, labelColor, darkMode, 'series.label');
+ }
+ return {
+ show,
+ position,
+ formatter: (params): string => {
+ const value = formatValue(params.value[1], item.decimals, item.units, false);
+ return `{value|${value}}`;
+ },
+ rich: {
+ value: labelStyle
+ }
+ };
+};
+
const createChartTextStyle = (font: Font, color: string, darkMode: boolean, colorKey?: string): ComponentStyle => {
const style = textStyle(font);
delete style.lineHeight;
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts
index 48c2dfaf57..c36327fec6 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts
@@ -21,6 +21,7 @@ import {
createTimeSeriesXAxisOption,
createTimeSeriesYAxis,
generateChartData,
+ parseThresholdData, SeriesLabelPosition,
TimeSeriesChartDataItem,
timeSeriesChartDefaultSettings,
timeSeriesChartKeyDefaultSettings,
@@ -30,8 +31,6 @@ import {
TimeSeriesChartThresholdItem,
TimeSeriesChartThresholdType,
TimeSeriesChartYAxis,
- parseThresholdData,
- PointLabelPosition,
updateDarkMode
} from '@home/components/widget/lib/chart/time-series-chart.models';
import { ResizeObserver } from '@juggle/resize-observer';
@@ -60,9 +59,20 @@ import { BehaviorSubject } from 'rxjs';
import { AggregationType } from '@shared/models/time/time.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { WidgetSubscriptionOptions } from '@core/api/widget-api.models';
+import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models';
export class TbTimeSeriesChart {
+ public static dataKeySettings(): DataKeySettingsFunction {
+ return (key, isLatestDataKey) => {
+ if (!isLatestDataKey) {
+ return mergeDeep({} as TimeSeriesChartKeySettings,
+ timeSeriesChartKeyDefaultSettings);
+ }
+ return null;
+ };
+ }
+
private readonly shapeResize$: ResizeObserver;
private dataItems: TimeSeriesChartDataItem[] = [];
@@ -247,7 +257,10 @@ export class TbTimeSeriesChart {
for (const dataKey of dataKeys) {
const keySettings = mergeDeep({} as TimeSeriesChartKeySettings,
timeSeriesChartKeyDefaultSettings, dataKey.settings);
- if (keySettings.showPointLabel && keySettings.pointLabelPosition === PointLabelPosition.top) {
+ 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)) {
this.topPointLabels = true;
}
dataKey.settings = keySettings;
@@ -280,8 +293,9 @@ export class TbTimeSeriesChart {
if (this.ctx.datasources.length) {
for (const datasource of this.ctx.datasources) {
latestDataKey = datasource.latestDataKeys?.find(d =>
- (d.type === DataKeyType.function && d.label === threshold.latestKeyName) ||
- (d.type !== DataKeyType.function && d.name === threshold.latestKeyName));
+ (d.type === DataKeyType.function && d.label === threshold.latestKey) ||
+ (d.type !== DataKeyType.function && d.name === threshold.latestKey &&
+ d.type === threshold.latestKeyType));
if (latestDataKey) {
break;
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts
index a760d1ece1..a46e6a8427 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts
@@ -298,6 +298,6 @@ export class MapWidgetController implements MapWidgetInterface {
}
}
-export let TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController;
+export const TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController;
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.html
new file mode 100644
index 0000000000..b1d5d3d302
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.html
@@ -0,0 +1,36 @@
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.ts
new file mode 100644
index 0000000000..8f266fa9b6
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.ts
@@ -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 { Component } from '@angular/core';
+import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
+import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
+import { Store } from '@ngrx/store';
+import { AppState } from '@core/core.state';
+import { mergeDeep } from '@core/utils';
+import {
+ timeSeriesChartKeyDefaultSettings,
+ TimeSeriesChartKeySettings
+} from '@home/components/widget/lib/chart/time-series-chart.models';
+import { WidgetConfigComponentData } from '@home/models/widget-component.models';
+
+@Component({
+ selector: 'tb-time-series-chart-key-settings',
+ templateUrl: './time-series-chart-key-settings.component.html',
+ styleUrls: ['./../widget-settings.scss']
+})
+export class TimeSeriesChartKeySettingsComponent extends WidgetSettingsComponent {
+
+ timeSeriesChartKeySettingsForm: UntypedFormGroup;
+
+ constructor(protected store: Store,
+ private fb: UntypedFormBuilder) {
+ super(store);
+ }
+
+ protected settingsForm(): UntypedFormGroup {
+ return this.timeSeriesChartKeySettingsForm;
+ }
+
+ protected onWidgetConfigSet(widgetConfig: WidgetConfigComponentData) {
+ const params = widgetConfig.typeParameters as any;
+ // const timeSeriesChartType = params.timeSeriesChartType;
+ }
+
+ protected defaultSettings(): WidgetSettings {
+ return mergeDeep({} as TimeSeriesChartKeySettings,
+ timeSeriesChartKeyDefaultSettings);
+ }
+
+ protected onSettingsSet(settings: WidgetSettings) {
+ const seriesSettings = settings as TimeSeriesChartKeySettings;
+ this.timeSeriesChartKeySettingsForm = this.fb.group({
+ showInLegend: [seriesSettings.showInLegend, []],
+ dataHiddenByDefault: [seriesSettings.dataHiddenByDefault, []],
+ type: [seriesSettings.type, []],
+ lineSettings: [settings.lineSettings, []],
+ barSettings: [settings.barSettings, []]
+ });
+ }
+
+ protected validatorTriggers(): string[] {
+ return ['showInLegend'];
+ }
+
+ protected updateValidators(_emitEvent: boolean) {
+ const showInLegend: boolean = this.timeSeriesChartKeySettingsForm.get('showInLegend').value;
+ if (showInLegend) {
+ this.timeSeriesChartKeySettingsForm.get('dataHiddenByDefault').enable();
+ } else {
+ this.timeSeriesChartKeySettingsForm.get('dataHiddenByDefault').patchValue(false, {emitEvent: false});
+ this.timeSeriesChartKeySettingsForm.get('dataHiddenByDefault').disable();
+ }
+ }
+
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.html
index 44ab1d0bad..3dd5e08860 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.html
@@ -16,7 +16,7 @@
-->
-