@terminal-blueprint/react 0.1.1 → 0.1.2

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
@@ -156,6 +156,7 @@ function CardRoot({
156
156
  brackets = false,
157
157
  pulse = false,
158
158
  hoverable = false,
159
+ href,
159
160
  className,
160
161
  children,
161
162
  ref,
@@ -171,10 +172,12 @@ function CardRoot({
171
172
  brackets && "tb-card--bracketed",
172
173
  pulse && "tb-card--pulse",
173
174
  hoverable && "tb-card--hoverable",
175
+ href && "tb-card--link",
174
176
  className
175
177
  ),
176
178
  ...props,
177
179
  children: [
180
+ href && /* @__PURE__ */ jsx4("a", { className: "tb-card__link", href, "aria-hidden": "true", tabIndex: -1 }),
178
181
  brackets && /* @__PURE__ */ jsxs4(Fragment, { children: [
179
182
  /* @__PURE__ */ jsx4("span", { className: "tb-card__bracket tb-card__bracket--tl" }),
180
183
  /* @__PURE__ */ jsx4("span", { className: "tb-card__bracket tb-card__bracket--tr" }),
@@ -1343,22 +1346,81 @@ function Progress({
1343
1346
  );
1344
1347
  }
1345
1348
 
1346
- // src/components/Battery.tsx
1349
+ // src/components/Slider.tsx
1350
+ import { useId as useId4 } from "react";
1347
1351
  import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
1352
+ function Slider({
1353
+ value,
1354
+ onChange,
1355
+ min = 0,
1356
+ max = 100,
1357
+ step = 1,
1358
+ variant = "default",
1359
+ label,
1360
+ showValue = false,
1361
+ disabled = false,
1362
+ className,
1363
+ ref
1364
+ }) {
1365
+ const id = useId4();
1366
+ const percent = (value - min) / (max - min) * 100;
1367
+ return /* @__PURE__ */ jsxs18(
1368
+ "div",
1369
+ {
1370
+ className: cn(
1371
+ "tb-slider",
1372
+ variant !== "default" && `tb-slider--${variant}`,
1373
+ disabled && "tb-slider--disabled",
1374
+ className
1375
+ ),
1376
+ children: [
1377
+ (label || showValue) && /* @__PURE__ */ jsxs18("div", { className: "tb-slider__header", children: [
1378
+ label && /* @__PURE__ */ jsx22("label", { className: "tb-slider__label", htmlFor: id, children: label }),
1379
+ showValue && /* @__PURE__ */ jsx22("span", { className: "tb-slider__value", children: value })
1380
+ ] }),
1381
+ /* @__PURE__ */ jsx22(
1382
+ "input",
1383
+ {
1384
+ ref,
1385
+ id,
1386
+ type: "range",
1387
+ className: "tb-slider__input",
1388
+ style: { "--slider-percent": `${percent}%` },
1389
+ value,
1390
+ min,
1391
+ max,
1392
+ step,
1393
+ disabled,
1394
+ onChange: (e) => onChange(Number(e.target.value)),
1395
+ "aria-label": label,
1396
+ "aria-valuenow": value,
1397
+ "aria-valuemin": min,
1398
+ "aria-valuemax": max
1399
+ }
1400
+ )
1401
+ ]
1402
+ }
1403
+ );
1404
+ }
1405
+
1406
+ // src/components/Battery.tsx
1407
+ import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
1348
1408
  function Battery({
1349
1409
  value,
1350
1410
  total = 10,
1351
1411
  variant = "default",
1352
1412
  animated = false,
1353
1413
  label,
1414
+ showValue = false,
1354
1415
  className
1355
1416
  }) {
1356
1417
  const clampedValue = Math.max(0, Math.min(total, value));
1357
- return /* @__PURE__ */ jsxs18(
1418
+ const percent = Math.round(clampedValue / total * 100);
1419
+ return /* @__PURE__ */ jsxs19(
1358
1420
  "div",
1359
1421
  {
1360
1422
  className: cn(
1361
- "tb-battery",
1423
+ "tb-battery tb-battery--inline",
1362
1424
  variant !== "default" && `tb-battery--${variant}`,
1363
1425
  animated && "tb-battery--animated",
1364
1426
  className
@@ -1369,19 +1431,20 @@ function Battery({
1369
1431
  "aria-valuemax": total,
1370
1432
  "aria-label": label,
1371
1433
  children: [
1372
- label && /* @__PURE__ */ jsx22("span", { className: "tb-battery__label", children: label }),
1373
- /* @__PURE__ */ jsxs18("div", { className: "tb-battery__body", children: [
1374
- /* @__PURE__ */ jsx22("div", { className: "tb-battery__cap" }),
1375
- /* @__PURE__ */ jsx22("div", { className: "tb-battery__segments", children: Array.from({ length: total }, (_, i) => /* @__PURE__ */ jsx22(
1376
- "div",
1377
- {
1378
- className: cn(
1379
- "tb-battery__segment",
1380
- i < clampedValue && "tb-battery__segment--filled"
1381
- )
1382
- },
1383
- i
1384
- )) })
1434
+ label && /* @__PURE__ */ jsx23("span", { className: "tb-battery__label", children: label }),
1435
+ /* @__PURE__ */ jsx23("div", { className: "tb-battery__track", children: Array.from({ length: total }, (_, i) => /* @__PURE__ */ jsx23(
1436
+ "div",
1437
+ {
1438
+ className: cn(
1439
+ "tb-battery__segment",
1440
+ i < clampedValue && "tb-battery__segment--filled"
1441
+ )
1442
+ },
1443
+ i
1444
+ )) }),
1445
+ showValue && /* @__PURE__ */ jsxs19("span", { className: "tb-battery__value", children: [
1446
+ percent,
1447
+ "%"
1385
1448
  ] })
1386
1449
  ]
1387
1450
  }
@@ -1389,7 +1452,7 @@ function Battery({
1389
1452
  }
1390
1453
 
1391
1454
  // src/components/BatteryInline.tsx
1392
- import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
1455
+ import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
1393
1456
  function BatteryInline({
1394
1457
  value,
1395
1458
  total = 10,
@@ -1401,11 +1464,12 @@ function BatteryInline({
1401
1464
  }) {
1402
1465
  const clampedValue = Math.max(0, Math.min(total, value));
1403
1466
  const percent = Math.round(clampedValue / total * 100);
1404
- return /* @__PURE__ */ jsxs19(
1467
+ return /* @__PURE__ */ jsxs20(
1405
1468
  "div",
1406
1469
  {
1407
1470
  className: cn(
1408
1471
  "tb-battery tb-battery--inline",
1472
+ variant !== "default" && `tb-battery--${variant}`,
1409
1473
  animated && "tb-battery--animated",
1410
1474
  className
1411
1475
  ),
@@ -1415,20 +1479,18 @@ function BatteryInline({
1415
1479
  "aria-valuemax": total,
1416
1480
  "aria-label": label,
1417
1481
  children: [
1418
- label && /* @__PURE__ */ jsx23("span", { className: "tb-battery__label", children: label }),
1419
- /* @__PURE__ */ jsx23("div", { className: "tb-battery__track", children: Array.from({ length: total }, (_, i) => /* @__PURE__ */ jsx23(
1482
+ label && /* @__PURE__ */ jsx24("span", { className: "tb-battery__label", children: label }),
1483
+ /* @__PURE__ */ jsx24("div", { className: "tb-battery__track", children: Array.from({ length: total }, (_, i) => /* @__PURE__ */ jsx24(
1420
1484
  "div",
1421
1485
  {
1422
1486
  className: cn(
1423
1487
  "tb-battery__segment",
1424
- i < clampedValue && "tb-battery__segment--filled",
1425
- i < clampedValue && variant !== "default" && `tb-battery__segment--${variant}`
1488
+ i < clampedValue && "tb-battery__segment--filled"
1426
1489
  )
1427
1490
  },
1428
1491
  i
1429
1492
  )) }),
1430
- /* @__PURE__ */ jsx23("div", { className: "tb-battery__cap" }),
1431
- showValue && /* @__PURE__ */ jsxs19("span", { className: "tb-battery__value", children: [
1493
+ showValue && /* @__PURE__ */ jsxs20("span", { className: "tb-battery__value", children: [
1432
1494
  percent,
1433
1495
  "%"
1434
1496
  ] })
@@ -1438,7 +1500,7 @@ function BatteryInline({
1438
1500
  }
1439
1501
 
1440
1502
  // src/components/Table.tsx
1441
- import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
1503
+ import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
1442
1504
  function getCellValue(row, accessor) {
1443
1505
  if (typeof accessor === "function") return accessor(row);
1444
1506
  return row[accessor];
@@ -1449,9 +1511,9 @@ function Table({
1449
1511
  onRowClick,
1450
1512
  className
1451
1513
  }) {
1452
- return /* @__PURE__ */ jsx24("div", { className: cn("tb-table__wrapper", className), children: /* @__PURE__ */ jsxs20("table", { className: "tb-table", children: [
1453
- /* @__PURE__ */ jsx24("thead", { className: "tb-table__head", children: /* @__PURE__ */ jsx24("tr", { className: "tb-table__row", children: columns.map((col) => /* @__PURE__ */ jsx24("th", { className: "tb-table__th", children: col.header }, col.id)) }) }),
1454
- /* @__PURE__ */ jsx24("tbody", { className: "tb-table__body", children: data.map((row, rowIndex) => /* @__PURE__ */ jsx24(
1514
+ return /* @__PURE__ */ jsx25("div", { className: cn("tb-table__wrapper", className), children: /* @__PURE__ */ jsxs21("table", { className: "tb-table", children: [
1515
+ /* @__PURE__ */ jsx25("thead", { className: "tb-table__head", children: /* @__PURE__ */ jsx25("tr", { className: "tb-table__row", children: columns.map((col) => /* @__PURE__ */ jsx25("th", { className: "tb-table__th", children: col.header }, col.id)) }) }),
1516
+ /* @__PURE__ */ jsx25("tbody", { className: "tb-table__body", children: data.map((row, rowIndex) => /* @__PURE__ */ jsx25(
1455
1517
  "tr",
1456
1518
  {
1457
1519
  className: cn(
@@ -1461,7 +1523,7 @@ function Table({
1461
1523
  onClick: onRowClick ? () => onRowClick(row) : void 0,
1462
1524
  children: columns.map((col) => {
1463
1525
  const value = getCellValue(row, col.accessor);
1464
- return /* @__PURE__ */ jsx24("td", { className: "tb-table__td", children: col.cell ? col.cell(value, row) : value }, col.id);
1526
+ return /* @__PURE__ */ jsx25("td", { className: "tb-table__td", children: col.cell ? col.cell(value, row) : value }, col.id);
1465
1527
  })
1466
1528
  },
1467
1529
  rowIndex
@@ -1470,7 +1532,7 @@ function Table({
1470
1532
  }
1471
1533
 
1472
1534
  // src/components/Skeleton.tsx
1473
- import { jsx as jsx25 } from "react/jsx-runtime";
1535
+ import { jsx as jsx26 } from "react/jsx-runtime";
1474
1536
  function Skeleton({
1475
1537
  variant = "text",
1476
1538
  width,
@@ -1483,7 +1545,7 @@ function Skeleton({
1483
1545
  height: typeof height === "number" ? `${height}px` : height
1484
1546
  };
1485
1547
  if (variant === "text" && lines > 1) {
1486
- return /* @__PURE__ */ jsx25("div", { className: cn("tb-skeleton__group", className), children: Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx25(
1548
+ return /* @__PURE__ */ jsx26("div", { className: cn("tb-skeleton__group", className), children: Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx26(
1487
1549
  "div",
1488
1550
  {
1489
1551
  className: cn(
@@ -1495,7 +1557,7 @@ function Skeleton({
1495
1557
  i
1496
1558
  )) });
1497
1559
  }
1498
- return /* @__PURE__ */ jsx25(
1560
+ return /* @__PURE__ */ jsx26(
1499
1561
  "div",
1500
1562
  {
1501
1563
  className: cn(
@@ -1509,13 +1571,13 @@ function Skeleton({
1509
1571
  }
1510
1572
 
1511
1573
  // src/components/Spinner.tsx
1512
- import { Fragment as Fragment3, jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
1574
+ import { Fragment as Fragment3, jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
1513
1575
  function Spinner({
1514
1576
  variant = "multi-square",
1515
1577
  size = "md",
1516
1578
  className
1517
1579
  }) {
1518
- return /* @__PURE__ */ jsx26(
1580
+ return /* @__PURE__ */ jsx27(
1519
1581
  "span",
1520
1582
  {
1521
1583
  className: cn(
@@ -1526,19 +1588,19 @@ function Spinner({
1526
1588
  ),
1527
1589
  role: "status",
1528
1590
  "aria-label": "Loading",
1529
- children: variant === "multi-square" && /* @__PURE__ */ jsxs21(Fragment3, { children: [
1530
- /* @__PURE__ */ jsx26("span", { className: "tb-spinner__square" }),
1531
- /* @__PURE__ */ jsx26("span", { className: "tb-spinner__square" }),
1532
- /* @__PURE__ */ jsx26("span", { className: "tb-spinner__square" }),
1533
- /* @__PURE__ */ jsx26("span", { className: "tb-spinner__square" }),
1534
- /* @__PURE__ */ jsx26("span", { className: "tb-spinner__square" })
1591
+ children: variant === "multi-square" && /* @__PURE__ */ jsxs22(Fragment3, { children: [
1592
+ /* @__PURE__ */ jsx27("span", { className: "tb-spinner__square" }),
1593
+ /* @__PURE__ */ jsx27("span", { className: "tb-spinner__square" }),
1594
+ /* @__PURE__ */ jsx27("span", { className: "tb-spinner__square" }),
1595
+ /* @__PURE__ */ jsx27("span", { className: "tb-spinner__square" }),
1596
+ /* @__PURE__ */ jsx27("span", { className: "tb-spinner__square" })
1535
1597
  ] })
1536
1598
  }
1537
1599
  );
1538
1600
  }
1539
1601
 
1540
1602
  // src/components/Pagination.tsx
1541
- import { jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
1603
+ import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
1542
1604
  function getPageRange(page, totalPages, siblingCount) {
1543
1605
  const totalSlots = siblingCount * 2 + 5;
1544
1606
  if (totalPages <= totalSlots) {
@@ -1571,8 +1633,8 @@ function Pagination({
1571
1633
  className
1572
1634
  }) {
1573
1635
  const pages = getPageRange(page, totalPages, siblingCount);
1574
- return /* @__PURE__ */ jsxs22("nav", { className: cn("tb-pagination", className), "aria-label": "Pagination", children: [
1575
- /* @__PURE__ */ jsx27(
1636
+ return /* @__PURE__ */ jsxs23("nav", { className: cn("tb-pagination", className), "aria-label": "Pagination", children: [
1637
+ /* @__PURE__ */ jsx28(
1576
1638
  "button",
1577
1639
  {
1578
1640
  className: "tb-pagination__btn tb-pagination__btn--prev",
@@ -1583,7 +1645,7 @@ function Pagination({
1583
1645
  }
1584
1646
  ),
1585
1647
  pages.map(
1586
- (p, i) => p === "ellipsis" ? /* @__PURE__ */ jsx27("span", { className: "tb-pagination__ellipsis", children: "..." }, `ellipsis-${i}`) : /* @__PURE__ */ jsx27(
1648
+ (p, i) => p === "ellipsis" ? /* @__PURE__ */ jsx28("span", { className: "tb-pagination__ellipsis", children: "..." }, `ellipsis-${i}`) : /* @__PURE__ */ jsx28(
1587
1649
  "button",
1588
1650
  {
1589
1651
  className: cn(
@@ -1597,7 +1659,7 @@ function Pagination({
1597
1659
  p
1598
1660
  )
1599
1661
  ),
1600
- /* @__PURE__ */ jsx27(
1662
+ /* @__PURE__ */ jsx28(
1601
1663
  "button",
1602
1664
  {
1603
1665
  className: "tb-pagination__btn tb-pagination__btn--next",
@@ -1611,7 +1673,7 @@ function Pagination({
1611
1673
  }
1612
1674
 
1613
1675
  // src/components/Breadcrumbs.tsx
1614
- import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
1676
+ import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
1615
1677
  function Breadcrumbs({
1616
1678
  items,
1617
1679
  separator = "/",
@@ -1619,10 +1681,10 @@ function Breadcrumbs({
1619
1681
  className
1620
1682
  }) {
1621
1683
  const LinkComponent = linkComponent || "a";
1622
- return /* @__PURE__ */ jsx28("nav", { className: cn("tb-breadcrumbs", className), "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx28("ol", { className: "tb-breadcrumbs__list", children: items.map((item, i) => {
1684
+ return /* @__PURE__ */ jsx29("nav", { className: cn("tb-breadcrumbs", className), "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx29("ol", { className: "tb-breadcrumbs__list", children: items.map((item, i) => {
1623
1685
  const isLast = i === items.length - 1;
1624
- return /* @__PURE__ */ jsxs23("li", { className: "tb-breadcrumbs__item", children: [
1625
- item.href && !isLast ? /* @__PURE__ */ jsx28(LinkComponent, { href: item.href, className: "tb-breadcrumbs__link", children: item.label }) : /* @__PURE__ */ jsx28(
1686
+ return /* @__PURE__ */ jsxs24("li", { className: "tb-breadcrumbs__item", children: [
1687
+ item.href && !isLast ? /* @__PURE__ */ jsx29(LinkComponent, { href: item.href, className: "tb-breadcrumbs__link", children: item.label }) : /* @__PURE__ */ jsx29(
1626
1688
  "span",
1627
1689
  {
1628
1690
  className: "tb-breadcrumbs__current",
@@ -1630,14 +1692,14 @@ function Breadcrumbs({
1630
1692
  children: item.label
1631
1693
  }
1632
1694
  ),
1633
- !isLast && /* @__PURE__ */ jsx28("span", { className: "tb-breadcrumbs__separator", "aria-hidden": "true", children: separator })
1695
+ !isLast && /* @__PURE__ */ jsx29("span", { className: "tb-breadcrumbs__separator", "aria-hidden": "true", children: separator })
1634
1696
  ] }, i);
1635
1697
  }) }) });
1636
1698
  }
1637
1699
 
1638
1700
  // src/components/Toolbar.tsx
1639
1701
  import { useState as useState8, useRef as useRef5, useEffect as useEffect6 } from "react";
1640
- import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
1702
+ import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
1641
1703
  function ToolbarSelect({
1642
1704
  options,
1643
1705
  value: controlledValue,
@@ -1666,13 +1728,13 @@ function ToolbarSelect({
1666
1728
  onChange?.(opt.value);
1667
1729
  setOpen(false);
1668
1730
  }
1669
- return /* @__PURE__ */ jsxs24(
1731
+ return /* @__PURE__ */ jsxs25(
1670
1732
  "div",
1671
1733
  {
1672
1734
  ref,
1673
1735
  className: cn("tb-toolbar__select", open && "tb-toolbar__select--open", className),
1674
1736
  children: [
1675
- /* @__PURE__ */ jsxs24(
1737
+ /* @__PURE__ */ jsxs25(
1676
1738
  "button",
1677
1739
  {
1678
1740
  type: "button",
@@ -1683,19 +1745,19 @@ function ToolbarSelect({
1683
1745
  setOpen(!open);
1684
1746
  },
1685
1747
  children: [
1686
- /* @__PURE__ */ jsx29("span", { children: activeOption?.label ?? "" }),
1687
- /* @__PURE__ */ jsx29("span", { className: "tb-toolbar__chevron" })
1748
+ /* @__PURE__ */ jsx30("span", { children: activeOption?.label ?? "" }),
1749
+ /* @__PURE__ */ jsx30("span", { className: "tb-toolbar__chevron" })
1688
1750
  ]
1689
1751
  }
1690
1752
  ),
1691
- /* @__PURE__ */ jsx29("div", { className: cn("tb-toolbar__dropdown", dropDown && "tb-toolbar__dropdown--down"), children: options.map((opt) => /* @__PURE__ */ jsxs24(
1753
+ /* @__PURE__ */ jsx30("div", { className: cn("tb-toolbar__dropdown", dropDown && "tb-toolbar__dropdown--down"), children: options.map((opt) => /* @__PURE__ */ jsxs25(
1692
1754
  "div",
1693
1755
  {
1694
1756
  className: cn("tb-toolbar__option", opt.value === value && "tb-toolbar__option--active"),
1695
1757
  onClick: () => select(opt),
1696
1758
  children: [
1697
1759
  opt.label,
1698
- opt.meta && /* @__PURE__ */ jsx29("span", { className: "tb-toolbar__option-meta", children: opt.meta })
1760
+ opt.meta && /* @__PURE__ */ jsx30("span", { className: "tb-toolbar__option-meta", children: opt.meta })
1699
1761
  ]
1700
1762
  },
1701
1763
  opt.value
@@ -1705,13 +1767,13 @@ function ToolbarSelect({
1705
1767
  );
1706
1768
  }
1707
1769
  function ToolbarLabel({ children, className, ...props }) {
1708
- return /* @__PURE__ */ jsx29("span", { className: cn("tb-toolbar__label", className), ...props, children });
1770
+ return /* @__PURE__ */ jsx30("span", { className: cn("tb-toolbar__label", className), ...props, children });
1709
1771
  }
1710
1772
  function ToolbarDivider({ className, ...props }) {
1711
- return /* @__PURE__ */ jsx29("div", { className: cn("tb-toolbar__divider", className), ...props });
1773
+ return /* @__PURE__ */ jsx30("div", { className: cn("tb-toolbar__divider", className), ...props });
1712
1774
  }
1713
1775
  function ToolbarRoot({ position, fixed = false, children, className, ref, ...props }) {
1714
- return /* @__PURE__ */ jsx29(
1776
+ return /* @__PURE__ */ jsx30(
1715
1777
  "div",
1716
1778
  {
1717
1779
  ref,
@@ -1732,13 +1794,207 @@ var Toolbar = Object.assign(ToolbarRoot, {
1732
1794
  Divider: ToolbarDivider
1733
1795
  });
1734
1796
 
1797
+ // src/components/FileUpload.tsx
1798
+ import {
1799
+ useState as useState9,
1800
+ useCallback as useCallback4,
1801
+ useRef as useRef6,
1802
+ useId as useId5
1803
+ } from "react";
1804
+ import { Fragment as Fragment4, jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
1805
+ function formatSize(bytes) {
1806
+ if (bytes < 1024) return `${bytes} B`;
1807
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1808
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1809
+ }
1810
+ function isImage(file) {
1811
+ return file.type.startsWith("image/");
1812
+ }
1813
+ function FileUpload({
1814
+ onChange,
1815
+ value,
1816
+ accept,
1817
+ multiple = false,
1818
+ maxSize,
1819
+ compact = false,
1820
+ preview = false,
1821
+ label,
1822
+ placeholder,
1823
+ hint,
1824
+ error: errorProp,
1825
+ icon,
1826
+ disabled = false,
1827
+ className,
1828
+ ref
1829
+ }) {
1830
+ const autoId = useId5();
1831
+ const inputRef = useRef6(null);
1832
+ const [dragging, setDragging] = useState9(false);
1833
+ const [files, setFiles] = useState9([]);
1834
+ const [internalError, setInternalError] = useState9();
1835
+ const error = errorProp ?? internalError;
1836
+ const displayFiles = value ? value.map((f) => ({ file: f, preview: preview && isImage(f) ? URL.createObjectURL(f) : void 0 })) : files;
1837
+ const processFiles = useCallback4(
1838
+ (incoming) => {
1839
+ const arr = Array.from(incoming);
1840
+ setInternalError(void 0);
1841
+ if (maxSize) {
1842
+ const tooBig = arr.find((f) => f.size > maxSize);
1843
+ if (tooBig) {
1844
+ setInternalError(`${tooBig.name} exceeds ${formatSize(maxSize)}`);
1845
+ return;
1846
+ }
1847
+ }
1848
+ const newFiles = arr.map((f) => ({
1849
+ file: f,
1850
+ preview: preview && isImage(f) ? URL.createObjectURL(f) : void 0
1851
+ }));
1852
+ if (multiple) {
1853
+ const merged = [...files, ...newFiles];
1854
+ setFiles(merged);
1855
+ onChange?.(merged.map((f) => f.file));
1856
+ } else {
1857
+ files.forEach((f) => f.preview && URL.revokeObjectURL(f.preview));
1858
+ setFiles(newFiles.slice(0, 1));
1859
+ onChange?.(newFiles.slice(0, 1).map((f) => f.file));
1860
+ }
1861
+ },
1862
+ [files, maxSize, multiple, onChange, preview]
1863
+ );
1864
+ const handleChange = useCallback4(
1865
+ (e) => {
1866
+ if (e.target.files?.length) {
1867
+ processFiles(e.target.files);
1868
+ }
1869
+ e.target.value = "";
1870
+ },
1871
+ [processFiles]
1872
+ );
1873
+ const handleDragOver = useCallback4(
1874
+ (e) => {
1875
+ e.preventDefault();
1876
+ if (!disabled) setDragging(true);
1877
+ },
1878
+ [disabled]
1879
+ );
1880
+ const handleDragLeave = useCallback4((e) => {
1881
+ e.preventDefault();
1882
+ setDragging(false);
1883
+ }, []);
1884
+ const handleDrop = useCallback4(
1885
+ (e) => {
1886
+ e.preventDefault();
1887
+ setDragging(false);
1888
+ if (!disabled && e.dataTransfer.files.length) {
1889
+ processFiles(e.dataTransfer.files);
1890
+ }
1891
+ },
1892
+ [disabled, processFiles]
1893
+ );
1894
+ const removeFile = useCallback4(
1895
+ (index) => {
1896
+ const removed = files[index];
1897
+ if (removed?.preview) URL.revokeObjectURL(removed.preview);
1898
+ const next = files.filter((_, i) => i !== index);
1899
+ setFiles(next);
1900
+ onChange?.(next.map((f) => f.file));
1901
+ },
1902
+ [files, onChange]
1903
+ );
1904
+ const setRef = useCallback4(
1905
+ (el) => {
1906
+ inputRef.current = el;
1907
+ if (typeof ref === "function") ref(el);
1908
+ else if (ref) ref.current = el;
1909
+ },
1910
+ [ref]
1911
+ );
1912
+ const defaultPlaceholder = compact ? "Choose file" : "Drop files here or click to browse";
1913
+ return /* @__PURE__ */ jsxs26(
1914
+ "div",
1915
+ {
1916
+ className: cn(
1917
+ "tb-file-upload",
1918
+ compact && "tb-file-upload--compact",
1919
+ className
1920
+ ),
1921
+ children: [
1922
+ label && /* @__PURE__ */ jsx31("label", { htmlFor: autoId, className: "tb-label", children: label }),
1923
+ /* @__PURE__ */ jsxs26(
1924
+ "div",
1925
+ {
1926
+ className: cn(
1927
+ "tb-file-upload__zone",
1928
+ dragging && "tb-file-upload__zone--drag",
1929
+ error && "tb-file-upload__zone--error",
1930
+ disabled && "tb-file-upload__zone--disabled"
1931
+ ),
1932
+ onDragOver: handleDragOver,
1933
+ onDragLeave: handleDragLeave,
1934
+ onDrop: handleDrop,
1935
+ children: [
1936
+ /* @__PURE__ */ jsx31(
1937
+ "input",
1938
+ {
1939
+ ref: setRef,
1940
+ id: autoId,
1941
+ type: "file",
1942
+ className: "tb-file-upload__input",
1943
+ accept,
1944
+ multiple,
1945
+ disabled,
1946
+ onChange: handleChange,
1947
+ "aria-describedby": error ? `${autoId}-error` : void 0
1948
+ }
1949
+ ),
1950
+ compact ? /* @__PURE__ */ jsxs26("span", { className: "tb-btn tb-btn--sm", children: [
1951
+ icon && /* @__PURE__ */ jsx31("span", { className: "tb-btn__icon", children: icon }),
1952
+ placeholder ?? defaultPlaceholder
1953
+ ] }) : /* @__PURE__ */ jsxs26(Fragment4, { children: [
1954
+ /* @__PURE__ */ jsx31("span", { className: "tb-file-upload__icon", "aria-hidden": "true", children: icon ?? /* @__PURE__ */ jsx31("span", { className: "material-symbols-outlined", children: "upload_file" }) }),
1955
+ /* @__PURE__ */ jsx31("span", { className: "tb-file-upload__text", children: placeholder ?? defaultPlaceholder }),
1956
+ hint && /* @__PURE__ */ jsx31("span", { className: "tb-file-upload__hint-text", children: hint })
1957
+ ] })
1958
+ ]
1959
+ }
1960
+ ),
1961
+ error && /* @__PURE__ */ jsx31("span", { id: `${autoId}-error`, className: "tb-file-upload__error", role: "alert", children: error }),
1962
+ displayFiles.length > 0 && /* @__PURE__ */ jsx31("div", { className: "tb-file-upload__files", children: displayFiles.map((f, i) => /* @__PURE__ */ jsxs26("div", { className: "tb-file-upload__file", children: [
1963
+ f.preview ? /* @__PURE__ */ jsx31(
1964
+ "img",
1965
+ {
1966
+ src: f.preview,
1967
+ alt: f.file.name,
1968
+ className: "tb-file-upload__preview"
1969
+ }
1970
+ ) : /* @__PURE__ */ jsx31("span", { className: "tb-file-upload__file-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx31("span", { className: "material-symbols-outlined", children: isImage(f.file) ? "image" : "description" }) }),
1971
+ /* @__PURE__ */ jsxs26("div", { className: "tb-file-upload__file-info", children: [
1972
+ /* @__PURE__ */ jsx31("div", { className: "tb-file-upload__file-name", children: f.file.name }),
1973
+ /* @__PURE__ */ jsx31("div", { className: "tb-file-upload__file-size", children: formatSize(f.file.size) })
1974
+ ] }),
1975
+ !value && /* @__PURE__ */ jsx31(
1976
+ "button",
1977
+ {
1978
+ type: "button",
1979
+ className: "tb-file-upload__file-remove",
1980
+ onClick: () => removeFile(i),
1981
+ "aria-label": `Remove ${f.file.name}`,
1982
+ children: /* @__PURE__ */ jsx31("span", { className: "material-symbols-outlined", children: "close" })
1983
+ }
1984
+ )
1985
+ ] }, `${f.file.name}-${i}`)) })
1986
+ ]
1987
+ }
1988
+ );
1989
+ }
1990
+
1735
1991
  // src/components/Sidenav/Sidenav.tsx
1736
1992
  import {
1737
1993
  createContext as createContext5,
1738
1994
  useContext as useContext5,
1739
- useState as useState9
1995
+ useState as useState10
1740
1996
  } from "react";
1741
- import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
1997
+ import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
1742
1998
  var SidenavContext = createContext5({
1743
1999
  iconPosition: "left",
1744
2000
  borderSide: "left"
@@ -1751,7 +2007,7 @@ function SidenavRoot({
1751
2007
  ref,
1752
2008
  ...props
1753
2009
  }) {
1754
- return /* @__PURE__ */ jsx30(SidenavContext.Provider, { value: { iconPosition, borderSide }, children: /* @__PURE__ */ jsx30(
2010
+ return /* @__PURE__ */ jsx32(SidenavContext.Provider, { value: { iconPosition, borderSide }, children: /* @__PURE__ */ jsx32(
1755
2011
  "nav",
1756
2012
  {
1757
2013
  ref,
@@ -1775,7 +2031,7 @@ function Item({
1775
2031
  ...props
1776
2032
  }) {
1777
2033
  const Component = as || "a";
1778
- return /* @__PURE__ */ jsx30(
2034
+ return /* @__PURE__ */ jsx32(
1779
2035
  Component,
1780
2036
  {
1781
2037
  ref,
@@ -1790,7 +2046,7 @@ function Item({
1790
2046
  );
1791
2047
  }
1792
2048
  function Icon({ className, ref, ...props }) {
1793
- return /* @__PURE__ */ jsx30(
2049
+ return /* @__PURE__ */ jsx32(
1794
2050
  "span",
1795
2051
  {
1796
2052
  ref,
@@ -1810,7 +2066,7 @@ function Group({
1810
2066
  ref,
1811
2067
  ...props
1812
2068
  }) {
1813
- const [internalOpen, setInternalOpen] = useState9(defaultOpen);
2069
+ const [internalOpen, setInternalOpen] = useState10(defaultOpen);
1814
2070
  const isControlled = controlledOpen !== void 0;
1815
2071
  const isOpen = isControlled ? controlledOpen : internalOpen;
1816
2072
  function toggle() {
@@ -1818,7 +2074,7 @@ function Group({
1818
2074
  if (!isControlled) setInternalOpen(next);
1819
2075
  onOpenChange?.(next);
1820
2076
  }
1821
- return /* @__PURE__ */ jsxs25(
2077
+ return /* @__PURE__ */ jsxs27(
1822
2078
  "div",
1823
2079
  {
1824
2080
  ref,
@@ -1829,7 +2085,7 @@ function Group({
1829
2085
  ),
1830
2086
  ...props,
1831
2087
  children: [
1832
- /* @__PURE__ */ jsxs25(
2088
+ /* @__PURE__ */ jsxs27(
1833
2089
  "button",
1834
2090
  {
1835
2091
  type: "button",
@@ -1837,13 +2093,13 @@ function Group({
1837
2093
  onClick: toggle,
1838
2094
  "aria-expanded": isOpen,
1839
2095
  children: [
1840
- icon && /* @__PURE__ */ jsx30("span", { className: "tb-sidenav__icon", children: icon }),
2096
+ icon && /* @__PURE__ */ jsx32("span", { className: "tb-sidenav__icon", children: icon }),
1841
2097
  label,
1842
- /* @__PURE__ */ jsx30("span", { className: "tb-sidenav__group-chevron material-symbols-outlined", "aria-hidden": "true", children: "expand_more" })
2098
+ /* @__PURE__ */ jsx32("span", { className: "tb-sidenav__group-chevron material-symbols-outlined", "aria-hidden": "true", children: "expand_more" })
1843
2099
  ]
1844
2100
  }
1845
2101
  ),
1846
- /* @__PURE__ */ jsx30("div", { className: "tb-sidenav__group-content", children })
2102
+ /* @__PURE__ */ jsx32("div", { className: "tb-sidenav__group-content", children })
1847
2103
  ]
1848
2104
  }
1849
2105
  );
@@ -1855,27 +2111,27 @@ var Sidenav = Object.assign(SidenavRoot, {
1855
2111
  });
1856
2112
 
1857
2113
  // src/components/CodeBlock.tsx
1858
- import { useState as useState10, useRef as useRef6 } from "react";
1859
- import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
2114
+ import { useState as useState11, useRef as useRef7 } from "react";
2115
+ import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
1860
2116
  function CodeBlock({ children, copyText, className, ref, ...props }) {
1861
- const [copied, setCopied] = useState10(false);
1862
- const contentRef = useRef6(null);
2117
+ const [copied, setCopied] = useState11(false);
2118
+ const contentRef = useRef7(null);
1863
2119
  async function handleCopy() {
1864
2120
  const text = copyText ?? contentRef.current?.textContent ?? "";
1865
2121
  await navigator.clipboard.writeText(text);
1866
2122
  setCopied(true);
1867
2123
  setTimeout(() => setCopied(false), 2e3);
1868
2124
  }
1869
- return /* @__PURE__ */ jsxs26("div", { ref, className: cn("tb-code-block", className), ...props, children: [
1870
- /* @__PURE__ */ jsx31("div", { ref: contentRef, children }),
1871
- /* @__PURE__ */ jsx31(
2125
+ return /* @__PURE__ */ jsxs28("div", { ref, className: cn("tb-code-block", className), ...props, children: [
2126
+ /* @__PURE__ */ jsx33("div", { ref: contentRef, children }),
2127
+ /* @__PURE__ */ jsx33(
1872
2128
  "button",
1873
2129
  {
1874
2130
  type: "button",
1875
2131
  className: cn("tb-code-block__copy", copied && "tb-code-block__copy--copied"),
1876
2132
  onClick: handleCopy,
1877
2133
  "aria-label": copied ? "Copied" : "Copy to clipboard",
1878
- children: /* @__PURE__ */ jsx31("span", { className: "material-symbols-outlined", style: { fontSize: 14 }, children: copied ? "check" : "content_copy" })
2134
+ children: /* @__PURE__ */ jsx33("span", { className: "material-symbols-outlined", style: { fontSize: 14 }, children: copied ? "check" : "content_copy" })
1879
2135
  }
1880
2136
  )
1881
2137
  ] });
@@ -1885,11 +2141,11 @@ function CodeBlock({ children, copyText, className, ref, ...props }) {
1885
2141
  import {
1886
2142
  createContext as createContext6,
1887
2143
  useContext as useContext6,
1888
- useState as useState11,
2144
+ useState as useState12,
1889
2145
  useEffect as useEffect7,
1890
- useCallback as useCallback4
2146
+ useCallback as useCallback5
1891
2147
  } from "react";
1892
- import { jsx as jsx32 } from "react/jsx-runtime";
2148
+ import { jsx as jsx34 } from "react/jsx-runtime";
1893
2149
  var ThemeContext = createContext6(null);
1894
2150
  function useTheme() {
1895
2151
  const ctx = useContext6(ThemeContext);
@@ -1907,25 +2163,25 @@ function ThemeProvider({
1907
2163
  }) {
1908
2164
  const isThemeControlled = controlledTheme !== void 0;
1909
2165
  const isContrastControlled = controlledContrast !== void 0;
1910
- const [internalTheme, setInternalTheme] = useState11(defaultTheme);
1911
- const [internalContrast, setInternalContrast] = useState11(defaultContrast);
2166
+ const [internalTheme, setInternalTheme] = useState12(defaultTheme);
2167
+ const [internalContrast, setInternalContrast] = useState12(defaultContrast);
1912
2168
  const theme = isThemeControlled ? controlledTheme : internalTheme;
1913
2169
  const contrast = isContrastControlled ? controlledContrast : internalContrast;
1914
- const setTheme = useCallback4(
2170
+ const setTheme = useCallback5(
1915
2171
  (next) => {
1916
2172
  if (!isThemeControlled) setInternalTheme(next);
1917
2173
  onThemeChange?.(next);
1918
2174
  },
1919
2175
  [isThemeControlled, onThemeChange]
1920
2176
  );
1921
- const setContrast = useCallback4(
2177
+ const setContrast = useCallback5(
1922
2178
  (next) => {
1923
2179
  if (!isContrastControlled) setInternalContrast(next);
1924
2180
  onContrastChange?.(next);
1925
2181
  },
1926
2182
  [isContrastControlled, onContrastChange]
1927
2183
  );
1928
- const toggleTheme = useCallback4(
2184
+ const toggleTheme = useCallback5(
1929
2185
  () => setTheme(theme === "dark" ? "light" : "dark"),
1930
2186
  [theme, setTheme]
1931
2187
  );
@@ -1938,23 +2194,23 @@ function ThemeProvider({
1938
2194
  root.removeAttribute("data-contrast");
1939
2195
  }
1940
2196
  }, [theme, contrast]);
1941
- return /* @__PURE__ */ jsx32(ThemeContext.Provider, { value: { theme, setTheme, contrast, setContrast, toggleTheme }, children });
2197
+ return /* @__PURE__ */ jsx34(ThemeContext.Provider, { value: { theme, setTheme, contrast, setContrast, toggleTheme }, children });
1942
2198
  }
1943
2199
 
1944
2200
  // src/hooks/useDisclosure.ts
1945
- import { useState as useState12, useCallback as useCallback5 } from "react";
2201
+ import { useState as useState13, useCallback as useCallback6 } from "react";
1946
2202
  function useDisclosure(options = {}) {
1947
2203
  const { defaultOpen = false, onOpen: onOpenCallback, onClose: onCloseCallback } = options;
1948
- const [isOpen, setIsOpen] = useState12(defaultOpen);
1949
- const onOpen = useCallback5(() => {
2204
+ const [isOpen, setIsOpen] = useState13(defaultOpen);
2205
+ const onOpen = useCallback6(() => {
1950
2206
  setIsOpen(true);
1951
2207
  onOpenCallback?.();
1952
2208
  }, [onOpenCallback]);
1953
- const onClose = useCallback5(() => {
2209
+ const onClose = useCallback6(() => {
1954
2210
  setIsOpen(false);
1955
2211
  onCloseCallback?.();
1956
2212
  }, [onCloseCallback]);
1957
- const onToggle = useCallback5(() => {
2213
+ const onToggle = useCallback6(() => {
1958
2214
  if (isOpen) {
1959
2215
  onClose();
1960
2216
  } else {
@@ -1965,7 +2221,7 @@ function useDisclosure(options = {}) {
1965
2221
  }
1966
2222
 
1967
2223
  // src/hooks/useFocusTrap.ts
1968
- import { useEffect as useEffect8, useRef as useRef7 } from "react";
2224
+ import { useEffect as useEffect8, useRef as useRef8 } from "react";
1969
2225
  var FOCUSABLE_SELECTOR = [
1970
2226
  "a[href]",
1971
2227
  "button:not([disabled])",
@@ -1975,7 +2231,7 @@ var FOCUSABLE_SELECTOR = [
1975
2231
  '[tabindex]:not([tabindex="-1"])'
1976
2232
  ].join(", ");
1977
2233
  function useFocusTrap(ref, active) {
1978
- const previouslyFocusedRef = useRef7(null);
2234
+ const previouslyFocusedRef = useRef8(null);
1979
2235
  useEffect8(() => {
1980
2236
  if (!active || !ref.current) return;
1981
2237
  previouslyFocusedRef.current = document.activeElement;
@@ -2013,13 +2269,13 @@ function useFocusTrap(ref, active) {
2013
2269
  }
2014
2270
 
2015
2271
  // src/hooks/useKeyboardNav.ts
2016
- import { useState as useState13, useCallback as useCallback6 } from "react";
2272
+ import { useState as useState14, useCallback as useCallback7 } from "react";
2017
2273
  function useKeyboardNav(items, options = {}) {
2018
2274
  const { orientation = "vertical", loop = true, onSelect } = options;
2019
- const [activeIndex, setActiveIndex] = useState13(0);
2275
+ const [activeIndex, setActiveIndex] = useState14(0);
2020
2276
  const prevKey = orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
2021
2277
  const nextKey = orientation === "vertical" ? "ArrowDown" : "ArrowRight";
2022
- const onKeyDown = useCallback6(
2278
+ const onKeyDown = useCallback7(
2023
2279
  (e) => {
2024
2280
  const list = items.current;
2025
2281
  if (!list || list.length === 0) return;
@@ -2068,9 +2324,9 @@ function useKeyboardNav(items, options = {}) {
2068
2324
  }
2069
2325
 
2070
2326
  // src/hooks/useReducedMotion.ts
2071
- import { useState as useState14, useEffect as useEffect9 } from "react";
2327
+ import { useState as useState15, useEffect as useEffect9 } from "react";
2072
2328
  function useReducedMotion() {
2073
- const [reduced, setReduced] = useState14(false);
2329
+ const [reduced, setReduced] = useState15(false);
2074
2330
  useEffect9(() => {
2075
2331
  const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
2076
2332
  setReduced(mq.matches);
@@ -2082,9 +2338,9 @@ function useReducedMotion() {
2082
2338
  }
2083
2339
 
2084
2340
  // src/hooks/useMergedRef.ts
2085
- import { useCallback as useCallback7 } from "react";
2341
+ import { useCallback as useCallback8 } from "react";
2086
2342
  function useMergedRef(...refs) {
2087
- return useCallback7((node) => {
2343
+ return useCallback8((node) => {
2088
2344
  refs.forEach((ref) => {
2089
2345
  if (typeof ref === "function") ref(node);
2090
2346
  else if (ref) ref.current = node;
@@ -2101,6 +2357,7 @@ export {
2101
2357
  Checkbox,
2102
2358
  CodeBlock,
2103
2359
  CornerBrackets,
2360
+ FileUpload,
2104
2361
  Input,
2105
2362
  Modal,
2106
2363
  NavDropdown,
@@ -2115,6 +2372,7 @@ export {
2115
2372
  SelectTrigger,
2116
2373
  Sidenav,
2117
2374
  Skeleton,
2375
+ Slider,
2118
2376
  Spinner,
2119
2377
  Table,
2120
2378
  Tabs,