@x-plat/design-system 0.5.34 → 0.5.35

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.
@@ -35,8 +35,24 @@ __export(Chart_exports, {
35
35
  module.exports = __toCommonJS(Chart_exports);
36
36
 
37
37
  // src/components/Chart/Chart.tsx
38
+ var import_react2 = __toESM(require("react"), 1);
39
+
40
+ // src/tokens/hooks/Portal.tsx
38
41
  var import_react = __toESM(require("react"), 1);
42
+ var import_react_dom = __toESM(require("react-dom"), 1);
39
43
  var import_jsx_runtime = require("react/jsx-runtime");
44
+ var PortalContainerContext = import_react.default.createContext(null);
45
+ var Portal = ({ children }) => {
46
+ const contextContainer = import_react.default.useContext(PortalContainerContext);
47
+ if (typeof document === "undefined") return null;
48
+ const container = contextContainer ?? document.body;
49
+ return import_react_dom.default.createPortal(children, container);
50
+ };
51
+ Portal.displayName = "Portal";
52
+ var Portal_default = Portal;
53
+
54
+ // src/components/Chart/Chart.tsx
55
+ var import_jsx_runtime2 = require("react/jsx-runtime");
40
56
  var CATEGORICAL_COUNT = 8;
41
57
  var LINE_BAR_PALETTES = Array.from({ length: CATEGORICAL_COUNT }, (_, i) => {
42
58
  const n = i + 1;
@@ -82,11 +98,11 @@ var toSmoothPath = (points) => {
82
98
  };
83
99
  var RESIZE_SETTLE_MS = 150;
84
100
  var useChartSize = (ref) => {
85
- const [size, setSize] = import_react.default.useState({ width: 0, height: 0 });
86
- const settleTimer = import_react.default.useRef(0);
87
- const committedSize = import_react.default.useRef({ width: 0, height: 0 });
88
- const initialRef = import_react.default.useRef(true);
89
- import_react.default.useEffect(() => {
101
+ const [size, setSize] = import_react2.default.useState({ width: 0, height: 0 });
102
+ const settleTimer = import_react2.default.useRef(0);
103
+ const committedSize = import_react2.default.useRef({ width: 0, height: 0 });
104
+ const initialRef = import_react2.default.useRef(true);
105
+ import_react2.default.useEffect(() => {
90
106
  const el = ref.current;
91
107
  if (!el) return;
92
108
  const observer = new ResizeObserver((entries) => {
@@ -128,10 +144,10 @@ var useChartSize = (ref) => {
128
144
  };
129
145
  var prefersReducedMotion = () => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
130
146
  var useChartAnimation = (containerRef, dataKey) => {
131
- const [animate, setAnimate] = import_react.default.useState(false);
132
- const prevDataKey = import_react.default.useRef(dataKey);
133
- const hasAnimated = import_react.default.useRef(false);
134
- import_react.default.useEffect(() => {
147
+ const [animate, setAnimate] = import_react2.default.useState(false);
148
+ const prevDataKey = import_react2.default.useRef(dataKey);
149
+ const hasAnimated = import_react2.default.useRef(false);
150
+ import_react2.default.useEffect(() => {
135
151
  if (prefersReducedMotion()) return;
136
152
  const el = containerRef.current;
137
153
  if (!el) return;
@@ -147,7 +163,7 @@ var useChartAnimation = (containerRef, dataKey) => {
147
163
  observer.observe(el);
148
164
  return () => observer.disconnect();
149
165
  }, [containerRef]);
150
- import_react.default.useEffect(() => {
166
+ import_react2.default.useEffect(() => {
151
167
  if (dataKey !== prevDataKey.current) {
152
168
  prevDataKey.current = dataKey;
153
169
  if (prefersReducedMotion()) return;
@@ -161,15 +177,15 @@ var useChartAnimation = (containerRef, dataKey) => {
161
177
  };
162
178
  var TOOLTIP_OFFSET = 12;
163
179
  var useChartTooltip = (enabled) => {
164
- const [tooltip, setTooltip] = import_react.default.useState({
180
+ const [tooltip, setTooltip] = import_react2.default.useState({
165
181
  visible: false,
166
182
  clientX: 0,
167
183
  clientY: 0,
168
184
  content: ""
169
185
  });
170
- const containerRef = import_react.default.useRef(null);
171
- const rafRef = import_react.default.useRef(0);
172
- const move = import_react.default.useCallback((e) => {
186
+ const containerRef = import_react2.default.useRef(null);
187
+ const rafRef = import_react2.default.useRef(0);
188
+ const move = import_react2.default.useCallback((e) => {
173
189
  if (!enabled) return;
174
190
  const cx = e.clientX;
175
191
  const cy = e.clientY;
@@ -178,22 +194,22 @@ var useChartTooltip = (enabled) => {
178
194
  setTooltip((prev) => ({ ...prev, clientX: cx, clientY: cy }));
179
195
  });
180
196
  }, [enabled]);
181
- const show = import_react.default.useCallback((e, content) => {
197
+ const show = import_react2.default.useCallback((e, content) => {
182
198
  if (!enabled) return;
183
199
  setTooltip({ visible: true, clientX: e.clientX, clientY: e.clientY, content });
184
200
  }, [enabled]);
185
- const hide = import_react.default.useCallback(() => {
201
+ const hide = import_react2.default.useCallback(() => {
186
202
  cancelAnimationFrame(rafRef.current);
187
203
  setTooltip((prev) => ({ ...prev, visible: false }));
188
204
  }, []);
189
205
  return { tooltip, show, hide, move, containerRef };
190
206
  };
191
- var GridLines = import_react.default.memo(({ width, height, chartH, maxVal }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: [0, 0.25, 0.5, 0.75, 1].map((ratio) => {
207
+ var GridLines = import_react2.default.memo(({ width, height, chartH, maxVal }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: [0, 0.25, 0.5, 0.75, 1].map((ratio) => {
192
208
  const y = PADDING.top + (1 - ratio) * chartH;
193
209
  const val = Math.round(maxVal * ratio);
194
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
195
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: PADDING.left, y1: y, x2: width - PADDING.right, y2: y, className: "chart-grid" }),
196
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: PADDING.left - 8, y: y + 4, className: "chart-axis-label", textAnchor: "end", children: val })
210
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { children: [
211
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: PADDING.left, y1: y, x2: width - PADDING.right, y2: y, className: "chart-grid" }),
212
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: PADDING.left - 8, y: y + 4, className: "chart-axis-label", textAnchor: "end", children: val })
197
213
  ] }, ratio);
198
214
  }) }));
199
215
  GridLines.displayName = "GridLines";
@@ -203,18 +219,18 @@ var getLabelStep = (count, chartW) => {
203
219
  if (count <= maxLabels) return 1;
204
220
  return Math.ceil(count / maxLabels);
205
221
  };
206
- var AxisLabels = import_react.default.memo(({ labels, count, chartW, height }) => {
222
+ var AxisLabels = import_react2.default.memo(({ labels, count, chartW, height }) => {
207
223
  const step = getLabelStep(count, chartW);
208
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: labels.map((label, i) => {
224
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: labels.map((label, i) => {
209
225
  if (i % step !== 0) return null;
210
226
  const x = PADDING.left + i / (count - 1 || 1) * chartW;
211
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
227
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
212
228
  }) });
213
229
  });
214
230
  AxisLabels.displayName = "AxisLabels";
215
231
  var useCrosshair = (seriesPoints, entries, labels, chartH) => {
216
- const [activeIndex, setActiveIndex] = import_react.default.useState(null);
217
- const handleMouseMove = import_react.default.useCallback((e) => {
232
+ const [activeIndex, setActiveIndex] = import_react2.default.useState(null);
233
+ const handleMouseMove = import_react2.default.useCallback((e) => {
218
234
  const svg = e.currentTarget;
219
235
  const rect = svg.getBoundingClientRect();
220
236
  const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
@@ -233,17 +249,17 @@ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
233
249
  }
234
250
  setActiveIndex(minDist <= threshold ? closest : null);
235
251
  }, [seriesPoints]);
236
- const handleMouseLeave = import_react.default.useCallback(() => {
252
+ const handleMouseLeave = import_react2.default.useCallback(() => {
237
253
  setActiveIndex(null);
238
254
  }, []);
239
- const tooltipContent = import_react.default.useMemo(() => {
255
+ const tooltipContent = import_react2.default.useMemo(() => {
240
256
  if (activeIndex === null) return "";
241
257
  return entries.map(([key], di) => {
242
258
  const p = seriesPoints[di]?.[activeIndex];
243
259
  return p ? `${key}: ${p.v}` : "";
244
260
  }).filter(Boolean).join(" / ");
245
261
  }, [activeIndex, entries, seriesPoints]);
246
- const getTooltipAt = import_react.default.useCallback((idx) => {
262
+ const getTooltipAt = import_react2.default.useCallback((idx) => {
247
263
  return entries.map(([key], di) => {
248
264
  const p = seriesPoints[di]?.[idx];
249
265
  return p ? `${key}: ${p.v}` : "";
@@ -251,16 +267,16 @@ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
251
267
  }, [entries, seriesPoints]);
252
268
  return { activeIndex, handleMouseMove, handleMouseLeave, tooltipContent, getTooltipAt };
253
269
  };
254
- var LineChart = import_react.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
255
- const entries = import_react.default.useMemo(() => Object.entries(data), [data]);
256
- const maxVal = import_react.default.useMemo(() => {
270
+ var LineChart = import_react2.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
271
+ const entries = import_react2.default.useMemo(() => Object.entries(data), [data]);
272
+ const maxVal = import_react2.default.useMemo(() => {
257
273
  const allValues = entries.flatMap(([, v]) => v);
258
274
  return Math.max(...allValues) * 1.2 || 1;
259
275
  }, [entries]);
260
276
  const count = labels.length;
261
277
  const chartW = width - PADDING.left - PADDING.right;
262
278
  const chartH = height - PADDING.top - PADDING.bottom;
263
- const seriesPoints = import_react.default.useMemo(
279
+ const seriesPoints = import_react2.default.useMemo(
264
280
  () => entries.map(
265
281
  ([, values]) => values.map((v, i) => ({
266
282
  x: PADDING.left + i / (count - 1 || 1) * chartW,
@@ -270,9 +286,9 @@ var LineChart = import_react.default.memo(({ data, labels, width, height, animat
270
286
  ),
271
287
  [entries, count, chartW, chartH, maxVal]
272
288
  );
273
- const clipRef = import_react.default.useRef(null);
289
+ const clipRef = import_react2.default.useRef(null);
274
290
  const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
275
- import_react.default.useEffect(() => {
291
+ import_react2.default.useEffect(() => {
276
292
  if (!animate || !clipRef.current) return;
277
293
  clipRef.current.setAttribute("width", "0");
278
294
  requestAnimationFrame(() => {
@@ -284,7 +300,7 @@ var LineChart = import_react.default.memo(({ data, labels, width, height, animat
284
300
  }, [animate, width]);
285
301
  const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x ?? null : null;
286
302
  const lineClipId = "line-area-clip";
287
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
303
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
288
304
  "svg",
289
305
  {
290
306
  viewBox: `0 0 ${width} ${height}`,
@@ -317,9 +333,9 @@ var LineChart = import_react.default.memo(({ data, labels, width, height, animat
317
333
  onLeave();
318
334
  },
319
335
  children: [
320
- animate && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("clipPath", { id: lineClipId, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { ref: clipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
321
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GridLines, { width, height, chartH, maxVal }),
322
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AxisLabels, { labels, count, chartW, height }),
336
+ animate && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("clipPath", { id: lineClipId, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { ref: clipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
337
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GridLines, { width, height, chartH, maxVal }),
338
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AxisLabels, { labels, count, chartW, height }),
323
339
  entries.map(([key], di) => {
324
340
  const palette = getPalette(LINE_BAR_PALETTES, di, key);
325
341
  const color = palette[2];
@@ -328,16 +344,16 @@ var LineChart = import_react.default.memo(({ data, labels, width, height, animat
328
344
  const gradientId = `line-gradient-${di}`;
329
345
  const polyPoints = points.map((p) => `${p.x},${p.y}`).join(" ");
330
346
  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`;
331
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
332
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
333
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
334
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
347
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { children: [
348
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
349
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
350
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
335
351
  ] }) }),
336
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { clipPath: animate ? `url(#${lineClipId})` : void 0, children: [
337
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: areaD, fill: `url(#${gradientId})` }),
338
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: polyPoints, fill: "none", stroke: color, strokeWidth: "2" })
352
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { clipPath: animate ? `url(#${lineClipId})` : void 0, children: [
353
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: areaD, fill: `url(#${gradientId})` }),
354
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: polyPoints, fill: "none", stroke: color, strokeWidth: "2" })
339
355
  ] }),
340
- activeIndex !== null && points[activeIndex] && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
356
+ activeIndex !== null && points[activeIndex] && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
341
357
  "circle",
342
358
  {
343
359
  cx: points[activeIndex].x,
@@ -349,7 +365,7 @@ var LineChart = import_react.default.memo(({ data, labels, width, height, animat
349
365
  )
350
366
  ] }, di);
351
367
  }),
352
- activeX !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
368
+ activeX !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
353
369
  "line",
354
370
  {
355
371
  x1: activeX,
@@ -359,7 +375,7 @@ var LineChart = import_react.default.memo(({ data, labels, width, height, animat
359
375
  className: "chart-crosshair"
360
376
  }
361
377
  ),
362
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
378
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
363
379
  "rect",
364
380
  {
365
381
  x: PADDING.left,
@@ -375,16 +391,16 @@ var LineChart = import_react.default.memo(({ data, labels, width, height, animat
375
391
  );
376
392
  });
377
393
  LineChart.displayName = "LineChart";
378
- var CurveChart = import_react.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
379
- const entries = import_react.default.useMemo(() => Object.entries(data), [data]);
380
- const maxVal = import_react.default.useMemo(() => {
394
+ var CurveChart = import_react2.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
395
+ const entries = import_react2.default.useMemo(() => Object.entries(data), [data]);
396
+ const maxVal = import_react2.default.useMemo(() => {
381
397
  const allValues = entries.flatMap(([, v]) => v);
382
398
  return Math.max(...allValues) * 1.2 || 1;
383
399
  }, [entries]);
384
400
  const count = labels.length;
385
401
  const chartW = width - PADDING.left - PADDING.right;
386
402
  const chartH = height - PADDING.top - PADDING.bottom;
387
- const seriesPoints = import_react.default.useMemo(
403
+ const seriesPoints = import_react2.default.useMemo(
388
404
  () => entries.map(
389
405
  ([, values]) => values.map((v, i) => ({
390
406
  x: PADDING.left + i / (count - 1 || 1) * chartW,
@@ -394,9 +410,9 @@ var CurveChart = import_react.default.memo(({ data, labels, width, height, anima
394
410
  ),
395
411
  [entries, count, chartW, chartH, maxVal]
396
412
  );
397
- const curveClipRef = import_react.default.useRef(null);
413
+ const curveClipRef = import_react2.default.useRef(null);
398
414
  const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
399
- import_react.default.useEffect(() => {
415
+ import_react2.default.useEffect(() => {
400
416
  if (!animate || !curveClipRef.current) return;
401
417
  curveClipRef.current.setAttribute("width", "0");
402
418
  requestAnimationFrame(() => {
@@ -408,7 +424,7 @@ var CurveChart = import_react.default.memo(({ data, labels, width, height, anima
408
424
  }, [animate, width]);
409
425
  const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x ?? null : null;
410
426
  const curveClipId = "curve-area-clip";
411
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
427
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
412
428
  "svg",
413
429
  {
414
430
  viewBox: `0 0 ${width} ${height}`,
@@ -441,9 +457,9 @@ var CurveChart = import_react.default.memo(({ data, labels, width, height, anima
441
457
  onLeave();
442
458
  },
443
459
  children: [
444
- animate && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("clipPath", { id: curveClipId, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { ref: curveClipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
445
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GridLines, { width, height, chartH, maxVal }),
446
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AxisLabels, { labels, count, chartW, height }),
460
+ animate && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("clipPath", { id: curveClipId, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { ref: curveClipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
461
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GridLines, { width, height, chartH, maxVal }),
462
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AxisLabels, { labels, count, chartW, height }),
447
463
  entries.map(([key], di) => {
448
464
  const palette = getPalette(LINE_BAR_PALETTES, di, key);
449
465
  const color = palette[2];
@@ -452,16 +468,16 @@ var CurveChart = import_react.default.memo(({ data, labels, width, height, anima
452
468
  const gradientId = `curve-gradient-${di}`;
453
469
  const linePath = toSmoothPath(points);
454
470
  const areaPath = linePath + ` L ${points[points.length - 1].x} ${PADDING.top + chartH} L ${points[0].x} ${PADDING.top + chartH} Z`;
455
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
456
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
457
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
458
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
471
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { children: [
472
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
473
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
474
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
459
475
  ] }) }),
460
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { clipPath: animate ? `url(#${curveClipId})` : void 0, children: [
461
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: areaPath, fill: `url(#${gradientId})` }),
462
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: linePath, fill: "none", stroke: color, strokeWidth: "2" })
476
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { clipPath: animate ? `url(#${curveClipId})` : void 0, children: [
477
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: areaPath, fill: `url(#${gradientId})` }),
478
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: linePath, fill: "none", stroke: color, strokeWidth: "2" })
463
479
  ] }),
464
- activeIndex !== null && points[activeIndex] && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
480
+ activeIndex !== null && points[activeIndex] && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
465
481
  "circle",
466
482
  {
467
483
  cx: points[activeIndex].x,
@@ -473,7 +489,7 @@ var CurveChart = import_react.default.memo(({ data, labels, width, height, anima
473
489
  )
474
490
  ] }, di);
475
491
  }),
476
- activeX !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
492
+ activeX !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
477
493
  "line",
478
494
  {
479
495
  x1: activeX,
@@ -483,7 +499,7 @@ var CurveChart = import_react.default.memo(({ data, labels, width, height, anima
483
499
  className: "chart-crosshair"
484
500
  }
485
501
  ),
486
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
502
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
487
503
  "rect",
488
504
  {
489
505
  x: PADDING.left,
@@ -499,9 +515,9 @@ var CurveChart = import_react.default.memo(({ data, labels, width, height, anima
499
515
  );
500
516
  });
501
517
  CurveChart.displayName = "CurveChart";
502
- var BarChart = import_react.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
503
- const entries = import_react.default.useMemo(() => Object.entries(data), [data]);
504
- const maxVal = import_react.default.useMemo(() => {
518
+ var BarChart = import_react2.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
519
+ const entries = import_react2.default.useMemo(() => Object.entries(data), [data]);
520
+ const maxVal = import_react2.default.useMemo(() => {
505
521
  const allValues = entries.flatMap(([, v]) => v);
506
522
  return Math.max(...allValues) * 1.2 || 1;
507
523
  }, [entries]);
@@ -513,7 +529,7 @@ var BarChart = import_react.default.memo(({ data, labels, width, height, animate
513
529
  const barGap = groupCount > 1 ? 2 : 0;
514
530
  const barW = Math.max(1, Math.min(32, (groupW * 0.7 - barGap * (groupCount - 1)) / groupCount));
515
531
  const baseline = PADDING.top + chartH;
516
- const bars = import_react.default.useMemo(
532
+ const bars = import_react2.default.useMemo(
517
533
  () => entries.map(
518
534
  ([, values], di) => values.map((v, i) => {
519
535
  const totalBarsW = barW * groupCount + barGap * (groupCount - 1);
@@ -526,11 +542,11 @@ var BarChart = import_react.default.memo(({ data, labels, width, height, animate
526
542
  [entries, maxVal, chartH, groupW, barW, barGap, groupCount]
527
543
  );
528
544
  const barLabelStep = getLabelStep(count, chartW);
529
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
530
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GridLines, { width, height, chartH, maxVal }),
545
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
546
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GridLines, { width, height, chartH, maxVal }),
531
547
  labels.map((label, i) => {
532
548
  if (i % barLabelStep !== 0) return null;
533
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: PADDING.left + groupW * i + groupW / 2, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
549
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: PADDING.left + groupW * i + groupW / 2, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
534
550
  }),
535
551
  entries.map(([key], di) => {
536
552
  const palette = getPalette(LINE_BAR_PALETTES, di, key);
@@ -539,7 +555,7 @@ var BarChart = import_react.default.memo(({ data, labels, width, height, animate
539
555
  const r = Math.min(4, b.w / 2);
540
556
  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`;
541
557
  const delay = 100 + i * 80;
542
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
558
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
543
559
  "path",
544
560
  {
545
561
  d,
@@ -560,11 +576,11 @@ var BarChart = import_react.default.memo(({ data, labels, width, height, animate
560
576
  ] });
561
577
  });
562
578
  BarChart.displayName = "BarChart";
563
- var PieDonutChart = import_react.default.memo(
579
+ var PieDonutChart = import_react2.default.memo(
564
580
  ({ data, labels, width, height, animate, isDoughnut, onHover, onMove, onLeave }) => {
565
- const entries = import_react.default.useMemo(() => Object.entries(data), [data]);
566
- const values = import_react.default.useMemo(() => entries.flatMap(([, v]) => v), [entries]);
567
- const total = import_react.default.useMemo(() => values.reduce((a, b) => a + b, 0) || 1, [values]);
581
+ const entries = import_react2.default.useMemo(() => Object.entries(data), [data]);
582
+ const values = import_react2.default.useMemo(() => entries.flatMap(([, v]) => v), [entries]);
583
+ const total = import_react2.default.useMemo(() => values.reduce((a, b) => a + b, 0) || 1, [values]);
568
584
  const size = Math.min(width, height);
569
585
  const cx = size / 2;
570
586
  const cy = size / 2;
@@ -572,10 +588,10 @@ var PieDonutChart = import_react.default.memo(
572
588
  const innerR = isDoughnut ? r * 0.5 : 0;
573
589
  const firstKey = entries[0]?.[0] ?? "";
574
590
  const colorOffset = hashString(firstKey);
575
- const maskRef = import_react.default.useRef(null);
591
+ const maskRef = import_react2.default.useRef(null);
576
592
  const maskR = r + 10;
577
593
  const maskCircumference = 2 * Math.PI * maskR;
578
- import_react.default.useEffect(() => {
594
+ import_react2.default.useEffect(() => {
579
595
  if (!animate || !maskRef.current) return;
580
596
  const el = maskRef.current;
581
597
  el.style.strokeDasharray = `${maskCircumference}`;
@@ -585,7 +601,7 @@ var PieDonutChart = import_react.default.memo(
585
601
  el.style.strokeDashoffset = "0";
586
602
  });
587
603
  }, [animate, maskCircumference]);
588
- const sliceData = import_react.default.useMemo(() => {
604
+ const sliceData = import_react2.default.useMemo(() => {
589
605
  let angle0 = -Math.PI / 2;
590
606
  let cumulativeAngle = 0;
591
607
  return values.map((v, i) => {
@@ -619,8 +635,8 @@ var PieDonutChart = import_react.default.memo(
619
635
  });
620
636
  }, [values, total, cx, cy, r, innerR, labels]);
621
637
  const maskId = `pie-mask-${isDoughnut ? "d" : "p"}`;
622
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: [
623
- animate && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("mask", { id: maskId, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
638
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: [
639
+ animate && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("mask", { id: maskId, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
624
640
  "circle",
625
641
  {
626
642
  ref: maskRef,
@@ -633,7 +649,7 @@ var PieDonutChart = import_react.default.memo(
633
649
  transform: `rotate(-90 ${cx} ${cy})`
634
650
  }
635
651
  ) }) }),
636
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { mask: animate ? `url(#${maskId})` : void 0, children: sliceData.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
652
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { mask: animate ? `url(#${maskId})` : void 0, children: sliceData.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
637
653
  "path",
638
654
  {
639
655
  d: s.d,
@@ -644,7 +660,7 @@ var PieDonutChart = import_react.default.memo(
644
660
  onMouseLeave: onLeave
645
661
  }
646
662
  ) }, i)) }),
647
- sliceData.map((s, i) => s.angle > 0.2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
663
+ sliceData.map((s, i) => s.angle > 0.2 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
648
664
  "text",
649
665
  {
650
666
  x: s.lx,
@@ -662,9 +678,9 @@ var PieDonutChart = import_react.default.memo(
662
678
  );
663
679
  PieDonutChart.displayName = "PieDonutChart";
664
680
  var ChartTooltipPortal = ({ clientX, clientY, visible, children }) => {
665
- const ref = import_react.default.useRef(null);
666
- const [pos, setPos] = import_react.default.useState({ left: 0, top: 0 });
667
- import_react.default.useLayoutEffect(() => {
681
+ const ref = import_react2.default.useRef(null);
682
+ const [pos, setPos] = import_react2.default.useState({ left: 0, top: 0 });
683
+ import_react2.default.useLayoutEffect(() => {
668
684
  const el = ref.current;
669
685
  if (!el) return;
670
686
  const w = el.offsetWidth;
@@ -677,7 +693,7 @@ var ChartTooltipPortal = ({ clientX, clientY, visible, children }) => {
677
693
  if (left < 8) left = 8;
678
694
  setPos({ left, top });
679
695
  }, [clientX, clientY]);
680
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
696
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
681
697
  "div",
682
698
  {
683
699
  ref,
@@ -694,14 +710,14 @@ var ChartLegend = ({ data, labels, type }) => {
694
710
  const total = values.reduce((a, b) => a + b, 0) || 1;
695
711
  const firstKey = entries[0]?.[0] ?? "";
696
712
  const colorOffset = hashString(firstKey);
697
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "chart-legend", children: values.map((v, i) => {
713
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "chart-legend", children: values.map((v, i) => {
698
714
  const pct = Math.round(v / total * 100);
699
715
  const color = PIE_COLORS[(i + colorOffset) % PIE_COLORS.length];
700
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "chart-legend-item", children: [
701
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
702
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "chart-legend-text", children: [
703
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "chart-legend-label", children: labels[i] || `${i + 1}` }),
704
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "chart-legend-value", children: [
716
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "chart-legend-item", children: [
717
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
718
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "chart-legend-text", children: [
719
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "chart-legend-label", children: labels[i] || `${i + 1}` }),
720
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "chart-legend-value", children: [
705
721
  v.toLocaleString(),
706
722
  "(",
707
723
  pct,
@@ -711,37 +727,37 @@ var ChartLegend = ({ data, labels, type }) => {
711
727
  ] }, i);
712
728
  }) });
713
729
  }
714
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "chart-legend", children: entries.map(([key], di) => {
730
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "chart-legend", children: entries.map(([key], di) => {
715
731
  const palette = getPalette(LINE_BAR_PALETTES, di, key);
716
732
  const color = palette[2];
717
733
  const values = entries[di][1];
718
734
  const sum = values.reduce((a, b) => a + b, 0);
719
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "chart-legend-item", children: [
720
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
721
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "chart-legend-text", children: [
722
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "chart-legend-label", children: key }),
723
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "chart-legend-value", children: sum.toLocaleString() })
735
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "chart-legend-item", children: [
736
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
737
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "chart-legend-text", children: [
738
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "chart-legend-label", children: key }),
739
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "chart-legend-value", children: sum.toLocaleString() })
724
740
  ] })
725
741
  ] }, di);
726
742
  }) });
727
743
  };
728
- var Chart = import_react.default.memo((props) => {
744
+ var Chart = import_react2.default.memo((props) => {
729
745
  const { type, data, labels, tooltip: showTooltip = true } = props;
730
746
  const { tooltip, show, hide, move, containerRef } = useChartTooltip(showTooltip);
731
747
  const { width, height } = useChartSize(containerRef);
732
- const stableData = import_react.default.useMemo(() => data, [JSON.stringify(data)]);
733
- const stableLabels = import_react.default.useMemo(() => labels, [JSON.stringify(labels)]);
734
- const dataKey = import_react.default.useMemo(() => JSON.stringify(labels), [labels]);
748
+ const stableData = import_react2.default.useMemo(() => data, [JSON.stringify(data)]);
749
+ const stableLabels = import_react2.default.useMemo(() => labels, [JSON.stringify(labels)]);
750
+ const dataKey = import_react2.default.useMemo(() => JSON.stringify(labels), [labels]);
735
751
  const animate = useChartAnimation(containerRef, dataKey);
736
752
  const ready = width > 0 && height > 0;
737
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lib-xplat-chart", ref: containerRef, children: [
738
- ready && type === "line" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LineChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
739
- ready && type === "curve" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CurveChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
740
- ready && type === "bar" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
741
- ready && type === "pie" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
742
- ready && type === "doughnut" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
743
- ready && (type === "bar" || type === "pie" || type === "doughnut") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartLegend, { data: stableData, labels: stableLabels, type }),
744
- tooltip.content && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartTooltipPortal, { clientX: tooltip.clientX, clientY: tooltip.clientY, visible: tooltip.visible, children: tooltip.content })
753
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lib-xplat-chart", ref: containerRef, children: [
754
+ ready && type === "line" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LineChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
755
+ ready && type === "curve" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CurveChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
756
+ ready && type === "bar" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
757
+ ready && type === "pie" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
758
+ ready && type === "doughnut" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
759
+ ready && (type === "bar" || type === "pie" || type === "doughnut") && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChartLegend, { data: stableData, labels: stableLabels, type }),
760
+ tooltip.content && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChartTooltipPortal, { clientX: tooltip.clientX, clientY: tooltip.clientY, visible: tooltip.visible, children: tooltip.content }) })
745
761
  ] });
746
762
  });
747
763
  Chart.displayName = "Chart";
@@ -4,11 +4,14 @@
4
4
  width: 100%;
5
5
  height: 100%;
6
6
  position: relative;
7
+ display: flex;
8
+ flex-direction: column;
7
9
  }
8
10
  .lib-xplat-chart .chart-svg {
9
11
  display: block;
10
12
  width: 100%;
11
- height: 100%;
13
+ flex: 1;
14
+ min-height: 0;
12
15
  will-change: transform;
13
16
  contain: layout style paint;
14
17
  }