@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.js CHANGED
@@ -645,10 +645,15 @@ var sheetVariants = cva3(
645
645
  {
646
646
  variants: {
647
647
  side: {
648
- top: "inset-x-0 top-0 border-b data-[state=closed]:-translate-y-full data-[state=open]:translate-y-0",
649
- bottom: "inset-x-0 bottom-0 border-t data-[state=closed]:translate-y-full data-[state=open]:translate-y-0",
650
- left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:-translate-x-full data-[state=open]:translate-x-0 sm:max-w-sm",
651
- right: "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:translate-x-full data-[state=open]:translate-x-0 sm:max-w-sm"
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-4xl"
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/patterns/combobox/Combobox.tsx
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__ */ jsxs11(
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__ */ jsx25(
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__ */ jsx25(Check4, { className: cn("h-4 w-4 shrink-0", isSelected ? "opacity-100" : "opacity-0") }),
1334
- /* @__PURE__ */ jsx25("span", { className: "truncate", children: option.label })
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__ */ jsxs11(Popover, { open, onOpenChange: setOpen, children: [
1341
- /* @__PURE__ */ jsx25(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs11(
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__ */ jsx25("span", { className: "truncate", children: triggerLabel }),
1358
- /* @__PURE__ */ jsxs11("span", { className: "flex items-center gap-1", children: [
1359
- clearable && !loading && selectedValues.length > 0 ? /* @__PURE__ */ jsx25(
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__ */ jsx25(X3, { className: "h-3.5 w-3.5" })
1450
+ children: /* @__PURE__ */ jsx26(X3, { className: "h-3.5 w-3.5" })
1375
1451
  }
1376
1452
  ) : null,
1377
- /* @__PURE__ */ jsx25(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50", "aria-hidden": "true" })
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__ */ jsx25(
1458
+ /* @__PURE__ */ jsx26(
1383
1459
  PopoverContent,
1384
1460
  {
1385
1461
  align: "start",
1386
1462
  container,
1387
- className: "w-[var(--radix-popover-trigger-width)] min-w-[10rem] p-2",
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__ */ jsxs11("div", { className: "flex flex-col gap-2", children: [
1390
- /* @__PURE__ */ jsx25(
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__ */ jsx25("div", { role: "listbox", className: "flex max-h-60 flex-col gap-0.5 overflow-y-auto", children: renderChunks.length === 0 ? /* @__PURE__ */ jsx25("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : renderChunks.map((chunk) => {
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__ */ jsxs11("div", { className: "flex flex-col gap-0.5 pt-1 first:pt-0", children: [
1416
- /* @__PURE__ */ jsxs11(
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__ */ jsx25(
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__ */ jsx25("span", { className: "truncate", children: groupLabel(chunk.group) })
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 jsx26, jsxs as jsxs12 } from "react/jsx-runtime";
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__ */ jsxs12(
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__ */ jsx26(
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__ */ jsx26("span", { className: "truncate", children: option.label })
1658
+ /* @__PURE__ */ jsx27("span", { className: "truncate", children: option.label })
1570
1659
  ]
1571
1660
  },
1572
1661
  option.value
1573
1662
  );
1574
1663
  }
1575
- return /* @__PURE__ */ jsxs12(Popover, { open, onOpenChange: setOpen, children: [
1576
- /* @__PURE__ */ jsx26(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs12(
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__ */ jsxs12(
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__ */ jsx26("span", { className: "max-w-48 truncate", children: option.label }),
1600
- !disabled && /* @__PURE__ */ jsx26(
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__ */ jsx26(X4, { className: "h-3 w-3" })
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__ */ jsx26(
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__ */ jsx26(
1724
+ /* @__PURE__ */ jsx27(
1636
1725
  PopoverContent,
1637
1726
  {
1638
1727
  align: "start",
1639
1728
  container,
1640
- className: "w-[var(--radix-popover-trigger-width)] min-w-[10rem] p-2",
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__ */ jsx26("div", { role: "listbox", className: "flex max-h-60 flex-col gap-0.5 overflow-y-auto", children: loading ? /* @__PURE__ */ jsx26("p", { className: "py-2 text-center text-sm text-muted-foreground", children: loadingMessage }) : renderChunks.length === 0 ? /* @__PURE__ */ jsx26("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : renderChunks.map((chunk) => {
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__ */ jsxs12("div", { className: "flex flex-col gap-0.5 pt-1 first:pt-0", children: [
1657
- /* @__PURE__ */ jsxs12(
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__ */ jsx26(
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__ */ jsx26("span", { className: "truncate", children: groupLabel(chunk.group) })
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 jsx27, jsxs as jsxs13 } from "react/jsx-runtime";
1793
+ import { jsx as jsx28, jsxs as jsxs14 } from "react/jsx-runtime";
1693
1794
  var variantStyles = {
1694
- error: "border-destructive bg-destructive/10 text-destructive",
1695
- warning: "border-amber-500 bg-amber-500/10 text-amber-700 dark:text-amber-300",
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__ */ jsx27(AlertCircle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
1701
- warning: /* @__PURE__ */ jsx27(AlertTriangle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
1702
- info: /* @__PURE__ */ jsx27(Info, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
1703
- success: /* @__PURE__ */ jsx27(CheckCircle2, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })
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__ */ jsx27("div", { className: cn("rounded-md border p-3 text-sm", variantStyles[variant], className), children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-row items-start gap-2", children: [
1708
- resolvedIcon != null && /* @__PURE__ */ jsx27("span", { className: "mt-0.5 shrink-0", children: resolvedIcon }),
1709
- /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-1", children: [
1710
- title && /* @__PURE__ */ jsx27("p", { className: "font-bold", children: title }),
1711
- /* @__PURE__ */ jsx27("div", { children })
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 jsx28 } from "react/jsx-runtime";
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-700 dark:bg-amber-500/20 dark:text-amber-300",
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-destructive",
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__ */ jsx28(Badge, { variant: "outline", className: cn(TONE_CLASSES[resolvedTone], className), ...props, children: displayLabel });
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 jsx29, jsxs as jsxs14 } from "react/jsx-runtime";
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__ */ jsx29("div", { role: "group", "aria-label": props["aria-label"], className: cn("inline-flex", className), "data-testid": props["data-testid"], children: options.map((option, index) => {
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__ */ jsxs14(
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__ */ jsx29(Icon2, { className: "h-4 w-4" }),
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 jsx30, jsxs as jsxs15 } from "react/jsx-runtime";
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__ */ jsxs15("span", { role: "status", className: cn("inline-flex items-center gap-2", className), ...props, children: [
1830
- /* @__PURE__ */ jsx30(Loader2, { className: cn(loadingSpinnerIconVariants({ size })), "aria-hidden": "true" }),
1831
- label ? /* @__PURE__ */ jsx30("span", { children: label }) : null
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__ */ jsx30(
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__ */ jsx30(LoadingSpinner, { size, label })
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 jsx31, jsxs as jsxs16 } from "react/jsx-runtime";
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__ */ jsxs16("div", { className: cn("relative", className), children: [
1862
- /* @__PURE__ */ jsx31("span", { className: "pointer-events-none absolute top-1/2 left-4 -translate-y-1/2 text-muted-foreground", children: isLoading ? /* @__PURE__ */ jsx31(LoadingSpinner, { size: "sm" }) : /* @__PURE__ */ jsx31(Search, { className: "h-4 w-4", "aria-hidden": "true" }) }),
1863
- /* @__PURE__ */ jsx31(
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__ */ jsx31(
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__ */ jsx31(X5, { className: "h-4 w-4", "aria-hidden": "true" })
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 jsx32, jsxs as jsxs17 } from "react/jsx-runtime";
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__ */ jsxs17(
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__ */ jsx32(
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__ */ jsx32(
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__ */ jsxs17(Tooltip, { children: [
1952
- /* @__PURE__ */ jsx32(TooltipTrigger, { asChild: true, children: row }),
1953
- /* @__PURE__ */ jsxs17(TooltipContent, { side: "right", className: "flex items-center gap-1.5", children: [
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 jsx33, jsxs as jsxs18 } from "react/jsx-runtime";
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__ */ jsxs18("div", { className: cn("flex flex-col gap-0.5", className), children: [
1980
- label && (railCollapsed ? /* @__PURE__ */ jsx33("div", { className: "mx-2 my-1 h-px bg-border/50", "aria-hidden": true }) : /* @__PURE__ */ jsxs18(
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__ */ jsx33("span", { children: label }),
1989
- /* @__PURE__ */ jsx33(
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__ */ jsx33("div", { className: "flex flex-col gap-0.5", children })
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 jsx34, jsxs as jsxs19 } from "react/jsx-runtime";
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__ */ jsx34(SidebarContextProvider, { value: { collapsed }, children: /* @__PURE__ */ jsx34(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs19(
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__ */ jsxs19("div", { className: "flex h-16 items-center gap-2 border-b border-border px-3", children: [
2072
- header && /* @__PURE__ */ jsx34("div", { className: "min-w-0 flex-1", children: resolve(header, collapsed) }),
2073
- onCollapsedChange && /* @__PURE__ */ jsx34(
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": collapsed ? "Expand sidebar" : "Collapse sidebar",
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: collapsed ? /* @__PURE__ */ jsx34(PanelLeftOpen, { className: "h-4 w-4", "aria-hidden": true }) : /* @__PURE__ */ jsx34(PanelLeftClose, { className: "h-4 w-4", "aria-hidden": true })
2557
+ children: /* @__PURE__ */ jsx38(PanelLeftClose, { className: "h-4 w-4", "aria-hidden": true })
2081
2558
  }
2082
2559
  )
2083
- ] }),
2084
- /* @__PURE__ */ jsx34(SidebarNav, { children }),
2085
- footer && /* @__PURE__ */ jsx34("div", { className: "border-t border-border/50 px-2 py-2", children: resolve(footer, collapsed) }),
2086
- resizable && !collapsed && /* @__PURE__ */ jsx34(
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__ */ jsxs19("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [
2117
- /* @__PURE__ */ jsx34(
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__ */ jsx34(
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,