@x-plat/design-system 0.5.34 → 0.5.35
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/dist/components/Chart/index.cjs +131 -115
- package/dist/components/Chart/index.css +4 -1
- package/dist/components/Chart/index.js +117 -101
- package/dist/components/index.cjs +162 -162
- package/dist/components/index.css +4 -1
- package/dist/components/index.js +142 -142
- package/dist/index.cjs +162 -162
- package/dist/index.css +4 -1
- package/dist/index.js +142 -142
- package/package.json +1 -1
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
// src/components/Chart/Chart.tsx
|
|
2
|
+
import React2 from "react";
|
|
3
|
+
|
|
4
|
+
// src/tokens/hooks/Portal.tsx
|
|
2
5
|
import React from "react";
|
|
3
|
-
import
|
|
6
|
+
import ReactDOM from "react-dom";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
var PortalContainerContext = React.createContext(null);
|
|
9
|
+
var Portal = ({ children }) => {
|
|
10
|
+
const contextContainer = React.useContext(PortalContainerContext);
|
|
11
|
+
if (typeof document === "undefined") return null;
|
|
12
|
+
const container = contextContainer ?? document.body;
|
|
13
|
+
return ReactDOM.createPortal(children, container);
|
|
14
|
+
};
|
|
15
|
+
Portal.displayName = "Portal";
|
|
16
|
+
var Portal_default = Portal;
|
|
17
|
+
|
|
18
|
+
// src/components/Chart/Chart.tsx
|
|
19
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
4
20
|
var CATEGORICAL_COUNT = 8;
|
|
5
21
|
var LINE_BAR_PALETTES = Array.from({ length: CATEGORICAL_COUNT }, (_, i) => {
|
|
6
22
|
const n = i + 1;
|
|
@@ -46,11 +62,11 @@ var toSmoothPath = (points) => {
|
|
|
46
62
|
};
|
|
47
63
|
var RESIZE_SETTLE_MS = 150;
|
|
48
64
|
var useChartSize = (ref) => {
|
|
49
|
-
const [size, setSize] =
|
|
50
|
-
const settleTimer =
|
|
51
|
-
const committedSize =
|
|
52
|
-
const initialRef =
|
|
53
|
-
|
|
65
|
+
const [size, setSize] = React2.useState({ width: 0, height: 0 });
|
|
66
|
+
const settleTimer = React2.useRef(0);
|
|
67
|
+
const committedSize = React2.useRef({ width: 0, height: 0 });
|
|
68
|
+
const initialRef = React2.useRef(true);
|
|
69
|
+
React2.useEffect(() => {
|
|
54
70
|
const el = ref.current;
|
|
55
71
|
if (!el) return;
|
|
56
72
|
const observer = new ResizeObserver((entries) => {
|
|
@@ -92,10 +108,10 @@ var useChartSize = (ref) => {
|
|
|
92
108
|
};
|
|
93
109
|
var prefersReducedMotion = () => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
94
110
|
var useChartAnimation = (containerRef, dataKey) => {
|
|
95
|
-
const [animate, setAnimate] =
|
|
96
|
-
const prevDataKey =
|
|
97
|
-
const hasAnimated =
|
|
98
|
-
|
|
111
|
+
const [animate, setAnimate] = React2.useState(false);
|
|
112
|
+
const prevDataKey = React2.useRef(dataKey);
|
|
113
|
+
const hasAnimated = React2.useRef(false);
|
|
114
|
+
React2.useEffect(() => {
|
|
99
115
|
if (prefersReducedMotion()) return;
|
|
100
116
|
const el = containerRef.current;
|
|
101
117
|
if (!el) return;
|
|
@@ -111,7 +127,7 @@ var useChartAnimation = (containerRef, dataKey) => {
|
|
|
111
127
|
observer.observe(el);
|
|
112
128
|
return () => observer.disconnect();
|
|
113
129
|
}, [containerRef]);
|
|
114
|
-
|
|
130
|
+
React2.useEffect(() => {
|
|
115
131
|
if (dataKey !== prevDataKey.current) {
|
|
116
132
|
prevDataKey.current = dataKey;
|
|
117
133
|
if (prefersReducedMotion()) return;
|
|
@@ -125,15 +141,15 @@ var useChartAnimation = (containerRef, dataKey) => {
|
|
|
125
141
|
};
|
|
126
142
|
var TOOLTIP_OFFSET = 12;
|
|
127
143
|
var useChartTooltip = (enabled) => {
|
|
128
|
-
const [tooltip, setTooltip] =
|
|
144
|
+
const [tooltip, setTooltip] = React2.useState({
|
|
129
145
|
visible: false,
|
|
130
146
|
clientX: 0,
|
|
131
147
|
clientY: 0,
|
|
132
148
|
content: ""
|
|
133
149
|
});
|
|
134
|
-
const containerRef =
|
|
135
|
-
const rafRef =
|
|
136
|
-
const move =
|
|
150
|
+
const containerRef = React2.useRef(null);
|
|
151
|
+
const rafRef = React2.useRef(0);
|
|
152
|
+
const move = React2.useCallback((e) => {
|
|
137
153
|
if (!enabled) return;
|
|
138
154
|
const cx = e.clientX;
|
|
139
155
|
const cy = e.clientY;
|
|
@@ -142,22 +158,22 @@ var useChartTooltip = (enabled) => {
|
|
|
142
158
|
setTooltip((prev) => ({ ...prev, clientX: cx, clientY: cy }));
|
|
143
159
|
});
|
|
144
160
|
}, [enabled]);
|
|
145
|
-
const show =
|
|
161
|
+
const show = React2.useCallback((e, content) => {
|
|
146
162
|
if (!enabled) return;
|
|
147
163
|
setTooltip({ visible: true, clientX: e.clientX, clientY: e.clientY, content });
|
|
148
164
|
}, [enabled]);
|
|
149
|
-
const hide =
|
|
165
|
+
const hide = React2.useCallback(() => {
|
|
150
166
|
cancelAnimationFrame(rafRef.current);
|
|
151
167
|
setTooltip((prev) => ({ ...prev, visible: false }));
|
|
152
168
|
}, []);
|
|
153
169
|
return { tooltip, show, hide, move, containerRef };
|
|
154
170
|
};
|
|
155
|
-
var GridLines =
|
|
171
|
+
var GridLines = React2.memo(({ width, height, chartH, maxVal }) => /* @__PURE__ */ jsx2(Fragment, { children: [0, 0.25, 0.5, 0.75, 1].map((ratio) => {
|
|
156
172
|
const y = PADDING.top + (1 - ratio) * chartH;
|
|
157
173
|
const val = Math.round(maxVal * ratio);
|
|
158
174
|
return /* @__PURE__ */ jsxs("g", { children: [
|
|
159
|
-
/* @__PURE__ */
|
|
160
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ jsx2("line", { x1: PADDING.left, y1: y, x2: width - PADDING.right, y2: y, className: "chart-grid" }),
|
|
176
|
+
/* @__PURE__ */ jsx2("text", { x: PADDING.left - 8, y: y + 4, className: "chart-axis-label", textAnchor: "end", children: val })
|
|
161
177
|
] }, ratio);
|
|
162
178
|
}) }));
|
|
163
179
|
GridLines.displayName = "GridLines";
|
|
@@ -167,18 +183,18 @@ var getLabelStep = (count, chartW) => {
|
|
|
167
183
|
if (count <= maxLabels) return 1;
|
|
168
184
|
return Math.ceil(count / maxLabels);
|
|
169
185
|
};
|
|
170
|
-
var AxisLabels =
|
|
186
|
+
var AxisLabels = React2.memo(({ labels, count, chartW, height }) => {
|
|
171
187
|
const step = getLabelStep(count, chartW);
|
|
172
|
-
return /* @__PURE__ */
|
|
188
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: labels.map((label, i) => {
|
|
173
189
|
if (i % step !== 0) return null;
|
|
174
190
|
const x = PADDING.left + i / (count - 1 || 1) * chartW;
|
|
175
|
-
return /* @__PURE__ */
|
|
191
|
+
return /* @__PURE__ */ jsx2("text", { x, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
|
|
176
192
|
}) });
|
|
177
193
|
});
|
|
178
194
|
AxisLabels.displayName = "AxisLabels";
|
|
179
195
|
var useCrosshair = (seriesPoints, entries, labels, chartH) => {
|
|
180
|
-
const [activeIndex, setActiveIndex] =
|
|
181
|
-
const handleMouseMove =
|
|
196
|
+
const [activeIndex, setActiveIndex] = React2.useState(null);
|
|
197
|
+
const handleMouseMove = React2.useCallback((e) => {
|
|
182
198
|
const svg = e.currentTarget;
|
|
183
199
|
const rect = svg.getBoundingClientRect();
|
|
184
200
|
const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
|
|
@@ -197,17 +213,17 @@ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
|
|
|
197
213
|
}
|
|
198
214
|
setActiveIndex(minDist <= threshold ? closest : null);
|
|
199
215
|
}, [seriesPoints]);
|
|
200
|
-
const handleMouseLeave =
|
|
216
|
+
const handleMouseLeave = React2.useCallback(() => {
|
|
201
217
|
setActiveIndex(null);
|
|
202
218
|
}, []);
|
|
203
|
-
const tooltipContent =
|
|
219
|
+
const tooltipContent = React2.useMemo(() => {
|
|
204
220
|
if (activeIndex === null) return "";
|
|
205
221
|
return entries.map(([key], di) => {
|
|
206
222
|
const p = seriesPoints[di]?.[activeIndex];
|
|
207
223
|
return p ? `${key}: ${p.v}` : "";
|
|
208
224
|
}).filter(Boolean).join(" / ");
|
|
209
225
|
}, [activeIndex, entries, seriesPoints]);
|
|
210
|
-
const getTooltipAt =
|
|
226
|
+
const getTooltipAt = React2.useCallback((idx) => {
|
|
211
227
|
return entries.map(([key], di) => {
|
|
212
228
|
const p = seriesPoints[di]?.[idx];
|
|
213
229
|
return p ? `${key}: ${p.v}` : "";
|
|
@@ -215,16 +231,16 @@ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
|
|
|
215
231
|
}, [entries, seriesPoints]);
|
|
216
232
|
return { activeIndex, handleMouseMove, handleMouseLeave, tooltipContent, getTooltipAt };
|
|
217
233
|
};
|
|
218
|
-
var LineChart =
|
|
219
|
-
const entries =
|
|
220
|
-
const maxVal =
|
|
234
|
+
var LineChart = React2.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
235
|
+
const entries = React2.useMemo(() => Object.entries(data), [data]);
|
|
236
|
+
const maxVal = React2.useMemo(() => {
|
|
221
237
|
const allValues = entries.flatMap(([, v]) => v);
|
|
222
238
|
return Math.max(...allValues) * 1.2 || 1;
|
|
223
239
|
}, [entries]);
|
|
224
240
|
const count = labels.length;
|
|
225
241
|
const chartW = width - PADDING.left - PADDING.right;
|
|
226
242
|
const chartH = height - PADDING.top - PADDING.bottom;
|
|
227
|
-
const seriesPoints =
|
|
243
|
+
const seriesPoints = React2.useMemo(
|
|
228
244
|
() => entries.map(
|
|
229
245
|
([, values]) => values.map((v, i) => ({
|
|
230
246
|
x: PADDING.left + i / (count - 1 || 1) * chartW,
|
|
@@ -234,9 +250,9 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onM
|
|
|
234
250
|
),
|
|
235
251
|
[entries, count, chartW, chartH, maxVal]
|
|
236
252
|
);
|
|
237
|
-
const clipRef =
|
|
253
|
+
const clipRef = React2.useRef(null);
|
|
238
254
|
const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
|
|
239
|
-
|
|
255
|
+
React2.useEffect(() => {
|
|
240
256
|
if (!animate || !clipRef.current) return;
|
|
241
257
|
clipRef.current.setAttribute("width", "0");
|
|
242
258
|
requestAnimationFrame(() => {
|
|
@@ -281,9 +297,9 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onM
|
|
|
281
297
|
onLeave();
|
|
282
298
|
},
|
|
283
299
|
children: [
|
|
284
|
-
animate && /* @__PURE__ */
|
|
285
|
-
/* @__PURE__ */
|
|
286
|
-
/* @__PURE__ */
|
|
300
|
+
animate && /* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsx2("clipPath", { id: lineClipId, children: /* @__PURE__ */ jsx2("rect", { ref: clipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
|
|
301
|
+
/* @__PURE__ */ jsx2(GridLines, { width, height, chartH, maxVal }),
|
|
302
|
+
/* @__PURE__ */ jsx2(AxisLabels, { labels, count, chartW, height }),
|
|
287
303
|
entries.map(([key], di) => {
|
|
288
304
|
const palette = getPalette(LINE_BAR_PALETTES, di, key);
|
|
289
305
|
const color = palette[2];
|
|
@@ -293,15 +309,15 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onM
|
|
|
293
309
|
const polyPoints = points.map((p) => `${p.x},${p.y}`).join(" ");
|
|
294
310
|
const areaD = `M ${points[0].x},${points[0].y} ${points.map((p) => `L ${p.x},${p.y}`).join(" ")} L ${points[points.length - 1].x},${PADDING.top + chartH} L ${points[0].x},${PADDING.top + chartH} Z`;
|
|
295
311
|
return /* @__PURE__ */ jsxs("g", { children: [
|
|
296
|
-
/* @__PURE__ */
|
|
297
|
-
/* @__PURE__ */
|
|
298
|
-
/* @__PURE__ */
|
|
312
|
+
/* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
313
|
+
/* @__PURE__ */ jsx2("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
|
|
314
|
+
/* @__PURE__ */ jsx2("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
|
|
299
315
|
] }) }),
|
|
300
316
|
/* @__PURE__ */ jsxs("g", { clipPath: animate ? `url(#${lineClipId})` : void 0, children: [
|
|
301
|
-
/* @__PURE__ */
|
|
302
|
-
/* @__PURE__ */
|
|
317
|
+
/* @__PURE__ */ jsx2("path", { d: areaD, fill: `url(#${gradientId})` }),
|
|
318
|
+
/* @__PURE__ */ jsx2("polyline", { points: polyPoints, fill: "none", stroke: color, strokeWidth: "2" })
|
|
303
319
|
] }),
|
|
304
|
-
activeIndex !== null && points[activeIndex] && /* @__PURE__ */
|
|
320
|
+
activeIndex !== null && points[activeIndex] && /* @__PURE__ */ jsx2(
|
|
305
321
|
"circle",
|
|
306
322
|
{
|
|
307
323
|
cx: points[activeIndex].x,
|
|
@@ -313,7 +329,7 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onM
|
|
|
313
329
|
)
|
|
314
330
|
] }, di);
|
|
315
331
|
}),
|
|
316
|
-
activeX !== null && /* @__PURE__ */
|
|
332
|
+
activeX !== null && /* @__PURE__ */ jsx2(
|
|
317
333
|
"line",
|
|
318
334
|
{
|
|
319
335
|
x1: activeX,
|
|
@@ -323,7 +339,7 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onM
|
|
|
323
339
|
className: "chart-crosshair"
|
|
324
340
|
}
|
|
325
341
|
),
|
|
326
|
-
/* @__PURE__ */
|
|
342
|
+
/* @__PURE__ */ jsx2(
|
|
327
343
|
"rect",
|
|
328
344
|
{
|
|
329
345
|
x: PADDING.left,
|
|
@@ -339,16 +355,16 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onM
|
|
|
339
355
|
);
|
|
340
356
|
});
|
|
341
357
|
LineChart.displayName = "LineChart";
|
|
342
|
-
var CurveChart =
|
|
343
|
-
const entries =
|
|
344
|
-
const maxVal =
|
|
358
|
+
var CurveChart = React2.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
359
|
+
const entries = React2.useMemo(() => Object.entries(data), [data]);
|
|
360
|
+
const maxVal = React2.useMemo(() => {
|
|
345
361
|
const allValues = entries.flatMap(([, v]) => v);
|
|
346
362
|
return Math.max(...allValues) * 1.2 || 1;
|
|
347
363
|
}, [entries]);
|
|
348
364
|
const count = labels.length;
|
|
349
365
|
const chartW = width - PADDING.left - PADDING.right;
|
|
350
366
|
const chartH = height - PADDING.top - PADDING.bottom;
|
|
351
|
-
const seriesPoints =
|
|
367
|
+
const seriesPoints = React2.useMemo(
|
|
352
368
|
() => entries.map(
|
|
353
369
|
([, values]) => values.map((v, i) => ({
|
|
354
370
|
x: PADDING.left + i / (count - 1 || 1) * chartW,
|
|
@@ -358,9 +374,9 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
|
|
|
358
374
|
),
|
|
359
375
|
[entries, count, chartW, chartH, maxVal]
|
|
360
376
|
);
|
|
361
|
-
const curveClipRef =
|
|
377
|
+
const curveClipRef = React2.useRef(null);
|
|
362
378
|
const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
|
|
363
|
-
|
|
379
|
+
React2.useEffect(() => {
|
|
364
380
|
if (!animate || !curveClipRef.current) return;
|
|
365
381
|
curveClipRef.current.setAttribute("width", "0");
|
|
366
382
|
requestAnimationFrame(() => {
|
|
@@ -405,9 +421,9 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
|
|
|
405
421
|
onLeave();
|
|
406
422
|
},
|
|
407
423
|
children: [
|
|
408
|
-
animate && /* @__PURE__ */
|
|
409
|
-
/* @__PURE__ */
|
|
410
|
-
/* @__PURE__ */
|
|
424
|
+
animate && /* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsx2("clipPath", { id: curveClipId, children: /* @__PURE__ */ jsx2("rect", { ref: curveClipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
|
|
425
|
+
/* @__PURE__ */ jsx2(GridLines, { width, height, chartH, maxVal }),
|
|
426
|
+
/* @__PURE__ */ jsx2(AxisLabels, { labels, count, chartW, height }),
|
|
411
427
|
entries.map(([key], di) => {
|
|
412
428
|
const palette = getPalette(LINE_BAR_PALETTES, di, key);
|
|
413
429
|
const color = palette[2];
|
|
@@ -417,15 +433,15 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
|
|
|
417
433
|
const linePath = toSmoothPath(points);
|
|
418
434
|
const areaPath = linePath + ` L ${points[points.length - 1].x} ${PADDING.top + chartH} L ${points[0].x} ${PADDING.top + chartH} Z`;
|
|
419
435
|
return /* @__PURE__ */ jsxs("g", { children: [
|
|
420
|
-
/* @__PURE__ */
|
|
421
|
-
/* @__PURE__ */
|
|
422
|
-
/* @__PURE__ */
|
|
436
|
+
/* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
437
|
+
/* @__PURE__ */ jsx2("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
|
|
438
|
+
/* @__PURE__ */ jsx2("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
|
|
423
439
|
] }) }),
|
|
424
440
|
/* @__PURE__ */ jsxs("g", { clipPath: animate ? `url(#${curveClipId})` : void 0, children: [
|
|
425
|
-
/* @__PURE__ */
|
|
426
|
-
/* @__PURE__ */
|
|
441
|
+
/* @__PURE__ */ jsx2("path", { d: areaPath, fill: `url(#${gradientId})` }),
|
|
442
|
+
/* @__PURE__ */ jsx2("path", { d: linePath, fill: "none", stroke: color, strokeWidth: "2" })
|
|
427
443
|
] }),
|
|
428
|
-
activeIndex !== null && points[activeIndex] && /* @__PURE__ */
|
|
444
|
+
activeIndex !== null && points[activeIndex] && /* @__PURE__ */ jsx2(
|
|
429
445
|
"circle",
|
|
430
446
|
{
|
|
431
447
|
cx: points[activeIndex].x,
|
|
@@ -437,7 +453,7 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
|
|
|
437
453
|
)
|
|
438
454
|
] }, di);
|
|
439
455
|
}),
|
|
440
|
-
activeX !== null && /* @__PURE__ */
|
|
456
|
+
activeX !== null && /* @__PURE__ */ jsx2(
|
|
441
457
|
"line",
|
|
442
458
|
{
|
|
443
459
|
x1: activeX,
|
|
@@ -447,7 +463,7 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
|
|
|
447
463
|
className: "chart-crosshair"
|
|
448
464
|
}
|
|
449
465
|
),
|
|
450
|
-
/* @__PURE__ */
|
|
466
|
+
/* @__PURE__ */ jsx2(
|
|
451
467
|
"rect",
|
|
452
468
|
{
|
|
453
469
|
x: PADDING.left,
|
|
@@ -463,9 +479,9 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
|
|
|
463
479
|
);
|
|
464
480
|
});
|
|
465
481
|
CurveChart.displayName = "CurveChart";
|
|
466
|
-
var BarChart =
|
|
467
|
-
const entries =
|
|
468
|
-
const maxVal =
|
|
482
|
+
var BarChart = React2.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
483
|
+
const entries = React2.useMemo(() => Object.entries(data), [data]);
|
|
484
|
+
const maxVal = React2.useMemo(() => {
|
|
469
485
|
const allValues = entries.flatMap(([, v]) => v);
|
|
470
486
|
return Math.max(...allValues) * 1.2 || 1;
|
|
471
487
|
}, [entries]);
|
|
@@ -477,7 +493,7 @@ var BarChart = React.memo(({ data, labels, width, height, animate, onHover, onMo
|
|
|
477
493
|
const barGap = groupCount > 1 ? 2 : 0;
|
|
478
494
|
const barW = Math.max(1, Math.min(32, (groupW * 0.7 - barGap * (groupCount - 1)) / groupCount));
|
|
479
495
|
const baseline = PADDING.top + chartH;
|
|
480
|
-
const bars =
|
|
496
|
+
const bars = React2.useMemo(
|
|
481
497
|
() => entries.map(
|
|
482
498
|
([, values], di) => values.map((v, i) => {
|
|
483
499
|
const totalBarsW = barW * groupCount + barGap * (groupCount - 1);
|
|
@@ -491,10 +507,10 @@ var BarChart = React.memo(({ data, labels, width, height, animate, onHover, onMo
|
|
|
491
507
|
);
|
|
492
508
|
const barLabelStep = getLabelStep(count, chartW);
|
|
493
509
|
return /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
|
|
494
|
-
/* @__PURE__ */
|
|
510
|
+
/* @__PURE__ */ jsx2(GridLines, { width, height, chartH, maxVal }),
|
|
495
511
|
labels.map((label, i) => {
|
|
496
512
|
if (i % barLabelStep !== 0) return null;
|
|
497
|
-
return /* @__PURE__ */
|
|
513
|
+
return /* @__PURE__ */ jsx2("text", { x: PADDING.left + groupW * i + groupW / 2, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
|
|
498
514
|
}),
|
|
499
515
|
entries.map(([key], di) => {
|
|
500
516
|
const palette = getPalette(LINE_BAR_PALETTES, di, key);
|
|
@@ -503,7 +519,7 @@ var BarChart = React.memo(({ data, labels, width, height, animate, onHover, onMo
|
|
|
503
519
|
const r = Math.min(4, b.w / 2);
|
|
504
520
|
const d = b.h <= r ? `M ${b.x} ${b.y + b.h} V ${b.y} H ${b.x + b.w} V ${b.y + b.h} Z` : `M ${b.x} ${b.y + b.h} V ${b.y + r} Q ${b.x} ${b.y} ${b.x + r} ${b.y} H ${b.x + b.w - r} Q ${b.x + b.w} ${b.y} ${b.x + b.w} ${b.y + r} V ${b.y + b.h} Z`;
|
|
505
521
|
const delay = 100 + i * 80;
|
|
506
|
-
return /* @__PURE__ */
|
|
522
|
+
return /* @__PURE__ */ jsx2(
|
|
507
523
|
"path",
|
|
508
524
|
{
|
|
509
525
|
d,
|
|
@@ -524,11 +540,11 @@ var BarChart = React.memo(({ data, labels, width, height, animate, onHover, onMo
|
|
|
524
540
|
] });
|
|
525
541
|
});
|
|
526
542
|
BarChart.displayName = "BarChart";
|
|
527
|
-
var PieDonutChart =
|
|
543
|
+
var PieDonutChart = React2.memo(
|
|
528
544
|
({ data, labels, width, height, animate, isDoughnut, onHover, onMove, onLeave }) => {
|
|
529
|
-
const entries =
|
|
530
|
-
const values =
|
|
531
|
-
const total =
|
|
545
|
+
const entries = React2.useMemo(() => Object.entries(data), [data]);
|
|
546
|
+
const values = React2.useMemo(() => entries.flatMap(([, v]) => v), [entries]);
|
|
547
|
+
const total = React2.useMemo(() => values.reduce((a, b) => a + b, 0) || 1, [values]);
|
|
532
548
|
const size = Math.min(width, height);
|
|
533
549
|
const cx = size / 2;
|
|
534
550
|
const cy = size / 2;
|
|
@@ -536,10 +552,10 @@ var PieDonutChart = React.memo(
|
|
|
536
552
|
const innerR = isDoughnut ? r * 0.5 : 0;
|
|
537
553
|
const firstKey = entries[0]?.[0] ?? "";
|
|
538
554
|
const colorOffset = hashString(firstKey);
|
|
539
|
-
const maskRef =
|
|
555
|
+
const maskRef = React2.useRef(null);
|
|
540
556
|
const maskR = r + 10;
|
|
541
557
|
const maskCircumference = 2 * Math.PI * maskR;
|
|
542
|
-
|
|
558
|
+
React2.useEffect(() => {
|
|
543
559
|
if (!animate || !maskRef.current) return;
|
|
544
560
|
const el = maskRef.current;
|
|
545
561
|
el.style.strokeDasharray = `${maskCircumference}`;
|
|
@@ -549,7 +565,7 @@ var PieDonutChart = React.memo(
|
|
|
549
565
|
el.style.strokeDashoffset = "0";
|
|
550
566
|
});
|
|
551
567
|
}, [animate, maskCircumference]);
|
|
552
|
-
const sliceData =
|
|
568
|
+
const sliceData = React2.useMemo(() => {
|
|
553
569
|
let angle0 = -Math.PI / 2;
|
|
554
570
|
let cumulativeAngle = 0;
|
|
555
571
|
return values.map((v, i) => {
|
|
@@ -584,7 +600,7 @@ var PieDonutChart = React.memo(
|
|
|
584
600
|
}, [values, total, cx, cy, r, innerR, labels]);
|
|
585
601
|
const maskId = `pie-mask-${isDoughnut ? "d" : "p"}`;
|
|
586
602
|
return /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: [
|
|
587
|
-
animate && /* @__PURE__ */
|
|
603
|
+
animate && /* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsx2("mask", { id: maskId, children: /* @__PURE__ */ jsx2(
|
|
588
604
|
"circle",
|
|
589
605
|
{
|
|
590
606
|
ref: maskRef,
|
|
@@ -597,7 +613,7 @@ var PieDonutChart = React.memo(
|
|
|
597
613
|
transform: `rotate(-90 ${cx} ${cy})`
|
|
598
614
|
}
|
|
599
615
|
) }) }),
|
|
600
|
-
/* @__PURE__ */
|
|
616
|
+
/* @__PURE__ */ jsx2("g", { mask: animate ? `url(#${maskId})` : void 0, children: sliceData.map((s, i) => /* @__PURE__ */ jsx2("g", { children: /* @__PURE__ */ jsx2(
|
|
601
617
|
"path",
|
|
602
618
|
{
|
|
603
619
|
d: s.d,
|
|
@@ -608,7 +624,7 @@ var PieDonutChart = React.memo(
|
|
|
608
624
|
onMouseLeave: onLeave
|
|
609
625
|
}
|
|
610
626
|
) }, i)) }),
|
|
611
|
-
sliceData.map((s, i) => s.angle > 0.2 && /* @__PURE__ */
|
|
627
|
+
sliceData.map((s, i) => s.angle > 0.2 && /* @__PURE__ */ jsx2(
|
|
612
628
|
"text",
|
|
613
629
|
{
|
|
614
630
|
x: s.lx,
|
|
@@ -626,9 +642,9 @@ var PieDonutChart = React.memo(
|
|
|
626
642
|
);
|
|
627
643
|
PieDonutChart.displayName = "PieDonutChart";
|
|
628
644
|
var ChartTooltipPortal = ({ clientX, clientY, visible, children }) => {
|
|
629
|
-
const ref =
|
|
630
|
-
const [pos, setPos] =
|
|
631
|
-
|
|
645
|
+
const ref = React2.useRef(null);
|
|
646
|
+
const [pos, setPos] = React2.useState({ left: 0, top: 0 });
|
|
647
|
+
React2.useLayoutEffect(() => {
|
|
632
648
|
const el = ref.current;
|
|
633
649
|
if (!el) return;
|
|
634
650
|
const w = el.offsetWidth;
|
|
@@ -641,7 +657,7 @@ var ChartTooltipPortal = ({ clientX, clientY, visible, children }) => {
|
|
|
641
657
|
if (left < 8) left = 8;
|
|
642
658
|
setPos({ left, top });
|
|
643
659
|
}, [clientX, clientY]);
|
|
644
|
-
return /* @__PURE__ */
|
|
660
|
+
return /* @__PURE__ */ jsx2(
|
|
645
661
|
"div",
|
|
646
662
|
{
|
|
647
663
|
ref,
|
|
@@ -658,13 +674,13 @@ var ChartLegend = ({ data, labels, type }) => {
|
|
|
658
674
|
const total = values.reduce((a, b) => a + b, 0) || 1;
|
|
659
675
|
const firstKey = entries[0]?.[0] ?? "";
|
|
660
676
|
const colorOffset = hashString(firstKey);
|
|
661
|
-
return /* @__PURE__ */
|
|
677
|
+
return /* @__PURE__ */ jsx2("div", { className: "chart-legend", children: values.map((v, i) => {
|
|
662
678
|
const pct = Math.round(v / total * 100);
|
|
663
679
|
const color = PIE_COLORS[(i + colorOffset) % PIE_COLORS.length];
|
|
664
680
|
return /* @__PURE__ */ jsxs("div", { className: "chart-legend-item", children: [
|
|
665
|
-
/* @__PURE__ */
|
|
681
|
+
/* @__PURE__ */ jsx2("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
|
|
666
682
|
/* @__PURE__ */ jsxs("div", { className: "chart-legend-text", children: [
|
|
667
|
-
/* @__PURE__ */
|
|
683
|
+
/* @__PURE__ */ jsx2("span", { className: "chart-legend-label", children: labels[i] || `${i + 1}` }),
|
|
668
684
|
/* @__PURE__ */ jsxs("span", { className: "chart-legend-value", children: [
|
|
669
685
|
v.toLocaleString(),
|
|
670
686
|
"(",
|
|
@@ -675,37 +691,37 @@ var ChartLegend = ({ data, labels, type }) => {
|
|
|
675
691
|
] }, i);
|
|
676
692
|
}) });
|
|
677
693
|
}
|
|
678
|
-
return /* @__PURE__ */
|
|
694
|
+
return /* @__PURE__ */ jsx2("div", { className: "chart-legend", children: entries.map(([key], di) => {
|
|
679
695
|
const palette = getPalette(LINE_BAR_PALETTES, di, key);
|
|
680
696
|
const color = palette[2];
|
|
681
697
|
const values = entries[di][1];
|
|
682
698
|
const sum = values.reduce((a, b) => a + b, 0);
|
|
683
699
|
return /* @__PURE__ */ jsxs("div", { className: "chart-legend-item", children: [
|
|
684
|
-
/* @__PURE__ */
|
|
700
|
+
/* @__PURE__ */ jsx2("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
|
|
685
701
|
/* @__PURE__ */ jsxs("div", { className: "chart-legend-text", children: [
|
|
686
|
-
/* @__PURE__ */
|
|
687
|
-
/* @__PURE__ */
|
|
702
|
+
/* @__PURE__ */ jsx2("span", { className: "chart-legend-label", children: key }),
|
|
703
|
+
/* @__PURE__ */ jsx2("span", { className: "chart-legend-value", children: sum.toLocaleString() })
|
|
688
704
|
] })
|
|
689
705
|
] }, di);
|
|
690
706
|
}) });
|
|
691
707
|
};
|
|
692
|
-
var Chart =
|
|
708
|
+
var Chart = React2.memo((props) => {
|
|
693
709
|
const { type, data, labels, tooltip: showTooltip = true } = props;
|
|
694
710
|
const { tooltip, show, hide, move, containerRef } = useChartTooltip(showTooltip);
|
|
695
711
|
const { width, height } = useChartSize(containerRef);
|
|
696
|
-
const stableData =
|
|
697
|
-
const stableLabels =
|
|
698
|
-
const dataKey =
|
|
712
|
+
const stableData = React2.useMemo(() => data, [JSON.stringify(data)]);
|
|
713
|
+
const stableLabels = React2.useMemo(() => labels, [JSON.stringify(labels)]);
|
|
714
|
+
const dataKey = React2.useMemo(() => JSON.stringify(labels), [labels]);
|
|
699
715
|
const animate = useChartAnimation(containerRef, dataKey);
|
|
700
716
|
const ready = width > 0 && height > 0;
|
|
701
717
|
return /* @__PURE__ */ jsxs("div", { className: "lib-xplat-chart", ref: containerRef, children: [
|
|
702
|
-
ready && type === "line" && /* @__PURE__ */
|
|
703
|
-
ready && type === "curve" && /* @__PURE__ */
|
|
704
|
-
ready && type === "bar" && /* @__PURE__ */
|
|
705
|
-
ready && type === "pie" && /* @__PURE__ */
|
|
706
|
-
ready && type === "doughnut" && /* @__PURE__ */
|
|
707
|
-
ready && (type === "bar" || type === "pie" || type === "doughnut") && /* @__PURE__ */
|
|
708
|
-
tooltip.content && /* @__PURE__ */
|
|
718
|
+
ready && type === "line" && /* @__PURE__ */ jsx2(LineChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
719
|
+
ready && type === "curve" && /* @__PURE__ */ jsx2(CurveChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
720
|
+
ready && type === "bar" && /* @__PURE__ */ jsx2(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
721
|
+
ready && type === "pie" && /* @__PURE__ */ jsx2(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
722
|
+
ready && type === "doughnut" && /* @__PURE__ */ jsx2(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
|
|
723
|
+
ready && (type === "bar" || type === "pie" || type === "doughnut") && /* @__PURE__ */ jsx2(ChartLegend, { data: stableData, labels: stableLabels, type }),
|
|
724
|
+
tooltip.content && /* @__PURE__ */ jsx2(Portal_default, { children: /* @__PURE__ */ jsx2(ChartTooltipPortal, { clientX: tooltip.clientX, clientY: tooltip.clientY, visible: tooltip.visible, children: tooltip.content }) })
|
|
709
725
|
] });
|
|
710
726
|
});
|
|
711
727
|
Chart.displayName = "Chart";
|