@mirror-physics/fractal-ui 0.2.0-next.76 → 0.2.0-next.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -28,8 +28,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
30
  // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
33
  Alert: () => Alert,
34
34
  AlertDescription: () => AlertDescription,
35
35
  AlertGhost: () => AlertGhost,
@@ -49,6 +49,7 @@ __export(index_exports, {
49
49
  Chip: () => Chip,
50
50
  ChipGhost: () => ChipGhost,
51
51
  CodeBlock: () => CodeBlock,
52
+ ColumnEditor: () => ColumnEditor,
52
53
  ContentSwitcher: () => ContentSwitcher,
53
54
  ContentSwitcherGhost: () => ContentSwitcherGhost,
54
55
  ConversationGhost: () => ConversationGhost,
@@ -110,7 +111,7 @@ __export(index_exports, {
110
111
  cn: () => cn,
111
112
  useEscapeDismiss: () => useEscapeDismiss
112
113
  });
113
- module.exports = __toCommonJS(index_exports);
114
+ module.exports = __toCommonJS(src_exports);
114
115
 
115
116
  // src/components/Button/Button.tsx
116
117
  var React = __toESM(require("react"), 1);
@@ -1413,30 +1414,294 @@ function Modal({ open, onClose, title, children, danger, warn, footer, noClose,
1413
1414
  );
1414
1415
  }
1415
1416
 
1416
- // src/components/DataTable/DataTable.tsx
1417
+ // src/components/TableLayout/TableOverflowMenu.tsx
1418
+ var import_react4 = require("react");
1419
+ var import_react_dom3 = require("react-dom");
1420
+ var import_fractal_icons9 = require("@mirror-physics/fractal-icons");
1417
1421
  var import_jsx_runtime17 = require("react/jsx-runtime");
1418
- function DataTable({ columns, data, emptyMessage, header, size = "md", className }) {
1419
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: cn("fractal-datatable", `fractal-datatable--${size}`, className), children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "fractal-datatable-surface", children: [
1420
- header && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "fractal-datatable-header", children: header }),
1421
- data.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "fractal-datatable-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("table", { className: "fractal-datatable-table", children: [
1422
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("tr", { className: "fractal-datatable-thead-row", children: columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { className: "fractal-datatable-th", style: { width: col.width }, children: col.header }, col.key)) }) }),
1423
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1422
+ function TableOverflowMenu({ items, label, disabled, onSelect }) {
1423
+ const id = (0, import_react4.useId)();
1424
+ const trigger = (0, import_react4.useRef)(null);
1425
+ const panel = (0, import_react4.useRef)(null);
1426
+ const initialLast = (0, import_react4.useRef)(false);
1427
+ const [open, setOpen] = (0, import_react4.useState)(false);
1428
+ const [position, setPosition] = (0, import_react4.useState)({ top: 0, left: 0 });
1429
+ const buttons = () => [...panel.current?.querySelectorAll("button:not(:disabled)") ?? []];
1430
+ const close = (restore = true) => {
1431
+ setOpen(false);
1432
+ if (restore) trigger.current?.focus({ preventScroll: true });
1433
+ };
1434
+ useEscapeDismiss(100, () => close(), open);
1435
+ (0, import_react4.useLayoutEffect)(() => {
1436
+ if (!open || !trigger.current || !panel.current) return;
1437
+ const place = () => {
1438
+ const anchor = trigger.current.getBoundingClientRect();
1439
+ const menu = panel.current.getBoundingClientRect();
1440
+ const above = window.innerHeight - anchor.bottom < menu.height + 8 && anchor.top > window.innerHeight - anchor.bottom;
1441
+ setPosition({
1442
+ top: Math.max(8, Math.min(above ? anchor.top - menu.height - 4 : anchor.bottom + 4, window.innerHeight - menu.height - 8)),
1443
+ left: Math.max(8, Math.min(anchor.right - menu.width, window.innerWidth - menu.width - 8))
1444
+ });
1445
+ };
1446
+ place();
1447
+ const enabled = buttons();
1448
+ (initialLast.current ? enabled[enabled.length - 1] : enabled[0])?.focus({ preventScroll: true });
1449
+ if (enabled.length === 0) panel.current.focus({ preventScroll: true });
1450
+ const outside = (event) => {
1451
+ if (!panel.current?.contains(event.target) && !trigger.current?.contains(event.target)) setOpen(false);
1452
+ };
1453
+ const scroll = (event) => {
1454
+ if (!panel.current?.contains(event.target)) setOpen(false);
1455
+ };
1456
+ const observer = new ResizeObserver(place);
1457
+ observer.observe(panel.current);
1458
+ document.addEventListener("pointerdown", outside);
1459
+ window.addEventListener("scroll", scroll, true);
1460
+ window.addEventListener("resize", place);
1461
+ return () => {
1462
+ observer.disconnect();
1463
+ document.removeEventListener("pointerdown", outside);
1464
+ window.removeEventListener("scroll", scroll, true);
1465
+ window.removeEventListener("resize", place);
1466
+ };
1467
+ }, [open]);
1468
+ (0, import_react4.useLayoutEffect)(() => {
1469
+ if (disabled || items.length === 0) setOpen(false);
1470
+ }, [disabled, items.length]);
1471
+ if (items.length === 0) return null;
1472
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "fractal-table-overflow-trigger", children: [
1473
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1474
+ Button,
1475
+ {
1476
+ ref: trigger,
1477
+ buttonType: "ghost",
1478
+ controlSize: "sm",
1479
+ hasIconOnly: true,
1480
+ icon: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_fractal_icons9.Ellipsis, { "aria-hidden": true }),
1481
+ "aria-label": label,
1482
+ "aria-haspopup": "menu",
1483
+ "aria-expanded": open,
1484
+ "aria-controls": open ? id : void 0,
1485
+ disabled,
1486
+ onClick: () => {
1487
+ initialLast.current = false;
1488
+ setOpen(!open);
1489
+ },
1490
+ onKeyDown: (event) => {
1491
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
1492
+ event.preventDefault();
1493
+ initialLast.current = event.key === "ArrowUp";
1494
+ setOpen(true);
1495
+ }
1496
+ }
1497
+ }
1498
+ ),
1499
+ open && (0, import_react_dom3.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1500
+ "div",
1501
+ {
1502
+ ref: panel,
1503
+ id,
1504
+ role: "menu",
1505
+ tabIndex: -1,
1506
+ "aria-label": label,
1507
+ className: "fractal-table-overflow-menu",
1508
+ style: position,
1509
+ onClick: (event) => event.stopPropagation(),
1510
+ onKeyDown: (event) => {
1511
+ event.stopPropagation();
1512
+ const enabled = buttons(), index = enabled.indexOf(document.activeElement);
1513
+ if (event.key === "Escape") {
1514
+ event.preventDefault();
1515
+ close();
1516
+ } else if (event.key === "Tab") {
1517
+ close();
1518
+ } else if (["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) {
1519
+ event.preventDefault();
1520
+ const next = event.key === "Home" ? 0 : event.key === "End" ? enabled.length - 1 : (index + (event.key === "ArrowDown" ? 1 : -1) + enabled.length) % enabled.length;
1521
+ enabled[next]?.focus();
1522
+ } else if (event.key.length === 1 && /\S/.test(event.key)) {
1523
+ const ordered = [...enabled.slice(index + 1), ...enabled.slice(0, index + 1)];
1524
+ ordered.find((button) => button.textContent?.trim().toLocaleLowerCase().startsWith(event.key.toLocaleLowerCase()))?.focus();
1525
+ }
1526
+ },
1527
+ children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1528
+ "button",
1529
+ {
1530
+ type: "button",
1531
+ role: "menuitem",
1532
+ tabIndex: -1,
1533
+ disabled: item.disabled,
1534
+ onClick: () => {
1535
+ onSelect(item.value);
1536
+ close();
1537
+ },
1538
+ children: [
1539
+ item.icon && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { "aria-hidden": "true", children: item.icon }),
1540
+ item.label
1541
+ ]
1542
+ },
1543
+ item.value
1544
+ ))
1545
+ }
1546
+ ), document.body)
1547
+ ] });
1548
+ }
1549
+
1550
+ // src/components/TableLayout/TableLayout.tsx
1551
+ var import_react5 = require("react");
1552
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1553
+ var length = (value) => typeof value === "number" ? `${value}px` : value;
1554
+ function columnStyle(column) {
1555
+ return {
1556
+ width: column.width,
1557
+ "--table-column-width": !column.width || column.width.includes("%") ? void 0 : `calc(${column.width} - 1px)`,
1558
+ "--table-column-min": length(column.minWidth),
1559
+ "--table-column-max": length(column.maxWidth)
1560
+ };
1561
+ }
1562
+ function useOverflow() {
1563
+ const ref = (0, import_react5.useRef)(null);
1564
+ const [overflow, setOverflow] = (0, import_react5.useState)(false);
1565
+ (0, import_react5.useLayoutEffect)(() => {
1566
+ const el = ref.current;
1567
+ if (!el) return;
1568
+ const measure = () => setOverflow(el.scrollWidth > el.clientWidth + 1 || el.scrollHeight > el.clientHeight + 1);
1569
+ const resize = new ResizeObserver(measure);
1570
+ const observe = () => {
1571
+ resize.disconnect();
1572
+ resize.observe(el);
1573
+ for (const child of el.children) resize.observe(child);
1574
+ measure();
1575
+ };
1576
+ const mutation = new MutationObserver(observe);
1577
+ mutation.observe(el, { childList: true, subtree: true, characterData: true });
1578
+ observe();
1579
+ return () => {
1580
+ resize.disconnect();
1581
+ mutation.disconnect();
1582
+ };
1583
+ }, []);
1584
+ return { ref, overflow };
1585
+ }
1586
+ function TableViewport({ children, maxHeight, label }) {
1587
+ const { ref, overflow } = useOverflow();
1588
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref, className: "fractal-table-viewport", style: { maxHeight }, tabIndex: overflow ? 0 : void 0, role: overflow ? "region" : void 0, "aria-label": overflow ? `${label}, scrollable table` : void 0, children });
1589
+ }
1590
+ function TableCellContent({ children, label }) {
1591
+ const { ref, overflow } = useOverflow();
1592
+ (0, import_react5.useLayoutEffect)(() => {
1593
+ const cell = ref.current;
1594
+ if (!cell) return;
1595
+ const wheel = (event) => {
1596
+ if (event.defaultPrevented || event.ctrlKey || !event.cancelable || Math.abs(event.deltaX) <= Math.abs(event.deltaY)) return;
1597
+ const range = cell.scrollWidth - cell.clientWidth;
1598
+ if (range <= 1) return;
1599
+ const rtl = getComputedStyle(cell).direction === "rtl";
1600
+ const left = rtl ? -range : 0, right = rtl ? 0 : range;
1601
+ if (event.deltaX > 0 ? cell.scrollLeft < right - 1 : cell.scrollLeft > left + 1) return;
1602
+ const table = cell.closest(".fractal-table-viewport");
1603
+ if (!table) return;
1604
+ const tableRange = table.scrollWidth - table.clientWidth;
1605
+ const tableRtl = getComputedStyle(table).direction === "rtl";
1606
+ const tableLeft = tableRtl ? -tableRange : 0, tableRight = tableRtl ? 0 : tableRange;
1607
+ if (event.deltaX > 0 ? table.scrollLeft >= tableRight - 1 : table.scrollLeft <= tableLeft + 1) return;
1608
+ const unit = event.deltaMode === 1 ? parseFloat(getComputedStyle(cell).lineHeight) || 16 : event.deltaMode === 2 ? table.clientWidth : 1;
1609
+ event.preventDefault();
1610
+ table.scrollLeft += event.deltaX * unit;
1611
+ };
1612
+ cell.addEventListener("wheel", wheel, { passive: false });
1613
+ return () => cell.removeEventListener("wheel", wheel);
1614
+ }, [ref]);
1615
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1616
+ "div",
1617
+ {
1618
+ ref,
1619
+ className: "fractal-table-cell-scroll",
1620
+ "data-overflow": overflow || void 0,
1621
+ tabIndex: overflow ? 0 : void 0,
1622
+ role: overflow ? "region" : void 0,
1623
+ "aria-label": overflow ? `${label}, scroll horizontally for full content` : void 0,
1624
+ onKeyDown: (event) => {
1625
+ if (!overflow || event.target !== event.currentTarget) return;
1626
+ if (["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) {
1627
+ event.preventDefault();
1628
+ event.stopPropagation();
1629
+ const el = event.currentTarget;
1630
+ if (event.key === "ArrowLeft" || event.key === "ArrowRight") el.scrollBy({ left: event.key === "ArrowLeft" ? -40 : 40 });
1631
+ else el.scrollLeft = event.key === "Home" ? 0 : el.scrollWidth * (getComputedStyle(el).direction === "rtl" ? -1 : 1);
1632
+ } else if (event.key === " " || event.key === "Enter") event.stopPropagation();
1633
+ },
1634
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "fractal-table-cell-content", children })
1635
+ }
1636
+ );
1637
+ }
1638
+
1639
+ // src/components/DataTable/DataTable.tsx
1640
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1641
+ function DataTable({ columns, data, emptyMessage, header, size = "md", className, overflowMenu, layout = "fill", stickyHeader = false, stickyFirstColumn = false, maxHeight, "aria-label": ariaLabel = "Data table" }) {
1642
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: cn("fractal-table-layout", "fractal-datatable", `fractal-datatable--${size}`, className), "data-overflow-menu": Boolean(overflowMenu), "data-table-layout": layout, "data-sticky-first": stickyFirstColumn, "data-sticky-header": stickyHeader, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "fractal-datatable-surface", children: [
1643
+ header && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "fractal-datatable-header", children: header }),
1644
+ data.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "fractal-datatable-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(TableViewport, { maxHeight, label: ariaLabel, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("table", { "aria-label": ariaLabel, className: "fractal-datatable-table", children: [
1645
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("tr", { className: "fractal-datatable-thead-row", children: [
1646
+ columns.map((col, columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("th", { scope: "col", className: "fractal-datatable-th", style: columnStyle(col), "data-pinned-column": columnIndex === 0 ? "data" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(TableCellContent, { label: `${col.header} column`, children: col.header }) }, col.key)),
1647
+ overflowMenu && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("th", { scope: "col", "aria-label": "Row actions", className: "fractal-datatable-th fractal-table-overflow-cell", "data-pinned-column": "overflow" })
1648
+ ] }) }),
1649
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
1424
1650
  "tr",
1425
1651
  {
1426
1652
  className: "fractal-datatable-row",
1427
- children: columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "fractal-datatable-td", style: { width: col.width }, children: col.render(row, rowIndex) }, col.key))
1653
+ children: [
1654
+ columns.map((col, columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("td", { className: "fractal-datatable-td", style: columnStyle(col), "data-pinned-column": columnIndex === 0 ? "data" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(TableCellContent, { label: `${col.header || "Value"}, row ${rowIndex + 1}`, children: col.render(row, rowIndex) }) }, col.key)),
1655
+ overflowMenu && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("td", { className: "fractal-datatable-td fractal-table-overflow-cell", "data-pinned-column": "overflow", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(TableOverflowMenu, { items: overflowMenu.items(row, rowIndex), label: overflowMenu.getLabel?.(row, rowIndex) ?? `Actions for row ${rowIndex + 1}`, disabled: overflowMenu.isDisabled?.(row, rowIndex), onSelect: (value) => overflowMenu.onSelect(value, row, rowIndex) }) })
1656
+ ]
1428
1657
  },
1429
1658
  rowIndex
1430
1659
  )) })
1431
- ] })
1660
+ ] }) })
1432
1661
  ] }) });
1433
1662
  }
1434
1663
 
1435
1664
  // src/components/Dropdown/Dropdown.tsx
1436
1665
  var React12 = __toESM(require("react"), 1);
1437
- var import_fractal_icons9 = require("@mirror-physics/fractal-icons");
1438
- var import_jsx_runtime18 = require("react/jsx-runtime");
1439
- var MARGIN = 4;
1666
+ var import_fractal_icons10 = require("@mirror-physics/fractal-icons");
1667
+
1668
+ // src/components/Dropdown/position.ts
1669
+ function positionDropdown(trigger, panel, bounds, direction, align) {
1670
+ const gap = 4;
1671
+ const width = Math.min(panel.width, Math.max(0, bounds.right - bounds.left));
1672
+ let maxHeight = Math.max(0, bounds.bottom - bounds.top);
1673
+ let left;
1674
+ let top;
1675
+ if (direction === "left" || direction === "right") {
1676
+ const after = bounds.right - trigger.right - gap;
1677
+ const before = trigger.left - bounds.left - gap;
1678
+ let placeRight = direction === "right";
1679
+ if (width > (placeRight ? after : before) && (placeRight ? before > after : after > before)) {
1680
+ placeRight = !placeRight;
1681
+ }
1682
+ left = placeRight ? trigger.right + gap : trigger.left - width - gap;
1683
+ top = trigger.top;
1684
+ } else {
1685
+ const below = Math.max(0, bounds.bottom - trigger.bottom - gap);
1686
+ const above = Math.max(0, trigger.top - bounds.top - gap);
1687
+ let placeAbove = direction === "up";
1688
+ if (panel.height > (placeAbove ? above : below) && (placeAbove ? below > above : above > below)) {
1689
+ placeAbove = !placeAbove;
1690
+ }
1691
+ maxHeight = Math.min(maxHeight, placeAbove ? above : below);
1692
+ top = placeAbove ? trigger.top - Math.min(panel.height, maxHeight) - gap : trigger.bottom + gap;
1693
+ left = align === "right" ? trigger.right - width : trigger.left;
1694
+ }
1695
+ const height = Math.min(panel.height, maxHeight);
1696
+ return {
1697
+ left: Math.max(bounds.left, Math.min(left, bounds.right - width)),
1698
+ top: Math.max(bounds.top, Math.min(top, bounds.bottom - height)),
1699
+ maxHeight
1700
+ };
1701
+ }
1702
+
1703
+ // src/components/Dropdown/Dropdown.tsx
1704
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1440
1705
  function Dropdown({
1441
1706
  items,
1442
1707
  selectedValue,
@@ -1444,6 +1709,8 @@ function Dropdown({
1444
1709
  triggerLabel = "Options",
1445
1710
  direction = "down",
1446
1711
  align = "left",
1712
+ maxHeight = 320,
1713
+ collisionPadding = 8,
1447
1714
  triggerOrientation = "horizontal",
1448
1715
  triggerContent,
1449
1716
  label,
@@ -1461,69 +1728,98 @@ function Dropdown({
1461
1728
  }) {
1462
1729
  const size = controlSize ?? sizeAlias;
1463
1730
  const [open, setOpen] = React12.useState(false);
1731
+ const rootRef = React12.useRef(null);
1732
+ const menuId = React12.useId();
1733
+ const focusOnOpen = React12.useRef(false);
1464
1734
  const triggerRef = React12.useRef(null);
1465
1735
  const panelRef = React12.useRef(null);
1466
1736
  const [panelStyle, setPanelStyle] = React12.useState({});
1467
1737
  const visibleItems = items.filter(
1468
1738
  (item) => item.icon != null || item.description != null && (typeof item.description === "string" ? item.description !== "" : true)
1469
1739
  );
1470
- if (visibleItems.length === 0) return null;
1471
1740
  const selectableItems = visibleItems.filter((i) => i.kind !== "header");
1472
1741
  const selected = items.find((i) => i.value === selectedValue && i.kind !== "header") ?? selectableItems[0] ?? visibleItems[0];
1473
- const isUp = direction === "up";
1474
- const isHorizontalDirection = direction === "left" || direction === "right";
1475
- const ChevronIcon = direction === "up" ? import_fractal_icons9.ChevronUp : direction === "left" ? import_fractal_icons9.ChevronLeft : direction === "right" ? import_fractal_icons9.ChevronRight : import_fractal_icons9.ChevronDown;
1742
+ const ChevronIcon = direction === "up" ? import_fractal_icons10.ChevronUp : direction === "left" ? import_fractal_icons10.ChevronLeft : direction === "right" ? import_fractal_icons10.ChevronRight : import_fractal_icons10.ChevronDown;
1743
+ useEscapeDismiss(100, () => {
1744
+ setOpen(false);
1745
+ triggerRef.current?.focus();
1746
+ }, open);
1476
1747
  React12.useLayoutEffect(() => {
1477
- if (!open || !triggerRef.current || !panelRef.current) return;
1478
- const triggerRect = triggerRef.current.getBoundingClientRect();
1479
- const panelRect = panelRef.current.getBoundingClientRect();
1480
- const vw = window.innerWidth;
1481
- const vh = window.innerHeight;
1482
- let top;
1483
- let left;
1484
- let right;
1485
- if (isHorizontalDirection) {
1486
- const spaceRight = vw - triggerRect.right - MARGIN;
1487
- const spaceLeft = triggerRect.left - MARGIN;
1488
- let placeRight = direction === "right";
1489
- if (placeRight && panelRect.width > spaceRight && spaceLeft > spaceRight) {
1490
- placeRight = false;
1491
- } else if (!placeRight && panelRect.width > spaceLeft && spaceRight > spaceLeft) {
1492
- placeRight = true;
1493
- }
1494
- left = placeRight ? triggerRect.width + MARGIN : -(panelRect.width + MARGIN);
1495
- top = 0;
1496
- const panelBottom = triggerRect.top + panelRect.height;
1497
- if (panelBottom > vh - MARGIN) top -= panelBottom - (vh - MARGIN);
1498
- if (triggerRect.top + top < MARGIN) top += MARGIN - (triggerRect.top + top);
1499
- } else {
1500
- const spaceBelow = vh - triggerRect.bottom - MARGIN;
1501
- const spaceAbove = triggerRect.top - MARGIN;
1502
- let placeAbove = isUp;
1503
- if (placeAbove && panelRect.height > spaceAbove && spaceBelow > spaceAbove) {
1504
- placeAbove = false;
1505
- } else if (!placeAbove && panelRect.height > spaceBelow && spaceAbove > spaceBelow) {
1506
- placeAbove = true;
1507
- }
1508
- top = placeAbove ? -(panelRect.height + MARGIN) : triggerRect.height + MARGIN;
1509
- if (align === "right") {
1510
- right = 0;
1511
- const panelLeft = triggerRect.right - panelRect.width;
1512
- if (panelLeft < 0) {
1513
- right = void 0;
1514
- left = 0;
1748
+ const root = rootRef.current;
1749
+ const trigger = triggerRef.current;
1750
+ const panel = panelRef.current;
1751
+ if (!open || !root || !trigger || !panel) return;
1752
+ const updatePosition = () => {
1753
+ const viewport = window.visualViewport;
1754
+ const bounds = {
1755
+ left: viewport?.offsetLeft ?? 0,
1756
+ top: viewport?.offsetTop ?? 0,
1757
+ right: (viewport?.offsetLeft ?? 0) + (viewport?.width ?? document.documentElement.clientWidth),
1758
+ bottom: (viewport?.offsetTop ?? 0) + (viewport?.height ?? document.documentElement.clientHeight)
1759
+ };
1760
+ for (let ancestor = root.parentElement; ancestor; ancestor = ancestor.parentElement) {
1761
+ const style = getComputedStyle(ancestor);
1762
+ const rect = ancestor.getBoundingClientRect();
1763
+ if (/(auto|scroll|hidden|clip)/.test(style.overflowX)) {
1764
+ bounds.left = Math.max(bounds.left, rect.left + ancestor.clientLeft);
1765
+ bounds.right = Math.min(bounds.right, rect.left + ancestor.clientLeft + ancestor.clientWidth);
1515
1766
  }
1516
- } else {
1517
- left = 0;
1518
- const panelRight = triggerRect.left + panelRect.width;
1519
- if (panelRight > vw) {
1520
- left = void 0;
1521
- right = 0;
1767
+ if (/(auto|scroll|hidden|clip)/.test(style.overflowY)) {
1768
+ bounds.top = Math.max(bounds.top, rect.top + ancestor.clientTop);
1769
+ bounds.bottom = Math.min(bounds.bottom, rect.top + ancestor.clientTop + ancestor.clientHeight);
1522
1770
  }
1523
1771
  }
1772
+ const padding = Math.max(0, collisionPadding);
1773
+ bounds.left += padding;
1774
+ bounds.right = Math.max(bounds.left, bounds.right - padding);
1775
+ bounds.top += padding;
1776
+ bounds.bottom = Math.max(bounds.top, bounds.bottom - padding);
1777
+ const triggerRect = trigger.getBoundingClientRect();
1778
+ const rootRect = root.getBoundingClientRect();
1779
+ const availableWidth = bounds.right - bounds.left;
1780
+ const heightLimit = Math.max(0, maxHeight);
1781
+ panel.style.maxWidth = `${availableWidth}px`;
1782
+ panel.style.minWidth = `${Math.min(triggerRect.width, availableWidth)}px`;
1783
+ const panelRect = panel.getBoundingClientRect();
1784
+ const desiredHeight = Math.min(heightLimit, panel.scrollHeight + panel.offsetHeight - panel.clientHeight);
1785
+ const position = positionDropdown(triggerRect, { width: panelRect.width, height: desiredHeight }, bounds, direction, align);
1786
+ const nextStyle = {
1787
+ left: position.left - rootRect.left - root.clientLeft + root.scrollLeft,
1788
+ top: position.top - rootRect.top - root.clientTop + root.scrollTop,
1789
+ minWidth: Math.min(triggerRect.width, availableWidth),
1790
+ maxWidth: availableWidth,
1791
+ maxHeight: Math.min(heightLimit, position.maxHeight)
1792
+ };
1793
+ for (const key of Object.keys(nextStyle)) {
1794
+ panel.style[key] = `${nextStyle[key]}px`;
1795
+ }
1796
+ setPanelStyle((previous) => Object.entries(nextStyle).every(([key, value]) => previous[key] === value) ? previous : nextStyle);
1797
+ };
1798
+ updatePosition();
1799
+ if (focusOnOpen.current) {
1800
+ focusOnOpen.current = false;
1801
+ const option = panel.querySelector('[aria-selected="true"]') ?? panel.querySelector('[role="option"]');
1802
+ option?.focus({ preventScroll: true });
1803
+ option?.scrollIntoView({ block: "nearest" });
1804
+ }
1805
+ const resize = new ResizeObserver(updatePosition);
1806
+ resize.observe(root);
1807
+ resize.observe(panel);
1808
+ for (let ancestor = root.parentElement; ancestor; ancestor = ancestor.parentElement) {
1809
+ resize.observe(ancestor);
1524
1810
  }
1525
- setPanelStyle({ top, left, right });
1526
- }, [open, direction, isHorizontalDirection, isUp, align]);
1811
+ window.addEventListener("resize", updatePosition);
1812
+ window.addEventListener("scroll", updatePosition, true);
1813
+ window.visualViewport?.addEventListener("resize", updatePosition);
1814
+ window.visualViewport?.addEventListener("scroll", updatePosition);
1815
+ return () => {
1816
+ resize.disconnect();
1817
+ window.removeEventListener("resize", updatePosition);
1818
+ window.removeEventListener("scroll", updatePosition, true);
1819
+ window.visualViewport?.removeEventListener("resize", updatePosition);
1820
+ window.visualViewport?.removeEventListener("scroll", updatePosition);
1821
+ };
1822
+ }, [open, direction, align, maxHeight, collisionPadding, items, label]);
1527
1823
  React12.useEffect(() => {
1528
1824
  if (!open) return;
1529
1825
  const handleClickOutside = (e) => {
@@ -1534,8 +1830,9 @@ function Dropdown({
1534
1830
  document.addEventListener("mousedown", handleClickOutside);
1535
1831
  return () => document.removeEventListener("mousedown", handleClickOutside);
1536
1832
  }, [open]);
1537
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { "data-control-size": size, className: cn("fractal-dropdown", className), children: [
1538
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1833
+ if (visibleItems.length === 0) return null;
1834
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { ref: rootRef, "data-control-size": size, className: cn("fractal-dropdown", className), children: [
1835
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1539
1836
  "button",
1540
1837
  {
1541
1838
  ref: triggerRef,
@@ -1545,6 +1842,20 @@ function Dropdown({
1545
1842
  disabled,
1546
1843
  "aria-haspopup": "listbox",
1547
1844
  "aria-expanded": open,
1845
+ "aria-controls": open ? menuId : void 0,
1846
+ onKeyDown: (event) => {
1847
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
1848
+ event.preventDefault();
1849
+ if (open) {
1850
+ const option = panelRef.current?.querySelector('[aria-selected="true"]') ?? panelRef.current?.querySelector('[role="option"]');
1851
+ option?.focus({ preventScroll: true });
1852
+ option?.scrollIntoView({ block: "nearest" });
1853
+ } else {
1854
+ focusOnOpen.current = true;
1855
+ setOpen(true);
1856
+ }
1857
+ }
1858
+ },
1548
1859
  "aria-label": triggerLabel,
1549
1860
  onClick: () => setOpen((o) => !o),
1550
1861
  className: cn(
@@ -1557,38 +1868,48 @@ function Dropdown({
1557
1868
  triggerClassName
1558
1869
  ),
1559
1870
  children: [
1560
- triggerContent != null ? triggerContent : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
1561
- selected.icon != null && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "fractal-dropdown-icon fractal-dropdown-icon--trigger", children: selected.icon }),
1562
- selected.description != null && (typeof selected.description !== "string" || selected.description !== "") && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "fractal-dropdown-selected-text", children: selected.description })
1871
+ triggerContent != null ? triggerContent : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
1872
+ selected.icon != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-icon fractal-dropdown-icon--trigger", children: selected.icon }),
1873
+ selected.description != null && (typeof selected.description !== "string" || selected.description !== "") && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-selected-text", children: selected.description })
1563
1874
  ] }),
1564
- !noChevron && !iconOnly && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "fractal-dropdown-chevron", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChevronIcon, { size: size === "sm" ? 14 : 16 }) })
1875
+ !noChevron && !iconOnly && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-chevron", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ChevronIcon, { size: size === "sm" ? 14 : 16 }) })
1565
1876
  ]
1566
1877
  }
1567
1878
  ),
1568
- open && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1879
+ open && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1569
1880
  "div",
1570
1881
  {
1571
1882
  ref: panelRef,
1883
+ id: menuId,
1884
+ onKeyDown: (event) => {
1885
+ if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
1886
+ event.preventDefault();
1887
+ const options = Array.from(panelRef.current?.querySelectorAll('[role="option"]') ?? []);
1888
+ const current = options.indexOf(document.activeElement);
1889
+ const index = event.key === "Home" ? 0 : event.key === "End" ? options.length - 1 : (current + (event.key === "ArrowDown" ? 1 : -1) + options.length) % options.length;
1890
+ options[index]?.focus({ preventScroll: true });
1891
+ options[index]?.scrollIntoView({ block: "nearest" });
1892
+ },
1572
1893
  role: "listbox",
1573
1894
  "aria-label": triggerLabel,
1574
1895
  className: cn("fractal-dropdown-panel", `fractal-dropdown-panel--${size}`, panelClassName),
1575
1896
  style: panelStyle,
1576
1897
  children: [
1577
- label != null && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "fractal-dropdown-label", children: label }),
1898
+ label != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "fractal-dropdown-label", children: label }),
1578
1899
  visibleItems.map((item, index) => {
1579
- const topDivider = item.border === "top" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "fractal-dropdown-separator" }) : null;
1580
- const bottomDivider = item.border === "bottom" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "fractal-dropdown-separator" }) : null;
1900
+ const topDivider = item.border === "top" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "fractal-dropdown-separator" }) : null;
1901
+ const bottomDivider = item.border === "bottom" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "fractal-dropdown-separator" }) : null;
1581
1902
  if (item.kind === "header") {
1582
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(React12.Fragment, { children: [
1903
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(React12.Fragment, { children: [
1583
1904
  topDivider,
1584
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { role: "presentation", className: "fractal-dropdown-label", children: item.description }),
1905
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { role: "presentation", className: "fractal-dropdown-label", children: item.description }),
1585
1906
  bottomDivider
1586
1907
  ] }, `header-${index}`);
1587
1908
  }
1588
1909
  const isSelected = item.value === selectedValue;
1589
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(React12.Fragment, { children: [
1910
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(React12.Fragment, { children: [
1590
1911
  topDivider,
1591
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1912
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1592
1913
  "button",
1593
1914
  {
1594
1915
  type: "button",
@@ -1596,13 +1917,16 @@ function Dropdown({
1596
1917
  "aria-selected": isSelected,
1597
1918
  onClick: () => {
1598
1919
  onSelect(item.value);
1599
- if (closeOnSelect) setOpen(false);
1920
+ if (closeOnSelect) {
1921
+ setOpen(false);
1922
+ triggerRef.current?.focus();
1923
+ }
1600
1924
  },
1601
1925
  className: "fractal-dropdown-option",
1602
1926
  children: [
1603
- item.icon != null && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "fractal-dropdown-icon", children: item.icon }),
1604
- item.description != null && (typeof item.description !== "string" || item.description !== "") && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "fractal-dropdown-option-text", children: item.description }),
1605
- item.trailing != null && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "fractal-dropdown-trailing", children: item.trailing })
1927
+ item.icon != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-icon", children: item.icon }),
1928
+ item.description != null && (typeof item.description !== "string" || item.description !== "") && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-option-text", children: item.description }),
1929
+ item.trailing != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-trailing", children: item.trailing })
1606
1930
  ]
1607
1931
  }
1608
1932
  ),
@@ -1617,9 +1941,9 @@ function Dropdown({
1617
1941
 
1618
1942
  // src/components/Link/Link.tsx
1619
1943
  var React13 = __toESM(require("react"), 1);
1620
- var import_jsx_runtime19 = require("react/jsx-runtime");
1944
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1621
1945
  var Link = React13.forwardRef(
1622
- ({ className, theme = "default", size = "md", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1946
+ ({ className, theme = "default", size = "md", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1623
1947
  "a",
1624
1948
  {
1625
1949
  ref,
@@ -1632,13 +1956,12 @@ Link.displayName = "Link";
1632
1956
 
1633
1957
  // src/components/Chip/Chip.tsx
1634
1958
  var React14 = __toESM(require("react"), 1);
1635
- var import_jsx_runtime20 = require("react/jsx-runtime");
1959
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1636
1960
  var Chip = React14.forwardRef(
1637
- ({ className, tone = "neutral", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1961
+ ({ className, tone = "neutral", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1638
1962
  "span",
1639
1963
  {
1640
1964
  ref,
1641
- "data-fractal-border-surface": tone === "neutral" ? "surface-2" : void 0,
1642
1965
  className: cn("fractal-chip", `fractal-chip--${tone}`, className),
1643
1966
  ...props
1644
1967
  }
@@ -1647,8 +1970,8 @@ var Chip = React14.forwardRef(
1647
1970
  Chip.displayName = "Chip";
1648
1971
 
1649
1972
  // src/components/Checkbox/Checkbox.tsx
1650
- var import_fractal_icons10 = require("@mirror-physics/fractal-icons");
1651
- var import_jsx_runtime21 = require("react/jsx-runtime");
1973
+ var import_fractal_icons11 = require("@mirror-physics/fractal-icons");
1974
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1652
1975
  function Checkbox({
1653
1976
  checked,
1654
1977
  indeterminate = false,
@@ -1659,7 +1982,7 @@ function Checkbox({
1659
1982
  onKeyDown,
1660
1983
  ...props
1661
1984
  }) {
1662
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1985
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1663
1986
  "button",
1664
1987
  {
1665
1988
  ...props,
@@ -1681,16 +2004,16 @@ function Checkbox({
1681
2004
  onKeyDown?.(event);
1682
2005
  if (event.key === " " || event.key === "Enter") event.stopPropagation();
1683
2006
  },
1684
- children: indeterminate ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "fractal-checkbox__indeterminate-icon", "aria-hidden": "true", children: [
1685
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_fractal_icons10.Checkbox, { size: 18 }),
1686
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_fractal_icons10.Minus, { className: "fractal-checkbox__indeterminate-mark", size: 12 })
1687
- ] }) : checked ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_fractal_icons10.CheckboxChecked, { "aria-hidden": "true", size: 18 }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_fractal_icons10.Checkbox, { "aria-hidden": "true", size: 18 })
2007
+ children: indeterminate ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "fractal-checkbox__indeterminate-icon", "aria-hidden": "true", children: [
2008
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_fractal_icons11.Checkbox, { size: 18 }),
2009
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_fractal_icons11.Minus, { className: "fractal-checkbox__indeterminate-mark", size: 12 })
2010
+ ] }) : checked ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_fractal_icons11.CheckboxChecked, { "aria-hidden": "true", size: 18 }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_fractal_icons11.Checkbox, { "aria-hidden": "true", size: 18 })
1688
2011
  }
1689
2012
  );
1690
2013
  }
1691
2014
 
1692
2015
  // src/components/Radio/Radio.tsx
1693
- var import_jsx_runtime22 = require("react/jsx-runtime");
2016
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1694
2017
  function Radio({
1695
2018
  checked,
1696
2019
  className,
@@ -1700,8 +2023,8 @@ function Radio({
1700
2023
  onCheckedChange,
1701
2024
  ...props
1702
2025
  }) {
1703
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("label", { className: cn("fractal-radio", disabled && "fractal-radio--disabled", className), children: [
1704
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2026
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className: cn("fractal-radio", disabled && "fractal-radio--disabled", className), children: [
2027
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1705
2028
  "input",
1706
2029
  {
1707
2030
  ...props,
@@ -1712,18 +2035,18 @@ function Radio({
1712
2035
  type: "radio"
1713
2036
  }
1714
2037
  ),
1715
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": "true", className: "fractal-radio__control", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "fractal-radio__dot" }) }),
1716
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "fractal-radio__content", children: [
1717
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "fractal-radio__label", children: label }),
1718
- description && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "fractal-radio__description", children: description })
2038
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { "aria-hidden": "true", className: "fractal-radio__control", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "fractal-radio__dot" }) }),
2039
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "fractal-radio__content", children: [
2040
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "fractal-radio__label", children: label }),
2041
+ description && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "fractal-radio__description", children: description })
1719
2042
  ] })
1720
2043
  ] });
1721
2044
  }
1722
2045
 
1723
2046
  // src/components/SortableTable/SortableTable.tsx
1724
- var import_react4 = require("react");
1725
- var import_fractal_icons11 = require("@mirror-physics/fractal-icons");
1726
- var import_jsx_runtime23 = require("react/jsx-runtime");
2047
+ var import_react6 = require("react");
2048
+ var import_fractal_icons12 = require("@mirror-physics/fractal-icons");
2049
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1727
2050
  function SortableTable({
1728
2051
  columns,
1729
2052
  data,
@@ -1732,6 +2055,11 @@ function SortableTable({
1732
2055
  header,
1733
2056
  size = "md",
1734
2057
  embedded = false,
2058
+ layout = "fill",
2059
+ stickyHeader = false,
2060
+ stickyFirstColumn = false,
2061
+ maxHeight,
2062
+ overflowMenu,
1735
2063
  className,
1736
2064
  defaultSort,
1737
2065
  defaultSortKey = void 0,
@@ -1748,8 +2076,8 @@ function SortableTable({
1748
2076
  onRowDoubleClick,
1749
2077
  rowAction
1750
2078
  }) {
1751
- const [internalKey, setInternalKey] = (0, import_react4.useState)(defaultSortKey ?? null);
1752
- const [internalDirection, setInternalDirection] = (0, import_react4.useState)(defaultSortDirection);
2079
+ const [internalKey, setInternalKey] = (0, import_react6.useState)(defaultSortKey ?? null);
2080
+ const [internalDirection, setInternalDirection] = (0, import_react6.useState)(defaultSortDirection);
1753
2081
  const isControlled = controlledKey !== void 0 && controlledDirection !== void 0;
1754
2082
  const activeKey = isControlled ? controlledKey : internalKey;
1755
2083
  const activeDirection = isControlled ? controlledDirection : internalDirection;
@@ -1757,7 +2085,7 @@ function SortableTable({
1757
2085
  const baselineDirection = defaultSort?.direction ?? null;
1758
2086
  const effectiveKey = activeKey && activeDirection ? activeKey : baselineKey;
1759
2087
  const effectiveDirection = activeKey && activeDirection ? activeDirection : baselineDirection;
1760
- const sorted = (0, import_react4.useMemo)(() => {
2088
+ const sorted = (0, import_react6.useMemo)(() => {
1761
2089
  if (!effectiveKey || !effectiveDirection) return data;
1762
2090
  const col = columns.find((c) => c.key === effectiveKey);
1763
2091
  if (!col) return data;
@@ -1783,7 +2111,7 @@ function SortableTable({
1783
2111
  });
1784
2112
  return copy;
1785
2113
  }, [data, columns, effectiveKey, effectiveDirection]);
1786
- const visibleRows = (0, import_react4.useMemo)(() => {
2114
+ const visibleRows = (0, import_react6.useMemo)(() => {
1787
2115
  if (!pagination) return sorted;
1788
2116
  const pageSize = Math.max(1, pagination.pageSize);
1789
2117
  const totalPages = Math.max(1, Math.ceil(sorted.length / pageSize));
@@ -1818,16 +2146,17 @@ function SortableTable({
1818
2146
  onSortChange?.(nextKey, nextDirection);
1819
2147
  }
1820
2148
  };
1821
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cn("fractal-sortable-table", `fractal-sortable-table--${size}`, embedded && "fractal-sortable-table--embedded", className), children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "fractal-sortable-table-surface", children: [
1822
- header && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "fractal-sortable-table-header", children: header }),
1823
- visibleRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "fractal-sortable-table-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("table", { "aria-label": ariaLabel, className: "fractal-sortable-table-table", children: [
1824
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("tr", { className: "fractal-sortable-table-thead-row", children: [
1825
- selection && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2149
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: cn("fractal-table-layout", "fractal-sortable-table", `fractal-sortable-table--${size}`, embedded && "fractal-sortable-table--embedded", className), "data-overflow-menu": Boolean(overflowMenu), "data-table-layout": layout, "data-sticky-first": stickyFirstColumn, "data-sticky-header": stickyHeader, "data-table-selection": Boolean(selection), children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "fractal-sortable-table-surface", children: [
2150
+ header && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "fractal-sortable-table-header", children: header }),
2151
+ visibleRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "fractal-sortable-table-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TableViewport, { maxHeight, label: ariaLabel || "Data table", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("table", { "aria-label": ariaLabel, className: "fractal-sortable-table-table", children: [
2152
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("tr", { className: "fractal-sortable-table-thead-row", children: [
2153
+ selection && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1826
2154
  "th",
1827
2155
  {
1828
2156
  className: "fractal-sortable-table-th fractal-sortable-table-selection-cell",
2157
+ "data-pinned-column": "selection",
1829
2158
  scope: "col",
1830
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2159
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1831
2160
  Checkbox,
1832
2161
  {
1833
2162
  "aria-label": selection.selectAllLabel ?? "Select all rows",
@@ -1850,27 +2179,29 @@ function SortableTable({
1850
2179
  )
1851
2180
  }
1852
2181
  ),
1853
- columns.map((col) => {
2182
+ columns.map((col, columnIndex) => {
1854
2183
  const isActive = activeKey === col.key && activeDirection !== null;
1855
2184
  if (!col.sortable) {
1856
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2185
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1857
2186
  "th",
1858
2187
  {
1859
2188
  className: "fractal-sortable-table-th",
1860
2189
  scope: "col",
1861
- style: { width: col.width },
1862
- children: col.header
2190
+ style: columnStyle(col),
2191
+ "data-pinned-column": columnIndex === 0 ? "data" : void 0,
2192
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TableCellContent, { label: `${col.header} column`, children: col.header })
1863
2193
  },
1864
2194
  col.key
1865
2195
  );
1866
2196
  }
1867
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2197
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1868
2198
  "th",
1869
2199
  {
1870
2200
  className: "fractal-sortable-table-th fractal-sortable-table-th--sortable",
1871
2201
  scope: "col",
1872
- style: { width: col.width },
1873
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2202
+ style: columnStyle(col),
2203
+ "data-pinned-column": columnIndex === 0 ? "data" : void 0,
2204
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TableCellContent, { label: `${col.header} column`, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1874
2205
  "button",
1875
2206
  {
1876
2207
  type: "button",
@@ -1881,17 +2212,18 @@ function SortableTable({
1881
2212
  onClick: () => handleSortClick(col.key),
1882
2213
  "aria-sort": isActive ? activeDirection === "asc" ? "ascending" : "descending" : "none",
1883
2214
  children: [
1884
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: col.header }),
1885
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "fractal-sortable-table-sort-icon", "aria-hidden": "true", children: isActive && activeDirection === "asc" ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_fractal_icons11.ArrowUp, { size: 14 }) : isActive && activeDirection === "desc" ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_fractal_icons11.ArrowDown, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_fractal_icons11.ArrowsUpDown, { size: 14 }) })
2215
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: col.header }),
2216
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "fractal-sortable-table-sort-icon", "aria-hidden": "true", children: isActive && activeDirection === "asc" ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_fractal_icons12.ArrowUp, { size: 14 }) : isActive && activeDirection === "desc" ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_fractal_icons12.ArrowDown, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_fractal_icons12.ArrowsUpDown, { size: 14 }) })
1886
2217
  ]
1887
2218
  }
1888
- )
2219
+ ) })
1889
2220
  },
1890
2221
  col.key
1891
2222
  );
1892
- })
2223
+ }),
2224
+ overflowMenu && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("th", { scope: "col", "aria-label": "Row actions", className: "fractal-sortable-table-th fractal-table-overflow-cell", "data-pinned-column": "overflow" })
1893
2225
  ] }) }),
1894
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("tbody", { children: visibleRows.map((row, rowIndex) => {
2226
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("tbody", { children: visibleRows.map((row, rowIndex) => {
1895
2227
  const highlighted = highlightRow?.(row, rowIndex) ?? false;
1896
2228
  const disabled = isRowDisabled?.(row) ?? false;
1897
2229
  const selectionDisabled = disabled || (isRowSelectionDisabled?.(row) ?? false);
@@ -1900,7 +2232,7 @@ function SortableTable({
1900
2232
  label: rowAction.label(row, rowIndex),
1901
2233
  icon: rowAction.icon(row, rowIndex)
1902
2234
  } : null;
1903
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2235
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1904
2236
  "tr",
1905
2237
  {
1906
2238
  className: cn(
@@ -1924,7 +2256,7 @@ function SortableTable({
1924
2256
  role: interactive ? "button" : void 0,
1925
2257
  tabIndex: interactive ? 0 : void 0,
1926
2258
  children: [
1927
- selection && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "fractal-sortable-table-td fractal-sortable-table-selection-cell", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2259
+ selection && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("td", { className: "fractal-sortable-table-td fractal-sortable-table-selection-cell", "data-pinned-column": "selection", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1928
2260
  Checkbox,
1929
2261
  {
1930
2262
  "aria-label": selection.getRowLabel(row),
@@ -1936,33 +2268,46 @@ function SortableTable({
1936
2268
  }
1937
2269
  }
1938
2270
  ) }),
1939
- columns.map((col, columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2271
+ columns.map((col, columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1940
2272
  "td",
1941
2273
  {
1942
2274
  className: cn(
1943
2275
  "fractal-sortable-table-td",
1944
2276
  action && columnIndex === columns.length - 1 && "fractal-sortable-table-td--row-action"
1945
2277
  ),
1946
- style: { width: col.width, textAlign: col.align ?? "left" },
2278
+ style: { ...columnStyle(col), textAlign: col.align ?? "left" },
2279
+ "data-column-align": col.align,
2280
+ "data-pinned-column": columnIndex === 0 ? "data" : void 0,
1947
2281
  children: [
1948
- col.render(row, rowIndex),
1949
- action && columnIndex === columns.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "fractal-sortable-table-row-action", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "fractal-sortable-table-row-action__icon", children: action.icon }) })
2282
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TableCellContent, { label: `${col.header || "Value"}, row ${rowIndex + 1}`, children: col.render(row, rowIndex) }),
2283
+ action && columnIndex === columns.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "fractal-sortable-table-row-action", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "fractal-sortable-table-row-action__icon", children: action.icon }) })
1950
2284
  ]
1951
2285
  },
1952
2286
  col.key
1953
- ))
2287
+ )),
2288
+ overflowMenu && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2289
+ "td",
2290
+ {
2291
+ className: "fractal-sortable-table-td fractal-table-overflow-cell",
2292
+ "data-pinned-column": "overflow",
2293
+ onClick: (event) => event.stopPropagation(),
2294
+ onDoubleClick: (event) => event.stopPropagation(),
2295
+ onKeyDown: (event) => event.stopPropagation(),
2296
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TableOverflowMenu, { items: overflowMenu.items(row, rowIndex), label: overflowMenu.getLabel?.(row, rowIndex) ?? `Actions for row ${rowIndex + 1}`, disabled: disabled || overflowMenu.isDisabled?.(row, rowIndex), onSelect: (value) => overflowMenu.onSelect(value, row, rowIndex) })
2297
+ }
2298
+ )
1954
2299
  ]
1955
2300
  },
1956
2301
  rowIndex
1957
2302
  );
1958
2303
  }) })
1959
- ] })
2304
+ ] }) })
1960
2305
  ] }) });
1961
2306
  }
1962
2307
 
1963
2308
  // src/components/Tabs/Tabs.tsx
1964
- var import_react5 = require("react");
1965
- var import_jsx_runtime24 = require("react/jsx-runtime");
2309
+ var import_react7 = require("react");
2310
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1966
2311
  function Tabs({
1967
2312
  items,
1968
2313
  defaultValue,
@@ -1973,16 +2318,16 @@ function Tabs({
1973
2318
  divider = false,
1974
2319
  variant = "line"
1975
2320
  }) {
1976
- const reactId = (0, import_react5.useId)();
1977
- const [internalValue, setInternalValue] = (0, import_react5.useState)(
2321
+ const reactId = (0, import_react7.useId)();
2322
+ const [internalValue, setInternalValue] = (0, import_react7.useState)(
1978
2323
  defaultValue ?? items.find((i) => !i.disabled)?.id ?? items[0]?.id ?? ""
1979
2324
  );
1980
2325
  const isControlled = controlledValue !== void 0;
1981
2326
  const activeId = isControlled ? controlledValue : internalValue;
1982
- const tabListRef = (0, import_react5.useRef)(null);
1983
- const tabRefs = (0, import_react5.useRef)({});
1984
- const [indicator, setIndicator] = (0, import_react5.useState)(null);
1985
- (0, import_react5.useLayoutEffect)(() => {
2327
+ const tabListRef = (0, import_react7.useRef)(null);
2328
+ const tabRefs = (0, import_react7.useRef)({});
2329
+ const [indicator, setIndicator] = (0, import_react7.useState)(null);
2330
+ (0, import_react7.useLayoutEffect)(() => {
1986
2331
  const updateIndicator = () => {
1987
2332
  const activeTab = tabRefs.current[activeId];
1988
2333
  if (!activeTab) return;
@@ -2033,8 +2378,8 @@ function Tabs({
2033
2378
  selectTab(nextId2);
2034
2379
  requestAnimationFrame(() => tabRefs.current[nextId2]?.focus());
2035
2380
  };
2036
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: cn("fractal-tabs", `fractal-tabs--${variant}`, divider && "fractal-tabs--divider", className), children: [
2037
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2381
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: cn("fractal-tabs", `fractal-tabs--${variant}`, divider && "fractal-tabs--divider", className), children: [
2382
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2038
2383
  "div",
2039
2384
  {
2040
2385
  ref: tabListRef,
@@ -2047,7 +2392,7 @@ function Tabs({
2047
2392
  const isActive = item.id === activeId;
2048
2393
  const tabId = `${reactId}-tab-${item.id}`;
2049
2394
  const panelId = `${reactId}-panel-${item.id}`;
2050
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2395
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2051
2396
  "button",
2052
2397
  {
2053
2398
  ref: (el) => {
@@ -2078,7 +2423,7 @@ function Tabs({
2078
2423
  const tabId = `${reactId}-tab-${item.id}`;
2079
2424
  const panelId = `${reactId}-panel-${item.id}`;
2080
2425
  const isActive = item.id === activeId;
2081
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2426
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2082
2427
  "div",
2083
2428
  {
2084
2429
  id: panelId,
@@ -2096,7 +2441,7 @@ function Tabs({
2096
2441
 
2097
2442
  // src/components/ContentSwitcher/ContentSwitcher.tsx
2098
2443
  var React15 = __toESM(require("react"), 1);
2099
- var import_jsx_runtime25 = require("react/jsx-runtime");
2444
+ var import_jsx_runtime27 = require("react/jsx-runtime");
2100
2445
  function ContentSwitcher({ items, value, onValueChange, ariaLabel, fullWidth = false, className }) {
2101
2446
  const switcherRef = React15.useRef(null);
2102
2447
  const buttonRefs = React15.useRef([]);
@@ -2154,7 +2499,7 @@ function ContentSwitcher({ items, value, onValueChange, ariaLabel, fullWidth = f
2154
2499
  onValueChange(items[nextIndex].value);
2155
2500
  }
2156
2501
  };
2157
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
2502
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
2158
2503
  "div",
2159
2504
  {
2160
2505
  className: cn("fractal-content-switcher", fullWidth && "fractal-content-switcher--full-width", className),
@@ -2162,10 +2507,10 @@ function ContentSwitcher({ items, value, onValueChange, ariaLabel, fullWidth = f
2162
2507
  "aria-label": ariaLabel,
2163
2508
  ref: switcherRef,
2164
2509
  children: [
2165
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "fractal-content-switcher__indicator", "aria-hidden": true }),
2510
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "fractal-content-switcher__indicator", "aria-hidden": true }),
2166
2511
  items.map((item, index) => {
2167
2512
  const active = item.value === value;
2168
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2513
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2169
2514
  "button",
2170
2515
  {
2171
2516
  className: cn("fractal-content-switcher__item", active && "fractal-content-switcher__item--active"),
@@ -2197,9 +2542,9 @@ function ContentSwitcher({ items, value, onValueChange, ariaLabel, fullWidth = f
2197
2542
  }
2198
2543
 
2199
2544
  // src/components/Disclosure/Disclosure.tsx
2200
- var import_react6 = require("react");
2201
- var import_fractal_icons12 = require("@mirror-physics/fractal-icons");
2202
- var import_jsx_runtime26 = require("react/jsx-runtime");
2545
+ var import_react8 = require("react");
2546
+ var import_fractal_icons13 = require("@mirror-physics/fractal-icons");
2547
+ var import_jsx_runtime28 = require("react/jsx-runtime");
2203
2548
  function Disclosure({
2204
2549
  title,
2205
2550
  meta,
@@ -2211,10 +2556,10 @@ function Disclosure({
2211
2556
  className,
2212
2557
  variant = "card"
2213
2558
  }) {
2214
- const reactId = (0, import_react6.useId)();
2559
+ const reactId = (0, import_react8.useId)();
2215
2560
  const headerId = `${reactId}-header`;
2216
2561
  const panelId = `${reactId}-panel`;
2217
- const [internalOpen, setInternalOpen] = (0, import_react6.useState)(defaultOpen);
2562
+ const [internalOpen, setInternalOpen] = (0, import_react8.useState)(defaultOpen);
2218
2563
  const isControlled = controlledOpen !== void 0;
2219
2564
  const isOpen = isControlled ? controlledOpen : internalOpen;
2220
2565
  const toggle = () => {
@@ -2227,7 +2572,7 @@ function Disclosure({
2227
2572
  onOpenChange?.(next);
2228
2573
  }
2229
2574
  };
2230
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2575
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
2231
2576
  "div",
2232
2577
  {
2233
2578
  className: cn(
@@ -2238,7 +2583,7 @@ function Disclosure({
2238
2583
  className
2239
2584
  ),
2240
2585
  children: [
2241
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2586
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
2242
2587
  "button",
2243
2588
  {
2244
2589
  type: "button",
@@ -2249,13 +2594,13 @@ function Disclosure({
2249
2594
  disabled,
2250
2595
  onClick: toggle,
2251
2596
  children: [
2252
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "fractal-disclosure-icon", "aria-hidden": "true", children: isOpen ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_fractal_icons12.ChevronDown, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_fractal_icons12.ChevronRight, { size: 16 }) }),
2253
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "fractal-disclosure-title", children: title }),
2254
- meta && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "fractal-disclosure-meta", children: meta })
2597
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "fractal-disclosure-icon", "aria-hidden": "true", children: isOpen ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fractal_icons13.ChevronDown, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fractal_icons13.ChevronRight, { size: 16 }) }),
2598
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "fractal-disclosure-title", children: title }),
2599
+ meta && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "fractal-disclosure-meta", children: meta })
2255
2600
  ]
2256
2601
  }
2257
2602
  ),
2258
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2603
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2259
2604
  "div",
2260
2605
  {
2261
2606
  id: panelId,
@@ -2263,7 +2608,7 @@ function Disclosure({
2263
2608
  "aria-labelledby": headerId,
2264
2609
  className: "fractal-disclosure-panel",
2265
2610
  hidden: !isOpen,
2266
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "fractal-disclosure-panel-inner", children })
2611
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "fractal-disclosure-panel-inner", children })
2267
2612
  }
2268
2613
  )
2269
2614
  ]
@@ -2272,18 +2617,18 @@ function Disclosure({
2272
2617
  }
2273
2618
 
2274
2619
  // src/components/LatticeLoader/LatticeLoader.tsx
2275
- var import_jsx_runtime27 = require("react/jsx-runtime");
2620
+ var import_jsx_runtime29 = require("react/jsx-runtime");
2276
2621
  var LATTICE_COUNT = 160;
2277
2622
  var WAVE_PERIOD = 80;
2278
2623
  function LatticeLoader({ className, label = "Loading", ...props }) {
2279
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2624
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
2280
2625
  "span",
2281
2626
  {
2282
2627
  "aria-label": label,
2283
2628
  className: cn("fractal-lattice-loader", className),
2284
2629
  role: "progressbar",
2285
2630
  ...props,
2286
- children: Array.from({ length: LATTICE_COUNT }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2631
+ children: Array.from({ length: LATTICE_COUNT }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
2287
2632
  "span",
2288
2633
  {
2289
2634
  "aria-hidden": "true",
@@ -2297,10 +2642,10 @@ function LatticeLoader({ className, label = "Loading", ...props }) {
2297
2642
  }
2298
2643
 
2299
2644
  // src/components/Ghost/Ghost.tsx
2300
- var import_jsx_runtime28 = require("react/jsx-runtime");
2645
+ var import_jsx_runtime30 = require("react/jsx-runtime");
2301
2646
  var toCssLength = (value) => typeof value === "number" ? `${value}px` : value;
2302
2647
  function Ghost({ className, width, height, radius, style, ...props }) {
2303
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2648
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2304
2649
  "div",
2305
2650
  {
2306
2651
  "aria-hidden": "true",
@@ -2316,15 +2661,15 @@ function Ghost({ className, width, height, radius, style, ...props }) {
2316
2661
  );
2317
2662
  }
2318
2663
  function GhostLine({ className, size = "md", width = "100%", ...props }) {
2319
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, { className: cn("fractal-ghost-line", `fractal-ghost-line--${size}`, className), width, ...props });
2664
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { className: cn("fractal-ghost-line", `fractal-ghost-line--${size}`, className), width, ...props });
2320
2665
  }
2321
2666
  function ButtonGhost({ className, size = "md", controlSize, iconOnly = false, width, ...props }) {
2322
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, { "data-control-size": controlSize ?? size, className: cn("fractal-ghost-button", `fractal-ghost-button--${size}`, iconOnly && "fractal-ghost-button--icon-only", className), width, ...props });
2667
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { "data-control-size": controlSize ?? size, className: cn("fractal-ghost-button", `fractal-ghost-button--${size}`, iconOnly && "fractal-ghost-button--icon-only", className), width, ...props });
2323
2668
  }
2324
2669
  function CardGhost({ className, lines = 3, showHeader = true, ...props }) {
2325
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { "aria-hidden": "true", className: cn("fractal-ghost-card", className), ...props, children: [
2326
- showHeader && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "38%" }),
2327
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "fractal-ghost-stack", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: index === lines - 1 ? "62%" : "100%" }, index)) })
2670
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { "aria-hidden": "true", className: cn("fractal-ghost-card", className), ...props, children: [
2671
+ showHeader && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "38%" }),
2672
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "fractal-ghost-stack", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: index === lines - 1 ? "62%" : "100%" }, index)) })
2328
2673
  ] });
2329
2674
  }
2330
2675
  function DataTableGhost({
@@ -2338,81 +2683,81 @@ function DataTableGhost({
2338
2683
  ...props
2339
2684
  }) {
2340
2685
  const cells = Array.from({ length: columns }, (_, index) => index);
2341
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { "aria-busy": "true", "aria-label": "Loading table", className: cn("fractal-ghost-table", `fractal-ghost-table--${size}`, className), role: "status", ...props, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("table", { className: "fractal-ghost-table__table", children: [
2342
- (showHeader || headers) && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("tr", { className: "fractal-ghost-table__head-row", children: cells.map((index) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { style: { width: columnWidths?.[index] }, children: headers?.[index] ?? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { size: "sm", width: index === columns - 1 ? "62%" : "76%" }) }, index)) }) }),
2343
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("tbody", { children: Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("tr", { className: "fractal-ghost-table__row", children: cells.map((columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { style: { width: columnWidths?.[columnIndex] }, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: `${[72, 54, 82, 63, 46][(rowIndex + columnIndex) % 5]}%` }) }, columnIndex)) }, rowIndex)) })
2686
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { "aria-busy": "true", "aria-label": "Loading table", className: cn("fractal-ghost-table", `fractal-ghost-table--${size}`, className), role: "status", ...props, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("table", { className: "fractal-ghost-table__table", children: [
2687
+ (showHeader || headers) && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("tr", { className: "fractal-ghost-table__head-row", children: cells.map((index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("th", { style: { width: columnWidths?.[index] }, children: headers?.[index] ?? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { size: "sm", width: index === columns - 1 ? "62%" : "76%" }) }, index)) }) }),
2688
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("tbody", { children: Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("tr", { className: "fractal-ghost-table__row", children: cells.map((columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { style: { width: columnWidths?.[columnIndex] }, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: `${[72, 54, 82, 63, 46][(rowIndex + columnIndex) % 5]}%` }) }, columnIndex)) }, rowIndex)) })
2344
2689
  ] }) });
2345
2690
  }
2346
2691
  function InputGhost({ className, labelWidth = "26%", ...props }) {
2347
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(FormFieldGhost, { className, labelWidth, ...props });
2692
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(FormFieldGhost, { className, labelWidth, ...props });
2348
2693
  }
2349
2694
  function TextareaGhost({ className, labelWidth = "26%", ...props }) {
2350
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(FormFieldGhost, { className, labelWidth, multiline: true, ...props });
2695
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(FormFieldGhost, { className, labelWidth, multiline: true, ...props });
2351
2696
  }
2352
2697
  function LabelGhost({ className, width = "26%", ...props }) {
2353
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { className: cn("fractal-ghost-label", className), size: "sm", width, ...props });
2698
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { className: cn("fractal-ghost-label", className), size: "sm", width, ...props });
2354
2699
  }
2355
2700
  function FormFieldGhost({ className, labelWidth = "26%", multiline = false, controlSize = "md", ...props }) {
2356
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { "aria-hidden": "true", "data-control-size": controlSize, className: cn("fractal-ghost-field", className), ...props, children: [
2357
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(LabelGhost, { width: labelWidth }),
2358
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, { className: cn("fractal-ghost-field__control", multiline && "fractal-ghost-field__control--multiline") })
2701
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { "aria-hidden": "true", "data-control-size": controlSize, className: cn("fractal-ghost-field", className), ...props, children: [
2702
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(LabelGhost, { width: labelWidth }),
2703
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { className: cn("fractal-ghost-field__control", multiline && "fractal-ghost-field__control--multiline") })
2359
2704
  ] });
2360
2705
  }
2361
2706
  function AlertGhost({ className, ...props }) {
2362
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { "aria-hidden": "true", className: cn("fractal-ghost-alert", className), ...props, children: [
2363
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "30%" }),
2364
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { size: "sm", width: "78%" })
2707
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { "aria-hidden": "true", className: cn("fractal-ghost-alert", className), ...props, children: [
2708
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "30%" }),
2709
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { size: "sm", width: "78%" })
2365
2710
  ] });
2366
2711
  }
2367
2712
  function CheckboxGhost({ className, ...props }) {
2368
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { "aria-hidden": "true", className: cn("fractal-ghost-choice", className), ...props, children: [
2369
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, { className: "fractal-ghost-choice__control" }),
2370
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "42%" })
2713
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { "aria-hidden": "true", className: cn("fractal-ghost-choice", className), ...props, children: [
2714
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { className: "fractal-ghost-choice__control" }),
2715
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "42%" })
2371
2716
  ] });
2372
2717
  }
2373
2718
  var ToggleGhost = CheckboxGhost;
2374
2719
  function ChipGhost({ className, width = 74, ...props }) {
2375
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, { className: cn("fractal-ghost-chip", className), height: 24, width, ...props });
2720
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { className: cn("fractal-ghost-chip", className), height: 24, width, ...props });
2376
2721
  }
2377
2722
  function LinkGhost({ className, width = 96, ...props }) {
2378
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { className: cn("fractal-ghost-link", className), size: "sm", width, ...props });
2723
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { className: cn("fractal-ghost-link", className), size: "sm", width, ...props });
2379
2724
  }
2380
2725
  var TextButtonGhost = LinkGhost;
2381
2726
  function DropdownGhost({ className, controlSize = "md", ...props }) {
2382
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { "aria-hidden": "true", "data-control-size": controlSize, className: cn("fractal-ghost-dropdown", className), ...props, children: [
2383
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "38%" }),
2384
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, { className: "fractal-ghost-dropdown__chevron" })
2727
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { "aria-hidden": "true", "data-control-size": controlSize, className: cn("fractal-ghost-dropdown", className), ...props, children: [
2728
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "38%" }),
2729
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { className: "fractal-ghost-dropdown__chevron" })
2385
2730
  ] });
2386
2731
  }
2387
2732
  function ContentSwitcherGhost({ className, options = 3, ...props }) {
2388
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { "aria-hidden": "true", className: cn("fractal-ghost-switcher", className), ...props, children: Array.from({ length: options }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, {}, index)) });
2733
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { "aria-hidden": "true", className: cn("fractal-ghost-switcher", className), ...props, children: Array.from({ length: options }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, {}, index)) });
2389
2734
  }
2390
2735
  var TabsGhost = ContentSwitcherGhost;
2391
2736
  function DisclosureGhost({ className, ...props }) {
2392
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { "aria-hidden": "true", className: cn("fractal-ghost-disclosure", className), ...props, children: [
2393
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, { className: "fractal-ghost-disclosure__icon" }),
2394
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "42%" })
2737
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { "aria-hidden": "true", className: cn("fractal-ghost-disclosure", className), ...props, children: [
2738
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { className: "fractal-ghost-disclosure__icon" }),
2739
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "42%" })
2395
2740
  ] });
2396
2741
  }
2397
2742
  function PaginationGhost({ className, pages = 5, ...props }) {
2398
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { "aria-hidden": "true", className: cn("fractal-ghost-pagination", className), ...props, children: Array.from({ length: pages }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, {}, index)) });
2743
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { "aria-hidden": "true", className: cn("fractal-ghost-pagination", className), ...props, children: Array.from({ length: pages }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, {}, index)) });
2399
2744
  }
2400
2745
  function FileDropzoneGhost({ className, ...props }) {
2401
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { "aria-hidden": "true", className: cn("fractal-ghost-dropzone", className), ...props, children: [
2402
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, { className: "fractal-ghost-dropzone__icon" }),
2403
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "26%" }),
2404
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { size: "sm", width: "46%" })
2746
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { "aria-hidden": "true", className: cn("fractal-ghost-dropzone", className), ...props, children: [
2747
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { className: "fractal-ghost-dropzone__icon" }),
2748
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "26%" }),
2749
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { size: "sm", width: "46%" })
2405
2750
  ] });
2406
2751
  }
2407
2752
  function PromptCardGhost({ className, ...props }) {
2408
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(CardGhost, { className: cn("fractal-ghost-prompt-card", className), lines: 4, ...props });
2753
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(CardGhost, { className: cn("fractal-ghost-prompt-card", className), lines: 4, ...props });
2409
2754
  }
2410
2755
  function ConversationGhost({
2411
2756
  className,
2412
2757
  "aria-label": ariaLabel = "Loading conversation",
2413
2758
  ...props
2414
2759
  }) {
2415
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
2760
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
2416
2761
  "div",
2417
2762
  {
2418
2763
  "aria-busy": "true",
@@ -2421,54 +2766,54 @@ function ConversationGhost({
2421
2766
  role: "status",
2422
2767
  ...props,
2423
2768
  children: [
2424
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { "aria-hidden": "true", className: "fractal-ghost-conversation__message fractal-ghost-conversation__message--user", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "fractal-ghost-conversation__bubble", children: [
2425
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "100%" }),
2426
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "86%" }),
2427
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "64%" })
2769
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { "aria-hidden": "true", className: "fractal-ghost-conversation__message fractal-ghost-conversation__message--user", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "fractal-ghost-conversation__bubble", children: [
2770
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "100%" }),
2771
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "86%" }),
2772
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "64%" })
2428
2773
  ] }) }),
2429
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { "aria-hidden": "true", className: "fractal-ghost-conversation__message fractal-ghost-conversation__message--assistant", children: [
2430
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "92%" }),
2431
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "78%" }),
2432
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "58%" })
2774
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { "aria-hidden": "true", className: "fractal-ghost-conversation__message fractal-ghost-conversation__message--assistant", children: [
2775
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "92%" }),
2776
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "78%" }),
2777
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "58%" })
2433
2778
  ] })
2434
2779
  ]
2435
2780
  }
2436
2781
  );
2437
2782
  }
2438
2783
  function SortableTableGhost(props) {
2439
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(DataTableGhost, { ...props });
2784
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(DataTableGhost, { ...props });
2440
2785
  }
2441
2786
  function SidebarGhost({ className, items = 6, ...props }) {
2442
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("aside", { "aria-hidden": "true", className: cn("fractal-ghost-sidebar", className), ...props, children: [
2443
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, { className: "fractal-ghost-sidebar__brand" }),
2444
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "fractal-ghost-sidebar__items", children: Array.from({ length: items }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "fractal-ghost-sidebar__item", children: [
2445
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, {}),
2446
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: `${[54, 68, 44, 61][index % 4]}%` })
2787
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("aside", { "aria-hidden": "true", className: cn("fractal-ghost-sidebar", className), ...props, children: [
2788
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { className: "fractal-ghost-sidebar__brand" }),
2789
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "fractal-ghost-sidebar__items", children: Array.from({ length: items }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "fractal-ghost-sidebar__item", children: [
2790
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, {}),
2791
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: `${[54, 68, 44, 61][index % 4]}%` })
2447
2792
  ] }, index)) })
2448
2793
  ] });
2449
2794
  }
2450
2795
  function ModalGhost({ className, title, lines = 3, ...props }) {
2451
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SurfaceGhost, { className: cn("fractal-ghost-modal", className), title, lines, ...props });
2796
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(SurfaceGhost, { className: cn("fractal-ghost-modal", className), title, lines, ...props });
2452
2797
  }
2453
2798
  function SidePanelGhost({ className, title, lines = 5, ...props }) {
2454
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SurfaceGhost, { className: cn("fractal-ghost-side-panel", className), title, lines, ...props });
2799
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(SurfaceGhost, { className: cn("fractal-ghost-side-panel", className), title, lines, ...props });
2455
2800
  }
2456
2801
  function SurfaceGhost({ className, title, lines = 3, ...props }) {
2457
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { "aria-hidden": "true", className: cn("fractal-ghost-surface", className), ...props, children: [
2458
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "fractal-ghost-surface__header", children: [
2459
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: "42%" }),
2460
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Ghost, { className: "fractal-ghost-surface__close" })
2802
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { "aria-hidden": "true", className: cn("fractal-ghost-surface", className), ...props, children: [
2803
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "fractal-ghost-surface__header", children: [
2804
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "42%" }),
2805
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { className: "fractal-ghost-surface__close" })
2461
2806
  ] }),
2462
- title && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "fractal-ghost-surface__title", children: title }),
2463
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "fractal-ghost-stack", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GhostLine, { width: index === lines - 1 ? "64%" : "100%" }, index)) })
2807
+ title && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "fractal-ghost-surface__title", children: title }),
2808
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "fractal-ghost-stack", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: index === lines - 1 ? "64%" : "100%" }, index)) })
2464
2809
  ] });
2465
2810
  }
2466
2811
 
2467
2812
  // src/components/Sidebar/Sidebar.tsx
2468
2813
  var React16 = __toESM(require("react"), 1);
2469
- var import_jsx_runtime29 = require("react/jsx-runtime");
2814
+ var import_jsx_runtime31 = require("react/jsx-runtime");
2470
2815
  var Sidebar = React16.forwardRef(
2471
- ({ className, width, compact = false, style, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
2816
+ ({ className, width, compact = false, style, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
2472
2817
  "aside",
2473
2818
  {
2474
2819
  ref,
@@ -2481,11 +2826,11 @@ var Sidebar = React16.forwardRef(
2481
2826
  );
2482
2827
  Sidebar.displayName = "Sidebar";
2483
2828
  var SidebarHeader = React16.forwardRef(
2484
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
2829
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
2485
2830
  );
2486
2831
  SidebarHeader.displayName = "SidebarHeader";
2487
2832
  var SidebarBrand = React16.forwardRef(
2488
- ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
2833
+ ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
2489
2834
  );
2490
2835
  SidebarBrand.displayName = "SidebarBrand";
2491
2836
  function SidebarAccountMenu({
@@ -2496,7 +2841,7 @@ function SidebarAccountMenu({
2496
2841
  icon,
2497
2842
  className
2498
2843
  }) {
2499
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
2844
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
2500
2845
  Dropdown,
2501
2846
  {
2502
2847
  align: "left",
@@ -2506,9 +2851,9 @@ function SidebarAccountMenu({
2506
2851
  panelClassName: "fractal-sidebar__account-panel",
2507
2852
  selectedValue: "",
2508
2853
  triggerClassName: "fractal-sidebar__account-trigger",
2509
- triggerContent: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
2510
- icon && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "fractal-sidebar__account-icon", "aria-hidden": "true", children: icon }),
2511
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "fractal-sidebar__account-name", children: name })
2854
+ triggerContent: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
2855
+ icon && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "fractal-sidebar__account-icon", "aria-hidden": "true", children: icon }),
2856
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "fractal-sidebar__account-name", children: name })
2512
2857
  ] }),
2513
2858
  triggerLabel,
2514
2859
  triggerVariant: "tertiary"
@@ -2516,15 +2861,15 @@ function SidebarAccountMenu({
2516
2861
  );
2517
2862
  }
2518
2863
  var SidebarNav = React16.forwardRef(
2519
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
2864
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
2520
2865
  );
2521
2866
  SidebarNav.displayName = "SidebarNav";
2522
2867
  var SidebarSection = React16.forwardRef(
2523
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
2868
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
2524
2869
  );
2525
2870
  SidebarSection.displayName = "SidebarSection";
2526
2871
  var SidebarNavItem = React16.forwardRef(
2527
- ({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
2872
+ ({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
2528
2873
  "button",
2529
2874
  {
2530
2875
  ref,
@@ -2534,28 +2879,60 @@ var SidebarNavItem = React16.forwardRef(
2534
2879
  "aria-current": active ? "page" : void 0,
2535
2880
  ...props,
2536
2881
  children: [
2537
- icon && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "fractal-sidebar__nav-item-icon", "aria-hidden": "true", children: icon }),
2538
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "fractal-sidebar__nav-item-label", children }),
2539
- endAdornment && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "fractal-sidebar__nav-item-end", children: endAdornment })
2882
+ icon && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "fractal-sidebar__nav-item-icon", "aria-hidden": "true", children: icon }),
2883
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "fractal-sidebar__nav-item-label", children }),
2884
+ endAdornment && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "fractal-sidebar__nav-item-end", children: endAdornment })
2540
2885
  ]
2541
2886
  }
2542
2887
  )
2543
2888
  );
2544
2889
  SidebarNavItem.displayName = "SidebarNavItem";
2545
2890
  var SidebarFooter = React16.forwardRef(
2546
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
2891
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
2547
2892
  );
2548
2893
  SidebarFooter.displayName = "SidebarFooter";
2549
2894
 
2550
2895
  // src/components/SidePanel/SidePanel.tsx
2551
- var import_react_dom3 = require("react-dom");
2552
- var import_fractal_icons13 = require("@mirror-physics/fractal-icons");
2553
- var import_jsx_runtime30 = require("react/jsx-runtime");
2896
+ var React17 = __toESM(require("react"), 1);
2897
+ var import_react_dom4 = require("react-dom");
2898
+ var import_fractal_icons14 = require("@mirror-physics/fractal-icons");
2899
+ var import_jsx_runtime32 = require("react/jsx-runtime");
2554
2900
  function SidePanel({ open, onClose, title, description, children, footer, size = "md", loading = false, loadingLabel = "Loading", className }) {
2555
2901
  useEscapeDismiss(80, onClose, open);
2902
+ const bodyRef = React17.useRef(null);
2903
+ const [scrollEdges, setScrollEdges] = React17.useState({ above: false, below: false });
2904
+ React17.useLayoutEffect(() => {
2905
+ const body = bodyRef.current;
2906
+ if (!open || !body) return;
2907
+ const measure = () => {
2908
+ const maxScroll = Math.max(0, body.scrollHeight - body.clientHeight);
2909
+ const top = Math.max(0, Math.min(body.scrollTop, maxScroll));
2910
+ const above = maxScroll > 1 && top > 0;
2911
+ const below = maxScroll - top > 1;
2912
+ setScrollEdges((previous) => previous.above === above && previous.below === below ? previous : { above, below });
2913
+ };
2914
+ const resize = new ResizeObserver(measure);
2915
+ const observeContent = () => {
2916
+ resize.disconnect();
2917
+ resize.observe(body);
2918
+ for (const child of body.children) resize.observe(child);
2919
+ measure();
2920
+ };
2921
+ const mutations = new MutationObserver(observeContent);
2922
+ mutations.observe(body, { childList: true, subtree: true, characterData: true, attributes: true });
2923
+ body.addEventListener("scroll", measure, { passive: true });
2924
+ body.addEventListener("load", measure, true);
2925
+ observeContent();
2926
+ return () => {
2927
+ resize.disconnect();
2928
+ mutations.disconnect();
2929
+ body.removeEventListener("scroll", measure);
2930
+ body.removeEventListener("load", measure, true);
2931
+ };
2932
+ }, [open, loading]);
2556
2933
  if (!open) return null;
2557
- return (0, import_react_dom3.createPortal)(
2558
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
2934
+ return (0, import_react_dom4.createPortal)(
2935
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
2559
2936
  "aside",
2560
2937
  {
2561
2938
  className: cn("fractal-side-panel", `fractal-side-panel--${size}`, className),
@@ -2564,15 +2941,15 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
2564
2941
  role: "dialog",
2565
2942
  onMouseDown: (event) => event.stopPropagation(),
2566
2943
  children: [
2567
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("header", { className: "fractal-side-panel__header", children: [
2568
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "fractal-side-panel__heading", children: [
2569
- typeof title === "string" ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("h2", { className: "fractal-side-panel__title", children: title }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "fractal-side-panel__title", children: title }),
2570
- description && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "fractal-side-panel__description", children: description })
2944
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("header", { className: "fractal-side-panel__header", "data-scroll-divider": scrollEdges.above, children: [
2945
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "fractal-side-panel__heading", children: [
2946
+ typeof title === "string" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("h2", { className: "fractal-side-panel__title", children: title }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "fractal-side-panel__title", children: title }),
2947
+ description && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "fractal-side-panel__description", children: description })
2571
2948
  ] }),
2572
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_fractal_icons13.Close, { size: 18 }) })
2949
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_fractal_icons14.Close, { size: 18 }) })
2573
2950
  ] }),
2574
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { "aria-busy": loading || void 0, className: cn("fractal-side-panel__body", loading && "fractal-side-panel__body--loading"), children: loading ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(UILoader, { label: loadingLabel }) : children }),
2575
- footer && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("footer", { className: "fractal-side-panel__footer", children: footer })
2951
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { ref: bodyRef, "aria-busy": loading || void 0, className: cn("fractal-side-panel__body", loading && "fractal-side-panel__body--loading"), children: loading ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(UILoader, { label: loadingLabel }) : children }),
2952
+ footer && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("footer", { className: "fractal-side-panel__footer", "data-scroll-divider": scrollEdges.below, children: footer })
2576
2953
  ]
2577
2954
  }
2578
2955
  ) }),
@@ -2581,18 +2958,18 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
2581
2958
  }
2582
2959
 
2583
2960
  // src/components/Textarea/Textarea.tsx
2584
- var React17 = __toESM(require("react"), 1);
2585
- var import_jsx_runtime31 = require("react/jsx-runtime");
2586
- var Textarea = React17.forwardRef(
2587
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("textarea", { ref, className: cn("fractal-textarea", className), ...props })
2961
+ var React18 = __toESM(require("react"), 1);
2962
+ var import_jsx_runtime33 = require("react/jsx-runtime");
2963
+ var Textarea = React18.forwardRef(
2964
+ ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("textarea", { ref, className: cn("fractal-textarea", className), ...props })
2588
2965
  );
2589
2966
  Textarea.displayName = "Textarea";
2590
2967
 
2591
2968
  // src/components/TextButton/TextButton.tsx
2592
- var React18 = __toESM(require("react"), 1);
2593
- var import_jsx_runtime32 = require("react/jsx-runtime");
2594
- var TextButton = React18.forwardRef(
2595
- ({ className, icon, iconPosition = "start", size = "md", tone = "primary", children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
2969
+ var React19 = __toESM(require("react"), 1);
2970
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2971
+ var TextButton = React19.forwardRef(
2972
+ ({ className, icon, iconPosition = "start", size = "md", tone = "primary", children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2596
2973
  "button",
2597
2974
  {
2598
2975
  ref,
@@ -2600,9 +2977,9 @@ var TextButton = React18.forwardRef(
2600
2977
  type: "button",
2601
2978
  ...props,
2602
2979
  children: [
2603
- icon && iconPosition === "start" && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon }),
2604
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { children }),
2605
- icon && iconPosition === "end" && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon })
2980
+ icon && iconPosition === "start" && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon }),
2981
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { children }),
2982
+ icon && iconPosition === "end" && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon })
2606
2983
  ]
2607
2984
  }
2608
2985
  )
@@ -2610,9 +2987,9 @@ var TextButton = React18.forwardRef(
2610
2987
  TextButton.displayName = "TextButton";
2611
2988
 
2612
2989
  // src/components/FileDropzone/FileDropzone.tsx
2613
- var React19 = __toESM(require("react"), 1);
2614
- var import_fractal_icons14 = require("@mirror-physics/fractal-icons");
2615
- var import_jsx_runtime33 = require("react/jsx-runtime");
2990
+ var React20 = __toESM(require("react"), 1);
2991
+ var import_fractal_icons15 = require("@mirror-physics/fractal-icons");
2992
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2616
2993
  function FileDropzone({
2617
2994
  files,
2618
2995
  onFilesChange,
@@ -2623,17 +3000,17 @@ function FileDropzone({
2623
3000
  description = "Files stay attached to this record.",
2624
3001
  className
2625
3002
  }) {
2626
- const inputId = React19.useId();
2627
- const inputRef = React19.useRef(null);
2628
- const [isDragging, setIsDragging] = React19.useState(false);
3003
+ const inputId = React20.useId();
3004
+ const inputRef = React20.useRef(null);
3005
+ const [isDragging, setIsDragging] = React20.useState(false);
2629
3006
  const addFiles = (nextFiles) => {
2630
3007
  if (disabled) return;
2631
3008
  const additions = Array.from(nextFiles);
2632
3009
  if (additions.length === 0) return;
2633
3010
  onFilesChange(multiple ? [...files, ...additions] : additions.slice(0, 1));
2634
3011
  };
2635
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: cn("fractal-file-dropzone", className), children: [
2636
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3012
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: cn("fractal-file-dropzone", className), children: [
3013
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2637
3014
  "input",
2638
3015
  {
2639
3016
  ref: inputRef,
@@ -2649,7 +3026,7 @@ function FileDropzone({
2649
3026
  }
2650
3027
  }
2651
3028
  ),
2652
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
3029
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
2653
3030
  "button",
2654
3031
  {
2655
3032
  className: "fractal-file-dropzone__target",
@@ -2672,18 +3049,18 @@ function FileDropzone({
2672
3049
  addFiles(event.dataTransfer.files);
2673
3050
  },
2674
3051
  children: [
2675
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_fractal_icons14.FileArrowUp, { "aria-hidden": "true", size: 20 }),
2676
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "fractal-file-dropzone__copy", children: [
2677
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "fractal-file-dropzone__title", children: title }),
2678
- description && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "fractal-file-dropzone__description", children: description })
3052
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_fractal_icons15.FileArrowUp, { "aria-hidden": "true", size: 20 }),
3053
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "fractal-file-dropzone__copy", children: [
3054
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "fractal-file-dropzone__title", children: title }),
3055
+ description && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "fractal-file-dropzone__description", children: description })
2679
3056
  ] })
2680
3057
  ]
2681
3058
  }
2682
3059
  ),
2683
- files.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("ul", { className: "fractal-file-dropzone__files", "aria-label": "Attached files", children: files.map((file, index) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("li", { className: "fractal-file-dropzone__file", children: [
2684
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "fractal-file-dropzone__file-name", children: file.name }),
2685
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "fractal-file-dropzone__file-meta", children: formatFileSize(file.size) }),
2686
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3060
+ files.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { className: "fractal-file-dropzone__files", "aria-label": "Attached files", children: files.map((file, index) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("li", { className: "fractal-file-dropzone__file", children: [
3061
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "fractal-file-dropzone__file-name", children: file.name }),
3062
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "fractal-file-dropzone__file-meta", children: formatFileSize(file.size) }),
3063
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2687
3064
  "button",
2688
3065
  {
2689
3066
  className: "fractal-file-dropzone__remove",
@@ -2691,7 +3068,7 @@ function FileDropzone({
2691
3068
  "aria-label": `Remove ${file.name}`,
2692
3069
  disabled,
2693
3070
  onClick: () => onFilesChange(files.filter((_, fileIndex) => fileIndex !== index)),
2694
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_fractal_icons14.Close, { "aria-hidden": "true", size: 14 })
3071
+ children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_fractal_icons15.Close, { "aria-hidden": "true", size: 14 })
2695
3072
  }
2696
3073
  )
2697
3074
  ] }, `${file.name}-${file.size}-${index}`)) })
@@ -2704,8 +3081,8 @@ function formatFileSize(bytes) {
2704
3081
  }
2705
3082
 
2706
3083
  // src/components/Pagination/Pagination.tsx
2707
- var import_fractal_icons15 = require("@mirror-physics/fractal-icons");
2708
- var import_jsx_runtime34 = require("react/jsx-runtime");
3084
+ var import_fractal_icons16 = require("@mirror-physics/fractal-icons");
3085
+ var import_jsx_runtime36 = require("react/jsx-runtime");
2709
3086
  function Pagination({
2710
3087
  page,
2711
3088
  pageSize,
@@ -2721,10 +3098,10 @@ function Pagination({
2721
3098
  const start = totalItems === 0 ? 0 : (currentPage - 1) * pageSize + 1;
2722
3099
  const end = Math.min(currentPage * pageSize, totalItems);
2723
3100
  const canChangePageSize = pageSizeOptions != null && pageSizeOptions.length > 0 && onPageSizeChange != null;
2724
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("nav", { className: cn("fractal-pagination", className), "aria-label": ariaLabel, children: [
2725
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "fractal-pagination__summary", children: totalItems === 0 ? "No results" : `${start}-${end} of ${totalItems}` }),
2726
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "fractal-pagination__controls", children: [
2727
- canChangePageSize && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
3101
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("nav", { className: cn("fractal-pagination", className), "aria-label": ariaLabel, children: [
3102
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "fractal-pagination__summary", children: totalItems === 0 ? "No results" : `${start}-${end} of ${totalItems}` }),
3103
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "fractal-pagination__controls", children: [
3104
+ canChangePageSize && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2728
3105
  Dropdown,
2729
3106
  {
2730
3107
  align: "right",
@@ -2741,7 +3118,7 @@ function Pagination({
2741
3118
  triggerVariant: "tertiary"
2742
3119
  }
2743
3120
  ),
2744
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
3121
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2745
3122
  "button",
2746
3123
  {
2747
3124
  className: "fractal-pagination__button",
@@ -2750,15 +3127,15 @@ function Pagination({
2750
3127
  title: "Previous page",
2751
3128
  disabled: currentPage === 1 || totalItems === 0,
2752
3129
  onClick: () => onPageChange(currentPage - 1),
2753
- children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_fractal_icons15.ArrowLeft, { "aria-hidden": "true", size: 16 })
3130
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_fractal_icons16.ArrowLeft, { "aria-hidden": "true", size: 16 })
2754
3131
  }
2755
3132
  ),
2756
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "fractal-pagination__page", "aria-current": "page", children: [
3133
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "fractal-pagination__page", "aria-current": "page", children: [
2757
3134
  currentPage,
2758
3135
  " of ",
2759
3136
  totalPages
2760
3137
  ] }),
2761
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
3138
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2762
3139
  "button",
2763
3140
  {
2764
3141
  className: "fractal-pagination__button",
@@ -2767,12 +3144,195 @@ function Pagination({
2767
3144
  title: "Next page",
2768
3145
  disabled: currentPage === totalPages || totalItems === 0,
2769
3146
  onClick: () => onPageChange(currentPage + 1),
2770
- children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_fractal_icons15.ArrowRight, { "aria-hidden": "true", size: 16 })
3147
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_fractal_icons16.ArrowRight, { "aria-hidden": "true", size: 16 })
2771
3148
  }
2772
3149
  )
2773
3150
  ] })
2774
3151
  ] });
2775
3152
  }
3153
+
3154
+ // src/components/ColumnEditor/ColumnEditor.tsx
3155
+ var React21 = __toESM(require("react"), 1);
3156
+ var import_fractal_icons17 = require("@mirror-physics/fractal-icons");
3157
+ var import_jsx_runtime37 = require("react/jsx-runtime");
3158
+ var equal = (a, b) => a.length === b.length && a.every((id, index) => id === b[index]);
3159
+ function ColumnEditor({ items, value, onChange, label = "Edit columns", hiddenLabel = "Hidden columns", className }) {
3160
+ const [open, setOpen] = React21.useState(false);
3161
+ const [preview, setPreview] = React21.useState(null);
3162
+ const [announcement, setAnnouncement] = React21.useState("");
3163
+ const root = React21.useRef(null);
3164
+ const trigger = React21.useRef(null);
3165
+ const panel = React21.useRef(null);
3166
+ const drag = React21.useRef(null);
3167
+ const positions = React21.useRef(null);
3168
+ const id = React21.useId();
3169
+ function capturePositions() {
3170
+ positions.current = new Map(Array.from(panel.current?.querySelectorAll("[data-column-editor-item]") ?? []).map((row2) => [row2.dataset.columnEditorItem, row2.getBoundingClientRect()]));
3171
+ }
3172
+ React21.useLayoutEffect(() => {
3173
+ const before = positions.current;
3174
+ positions.current = null;
3175
+ if (!before || !panel.current || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
3176
+ const styles = getComputedStyle(panel.current);
3177
+ const token = styles.getPropertyValue("--duration-normal").trim();
3178
+ const duration = parseFloat(token) * (token.endsWith("ms") ? 1 : 1e3);
3179
+ const easing = styles.getPropertyValue("--ease-out").trim();
3180
+ for (const row2 of panel.current.querySelectorAll("[data-column-editor-item]")) {
3181
+ const previous = before.get(row2.dataset.columnEditorItem);
3182
+ row2.getAnimations().filter((animation) => animation.id === "fractal-column-position").forEach((animation) => animation.cancel());
3183
+ if (!previous) continue;
3184
+ const next = row2.getBoundingClientRect();
3185
+ const x = previous.left - next.left, y = previous.top - next.top;
3186
+ if (Math.abs(x) < 1 && Math.abs(y) < 1) continue;
3187
+ row2.animate([{ transform: `translate(${x}px, ${y}px)` }, { transform: "translate(0, 0)" }], { duration, easing, id: "fractal-column-position" });
3188
+ }
3189
+ });
3190
+ const valid = [...new Set(value)].filter((key) => items.some((item) => item.id === key));
3191
+ const committed = [...valid, ...items.filter((item) => item.required && !valid.includes(item.id)).map((item) => item.id)];
3192
+ const visible = preview?.visible ?? committed;
3193
+ const hidden = preview?.hidden ?? items.filter((item) => !committed.includes(item.id)).map((item) => item.id);
3194
+ function cancel() {
3195
+ capturePositions();
3196
+ drag.current = null;
3197
+ setPreview(null);
3198
+ }
3199
+ React21.useEffect(() => {
3200
+ if (!open) return;
3201
+ panel.current?.focus();
3202
+ function outside(event) {
3203
+ if (!drag.current && !root.current?.contains(event.target)) setOpen(false);
3204
+ }
3205
+ function escape(event) {
3206
+ if (event.key !== "Escape") return;
3207
+ event.preventDefault();
3208
+ if (drag.current) {
3209
+ capturePositions();
3210
+ drag.current = null;
3211
+ setPreview(null);
3212
+ setAnnouncement("Reordering cancelled");
3213
+ } else {
3214
+ setOpen(false);
3215
+ trigger.current?.focus();
3216
+ }
3217
+ }
3218
+ function blur(event) {
3219
+ if (event.relatedTarget instanceof Node && !root.current?.contains(event.relatedTarget)) setOpen(false);
3220
+ }
3221
+ const element = root.current;
3222
+ document.addEventListener("pointerdown", outside);
3223
+ document.addEventListener("keydown", escape);
3224
+ element?.addEventListener("focusout", blur);
3225
+ return () => {
3226
+ document.removeEventListener("pointerdown", outside);
3227
+ document.removeEventListener("keydown", escape);
3228
+ element?.removeEventListener("focusout", blur);
3229
+ };
3230
+ }, [open]);
3231
+ function commit(next, message) {
3232
+ capturePositions();
3233
+ onChange(next);
3234
+ setAnnouncement(message);
3235
+ }
3236
+ function move(key, offset) {
3237
+ const index = committed.indexOf(key);
3238
+ const next = committed.filter((item) => item !== key);
3239
+ if (offset > 0 && index === committed.length - 1 && !items.find((item) => item.id === key)?.required) {
3240
+ commit(next, `${items.find((item) => item.id === key)?.label} hidden`);
3241
+ return;
3242
+ }
3243
+ next.splice(Math.max(0, Math.min(next.length, index + offset)), 0, key);
3244
+ commit(next, `${items.find((item) => item.id === key)?.label} moved ${offset < 0 ? "up" : "down"}`);
3245
+ }
3246
+ function start(event, key) {
3247
+ if (event.button !== 0 || event.target.closest("button")) return;
3248
+ drag.current = { id: key, pointerId: event.pointerId, x: event.clientX, y: event.clientY, active: false, visible: [...committed], hidden: [...hidden] };
3249
+ panel.current?.setPointerCapture(event.pointerId);
3250
+ event.preventDefault();
3251
+ }
3252
+ function update(event) {
3253
+ const current = drag.current;
3254
+ if (!current || event.pointerId !== current.pointerId) return;
3255
+ if (!current.active && Math.hypot(event.clientX - current.x, event.clientY - current.y) < 4) return;
3256
+ current.active = true;
3257
+ const container = panel.current;
3258
+ if (!container) return;
3259
+ const bounds = container.getBoundingClientRect();
3260
+ const hiddenGroup = container.querySelector('[data-column-editor-group="hidden"]');
3261
+ const inside = event.clientX >= bounds.left && event.clientX <= bounds.right && event.clientY >= bounds.top && event.clientY <= bounds.bottom;
3262
+ if (!inside) return;
3263
+ const destination = event.clientY >= hiddenGroup.getBoundingClientRect().top ? "hidden" : "visible";
3264
+ if (destination === "hidden" && items.find((item) => item.id === current.id)?.required) return;
3265
+ const slots = Array.from(container.querySelectorAll(`[data-column-editor-group="${destination}"] [data-column-editor-item]`));
3266
+ const localY = event.clientY - bounds.top + container.scrollTop - container.clientTop;
3267
+ const ownSlot = slots.find((row2) => row2.dataset.columnEditorItem === current.id);
3268
+ if (ownSlot && localY >= ownSlot.offsetTop && localY <= ownSlot.offsetTop + ownSlot.offsetHeight) return;
3269
+ const nextVisible = current.visible.filter((key) => key !== current.id);
3270
+ let nextHidden = current.hidden.filter((key) => key !== current.id);
3271
+ if (destination === "visible") {
3272
+ const index = slots.filter((row2) => row2.dataset.columnEditorItem !== current.id && localY > row2.offsetTop + row2.offsetHeight / 2).length;
3273
+ nextVisible.splice(index, 0, current.id);
3274
+ } else {
3275
+ nextHidden.push(current.id);
3276
+ nextHidden = items.filter((item) => nextHidden.includes(item.id)).map((item) => item.id);
3277
+ }
3278
+ if (!equal(nextVisible, current.visible) || !equal(nextHidden, current.hidden)) {
3279
+ capturePositions();
3280
+ current.visible = nextVisible;
3281
+ current.hidden = nextHidden;
3282
+ }
3283
+ setPreview({ visible: current.visible, hidden: current.hidden, active: current.id });
3284
+ if (event.clientY < bounds.top + 32) container.scrollTop -= 12;
3285
+ else if (event.clientY > bounds.bottom - 32) container.scrollTop += 12;
3286
+ }
3287
+ function finish(event) {
3288
+ const current = drag.current;
3289
+ if (!current || current.pointerId !== event.pointerId) return;
3290
+ const hit = document.elementFromPoint(event.clientX, event.clientY);
3291
+ if (current.active && hit && panel.current?.contains(hit)) commit(current.visible, "Column order updated");
3292
+ cancel();
3293
+ if (panel.current?.hasPointerCapture(event.pointerId)) panel.current.releasePointerCapture(event.pointerId);
3294
+ }
3295
+ function row(key, isHidden, index) {
3296
+ const item = items.find((item2) => item2.id === key);
3297
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
3298
+ "li",
3299
+ {
3300
+ className: "fractal-column-editor__row",
3301
+ "data-column-editor-item": key,
3302
+ "data-hidden": isHidden || void 0,
3303
+ "data-dragging": preview?.active === key || void 0,
3304
+ onPointerDown: (event) => start(event, key),
3305
+ children: [
3306
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fractal_icons17.DotsSixVertical, { className: "fractal-column-editor__grip", "aria-hidden": "true" }),
3307
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "fractal-column-editor__name", children: item.label }),
3308
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "fractal-column-editor__actions", onPointerDown: (event) => event.stopPropagation(), children: isHidden ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Button, { buttonType: "ghost", controlSize: "sm", hasIconOnly: true, icon: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fractal_icons17.Eye, { "aria-hidden": "true" }), "aria-label": `Show ${item.label} column`, onClick: () => commit([...committed, key], `${item.label} shown`) }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
3309
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Button, { buttonType: "ghost", controlSize: "sm", hasIconOnly: true, icon: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fractal_icons17.ArrowUp, { "aria-hidden": "true" }), "aria-label": `Move ${item.label} up`, disabled: index === 0, onClick: () => move(key, -1) }),
3310
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Button, { buttonType: "ghost", controlSize: "sm", hasIconOnly: true, icon: index === visible.length - 1 && !item.required ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fractal_icons17.EyeSlash, { "aria-hidden": "true" }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fractal_icons17.ArrowDown, { "aria-hidden": "true" }), "aria-label": index === visible.length - 1 && !item.required ? `Hide ${item.label} column` : `Move ${item.label} down`, disabled: index === visible.length - 1 && item.required, onClick: () => move(key, 1) })
3311
+ ] }) })
3312
+ ]
3313
+ },
3314
+ key
3315
+ );
3316
+ }
3317
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: cn("fractal-column-editor", className), ref: root, children: [
3318
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Button, { ref: trigger, controlSize: "lg", buttonType: "secondary", icon: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fractal_icons17.Gear, { "aria-hidden": "true" }), iconPosition: "right", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": id, onClick: () => {
3319
+ cancel();
3320
+ setOpen((current) => !current);
3321
+ }, children: label }),
3322
+ open ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { id, className: "fractal-column-editor__panel", ref: panel, role: "dialog", "data-dragging": Boolean(preview) || void 0, "aria-label": label, tabIndex: -1, onPointerMove: update, onPointerUp: finish, onPointerCancel: cancel, onLostPointerCapture: cancel, children: [
3323
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ol", { className: "fractal-column-editor__list", "data-column-editor-group": "visible", "aria-label": "Visible columns", children: visible.map((key, index) => row(key, false, index)) }),
3324
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("section", { "data-column-editor-group": "hidden", className: "fractal-column-editor__hidden", "aria-label": hiddenLabel, children: [
3325
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "fractal-column-editor__divider", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { children: [
3326
+ hiddenLabel,
3327
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fractal_icons17.EyeSlash, { "aria-hidden": "true" })
3328
+ ] }) }),
3329
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ol", { className: "fractal-column-editor__list", children: hidden.map((key, index) => row(key, true, index)) }),
3330
+ !hidden.length ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "fractal-column-editor__empty", children: "Drag columns here to hide them." }) : null
3331
+ ] }),
3332
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "fractal-column-editor__announcement", role: "status", "aria-live": "polite", children: announcement })
3333
+ ] }) : null
3334
+ ] });
3335
+ }
2776
3336
  // Annotate the CommonJS export names for ESM import in node:
2777
3337
  0 && (module.exports = {
2778
3338
  Alert,
@@ -2794,6 +3354,7 @@ function Pagination({
2794
3354
  Chip,
2795
3355
  ChipGhost,
2796
3356
  CodeBlock,
3357
+ ColumnEditor,
2797
3358
  ContentSwitcher,
2798
3359
  ContentSwitcherGhost,
2799
3360
  ConversationGhost,