@gravity-ui/charts 1.39.1 → 1.41.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/README.md +4 -24
- 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/Row.js +4 -4
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/RowWithAggregation.js +3 -2
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/index.js +128 -118
- 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/components/Tooltip/styles.css +33 -7
- package/dist/cjs/components/index.d.ts +4 -1
- package/dist/cjs/components/index.js +8 -3
- 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/useShapes/bar-x/prepare-data.js +3 -2
- package/dist/cjs/hooks/useShapes/bar-x/types.d.ts +5 -0
- 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 +44 -1
- 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/Row.js +4 -4
- package/dist/esm/components/Tooltip/DefaultTooltipContent/RowWithAggregation.js +3 -2
- package/dist/esm/components/Tooltip/DefaultTooltipContent/index.js +128 -118
- 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/components/Tooltip/styles.css +33 -7
- package/dist/esm/components/index.d.ts +4 -1
- package/dist/esm/components/index.js +8 -3
- 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/useShapes/bar-x/prepare-data.js +3 -2
- package/dist/esm/hooks/useShapes/bar-x/types.d.ts +5 -0
- 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 +44 -1
- package/dist/esm/utils/chart/zoom.js +2 -2
- package/package.json +2 -1
|
@@ -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;
|
|
@@ -162,6 +162,7 @@ export const prepareBarXData = async (args) => {
|
|
|
162
162
|
: yAxisTop + base + negativeStackHeight,
|
|
163
163
|
width: rectWidth,
|
|
164
164
|
height: shapeHeight,
|
|
165
|
+
_height: height,
|
|
165
166
|
opacity: get(yValue.data, 'opacity', null),
|
|
166
167
|
data: yValue.data,
|
|
167
168
|
series: yValue.series,
|
|
@@ -178,9 +179,9 @@ export const prepareBarXData = async (args) => {
|
|
|
178
179
|
}
|
|
179
180
|
if (series.some((s) => s.stacking === 'percent')) {
|
|
180
181
|
let acc = 0;
|
|
181
|
-
const ratio = plotHeight /
|
|
182
|
+
const ratio = plotHeight / positiveStackHeight;
|
|
182
183
|
stackItems.forEach((item) => {
|
|
183
|
-
item.height = item.
|
|
184
|
+
item.height = item._height * ratio;
|
|
184
185
|
item.y = plotHeight - item.height - acc;
|
|
185
186
|
acc += item.height + 1;
|
|
186
187
|
});
|
|
@@ -10,4 +10,9 @@ export type PreparedBarXData = Omit<TooltipDataChunkBarX, 'series'> & {
|
|
|
10
10
|
label?: LabelData;
|
|
11
11
|
htmlElements: HtmlItem[];
|
|
12
12
|
isLastStackItem: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* the utility field for storing the original height (for recalculations, etc.)
|
|
15
|
+
* should not be used for displaying
|
|
16
|
+
*/
|
|
17
|
+
_height: number;
|
|
13
18
|
};
|
|
@@ -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
|
*
|
|
@@ -113,8 +113,13 @@ export type ChartTooltipRowRendererArgs = {
|
|
|
113
113
|
value: string | number | null | undefined;
|
|
114
114
|
formattedValue?: string;
|
|
115
115
|
hovered?: TooltipDataChunk<unknown>[];
|
|
116
|
+
/**
|
|
117
|
+
* CSS class name pre-built with active/striped modifiers.
|
|
118
|
+
* Apply it to the root `<tr>` element of the returned row: `<tr className={className}>`.
|
|
119
|
+
*/
|
|
116
120
|
className?: string;
|
|
117
121
|
};
|
|
122
|
+
export type ChartTooltipSortComparator<T = MeaningfulAny> = (a: TooltipDataChunk<T>, b: TooltipDataChunk<T>) => number;
|
|
118
123
|
export interface ChartTooltip<T = MeaningfulAny> {
|
|
119
124
|
enabled?: boolean;
|
|
120
125
|
/** Specifies the renderer for the tooltip. If returned null default tooltip renderer will be used. */
|
|
@@ -122,7 +127,26 @@ export interface ChartTooltip<T = MeaningfulAny> {
|
|
|
122
127
|
/**
|
|
123
128
|
* Defines the way a single data/series is displayed (corresponding to a separate selected point/ruler/shape on the chart).
|
|
124
129
|
* It is useful in cases where you need to display additional information, but keep the general format of the tooltip.
|
|
125
|
-
*
|
|
130
|
+
*
|
|
131
|
+
* The returned React element must be a `<tr>` so that it fits into the table layout used by the tooltip.
|
|
132
|
+
* Apply the `className` arg to the root `<tr>` to get the correct active/striped styles.
|
|
133
|
+
*
|
|
134
|
+
* If a string is returned, it will be parsed as HTML and rendered as-is — the string must be a complete
|
|
135
|
+
* `<tr>...</tr>` element.
|
|
136
|
+
* @example React element
|
|
137
|
+
* ```tsx
|
|
138
|
+
* rowRenderer: ({id, name, value, className}) => (
|
|
139
|
+
* <tr key={id} className={className}>
|
|
140
|
+
* <td>{name}</td>
|
|
141
|
+
* <td>{value}</td>
|
|
142
|
+
* </tr>
|
|
143
|
+
* )
|
|
144
|
+
* ```
|
|
145
|
+
* @example Raw HTML string
|
|
146
|
+
* ```ts
|
|
147
|
+
* rowRenderer: ({name, value, className}) =>
|
|
148
|
+
* `<tr class="${className}"><td>${name}</td><td>${value}</td></tr>`
|
|
149
|
+
* ```
|
|
126
150
|
*/
|
|
127
151
|
rowRenderer?: ((args: ChartTooltipRowRendererArgs) => React.ReactElement | string) | null;
|
|
128
152
|
pin?: {
|
|
@@ -158,4 +182,23 @@ export interface ChartTooltip<T = MeaningfulAny> {
|
|
|
158
182
|
* It is assigned as a data-qa attribute to an element.
|
|
159
183
|
*/
|
|
160
184
|
qa?: string;
|
|
185
|
+
/**
|
|
186
|
+
* Controls the order of tooltip rows. Applied to `hovered` before rendering.
|
|
187
|
+
* Use a custom comparator `(a, b) => number` for arbitrary ordering.
|
|
188
|
+
*/
|
|
189
|
+
sorting?: {
|
|
190
|
+
/**
|
|
191
|
+
* Determines what data should be used to sort by.
|
|
192
|
+
* `'value'` uses the numeric value of each series point: `y` for most series
|
|
193
|
+
* (line, area, bar-x, scatter, waterfall), `x` for bar-y, and `value` for
|
|
194
|
+
* pie, radar, heatmap, treemap, funnel. `null` values are sorted as lowest.
|
|
195
|
+
* @default undefined (sorting disabled)
|
|
196
|
+
*/
|
|
197
|
+
key?: 'value' | undefined;
|
|
198
|
+
/**
|
|
199
|
+
* Sorting direction.
|
|
200
|
+
* @default 'asc'
|
|
201
|
+
*/
|
|
202
|
+
direction?: 'asc' | 'desc';
|
|
203
|
+
} | ChartTooltipSortComparator<T>;
|
|
161
204
|
}
|
|
@@ -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[]>;
|
|
@@ -12,8 +12,8 @@ export function Row(props) {
|
|
|
12
12
|
}
|
|
13
13
|
return null;
|
|
14
14
|
}, [color, colorSymbol]);
|
|
15
|
-
return (React.createElement("
|
|
16
|
-
colorItem,
|
|
17
|
-
React.createElement("
|
|
18
|
-
value && React.createElement("
|
|
15
|
+
return (React.createElement("tr", { className: b('content-row', { active, striped }, className), style: style },
|
|
16
|
+
colorItem && React.createElement("td", { className: b('content-row-color-cell') }, colorItem),
|
|
17
|
+
React.createElement("td", { className: b('content-row-label-cell') }, label),
|
|
18
|
+
value && React.createElement("td", { className: b('content-row-value-cell') }, value)));
|
|
19
19
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { block } from '../../../utils';
|
|
3
3
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
4
|
-
import { Row } from './Row';
|
|
5
4
|
import { getBuiltInAggregatedValue, getBuiltInAggregationLabel } from './utils';
|
|
6
5
|
const b = block('tooltip');
|
|
7
6
|
export function RowWithAggregation(props) {
|
|
@@ -19,5 +18,7 @@ export function RowWithAggregation(props) {
|
|
|
19
18
|
format: valueFormat || { type: 'number' },
|
|
20
19
|
})
|
|
21
20
|
: resultValue;
|
|
22
|
-
return (React.createElement(
|
|
21
|
+
return (React.createElement("div", { className: b('content-row', { totals: true }), style: style },
|
|
22
|
+
React.createElement("span", { className: b('content-row-totals-label') }, resultLabel),
|
|
23
|
+
formattedResultValue && (React.createElement("span", { className: b('content-row-totals-value') }, formattedResultValue))));
|
|
23
24
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Divider } from '@gravity-ui/uikit';
|
|
3
|
+
import parse from 'html-react-parser';
|
|
3
4
|
import get from 'lodash/get';
|
|
4
5
|
import isEqual from 'lodash/isEqual';
|
|
5
6
|
import { usePrevious } from '../../../hooks';
|
|
@@ -35,7 +36,7 @@ export const DefaultTooltipContent = ({ hovered, pinned, rowRenderer, totals, va
|
|
|
35
36
|
hovered,
|
|
36
37
|
});
|
|
37
38
|
if (typeof result === 'string') {
|
|
38
|
-
return React.createElement(
|
|
39
|
+
return React.createElement(React.Fragment, { key: id }, parse(result));
|
|
39
40
|
}
|
|
40
41
|
return result;
|
|
41
42
|
}
|
|
@@ -75,127 +76,136 @@ export const DefaultTooltipContent = ({ hovered, pinned, rowRenderer, totals, va
|
|
|
75
76
|
setScrollBarWidth(0);
|
|
76
77
|
}
|
|
77
78
|
}, [pinned]);
|
|
79
|
+
const rowsContent = (React.createElement(React.Fragment, null, visibleHovered.map((seriesItem, i) => {
|
|
80
|
+
var _a;
|
|
81
|
+
const { data, series, closest } = seriesItem;
|
|
82
|
+
const id = `${get(series, 'id')}_${i}`;
|
|
83
|
+
const color = get(data, 'color') || get(series, 'color');
|
|
84
|
+
// TODO: improve active item display https://github.com/gravity-ui/charts/issues/208
|
|
85
|
+
const active = closest && hovered.length > 1;
|
|
86
|
+
const striped = (i + 1) % 2 === 0;
|
|
87
|
+
const rowValueFormat = get(series, 'tooltip.valueFormat', valueFormat);
|
|
88
|
+
switch (series.type) {
|
|
89
|
+
case 'scatter':
|
|
90
|
+
case 'line':
|
|
91
|
+
case 'area':
|
|
92
|
+
case 'bar-x': {
|
|
93
|
+
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
94
|
+
const formattedValue = getFormattedValue({
|
|
95
|
+
value: hoveredValues[i],
|
|
96
|
+
format,
|
|
97
|
+
});
|
|
98
|
+
return renderRow({
|
|
99
|
+
id,
|
|
100
|
+
active,
|
|
101
|
+
color,
|
|
102
|
+
name: series.name,
|
|
103
|
+
striped,
|
|
104
|
+
value: hoveredValues[i],
|
|
105
|
+
formattedValue,
|
|
106
|
+
series,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
case 'waterfall': {
|
|
110
|
+
const isTotal = get(data, 'total', false);
|
|
111
|
+
const subTotalValue = (_a = seriesItem.subTotal) !== null && _a !== void 0 ? _a : 0;
|
|
112
|
+
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
113
|
+
const subTotal = getFormattedValue({
|
|
114
|
+
value: subTotalValue,
|
|
115
|
+
format,
|
|
116
|
+
});
|
|
117
|
+
const formattedValue = getFormattedValue({
|
|
118
|
+
value: hoveredValues[i],
|
|
119
|
+
format,
|
|
120
|
+
});
|
|
121
|
+
return (React.createElement(React.Fragment, { key: id },
|
|
122
|
+
!isTotal && (React.createElement(React.Fragment, null,
|
|
123
|
+
React.createElement("div", { className: b('series-name') }, getXRowData(data, xAxis)),
|
|
124
|
+
React.createElement(Row, { label: series.name, value: formattedValue }))),
|
|
125
|
+
React.createElement(Row, { label: isTotal ? 'Total' : 'Subtotal', value: subTotal })));
|
|
126
|
+
}
|
|
127
|
+
case 'bar-y': {
|
|
128
|
+
const format = rowValueFormat || getDefaultValueFormat({ axis: xAxis });
|
|
129
|
+
const formattedValue = getFormattedValue({
|
|
130
|
+
value: hoveredValues[i],
|
|
131
|
+
format,
|
|
132
|
+
});
|
|
133
|
+
return renderRow({
|
|
134
|
+
id,
|
|
135
|
+
active,
|
|
136
|
+
color,
|
|
137
|
+
name: series.name,
|
|
138
|
+
striped,
|
|
139
|
+
value: hoveredValues[i],
|
|
140
|
+
formattedValue,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
case 'pie':
|
|
144
|
+
case 'heatmap':
|
|
145
|
+
case 'treemap':
|
|
146
|
+
case 'funnel': {
|
|
147
|
+
const seriesData = data;
|
|
148
|
+
const formattedValue = getFormattedValue({
|
|
149
|
+
value: hoveredValues[i],
|
|
150
|
+
format: rowValueFormat || { type: 'number' },
|
|
151
|
+
});
|
|
152
|
+
return renderRow({
|
|
153
|
+
id,
|
|
154
|
+
color,
|
|
155
|
+
name: [seriesData.name || seriesData.id].flat().join('\n'),
|
|
156
|
+
value: hoveredValues[i],
|
|
157
|
+
formattedValue,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
case 'sankey': {
|
|
161
|
+
const { target, data: source } = seriesItem;
|
|
162
|
+
const formattedValue = getFormattedValue({
|
|
163
|
+
value: hoveredValues[i],
|
|
164
|
+
format: rowValueFormat || { type: 'number' },
|
|
165
|
+
});
|
|
166
|
+
return renderRow({
|
|
167
|
+
id,
|
|
168
|
+
color,
|
|
169
|
+
name: `${source.name} → ${target === null || target === void 0 ? void 0 : target.name}`,
|
|
170
|
+
value: hoveredValues[i],
|
|
171
|
+
formattedValue,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
case 'radar': {
|
|
175
|
+
const radarSeries = series;
|
|
176
|
+
const formattedValue = getFormattedValue({
|
|
177
|
+
value: hoveredValues[i],
|
|
178
|
+
format: rowValueFormat || { type: 'number' },
|
|
179
|
+
});
|
|
180
|
+
return renderRow({
|
|
181
|
+
id,
|
|
182
|
+
color,
|
|
183
|
+
active,
|
|
184
|
+
name: radarSeries.name || radarSeries.id,
|
|
185
|
+
value: hoveredValues[i],
|
|
186
|
+
formattedValue,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
default: {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
})));
|
|
78
194
|
return (React.createElement("div", { className: b('content'), "data-qa": qa },
|
|
79
195
|
formattedHeadValue && (React.createElement("div", { className: b('series-name') },
|
|
80
196
|
React.createElement("div", { className: b('series-name-text'), dangerouslySetInnerHTML: { __html: formattedHeadValue } }))),
|
|
81
197
|
React.createElement("div", { className: b('content-rows', { pinned }), ref: contentRowsRef, style: pinned ? { maxHeight: maxContentRowsHeight } : undefined },
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// TODO: improve action item display https://github.com/gravity-ui/charts/issues/208
|
|
88
|
-
const active = closest && hovered.length > 1;
|
|
89
|
-
const striped = (i + 1) % 2 === 0;
|
|
90
|
-
const rowValueFormat = get(series, 'tooltip.valueFormat', valueFormat);
|
|
91
|
-
switch (series.type) {
|
|
92
|
-
case 'scatter':
|
|
93
|
-
case 'line':
|
|
94
|
-
case 'area':
|
|
95
|
-
case 'bar-x': {
|
|
96
|
-
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
97
|
-
const formattedValue = getFormattedValue({
|
|
98
|
-
value: hoveredValues[i],
|
|
99
|
-
format,
|
|
100
|
-
});
|
|
101
|
-
return renderRow({
|
|
102
|
-
id,
|
|
103
|
-
active,
|
|
104
|
-
color,
|
|
105
|
-
name: series.name,
|
|
106
|
-
striped,
|
|
107
|
-
value: hoveredValues[i],
|
|
108
|
-
formattedValue,
|
|
109
|
-
series,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
case 'waterfall': {
|
|
113
|
-
const isTotal = get(data, 'total', false);
|
|
114
|
-
const subTotalValue = (_a = seriesItem.subTotal) !== null && _a !== void 0 ? _a : 0;
|
|
115
|
-
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
116
|
-
const subTotal = getFormattedValue({
|
|
117
|
-
value: subTotalValue,
|
|
118
|
-
format,
|
|
119
|
-
});
|
|
120
|
-
const formattedValue = getFormattedValue({
|
|
121
|
-
value: hoveredValues[i],
|
|
122
|
-
format,
|
|
123
|
-
});
|
|
124
|
-
return (React.createElement(React.Fragment, { key: id },
|
|
125
|
-
!isTotal && (React.createElement(React.Fragment, null,
|
|
126
|
-
React.createElement("div", { className: b('series-name') }, getXRowData(data, xAxis)),
|
|
127
|
-
React.createElement(Row, { label: series.name, value: formattedValue }))),
|
|
128
|
-
React.createElement(Row, { label: isTotal ? 'Total' : 'Subtotal', value: subTotal })));
|
|
129
|
-
}
|
|
130
|
-
case 'bar-y': {
|
|
131
|
-
const format = rowValueFormat || getDefaultValueFormat({ axis: xAxis });
|
|
132
|
-
const formattedValue = getFormattedValue({
|
|
133
|
-
value: hoveredValues[i],
|
|
134
|
-
format,
|
|
135
|
-
});
|
|
136
|
-
return renderRow({
|
|
137
|
-
id,
|
|
138
|
-
active,
|
|
139
|
-
color,
|
|
140
|
-
name: series.name,
|
|
141
|
-
striped,
|
|
142
|
-
value: hoveredValues[i],
|
|
143
|
-
formattedValue,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
case 'pie':
|
|
147
|
-
case 'heatmap':
|
|
148
|
-
case 'treemap':
|
|
149
|
-
case 'funnel': {
|
|
150
|
-
const seriesData = data;
|
|
151
|
-
const formattedValue = getFormattedValue({
|
|
152
|
-
value: hoveredValues[i],
|
|
153
|
-
format: rowValueFormat || { type: 'number' },
|
|
154
|
-
});
|
|
155
|
-
return renderRow({
|
|
156
|
-
id,
|
|
157
|
-
color,
|
|
158
|
-
name: [seriesData.name || seriesData.id].flat().join('\n'),
|
|
159
|
-
value: hoveredValues[i],
|
|
160
|
-
formattedValue,
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
case 'sankey': {
|
|
164
|
-
const { target, data: source } = seriesItem;
|
|
165
|
-
const formattedValue = getFormattedValue({
|
|
166
|
-
value: hoveredValues[i],
|
|
167
|
-
format: rowValueFormat || { type: 'number' },
|
|
168
|
-
});
|
|
169
|
-
return renderRow({
|
|
170
|
-
id,
|
|
171
|
-
color,
|
|
172
|
-
name: `${source.name} → ${target === null || target === void 0 ? void 0 : target.name}`,
|
|
173
|
-
value: hoveredValues[i],
|
|
174
|
-
formattedValue,
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
case 'radar': {
|
|
178
|
-
const radarSeries = series;
|
|
179
|
-
const formattedValue = getFormattedValue({
|
|
180
|
-
value: hoveredValues[i],
|
|
181
|
-
format: rowValueFormat || { type: 'number' },
|
|
182
|
-
});
|
|
183
|
-
return renderRow({
|
|
184
|
-
id,
|
|
185
|
-
color,
|
|
186
|
-
active,
|
|
187
|
-
name: radarSeries.name || radarSeries.id,
|
|
188
|
-
value: hoveredValues[i],
|
|
189
|
-
formattedValue,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
default: {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}),
|
|
197
|
-
Boolean(restHoveredValues.length) && (React.createElement(Row, { label: i18n('tooltip', 'label_more', { count: restHoveredValues.length }), striped: (visibleHovered.length + 1) % 2 === 0 }))),
|
|
198
|
+
React.createElement("table", { className: b('content-rows-table') },
|
|
199
|
+
React.createElement("tbody", null, rowsContent))),
|
|
200
|
+
Boolean(restHoveredValues.length) && (React.createElement("div", { className: b('content-row', {
|
|
201
|
+
striped: (visibleHovered.length + 1) % 2 === 0,
|
|
202
|
+
}) }, i18n('tooltip', 'label_more', { count: restHoveredValues.length }))),
|
|
198
203
|
(totals === null || totals === void 0 ? void 0 : totals.enabled) && hovered.length > 1 && (React.createElement(React.Fragment, null,
|
|
199
204
|
React.createElement(Divider, { className: b('content-row-totals-divider') }),
|
|
200
|
-
React.createElement(RowWithAggregation, { aggregation: getPreparedAggregation({
|
|
205
|
+
React.createElement(RowWithAggregation, { aggregation: getPreparedAggregation({
|
|
206
|
+
hovered,
|
|
207
|
+
totals,
|
|
208
|
+
xAxis,
|
|
209
|
+
yAxis,
|
|
210
|
+
}), label: totals.label, style: { marginRight: scrollBarWidth }, values: hoveredValues, valueFormat: (_a = totals.valueFormat) !== null && _a !== void 0 ? _a : valueFormat })))));
|
|
201
211
|
};
|
|
@@ -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'];
|