@underverse-ui/underverse 1.0.14 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -17244,29 +17244,52 @@ var import_react25 = require("react");
17244
17244
  var import_react24 = require("react");
17245
17245
  var import_react_dom6 = require("react-dom");
17246
17246
  var import_jsx_runtime55 = require("react/jsx-runtime");
17247
- function ChartTooltip({ x, y, visible, label, value, color, secondaryLabel, secondaryValue, items, containerRef }) {
17247
+ function ChartTooltip({
17248
+ x,
17249
+ y,
17250
+ visible,
17251
+ label,
17252
+ value,
17253
+ color,
17254
+ secondaryLabel,
17255
+ secondaryValue,
17256
+ items,
17257
+ containerRef,
17258
+ formatter
17259
+ }) {
17248
17260
  const [isMounted, setIsMounted] = (0, import_react24.useState)(false);
17249
17261
  const [position, setPosition] = (0, import_react24.useState)(null);
17262
+ const tooltipRef = (0, import_react24.useRef)(null);
17250
17263
  (0, import_react24.useEffect)(() => {
17251
17264
  setIsMounted(true);
17252
17265
  }, []);
17253
17266
  (0, import_react24.useEffect)(() => {
17254
17267
  if (visible && containerRef?.current) {
17255
17268
  const rect = containerRef.current.getBoundingClientRect();
17256
- setPosition({
17257
- top: rect.top + y,
17258
- left: rect.left + x
17259
- });
17269
+ const tw = tooltipRef.current?.offsetWidth ?? 160;
17270
+ const th = tooltipRef.current?.offsetHeight ?? 80;
17271
+ let left = rect.left + x + 12;
17272
+ let top = rect.top + y - th / 2;
17273
+ if (left + tw > window.innerWidth - 8) {
17274
+ left = rect.left + x - tw - 12;
17275
+ }
17276
+ if (top + th > window.innerHeight - 8) {
17277
+ top = window.innerHeight - th - 8;
17278
+ }
17279
+ if (top < 8) top = 8;
17280
+ if (left < 8) left = 8;
17281
+ setPosition({ top, left });
17260
17282
  }
17261
17283
  }, [visible, x, y, containerRef]);
17262
17284
  if (!visible || !isMounted || !position) return null;
17263
17285
  const tooltipContent = /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
17264
17286
  "div",
17265
17287
  {
17288
+ ref: tooltipRef,
17266
17289
  style: {
17267
17290
  position: "fixed",
17268
17291
  top: position.top,
17269
- left: position.left + 12,
17292
+ left: position.left,
17270
17293
  zIndex: 99999,
17271
17294
  pointerEvents: "none",
17272
17295
  animation: "chartTooltipFadeIn 0.15s ease-out"
@@ -17289,11 +17312,11 @@ function ChartTooltip({ x, y, visible, label, value, color, secondaryLabel, seco
17289
17312
  item.label,
17290
17313
  ":"
17291
17314
  ] }),
17292
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "font-semibold ml-auto", children: item.value })
17315
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "font-semibold ml-auto", children: formatter ? formatter(item.value) : item.value })
17293
17316
  ] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_jsx_runtime55.Fragment, { children: [
17294
17317
  /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex items-center gap-2", children: [
17295
17318
  color && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "w-2 h-2 rounded-full shrink-0", style: { backgroundColor: color } }),
17296
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "font-semibold", children: value })
17319
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "font-semibold", children: formatter && value != null ? formatter(value) : value })
17297
17320
  ] }),
17298
17321
  secondaryLabel && /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "text-muted-foreground text-xs mt-1", children: [
17299
17322
  secondaryLabel,
@@ -17316,10 +17339,39 @@ function ChartTooltip({ x, y, visible, label, value, color, secondaryLabel, seco
17316
17339
  return (0, import_react_dom6.createPortal)(tooltipContent, document.body);
17317
17340
  }
17318
17341
 
17342
+ // ../../components/ui/chart-utils.ts
17343
+ function getCatmullRomSpline(points, tension = 0.5) {
17344
+ if (points.length < 2) return "";
17345
+ if (points.length === 2) {
17346
+ return `M ${points[0].x} ${points[0].y} L ${points[1].x} ${points[1].y}`;
17347
+ }
17348
+ let path = `M ${points[0].x} ${points[0].y}`;
17349
+ for (let i = 0; i < points.length - 1; i++) {
17350
+ const p0 = points[Math.max(0, i - 1)];
17351
+ const p1 = points[i];
17352
+ const p2 = points[i + 1];
17353
+ const p3 = points[Math.min(points.length - 1, i + 2)];
17354
+ const cp1x = p1.x + (p2.x - p0.x) * tension / 6;
17355
+ const cp1y = p1.y + (p2.y - p0.y) * tension / 6;
17356
+ const cp2x = p2.x - (p3.x - p1.x) * tension / 6;
17357
+ const cp2y = p2.y - (p3.y - p1.y) * tension / 6;
17358
+ path += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${p2.x} ${p2.y}`;
17359
+ }
17360
+ return path;
17361
+ }
17362
+ function getPathLength(points) {
17363
+ return points.reduce((acc, p, i) => {
17364
+ if (i === 0) return 0;
17365
+ const prev = points[i - 1];
17366
+ return acc + Math.sqrt((p.x - prev.x) ** 2 + (p.y - prev.y) ** 2);
17367
+ }, 0);
17368
+ }
17369
+
17319
17370
  // ../../components/ui/LineChart.tsx
17320
17371
  var import_jsx_runtime56 = require("react/jsx-runtime");
17321
17372
  function LineChart({
17322
17373
  data,
17374
+ series,
17323
17375
  width = 400,
17324
17376
  height = 200,
17325
17377
  color = "currentColor",
@@ -17328,50 +17380,63 @@ function LineChart({
17328
17380
  showGrid = true,
17329
17381
  showLabels = true,
17330
17382
  showValues = false,
17383
+ showLegend,
17331
17384
  animated = true,
17332
17385
  curved = true,
17386
+ formatValue,
17387
+ referenceLines = [],
17333
17388
  className = ""
17334
17389
  }) {
17335
17390
  const svgRef = (0, import_react25.useRef)(null);
17391
+ const clipId = (0, import_react25.useRef)(`line-clip-${Math.random().toString(36).slice(2, 8)}`).current;
17336
17392
  const padding = { top: 20, right: 20, bottom: 40, left: 40 };
17337
17393
  const chartWidth = width - padding.left - padding.right;
17338
17394
  const chartHeight = height - padding.top - padding.bottom;
17339
17395
  const [hoveredPoint, setHoveredPoint] = (0, import_react25.useState)(null);
17340
- const { minValue, maxValue, points, linePath, areaPath } = (0, import_react25.useMemo)(() => {
17341
- if (!data.length) return { minValue: 0, maxValue: 0, points: [], linePath: "", areaPath: "" };
17342
- const values = data.map((d) => d.value);
17343
- const min = Math.min(...values);
17344
- const max = Math.max(...values);
17396
+ const [hiddenSeries, setHiddenSeries] = (0, import_react25.useState)(/* @__PURE__ */ new Set());
17397
+ const toggleSeries = (0, import_react25.useCallback)((name) => {
17398
+ setHiddenSeries((prev) => {
17399
+ const next = new Set(prev);
17400
+ if (next.has(name)) next.delete(name);
17401
+ else next.add(name);
17402
+ return next;
17403
+ });
17404
+ }, []);
17405
+ const normalizedSeries = (0, import_react25.useMemo)(() => {
17406
+ if (series && series.length > 0) return series;
17407
+ if (data && data.length > 0) return [{ name: "", data, color, fillColor }];
17408
+ return [];
17409
+ }, [series, data, color, fillColor]);
17410
+ const isMultiSeries = normalizedSeries.length > 1;
17411
+ const { minValue, maxValue, processedSeries, labels } = (0, import_react25.useMemo)(() => {
17412
+ if (!normalizedSeries.length || !normalizedSeries[0]?.data?.length) {
17413
+ return { minValue: 0, maxValue: 0, processedSeries: [], labels: [] };
17414
+ }
17415
+ const allLabels = normalizedSeries[0].data.map((d) => d.label);
17416
+ const allValues = normalizedSeries.flatMap((s) => s.data.map((d) => d.value));
17417
+ const min = Math.min(...allValues);
17418
+ const max = Math.max(...allValues);
17345
17419
  const range = max - min || 1;
17346
- const pts = data.map((d, i) => ({
17347
- x: padding.left + i / (data.length - 1 || 1) * chartWidth,
17348
- y: padding.top + chartHeight - (d.value - min) / range * chartHeight,
17349
- ...d
17350
- }));
17351
- let path = "";
17352
- let area = "";
17353
- if (curved && pts.length > 2) {
17354
- path = `M ${pts[0].x} ${pts[0].y}`;
17355
- area = `M ${pts[0].x} ${padding.top + chartHeight} L ${pts[0].x} ${pts[0].y}`;
17356
- for (let i = 0; i < pts.length - 1; i++) {
17357
- const p0 = pts[Math.max(0, i - 1)];
17358
- const p1 = pts[i];
17359
- const p2 = pts[i + 1];
17360
- const p3 = pts[Math.min(pts.length - 1, i + 2)];
17361
- const cp1x = p1.x + (p2.x - p0.x) / 6;
17362
- const cp1y = p1.y + (p2.y - p0.y) / 6;
17363
- const cp2x = p2.x - (p3.x - p1.x) / 6;
17364
- const cp2y = p2.y - (p3.y - p1.y) / 6;
17365
- path += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${p2.x} ${p2.y}`;
17366
- area += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${p2.x} ${p2.y}`;
17367
- }
17368
- area += ` L ${pts[pts.length - 1].x} ${padding.top + chartHeight} Z`;
17369
- } else {
17370
- path = pts.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`).join(" ");
17371
- area = `M ${pts[0].x} ${padding.top + chartHeight} ` + pts.map((p) => `L ${p.x} ${p.y}`).join(" ") + ` L ${pts[pts.length - 1].x} ${padding.top + chartHeight} Z`;
17372
- }
17373
- return { minValue: min, maxValue: max, points: pts, linePath: path, areaPath: area };
17374
- }, [data, chartWidth, chartHeight, curved, padding.left, padding.top]);
17420
+ const processed = normalizedSeries.map((s) => {
17421
+ const pts = s.data.map((d, i) => ({
17422
+ x: padding.left + i / (s.data.length - 1 || 1) * chartWidth,
17423
+ y: padding.top + chartHeight - (d.value - min) / range * chartHeight,
17424
+ ...d
17425
+ }));
17426
+ let linePath = "";
17427
+ let areaPath = "";
17428
+ if (curved && pts.length > 2) {
17429
+ linePath = getCatmullRomSpline(pts);
17430
+ areaPath = `${linePath} L ${pts[pts.length - 1].x} ${padding.top + chartHeight} L ${pts[0].x} ${padding.top + chartHeight} Z`;
17431
+ } else {
17432
+ linePath = pts.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`).join(" ");
17433
+ areaPath = `M ${pts[0].x} ${padding.top + chartHeight} ` + pts.map((p) => `L ${p.x} ${p.y}`).join(" ") + ` L ${pts[pts.length - 1].x} ${padding.top + chartHeight} Z`;
17434
+ }
17435
+ const lineLength = getPathLength(pts);
17436
+ return { ...s, points: pts, linePath, areaPath, lineLength };
17437
+ });
17438
+ return { minValue: min, maxValue: max, processedSeries: processed, labels: allLabels };
17439
+ }, [normalizedSeries, chartWidth, chartHeight, curved, padding.left, padding.top]);
17375
17440
  const gridLines = (0, import_react25.useMemo)(() => {
17376
17441
  const lines = [];
17377
17442
  const steps = 5;
@@ -17384,84 +17449,134 @@ function LineChart({
17384
17449
  }, [minValue, maxValue, chartHeight, padding.top]);
17385
17450
  return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_jsx_runtime56.Fragment, { children: [
17386
17451
  /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("svg", { ref: svgRef, width, height, className: `overflow-visible ${className}`, style: { fontFamily: "inherit" }, children: [
17452
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("clipPath", { id: clipId, children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("rect", { x: padding.left, y: padding.top, width: chartWidth, height: chartHeight }) }) }),
17387
17453
  showGrid && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("g", { className: "text-muted-foreground/20", children: gridLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("g", { children: [
17388
17454
  /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("line", { x1: padding.left, y1: line.y, x2: width - padding.right, y2: line.y, stroke: "currentColor", strokeDasharray: "4 4" }),
17389
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("text", { x: padding.left - 8, y: line.y + 4, textAnchor: "end", fontSize: "10", fill: "currentColor", className: "text-muted-foreground", children: line.value.toFixed(0) })
17455
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("text", { x: padding.left - 8, y: line.y + 4, textAnchor: "end", fontSize: "10", fill: "currentColor", className: "text-muted-foreground", children: formatValue ? formatValue(line.value) : line.value.toFixed(0) })
17390
17456
  ] }, i)) }),
17391
- fillColor && areaPath && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("path", { d: areaPath, fill: fillColor, opacity: 0.2, className: animated ? "animate-[fadeIn_0.6s_ease-out]" : "" }),
17392
- linePath && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
17393
- "path",
17394
- {
17395
- d: linePath,
17396
- fill: "none",
17397
- stroke: color,
17398
- strokeWidth: 2,
17399
- strokeLinecap: "round",
17400
- strokeLinejoin: "round",
17401
- className: animated ? "animate-[drawLine_1s_ease-out]" : "",
17402
- style: animated ? {
17403
- strokeDasharray: 1e3,
17404
- strokeDashoffset: 0,
17405
- animation: "drawLine 1s ease-out forwards"
17406
- } : void 0
17407
- }
17408
- ),
17409
- showDots && points.map((point, i) => /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
17410
- "g",
17411
- {
17412
- onMouseEnter: () => setHoveredPoint({ x: point.x, y: point.y, label: point.label, value: point.value }),
17413
- onMouseLeave: () => setHoveredPoint(null),
17414
- className: "cursor-pointer",
17415
- children: [
17416
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
17417
- "circle",
17418
- {
17419
- cx: point.x,
17420
- cy: point.y,
17421
- r: hoveredPoint?.x === point.x ? 6 : 4,
17422
- fill: color,
17423
- className: `transition-all duration-150 ${animated ? "animate-[scaleIn_0.3s_ease-out]" : ""}`,
17424
- style: animated ? { animationDelay: `${i * 0.05}s`, animationFillMode: "both" } : void 0
17425
- }
17426
- ),
17427
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("circle", { cx: point.x, cy: point.y, r: 16, fill: "transparent" }),
17428
- showValues && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("text", { x: point.x, y: point.y - 12, textAnchor: "middle", fontSize: "10", fontWeight: "500", className: "text-foreground", fill: "currentColor", children: point.value })
17429
- ]
17430
- },
17431
- i
17432
- )),
17433
- hoveredPoint && /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("g", { className: "pointer-events-none", children: [
17434
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
17435
- "line",
17436
- {
17437
- x1: hoveredPoint.x,
17438
- y1: padding.top,
17439
- x2: hoveredPoint.x,
17440
- y2: padding.top + chartHeight,
17441
- stroke: color,
17442
- strokeWidth: 1,
17443
- strokeDasharray: "4 4",
17444
- opacity: 0.5
17445
- }
17457
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("g", { clipPath: `url(#${clipId})`, children: [
17458
+ processedSeries.map(
17459
+ (s, si) => s.fillColor && s.areaPath && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
17460
+ "path",
17461
+ {
17462
+ d: s.areaPath,
17463
+ fill: s.fillColor,
17464
+ className: "transition-opacity duration-300",
17465
+ opacity: hiddenSeries.has(s.name) ? 0 : 0.15,
17466
+ style: animated ? { opacity: 0, animation: `fadeIn 0.6s ease-out ${si * 0.1}s forwards` } : void 0
17467
+ },
17468
+ `area-${si}`
17469
+ )
17446
17470
  ),
17447
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
17448
- "line",
17471
+ processedSeries.map((s, si) => /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
17472
+ "path",
17449
17473
  {
17450
- x1: padding.left,
17451
- y1: hoveredPoint.y,
17452
- x2: padding.left + chartWidth,
17453
- y2: hoveredPoint.y,
17454
- stroke: color,
17455
- strokeWidth: 1,
17456
- strokeDasharray: "4 4",
17457
- opacity: 0.5
17458
- }
17459
- )
17474
+ d: s.linePath,
17475
+ fill: "none",
17476
+ stroke: s.color,
17477
+ strokeWidth: 2,
17478
+ strokeLinecap: "round",
17479
+ strokeLinejoin: "round",
17480
+ className: "transition-opacity duration-300",
17481
+ opacity: hiddenSeries.has(s.name) ? 0 : 1,
17482
+ style: animated ? {
17483
+ strokeDasharray: s.lineLength,
17484
+ strokeDashoffset: s.lineLength,
17485
+ animation: `drawLine 1s ease-out ${si * 0.15}s forwards`
17486
+ } : void 0
17487
+ },
17488
+ `line-${si}`
17489
+ ))
17460
17490
  ] }),
17461
- showLabels && points.map((point, i) => /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("text", { x: point.x, y: height - 10, textAnchor: "middle", fontSize: "10", className: "text-muted-foreground", fill: "currentColor", children: point.label }, i)),
17491
+ showDots && processedSeries.map(
17492
+ (s, si) => !hiddenSeries.has(s.name) && s.points.map((point, i) => /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
17493
+ "g",
17494
+ {
17495
+ onMouseEnter: () => {
17496
+ if (isMultiSeries) {
17497
+ const items = processedSeries.filter((ps) => !hiddenSeries.has(ps.name)).map((ps) => ({
17498
+ label: ps.name,
17499
+ value: ps.points[i]?.value ?? 0,
17500
+ color: ps.color
17501
+ }));
17502
+ setHoveredPoint({ x: point.x, y: point.y, label: point.label, value: point.value, items });
17503
+ } else {
17504
+ setHoveredPoint({ x: point.x, y: point.y, label: point.label, value: point.value });
17505
+ }
17506
+ },
17507
+ onMouseLeave: () => setHoveredPoint(null),
17508
+ className: "cursor-pointer",
17509
+ children: [
17510
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
17511
+ "circle",
17512
+ {
17513
+ cx: point.x,
17514
+ cy: point.y,
17515
+ r: hoveredPoint?.x === point.x ? 6 : 4,
17516
+ fill: s.color,
17517
+ className: `transition-all duration-150 ${animated ? "animate-[scaleIn_0.3s_ease-out]" : ""}`,
17518
+ style: animated ? { animationDelay: `${si * 0.15 + i * 0.05}s`, animationFillMode: "both" } : void 0
17519
+ }
17520
+ ),
17521
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("circle", { cx: point.x, cy: point.y, r: 16, fill: "transparent" }),
17522
+ showValues && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
17523
+ "text",
17524
+ {
17525
+ x: point.x,
17526
+ y: point.y - 12,
17527
+ textAnchor: "middle",
17528
+ fontSize: "10",
17529
+ fontWeight: "500",
17530
+ className: "text-foreground",
17531
+ fill: "currentColor",
17532
+ children: formatValue ? formatValue(point.value) : point.value
17533
+ }
17534
+ )
17535
+ ]
17536
+ },
17537
+ `dot-${si}-${i}`
17538
+ ))
17539
+ ),
17540
+ referenceLines.map((ref, i) => {
17541
+ const range = maxValue - minValue || 1;
17542
+ const y = padding.top + chartHeight - (ref.value - minValue) / range * chartHeight;
17543
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("g", { className: "pointer-events-none", children: [
17544
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
17545
+ "line",
17546
+ {
17547
+ x1: padding.left,
17548
+ y1: y,
17549
+ x2: padding.left + chartWidth,
17550
+ y2: y,
17551
+ stroke: ref.color || "#ef4444",
17552
+ strokeWidth: 1.5,
17553
+ strokeDasharray: ref.strokeDasharray || "6 4",
17554
+ opacity: 0.7
17555
+ }
17556
+ ),
17557
+ ref.label && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("text", { x: padding.left + chartWidth + 4, y: y + 4, fontSize: "10", fill: ref.color || "#ef4444", fontWeight: "500", children: ref.label })
17558
+ ] }, `ref-${i}`);
17559
+ }),
17560
+ hoveredPoint && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("g", { className: "pointer-events-none", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
17561
+ "line",
17562
+ {
17563
+ x1: hoveredPoint.x,
17564
+ y1: padding.top,
17565
+ x2: hoveredPoint.x,
17566
+ y2: padding.top + chartHeight,
17567
+ stroke: "currentColor",
17568
+ strokeWidth: 1,
17569
+ strokeDasharray: "4 4",
17570
+ opacity: 0.3,
17571
+ className: "text-foreground"
17572
+ }
17573
+ ) }),
17574
+ showLabels && labels.map((lbl, i) => {
17575
+ const x = padding.left + i / (labels.length - 1 || 1) * chartWidth;
17576
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("text", { x, y: height - 10, textAnchor: "middle", fontSize: "10", className: "text-muted-foreground", fill: "currentColor", children: lbl }, i);
17577
+ }),
17462
17578
  /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("style", { children: `
17463
17579
  @keyframes drawLine {
17464
- from { stroke-dashoffset: 1000; }
17465
17580
  to { stroke-dashoffset: 0; }
17466
17581
  }
17467
17582
  @keyframes scaleIn {
@@ -17470,10 +17585,20 @@ function LineChart({
17470
17585
  }
17471
17586
  @keyframes fadeIn {
17472
17587
  from { opacity: 0; }
17473
- to { opacity: 0.2; }
17588
+ to { opacity: 0.15; }
17474
17589
  }
17475
17590
  ` })
17476
17591
  ] }),
17592
+ (showLegend ?? isMultiSeries) && isMultiSeries && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "flex items-center justify-center gap-6 mt-2", children: normalizedSeries.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "flex items-center gap-2 text-sm cursor-pointer select-none", onClick: () => toggleSeries(s.name), children: [
17593
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
17594
+ "div",
17595
+ {
17596
+ className: "w-3 h-3 rounded-md transition-opacity",
17597
+ style: { backgroundColor: s.color, opacity: hiddenSeries.has(s.name) ? 0.3 : 1 }
17598
+ }
17599
+ ),
17600
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: hiddenSeries.has(s.name) ? "text-muted-foreground/40 line-through" : "text-muted-foreground", children: s.name })
17601
+ ] }, i)) }),
17477
17602
  /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
17478
17603
  ChartTooltip,
17479
17604
  {
@@ -17481,9 +17606,11 @@ function LineChart({
17481
17606
  y: hoveredPoint?.y ?? 0,
17482
17607
  visible: !!hoveredPoint,
17483
17608
  label: hoveredPoint?.label,
17484
- value: hoveredPoint?.value,
17485
- color,
17486
- containerRef: svgRef
17609
+ value: !isMultiSeries ? hoveredPoint?.value : void 0,
17610
+ items: isMultiSeries ? hoveredPoint?.items : void 0,
17611
+ color: !isMultiSeries ? processedSeries[0]?.color || color : void 0,
17612
+ containerRef: svgRef,
17613
+ formatter: formatValue ? (v) => formatValue(Number(v)) : void 0
17487
17614
  }
17488
17615
  )
17489
17616
  ] });
@@ -17494,16 +17621,20 @@ var import_react26 = require("react");
17494
17621
  var import_jsx_runtime57 = require("react/jsx-runtime");
17495
17622
  function BarChart({
17496
17623
  data,
17624
+ series,
17497
17625
  width = 400,
17498
17626
  height = 200,
17499
17627
  color = "currentColor",
17500
17628
  showLabels = true,
17501
17629
  showValues = true,
17502
17630
  showGrid = true,
17631
+ showLegend,
17503
17632
  horizontal = false,
17504
17633
  animated = true,
17634
+ stacked = false,
17505
17635
  barRadius = 4,
17506
17636
  barGap = 0.3,
17637
+ formatValue,
17507
17638
  className = ""
17508
17639
  }) {
17509
17640
  const svgRef = (0, import_react26.useRef)(null);
@@ -17511,31 +17642,109 @@ function BarChart({
17511
17642
  const chartWidth = width - padding.left - padding.right;
17512
17643
  const chartHeight = height - padding.top - padding.bottom;
17513
17644
  const [hoveredBar, setHoveredBar] = (0, import_react26.useState)(null);
17514
- const { maxValue, bars, gridLines } = (0, import_react26.useMemo)(() => {
17515
- if (!data.length) return { maxValue: 0, bars: [], gridLines: [] };
17516
- const max = Math.max(...data.map((d) => d.value));
17517
- const barCount = data.length;
17518
- const barsData = data.map((d, i) => {
17645
+ const normalizedSeries = (0, import_react26.useMemo)(() => {
17646
+ if (series && series.length > 0) return series;
17647
+ if (data && data.length > 0) return [{ name: "", data, color }];
17648
+ return [];
17649
+ }, [series, data, color]);
17650
+ const isMultiSeries = normalizedSeries.length > 1;
17651
+ const { maxValue, barGroups, gridLines } = (0, import_react26.useMemo)(() => {
17652
+ if (!normalizedSeries.length || !normalizedSeries[0]?.data?.length) {
17653
+ return { maxValue: 0, barGroups: [], gridLines: [] };
17654
+ }
17655
+ const allLabels = normalizedSeries[0].data.map((d) => d.label);
17656
+ const seriesCount = normalizedSeries.length;
17657
+ const labelCount = allLabels.length;
17658
+ let max = 0;
17659
+ if (stacked) {
17660
+ for (let li = 0; li < labelCount; li++) {
17661
+ const stackVal = normalizedSeries.reduce((sum, s) => sum + (s.data[li]?.value || 0), 0);
17662
+ max = Math.max(max, stackVal);
17663
+ }
17664
+ } else {
17665
+ max = Math.max(...normalizedSeries.flatMap((s) => s.data.map((d) => d.value)));
17666
+ }
17667
+ const groups = allLabels.map((label, li) => {
17519
17668
  if (horizontal) {
17520
- const barHeight = chartHeight / barCount * (1 - barGap);
17521
- const gap = chartHeight / barCount * barGap;
17522
- return {
17523
- x: padding.left,
17524
- y: padding.top + i * (barHeight + gap) + gap / 2,
17525
- width: d.value / max * chartWidth,
17526
- height: barHeight,
17527
- ...d
17528
- };
17669
+ const groupH = chartHeight / labelCount;
17670
+ const gap = groupH * barGap;
17671
+ const usable = groupH - gap;
17672
+ if (stacked) {
17673
+ let cum = 0;
17674
+ const bars = normalizedSeries.map((s) => {
17675
+ const val = s.data[li]?.value || 0;
17676
+ const w = val / max * chartWidth;
17677
+ const bar = {
17678
+ x: padding.left + cum,
17679
+ y: padding.top + li * groupH + gap / 2,
17680
+ width: w,
17681
+ height: usable,
17682
+ value: val,
17683
+ color: s.data[li]?.color || s.color,
17684
+ seriesName: s.name,
17685
+ label
17686
+ };
17687
+ cum += w;
17688
+ return bar;
17689
+ });
17690
+ return { label, bars };
17691
+ } else {
17692
+ const bH = usable / seriesCount;
17693
+ const bars = normalizedSeries.map((s, si) => {
17694
+ const val = s.data[li]?.value || 0;
17695
+ return {
17696
+ x: padding.left,
17697
+ y: padding.top + li * groupH + gap / 2 + si * bH,
17698
+ width: val / max * chartWidth,
17699
+ height: bH,
17700
+ value: val,
17701
+ color: s.data[li]?.color || s.color,
17702
+ seriesName: s.name,
17703
+ label
17704
+ };
17705
+ });
17706
+ return { label, bars };
17707
+ }
17529
17708
  } else {
17530
- const barWidth = chartWidth / barCount * (1 - barGap);
17531
- const gap = chartWidth / barCount * barGap;
17532
- return {
17533
- x: padding.left + i * (barWidth + gap) + gap / 2,
17534
- y: padding.top + chartHeight - d.value / max * chartHeight,
17535
- width: barWidth,
17536
- height: d.value / max * chartHeight,
17537
- ...d
17538
- };
17709
+ const groupW = chartWidth / labelCount;
17710
+ const gap = groupW * barGap;
17711
+ const usable = groupW - gap;
17712
+ if (stacked) {
17713
+ let cum = 0;
17714
+ const bars = normalizedSeries.map((s) => {
17715
+ const val = s.data[li]?.value || 0;
17716
+ const h = val / max * chartHeight;
17717
+ const bar = {
17718
+ x: padding.left + li * groupW + gap / 2,
17719
+ y: padding.top + chartHeight - cum - h,
17720
+ width: usable,
17721
+ height: h,
17722
+ value: val,
17723
+ color: s.data[li]?.color || s.color,
17724
+ seriesName: s.name,
17725
+ label
17726
+ };
17727
+ cum += h;
17728
+ return bar;
17729
+ });
17730
+ return { label, bars };
17731
+ } else {
17732
+ const bW = usable / seriesCount;
17733
+ const bars = normalizedSeries.map((s, si) => {
17734
+ const val = s.data[li]?.value || 0;
17735
+ return {
17736
+ x: padding.left + li * groupW + gap / 2 + si * bW,
17737
+ y: padding.top + chartHeight - val / max * chartHeight,
17738
+ width: bW,
17739
+ height: val / max * chartHeight,
17740
+ value: val,
17741
+ color: s.data[li]?.color || s.color,
17742
+ seriesName: s.name,
17743
+ label
17744
+ };
17745
+ });
17746
+ return { label, bars };
17747
+ }
17539
17748
  }
17540
17749
  });
17541
17750
  const lines = [];
@@ -17543,106 +17752,102 @@ function BarChart({
17543
17752
  for (let i = 0; i <= steps; i++) {
17544
17753
  const value = i / steps * max;
17545
17754
  if (horizontal) {
17546
- lines.push({
17547
- x: padding.left + i / steps * chartWidth,
17548
- y1: padding.top,
17549
- y2: height - padding.bottom,
17550
- value
17551
- });
17755
+ lines.push({ x: padding.left + i / steps * chartWidth, y1: padding.top, y2: height - padding.bottom, value });
17552
17756
  } else {
17553
- lines.push({
17554
- y: padding.top + chartHeight - i / steps * chartHeight,
17555
- x1: padding.left,
17556
- x2: width - padding.right,
17557
- value
17558
- });
17757
+ lines.push({ y: padding.top + chartHeight - i / steps * chartHeight, x1: padding.left, x2: width - padding.right, value });
17559
17758
  }
17560
17759
  }
17561
- return { maxValue: max, bars: barsData, gridLines: lines };
17562
- }, [data, chartWidth, chartHeight, horizontal, barGap, padding, width, height]);
17760
+ return { maxValue: max, barGroups: groups, gridLines: lines };
17761
+ }, [normalizedSeries, chartWidth, chartHeight, horizontal, stacked, barGap, padding, width, height]);
17563
17762
  return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
17564
17763
  /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("svg", { ref: svgRef, width, height, className: `overflow-visible ${className}`, style: { fontFamily: "inherit" }, children: [
17565
17764
  showGrid && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("g", { className: "text-muted-foreground/20", children: gridLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("g", { children: horizontal ? /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
17566
17765
  /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("line", { x1: line.x, y1: line.y1, x2: line.x, y2: line.y2, stroke: "currentColor", strokeDasharray: "4 4" }),
17567
- /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("text", { x: line.x, y: height - 8, textAnchor: "middle", fontSize: "10", className: "text-muted-foreground", fill: "currentColor", children: line.value.toFixed(0) })
17766
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("text", { x: line.x, y: height - 8, textAnchor: "middle", fontSize: "10", className: "text-muted-foreground", fill: "currentColor", children: formatValue ? formatValue(line.value) : line.value.toFixed(0) })
17568
17767
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
17569
17768
  /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("line", { x1: line.x1, y1: line.y, x2: line.x2, y2: line.y, stroke: "currentColor", strokeDasharray: "4 4" }),
17570
- /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("text", { x: padding.left - 8, y: line.y + 4, textAnchor: "end", fontSize: "10", className: "text-muted-foreground", fill: "currentColor", children: line.value.toFixed(0) })
17769
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("text", { x: padding.left - 8, y: line.y + 4, textAnchor: "end", fontSize: "10", className: "text-muted-foreground", fill: "currentColor", children: formatValue ? formatValue(line.value) : line.value.toFixed(0) })
17571
17770
  ] }) }, i)) }),
17572
- bars.map((bar, i) => /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
17573
- "g",
17574
- {
17575
- onMouseEnter: () => setHoveredBar({
17576
- x: horizontal ? bar.x + bar.width : bar.x + bar.width / 2,
17577
- y: horizontal ? bar.y + bar.height / 2 : bar.y,
17578
- label: bar.label,
17579
- value: bar.value,
17580
- color: bar.color || color
17581
- }),
17582
- onMouseLeave: () => setHoveredBar(null),
17583
- className: "cursor-pointer",
17584
- children: [
17585
- /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
17586
- "rect",
17587
- {
17588
- x: bar.x,
17589
- y: horizontal ? bar.y : bar.y,
17590
- width: animated && !horizontal ? 0 : bar.width,
17591
- height: animated && horizontal ? bar.height : horizontal ? bar.height : 0,
17592
- rx: barRadius,
17593
- ry: barRadius,
17594
- fill: bar.color || color,
17595
- className: `transition-all duration-150 ${hoveredBar?.label === bar.label ? "opacity-80" : ""}`,
17596
- style: animated ? {
17597
- animation: horizontal ? `growWidth 0.5s ease-out ${i * 0.1}s forwards` : `growHeight 0.5s ease-out ${i * 0.1}s forwards`,
17598
- ...horizontal ? { width: 0 } : { height: 0, y: padding.top + chartHeight }
17599
- } : void 0,
17600
- children: [
17601
- /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
17602
- "animate",
17603
- {
17604
- attributeName: horizontal ? "width" : "height",
17605
- from: "0",
17606
- to: horizontal ? bar.width : bar.height,
17607
- dur: "0.5s",
17608
- begin: `${i * 0.1}s`,
17609
- fill: "freeze"
17610
- }
17611
- ),
17612
- !horizontal && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("animate", { attributeName: "y", from: padding.top + chartHeight, to: bar.y, dur: "0.5s", begin: `${i * 0.1}s`, fill: "freeze" })
17613
- ]
17614
- }
17615
- ),
17616
- showValues && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
17617
- "text",
17618
- {
17619
- x: horizontal ? bar.x + bar.width + 8 : bar.x + bar.width / 2,
17620
- y: horizontal ? bar.y + bar.height / 2 + 4 : bar.y - 8,
17621
- textAnchor: horizontal ? "start" : "middle",
17622
- fontSize: "11",
17623
- fontWeight: "500",
17624
- className: "text-foreground",
17625
- fill: "currentColor",
17626
- style: animated ? { opacity: 0, animation: `fadeIn 0.3s ease-out ${i * 0.1 + 0.3}s forwards` } : void 0,
17627
- children: bar.value
17628
- }
17629
- ),
17630
- showLabels && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
17631
- "text",
17632
- {
17633
- x: horizontal ? padding.left - 8 : bar.x + bar.width / 2,
17634
- y: horizontal ? bar.y + bar.height / 2 + 4 : height - 10,
17635
- textAnchor: horizontal ? "end" : "middle",
17636
- fontSize: "10",
17637
- className: "text-muted-foreground",
17638
- fill: "currentColor",
17639
- children: bar.label
17640
- }
17641
- )
17642
- ]
17643
- },
17644
- i
17645
- )),
17771
+ barGroups.map((group, gi) => /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("g", { children: [
17772
+ group.bars.map((bar, bi) => {
17773
+ const animDelay = gi * 0.08 + bi * 0.04;
17774
+ return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
17775
+ "g",
17776
+ {
17777
+ onMouseEnter: () => setHoveredBar({
17778
+ x: horizontal ? bar.x + bar.width : bar.x + bar.width / 2,
17779
+ y: horizontal ? bar.y + bar.height / 2 : bar.y,
17780
+ label: bar.label,
17781
+ value: bar.value,
17782
+ color: bar.color,
17783
+ seriesName: bar.seriesName
17784
+ }),
17785
+ onMouseLeave: () => setHoveredBar(null),
17786
+ className: "cursor-pointer",
17787
+ children: [
17788
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
17789
+ "rect",
17790
+ {
17791
+ x: bar.x,
17792
+ y: bar.y,
17793
+ width: bar.width,
17794
+ height: bar.height,
17795
+ rx: barRadius,
17796
+ ry: barRadius,
17797
+ fill: bar.color,
17798
+ className: `transition-all duration-150 ${hoveredBar?.label === bar.label && hoveredBar?.seriesName === bar.seriesName ? "opacity-80" : ""}`,
17799
+ style: animated ? {
17800
+ animation: horizontal ? `growWidth 0.5s ease-out ${animDelay}s forwards` : `growHeight 0.5s ease-out ${animDelay}s forwards`,
17801
+ ...horizontal ? { width: 0 } : { height: 0, y: padding.top + chartHeight }
17802
+ } : void 0,
17803
+ children: [
17804
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
17805
+ "animate",
17806
+ {
17807
+ attributeName: horizontal ? "width" : "height",
17808
+ from: "0",
17809
+ to: horizontal ? bar.width : bar.height,
17810
+ dur: "0.5s",
17811
+ begin: `${animDelay}s`,
17812
+ fill: "freeze"
17813
+ }
17814
+ ),
17815
+ !horizontal && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("animate", { attributeName: "y", from: padding.top + chartHeight, to: bar.y, dur: "0.5s", begin: `${animDelay}s`, fill: "freeze" })
17816
+ ]
17817
+ }
17818
+ ),
17819
+ showValues && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
17820
+ "text",
17821
+ {
17822
+ x: horizontal ? bar.x + bar.width + 8 : bar.x + bar.width / 2,
17823
+ y: horizontal ? bar.y + bar.height / 2 + 4 : bar.y - 8,
17824
+ textAnchor: horizontal ? "start" : "middle",
17825
+ fontSize: "11",
17826
+ fontWeight: "500",
17827
+ className: "text-foreground",
17828
+ fill: "currentColor",
17829
+ style: animated ? { opacity: 0, animation: `fadeIn 0.3s ease-out ${animDelay + 0.3}s forwards` } : void 0,
17830
+ children: formatValue ? formatValue(bar.value) : bar.value
17831
+ }
17832
+ )
17833
+ ]
17834
+ },
17835
+ bi
17836
+ );
17837
+ }),
17838
+ showLabels && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
17839
+ "text",
17840
+ {
17841
+ x: horizontal ? padding.left - 8 : padding.left + (gi + 0.5) * (chartWidth / barGroups.length),
17842
+ y: horizontal ? padding.top + (gi + 0.5) * (chartHeight / barGroups.length) + 4 : height - 10,
17843
+ textAnchor: horizontal ? "end" : "middle",
17844
+ fontSize: "10",
17845
+ className: "text-muted-foreground",
17846
+ fill: "currentColor",
17847
+ children: group.label
17848
+ }
17849
+ )
17850
+ ] }, gi)),
17646
17851
  /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("style", { children: `
17647
17852
  @keyframes growHeight {
17648
17853
  from { height: 0; }
@@ -17656,16 +17861,21 @@ function BarChart({
17656
17861
  }
17657
17862
  ` })
17658
17863
  ] }),
17864
+ (showLegend ?? isMultiSeries) && isMultiSeries && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { className: "flex items-center justify-center gap-6 mt-2", children: normalizedSeries.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: "flex items-center gap-2 text-sm", children: [
17865
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { className: "w-3 h-3 rounded-md", style: { backgroundColor: s.color } }),
17866
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("span", { className: "text-muted-foreground", children: s.name })
17867
+ ] }, i)) }),
17659
17868
  /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
17660
17869
  ChartTooltip,
17661
17870
  {
17662
17871
  x: hoveredBar?.x ?? 0,
17663
17872
  y: hoveredBar?.y ?? 0,
17664
17873
  visible: !!hoveredBar,
17665
- label: hoveredBar?.label,
17874
+ label: hoveredBar?.seriesName ? `${hoveredBar.label} \u2014 ${hoveredBar.seriesName}` : hoveredBar?.label,
17666
17875
  value: hoveredBar?.value,
17667
17876
  color: hoveredBar?.color,
17668
- containerRef: svgRef
17877
+ containerRef: svgRef,
17878
+ formatter: formatValue ? (v) => formatValue(Number(v)) : void 0
17669
17879
  }
17670
17880
  )
17671
17881
  ] });
@@ -17684,6 +17894,8 @@ function PieChart({
17684
17894
  showPercentage = true,
17685
17895
  animated = true,
17686
17896
  startAngle = -90,
17897
+ renderCenter,
17898
+ formatValue,
17687
17899
  className = ""
17688
17900
  }) {
17689
17901
  const containerRef = (0, import_react27.useRef)(null);
@@ -17779,17 +17991,17 @@ function PieChart({
17779
17991
  className: "text-foreground",
17780
17992
  fill: "currentColor",
17781
17993
  style: animated ? { opacity: 0, animation: `fadeIn 0.3s ease-out ${i * 0.1 + 0.3}s forwards` } : void 0,
17782
- children: showPercentage ? `${(seg.percentage * 100).toFixed(0)}%` : seg.value
17994
+ children: showPercentage ? `${(seg.percentage * 100).toFixed(0)}%` : formatValue ? formatValue(seg.value) : seg.value
17783
17995
  }
17784
17996
  )
17785
17997
  ]
17786
17998
  },
17787
17999
  i
17788
18000
  )),
17789
- donut && /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("g", { children: [
18001
+ donut && (renderCenter ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("foreignObject", { x: center - donutWidth, y: center - donutWidth / 2, width: donutWidth * 2, height: donutWidth, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", width: "100%", height: "100%" }, children: renderCenter(total) }) }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("g", { children: [
17790
18002
  /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("text", { x: center, y: center - 5, textAnchor: "middle", fontSize: "12", className: "text-muted-foreground", fill: "currentColor", children: "Total" }),
17791
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("text", { x: center, y: center + 15, textAnchor: "middle", fontSize: "18", fontWeight: "600", className: "text-foreground", fill: "currentColor", children: total })
17792
- ] })
18003
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("text", { x: center, y: center + 15, textAnchor: "middle", fontSize: "18", fontWeight: "600", className: "text-foreground", fill: "currentColor", children: formatValue ? formatValue(total) : total })
18004
+ ] }))
17793
18005
  ] }),
17794
18006
  /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("style", { children: `
17795
18007
  @keyframes pieSlice {
@@ -17816,7 +18028,7 @@ function PieChart({
17816
18028
  children: [
17817
18029
  /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "w-3 h-3 rounded-md shrink-0", style: { backgroundColor: seg.color } }),
17818
18030
  /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "text-muted-foreground", children: seg.label }),
17819
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "text-foreground font-medium ml-auto", children: showPercentage ? `${(seg.percentage * 100).toFixed(0)}%` : seg.value })
18031
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "text-foreground font-medium ml-auto", children: showPercentage ? `${(seg.percentage * 100).toFixed(0)}%` : formatValue ? formatValue(seg.value) : seg.value })
17820
18032
  ]
17821
18033
  },
17822
18034
  i
@@ -17839,26 +18051,6 @@ function PieChart({
17839
18051
  // ../../components/ui/AreaChart.tsx
17840
18052
  var import_react28 = require("react");
17841
18053
  var import_jsx_runtime59 = require("react/jsx-runtime");
17842
- function getCatmullRomSpline(points) {
17843
- if (points.length < 2) return "";
17844
- if (points.length === 2) {
17845
- return `M ${points[0].x} ${points[0].y} L ${points[1].x} ${points[1].y}`;
17846
- }
17847
- let path = `M ${points[0].x} ${points[0].y}`;
17848
- for (let i = 0; i < points.length - 1; i++) {
17849
- const p0 = points[Math.max(0, i - 1)];
17850
- const p1 = points[i];
17851
- const p2 = points[i + 1];
17852
- const p3 = points[Math.min(points.length - 1, i + 2)];
17853
- const tension = 0.5;
17854
- const cp1x = p1.x + (p2.x - p0.x) * tension / 6;
17855
- const cp1y = p1.y + (p2.y - p0.y) * tension / 6;
17856
- const cp2x = p2.x - (p3.x - p1.x) * tension / 6;
17857
- const cp2y = p2.y - (p3.y - p1.y) * tension / 6;
17858
- path += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${p2.x} ${p2.y}`;
17859
- }
17860
- return path;
17861
- }
17862
18054
  function AreaChart({
17863
18055
  series,
17864
18056
  width = 400,
@@ -17870,13 +18062,25 @@ function AreaChart({
17870
18062
  animated = true,
17871
18063
  stacked = false,
17872
18064
  curved = true,
18065
+ formatValue,
18066
+ emptyText = "No data",
17873
18067
  className = ""
17874
18068
  }) {
17875
18069
  const containerRef = (0, import_react28.useRef)(null);
18070
+ const clipId = (0, import_react28.useRef)(`area-clip-${Math.random().toString(36).slice(2, 8)}`).current;
17876
18071
  const padding = { top: 20, right: 20, bottom: 40, left: 50 };
17877
18072
  const chartWidth = width - padding.left - padding.right;
17878
18073
  const chartHeight = height - padding.top - padding.bottom;
17879
18074
  const [hoveredPoint, setHoveredPoint] = (0, import_react28.useState)(null);
18075
+ const [hiddenSeries, setHiddenSeries] = (0, import_react28.useState)(/* @__PURE__ */ new Set());
18076
+ const toggleSeries = (0, import_react28.useCallback)((name) => {
18077
+ setHiddenSeries((prev) => {
18078
+ const next = new Set(prev);
18079
+ if (next.has(name)) next.delete(name);
18080
+ else next.add(name);
18081
+ return next;
18082
+ });
18083
+ }, []);
17880
18084
  const { processedSeries, gridLines, maxValue, labels } = (0, import_react28.useMemo)(() => {
17881
18085
  if (!series.length || !series[0]?.data?.length) {
17882
18086
  return { processedSeries: [], gridLines: [], maxValue: 0, labels: [] };
@@ -17947,16 +18151,17 @@ function AreaChart({
17947
18151
  }, [series, chartWidth, chartHeight, padding, stacked, curved]);
17948
18152
  return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { ref: containerRef, className: `relative flex flex-col gap-4 ${className}`, children: [
17949
18153
  /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("svg", { width, height, className: "overflow-visible", style: { fontFamily: "inherit" }, children: [
18154
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("clipPath", { id: clipId, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("rect", { x: padding.left, y: padding.top, width: chartWidth, height: chartHeight }) }) }),
17950
18155
  showGrid && /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("g", { className: "text-muted-foreground/20", children: gridLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("g", { children: [
17951
18156
  /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("line", { x1: padding.left, y1: line.y, x2: width - padding.right, y2: line.y, stroke: "currentColor", strokeDasharray: "4 4" }),
17952
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("text", { x: padding.left - 8, y: line.y + 4, textAnchor: "end", fontSize: "10", className: "text-muted-foreground", fill: "currentColor", children: line.value.toFixed(0) })
18157
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("text", { x: padding.left - 8, y: line.y + 4, textAnchor: "end", fontSize: "10", className: "text-muted-foreground", fill: "currentColor", children: formatValue ? formatValue(line.value) : line.value.toFixed(0) })
17953
18158
  ] }, i)) }),
17954
- [...processedSeries].reverse().map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
18159
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("g", { clipPath: `url(#${clipId})`, children: [...processedSeries].reverse().map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
17955
18160
  "path",
17956
18161
  {
17957
18162
  d: s.areaPath,
17958
18163
  fill: s.color,
17959
- fillOpacity: s.fillOpacity ?? 0.3,
18164
+ fillOpacity: hiddenSeries.has(s.name) ? 0 : s.fillOpacity ?? 0.3,
17960
18165
  className: "transition-all duration-300",
17961
18166
  style: animated ? {
17962
18167
  opacity: 0,
@@ -17964,8 +18169,8 @@ function AreaChart({
17964
18169
  } : void 0
17965
18170
  },
17966
18171
  `area-${i}`
17967
- )),
17968
- processedSeries.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
18172
+ )) }),
18173
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("g", { clipPath: `url(#${clipId})`, children: processedSeries.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
17969
18174
  "path",
17970
18175
  {
17971
18176
  d: s.linePath,
@@ -17974,6 +18179,8 @@ function AreaChart({
17974
18179
  strokeWidth: 2,
17975
18180
  strokeLinecap: "round",
17976
18181
  strokeLinejoin: "round",
18182
+ className: "transition-opacity duration-300",
18183
+ opacity: hiddenSeries.has(s.name) ? 0 : 1,
17977
18184
  style: animated ? {
17978
18185
  strokeDasharray: s.lineLength,
17979
18186
  strokeDashoffset: s.lineLength,
@@ -17981,9 +18188,9 @@ function AreaChart({
17981
18188
  } : void 0
17982
18189
  },
17983
18190
  `line-${i}`
17984
- )),
18191
+ )) }),
17985
18192
  showDots && processedSeries.map(
17986
- (s, seriesIdx) => s.points.map((point, i) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(
18193
+ (s, seriesIdx) => !hiddenSeries.has(s.name) && s.points.map((point, i) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(
17987
18194
  "g",
17988
18195
  {
17989
18196
  onMouseEnter: () => {
@@ -18067,11 +18274,18 @@ function AreaChart({
18067
18274
  showLegend && /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("div", { className: "flex items-center justify-center gap-6", children: series.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(
18068
18275
  "div",
18069
18276
  {
18070
- className: "flex items-center gap-2 text-sm",
18277
+ className: "flex items-center gap-2 text-sm cursor-pointer select-none",
18071
18278
  style: animated ? { opacity: 0, animation: `fadeIn 0.3s ease-out ${i * 0.1 + 0.5}s forwards` } : void 0,
18279
+ onClick: () => toggleSeries(s.name),
18072
18280
  children: [
18073
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("div", { className: "w-3 h-3 rounded-md", style: { backgroundColor: s.color } }),
18074
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "text-muted-foreground", children: s.name })
18281
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
18282
+ "div",
18283
+ {
18284
+ className: "w-3 h-3 rounded-md transition-opacity",
18285
+ style: { backgroundColor: s.color, opacity: hiddenSeries.has(s.name) ? 0.3 : 1 }
18286
+ }
18287
+ ),
18288
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: hiddenSeries.has(s.name) ? "text-muted-foreground/40 line-through" : "text-muted-foreground", children: s.name })
18075
18289
  ]
18076
18290
  },
18077
18291
  i
@@ -18093,26 +18307,6 @@ function AreaChart({
18093
18307
  // ../../components/ui/Sparkline.tsx
18094
18308
  var import_react29 = require("react");
18095
18309
  var import_jsx_runtime60 = require("react/jsx-runtime");
18096
- function getCatmullRomSpline2(points) {
18097
- if (points.length < 2) return "";
18098
- if (points.length === 2) {
18099
- return `M ${points[0].x} ${points[0].y} L ${points[1].x} ${points[1].y}`;
18100
- }
18101
- let path = `M ${points[0].x} ${points[0].y}`;
18102
- for (let i = 0; i < points.length - 1; i++) {
18103
- const p0 = points[Math.max(0, i - 1)];
18104
- const p1 = points[i];
18105
- const p2 = points[i + 1];
18106
- const p3 = points[Math.min(points.length - 1, i + 2)];
18107
- const tension = 0.5;
18108
- const cp1x = p1.x + (p2.x - p0.x) * tension / 6;
18109
- const cp1y = p1.y + (p2.y - p0.y) * tension / 6;
18110
- const cp2x = p2.x - (p3.x - p1.x) * tension / 6;
18111
- const cp2y = p2.y - (p3.y - p1.y) * tension / 6;
18112
- path += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${p2.x} ${p2.y}`;
18113
- }
18114
- return path;
18115
- }
18116
18310
  function Sparkline({
18117
18311
  data,
18118
18312
  width = 100,
@@ -18143,7 +18337,7 @@ function Sparkline({
18143
18337
  y: padding + chartHeight - (value - min) / range * chartHeight,
18144
18338
  value
18145
18339
  }));
18146
- const line = curved ? getCatmullRomSpline2(pts) : `M ${pts.map((p) => `${p.x} ${p.y}`).join(" L ")}`;
18340
+ const line = curved ? getCatmullRomSpline(pts) : `M ${pts.map((p) => `${p.x} ${p.y}`).join(" L ")}`;
18147
18341
  const area = `${line} L ${padding + chartWidth} ${padding + chartHeight} L ${padding} ${padding + chartHeight} Z`;
18148
18342
  const length = pts.reduce((acc, p, i) => {
18149
18343
  if (i === 0) return 0;
@@ -18244,12 +18438,22 @@ function RadarChart({
18244
18438
  showLegend = true,
18245
18439
  showValues = false,
18246
18440
  animated = true,
18441
+ formatValue,
18247
18442
  className = ""
18248
18443
  }) {
18249
18444
  const containerRef = (0, import_react30.useRef)(null);
18250
18445
  const center = size / 2;
18251
18446
  const radius = size / 2 - 40;
18252
18447
  const [hoveredPoint, setHoveredPoint] = (0, import_react30.useState)(null);
18448
+ const [hiddenSeries, setHiddenSeries] = (0, import_react30.useState)(/* @__PURE__ */ new Set());
18449
+ const toggleSeries = (0, import_react30.useCallback)((name) => {
18450
+ setHiddenSeries((prev) => {
18451
+ const next = new Set(prev);
18452
+ if (next.has(name)) next.delete(name);
18453
+ else next.add(name);
18454
+ return next;
18455
+ });
18456
+ }, []);
18253
18457
  const { axes, processedSeries, levelPaths } = (0, import_react30.useMemo)(() => {
18254
18458
  if (!series.length || !series[0]?.data?.length) {
18255
18459
  return { axes: [], processedSeries: [], levelPaths: [] };
@@ -18304,81 +18508,83 @@ function RadarChart({
18304
18508
  /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("svg", { width: size, height: size, className: "overflow-visible", style: { fontFamily: "inherit" }, children: [
18305
18509
  /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("g", { className: "text-muted-foreground/20", children: levelPaths.map((level, i) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("path", { d: level.path, fill: "none", stroke: "currentColor", strokeWidth: 1 }, i)) }),
18306
18510
  /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("g", { className: "text-muted-foreground/30", children: axes.map((axis, i) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("line", { x1: center, y1: center, x2: axis.x, y2: axis.y, stroke: "currentColor", strokeWidth: 1 }, i)) }),
18307
- processedSeries.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("g", { children: [
18308
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
18309
- "path",
18310
- {
18311
- d: s.path,
18312
- fill: s.color,
18313
- fillOpacity: s.fillOpacity ?? 0.2,
18314
- stroke: s.color,
18315
- strokeWidth: 2,
18316
- strokeLinejoin: "round",
18317
- className: "transition-all duration-300",
18318
- style: animated ? {
18319
- opacity: 0,
18320
- transform: "scale(0)",
18321
- transformOrigin: `${center}px ${center}px`,
18322
- animation: `radarPop 0.5s ease-out ${i * 0.15}s forwards`
18323
- } : void 0
18324
- }
18325
- ),
18326
- s.points.map((point, j) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
18327
- "g",
18328
- {
18329
- onMouseEnter: () => {
18330
- const items = processedSeries.map((ps) => ({
18331
- label: ps.name,
18332
- value: ps.points[j]?.value ?? 0,
18333
- color: ps.color
18334
- }));
18335
- setHoveredPoint({
18336
- x: point.x,
18337
- y: point.y,
18338
- axis: series[0]?.data[j]?.axis ?? "",
18339
- items
18340
- });
18511
+ processedSeries.map(
18512
+ (s, i) => !hiddenSeries.has(s.name) && /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("g", { children: [
18513
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
18514
+ "path",
18515
+ {
18516
+ d: s.path,
18517
+ fill: s.color,
18518
+ fillOpacity: s.fillOpacity ?? 0.2,
18519
+ stroke: s.color,
18520
+ strokeWidth: 2,
18521
+ strokeLinejoin: "round",
18522
+ className: "transition-all duration-300",
18523
+ style: animated ? {
18524
+ opacity: 0,
18525
+ transform: "scale(0)",
18526
+ transformOrigin: `${center}px ${center}px`,
18527
+ animation: `radarPop 0.5s ease-out ${i * 0.15}s forwards`
18528
+ } : void 0
18529
+ }
18530
+ ),
18531
+ s.points.map((point, j) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
18532
+ "g",
18533
+ {
18534
+ onMouseEnter: () => {
18535
+ const items = processedSeries.map((ps) => ({
18536
+ label: ps.name,
18537
+ value: ps.points[j]?.value ?? 0,
18538
+ color: ps.color
18539
+ }));
18540
+ setHoveredPoint({
18541
+ x: point.x,
18542
+ y: point.y,
18543
+ axis: series[0]?.data[j]?.axis ?? "",
18544
+ items
18545
+ });
18546
+ },
18547
+ onMouseLeave: () => setHoveredPoint(null),
18548
+ className: "cursor-pointer",
18549
+ children: [
18550
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
18551
+ "circle",
18552
+ {
18553
+ cx: point.x,
18554
+ cy: point.y,
18555
+ r: hoveredPoint?.axis === series[0]?.data[j]?.axis ? 6 : 4,
18556
+ fill: s.color,
18557
+ className: "transition-all duration-150",
18558
+ style: animated ? {
18559
+ opacity: 0,
18560
+ transform: "scale(0)",
18561
+ transformOrigin: `${point.x}px ${point.y}px`,
18562
+ animation: `dotPop 0.3s ease-out ${i * 0.15 + j * 0.05 + 0.3}s forwards`
18563
+ } : void 0
18564
+ }
18565
+ ),
18566
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("circle", { cx: point.x, cy: point.y, r: 12, fill: "transparent" })
18567
+ ]
18341
18568
  },
18342
- onMouseLeave: () => setHoveredPoint(null),
18343
- className: "cursor-pointer",
18344
- children: [
18345
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
18346
- "circle",
18347
- {
18348
- cx: point.x,
18349
- cy: point.y,
18350
- r: hoveredPoint?.axis === series[0]?.data[j]?.axis ? 6 : 4,
18351
- fill: s.color,
18352
- className: "transition-all duration-150",
18353
- style: animated ? {
18354
- opacity: 0,
18355
- transform: "scale(0)",
18356
- transformOrigin: `${point.x}px ${point.y}px`,
18357
- animation: `dotPop 0.3s ease-out ${i * 0.15 + j * 0.05 + 0.3}s forwards`
18358
- } : void 0
18359
- }
18360
- ),
18361
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("circle", { cx: point.x, cy: point.y, r: 12, fill: "transparent" })
18362
- ]
18363
- },
18364
- j
18365
- )),
18366
- showValues && s.points.map((point, j) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
18367
- "text",
18368
- {
18369
- x: point.x,
18370
- y: point.y - 10,
18371
- textAnchor: "middle",
18372
- fontSize: "10",
18373
- fontWeight: "500",
18374
- className: "text-foreground",
18375
- fill: "currentColor",
18376
- style: animated ? { opacity: 0, animation: `fadeIn 0.3s ease-out ${i * 0.15 + 0.5}s forwards` } : void 0,
18377
- children: point.value
18378
- },
18379
- `val-${j}`
18380
- ))
18381
- ] }, i)),
18569
+ j
18570
+ )),
18571
+ showValues && s.points.map((point, j) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
18572
+ "text",
18573
+ {
18574
+ x: point.x,
18575
+ y: point.y - 10,
18576
+ textAnchor: "middle",
18577
+ fontSize: "10",
18578
+ fontWeight: "500",
18579
+ className: "text-foreground",
18580
+ fill: "currentColor",
18581
+ style: animated ? { opacity: 0, animation: `fadeIn 0.3s ease-out ${i * 0.15 + 0.5}s forwards` } : void 0,
18582
+ children: point.value
18583
+ },
18584
+ `val-${j}`
18585
+ ))
18586
+ ] }, i)
18587
+ ),
18382
18588
  showLabels && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("g", { className: "text-muted-foreground", children: axes.map((axis, i) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("text", { x: axis.labelX, y: axis.labelY, textAnchor: "middle", dominantBaseline: "middle", fontSize: "11", fill: "currentColor", children: axis.label }, i)) }),
18383
18589
  /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("style", { children: `
18384
18590
  @keyframes radarPop {
@@ -18410,11 +18616,18 @@ function RadarChart({
18410
18616
  showLegend && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: "flex items-center justify-center gap-6", children: series.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
18411
18617
  "div",
18412
18618
  {
18413
- className: "flex items-center gap-2 text-sm",
18619
+ className: "flex items-center gap-2 text-sm cursor-pointer select-none",
18414
18620
  style: animated ? { opacity: 0, animation: `fadeIn 0.3s ease-out ${i * 0.1 + 0.5}s forwards` } : void 0,
18621
+ onClick: () => toggleSeries(s.name),
18415
18622
  children: [
18416
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className: "w-3 h-3 rounded-md", style: { backgroundColor: s.color } }),
18417
- /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "text-muted-foreground", children: s.name })
18623
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
18624
+ "div",
18625
+ {
18626
+ className: "w-3 h-3 rounded-md transition-opacity",
18627
+ style: { backgroundColor: s.color, opacity: hiddenSeries.has(s.name) ? 0.3 : 1 }
18628
+ }
18629
+ ),
18630
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: hiddenSeries.has(s.name) ? "text-muted-foreground/40 line-through" : "text-muted-foreground", children: s.name })
18418
18631
  ]
18419
18632
  },
18420
18633
  i
@@ -18450,11 +18663,13 @@ function GaugeChart({
18450
18663
  animated = true,
18451
18664
  startAngle = -135,
18452
18665
  endAngle = 135,
18666
+ zones,
18667
+ formatValue,
18453
18668
  className = ""
18454
18669
  }) {
18455
18670
  const center = size / 2;
18456
18671
  const radius = center - thickness / 2 - 10;
18457
- const { backgroundPath, valuePath, percentage, needleAngle } = (0, import_react31.useMemo)(() => {
18672
+ const { backgroundPath, valuePath, percentage, needleAngle, zonePaths } = (0, import_react31.useMemo)(() => {
18458
18673
  const normalizedValue = Math.min(Math.max(value, min), max);
18459
18674
  const pct = (normalizedValue - min) / (max - min);
18460
18675
  const totalAngle = endAngle - startAngle;
@@ -18472,13 +18687,19 @@ function GaugeChart({
18472
18687
  const largeArc = Math.abs(end - start) > 180 ? 1 : 0;
18473
18688
  return `M ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArc} 1 ${endPoint.x} ${endPoint.y}`;
18474
18689
  };
18690
+ const zonePaths2 = (zones ?? []).map((zone) => {
18691
+ const zoneStart = startAngle + (Math.max(zone.min, min) - min) / (max - min) * totalAngle;
18692
+ const zoneEnd = startAngle + (Math.min(zone.max, max) - min) / (max - min) * totalAngle;
18693
+ return { path: createArc(zoneStart, zoneEnd), color: zone.color };
18694
+ });
18475
18695
  return {
18476
18696
  backgroundPath: createArc(startAngle, endAngle),
18477
18697
  valuePath: createArc(startAngle, currentAngle),
18478
18698
  percentage: pct,
18479
- needleAngle: currentAngle
18699
+ needleAngle: currentAngle,
18700
+ zonePaths: zonePaths2
18480
18701
  };
18481
- }, [value, min, max, center, radius, startAngle, endAngle]);
18702
+ }, [value, min, max, center, radius, startAngle, endAngle, zones]);
18482
18703
  const needleLength = radius - 10;
18483
18704
  const needleAngleRad = needleAngle * Math.PI / 180;
18484
18705
  const needleX = center + needleLength * Math.cos(needleAngleRad);
@@ -18495,6 +18716,7 @@ function GaugeChart({
18495
18716
  className: !backgroundColor ? "text-muted-foreground/20" : ""
18496
18717
  }
18497
18718
  ),
18719
+ zonePaths.map((zone, i) => /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("path", { d: zone.path, fill: "none", stroke: zone.color, strokeWidth: thickness, strokeLinecap: "round", opacity: 0.35 }, `zone-${i}`)),
18498
18720
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
18499
18721
  "path",
18500
18722
  {
@@ -18565,7 +18787,7 @@ function GaugeChart({
18565
18787
  className: "text-foreground",
18566
18788
  fill: "currentColor",
18567
18789
  style: animated ? { opacity: 0, animation: "fadeIn 0.5s ease-out 0.5s forwards" } : void 0,
18568
- children: value
18790
+ children: formatValue ? formatValue(value) : value
18569
18791
  }
18570
18792
  ),
18571
18793
  label && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(