@gravity-ui/charts 1.21.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/ChartInner/index.js +7 -3
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +16 -2
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +2 -3
- package/dist/cjs/components/ChartInner/utils.d.ts +10 -1
- package/dist/cjs/components/ChartInner/utils.js +60 -0
- package/dist/cjs/hooks/useChartOptions/chart.js +6 -1
- package/dist/cjs/types/chart/zoom.d.ts +29 -0
- package/dist/cjs/utils/chart/text.js +24 -21
- package/dist/esm/components/ChartInner/index.js +7 -3
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +16 -2
- package/dist/esm/components/ChartInner/useChartInnerProps.js +2 -3
- package/dist/esm/components/ChartInner/utils.d.ts +10 -1
- package/dist/esm/components/ChartInner/utils.js +60 -0
- package/dist/esm/hooks/useChartOptions/chart.js +6 -1
- package/dist/esm/types/chart/zoom.d.ts +29 -0
- package/dist/esm/utils/chart/text.js +24 -21
- package/package.json +1 -1
|
@@ -14,13 +14,14 @@ import { Tooltip } from '../Tooltip';
|
|
|
14
14
|
import { useChartInnerHandlers } from './useChartInnerHandlers';
|
|
15
15
|
import { useChartInnerProps } from './useChartInnerProps';
|
|
16
16
|
import { useChartInnerState } from './useChartInnerState';
|
|
17
|
-
import { useAsyncState } from './utils';
|
|
17
|
+
import { getResetZoomButtonStyle, useAsyncState } from './utils';
|
|
18
18
|
import './styles.css';
|
|
19
19
|
const b = block('chart');
|
|
20
20
|
export const ChartInner = (props) => {
|
|
21
21
|
var _a, _b, _c, _d;
|
|
22
22
|
const { width, height, data } = props;
|
|
23
23
|
const svgRef = React.useRef(null);
|
|
24
|
+
const resetZoomButtonRef = React.useRef(null);
|
|
24
25
|
const [htmlLayout, setHtmlLayout] = React.useState(null);
|
|
25
26
|
const plotRef = React.useRef(null);
|
|
26
27
|
const plotBeforeRef = React.useRef(null);
|
|
@@ -39,7 +40,7 @@ export const ChartInner = (props) => {
|
|
|
39
40
|
dispatcher,
|
|
40
41
|
tooltip: preparedTooltip,
|
|
41
42
|
});
|
|
42
|
-
const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, isOutsideBounds, legendConfig, legendItems, preparedSeries, preparedSplit, preparedLegend, prevHeight, prevWidth, shapes, shapesData, title, xAxis, xScale, yAxis, yScale,
|
|
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,
|
|
43
44
|
dispatcher,
|
|
44
45
|
htmlLayout, plotNode: plotRef.current, svgContainer: svgRef.current, updateZoomState,
|
|
45
46
|
zoomState }));
|
|
@@ -143,7 +144,10 @@ export const ChartInner = (props) => {
|
|
|
143
144
|
React.createElement("div", { className: b('html-layer'), ref: setHtmlLayout, style: {
|
|
144
145
|
'--g-html-layout-transform': `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
|
|
145
146
|
} }),
|
|
146
|
-
Object.keys(zoomState).length > 0 && (React.createElement(Button, {
|
|
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)) },
|
|
147
151
|
React.createElement(ButtonIcon, null,
|
|
148
152
|
React.createElement(ArrowRotateLeft, null)))),
|
|
149
153
|
React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: preparedTooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0], onOutsideClick: unpinTooltip, tooltipPinned: tooltipPinned })));
|
|
@@ -12,8 +12,6 @@ type Props = ChartInnerProps & {
|
|
|
12
12
|
zoomState: Partial<ZoomState>;
|
|
13
13
|
};
|
|
14
14
|
export declare function useChartInnerProps(props: Props): {
|
|
15
|
-
svgBottomPos: number | undefined;
|
|
16
|
-
svgTopPos: number | undefined;
|
|
17
15
|
svgXPos: number | undefined;
|
|
18
16
|
boundsHeight: number;
|
|
19
17
|
boundsOffsetLeft: number;
|
|
@@ -38,6 +36,22 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
38
36
|
preparedLegend: import("../../hooks").PreparedLegend | null;
|
|
39
37
|
preparedSeries: import("../../hooks").PreparedSeries[];
|
|
40
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;
|
|
41
55
|
prevHeight: number | undefined;
|
|
42
56
|
prevWidth: number | undefined;
|
|
43
57
|
shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
|
|
@@ -138,10 +138,8 @@ export function useChartInnerProps(props) {
|
|
|
138
138
|
}
|
|
139
139
|
return acc;
|
|
140
140
|
}, 0);
|
|
141
|
-
const {
|
|
141
|
+
const { x } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
|
|
142
142
|
return {
|
|
143
|
-
svgBottomPos: bottom,
|
|
144
|
-
svgTopPos: top,
|
|
145
143
|
svgXPos: x,
|
|
146
144
|
boundsHeight,
|
|
147
145
|
boundsOffsetLeft,
|
|
@@ -154,6 +152,7 @@ export function useChartInnerProps(props) {
|
|
|
154
152
|
preparedLegend,
|
|
155
153
|
preparedSeries,
|
|
156
154
|
preparedSplit,
|
|
155
|
+
preparedZoom: chart.zoom,
|
|
157
156
|
prevHeight,
|
|
158
157
|
prevWidth,
|
|
159
158
|
shapes,
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import type { PreparedSeries } from '../../hooks';
|
|
2
|
-
import type { PreparedAxis } from '../../hooks/useChartOptions/types';
|
|
3
|
+
import type { PreparedAxis, PreparedZoom } from '../../hooks/useChartOptions/types';
|
|
3
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;
|
|
@@ -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
|
+
}
|
|
@@ -72,7 +72,7 @@ function getZoomType(args) {
|
|
|
72
72
|
return undefined;
|
|
73
73
|
}
|
|
74
74
|
function getPreparedZoom(args) {
|
|
75
|
-
var _a;
|
|
75
|
+
var _a, _b, _c, _d;
|
|
76
76
|
const { zoom, seriesData } = args;
|
|
77
77
|
if (!(zoom === null || zoom === void 0 ? void 0 : zoom.enabled)) {
|
|
78
78
|
return null;
|
|
@@ -86,6 +86,11 @@ function getPreparedZoom(args) {
|
|
|
86
86
|
brush: {
|
|
87
87
|
style: Object.assign({ fillOpacity: 1 }, (_a = zoom === null || zoom === void 0 ? void 0 : zoom.brush) === null || _a === void 0 ? void 0 : _a.style),
|
|
88
88
|
},
|
|
89
|
+
resetButton: {
|
|
90
|
+
align: ((_b = zoom === null || zoom === void 0 ? void 0 : zoom.resetButton) === null || _b === void 0 ? void 0 : _b.align) || 'top-right',
|
|
91
|
+
offset: Object.assign({ x: 0, y: 0 }, (_c = zoom === null || zoom === void 0 ? void 0 : zoom.resetButton) === null || _c === void 0 ? void 0 : _c.offset),
|
|
92
|
+
relativeTo: ((_d = zoom === null || zoom === void 0 ? void 0 : zoom.resetButton) === null || _d === void 0 ? void 0 : _d.relativeTo) || 'chart-box',
|
|
93
|
+
},
|
|
89
94
|
};
|
|
90
95
|
}
|
|
91
96
|
export const getPreparedChart = (args) => {
|
|
@@ -33,4 +33,33 @@ export interface ChartZoom {
|
|
|
33
33
|
fillOpacity?: number;
|
|
34
34
|
};
|
|
35
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* Reset zoom button configuration.
|
|
38
|
+
* The button appears only after the zoom has been applied.
|
|
39
|
+
*/
|
|
40
|
+
resetButton?: {
|
|
41
|
+
/**
|
|
42
|
+
* The alignment of the button.
|
|
43
|
+
*
|
|
44
|
+
* @default 'top-right'
|
|
45
|
+
*/
|
|
46
|
+
align?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';
|
|
47
|
+
/**
|
|
48
|
+
* The offset of the button.
|
|
49
|
+
*
|
|
50
|
+
* @default {x: 0, y: 0}
|
|
51
|
+
*/
|
|
52
|
+
offset?: {
|
|
53
|
+
x?: number;
|
|
54
|
+
y?: number;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* The box to which the button is positioned relative to.
|
|
58
|
+
* - `chart-box` refers to the entire chart area, including titles and legends.
|
|
59
|
+
* - `plot-box` refers to the area where the series are drawn.
|
|
60
|
+
*
|
|
61
|
+
* @default 'chart-box'
|
|
62
|
+
*/
|
|
63
|
+
relativeTo?: 'chart-box' | 'plot-box';
|
|
64
|
+
};
|
|
36
65
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { select } from 'd3-selection';
|
|
2
|
+
import { block } from '../cn';
|
|
3
|
+
const b = block('chart');
|
|
2
4
|
export function handleOverflowingText(tSpan, maxWidth, textWidth) {
|
|
3
5
|
var _a, _b, _c;
|
|
4
6
|
if (!tSpan) {
|
|
@@ -186,29 +188,30 @@ function unescapeHtml(str) {
|
|
|
186
188
|
return result.replace(value, key);
|
|
187
189
|
}, str);
|
|
188
190
|
}
|
|
191
|
+
function getCssStyle(prop, el = document.body) {
|
|
192
|
+
return window.getComputedStyle(el, null).getPropertyValue(prop);
|
|
193
|
+
}
|
|
194
|
+
let measureCanvas = null;
|
|
189
195
|
export function getTextSizeFn({ style }) {
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
var _a;
|
|
197
|
+
const canvas = measureCanvas || (measureCanvas = document.createElement('canvas'));
|
|
198
|
+
const context = canvas.getContext('2d');
|
|
199
|
+
if (!context) {
|
|
200
|
+
throw new Error("Couldn't get canvas context");
|
|
201
|
+
}
|
|
202
|
+
const element = (_a = document.getElementsByClassName(b())[0]) !== null && _a !== void 0 ? _a : document.body;
|
|
203
|
+
const defaultFontFamily = getCssStyle('font-family', element);
|
|
204
|
+
const defaultFontSize = getCssStyle('font-size', element);
|
|
205
|
+
const defaultFontWeight = getCssStyle('font-weight', element);
|
|
199
206
|
return async (str) => {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
width += map[s].width;
|
|
209
|
-
height = Math.max(height, map[s].height);
|
|
210
|
-
}
|
|
211
|
-
return { width, height };
|
|
207
|
+
var _a, _b;
|
|
208
|
+
await document.fonts.ready;
|
|
209
|
+
context.font = `${(_a = style === null || style === void 0 ? void 0 : style.fontWeight) !== null && _a !== void 0 ? _a : defaultFontWeight} ${(_b = style === null || style === void 0 ? void 0 : style.fontSize) !== null && _b !== void 0 ? _b : defaultFontSize} ${defaultFontFamily}`;
|
|
210
|
+
const textMetric = context.measureText(unescapeHtml(str));
|
|
211
|
+
return {
|
|
212
|
+
width: textMetric.width,
|
|
213
|
+
height: textMetric.fontBoundingBoxDescent + textMetric.fontBoundingBoxAscent,
|
|
214
|
+
};
|
|
212
215
|
};
|
|
213
216
|
}
|
|
214
217
|
// We ignore an inaccuracy of less than a pixel.
|
|
@@ -14,13 +14,14 @@ import { Tooltip } from '../Tooltip';
|
|
|
14
14
|
import { useChartInnerHandlers } from './useChartInnerHandlers';
|
|
15
15
|
import { useChartInnerProps } from './useChartInnerProps';
|
|
16
16
|
import { useChartInnerState } from './useChartInnerState';
|
|
17
|
-
import { useAsyncState } from './utils';
|
|
17
|
+
import { getResetZoomButtonStyle, useAsyncState } from './utils';
|
|
18
18
|
import './styles.css';
|
|
19
19
|
const b = block('chart');
|
|
20
20
|
export const ChartInner = (props) => {
|
|
21
21
|
var _a, _b, _c, _d;
|
|
22
22
|
const { width, height, data } = props;
|
|
23
23
|
const svgRef = React.useRef(null);
|
|
24
|
+
const resetZoomButtonRef = React.useRef(null);
|
|
24
25
|
const [htmlLayout, setHtmlLayout] = React.useState(null);
|
|
25
26
|
const plotRef = React.useRef(null);
|
|
26
27
|
const plotBeforeRef = React.useRef(null);
|
|
@@ -39,7 +40,7 @@ export const ChartInner = (props) => {
|
|
|
39
40
|
dispatcher,
|
|
40
41
|
tooltip: preparedTooltip,
|
|
41
42
|
});
|
|
42
|
-
const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, isOutsideBounds, legendConfig, legendItems, preparedSeries, preparedSplit, preparedLegend, prevHeight, prevWidth, shapes, shapesData, title, xAxis, xScale, yAxis, yScale,
|
|
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,
|
|
43
44
|
dispatcher,
|
|
44
45
|
htmlLayout, plotNode: plotRef.current, svgContainer: svgRef.current, updateZoomState,
|
|
45
46
|
zoomState }));
|
|
@@ -143,7 +144,10 @@ export const ChartInner = (props) => {
|
|
|
143
144
|
React.createElement("div", { className: b('html-layer'), ref: setHtmlLayout, style: {
|
|
144
145
|
'--g-html-layout-transform': `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
|
|
145
146
|
} }),
|
|
146
|
-
Object.keys(zoomState).length > 0 && (React.createElement(Button, {
|
|
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)) },
|
|
147
151
|
React.createElement(ButtonIcon, null,
|
|
148
152
|
React.createElement(ArrowRotateLeft, null)))),
|
|
149
153
|
React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: preparedTooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0], onOutsideClick: unpinTooltip, tooltipPinned: tooltipPinned })));
|
|
@@ -12,8 +12,6 @@ type Props = ChartInnerProps & {
|
|
|
12
12
|
zoomState: Partial<ZoomState>;
|
|
13
13
|
};
|
|
14
14
|
export declare function useChartInnerProps(props: Props): {
|
|
15
|
-
svgBottomPos: number | undefined;
|
|
16
|
-
svgTopPos: number | undefined;
|
|
17
15
|
svgXPos: number | undefined;
|
|
18
16
|
boundsHeight: number;
|
|
19
17
|
boundsOffsetLeft: number;
|
|
@@ -38,6 +36,22 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
38
36
|
preparedLegend: import("../../hooks").PreparedLegend | null;
|
|
39
37
|
preparedSeries: import("../../hooks").PreparedSeries[];
|
|
40
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;
|
|
41
55
|
prevHeight: number | undefined;
|
|
42
56
|
prevWidth: number | undefined;
|
|
43
57
|
shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
|
|
@@ -138,10 +138,8 @@ export function useChartInnerProps(props) {
|
|
|
138
138
|
}
|
|
139
139
|
return acc;
|
|
140
140
|
}, 0);
|
|
141
|
-
const {
|
|
141
|
+
const { x } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
|
|
142
142
|
return {
|
|
143
|
-
svgBottomPos: bottom,
|
|
144
|
-
svgTopPos: top,
|
|
145
143
|
svgXPos: x,
|
|
146
144
|
boundsHeight,
|
|
147
145
|
boundsOffsetLeft,
|
|
@@ -154,6 +152,7 @@ export function useChartInnerProps(props) {
|
|
|
154
152
|
preparedLegend,
|
|
155
153
|
preparedSeries,
|
|
156
154
|
preparedSplit,
|
|
155
|
+
preparedZoom: chart.zoom,
|
|
157
156
|
prevHeight,
|
|
158
157
|
prevWidth,
|
|
159
158
|
shapes,
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import type { PreparedSeries } from '../../hooks';
|
|
2
|
-
import type { PreparedAxis } from '../../hooks/useChartOptions/types';
|
|
3
|
+
import type { PreparedAxis, PreparedZoom } from '../../hooks/useChartOptions/types';
|
|
3
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;
|
|
@@ -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
|
+
}
|
|
@@ -72,7 +72,7 @@ function getZoomType(args) {
|
|
|
72
72
|
return undefined;
|
|
73
73
|
}
|
|
74
74
|
function getPreparedZoom(args) {
|
|
75
|
-
var _a;
|
|
75
|
+
var _a, _b, _c, _d;
|
|
76
76
|
const { zoom, seriesData } = args;
|
|
77
77
|
if (!(zoom === null || zoom === void 0 ? void 0 : zoom.enabled)) {
|
|
78
78
|
return null;
|
|
@@ -86,6 +86,11 @@ function getPreparedZoom(args) {
|
|
|
86
86
|
brush: {
|
|
87
87
|
style: Object.assign({ fillOpacity: 1 }, (_a = zoom === null || zoom === void 0 ? void 0 : zoom.brush) === null || _a === void 0 ? void 0 : _a.style),
|
|
88
88
|
},
|
|
89
|
+
resetButton: {
|
|
90
|
+
align: ((_b = zoom === null || zoom === void 0 ? void 0 : zoom.resetButton) === null || _b === void 0 ? void 0 : _b.align) || 'top-right',
|
|
91
|
+
offset: Object.assign({ x: 0, y: 0 }, (_c = zoom === null || zoom === void 0 ? void 0 : zoom.resetButton) === null || _c === void 0 ? void 0 : _c.offset),
|
|
92
|
+
relativeTo: ((_d = zoom === null || zoom === void 0 ? void 0 : zoom.resetButton) === null || _d === void 0 ? void 0 : _d.relativeTo) || 'chart-box',
|
|
93
|
+
},
|
|
89
94
|
};
|
|
90
95
|
}
|
|
91
96
|
export const getPreparedChart = (args) => {
|
|
@@ -33,4 +33,33 @@ export interface ChartZoom {
|
|
|
33
33
|
fillOpacity?: number;
|
|
34
34
|
};
|
|
35
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* Reset zoom button configuration.
|
|
38
|
+
* The button appears only after the zoom has been applied.
|
|
39
|
+
*/
|
|
40
|
+
resetButton?: {
|
|
41
|
+
/**
|
|
42
|
+
* The alignment of the button.
|
|
43
|
+
*
|
|
44
|
+
* @default 'top-right'
|
|
45
|
+
*/
|
|
46
|
+
align?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';
|
|
47
|
+
/**
|
|
48
|
+
* The offset of the button.
|
|
49
|
+
*
|
|
50
|
+
* @default {x: 0, y: 0}
|
|
51
|
+
*/
|
|
52
|
+
offset?: {
|
|
53
|
+
x?: number;
|
|
54
|
+
y?: number;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* The box to which the button is positioned relative to.
|
|
58
|
+
* - `chart-box` refers to the entire chart area, including titles and legends.
|
|
59
|
+
* - `plot-box` refers to the area where the series are drawn.
|
|
60
|
+
*
|
|
61
|
+
* @default 'chart-box'
|
|
62
|
+
*/
|
|
63
|
+
relativeTo?: 'chart-box' | 'plot-box';
|
|
64
|
+
};
|
|
36
65
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { select } from 'd3-selection';
|
|
2
|
+
import { block } from '../cn';
|
|
3
|
+
const b = block('chart');
|
|
2
4
|
export function handleOverflowingText(tSpan, maxWidth, textWidth) {
|
|
3
5
|
var _a, _b, _c;
|
|
4
6
|
if (!tSpan) {
|
|
@@ -186,29 +188,30 @@ function unescapeHtml(str) {
|
|
|
186
188
|
return result.replace(value, key);
|
|
187
189
|
}, str);
|
|
188
190
|
}
|
|
191
|
+
function getCssStyle(prop, el = document.body) {
|
|
192
|
+
return window.getComputedStyle(el, null).getPropertyValue(prop);
|
|
193
|
+
}
|
|
194
|
+
let measureCanvas = null;
|
|
189
195
|
export function getTextSizeFn({ style }) {
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
var _a;
|
|
197
|
+
const canvas = measureCanvas || (measureCanvas = document.createElement('canvas'));
|
|
198
|
+
const context = canvas.getContext('2d');
|
|
199
|
+
if (!context) {
|
|
200
|
+
throw new Error("Couldn't get canvas context");
|
|
201
|
+
}
|
|
202
|
+
const element = (_a = document.getElementsByClassName(b())[0]) !== null && _a !== void 0 ? _a : document.body;
|
|
203
|
+
const defaultFontFamily = getCssStyle('font-family', element);
|
|
204
|
+
const defaultFontSize = getCssStyle('font-size', element);
|
|
205
|
+
const defaultFontWeight = getCssStyle('font-weight', element);
|
|
199
206
|
return async (str) => {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
width += map[s].width;
|
|
209
|
-
height = Math.max(height, map[s].height);
|
|
210
|
-
}
|
|
211
|
-
return { width, height };
|
|
207
|
+
var _a, _b;
|
|
208
|
+
await document.fonts.ready;
|
|
209
|
+
context.font = `${(_a = style === null || style === void 0 ? void 0 : style.fontWeight) !== null && _a !== void 0 ? _a : defaultFontWeight} ${(_b = style === null || style === void 0 ? void 0 : style.fontSize) !== null && _b !== void 0 ? _b : defaultFontSize} ${defaultFontFamily}`;
|
|
210
|
+
const textMetric = context.measureText(unescapeHtml(str));
|
|
211
|
+
return {
|
|
212
|
+
width: textMetric.width,
|
|
213
|
+
height: textMetric.fontBoundingBoxDescent + textMetric.fontBoundingBoxAscent,
|
|
214
|
+
};
|
|
212
215
|
};
|
|
213
216
|
}
|
|
214
217
|
// We ignore an inaccuracy of less than a pixel.
|