@gravity-ui/charts 1.43.1 → 1.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/ChartInner/utils/zoom.js +3 -1
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/index.js +31 -6
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/utils.js +4 -5
- package/dist/cjs/core/constants/chart-types.d.ts +1 -0
- package/dist/cjs/core/constants/chart-types.js +1 -0
- package/dist/cjs/core/constants/defaults/series-options.d.ts +5 -1
- package/dist/cjs/core/constants/defaults/series-options.js +13 -0
- package/dist/cjs/core/i18n/keysets/en.json +2 -1
- package/dist/cjs/core/i18n/keysets/ru.json +2 -1
- package/dist/cjs/core/series/prepare-legend.js +2 -2
- package/dist/cjs/core/series/prepare-x-range.d.ts +11 -0
- package/dist/cjs/core/series/prepare-x-range.js +41 -0
- package/dist/cjs/core/series/prepareSeries.js +9 -0
- package/dist/cjs/core/series/types.d.ts +18 -2
- package/dist/cjs/core/types/chart/area.d.ts +2 -1
- package/dist/cjs/core/types/chart/series.d.ts +29 -2
- package/dist/cjs/core/types/chart/tooltip.d.ts +6 -1
- package/dist/cjs/core/types/chart/x-range.d.ts +59 -0
- package/dist/cjs/core/types/chart/x-range.js +1 -0
- package/dist/cjs/core/types/chart/zoom.d.ts +1 -1
- package/dist/cjs/core/types/index.d.ts +1 -0
- package/dist/cjs/core/types/index.js +1 -0
- package/dist/cjs/core/utils/axis/x-axis.js +9 -1
- package/dist/cjs/core/utils/color.js +6 -0
- package/dist/cjs/core/utils/common.js +10 -0
- package/dist/cjs/core/utils/get-closest-data.js +19 -0
- package/dist/cjs/core/validation/index.js +13 -0
- package/dist/cjs/core/zoom/zoom.js +24 -7
- package/dist/cjs/hooks/useShapes/area/prepare-data.js +15 -14
- package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +22 -9
- package/dist/cjs/hooks/useShapes/index.d.ts +2 -1
- package/dist/cjs/hooks/useShapes/index.js +17 -0
- package/dist/cjs/hooks/useShapes/x-range/index.d.ts +14 -0
- package/dist/cjs/hooks/useShapes/x-range/index.js +115 -0
- package/dist/cjs/hooks/useShapes/x-range/prepare-data.d.ts +15 -0
- package/dist/cjs/hooks/useShapes/x-range/prepare-data.js +147 -0
- package/dist/cjs/hooks/useShapes/x-range/types.d.ts +12 -0
- package/dist/cjs/hooks/useShapes/x-range/types.js +1 -0
- package/dist/esm/components/ChartInner/utils/zoom.js +3 -1
- package/dist/esm/components/Tooltip/DefaultTooltipContent/index.js +31 -6
- package/dist/esm/components/Tooltip/DefaultTooltipContent/utils.js +4 -5
- package/dist/esm/core/constants/chart-types.d.ts +1 -0
- package/dist/esm/core/constants/chart-types.js +1 -0
- package/dist/esm/core/constants/defaults/series-options.d.ts +5 -1
- package/dist/esm/core/constants/defaults/series-options.js +13 -0
- package/dist/esm/core/i18n/keysets/en.json +2 -1
- package/dist/esm/core/i18n/keysets/ru.json +2 -1
- package/dist/esm/core/series/prepare-legend.js +2 -2
- package/dist/esm/core/series/prepare-x-range.d.ts +11 -0
- package/dist/esm/core/series/prepare-x-range.js +41 -0
- package/dist/esm/core/series/prepareSeries.js +9 -0
- package/dist/esm/core/series/types.d.ts +18 -2
- package/dist/esm/core/types/chart/area.d.ts +2 -1
- package/dist/esm/core/types/chart/series.d.ts +29 -2
- package/dist/esm/core/types/chart/tooltip.d.ts +6 -1
- package/dist/esm/core/types/chart/x-range.d.ts +59 -0
- package/dist/esm/core/types/chart/x-range.js +1 -0
- package/dist/esm/core/types/chart/zoom.d.ts +1 -1
- package/dist/esm/core/types/index.d.ts +1 -0
- package/dist/esm/core/types/index.js +1 -0
- package/dist/esm/core/utils/axis/x-axis.js +9 -1
- package/dist/esm/core/utils/color.js +6 -0
- package/dist/esm/core/utils/common.js +10 -0
- package/dist/esm/core/utils/get-closest-data.js +19 -0
- package/dist/esm/core/validation/index.js +13 -0
- package/dist/esm/core/zoom/zoom.js +24 -7
- package/dist/esm/hooks/useShapes/area/prepare-data.js +15 -14
- package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +22 -9
- package/dist/esm/hooks/useShapes/index.d.ts +2 -1
- package/dist/esm/hooks/useShapes/index.js +17 -0
- package/dist/esm/hooks/useShapes/x-range/index.d.ts +14 -0
- package/dist/esm/hooks/useShapes/x-range/index.js +115 -0
- package/dist/esm/hooks/useShapes/x-range/prepare-data.d.ts +15 -0
- package/dist/esm/hooks/useShapes/x-range/prepare-data.js +147 -0
- package/dist/esm/hooks/useShapes/x-range/types.d.ts +12 -0
- package/dist/esm/hooks/useShapes/x-range/types.js +1 -0
- package/package.json +1 -1
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"label_invalid-axis-labels-html-not-supported-axis-type": "It seems you are trying to use \"labels.html\" property for an axis with an unsupported type. This property is supported only for \"category\" axis.",
|
|
22
22
|
"label_duplicate-axis-categories": "It seems you have duplicate value \"{{duplicate}}\" found in {{key}}[{{axisIndex}}].",
|
|
23
23
|
"label_invalid-axis-categories": "It seems you are trying to use inappropriate value for \"categories\", or defined it incorrectly. Categories must be a non-empty array for an axis with \"category\" type.",
|
|
24
|
-
"label_inconsistent-y-axis-configuration": "It seems you have inconsistent Y-axis configuration. Possible reasons:\n1. Multiple Y axes with the same position and plot index.\n2. At the moment, 'category' axis is not supported in dual Y-axis configurations."
|
|
24
|
+
"label_inconsistent-y-axis-configuration": "It seems you have inconsistent Y-axis configuration. Possible reasons:\n1. Multiple Y axes with the same position and plot index.\n2. At the moment, 'category' axis is not supported in dual Y-axis configurations.",
|
|
25
|
+
"label_stacking-area-connect-null-mode": "It seems you are using \"nullMode: 'connect'\" with a stacking area series. The \"connect\" null mode is not supported in stacking mode. Use \"zero\" or \"skip\" instead."
|
|
25
26
|
},
|
|
26
27
|
"tooltip": {
|
|
27
28
|
"label_totals_sum": "Sum",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"label_invalid-axis-labels-html-not-supported-axis-type": "Похоже, что вы пытаетесь использовать свойство \"labels.html\" для оси с неподдерживаемым типом. Это свойство поддерживается только для оси типа \"category\".",
|
|
22
22
|
"label_duplicate-axis-categories": "Похоже, что у вас есть дублирующееся значение категории \"{{duplicate}}\" в оси {{key}}[{{axisIndex}}].",
|
|
23
23
|
"label_invalid-axis-categories": "Похоже, что вы пытаетесь использовать недопустимое значение для \"categories\", или указали его неверно. Категории для оси типа \"category\" должны быть непустым массивом.",
|
|
24
|
-
"label_inconsistent-y-axis-configuration": "Похоже, что конфигурация осей Y неконсистентна. Возможные причины:\n1. Несколько осей Y имеют одинаковые значения position и plotIndex.\n2. На данный момент категорийные оси не поддерживаются в конфигурациях с двумя осями Y."
|
|
24
|
+
"label_inconsistent-y-axis-configuration": "Похоже, что конфигурация осей Y неконсистентна. Возможные причины:\n1. Несколько осей Y имеют одинаковые значения position и plotIndex.\n2. На данный момент категорийные оси не поддерживаются в конфигурациях с двумя осями Y.",
|
|
25
|
+
"label_stacking-area-connect-null-mode": "Похоже, что вы используете \"nullMode: 'connect'\" с серией area с накоплением. Режим null \"connect\" не поддерживается в режиме накопления. Используйте \"zero\" или \"skip\"."
|
|
25
26
|
},
|
|
26
27
|
"tooltip": {
|
|
27
28
|
"label_totals_sum": "Сумма",
|
|
@@ -136,7 +136,7 @@ async function getGroupedLegendItems(args) {
|
|
|
136
136
|
else {
|
|
137
137
|
resultItem.textWidth = textWidth;
|
|
138
138
|
}
|
|
139
|
-
textWidthsInLine.push(textWidth);
|
|
139
|
+
textWidthsInLine.push(resultItem.textWidth);
|
|
140
140
|
const textsWidth = textWidthsInLine.reduce((acc, width) => acc + width, 0);
|
|
141
141
|
if (!result[lineIndex]) {
|
|
142
142
|
result[lineIndex] = [];
|
|
@@ -155,7 +155,7 @@ async function getGroupedLegendItems(args) {
|
|
|
155
155
|
else if (isCurrentLineOverMaxWidth) {
|
|
156
156
|
result[lineIndex].pop();
|
|
157
157
|
lineIndex += 1;
|
|
158
|
-
textWidthsInLine = [textWidth];
|
|
158
|
+
textWidthsInLine = [resultItem.textWidth];
|
|
159
159
|
const nextLineIndex = lineIndex;
|
|
160
160
|
result[nextLineIndex] = [];
|
|
161
161
|
result[nextLineIndex].push(resultItem);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ScaleOrdinal } from 'd3-scale';
|
|
2
|
+
import type { ChartSeriesOptions, XRangeSeries } from '../../types';
|
|
3
|
+
import type { PreparedLegend, PreparedXRangeSeries } from './types';
|
|
4
|
+
type PrepareXRangeSeriesArgs = {
|
|
5
|
+
colorScale: ScaleOrdinal<string, string>;
|
|
6
|
+
series: XRangeSeries[];
|
|
7
|
+
legend: PreparedLegend;
|
|
8
|
+
seriesOptions?: ChartSeriesOptions;
|
|
9
|
+
};
|
|
10
|
+
export declare function prepareXRangeSeries(args: PrepareXRangeSeriesArgs): PreparedXRangeSeries[];
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import get from 'lodash/get';
|
|
2
|
+
import { getUniqId } from '../utils';
|
|
3
|
+
import { DASH_STYLE, DEFAULT_DATALABELS_STYLE } from '../constants';
|
|
4
|
+
import { DEFAULT_DATALABELS_PADDING } from './constants';
|
|
5
|
+
import { prepareLegendSymbol } from './utils';
|
|
6
|
+
export function prepareXRangeSeries(args) {
|
|
7
|
+
const { colorScale, series: seriesList, seriesOptions, legend } = args;
|
|
8
|
+
return seriesList.map((series) => {
|
|
9
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
|
|
10
|
+
const name = series.name || '';
|
|
11
|
+
const color = series.color || colorScale(name);
|
|
12
|
+
return {
|
|
13
|
+
type: series.type,
|
|
14
|
+
color,
|
|
15
|
+
name,
|
|
16
|
+
id: getUniqId(),
|
|
17
|
+
visible: get(series, 'visible', true),
|
|
18
|
+
legend: {
|
|
19
|
+
enabled: get(series, 'legend.enabled', legend.enabled),
|
|
20
|
+
symbol: prepareLegendSymbol(series),
|
|
21
|
+
groupId: (_b = (_a = series.legend) === null || _a === void 0 ? void 0 : _a.groupId) !== null && _b !== void 0 ? _b : getUniqId(),
|
|
22
|
+
itemText: (_d = (_c = series.legend) === null || _c === void 0 ? void 0 : _c.itemText) !== null && _d !== void 0 ? _d : name,
|
|
23
|
+
},
|
|
24
|
+
data: series.data,
|
|
25
|
+
opacity: (_e = series.opacity) !== null && _e !== void 0 ? _e : null,
|
|
26
|
+
borderRadius: (_h = (_f = series.borderRadius) !== null && _f !== void 0 ? _f : (_g = seriesOptions === null || seriesOptions === void 0 ? void 0 : seriesOptions['x-range']) === null || _g === void 0 ? void 0 : _g.borderRadius) !== null && _h !== void 0 ? _h : 0,
|
|
27
|
+
borderWidth: (_l = (_j = series.borderWidth) !== null && _j !== void 0 ? _j : (_k = seriesOptions === null || seriesOptions === void 0 ? void 0 : seriesOptions['x-range']) === null || _k === void 0 ? void 0 : _k.borderWidth) !== null && _l !== void 0 ? _l : 0,
|
|
28
|
+
borderColor: (_p = (_m = series.borderColor) !== null && _m !== void 0 ? _m : (_o = seriesOptions === null || seriesOptions === void 0 ? void 0 : seriesOptions['x-range']) === null || _o === void 0 ? void 0 : _o.borderColor) !== null && _p !== void 0 ? _p : 'var(--gcharts-shape-border-color)',
|
|
29
|
+
borderDashStyle: (_s = (_q = series.borderDashStyle) !== null && _q !== void 0 ? _q : (_r = seriesOptions === null || seriesOptions === void 0 ? void 0 : seriesOptions['x-range']) === null || _r === void 0 ? void 0 : _r.borderDashStyle) !== null && _s !== void 0 ? _s : DASH_STYLE.Solid,
|
|
30
|
+
cursor: get(series, 'cursor', null),
|
|
31
|
+
tooltip: series.tooltip,
|
|
32
|
+
dataLabels: {
|
|
33
|
+
enabled: get(series, 'dataLabels.enabled', false),
|
|
34
|
+
style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_t = series.dataLabels) === null || _t === void 0 ? void 0 : _t.style),
|
|
35
|
+
html: get(series, 'dataLabels.html', false),
|
|
36
|
+
padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING),
|
|
37
|
+
format: (_u = series.dataLabels) === null || _u === void 0 ? void 0 : _u.format,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -11,6 +11,7 @@ import { prepareSankeySeries } from './prepare-sankey';
|
|
|
11
11
|
import { prepareScatterSeries } from './prepare-scatter';
|
|
12
12
|
import { prepareTreemap } from './prepare-treemap';
|
|
13
13
|
import { prepareWaterfallSeries } from './prepare-waterfall';
|
|
14
|
+
import { prepareXRangeSeries } from './prepare-x-range';
|
|
14
15
|
export async function prepareSeries(args) {
|
|
15
16
|
const { type, series, seriesOptions, legend, colors, colorScale } = args;
|
|
16
17
|
switch (type) {
|
|
@@ -108,6 +109,14 @@ export async function prepareSeries(args) {
|
|
|
108
109
|
colors,
|
|
109
110
|
});
|
|
110
111
|
}
|
|
112
|
+
case 'x-range': {
|
|
113
|
+
return prepareXRangeSeries({
|
|
114
|
+
series: series,
|
|
115
|
+
seriesOptions,
|
|
116
|
+
legend,
|
|
117
|
+
colorScale,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
111
120
|
default: {
|
|
112
121
|
throw new ChartError({
|
|
113
122
|
message: `Series type "${type}" does not support data preparation for series that do not support the presence of axes`,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AreaSeries, AreaSeriesData, BarXSeries, BarXSeriesData, BarYSeries, BarYSeriesData, BaseTextStyle, ChartLegend, ChartSeries, ChartSeriesRangeSliderOptions, ConnectorCurve, ConnectorShape, FunnelSeries, FunnelSeriesData, HeatmapSeries, HeatmapSeriesData, LineSeries, LineSeriesData, LineSeriesLineBaseStyle, PathLegendSymbolOptions, PieSeries, PieSeriesData, RadarSeries, RadarSeriesCategory, RadarSeriesData, RectLegendSymbolOptions, SankeySeries, SankeySeriesData, ScatterSeries, ScatterSeriesData, SymbolLegendSymbolOptions, TreemapSeries, TreemapSeriesData, ValueFormat, WaterfallSeries, WaterfallSeriesData } from '../../types';
|
|
1
|
+
import type { AreaSeries, AreaSeriesData, BarXSeries, BarXSeriesData, BarYSeries, BarYSeriesData, BaseTextStyle, ChartLegend, ChartSeries, ChartSeriesRangeSliderOptions, ConnectorCurve, ConnectorShape, FunnelSeries, FunnelSeriesData, HeatmapSeries, HeatmapSeriesData, LineSeries, LineSeriesData, LineSeriesLineBaseStyle, PathLegendSymbolOptions, PieSeries, PieSeriesData, RadarSeries, RadarSeriesCategory, RadarSeriesData, RectLegendSymbolOptions, SankeySeries, SankeySeriesData, ScatterSeries, ScatterSeriesData, SymbolLegendSymbolOptions, TreemapSeries, TreemapSeriesData, ValueFormat, WaterfallSeries, WaterfallSeriesData, XRangeSeries, XRangeSeriesData } from '../../types';
|
|
2
2
|
import type { DashStyle, LayoutAlgorithm, LineCap, LineJoin, SeriesOptionsDefaults, SymbolType } from '../constants';
|
|
3
3
|
export type RectLegendSymbol = {
|
|
4
4
|
shape: 'rect';
|
|
@@ -339,7 +339,23 @@ export type PreparedFunnelSeries = {
|
|
|
339
339
|
};
|
|
340
340
|
connectors: Required<FunnelSeries['connectors']>;
|
|
341
341
|
} & BasePreparedSeries;
|
|
342
|
-
export type
|
|
342
|
+
export type PreparedXRangeSeries = {
|
|
343
|
+
type: XRangeSeries['type'];
|
|
344
|
+
data: XRangeSeriesData[];
|
|
345
|
+
borderRadius: number;
|
|
346
|
+
borderWidth: number;
|
|
347
|
+
borderColor: string;
|
|
348
|
+
borderDashStyle: DashStyle;
|
|
349
|
+
opacity: number | null;
|
|
350
|
+
dataLabels: {
|
|
351
|
+
enabled: boolean;
|
|
352
|
+
style: BaseTextStyle;
|
|
353
|
+
html: boolean;
|
|
354
|
+
padding: number;
|
|
355
|
+
format?: ValueFormat;
|
|
356
|
+
};
|
|
357
|
+
} & BasePreparedSeries;
|
|
358
|
+
export type PreparedSeries = PreparedScatterSeries | PreparedBarXSeries | PreparedBarYSeries | PreparedPieSeries | PreparedLineSeries | PreparedAreaSeries | PreparedTreemapSeries | PreparedWaterfallSeries | PreparedSankeySeries | PreparedRadarSeries | PreparedHeatmapSeries | PreparedFunnelSeries | PreparedXRangeSeries;
|
|
343
359
|
export type PreparedZoomableSeries = Extract<PreparedSeries, {
|
|
344
360
|
data: Array<unknown>;
|
|
345
361
|
}>;
|
|
@@ -76,9 +76,10 @@ export interface AreaSeries<T = MeaningfulAny> extends BaseSeries {
|
|
|
76
76
|
/**
|
|
77
77
|
* Specifies how null or undefined values should be handled in the series.
|
|
78
78
|
*
|
|
79
|
-
* - `'connect'`: Connect points across null values (skip nulls in rendering)
|
|
79
|
+
* - `'connect'`: Connect points across null values (skip nulls in rendering). **Not supported with `stacking`.**
|
|
80
80
|
* - `'zero'`: Treat null values as zero
|
|
81
81
|
* - `'skip'`: Omit the data point (creates gap in area)
|
|
82
|
+
*
|
|
82
83
|
* @default 'skip'
|
|
83
84
|
*/
|
|
84
85
|
nullMode?: 'connect' | 'zero' | 'skip';
|
|
@@ -15,8 +15,9 @@ import type { SankeySeries, SankeySeriesData } from './sankey';
|
|
|
15
15
|
import type { ScatterSeries, ScatterSeriesData } from './scatter';
|
|
16
16
|
import type { TreemapSeries, TreemapSeriesData } from './treemap';
|
|
17
17
|
import type { WaterfallSeries, WaterfallSeriesData } from './waterfall';
|
|
18
|
-
|
|
19
|
-
export type
|
|
18
|
+
import type { XRangeSeries, XRangeSeriesData } from './x-range';
|
|
19
|
+
export type ChartSeries<T = MeaningfulAny> = ScatterSeries<T> | PieSeries<T> | BarXSeries<T> | BarYSeries<T> | LineSeries<T> | AreaSeries<T> | TreemapSeries<T> | WaterfallSeries<T> | SankeySeries<T> | RadarSeries<T> | HeatmapSeries<T> | FunnelSeries<T> | XRangeSeries<T>;
|
|
20
|
+
export type ChartSeriesData<T = MeaningfulAny> = ScatterSeriesData<T> | PieSeriesData<T> | BarXSeriesData<T> | BarYSeriesData<T> | LineSeriesData<T> | AreaSeriesData<T> | TreemapSeriesData<T> | WaterfallSeriesData<T> | SankeySeriesData<T> | RadarSeriesData<T> | HeatmapSeriesData<T> | FunnelSeriesData<T> | XRangeSeriesData<T>;
|
|
20
21
|
export interface DataLabelRendererData<T = MeaningfulAny> {
|
|
21
22
|
data: ChartSeriesData<T>;
|
|
22
23
|
}
|
|
@@ -291,6 +292,32 @@ export interface ChartSeriesOptions {
|
|
|
291
292
|
hover?: BasicHoverState;
|
|
292
293
|
};
|
|
293
294
|
};
|
|
295
|
+
'x-range'?: {
|
|
296
|
+
/**
|
|
297
|
+
* The corner radius of the border surrounding each bar.
|
|
298
|
+
* @default 0
|
|
299
|
+
*/
|
|
300
|
+
borderRadius?: number;
|
|
301
|
+
/**
|
|
302
|
+
* The width of the border surrounding each bar.
|
|
303
|
+
* @default 0
|
|
304
|
+
*/
|
|
305
|
+
borderWidth?: number;
|
|
306
|
+
/**
|
|
307
|
+
* The color of the border surrounding each bar.
|
|
308
|
+
*/
|
|
309
|
+
borderColor?: string;
|
|
310
|
+
/**
|
|
311
|
+
* The dash style of the border surrounding each bar.
|
|
312
|
+
* @default 'Solid'
|
|
313
|
+
*/
|
|
314
|
+
borderDashStyle?: DashStyle;
|
|
315
|
+
/** Options for the series states that provide additional styling information to the series. */
|
|
316
|
+
states?: {
|
|
317
|
+
hover?: BasicHoverState;
|
|
318
|
+
inactive?: BasicInactiveState;
|
|
319
|
+
};
|
|
320
|
+
};
|
|
294
321
|
}
|
|
295
322
|
export type ChartSeriesRangeSliderOptions = {
|
|
296
323
|
/**
|
|
@@ -15,6 +15,7 @@ import type { SankeySeries, SankeySeriesData } from './sankey';
|
|
|
15
15
|
import type { ScatterSeries, ScatterSeriesData } from './scatter';
|
|
16
16
|
import type { TreemapSeries, TreemapSeriesData } from './treemap';
|
|
17
17
|
import type { WaterfallSeries, WaterfallSeriesData } from './waterfall';
|
|
18
|
+
import type { XRangeSeries, XRangeSeriesData } from './x-range';
|
|
18
19
|
export interface TooltipDataChunkBarX<T = MeaningfulAny> {
|
|
19
20
|
data: BarXSeriesData<T>;
|
|
20
21
|
series: BarXSeries<T>;
|
|
@@ -87,7 +88,11 @@ export interface TooltipDataChunkFunnel<T = MeaningfulAny> {
|
|
|
87
88
|
name: string;
|
|
88
89
|
};
|
|
89
90
|
}
|
|
90
|
-
export
|
|
91
|
+
export interface TooltipDataChunkXRange<T = MeaningfulAny> {
|
|
92
|
+
data: XRangeSeriesData<T>;
|
|
93
|
+
series: XRangeSeries<T>;
|
|
94
|
+
}
|
|
95
|
+
export type TooltipDataChunk<T = MeaningfulAny> = (TooltipDataChunkBarX<T> | TooltipDataChunkBarY<T> | TooltipDataChunkPie<T> | TooltipDataChunkScatter<T> | TooltipDataChunkLine<T> | TooltipDataChunkArea<T> | TooltipDataChunkTreemap<T> | TooltipDataChunkSankey<T> | TooltipDataChunkWaterfall<T> | TooltipDataChunkRadar<T> | TooltipDataChunkHeatmap<T> | TooltipDataChunkFunnel<T> | TooltipDataChunkXRange<T>) & {
|
|
91
96
|
closest?: boolean;
|
|
92
97
|
};
|
|
93
98
|
export interface ChartTooltipRendererArgs<T = MeaningfulAny> {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { DashStyle, SERIES_TYPE } from '../../constants';
|
|
2
|
+
import type { MeaningfulAny } from '../misc';
|
|
3
|
+
import type { BaseSeries, BaseSeriesData, BaseSeriesLegend } from './base';
|
|
4
|
+
import type { RectLegendSymbolOptions } from './legend';
|
|
5
|
+
export interface XRangeSeriesData<T = MeaningfulAny> extends BaseSeriesData<T> {
|
|
6
|
+
/**
|
|
7
|
+
* The start value of the bar on the x-axis. Can be a numeric or timestamp value.
|
|
8
|
+
*/
|
|
9
|
+
x0: number | string;
|
|
10
|
+
/**
|
|
11
|
+
* The end value of the bar on the x-axis. Can be a numeric or timestamp value.
|
|
12
|
+
*/
|
|
13
|
+
x1: number | string;
|
|
14
|
+
/**
|
|
15
|
+
* The `y` value. Depending on the context it may represent:
|
|
16
|
+
* - a category value (for `category` y axis). If string, it is the category itself. If number, it is the index in `yAxis[0].categories`.
|
|
17
|
+
* - a numeric value (for `linear` y axis)
|
|
18
|
+
*/
|
|
19
|
+
y?: string | number;
|
|
20
|
+
/** Individual color for the bar. Overrides the series color. */
|
|
21
|
+
color?: string;
|
|
22
|
+
/** Data label value. If not specified, the series name is used. */
|
|
23
|
+
label?: string | number;
|
|
24
|
+
/** Individual opacity for the bar. */
|
|
25
|
+
opacity?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface XRangeSeries<T = MeaningfulAny> extends BaseSeries {
|
|
28
|
+
type: typeof SERIES_TYPE.XRange;
|
|
29
|
+
data: XRangeSeriesData<T>[];
|
|
30
|
+
/** The name of the series (used in legend, tooltip etc) */
|
|
31
|
+
name: string;
|
|
32
|
+
/** The main color of the series (hex, rgba) */
|
|
33
|
+
color?: string;
|
|
34
|
+
/** Individual opacity for the entire series. */
|
|
35
|
+
opacity?: number | null;
|
|
36
|
+
/**
|
|
37
|
+
* The corner radius of the border surrounding each bar.
|
|
38
|
+
* @default 0
|
|
39
|
+
*/
|
|
40
|
+
borderRadius?: number;
|
|
41
|
+
/**
|
|
42
|
+
* The width of the border surrounding each bar.
|
|
43
|
+
* @default 0
|
|
44
|
+
*/
|
|
45
|
+
borderWidth?: number;
|
|
46
|
+
/**
|
|
47
|
+
* The color of the border surrounding each bar.
|
|
48
|
+
*/
|
|
49
|
+
borderColor?: string;
|
|
50
|
+
/**
|
|
51
|
+
* The dash style of the border surrounding each bar.
|
|
52
|
+
* @default 'Solid'
|
|
53
|
+
*/
|
|
54
|
+
borderDashStyle?: DashStyle;
|
|
55
|
+
/** Individual series legend options. Has higher priority than legend options in widget data */
|
|
56
|
+
legend?: BaseSeriesLegend & {
|
|
57
|
+
symbol?: RectLegendSymbolOptions;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -30,6 +30,7 @@ export * from './chart/radar';
|
|
|
30
30
|
export * from './chart/heatmap';
|
|
31
31
|
export * from './chart/funnel';
|
|
32
32
|
export * from './chart/brush';
|
|
33
|
+
export * from './chart/x-range';
|
|
33
34
|
export interface ChartData<T = MeaningfulAny> {
|
|
34
35
|
/**
|
|
35
36
|
* General options for the chart.
|
|
@@ -12,7 +12,15 @@ function getTicksCount(args) {
|
|
|
12
12
|
if (series) {
|
|
13
13
|
const xDataSet = new Set();
|
|
14
14
|
series === null || series === void 0 ? void 0 : series.forEach((item) => {
|
|
15
|
-
if (
|
|
15
|
+
if (item.type === 'x-range') {
|
|
16
|
+
item.data.forEach((d) => {
|
|
17
|
+
if (d.x0 !== null && d.x1 !== null) {
|
|
18
|
+
xDataSet.add(d.x0);
|
|
19
|
+
xDataSet.add(d.x1);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
else if (isSeriesWithNumericalXValues(item)) {
|
|
16
24
|
item.data.forEach((data) => {
|
|
17
25
|
xDataSet.add(data.x);
|
|
18
26
|
});
|
|
@@ -22,6 +22,12 @@ export function getDomainForContinuousColorScale(args) {
|
|
|
22
22
|
acc.push(...s.data.map((d) => Number(d.y)));
|
|
23
23
|
break;
|
|
24
24
|
}
|
|
25
|
+
case 'x-range': {
|
|
26
|
+
// Use bar duration (x1 - x0) as the color domain value so that
|
|
27
|
+
// longer bars can be visually distinguished by color intensity.
|
|
28
|
+
acc.push(...s.data.map((d) => Math.abs(Number(d.x1) - Number(d.x0))));
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
25
31
|
default: {
|
|
26
32
|
throw Error(`The method for calculation a domain for a continuous color scale for the "${s.type}" series is not defined`);
|
|
27
33
|
}
|
|
@@ -57,6 +57,16 @@ export const getDomainDataXBySeries = (series) => {
|
|
|
57
57
|
acc.push(...getDomainDataForStackedSeries(seriesList, 'y', 'x'));
|
|
58
58
|
break;
|
|
59
59
|
}
|
|
60
|
+
case 'x-range': {
|
|
61
|
+
seriesList.forEach((s) => {
|
|
62
|
+
s.data.forEach((d) => {
|
|
63
|
+
if (!isNil(d.x0) && !isNil(d.x1)) {
|
|
64
|
+
acc.push(d.x0, d.x1);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
60
70
|
default: {
|
|
61
71
|
seriesList.filter(isSeriesWithNumericalXValues).forEach((s) => {
|
|
62
72
|
acc.push(...s.data.map((d) => d.x));
|
|
@@ -288,6 +288,25 @@ export function getClosestPoints(args) {
|
|
|
288
288
|
}
|
|
289
289
|
break;
|
|
290
290
|
}
|
|
291
|
+
case 'x-range': {
|
|
292
|
+
const data = list;
|
|
293
|
+
const pointsInXRange = data.filter((d) => pointerX >= d.x &&
|
|
294
|
+
pointerX <= d.x + d.width &&
|
|
295
|
+
pointerY >= d.y &&
|
|
296
|
+
pointerY <= d.y + d.height);
|
|
297
|
+
if (pointsInXRange.length === 0) {
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
const closestByX = pointsInXRange.length === 1
|
|
301
|
+
? pointsInXRange[0]
|
|
302
|
+
: sort(pointsInXRange, (d) => Math.abs(d.x + d.width / 2 - pointerX))[0];
|
|
303
|
+
result.push(...pointsInXRange.map((d) => ({
|
|
304
|
+
data: d.data,
|
|
305
|
+
series: d.series,
|
|
306
|
+
closest: d === closestByX,
|
|
307
|
+
})));
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
291
310
|
}
|
|
292
311
|
});
|
|
293
312
|
if (closestPointsByXValue.length) {
|
|
@@ -265,6 +265,18 @@ function validateStacking({ series }) {
|
|
|
265
265
|
});
|
|
266
266
|
}
|
|
267
267
|
}
|
|
268
|
+
function validateStackingAreaNullMode({ series }) {
|
|
269
|
+
const availableStackingValues = ['normal', 'percent'];
|
|
270
|
+
const invalid = series.find((s) => s.type === 'area' &&
|
|
271
|
+
availableStackingValues.includes(s.stacking) &&
|
|
272
|
+
s.nullMode === 'connect');
|
|
273
|
+
if (invalid) {
|
|
274
|
+
throw new ChartError({
|
|
275
|
+
code: CHART_ERROR_CODE.INVALID_DATA,
|
|
276
|
+
message: i18n('error', 'label_stacking-area-connect-null-mode'),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
268
280
|
function validateTreemapSeries({ series }) {
|
|
269
281
|
const parentIds = {};
|
|
270
282
|
series.data.forEach((d) => {
|
|
@@ -379,6 +391,7 @@ export function validateData(data) {
|
|
|
379
391
|
}
|
|
380
392
|
validateAxes({ xAxis: data.xAxis, yAxis: data.yAxis });
|
|
381
393
|
validateTooltip({ tooltip: data.tooltip });
|
|
394
|
+
validateStackingAreaNullMode({ series: data.series.data });
|
|
382
395
|
if (data.series.data.some((s) => isEmpty(s.data))) {
|
|
383
396
|
throw new ChartError({
|
|
384
397
|
code: CHART_ERROR_CODE.INVALID_DATA,
|
|
@@ -69,13 +69,30 @@ export function getZoomedSeriesData(args) {
|
|
|
69
69
|
prevPointInRange = currentPointInRange;
|
|
70
70
|
if (zoomState.x) {
|
|
71
71
|
const [xMin, xMax] = zoomState.x;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
if ('x0' in point && 'x1' in point) {
|
|
73
|
+
const isStartInRange = isValueInRange({
|
|
74
|
+
axis: xAxis,
|
|
75
|
+
value: point.x0,
|
|
76
|
+
min: xMin,
|
|
77
|
+
max: xMax,
|
|
78
|
+
});
|
|
79
|
+
const isEndInRange = isValueInRange({
|
|
80
|
+
axis: xAxis,
|
|
81
|
+
value: point.x1,
|
|
82
|
+
min: xMin,
|
|
83
|
+
max: xMax,
|
|
84
|
+
});
|
|
85
|
+
inXRange = isStartInRange || isEndInRange;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
const x = 'x' in point ? ((_a = point.x) !== null && _a !== void 0 ? _a : undefined) : undefined;
|
|
89
|
+
inXRange = isValueInRange({
|
|
90
|
+
axis: xAxis,
|
|
91
|
+
value: x,
|
|
92
|
+
min: xMin,
|
|
93
|
+
max: xMax,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
79
96
|
}
|
|
80
97
|
if (zoomState.y) {
|
|
81
98
|
const yAxisIndex = 'yAxis' in seriesItem && typeof seriesItem.yAxis === 'number'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { group, min } from 'd3-array';
|
|
1
|
+
import { group, min, sort } from 'd3-array';
|
|
2
2
|
import isNil from 'lodash/isNil';
|
|
3
3
|
import round from 'lodash/round';
|
|
4
4
|
import { getDataCategoryValue, getLabelsSize, getTextSizeFn } from '../../../core/utils';
|
|
@@ -27,7 +27,7 @@ function getXValues(series, xAxis, xScale) {
|
|
|
27
27
|
return acc;
|
|
28
28
|
}, []);
|
|
29
29
|
}
|
|
30
|
-
return Array.from(xValues);
|
|
30
|
+
return sort(Array.from(xValues), (d) => d[1]);
|
|
31
31
|
}
|
|
32
32
|
async function prepareDataLabels({ series, points, xMax, yAxisTop, isOutsideBounds, }) {
|
|
33
33
|
var _a;
|
|
@@ -166,13 +166,14 @@ export const prepareAreaData = async (args) => {
|
|
|
166
166
|
return m.set(key, d);
|
|
167
167
|
}, new Map());
|
|
168
168
|
const points = xValues.reduce((pointsAcc, [x, xValue], index) => {
|
|
169
|
-
var _a, _b, _c, _d, _e, _f, _g
|
|
170
|
-
const
|
|
169
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
170
|
+
const rawData = seriesData.get(x);
|
|
171
|
+
const d = rawData !== null && rawData !== void 0 ? rawData : {
|
|
171
172
|
x,
|
|
172
173
|
y: 0,
|
|
173
174
|
};
|
|
174
|
-
let yDataValue = (
|
|
175
|
-
if (s.nullMode === 'connect' && yDataValue === null) {
|
|
175
|
+
let yDataValue = (_a = d.y) !== null && _a !== void 0 ? _a : null;
|
|
176
|
+
if (s.nullMode === 'connect' && (yDataValue === null || !rawData)) {
|
|
176
177
|
return pointsAcc;
|
|
177
178
|
}
|
|
178
179
|
if (yDataValue && isPercentStacking) {
|
|
@@ -187,13 +188,13 @@ export const prepareAreaData = async (args) => {
|
|
|
187
188
|
});
|
|
188
189
|
if (typeof yDataValue === 'number' && yValue !== null) {
|
|
189
190
|
yValue = round(yValue, 2);
|
|
190
|
-
const prevPoint = seriesData.get((
|
|
191
|
-
const nextPoint = seriesData.get((
|
|
191
|
+
const prevPoint = seriesData.get((_b = xValues[index - 1]) === null || _b === void 0 ? void 0 : _b[0]);
|
|
192
|
+
const nextPoint = seriesData.get((_c = xValues[index + 1]) === null || _c === void 0 ? void 0 : _c[0]);
|
|
192
193
|
const currentPointStackHeight = Math.abs(yMin - yValue);
|
|
193
194
|
if (yDataValue >= 0) {
|
|
194
195
|
const positiveStackHeights = positiveStackValues.get(x);
|
|
195
|
-
let prevSectionStackHeight = (
|
|
196
|
-
let nextSectionStackHeight = (
|
|
196
|
+
let prevSectionStackHeight = (_d = positiveStackHeights === null || positiveStackHeights === void 0 ? void 0 : positiveStackHeights.prev) !== null && _d !== void 0 ? _d : 0;
|
|
197
|
+
let nextSectionStackHeight = (_e = positiveStackHeights === null || positiveStackHeights === void 0 ? void 0 : positiveStackHeights.next) !== null && _e !== void 0 ? _e : 0;
|
|
197
198
|
const point = {
|
|
198
199
|
y0: yAxisTop + yMin - prevSectionStackHeight,
|
|
199
200
|
x: xValue,
|
|
@@ -219,11 +220,11 @@ export const prepareAreaData = async (args) => {
|
|
|
219
220
|
point2.y = newYValue;
|
|
220
221
|
}
|
|
221
222
|
}
|
|
222
|
-
if ((prevPoint === null || prevPoint === void 0 ? void 0 : prevPoint.y) !== null) {
|
|
223
|
+
if ((prevPoint === null || prevPoint === void 0 ? void 0 : prevPoint.y) !== null || s.nullMode === 'zero') {
|
|
223
224
|
prevSectionStackHeight =
|
|
224
225
|
prevSectionStackHeight + currentPointStackHeight;
|
|
225
226
|
}
|
|
226
|
-
if ((nextPoint === null || nextPoint === void 0 ? void 0 : nextPoint.y) !== null) {
|
|
227
|
+
if ((nextPoint === null || nextPoint === void 0 ? void 0 : nextPoint.y) !== null || s.nullMode === 'zero') {
|
|
227
228
|
nextSectionStackHeight =
|
|
228
229
|
nextSectionStackHeight + currentPointStackHeight;
|
|
229
230
|
}
|
|
@@ -234,8 +235,8 @@ export const prepareAreaData = async (args) => {
|
|
|
234
235
|
}
|
|
235
236
|
else {
|
|
236
237
|
const negativeStackHeights = negativeStackValues.get(x);
|
|
237
|
-
let prevSectionStackHeight = (
|
|
238
|
-
let nextSectionStackHeight = (
|
|
238
|
+
let prevSectionStackHeight = (_f = negativeStackHeights === null || negativeStackHeights === void 0 ? void 0 : negativeStackHeights.prev) !== null && _f !== void 0 ? _f : 0;
|
|
239
|
+
let nextSectionStackHeight = (_g = negativeStackHeights === null || negativeStackHeights === void 0 ? void 0 : negativeStackHeights.next) !== null && _g !== void 0 ? _g : 0;
|
|
239
240
|
pointsAcc.push({
|
|
240
241
|
y0: yAxisTop + yMin + prevSectionStackHeight,
|
|
241
242
|
x: xValue,
|
|
@@ -110,8 +110,8 @@ export const prepareBarXData = async (args) => {
|
|
|
110
110
|
const currentGroupWidth = rectWidth * stacks.length + rectGap * (stacks.length - 1);
|
|
111
111
|
for (let groupItemIndex = 0; groupItemIndex < stacks.length; groupItemIndex++) {
|
|
112
112
|
const yValues = stacks[groupItemIndex];
|
|
113
|
-
let
|
|
114
|
-
let
|
|
113
|
+
let positiveStackSum = 0;
|
|
114
|
+
let negativeStackSum = 0;
|
|
115
115
|
const stackItems = [];
|
|
116
116
|
let sortedData = yValues;
|
|
117
117
|
if (sortKey) {
|
|
@@ -144,7 +144,6 @@ export const prepareBarXData = async (args) => {
|
|
|
144
144
|
}
|
|
145
145
|
const x = xCenter - currentGroupWidth / 2 + (rectWidth + rectGap) * groupItemIndex;
|
|
146
146
|
const yDataValue = ((_d = yValue.data.y) !== null && _d !== void 0 ? _d : 0);
|
|
147
|
-
const y = seriesYScale(yDataValue);
|
|
148
147
|
let base = 0;
|
|
149
148
|
if (seriesYAxis.type === 'logarithmic') {
|
|
150
149
|
const domainData = seriesYScale.domain();
|
|
@@ -155,7 +154,22 @@ export const prepareBarXData = async (args) => {
|
|
|
155
154
|
base = seriesYScale(0);
|
|
156
155
|
}
|
|
157
156
|
const isLastStackItem = yValueIndex === sortedData.length - 1;
|
|
158
|
-
|
|
157
|
+
let height;
|
|
158
|
+
let barPositionY;
|
|
159
|
+
if (yDataValue > 0) {
|
|
160
|
+
const newSum = positiveStackSum + yDataValue;
|
|
161
|
+
const topPixel = seriesYScale(newSum);
|
|
162
|
+
const bottomPixel = positiveStackSum === 0 ? base : seriesYScale(positiveStackSum);
|
|
163
|
+
height = Math.abs(bottomPixel - topPixel);
|
|
164
|
+
barPositionY = yAxisTop + topPixel;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const newSum = negativeStackSum + yDataValue;
|
|
168
|
+
const bottomPixel = negativeStackSum === 0 ? base : seriesYScale(negativeStackSum);
|
|
169
|
+
const topPixel = seriesYScale(newSum);
|
|
170
|
+
height = Math.abs(bottomPixel - topPixel);
|
|
171
|
+
barPositionY = yAxisTop + bottomPixel;
|
|
172
|
+
}
|
|
159
173
|
let shapeHeight = height - (stackItems.length ? stackGap : 0);
|
|
160
174
|
if (shapeHeight < 0) {
|
|
161
175
|
shapeHeight = height;
|
|
@@ -165,9 +179,7 @@ export const prepareBarXData = async (args) => {
|
|
|
165
179
|
}
|
|
166
180
|
const barData = {
|
|
167
181
|
x,
|
|
168
|
-
y:
|
|
169
|
-
? yAxisTop + y - positiveStackHeight
|
|
170
|
-
: yAxisTop + base + negativeStackHeight,
|
|
182
|
+
y: barPositionY,
|
|
171
183
|
width: rectWidth,
|
|
172
184
|
height: shapeHeight,
|
|
173
185
|
_height: height,
|
|
@@ -180,14 +192,15 @@ export const prepareBarXData = async (args) => {
|
|
|
180
192
|
};
|
|
181
193
|
stackItems.push(barData);
|
|
182
194
|
if (yDataValue > 0) {
|
|
183
|
-
|
|
195
|
+
positiveStackSum += yDataValue;
|
|
184
196
|
}
|
|
185
197
|
else {
|
|
186
|
-
|
|
198
|
+
negativeStackSum += yDataValue;
|
|
187
199
|
}
|
|
188
200
|
}
|
|
189
201
|
if (series.some((s) => s.stacking === 'percent')) {
|
|
190
202
|
let acc = 0;
|
|
203
|
+
const positiveStackHeight = stackItems.reduce((sum, item) => sum + item._height, 0);
|
|
191
204
|
const ratio = plotHeight / positiveStackHeight;
|
|
192
205
|
stackItems.forEach((item) => {
|
|
193
206
|
item.height = item._height * ratio;
|
|
@@ -19,8 +19,9 @@ import type { PreparedScatterData } from './scatter/types';
|
|
|
19
19
|
export type { PreparedBarXData } from './bar-x';
|
|
20
20
|
export type { PreparedScatterData } from './scatter/types';
|
|
21
21
|
import type { PreparedWaterfallData } from './waterfall';
|
|
22
|
+
import type { PreparedXRangeData } from './x-range';
|
|
22
23
|
import './styles.css';
|
|
23
|
-
export type ShapeData = PreparedBarXData | PreparedBarYData | PreparedScatterData | PreparedLineData | PreparedPieData | PreparedAreaData | PreparedWaterfallData | PreparedSankeyData | PreparedRadarData | PreparedHeatmapData | PreparedFunnelData;
|
|
24
|
+
export type ShapeData = PreparedBarXData | PreparedBarYData | PreparedScatterData | PreparedLineData | PreparedPieData | PreparedAreaData | PreparedWaterfallData | PreparedSankeyData | PreparedRadarData | PreparedHeatmapData | PreparedFunnelData | PreparedXRangeData;
|
|
24
25
|
export type ClipPathBySeriesType = Partial<Record<SeriesType, boolean>>;
|
|
25
26
|
type Args = {
|
|
26
27
|
boundsWidth: number;
|