@gravity-ui/charts 0.4.1 → 0.6.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.d.ts +2 -8
- package/dist/cjs/components/ChartInner/index.js +22 -117
- package/dist/cjs/components/ChartInner/types.d.ts +6 -0
- package/dist/cjs/components/ChartInner/useChartInnerHandlers.d.ts +26 -0
- package/dist/cjs/components/ChartInner/useChartInnerHandlers.js +94 -0
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +43 -0
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +81 -0
- package/dist/cjs/components/ChartInner/useChartInnerState.d.ts +13 -0
- package/dist/cjs/components/ChartInner/useChartInnerState.js +34 -0
- package/dist/cjs/components/Legend/index.d.ts +1 -0
- package/dist/cjs/components/Legend/index.js +26 -6
- package/dist/cjs/components/Tooltip/index.d.ts +2 -0
- package/dist/cjs/components/Tooltip/index.js +9 -3
- package/dist/cjs/components/Tooltip/styles.css +3 -0
- package/dist/cjs/hooks/index.d.ts +2 -1
- package/dist/cjs/hooks/index.js +2 -1
- package/dist/cjs/hooks/usePrevious/index.d.ts +1 -0
- package/dist/cjs/hooks/usePrevious/index.js +8 -0
- package/dist/cjs/hooks/useSeries/prepare-legend.js +1 -0
- package/dist/cjs/hooks/useSeries/types.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/area/index.js +8 -2
- package/dist/cjs/hooks/useShapes/bar-x/index.js +8 -2
- package/dist/cjs/hooks/useShapes/bar-y/index.js +8 -2
- package/dist/cjs/hooks/useShapes/line/index.js +8 -2
- package/dist/cjs/hooks/useShapes/scatter/index.js +8 -2
- package/dist/cjs/hooks/useShapes/treemap/index.js +8 -2
- package/dist/cjs/hooks/useShapes/utils.d.ts +2 -2
- package/dist/cjs/hooks/useShapes/utils.js +5 -3
- package/dist/cjs/hooks/useShapes/waterfall/index.js +8 -2
- package/dist/cjs/hooks/useTooltip/index.d.ts +2 -3
- package/dist/cjs/types/chart/chart.d.ts +2 -2
- package/dist/cjs/types/chart/legend.d.ts +2 -0
- package/dist/cjs/types/chart/series.d.ts +1 -2
- package/dist/cjs/types/chart/tooltip.d.ts +6 -6
- package/dist/cjs/types/misc.d.ts +1 -0
- package/dist/cjs/utils/misc.d.ts +10 -2
- package/dist/cjs/utils/misc.js +15 -3
- package/dist/esm/components/ChartInner/index.d.ts +2 -8
- package/dist/esm/components/ChartInner/index.js +22 -117
- package/dist/esm/components/ChartInner/types.d.ts +6 -0
- package/dist/esm/components/ChartInner/useChartInnerHandlers.d.ts +26 -0
- package/dist/esm/components/ChartInner/useChartInnerHandlers.js +94 -0
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +43 -0
- package/dist/esm/components/ChartInner/useChartInnerProps.js +81 -0
- package/dist/esm/components/ChartInner/useChartInnerState.d.ts +13 -0
- package/dist/esm/components/ChartInner/useChartInnerState.js +34 -0
- package/dist/esm/components/Legend/index.d.ts +1 -0
- package/dist/esm/components/Legend/index.js +26 -6
- package/dist/esm/components/Tooltip/index.d.ts +2 -0
- package/dist/esm/components/Tooltip/index.js +9 -3
- package/dist/esm/components/Tooltip/styles.css +3 -0
- package/dist/esm/hooks/index.d.ts +2 -1
- package/dist/esm/hooks/index.js +2 -1
- package/dist/esm/hooks/usePrevious/index.d.ts +1 -0
- package/dist/esm/hooks/usePrevious/index.js +8 -0
- package/dist/esm/hooks/useSeries/prepare-legend.js +1 -0
- package/dist/esm/hooks/useSeries/types.d.ts +1 -0
- package/dist/esm/hooks/useShapes/area/index.js +8 -2
- package/dist/esm/hooks/useShapes/bar-x/index.js +8 -2
- package/dist/esm/hooks/useShapes/bar-y/index.js +8 -2
- package/dist/esm/hooks/useShapes/line/index.js +8 -2
- package/dist/esm/hooks/useShapes/scatter/index.js +8 -2
- package/dist/esm/hooks/useShapes/treemap/index.js +8 -2
- package/dist/esm/hooks/useShapes/utils.d.ts +2 -2
- package/dist/esm/hooks/useShapes/utils.js +5 -3
- package/dist/esm/hooks/useShapes/waterfall/index.js +8 -2
- package/dist/esm/hooks/useTooltip/index.d.ts +2 -3
- package/dist/esm/types/chart/chart.d.ts +2 -2
- package/dist/esm/types/chart/legend.d.ts +2 -0
- package/dist/esm/types/chart/series.d.ts +1 -2
- package/dist/esm/types/chart/tooltip.d.ts +6 -6
- package/dist/esm/types/misc.d.ts +1 -0
- package/dist/esm/utils/misc.d.ts +10 -2
- package/dist/esm/utils/misc.js +15 -3
- package/package.json +1 -1
- package/dist/cjs/hooks/useTooltip/types.d.ts +0 -1
- package/dist/esm/hooks/useTooltip/types.d.ts +0 -1
- /package/dist/cjs/{hooks/useTooltip → components/ChartInner}/types.js +0 -0
- /package/dist/esm/{hooks/useTooltip → components/ChartInner}/types.js +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Dispatch } from 'd3';
|
|
3
|
+
import type { PreparedAxis, ShapeData } from '../../hooks';
|
|
4
|
+
import type { useChartInnerState } from './useChartInnerState';
|
|
5
|
+
type ChartInnerState = ReturnType<typeof useChartInnerState>;
|
|
6
|
+
type Props = {
|
|
7
|
+
boundsHeight: number;
|
|
8
|
+
boundsOffsetLeft: number;
|
|
9
|
+
boundsOffsetTop: number;
|
|
10
|
+
boundsWidth: number;
|
|
11
|
+
dispatcher: Dispatch<object>;
|
|
12
|
+
shapesData: ShapeData[];
|
|
13
|
+
svgContainer: SVGSVGElement | null;
|
|
14
|
+
togglePinTooltip: ChartInnerState['togglePinTooltip'];
|
|
15
|
+
tooltipPinned: boolean;
|
|
16
|
+
unpinTooltip: ChartInnerState['unpinTooltip'];
|
|
17
|
+
xAxis: PreparedAxis;
|
|
18
|
+
yAxis: PreparedAxis[];
|
|
19
|
+
};
|
|
20
|
+
export declare function useChartInnerHandlers(props: Props): {
|
|
21
|
+
handleChartClick: (event: React.MouseEvent<SVGSVGElement>) => void;
|
|
22
|
+
handleMouseLeave: React.MouseEventHandler<SVGSVGElement>;
|
|
23
|
+
throttledHandleMouseMove: import("lodash").DebouncedFuncLeading<React.MouseEventHandler<SVGSVGElement>> | undefined;
|
|
24
|
+
throttledHandleTouchMove: import("lodash").DebouncedFuncLeading<React.TouchEventHandler<SVGSVGElement>> | undefined;
|
|
25
|
+
};
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { pointer } from 'd3';
|
|
3
|
+
import throttle from 'lodash/throttle';
|
|
4
|
+
import { IS_TOUCH_ENABLED } from '../../constants';
|
|
5
|
+
import { EventType } from '../../utils';
|
|
6
|
+
import { getClosestPoints } from '../../utils/chart/get-closest-data';
|
|
7
|
+
const THROTTLE_DELAY = 50;
|
|
8
|
+
export function useChartInnerHandlers(props) {
|
|
9
|
+
const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, dispatcher, shapesData, svgContainer, togglePinTooltip, tooltipPinned, unpinTooltip, xAxis, yAxis, } = props;
|
|
10
|
+
const isOutsideBounds = React.useCallback((x, y) => {
|
|
11
|
+
return x < 0 || x > boundsWidth || y < 0 || y > boundsHeight;
|
|
12
|
+
}, [boundsHeight, boundsWidth]);
|
|
13
|
+
const handleMove = ([pointerX, pointerY], event) => {
|
|
14
|
+
if (tooltipPinned) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const x = pointerX - boundsOffsetLeft;
|
|
18
|
+
const y = pointerY - boundsOffsetTop;
|
|
19
|
+
if (isOutsideBounds(x, y)) {
|
|
20
|
+
dispatcher.call(EventType.HOVER_SHAPE, {}, undefined);
|
|
21
|
+
dispatcher.call(EventType.POINTERMOVE_CHART, {}, undefined, event);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const closest = getClosestPoints({
|
|
25
|
+
position: [x, y],
|
|
26
|
+
shapesData,
|
|
27
|
+
});
|
|
28
|
+
dispatcher.call(EventType.HOVER_SHAPE, event.target, closest, [pointerX, pointerY]);
|
|
29
|
+
dispatcher.call(EventType.POINTERMOVE_CHART, {}, {
|
|
30
|
+
hovered: closest,
|
|
31
|
+
xAxis,
|
|
32
|
+
yAxis: yAxis[0],
|
|
33
|
+
}, event);
|
|
34
|
+
};
|
|
35
|
+
const handleMouseMove = (event) => {
|
|
36
|
+
const [pointerX, pointerY] = pointer(event, svgContainer);
|
|
37
|
+
handleMove([pointerX, pointerY], event);
|
|
38
|
+
};
|
|
39
|
+
const throttledHandleMouseMove = IS_TOUCH_ENABLED
|
|
40
|
+
? undefined
|
|
41
|
+
: throttle(handleMouseMove, THROTTLE_DELAY);
|
|
42
|
+
const handleMouseLeave = (event) => {
|
|
43
|
+
if (tooltipPinned) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
throttledHandleMouseMove === null || throttledHandleMouseMove === void 0 ? void 0 : throttledHandleMouseMove.cancel();
|
|
47
|
+
dispatcher.call(EventType.HOVER_SHAPE, {}, undefined);
|
|
48
|
+
dispatcher.call(EventType.POINTERMOVE_CHART, {}, undefined, event);
|
|
49
|
+
};
|
|
50
|
+
const handleTouchMove = (event) => {
|
|
51
|
+
const touch = event.touches[0];
|
|
52
|
+
const [pointerX, pointerY] = pointer(touch, svgContainer);
|
|
53
|
+
handleMove([pointerX, pointerY], event);
|
|
54
|
+
};
|
|
55
|
+
const throttledHandleTouchMove = IS_TOUCH_ENABLED
|
|
56
|
+
? throttle(handleTouchMove, THROTTLE_DELAY)
|
|
57
|
+
: undefined;
|
|
58
|
+
const handleChartClick = (event) => {
|
|
59
|
+
const [pointerX, pointerY] = pointer(event, svgContainer);
|
|
60
|
+
const x = pointerX - boundsOffsetLeft;
|
|
61
|
+
const y = pointerY - boundsOffsetTop;
|
|
62
|
+
if (isOutsideBounds(x, y)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const items = getClosestPoints({
|
|
66
|
+
position: [x, y],
|
|
67
|
+
shapesData,
|
|
68
|
+
});
|
|
69
|
+
const selected = items === null || items === void 0 ? void 0 : items.find((item) => item.closest);
|
|
70
|
+
if (!selected) {
|
|
71
|
+
if (tooltipPinned) {
|
|
72
|
+
unpinTooltip === null || unpinTooltip === void 0 ? void 0 : unpinTooltip();
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
dispatcher.call(EventType.CLICK_CHART, undefined, { point: selected.data, series: selected.series }, event);
|
|
77
|
+
const nextTooltipFixed = !tooltipPinned;
|
|
78
|
+
if (!nextTooltipFixed) {
|
|
79
|
+
dispatcher.call(EventType.HOVER_SHAPE, event.target, items, [pointerX, pointerY]);
|
|
80
|
+
dispatcher.call(EventType.POINTERMOVE_CHART, {}, {
|
|
81
|
+
hovered: items,
|
|
82
|
+
xAxis,
|
|
83
|
+
yAxis: yAxis[0],
|
|
84
|
+
}, event);
|
|
85
|
+
}
|
|
86
|
+
togglePinTooltip === null || togglePinTooltip === void 0 ? void 0 : togglePinTooltip(nextTooltipFixed, event);
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
handleChartClick,
|
|
90
|
+
handleMouseLeave,
|
|
91
|
+
throttledHandleMouseMove,
|
|
92
|
+
throttledHandleTouchMove,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Dispatch } from 'd3';
|
|
3
|
+
import type { ChartInnerProps } from './types';
|
|
4
|
+
type Props = ChartInnerProps & {
|
|
5
|
+
dispatcher: Dispatch<object>;
|
|
6
|
+
htmlLayout: HTMLElement | null;
|
|
7
|
+
};
|
|
8
|
+
export declare function useChartInnerProps(props: Props): {
|
|
9
|
+
boundsHeight: number;
|
|
10
|
+
boundsOffsetLeft: number;
|
|
11
|
+
boundsOffsetTop: number;
|
|
12
|
+
boundsWidth: number;
|
|
13
|
+
handleLegendItemClick: import("../../hooks").OnLegendItemClick;
|
|
14
|
+
legendConfig: {
|
|
15
|
+
offset: {
|
|
16
|
+
left: number;
|
|
17
|
+
top: number;
|
|
18
|
+
};
|
|
19
|
+
pagination: {
|
|
20
|
+
limit: number;
|
|
21
|
+
maxPage: number;
|
|
22
|
+
} | undefined;
|
|
23
|
+
};
|
|
24
|
+
legendItems: import("../../hooks").LegendItem[][];
|
|
25
|
+
preparedLegend: import("../../hooks").PreparedLegend;
|
|
26
|
+
preparedSeries: import("../../hooks").PreparedSeries[];
|
|
27
|
+
preparedSplit: import("../../hooks").PreparedSplit;
|
|
28
|
+
prevHeight: number | undefined;
|
|
29
|
+
prevWidth: number | undefined;
|
|
30
|
+
shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
|
|
31
|
+
shapesData: import("../../hooks").ShapeData[];
|
|
32
|
+
title: (import("../../types").ChartTitle & {
|
|
33
|
+
height: number;
|
|
34
|
+
}) | undefined;
|
|
35
|
+
tooltip: import("../../types").ChartTooltip<any> & {
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
};
|
|
38
|
+
xAxis: import("../../hooks").PreparedAxis;
|
|
39
|
+
xScale: import("../../hooks").ChartScale | undefined;
|
|
40
|
+
yAxis: import("../../hooks").PreparedAxis[];
|
|
41
|
+
yScale: import("../../hooks").ChartScale[] | undefined;
|
|
42
|
+
};
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useAxisScales, useChartDimensions, useChartOptions, usePrevious, useSeries, useShapes, useSplit, } from '../../hooks';
|
|
3
|
+
import { getYAxisWidth } from '../../hooks/useChartDimensions/utils';
|
|
4
|
+
import { getPreparedXAxis } from '../../hooks/useChartOptions/x-axis';
|
|
5
|
+
import { getPreparedYAxis } from '../../hooks/useChartOptions/y-axis';
|
|
6
|
+
export function useChartInnerProps(props) {
|
|
7
|
+
const { width, height, data, dispatcher, htmlLayout } = props;
|
|
8
|
+
const prevWidth = usePrevious(width);
|
|
9
|
+
const prevHeight = usePrevious(height);
|
|
10
|
+
const { chart, title, tooltip } = useChartOptions({ data });
|
|
11
|
+
const xAxis = React.useMemo(() => getPreparedXAxis({ xAxis: data.xAxis, width, series: data.series.data }), [data, width]);
|
|
12
|
+
const yAxis = React.useMemo(() => getPreparedYAxis({
|
|
13
|
+
series: data.series.data,
|
|
14
|
+
yAxis: data.yAxis,
|
|
15
|
+
height,
|
|
16
|
+
}), [data, height]);
|
|
17
|
+
const { legendItems, legendConfig, preparedSeries, preparedSeriesOptions, preparedLegend, handleLegendItemClick, } = useSeries({
|
|
18
|
+
chartWidth: width,
|
|
19
|
+
chartHeight: height,
|
|
20
|
+
chartMargin: chart.margin,
|
|
21
|
+
series: data.series,
|
|
22
|
+
legend: data.legend,
|
|
23
|
+
preparedYAxis: yAxis,
|
|
24
|
+
});
|
|
25
|
+
const { boundsWidth, boundsHeight } = useChartDimensions({
|
|
26
|
+
width,
|
|
27
|
+
height,
|
|
28
|
+
margin: chart.margin,
|
|
29
|
+
preparedLegend,
|
|
30
|
+
preparedXAxis: xAxis,
|
|
31
|
+
preparedYAxis: yAxis,
|
|
32
|
+
preparedSeries: preparedSeries,
|
|
33
|
+
});
|
|
34
|
+
const preparedSplit = useSplit({ split: data.split, boundsHeight, chartWidth: width });
|
|
35
|
+
const { xScale, yScale } = useAxisScales({
|
|
36
|
+
boundsWidth,
|
|
37
|
+
boundsHeight,
|
|
38
|
+
series: preparedSeries,
|
|
39
|
+
xAxis,
|
|
40
|
+
yAxis,
|
|
41
|
+
split: preparedSplit,
|
|
42
|
+
});
|
|
43
|
+
const { shapes, shapesData } = useShapes({
|
|
44
|
+
boundsWidth,
|
|
45
|
+
boundsHeight,
|
|
46
|
+
dispatcher,
|
|
47
|
+
series: preparedSeries,
|
|
48
|
+
seriesOptions: preparedSeriesOptions,
|
|
49
|
+
xAxis,
|
|
50
|
+
xScale,
|
|
51
|
+
yAxis,
|
|
52
|
+
yScale,
|
|
53
|
+
split: preparedSplit,
|
|
54
|
+
htmlLayout,
|
|
55
|
+
});
|
|
56
|
+
const boundsOffsetTop = chart.margin.top;
|
|
57
|
+
// We only need to consider the width of the first left axis
|
|
58
|
+
const boundsOffsetLeft = chart.margin.left + getYAxisWidth(yAxis[0]);
|
|
59
|
+
return {
|
|
60
|
+
boundsHeight,
|
|
61
|
+
boundsOffsetLeft,
|
|
62
|
+
boundsOffsetTop,
|
|
63
|
+
boundsWidth,
|
|
64
|
+
handleLegendItemClick,
|
|
65
|
+
legendConfig,
|
|
66
|
+
legendItems,
|
|
67
|
+
preparedLegend,
|
|
68
|
+
preparedSeries,
|
|
69
|
+
preparedSplit,
|
|
70
|
+
prevHeight,
|
|
71
|
+
prevWidth,
|
|
72
|
+
shapes,
|
|
73
|
+
shapesData,
|
|
74
|
+
title,
|
|
75
|
+
tooltip,
|
|
76
|
+
xAxis,
|
|
77
|
+
xScale,
|
|
78
|
+
yAxis,
|
|
79
|
+
yScale,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Dispatch } from 'd3';
|
|
3
|
+
import type { ChartTooltip } from '../../types';
|
|
4
|
+
type Props = {
|
|
5
|
+
dispatcher: Dispatch<object>;
|
|
6
|
+
tooltip?: ChartTooltip;
|
|
7
|
+
};
|
|
8
|
+
export declare function useChartInnerState(props: Props): {
|
|
9
|
+
tooltipPinned: boolean;
|
|
10
|
+
togglePinTooltip: ((value: boolean, event: React.MouseEvent) => void) | undefined;
|
|
11
|
+
unpinTooltip: (() => void) | undefined;
|
|
12
|
+
};
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { EventType, isMacintosh } from '../../utils';
|
|
3
|
+
export function useChartInnerState(props) {
|
|
4
|
+
var _a, _b;
|
|
5
|
+
const { dispatcher, tooltip } = props;
|
|
6
|
+
const [tooltipPinned, setTooltipPinned] = React.useState(false);
|
|
7
|
+
const tooltipEnabled = tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabled;
|
|
8
|
+
const tooltipPinEnabled = (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.pin) === null || _a === void 0 ? void 0 : _a.enabled;
|
|
9
|
+
const modifierKey = (_b = tooltip === null || tooltip === void 0 ? void 0 : tooltip.pin) === null || _b === void 0 ? void 0 : _b.modifierKey;
|
|
10
|
+
const togglePinTooltip = React.useCallback((value, event) => {
|
|
11
|
+
let resultValue = value;
|
|
12
|
+
if (value && modifierKey) {
|
|
13
|
+
switch (modifierKey) {
|
|
14
|
+
case 'altKey': {
|
|
15
|
+
resultValue = event.altKey;
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
case 'metaKey': {
|
|
19
|
+
resultValue = isMacintosh() ? event.metaKey : event.ctrlKey;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
setTooltipPinned(resultValue);
|
|
24
|
+
}, [modifierKey]);
|
|
25
|
+
const unpinTooltip = React.useCallback(() => {
|
|
26
|
+
setTooltipPinned(false);
|
|
27
|
+
dispatcher.call(EventType.HOVER_SHAPE, {}, undefined);
|
|
28
|
+
}, [dispatcher]);
|
|
29
|
+
return {
|
|
30
|
+
tooltipPinned,
|
|
31
|
+
togglePinTooltip: tooltipEnabled && tooltipPinEnabled ? togglePinTooltip : undefined,
|
|
32
|
+
unpinTooltip: tooltipEnabled && tooltipPinEnabled ? unpinTooltip : undefined,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -2,12 +2,13 @@ import React from 'react';
|
|
|
2
2
|
import { line as lineGenerator, scaleLinear, select, symbol } from 'd3';
|
|
3
3
|
import { CONTINUOUS_LEGEND_SIZE } from '../../constants';
|
|
4
4
|
import { getLineDashArray } from '../../hooks/useShapes/utils';
|
|
5
|
+
import { formatNumber } from '../../libs';
|
|
5
6
|
import { block, createGradientRect, getContinuesColorFn, getLabelsSize, getSymbol, } from '../../utils';
|
|
6
7
|
import { axisBottom } from '../../utils/chart/axis-generators';
|
|
7
8
|
import './styles.css';
|
|
8
9
|
const b = block('d3-legend');
|
|
9
10
|
const getLegendPosition = (args) => {
|
|
10
|
-
const { align, offsetWidth, width, contentWidth } = args;
|
|
11
|
+
const { align, offsetWidth = 0, width, contentWidth } = args;
|
|
11
12
|
const top = 0;
|
|
12
13
|
if (align === 'left') {
|
|
13
14
|
return { top, left: offsetWidth };
|
|
@@ -133,7 +134,7 @@ function renderLegendSymbol(args) {
|
|
|
133
134
|
});
|
|
134
135
|
}
|
|
135
136
|
export const Legend = (props) => {
|
|
136
|
-
const { boundsWidth, chartSeries, legend, items, config, onItemClick } = props;
|
|
137
|
+
const { boundsWidth, chartSeries, legend, items, config, onItemClick, onUpdate } = props;
|
|
137
138
|
const ref = React.useRef(null);
|
|
138
139
|
const [paginationOffset, setPaginationOffset] = React.useState(0);
|
|
139
140
|
React.useEffect(() => {
|
|
@@ -163,6 +164,7 @@ export const Legend = (props) => {
|
|
|
163
164
|
.attr('class', b('item'))
|
|
164
165
|
.on('click', function (e, d) {
|
|
165
166
|
onItemClick({ name: d.name, metaKey: e.metaKey });
|
|
167
|
+
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate();
|
|
166
168
|
});
|
|
167
169
|
const getXPosition = (i) => {
|
|
168
170
|
return line.slice(0, i).reduce((acc, legendItem) => {
|
|
@@ -229,14 +231,16 @@ export const Legend = (props) => {
|
|
|
229
231
|
}),
|
|
230
232
|
});
|
|
231
233
|
// ticks
|
|
234
|
+
const scale = scaleLinear(domain, [0, legend.width]);
|
|
232
235
|
const xAxisGenerator = axisBottom({
|
|
233
|
-
scale
|
|
236
|
+
scale,
|
|
234
237
|
ticks: {
|
|
235
238
|
items: [[0, -rectHeight]],
|
|
236
239
|
labelsMargin: legend.ticks.labelsMargin,
|
|
237
240
|
labelsLineHeight: legend.ticks.labelsLineHeight,
|
|
238
241
|
maxTickCount: 4,
|
|
239
242
|
tickColor: '#fff',
|
|
243
|
+
labelFormat: (value) => formatNumber(value, { unit: 'auto' }),
|
|
240
244
|
},
|
|
241
245
|
domain: {
|
|
242
246
|
size: legend.width,
|
|
@@ -251,15 +255,31 @@ export const Legend = (props) => {
|
|
|
251
255
|
legendWidth = legend.width;
|
|
252
256
|
}
|
|
253
257
|
if (legend.title.enable) {
|
|
254
|
-
const { maxWidth:
|
|
258
|
+
const { maxWidth: titleWidth } = getLabelsSize({
|
|
255
259
|
labels: [legend.title.text],
|
|
256
260
|
style: legend.title.style,
|
|
257
261
|
});
|
|
262
|
+
let dx = 0;
|
|
263
|
+
switch (legend.title.align) {
|
|
264
|
+
case 'center': {
|
|
265
|
+
dx = legend.width / 2 - titleWidth / 2;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
case 'right': {
|
|
269
|
+
dx = legend.width - titleWidth;
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
case 'left':
|
|
273
|
+
default: {
|
|
274
|
+
dx = 0;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
258
278
|
svgElement
|
|
259
279
|
.append('g')
|
|
260
280
|
.attr('class', b('title'))
|
|
261
281
|
.append('text')
|
|
262
|
-
.attr('dx',
|
|
282
|
+
.attr('dx', dx)
|
|
263
283
|
.attr('font-weight', (_c = legend.title.style.fontWeight) !== null && _c !== void 0 ? _c : null)
|
|
264
284
|
.attr('font-size', (_d = legend.title.style.fontSize) !== null && _d !== void 0 ? _d : null)
|
|
265
285
|
.attr('fill', (_e = legend.title.style.fontColor) !== null && _e !== void 0 ? _e : null)
|
|
@@ -273,6 +293,6 @@ export const Legend = (props) => {
|
|
|
273
293
|
contentWidth: legendWidth,
|
|
274
294
|
});
|
|
275
295
|
svgElement.attr('transform', `translate(${[left, config.offset.top].join(',')})`);
|
|
276
|
-
}, [boundsWidth, chartSeries, onItemClick, legend, items, config, paginationOffset]);
|
|
296
|
+
}, [boundsWidth, chartSeries, onItemClick, onUpdate, legend, items, config, paginationOffset]);
|
|
277
297
|
return React.createElement("g", { className: b(), ref: ref, width: boundsWidth, height: legend.height });
|
|
278
298
|
};
|
|
@@ -8,6 +8,8 @@ type TooltipProps = {
|
|
|
8
8
|
svgContainer: SVGSVGElement | null;
|
|
9
9
|
xAxis: PreparedAxis;
|
|
10
10
|
yAxis: PreparedAxis;
|
|
11
|
+
tooltipPinned: boolean;
|
|
12
|
+
onOutsideClick?: () => void;
|
|
11
13
|
};
|
|
12
14
|
export declare const Tooltip: (props: TooltipProps) => React.JSX.Element | null;
|
|
13
15
|
export {};
|
|
@@ -6,16 +6,22 @@ import { ChartTooltipContent } from './ChartTooltipContent';
|
|
|
6
6
|
import './styles.css';
|
|
7
7
|
const b = block('d3-tooltip');
|
|
8
8
|
export const Tooltip = (props) => {
|
|
9
|
-
const { tooltip, xAxis, yAxis, svgContainer, dispatcher } = props;
|
|
9
|
+
const { tooltip, xAxis, yAxis, svgContainer, dispatcher, tooltipPinned, onOutsideClick } = props;
|
|
10
10
|
const { hovered, pointerPosition } = useTooltip({ dispatcher, tooltip });
|
|
11
11
|
const containerRect = (svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) || { left: 0, top: 0 };
|
|
12
12
|
const left = ((pointerPosition === null || pointerPosition === void 0 ? void 0 : pointerPosition[0]) || 0) + containerRect.left;
|
|
13
13
|
const top = ((pointerPosition === null || pointerPosition === void 0 ? void 0 : pointerPosition[1]) || 0) + containerRect.top;
|
|
14
|
-
const anchorRef = useVirtualElementRef({ rect: {
|
|
14
|
+
const anchorRef = useVirtualElementRef({ rect: { left, top } });
|
|
15
|
+
const handleOutsideClick = (e) => {
|
|
16
|
+
if (svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.contains(e.target)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
onOutsideClick === null || onOutsideClick === void 0 ? void 0 : onOutsideClick();
|
|
20
|
+
};
|
|
15
21
|
React.useEffect(() => {
|
|
16
22
|
window.dispatchEvent(new CustomEvent('scroll'));
|
|
17
23
|
}, [left, top]);
|
|
18
|
-
return (hovered === null || hovered === void 0 ? void 0 : hovered.length) ? (React.createElement(Popup, { className: b(), open: true, anchorRef: anchorRef, offset: [0, 20], placement: ['right', 'left', 'top', 'bottom'], modifiers: [{ name: 'preventOverflow', options: { padding: 10, altAxis: true } }] },
|
|
24
|
+
return (hovered === null || hovered === void 0 ? void 0 : hovered.length) ? (React.createElement(Popup, { className: b({ pinned: tooltipPinned }), open: true, anchorRef: anchorRef, offset: [0, 20], placement: ['right', 'left', 'top', 'bottom'], modifiers: [{ name: 'preventOverflow', options: { padding: 10, altAxis: true } }], onOutsideClick: tooltipPinned ? handleOutsideClick : undefined },
|
|
19
25
|
React.createElement("div", { className: b('content') },
|
|
20
26
|
React.createElement(ChartTooltipContent, { hovered: hovered, xAxis: xAxis, yAxis: yAxis, renderer: tooltip.renderer })))) : null;
|
|
21
27
|
};
|
|
@@ -2,9 +2,10 @@ export * from './useChartDimensions';
|
|
|
2
2
|
export * from './useChartOptions';
|
|
3
3
|
export * from './useChartOptions/types';
|
|
4
4
|
export * from './useAxisScales';
|
|
5
|
+
export * from './usePrevious';
|
|
5
6
|
export * from './useSeries';
|
|
6
7
|
export * from './useSeries/types';
|
|
7
8
|
export * from './useShapes';
|
|
8
9
|
export * from './useTooltip';
|
|
9
|
-
export * from './
|
|
10
|
+
export * from './useSplit';
|
|
10
11
|
export * from './useSplit/types';
|
package/dist/esm/hooks/index.js
CHANGED
|
@@ -2,9 +2,10 @@ export * from './useChartDimensions';
|
|
|
2
2
|
export * from './useChartOptions';
|
|
3
3
|
export * from './useChartOptions/types';
|
|
4
4
|
export * from './useAxisScales';
|
|
5
|
+
export * from './usePrevious';
|
|
5
6
|
export * from './useSeries';
|
|
6
7
|
export * from './useSeries/types';
|
|
7
8
|
export * from './useShapes';
|
|
8
9
|
export * from './useTooltip';
|
|
9
|
-
export * from './
|
|
10
|
+
export * from './useSplit';
|
|
10
11
|
export * from './useSplit/types';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function usePrevious<T>(value: T): T | undefined;
|
|
@@ -8,6 +8,7 @@ import { setActiveState } from '../utils';
|
|
|
8
8
|
const b = block('d3-area');
|
|
9
9
|
export const AreaSeriesShapes = (args) => {
|
|
10
10
|
const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
|
|
11
|
+
const hoveredDataRef = React.useRef(null);
|
|
11
12
|
const ref = React.useRef(null);
|
|
12
13
|
React.useEffect(() => {
|
|
13
14
|
var _a;
|
|
@@ -72,7 +73,8 @@ export const AreaSeriesShapes = (args) => {
|
|
|
72
73
|
.call(renderMarker);
|
|
73
74
|
const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
|
|
74
75
|
const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
|
|
75
|
-
|
|
76
|
+
function handleShapeHover(data) {
|
|
77
|
+
hoveredDataRef.current = data;
|
|
76
78
|
const selected = (data === null || data === void 0 ? void 0 : data.filter((d) => d.series.type === 'area')) || [];
|
|
77
79
|
const selectedDataItems = selected.map((d) => d.data);
|
|
78
80
|
const selectedSeriesIds = selected.map((d) => { var _a; return (_a = d.series) === null || _a === void 0 ? void 0 : _a.id; });
|
|
@@ -132,7 +134,11 @@ export const AreaSeriesShapes = (args) => {
|
|
|
132
134
|
}
|
|
133
135
|
return d;
|
|
134
136
|
});
|
|
135
|
-
}
|
|
137
|
+
}
|
|
138
|
+
if (hoveredDataRef.current !== null) {
|
|
139
|
+
handleShapeHover(hoveredDataRef.current);
|
|
140
|
+
}
|
|
141
|
+
dispatcher.on('hover-shape.area', handleShapeHover);
|
|
136
142
|
return () => {
|
|
137
143
|
dispatcher.on('hover-shape.area', null);
|
|
138
144
|
};
|
|
@@ -8,6 +8,7 @@ export * from './types';
|
|
|
8
8
|
const b = block('d3-bar-x');
|
|
9
9
|
export const BarXSeriesShapes = (args) => {
|
|
10
10
|
const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
|
|
11
|
+
const hoveredDataRef = React.useRef(null);
|
|
11
12
|
const ref = React.useRef(null);
|
|
12
13
|
React.useEffect(() => {
|
|
13
14
|
var _a;
|
|
@@ -46,7 +47,8 @@ export const BarXSeriesShapes = (args) => {
|
|
|
46
47
|
.style('font-size', (d) => d.style.fontSize)
|
|
47
48
|
.style('font-weight', (d) => d.style.fontWeight || null)
|
|
48
49
|
.style('fill', (d) => d.style.fontColor || null);
|
|
49
|
-
|
|
50
|
+
function handleShapeHover(data) {
|
|
51
|
+
hoveredDataRef.current = data;
|
|
50
52
|
const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
|
|
51
53
|
const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
|
|
52
54
|
if (!data) {
|
|
@@ -84,7 +86,11 @@ export const BarXSeriesShapes = (args) => {
|
|
|
84
86
|
: (inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.opacity) || null;
|
|
85
87
|
});
|
|
86
88
|
}
|
|
87
|
-
}
|
|
89
|
+
}
|
|
90
|
+
if (hoveredDataRef.current !== null) {
|
|
91
|
+
handleShapeHover(hoveredDataRef.current);
|
|
92
|
+
}
|
|
93
|
+
dispatcher.on('hover-shape.bar-x', handleShapeHover);
|
|
88
94
|
return () => {
|
|
89
95
|
dispatcher.on('hover-shape.bar-x', null);
|
|
90
96
|
};
|
|
@@ -7,6 +7,7 @@ export { prepareBarYData } from './prepare-data';
|
|
|
7
7
|
const b = block('d3-bar-y');
|
|
8
8
|
export const BarYSeriesShapes = (args) => {
|
|
9
9
|
const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
|
|
10
|
+
const hoveredDataRef = React.useRef(null);
|
|
10
11
|
const ref = React.useRef(null);
|
|
11
12
|
React.useEffect(() => {
|
|
12
13
|
if (!ref.current) {
|
|
@@ -46,7 +47,8 @@ export const BarYSeriesShapes = (args) => {
|
|
|
46
47
|
.style('fill', (d) => d.style.fontColor || null);
|
|
47
48
|
const hoverOptions = get(seriesOptions, 'bar-y.states.hover');
|
|
48
49
|
const inactiveOptions = get(seriesOptions, 'bar-y.states.inactive');
|
|
49
|
-
|
|
50
|
+
function handleShapeHover(data) {
|
|
51
|
+
hoveredDataRef.current = data;
|
|
50
52
|
if (hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled) {
|
|
51
53
|
const hovered = data === null || data === void 0 ? void 0 : data.reduce((acc, d) => {
|
|
52
54
|
acc.add(d.data.y);
|
|
@@ -73,7 +75,11 @@ export const BarYSeriesShapes = (args) => {
|
|
|
73
75
|
rectSelection.attr('opacity', newOpacity);
|
|
74
76
|
labelSelection.attr('opacity', newOpacity);
|
|
75
77
|
}
|
|
76
|
-
}
|
|
78
|
+
}
|
|
79
|
+
if (hoveredDataRef.current !== null) {
|
|
80
|
+
handleShapeHover(hoveredDataRef.current);
|
|
81
|
+
}
|
|
82
|
+
dispatcher.on('hover-shape.bar-y', handleShapeHover);
|
|
77
83
|
return () => {
|
|
78
84
|
dispatcher.on('hover-shape.bar-y', null);
|
|
79
85
|
};
|
|
@@ -8,6 +8,7 @@ import { getLineDashArray, setActiveState } from '../utils';
|
|
|
8
8
|
const b = block('d3-line');
|
|
9
9
|
export const LineSeriesShapes = (args) => {
|
|
10
10
|
const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
|
|
11
|
+
const hoveredDataRef = React.useRef(null);
|
|
11
12
|
const ref = React.useRef(null);
|
|
12
13
|
React.useEffect(() => {
|
|
13
14
|
var _a;
|
|
@@ -60,7 +61,8 @@ export const LineSeriesShapes = (args) => {
|
|
|
60
61
|
.call(renderMarker);
|
|
61
62
|
const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
|
|
62
63
|
const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
|
|
63
|
-
|
|
64
|
+
function handleShapeHover(data) {
|
|
65
|
+
hoveredDataRef.current = data;
|
|
64
66
|
const selected = (data === null || data === void 0 ? void 0 : data.filter((d) => d.series.type === 'line')) || [];
|
|
65
67
|
const selectedDataItems = selected.map((d) => d.data);
|
|
66
68
|
const selectedSeriesIds = selected.map((d) => { var _a; return (_a = d.series) === null || _a === void 0 ? void 0 : _a.id; });
|
|
@@ -119,7 +121,11 @@ export const LineSeriesShapes = (args) => {
|
|
|
119
121
|
}
|
|
120
122
|
return d;
|
|
121
123
|
});
|
|
122
|
-
}
|
|
124
|
+
}
|
|
125
|
+
if (hoveredDataRef.current !== null) {
|
|
126
|
+
handleShapeHover(hoveredDataRef.current);
|
|
127
|
+
}
|
|
128
|
+
dispatcher.on('hover-shape.line', handleShapeHover);
|
|
123
129
|
return () => {
|
|
124
130
|
dispatcher.on('hover-shape.line', null);
|
|
125
131
|
};
|
|
@@ -9,6 +9,7 @@ export { prepareScatterData } from './prepare-data';
|
|
|
9
9
|
const b = block('d3-scatter');
|
|
10
10
|
export function ScatterSeriesShape(props) {
|
|
11
11
|
const { dispatcher, preparedData, seriesOptions, htmlLayout } = props;
|
|
12
|
+
const hoveredDataRef = React.useRef(null);
|
|
12
13
|
const ref = React.useRef(null);
|
|
13
14
|
React.useEffect(() => {
|
|
14
15
|
if (!ref.current) {
|
|
@@ -28,8 +29,9 @@ export function ScatterSeriesShape(props) {
|
|
|
28
29
|
.attr('cursor', (d) => d.point.series.cursor);
|
|
29
30
|
const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
|
|
30
31
|
const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
|
|
31
|
-
|
|
32
|
+
function handleShapeHover(data) {
|
|
32
33
|
var _a;
|
|
34
|
+
hoveredDataRef.current = data;
|
|
33
35
|
const selected = data === null || data === void 0 ? void 0 : data.find((d) => d.series.type === 'scatter');
|
|
34
36
|
const selectedDataItem = selected === null || selected === void 0 ? void 0 : selected.data;
|
|
35
37
|
const selectedSeriesId = (_a = selected === null || selected === void 0 ? void 0 : selected.series) === null || _a === void 0 ? void 0 : _a.id;
|
|
@@ -58,7 +60,11 @@ export function ScatterSeriesShape(props) {
|
|
|
58
60
|
}
|
|
59
61
|
return d;
|
|
60
62
|
});
|
|
61
|
-
}
|
|
63
|
+
}
|
|
64
|
+
if (hoveredDataRef.current !== null) {
|
|
65
|
+
handleShapeHover(hoveredDataRef.current);
|
|
66
|
+
}
|
|
67
|
+
dispatcher.on('hover-shape.scatter', handleShapeHover);
|
|
62
68
|
return () => {
|
|
63
69
|
dispatcher.on('hover-shape.scatter', null);
|
|
64
70
|
};
|