@hsu-react/ui 2.4.0 → 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.
Files changed (57) hide show
  1. package/es/components/Chart/Bar/index.js +21 -7
  2. package/es/components/Chart/Common/index.js +9 -2
  3. package/es/components/Chart/Gauge/index.js +9 -2
  4. package/es/components/Chart/Heatmap/index.js +21 -6
  5. package/es/components/Chart/Line/index.js +21 -6
  6. package/es/components/Chart/Pie/Pie3D/index.js +9 -2
  7. package/es/components/Chart/Pie/index.js +9 -2
  8. package/es/components/Chart/Polar/index.js +9 -2
  9. package/es/components/Chart/Radar/index.js +9 -2
  10. package/es/components/Chart/Sankey/index.js +9 -2
  11. package/es/components/Chart/Tree/index.js +9 -2
  12. package/es/components/Chart/_hooks/useContainerReady.d.ts +18 -0
  13. package/es/components/Chart/_hooks/useContainerReady.js +56 -0
  14. package/es/components/Chart/_utils/cartesian.d.ts +3 -2
  15. package/es/components/Chart/_utils/cartesian.js +9 -10
  16. package/es/components/Chart/_utils/chartTheme.d.ts +22 -0
  17. package/es/components/Chart/_utils/chartTheme.js +23 -0
  18. package/es/components/Chart/_utils/heatmap.d.ts +3 -2
  19. package/es/components/Chart/_utils/heatmap.js +7 -10
  20. package/es/components/Panel/ListPanel/ListModalPanel/index.module.scss +10 -0
  21. package/lib/components/Chart/Bar/index.js +19 -7
  22. package/lib/components/Chart/Common/index.js +9 -2
  23. package/lib/components/Chart/Gauge/index.js +9 -2
  24. package/lib/components/Chart/Heatmap/index.js +19 -6
  25. package/lib/components/Chart/Line/index.js +19 -6
  26. package/lib/components/Chart/Pie/Pie3D/index.js +9 -2
  27. package/lib/components/Chart/Pie/index.js +9 -2
  28. package/lib/components/Chart/Polar/index.js +9 -2
  29. package/lib/components/Chart/Radar/index.js +9 -2
  30. package/lib/components/Chart/Sankey/index.js +9 -2
  31. package/lib/components/Chart/Tree/index.js +9 -2
  32. package/lib/components/Chart/_hooks/useContainerReady.d.ts +18 -0
  33. package/lib/components/Chart/_hooks/useContainerReady.js +51 -0
  34. package/lib/components/Chart/_utils/cartesian.d.ts +3 -2
  35. package/lib/components/Chart/_utils/cartesian.js +9 -11
  36. package/lib/components/Chart/_utils/chartTheme.d.ts +22 -0
  37. package/lib/components/Chart/_utils/chartTheme.js +29 -0
  38. package/lib/components/Chart/_utils/heatmap.d.ts +3 -2
  39. package/lib/components/Chart/_utils/heatmap.js +7 -10
  40. package/lib/components/Panel/ListPanel/ListModalPanel/index.module.scss +10 -0
  41. package/package.json +1 -1
  42. package/src/components/Chart/Bar/index.tsx +20 -5
  43. package/src/components/Chart/Common/index.tsx +12 -2
  44. package/src/components/Chart/Gauge/index.tsx +12 -2
  45. package/src/components/Chart/Heatmap/index.tsx +22 -5
  46. package/src/components/Chart/Line/index.tsx +20 -4
  47. package/src/components/Chart/Pie/Pie3D/index.tsx +10 -1
  48. package/src/components/Chart/Pie/index.tsx +10 -1
  49. package/src/components/Chart/Polar/index.tsx +12 -2
  50. package/src/components/Chart/Radar/index.tsx +10 -1
  51. package/src/components/Chart/Sankey/index.tsx +12 -2
  52. package/src/components/Chart/Tree/index.tsx +12 -2
  53. package/src/components/Chart/_hooks/useContainerReady.ts +50 -0
  54. package/src/components/Chart/_utils/cartesian.ts +11 -10
  55. package/src/components/Chart/_utils/chartTheme.ts +34 -0
  56. package/src/components/Chart/_utils/heatmap.ts +10 -10
  57. package/src/components/Panel/ListPanel/ListModalPanel/index.module.scss +10 -0
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  var echarts = _interopRequireWildcard(require("echarts"));
8
+ var _useContainerReady = _interopRequireDefault(require("../_hooks/useContainerReady"));
8
9
  var _react = _interopRequireWildcard(require("react"));
9
10
  var _indexModule = _interopRequireDefault(require("../index.module.scss"));
10
11
  var _jsxRuntime = require("react/jsx-runtime");
@@ -30,6 +31,8 @@ const Polar = props => {
30
31
  } = props;
31
32
  const chartRef = (0, _react.useRef)(null);
32
33
  const chartInstanceRef = (0, _react.useRef)(null);
34
+ // Defers `echarts.init` until the container has a box — see the hook for why
35
+ const containerReady = (0, _useContainerReady.default)(chartRef);
33
36
  const resizeObserverRef = (0, _react.useRef)(null);
34
37
 
35
38
  // Cache the chart option with useMemo
@@ -144,12 +147,16 @@ const Polar = props => {
144
147
 
145
148
  // Callback handling chart resize
146
149
  const handleResize = (0, _react.useCallback)(() => {
150
+ // A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
151
+ // the laid-out canvas away and it has to be rebuilt when the tab comes back
152
+ const el = chartRef.current;
153
+ if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
147
154
  chartInstanceRef.current?.resize();
148
155
  }, []);
149
156
 
150
157
  // Initialize the chart
151
158
  (0, _react.useEffect)(() => {
152
- if (!chartRef.current) return;
159
+ if (!chartRef.current || !containerReady) return;
153
160
 
154
161
  // Initialize a new instance or reuse the existing one
155
162
  let chart = chartInstanceRef.current;
@@ -174,7 +181,7 @@ const Polar = props => {
174
181
  return () => {
175
182
  window.removeEventListener("resize", handleResize);
176
183
  };
177
- }, [chartOption, handleResize]);
184
+ }, [chartOption, handleResize, containerReady]);
178
185
 
179
186
  // Clean up resources on unmount
180
187
  (0, _react.useEffect)(() => {
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  var echarts = _interopRequireWildcard(require("echarts"));
8
+ var _useContainerReady = _interopRequireDefault(require("../_hooks/useContainerReady"));
8
9
  var _react = _interopRequireWildcard(require("react"));
9
10
  var _indexModule = _interopRequireDefault(require("../index.module.scss"));
10
11
  var _jsxRuntime = require("react/jsx-runtime");
@@ -33,6 +34,8 @@ const Radar = props => {
33
34
  } = props;
34
35
  const chartRef = (0, _react.useRef)(null);
35
36
  const chartInstanceRef = (0, _react.useRef)(null);
37
+ // Defers `echarts.init` until the container has a box — see the hook for why
38
+ const containerReady = (0, _useContainerReady.default)(chartRef);
36
39
  const resizeObserverRef = (0, _react.useRef)(null);
37
40
  const hoveredIndicatorRef = (0, _react.useRef)(-1);
38
41
  const singleIndicatorEnabled = Boolean(singleIndicatorTooltipProp) && singleIndicatorMode !== undefined;
@@ -144,10 +147,14 @@ const Radar = props => {
144
147
  return option;
145
148
  }, [indicators, data, color, name, radar, series, coreOption, singleIndicatorEnabled, singleIndicatorFormatter, singleIndicatorRegion]);
146
149
  const handleResize = (0, _react.useCallback)(() => {
150
+ // A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
151
+ // the laid-out canvas away and it has to be rebuilt when the tab comes back
152
+ const el = chartRef.current;
153
+ if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
147
154
  chartInstanceRef.current?.resize();
148
155
  }, []);
149
156
  (0, _react.useEffect)(() => {
150
- if (!chartRef.current) return;
157
+ if (!chartRef.current || !containerReady) return;
151
158
  let chart = chartInstanceRef.current;
152
159
  if (!chart) {
153
160
  chart = echarts.init(chartRef.current);
@@ -263,7 +270,7 @@ const Radar = props => {
263
270
  }
264
271
  }
265
272
  };
266
- }, [chartOption, handleResize, singleIndicatorEnabled, singleIndicatorRegion, singleIndicatorHighlight, singleIndicatorHoverFill, color]);
273
+ }, [chartOption, handleResize, singleIndicatorEnabled, singleIndicatorRegion, singleIndicatorHighlight, singleIndicatorHoverFill, color, containerReady]);
267
274
  (0, _react.useEffect)(() => {
268
275
  return () => {
269
276
  if (resizeObserverRef.current) {
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
+ var _useContainerReady = _interopRequireDefault(require("../_hooks/useContainerReady"));
8
9
  var _indexModule = _interopRequireDefault(require("../index.module.scss"));
9
10
  var echarts = _interopRequireWildcard(require("echarts"));
10
11
  var _jsxRuntime = require("react/jsx-runtime");
@@ -22,6 +23,8 @@ const Sankey = props => {
22
23
  } = props;
23
24
  const chartRef = (0, _react.useRef)(null);
24
25
  const chartInstanceRef = (0, _react.useRef)(null);
26
+ // Defers `echarts.init` until the container has a box — see the hook for why
27
+ const containerReady = (0, _useContainerReady.default)(chartRef);
25
28
  const resizeObserverRef = (0, _react.useRef)(null);
26
29
 
27
30
  // Cache the chart option with useMemo
@@ -103,12 +106,16 @@ const Sankey = props => {
103
106
 
104
107
  // Callback handling chart resize
105
108
  const handleResize = (0, _react.useCallback)(() => {
109
+ // A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
110
+ // the laid-out canvas away and it has to be rebuilt when the tab comes back
111
+ const el = chartRef.current;
112
+ if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
106
113
  chartInstanceRef.current?.resize();
107
114
  }, []);
108
115
 
109
116
  // Initialize the chart
110
117
  (0, _react.useEffect)(() => {
111
- if (!chartRef.current) return;
118
+ if (!chartRef.current || !containerReady) return;
112
119
 
113
120
  // Initialize a new instance or reuse the existing one
114
121
  let chart = chartInstanceRef.current;
@@ -142,7 +149,7 @@ const Sankey = props => {
142
149
  return () => {
143
150
  window.removeEventListener("resize", handleResize);
144
151
  };
145
- }, [chartOption, handleResize, getImage]);
152
+ }, [chartOption, handleResize, getImage, containerReady]);
146
153
 
147
154
  // Clean up resources on unmount
148
155
  (0, _react.useEffect)(() => {
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
+ var _useContainerReady = _interopRequireDefault(require("../_hooks/useContainerReady"));
8
9
  var _indexModule = _interopRequireDefault(require("../index.module.scss"));
9
10
  var echarts = _interopRequireWildcard(require("echarts"));
10
11
  var _hsuUtils = require("hsu-utils");
@@ -23,6 +24,8 @@ const Tree = props => {
23
24
  } = props;
24
25
  const chartRef = (0, _react.useRef)(null);
25
26
  const chartInstanceRef = (0, _react.useRef)(null);
27
+ // Defers `echarts.init` until the container has a box — see the hook for why
28
+ const containerReady = (0, _useContainerReady.default)(chartRef);
26
29
  const resizeObserverRef = (0, _react.useRef)(null);
27
30
 
28
31
  // Cache the chart option with useMemo
@@ -43,12 +46,16 @@ const Tree = props => {
43
46
 
44
47
  // Callback handling chart resize
45
48
  const handleResize = (0, _react.useCallback)(() => {
49
+ // A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
50
+ // the laid-out canvas away and it has to be rebuilt when the tab comes back
51
+ const el = chartRef.current;
52
+ if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
46
53
  chartInstanceRef.current?.resize();
47
54
  }, []);
48
55
 
49
56
  // Initialize the chart
50
57
  (0, _react.useEffect)(() => {
51
- if (!chartRef.current) return;
58
+ if (!chartRef.current || !containerReady) return;
52
59
 
53
60
  // Initialize a new instance or reuse the existing one
54
61
  let chart = chartInstanceRef.current;
@@ -82,7 +89,7 @@ const Tree = props => {
82
89
  return () => {
83
90
  window.removeEventListener("resize", handleResize);
84
91
  };
85
- }, [chartOption, handleResize, getImage]);
92
+ }, [chartOption, handleResize, getImage, containerReady]);
86
93
 
87
94
  // Clean up resources on unmount
88
95
  (0, _react.useEffect)(() => {
@@ -0,0 +1,18 @@
1
+ import { RefObject } from "react";
2
+ /**
3
+ * Whether the referenced element has a non-zero box yet.
4
+ *
5
+ * `echarts.init` on a 0×0 element logs `Can't get DOM width or height` and builds a canvas with no
6
+ * size, recovering only on the next `resize()`. A chart hits that path whenever it mounts before
7
+ * layout has given it space — inside a keep-alive tab that is not the active one, a collapsed
8
+ * panel, a modal that has not opened yet — so consuming apps see the warning for charts that end
9
+ * up rendering perfectly well.
10
+ *
11
+ * Gating `init` on this flag defers it to the first frame the element actually occupies space.
12
+ *
13
+ * It **latches**: once measured, the element going back to 0×0 (a keep-alive tab losing focus)
14
+ * must not read as "not ready" again, or the init effect would tear the chart down and re-create
15
+ * it on every tab switch.
16
+ */
17
+ declare const useContainerReady: (ref: RefObject<HTMLElement | null>) => boolean;
18
+ export default useContainerReady;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = require("react");
8
+ /**
9
+ * Whether the referenced element has a non-zero box yet.
10
+ *
11
+ * `echarts.init` on a 0×0 element logs `Can't get DOM width or height` and builds a canvas with no
12
+ * size, recovering only on the next `resize()`. A chart hits that path whenever it mounts before
13
+ * layout has given it space — inside a keep-alive tab that is not the active one, a collapsed
14
+ * panel, a modal that has not opened yet — so consuming apps see the warning for charts that end
15
+ * up rendering perfectly well.
16
+ *
17
+ * Gating `init` on this flag defers it to the first frame the element actually occupies space.
18
+ *
19
+ * It **latches**: once measured, the element going back to 0×0 (a keep-alive tab losing focus)
20
+ * must not read as "not ready" again, or the init effect would tear the chart down and re-create
21
+ * it on every tab switch.
22
+ */
23
+ const useContainerReady = ref => {
24
+ const [ready, setReady] = (0, _react.useState)(false);
25
+ (0, _react.useEffect)(() => {
26
+ if (ready) return;
27
+ const el = ref.current;
28
+ if (!el) return;
29
+ const measure = () => {
30
+ const {
31
+ width,
32
+ height
33
+ } = el.getBoundingClientRect();
34
+ if (width > 0 && height > 0) {
35
+ setReady(true);
36
+ return true;
37
+ }
38
+ return false;
39
+ };
40
+
41
+ // Already laid out on mount — the common case, and it must not cost an extra frame
42
+ if (measure()) return;
43
+ const observer = new ResizeObserver(() => {
44
+ if (measure()) observer.disconnect();
45
+ });
46
+ observer.observe(el);
47
+ return () => observer.disconnect();
48
+ }, [ready, ref]);
49
+ return ready;
50
+ };
51
+ var _default = exports.default = useContainerReady;
@@ -1,5 +1,6 @@
1
1
  import * as echarts from "echarts";
2
2
  import { ChartOptionType, SeriesDataType } from "..";
3
+ import { ChartChrome } from "./chartTheme";
3
4
  export interface ChartScrollConfig {
4
5
  enabled?: boolean;
5
6
  autoScroll?: boolean;
@@ -29,8 +30,8 @@ export interface DataZoomIndexWindow {
29
30
  * percentages produced by native echarts interactions are rounded outward, so each boundary may include at most one extra item, without affecting the axis magnitude.
30
31
  */
31
32
  export declare const percentWindowToIndexWindow: (startPercent: number, endPercent: number, total: number) => DataZoomIndexWindow;
32
- export declare const createDefaultCategoryXAxis: (xAxisData?: Array<string>) => ChartOptionType;
33
- export declare const createDefaultValueYAxis: (mode?: "bar" | "line") => ChartOptionType;
33
+ export declare const createDefaultCategoryXAxis: (xAxisData: Array<string> | undefined, chrome: ChartChrome) => ChartOptionType;
34
+ export declare const createDefaultValueYAxis: (mode: "bar" | "line" | undefined, chrome: ChartChrome) => ChartOptionType;
34
35
  export declare const buildCartesianSeriesOptions: (mode: "bar" | "line", seriesData?: SeriesDataType, series?: echarts.SeriesOption | echarts.SeriesOption[]) => echarts.SeriesOption[];
35
36
  export declare const getSliderShow: (zoom?: echarts.DataZoomComponentOption | Record<string, unknown>) => boolean | undefined;
36
37
  export declare const resolveScrollConfig: (scrollConfig?: ChartScrollConfig) => NormalizedScrollConfig;
@@ -24,34 +24,32 @@ const percentWindowToIndexWindow = (startPercent, endPercent, total) => {
24
24
  };
25
25
  };
26
26
  exports.percentWindowToIndexWindow = percentWindowToIndexWindow;
27
- const createDefaultCategoryXAxis = xAxisData => ({
27
+ const createDefaultCategoryXAxis = (xAxisData, chrome) => ({
28
28
  type: "category",
29
29
  boundaryGap: true,
30
30
  data: xAxisData,
31
31
  axisLabel: {
32
32
  interval: 0,
33
33
  hideOverlap: true,
34
- textStyle: {
35
- fontSize: 14,
36
- color: "#373D48"
37
- }
34
+ // echarts removed the `textStyle` nesting under `axisLabel` in v4: keeping it means both
35
+ // properties are silently dropped *and* a deprecation warning is logged on every render.
36
+ fontSize: chrome.fontSize,
37
+ color: chrome.axis
38
38
  },
39
39
  axisTick: {
40
40
  show: false
41
41
  }
42
42
  });
43
43
  exports.createDefaultCategoryXAxis = createDefaultCategoryXAxis;
44
- const createDefaultValueYAxis = (mode = "bar") => ({
44
+ const createDefaultValueYAxis = (mode = "bar", chrome) => ({
45
45
  type: "value",
46
46
  axisLabel: {
47
- textStyle: {
48
- fontSize: 14,
49
- color: "#373D48"
50
- }
47
+ fontSize: chrome.fontSize,
48
+ color: chrome.axis
51
49
  },
52
50
  splitLine: mode === "line" ? {
53
51
  lineStyle: {
54
- color: "#C9CED6",
52
+ color: chrome.splitLine,
55
53
  type: "dashed"
56
54
  }
57
55
  } : {
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Colours for the chart *chrome* — axis ticks, split lines, legend labels.
3
+ *
4
+ * echarts options are plain JS, so they cannot read the `--vita-*` CSS variables the rest of the
5
+ * library styles itself with; the literal values have to be resolved here instead. These used to
6
+ * be hard-coded to a light-mode slate (`#373D48` / `#C9CED6`), which left every axis and legend
7
+ * nearly invisible once the page switched to the dark palette.
8
+ *
9
+ * The plotted data is deliberately *not* covered here: series colours come from echarts' own
10
+ * palette or from whatever the caller passes as `color`, and both read fine on either background.
11
+ */
12
+ export interface ChartChrome {
13
+ /** Legend labels — a real label, so it sits on the foreground ramp */
14
+ text: string;
15
+ /** Axis tick labels — secondary information, one step down */
16
+ axis: string;
17
+ /** Split lines behind the plot area */
18
+ splitLine: string;
19
+ /** Base font size, same scale the rest of the library uses */
20
+ fontSize: number;
21
+ }
22
+ export declare const resolveChartChrome: (dark: boolean) => ChartChrome;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.resolveChartChrome = void 0;
7
+ var _tokens = require("../../../styles/tokens");
8
+ /**
9
+ * Colours for the chart *chrome* — axis ticks, split lines, legend labels.
10
+ *
11
+ * echarts options are plain JS, so they cannot read the `--vita-*` CSS variables the rest of the
12
+ * library styles itself with; the literal values have to be resolved here instead. These used to
13
+ * be hard-coded to a light-mode slate (`#373D48` / `#C9CED6`), which left every axis and legend
14
+ * nearly invisible once the page switched to the dark palette.
15
+ *
16
+ * The plotted data is deliberately *not* covered here: series colours come from echarts' own
17
+ * palette or from whatever the caller passes as `color`, and both read fine on either background.
18
+ */
19
+
20
+ const resolveChartChrome = dark => {
21
+ const t = dark ? _tokens.darkTokens : _tokens.lightTokens;
22
+ return {
23
+ text: t.foreground,
24
+ axis: t.mutedForeground,
25
+ splitLine: t.border,
26
+ fontSize: _tokens.fontTokens.size
27
+ };
28
+ };
29
+ exports.resolveChartChrome = resolveChartChrome;
@@ -1,3 +1,4 @@
1
1
  import { ChartOptionType } from "..";
2
- export declare const createDefaultHeatmapXAxis: (xAxisData?: string[]) => ChartOptionType;
3
- export declare const createDefaultHeatmapYAxis: (yAxisData?: string[]) => ChartOptionType;
2
+ import { ChartChrome } from "./chartTheme";
3
+ export declare const createDefaultHeatmapXAxis: (xAxisData: string[] | undefined, chrome: ChartChrome) => ChartOptionType;
4
+ export declare const createDefaultHeatmapYAxis: (yAxisData: string[] | undefined, chrome: ChartChrome) => ChartOptionType;
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.createDefaultHeatmapYAxis = exports.createDefaultHeatmapXAxis = void 0;
7
- const createDefaultHeatmapXAxis = xAxisData => ({
7
+ const createDefaultHeatmapXAxis = (xAxisData, chrome) => ({
8
8
  type: "category",
9
9
  data: xAxisData,
10
10
  splitArea: {
@@ -13,27 +13,24 @@ const createDefaultHeatmapXAxis = xAxisData => ({
13
13
  axisLabel: {
14
14
  interval: 0,
15
15
  hideOverlap: true,
16
- textStyle: {
17
- fontSize: 14,
18
- color: "#373D48"
19
- }
16
+ // See the note in `cartesian.ts`: `axisLabel.textStyle` has been deprecated since echarts 4
17
+ fontSize: chrome.fontSize,
18
+ color: chrome.axis
20
19
  },
21
20
  axisTick: {
22
21
  show: false
23
22
  }
24
23
  });
25
24
  exports.createDefaultHeatmapXAxis = createDefaultHeatmapXAxis;
26
- const createDefaultHeatmapYAxis = yAxisData => ({
25
+ const createDefaultHeatmapYAxis = (yAxisData, chrome) => ({
27
26
  type: "category",
28
27
  data: yAxisData,
29
28
  splitArea: {
30
29
  show: true
31
30
  },
32
31
  axisLabel: {
33
- textStyle: {
34
- fontSize: 14,
35
- color: "#373D48"
36
- }
32
+ fontSize: chrome.fontSize,
33
+ color: chrome.axis
37
34
  },
38
35
  axisTick: {
39
36
  show: false
@@ -21,8 +21,18 @@
21
21
 
22
22
  height: 100%;
23
23
 
24
+ // Inside a modal the Search must not keep its card chrome. On a page it is an outlined white
25
+ // card standing on the grey page background; in a modal the body is already `--vita-surface`,
26
+ // so the same treatment paints a white card on white and only the border survives — a stray
27
+ // outline around the search row. The padding was already being cleared here; the background and
28
+ // border are the other half of the same intent, and `.tableContainer` below is flush for
29
+ // exactly this reason.
24
30
  .search {
25
31
  padding: 0px;
32
+
33
+ background: transparent;
34
+
35
+ border: none;
26
36
  }
27
37
 
28
38
  .tableContainer {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hsu-react/ui",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
4
4
  "description": "一套基于 React + Ant Design 的中后台业务组件库",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -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,
@@ -21,6 +22,8 @@ import {
21
22
  percentWindowToIndexWindow,
22
23
  resolveScrollConfig,
23
24
  } from "../_utils/cartesian";
25
+ import { resolveChartChrome } from "../_utils/chartTheme";
26
+ import useIsDark from "../../../hooks/useIsDark";
24
27
 
25
28
  export interface ChartBarProps extends ChartCommonProps {
26
29
  chartTitle?: string;
@@ -70,6 +73,8 @@ const ChartBar: React.FC<ChartBarProps> = (props) => {
70
73
  } = props;
71
74
  const chartRef = useRef<HTMLDivElement>(null);
72
75
  const chartInstanceRef = useRef<echarts.ECharts | null>(null);
76
+ // Defers `echarts.init` until the container has a box — see the hook for why
77
+ const containerReady = useContainerReady(chartRef);
73
78
  const resizeObserverRef = useRef<ResizeObserver | null>(null);
74
79
  const prevXAxisKeyRef = useRef<string | null>(null);
75
80
  const legendScrollRef = useRef<ReturnType<typeof autoScrollLegend> | null>(
@@ -79,11 +84,15 @@ const ChartBar: React.FC<ChartBarProps> = (props) => {
79
84
  () => resolveScrollConfig(scrollConfig),
80
85
  [scrollConfig],
81
86
  );
87
+ // echarts options are plain JS and cannot read the `--vita-*` variables, so the axis and
88
+ // legend colours have to be resolved against the active appearance here
89
+ const isDark = useIsDark();
90
+ const chrome = useMemo(() => resolveChartChrome(isDark), [isDark]);
82
91
 
83
92
  // Cache the chart option with useMemo
84
93
  const chartOption = useMemo(() => {
85
- const def_xAxis = createDefaultCategoryXAxis(xAxisData);
86
- const def_yAxis = createDefaultValueYAxis("bar");
94
+ const def_xAxis = createDefaultCategoryXAxis(xAxisData, chrome);
95
+ const def_yAxis = createDefaultValueYAxis("bar", chrome);
87
96
 
88
97
  // Process xAxis config
89
98
  const processedXAxis = Array.isArray(xAxis)
@@ -212,8 +221,7 @@ const ChartBar: React.FC<ChartBarProps> = (props) => {
212
221
  data: legendData,
213
222
  icon: "circle",
214
223
  textStyle: {
215
- color: "#373D48",
216
- fontSize: 16,
224
+ color: chrome.text,
217
225
  },
218
226
  itemWidth: 8,
219
227
  itemHeight: 8,
@@ -259,16 +267,22 @@ const ChartBar: React.FC<ChartBarProps> = (props) => {
259
267
  series,
260
268
  enableLegendAutoScroll,
261
269
  coreOption,
270
+ chrome,
262
271
  ]);
263
272
 
264
273
  // Callback for handling chart resize
265
274
  const handleResize = useCallback(() => {
275
+ // A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
276
+ // the laid-out canvas away and it has to be rebuilt when the tab comes back
277
+ const el = chartRef.current;
278
+ if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
279
+
266
280
  chartInstanceRef.current?.resize();
267
281
  }, []);
268
282
 
269
283
  // Initialize the chart
270
284
  useEffect(() => {
271
- if (!chartRef.current) return;
285
+ if (!chartRef.current || !containerReady) return;
272
286
 
273
287
  // Initialize or reuse the existing instance
274
288
  let chart = chartInstanceRef.current;
@@ -463,6 +477,7 @@ const ChartBar: React.FC<ChartBarProps> = (props) => {
463
477
  legendScrollInterval,
464
478
  // 图例是否接管滚轮由它推导,漏了会拿到过期的滚动配置
465
479
  normalizedScroll,
480
+ containerReady,
466
481
  ]);
467
482
 
468
483
  // Handle auto play
@@ -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";
@@ -7,6 +8,8 @@ const Common: React.FC<ChartCommonProps> = (props) => {
7
8
  const { className, style, onChart, ...coreOption } = props;
8
9
  const chartRef = useRef<HTMLDivElement>(null);
9
10
  const chartInstanceRef = useRef<echarts.ECharts | null>(null);
11
+ // Defers `echarts.init` until the container has a box — see the hook for why
12
+ const containerReady = useContainerReady(chartRef);
10
13
  const resizeObserverRef = useRef<ResizeObserver | null>(null);
11
14
 
12
15
  // Cache the chart configuration with useMemo
@@ -20,12 +23,17 @@ const Common: React.FC<ChartCommonProps> = (props) => {
20
23
 
21
24
  // Callback that handles chart resize
22
25
  const handleResize = useCallback(() => {
26
+ // A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
27
+ // the laid-out canvas away and it has to be rebuilt when the tab comes back
28
+ const el = chartRef.current;
29
+ if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
30
+
23
31
  chartInstanceRef.current?.resize();
24
32
  }, []);
25
33
 
26
34
  // Initialize the chart
27
35
  useEffect(() => {
28
- if (!chartRef.current) return;
36
+ if (!chartRef.current || !containerReady) return;
29
37
 
30
38
  // Initialize or reuse an existing instance
31
39
  let chart = chartInstanceRef.current;
@@ -53,7 +61,9 @@ const Common: React.FC<ChartCommonProps> = (props) => {
53
61
  return () => {
54
62
  window.removeEventListener("resize", handleResize);
55
63
  };
56
- }, [chartOption, handleResize, onChart]);
64
+ }, [chartOption, handleResize, onChart,
65
+ containerReady,
66
+ ]);
57
67
 
58
68
  // Clean up resources on component unmount
59
69
  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,
@@ -31,6 +32,8 @@ const Gauge: React.FC<ChartGaugeProps> = (props) => {
31
32
  } = series as echarts.GaugeSeriesOption;
32
33
  const chartRef = useRef<HTMLDivElement>(null);
33
34
  const chartInstanceRef = useRef<echarts.ECharts | null>(null);
35
+ // Defers `echarts.init` until the container has a box — see the hook for why
36
+ const containerReady = useContainerReady(chartRef);
34
37
  const resizeObserverRef = useRef<ResizeObserver | null>(null);
35
38
 
36
39
  // Cache the chart option with useMemo
@@ -104,12 +107,17 @@ const Gauge: React.FC<ChartGaugeProps> = (props) => {
104
107
 
105
108
  // Callback for handling chart resize
106
109
  const handleResize = useCallback(() => {
110
+ // A keep-alive tab losing focus fires the observer with a 0×0 box; resizing to that throws
111
+ // the laid-out canvas away and it has to be rebuilt when the tab comes back
112
+ const el = chartRef.current;
113
+ if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
114
+
107
115
  chartInstanceRef.current?.resize();
108
116
  }, []);
109
117
 
110
118
  // Initialize the chart
111
119
  useEffect(() => {
112
- if (!chartRef.current) return;
120
+ if (!chartRef.current || !containerReady) return;
113
121
 
114
122
  // Initialize or reuse the existing instance
115
123
  let chart = chartInstanceRef.current;
@@ -134,7 +142,9 @@ const Gauge: React.FC<ChartGaugeProps> = (props) => {
134
142
  return () => {
135
143
  window.removeEventListener("resize", handleResize);
136
144
  };
137
- }, [chartOption, handleResize]);
145
+ }, [chartOption, handleResize,
146
+ containerReady,
147
+ ]);
138
148
 
139
149
  // Clean up resources when the component unmounts
140
150
  useEffect(() => {