@x-plat/design-system 0.5.20 → 0.5.21

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.
@@ -475,8 +475,22 @@ var PieDonutChart = import_react.default.memo(
475
475
  const innerR = isDoughnut ? r * 0.5 : 0;
476
476
  const firstKey = entries[0]?.[0] ?? "";
477
477
  const colorOffset = hashString(firstKey);
478
+ const maskRef = import_react.default.useRef(null);
479
+ const maskR = r + 10;
480
+ const maskCircumference = 2 * Math.PI * maskR;
481
+ import_react.default.useEffect(() => {
482
+ if (!animate || !maskRef.current) return;
483
+ const el = maskRef.current;
484
+ el.style.strokeDasharray = `${maskCircumference}`;
485
+ el.style.strokeDashoffset = `${maskCircumference}`;
486
+ requestAnimationFrame(() => {
487
+ el.style.transition = "stroke-dashoffset 1000ms ease-out";
488
+ el.style.strokeDashoffset = "0";
489
+ });
490
+ }, [animate, maskCircumference]);
478
491
  const sliceData = import_react.default.useMemo(() => {
479
492
  let angle0 = -Math.PI / 2;
493
+ let cumulativeAngle = 0;
480
494
  return values.map((v, i) => {
481
495
  const angle = v / total * Math.PI * 2;
482
496
  const endAngle = angle0 + angle;
@@ -500,38 +514,53 @@ var PieDonutChart = import_react.default.memo(
500
514
  const lx = cx + labelR * Math.cos(midAngle);
501
515
  const ly = cy + labelR * Math.sin(midAngle);
502
516
  const pct = Math.round(v / total * 100);
517
+ cumulativeAngle += angle;
518
+ const sliceEndRatio = cumulativeAngle / (Math.PI * 2);
519
+ const labelDelay = 1e3 * sliceEndRatio + 150;
503
520
  angle0 = endAngle;
504
- return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}` };
521
+ return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}`, labelDelay };
505
522
  });
506
523
  }, [values, total, cx, cy, r, innerR, labels]);
507
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: sliceData.map((s, i) => {
508
- const delay = i * 100;
509
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { className: animate ? "chart-slice-group-animate" : "", style: animate ? { animationDelay: `${delay}ms` } : void 0, children: [
510
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
511
- "path",
512
- {
513
- d: s.d,
514
- fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
515
- className: "chart-slice",
516
- onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
517
- onMouseMove: onMove,
518
- onMouseLeave: onLeave
519
- }
520
- ),
521
- s.angle > 0.2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
522
- "text",
523
- {
524
- x: s.lx,
525
- y: s.ly,
526
- className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
527
- style: animate ? { animationDelay: `${delay + 150}ms` } : void 0,
528
- textAnchor: "middle",
529
- dominantBaseline: "central",
530
- children: s.v
531
- }
532
- )
533
- ] }, i);
534
- }) });
524
+ const maskId = `pie-mask-${isDoughnut ? "d" : "p"}`;
525
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: [
526
+ animate && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("mask", { id: maskId, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
527
+ "circle",
528
+ {
529
+ ref: maskRef,
530
+ cx,
531
+ cy,
532
+ r: maskR,
533
+ fill: "none",
534
+ stroke: "white",
535
+ strokeWidth: maskR * 2,
536
+ transform: `rotate(-90 ${cx} ${cy})`
537
+ }
538
+ ) }) }),
539
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { mask: animate ? `url(#${maskId})` : void 0, children: sliceData.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
540
+ "path",
541
+ {
542
+ d: s.d,
543
+ fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
544
+ className: "chart-slice",
545
+ onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
546
+ onMouseMove: onMove,
547
+ onMouseLeave: onLeave
548
+ }
549
+ ) }, i)) }),
550
+ sliceData.map((s, i) => s.angle > 0.2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
551
+ "text",
552
+ {
553
+ x: s.lx,
554
+ y: s.ly,
555
+ className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
556
+ style: animate ? { animationDelay: `${s.labelDelay}ms` } : void 0,
557
+ textAnchor: "middle",
558
+ dominantBaseline: "central",
559
+ children: s.v
560
+ },
561
+ `label-${i}`
562
+ ))
563
+ ] });
535
564
  }
536
565
  );
537
566
  PieDonutChart.displayName = "PieDonutChart";
@@ -63,7 +63,7 @@
63
63
  transform 0.15s ease-out;
64
64
  transform-origin: center;
65
65
  }
66
- .lib-xplat-chart .chart-svg:hover .chart-slice {
66
+ .lib-xplat-chart .chart-pie:has(.chart-slice:hover) .chart-slice {
67
67
  opacity: 0.5;
68
68
  }
69
69
  .lib-xplat-chart .chart-slice:hover {
@@ -91,10 +91,8 @@
91
91
  .lib-xplat-chart .chart-bar-animate {
92
92
  animation: chart-bar-grow 800ms ease-out both;
93
93
  }
94
- .lib-xplat-chart .chart-slice-group-animate {
95
- animation: chart-slice-in 1000ms ease-out both;
96
- }
97
94
  .lib-xplat-chart .chart-pie-label-animate {
95
+ opacity: 0;
98
96
  animation: chart-fade-in 150ms ease-out both;
99
97
  }
100
98
  .lib-xplat-chart .chart-area[style*=animationDelay] {
@@ -108,16 +106,6 @@
108
106
  transform: scaleY(1);
109
107
  }
110
108
  }
111
- @keyframes chart-slice-in {
112
- from {
113
- opacity: 0;
114
- transform: scale(0.8);
115
- }
116
- to {
117
- opacity: 1;
118
- transform: scale(1);
119
- }
120
- }
121
109
  @keyframes chart-fade-in {
122
110
  from {
123
111
  opacity: 0;
@@ -138,7 +126,6 @@
138
126
  }
139
127
  @media (prefers-reduced-motion: reduce) {
140
128
  .lib-xplat-chart .chart-bar-animate,
141
- .lib-xplat-chart .chart-slice-group-animate,
142
129
  .lib-xplat-chart .chart-pie-label-animate,
143
130
  .lib-xplat-chart .chart-area {
144
131
  animation: none !important;
@@ -439,8 +439,22 @@ var PieDonutChart = React.memo(
439
439
  const innerR = isDoughnut ? r * 0.5 : 0;
440
440
  const firstKey = entries[0]?.[0] ?? "";
441
441
  const colorOffset = hashString(firstKey);
442
+ const maskRef = React.useRef(null);
443
+ const maskR = r + 10;
444
+ const maskCircumference = 2 * Math.PI * maskR;
445
+ React.useEffect(() => {
446
+ if (!animate || !maskRef.current) return;
447
+ const el = maskRef.current;
448
+ el.style.strokeDasharray = `${maskCircumference}`;
449
+ el.style.strokeDashoffset = `${maskCircumference}`;
450
+ requestAnimationFrame(() => {
451
+ el.style.transition = "stroke-dashoffset 1000ms ease-out";
452
+ el.style.strokeDashoffset = "0";
453
+ });
454
+ }, [animate, maskCircumference]);
442
455
  const sliceData = React.useMemo(() => {
443
456
  let angle0 = -Math.PI / 2;
457
+ let cumulativeAngle = 0;
444
458
  return values.map((v, i) => {
445
459
  const angle = v / total * Math.PI * 2;
446
460
  const endAngle = angle0 + angle;
@@ -464,38 +478,53 @@ var PieDonutChart = React.memo(
464
478
  const lx = cx + labelR * Math.cos(midAngle);
465
479
  const ly = cy + labelR * Math.sin(midAngle);
466
480
  const pct = Math.round(v / total * 100);
481
+ cumulativeAngle += angle;
482
+ const sliceEndRatio = cumulativeAngle / (Math.PI * 2);
483
+ const labelDelay = 1e3 * sliceEndRatio + 150;
467
484
  angle0 = endAngle;
468
- return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}` };
485
+ return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}`, labelDelay };
469
486
  });
470
487
  }, [values, total, cx, cy, r, innerR, labels]);
471
- return /* @__PURE__ */ jsx("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: sliceData.map((s, i) => {
472
- const delay = i * 100;
473
- return /* @__PURE__ */ jsxs("g", { className: animate ? "chart-slice-group-animate" : "", style: animate ? { animationDelay: `${delay}ms` } : void 0, children: [
474
- /* @__PURE__ */ jsx(
475
- "path",
476
- {
477
- d: s.d,
478
- fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
479
- className: "chart-slice",
480
- onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
481
- onMouseMove: onMove,
482
- onMouseLeave: onLeave
483
- }
484
- ),
485
- s.angle > 0.2 && /* @__PURE__ */ jsx(
486
- "text",
487
- {
488
- x: s.lx,
489
- y: s.ly,
490
- className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
491
- style: animate ? { animationDelay: `${delay + 150}ms` } : void 0,
492
- textAnchor: "middle",
493
- dominantBaseline: "central",
494
- children: s.v
495
- }
496
- )
497
- ] }, i);
498
- }) });
488
+ const maskId = `pie-mask-${isDoughnut ? "d" : "p"}`;
489
+ return /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: [
490
+ animate && /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("mask", { id: maskId, children: /* @__PURE__ */ jsx(
491
+ "circle",
492
+ {
493
+ ref: maskRef,
494
+ cx,
495
+ cy,
496
+ r: maskR,
497
+ fill: "none",
498
+ stroke: "white",
499
+ strokeWidth: maskR * 2,
500
+ transform: `rotate(-90 ${cx} ${cy})`
501
+ }
502
+ ) }) }),
503
+ /* @__PURE__ */ jsx("g", { mask: animate ? `url(#${maskId})` : void 0, children: sliceData.map((s, i) => /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx(
504
+ "path",
505
+ {
506
+ d: s.d,
507
+ fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
508
+ className: "chart-slice",
509
+ onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
510
+ onMouseMove: onMove,
511
+ onMouseLeave: onLeave
512
+ }
513
+ ) }, i)) }),
514
+ sliceData.map((s, i) => s.angle > 0.2 && /* @__PURE__ */ jsx(
515
+ "text",
516
+ {
517
+ x: s.lx,
518
+ y: s.ly,
519
+ className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
520
+ style: animate ? { animationDelay: `${s.labelDelay}ms` } : void 0,
521
+ textAnchor: "middle",
522
+ dominantBaseline: "central",
523
+ children: s.v
524
+ },
525
+ `label-${i}`
526
+ ))
527
+ ] });
499
528
  }
500
529
  );
501
530
  PieDonutChart.displayName = "PieDonutChart";
@@ -51,6 +51,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
51
51
  const popH = popRef.current.offsetHeight;
52
52
  const viewportHeight = window.innerHeight;
53
53
  const viewportWidth = window.innerWidth;
54
+ if (popH === 0 || popW === 0) return;
54
55
  let direction = "bottom";
55
56
  let top;
56
57
  let left;
@@ -76,6 +77,14 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
76
77
  const raf = requestAnimationFrame(calculatePosition);
77
78
  return () => cancelAnimationFrame(raf);
78
79
  }, [calculatePosition, enabled]);
80
+ import_react.default.useEffect(() => {
81
+ if (!enabled || !popRef.current) return;
82
+ const observer = new ResizeObserver(() => {
83
+ calculatePosition();
84
+ });
85
+ observer.observe(popRef.current);
86
+ return () => observer.disconnect();
87
+ }, [calculatePosition, enabled, popRef]);
79
88
  import_react.default.useEffect(() => {
80
89
  if (!enabled) return;
81
90
  window.addEventListener("resize", calculatePosition);
@@ -15,6 +15,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
15
15
  const popH = popRef.current.offsetHeight;
16
16
  const viewportHeight = window.innerHeight;
17
17
  const viewportWidth = window.innerWidth;
18
+ if (popH === 0 || popW === 0) return;
18
19
  let direction = "bottom";
19
20
  let top;
20
21
  let left;
@@ -40,6 +41,14 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
40
41
  const raf = requestAnimationFrame(calculatePosition);
41
42
  return () => cancelAnimationFrame(raf);
42
43
  }, [calculatePosition, enabled]);
44
+ React.useEffect(() => {
45
+ if (!enabled || !popRef.current) return;
46
+ const observer = new ResizeObserver(() => {
47
+ calculatePosition();
48
+ });
49
+ observer.observe(popRef.current);
50
+ return () => observer.disconnect();
51
+ }, [calculatePosition, enabled, popRef]);
43
52
  React.useEffect(() => {
44
53
  if (!enabled) return;
45
54
  window.addEventListener("resize", calculatePosition);
@@ -51,6 +51,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
51
51
  const popH = popRef.current.offsetHeight;
52
52
  const viewportHeight = window.innerHeight;
53
53
  const viewportWidth = window.innerWidth;
54
+ if (popH === 0 || popW === 0) return;
54
55
  let direction = "bottom";
55
56
  let top;
56
57
  let left;
@@ -76,6 +77,14 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
76
77
  const raf = requestAnimationFrame(calculatePosition);
77
78
  return () => cancelAnimationFrame(raf);
78
79
  }, [calculatePosition, enabled]);
80
+ import_react.default.useEffect(() => {
81
+ if (!enabled || !popRef.current) return;
82
+ const observer = new ResizeObserver(() => {
83
+ calculatePosition();
84
+ });
85
+ observer.observe(popRef.current);
86
+ return () => observer.disconnect();
87
+ }, [calculatePosition, enabled, popRef]);
79
88
  import_react.default.useEffect(() => {
80
89
  if (!enabled) return;
81
90
  window.addEventListener("resize", calculatePosition);
@@ -15,6 +15,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
15
15
  const popH = popRef.current.offsetHeight;
16
16
  const viewportHeight = window.innerHeight;
17
17
  const viewportWidth = window.innerWidth;
18
+ if (popH === 0 || popW === 0) return;
18
19
  let direction = "bottom";
19
20
  let top;
20
21
  let left;
@@ -40,6 +41,14 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
40
41
  const raf = requestAnimationFrame(calculatePosition);
41
42
  return () => cancelAnimationFrame(raf);
42
43
  }, [calculatePosition, enabled]);
44
+ React.useEffect(() => {
45
+ if (!enabled || !popRef.current) return;
46
+ const observer = new ResizeObserver(() => {
47
+ calculatePosition();
48
+ });
49
+ observer.observe(popRef.current);
50
+ return () => observer.disconnect();
51
+ }, [calculatePosition, enabled, popRef]);
43
52
  React.useEffect(() => {
44
53
  if (!enabled) return;
45
54
  window.addEventListener("resize", calculatePosition);
@@ -52,6 +52,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
52
52
  const popH = popRef.current.offsetHeight;
53
53
  const viewportHeight = window.innerHeight;
54
54
  const viewportWidth = window.innerWidth;
55
+ if (popH === 0 || popW === 0) return;
55
56
  let direction = "bottom";
56
57
  let top;
57
58
  let left;
@@ -77,6 +78,14 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
77
78
  const raf = requestAnimationFrame(calculatePosition);
78
79
  return () => cancelAnimationFrame(raf);
79
80
  }, [calculatePosition, enabled]);
81
+ import_react.default.useEffect(() => {
82
+ if (!enabled || !popRef.current) return;
83
+ const observer = new ResizeObserver(() => {
84
+ calculatePosition();
85
+ });
86
+ observer.observe(popRef.current);
87
+ return () => observer.disconnect();
88
+ }, [calculatePosition, enabled, popRef]);
80
89
  import_react.default.useEffect(() => {
81
90
  if (!enabled) return;
82
91
  window.addEventListener("resize", calculatePosition);
@@ -15,6 +15,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
15
15
  const popH = popRef.current.offsetHeight;
16
16
  const viewportHeight = window.innerHeight;
17
17
  const viewportWidth = window.innerWidth;
18
+ if (popH === 0 || popW === 0) return;
18
19
  let direction = "bottom";
19
20
  let top;
20
21
  let left;
@@ -40,6 +41,14 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
40
41
  const raf = requestAnimationFrame(calculatePosition);
41
42
  return () => cancelAnimationFrame(raf);
42
43
  }, [calculatePosition, enabled]);
44
+ React.useEffect(() => {
45
+ if (!enabled || !popRef.current) return;
46
+ const observer = new ResizeObserver(() => {
47
+ calculatePosition();
48
+ });
49
+ observer.observe(popRef.current);
50
+ return () => observer.disconnect();
51
+ }, [calculatePosition, enabled, popRef]);
43
52
  React.useEffect(() => {
44
53
  if (!enabled) return;
45
54
  window.addEventListener("resize", calculatePosition);
@@ -2546,8 +2546,22 @@ var PieDonutChart = import_react5.default.memo(
2546
2546
  const innerR = isDoughnut ? r2 * 0.5 : 0;
2547
2547
  const firstKey = entries[0]?.[0] ?? "";
2548
2548
  const colorOffset = hashString(firstKey);
2549
+ const maskRef = import_react5.default.useRef(null);
2550
+ const maskR = r2 + 10;
2551
+ const maskCircumference = 2 * Math.PI * maskR;
2552
+ import_react5.default.useEffect(() => {
2553
+ if (!animate || !maskRef.current) return;
2554
+ const el = maskRef.current;
2555
+ el.style.strokeDasharray = `${maskCircumference}`;
2556
+ el.style.strokeDashoffset = `${maskCircumference}`;
2557
+ requestAnimationFrame(() => {
2558
+ el.style.transition = "stroke-dashoffset 1000ms ease-out";
2559
+ el.style.strokeDashoffset = "0";
2560
+ });
2561
+ }, [animate, maskCircumference]);
2549
2562
  const sliceData = import_react5.default.useMemo(() => {
2550
2563
  let angle0 = -Math.PI / 2;
2564
+ let cumulativeAngle = 0;
2551
2565
  return values.map((v, i) => {
2552
2566
  const angle = v / total * Math.PI * 2;
2553
2567
  const endAngle = angle0 + angle;
@@ -2571,38 +2585,53 @@ var PieDonutChart = import_react5.default.memo(
2571
2585
  const lx = cx + labelR * Math.cos(midAngle);
2572
2586
  const ly = cy + labelR * Math.sin(midAngle);
2573
2587
  const pct = Math.round(v / total * 100);
2588
+ cumulativeAngle += angle;
2589
+ const sliceEndRatio = cumulativeAngle / (Math.PI * 2);
2590
+ const labelDelay = 1e3 * sliceEndRatio + 150;
2574
2591
  angle0 = endAngle;
2575
- return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}` };
2592
+ return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}`, labelDelay };
2576
2593
  });
2577
2594
  }, [values, total, cx, cy, r2, innerR, labels]);
2578
- return /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: sliceData.map((s, i) => {
2579
- const delay = i * 100;
2580
- return /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("g", { className: animate ? "chart-slice-group-animate" : "", style: animate ? { animationDelay: `${delay}ms` } : void 0, children: [
2581
- /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
2582
- "path",
2583
- {
2584
- d: s.d,
2585
- fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
2586
- className: "chart-slice",
2587
- onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
2588
- onMouseMove: onMove,
2589
- onMouseLeave: onLeave
2590
- }
2591
- ),
2592
- s.angle > 0.2 && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
2593
- "text",
2594
- {
2595
- x: s.lx,
2596
- y: s.ly,
2597
- className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
2598
- style: animate ? { animationDelay: `${delay + 150}ms` } : void 0,
2599
- textAnchor: "middle",
2600
- dominantBaseline: "central",
2601
- children: s.v
2602
- }
2603
- )
2604
- ] }, i);
2605
- }) });
2595
+ const maskId = `pie-mask-${isDoughnut ? "d" : "p"}`;
2596
+ return /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: [
2597
+ animate && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("mask", { id: maskId, children: /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
2598
+ "circle",
2599
+ {
2600
+ ref: maskRef,
2601
+ cx,
2602
+ cy,
2603
+ r: maskR,
2604
+ fill: "none",
2605
+ stroke: "white",
2606
+ strokeWidth: maskR * 2,
2607
+ transform: `rotate(-90 ${cx} ${cy})`
2608
+ }
2609
+ ) }) }),
2610
+ /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("g", { mask: animate ? `url(#${maskId})` : void 0, children: sliceData.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("g", { children: /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
2611
+ "path",
2612
+ {
2613
+ d: s.d,
2614
+ fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
2615
+ className: "chart-slice",
2616
+ onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
2617
+ onMouseMove: onMove,
2618
+ onMouseLeave: onLeave
2619
+ }
2620
+ ) }, i)) }),
2621
+ sliceData.map((s, i) => s.angle > 0.2 && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
2622
+ "text",
2623
+ {
2624
+ x: s.lx,
2625
+ y: s.ly,
2626
+ className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
2627
+ style: animate ? { animationDelay: `${s.labelDelay}ms` } : void 0,
2628
+ textAnchor: "middle",
2629
+ dominantBaseline: "central",
2630
+ children: s.v
2631
+ },
2632
+ `label-${i}`
2633
+ ))
2634
+ ] });
2606
2635
  }
2607
2636
  );
2608
2637
  PieDonutChart.displayName = "PieDonutChart";
@@ -3519,6 +3548,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3519
3548
  const popH = popRef.current.offsetHeight;
3520
3549
  const viewportHeight = window.innerHeight;
3521
3550
  const viewportWidth = window.innerWidth;
3551
+ if (popH === 0 || popW === 0) return;
3522
3552
  let direction = "bottom";
3523
3553
  let top;
3524
3554
  let left;
@@ -3544,6 +3574,14 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3544
3574
  const raf = requestAnimationFrame(calculatePosition);
3545
3575
  return () => cancelAnimationFrame(raf);
3546
3576
  }, [calculatePosition, enabled]);
3577
+ import_react18.default.useEffect(() => {
3578
+ if (!enabled || !popRef.current) return;
3579
+ const observer = new ResizeObserver(() => {
3580
+ calculatePosition();
3581
+ });
3582
+ observer.observe(popRef.current);
3583
+ return () => observer.disconnect();
3584
+ }, [calculatePosition, enabled, popRef]);
3547
3585
  import_react18.default.useEffect(() => {
3548
3586
  if (!enabled) return;
3549
3587
  window.addEventListener("resize", calculatePosition);
@@ -1864,7 +1864,7 @@
1864
1864
  transform 0.15s ease-out;
1865
1865
  transform-origin: center;
1866
1866
  }
1867
- .lib-xplat-chart .chart-svg:hover .chart-slice {
1867
+ .lib-xplat-chart .chart-pie:has(.chart-slice:hover) .chart-slice {
1868
1868
  opacity: 0.5;
1869
1869
  }
1870
1870
  .lib-xplat-chart .chart-slice:hover {
@@ -1892,10 +1892,8 @@
1892
1892
  .lib-xplat-chart .chart-bar-animate {
1893
1893
  animation: chart-bar-grow 800ms ease-out both;
1894
1894
  }
1895
- .lib-xplat-chart .chart-slice-group-animate {
1896
- animation: chart-slice-in 1000ms ease-out both;
1897
- }
1898
1895
  .lib-xplat-chart .chart-pie-label-animate {
1896
+ opacity: 0;
1899
1897
  animation: chart-fade-in 150ms ease-out both;
1900
1898
  }
1901
1899
  .lib-xplat-chart .chart-area[style*=animationDelay] {
@@ -1909,16 +1907,6 @@
1909
1907
  transform: scaleY(1);
1910
1908
  }
1911
1909
  }
1912
- @keyframes chart-slice-in {
1913
- from {
1914
- opacity: 0;
1915
- transform: scale(0.8);
1916
- }
1917
- to {
1918
- opacity: 1;
1919
- transform: scale(1);
1920
- }
1921
- }
1922
1910
  @keyframes chart-fade-in {
1923
1911
  from {
1924
1912
  opacity: 0;
@@ -1939,7 +1927,6 @@
1939
1927
  }
1940
1928
  @media (prefers-reduced-motion: reduce) {
1941
1929
  .lib-xplat-chart .chart-bar-animate,
1942
- .lib-xplat-chart .chart-slice-group-animate,
1943
1930
  .lib-xplat-chart .chart-pie-label-animate,
1944
1931
  .lib-xplat-chart .chart-area {
1945
1932
  animation: none !important;
@@ -2458,8 +2458,22 @@ var PieDonutChart = React5.memo(
2458
2458
  const innerR = isDoughnut ? r2 * 0.5 : 0;
2459
2459
  const firstKey = entries[0]?.[0] ?? "";
2460
2460
  const colorOffset = hashString(firstKey);
2461
+ const maskRef = React5.useRef(null);
2462
+ const maskR = r2 + 10;
2463
+ const maskCircumference = 2 * Math.PI * maskR;
2464
+ React5.useEffect(() => {
2465
+ if (!animate || !maskRef.current) return;
2466
+ const el = maskRef.current;
2467
+ el.style.strokeDasharray = `${maskCircumference}`;
2468
+ el.style.strokeDashoffset = `${maskCircumference}`;
2469
+ requestAnimationFrame(() => {
2470
+ el.style.transition = "stroke-dashoffset 1000ms ease-out";
2471
+ el.style.strokeDashoffset = "0";
2472
+ });
2473
+ }, [animate, maskCircumference]);
2461
2474
  const sliceData = React5.useMemo(() => {
2462
2475
  let angle0 = -Math.PI / 2;
2476
+ let cumulativeAngle = 0;
2463
2477
  return values.map((v, i) => {
2464
2478
  const angle = v / total * Math.PI * 2;
2465
2479
  const endAngle = angle0 + angle;
@@ -2483,38 +2497,53 @@ var PieDonutChart = React5.memo(
2483
2497
  const lx = cx + labelR * Math.cos(midAngle);
2484
2498
  const ly = cy + labelR * Math.sin(midAngle);
2485
2499
  const pct = Math.round(v / total * 100);
2500
+ cumulativeAngle += angle;
2501
+ const sliceEndRatio = cumulativeAngle / (Math.PI * 2);
2502
+ const labelDelay = 1e3 * sliceEndRatio + 150;
2486
2503
  angle0 = endAngle;
2487
- return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}` };
2504
+ return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}`, labelDelay };
2488
2505
  });
2489
2506
  }, [values, total, cx, cy, r2, innerR, labels]);
2490
- return /* @__PURE__ */ jsx305("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: sliceData.map((s, i) => {
2491
- const delay = i * 100;
2492
- return /* @__PURE__ */ jsxs196("g", { className: animate ? "chart-slice-group-animate" : "", style: animate ? { animationDelay: `${delay}ms` } : void 0, children: [
2493
- /* @__PURE__ */ jsx305(
2494
- "path",
2495
- {
2496
- d: s.d,
2497
- fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
2498
- className: "chart-slice",
2499
- onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
2500
- onMouseMove: onMove,
2501
- onMouseLeave: onLeave
2502
- }
2503
- ),
2504
- s.angle > 0.2 && /* @__PURE__ */ jsx305(
2505
- "text",
2506
- {
2507
- x: s.lx,
2508
- y: s.ly,
2509
- className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
2510
- style: animate ? { animationDelay: `${delay + 150}ms` } : void 0,
2511
- textAnchor: "middle",
2512
- dominantBaseline: "central",
2513
- children: s.v
2514
- }
2515
- )
2516
- ] }, i);
2517
- }) });
2507
+ const maskId = `pie-mask-${isDoughnut ? "d" : "p"}`;
2508
+ return /* @__PURE__ */ jsxs196("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: [
2509
+ animate && /* @__PURE__ */ jsx305("defs", { children: /* @__PURE__ */ jsx305("mask", { id: maskId, children: /* @__PURE__ */ jsx305(
2510
+ "circle",
2511
+ {
2512
+ ref: maskRef,
2513
+ cx,
2514
+ cy,
2515
+ r: maskR,
2516
+ fill: "none",
2517
+ stroke: "white",
2518
+ strokeWidth: maskR * 2,
2519
+ transform: `rotate(-90 ${cx} ${cy})`
2520
+ }
2521
+ ) }) }),
2522
+ /* @__PURE__ */ jsx305("g", { mask: animate ? `url(#${maskId})` : void 0, children: sliceData.map((s, i) => /* @__PURE__ */ jsx305("g", { children: /* @__PURE__ */ jsx305(
2523
+ "path",
2524
+ {
2525
+ d: s.d,
2526
+ fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
2527
+ className: "chart-slice",
2528
+ onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
2529
+ onMouseMove: onMove,
2530
+ onMouseLeave: onLeave
2531
+ }
2532
+ ) }, i)) }),
2533
+ sliceData.map((s, i) => s.angle > 0.2 && /* @__PURE__ */ jsx305(
2534
+ "text",
2535
+ {
2536
+ x: s.lx,
2537
+ y: s.ly,
2538
+ className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
2539
+ style: animate ? { animationDelay: `${s.labelDelay}ms` } : void 0,
2540
+ textAnchor: "middle",
2541
+ dominantBaseline: "central",
2542
+ children: s.v
2543
+ },
2544
+ `label-${i}`
2545
+ ))
2546
+ ] });
2518
2547
  }
2519
2548
  );
2520
2549
  PieDonutChart.displayName = "PieDonutChart";
@@ -3431,6 +3460,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3431
3460
  const popH = popRef.current.offsetHeight;
3432
3461
  const viewportHeight = window.innerHeight;
3433
3462
  const viewportWidth = window.innerWidth;
3463
+ if (popH === 0 || popW === 0) return;
3434
3464
  let direction = "bottom";
3435
3465
  let top;
3436
3466
  let left;
@@ -3456,6 +3486,14 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3456
3486
  const raf = requestAnimationFrame(calculatePosition);
3457
3487
  return () => cancelAnimationFrame(raf);
3458
3488
  }, [calculatePosition, enabled]);
3489
+ React17.useEffect(() => {
3490
+ if (!enabled || !popRef.current) return;
3491
+ const observer = new ResizeObserver(() => {
3492
+ calculatePosition();
3493
+ });
3494
+ observer.observe(popRef.current);
3495
+ return () => observer.disconnect();
3496
+ }, [calculatePosition, enabled, popRef]);
3459
3497
  React17.useEffect(() => {
3460
3498
  if (!enabled) return;
3461
3499
  window.addEventListener("resize", calculatePosition);
package/dist/index.cjs CHANGED
@@ -6957,8 +6957,22 @@ var PieDonutChart = import_react5.default.memo(
6957
6957
  const innerR = isDoughnut ? r2 * 0.5 : 0;
6958
6958
  const firstKey = entries[0]?.[0] ?? "";
6959
6959
  const colorOffset = hashString(firstKey);
6960
+ const maskRef = import_react5.default.useRef(null);
6961
+ const maskR = r2 + 10;
6962
+ const maskCircumference = 2 * Math.PI * maskR;
6963
+ import_react5.default.useEffect(() => {
6964
+ if (!animate || !maskRef.current) return;
6965
+ const el = maskRef.current;
6966
+ el.style.strokeDasharray = `${maskCircumference}`;
6967
+ el.style.strokeDashoffset = `${maskCircumference}`;
6968
+ requestAnimationFrame(() => {
6969
+ el.style.transition = "stroke-dashoffset 1000ms ease-out";
6970
+ el.style.strokeDashoffset = "0";
6971
+ });
6972
+ }, [animate, maskCircumference]);
6960
6973
  const sliceData = import_react5.default.useMemo(() => {
6961
6974
  let angle0 = -Math.PI / 2;
6975
+ let cumulativeAngle = 0;
6962
6976
  return values.map((v, i) => {
6963
6977
  const angle = v / total * Math.PI * 2;
6964
6978
  const endAngle = angle0 + angle;
@@ -6982,38 +6996,53 @@ var PieDonutChart = import_react5.default.memo(
6982
6996
  const lx = cx + labelR * Math.cos(midAngle);
6983
6997
  const ly = cy + labelR * Math.sin(midAngle);
6984
6998
  const pct = Math.round(v / total * 100);
6999
+ cumulativeAngle += angle;
7000
+ const sliceEndRatio = cumulativeAngle / (Math.PI * 2);
7001
+ const labelDelay = 1e3 * sliceEndRatio + 150;
6985
7002
  angle0 = endAngle;
6986
- return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}` };
7003
+ return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}`, labelDelay };
6987
7004
  });
6988
7005
  }, [values, total, cx, cy, r2, innerR, labels]);
6989
- return /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: sliceData.map((s, i) => {
6990
- const delay = i * 100;
6991
- return /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("g", { className: animate ? "chart-slice-group-animate" : "", style: animate ? { animationDelay: `${delay}ms` } : void 0, children: [
6992
- /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
6993
- "path",
6994
- {
6995
- d: s.d,
6996
- fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
6997
- className: "chart-slice",
6998
- onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
6999
- onMouseMove: onMove,
7000
- onMouseLeave: onLeave
7001
- }
7002
- ),
7003
- s.angle > 0.2 && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
7004
- "text",
7005
- {
7006
- x: s.lx,
7007
- y: s.ly,
7008
- className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
7009
- style: animate ? { animationDelay: `${delay + 150}ms` } : void 0,
7010
- textAnchor: "middle",
7011
- dominantBaseline: "central",
7012
- children: s.v
7013
- }
7014
- )
7015
- ] }, i);
7016
- }) });
7006
+ const maskId = `pie-mask-${isDoughnut ? "d" : "p"}`;
7007
+ return /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: [
7008
+ animate && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("mask", { id: maskId, children: /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
7009
+ "circle",
7010
+ {
7011
+ ref: maskRef,
7012
+ cx,
7013
+ cy,
7014
+ r: maskR,
7015
+ fill: "none",
7016
+ stroke: "white",
7017
+ strokeWidth: maskR * 2,
7018
+ transform: `rotate(-90 ${cx} ${cy})`
7019
+ }
7020
+ ) }) }),
7021
+ /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("g", { mask: animate ? `url(#${maskId})` : void 0, children: sliceData.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("g", { children: /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
7022
+ "path",
7023
+ {
7024
+ d: s.d,
7025
+ fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
7026
+ className: "chart-slice",
7027
+ onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
7028
+ onMouseMove: onMove,
7029
+ onMouseLeave: onLeave
7030
+ }
7031
+ ) }, i)) }),
7032
+ sliceData.map((s, i) => s.angle > 0.2 && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
7033
+ "text",
7034
+ {
7035
+ x: s.lx,
7036
+ y: s.ly,
7037
+ className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
7038
+ style: animate ? { animationDelay: `${s.labelDelay}ms` } : void 0,
7039
+ textAnchor: "middle",
7040
+ dominantBaseline: "central",
7041
+ children: s.v
7042
+ },
7043
+ `label-${i}`
7044
+ ))
7045
+ ] });
7017
7046
  }
7018
7047
  );
7019
7048
  PieDonutChart.displayName = "PieDonutChart";
@@ -7943,6 +7972,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
7943
7972
  const popH = popRef.current.offsetHeight;
7944
7973
  const viewportHeight = window.innerHeight;
7945
7974
  const viewportWidth = window.innerWidth;
7975
+ if (popH === 0 || popW === 0) return;
7946
7976
  let direction = "bottom";
7947
7977
  let top;
7948
7978
  let left;
@@ -7968,6 +7998,14 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
7968
7998
  const raf = requestAnimationFrame(calculatePosition);
7969
7999
  return () => cancelAnimationFrame(raf);
7970
8000
  }, [calculatePosition, enabled]);
8001
+ import_react18.default.useEffect(() => {
8002
+ if (!enabled || !popRef.current) return;
8003
+ const observer = new ResizeObserver(() => {
8004
+ calculatePosition();
8005
+ });
8006
+ observer.observe(popRef.current);
8007
+ return () => observer.disconnect();
8008
+ }, [calculatePosition, enabled, popRef]);
7971
8009
  import_react18.default.useEffect(() => {
7972
8010
  if (!enabled) return;
7973
8011
  window.addEventListener("resize", calculatePosition);
package/dist/index.css CHANGED
@@ -1864,7 +1864,7 @@
1864
1864
  transform 0.15s ease-out;
1865
1865
  transform-origin: center;
1866
1866
  }
1867
- .lib-xplat-chart .chart-svg:hover .chart-slice {
1867
+ .lib-xplat-chart .chart-pie:has(.chart-slice:hover) .chart-slice {
1868
1868
  opacity: 0.5;
1869
1869
  }
1870
1870
  .lib-xplat-chart .chart-slice:hover {
@@ -1892,10 +1892,8 @@
1892
1892
  .lib-xplat-chart .chart-bar-animate {
1893
1893
  animation: chart-bar-grow 800ms ease-out both;
1894
1894
  }
1895
- .lib-xplat-chart .chart-slice-group-animate {
1896
- animation: chart-slice-in 1000ms ease-out both;
1897
- }
1898
1895
  .lib-xplat-chart .chart-pie-label-animate {
1896
+ opacity: 0;
1899
1897
  animation: chart-fade-in 150ms ease-out both;
1900
1898
  }
1901
1899
  .lib-xplat-chart .chart-area[style*=animationDelay] {
@@ -1909,16 +1907,6 @@
1909
1907
  transform: scaleY(1);
1910
1908
  }
1911
1909
  }
1912
- @keyframes chart-slice-in {
1913
- from {
1914
- opacity: 0;
1915
- transform: scale(0.8);
1916
- }
1917
- to {
1918
- opacity: 1;
1919
- transform: scale(1);
1920
- }
1921
- }
1922
1910
  @keyframes chart-fade-in {
1923
1911
  from {
1924
1912
  opacity: 0;
@@ -1939,7 +1927,6 @@
1939
1927
  }
1940
1928
  @media (prefers-reduced-motion: reduce) {
1941
1929
  .lib-xplat-chart .chart-bar-animate,
1942
- .lib-xplat-chart .chart-slice-group-animate,
1943
1930
  .lib-xplat-chart .chart-pie-label-animate,
1944
1931
  .lib-xplat-chart .chart-area {
1945
1932
  animation: none !important;
package/dist/index.js CHANGED
@@ -6560,8 +6560,22 @@ var PieDonutChart = React5.memo(
6560
6560
  const innerR = isDoughnut ? r2 * 0.5 : 0;
6561
6561
  const firstKey = entries[0]?.[0] ?? "";
6562
6562
  const colorOffset = hashString(firstKey);
6563
+ const maskRef = React5.useRef(null);
6564
+ const maskR = r2 + 10;
6565
+ const maskCircumference = 2 * Math.PI * maskR;
6566
+ React5.useEffect(() => {
6567
+ if (!animate || !maskRef.current) return;
6568
+ const el = maskRef.current;
6569
+ el.style.strokeDasharray = `${maskCircumference}`;
6570
+ el.style.strokeDashoffset = `${maskCircumference}`;
6571
+ requestAnimationFrame(() => {
6572
+ el.style.transition = "stroke-dashoffset 1000ms ease-out";
6573
+ el.style.strokeDashoffset = "0";
6574
+ });
6575
+ }, [animate, maskCircumference]);
6563
6576
  const sliceData = React5.useMemo(() => {
6564
6577
  let angle0 = -Math.PI / 2;
6578
+ let cumulativeAngle = 0;
6565
6579
  return values.map((v, i) => {
6566
6580
  const angle = v / total * Math.PI * 2;
6567
6581
  const endAngle = angle0 + angle;
@@ -6585,38 +6599,53 @@ var PieDonutChart = React5.memo(
6585
6599
  const lx = cx + labelR * Math.cos(midAngle);
6586
6600
  const ly = cy + labelR * Math.sin(midAngle);
6587
6601
  const pct = Math.round(v / total * 100);
6602
+ cumulativeAngle += angle;
6603
+ const sliceEndRatio = cumulativeAngle / (Math.PI * 2);
6604
+ const labelDelay = 1e3 * sliceEndRatio + 150;
6588
6605
  angle0 = endAngle;
6589
- return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}` };
6606
+ return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}`, labelDelay };
6590
6607
  });
6591
6608
  }, [values, total, cx, cy, r2, innerR, labels]);
6592
- return /* @__PURE__ */ jsx305("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: sliceData.map((s, i) => {
6593
- const delay = i * 100;
6594
- return /* @__PURE__ */ jsxs196("g", { className: animate ? "chart-slice-group-animate" : "", style: animate ? { animationDelay: `${delay}ms` } : void 0, children: [
6595
- /* @__PURE__ */ jsx305(
6596
- "path",
6597
- {
6598
- d: s.d,
6599
- fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
6600
- className: "chart-slice",
6601
- onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
6602
- onMouseMove: onMove,
6603
- onMouseLeave: onLeave
6604
- }
6605
- ),
6606
- s.angle > 0.2 && /* @__PURE__ */ jsx305(
6607
- "text",
6608
- {
6609
- x: s.lx,
6610
- y: s.ly,
6611
- className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
6612
- style: animate ? { animationDelay: `${delay + 150}ms` } : void 0,
6613
- textAnchor: "middle",
6614
- dominantBaseline: "central",
6615
- children: s.v
6616
- }
6617
- )
6618
- ] }, i);
6619
- }) });
6609
+ const maskId = `pie-mask-${isDoughnut ? "d" : "p"}`;
6610
+ return /* @__PURE__ */ jsxs196("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: [
6611
+ animate && /* @__PURE__ */ jsx305("defs", { children: /* @__PURE__ */ jsx305("mask", { id: maskId, children: /* @__PURE__ */ jsx305(
6612
+ "circle",
6613
+ {
6614
+ ref: maskRef,
6615
+ cx,
6616
+ cy,
6617
+ r: maskR,
6618
+ fill: "none",
6619
+ stroke: "white",
6620
+ strokeWidth: maskR * 2,
6621
+ transform: `rotate(-90 ${cx} ${cy})`
6622
+ }
6623
+ ) }) }),
6624
+ /* @__PURE__ */ jsx305("g", { mask: animate ? `url(#${maskId})` : void 0, children: sliceData.map((s, i) => /* @__PURE__ */ jsx305("g", { children: /* @__PURE__ */ jsx305(
6625
+ "path",
6626
+ {
6627
+ d: s.d,
6628
+ fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
6629
+ className: "chart-slice",
6630
+ onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
6631
+ onMouseMove: onMove,
6632
+ onMouseLeave: onLeave
6633
+ }
6634
+ ) }, i)) }),
6635
+ sliceData.map((s, i) => s.angle > 0.2 && /* @__PURE__ */ jsx305(
6636
+ "text",
6637
+ {
6638
+ x: s.lx,
6639
+ y: s.ly,
6640
+ className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
6641
+ style: animate ? { animationDelay: `${s.labelDelay}ms` } : void 0,
6642
+ textAnchor: "middle",
6643
+ dominantBaseline: "central",
6644
+ children: s.v
6645
+ },
6646
+ `label-${i}`
6647
+ ))
6648
+ ] });
6620
6649
  }
6621
6650
  );
6622
6651
  PieDonutChart.displayName = "PieDonutChart";
@@ -7546,6 +7575,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
7546
7575
  const popH = popRef.current.offsetHeight;
7547
7576
  const viewportHeight = window.innerHeight;
7548
7577
  const viewportWidth = window.innerWidth;
7578
+ if (popH === 0 || popW === 0) return;
7549
7579
  let direction = "bottom";
7550
7580
  let top;
7551
7581
  let left;
@@ -7571,6 +7601,14 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
7571
7601
  const raf = requestAnimationFrame(calculatePosition);
7572
7602
  return () => cancelAnimationFrame(raf);
7573
7603
  }, [calculatePosition, enabled]);
7604
+ React17.useEffect(() => {
7605
+ if (!enabled || !popRef.current) return;
7606
+ const observer = new ResizeObserver(() => {
7607
+ calculatePosition();
7608
+ });
7609
+ observer.observe(popRef.current);
7610
+ return () => observer.disconnect();
7611
+ }, [calculatePosition, enabled, popRef]);
7574
7612
  React17.useEffect(() => {
7575
7613
  if (!enabled) return;
7576
7614
  window.addEventListener("resize", calculatePosition);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-plat/design-system",
3
- "version": "0.5.20",
3
+ "version": "0.5.21",
4
4
  "description": "XPLAT UI Design System",
5
5
  "author": "XPLAT WOONG",
6
6
  "main": "dist/index.cjs",