@x-plat/design-system 0.5.31 → 0.5.33

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.
@@ -121,40 +121,28 @@ var useChartAnimation = (containerRef, dataKey) => {
121
121
  }, [dataKey]);
122
122
  return animate || prefersReducedMotion();
123
123
  };
124
+ var TOOLTIP_OFFSET = 12;
124
125
  var useChartTooltip = (enabled) => {
125
126
  const [tooltip, setTooltip] = React.useState({
126
127
  visible: false,
127
- x: 0,
128
- y: 0,
128
+ clientX: 0,
129
+ clientY: 0,
129
130
  content: ""
130
131
  });
131
132
  const containerRef = React.useRef(null);
132
133
  const rafRef = React.useRef(0);
133
134
  const move = React.useCallback((e) => {
134
135
  if (!enabled) return;
135
- const clientX = e.clientX;
136
- const clientY = e.clientY;
136
+ const cx = e.clientX;
137
+ const cy = e.clientY;
137
138
  cancelAnimationFrame(rafRef.current);
138
139
  rafRef.current = requestAnimationFrame(() => {
139
- const rect = containerRef.current?.getBoundingClientRect();
140
- if (!rect) return;
141
- setTooltip((prev) => ({
142
- ...prev,
143
- x: clientX - rect.left,
144
- y: clientY - rect.top - 12
145
- }));
140
+ setTooltip((prev) => ({ ...prev, clientX: cx, clientY: cy }));
146
141
  });
147
142
  }, [enabled]);
148
143
  const show = React.useCallback((e, content) => {
149
144
  if (!enabled) return;
150
- const rect = containerRef.current?.getBoundingClientRect();
151
- if (!rect) return;
152
- setTooltip({
153
- visible: true,
154
- x: e.clientX - rect.left,
155
- y: e.clientY - rect.top - 12,
156
- content
157
- });
145
+ setTooltip({ visible: true, clientX: e.clientX, clientY: e.clientY, content });
158
146
  }, [enabled]);
159
147
  const hide = React.useCallback(() => {
160
148
  cancelAnimationFrame(rafRef.current);
@@ -186,6 +174,45 @@ var AxisLabels = React.memo(({ labels, count, chartW, height }) => {
186
174
  }) });
187
175
  });
188
176
  AxisLabels.displayName = "AxisLabels";
177
+ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
178
+ const [activeIndex, setActiveIndex] = React.useState(null);
179
+ const handleMouseMove = React.useCallback((e) => {
180
+ const svg = e.currentTarget;
181
+ const rect = svg.getBoundingClientRect();
182
+ const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
183
+ if (seriesPoints.length === 0 || seriesPoints[0].length === 0) return;
184
+ const points = seriesPoints[0];
185
+ const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
186
+ const threshold = step / 2;
187
+ let closest = 0;
188
+ let minDist = Math.abs(points[0].x - mx);
189
+ for (let i = 1; i < points.length; i++) {
190
+ const dist = Math.abs(points[i].x - mx);
191
+ if (dist < minDist) {
192
+ minDist = dist;
193
+ closest = i;
194
+ }
195
+ }
196
+ setActiveIndex(minDist <= threshold ? closest : null);
197
+ }, [seriesPoints]);
198
+ const handleMouseLeave = React.useCallback(() => {
199
+ setActiveIndex(null);
200
+ }, []);
201
+ const tooltipContent = React.useMemo(() => {
202
+ if (activeIndex === null) return "";
203
+ return entries.map(([key], di) => {
204
+ const p = seriesPoints[di]?.[activeIndex];
205
+ return p ? `${key}: ${p.v}` : "";
206
+ }).filter(Boolean).join(" / ");
207
+ }, [activeIndex, entries, seriesPoints]);
208
+ const getTooltipAt = React.useCallback((idx) => {
209
+ return entries.map(([key], di) => {
210
+ const p = seriesPoints[di]?.[idx];
211
+ return p ? `${key}: ${p.v}` : "";
212
+ }).filter(Boolean).join(" / ");
213
+ }, [entries, seriesPoints]);
214
+ return { activeIndex, handleMouseMove, handleMouseLeave, tooltipContent, getTooltipAt };
215
+ };
189
216
  var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
190
217
  const entries = React.useMemo(() => Object.entries(data), [data]);
191
218
  const maxVal = React.useMemo(() => {
@@ -205,8 +232,9 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onM
205
232
  ),
206
233
  [entries, count, chartW, chartH, maxVal]
207
234
  );
208
- const showPoints = count <= 100;
209
235
  const lineRefs = React.useRef([]);
236
+ const clipRef = React.useRef(null);
237
+ const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
210
238
  React.useEffect(() => {
211
239
  if (!animate) return;
212
240
  lineRefs.current.forEach((el) => {
@@ -219,61 +247,123 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onM
219
247
  el.style.strokeDashoffset = "0";
220
248
  });
221
249
  });
222
- }, [animate, seriesPoints]);
223
- return /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
224
- /* @__PURE__ */ jsx(GridLines, { width, height, chartH, maxVal }),
225
- /* @__PURE__ */ jsx(AxisLabels, { labels, count, chartW, height }),
226
- entries.map(([key], di) => {
227
- const palette = getPalette(LINE_BAR_PALETTES, di, key);
228
- const color = palette[2];
229
- const areaColor = palette[0];
230
- const points = seriesPoints[di];
231
- const gradientId = `line-gradient-${di}`;
232
- const polyPoints = points.map((p) => `${p.x},${p.y}`).join(" ");
233
- 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`;
234
- return /* @__PURE__ */ jsxs("g", { children: [
235
- /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
236
- /* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
237
- /* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
238
- ] }) }),
239
- /* @__PURE__ */ jsx(
240
- "path",
250
+ if (clipRef.current) {
251
+ clipRef.current.setAttribute("width", "0");
252
+ requestAnimationFrame(() => {
253
+ if (clipRef.current) {
254
+ clipRef.current.style.transition = "width 1200ms ease-out 200ms";
255
+ clipRef.current.setAttribute("width", `${width}`);
256
+ }
257
+ });
258
+ }
259
+ }, [animate, seriesPoints, width]);
260
+ const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x ?? null : null;
261
+ const lineClipId = "line-area-clip";
262
+ return /* @__PURE__ */ jsxs(
263
+ "svg",
264
+ {
265
+ viewBox: `0 0 ${width} ${height}`,
266
+ className: "chart-svg",
267
+ onMouseMove: (e) => {
268
+ handleMouseMove(e);
269
+ const svg = e.currentTarget;
270
+ const rect = svg.getBoundingClientRect();
271
+ const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
272
+ const points = seriesPoints[0];
273
+ if (!points || points.length === 0) return;
274
+ const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
275
+ let closest = 0;
276
+ let minDist = Math.abs(points[0].x - mx);
277
+ for (let i = 1; i < points.length; i++) {
278
+ const dist = Math.abs(points[i].x - mx);
279
+ if (dist < minDist) {
280
+ minDist = dist;
281
+ closest = i;
282
+ }
283
+ }
284
+ if (minDist <= step / 2) {
285
+ onHover(e, `${labels[closest]} \u2014 ${getTooltipAt(closest)}`);
286
+ } else {
287
+ onLeave();
288
+ }
289
+ },
290
+ onMouseLeave: () => {
291
+ handleMouseLeave();
292
+ onLeave();
293
+ },
294
+ children: [
295
+ 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 }) }) }),
296
+ /* @__PURE__ */ jsx(GridLines, { width, height, chartH, maxVal }),
297
+ /* @__PURE__ */ jsx(AxisLabels, { labels, count, chartW, height }),
298
+ entries.map(([key], di) => {
299
+ const palette = getPalette(LINE_BAR_PALETTES, di, key);
300
+ const color = palette[2];
301
+ const areaColor = palette[0];
302
+ const points = seriesPoints[di];
303
+ const gradientId = `line-gradient-${di}`;
304
+ const polyPoints = points.map((p) => `${p.x},${p.y}`).join(" ");
305
+ 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`;
306
+ return /* @__PURE__ */ jsxs("g", { children: [
307
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
308
+ /* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
309
+ /* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
310
+ ] }) }),
311
+ /* @__PURE__ */ jsx(
312
+ "path",
313
+ {
314
+ d: areaD,
315
+ fill: `url(#${gradientId})`,
316
+ clipPath: animate ? `url(#${lineClipId})` : void 0
317
+ }
318
+ ),
319
+ /* @__PURE__ */ jsx(
320
+ "polyline",
321
+ {
322
+ ref: (el) => {
323
+ lineRefs.current[di] = el;
324
+ },
325
+ points: polyPoints,
326
+ fill: "none",
327
+ stroke: color,
328
+ strokeWidth: "2"
329
+ }
330
+ ),
331
+ activeIndex !== null && points[activeIndex] && /* @__PURE__ */ jsx(
332
+ "circle",
333
+ {
334
+ cx: points[activeIndex].x,
335
+ cy: points[activeIndex].y,
336
+ r: "5",
337
+ fill: color,
338
+ className: "chart-point-active"
339
+ }
340
+ )
341
+ ] }, di);
342
+ }),
343
+ activeX !== null && /* @__PURE__ */ jsx(
344
+ "line",
241
345
  {
242
- d: areaD,
243
- fill: `url(#${gradientId})`,
244
- className: "chart-area",
245
- style: animate ? { animationDelay: "600ms" } : { opacity: 1 }
346
+ x1: activeX,
347
+ y1: PADDING.top,
348
+ x2: activeX,
349
+ y2: PADDING.top + chartH,
350
+ className: "chart-crosshair"
246
351
  }
247
352
  ),
248
353
  /* @__PURE__ */ jsx(
249
- "polyline",
354
+ "rect",
250
355
  {
251
- ref: (el) => {
252
- lineRefs.current[di] = el;
253
- },
254
- points: polyPoints,
255
- fill: "none",
256
- stroke: color,
257
- strokeWidth: "2"
356
+ x: PADDING.left,
357
+ y: PADDING.top,
358
+ width: chartW,
359
+ height: chartH,
360
+ fill: "transparent",
361
+ style: { cursor: "crosshair" }
258
362
  }
259
- ),
260
- showPoints && points.map((p, i) => /* @__PURE__ */ jsx(
261
- "circle",
262
- {
263
- cx: p.x,
264
- cy: p.y,
265
- r: "4",
266
- fill: color,
267
- className: "chart-point",
268
- onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${p.v}`),
269
- onMouseMove: onMove,
270
- onMouseLeave: onLeave
271
- },
272
- i
273
- ))
274
- ] }, di);
275
- })
276
- ] });
363
+ )
364
+ ]
365
+ }
366
+ );
277
367
  });
278
368
  LineChart.displayName = "LineChart";
279
369
  var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
@@ -295,8 +385,9 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
295
385
  ),
296
386
  [entries, count, chartW, chartH, maxVal]
297
387
  );
298
- const showPoints = count <= 100;
299
388
  const lineRefs = React.useRef([]);
389
+ const curveClipRef = React.useRef(null);
390
+ const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
300
391
  React.useEffect(() => {
301
392
  if (!animate) return;
302
393
  lineRefs.current.forEach((el) => {
@@ -309,61 +400,123 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
309
400
  el.style.strokeDashoffset = "0";
310
401
  });
311
402
  });
312
- }, [animate, seriesPoints]);
313
- return /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
314
- /* @__PURE__ */ jsx(GridLines, { width, height, chartH, maxVal }),
315
- /* @__PURE__ */ jsx(AxisLabels, { labels, count, chartW, height }),
316
- entries.map(([key], di) => {
317
- const palette = getPalette(LINE_BAR_PALETTES, di, key);
318
- const color = palette[2];
319
- const areaColor = palette[0];
320
- const points = seriesPoints[di];
321
- const gradientId = `curve-gradient-${di}`;
322
- const linePath = toSmoothPath(points);
323
- const areaPath = linePath + ` L ${points[points.length - 1].x} ${PADDING.top + chartH} L ${points[0].x} ${PADDING.top + chartH} Z`;
324
- return /* @__PURE__ */ jsxs("g", { children: [
325
- /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
326
- /* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
327
- /* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
328
- ] }) }),
329
- /* @__PURE__ */ jsx(
330
- "path",
403
+ if (curveClipRef.current) {
404
+ curveClipRef.current.setAttribute("width", "0");
405
+ requestAnimationFrame(() => {
406
+ if (curveClipRef.current) {
407
+ curveClipRef.current.style.transition = "width 1200ms ease-out 200ms";
408
+ curveClipRef.current.setAttribute("width", `${width}`);
409
+ }
410
+ });
411
+ }
412
+ }, [animate, seriesPoints, width]);
413
+ const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x ?? null : null;
414
+ const curveClipId = "curve-area-clip";
415
+ return /* @__PURE__ */ jsxs(
416
+ "svg",
417
+ {
418
+ viewBox: `0 0 ${width} ${height}`,
419
+ className: "chart-svg",
420
+ onMouseMove: (e) => {
421
+ handleMouseMove(e);
422
+ const svg = e.currentTarget;
423
+ const rect = svg.getBoundingClientRect();
424
+ const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
425
+ const points = seriesPoints[0];
426
+ if (!points || points.length === 0) return;
427
+ const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
428
+ let closest = 0;
429
+ let minDist = Math.abs(points[0].x - mx);
430
+ for (let i = 1; i < points.length; i++) {
431
+ const dist = Math.abs(points[i].x - mx);
432
+ if (dist < minDist) {
433
+ minDist = dist;
434
+ closest = i;
435
+ }
436
+ }
437
+ if (minDist <= step / 2) {
438
+ onHover(e, `${labels[closest]} \u2014 ${getTooltipAt(closest)}`);
439
+ } else {
440
+ onLeave();
441
+ }
442
+ },
443
+ onMouseLeave: () => {
444
+ handleMouseLeave();
445
+ onLeave();
446
+ },
447
+ children: [
448
+ 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 }) }) }),
449
+ /* @__PURE__ */ jsx(GridLines, { width, height, chartH, maxVal }),
450
+ /* @__PURE__ */ jsx(AxisLabels, { labels, count, chartW, height }),
451
+ entries.map(([key], di) => {
452
+ const palette = getPalette(LINE_BAR_PALETTES, di, key);
453
+ const color = palette[2];
454
+ const areaColor = palette[0];
455
+ const points = seriesPoints[di];
456
+ const gradientId = `curve-gradient-${di}`;
457
+ const linePath = toSmoothPath(points);
458
+ const areaPath = linePath + ` L ${points[points.length - 1].x} ${PADDING.top + chartH} L ${points[0].x} ${PADDING.top + chartH} Z`;
459
+ return /* @__PURE__ */ jsxs("g", { children: [
460
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
461
+ /* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
462
+ /* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
463
+ ] }) }),
464
+ /* @__PURE__ */ jsx(
465
+ "path",
466
+ {
467
+ d: areaPath,
468
+ fill: `url(#${gradientId})`,
469
+ clipPath: animate ? `url(#${curveClipId})` : void 0
470
+ }
471
+ ),
472
+ /* @__PURE__ */ jsx(
473
+ "path",
474
+ {
475
+ ref: (el) => {
476
+ lineRefs.current[di] = el;
477
+ },
478
+ d: linePath,
479
+ fill: "none",
480
+ stroke: color,
481
+ strokeWidth: "2"
482
+ }
483
+ ),
484
+ activeIndex !== null && points[activeIndex] && /* @__PURE__ */ jsx(
485
+ "circle",
486
+ {
487
+ cx: points[activeIndex].x,
488
+ cy: points[activeIndex].y,
489
+ r: "5",
490
+ fill: color,
491
+ className: "chart-point-active"
492
+ }
493
+ )
494
+ ] }, di);
495
+ }),
496
+ activeX !== null && /* @__PURE__ */ jsx(
497
+ "line",
331
498
  {
332
- d: areaPath,
333
- fill: `url(#${gradientId})`,
334
- className: "chart-area",
335
- style: animate ? { animationDelay: "600ms" } : { opacity: 1 }
499
+ x1: activeX,
500
+ y1: PADDING.top,
501
+ x2: activeX,
502
+ y2: PADDING.top + chartH,
503
+ className: "chart-crosshair"
336
504
  }
337
505
  ),
338
506
  /* @__PURE__ */ jsx(
339
- "path",
507
+ "rect",
340
508
  {
341
- ref: (el) => {
342
- lineRefs.current[di] = el;
343
- },
344
- d: linePath,
345
- fill: "none",
346
- stroke: color,
347
- strokeWidth: "2"
509
+ x: PADDING.left,
510
+ y: PADDING.top,
511
+ width: chartW,
512
+ height: chartH,
513
+ fill: "transparent",
514
+ style: { cursor: "crosshair" }
348
515
  }
349
- ),
350
- showPoints && points.map((p, i) => /* @__PURE__ */ jsx(
351
- "circle",
352
- {
353
- cx: p.x,
354
- cy: p.y,
355
- r: "4",
356
- fill: color,
357
- className: "chart-point",
358
- onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${p.v}`),
359
- onMouseMove: onMove,
360
- onMouseLeave: onLeave
361
- },
362
- i
363
- ))
364
- ] }, di);
365
- })
366
- ] });
516
+ )
517
+ ]
518
+ }
519
+ );
367
520
  });
368
521
  CurveChart.displayName = "CurveChart";
369
522
  var BarChart = React.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
@@ -528,30 +681,70 @@ var PieDonutChart = React.memo(
528
681
  }
529
682
  );
530
683
  PieDonutChart.displayName = "PieDonutChart";
531
- var TooltipBubble = ({ x, y, containerWidth, children }) => {
684
+ var ChartTooltipPortal = ({ clientX, clientY, visible, children }) => {
532
685
  const ref = React.useRef(null);
533
- const [adjustedX, setAdjustedX] = React.useState(x);
534
- React.useEffect(() => {
686
+ const [pos, setPos] = React.useState({ left: 0, top: 0 });
687
+ React.useLayoutEffect(() => {
535
688
  const el = ref.current;
536
689
  if (!el) return;
537
690
  const w = el.offsetWidth;
538
- const half = w / 2;
539
- const margin = 8;
540
- let nx = x;
541
- if (x - half < margin) nx = half + margin;
542
- else if (x + half > containerWidth - margin) nx = containerWidth - half - margin;
543
- setAdjustedX(nx);
544
- }, [x, containerWidth]);
691
+ const h = el.offsetHeight;
692
+ const vw = window.innerWidth;
693
+ let left = clientX + TOOLTIP_OFFSET;
694
+ let top = clientY - h - TOOLTIP_OFFSET;
695
+ if (left + w > vw - 8) left = clientX - w - TOOLTIP_OFFSET;
696
+ if (top < 8) top = clientY + TOOLTIP_OFFSET;
697
+ if (left < 8) left = 8;
698
+ setPos({ left, top });
699
+ }, [clientX, clientY]);
545
700
  return /* @__PURE__ */ jsx(
546
701
  "div",
547
702
  {
548
703
  ref,
549
- className: "chart-tooltip",
550
- style: { left: adjustedX, top: y },
704
+ className: `chart-tooltip ${visible ? "chart-tooltip-show" : "chart-tooltip-hide"}`,
705
+ style: { position: "fixed", left: pos.left, top: pos.top, zIndex: 1100 },
551
706
  children
552
707
  }
553
708
  );
554
709
  };
710
+ var ChartLegend = ({ data, labels, type }) => {
711
+ const entries = Object.entries(data);
712
+ if (type === "pie" || type === "doughnut") {
713
+ const values = entries.flatMap(([, v]) => v);
714
+ const total = values.reduce((a, b) => a + b, 0) || 1;
715
+ const firstKey = entries[0]?.[0] ?? "";
716
+ const colorOffset = hashString(firstKey);
717
+ return /* @__PURE__ */ jsx("div", { className: "chart-legend", children: values.map((v, i) => {
718
+ const pct = Math.round(v / total * 100);
719
+ const color = PIE_COLORS[(i + colorOffset) % PIE_COLORS.length];
720
+ return /* @__PURE__ */ jsxs("div", { className: "chart-legend-item", children: [
721
+ /* @__PURE__ */ jsx("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
722
+ /* @__PURE__ */ jsxs("div", { className: "chart-legend-text", children: [
723
+ /* @__PURE__ */ jsx("span", { className: "chart-legend-label", children: labels[i] || `${i + 1}` }),
724
+ /* @__PURE__ */ jsxs("span", { className: "chart-legend-value", children: [
725
+ v.toLocaleString(),
726
+ "(",
727
+ pct,
728
+ "%)"
729
+ ] })
730
+ ] })
731
+ ] }, i);
732
+ }) });
733
+ }
734
+ return /* @__PURE__ */ jsx("div", { className: "chart-legend", children: entries.map(([key], di) => {
735
+ const palette = getPalette(LINE_BAR_PALETTES, di, key);
736
+ const color = palette[2];
737
+ const values = entries[di][1];
738
+ const sum = values.reduce((a, b) => a + b, 0);
739
+ return /* @__PURE__ */ jsxs("div", { className: "chart-legend-item", children: [
740
+ /* @__PURE__ */ jsx("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
741
+ /* @__PURE__ */ jsxs("div", { className: "chart-legend-text", children: [
742
+ /* @__PURE__ */ jsx("span", { className: "chart-legend-label", children: key }),
743
+ /* @__PURE__ */ jsx("span", { className: "chart-legend-value", children: sum.toLocaleString() })
744
+ ] })
745
+ ] }, di);
746
+ }) });
747
+ };
555
748
  var Chart = React.memo((props) => {
556
749
  const { type, data, labels, tooltip: showTooltip = true } = props;
557
750
  const { tooltip, show, hide, move, containerRef } = useChartTooltip(showTooltip);
@@ -567,7 +760,8 @@ var Chart = React.memo((props) => {
567
760
  ready && type === "bar" && /* @__PURE__ */ jsx(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
568
761
  ready && type === "pie" && /* @__PURE__ */ jsx(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
569
762
  ready && type === "doughnut" && /* @__PURE__ */ jsx(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
570
- tooltip.visible && /* @__PURE__ */ jsx(TooltipBubble, { x: tooltip.x, y: tooltip.y, containerWidth: width, children: tooltip.content })
763
+ ready && (type === "bar" || type === "pie" || type === "doughnut") && /* @__PURE__ */ jsx(ChartLegend, { data: stableData, labels: stableLabels, type }),
764
+ tooltip.content && /* @__PURE__ */ jsx(ChartTooltipPortal, { clientX: tooltip.clientX, clientY: tooltip.clientY, visible: tooltip.visible, children: tooltip.content })
571
765
  ] });
572
766
  });
573
767
  Chart.displayName = "Chart";
@@ -5,6 +5,10 @@
5
5
  height: 100%;
6
6
  position: relative;
7
7
  overflow: auto;
8
+ scrollbar-width: none;
9
+ }
10
+ .lib-xplat-table-wrapper::-webkit-scrollbar {
11
+ display: none;
8
12
  }
9
13
  .lib-xplat-table-wrapper.sm > .lib-xplat-table > thead > tr > th,
10
14
  .lib-xplat-table-wrapper.sm > .lib-xplat-table > thead > tr > td,