Browse Source

UI: Update range chart advanced settings. Add label background option to series/threshold labels. Minor improvements and fixes.

pull/10473/head
Igor Kulikov 2 years ago
parent
commit
1a9e69c762
  1. 26
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html
  2. 34
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts
  3. 101
      ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts
  4. 49
      ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts
  5. 3
      ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-bar.models.ts
  6. 99
      ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts
  7. 7
      ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts
  8. 202
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html
  9. 127
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts
  10. 9
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.html
  11. 14
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.ts
  12. 13
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html
  13. 22
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts
  14. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html
  15. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts
  16. 15
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html
  17. 74
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts
  18. 19
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html
  19. 40
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts
  20. 39
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.html
  21. 124
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.ts
  22. 5
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts
  23. 10
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  24. 2
      ui-ngx/src/assets/locale/locale.constant-pl_PL.json
  25. 2
      ui-ngx/src/assets/locale/locale.constant-zh_CN.json

26
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html

@ -102,10 +102,17 @@
formControlName="outOfRangeColor">
</tb-color-input>
</div>
<div class="tb-form-row">
<div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="showRangeThresholds">
{{ 'widgets.range-chart.show-range-thresholds' | translate }}
</mat-slide-toggle>
<tb-time-series-chart-threshold-settings
boxButton
hideYAxis
[widgetConfig]="widgetConfig?.config"
title="widgets.range-chart.range-thresholds-settings"
formControlName="rangeThreshold">
</tb-time-series-chart-threshold-settings>
</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="fillArea">
@ -115,8 +122,8 @@
<div class="tb-form-row space-between column-xs">
<div translate>widgets.range-chart.fill-area-opacity</div>
<mat-form-field class="medium-width number" appearance="outline" subscriptSizing="dynamic">
<input matInput type="number" formControlName="fillAreaOpacity" min="0" max="100"
step="1" placeholder="{{ 'widget-config.set' | translate }}"/>
<input matInput type="number" formControlName="fillAreaOpacity" min="0" max="1"
step="0.1" placeholder="{{ 'widget-config.set' | translate }}"/>
</mat-form-field>
</div>
<div class="tb-form-panel stroked">
@ -193,12 +200,21 @@
</tb-color-input>
</div>
</div>
<div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="enablePointLabelBackground">
{{ 'widgets.time-series-chart.series.point.point-label-background' | translate }}
</mat-slide-toggle>
<tb-color-input asBoxInput
colorClearButton
formControlName="pointLabelBackground">
</tb-color-input>
</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 *ngFor="let shape of echartsShapes" [value]="shape">
{{ echartsShapeTranslations.get(shape) | translate }}
</mat-option>
</mat-select>
</mat-form-field>

34
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts

@ -49,11 +49,10 @@ import {
lineSeriesStepTypeTranslations,
seriesLabelPositions,
seriesLabelPositionTranslations,
timeSeriesChartShapes,
timeSeriesChartShapeTranslations,
timeSeriesLineTypes,
timeSeriesLineTypeTranslations
} from '@home/components/widget/lib/chart/time-series-chart.models';
import { echartsShapes, echartsShapeTranslations } from '@home/components/widget/lib/chart/echarts-widget.models';
@Component({
selector: 'tb-range-chart-basic-config',
@ -83,9 +82,9 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent {
seriesLabelPositionTranslations = seriesLabelPositionTranslations;
timeSeriesChartShapes = timeSeriesChartShapes;
echartsShapes = echartsShapes;
timeSeriesChartShapeTranslations = timeSeriesChartShapeTranslations;
echartsShapeTranslations = echartsShapeTranslations;
legendPositions = legendPositions;
@ -139,8 +138,9 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent {
rangeColors: [settings.rangeColors, []],
outOfRangeColor: [settings.outOfRangeColor, []],
showRangeThresholds: [settings.showRangeThresholds, []],
rangeThreshold: [settings.rangeThreshold, []],
fillArea: [settings.fillArea, []],
fillAreaOpacity: [settings.fillAreaOpacity, [Validators.min(0), Validators.max(100)]],
fillAreaOpacity: [settings.fillAreaOpacity, [Validators.min(0), Validators.max(1)]],
showLine: [settings.showLine, []],
step: [settings.step, []],
@ -154,6 +154,8 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent {
pointLabelPosition: [settings.pointLabelPosition, []],
pointLabelFont: [settings.pointLabelFont, []],
pointLabelColor: [settings.pointLabelColor, []],
enablePointLabelBackground: [settings.enablePointLabelBackground, []],
pointLabelBackground: [settings.pointLabelBackground, []],
pointShape: [settings.pointShape, []],
pointSize: [settings.pointSize, [Validators.min(0)]],
@ -216,6 +218,7 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent {
this.widgetConfig.config.settings.rangeColors = config.rangeColors;
this.widgetConfig.config.settings.outOfRangeColor = config.outOfRangeColor;
this.widgetConfig.config.settings.showRangeThresholds = config.showRangeThresholds;
this.widgetConfig.config.settings.rangeThreshold = config.rangeThreshold;
this.widgetConfig.config.settings.fillArea = config.fillArea;
this.widgetConfig.config.settings.fillAreaOpacity = config.fillAreaOpacity;
@ -231,6 +234,8 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent {
this.widgetConfig.config.settings.pointLabelPosition = config.pointLabelPosition;
this.widgetConfig.config.settings.pointLabelFont = config.pointLabelFont;
this.widgetConfig.config.settings.pointLabelColor = config.pointLabelColor;
this.widgetConfig.config.settings.enablePointLabelBackground = config.enablePointLabelBackground;
this.widgetConfig.config.settings.pointLabelBackground = config.pointLabelBackground;
this.widgetConfig.config.settings.pointShape = config.pointShape;
this.widgetConfig.config.settings.pointSize = config.pointSize;
@ -268,16 +273,19 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent {
}
protected validatorTriggers(): string[] {
return ['showTitle', 'showIcon', 'fillArea', 'showLine', 'step', 'showPointLabel', 'showLegend', 'showTooltip', 'tooltipShowDate'];
return ['showTitle', 'showIcon', 'showRangeThresholds', 'fillArea', 'showLine',
'step', 'showPointLabel', 'enablePointLabelBackground', 'showLegend', 'showTooltip', 'tooltipShowDate'];
}
protected updateValidators(emitEvent: boolean, trigger?: string) {
const showTitle: boolean = this.rangeChartWidgetConfigForm.get('showTitle').value;
const showIcon: boolean = this.rangeChartWidgetConfigForm.get('showIcon').value;
const showRangeThresholds: boolean = this.rangeChartWidgetConfigForm.get('showRangeThresholds').value;
const fillArea: boolean = this.rangeChartWidgetConfigForm.get('fillArea').value;
const showLine: boolean = this.rangeChartWidgetConfigForm.get('showLine').value;
const step: boolean = this.rangeChartWidgetConfigForm.get('step').value;
const showPointLabel: boolean = this.rangeChartWidgetConfigForm.get('showPointLabel').value;
const enablePointLabelBackground: boolean = this.rangeChartWidgetConfigForm.get('enablePointLabelBackground').value;
const showLegend: boolean = this.rangeChartWidgetConfigForm.get('showLegend').value;
const showTooltip: boolean = this.rangeChartWidgetConfigForm.get('showTooltip').value;
const tooltipShowDate: boolean = this.rangeChartWidgetConfigForm.get('tooltipShowDate').value;
@ -309,6 +317,12 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent {
this.rangeChartWidgetConfigForm.get('iconColor').disable();
}
if (showRangeThresholds) {
this.rangeChartWidgetConfigForm.get('rangeThreshold').enable();
} else {
this.rangeChartWidgetConfigForm.get('rangeThreshold').disable();
}
if (fillArea) {
this.rangeChartWidgetConfigForm.get('fillAreaOpacity').enable();
} else {
@ -337,10 +351,18 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent {
this.rangeChartWidgetConfigForm.get('pointLabelPosition').enable();
this.rangeChartWidgetConfigForm.get('pointLabelFont').enable();
this.rangeChartWidgetConfigForm.get('pointLabelColor').enable();
this.rangeChartWidgetConfigForm.get('enablePointLabelBackground').enable({emitEvent: false});
if (enablePointLabelBackground) {
this.rangeChartWidgetConfigForm.get('pointLabelBackground').enable();
} else {
this.rangeChartWidgetConfigForm.get('pointLabelBackground').disable();
}
} else {
this.rangeChartWidgetConfigForm.get('pointLabelPosition').disable();
this.rangeChartWidgetConfigForm.get('pointLabelFont').disable();
this.rangeChartWidgetConfigForm.get('pointLabelColor').disable();
this.rangeChartWidgetConfigForm.get('enablePointLabelBackground').disable({emitEvent: false});
this.rangeChartWidgetConfigForm.get('pointLabelBackground').disable();
}
if (showLegend) {

101
ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts

@ -17,7 +17,7 @@
import * as echarts from 'echarts/core';
import AxisModel from 'echarts/types/src/coord/cartesian/AxisModel';
import { estimateLabelUnionRect } from 'echarts/lib/coord/axisHelper';
import { formatValue, isDefinedAndNotNull } from '@core/utils';
import { formatValue, isDefinedAndNotNull, isNumber } from '@core/utils';
import {
DataZoomComponent,
DataZoomComponentOption,
@ -55,6 +55,7 @@ import { DateFormatProcessor, DateFormatSettings, Font } from '@shared/models/wi
import GlobalModel from 'echarts/types/src/model/Global';
import Axis2D from 'echarts/types/src/coord/cartesian/Axis2D';
import SeriesModel from 'echarts/types/src/model/Series';
import { MarkLine2DDataItemOption } from 'echarts/types/src/component/marker/MarkLineModel';
class EChartsModule {
private initialized = false;
@ -110,6 +111,51 @@ export type EChartsSeriesItem = {
decimals?: number;
};
export enum EChartsShape {
emptyCircle = 'emptyCircle',
circle = 'circle',
rect = 'rect',
roundRect = 'roundRect',
triangle = 'triangle',
diamond = 'diamond',
pin = 'pin',
arrow = 'arrow',
none = 'none'
}
export const echartsShapes = Object.keys(EChartsShape) as EChartsShape[];
export const echartsShapeTranslations = new Map<EChartsShape, string>(
[
[EChartsShape.emptyCircle, 'widgets.time-series-chart.shape-empty-circle'],
[EChartsShape.circle, 'widgets.time-series-chart.shape-circle'],
[EChartsShape.rect, 'widgets.time-series-chart.shape-rect'],
[EChartsShape.roundRect, 'widgets.time-series-chart.shape-round-rect'],
[EChartsShape.triangle, 'widgets.time-series-chart.shape-triangle'],
[EChartsShape.diamond, 'widgets.time-series-chart.shape-diamond'],
[EChartsShape.pin, 'widgets.time-series-chart.shape-pin'],
[EChartsShape.arrow, 'widgets.time-series-chart.shape-arrow'],
[EChartsShape.none, 'widgets.time-series-chart.shape-none']
]
);
type EChartsShapeOffsetFunction = (size: number) => number;
export const timeSeriesChartShapeOffsetFunctions = new Map<EChartsShape, EChartsShapeOffsetFunction>(
[
[EChartsShape.emptyCircle, size => size / 2 + 1],
[EChartsShape.circle, size => size / 2],
[EChartsShape.rect, size => size / 2],
[EChartsShape.roundRect, size => size / 2],
[EChartsShape.triangle, size => size / 2],
[EChartsShape.diamond, size => size / 2],
[EChartsShape.pin, size => size],
[EChartsShape.arrow, () => 0],
[EChartsShape.none, () => 0],
]
);
export const timeAxisBandWidthCalculator: TimeAxisBandWidthCalculator = (model) => {
let interval: number;
const axisOption = model.option;
@ -211,7 +257,23 @@ export const measureXAxisNameHeight = (chart: ECharts, name: string): number =>
return 0;
};
export const measureThresholdLabelOffset = (chart: ECharts, axisId: string, thresholdId: string, value: any): [number, number] => {
const measureSymbolOffset = (symbol: string, symbolSize: any): number => {
if (isNumber(symbolSize)) {
if (symbol) {
const offsetFunction = timeSeriesChartShapeOffsetFunctions.get(symbol as EChartsShape);
if (offsetFunction) {
return offsetFunction(symbolSize);
} else {
return symbolSize / 2;
}
}
} else {
return 0;
}
}
export const measureThresholdOffset = (chart: ECharts, axisId: string, thresholdId: string, value: any): [number, number] => {
const offset: [number, number] = [0,0];
const axis = getYAxis(chart, axisId);
if (axis && !axis.scale.isBlank()) {
const extent = axis.scale.getExtent();
@ -220,6 +282,16 @@ export const measureThresholdLabelOffset = (chart: ECharts, axisId: string, thre
if (models?.length) {
const lineSeriesModel = models[0] as SeriesModel<LineSeriesOption>;
const markLineModel = lineSeriesModel.getModel('markLine');
const dataOption = markLineModel.get('data');
for (const dataItemOption of dataOption) {
const dataItem = dataItemOption as MarkLine2DDataItemOption;
const start = dataItem[0];
const startOffset = measureSymbolOffset(start.symbol, start.symbolSize);
offset[0] = Math.max(offset[0], startOffset);
const end = dataItem[1];
const endOffset = measureSymbolOffset(end.symbol, end.symbolSize);
offset[1] = Math.max(offset[1], endOffset);
}
const labelPosition = markLineModel.get(['label', 'position']);
if (labelPosition === 'start' || labelPosition === 'end') {
const labelModel = markLineModel.getModel('label');
@ -239,23 +311,38 @@ export const measureThresholdLabelOffset = (chart: ECharts, axisId: string, thre
}
}
if (!textWidth) {
return [0,0];
return offset;
}
const distanceOpt = markLineModel.get(['label', 'distance']);
let distance = 5;
if (distanceOpt) {
distance = typeof distanceOpt === 'number' ? distanceOpt : distanceOpt[0];
}
const offset = distance + textWidth;
const paddingOpt = markLineModel.get(['label', 'padding']);
let leftPadding = 0;
let rightPadding = 0;
if (paddingOpt) {
if (Array.isArray(paddingOpt)) {
if (paddingOpt.length === 4) {
leftPadding = paddingOpt[3];
rightPadding = paddingOpt[1];
} else if (paddingOpt.length === 2) {
leftPadding = rightPadding = paddingOpt[1];
}
} else {
leftPadding = rightPadding = paddingOpt;
}
}
const textOffset = distance + textWidth + leftPadding + rightPadding;
if (labelPosition === 'start') {
return [offset, 0];
offset[0] = Math.max(offset[0], textOffset);
} else {
return [0, offset];
offset[1] = Math.max(offset[1], textOffset);
}
}
}
}
return [0,0];
return offset;
};
export const getAxisExtent = (chart: ECharts, axisId: string): [number, number] => {

49
ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts

@ -24,22 +24,21 @@ import {
sortedColorRange
} from '@shared/models/widget-settings.models';
import { LegendPosition } from '@shared/models/widget.models';
import { EChartsTooltipWidgetSettings } from '@home/components/widget/lib/chart/echarts-widget.models';
import { EChartsShape, EChartsTooltipWidgetSettings } from '@home/components/widget/lib/chart/echarts-widget.models';
import {
createTimeSeriesChartVisualMapPiece,
defaultTimeSeriesChartXAxisSettings,
defaultTimeSeriesChartYAxisSettings,
LineSeriesStepType,
SeriesFillType,
SeriesLabelPosition, timeSeriesChartAnimationDefaultSettings,
SeriesLabelPosition, ThresholdLabelPosition, timeSeriesChartAnimationDefaultSettings,
TimeSeriesChartAnimationSettings,
timeSeriesChartColorScheme,
TimeSeriesChartKeySettings,
TimeSeriesChartLineType,
TimeSeriesChartSeriesType,
TimeSeriesChartSettings,
TimeSeriesChartShape,
TimeSeriesChartThreshold,
TimeSeriesChartThreshold, timeSeriesChartThresholdDefaultSettings,
TimeSeriesChartThresholdType,
TimeSeriesChartVisualMapPiece,
TimeSeriesChartXAxisSettings,
@ -64,6 +63,7 @@ export interface RangeChartWidgetSettings extends EChartsTooltipWidgetSettings {
rangeColors: Array<ColorRange>;
outOfRangeColor: string;
showRangeThresholds: boolean;
rangeThreshold: Partial<TimeSeriesChartThreshold>;
fillArea: boolean;
fillAreaOpacity: number;
showLine: boolean;
@ -77,7 +77,9 @@ export interface RangeChartWidgetSettings extends EChartsTooltipWidgetSettings {
pointLabelPosition: SeriesLabelPosition;
pointLabelFont: Font;
pointLabelColor: string;
pointShape: TimeSeriesChartShape;
enablePointLabelBackground: boolean;
pointLabelBackground: string;
pointShape: EChartsShape;
pointSize: number;
yAxis: TimeSeriesChartYAxisSettings;
xAxis: TimeSeriesChartXAxisSettings;
@ -104,6 +106,17 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = {
],
outOfRangeColor: '#ccc',
showRangeThresholds: true,
rangeThreshold: mergeDeep({} as Partial<TimeSeriesChartThreshold>,
timeSeriesChartThresholdDefaultSettings,
{ lineColor: '#37383b',
lineType: TimeSeriesChartLineType.dashed,
startSymbol: EChartsShape.circle,
startSymbolSize: 5,
endSymbol: EChartsShape.arrow,
endSymbolSize: 7,
labelPosition: ThresholdLabelPosition.insideEndTop,
labelColor: '#37383b',
enableLabelBackground: true}),
fillArea: true,
fillAreaOpacity: 0.7,
showLine: true,
@ -124,7 +137,9 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = {
lineHeight: '1'
},
pointLabelColor: timeSeriesChartColorScheme['series.label'].light,
pointShape: TimeSeriesChartShape.emptyCircle,
enablePointLabelBackground: false,
pointLabelBackground: 'rgba(255,255,255,0.56)',
pointShape: EChartsShape.emptyCircle,
pointSize: 4,
yAxis: mergeDeep({} as TimeSeriesChartYAxisSettings,
defaultTimeSeriesChartYAxisSettings,
@ -185,26 +200,12 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = {
export const rangeChartTimeSeriesSettings = (settings: RangeChartWidgetSettings, rangeItems: RangeItem[],
decimals: number, units: string): DeepPartial<TimeSeriesChartSettings> => {
let thresholds: DeepPartial<TimeSeriesChartThreshold>[] = settings.showRangeThresholds ? getMarkPoints(rangeItems).map(item => ({
type: TimeSeriesChartThresholdType.constant,
...{type: TimeSeriesChartThresholdType.constant,
yAxisId: 'default',
units,
decimals,
lineWidth: 1,
lineColor: '#37383b',
lineType: [3, 3],
startSymbol: TimeSeriesChartShape.circle,
startSymbolSize: 5,
endSymbol: TimeSeriesChartShape.arrow,
endSymbolSize: 7,
showLabel: true,
labelPosition: 'insideEndTop',
labelColor: '#37383b',
additionalLabelOption: {
backgroundColor: 'rgba(255,255,255,0.56)',
padding: [4, 5],
borderRadius: 4,
},
value: item
value: item},
...settings.rangeThreshold
} as DeepPartial<TimeSeriesChartThreshold>)) : [];
if (settings.thresholds?.length) {
thresholds = thresholds.concat(settings.thresholds);
@ -254,6 +255,8 @@ export const rangeChartTimeSeriesKeySettings = (settings: RangeChartWidgetSettin
pointLabelPosition: settings.pointLabelPosition,
pointLabelFont: settings.pointLabelFont,
pointLabelColor: settings.pointLabelColor,
enablePointLabelBackground: settings.enablePointLabelBackground,
pointLabelBackground: settings.pointLabelBackground,
pointShape: settings.pointShape,
pointSize: settings.pointSize,
fillAreaSettings: {

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

@ -152,6 +152,9 @@ export const renderTimeSeriesBar = (params: CustomSeriesRenderItemParams, api: C
} as CallbackDataParams);
style.textDistance = 5;
style.textPosition = position;
style.textBackgroundColor = renderCtx.labelOption.backgroundColor;
style.textPadding = renderCtx.labelOption.padding;
style.textBorderRadius = renderCtx.labelOption.borderRadius;
style.rich = renderCtx.labelOption.rich;
if (renderCtx.additionalLabelOption) {
style = {...style, ...renderCtx.additionalLabelOption};

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

@ -17,10 +17,10 @@
import {
ECharts,
EChartsOption,
EChartsSeriesItem,
EChartsSeriesItem, EChartsShape,
EChartsTooltipTrigger,
EChartsTooltipWidgetSettings,
measureThresholdLabelOffset,
measureThresholdOffset,
timeAxisBandWidthCalculator
} from '@home/components/widget/lib/chart/echarts-widget.models';
import {
@ -134,34 +134,6 @@ export const timeSeriesAxisPositionTranslations = new Map<AxisPosition, string>(
]
);
export enum TimeSeriesChartShape {
emptyCircle = 'emptyCircle',
circle = 'circle',
rect = 'rect',
roundRect = 'roundRect',
triangle = 'triangle',
diamond = 'diamond',
pin = 'pin',
arrow = 'arrow',
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',
@ -456,14 +428,16 @@ export interface TimeSeriesChartThreshold {
lineColor: string;
lineType: TimeSeriesChartLineType | number | number[];
lineWidth: number;
startSymbol: TimeSeriesChartShape;
startSymbol: EChartsShape;
startSymbolSize: number;
endSymbol: TimeSeriesChartShape;
endSymbol: EChartsShape;
endSymbolSize: number;
showLabel: boolean;
labelPosition: ThresholdLabelPosition;
labelFont: Font;
labelColor: string;
enableLabelBackground: boolean;
labelBackground: string;
additionalLabelOption?: {[key: string]: any};
}
@ -509,9 +483,9 @@ export const timeSeriesChartThresholdDefaultSettings: TimeSeriesChartThreshold =
lineColor: timeSeriesChartColorScheme['threshold.line'].light,
lineType: TimeSeriesChartLineType.solid,
lineWidth: 1,
startSymbol: TimeSeriesChartShape.none,
startSymbol: EChartsShape.none,
startSymbolSize: 5,
endSymbol: TimeSeriesChartShape.arrow,
endSymbol: EChartsShape.arrow,
endSymbolSize: 5,
showLabel: true,
labelPosition: ThresholdLabelPosition.end,
@ -523,7 +497,9 @@ export const timeSeriesChartThresholdDefaultSettings: TimeSeriesChartThreshold =
weight: '400',
lineHeight: '1'
},
labelColor: timeSeriesChartColorScheme['threshold.label'].light
labelColor: timeSeriesChartColorScheme['threshold.label'].light,
enableLabelBackground: false,
labelBackground: 'rgba(255,255,255,0.56)'
};
export enum TimeSeriesChartNoAggregationBarWidthStrategy {
@ -744,8 +720,10 @@ export interface LineSeriesSettings {
pointLabelPosition: SeriesLabelPosition;
pointLabelFont: Font;
pointLabelColor: string;
enablePointLabelBackground: boolean;
pointLabelBackground: string;
pointLabelFormatter?: string | LabelFormatterCallback;
pointShape: TimeSeriesChartShape;
pointShape: EChartsShape;
pointSize: number;
fillAreaSettings: SeriesFillSettings;
}
@ -758,6 +736,8 @@ export interface BarSeriesSettings {
labelPosition: SeriesLabelPosition | BuiltinTextPosition;
labelFont: Font;
labelColor: string;
enableLabelBackground: boolean;
labelBackground: string;
labelFormatter?: string | LabelFormatterCallback;
labelLayout?: LabelLayoutOption | LabelLayoutOptionCallback;
additionalLabelOption?: {[key: string]: any};
@ -797,7 +777,9 @@ export const timeSeriesChartKeyDefaultSettings: TimeSeriesChartKeySettings = {
lineHeight: '1'
},
pointLabelColor: timeSeriesChartColorScheme['series.label'].light,
pointShape: TimeSeriesChartShape.emptyCircle,
enablePointLabelBackground: false,
pointLabelBackground: 'rgba(255,255,255,0.56)',
pointShape: EChartsShape.emptyCircle,
pointSize: 4,
fillAreaSettings: {
type: SeriesFillType.none,
@ -823,6 +805,8 @@ export const timeSeriesChartKeyDefaultSettings: TimeSeriesChartKeySettings = {
lineHeight: '1'
},
labelColor: timeSeriesChartColorScheme['series.label'].light,
enableLabelBackground: false,
labelBackground: 'rgba(255,255,255,0.56)',
backgroundSettings: {
type: SeriesFillType.none,
opacity: 0.4,
@ -1080,7 +1064,7 @@ export const calculateThresholdsOffset = (chart: ECharts,
const result: [number, number] = [0, 0];
for (const item of thresholdItems) {
const yAxis = yAxisList[item.yAxisIndex];
const offset = measureThresholdLabelOffset(chart, yAxis.id, item.id, item.value);
const offset = measureThresholdOffset(chart, yAxis.id, item.id, item.value);
result[0] = Math.max(result[0], offset[0]);
result[1] = Math.max(result[1], offset[1]);
}
@ -1141,6 +1125,11 @@ const generateChartThresholds = (thresholdItems: TimeSeriesChartThresholdItem[])
}
}
};
if (item.settings.enableLabelBackground) {
seriesOption.markLine.label.backgroundColor = item.settings.labelBackground;
seriesOption.markLine.label.padding = [4, 5];
seriesOption.markLine.label.borderRadius = 4;
}
if (item.settings.additionalLabelOption) {
seriesOption.markLine.label = {...seriesOption.markLine.label, ...item.settings.additionalLabelOption};
}
@ -1259,7 +1248,7 @@ export const updateDarkMode = (options: EChartsOption, settings: TimeSeriesChart
} else {
if (item.barRenderContext?.labelOption?.show) {
const barSettings = item.dataKey.settings as BarSeriesSettings;
item.barRenderContext.labelOption.rich.value.color = prepareChartThemeColor(barSettings.labelColor,
(item.barRenderContext.labelOption.rich.value as any).fill = prepareChartThemeColor(barSettings.labelColor,
darkMode, 'series.label');
}
}
@ -1303,8 +1292,10 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem,
const lineSeriesOption = seriesOption as LineSeriesOption;
lineSeriesOption.type = 'line';
lineSeriesOption.label = createSeriesLabelOption(item, lineSettings.showPointLabel,
lineSettings.pointLabelFont, lineSettings.pointLabelColor, lineSettings.pointLabelPosition,
lineSettings.pointLabelFormatter, darkMode);
lineSettings.pointLabelFont, lineSettings.pointLabelColor,
lineSettings.enablePointLabelBackground, lineSettings.pointLabelBackground,
lineSettings.pointLabelPosition,
lineSettings.pointLabelFormatter, false, darkMode);
lineSeriesOption.step = lineSettings.step ? lineSettings.stepType : false;
lineSeriesOption.smooth = lineSettings.smooth;
if (lineSettings.smooth) {
@ -1345,7 +1336,8 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem,
}
item.barRenderContext.visualSettings = barVisualSettings;
item.barRenderContext.labelOption = createSeriesLabelOption(item, barSettings.showLabel,
barSettings.labelFont, barSettings.labelColor, barSettings.labelPosition, barSettings.labelFormatter, darkMode);
barSettings.labelFont, barSettings.labelColor, barSettings.enableLabelBackground, barSettings.labelBackground,
barSettings.labelPosition, barSettings.labelFormatter, true, darkMode);
item.barRenderContext.additionalLabelOption = barSettings.additionalLabelOption;
barSeriesOption.renderItem = (params, api) =>
renderTimeSeriesBar(params, api, item.barRenderContext);
@ -1357,12 +1349,15 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem,
};
const createSeriesLabelOption = (item: TimeSeriesChartDataItem, show: boolean,
labelFont: Font, labelColor: string, position: SeriesLabelPosition | BuiltinTextPosition,
labelFont: Font, labelColor: string,
enableBackground: boolean, labelBackground: string,
position: SeriesLabelPosition | BuiltinTextPosition,
labelFormatter: string | LabelFormatterCallback,
labelColorFill: boolean,
darkMode: boolean): SeriesLabelOption => {
let labelStyle: ComponentStyle = {};
if (show) {
labelStyle = createChartTextStyle(labelFont, labelColor, darkMode, 'series.label');
labelStyle = createChartTextStyle(labelFont, labelColor, darkMode, 'series.label', labelColorFill);
}
let formatter: LabelFormatterCallback;
if (isFunction(labelFormatter)) {
@ -1386,7 +1381,7 @@ const createSeriesLabelOption = (item: TimeSeriesChartDataItem, show: boolean,
return `{value|${value}}`;
};
}
return {
const labelOption: SeriesLabelOption = {
show,
position,
formatter,
@ -1394,13 +1389,23 @@ const createSeriesLabelOption = (item: TimeSeriesChartDataItem, show: boolean,
value: labelStyle
}
};
if (enableBackground) {
labelOption.backgroundColor = labelBackground;
labelOption.padding = [4, 5];
labelOption.borderRadius = 4;
}
return labelOption;
};
const createChartTextStyle = (font: Font, color: string, darkMode: boolean, colorKey?: string): ComponentStyle => {
const createChartTextStyle = (font: Font, color: string, darkMode: boolean, colorKey?: string, fill = false): ComponentStyle => {
const style = textStyle(font);
delete style.lineHeight;
style.fontSize = font.size;
style.color = prepareChartThemeColor(color, darkMode, colorKey);
if (fill) {
style.fill = prepareChartThemeColor(color, darkMode, colorKey);
} else {
style.color = prepareChartThemeColor(color, darkMode, colorKey);
}
return style;
};

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

@ -32,7 +32,6 @@ import {
TimeSeriesChartNoAggregationBarWidthStrategy,
TimeSeriesChartSeriesType,
TimeSeriesChartSettings,
TimeSeriesChartShape,
TimeSeriesChartThreshold,
timeSeriesChartThresholdDefaultSettings,
TimeSeriesChartThresholdItem,
@ -49,7 +48,7 @@ import {
calculateYAxisWidth,
ECharts,
echartsModule,
EChartsOption,
EChartsOption, EChartsShape,
echartsTooltipFormatter,
EChartsTooltipTrigger,
getAxisExtent,
@ -88,7 +87,7 @@ export class TbTimeSeriesChart {
settings.type = TimeSeriesChartSeriesType.line;
settings.lineSettings.showLine = false;
settings.lineSettings.showPoints = true;
settings.lineSettings.pointShape = TimeSeriesChartShape.circle;
settings.lineSettings.pointShape = EChartsShape.circle;
settings.lineSettings.pointSize = 8;
}
return settings;
@ -738,7 +737,7 @@ export class TbTimeSeriesChart {
private minTopOffset(): number {
const showTickLabels =
!!this.yAxisList.find(yAxis => yAxis.settings.show && yAxis.settings.showTickLabels);
return (this.topPointLabels) ? 20 :
return (this.topPointLabels) ? 25 :
(showTickLabels ? 10 : 5);
}

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

@ -17,29 +17,175 @@
-->
<ng-container [formGroup]="rangeChartWidgetSettingsForm">
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.range-chart.range-chart-card-style</div>
<div class="tb-form-panel-title" translate>widgets.range-chart.range-chart-style</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="dataZoom">
{{ 'widgets.range-chart.data-zoom' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-row space-between">
<div>{{ 'widgets.range-chart.range-colors' | translate }}</div>
<tb-color-range-settings formControlName="rangeColors">
</tb-color-range-settings>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.range-chart.range-chart-appearance</div>
<div class="tb-form-row space-between">
<div>{{ 'widgets.range-chart.range-colors' | translate }}</div>
<tb-color-range-settings formControlName="rangeColors">
</tb-color-range-settings>
</div>
<div class="tb-form-row space-between">
<div>{{ 'widgets.range-chart.out-of-range-color' | translate }}</div>
<tb-color-input asBoxInput
colorClearButton
formControlName="outOfRangeColor">
</tb-color-input>
</div>
<div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="showRangeThresholds">
{{ 'widgets.range-chart.show-range-thresholds' | translate }}
</mat-slide-toggle>
<tb-time-series-chart-threshold-settings
boxButton
hideYAxis
[widgetConfig]="widgetConfig?.config"
title="widgets.range-chart.range-thresholds-settings"
formControlName="rangeThreshold">
</tb-time-series-chart-threshold-settings>
</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="fillArea">
{{ 'widgets.range-chart.fill-area' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-row space-between column-xs">
<div translate>widgets.range-chart.fill-area-opacity</div>
<mat-form-field class="medium-width number" appearance="outline" subscriptSizing="dynamic">
<input matInput type="number" formControlName="fillAreaOpacity" min="0" max="1"
step="0.1" placeholder="{{ 'widget-config.set' | translate }}"/>
</mat-form-field>
</div>
<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
disabledLineHeight
forceSizeUnit="px"
[previewText]="pointLabelPreviewFn">
</tb-font-settings>
<tb-color-input asBoxInput
colorClearButton
formControlName="pointLabelColor">
</tb-color-input>
</div>
</div>
<div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="enablePointLabelBackground">
{{ 'widgets.time-series-chart.series.point.point-label-background' | translate }}
</mat-slide-toggle>
<tb-color-input asBoxInput
colorClearButton
formControlName="pointLabelBackground">
</tb-color-input>
</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 echartsShapes" [value]="shape">
{{ echartsShapeTranslations.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>
</div>
<div class="tb-form-row space-between">
<div>{{ 'widgets.range-chart.out-of-range-color' | translate }}</div>
<tb-color-input asBoxInput
colorClearButton
formControlName="outOfRangeColor">
</tb-color-input>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.time-series-chart.axis.y-axis</div>
<tb-time-series-chart-axis-settings
formControlName="yAxis"
axisType="yAxis"
hideUnits
hideDecimals>
</tb-time-series-chart-axis-settings>
</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="fillArea">
{{ 'widgets.range-chart.fill-area' | translate }}
</mat-slide-toggle>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.time-series-chart.axis.x-axis</div>
<tb-time-series-chart-axis-settings
formControlName="xAxis"
axisType="xAxis">
</tb-time-series-chart-axis-settings>
</div>
<tb-time-series-chart-thresholds-panel
formControlName="thresholds"
[aliasController]="aliasController"
[dataKeyCallbacks]="dataKeyCallbacks"
[datasource]="datasource"
[widgetConfig]="widgetConfig?.config"
hideYAxis>
</tb-time-series-chart-thresholds-panel>
<div class="tb-form-panel tb-slide-toggle">
<mat-expansion-panel class="tb-settings" [expanded]="rangeChartWidgetSettingsForm.get('showLegend').value" [disabled]="!rangeChartWidgetSettingsForm.get('showLegend').value">
<mat-expansion-panel-header fxLayout="row wrap">
@ -139,16 +285,22 @@
</ng-template>
</mat-expansion-panel>
</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">
<div>{{ 'widget-config.card-padding' | translate }}</div>
<mat-form-field appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="padding" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<tb-time-series-chart-animation-settings
formControlName="animation">
</tb-time-series-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">
<div>{{ 'widget-config.card-padding' | translate }}</div>
<mat-form-field appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="padding" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
</div>
</div>
</ng-container>

127
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts

@ -16,17 +16,24 @@
import { Component, Injector } from '@angular/core';
import {
Datasource,
legendPositions,
legendPositionTranslationMap,
WidgetSettings,
WidgetSettingsComponent
} from '@shared/models/widget.models';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { formatValue } from '@core/utils';
import { rangeChartDefaultSettings } from '@home/components/widget/lib/chart/range-chart-widget.models';
import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-settings.models';
import {
lineSeriesStepTypes, lineSeriesStepTypeTranslations,
seriesLabelPositions, seriesLabelPositionTranslations,
timeSeriesLineTypes, timeSeriesLineTypeTranslations
} from '@home/components/widget/lib/chart/time-series-chart.models';
import { echartsShapes, echartsShapeTranslations } from '@home/components/widget/lib/chart/echarts-widget.models';
@Component({
selector: 'tb-range-chart-widget-settings',
@ -35,12 +42,39 @@ import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-s
})
export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent {
public get datasource(): Datasource {
const datasources: Datasource[] = this.widgetConfig.config.datasources;
if (datasources && datasources.length) {
return datasources[0];
} else {
return null;
}
}
lineSeriesStepTypes = lineSeriesStepTypes;
lineSeriesStepTypeTranslations = lineSeriesStepTypeTranslations;
timeSeriesLineTypes = timeSeriesLineTypes;
timeSeriesLineTypeTranslations = timeSeriesLineTypeTranslations;
seriesLabelPositions = seriesLabelPositions;
seriesLabelPositionTranslations = seriesLabelPositionTranslations;
echartsShapes = echartsShapes;
echartsShapeTranslations = echartsShapeTranslations;
legendPositions = legendPositions;
legendPositionTranslationMap = legendPositionTranslationMap;
rangeChartWidgetSettingsForm: UntypedFormGroup;
pointLabelPreviewFn = this._pointLabelPreviewFn.bind(this);
tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this);
tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this);
@ -64,7 +98,34 @@ export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent {
dataZoom: [settings.dataZoom, []],
rangeColors: [settings.rangeColors, []],
outOfRangeColor: [settings.outOfRangeColor, []],
showRangeThresholds: [settings.showRangeThresholds, []],
rangeThreshold: [settings.rangeThreshold, []],
fillArea: [settings.fillArea, []],
fillAreaOpacity: [settings.fillAreaOpacity, [Validators.min(0), Validators.max(1)]],
showLine: [settings.showLine, []],
step: [settings.step, []],
stepType: [settings.stepType, []],
smooth: [settings.smooth, []],
lineType: [settings.lineType, []],
lineWidth: [settings.lineWidth, [Validators.min(0)]],
showPoints: [settings.showPoints, []],
showPointLabel: [settings.showPointLabel, []],
pointLabelPosition: [settings.pointLabelPosition, []],
pointLabelFont: [settings.pointLabelFont, []],
pointLabelColor: [settings.pointLabelColor, []],
enablePointLabelBackground: [settings.enablePointLabelBackground, []],
pointLabelBackground: [settings.pointLabelBackground, []],
pointShape: [settings.pointShape, []],
pointSize: [settings.pointSize, [Validators.min(0)]],
yAxis: [settings.yAxis, []],
xAxis: [settings.xAxis, []],
thresholds: [settings.thresholds, []],
animation: [settings.animation, []],
showLegend: [settings.showLegend, []],
legendPosition: [settings.legendPosition, []],
@ -89,14 +150,69 @@ export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent {
}
protected validatorTriggers(): string[] {
return ['showLegend', 'showTooltip', 'tooltipShowDate'];
return ['showRangeThresholds', 'fillArea', 'showLine', 'step', 'showPointLabel', 'enablePointLabelBackground',
'showLegend', 'showTooltip', 'tooltipShowDate'];
}
protected updateValidators(emitEvent: boolean) {
const showRangeThresholds: boolean = this.rangeChartWidgetSettingsForm.get('showRangeThresholds').value;
const fillArea: boolean = this.rangeChartWidgetSettingsForm.get('fillArea').value;
const showLine: boolean = this.rangeChartWidgetSettingsForm.get('showLine').value;
const step: boolean = this.rangeChartWidgetSettingsForm.get('step').value;
const showPointLabel: boolean = this.rangeChartWidgetSettingsForm.get('showPointLabel').value;
const enablePointLabelBackground: boolean = this.rangeChartWidgetSettingsForm.get('enablePointLabelBackground').value;
const showLegend: boolean = this.rangeChartWidgetSettingsForm.get('showLegend').value;
const showTooltip: boolean = this.rangeChartWidgetSettingsForm.get('showTooltip').value;
const tooltipShowDate: boolean = this.rangeChartWidgetSettingsForm.get('tooltipShowDate').value;
if (showRangeThresholds) {
this.rangeChartWidgetSettingsForm.get('rangeThreshold').enable();
} else {
this.rangeChartWidgetSettingsForm.get('rangeThreshold').disable();
}
if (fillArea) {
this.rangeChartWidgetSettingsForm.get('fillAreaOpacity').enable();
} else {
this.rangeChartWidgetSettingsForm.get('fillAreaOpacity').disable();
}
if (showLine) {
this.rangeChartWidgetSettingsForm.get('step').enable({emitEvent: false});
if (step) {
this.rangeChartWidgetSettingsForm.get('stepType').enable();
this.rangeChartWidgetSettingsForm.get('smooth').disable();
} else {
this.rangeChartWidgetSettingsForm.get('stepType').disable();
this.rangeChartWidgetSettingsForm.get('smooth').enable();
}
this.rangeChartWidgetSettingsForm.get('lineType').enable();
this.rangeChartWidgetSettingsForm.get('lineWidth').enable();
} else {
this.rangeChartWidgetSettingsForm.get('step').disable({emitEvent: false});
this.rangeChartWidgetSettingsForm.get('stepType').disable();
this.rangeChartWidgetSettingsForm.get('smooth').disable();
this.rangeChartWidgetSettingsForm.get('lineType').disable();
this.rangeChartWidgetSettingsForm.get('lineWidth').disable();
}
if (showPointLabel) {
this.rangeChartWidgetSettingsForm.get('pointLabelPosition').enable();
this.rangeChartWidgetSettingsForm.get('pointLabelFont').enable();
this.rangeChartWidgetSettingsForm.get('pointLabelColor').enable();
this.rangeChartWidgetSettingsForm.get('enablePointLabelBackground').enable({emitEvent: false});
if (enablePointLabelBackground) {
this.rangeChartWidgetSettingsForm.get('pointLabelBackground').enable();
} else {
this.rangeChartWidgetSettingsForm.get('pointLabelBackground').disable();
}
} else {
this.rangeChartWidgetSettingsForm.get('pointLabelPosition').disable();
this.rangeChartWidgetSettingsForm.get('pointLabelFont').disable();
this.rangeChartWidgetSettingsForm.get('pointLabelColor').disable();
this.rangeChartWidgetSettingsForm.get('enablePointLabelBackground').disable({emitEvent: false});
this.rangeChartWidgetSettingsForm.get('pointLabelBackground').disable();
}
if (showLegend) {
this.rangeChartWidgetSettingsForm.get('legendPosition').enable();
this.rangeChartWidgetSettingsForm.get('legendLabelFont').enable();
@ -137,6 +253,12 @@ export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent {
}
}
private _pointLabelPreviewFn(): string {
const units: string = this.widgetConfig.config.units;
const decimals: number = this.widgetConfig.config.decimals;
return formatValue(22, decimals, units, false);
}
private _tooltipValuePreviewFn(): string {
const units: string = this.widgetConfig.config.units;
const decimals: number = this.widgetConfig.config.decimals;
@ -149,5 +271,4 @@ export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent {
processor.update(Date.now());
return processor.formatted;
}
}

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

@ -59,6 +59,15 @@
</tb-color-input>
</div>
</div>
<div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="enableLabelBackground">
{{ 'widgets.time-series-chart.series.bar.label-background' | translate }}
</mat-slide-toggle>
<tb-color-input asBoxInput
colorClearButton
formControlName="labelBackground">
</tb-color-input>
</div>
<tb-time-series-chart-fill-settings
formControlName="backgroundSettings"
title="widgets.time-series-chart.series.background"

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

@ -76,13 +76,16 @@ export class TimeSeriesChartBarSettingsComponent implements OnInit, ControlValue
labelPosition: [null, []],
labelFont: [null, []],
labelColor: [null, []],
enableLabelBackground: [null, []],
labelBackground: [null, []],
backgroundSettings: [null, []]
});
this.barSettingsFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
merge(this.barSettingsFormGroup.get('showBorder').valueChanges,
this.barSettingsFormGroup.get('showLabel').valueChanges)
this.barSettingsFormGroup.get('showLabel').valueChanges,
this.barSettingsFormGroup.get('enableLabelBackground').valueChanges)
.subscribe(() => {
this.updateValidators();
});
@ -116,6 +119,7 @@ export class TimeSeriesChartBarSettingsComponent implements OnInit, ControlValue
private updateValidators() {
const showBorder: boolean = this.barSettingsFormGroup.get('showBorder').value;
const showLabel: boolean = this.barSettingsFormGroup.get('showLabel').value;
const enableLabelBackground: boolean = this.barSettingsFormGroup.get('enableLabelBackground').value;
if (showBorder) {
this.barSettingsFormGroup.get('borderWidth').enable({emitEvent: false});
} else {
@ -125,10 +129,18 @@ export class TimeSeriesChartBarSettingsComponent implements OnInit, ControlValue
this.barSettingsFormGroup.get('labelPosition').enable({emitEvent: false});
this.barSettingsFormGroup.get('labelFont').enable({emitEvent: false});
this.barSettingsFormGroup.get('labelColor').enable({emitEvent: false});
this.barSettingsFormGroup.get('enableLabelBackground').enable({emitEvent: false});
if (enableLabelBackground) {
this.barSettingsFormGroup.get('labelBackground').enable({emitEvent: false});
} else {
this.barSettingsFormGroup.get('labelBackground').disable({emitEvent: false});
}
} else {
this.barSettingsFormGroup.get('labelPosition').disable({emitEvent: false});
this.barSettingsFormGroup.get('labelFont').disable({emitEvent: false});
this.barSettingsFormGroup.get('labelColor').disable({emitEvent: false});
this.barSettingsFormGroup.get('enableLabelBackground').disable({emitEvent: false});
this.barSettingsFormGroup.get('labelBackground').disable({emitEvent: false});
}
}

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

@ -97,12 +97,21 @@
</tb-color-input>
</div>
</div>
<div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="enablePointLabelBackground">
{{ 'widgets.time-series-chart.series.point.point-label-background' | translate }}
</mat-slide-toggle>
<tb-color-input asBoxInput
colorClearButton
formControlName="pointLabelBackground">
</tb-color-input>
</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 *ngFor="let shape of echartsShapes" [value]="shape">
{{ echartsShapeTranslations.get(shape) | translate }}
</mat-option>
</mat-select>
</mat-form-field>

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

@ -28,11 +28,11 @@ import {
lineSeriesStepTypeTranslations,
seriesLabelPositions,
seriesLabelPositionTranslations,
timeSeriesChartShapes,
timeSeriesChartShapeTranslations, TimeSeriesChartType,
TimeSeriesChartType,
timeSeriesLineTypes,
timeSeriesLineTypeTranslations
} from '@home/components/widget/lib/chart/time-series-chart.models';
import { echartsShapes, echartsShapeTranslations } from '@home/components/widget/lib/chart/echarts-widget.models';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { merge } from 'rxjs';
@ -67,9 +67,9 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu
seriesLabelPositionTranslations = seriesLabelPositionTranslations;
timeSeriesChartShapes = timeSeriesChartShapes;
echartsShapes = echartsShapes;
timeSeriesChartShapeTranslations = timeSeriesChartShapeTranslations;
echartsShapeTranslations = echartsShapeTranslations;
pointLabelPreviewFn = this._pointLabelPreviewFn.bind(this);
@ -103,6 +103,8 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu
pointLabelPosition: [null, []],
pointLabelFont: [null, []],
pointLabelColor: [null, []],
enablePointLabelBackground: [null, []],
pointLabelBackground: [null, []],
pointShape: [null, []],
pointSize: [null, [Validators.min(0)]],
fillAreaSettings: [null, []]
@ -112,7 +114,8 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu
});
merge(this.lineSettingsFormGroup.get('showLine').valueChanges,
this.lineSettingsFormGroup.get('step').valueChanges,
this.lineSettingsFormGroup.get('showPointLabel').valueChanges)
this.lineSettingsFormGroup.get('showPointLabel').valueChanges,
this.lineSettingsFormGroup.get('enablePointLabelBackground').valueChanges)
.subscribe(() => {
this.updateValidators();
});
@ -147,6 +150,7 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu
const showLine: boolean = this.lineSettingsFormGroup.get('showLine').value;
const step: boolean = this.lineSettingsFormGroup.get('step').value;
const showPointLabel: boolean = this.lineSettingsFormGroup.get('showPointLabel').value;
const enablePointLabelBackground: boolean = this.lineSettingsFormGroup.get('enablePointLabelBackground').value;
if (showLine) {
this.lineSettingsFormGroup.get('step').enable({emitEvent: false});
if (step) {
@ -169,10 +173,18 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu
this.lineSettingsFormGroup.get('pointLabelPosition').enable({emitEvent: false});
this.lineSettingsFormGroup.get('pointLabelFont').enable({emitEvent: false});
this.lineSettingsFormGroup.get('pointLabelColor').enable({emitEvent: false});
this.lineSettingsFormGroup.get('enablePointLabelBackground').enable({emitEvent: false});
if (enablePointLabelBackground) {
this.lineSettingsFormGroup.get('pointLabelBackground').enable({emitEvent: false});
} else {
this.lineSettingsFormGroup.get('pointLabelBackground').disable({emitEvent: false});
}
} else {
this.lineSettingsFormGroup.get('pointLabelPosition').disable({emitEvent: false});
this.lineSettingsFormGroup.get('pointLabelFont').disable({emitEvent: false});
this.lineSettingsFormGroup.get('pointLabelColor').disable({emitEvent: false});
this.lineSettingsFormGroup.get('enablePointLabelBackground').disable({emitEvent: false});
this.lineSettingsFormGroup.get('pointLabelBackground').disable({emitEvent: false});
}
}

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

@ -27,8 +27,8 @@
<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 }}"/>
<input matInput type="number" formControlName="opacity" min="0" max="1"
step="0.1" placeholder="{{ 'widget-config.set' | translate }}"/>
</mat-form-field>
</div>
</ng-container>

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

@ -73,7 +73,7 @@ export class TimeSeriesChartFillSettingsComponent implements OnInit, ControlValu
ngOnInit(): void {
this.fillSettingsFormGroup = this.fb.group({
type: [null, []],
opacity: [null, [Validators.min(0), Validators.max(100)]],
opacity: [null, [Validators.min(0), Validators.max(1)]],
gradient: this.fb.group({
start: [null, [Validators.min(0), Validators.max(100)]],
end: [null, [Validators.min(0), Validators.max(100)]]

15
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html

@ -98,14 +98,13 @@
</mat-form-field>
</div>
<div class="tb-form-table-row-cell-buttons">
<button type="button"
mat-icon-button
#matButton
(click)="editThreshold($event, matButton)"
matTooltip="{{ 'widgets.time-series-chart.threshold.threshold-settings' | translate }}"
matTooltipPosition="above">
<mat-icon>settings</mat-icon>
</button>
<tb-time-series-chart-threshold-settings
[widgetConfig]="widgetConfig"
[yAxisIds]="yAxisIds"
[hideYAxis]="hideYAxis"
icon="settings"
[formControl]="thresholdSettingsFormControl">
</tb-time-series-chart-threshold-settings>
<button type="button"
mat-icon-button
(click)="thresholdRemoved.emit()"

74
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts

@ -19,11 +19,11 @@ import {
Component,
EventEmitter,
forwardRef,
Input, OnChanges,
Input,
OnChanges,
OnInit,
Output,
Renderer2, SimpleChanges,
ViewContainerRef,
SimpleChanges,
ViewEncapsulation
} from '@angular/core';
import {
@ -36,7 +36,8 @@ import {
} from '@angular/forms';
import {
TimeSeriesChartThreshold,
TimeSeriesChartThresholdType, TimeSeriesChartYAxisId,
TimeSeriesChartThresholdType,
TimeSeriesChartYAxisId,
timeSeriesThresholdTypes,
timeSeriesThresholdTypeTranslations
} from '@home/components/widget/lib/chart/time-series-chart.models';
@ -47,12 +48,7 @@ import { IAliasController } from '@core/api/widget-api.models';
import { DataKey, Datasource, DatasourceType, WidgetConfig } from '@shared/models/widget.models';
import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { MatButton } from '@angular/material/button';
import { TbPopoverService } from '@shared/components/popover.service';
import { deepClone } from '@core/utils';
import {
TimeSeriesChartThresholdSettingsPanelComponent
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component';
import { coerceBoolean } from '@shared/decorators/coercion';
@Component({
@ -117,12 +113,11 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso
entityKeyFormControl: UntypedFormControl;
thresholdSettingsFormControl: UntypedFormControl;
private propagateChange = (_val: any) => {};
constructor(private fb: UntypedFormBuilder,
private popoverService: TbPopoverService,
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef,
private thresholdsPanel: TimeSeriesChartThresholdsPanelComponent,
private cd: ChangeDetectorRef) {
}
@ -139,6 +134,7 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso
});
this.latestKeyFormControl = this.fb.control(null, [Validators.required]);
this.entityKeyFormControl = this.fb.control(null, [Validators.required]);
this.thresholdSettingsFormControl = this.fb.control(null);
this.thresholdFormGroup.valueChanges.subscribe(
() => this.updateModel()
);
@ -148,6 +144,18 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso
this.entityKeyFormControl.valueChanges.subscribe(
() => this.updateModel()
);
this.thresholdSettingsFormControl.valueChanges.subscribe((thresholdSettings: Partial<TimeSeriesChartThreshold>) => {
this.modelValue = {...this.modelValue, ...thresholdSettings};
this.thresholdFormGroup.patchValue(
{
yAxisId: this.modelValue.yAxisId,
units: this.modelValue.units,
decimals: this.modelValue.decimals,
lineColor: this.modelValue.lineColor
},
{emitEvent: false});
this.propagateChange(this.modelValue);
});
this.thresholdFormGroup.get('type').valueChanges.subscribe(() => {
this.updateValidators();
});
@ -180,8 +188,10 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso
this.thresholdFormGroup.disable({emitEvent: false});
this.latestKeyFormControl.disable({emitEvent: false});
this.entityKeyFormControl.disable({emitEvent: false});
this.thresholdSettingsFormControl.disable({emitEvent: false});
} else {
this.thresholdFormGroup.enable({emitEvent: false});
this.thresholdSettingsFormControl.enable({emitEvent: false});
this.updateValidators();
}
}
@ -210,46 +220,12 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso
name: value.entityKey
}, {emitEvent: false});
}
this.thresholdSettingsFormControl.patchValue(deepClone(this.modelValue),
{emitEvent: false});
this.updateValidators();
this.cd.markForCheck();
}
editThreshold($event: Event, matButton: MatButton) {
if ($event) {
$event.stopPropagation();
}
const trigger = matButton._elementRef.nativeElement;
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const ctx: any = {
thresholdSettings: deepClone(this.modelValue),
widgetConfig: this.widgetConfig,
hideYAxis: this.hideYAxis,
yAxisIds: this.yAxisIds
};
const thresholdSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, TimeSeriesChartThresholdSettingsPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null,
ctx,
{},
{}, {}, true);
thresholdSettingsPanelPopover.tbComponentRef.instance.popover = thresholdSettingsPanelPopover;
thresholdSettingsPanelPopover.tbComponentRef.instance.thresholdSettingsApplied.subscribe((thresholdSettings) => {
thresholdSettingsPanelPopover.hide();
this.modelValue = {...this.modelValue, ...thresholdSettings};
this.thresholdFormGroup.patchValue(
{
yAxisId: this.modelValue.yAxisId,
units: this.modelValue.units,
decimals: this.modelValue.decimals,
lineColor: this.modelValue.lineColor
},
{emitEvent: false});
this.propagateChange(this.modelValue);
});
}
}
private updateValidators() {
const type: TimeSeriesChartThresholdType = this.thresholdFormGroup.get('type').value;
if (type === TimeSeriesChartThresholdType.constant) {
@ -288,6 +264,8 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso
this.modelValue.entityKey = entityKey?.name;
this.modelValue.entityKeyType = (entityKey?.type as any);
}
this.thresholdSettingsFormControl.patchValue(deepClone(this.modelValue),
{emitEvent: false});
this.propagateChange(this.modelValue);
}
}

19
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html

@ -16,7 +16,7 @@
-->
<div class="tb-threshold-settings-panel" [formGroup]="thresholdSettingsFormGroup">
<div class="tb-threshold-settings-title">{{ 'widgets.time-series-chart.threshold.threshold-settings' | translate }}</div>
<div class="tb-threshold-settings-title">{{ panelTitle | translate }}</div>
<div class="tb-threshold-settings-panel-content">
<div *ngIf="!hideYAxis" class="tb-form-row space-between column-xs">
<div translate>widgets.time-series-chart.axis.y-axis</div>
@ -64,6 +64,15 @@
</tb-color-input>
</div>
</div>
<div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="enableLabelBackground">
{{ 'widgets.time-series-chart.threshold.label-background' | translate }}
</mat-slide-toggle>
<tb-color-input asBoxInput
colorClearButton
formControlName="labelBackground">
</tb-color-input>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.time-series-chart.threshold.line-appearance</div>
<div class="tb-form-row space-between">
@ -94,8 +103,8 @@
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="startSymbol">
<mat-option *ngFor="let shape of timeSeriesChartShapes" [value]="shape">
{{ timeSeriesChartShapeTranslations.get(shape) | translate }}
<mat-option *ngFor="let shape of echartsShapes" [value]="shape">
{{ echartsShapeTranslations.get(shape) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
@ -111,8 +120,8 @@
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="endSymbol">
<mat-option *ngFor="let shape of timeSeriesChartShapes" [value]="shape">
{{ timeSeriesChartShapeTranslations.get(shape) | translate }}
<mat-option *ngFor="let shape of echartsShapes" [value]="shape">
{{ echartsShapeTranslations.get(shape) | translate }}
</mat-option>
</mat-select>
</mat-form-field>

40
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts

@ -18,15 +18,18 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } fro
import { TbPopoverComponent } from '@shared/components/popover.component';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import {
TimeSeriesChartShape,
timeSeriesChartShapes,
timeSeriesChartShapeTranslations,
TimeSeriesChartThreshold, TimeSeriesChartYAxisId,
TimeSeriesChartThreshold,
TimeSeriesChartYAxisId,
timeSeriesLineTypes,
timeSeriesLineTypeTranslations,
timeSeriesThresholdLabelPositions,
timeSeriesThresholdLabelPositionTranslations
} from '@home/components/widget/lib/chart/time-series-chart.models';
import {
EChartsShape,
echartsShapes,
echartsShapeTranslations
} from '@home/components/widget/lib/chart/echarts-widget.models';
import { merge } from 'rxjs';
import { WidgetConfig } from '@shared/models/widget.models';
import { formatValue, isDefinedAndNotNull } from '@core/utils';
@ -45,9 +48,9 @@ export class TimeSeriesChartThresholdSettingsPanelComponent implements OnInit {
timeSeriesLineTypeTranslations = timeSeriesLineTypeTranslations;
timeSeriesChartShapes = timeSeriesChartShapes;
echartsShapes = echartsShapes;
timeSeriesChartShapeTranslations = timeSeriesChartShapeTranslations;
echartsShapeTranslations = echartsShapeTranslations;
timeSeriesThresholdLabelPositions = timeSeriesThresholdLabelPositions;
@ -71,6 +74,9 @@ export class TimeSeriesChartThresholdSettingsPanelComponent implements OnInit {
@coerceBoolean()
hideYAxis = false;
@Input()
panelTitle = 'widgets.time-series-chart.threshold.threshold-settings';
@Output()
thresholdSettingsApplied = new EventEmitter<Partial<TimeSeriesChartThreshold>>();
@ -95,10 +101,13 @@ export class TimeSeriesChartThresholdSettingsPanelComponent implements OnInit {
showLabel: [this.thresholdSettings.showLabel, []],
labelPosition: [this.thresholdSettings.labelPosition, []],
labelFont: [this.thresholdSettings.labelFont, []],
labelColor: [this.thresholdSettings.labelColor, []]
labelColor: [this.thresholdSettings.labelColor, []],
enableLabelBackground: [this.thresholdSettings.enableLabelBackground, []],
labelBackground: [this.thresholdSettings.labelBackground, []]
}
);
merge(this.thresholdSettingsFormGroup.get('showLabel').valueChanges,
this.thresholdSettingsFormGroup.get('enableLabelBackground').valueChanges,
this.thresholdSettingsFormGroup.get('startSymbol').valueChanges,
this.thresholdSettingsFormGroup.get('endSymbol').valueChanges).subscribe(() => {
this.updateValidators();
@ -117,23 +126,32 @@ export class TimeSeriesChartThresholdSettingsPanelComponent implements OnInit {
private updateValidators() {
const showLabel: boolean = this.thresholdSettingsFormGroup.get('showLabel').value;
const startSymbol: TimeSeriesChartShape = this.thresholdSettingsFormGroup.get('startSymbol').value;
const endSymbol: TimeSeriesChartShape = this.thresholdSettingsFormGroup.get('endSymbol').value;
const enableLabelBackground: boolean = this.thresholdSettingsFormGroup.get('enableLabelBackground').value;
const startSymbol: EChartsShape = this.thresholdSettingsFormGroup.get('startSymbol').value;
const endSymbol: EChartsShape = this.thresholdSettingsFormGroup.get('endSymbol').value;
if (showLabel) {
this.thresholdSettingsFormGroup.get('labelPosition').enable({emitEvent: false});
this.thresholdSettingsFormGroup.get('labelFont').enable({emitEvent: false});
this.thresholdSettingsFormGroup.get('labelColor').enable({emitEvent: false});
this.thresholdSettingsFormGroup.get('enableLabelBackground').enable({emitEvent: false});
if (enableLabelBackground) {
this.thresholdSettingsFormGroup.get('labelBackground').enable({emitEvent: false});
} else {
this.thresholdSettingsFormGroup.get('labelBackground').disable({emitEvent: false});
}
} else {
this.thresholdSettingsFormGroup.get('labelPosition').disable({emitEvent: false});
this.thresholdSettingsFormGroup.get('labelFont').disable({emitEvent: false});
this.thresholdSettingsFormGroup.get('labelColor').disable({emitEvent: false});
this.thresholdSettingsFormGroup.get('enableLabelBackground').disable({emitEvent: false});
this.thresholdSettingsFormGroup.get('labelBackground').disable({emitEvent: false});
}
if (startSymbol === TimeSeriesChartShape.none) {
if (startSymbol === EChartsShape.none) {
this.thresholdSettingsFormGroup.get('startSymbolSize').disable({emitEvent: false});
} else {
this.thresholdSettingsFormGroup.get('startSymbolSize').enable({emitEvent: false});
}
if (endSymbol === TimeSeriesChartShape.none) {
if (endSymbol === EChartsShape.none) {
this.thresholdSettingsFormGroup.get('endSymbolSize').disable({emitEvent: false});
} else {
this.thresholdSettingsFormGroup.get('endSymbolSize').enable({emitEvent: false});

39
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.html

@ -0,0 +1,39 @@
<!--
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.
-->
<button *ngIf="boxButton; else iconButton"
type="button"
mat-stroked-button
class="tb-box-button"
[disabled]="disabled"
#matButton
(click)="openThresholdSettingsPopup($event, matButton)"
matTooltip="{{ title | translate }}"
matTooltipPosition="above">
<tb-icon matButtonIcon>{{icon}}</tb-icon>
</button>
<ng-template #iconButton>
<button type="button"
mat-icon-button
[disabled]="disabled"
#matButton
(click)="openThresholdSettingsPopup($event, matButton)"
matTooltip="{{ title | translate }}"
matTooltipPosition="above">
<tb-icon>{{icon}}</tb-icon>
</button>
</ng-template>

124
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.ts

@ -0,0 +1,124 @@
///
/// 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, Renderer2, ViewContainerRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { MatButton } from '@angular/material/button';
import { TbPopoverService } from '@shared/components/popover.service';
import { deepClone } from '@core/utils';
import { coerceBoolean } from '@shared/decorators/coercion';
import { WidgetConfig } from '@shared/models/widget.models';
import {
TimeSeriesChartThreshold,
TimeSeriesChartYAxisId
} from '@home/components/widget/lib/chart/time-series-chart.models';
import {
TimeSeriesChartThresholdSettingsPanelComponent
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component';
@Component({
selector: 'tb-time-series-chart-threshold-settings',
templateUrl: './time-series-chart-threshold-settings.component.html',
styleUrls: [],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TimeSeriesChartThresholdSettingsComponent),
multi: true
}
]
})
export class TimeSeriesChartThresholdSettingsComponent implements OnInit, ControlValueAccessor {
@Input()
disabled: boolean;
@Input()
widgetConfig: WidgetConfig;
@Input()
yAxisIds: TimeSeriesChartYAxisId[];
@Input()
@coerceBoolean()
hideYAxis = false;
@Input()
@coerceBoolean()
boxButton = false;
@Input()
icon = 'settings';
@Input()
title = 'widgets.time-series-chart.threshold.threshold-settings';
private modelValue: Partial<TimeSeriesChartThreshold>;
private propagateChange = null;
constructor(private popoverService: TbPopoverService,
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef) {}
ngOnInit(): void {
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
writeValue(value: Partial<TimeSeriesChartThreshold>): void {
this.modelValue = value;
}
openThresholdSettingsPopup($event: Event, matButton: MatButton) {
if ($event) {
$event.stopPropagation();
}
const trigger = matButton._elementRef.nativeElement;
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const ctx: any = {
thresholdSettings: deepClone(this.modelValue),
panelTitle: this.title,
widgetConfig: this.widgetConfig,
hideYAxis: this.hideYAxis,
yAxisIds: this.yAxisIds
};
const thresholdSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, TimeSeriesChartThresholdSettingsPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null,
ctx,
{},
{}, {}, true);
thresholdSettingsPanelPopover.tbComponentRef.instance.popover = thresholdSettingsPanelPopover;
thresholdSettingsPanelPopover.tbComponentRef.instance.thresholdSettingsApplied.subscribe((thresholdSettings) => {
thresholdSettingsPanelPopover.hide();
this.modelValue = thresholdSettings;
this.propagateChange(this.modelValue);
});
}
}
}

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

@ -130,6 +130,9 @@ import {
import {
TimeSeriesChartFillSettingsComponent
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component';
import {
TimeSeriesChartThresholdSettingsComponent
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component';
@NgModule({
declarations: [
@ -178,6 +181,7 @@ import {
TimeSeriesChartYAxisSettingsPanelComponent,
TimeSeriesChartAnimationSettingsComponent,
TimeSeriesChartFillSettingsComponent,
TimeSeriesChartThresholdSettingsComponent,
DataKeyInputComponent,
EntityAliasInputComponent
],
@ -232,6 +236,7 @@ import {
TimeSeriesChartYAxisSettingsPanelComponent,
TimeSeriesChartAnimationSettingsComponent,
TimeSeriesChartFillSettingsComponent,
TimeSeriesChartThresholdSettingsComponent,
DataKeyInputComponent,
EntityAliasInputComponent
],

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

@ -6176,9 +6176,10 @@
"range-colors": "Range colors",
"out-of-range-color": "Out of range color",
"show-range-thresholds": "Show range thresholds",
"range-thresholds-settings": "Range thresholds settings",
"fill-area": "Fill area",
"fill-area-opacity": "Fill area opacity",
"range-chart-card-style": "Range chart card style"
"range-chart-style": "Range chart style"
},
"rpc": {
"value-settings": "Value settings",
@ -6772,7 +6773,8 @@
"label-position-inside-middle-bottom": "Inside middle bottom",
"label-position-inside-end": "Inside end",
"label-position-inside-end-top": "Inside end top",
"label-position-inside-end-bottom": "Inside end bottom"
"label-position-inside-end-bottom": "Inside end bottom",
"label-background": "Label background"
},
"axis": {
"axes": "Axes",
@ -6853,6 +6855,7 @@
"show-points": "Show points",
"point-label": "Point label",
"point-label-hint": "Display label with value over the series point.",
"point-label-background": "Point label background",
"point-shape": "Point shape",
"point-size": "Point size"
},
@ -6861,7 +6864,8 @@
"border-width": "Border width",
"border-radius": "Border radius",
"label": "Label",
"label-hint": "Display label with value over the bar."
"label-hint": "Display label with value over the bar.",
"label-background": "Label background"
}
}
},

2
ui-ngx/src/assets/locale/locale.constant-pl_PL.json

@ -5924,7 +5924,7 @@
"range-colors":"Kolory zakresu",
"out-of-range-color":"Kolor poza zakresem",
"fill-area":"Wypełnij obszar",
"range-chart-card-style":"Styl karty wykresu zakresu"
"range-chart-style":"Styl wykresu zakresu"
},
"rpc":{
"value-settings":"Ustawienia wartości",

2
ui-ngx/src/assets/locale/locale.constant-zh_CN.json

@ -5491,7 +5491,7 @@
"range-colors": "范围颜色",
"out-of-range-color": "超出范围颜色",
"fill-area": "填充区域",
"range-chart-card-style": "范围图表卡片样式"
"range-chart-style": "范围图样式"
},
"rpc": {
"value-settings": "值设置",

Loading…
Cancel
Save