@hsu-react/ui 2.4.1 → 2.4.2
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/Chart/Bar/index.js +9 -2
- package/es/components/Chart/Common/index.js +9 -2
- package/es/components/Chart/Gauge/index.js +9 -2
- package/es/components/Chart/Heatmap/index.js +9 -2
- package/es/components/Chart/Line/index.js +9 -2
- package/es/components/Chart/Pie/Pie3D/index.js +9 -2
- package/es/components/Chart/Pie/index.js +9 -2
- package/es/components/Chart/Polar/index.js +9 -2
- package/es/components/Chart/Radar/index.js +9 -2
- package/es/components/Chart/Sankey/index.js +9 -2
- package/es/components/Chart/Tree/index.js +9 -2
- package/es/components/Chart/_hooks/useContainerReady.d.ts +18 -0
- package/es/components/Chart/_hooks/useContainerReady.js +56 -0
- package/lib/components/Chart/Bar/index.js +9 -2
- package/lib/components/Chart/Common/index.js +9 -2
- package/lib/components/Chart/Gauge/index.js +9 -2
- package/lib/components/Chart/Heatmap/index.js +9 -2
- package/lib/components/Chart/Line/index.js +9 -2
- package/lib/components/Chart/Pie/Pie3D/index.js +9 -2
- package/lib/components/Chart/Pie/index.js +9 -2
- package/lib/components/Chart/Polar/index.js +9 -2
- package/lib/components/Chart/Radar/index.js +9 -2
- package/lib/components/Chart/Sankey/index.js +9 -2
- package/lib/components/Chart/Tree/index.js +9 -2
- package/lib/components/Chart/_hooks/useContainerReady.d.ts +18 -0
- package/lib/components/Chart/_hooks/useContainerReady.js +51 -0
- package/package.json +1 -1
- package/src/components/Chart/Bar/index.tsx +10 -1
- package/src/components/Chart/Common/index.tsx +12 -2
- package/src/components/Chart/Gauge/index.tsx +12 -2
- package/src/components/Chart/Heatmap/index.tsx +12 -2
- package/src/components/Chart/Line/index.tsx +10 -1
- package/src/components/Chart/Pie/Pie3D/index.tsx +10 -1
- package/src/components/Chart/Pie/index.tsx +10 -1
- package/src/components/Chart/Polar/index.tsx +12 -2
- package/src/components/Chart/Radar/index.tsx +10 -1
- package/src/components/Chart/Sankey/index.tsx +12 -2
- package/src/components/Chart/Tree/index.tsx +12 -2
- package/src/components/Chart/_hooks/useContainerReady.ts +50 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import useContainerReady from "../_hooks/useContainerReady";
|
|
2
3
|
import styles from "../index.module.scss";
|
|
3
4
|
import { ChartCommonProps, ChartOptionType, ChartsOption } from "..";
|
|
4
5
|
import * as echarts from "echarts";
|
|
@@ -74,6 +75,8 @@ const Heatmap: React.FC<ChartHeatmapProps> = (props) => {
|
|
|
74
75
|
} = props;
|
|
75
76
|
const chartRef = useRef<HTMLDivElement>(null);
|
|
76
77
|
const chartInstanceRef = useRef<echarts.ECharts | null>(null);
|
|
78
|
+
// Defers `echarts.init` until the container has a box — see the hook for why
|
|
79
|
+
const containerReady = useContainerReady(chartRef);
|
|
77
80
|
const resizeObserverRef = useRef<ResizeObserver | null>(null);
|
|
78
81
|
// echarts options are plain JS and cannot read the `--vita-*` variables, so the axis and legend
|
|
79
82
|
// colours have to be resolved against the active appearance here
|
|
@@ -255,12 +258,17 @@ const Heatmap: React.FC<ChartHeatmapProps> = (props) => {
|
|
|
255
258
|
|
|
256
259
|
// Callback for handling chart resize
|
|
257
260
|
const handleResize = useCallback(() => {
|
|
261
|
+
// A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
|
|
262
|
+
// the laid-out canvas away and it has to be rebuilt when the tab comes back
|
|
263
|
+
const el = chartRef.current;
|
|
264
|
+
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
265
|
+
|
|
258
266
|
chartInstanceRef.current?.resize();
|
|
259
267
|
}, []);
|
|
260
268
|
|
|
261
269
|
// Initialize the chart
|
|
262
270
|
useEffect(() => {
|
|
263
|
-
if (!chartRef.current) return;
|
|
271
|
+
if (!chartRef.current || !containerReady) return;
|
|
264
272
|
|
|
265
273
|
// Initialize or reuse the existing instance
|
|
266
274
|
let chart = chartInstanceRef.current;
|
|
@@ -296,7 +304,9 @@ const Heatmap: React.FC<ChartHeatmapProps> = (props) => {
|
|
|
296
304
|
chartInstanceRef.current?.off("click", onClick);
|
|
297
305
|
}
|
|
298
306
|
};
|
|
299
|
-
}, [chartOption, handleResize, onChart, onClick
|
|
307
|
+
}, [chartOption, handleResize, onChart, onClick,
|
|
308
|
+
containerReady,
|
|
309
|
+
]);
|
|
300
310
|
|
|
301
311
|
// Clean up resources when the component unmounts
|
|
302
312
|
useEffect(() => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as echarts from "echarts";
|
|
2
|
+
import useContainerReady from "../_hooks/useContainerReady";
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
ChartCommonProps,
|
|
@@ -73,6 +74,8 @@ const ChartLine: React.FC<ChartLineProps> = (props) => {
|
|
|
73
74
|
} = props;
|
|
74
75
|
const chartRef = useRef<HTMLDivElement>(null);
|
|
75
76
|
const chartInstanceRef = useRef<echarts.ECharts | null>(null);
|
|
77
|
+
// Defers `echarts.init` until the container has a box — see the hook for why
|
|
78
|
+
const containerReady = useContainerReady(chartRef);
|
|
76
79
|
const resizeObserverRef = useRef<ResizeObserver | null>(null);
|
|
77
80
|
const prevXAxisKeyRef = useRef<string | null>(null);
|
|
78
81
|
const legendScrollRef = useRef<ReturnType<typeof autoScrollLegend> | null>(
|
|
@@ -267,12 +270,17 @@ const ChartLine: React.FC<ChartLineProps> = (props) => {
|
|
|
267
270
|
|
|
268
271
|
// Callback for handling chart resize
|
|
269
272
|
const handleResize = useCallback(() => {
|
|
273
|
+
// A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
|
|
274
|
+
// the laid-out canvas away and it has to be rebuilt when the tab comes back
|
|
275
|
+
const el = chartRef.current;
|
|
276
|
+
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
277
|
+
|
|
270
278
|
chartInstanceRef.current?.resize();
|
|
271
279
|
}, []);
|
|
272
280
|
|
|
273
281
|
// Initialize the chart
|
|
274
282
|
useEffect(() => {
|
|
275
|
-
if (!chartRef.current) return;
|
|
283
|
+
if (!chartRef.current || !containerReady) return;
|
|
276
284
|
|
|
277
285
|
// Initialize or reuse the existing instance
|
|
278
286
|
let chart = chartInstanceRef.current;
|
|
@@ -467,6 +475,7 @@ const ChartLine: React.FC<ChartLineProps> = (props) => {
|
|
|
467
475
|
legendScrollInterval,
|
|
468
476
|
// 图例是否接管滚轮由它推导,漏了会拿到过期的滚动配置
|
|
469
477
|
normalizedScroll,
|
|
478
|
+
containerReady,
|
|
470
479
|
]);
|
|
471
480
|
|
|
472
481
|
// Handle auto play
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as echarts from "echarts";
|
|
2
|
+
import useContainerReady from "../../_hooks/useContainerReady";
|
|
2
3
|
import "echarts-gl";
|
|
3
4
|
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
|
4
5
|
|
|
@@ -108,6 +109,8 @@ const ChartPie3D: React.FC<ChartPie3DProps> = (props) => {
|
|
|
108
109
|
|
|
109
110
|
const chartRef = useRef<HTMLDivElement>(null);
|
|
110
111
|
const chartInstanceRef = useRef<echarts.ECharts | null>(null);
|
|
112
|
+
// Defers `echarts.init` until the container has a box — see the hook for why
|
|
113
|
+
const containerReady = useContainerReady(chartRef);
|
|
111
114
|
const resizeObserverRef = useRef<ResizeObserver | null>(null);
|
|
112
115
|
const hoveredIndexRef = useRef<number | "">("");
|
|
113
116
|
const optionRef = useRef<ChartsOption | null>(null);
|
|
@@ -145,6 +148,11 @@ const ChartPie3D: React.FC<ChartPie3DProps> = (props) => {
|
|
|
145
148
|
]);
|
|
146
149
|
|
|
147
150
|
const handleResize = useCallback(() => {
|
|
151
|
+
// A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
|
|
152
|
+
// the laid-out canvas away and it has to be rebuilt when the tab comes back
|
|
153
|
+
const el = chartRef.current;
|
|
154
|
+
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
155
|
+
|
|
148
156
|
chartInstanceRef.current?.resize();
|
|
149
157
|
}, []);
|
|
150
158
|
|
|
@@ -163,7 +171,7 @@ const ChartPie3D: React.FC<ChartPie3DProps> = (props) => {
|
|
|
163
171
|
);
|
|
164
172
|
|
|
165
173
|
useEffect(() => {
|
|
166
|
-
if (!chartRef.current) return;
|
|
174
|
+
if (!chartRef.current || !containerReady) return;
|
|
167
175
|
|
|
168
176
|
let chart = chartInstanceRef.current;
|
|
169
177
|
if (!chart) {
|
|
@@ -205,6 +213,7 @@ const ChartPie3D: React.FC<ChartPie3DProps> = (props) => {
|
|
|
205
213
|
handleGlobalOut,
|
|
206
214
|
onChart,
|
|
207
215
|
onClick,
|
|
216
|
+
containerReady,
|
|
208
217
|
]);
|
|
209
218
|
|
|
210
219
|
useEffect(() => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as echarts from "echarts";
|
|
2
|
+
import useContainerReady from "../_hooks/useContainerReady";
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
ChartCommonProps,
|
|
@@ -66,6 +67,8 @@ const ChartPie: ChartPieFC = (props) => {
|
|
|
66
67
|
} = props;
|
|
67
68
|
const chartRef = useRef<HTMLDivElement>(null);
|
|
68
69
|
const chartInstanceRef = useRef<echarts.ECharts | null>(null);
|
|
70
|
+
// Defers `echarts.init` until the container has a box — see the hook for why
|
|
71
|
+
const containerReady = useContainerReady(chartRef);
|
|
69
72
|
const resizeObserverRef = useRef<ResizeObserver | null>(null);
|
|
70
73
|
const legendScrollRef = useRef<ReturnType<typeof autoScrollLegend> | null>(
|
|
71
74
|
null
|
|
@@ -241,12 +244,17 @@ const ChartPie: ChartPieFC = (props) => {
|
|
|
241
244
|
|
|
242
245
|
// Callback for handling chart resize
|
|
243
246
|
const handleResize = useCallback(() => {
|
|
247
|
+
// A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
|
|
248
|
+
// the laid-out canvas away and it has to be rebuilt when the tab comes back
|
|
249
|
+
const el = chartRef.current;
|
|
250
|
+
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
251
|
+
|
|
244
252
|
chartInstanceRef.current?.resize();
|
|
245
253
|
}, []);
|
|
246
254
|
|
|
247
255
|
// Initialize the chart
|
|
248
256
|
useEffect(() => {
|
|
249
|
-
if (!chartRef.current) return;
|
|
257
|
+
if (!chartRef.current || !containerReady) return;
|
|
250
258
|
|
|
251
259
|
// Initialize or reuse the existing instance
|
|
252
260
|
let chart = chartInstanceRef.current;
|
|
@@ -313,6 +321,7 @@ const ChartPie: ChartPieFC = (props) => {
|
|
|
313
321
|
seriesData,
|
|
314
322
|
legendVisibleCount,
|
|
315
323
|
legendScrollInterval,
|
|
324
|
+
containerReady,
|
|
316
325
|
]);
|
|
317
326
|
|
|
318
327
|
// Clean up resources when the component unmounts
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as echarts from "echarts";
|
|
2
|
+
import useContainerReady from "../_hooks/useContainerReady";
|
|
2
3
|
import {
|
|
3
4
|
ChartCommonProps,
|
|
4
5
|
ChartOptionType,
|
|
@@ -36,6 +37,8 @@ const Polar: React.FC<ChartPolarProps> = (props) => {
|
|
|
36
37
|
} = props;
|
|
37
38
|
const chartRef = useRef<HTMLDivElement>(null);
|
|
38
39
|
const chartInstanceRef = useRef<echarts.ECharts | null>(null);
|
|
40
|
+
// Defers `echarts.init` until the container has a box — see the hook for why
|
|
41
|
+
const containerReady = useContainerReady(chartRef);
|
|
39
42
|
const resizeObserverRef = useRef<ResizeObserver | null>(null);
|
|
40
43
|
|
|
41
44
|
// Cache the chart option with useMemo
|
|
@@ -170,12 +173,17 @@ const Polar: React.FC<ChartPolarProps> = (props) => {
|
|
|
170
173
|
|
|
171
174
|
// Callback handling chart resize
|
|
172
175
|
const handleResize = useCallback(() => {
|
|
176
|
+
// A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
|
|
177
|
+
// the laid-out canvas away and it has to be rebuilt when the tab comes back
|
|
178
|
+
const el = chartRef.current;
|
|
179
|
+
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
180
|
+
|
|
173
181
|
chartInstanceRef.current?.resize();
|
|
174
182
|
}, []);
|
|
175
183
|
|
|
176
184
|
// Initialize the chart
|
|
177
185
|
useEffect(() => {
|
|
178
|
-
if (!chartRef.current) return;
|
|
186
|
+
if (!chartRef.current || !containerReady) return;
|
|
179
187
|
|
|
180
188
|
// Initialize a new instance or reuse the existing one
|
|
181
189
|
let chart = chartInstanceRef.current;
|
|
@@ -200,7 +208,9 @@ const Polar: React.FC<ChartPolarProps> = (props) => {
|
|
|
200
208
|
return () => {
|
|
201
209
|
window.removeEventListener("resize", handleResize);
|
|
202
210
|
};
|
|
203
|
-
}, [chartOption, handleResize
|
|
211
|
+
}, [chartOption, handleResize,
|
|
212
|
+
containerReady,
|
|
213
|
+
]);
|
|
204
214
|
|
|
205
215
|
// Clean up resources on unmount
|
|
206
216
|
useEffect(() => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as echarts from "echarts";
|
|
2
|
+
import useContainerReady from "../_hooks/useContainerReady";
|
|
2
3
|
import { ChartCommonProps, ChartOptionType, ChartsOption } from "..";
|
|
3
4
|
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
|
4
5
|
import styles from "../index.module.scss";
|
|
@@ -55,6 +56,8 @@ const Radar: React.FC<ChartRadarProps> = (props) => {
|
|
|
55
56
|
|
|
56
57
|
const chartRef = useRef<HTMLDivElement>(null);
|
|
57
58
|
const chartInstanceRef = useRef<echarts.ECharts | null>(null);
|
|
59
|
+
// Defers `echarts.init` until the container has a box — see the hook for why
|
|
60
|
+
const containerReady = useContainerReady(chartRef);
|
|
58
61
|
const resizeObserverRef = useRef<ResizeObserver | null>(null);
|
|
59
62
|
const hoveredIndicatorRef = useRef<number>(-1);
|
|
60
63
|
|
|
@@ -188,11 +191,16 @@ const Radar: React.FC<ChartRadarProps> = (props) => {
|
|
|
188
191
|
]);
|
|
189
192
|
|
|
190
193
|
const handleResize = useCallback(() => {
|
|
194
|
+
// A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
|
|
195
|
+
// the laid-out canvas away and it has to be rebuilt when the tab comes back
|
|
196
|
+
const el = chartRef.current;
|
|
197
|
+
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
198
|
+
|
|
191
199
|
chartInstanceRef.current?.resize();
|
|
192
200
|
}, []);
|
|
193
201
|
|
|
194
202
|
useEffect(() => {
|
|
195
|
-
if (!chartRef.current) return;
|
|
203
|
+
if (!chartRef.current || !containerReady) return;
|
|
196
204
|
|
|
197
205
|
let chart = chartInstanceRef.current;
|
|
198
206
|
if (!chart) {
|
|
@@ -343,6 +351,7 @@ const Radar: React.FC<ChartRadarProps> = (props) => {
|
|
|
343
351
|
singleIndicatorHighlight,
|
|
344
352
|
singleIndicatorHoverFill,
|
|
345
353
|
color,
|
|
354
|
+
containerReady,
|
|
346
355
|
]);
|
|
347
356
|
|
|
348
357
|
useEffect(() => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import useContainerReady from "../_hooks/useContainerReady";
|
|
2
3
|
import styles from "../index.module.scss";
|
|
3
4
|
import { ChartCommonProps, ChartOptionType, ChartsOption } from "..";
|
|
4
5
|
import * as echarts from "echarts";
|
|
@@ -16,6 +17,8 @@ const Sankey: React.FC<ChartSankeyProps> = (props) => {
|
|
|
16
17
|
const { className, style, seriesData, seriesLinks, series, getImage } = props;
|
|
17
18
|
const chartRef = useRef<HTMLDivElement>(null);
|
|
18
19
|
const chartInstanceRef = useRef<echarts.ECharts | null>(null);
|
|
20
|
+
// Defers `echarts.init` until the container has a box — see the hook for why
|
|
21
|
+
const containerReady = useContainerReady(chartRef);
|
|
19
22
|
const resizeObserverRef = useRef<ResizeObserver | null>(null);
|
|
20
23
|
|
|
21
24
|
// Cache the chart option with useMemo
|
|
@@ -100,12 +103,17 @@ const Sankey: React.FC<ChartSankeyProps> = (props) => {
|
|
|
100
103
|
|
|
101
104
|
// Callback handling chart resize
|
|
102
105
|
const handleResize = useCallback(() => {
|
|
106
|
+
// A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
|
|
107
|
+
// the laid-out canvas away and it has to be rebuilt when the tab comes back
|
|
108
|
+
const el = chartRef.current;
|
|
109
|
+
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
110
|
+
|
|
103
111
|
chartInstanceRef.current?.resize();
|
|
104
112
|
}, []);
|
|
105
113
|
|
|
106
114
|
// Initialize the chart
|
|
107
115
|
useEffect(() => {
|
|
108
|
-
if (!chartRef.current) return;
|
|
116
|
+
if (!chartRef.current || !containerReady) return;
|
|
109
117
|
|
|
110
118
|
// Initialize a new instance or reuse the existing one
|
|
111
119
|
let chart = chartInstanceRef.current;
|
|
@@ -141,7 +149,9 @@ const Sankey: React.FC<ChartSankeyProps> = (props) => {
|
|
|
141
149
|
return () => {
|
|
142
150
|
window.removeEventListener("resize", handleResize);
|
|
143
151
|
};
|
|
144
|
-
}, [chartOption, handleResize, getImage
|
|
152
|
+
}, [chartOption, handleResize, getImage,
|
|
153
|
+
containerReady,
|
|
154
|
+
]);
|
|
145
155
|
|
|
146
156
|
// Clean up resources on unmount
|
|
147
157
|
useEffect(() => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import useContainerReady from "../_hooks/useContainerReady";
|
|
2
3
|
import styles from "../index.module.scss";
|
|
3
4
|
import {
|
|
4
5
|
ChartCommonProps,
|
|
@@ -19,6 +20,8 @@ const Tree: React.FC<ChartTreeProps> = (props) => {
|
|
|
19
20
|
const { className, style, seriesData, series, getImage } = props;
|
|
20
21
|
const chartRef = useRef<HTMLDivElement>(null);
|
|
21
22
|
const chartInstanceRef = useRef<echarts.ECharts | null>(null);
|
|
23
|
+
// Defers `echarts.init` until the container has a box — see the hook for why
|
|
24
|
+
const containerReady = useContainerReady(chartRef);
|
|
22
25
|
const resizeObserverRef = useRef<ResizeObserver | null>(null);
|
|
23
26
|
|
|
24
27
|
// Cache the chart option with useMemo
|
|
@@ -40,12 +43,17 @@ const Tree: React.FC<ChartTreeProps> = (props) => {
|
|
|
40
43
|
|
|
41
44
|
// Callback handling chart resize
|
|
42
45
|
const handleResize = useCallback(() => {
|
|
46
|
+
// A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
|
|
47
|
+
// the laid-out canvas away and it has to be rebuilt when the tab comes back
|
|
48
|
+
const el = chartRef.current;
|
|
49
|
+
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
50
|
+
|
|
43
51
|
chartInstanceRef.current?.resize();
|
|
44
52
|
}, []);
|
|
45
53
|
|
|
46
54
|
// Initialize the chart
|
|
47
55
|
useEffect(() => {
|
|
48
|
-
if (!chartRef.current) return;
|
|
56
|
+
if (!chartRef.current || !containerReady) return;
|
|
49
57
|
|
|
50
58
|
// Initialize a new instance or reuse the existing one
|
|
51
59
|
let chart = chartInstanceRef.current;
|
|
@@ -81,7 +89,9 @@ const Tree: React.FC<ChartTreeProps> = (props) => {
|
|
|
81
89
|
return () => {
|
|
82
90
|
window.removeEventListener("resize", handleResize);
|
|
83
91
|
};
|
|
84
|
-
}, [chartOption, handleResize, getImage
|
|
92
|
+
}, [chartOption, handleResize, getImage,
|
|
93
|
+
containerReady,
|
|
94
|
+
]);
|
|
85
95
|
|
|
86
96
|
// Clean up resources on unmount
|
|
87
97
|
useEffect(() => {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { RefObject, useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Whether the referenced element has a non-zero box yet.
|
|
5
|
+
*
|
|
6
|
+
* `echarts.init` on a 0×0 element logs `Can't get DOM width or height` and builds a canvas with no
|
|
7
|
+
* size, recovering only on the next `resize()`. A chart hits that path whenever it mounts before
|
|
8
|
+
* layout has given it space — inside a keep-alive tab that is not the active one, a collapsed
|
|
9
|
+
* panel, a modal that has not opened yet — so consuming apps see the warning for charts that end
|
|
10
|
+
* up rendering perfectly well.
|
|
11
|
+
*
|
|
12
|
+
* Gating `init` on this flag defers it to the first frame the element actually occupies space.
|
|
13
|
+
*
|
|
14
|
+
* It **latches**: once measured, the element going back to 0×0 (a keep-alive tab losing focus)
|
|
15
|
+
* must not read as "not ready" again, or the init effect would tear the chart down and re-create
|
|
16
|
+
* it on every tab switch.
|
|
17
|
+
*/
|
|
18
|
+
const useContainerReady = (ref: RefObject<HTMLElement | null>): boolean => {
|
|
19
|
+
const [ready, setReady] = useState(false);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (ready) return;
|
|
23
|
+
|
|
24
|
+
const el = ref.current;
|
|
25
|
+
if (!el) return;
|
|
26
|
+
|
|
27
|
+
const measure = (): boolean => {
|
|
28
|
+
const { width, height } = el.getBoundingClientRect();
|
|
29
|
+
if (width > 0 && height > 0) {
|
|
30
|
+
setReady(true);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Already laid out on mount — the common case, and it must not cost an extra frame
|
|
37
|
+
if (measure()) return;
|
|
38
|
+
|
|
39
|
+
const observer = new ResizeObserver(() => {
|
|
40
|
+
if (measure()) observer.disconnect();
|
|
41
|
+
});
|
|
42
|
+
observer.observe(el);
|
|
43
|
+
|
|
44
|
+
return () => observer.disconnect();
|
|
45
|
+
}, [ready, ref]);
|
|
46
|
+
|
|
47
|
+
return ready;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default useContainerReady;
|