From 7900d5f29cdc70dbf134335b4b66fd37dd91393f Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 27 Mar 2024 18:23:12 +0200 Subject: [PATCH 1/8] UI: Refactor bar chart with labels widget to use time series chart class. --- ...ar-chart-with-labels-widget.component.html | 12 +- .../bar-chart-with-labels-widget.component.ts | 393 ++---------------- .../bar-chart-with-labels-widget.models.ts | 107 ++++- .../widget/lib/chart/echarts-widget.models.ts | 43 ++ .../lib/chart/time-series-chart-bar.models.ts | 27 +- .../lib/chart/time-series-chart.models.ts | 67 ++- .../widget/lib/chart/time-series-chart.ts | 9 +- 7 files changed, 262 insertions(+), 396 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.html index 8d660a7884..c58dfdc90d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.html @@ -22,13 +22,13 @@
-
+
-
-
{{ legendItem.label }}
+
+
{{ legendKey.label }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts index e2779f2821..5aaa2671ea 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts @@ -28,45 +28,18 @@ import { ViewEncapsulation } from '@angular/core'; import { WidgetContext } from '@home/models/widget-component.models'; -import { - backgroundStyle, - ComponentStyle, - DateFormatProcessor, - overlayStyle, - textStyle -} from '@shared/models/widget-settings.models'; -import { ResizeObserver } from '@juggle/resize-observer'; -import { formatValue } from '@core/utils'; +import { backgroundStyle, ComponentStyle, overlayStyle, textStyle } from '@shared/models/widget-settings.models'; import { Observable } from 'rxjs'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; import { barChartWithLabelsDefaultSettings, + barChartWithLabelsTimeSeriesKeySettings, + barChartWithLabelsTimeSeriesSettings, BarChartWithLabelsWidgetSettings } from '@home/components/widget/lib/chart/bar-chart-with-labels-widget.models'; - -import * as echarts from 'echarts/core'; -import { CustomSeriesOption } from 'echarts/charts'; -import { CallbackDataParams, CustomSeriesRenderItem, LabelLayoutOptionCallback } from 'echarts/types/dist/shared'; - -import { - ECharts, - echartsModule, - EChartsOption, - EChartsSeriesItem, - echartsTooltipFormatter, timeAxisBandWidthCalculator, - toNamedData -} from '@home/components/widget/lib/chart/echarts-widget.models'; -import { AggregationType, IntervalMath } from '@shared/models/time/time.models'; - -type BarChartDataItem = EChartsSeriesItem; - -interface BarChartLegendItem { - id: string; - color: string; - label: string; - enabled: boolean; -} +import { TbTimeSeriesChart } from '@home/components/widget/lib/chart/time-series-chart'; +import { DataKey } from '@shared/models/widget.models'; @Component({ selector: 'tb-bar-chart-with-labels-widget', @@ -93,29 +66,11 @@ export class BarChartWithLabelsWidgetComponent implements OnInit, OnDestroy, Aft backgroundStyle$: Observable; overlayStyle: ComponentStyle = {}; - legendItems: BarChartLegendItem[]; + legendKeys: DataKey[]; legendLabelStyle: ComponentStyle; disabledLegendLabelStyle: ComponentStyle; - private get noAggregation(): boolean { - return this.ctx.defaultSubscription.timeWindowConfig?.aggregation?.type === AggregationType.NONE; - } - - private shapeResize$: ResizeObserver; - - private decimals = 0; - private units = ''; - - private dataItems: BarChartDataItem[] = []; - - private drawChartPending = false; - private barChart: ECharts; - private barChartOptions: EChartsOption; - - private tooltipDateFormat: DateFormatProcessor; - - private barRenderItem: CustomSeriesRenderItem; - private barLabelLayoutCallback: LabelLayoutOptionCallback; + private timeSeriesChart: TbTimeSeriesChart; constructor(private imagePipe: ImagePipe, private sanitizer: DomSanitizer, @@ -127,356 +82,62 @@ export class BarChartWithLabelsWidgetComponent implements OnInit, OnDestroy, Aft this.ctx.$scope.barChartWidget = this; this.settings = {...barChartWithLabelsDefaultSettings, ...this.ctx.settings}; - this.decimals = this.ctx.decimals; - this.units = this.ctx.units; - this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); this.overlayStyle = overlayStyle(this.settings.background.overlay); this.showLegend = this.settings.showLegend; if (this.showLegend) { - this.legendItems = []; + this.legendKeys = []; this.legendClass = `legend-${this.settings.legendPosition}`; this.legendLabelStyle = textStyle(this.settings.legendLabelFont); this.disabledLegendLabelStyle = textStyle(this.settings.legendLabelFont); this.legendLabelStyle.color = this.settings.legendLabelColor; } - let counter = 0; - if (this.ctx.datasources.length) { - for (const datasource of this.ctx.datasources) { - const dataKeys = datasource.dataKeys; - for (const dataKey of dataKeys) { - const id = counter++; - const datasourceData = this.ctx.data ? this.ctx.data.find(d => d.dataKey === dataKey) : null; - const namedData = datasourceData?.data ? toNamedData(datasourceData.data) : []; - this.dataItems.push({ - id: id+'', - dataKey, - data: namedData, - enabled: true - }); - if (this.showLegend) { - this.legendItems.push( - { - id: id+'', - label: dataKey.label, - color: dataKey.color, - enabled: true - } - ); - } + const barSettings = barChartWithLabelsTimeSeriesKeySettings(this.settings, this.ctx.decimals); + for (const datasource of this.ctx.datasources) { + const dataKeys = datasource.dataKeys; + for (const dataKey of dataKeys) { + dataKey.settings = barSettings; + if (this.showLegend) { + this.legendKeys.push(dataKey); } } } - - if (this.settings.showTooltip && this.settings.tooltipShowDate) { - this.tooltipDateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.tooltipDateFormat); - } - - const barValueStyle: ComponentStyle = textStyle(this.settings.barValueFont); - delete barValueStyle.lineHeight; - barValueStyle.fontSize = this.settings.barValueFont.size; - barValueStyle.fill = this.settings.barValueColor; - - const barLabelStyle: ComponentStyle = textStyle(this.settings.barLabelFont); - delete barLabelStyle.lineHeight; - barLabelStyle.fontSize = this.settings.barLabelFont.size; - barLabelStyle.fill = this.settings.barLabelColor; - - this.barRenderItem = (params, api) => { - - const time = api.value(0) as number; - let start = api.value(2) as number; - const end = api.value(3) as number; - let interval = end - start; - if (!start || !end || !interval) { - interval = IntervalMath.numberValue(this.ctx.timeWindow.interval); - start = time - interval / 2; - } - const enabledDataItems = this.dataItems.filter(d => d.enabled); - const barInterval = interval / (enabledDataItems.length + 1); - const intervalGap = barInterval / 2; - - const index = enabledDataItems.findIndex(d => d.id === params.seriesId); - const value = api.value(1); - const startTime = start + intervalGap + barInterval * index; - const delta = barInterval; - const lowerLeft = api.coord([startTime, value >= 0 ? value : 0]); - const height = api.size([delta, value])[1]; - const width = api.size([delta, 10])[0]; - - const coordSys: {x: number; y: number; width: number; height: number} = params.coordSys as any; - - const rectShape = echarts.graphic.clipRectByRect({ - x: lowerLeft[0], - y: lowerLeft[1], - width, - height - }, { - x: coordSys.x, - y: coordSys.y, - width: coordSys.width, - height: coordSys.height - }); - - const zeroPos = api.coord([0, 0]); - const labelParts: string[] = []; - if (this.settings.showBarValue) { - const labelValue = formatValue(value, this.decimals, '', false); - labelParts.push(`{value|${labelValue}}`); - } - if (this.settings.showBarLabel) { - labelParts.push(`{label|${params.seriesName}}`); - } - const barLabel = labelParts.join(' '); - return rectShape && { - type: 'rect', - id: time + '', - shape: rectShape, - style: { - fill: api.visual('color'), - text: barLabel, - textPosition: 'insideBottom', - textRotation: Math.PI / 2, - textDistance: 15, - textStrokeWidth: 0, - textAlign: 'left', - textVerticalAlign: 'middle', - rich: { - value: barValueStyle, - label: barLabelStyle - } - }, - focus: 'series', - transition: 'all', - enterFrom: { - style: { opacity: 0 }, - shape: { height: 0, y: zeroPos[1] } - } - }; - }; - - this.barLabelLayoutCallback = (params) => { - if (params.rect.width - params.labelRect.width < 2) { - return { - y: '100000%', - }; - } else { - return { - hideOverlap: true - }; - } - }; } ngAfterViewInit() { - if (this.drawChartPending) { - this.drawChart(); - } + const settings = barChartWithLabelsTimeSeriesSettings(this.settings); + this.timeSeriesChart = new TbTimeSeriesChart(this.ctx, settings, this.chartShape.nativeElement, this.renderer); } ngOnDestroy() { - if (this.shapeResize$) { - this.shapeResize$.disconnect(); - } - if (this.barChart) { - this.barChart.dispose(); + if (this.timeSeriesChart) { + this.timeSeriesChart.destroy(); } } public onInit() { const borderRadius = this.ctx.$widgetElement.css('borderRadius'); this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; - if (this.chartShape) { - this.drawChart(); - } else { - this.drawChartPending = true; - } this.cd.detectChanges(); } public onDataUpdated() { - for (const item of this.dataItems) { - const datasourceData = this.ctx.data ? this.ctx.data.find(d => d.dataKey === item.dataKey) : null; - item.data = datasourceData?.data ? toNamedData(datasourceData.data) : []; - } - if (this.barChart) { - (this.barChartOptions.xAxis as any).min = this.ctx.defaultSubscription.timeWindow.minTime; - (this.barChartOptions.xAxis as any).max = this.ctx.defaultSubscription.timeWindow.maxTime; - (this.barChartOptions.xAxis as any).tbTimeWindow = this.ctx.defaultSubscription.timeWindow; - this.barChartOptions.series = this.updateSeries(); - this.barChart.setOption(this.barChartOptions); + if (this.timeSeriesChart) { + this.timeSeriesChart.update(); } } - private updateSeries(): Array { - const series: Array = []; - for (const item of this.dataItems) { - if (item.enabled) { - const seriesOption: CustomSeriesOption = { - type: 'custom', - id: item.id, - name: item.dataKey.label, - color: item.dataKey.color, - data: item.data, - renderItem: this.barRenderItem, - labelLayout: this.barLabelLayoutCallback, - dimensions: [ - {name: 'intervalStart', type: 'number'}, - {name: 'intervalEnd', type: 'number'} - ], - encode: { - intervalStart: 2, - intervalEnd: 3 - } - }; - series.push(seriesOption); - } - } - return series; + public onLegendKeyEnter(key: DataKey) { + this.timeSeriesChart.keyEnter(key); } - - public onLegendItemEnter(item: BarChartLegendItem) { - this.barChart.dispatchAction({ - type: 'highlight', - seriesId: item.id - }); - } - - public onLegendItemLeave(item: BarChartLegendItem) { - this.barChart.dispatchAction({ - type: 'downplay', - seriesId: item.id - }); - } - - public toggleLegendItem(item: BarChartLegendItem) { - const enable = !item.enabled; - const dataItem = this.dataItems.find(d => d.id === item.id); - if (dataItem) { - dataItem.enabled = enable; - if (!enable) { - this.barChart.dispatchAction({ - type: 'downplay', - seriesId: item.id - }); - } - this.barChartOptions.series = this.updateSeries(); - this.barChart.setOption(this.barChartOptions, {replaceMerge: ['series']}); - item.enabled = enable; - if (enable) { - this.barChart.dispatchAction({ - type: 'highlight', - seriesId: item.id - }); - } - } + public onLegendKeyLeave(key: DataKey) { + this.timeSeriesChart.keyLeave(key); } - private drawChart() { - echartsModule.init(); - this.barChart = echarts.init(this.chartShape.nativeElement, null, { - renderer: 'canvas', - }); - this.barChartOptions = { - tooltip: { - trigger: 'axis', - confine: true, - appendTo: 'body', - axisPointer: { - type: 'shadow' - }, - formatter: (params: CallbackDataParams[]) => { - if (this.settings.showTooltip) { - const focusedSeriesIndex = this.focusedSeriesIndex(); - return echartsTooltipFormatter(this.renderer, this.tooltipDateFormat, - this.settings, params, this.decimals, this.units, focusedSeriesIndex, null, - this.noAggregation ? null : this.ctx.timeWindow.interval); - } else { - return undefined; - } - }, - padding: [8, 12], - backgroundColor: this.settings.tooltipBackgroundColor, - borderWidth: 0, - extraCssText: `line-height: 1; backdrop-filter: blur(${this.settings.tooltipBackgroundBlur}px);` - }, - grid: { - containLabel: true, - top: '30', - left: 0, - right: 0, - bottom: 0 - }, - xAxis: { - type: 'time', - scale: true, - axisTick: { - show: false - }, - axisLabel: { - hideOverlap: true, - fontSize: 10 - }, - axisLine: { - onZero: false - }, - min: this.ctx.defaultSubscription.timeWindow.minTime, - max: this.ctx.defaultSubscription.timeWindow.maxTime, - bandWidthCalculator: timeAxisBandWidthCalculator - }, - yAxis: { - type: 'value', - axisLabel: { - formatter: (value: any) => formatValue(value, this.decimals, this.units, false) - } - } - }; - - (this.barChartOptions.xAxis as any).tbTimeWindow = this.ctx.defaultSubscription.timeWindow; - - this.barChartOptions.series = this.updateSeries(); - - this.barChart.setOption(this.barChartOptions); - - this.shapeResize$ = new ResizeObserver(() => { - this.onResize(); - }); - this.shapeResize$.observe(this.chartShape.nativeElement); - this.onResize(); - } - - private focusedSeriesIndex(): number { - let index = - 1; - const views: any[] = (this.barChart as any)._chartsViews; - if (views) { - const hasBlurredView = !!views.find(view => { - const graphicEls: any[] = view._data._graphicEls; - return !!graphicEls.find(el => el?.currentStates.includes('blur')); - }); - if (hasBlurredView) { - const focusedView = views.find(view => { - const graphicEls: any[] = view._data._graphicEls; - return !!graphicEls.find(el => !el?.currentStates.includes('blur')); - }); - if (focusedView) { - index = !!focusedView._model ? - focusedView._model.seriesIndex : (!!focusedView.__model ? focusedView.__model.seriesIndex : -1); - } - } - } - return index; - } - - private onResize() { - const width = this.barChart.getWidth(); - const height = this.barChart.getHeight(); - const shapeWidth = this.chartShape.nativeElement.offsetWidth; - const shapeHeight = this.chartShape.nativeElement.offsetHeight; - if (width !== shapeWidth || height !== shapeHeight) { - this.barChart.resize(); - } + public toggleLegendKey(key: DataKey) { + this.timeSeriesChart.toggleKey(key); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts index 99ea088c92..153a038d1e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts @@ -14,9 +14,23 @@ /// limitations under the License. /// -import { BackgroundSettings, BackgroundType, customDateFormat, Font } from '@shared/models/widget-settings.models'; +import { + BackgroundSettings, + BackgroundType, + ComponentStyle, + customDateFormat, + Font, textStyle +} 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 { DeepPartial } from '@shared/models/common'; +import { + TimeSeriesChartKeySettings, TimeSeriesChartSeriesType, + TimeSeriesChartSettings +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { CallbackDataParams, LabelLayoutOptionCallbackParams } from 'echarts/types/dist/shared'; +import { formatValue } from '@core/utils'; +import { LabelLayoutOption } from 'echarts/types/src/util/types'; export interface BarChartWithLabelsWidgetSettings extends EChartsTooltipWidgetSettings { showBarLabel: boolean; @@ -98,3 +112,94 @@ export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings } } }; + +export const barChartWithLabelsTimeSeriesSettings = (settings: BarChartWithLabelsWidgetSettings): DeepPartial => ({ + dataZoom: false, + yAxes: { + default: { + show: true, + showLine: false, + showTicks: false, + showTickLabels: true, + showSplitLines: true, + } + }, + xAxis: { + show: true, + showLine: true, + showTicks: false, + showTickLabels: true, + showSplitLines: false + }, + barWidthSettings: { + barGap: 0, + intervalGap: 0.5 + }, + showTooltip: settings.showTooltip, + tooltipValueFont: settings.tooltipValueFont, + tooltipValueColor: settings.tooltipValueColor, + tooltipShowDate: settings.tooltipShowDate, + tooltipDateInterval: settings.tooltipDateInterval, + tooltipDateFormat: settings.tooltipDateFormat, + tooltipDateFont: settings.tooltipDateFont, + tooltipDateColor: settings.tooltipDateColor, + tooltipBackgroundColor: settings.tooltipBackgroundColor, + tooltipBackgroundBlur: settings.tooltipBackgroundBlur, + tooltipShowFocusedSeries: true +}); + +export const barChartWithLabelsTimeSeriesKeySettings = (settings: BarChartWithLabelsWidgetSettings, + decimals: number): DeepPartial => { + const barValueStyle: ComponentStyle = textStyle(settings.barValueFont); + delete barValueStyle.lineHeight; + barValueStyle.fontSize = settings.barValueFont.size; + barValueStyle.fill = settings.barValueColor; + + const barLabelStyle: ComponentStyle = textStyle(settings.barLabelFont); + delete barLabelStyle.lineHeight; + barLabelStyle.fontSize = settings.barLabelFont.size; + barLabelStyle.fill = settings.barLabelColor; + return { + type: TimeSeriesChartSeriesType.bar, + barSettings: { + showBorder: false, + borderWidth: 0, + borderRadius: 0, + showLabel: settings.showBarLabel, + labelPosition: 'insideBottom', + labelFormatter: (params: CallbackDataParams): string => { + const labelParts: string[] = []; + if (settings.showBarValue) { + const labelValue = formatValue(params.value[1], decimals, '', false); + labelParts.push(`{value|${labelValue}}`); + } + if (settings.showBarLabel) { + labelParts.push(`{label|${params.seriesName}}`); + } + return labelParts.join(' '); + }, + labelLayout: (params: LabelLayoutOptionCallbackParams): LabelLayoutOption => { + if (params.rect.width - params.labelRect.width < 2) { + return { + y: '100000%', + }; + } else { + return { + hideOverlap: true + }; + } + }, + additionalLabelOption: { + textRotation: Math.PI / 2, + textDistance: 15, + textStrokeWidth: 0, + textAlign: 'left', + textVerticalAlign: 'middle', + rich: { + value: barValueStyle, + label: barLabelStyle + } + } + } + }; +}; 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 816f45a7e8..c9c4b319cc 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 @@ -266,6 +266,48 @@ export const getAxisExtent = (chart: ECharts, axisId: string): [number, number] return [0,0]; }; +let componentBlurredKey: string; + +const isBlurred = (model: SeriesModel): boolean => { + if (!componentBlurredKey) { + const innerKeys = Object.keys(model).filter(k => k.startsWith('__ec_inner_')); + for (const k of innerKeys) { + const obj = model[k]; + if (obj.hasOwnProperty('isBlured')) { + componentBlurredKey = k; + break; + } + } + } + if (componentBlurredKey) { + const obj = model[componentBlurredKey]; + return !!obj?.isBlured; + } else { + return false; + } +}; + +export const getFocusedSeriesIndex = (chart: ECharts): number => { + const model: GlobalModel = (chart as any).getModel(); + const models = model.queryComponents({mainType: 'series'}); + if (models) { + let hasBlurred = false; + let notBlurredIndex = -1; + for (const _model of models) { + const seriesModel = _model as SeriesModel; + const blurred = isBlurred(seriesModel); + if (!blurred) { + notBlurredIndex = seriesModel.seriesIndex; + } + hasBlurred = blurred || hasBlurred; + } + if (hasBlurred) { + return notBlurredIndex; + } + } + return -1; +}; + export const toNamedData = (data: DataSet): NamedDataSet => { if (!data?.length) { return []; @@ -304,6 +346,7 @@ export const tooltipTriggerTranslationMap = new Map return thresholdValue; }; -const generateChartThresholds = (thresholdItems: TimeSeriesChartThresholdItem[], darkMode: boolean): Array => { +const generateChartThresholds = (thresholdItems: TimeSeriesChartThresholdItem[]): Array => { const series: Array = []; for (const item of thresholdItems) { if (isDefinedAndNotNull(item.value)) { @@ -1215,7 +1231,8 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem, const lineSeriesOption = seriesOption as LineSeriesOption; lineSeriesOption.type = 'line'; lineSeriesOption.label = createSeriesLabelOption(item, lineSettings.showPointLabel, - lineSettings.pointLabelFont, lineSettings.pointLabelColor, lineSettings.pointLabelPosition, darkMode); + lineSettings.pointLabelFont, lineSettings.pointLabelColor, lineSettings.pointLabelPosition, + lineSettings.pointLabelFormatter, darkMode); lineSeriesOption.step = lineSettings.step ? lineSettings.stepType : false; lineSeriesOption.smooth = lineSettings.smooth; if (lineSettings.smooth) { @@ -1256,9 +1273,11 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem, } item.barRenderContext.visualSettings = barVisualSettings; item.barRenderContext.labelOption = createSeriesLabelOption(item, barSettings.showLabel, - barSettings.labelFont, barSettings.labelColor, barSettings.labelPosition, darkMode); + barSettings.labelFont, barSettings.labelColor, barSettings.labelPosition, barSettings.labelFormatter, darkMode); + item.barRenderContext.additionalLabelOption = barSettings.additionalLabelOption; barSeriesOption.renderItem = (params, api) => renderTimeSeriesBar(params, api, item.barRenderContext); + barSeriesOption.labelLayout = barSettings.labelLayout; } } seriesOption.data = item.data; @@ -1266,19 +1285,41 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem, }; const createSeriesLabelOption = (item: TimeSeriesChartDataItem, show: boolean, - labelFont: Font, labelColor: string, position: SeriesLabelPosition, + labelFont: Font, labelColor: string, position: SeriesLabelPosition | BuiltinTextPosition, + labelFormatter: string | LabelFormatterCallback, darkMode: boolean): SeriesLabelOption => { let labelStyle: ComponentStyle = {}; if (show) { labelStyle = createChartTextStyle(labelFont, labelColor, darkMode, 'series.label'); } + let formatter: LabelFormatterCallback; + if (labelFormatter) { + if (isFunction(labelFormatter)) { + formatter = labelFormatter as LabelFormatterCallback; + } else if (labelFormatter.length) { + const formatFunction = parseFunction(labelFormatter, ['value']); + formatter = (params): string => { + let result: string; + try { + result = formatFunction(params.value[1]); + } catch (_e) { + } + if (isUndefined(result)) { + result = formatValue(params.value[1], item.decimals, item.units, false); + } + return `{value|${result}}`; + }; + } else { + formatter = (params): string => { + const value = formatValue(params.value[1], item.decimals, item.units, false); + return `{value|${value}}`; + }; + } + } return { show, position, - formatter: (params): string => { - const value = formatValue(params.value[1], item.decimals, item.units, false); - return `{value|${value}}`; - }, + formatter, rich: { value: labelStyle } 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 bd9005c497..c14cd66b56 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 @@ -51,7 +51,7 @@ import { EChartsOption, echartsTooltipFormatter, EChartsTooltipTrigger, - getAxisExtent, + getAxisExtent, getFocusedSeriesIndex, measureXAxisNameHeight, measureYAxisNameWidth, toNamedData @@ -300,6 +300,8 @@ export class TbTimeSeriesChart { const targetBarWidth = noAggregationBarWidthSettings.strategy === TimeSeriesChartNoAggregationBarWidthStrategy.group ? noAggregationBarWidthSettings.groupWidth : noAggregationBarWidthSettings.barWidth; this.barRenderSharedContext = { + barGap: this.settings.barWidthSettings.barGap, + intervalGap: this.settings.barWidthSettings.intervalGap, timeInterval: this.ctx.timeWindow?.interval, noAggregationBarWidthStrategy: noAggregationBarWidthSettings.strategy, noAggregationWidthRelative: targetBarWidth.relative, @@ -492,8 +494,9 @@ export class TbTimeSeriesChart { }, formatter: (params: CallbackDataParams[]) => this.settings.showTooltip ? echartsTooltipFormatter(this.renderer, this.tooltipDateFormat, - this.settings, params, 0, '', -1, this.dataItems, - this.noAggregation ? null : this.ctx.timeWindow.interval) : undefined, + this.settings, params, 0, '', + this.settings.tooltipShowFocusedSeries ? getFocusedSeriesIndex(this.timeSeriesChart) : -1, + this.dataItems, this.noAggregation ? null : this.ctx.timeWindow.interval) : undefined, padding: [8, 12], backgroundColor: this.settings.tooltipBackgroundColor, borderWidth: 0, From 2fb604e398f82d24b98c04f607e03f10b5b14bfb Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 27 Mar 2024 20:21:28 +0200 Subject: [PATCH 2/8] UI: Refactor range chart widget to use time series chart class. --- .../lib/chart/range-chart-widget.component.ts | 318 +++++------------- .../lib/chart/time-series-chart.models.ts | 74 +++- .../widget/lib/chart/time-series-chart.ts | 39 ++- 3 files changed, 193 insertions(+), 238 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts index 1ef756f1f7..afc2c720f9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts @@ -32,48 +32,38 @@ import { backgroundStyle, ColorRange, ComponentStyle, - DateFormatProcessor, filterIncludingColorRanges, getDataKey, overlayStyle, sortedColorRange, textStyle } from '@shared/models/widget-settings.models'; -import { ResizeObserver } from '@juggle/resize-observer'; -import * as echarts from 'echarts/core'; -import { formatValue, isDefinedAndNotNull, isNumber } from '@core/utils'; +import { isDefinedAndNotNull, isNumber } from '@core/utils'; import { rangeChartDefaultSettings, RangeChartWidgetSettings } from './range-chart-widget.models'; import { Observable } from 'rxjs'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; +import { DeepPartial } from '@shared/models/common'; import { - ECharts, - echartsModule, - EChartsOption, - echartsTooltipFormatter, timeAxisBandWidthCalculator, - toNamedData -} from '@home/components/widget/lib/chart/echarts-widget.models'; -import { CallbackDataParams } from 'echarts/types/dist/shared'; -import { AggregationType } from '@shared/models/time/time.models'; - -interface VisualPiece { - lt?: number; - gt?: number; - lte?: number; - gte?: number; - value?: number; - color?: string; -} + createTimeSeriesChartVisualMapPiece, + SeriesFillType, + TimeSeriesChartKeySettings, + TimeSeriesChartSettings, + TimeSeriesChartShape, + TimeSeriesChartThreshold, + TimeSeriesChartThresholdType, TimeSeriesChartVisualMapPiece +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { TbTimeSeriesChart } from '@home/components/widget/lib/chart/time-series-chart'; interface RangeItem { index: number; from?: number; to?: number; - piece: VisualPiece; color: string; label: string; visible: boolean; enabled: boolean; + piece: TimeSeriesChartVisualMapPiece; } const rangeItemLabel = (from?: number, to?: number): string => { @@ -92,25 +82,6 @@ const rangeItemLabel = (from?: number, to?: number): string => { } }; -const toVisualPiece = (color: string, from?: number, to?: number): VisualPiece => { - const piece: VisualPiece = { - color - }; - if (isNumber(from) && isNumber(to)) { - if (from === to) { - piece.value = from; - } else { - piece.gte = from; - piece.lt = to; - } - } else if (isNumber(from)) { - piece.gte = from; - } else if (isNumber(to)) { - piece.lt = to; - } - return piece; -}; - const toRangeItems = (colorRanges: Array): RangeItem[] => { const rangeItems: RangeItem[] = []; let counter = 0; @@ -134,7 +105,7 @@ const toRangeItems = (colorRanges: Array): RangeItem[] => { from, to, label: rangeItemLabel(from, to), - piece: toVisualPiece(range.color, from, to) + piece: createTimeSeriesChartVisualMapPiece(range.color, from, to) } ); if (!isNumber(from) || !isNumber(to)) { @@ -198,23 +169,12 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn disabledLegendLabelStyle: ComponentStyle; visibleRangeItems: RangeItem[]; - private get noAggregation(): boolean { - return this.ctx.defaultSubscription.timeWindowConfig?.aggregation?.type === AggregationType.NONE; - } - - private rangeItems: RangeItem[]; - - private shapeResize$: ResizeObserver; - private decimals = 0; private units = ''; - private drawChartPending = false; - private rangeChart: ECharts; - private rangeChartOptions: EChartsOption; - private selectedRanges: {[key: number]: boolean} = {}; + private rangeItems: RangeItem[]; - private tooltipDateFormat: DateFormatProcessor; + private timeSeriesChart: TbTimeSeriesChart; constructor(private imagePipe: ImagePipe, private sanitizer: DomSanitizer, @@ -235,16 +195,25 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn if (dataKey?.units) { this.units = dataKey.units; } - + if (dataKey) { + dataKey.settings = { + type: 'line', + lineSettings: { + showLine: true, + smooth: false, + showPoints: false, + fillAreaSettings: { + type: this.settings.fillArea ? 'default' : SeriesFillType.none + } + } + } as DeepPartial; + } this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); this.overlayStyle = overlayStyle(this.settings.background.overlay); this.rangeItems = toRangeItems(this.settings.rangeColors); this.visibleRangeItems = this.rangeItems.filter(item => item.visible); - for (const range of this.rangeItems) { - this.selectedRanges[range.index] = true; - } this.showLegend = this.settings.showLegend && !!this.rangeItems.length; @@ -254,193 +223,90 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn this.disabledLegendLabelStyle = textStyle(this.settings.legendLabelFont); this.legendLabelStyle.color = this.settings.legendLabelColor; } - - if (this.settings.showTooltip && this.settings.tooltipShowDate) { - this.tooltipDateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.tooltipDateFormat); - } } ngAfterViewInit() { - if (this.drawChartPending) { - this.drawChart(); - } + const thresholds: DeepPartial[] = getMarkPoints(this.rangeItems).map(item => ({ + type: TimeSeriesChartThresholdType.constant, + yAxisId: 'default', + units: this.units, + decimals: this.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 + } as DeepPartial)); + const settings: DeepPartial = { + dataZoom: this.settings.dataZoom, + thresholds, + yAxes: { + default: { + show: true, + showLine: false, + showTicks: false, + showTickLabels: true, + showSplitLines: true, + decimals: this.decimals, + units: this.units + } + }, + xAxis: { + show: true, + showLine: true, + showTicks: true, + showTickLabels: true, + showSplitLines: false + }, + visualMapSettings: { + outOfRangeColor: this.settings.outOfRangeColor, + pieces: this.rangeItems.map(item => item.piece) + }, + showTooltip: this.settings.showTooltip, + tooltipValueFont: this.settings.tooltipValueFont, + tooltipValueColor: this.settings.tooltipValueColor, + tooltipShowDate: this.settings.tooltipShowDate, + tooltipDateInterval: this.settings.tooltipDateInterval, + tooltipDateFormat: this.settings.tooltipDateFormat, + tooltipDateFont: this.settings.tooltipDateFont, + tooltipDateColor: this.settings.tooltipDateColor, + tooltipBackgroundColor: this.settings.tooltipBackgroundColor, + tooltipBackgroundBlur: this.settings.tooltipBackgroundBlur, + }; + this.timeSeriesChart = new TbTimeSeriesChart(this.ctx, settings, this.chartShape.nativeElement, this.renderer); } ngOnDestroy() { - if (this.shapeResize$) { - this.shapeResize$.disconnect(); - } - if (this.rangeChart) { - this.rangeChart.dispose(); + if (this.timeSeriesChart) { + this.timeSeriesChart.destroy(); } } public onInit() { const borderRadius = this.ctx.$widgetElement.css('borderRadius'); this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; - if (this.chartShape) { - this.drawChart(); - } else { - this.drawChartPending = true; - } this.cd.detectChanges(); } public onDataUpdated() { - if (this.rangeChart) { - this.rangeChart.setOption({ - xAxis: { - min: this.ctx.defaultSubscription.timeWindow.minTime, - max: this.ctx.defaultSubscription.timeWindow.maxTime, - tbTimeWindow: this.ctx.defaultSubscription.timeWindow - }, - series: [ - {data: this.ctx.data?.length ? toNamedData(this.ctx.data[0].data) : []} - ], - visualMap: { - selected: this.selectedRanges - } - }); + if (this.timeSeriesChart) { + this.timeSeriesChart.update(); } } public toggleRangeItem(item: RangeItem) { item.enabled = !item.enabled; - this.selectedRanges[item.index] = item.enabled; - this.rangeChart.dispatchAction({ - type: 'selectDataRange', - selected: this.selectedRanges - }); - } - - private drawChart() { - echartsModule.init(); - const dataKey = getDataKey(this.ctx.datasources); - this.rangeChart = echarts.init(this.chartShape.nativeElement, null, { - renderer: 'canvas', - }); - this.rangeChartOptions = { - tooltip: { - trigger: 'axis', - confine: true, - appendTo: 'body', - axisPointer: { - type: 'shadow' - }, - formatter: (params: CallbackDataParams[]) => - this.settings.showTooltip ? echartsTooltipFormatter(this.renderer, this.tooltipDateFormat, - this.settings, params, this.decimals, this.units, 0, null, - this.noAggregation ? null : this.ctx.timeWindow.interval) : undefined, - padding: [8, 12], - backgroundColor: this.settings.tooltipBackgroundColor, - borderWidth: 0, - extraCssText: `line-height: 1; backdrop-filter: blur(${this.settings.tooltipBackgroundBlur}px);` - }, - grid: { - containLabel: true, - top: '30', - left: 0, - right: 0, - bottom: this.settings.dataZoom ? 60 : 0 - }, - xAxis: { - type: 'time', - axisTick: { - show: true - }, - axisLabel: { - hideOverlap: true, - fontSize: 10 - }, - axisLine: { - onZero: false - }, - min: this.ctx.defaultSubscription.timeWindow.minTime, - max: this.ctx.defaultSubscription.timeWindow.maxTime, - bandWidthCalculator: timeAxisBandWidthCalculator - }, - yAxis: { - type: 'value', - axisLabel: { - formatter: (value: any) => formatValue(value, this.decimals, this.units, false) - } - }, - series: [{ - type: 'line', - name: dataKey?.label, - smooth: false, - showSymbol: false, - animation: true, - areaStyle: this.settings.fillArea ? {} : undefined, - data: this.ctx.data?.length ? toNamedData(this.ctx.data[0].data) : [], - markLine: this.rangeItems.length ? { - animation: true, - symbol: ['circle', 'arrow'], - symbolSize: [5, 7], - lineStyle: { - width: 1, - type: [3, 3], - color: '#37383b' - }, - label: { - position: 'insideEndTop', - color: '#37383b', - backgroundColor: 'rgba(255,255,255,0.56)', - padding: [4, 5], - borderRadius: 4, - formatter: params => formatValue(params.value, this.decimals, this.units, false) - }, - emphasis: { - disabled: true - }, - data: getMarkPoints(this.rangeItems).map(point => ({ yAxis: point })) - } : undefined - }], - dataZoom: [ - { - type: 'inside', - disabled: !this.settings.dataZoom - }, - { - type: 'slider', - show: this.settings.dataZoom, - showDetail: false, - right: 10 - } - ], - visualMap: { - show: false, - type: 'piecewise', - selected: this.selectedRanges, - dimension: 1, - pieces: this.rangeItems.map(item => item.piece), - outOfRange: { - color: this.settings.outOfRangeColor - }, - inRange: !this.rangeItems.length ? { - color: this.settings.outOfRangeColor - } : undefined - } - }; - - (this.rangeChartOptions.xAxis as any).tbTimeWindow = this.ctx.defaultSubscription.timeWindow; - - this.rangeChart.setOption(this.rangeChartOptions); - - this.shapeResize$ = new ResizeObserver(() => { - this.onResize(); - }); - this.shapeResize$.observe(this.chartShape.nativeElement); - this.onResize(); - } - - private onResize() { - const width = this.rangeChart.getWidth(); - const height = this.rangeChart.getHeight(); - const shapeWidth = this.chartShape.nativeElement.offsetWidth; - const shapeHeight = this.chartShape.nativeElement.offsetHeight; - if (width !== shapeWidth || height !== shapeHeight) { - this.rangeChart.resize(); - } + this.timeSeriesChart.toggleVisualMapRange(item.index); } } 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 2c3b79a30e..297e4b025a 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 @@ -31,12 +31,17 @@ import { textStyle, tsToFormatTimeUnit } from '@shared/models/widget-settings.models'; -import { LabelLayoutOptionCallback, XAXisOption, YAXisOption } from 'echarts/types/dist/shared'; +import { + LabelLayoutOptionCallback, + VisualMapComponentOption, + XAXisOption, + YAXisOption +} from 'echarts/types/dist/shared'; import { CustomSeriesOption, LineSeriesOption } from 'echarts/charts'; import { formatValue, isDefinedAndNotNull, - isFunction, + isFunction, isNumber, isNumeric, isUndefined, isUndefinedOrNull, @@ -417,7 +422,7 @@ export interface TimeSeriesChartThreshold { units?: string; decimals?: number; lineColor: string; - lineType: TimeSeriesChartLineType; + lineType: TimeSeriesChartLineType | number | number[]; lineWidth: number; startSymbol: TimeSeriesChartShape; startSymbolSize: number; @@ -427,6 +432,7 @@ export interface TimeSeriesChartThreshold { labelPosition: ThresholdLabelPosition; labelFont: Font; labelColor: string; + additionalLabelOption?: {[key: string]: any}; } export const timeSeriesChartThresholdValid = (threshold: TimeSeriesChartThreshold): boolean => { @@ -567,6 +573,39 @@ export interface TimeSeriesChartAnimationSettings { animationDelayUpdate: number; } +export interface TimeSeriesChartVisualMapPiece { + lt?: number; + gt?: number; + lte?: number; + gte?: number; + value?: number; + color?: string; +} + +export const createTimeSeriesChartVisualMapPiece = (color: string, from?: number, to?: number): TimeSeriesChartVisualMapPiece => { + const piece: TimeSeriesChartVisualMapPiece = { + color + }; + if (isNumber(from) && isNumber(to)) { + if (from === to) { + piece.value = from; + } else { + piece.gte = from; + piece.lt = to; + } + } else if (isNumber(from)) { + piece.gte = from; + } else if (isNumber(to)) { + piece.lt = to; + } + return piece; +}; + +export interface TimeSeriesChartVisualMapSettings { + outOfRangeColor: string; + pieces: TimeSeriesChartVisualMapPiece[]; +} + export interface TimeSeriesChartSettings extends EChartsTooltipWidgetSettings { thresholds: TimeSeriesChartThreshold[]; darkMode: boolean; @@ -577,6 +616,7 @@ export interface TimeSeriesChartSettings extends EChartsTooltipWidgetSettings { animation: TimeSeriesChartAnimationSettings; barWidthSettings: TimeSeriesChartBarWidthSettings; noAggregationBarWidthSettings: TimeSeriesChartNoAggregationBarWidthSettings; + visualMapSettings?: TimeSeriesChartVisualMapSettings; } export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = { @@ -675,7 +715,7 @@ export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = { }; export interface SeriesFillSettings { - type: SeriesFillType; + type: SeriesFillType | 'default'; opacity: number; gradient: { start: number; @@ -960,7 +1000,7 @@ export const createTimeSeriesXAxisOption = (settings: TimeSeriesChartXAxisSettin fontFamily: xAxisTickLabelStyle.fontFamily, fontSize: xAxisTickLabelStyle.fontSize, hideOverlap: true, - formatter: (value: number, index: number, extra: {level: number}) => { + formatter: (value: number, _index: number, extra: {level: number}) => { const unit = tsToFormatTimeUnit(value); const format = ticksFormat[unit]; const formatted = datePipe.transform(value, format); @@ -990,6 +1030,21 @@ export const createTimeSeriesXAxisOption = (settings: TimeSeriesChartXAxisSettin }; }; +export const createTimeSeriesVisualMapOption = (settings: TimeSeriesChartVisualMapSettings, + selectedRanges: {[key: number]: boolean}): VisualMapComponentOption => ({ + show: false, + type: 'piecewise', + selected: selectedRanges, + dimension: 1, + pieces: settings.pieces, + outOfRange: { + color: settings.outOfRangeColor +}, + inRange: !settings.pieces.length ? { + color: settings.outOfRangeColor + } : undefined +}); + export const generateChartData = (dataItems: TimeSeriesChartDataItem[], thresholdItems: TimeSeriesChartThresholdItem[], stack: boolean, @@ -1072,6 +1127,9 @@ const generateChartThresholds = (thresholdItems: TimeSeriesChartThresholdItem[]) } } }; + if (item.settings.additionalLabelOption) { + seriesOption.markLine.label = {...seriesOption.markLine.label, ...item.settings.additionalLabelOption}; + } item.option = seriesOption; } seriesOption.markLine.data = []; @@ -1147,7 +1205,7 @@ const generateChartSeries = (dataItems: TimeSeriesChartDataItem[], export const updateDarkMode = (options: EChartsOption, settings: TimeSeriesChartSettings, yAxisList: TimeSeriesChartYAxis[], - dataItems: TimeSeriesChartDataItem[], thresholdDataItems: TimeSeriesChartThresholdItem[], + dataItems: TimeSeriesChartDataItem[], darkMode: boolean): EChartsOption => { options.darkMode = darkMode; if (Array.isArray(options.yAxis)) { @@ -1246,7 +1304,7 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem, lineSeriesOption.areaStyle = {}; if (lineSettings.fillAreaSettings.type === SeriesFillType.opacity) { lineSeriesOption.areaStyle.opacity = lineSettings.fillAreaSettings.opacity; - } else { + } else if (lineSettings.fillAreaSettings.type === SeriesFillType.gradient) { lineSeriesOption.areaStyle.opacity = 1; lineSeriesOption.areaStyle.color = createLinearOpacityGradient(seriesColor, lineSettings.fillAreaSettings.gradient); } @@ -1264,7 +1322,7 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem, borderWidth: barSettings.showBorder ? barSettings.borderWidth : 0, borderRadius: barSettings.borderRadius }; - if (barSettings.backgroundSettings.type === SeriesFillType.none) { + if (barSettings.backgroundSettings.type === SeriesFillType.none || barSettings.backgroundSettings.type === 'default') { barVisualSettings.color = seriesColor; } else if (barSettings.backgroundSettings.type === SeriesFillType.opacity) { barVisualSettings.color = tinycolor(seriesColor).setAlpha(barSettings.backgroundSettings.opacity).toRgbString(); 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 c14cd66b56..4abd10a6e3 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 @@ -18,6 +18,7 @@ import { WidgetContext } from '@home/models/widget-component.models'; import { AxisPosition, calculateThresholdsOffset, + createTimeSeriesVisualMapOption, createTimeSeriesXAxisOption, createTimeSeriesYAxis, defaultTimeSeriesChartYAxisSettings, @@ -51,7 +52,8 @@ import { EChartsOption, echartsTooltipFormatter, EChartsTooltipTrigger, - getAxisExtent, getFocusedSeriesIndex, + getAxisExtent, + getFocusedSeriesIndex, measureXAxisNameHeight, measureYAxisNameWidth, toNamedData @@ -60,7 +62,7 @@ import { DateFormatProcessor } from '@shared/models/widget-settings.models'; import { isDefinedAndNotNull, isEqual, mergeDeep } from '@core/utils'; import { DataKey, Datasource, DatasourceType, widgetType } from '@shared/models/widget.models'; import * as echarts from 'echarts/core'; -import { CallbackDataParams } from 'echarts/types/dist/shared'; +import { CallbackDataParams, PiecewiseVisualMapOption } from 'echarts/types/dist/shared'; import { Renderer2 } from '@angular/core'; import { CustomSeriesOption, LineSeriesOption } from 'echarts/charts'; import { BehaviorSubject } from 'rxjs'; @@ -107,6 +109,9 @@ export class TbTimeSeriesChart { private dataItems: TimeSeriesChartDataItem[] = []; private thresholdItems: TimeSeriesChartThresholdItem[] = []; + private hasVisualMap = false; + private visualMapSelectedRanges: {[key: number]: boolean}; + private timeSeriesChart: ECharts; private timeSeriesChartOptions: EChartsOption; @@ -145,6 +150,7 @@ export class TbTimeSeriesChart { this.setupYAxes(); this.setupData(); this.setupThresholds(); + this.setupVisualMap(); if (this.settings.showTooltip && this.settings.tooltipShowDate) { this.tooltipDateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.tooltipDateFormat); } @@ -186,6 +192,9 @@ export class TbTimeSeriesChart { } else { this.timeSeriesChartOptions.tooltip[0].axisPointer.type = 'shadow'; } + if (this.hasVisualMap) { + (this.timeSeriesChartOptions.visualMap as PiecewiseVisualMapOption).selected = this.visualMapSelectedRanges; + } this.barRenderSharedContext.timeInterval = this.ctx.timeWindow.interval; this.updateSeriesData(true); if (this.highlightedDataKey) { @@ -263,6 +272,16 @@ export class TbTimeSeriesChart { } } + public toggleVisualMapRange(index: number): void { + if (this.hasVisualMap) { + this.visualMapSelectedRanges[index] = !this.visualMapSelectedRanges[index]; + this.timeSeriesChart.dispatchAction({ + type: 'selectDataRange', + selected: this.visualMapSelectedRanges + }); + } + } + public destroy(): void { if (this.shapeResize$) { this.shapeResize$.disconnect(); @@ -284,8 +303,7 @@ export class TbTimeSeriesChart { this.darkMode = darkMode; if (this.timeSeriesChart) { this.timeSeriesChartOptions = updateDarkMode(this.timeSeriesChartOptions, - this.settings, this.yAxisList, this.dataItems, - this.thresholdItems, darkMode); + this.settings, this.yAxisList, this.dataItems, darkMode); this.timeSeriesChart.setOption(this.timeSeriesChartOptions); } } @@ -431,6 +449,15 @@ export class TbTimeSeriesChart { } } + private setupVisualMap(): void { + if (this.settings.visualMapSettings?.pieces && this.settings.visualMapSettings?.pieces.length) { + this.hasVisualMap = true; + this.visualMapSelectedRanges = {}; + this.settings.visualMapSettings.pieces.forEach((_val, index) => { + this.visualMapSelectedRanges[index] = true; + }); + } + } private nextComponentId(): string { return (this.componentIndexCounter++) + ''; @@ -536,6 +563,10 @@ export class TbTimeSeriesChart { animationEasingUpdate: this.settings.animation.animationEasingUpdate, animationDelayUpdate: this.settings.animation.animationDelayUpdate }; + if (this.hasVisualMap) { + this.timeSeriesChartOptions.visualMap = + createTimeSeriesVisualMapOption(this.settings.visualMapSettings, this.visualMapSelectedRanges); + } this.timeSeriesChartOptions.xAxis[0].tbTimeWindow = this.ctx.defaultSubscription.timeWindow; From ce3be067968033371b96413feb8a8c6a6f64fa3d Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2024 12:10:47 +0200 Subject: [PATCH 3/8] UI: Range chart widget config improvements. --- .../lib/chart/range-chart-widget.component.ts | 180 ++---------------- .../lib/chart/range-chart-widget.models.ts | 177 ++++++++++++++++- .../lib/chart/time-series-chart.models.ts | 4 +- 3 files changed, 189 insertions(+), 172 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts index afc2c720f9..42c72d6526 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts @@ -30,116 +30,25 @@ import { import { WidgetContext } from '@home/models/widget-component.models'; import { backgroundStyle, - ColorRange, ComponentStyle, - filterIncludingColorRanges, getDataKey, overlayStyle, - sortedColorRange, textStyle } from '@shared/models/widget-settings.models'; -import { isDefinedAndNotNull, isNumber } from '@core/utils'; -import { rangeChartDefaultSettings, RangeChartWidgetSettings } from './range-chart-widget.models'; +import { isDefinedAndNotNull } from '@core/utils'; +import { + rangeChartDefaultSettings, + rangeChartTimeSeriesKeySettings, + rangeChartTimeSeriesSettings, + RangeChartWidgetSettings, + RangeItem, + toRangeItems +} from './range-chart-widget.models'; import { Observable } from 'rxjs'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; -import { DeepPartial } from '@shared/models/common'; -import { - createTimeSeriesChartVisualMapPiece, - SeriesFillType, - TimeSeriesChartKeySettings, - TimeSeriesChartSettings, - TimeSeriesChartShape, - TimeSeriesChartThreshold, - TimeSeriesChartThresholdType, TimeSeriesChartVisualMapPiece -} from '@home/components/widget/lib/chart/time-series-chart.models'; import { TbTimeSeriesChart } from '@home/components/widget/lib/chart/time-series-chart'; -interface RangeItem { - index: number; - from?: number; - to?: number; - color: string; - label: string; - visible: boolean; - enabled: boolean; - piece: TimeSeriesChartVisualMapPiece; -} - -const rangeItemLabel = (from?: number, to?: number): string => { - if (isNumber(from) && isNumber(to)) { - if (from === to) { - return `${from}`; - } else { - return `${from} - ${to}`; - } - } else if (isNumber(from)) { - return `≥ ${from}`; - } else if (isNumber(to)) { - return `< ${to}`; - } else { - return null; - } -}; - -const toRangeItems = (colorRanges: Array): RangeItem[] => { - const rangeItems: RangeItem[] = []; - let counter = 0; - const ranges = sortedColorRange(filterIncludingColorRanges(colorRanges)).filter(r => isNumber(r.from) || isNumber(r.to)); - for (let i = 0; i < ranges.length; i++) { - const range = ranges[i]; - let from = range.from; - const to = range.to; - if (i > 0) { - const prevRange = ranges[i - 1]; - if (isNumber(prevRange.to) && isNumber(from) && from < prevRange.to) { - from = prevRange.to; - } - } - rangeItems.push( - { - index: counter++, - color: range.color, - enabled: true, - visible: true, - from, - to, - label: rangeItemLabel(from, to), - piece: createTimeSeriesChartVisualMapPiece(range.color, from, to) - } - ); - if (!isNumber(from) || !isNumber(to)) { - const value = !isNumber(from) ? to : from; - rangeItems.push( - { - index: counter++, - color: 'transparent', - enabled: true, - visible: false, - label: '', - piece: { gt: value - 0.000000001, lt: value + 0.000000001, color: 'transparent'} - } - ); - } - } - return rangeItems; -}; - -const getMarkPoints = (ranges: Array): number[] => { - const points = new Set(); - for (const range of ranges) { - if (range.visible) { - if (isNumber(range.from)) { - points.add(range.from); - } - if (isNumber(range.to)) { - points.add(range.to); - } - } - } - return Array.from(points).sort(); -}; - @Component({ selector: 'tb-range-chart-widget', templateUrl: './range-chart-widget.component.html', @@ -196,17 +105,7 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn this.units = dataKey.units; } if (dataKey) { - dataKey.settings = { - type: 'line', - lineSettings: { - showLine: true, - smooth: false, - showPoints: false, - fillAreaSettings: { - type: this.settings.fillArea ? 'default' : SeriesFillType.none - } - } - } as DeepPartial; + dataKey.settings = rangeChartTimeSeriesKeySettings(this.settings); } this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); @@ -226,64 +125,7 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn } ngAfterViewInit() { - const thresholds: DeepPartial[] = getMarkPoints(this.rangeItems).map(item => ({ - type: TimeSeriesChartThresholdType.constant, - yAxisId: 'default', - units: this.units, - decimals: this.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 - } as DeepPartial)); - const settings: DeepPartial = { - dataZoom: this.settings.dataZoom, - thresholds, - yAxes: { - default: { - show: true, - showLine: false, - showTicks: false, - showTickLabels: true, - showSplitLines: true, - decimals: this.decimals, - units: this.units - } - }, - xAxis: { - show: true, - showLine: true, - showTicks: true, - showTickLabels: true, - showSplitLines: false - }, - visualMapSettings: { - outOfRangeColor: this.settings.outOfRangeColor, - pieces: this.rangeItems.map(item => item.piece) - }, - showTooltip: this.settings.showTooltip, - tooltipValueFont: this.settings.tooltipValueFont, - tooltipValueColor: this.settings.tooltipValueColor, - tooltipShowDate: this.settings.tooltipShowDate, - tooltipDateInterval: this.settings.tooltipDateInterval, - tooltipDateFormat: this.settings.tooltipDateFormat, - tooltipDateFont: this.settings.tooltipDateFont, - tooltipDateColor: this.settings.tooltipDateColor, - tooltipBackgroundColor: this.settings.tooltipBackgroundColor, - tooltipBackgroundBlur: this.settings.tooltipBackgroundBlur, - }; + const settings = rangeChartTimeSeriesSettings(this.settings, this.rangeItems, this.decimals, this.units); this.timeSeriesChart = new TbTimeSeriesChart(this.ctx, settings, this.chartShape.nativeElement, this.renderer); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts index 31905e70ba..bff0c8fb1b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts @@ -18,11 +18,37 @@ import { BackgroundSettings, BackgroundType, ColorRange, + filterIncludingColorRanges, Font, - simpleDateFormat + simpleDateFormat, + 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 { + createTimeSeriesChartVisualMapPiece, + SeriesFillType, + TimeSeriesChartKeySettings, + TimeSeriesChartSeriesType, + TimeSeriesChartSettings, + TimeSeriesChartShape, + TimeSeriesChartThreshold, + TimeSeriesChartThresholdType, + TimeSeriesChartVisualMapPiece +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { isNumber } from '@core/utils'; +import { DeepPartial } from '@shared/models/common'; + +export interface RangeItem { + index: number; + from?: number; + to?: number; + color: string; + label: string; + visible: boolean; + enabled: boolean; + piece: TimeSeriesChartVisualMapPiece; +} export interface RangeChartWidgetSettings extends EChartsTooltipWidgetSettings { dataZoom: boolean; @@ -94,3 +120,152 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = { } } }; + +export const rangeChartTimeSeriesSettings = (settings: RangeChartWidgetSettings, rangeItems: RangeItem[], + decimals: number, units: string): DeepPartial => { + const thresholds: DeepPartial[] = getMarkPoints(rangeItems).map(item => ({ + 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 + } as DeepPartial)); + return { + dataZoom: settings.dataZoom, + thresholds, + yAxes: { + default: { + show: true, + showLine: false, + showTicks: false, + showTickLabels: true, + showSplitLines: true, + decimals, + units + } + }, + xAxis: { + show: true, + showLine: true, + showTicks: true, + showTickLabels: true, + showSplitLines: false + }, + visualMapSettings: { + outOfRangeColor: settings.outOfRangeColor, + pieces: rangeItems.map(item => item.piece) + }, + showTooltip: settings.showTooltip, + tooltipValueFont: settings.tooltipValueFont, + tooltipValueColor: settings.tooltipValueColor, + tooltipShowDate: settings.tooltipShowDate, + tooltipDateInterval: settings.tooltipDateInterval, + tooltipDateFormat: settings.tooltipDateFormat, + tooltipDateFont: settings.tooltipDateFont, + tooltipDateColor: settings.tooltipDateColor, + tooltipBackgroundColor: settings.tooltipBackgroundColor, + tooltipBackgroundBlur: settings.tooltipBackgroundBlur, + }; +}; + +export const rangeChartTimeSeriesKeySettings = (settings: RangeChartWidgetSettings): DeepPartial => ({ + type: TimeSeriesChartSeriesType.line, + lineSettings: { + showLine: true, + smooth: false, + showPoints: false, + fillAreaSettings: { + type: settings.fillArea ? SeriesFillType.opacity : SeriesFillType.none, + opacity: 0.7 + } + } + }); + +export const toRangeItems = (colorRanges: Array): RangeItem[] => { + const rangeItems: RangeItem[] = []; + let counter = 0; + const ranges = sortedColorRange(filterIncludingColorRanges(colorRanges)).filter(r => isNumber(r.from) || isNumber(r.to)); + for (let i = 0; i < ranges.length; i++) { + const range = ranges[i]; + let from = range.from; + const to = range.to; + if (i > 0) { + const prevRange = ranges[i - 1]; + if (isNumber(prevRange.to) && isNumber(from) && from < prevRange.to) { + from = prevRange.to; + } + } + rangeItems.push( + { + index: counter++, + color: range.color, + enabled: true, + visible: true, + from, + to, + label: rangeItemLabel(from, to), + piece: createTimeSeriesChartVisualMapPiece(range.color, from, to) + } + ); + if (!isNumber(from) || !isNumber(to)) { + const value = !isNumber(from) ? to : from; + rangeItems.push( + { + index: counter++, + color: 'transparent', + enabled: true, + visible: false, + label: '', + piece: { gt: value - 0.000000001, lt: value + 0.000000001, color: 'transparent'} + } + ); + } + } + return rangeItems; +}; + +const rangeItemLabel = (from?: number, to?: number): string => { + if (isNumber(from) && isNumber(to)) { + if (from === to) { + return `${from}`; + } else { + return `${from} - ${to}`; + } + } else if (isNumber(from)) { + return `≥ ${from}`; + } else if (isNumber(to)) { + return `< ${to}`; + } else { + return null; + } +}; + +const getMarkPoints = (ranges: Array): number[] => { + const points = new Set(); + for (const range of ranges) { + if (range.visible) { + if (isNumber(range.from)) { + points.add(range.from); + } + if (isNumber(range.to)) { + points.add(range.to); + } + } + } + return Array.from(points).sort(); +}; 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 297e4b025a..02cd2bba75 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 @@ -715,7 +715,7 @@ export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = { }; export interface SeriesFillSettings { - type: SeriesFillType | 'default'; + type: SeriesFillType; opacity: number; gradient: { start: number; @@ -1322,7 +1322,7 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem, borderWidth: barSettings.showBorder ? barSettings.borderWidth : 0, borderRadius: barSettings.borderRadius }; - if (barSettings.backgroundSettings.type === SeriesFillType.none || barSettings.backgroundSettings.type === 'default') { + if (barSettings.backgroundSettings.type === SeriesFillType.none) { barVisualSettings.color = seriesColor; } else if (barSettings.backgroundSettings.type === SeriesFillType.opacity) { barVisualSettings.color = tinycolor(seriesColor).setAlpha(barSettings.backgroundSettings.opacity).toRgbString(); From cff99ab843fd8518b7f21387629622b1a9a02f5c Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2024 12:50:47 +0200 Subject: [PATCH 4/8] UI: Improve charts layout. --- .../bar-chart-with-labels-basic-config.component.html | 6 ++++++ .../bar-chart-with-labels-basic-config.component.ts | 2 ++ .../chart/range-chart-basic-config.component.html | 6 ++++++ .../basic/chart/range-chart-basic-config.component.ts | 2 ++ .../chart/bar-chart-with-labels-widget.component.html | 3 ++- .../chart/bar-chart-with-labels-widget.component.scss | 11 +++++++---- .../chart/bar-chart-with-labels-widget.component.ts | 4 ++++ .../lib/chart/bar-chart-with-labels-widget.models.ts | 4 +++- .../lib/chart/range-chart-widget.component.html | 3 ++- .../lib/chart/range-chart-widget.component.scss | 11 +++++++---- .../widget/lib/chart/range-chart-widget.component.ts | 4 ++++ .../widget/lib/chart/range-chart-widget.models.ts | 4 +++- .../components/widget/lib/chart/time-series-chart.ts | 10 +++++++++- ...r-chart-with-labels-widget-settings.component.html | 6 ++++++ ...bar-chart-with-labels-widget-settings.component.ts | 3 ++- .../chart/range-chart-widget-settings.component.html | 6 ++++++ .../chart/range-chart-widget-settings.component.ts | 3 ++- 17 files changed, 73 insertions(+), 15 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html index c98c9274cc..ca3d8a5b44 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html @@ -249,6 +249,12 @@
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts index 78b0b6073b..8cdd89f01c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts @@ -136,6 +136,7 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom cardButtons: [this.getCardButtons(configData.config), []], borderRadius: [configData.config.borderRadius, []], + padding: [settings.padding, []], actions: [configData.config.actions || {}, []] }); @@ -188,6 +189,7 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom this.setCardButtons(config.cardButtons, this.widgetConfig.config); this.widgetConfig.config.borderRadius = config.borderRadius; + this.widgetConfig.config.settings.padding = config.padding; this.widgetConfig.config.actions = config.actions; return this.widgetConfig; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html index 5be56204fa..bb64c59302 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html @@ -223,6 +223,12 @@ +
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts index 0add69096b..566dd4d86e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts @@ -117,6 +117,7 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { cardButtons: [this.getCardButtons(configData.config), []], borderRadius: [configData.config.borderRadius, []], + padding: [settings.padding, []], actions: [configData.config.actions || {}, []] }); @@ -165,6 +166,7 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { this.setCardButtons(config.cardButtons, this.widgetConfig.config); this.widgetConfig.config.borderRadius = config.borderRadius; + this.widgetConfig.config.settings.padding = config.padding; this.widgetConfig.config.actions = config.actions; return this.widgetConfig; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.html index c58dfdc90d..f48b8def30 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.html @@ -15,7 +15,8 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.scss index feb65353f6..ac7229672e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.scss @@ -19,8 +19,10 @@ position: relative; display: flex; flex-direction: column; - gap: 16px; - padding: 20px 24px 24px 24px; + gap: 8px; + &.overlay { + padding: 20px 24px 24px 24px; + } > div:not(.tb-bar-chart-overlay) { z-index: 1; } @@ -40,7 +42,7 @@ min-height: 0; display: flex; flex-direction: column; - gap: 16px; + gap: 8px; &.legend-top { flex-direction: column-reverse; } @@ -88,8 +90,9 @@ } } &.legend-right, &.legend-left { - gap: 24px; + gap: 16px; .tb-bar-chart-legend { + padding-top: 8px; flex-direction: column-reverse; justify-content: flex-end; align-items: stretch; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts index 5aaa2671ea..b840f49e3b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts @@ -65,6 +65,8 @@ export class BarChartWithLabelsWidgetComponent implements OnInit, OnDestroy, Aft backgroundStyle$: Observable; overlayStyle: ComponentStyle = {}; + overlayEnabled: boolean; + padding: string; legendKeys: DataKey[]; legendLabelStyle: ComponentStyle; @@ -84,6 +86,8 @@ export class BarChartWithLabelsWidgetComponent implements OnInit, OnDestroy, Aft this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); this.overlayStyle = overlayStyle(this.settings.background.overlay); + this.overlayEnabled = this.settings.background.overlay.enabled; + this.padding = this.overlayEnabled ? undefined : this.settings.padding; this.showLegend = this.settings.showLegend; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts index 153a038d1e..8d99a7bd3f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts @@ -44,6 +44,7 @@ export interface BarChartWithLabelsWidgetSettings extends EChartsTooltipWidgetSe legendLabelFont: Font; legendLabelColor: string; background: BackgroundSettings; + padding: string; } export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings = { @@ -110,7 +111,8 @@ export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings color: 'rgba(255,255,255,0.72)', blur: 3 } - } + }, + padding: '12px' }; export const barChartWithLabelsTimeSeriesSettings = (settings: BarChartWithLabelsWidgetSettings): DeepPartial => ({ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html index 6d3b6d43db..63a549bb48 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html @@ -15,7 +15,8 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss index de2ca546fc..d956173281 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss @@ -19,8 +19,10 @@ position: relative; display: flex; flex-direction: column; - gap: 16px; - padding: 20px 24px 24px 24px; + gap: 8px; + &.overlay { + padding: 20px 24px 24px 24px; + } > div:not(.tb-range-chart-overlay) { z-index: 1; } @@ -40,7 +42,7 @@ min-height: 0; display: flex; flex-direction: column; - gap: 16px; + gap: 8px; &.legend-top { flex-direction: column-reverse; } @@ -88,8 +90,9 @@ } } &.legend-right, &.legend-left { - gap: 24px; + gap: 16px; .tb-range-chart-legend { + padding-top: 8px; flex-direction: column-reverse; justify-content: flex-end; align-items: stretch; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts index 42c72d6526..9d89154111 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts @@ -73,6 +73,8 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn backgroundStyle$: Observable; overlayStyle: ComponentStyle = {}; + overlayEnabled: boolean; + padding: string; legendLabelStyle: ComponentStyle; disabledLegendLabelStyle: ComponentStyle; @@ -110,6 +112,8 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); this.overlayStyle = overlayStyle(this.settings.background.overlay); + this.overlayEnabled = this.settings.background.overlay.enabled; + this.padding = this.overlayEnabled ? undefined : this.settings.padding; this.rangeItems = toRangeItems(this.settings.rangeColors); this.visibleRangeItems = this.rangeItems.filter(item => item.visible); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts index bff0c8fb1b..ee7b2835e6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts @@ -60,6 +60,7 @@ export interface RangeChartWidgetSettings extends EChartsTooltipWidgetSettings { legendLabelFont: Font; legendLabelColor: string; background: BackgroundSettings; + padding: string; } export const rangeChartDefaultSettings: RangeChartWidgetSettings = { @@ -118,7 +119,8 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = { color: 'rgba(255,255,255,0.72)', blur: 3 } - } + }, + padding: '12px' }; export const rangeChartTimeSeriesSettings = (settings: RangeChartWidgetSettings, rangeItems: RangeItem[], 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 4abd10a6e3..df8b74f898 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 @@ -333,7 +333,10 @@ export class TbTimeSeriesChart { timeSeriesChartKeyDefaultSettings, dataKey.settings); if ((keySettings.type === TimeSeriesChartSeriesType.line && keySettings.lineSettings.showPointLabel && keySettings.lineSettings.pointLabelPosition === SeriesLabelPosition.top) || - (keySettings.type === TimeSeriesChartSeriesType.bar && keySettings.barSettings.showLabel)) { + (keySettings.type === TimeSeriesChartSeriesType.bar && + keySettings.barSettings.showLabel && + [SeriesLabelPosition.top, SeriesLabelPosition.bottom] + .includes(keySettings.barSettings.labelPosition as SeriesLabelPosition))) { this.topPointLabels = true; } dataKey.settings = keySettings; @@ -366,6 +369,11 @@ export class TbTimeSeriesChart { for (const thresholdSettings of this.settings.thresholds) { const threshold = mergeDeep({} as TimeSeriesChartThreshold, timeSeriesChartThresholdDefaultSettings, thresholdSettings); + if (!this.topPointLabels) { + if (threshold.showLabel && !threshold.labelPosition.endsWith('Bottom')) { + this.topPointLabels = true; + } + } let latestDataKey: DataKey = null; let entityDataKey: DataKey = null; let value = null; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html index b20b941bd1..bc8044b957 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html @@ -154,5 +154,11 @@
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts index b69b023d34..f61a130e9d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts @@ -88,7 +88,8 @@ export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsCom tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], - background: [settings.background, []] + background: [settings.background, []], + padding: [settings.padding, []] }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html index cf5bbcfdd5..4a7331d4c2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html @@ -144,5 +144,11 @@
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts index f0870dbd80..4ef2971467 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts @@ -83,7 +83,8 @@ export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent { tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], - background: [settings.background, []] + background: [settings.background, []], + padding: [settings.padding, []] }); } From 604a35b2671ce9c38ddfe13268ee0cf3b190dd43 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2024 17:38:06 +0200 Subject: [PATCH 5/8] UI: Improve bar chart with labels width configuration. --- ...rt-with-labels-basic-config.component.html | 79 ++++++++-- ...hart-with-labels-basic-config.component.ts | 41 ++++- .../bar-chart-with-labels-widget.models.ts | 84 +++++++--- .../lib/chart/time-series-chart.models.ts | 122 ++++++++------- .../widget/lib/chart/time-series-chart.ts | 6 +- ...with-labels-widget-settings.component.html | 144 +++++++++++++----- ...t-with-labels-widget-settings.component.ts | 33 +++- ...-series-chart-axis-settings.component.html | 4 +- ...me-series-chart-axis-settings.component.ts | 8 + ...-series-chart-fill-settings.component.html | 0 ...me-series-chart-fill-settings.component.ts | 0 ...-series-chart-threshold-row.component.html | 2 +- ...me-series-chart-threshold-row.component.ts | 6 + ...rt-threshold-settings-panel.component.html | 2 +- ...hart-threshold-settings-panel.component.ts | 5 + ...ries-chart-thresholds-panel.component.html | 3 +- ...series-chart-thresholds-panel.component.ts | 5 + ...regation-bar-width-settings.component.html | 2 +- ...ggregation-bar-width-settings.component.ts | 5 + .../common/widget-settings-common.module.ts | 5 + .../lib/settings/widget-settings.module.ts | 5 - .../assets/locale/locale.constant-en_US.json | 2 +- .../assets/locale/locale.constant-pl_PL.json | 2 +- 23 files changed, 421 insertions(+), 144 deletions(-) rename ui-ngx/src/app/modules/home/components/widget/lib/settings/{ => common}/chart/time-series-chart-fill-settings.component.html (100%) rename ui-ngx/src/app/modules/home/components/widget/lib/settings/{ => common}/chart/time-series-chart-fill-settings.component.ts (100%) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html index ca3d8a5b44..0b629e4cae 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html @@ -84,8 +84,28 @@
+
+
widgets.time-series-chart.chart
+
+ + {{ 'widgets.time-series-chart.data-zoom' | translate }} + +
+
widgets.bar-chart.bar-appearance
+
+
widget-config.units-short
+ + +
+
+
widget-config.decimals-short
+ + + +
{{ 'widgets.bar-chart.label-on-bar' | translate }} @@ -118,19 +138,57 @@
-
-
widget-config.units-short
- - +
+ + {{ 'widgets.time-series-chart.series.bar.show-border' | translate }} +
-
-
widget-config.decimals-short
- - +
+
widgets.time-series-chart.series.bar.border-width
+ + + +
+
+
widgets.time-series-chart.series.bar.border-radius
+ +
+ + + + +
+
+
widgets.time-series-chart.axis.y-axis
+ + +
+
+
widgets.time-series-chart.axis.x-axis
+ +
+ +
@@ -230,6 +288,9 @@
+ +
widget-config.card-appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts index 8cdd89f01c..ab5fc0a867 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts @@ -44,6 +44,7 @@ import { barChartWithLabelsDefaultSettings, BarChartWithLabelsWidgetSettings } from '@home/components/widget/lib/chart/bar-chart-with-labels-widget.models'; +import { TimeSeriesChartType } from '@home/components/widget/lib/chart/time-series-chart.models'; @Component({ selector: 'tb-bar-chart-with-labels-basic-config', @@ -105,16 +106,30 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom icon: [configData.config.titleIcon, []], iconColor: [configData.config.iconColor, []], + dataZoom: [settings.dataZoom, []], + showBarLabel: [settings.showBarLabel, []], barLabelFont: [settings.barLabelFont, []], barLabelColor: [settings.barLabelColor, []], showBarValue: [settings.showBarValue, []], barValueFont: [settings.barValueFont, []], barValueColor: [settings.barValueColor, []], + showBarBorder: [settings.showBarBorder, []], + barBorderWidth: [settings.barBorderWidth, []], + barBorderRadius: [settings.barBorderRadius, []], + barBackgroundSettings: [settings.barBackgroundSettings, []], + noAggregationBarWidthSettings: [settings.noAggregationBarWidthSettings, []], units: [configData.config.units, []], decimals: [configData.config.decimals, []], + yAxis: [settings.yAxis, []], + xAxis: [settings.xAxis, []], + + thresholds: [settings.thresholds, []], + + animation: [settings.animation, []], + showLegend: [settings.showLegend, []], legendPosition: [settings.legendPosition, []], legendLabelFont: [settings.legendLabelFont, []], @@ -159,6 +174,8 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + this.widgetConfig.config.settings.dataZoom = config.dataZoom; + this.widgetConfig.config.settings.showBarLabel = config.showBarLabel; this.widgetConfig.config.settings.barLabelFont = config.barLabelFont; this.widgetConfig.config.settings.barLabelColor = config.barLabelColor; @@ -166,9 +183,22 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom this.widgetConfig.config.settings.barValueFont = config.barValueFont; this.widgetConfig.config.settings.barValueColor = config.barValueColor; + this.widgetConfig.config.settings.showBarBorder = config.showBarBorder; + this.widgetConfig.config.settings.barBorderWidth = config.barBorderWidth; + this.widgetConfig.config.settings.barBorderRadius = config.barBorderRadius; + this.widgetConfig.config.settings.barBackgroundSettings = config.barBackgroundSettings; + this.widgetConfig.config.settings.noAggregationBarWidthSettings = config.noAggregationBarWidthSettings; + this.widgetConfig.config.units = config.units; this.widgetConfig.config.decimals = config.decimals; + this.widgetConfig.config.settings.yAxis = config.yAxis; + this.widgetConfig.config.settings.xAxis = config.xAxis; + + this.widgetConfig.config.settings.thresholds = config.thresholds; + + this.widgetConfig.config.settings.animation = config.animation; + this.widgetConfig.config.settings.showLegend = config.showLegend; this.widgetConfig.config.settings.legendPosition = config.legendPosition; this.widgetConfig.config.settings.legendLabelFont = config.legendLabelFont; @@ -196,7 +226,7 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom } protected validatorTriggers(): string[] { - return ['showTitle', 'showIcon', 'showBarLabel', 'showBarValue', 'showLegend', 'showTooltip', 'tooltipShowDate']; + return ['showTitle', 'showIcon', 'showBarLabel', 'showBarValue', 'showBarBorder', 'showLegend', 'showTooltip', 'tooltipShowDate']; } protected updateValidators(emitEvent: boolean, trigger?: string) { @@ -204,6 +234,7 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom const showIcon: boolean = this.barChartWidgetConfigForm.get('showIcon').value; const showBarLabel: boolean = this.barChartWidgetConfigForm.get('showBarLabel').value; const showBarValue: boolean = this.barChartWidgetConfigForm.get('showBarValue').value; + const showBarBorder: boolean = this.barChartWidgetConfigForm.get('showBarBorder').value; const showLegend: boolean = this.barChartWidgetConfigForm.get('showLegend').value; const showTooltip: boolean = this.barChartWidgetConfigForm.get('showTooltip').value; const tooltipShowDate: boolean = this.barChartWidgetConfigForm.get('tooltipShowDate').value; @@ -250,7 +281,11 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom this.barChartWidgetConfigForm.get('barValueFont').disable(); this.barChartWidgetConfigForm.get('barValueColor').disable(); } - + if (showBarBorder) { + this.barChartWidgetConfigForm.get('barBorderWidth').enable(); + } else { + this.barChartWidgetConfigForm.get('barBorderWidth').disable(); + } if (showLegend) { this.barChartWidgetConfigForm.get('legendPosition').enable(); this.barChartWidgetConfigForm.get('legendLabelFont').enable(); @@ -328,4 +363,6 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom processor.update(Date.now()); return processor.formatted; } + + protected readonly TimeSeriesChartType = TimeSeriesChartType; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts index 8d99a7bd3f..73ecad5264 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts @@ -19,26 +19,49 @@ import { BackgroundType, ComponentStyle, customDateFormat, - Font, textStyle + Font, + textStyle } 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 { DeepPartial } from '@shared/models/common'; import { - TimeSeriesChartKeySettings, TimeSeriesChartSeriesType, - TimeSeriesChartSettings + defaultTimeSeriesChartXAxisSettings, + defaultTimeSeriesChartYAxisSettings, + SeriesFillSettings, + SeriesFillType, + timeSeriesChartAnimationDefaultSettings, + TimeSeriesChartAnimationSettings, + TimeSeriesChartKeySettings, + timeSeriesChartNoAggregationBarWidthDefaultSettings, + TimeSeriesChartNoAggregationBarWidthSettings, + TimeSeriesChartSeriesType, + TimeSeriesChartSettings, + TimeSeriesChartThreshold, + TimeSeriesChartXAxisSettings, + TimeSeriesChartYAxisSettings } from '@home/components/widget/lib/chart/time-series-chart.models'; import { CallbackDataParams, LabelLayoutOptionCallbackParams } from 'echarts/types/dist/shared'; -import { formatValue } from '@core/utils'; +import { formatValue, mergeDeep } from '@core/utils'; import { LabelLayoutOption } from 'echarts/types/src/util/types'; export interface BarChartWithLabelsWidgetSettings extends EChartsTooltipWidgetSettings { + dataZoom: boolean; showBarLabel: boolean; barLabelFont: Font; barLabelColor: string; showBarValue: boolean; barValueFont: Font; barValueColor: string; + showBarBorder: boolean; + barBorderWidth: number; + barBorderRadius: number; + barBackgroundSettings: SeriesFillSettings; + noAggregationBarWidthSettings: TimeSeriesChartNoAggregationBarWidthSettings; + yAxis: TimeSeriesChartYAxisSettings; + xAxis: TimeSeriesChartXAxisSettings; + animation: TimeSeriesChartAnimationSettings; + thresholds: TimeSeriesChartThreshold[]; showLegend: boolean; legendPosition: LegendPosition; legendLabelFont: Font; @@ -48,6 +71,7 @@ export interface BarChartWithLabelsWidgetSettings extends EChartsTooltipWidgetSe } export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings = { + dataZoom: false, showBarLabel: true, barLabelFont: { family: 'Roboto', @@ -68,6 +92,28 @@ export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings lineHeight: '12px' }, barValueColor: 'rgba(0, 0, 0, 0.76)', + showBarBorder: false, + barBorderWidth: 2, + barBorderRadius: 0, + barBackgroundSettings: { + type: SeriesFillType.none, + opacity: 0.4, + gradient: { + start: 100, + end: 0 + } + }, + noAggregationBarWidthSettings: mergeDeep({} as TimeSeriesChartNoAggregationBarWidthSettings, + timeSeriesChartNoAggregationBarWidthDefaultSettings), + yAxis: mergeDeep({} as TimeSeriesChartYAxisSettings, + defaultTimeSeriesChartYAxisSettings, + { id: 'default', order: 0, showLine: false, showTicks: false } as TimeSeriesChartYAxisSettings), + xAxis: mergeDeep({} as TimeSeriesChartXAxisSettings, + defaultTimeSeriesChartXAxisSettings, + {showTicks: false, showSplitLines: false} as TimeSeriesChartXAxisSettings), + animation: mergeDeep({} as TimeSeriesChartAnimationSettings, + timeSeriesChartAnimationDefaultSettings), + thresholds: [], showLegend: true, legendPosition: LegendPosition.top, legendLabelFont: { @@ -116,27 +162,18 @@ export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings }; export const barChartWithLabelsTimeSeriesSettings = (settings: BarChartWithLabelsWidgetSettings): DeepPartial => ({ - dataZoom: false, + dataZoom: settings.dataZoom, yAxes: { - default: { - show: true, - showLine: false, - showTicks: false, - showTickLabels: true, - showSplitLines: true, - } - }, - xAxis: { - show: true, - showLine: true, - showTicks: false, - showTickLabels: true, - showSplitLines: false + default: settings.yAxis }, + xAxis: settings.xAxis, barWidthSettings: { barGap: 0, intervalGap: 0.5 }, + noAggregationBarWidthSettings: settings.noAggregationBarWidthSettings, + animation: settings.animation, + thresholds: settings.thresholds, showTooltip: settings.showTooltip, tooltipValueFont: settings.tooltipValueFont, tooltipValueColor: settings.tooltipValueColor, @@ -164,10 +201,11 @@ export const barChartWithLabelsTimeSeriesKeySettings = (settings: BarChartWithLa return { type: TimeSeriesChartSeriesType.bar, barSettings: { - showBorder: false, - borderWidth: 0, - borderRadius: 0, - showLabel: settings.showBarLabel, + showBorder: settings.showBarBorder, + borderWidth: settings.barBorderWidth, + borderRadius: settings.barBorderRadius, + backgroundSettings: settings.barBackgroundSettings, + showLabel: settings.showBarLabel || settings.showBarValue, labelPosition: 'insideBottom', labelFormatter: (params: CallbackDataParams): string => { const labelParts: string[] = []; 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 02cd2bba75..df47a03692 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 @@ -408,6 +408,38 @@ export const defaultTimeSeriesChartYAxisSettings: TimeSeriesChartYAxisSettings = splitLinesColor: timeSeriesChartColorScheme['axis.splitLine'].light }; +export const defaultTimeSeriesChartXAxisSettings: TimeSeriesChartXAxisSettings = { + show: true, + label: '', + labelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '600', + lineHeight: '1' + }, + labelColor: timeSeriesChartColorScheme['axis.label'].light, + position: AxisPosition.bottom, + showTickLabels: true, + tickLabelFont: { + family: 'Roboto', + size: 10, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '1' + }, + tickLabelColor: timeSeriesChartColorScheme['axis.tickLabel'].light, + ticksFormat: {}, + showTicks: true, + ticksColor: timeSeriesChartColorScheme['axis.ticks'].light, + showLine: true, + lineColor: timeSeriesChartColorScheme['axis.line'].light, + showSplitLines: true, + splitLinesColor: timeSeriesChartColorScheme['axis.splitLine'].light +}; + export type TimeSeriesChartYAxes = {[id: TimeSeriesChartYAxisId]: TimeSeriesChartYAxisSettings}; export interface TimeSeriesChartThreshold { @@ -521,6 +553,20 @@ export interface TimeSeriesChartNoAggregationBarWidthSettings { barWidth?: TimeSeriesChartBarWidth; } +export const timeSeriesChartNoAggregationBarWidthDefaultSettings: TimeSeriesChartNoAggregationBarWidthSettings = { + strategy: TimeSeriesChartNoAggregationBarWidthStrategy.group, + groupWidth: { + relative: true, + relativeWidth: 2, + absoluteWidth: 1000 + }, + barWidth: { + relative: true, + relativeWidth: 2, + absoluteWidth: 1000 + } +}; + export interface TimeSeriesChartBarWidthSettings { barGap: number; intervalGap: number; @@ -573,6 +619,17 @@ export interface TimeSeriesChartAnimationSettings { animationDelayUpdate: number; } +export const timeSeriesChartAnimationDefaultSettings: TimeSeriesChartAnimationSettings = { + animation: true, + animationThreshold: 2000, + animationDuration: 500, + animationEasing: TimeSeriesChartAnimationEasing.cubicOut, + animationDelay: 0, + animationDurationUpdate: 300, + animationEasingUpdate: TimeSeriesChartAnimationEasing.cubicOut, + animationDelayUpdate: 0 +}; + export interface TimeSeriesChartVisualMapPiece { lt?: number; gt?: number; @@ -629,64 +686,16 @@ export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = { defaultTimeSeriesChartYAxisSettings, { id: 'default', order: 0 } as TimeSeriesChartYAxisSettings) }, - xAxis: { - show: true, - label: '', - labelFont: { - family: 'Roboto', - size: 12, - sizeUnit: 'px', - style: 'normal', - weight: '600', - lineHeight: '1' - }, - labelColor: timeSeriesChartColorScheme['axis.label'].light, - position: AxisPosition.bottom, - showTickLabels: true, - tickLabelFont: { - family: 'Roboto', - size: 10, - sizeUnit: 'px', - style: 'normal', - weight: '400', - lineHeight: '1' - }, - tickLabelColor: timeSeriesChartColorScheme['axis.tickLabel'].light, - ticksFormat: {}, - showTicks: true, - ticksColor: timeSeriesChartColorScheme['axis.ticks'].light, - showLine: true, - lineColor: timeSeriesChartColorScheme['axis.line'].light, - showSplitLines: true, - splitLinesColor: timeSeriesChartColorScheme['axis.splitLine'].light - }, - animation: { - animation: true, - animationThreshold: 2000, - animationDuration: 500, - animationEasing: TimeSeriesChartAnimationEasing.cubicOut, - animationDelay: 0, - animationDurationUpdate: 300, - animationEasingUpdate: TimeSeriesChartAnimationEasing.cubicOut, - animationDelayUpdate: 0 - }, + xAxis: mergeDeep({} as TimeSeriesChartXAxisSettings, + defaultTimeSeriesChartXAxisSettings), + animation: mergeDeep({} as TimeSeriesChartAnimationSettings, + timeSeriesChartAnimationDefaultSettings), barWidthSettings: { barGap: 0.3, intervalGap: 0.6 }, - noAggregationBarWidthSettings: { - strategy: TimeSeriesChartNoAggregationBarWidthStrategy.group, - groupWidth: { - relative: true, - relativeWidth: 2, - absoluteWidth: 1000 - }, - barWidth: { - relative: true, - relativeWidth: 2, - absoluteWidth: 1000 - } - }, + noAggregationBarWidthSettings: mergeDeep({} as TimeSeriesChartNoAggregationBarWidthSettings, + timeSeriesChartNoAggregationBarWidthDefaultSettings), showTooltip: true, tooltipTrigger: EChartsTooltipTrigger.axis, tooltipValueFont: { @@ -1000,6 +1009,11 @@ export const createTimeSeriesXAxisOption = (settings: TimeSeriesChartXAxisSettin fontFamily: xAxisTickLabelStyle.fontFamily, fontSize: xAxisTickLabelStyle.fontSize, hideOverlap: true, + /** Min/Max time label always visible **/ + /* alignMinLabel: 'left', + alignMaxLabel: 'right', + showMinLabel: true, + showMaxLabel: true, */ formatter: (value: number, _index: number, extra: {level: number}) => { const unit = tsToFormatTimeUnit(value); const format = ticksFormat[unit]; 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 df8b74f898..98cc1fd454 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 @@ -584,7 +584,7 @@ export class TbTimeSeriesChart { } this.timeSeriesChart.setOption(this.timeSeriesChartOptions); - this.updateAxes(); + this.updateAxes(false); if (this.settings.dataZoom) { this.timeSeriesChart.on('datazoom', () => { @@ -609,7 +609,7 @@ export class TbTimeSeriesChart { this.barRenderSharedContext, this.darkMode); } - private updateAxes() { + private updateAxes(lazy = true) { const leftAxisList = this.yAxisList.filter(axis => axis.option.position === 'left'); let res = this.updateYAxisOffset(leftAxisList); let leftOffset = res.offset + (!res.offset && this.settings.dataZoom ? 5 : 0); @@ -659,7 +659,7 @@ export class TbTimeSeriesChart { } if (changed) { this.timeSeriesChartOptions.yAxis = this.yAxisList.map(axis => axis.option); - this.timeSeriesChart.setOption(this.timeSeriesChartOptions, {replaceMerge: ['yAxis', 'xAxis', 'grid'], lazyUpdate: true}); + this.timeSeriesChart.setOption(this.timeSeriesChartOptions, {replaceMerge: ['yAxis', 'xAxis', 'grid'], lazyUpdate: lazy}); } if (this.yAxisList.length) { const extent = getAxisExtent(this.timeSeriesChart, this.yAxisList[0].id); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html index bc8044b957..7db5a92746 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html @@ -17,39 +17,99 @@ -->
-
widgets.bar-chart.bar-chart-card-style
-
- - {{ 'widgets.bar-chart.label-on-bar' | translate }} +
widgets.bar-chart.bar-chart-style
+
+ + {{ 'widgets.time-series-chart.data-zoom' | translate }} -
- - - - -
-
- - {{ 'widgets.bar-chart.value-on-bar' | translate }} - -
- - - - +
+
widgets.bar-chart.bar-appearance
+
+ + {{ 'widgets.bar-chart.label-on-bar' | translate }} + +
+ + + + +
+
+
+ + {{ 'widgets.bar-chart.value-on-bar' | translate }} + +
+ + + + +
+
+
+ + {{ 'widgets.time-series-chart.series.bar.show-border' | translate }} + +
+
+
widgets.time-series-chart.series.bar.border-width
+ + + +
+
+
widgets.time-series-chart.series.bar.border-radius
+ + +
+ + + +
+
+
widgets.time-series-chart.axis.y-axis
+ + +
+
+
widgets.time-series-chart.axis.x-axis
+ + +
+ +
@@ -149,16 +209,22 @@
-
-
{{ 'widgets.background.background' | translate }}
- - -
-
-
{{ 'widget-config.card-padding' | translate }}
- - - + + +
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts index f61a130e9d..b221d09cf9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts @@ -16,6 +16,7 @@ import { Component, Injector } from '@angular/core'; import { + Datasource, legendPositions, legendPositionTranslationMap, WidgetSettings, @@ -37,6 +38,15 @@ import { }) export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsComponent { + public get datasource(): Datasource { + const datasources: Datasource[] = this.widgetConfig.config.datasources; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + legendPositions = legendPositions; legendPositionTranslationMap = legendPositionTranslationMap; @@ -64,12 +74,26 @@ export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsCom protected onSettingsSet(settings: WidgetSettings) { this.barChartWidgetSettingsForm = this.fb.group({ + dataZoom: [settings.dataZoom, []], + showBarLabel: [settings.showBarLabel, []], barLabelFont: [settings.barLabelFont, []], barLabelColor: [settings.barLabelColor, []], showBarValue: [settings.showBarValue, []], barValueFont: [settings.barValueFont, []], barValueColor: [settings.barValueColor, []], + showBarBorder: [settings.showBarBorder, []], + barBorderWidth: [settings.barBorderWidth, []], + barBorderRadius: [settings.barBorderRadius, []], + barBackgroundSettings: [settings.barBackgroundSettings, []], + noAggregationBarWidthSettings: [settings.noAggregationBarWidthSettings, []], + + yAxis: [settings.yAxis, []], + xAxis: [settings.xAxis, []], + + thresholds: [settings.thresholds, []], + + animation: [settings.animation, []], showLegend: [settings.showLegend, []], legendPosition: [settings.legendPosition, []], @@ -94,12 +118,13 @@ export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsCom } protected validatorTriggers(): string[] { - return ['showBarLabel', 'showBarValue', 'showLegend', 'showTooltip', 'tooltipShowDate']; + return ['showBarLabel', 'showBarValue', 'showBarBorder', 'showLegend', 'showTooltip', 'tooltipShowDate']; } protected updateValidators(emitEvent: boolean) { const showBarLabel: boolean = this.barChartWidgetSettingsForm.get('showBarLabel').value; const showBarValue: boolean = this.barChartWidgetSettingsForm.get('showBarValue').value; + const showBarBorder: boolean = this.barChartWidgetSettingsForm.get('showBarBorder').value; const showLegend: boolean = this.barChartWidgetSettingsForm.get('showLegend').value; const showTooltip: boolean = this.barChartWidgetSettingsForm.get('showTooltip').value; const tooltipShowDate: boolean = this.barChartWidgetSettingsForm.get('tooltipShowDate').value; @@ -120,6 +145,12 @@ export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsCom this.barChartWidgetSettingsForm.get('barValueColor').disable(); } + if (showBarBorder) { + this.barChartWidgetSettingsForm.get('barBorderWidth').enable(); + } else { + this.barChartWidgetSettingsForm.get('barBorderWidth').disable(); + } + if (showLegend) { this.barChartWidgetSettingsForm.get('legendPosition').enable(); this.barChartWidgetSettingsForm.get('legendLabelFont').enable(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.html index 1d4bb95f5c..66b6c9896d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.html @@ -56,13 +56,13 @@
-
+
widget-config.units-short
-
+
widget-config.decimals-short
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts index 8dafc7050f..ac49b8137d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts @@ -70,6 +70,14 @@ export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValu @coerceBoolean() advanced = false; + @Input() + @coerceBoolean() + hideUnits = false; + + @Input() + @coerceBoolean() + hideDecimals = false; + private modelValue: TimeSeriesChartXAxisSettings | TimeSeriesChartYAxisSettings; private propagateChange = null; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html index 7e2c9f74c2..386ea306a0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html @@ -73,7 +73,7 @@ [formControl]="entityKeyFormControl">
-
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts index baebf8c697..4f51c41c26 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts @@ -53,6 +53,7 @@ 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({ selector: 'tb-time-series-chart-threshold-row', @@ -101,6 +102,10 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso @Input() yAxisIds: TimeSeriesChartYAxisId[]; + @Input() + @coerceBoolean() + hideYAxis = false; + @Output() thresholdRemoved = new EventEmitter(); @@ -220,6 +225,7 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso const ctx: any = { thresholdSettings: deepClone(this.modelValue), widgetConfig: this.widgetConfig, + hideYAxis: this.hideYAxis, yAxisIds: this.yAxisIds }; const thresholdSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html index 3437a578d6..d7cc446b64 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html @@ -18,7 +18,7 @@
{{ 'widgets.time-series-chart.threshold.threshold-settings' | translate }}
-
+
widgets.time-series-chart.axis.y-axis
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts index b9f16103bd..bc68042f9b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts @@ -30,6 +30,7 @@ import { import { merge } from 'rxjs'; import { WidgetConfig } from '@shared/models/widget.models'; import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-time-series-chart-threshold-settings-panel', @@ -66,6 +67,10 @@ export class TimeSeriesChartThresholdSettingsPanelComponent implements OnInit { @Input() popover: TbPopoverComponent; + @Input() + @coerceBoolean() + hideYAxis = false; + @Output() thresholdSettingsApplied = new EventEmitter>(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html index e975eb4653..ba0325da93 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html @@ -21,7 +21,7 @@
widgets.time-series-chart.threshold.source
widgets.time-series-chart.threshold.key-value
-
widgets.time-series-chart.axis.y-axis
+
widgets.time-series-chart.axis.y-axis
widgets.color.color
widget-config.units-short
widget-config.decimals-short
@@ -31,6 +31,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts index 9cb5e5ced2..95e3b242c3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts @@ -38,6 +38,7 @@ import { IAliasController } from '@core/api/widget-api.models'; import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; import { DataKey, Datasource, WidgetConfig } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-time-series-chart-thresholds-panel', @@ -77,6 +78,10 @@ export class TimeSeriesChartThresholdsPanelComponent implements ControlValueAcce @Input() yAxisIds: TimeSeriesChartYAxisId[]; + @Input() + @coerceBoolean() + hideYAxis = false; + thresholdsFormGroup: UntypedFormGroup; private propagateChange = (_val: any) => {}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html index 239c6ea18c..28c79a1310 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
widgets.time-series-chart.no-aggregation-bar-width-strategy
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts index 36d8cb917a..8601c48159 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts @@ -29,6 +29,7 @@ import { timeSeriesChartNoAggregationBarWidthStrategyTranslations } from '@home/components/widget/lib/chart/time-series-chart.models'; import { merge } from 'rxjs'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-time-series-no-aggregation-bar-width-settings', @@ -53,6 +54,10 @@ export class TimeSeriesNoAggregationBarWidthSettingsComponent implements OnInit, @Input() disabled: boolean; + @Input() + @coerceBoolean() + stroked = false; + private modelValue: TimeSeriesChartNoAggregationBarWidthSettings; private propagateChange = null; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index 5ed50a226f..08d45775c5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts @@ -127,6 +127,9 @@ import { import { AutoDateFormatSettingsComponent } from '@home/components/widget/lib/settings/common/auto-date-format-settings.component'; +import { + TimeSeriesChartFillSettingsComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component'; @NgModule({ declarations: [ @@ -174,6 +177,7 @@ import { TimeSeriesChartYAxisRowComponent, TimeSeriesChartYAxisSettingsPanelComponent, TimeSeriesChartAnimationSettingsComponent, + TimeSeriesChartFillSettingsComponent, DataKeyInputComponent, EntityAliasInputComponent ], @@ -227,6 +231,7 @@ import { TimeSeriesChartYAxisRowComponent, TimeSeriesChartYAxisSettingsPanelComponent, TimeSeriesChartAnimationSettingsComponent, + TimeSeriesChartFillSettingsComponent, DataKeyInputComponent, EntityAliasInputComponent ], diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 8522734eda..5b83f859e1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -336,9 +336,6 @@ import { import { TimeSeriesChartLineSettingsComponent } from '@home/components/widget/lib/settings/chart/time-series-chart-line-settings.component'; -import { - TimeSeriesChartFillSettingsComponent -} from '@home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component'; import { TimeSeriesChartBarSettingsComponent } from '@home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component'; @@ -467,7 +464,6 @@ import { TimeSeriesChartKeySettingsComponent, TimeSeriesChartLineSettingsComponent, TimeSeriesChartBarSettingsComponent, - TimeSeriesChartFillSettingsComponent, TimeSeriesChartWidgetSettingsComponent ], imports: [ @@ -596,7 +592,6 @@ import { TimeSeriesChartKeySettingsComponent, TimeSeriesChartLineSettingsComponent, TimeSeriesChartBarSettingsComponent, - TimeSeriesChartFillSettingsComponent, TimeSeriesChartWidgetSettingsComponent ] }) diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index f8f752b5b3..43e20a46ce 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5422,7 +5422,7 @@ "bar-appearance": "Bar appearance", "label-on-bar": "Label on bar", "value-on-bar": "Value on bar", - "bar-chart-card-style": "Bar chart card style" + "bar-chart-style": "Bar chart style" }, "battery-level": { "layout": "Layout", diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index 014666017c..820a616382 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -5172,7 +5172,7 @@ "bar-appearance":"Wygląd słupka", "label-on-bar":"Etykieta na słupku", "value-on-bar":"Wartość na słupku", - "bar-chart-card-style":"Styl karty wykresu słupkowego" + "bar-chart-style":"Styl wykresu słupkowego" }, "battery-level":{ "layout":"Układ", From c8b2a2a6cda31257258e6dceae45fa82d8414792 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2024 19:26:25 +0200 Subject: [PATCH 6/8] UI: Range chart widget config improvements. Update range chart basic config. --- .../range-chart-basic-config.component.html | 153 ++++++++++++++++-- .../range-chart-basic-config.component.ts | 153 +++++++++++++++++- .../lib/chart/range-chart-widget.models.ts | 109 ++++++++++--- .../lib/chart/time-series-chart.models.ts | 44 +++-- .../assets/locale/locale.constant-en_US.json | 3 + 5 files changed, 400 insertions(+), 62 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html index bb64c59302..4bda5fa9b5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html @@ -75,6 +75,21 @@ {{ 'widgets.range-chart.data-zoom' | translate }}
+
+
+
widgets.range-chart.range-chart-appearance
+
+
widget-config.units-short
+ + +
+
+
widget-config.decimals-short
+ + + +
{{ 'widgets.range-chart.range-colors' | translate }}
@@ -87,24 +102,139 @@ formControlName="outOfRangeColor">
+
+ + {{ 'widgets.range-chart.show-range-thresholds' | translate }} + +
{{ 'widgets.range-chart.fill-area' | translate }}
-
-
widget-config.units-short
- - -
-
-
widget-config.decimals-short
- - +
+
widgets.range-chart.fill-area-opacity
+ + +
+
+
widgets.time-series-chart.series.line.line
+
+ + {{ 'widgets.time-series-chart.series.line.show-line' | translate }} +
+
+ + {{ 'widgets.time-series-chart.series.line.step-line' | translate }} + + + + + {{ lineSeriesStepTypeTranslations.get(stepType) | translate }} + + + +
+
+ + {{ 'widgets.time-series-chart.series.line.smooth-line' | translate }} + +
+
+
widgets.time-series-chart.line-type
+ + + + {{ timeSeriesLineTypeTranslations.get(lineType) | translate }} + + + +
+
+
widgets.time-series-chart.line-width
+ + + +
+
+
+
widgets.time-series-chart.series.point.points
+
+ + {{ 'widgets.time-series-chart.series.point.show-points' | translate }} + +
+
+ +
+ {{ 'widgets.time-series-chart.series.point.point-label' | translate }} +
+
+
+ + + + {{ seriesLabelPositionTranslations.get(position) | translate }} + + + + + + + +
+
+
+
widgets.time-series-chart.series.point.point-shape
+ + + + {{ timeSeriesChartShapeTranslations.get(shape) | translate }} + + + +
+
+
widgets.time-series-chart.series.point.point-size
+ + + +
+
+
+
+
widgets.time-series-chart.axis.y-axis
+ + +
+
+
widgets.time-series-chart.axis.x-axis
+ +
+ +
@@ -204,6 +334,9 @@
+ +
widget-config.card-appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts index 566dd4d86e..fc006d05a3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts @@ -14,13 +14,19 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Injector } from '@angular/core'; +import { Component, Injector } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; -import { DataKey, legendPositions, legendPositionTranslationMap, WidgetConfig, } from '@shared/models/widget.models'; +import { + DataKey, + Datasource, + legendPositions, + legendPositionTranslationMap, + WidgetConfig, +} from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { @@ -38,6 +44,16 @@ import { rangeChartDefaultSettings, RangeChartWidgetSettings } from '@home/components/widget/lib/chart/range-chart-widget.models'; +import { + lineSeriesStepTypes, + lineSeriesStepTypeTranslations, + seriesLabelPositions, + seriesLabelPositionTranslations, + timeSeriesChartShapes, + timeSeriesChartShapeTranslations, + timeSeriesLineTypes, + timeSeriesLineTypeTranslations +} from '@home/components/widget/lib/chart/time-series-chart.models'; @Component({ selector: 'tb-range-chart-basic-config', @@ -46,12 +62,39 @@ import { }) export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { + public get datasource(): Datasource { + const datasources: Datasource[] = this.rangeChartWidgetConfigForm.get('datasources').value; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + lineSeriesStepTypes = lineSeriesStepTypes; + + lineSeriesStepTypeTranslations = lineSeriesStepTypeTranslations; + + timeSeriesLineTypes = timeSeriesLineTypes; + + timeSeriesLineTypeTranslations = timeSeriesLineTypeTranslations; + + seriesLabelPositions = seriesLabelPositions; + + seriesLabelPositionTranslations = seriesLabelPositionTranslations; + + timeSeriesChartShapes = timeSeriesChartShapes; + + timeSeriesChartShapeTranslations = timeSeriesChartShapeTranslations; + legendPositions = legendPositions; legendPositionTranslationMap = legendPositionTranslationMap; rangeChartWidgetConfigForm: UntypedFormGroup; + pointLabelPreviewFn = this._pointLabelPreviewFn.bind(this); + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); @@ -90,11 +133,36 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { iconColor: [configData.config.iconColor, []], dataZoom: [settings.dataZoom, []], + + units: [configData.config.units, []], + decimals: [configData.config.decimals, []], rangeColors: [settings.rangeColors, []], outOfRangeColor: [settings.outOfRangeColor, []], + showRangeThresholds: [settings.showRangeThresholds, []], fillArea: [settings.fillArea, []], - units: [configData.config.units, []], - decimals: [configData.config.decimals, []], + fillAreaOpacity: [settings.fillAreaOpacity, [Validators.min(0), Validators.max(100)]], + + 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, []], + 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, []], @@ -139,12 +207,39 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + this.widgetConfig.config.settings.dataZoom = config.dataZoom; + + this.widgetConfig.config.units = config.units; + this.widgetConfig.config.decimals = config.decimals; + 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.fillArea = config.fillArea; - this.widgetConfig.config.units = config.units; - this.widgetConfig.config.decimals = config.decimals; + this.widgetConfig.config.settings.fillAreaOpacity = config.fillAreaOpacity; + + this.widgetConfig.config.settings.showLine = config.showLine; + this.widgetConfig.config.settings.step = config.step; + this.widgetConfig.config.settings.stepType = config.stepType; + this.widgetConfig.config.settings.smooth = config.smooth; + this.widgetConfig.config.settings.lineType = config.lineType; + this.widgetConfig.config.settings.lineWidth = config.lineWidth; + + this.widgetConfig.config.settings.showPoints = config.showPoints; + this.widgetConfig.config.settings.showPointLabel = config.showPointLabel; + 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.pointShape = config.pointShape; + this.widgetConfig.config.settings.pointSize = config.pointSize; + + this.widgetConfig.config.settings.yAxis = config.yAxis; + this.widgetConfig.config.settings.xAxis = config.xAxis; + + this.widgetConfig.config.settings.thresholds = config.thresholds; + + this.widgetConfig.config.settings.animation = config.animation; this.widgetConfig.config.settings.showLegend = config.showLegend; this.widgetConfig.config.settings.legendPosition = config.legendPosition; @@ -173,12 +268,16 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { } protected validatorTriggers(): string[] { - return ['showTitle', 'showIcon', 'showLegend', 'showTooltip', 'tooltipShowDate']; + return ['showTitle', 'showIcon', 'fillArea', 'showLine', 'step', 'showPointLabel', '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 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 showLegend: boolean = this.rangeChartWidgetConfigForm.get('showLegend').value; const showTooltip: boolean = this.rangeChartWidgetConfigForm.get('showTooltip').value; const tooltipShowDate: boolean = this.rangeChartWidgetConfigForm.get('tooltipShowDate').value; @@ -210,6 +309,40 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { this.rangeChartWidgetConfigForm.get('iconColor').disable(); } + if (fillArea) { + this.rangeChartWidgetConfigForm.get('fillAreaOpacity').enable(); + } else { + this.rangeChartWidgetConfigForm.get('fillAreaOpacity').disable(); + } + + if (showLine) { + this.rangeChartWidgetConfigForm.get('step').enable({emitEvent: false}); + if (step) { + this.rangeChartWidgetConfigForm.get('stepType').enable(); + this.rangeChartWidgetConfigForm.get('smooth').disable(); + } else { + this.rangeChartWidgetConfigForm.get('stepType').disable(); + this.rangeChartWidgetConfigForm.get('smooth').enable(); + } + this.rangeChartWidgetConfigForm.get('lineType').enable(); + this.rangeChartWidgetConfigForm.get('lineWidth').enable(); + } else { + this.rangeChartWidgetConfigForm.get('step').disable({emitEvent: false}); + this.rangeChartWidgetConfigForm.get('stepType').disable(); + this.rangeChartWidgetConfigForm.get('smooth').disable(); + this.rangeChartWidgetConfigForm.get('lineType').disable(); + this.rangeChartWidgetConfigForm.get('lineWidth').disable(); + } + if (showPointLabel) { + this.rangeChartWidgetConfigForm.get('pointLabelPosition').enable(); + this.rangeChartWidgetConfigForm.get('pointLabelFont').enable(); + this.rangeChartWidgetConfigForm.get('pointLabelColor').enable(); + } else { + this.rangeChartWidgetConfigForm.get('pointLabelPosition').disable(); + this.rangeChartWidgetConfigForm.get('pointLabelFont').disable(); + this.rangeChartWidgetConfigForm.get('pointLabelColor').disable(); + } + if (showLegend) { this.rangeChartWidgetConfigForm.get('legendPosition').enable(); this.rangeChartWidgetConfigForm.get('legendLabelFont').enable(); @@ -262,6 +395,12 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { config.enableFullscreen = buttons.includes('fullscreen'); } + private _pointLabelPreviewFn(): string { + const units: string = this.rangeChartWidgetConfigForm.get('units').value; + const decimals: number = this.rangeChartWidgetConfigForm.get('decimals').value; + return formatValue(22, decimals, units, false); + } + private _tooltipValuePreviewFn(): string { const units: string = this.rangeChartWidgetConfigForm.get('units').value; const decimals: number = this.rangeChartWidgetConfigForm.get('decimals').value; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts index ee7b2835e6..eb6ea4a397 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts @@ -27,16 +27,25 @@ import { LegendPosition } from '@shared/models/widget.models'; import { EChartsTooltipWidgetSettings } from '@home/components/widget/lib/chart/echarts-widget.models'; import { createTimeSeriesChartVisualMapPiece, + defaultTimeSeriesChartXAxisSettings, + defaultTimeSeriesChartYAxisSettings, + LineSeriesStepType, SeriesFillType, + SeriesLabelPosition, timeSeriesChartAnimationDefaultSettings, + TimeSeriesChartAnimationSettings, + timeSeriesChartColorScheme, TimeSeriesChartKeySettings, + TimeSeriesChartLineType, TimeSeriesChartSeriesType, TimeSeriesChartSettings, TimeSeriesChartShape, TimeSeriesChartThreshold, TimeSeriesChartThresholdType, - TimeSeriesChartVisualMapPiece + TimeSeriesChartVisualMapPiece, + TimeSeriesChartXAxisSettings, + TimeSeriesChartYAxisSettings } from '@home/components/widget/lib/chart/time-series-chart.models'; -import { isNumber } from '@core/utils'; +import { isNumber, mergeDeep } from '@core/utils'; import { DeepPartial } from '@shared/models/common'; export interface RangeItem { @@ -54,7 +63,26 @@ export interface RangeChartWidgetSettings extends EChartsTooltipWidgetSettings { dataZoom: boolean; rangeColors: Array; outOfRangeColor: string; + showRangeThresholds: boolean; fillArea: boolean; + fillAreaOpacity: number; + showLine: boolean; + step: boolean; + stepType: LineSeriesStepType; + smooth: boolean; + lineType: TimeSeriesChartLineType; + lineWidth: number; + showPoints: boolean; + showPointLabel: boolean; + pointLabelPosition: SeriesLabelPosition; + pointLabelFont: Font; + pointLabelColor: string; + pointShape: TimeSeriesChartShape; + pointSize: number; + yAxis: TimeSeriesChartYAxisSettings; + xAxis: TimeSeriesChartXAxisSettings; + animation: TimeSeriesChartAnimationSettings; + thresholds: TimeSeriesChartThreshold[]; showLegend: boolean; legendPosition: LegendPosition; legendLabelFont: Font; @@ -75,7 +103,38 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = { {from: 40, color: '#D81838'} ], outOfRangeColor: '#ccc', + showRangeThresholds: true, fillArea: true, + fillAreaOpacity: 0.7, + showLine: true, + step: false, + stepType: LineSeriesStepType.start, + smooth: false, + 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, + yAxis: mergeDeep({} as TimeSeriesChartYAxisSettings, + defaultTimeSeriesChartYAxisSettings, + { id: 'default', order: 0, showLine: false, showTicks: false } as TimeSeriesChartYAxisSettings), + xAxis: mergeDeep({} as TimeSeriesChartXAxisSettings, + defaultTimeSeriesChartXAxisSettings, + {showSplitLines: false} as TimeSeriesChartXAxisSettings), + animation: mergeDeep({} as TimeSeriesChartAnimationSettings, + timeSeriesChartAnimationDefaultSettings), + thresholds: [], showLegend: true, legendPosition: LegendPosition.top, legendLabelFont: { @@ -125,7 +184,7 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = { export const rangeChartTimeSeriesSettings = (settings: RangeChartWidgetSettings, rangeItems: RangeItem[], decimals: number, units: string): DeepPartial => { - const thresholds: DeepPartial[] = getMarkPoints(rangeItems).map(item => ({ + let thresholds: DeepPartial[] = settings.showRangeThresholds ? getMarkPoints(rangeItems).map(item => ({ type: TimeSeriesChartThresholdType.constant, yAxisId: 'default', units, @@ -146,28 +205,24 @@ export const rangeChartTimeSeriesSettings = (settings: RangeChartWidgetSettings, borderRadius: 4, }, value: item - } as DeepPartial)); + } as DeepPartial)) : []; + if (settings.thresholds?.length) { + thresholds = thresholds.concat(settings.thresholds); + } return { dataZoom: settings.dataZoom, thresholds, yAxes: { default: { - show: true, - showLine: false, - showTicks: false, - showTickLabels: true, - showSplitLines: true, - decimals, - units + ...settings.yAxis, + ...{ + decimals, + units + } } }, - xAxis: { - show: true, - showLine: true, - showTicks: true, - showTickLabels: true, - showSplitLines: false - }, + xAxis: settings.xAxis, + animation: settings.animation, visualMapSettings: { outOfRangeColor: settings.outOfRangeColor, pieces: rangeItems.map(item => item.piece) @@ -188,12 +243,22 @@ export const rangeChartTimeSeriesSettings = (settings: RangeChartWidgetSettings, export const rangeChartTimeSeriesKeySettings = (settings: RangeChartWidgetSettings): DeepPartial => ({ type: TimeSeriesChartSeriesType.line, lineSettings: { - showLine: true, - smooth: false, - showPoints: false, + showLine: settings.showLine, + step: settings.step, + stepType: settings.stepType, + smooth: settings.smooth, + lineType: settings.lineType, + lineWidth: settings.lineWidth, + showPoints: settings.showPoints, + showPointLabel: settings.showPointLabel, + pointLabelPosition: settings.pointLabelPosition, + pointLabelFont: settings.pointLabelFont, + pointLabelColor: settings.pointLabelColor, + pointShape: settings.pointShape, + pointSize: settings.pointSize, fillAreaSettings: { type: settings.fillArea ? SeriesFillType.opacity : SeriesFillType.none, - opacity: 0.7 + opacity: settings.fillAreaOpacity } } }); 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 df47a03692..4dc36b6682 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 @@ -81,7 +81,7 @@ export const timeSeriesChartTypeTranslations = new Map { - let result: string; - try { - result = formatFunction(params.value[1]); - } catch (_e) { - } - if (isUndefined(result)) { - result = formatValue(params.value[1], item.decimals, item.units, false); - } - return `{value|${result}}`; - }; - } else { - formatter = (params): string => { - const value = formatValue(params.value[1], item.decimals, item.units, false); - return `{value|${value}}`; - }; - } + if (isFunction(labelFormatter)) { + formatter = labelFormatter as LabelFormatterCallback; + } else if (labelFormatter?.length) { + const formatFunction = parseFunction(labelFormatter, ['value']); + formatter = (params): string => { + let result: string; + try { + result = formatFunction(params.value[1]); + } catch (_e) { + } + if (isUndefined(result)) { + result = formatValue(params.value[1], item.decimals, item.units, false); + } + return `{value|${result}}`; + }; + } else { + formatter = (params): string => { + const value = formatValue(params.value[1], item.decimals, item.units, false); + return `{value|${value}}`; + }; } return { show, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 43e20a46ce..b2dbc2b29f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6172,9 +6172,12 @@ "range-chart": { "chart": "Chart", "data-zoom": "Data zoom", + "range-chart-appearance": "Range chart appearance", "range-colors": "Range colors", "out-of-range-color": "Out of range color", + "show-range-thresholds": "Show range thresholds", "fill-area": "Fill area", + "fill-area-opacity": "Fill area opacity", "range-chart-card-style": "Range chart card style" }, "rpc": { From 1a9e69c762982400deb06a9b06efd2bb207e1aef Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 29 Mar 2024 18:58:16 +0200 Subject: [PATCH 7/8] UI: Update range chart advanced settings. Add label background option to series/threshold labels. Minor improvements and fixes. --- .../range-chart-basic-config.component.html | 26 ++- .../range-chart-basic-config.component.ts | 34 ++- .../widget/lib/chart/echarts-widget.models.ts | 101 ++++++++- .../lib/chart/range-chart-widget.models.ts | 49 +++-- .../lib/chart/time-series-chart-bar.models.ts | 3 + .../lib/chart/time-series-chart.models.ts | 99 +++++---- .../widget/lib/chart/time-series-chart.ts | 7 +- ...range-chart-widget-settings.component.html | 202 +++++++++++++++--- .../range-chart-widget-settings.component.ts | 127 ++++++++++- ...e-series-chart-bar-settings.component.html | 9 + ...ime-series-chart-bar-settings.component.ts | 14 +- ...-series-chart-line-settings.component.html | 13 +- ...me-series-chart-line-settings.component.ts | 22 +- ...-series-chart-fill-settings.component.html | 4 +- ...me-series-chart-fill-settings.component.ts | 2 +- ...-series-chart-threshold-row.component.html | 15 +- ...me-series-chart-threshold-row.component.ts | 74 +++---- ...rt-threshold-settings-panel.component.html | 19 +- ...hart-threshold-settings-panel.component.ts | 40 +++- ...es-chart-threshold-settings.component.html | 39 ++++ ...ries-chart-threshold-settings.component.ts | 124 +++++++++++ .../common/widget-settings-common.module.ts | 5 + .../assets/locale/locale.constant-en_US.json | 10 +- .../assets/locale/locale.constant-pl_PL.json | 2 +- .../assets/locale/locale.constant-zh_CN.json | 2 +- 25 files changed, 834 insertions(+), 208 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html index 4bda5fa9b5..eb94f9c977 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html @@ -102,10 +102,17 @@ formControlName="outOfRangeColor">
-
+
{{ 'widgets.range-chart.show-range-thresholds' | translate }} + +
@@ -115,8 +122,8 @@
widgets.range-chart.fill-area-opacity
- +
@@ -193,12 +200,21 @@
+
+ + {{ 'widgets.time-series-chart.series.point.point-label-background' | translate }} + + + +
widgets.time-series-chart.series.point.point-shape
- - {{ timeSeriesChartShapeTranslations.get(shape) | translate }} + + {{ echartsShapeTranslations.get(shape) | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts index fc006d05a3..40c9bb9d12 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts +++ b/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) { 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 c9c4b319cc..81ffb457b9 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 @@ -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.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.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; 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] => { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts index eb6ea4a397..e04dccf74d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts +++ b/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; outOfRangeColor: string; showRangeThresholds: boolean; + rangeThreshold: Partial; 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, + 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 => { let thresholds: DeepPartial[] = 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)) : []; 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: { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-bar.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-bar.models.ts index 8ebe189ee0..ee3c703a17 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-bar.models.ts +++ b/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}; 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 4dc36b6682..3589d69d4f 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 @@ -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( ] ); -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.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; }; 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 98cc1fd454..00f2c55d3f 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 @@ -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); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html index 4a7331d4c2..a19b8eb845 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html @@ -17,29 +17,175 @@ -->
-
widgets.range-chart.range-chart-card-style
+
widgets.range-chart.range-chart-style
{{ 'widgets.range-chart.data-zoom' | translate }}
-
-
{{ 'widgets.range-chart.range-colors' | translate }}
- - +
+
widgets.range-chart.range-chart-appearance
+
+
{{ 'widgets.range-chart.range-colors' | translate }}
+ + +
+
+
{{ 'widgets.range-chart.out-of-range-color' | translate }}
+ + +
+
+ + {{ 'widgets.range-chart.show-range-thresholds' | translate }} + + + +
+
+ + {{ 'widgets.range-chart.fill-area' | translate }} + +
+
+
widgets.range-chart.fill-area-opacity
+ + + +
+
+
widgets.time-series-chart.series.line.line
+
+ + {{ 'widgets.time-series-chart.series.line.show-line' | translate }} + +
+
+ + {{ 'widgets.time-series-chart.series.line.step-line' | translate }} + + + + + {{ lineSeriesStepTypeTranslations.get(stepType) | translate }} + + + +
+
+ + {{ 'widgets.time-series-chart.series.line.smooth-line' | translate }} + +
+
+
widgets.time-series-chart.line-type
+ + + + {{ timeSeriesLineTypeTranslations.get(lineType) | translate }} + + + +
+
+
widgets.time-series-chart.line-width
+ + + +
+
+
+
widgets.time-series-chart.series.point.points
+
+ + {{ 'widgets.time-series-chart.series.point.show-points' | translate }} + +
+
+ +
+ {{ 'widgets.time-series-chart.series.point.point-label' | translate }} +
+
+
+ + + + {{ seriesLabelPositionTranslations.get(position) | translate }} + + + + + + + +
+
+
+ + {{ 'widgets.time-series-chart.series.point.point-label-background' | translate }} + + + +
+
+
widgets.time-series-chart.series.point.point-shape
+ + + + {{ echartsShapeTranslations.get(shape) | translate }} + + + +
+
+
widgets.time-series-chart.series.point.point-size
+ + + +
+
-
-
{{ 'widgets.range-chart.out-of-range-color' | translate }}
- - +
+
widgets.time-series-chart.axis.y-axis
+ +
-
- - {{ 'widgets.range-chart.fill-area' | translate }} - +
+
widgets.time-series-chart.axis.x-axis
+ +
+ +
@@ -139,16 +285,22 @@
-
-
{{ 'widgets.background.background' | translate }}
- - -
-
-
{{ 'widget-config.card-padding' | translate }}
- - - + + +
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts index 4ef2971467..b9a058af24 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts +++ b/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; } - } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.html index 447784091e..ddbeaf2480 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.html @@ -59,6 +59,15 @@
+
+ + {{ 'widgets.time-series-chart.series.bar.label-background' | translate }} + + + +
{ 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}); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html index 78f1129727..cc3102c475 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html @@ -97,12 +97,21 @@
+
+ + {{ 'widgets.time-series-chart.series.point.point-label-background' | translate }} + + + +
widgets.time-series-chart.series.point.point-shape
- - {{ timeSeriesChartShapeTranslations.get(shape) | translate }} + + {{ echartsShapeTranslations.get(shape) | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts index 84533b26e4..5b4701bf07 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts +++ b/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}); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html index 934f985483..99e3c1be8f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html @@ -27,8 +27,8 @@
widgets.time-series-chart.series.opacity
- +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts index 44cc870f5b..9fbc464348 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts +++ b/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)]] diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html index 386ea306a0..d0ea8f0ef3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html @@ -98,14 +98,13 @@
- + + + + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.ts new file mode 100644 index 0000000000..6908b086af --- /dev/null +++ b/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; + + 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): 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); + }); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index 08d45775c5..86e460ff9d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts +++ b/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 ], diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b2dbc2b29f..43bd687886 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/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" } } }, diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index 820a616382..58d4a0168f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/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", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index fdc0274cdf..886151fdf2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/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": "值设置", From e761b1ec4754f14841f4a2a61c481505e0aacd36 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 29 Mar 2024 19:37:41 +0200 Subject: [PATCH 8/8] UI: Fix scss style path. --- .../common/chart/time-series-chart-fill-settings.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts index 9fbc464348..dd7f4dd7f6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts @@ -34,7 +34,7 @@ import { AppState } from '@core/core.state'; @Component({ selector: 'tb-time-series-chart-fill-settings', templateUrl: './time-series-chart-fill-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR,