@x-plat/design-system 0.5.35 → 0.5.36

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.
@@ -1,22 +1,6 @@
1
1
  // src/components/Chart/Chart.tsx
2
- import React2 from "react";
3
-
4
- // src/tokens/hooks/Portal.tsx
5
2
  import React from "react";
6
- import ReactDOM from "react-dom";
7
- import { jsx } from "react/jsx-runtime";
8
- var PortalContainerContext = React.createContext(null);
9
- var Portal = ({ children }) => {
10
- const contextContainer = React.useContext(PortalContainerContext);
11
- if (typeof document === "undefined") return null;
12
- const container = contextContainer ?? document.body;
13
- return ReactDOM.createPortal(children, container);
14
- };
15
- Portal.displayName = "Portal";
16
- var Portal_default = Portal;
17
-
18
- // src/components/Chart/Chart.tsx
19
- import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
20
4
  var CATEGORICAL_COUNT = 8;
21
5
  var LINE_BAR_PALETTES = Array.from({ length: CATEGORICAL_COUNT }, (_, i) => {
22
6
  const n = i + 1;
@@ -62,11 +46,11 @@ var toSmoothPath = (points) => {
62
46
  };
63
47
  var RESIZE_SETTLE_MS = 150;
64
48
  var useChartSize = (ref) => {
65
- const [size, setSize] = React2.useState({ width: 0, height: 0 });
66
- const settleTimer = React2.useRef(0);
67
- const committedSize = React2.useRef({ width: 0, height: 0 });
68
- const initialRef = React2.useRef(true);
69
- React2.useEffect(() => {
49
+ const [size, setSize] = React.useState({ width: 0, height: 0 });
50
+ const settleTimer = React.useRef(0);
51
+ const committedSize = React.useRef({ width: 0, height: 0 });
52
+ const initialRef = React.useRef(true);
53
+ React.useEffect(() => {
70
54
  const el = ref.current;
71
55
  if (!el) return;
72
56
  const observer = new ResizeObserver((entries) => {
@@ -108,10 +92,10 @@ var useChartSize = (ref) => {
108
92
  };
109
93
  var prefersReducedMotion = () => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
110
94
  var useChartAnimation = (containerRef, dataKey) => {
111
- const [animate, setAnimate] = React2.useState(false);
112
- const prevDataKey = React2.useRef(dataKey);
113
- const hasAnimated = React2.useRef(false);
114
- React2.useEffect(() => {
95
+ const [animate, setAnimate] = React.useState(false);
96
+ const prevDataKey = React.useRef(dataKey);
97
+ const hasAnimated = React.useRef(false);
98
+ React.useEffect(() => {
115
99
  if (prefersReducedMotion()) return;
116
100
  const el = containerRef.current;
117
101
  if (!el) return;
@@ -127,7 +111,7 @@ var useChartAnimation = (containerRef, dataKey) => {
127
111
  observer.observe(el);
128
112
  return () => observer.disconnect();
129
113
  }, [containerRef]);
130
- React2.useEffect(() => {
114
+ React.useEffect(() => {
131
115
  if (dataKey !== prevDataKey.current) {
132
116
  prevDataKey.current = dataKey;
133
117
  if (prefersReducedMotion()) return;
@@ -141,39 +125,47 @@ var useChartAnimation = (containerRef, dataKey) => {
141
125
  };
142
126
  var TOOLTIP_OFFSET = 12;
143
127
  var useChartTooltip = (enabled) => {
144
- const [tooltip, setTooltip] = React2.useState({
128
+ const [tooltip, setTooltip] = React.useState({
145
129
  visible: false,
146
- clientX: 0,
147
- clientY: 0,
130
+ x: 0,
131
+ y: 0,
148
132
  content: ""
149
133
  });
150
- const containerRef = React2.useRef(null);
151
- const rafRef = React2.useRef(0);
152
- const move = React2.useCallback((e) => {
134
+ const containerRef = React.useRef(null);
135
+ const rafRef = React.useRef(0);
136
+ const move = React.useCallback((e) => {
153
137
  if (!enabled) return;
154
138
  const cx = e.clientX;
155
139
  const cy = e.clientY;
156
140
  cancelAnimationFrame(rafRef.current);
157
141
  rafRef.current = requestAnimationFrame(() => {
158
- setTooltip((prev) => ({ ...prev, clientX: cx, clientY: cy }));
142
+ const rect = containerRef.current?.getBoundingClientRect();
143
+ if (!rect) return;
144
+ setTooltip((prev) => ({ ...prev, x: cx - rect.left, y: cy - rect.top }));
159
145
  });
160
146
  }, [enabled]);
161
- const show = React2.useCallback((e, content) => {
147
+ const show = React.useCallback((e, content) => {
162
148
  if (!enabled) return;
163
- setTooltip({ visible: true, clientX: e.clientX, clientY: e.clientY, content });
149
+ const rect = containerRef.current?.getBoundingClientRect();
150
+ if (!rect) return;
151
+ setTooltip({ visible: true, x: e.clientX - rect.left, y: e.clientY - rect.top, content });
164
152
  }, [enabled]);
165
- const hide = React2.useCallback(() => {
153
+ const showAt = React.useCallback((x, y, content) => {
154
+ if (!enabled) return;
155
+ setTooltip({ visible: true, x, y, content });
156
+ }, [enabled]);
157
+ const hide = React.useCallback(() => {
166
158
  cancelAnimationFrame(rafRef.current);
167
159
  setTooltip((prev) => ({ ...prev, visible: false }));
168
160
  }, []);
169
- return { tooltip, show, hide, move, containerRef };
161
+ return { tooltip, show, showAt, hide, move, containerRef };
170
162
  };
171
- var GridLines = React2.memo(({ width, height, chartH, maxVal }) => /* @__PURE__ */ jsx2(Fragment, { children: [0, 0.25, 0.5, 0.75, 1].map((ratio) => {
163
+ var GridLines = React.memo(({ width, height, chartH, maxVal }) => /* @__PURE__ */ jsx(Fragment, { children: [0, 0.25, 0.5, 0.75, 1].map((ratio) => {
172
164
  const y = PADDING.top + (1 - ratio) * chartH;
173
165
  const val = Math.round(maxVal * ratio);
174
166
  return /* @__PURE__ */ jsxs("g", { children: [
175
- /* @__PURE__ */ jsx2("line", { x1: PADDING.left, y1: y, x2: width - PADDING.right, y2: y, className: "chart-grid" }),
176
- /* @__PURE__ */ jsx2("text", { x: PADDING.left - 8, y: y + 4, className: "chart-axis-label", textAnchor: "end", children: val })
167
+ /* @__PURE__ */ jsx("line", { x1: PADDING.left, y1: y, x2: width - PADDING.right, y2: y, className: "chart-grid" }),
168
+ /* @__PURE__ */ jsx("text", { x: PADDING.left - 8, y: y + 4, className: "chart-axis-label", textAnchor: "end", children: val })
177
169
  ] }, ratio);
178
170
  }) }));
179
171
  GridLines.displayName = "GridLines";
@@ -183,18 +175,18 @@ var getLabelStep = (count, chartW) => {
183
175
  if (count <= maxLabels) return 1;
184
176
  return Math.ceil(count / maxLabels);
185
177
  };
186
- var AxisLabels = React2.memo(({ labels, count, chartW, height }) => {
178
+ var AxisLabels = React.memo(({ labels, count, chartW, height }) => {
187
179
  const step = getLabelStep(count, chartW);
188
- return /* @__PURE__ */ jsx2(Fragment, { children: labels.map((label, i) => {
180
+ return /* @__PURE__ */ jsx(Fragment, { children: labels.map((label, i) => {
189
181
  if (i % step !== 0) return null;
190
182
  const x = PADDING.left + i / (count - 1 || 1) * chartW;
191
- return /* @__PURE__ */ jsx2("text", { x, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
183
+ return /* @__PURE__ */ jsx("text", { x, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
192
184
  }) });
193
185
  });
194
186
  AxisLabels.displayName = "AxisLabels";
195
187
  var useCrosshair = (seriesPoints, entries, labels, chartH) => {
196
- const [activeIndex, setActiveIndex] = React2.useState(null);
197
- const handleMouseMove = React2.useCallback((e) => {
188
+ const [activeIndex, setActiveIndex] = React.useState(null);
189
+ const handleMouseMove = React.useCallback((e) => {
198
190
  const svg = e.currentTarget;
199
191
  const rect = svg.getBoundingClientRect();
200
192
  const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
@@ -213,17 +205,17 @@ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
213
205
  }
214
206
  setActiveIndex(minDist <= threshold ? closest : null);
215
207
  }, [seriesPoints]);
216
- const handleMouseLeave = React2.useCallback(() => {
208
+ const handleMouseLeave = React.useCallback(() => {
217
209
  setActiveIndex(null);
218
210
  }, []);
219
- const tooltipContent = React2.useMemo(() => {
211
+ const tooltipContent = React.useMemo(() => {
220
212
  if (activeIndex === null) return "";
221
213
  return entries.map(([key], di) => {
222
214
  const p = seriesPoints[di]?.[activeIndex];
223
215
  return p ? `${key}: ${p.v}` : "";
224
216
  }).filter(Boolean).join(" / ");
225
217
  }, [activeIndex, entries, seriesPoints]);
226
- const getTooltipAt = React2.useCallback((idx) => {
218
+ const getTooltipAt = React.useCallback((idx) => {
227
219
  return entries.map(([key], di) => {
228
220
  const p = seriesPoints[di]?.[idx];
229
221
  return p ? `${key}: ${p.v}` : "";
@@ -231,16 +223,16 @@ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
231
223
  }, [entries, seriesPoints]);
232
224
  return { activeIndex, handleMouseMove, handleMouseLeave, tooltipContent, getTooltipAt };
233
225
  };
234
- var LineChart = React2.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
235
- const entries = React2.useMemo(() => Object.entries(data), [data]);
236
- const maxVal = React2.useMemo(() => {
226
+ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onShowAt, onMove, onLeave }) => {
227
+ const entries = React.useMemo(() => Object.entries(data), [data]);
228
+ const maxVal = React.useMemo(() => {
237
229
  const allValues = entries.flatMap(([, v]) => v);
238
230
  return Math.max(...allValues) * 1.2 || 1;
239
231
  }, [entries]);
240
232
  const count = labels.length;
241
233
  const chartW = width - PADDING.left - PADDING.right;
242
234
  const chartH = height - PADDING.top - PADDING.bottom;
243
- const seriesPoints = React2.useMemo(
235
+ const seriesPoints = React.useMemo(
244
236
  () => entries.map(
245
237
  ([, values]) => values.map((v, i) => ({
246
238
  x: PADDING.left + i / (count - 1 || 1) * chartW,
@@ -250,9 +242,9 @@ var LineChart = React2.memo(({ data, labels, width, height, animate, onHover, on
250
242
  ),
251
243
  [entries, count, chartW, chartH, maxVal]
252
244
  );
253
- const clipRef = React2.useRef(null);
245
+ const clipRef = React.useRef(null);
254
246
  const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
255
- React2.useEffect(() => {
247
+ React.useEffect(() => {
256
248
  if (!animate || !clipRef.current) return;
257
249
  clipRef.current.setAttribute("width", "0");
258
250
  requestAnimationFrame(() => {
@@ -271,23 +263,9 @@ var LineChart = React2.memo(({ data, labels, width, height, animate, onHover, on
271
263
  className: "chart-svg",
272
264
  onMouseMove: (e) => {
273
265
  handleMouseMove(e);
274
- const svg = e.currentTarget;
275
- const rect = svg.getBoundingClientRect();
276
- const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
277
- const points = seriesPoints[0];
278
- if (!points || points.length === 0) return;
279
- const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
280
- let closest = 0;
281
- let minDist = Math.abs(points[0].x - mx);
282
- for (let i = 1; i < points.length; i++) {
283
- const dist = Math.abs(points[i].x - mx);
284
- if (dist < minDist) {
285
- minDist = dist;
286
- closest = i;
287
- }
288
- }
289
- if (minDist <= step / 2) {
290
- onHover(e, `${labels[closest]} \u2014 ${getTooltipAt(closest)}`);
266
+ if (activeIndex !== null && seriesPoints[0]?.[activeIndex]) {
267
+ const p = seriesPoints[0][activeIndex];
268
+ onShowAt(p.x, p.y, `${labels[activeIndex]} \u2014 ${getTooltipAt(activeIndex)}`);
291
269
  } else {
292
270
  onLeave();
293
271
  }
@@ -297,9 +275,9 @@ var LineChart = React2.memo(({ data, labels, width, height, animate, onHover, on
297
275
  onLeave();
298
276
  },
299
277
  children: [
300
- animate && /* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsx2("clipPath", { id: lineClipId, children: /* @__PURE__ */ jsx2("rect", { ref: clipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
301
- /* @__PURE__ */ jsx2(GridLines, { width, height, chartH, maxVal }),
302
- /* @__PURE__ */ jsx2(AxisLabels, { labels, count, chartW, height }),
278
+ animate && /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("clipPath", { id: lineClipId, children: /* @__PURE__ */ jsx("rect", { ref: clipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
279
+ /* @__PURE__ */ jsx(GridLines, { width, height, chartH, maxVal }),
280
+ /* @__PURE__ */ jsx(AxisLabels, { labels, count, chartW, height }),
303
281
  entries.map(([key], di) => {
304
282
  const palette = getPalette(LINE_BAR_PALETTES, di, key);
305
283
  const color = palette[2];
@@ -309,15 +287,15 @@ var LineChart = React2.memo(({ data, labels, width, height, animate, onHover, on
309
287
  const polyPoints = points.map((p) => `${p.x},${p.y}`).join(" ");
310
288
  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`;
311
289
  return /* @__PURE__ */ jsxs("g", { children: [
312
- /* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
313
- /* @__PURE__ */ jsx2("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
314
- /* @__PURE__ */ jsx2("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
290
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
291
+ /* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
292
+ /* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
315
293
  ] }) }),
316
294
  /* @__PURE__ */ jsxs("g", { clipPath: animate ? `url(#${lineClipId})` : void 0, children: [
317
- /* @__PURE__ */ jsx2("path", { d: areaD, fill: `url(#${gradientId})` }),
318
- /* @__PURE__ */ jsx2("polyline", { points: polyPoints, fill: "none", stroke: color, strokeWidth: "2" })
295
+ /* @__PURE__ */ jsx("path", { d: areaD, fill: `url(#${gradientId})` }),
296
+ /* @__PURE__ */ jsx("polyline", { points: polyPoints, fill: "none", stroke: color, strokeWidth: "2" })
319
297
  ] }),
320
- activeIndex !== null && points[activeIndex] && /* @__PURE__ */ jsx2(
298
+ activeIndex !== null && points[activeIndex] && /* @__PURE__ */ jsx(
321
299
  "circle",
322
300
  {
323
301
  cx: points[activeIndex].x,
@@ -329,7 +307,7 @@ var LineChart = React2.memo(({ data, labels, width, height, animate, onHover, on
329
307
  )
330
308
  ] }, di);
331
309
  }),
332
- activeX !== null && /* @__PURE__ */ jsx2(
310
+ activeX !== null && /* @__PURE__ */ jsx(
333
311
  "line",
334
312
  {
335
313
  x1: activeX,
@@ -339,7 +317,7 @@ var LineChart = React2.memo(({ data, labels, width, height, animate, onHover, on
339
317
  className: "chart-crosshair"
340
318
  }
341
319
  ),
342
- /* @__PURE__ */ jsx2(
320
+ /* @__PURE__ */ jsx(
343
321
  "rect",
344
322
  {
345
323
  x: PADDING.left,
@@ -355,16 +333,16 @@ var LineChart = React2.memo(({ data, labels, width, height, animate, onHover, on
355
333
  );
356
334
  });
357
335
  LineChart.displayName = "LineChart";
358
- var CurveChart = React2.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
359
- const entries = React2.useMemo(() => Object.entries(data), [data]);
360
- const maxVal = React2.useMemo(() => {
336
+ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, onShowAt, onMove, onLeave }) => {
337
+ const entries = React.useMemo(() => Object.entries(data), [data]);
338
+ const maxVal = React.useMemo(() => {
361
339
  const allValues = entries.flatMap(([, v]) => v);
362
340
  return Math.max(...allValues) * 1.2 || 1;
363
341
  }, [entries]);
364
342
  const count = labels.length;
365
343
  const chartW = width - PADDING.left - PADDING.right;
366
344
  const chartH = height - PADDING.top - PADDING.bottom;
367
- const seriesPoints = React2.useMemo(
345
+ const seriesPoints = React.useMemo(
368
346
  () => entries.map(
369
347
  ([, values]) => values.map((v, i) => ({
370
348
  x: PADDING.left + i / (count - 1 || 1) * chartW,
@@ -374,9 +352,9 @@ var CurveChart = React2.memo(({ data, labels, width, height, animate, onHover, o
374
352
  ),
375
353
  [entries, count, chartW, chartH, maxVal]
376
354
  );
377
- const curveClipRef = React2.useRef(null);
355
+ const curveClipRef = React.useRef(null);
378
356
  const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
379
- React2.useEffect(() => {
357
+ React.useEffect(() => {
380
358
  if (!animate || !curveClipRef.current) return;
381
359
  curveClipRef.current.setAttribute("width", "0");
382
360
  requestAnimationFrame(() => {
@@ -395,23 +373,9 @@ var CurveChart = React2.memo(({ data, labels, width, height, animate, onHover, o
395
373
  className: "chart-svg",
396
374
  onMouseMove: (e) => {
397
375
  handleMouseMove(e);
398
- const svg = e.currentTarget;
399
- const rect = svg.getBoundingClientRect();
400
- const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
401
- const points = seriesPoints[0];
402
- if (!points || points.length === 0) return;
403
- const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
404
- let closest = 0;
405
- let minDist = Math.abs(points[0].x - mx);
406
- for (let i = 1; i < points.length; i++) {
407
- const dist = Math.abs(points[i].x - mx);
408
- if (dist < minDist) {
409
- minDist = dist;
410
- closest = i;
411
- }
412
- }
413
- if (minDist <= step / 2) {
414
- onHover(e, `${labels[closest]} \u2014 ${getTooltipAt(closest)}`);
376
+ if (activeIndex !== null && seriesPoints[0]?.[activeIndex]) {
377
+ const p = seriesPoints[0][activeIndex];
378
+ onShowAt(p.x, p.y, `${labels[activeIndex]} \u2014 ${getTooltipAt(activeIndex)}`);
415
379
  } else {
416
380
  onLeave();
417
381
  }
@@ -421,9 +385,9 @@ var CurveChart = React2.memo(({ data, labels, width, height, animate, onHover, o
421
385
  onLeave();
422
386
  },
423
387
  children: [
424
- animate && /* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsx2("clipPath", { id: curveClipId, children: /* @__PURE__ */ jsx2("rect", { ref: curveClipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
425
- /* @__PURE__ */ jsx2(GridLines, { width, height, chartH, maxVal }),
426
- /* @__PURE__ */ jsx2(AxisLabels, { labels, count, chartW, height }),
388
+ animate && /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("clipPath", { id: curveClipId, children: /* @__PURE__ */ jsx("rect", { ref: curveClipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
389
+ /* @__PURE__ */ jsx(GridLines, { width, height, chartH, maxVal }),
390
+ /* @__PURE__ */ jsx(AxisLabels, { labels, count, chartW, height }),
427
391
  entries.map(([key], di) => {
428
392
  const palette = getPalette(LINE_BAR_PALETTES, di, key);
429
393
  const color = palette[2];
@@ -433,15 +397,15 @@ var CurveChart = React2.memo(({ data, labels, width, height, animate, onHover, o
433
397
  const linePath = toSmoothPath(points);
434
398
  const areaPath = linePath + ` L ${points[points.length - 1].x} ${PADDING.top + chartH} L ${points[0].x} ${PADDING.top + chartH} Z`;
435
399
  return /* @__PURE__ */ jsxs("g", { children: [
436
- /* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
437
- /* @__PURE__ */ jsx2("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
438
- /* @__PURE__ */ jsx2("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
400
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
401
+ /* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
402
+ /* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
439
403
  ] }) }),
440
404
  /* @__PURE__ */ jsxs("g", { clipPath: animate ? `url(#${curveClipId})` : void 0, children: [
441
- /* @__PURE__ */ jsx2("path", { d: areaPath, fill: `url(#${gradientId})` }),
442
- /* @__PURE__ */ jsx2("path", { d: linePath, fill: "none", stroke: color, strokeWidth: "2" })
405
+ /* @__PURE__ */ jsx("path", { d: areaPath, fill: `url(#${gradientId})` }),
406
+ /* @__PURE__ */ jsx("path", { d: linePath, fill: "none", stroke: color, strokeWidth: "2" })
443
407
  ] }),
444
- activeIndex !== null && points[activeIndex] && /* @__PURE__ */ jsx2(
408
+ activeIndex !== null && points[activeIndex] && /* @__PURE__ */ jsx(
445
409
  "circle",
446
410
  {
447
411
  cx: points[activeIndex].x,
@@ -453,7 +417,7 @@ var CurveChart = React2.memo(({ data, labels, width, height, animate, onHover, o
453
417
  )
454
418
  ] }, di);
455
419
  }),
456
- activeX !== null && /* @__PURE__ */ jsx2(
420
+ activeX !== null && /* @__PURE__ */ jsx(
457
421
  "line",
458
422
  {
459
423
  x1: activeX,
@@ -463,7 +427,7 @@ var CurveChart = React2.memo(({ data, labels, width, height, animate, onHover, o
463
427
  className: "chart-crosshair"
464
428
  }
465
429
  ),
466
- /* @__PURE__ */ jsx2(
430
+ /* @__PURE__ */ jsx(
467
431
  "rect",
468
432
  {
469
433
  x: PADDING.left,
@@ -479,9 +443,9 @@ var CurveChart = React2.memo(({ data, labels, width, height, animate, onHover, o
479
443
  );
480
444
  });
481
445
  CurveChart.displayName = "CurveChart";
482
- var BarChart = React2.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
483
- const entries = React2.useMemo(() => Object.entries(data), [data]);
484
- const maxVal = React2.useMemo(() => {
446
+ var BarChart = React.memo(({ data, labels, width, height, animate, onHover, onShowAt, onMove, onLeave }) => {
447
+ const entries = React.useMemo(() => Object.entries(data), [data]);
448
+ const maxVal = React.useMemo(() => {
485
449
  const allValues = entries.flatMap(([, v]) => v);
486
450
  return Math.max(...allValues) * 1.2 || 1;
487
451
  }, [entries]);
@@ -493,7 +457,7 @@ var BarChart = React2.memo(({ data, labels, width, height, animate, onHover, onM
493
457
  const barGap = groupCount > 1 ? 2 : 0;
494
458
  const barW = Math.max(1, Math.min(32, (groupW * 0.7 - barGap * (groupCount - 1)) / groupCount));
495
459
  const baseline = PADDING.top + chartH;
496
- const bars = React2.useMemo(
460
+ const bars = React.useMemo(
497
461
  () => entries.map(
498
462
  ([, values], di) => values.map((v, i) => {
499
463
  const totalBarsW = barW * groupCount + barGap * (groupCount - 1);
@@ -507,10 +471,10 @@ var BarChart = React2.memo(({ data, labels, width, height, animate, onHover, onM
507
471
  );
508
472
  const barLabelStep = getLabelStep(count, chartW);
509
473
  return /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
510
- /* @__PURE__ */ jsx2(GridLines, { width, height, chartH, maxVal }),
474
+ /* @__PURE__ */ jsx(GridLines, { width, height, chartH, maxVal }),
511
475
  labels.map((label, i) => {
512
476
  if (i % barLabelStep !== 0) return null;
513
- return /* @__PURE__ */ jsx2("text", { x: PADDING.left + groupW * i + groupW / 2, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
477
+ return /* @__PURE__ */ jsx("text", { x: PADDING.left + groupW * i + groupW / 2, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
514
478
  }),
515
479
  entries.map(([key], di) => {
516
480
  const palette = getPalette(LINE_BAR_PALETTES, di, key);
@@ -519,7 +483,7 @@ var BarChart = React2.memo(({ data, labels, width, height, animate, onHover, onM
519
483
  const r = Math.min(4, b.w / 2);
520
484
  const d = b.h <= r ? `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 + r} Q ${b.x} ${b.y} ${b.x + r} ${b.y} H ${b.x + b.w - r} Q ${b.x + b.w} ${b.y} ${b.x + b.w} ${b.y + r} V ${b.y + b.h} Z`;
521
485
  const delay = 100 + i * 80;
522
- return /* @__PURE__ */ jsx2(
486
+ return /* @__PURE__ */ jsx(
523
487
  "path",
524
488
  {
525
489
  d,
@@ -529,8 +493,7 @@ var BarChart = React2.memo(({ data, labels, width, height, animate, onHover, onM
529
493
  transformOrigin: `${b.x + b.w / 2}px ${baseline}px`,
530
494
  animationDelay: `${delay}ms`
531
495
  } : void 0,
532
- onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${b.v}`),
533
- onMouseMove: onMove,
496
+ onMouseEnter: () => onShowAt(b.x + b.w / 2, b.y, `${key}: ${labels[i]} \u2014 ${b.v}`),
534
497
  onMouseLeave: onLeave
535
498
  },
536
499
  `${di}-${i}`
@@ -540,11 +503,11 @@ var BarChart = React2.memo(({ data, labels, width, height, animate, onHover, onM
540
503
  ] });
541
504
  });
542
505
  BarChart.displayName = "BarChart";
543
- var PieDonutChart = React2.memo(
506
+ var PieDonutChart = React.memo(
544
507
  ({ data, labels, width, height, animate, isDoughnut, onHover, onMove, onLeave }) => {
545
- const entries = React2.useMemo(() => Object.entries(data), [data]);
546
- const values = React2.useMemo(() => entries.flatMap(([, v]) => v), [entries]);
547
- const total = React2.useMemo(() => values.reduce((a, b) => a + b, 0) || 1, [values]);
508
+ const entries = React.useMemo(() => Object.entries(data), [data]);
509
+ const values = React.useMemo(() => entries.flatMap(([, v]) => v), [entries]);
510
+ const total = React.useMemo(() => values.reduce((a, b) => a + b, 0) || 1, [values]);
548
511
  const size = Math.min(width, height);
549
512
  const cx = size / 2;
550
513
  const cy = size / 2;
@@ -552,10 +515,10 @@ var PieDonutChart = React2.memo(
552
515
  const innerR = isDoughnut ? r * 0.5 : 0;
553
516
  const firstKey = entries[0]?.[0] ?? "";
554
517
  const colorOffset = hashString(firstKey);
555
- const maskRef = React2.useRef(null);
518
+ const maskRef = React.useRef(null);
556
519
  const maskR = r + 10;
557
520
  const maskCircumference = 2 * Math.PI * maskR;
558
- React2.useEffect(() => {
521
+ React.useEffect(() => {
559
522
  if (!animate || !maskRef.current) return;
560
523
  const el = maskRef.current;
561
524
  el.style.strokeDasharray = `${maskCircumference}`;
@@ -565,7 +528,7 @@ var PieDonutChart = React2.memo(
565
528
  el.style.strokeDashoffset = "0";
566
529
  });
567
530
  }, [animate, maskCircumference]);
568
- const sliceData = React2.useMemo(() => {
531
+ const sliceData = React.useMemo(() => {
569
532
  let angle0 = -Math.PI / 2;
570
533
  let cumulativeAngle = 0;
571
534
  return values.map((v, i) => {
@@ -600,7 +563,7 @@ var PieDonutChart = React2.memo(
600
563
  }, [values, total, cx, cy, r, innerR, labels]);
601
564
  const maskId = `pie-mask-${isDoughnut ? "d" : "p"}`;
602
565
  return /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: [
603
- animate && /* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsx2("mask", { id: maskId, children: /* @__PURE__ */ jsx2(
566
+ animate && /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("mask", { id: maskId, children: /* @__PURE__ */ jsx(
604
567
  "circle",
605
568
  {
606
569
  ref: maskRef,
@@ -613,56 +576,39 @@ var PieDonutChart = React2.memo(
613
576
  transform: `rotate(-90 ${cx} ${cy})`
614
577
  }
615
578
  ) }) }),
616
- /* @__PURE__ */ jsx2("g", { mask: animate ? `url(#${maskId})` : void 0, children: sliceData.map((s, i) => /* @__PURE__ */ jsx2("g", { children: /* @__PURE__ */ jsx2(
579
+ /* @__PURE__ */ jsx("g", { mask: animate ? `url(#${maskId})` : void 0, children: sliceData.map((s, i) => /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx(
617
580
  "path",
618
581
  {
619
582
  d: s.d,
620
583
  fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
621
- className: "chart-slice",
622
- onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
623
- onMouseMove: onMove,
624
- onMouseLeave: onLeave
584
+ className: "chart-slice"
625
585
  }
626
- ) }, i)) }),
627
- sliceData.map((s, i) => s.angle > 0.2 && /* @__PURE__ */ jsx2(
628
- "text",
629
- {
630
- x: s.lx,
631
- y: s.ly,
632
- className: `chart-pie-label ${animate ? "chart-pie-label-animate" : ""}`,
633
- style: animate ? { animationDelay: `${s.labelDelay}ms` } : void 0,
634
- textAnchor: "middle",
635
- dominantBaseline: "central",
636
- children: s.v
637
- },
638
- `label-${i}`
639
- ))
586
+ ) }, i)) })
640
587
  ] });
641
588
  }
642
589
  );
643
590
  PieDonutChart.displayName = "PieDonutChart";
644
- var ChartTooltipPortal = ({ clientX, clientY, visible, children }) => {
645
- const ref = React2.useRef(null);
646
- const [pos, setPos] = React2.useState({ left: 0, top: 0 });
647
- React2.useLayoutEffect(() => {
591
+ var ChartTooltip = ({ x, y, containerWidth, containerHeight, children }) => {
592
+ const ref = React.useRef(null);
593
+ const [pos, setPos] = React.useState({ left: 0, top: 0 });
594
+ React.useLayoutEffect(() => {
648
595
  const el = ref.current;
649
596
  if (!el) return;
650
597
  const w = el.offsetWidth;
651
598
  const h = el.offsetHeight;
652
- const vw = window.innerWidth;
653
- let left = clientX + TOOLTIP_OFFSET;
654
- let top = clientY - h - TOOLTIP_OFFSET;
655
- if (left + w > vw - 8) left = clientX - w - TOOLTIP_OFFSET;
656
- if (top < 8) top = clientY + TOOLTIP_OFFSET;
657
- if (left < 8) left = 8;
599
+ let left = x + TOOLTIP_OFFSET;
600
+ let top = y - h - TOOLTIP_OFFSET;
601
+ if (left + w > containerWidth) left = x - w - TOOLTIP_OFFSET;
602
+ if (top < 0) top = y + TOOLTIP_OFFSET;
603
+ if (left < 0) left = 0;
658
604
  setPos({ left, top });
659
- }, [clientX, clientY]);
660
- return /* @__PURE__ */ jsx2(
605
+ }, [x, y, containerWidth, containerHeight]);
606
+ return /* @__PURE__ */ jsx(
661
607
  "div",
662
608
  {
663
609
  ref,
664
- className: `chart-tooltip ${visible ? "chart-tooltip-show" : "chart-tooltip-hide"}`,
665
- style: { position: "fixed", left: pos.left, top: pos.top, zIndex: 1100 },
610
+ className: "chart-tooltip chart-tooltip-show",
611
+ style: { left: pos.left, top: pos.top },
666
612
  children
667
613
  }
668
614
  );
@@ -674,13 +620,13 @@ var ChartLegend = ({ data, labels, type }) => {
674
620
  const total = values.reduce((a, b) => a + b, 0) || 1;
675
621
  const firstKey = entries[0]?.[0] ?? "";
676
622
  const colorOffset = hashString(firstKey);
677
- return /* @__PURE__ */ jsx2("div", { className: "chart-legend", children: values.map((v, i) => {
623
+ return /* @__PURE__ */ jsx("div", { className: "chart-legend", children: values.map((v, i) => {
678
624
  const pct = Math.round(v / total * 100);
679
625
  const color = PIE_COLORS[(i + colorOffset) % PIE_COLORS.length];
680
626
  return /* @__PURE__ */ jsxs("div", { className: "chart-legend-item", children: [
681
- /* @__PURE__ */ jsx2("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
627
+ /* @__PURE__ */ jsx("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
682
628
  /* @__PURE__ */ jsxs("div", { className: "chart-legend-text", children: [
683
- /* @__PURE__ */ jsx2("span", { className: "chart-legend-label", children: labels[i] || `${i + 1}` }),
629
+ /* @__PURE__ */ jsx("span", { className: "chart-legend-label", children: labels[i] || `${i + 1}` }),
684
630
  /* @__PURE__ */ jsxs("span", { className: "chart-legend-value", children: [
685
631
  v.toLocaleString(),
686
632
  "(",
@@ -691,37 +637,37 @@ var ChartLegend = ({ data, labels, type }) => {
691
637
  ] }, i);
692
638
  }) });
693
639
  }
694
- return /* @__PURE__ */ jsx2("div", { className: "chart-legend", children: entries.map(([key], di) => {
640
+ return /* @__PURE__ */ jsx("div", { className: "chart-legend", children: entries.map(([key], di) => {
695
641
  const palette = getPalette(LINE_BAR_PALETTES, di, key);
696
642
  const color = palette[2];
697
643
  const values = entries[di][1];
698
644
  const sum = values.reduce((a, b) => a + b, 0);
699
645
  return /* @__PURE__ */ jsxs("div", { className: "chart-legend-item", children: [
700
- /* @__PURE__ */ jsx2("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
646
+ /* @__PURE__ */ jsx("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
701
647
  /* @__PURE__ */ jsxs("div", { className: "chart-legend-text", children: [
702
- /* @__PURE__ */ jsx2("span", { className: "chart-legend-label", children: key }),
703
- /* @__PURE__ */ jsx2("span", { className: "chart-legend-value", children: sum.toLocaleString() })
648
+ /* @__PURE__ */ jsx("span", { className: "chart-legend-label", children: key }),
649
+ /* @__PURE__ */ jsx("span", { className: "chart-legend-value", children: sum.toLocaleString() })
704
650
  ] })
705
651
  ] }, di);
706
652
  }) });
707
653
  };
708
- var Chart = React2.memo((props) => {
654
+ var Chart = React.memo((props) => {
709
655
  const { type, data, labels, tooltip: showTooltip = true } = props;
710
- const { tooltip, show, hide, move, containerRef } = useChartTooltip(showTooltip);
656
+ const { tooltip, show, showAt, hide, move, containerRef } = useChartTooltip(showTooltip);
711
657
  const { width, height } = useChartSize(containerRef);
712
- const stableData = React2.useMemo(() => data, [JSON.stringify(data)]);
713
- const stableLabels = React2.useMemo(() => labels, [JSON.stringify(labels)]);
714
- const dataKey = React2.useMemo(() => JSON.stringify(labels), [labels]);
658
+ const stableData = React.useMemo(() => data, [JSON.stringify(data)]);
659
+ const stableLabels = React.useMemo(() => labels, [JSON.stringify(labels)]);
660
+ const dataKey = React.useMemo(() => JSON.stringify(labels), [labels]);
715
661
  const animate = useChartAnimation(containerRef, dataKey);
716
662
  const ready = width > 0 && height > 0;
717
663
  return /* @__PURE__ */ jsxs("div", { className: "lib-xplat-chart", ref: containerRef, children: [
718
- ready && type === "line" && /* @__PURE__ */ jsx2(LineChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
719
- ready && type === "curve" && /* @__PURE__ */ jsx2(CurveChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
720
- ready && type === "bar" && /* @__PURE__ */ jsx2(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
721
- ready && type === "pie" && /* @__PURE__ */ jsx2(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
722
- ready && type === "doughnut" && /* @__PURE__ */ jsx2(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
723
- ready && (type === "bar" || type === "pie" || type === "doughnut") && /* @__PURE__ */ jsx2(ChartLegend, { data: stableData, labels: stableLabels, type }),
724
- tooltip.content && /* @__PURE__ */ jsx2(Portal_default, { children: /* @__PURE__ */ jsx2(ChartTooltipPortal, { clientX: tooltip.clientX, clientY: tooltip.clientY, visible: tooltip.visible, children: tooltip.content }) })
664
+ ready && type === "line" && /* @__PURE__ */ jsx(LineChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onShowAt: showAt, onMove: move, onLeave: hide }),
665
+ ready && type === "curve" && /* @__PURE__ */ jsx(CurveChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onShowAt: showAt, onMove: move, onLeave: hide }),
666
+ ready && type === "bar" && /* @__PURE__ */ jsx(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onShowAt: showAt, onMove: move, onLeave: hide }),
667
+ ready && type === "pie" && /* @__PURE__ */ jsx(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onShowAt: showAt, onMove: move, onLeave: hide }),
668
+ ready && type === "doughnut" && /* @__PURE__ */ jsx(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onShowAt: showAt, onMove: move, onLeave: hide }),
669
+ ready && (type === "bar" || type === "pie" || type === "doughnut") && /* @__PURE__ */ jsx(ChartLegend, { data: stableData, labels: stableLabels, type }),
670
+ tooltip.visible && tooltip.content && /* @__PURE__ */ jsx(ChartTooltip, { x: tooltip.x, y: tooltip.y, containerWidth: width, containerHeight: height, children: tooltip.content })
725
671
  ] });
726
672
  });
727
673
  Chart.displayName = "Chart";