@hsu-react/ui 2.5.3 → 2.5.5
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/es/components/ChainGraph/_hooks/useChainGraphData.js +29 -8
- package/es/components/ChainGraph/_hooks/useChainGraphLayout.js +14 -3
- package/es/components/Chart/Bar/index.js +49 -19
- package/es/components/Chart/Bubble/index.js +21 -6
- package/es/components/Chart/Common/index.js +7 -2
- package/es/components/Chart/Heatmap/index.js +21 -6
- package/es/components/Chart/Line/index.js +49 -19
- package/es/components/Chart/Pie/Pie3D/index.js +22 -6
- package/es/components/Chart/Pie/index.js +21 -6
- package/es/components/Chart/Sankey/index.js +11 -2
- package/es/components/Chart/Tree/index.js +11 -2
- package/es/components/Chat/ChatInput/index.js +7 -2
- package/es/components/Icon/index.js +8 -2
- package/es/components/Input/Number/index.js +28 -21
- package/es/components/Input/Password/index.js +44 -36
- package/es/components/Input/Search/index.js +44 -36
- package/es/components/Input/TextArea/index.js +64 -42
- package/es/components/Input/index.js +40 -34
- package/es/components/Select/_hooks/useSelectComposition.js +6 -2
- package/es/components/Select/_hooks/useSelectInputOptions.js +8 -2
- package/es/components/Slider/index.js +24 -15
- package/es/components/Table/_hooks/useAutoScrolling.js +16 -7
- package/es/components/Upload/_hooks/useUploadFileList.js +8 -2
- package/es/components/Upload/_hooks/useUploadOperations.js +8 -2
- package/es/hooks/useLatestRef.d.ts +24 -0
- package/es/hooks/useLatestRef.js +29 -0
- package/es/layout/Menu/_hooks/useOnlyLvOneMenu.js +9 -2
- package/lib/components/ChainGraph/_hooks/useChainGraphData.js +24 -7
- package/lib/components/ChainGraph/_hooks/useChainGraphLayout.js +12 -2
- package/lib/components/Chart/Bar/index.js +45 -18
- package/lib/components/Chart/Bubble/index.js +19 -6
- package/lib/components/Chart/Common/index.js +6 -2
- package/lib/components/Chart/Heatmap/index.js +19 -6
- package/lib/components/Chart/Line/index.js +45 -18
- package/lib/components/Chart/Pie/Pie3D/index.js +20 -6
- package/lib/components/Chart/Pie/index.js +19 -6
- package/lib/components/Chart/Sankey/index.js +10 -2
- package/lib/components/Chart/Tree/index.js +10 -2
- package/lib/components/Chat/ChatInput/index.js +6 -2
- package/lib/components/Icon/index.js +7 -2
- package/lib/components/Input/Number/index.js +27 -18
- package/lib/components/Input/Password/index.js +40 -26
- package/lib/components/Input/Search/index.js +42 -26
- package/lib/components/Input/TextArea/index.js +54 -26
- package/lib/components/Input/index.js +37 -24
- package/lib/components/Select/_hooks/useSelectComposition.js +6 -2
- package/lib/components/Select/_hooks/useSelectInputOptions.js +7 -2
- package/lib/components/Slider/index.js +23 -11
- package/lib/components/Table/_hooks/useAutoScrolling.js +16 -7
- package/lib/components/Upload/_hooks/useUploadFileList.js +7 -2
- package/lib/components/Upload/_hooks/useUploadOperations.js +7 -2
- package/lib/hooks/useLatestRef.d.ts +24 -0
- package/lib/hooks/useLatestRef.js +35 -0
- package/lib/layout/Menu/_hooks/useOnlyLvOneMenu.js +8 -2
- package/package.json +9 -3
- package/src/__tests__/callbackDepsGuard.test.ts +119 -0
- package/src/__tests__/setup.ts +26 -0
- package/src/components/ChainGraph/_hooks/callbacks.test.tsx +155 -0
- package/src/components/ChainGraph/_hooks/useChainGraphData.ts +38 -10
- package/src/components/ChainGraph/_hooks/useChainGraphLayout.ts +16 -3
- package/src/components/Chart/Bar/index.tsx +59 -21
- package/src/components/Chart/Bubble/index.tsx +22 -7
- package/src/components/Chart/Common/index.tsx +7 -2
- package/src/components/Chart/Heatmap/index.tsx +25 -6
- package/src/components/Chart/Line/index.tsx +59 -21
- package/src/components/Chart/Pie/Pie3D/index.tsx +22 -7
- package/src/components/Chart/Pie/index.tsx +22 -7
- package/src/components/Chart/Sankey/index.tsx +10 -4
- package/src/components/Chart/Tree/index.tsx +10 -4
- package/src/components/Chart/callbacks.test.tsx +315 -0
- package/src/components/Chat/ChatInput/index.tsx +7 -2
- package/src/components/Icon/index.tsx +7 -2
- package/src/components/Input/Number/index.tsx +25 -23
- package/src/components/Input/Password/index.tsx +37 -26
- package/src/components/Input/Search/index.tsx +39 -26
- package/src/components/Input/TextArea/index.test.tsx +208 -0
- package/src/components/Input/TextArea/index.tsx +56 -30
- package/src/components/Input/index.test.tsx +189 -0
- package/src/components/Input/index.tsx +36 -29
- package/src/components/Select/_hooks/useSelectComposition.test.ts +47 -0
- package/src/components/Select/_hooks/useSelectComposition.ts +6 -2
- package/src/components/Select/_hooks/useSelectInputOptions.ts +6 -9
- package/src/components/Slider/index.tsx +22 -12
- package/src/components/Table/_hooks/useAutoScrolling.test.tsx +95 -0
- package/src/components/Table/_hooks/useAutoScrolling.ts +16 -7
- package/src/components/Upload/_hooks/useUploadFileList.ts +6 -2
- package/src/components/Upload/_hooks/useUploadOperations.ts +7 -2
- package/src/hooks/useLatestRef.ts +31 -0
- package/src/layout/Menu/_hooks/useOnlyLvOneMenu.test.tsx +56 -0
- package/src/layout/Menu/_hooks/useOnlyLvOneMenu.ts +8 -2
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.useChainGraphData = useChainGraphData;
|
|
7
7
|
var _react = require("react");
|
|
8
8
|
var _hsuUtils = require("hsu-utils");
|
|
9
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
9
10
|
function useChainGraphData(props) {
|
|
10
11
|
const {
|
|
11
12
|
graph,
|
|
@@ -18,6 +19,25 @@ function useChainGraphData(props) {
|
|
|
18
19
|
} = props;
|
|
19
20
|
const [lastData, setLastData] = (0, _react.useState)(undefined);
|
|
20
21
|
const [isLayouting, setIsLayouting] = (0, _react.useState)(true);
|
|
22
|
+
|
|
23
|
+
// 这三个回调都是「交给图实例、由它在后续事件里回调」的出口,不是数据本身。
|
|
24
|
+
// 它们进依赖数组是条死依赖:effect 体被 ObjEqual(lastData, data) 挡住,
|
|
25
|
+
// 引用变了也不会重新 setData —— 图里存的还是首次 setData 时那个闭包,
|
|
26
|
+
// 换了引用永远调不到(stale closure)。改成引用恒定的转发函数:
|
|
27
|
+
// 依赖数组只留真正的输入(graph/data/level/rootLevel),调用时总是取到最新的回调。
|
|
28
|
+
const getImageRef = (0, _useLatestRef.useLatestRef)(getImage);
|
|
29
|
+
const labelRenderRef = (0, _useLatestRef.useLatestRef)(labelRender);
|
|
30
|
+
const onLayoutingChangeRef = (0, _useLatestRef.useLatestRef)(onLayoutingChange);
|
|
31
|
+
const emitImage = (0, _react.useCallback)(img => {
|
|
32
|
+
getImageRef.current?.(img);
|
|
33
|
+
}, [getImageRef]);
|
|
34
|
+
// labelRender 在 ChainGraphServices.setData 里是「传了才改写 label」的真值判断,
|
|
35
|
+
// 所以这里保留「有没有传」的语义:没传就传 undefined,传了才给转发函数。
|
|
36
|
+
const renderLabel = (0, _react.useCallback)(label => labelRenderRef.current(label), [labelRenderRef]);
|
|
37
|
+
const emitLayouting = (0, _react.useCallback)(value => {
|
|
38
|
+
setIsLayouting(value);
|
|
39
|
+
onLayoutingChangeRef.current?.(value);
|
|
40
|
+
}, [onLayoutingChangeRef]);
|
|
21
41
|
(0, _react.useEffect)(() => {
|
|
22
42
|
if (graph && !_hsuUtils.Equal.ObjEqual(lastData, data)) {
|
|
23
43
|
setLastData(data);
|
|
@@ -25,15 +45,12 @@ function useChainGraphData(props) {
|
|
|
25
45
|
data,
|
|
26
46
|
level,
|
|
27
47
|
rootLevel,
|
|
28
|
-
getImage,
|
|
29
|
-
isLayouting:
|
|
30
|
-
|
|
31
|
-
onLayoutingChange?.(value);
|
|
32
|
-
},
|
|
33
|
-
labelRender
|
|
48
|
+
getImage: emitImage,
|
|
49
|
+
isLayouting: emitLayouting,
|
|
50
|
+
labelRender: labelRenderRef.current ? renderLabel : undefined
|
|
34
51
|
});
|
|
35
52
|
}
|
|
36
|
-
}, [graph, data, level, rootLevel,
|
|
53
|
+
}, [graph, data, level, rootLevel, lastData, emitImage, emitLayouting, renderLabel, labelRenderRef]);
|
|
37
54
|
return {
|
|
38
55
|
isLayouting
|
|
39
56
|
};
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.useChainGraphLayout = useChainGraphLayout;
|
|
7
7
|
var _react = require("react");
|
|
8
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
8
9
|
function useChainGraphLayout(props) {
|
|
9
10
|
const {
|
|
10
11
|
graph,
|
|
@@ -12,9 +13,18 @@ function useChainGraphLayout(props) {
|
|
|
12
13
|
rootLevel,
|
|
13
14
|
getImage
|
|
14
15
|
} = props;
|
|
16
|
+
|
|
17
|
+
// getImage 只是「布局完成后把截图 dataURL 交出去」的出口,不是布局的输入。
|
|
18
|
+
// 它进依赖数组会让消费方每传一次内联箭头就重新 changeLayout 一次 —— 整张图重排;
|
|
19
|
+
// 消费方再把截图存进 state,就变成「重排 → 出图 → setState → 新引用 → 再重排」的死循环。
|
|
20
|
+
// 用引用恒定的转发函数注册,调用时再取最新的 getImage。
|
|
21
|
+
const getImageRef = (0, _useLatestRef.useLatestRef)(getImage);
|
|
22
|
+
const emitImage = (0, _react.useCallback)(img => {
|
|
23
|
+
getImageRef.current?.(img);
|
|
24
|
+
}, [getImageRef]);
|
|
15
25
|
(0, _react.useEffect)(() => {
|
|
16
26
|
if (graph && octopus !== undefined) {
|
|
17
|
-
graph.changeLayout(octopus, rootLevel,
|
|
27
|
+
graph.changeLayout(octopus, rootLevel, emitImage);
|
|
18
28
|
}
|
|
19
|
-
}, [graph, octopus, rootLevel,
|
|
29
|
+
}, [graph, octopus, rootLevel, emitImage]);
|
|
20
30
|
}
|
|
@@ -13,6 +13,7 @@ var _autoSmooth = require("../_utils/autoSmooth");
|
|
|
13
13
|
var _cartesian = require("../_utils/cartesian");
|
|
14
14
|
var _chartTheme = require("../_utils/chartTheme");
|
|
15
15
|
var _useIsDark = _interopRequireDefault(require("../../../hooks/useIsDark"));
|
|
16
|
+
var _useLatestRef = _interopRequireDefault(require("../../../hooks/useLatestRef"));
|
|
16
17
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
18
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
18
19
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -57,6 +58,29 @@ const ChartBar = props => {
|
|
|
57
58
|
const isDark = (0, _useIsDark.default)();
|
|
58
59
|
const chrome = (0, _react.useMemo)(() => (0, _chartTheme.resolveChartChrome)(isDark), [isDark]);
|
|
59
60
|
|
|
61
|
+
// 回调 prop 一律走 useLatestRef,不进依赖数组:消费方传内联箭头(React 里最常见的
|
|
62
|
+
// 写法)时每次渲染都是新引用,进依赖数组会让「init + setOption(notMerge)」整段重跑,
|
|
63
|
+
// 还会把当前可视窗口再同步一次给父级 —— 父级据此改 state 就是死循环。
|
|
64
|
+
// 但「有没有传回调」决定要不要注册监听,所以单独拆成布尔量进依赖数组,
|
|
65
|
+
// 这样「从无到有传入回调」仍会重新注册,不会漏。
|
|
66
|
+
const onClickRef = (0, _useLatestRef.default)(onClick);
|
|
67
|
+
const onLegendSelectChangedRef = (0, _useLatestRef.default)(onLegendSelectChanged);
|
|
68
|
+
const onDataZoomWindowChangedRef = (0, _useLatestRef.default)(onDataZoomWindowChanged);
|
|
69
|
+
const hasOnClick = !!onClick;
|
|
70
|
+
const hasOnLegendSelectChanged = !!onLegendSelectChanged;
|
|
71
|
+
const hasOnDataZoomWindowChanged = !!onDataZoomWindowChanged;
|
|
72
|
+
// 已通知过父级的窗口用 ref 记账(不参与渲染):窗口没变就不再通知,
|
|
73
|
+
// 掐断「通知 → 父级重算坐标轴 → chartOption 变 → effect 重跑 → 再通知」的自激回路
|
|
74
|
+
const notifiedZoomWindowRef = (0, _react.useRef)(null);
|
|
75
|
+
const emitZoomWindow = (0, _react.useCallback)(next => {
|
|
76
|
+
const prev = notifiedZoomWindowRef.current;
|
|
77
|
+
if (prev && prev.startIndex === next.startIndex && prev.endIndex === next.endIndex) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
notifiedZoomWindowRef.current = next;
|
|
81
|
+
onDataZoomWindowChangedRef.current?.(next);
|
|
82
|
+
}, [onDataZoomWindowChangedRef]);
|
|
83
|
+
|
|
60
84
|
// Cache the chart option with useMemo
|
|
61
85
|
const chartOption = (0, _react.useMemo)(() => {
|
|
62
86
|
const def_xAxis = (0, _cartesian.createDefaultCategoryXAxis)(xAxisData, chrome);
|
|
@@ -241,15 +265,15 @@ const ChartBar = props => {
|
|
|
241
265
|
}
|
|
242
266
|
|
|
243
267
|
// Sync the current visible window to the caller (on initial render and after restore), used to recalculate axes based on the displayed portion
|
|
244
|
-
if (
|
|
268
|
+
if (hasOnDataZoomWindowChanged) {
|
|
245
269
|
const zooms = chart.getOption()?.dataZoom;
|
|
246
270
|
const zoom = zooms?.[0];
|
|
247
271
|
const totalLen = xAxisData?.length ?? 0;
|
|
248
272
|
if (typeof zoom?.start === "number" && typeof zoom?.end === "number") {
|
|
249
|
-
|
|
273
|
+
emitZoomWindow((0, _cartesian.percentWindowToIndexWindow)(zoom.start, zoom.end, totalLen));
|
|
250
274
|
} else {
|
|
251
275
|
// Without dataZoom (e.g. scrolling not enabled due to insufficient data) the window is the full range; sync once to avoid a stale old window
|
|
252
|
-
|
|
276
|
+
emitZoomWindow({
|
|
253
277
|
startIndex: 0,
|
|
254
278
|
endIndex: Math.max(0, totalLen - 1)
|
|
255
279
|
});
|
|
@@ -265,16 +289,19 @@ const ChartBar = props => {
|
|
|
265
289
|
resizeObserverRef.current.observe(chartRef.current);
|
|
266
290
|
}
|
|
267
291
|
|
|
268
|
-
// Add click event
|
|
269
|
-
|
|
270
|
-
|
|
292
|
+
// Add click event(注册与否看布尔量,实际调用取最新引用)
|
|
293
|
+
const handleClick = event => {
|
|
294
|
+
onClickRef.current?.(event);
|
|
295
|
+
};
|
|
296
|
+
if (hasOnClick) {
|
|
297
|
+
chartInstanceRef.current?.on("click", handleClick);
|
|
271
298
|
}
|
|
272
299
|
|
|
273
300
|
// Legend selection event
|
|
274
301
|
const handleLegendSelectChanged = params => {
|
|
275
|
-
|
|
302
|
+
onLegendSelectChangedRef.current?.(params.selected);
|
|
276
303
|
};
|
|
277
|
-
if (
|
|
304
|
+
if (hasOnLegendSelectChanged) {
|
|
278
305
|
chartInstanceRef.current?.on("legendselectchanged", handleLegendSelectChanged);
|
|
279
306
|
}
|
|
280
307
|
|
|
@@ -283,15 +310,15 @@ const ChartBar = props => {
|
|
|
283
310
|
const raw = params;
|
|
284
311
|
const info = raw?.batch?.[0] ?? raw;
|
|
285
312
|
if (typeof info?.startValue === "number" && typeof info?.endValue === "number") {
|
|
286
|
-
|
|
313
|
+
emitZoomWindow({
|
|
287
314
|
startIndex: Math.max(0, Math.round(info.startValue)),
|
|
288
315
|
endIndex: Math.max(0, Math.round(info.endValue))
|
|
289
316
|
});
|
|
290
317
|
} else if (typeof info?.start === "number" && typeof info?.end === "number") {
|
|
291
|
-
|
|
318
|
+
emitZoomWindow((0, _cartesian.percentWindowToIndexWindow)(info.start, info.end, xAxisData?.length ?? 0));
|
|
292
319
|
}
|
|
293
320
|
};
|
|
294
|
-
if (
|
|
321
|
+
if (hasOnDataZoomWindowChanged) {
|
|
295
322
|
chartInstanceRef.current?.on("datazoom", handleDataZoom);
|
|
296
323
|
}
|
|
297
324
|
|
|
@@ -318,13 +345,13 @@ const ChartBar = props => {
|
|
|
318
345
|
// Cleanup function
|
|
319
346
|
return () => {
|
|
320
347
|
window.removeEventListener("resize", handleResize);
|
|
321
|
-
if (
|
|
322
|
-
chartInstanceRef.current?.off("click",
|
|
348
|
+
if (hasOnClick) {
|
|
349
|
+
chartInstanceRef.current?.off("click", handleClick);
|
|
323
350
|
}
|
|
324
|
-
if (
|
|
351
|
+
if (hasOnLegendSelectChanged) {
|
|
325
352
|
chartInstanceRef.current?.off("legendselectchanged", handleLegendSelectChanged);
|
|
326
353
|
}
|
|
327
|
-
if (
|
|
354
|
+
if (hasOnDataZoomWindowChanged) {
|
|
328
355
|
chartInstanceRef.current?.off("datazoom", handleDataZoom);
|
|
329
356
|
}
|
|
330
357
|
if (legendScrollRef.current) {
|
|
@@ -332,7 +359,7 @@ const ChartBar = props => {
|
|
|
332
359
|
legendScrollRef.current = null;
|
|
333
360
|
}
|
|
334
361
|
};
|
|
335
|
-
}, [chartOption, handleResize,
|
|
362
|
+
}, [chartOption, handleResize, onClickRef, onLegendSelectChangedRef, hasOnClick, hasOnLegendSelectChanged, hasOnDataZoomWindowChanged, emitZoomWindow, xAxisData, enableLegendAutoScroll, legendVisibleCount, legendScrollInterval,
|
|
336
363
|
// 图例是否接管滚轮由它推导,漏了会拿到过期的滚动配置
|
|
337
364
|
normalizedScroll, containerReady]);
|
|
338
365
|
|
|
@@ -351,10 +378,10 @@ const ChartBar = props => {
|
|
|
351
378
|
startIndex: normalizedScroll.startIndex,
|
|
352
379
|
autoPlay: normalizedScroll.autoScroll,
|
|
353
380
|
enableWheelScroll,
|
|
354
|
-
onWindowChange:
|
|
381
|
+
onWindowChange: hasOnDataZoomWindowChanged ? (startPercent, endPercent) => emitZoomWindow((0, _cartesian.percentWindowToIndexWindow)(startPercent, endPercent, xAxisData.length)) : undefined
|
|
355
382
|
});
|
|
356
383
|
return dispose;
|
|
357
|
-
}, [dataZoom, normalizedScroll, sliderDataZoom, xAxisData,
|
|
384
|
+
}, [dataZoom, normalizedScroll, sliderDataZoom, xAxisData, hasOnDataZoomWindowChanged, emitZoomWindow]);
|
|
358
385
|
|
|
359
386
|
// Clean up resources when the component unmounts
|
|
360
387
|
(0, _react.useEffect)(() => {
|
|
@@ -8,6 +8,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
8
8
|
var _indexModule = _interopRequireDefault(require("../index.module.scss"));
|
|
9
9
|
var echarts = _interopRequireWildcard(require("echarts"));
|
|
10
10
|
var _bubble = require("../_utils/bubble");
|
|
11
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
11
12
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
13
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
14
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -254,7 +255,16 @@ const Bubble = props => {
|
|
|
254
255
|
chartInstanceRef.current.setOption(chartOption, true);
|
|
255
256
|
}
|
|
256
257
|
}, [chartOption, data]);
|
|
258
|
+
const onChartRef = (0, _useLatestRef.useLatestRef)(onChart);
|
|
257
259
|
|
|
260
|
+
// onClick 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 跟着重跑就会
|
|
261
|
+
// 再走一遍 setOption(notMerge),动画重播、悬浮/高亮态被清掉。注册与否看布尔量,
|
|
262
|
+
// 这样「从无到有传入 onClick」仍会注册;实际调用取 ref 里的最新引用。
|
|
263
|
+
const onClickRef = (0, _useLatestRef.useLatestRef)(onClick);
|
|
264
|
+
const hasOnClick = !!onClick;
|
|
265
|
+
|
|
266
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
267
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
258
268
|
// Initialize the chart
|
|
259
269
|
(0, _react.useEffect)(() => {
|
|
260
270
|
if (!chartRef.current) return;
|
|
@@ -267,7 +277,7 @@ const Bubble = props => {
|
|
|
267
277
|
chart = echarts.init(chartRef.current);
|
|
268
278
|
chartInstanceRef.current = chart;
|
|
269
279
|
// Call the onChart callback on first initialization
|
|
270
|
-
|
|
280
|
+
onChartRef.current?.(chart);
|
|
271
281
|
}
|
|
272
282
|
|
|
273
283
|
// Resize first so echarts picks up the correct container size, then apply the option
|
|
@@ -278,18 +288,21 @@ const Bubble = props => {
|
|
|
278
288
|
window.addEventListener("resize", handleResize);
|
|
279
289
|
|
|
280
290
|
// Add click event
|
|
281
|
-
|
|
282
|
-
|
|
291
|
+
const handleClick = event => {
|
|
292
|
+
onClickRef.current?.(event);
|
|
293
|
+
};
|
|
294
|
+
if (hasOnClick) {
|
|
295
|
+
chartInstanceRef.current?.on("click", handleClick);
|
|
283
296
|
}
|
|
284
297
|
|
|
285
298
|
// Cleanup function
|
|
286
299
|
return () => {
|
|
287
300
|
window.removeEventListener("resize", handleResize);
|
|
288
|
-
if (
|
|
289
|
-
chartInstanceRef.current?.off("click",
|
|
301
|
+
if (hasOnClick) {
|
|
302
|
+
chartInstanceRef.current?.off("click", handleClick);
|
|
290
303
|
}
|
|
291
304
|
};
|
|
292
|
-
}, [chartOption, containerSize.width, containerSize.height, handleResize,
|
|
305
|
+
}, [chartOption, containerSize.width, containerSize.height, handleResize, onChartRef, onClickRef, hasOnClick]);
|
|
293
306
|
|
|
294
307
|
// Dispose the chart instance when the component unmounts
|
|
295
308
|
(0, _react.useEffect)(() => {
|
|
@@ -8,6 +8,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
8
8
|
var _useContainerReady = _interopRequireDefault(require("../_hooks/useContainerReady"));
|
|
9
9
|
var _indexModule = _interopRequireDefault(require("../index.module.scss"));
|
|
10
10
|
var echarts = _interopRequireWildcard(require("echarts"));
|
|
11
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
11
12
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
13
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
14
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -41,7 +42,10 @@ const Common = props => {
|
|
|
41
42
|
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
42
43
|
chartInstanceRef.current?.resize();
|
|
43
44
|
}, []);
|
|
45
|
+
const onChartRef = (0, _useLatestRef.useLatestRef)(onChart);
|
|
44
46
|
|
|
47
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
48
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
45
49
|
// Initialize the chart
|
|
46
50
|
(0, _react.useEffect)(() => {
|
|
47
51
|
if (!chartRef.current || !containerReady) return;
|
|
@@ -57,7 +61,7 @@ const Common = props => {
|
|
|
57
61
|
chart.setOption(chartOption, true);
|
|
58
62
|
|
|
59
63
|
// Chart-ready callback (can be used for legend auto-scroll, etc.)
|
|
60
|
-
|
|
64
|
+
onChartRef.current?.(chart);
|
|
61
65
|
|
|
62
66
|
// Add resize listener
|
|
63
67
|
window.addEventListener("resize", handleResize);
|
|
@@ -72,7 +76,7 @@ const Common = props => {
|
|
|
72
76
|
return () => {
|
|
73
77
|
window.removeEventListener("resize", handleResize);
|
|
74
78
|
};
|
|
75
|
-
}, [chartOption, handleResize,
|
|
79
|
+
}, [chartOption, handleResize, onChartRef, containerReady]);
|
|
76
80
|
|
|
77
81
|
// Clean up resources on component unmount
|
|
78
82
|
(0, _react.useEffect)(() => {
|
|
@@ -11,6 +11,7 @@ var echarts = _interopRequireWildcard(require("echarts"));
|
|
|
11
11
|
var _heatmap = require("../_utils/heatmap");
|
|
12
12
|
var _chartTheme = require("../_utils/chartTheme");
|
|
13
13
|
var _useIsDark = _interopRequireDefault(require("../../../hooks/useIsDark"));
|
|
14
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
14
15
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
17
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -191,7 +192,16 @@ const Heatmap = props => {
|
|
|
191
192
|
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
192
193
|
chartInstanceRef.current?.resize();
|
|
193
194
|
}, []);
|
|
195
|
+
const onChartRef = (0, _useLatestRef.useLatestRef)(onChart);
|
|
194
196
|
|
|
197
|
+
// onClick 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 跟着重跑就会
|
|
198
|
+
// 再走一遍 setOption(notMerge),动画重播、悬浮/高亮态被清掉。注册与否看布尔量,
|
|
199
|
+
// 这样「从无到有传入 onClick」仍会注册;实际调用取 ref 里的最新引用。
|
|
200
|
+
const onClickRef = (0, _useLatestRef.useLatestRef)(onClick);
|
|
201
|
+
const hasOnClick = !!onClick;
|
|
202
|
+
|
|
203
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
204
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
195
205
|
// Initialize the chart
|
|
196
206
|
(0, _react.useEffect)(() => {
|
|
197
207
|
if (!chartRef.current || !containerReady) return;
|
|
@@ -202,7 +212,7 @@ const Heatmap = props => {
|
|
|
202
212
|
chart = echarts.init(chartRef.current);
|
|
203
213
|
chartInstanceRef.current = chart;
|
|
204
214
|
// Call the onChart callback on first initialization
|
|
205
|
-
|
|
215
|
+
onChartRef.current?.(chart);
|
|
206
216
|
}
|
|
207
217
|
|
|
208
218
|
// Apply the option
|
|
@@ -218,18 +228,21 @@ const Heatmap = props => {
|
|
|
218
228
|
}
|
|
219
229
|
|
|
220
230
|
// Add click event
|
|
221
|
-
|
|
222
|
-
|
|
231
|
+
const handleClick = event => {
|
|
232
|
+
onClickRef.current?.(event);
|
|
233
|
+
};
|
|
234
|
+
if (hasOnClick) {
|
|
235
|
+
chartInstanceRef.current?.on("click", handleClick);
|
|
223
236
|
}
|
|
224
237
|
|
|
225
238
|
// Cleanup function
|
|
226
239
|
return () => {
|
|
227
240
|
window.removeEventListener("resize", handleResize);
|
|
228
|
-
if (
|
|
229
|
-
chartInstanceRef.current?.off("click",
|
|
241
|
+
if (hasOnClick) {
|
|
242
|
+
chartInstanceRef.current?.off("click", handleClick);
|
|
230
243
|
}
|
|
231
244
|
};
|
|
232
|
-
}, [chartOption, handleResize,
|
|
245
|
+
}, [chartOption, handleResize, onChartRef, onClickRef, hasOnClick, containerReady]);
|
|
233
246
|
|
|
234
247
|
// Clean up resources when the component unmounts
|
|
235
248
|
(0, _react.useEffect)(() => {
|
|
@@ -13,6 +13,7 @@ var _autoSmooth = require("../_utils/autoSmooth");
|
|
|
13
13
|
var _cartesian = require("../_utils/cartesian");
|
|
14
14
|
var _chartTheme = require("../_utils/chartTheme");
|
|
15
15
|
var _useIsDark = _interopRequireDefault(require("../../../hooks/useIsDark"));
|
|
16
|
+
var _useLatestRef = _interopRequireDefault(require("../../../hooks/useLatestRef"));
|
|
16
17
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
18
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
18
19
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -57,6 +58,29 @@ const ChartLine = props => {
|
|
|
57
58
|
const isDark = (0, _useIsDark.default)();
|
|
58
59
|
const chrome = (0, _react.useMemo)(() => (0, _chartTheme.resolveChartChrome)(isDark), [isDark]);
|
|
59
60
|
|
|
61
|
+
// 回调 prop 一律走 useLatestRef,不进依赖数组:消费方传内联箭头(React 里最常见的
|
|
62
|
+
// 写法)时每次渲染都是新引用,进依赖数组会让「init + setOption(notMerge)」整段重跑,
|
|
63
|
+
// 还会把当前可视窗口再同步一次给父级 —— 父级据此改 state 就是死循环。
|
|
64
|
+
// 但「有没有传回调」决定要不要注册监听,所以单独拆成布尔量进依赖数组,
|
|
65
|
+
// 这样「从无到有传入回调」仍会重新注册,不会漏。
|
|
66
|
+
const onClickRef = (0, _useLatestRef.default)(onClick);
|
|
67
|
+
const onLegendSelectChangedRef = (0, _useLatestRef.default)(onLegendSelectChanged);
|
|
68
|
+
const onDataZoomWindowChangedRef = (0, _useLatestRef.default)(onDataZoomWindowChanged);
|
|
69
|
+
const hasOnClick = !!onClick;
|
|
70
|
+
const hasOnLegendSelectChanged = !!onLegendSelectChanged;
|
|
71
|
+
const hasOnDataZoomWindowChanged = !!onDataZoomWindowChanged;
|
|
72
|
+
// 已通知过父级的窗口用 ref 记账(不参与渲染):窗口没变就不再通知,
|
|
73
|
+
// 掐断「通知 → 父级重算坐标轴 → chartOption 变 → effect 重跑 → 再通知」的自激回路
|
|
74
|
+
const notifiedZoomWindowRef = (0, _react.useRef)(null);
|
|
75
|
+
const emitZoomWindow = (0, _react.useCallback)(next => {
|
|
76
|
+
const prev = notifiedZoomWindowRef.current;
|
|
77
|
+
if (prev && prev.startIndex === next.startIndex && prev.endIndex === next.endIndex) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
notifiedZoomWindowRef.current = next;
|
|
81
|
+
onDataZoomWindowChangedRef.current?.(next);
|
|
82
|
+
}, [onDataZoomWindowChangedRef]);
|
|
83
|
+
|
|
60
84
|
// Cache the chart option with useMemo
|
|
61
85
|
const chartOption = (0, _react.useMemo)(() => {
|
|
62
86
|
const def_xAxis = (0, _cartesian.createDefaultCategoryXAxis)(xAxisData, chrome);
|
|
@@ -238,15 +262,15 @@ const ChartLine = props => {
|
|
|
238
262
|
}
|
|
239
263
|
|
|
240
264
|
// Sync the current visible window to the caller (on initial render and after restore), used to recalculate axes based on the displayed portion
|
|
241
|
-
if (
|
|
265
|
+
if (hasOnDataZoomWindowChanged) {
|
|
242
266
|
const zooms = chart.getOption()?.dataZoom;
|
|
243
267
|
const zoom = zooms?.[0];
|
|
244
268
|
const totalLen = xAxisData?.length ?? 0;
|
|
245
269
|
if (typeof zoom?.start === "number" && typeof zoom?.end === "number") {
|
|
246
|
-
|
|
270
|
+
emitZoomWindow((0, _cartesian.percentWindowToIndexWindow)(zoom.start, zoom.end, totalLen));
|
|
247
271
|
} else {
|
|
248
272
|
// Without dataZoom (e.g. scrolling not enabled due to insufficient data) the window is the full range; sync once to avoid a stale old window
|
|
249
|
-
|
|
273
|
+
emitZoomWindow({
|
|
250
274
|
startIndex: 0,
|
|
251
275
|
endIndex: Math.max(0, totalLen - 1)
|
|
252
276
|
});
|
|
@@ -262,16 +286,19 @@ const ChartLine = props => {
|
|
|
262
286
|
resizeObserverRef.current.observe(chartRef.current);
|
|
263
287
|
}
|
|
264
288
|
|
|
265
|
-
// Add click event
|
|
266
|
-
|
|
267
|
-
|
|
289
|
+
// Add click event(注册与否看布尔量,实际调用取最新引用)
|
|
290
|
+
const handleClick = event => {
|
|
291
|
+
onClickRef.current?.(event);
|
|
292
|
+
};
|
|
293
|
+
if (hasOnClick) {
|
|
294
|
+
chartInstanceRef.current?.on("click", handleClick);
|
|
268
295
|
}
|
|
269
296
|
|
|
270
297
|
// Legend selection event
|
|
271
298
|
const handleLegendSelectChanged = params => {
|
|
272
|
-
|
|
299
|
+
onLegendSelectChangedRef.current?.(params.selected);
|
|
273
300
|
};
|
|
274
|
-
if (
|
|
301
|
+
if (hasOnLegendSelectChanged) {
|
|
275
302
|
chartInstanceRef.current?.on("legendselectchanged", handleLegendSelectChanged);
|
|
276
303
|
}
|
|
277
304
|
|
|
@@ -280,15 +307,15 @@ const ChartLine = props => {
|
|
|
280
307
|
const raw = params;
|
|
281
308
|
const info = raw?.batch?.[0] ?? raw;
|
|
282
309
|
if (typeof info?.startValue === "number" && typeof info?.endValue === "number") {
|
|
283
|
-
|
|
310
|
+
emitZoomWindow({
|
|
284
311
|
startIndex: Math.max(0, Math.round(info.startValue)),
|
|
285
312
|
endIndex: Math.max(0, Math.round(info.endValue))
|
|
286
313
|
});
|
|
287
314
|
} else if (typeof info?.start === "number" && typeof info?.end === "number") {
|
|
288
|
-
|
|
315
|
+
emitZoomWindow((0, _cartesian.percentWindowToIndexWindow)(info.start, info.end, xAxisData?.length ?? 0));
|
|
289
316
|
}
|
|
290
317
|
};
|
|
291
|
-
if (
|
|
318
|
+
if (hasOnDataZoomWindowChanged) {
|
|
292
319
|
chartInstanceRef.current?.on("datazoom", handleDataZoom);
|
|
293
320
|
}
|
|
294
321
|
|
|
@@ -315,13 +342,13 @@ const ChartLine = props => {
|
|
|
315
342
|
// Cleanup function
|
|
316
343
|
return () => {
|
|
317
344
|
window.removeEventListener("resize", handleResize);
|
|
318
|
-
if (
|
|
319
|
-
chartInstanceRef.current?.off("click",
|
|
345
|
+
if (hasOnClick) {
|
|
346
|
+
chartInstanceRef.current?.off("click", handleClick);
|
|
320
347
|
}
|
|
321
|
-
if (
|
|
348
|
+
if (hasOnLegendSelectChanged) {
|
|
322
349
|
chartInstanceRef.current?.off("legendselectchanged", handleLegendSelectChanged);
|
|
323
350
|
}
|
|
324
|
-
if (
|
|
351
|
+
if (hasOnDataZoomWindowChanged) {
|
|
325
352
|
chartInstanceRef.current?.off("datazoom", handleDataZoom);
|
|
326
353
|
}
|
|
327
354
|
if (legendScrollRef.current) {
|
|
@@ -329,7 +356,7 @@ const ChartLine = props => {
|
|
|
329
356
|
legendScrollRef.current = null;
|
|
330
357
|
}
|
|
331
358
|
};
|
|
332
|
-
}, [chartOption, handleResize,
|
|
359
|
+
}, [chartOption, handleResize, onClickRef, onLegendSelectChangedRef, hasOnClick, hasOnLegendSelectChanged, hasOnDataZoomWindowChanged, emitZoomWindow, xAxisData, enableLegendAutoScroll, legendVisibleCount, legendScrollInterval,
|
|
333
360
|
// 图例是否接管滚轮由它推导,漏了会拿到过期的滚动配置
|
|
334
361
|
normalizedScroll, containerReady]);
|
|
335
362
|
|
|
@@ -348,10 +375,10 @@ const ChartLine = props => {
|
|
|
348
375
|
startIndex: normalizedScroll.startIndex,
|
|
349
376
|
autoPlay: normalizedScroll.autoScroll,
|
|
350
377
|
enableWheelScroll,
|
|
351
|
-
onWindowChange:
|
|
378
|
+
onWindowChange: hasOnDataZoomWindowChanged ? (startPercent, endPercent) => emitZoomWindow((0, _cartesian.percentWindowToIndexWindow)(startPercent, endPercent, xAxisData.length)) : undefined
|
|
352
379
|
});
|
|
353
380
|
return dispose;
|
|
354
|
-
}, [dataZoom, normalizedScroll, sliderDataZoom, xAxisData,
|
|
381
|
+
}, [dataZoom, normalizedScroll, sliderDataZoom, xAxisData, hasOnDataZoomWindowChanged, emitZoomWindow]);
|
|
355
382
|
|
|
356
383
|
// Clean up resources when the component unmounts
|
|
357
384
|
(0, _react.useEffect)(() => {
|
|
@@ -11,6 +11,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
11
11
|
var _hooks = require("./_hooks");
|
|
12
12
|
var _option = require("./_utils/option");
|
|
13
13
|
var _indexModule = _interopRequireDefault(require("../../index.module.scss"));
|
|
14
|
+
var _useLatestRef = require("../../../../hooks/useLatestRef");
|
|
14
15
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
17
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -84,13 +85,23 @@ const ChartPie3D = props => {
|
|
|
84
85
|
yOffset,
|
|
85
86
|
autoRotate
|
|
86
87
|
});
|
|
88
|
+
const onChartRef = (0, _useLatestRef.useLatestRef)(onChart);
|
|
89
|
+
|
|
90
|
+
// onClick 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 跟着重跑会重建
|
|
91
|
+
// 3D 饼的 option 并重播动画。注册与否看布尔量(保证「从无到有传入」仍会注册),
|
|
92
|
+
// 实际调用取 ref 里的最新引用。
|
|
93
|
+
const onClickRef = (0, _useLatestRef.useLatestRef)(onClick);
|
|
94
|
+
const hasOnClick = !!onClick;
|
|
95
|
+
|
|
96
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
97
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
87
98
|
(0, _react.useEffect)(() => {
|
|
88
99
|
if (!chartRef.current || !containerReady) return;
|
|
89
100
|
let chart = chartInstanceRef.current;
|
|
90
101
|
if (!chart) {
|
|
91
102
|
chart = echarts.init(chartRef.current);
|
|
92
103
|
chartInstanceRef.current = chart;
|
|
93
|
-
|
|
104
|
+
onChartRef.current?.(chart);
|
|
94
105
|
}
|
|
95
106
|
chart.setOption(chartOption, true);
|
|
96
107
|
optionRef.current = chartOption;
|
|
@@ -101,20 +112,23 @@ const ChartPie3D = props => {
|
|
|
101
112
|
}
|
|
102
113
|
chart.on("mouseover", handleMouseOver);
|
|
103
114
|
chart.on("globalout", handleGlobalOut);
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
const handleClick = event => {
|
|
116
|
+
onClickRef.current?.(event);
|
|
117
|
+
};
|
|
118
|
+
if (hasOnClick) {
|
|
119
|
+
chart.on("click", handleClick);
|
|
106
120
|
}
|
|
107
121
|
return () => {
|
|
108
122
|
window.removeEventListener("resize", handleResize);
|
|
109
123
|
if (chart) {
|
|
110
124
|
chart.off("mouseover", handleMouseOver);
|
|
111
125
|
chart.off("globalout", handleGlobalOut);
|
|
112
|
-
if (
|
|
113
|
-
chart.off("click",
|
|
126
|
+
if (hasOnClick) {
|
|
127
|
+
chart.off("click", handleClick);
|
|
114
128
|
}
|
|
115
129
|
}
|
|
116
130
|
};
|
|
117
|
-
}, [chartOption, handleResize, handleMouseOver, handleGlobalOut,
|
|
131
|
+
}, [chartOption, handleResize, handleMouseOver, handleGlobalOut, onChartRef, onClickRef, hasOnClick, containerReady]);
|
|
118
132
|
(0, _react.useEffect)(() => {
|
|
119
133
|
return () => {
|
|
120
134
|
if (resizeObserverRef.current) {
|
|
@@ -10,6 +10,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
10
10
|
var _chartUtils = require("../chartUtils");
|
|
11
11
|
var _indexModule = _interopRequireDefault(require("../index.module.scss"));
|
|
12
12
|
var _Pie3D = _interopRequireDefault(require("./Pie3D"));
|
|
13
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
13
14
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
15
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
16
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -200,7 +201,16 @@ const ChartPie = props => {
|
|
|
200
201
|
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
201
202
|
chartInstanceRef.current?.resize();
|
|
202
203
|
}, []);
|
|
204
|
+
const onChartRef = (0, _useLatestRef.useLatestRef)(onChart);
|
|
203
205
|
|
|
206
|
+
// onClick 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 跟着重跑就会
|
|
207
|
+
// 再走一遍 setOption(notMerge),动画重播、悬浮/高亮态被清掉。注册与否看布尔量,
|
|
208
|
+
// 这样「从无到有传入 onClick」仍会注册;实际调用取 ref 里的最新引用。
|
|
209
|
+
const onClickRef = (0, _useLatestRef.useLatestRef)(onClick);
|
|
210
|
+
const hasOnClick = !!onClick;
|
|
211
|
+
|
|
212
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
213
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
204
214
|
// Initialize the chart
|
|
205
215
|
(0, _react.useEffect)(() => {
|
|
206
216
|
if (!chartRef.current || !containerReady) return;
|
|
@@ -211,7 +221,7 @@ const ChartPie = props => {
|
|
|
211
221
|
chart = echarts.init(chartRef.current);
|
|
212
222
|
chartInstanceRef.current = chart;
|
|
213
223
|
// Call the onChart callback on first initialization
|
|
214
|
-
|
|
224
|
+
onChartRef.current?.(chart);
|
|
215
225
|
}
|
|
216
226
|
|
|
217
227
|
// Apply the option
|
|
@@ -227,8 +237,11 @@ const ChartPie = props => {
|
|
|
227
237
|
}
|
|
228
238
|
|
|
229
239
|
// Add click event
|
|
230
|
-
|
|
231
|
-
|
|
240
|
+
const handleClick = event => {
|
|
241
|
+
onClickRef.current?.(event);
|
|
242
|
+
};
|
|
243
|
+
if (hasOnClick) {
|
|
244
|
+
chartInstanceRef.current?.on("click", handleClick);
|
|
232
245
|
}
|
|
233
246
|
|
|
234
247
|
// Legend auto-scroll
|
|
@@ -249,8 +262,8 @@ const ChartPie = props => {
|
|
|
249
262
|
// Cleanup function
|
|
250
263
|
return () => {
|
|
251
264
|
window.removeEventListener("resize", handleResize);
|
|
252
|
-
if (
|
|
253
|
-
chartInstanceRef.current?.off("click",
|
|
265
|
+
if (hasOnClick) {
|
|
266
|
+
chartInstanceRef.current?.off("click", handleClick);
|
|
254
267
|
}
|
|
255
268
|
|
|
256
269
|
// Clean up legend scrolling
|
|
@@ -259,7 +272,7 @@ const ChartPie = props => {
|
|
|
259
272
|
legendScrollRef.current = null;
|
|
260
273
|
}
|
|
261
274
|
};
|
|
262
|
-
}, [chartOption, handleResize,
|
|
275
|
+
}, [chartOption, handleResize, onChartRef, onClickRef, hasOnClick, enableLegendAutoScroll, seriesData, legendVisibleCount, legendScrollInterval, containerReady]);
|
|
263
276
|
|
|
264
277
|
// Clean up resources when the component unmounts
|
|
265
278
|
(0, _react.useEffect)(() => {
|