@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 +590 -368
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -9
- package/dist/index.d.ts +61 -9
- package/dist/index.js +626 -404
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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({
|
|
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
|
-
|
|
17257
|
-
|
|
17258
|
-
|
|
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
|
|
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
|
|
17341
|
-
|
|
17342
|
-
|
|
17343
|
-
|
|
17344
|
-
|
|
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
|
|
17347
|
-
|
|
17348
|
-
|
|
17349
|
-
|
|
17350
|
-
|
|
17351
|
-
|
|
17352
|
-
|
|
17353
|
-
|
|
17354
|
-
|
|
17355
|
-
|
|
17356
|
-
|
|
17357
|
-
|
|
17358
|
-
|
|
17359
|
-
|
|
17360
|
-
|
|
17361
|
-
|
|
17362
|
-
|
|
17363
|
-
|
|
17364
|
-
|
|
17365
|
-
|
|
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
|
-
|
|
17392
|
-
|
|
17393
|
-
|
|
17394
|
-
|
|
17395
|
-
|
|
17396
|
-
|
|
17397
|
-
|
|
17398
|
-
|
|
17399
|
-
|
|
17400
|
-
|
|
17401
|
-
|
|
17402
|
-
|
|
17403
|
-
|
|
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
|
-
"
|
|
17471
|
+
processedSeries.map((s, si) => /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
17472
|
+
"path",
|
|
17449
17473
|
{
|
|
17450
|
-
|
|
17451
|
-
|
|
17452
|
-
|
|
17453
|
-
|
|
17454
|
-
|
|
17455
|
-
|
|
17456
|
-
|
|
17457
|
-
opacity: 0
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
17486
|
-
|
|
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
|
|
17515
|
-
if (
|
|
17516
|
-
|
|
17517
|
-
|
|
17518
|
-
|
|
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
|
|
17521
|
-
const gap =
|
|
17522
|
-
|
|
17523
|
-
|
|
17524
|
-
|
|
17525
|
-
|
|
17526
|
-
|
|
17527
|
-
|
|
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
|
|
17531
|
-
const gap =
|
|
17532
|
-
|
|
17533
|
-
|
|
17534
|
-
|
|
17535
|
-
|
|
17536
|
-
|
|
17537
|
-
|
|
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,
|
|
17562
|
-
}, [
|
|
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
|
-
|
|
17573
|
-
|
|
17574
|
-
|
|
17575
|
-
|
|
17576
|
-
|
|
17577
|
-
|
|
17578
|
-
|
|
17579
|
-
|
|
17580
|
-
|
|
17581
|
-
|
|
17582
|
-
|
|
17583
|
-
|
|
17584
|
-
|
|
17585
|
-
|
|
17586
|
-
|
|
17587
|
-
|
|
17588
|
-
|
|
17589
|
-
|
|
17590
|
-
|
|
17591
|
-
|
|
17592
|
-
|
|
17593
|
-
|
|
17594
|
-
|
|
17595
|
-
|
|
17596
|
-
|
|
17597
|
-
|
|
17598
|
-
|
|
17599
|
-
|
|
17600
|
-
|
|
17601
|
-
|
|
17602
|
-
|
|
17603
|
-
|
|
17604
|
-
|
|
17605
|
-
|
|
17606
|
-
|
|
17607
|
-
|
|
17608
|
-
|
|
17609
|
-
|
|
17610
|
-
|
|
17611
|
-
|
|
17612
|
-
|
|
17613
|
-
|
|
17614
|
-
|
|
17615
|
-
|
|
17616
|
-
|
|
17617
|
-
|
|
17618
|
-
|
|
17619
|
-
|
|
17620
|
-
|
|
17621
|
-
|
|
17622
|
-
|
|
17623
|
-
|
|
17624
|
-
|
|
17625
|
-
|
|
17626
|
-
|
|
17627
|
-
|
|
17628
|
-
|
|
17629
|
-
|
|
17630
|
-
|
|
17631
|
-
|
|
17632
|
-
|
|
17633
|
-
|
|
17634
|
-
|
|
17635
|
-
|
|
17636
|
-
|
|
17637
|
-
|
|
17638
|
-
|
|
17639
|
-
|
|
17640
|
-
|
|
17641
|
-
|
|
17642
|
-
|
|
17643
|
-
|
|
17644
|
-
|
|
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)(
|
|
18074
|
-
|
|
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 ?
|
|
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(
|
|
18308
|
-
/* @__PURE__ */ (0, import_jsx_runtime61.
|
|
18309
|
-
|
|
18310
|
-
|
|
18311
|
-
|
|
18312
|
-
|
|
18313
|
-
|
|
18314
|
-
|
|
18315
|
-
|
|
18316
|
-
|
|
18317
|
-
|
|
18318
|
-
|
|
18319
|
-
|
|
18320
|
-
|
|
18321
|
-
|
|
18322
|
-
|
|
18323
|
-
|
|
18324
|
-
|
|
18325
|
-
|
|
18326
|
-
|
|
18327
|
-
|
|
18328
|
-
|
|
18329
|
-
|
|
18330
|
-
|
|
18331
|
-
|
|
18332
|
-
|
|
18333
|
-
|
|
18334
|
-
|
|
18335
|
-
|
|
18336
|
-
|
|
18337
|
-
|
|
18338
|
-
|
|
18339
|
-
|
|
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
|
-
|
|
18343
|
-
|
|
18344
|
-
|
|
18345
|
-
|
|
18346
|
-
|
|
18347
|
-
|
|
18348
|
-
|
|
18349
|
-
|
|
18350
|
-
|
|
18351
|
-
|
|
18352
|
-
|
|
18353
|
-
|
|
18354
|
-
|
|
18355
|
-
|
|
18356
|
-
|
|
18357
|
-
|
|
18358
|
-
|
|
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)(
|
|
18417
|
-
|
|
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)(
|