diff --git a/ui-ngx/patches/echarts+5.5.0.patch b/ui-ngx/patches/echarts+5.5.0.patch index 39551ac137..f74beb7ea8 100644 --- a/ui-ngx/patches/echarts+5.5.0.patch +++ b/ui-ngx/patches/echarts+5.5.0.patch @@ -207,3 +207,144 @@ index b8a9b95..8e4cb2f 100644 return; } var axisValueLabel = axisPointerViewHelper.getValueLabel(axisValue, axisModel.axis, ecModel, axisItem.seriesDataIndices, axisItem.valueLabelOpt); +diff --git a/node_modules/echarts/lib/coord/axisHelper.js b/node_modules/echarts/lib/coord/axisHelper.js +index a76c66b..be22cb0 100644 +--- a/node_modules/echarts/lib/coord/axisHelper.js ++++ b/node_modules/echarts/lib/coord/axisHelper.js +@@ -187,7 +187,9 @@ export function createScaleByModel(model, axisType) { + }); + default: + // case 'value'/'interval', 'log', or others. +- return new (Scale.getClass(axisType) || IntervalScale)(); ++ return new (Scale.getClass(axisType) || IntervalScale)({ ++ ticksGenerator: model.get('ticksGenerator') ++ }); + } + } + } +diff --git a/node_modules/echarts/lib/coord/cartesian/Grid.js b/node_modules/echarts/lib/coord/cartesian/Grid.js +index 5b18f02..4960e67 100644 +--- a/node_modules/echarts/lib/coord/cartesian/Grid.js ++++ b/node_modules/echarts/lib/coord/cartesian/Grid.js +@@ -91,11 +91,11 @@ var Grid = /** @class */function () { + var scale = axis.scale; + if ( + // Only value and log axis without interval support alignTicks. +- isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null) { ++ isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null && model.get('ticksGenerator') == null) { + axisNeedsAlign.push(axis); + } else { + niceScaleExtent(scale, model); +- if (isIntervalOrLogScale(scale)) { ++ if (isIntervalOrLogScale(scale) && !scale.isBlank()) { + // Can only align to interval or log axis. + alignTo = axis; + } +@@ -105,10 +105,15 @@ var Grid = /** @class */function () { + // All axes has set alignTicks. Pick the first one. + // PENDING. Should we find the axis that both set interval, min, max and align to this one? + if (axisNeedsAlign.length) { +- if (!alignTo) { +- alignTo = axisNeedsAlign.pop(); +- niceScaleExtent(alignTo.scale, alignTo.model); ++ while (!alignTo && axisNeedsAlign.length) { ++ var axis = axisNeedsAlign.pop(); ++ niceScaleExtent(axis.scale, axis.model); ++ if (!axis.scale.isBlank()) { ++ alignTo = axis; ++ } + } ++ } ++ if (axisNeedsAlign.length && alignTo) { + each(axisNeedsAlign, function (axis) { + alignScaleTicks(axis.scale, axis.model, alignTo.scale); + }); +diff --git a/node_modules/echarts/lib/scale/Interval.js b/node_modules/echarts/lib/scale/Interval.js +index 1094662..8f4e07a 100644 +--- a/node_modules/echarts/lib/scale/Interval.js ++++ b/node_modules/echarts/lib/scale/Interval.js +@@ -46,12 +46,17 @@ import * as numberUtil from '../util/number.js'; + import * as formatUtil from '../util/format.js'; + import Scale from './Scale.js'; + import * as helper from './helper.js'; ++import { isFunction } from 'zrender/lib/core/util.js'; + var roundNumber = numberUtil.round; + var IntervalScale = /** @class */function (_super) { + __extends(IntervalScale, _super); +- function IntervalScale() { +- var _this = _super !== null && _super.apply(this, arguments) || this; ++ function IntervalScale(setting) { ++ var _this = _super.call(this, setting) || this; + _this.type = 'interval'; ++ var ticksGenerator = _this.getSetting('ticksGenerator'); ++ if (isFunction(ticksGenerator)) { ++ _this._ticksGenerator = ticksGenerator; ++ } + // Step is calculated in adjustExtent. + _this._interval = 0; + _this._intervalPrecision = 2; +@@ -104,7 +109,17 @@ var IntervalScale = /** @class */function (_super) { + var extent = this._extent; + var niceTickExtent = this._niceExtent; + var intervalPrecision = this._intervalPrecision; +- var ticks = []; ++ var ticksGenerator = this._ticksGenerator; ++ var ticks; ++ if (ticksGenerator) { ++ try { ++ ticks = ticksGenerator(extent, interval, niceTickExtent, intervalPrecision); ++ if (ticks) { ++ return ticks; ++ } ++ } catch (_e) {} ++ } ++ ticks = []; + // If interval is 0, return []; + if (!interval) { + return ticks; +diff --git a/node_modules/echarts/types/dist/shared.d.ts b/node_modules/echarts/types/dist/shared.d.ts +index ca74097..98f8b18 100644 +--- a/node_modules/echarts/types/dist/shared.d.ts ++++ b/node_modules/echarts/types/dist/shared.d.ts +@@ -2422,6 +2422,9 @@ interface AxisBaseOptionCommon extends ComponentOption, AnimationOptionMixin { + max: number; + }) => ScaleDataValue); + } ++ ++declare type NumericAxisTicksGenerator = (extent?: number[], interval?: number, niceTickExtent?: number[], intervalPrecision?: number) => {value: number}[]; ++ + interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon { + boundaryGap?: [number | string, number | string]; + /** +@@ -2447,6 +2450,8 @@ interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon { + * Will be ignored if interval is set. + */ + alignTicks?: boolean; ++ ++ ticksGenerator?: NumericAxisTicksGenerator; + } + interface CategoryAxisBaseOption extends AxisBaseOptionCommon { + type?: 'category'; +diff --git a/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts b/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts +index c5c2792..d524b70 100644 +--- a/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts ++++ b/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts +@@ -56,6 +56,9 @@ export interface AxisBaseOptionCommon extends ComponentOption, AnimationOptionMi + max: number; + }) => ScaleDataValue); + } ++ ++export declare type NumericAxisTicksGenerator = (extent?: number[], interval?: number, niceTickExtent?: number[], intervalPrecision?: number) => {value: number}[]; ++ + export interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon { + boundaryGap?: [number | string, number | string]; + /** +@@ -81,6 +84,8 @@ export interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon { + * Will be ignored if interval is set. + */ + alignTicks?: boolean; ++ ++ ticksGenerator?: NumericAxisTicksGenerator; + } + export interface CategoryAxisBaseOption extends AxisBaseOptionCommon { + type?: 'category'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts index 7ed3e9c1cd..908ed263e4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts @@ -44,7 +44,6 @@ import { getDataKey, getLatestSingleTsValue, overlayStyle, - simpleDateFormat, textStyle } from '@shared/models/widget-settings.models'; import { DataKey } from '@shared/models/widget.models'; @@ -55,10 +54,9 @@ import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; import { TbTimeSeriesChart } from '@home/components/widget/lib/chart/time-series-chart'; import { - TimeSeriesChartAxisSettings, TimeSeriesChartKeySettings, TimeSeriesChartSeriesType, - TimeSeriesChartSettings, TimeSeriesChartYAxisSettings + TimeSeriesChartSettings } from '@home/components/widget/lib/chart/time-series-chart.models'; import { DeepPartial } from '@shared/models/common'; @@ -189,8 +187,7 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit showSplitLines: true, min: 'dataMin', max: 'dataMax', - intervalCalculator: - 'var scale = axis.scale; return !scale.isBlank() ? ((scale.getExtent()[1] - scale.getExtent()[0]) / 2) : undefined;' + ticksGenerator: (extent?: number[]) => (extent ? [{ value: (extent[0] + extent[1]) / 2}] : []) } }, tooltipDateInterval: false, 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 3c2343853c..fba1a9f8e2 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 @@ -19,21 +19,23 @@ import { EChartsOption, EChartsSeriesItem, EChartsTooltipTrigger, - EChartsTooltipWidgetSettings, getYAxis, + EChartsTooltipWidgetSettings, measureThresholdLabelOffset } from '@home/components/widget/lib/chart/echarts-widget.models'; import { - autoDateFormat, AutoDateFormatSettings, + autoDateFormat, + AutoDateFormatSettings, ComponentStyle, Font, - simpleDateFormat, - textStyle, tsToFormatTimeUnit + textStyle, + tsToFormatTimeUnit } from '@shared/models/widget-settings.models'; import { XAXisOption, YAXisOption } from 'echarts/types/dist/shared'; import { CustomSeriesOption, LineSeriesOption } from 'echarts/charts'; import { formatValue, isDefinedAndNotNull, + isFunction, isNumeric, isUndefined, isUndefinedOrNull, @@ -42,7 +44,6 @@ import { } from '@core/utils'; import { LinearGradientObject } from 'zrender/lib/graphic/LinearGradient'; import tinycolor from 'tinycolor2'; -import Axis2D from 'echarts/types/src/coord/cartesian/Axis2D'; import { ValueAxisBaseOption } from 'echarts/types/src/coord/axisCommonTypes'; import { SeriesLabelOption } from 'echarts/types/src/util/types'; import { @@ -320,6 +321,12 @@ export const defaultXAxisTicksFormat: AutoDateFormatSettings = { export type TimeSeriesChartYAxisId = 'default' | string; +export type TimeSeriesChartTicksGenerator = + (extent?: number[], interval?: number, niceTickExtent?: number[], intervalPrecision?: number) => {value: number}[]; + +export type TimeSeriesChartTicksFormatter = + (value: any) => string; + export interface TimeSeriesChartYAxisSettings extends TimeSeriesChartAxisSettings { id?: TimeSeriesChartYAxisId; order?: number; @@ -329,8 +336,8 @@ export interface TimeSeriesChartYAxisSettings extends TimeSeriesChartAxisSetting splitNumber?: number; min?: number | string; max?: number | string; - intervalCalculator?: string; - ticksFormatter?: string; + ticksGenerator?: TimeSeriesChartTicksGenerator | string; + ticksFormatter?: TimeSeriesChartTicksFormatter | string; } export const timeSeriesChartYAxisValid = (axis: TimeSeriesChartYAxisSettings): boolean => @@ -788,8 +795,6 @@ export interface TimeSeriesChartYAxis { decimals: number; settings: TimeSeriesChartYAxisSettings; option: YAXisOption & ValueAxisBaseOption; - intervalCalculator?: (axis: Axis2D) => number; - ticksFormatter?: (value: any) => string; } export const createTimeSeriesYAxis = (units: string, @@ -800,11 +805,37 @@ export const createTimeSeriesYAxis = (units: string, settings.tickLabelColor, darkMode, 'axis.tickLabel'); const yAxisNameStyle = createChartTextStyle(settings.labelFont, settings.labelColor, darkMode, 'axis.label'); - let ticksFormatter: (value: any) => string; - if (settings.ticksFormatter && settings.ticksFormatter.length) { - ticksFormatter = parseFunction(settings.ticksFormatter, ['value']); + + let ticksFormatter: TimeSeriesChartTicksFormatter; + if (settings.ticksFormatter) { + if (isFunction(settings.ticksFormatter)) { + ticksFormatter = settings.ticksFormatter as TimeSeriesChartTicksFormatter; + } else if (settings.ticksFormatter.length) { + ticksFormatter = parseFunction(settings.ticksFormatter, ['value']); + } } - const yAxis: TimeSeriesChartYAxis = { + let ticksGenerator: TimeSeriesChartTicksGenerator; + let minInterval: number; + let interval: number; + let splitNumber: number; + if (settings.ticksGenerator) { + if (isFunction(settings.ticksGenerator)) { + ticksGenerator = settings.ticksGenerator as TimeSeriesChartTicksGenerator; + } else if (settings.ticksGenerator.length) { + ticksGenerator = parseFunction(settings.ticksGenerator, ['extent', 'interval', 'niceTickExtent', 'intervalPrecision']); + } + } + if (!ticksGenerator) { + interval = settings.interval; + if (isUndefinedOrNull(interval)) { + if (isDefinedAndNotNull(settings.splitNumber)) { + splitNumber = settings.splitNumber; + } else { + minInterval = (1 / Math.pow(10, decimals)); + } + } + } + return { id: settings.id, decimals, settings, @@ -818,6 +849,10 @@ export const createTimeSeriesYAxis = (units: string, scale: true, min: settings.min, max: settings.max, + minInterval, + splitNumber, + interval, + ticksGenerator, name: settings.label, nameLocation: 'middle', nameRotate: settings.position === AxisPosition.left ? 90 : -90, @@ -853,7 +888,8 @@ export const createTimeSeriesYAxis = (units: string, if (ticksFormatter) { try { result = ticksFormatter(value); - } catch (_e) {} + } catch (_e) { + } } if (isUndefined(result)) { result = formatValue(value, decimals, units, false); @@ -869,54 +905,6 @@ export const createTimeSeriesYAxis = (units: string, } } }; - if (settings.intervalCalculator && settings.intervalCalculator.length) { - yAxis.intervalCalculator = parseFunction(settings.intervalCalculator, ['axis']); - } - return yAxis; -}; - -export const updateYAxisIntervals = (chart: ECharts, - yAxis: TimeSeriesChartYAxis, empty: boolean): boolean => { - let changed = false; - let interval: number; - let splitNumber: number; - let minInterval: number; - if (!empty) { - interval = calculateYAxisInterval(chart, yAxis); - if (isUndefinedOrNull(interval)) { - if (isDefinedAndNotNull(yAxis.settings.splitNumber)) { - splitNumber = yAxis.settings.splitNumber; - } else { - minInterval = (1 / Math.pow(10, yAxis.decimals)); - } - } - } - if (yAxis.option.interval !== interval) { - yAxis.option.interval = interval; - changed = true; - } - if (yAxis.option.splitNumber !== splitNumber) { - yAxis.option.splitNumber = splitNumber; - changed = true; - } - if (yAxis.option.minInterval !== minInterval) { - yAxis.option.minInterval = minInterval; - changed = true; - } - return changed; -}; - -const calculateYAxisInterval = (chart: ECharts, yAxis: TimeSeriesChartYAxis): number | undefined => { - let interval = yAxis.settings.interval; - if (yAxis.intervalCalculator) { - const axis = getYAxis(chart, yAxis.id); - if (axis) { - try { - interval = yAxis.intervalCalculator(axis); - } catch (_e) {} - } - } - return interval; }; export const createTimeSeriesXAxisOption = (settings: TimeSeriesChartXAxisSettings, 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 521a3ce06e..cbb6c2e3e0 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 @@ -27,7 +27,8 @@ import { TimeSeriesChartDataItem, timeSeriesChartDefaultSettings, timeSeriesChartKeyDefaultSettings, - TimeSeriesChartKeySettings, TimeSeriesChartNoAggregationBarWidthStrategy, + TimeSeriesChartKeySettings, + TimeSeriesChartNoAggregationBarWidthStrategy, TimeSeriesChartSeriesType, TimeSeriesChartSettings, TimeSeriesChartShape, @@ -39,7 +40,7 @@ import { TimeSeriesChartYAxis, TimeSeriesChartYAxisId, TimeSeriesChartYAxisSettings, - updateDarkMode, updateYAxisIntervals + updateDarkMode } from '@home/components/widget/lib/chart/time-series-chart.models'; import { ResizeObserver } from '@juggle/resize-observer'; import { @@ -611,11 +612,6 @@ export class TbTimeSeriesChart { this.timeSeriesChartOptions.yAxis = this.yAxisList.map(axis => axis.option); this.timeSeriesChart.setOption(this.timeSeriesChartOptions, {replaceMerge: ['yAxis', 'xAxis', 'grid'], lazyUpdate: true}); } - changed = this.updateYAxesIntervals(this.yAxisList); - if (changed) { - this.timeSeriesChartOptions.yAxis = this.yAxisList.map(axis => axis.option); - this.timeSeriesChart.setOption(this.timeSeriesChartOptions, {replaceMerge: ['yAxis'], lazyUpdate: true}); - } if (this.yAxisList.length) { const extent = getAxisExtent(this.timeSeriesChart, this.yAxisList[0].id); const min = extent[0]; @@ -680,9 +676,6 @@ export class TbTimeSeriesChart { yAxis.option.scale = scaleYAxis; changed = true; } - if (updateYAxisIntervals(this.timeSeriesChart, yAxis, this.yAxisEmpty(yAxis))) { - changed = true; - } } return changed; } @@ -693,22 +686,6 @@ export class TbTimeSeriesChart { return !axisBarDataItems.length; } - private yAxisEmpty(yAxis: TimeSeriesChartYAxis): boolean { - const axisDataItems = this.dataItems.filter(d => d.yAxisId === yAxis.id && d.enabled && - d.data.length); - return !axisDataItems.length; - } - - private updateYAxesIntervals(axisList: TimeSeriesChartYAxis[]): boolean { - let changed = false; - for (const yAxis of axisList) { - if (updateYAxisIntervals(this.timeSeriesChart, yAxis, this.yAxisEmpty(yAxis))) { - changed = true; - } - } - return changed; - } - private minTopOffset(): number { const showTickLabels = !!this.yAxisList.find(yAxis => yAxis.settings.show && yAxis.settings.showTickLabels);