@underverse-ui/underverse 1.0.14 → 1.0.15
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 +572 -356
- 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 +608 -392
- 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,8 +17380,11 @@ 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);
|
|
@@ -17337,41 +17392,50 @@ function LineChart({
|
|
|
17337
17392
|
const chartWidth = width - padding.left - padding.right;
|
|
17338
17393
|
const chartHeight = height - padding.top - padding.bottom;
|
|
17339
17394
|
const [hoveredPoint, setHoveredPoint] = (0, import_react25.useState)(null);
|
|
17340
|
-
const
|
|
17341
|
-
|
|
17342
|
-
|
|
17343
|
-
|
|
17344
|
-
|
|
17395
|
+
const [hiddenSeries, setHiddenSeries] = (0, import_react25.useState)(/* @__PURE__ */ new Set());
|
|
17396
|
+
const toggleSeries = (0, import_react25.useCallback)((name) => {
|
|
17397
|
+
setHiddenSeries((prev) => {
|
|
17398
|
+
const next = new Set(prev);
|
|
17399
|
+
if (next.has(name)) next.delete(name);
|
|
17400
|
+
else next.add(name);
|
|
17401
|
+
return next;
|
|
17402
|
+
});
|
|
17403
|
+
}, []);
|
|
17404
|
+
const normalizedSeries = (0, import_react25.useMemo)(() => {
|
|
17405
|
+
if (series && series.length > 0) return series;
|
|
17406
|
+
if (data && data.length > 0) return [{ name: "", data, color, fillColor }];
|
|
17407
|
+
return [];
|
|
17408
|
+
}, [series, data, color, fillColor]);
|
|
17409
|
+
const isMultiSeries = normalizedSeries.length > 1;
|
|
17410
|
+
const { minValue, maxValue, processedSeries, labels } = (0, import_react25.useMemo)(() => {
|
|
17411
|
+
if (!normalizedSeries.length || !normalizedSeries[0]?.data?.length) {
|
|
17412
|
+
return { minValue: 0, maxValue: 0, processedSeries: [], labels: [] };
|
|
17413
|
+
}
|
|
17414
|
+
const allLabels = normalizedSeries[0].data.map((d) => d.label);
|
|
17415
|
+
const allValues = normalizedSeries.flatMap((s) => s.data.map((d) => d.value));
|
|
17416
|
+
const min = Math.min(...allValues);
|
|
17417
|
+
const max = Math.max(...allValues);
|
|
17345
17418
|
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]);
|
|
17419
|
+
const processed = normalizedSeries.map((s) => {
|
|
17420
|
+
const pts = s.data.map((d, i) => ({
|
|
17421
|
+
x: padding.left + i / (s.data.length - 1 || 1) * chartWidth,
|
|
17422
|
+
y: padding.top + chartHeight - (d.value - min) / range * chartHeight,
|
|
17423
|
+
...d
|
|
17424
|
+
}));
|
|
17425
|
+
let linePath = "";
|
|
17426
|
+
let areaPath = "";
|
|
17427
|
+
if (curved && pts.length > 2) {
|
|
17428
|
+
linePath = getCatmullRomSpline(pts);
|
|
17429
|
+
areaPath = `${linePath} L ${pts[pts.length - 1].x} ${padding.top + chartHeight} L ${pts[0].x} ${padding.top + chartHeight} Z`;
|
|
17430
|
+
} else {
|
|
17431
|
+
linePath = pts.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`).join(" ");
|
|
17432
|
+
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`;
|
|
17433
|
+
}
|
|
17434
|
+
const lineLength = getPathLength(pts);
|
|
17435
|
+
return { ...s, points: pts, linePath, areaPath, lineLength };
|
|
17436
|
+
});
|
|
17437
|
+
return { minValue: min, maxValue: max, processedSeries: processed, labels: allLabels };
|
|
17438
|
+
}, [normalizedSeries, chartWidth, chartHeight, curved, padding.left, padding.top]);
|
|
17375
17439
|
const gridLines = (0, import_react25.useMemo)(() => {
|
|
17376
17440
|
const lines = [];
|
|
17377
17441
|
const steps = 5;
|
|
@@ -17386,82 +17450,129 @@ function LineChart({
|
|
|
17386
17450
|
/* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("svg", { ref: svgRef, width, height, className: `overflow-visible ${className}`, style: { fontFamily: "inherit" }, children: [
|
|
17387
17451
|
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
17452
|
/* @__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) })
|
|
17453
|
+
/* @__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
17454
|
] }, i)) }),
|
|
17391
|
-
|
|
17392
|
-
|
|
17455
|
+
processedSeries.map(
|
|
17456
|
+
(s, si) => s.fillColor && s.areaPath && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
17457
|
+
"path",
|
|
17458
|
+
{
|
|
17459
|
+
d: s.areaPath,
|
|
17460
|
+
fill: s.fillColor,
|
|
17461
|
+
className: "transition-opacity duration-300",
|
|
17462
|
+
opacity: hiddenSeries.has(s.name) ? 0 : 0.15,
|
|
17463
|
+
style: animated ? { opacity: 0, animation: `fadeIn 0.6s ease-out ${si * 0.1}s forwards` } : void 0
|
|
17464
|
+
},
|
|
17465
|
+
`area-${si}`
|
|
17466
|
+
)
|
|
17467
|
+
),
|
|
17468
|
+
processedSeries.map((s, si) => /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
17393
17469
|
"path",
|
|
17394
17470
|
{
|
|
17395
|
-
d: linePath,
|
|
17471
|
+
d: s.linePath,
|
|
17396
17472
|
fill: "none",
|
|
17397
|
-
stroke: color,
|
|
17473
|
+
stroke: s.color,
|
|
17398
17474
|
strokeWidth: 2,
|
|
17399
17475
|
strokeLinecap: "round",
|
|
17400
17476
|
strokeLinejoin: "round",
|
|
17401
|
-
className:
|
|
17477
|
+
className: "transition-opacity duration-300",
|
|
17478
|
+
opacity: hiddenSeries.has(s.name) ? 0 : 1,
|
|
17402
17479
|
style: animated ? {
|
|
17403
|
-
strokeDasharray:
|
|
17404
|
-
strokeDashoffset:
|
|
17405
|
-
animation:
|
|
17480
|
+
strokeDasharray: s.lineLength,
|
|
17481
|
+
strokeDashoffset: s.lineLength,
|
|
17482
|
+
animation: `drawLine 1s ease-out ${si * 0.15}s forwards`
|
|
17406
17483
|
} : 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
17484
|
},
|
|
17431
|
-
|
|
17485
|
+
`line-${si}`
|
|
17432
17486
|
)),
|
|
17433
|
-
|
|
17434
|
-
/* @__PURE__ */ (0, import_jsx_runtime56.
|
|
17435
|
-
"
|
|
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
|
-
}
|
|
17446
|
-
),
|
|
17447
|
-
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
17448
|
-
"line",
|
|
17487
|
+
showDots && processedSeries.map(
|
|
17488
|
+
(s, si) => !hiddenSeries.has(s.name) && s.points.map((point, i) => /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
|
|
17489
|
+
"g",
|
|
17449
17490
|
{
|
|
17450
|
-
|
|
17451
|
-
|
|
17452
|
-
|
|
17453
|
-
|
|
17454
|
-
|
|
17455
|
-
|
|
17456
|
-
|
|
17457
|
-
|
|
17458
|
-
|
|
17459
|
-
|
|
17460
|
-
|
|
17461
|
-
|
|
17491
|
+
onMouseEnter: () => {
|
|
17492
|
+
if (isMultiSeries) {
|
|
17493
|
+
const items = processedSeries.filter((ps) => !hiddenSeries.has(ps.name)).map((ps) => ({
|
|
17494
|
+
label: ps.name,
|
|
17495
|
+
value: ps.points[i]?.value ?? 0,
|
|
17496
|
+
color: ps.color
|
|
17497
|
+
}));
|
|
17498
|
+
setHoveredPoint({ x: point.x, y: point.y, label: point.label, value: point.value, items });
|
|
17499
|
+
} else {
|
|
17500
|
+
setHoveredPoint({ x: point.x, y: point.y, label: point.label, value: point.value });
|
|
17501
|
+
}
|
|
17502
|
+
},
|
|
17503
|
+
onMouseLeave: () => setHoveredPoint(null),
|
|
17504
|
+
className: "cursor-pointer",
|
|
17505
|
+
children: [
|
|
17506
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
17507
|
+
"circle",
|
|
17508
|
+
{
|
|
17509
|
+
cx: point.x,
|
|
17510
|
+
cy: point.y,
|
|
17511
|
+
r: hoveredPoint?.x === point.x ? 6 : 4,
|
|
17512
|
+
fill: s.color,
|
|
17513
|
+
className: `transition-all duration-150 ${animated ? "animate-[scaleIn_0.3s_ease-out]" : ""}`,
|
|
17514
|
+
style: animated ? { animationDelay: `${si * 0.15 + i * 0.05}s`, animationFillMode: "both" } : void 0
|
|
17515
|
+
}
|
|
17516
|
+
),
|
|
17517
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)("circle", { cx: point.x, cy: point.y, r: 16, fill: "transparent" }),
|
|
17518
|
+
showValues && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
17519
|
+
"text",
|
|
17520
|
+
{
|
|
17521
|
+
x: point.x,
|
|
17522
|
+
y: point.y - 12,
|
|
17523
|
+
textAnchor: "middle",
|
|
17524
|
+
fontSize: "10",
|
|
17525
|
+
fontWeight: "500",
|
|
17526
|
+
className: "text-foreground",
|
|
17527
|
+
fill: "currentColor",
|
|
17528
|
+
children: formatValue ? formatValue(point.value) : point.value
|
|
17529
|
+
}
|
|
17530
|
+
)
|
|
17531
|
+
]
|
|
17532
|
+
},
|
|
17533
|
+
`dot-${si}-${i}`
|
|
17534
|
+
))
|
|
17535
|
+
),
|
|
17536
|
+
referenceLines.map((ref, i) => {
|
|
17537
|
+
const range = maxValue - minValue || 1;
|
|
17538
|
+
const y = padding.top + chartHeight - (ref.value - minValue) / range * chartHeight;
|
|
17539
|
+
return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("g", { className: "pointer-events-none", children: [
|
|
17540
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
17541
|
+
"line",
|
|
17542
|
+
{
|
|
17543
|
+
x1: padding.left,
|
|
17544
|
+
y1: y,
|
|
17545
|
+
x2: padding.left + chartWidth,
|
|
17546
|
+
y2: y,
|
|
17547
|
+
stroke: ref.color || "#ef4444",
|
|
17548
|
+
strokeWidth: 1.5,
|
|
17549
|
+
strokeDasharray: ref.strokeDasharray || "6 4",
|
|
17550
|
+
opacity: 0.7
|
|
17551
|
+
}
|
|
17552
|
+
),
|
|
17553
|
+
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 })
|
|
17554
|
+
] }, `ref-${i}`);
|
|
17555
|
+
}),
|
|
17556
|
+
hoveredPoint && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("g", { className: "pointer-events-none", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
17557
|
+
"line",
|
|
17558
|
+
{
|
|
17559
|
+
x1: hoveredPoint.x,
|
|
17560
|
+
y1: padding.top,
|
|
17561
|
+
x2: hoveredPoint.x,
|
|
17562
|
+
y2: padding.top + chartHeight,
|
|
17563
|
+
stroke: "currentColor",
|
|
17564
|
+
strokeWidth: 1,
|
|
17565
|
+
strokeDasharray: "4 4",
|
|
17566
|
+
opacity: 0.3,
|
|
17567
|
+
className: "text-foreground"
|
|
17568
|
+
}
|
|
17569
|
+
) }),
|
|
17570
|
+
showLabels && labels.map((lbl, i) => {
|
|
17571
|
+
const x = padding.left + i / (labels.length - 1 || 1) * chartWidth;
|
|
17572
|
+
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);
|
|
17573
|
+
}),
|
|
17462
17574
|
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)("style", { children: `
|
|
17463
17575
|
@keyframes drawLine {
|
|
17464
|
-
from { stroke-dashoffset: 1000; }
|
|
17465
17576
|
to { stroke-dashoffset: 0; }
|
|
17466
17577
|
}
|
|
17467
17578
|
@keyframes scaleIn {
|
|
@@ -17470,10 +17581,20 @@ function LineChart({
|
|
|
17470
17581
|
}
|
|
17471
17582
|
@keyframes fadeIn {
|
|
17472
17583
|
from { opacity: 0; }
|
|
17473
|
-
to { opacity: 0.
|
|
17584
|
+
to { opacity: 0.15; }
|
|
17474
17585
|
}
|
|
17475
17586
|
` })
|
|
17476
17587
|
] }),
|
|
17588
|
+
(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: [
|
|
17589
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
17590
|
+
"div",
|
|
17591
|
+
{
|
|
17592
|
+
className: "w-3 h-3 rounded-md transition-opacity",
|
|
17593
|
+
style: { backgroundColor: s.color, opacity: hiddenSeries.has(s.name) ? 0.3 : 1 }
|
|
17594
|
+
}
|
|
17595
|
+
),
|
|
17596
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: hiddenSeries.has(s.name) ? "text-muted-foreground/40 line-through" : "text-muted-foreground", children: s.name })
|
|
17597
|
+
] }, i)) }),
|
|
17477
17598
|
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
17478
17599
|
ChartTooltip,
|
|
17479
17600
|
{
|
|
@@ -17481,9 +17602,11 @@ function LineChart({
|
|
|
17481
17602
|
y: hoveredPoint?.y ?? 0,
|
|
17482
17603
|
visible: !!hoveredPoint,
|
|
17483
17604
|
label: hoveredPoint?.label,
|
|
17484
|
-
value: hoveredPoint?.value,
|
|
17485
|
-
|
|
17486
|
-
|
|
17605
|
+
value: !isMultiSeries ? hoveredPoint?.value : void 0,
|
|
17606
|
+
items: isMultiSeries ? hoveredPoint?.items : void 0,
|
|
17607
|
+
color: !isMultiSeries ? processedSeries[0]?.color || color : void 0,
|
|
17608
|
+
containerRef: svgRef,
|
|
17609
|
+
formatter: formatValue ? (v) => formatValue(Number(v)) : void 0
|
|
17487
17610
|
}
|
|
17488
17611
|
)
|
|
17489
17612
|
] });
|
|
@@ -17494,16 +17617,20 @@ var import_react26 = require("react");
|
|
|
17494
17617
|
var import_jsx_runtime57 = require("react/jsx-runtime");
|
|
17495
17618
|
function BarChart({
|
|
17496
17619
|
data,
|
|
17620
|
+
series,
|
|
17497
17621
|
width = 400,
|
|
17498
17622
|
height = 200,
|
|
17499
17623
|
color = "currentColor",
|
|
17500
17624
|
showLabels = true,
|
|
17501
17625
|
showValues = true,
|
|
17502
17626
|
showGrid = true,
|
|
17627
|
+
showLegend,
|
|
17503
17628
|
horizontal = false,
|
|
17504
17629
|
animated = true,
|
|
17630
|
+
stacked = false,
|
|
17505
17631
|
barRadius = 4,
|
|
17506
17632
|
barGap = 0.3,
|
|
17633
|
+
formatValue,
|
|
17507
17634
|
className = ""
|
|
17508
17635
|
}) {
|
|
17509
17636
|
const svgRef = (0, import_react26.useRef)(null);
|
|
@@ -17511,31 +17638,109 @@ function BarChart({
|
|
|
17511
17638
|
const chartWidth = width - padding.left - padding.right;
|
|
17512
17639
|
const chartHeight = height - padding.top - padding.bottom;
|
|
17513
17640
|
const [hoveredBar, setHoveredBar] = (0, import_react26.useState)(null);
|
|
17514
|
-
const
|
|
17515
|
-
if (
|
|
17516
|
-
|
|
17517
|
-
|
|
17518
|
-
|
|
17641
|
+
const normalizedSeries = (0, import_react26.useMemo)(() => {
|
|
17642
|
+
if (series && series.length > 0) return series;
|
|
17643
|
+
if (data && data.length > 0) return [{ name: "", data, color }];
|
|
17644
|
+
return [];
|
|
17645
|
+
}, [series, data, color]);
|
|
17646
|
+
const isMultiSeries = normalizedSeries.length > 1;
|
|
17647
|
+
const { maxValue, barGroups, gridLines } = (0, import_react26.useMemo)(() => {
|
|
17648
|
+
if (!normalizedSeries.length || !normalizedSeries[0]?.data?.length) {
|
|
17649
|
+
return { maxValue: 0, barGroups: [], gridLines: [] };
|
|
17650
|
+
}
|
|
17651
|
+
const allLabels = normalizedSeries[0].data.map((d) => d.label);
|
|
17652
|
+
const seriesCount = normalizedSeries.length;
|
|
17653
|
+
const labelCount = allLabels.length;
|
|
17654
|
+
let max = 0;
|
|
17655
|
+
if (stacked) {
|
|
17656
|
+
for (let li = 0; li < labelCount; li++) {
|
|
17657
|
+
const stackVal = normalizedSeries.reduce((sum, s) => sum + (s.data[li]?.value || 0), 0);
|
|
17658
|
+
max = Math.max(max, stackVal);
|
|
17659
|
+
}
|
|
17660
|
+
} else {
|
|
17661
|
+
max = Math.max(...normalizedSeries.flatMap((s) => s.data.map((d) => d.value)));
|
|
17662
|
+
}
|
|
17663
|
+
const groups = allLabels.map((label, li) => {
|
|
17519
17664
|
if (horizontal) {
|
|
17520
|
-
const
|
|
17521
|
-
const gap =
|
|
17522
|
-
|
|
17523
|
-
|
|
17524
|
-
|
|
17525
|
-
|
|
17526
|
-
|
|
17527
|
-
|
|
17528
|
-
|
|
17665
|
+
const groupH = chartHeight / labelCount;
|
|
17666
|
+
const gap = groupH * barGap;
|
|
17667
|
+
const usable = groupH - gap;
|
|
17668
|
+
if (stacked) {
|
|
17669
|
+
let cum = 0;
|
|
17670
|
+
const bars = normalizedSeries.map((s) => {
|
|
17671
|
+
const val = s.data[li]?.value || 0;
|
|
17672
|
+
const w = val / max * chartWidth;
|
|
17673
|
+
const bar = {
|
|
17674
|
+
x: padding.left + cum,
|
|
17675
|
+
y: padding.top + li * groupH + gap / 2,
|
|
17676
|
+
width: w,
|
|
17677
|
+
height: usable,
|
|
17678
|
+
value: val,
|
|
17679
|
+
color: s.data[li]?.color || s.color,
|
|
17680
|
+
seriesName: s.name,
|
|
17681
|
+
label
|
|
17682
|
+
};
|
|
17683
|
+
cum += w;
|
|
17684
|
+
return bar;
|
|
17685
|
+
});
|
|
17686
|
+
return { label, bars };
|
|
17687
|
+
} else {
|
|
17688
|
+
const bH = usable / seriesCount;
|
|
17689
|
+
const bars = normalizedSeries.map((s, si) => {
|
|
17690
|
+
const val = s.data[li]?.value || 0;
|
|
17691
|
+
return {
|
|
17692
|
+
x: padding.left,
|
|
17693
|
+
y: padding.top + li * groupH + gap / 2 + si * bH,
|
|
17694
|
+
width: val / max * chartWidth,
|
|
17695
|
+
height: bH,
|
|
17696
|
+
value: val,
|
|
17697
|
+
color: s.data[li]?.color || s.color,
|
|
17698
|
+
seriesName: s.name,
|
|
17699
|
+
label
|
|
17700
|
+
};
|
|
17701
|
+
});
|
|
17702
|
+
return { label, bars };
|
|
17703
|
+
}
|
|
17529
17704
|
} else {
|
|
17530
|
-
const
|
|
17531
|
-
const gap =
|
|
17532
|
-
|
|
17533
|
-
|
|
17534
|
-
|
|
17535
|
-
|
|
17536
|
-
|
|
17537
|
-
|
|
17538
|
-
|
|
17705
|
+
const groupW = chartWidth / labelCount;
|
|
17706
|
+
const gap = groupW * barGap;
|
|
17707
|
+
const usable = groupW - gap;
|
|
17708
|
+
if (stacked) {
|
|
17709
|
+
let cum = 0;
|
|
17710
|
+
const bars = normalizedSeries.map((s) => {
|
|
17711
|
+
const val = s.data[li]?.value || 0;
|
|
17712
|
+
const h = val / max * chartHeight;
|
|
17713
|
+
const bar = {
|
|
17714
|
+
x: padding.left + li * groupW + gap / 2,
|
|
17715
|
+
y: padding.top + chartHeight - cum - h,
|
|
17716
|
+
width: usable,
|
|
17717
|
+
height: h,
|
|
17718
|
+
value: val,
|
|
17719
|
+
color: s.data[li]?.color || s.color,
|
|
17720
|
+
seriesName: s.name,
|
|
17721
|
+
label
|
|
17722
|
+
};
|
|
17723
|
+
cum += h;
|
|
17724
|
+
return bar;
|
|
17725
|
+
});
|
|
17726
|
+
return { label, bars };
|
|
17727
|
+
} else {
|
|
17728
|
+
const bW = usable / seriesCount;
|
|
17729
|
+
const bars = normalizedSeries.map((s, si) => {
|
|
17730
|
+
const val = s.data[li]?.value || 0;
|
|
17731
|
+
return {
|
|
17732
|
+
x: padding.left + li * groupW + gap / 2 + si * bW,
|
|
17733
|
+
y: padding.top + chartHeight - val / max * chartHeight,
|
|
17734
|
+
width: bW,
|
|
17735
|
+
height: val / max * chartHeight,
|
|
17736
|
+
value: val,
|
|
17737
|
+
color: s.data[li]?.color || s.color,
|
|
17738
|
+
seriesName: s.name,
|
|
17739
|
+
label
|
|
17740
|
+
};
|
|
17741
|
+
});
|
|
17742
|
+
return { label, bars };
|
|
17743
|
+
}
|
|
17539
17744
|
}
|
|
17540
17745
|
});
|
|
17541
17746
|
const lines = [];
|
|
@@ -17543,106 +17748,102 @@ function BarChart({
|
|
|
17543
17748
|
for (let i = 0; i <= steps; i++) {
|
|
17544
17749
|
const value = i / steps * max;
|
|
17545
17750
|
if (horizontal) {
|
|
17546
|
-
lines.push({
|
|
17547
|
-
x: padding.left + i / steps * chartWidth,
|
|
17548
|
-
y1: padding.top,
|
|
17549
|
-
y2: height - padding.bottom,
|
|
17550
|
-
value
|
|
17551
|
-
});
|
|
17751
|
+
lines.push({ x: padding.left + i / steps * chartWidth, y1: padding.top, y2: height - padding.bottom, value });
|
|
17552
17752
|
} else {
|
|
17553
|
-
lines.push({
|
|
17554
|
-
y: padding.top + chartHeight - i / steps * chartHeight,
|
|
17555
|
-
x1: padding.left,
|
|
17556
|
-
x2: width - padding.right,
|
|
17557
|
-
value
|
|
17558
|
-
});
|
|
17753
|
+
lines.push({ y: padding.top + chartHeight - i / steps * chartHeight, x1: padding.left, x2: width - padding.right, value });
|
|
17559
17754
|
}
|
|
17560
17755
|
}
|
|
17561
|
-
return { maxValue: max,
|
|
17562
|
-
}, [
|
|
17756
|
+
return { maxValue: max, barGroups: groups, gridLines: lines };
|
|
17757
|
+
}, [normalizedSeries, chartWidth, chartHeight, horizontal, stacked, barGap, padding, width, height]);
|
|
17563
17758
|
return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
|
|
17564
17759
|
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("svg", { ref: svgRef, width, height, className: `overflow-visible ${className}`, style: { fontFamily: "inherit" }, children: [
|
|
17565
17760
|
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
17761
|
/* @__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) })
|
|
17762
|
+
/* @__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
17763
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
|
|
17569
17764
|
/* @__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) })
|
|
17765
|
+
/* @__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
17766
|
] }) }, 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
|
-
|
|
17767
|
+
barGroups.map((group, gi) => /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("g", { children: [
|
|
17768
|
+
group.bars.map((bar, bi) => {
|
|
17769
|
+
const animDelay = gi * 0.08 + bi * 0.04;
|
|
17770
|
+
return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
|
|
17771
|
+
"g",
|
|
17772
|
+
{
|
|
17773
|
+
onMouseEnter: () => setHoveredBar({
|
|
17774
|
+
x: horizontal ? bar.x + bar.width : bar.x + bar.width / 2,
|
|
17775
|
+
y: horizontal ? bar.y + bar.height / 2 : bar.y,
|
|
17776
|
+
label: bar.label,
|
|
17777
|
+
value: bar.value,
|
|
17778
|
+
color: bar.color,
|
|
17779
|
+
seriesName: bar.seriesName
|
|
17780
|
+
}),
|
|
17781
|
+
onMouseLeave: () => setHoveredBar(null),
|
|
17782
|
+
className: "cursor-pointer",
|
|
17783
|
+
children: [
|
|
17784
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
|
|
17785
|
+
"rect",
|
|
17786
|
+
{
|
|
17787
|
+
x: bar.x,
|
|
17788
|
+
y: bar.y,
|
|
17789
|
+
width: bar.width,
|
|
17790
|
+
height: bar.height,
|
|
17791
|
+
rx: barRadius,
|
|
17792
|
+
ry: barRadius,
|
|
17793
|
+
fill: bar.color,
|
|
17794
|
+
className: `transition-all duration-150 ${hoveredBar?.label === bar.label && hoveredBar?.seriesName === bar.seriesName ? "opacity-80" : ""}`,
|
|
17795
|
+
style: animated ? {
|
|
17796
|
+
animation: horizontal ? `growWidth 0.5s ease-out ${animDelay}s forwards` : `growHeight 0.5s ease-out ${animDelay}s forwards`,
|
|
17797
|
+
...horizontal ? { width: 0 } : { height: 0, y: padding.top + chartHeight }
|
|
17798
|
+
} : void 0,
|
|
17799
|
+
children: [
|
|
17800
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
17801
|
+
"animate",
|
|
17802
|
+
{
|
|
17803
|
+
attributeName: horizontal ? "width" : "height",
|
|
17804
|
+
from: "0",
|
|
17805
|
+
to: horizontal ? bar.width : bar.height,
|
|
17806
|
+
dur: "0.5s",
|
|
17807
|
+
begin: `${animDelay}s`,
|
|
17808
|
+
fill: "freeze"
|
|
17809
|
+
}
|
|
17810
|
+
),
|
|
17811
|
+
!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" })
|
|
17812
|
+
]
|
|
17813
|
+
}
|
|
17814
|
+
),
|
|
17815
|
+
showValues && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
17816
|
+
"text",
|
|
17817
|
+
{
|
|
17818
|
+
x: horizontal ? bar.x + bar.width + 8 : bar.x + bar.width / 2,
|
|
17819
|
+
y: horizontal ? bar.y + bar.height / 2 + 4 : bar.y - 8,
|
|
17820
|
+
textAnchor: horizontal ? "start" : "middle",
|
|
17821
|
+
fontSize: "11",
|
|
17822
|
+
fontWeight: "500",
|
|
17823
|
+
className: "text-foreground",
|
|
17824
|
+
fill: "currentColor",
|
|
17825
|
+
style: animated ? { opacity: 0, animation: `fadeIn 0.3s ease-out ${animDelay + 0.3}s forwards` } : void 0,
|
|
17826
|
+
children: formatValue ? formatValue(bar.value) : bar.value
|
|
17827
|
+
}
|
|
17828
|
+
)
|
|
17829
|
+
]
|
|
17830
|
+
},
|
|
17831
|
+
bi
|
|
17832
|
+
);
|
|
17833
|
+
}),
|
|
17834
|
+
showLabels && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
17835
|
+
"text",
|
|
17836
|
+
{
|
|
17837
|
+
x: horizontal ? padding.left - 8 : padding.left + (gi + 0.5) * (chartWidth / barGroups.length),
|
|
17838
|
+
y: horizontal ? padding.top + (gi + 0.5) * (chartHeight / barGroups.length) + 4 : height - 10,
|
|
17839
|
+
textAnchor: horizontal ? "end" : "middle",
|
|
17840
|
+
fontSize: "10",
|
|
17841
|
+
className: "text-muted-foreground",
|
|
17842
|
+
fill: "currentColor",
|
|
17843
|
+
children: group.label
|
|
17844
|
+
}
|
|
17845
|
+
)
|
|
17846
|
+
] }, gi)),
|
|
17646
17847
|
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("style", { children: `
|
|
17647
17848
|
@keyframes growHeight {
|
|
17648
17849
|
from { height: 0; }
|
|
@@ -17656,16 +17857,21 @@ function BarChart({
|
|
|
17656
17857
|
}
|
|
17657
17858
|
` })
|
|
17658
17859
|
] }),
|
|
17860
|
+
(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: [
|
|
17861
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { className: "w-3 h-3 rounded-md", style: { backgroundColor: s.color } }),
|
|
17862
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("span", { className: "text-muted-foreground", children: s.name })
|
|
17863
|
+
] }, i)) }),
|
|
17659
17864
|
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
17660
17865
|
ChartTooltip,
|
|
17661
17866
|
{
|
|
17662
17867
|
x: hoveredBar?.x ?? 0,
|
|
17663
17868
|
y: hoveredBar?.y ?? 0,
|
|
17664
17869
|
visible: !!hoveredBar,
|
|
17665
|
-
label: hoveredBar?.label,
|
|
17870
|
+
label: hoveredBar?.seriesName ? `${hoveredBar.label} \u2014 ${hoveredBar.seriesName}` : hoveredBar?.label,
|
|
17666
17871
|
value: hoveredBar?.value,
|
|
17667
17872
|
color: hoveredBar?.color,
|
|
17668
|
-
containerRef: svgRef
|
|
17873
|
+
containerRef: svgRef,
|
|
17874
|
+
formatter: formatValue ? (v) => formatValue(Number(v)) : void 0
|
|
17669
17875
|
}
|
|
17670
17876
|
)
|
|
17671
17877
|
] });
|
|
@@ -17684,6 +17890,8 @@ function PieChart({
|
|
|
17684
17890
|
showPercentage = true,
|
|
17685
17891
|
animated = true,
|
|
17686
17892
|
startAngle = -90,
|
|
17893
|
+
renderCenter,
|
|
17894
|
+
formatValue,
|
|
17687
17895
|
className = ""
|
|
17688
17896
|
}) {
|
|
17689
17897
|
const containerRef = (0, import_react27.useRef)(null);
|
|
@@ -17779,17 +17987,17 @@ function PieChart({
|
|
|
17779
17987
|
className: "text-foreground",
|
|
17780
17988
|
fill: "currentColor",
|
|
17781
17989
|
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
|
|
17990
|
+
children: showPercentage ? `${(seg.percentage * 100).toFixed(0)}%` : formatValue ? formatValue(seg.value) : seg.value
|
|
17783
17991
|
}
|
|
17784
17992
|
)
|
|
17785
17993
|
]
|
|
17786
17994
|
},
|
|
17787
17995
|
i
|
|
17788
17996
|
)),
|
|
17789
|
-
donut && /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("g", { children: [
|
|
17997
|
+
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
17998
|
/* @__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
|
-
] })
|
|
17999
|
+
/* @__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 })
|
|
18000
|
+
] }))
|
|
17793
18001
|
] }),
|
|
17794
18002
|
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("style", { children: `
|
|
17795
18003
|
@keyframes pieSlice {
|
|
@@ -17816,7 +18024,7 @@ function PieChart({
|
|
|
17816
18024
|
children: [
|
|
17817
18025
|
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "w-3 h-3 rounded-md shrink-0", style: { backgroundColor: seg.color } }),
|
|
17818
18026
|
/* @__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 })
|
|
18027
|
+
/* @__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
18028
|
]
|
|
17821
18029
|
},
|
|
17822
18030
|
i
|
|
@@ -17839,26 +18047,6 @@ function PieChart({
|
|
|
17839
18047
|
// ../../components/ui/AreaChart.tsx
|
|
17840
18048
|
var import_react28 = require("react");
|
|
17841
18049
|
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
18050
|
function AreaChart({
|
|
17863
18051
|
series,
|
|
17864
18052
|
width = 400,
|
|
@@ -17870,6 +18058,8 @@ function AreaChart({
|
|
|
17870
18058
|
animated = true,
|
|
17871
18059
|
stacked = false,
|
|
17872
18060
|
curved = true,
|
|
18061
|
+
formatValue,
|
|
18062
|
+
emptyText = "No data",
|
|
17873
18063
|
className = ""
|
|
17874
18064
|
}) {
|
|
17875
18065
|
const containerRef = (0, import_react28.useRef)(null);
|
|
@@ -17877,6 +18067,15 @@ function AreaChart({
|
|
|
17877
18067
|
const chartWidth = width - padding.left - padding.right;
|
|
17878
18068
|
const chartHeight = height - padding.top - padding.bottom;
|
|
17879
18069
|
const [hoveredPoint, setHoveredPoint] = (0, import_react28.useState)(null);
|
|
18070
|
+
const [hiddenSeries, setHiddenSeries] = (0, import_react28.useState)(/* @__PURE__ */ new Set());
|
|
18071
|
+
const toggleSeries = (0, import_react28.useCallback)((name) => {
|
|
18072
|
+
setHiddenSeries((prev) => {
|
|
18073
|
+
const next = new Set(prev);
|
|
18074
|
+
if (next.has(name)) next.delete(name);
|
|
18075
|
+
else next.add(name);
|
|
18076
|
+
return next;
|
|
18077
|
+
});
|
|
18078
|
+
}, []);
|
|
17880
18079
|
const { processedSeries, gridLines, maxValue, labels } = (0, import_react28.useMemo)(() => {
|
|
17881
18080
|
if (!series.length || !series[0]?.data?.length) {
|
|
17882
18081
|
return { processedSeries: [], gridLines: [], maxValue: 0, labels: [] };
|
|
@@ -17949,14 +18148,14 @@ function AreaChart({
|
|
|
17949
18148
|
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("svg", { width, height, className: "overflow-visible", style: { fontFamily: "inherit" }, children: [
|
|
17950
18149
|
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
18150
|
/* @__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) })
|
|
18151
|
+
/* @__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
18152
|
] }, i)) }),
|
|
17954
18153
|
[...processedSeries].reverse().map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
17955
18154
|
"path",
|
|
17956
18155
|
{
|
|
17957
18156
|
d: s.areaPath,
|
|
17958
18157
|
fill: s.color,
|
|
17959
|
-
fillOpacity: s.fillOpacity ?? 0.3,
|
|
18158
|
+
fillOpacity: hiddenSeries.has(s.name) ? 0 : s.fillOpacity ?? 0.3,
|
|
17960
18159
|
className: "transition-all duration-300",
|
|
17961
18160
|
style: animated ? {
|
|
17962
18161
|
opacity: 0,
|
|
@@ -17974,6 +18173,8 @@ function AreaChart({
|
|
|
17974
18173
|
strokeWidth: 2,
|
|
17975
18174
|
strokeLinecap: "round",
|
|
17976
18175
|
strokeLinejoin: "round",
|
|
18176
|
+
className: "transition-opacity duration-300",
|
|
18177
|
+
opacity: hiddenSeries.has(s.name) ? 0 : 1,
|
|
17977
18178
|
style: animated ? {
|
|
17978
18179
|
strokeDasharray: s.lineLength,
|
|
17979
18180
|
strokeDashoffset: s.lineLength,
|
|
@@ -17983,7 +18184,7 @@ function AreaChart({
|
|
|
17983
18184
|
`line-${i}`
|
|
17984
18185
|
)),
|
|
17985
18186
|
showDots && processedSeries.map(
|
|
17986
|
-
(s, seriesIdx) => s.points.map((point, i) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(
|
|
18187
|
+
(s, seriesIdx) => !hiddenSeries.has(s.name) && s.points.map((point, i) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(
|
|
17987
18188
|
"g",
|
|
17988
18189
|
{
|
|
17989
18190
|
onMouseEnter: () => {
|
|
@@ -18067,11 +18268,18 @@ function AreaChart({
|
|
|
18067
18268
|
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
18269
|
"div",
|
|
18069
18270
|
{
|
|
18070
|
-
className: "flex items-center gap-2 text-sm",
|
|
18271
|
+
className: "flex items-center gap-2 text-sm cursor-pointer select-none",
|
|
18071
18272
|
style: animated ? { opacity: 0, animation: `fadeIn 0.3s ease-out ${i * 0.1 + 0.5}s forwards` } : void 0,
|
|
18273
|
+
onClick: () => toggleSeries(s.name),
|
|
18072
18274
|
children: [
|
|
18073
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
18074
|
-
|
|
18275
|
+
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
18276
|
+
"div",
|
|
18277
|
+
{
|
|
18278
|
+
className: "w-3 h-3 rounded-md transition-opacity",
|
|
18279
|
+
style: { backgroundColor: s.color, opacity: hiddenSeries.has(s.name) ? 0.3 : 1 }
|
|
18280
|
+
}
|
|
18281
|
+
),
|
|
18282
|
+
/* @__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
18283
|
]
|
|
18076
18284
|
},
|
|
18077
18285
|
i
|
|
@@ -18093,26 +18301,6 @@ function AreaChart({
|
|
|
18093
18301
|
// ../../components/ui/Sparkline.tsx
|
|
18094
18302
|
var import_react29 = require("react");
|
|
18095
18303
|
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
18304
|
function Sparkline({
|
|
18117
18305
|
data,
|
|
18118
18306
|
width = 100,
|
|
@@ -18143,7 +18331,7 @@ function Sparkline({
|
|
|
18143
18331
|
y: padding + chartHeight - (value - min) / range * chartHeight,
|
|
18144
18332
|
value
|
|
18145
18333
|
}));
|
|
18146
|
-
const line = curved ?
|
|
18334
|
+
const line = curved ? getCatmullRomSpline(pts) : `M ${pts.map((p) => `${p.x} ${p.y}`).join(" L ")}`;
|
|
18147
18335
|
const area = `${line} L ${padding + chartWidth} ${padding + chartHeight} L ${padding} ${padding + chartHeight} Z`;
|
|
18148
18336
|
const length = pts.reduce((acc, p, i) => {
|
|
18149
18337
|
if (i === 0) return 0;
|
|
@@ -18244,12 +18432,22 @@ function RadarChart({
|
|
|
18244
18432
|
showLegend = true,
|
|
18245
18433
|
showValues = false,
|
|
18246
18434
|
animated = true,
|
|
18435
|
+
formatValue,
|
|
18247
18436
|
className = ""
|
|
18248
18437
|
}) {
|
|
18249
18438
|
const containerRef = (0, import_react30.useRef)(null);
|
|
18250
18439
|
const center = size / 2;
|
|
18251
18440
|
const radius = size / 2 - 40;
|
|
18252
18441
|
const [hoveredPoint, setHoveredPoint] = (0, import_react30.useState)(null);
|
|
18442
|
+
const [hiddenSeries, setHiddenSeries] = (0, import_react30.useState)(/* @__PURE__ */ new Set());
|
|
18443
|
+
const toggleSeries = (0, import_react30.useCallback)((name) => {
|
|
18444
|
+
setHiddenSeries((prev) => {
|
|
18445
|
+
const next = new Set(prev);
|
|
18446
|
+
if (next.has(name)) next.delete(name);
|
|
18447
|
+
else next.add(name);
|
|
18448
|
+
return next;
|
|
18449
|
+
});
|
|
18450
|
+
}, []);
|
|
18253
18451
|
const { axes, processedSeries, levelPaths } = (0, import_react30.useMemo)(() => {
|
|
18254
18452
|
if (!series.length || !series[0]?.data?.length) {
|
|
18255
18453
|
return { axes: [], processedSeries: [], levelPaths: [] };
|
|
@@ -18304,81 +18502,83 @@ function RadarChart({
|
|
|
18304
18502
|
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("svg", { width: size, height: size, className: "overflow-visible", style: { fontFamily: "inherit" }, children: [
|
|
18305
18503
|
/* @__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
18504
|
/* @__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
|
-
|
|
18505
|
+
processedSeries.map(
|
|
18506
|
+
(s, i) => !hiddenSeries.has(s.name) && /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("g", { children: [
|
|
18507
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
18508
|
+
"path",
|
|
18509
|
+
{
|
|
18510
|
+
d: s.path,
|
|
18511
|
+
fill: s.color,
|
|
18512
|
+
fillOpacity: s.fillOpacity ?? 0.2,
|
|
18513
|
+
stroke: s.color,
|
|
18514
|
+
strokeWidth: 2,
|
|
18515
|
+
strokeLinejoin: "round",
|
|
18516
|
+
className: "transition-all duration-300",
|
|
18517
|
+
style: animated ? {
|
|
18518
|
+
opacity: 0,
|
|
18519
|
+
transform: "scale(0)",
|
|
18520
|
+
transformOrigin: `${center}px ${center}px`,
|
|
18521
|
+
animation: `radarPop 0.5s ease-out ${i * 0.15}s forwards`
|
|
18522
|
+
} : void 0
|
|
18523
|
+
}
|
|
18524
|
+
),
|
|
18525
|
+
s.points.map((point, j) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
|
|
18526
|
+
"g",
|
|
18527
|
+
{
|
|
18528
|
+
onMouseEnter: () => {
|
|
18529
|
+
const items = processedSeries.map((ps) => ({
|
|
18530
|
+
label: ps.name,
|
|
18531
|
+
value: ps.points[j]?.value ?? 0,
|
|
18532
|
+
color: ps.color
|
|
18533
|
+
}));
|
|
18534
|
+
setHoveredPoint({
|
|
18535
|
+
x: point.x,
|
|
18536
|
+
y: point.y,
|
|
18537
|
+
axis: series[0]?.data[j]?.axis ?? "",
|
|
18538
|
+
items
|
|
18539
|
+
});
|
|
18540
|
+
},
|
|
18541
|
+
onMouseLeave: () => setHoveredPoint(null),
|
|
18542
|
+
className: "cursor-pointer",
|
|
18543
|
+
children: [
|
|
18544
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
18545
|
+
"circle",
|
|
18546
|
+
{
|
|
18547
|
+
cx: point.x,
|
|
18548
|
+
cy: point.y,
|
|
18549
|
+
r: hoveredPoint?.axis === series[0]?.data[j]?.axis ? 6 : 4,
|
|
18550
|
+
fill: s.color,
|
|
18551
|
+
className: "transition-all duration-150",
|
|
18552
|
+
style: animated ? {
|
|
18553
|
+
opacity: 0,
|
|
18554
|
+
transform: "scale(0)",
|
|
18555
|
+
transformOrigin: `${point.x}px ${point.y}px`,
|
|
18556
|
+
animation: `dotPop 0.3s ease-out ${i * 0.15 + j * 0.05 + 0.3}s forwards`
|
|
18557
|
+
} : void 0
|
|
18558
|
+
}
|
|
18559
|
+
),
|
|
18560
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("circle", { cx: point.x, cy: point.y, r: 12, fill: "transparent" })
|
|
18561
|
+
]
|
|
18341
18562
|
},
|
|
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)),
|
|
18563
|
+
j
|
|
18564
|
+
)),
|
|
18565
|
+
showValues && s.points.map((point, j) => /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
18566
|
+
"text",
|
|
18567
|
+
{
|
|
18568
|
+
x: point.x,
|
|
18569
|
+
y: point.y - 10,
|
|
18570
|
+
textAnchor: "middle",
|
|
18571
|
+
fontSize: "10",
|
|
18572
|
+
fontWeight: "500",
|
|
18573
|
+
className: "text-foreground",
|
|
18574
|
+
fill: "currentColor",
|
|
18575
|
+
style: animated ? { opacity: 0, animation: `fadeIn 0.3s ease-out ${i * 0.15 + 0.5}s forwards` } : void 0,
|
|
18576
|
+
children: point.value
|
|
18577
|
+
},
|
|
18578
|
+
`val-${j}`
|
|
18579
|
+
))
|
|
18580
|
+
] }, i)
|
|
18581
|
+
),
|
|
18382
18582
|
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
18583
|
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)("style", { children: `
|
|
18384
18584
|
@keyframes radarPop {
|
|
@@ -18410,11 +18610,18 @@ function RadarChart({
|
|
|
18410
18610
|
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
18611
|
"div",
|
|
18412
18612
|
{
|
|
18413
|
-
className: "flex items-center gap-2 text-sm",
|
|
18613
|
+
className: "flex items-center gap-2 text-sm cursor-pointer select-none",
|
|
18414
18614
|
style: animated ? { opacity: 0, animation: `fadeIn 0.3s ease-out ${i * 0.1 + 0.5}s forwards` } : void 0,
|
|
18615
|
+
onClick: () => toggleSeries(s.name),
|
|
18415
18616
|
children: [
|
|
18416
|
-
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
18417
|
-
|
|
18617
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
18618
|
+
"div",
|
|
18619
|
+
{
|
|
18620
|
+
className: "w-3 h-3 rounded-md transition-opacity",
|
|
18621
|
+
style: { backgroundColor: s.color, opacity: hiddenSeries.has(s.name) ? 0.3 : 1 }
|
|
18622
|
+
}
|
|
18623
|
+
),
|
|
18624
|
+
/* @__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
18625
|
]
|
|
18419
18626
|
},
|
|
18420
18627
|
i
|
|
@@ -18450,11 +18657,13 @@ function GaugeChart({
|
|
|
18450
18657
|
animated = true,
|
|
18451
18658
|
startAngle = -135,
|
|
18452
18659
|
endAngle = 135,
|
|
18660
|
+
zones,
|
|
18661
|
+
formatValue,
|
|
18453
18662
|
className = ""
|
|
18454
18663
|
}) {
|
|
18455
18664
|
const center = size / 2;
|
|
18456
18665
|
const radius = center - thickness / 2 - 10;
|
|
18457
|
-
const { backgroundPath, valuePath, percentage, needleAngle } = (0, import_react31.useMemo)(() => {
|
|
18666
|
+
const { backgroundPath, valuePath, percentage, needleAngle, zonePaths } = (0, import_react31.useMemo)(() => {
|
|
18458
18667
|
const normalizedValue = Math.min(Math.max(value, min), max);
|
|
18459
18668
|
const pct = (normalizedValue - min) / (max - min);
|
|
18460
18669
|
const totalAngle = endAngle - startAngle;
|
|
@@ -18472,13 +18681,19 @@ function GaugeChart({
|
|
|
18472
18681
|
const largeArc = Math.abs(end - start) > 180 ? 1 : 0;
|
|
18473
18682
|
return `M ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArc} 1 ${endPoint.x} ${endPoint.y}`;
|
|
18474
18683
|
};
|
|
18684
|
+
const zonePaths2 = (zones ?? []).map((zone) => {
|
|
18685
|
+
const zoneStart = startAngle + (Math.max(zone.min, min) - min) / (max - min) * totalAngle;
|
|
18686
|
+
const zoneEnd = startAngle + (Math.min(zone.max, max) - min) / (max - min) * totalAngle;
|
|
18687
|
+
return { path: createArc(zoneStart, zoneEnd), color: zone.color };
|
|
18688
|
+
});
|
|
18475
18689
|
return {
|
|
18476
18690
|
backgroundPath: createArc(startAngle, endAngle),
|
|
18477
18691
|
valuePath: createArc(startAngle, currentAngle),
|
|
18478
18692
|
percentage: pct,
|
|
18479
|
-
needleAngle: currentAngle
|
|
18693
|
+
needleAngle: currentAngle,
|
|
18694
|
+
zonePaths: zonePaths2
|
|
18480
18695
|
};
|
|
18481
|
-
}, [value, min, max, center, radius, startAngle, endAngle]);
|
|
18696
|
+
}, [value, min, max, center, radius, startAngle, endAngle, zones]);
|
|
18482
18697
|
const needleLength = radius - 10;
|
|
18483
18698
|
const needleAngleRad = needleAngle * Math.PI / 180;
|
|
18484
18699
|
const needleX = center + needleLength * Math.cos(needleAngleRad);
|
|
@@ -18495,6 +18710,7 @@ function GaugeChart({
|
|
|
18495
18710
|
className: !backgroundColor ? "text-muted-foreground/20" : ""
|
|
18496
18711
|
}
|
|
18497
18712
|
),
|
|
18713
|
+
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
18714
|
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
18499
18715
|
"path",
|
|
18500
18716
|
{
|
|
@@ -18565,7 +18781,7 @@ function GaugeChart({
|
|
|
18565
18781
|
className: "text-foreground",
|
|
18566
18782
|
fill: "currentColor",
|
|
18567
18783
|
style: animated ? { opacity: 0, animation: "fadeIn 0.5s ease-out 0.5s forwards" } : void 0,
|
|
18568
|
-
children: value
|
|
18784
|
+
children: formatValue ? formatValue(value) : value
|
|
18569
18785
|
}
|
|
18570
18786
|
),
|
|
18571
18787
|
label && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|