@bmsuisse/ui 0.11.0 → 0.12.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.d.ts +139 -5
- package/dist/index.js +588 -106
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -645,10 +645,15 @@ var sheetVariants = cva3(
|
|
|
645
645
|
{
|
|
646
646
|
variants: {
|
|
647
647
|
side: {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
648
|
+
// Every edge border below is paired with `border-border` explicitly —
|
|
649
|
+
// Tailwind v4's bare `border-*` utilities default to `currentColor`,
|
|
650
|
+
// not the theme's border token, which otherwise renders this seam in
|
|
651
|
+
// the foreground text color (a harsh, high-contrast line in both
|
|
652
|
+
// themes) instead of the intended subtle divider.
|
|
653
|
+
top: "inset-x-0 top-0 border-b border-border data-[state=closed]:-translate-y-full data-[state=open]:translate-y-0",
|
|
654
|
+
bottom: "inset-x-0 bottom-0 border-t border-border data-[state=closed]:translate-y-full data-[state=open]:translate-y-0",
|
|
655
|
+
left: "inset-y-0 left-0 h-full w-3/4 border-r border-border data-[state=closed]:-translate-x-full data-[state=open]:translate-x-0 sm:max-w-sm",
|
|
656
|
+
right: "inset-y-0 right-0 h-full w-3/4 border-l border-border data-[state=closed]:translate-x-full data-[state=open]:translate-x-0 sm:max-w-sm"
|
|
652
657
|
}
|
|
653
658
|
},
|
|
654
659
|
defaultVariants: {
|
|
@@ -929,7 +934,7 @@ var desktopSizeClasses = {
|
|
|
929
934
|
sm: "max-w-md",
|
|
930
935
|
md: "max-w-xl",
|
|
931
936
|
lg: "max-w-2xl",
|
|
932
|
-
xl: "max-w-
|
|
937
|
+
xl: "max-w-7xl"
|
|
933
938
|
};
|
|
934
939
|
var drawerSizeClasses = {
|
|
935
940
|
sm: "max-h-[50vh]",
|
|
@@ -1201,8 +1206,78 @@ function buildRenderChunks(visibleOptions) {
|
|
|
1201
1206
|
return chunks;
|
|
1202
1207
|
}
|
|
1203
1208
|
|
|
1204
|
-
// src/
|
|
1209
|
+
// src/lib/optionGrid.tsx
|
|
1205
1210
|
import { jsx as jsx25, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1211
|
+
function renderGridOptions({
|
|
1212
|
+
columns,
|
|
1213
|
+
options,
|
|
1214
|
+
activeIndex,
|
|
1215
|
+
multiple,
|
|
1216
|
+
isSelected,
|
|
1217
|
+
onSelect,
|
|
1218
|
+
onHover,
|
|
1219
|
+
testId
|
|
1220
|
+
}) {
|
|
1221
|
+
return /* @__PURE__ */ jsxs11(Table, { className: "text-sm", children: [
|
|
1222
|
+
/* @__PURE__ */ jsx25(TableHeader, { children: /* @__PURE__ */ jsxs11(TableRow, { className: "hover:bg-transparent", children: [
|
|
1223
|
+
multiple && /* @__PURE__ */ jsx25(TableHead, { className: "sticky top-0 z-10 w-6 bg-muted" }),
|
|
1224
|
+
columns.map((column) => /* @__PURE__ */ jsx25(
|
|
1225
|
+
TableHead,
|
|
1226
|
+
{
|
|
1227
|
+
className: cn("sticky top-0 z-10 whitespace-nowrap bg-muted text-xs", column.className),
|
|
1228
|
+
children: column.header
|
|
1229
|
+
},
|
|
1230
|
+
column.key
|
|
1231
|
+
))
|
|
1232
|
+
] }) }),
|
|
1233
|
+
/* @__PURE__ */ jsx25(TableBody, { children: options.map((option, index) => {
|
|
1234
|
+
const selected = isSelected(option);
|
|
1235
|
+
const active = index === activeIndex;
|
|
1236
|
+
return /* @__PURE__ */ jsxs11(
|
|
1237
|
+
TableRow,
|
|
1238
|
+
{
|
|
1239
|
+
role: "option",
|
|
1240
|
+
"aria-selected": selected,
|
|
1241
|
+
"aria-label": option.label,
|
|
1242
|
+
"aria-disabled": option.disabled,
|
|
1243
|
+
"data-testid": testId ? `${testId}-option` : void 0,
|
|
1244
|
+
"data-option-value": option.value,
|
|
1245
|
+
onMouseEnter: () => onHover(index),
|
|
1246
|
+
onClick: () => {
|
|
1247
|
+
if (!option.disabled) onSelect(option);
|
|
1248
|
+
},
|
|
1249
|
+
className: cn(
|
|
1250
|
+
"cursor-pointer outline-none",
|
|
1251
|
+
// `hover:bg-accent` repeats the active row's own background under
|
|
1252
|
+
// `:hover` -- otherwise TableRow's default `hover:bg-muted/50`
|
|
1253
|
+
// would win over the plain `bg-accent` below (later in the
|
|
1254
|
+
// stylesheet) and flash the active/keyboard-selected row to the
|
|
1255
|
+
// wrong color the instant the mouse sits over it.
|
|
1256
|
+
active && "bg-accent text-accent-foreground hover:bg-accent hover:text-accent-foreground",
|
|
1257
|
+
option.disabled && "pointer-events-none opacity-50"
|
|
1258
|
+
),
|
|
1259
|
+
children: [
|
|
1260
|
+
multiple && /* @__PURE__ */ jsx25(TableCell, { className: "w-6", children: /* @__PURE__ */ jsx25(
|
|
1261
|
+
"input",
|
|
1262
|
+
{
|
|
1263
|
+
type: "checkbox",
|
|
1264
|
+
checked: selected,
|
|
1265
|
+
readOnly: true,
|
|
1266
|
+
tabIndex: -1,
|
|
1267
|
+
className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
|
|
1268
|
+
}
|
|
1269
|
+
) }),
|
|
1270
|
+
columns.map((column) => /* @__PURE__ */ jsx25(TableCell, { className: cn("truncate", column.className), children: column.key === "label" ? option.label : option.data?.[column.key] }, column.key))
|
|
1271
|
+
]
|
|
1272
|
+
},
|
|
1273
|
+
option.value
|
|
1274
|
+
);
|
|
1275
|
+
}) })
|
|
1276
|
+
] });
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// src/patterns/combobox/Combobox.tsx
|
|
1280
|
+
import { jsx as jsx26, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1206
1281
|
function resolveGroupTriggerLabel(options, selectedValues, groupLabel) {
|
|
1207
1282
|
const selectedSet = new Set(selectedValues);
|
|
1208
1283
|
const groupsPresent = Array.from(
|
|
@@ -1237,7 +1312,8 @@ function Combobox(props) {
|
|
|
1237
1312
|
groupLabels,
|
|
1238
1313
|
container,
|
|
1239
1314
|
onSearchChange,
|
|
1240
|
-
selectedLabel
|
|
1315
|
+
selectedLabel,
|
|
1316
|
+
columns
|
|
1241
1317
|
} = props;
|
|
1242
1318
|
const testId = props["data-testid"];
|
|
1243
1319
|
const [open, setOpen] = useState6(false);
|
|
@@ -1301,11 +1377,11 @@ function Combobox(props) {
|
|
|
1301
1377
|
props.onChange(next);
|
|
1302
1378
|
}
|
|
1303
1379
|
const triggerLabel = loading ? loadingMessage : selectedOptions.length === 0 ? selectedValues.length > 0 ? selectedLabel ?? placeholder : placeholder : selectedOptions.length === 1 ? selectedOptions[0]?.label ?? placeholder : (props.multiple ? resolveGroupTriggerLabel(options, selectedValues, groupLabel) : null) ?? `${selectedOptions.length} selected`;
|
|
1304
|
-
const renderChunks = useMemo(() => buildRenderChunks(visibleOptions), [visibleOptions]);
|
|
1380
|
+
const renderChunks = useMemo(() => columns ? [] : buildRenderChunks(visibleOptions), [visibleOptions, columns]);
|
|
1305
1381
|
function renderOption({ option, index }) {
|
|
1306
1382
|
const isSelected = selectedValues.includes(option.value);
|
|
1307
1383
|
const isActive = index === activeIndex;
|
|
1308
|
-
return /* @__PURE__ */
|
|
1384
|
+
return /* @__PURE__ */ jsxs12(
|
|
1309
1385
|
"button",
|
|
1310
1386
|
{
|
|
1311
1387
|
type: "button",
|
|
@@ -1321,7 +1397,7 @@ function Combobox(props) {
|
|
|
1321
1397
|
isActive && "bg-accent text-accent-foreground"
|
|
1322
1398
|
),
|
|
1323
1399
|
children: [
|
|
1324
|
-
props.multiple ? /* @__PURE__ */
|
|
1400
|
+
props.multiple ? /* @__PURE__ */ jsx26(
|
|
1325
1401
|
"input",
|
|
1326
1402
|
{
|
|
1327
1403
|
type: "checkbox",
|
|
@@ -1330,15 +1406,15 @@ function Combobox(props) {
|
|
|
1330
1406
|
tabIndex: -1,
|
|
1331
1407
|
className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
|
|
1332
1408
|
}
|
|
1333
|
-
) : /* @__PURE__ */
|
|
1334
|
-
/* @__PURE__ */
|
|
1409
|
+
) : /* @__PURE__ */ jsx26(Check4, { className: cn("h-4 w-4 shrink-0", isSelected ? "opacity-100" : "opacity-0") }),
|
|
1410
|
+
/* @__PURE__ */ jsx26("span", { className: "truncate", children: option.label })
|
|
1335
1411
|
]
|
|
1336
1412
|
},
|
|
1337
1413
|
option.value
|
|
1338
1414
|
);
|
|
1339
1415
|
}
|
|
1340
|
-
return /* @__PURE__ */
|
|
1341
|
-
/* @__PURE__ */
|
|
1416
|
+
return /* @__PURE__ */ jsxs12(Popover, { open, onOpenChange: setOpen, children: [
|
|
1417
|
+
/* @__PURE__ */ jsx26(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs12(
|
|
1342
1418
|
Button,
|
|
1343
1419
|
{
|
|
1344
1420
|
id,
|
|
@@ -1354,9 +1430,9 @@ function Combobox(props) {
|
|
|
1354
1430
|
className
|
|
1355
1431
|
),
|
|
1356
1432
|
children: [
|
|
1357
|
-
/* @__PURE__ */
|
|
1358
|
-
/* @__PURE__ */
|
|
1359
|
-
clearable && !loading && selectedValues.length > 0 ? /* @__PURE__ */
|
|
1433
|
+
/* @__PURE__ */ jsx26("span", { className: "truncate", children: triggerLabel }),
|
|
1434
|
+
/* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-1", children: [
|
|
1435
|
+
clearable && !loading && selectedValues.length > 0 ? /* @__PURE__ */ jsx26(
|
|
1360
1436
|
"span",
|
|
1361
1437
|
{
|
|
1362
1438
|
role: "button",
|
|
@@ -1371,23 +1447,26 @@ function Combobox(props) {
|
|
|
1371
1447
|
props.onChange(null);
|
|
1372
1448
|
}
|
|
1373
1449
|
},
|
|
1374
|
-
children: /* @__PURE__ */
|
|
1450
|
+
children: /* @__PURE__ */ jsx26(X3, { className: "h-3.5 w-3.5" })
|
|
1375
1451
|
}
|
|
1376
1452
|
) : null,
|
|
1377
|
-
/* @__PURE__ */
|
|
1453
|
+
/* @__PURE__ */ jsx26(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50", "aria-hidden": "true" })
|
|
1378
1454
|
] })
|
|
1379
1455
|
]
|
|
1380
1456
|
}
|
|
1381
1457
|
) }),
|
|
1382
|
-
/* @__PURE__ */
|
|
1458
|
+
/* @__PURE__ */ jsx26(
|
|
1383
1459
|
PopoverContent,
|
|
1384
1460
|
{
|
|
1385
1461
|
align: "start",
|
|
1386
1462
|
container,
|
|
1387
|
-
className:
|
|
1463
|
+
className: cn(
|
|
1464
|
+
"w-[var(--radix-popover-trigger-width)] min-w-[10rem] p-2",
|
|
1465
|
+
columns && "w-max min-w-[var(--radix-popover-trigger-width)] max-w-[min(40rem,90vw)]"
|
|
1466
|
+
),
|
|
1388
1467
|
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
1389
|
-
children: /* @__PURE__ */
|
|
1390
|
-
/* @__PURE__ */
|
|
1468
|
+
children: /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-2", children: [
|
|
1469
|
+
/* @__PURE__ */ jsx26(
|
|
1391
1470
|
Input,
|
|
1392
1471
|
{
|
|
1393
1472
|
ref: inputRef,
|
|
@@ -1401,7 +1480,16 @@ function Combobox(props) {
|
|
|
1401
1480
|
"aria-label": searchPlaceholder
|
|
1402
1481
|
}
|
|
1403
1482
|
),
|
|
1404
|
-
/* @__PURE__ */
|
|
1483
|
+
/* @__PURE__ */ jsx26("div", { role: "listbox", className: cn("max-h-60 overflow-y-auto", !columns && "flex flex-col gap-0.5"), children: visibleOptions.length === 0 ? /* @__PURE__ */ jsx26("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : columns ? renderGridOptions({
|
|
1484
|
+
columns,
|
|
1485
|
+
options: visibleOptions,
|
|
1486
|
+
activeIndex,
|
|
1487
|
+
multiple: Boolean(props.multiple),
|
|
1488
|
+
isSelected: (option) => selectedValues.includes(option.value),
|
|
1489
|
+
onSelect: selectOption,
|
|
1490
|
+
onHover: setActiveIndex,
|
|
1491
|
+
testId
|
|
1492
|
+
}) : renderChunks.map((chunk) => {
|
|
1405
1493
|
if (chunk.kind === "single") return renderOption(chunk.row);
|
|
1406
1494
|
const headerState = groupCheckState(options, selectedValues, chunk.group);
|
|
1407
1495
|
return (
|
|
@@ -1412,14 +1500,14 @@ function Combobox(props) {
|
|
|
1412
1500
|
// lives on this wrapper's `pt-1`, not on the header itself -- a margin on
|
|
1413
1501
|
// the sticky element would travel with it once stuck, leaving a gap at the
|
|
1414
1502
|
// scrollport's top that the row behind it shows through.
|
|
1415
|
-
/* @__PURE__ */
|
|
1416
|
-
/* @__PURE__ */
|
|
1503
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-0.5 pt-1 first:pt-0", children: [
|
|
1504
|
+
/* @__PURE__ */ jsxs12(
|
|
1417
1505
|
"div",
|
|
1418
1506
|
{
|
|
1419
1507
|
"data-group-header": true,
|
|
1420
1508
|
className: "sticky top-0 z-10 flex items-center gap-2 rounded-sm bg-muted px-2 py-1.5 text-sm font-semibold text-foreground",
|
|
1421
1509
|
children: [
|
|
1422
|
-
props.multiple ? /* @__PURE__ */
|
|
1510
|
+
props.multiple ? /* @__PURE__ */ jsx26(
|
|
1423
1511
|
"input",
|
|
1424
1512
|
{
|
|
1425
1513
|
type: "checkbox",
|
|
@@ -1433,7 +1521,7 @@ function Combobox(props) {
|
|
|
1433
1521
|
"aria-label": `Select all of ${groupLabel(chunk.group)}`
|
|
1434
1522
|
}
|
|
1435
1523
|
) : null,
|
|
1436
|
-
/* @__PURE__ */
|
|
1524
|
+
/* @__PURE__ */ jsx26("span", { className: "truncate", children: groupLabel(chunk.group) })
|
|
1437
1525
|
]
|
|
1438
1526
|
}
|
|
1439
1527
|
),
|
|
@@ -1450,7 +1538,7 @@ function Combobox(props) {
|
|
|
1450
1538
|
// src/patterns/tag-combobox/TagCombobox.tsx
|
|
1451
1539
|
import { X as X4 } from "lucide-react";
|
|
1452
1540
|
import { useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState7 } from "react";
|
|
1453
|
-
import { jsx as
|
|
1541
|
+
import { jsx as jsx27, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1454
1542
|
function TagCombobox({
|
|
1455
1543
|
options,
|
|
1456
1544
|
value,
|
|
@@ -1465,6 +1553,7 @@ function TagCombobox({
|
|
|
1465
1553
|
onSearchChange,
|
|
1466
1554
|
container,
|
|
1467
1555
|
groupLabels,
|
|
1556
|
+
columns,
|
|
1468
1557
|
...rest
|
|
1469
1558
|
}) {
|
|
1470
1559
|
const testId = rest["data-testid"];
|
|
@@ -1486,7 +1575,7 @@ function TagCombobox({
|
|
|
1486
1575
|
useEffect4(() => {
|
|
1487
1576
|
setActiveIndex((i) => Math.min(Math.max(i, 0), Math.max(visibleOptions.length - 1, 0)));
|
|
1488
1577
|
}, [visibleOptions.length]);
|
|
1489
|
-
const renderChunks = useMemo2(() => buildRenderChunks(visibleOptions), [visibleOptions]);
|
|
1578
|
+
const renderChunks = useMemo2(() => columns ? [] : buildRenderChunks(visibleOptions), [visibleOptions, columns]);
|
|
1490
1579
|
useEffect4(() => {
|
|
1491
1580
|
if (!open) return;
|
|
1492
1581
|
setSearch("");
|
|
@@ -1540,7 +1629,7 @@ function TagCombobox({
|
|
|
1540
1629
|
function renderOption({ option, index }) {
|
|
1541
1630
|
const isSelected = value.includes(option.value);
|
|
1542
1631
|
const isActive = index === activeIndex;
|
|
1543
|
-
return /* @__PURE__ */
|
|
1632
|
+
return /* @__PURE__ */ jsxs13(
|
|
1544
1633
|
"button",
|
|
1545
1634
|
{
|
|
1546
1635
|
type: "button",
|
|
@@ -1556,7 +1645,7 @@ function TagCombobox({
|
|
|
1556
1645
|
isActive && "bg-accent text-accent-foreground"
|
|
1557
1646
|
),
|
|
1558
1647
|
children: [
|
|
1559
|
-
/* @__PURE__ */
|
|
1648
|
+
/* @__PURE__ */ jsx27(
|
|
1560
1649
|
"input",
|
|
1561
1650
|
{
|
|
1562
1651
|
type: "checkbox",
|
|
@@ -1566,14 +1655,14 @@ function TagCombobox({
|
|
|
1566
1655
|
className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
|
|
1567
1656
|
}
|
|
1568
1657
|
),
|
|
1569
|
-
/* @__PURE__ */
|
|
1658
|
+
/* @__PURE__ */ jsx27("span", { className: "truncate", children: option.label })
|
|
1570
1659
|
]
|
|
1571
1660
|
},
|
|
1572
1661
|
option.value
|
|
1573
1662
|
);
|
|
1574
1663
|
}
|
|
1575
|
-
return /* @__PURE__ */
|
|
1576
|
-
/* @__PURE__ */
|
|
1664
|
+
return /* @__PURE__ */ jsxs13(Popover, { open, onOpenChange: setOpen, children: [
|
|
1665
|
+
/* @__PURE__ */ jsx27(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs13(
|
|
1577
1666
|
"div",
|
|
1578
1667
|
{
|
|
1579
1668
|
ref: fieldRef,
|
|
@@ -1591,13 +1680,13 @@ function TagCombobox({
|
|
|
1591
1680
|
if (!disabled) inputRef.current?.focus();
|
|
1592
1681
|
},
|
|
1593
1682
|
children: [
|
|
1594
|
-
selectedOptions.map((option) => /* @__PURE__ */
|
|
1683
|
+
selectedOptions.map((option) => /* @__PURE__ */ jsxs13(
|
|
1595
1684
|
"span",
|
|
1596
1685
|
{
|
|
1597
1686
|
className: "inline-flex items-center gap-1 rounded-full bg-muted py-0.5 pr-1 pl-2.5 text-xs font-medium text-muted-foreground",
|
|
1598
1687
|
children: [
|
|
1599
|
-
/* @__PURE__ */
|
|
1600
|
-
!disabled && /* @__PURE__ */
|
|
1688
|
+
/* @__PURE__ */ jsx27("span", { className: "max-w-48 truncate", children: option.label }),
|
|
1689
|
+
!disabled && /* @__PURE__ */ jsx27(
|
|
1601
1690
|
"button",
|
|
1602
1691
|
{
|
|
1603
1692
|
type: "button",
|
|
@@ -1607,14 +1696,14 @@ function TagCombobox({
|
|
|
1607
1696
|
event.stopPropagation();
|
|
1608
1697
|
removeValue(option.value);
|
|
1609
1698
|
},
|
|
1610
|
-
children: /* @__PURE__ */
|
|
1699
|
+
children: /* @__PURE__ */ jsx27(X4, { className: "h-3 w-3" })
|
|
1611
1700
|
}
|
|
1612
1701
|
)
|
|
1613
1702
|
]
|
|
1614
1703
|
},
|
|
1615
1704
|
option.value
|
|
1616
1705
|
)),
|
|
1617
|
-
/* @__PURE__ */
|
|
1706
|
+
/* @__PURE__ */ jsx27(
|
|
1618
1707
|
"input",
|
|
1619
1708
|
{
|
|
1620
1709
|
ref: inputRef,
|
|
@@ -1632,18 +1721,30 @@ function TagCombobox({
|
|
|
1632
1721
|
]
|
|
1633
1722
|
}
|
|
1634
1723
|
) }),
|
|
1635
|
-
/* @__PURE__ */
|
|
1724
|
+
/* @__PURE__ */ jsx27(
|
|
1636
1725
|
PopoverContent,
|
|
1637
1726
|
{
|
|
1638
1727
|
align: "start",
|
|
1639
1728
|
container,
|
|
1640
|
-
className:
|
|
1729
|
+
className: cn(
|
|
1730
|
+
"w-[var(--radix-popover-trigger-width)] min-w-[10rem] p-2",
|
|
1731
|
+
columns && "w-max min-w-[var(--radix-popover-trigger-width)] max-w-[min(40rem,90vw)]"
|
|
1732
|
+
),
|
|
1641
1733
|
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
1642
1734
|
onCloseAutoFocus: (event) => event.preventDefault(),
|
|
1643
1735
|
onInteractOutside: (event) => {
|
|
1644
1736
|
if (fieldRef.current?.contains(event.target)) event.preventDefault();
|
|
1645
1737
|
},
|
|
1646
|
-
children: /* @__PURE__ */
|
|
1738
|
+
children: /* @__PURE__ */ jsx27("div", { role: "listbox", className: cn("max-h-60 overflow-y-auto", !columns && "flex flex-col gap-0.5"), children: loading ? /* @__PURE__ */ jsx27("p", { className: "py-2 text-center text-sm text-muted-foreground", children: loadingMessage }) : visibleOptions.length === 0 ? /* @__PURE__ */ jsx27("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : columns ? renderGridOptions({
|
|
1739
|
+
columns,
|
|
1740
|
+
options: visibleOptions,
|
|
1741
|
+
activeIndex,
|
|
1742
|
+
multiple: true,
|
|
1743
|
+
isSelected: (option) => value.includes(option.value),
|
|
1744
|
+
onSelect: toggleOption,
|
|
1745
|
+
onHover: setActiveIndex,
|
|
1746
|
+
testId
|
|
1747
|
+
}) : renderChunks.map((chunk) => {
|
|
1647
1748
|
if (chunk.kind === "single") return renderOption(chunk.row);
|
|
1648
1749
|
const headerState = groupCheckState(options, value, chunk.group);
|
|
1649
1750
|
return (
|
|
@@ -1653,14 +1754,14 @@ function TagCombobox({
|
|
|
1653
1754
|
// past its first row. Spacing above a group lives on this wrapper's
|
|
1654
1755
|
// `pt-1`/`first:pt-0`, not on the header itself -- see `Combobox`'s own
|
|
1655
1756
|
// identical structure for why both of these matter.
|
|
1656
|
-
/* @__PURE__ */
|
|
1657
|
-
/* @__PURE__ */
|
|
1757
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-0.5 pt-1 first:pt-0", children: [
|
|
1758
|
+
/* @__PURE__ */ jsxs13(
|
|
1658
1759
|
"div",
|
|
1659
1760
|
{
|
|
1660
1761
|
"data-group-header": true,
|
|
1661
1762
|
className: "sticky top-0 z-10 flex items-center gap-2 rounded-sm bg-muted px-2 py-1.5 text-sm font-semibold text-foreground",
|
|
1662
1763
|
children: [
|
|
1663
|
-
/* @__PURE__ */
|
|
1764
|
+
/* @__PURE__ */ jsx27(
|
|
1664
1765
|
"input",
|
|
1665
1766
|
{
|
|
1666
1767
|
type: "checkbox",
|
|
@@ -1674,7 +1775,7 @@ function TagCombobox({
|
|
|
1674
1775
|
"aria-label": `Select all of ${groupLabel(chunk.group)}`
|
|
1675
1776
|
}
|
|
1676
1777
|
),
|
|
1677
|
-
/* @__PURE__ */
|
|
1778
|
+
/* @__PURE__ */ jsx27("span", { className: "truncate", children: groupLabel(chunk.group) })
|
|
1678
1779
|
]
|
|
1679
1780
|
}
|
|
1680
1781
|
),
|
|
@@ -1689,37 +1790,37 @@ function TagCombobox({
|
|
|
1689
1790
|
|
|
1690
1791
|
// src/patterns/alert-box/AlertBox.tsx
|
|
1691
1792
|
import { AlertCircle, AlertTriangle, CheckCircle2, Info } from "lucide-react";
|
|
1692
|
-
import { jsx as
|
|
1793
|
+
import { jsx as jsx28, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1693
1794
|
var variantStyles = {
|
|
1694
|
-
error: "border-destructive bg-destructive/10 text-
|
|
1695
|
-
warning: "border-amber-500 bg-amber-500/10 text-amber-
|
|
1795
|
+
error: "border-destructive bg-destructive/10 text-red-800 dark:text-red-300",
|
|
1796
|
+
warning: "border-amber-500 bg-amber-500/10 text-amber-900 dark:text-amber-200",
|
|
1696
1797
|
info: "border-sky-500 bg-sky-500/10 text-sky-700 dark:text-sky-300",
|
|
1697
1798
|
success: "border-emerald-500 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"
|
|
1698
1799
|
};
|
|
1699
1800
|
var defaultIcons = {
|
|
1700
|
-
error: /* @__PURE__ */
|
|
1701
|
-
warning: /* @__PURE__ */
|
|
1702
|
-
info: /* @__PURE__ */
|
|
1703
|
-
success: /* @__PURE__ */
|
|
1801
|
+
error: /* @__PURE__ */ jsx28(AlertCircle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
1802
|
+
warning: /* @__PURE__ */ jsx28(AlertTriangle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
1803
|
+
info: /* @__PURE__ */ jsx28(Info, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
1804
|
+
success: /* @__PURE__ */ jsx28(CheckCircle2, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })
|
|
1704
1805
|
};
|
|
1705
1806
|
var AlertBox = ({ variant, title, children, className, icon }) => {
|
|
1706
1807
|
const resolvedIcon = icon === void 0 ? defaultIcons[variant] : icon;
|
|
1707
|
-
return /* @__PURE__ */
|
|
1708
|
-
resolvedIcon != null && /* @__PURE__ */
|
|
1709
|
-
/* @__PURE__ */
|
|
1710
|
-
title && /* @__PURE__ */
|
|
1711
|
-
/* @__PURE__ */
|
|
1808
|
+
return /* @__PURE__ */ jsx28("div", { className: cn("rounded-md border p-3 text-sm", variantStyles[variant], className), children: /* @__PURE__ */ jsxs14("div", { className: "flex flex-row items-start gap-2", children: [
|
|
1809
|
+
resolvedIcon != null && /* @__PURE__ */ jsx28("span", { className: "mt-0.5 shrink-0", children: resolvedIcon }),
|
|
1810
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1", children: [
|
|
1811
|
+
title && /* @__PURE__ */ jsx28("p", { className: "font-bold", children: title }),
|
|
1812
|
+
/* @__PURE__ */ jsx28("div", { children })
|
|
1712
1813
|
] })
|
|
1713
1814
|
] }) });
|
|
1714
1815
|
};
|
|
1715
1816
|
|
|
1716
1817
|
// src/patterns/status-badge/StatusBadge.tsx
|
|
1717
|
-
import { jsx as
|
|
1818
|
+
import { jsx as jsx29 } from "react/jsx-runtime";
|
|
1718
1819
|
var TONE_CLASSES = {
|
|
1719
1820
|
success: "border-transparent bg-emerald-500/15 text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-300",
|
|
1720
|
-
warning: "border-transparent bg-amber-500/15 text-amber-
|
|
1821
|
+
warning: "border-transparent bg-amber-500/15 text-amber-800 dark:bg-amber-500/20 dark:text-amber-300",
|
|
1721
1822
|
info: "border-transparent bg-sky-500/15 text-sky-700 dark:bg-sky-500/20 dark:text-sky-300",
|
|
1722
|
-
error: "border-transparent bg-destructive/15 text-
|
|
1823
|
+
error: "border-transparent bg-destructive/15 text-red-800 dark:text-red-300",
|
|
1723
1824
|
neutral: "border-transparent bg-muted text-muted-foreground"
|
|
1724
1825
|
};
|
|
1725
1826
|
var DEFAULT_TONE_MAP = {
|
|
@@ -1761,11 +1862,11 @@ function resolveTone(status, tone, toneMap) {
|
|
|
1761
1862
|
var StatusBadge = ({ status, label, tone, toneMap, className, ...props }) => {
|
|
1762
1863
|
const resolvedTone = resolveTone(status, tone, toneMap);
|
|
1763
1864
|
const displayLabel = label ?? deriveLabel(status);
|
|
1764
|
-
return /* @__PURE__ */
|
|
1865
|
+
return /* @__PURE__ */ jsx29(Badge, { variant: "outline", className: cn(TONE_CLASSES[resolvedTone], className), ...props, children: displayLabel });
|
|
1765
1866
|
};
|
|
1766
1867
|
|
|
1767
1868
|
// src/patterns/button-group/ButtonGroup.tsx
|
|
1768
|
-
import { jsx as
|
|
1869
|
+
import { jsx as jsx30, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1769
1870
|
function ButtonGroup({
|
|
1770
1871
|
options,
|
|
1771
1872
|
value,
|
|
@@ -1775,10 +1876,10 @@ function ButtonGroup({
|
|
|
1775
1876
|
className,
|
|
1776
1877
|
...props
|
|
1777
1878
|
}) {
|
|
1778
|
-
return /* @__PURE__ */
|
|
1879
|
+
return /* @__PURE__ */ jsx30("div", { role: "group", "aria-label": props["aria-label"], className: cn("inline-flex", className), "data-testid": props["data-testid"], children: options.map((option, index) => {
|
|
1779
1880
|
const selected = option.value === value;
|
|
1780
1881
|
const Icon2 = option.icon;
|
|
1781
|
-
return /* @__PURE__ */
|
|
1882
|
+
return /* @__PURE__ */ jsxs15(
|
|
1782
1883
|
Button,
|
|
1783
1884
|
{
|
|
1784
1885
|
type: "button",
|
|
@@ -1796,7 +1897,7 @@ function ButtonGroup({
|
|
|
1796
1897
|
index > 0 && "-ml-px"
|
|
1797
1898
|
),
|
|
1798
1899
|
children: [
|
|
1799
|
-
Icon2 && /* @__PURE__ */
|
|
1900
|
+
Icon2 && /* @__PURE__ */ jsx30(Icon2, { className: "h-4 w-4" }),
|
|
1800
1901
|
option.label
|
|
1801
1902
|
]
|
|
1802
1903
|
},
|
|
@@ -1808,7 +1909,7 @@ function ButtonGroup({
|
|
|
1808
1909
|
// src/patterns/loading-spinner/LoadingSpinner.tsx
|
|
1809
1910
|
import { cva as cva4 } from "class-variance-authority";
|
|
1810
1911
|
import { Loader2 } from "lucide-react";
|
|
1811
|
-
import { jsx as
|
|
1912
|
+
import { jsx as jsx31, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1812
1913
|
var loadingSpinnerIconVariants = cva4("animate-spin text-muted-foreground", {
|
|
1813
1914
|
variants: {
|
|
1814
1915
|
size: {
|
|
@@ -1826,27 +1927,27 @@ var LoadingSpinner = ({
|
|
|
1826
1927
|
label,
|
|
1827
1928
|
className,
|
|
1828
1929
|
...props
|
|
1829
|
-
}) => /* @__PURE__ */
|
|
1830
|
-
/* @__PURE__ */
|
|
1831
|
-
label ? /* @__PURE__ */
|
|
1930
|
+
}) => /* @__PURE__ */ jsxs16("span", { role: "status", className: cn("inline-flex items-center gap-2", className), ...props, children: [
|
|
1931
|
+
/* @__PURE__ */ jsx31(Loader2, { className: cn(loadingSpinnerIconVariants({ size })), "aria-hidden": "true" }),
|
|
1932
|
+
label ? /* @__PURE__ */ jsx31("span", { children: label }) : null
|
|
1832
1933
|
] });
|
|
1833
1934
|
var LoadingOverlay = ({
|
|
1834
1935
|
size = "lg",
|
|
1835
1936
|
label,
|
|
1836
1937
|
className,
|
|
1837
1938
|
...props
|
|
1838
|
-
}) => /* @__PURE__ */
|
|
1939
|
+
}) => /* @__PURE__ */ jsx31(
|
|
1839
1940
|
"div",
|
|
1840
1941
|
{
|
|
1841
1942
|
className: cn("flex h-full min-h-32 w-full items-center justify-center", className),
|
|
1842
1943
|
...props,
|
|
1843
|
-
children: /* @__PURE__ */
|
|
1944
|
+
children: /* @__PURE__ */ jsx31(LoadingSpinner, { size, label })
|
|
1844
1945
|
}
|
|
1845
1946
|
);
|
|
1846
1947
|
|
|
1847
1948
|
// src/patterns/search-bar/SearchBar.tsx
|
|
1848
1949
|
import { Search, X as X5 } from "lucide-react";
|
|
1849
|
-
import { jsx as
|
|
1950
|
+
import { jsx as jsx32, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1850
1951
|
function SearchBar({
|
|
1851
1952
|
value,
|
|
1852
1953
|
onChange,
|
|
@@ -1858,9 +1959,9 @@ function SearchBar({
|
|
|
1858
1959
|
...props
|
|
1859
1960
|
}) {
|
|
1860
1961
|
const showClear = onClear !== false && value.length > 0;
|
|
1861
|
-
return /* @__PURE__ */
|
|
1862
|
-
/* @__PURE__ */
|
|
1863
|
-
/* @__PURE__ */
|
|
1962
|
+
return /* @__PURE__ */ jsxs17("div", { className: cn("relative", className), children: [
|
|
1963
|
+
/* @__PURE__ */ jsx32("span", { className: "pointer-events-none absolute top-1/2 left-4 -translate-y-1/2 text-muted-foreground", children: isLoading ? /* @__PURE__ */ jsx32(LoadingSpinner, { size: "sm" }) : /* @__PURE__ */ jsx32(Search, { className: "h-4 w-4", "aria-hidden": "true" }) }),
|
|
1964
|
+
/* @__PURE__ */ jsx32(
|
|
1864
1965
|
"input",
|
|
1865
1966
|
{
|
|
1866
1967
|
ref: inputRef,
|
|
@@ -1879,19 +1980,370 @@ function SearchBar({
|
|
|
1879
1980
|
...props
|
|
1880
1981
|
}
|
|
1881
1982
|
),
|
|
1882
|
-
showClear && /* @__PURE__ */
|
|
1983
|
+
showClear && /* @__PURE__ */ jsx32(
|
|
1883
1984
|
"button",
|
|
1884
1985
|
{
|
|
1885
1986
|
type: "button",
|
|
1886
1987
|
onClick: () => onClear ? onClear() : onChange(""),
|
|
1887
1988
|
"aria-label": clearLabel,
|
|
1888
1989
|
className: "absolute top-1/2 right-2 flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded-full text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
1889
|
-
children: /* @__PURE__ */
|
|
1990
|
+
children: /* @__PURE__ */ jsx32(X5, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
1890
1991
|
}
|
|
1891
1992
|
)
|
|
1892
1993
|
] });
|
|
1893
1994
|
}
|
|
1894
1995
|
|
|
1996
|
+
// src/patterns/kpi-card/KpiCard.tsx
|
|
1997
|
+
import { memo } from "react";
|
|
1998
|
+
import { Fragment as Fragment2, jsx as jsx33, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1999
|
+
var DONUT_COLORS = [
|
|
2000
|
+
"var(--chart-1, #3b82f6)",
|
|
2001
|
+
"var(--chart-2, #22c55e)",
|
|
2002
|
+
"var(--chart-3, #f59e0b)",
|
|
2003
|
+
"var(--chart-4, #8b5cf6)",
|
|
2004
|
+
"var(--chart-5, #ef4444)",
|
|
2005
|
+
"var(--chart-6, #06b6d4)"
|
|
2006
|
+
];
|
|
2007
|
+
function DonutChart({
|
|
2008
|
+
data,
|
|
2009
|
+
size = 96,
|
|
2010
|
+
thickness = 14,
|
|
2011
|
+
centerValue,
|
|
2012
|
+
centerLabel
|
|
2013
|
+
}) {
|
|
2014
|
+
const total = data.reduce((sum, d) => sum + d.value, 0);
|
|
2015
|
+
if (total <= 0) return null;
|
|
2016
|
+
const radius = (size - thickness) / 2;
|
|
2017
|
+
const circumference = 2 * Math.PI * radius;
|
|
2018
|
+
let offset = 0;
|
|
2019
|
+
return /* @__PURE__ */ jsxs18("div", { className: "relative inline-flex shrink-0 items-center justify-center", style: { width: size, height: size }, children: [
|
|
2020
|
+
/* @__PURE__ */ jsxs18("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, className: "-rotate-90", children: [
|
|
2021
|
+
/* @__PURE__ */ jsx33("circle", { cx: size / 2, cy: size / 2, r: radius, fill: "none", stroke: "currentColor", className: "text-muted/40", strokeWidth: thickness }),
|
|
2022
|
+
data.map((segment, i) => {
|
|
2023
|
+
const fraction = segment.value / total;
|
|
2024
|
+
const dash = fraction * circumference;
|
|
2025
|
+
const el = /* @__PURE__ */ jsx33(
|
|
2026
|
+
"circle",
|
|
2027
|
+
{
|
|
2028
|
+
cx: size / 2,
|
|
2029
|
+
cy: size / 2,
|
|
2030
|
+
r: radius,
|
|
2031
|
+
fill: "none",
|
|
2032
|
+
stroke: segment.color ?? DONUT_COLORS[i % DONUT_COLORS.length],
|
|
2033
|
+
strokeWidth: thickness,
|
|
2034
|
+
strokeDasharray: `${dash} ${circumference - dash}`,
|
|
2035
|
+
strokeDashoffset: -offset,
|
|
2036
|
+
strokeLinecap: data.length > 1 ? "butt" : "round"
|
|
2037
|
+
},
|
|
2038
|
+
segment.label
|
|
2039
|
+
);
|
|
2040
|
+
offset += dash;
|
|
2041
|
+
return el;
|
|
2042
|
+
})
|
|
2043
|
+
] }),
|
|
2044
|
+
(centerValue || centerLabel) && /* @__PURE__ */ jsxs18("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: [
|
|
2045
|
+
centerValue && /* @__PURE__ */ jsx33("span", { className: "text-base font-black tracking-tight text-foreground tabular-nums", children: centerValue }),
|
|
2046
|
+
centerLabel && /* @__PURE__ */ jsx33("span", { className: "text-[9px] font-medium text-muted-foreground uppercase", children: centerLabel })
|
|
2047
|
+
] })
|
|
2048
|
+
] });
|
|
2049
|
+
}
|
|
2050
|
+
function Sparkline({ data, color }) {
|
|
2051
|
+
if (data.length < 2) return null;
|
|
2052
|
+
const min = Math.min(...data);
|
|
2053
|
+
const max = Math.max(...data);
|
|
2054
|
+
const range = max - min || 1;
|
|
2055
|
+
const W = 72;
|
|
2056
|
+
const H = 28;
|
|
2057
|
+
const pts = data.map((v, i) => `${i / (data.length - 1) * W},${H - (v - min) / range * (H - 2) - 1}`).join(" ");
|
|
2058
|
+
return /* @__PURE__ */ jsx33("svg", { width: W, height: H, viewBox: `0 0 ${W} ${H}`, className: "overflow-visible opacity-70", children: /* @__PURE__ */ jsx33("polyline", { points: pts, fill: "none", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
2059
|
+
}
|
|
2060
|
+
function AreaSparkline({ data, color }) {
|
|
2061
|
+
if (data.length < 2) return null;
|
|
2062
|
+
const min = Math.min(...data);
|
|
2063
|
+
const max = Math.max(...data);
|
|
2064
|
+
const range = max - min || 1;
|
|
2065
|
+
const W = 200;
|
|
2066
|
+
const H = 28;
|
|
2067
|
+
const P = 2;
|
|
2068
|
+
const xs = data.map((_, i) => i / (data.length - 1) * W);
|
|
2069
|
+
const ys = data.map((v) => H - (v - min) / range * (H - 2 * P) - P);
|
|
2070
|
+
const linePts = xs.map((x, i) => `${x},${ys[i]}`).join(" ");
|
|
2071
|
+
const areaPath = `M${xs[0]},${H} ` + xs.map((x, i) => `L${x},${ys[i]}`).join(" ") + ` L${xs[xs.length - 1]},${H} Z`;
|
|
2072
|
+
return /* @__PURE__ */ jsxs18("svg", { width: W, height: H, viewBox: `0 0 ${W} ${H}`, className: "overflow-visible", preserveAspectRatio: "none", children: [
|
|
2073
|
+
/* @__PURE__ */ jsx33("defs", { children: /* @__PURE__ */ jsxs18("linearGradient", { id: "kpi-card-spark-fill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
2074
|
+
/* @__PURE__ */ jsx33("stop", { offset: "0%", stopColor: color, stopOpacity: "0.22" }),
|
|
2075
|
+
/* @__PURE__ */ jsx33("stop", { offset: "100%", stopColor: color, stopOpacity: "0" })
|
|
2076
|
+
] }) }),
|
|
2077
|
+
/* @__PURE__ */ jsx33("path", { d: areaPath, fill: "url(#kpi-card-spark-fill)" }),
|
|
2078
|
+
/* @__PURE__ */ jsx33("polyline", { points: linePts, fill: "none", stroke: color, strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
2079
|
+
/* @__PURE__ */ jsx33("circle", { cx: xs[xs.length - 1], cy: ys[ys.length - 1], r: "2.5", fill: color })
|
|
2080
|
+
] });
|
|
2081
|
+
}
|
|
2082
|
+
var rowBetween = "flex items-center justify-between";
|
|
2083
|
+
var KpiCard = memo(function KpiCard2({
|
|
2084
|
+
label,
|
|
2085
|
+
value,
|
|
2086
|
+
icon: Icon2,
|
|
2087
|
+
badge,
|
|
2088
|
+
badgeLabel,
|
|
2089
|
+
variant = "default",
|
|
2090
|
+
loading = false,
|
|
2091
|
+
sub,
|
|
2092
|
+
subTone = "default",
|
|
2093
|
+
href,
|
|
2094
|
+
sparkline,
|
|
2095
|
+
sparklineColor = "var(--color-primary, #3b82f6)",
|
|
2096
|
+
progress,
|
|
2097
|
+
progressLabel,
|
|
2098
|
+
segments,
|
|
2099
|
+
centerValue,
|
|
2100
|
+
testId
|
|
2101
|
+
}) {
|
|
2102
|
+
if (variant === "hero") {
|
|
2103
|
+
return /* @__PURE__ */ jsxs18(
|
|
2104
|
+
"div",
|
|
2105
|
+
{
|
|
2106
|
+
"data-testid": testId,
|
|
2107
|
+
className: "relative col-span-2 flex min-w-0 flex-1 flex-col gap-1.5 overflow-hidden rounded-2xl p-4 md:p-3 lg:col-span-1 lg:max-w-[340px] lg:min-w-[260px]",
|
|
2108
|
+
style: {
|
|
2109
|
+
background: "var(--color-primary)",
|
|
2110
|
+
boxShadow: "0 2px 10px color-mix(in oklch, var(--color-primary) 18%, transparent)"
|
|
2111
|
+
},
|
|
2112
|
+
children: [
|
|
2113
|
+
/* @__PURE__ */ jsxs18("div", { className: "relative flex items-start justify-between", children: [
|
|
2114
|
+
/* @__PURE__ */ jsx33("p", { className: "text-[11px] font-bold tracking-[0.14em] text-white/70 uppercase", children: label }),
|
|
2115
|
+
Icon2 && /* @__PURE__ */ jsx33("div", { className: "flex h-6 w-6 items-center justify-center rounded-lg bg-white/15 ring-1 ring-white/20", children: /* @__PURE__ */ jsx33(Icon2, { className: "h-3 w-3 text-white/90", "aria-hidden": "true" }) })
|
|
2116
|
+
] }),
|
|
2117
|
+
/* @__PURE__ */ jsx33("div", { className: "relative", children: loading ? /* @__PURE__ */ jsx33("div", { className: "h-6 w-28 animate-pulse rounded-lg bg-white/15" }) : /* @__PURE__ */ jsx33("p", { className: "text-2xl leading-none font-black tracking-tight text-white tabular-nums md:text-[22px]", children: value ?? "\u2014" }) }),
|
|
2118
|
+
(badge || badgeLabel) && !loading && /* @__PURE__ */ jsxs18("div", { className: "relative flex items-center gap-2", children: [
|
|
2119
|
+
badge && /* @__PURE__ */ jsx33(
|
|
2120
|
+
"span",
|
|
2121
|
+
{
|
|
2122
|
+
className: cn(
|
|
2123
|
+
"inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-[11px] font-bold tabular-nums ring-1",
|
|
2124
|
+
badge.positive ? "bg-white/15 text-white ring-white/25" : "bg-black/25 text-white ring-white/15"
|
|
2125
|
+
),
|
|
2126
|
+
children: badge.text
|
|
2127
|
+
}
|
|
2128
|
+
),
|
|
2129
|
+
badgeLabel && /* @__PURE__ */ jsx33("span", { className: "text-[11px] text-white/70", children: badgeLabel })
|
|
2130
|
+
] }),
|
|
2131
|
+
sub && !loading && /* @__PURE__ */ jsx33("p", { className: "relative -mt-1 text-[11px] font-medium text-white/75 tabular-nums", children: sub }),
|
|
2132
|
+
progress != null && !loading && /* @__PURE__ */ jsxs18("div", { className: "relative flex items-center gap-1.5", children: [
|
|
2133
|
+
/* @__PURE__ */ jsx33("div", { className: "h-1 flex-1 overflow-hidden rounded-full bg-white/20", children: /* @__PURE__ */ jsx33("div", { className: "h-1 rounded-full bg-white/85", style: { width: `${Math.min(100, Math.max(0, progress))}%` } }) }),
|
|
2134
|
+
progressLabel && /* @__PURE__ */ jsx33("span", { className: "shrink-0 text-[11px] font-semibold text-white/80 tabular-nums", children: progressLabel })
|
|
2135
|
+
] }),
|
|
2136
|
+
sparkline && sparkline.length > 1 && !loading && /* @__PURE__ */ jsx33("div", { className: "relative -mx-4 mt-auto -mb-4 md:-mx-3 md:-mb-3", children: /* @__PURE__ */ jsx33(AreaSparkline, { data: sparkline, color: "#ffffff" }) })
|
|
2137
|
+
]
|
|
2138
|
+
}
|
|
2139
|
+
);
|
|
2140
|
+
}
|
|
2141
|
+
if (variant === "mini") {
|
|
2142
|
+
const iconSlot = Icon2 ? href ? /* @__PURE__ */ jsx33(
|
|
2143
|
+
"a",
|
|
2144
|
+
{
|
|
2145
|
+
href,
|
|
2146
|
+
className: "tap-target flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary transition-[background-color,scale] hover:bg-primary/20 active:scale-90 motion-reduce:transition-none md:h-6 md:w-6",
|
|
2147
|
+
children: /* @__PURE__ */ jsx33(Icon2, { className: "h-4 w-4 md:h-3.5 md:w-3.5", "aria-hidden": "true" })
|
|
2148
|
+
}
|
|
2149
|
+
) : /* @__PURE__ */ jsx33("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary md:h-6 md:w-6", children: /* @__PURE__ */ jsx33(Icon2, { className: "h-4 w-4 md:h-3.5 md:w-3.5", "aria-hidden": "true" }) }) : null;
|
|
2150
|
+
return /* @__PURE__ */ jsxs18(
|
|
2151
|
+
"div",
|
|
2152
|
+
{
|
|
2153
|
+
"data-testid": testId,
|
|
2154
|
+
className: cn(
|
|
2155
|
+
"flex h-full min-w-0 flex-1 flex-col gap-1.5 overflow-hidden rounded-2xl border border-border bg-card p-3 transition-shadow md:p-2.5",
|
|
2156
|
+
href && "hover:border-primary/20 hover:shadow-md"
|
|
2157
|
+
),
|
|
2158
|
+
children: [
|
|
2159
|
+
/* @__PURE__ */ jsxs18("div", { className: rowBetween, children: [
|
|
2160
|
+
/* @__PURE__ */ jsx33("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
|
|
2161
|
+
iconSlot
|
|
2162
|
+
] }),
|
|
2163
|
+
/* @__PURE__ */ jsx33("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: loading ? /* @__PURE__ */ jsx33(Skeleton, { className: "h-6 w-16" }) : /* @__PURE__ */ jsxs18(Fragment2, { children: [
|
|
2164
|
+
/* @__PURE__ */ jsx33("p", { className: "min-w-0 animate-in truncate text-[20px] leading-none font-black tracking-tight text-foreground duration-200 fade-in-0 tabular-nums motion-reduce:animate-none md:text-[16px]", children: value ?? "\u2014" }),
|
|
2165
|
+
badge && /* @__PURE__ */ jsx33(
|
|
2166
|
+
"span",
|
|
2167
|
+
{
|
|
2168
|
+
className: cn(
|
|
2169
|
+
"shrink-0 animate-in rounded-md px-1.5 py-0.5 text-[11px] font-bold duration-200 fade-in-0 tabular-nums motion-reduce:animate-none",
|
|
2170
|
+
badge.positive ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400" : "bg-muted text-muted-foreground"
|
|
2171
|
+
),
|
|
2172
|
+
children: badge.text
|
|
2173
|
+
}
|
|
2174
|
+
)
|
|
2175
|
+
] }) }),
|
|
2176
|
+
(sub || sparkline) && !loading && /* @__PURE__ */ jsxs18("div", { className: "flex items-end justify-between gap-2", children: [
|
|
2177
|
+
sub && /* @__PURE__ */ jsx33(
|
|
2178
|
+
"p",
|
|
2179
|
+
{
|
|
2180
|
+
className: cn(
|
|
2181
|
+
"text-[11px]",
|
|
2182
|
+
subTone === "danger" ? "font-semibold text-red-600 dark:text-red-400" : subTone === "warn" ? "font-semibold text-amber-700 dark:text-amber-300" : "text-muted-foreground"
|
|
2183
|
+
),
|
|
2184
|
+
children: sub
|
|
2185
|
+
}
|
|
2186
|
+
),
|
|
2187
|
+
sparkline && /* @__PURE__ */ jsx33(Sparkline, { data: sparkline, color: sparklineColor })
|
|
2188
|
+
] })
|
|
2189
|
+
]
|
|
2190
|
+
}
|
|
2191
|
+
);
|
|
2192
|
+
}
|
|
2193
|
+
if (variant === "donut") {
|
|
2194
|
+
const total = segments?.reduce((sum, s) => sum + s.value, 0) ?? 0;
|
|
2195
|
+
return /* @__PURE__ */ jsxs18("div", { "data-testid": testId, className: "rounded-xl border border-border bg-card p-5", children: [
|
|
2196
|
+
/* @__PURE__ */ jsxs18("div", { className: rowBetween, children: [
|
|
2197
|
+
/* @__PURE__ */ jsx33("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
|
|
2198
|
+
Icon2 && /* @__PURE__ */ jsx33("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/8 text-primary", children: /* @__PURE__ */ jsx33(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })
|
|
2199
|
+
] }),
|
|
2200
|
+
loading ? /* @__PURE__ */ jsxs18("div", { className: "mt-3 flex items-center gap-4", children: [
|
|
2201
|
+
/* @__PURE__ */ jsx33(Skeleton, { className: "h-24 w-24 shrink-0 rounded-full" }),
|
|
2202
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex flex-1 flex-col gap-2", children: [
|
|
2203
|
+
/* @__PURE__ */ jsx33(Skeleton, { className: "h-3 w-full" }),
|
|
2204
|
+
/* @__PURE__ */ jsx33(Skeleton, { className: "h-3 w-3/4" }),
|
|
2205
|
+
/* @__PURE__ */ jsx33(Skeleton, { className: "h-3 w-1/2" })
|
|
2206
|
+
] })
|
|
2207
|
+
] }) : segments && segments.length > 0 ? /* @__PURE__ */ jsxs18("div", { className: "mt-3 flex items-center gap-4", children: [
|
|
2208
|
+
/* @__PURE__ */ jsx33(DonutChart, { data: segments, centerValue: centerValue ?? String(total) }),
|
|
2209
|
+
/* @__PURE__ */ jsx33("ul", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: segments.map((segment, i) => /* @__PURE__ */ jsxs18("li", { className: "flex items-center gap-2 text-[12px]", children: [
|
|
2210
|
+
/* @__PURE__ */ jsx33(
|
|
2211
|
+
"span",
|
|
2212
|
+
{
|
|
2213
|
+
className: "h-2 w-2 shrink-0 rounded-full",
|
|
2214
|
+
style: { backgroundColor: segment.color ?? DONUT_COLORS[i % DONUT_COLORS.length] },
|
|
2215
|
+
"aria-hidden": "true"
|
|
2216
|
+
}
|
|
2217
|
+
),
|
|
2218
|
+
/* @__PURE__ */ jsx33("span", { className: "min-w-0 flex-1 truncate text-muted-foreground", children: segment.label }),
|
|
2219
|
+
/* @__PURE__ */ jsx33("span", { className: "shrink-0 font-semibold text-foreground tabular-nums", children: total > 0 ? `${Math.round(segment.value / total * 100)}%` : segment.value })
|
|
2220
|
+
] }, segment.label)) })
|
|
2221
|
+
] }) : /* @__PURE__ */ jsx33("span", { className: "mt-3 block text-2xl font-black text-muted-foreground/40", children: "\u2014" }),
|
|
2222
|
+
sub && !loading && /* @__PURE__ */ jsx33("p", { className: "mt-2.5 text-[11px] text-muted-foreground tabular-nums", children: sub })
|
|
2223
|
+
] });
|
|
2224
|
+
}
|
|
2225
|
+
const strValue = value != null ? String(value) : null;
|
|
2226
|
+
const parts = strValue ? strValue.split(/\s(.+)/) : null;
|
|
2227
|
+
const plain = !parts || parts.length === 1;
|
|
2228
|
+
const isSuffix = !plain && /^\d/.test(parts[0]) && parts[1].length <= 3;
|
|
2229
|
+
const prefix = isSuffix ? null : parts?.[0];
|
|
2230
|
+
const number = isSuffix ? parts[0] : parts?.[1] ?? parts?.[0];
|
|
2231
|
+
const suffix = isSuffix ? parts[1] : null;
|
|
2232
|
+
return /* @__PURE__ */ jsxs18("div", { "data-testid": testId, className: "rounded-xl border border-border bg-card p-5", children: [
|
|
2233
|
+
/* @__PURE__ */ jsxs18("div", { className: rowBetween, children: [
|
|
2234
|
+
/* @__PURE__ */ jsx33("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
|
|
2235
|
+
Icon2 && /* @__PURE__ */ jsx33("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/8 text-primary", children: /* @__PURE__ */ jsx33(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })
|
|
2236
|
+
] }),
|
|
2237
|
+
/* @__PURE__ */ jsxs18("div", { className: "mt-3", children: [
|
|
2238
|
+
loading ? /* @__PURE__ */ jsx33(Skeleton, { className: "h-7 w-20" }) : value == null ? /* @__PURE__ */ jsx33("span", { className: "text-2xl font-black text-muted-foreground/40", children: "\u2014" }) : plain ? /* @__PURE__ */ jsx33("span", { className: "animate-in text-2xl font-black tracking-tight text-foreground duration-200 fade-in-0 tabular-nums motion-reduce:animate-none", children: strValue }) : /* @__PURE__ */ jsxs18("span", { className: "animate-in font-black tracking-tight text-foreground duration-200 fade-in-0 tabular-nums motion-reduce:animate-none", children: [
|
|
2239
|
+
prefix && /* @__PURE__ */ jsxs18("span", { className: "text-sm text-muted-foreground", children: [
|
|
2240
|
+
prefix,
|
|
2241
|
+
" "
|
|
2242
|
+
] }),
|
|
2243
|
+
/* @__PURE__ */ jsx33("span", { className: "text-2xl", children: number }),
|
|
2244
|
+
suffix && /* @__PURE__ */ jsxs18("span", { className: "text-sm text-muted-foreground", children: [
|
|
2245
|
+
" ",
|
|
2246
|
+
suffix
|
|
2247
|
+
] })
|
|
2248
|
+
] }),
|
|
2249
|
+
sub && !loading && /* @__PURE__ */ jsx33("p", { className: "mt-0.5 text-[11px] text-muted-foreground tabular-nums", children: sub })
|
|
2250
|
+
] }),
|
|
2251
|
+
(badge || badgeLabel || sparkline) && !loading && /* @__PURE__ */ jsxs18("div", { className: "mt-2.5 flex items-end justify-between gap-2", children: [
|
|
2252
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
2253
|
+
badge && /* @__PURE__ */ jsx33(
|
|
2254
|
+
"span",
|
|
2255
|
+
{
|
|
2256
|
+
className: cn(
|
|
2257
|
+
"rounded-md px-1.5 py-0.5 text-[11px] font-semibold tabular-nums",
|
|
2258
|
+
badge.positive ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400" : "bg-muted text-muted-foreground"
|
|
2259
|
+
),
|
|
2260
|
+
children: badge.text
|
|
2261
|
+
}
|
|
2262
|
+
),
|
|
2263
|
+
badgeLabel && /* @__PURE__ */ jsx33("span", { className: "text-[11px] text-muted-foreground", children: badgeLabel })
|
|
2264
|
+
] }),
|
|
2265
|
+
sparkline && /* @__PURE__ */ jsx33(Sparkline, { data: sparkline, color: sparklineColor })
|
|
2266
|
+
] })
|
|
2267
|
+
] });
|
|
2268
|
+
});
|
|
2269
|
+
|
|
2270
|
+
// src/patterns/search-panel/SearchPanel.tsx
|
|
2271
|
+
import { Search as Search2 } from "lucide-react";
|
|
2272
|
+
import { jsx as jsx34, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2273
|
+
function SearchPanel({
|
|
2274
|
+
value,
|
|
2275
|
+
onChange,
|
|
2276
|
+
isLoading,
|
|
2277
|
+
trailingSlot,
|
|
2278
|
+
shortcutHint,
|
|
2279
|
+
modes,
|
|
2280
|
+
activeMode,
|
|
2281
|
+
onModeChange,
|
|
2282
|
+
expanded = false,
|
|
2283
|
+
inputRef,
|
|
2284
|
+
className,
|
|
2285
|
+
...props
|
|
2286
|
+
}) {
|
|
2287
|
+
return /* @__PURE__ */ jsxs19(
|
|
2288
|
+
"div",
|
|
2289
|
+
{
|
|
2290
|
+
className: cn(
|
|
2291
|
+
"flex flex-col border border-border bg-card shadow-sm",
|
|
2292
|
+
expanded ? "rounded-t-2xl border-b-transparent" : "rounded-2xl",
|
|
2293
|
+
className
|
|
2294
|
+
),
|
|
2295
|
+
children: [
|
|
2296
|
+
/* @__PURE__ */ jsxs19("div", { className: "flex h-14 items-center gap-3 px-4", children: [
|
|
2297
|
+
isLoading ? /* @__PURE__ */ jsx34(LoadingSpinner, { size: "sm" }) : /* @__PURE__ */ jsx34(Search2, { className: "h-5 w-5 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
|
|
2298
|
+
/* @__PURE__ */ jsx34(
|
|
2299
|
+
"input",
|
|
2300
|
+
{
|
|
2301
|
+
ref: inputRef,
|
|
2302
|
+
type: "text",
|
|
2303
|
+
value,
|
|
2304
|
+
onChange: (event) => onChange(event.target.value),
|
|
2305
|
+
className: "min-w-0 flex-1 bg-transparent text-[15px] text-foreground outline-none placeholder:text-muted-foreground",
|
|
2306
|
+
...props
|
|
2307
|
+
}
|
|
2308
|
+
),
|
|
2309
|
+
trailingSlot,
|
|
2310
|
+
shortcutHint && /* @__PURE__ */ jsx34(
|
|
2311
|
+
"kbd",
|
|
2312
|
+
{
|
|
2313
|
+
"aria-hidden": "true",
|
|
2314
|
+
className: "hidden shrink-0 items-center rounded border border-border bg-muted/60 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground select-none sm:inline-flex",
|
|
2315
|
+
children: shortcutHint
|
|
2316
|
+
}
|
|
2317
|
+
)
|
|
2318
|
+
] }),
|
|
2319
|
+
modes && modes.length > 0 && /* @__PURE__ */ jsx34("div", { className: "flex items-center gap-2 px-4 pb-3", children: modes.map(({ key, label, icon: Icon2 }) => /* @__PURE__ */ jsxs19(
|
|
2320
|
+
"button",
|
|
2321
|
+
{
|
|
2322
|
+
type: "button",
|
|
2323
|
+
onClick: () => onModeChange?.(key),
|
|
2324
|
+
className: cn(
|
|
2325
|
+
"flex items-center gap-1.5 rounded-full px-3 py-1.5 text-[13px] font-medium transition-colors",
|
|
2326
|
+
activeMode === key ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"
|
|
2327
|
+
),
|
|
2328
|
+
children: [
|
|
2329
|
+
Icon2 && /* @__PURE__ */ jsx34(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
2330
|
+
label
|
|
2331
|
+
]
|
|
2332
|
+
},
|
|
2333
|
+
key
|
|
2334
|
+
)) })
|
|
2335
|
+
]
|
|
2336
|
+
}
|
|
2337
|
+
);
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
// src/patterns/search-panel/SearchTrigger.tsx
|
|
2341
|
+
import { Search as Search3 } from "lucide-react";
|
|
2342
|
+
import { jsx as jsx35 } from "react/jsx-runtime";
|
|
2343
|
+
function SearchTrigger({ label = "Search", ...props }) {
|
|
2344
|
+
return /* @__PURE__ */ jsx35(Button, { type: "button", variant: "ghost", size: "icon", "aria-label": label, ...props, children: /* @__PURE__ */ jsx35(Search3, { className: "h-[18px] w-[18px]", "aria-hidden": "true" }) });
|
|
2345
|
+
}
|
|
2346
|
+
|
|
1895
2347
|
// src/patterns/sidebar/context.ts
|
|
1896
2348
|
import { createContext, useContext } from "react";
|
|
1897
2349
|
var SidebarContext = createContext({ collapsed: false });
|
|
@@ -1901,7 +2353,7 @@ function useSidebarCollapsed() {
|
|
|
1901
2353
|
}
|
|
1902
2354
|
|
|
1903
2355
|
// src/patterns/sidebar/NavItem.tsx
|
|
1904
|
-
import { jsx as
|
|
2356
|
+
import { jsx as jsx36, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1905
2357
|
function NavItem({
|
|
1906
2358
|
as,
|
|
1907
2359
|
icon: Icon2,
|
|
@@ -1913,7 +2365,7 @@ function NavItem({
|
|
|
1913
2365
|
}) {
|
|
1914
2366
|
const collapsed = useSidebarCollapsed();
|
|
1915
2367
|
const Comp = as ?? "a";
|
|
1916
|
-
const row = /* @__PURE__ */
|
|
2368
|
+
const row = /* @__PURE__ */ jsxs20(
|
|
1917
2369
|
Comp,
|
|
1918
2370
|
{
|
|
1919
2371
|
className: cn(
|
|
@@ -1925,7 +2377,7 @@ function NavItem({
|
|
|
1925
2377
|
),
|
|
1926
2378
|
...rest,
|
|
1927
2379
|
children: [
|
|
1928
|
-
Icon2 && /* @__PURE__ */
|
|
2380
|
+
Icon2 && /* @__PURE__ */ jsx36(
|
|
1929
2381
|
Icon2,
|
|
1930
2382
|
{
|
|
1931
2383
|
className: cn("h-[18px] w-[18px] shrink-0", active ? "text-nav-primary" : "text-muted-foreground"),
|
|
@@ -1933,7 +2385,7 @@ function NavItem({
|
|
|
1933
2385
|
"aria-hidden": true
|
|
1934
2386
|
}
|
|
1935
2387
|
),
|
|
1936
|
-
/* @__PURE__ */
|
|
2388
|
+
/* @__PURE__ */ jsx36(
|
|
1937
2389
|
"span",
|
|
1938
2390
|
{
|
|
1939
2391
|
className: cn(
|
|
@@ -1948,9 +2400,9 @@ function NavItem({
|
|
|
1948
2400
|
}
|
|
1949
2401
|
);
|
|
1950
2402
|
if (!collapsed) return row;
|
|
1951
|
-
return /* @__PURE__ */
|
|
1952
|
-
/* @__PURE__ */
|
|
1953
|
-
/* @__PURE__ */
|
|
2403
|
+
return /* @__PURE__ */ jsxs20(Tooltip, { children: [
|
|
2404
|
+
/* @__PURE__ */ jsx36(TooltipTrigger, { asChild: true, children: row }),
|
|
2405
|
+
/* @__PURE__ */ jsxs20(TooltipContent, { side: "right", className: "flex items-center gap-1.5", children: [
|
|
1954
2406
|
label,
|
|
1955
2407
|
badge
|
|
1956
2408
|
] })
|
|
@@ -1960,7 +2412,7 @@ function NavItem({
|
|
|
1960
2412
|
// src/patterns/sidebar/NavGroup.tsx
|
|
1961
2413
|
import { ChevronDown as ChevronDown2 } from "lucide-react";
|
|
1962
2414
|
import { useState as useState8 } from "react";
|
|
1963
|
-
import { jsx as
|
|
2415
|
+
import { jsx as jsx37, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1964
2416
|
function NavGroup({
|
|
1965
2417
|
label,
|
|
1966
2418
|
defaultCollapsed = false,
|
|
@@ -1976,8 +2428,8 @@ function NavGroup({
|
|
|
1976
2428
|
onCollapsedChange?.(next);
|
|
1977
2429
|
if (collapsed === void 0) setInternalCollapsed(next);
|
|
1978
2430
|
};
|
|
1979
|
-
return /* @__PURE__ */
|
|
1980
|
-
label && (railCollapsed ? /* @__PURE__ */
|
|
2431
|
+
return /* @__PURE__ */ jsxs21("div", { className: cn("flex flex-col gap-0.5", className), children: [
|
|
2432
|
+
label && (railCollapsed ? /* @__PURE__ */ jsx37("div", { className: "mx-2 my-1 h-px bg-border/50", "aria-hidden": true }) : /* @__PURE__ */ jsxs21(
|
|
1981
2433
|
"button",
|
|
1982
2434
|
{
|
|
1983
2435
|
type: "button",
|
|
@@ -1985,8 +2437,8 @@ function NavGroup({
|
|
|
1985
2437
|
"aria-expanded": !groupCollapsed,
|
|
1986
2438
|
className: "group flex w-full items-center justify-between px-2.5 py-0.5 text-[11px] font-medium text-muted-foreground/80 hover:text-foreground",
|
|
1987
2439
|
children: [
|
|
1988
|
-
/* @__PURE__ */
|
|
1989
|
-
/* @__PURE__ */
|
|
2440
|
+
/* @__PURE__ */ jsx37("span", { children: label }),
|
|
2441
|
+
/* @__PURE__ */ jsx37(
|
|
1990
2442
|
ChevronDown2,
|
|
1991
2443
|
{
|
|
1992
2444
|
className: cn(
|
|
@@ -1999,7 +2451,7 @@ function NavGroup({
|
|
|
1999
2451
|
]
|
|
2000
2452
|
}
|
|
2001
2453
|
)),
|
|
2002
|
-
!groupCollapsed && /* @__PURE__ */
|
|
2454
|
+
!groupCollapsed && /* @__PURE__ */ jsx37("div", { className: "flex flex-col gap-0.5", children })
|
|
2003
2455
|
] });
|
|
2004
2456
|
}
|
|
2005
2457
|
|
|
@@ -2011,7 +2463,7 @@ import {
|
|
|
2011
2463
|
useRef as useRef5,
|
|
2012
2464
|
useState as useState9
|
|
2013
2465
|
} from "react";
|
|
2014
|
-
import { jsx as
|
|
2466
|
+
import { Fragment as Fragment3, jsx as jsx38, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2015
2467
|
function resolve(value, collapsed) {
|
|
2016
2468
|
return typeof value === "function" ? value(collapsed) : value;
|
|
2017
2469
|
}
|
|
@@ -2058,7 +2510,7 @@ function Sidebar({
|
|
|
2058
2510
|
dragStartRef.current = null;
|
|
2059
2511
|
setIsDragging(false);
|
|
2060
2512
|
};
|
|
2061
|
-
return /* @__PURE__ */
|
|
2513
|
+
return /* @__PURE__ */ jsx38(SidebarContextProvider, { value: { collapsed }, children: /* @__PURE__ */ jsx38(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs22(
|
|
2062
2514
|
"aside",
|
|
2063
2515
|
{
|
|
2064
2516
|
style: { width: collapsed ? railWidth : currentWidth },
|
|
@@ -2068,22 +2520,47 @@ function Sidebar({
|
|
|
2068
2520
|
className
|
|
2069
2521
|
),
|
|
2070
2522
|
children: [
|
|
2071
|
-
(header || onCollapsedChange) && /* @__PURE__ */
|
|
2072
|
-
|
|
2073
|
-
|
|
2523
|
+
(header || onCollapsedChange) && /* @__PURE__ */ jsx38("div", { className: "flex h-16 items-center gap-2 border-b border-border px-3", children: collapsed && onCollapsedChange ? (
|
|
2524
|
+
// Collapsed rail: the header slot (e.g. a compact logo mark) and the
|
|
2525
|
+
// expand toggle would otherwise compete for the narrow rail's width
|
|
2526
|
+
// and clip each other, so they're stacked instead — the header shows
|
|
2527
|
+
// by default and cross-fades to the toggle icon on hover, matching
|
|
2528
|
+
// the same affordance OneSales' own sidebar rail uses.
|
|
2529
|
+
/* @__PURE__ */ jsxs22(
|
|
2530
|
+
"button",
|
|
2531
|
+
{
|
|
2532
|
+
type: "button",
|
|
2533
|
+
onClick: () => onCollapsedChange(!collapsed),
|
|
2534
|
+
"aria-label": "Expand sidebar",
|
|
2535
|
+
className: "group relative flex h-9 w-9 shrink-0 items-center justify-center",
|
|
2536
|
+
children: [
|
|
2537
|
+
header && /* @__PURE__ */ jsx38("span", { className: "transition-opacity group-hover:opacity-0", children: resolve(header, collapsed) }),
|
|
2538
|
+
/* @__PURE__ */ jsx38(
|
|
2539
|
+
PanelLeftOpen,
|
|
2540
|
+
{
|
|
2541
|
+
className: "absolute h-4 w-4 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100",
|
|
2542
|
+
"aria-hidden": true
|
|
2543
|
+
}
|
|
2544
|
+
)
|
|
2545
|
+
]
|
|
2546
|
+
}
|
|
2547
|
+
)
|
|
2548
|
+
) : /* @__PURE__ */ jsxs22(Fragment3, { children: [
|
|
2549
|
+
header && /* @__PURE__ */ jsx38("div", { className: "min-w-0 flex-1", children: resolve(header, collapsed) }),
|
|
2550
|
+
onCollapsedChange && /* @__PURE__ */ jsx38(
|
|
2074
2551
|
"button",
|
|
2075
2552
|
{
|
|
2076
2553
|
type: "button",
|
|
2077
2554
|
onClick: () => onCollapsedChange(!collapsed),
|
|
2078
|
-
"aria-label":
|
|
2555
|
+
"aria-label": "Collapse sidebar",
|
|
2079
2556
|
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
2080
|
-
children:
|
|
2557
|
+
children: /* @__PURE__ */ jsx38(PanelLeftClose, { className: "h-4 w-4", "aria-hidden": true })
|
|
2081
2558
|
}
|
|
2082
2559
|
)
|
|
2083
|
-
] }),
|
|
2084
|
-
/* @__PURE__ */
|
|
2085
|
-
footer && /* @__PURE__ */
|
|
2086
|
-
resizable && !collapsed && /* @__PURE__ */
|
|
2560
|
+
] }) }),
|
|
2561
|
+
/* @__PURE__ */ jsx38(SidebarNav, { children }),
|
|
2562
|
+
footer && /* @__PURE__ */ jsx38("div", { className: "border-t border-border/50 px-2 py-2", children: resolve(footer, collapsed) }),
|
|
2563
|
+
resizable && !collapsed && /* @__PURE__ */ jsx38(
|
|
2087
2564
|
"div",
|
|
2088
2565
|
{
|
|
2089
2566
|
onPointerDown: handlePointerDown,
|
|
@@ -2113,8 +2590,8 @@ function SidebarNav({ children, className, ...rest }) {
|
|
|
2113
2590
|
observer.disconnect();
|
|
2114
2591
|
};
|
|
2115
2592
|
}, []);
|
|
2116
|
-
return /* @__PURE__ */
|
|
2117
|
-
/* @__PURE__ */
|
|
2593
|
+
return /* @__PURE__ */ jsxs22("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [
|
|
2594
|
+
/* @__PURE__ */ jsx38(
|
|
2118
2595
|
"div",
|
|
2119
2596
|
{
|
|
2120
2597
|
ref,
|
|
@@ -2123,7 +2600,7 @@ function SidebarNav({ children, className, ...rest }) {
|
|
|
2123
2600
|
children
|
|
2124
2601
|
}
|
|
2125
2602
|
),
|
|
2126
|
-
showFade && /* @__PURE__ */
|
|
2603
|
+
showFade && /* @__PURE__ */ jsx38(
|
|
2127
2604
|
"div",
|
|
2128
2605
|
{
|
|
2129
2606
|
className: "pointer-events-none absolute right-0 bottom-0 left-0 h-6 bg-gradient-to-t from-sidebar to-transparent",
|
|
@@ -2153,6 +2630,7 @@ export {
|
|
|
2153
2630
|
DialogHeader,
|
|
2154
2631
|
DialogTitle,
|
|
2155
2632
|
DialogTrigger,
|
|
2633
|
+
DonutChart,
|
|
2156
2634
|
DropdownMenu,
|
|
2157
2635
|
DropdownMenuCheckboxItem,
|
|
2158
2636
|
DropdownMenuContent,
|
|
@@ -2171,6 +2649,7 @@ export {
|
|
|
2171
2649
|
FormField,
|
|
2172
2650
|
FormModal,
|
|
2173
2651
|
Input,
|
|
2652
|
+
KpiCard,
|
|
2174
2653
|
Label,
|
|
2175
2654
|
LoadingOverlay,
|
|
2176
2655
|
LoadingSpinner,
|
|
@@ -2185,6 +2664,8 @@ export {
|
|
|
2185
2664
|
ScrollArea,
|
|
2186
2665
|
ScrollBar,
|
|
2187
2666
|
SearchBar,
|
|
2667
|
+
SearchPanel,
|
|
2668
|
+
SearchTrigger,
|
|
2188
2669
|
Select,
|
|
2189
2670
|
SelectContent,
|
|
2190
2671
|
SelectGroup,
|
|
@@ -2204,6 +2685,7 @@ export {
|
|
|
2204
2685
|
Sidebar,
|
|
2205
2686
|
SidebarNav,
|
|
2206
2687
|
Skeleton,
|
|
2688
|
+
Sparkline,
|
|
2207
2689
|
StatusBadge,
|
|
2208
2690
|
Switch,
|
|
2209
2691
|
Table,
|