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