@gravity-ui/charts 1.43.1 → 1.45.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/annotation.d.ts +12 -0
- package/dist/cjs/core/constants/defaults/annotation.js +12 -0
- package/dist/cjs/core/constants/defaults/index.d.ts +1 -0
- package/dist/cjs/core/constants/defaults/index.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/constants.d.ts +1 -1
- package/dist/cjs/core/series/constants.js +1 -1
- package/dist/cjs/core/series/prepare-annotation.d.ts +12 -0
- package/dist/cjs/core/series/prepare-annotation.js +31 -0
- 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 +34 -2
- package/dist/cjs/core/types/chart/annotation.d.ts +45 -0
- package/dist/cjs/core/types/chart/annotation.js +1 -0
- package/dist/cjs/core/types/chart/area.d.ts +10 -1
- package/dist/cjs/core/types/chart/bar-x.d.ts +6 -0
- package/dist/cjs/core/types/chart/line.d.ts +8 -0
- package/dist/cjs/core/types/chart/marker.d.ts +6 -4
- package/dist/cjs/core/types/chart/series.d.ts +36 -2
- package/dist/cjs/core/types/chart/tooltip.d.ts +7 -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 +2 -0
- package/dist/cjs/core/types/index.js +2 -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/utils/text.d.ts +8 -0
- package/dist/cjs/core/utils/text.js +9 -1
- package/dist/cjs/core/validation/index.js +13 -0
- package/dist/cjs/core/zoom/zoom.js +24 -7
- package/dist/cjs/hooks/useShapes/annotation/index.d.ts +14 -0
- package/dist/cjs/hooks/useShapes/annotation/index.js +200 -0
- package/dist/cjs/hooks/useShapes/area/index.d.ts +2 -0
- package/dist/cjs/hooks/useShapes/area/index.js +21 -2
- package/dist/cjs/hooks/useShapes/area/prepare-data.d.ts +2 -1
- package/dist/cjs/hooks/useShapes/area/prepare-data.js +45 -26
- package/dist/cjs/hooks/useShapes/area/types.d.ts +4 -0
- package/dist/cjs/hooks/useShapes/bar-x/index.d.ts +2 -0
- package/dist/cjs/hooks/useShapes/bar-x/index.js +30 -2
- package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +32 -11
- package/dist/cjs/hooks/useShapes/bar-x/types.d.ts +2 -0
- package/dist/cjs/hooks/useShapes/index.d.ts +2 -1
- package/dist/cjs/hooks/useShapes/index.js +22 -3
- package/dist/cjs/hooks/useShapes/line/index.d.ts +2 -0
- package/dist/cjs/hooks/useShapes/line/index.js +21 -7
- package/dist/cjs/hooks/useShapes/line/prepare-data.d.ts +2 -1
- package/dist/cjs/hooks/useShapes/line/prepare-data.js +28 -10
- package/dist/cjs/hooks/useShapes/line/types.d.ts +4 -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/annotation.d.ts +12 -0
- package/dist/esm/core/constants/defaults/annotation.js +12 -0
- package/dist/esm/core/constants/defaults/index.d.ts +1 -0
- package/dist/esm/core/constants/defaults/index.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/constants.d.ts +1 -1
- package/dist/esm/core/series/constants.js +1 -1
- package/dist/esm/core/series/prepare-annotation.d.ts +12 -0
- package/dist/esm/core/series/prepare-annotation.js +31 -0
- 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 +34 -2
- package/dist/esm/core/types/chart/annotation.d.ts +45 -0
- package/dist/esm/core/types/chart/annotation.js +1 -0
- package/dist/esm/core/types/chart/area.d.ts +10 -1
- package/dist/esm/core/types/chart/bar-x.d.ts +6 -0
- package/dist/esm/core/types/chart/line.d.ts +8 -0
- package/dist/esm/core/types/chart/marker.d.ts +6 -4
- package/dist/esm/core/types/chart/series.d.ts +36 -2
- package/dist/esm/core/types/chart/tooltip.d.ts +7 -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 +2 -0
- package/dist/esm/core/types/index.js +2 -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/utils/text.d.ts +8 -0
- package/dist/esm/core/utils/text.js +9 -1
- package/dist/esm/core/validation/index.js +13 -0
- package/dist/esm/core/zoom/zoom.js +24 -7
- package/dist/esm/hooks/useShapes/annotation/index.d.ts +14 -0
- package/dist/esm/hooks/useShapes/annotation/index.js +200 -0
- package/dist/esm/hooks/useShapes/area/index.d.ts +2 -0
- package/dist/esm/hooks/useShapes/area/index.js +21 -2
- package/dist/esm/hooks/useShapes/area/prepare-data.d.ts +2 -1
- package/dist/esm/hooks/useShapes/area/prepare-data.js +45 -26
- package/dist/esm/hooks/useShapes/area/types.d.ts +4 -0
- package/dist/esm/hooks/useShapes/bar-x/index.d.ts +2 -0
- package/dist/esm/hooks/useShapes/bar-x/index.js +30 -2
- package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +32 -11
- package/dist/esm/hooks/useShapes/bar-x/types.d.ts +2 -0
- package/dist/esm/hooks/useShapes/index.d.ts +2 -1
- package/dist/esm/hooks/useShapes/index.js +22 -3
- package/dist/esm/hooks/useShapes/line/index.d.ts +2 -0
- package/dist/esm/hooks/useShapes/line/index.js +21 -7
- package/dist/esm/hooks/useShapes/line/prepare-data.d.ts +2 -1
- package/dist/esm/hooks/useShapes/line/prepare-data.js +28 -10
- package/dist/esm/hooks/useShapes/line/types.d.ts +4 -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 +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import type { DashStyle, LineCap, LineJoin } from '../../constants';
|
|
3
3
|
import type { MeaningfulAny } from '../misc';
|
|
4
|
+
import type { ChartAnnotationSeriesOptions } from './annotation';
|
|
4
5
|
import type { AreaSeries, AreaSeriesData } from './area';
|
|
5
6
|
import type { BarXSeries, BarXSeriesData } from './bar-x';
|
|
6
7
|
import type { BarYSeries, BarYSeriesData } from './bar-y';
|
|
@@ -15,8 +16,9 @@ import type { SankeySeries, SankeySeriesData } from './sankey';
|
|
|
15
16
|
import type { ScatterSeries, ScatterSeriesData } from './scatter';
|
|
16
17
|
import type { TreemapSeries, TreemapSeriesData } from './treemap';
|
|
17
18
|
import type { WaterfallSeries, WaterfallSeriesData } from './waterfall';
|
|
18
|
-
|
|
19
|
-
export type
|
|
19
|
+
import type { XRangeSeries, XRangeSeriesData } from './x-range';
|
|
20
|
+
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>;
|
|
21
|
+
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
22
|
export interface DataLabelRendererData<T = MeaningfulAny> {
|
|
21
23
|
data: ChartSeriesData<T>;
|
|
22
24
|
}
|
|
@@ -100,6 +102,8 @@ export interface ChartSeriesOptions {
|
|
|
100
102
|
hover?: BasicHoverState;
|
|
101
103
|
inactive?: BasicInactiveState;
|
|
102
104
|
};
|
|
105
|
+
/** Default annotation settings for all bar-x data points */
|
|
106
|
+
annotation?: ChartAnnotationSeriesOptions;
|
|
103
107
|
};
|
|
104
108
|
'bar-y'?: {
|
|
105
109
|
/**
|
|
@@ -211,6 +215,8 @@ export interface ChartSeriesOptions {
|
|
|
211
215
|
* @default 'round' when dashStyle is not 'solid', 'unset' when dashStyle is not 'solid'
|
|
212
216
|
*/
|
|
213
217
|
linejoin?: `${LineJoin}`;
|
|
218
|
+
/** Default annotation settings for all line data points */
|
|
219
|
+
annotation?: ChartAnnotationSeriesOptions;
|
|
214
220
|
};
|
|
215
221
|
area?: {
|
|
216
222
|
/**
|
|
@@ -230,6 +236,8 @@ export interface ChartSeriesOptions {
|
|
|
230
236
|
};
|
|
231
237
|
/** Options for the point markers of line series */
|
|
232
238
|
marker?: PointMarkerOptions;
|
|
239
|
+
/** Default annotation settings for all area data points */
|
|
240
|
+
annotation?: ChartAnnotationSeriesOptions;
|
|
233
241
|
};
|
|
234
242
|
treemap?: {
|
|
235
243
|
/** Options for the series states that provide additional styling information to the series. */
|
|
@@ -291,6 +299,32 @@ export interface ChartSeriesOptions {
|
|
|
291
299
|
hover?: BasicHoverState;
|
|
292
300
|
};
|
|
293
301
|
};
|
|
302
|
+
'x-range'?: {
|
|
303
|
+
/**
|
|
304
|
+
* The corner radius of the border surrounding each bar.
|
|
305
|
+
* @default 0
|
|
306
|
+
*/
|
|
307
|
+
borderRadius?: number;
|
|
308
|
+
/**
|
|
309
|
+
* The width of the border surrounding each bar.
|
|
310
|
+
* @default 0
|
|
311
|
+
*/
|
|
312
|
+
borderWidth?: number;
|
|
313
|
+
/**
|
|
314
|
+
* The color of the border surrounding each bar.
|
|
315
|
+
*/
|
|
316
|
+
borderColor?: string;
|
|
317
|
+
/**
|
|
318
|
+
* The dash style of the border surrounding each bar.
|
|
319
|
+
* @default 'Solid'
|
|
320
|
+
*/
|
|
321
|
+
borderDashStyle?: DashStyle;
|
|
322
|
+
/** Options for the series states that provide additional styling information to the series. */
|
|
323
|
+
states?: {
|
|
324
|
+
hover?: BasicHoverState;
|
|
325
|
+
inactive?: BasicInactiveState;
|
|
326
|
+
};
|
|
327
|
+
};
|
|
294
328
|
}
|
|
295
329
|
export type ChartSeriesRangeSliderOptions = {
|
|
296
330
|
/**
|
|
@@ -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>;
|
|
@@ -46,6 +47,7 @@ export interface TooltipDataChunkLine<T = MeaningfulAny> {
|
|
|
46
47
|
id: string;
|
|
47
48
|
name: string;
|
|
48
49
|
};
|
|
50
|
+
closest?: boolean;
|
|
49
51
|
}
|
|
50
52
|
export interface TooltipDataChunkArea<T = MeaningfulAny> {
|
|
51
53
|
data: AreaSeriesData<T>;
|
|
@@ -87,7 +89,11 @@ export interface TooltipDataChunkFunnel<T = MeaningfulAny> {
|
|
|
87
89
|
name: string;
|
|
88
90
|
};
|
|
89
91
|
}
|
|
90
|
-
export
|
|
92
|
+
export interface TooltipDataChunkXRange<T = MeaningfulAny> {
|
|
93
|
+
data: XRangeSeriesData<T>;
|
|
94
|
+
series: XRangeSeries<T>;
|
|
95
|
+
}
|
|
96
|
+
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
97
|
closest?: boolean;
|
|
92
98
|
};
|
|
93
99
|
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 {};
|
|
@@ -7,6 +7,7 @@ import type { ChartTitle } from './chart/title';
|
|
|
7
7
|
import type { ChartTooltip } from './chart/tooltip';
|
|
8
8
|
import type { MeaningfulAny } from './misc';
|
|
9
9
|
export * from './misc';
|
|
10
|
+
export * from './chart/annotation';
|
|
10
11
|
export * from './chart/axis';
|
|
11
12
|
export * from './chart/base';
|
|
12
13
|
export * from './chart/chart';
|
|
@@ -30,6 +31,7 @@ export * from './chart/radar';
|
|
|
30
31
|
export * from './chart/heatmap';
|
|
31
32
|
export * from './chart/funnel';
|
|
32
33
|
export * from './chart/brush';
|
|
34
|
+
export * from './chart/x-range';
|
|
33
35
|
export interface ChartData<T = MeaningfulAny> {
|
|
34
36
|
/**
|
|
35
37
|
* General options for the chart.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from './misc';
|
|
2
|
+
export * from './chart/annotation';
|
|
2
3
|
export * from './chart/axis';
|
|
3
4
|
export * from './chart/base';
|
|
4
5
|
export * from './chart/chart';
|
|
@@ -22,3 +23,4 @@ export * from './chart/radar';
|
|
|
22
23
|
export * from './chart/heatmap';
|
|
23
24
|
export * from './chart/funnel';
|
|
24
25
|
export * from './chart/brush';
|
|
26
|
+
export * from './chart/x-range';
|
|
@@ -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) {
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { Selection } from 'd3-selection';
|
|
2
2
|
import type { BaseTextStyle, MeaningfulAny } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Approximate ratio of descenders relative to the full font em height.
|
|
5
|
+
* Based on the Chromium hanging baseline algorithm where hanging offset ≈ ascent × 0.2.
|
|
6
|
+
* This means ascent ≈ 80% of em height, descenders ≈ 20%.
|
|
7
|
+
*
|
|
8
|
+
* @see https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/html/canvas/text_metrics.cc;l=32
|
|
9
|
+
*/
|
|
10
|
+
export declare const DESCENDER_RATIO = 0.2;
|
|
3
11
|
export declare function handleOverflowingText(tSpan: SVGTSpanElement | null, maxWidth: number, textWidth?: number): void;
|
|
4
12
|
export declare function setEllipsisForOverflowText<T>(selection: Selection<SVGTextElement, T, null, unknown>, maxWidth: number, textWidth?: number): void;
|
|
5
13
|
export declare function setEllipsisForOverflowTexts<T>(selection: Selection<SVGTextElement, T, MeaningfulAny, unknown>, maxWidth: ((datum: T) => number) | number, currentWidth?: (datum: T) => number): void;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { select } from 'd3-selection';
|
|
2
2
|
import { block } from '../../utils/cn';
|
|
3
3
|
const b = block('chart');
|
|
4
|
+
/**
|
|
5
|
+
* Approximate ratio of descenders relative to the full font em height.
|
|
6
|
+
* Based on the Chromium hanging baseline algorithm where hanging offset ≈ ascent × 0.2.
|
|
7
|
+
* This means ascent ≈ 80% of em height, descenders ≈ 20%.
|
|
8
|
+
*
|
|
9
|
+
* @see https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/html/canvas/text_metrics.cc;l=32
|
|
10
|
+
*/
|
|
11
|
+
export const DESCENDER_RATIO = 0.2;
|
|
4
12
|
export function handleOverflowingText(tSpan, maxWidth, textWidth) {
|
|
5
13
|
var _a, _b, _c;
|
|
6
14
|
if (!tSpan) {
|
|
@@ -210,7 +218,7 @@ export function getTextSizeFn({ style }) {
|
|
|
210
218
|
return {
|
|
211
219
|
width: textMetric.width,
|
|
212
220
|
height: textMetric.fontBoundingBoxDescent + textMetric.fontBoundingBoxAscent,
|
|
213
|
-
hangingOffset: textMetric.fontBoundingBoxAscent *
|
|
221
|
+
hangingOffset: textMetric.fontBoundingBoxAscent * DESCENDER_RATIO,
|
|
214
222
|
};
|
|
215
223
|
};
|
|
216
224
|
}
|
|
@@ -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'
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Selection } from 'd3-selection';
|
|
2
|
+
import type { PreparedAnnotation } from '../../../core/series/types';
|
|
3
|
+
type AnnotationAnchor = {
|
|
4
|
+
annotation: PreparedAnnotation;
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
};
|
|
8
|
+
export { type AnnotationAnchor };
|
|
9
|
+
export declare function renderAnnotations(args: {
|
|
10
|
+
anchors: AnnotationAnchor[];
|
|
11
|
+
container: Selection<SVGGElement, unknown, null, undefined>;
|
|
12
|
+
plotHeight: number;
|
|
13
|
+
plotWidth: number;
|
|
14
|
+
}): void;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { select } from 'd3-selection';
|
|
2
|
+
import { DESCENDER_RATIO } from '../../../core/utils/text';
|
|
3
|
+
import { block } from '../../../utils';
|
|
4
|
+
const b = block('annotation');
|
|
5
|
+
const ARROW_WIDTH = 18;
|
|
6
|
+
const ARROW_HEIGHT = 9;
|
|
7
|
+
// Base arrow path pointing downward (for "top" placement).
|
|
8
|
+
// Elliptical arc matching gravity-ui/uikit Popup arrow geometry.
|
|
9
|
+
// uikit builds the arrow from two 28×30 circles ($arrow-circle-width/height) with
|
|
10
|
+
// a 5px inset box-shadow ($arrow-border), clipped in 9×9 wrappers.
|
|
11
|
+
// The visible curve follows the inner edge of the ring:
|
|
12
|
+
// rx = circle_width/2 - (arrow_border - border_width) = 14 - 4 = 10
|
|
13
|
+
// ry = circle_height/2 - (arrow_border - border_width) = 15 - 4 = 11
|
|
14
|
+
const ARROW_RX = 10;
|
|
15
|
+
const ARROW_RY = 11;
|
|
16
|
+
const ARROW_PATH = (() => {
|
|
17
|
+
const hw = ARROW_WIDTH / 2;
|
|
18
|
+
const h = ARROW_HEIGHT;
|
|
19
|
+
return `M ${-hw},0 A ${ARROW_RX} ${ARROW_RY} 0 0 1 0,${h} A ${ARROW_RX} ${ARROW_RY} 0 0 1 ${hw},0 Z`;
|
|
20
|
+
})();
|
|
21
|
+
function getArrowRotation(placement) {
|
|
22
|
+
switch (placement) {
|
|
23
|
+
case 'top':
|
|
24
|
+
return 0;
|
|
25
|
+
case 'bottom':
|
|
26
|
+
return 180;
|
|
27
|
+
case 'right':
|
|
28
|
+
return 90;
|
|
29
|
+
case 'left':
|
|
30
|
+
default:
|
|
31
|
+
return -90;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function clampX(x, width, plotWidth) {
|
|
35
|
+
return Math.max(0, Math.min(x, plotWidth - width));
|
|
36
|
+
}
|
|
37
|
+
function clampY(y, height, plotHeight) {
|
|
38
|
+
return Math.max(0, Math.min(y, plotHeight - height));
|
|
39
|
+
}
|
|
40
|
+
function calculateLayout(args) {
|
|
41
|
+
const { anchorX, anchorY, popupWidth, popupHeight, offset, plotWidth, plotHeight } = args;
|
|
42
|
+
// Minimum distance from popup edge to arrow center (arrow half-width + border radius clearance)
|
|
43
|
+
const arrowEdgePadding = ARROW_WIDTH / 2;
|
|
44
|
+
// Check if anchor falls within popup's horizontal span (for top/bottom placement)
|
|
45
|
+
function isAnchorInPopupX(popupX) {
|
|
46
|
+
return (anchorX >= popupX + arrowEdgePadding &&
|
|
47
|
+
anchorX <= popupX + popupWidth - arrowEdgePadding);
|
|
48
|
+
}
|
|
49
|
+
// Check if anchor falls within popup's vertical span (for right/left placement)
|
|
50
|
+
function isAnchorInPopupY(popupY) {
|
|
51
|
+
return (anchorY >= popupY + arrowEdgePadding &&
|
|
52
|
+
anchorY <= popupY + popupHeight - arrowEdgePadding);
|
|
53
|
+
}
|
|
54
|
+
// Try top
|
|
55
|
+
const topY = anchorY - offset - ARROW_HEIGHT - popupHeight;
|
|
56
|
+
if (topY >= 0) {
|
|
57
|
+
const popupX = clampX(anchorX - popupWidth / 2, popupWidth, plotWidth);
|
|
58
|
+
if (isAnchorInPopupX(popupX)) {
|
|
59
|
+
return {
|
|
60
|
+
arrowX: anchorX,
|
|
61
|
+
arrowY: anchorY,
|
|
62
|
+
popupX,
|
|
63
|
+
popupY: topY,
|
|
64
|
+
placement: 'top',
|
|
65
|
+
showArrow: true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Try bottom
|
|
70
|
+
const bottomY = anchorY + offset + ARROW_HEIGHT;
|
|
71
|
+
if (bottomY + popupHeight <= plotHeight) {
|
|
72
|
+
const popupX = clampX(anchorX - popupWidth / 2, popupWidth, plotWidth);
|
|
73
|
+
if (isAnchorInPopupX(popupX)) {
|
|
74
|
+
return {
|
|
75
|
+
arrowX: anchorX,
|
|
76
|
+
arrowY: anchorY,
|
|
77
|
+
popupX,
|
|
78
|
+
popupY: bottomY,
|
|
79
|
+
placement: 'bottom',
|
|
80
|
+
showArrow: true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Try right
|
|
85
|
+
const rightX = anchorX + offset + ARROW_HEIGHT;
|
|
86
|
+
if (rightX + popupWidth <= plotWidth) {
|
|
87
|
+
const popupY = clampY(anchorY - popupHeight / 2, popupHeight, plotHeight);
|
|
88
|
+
if (isAnchorInPopupY(popupY)) {
|
|
89
|
+
return {
|
|
90
|
+
arrowX: anchorX,
|
|
91
|
+
arrowY: anchorY,
|
|
92
|
+
popupX: rightX,
|
|
93
|
+
popupY,
|
|
94
|
+
placement: 'right',
|
|
95
|
+
showArrow: true,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Try left
|
|
100
|
+
const leftX = anchorX - offset - ARROW_HEIGHT - popupWidth;
|
|
101
|
+
if (leftX >= 0) {
|
|
102
|
+
const popupY = clampY(anchorY - popupHeight / 2, popupHeight, plotHeight);
|
|
103
|
+
if (isAnchorInPopupY(popupY)) {
|
|
104
|
+
return {
|
|
105
|
+
arrowX: anchorX,
|
|
106
|
+
arrowY: anchorY,
|
|
107
|
+
popupX: leftX,
|
|
108
|
+
popupY,
|
|
109
|
+
placement: 'left',
|
|
110
|
+
showArrow: true,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Fallback: no arrow, popup near anchor (prefer above, then below)
|
|
115
|
+
const popupX = clampX(anchorX - popupWidth / 2, popupWidth, plotWidth);
|
|
116
|
+
const fallbackTopY = anchorY - offset - popupHeight;
|
|
117
|
+
const popupY = fallbackTopY >= 0 ? fallbackTopY : Math.min(plotHeight - popupHeight, anchorY + offset);
|
|
118
|
+
return {
|
|
119
|
+
arrowX: anchorX,
|
|
120
|
+
arrowY: anchorY,
|
|
121
|
+
popupX,
|
|
122
|
+
popupY,
|
|
123
|
+
placement: 'top',
|
|
124
|
+
showArrow: false,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function getArrowTranslate(layout, popupWidth, popupHeight) {
|
|
128
|
+
const { arrowX, arrowY, popupX, popupY, placement } = layout;
|
|
129
|
+
// Overlap by 0.5px to avoid subpixel gap between arrow and popup rect
|
|
130
|
+
const overlap = 0.5;
|
|
131
|
+
switch (placement) {
|
|
132
|
+
case 'top':
|
|
133
|
+
return `translate(${arrowX}, ${popupY + popupHeight - overlap})`;
|
|
134
|
+
case 'bottom':
|
|
135
|
+
return `translate(${arrowX}, ${popupY + overlap})`;
|
|
136
|
+
case 'right':
|
|
137
|
+
return `translate(${popupX + overlap}, ${arrowY})`;
|
|
138
|
+
case 'left':
|
|
139
|
+
default:
|
|
140
|
+
return `translate(${popupX + popupWidth - overlap}, ${arrowY})`;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
export function renderAnnotations(args) {
|
|
144
|
+
const { container, anchors, plotWidth, plotHeight } = args;
|
|
145
|
+
container.selectAll(`.${b()}`).remove();
|
|
146
|
+
if (!anchors.length) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const groups = container
|
|
150
|
+
.selectAll(`.${b()}`)
|
|
151
|
+
.data(anchors)
|
|
152
|
+
.join('g')
|
|
153
|
+
.attr('class', b());
|
|
154
|
+
groups.each(function (d) {
|
|
155
|
+
const g = select(this);
|
|
156
|
+
const { annotation, x: anchorX, y: anchorY } = d;
|
|
157
|
+
const { label, popup } = annotation;
|
|
158
|
+
const [paddingV, paddingH] = popup.padding;
|
|
159
|
+
const popupWidth = label.size.width + paddingH * 2;
|
|
160
|
+
const popupHeight = label.size.height + paddingV * 2;
|
|
161
|
+
const layout = calculateLayout({
|
|
162
|
+
anchorX,
|
|
163
|
+
anchorY,
|
|
164
|
+
popupWidth,
|
|
165
|
+
popupHeight,
|
|
166
|
+
offset: popup.offset,
|
|
167
|
+
plotWidth,
|
|
168
|
+
plotHeight,
|
|
169
|
+
});
|
|
170
|
+
// Popup background
|
|
171
|
+
g.append('rect')
|
|
172
|
+
.attr('class', b('popup'))
|
|
173
|
+
.attr('x', layout.popupX)
|
|
174
|
+
.attr('y', layout.popupY)
|
|
175
|
+
.attr('width', popupWidth)
|
|
176
|
+
.attr('height', popupHeight)
|
|
177
|
+
.attr('rx', popup.borderRadius)
|
|
178
|
+
.attr('ry', popup.borderRadius)
|
|
179
|
+
.attr('fill', popup.backgroundColor);
|
|
180
|
+
// Arrow
|
|
181
|
+
if (layout.showArrow) {
|
|
182
|
+
const arrowTranslate = getArrowTranslate(layout, popupWidth, popupHeight);
|
|
183
|
+
const arrowRotation = getArrowRotation(layout.placement);
|
|
184
|
+
g.append('path')
|
|
185
|
+
.attr('class', b('arrow'))
|
|
186
|
+
.attr('d', ARROW_PATH)
|
|
187
|
+
.attr('fill', popup.backgroundColor)
|
|
188
|
+
.attr('transform', `${arrowTranslate} rotate(${arrowRotation})`);
|
|
189
|
+
}
|
|
190
|
+
// Text
|
|
191
|
+
g.append('text')
|
|
192
|
+
.attr('class', b('text'))
|
|
193
|
+
.text(label.text)
|
|
194
|
+
.attr('x', layout.popupX + paddingH)
|
|
195
|
+
.attr('y', layout.popupY + paddingV + label.size.height * (1 - DESCENDER_RATIO))
|
|
196
|
+
.style('font-size', label.style.fontSize)
|
|
197
|
+
.style('font-weight', label.style.fontWeight || '')
|
|
198
|
+
.style('fill', label.style.fontColor || '');
|
|
199
|
+
});
|
|
200
|
+
}
|
|
@@ -3,6 +3,8 @@ import type { Dispatch } from 'd3-dispatch';
|
|
|
3
3
|
import type { PreparedSeriesOptions } from '../../useSeries/types';
|
|
4
4
|
import type { PreparedAreaData } from './types';
|
|
5
5
|
type Args = {
|
|
6
|
+
boundsHeight: number;
|
|
7
|
+
boundsWidth: number;
|
|
6
8
|
clipPathId: string;
|
|
7
9
|
htmlLayout: HTMLElement | null;
|
|
8
10
|
preparedData: PreparedAreaData[];
|