@kjaniec-dev/ui 0.9.3 → 1.0.0
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 +2140 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +388 -1
- package/dist/index.d.ts +388 -1
- package/dist/index.js +2124 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -550,9 +550,18 @@ var CheckboxField = React52__namespace.forwardRef(
|
|
|
550
550
|
);
|
|
551
551
|
CheckboxField.displayName = "CheckboxField";
|
|
552
552
|
var Switch = React52__namespace.forwardRef(
|
|
553
|
-
({ className, label, id, ...props }, ref) => {
|
|
553
|
+
({ className, label, id, checked, defaultChecked, onChange, ...props }, ref) => {
|
|
554
554
|
const autoId = React52__namespace.useId();
|
|
555
555
|
const inputId = id ?? autoId;
|
|
556
|
+
const isControlled = checked !== void 0;
|
|
557
|
+
const [internalChecked, setInternalChecked] = React52__namespace.useState(Boolean(defaultChecked));
|
|
558
|
+
const isChecked = isControlled ? Boolean(checked) : internalChecked;
|
|
559
|
+
const handleChange = (e) => {
|
|
560
|
+
if (!isControlled) {
|
|
561
|
+
setInternalChecked(e.target.checked);
|
|
562
|
+
}
|
|
563
|
+
onChange?.(e);
|
|
564
|
+
};
|
|
556
565
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
557
566
|
"label",
|
|
558
567
|
{
|
|
@@ -569,6 +578,10 @@ var Switch = React52__namespace.forwardRef(
|
|
|
569
578
|
id: inputId,
|
|
570
579
|
type: "checkbox",
|
|
571
580
|
role: "switch",
|
|
581
|
+
"aria-checked": isChecked,
|
|
582
|
+
checked: isControlled ? checked : void 0,
|
|
583
|
+
defaultChecked: !isControlled ? defaultChecked : void 0,
|
|
584
|
+
onChange: handleChange,
|
|
572
585
|
className: "peer sr-only",
|
|
573
586
|
...props
|
|
574
587
|
}
|
|
@@ -1675,16 +1688,44 @@ function ToastProvider({ children }) {
|
|
|
1675
1688
|
"div",
|
|
1676
1689
|
{
|
|
1677
1690
|
role: "status",
|
|
1678
|
-
onClick: () => remove(t.id),
|
|
1679
1691
|
className: cn(
|
|
1680
|
-
"flex items-center gap-
|
|
1692
|
+
"flex items-center justify-between gap-3 min-w-[280px] px-4 py-3",
|
|
1681
1693
|
"bg-surface border border-border border-l-[3px] rounded-kj-md shadow-kj-lg text-[0.85rem] font-medium",
|
|
1682
1694
|
toneBorder[t.tone],
|
|
1683
1695
|
t.leaving ? "animate-[kjtoastout_.25s_ease_forwards]" : "animate-[kjtoastin_.25s_ease]"
|
|
1684
1696
|
),
|
|
1685
1697
|
children: [
|
|
1686
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1687
|
-
|
|
1698
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
|
|
1699
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "grid place-items-center", children: icons[t.tone] }),
|
|
1700
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t.message })
|
|
1701
|
+
] }),
|
|
1702
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1703
|
+
"button",
|
|
1704
|
+
{
|
|
1705
|
+
type: "button",
|
|
1706
|
+
"aria-label": "Dismiss notification",
|
|
1707
|
+
onClick: () => remove(t.id),
|
|
1708
|
+
className: "ml-auto text-muted-foreground hover:text-foreground p-1 -mr-1 rounded-kj-sm transition-colors cursor-pointer",
|
|
1709
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1710
|
+
"svg",
|
|
1711
|
+
{
|
|
1712
|
+
width: 14,
|
|
1713
|
+
height: 14,
|
|
1714
|
+
viewBox: "0 0 24 24",
|
|
1715
|
+
fill: "none",
|
|
1716
|
+
stroke: "currentColor",
|
|
1717
|
+
strokeWidth: 2,
|
|
1718
|
+
strokeLinecap: "round",
|
|
1719
|
+
strokeLinejoin: "round",
|
|
1720
|
+
"aria-hidden": "true",
|
|
1721
|
+
children: [
|
|
1722
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
1723
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
1724
|
+
]
|
|
1725
|
+
}
|
|
1726
|
+
)
|
|
1727
|
+
}
|
|
1728
|
+
)
|
|
1688
1729
|
]
|
|
1689
1730
|
},
|
|
1690
1731
|
t.id
|
|
@@ -1910,7 +1951,7 @@ function DataTable({
|
|
|
1910
1951
|
onSelectionChange(next);
|
|
1911
1952
|
};
|
|
1912
1953
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-4 w-full", className), children: [
|
|
1913
|
-
toolbar && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "
|
|
1954
|
+
toolbar && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full min-w-0", children: toolbar }),
|
|
1914
1955
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1915
1956
|
TableWrap,
|
|
1916
1957
|
{
|
|
@@ -2383,12 +2424,12 @@ var TableToolbar = React52__namespace.forwardRef(
|
|
|
2383
2424
|
{
|
|
2384
2425
|
ref,
|
|
2385
2426
|
className: cn(
|
|
2386
|
-
"flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-3 w-full pb-1",
|
|
2427
|
+
"flex flex-col sm:flex-row flex-wrap items-stretch sm:items-center justify-between gap-3 w-full pb-1",
|
|
2387
2428
|
className
|
|
2388
2429
|
),
|
|
2389
2430
|
...props,
|
|
2390
2431
|
children: [
|
|
2391
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col sm:flex-row items-stretch sm:items-center gap-2 max-w-xl", children: [
|
|
2432
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col sm:flex-row sm:flex-wrap items-stretch sm:items-center gap-2 min-w-0 max-w-xl", children: [
|
|
2392
2433
|
onSearchChange && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2393
2434
|
Input,
|
|
2394
2435
|
{
|
|
@@ -2396,7 +2437,7 @@ var TableToolbar = React52__namespace.forwardRef(
|
|
|
2396
2437
|
placeholder: searchPlaceholder,
|
|
2397
2438
|
value: searchQuery,
|
|
2398
2439
|
onChange: (e) => onSearchChange(e.target.value),
|
|
2399
|
-
className: "sm:w-64",
|
|
2440
|
+
className: "w-full sm:w-64 max-w-full",
|
|
2400
2441
|
leadingIcon: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2401
2442
|
"svg",
|
|
2402
2443
|
{
|
|
@@ -2417,9 +2458,9 @@ var TableToolbar = React52__namespace.forwardRef(
|
|
|
2417
2458
|
)
|
|
2418
2459
|
}
|
|
2419
2460
|
),
|
|
2420
|
-
filters && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: filters })
|
|
2461
|
+
filters && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2 min-w-0", children: filters })
|
|
2421
2462
|
] }),
|
|
2422
|
-
actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 shrink-0 justify-end", children: actions })
|
|
2463
|
+
actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2 shrink-0 justify-end sm:ml-auto", children: actions })
|
|
2423
2464
|
]
|
|
2424
2465
|
}
|
|
2425
2466
|
);
|
|
@@ -3005,7 +3046,6 @@ function BottomSheet({
|
|
|
3005
3046
|
return /* @__PURE__ */ jsxRuntime.jsx(BottomSheetContext.Provider, { value: { titleId, descId }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3006
3047
|
"div",
|
|
3007
3048
|
{
|
|
3008
|
-
role: "presentation",
|
|
3009
3049
|
onClick: (e) => e.target === e.currentTarget && onClose(),
|
|
3010
3050
|
className: cn(
|
|
3011
3051
|
"fixed inset-0 z-[100] grid items-end sm:place-items-center p-0 sm:p-6 backdrop-blur-[3px] transition-all duration-200",
|
|
@@ -7305,6 +7345,2077 @@ var InPostGeowidgetModal = ({
|
|
|
7305
7345
|
] });
|
|
7306
7346
|
};
|
|
7307
7347
|
|
|
7348
|
+
// src/components/chart-math.ts
|
|
7349
|
+
function formatCoordinate(val) {
|
|
7350
|
+
if (!Number.isFinite(val)) return "0";
|
|
7351
|
+
return Number.isInteger(val) ? val.toString() : Number(val.toFixed(2)).toString();
|
|
7352
|
+
}
|
|
7353
|
+
function niceNum(range2, round) {
|
|
7354
|
+
if (range2 <= 0 || !Number.isFinite(range2)) return 1;
|
|
7355
|
+
const exponent = Math.floor(Math.log10(range2));
|
|
7356
|
+
const fraction = range2 / 10 ** exponent;
|
|
7357
|
+
let niceFraction;
|
|
7358
|
+
{
|
|
7359
|
+
if (fraction < 1.5) niceFraction = 1;
|
|
7360
|
+
else if (fraction < 3) niceFraction = 2;
|
|
7361
|
+
else if (fraction < 7) niceFraction = 5;
|
|
7362
|
+
else niceFraction = 10;
|
|
7363
|
+
}
|
|
7364
|
+
return niceFraction * 10 ** exponent;
|
|
7365
|
+
}
|
|
7366
|
+
function getNiceScale(minInput, maxInput, tickCount = 5) {
|
|
7367
|
+
let min = Number.isFinite(minInput) ? minInput : 0;
|
|
7368
|
+
let max = Number.isFinite(maxInput) ? maxInput : 1;
|
|
7369
|
+
if (min > max) {
|
|
7370
|
+
const temp = min;
|
|
7371
|
+
min = max;
|
|
7372
|
+
max = temp;
|
|
7373
|
+
}
|
|
7374
|
+
if (min === max) {
|
|
7375
|
+
if (min === 0) {
|
|
7376
|
+
min = 0;
|
|
7377
|
+
max = 1;
|
|
7378
|
+
} else {
|
|
7379
|
+
const delta = Math.abs(min) * 0.2 || 1;
|
|
7380
|
+
min = min - delta;
|
|
7381
|
+
max = max + delta;
|
|
7382
|
+
}
|
|
7383
|
+
}
|
|
7384
|
+
const rawRange = max - min;
|
|
7385
|
+
const count = Math.max(2, tickCount);
|
|
7386
|
+
const step = niceNum(rawRange / count);
|
|
7387
|
+
const niceMin = Math.floor(min / step) * step;
|
|
7388
|
+
const niceMax = Math.ceil(max / step) * step;
|
|
7389
|
+
const ticks = [];
|
|
7390
|
+
const totalTicks = Math.round((niceMax - niceMin) / step);
|
|
7391
|
+
for (let i = 0; i <= totalTicks; i++) {
|
|
7392
|
+
const val = niceMin + i * step;
|
|
7393
|
+
ticks.push(Number(parseFloat(val.toPrecision(12))));
|
|
7394
|
+
}
|
|
7395
|
+
return {
|
|
7396
|
+
min: Number(parseFloat(niceMin.toPrecision(12))),
|
|
7397
|
+
max: Number(parseFloat(niceMax.toPrecision(12))),
|
|
7398
|
+
ticks
|
|
7399
|
+
};
|
|
7400
|
+
}
|
|
7401
|
+
function linearScale(val, domain, range2) {
|
|
7402
|
+
if (!Number.isFinite(val)) return range2[0];
|
|
7403
|
+
const [d0, d1] = domain;
|
|
7404
|
+
const [r0, r1] = range2;
|
|
7405
|
+
const domainDelta = d1 - d0;
|
|
7406
|
+
if (domainDelta === 0) {
|
|
7407
|
+
return r0;
|
|
7408
|
+
}
|
|
7409
|
+
const ratio = (val - d0) / domainDelta;
|
|
7410
|
+
return r0 + ratio * (r1 - r0);
|
|
7411
|
+
}
|
|
7412
|
+
function generateLinePath(points, curve = "linear") {
|
|
7413
|
+
const n = points.length;
|
|
7414
|
+
if (n === 0) return "";
|
|
7415
|
+
if (n === 1) return `M ${formatCoordinate(points[0].x)} ${formatCoordinate(points[0].y)}`;
|
|
7416
|
+
if (curve === "step") {
|
|
7417
|
+
const commands2 = [
|
|
7418
|
+
`M ${formatCoordinate(points[0].x)} ${formatCoordinate(points[0].y)}`
|
|
7419
|
+
];
|
|
7420
|
+
for (let i = 1; i < n; i++) {
|
|
7421
|
+
const p = points[i];
|
|
7422
|
+
commands2.push(`H ${formatCoordinate(p.x)} V ${formatCoordinate(p.y)}`);
|
|
7423
|
+
}
|
|
7424
|
+
return commands2.join(" ");
|
|
7425
|
+
}
|
|
7426
|
+
if (curve === "smooth") {
|
|
7427
|
+
const commands2 = [
|
|
7428
|
+
`M ${formatCoordinate(points[0].x)} ${formatCoordinate(points[0].y)}`
|
|
7429
|
+
];
|
|
7430
|
+
for (let i = 0; i < n - 1; i++) {
|
|
7431
|
+
const p0 = i > 0 ? points[i - 1] : {
|
|
7432
|
+
x: 2 * points[0].x - points[1].x,
|
|
7433
|
+
y: 2 * points[0].y - points[1].y
|
|
7434
|
+
};
|
|
7435
|
+
const p1 = points[i];
|
|
7436
|
+
const p2 = points[i + 1];
|
|
7437
|
+
const p3 = i + 2 < n ? points[i + 2] : {
|
|
7438
|
+
x: 2 * points[n - 1].x - points[n - 2].x,
|
|
7439
|
+
y: 2 * points[n - 1].y - points[n - 2].y
|
|
7440
|
+
};
|
|
7441
|
+
const cp1x = p1.x + (p2.x - p0.x) / 6;
|
|
7442
|
+
const cp1y = p1.y + (p2.y - p0.y) / 6;
|
|
7443
|
+
const cp2x = p2.x - (p3.x - p1.x) / 6;
|
|
7444
|
+
const cp2y = p2.y - (p3.y - p1.y) / 6;
|
|
7445
|
+
commands2.push(
|
|
7446
|
+
`C ${formatCoordinate(cp1x)} ${formatCoordinate(cp1y)}, ${formatCoordinate(cp2x)} ${formatCoordinate(cp2y)}, ${formatCoordinate(p2.x)} ${formatCoordinate(p2.y)}`
|
|
7447
|
+
);
|
|
7448
|
+
}
|
|
7449
|
+
return commands2.join(" ");
|
|
7450
|
+
}
|
|
7451
|
+
const commands = points.map(
|
|
7452
|
+
(p, i) => i === 0 ? `M ${formatCoordinate(p.x)} ${formatCoordinate(p.y)}` : `L ${formatCoordinate(p.x)} ${formatCoordinate(p.y)}`
|
|
7453
|
+
);
|
|
7454
|
+
return commands.join(" ");
|
|
7455
|
+
}
|
|
7456
|
+
function generateAreaPath(points, baselineY, curve = "linear") {
|
|
7457
|
+
if (points.length === 0) return "";
|
|
7458
|
+
if (points.length === 1) {
|
|
7459
|
+
const p = points[0];
|
|
7460
|
+
return `M ${formatCoordinate(p.x)} ${formatCoordinate(p.y)} L ${formatCoordinate(p.x)} ${formatCoordinate(baselineY)} Z`;
|
|
7461
|
+
}
|
|
7462
|
+
const linePath = generateLinePath(points, curve);
|
|
7463
|
+
const first = points[0];
|
|
7464
|
+
const last = points[points.length - 1];
|
|
7465
|
+
return `${linePath} L ${formatCoordinate(last.x)} ${formatCoordinate(baselineY)} L ${formatCoordinate(first.x)} ${formatCoordinate(baselineY)} Z`;
|
|
7466
|
+
}
|
|
7467
|
+
function calculateBarLayout(options) {
|
|
7468
|
+
const {
|
|
7469
|
+
dataCount,
|
|
7470
|
+
seriesCount,
|
|
7471
|
+
plotWidth,
|
|
7472
|
+
plotHeight,
|
|
7473
|
+
yDomain,
|
|
7474
|
+
data,
|
|
7475
|
+
mode = "grouped",
|
|
7476
|
+
layout = "vertical",
|
|
7477
|
+
barMaxWidth,
|
|
7478
|
+
groupPadding = 0.2,
|
|
7479
|
+
barPadding = 2
|
|
7480
|
+
} = options;
|
|
7481
|
+
if (data.length === 0 || dataCount <= 0 || seriesCount <= 0) {
|
|
7482
|
+
return [];
|
|
7483
|
+
}
|
|
7484
|
+
const domainSpan = yDomain[1] - yDomain[0];
|
|
7485
|
+
const safeDomainSpan = domainSpan !== 0 ? domainSpan : 1;
|
|
7486
|
+
const isVertical = layout === "vertical";
|
|
7487
|
+
const slotSize = isVertical ? plotWidth / dataCount : plotHeight / dataCount;
|
|
7488
|
+
if (isVertical) {
|
|
7489
|
+
if (mode === "grouped") {
|
|
7490
|
+
const groupWidth = slotSize * (1 - groupPadding);
|
|
7491
|
+
const totalGaps = Math.max(0, seriesCount - 1) * barPadding;
|
|
7492
|
+
let rawBarWidth2 = (groupWidth - totalGaps) / seriesCount;
|
|
7493
|
+
if (rawBarWidth2 < 1) rawBarWidth2 = Math.max(1, groupWidth / seriesCount);
|
|
7494
|
+
const barWidth2 = barMaxWidth ? Math.min(rawBarWidth2, barMaxWidth) : rawBarWidth2;
|
|
7495
|
+
const actualGroupBarsWidth = seriesCount * barWidth2 + (seriesCount - 1) * barPadding;
|
|
7496
|
+
return data.map((item) => {
|
|
7497
|
+
const groupStart = item.group * slotSize + (slotSize - actualGroupBarsWidth) / 2;
|
|
7498
|
+
const x = groupStart + item.seriesIndex * (barWidth2 + barPadding);
|
|
7499
|
+
const height = Math.abs(item.value) / safeDomainSpan * plotHeight;
|
|
7500
|
+
const y = plotHeight - height;
|
|
7501
|
+
return {
|
|
7502
|
+
...item,
|
|
7503
|
+
x: Number(parseFloat(x.toPrecision(10))),
|
|
7504
|
+
y: Number(parseFloat(y.toPrecision(10))),
|
|
7505
|
+
width: Number(parseFloat(barWidth2.toPrecision(10))),
|
|
7506
|
+
height: Number(parseFloat(height.toPrecision(10)))
|
|
7507
|
+
};
|
|
7508
|
+
});
|
|
7509
|
+
}
|
|
7510
|
+
const rawBarWidth = slotSize * (1 - groupPadding);
|
|
7511
|
+
const barWidth = barMaxWidth ? Math.min(rawBarWidth, barMaxWidth) : rawBarWidth;
|
|
7512
|
+
const currentStackTops = /* @__PURE__ */ new Map();
|
|
7513
|
+
return data.map((item) => {
|
|
7514
|
+
const groupCenter = item.group * slotSize + slotSize / 2;
|
|
7515
|
+
const x = groupCenter - barWidth / 2;
|
|
7516
|
+
const height = Math.abs(item.value) / safeDomainSpan * plotHeight;
|
|
7517
|
+
const currentY = currentStackTops.get(item.group) ?? plotHeight;
|
|
7518
|
+
const y = currentY - height;
|
|
7519
|
+
currentStackTops.set(item.group, y);
|
|
7520
|
+
return {
|
|
7521
|
+
...item,
|
|
7522
|
+
x: Number(parseFloat(x.toPrecision(10))),
|
|
7523
|
+
y: Number(parseFloat(y.toPrecision(10))),
|
|
7524
|
+
width: Number(parseFloat(barWidth.toPrecision(10))),
|
|
7525
|
+
height: Number(parseFloat(height.toPrecision(10)))
|
|
7526
|
+
};
|
|
7527
|
+
});
|
|
7528
|
+
}
|
|
7529
|
+
if (mode === "grouped") {
|
|
7530
|
+
const groupHeight = slotSize * (1 - groupPadding);
|
|
7531
|
+
const totalGaps = Math.max(0, seriesCount - 1) * barPadding;
|
|
7532
|
+
let rawBarHeight2 = (groupHeight - totalGaps) / seriesCount;
|
|
7533
|
+
if (rawBarHeight2 < 1) rawBarHeight2 = Math.max(1, groupHeight / seriesCount);
|
|
7534
|
+
const barHeight2 = barMaxWidth ? Math.min(rawBarHeight2, barMaxWidth) : rawBarHeight2;
|
|
7535
|
+
const actualGroupBarsHeight = seriesCount * barHeight2 + (seriesCount - 1) * barPadding;
|
|
7536
|
+
return data.map((item) => {
|
|
7537
|
+
const groupStart = item.group * slotSize + (slotSize - actualGroupBarsHeight) / 2;
|
|
7538
|
+
const y = groupStart + item.seriesIndex * (barHeight2 + barPadding);
|
|
7539
|
+
const width = Math.abs(item.value) / safeDomainSpan * plotWidth;
|
|
7540
|
+
const x = 0;
|
|
7541
|
+
return {
|
|
7542
|
+
...item,
|
|
7543
|
+
x: Number(parseFloat(x.toPrecision(10))),
|
|
7544
|
+
y: Number(parseFloat(y.toPrecision(10))),
|
|
7545
|
+
width: Number(parseFloat(width.toPrecision(10))),
|
|
7546
|
+
height: Number(parseFloat(barHeight2.toPrecision(10)))
|
|
7547
|
+
};
|
|
7548
|
+
});
|
|
7549
|
+
}
|
|
7550
|
+
const rawBarHeight = slotSize * (1 - groupPadding);
|
|
7551
|
+
const barHeight = barMaxWidth ? Math.min(rawBarHeight, barMaxWidth) : rawBarHeight;
|
|
7552
|
+
const currentStackRights = /* @__PURE__ */ new Map();
|
|
7553
|
+
return data.map((item) => {
|
|
7554
|
+
const groupCenter = item.group * slotSize + slotSize / 2;
|
|
7555
|
+
const y = groupCenter - barHeight / 2;
|
|
7556
|
+
const width = Math.abs(item.value) / safeDomainSpan * plotWidth;
|
|
7557
|
+
const currentX = currentStackRights.get(item.group) ?? 0;
|
|
7558
|
+
const x = currentX;
|
|
7559
|
+
currentStackRights.set(item.group, currentX + width);
|
|
7560
|
+
return {
|
|
7561
|
+
...item,
|
|
7562
|
+
x: Number(parseFloat(x.toPrecision(10))),
|
|
7563
|
+
y: Number(parseFloat(y.toPrecision(10))),
|
|
7564
|
+
width: Number(parseFloat(width.toPrecision(10))),
|
|
7565
|
+
height: Number(parseFloat(barHeight.toPrecision(10)))
|
|
7566
|
+
};
|
|
7567
|
+
});
|
|
7568
|
+
}
|
|
7569
|
+
function calculateDonutSegments(slices, options) {
|
|
7570
|
+
const { cx, cy, innerRadius, outerRadius, startAngle = -90 } = options;
|
|
7571
|
+
if (slices.length === 0) {
|
|
7572
|
+
return [];
|
|
7573
|
+
}
|
|
7574
|
+
const total = slices.reduce((sum, s) => sum + (s.value > 0 ? s.value : 0), 0);
|
|
7575
|
+
if (total === 0) {
|
|
7576
|
+
return slices.map((slice) => ({
|
|
7577
|
+
...slice,
|
|
7578
|
+
percentage: 0,
|
|
7579
|
+
path: "",
|
|
7580
|
+
startAngle: 0,
|
|
7581
|
+
endAngle: 0,
|
|
7582
|
+
middleAngle: 0
|
|
7583
|
+
}));
|
|
7584
|
+
}
|
|
7585
|
+
let currentAngleRad = startAngle * Math.PI / 180;
|
|
7586
|
+
const segments = [];
|
|
7587
|
+
for (const slice of slices) {
|
|
7588
|
+
if (slice.value <= 0) {
|
|
7589
|
+
segments.push({
|
|
7590
|
+
...slice,
|
|
7591
|
+
percentage: 0,
|
|
7592
|
+
path: "",
|
|
7593
|
+
startAngle: Number(parseFloat((currentAngleRad * (180 / Math.PI)).toPrecision(8))),
|
|
7594
|
+
endAngle: Number(parseFloat((currentAngleRad * (180 / Math.PI)).toPrecision(8))),
|
|
7595
|
+
middleAngle: Number(parseFloat((currentAngleRad * (180 / Math.PI)).toPrecision(8)))
|
|
7596
|
+
});
|
|
7597
|
+
continue;
|
|
7598
|
+
}
|
|
7599
|
+
const ratio = slice.value / total;
|
|
7600
|
+
const percentage = Math.round(ratio * 1e4) / 100;
|
|
7601
|
+
const angleSpanRad = ratio * 2 * Math.PI;
|
|
7602
|
+
const startA = currentAngleRad;
|
|
7603
|
+
const endA = currentAngleRad + angleSpanRad;
|
|
7604
|
+
const middleA = currentAngleRad + angleSpanRad / 2;
|
|
7605
|
+
currentAngleRad = endA;
|
|
7606
|
+
const startDeg = Number(parseFloat((startA * (180 / Math.PI)).toPrecision(8)));
|
|
7607
|
+
const endDeg = Number(parseFloat((endA * (180 / Math.PI)).toPrecision(8)));
|
|
7608
|
+
const middleDeg = Number(parseFloat((middleA * (180 / Math.PI)).toPrecision(8)));
|
|
7609
|
+
if (ratio >= 0.99999) {
|
|
7610
|
+
let fullPath = "";
|
|
7611
|
+
if (innerRadius > 0) {
|
|
7612
|
+
fullPath = `M ${formatCoordinate(cx)} ${formatCoordinate(cy - outerRadius)} A ${formatCoordinate(outerRadius)} ${formatCoordinate(outerRadius)} 0 1 1 ${formatCoordinate(cx)} ${formatCoordinate(cy + outerRadius)} A ${formatCoordinate(outerRadius)} ${formatCoordinate(outerRadius)} 0 1 1 ${formatCoordinate(cx)} ${formatCoordinate(cy - outerRadius)} L ${formatCoordinate(cx)} ${formatCoordinate(cy - innerRadius)} A ${formatCoordinate(innerRadius)} ${formatCoordinate(innerRadius)} 0 1 0 ${formatCoordinate(cx)} ${formatCoordinate(cy + innerRadius)} A ${formatCoordinate(innerRadius)} ${formatCoordinate(innerRadius)} 0 1 0 ${formatCoordinate(cx)} ${formatCoordinate(cy - innerRadius)} Z`;
|
|
7613
|
+
} else {
|
|
7614
|
+
fullPath = `M ${formatCoordinate(cx)} ${formatCoordinate(cy - outerRadius)} A ${formatCoordinate(outerRadius)} ${formatCoordinate(outerRadius)} 0 1 1 ${formatCoordinate(cx)} ${formatCoordinate(cy + outerRadius)} A ${formatCoordinate(outerRadius)} ${formatCoordinate(outerRadius)} 0 1 1 ${formatCoordinate(cx)} ${formatCoordinate(cy - outerRadius)} Z`;
|
|
7615
|
+
}
|
|
7616
|
+
segments.push({
|
|
7617
|
+
...slice,
|
|
7618
|
+
percentage,
|
|
7619
|
+
path: fullPath,
|
|
7620
|
+
startAngle: startDeg,
|
|
7621
|
+
endAngle: endDeg,
|
|
7622
|
+
middleAngle: middleDeg
|
|
7623
|
+
});
|
|
7624
|
+
continue;
|
|
7625
|
+
}
|
|
7626
|
+
const x1 = cx + outerRadius * Math.cos(startA);
|
|
7627
|
+
const y1 = cy + outerRadius * Math.sin(startA);
|
|
7628
|
+
const x2 = cx + outerRadius * Math.cos(endA);
|
|
7629
|
+
const y2 = cy + outerRadius * Math.sin(endA);
|
|
7630
|
+
const largeArcFlag = angleSpanRad > Math.PI ? 1 : 0;
|
|
7631
|
+
let path = "";
|
|
7632
|
+
if (innerRadius > 0) {
|
|
7633
|
+
const x3 = cx + innerRadius * Math.cos(endA);
|
|
7634
|
+
const y3 = cy + innerRadius * Math.sin(endA);
|
|
7635
|
+
const x4 = cx + innerRadius * Math.cos(startA);
|
|
7636
|
+
const y4 = cy + innerRadius * Math.sin(startA);
|
|
7637
|
+
path = `M ${formatCoordinate(x1)} ${formatCoordinate(y1)} A ${formatCoordinate(outerRadius)} ${formatCoordinate(outerRadius)} 0 ${largeArcFlag} 1 ${formatCoordinate(x2)} ${formatCoordinate(y2)} L ${formatCoordinate(x3)} ${formatCoordinate(y3)} A ${formatCoordinate(innerRadius)} ${formatCoordinate(innerRadius)} 0 ${largeArcFlag} 0 ${formatCoordinate(x4)} ${formatCoordinate(y4)} Z`;
|
|
7638
|
+
} else {
|
|
7639
|
+
path = `M ${formatCoordinate(x1)} ${formatCoordinate(y1)} A ${formatCoordinate(outerRadius)} ${formatCoordinate(outerRadius)} 0 ${largeArcFlag} 1 ${formatCoordinate(x2)} ${formatCoordinate(y2)} L ${formatCoordinate(cx)} ${formatCoordinate(cy)} Z`;
|
|
7640
|
+
}
|
|
7641
|
+
segments.push({
|
|
7642
|
+
...slice,
|
|
7643
|
+
percentage,
|
|
7644
|
+
path,
|
|
7645
|
+
startAngle: startDeg,
|
|
7646
|
+
endAngle: endDeg,
|
|
7647
|
+
middleAngle: middleDeg
|
|
7648
|
+
});
|
|
7649
|
+
}
|
|
7650
|
+
return segments;
|
|
7651
|
+
}
|
|
7652
|
+
var CHART_COLOR_VARS = {
|
|
7653
|
+
chart1: "var(--kj-chart1, #a84f08)",
|
|
7654
|
+
chart2: "var(--kj-chart2, #0f746d)",
|
|
7655
|
+
chart3: "var(--kj-chart3, #0284c7)",
|
|
7656
|
+
chart4: "var(--kj-chart4, #7c3aed)",
|
|
7657
|
+
chart5: "var(--kj-chart5, #e11d48)",
|
|
7658
|
+
chart6: "var(--kj-chart6, #65a30d)"
|
|
7659
|
+
};
|
|
7660
|
+
var DEFAULT_CHART_COLORS = [
|
|
7661
|
+
"chart1",
|
|
7662
|
+
"chart2",
|
|
7663
|
+
"chart3",
|
|
7664
|
+
"chart4",
|
|
7665
|
+
"chart5",
|
|
7666
|
+
"chart6"
|
|
7667
|
+
];
|
|
7668
|
+
function getChartColor(color, fallbackIndex = 0) {
|
|
7669
|
+
if (!color) {
|
|
7670
|
+
const defaultKey = DEFAULT_CHART_COLORS[fallbackIndex % DEFAULT_CHART_COLORS.length];
|
|
7671
|
+
return CHART_COLOR_VARS[defaultKey] || defaultKey;
|
|
7672
|
+
}
|
|
7673
|
+
return CHART_COLOR_VARS[color] ?? color;
|
|
7674
|
+
}
|
|
7675
|
+
var SVG_TAGS = /* @__PURE__ */ new Set([
|
|
7676
|
+
"path",
|
|
7677
|
+
"rect",
|
|
7678
|
+
"circle",
|
|
7679
|
+
"ellipse",
|
|
7680
|
+
"line",
|
|
7681
|
+
"polyline",
|
|
7682
|
+
"polygon",
|
|
7683
|
+
"text",
|
|
7684
|
+
"tspan",
|
|
7685
|
+
"g",
|
|
7686
|
+
"defs",
|
|
7687
|
+
"linearGradient",
|
|
7688
|
+
"radialGradient",
|
|
7689
|
+
"stop",
|
|
7690
|
+
"pattern",
|
|
7691
|
+
"clipPath",
|
|
7692
|
+
"mask",
|
|
7693
|
+
"image",
|
|
7694
|
+
"use",
|
|
7695
|
+
"symbol",
|
|
7696
|
+
"animate",
|
|
7697
|
+
"animateTransform",
|
|
7698
|
+
"foreignObject"
|
|
7699
|
+
]);
|
|
7700
|
+
function isSvgChild(child) {
|
|
7701
|
+
if (!React52__namespace.isValidElement(child)) return false;
|
|
7702
|
+
if (typeof child.type === "string") {
|
|
7703
|
+
return SVG_TAGS.has(child.type);
|
|
7704
|
+
}
|
|
7705
|
+
const comp = child.type;
|
|
7706
|
+
if (comp.isSvg) return true;
|
|
7707
|
+
const name = comp.displayName || comp.name || "";
|
|
7708
|
+
if (name.includes("Grid") || name.includes("Axis")) {
|
|
7709
|
+
return true;
|
|
7710
|
+
}
|
|
7711
|
+
return false;
|
|
7712
|
+
}
|
|
7713
|
+
function isOverlayChild(child) {
|
|
7714
|
+
if (!React52__namespace.isValidElement(child)) return false;
|
|
7715
|
+
const comp = child.type;
|
|
7716
|
+
const name = comp?.displayName || comp?.name || "";
|
|
7717
|
+
if (name.includes("Tooltip")) return true;
|
|
7718
|
+
const className = child.props?.className;
|
|
7719
|
+
if (typeof className === "string") {
|
|
7720
|
+
if (className.includes("chart-tooltip") || className.includes("chart-donut-center") || className.includes("chart-empty-message") || className.includes("absolute")) {
|
|
7721
|
+
return true;
|
|
7722
|
+
}
|
|
7723
|
+
}
|
|
7724
|
+
return false;
|
|
7725
|
+
}
|
|
7726
|
+
function partitionChildren(children) {
|
|
7727
|
+
const svgChildren = [];
|
|
7728
|
+
const overlayChildren = [];
|
|
7729
|
+
const flowChildren = [];
|
|
7730
|
+
const categorize = (child) => {
|
|
7731
|
+
if (child === null || child === void 0 || typeof child === "boolean") {
|
|
7732
|
+
return;
|
|
7733
|
+
}
|
|
7734
|
+
if (React52__namespace.isValidElement(child) && child.type === React52__namespace.Fragment) {
|
|
7735
|
+
const inner = partitionChildren(child.props.children);
|
|
7736
|
+
svgChildren.push(...inner.svgChildren);
|
|
7737
|
+
overlayChildren.push(...inner.overlayChildren);
|
|
7738
|
+
flowChildren.push(...inner.flowChildren);
|
|
7739
|
+
return;
|
|
7740
|
+
}
|
|
7741
|
+
if (isSvgChild(child)) {
|
|
7742
|
+
svgChildren.push(child);
|
|
7743
|
+
return;
|
|
7744
|
+
}
|
|
7745
|
+
if (isOverlayChild(child)) {
|
|
7746
|
+
overlayChildren.push(child);
|
|
7747
|
+
return;
|
|
7748
|
+
}
|
|
7749
|
+
flowChildren.push(child);
|
|
7750
|
+
};
|
|
7751
|
+
React52__namespace.Children.forEach(children, categorize);
|
|
7752
|
+
return { svgChildren, overlayChildren, flowChildren };
|
|
7753
|
+
}
|
|
7754
|
+
function useChartWidth(containerRef, defaultWidth = 600) {
|
|
7755
|
+
const [width, setWidth] = React52__namespace.useState(defaultWidth);
|
|
7756
|
+
React52__namespace.useEffect(() => {
|
|
7757
|
+
const el = containerRef.current;
|
|
7758
|
+
if (!el) return;
|
|
7759
|
+
const measure = () => {
|
|
7760
|
+
const rect = el.getBoundingClientRect();
|
|
7761
|
+
if (rect.width > 0) {
|
|
7762
|
+
setWidth(Math.round(rect.width));
|
|
7763
|
+
}
|
|
7764
|
+
};
|
|
7765
|
+
measure();
|
|
7766
|
+
if (typeof ResizeObserver === "undefined") {
|
|
7767
|
+
return;
|
|
7768
|
+
}
|
|
7769
|
+
const observer = new ResizeObserver((entries) => {
|
|
7770
|
+
for (const entry of entries) {
|
|
7771
|
+
const w = entry.contentRect.width;
|
|
7772
|
+
if (w > 0) {
|
|
7773
|
+
setWidth(Math.round(w));
|
|
7774
|
+
}
|
|
7775
|
+
}
|
|
7776
|
+
});
|
|
7777
|
+
observer.observe(el);
|
|
7778
|
+
return () => {
|
|
7779
|
+
observer.disconnect();
|
|
7780
|
+
};
|
|
7781
|
+
}, [containerRef]);
|
|
7782
|
+
return width;
|
|
7783
|
+
}
|
|
7784
|
+
var ChartContainer = React52__namespace.forwardRef(
|
|
7785
|
+
({
|
|
7786
|
+
width,
|
|
7787
|
+
height,
|
|
7788
|
+
viewBox,
|
|
7789
|
+
"aria-label": ariaLabelProp,
|
|
7790
|
+
ariaLabel,
|
|
7791
|
+
overlay,
|
|
7792
|
+
svgClassName,
|
|
7793
|
+
svgRef,
|
|
7794
|
+
plotWrapperClassName,
|
|
7795
|
+
plotWrapperStyle,
|
|
7796
|
+
children,
|
|
7797
|
+
className,
|
|
7798
|
+
style,
|
|
7799
|
+
...props
|
|
7800
|
+
}, ref) => {
|
|
7801
|
+
const label = ariaLabelProp || ariaLabel;
|
|
7802
|
+
const { svgChildren, overlayChildren, flowChildren } = partitionChildren(children);
|
|
7803
|
+
const heightNum = typeof height === "number" ? height : void 0;
|
|
7804
|
+
const computedViewBox = viewBox || (width !== void 0 && heightNum !== void 0 ? `0 0 ${width} ${heightNum}` : void 0);
|
|
7805
|
+
const plotHeightStyle = typeof height === "number" ? `${height}px` : height ?? void 0;
|
|
7806
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7807
|
+
"div",
|
|
7808
|
+
{
|
|
7809
|
+
ref,
|
|
7810
|
+
className: cn("chart-container relative w-full flex flex-col", className),
|
|
7811
|
+
style,
|
|
7812
|
+
...props,
|
|
7813
|
+
children: [
|
|
7814
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7815
|
+
"div",
|
|
7816
|
+
{
|
|
7817
|
+
className: cn("chart-plot-wrapper relative w-full", plotWrapperClassName),
|
|
7818
|
+
style: {
|
|
7819
|
+
height: plotHeightStyle,
|
|
7820
|
+
width: typeof width === "number" ? `${width}px` : void 0,
|
|
7821
|
+
...plotWrapperStyle
|
|
7822
|
+
},
|
|
7823
|
+
children: [
|
|
7824
|
+
svgChildren.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7825
|
+
"svg",
|
|
7826
|
+
{
|
|
7827
|
+
ref: svgRef,
|
|
7828
|
+
role: "img",
|
|
7829
|
+
"aria-label": label,
|
|
7830
|
+
viewBox: computedViewBox,
|
|
7831
|
+
width,
|
|
7832
|
+
height: heightNum,
|
|
7833
|
+
className: cn(
|
|
7834
|
+
"chart-svg block w-full overflow-visible",
|
|
7835
|
+
height !== void 0 ? "h-full" : "h-auto",
|
|
7836
|
+
svgClassName
|
|
7837
|
+
),
|
|
7838
|
+
children: svgChildren
|
|
7839
|
+
}
|
|
7840
|
+
),
|
|
7841
|
+
overlayChildren,
|
|
7842
|
+
overlay
|
|
7843
|
+
]
|
|
7844
|
+
}
|
|
7845
|
+
),
|
|
7846
|
+
flowChildren
|
|
7847
|
+
]
|
|
7848
|
+
}
|
|
7849
|
+
);
|
|
7850
|
+
}
|
|
7851
|
+
);
|
|
7852
|
+
ChartContainer.displayName = "ChartContainer";
|
|
7853
|
+
var ChartGrid = ({
|
|
7854
|
+
width = 0,
|
|
7855
|
+
height = 0,
|
|
7856
|
+
yTicks,
|
|
7857
|
+
xTicks,
|
|
7858
|
+
scaleY,
|
|
7859
|
+
scaleX,
|
|
7860
|
+
yDomain,
|
|
7861
|
+
yRange,
|
|
7862
|
+
xDomain,
|
|
7863
|
+
xRange,
|
|
7864
|
+
stroke = "var(--kj-border-subtle, #e5e7eb)",
|
|
7865
|
+
strokeDasharray = "3 3",
|
|
7866
|
+
strokeWidth = 1,
|
|
7867
|
+
strokeOpacity = 1,
|
|
7868
|
+
horizontal = true,
|
|
7869
|
+
vertical,
|
|
7870
|
+
className,
|
|
7871
|
+
...props
|
|
7872
|
+
}) => {
|
|
7873
|
+
const shouldDrawHorizontal = horizontal && Boolean(yTicks && yTicks.length > 0);
|
|
7874
|
+
const shouldDrawVertical = (vertical ?? Boolean(xTicks && xTicks.length > 0)) && Boolean(xTicks && xTicks.length > 0);
|
|
7875
|
+
const getY = scaleY ?? (yDomain && yRange ? (val) => linearScale(val, yDomain, yRange) : (val) => val);
|
|
7876
|
+
const getX = scaleX ?? (xDomain && xRange ? (val) => linearScale(val, xDomain, xRange) : (val) => val);
|
|
7877
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("g", { className: cn("chart-grid pointer-events-none", className), ...props, children: [
|
|
7878
|
+
shouldDrawHorizontal && yTicks?.map((tick, idx) => {
|
|
7879
|
+
const yPos = getY(tick);
|
|
7880
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7881
|
+
"line",
|
|
7882
|
+
{
|
|
7883
|
+
x1: 0,
|
|
7884
|
+
y1: yPos,
|
|
7885
|
+
x2: width,
|
|
7886
|
+
y2: yPos,
|
|
7887
|
+
stroke,
|
|
7888
|
+
strokeDasharray,
|
|
7889
|
+
strokeWidth,
|
|
7890
|
+
strokeOpacity,
|
|
7891
|
+
className: "chart-grid-line chart-grid-line-y"
|
|
7892
|
+
},
|
|
7893
|
+
`grid-y-${idx}-${tick}`
|
|
7894
|
+
);
|
|
7895
|
+
}),
|
|
7896
|
+
shouldDrawVertical && xTicks?.map((tick, idx) => {
|
|
7897
|
+
const xPos = getX(tick);
|
|
7898
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7899
|
+
"line",
|
|
7900
|
+
{
|
|
7901
|
+
x1: xPos,
|
|
7902
|
+
y1: 0,
|
|
7903
|
+
x2: xPos,
|
|
7904
|
+
y2: height,
|
|
7905
|
+
stroke,
|
|
7906
|
+
strokeDasharray,
|
|
7907
|
+
strokeWidth,
|
|
7908
|
+
strokeOpacity,
|
|
7909
|
+
className: "chart-grid-line chart-grid-line-x"
|
|
7910
|
+
},
|
|
7911
|
+
`grid-x-${idx}-${tick}`
|
|
7912
|
+
);
|
|
7913
|
+
})
|
|
7914
|
+
] });
|
|
7915
|
+
};
|
|
7916
|
+
ChartGrid.displayName = "ChartGrid";
|
|
7917
|
+
var ChartXAxis = ({
|
|
7918
|
+
ticks,
|
|
7919
|
+
y,
|
|
7920
|
+
formatter,
|
|
7921
|
+
valueFormatter,
|
|
7922
|
+
textAnchor = "middle",
|
|
7923
|
+
dy = "1.2em",
|
|
7924
|
+
className,
|
|
7925
|
+
tickClassName,
|
|
7926
|
+
...props
|
|
7927
|
+
}) => {
|
|
7928
|
+
const format = formatter ?? valueFormatter ?? ((v) => String(v));
|
|
7929
|
+
return /* @__PURE__ */ jsxRuntime.jsx("g", { className: cn("chart-x-axis", className), ...props, children: ticks.map((tick, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
7930
|
+
"text",
|
|
7931
|
+
{
|
|
7932
|
+
x: tick.x,
|
|
7933
|
+
y,
|
|
7934
|
+
dy,
|
|
7935
|
+
textAnchor,
|
|
7936
|
+
fill: "currentColor",
|
|
7937
|
+
className: cn("chart-x-tick text-xs fill-muted-foreground select-none", tickClassName),
|
|
7938
|
+
children: format(tick.value)
|
|
7939
|
+
},
|
|
7940
|
+
`x-tick-${idx}-${tick.value}`
|
|
7941
|
+
)) });
|
|
7942
|
+
};
|
|
7943
|
+
ChartXAxis.displayName = "ChartXAxis";
|
|
7944
|
+
var ChartYAxis = ({
|
|
7945
|
+
ticks,
|
|
7946
|
+
x,
|
|
7947
|
+
formatter,
|
|
7948
|
+
valueFormatter,
|
|
7949
|
+
textAnchor = "end",
|
|
7950
|
+
dx = "-0.5em",
|
|
7951
|
+
dominantBaseline = "middle",
|
|
7952
|
+
className,
|
|
7953
|
+
tickClassName,
|
|
7954
|
+
...props
|
|
7955
|
+
}) => {
|
|
7956
|
+
const format = formatter ?? valueFormatter ?? ((v) => String(v));
|
|
7957
|
+
return /* @__PURE__ */ jsxRuntime.jsx("g", { className: cn("chart-y-axis", className), ...props, children: ticks.map((tick, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
7958
|
+
"text",
|
|
7959
|
+
{
|
|
7960
|
+
x,
|
|
7961
|
+
y: tick.y,
|
|
7962
|
+
dx,
|
|
7963
|
+
textAnchor,
|
|
7964
|
+
dominantBaseline,
|
|
7965
|
+
fill: "currentColor",
|
|
7966
|
+
className: cn("chart-y-tick text-xs fill-muted-foreground select-none", tickClassName),
|
|
7967
|
+
children: format(tick.value)
|
|
7968
|
+
},
|
|
7969
|
+
`y-tick-${idx}-${tick.value}`
|
|
7970
|
+
)) });
|
|
7971
|
+
};
|
|
7972
|
+
ChartYAxis.displayName = "ChartYAxis";
|
|
7973
|
+
var ChartTooltip = React52__namespace.forwardRef(
|
|
7974
|
+
({
|
|
7975
|
+
title,
|
|
7976
|
+
items,
|
|
7977
|
+
x,
|
|
7978
|
+
y,
|
|
7979
|
+
open = true,
|
|
7980
|
+
crosshairX,
|
|
7981
|
+
crosshairHeight,
|
|
7982
|
+
showCrosshair = false,
|
|
7983
|
+
valueFormatter,
|
|
7984
|
+
content,
|
|
7985
|
+
className,
|
|
7986
|
+
style,
|
|
7987
|
+
...props
|
|
7988
|
+
}, ref) => {
|
|
7989
|
+
if (!open) {
|
|
7990
|
+
return null;
|
|
7991
|
+
}
|
|
7992
|
+
const hasCoordinates = x !== void 0 && y !== void 0;
|
|
7993
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7994
|
+
showCrosshair && crosshairX !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7995
|
+
"div",
|
|
7996
|
+
{
|
|
7997
|
+
className: "chart-crosshair pointer-events-none absolute top-0 w-px border-l border-dashed border-border/80",
|
|
7998
|
+
style: {
|
|
7999
|
+
left: `${crosshairX}px`,
|
|
8000
|
+
height: crosshairHeight !== void 0 ? `${crosshairHeight}px` : "100%"
|
|
8001
|
+
},
|
|
8002
|
+
"aria-hidden": "true"
|
|
8003
|
+
}
|
|
8004
|
+
),
|
|
8005
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8006
|
+
"div",
|
|
8007
|
+
{
|
|
8008
|
+
ref,
|
|
8009
|
+
className: cn(
|
|
8010
|
+
"chart-tooltip pointer-events-none absolute z-50 min-w-[120px] rounded-lg border border-border bg-popover/95 p-2.5 text-xs text-popover-foreground shadow-md backdrop-blur-sm transition-opacity duration-150",
|
|
8011
|
+
className
|
|
8012
|
+
),
|
|
8013
|
+
style: {
|
|
8014
|
+
left: x !== void 0 ? `${x}px` : void 0,
|
|
8015
|
+
top: y !== void 0 ? `${y}px` : void 0,
|
|
8016
|
+
transform: hasCoordinates ? "translate(-50%, -100%) translateY(-8px)" : void 0,
|
|
8017
|
+
...style
|
|
8018
|
+
},
|
|
8019
|
+
...props,
|
|
8020
|
+
children: content ? content : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
8021
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chart-tooltip-title font-medium text-foreground mb-1.5 pb-1 border-b border-border/50", children: title }),
|
|
8022
|
+
items && items.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chart-tooltip-items space-y-1", children: items.map((item, idx) => {
|
|
8023
|
+
const itemVal = item.formattedValue ?? (typeof item.value === "number" && valueFormatter ? valueFormatter(item.value) : item.value);
|
|
8024
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8025
|
+
"div",
|
|
8026
|
+
{
|
|
8027
|
+
className: "chart-tooltip-item flex items-center justify-between gap-3",
|
|
8028
|
+
children: [
|
|
8029
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
8030
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8031
|
+
"span",
|
|
8032
|
+
{
|
|
8033
|
+
className: "chart-tooltip-dot h-2 w-2 rounded-full shrink-0",
|
|
8034
|
+
style: { backgroundColor: getChartColor(item.color, idx) },
|
|
8035
|
+
"aria-hidden": "true"
|
|
8036
|
+
}
|
|
8037
|
+
),
|
|
8038
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-muted-foreground", children: item.label })
|
|
8039
|
+
] }),
|
|
8040
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground shrink-0", children: itemVal })
|
|
8041
|
+
]
|
|
8042
|
+
},
|
|
8043
|
+
item.key ?? idx
|
|
8044
|
+
);
|
|
8045
|
+
}) })
|
|
8046
|
+
] })
|
|
8047
|
+
}
|
|
8048
|
+
)
|
|
8049
|
+
] });
|
|
8050
|
+
}
|
|
8051
|
+
);
|
|
8052
|
+
ChartTooltip.displayName = "ChartTooltip";
|
|
8053
|
+
ChartTooltip.isHtml = true;
|
|
8054
|
+
var ChartLegend = ({
|
|
8055
|
+
series,
|
|
8056
|
+
activeSeries,
|
|
8057
|
+
disabledSeries,
|
|
8058
|
+
onToggle,
|
|
8059
|
+
orientation = "horizontal",
|
|
8060
|
+
className,
|
|
8061
|
+
itemClassName,
|
|
8062
|
+
...props
|
|
8063
|
+
}) => {
|
|
8064
|
+
const isInteractive = Boolean(onToggle);
|
|
8065
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8066
|
+
"div",
|
|
8067
|
+
{
|
|
8068
|
+
className: cn(
|
|
8069
|
+
"chart-legend flex flex-wrap items-center justify-center gap-4 py-2",
|
|
8070
|
+
orientation === "vertical" && "flex-col items-start gap-2",
|
|
8071
|
+
className
|
|
8072
|
+
),
|
|
8073
|
+
...props,
|
|
8074
|
+
children: series.map((item, idx) => {
|
|
8075
|
+
const isDisabled = disabledSeries?.includes(item.key) || activeSeries !== void 0 && !activeSeries.includes(item.key);
|
|
8076
|
+
const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
8077
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8078
|
+
"span",
|
|
8079
|
+
{
|
|
8080
|
+
className: "chart-legend-dot h-2.5 w-2.5 rounded-full shrink-0",
|
|
8081
|
+
style: { backgroundColor: getChartColor(item.color, idx) },
|
|
8082
|
+
"aria-hidden": "true"
|
|
8083
|
+
}
|
|
8084
|
+
),
|
|
8085
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "chart-legend-label", children: item.label })
|
|
8086
|
+
] });
|
|
8087
|
+
if (isInteractive) {
|
|
8088
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8089
|
+
"button",
|
|
8090
|
+
{
|
|
8091
|
+
type: "button",
|
|
8092
|
+
onClick: () => onToggle?.(item.key),
|
|
8093
|
+
onKeyDown: (e) => {
|
|
8094
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
8095
|
+
e.preventDefault();
|
|
8096
|
+
onToggle?.(item.key);
|
|
8097
|
+
}
|
|
8098
|
+
},
|
|
8099
|
+
"aria-pressed": !isDisabled,
|
|
8100
|
+
className: cn(
|
|
8101
|
+
"chart-legend-item inline-flex items-center gap-1.5 text-xs font-medium transition-opacity cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 rounded px-1 py-0.5",
|
|
8102
|
+
isDisabled ? "opacity-40 line-through text-muted-foreground" : "text-foreground hover:opacity-80",
|
|
8103
|
+
itemClassName
|
|
8104
|
+
),
|
|
8105
|
+
children: content
|
|
8106
|
+
},
|
|
8107
|
+
item.key
|
|
8108
|
+
);
|
|
8109
|
+
}
|
|
8110
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8111
|
+
"div",
|
|
8112
|
+
{
|
|
8113
|
+
className: cn(
|
|
8114
|
+
"chart-legend-item inline-flex items-center gap-1.5 text-xs font-medium",
|
|
8115
|
+
isDisabled ? "opacity-40 text-muted-foreground" : "text-foreground",
|
|
8116
|
+
itemClassName
|
|
8117
|
+
),
|
|
8118
|
+
children: content
|
|
8119
|
+
},
|
|
8120
|
+
item.key
|
|
8121
|
+
);
|
|
8122
|
+
})
|
|
8123
|
+
}
|
|
8124
|
+
);
|
|
8125
|
+
};
|
|
8126
|
+
ChartLegend.displayName = "ChartLegend";
|
|
8127
|
+
ChartLegend.isHtml = true;
|
|
8128
|
+
var ChartA11yTable = ({
|
|
8129
|
+
caption,
|
|
8130
|
+
categories,
|
|
8131
|
+
series,
|
|
8132
|
+
data,
|
|
8133
|
+
indexKey,
|
|
8134
|
+
index,
|
|
8135
|
+
valueFormatter,
|
|
8136
|
+
className
|
|
8137
|
+
}) => {
|
|
8138
|
+
const categoryKey = indexKey ?? index;
|
|
8139
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("table", { className: cn("sr-only", className), children: [
|
|
8140
|
+
caption && /* @__PURE__ */ jsxRuntime.jsx("caption", { children: caption }),
|
|
8141
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
8142
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: categoryKey || "Category" }),
|
|
8143
|
+
series.map((s) => /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: s.label }, s.key))
|
|
8144
|
+
] }) }),
|
|
8145
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: data.map((row, rowIdx) => {
|
|
8146
|
+
let categoryLabel = "";
|
|
8147
|
+
if (typeof row === "object" && row !== null && categoryKey && row[categoryKey] !== void 0) {
|
|
8148
|
+
categoryLabel = String(row[categoryKey]);
|
|
8149
|
+
} else if (categories && categories[rowIdx] !== void 0) {
|
|
8150
|
+
categoryLabel = String(categories[rowIdx]);
|
|
8151
|
+
} else {
|
|
8152
|
+
categoryLabel = `Item ${rowIdx + 1}`;
|
|
8153
|
+
}
|
|
8154
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
8155
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "row", children: categoryLabel }),
|
|
8156
|
+
series.map((s) => {
|
|
8157
|
+
let rawVal;
|
|
8158
|
+
if (typeof row === "number") {
|
|
8159
|
+
rawVal = row;
|
|
8160
|
+
} else if (typeof row === "object" && row !== null) {
|
|
8161
|
+
rawVal = row[s.key];
|
|
8162
|
+
}
|
|
8163
|
+
const displayVal = valueFormatter && typeof rawVal === "number" ? valueFormatter(rawVal) : rawVal !== void 0 && rawVal !== null ? String(rawVal) : "-";
|
|
8164
|
+
return /* @__PURE__ */ jsxRuntime.jsx("td", { children: displayVal }, s.key);
|
|
8165
|
+
})
|
|
8166
|
+
] }, `a11y-row-${rowIdx}`);
|
|
8167
|
+
}) })
|
|
8168
|
+
] });
|
|
8169
|
+
};
|
|
8170
|
+
ChartA11yTable.displayName = "ChartA11yTable";
|
|
8171
|
+
ChartA11yTable.isHtml = true;
|
|
8172
|
+
var Sparkline = React52__namespace.forwardRef(
|
|
8173
|
+
({
|
|
8174
|
+
data,
|
|
8175
|
+
dataKey,
|
|
8176
|
+
variant = "line",
|
|
8177
|
+
curve = "smooth",
|
|
8178
|
+
color = "chart1",
|
|
8179
|
+
strokeWidth = 2,
|
|
8180
|
+
showEndDot = false,
|
|
8181
|
+
showGradient = true,
|
|
8182
|
+
height = 36,
|
|
8183
|
+
width = "100%",
|
|
8184
|
+
barRadius = 2,
|
|
8185
|
+
barPadding = 2,
|
|
8186
|
+
ariaLabel,
|
|
8187
|
+
className,
|
|
8188
|
+
style,
|
|
8189
|
+
preserveAspectRatio = "none",
|
|
8190
|
+
...props
|
|
8191
|
+
}, ref) => {
|
|
8192
|
+
const rawId = React52__namespace.useId();
|
|
8193
|
+
const gradientId = `sparkline-grad-${rawId.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
8194
|
+
const values = React52__namespace.useMemo(() => {
|
|
8195
|
+
if (!data || !Array.isArray(data) || data.length === 0) return [];
|
|
8196
|
+
return data.map((item) => {
|
|
8197
|
+
if (typeof item === "number") return item;
|
|
8198
|
+
if (typeof item === "object" && item !== null) {
|
|
8199
|
+
if (dataKey && item[dataKey] !== void 0) {
|
|
8200
|
+
const num = Number(item[dataKey]);
|
|
8201
|
+
return Number.isFinite(num) ? num : 0;
|
|
8202
|
+
}
|
|
8203
|
+
if ("value" in item && item.value !== void 0) {
|
|
8204
|
+
const num = Number(item.value);
|
|
8205
|
+
return Number.isFinite(num) ? num : 0;
|
|
8206
|
+
}
|
|
8207
|
+
for (const key of Object.keys(item)) {
|
|
8208
|
+
const val = item[key];
|
|
8209
|
+
if (typeof val === "number") return val;
|
|
8210
|
+
const num = Number(val);
|
|
8211
|
+
if (Number.isFinite(num)) return num;
|
|
8212
|
+
}
|
|
8213
|
+
}
|
|
8214
|
+
const parsed = Number(item);
|
|
8215
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
8216
|
+
}).filter((v) => Number.isFinite(v));
|
|
8217
|
+
}, [data, dataKey]);
|
|
8218
|
+
const resolvedColor = getChartColor(color, 0);
|
|
8219
|
+
const computedAriaLabel = props["aria-label"] ?? ariaLabel ?? "Sparkline";
|
|
8220
|
+
const viewBoxWidth = typeof width === "number" && width > 0 ? width : 120;
|
|
8221
|
+
const isHidden = props["aria-hidden"] === true || props["aria-hidden"] === "true";
|
|
8222
|
+
if (values.length === 0) {
|
|
8223
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8224
|
+
"svg",
|
|
8225
|
+
{
|
|
8226
|
+
ref,
|
|
8227
|
+
role: isHidden ? void 0 : "img",
|
|
8228
|
+
"aria-label": isHidden ? void 0 : computedAriaLabel,
|
|
8229
|
+
viewBox: `0 0 ${viewBoxWidth} ${height}`,
|
|
8230
|
+
width,
|
|
8231
|
+
height,
|
|
8232
|
+
className: cn("chart-sparkline chart-sparkline-empty overflow-visible", className),
|
|
8233
|
+
style: {
|
|
8234
|
+
height: typeof height === "number" ? `${height}px` : height,
|
|
8235
|
+
width: typeof width === "number" ? `${width}px` : width,
|
|
8236
|
+
...style
|
|
8237
|
+
},
|
|
8238
|
+
preserveAspectRatio,
|
|
8239
|
+
...props,
|
|
8240
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8241
|
+
"line",
|
|
8242
|
+
{
|
|
8243
|
+
x1: 0,
|
|
8244
|
+
y1: height / 2,
|
|
8245
|
+
x2: viewBoxWidth,
|
|
8246
|
+
y2: height / 2,
|
|
8247
|
+
stroke: "currentColor",
|
|
8248
|
+
strokeDasharray: "2 2",
|
|
8249
|
+
strokeWidth: 1,
|
|
8250
|
+
className: "text-muted-foreground/30 stroke-current"
|
|
8251
|
+
}
|
|
8252
|
+
)
|
|
8253
|
+
}
|
|
8254
|
+
);
|
|
8255
|
+
}
|
|
8256
|
+
if (variant === "bar") {
|
|
8257
|
+
const minVal2 = Math.min(0, ...values);
|
|
8258
|
+
const maxVal2 = Math.max(0, ...values) || 1;
|
|
8259
|
+
const barData = values.map((val, idx) => ({
|
|
8260
|
+
group: idx,
|
|
8261
|
+
seriesIndex: 0,
|
|
8262
|
+
value: val
|
|
8263
|
+
}));
|
|
8264
|
+
const barRects = calculateBarLayout({
|
|
8265
|
+
dataCount: values.length,
|
|
8266
|
+
seriesCount: 1,
|
|
8267
|
+
plotWidth: viewBoxWidth,
|
|
8268
|
+
plotHeight: height,
|
|
8269
|
+
yDomain: [minVal2, maxVal2],
|
|
8270
|
+
data: barData,
|
|
8271
|
+
mode: "grouped",
|
|
8272
|
+
layout: "vertical",
|
|
8273
|
+
groupPadding: 0.2,
|
|
8274
|
+
barPadding
|
|
8275
|
+
});
|
|
8276
|
+
const lastBar = barRects.length > 0 ? barRects[barRects.length - 1] : void 0;
|
|
8277
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8278
|
+
"svg",
|
|
8279
|
+
{
|
|
8280
|
+
ref,
|
|
8281
|
+
role: isHidden ? void 0 : "img",
|
|
8282
|
+
"aria-label": isHidden ? void 0 : computedAriaLabel,
|
|
8283
|
+
viewBox: `0 0 ${viewBoxWidth} ${height}`,
|
|
8284
|
+
width,
|
|
8285
|
+
height,
|
|
8286
|
+
className: cn("chart-sparkline chart-sparkline-bars overflow-visible", className),
|
|
8287
|
+
style: {
|
|
8288
|
+
height: typeof height === "number" ? `${height}px` : height,
|
|
8289
|
+
width: typeof width === "number" ? `${width}px` : width,
|
|
8290
|
+
...style
|
|
8291
|
+
},
|
|
8292
|
+
preserveAspectRatio,
|
|
8293
|
+
...props,
|
|
8294
|
+
children: [
|
|
8295
|
+
barRects.map((bar, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
8296
|
+
"rect",
|
|
8297
|
+
{
|
|
8298
|
+
x: bar.x,
|
|
8299
|
+
y: bar.y,
|
|
8300
|
+
width: bar.width,
|
|
8301
|
+
height: Math.max(bar.height, bar.value === 0 ? 0 : 1),
|
|
8302
|
+
rx: barRadius,
|
|
8303
|
+
fill: resolvedColor,
|
|
8304
|
+
className: "chart-sparkline-bar"
|
|
8305
|
+
},
|
|
8306
|
+
`sparkline-bar-${idx}-${bar.x}`
|
|
8307
|
+
)),
|
|
8308
|
+
showEndDot && lastBar && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8309
|
+
"circle",
|
|
8310
|
+
{
|
|
8311
|
+
cx: lastBar.x + lastBar.width / 2,
|
|
8312
|
+
cy: lastBar.y,
|
|
8313
|
+
r: 3,
|
|
8314
|
+
fill: resolvedColor,
|
|
8315
|
+
className: "chart-sparkline-dot"
|
|
8316
|
+
}
|
|
8317
|
+
)
|
|
8318
|
+
]
|
|
8319
|
+
}
|
|
8320
|
+
);
|
|
8321
|
+
}
|
|
8322
|
+
const paddingTop = 4;
|
|
8323
|
+
const paddingBottom = 4;
|
|
8324
|
+
const paddingLeft = showEndDot ? 4 : 2;
|
|
8325
|
+
const paddingRight = showEndDot ? 4 : 2;
|
|
8326
|
+
const minVal = Math.min(...values);
|
|
8327
|
+
const maxVal = Math.max(...values);
|
|
8328
|
+
const points = values.map((val, idx) => {
|
|
8329
|
+
const x = values.length === 1 ? viewBoxWidth / 2 : linearScale(idx, [0, values.length - 1], [paddingLeft, viewBoxWidth - paddingRight]);
|
|
8330
|
+
const y = minVal === maxVal ? height / 2 : linearScale(val, [minVal, maxVal], [height - paddingBottom, paddingTop]);
|
|
8331
|
+
return { x, y };
|
|
8332
|
+
});
|
|
8333
|
+
const linePath = generateLinePath(points, curve);
|
|
8334
|
+
const areaPath = variant === "area" ? generateAreaPath(points, height, curve) : "";
|
|
8335
|
+
const lastPoint = points[points.length - 1];
|
|
8336
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8337
|
+
"svg",
|
|
8338
|
+
{
|
|
8339
|
+
ref,
|
|
8340
|
+
role: isHidden ? void 0 : "img",
|
|
8341
|
+
"aria-label": isHidden ? void 0 : computedAriaLabel,
|
|
8342
|
+
viewBox: `0 0 ${viewBoxWidth} ${height}`,
|
|
8343
|
+
width,
|
|
8344
|
+
height,
|
|
8345
|
+
className: cn(
|
|
8346
|
+
"chart-sparkline overflow-visible",
|
|
8347
|
+
variant === "area" ? "chart-sparkline-area-container" : "chart-sparkline-line-container",
|
|
8348
|
+
className
|
|
8349
|
+
),
|
|
8350
|
+
style: {
|
|
8351
|
+
height: typeof height === "number" ? `${height}px` : height,
|
|
8352
|
+
width: typeof width === "number" ? `${width}px` : width,
|
|
8353
|
+
...style
|
|
8354
|
+
},
|
|
8355
|
+
preserveAspectRatio,
|
|
8356
|
+
...props,
|
|
8357
|
+
children: [
|
|
8358
|
+
variant === "area" && showGradient && /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
8359
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: resolvedColor, stopOpacity: "0.28" }),
|
|
8360
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: resolvedColor, stopOpacity: "0.0" })
|
|
8361
|
+
] }) }),
|
|
8362
|
+
variant === "area" && areaPath && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8363
|
+
"path",
|
|
8364
|
+
{
|
|
8365
|
+
d: areaPath,
|
|
8366
|
+
fill: showGradient ? `url(#${gradientId})` : resolvedColor,
|
|
8367
|
+
fillOpacity: showGradient ? void 0 : 0.15,
|
|
8368
|
+
stroke: "none",
|
|
8369
|
+
className: "chart-sparkline-area"
|
|
8370
|
+
}
|
|
8371
|
+
),
|
|
8372
|
+
linePath && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8373
|
+
"path",
|
|
8374
|
+
{
|
|
8375
|
+
d: linePath,
|
|
8376
|
+
fill: "none",
|
|
8377
|
+
stroke: resolvedColor,
|
|
8378
|
+
strokeWidth,
|
|
8379
|
+
strokeLinecap: "round",
|
|
8380
|
+
strokeLinejoin: "round",
|
|
8381
|
+
vectorEffect: "non-scaling-stroke",
|
|
8382
|
+
className: "chart-sparkline-line"
|
|
8383
|
+
}
|
|
8384
|
+
),
|
|
8385
|
+
showEndDot && lastPoint && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8386
|
+
"circle",
|
|
8387
|
+
{
|
|
8388
|
+
cx: lastPoint.x,
|
|
8389
|
+
cy: lastPoint.y,
|
|
8390
|
+
r: 3,
|
|
8391
|
+
fill: resolvedColor,
|
|
8392
|
+
stroke: "var(--kj-background, #ffffff)",
|
|
8393
|
+
strokeWidth: 1.5,
|
|
8394
|
+
vectorEffect: "non-scaling-stroke",
|
|
8395
|
+
className: "chart-sparkline-dot"
|
|
8396
|
+
}
|
|
8397
|
+
)
|
|
8398
|
+
]
|
|
8399
|
+
}
|
|
8400
|
+
);
|
|
8401
|
+
}
|
|
8402
|
+
);
|
|
8403
|
+
Sparkline.displayName = "Sparkline";
|
|
8404
|
+
var LineChart = React52__namespace.forwardRef(
|
|
8405
|
+
({
|
|
8406
|
+
data,
|
|
8407
|
+
index,
|
|
8408
|
+
series,
|
|
8409
|
+
variant = "line",
|
|
8410
|
+
curve = "linear",
|
|
8411
|
+
showGrid = true,
|
|
8412
|
+
showXAxis = true,
|
|
8413
|
+
showYAxis = true,
|
|
8414
|
+
showLegend,
|
|
8415
|
+
showTooltip = true,
|
|
8416
|
+
showDots = false,
|
|
8417
|
+
showGradient = true,
|
|
8418
|
+
showA11yTable,
|
|
8419
|
+
valueFormatter,
|
|
8420
|
+
indexFormatter,
|
|
8421
|
+
emptyMessage = "Brak danych do wy\u015Bwietlenia",
|
|
8422
|
+
height = 280,
|
|
8423
|
+
width = "100%",
|
|
8424
|
+
margin: marginProp,
|
|
8425
|
+
ariaLabel,
|
|
8426
|
+
caption,
|
|
8427
|
+
strokeWidth = 2,
|
|
8428
|
+
activeSeries: activeSeriesProp,
|
|
8429
|
+
onSeriesToggle,
|
|
8430
|
+
yTicksCount = 5,
|
|
8431
|
+
yDomain: yDomainProp,
|
|
8432
|
+
autoScale = false,
|
|
8433
|
+
className,
|
|
8434
|
+
style,
|
|
8435
|
+
...props
|
|
8436
|
+
}, ref) => {
|
|
8437
|
+
const rawId = React52__namespace.useId();
|
|
8438
|
+
const idPrefix = `kj-chart-${rawId.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
8439
|
+
const containerRef = React52__namespace.useRef(null);
|
|
8440
|
+
const combinedRef = (node) => {
|
|
8441
|
+
containerRef.current = node;
|
|
8442
|
+
if (typeof ref === "function") {
|
|
8443
|
+
ref(node);
|
|
8444
|
+
} else if (ref) {
|
|
8445
|
+
ref.current = node;
|
|
8446
|
+
}
|
|
8447
|
+
};
|
|
8448
|
+
const [internalDisabledSeries, setInternalDisabledSeries] = React52__namespace.useState([]);
|
|
8449
|
+
const disabledSeries = activeSeriesProp ? series.filter((s) => !activeSeriesProp.includes(s.key)).map((s) => s.key) : internalDisabledSeries;
|
|
8450
|
+
const handleSeriesToggle = (key) => {
|
|
8451
|
+
onSeriesToggle?.(key);
|
|
8452
|
+
setInternalDisabledSeries(
|
|
8453
|
+
(prev) => prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]
|
|
8454
|
+
);
|
|
8455
|
+
};
|
|
8456
|
+
const activeSeriesList = React52__namespace.useMemo(
|
|
8457
|
+
() => series.filter((s) => !disabledSeries.includes(s.key)),
|
|
8458
|
+
[series, disabledSeries]
|
|
8459
|
+
);
|
|
8460
|
+
const [activeIndex, setActiveIndex] = React52__namespace.useState(null);
|
|
8461
|
+
const [tooltipPos, setTooltipPos] = React52__namespace.useState(null);
|
|
8462
|
+
const computedAriaLabel = props["aria-label"] ?? ariaLabel ?? (variant === "area" ? "Wykres warstwowy" : "Wykres liniowy");
|
|
8463
|
+
if (!data || data.length === 0) {
|
|
8464
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8465
|
+
ChartContainer,
|
|
8466
|
+
{
|
|
8467
|
+
ref: combinedRef,
|
|
8468
|
+
width: typeof width === "number" ? width : void 0,
|
|
8469
|
+
height,
|
|
8470
|
+
"aria-label": computedAriaLabel,
|
|
8471
|
+
className: cn("chart-line-empty", className),
|
|
8472
|
+
style,
|
|
8473
|
+
...props,
|
|
8474
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chart-empty-message flex h-full min-h-[160px] w-full items-center justify-center text-sm text-muted-foreground select-none", children: emptyMessage })
|
|
8475
|
+
}
|
|
8476
|
+
);
|
|
8477
|
+
}
|
|
8478
|
+
const measuredWidth = useChartWidth(
|
|
8479
|
+
containerRef,
|
|
8480
|
+
typeof width === "number" && width > 0 ? width : 600
|
|
8481
|
+
);
|
|
8482
|
+
const viewBoxWidth = typeof width === "number" && width > 0 ? width : measuredWidth > 0 ? measuredWidth : 600;
|
|
8483
|
+
const viewBoxHeight = typeof height === "number" && height > 0 ? height : 280;
|
|
8484
|
+
const margin = {
|
|
8485
|
+
top: 16,
|
|
8486
|
+
right: 20,
|
|
8487
|
+
bottom: showXAxis ? 32 : 16,
|
|
8488
|
+
left: showYAxis ? 48 : 16,
|
|
8489
|
+
...marginProp
|
|
8490
|
+
};
|
|
8491
|
+
const plotLeft = margin.left;
|
|
8492
|
+
const plotRight = viewBoxWidth - margin.right;
|
|
8493
|
+
const plotTop = margin.top;
|
|
8494
|
+
const plotBottom = viewBoxHeight - margin.bottom;
|
|
8495
|
+
const plotWidth = Math.max(0, plotRight - plotLeft);
|
|
8496
|
+
const plotHeight = Math.max(0, plotBottom - plotTop);
|
|
8497
|
+
const allValues = [];
|
|
8498
|
+
for (const row of data) {
|
|
8499
|
+
for (const s of activeSeriesList) {
|
|
8500
|
+
const val = row[s.key];
|
|
8501
|
+
if (typeof val === "number" && Number.isFinite(val)) {
|
|
8502
|
+
allValues.push(val);
|
|
8503
|
+
}
|
|
8504
|
+
}
|
|
8505
|
+
}
|
|
8506
|
+
let dataMin = allValues.length > 0 ? Math.min(...allValues) : 0;
|
|
8507
|
+
const dataMax = allValues.length > 0 ? Math.max(...allValues) : 100;
|
|
8508
|
+
if (!autoScale && dataMin > 0) {
|
|
8509
|
+
dataMin = 0;
|
|
8510
|
+
}
|
|
8511
|
+
const niceScale = getNiceScale(dataMin, dataMax, yTicksCount);
|
|
8512
|
+
const domainMin = yDomainProp ? yDomainProp[0] : niceScale.min;
|
|
8513
|
+
const domainMax = yDomainProp ? yDomainProp[1] : niceScale.max;
|
|
8514
|
+
const getY = (val) => {
|
|
8515
|
+
return linearScale(val, [domainMin, domainMax], [plotBottom, plotTop]);
|
|
8516
|
+
};
|
|
8517
|
+
const getX = (idx) => {
|
|
8518
|
+
if (data.length <= 1) {
|
|
8519
|
+
return plotLeft + plotWidth / 2;
|
|
8520
|
+
}
|
|
8521
|
+
return linearScale(idx, [0, data.length - 1], [plotLeft, plotRight]);
|
|
8522
|
+
};
|
|
8523
|
+
const yTicks = (yDomainProp ? getNiceScale(yDomainProp[0], yDomainProp[1], yTicksCount).ticks : niceScale.ticks).map((val) => ({
|
|
8524
|
+
value: val,
|
|
8525
|
+
y: getY(val)
|
|
8526
|
+
}));
|
|
8527
|
+
const maxXTicks = Math.max(2, Math.floor(plotWidth / 60));
|
|
8528
|
+
const xStep = data.length > maxXTicks ? Math.ceil(data.length / maxXTicks) : 1;
|
|
8529
|
+
const xTicks = data.map((row, idx) => ({
|
|
8530
|
+
value: row[index] ?? "",
|
|
8531
|
+
x: getX(idx),
|
|
8532
|
+
idx
|
|
8533
|
+
})).filter((_, i) => i % xStep === 0 || i === data.length - 1);
|
|
8534
|
+
const handlePointerMove = (clientX, clientY, target) => {
|
|
8535
|
+
const rect = target.getBoundingClientRect();
|
|
8536
|
+
let relativeX;
|
|
8537
|
+
if (rect.width > 0) {
|
|
8538
|
+
relativeX = (clientX - rect.left) / rect.width * plotWidth + plotLeft;
|
|
8539
|
+
} else {
|
|
8540
|
+
relativeX = clientX;
|
|
8541
|
+
}
|
|
8542
|
+
let closestIdx = 0;
|
|
8543
|
+
let minDiff = Infinity;
|
|
8544
|
+
for (let i = 0; i < data.length; i++) {
|
|
8545
|
+
const ptX = getX(i);
|
|
8546
|
+
const diff = Math.abs(ptX - relativeX);
|
|
8547
|
+
if (diff < minDiff) {
|
|
8548
|
+
minDiff = diff;
|
|
8549
|
+
closestIdx = i;
|
|
8550
|
+
}
|
|
8551
|
+
}
|
|
8552
|
+
setActiveIndex(closestIdx);
|
|
8553
|
+
if (containerRef.current) {
|
|
8554
|
+
const containerRect = containerRef.current.getBoundingClientRect();
|
|
8555
|
+
if (containerRect.width > 0) {
|
|
8556
|
+
const scaleX = containerRect.width / viewBoxWidth;
|
|
8557
|
+
const pixelX = getX(closestIdx) * scaleX;
|
|
8558
|
+
const pixelY = clientY - containerRect.top;
|
|
8559
|
+
setTooltipPos({ x: pixelX, y: Math.max(20, pixelY) });
|
|
8560
|
+
return;
|
|
8561
|
+
}
|
|
8562
|
+
}
|
|
8563
|
+
setTooltipPos({ x: getX(closestIdx), y: clientY });
|
|
8564
|
+
};
|
|
8565
|
+
const handleMouseMove = (e) => {
|
|
8566
|
+
handlePointerMove(e.clientX, e.clientY, e.currentTarget);
|
|
8567
|
+
};
|
|
8568
|
+
const handleTouch = (e) => {
|
|
8569
|
+
if (e.touches.length > 0) {
|
|
8570
|
+
const touch = e.touches[0];
|
|
8571
|
+
handlePointerMove(touch.clientX, touch.clientY, e.currentTarget);
|
|
8572
|
+
}
|
|
8573
|
+
};
|
|
8574
|
+
const handleMouseLeave = () => {
|
|
8575
|
+
setActiveIndex(null);
|
|
8576
|
+
setTooltipPos(null);
|
|
8577
|
+
};
|
|
8578
|
+
const activeRow = activeIndex !== null && data[activeIndex] ? data[activeIndex] : null;
|
|
8579
|
+
const tooltipTitle = activeRow !== null ? indexFormatter ? indexFormatter(activeRow[index]) : String(activeRow[index] ?? "") : "";
|
|
8580
|
+
const tooltipItems = activeRow !== null ? activeSeriesList.map((s, sIdx) => {
|
|
8581
|
+
const origIdx = series.findIndex((orig) => orig.key === s.key);
|
|
8582
|
+
const seriesColorIndex = origIdx >= 0 ? origIdx : sIdx;
|
|
8583
|
+
const raw = activeRow[s.key];
|
|
8584
|
+
const num = typeof raw === "number" ? raw : Number(raw) || 0;
|
|
8585
|
+
return {
|
|
8586
|
+
key: s.key,
|
|
8587
|
+
label: s.label,
|
|
8588
|
+
value: num,
|
|
8589
|
+
color: s.color ?? `chart${seriesColorIndex % 6 + 1}`,
|
|
8590
|
+
formattedValue: valueFormatter ? valueFormatter(num) : void 0
|
|
8591
|
+
};
|
|
8592
|
+
}) : [];
|
|
8593
|
+
const baselineY = getY(Math.max(0, domainMin));
|
|
8594
|
+
const shouldShowLegend = showLegend ?? series.length > 1;
|
|
8595
|
+
const shouldShowA11yTable = showA11yTable ?? Boolean(caption);
|
|
8596
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8597
|
+
ChartContainer,
|
|
8598
|
+
{
|
|
8599
|
+
ref: combinedRef,
|
|
8600
|
+
width: typeof width === "number" ? width : void 0,
|
|
8601
|
+
height,
|
|
8602
|
+
viewBox: `0 0 ${viewBoxWidth} ${viewBoxHeight}`,
|
|
8603
|
+
"aria-label": computedAriaLabel,
|
|
8604
|
+
className: cn(
|
|
8605
|
+
"chart-line-root flex flex-col",
|
|
8606
|
+
variant === "area" ? "chart-area-root" : "chart-line-root",
|
|
8607
|
+
className
|
|
8608
|
+
),
|
|
8609
|
+
style,
|
|
8610
|
+
...props,
|
|
8611
|
+
children: [
|
|
8612
|
+
/* @__PURE__ */ jsxRuntime.jsx("defs", { children: variant === "area" && showGradient && series.map((s, idx) => {
|
|
8613
|
+
const gradColor = getChartColor(s.color, idx);
|
|
8614
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8615
|
+
"linearGradient",
|
|
8616
|
+
{
|
|
8617
|
+
id: `${idPrefix}-grad-${s.key}`,
|
|
8618
|
+
x1: "0",
|
|
8619
|
+
y1: "0",
|
|
8620
|
+
x2: "0",
|
|
8621
|
+
y2: "1",
|
|
8622
|
+
children: [
|
|
8623
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: gradColor, stopOpacity: "0.28" }),
|
|
8624
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: gradColor, stopOpacity: "0.0" })
|
|
8625
|
+
]
|
|
8626
|
+
},
|
|
8627
|
+
`grad-${s.key}`
|
|
8628
|
+
);
|
|
8629
|
+
}) }),
|
|
8630
|
+
showGrid && /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${plotLeft}, 0)`, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8631
|
+
ChartGrid,
|
|
8632
|
+
{
|
|
8633
|
+
horizontal: true,
|
|
8634
|
+
yTicks: yTicks.map((t) => t.value),
|
|
8635
|
+
scaleY: getY,
|
|
8636
|
+
width: plotWidth
|
|
8637
|
+
}
|
|
8638
|
+
) }),
|
|
8639
|
+
activeSeriesList.map((s, sIdx) => {
|
|
8640
|
+
const origIdx = series.findIndex((orig) => orig.key === s.key);
|
|
8641
|
+
const seriesColorIndex = origIdx >= 0 ? origIdx : sIdx;
|
|
8642
|
+
const sColor = getChartColor(s.color, seriesColorIndex);
|
|
8643
|
+
const points = [];
|
|
8644
|
+
for (let i = 0; i < data.length; i++) {
|
|
8645
|
+
const rawVal = data[i][s.key];
|
|
8646
|
+
if (rawVal !== void 0 && rawVal !== null && Number.isFinite(Number(rawVal))) {
|
|
8647
|
+
points.push({ x: getX(i), y: getY(Number(rawVal)) });
|
|
8648
|
+
}
|
|
8649
|
+
}
|
|
8650
|
+
if (points.length === 0) return null;
|
|
8651
|
+
const linePath = generateLinePath(points, curve);
|
|
8652
|
+
const areaPath = variant === "area" ? generateAreaPath(points, baselineY, curve) : "";
|
|
8653
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("g", { className: `chart-series chart-series-${s.key}`, children: [
|
|
8654
|
+
variant === "area" && areaPath && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8655
|
+
"path",
|
|
8656
|
+
{
|
|
8657
|
+
d: areaPath,
|
|
8658
|
+
fill: showGradient ? `url(#${idPrefix}-grad-${s.key})` : sColor,
|
|
8659
|
+
fillOpacity: showGradient ? void 0 : 0.15,
|
|
8660
|
+
stroke: "none",
|
|
8661
|
+
className: "chart-area pointer-events-none transition-opacity duration-200"
|
|
8662
|
+
}
|
|
8663
|
+
),
|
|
8664
|
+
linePath && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8665
|
+
"path",
|
|
8666
|
+
{
|
|
8667
|
+
d: linePath,
|
|
8668
|
+
fill: "none",
|
|
8669
|
+
stroke: sColor,
|
|
8670
|
+
strokeWidth,
|
|
8671
|
+
strokeLinecap: "round",
|
|
8672
|
+
strokeLinejoin: "round",
|
|
8673
|
+
vectorEffect: "non-scaling-stroke",
|
|
8674
|
+
className: "chart-line pointer-events-none transition-opacity duration-200"
|
|
8675
|
+
}
|
|
8676
|
+
),
|
|
8677
|
+
showDots && points.map((pt, ptIdx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
8678
|
+
"circle",
|
|
8679
|
+
{
|
|
8680
|
+
cx: pt.x,
|
|
8681
|
+
cy: pt.y,
|
|
8682
|
+
r: 3,
|
|
8683
|
+
fill: sColor,
|
|
8684
|
+
stroke: "var(--kj-background, #ffffff)",
|
|
8685
|
+
strokeWidth: 1.5,
|
|
8686
|
+
className: "chart-dot pointer-events-none"
|
|
8687
|
+
},
|
|
8688
|
+
`dot-${s.key}-${ptIdx}`
|
|
8689
|
+
)),
|
|
8690
|
+
activeIndex !== null && activeRow && activeRow[s.key] !== void 0 && activeRow[s.key] !== null && Number.isFinite(Number(activeRow[s.key])) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8691
|
+
"circle",
|
|
8692
|
+
{
|
|
8693
|
+
cx: getX(activeIndex),
|
|
8694
|
+
cy: getY(Number(activeRow[s.key])),
|
|
8695
|
+
r: 4.5,
|
|
8696
|
+
fill: sColor,
|
|
8697
|
+
stroke: "var(--kj-background, #ffffff)",
|
|
8698
|
+
strokeWidth: 2,
|
|
8699
|
+
className: "chart-active-dot pointer-events-none"
|
|
8700
|
+
}
|
|
8701
|
+
)
|
|
8702
|
+
] }, `series-${s.key}`);
|
|
8703
|
+
}),
|
|
8704
|
+
showXAxis && /* @__PURE__ */ jsxRuntime.jsx(ChartXAxis, { ticks: xTicks, y: plotBottom + 16, formatter: indexFormatter }),
|
|
8705
|
+
showYAxis && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8706
|
+
ChartYAxis,
|
|
8707
|
+
{
|
|
8708
|
+
ticks: yTicks,
|
|
8709
|
+
x: plotLeft - 8,
|
|
8710
|
+
formatter: valueFormatter
|
|
8711
|
+
}
|
|
8712
|
+
),
|
|
8713
|
+
showTooltip && /* biome-ignore lint/a11y/noStaticElementInteractions: transparent SVG overlay tracks pointer for chart tooltip */
|
|
8714
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8715
|
+
"rect",
|
|
8716
|
+
{
|
|
8717
|
+
className: "chart-interactive-overlay cursor-crosshair",
|
|
8718
|
+
x: plotLeft,
|
|
8719
|
+
y: plotTop,
|
|
8720
|
+
width: plotWidth,
|
|
8721
|
+
height: plotHeight,
|
|
8722
|
+
fill: "transparent",
|
|
8723
|
+
pointerEvents: "all",
|
|
8724
|
+
onMouseMove: handleMouseMove,
|
|
8725
|
+
onMouseLeave: handleMouseLeave,
|
|
8726
|
+
onTouchStart: handleTouch,
|
|
8727
|
+
onTouchMove: handleTouch,
|
|
8728
|
+
onTouchEnd: handleMouseLeave
|
|
8729
|
+
}
|
|
8730
|
+
),
|
|
8731
|
+
showTooltip && activeIndex !== null && tooltipPos && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8732
|
+
ChartTooltip,
|
|
8733
|
+
{
|
|
8734
|
+
open: activeIndex !== null,
|
|
8735
|
+
title: tooltipTitle,
|
|
8736
|
+
items: tooltipItems,
|
|
8737
|
+
x: tooltipPos.x,
|
|
8738
|
+
y: tooltipPos.y,
|
|
8739
|
+
showCrosshair: true,
|
|
8740
|
+
crosshairX: tooltipPos.x,
|
|
8741
|
+
crosshairHeight: plotBottom,
|
|
8742
|
+
valueFormatter
|
|
8743
|
+
}
|
|
8744
|
+
),
|
|
8745
|
+
shouldShowLegend && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8746
|
+
ChartLegend,
|
|
8747
|
+
{
|
|
8748
|
+
series,
|
|
8749
|
+
disabledSeries,
|
|
8750
|
+
onToggle: handleSeriesToggle
|
|
8751
|
+
}
|
|
8752
|
+
),
|
|
8753
|
+
shouldShowA11yTable && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8754
|
+
ChartA11yTable,
|
|
8755
|
+
{
|
|
8756
|
+
caption: caption ?? computedAriaLabel,
|
|
8757
|
+
series,
|
|
8758
|
+
data,
|
|
8759
|
+
indexKey: index,
|
|
8760
|
+
valueFormatter
|
|
8761
|
+
}
|
|
8762
|
+
)
|
|
8763
|
+
]
|
|
8764
|
+
}
|
|
8765
|
+
);
|
|
8766
|
+
}
|
|
8767
|
+
);
|
|
8768
|
+
LineChart.displayName = "LineChart";
|
|
8769
|
+
var AreaChart = React52__namespace.forwardRef((props, ref) => /* @__PURE__ */ jsxRuntime.jsx(LineChart, { ref, variant: "area", ...props }));
|
|
8770
|
+
AreaChart.displayName = "AreaChart";
|
|
8771
|
+
var BarChart = React52__namespace.forwardRef(
|
|
8772
|
+
({
|
|
8773
|
+
data,
|
|
8774
|
+
index,
|
|
8775
|
+
series,
|
|
8776
|
+
type = "grouped",
|
|
8777
|
+
layout = "vertical",
|
|
8778
|
+
radius = 4,
|
|
8779
|
+
showGrid = true,
|
|
8780
|
+
showXAxis = true,
|
|
8781
|
+
showYAxis = true,
|
|
8782
|
+
showLegend,
|
|
8783
|
+
showTooltip = true,
|
|
8784
|
+
showA11yTable,
|
|
8785
|
+
valueFormatter,
|
|
8786
|
+
indexFormatter,
|
|
8787
|
+
emptyMessage = "Brak danych do wy\u015Bwietlenia",
|
|
8788
|
+
height = 280,
|
|
8789
|
+
width = "100%",
|
|
8790
|
+
margin: marginProp,
|
|
8791
|
+
ariaLabel,
|
|
8792
|
+
caption,
|
|
8793
|
+
activeSeries: activeSeriesProp,
|
|
8794
|
+
onSeriesToggle,
|
|
8795
|
+
yTicksCount = 5,
|
|
8796
|
+
yDomain: yDomainProp,
|
|
8797
|
+
autoScale = false,
|
|
8798
|
+
barMaxWidth,
|
|
8799
|
+
groupPadding = 0.2,
|
|
8800
|
+
barPadding = 2,
|
|
8801
|
+
barClassName,
|
|
8802
|
+
className,
|
|
8803
|
+
style,
|
|
8804
|
+
...props
|
|
8805
|
+
}, ref) => {
|
|
8806
|
+
const containerRef = React52__namespace.useRef(null);
|
|
8807
|
+
const combinedRef = (node) => {
|
|
8808
|
+
containerRef.current = node;
|
|
8809
|
+
if (typeof ref === "function") {
|
|
8810
|
+
ref(node);
|
|
8811
|
+
} else if (ref) {
|
|
8812
|
+
ref.current = node;
|
|
8813
|
+
}
|
|
8814
|
+
};
|
|
8815
|
+
const [internalDisabledSeries, setInternalDisabledSeries] = React52__namespace.useState([]);
|
|
8816
|
+
const disabledSeries = activeSeriesProp ? series.filter((s) => !activeSeriesProp.includes(s.key)).map((s) => s.key) : internalDisabledSeries;
|
|
8817
|
+
const handleSeriesToggle = (key) => {
|
|
8818
|
+
onSeriesToggle?.(key);
|
|
8819
|
+
setInternalDisabledSeries(
|
|
8820
|
+
(prev) => prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]
|
|
8821
|
+
);
|
|
8822
|
+
};
|
|
8823
|
+
const activeSeriesList = React52__namespace.useMemo(
|
|
8824
|
+
() => series.filter((s) => !disabledSeries.includes(s.key)),
|
|
8825
|
+
[series, disabledSeries]
|
|
8826
|
+
);
|
|
8827
|
+
const [activeGroup, setActiveGroup] = React52__namespace.useState(null);
|
|
8828
|
+
const [activeSeriesKey, setActiveSeriesKey] = React52__namespace.useState(null);
|
|
8829
|
+
const [tooltipPos, setTooltipPos] = React52__namespace.useState(null);
|
|
8830
|
+
const computedAriaLabel = props["aria-label"] ?? ariaLabel ?? (type === "stacked" ? "Wykres s\u0142upkowy skumulowany" : "Wykres s\u0142upkowy");
|
|
8831
|
+
if (!data || data.length === 0) {
|
|
8832
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8833
|
+
ChartContainer,
|
|
8834
|
+
{
|
|
8835
|
+
ref: combinedRef,
|
|
8836
|
+
width: typeof width === "number" ? width : void 0,
|
|
8837
|
+
height,
|
|
8838
|
+
"aria-label": computedAriaLabel,
|
|
8839
|
+
className: cn("chart-bar-empty", className),
|
|
8840
|
+
style,
|
|
8841
|
+
...props,
|
|
8842
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chart-empty-message flex h-full min-h-[160px] w-full items-center justify-center text-sm text-muted-foreground select-none", children: emptyMessage })
|
|
8843
|
+
}
|
|
8844
|
+
);
|
|
8845
|
+
}
|
|
8846
|
+
const measuredWidth = useChartWidth(
|
|
8847
|
+
containerRef,
|
|
8848
|
+
typeof width === "number" && width > 0 ? width : 600
|
|
8849
|
+
);
|
|
8850
|
+
const viewBoxWidth = typeof width === "number" && width > 0 ? width : measuredWidth > 0 ? measuredWidth : 600;
|
|
8851
|
+
const viewBoxHeight = typeof height === "number" && height > 0 ? height : 280;
|
|
8852
|
+
const margin = {
|
|
8853
|
+
top: 16,
|
|
8854
|
+
right: 20,
|
|
8855
|
+
bottom: showXAxis ? 32 : 16,
|
|
8856
|
+
left: showYAxis ? layout === "horizontal" ? 64 : 48 : 16,
|
|
8857
|
+
...marginProp
|
|
8858
|
+
};
|
|
8859
|
+
const plotLeft = margin.left;
|
|
8860
|
+
const plotRight = viewBoxWidth - margin.right;
|
|
8861
|
+
const plotTop = margin.top;
|
|
8862
|
+
const plotBottom = viewBoxHeight - margin.bottom;
|
|
8863
|
+
const plotWidth = Math.max(0, plotRight - plotLeft);
|
|
8864
|
+
const plotHeight = Math.max(0, plotBottom - plotTop);
|
|
8865
|
+
let dataMin = 0;
|
|
8866
|
+
let dataMax = 100;
|
|
8867
|
+
if (type === "stacked") {
|
|
8868
|
+
const stackSums = [];
|
|
8869
|
+
for (const row of data) {
|
|
8870
|
+
let sum = 0;
|
|
8871
|
+
for (const s of activeSeriesList) {
|
|
8872
|
+
const val = row[s.key];
|
|
8873
|
+
if (typeof val === "number" && Number.isFinite(val)) {
|
|
8874
|
+
sum += Math.max(0, val);
|
|
8875
|
+
}
|
|
8876
|
+
}
|
|
8877
|
+
stackSums.push(sum);
|
|
8878
|
+
}
|
|
8879
|
+
dataMax = stackSums.length > 0 ? Math.max(...stackSums, 0) : 100;
|
|
8880
|
+
if (dataMax === 0) dataMax = 100;
|
|
8881
|
+
} else {
|
|
8882
|
+
const allValues = [];
|
|
8883
|
+
for (const row of data) {
|
|
8884
|
+
for (const s of activeSeriesList) {
|
|
8885
|
+
const val = row[s.key];
|
|
8886
|
+
if (typeof val === "number" && Number.isFinite(val)) {
|
|
8887
|
+
allValues.push(val);
|
|
8888
|
+
}
|
|
8889
|
+
}
|
|
8890
|
+
}
|
|
8891
|
+
dataMax = allValues.length > 0 ? Math.max(...allValues, 0) : 100;
|
|
8892
|
+
if (allValues.length > 0 && autoScale) {
|
|
8893
|
+
dataMin = Math.min(...allValues, 0);
|
|
8894
|
+
}
|
|
8895
|
+
if (dataMax === 0) dataMax = 100;
|
|
8896
|
+
}
|
|
8897
|
+
const niceScale = getNiceScale(dataMin, dataMax, yTicksCount);
|
|
8898
|
+
const domainMin = yDomainProp ? yDomainProp[0] : niceScale.min;
|
|
8899
|
+
const domainMax = yDomainProp ? yDomainProp[1] : niceScale.max;
|
|
8900
|
+
const yDomain = [domainMin, domainMax];
|
|
8901
|
+
const barDataPoints = [];
|
|
8902
|
+
for (let group = 0; group < data.length; group++) {
|
|
8903
|
+
const row = data[group];
|
|
8904
|
+
for (let sIdx = 0; sIdx < activeSeriesList.length; sIdx++) {
|
|
8905
|
+
const s = activeSeriesList[sIdx];
|
|
8906
|
+
const rawVal = row[s.key];
|
|
8907
|
+
const num = typeof rawVal === "number" && Number.isFinite(rawVal) ? rawVal : Number(rawVal) || 0;
|
|
8908
|
+
barDataPoints.push({
|
|
8909
|
+
group,
|
|
8910
|
+
seriesIndex: sIdx,
|
|
8911
|
+
value: num,
|
|
8912
|
+
seriesKey: s.key,
|
|
8913
|
+
seriesLabel: s.label,
|
|
8914
|
+
categoryValue: row[index]
|
|
8915
|
+
});
|
|
8916
|
+
}
|
|
8917
|
+
}
|
|
8918
|
+
const barRects = calculateBarLayout({
|
|
8919
|
+
dataCount: data.length,
|
|
8920
|
+
seriesCount: activeSeriesList.length,
|
|
8921
|
+
plotWidth,
|
|
8922
|
+
plotHeight,
|
|
8923
|
+
yDomain,
|
|
8924
|
+
data: barDataPoints,
|
|
8925
|
+
mode: type,
|
|
8926
|
+
layout,
|
|
8927
|
+
barMaxWidth,
|
|
8928
|
+
groupPadding,
|
|
8929
|
+
barPadding
|
|
8930
|
+
});
|
|
8931
|
+
const isVertical = layout === "vertical";
|
|
8932
|
+
const slotSize = data.length > 0 ? isVertical ? plotWidth / data.length : plotHeight / data.length : 0;
|
|
8933
|
+
const categoryTicks = data.map((row, idx) => ({
|
|
8934
|
+
value: row[index] ?? "",
|
|
8935
|
+
coord: isVertical ? plotLeft + (idx + 0.5) * slotSize : plotTop + (idx + 0.5) * slotSize
|
|
8936
|
+
}));
|
|
8937
|
+
const valueTicks = (yDomainProp ? getNiceScale(yDomainProp[0], yDomainProp[1], yTicksCount).ticks : niceScale.ticks).map((val) => ({
|
|
8938
|
+
value: val,
|
|
8939
|
+
coord: isVertical ? linearScale(val, yDomain, [plotBottom, plotTop]) : linearScale(val, yDomain, [plotLeft, plotRight])
|
|
8940
|
+
}));
|
|
8941
|
+
const updateTooltipPosition = (bar) => {
|
|
8942
|
+
let targetX;
|
|
8943
|
+
let targetY;
|
|
8944
|
+
if (layout === "horizontal") {
|
|
8945
|
+
targetX = plotLeft + bar.x + bar.width;
|
|
8946
|
+
targetY = plotTop + bar.y + bar.height / 2;
|
|
8947
|
+
} else {
|
|
8948
|
+
targetX = plotLeft + bar.x + bar.width / 2;
|
|
8949
|
+
targetY = plotTop + bar.y;
|
|
8950
|
+
}
|
|
8951
|
+
if (containerRef.current) {
|
|
8952
|
+
const containerRect = containerRef.current.getBoundingClientRect();
|
|
8953
|
+
if (containerRect.width > 0 && containerRect.height > 0) {
|
|
8954
|
+
const scaleX = containerRect.width / viewBoxWidth;
|
|
8955
|
+
const scaleY = containerRect.height / viewBoxHeight;
|
|
8956
|
+
setTooltipPos({ x: targetX * scaleX, y: targetY * scaleY });
|
|
8957
|
+
return;
|
|
8958
|
+
}
|
|
8959
|
+
}
|
|
8960
|
+
setTooltipPos({ x: targetX, y: targetY });
|
|
8961
|
+
};
|
|
8962
|
+
const handleBarMouseEnter = (bar) => {
|
|
8963
|
+
if (!showTooltip) return;
|
|
8964
|
+
setActiveGroup(bar.group);
|
|
8965
|
+
setActiveSeriesKey(bar.seriesKey);
|
|
8966
|
+
updateTooltipPosition(bar);
|
|
8967
|
+
};
|
|
8968
|
+
const handleBarMouseLeave = () => {
|
|
8969
|
+
setActiveGroup(null);
|
|
8970
|
+
setActiveSeriesKey(null);
|
|
8971
|
+
setTooltipPos(null);
|
|
8972
|
+
};
|
|
8973
|
+
const handleBarTouchStart = (bar) => {
|
|
8974
|
+
if (!showTooltip) return;
|
|
8975
|
+
setActiveGroup(bar.group);
|
|
8976
|
+
setActiveSeriesKey(bar.seriesKey);
|
|
8977
|
+
updateTooltipPosition(bar);
|
|
8978
|
+
};
|
|
8979
|
+
const handleBarTouchEnd = () => {
|
|
8980
|
+
setActiveGroup(null);
|
|
8981
|
+
setActiveSeriesKey(null);
|
|
8982
|
+
setTooltipPos(null);
|
|
8983
|
+
};
|
|
8984
|
+
const activeRow = activeGroup !== null && data[activeGroup] ? data[activeGroup] : null;
|
|
8985
|
+
const tooltipTitle = activeRow !== null ? indexFormatter ? indexFormatter(activeRow[index]) : String(activeRow[index] ?? "") : "";
|
|
8986
|
+
const tooltipItems = activeRow !== null ? activeSeriesList.map((s, sIdx) => {
|
|
8987
|
+
const origIdx = series.findIndex((orig) => orig.key === s.key);
|
|
8988
|
+
const seriesColorIndex = origIdx >= 0 ? origIdx : sIdx;
|
|
8989
|
+
const raw = activeRow[s.key];
|
|
8990
|
+
const num = typeof raw === "number" ? raw : Number(raw) || 0;
|
|
8991
|
+
return {
|
|
8992
|
+
key: s.key,
|
|
8993
|
+
label: s.label,
|
|
8994
|
+
value: num,
|
|
8995
|
+
color: s.color ?? `chart${seriesColorIndex % 6 + 1}`,
|
|
8996
|
+
formattedValue: valueFormatter ? valueFormatter(num) : void 0
|
|
8997
|
+
};
|
|
8998
|
+
}) : [];
|
|
8999
|
+
const shouldShowLegend = showLegend ?? series.length > 1;
|
|
9000
|
+
const shouldShowA11yTable = showA11yTable ?? Boolean(caption);
|
|
9001
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9002
|
+
ChartContainer,
|
|
9003
|
+
{
|
|
9004
|
+
ref: combinedRef,
|
|
9005
|
+
width: typeof width === "number" ? width : void 0,
|
|
9006
|
+
height,
|
|
9007
|
+
viewBox: `0 0 ${viewBoxWidth} ${viewBoxHeight}`,
|
|
9008
|
+
"aria-label": computedAriaLabel,
|
|
9009
|
+
className: cn(
|
|
9010
|
+
"chart-bar-root flex flex-col",
|
|
9011
|
+
type === "stacked" ? "chart-bar-stacked" : "chart-bar-grouped",
|
|
9012
|
+
layout === "horizontal" ? "chart-bar-horizontal" : "chart-bar-vertical",
|
|
9013
|
+
className
|
|
9014
|
+
),
|
|
9015
|
+
style,
|
|
9016
|
+
...props,
|
|
9017
|
+
children: [
|
|
9018
|
+
showGrid && (isVertical ? /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${plotLeft}, 0)`, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9019
|
+
ChartGrid,
|
|
9020
|
+
{
|
|
9021
|
+
horizontal: true,
|
|
9022
|
+
yTicks: valueTicks.map((t) => t.value),
|
|
9023
|
+
scaleY: (val) => linearScale(val, yDomain, [plotBottom, plotTop]),
|
|
9024
|
+
width: plotWidth
|
|
9025
|
+
}
|
|
9026
|
+
) }) : /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(0, ${plotTop})`, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9027
|
+
ChartGrid,
|
|
9028
|
+
{
|
|
9029
|
+
vertical: true,
|
|
9030
|
+
horizontal: false,
|
|
9031
|
+
xTicks: valueTicks.map((t) => t.value),
|
|
9032
|
+
scaleX: (val) => linearScale(val, yDomain, [plotLeft, plotRight]),
|
|
9033
|
+
height: plotHeight
|
|
9034
|
+
}
|
|
9035
|
+
) })),
|
|
9036
|
+
/* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${plotLeft}, ${plotTop})`, className: "chart-bars-group", children: barRects.map((bar) => {
|
|
9037
|
+
const origIdx = series.findIndex((orig) => orig.key === bar.seriesKey);
|
|
9038
|
+
const seriesColorIndex = origIdx >= 0 ? origIdx : bar.seriesIndex;
|
|
9039
|
+
const matchingSeries = series[origIdx] ?? activeSeriesList[bar.seriesIndex];
|
|
9040
|
+
const sColor = getChartColor(matchingSeries?.color, seriesColorIndex);
|
|
9041
|
+
const barW = Math.max(0, bar.width);
|
|
9042
|
+
const barH = Math.max(0, bar.height);
|
|
9043
|
+
const safeRadius = radius > 0 ? Math.min(radius, Math.max(0, barW / 2), Math.max(0, barH / 2)) : 0;
|
|
9044
|
+
const isDimmed = activeGroup !== null && (bar.group !== activeGroup || type === "grouped" && activeSeriesKey !== null && bar.seriesKey !== activeSeriesKey);
|
|
9045
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9046
|
+
"rect",
|
|
9047
|
+
{
|
|
9048
|
+
className: cn(
|
|
9049
|
+
"chart-bar transition-opacity duration-150 cursor-pointer",
|
|
9050
|
+
barClassName
|
|
9051
|
+
),
|
|
9052
|
+
x: bar.x,
|
|
9053
|
+
y: bar.y,
|
|
9054
|
+
width: barW,
|
|
9055
|
+
height: barH,
|
|
9056
|
+
rx: safeRadius,
|
|
9057
|
+
ry: safeRadius,
|
|
9058
|
+
fill: sColor,
|
|
9059
|
+
opacity: isDimmed ? 0.45 : 1,
|
|
9060
|
+
tabIndex: 0,
|
|
9061
|
+
role: "graphics-symbol",
|
|
9062
|
+
"aria-label": `${bar.seriesLabel ?? bar.seriesKey}: ${valueFormatter ? valueFormatter(bar.value) : bar.value}, ${bar.categoryValue}`,
|
|
9063
|
+
onMouseEnter: () => handleBarMouseEnter(bar),
|
|
9064
|
+
onMouseMove: () => handleBarMouseEnter(bar),
|
|
9065
|
+
onMouseLeave: handleBarMouseLeave,
|
|
9066
|
+
onTouchStart: () => handleBarTouchStart(bar),
|
|
9067
|
+
onTouchEnd: handleBarTouchEnd,
|
|
9068
|
+
onFocus: () => handleBarMouseEnter(bar),
|
|
9069
|
+
onBlur: handleBarMouseLeave
|
|
9070
|
+
},
|
|
9071
|
+
`bar-${bar.group}-${bar.seriesKey}`
|
|
9072
|
+
);
|
|
9073
|
+
}) }),
|
|
9074
|
+
showXAxis && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9075
|
+
ChartXAxis,
|
|
9076
|
+
{
|
|
9077
|
+
ticks: isVertical ? categoryTicks.map((t) => ({ value: t.value, x: t.coord })) : valueTicks.map((t) => ({ value: t.value, x: t.coord })),
|
|
9078
|
+
y: plotBottom + 16,
|
|
9079
|
+
formatter: isVertical ? indexFormatter : valueFormatter
|
|
9080
|
+
}
|
|
9081
|
+
),
|
|
9082
|
+
showYAxis && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9083
|
+
ChartYAxis,
|
|
9084
|
+
{
|
|
9085
|
+
ticks: isVertical ? valueTicks.map((t) => ({ value: t.value, y: t.coord })) : categoryTicks.map((t) => ({ value: t.value, y: t.coord })),
|
|
9086
|
+
x: plotLeft - 8,
|
|
9087
|
+
formatter: isVertical ? valueFormatter : indexFormatter
|
|
9088
|
+
}
|
|
9089
|
+
),
|
|
9090
|
+
showTooltip && activeGroup !== null && tooltipPos && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9091
|
+
ChartTooltip,
|
|
9092
|
+
{
|
|
9093
|
+
open: activeGroup !== null,
|
|
9094
|
+
title: tooltipTitle,
|
|
9095
|
+
items: tooltipItems,
|
|
9096
|
+
x: tooltipPos.x,
|
|
9097
|
+
y: tooltipPos.y,
|
|
9098
|
+
valueFormatter
|
|
9099
|
+
}
|
|
9100
|
+
),
|
|
9101
|
+
shouldShowLegend && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9102
|
+
ChartLegend,
|
|
9103
|
+
{
|
|
9104
|
+
series,
|
|
9105
|
+
disabledSeries,
|
|
9106
|
+
onToggle: handleSeriesToggle
|
|
9107
|
+
}
|
|
9108
|
+
),
|
|
9109
|
+
shouldShowA11yTable && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9110
|
+
ChartA11yTable,
|
|
9111
|
+
{
|
|
9112
|
+
caption: caption ?? computedAriaLabel,
|
|
9113
|
+
series,
|
|
9114
|
+
data,
|
|
9115
|
+
indexKey: index,
|
|
9116
|
+
valueFormatter
|
|
9117
|
+
}
|
|
9118
|
+
)
|
|
9119
|
+
]
|
|
9120
|
+
}
|
|
9121
|
+
);
|
|
9122
|
+
}
|
|
9123
|
+
);
|
|
9124
|
+
BarChart.displayName = "BarChart";
|
|
9125
|
+
var SIZE_MAP = {
|
|
9126
|
+
sm: 160,
|
|
9127
|
+
md: 220,
|
|
9128
|
+
lg: 280
|
|
9129
|
+
};
|
|
9130
|
+
function getDimension(size = "md") {
|
|
9131
|
+
if (typeof size === "number") {
|
|
9132
|
+
return size > 0 ? size : 220;
|
|
9133
|
+
}
|
|
9134
|
+
return SIZE_MAP[size] ?? 220;
|
|
9135
|
+
}
|
|
9136
|
+
var DonutChart = React52__namespace.forwardRef(
|
|
9137
|
+
({
|
|
9138
|
+
data,
|
|
9139
|
+
category,
|
|
9140
|
+
value,
|
|
9141
|
+
centerLabel,
|
|
9142
|
+
size = "md",
|
|
9143
|
+
innerRadius: innerRadiusProp,
|
|
9144
|
+
colors,
|
|
9145
|
+
showLegend = true,
|
|
9146
|
+
showTooltip = true,
|
|
9147
|
+
showA11yTable,
|
|
9148
|
+
valueFormatter,
|
|
9149
|
+
emptyMessage = "Brak danych do wy\u015Bwietlenia",
|
|
9150
|
+
ariaLabel,
|
|
9151
|
+
caption,
|
|
9152
|
+
valueLabel = "Warto\u015B\u0107",
|
|
9153
|
+
activeCategories: activeCategoriesProp,
|
|
9154
|
+
onCategoryToggle,
|
|
9155
|
+
sliceClassName,
|
|
9156
|
+
className,
|
|
9157
|
+
style,
|
|
9158
|
+
...props
|
|
9159
|
+
}, ref) => {
|
|
9160
|
+
const containerRef = React52__namespace.useRef(null);
|
|
9161
|
+
const combinedRef = (node) => {
|
|
9162
|
+
containerRef.current = node;
|
|
9163
|
+
if (typeof ref === "function") {
|
|
9164
|
+
ref(node);
|
|
9165
|
+
} else if (ref) {
|
|
9166
|
+
ref.current = node;
|
|
9167
|
+
}
|
|
9168
|
+
};
|
|
9169
|
+
const [internalDisabled, setInternalDisabled] = React52__namespace.useState([]);
|
|
9170
|
+
const [activeIdx, setActiveIdx] = React52__namespace.useState(null);
|
|
9171
|
+
const [tooltipPos, setTooltipPos] = React52__namespace.useState(null);
|
|
9172
|
+
const safeData = data || [];
|
|
9173
|
+
const dimension = getDimension(size);
|
|
9174
|
+
const cx = dimension / 2;
|
|
9175
|
+
const cy = dimension / 2;
|
|
9176
|
+
const padding = 8;
|
|
9177
|
+
const outerRadius = Math.max(10, dimension / 2 - padding);
|
|
9178
|
+
let computedInnerRadius;
|
|
9179
|
+
if (innerRadiusProp !== void 0) {
|
|
9180
|
+
if (innerRadiusProp >= 0 && innerRadiusProp <= 1) {
|
|
9181
|
+
computedInnerRadius = outerRadius * innerRadiusProp;
|
|
9182
|
+
} else {
|
|
9183
|
+
computedInnerRadius = Math.min(innerRadiusProp, Math.max(0, outerRadius - 4));
|
|
9184
|
+
}
|
|
9185
|
+
} else {
|
|
9186
|
+
computedInnerRadius = outerRadius * 0.65;
|
|
9187
|
+
}
|
|
9188
|
+
const legendSeries = React52__namespace.useMemo(() => {
|
|
9189
|
+
return safeData.map((item, idx) => {
|
|
9190
|
+
const cat = String(item[category] ?? `Item ${idx + 1}`);
|
|
9191
|
+
const rawColor = item.color ?? (colors ? colors[idx % colors.length] : DEFAULT_CHART_COLORS[idx % DEFAULT_CHART_COLORS.length]);
|
|
9192
|
+
return {
|
|
9193
|
+
key: cat,
|
|
9194
|
+
label: cat,
|
|
9195
|
+
color: rawColor
|
|
9196
|
+
};
|
|
9197
|
+
});
|
|
9198
|
+
}, [safeData, category, colors]);
|
|
9199
|
+
const disabledCategories = activeCategoriesProp ? legendSeries.filter((s) => !activeCategoriesProp.includes(s.key)).map((s) => s.key) : internalDisabled;
|
|
9200
|
+
const handleLegendToggle = (catKey) => {
|
|
9201
|
+
onCategoryToggle?.(catKey);
|
|
9202
|
+
setInternalDisabled(
|
|
9203
|
+
(prev) => prev.includes(catKey) ? prev.filter((k) => k !== catKey) : [...prev, catKey]
|
|
9204
|
+
);
|
|
9205
|
+
};
|
|
9206
|
+
const sliceInputs = React52__namespace.useMemo(() => {
|
|
9207
|
+
return safeData.map((item, idx) => {
|
|
9208
|
+
const cat = String(item[category] ?? `Item ${idx + 1}`);
|
|
9209
|
+
const isDisabled = disabledCategories.includes(cat);
|
|
9210
|
+
const rawVal = item[value];
|
|
9211
|
+
const numVal = typeof rawVal === "number" && Number.isFinite(rawVal) ? rawVal : Number(rawVal) || 0;
|
|
9212
|
+
const rawColor = item.color ?? (colors ? colors[idx % colors.length] : DEFAULT_CHART_COLORS[idx % DEFAULT_CHART_COLORS.length]);
|
|
9213
|
+
return {
|
|
9214
|
+
label: cat,
|
|
9215
|
+
value: isDisabled ? 0 : Math.max(0, numVal),
|
|
9216
|
+
color: getChartColor(rawColor, idx),
|
|
9217
|
+
originalValue: numVal,
|
|
9218
|
+
originalIndex: idx
|
|
9219
|
+
};
|
|
9220
|
+
});
|
|
9221
|
+
}, [safeData, category, value, colors, disabledCategories]);
|
|
9222
|
+
const segments = React52__namespace.useMemo(() => {
|
|
9223
|
+
return calculateDonutSegments(sliceInputs, {
|
|
9224
|
+
cx,
|
|
9225
|
+
cy,
|
|
9226
|
+
innerRadius: computedInnerRadius,
|
|
9227
|
+
outerRadius,
|
|
9228
|
+
startAngle: -90
|
|
9229
|
+
});
|
|
9230
|
+
}, [sliceInputs, cx, cy, computedInnerRadius, outerRadius]);
|
|
9231
|
+
const computedAriaLabel = props["aria-label"] ?? ariaLabel ?? "Wykres pier\u015Bcieniowy";
|
|
9232
|
+
if (!data || data.length === 0) {
|
|
9233
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9234
|
+
ChartContainer,
|
|
9235
|
+
{
|
|
9236
|
+
ref: combinedRef,
|
|
9237
|
+
"aria-label": computedAriaLabel,
|
|
9238
|
+
className: cn("chart-donut-empty flex flex-col items-center justify-center", className),
|
|
9239
|
+
style,
|
|
9240
|
+
...props,
|
|
9241
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chart-empty-message flex min-h-[160px] w-full items-center justify-center text-sm text-muted-foreground select-none", children: emptyMessage })
|
|
9242
|
+
}
|
|
9243
|
+
);
|
|
9244
|
+
}
|
|
9245
|
+
const updateTooltipPosition = (segment, clientX, clientY) => {
|
|
9246
|
+
const midRad = segment.middleAngle * Math.PI / 180;
|
|
9247
|
+
const midRadius = (computedInnerRadius + outerRadius) / 2;
|
|
9248
|
+
const targetX = cx + midRadius * Math.cos(midRad);
|
|
9249
|
+
const targetY = cy + midRadius * Math.sin(midRad);
|
|
9250
|
+
if (containerRef.current) {
|
|
9251
|
+
const containerRect = containerRef.current.getBoundingClientRect();
|
|
9252
|
+
if (containerRect.width > 0 && containerRect.height > 0 && clientX !== void 0 && clientY !== void 0) {
|
|
9253
|
+
setTooltipPos({
|
|
9254
|
+
x: clientX - containerRect.left,
|
|
9255
|
+
y: clientY - containerRect.top
|
|
9256
|
+
});
|
|
9257
|
+
return;
|
|
9258
|
+
}
|
|
9259
|
+
}
|
|
9260
|
+
setTooltipPos({ x: targetX, y: targetY });
|
|
9261
|
+
};
|
|
9262
|
+
const handleSliceMouseEnter = (idx, segment, e) => {
|
|
9263
|
+
if (!showTooltip) return;
|
|
9264
|
+
setActiveIdx(idx);
|
|
9265
|
+
updateTooltipPosition(segment, e.clientX, e.clientY);
|
|
9266
|
+
};
|
|
9267
|
+
const handleSliceMouseMove = (_idx, segment, e) => {
|
|
9268
|
+
if (!showTooltip) return;
|
|
9269
|
+
updateTooltipPosition(segment, e.clientX, e.clientY);
|
|
9270
|
+
};
|
|
9271
|
+
const handleSliceMouseLeave = () => {
|
|
9272
|
+
setActiveIdx(null);
|
|
9273
|
+
setTooltipPos(null);
|
|
9274
|
+
};
|
|
9275
|
+
const handleSliceTouchStart = (idx, segment, e) => {
|
|
9276
|
+
if (!showTooltip) return;
|
|
9277
|
+
setActiveIdx(idx);
|
|
9278
|
+
if (e.touches.length > 0) {
|
|
9279
|
+
const touch = e.touches[0];
|
|
9280
|
+
updateTooltipPosition(segment, touch.clientX, touch.clientY);
|
|
9281
|
+
} else {
|
|
9282
|
+
updateTooltipPosition(segment);
|
|
9283
|
+
}
|
|
9284
|
+
};
|
|
9285
|
+
const handleSliceTouchEnd = () => {
|
|
9286
|
+
setActiveIdx(null);
|
|
9287
|
+
setTooltipPos(null);
|
|
9288
|
+
};
|
|
9289
|
+
const activeSegment = activeIdx !== null && segments[activeIdx] ? segments[activeIdx] : null;
|
|
9290
|
+
const shouldShowA11yTable = showA11yTable ?? Boolean(caption);
|
|
9291
|
+
const a11ySeries = [
|
|
9292
|
+
{ key: value, label: valueLabel },
|
|
9293
|
+
{ key: "__percentage__", label: "Udzia\u0142 (%)" }
|
|
9294
|
+
];
|
|
9295
|
+
const a11yData = data.map((row) => {
|
|
9296
|
+
const cat = String(row[category] ?? "");
|
|
9297
|
+
const seg = segments.find((s) => s.label === cat);
|
|
9298
|
+
return {
|
|
9299
|
+
...row,
|
|
9300
|
+
__percentage__: seg ? `${seg.percentage}%` : "0%"
|
|
9301
|
+
};
|
|
9302
|
+
});
|
|
9303
|
+
const sizeClass = typeof size === "number" ? void 0 : size === "sm" ? "max-w-[160px] max-h-[160px]" : size === "lg" ? "max-w-[280px] max-h-[280px]" : "max-w-[220px] max-h-[220px]";
|
|
9304
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9305
|
+
ChartContainer,
|
|
9306
|
+
{
|
|
9307
|
+
ref: combinedRef,
|
|
9308
|
+
width: dimension,
|
|
9309
|
+
height: dimension,
|
|
9310
|
+
viewBox: `0 0 ${dimension} ${dimension}`,
|
|
9311
|
+
"aria-label": computedAriaLabel,
|
|
9312
|
+
className: cn(
|
|
9313
|
+
"chart-donut-root relative flex flex-col items-center justify-start mx-auto w-full",
|
|
9314
|
+
className
|
|
9315
|
+
),
|
|
9316
|
+
svgClassName: cn("chart-donut-svg mx-auto shrink-0", sizeClass),
|
|
9317
|
+
style: {
|
|
9318
|
+
"--donut-dim": `${dimension}px`,
|
|
9319
|
+
...style
|
|
9320
|
+
},
|
|
9321
|
+
...props,
|
|
9322
|
+
children: [
|
|
9323
|
+
/* @__PURE__ */ jsxRuntime.jsx("g", { className: "chart-donut-slices", children: segments.map((segment, idx) => {
|
|
9324
|
+
const isHovered = activeIdx === idx;
|
|
9325
|
+
const isDimmed = activeIdx !== null && !isHovered;
|
|
9326
|
+
const origVal = segment.originalValue ?? segment.value;
|
|
9327
|
+
const displayVal = valueFormatter ? valueFormatter(origVal) : String(origVal);
|
|
9328
|
+
const hasPath = Boolean(segment.path);
|
|
9329
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9330
|
+
"path",
|
|
9331
|
+
{
|
|
9332
|
+
className: cn(
|
|
9333
|
+
"chart-donut-slice transition-all duration-150 cursor-pointer outline-none",
|
|
9334
|
+
sliceClassName
|
|
9335
|
+
),
|
|
9336
|
+
d: segment.path || "",
|
|
9337
|
+
fill: segment.color,
|
|
9338
|
+
opacity: !hasPath ? 0 : isDimmed ? 0.4 : 1,
|
|
9339
|
+
style: {
|
|
9340
|
+
transformOrigin: `${cx}px ${cy}px`,
|
|
9341
|
+
transform: isHovered && hasPath ? "scale(1.03)" : "scale(1)",
|
|
9342
|
+
filter: isHovered && hasPath ? "brightness(1.1)" : void 0,
|
|
9343
|
+
pointerEvents: hasPath ? void 0 : "none"
|
|
9344
|
+
},
|
|
9345
|
+
tabIndex: hasPath ? 0 : -1,
|
|
9346
|
+
role: "graphics-symbol",
|
|
9347
|
+
"aria-label": `${segment.label}: ${displayVal} (${segment.percentage}%)`,
|
|
9348
|
+
onMouseEnter: hasPath ? (e) => handleSliceMouseEnter(idx, segment, e) : void 0,
|
|
9349
|
+
onMouseMove: hasPath ? (e) => handleSliceMouseMove(idx, segment, e) : void 0,
|
|
9350
|
+
onMouseLeave: hasPath ? handleSliceMouseLeave : void 0,
|
|
9351
|
+
onTouchStart: hasPath ? (e) => handleSliceTouchStart(idx, segment, e) : void 0,
|
|
9352
|
+
onTouchEnd: hasPath ? handleSliceTouchEnd : void 0,
|
|
9353
|
+
onFocus: hasPath ? (e) => handleSliceMouseEnter(
|
|
9354
|
+
idx,
|
|
9355
|
+
segment,
|
|
9356
|
+
e
|
|
9357
|
+
) : void 0,
|
|
9358
|
+
onBlur: hasPath ? handleSliceMouseLeave : void 0
|
|
9359
|
+
},
|
|
9360
|
+
`slice-${idx}-${segment.label}`
|
|
9361
|
+
);
|
|
9362
|
+
}) }),
|
|
9363
|
+
centerLabel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9364
|
+
"div",
|
|
9365
|
+
{
|
|
9366
|
+
className: "chart-donut-center pointer-events-none absolute flex flex-col items-center justify-center text-center select-none",
|
|
9367
|
+
style: {
|
|
9368
|
+
width: `${Math.max(20, Math.floor(computedInnerRadius * 1.8))}px`,
|
|
9369
|
+
height: `${Math.max(20, Math.floor(computedInnerRadius * 1.8))}px`,
|
|
9370
|
+
left: "50%",
|
|
9371
|
+
top: `${dimension / 2}px`,
|
|
9372
|
+
transform: "translate(-50%, -50%)"
|
|
9373
|
+
},
|
|
9374
|
+
children: centerLabel
|
|
9375
|
+
}
|
|
9376
|
+
),
|
|
9377
|
+
showTooltip && activeSegment && tooltipPos && activeSegment.path && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9378
|
+
ChartTooltip,
|
|
9379
|
+
{
|
|
9380
|
+
open: Boolean(activeSegment),
|
|
9381
|
+
x: tooltipPos.x,
|
|
9382
|
+
y: tooltipPos.y,
|
|
9383
|
+
items: [
|
|
9384
|
+
{
|
|
9385
|
+
key: activeSegment.label,
|
|
9386
|
+
label: activeSegment.label,
|
|
9387
|
+
value: activeSegment.originalValue ?? activeSegment.value,
|
|
9388
|
+
formattedValue: `${valueFormatter ? valueFormatter(activeSegment.originalValue ?? activeSegment.value) : activeSegment.originalValue ?? activeSegment.value} (${activeSegment.percentage}%)`,
|
|
9389
|
+
color: activeSegment.color
|
|
9390
|
+
}
|
|
9391
|
+
]
|
|
9392
|
+
}
|
|
9393
|
+
),
|
|
9394
|
+
showLegend && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9395
|
+
ChartLegend,
|
|
9396
|
+
{
|
|
9397
|
+
series: legendSeries,
|
|
9398
|
+
disabledSeries: disabledCategories,
|
|
9399
|
+
onToggle: handleLegendToggle
|
|
9400
|
+
}
|
|
9401
|
+
),
|
|
9402
|
+
shouldShowA11yTable && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9403
|
+
ChartA11yTable,
|
|
9404
|
+
{
|
|
9405
|
+
caption: caption ?? computedAriaLabel,
|
|
9406
|
+
series: a11ySeries,
|
|
9407
|
+
data: a11yData,
|
|
9408
|
+
indexKey: category,
|
|
9409
|
+
valueFormatter: (v) => typeof v === "number" && valueFormatter ? valueFormatter(v) : String(v)
|
|
9410
|
+
}
|
|
9411
|
+
)
|
|
9412
|
+
]
|
|
9413
|
+
}
|
|
9414
|
+
);
|
|
9415
|
+
}
|
|
9416
|
+
);
|
|
9417
|
+
DonutChart.displayName = "DonutChart";
|
|
9418
|
+
|
|
7308
9419
|
exports.Accordion = Accordion;
|
|
7309
9420
|
exports.AccordionContent = AccordionContent;
|
|
7310
9421
|
exports.AccordionItem = AccordionItem;
|
|
@@ -7315,9 +9426,11 @@ exports.AppShellBanner = AppShellBanner;
|
|
|
7315
9426
|
exports.AppShellFooter = AppShellFooter;
|
|
7316
9427
|
exports.AppShellHeader = AppShellHeader;
|
|
7317
9428
|
exports.AppShellMain = AppShellMain;
|
|
9429
|
+
exports.AreaChart = AreaChart;
|
|
7318
9430
|
exports.Avatar = Avatar;
|
|
7319
9431
|
exports.AvatarGroup = AvatarGroup;
|
|
7320
9432
|
exports.Badge = Badge;
|
|
9433
|
+
exports.BarChart = BarChart;
|
|
7321
9434
|
exports.BlogCard = BlogCard;
|
|
7322
9435
|
exports.BottomNavigation = BottomNavigation;
|
|
7323
9436
|
exports.BottomSheet = BottomSheet;
|
|
@@ -7330,6 +9443,7 @@ exports.Breadcrumb = Breadcrumb;
|
|
|
7330
9443
|
exports.BreadcrumbItem = BreadcrumbItem;
|
|
7331
9444
|
exports.BreadcrumbSeparator = BreadcrumbSeparator;
|
|
7332
9445
|
exports.Button = Button;
|
|
9446
|
+
exports.CHART_COLOR_VARS = CHART_COLOR_VARS;
|
|
7333
9447
|
exports.Calendar = Calendar;
|
|
7334
9448
|
exports.Card = Card;
|
|
7335
9449
|
exports.CardContent = CardContent;
|
|
@@ -7337,6 +9451,13 @@ exports.CardDescription = CardDescription;
|
|
|
7337
9451
|
exports.CardFooter = CardFooter;
|
|
7338
9452
|
exports.CardHeader = CardHeader;
|
|
7339
9453
|
exports.CardTitle = CardTitle;
|
|
9454
|
+
exports.ChartA11yTable = ChartA11yTable;
|
|
9455
|
+
exports.ChartContainer = ChartContainer;
|
|
9456
|
+
exports.ChartGrid = ChartGrid;
|
|
9457
|
+
exports.ChartLegend = ChartLegend;
|
|
9458
|
+
exports.ChartTooltip = ChartTooltip;
|
|
9459
|
+
exports.ChartXAxis = ChartXAxis;
|
|
9460
|
+
exports.ChartYAxis = ChartYAxis;
|
|
7340
9461
|
exports.Checkbox = Checkbox;
|
|
7341
9462
|
exports.CheckboxField = CheckboxField;
|
|
7342
9463
|
exports.CodeBlock = CodeBlock;
|
|
@@ -7347,6 +9468,7 @@ exports.Combobox = Combobox;
|
|
|
7347
9468
|
exports.ComboboxField = ComboboxField;
|
|
7348
9469
|
exports.CommandPalette = CommandPalette;
|
|
7349
9470
|
exports.ConfirmDialog = ConfirmDialog;
|
|
9471
|
+
exports.DEFAULT_CHART_COLORS = DEFAULT_CHART_COLORS;
|
|
7350
9472
|
exports.DEFAULT_COLOR_SWATCHES = DEFAULT_COLOR_SWATCHES;
|
|
7351
9473
|
exports.DashboardShell = DashboardShell;
|
|
7352
9474
|
exports.DataTable = DataTable;
|
|
@@ -7355,6 +9477,7 @@ exports.DatePickerField = DatePickerField;
|
|
|
7355
9477
|
exports.DateRangePicker = DateRangePicker;
|
|
7356
9478
|
exports.DateRangePickerField = DateRangePickerField;
|
|
7357
9479
|
exports.DetailPageLayout = DetailPageLayout;
|
|
9480
|
+
exports.DonutChart = DonutChart;
|
|
7358
9481
|
exports.Drawer = Drawer;
|
|
7359
9482
|
exports.DropdownMenu = DropdownMenu;
|
|
7360
9483
|
exports.DropdownMenuContent = DropdownMenuContent;
|
|
@@ -7379,6 +9502,7 @@ exports.InboxTrigger = InboxTrigger;
|
|
|
7379
9502
|
exports.Input = Input;
|
|
7380
9503
|
exports.Kbd = Kbd;
|
|
7381
9504
|
exports.Label = Label;
|
|
9505
|
+
exports.LineChart = LineChart;
|
|
7382
9506
|
exports.MetricCard = MetricCard;
|
|
7383
9507
|
exports.Modal = Modal;
|
|
7384
9508
|
exports.ModalActions = ModalActions;
|
|
@@ -7409,6 +9533,7 @@ exports.SettingsLayout = SettingsLayout;
|
|
|
7409
9533
|
exports.SidebarNav = SidebarNav;
|
|
7410
9534
|
exports.Skeleton = Skeleton;
|
|
7411
9535
|
exports.Slider = Slider;
|
|
9536
|
+
exports.Sparkline = Sparkline;
|
|
7412
9537
|
exports.Spinner = Spinner;
|
|
7413
9538
|
exports.Stat = Stat;
|
|
7414
9539
|
exports.Stepper = Stepper;
|
|
@@ -7449,12 +9574,15 @@ exports.Tooltip = Tooltip;
|
|
|
7449
9574
|
exports.badgeVariants = badgeVariants;
|
|
7450
9575
|
exports.buttonVariants = buttonVariants;
|
|
7451
9576
|
exports.cn = cn;
|
|
9577
|
+
exports.fabVariants = fabVariants;
|
|
7452
9578
|
exports.formatRelativeTime = formatRelativeTime;
|
|
9579
|
+
exports.getChartColor = getChartColor;
|
|
7453
9580
|
exports.hexToHsl = hexToHsl;
|
|
7454
9581
|
exports.hslToHex = hslToHex;
|
|
7455
9582
|
exports.isValidHex = isValidHex;
|
|
7456
9583
|
exports.normalizeHex = normalizeHex;
|
|
7457
9584
|
exports.sliderThumbCSS = sliderThumbCSS;
|
|
9585
|
+
exports.useChartWidth = useChartWidth;
|
|
7458
9586
|
exports.useInPostScript = useInPostScript;
|
|
7459
9587
|
exports.useInboxState = useInboxState;
|
|
7460
9588
|
exports.useStepper = useStepper;
|