@x-plat/design-system 0.5.32 → 0.5.34
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 +148 -144
- package/dist/components/Chart/index.css +54 -10
- package/dist/components/Chart/index.js +148 -144
- package/dist/components/index.cjs +148 -144
- package/dist/components/index.css +54 -10
- package/dist/components/index.js +148 -144
- package/dist/index.cjs +148 -144
- package/dist/index.css +54 -10
- package/dist/index.js +148 -144
- package/package.json +1 -1
|
@@ -2346,45 +2346,35 @@ var useChartAnimation = (containerRef, dataKey) => {
|
|
|
2346
2346
|
prevDataKey.current = dataKey;
|
|
2347
2347
|
if (prefersReducedMotion()) return;
|
|
2348
2348
|
setAnimate(false);
|
|
2349
|
-
requestAnimationFrame(() =>
|
|
2349
|
+
requestAnimationFrame(() => {
|
|
2350
|
+
requestAnimationFrame(() => setAnimate(true));
|
|
2351
|
+
});
|
|
2350
2352
|
}
|
|
2351
2353
|
}, [dataKey]);
|
|
2352
2354
|
return animate || prefersReducedMotion();
|
|
2353
2355
|
};
|
|
2356
|
+
var TOOLTIP_OFFSET = 12;
|
|
2354
2357
|
var useChartTooltip = (enabled) => {
|
|
2355
2358
|
const [tooltip, setTooltip] = import_react6.default.useState({
|
|
2356
2359
|
visible: false,
|
|
2357
|
-
|
|
2358
|
-
|
|
2360
|
+
clientX: 0,
|
|
2361
|
+
clientY: 0,
|
|
2359
2362
|
content: ""
|
|
2360
2363
|
});
|
|
2361
2364
|
const containerRef = import_react6.default.useRef(null);
|
|
2362
2365
|
const rafRef = import_react6.default.useRef(0);
|
|
2363
2366
|
const move = import_react6.default.useCallback((e) => {
|
|
2364
2367
|
if (!enabled) return;
|
|
2365
|
-
const
|
|
2366
|
-
const
|
|
2368
|
+
const cx = e.clientX;
|
|
2369
|
+
const cy = e.clientY;
|
|
2367
2370
|
cancelAnimationFrame(rafRef.current);
|
|
2368
2371
|
rafRef.current = requestAnimationFrame(() => {
|
|
2369
|
-
|
|
2370
|
-
if (!rect) return;
|
|
2371
|
-
setTooltip((prev) => ({
|
|
2372
|
-
...prev,
|
|
2373
|
-
x: clientX - rect.left,
|
|
2374
|
-
y: clientY - rect.top - 12
|
|
2375
|
-
}));
|
|
2372
|
+
setTooltip((prev) => ({ ...prev, clientX: cx, clientY: cy }));
|
|
2376
2373
|
});
|
|
2377
2374
|
}, [enabled]);
|
|
2378
2375
|
const show = import_react6.default.useCallback((e, content) => {
|
|
2379
2376
|
if (!enabled) return;
|
|
2380
|
-
|
|
2381
|
-
if (!rect) return;
|
|
2382
|
-
setTooltip({
|
|
2383
|
-
visible: true,
|
|
2384
|
-
x: e.clientX - rect.left,
|
|
2385
|
-
y: e.clientY - rect.top - 12,
|
|
2386
|
-
content
|
|
2387
|
-
});
|
|
2377
|
+
setTooltip({ visible: true, clientX: e.clientX, clientY: e.clientY, content });
|
|
2388
2378
|
}, [enabled]);
|
|
2389
2379
|
const hide = import_react6.default.useCallback(() => {
|
|
2390
2380
|
cancelAnimationFrame(rafRef.current);
|
|
@@ -2418,14 +2408,14 @@ var AxisLabels = import_react6.default.memo(({ labels, count, chartW, height })
|
|
|
2418
2408
|
AxisLabels.displayName = "AxisLabels";
|
|
2419
2409
|
var useCrosshair = (seriesPoints, entries, labels, chartH) => {
|
|
2420
2410
|
const [activeIndex, setActiveIndex] = import_react6.default.useState(null);
|
|
2421
|
-
const [mouseX, setMouseX] = import_react6.default.useState(null);
|
|
2422
2411
|
const handleMouseMove = import_react6.default.useCallback((e) => {
|
|
2423
2412
|
const svg = e.currentTarget;
|
|
2424
2413
|
const rect = svg.getBoundingClientRect();
|
|
2425
2414
|
const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
|
|
2426
|
-
setMouseX(mx);
|
|
2427
2415
|
if (seriesPoints.length === 0 || seriesPoints[0].length === 0) return;
|
|
2428
2416
|
const points = seriesPoints[0];
|
|
2417
|
+
const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
|
|
2418
|
+
const threshold = step / 2;
|
|
2429
2419
|
let closest = 0;
|
|
2430
2420
|
let minDist = Math.abs(points[0].x - mx);
|
|
2431
2421
|
for (let i = 1; i < points.length; i++) {
|
|
@@ -2435,11 +2425,10 @@ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
|
|
|
2435
2425
|
closest = i;
|
|
2436
2426
|
}
|
|
2437
2427
|
}
|
|
2438
|
-
setActiveIndex(closest);
|
|
2428
|
+
setActiveIndex(minDist <= threshold ? closest : null);
|
|
2439
2429
|
}, [seriesPoints]);
|
|
2440
2430
|
const handleMouseLeave = import_react6.default.useCallback(() => {
|
|
2441
2431
|
setActiveIndex(null);
|
|
2442
|
-
setMouseX(null);
|
|
2443
2432
|
}, []);
|
|
2444
2433
|
const tooltipContent = import_react6.default.useMemo(() => {
|
|
2445
2434
|
if (activeIndex === null) return "";
|
|
@@ -2448,7 +2437,13 @@ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
|
|
|
2448
2437
|
return p ? `${key}: ${p.v}` : "";
|
|
2449
2438
|
}).filter(Boolean).join(" / ");
|
|
2450
2439
|
}, [activeIndex, entries, seriesPoints]);
|
|
2451
|
-
|
|
2440
|
+
const getTooltipAt = import_react6.default.useCallback((idx) => {
|
|
2441
|
+
return entries.map(([key], di) => {
|
|
2442
|
+
const p = seriesPoints[di]?.[idx];
|
|
2443
|
+
return p ? `${key}: ${p.v}` : "";
|
|
2444
|
+
}).filter(Boolean).join(" / ");
|
|
2445
|
+
}, [entries, seriesPoints]);
|
|
2446
|
+
return { activeIndex, handleMouseMove, handleMouseLeave, tooltipContent, getTooltipAt };
|
|
2452
2447
|
};
|
|
2453
2448
|
var LineChart = import_react6.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
2454
2449
|
const entries = import_react6.default.useMemo(() => Object.entries(data), [data]);
|
|
@@ -2469,33 +2464,19 @@ var LineChart = import_react6.default.memo(({ data, labels, width, height, anima
|
|
|
2469
2464
|
),
|
|
2470
2465
|
[entries, count, chartW, chartH, maxVal]
|
|
2471
2466
|
);
|
|
2472
|
-
const lineRefs = import_react6.default.useRef([]);
|
|
2473
2467
|
const clipRef = import_react6.default.useRef(null);
|
|
2474
|
-
const { activeIndex,
|
|
2468
|
+
const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
|
|
2475
2469
|
import_react6.default.useEffect(() => {
|
|
2476
|
-
if (!animate) return;
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
el.style.transition = "stroke-dashoffset 1200ms ease-out 200ms";
|
|
2484
|
-
el.style.strokeDashoffset = "0";
|
|
2485
|
-
});
|
|
2470
|
+
if (!animate || !clipRef.current) return;
|
|
2471
|
+
clipRef.current.setAttribute("width", "0");
|
|
2472
|
+
requestAnimationFrame(() => {
|
|
2473
|
+
if (clipRef.current) {
|
|
2474
|
+
clipRef.current.style.transition = "width 1200ms ease-out 200ms";
|
|
2475
|
+
clipRef.current.setAttribute("width", `${width}`);
|
|
2476
|
+
}
|
|
2486
2477
|
});
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
requestAnimationFrame(() => {
|
|
2490
|
-
if (clipRef.current) {
|
|
2491
|
-
clipRef.current.style.transition = "width 1200ms ease-out 200ms";
|
|
2492
|
-
clipRef.current.setAttribute("width", `${width}`);
|
|
2493
|
-
}
|
|
2494
|
-
});
|
|
2495
|
-
}
|
|
2496
|
-
}, [animate, seriesPoints, width]);
|
|
2497
|
-
const guideX = mouseX != null && mouseX >= PADDING.left && mouseX <= width - PADDING.right ? mouseX : null;
|
|
2498
|
-
const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x : null;
|
|
2478
|
+
}, [animate, width]);
|
|
2479
|
+
const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x ?? null : null;
|
|
2499
2480
|
const lineClipId = "line-area-clip";
|
|
2500
2481
|
return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)(
|
|
2501
2482
|
"svg",
|
|
@@ -2504,7 +2485,26 @@ var LineChart = import_react6.default.memo(({ data, labels, width, height, anima
|
|
|
2504
2485
|
className: "chart-svg",
|
|
2505
2486
|
onMouseMove: (e) => {
|
|
2506
2487
|
handleMouseMove(e);
|
|
2507
|
-
|
|
2488
|
+
const svg = e.currentTarget;
|
|
2489
|
+
const rect = svg.getBoundingClientRect();
|
|
2490
|
+
const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
|
|
2491
|
+
const points = seriesPoints[0];
|
|
2492
|
+
if (!points || points.length === 0) return;
|
|
2493
|
+
const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
|
|
2494
|
+
let closest = 0;
|
|
2495
|
+
let minDist = Math.abs(points[0].x - mx);
|
|
2496
|
+
for (let i = 1; i < points.length; i++) {
|
|
2497
|
+
const dist = Math.abs(points[i].x - mx);
|
|
2498
|
+
if (dist < minDist) {
|
|
2499
|
+
minDist = dist;
|
|
2500
|
+
closest = i;
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
if (minDist <= step / 2) {
|
|
2504
|
+
onHover(e, `${labels[closest]} \u2014 ${getTooltipAt(closest)}`);
|
|
2505
|
+
} else {
|
|
2506
|
+
onLeave();
|
|
2507
|
+
}
|
|
2508
2508
|
},
|
|
2509
2509
|
onMouseLeave: () => {
|
|
2510
2510
|
handleMouseLeave();
|
|
@@ -2527,26 +2527,10 @@ var LineChart = import_react6.default.memo(({ data, labels, width, height, anima
|
|
|
2527
2527
|
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
|
|
2528
2528
|
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
|
|
2529
2529
|
] }) }),
|
|
2530
|
-
/* @__PURE__ */ (0, import_jsx_runtime307.
|
|
2531
|
-
"path",
|
|
2532
|
-
{
|
|
2533
|
-
|
|
2534
|
-
fill: `url(#${gradientId})`,
|
|
2535
|
-
clipPath: animate ? `url(#${lineClipId})` : void 0
|
|
2536
|
-
}
|
|
2537
|
-
),
|
|
2538
|
-
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
|
|
2539
|
-
"polyline",
|
|
2540
|
-
{
|
|
2541
|
-
ref: (el) => {
|
|
2542
|
-
lineRefs.current[di] = el;
|
|
2543
|
-
},
|
|
2544
|
-
points: polyPoints,
|
|
2545
|
-
fill: "none",
|
|
2546
|
-
stroke: color,
|
|
2547
|
-
strokeWidth: "2"
|
|
2548
|
-
}
|
|
2549
|
-
),
|
|
2530
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("g", { clipPath: animate ? `url(#${lineClipId})` : void 0, children: [
|
|
2531
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("path", { d: areaD, fill: `url(#${gradientId})` }),
|
|
2532
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("polyline", { points: polyPoints, fill: "none", stroke: color, strokeWidth: "2" })
|
|
2533
|
+
] }),
|
|
2550
2534
|
activeIndex !== null && points[activeIndex] && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
|
|
2551
2535
|
"circle",
|
|
2552
2536
|
{
|
|
@@ -2559,21 +2543,16 @@ var LineChart = import_react6.default.memo(({ data, labels, width, height, anima
|
|
|
2559
2543
|
)
|
|
2560
2544
|
] }, di);
|
|
2561
2545
|
}),
|
|
2562
|
-
|
|
2546
|
+
activeX !== null && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
|
|
2563
2547
|
"line",
|
|
2564
2548
|
{
|
|
2565
|
-
x1:
|
|
2549
|
+
x1: activeX,
|
|
2566
2550
|
y1: PADDING.top,
|
|
2567
|
-
x2:
|
|
2551
|
+
x2: activeX,
|
|
2568
2552
|
y2: PADDING.top + chartH,
|
|
2569
2553
|
className: "chart-crosshair"
|
|
2570
2554
|
}
|
|
2571
2555
|
),
|
|
2572
|
-
activeIndex !== null && activeX !== null && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("foreignObject", { x: activeX - 100, y: 0, width: "200", height: PADDING.top, children: /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("div", { className: "chart-crosshair-label", children: [
|
|
2573
|
-
labels[activeIndex],
|
|
2574
|
-
" \u2014 ",
|
|
2575
|
-
tooltipContent
|
|
2576
|
-
] }) }),
|
|
2577
2556
|
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
|
|
2578
2557
|
"rect",
|
|
2579
2558
|
{
|
|
@@ -2609,33 +2588,19 @@ var CurveChart = import_react6.default.memo(({ data, labels, width, height, anim
|
|
|
2609
2588
|
),
|
|
2610
2589
|
[entries, count, chartW, chartH, maxVal]
|
|
2611
2590
|
);
|
|
2612
|
-
const lineRefs = import_react6.default.useRef([]);
|
|
2613
2591
|
const curveClipRef = import_react6.default.useRef(null);
|
|
2614
|
-
const { activeIndex,
|
|
2592
|
+
const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
|
|
2615
2593
|
import_react6.default.useEffect(() => {
|
|
2616
|
-
if (!animate) return;
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
el.style.transition = "stroke-dashoffset 1200ms ease-out 200ms";
|
|
2624
|
-
el.style.strokeDashoffset = "0";
|
|
2625
|
-
});
|
|
2594
|
+
if (!animate || !curveClipRef.current) return;
|
|
2595
|
+
curveClipRef.current.setAttribute("width", "0");
|
|
2596
|
+
requestAnimationFrame(() => {
|
|
2597
|
+
if (curveClipRef.current) {
|
|
2598
|
+
curveClipRef.current.style.transition = "width 1200ms ease-out 200ms";
|
|
2599
|
+
curveClipRef.current.setAttribute("width", `${width}`);
|
|
2600
|
+
}
|
|
2626
2601
|
});
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
requestAnimationFrame(() => {
|
|
2630
|
-
if (curveClipRef.current) {
|
|
2631
|
-
curveClipRef.current.style.transition = "width 1200ms ease-out 200ms";
|
|
2632
|
-
curveClipRef.current.setAttribute("width", `${width}`);
|
|
2633
|
-
}
|
|
2634
|
-
});
|
|
2635
|
-
}
|
|
2636
|
-
}, [animate, seriesPoints, width]);
|
|
2637
|
-
const guideX = mouseX != null && mouseX >= PADDING.left && mouseX <= width - PADDING.right ? mouseX : null;
|
|
2638
|
-
const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x : null;
|
|
2602
|
+
}, [animate, width]);
|
|
2603
|
+
const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x ?? null : null;
|
|
2639
2604
|
const curveClipId = "curve-area-clip";
|
|
2640
2605
|
return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)(
|
|
2641
2606
|
"svg",
|
|
@@ -2644,7 +2609,26 @@ var CurveChart = import_react6.default.memo(({ data, labels, width, height, anim
|
|
|
2644
2609
|
className: "chart-svg",
|
|
2645
2610
|
onMouseMove: (e) => {
|
|
2646
2611
|
handleMouseMove(e);
|
|
2647
|
-
|
|
2612
|
+
const svg = e.currentTarget;
|
|
2613
|
+
const rect = svg.getBoundingClientRect();
|
|
2614
|
+
const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
|
|
2615
|
+
const points = seriesPoints[0];
|
|
2616
|
+
if (!points || points.length === 0) return;
|
|
2617
|
+
const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
|
|
2618
|
+
let closest = 0;
|
|
2619
|
+
let minDist = Math.abs(points[0].x - mx);
|
|
2620
|
+
for (let i = 1; i < points.length; i++) {
|
|
2621
|
+
const dist = Math.abs(points[i].x - mx);
|
|
2622
|
+
if (dist < minDist) {
|
|
2623
|
+
minDist = dist;
|
|
2624
|
+
closest = i;
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
if (minDist <= step / 2) {
|
|
2628
|
+
onHover(e, `${labels[closest]} \u2014 ${getTooltipAt(closest)}`);
|
|
2629
|
+
} else {
|
|
2630
|
+
onLeave();
|
|
2631
|
+
}
|
|
2648
2632
|
},
|
|
2649
2633
|
onMouseLeave: () => {
|
|
2650
2634
|
handleMouseLeave();
|
|
@@ -2667,26 +2651,10 @@ var CurveChart = import_react6.default.memo(({ data, labels, width, height, anim
|
|
|
2667
2651
|
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
|
|
2668
2652
|
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
|
|
2669
2653
|
] }) }),
|
|
2670
|
-
/* @__PURE__ */ (0, import_jsx_runtime307.
|
|
2671
|
-
"path",
|
|
2672
|
-
{
|
|
2673
|
-
|
|
2674
|
-
fill: `url(#${gradientId})`,
|
|
2675
|
-
clipPath: animate ? `url(#${curveClipId})` : void 0
|
|
2676
|
-
}
|
|
2677
|
-
),
|
|
2678
|
-
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
|
|
2679
|
-
"path",
|
|
2680
|
-
{
|
|
2681
|
-
ref: (el) => {
|
|
2682
|
-
lineRefs.current[di] = el;
|
|
2683
|
-
},
|
|
2684
|
-
d: linePath,
|
|
2685
|
-
fill: "none",
|
|
2686
|
-
stroke: color,
|
|
2687
|
-
strokeWidth: "2"
|
|
2688
|
-
}
|
|
2689
|
-
),
|
|
2654
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("g", { clipPath: animate ? `url(#${curveClipId})` : void 0, children: [
|
|
2655
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("path", { d: areaPath, fill: `url(#${gradientId})` }),
|
|
2656
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("path", { d: linePath, fill: "none", stroke: color, strokeWidth: "2" })
|
|
2657
|
+
] }),
|
|
2690
2658
|
activeIndex !== null && points[activeIndex] && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
|
|
2691
2659
|
"circle",
|
|
2692
2660
|
{
|
|
@@ -2699,21 +2667,16 @@ var CurveChart = import_react6.default.memo(({ data, labels, width, height, anim
|
|
|
2699
2667
|
)
|
|
2700
2668
|
] }, di);
|
|
2701
2669
|
}),
|
|
2702
|
-
|
|
2670
|
+
activeX !== null && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
|
|
2703
2671
|
"line",
|
|
2704
2672
|
{
|
|
2705
|
-
x1:
|
|
2673
|
+
x1: activeX,
|
|
2706
2674
|
y1: PADDING.top,
|
|
2707
|
-
x2:
|
|
2675
|
+
x2: activeX,
|
|
2708
2676
|
y2: PADDING.top + chartH,
|
|
2709
2677
|
className: "chart-crosshair"
|
|
2710
2678
|
}
|
|
2711
2679
|
),
|
|
2712
|
-
activeIndex !== null && activeX !== null && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("foreignObject", { x: activeX - 100, y: 0, width: "200", height: PADDING.top, children: /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("div", { className: "chart-crosshair-label", children: [
|
|
2713
|
-
labels[activeIndex],
|
|
2714
|
-
" \u2014 ",
|
|
2715
|
-
tooltipContent
|
|
2716
|
-
] }) }),
|
|
2717
2680
|
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
|
|
2718
2681
|
"rect",
|
|
2719
2682
|
{
|
|
@@ -2892,30 +2855,70 @@ var PieDonutChart = import_react6.default.memo(
|
|
|
2892
2855
|
}
|
|
2893
2856
|
);
|
|
2894
2857
|
PieDonutChart.displayName = "PieDonutChart";
|
|
2895
|
-
var
|
|
2858
|
+
var ChartTooltipPortal = ({ clientX, clientY, visible, children }) => {
|
|
2896
2859
|
const ref = import_react6.default.useRef(null);
|
|
2897
|
-
const [
|
|
2898
|
-
import_react6.default.
|
|
2860
|
+
const [pos, setPos] = import_react6.default.useState({ left: 0, top: 0 });
|
|
2861
|
+
import_react6.default.useLayoutEffect(() => {
|
|
2899
2862
|
const el = ref.current;
|
|
2900
2863
|
if (!el) return;
|
|
2901
2864
|
const w = el.offsetWidth;
|
|
2902
|
-
const
|
|
2903
|
-
const
|
|
2904
|
-
let
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2865
|
+
const h = el.offsetHeight;
|
|
2866
|
+
const vw = window.innerWidth;
|
|
2867
|
+
let left = clientX + TOOLTIP_OFFSET;
|
|
2868
|
+
let top = clientY - h - TOOLTIP_OFFSET;
|
|
2869
|
+
if (left + w > vw - 8) left = clientX - w - TOOLTIP_OFFSET;
|
|
2870
|
+
if (top < 8) top = clientY + TOOLTIP_OFFSET;
|
|
2871
|
+
if (left < 8) left = 8;
|
|
2872
|
+
setPos({ left, top });
|
|
2873
|
+
}, [clientX, clientY]);
|
|
2909
2874
|
return /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
|
|
2910
2875
|
"div",
|
|
2911
2876
|
{
|
|
2912
2877
|
ref,
|
|
2913
|
-
className: "chart-tooltip"
|
|
2914
|
-
style: { left:
|
|
2878
|
+
className: `chart-tooltip ${visible ? "chart-tooltip-show" : "chart-tooltip-hide"}`,
|
|
2879
|
+
style: { position: "fixed", left: pos.left, top: pos.top, zIndex: 1100 },
|
|
2915
2880
|
children
|
|
2916
2881
|
}
|
|
2917
2882
|
);
|
|
2918
2883
|
};
|
|
2884
|
+
var ChartLegend = ({ data, labels, type }) => {
|
|
2885
|
+
const entries = Object.entries(data);
|
|
2886
|
+
if (type === "pie" || type === "doughnut") {
|
|
2887
|
+
const values = entries.flatMap(([, v]) => v);
|
|
2888
|
+
const total = values.reduce((a, b) => a + b, 0) || 1;
|
|
2889
|
+
const firstKey = entries[0]?.[0] ?? "";
|
|
2890
|
+
const colorOffset = hashString(firstKey);
|
|
2891
|
+
return /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("div", { className: "chart-legend", children: values.map((v, i) => {
|
|
2892
|
+
const pct = Math.round(v / total * 100);
|
|
2893
|
+
const color = PIE_COLORS[(i + colorOffset) % PIE_COLORS.length];
|
|
2894
|
+
return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("div", { className: "chart-legend-item", children: [
|
|
2895
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
|
|
2896
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("div", { className: "chart-legend-text", children: [
|
|
2897
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("span", { className: "chart-legend-label", children: labels[i] || `${i + 1}` }),
|
|
2898
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("span", { className: "chart-legend-value", children: [
|
|
2899
|
+
v.toLocaleString(),
|
|
2900
|
+
"(",
|
|
2901
|
+
pct,
|
|
2902
|
+
"%)"
|
|
2903
|
+
] })
|
|
2904
|
+
] })
|
|
2905
|
+
] }, i);
|
|
2906
|
+
}) });
|
|
2907
|
+
}
|
|
2908
|
+
return /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("div", { className: "chart-legend", children: entries.map(([key], di) => {
|
|
2909
|
+
const palette = getPalette(LINE_BAR_PALETTES, di, key);
|
|
2910
|
+
const color = palette[2];
|
|
2911
|
+
const values = entries[di][1];
|
|
2912
|
+
const sum = values.reduce((a, b) => a + b, 0);
|
|
2913
|
+
return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("div", { className: "chart-legend-item", children: [
|
|
2914
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
|
|
2915
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("div", { className: "chart-legend-text", children: [
|
|
2916
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("span", { className: "chart-legend-label", children: key }),
|
|
2917
|
+
/* @__PURE__ */ (0, import_jsx_runtime307.jsx)("span", { className: "chart-legend-value", children: sum.toLocaleString() })
|
|
2918
|
+
] })
|
|
2919
|
+
] }, di);
|
|
2920
|
+
}) });
|
|
2921
|
+
};
|
|
2919
2922
|
var Chart = import_react6.default.memo((props) => {
|
|
2920
2923
|
const { type, data, labels, tooltip: showTooltip = true } = props;
|
|
2921
2924
|
const { tooltip, show, hide, move, containerRef } = useChartTooltip(showTooltip);
|
|
@@ -2931,7 +2934,8 @@ var Chart = import_react6.default.memo((props) => {
|
|
|
2931
2934
|
ready && type === "bar" && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
2932
2935
|
ready && type === "pie" && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
2933
2936
|
ready && type === "doughnut" && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
|
|
2934
|
-
|
|
2937
|
+
ready && (type === "bar" || type === "pie" || type === "doughnut") && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(ChartLegend, { data: stableData, labels: stableLabels, type }),
|
|
2938
|
+
tooltip.content && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(ChartTooltipPortal, { clientX: tooltip.clientX, clientY: tooltip.clientY, visible: tooltip.visible, children: tooltip.content })
|
|
2935
2939
|
] });
|
|
2936
2940
|
});
|
|
2937
2941
|
Chart.displayName = "Chart";
|
|
@@ -2000,9 +2000,9 @@
|
|
|
2000
2000
|
opacity: 1;
|
|
2001
2001
|
}
|
|
2002
2002
|
.lib-xplat-chart .chart-crosshair {
|
|
2003
|
-
stroke: var(--semantic-border-
|
|
2003
|
+
stroke: var(--semantic-border-strong);
|
|
2004
2004
|
stroke-width: 1;
|
|
2005
|
-
stroke-dasharray: 4
|
|
2005
|
+
stroke-dasharray: 4 3;
|
|
2006
2006
|
pointer-events: none;
|
|
2007
2007
|
}
|
|
2008
2008
|
.lib-xplat-chart .chart-point-active {
|
|
@@ -2018,18 +2018,24 @@
|
|
|
2018
2018
|
overflow: visible;
|
|
2019
2019
|
}
|
|
2020
2020
|
.lib-xplat-chart .chart-tooltip {
|
|
2021
|
-
|
|
2022
|
-
transform: translate(-50%, -100%);
|
|
2023
|
-
padding: var(--spacing-space-2) var(--spacing-space-3);
|
|
2021
|
+
padding: var(--spacing-space-3);
|
|
2024
2022
|
background-color: var(--semantic-surface-neutral-strong);
|
|
2025
2023
|
color: var(--semantic-text-inverse);
|
|
2026
2024
|
font-size: 12px;
|
|
2025
|
+
line-height: 18px;
|
|
2027
2026
|
font-weight: 500;
|
|
2028
2027
|
border-radius: var(--spacing-radius-md);
|
|
2029
|
-
|
|
2028
|
+
max-width: 240px;
|
|
2030
2029
|
pointer-events: none;
|
|
2031
|
-
|
|
2032
|
-
|
|
2030
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
2031
|
+
}
|
|
2032
|
+
.lib-xplat-chart .chart-tooltip.chart-tooltip-show {
|
|
2033
|
+
opacity: 1;
|
|
2034
|
+
animation: chart-tooltip-in 120ms ease-out;
|
|
2035
|
+
}
|
|
2036
|
+
.lib-xplat-chart .chart-tooltip.chart-tooltip-hide {
|
|
2037
|
+
opacity: 0;
|
|
2038
|
+
animation: chart-tooltip-out 80ms ease-in;
|
|
2033
2039
|
}
|
|
2034
2040
|
.lib-xplat-chart .chart-bar-animate {
|
|
2035
2041
|
animation: chart-bar-grow 800ms ease-out both;
|
|
@@ -2041,6 +2047,38 @@
|
|
|
2041
2047
|
.lib-xplat-chart .chart-area[style*=animationDelay] {
|
|
2042
2048
|
animation: chart-fade-in 800ms ease-out both;
|
|
2043
2049
|
}
|
|
2050
|
+
.lib-xplat-chart .chart-legend {
|
|
2051
|
+
display: flex;
|
|
2052
|
+
flex-wrap: wrap;
|
|
2053
|
+
justify-content: center;
|
|
2054
|
+
gap: var(--spacing-space-4);
|
|
2055
|
+
padding: var(--spacing-space-3) 0;
|
|
2056
|
+
}
|
|
2057
|
+
.lib-xplat-chart .chart-legend-item {
|
|
2058
|
+
display: flex;
|
|
2059
|
+
align-items: flex-start;
|
|
2060
|
+
gap: var(--spacing-space-2);
|
|
2061
|
+
}
|
|
2062
|
+
.lib-xplat-chart .chart-legend-dot {
|
|
2063
|
+
flex-shrink: 0;
|
|
2064
|
+
width: 10px;
|
|
2065
|
+
height: 10px;
|
|
2066
|
+
border-radius: 50%;
|
|
2067
|
+
margin-top: 3px;
|
|
2068
|
+
}
|
|
2069
|
+
.lib-xplat-chart .chart-legend-text {
|
|
2070
|
+
display: flex;
|
|
2071
|
+
flex-direction: column;
|
|
2072
|
+
}
|
|
2073
|
+
.lib-xplat-chart .chart-legend-label {
|
|
2074
|
+
font-size: 12px;
|
|
2075
|
+
color: var(--semantic-text-muted);
|
|
2076
|
+
}
|
|
2077
|
+
.lib-xplat-chart .chart-legend-value {
|
|
2078
|
+
font-size: 14px;
|
|
2079
|
+
font-weight: 600;
|
|
2080
|
+
color: var(--semantic-text-strong);
|
|
2081
|
+
}
|
|
2044
2082
|
@keyframes chart-bar-grow {
|
|
2045
2083
|
from {
|
|
2046
2084
|
transform: scaleY(0);
|
|
@@ -2060,11 +2098,17 @@
|
|
|
2060
2098
|
@keyframes chart-tooltip-in {
|
|
2061
2099
|
from {
|
|
2062
2100
|
opacity: 0;
|
|
2063
|
-
transform: translate(-50%, -90%);
|
|
2064
2101
|
}
|
|
2065
2102
|
to {
|
|
2066
2103
|
opacity: 1;
|
|
2067
|
-
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
@keyframes chart-tooltip-out {
|
|
2107
|
+
from {
|
|
2108
|
+
opacity: 1;
|
|
2109
|
+
}
|
|
2110
|
+
to {
|
|
2111
|
+
opacity: 0;
|
|
2068
2112
|
}
|
|
2069
2113
|
}
|
|
2070
2114
|
@media (prefers-reduced-motion: reduce) {
|