@x-plat/design-system 0.5.17 → 0.5.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/components/Chart/index.cjs +147 -27
  2. package/dist/components/Chart/index.css +74 -7
  3. package/dist/components/Chart/index.js +147 -27
  4. package/dist/components/Drawer/index.cjs +2 -4
  5. package/dist/components/Drawer/index.css +12 -0
  6. package/dist/components/Drawer/index.d.cts +2 -1
  7. package/dist/components/Drawer/index.d.ts +2 -1
  8. package/dist/components/Drawer/index.js +2 -4
  9. package/dist/components/Dropdown/index.cjs +2 -0
  10. package/dist/components/Dropdown/index.js +2 -0
  11. package/dist/components/PopOver/index.cjs +2 -0
  12. package/dist/components/PopOver/index.css +2 -1
  13. package/dist/components/PopOver/index.js +2 -0
  14. package/dist/components/Select/index.cjs +2 -0
  15. package/dist/components/Select/index.js +2 -0
  16. package/dist/components/Skeleton/index.cjs +3 -2
  17. package/dist/components/Skeleton/index.d.cts +3 -2
  18. package/dist/components/Skeleton/index.d.ts +3 -2
  19. package/dist/components/Skeleton/index.js +3 -2
  20. package/dist/components/index.cjs +154 -33
  21. package/dist/components/index.css +88 -8
  22. package/dist/components/index.js +154 -33
  23. package/dist/index.cjs +158 -49
  24. package/dist/index.css +88 -8
  25. package/dist/index.js +158 -49
  26. package/dist/layout/Grid/FullGrid/index.cjs +2 -8
  27. package/dist/layout/Grid/FullGrid/index.d.cts +2 -1
  28. package/dist/layout/Grid/FullGrid/index.d.ts +2 -1
  29. package/dist/layout/Grid/FullGrid/index.js +2 -8
  30. package/dist/layout/Grid/FullScreen/index.cjs +2 -8
  31. package/dist/layout/Grid/FullScreen/index.d.cts +2 -1
  32. package/dist/layout/Grid/FullScreen/index.d.ts +2 -1
  33. package/dist/layout/Grid/FullScreen/index.js +2 -8
  34. package/dist/layout/Grid/index.cjs +4 -16
  35. package/dist/layout/Grid/index.js +4 -16
  36. package/dist/layout/index.cjs +4 -16
  37. package/dist/layout/index.js +4 -16
  38. package/package.json +1 -1
@@ -58,7 +58,7 @@ var clsx_default = clsx;
58
58
  var import_jsx_runtime = require("react/jsx-runtime");
59
59
  var ANIMATION_DURATION_MS = 250;
60
60
  var Drawer = (props) => {
61
- const { isOpen, onClose, placement = "right", width = 320, title, children } = props;
61
+ const { isOpen, onClose, placement = "right", size = "md", title, children } = props;
62
62
  const [mounted, setMounted] = import_react.default.useState(false);
63
63
  const [visible, setVisible] = import_react.default.useState(false);
64
64
  import_react.default.useEffect(() => {
@@ -74,7 +74,6 @@ var Drawer = (props) => {
74
74
  if (typeof document === "undefined") return null;
75
75
  if (!mounted) return null;
76
76
  const stateClass = visible ? "enter" : "exit";
77
- const widthValue = typeof width === "number" ? `${width}px` : width;
78
77
  return (0, import_react_dom.createPortal)(
79
78
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
80
79
  "div",
@@ -84,8 +83,7 @@ var Drawer = (props) => {
84
83
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
85
84
  "div",
86
85
  {
87
- className: clsx_default("lib-xplat-drawer", placement, stateClass),
88
- style: { width: widthValue },
86
+ className: clsx_default("lib-xplat-drawer", placement, size, stateClass),
89
87
  role: "dialog",
90
88
  "aria-modal": "true",
91
89
  onClick: (e) => e.stopPropagation(),
@@ -23,6 +23,18 @@
23
23
  transition: transform 0.25s ease;
24
24
  z-index: 1001;
25
25
  }
26
+ .lib-xplat-drawer.sm {
27
+ width: 280px;
28
+ }
29
+ .lib-xplat-drawer.md {
30
+ width: 360px;
31
+ }
32
+ .lib-xplat-drawer.lg {
33
+ width: 480px;
34
+ }
35
+ .lib-xplat-drawer.xl {
36
+ width: 640px;
37
+ }
26
38
  .lib-xplat-drawer.right {
27
39
  right: 0;
28
40
  transform: translateX(100%);
@@ -1,11 +1,12 @@
1
1
  import React from 'react';
2
2
 
3
3
  type DrawerPlacement = "left" | "right";
4
+ type DrawerSize = "sm" | "md" | "lg" | "xl";
4
5
  interface DrawerProps {
5
6
  isOpen: boolean;
6
7
  onClose: () => void;
7
8
  placement?: DrawerPlacement;
8
- width?: string | number;
9
+ size?: DrawerSize;
9
10
  title?: React.ReactNode;
10
11
  children?: React.ReactNode;
11
12
  }
@@ -1,11 +1,12 @@
1
1
  import React from 'react';
2
2
 
3
3
  type DrawerPlacement = "left" | "right";
4
+ type DrawerSize = "sm" | "md" | "lg" | "xl";
4
5
  interface DrawerProps {
5
6
  isOpen: boolean;
6
7
  onClose: () => void;
7
8
  placement?: DrawerPlacement;
8
- width?: string | number;
9
+ size?: DrawerSize;
9
10
  title?: React.ReactNode;
10
11
  children?: React.ReactNode;
11
12
  }
@@ -22,7 +22,7 @@ var clsx_default = clsx;
22
22
  import { jsx, jsxs } from "react/jsx-runtime";
23
23
  var ANIMATION_DURATION_MS = 250;
24
24
  var Drawer = (props) => {
25
- const { isOpen, onClose, placement = "right", width = 320, title, children } = props;
25
+ const { isOpen, onClose, placement = "right", size = "md", title, children } = props;
26
26
  const [mounted, setMounted] = React.useState(false);
27
27
  const [visible, setVisible] = React.useState(false);
28
28
  React.useEffect(() => {
@@ -38,7 +38,6 @@ var Drawer = (props) => {
38
38
  if (typeof document === "undefined") return null;
39
39
  if (!mounted) return null;
40
40
  const stateClass = visible ? "enter" : "exit";
41
- const widthValue = typeof width === "number" ? `${width}px` : width;
42
41
  return createPortal(
43
42
  /* @__PURE__ */ jsx(
44
43
  "div",
@@ -48,8 +47,7 @@ var Drawer = (props) => {
48
47
  children: /* @__PURE__ */ jsxs(
49
48
  "div",
50
49
  {
51
- className: clsx_default("lib-xplat-drawer", placement, stateClass),
52
- style: { width: widthValue },
50
+ className: clsx_default("lib-xplat-drawer", placement, size, stateClass),
53
51
  role: "dialog",
54
52
  "aria-modal": "true",
55
53
  onClick: (e) => e.stopPropagation(),
@@ -73,6 +73,8 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
73
73
  import_react.default.useLayoutEffect(() => {
74
74
  if (!enabled) return;
75
75
  calculatePosition();
76
+ const raf = requestAnimationFrame(calculatePosition);
77
+ return () => cancelAnimationFrame(raf);
76
78
  }, [calculatePosition, enabled]);
77
79
  import_react.default.useEffect(() => {
78
80
  if (!enabled) return;
@@ -37,6 +37,8 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
37
37
  React.useLayoutEffect(() => {
38
38
  if (!enabled) return;
39
39
  calculatePosition();
40
+ const raf = requestAnimationFrame(calculatePosition);
41
+ return () => cancelAnimationFrame(raf);
40
42
  }, [calculatePosition, enabled]);
41
43
  React.useEffect(() => {
42
44
  if (!enabled) return;
@@ -73,6 +73,8 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
73
73
  import_react.default.useLayoutEffect(() => {
74
74
  if (!enabled) return;
75
75
  calculatePosition();
76
+ const raf = requestAnimationFrame(calculatePosition);
77
+ return () => cancelAnimationFrame(raf);
76
78
  }, [calculatePosition, enabled]);
77
79
  import_react.default.useEffect(() => {
78
80
  if (!enabled) return;
@@ -1,7 +1,8 @@
1
1
  /* src/components/PopOver/popOver.scss */
2
2
  .lib-xplat-pop-over {
3
3
  position: relative;
4
- width: fit-content;
4
+ width: 100%;
5
+ height: 100%;
5
6
  cursor: pointer;
6
7
  user-select: none;
7
8
  }
@@ -37,6 +37,8 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
37
37
  React.useLayoutEffect(() => {
38
38
  if (!enabled) return;
39
39
  calculatePosition();
40
+ const raf = requestAnimationFrame(calculatePosition);
41
+ return () => cancelAnimationFrame(raf);
40
42
  }, [calculatePosition, enabled]);
41
43
  React.useEffect(() => {
42
44
  if (!enabled) return;
@@ -74,6 +74,8 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
74
74
  import_react.default.useLayoutEffect(() => {
75
75
  if (!enabled) return;
76
76
  calculatePosition();
77
+ const raf = requestAnimationFrame(calculatePosition);
78
+ return () => cancelAnimationFrame(raf);
77
79
  }, [calculatePosition, enabled]);
78
80
  import_react.default.useEffect(() => {
79
81
  if (!enabled) return;
@@ -37,6 +37,8 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
37
37
  React.useLayoutEffect(() => {
38
38
  if (!enabled) return;
39
39
  calculatePosition();
40
+ const raf = requestAnimationFrame(calculatePosition);
41
+ return () => cancelAnimationFrame(raf);
40
42
  }, [calculatePosition, enabled]);
41
43
  React.useEffect(() => {
42
44
  if (!enabled) return;
@@ -42,11 +42,12 @@ var clsx_default = clsx;
42
42
 
43
43
  // src/components/Skeleton/Skeleton.tsx
44
44
  var import_jsx_runtime = require("react/jsx-runtime");
45
+ var toSizeVar = (token) => token === "full" ? "100%" : `var(--spacing-size-${token})`;
45
46
  var Skeleton = (props) => {
46
47
  const { variant = "text", width, height } = props;
47
48
  const style = {
48
- width: typeof width === "number" ? `${width}px` : width,
49
- height: typeof height === "number" ? `${height}px` : height
49
+ ...width != null && { width: toSizeVar(width) },
50
+ ...height != null && { height: toSizeVar(height) }
50
51
  };
51
52
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
52
53
  "div",
@@ -1,10 +1,11 @@
1
1
  import React from 'react';
2
2
 
3
3
  type SkeletonVariant = "text" | "circular" | "rectangular";
4
+ type SizeToken = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | "full";
4
5
  interface SkeletonProps {
5
6
  variant?: SkeletonVariant;
6
- width?: string | number;
7
- height?: string | number;
7
+ width?: SizeToken;
8
+ height?: SizeToken;
8
9
  }
9
10
  declare const Skeleton: React.FC<SkeletonProps>;
10
11
 
@@ -1,10 +1,11 @@
1
1
  import React from 'react';
2
2
 
3
3
  type SkeletonVariant = "text" | "circular" | "rectangular";
4
+ type SizeToken = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | "full";
4
5
  interface SkeletonProps {
5
6
  variant?: SkeletonVariant;
6
- width?: string | number;
7
- height?: string | number;
7
+ width?: SizeToken;
8
+ height?: SizeToken;
8
9
  }
9
10
  declare const Skeleton: React.FC<SkeletonProps>;
10
11
 
@@ -16,11 +16,12 @@ var clsx_default = clsx;
16
16
 
17
17
  // src/components/Skeleton/Skeleton.tsx
18
18
  import { jsx } from "react/jsx-runtime";
19
+ var toSizeVar = (token) => token === "full" ? "100%" : `var(--spacing-size-${token})`;
19
20
  var Skeleton = (props) => {
20
21
  const { variant = "text", width, height } = props;
21
22
  const style = {
22
- width: typeof width === "number" ? `${width}px` : width,
23
- height: typeof height === "number" ? `${height}px` : height
23
+ ...width != null && { width: toSizeVar(width) },
24
+ ...height != null && { height: toSizeVar(height) }
24
25
  };
25
26
  return /* @__PURE__ */ jsx(
26
27
  "div",
@@ -2197,6 +2197,37 @@ var useChartSize = (ref) => {
2197
2197
  }, [ref]);
2198
2198
  return size;
2199
2199
  };
2200
+ var prefersReducedMotion = () => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
2201
+ var useChartAnimation = (containerRef, dataKey) => {
2202
+ const [animate, setAnimate] = import_react5.default.useState(false);
2203
+ const prevDataKey = import_react5.default.useRef(dataKey);
2204
+ const hasAnimated = import_react5.default.useRef(false);
2205
+ import_react5.default.useEffect(() => {
2206
+ if (prefersReducedMotion()) return;
2207
+ const el = containerRef.current;
2208
+ if (!el) return;
2209
+ const observer = new IntersectionObserver(
2210
+ ([entry]) => {
2211
+ if (entry.isIntersecting && !hasAnimated.current) {
2212
+ hasAnimated.current = true;
2213
+ setAnimate(true);
2214
+ }
2215
+ },
2216
+ { threshold: 0.1 }
2217
+ );
2218
+ observer.observe(el);
2219
+ return () => observer.disconnect();
2220
+ }, [containerRef]);
2221
+ import_react5.default.useEffect(() => {
2222
+ if (dataKey !== prevDataKey.current) {
2223
+ prevDataKey.current = dataKey;
2224
+ if (prefersReducedMotion()) return;
2225
+ setAnimate(false);
2226
+ requestAnimationFrame(() => setAnimate(true));
2227
+ }
2228
+ }, [dataKey]);
2229
+ return animate || prefersReducedMotion();
2230
+ };
2200
2231
  var useChartTooltip = (enabled) => {
2201
2232
  const [tooltip, setTooltip] = import_react5.default.useState({
2202
2233
  visible: false,
@@ -2262,7 +2293,7 @@ var AxisLabels = import_react5.default.memo(({ labels, count, chartW, height })
2262
2293
  }) });
2263
2294
  });
2264
2295
  AxisLabels.displayName = "AxisLabels";
2265
- var LineChart = import_react5.default.memo(({ data, labels, width, height, onHover, onMove, onLeave }) => {
2296
+ var LineChart = import_react5.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
2266
2297
  const entries = import_react5.default.useMemo(() => Object.entries(data), [data]);
2267
2298
  const maxVal = import_react5.default.useMemo(() => {
2268
2299
  const allValues = entries.flatMap(([, v]) => v);
@@ -2282,18 +2313,52 @@ var LineChart = import_react5.default.memo(({ data, labels, width, height, onHov
2282
2313
  [entries, count, chartW, chartH, maxVal]
2283
2314
  );
2284
2315
  const showPoints = count <= 100;
2316
+ const lineRefs = import_react5.default.useRef([]);
2317
+ import_react5.default.useEffect(() => {
2318
+ if (!animate) return;
2319
+ lineRefs.current.forEach((el) => {
2320
+ if (!el) return;
2321
+ const len = el.getTotalLength();
2322
+ el.style.strokeDasharray = `${len}`;
2323
+ el.style.strokeDashoffset = `${len}`;
2324
+ requestAnimationFrame(() => {
2325
+ el.style.transition = "stroke-dashoffset 1200ms ease-out 200ms";
2326
+ el.style.strokeDashoffset = "0";
2327
+ });
2328
+ });
2329
+ }, [animate, seriesPoints]);
2285
2330
  return /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
2286
2331
  /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(GridLines, { width, height, chartH, maxVal }),
2287
2332
  /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(AxisLabels, { labels, count, chartW, height }),
2288
2333
  entries.map(([key], di) => {
2289
2334
  const palette = getPalette(LINE_BAR_PALETTES, di, key);
2290
2335
  const color = palette[2];
2336
+ const areaColor = palette[0];
2291
2337
  const points = seriesPoints[di];
2338
+ const gradientId = `line-gradient-${di}`;
2339
+ const polyPoints = points.map((p) => `${p.x},${p.y}`).join(" ");
2340
+ 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`;
2292
2341
  return /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("g", { children: [
2342
+ /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
2343
+ /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
2344
+ /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
2345
+ ] }) }),
2346
+ /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
2347
+ "path",
2348
+ {
2349
+ d: areaD,
2350
+ fill: `url(#${gradientId})`,
2351
+ className: "chart-area",
2352
+ style: animate ? { animationDelay: "600ms" } : { opacity: 1 }
2353
+ }
2354
+ ),
2293
2355
  /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
2294
2356
  "polyline",
2295
2357
  {
2296
- points: points.map((p) => `${p.x},${p.y}`).join(" "),
2358
+ ref: (el) => {
2359
+ lineRefs.current[di] = el;
2360
+ },
2361
+ points: polyPoints,
2297
2362
  fill: "none",
2298
2363
  stroke: color,
2299
2364
  strokeWidth: "2"
@@ -2318,7 +2383,7 @@ var LineChart = import_react5.default.memo(({ data, labels, width, height, onHov
2318
2383
  ] });
2319
2384
  });
2320
2385
  LineChart.displayName = "LineChart";
2321
- var CurveChart = import_react5.default.memo(({ data, labels, width, height, onHover, onMove, onLeave }) => {
2386
+ var CurveChart = import_react5.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
2322
2387
  const entries = import_react5.default.useMemo(() => Object.entries(data), [data]);
2323
2388
  const maxVal = import_react5.default.useMemo(() => {
2324
2389
  const allValues = entries.flatMap(([, v]) => v);
@@ -2338,6 +2403,20 @@ var CurveChart = import_react5.default.memo(({ data, labels, width, height, onHo
2338
2403
  [entries, count, chartW, chartH, maxVal]
2339
2404
  );
2340
2405
  const showPoints = count <= 100;
2406
+ const lineRefs = import_react5.default.useRef([]);
2407
+ import_react5.default.useEffect(() => {
2408
+ if (!animate) return;
2409
+ lineRefs.current.forEach((el) => {
2410
+ if (!el) return;
2411
+ const len = el.getTotalLength();
2412
+ el.style.strokeDasharray = `${len}`;
2413
+ el.style.strokeDashoffset = `${len}`;
2414
+ requestAnimationFrame(() => {
2415
+ el.style.transition = "stroke-dashoffset 1200ms ease-out 200ms";
2416
+ el.style.strokeDashoffset = "0";
2417
+ });
2418
+ });
2419
+ }, [animate, seriesPoints]);
2341
2420
  return /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
2342
2421
  /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(GridLines, { width, height, chartH, maxVal }),
2343
2422
  /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(AxisLabels, { labels, count, chartW, height }),
@@ -2354,8 +2433,27 @@ var CurveChart = import_react5.default.memo(({ data, labels, width, height, onHo
2354
2433
  /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
2355
2434
  /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
2356
2435
  ] }) }),
2357
- /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("path", { d: areaPath, fill: `url(#${gradientId})` }),
2358
- /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("path", { d: linePath, fill: "none", stroke: color, strokeWidth: "2" }),
2436
+ /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
2437
+ "path",
2438
+ {
2439
+ d: areaPath,
2440
+ fill: `url(#${gradientId})`,
2441
+ className: "chart-area",
2442
+ style: animate ? { animationDelay: "600ms" } : { opacity: 1 }
2443
+ }
2444
+ ),
2445
+ /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
2446
+ "path",
2447
+ {
2448
+ ref: (el) => {
2449
+ lineRefs.current[di] = el;
2450
+ },
2451
+ d: linePath,
2452
+ fill: "none",
2453
+ stroke: color,
2454
+ strokeWidth: "2"
2455
+ }
2456
+ ),
2359
2457
  showPoints && points.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
2360
2458
  "circle",
2361
2459
  {
@@ -2375,7 +2473,7 @@ var CurveChart = import_react5.default.memo(({ data, labels, width, height, onHo
2375
2473
  ] });
2376
2474
  });
2377
2475
  CurveChart.displayName = "CurveChart";
2378
- var BarChart = import_react5.default.memo(({ data, labels, width, height, onHover, onMove, onLeave }) => {
2476
+ var BarChart = import_react5.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
2379
2477
  const entries = import_react5.default.useMemo(() => Object.entries(data), [data]);
2380
2478
  const maxVal = import_react5.default.useMemo(() => {
2381
2479
  const allValues = entries.flatMap(([, v]) => v);
@@ -2388,6 +2486,7 @@ var BarChart = import_react5.default.memo(({ data, labels, width, height, onHove
2388
2486
  const groupW = chartW / count;
2389
2487
  const barGap = groupCount > 1 ? 2 : 0;
2390
2488
  const barW = Math.max(1, Math.min(32, (groupW * 0.7 - barGap * (groupCount - 1)) / groupCount));
2489
+ const baseline = PADDING.top + chartH;
2391
2490
  const bars = import_react5.default.useMemo(
2392
2491
  () => entries.map(
2393
2492
  ([, values], di) => values.map((v, i) => {
@@ -2413,12 +2512,17 @@ var BarChart = import_react5.default.memo(({ data, labels, width, height, onHove
2413
2512
  return bars[di].map((b, i) => {
2414
2513
  const r2 = Math.min(4, b.w / 2);
2415
2514
  const d = b.h <= r2 ? `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 + r2} Q ${b.x} ${b.y} ${b.x + r2} ${b.y} H ${b.x + b.w - r2} Q ${b.x + b.w} ${b.y} ${b.x + b.w} ${b.y + r2} V ${b.y + b.h} Z`;
2515
+ const delay = 100 + i * 80;
2416
2516
  return /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
2417
2517
  "path",
2418
2518
  {
2419
2519
  d,
2420
2520
  fill: color,
2421
- className: "chart-bar",
2521
+ className: `chart-bar ${animate ? "chart-bar-animate" : ""}`,
2522
+ style: animate ? {
2523
+ transformOrigin: `${b.x + b.w / 2}px ${baseline}px`,
2524
+ animationDelay: `${delay}ms`
2525
+ } : void 0,
2422
2526
  onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${b.v}`),
2423
2527
  onMouseMove: onMove,
2424
2528
  onMouseLeave: onLeave
@@ -2431,7 +2535,7 @@ var BarChart = import_react5.default.memo(({ data, labels, width, height, onHove
2431
2535
  });
2432
2536
  BarChart.displayName = "BarChart";
2433
2537
  var PieDonutChart = import_react5.default.memo(
2434
- ({ data, labels, width, height, isDoughnut, onHover, onMove, onLeave }) => {
2538
+ ({ data, labels, width, height, animate, isDoughnut, onHover, onMove, onLeave }) => {
2435
2539
  const entries = import_react5.default.useMemo(() => Object.entries(data), [data]);
2436
2540
  const values = import_react5.default.useMemo(() => entries.flatMap(([, v]) => v), [entries]);
2437
2541
  const total = import_react5.default.useMemo(() => values.reduce((a, b) => a + b, 0) || 1, [values]);
@@ -2471,20 +2575,34 @@ var PieDonutChart = import_react5.default.memo(
2471
2575
  return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}` };
2472
2576
  });
2473
2577
  }, [values, total, cx, cy, r2, innerR, labels]);
2474
- return /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: sliceData.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("g", { children: [
2475
- /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(
2476
- "path",
2477
- {
2478
- d: s.d,
2479
- fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
2480
- className: "chart-slice",
2481
- onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
2482
- onMouseMove: onMove,
2483
- onMouseLeave: onLeave
2484
- }
2485
- ),
2486
- s.angle > 0.2 && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)("text", { x: s.lx, y: s.ly, className: "chart-pie-label", textAnchor: "middle", dominantBaseline: "central", children: s.v })
2487
- ] }, i)) });
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
+ }) });
2488
2606
  }
2489
2607
  );
2490
2608
  PieDonutChart.displayName = "PieDonutChart";
@@ -2518,13 +2636,15 @@ var Chart = import_react5.default.memo((props) => {
2518
2636
  const { width, height } = useChartSize(containerRef);
2519
2637
  const stableData = import_react5.default.useMemo(() => data, [JSON.stringify(data)]);
2520
2638
  const stableLabels = import_react5.default.useMemo(() => labels, [JSON.stringify(labels)]);
2639
+ const dataKey = import_react5.default.useMemo(() => JSON.stringify(labels), [labels]);
2640
+ const animate = useChartAnimation(containerRef, dataKey);
2521
2641
  const ready = width > 0 && height > 0;
2522
2642
  return /* @__PURE__ */ (0, import_jsx_runtime305.jsxs)("div", { className: "lib-xplat-chart", ref: containerRef, children: [
2523
- ready && type === "line" && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(LineChart, { data: stableData, labels: stableLabels, width, height, onHover: show, onMove: move, onLeave: hide }),
2524
- ready && type === "curve" && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(CurveChart, { data: stableData, labels: stableLabels, width, height, onHover: show, onMove: move, onLeave: hide }),
2525
- ready && type === "bar" && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(BarChart, { data: stableData, labels: stableLabels, width, height, onHover: show, onMove: move, onLeave: hide }),
2526
- ready && type === "pie" && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, onHover: show, onMove: move, onLeave: hide }),
2527
- ready && type === "doughnut" && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
2643
+ ready && type === "line" && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(LineChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
2644
+ ready && type === "curve" && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(CurveChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
2645
+ ready && type === "bar" && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
2646
+ ready && type === "pie" && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
2647
+ ready && type === "doughnut" && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
2528
2648
  tooltip.visible && /* @__PURE__ */ (0, import_jsx_runtime305.jsx)(TooltipBubble, { x: tooltip.x, y: tooltip.y, containerWidth: width, children: tooltip.content })
2529
2649
  ] });
2530
2650
  });
@@ -3336,7 +3456,7 @@ var import_react_dom3 = require("react-dom");
3336
3456
  var import_jsx_runtime320 = require("react/jsx-runtime");
3337
3457
  var ANIMATION_DURATION_MS2 = 250;
3338
3458
  var Drawer = (props) => {
3339
- const { isOpen, onClose, placement = "right", width = 320, title, children } = props;
3459
+ const { isOpen, onClose, placement = "right", size = "md", title, children } = props;
3340
3460
  const [mounted, setMounted] = import_react17.default.useState(false);
3341
3461
  const [visible, setVisible] = import_react17.default.useState(false);
3342
3462
  import_react17.default.useEffect(() => {
@@ -3352,7 +3472,6 @@ var Drawer = (props) => {
3352
3472
  if (typeof document === "undefined") return null;
3353
3473
  if (!mounted) return null;
3354
3474
  const stateClass = visible ? "enter" : "exit";
3355
- const widthValue = typeof width === "number" ? `${width}px` : width;
3356
3475
  return (0, import_react_dom3.createPortal)(
3357
3476
  /* @__PURE__ */ (0, import_jsx_runtime320.jsx)(
3358
3477
  "div",
@@ -3362,8 +3481,7 @@ var Drawer = (props) => {
3362
3481
  children: /* @__PURE__ */ (0, import_jsx_runtime320.jsxs)(
3363
3482
  "div",
3364
3483
  {
3365
- className: clsx_default("lib-xplat-drawer", placement, stateClass),
3366
- style: { width: widthValue },
3484
+ className: clsx_default("lib-xplat-drawer", placement, size, stateClass),
3367
3485
  role: "dialog",
3368
3486
  "aria-modal": "true",
3369
3487
  onClick: (e) => e.stopPropagation(),
@@ -3423,6 +3541,8 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
3423
3541
  import_react18.default.useLayoutEffect(() => {
3424
3542
  if (!enabled) return;
3425
3543
  calculatePosition();
3544
+ const raf = requestAnimationFrame(calculatePosition);
3545
+ return () => cancelAnimationFrame(raf);
3426
3546
  }, [calculatePosition, enabled]);
3427
3547
  import_react18.default.useEffect(() => {
3428
3548
  if (!enabled) return;
@@ -4239,11 +4359,12 @@ var Select_default = Select;
4239
4359
 
4240
4360
  // src/components/Skeleton/Skeleton.tsx
4241
4361
  var import_jsx_runtime333 = require("react/jsx-runtime");
4362
+ var toSizeVar = (token) => token === "full" ? "100%" : `var(--spacing-size-${token})`;
4242
4363
  var Skeleton = (props) => {
4243
4364
  const { variant = "text", width, height } = props;
4244
4365
  const style = {
4245
- width: typeof width === "number" ? `${width}px` : width,
4246
- height: typeof height === "number" ? `${height}px` : height
4366
+ ...width != null && { width: toSizeVar(width) },
4367
+ ...height != null && { height: toSizeVar(height) }
4247
4368
  };
4248
4369
  return /* @__PURE__ */ (0, import_jsx_runtime333.jsx)(
4249
4370
  "div",