From 2fb604e398f82d24b98c04f607e03f10b5b14bfb Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 27 Mar 2024 20:21:28 +0200 Subject: [PATCH] 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;