@gravity-ui/charts 0.5.0 → 0.6.1

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.
Files changed (91) hide show
  1. package/dist/cjs/components/ChartInner/index.d.ts +2 -8
  2. package/dist/cjs/components/ChartInner/index.js +22 -117
  3. package/dist/cjs/components/ChartInner/types.d.ts +6 -0
  4. package/dist/cjs/components/ChartInner/useChartInnerHandlers.d.ts +26 -0
  5. package/dist/cjs/components/ChartInner/useChartInnerHandlers.js +94 -0
  6. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +43 -0
  7. package/dist/cjs/components/ChartInner/useChartInnerProps.js +81 -0
  8. package/dist/cjs/components/ChartInner/useChartInnerState.d.ts +13 -0
  9. package/dist/cjs/components/ChartInner/useChartInnerState.js +34 -0
  10. package/dist/cjs/components/Legend/index.d.ts +1 -0
  11. package/dist/cjs/components/Legend/index.js +3 -2
  12. package/dist/cjs/components/Tooltip/index.d.ts +2 -0
  13. package/dist/cjs/components/Tooltip/index.js +10 -4
  14. package/dist/cjs/components/Tooltip/styles.css +13 -8
  15. package/dist/cjs/hooks/index.d.ts +2 -1
  16. package/dist/cjs/hooks/index.js +2 -1
  17. package/dist/cjs/hooks/usePrevious/index.d.ts +1 -0
  18. package/dist/cjs/hooks/usePrevious/index.js +8 -0
  19. package/dist/cjs/hooks/useShapes/area/index.js +8 -2
  20. package/dist/cjs/hooks/useShapes/area/prepare-data.js +4 -0
  21. package/dist/cjs/hooks/useShapes/bar-x/index.js +8 -2
  22. package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +1 -0
  23. package/dist/cjs/hooks/useShapes/bar-y/index.js +8 -2
  24. package/dist/cjs/hooks/useShapes/bar-y/prepare-data.js +1 -0
  25. package/dist/cjs/hooks/useShapes/line/index.js +8 -2
  26. package/dist/cjs/hooks/useShapes/line/prepare-data.js +1 -0
  27. package/dist/cjs/hooks/useShapes/pie/index.js +2 -1
  28. package/dist/cjs/hooks/useShapes/pie/prepare-data.js +159 -111
  29. package/dist/cjs/hooks/useShapes/pie/types.d.ts +1 -1
  30. package/dist/cjs/hooks/useShapes/scatter/index.js +8 -2
  31. package/dist/cjs/hooks/useShapes/treemap/index.js +8 -2
  32. package/dist/cjs/hooks/useShapes/treemap/prepare-data.js +1 -0
  33. package/dist/cjs/hooks/useShapes/utils.d.ts +2 -2
  34. package/dist/cjs/hooks/useShapes/utils.js +5 -3
  35. package/dist/cjs/hooks/useShapes/waterfall/index.js +8 -2
  36. package/dist/cjs/hooks/useTooltip/index.d.ts +2 -3
  37. package/dist/cjs/types/chart/chart.d.ts +2 -2
  38. package/dist/cjs/types/chart/series.d.ts +1 -2
  39. package/dist/cjs/types/chart/tooltip.d.ts +6 -6
  40. package/dist/cjs/types/chart-ui.d.ts +4 -0
  41. package/dist/cjs/types/misc.d.ts +1 -0
  42. package/dist/cjs/utils/misc.d.ts +10 -2
  43. package/dist/cjs/utils/misc.js +15 -3
  44. package/dist/esm/components/ChartInner/index.d.ts +2 -8
  45. package/dist/esm/components/ChartInner/index.js +22 -117
  46. package/dist/esm/components/ChartInner/types.d.ts +6 -0
  47. package/dist/esm/components/ChartInner/useChartInnerHandlers.d.ts +26 -0
  48. package/dist/esm/components/ChartInner/useChartInnerHandlers.js +94 -0
  49. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +43 -0
  50. package/dist/esm/components/ChartInner/useChartInnerProps.js +81 -0
  51. package/dist/esm/components/ChartInner/useChartInnerState.d.ts +13 -0
  52. package/dist/esm/components/ChartInner/useChartInnerState.js +34 -0
  53. package/dist/esm/components/Legend/index.d.ts +1 -0
  54. package/dist/esm/components/Legend/index.js +3 -2
  55. package/dist/esm/components/Tooltip/index.d.ts +2 -0
  56. package/dist/esm/components/Tooltip/index.js +10 -4
  57. package/dist/esm/components/Tooltip/styles.css +13 -8
  58. package/dist/esm/hooks/index.d.ts +2 -1
  59. package/dist/esm/hooks/index.js +2 -1
  60. package/dist/esm/hooks/usePrevious/index.d.ts +1 -0
  61. package/dist/esm/hooks/usePrevious/index.js +8 -0
  62. package/dist/esm/hooks/useShapes/area/index.js +8 -2
  63. package/dist/esm/hooks/useShapes/area/prepare-data.js +4 -0
  64. package/dist/esm/hooks/useShapes/bar-x/index.js +8 -2
  65. package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +1 -0
  66. package/dist/esm/hooks/useShapes/bar-y/index.js +8 -2
  67. package/dist/esm/hooks/useShapes/bar-y/prepare-data.js +1 -0
  68. package/dist/esm/hooks/useShapes/line/index.js +8 -2
  69. package/dist/esm/hooks/useShapes/line/prepare-data.js +1 -0
  70. package/dist/esm/hooks/useShapes/pie/index.js +2 -1
  71. package/dist/esm/hooks/useShapes/pie/prepare-data.js +159 -111
  72. package/dist/esm/hooks/useShapes/pie/types.d.ts +1 -1
  73. package/dist/esm/hooks/useShapes/scatter/index.js +8 -2
  74. package/dist/esm/hooks/useShapes/treemap/index.js +8 -2
  75. package/dist/esm/hooks/useShapes/treemap/prepare-data.js +1 -0
  76. package/dist/esm/hooks/useShapes/utils.d.ts +2 -2
  77. package/dist/esm/hooks/useShapes/utils.js +5 -3
  78. package/dist/esm/hooks/useShapes/waterfall/index.js +8 -2
  79. package/dist/esm/hooks/useTooltip/index.d.ts +2 -3
  80. package/dist/esm/types/chart/chart.d.ts +2 -2
  81. package/dist/esm/types/chart/series.d.ts +1 -2
  82. package/dist/esm/types/chart/tooltip.d.ts +6 -6
  83. package/dist/esm/types/chart-ui.d.ts +4 -0
  84. package/dist/esm/types/misc.d.ts +1 -0
  85. package/dist/esm/utils/misc.d.ts +10 -2
  86. package/dist/esm/utils/misc.js +15 -3
  87. package/package.json +1 -1
  88. package/dist/cjs/hooks/useTooltip/types.d.ts +0 -1
  89. package/dist/esm/hooks/useTooltip/types.d.ts +0 -1
  90. /package/dist/cjs/{hooks/useTooltip → components/ChartInner}/types.js +0 -0
  91. /package/dist/esm/{hooks/useTooltip → components/ChartInner}/types.js +0 -0
@@ -1,10 +1,4 @@
1
1
  import React from 'react';
2
- import type { ChartData } from '../../types';
2
+ import type { ChartInnerProps } from './types';
3
3
  import './styles.css';
4
- type Props = {
5
- width: number;
6
- height: number;
7
- data: ChartData;
8
- };
9
- export declare const ChartInner: (props: Props) => React.JSX.Element;
10
- export {};
4
+ export declare const ChartInner: (props: ChartInnerProps) => React.JSX.Element;
@@ -1,77 +1,39 @@
1
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 { useAxisScales, useChartDimensions, useChartOptions, useSeries, useShapes, } from '../../hooks';
6
- import { getYAxisWidth } from '../../hooks/useChartDimensions/utils';
7
- import { getPreparedXAxis } from '../../hooks/useChartOptions/x-axis';
8
- import { getPreparedYAxis } from '../../hooks/useChartOptions/y-axis';
9
- import { useSplit } from '../../hooks/useSplit';
10
2
  import { EventType, block, getD3Dispatcher } from '../../utils';
11
- import { getClosestPoints } from '../../utils/chart/get-closest-data';
12
3
  import { AxisX, AxisY } from '../Axis';
13
4
  import { Legend } from '../Legend';
14
5
  import { PlotTitle } from '../PlotTitle';
15
6
  import { Title } from '../Title';
16
7
  import { Tooltip } from '../Tooltip';
8
+ import { useChartInnerHandlers } from './useChartInnerHandlers';
9
+ import { useChartInnerProps } from './useChartInnerProps';
10
+ import { useChartInnerState } from './useChartInnerState';
17
11
  import './styles.css';
18
12
  const b = block('d3');
19
- const THROTTLE_DELAY = 50;
20
13
  export const ChartInner = (props) => {
21
14
  var _a, _b, _c, _d;
22
15
  const { width, height, data } = props;
23
16
  const svgRef = React.useRef(null);
24
17
  const htmlLayerRef = React.useRef(null);
25
- const dispatcher = React.useMemo(() => {
26
- return getD3Dispatcher();
27
- }, []);
28
- const { chart, title, tooltip } = useChartOptions({
29
- data,
30
- });
31
- const xAxis = React.useMemo(() => getPreparedXAxis({ xAxis: data.xAxis, width, series: data.series.data }), [data, width]);
32
- const yAxis = React.useMemo(() => getPreparedYAxis({
33
- series: data.series.data,
34
- yAxis: data.yAxis,
35
- height,
36
- }), [data, height]);
37
- const { legendItems, legendConfig, preparedSeries, preparedSeriesOptions, preparedLegend, handleLegendItemClick, } = useSeries({
38
- chartWidth: width,
39
- chartHeight: height,
40
- chartMargin: chart.margin,
41
- series: data.series,
42
- legend: data.legend,
43
- preparedYAxis: yAxis,
44
- });
45
- const { boundsWidth, boundsHeight } = useChartDimensions({
46
- width,
47
- height,
48
- margin: chart.margin,
49
- preparedLegend,
50
- preparedXAxis: xAxis,
51
- preparedYAxis: yAxis,
52
- preparedSeries: preparedSeries,
18
+ const dispatcher = React.useMemo(() => getD3Dispatcher(), []);
19
+ const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, legendConfig, legendItems, preparedSeries, preparedSplit, preparedLegend, prevHeight, prevWidth, shapes, shapesData, title, tooltip, xAxis, xScale, yAxis, yScale, } = useChartInnerProps(Object.assign(Object.assign({}, props), { dispatcher, htmlLayout: htmlLayerRef.current }));
20
+ const { tooltipPinned, togglePinTooltip, unpinTooltip } = useChartInnerState({
21
+ dispatcher,
22
+ tooltip,
53
23
  });
54
- const preparedSplit = useSplit({ split: data.split, boundsHeight, chartWidth: width });
55
- const { xScale, yScale } = useAxisScales({
56
- boundsWidth,
24
+ const { handleChartClick, handleMouseLeave, throttledHandleMouseMove, throttledHandleTouchMove } = useChartInnerHandlers({
57
25
  boundsHeight,
58
- series: preparedSeries,
59
- xAxis,
60
- yAxis,
61
- split: preparedSplit,
62
- });
63
- const { shapes, shapesData } = useShapes({
26
+ boundsOffsetLeft,
27
+ boundsOffsetTop,
64
28
  boundsWidth,
65
- boundsHeight,
66
29
  dispatcher,
67
- series: preparedSeries,
68
- seriesOptions: preparedSeriesOptions,
30
+ shapesData,
31
+ svgContainer: svgRef.current,
32
+ togglePinTooltip,
33
+ tooltipPinned,
34
+ unpinTooltip,
69
35
  xAxis,
70
- xScale,
71
36
  yAxis,
72
- yScale,
73
- split: preparedSplit,
74
- htmlLayout: htmlLayerRef.current,
75
37
  });
76
38
  const clickHandler = (_b = (_a = data.chart) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.click;
77
39
  const pointerMoveHandler = (_d = (_c = data.chart) === null || _c === void 0 ? void 0 : _c.events) === null || _d === void 0 ? void 0 : _d.pointermove;
@@ -90,68 +52,11 @@ export const ChartInner = (props) => {
90
52
  dispatcher.on(EventType.POINTERMOVE_CHART, null);
91
53
  };
92
54
  }, [dispatcher, clickHandler, pointerMoveHandler]);
93
- const boundsOffsetTop = chart.margin.top;
94
- // We only need to consider the width of the first left axis
95
- const boundsOffsetLeft = chart.margin.left + getYAxisWidth(yAxis[0]);
96
- const isOutsideBounds = React.useCallback((x, y) => {
97
- return x < 0 || x > boundsWidth || y < 0 || y > boundsHeight;
98
- }, [boundsHeight, boundsWidth]);
99
- const handleMove = ([pointerX, pointerY], event) => {
100
- const x = pointerX - boundsOffsetLeft;
101
- const y = pointerY - boundsOffsetTop;
102
- if (isOutsideBounds(x, y)) {
103
- dispatcher.call(EventType.HOVER_SHAPE, {}, undefined);
104
- dispatcher.call(EventType.POINTERMOVE_CHART, {}, undefined, event);
105
- return;
106
- }
107
- const closest = getClosestPoints({
108
- position: [x, y],
109
- shapesData,
110
- });
111
- dispatcher.call(EventType.HOVER_SHAPE, event.target, closest, [pointerX, pointerY]);
112
- dispatcher.call(EventType.POINTERMOVE_CHART, {}, {
113
- hovered: closest,
114
- xAxis,
115
- yAxis: yAxis[0],
116
- }, event);
117
- };
118
- const handleMouseMove = (event) => {
119
- const [pointerX, pointerY] = pointer(event, svgRef.current);
120
- handleMove([pointerX, pointerY], event);
121
- };
122
- const throttledHandleMouseMove = IS_TOUCH_ENABLED
123
- ? undefined
124
- : throttle(handleMouseMove, THROTTLE_DELAY);
125
- const handleMouseLeave = (event) => {
126
- throttledHandleMouseMove === null || throttledHandleMouseMove === void 0 ? void 0 : throttledHandleMouseMove.cancel();
127
- dispatcher.call(EventType.HOVER_SHAPE, {}, undefined);
128
- dispatcher.call(EventType.POINTERMOVE_CHART, {}, undefined, event);
129
- };
130
- const handleTouchMove = (event) => {
131
- const touch = event.touches[0];
132
- const [pointerX, pointerY] = pointer(touch, svgRef.current);
133
- handleMove([pointerX, pointerY], event);
134
- };
135
- const throttledHandleTouchMove = IS_TOUCH_ENABLED
136
- ? throttle(handleTouchMove, THROTTLE_DELAY)
137
- : undefined;
138
- const handleChartClick = React.useCallback((event) => {
139
- const [pointerX, pointerY] = pointer(event, svgRef.current);
140
- const x = pointerX - boundsOffsetLeft;
141
- const y = pointerY - boundsOffsetTop;
142
- if (isOutsideBounds(x, y)) {
143
- return;
144
- }
145
- const items = getClosestPoints({
146
- position: [x, y],
147
- shapesData,
148
- });
149
- const selected = items === null || items === void 0 ? void 0 : items.find((item) => item.closest);
150
- if (!selected) {
151
- return;
55
+ React.useEffect(() => {
56
+ if ((prevWidth !== width || prevHeight !== height) && tooltipPinned) {
57
+ unpinTooltip === null || unpinTooltip === void 0 ? void 0 : unpinTooltip();
152
58
  }
153
- dispatcher.call('click-chart', undefined, { point: selected.data, series: selected.series }, event);
154
- }, [boundsOffsetLeft, boundsOffsetTop, dispatcher, isOutsideBounds, shapesData]);
59
+ }, [prevWidth, width, prevHeight, height, tooltipPinned, unpinTooltip]);
155
60
  return (React.createElement(React.Fragment, null,
156
61
  React.createElement("svg", { ref: svgRef, className: b(), width: width, height: height, onMouseMove: throttledHandleMouseMove, onMouseLeave: handleMouseLeave, onTouchStart: throttledHandleTouchMove, onTouchMove: throttledHandleTouchMove, onClick: handleChartClick },
157
62
  title && React.createElement(Title, Object.assign({}, title, { chartWidth: width })),
@@ -164,9 +69,9 @@ export const ChartInner = (props) => {
164
69
  React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
165
70
  React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale, split: preparedSplit })))),
166
71
  shapes),
167
- preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick }))),
72
+ preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick, onUpdate: unpinTooltip }))),
168
73
  React.createElement("div", { className: b('html-layer'), ref: htmlLayerRef, style: {
169
74
  transform: `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
170
75
  } }),
171
- React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: tooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0] })));
76
+ React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: tooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0], onOutsideClick: unpinTooltip, tooltipPinned: tooltipPinned })));
172
77
  };
@@ -0,0 +1,6 @@
1
+ import type { ChartData } from '../../types';
2
+ export type ChartInnerProps = {
3
+ width: number;
4
+ height: number;
5
+ data: ChartData;
6
+ };
@@ -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
+ }
@@ -8,6 +8,7 @@ type Props = {
8
8
  items: LegendItem[][];
9
9
  config: LegendConfig;
10
10
  onItemClick: OnLegendItemClick;
11
+ onUpdate?: () => void;
11
12
  };
12
13
  export declare const Legend: (props: Props) => React.JSX.Element;
13
14
  export {};
@@ -134,7 +134,7 @@ function renderLegendSymbol(args) {
134
134
  });
135
135
  }
136
136
  export const Legend = (props) => {
137
- const { boundsWidth, chartSeries, legend, items, config, onItemClick } = props;
137
+ const { boundsWidth, chartSeries, legend, items, config, onItemClick, onUpdate } = props;
138
138
  const ref = React.useRef(null);
139
139
  const [paginationOffset, setPaginationOffset] = React.useState(0);
140
140
  React.useEffect(() => {
@@ -164,6 +164,7 @@ export const Legend = (props) => {
164
164
  .attr('class', b('item'))
165
165
  .on('click', function (e, d) {
166
166
  onItemClick({ name: d.name, metaKey: e.metaKey });
167
+ onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate();
167
168
  });
168
169
  const getXPosition = (i) => {
169
170
  return line.slice(0, i).reduce((acc, legendItem) => {
@@ -292,6 +293,6 @@ export const Legend = (props) => {
292
293
  contentWidth: legendWidth,
293
294
  });
294
295
  svgElement.attr('transform', `translate(${[left, config.offset.top].join(',')})`);
295
- }, [boundsWidth, chartSeries, onItemClick, legend, items, config, paginationOffset]);
296
+ }, [boundsWidth, chartSeries, onItemClick, onUpdate, legend, items, config, paginationOffset]);
296
297
  return React.createElement("g", { className: b(), ref: ref, width: boundsWidth, height: legend.height });
297
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 {};
@@ -4,18 +4,24 @@ import { useTooltip } from '../../hooks';
4
4
  import { block } from '../../utils';
5
5
  import { ChartTooltipContent } from './ChartTooltipContent';
6
6
  import './styles.css';
7
- const b = block('d3-tooltip');
7
+ const b = block('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: { top, left } });
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 }), contentClassName: b('popup-content'), 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
  };
@@ -1,25 +1,30 @@
1
- .gcharts-d3-tooltip[class] {
1
+ .gcharts-tooltip[class] {
2
2
  --g-popup-border-width: 0;
3
3
  pointer-events: none;
4
4
  }
5
- .gcharts-d3-tooltip[class] > div {
5
+ .gcharts-tooltip[class] > div {
6
6
  animation-duration: unset;
7
7
  animation-timing-function: unset;
8
8
  animation-fill-mode: unset;
9
9
  }
10
- .gcharts-d3-tooltip__content {
11
- padding: 10px 14px;
10
+ .gcharts-tooltip[class].gcharts-tooltip_pinned {
11
+ pointer-events: inherit;
12
+ }
13
+ .gcharts-tooltip__popup-content {
14
+ box-shadow: none;
15
+ }
16
+ .gcharts-tooltip__content {
17
+ padding: 8px 14px;
12
18
  text-wrap: nowrap;
13
- border: 1px solid var(--g-color-line-generic);
14
- border-radius: 3px;
19
+ border-radius: 4px;
15
20
  background-color: var(--g-color-infographics-tooltip-bg);
16
21
  box-shadow: 0 2px 12px var(--g-color-sfx-shadow);
17
22
  }
18
- .gcharts-d3-tooltip__content-row {
23
+ .gcharts-tooltip__content-row {
19
24
  display: flex;
20
25
  align-items: center;
21
26
  }
22
- .gcharts-d3-tooltip__color {
27
+ .gcharts-tooltip__color {
23
28
  display: inline-block;
24
29
  width: 16px;
25
30
  height: 8px;
@@ -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 './useTooltip/types';
10
+ export * from './useSplit';
10
11
  export * from './useSplit/types';
@@ -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 './useTooltip/types';
10
+ export * from './useSplit';
10
11
  export * from './useSplit/types';
@@ -0,0 +1 @@
1
+ export declare function usePrevious<T>(value: T): T | undefined;
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ export function usePrevious(value) {
3
+ const ref = React.useRef();
4
+ React.useEffect(() => {
5
+ ref.current = value;
6
+ }, [value]);
7
+ return ref.current;
8
+ }