@x-plat/design-system 0.5.18 → 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.
package/dist/index.css CHANGED
@@ -1826,6 +1826,7 @@
1826
1826
  font-weight: 600;
1827
1827
  fill: var(--semantic-text-inverse);
1828
1828
  pointer-events: none;
1829
+ opacity: 1;
1829
1830
  }
1830
1831
  .lib-xplat-chart .chart-pie {
1831
1832
  max-width: 300px;
@@ -1835,10 +1836,10 @@
1835
1836
  cursor: pointer;
1836
1837
  r: 0;
1837
1838
  opacity: 0;
1838
- transition: r 0.15s, opacity 0.15s;
1839
+ transition: r 0.15s ease-out, opacity 0.15s ease-out;
1839
1840
  }
1840
1841
  .lib-xplat-chart .chart-point:hover {
1841
- r: 5;
1842
+ r: 6;
1842
1843
  opacity: 1;
1843
1844
  }
1844
1845
  .lib-xplat-chart .chart-svg:hover .chart-point {
@@ -1847,7 +1848,7 @@
1847
1848
  }
1848
1849
  .lib-xplat-chart .chart-bar {
1849
1850
  cursor: pointer;
1850
- transition: opacity 0.15s, filter 0.15s;
1851
+ transition: opacity 0.15s ease-out, filter 0.15s ease-out;
1851
1852
  }
1852
1853
  .lib-xplat-chart .chart-bar:hover {
1853
1854
  opacity: 0.85;
@@ -1858,15 +1859,22 @@
1858
1859
  stroke: var(--semantic-surface-neutral-default);
1859
1860
  stroke-width: 2;
1860
1861
  transition:
1861
- opacity 0.15s,
1862
- filter 0.15s,
1863
- transform 0.15s;
1862
+ opacity 0.15s ease-out,
1863
+ filter 0.15s ease-out,
1864
+ transform 0.15s ease-out;
1864
1865
  transform-origin: center;
1865
1866
  }
1867
+ .lib-xplat-chart .chart-svg:hover .chart-slice {
1868
+ opacity: 0.5;
1869
+ }
1866
1870
  .lib-xplat-chart .chart-slice:hover {
1867
- opacity: 0.9;
1871
+ opacity: 1 !important;
1872
+ transform: scale(1.03);
1868
1873
  filter: brightness(1.05) drop-shadow(0 2px 6px rgba(0, 0, 0, 0.2));
1869
1874
  }
1875
+ .lib-xplat-chart .chart-area {
1876
+ opacity: 1;
1877
+ }
1870
1878
  .lib-xplat-chart .chart-tooltip {
1871
1879
  position: absolute;
1872
1880
  transform: translate(-50%, -100%);
@@ -1879,6 +1887,65 @@
1879
1887
  white-space: nowrap;
1880
1888
  pointer-events: none;
1881
1889
  z-index: 10;
1890
+ animation: chart-tooltip-in 150ms ease-out;
1891
+ }
1892
+ .lib-xplat-chart .chart-bar-animate {
1893
+ animation: chart-bar-grow 800ms ease-out both;
1894
+ }
1895
+ .lib-xplat-chart .chart-slice-group-animate {
1896
+ animation: chart-slice-in 1000ms ease-out both;
1897
+ }
1898
+ .lib-xplat-chart .chart-pie-label-animate {
1899
+ animation: chart-fade-in 150ms ease-out both;
1900
+ }
1901
+ .lib-xplat-chart .chart-area[style*=animationDelay] {
1902
+ animation: chart-fade-in 800ms ease-out both;
1903
+ }
1904
+ @keyframes chart-bar-grow {
1905
+ from {
1906
+ transform: scaleY(0);
1907
+ }
1908
+ to {
1909
+ transform: scaleY(1);
1910
+ }
1911
+ }
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
+ @keyframes chart-fade-in {
1923
+ from {
1924
+ opacity: 0;
1925
+ }
1926
+ to {
1927
+ opacity: 1;
1928
+ }
1929
+ }
1930
+ @keyframes chart-tooltip-in {
1931
+ from {
1932
+ opacity: 0;
1933
+ transform: translate(-50%, -90%);
1934
+ }
1935
+ to {
1936
+ opacity: 1;
1937
+ transform: translate(-50%, -100%);
1938
+ }
1939
+ }
1940
+ @media (prefers-reduced-motion: reduce) {
1941
+ .lib-xplat-chart .chart-bar-animate,
1942
+ .lib-xplat-chart .chart-slice-group-animate,
1943
+ .lib-xplat-chart .chart-pie-label-animate,
1944
+ .lib-xplat-chart .chart-area {
1945
+ animation: none !important;
1946
+ opacity: 1 !important;
1947
+ transform: none !important;
1948
+ }
1882
1949
  }
1883
1950
 
1884
1951
  /* src/components/CheckBox/checkbox.scss */
@@ -2946,6 +3013,8 @@
2946
3013
  /* src/components/PopOver/popOver.scss */
2947
3014
  .lib-xplat-pop-over {
2948
3015
  position: relative;
3016
+ width: 100%;
3017
+ height: 100%;
2949
3018
  cursor: pointer;
2950
3019
  user-select: none;
2951
3020
  }
package/dist/index.js CHANGED
@@ -6211,6 +6211,37 @@ var useChartSize = (ref) => {
6211
6211
  }, [ref]);
6212
6212
  return size;
6213
6213
  };
6214
+ var prefersReducedMotion = () => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
6215
+ var useChartAnimation = (containerRef, dataKey) => {
6216
+ const [animate, setAnimate] = React5.useState(false);
6217
+ const prevDataKey = React5.useRef(dataKey);
6218
+ const hasAnimated = React5.useRef(false);
6219
+ React5.useEffect(() => {
6220
+ if (prefersReducedMotion()) return;
6221
+ const el = containerRef.current;
6222
+ if (!el) return;
6223
+ const observer = new IntersectionObserver(
6224
+ ([entry]) => {
6225
+ if (entry.isIntersecting && !hasAnimated.current) {
6226
+ hasAnimated.current = true;
6227
+ setAnimate(true);
6228
+ }
6229
+ },
6230
+ { threshold: 0.1 }
6231
+ );
6232
+ observer.observe(el);
6233
+ return () => observer.disconnect();
6234
+ }, [containerRef]);
6235
+ React5.useEffect(() => {
6236
+ if (dataKey !== prevDataKey.current) {
6237
+ prevDataKey.current = dataKey;
6238
+ if (prefersReducedMotion()) return;
6239
+ setAnimate(false);
6240
+ requestAnimationFrame(() => setAnimate(true));
6241
+ }
6242
+ }, [dataKey]);
6243
+ return animate || prefersReducedMotion();
6244
+ };
6214
6245
  var useChartTooltip = (enabled) => {
6215
6246
  const [tooltip, setTooltip] = React5.useState({
6216
6247
  visible: false,
@@ -6276,7 +6307,7 @@ var AxisLabels = React5.memo(({ labels, count, chartW, height }) => {
6276
6307
  }) });
6277
6308
  });
6278
6309
  AxisLabels.displayName = "AxisLabels";
6279
- var LineChart = React5.memo(({ data, labels, width, height, onHover, onMove, onLeave }) => {
6310
+ var LineChart = React5.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
6280
6311
  const entries = React5.useMemo(() => Object.entries(data), [data]);
6281
6312
  const maxVal = React5.useMemo(() => {
6282
6313
  const allValues = entries.flatMap(([, v]) => v);
@@ -6296,18 +6327,52 @@ var LineChart = React5.memo(({ data, labels, width, height, onHover, onMove, onL
6296
6327
  [entries, count, chartW, chartH, maxVal]
6297
6328
  );
6298
6329
  const showPoints = count <= 100;
6330
+ const lineRefs = React5.useRef([]);
6331
+ React5.useEffect(() => {
6332
+ if (!animate) return;
6333
+ lineRefs.current.forEach((el) => {
6334
+ if (!el) return;
6335
+ const len = el.getTotalLength();
6336
+ el.style.strokeDasharray = `${len}`;
6337
+ el.style.strokeDashoffset = `${len}`;
6338
+ requestAnimationFrame(() => {
6339
+ el.style.transition = "stroke-dashoffset 1200ms ease-out 200ms";
6340
+ el.style.strokeDashoffset = "0";
6341
+ });
6342
+ });
6343
+ }, [animate, seriesPoints]);
6299
6344
  return /* @__PURE__ */ jsxs196("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
6300
6345
  /* @__PURE__ */ jsx305(GridLines, { width, height, chartH, maxVal }),
6301
6346
  /* @__PURE__ */ jsx305(AxisLabels, { labels, count, chartW, height }),
6302
6347
  entries.map(([key], di) => {
6303
6348
  const palette = getPalette(LINE_BAR_PALETTES, di, key);
6304
6349
  const color = palette[2];
6350
+ const areaColor = palette[0];
6305
6351
  const points = seriesPoints[di];
6352
+ const gradientId = `line-gradient-${di}`;
6353
+ const polyPoints = points.map((p) => `${p.x},${p.y}`).join(" ");
6354
+ 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`;
6306
6355
  return /* @__PURE__ */ jsxs196("g", { children: [
6356
+ /* @__PURE__ */ jsx305("defs", { children: /* @__PURE__ */ jsxs196("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
6357
+ /* @__PURE__ */ jsx305("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
6358
+ /* @__PURE__ */ jsx305("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
6359
+ ] }) }),
6360
+ /* @__PURE__ */ jsx305(
6361
+ "path",
6362
+ {
6363
+ d: areaD,
6364
+ fill: `url(#${gradientId})`,
6365
+ className: "chart-area",
6366
+ style: animate ? { animationDelay: "600ms" } : { opacity: 1 }
6367
+ }
6368
+ ),
6307
6369
  /* @__PURE__ */ jsx305(
6308
6370
  "polyline",
6309
6371
  {
6310
- points: points.map((p) => `${p.x},${p.y}`).join(" "),
6372
+ ref: (el) => {
6373
+ lineRefs.current[di] = el;
6374
+ },
6375
+ points: polyPoints,
6311
6376
  fill: "none",
6312
6377
  stroke: color,
6313
6378
  strokeWidth: "2"
@@ -6332,7 +6397,7 @@ var LineChart = React5.memo(({ data, labels, width, height, onHover, onMove, onL
6332
6397
  ] });
6333
6398
  });
6334
6399
  LineChart.displayName = "LineChart";
6335
- var CurveChart = React5.memo(({ data, labels, width, height, onHover, onMove, onLeave }) => {
6400
+ var CurveChart = React5.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
6336
6401
  const entries = React5.useMemo(() => Object.entries(data), [data]);
6337
6402
  const maxVal = React5.useMemo(() => {
6338
6403
  const allValues = entries.flatMap(([, v]) => v);
@@ -6352,6 +6417,20 @@ var CurveChart = React5.memo(({ data, labels, width, height, onHover, onMove, on
6352
6417
  [entries, count, chartW, chartH, maxVal]
6353
6418
  );
6354
6419
  const showPoints = count <= 100;
6420
+ const lineRefs = React5.useRef([]);
6421
+ React5.useEffect(() => {
6422
+ if (!animate) return;
6423
+ lineRefs.current.forEach((el) => {
6424
+ if (!el) return;
6425
+ const len = el.getTotalLength();
6426
+ el.style.strokeDasharray = `${len}`;
6427
+ el.style.strokeDashoffset = `${len}`;
6428
+ requestAnimationFrame(() => {
6429
+ el.style.transition = "stroke-dashoffset 1200ms ease-out 200ms";
6430
+ el.style.strokeDashoffset = "0";
6431
+ });
6432
+ });
6433
+ }, [animate, seriesPoints]);
6355
6434
  return /* @__PURE__ */ jsxs196("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
6356
6435
  /* @__PURE__ */ jsx305(GridLines, { width, height, chartH, maxVal }),
6357
6436
  /* @__PURE__ */ jsx305(AxisLabels, { labels, count, chartW, height }),
@@ -6368,8 +6447,27 @@ var CurveChart = React5.memo(({ data, labels, width, height, onHover, onMove, on
6368
6447
  /* @__PURE__ */ jsx305("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
6369
6448
  /* @__PURE__ */ jsx305("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
6370
6449
  ] }) }),
6371
- /* @__PURE__ */ jsx305("path", { d: areaPath, fill: `url(#${gradientId})` }),
6372
- /* @__PURE__ */ jsx305("path", { d: linePath, fill: "none", stroke: color, strokeWidth: "2" }),
6450
+ /* @__PURE__ */ jsx305(
6451
+ "path",
6452
+ {
6453
+ d: areaPath,
6454
+ fill: `url(#${gradientId})`,
6455
+ className: "chart-area",
6456
+ style: animate ? { animationDelay: "600ms" } : { opacity: 1 }
6457
+ }
6458
+ ),
6459
+ /* @__PURE__ */ jsx305(
6460
+ "path",
6461
+ {
6462
+ ref: (el) => {
6463
+ lineRefs.current[di] = el;
6464
+ },
6465
+ d: linePath,
6466
+ fill: "none",
6467
+ stroke: color,
6468
+ strokeWidth: "2"
6469
+ }
6470
+ ),
6373
6471
  showPoints && points.map((p, i) => /* @__PURE__ */ jsx305(
6374
6472
  "circle",
6375
6473
  {
@@ -6389,7 +6487,7 @@ var CurveChart = React5.memo(({ data, labels, width, height, onHover, onMove, on
6389
6487
  ] });
6390
6488
  });
6391
6489
  CurveChart.displayName = "CurveChart";
6392
- var BarChart = React5.memo(({ data, labels, width, height, onHover, onMove, onLeave }) => {
6490
+ var BarChart = React5.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
6393
6491
  const entries = React5.useMemo(() => Object.entries(data), [data]);
6394
6492
  const maxVal = React5.useMemo(() => {
6395
6493
  const allValues = entries.flatMap(([, v]) => v);
@@ -6402,6 +6500,7 @@ var BarChart = React5.memo(({ data, labels, width, height, onHover, onMove, onLe
6402
6500
  const groupW = chartW / count;
6403
6501
  const barGap = groupCount > 1 ? 2 : 0;
6404
6502
  const barW = Math.max(1, Math.min(32, (groupW * 0.7 - barGap * (groupCount - 1)) / groupCount));
6503
+ const baseline = PADDING.top + chartH;
6405
6504
  const bars = React5.useMemo(
6406
6505
  () => entries.map(
6407
6506
  ([, values], di) => values.map((v, i) => {
@@ -6427,12 +6526,17 @@ var BarChart = React5.memo(({ data, labels, width, height, onHover, onMove, onLe
6427
6526
  return bars[di].map((b, i) => {
6428
6527
  const r2 = Math.min(4, b.w / 2);
6429
6528
  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`;
6529
+ const delay = 100 + i * 80;
6430
6530
  return /* @__PURE__ */ jsx305(
6431
6531
  "path",
6432
6532
  {
6433
6533
  d,
6434
6534
  fill: color,
6435
- className: "chart-bar",
6535
+ className: `chart-bar ${animate ? "chart-bar-animate" : ""}`,
6536
+ style: animate ? {
6537
+ transformOrigin: `${b.x + b.w / 2}px ${baseline}px`,
6538
+ animationDelay: `${delay}ms`
6539
+ } : void 0,
6436
6540
  onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${b.v}`),
6437
6541
  onMouseMove: onMove,
6438
6542
  onMouseLeave: onLeave
@@ -6445,7 +6549,7 @@ var BarChart = React5.memo(({ data, labels, width, height, onHover, onMove, onLe
6445
6549
  });
6446
6550
  BarChart.displayName = "BarChart";
6447
6551
  var PieDonutChart = React5.memo(
6448
- ({ data, labels, width, height, isDoughnut, onHover, onMove, onLeave }) => {
6552
+ ({ data, labels, width, height, animate, isDoughnut, onHover, onMove, onLeave }) => {
6449
6553
  const entries = React5.useMemo(() => Object.entries(data), [data]);
6450
6554
  const values = React5.useMemo(() => entries.flatMap(([, v]) => v), [entries]);
6451
6555
  const total = React5.useMemo(() => values.reduce((a, b) => a + b, 0) || 1, [values]);
@@ -6485,20 +6589,34 @@ var PieDonutChart = React5.memo(
6485
6589
  return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}` };
6486
6590
  });
6487
6591
  }, [values, total, cx, cy, r2, innerR, labels]);
6488
- return /* @__PURE__ */ jsx305("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: sliceData.map((s, i) => /* @__PURE__ */ jsxs196("g", { children: [
6489
- /* @__PURE__ */ jsx305(
6490
- "path",
6491
- {
6492
- d: s.d,
6493
- fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
6494
- className: "chart-slice",
6495
- onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
6496
- onMouseMove: onMove,
6497
- onMouseLeave: onLeave
6498
- }
6499
- ),
6500
- s.angle > 0.2 && /* @__PURE__ */ jsx305("text", { x: s.lx, y: s.ly, className: "chart-pie-label", textAnchor: "middle", dominantBaseline: "central", children: s.v })
6501
- ] }, i)) });
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
+ }) });
6502
6620
  }
6503
6621
  );
6504
6622
  PieDonutChart.displayName = "PieDonutChart";
@@ -6532,13 +6650,15 @@ var Chart = React5.memo((props) => {
6532
6650
  const { width, height } = useChartSize(containerRef);
6533
6651
  const stableData = React5.useMemo(() => data, [JSON.stringify(data)]);
6534
6652
  const stableLabels = React5.useMemo(() => labels, [JSON.stringify(labels)]);
6653
+ const dataKey = React5.useMemo(() => JSON.stringify(labels), [labels]);
6654
+ const animate = useChartAnimation(containerRef, dataKey);
6535
6655
  const ready = width > 0 && height > 0;
6536
6656
  return /* @__PURE__ */ jsxs196("div", { className: "lib-xplat-chart", ref: containerRef, children: [
6537
- ready && type === "line" && /* @__PURE__ */ jsx305(LineChart, { data: stableData, labels: stableLabels, width, height, onHover: show, onMove: move, onLeave: hide }),
6538
- ready && type === "curve" && /* @__PURE__ */ jsx305(CurveChart, { data: stableData, labels: stableLabels, width, height, onHover: show, onMove: move, onLeave: hide }),
6539
- ready && type === "bar" && /* @__PURE__ */ jsx305(BarChart, { data: stableData, labels: stableLabels, width, height, onHover: show, onMove: move, onLeave: hide }),
6540
- ready && type === "pie" && /* @__PURE__ */ jsx305(PieDonutChart, { data: stableData, labels: stableLabels, width, height, onHover: show, onMove: move, onLeave: hide }),
6541
- ready && type === "doughnut" && /* @__PURE__ */ jsx305(PieDonutChart, { data: stableData, labels: stableLabels, width, height, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
6657
+ ready && type === "line" && /* @__PURE__ */ jsx305(LineChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
6658
+ ready && type === "curve" && /* @__PURE__ */ jsx305(CurveChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
6659
+ ready && type === "bar" && /* @__PURE__ */ jsx305(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
6660
+ ready && type === "pie" && /* @__PURE__ */ jsx305(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
6661
+ ready && type === "doughnut" && /* @__PURE__ */ jsx305(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
6542
6662
  tooltip.visible && /* @__PURE__ */ jsx305(TooltipBubble, { x: tooltip.x, y: tooltip.y, containerWidth: width, children: tooltip.content })
6543
6663
  ] });
6544
6664
  });
@@ -7448,6 +7568,8 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
7448
7568
  React17.useLayoutEffect(() => {
7449
7569
  if (!enabled) return;
7450
7570
  calculatePosition();
7571
+ const raf = requestAnimationFrame(calculatePosition);
7572
+ return () => cancelAnimationFrame(raf);
7451
7573
  }, [calculatePosition, enabled]);
7452
7574
  React17.useEffect(() => {
7453
7575
  if (!enabled) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-plat/design-system",
3
- "version": "0.5.18",
3
+ "version": "0.5.19",
4
4
  "description": "XPLAT UI Design System",
5
5
  "author": "XPLAT WOONG",
6
6
  "main": "dist/index.cjs",