@gravity-ui/charts 1.39.0 → 1.40.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/useChartInnerProps.d.ts +2 -38
- package/dist/cjs/components/ChartInner/utils/axis.d.ts +1 -37
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/utils.d.ts +6 -0
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/utils.js +41 -0
- package/dist/cjs/components/Tooltip/index.js +2 -0
- package/dist/cjs/hooks/useAxis/index.d.ts +1 -37
- package/dist/cjs/hooks/useAxis/types.d.ts +6 -4
- package/dist/cjs/hooks/useAxisScales/index.d.ts +2 -2
- package/dist/cjs/hooks/useCrosshair/index.js +21 -15
- package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +1 -1
- package/dist/cjs/hooks/useTooltip/index.d.ts +4 -1
- package/dist/cjs/hooks/useTooltip/index.js +12 -5
- package/dist/cjs/types/chart/axis.d.ts +2 -1
- package/dist/cjs/types/chart/tooltip.d.ts +20 -0
- package/dist/cjs/utils/chart/zoom.js +2 -2
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +2 -38
- package/dist/esm/components/ChartInner/utils/axis.d.ts +1 -37
- package/dist/esm/components/Tooltip/DefaultTooltipContent/utils.d.ts +6 -0
- package/dist/esm/components/Tooltip/DefaultTooltipContent/utils.js +41 -0
- package/dist/esm/components/Tooltip/index.js +2 -0
- package/dist/esm/hooks/useAxis/index.d.ts +1 -37
- package/dist/esm/hooks/useAxis/types.d.ts +6 -4
- package/dist/esm/hooks/useAxisScales/index.d.ts +2 -2
- package/dist/esm/hooks/useCrosshair/index.js +21 -15
- package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +1 -1
- package/dist/esm/hooks/useTooltip/index.d.ts +4 -1
- package/dist/esm/hooks/useTooltip/index.js +12 -5
- package/dist/esm/types/chart/axis.d.ts +2 -1
- package/dist/esm/types/chart/tooltip.d.ts +20 -0
- package/dist/esm/utils/chart/zoom.js +2 -2
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Dispatch } from 'd3';
|
|
3
|
-
import type { ChartScale, LegendItem, OnLegendItemClick, PreparedLegend, PreparedSeries, PreparedSplit, PreparedXAxis, RangeSliderState, ShapeData, ZoomState } from '../../hooks';
|
|
3
|
+
import type { ChartScale, LegendItem, OnLegendItemClick, PreparedLegend, PreparedSeries, PreparedSplit, PreparedXAxis, PreparedYAxis, RangeSliderState, ShapeData, ZoomState } from '../../hooks';
|
|
4
4
|
import type { PreparedChart } from '../../hooks/types';
|
|
5
5
|
import type { LegendConfig } from '../../types';
|
|
6
6
|
import type { ChartInnerProps } from './types';
|
|
@@ -21,43 +21,7 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
21
21
|
boundsHeight: number;
|
|
22
22
|
boundsWidth: number;
|
|
23
23
|
xAxis: PreparedXAxis | null;
|
|
24
|
-
yAxis:
|
|
25
|
-
type: import("../../types").ChartAxisType;
|
|
26
|
-
labels: Omit<import("../../types").ChartAxisLabels, "style" | "enabled" | "padding" | "autoRotation"> & Required<Pick<import("../../types").ChartAxisLabels, "margin" | "enabled" | "html" | "rotation" | "padding">> & {
|
|
27
|
-
style: import("../../types").BaseTextStyle;
|
|
28
|
-
rotation: number;
|
|
29
|
-
height: number;
|
|
30
|
-
width: number;
|
|
31
|
-
lineHeight: number;
|
|
32
|
-
maxWidth: number;
|
|
33
|
-
};
|
|
34
|
-
title: {
|
|
35
|
-
height: number;
|
|
36
|
-
width: number;
|
|
37
|
-
text: string;
|
|
38
|
-
margin: number;
|
|
39
|
-
style: import("../../types").BaseTextStyle;
|
|
40
|
-
align: import("../../types").ChartAxisTitleAlignment;
|
|
41
|
-
maxRowCount: number;
|
|
42
|
-
rotation: number;
|
|
43
|
-
maxWidth: number;
|
|
44
|
-
html: boolean;
|
|
45
|
-
};
|
|
46
|
-
min?: number;
|
|
47
|
-
grid: {
|
|
48
|
-
enabled: boolean;
|
|
49
|
-
};
|
|
50
|
-
maxPadding: number;
|
|
51
|
-
ticks: {
|
|
52
|
-
pixelInterval?: number;
|
|
53
|
-
};
|
|
54
|
-
tickMarks: import("../../hooks").PreparedAxisTickMarks;
|
|
55
|
-
position: "left" | "right" | "top" | "bottom";
|
|
56
|
-
plotIndex: number;
|
|
57
|
-
plotLines: import("../../hooks").PreparedAxisPlotLine[];
|
|
58
|
-
plotBands: import("../../hooks").PreparedAxisPlotBand[];
|
|
59
|
-
crosshair: Required<import("../../types").AxisCrosshair>;
|
|
60
|
-
})[];
|
|
24
|
+
yAxis: PreparedYAxis[];
|
|
61
25
|
shapesData: ShapeData[];
|
|
62
26
|
shapesReady: boolean;
|
|
63
27
|
handleLegendItemClick: OnLegendItemClick;
|
|
@@ -5,40 +5,4 @@ export declare function recalculateYAxisLabelsWidth(props: {
|
|
|
5
5
|
seriesData: PreparedSeries[];
|
|
6
6
|
yAxis: PreparedYAxis[];
|
|
7
7
|
yScale?: (ChartScale | undefined)[];
|
|
8
|
-
}): Promise<
|
|
9
|
-
type: import("../../..").ChartAxisType;
|
|
10
|
-
labels: Omit<import("../../..").ChartAxisLabels, "style" | "enabled" | "padding" | "autoRotation"> & Required<Pick<import("../../..").ChartAxisLabels, "margin" | "enabled" | "html" | "rotation" | "padding">> & {
|
|
11
|
-
style: import("../../..").BaseTextStyle;
|
|
12
|
-
rotation: number;
|
|
13
|
-
height: number;
|
|
14
|
-
width: number;
|
|
15
|
-
lineHeight: number;
|
|
16
|
-
maxWidth: number;
|
|
17
|
-
};
|
|
18
|
-
title: {
|
|
19
|
-
height: number;
|
|
20
|
-
width: number;
|
|
21
|
-
text: string;
|
|
22
|
-
margin: number;
|
|
23
|
-
style: import("../../..").BaseTextStyle;
|
|
24
|
-
align: import("../../..").ChartAxisTitleAlignment;
|
|
25
|
-
maxRowCount: number;
|
|
26
|
-
rotation: number;
|
|
27
|
-
maxWidth: number;
|
|
28
|
-
html: boolean;
|
|
29
|
-
};
|
|
30
|
-
min?: number;
|
|
31
|
-
grid: {
|
|
32
|
-
enabled: boolean;
|
|
33
|
-
};
|
|
34
|
-
maxPadding: number;
|
|
35
|
-
ticks: {
|
|
36
|
-
pixelInterval?: number;
|
|
37
|
-
};
|
|
38
|
-
tickMarks: import("../../../hooks").PreparedAxisTickMarks;
|
|
39
|
-
position: "left" | "right" | "top" | "bottom";
|
|
40
|
-
plotIndex: number;
|
|
41
|
-
plotLines: import("../../../hooks").PreparedAxisPlotLine[];
|
|
42
|
-
plotBands: import("../../../hooks").PreparedAxisPlotBand[];
|
|
43
|
-
crosshair: Required<import("../../..").AxisCrosshair>;
|
|
44
|
-
})[]>;
|
|
8
|
+
}): Promise<PreparedYAxis[]>;
|
|
@@ -35,6 +35,12 @@ export declare function getPreparedAggregation(args: {
|
|
|
35
35
|
xAxis?: ChartXAxis | null;
|
|
36
36
|
yAxis?: ChartYAxis;
|
|
37
37
|
}): ChartTooltipTotalsBuiltInAggregation | (() => ChartTooltipTotalsAggregationValue);
|
|
38
|
+
export declare function getSortedHovered(args: {
|
|
39
|
+
hovered: TooltipDataChunk[];
|
|
40
|
+
sorting?: ChartTooltip['sorting'];
|
|
41
|
+
xAxis?: ChartXAxis | null;
|
|
42
|
+
yAxis?: ChartYAxis;
|
|
43
|
+
}): TooltipDataChunk[];
|
|
38
44
|
export declare function getTooltipRowColorSymbol({ series, color, height, width, }: {
|
|
39
45
|
color?: string;
|
|
40
46
|
series?: TooltipDataChunk['series'];
|
|
@@ -130,6 +130,47 @@ export function getPreparedAggregation(args) {
|
|
|
130
130
|
}
|
|
131
131
|
return 'sum';
|
|
132
132
|
}
|
|
133
|
+
export function getSortedHovered(args) {
|
|
134
|
+
var _a;
|
|
135
|
+
const { hovered, sorting, xAxis, yAxis } = args;
|
|
136
|
+
if (!sorting) {
|
|
137
|
+
return hovered;
|
|
138
|
+
}
|
|
139
|
+
if (typeof sorting === 'function') {
|
|
140
|
+
return [...hovered].sort(sorting);
|
|
141
|
+
}
|
|
142
|
+
switch (sorting.key) {
|
|
143
|
+
case 'value': {
|
|
144
|
+
const values = getHoveredValues({ hovered, xAxis, yAxis });
|
|
145
|
+
const direction = (_a = sorting.direction) !== null && _a !== void 0 ? _a : 'asc';
|
|
146
|
+
const compareValue = (a, b) => {
|
|
147
|
+
const aNil = a === null || a === undefined;
|
|
148
|
+
const bNil = b === null || b === undefined;
|
|
149
|
+
if (aNil && bNil) {
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
if (aNil) {
|
|
153
|
+
return -1;
|
|
154
|
+
}
|
|
155
|
+
if (bNil) {
|
|
156
|
+
return 1;
|
|
157
|
+
}
|
|
158
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
159
|
+
return a - b;
|
|
160
|
+
}
|
|
161
|
+
return String(a).localeCompare(String(b));
|
|
162
|
+
};
|
|
163
|
+
const indices = hovered.map((_, i) => i);
|
|
164
|
+
indices.sort((i, j) => direction === 'asc'
|
|
165
|
+
? compareValue(values[i], values[j])
|
|
166
|
+
: compareValue(values[j], values[i]));
|
|
167
|
+
return indices.map((i) => hovered[i]);
|
|
168
|
+
}
|
|
169
|
+
default: {
|
|
170
|
+
return hovered;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
133
174
|
export function getTooltipRowColorSymbol({ series, color, height = 8, width = 16, }) {
|
|
134
175
|
if ((series === null || series === void 0 ? void 0 : series.type) === 'line') {
|
|
135
176
|
const colorSymbol = create('svg').attr('height', height).attr('width', width);
|
|
@@ -10,6 +10,8 @@ export const Tooltip = (props) => {
|
|
|
10
10
|
const { hovered, hoveredPlotLines, hoveredPlotBands, pointerPosition } = useTooltip({
|
|
11
11
|
dispatcher,
|
|
12
12
|
tooltip,
|
|
13
|
+
xAxis,
|
|
14
|
+
yAxis,
|
|
13
15
|
});
|
|
14
16
|
const containerRect = (svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) || { left: 0, top: 0 };
|
|
15
17
|
const left = ((pointerPosition === null || pointerPosition === void 0 ? void 0 : pointerPosition[0]) || 0) + containerRect.left;
|
|
@@ -16,43 +16,7 @@ interface UseAxesProps {
|
|
|
16
16
|
}
|
|
17
17
|
export declare function getAxes(props: UseAxesProps): Promise<{
|
|
18
18
|
xAxis: import("./types").PreparedXAxis | null;
|
|
19
|
-
yAxis:
|
|
20
|
-
type: import("../../types").ChartAxisType;
|
|
21
|
-
labels: Omit<import("../../types").ChartAxisLabels, "style" | "enabled" | "padding" | "autoRotation"> & Required<Pick<import("../../types").ChartAxisLabels, "margin" | "enabled" | "html" | "rotation" | "padding">> & {
|
|
22
|
-
style: import("../../types").BaseTextStyle;
|
|
23
|
-
rotation: number;
|
|
24
|
-
height: number;
|
|
25
|
-
width: number;
|
|
26
|
-
lineHeight: number;
|
|
27
|
-
maxWidth: number;
|
|
28
|
-
};
|
|
29
|
-
title: {
|
|
30
|
-
height: number;
|
|
31
|
-
width: number;
|
|
32
|
-
text: string;
|
|
33
|
-
margin: number;
|
|
34
|
-
style: import("../../types").BaseTextStyle;
|
|
35
|
-
align: import("../../types").ChartAxisTitleAlignment;
|
|
36
|
-
maxRowCount: number;
|
|
37
|
-
rotation: number;
|
|
38
|
-
maxWidth: number;
|
|
39
|
-
html: boolean;
|
|
40
|
-
};
|
|
41
|
-
min?: number;
|
|
42
|
-
grid: {
|
|
43
|
-
enabled: boolean;
|
|
44
|
-
};
|
|
45
|
-
maxPadding: number;
|
|
46
|
-
ticks: {
|
|
47
|
-
pixelInterval?: number;
|
|
48
|
-
};
|
|
49
|
-
tickMarks: import("./types").PreparedAxisTickMarks;
|
|
50
|
-
position: "left" | "right" | "top" | "bottom";
|
|
51
|
-
plotIndex: number;
|
|
52
|
-
plotLines: import("./types").PreparedAxisPlotLine[];
|
|
53
|
-
plotBands: import("./types").PreparedAxisPlotBand[];
|
|
54
|
-
crosshair: Required<import("../../types").AxisCrosshair>;
|
|
55
|
-
})[];
|
|
19
|
+
yAxis: import("./types").PreparedYAxis[];
|
|
56
20
|
}>;
|
|
57
21
|
export declare function useAxis(props: UseAxesProps): AxesState;
|
|
58
22
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DashStyle } from '../../constants';
|
|
2
|
-
import type { AxisCrosshair, AxisPlotBand, BaseTextStyle, ChartAxis, ChartAxisLabels, ChartAxisRangeSlider, ChartAxisTitleAlignment, ChartAxisType, DeepRequired, MeaningfulAny, PlotLayerPlacement } from '../../types';
|
|
2
|
+
import type { AxisCrosshair, AxisPlotBand, BaseTextStyle, ChartAxis, ChartAxisLabels, ChartAxisRangeSlider, ChartAxisTitleAlignment, ChartAxisTitleRotation, ChartAxisType, DeepRequired, MeaningfulAny, PlotLayerPlacement } from '../../types';
|
|
3
3
|
type PreparedAxisLabels = Omit<ChartAxisLabels, 'enabled' | 'padding' | 'style' | 'autoRotation'> & Required<Pick<ChartAxisLabels, 'enabled' | 'padding' | 'margin' | 'rotation' | 'html'>> & {
|
|
4
4
|
style: BaseTextStyle;
|
|
5
5
|
rotation: number;
|
|
@@ -51,7 +51,7 @@ type PreparedBaseAxis = Omit<ChartAxis, 'type' | 'labels' | 'plotLines' | 'plotB
|
|
|
51
51
|
style: BaseTextStyle;
|
|
52
52
|
align: ChartAxisTitleAlignment;
|
|
53
53
|
maxRowCount: number;
|
|
54
|
-
rotation:
|
|
54
|
+
rotation: ChartAxisTitleRotation;
|
|
55
55
|
maxWidth: number;
|
|
56
56
|
html: boolean;
|
|
57
57
|
};
|
|
@@ -64,7 +64,6 @@ type PreparedBaseAxis = Omit<ChartAxis, 'type' | 'labels' | 'plotLines' | 'plotB
|
|
|
64
64
|
pixelInterval?: number;
|
|
65
65
|
};
|
|
66
66
|
tickMarks: PreparedAxisTickMarks;
|
|
67
|
-
position: 'left' | 'right' | 'top' | 'bottom';
|
|
68
67
|
plotIndex: number;
|
|
69
68
|
plotLines: PreparedAxisPlotLine[];
|
|
70
69
|
plotBands: PreparedAxisPlotBand[];
|
|
@@ -72,8 +71,11 @@ type PreparedBaseAxis = Omit<ChartAxis, 'type' | 'labels' | 'plotLines' | 'plotB
|
|
|
72
71
|
};
|
|
73
72
|
export type PreparedXAxis = PreparedBaseAxis & {
|
|
74
73
|
rangeSlider: PreparedRangeSlider;
|
|
74
|
+
position: 'bottom';
|
|
75
|
+
};
|
|
76
|
+
export type PreparedYAxis = PreparedBaseAxis & {
|
|
77
|
+
position: 'left' | 'right';
|
|
75
78
|
};
|
|
76
|
-
export type PreparedYAxis = PreparedBaseAxis;
|
|
77
79
|
export type PreparedAxis = PreparedXAxis | PreparedYAxis;
|
|
78
80
|
export type AxesState = {
|
|
79
81
|
xAxis: PreparedXAxis | null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PreparedAxis, PreparedSeries, PreparedSplit, RangeSliderState, ZoomState } from '../../hooks';
|
|
1
|
+
import type { PreparedAxis, PreparedSeries, PreparedSplit, PreparedYAxis, RangeSliderState, ZoomState } from '../../hooks';
|
|
2
2
|
import type { ChartScale } from './types';
|
|
3
3
|
export { createXScale } from './x-scale';
|
|
4
4
|
export { createYScale } from './y-scale';
|
|
@@ -7,7 +7,7 @@ type Args = {
|
|
|
7
7
|
boundsHeight: number;
|
|
8
8
|
series: PreparedSeries[];
|
|
9
9
|
xAxis: PreparedAxis | null;
|
|
10
|
-
yAxis:
|
|
10
|
+
yAxis: PreparedYAxis[];
|
|
11
11
|
split: PreparedSplit;
|
|
12
12
|
isRangeSlider?: boolean;
|
|
13
13
|
rangeSliderState?: RangeSliderState;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { line, select } from 'd3';
|
|
3
3
|
import { getAxisPlotsPosition, getLineDashArray } from '../../utils';
|
|
4
|
+
import { getXValue, getYValue } from '../useShapes/utils';
|
|
4
5
|
import { useCrosshairHover } from './useCrosshairHover';
|
|
5
6
|
export const useCrosshair = (props) => {
|
|
6
7
|
var _a, _b;
|
|
@@ -29,15 +30,18 @@ export const useCrosshair = (props) => {
|
|
|
29
30
|
crosshairSelection
|
|
30
31
|
.append('path')
|
|
31
32
|
.attr('d', (hoveredElement) => {
|
|
32
|
-
var _a, _b, _c;
|
|
33
33
|
let lineValue = 0;
|
|
34
|
-
if (xAxis.crosshair.snap
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
if (xAxis.crosshair.snap &&
|
|
35
|
+
typeof hoveredElement.data === 'object' &&
|
|
36
|
+
'x' in hoveredElement.data) {
|
|
37
|
+
const xVal = getXValue({
|
|
38
|
+
point: hoveredElement.data,
|
|
39
|
+
xAxis,
|
|
40
|
+
xScale: xAxisScale,
|
|
41
|
+
});
|
|
42
|
+
lineValue = xVal !== null && !Number.isNaN(xVal) ? xVal : lineValue;
|
|
39
43
|
}
|
|
40
|
-
else {
|
|
44
|
+
else if (!xAxis.crosshair.snap) {
|
|
41
45
|
lineValue = pointerXPos - boundsOffsetLeft;
|
|
42
46
|
}
|
|
43
47
|
const points = [
|
|
@@ -89,16 +93,18 @@ export const useCrosshair = (props) => {
|
|
|
89
93
|
crosshairSelection
|
|
90
94
|
.append('path')
|
|
91
95
|
.attr('d', (hoveredElement) => {
|
|
92
|
-
var _a, _b, _c;
|
|
93
96
|
let lineValue = 0;
|
|
94
|
-
if (yAxis.crosshair.snap
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
if (yAxis.crosshair.snap &&
|
|
98
|
+
typeof hoveredElement.data === 'object' &&
|
|
99
|
+
'y' in hoveredElement.data) {
|
|
100
|
+
const yVal = getYValue({
|
|
101
|
+
point: hoveredElement.data,
|
|
102
|
+
yAxis,
|
|
103
|
+
yScale: yAxisScale,
|
|
104
|
+
});
|
|
105
|
+
lineValue = yVal !== null && !Number.isNaN(yVal) ? yVal : lineValue;
|
|
100
106
|
}
|
|
101
|
-
else {
|
|
107
|
+
else if (!yAxis.crosshair.snap) {
|
|
102
108
|
lineValue = pointerYPos - boundsOffsetTop;
|
|
103
109
|
}
|
|
104
110
|
const points = [
|
|
@@ -178,7 +178,7 @@ export const prepareBarXData = async (args) => {
|
|
|
178
178
|
}
|
|
179
179
|
if (series.some((s) => s.stacking === 'percent')) {
|
|
180
180
|
let acc = 0;
|
|
181
|
-
const ratio = plotHeight / (positiveStackHeight - stackItems.length);
|
|
181
|
+
const ratio = plotHeight / (positiveStackHeight - (stackItems.length - 1) * stackGap);
|
|
182
182
|
stackItems.forEach((item) => {
|
|
183
183
|
item.height = item.height * ratio;
|
|
184
184
|
item.y = plotHeight - item.height - acc;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { Dispatch } from 'd3';
|
|
2
2
|
import type { AxisPlotBand, AxisPlotLine, PointPosition, TooltipDataChunk } from '../../types';
|
|
3
3
|
import type { PreparedTooltip } from '../types';
|
|
4
|
+
import type { PreparedXAxis, PreparedYAxis } from '../useAxis/types';
|
|
4
5
|
type Args = {
|
|
5
6
|
dispatcher: Dispatch<object>;
|
|
6
7
|
tooltip: PreparedTooltip;
|
|
8
|
+
xAxis?: PreparedXAxis | null;
|
|
9
|
+
yAxis?: PreparedYAxis;
|
|
7
10
|
};
|
|
8
|
-
export declare const useTooltip: ({ dispatcher, tooltip }: Args) => {
|
|
11
|
+
export declare const useTooltip: ({ dispatcher, tooltip, xAxis, yAxis }: Args) => {
|
|
9
12
|
hovered: TooltipDataChunk[] | undefined;
|
|
10
13
|
hoveredPlotLines: AxisPlotLine[] | undefined;
|
|
11
14
|
hoveredPlotBands: AxisPlotBand[] | undefined;
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import isEqual from 'lodash/isEqual';
|
|
3
|
-
|
|
3
|
+
import { getSortedHovered } from '../../components/Tooltip/DefaultTooltipContent/utils';
|
|
4
|
+
export const useTooltip = ({ dispatcher, tooltip, xAxis, yAxis }) => {
|
|
4
5
|
const [{ hovered, hoveredPlotLines, hoveredPlotBands, pointerPosition }, setTooltipState] = React.useState({});
|
|
5
6
|
const prevHovered = React.useRef(hovered);
|
|
6
7
|
React.useEffect(() => {
|
|
7
8
|
if (tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabled) {
|
|
8
9
|
dispatcher.on('hover-shape.tooltip', (nextHovered, nextPointerPosition, nextHoveredPlots) => {
|
|
9
10
|
const filteredNextHovered = nextHovered === null || nextHovered === void 0 ? void 0 : nextHovered.filter((item) => 'y' in item.data ? item.data.y !== null : true);
|
|
10
|
-
const
|
|
11
|
+
const sortedHovered = getSortedHovered({
|
|
12
|
+
hovered: filteredNextHovered !== null && filteredNextHovered !== void 0 ? filteredNextHovered : [],
|
|
13
|
+
sorting: tooltip === null || tooltip === void 0 ? void 0 : tooltip.sorting,
|
|
14
|
+
xAxis,
|
|
15
|
+
yAxis,
|
|
16
|
+
});
|
|
17
|
+
const isHoveredChanged = !isEqual(prevHovered.current, sortedHovered);
|
|
11
18
|
const newTooltipState = {
|
|
12
|
-
hovered: isHoveredChanged ?
|
|
19
|
+
hovered: isHoveredChanged ? sortedHovered : prevHovered.current,
|
|
13
20
|
hoveredPlotLines: nextHoveredPlots === null || nextHoveredPlots === void 0 ? void 0 : nextHoveredPlots.lines,
|
|
14
21
|
hoveredPlotBands: nextHoveredPlots === null || nextHoveredPlots === void 0 ? void 0 : nextHoveredPlots.bands,
|
|
15
22
|
pointerPosition: nextPointerPosition,
|
|
16
23
|
};
|
|
17
24
|
if (isHoveredChanged) {
|
|
18
|
-
prevHovered.current =
|
|
25
|
+
prevHovered.current = sortedHovered;
|
|
19
26
|
}
|
|
20
27
|
setTooltipState(newTooltipState);
|
|
21
28
|
});
|
|
@@ -25,7 +32,7 @@ export const useTooltip = ({ dispatcher, tooltip }) => {
|
|
|
25
32
|
dispatcher.on('hover-shape.tooltip', null);
|
|
26
33
|
}
|
|
27
34
|
};
|
|
28
|
-
}, [dispatcher, tooltip]);
|
|
35
|
+
}, [dispatcher, tooltip, xAxis, yAxis]);
|
|
29
36
|
return {
|
|
30
37
|
hovered,
|
|
31
38
|
hoveredPlotLines,
|
|
@@ -6,6 +6,7 @@ import type { BaseTextStyle } from './base';
|
|
|
6
6
|
import type { ChartBrush } from './brush';
|
|
7
7
|
export type ChartAxisType = (typeof AXIS_TYPE)[keyof typeof AXIS_TYPE];
|
|
8
8
|
export type ChartAxisTitleAlignment = 'left' | 'center' | 'right';
|
|
9
|
+
export type ChartAxisTitleRotation = 0 | 90 | -90;
|
|
9
10
|
export interface ChartAxisLabels {
|
|
10
11
|
/** Enable or disable the axis labels. */
|
|
11
12
|
enabled?: boolean;
|
|
@@ -314,7 +315,7 @@ export interface ChartYAxisTitle extends ChartAxisTitle {
|
|
|
314
315
|
*
|
|
315
316
|
* The default values are -90 for the left axis and 90 for the right.
|
|
316
317
|
*/
|
|
317
|
-
rotation?:
|
|
318
|
+
rotation?: ChartAxisTitleRotation;
|
|
318
319
|
/**
|
|
319
320
|
* Interval of the tick marks(absolute or relative to the chart area).
|
|
320
321
|
*
|
|
@@ -115,6 +115,7 @@ export type ChartTooltipRowRendererArgs = {
|
|
|
115
115
|
hovered?: TooltipDataChunk<unknown>[];
|
|
116
116
|
className?: string;
|
|
117
117
|
};
|
|
118
|
+
export type ChartTooltipSortComparator<T = MeaningfulAny> = (a: TooltipDataChunk<T>, b: TooltipDataChunk<T>) => number;
|
|
118
119
|
export interface ChartTooltip<T = MeaningfulAny> {
|
|
119
120
|
enabled?: boolean;
|
|
120
121
|
/** Specifies the renderer for the tooltip. If returned null default tooltip renderer will be used. */
|
|
@@ -158,4 +159,23 @@ export interface ChartTooltip<T = MeaningfulAny> {
|
|
|
158
159
|
* It is assigned as a data-qa attribute to an element.
|
|
159
160
|
*/
|
|
160
161
|
qa?: string;
|
|
162
|
+
/**
|
|
163
|
+
* Controls the order of tooltip rows. Applied to `hovered` before rendering.
|
|
164
|
+
* Use a custom comparator `(a, b) => number` for arbitrary ordering.
|
|
165
|
+
*/
|
|
166
|
+
sorting?: {
|
|
167
|
+
/**
|
|
168
|
+
* Determines what data should be used to sort by.
|
|
169
|
+
* `'value'` uses the numeric value of each series point: `y` for most series
|
|
170
|
+
* (line, area, bar-x, scatter, waterfall), `x` for bar-y, and `value` for
|
|
171
|
+
* pie, radar, heatmap, treemap, funnel. `null` values are sorted as lowest.
|
|
172
|
+
* @default undefined (sorting disabled)
|
|
173
|
+
*/
|
|
174
|
+
key?: 'value' | undefined;
|
|
175
|
+
/**
|
|
176
|
+
* Sorting direction.
|
|
177
|
+
* @default 'asc'
|
|
178
|
+
*/
|
|
179
|
+
direction?: 'asc' | 'desc';
|
|
180
|
+
} | ChartTooltipSortComparator<T>;
|
|
161
181
|
}
|
|
@@ -50,9 +50,9 @@ export function getZoomedSeriesData(args) {
|
|
|
50
50
|
}
|
|
51
51
|
const zoomedSeriesData = [];
|
|
52
52
|
const zoomedShapesSeriesData = [];
|
|
53
|
-
let prevPointInRange = false;
|
|
54
|
-
let currentPointInRange = false;
|
|
55
53
|
seriesData.forEach((seriesItem) => {
|
|
54
|
+
let prevPointInRange = false;
|
|
55
|
+
let currentPointInRange = false;
|
|
56
56
|
const filteredData = [];
|
|
57
57
|
const filteredShapesData = SERIES_TYPE_WITH_HIDDEN_POINTS.includes(seriesItem.type) && (xAxis === null || xAxis === void 0 ? void 0 : xAxis.type) !== 'category'
|
|
58
58
|
? []
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Dispatch } from 'd3';
|
|
3
|
-
import type { ChartScale, LegendItem, OnLegendItemClick, PreparedLegend, PreparedSeries, PreparedSplit, PreparedXAxis, RangeSliderState, ShapeData, ZoomState } from '../../hooks';
|
|
3
|
+
import type { ChartScale, LegendItem, OnLegendItemClick, PreparedLegend, PreparedSeries, PreparedSplit, PreparedXAxis, PreparedYAxis, RangeSliderState, ShapeData, ZoomState } from '../../hooks';
|
|
4
4
|
import type { PreparedChart } from '../../hooks/types';
|
|
5
5
|
import type { LegendConfig } from '../../types';
|
|
6
6
|
import type { ChartInnerProps } from './types';
|
|
@@ -21,43 +21,7 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
21
21
|
boundsHeight: number;
|
|
22
22
|
boundsWidth: number;
|
|
23
23
|
xAxis: PreparedXAxis | null;
|
|
24
|
-
yAxis:
|
|
25
|
-
type: import("../..").ChartAxisType;
|
|
26
|
-
labels: Omit<import("../..").ChartAxisLabels, "style" | "enabled" | "padding" | "autoRotation"> & Required<Pick<import("../..").ChartAxisLabels, "margin" | "enabled" | "html" | "rotation" | "padding">> & {
|
|
27
|
-
style: import("../..").BaseTextStyle;
|
|
28
|
-
rotation: number;
|
|
29
|
-
height: number;
|
|
30
|
-
width: number;
|
|
31
|
-
lineHeight: number;
|
|
32
|
-
maxWidth: number;
|
|
33
|
-
};
|
|
34
|
-
title: {
|
|
35
|
-
height: number;
|
|
36
|
-
width: number;
|
|
37
|
-
text: string;
|
|
38
|
-
margin: number;
|
|
39
|
-
style: import("../..").BaseTextStyle;
|
|
40
|
-
align: import("../..").ChartAxisTitleAlignment;
|
|
41
|
-
maxRowCount: number;
|
|
42
|
-
rotation: number;
|
|
43
|
-
maxWidth: number;
|
|
44
|
-
html: boolean;
|
|
45
|
-
};
|
|
46
|
-
min?: number;
|
|
47
|
-
grid: {
|
|
48
|
-
enabled: boolean;
|
|
49
|
-
};
|
|
50
|
-
maxPadding: number;
|
|
51
|
-
ticks: {
|
|
52
|
-
pixelInterval?: number;
|
|
53
|
-
};
|
|
54
|
-
tickMarks: import("../../hooks").PreparedAxisTickMarks;
|
|
55
|
-
position: "left" | "right" | "top" | "bottom";
|
|
56
|
-
plotIndex: number;
|
|
57
|
-
plotLines: import("../../hooks").PreparedAxisPlotLine[];
|
|
58
|
-
plotBands: import("../../hooks").PreparedAxisPlotBand[];
|
|
59
|
-
crosshair: Required<import("../..").AxisCrosshair>;
|
|
60
|
-
})[];
|
|
24
|
+
yAxis: PreparedYAxis[];
|
|
61
25
|
shapesData: ShapeData[];
|
|
62
26
|
shapesReady: boolean;
|
|
63
27
|
handleLegendItemClick: OnLegendItemClick;
|
|
@@ -5,40 +5,4 @@ export declare function recalculateYAxisLabelsWidth(props: {
|
|
|
5
5
|
seriesData: PreparedSeries[];
|
|
6
6
|
yAxis: PreparedYAxis[];
|
|
7
7
|
yScale?: (ChartScale | undefined)[];
|
|
8
|
-
}): Promise<
|
|
9
|
-
type: import("../../..").ChartAxisType;
|
|
10
|
-
labels: Omit<import("../../..").ChartAxisLabels, "style" | "enabled" | "padding" | "autoRotation"> & Required<Pick<import("../../..").ChartAxisLabels, "margin" | "enabled" | "html" | "rotation" | "padding">> & {
|
|
11
|
-
style: import("../../..").BaseTextStyle;
|
|
12
|
-
rotation: number;
|
|
13
|
-
height: number;
|
|
14
|
-
width: number;
|
|
15
|
-
lineHeight: number;
|
|
16
|
-
maxWidth: number;
|
|
17
|
-
};
|
|
18
|
-
title: {
|
|
19
|
-
height: number;
|
|
20
|
-
width: number;
|
|
21
|
-
text: string;
|
|
22
|
-
margin: number;
|
|
23
|
-
style: import("../../..").BaseTextStyle;
|
|
24
|
-
align: import("../../..").ChartAxisTitleAlignment;
|
|
25
|
-
maxRowCount: number;
|
|
26
|
-
rotation: number;
|
|
27
|
-
maxWidth: number;
|
|
28
|
-
html: boolean;
|
|
29
|
-
};
|
|
30
|
-
min?: number;
|
|
31
|
-
grid: {
|
|
32
|
-
enabled: boolean;
|
|
33
|
-
};
|
|
34
|
-
maxPadding: number;
|
|
35
|
-
ticks: {
|
|
36
|
-
pixelInterval?: number;
|
|
37
|
-
};
|
|
38
|
-
tickMarks: import("../../../hooks").PreparedAxisTickMarks;
|
|
39
|
-
position: "left" | "right" | "top" | "bottom";
|
|
40
|
-
plotIndex: number;
|
|
41
|
-
plotLines: import("../../../hooks").PreparedAxisPlotLine[];
|
|
42
|
-
plotBands: import("../../../hooks").PreparedAxisPlotBand[];
|
|
43
|
-
crosshair: Required<import("../../..").AxisCrosshair>;
|
|
44
|
-
})[]>;
|
|
8
|
+
}): Promise<PreparedYAxis[]>;
|
|
@@ -35,6 +35,12 @@ export declare function getPreparedAggregation(args: {
|
|
|
35
35
|
xAxis?: ChartXAxis | null;
|
|
36
36
|
yAxis?: ChartYAxis;
|
|
37
37
|
}): ChartTooltipTotalsBuiltInAggregation | (() => ChartTooltipTotalsAggregationValue);
|
|
38
|
+
export declare function getSortedHovered(args: {
|
|
39
|
+
hovered: TooltipDataChunk[];
|
|
40
|
+
sorting?: ChartTooltip['sorting'];
|
|
41
|
+
xAxis?: ChartXAxis | null;
|
|
42
|
+
yAxis?: ChartYAxis;
|
|
43
|
+
}): TooltipDataChunk[];
|
|
38
44
|
export declare function getTooltipRowColorSymbol({ series, color, height, width, }: {
|
|
39
45
|
color?: string;
|
|
40
46
|
series?: TooltipDataChunk['series'];
|
|
@@ -130,6 +130,47 @@ export function getPreparedAggregation(args) {
|
|
|
130
130
|
}
|
|
131
131
|
return 'sum';
|
|
132
132
|
}
|
|
133
|
+
export function getSortedHovered(args) {
|
|
134
|
+
var _a;
|
|
135
|
+
const { hovered, sorting, xAxis, yAxis } = args;
|
|
136
|
+
if (!sorting) {
|
|
137
|
+
return hovered;
|
|
138
|
+
}
|
|
139
|
+
if (typeof sorting === 'function') {
|
|
140
|
+
return [...hovered].sort(sorting);
|
|
141
|
+
}
|
|
142
|
+
switch (sorting.key) {
|
|
143
|
+
case 'value': {
|
|
144
|
+
const values = getHoveredValues({ hovered, xAxis, yAxis });
|
|
145
|
+
const direction = (_a = sorting.direction) !== null && _a !== void 0 ? _a : 'asc';
|
|
146
|
+
const compareValue = (a, b) => {
|
|
147
|
+
const aNil = a === null || a === undefined;
|
|
148
|
+
const bNil = b === null || b === undefined;
|
|
149
|
+
if (aNil && bNil) {
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
if (aNil) {
|
|
153
|
+
return -1;
|
|
154
|
+
}
|
|
155
|
+
if (bNil) {
|
|
156
|
+
return 1;
|
|
157
|
+
}
|
|
158
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
159
|
+
return a - b;
|
|
160
|
+
}
|
|
161
|
+
return String(a).localeCompare(String(b));
|
|
162
|
+
};
|
|
163
|
+
const indices = hovered.map((_, i) => i);
|
|
164
|
+
indices.sort((i, j) => direction === 'asc'
|
|
165
|
+
? compareValue(values[i], values[j])
|
|
166
|
+
: compareValue(values[j], values[i]));
|
|
167
|
+
return indices.map((i) => hovered[i]);
|
|
168
|
+
}
|
|
169
|
+
default: {
|
|
170
|
+
return hovered;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
133
174
|
export function getTooltipRowColorSymbol({ series, color, height = 8, width = 16, }) {
|
|
134
175
|
if ((series === null || series === void 0 ? void 0 : series.type) === 'line') {
|
|
135
176
|
const colorSymbol = create('svg').attr('height', height).attr('width', width);
|
|
@@ -10,6 +10,8 @@ export const Tooltip = (props) => {
|
|
|
10
10
|
const { hovered, hoveredPlotLines, hoveredPlotBands, pointerPosition } = useTooltip({
|
|
11
11
|
dispatcher,
|
|
12
12
|
tooltip,
|
|
13
|
+
xAxis,
|
|
14
|
+
yAxis,
|
|
13
15
|
});
|
|
14
16
|
const containerRect = (svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) || { left: 0, top: 0 };
|
|
15
17
|
const left = ((pointerPosition === null || pointerPosition === void 0 ? void 0 : pointerPosition[0]) || 0) + containerRect.left;
|
|
@@ -16,43 +16,7 @@ interface UseAxesProps {
|
|
|
16
16
|
}
|
|
17
17
|
export declare function getAxes(props: UseAxesProps): Promise<{
|
|
18
18
|
xAxis: import("./types").PreparedXAxis | null;
|
|
19
|
-
yAxis:
|
|
20
|
-
type: import("../..").ChartAxisType;
|
|
21
|
-
labels: Omit<import("../..").ChartAxisLabels, "style" | "enabled" | "padding" | "autoRotation"> & Required<Pick<import("../..").ChartAxisLabels, "margin" | "enabled" | "html" | "rotation" | "padding">> & {
|
|
22
|
-
style: import("../..").BaseTextStyle;
|
|
23
|
-
rotation: number;
|
|
24
|
-
height: number;
|
|
25
|
-
width: number;
|
|
26
|
-
lineHeight: number;
|
|
27
|
-
maxWidth: number;
|
|
28
|
-
};
|
|
29
|
-
title: {
|
|
30
|
-
height: number;
|
|
31
|
-
width: number;
|
|
32
|
-
text: string;
|
|
33
|
-
margin: number;
|
|
34
|
-
style: import("../..").BaseTextStyle;
|
|
35
|
-
align: import("../..").ChartAxisTitleAlignment;
|
|
36
|
-
maxRowCount: number;
|
|
37
|
-
rotation: number;
|
|
38
|
-
maxWidth: number;
|
|
39
|
-
html: boolean;
|
|
40
|
-
};
|
|
41
|
-
min?: number;
|
|
42
|
-
grid: {
|
|
43
|
-
enabled: boolean;
|
|
44
|
-
};
|
|
45
|
-
maxPadding: number;
|
|
46
|
-
ticks: {
|
|
47
|
-
pixelInterval?: number;
|
|
48
|
-
};
|
|
49
|
-
tickMarks: import("./types").PreparedAxisTickMarks;
|
|
50
|
-
position: "left" | "right" | "top" | "bottom";
|
|
51
|
-
plotIndex: number;
|
|
52
|
-
plotLines: import("./types").PreparedAxisPlotLine[];
|
|
53
|
-
plotBands: import("./types").PreparedAxisPlotBand[];
|
|
54
|
-
crosshair: Required<import("../..").AxisCrosshair>;
|
|
55
|
-
})[];
|
|
19
|
+
yAxis: import("./types").PreparedYAxis[];
|
|
56
20
|
}>;
|
|
57
21
|
export declare function useAxis(props: UseAxesProps): AxesState;
|
|
58
22
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DashStyle } from '../../constants';
|
|
2
|
-
import type { AxisCrosshair, AxisPlotBand, BaseTextStyle, ChartAxis, ChartAxisLabels, ChartAxisRangeSlider, ChartAxisTitleAlignment, ChartAxisType, DeepRequired, MeaningfulAny, PlotLayerPlacement } from '../../types';
|
|
2
|
+
import type { AxisCrosshair, AxisPlotBand, BaseTextStyle, ChartAxis, ChartAxisLabels, ChartAxisRangeSlider, ChartAxisTitleAlignment, ChartAxisTitleRotation, ChartAxisType, DeepRequired, MeaningfulAny, PlotLayerPlacement } from '../../types';
|
|
3
3
|
type PreparedAxisLabels = Omit<ChartAxisLabels, 'enabled' | 'padding' | 'style' | 'autoRotation'> & Required<Pick<ChartAxisLabels, 'enabled' | 'padding' | 'margin' | 'rotation' | 'html'>> & {
|
|
4
4
|
style: BaseTextStyle;
|
|
5
5
|
rotation: number;
|
|
@@ -51,7 +51,7 @@ type PreparedBaseAxis = Omit<ChartAxis, 'type' | 'labels' | 'plotLines' | 'plotB
|
|
|
51
51
|
style: BaseTextStyle;
|
|
52
52
|
align: ChartAxisTitleAlignment;
|
|
53
53
|
maxRowCount: number;
|
|
54
|
-
rotation:
|
|
54
|
+
rotation: ChartAxisTitleRotation;
|
|
55
55
|
maxWidth: number;
|
|
56
56
|
html: boolean;
|
|
57
57
|
};
|
|
@@ -64,7 +64,6 @@ type PreparedBaseAxis = Omit<ChartAxis, 'type' | 'labels' | 'plotLines' | 'plotB
|
|
|
64
64
|
pixelInterval?: number;
|
|
65
65
|
};
|
|
66
66
|
tickMarks: PreparedAxisTickMarks;
|
|
67
|
-
position: 'left' | 'right' | 'top' | 'bottom';
|
|
68
67
|
plotIndex: number;
|
|
69
68
|
plotLines: PreparedAxisPlotLine[];
|
|
70
69
|
plotBands: PreparedAxisPlotBand[];
|
|
@@ -72,8 +71,11 @@ type PreparedBaseAxis = Omit<ChartAxis, 'type' | 'labels' | 'plotLines' | 'plotB
|
|
|
72
71
|
};
|
|
73
72
|
export type PreparedXAxis = PreparedBaseAxis & {
|
|
74
73
|
rangeSlider: PreparedRangeSlider;
|
|
74
|
+
position: 'bottom';
|
|
75
|
+
};
|
|
76
|
+
export type PreparedYAxis = PreparedBaseAxis & {
|
|
77
|
+
position: 'left' | 'right';
|
|
75
78
|
};
|
|
76
|
-
export type PreparedYAxis = PreparedBaseAxis;
|
|
77
79
|
export type PreparedAxis = PreparedXAxis | PreparedYAxis;
|
|
78
80
|
export type AxesState = {
|
|
79
81
|
xAxis: PreparedXAxis | null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PreparedAxis, PreparedSeries, PreparedSplit, RangeSliderState, ZoomState } from '../../hooks';
|
|
1
|
+
import type { PreparedAxis, PreparedSeries, PreparedSplit, PreparedYAxis, RangeSliderState, ZoomState } from '../../hooks';
|
|
2
2
|
import type { ChartScale } from './types';
|
|
3
3
|
export { createXScale } from './x-scale';
|
|
4
4
|
export { createYScale } from './y-scale';
|
|
@@ -7,7 +7,7 @@ type Args = {
|
|
|
7
7
|
boundsHeight: number;
|
|
8
8
|
series: PreparedSeries[];
|
|
9
9
|
xAxis: PreparedAxis | null;
|
|
10
|
-
yAxis:
|
|
10
|
+
yAxis: PreparedYAxis[];
|
|
11
11
|
split: PreparedSplit;
|
|
12
12
|
isRangeSlider?: boolean;
|
|
13
13
|
rangeSliderState?: RangeSliderState;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { line, select } from 'd3';
|
|
3
3
|
import { getAxisPlotsPosition, getLineDashArray } from '../../utils';
|
|
4
|
+
import { getXValue, getYValue } from '../useShapes/utils';
|
|
4
5
|
import { useCrosshairHover } from './useCrosshairHover';
|
|
5
6
|
export const useCrosshair = (props) => {
|
|
6
7
|
var _a, _b;
|
|
@@ -29,15 +30,18 @@ export const useCrosshair = (props) => {
|
|
|
29
30
|
crosshairSelection
|
|
30
31
|
.append('path')
|
|
31
32
|
.attr('d', (hoveredElement) => {
|
|
32
|
-
var _a, _b, _c;
|
|
33
33
|
let lineValue = 0;
|
|
34
|
-
if (xAxis.crosshair.snap
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
if (xAxis.crosshair.snap &&
|
|
35
|
+
typeof hoveredElement.data === 'object' &&
|
|
36
|
+
'x' in hoveredElement.data) {
|
|
37
|
+
const xVal = getXValue({
|
|
38
|
+
point: hoveredElement.data,
|
|
39
|
+
xAxis,
|
|
40
|
+
xScale: xAxisScale,
|
|
41
|
+
});
|
|
42
|
+
lineValue = xVal !== null && !Number.isNaN(xVal) ? xVal : lineValue;
|
|
39
43
|
}
|
|
40
|
-
else {
|
|
44
|
+
else if (!xAxis.crosshair.snap) {
|
|
41
45
|
lineValue = pointerXPos - boundsOffsetLeft;
|
|
42
46
|
}
|
|
43
47
|
const points = [
|
|
@@ -89,16 +93,18 @@ export const useCrosshair = (props) => {
|
|
|
89
93
|
crosshairSelection
|
|
90
94
|
.append('path')
|
|
91
95
|
.attr('d', (hoveredElement) => {
|
|
92
|
-
var _a, _b, _c;
|
|
93
96
|
let lineValue = 0;
|
|
94
|
-
if (yAxis.crosshair.snap
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
if (yAxis.crosshair.snap &&
|
|
98
|
+
typeof hoveredElement.data === 'object' &&
|
|
99
|
+
'y' in hoveredElement.data) {
|
|
100
|
+
const yVal = getYValue({
|
|
101
|
+
point: hoveredElement.data,
|
|
102
|
+
yAxis,
|
|
103
|
+
yScale: yAxisScale,
|
|
104
|
+
});
|
|
105
|
+
lineValue = yVal !== null && !Number.isNaN(yVal) ? yVal : lineValue;
|
|
100
106
|
}
|
|
101
|
-
else {
|
|
107
|
+
else if (!yAxis.crosshair.snap) {
|
|
102
108
|
lineValue = pointerYPos - boundsOffsetTop;
|
|
103
109
|
}
|
|
104
110
|
const points = [
|
|
@@ -178,7 +178,7 @@ export const prepareBarXData = async (args) => {
|
|
|
178
178
|
}
|
|
179
179
|
if (series.some((s) => s.stacking === 'percent')) {
|
|
180
180
|
let acc = 0;
|
|
181
|
-
const ratio = plotHeight / (positiveStackHeight - stackItems.length);
|
|
181
|
+
const ratio = plotHeight / (positiveStackHeight - (stackItems.length - 1) * stackGap);
|
|
182
182
|
stackItems.forEach((item) => {
|
|
183
183
|
item.height = item.height * ratio;
|
|
184
184
|
item.y = plotHeight - item.height - acc;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { Dispatch } from 'd3';
|
|
2
2
|
import type { AxisPlotBand, AxisPlotLine, PointPosition, TooltipDataChunk } from '../../types';
|
|
3
3
|
import type { PreparedTooltip } from '../types';
|
|
4
|
+
import type { PreparedXAxis, PreparedYAxis } from '../useAxis/types';
|
|
4
5
|
type Args = {
|
|
5
6
|
dispatcher: Dispatch<object>;
|
|
6
7
|
tooltip: PreparedTooltip;
|
|
8
|
+
xAxis?: PreparedXAxis | null;
|
|
9
|
+
yAxis?: PreparedYAxis;
|
|
7
10
|
};
|
|
8
|
-
export declare const useTooltip: ({ dispatcher, tooltip }: Args) => {
|
|
11
|
+
export declare const useTooltip: ({ dispatcher, tooltip, xAxis, yAxis }: Args) => {
|
|
9
12
|
hovered: TooltipDataChunk[] | undefined;
|
|
10
13
|
hoveredPlotLines: AxisPlotLine[] | undefined;
|
|
11
14
|
hoveredPlotBands: AxisPlotBand[] | undefined;
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import isEqual from 'lodash/isEqual';
|
|
3
|
-
|
|
3
|
+
import { getSortedHovered } from '../../components/Tooltip/DefaultTooltipContent/utils';
|
|
4
|
+
export const useTooltip = ({ dispatcher, tooltip, xAxis, yAxis }) => {
|
|
4
5
|
const [{ hovered, hoveredPlotLines, hoveredPlotBands, pointerPosition }, setTooltipState] = React.useState({});
|
|
5
6
|
const prevHovered = React.useRef(hovered);
|
|
6
7
|
React.useEffect(() => {
|
|
7
8
|
if (tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabled) {
|
|
8
9
|
dispatcher.on('hover-shape.tooltip', (nextHovered, nextPointerPosition, nextHoveredPlots) => {
|
|
9
10
|
const filteredNextHovered = nextHovered === null || nextHovered === void 0 ? void 0 : nextHovered.filter((item) => 'y' in item.data ? item.data.y !== null : true);
|
|
10
|
-
const
|
|
11
|
+
const sortedHovered = getSortedHovered({
|
|
12
|
+
hovered: filteredNextHovered !== null && filteredNextHovered !== void 0 ? filteredNextHovered : [],
|
|
13
|
+
sorting: tooltip === null || tooltip === void 0 ? void 0 : tooltip.sorting,
|
|
14
|
+
xAxis,
|
|
15
|
+
yAxis,
|
|
16
|
+
});
|
|
17
|
+
const isHoveredChanged = !isEqual(prevHovered.current, sortedHovered);
|
|
11
18
|
const newTooltipState = {
|
|
12
|
-
hovered: isHoveredChanged ?
|
|
19
|
+
hovered: isHoveredChanged ? sortedHovered : prevHovered.current,
|
|
13
20
|
hoveredPlotLines: nextHoveredPlots === null || nextHoveredPlots === void 0 ? void 0 : nextHoveredPlots.lines,
|
|
14
21
|
hoveredPlotBands: nextHoveredPlots === null || nextHoveredPlots === void 0 ? void 0 : nextHoveredPlots.bands,
|
|
15
22
|
pointerPosition: nextPointerPosition,
|
|
16
23
|
};
|
|
17
24
|
if (isHoveredChanged) {
|
|
18
|
-
prevHovered.current =
|
|
25
|
+
prevHovered.current = sortedHovered;
|
|
19
26
|
}
|
|
20
27
|
setTooltipState(newTooltipState);
|
|
21
28
|
});
|
|
@@ -25,7 +32,7 @@ export const useTooltip = ({ dispatcher, tooltip }) => {
|
|
|
25
32
|
dispatcher.on('hover-shape.tooltip', null);
|
|
26
33
|
}
|
|
27
34
|
};
|
|
28
|
-
}, [dispatcher, tooltip]);
|
|
35
|
+
}, [dispatcher, tooltip, xAxis, yAxis]);
|
|
29
36
|
return {
|
|
30
37
|
hovered,
|
|
31
38
|
hoveredPlotLines,
|
|
@@ -6,6 +6,7 @@ import type { BaseTextStyle } from './base';
|
|
|
6
6
|
import type { ChartBrush } from './brush';
|
|
7
7
|
export type ChartAxisType = (typeof AXIS_TYPE)[keyof typeof AXIS_TYPE];
|
|
8
8
|
export type ChartAxisTitleAlignment = 'left' | 'center' | 'right';
|
|
9
|
+
export type ChartAxisTitleRotation = 0 | 90 | -90;
|
|
9
10
|
export interface ChartAxisLabels {
|
|
10
11
|
/** Enable or disable the axis labels. */
|
|
11
12
|
enabled?: boolean;
|
|
@@ -314,7 +315,7 @@ export interface ChartYAxisTitle extends ChartAxisTitle {
|
|
|
314
315
|
*
|
|
315
316
|
* The default values are -90 for the left axis and 90 for the right.
|
|
316
317
|
*/
|
|
317
|
-
rotation?:
|
|
318
|
+
rotation?: ChartAxisTitleRotation;
|
|
318
319
|
/**
|
|
319
320
|
* Interval of the tick marks(absolute or relative to the chart area).
|
|
320
321
|
*
|
|
@@ -115,6 +115,7 @@ export type ChartTooltipRowRendererArgs = {
|
|
|
115
115
|
hovered?: TooltipDataChunk<unknown>[];
|
|
116
116
|
className?: string;
|
|
117
117
|
};
|
|
118
|
+
export type ChartTooltipSortComparator<T = MeaningfulAny> = (a: TooltipDataChunk<T>, b: TooltipDataChunk<T>) => number;
|
|
118
119
|
export interface ChartTooltip<T = MeaningfulAny> {
|
|
119
120
|
enabled?: boolean;
|
|
120
121
|
/** Specifies the renderer for the tooltip. If returned null default tooltip renderer will be used. */
|
|
@@ -158,4 +159,23 @@ export interface ChartTooltip<T = MeaningfulAny> {
|
|
|
158
159
|
* It is assigned as a data-qa attribute to an element.
|
|
159
160
|
*/
|
|
160
161
|
qa?: string;
|
|
162
|
+
/**
|
|
163
|
+
* Controls the order of tooltip rows. Applied to `hovered` before rendering.
|
|
164
|
+
* Use a custom comparator `(a, b) => number` for arbitrary ordering.
|
|
165
|
+
*/
|
|
166
|
+
sorting?: {
|
|
167
|
+
/**
|
|
168
|
+
* Determines what data should be used to sort by.
|
|
169
|
+
* `'value'` uses the numeric value of each series point: `y` for most series
|
|
170
|
+
* (line, area, bar-x, scatter, waterfall), `x` for bar-y, and `value` for
|
|
171
|
+
* pie, radar, heatmap, treemap, funnel. `null` values are sorted as lowest.
|
|
172
|
+
* @default undefined (sorting disabled)
|
|
173
|
+
*/
|
|
174
|
+
key?: 'value' | undefined;
|
|
175
|
+
/**
|
|
176
|
+
* Sorting direction.
|
|
177
|
+
* @default 'asc'
|
|
178
|
+
*/
|
|
179
|
+
direction?: 'asc' | 'desc';
|
|
180
|
+
} | ChartTooltipSortComparator<T>;
|
|
161
181
|
}
|
|
@@ -50,9 +50,9 @@ export function getZoomedSeriesData(args) {
|
|
|
50
50
|
}
|
|
51
51
|
const zoomedSeriesData = [];
|
|
52
52
|
const zoomedShapesSeriesData = [];
|
|
53
|
-
let prevPointInRange = false;
|
|
54
|
-
let currentPointInRange = false;
|
|
55
53
|
seriesData.forEach((seriesItem) => {
|
|
54
|
+
let prevPointInRange = false;
|
|
55
|
+
let currentPointInRange = false;
|
|
56
56
|
const filteredData = [];
|
|
57
57
|
const filteredShapesData = SERIES_TYPE_WITH_HIDDEN_POINTS.includes(seriesItem.type) && (xAxis === null || xAxis === void 0 ? void 0 : xAxis.type) !== 'category'
|
|
58
58
|
? []
|