@gravity-ui/charts 1.19.0 → 1.21.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/AxisY/AxisY.js +8 -1
- package/dist/cjs/components/AxisY/prepare-axis-data.js +39 -12
- package/dist/cjs/components/AxisY/types.d.ts +3 -0
- package/dist/cjs/components/ChartInner/index.js +18 -7
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +8 -11
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +39 -86
- package/dist/cjs/components/ChartInner/useChartInnerState.d.ts +4 -2
- package/dist/cjs/components/ChartInner/useChartInnerState.js +9 -0
- package/dist/cjs/components/ChartInner/utils.d.ts +2 -2
- package/dist/cjs/components/ChartInner/utils.js +1 -1
- package/dist/cjs/components/Title/index.d.ts +0 -1
- package/dist/cjs/components/Title/index.js +6 -4
- package/dist/cjs/hooks/index.d.ts +7 -3
- package/dist/cjs/hooks/index.js +7 -3
- package/dist/cjs/hooks/useAxis/index.d.ts +19 -0
- package/dist/cjs/hooks/useAxis/index.js +63 -0
- package/dist/cjs/hooks/useChartOptions/index.d.ts +1 -4
- package/dist/cjs/hooks/useChartOptions/index.js +2 -5
- package/dist/cjs/hooks/useChartOptions/title.js +4 -2
- package/dist/cjs/hooks/useChartOptions/types.d.ts +0 -1
- package/dist/cjs/hooks/useChartOptions/utils.d.ts +1 -4
- package/dist/cjs/hooks/useChartOptions/utils.js +29 -6
- package/dist/cjs/hooks/useChartOptions/x-axis.js +2 -2
- package/dist/cjs/hooks/useChartOptions/y-axis.js +10 -11
- package/dist/cjs/hooks/useNormalizedOriginalData/index.d.ts +40 -0
- package/dist/cjs/hooks/useNormalizedOriginalData/index.js +33 -0
- package/dist/cjs/hooks/useSeries/index.d.ts +0 -9
- package/dist/cjs/hooks/useSeries/index.js +0 -18
- package/dist/cjs/hooks/useSeries/types.d.ts +3 -0
- package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +4 -0
- package/dist/cjs/hooks/useShapes/bar-y/prepare-data.js +4 -0
- package/dist/cjs/hooks/useShapes/scatter/prepare-data.d.ts +2 -2
- package/dist/cjs/hooks/useShapes/scatter/prepare-data.js +40 -5
- package/dist/cjs/i18n/keysets/en.json +2 -1
- package/dist/cjs/i18n/keysets/ru.json +2 -1
- package/dist/cjs/types/chart/axis.d.ts +20 -2
- package/dist/cjs/utils/chart/get-closest-data.js +1 -1
- package/dist/cjs/utils/chart/series/sorting.d.ts +2 -2
- package/dist/cjs/utils/chart/series/sorting.js +3 -3
- package/dist/cjs/utils/chart/zoom.d.ts +7 -6
- package/dist/cjs/utils/chart/zoom.js +14 -6
- package/dist/cjs/validation/validate-axes.js +31 -1
- package/dist/esm/components/AxisY/AxisY.js +8 -1
- package/dist/esm/components/AxisY/prepare-axis-data.js +39 -12
- package/dist/esm/components/AxisY/types.d.ts +3 -0
- package/dist/esm/components/ChartInner/index.js +18 -7
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +7 -10
- package/dist/esm/components/ChartInner/useChartInnerProps.js +39 -86
- package/dist/esm/components/ChartInner/useChartInnerState.d.ts +4 -2
- package/dist/esm/components/ChartInner/useChartInnerState.js +9 -0
- package/dist/esm/components/ChartInner/utils.d.ts +2 -2
- package/dist/esm/components/ChartInner/utils.js +1 -1
- package/dist/esm/components/Title/index.d.ts +0 -1
- package/dist/esm/components/Title/index.js +6 -4
- package/dist/esm/hooks/index.d.ts +7 -3
- package/dist/esm/hooks/index.js +7 -3
- package/dist/esm/hooks/useAxis/index.d.ts +19 -0
- package/dist/esm/hooks/useAxis/index.js +63 -0
- package/dist/esm/hooks/useChartOptions/index.d.ts +1 -4
- package/dist/esm/hooks/useChartOptions/index.js +2 -5
- package/dist/esm/hooks/useChartOptions/title.js +4 -2
- package/dist/esm/hooks/useChartOptions/types.d.ts +0 -1
- package/dist/esm/hooks/useChartOptions/utils.d.ts +1 -4
- package/dist/esm/hooks/useChartOptions/utils.js +29 -6
- package/dist/esm/hooks/useChartOptions/x-axis.js +2 -2
- package/dist/esm/hooks/useChartOptions/y-axis.js +10 -11
- package/dist/esm/hooks/useNormalizedOriginalData/index.d.ts +40 -0
- package/dist/esm/hooks/useNormalizedOriginalData/index.js +33 -0
- package/dist/esm/hooks/useSeries/index.d.ts +0 -9
- package/dist/esm/hooks/useSeries/index.js +0 -18
- package/dist/esm/hooks/useSeries/types.d.ts +3 -0
- package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +4 -0
- package/dist/esm/hooks/useShapes/bar-y/prepare-data.js +4 -0
- package/dist/esm/hooks/useShapes/scatter/prepare-data.d.ts +2 -2
- package/dist/esm/hooks/useShapes/scatter/prepare-data.js +40 -5
- package/dist/esm/i18n/keysets/en.json +2 -1
- package/dist/esm/i18n/keysets/ru.json +2 -1
- package/dist/esm/types/chart/axis.d.ts +20 -2
- package/dist/esm/utils/chart/get-closest-data.js +1 -1
- package/dist/esm/utils/chart/series/sorting.d.ts +2 -2
- package/dist/esm/utils/chart/series/sorting.js +3 -3
- package/dist/esm/utils/chart/zoom.d.ts +7 -6
- package/dist/esm/utils/chart/zoom.js +14 -6
- package/dist/esm/validation/validate-axes.js +31 -1
- package/package.json +7 -16
- package/dist/cjs/components/Title/styles.css +0 -5
- package/dist/esm/components/Title/styles.css +0 -5
|
@@ -67,7 +67,14 @@ export const AxisY = (props) => {
|
|
|
67
67
|
}
|
|
68
68
|
if (tickData.svgLabel) {
|
|
69
69
|
const label = tickData.svgLabel;
|
|
70
|
-
const textSelection = tickSelection
|
|
70
|
+
const textSelection = tickSelection
|
|
71
|
+
.append('text')
|
|
72
|
+
.style('transform', [
|
|
73
|
+
`translate(${label.x}px, ${label.y}px)`,
|
|
74
|
+
label.angle ? `rotate(${label.angle}deg)` : '',
|
|
75
|
+
]
|
|
76
|
+
.filter(Boolean)
|
|
77
|
+
.join(' '));
|
|
71
78
|
if (label.title) {
|
|
72
79
|
textSelection.append('title').html(label.title);
|
|
73
80
|
}
|
|
@@ -9,15 +9,19 @@ async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHei
|
|
|
9
9
|
const labelMaxWidth = axis.labels.width; //axis.labels.maxWidth;
|
|
10
10
|
const size = originalTextSize;
|
|
11
11
|
const content = [];
|
|
12
|
-
//
|
|
13
|
-
|
|
12
|
+
// line breaks in the text are supported only
|
|
13
|
+
// 1. for the category axis - it will look strange for numbers or dates
|
|
14
|
+
// 2. for labels without rotation - it is unclear how to handle long strings correctly
|
|
15
|
+
if (originalTextSize.width > labelMaxWidth &&
|
|
16
|
+
axis.type === 'category' &&
|
|
17
|
+
axis.labels.rotation === 0) {
|
|
14
18
|
const textRows = await wrapText({
|
|
15
19
|
text,
|
|
16
20
|
style: axis.labels.style,
|
|
17
21
|
width: labelMaxWidth,
|
|
18
22
|
getTextSize,
|
|
19
23
|
});
|
|
20
|
-
let labelTopOffset =
|
|
24
|
+
let labelTopOffset = 0;
|
|
21
25
|
let newLabelWidth = 0;
|
|
22
26
|
let newLabelHeight = 0;
|
|
23
27
|
for (let textRowIndex = 0; textRowIndex < textRows.length; textRowIndex++) {
|
|
@@ -43,9 +47,7 @@ async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHei
|
|
|
43
47
|
});
|
|
44
48
|
textSize = await getTextSize(rowText);
|
|
45
49
|
}
|
|
46
|
-
const x = axis.position === 'left'
|
|
47
|
-
? left - textSize.width - axis.labels.margin
|
|
48
|
-
: left + axis.labels.margin;
|
|
50
|
+
const x = axis.position === 'left' ? -textSize.width : 0;
|
|
49
51
|
content.push({
|
|
50
52
|
text: rowText,
|
|
51
53
|
x,
|
|
@@ -62,21 +64,46 @@ async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHei
|
|
|
62
64
|
size.height = newLabelHeight;
|
|
63
65
|
}
|
|
64
66
|
else {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
let rowText = text;
|
|
68
|
+
let textSize = await getTextSize(rowText);
|
|
69
|
+
const textMaxWidth = Math.min(labelMaxWidth / calculateCos(axis.labels.rotation) -
|
|
70
|
+
textSize.height * calculateCos(90 - axis.labels.rotation),
|
|
71
|
+
// for vertical labels, we need to take into account the available height, otherwise there may be intersections
|
|
72
|
+
axis.labels.rotation === 90 ? labelMaxHeight : Infinity,
|
|
73
|
+
// if there is no rotation, then the height of the label does not affect the width of the text
|
|
74
|
+
axis.labels.rotation === 0
|
|
75
|
+
? Infinity
|
|
76
|
+
: (top + topOffset - textSize.height / 2) / calculateSin(axis.labels.rotation));
|
|
77
|
+
if (textSize.width > textMaxWidth) {
|
|
78
|
+
rowText = await getTextWithElipsis({
|
|
79
|
+
text: rowText,
|
|
80
|
+
getTextWidth: async (str) => (await getTextSize(str)).width,
|
|
81
|
+
maxWidth: textMaxWidth,
|
|
82
|
+
});
|
|
83
|
+
textSize = await getTextSize(rowText);
|
|
84
|
+
}
|
|
85
|
+
const actualTextHeight = axis.labels.rotation
|
|
86
|
+
? textSize.height / calculateSin(axis.labels.rotation)
|
|
87
|
+
: textSize.height;
|
|
88
|
+
const x = axis.position === 'left' ? -textSize.width : 0;
|
|
89
|
+
const y = Math.max(-topOffset - top, -actualTextHeight / 2);
|
|
68
90
|
content.push({
|
|
69
|
-
text,
|
|
91
|
+
text: rowText,
|
|
70
92
|
x,
|
|
71
|
-
y
|
|
72
|
-
size,
|
|
93
|
+
y,
|
|
94
|
+
size: textSize,
|
|
73
95
|
});
|
|
74
96
|
}
|
|
97
|
+
const x = axis.position === 'left' ? left - axis.labels.margin : left + axis.labels.margin;
|
|
98
|
+
const y = top;
|
|
75
99
|
const svgLabel = {
|
|
76
100
|
title: content.length > 1 || ((_a = content[0]) === null || _a === void 0 ? void 0 : _a.text) !== text ? text : undefined,
|
|
77
101
|
content: content,
|
|
78
102
|
style: axis.labels.style,
|
|
79
103
|
size: size,
|
|
104
|
+
x,
|
|
105
|
+
y,
|
|
106
|
+
angle: axis.labels.rotation,
|
|
80
107
|
};
|
|
81
108
|
return svgLabel;
|
|
82
109
|
}
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { ArrowRotateLeft } from '@gravity-ui/icons';
|
|
3
3
|
import { Button, ButtonIcon, useUniqId } from '@gravity-ui/uikit';
|
|
4
4
|
import { useCrosshair } from '../../hooks';
|
|
5
|
+
import { getPreparedTooltip } from '../../hooks/useChartOptions/tooltip';
|
|
5
6
|
import { EventType, block, getDispatcher } from '../../utils';
|
|
6
7
|
import { AxisX } from '../AxisX/AxisX';
|
|
7
8
|
import { AxisY } from '../AxisY/AxisY';
|
|
@@ -26,12 +27,22 @@ export const ChartInner = (props) => {
|
|
|
26
27
|
const plotAfterRef = React.useRef(null);
|
|
27
28
|
const dispatcher = React.useMemo(() => getDispatcher(), []);
|
|
28
29
|
const clipPathId = useUniqId();
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const preparedTooltip = React.useMemo(() => {
|
|
31
|
+
return getPreparedTooltip({
|
|
32
|
+
tooltip: data.tooltip,
|
|
33
|
+
seriesData: data.series.data,
|
|
34
|
+
yAxes: data.yAxis,
|
|
35
|
+
xAxis: data.xAxis,
|
|
36
|
+
});
|
|
37
|
+
}, [data.series.data, data.tooltip, data.yAxis, data.xAxis]);
|
|
38
|
+
const { tooltipPinned, togglePinTooltip, unpinTooltip, updateZoomState, zoomState } = useChartInnerState({
|
|
32
39
|
dispatcher,
|
|
33
|
-
tooltip,
|
|
40
|
+
tooltip: preparedTooltip,
|
|
34
41
|
});
|
|
42
|
+
const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, isOutsideBounds, legendConfig, legendItems, preparedSeries, preparedSplit, preparedLegend, prevHeight, prevWidth, shapes, shapesData, title, xAxis, xScale, yAxis, yScale, svgXPos, } = useChartInnerProps(Object.assign(Object.assign({}, props), { clipPathId,
|
|
43
|
+
dispatcher,
|
|
44
|
+
htmlLayout, plotNode: plotRef.current, svgContainer: svgRef.current, updateZoomState,
|
|
45
|
+
zoomState }));
|
|
35
46
|
const { handleChartClick, handleMouseLeave, throttledHandleMouseMove, throttledHandleTouchMove } = useChartInnerHandlers({
|
|
36
47
|
boundsHeight,
|
|
37
48
|
boundsOffsetLeft,
|
|
@@ -45,7 +56,7 @@ export const ChartInner = (props) => {
|
|
|
45
56
|
unpinTooltip,
|
|
46
57
|
xAxis,
|
|
47
58
|
yAxis,
|
|
48
|
-
tooltipThrottle:
|
|
59
|
+
tooltipThrottle: preparedTooltip.throttle,
|
|
49
60
|
isOutsideBounds,
|
|
50
61
|
});
|
|
51
62
|
const clickHandler = (_b = (_a = data.chart) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.click;
|
|
@@ -132,8 +143,8 @@ export const ChartInner = (props) => {
|
|
|
132
143
|
React.createElement("div", { className: b('html-layer'), ref: setHtmlLayout, style: {
|
|
133
144
|
'--g-html-layout-transform': `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
|
|
134
145
|
} }),
|
|
135
|
-
|
|
146
|
+
Object.keys(zoomState).length > 0 && (React.createElement(Button, { style: { position: 'absolute', top: 0, right: 0 }, onClick: () => updateZoomState({}) },
|
|
136
147
|
React.createElement(ButtonIcon, null,
|
|
137
148
|
React.createElement(ArrowRotateLeft, null)))),
|
|
138
|
-
React.createElement(Tooltip, { dispatcher: dispatcher, tooltip:
|
|
149
|
+
React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: preparedTooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0], onOutsideClick: unpinTooltip, tooltipPinned: tooltipPinned })));
|
|
139
150
|
};
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Dispatch } from 'd3';
|
|
3
|
-
import type {
|
|
3
|
+
import type { ZoomState } from '../../hooks/useZoom/types';
|
|
4
4
|
import type { ChartInnerProps } from './types';
|
|
5
5
|
type Props = ChartInnerProps & {
|
|
6
|
+
clipPathId: string;
|
|
6
7
|
dispatcher: Dispatch<object>;
|
|
7
8
|
htmlLayout: HTMLElement | null;
|
|
8
|
-
svgContainer: SVGGElement | null;
|
|
9
9
|
plotNode: SVGGElement | null;
|
|
10
|
-
|
|
10
|
+
svgContainer: SVGGElement | null;
|
|
11
|
+
updateZoomState: (nextZoomState: Partial<ZoomState>) => void;
|
|
12
|
+
zoomState: Partial<ZoomState>;
|
|
11
13
|
};
|
|
12
14
|
export declare function useChartInnerProps(props: Props): {
|
|
13
15
|
svgBottomPos: number | undefined;
|
|
@@ -18,7 +20,6 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
18
20
|
boundsOffsetTop: number;
|
|
19
21
|
boundsWidth: number;
|
|
20
22
|
handleLegendItemClick: import("../../hooks").OnLegendItemClick;
|
|
21
|
-
handleZoomReset: (() => void) | undefined;
|
|
22
23
|
isOutsideBounds: (x: number, y: number) => boolean;
|
|
23
24
|
legendConfig: {
|
|
24
25
|
offset: {
|
|
@@ -41,16 +42,12 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
41
42
|
prevWidth: number | undefined;
|
|
42
43
|
shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
|
|
43
44
|
shapesData: import("../../hooks").ShapeData[];
|
|
44
|
-
title: (import("
|
|
45
|
+
title: (import("../..").ChartTitle & {
|
|
45
46
|
height: number;
|
|
46
47
|
}) | undefined;
|
|
47
|
-
|
|
48
|
-
enabled: boolean;
|
|
49
|
-
throttle: number;
|
|
50
|
-
};
|
|
51
|
-
xAxis: PreparedAxis | null;
|
|
48
|
+
xAxis: import("../../hooks").PreparedAxis | null;
|
|
52
49
|
xScale: import("../../hooks").ChartScale | undefined;
|
|
53
|
-
yAxis: PreparedAxis[];
|
|
50
|
+
yAxis: import("../../hooks").PreparedAxis[];
|
|
54
51
|
yScale: (import("../../hooks").ChartScale | undefined)[] | undefined;
|
|
55
52
|
};
|
|
56
53
|
export {};
|
|
@@ -1,97 +1,45 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { useAxisScales, useChartDimensions, useChartOptions, usePrevious, useSeries,
|
|
2
|
+
import { useAxis, useAxisScales, useChartDimensions, useChartOptions, useNormalizedOriginalData, usePrevious, useSeries, useShapes, useSplit, } from '../../hooks';
|
|
3
3
|
import { getYAxisWidth } from '../../hooks/useChartDimensions/utils';
|
|
4
|
-
import { getPreparedXAxis } from '../../hooks/useChartOptions/x-axis';
|
|
5
|
-
import { getPreparedYAxis } from '../../hooks/useChartOptions/y-axis';
|
|
6
4
|
import { getLegendComponents } from '../../hooks/useSeries/prepare-legend';
|
|
7
5
|
import { getPreparedOptions } from '../../hooks/useSeries/prepare-options';
|
|
8
|
-
import { getActiveLegendItems } from '../../hooks/useSeries/utils';
|
|
9
6
|
import { useZoom } from '../../hooks/useZoom';
|
|
10
|
-
import {
|
|
11
|
-
import { hasAtLeastOneSeriesDataPerPlot
|
|
7
|
+
import { getZoomedSeriesData } from '../../utils';
|
|
8
|
+
import { hasAtLeastOneSeriesDataPerPlot } from './utils';
|
|
12
9
|
export function useChartInnerProps(props) {
|
|
13
10
|
var _a;
|
|
14
|
-
const {
|
|
11
|
+
const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, svgContainer, width, updateZoomState, zoomState, } = props;
|
|
15
12
|
const prevWidth = usePrevious(width);
|
|
16
13
|
const prevHeight = usePrevious(height);
|
|
17
|
-
const {
|
|
14
|
+
const { normalizedSeriesData, normalizedXAxis, normalizedYAxis } = useNormalizedOriginalData({
|
|
18
15
|
seriesData: data.series.data,
|
|
16
|
+
xAxis: data.xAxis,
|
|
17
|
+
yAxis: data.yAxis,
|
|
18
|
+
});
|
|
19
|
+
const { chart, title, colors } = useChartOptions({
|
|
19
20
|
chart: data.chart,
|
|
20
21
|
colors: data.colors,
|
|
22
|
+
seriesData: normalizedSeriesData,
|
|
21
23
|
title: data.title,
|
|
22
|
-
tooltip: data.tooltip,
|
|
23
|
-
xAxis: data.xAxis,
|
|
24
|
-
yAxes: data.yAxis,
|
|
25
24
|
});
|
|
26
25
|
const preparedSeriesOptions = React.useMemo(() => {
|
|
27
26
|
return getPreparedOptions(data.series.options);
|
|
28
27
|
}, [data.series.options]);
|
|
29
|
-
const
|
|
30
|
-
const sortedSeriesData = React.useMemo(() => {
|
|
31
|
-
return getSortedSeriesData({ seriesData: data.series.data, yAxes: data.yAxis });
|
|
32
|
-
}, [data.series.data, data.yAxis]);
|
|
33
|
-
const { zoomedSeriesData, zoomedShapesSeriesData } = React.useMemo(() => {
|
|
34
|
-
return getZoomedSeriesData({
|
|
35
|
-
seriesData: sortedSeriesData,
|
|
36
|
-
xAxis: data.xAxis,
|
|
37
|
-
yAxes: data.yAxis,
|
|
38
|
-
zoomState,
|
|
39
|
-
});
|
|
40
|
-
}, [data.xAxis, data.yAxis, sortedSeriesData, zoomState]);
|
|
41
|
-
const { preparedSeries, preparedLegend, handleLegendItemClick } = useSeries({
|
|
28
|
+
const { preparedSeries: basePreparedSeries, preparedLegend, handleLegendItemClick, } = useSeries({
|
|
42
29
|
colors,
|
|
43
30
|
legend: data.legend,
|
|
44
|
-
originalSeriesData:
|
|
45
|
-
seriesData:
|
|
31
|
+
originalSeriesData: normalizedSeriesData,
|
|
32
|
+
seriesData: normalizedSeriesData,
|
|
46
33
|
seriesOptions: data.series.options,
|
|
47
34
|
});
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
xAxis:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
seriesOptions: preparedSeriesOptions,
|
|
55
|
-
});
|
|
56
|
-
let estimatedBoundsHeight = height;
|
|
57
|
-
if (xAxis) {
|
|
58
|
-
estimatedBoundsHeight =
|
|
59
|
-
height -
|
|
60
|
-
(xAxis.title.height +
|
|
61
|
-
xAxis.title.margin +
|
|
62
|
-
xAxis.labels.margin +
|
|
63
|
-
xAxis.labels.height +
|
|
64
|
-
(preparedLegend ? preparedLegend.height + preparedLegend.margin : 0) +
|
|
65
|
-
chart.margin.top +
|
|
66
|
-
chart.margin.bottom);
|
|
67
|
-
}
|
|
68
|
-
const yAxis = await getPreparedYAxis({
|
|
69
|
-
height,
|
|
70
|
-
boundsHeight: estimatedBoundsHeight,
|
|
71
|
-
width,
|
|
72
|
-
seriesData,
|
|
73
|
-
yAxis: data.yAxis,
|
|
35
|
+
const { preparedSeries, preparedShapesSeries } = React.useMemo(() => {
|
|
36
|
+
return getZoomedSeriesData({
|
|
37
|
+
seriesData: basePreparedSeries,
|
|
38
|
+
xAxis: normalizedXAxis,
|
|
39
|
+
yAxis: normalizedYAxis,
|
|
40
|
+
zoomState,
|
|
74
41
|
});
|
|
75
|
-
|
|
76
|
-
}, [
|
|
77
|
-
chart.margin,
|
|
78
|
-
data.xAxis,
|
|
79
|
-
data.yAxis,
|
|
80
|
-
height,
|
|
81
|
-
preparedLegend,
|
|
82
|
-
preparedSeries,
|
|
83
|
-
preparedSeriesOptions,
|
|
84
|
-
width,
|
|
85
|
-
]);
|
|
86
|
-
const { xAxis, yAxis } = useAsyncState({ xAxis: null, yAxis: [] }, setAxes);
|
|
87
|
-
const activeLegendItems = React.useMemo(() => getActiveLegendItems(preparedSeries), [preparedSeries]);
|
|
88
|
-
const { preparedSeries: preparedShapesSeries } = useShapeSeries({
|
|
89
|
-
colors,
|
|
90
|
-
seriesData: zoomedShapesSeriesData,
|
|
91
|
-
seriesOptions: data.series.options,
|
|
92
|
-
activeLegendItems,
|
|
93
|
-
preparedLegend,
|
|
94
|
-
});
|
|
42
|
+
}, [basePreparedSeries, normalizedXAxis, normalizedYAxis, zoomState]);
|
|
95
43
|
const { legendConfig, legendItems } = React.useMemo(() => {
|
|
96
44
|
if (!preparedLegend) {
|
|
97
45
|
return { legendConfig: undefined, legendItems: [] };
|
|
@@ -104,14 +52,24 @@ export function useChartInnerProps(props) {
|
|
|
104
52
|
preparedLegend,
|
|
105
53
|
});
|
|
106
54
|
}, [width, height, chart.margin, preparedSeries, preparedLegend]);
|
|
107
|
-
const {
|
|
55
|
+
const { xAxis, yAxis } = useAxis({
|
|
56
|
+
height,
|
|
57
|
+
preparedChart: chart,
|
|
58
|
+
preparedLegend,
|
|
59
|
+
preparedSeries,
|
|
60
|
+
preparedSeriesOptions,
|
|
108
61
|
width,
|
|
62
|
+
xAxis: normalizedXAxis,
|
|
63
|
+
yAxis: normalizedYAxis,
|
|
64
|
+
});
|
|
65
|
+
const { boundsWidth, boundsHeight } = useChartDimensions({
|
|
109
66
|
height,
|
|
110
67
|
margin: chart.margin,
|
|
111
68
|
preparedLegend,
|
|
112
|
-
preparedXAxis: xAxis,
|
|
113
|
-
preparedYAxis: yAxis,
|
|
114
69
|
preparedSeries: preparedSeries,
|
|
70
|
+
preparedYAxis: yAxis,
|
|
71
|
+
preparedXAxis: xAxis,
|
|
72
|
+
width,
|
|
115
73
|
});
|
|
116
74
|
const preparedSplit = useSplit({ split: data.split, boundsHeight, chartWidth: width });
|
|
117
75
|
const { xScale, yScale } = useAxisScales({
|
|
@@ -144,17 +102,17 @@ export function useChartInnerProps(props) {
|
|
|
144
102
|
isOutsideBounds,
|
|
145
103
|
});
|
|
146
104
|
const handleAttemptToSetZoomState = React.useCallback((nextZoomState) => {
|
|
147
|
-
const {
|
|
148
|
-
seriesData:
|
|
149
|
-
xAxis
|
|
150
|
-
|
|
105
|
+
const { preparedSeries: nextZoomedSeriesData } = getZoomedSeriesData({
|
|
106
|
+
seriesData: preparedSeries,
|
|
107
|
+
xAxis,
|
|
108
|
+
yAxis,
|
|
151
109
|
zoomState: nextZoomState,
|
|
152
110
|
});
|
|
153
111
|
const hasData = hasAtLeastOneSeriesDataPerPlot(nextZoomedSeriesData, yAxis);
|
|
154
112
|
if (hasData) {
|
|
155
|
-
|
|
113
|
+
updateZoomState(nextZoomState);
|
|
156
114
|
}
|
|
157
|
-
}, [
|
|
115
|
+
}, [xAxis, yAxis, preparedSeries, updateZoomState]);
|
|
158
116
|
useZoom({
|
|
159
117
|
node: plotNode,
|
|
160
118
|
onUpdate: handleAttemptToSetZoomState,
|
|
@@ -181,9 +139,6 @@ export function useChartInnerProps(props) {
|
|
|
181
139
|
return acc;
|
|
182
140
|
}, 0);
|
|
183
141
|
const { bottom, top, x } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
|
|
184
|
-
const handleZoomReset = React.useCallback(() => {
|
|
185
|
-
setZoomState({});
|
|
186
|
-
}, []);
|
|
187
142
|
return {
|
|
188
143
|
svgBottomPos: bottom,
|
|
189
144
|
svgTopPos: top,
|
|
@@ -193,7 +148,6 @@ export function useChartInnerProps(props) {
|
|
|
193
148
|
boundsOffsetTop,
|
|
194
149
|
boundsWidth,
|
|
195
150
|
handleLegendItemClick,
|
|
196
|
-
handleZoomReset: Object.keys(zoomState).length > 0 ? handleZoomReset : undefined,
|
|
197
151
|
isOutsideBounds,
|
|
198
152
|
legendConfig,
|
|
199
153
|
legendItems,
|
|
@@ -205,7 +159,6 @@ export function useChartInnerProps(props) {
|
|
|
205
159
|
shapes,
|
|
206
160
|
shapesData,
|
|
207
161
|
title,
|
|
208
|
-
tooltip,
|
|
209
162
|
xAxis,
|
|
210
163
|
xScale,
|
|
211
164
|
yAxis,
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Dispatch } from 'd3';
|
|
3
|
-
import type {
|
|
3
|
+
import type { PreparedTooltip, ZoomState } from '../../hooks';
|
|
4
4
|
type Props = {
|
|
5
5
|
dispatcher: Dispatch<object>;
|
|
6
|
-
tooltip?:
|
|
6
|
+
tooltip?: PreparedTooltip;
|
|
7
7
|
};
|
|
8
8
|
export declare function useChartInnerState(props: Props): {
|
|
9
9
|
tooltipPinned: boolean;
|
|
10
10
|
togglePinTooltip: ((value: boolean, event: React.MouseEvent) => void) | undefined;
|
|
11
11
|
unpinTooltip: (() => void) | undefined;
|
|
12
|
+
updateZoomState: (nextZoomState: Partial<ZoomState>) => void;
|
|
13
|
+
zoomState: Partial<ZoomState>;
|
|
12
14
|
};
|
|
13
15
|
export {};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
2
3
|
import { EventType, isMacintosh } from '../../utils';
|
|
3
4
|
export function useChartInnerState(props) {
|
|
4
5
|
var _a, _b;
|
|
5
6
|
const { dispatcher, tooltip } = props;
|
|
6
7
|
const [tooltipPinned, setTooltipPinned] = React.useState(false);
|
|
8
|
+
const [zoomState, setZoomState] = React.useState({});
|
|
7
9
|
const tooltipEnabled = tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabled;
|
|
8
10
|
const tooltipPinEnabled = (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.pin) === null || _a === void 0 ? void 0 : _a.enabled;
|
|
9
11
|
const modifierKey = (_b = tooltip === null || tooltip === void 0 ? void 0 : tooltip.pin) === null || _b === void 0 ? void 0 : _b.modifierKey;
|
|
@@ -26,9 +28,16 @@ export function useChartInnerState(props) {
|
|
|
26
28
|
setTooltipPinned(false);
|
|
27
29
|
dispatcher.call(EventType.HOVER_SHAPE, {}, undefined);
|
|
28
30
|
}, [dispatcher]);
|
|
31
|
+
const updateZoomState = React.useCallback((nextZoomState) => {
|
|
32
|
+
if (!isEqual(zoomState, nextZoomState)) {
|
|
33
|
+
setZoomState(nextZoomState);
|
|
34
|
+
}
|
|
35
|
+
}, [zoomState]);
|
|
29
36
|
return {
|
|
30
37
|
tooltipPinned,
|
|
31
38
|
togglePinTooltip: tooltipEnabled && tooltipPinEnabled ? togglePinTooltip : undefined,
|
|
32
39
|
unpinTooltip: tooltipEnabled && tooltipPinEnabled ? unpinTooltip : undefined,
|
|
40
|
+
updateZoomState,
|
|
41
|
+
zoomState,
|
|
33
42
|
};
|
|
34
43
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
+
import type { PreparedSeries } from '../../hooks';
|
|
1
2
|
import type { PreparedAxis } from '../../hooks/useChartOptions/types';
|
|
2
|
-
|
|
3
|
-
export declare function hasAtLeastOneSeriesDataPerPlot(seriesData: ChartSeries[], yAxes?: PreparedAxis[]): boolean;
|
|
3
|
+
export declare function hasAtLeastOneSeriesDataPerPlot(seriesData: PreparedSeries[], yAxes?: PreparedAxis[]): boolean;
|
|
4
4
|
export declare function useAsyncState<T>(value: T, setState: () => Promise<T>): T;
|
|
@@ -21,7 +21,7 @@ export function hasAtLeastOneSeriesDataPerPlot(seriesData, yAxes = []) {
|
|
|
21
21
|
const yAxis = yAxes[yAxisIndex];
|
|
22
22
|
const plotIndex = (_a = yAxis === null || yAxis === void 0 ? void 0 : yAxis.plotIndex) !== null && _a !== void 0 ? _a : 0;
|
|
23
23
|
if (!hasDataMap.get(plotIndex)) {
|
|
24
|
-
if (seriesDataChunk.data.length > 0) {
|
|
24
|
+
if (Array.isArray(seriesDataChunk.data) && seriesDataChunk.data.length > 0) {
|
|
25
25
|
hasDataMap.set(plotIndex, true);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { block } from '../../utils';
|
|
3
|
-
import './styles.css';
|
|
4
|
-
const b = block('title');
|
|
5
2
|
export const Title = (props) => {
|
|
6
3
|
const { chartWidth, text, height, style } = props;
|
|
7
|
-
return (React.createElement("text", {
|
|
4
|
+
return (React.createElement("text", { dx: chartWidth / 2, dy: height / 2, dominantBaseline: "middle", textAnchor: "middle", style: {
|
|
5
|
+
fill: style === null || style === void 0 ? void 0 : style.fontColor,
|
|
6
|
+
fontSize: style === null || style === void 0 ? void 0 : style.fontSize,
|
|
7
|
+
fontWeight: style === null || style === void 0 ? void 0 : style.fontWeight,
|
|
8
|
+
lineHeight: `${height}px`,
|
|
9
|
+
} },
|
|
8
10
|
React.createElement("tspan", { dangerouslySetInnerHTML: { __html: text } })));
|
|
9
11
|
};
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
export * from './useAxis';
|
|
2
|
+
export * from './useAxisScales';
|
|
1
3
|
export * from './useChartDimensions';
|
|
2
4
|
export * from './useChartOptions';
|
|
3
5
|
export * from './useChartOptions/types';
|
|
4
|
-
export * from './
|
|
6
|
+
export * from './useCrosshair';
|
|
7
|
+
export * from './useNormalizedOriginalData';
|
|
5
8
|
export * from './usePrevious';
|
|
6
9
|
export * from './useSeries';
|
|
7
10
|
export * from './useSeries/types';
|
|
8
11
|
export * from './useShapes';
|
|
9
|
-
export * from './useTooltip';
|
|
10
12
|
export * from './useSplit';
|
|
11
13
|
export * from './useSplit/types';
|
|
12
|
-
export * from './
|
|
14
|
+
export * from './useTooltip';
|
|
15
|
+
export * from './useZoom';
|
|
16
|
+
export * from './useZoom/types';
|
package/dist/cjs/hooks/index.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
export * from './useAxis';
|
|
2
|
+
export * from './useAxisScales';
|
|
1
3
|
export * from './useChartDimensions';
|
|
2
4
|
export * from './useChartOptions';
|
|
3
5
|
export * from './useChartOptions/types';
|
|
4
|
-
export * from './
|
|
6
|
+
export * from './useCrosshair';
|
|
7
|
+
export * from './useNormalizedOriginalData';
|
|
5
8
|
export * from './usePrevious';
|
|
6
9
|
export * from './useSeries';
|
|
7
10
|
export * from './useSeries/types';
|
|
8
11
|
export * from './useShapes';
|
|
9
|
-
export * from './useTooltip';
|
|
10
12
|
export * from './useSplit';
|
|
11
13
|
export * from './useSplit/types';
|
|
12
|
-
export * from './
|
|
14
|
+
export * from './useTooltip';
|
|
15
|
+
export * from './useZoom';
|
|
16
|
+
export * from './useZoom/types';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ChartXAxis, ChartYAxis } from '../../types';
|
|
2
|
+
import type { PreparedAxis, PreparedChart } from '../useChartOptions/types';
|
|
3
|
+
import type { PreparedLegend, PreparedSeries, PreparedSeriesOptions } from '../useSeries/types';
|
|
4
|
+
interface UseAxesProps {
|
|
5
|
+
height: number;
|
|
6
|
+
preparedChart: PreparedChart;
|
|
7
|
+
preparedLegend: PreparedLegend | null;
|
|
8
|
+
preparedSeries: PreparedSeries[];
|
|
9
|
+
preparedSeriesOptions: PreparedSeriesOptions;
|
|
10
|
+
width: number;
|
|
11
|
+
boundsHeight?: number;
|
|
12
|
+
xAxis?: ChartXAxis;
|
|
13
|
+
yAxis?: ChartYAxis[];
|
|
14
|
+
}
|
|
15
|
+
export declare function useAxis(props: UseAxesProps): {
|
|
16
|
+
xAxis: PreparedAxis | null;
|
|
17
|
+
yAxis: PreparedAxis[];
|
|
18
|
+
};
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
3
|
+
import { getPreparedXAxis } from '../useChartOptions/x-axis';
|
|
4
|
+
import { getPreparedYAxis } from '../useChartOptions/y-axis';
|
|
5
|
+
export function useAxis(props) {
|
|
6
|
+
const { boundsHeight, height, preparedChart, preparedLegend, preparedSeries, preparedSeriesOptions, width, xAxis, yAxis, } = props;
|
|
7
|
+
const [axesState, setValue] = React.useState({ xAxis: null, yAxis: [] });
|
|
8
|
+
const axesStateRunRef = React.useRef(0);
|
|
9
|
+
const prevAxesStateValue = React.useRef(axesState);
|
|
10
|
+
const axesStateReady = React.useRef(false);
|
|
11
|
+
React.useEffect(() => {
|
|
12
|
+
axesStateRunRef.current++;
|
|
13
|
+
axesStateReady.current = false;
|
|
14
|
+
(async function () {
|
|
15
|
+
const currentRun = axesStateRunRef.current;
|
|
16
|
+
const seriesData = preparedSeries.filter((s) => s.visible);
|
|
17
|
+
const preparedXAxis = await getPreparedXAxis({
|
|
18
|
+
xAxis,
|
|
19
|
+
width,
|
|
20
|
+
seriesData,
|
|
21
|
+
seriesOptions: preparedSeriesOptions,
|
|
22
|
+
});
|
|
23
|
+
let estimatedBoundsHeight = boundsHeight !== null && boundsHeight !== void 0 ? boundsHeight : height;
|
|
24
|
+
if (preparedXAxis && typeof boundsHeight !== 'number') {
|
|
25
|
+
estimatedBoundsHeight =
|
|
26
|
+
height -
|
|
27
|
+
(preparedXAxis.title.height +
|
|
28
|
+
preparedXAxis.title.margin +
|
|
29
|
+
preparedXAxis.labels.margin +
|
|
30
|
+
preparedXAxis.labels.height +
|
|
31
|
+
(preparedLegend ? preparedLegend.height + preparedLegend.margin : 0) +
|
|
32
|
+
preparedChart.margin.top +
|
|
33
|
+
preparedChart.margin.bottom);
|
|
34
|
+
}
|
|
35
|
+
const preparedYAxis = await getPreparedYAxis({
|
|
36
|
+
height,
|
|
37
|
+
boundsHeight: estimatedBoundsHeight,
|
|
38
|
+
width,
|
|
39
|
+
seriesData,
|
|
40
|
+
yAxis,
|
|
41
|
+
});
|
|
42
|
+
const newStateValue = { xAxis: preparedXAxis, yAxis: preparedYAxis };
|
|
43
|
+
if (axesStateRunRef.current === currentRun) {
|
|
44
|
+
if (!isEqual(prevAxesStateValue.current, newStateValue)) {
|
|
45
|
+
setValue(newStateValue);
|
|
46
|
+
prevAxesStateValue.current = newStateValue;
|
|
47
|
+
}
|
|
48
|
+
axesStateReady.current = true;
|
|
49
|
+
}
|
|
50
|
+
})();
|
|
51
|
+
}, [
|
|
52
|
+
boundsHeight,
|
|
53
|
+
height,
|
|
54
|
+
preparedChart.margin,
|
|
55
|
+
preparedLegend,
|
|
56
|
+
preparedSeries,
|
|
57
|
+
preparedSeriesOptions,
|
|
58
|
+
width,
|
|
59
|
+
xAxis,
|
|
60
|
+
yAxis,
|
|
61
|
+
]);
|
|
62
|
+
return axesStateReady.current ? axesState : { xAxis: null, yAxis: [] };
|
|
63
|
+
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import type { ChartSeries, ChartTitle,
|
|
1
|
+
import type { ChartSeries, ChartTitle, ChartOptions as GeneralChartOptions } from '../../types';
|
|
2
2
|
import type { ChartOptions } from './types';
|
|
3
3
|
type Args = {
|
|
4
4
|
seriesData: ChartSeries[];
|
|
5
5
|
chart?: GeneralChartOptions;
|
|
6
6
|
colors?: string[];
|
|
7
7
|
title?: ChartTitle;
|
|
8
|
-
tooltip?: ChartTooltip;
|
|
9
|
-
yAxes?: ChartYAxis[];
|
|
10
|
-
xAxis?: ChartXAxis;
|
|
11
8
|
};
|
|
12
9
|
export declare const useChartOptions: (args: Args) => ChartOptions;
|
|
13
10
|
export {};
|