@x-plat/design-system 0.5.31 → 0.5.32

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.cjs CHANGED
@@ -6815,6 +6815,40 @@ var AxisLabels = import_react6.default.memo(({ labels, count, chartW, height })
6815
6815
  }) });
6816
6816
  });
6817
6817
  AxisLabels.displayName = "AxisLabels";
6818
+ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
6819
+ const [activeIndex, setActiveIndex] = import_react6.default.useState(null);
6820
+ const [mouseX, setMouseX] = import_react6.default.useState(null);
6821
+ const handleMouseMove = import_react6.default.useCallback((e) => {
6822
+ const svg = e.currentTarget;
6823
+ const rect = svg.getBoundingClientRect();
6824
+ const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
6825
+ setMouseX(mx);
6826
+ if (seriesPoints.length === 0 || seriesPoints[0].length === 0) return;
6827
+ const points = seriesPoints[0];
6828
+ let closest = 0;
6829
+ let minDist = Math.abs(points[0].x - mx);
6830
+ for (let i = 1; i < points.length; i++) {
6831
+ const dist = Math.abs(points[i].x - mx);
6832
+ if (dist < minDist) {
6833
+ minDist = dist;
6834
+ closest = i;
6835
+ }
6836
+ }
6837
+ setActiveIndex(closest);
6838
+ }, [seriesPoints]);
6839
+ const handleMouseLeave = import_react6.default.useCallback(() => {
6840
+ setActiveIndex(null);
6841
+ setMouseX(null);
6842
+ }, []);
6843
+ const tooltipContent = import_react6.default.useMemo(() => {
6844
+ if (activeIndex === null) return "";
6845
+ return entries.map(([key], di) => {
6846
+ const p = seriesPoints[di]?.[activeIndex];
6847
+ return p ? `${key}: ${p.v}` : "";
6848
+ }).filter(Boolean).join(" / ");
6849
+ }, [activeIndex, entries, seriesPoints]);
6850
+ return { activeIndex, mouseX, handleMouseMove, handleMouseLeave, tooltipContent };
6851
+ };
6818
6852
  var LineChart = import_react6.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
6819
6853
  const entries = import_react6.default.useMemo(() => Object.entries(data), [data]);
6820
6854
  const maxVal = import_react6.default.useMemo(() => {
@@ -6834,8 +6868,9 @@ var LineChart = import_react6.default.memo(({ data, labels, width, height, anima
6834
6868
  ),
6835
6869
  [entries, count, chartW, chartH, maxVal]
6836
6870
  );
6837
- const showPoints = count <= 100;
6838
6871
  const lineRefs = import_react6.default.useRef([]);
6872
+ const clipRef = import_react6.default.useRef(null);
6873
+ const { activeIndex, mouseX, handleMouseMove, handleMouseLeave, tooltipContent } = useCrosshair(seriesPoints, entries, labels, chartH);
6839
6874
  import_react6.default.useEffect(() => {
6840
6875
  if (!animate) return;
6841
6876
  lineRefs.current.forEach((el) => {
@@ -6848,61 +6883,110 @@ var LineChart = import_react6.default.memo(({ data, labels, width, height, anima
6848
6883
  el.style.strokeDashoffset = "0";
6849
6884
  });
6850
6885
  });
6851
- }, [animate, seriesPoints]);
6852
- return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
6853
- /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(GridLines, { width, height, chartH, maxVal }),
6854
- /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(AxisLabels, { labels, count, chartW, height }),
6855
- entries.map(([key], di) => {
6856
- const palette = getPalette(LINE_BAR_PALETTES, di, key);
6857
- const color = palette[2];
6858
- const areaColor = palette[0];
6859
- const points = seriesPoints[di];
6860
- const gradientId = `line-gradient-${di}`;
6861
- const polyPoints = points.map((p) => `${p.x},${p.y}`).join(" ");
6862
- 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`;
6863
- return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("g", { children: [
6864
- /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
6865
- /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
6866
- /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
6867
- ] }) }),
6868
- /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6869
- "path",
6886
+ if (clipRef.current) {
6887
+ clipRef.current.setAttribute("width", "0");
6888
+ requestAnimationFrame(() => {
6889
+ if (clipRef.current) {
6890
+ clipRef.current.style.transition = "width 1200ms ease-out 200ms";
6891
+ clipRef.current.setAttribute("width", `${width}`);
6892
+ }
6893
+ });
6894
+ }
6895
+ }, [animate, seriesPoints, width]);
6896
+ const guideX = mouseX != null && mouseX >= PADDING.left && mouseX <= width - PADDING.right ? mouseX : null;
6897
+ const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x : null;
6898
+ const lineClipId = "line-area-clip";
6899
+ return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)(
6900
+ "svg",
6901
+ {
6902
+ viewBox: `0 0 ${width} ${height}`,
6903
+ className: "chart-svg",
6904
+ onMouseMove: (e) => {
6905
+ handleMouseMove(e);
6906
+ onMove(e);
6907
+ },
6908
+ onMouseLeave: () => {
6909
+ handleMouseLeave();
6910
+ onLeave();
6911
+ },
6912
+ children: [
6913
+ animate && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("clipPath", { id: lineClipId, children: /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("rect", { ref: clipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
6914
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(GridLines, { width, height, chartH, maxVal }),
6915
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(AxisLabels, { labels, count, chartW, height }),
6916
+ entries.map(([key], di) => {
6917
+ const palette = getPalette(LINE_BAR_PALETTES, di, key);
6918
+ const color = palette[2];
6919
+ const areaColor = palette[0];
6920
+ const points = seriesPoints[di];
6921
+ const gradientId = `line-gradient-${di}`;
6922
+ const polyPoints = points.map((p) => `${p.x},${p.y}`).join(" ");
6923
+ 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`;
6924
+ return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("g", { children: [
6925
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
6926
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
6927
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
6928
+ ] }) }),
6929
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6930
+ "path",
6931
+ {
6932
+ d: areaD,
6933
+ fill: `url(#${gradientId})`,
6934
+ clipPath: animate ? `url(#${lineClipId})` : void 0
6935
+ }
6936
+ ),
6937
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6938
+ "polyline",
6939
+ {
6940
+ ref: (el) => {
6941
+ lineRefs.current[di] = el;
6942
+ },
6943
+ points: polyPoints,
6944
+ fill: "none",
6945
+ stroke: color,
6946
+ strokeWidth: "2"
6947
+ }
6948
+ ),
6949
+ activeIndex !== null && points[activeIndex] && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6950
+ "circle",
6951
+ {
6952
+ cx: points[activeIndex].x,
6953
+ cy: points[activeIndex].y,
6954
+ r: "5",
6955
+ fill: color,
6956
+ className: "chart-point-active"
6957
+ }
6958
+ )
6959
+ ] }, di);
6960
+ }),
6961
+ guideX !== null && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6962
+ "line",
6870
6963
  {
6871
- d: areaD,
6872
- fill: `url(#${gradientId})`,
6873
- className: "chart-area",
6874
- style: animate ? { animationDelay: "600ms" } : { opacity: 1 }
6964
+ x1: guideX,
6965
+ y1: PADDING.top,
6966
+ x2: guideX,
6967
+ y2: PADDING.top + chartH,
6968
+ className: "chart-crosshair"
6875
6969
  }
6876
6970
  ),
6971
+ 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: [
6972
+ labels[activeIndex],
6973
+ " \u2014 ",
6974
+ tooltipContent
6975
+ ] }) }),
6877
6976
  /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6878
- "polyline",
6977
+ "rect",
6879
6978
  {
6880
- ref: (el) => {
6881
- lineRefs.current[di] = el;
6882
- },
6883
- points: polyPoints,
6884
- fill: "none",
6885
- stroke: color,
6886
- strokeWidth: "2"
6979
+ x: PADDING.left,
6980
+ y: PADDING.top,
6981
+ width: chartW,
6982
+ height: chartH,
6983
+ fill: "transparent",
6984
+ style: { cursor: "crosshair" }
6887
6985
  }
6888
- ),
6889
- showPoints && points.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6890
- "circle",
6891
- {
6892
- cx: p.x,
6893
- cy: p.y,
6894
- r: "4",
6895
- fill: color,
6896
- className: "chart-point",
6897
- onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${p.v}`),
6898
- onMouseMove: onMove,
6899
- onMouseLeave: onLeave
6900
- },
6901
- i
6902
- ))
6903
- ] }, di);
6904
- })
6905
- ] });
6986
+ )
6987
+ ]
6988
+ }
6989
+ );
6906
6990
  });
6907
6991
  LineChart.displayName = "LineChart";
6908
6992
  var CurveChart = import_react6.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
@@ -6924,8 +7008,9 @@ var CurveChart = import_react6.default.memo(({ data, labels, width, height, anim
6924
7008
  ),
6925
7009
  [entries, count, chartW, chartH, maxVal]
6926
7010
  );
6927
- const showPoints = count <= 100;
6928
7011
  const lineRefs = import_react6.default.useRef([]);
7012
+ const curveClipRef = import_react6.default.useRef(null);
7013
+ const { activeIndex, mouseX, handleMouseMove, handleMouseLeave, tooltipContent } = useCrosshair(seriesPoints, entries, labels, chartH);
6929
7014
  import_react6.default.useEffect(() => {
6930
7015
  if (!animate) return;
6931
7016
  lineRefs.current.forEach((el) => {
@@ -6938,61 +7023,110 @@ var CurveChart = import_react6.default.memo(({ data, labels, width, height, anim
6938
7023
  el.style.strokeDashoffset = "0";
6939
7024
  });
6940
7025
  });
6941
- }, [animate, seriesPoints]);
6942
- return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
6943
- /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(GridLines, { width, height, chartH, maxVal }),
6944
- /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(AxisLabels, { labels, count, chartW, height }),
6945
- entries.map(([key], di) => {
6946
- const palette = getPalette(LINE_BAR_PALETTES, di, key);
6947
- const color = palette[2];
6948
- const areaColor = palette[0];
6949
- const points = seriesPoints[di];
6950
- const gradientId = `curve-gradient-${di}`;
6951
- const linePath = toSmoothPath(points);
6952
- const areaPath = linePath + ` L ${points[points.length - 1].x} ${PADDING.top + chartH} L ${points[0].x} ${PADDING.top + chartH} Z`;
6953
- return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("g", { children: [
6954
- /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
6955
- /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
6956
- /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
6957
- ] }) }),
6958
- /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6959
- "path",
7026
+ if (curveClipRef.current) {
7027
+ curveClipRef.current.setAttribute("width", "0");
7028
+ requestAnimationFrame(() => {
7029
+ if (curveClipRef.current) {
7030
+ curveClipRef.current.style.transition = "width 1200ms ease-out 200ms";
7031
+ curveClipRef.current.setAttribute("width", `${width}`);
7032
+ }
7033
+ });
7034
+ }
7035
+ }, [animate, seriesPoints, width]);
7036
+ const guideX = mouseX != null && mouseX >= PADDING.left && mouseX <= width - PADDING.right ? mouseX : null;
7037
+ const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x : null;
7038
+ const curveClipId = "curve-area-clip";
7039
+ return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)(
7040
+ "svg",
7041
+ {
7042
+ viewBox: `0 0 ${width} ${height}`,
7043
+ className: "chart-svg",
7044
+ onMouseMove: (e) => {
7045
+ handleMouseMove(e);
7046
+ onMove(e);
7047
+ },
7048
+ onMouseLeave: () => {
7049
+ handleMouseLeave();
7050
+ onLeave();
7051
+ },
7052
+ children: [
7053
+ animate && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("clipPath", { id: curveClipId, children: /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("rect", { ref: curveClipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
7054
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(GridLines, { width, height, chartH, maxVal }),
7055
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(AxisLabels, { labels, count, chartW, height }),
7056
+ entries.map(([key], di) => {
7057
+ const palette = getPalette(LINE_BAR_PALETTES, di, key);
7058
+ const color = palette[2];
7059
+ const areaColor = palette[0];
7060
+ const points = seriesPoints[di];
7061
+ const gradientId = `curve-gradient-${di}`;
7062
+ const linePath = toSmoothPath(points);
7063
+ const areaPath = linePath + ` L ${points[points.length - 1].x} ${PADDING.top + chartH} L ${points[0].x} ${PADDING.top + chartH} Z`;
7064
+ return /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("g", { children: [
7065
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime307.jsxs)("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
7066
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
7067
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
7068
+ ] }) }),
7069
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
7070
+ "path",
7071
+ {
7072
+ d: areaPath,
7073
+ fill: `url(#${gradientId})`,
7074
+ clipPath: animate ? `url(#${curveClipId})` : void 0
7075
+ }
7076
+ ),
7077
+ /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
7078
+ "path",
7079
+ {
7080
+ ref: (el) => {
7081
+ lineRefs.current[di] = el;
7082
+ },
7083
+ d: linePath,
7084
+ fill: "none",
7085
+ stroke: color,
7086
+ strokeWidth: "2"
7087
+ }
7088
+ ),
7089
+ activeIndex !== null && points[activeIndex] && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
7090
+ "circle",
7091
+ {
7092
+ cx: points[activeIndex].x,
7093
+ cy: points[activeIndex].y,
7094
+ r: "5",
7095
+ fill: color,
7096
+ className: "chart-point-active"
7097
+ }
7098
+ )
7099
+ ] }, di);
7100
+ }),
7101
+ guideX !== null && /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
7102
+ "line",
6960
7103
  {
6961
- d: areaPath,
6962
- fill: `url(#${gradientId})`,
6963
- className: "chart-area",
6964
- style: animate ? { animationDelay: "600ms" } : { opacity: 1 }
7104
+ x1: guideX,
7105
+ y1: PADDING.top,
7106
+ x2: guideX,
7107
+ y2: PADDING.top + chartH,
7108
+ className: "chart-crosshair"
6965
7109
  }
6966
7110
  ),
7111
+ 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: [
7112
+ labels[activeIndex],
7113
+ " \u2014 ",
7114
+ tooltipContent
7115
+ ] }) }),
6967
7116
  /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6968
- "path",
7117
+ "rect",
6969
7118
  {
6970
- ref: (el) => {
6971
- lineRefs.current[di] = el;
6972
- },
6973
- d: linePath,
6974
- fill: "none",
6975
- stroke: color,
6976
- strokeWidth: "2"
7119
+ x: PADDING.left,
7120
+ y: PADDING.top,
7121
+ width: chartW,
7122
+ height: chartH,
7123
+ fill: "transparent",
7124
+ style: { cursor: "crosshair" }
6977
7125
  }
6978
- ),
6979
- showPoints && points.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime307.jsx)(
6980
- "circle",
6981
- {
6982
- cx: p.x,
6983
- cy: p.y,
6984
- r: "4",
6985
- fill: color,
6986
- className: "chart-point",
6987
- onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${p.v}`),
6988
- onMouseMove: onMove,
6989
- onMouseLeave: onLeave
6990
- },
6991
- i
6992
- ))
6993
- ] }, di);
6994
- })
6995
- ] });
7126
+ )
7127
+ ]
7128
+ }
7129
+ );
6996
7130
  });
6997
7131
  CurveChart.displayName = "CurveChart";
6998
7132
  var BarChart = import_react6.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
package/dist/index.css CHANGED
@@ -1999,6 +1999,24 @@
1999
1999
  .lib-xplat-chart .chart-area {
2000
2000
  opacity: 1;
2001
2001
  }
2002
+ .lib-xplat-chart .chart-crosshair {
2003
+ stroke: var(--semantic-border-subtle);
2004
+ stroke-width: 1;
2005
+ stroke-dasharray: 4 2;
2006
+ pointer-events: none;
2007
+ }
2008
+ .lib-xplat-chart .chart-point-active {
2009
+ pointer-events: none;
2010
+ transition: cx 0.05s, cy 0.05s;
2011
+ }
2012
+ .lib-xplat-chart .chart-crosshair-label {
2013
+ font-size: 11px;
2014
+ font-weight: 500;
2015
+ color: var(--semantic-text-strong);
2016
+ text-align: center;
2017
+ white-space: nowrap;
2018
+ overflow: visible;
2019
+ }
2002
2020
  .lib-xplat-chart .chart-tooltip {
2003
2021
  position: absolute;
2004
2022
  transform: translate(-50%, -100%);
@@ -3844,6 +3862,10 @@
3844
3862
  height: 100%;
3845
3863
  position: relative;
3846
3864
  overflow: auto;
3865
+ scrollbar-width: none;
3866
+ }
3867
+ .lib-xplat-table-wrapper::-webkit-scrollbar {
3868
+ display: none;
3847
3869
  }
3848
3870
  .lib-xplat-table-wrapper.sm > .lib-xplat-table > thead > tr > th,
3849
3871
  .lib-xplat-table-wrapper.sm > .lib-xplat-table > thead > tr > td,