@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.
- package/es/components/Chart/Bar/index.js +21 -7
- 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 +21 -6
- package/es/components/Chart/Line/index.js +21 -6
- 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/es/components/Chart/_utils/cartesian.d.ts +3 -2
- package/es/components/Chart/_utils/cartesian.js +9 -10
- package/es/components/Chart/_utils/chartTheme.d.ts +22 -0
- package/es/components/Chart/_utils/chartTheme.js +23 -0
- package/es/components/Chart/_utils/heatmap.d.ts +3 -2
- package/es/components/Chart/_utils/heatmap.js +7 -10
- package/es/components/Panel/ListPanel/ListModalPanel/index.module.scss +10 -0
- package/lib/components/Chart/Bar/index.js +19 -7
- 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 +19 -6
- package/lib/components/Chart/Line/index.js +19 -6
- 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/lib/components/Chart/_utils/cartesian.d.ts +3 -2
- package/lib/components/Chart/_utils/cartesian.js +9 -11
- package/lib/components/Chart/_utils/chartTheme.d.ts +22 -0
- package/lib/components/Chart/_utils/chartTheme.js +29 -0
- package/lib/components/Chart/_utils/heatmap.d.ts +3 -2
- package/lib/components/Chart/_utils/heatmap.js +7 -10
- package/lib/components/Panel/ListPanel/ListModalPanel/index.module.scss +10 -0
- package/package.json +1 -1
- package/src/components/Chart/Bar/index.tsx +20 -5
- 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 +22 -5
- package/src/components/Chart/Line/index.tsx +20 -4
- 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
- package/src/components/Chart/_utils/cartesian.ts +11 -10
- package/src/components/Chart/_utils/chartTheme.ts +34 -0
- package/src/components/Chart/_utils/heatmap.ts +10 -10
- 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
|
|
33
|
-
export declare const createDefaultValueYAxis: (mode
|
|
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
|
-
|
|
36
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
color: "#373D48"
|
|
50
|
-
}
|
|
47
|
+
fontSize: chrome.fontSize,
|
|
48
|
+
color: chrome.axis
|
|
51
49
|
},
|
|
52
50
|
splitLine: mode === "line" ? {
|
|
53
51
|
lineStyle: {
|
|
54
|
-
color:
|
|
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
|
-
|
|
3
|
-
export declare const
|
|
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
|
-
|
|
18
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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,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:
|
|
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(() => {
|