@gravity-ui/charts 1.20.0 → 1.22.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 +23 -8
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +24 -13
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +41 -107
- 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 +12 -3
- package/dist/cjs/components/ChartInner/utils.js +61 -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/chart.js +6 -1
- 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/types/chart/axis.d.ts +20 -2
- package/dist/cjs/types/chart/zoom.d.ts +29 -0
- 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/text.js +24 -21
- package/dist/cjs/utils/chart/zoom.d.ts +7 -6
- package/dist/cjs/utils/chart/zoom.js +14 -6
- 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 +23 -8
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +23 -12
- package/dist/esm/components/ChartInner/useChartInnerProps.js +41 -107
- 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 +12 -3
- package/dist/esm/components/ChartInner/utils.js +61 -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/chart.js +6 -1
- 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/types/chart/axis.d.ts +20 -2
- package/dist/esm/types/chart/zoom.d.ts +29 -0
- 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/text.js +24 -21
- package/dist/esm/utils/chart/zoom.d.ts +7 -6
- package/dist/esm/utils/chart/zoom.js +14 -6
- 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';
|
|
@@ -13,25 +14,36 @@ import { Tooltip } from '../Tooltip';
|
|
|
13
14
|
import { useChartInnerHandlers } from './useChartInnerHandlers';
|
|
14
15
|
import { useChartInnerProps } from './useChartInnerProps';
|
|
15
16
|
import { useChartInnerState } from './useChartInnerState';
|
|
16
|
-
import { useAsyncState } from './utils';
|
|
17
|
+
import { getResetZoomButtonStyle, useAsyncState } from './utils';
|
|
17
18
|
import './styles.css';
|
|
18
19
|
const b = block('chart');
|
|
19
20
|
export const ChartInner = (props) => {
|
|
20
21
|
var _a, _b, _c, _d;
|
|
21
22
|
const { width, height, data } = props;
|
|
22
23
|
const svgRef = React.useRef(null);
|
|
24
|
+
const resetZoomButtonRef = React.useRef(null);
|
|
23
25
|
const [htmlLayout, setHtmlLayout] = React.useState(null);
|
|
24
26
|
const plotRef = React.useRef(null);
|
|
25
27
|
const plotBeforeRef = React.useRef(null);
|
|
26
28
|
const plotAfterRef = React.useRef(null);
|
|
27
29
|
const dispatcher = React.useMemo(() => getDispatcher(), []);
|
|
28
30
|
const clipPathId = useUniqId();
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
const preparedTooltip = React.useMemo(() => {
|
|
32
|
+
return getPreparedTooltip({
|
|
33
|
+
tooltip: data.tooltip,
|
|
34
|
+
seriesData: data.series.data,
|
|
35
|
+
yAxes: data.yAxis,
|
|
36
|
+
xAxis: data.xAxis,
|
|
37
|
+
});
|
|
38
|
+
}, [data.series.data, data.tooltip, data.yAxis, data.xAxis]);
|
|
39
|
+
const { tooltipPinned, togglePinTooltip, unpinTooltip, updateZoomState, zoomState } = useChartInnerState({
|
|
32
40
|
dispatcher,
|
|
33
|
-
tooltip,
|
|
41
|
+
tooltip: preparedTooltip,
|
|
34
42
|
});
|
|
43
|
+
const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, isOutsideBounds, legendConfig, legendItems, preparedSeries, preparedSplit, preparedLegend, preparedZoom, prevHeight, prevWidth, shapes, shapesData, svgXPos, title, xAxis, xScale, yAxis, yScale, } = useChartInnerProps(Object.assign(Object.assign({}, props), { clipPathId,
|
|
44
|
+
dispatcher,
|
|
45
|
+
htmlLayout, plotNode: plotRef.current, svgContainer: svgRef.current, updateZoomState,
|
|
46
|
+
zoomState }));
|
|
35
47
|
const { handleChartClick, handleMouseLeave, throttledHandleMouseMove, throttledHandleTouchMove } = useChartInnerHandlers({
|
|
36
48
|
boundsHeight,
|
|
37
49
|
boundsOffsetLeft,
|
|
@@ -45,7 +57,7 @@ export const ChartInner = (props) => {
|
|
|
45
57
|
unpinTooltip,
|
|
46
58
|
xAxis,
|
|
47
59
|
yAxis,
|
|
48
|
-
tooltipThrottle:
|
|
60
|
+
tooltipThrottle: preparedTooltip.throttle,
|
|
49
61
|
isOutsideBounds,
|
|
50
62
|
});
|
|
51
63
|
const clickHandler = (_b = (_a = data.chart) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.click;
|
|
@@ -132,8 +144,11 @@ export const ChartInner = (props) => {
|
|
|
132
144
|
React.createElement("div", { className: b('html-layer'), ref: setHtmlLayout, style: {
|
|
133
145
|
'--g-html-layout-transform': `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
|
|
134
146
|
} }),
|
|
135
|
-
|
|
147
|
+
Object.keys(zoomState).length > 0 && preparedZoom && (React.createElement(Button, { onClick: () => updateZoomState({}), ref: resetZoomButtonRef, style: getResetZoomButtonStyle(Object.assign({ boundsHeight,
|
|
148
|
+
boundsOffsetLeft,
|
|
149
|
+
boundsOffsetTop,
|
|
150
|
+
boundsWidth, node: resetZoomButtonRef.current, titleHeight: title === null || title === void 0 ? void 0 : title.height }, preparedZoom.resetButton)) },
|
|
136
151
|
React.createElement(ButtonIcon, null,
|
|
137
152
|
React.createElement(ArrowRotateLeft, null)))),
|
|
138
|
-
React.createElement(Tooltip, { dispatcher: dispatcher, tooltip:
|
|
153
|
+
React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: preparedTooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0], onOutsideClick: unpinTooltip, tooltipPinned: tooltipPinned })));
|
|
139
154
|
};
|
|
@@ -1,24 +1,23 @@
|
|
|
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
|
-
svgBottomPos: number | undefined;
|
|
14
|
-
svgTopPos: number | undefined;
|
|
15
15
|
svgXPos: number | undefined;
|
|
16
16
|
boundsHeight: number;
|
|
17
17
|
boundsOffsetLeft: number;
|
|
18
18
|
boundsOffsetTop: number;
|
|
19
19
|
boundsWidth: number;
|
|
20
20
|
handleLegendItemClick: import("../../hooks").OnLegendItemClick;
|
|
21
|
-
handleZoomReset: (() => void) | undefined;
|
|
22
21
|
isOutsideBounds: (x: number, y: number) => boolean;
|
|
23
22
|
legendConfig: {
|
|
24
23
|
offset: {
|
|
@@ -37,20 +36,32 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
37
36
|
preparedLegend: import("../../hooks").PreparedLegend | null;
|
|
38
37
|
preparedSeries: import("../../hooks").PreparedSeries[];
|
|
39
38
|
preparedSplit: import("../../hooks").PreparedSplit;
|
|
39
|
+
preparedZoom: Required<{
|
|
40
|
+
type?: ("x" | "y" | "xy") | undefined;
|
|
41
|
+
brush?: Required<{
|
|
42
|
+
style?: Required<{
|
|
43
|
+
fillOpacity?: number | undefined;
|
|
44
|
+
} | undefined>;
|
|
45
|
+
} | undefined>;
|
|
46
|
+
resetButton?: Required<{
|
|
47
|
+
align?: ("bottom-left" | "bottom-right" | "top-left" | "top-right") | undefined;
|
|
48
|
+
offset?: Required<{
|
|
49
|
+
x?: number | undefined;
|
|
50
|
+
y?: number | undefined;
|
|
51
|
+
} | undefined>;
|
|
52
|
+
relativeTo?: ("chart-box" | "plot-box") | undefined;
|
|
53
|
+
} | undefined>;
|
|
54
|
+
}> | null;
|
|
40
55
|
prevHeight: number | undefined;
|
|
41
56
|
prevWidth: number | undefined;
|
|
42
57
|
shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
|
|
43
58
|
shapesData: import("../../hooks").ShapeData[];
|
|
44
|
-
title: (import("
|
|
59
|
+
title: (import("../..").ChartTitle & {
|
|
45
60
|
height: number;
|
|
46
61
|
}) | undefined;
|
|
47
|
-
|
|
48
|
-
enabled: boolean;
|
|
49
|
-
throttle: number;
|
|
50
|
-
};
|
|
51
|
-
xAxis: PreparedAxis | null;
|
|
62
|
+
xAxis: import("../../hooks").PreparedAxis | null;
|
|
52
63
|
xScale: import("../../hooks").ChartScale | undefined;
|
|
53
|
-
yAxis: PreparedAxis[];
|
|
64
|
+
yAxis: import("../../hooks").PreparedAxis[];
|
|
54
65
|
yScale: (import("../../hooks").ChartScale | undefined)[] | undefined;
|
|
55
66
|
};
|
|
56
67
|
export {};
|
|
@@ -1,115 +1,45 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
3
|
-
import { useAxisScales, useChartDimensions, useChartOptions, usePrevious, useSeries, useShapeSeries, useShapes, useSplit, } from '../../hooks';
|
|
2
|
+
import { useAxis, useAxisScales, useChartDimensions, useChartOptions, useNormalizedOriginalData, usePrevious, useSeries, useShapes, useSplit, } from '../../hooks';
|
|
4
3
|
import { getYAxisWidth } from '../../hooks/useChartDimensions/utils';
|
|
5
|
-
import { getPreparedXAxis } from '../../hooks/useChartOptions/x-axis';
|
|
6
|
-
import { getPreparedYAxis } from '../../hooks/useChartOptions/y-axis';
|
|
7
4
|
import { getLegendComponents } from '../../hooks/useSeries/prepare-legend';
|
|
8
5
|
import { getPreparedOptions } from '../../hooks/useSeries/prepare-options';
|
|
9
|
-
import { getActiveLegendItems } from '../../hooks/useSeries/utils';
|
|
10
6
|
import { useZoom } from '../../hooks/useZoom';
|
|
11
|
-
import {
|
|
7
|
+
import { getZoomedSeriesData } from '../../utils';
|
|
12
8
|
import { hasAtLeastOneSeriesDataPerPlot } from './utils';
|
|
13
9
|
export function useChartInnerProps(props) {
|
|
14
10
|
var _a;
|
|
15
|
-
const {
|
|
11
|
+
const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, svgContainer, width, updateZoomState, zoomState, } = props;
|
|
16
12
|
const prevWidth = usePrevious(width);
|
|
17
13
|
const prevHeight = usePrevious(height);
|
|
18
|
-
const {
|
|
14
|
+
const { normalizedSeriesData, normalizedXAxis, normalizedYAxis } = useNormalizedOriginalData({
|
|
19
15
|
seriesData: data.series.data,
|
|
16
|
+
xAxis: data.xAxis,
|
|
17
|
+
yAxis: data.yAxis,
|
|
18
|
+
});
|
|
19
|
+
const { chart, title, colors } = useChartOptions({
|
|
20
20
|
chart: data.chart,
|
|
21
21
|
colors: data.colors,
|
|
22
|
+
seriesData: normalizedSeriesData,
|
|
22
23
|
title: data.title,
|
|
23
|
-
tooltip: data.tooltip,
|
|
24
|
-
xAxis: data.xAxis,
|
|
25
|
-
yAxes: data.yAxis,
|
|
26
24
|
});
|
|
27
25
|
const preparedSeriesOptions = React.useMemo(() => {
|
|
28
26
|
return getPreparedOptions(data.series.options);
|
|
29
27
|
}, [data.series.options]);
|
|
30
|
-
const
|
|
31
|
-
const sortedSeriesData = React.useMemo(() => {
|
|
32
|
-
return getSortedSeriesData({ seriesData: data.series.data, yAxes: data.yAxis });
|
|
33
|
-
}, [data.series.data, data.yAxis]);
|
|
34
|
-
const { zoomedSeriesData, zoomedShapesSeriesData } = React.useMemo(() => {
|
|
35
|
-
return getZoomedSeriesData({
|
|
36
|
-
seriesData: sortedSeriesData,
|
|
37
|
-
xAxis: data.xAxis,
|
|
38
|
-
yAxes: data.yAxis,
|
|
39
|
-
zoomState,
|
|
40
|
-
});
|
|
41
|
-
}, [data.xAxis, data.yAxis, sortedSeriesData, zoomState]);
|
|
42
|
-
const { preparedSeries, preparedLegend, handleLegendItemClick } = useSeries({
|
|
28
|
+
const { preparedSeries: basePreparedSeries, preparedLegend, handleLegendItemClick, } = useSeries({
|
|
43
29
|
colors,
|
|
44
30
|
legend: data.legend,
|
|
45
|
-
originalSeriesData:
|
|
46
|
-
seriesData:
|
|
31
|
+
originalSeriesData: normalizedSeriesData,
|
|
32
|
+
seriesData: normalizedSeriesData,
|
|
47
33
|
seriesOptions: data.series.options,
|
|
48
34
|
});
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
(async function () {
|
|
58
|
-
const currentRun = axesStateRunRef.current;
|
|
59
|
-
const seriesData = preparedSeries.filter((s) => s.visible);
|
|
60
|
-
const xAxis = await getPreparedXAxis({
|
|
61
|
-
xAxis: data.xAxis,
|
|
62
|
-
width,
|
|
63
|
-
seriesData,
|
|
64
|
-
seriesOptions: preparedSeriesOptions,
|
|
65
|
-
});
|
|
66
|
-
let estimatedBoundsHeight = height;
|
|
67
|
-
if (xAxis) {
|
|
68
|
-
estimatedBoundsHeight =
|
|
69
|
-
height -
|
|
70
|
-
(xAxis.title.height +
|
|
71
|
-
xAxis.title.margin +
|
|
72
|
-
xAxis.labels.margin +
|
|
73
|
-
xAxis.labels.height +
|
|
74
|
-
(preparedLegend ? preparedLegend.height + preparedLegend.margin : 0) +
|
|
75
|
-
chart.margin.top +
|
|
76
|
-
chart.margin.bottom);
|
|
77
|
-
}
|
|
78
|
-
const yAxis = await getPreparedYAxis({
|
|
79
|
-
height,
|
|
80
|
-
boundsHeight: estimatedBoundsHeight,
|
|
81
|
-
width,
|
|
82
|
-
seriesData,
|
|
83
|
-
yAxis: data.yAxis,
|
|
84
|
-
});
|
|
85
|
-
const newStateValue = { xAxis, yAxis };
|
|
86
|
-
if (axesStateRunRef.current === currentRun) {
|
|
87
|
-
if (!isEqual(prevAxesStateValue.current, newStateValue)) {
|
|
88
|
-
setValue(newStateValue);
|
|
89
|
-
prevAxesStateValue.current = newStateValue;
|
|
90
|
-
}
|
|
91
|
-
axesStateReady.current = true;
|
|
92
|
-
}
|
|
93
|
-
})();
|
|
94
|
-
}, [
|
|
95
|
-
chart.margin,
|
|
96
|
-
data.xAxis,
|
|
97
|
-
data.yAxis,
|
|
98
|
-
height,
|
|
99
|
-
preparedLegend,
|
|
100
|
-
preparedSeries,
|
|
101
|
-
preparedSeriesOptions,
|
|
102
|
-
width,
|
|
103
|
-
]);
|
|
104
|
-
const { xAxis, yAxis } = axesStateReady.current ? axesState : { xAxis: null, yAxis: [] };
|
|
105
|
-
const activeLegendItems = React.useMemo(() => getActiveLegendItems(preparedSeries), [preparedSeries]);
|
|
106
|
-
const { preparedSeries: preparedShapesSeries } = useShapeSeries({
|
|
107
|
-
colors,
|
|
108
|
-
seriesData: zoomedShapesSeriesData,
|
|
109
|
-
seriesOptions: data.series.options,
|
|
110
|
-
activeLegendItems,
|
|
111
|
-
preparedLegend,
|
|
112
|
-
});
|
|
35
|
+
const { preparedSeries, preparedShapesSeries } = React.useMemo(() => {
|
|
36
|
+
return getZoomedSeriesData({
|
|
37
|
+
seriesData: basePreparedSeries,
|
|
38
|
+
xAxis: normalizedXAxis,
|
|
39
|
+
yAxis: normalizedYAxis,
|
|
40
|
+
zoomState,
|
|
41
|
+
});
|
|
42
|
+
}, [basePreparedSeries, normalizedXAxis, normalizedYAxis, zoomState]);
|
|
113
43
|
const { legendConfig, legendItems } = React.useMemo(() => {
|
|
114
44
|
if (!preparedLegend) {
|
|
115
45
|
return { legendConfig: undefined, legendItems: [] };
|
|
@@ -122,14 +52,24 @@ export function useChartInnerProps(props) {
|
|
|
122
52
|
preparedLegend,
|
|
123
53
|
});
|
|
124
54
|
}, [width, height, chart.margin, preparedSeries, preparedLegend]);
|
|
125
|
-
const {
|
|
55
|
+
const { xAxis, yAxis } = useAxis({
|
|
56
|
+
height,
|
|
57
|
+
preparedChart: chart,
|
|
58
|
+
preparedLegend,
|
|
59
|
+
preparedSeries,
|
|
60
|
+
preparedSeriesOptions,
|
|
126
61
|
width,
|
|
62
|
+
xAxis: normalizedXAxis,
|
|
63
|
+
yAxis: normalizedYAxis,
|
|
64
|
+
});
|
|
65
|
+
const { boundsWidth, boundsHeight } = useChartDimensions({
|
|
127
66
|
height,
|
|
128
67
|
margin: chart.margin,
|
|
129
68
|
preparedLegend,
|
|
130
|
-
preparedXAxis: xAxis,
|
|
131
|
-
preparedYAxis: yAxis,
|
|
132
69
|
preparedSeries: preparedSeries,
|
|
70
|
+
preparedYAxis: yAxis,
|
|
71
|
+
preparedXAxis: xAxis,
|
|
72
|
+
width,
|
|
133
73
|
});
|
|
134
74
|
const preparedSplit = useSplit({ split: data.split, boundsHeight, chartWidth: width });
|
|
135
75
|
const { xScale, yScale } = useAxisScales({
|
|
@@ -162,17 +102,17 @@ export function useChartInnerProps(props) {
|
|
|
162
102
|
isOutsideBounds,
|
|
163
103
|
});
|
|
164
104
|
const handleAttemptToSetZoomState = React.useCallback((nextZoomState) => {
|
|
165
|
-
const {
|
|
166
|
-
seriesData:
|
|
167
|
-
xAxis
|
|
168
|
-
|
|
105
|
+
const { preparedSeries: nextZoomedSeriesData } = getZoomedSeriesData({
|
|
106
|
+
seriesData: preparedSeries,
|
|
107
|
+
xAxis,
|
|
108
|
+
yAxis,
|
|
169
109
|
zoomState: nextZoomState,
|
|
170
110
|
});
|
|
171
111
|
const hasData = hasAtLeastOneSeriesDataPerPlot(nextZoomedSeriesData, yAxis);
|
|
172
112
|
if (hasData) {
|
|
173
|
-
|
|
113
|
+
updateZoomState(nextZoomState);
|
|
174
114
|
}
|
|
175
|
-
}, [
|
|
115
|
+
}, [xAxis, yAxis, preparedSeries, updateZoomState]);
|
|
176
116
|
useZoom({
|
|
177
117
|
node: plotNode,
|
|
178
118
|
onUpdate: handleAttemptToSetZoomState,
|
|
@@ -198,32 +138,26 @@ export function useChartInnerProps(props) {
|
|
|
198
138
|
}
|
|
199
139
|
return acc;
|
|
200
140
|
}, 0);
|
|
201
|
-
const {
|
|
202
|
-
const handleZoomReset = React.useCallback(() => {
|
|
203
|
-
setZoomState({});
|
|
204
|
-
}, []);
|
|
141
|
+
const { x } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
|
|
205
142
|
return {
|
|
206
|
-
svgBottomPos: bottom,
|
|
207
|
-
svgTopPos: top,
|
|
208
143
|
svgXPos: x,
|
|
209
144
|
boundsHeight,
|
|
210
145
|
boundsOffsetLeft,
|
|
211
146
|
boundsOffsetTop,
|
|
212
147
|
boundsWidth,
|
|
213
148
|
handleLegendItemClick,
|
|
214
|
-
handleZoomReset: Object.keys(zoomState).length > 0 ? handleZoomReset : undefined,
|
|
215
149
|
isOutsideBounds,
|
|
216
150
|
legendConfig,
|
|
217
151
|
legendItems,
|
|
218
152
|
preparedLegend,
|
|
219
153
|
preparedSeries,
|
|
220
154
|
preparedSplit,
|
|
155
|
+
preparedZoom: chart.zoom,
|
|
221
156
|
prevHeight,
|
|
222
157
|
prevWidth,
|
|
223
158
|
shapes,
|
|
224
159
|
shapesData,
|
|
225
160
|
title,
|
|
226
|
-
tooltip,
|
|
227
161
|
xAxis,
|
|
228
162
|
xScale,
|
|
229
163
|
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,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
3
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { PreparedSeries } from '../../hooks';
|
|
3
|
+
import type { PreparedAxis, PreparedZoom } from '../../hooks/useChartOptions/types';
|
|
4
|
+
export declare function hasAtLeastOneSeriesDataPerPlot(seriesData: PreparedSeries[], yAxes?: PreparedAxis[]): boolean;
|
|
4
5
|
export declare function useAsyncState<T>(value: T, setState: () => Promise<T>): T;
|
|
6
|
+
export declare function getResetZoomButtonStyle(args: {
|
|
7
|
+
boundsHeight: number;
|
|
8
|
+
boundsOffsetLeft: number;
|
|
9
|
+
boundsOffsetTop: number;
|
|
10
|
+
boundsWidth: number;
|
|
11
|
+
node: HTMLElement | null;
|
|
12
|
+
titleHeight?: number;
|
|
13
|
+
} & PreparedZoom['resetButton']): React.CSSProperties;
|
|
@@ -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
|
}
|
|
@@ -47,3 +47,63 @@ export function useAsyncState(value, setState) {
|
|
|
47
47
|
}, [setState]);
|
|
48
48
|
return stateValue;
|
|
49
49
|
}
|
|
50
|
+
export function getResetZoomButtonStyle(args) {
|
|
51
|
+
const { align, boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, node, offset, relativeTo, titleHeight, } = args;
|
|
52
|
+
const style = {
|
|
53
|
+
position: 'absolute',
|
|
54
|
+
transform: `translate(${offset.x}px, ${offset.y}px)`,
|
|
55
|
+
};
|
|
56
|
+
switch (relativeTo) {
|
|
57
|
+
case 'chart-box': {
|
|
58
|
+
switch (align) {
|
|
59
|
+
case 'bottom-left': {
|
|
60
|
+
style.bottom = 0;
|
|
61
|
+
style.left = 0;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case 'bottom-right': {
|
|
65
|
+
style.bottom = 0;
|
|
66
|
+
style.right = 0;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case 'top-left': {
|
|
70
|
+
style.top = 0;
|
|
71
|
+
style.left = 0;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case 'top-right': {
|
|
75
|
+
style.top = 0;
|
|
76
|
+
style.right = 0;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case 'plot-box': {
|
|
83
|
+
switch (align) {
|
|
84
|
+
case 'bottom-left': {
|
|
85
|
+
style.left = boundsOffsetLeft;
|
|
86
|
+
style.top = boundsHeight - ((node === null || node === void 0 ? void 0 : node.clientHeight) || 0) + (titleHeight || 0);
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case 'bottom-right': {
|
|
90
|
+
style.left = boundsWidth + boundsOffsetLeft - ((node === null || node === void 0 ? void 0 : node.clientWidth) || 0);
|
|
91
|
+
style.top = boundsHeight - ((node === null || node === void 0 ? void 0 : node.clientHeight) || 0) + (titleHeight || 0);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case 'top-left': {
|
|
95
|
+
style.left = boundsOffsetLeft;
|
|
96
|
+
style.top = boundsOffsetTop;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case 'top-right': {
|
|
100
|
+
style.left = boundsWidth + boundsOffsetLeft - ((node === null || node === void 0 ? void 0 : node.clientWidth) || 0);
|
|
101
|
+
style.top = boundsOffsetTop;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return style;
|
|
109
|
+
}
|
|
@@ -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
|
};
|