@tensor_1/react 2.10.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +10 -1
  2. package/dist/ChartCore-Bt1C84ty.d.cts +22 -0
  3. package/dist/chart-preset.cjs +168 -0
  4. package/dist/chart-preset.d.cts +9 -0
  5. package/dist/chart-preset.d.ts +9 -0
  6. package/dist/chart-preset.js +35 -0
  7. package/dist/chart.cjs +58 -41
  8. package/dist/chart.d.cts +6 -21
  9. package/dist/chart.d.ts +2 -1
  10. package/dist/components/Alert.js +7 -15
  11. package/dist/components/AppShell.d.ts +1 -3
  12. package/dist/components/AppShell.js +1 -3
  13. package/dist/components/Avatar.js +1 -2
  14. package/dist/components/Button.js +16 -32
  15. package/dist/components/Card.d.ts +1 -3
  16. package/dist/components/Card.js +2 -2
  17. package/dist/components/Chart.d.ts +6 -21
  18. package/dist/components/Chart.js +5 -85
  19. package/dist/components/ChartCore.d.ts +33 -0
  20. package/dist/components/ChartCore.js +100 -0
  21. package/dist/components/CommandPalette.d.ts +2 -4
  22. package/dist/components/CommandPalette.js +13 -66
  23. package/dist/components/Comments.d.ts +1 -5
  24. package/dist/components/Comments.js +1 -1
  25. package/dist/components/Composer.d.ts +1 -8
  26. package/dist/components/Composer.js +2 -7
  27. package/dist/components/DetailLayout.js +1 -1
  28. package/dist/components/Drawer.d.ts +4 -3
  29. package/dist/components/Drawer.js +4 -6
  30. package/dist/components/Field.js +3 -6
  31. package/dist/components/Inbox.d.ts +3 -13
  32. package/dist/components/Inbox.js +3 -62
  33. package/dist/components/Input.d.ts +2 -13
  34. package/dist/components/Input.js +5 -20
  35. package/dist/components/Kanban.d.ts +3 -15
  36. package/dist/components/Kanban.js +10 -42
  37. package/dist/components/Meter.d.ts +1 -3
  38. package/dist/components/Meter.js +1 -10
  39. package/dist/components/Modal.d.ts +5 -3
  40. package/dist/components/Modal.js +4 -6
  41. package/dist/components/NavFavourites.js +35 -119
  42. package/dist/components/PageHeader.d.ts +1 -3
  43. package/dist/components/PageHeader.js +2 -2
  44. package/dist/components/Peek.d.ts +4 -3
  45. package/dist/components/Peek.js +6 -9
  46. package/dist/components/Progress.d.ts +1 -3
  47. package/dist/components/Progress.js +3 -5
  48. package/dist/components/Radio.d.ts +1 -3
  49. package/dist/components/Radio.js +2 -6
  50. package/dist/components/Segmented.js +3 -2
  51. package/dist/components/Sidebar.js +2 -4
  52. package/dist/components/Spinner.d.ts +1 -3
  53. package/dist/components/Spinner.js +3 -3
  54. package/dist/components/Stat.js +1 -5
  55. package/dist/components/States.d.ts +1 -3
  56. package/dist/components/States.js +2 -8
  57. package/dist/components/Table.d.ts +2 -9
  58. package/dist/components/Table.js +8 -11
  59. package/dist/components/Tabs.d.ts +1 -13
  60. package/dist/components/Tabs.js +4 -78
  61. package/dist/components/Timeline.d.ts +3 -3
  62. package/dist/components/Timeline.js +3 -3
  63. package/dist/components/Toast.js +5 -42
  64. package/dist/components/Tooltip.d.ts +1 -3
  65. package/dist/components/Tooltip.js +5 -44
  66. package/dist/components/Tree.d.ts +0 -2
  67. package/dist/components/Tree.js +4 -26
  68. package/dist/hooks/useDialog.d.ts +1 -1
  69. package/dist/hooks/useDialog.js +2 -17
  70. package/dist/hooks/useTable.d.ts +0 -5
  71. package/dist/hooks/useTable.js +12 -27
  72. package/dist/hooks/useVirtualRows.d.ts +27 -0
  73. package/dist/hooks/useVirtualRows.js +50 -0
  74. package/dist/index.cjs +291 -760
  75. package/dist/index.d.cts +61 -127
  76. package/dist/index.d.ts +3 -2
  77. package/dist/index.js +4 -3
  78. package/dist/table-engine.js +8 -6
  79. package/dist/tree-model.d.ts +0 -4
  80. package/dist/tree-model.js +0 -8
  81. package/package.json +16 -2
package/dist/index.cjs CHANGED
@@ -103,7 +103,6 @@ __export(src_exports, {
103
103
  SubNavItem: () => SubNavItem,
104
104
  Switch: () => Switch,
105
105
  Tab: () => Tab,
106
- TabPanel: () => TabPanel,
107
106
  Table: () => Table,
108
107
  TableWrap: () => TableWrap,
109
108
  Tabs: () => Tabs,
@@ -141,6 +140,7 @@ __export(src_exports, {
141
140
  useAnchoredPosition: () => useAnchoredPosition,
142
141
  useDialog: () => useDialog,
143
142
  useTable: () => useTable,
143
+ useVirtualRows: () => useVirtualRows,
144
144
  virtualWindow: () => virtualWindow
145
145
  });
146
146
  module.exports = __toCommonJS(src_exports);
@@ -182,10 +182,8 @@ function unlockBodyScroll() {
182
182
  saved = null;
183
183
  }
184
184
  }
185
- function useDialog(open, onClose) {
185
+ function useDialog(open) {
186
186
  const ref = (0, import_react.useRef)(null);
187
- const onCloseRef = (0, import_react.useRef)(onClose);
188
- onCloseRef.current = onClose;
189
187
  (0, import_react.useEffect)(() => {
190
188
  const el = ref.current;
191
189
  if (!el || !open) {
@@ -194,21 +192,8 @@ function useDialog(open, onClose) {
194
192
  }
195
193
  if (!el.open) el.showModal();
196
194
  lockBodyScroll();
197
- let locked = true;
198
- const handleClose = () => {
199
- if (locked) {
200
- locked = false;
201
- unlockBodyScroll();
202
- }
203
- onCloseRef.current?.();
204
- };
205
- el.addEventListener("close", handleClose);
206
195
  return () => {
207
- el.removeEventListener("close", handleClose);
208
- if (locked) {
209
- locked = false;
210
- unlockBodyScroll();
211
- }
196
+ unlockBodyScroll();
212
197
  if (el.open) el.close();
213
198
  };
214
199
  }, [open]);
@@ -219,22 +204,23 @@ function useDialog(open, onClose) {
219
204
  var import_react2 = require("react");
220
205
 
221
206
  // src/table-engine.ts
207
+ var stringCollator = new Intl.Collator(void 0, { numeric: true, sensitivity: "base" });
222
208
  function compare(a, b) {
223
209
  if (a == null && b == null) return 0;
224
210
  if (a == null) return -1;
225
211
  if (b == null) return 1;
226
212
  if (typeof a === "number" && typeof b === "number") return a - b;
227
- return String(a).localeCompare(String(b), void 0, { numeric: true, sensitivity: "base" });
213
+ return stringCollator.compare(String(a), String(b));
228
214
  }
229
215
  function multiSort(rows, keys) {
230
216
  if (!keys.length) return rows.slice();
231
- return rows.map((row, i) => ({ row, i })).sort((x, y) => {
217
+ return rows.slice().sort((a, b) => {
232
218
  for (const k of keys) {
233
- const c = compare(x.row[k.field], y.row[k.field]);
219
+ const c = compare(a[k.field], b[k.field]);
234
220
  if (c !== 0) return k.dir === "ascending" ? c : -c;
235
221
  }
236
- return x.i - y.i;
237
- }).map((w) => w.row);
222
+ return 0;
223
+ });
238
224
  }
239
225
  function nextSortDir(current) {
240
226
  if (current === "ascending") return "descending";
@@ -434,7 +420,8 @@ function toggleId(set, id) {
434
420
  return next;
435
421
  }
436
422
  function selectAllState(selected, pageIds) {
437
- const on = pageIds.filter((id) => selected.has(id)).length;
423
+ let on = 0;
424
+ for (const id of pageIds) if (selected.has(id)) on += 1;
438
425
  if (on === 0) return "none";
439
426
  return on === pageIds.length ? "all" : "some";
440
427
  }
@@ -460,39 +447,25 @@ function useTable(data, options = {}) {
460
447
  const [query, setQuery] = (0, import_react2.useState)(() => options.initialQuery ?? "");
461
448
  const [selected, setSelected] = (0, import_react2.useState)(() => /* @__PURE__ */ new Set());
462
449
  const [pinned, setPinned] = (0, import_react2.useState)(() => new Set(options.initialPinned ?? []));
463
- const rows = (0, import_react2.useMemo)(() => {
464
- const filtered = applyFilters(data, filters);
465
- const searched = searchRows(filtered, query, searchFields);
466
- const sorted = multiSort(searched, sort);
467
- return mergePinned(data, sorted, pinned, idField);
468
- }, [data, filters, query, searchFields, sort, pinned, idField]);
450
+ const visibleRows = (0, import_react2.useMemo)(() => {
451
+ const filtered = filters.length ? applyFilters(data, filters) : data;
452
+ const searched = query.trim() ? searchRows(filtered, query, searchFields) : filtered;
453
+ return sort.length ? multiSort(searched, sort) : searched;
454
+ }, [data, filters, query, searchFields, sort]);
455
+ const rows = (0, import_react2.useMemo)(
456
+ () => pinned.size ? mergePinned(data, visibleRows, pinned, idField) : visibleRows.slice(),
457
+ [data, visibleRows, pinned, idField]
458
+ );
469
459
  const pageIds = (0, import_react2.useMemo)(() => rows.map((r) => String(r[idField])), [rows, idField]);
470
460
  const selectAll = selectAllState(selected, pageIds);
471
461
  const doToggleSort = (0, import_react2.useCallback)((field, additive = false) => {
472
462
  setSort((keys) => toggleSort(keys, field, additive));
473
463
  }, []);
474
464
  const toggleRow = (0, import_react2.useCallback)((id) => setSelected((s) => toggleId(s, id)), []);
465
+ const isSelected = (0, import_react2.useCallback)((id) => selected.has(id), [selected]);
475
466
  const toggleAllRows = (0, import_react2.useCallback)(() => setSelected((s) => togglePage(s, pageIds)), [pageIds]);
476
467
  const clearSelection = (0, import_react2.useCallback)(() => setSelected(/* @__PURE__ */ new Set()), []);
477
468
  const togglePin = (0, import_react2.useCallback)((id) => setPinned((s) => toggleId(s, id)), []);
478
- const [announcement, setAnnouncement] = (0, import_react2.useState)("");
479
- const mounted = (0, import_react2.useRef)(false);
480
- (0, import_react2.useEffect)(() => {
481
- if (!mounted.current) return;
482
- const key = sort[0];
483
- setAnnouncement(key ? `Sorted by ${key.field}, ${key.dir}` : "Sort cleared");
484
- }, [sort]);
485
- (0, import_react2.useEffect)(() => {
486
- if (!mounted.current) return;
487
- setAnnouncement(`${rows.length} result${rows.length === 1 ? "" : "s"}`);
488
- }, [filters, query]);
489
- (0, import_react2.useEffect)(() => {
490
- if (!mounted.current) return;
491
- setAnnouncement(`${selected.size} selected`);
492
- }, [selected]);
493
- (0, import_react2.useEffect)(() => {
494
- mounted.current = true;
495
- }, []);
496
469
  const view = (0, import_react2.useMemo)(() => ({ sort, filters, q: query }), [sort, filters, query]);
497
470
  const viewQueryString = (0, import_react2.useMemo)(() => serializeView(view), [view]);
498
471
  const applyView = (0, import_react2.useCallback)((queryString) => {
@@ -509,12 +482,11 @@ function useTable(data, options = {}) {
509
482
  selected,
510
483
  pinned,
511
484
  selectAll,
512
- announcement,
513
485
  toggleSort: doToggleSort,
514
486
  setSort,
515
487
  setFilters,
516
488
  setQuery,
517
- isSelected: (id) => selected.has(id),
489
+ isSelected,
518
490
  toggleRow,
519
491
  toggleAllRows,
520
492
  clearSelection,
@@ -525,8 +497,55 @@ function useTable(data, options = {}) {
525
497
  };
526
498
  }
527
499
 
528
- // src/hooks/useAnchoredPosition.ts
500
+ // src/hooks/useVirtualRows.ts
529
501
  var import_react3 = require("react");
502
+ function useVirtualRows(rows, { rowHeight, overscan = 6, threshold = 100 }) {
503
+ const containerRef = (0, import_react3.useRef)(null);
504
+ const [viewport, setViewport] = (0, import_react3.useState)({ scrollTop: 0, height: 0 });
505
+ (0, import_react3.useLayoutEffect)(() => {
506
+ const element = containerRef.current;
507
+ if (!element) return;
508
+ let frame = 0;
509
+ const commitMeasurement = () => {
510
+ const next = { scrollTop: element.scrollTop, height: element.clientHeight };
511
+ setViewport((current) => current.scrollTop === next.scrollTop && current.height === next.height ? current : next);
512
+ };
513
+ const measure = () => {
514
+ if (frame) return;
515
+ frame = requestAnimationFrame(() => {
516
+ frame = 0;
517
+ commitMeasurement();
518
+ });
519
+ };
520
+ commitMeasurement();
521
+ element.addEventListener("scroll", measure, { passive: true });
522
+ const observer = new ResizeObserver(measure);
523
+ observer.observe(element);
524
+ return () => {
525
+ element.removeEventListener("scroll", measure);
526
+ observer.disconnect();
527
+ if (frame) cancelAnimationFrame(frame);
528
+ };
529
+ }, []);
530
+ const enabled = viewport.height > 0 && shouldVirtualize(rows.length, threshold);
531
+ const window2 = (0, import_react3.useMemo)(
532
+ () => enabled ? virtualWindow(viewport.scrollTop, viewport.height, rowHeight, rows.length, overscan) : { start: 0, end: rows.length, padTop: 0, padBottom: 0, total: rows.length * rowHeight },
533
+ [enabled, overscan, rowHeight, rows.length, viewport.height, viewport.scrollTop]
534
+ );
535
+ const visibleRows = (0, import_react3.useMemo)(() => rows.slice(window2.start, window2.end), [rows, window2.start, window2.end]);
536
+ return {
537
+ containerRef,
538
+ rows: visibleRows,
539
+ startIndex: window2.start,
540
+ padTop: window2.padTop,
541
+ padBottom: window2.padBottom,
542
+ totalHeight: window2.total,
543
+ virtualized: enabled
544
+ };
545
+ }
546
+
547
+ // src/hooks/useAnchoredPosition.ts
548
+ var import_react4 = require("react");
530
549
 
531
550
  // src/overlay-position.ts
532
551
  var OPPOSITE = { top: "bottom", bottom: "top", left: "right", right: "left" };
@@ -590,12 +609,12 @@ function computePosition(anchor, floating, viewport, options = {}) {
590
609
  }
591
610
 
592
611
  // src/hooks/useAnchoredPosition.ts
593
- var useIsomorphicLayoutEffect = typeof document !== "undefined" ? import_react3.useLayoutEffect : import_react3.useEffect;
612
+ var useIsomorphicLayoutEffect = typeof document !== "undefined" ? import_react4.useLayoutEffect : import_react4.useEffect;
594
613
  var resolve = (a) => a && typeof a === "object" && "current" in a ? a.current : a ?? null;
595
614
  function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-start", offset = 6, padding = 8 }) {
596
- const opts = (0, import_react3.useRef)({ placement, offset, padding });
615
+ const opts = (0, import_react4.useRef)({ placement, offset, padding });
597
616
  opts.current = { placement, offset, padding };
598
- const reposition = (0, import_react3.useCallback)(() => {
617
+ const reposition = (0, import_react4.useCallback)(() => {
599
618
  const anchorEl = resolve(anchor);
600
619
  const floating = floatingRef.current;
601
620
  if (!anchorEl || !floating) return;
@@ -630,42 +649,26 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
630
649
  }
631
650
 
632
651
  // src/components/Button.tsx
633
- var import_react4 = require("react");
652
+ var import_react5 = require("react");
634
653
  var import_jsx_runtime = require("react/jsx-runtime");
635
- var warnedIconButton = false;
636
- var Button = (0, import_react4.forwardRef)(
637
- ({ variant = "secondary", size = "md", icon = false, loading = false, className, children, disabled, onClick, ...props }, ref) => {
638
- if (!warnedIconButton && icon && children == null && !props["aria-label"] && !props["aria-labelledby"] && typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
639
- warnedIconButton = true;
640
- console.warn("uix: icon-only <Button> has no accessible name \u2014 pass aria-label or aria-labelledby.");
654
+ var Button = (0, import_react5.forwardRef)(
655
+ ({ variant = "secondary", size = "md", icon = false, loading = false, className, children, disabled, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
656
+ "button",
657
+ {
658
+ ref,
659
+ className: cx(
660
+ "uix-btn",
661
+ `uix-btn--${variant}`,
662
+ size !== "md" && `uix-btn--${size}`,
663
+ icon && "uix-btn--icon",
664
+ className
665
+ ),
666
+ disabled: disabled || loading,
667
+ "data-loading": loading || void 0,
668
+ ...props,
669
+ children
641
670
  }
642
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
643
- "button",
644
- {
645
- ref,
646
- className: cx(
647
- "uix-btn",
648
- `uix-btn--${variant}`,
649
- size !== "md" && `uix-btn--${size}`,
650
- icon && "uix-btn--icon",
651
- className
652
- ),
653
- disabled,
654
- "aria-disabled": loading || void 0,
655
- "aria-busy": loading || void 0,
656
- "data-loading": loading || void 0,
657
- onClick: loading || onClick ? (e) => {
658
- if (loading) {
659
- e.preventDefault();
660
- return;
661
- }
662
- onClick?.(e);
663
- } : void 0,
664
- ...props,
665
- children
666
- }
667
- );
668
- }
671
+ )
669
672
  );
670
673
  Button.displayName = "Button";
671
674
  function ButtonGroup({ children, className }) {
@@ -673,9 +676,9 @@ function ButtonGroup({ children, className }) {
673
676
  }
674
677
 
675
678
  // src/components/Input.tsx
676
- var import_react5 = require("react");
679
+ var import_react6 = require("react");
677
680
  var import_jsx_runtime2 = require("react/jsx-runtime");
678
- var Input = (0, import_react5.forwardRef)(
681
+ var Input = (0, import_react6.forwardRef)(
679
682
  ({ size = "md", invalid, valid, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
680
683
  "input",
681
684
  {
@@ -688,22 +691,7 @@ var Input = (0, import_react5.forwardRef)(
688
691
  )
689
692
  );
690
693
  Input.displayName = "Input";
691
- function InputGroup({
692
- children,
693
- leadingIcon,
694
- trailingIcon,
695
- className,
696
- id,
697
- "aria-describedby": describedBy,
698
- "aria-invalid": invalid,
699
- "aria-required": required
700
- }) {
701
- const control = (0, import_react5.isValidElement)(children) && (id ?? describedBy ?? invalid ?? required) != null ? (0, import_react5.cloneElement)(children, {
702
- id: children.props.id ?? id,
703
- "aria-describedby": children.props["aria-describedby"] ?? describedBy,
704
- "aria-invalid": children.props["aria-invalid"] ?? invalid,
705
- "aria-required": children.props["aria-required"] ?? required
706
- }) : children;
694
+ function InputGroup({ children, leadingIcon, trailingIcon, className }) {
707
695
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
708
696
  "div",
709
697
  {
@@ -714,18 +702,18 @@ function InputGroup({
714
702
  className
715
703
  ),
716
704
  children: [
717
- leadingIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uix-input-group__icon", "aria-hidden": "true", children: leadingIcon }),
718
- control,
719
- trailingIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uix-input-group__icon uix-input-group__icon--trailing", "aria-hidden": "true", children: trailingIcon })
705
+ leadingIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uix-input-group__icon", children: leadingIcon }),
706
+ children,
707
+ trailingIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "uix-input-group__icon uix-input-group__icon--trailing", children: trailingIcon })
720
708
  ]
721
709
  }
722
710
  );
723
711
  }
724
712
 
725
713
  // src/components/Textarea.tsx
726
- var import_react6 = require("react");
714
+ var import_react7 = require("react");
727
715
  var import_jsx_runtime3 = require("react/jsx-runtime");
728
- var Textarea = (0, import_react6.forwardRef)(
716
+ var Textarea = (0, import_react7.forwardRef)(
729
717
  ({ invalid, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
730
718
  "textarea",
731
719
  {
@@ -739,9 +727,9 @@ var Textarea = (0, import_react6.forwardRef)(
739
727
  Textarea.displayName = "Textarea";
740
728
 
741
729
  // src/components/Select.tsx
742
- var import_react7 = require("react");
730
+ var import_react8 = require("react");
743
731
  var import_jsx_runtime4 = require("react/jsx-runtime");
744
- var Select = (0, import_react7.forwardRef)(
732
+ var Select = (0, import_react8.forwardRef)(
745
733
  ({ size = "md", className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
746
734
  "select",
747
735
  {
@@ -754,14 +742,14 @@ var Select = (0, import_react7.forwardRef)(
754
742
  Select.displayName = "Select";
755
743
 
756
744
  // src/components/Checkbox.tsx
757
- var import_react8 = require("react");
745
+ var import_react9 = require("react");
758
746
  var import_jsx_runtime5 = require("react/jsx-runtime");
759
747
  var CheckIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "1.5,6 4.5,9 10.5,3" }) });
760
748
  var IndeterminateIcon = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 12 12", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: "2", y: "5", width: "8", height: "2", rx: "1", fill: "currentColor" }) });
761
- var Checkbox = (0, import_react8.forwardRef)(
749
+ var Checkbox = (0, import_react9.forwardRef)(
762
750
  ({ label, indeterminate, className, ...props }, forwardedRef) => {
763
- const innerRef = (0, import_react8.useRef)(null);
764
- (0, import_react8.useEffect)(() => {
751
+ const innerRef = (0, import_react9.useRef)(null);
752
+ (0, import_react9.useEffect)(() => {
765
753
  const el = innerRef.current;
766
754
  if (el) el.indeterminate = !!indeterminate;
767
755
  }, [indeterminate]);
@@ -786,9 +774,9 @@ var Checkbox = (0, import_react8.forwardRef)(
786
774
  Checkbox.displayName = "Checkbox";
787
775
 
788
776
  // src/components/Radio.tsx
789
- var import_react9 = require("react");
777
+ var import_react10 = require("react");
790
778
  var import_jsx_runtime6 = require("react/jsx-runtime");
791
- var Radio = (0, import_react9.forwardRef)(
779
+ var Radio = (0, import_react10.forwardRef)(
792
780
  ({ label, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: cx("uix-radio", className), children: [
793
781
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "radio", ref, ...props }),
794
782
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "uix-radio__dot" }),
@@ -796,18 +784,14 @@ var Radio = (0, import_react9.forwardRef)(
796
784
  ] })
797
785
  );
798
786
  Radio.displayName = "Radio";
799
- function RadioGroup({ children, className, label }) {
800
- if (label == null) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cx("uix-radio-group", className), children });
801
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { className: cx("uix-radio-group", className), children: [
802
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { className: "uix-radio-group__legend", children: label }),
803
- children
804
- ] });
787
+ function RadioGroup({ children, className }) {
788
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cx("uix-radio-group", className), children });
805
789
  }
806
790
 
807
791
  // src/components/Switch.tsx
808
- var import_react10 = require("react");
792
+ var import_react11 = require("react");
809
793
  var import_jsx_runtime7 = require("react/jsx-runtime");
810
- var Switch = (0, import_react10.forwardRef)(
794
+ var Switch = (0, import_react11.forwardRef)(
811
795
  ({ label, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: cx("uix-switch", className), children: [
812
796
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", role: "switch", ref, ...props }),
813
797
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "uix-switch__track" }),
@@ -817,36 +801,31 @@ var Switch = (0, import_react10.forwardRef)(
817
801
  Switch.displayName = "Switch";
818
802
 
819
803
  // src/components/Field.tsx
820
- var import_react11 = require("react");
804
+ var import_react12 = require("react");
821
805
  var import_jsx_runtime8 = require("react/jsx-runtime");
822
806
  function Field({ label, hint, error, success, required, children, className, htmlFor }) {
823
- const uid = (0, import_react11.useId)();
807
+ const uid = (0, import_react12.useId)();
824
808
  const msgId = `${uid}-msg`;
825
809
  const describedBy = error || success || hint ? msgId : void 0;
826
- const controlId = (0, import_react11.isValidElement)(children) ? children.props.id ?? `${uid}-ctl` : void 0;
827
- const control = (0, import_react11.isValidElement)(children) ? (0, import_react11.cloneElement)(children, {
828
- id: controlId,
810
+ const control = (0, import_react12.isValidElement)(children) ? (0, import_react12.cloneElement)(children, {
829
811
  "aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0,
830
- "aria-invalid": error ? true : children.props["aria-invalid"],
831
- "aria-required": children.props["aria-required"] ?? (required || void 0)
812
+ "aria-invalid": error ? true : children.props["aria-invalid"]
832
813
  }) : children;
833
814
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: cx("uix-field", className), children: [
834
- label && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "uix-field__label", htmlFor: htmlFor ?? controlId, "data-required": required || void 0, children: label }),
815
+ label && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "uix-field__label", htmlFor, "data-required": required || void 0, children: label }),
835
816
  control,
836
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "uix-field__msg", id: msgId, children: error ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__error", role: "alert", children: error }) : success ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__success", role: "status", children: success }) : hint ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__hint", children: hint }) : null })
817
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "uix-field__msg", id: msgId, children: error ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__error", role: "alert", children: error }) : success ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__success", children: success }) : hint ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__hint", children: hint }) : null })
837
818
  ] });
838
819
  }
839
820
 
840
821
  // src/components/Modal.tsx
841
- var import_react12 = require("react");
842
822
  var import_jsx_runtime9 = require("react/jsx-runtime");
843
823
  var CloseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" }) });
844
- function Modal({ open, onClose, title, children, footer, className, ...rest }) {
845
- const ref = useDialog(open, onClose);
846
- const titleId = (0, import_react12.useId)();
847
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("dialog", { ref, className: cx("uix-dialog", className), "aria-labelledby": title ? titleId : void 0, ...rest, children: [
824
+ function Modal({ open, onClose, title, children, footer, className, style }) {
825
+ const ref = useDialog(open);
826
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("dialog", { ref, className: cx("uix-dialog", className), style, onClose, children: [
848
827
  (title != null || onClose) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "uix-dialog__header", children: [
849
- title && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { className: "uix-dialog__title", id: titleId, children: title }),
828
+ title && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "uix-dialog__title", children: title }),
850
829
  onClose && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "uix-dialog__close", onClick: onClose, "aria-label": "Close dialog", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CloseIcon, {}) })
851
830
  ] }),
852
831
  children != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "uix-dialog__body", children }),
@@ -855,15 +834,13 @@ function Modal({ open, onClose, title, children, footer, className, ...rest }) {
855
834
  }
856
835
 
857
836
  // src/components/Drawer.tsx
858
- var import_react13 = require("react");
859
837
  var import_jsx_runtime10 = require("react/jsx-runtime");
860
838
  var CloseIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" }) });
861
- function Drawer({ open, onClose, title, children, footer, className, ...rest }) {
862
- const ref = useDialog(open, onClose);
863
- const titleId = (0, import_react13.useId)();
864
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("dialog", { ref, className: cx("uix-drawer", className), "aria-labelledby": title ? titleId : void 0, ...rest, children: [
839
+ function Drawer({ open, onClose, title, children, footer, className }) {
840
+ const ref = useDialog(open);
841
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("dialog", { ref, className: cx("uix-drawer", className), onClose, children: [
865
842
  (title != null || onClose) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "uix-drawer__header", children: [
866
- title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { id: titleId, style: { fontWeight: 600, fontSize: "var(--uix-text-h3)", fontFamily: "inherit", letterSpacing: "inherit", lineHeight: "inherit" }, children: title }),
843
+ title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { fontWeight: 600, fontSize: "var(--uix-text-h3)" }, children: title }),
867
844
  onClose && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
868
845
  "button",
869
846
  {
@@ -880,30 +857,27 @@ function Drawer({ open, onClose, title, children, footer, className, ...rest })
880
857
  }
881
858
 
882
859
  // src/components/Peek.tsx
883
- var import_react14 = require("react");
884
860
  var import_jsx_runtime11 = require("react/jsx-runtime");
885
861
  var CloseIcon3 = () => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M4 4l8 8M12 4l-8 8" }) });
886
862
  var ChevronUp = () => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M2 8l4-4 4 4" }) });
887
863
  var ChevronDown = () => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M2 4l4 4 4-4" }) });
888
- function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className, onClick, ...rest }) {
889
- const ref = useDialog(open, onClose);
864
+ function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className }) {
865
+ const ref = useDialog(open);
890
866
  const hasNav = onNavPrev != null || onNavNext != null;
891
- const titleId = (0, import_react14.useId)();
892
867
  const onBackdropClick = (e) => {
893
- onClick?.(e);
894
868
  if (!onClose || e.target !== e.currentTarget) return;
895
869
  const r = e.currentTarget.getBoundingClientRect();
896
870
  if (r.width === 0) return;
897
871
  const outside = e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom;
898
872
  if (outside) onClose();
899
873
  };
900
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("dialog", { ref, className: cx("uix-peek", className), "aria-labelledby": title ? titleId : void 0, ...rest, onClick: onBackdropClick, children: [
874
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("dialog", { ref, className: cx("uix-peek", className), onClose, onClick: onBackdropClick, children: [
901
875
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "uix-peek__header", children: [
902
876
  hasNav && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "uix-peek__nav", children: [
903
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "uix-peek__navbtn", onClick: () => onNavPrev?.(), "aria-disabled": onNavPrev ? void 0 : true, "aria-label": "Previous record", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChevronUp, {}) }),
904
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "uix-peek__navbtn", onClick: () => onNavNext?.(), "aria-disabled": onNavNext ? void 0 : true, "aria-label": "Next record", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChevronDown, {}) })
877
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "uix-peek__navbtn", onClick: onNavPrev, disabled: !onNavPrev, "aria-label": "Previous record", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChevronUp, {}) }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "uix-peek__navbtn", onClick: onNavNext, disabled: !onNavNext, "aria-label": "Next record", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChevronDown, {}) })
905
879
  ] }),
906
- title && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { className: "uix-peek__title", id: titleId, children: title }),
880
+ title && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "uix-peek__title", children: title }),
907
881
  hint && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "uix-peek__hint", children: hint }),
908
882
  onClose && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "uix-peek__close", onClick: onClose, "aria-label": "Close preview", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CloseIcon3, {}) })
909
883
  ] }),
@@ -914,12 +888,12 @@ function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hi
914
888
 
915
889
  // src/components/Card.tsx
916
890
  var import_jsx_runtime12 = require("react/jsx-runtime");
917
- function Card({ title, titleAs: TitleTag = "div", subtitle, headerAction, footer, children, className, ...props }) {
891
+ function Card({ title, subtitle, headerAction, footer, children, className, ...props }) {
918
892
  const hasHeader = title != null || subtitle != null || headerAction != null;
919
893
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: cx("uix-card", className), ...props, children: [
920
894
  hasHeader && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "uix-card__header", children: [
921
895
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { flex: 1 }, children: [
922
- title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TitleTag, { className: "uix-card__title", children: title }),
896
+ title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "uix-card__title", children: title }),
923
897
  subtitle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "uix-card__subtitle", children: subtitle })
924
898
  ] }),
925
899
  headerAction
@@ -931,11 +905,11 @@ function Card({ title, titleAs: TitleTag = "div", subtitle, headerAction, footer
931
905
 
932
906
  // src/components/PageHeader.tsx
933
907
  var import_jsx_runtime13 = require("react/jsx-runtime");
934
- function PageHeader({ eyebrow, title, subtitle, actions, as: TitleTag = "h1", className, ...props }) {
908
+ function PageHeader({ eyebrow, title, subtitle, actions, className, ...props }) {
935
909
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("header", { className: cx("uix-page-header", className), ...props, children: [
936
910
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "uix-page-header__titles", children: [
937
911
  eyebrow != null && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "uix-page-header__eyebrow", children: eyebrow }),
938
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TitleTag, { className: "uix-page-header__title", children: title }),
912
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h1", { className: "uix-page-header__title", children: title }),
939
913
  subtitle != null && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "uix-page-header__subtitle", children: subtitle })
940
914
  ] }),
941
915
  actions != null && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "uix-page-header__actions", children: actions })
@@ -947,7 +921,7 @@ var import_jsx_runtime14 = require("react/jsx-runtime");
947
921
  function DetailLayout({ side, children, className, ...props }) {
948
922
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: cx("uix-detail", className), ...props, children: [
949
923
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "uix-detail__main", children }),
950
- side != null && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("aside", { className: "uix-detail__side", children: side })
924
+ side != null && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "uix-detail__side", children: side })
951
925
  ] });
952
926
  }
953
927
 
@@ -970,11 +944,11 @@ function ListItem({ title, meta, trail, leading, children, className, ...props }
970
944
  }
971
945
 
972
946
  // src/components/DescriptionList.tsx
973
- var import_react15 = require("react");
947
+ var import_react13 = require("react");
974
948
  var import_jsx_runtime16 = require("react/jsx-runtime");
975
949
  function DescriptionList({ items, children, className, ...props }) {
976
950
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("dl", { className: cx("uix-dl", className), ...props, children: [
977
- items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react15.Fragment, { children: [
951
+ items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react13.Fragment, { children: [
978
952
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dt", { children: item.term }),
979
953
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dd", { children: item.description })
980
954
  ] }, item.id ?? i)),
@@ -989,80 +963,22 @@ function DescriptionItem({ term, children }) {
989
963
  }
990
964
 
991
965
  // src/components/Tabs.tsx
992
- var import_react16 = require("react");
966
+ var import_react14 = require("react");
993
967
  var import_jsx_runtime17 = require("react/jsx-runtime");
994
- var TabsCtx = (0, import_react16.createContext)({});
968
+ var TabsCtx = (0, import_react14.createContext)({});
995
969
  function Tabs({ variant = "line", value, onChange, children, className }) {
996
- const baseId = (0, import_react16.useId)();
997
- const listRef = (0, import_react16.useRef)(null);
998
- const kids = import_react16.Children.toArray(children);
999
- const panels = kids.filter((k) => (0, import_react16.isValidElement)(k) && k.type === TabPanel);
1000
- const tabs = kids.filter((k) => !((0, import_react16.isValidElement)(k) && k.type === TabPanel));
1001
- (0, import_react16.useEffect)(() => {
1002
- const list = listRef.current;
1003
- if (!list) return;
1004
- const all = Array.from(list.querySelectorAll('[role="tab"]'));
1005
- const enabled = all.filter((t) => !t.matches(":disabled"));
1006
- const active = enabled.find((t) => t.getAttribute("aria-selected") === "true") ?? enabled[0];
1007
- for (const t of all) t.tabIndex = t === active ? 0 : -1;
1008
- });
1009
- const onKeyDown = (e) => {
1010
- const items = Array.from(
1011
- listRef.current?.querySelectorAll('[role="tab"]:not(:disabled)') ?? []
1012
- );
1013
- const cur = e.target.closest('[role="tab"]');
1014
- const idx = cur ? items.indexOf(cur) : -1;
1015
- if (idx < 0 || items.length === 0) return;
1016
- let next;
1017
- switch (e.key) {
1018
- case "ArrowRight":
1019
- next = (idx + 1) % items.length;
1020
- break;
1021
- case "ArrowLeft":
1022
- next = (idx - 1 + items.length) % items.length;
1023
- break;
1024
- case "Home":
1025
- next = 0;
1026
- break;
1027
- case "End":
1028
- next = items.length - 1;
1029
- break;
1030
- default:
1031
- return;
1032
- }
1033
- e.preventDefault();
1034
- const target = items[next];
1035
- if (target) {
1036
- target.focus();
1037
- target.click();
1038
- }
1039
- };
1040
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(TabsCtx.Provider, { value: { value, onChange, baseId }, children: [
1041
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1042
- "div",
1043
- {
1044
- ref: listRef,
1045
- role: "tablist",
1046
- className: cx("uix-tabs", `uix-tabs--${variant}`, className),
1047
- onKeyDown,
1048
- children: tabs
1049
- }
1050
- ),
1051
- panels
1052
- ] });
970
+ const contextValue = (0, import_react14.useMemo)(() => ({ value, onChange }), [value, onChange]);
971
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TabsCtx.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { role: "tablist", className: cx("uix-tabs", `uix-tabs--${variant}`, className), children }) });
1053
972
  }
1054
973
  function Tab({ value, children, disabled, className, onClick, ...props }) {
1055
- const ctx = (0, import_react16.useContext)(TabsCtx);
974
+ const ctx = (0, import_react14.useContext)(TabsCtx);
1056
975
  const selected = ctx.value === value;
1057
976
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1058
977
  "button",
1059
978
  {
1060
979
  role: "tab",
1061
- id: ctx.baseId ? `${ctx.baseId}-tab-${value}` : void 0,
1062
- "aria-controls": ctx.baseId ? `${ctx.baseId}-panel-${value}` : void 0,
1063
980
  className: cx("uix-tab", className),
1064
981
  "aria-selected": selected,
1065
- tabIndex: selected ? 0 : -1,
1066
982
  disabled,
1067
983
  onClick: (e) => {
1068
984
  ctx.onChange?.(value);
@@ -1073,24 +989,9 @@ function Tab({ value, children, disabled, className, onClick, ...props }) {
1073
989
  }
1074
990
  );
1075
991
  }
1076
- function TabPanel({ value, children, ...props }) {
1077
- const ctx = (0, import_react16.useContext)(TabsCtx);
1078
- if (ctx.value !== value) return null;
1079
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1080
- "div",
1081
- {
1082
- role: "tabpanel",
1083
- id: ctx.baseId ? `${ctx.baseId}-panel-${value}` : void 0,
1084
- "aria-labelledby": ctx.baseId ? `${ctx.baseId}-tab-${value}` : void 0,
1085
- tabIndex: 0,
1086
- ...props,
1087
- children
1088
- }
1089
- );
1090
- }
1091
992
 
1092
993
  // src/components/AppShell.tsx
1093
- var import_react17 = require("react");
994
+ var import_react15 = require("react");
1094
995
  var import_jsx_runtime18 = require("react/jsx-runtime");
1095
996
  function AppShell({
1096
997
  nav,
@@ -1098,7 +999,6 @@ function AppShell({
1098
999
  focus,
1099
1000
  onExitFocus,
1100
1001
  mainBleed,
1101
- mainId = "uix-main",
1102
1002
  sidebar,
1103
1003
  topbar,
1104
1004
  children,
@@ -1106,7 +1006,7 @@ function AppShell({
1106
1006
  ...props
1107
1007
  }) {
1108
1008
  const navState = nav ?? (collapsed ? "rail" : "full");
1109
- (0, import_react17.useEffect)(() => {
1009
+ (0, import_react15.useEffect)(() => {
1110
1010
  if (!focus || !onExitFocus) return;
1111
1011
  const onKey = (e) => {
1112
1012
  if (e.key === "Escape") onExitFocus();
@@ -1123,10 +1023,9 @@ function AppShell({
1123
1023
  "data-focus": focus || void 0,
1124
1024
  ...props,
1125
1025
  children: [
1126
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("a", { className: "uix-skiplink", href: `#${mainId}`, children: "Skip to content" }),
1127
1026
  sidebar && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "uix-shell__sidebar", children: sidebar }),
1128
1027
  topbar && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "uix-shell__topbar", children: topbar }),
1129
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("main", { id: mainId, tabIndex: -1, className: cx("uix-shell__main", mainBleed && "uix-shell__main--bleed"), children }),
1028
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("main", { className: cx("uix-shell__main", mainBleed && "uix-shell__main--bleed"), children }),
1130
1029
  focus && onExitFocus && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "uix-shell__focus-exit", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: onExitFocus, children: "Esc \xB7 Exit focus" }) })
1131
1030
  ]
1132
1031
  }
@@ -1134,7 +1033,7 @@ function AppShell({
1134
1033
  }
1135
1034
 
1136
1035
  // src/components/Sidebar.tsx
1137
- var import_react18 = require("react");
1036
+ var import_react16 = require("react");
1138
1037
  var import_jsx_runtime19 = require("react/jsx-runtime");
1139
1038
  var ChevronIcon = () => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("svg", { className: "uix-navgroup__chevron", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "M2 4l4 4 4-4" }) });
1140
1039
  function Sidebar({ collapsed, onToggle, brand, toggleIcon, children, className, ...props }) {
@@ -1188,15 +1087,13 @@ function NavItem({ icon, badge, active, as: Tag = "a", children, className, ...p
1188
1087
  );
1189
1088
  }
1190
1089
  function NavGroup({ icon, label, children, defaultExpanded = true }) {
1191
- const [expanded, setExpanded] = (0, import_react18.useState)(defaultExpanded);
1192
- const panelId = (0, import_react18.useId)();
1090
+ const [expanded, setExpanded] = (0, import_react16.useState)(defaultExpanded);
1193
1091
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "uix-navgroup", children: [
1194
1092
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
1195
1093
  "button",
1196
1094
  {
1197
1095
  className: "uix-navitem uix-navgroup__trigger",
1198
1096
  "aria-expanded": expanded,
1199
- "aria-controls": panelId,
1200
1097
  onClick: () => setExpanded((v) => !v),
1201
1098
  children: [
1202
1099
  icon && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "uix-navitem__icon", "aria-hidden": "true", children: icon }),
@@ -1205,7 +1102,7 @@ function NavGroup({ icon, label, children, defaultExpanded = true }) {
1205
1102
  ]
1206
1103
  }
1207
1104
  ),
1208
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "uix-navgroup__panel", id: panelId, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { children }) })
1105
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "uix-navgroup__panel", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { children }) })
1209
1106
  ] });
1210
1107
  }
1211
1108
  function SubNavItem({ active, as: Tag = "a", children, className, ...props }) {
@@ -1258,7 +1155,7 @@ function StarButton({
1258
1155
  }
1259
1156
 
1260
1157
  // src/components/NavFavourites.tsx
1261
- var import_react19 = require("react");
1158
+ var import_react17 = require("react");
1262
1159
  var import_jsx_runtime21 = require("react/jsx-runtime");
1263
1160
  var ChevronDownIcon = () => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1264
1161
  "svg",
@@ -1272,7 +1169,7 @@ var ChevronDownIcon = () => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1272
1169
  strokeLinecap: "round",
1273
1170
  strokeLinejoin: "round",
1274
1171
  "aria-hidden": "true",
1275
- style: { flexShrink: 0 },
1172
+ className: "uix-favourites__chevron",
1276
1173
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "m6 9 6 6 6-6" })
1277
1174
  }
1278
1175
  );
@@ -1288,7 +1185,7 @@ var ChevronRightIcon = () => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1288
1185
  strokeLinecap: "round",
1289
1186
  strokeLinejoin: "round",
1290
1187
  "aria-hidden": "true",
1291
- style: { flexShrink: 0 },
1188
+ className: "uix-favourites__chevron",
1292
1189
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "m9 18 6-6-6-6" })
1293
1190
  }
1294
1191
  );
@@ -1311,7 +1208,6 @@ var MoreIcon = () => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1311
1208
  ]
1312
1209
  }
1313
1210
  );
1314
- var MUTED = "color-mix(in srgb, var(--uix-text) 50%, transparent)";
1315
1211
  function NavFavourites({
1316
1212
  items,
1317
1213
  collapsed = false,
@@ -1322,21 +1218,20 @@ function NavFavourites({
1322
1218
  emptyReason,
1323
1219
  className
1324
1220
  }) {
1325
- const regionId = (0, import_react19.useId)();
1326
- const headerRef = (0, import_react19.useRef)(null);
1327
- const regionRef = (0, import_react19.useRef)(null);
1328
- const [announcement, setAnnouncement] = (0, import_react19.useState)("");
1329
- const focusAfterRef = (0, import_react19.useRef)(null);
1330
- const triggerRefs = (0, import_react19.useRef)(/* @__PURE__ */ new Map());
1331
- const order = items.map((i) => i.id);
1332
- (0, import_react19.useEffect)(() => {
1221
+ const regionId = (0, import_react17.useId)();
1222
+ const headerRef = (0, import_react17.useRef)(null);
1223
+ const regionRef = (0, import_react17.useRef)(null);
1224
+ const focusAfterRef = (0, import_react17.useRef)(null);
1225
+ const triggerRefs = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
1226
+ const order = (0, import_react17.useMemo)(() => items.map((item) => item.id), [items]);
1227
+ (0, import_react17.useEffect)(() => {
1333
1228
  const id = focusAfterRef.current;
1334
1229
  if (!id) return;
1335
1230
  focusAfterRef.current = null;
1336
1231
  const el = triggerRefs.current.get(id);
1337
1232
  if (el) el.focus();
1338
1233
  else headerRef.current?.focus();
1339
- }, [order.join("|")]);
1234
+ }, [order]);
1340
1235
  const handleToggle = () => {
1341
1236
  if (!collapsed) {
1342
1237
  const active = document.activeElement;
@@ -1346,7 +1241,7 @@ function NavFavourites({
1346
1241
  }
1347
1242
  onToggleCollapsed?.();
1348
1243
  };
1349
- const move = (id, dir) => {
1244
+ const move = (0, import_react17.useCallback)((id, dir) => {
1350
1245
  const idx = order.indexOf(id);
1351
1246
  const next = idx + dir;
1352
1247
  if (idx < 0 || next < 0 || next >= order.length) return;
@@ -1355,18 +1250,19 @@ function NavFavourites({
1355
1250
  if (it === void 0) return;
1356
1251
  reordered.splice(next, 0, it);
1357
1252
  focusAfterRef.current = id;
1358
- const moved = items.find((i) => i.id === id);
1359
- if (moved) setAnnouncement(`${moved.label} moved to position ${next + 1} of ${order.length}`);
1360
1253
  onReorder(reordered);
1361
- };
1362
- const remove = (id) => {
1254
+ }, [onReorder, order]);
1255
+ const remove = (0, import_react17.useCallback)((id) => {
1363
1256
  const idx = order.indexOf(id);
1364
1257
  const neighbour = order[idx + 1] ?? order[idx - 1] ?? null;
1365
1258
  focusAfterRef.current = neighbour;
1366
1259
  onRemove(id);
1367
- };
1368
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: cx("uix-favourites", className), style: { marginBottom: "var(--uix-space-1)" }, children: [
1369
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
1260
+ }, [onRemove, order]);
1261
+ const registerTrigger = (0, import_react17.useCallback)((id, element) => {
1262
+ if (element) triggerRefs.current.set(id, element);
1263
+ else triggerRefs.current.delete(id);
1264
+ }, []);
1265
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: cx("uix-favourites", className), children: [
1370
1266
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1371
1267
  "button",
1372
1268
  {
@@ -1375,18 +1271,7 @@ function NavFavourites({
1375
1271
  "aria-expanded": !collapsed,
1376
1272
  "aria-controls": regionId,
1377
1273
  onClick: handleToggle,
1378
- className: "uix-text-eyebrow",
1379
- style: {
1380
- display: "flex",
1381
- width: "100%",
1382
- alignItems: "center",
1383
- gap: "6px",
1384
- padding: "8px 4px 4px",
1385
- border: 0,
1386
- background: "transparent",
1387
- cursor: "pointer",
1388
- color: "inherit"
1389
- },
1274
+ className: "uix-favourites__heading uix-text-eyebrow",
1390
1275
  children: [
1391
1276
  collapsed ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ChevronRightIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ChevronDownIcon, {}),
1392
1277
  labels.heading
@@ -1399,20 +1284,12 @@ function NavFavourites({
1399
1284
  ref: regionRef,
1400
1285
  id: regionId,
1401
1286
  hidden: collapsed,
1402
- style: {
1403
- display: "flex",
1404
- flexDirection: "column",
1405
- gap: "1px",
1406
- listStyle: "none",
1407
- margin: 0,
1408
- padding: 0
1409
- },
1287
+ className: "uix-favourites__list",
1410
1288
  children: items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1411
1289
  "li",
1412
1290
  {
1413
- className: "uix-text-meta",
1291
+ className: "uix-favourites__empty uix-text-meta",
1414
1292
  "data-empty-reason": emptyReason,
1415
- style: { padding: "6px 8px", color: MUTED },
1416
1293
  children: labels.empty
1417
1294
  }
1418
1295
  ) : items.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
@@ -1422,10 +1299,9 @@ function NavFavourites({
1422
1299
  isFirst: idx === 0,
1423
1300
  isLast: idx === items.length - 1,
1424
1301
  labels,
1425
- onMoveUp: () => move(item.id, -1),
1426
- onMoveDown: () => move(item.id, 1),
1427
- onRemove: () => remove(item.id),
1428
- registerTrigger: (el) => triggerRefs.current.set(item.id, el)
1302
+ onMove: move,
1303
+ onRemove: remove,
1304
+ registerTrigger
1429
1305
  },
1430
1306
  item.id
1431
1307
  ))
@@ -1433,28 +1309,26 @@ function NavFavourites({
1433
1309
  )
1434
1310
  ] });
1435
1311
  }
1436
- function FavouriteRow({
1312
+ var FavouriteRow = (0, import_react17.memo)(function FavouriteRow2({
1437
1313
  item,
1438
1314
  isFirst,
1439
1315
  isLast,
1440
1316
  labels,
1441
- onMoveUp,
1442
- onMoveDown,
1317
+ onMove,
1443
1318
  onRemove,
1444
1319
  registerTrigger
1445
1320
  }) {
1446
- const [open, setOpen] = (0, import_react19.useState)(false);
1447
- const [revealed, setRevealed] = (0, import_react19.useState)(false);
1448
- const menuId = (0, import_react19.useId)();
1449
- const triggerRef = (0, import_react19.useRef)(null);
1450
- const itemRefs = (0, import_react19.useRef)([]);
1321
+ const [open, setOpen] = (0, import_react17.useState)(false);
1322
+ const menuId = (0, import_react17.useId)();
1323
+ const triggerRef = (0, import_react17.useRef)(null);
1324
+ const itemRefs = (0, import_react17.useRef)([]);
1451
1325
  const optionsTemplate = labels.options ?? "{label} favourite options";
1452
1326
  const actions = [
1453
- ...isFirst ? [] : [{ label: labels.moveUp, onSelect: onMoveUp }],
1454
- ...isLast ? [] : [{ label: labels.moveDown, onSelect: onMoveDown }],
1455
- { label: labels.remove.replace("{label}", item.label), onSelect: onRemove }
1327
+ ...isFirst ? [] : [{ label: labels.moveUp, onSelect: () => onMove(item.id, -1) }],
1328
+ ...isLast ? [] : [{ label: labels.moveDown, onSelect: () => onMove(item.id, 1) }],
1329
+ { label: labels.remove.replace("{label}", item.label), onSelect: () => onRemove(item.id) }
1456
1330
  ];
1457
- (0, import_react19.useEffect)(() => {
1331
+ (0, import_react17.useEffect)(() => {
1458
1332
  if (open) itemRefs.current[0]?.focus();
1459
1333
  }, [open]);
1460
1334
  const closeAndReturnFocus = () => {
@@ -1470,12 +1344,6 @@ function FavouriteRow({
1470
1344
  } else if (e.key === "ArrowUp") {
1471
1345
  e.preventDefault();
1472
1346
  menuItems[(currentIdx - 1 + menuItems.length) % menuItems.length]?.focus();
1473
- } else if (e.key === "Home") {
1474
- e.preventDefault();
1475
- menuItems[0]?.focus();
1476
- } else if (e.key === "End") {
1477
- e.preventDefault();
1478
- menuItems[menuItems.length - 1]?.focus();
1479
1347
  } else if (e.key === "Escape") {
1480
1348
  e.preventDefault();
1481
1349
  closeAndReturnFocus();
@@ -1487,34 +1355,27 @@ function FavouriteRow({
1487
1355
  if (!e.altKey) return;
1488
1356
  if (e.key === "ArrowUp" && !isFirst) {
1489
1357
  e.preventDefault();
1490
- onMoveUp();
1358
+ onMove(item.id, -1);
1491
1359
  } else if (e.key === "ArrowDown" && !isLast) {
1492
1360
  e.preventDefault();
1493
- onMoveDown();
1361
+ onMove(item.id, 1);
1494
1362
  }
1495
1363
  };
1496
- const triggerVisible = revealed || open;
1497
1364
  return (
1498
1365
  // biome-ignore lint/a11y/noStaticElementInteractions: keyboard accelerators on the row; the focusable controls inside carry their own semantics.
1499
1366
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1500
1367
  "li",
1501
1368
  {
1502
- style: { position: "relative", display: "flex", alignItems: "center" },
1369
+ className: "uix-favourites__row",
1503
1370
  onKeyDown: onRowKeyDown,
1504
- onMouseEnter: () => setRevealed(true),
1505
- onMouseLeave: () => setRevealed(false),
1506
- onFocus: () => setRevealed(true),
1507
- onBlur: (e) => {
1508
- if (!e.currentTarget.contains(e.relatedTarget)) setRevealed(false);
1509
- },
1510
1371
  children: [
1511
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { minWidth: 0, flex: 1 }, children: item.link }),
1372
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "uix-favourites__link", children: item.link }),
1512
1373
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1513
1374
  "button",
1514
1375
  {
1515
1376
  ref: (el) => {
1516
1377
  triggerRef.current = el;
1517
- registerTrigger(el);
1378
+ registerTrigger(item.id, el);
1518
1379
  },
1519
1380
  type: "button",
1520
1381
  "aria-haspopup": "menu",
@@ -1522,22 +1383,7 @@ function FavouriteRow({
1522
1383
  "aria-controls": open ? menuId : void 0,
1523
1384
  "aria-label": optionsTemplate.replace("{label}", item.label),
1524
1385
  onClick: () => setOpen((v) => !v),
1525
- style: {
1526
- marginLeft: "auto",
1527
- display: "inline-flex",
1528
- height: "20px",
1529
- width: "20px",
1530
- flexShrink: 0,
1531
- alignItems: "center",
1532
- justifyContent: "center",
1533
- border: 0,
1534
- borderRadius: "2px",
1535
- background: "transparent",
1536
- cursor: "pointer",
1537
- color: MUTED,
1538
- opacity: triggerVisible ? 1 : 0,
1539
- transition: "opacity var(--uix-dur-fast) var(--uix-ease-out)"
1540
- },
1386
+ className: "uix-favourites__menu-trigger",
1541
1387
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(MoreIcon, {})
1542
1388
  }
1543
1389
  ),
@@ -1548,7 +1394,7 @@ function FavouriteRow({
1548
1394
  "aria-hidden": "true",
1549
1395
  role: "presentation",
1550
1396
  onClick: () => setOpen(false),
1551
- style: { position: "fixed", inset: 0, cursor: "default", background: "transparent", zIndex: 40 }
1397
+ className: "uix-favourites__backdrop"
1552
1398
  }
1553
1399
  ),
1554
1400
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
@@ -1556,23 +1402,8 @@ function FavouriteRow({
1556
1402
  {
1557
1403
  id: menuId,
1558
1404
  role: "menu",
1559
- "aria-label": optionsTemplate.replace("{label}", item.label),
1560
1405
  onKeyDown: onMenuKeyDown,
1561
- style: {
1562
- position: "absolute",
1563
- right: 0,
1564
- top: "calc(100% + 2px)",
1565
- zIndex: 50,
1566
- minWidth: "160px",
1567
- overflow: "hidden",
1568
- margin: 0,
1569
- padding: "4px 0",
1570
- listStyle: "none",
1571
- borderRadius: "6px",
1572
- border: "1px solid var(--uix-border)",
1573
- background: "var(--uix-surface)",
1574
- boxShadow: "var(--uix-shadow-popover)"
1575
- },
1406
+ className: "uix-favourites__menu",
1576
1407
  children: actions.map((action, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { role: "presentation", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1577
1408
  "button",
1578
1409
  {
@@ -1586,25 +1417,7 @@ function FavouriteRow({
1586
1417
  setOpen(false);
1587
1418
  action.onSelect();
1588
1419
  },
1589
- style: {
1590
- display: "flex",
1591
- width: "100%",
1592
- alignItems: "center",
1593
- padding: "6px 12px",
1594
- border: 0,
1595
- background: "transparent",
1596
- cursor: "pointer",
1597
- textAlign: "left",
1598
- fontSize: "13px",
1599
- color: "color-mix(in srgb, var(--uix-text) 85%, transparent)",
1600
- transition: "background-color var(--uix-dur-fast) var(--uix-ease-out)"
1601
- },
1602
- onMouseEnter: (e) => {
1603
- e.currentTarget.style.background = "var(--uix-bg-hover)";
1604
- },
1605
- onMouseLeave: (e) => {
1606
- e.currentTarget.style.background = "transparent";
1607
- },
1420
+ className: "uix-favourites__menu-item",
1608
1421
  children: action.label
1609
1422
  }
1610
1423
  ) }, action.label))
@@ -1615,113 +1428,67 @@ function FavouriteRow({
1615
1428
  }
1616
1429
  )
1617
1430
  );
1618
- }
1431
+ });
1619
1432
 
1620
1433
  // src/components/Alert.tsx
1621
1434
  var import_jsx_runtime22 = require("react/jsx-runtime");
1622
1435
  function Alert({ tone, title, icon, children, className, ...props }) {
1623
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
1624
- "div",
1625
- {
1626
- className: cx("uix-alert", tone && tone !== "neutral" && `uix-alert--${tone}`, className),
1627
- role: tone === "danger" || tone === "warning" ? "alert" : "status",
1628
- ...props,
1629
- children: [
1630
- icon && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "uix-alert__icon", children: icon }),
1631
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
1632
- title && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "uix-alert__title", children: title }),
1633
- children && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "uix-alert__body", children })
1634
- ] })
1635
- ]
1636
- }
1637
- );
1436
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: cx("uix-alert", tone && tone !== "neutral" && `uix-alert--${tone}`, className), ...props, children: [
1437
+ icon && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "uix-alert__icon", children: icon }),
1438
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
1439
+ title && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "uix-alert__title", children: title }),
1440
+ children && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "uix-alert__body", children })
1441
+ ] })
1442
+ ] });
1638
1443
  }
1639
1444
 
1640
1445
  // src/components/Spinner.tsx
1641
1446
  var import_jsx_runtime23 = require("react/jsx-runtime");
1642
- function Spinner({ size = "md", accent, label, className, ...props }) {
1447
+ function Spinner({ size = "md", accent, className, ...props }) {
1643
1448
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1644
1449
  "span",
1645
1450
  {
1646
1451
  className: cx("uix-spinner", accent && "uix-spinner--accent", size === "lg" && "uix-spinner--lg", className),
1647
1452
  role: "status",
1648
- ...props,
1649
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "uix-visually-hidden", children: label ?? "Loading\u2026" })
1453
+ "aria-label": "Loading",
1454
+ ...props
1650
1455
  }
1651
1456
  );
1652
1457
  }
1653
1458
 
1654
1459
  // src/components/Toast.tsx
1655
- var import_react20 = require("react");
1656
1460
  var import_jsx_runtime24 = require("react/jsx-runtime");
1657
- var ToasterContext = (0, import_react20.createContext)(null);
1658
1461
  function Toast({ title, message, tone, icon, onClose, leaving, className, ...props }) {
1659
1462
  const assertive = tone === "danger";
1660
- const toaster = (0, import_react20.useContext)(ToasterContext);
1661
- const bodyRef = (0, import_react20.useRef)(null);
1662
- (0, import_react20.useEffect)(() => {
1663
- if (assertive || !toaster) return;
1664
- const text = bodyRef.current?.textContent?.trim();
1665
- if (text) toaster.announce(text);
1666
- }, []);
1667
- const handleClose = (e) => {
1668
- const btn = e.currentTarget;
1669
- if (document.activeElement === btn) {
1670
- const toasterEl = btn.closest(".uix-toaster");
1671
- if (toasterEl) {
1672
- const closes = Array.from(toasterEl.querySelectorAll(".uix-toast__close"));
1673
- const i = closes.indexOf(btn);
1674
- (closes[i + 1] ?? closes[i - 1])?.focus();
1675
- }
1676
- }
1677
- onClose?.();
1678
- };
1679
- const ownLiveRegion = assertive || !toaster;
1680
1463
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
1681
1464
  "div",
1682
1465
  {
1683
1466
  className: cx("uix-toast", tone && `uix-toast--${tone}`, className),
1684
1467
  "data-leaving": leaving || void 0,
1685
- role: ownLiveRegion ? assertive ? "alert" : "status" : void 0,
1686
- "aria-live": ownLiveRegion ? assertive ? "assertive" : "polite" : void 0,
1468
+ role: assertive ? "alert" : "status",
1469
+ "aria-live": assertive ? "assertive" : "polite",
1687
1470
  ...props,
1688
1471
  children: [
1689
1472
  icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__icon", "aria-hidden": "true", children: icon }),
1690
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "uix-toast__body", ref: bodyRef, children: [
1473
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "uix-toast__body", children: [
1691
1474
  title && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__title", children: title }),
1692
1475
  message && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__msg", children: message })
1693
1476
  ] }),
1694
- onClose && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { className: "uix-toast__close", onClick: handleClose, "aria-label": "Dismiss", children: "\xD7" })
1477
+ onClose && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { className: "uix-toast__close", onClick: onClose, "aria-label": "Dismiss", children: "\xD7" })
1695
1478
  ]
1696
1479
  }
1697
1480
  );
1698
1481
  }
1699
1482
  function Toaster({ children, className, ...props }) {
1700
- const [announced, setAnnounced] = (0, import_react20.useState)("");
1701
- const clearTimer = (0, import_react20.useRef)(null);
1702
- const announce = (0, import_react20.useCallback)((text) => {
1703
- setAnnounced((cur) => cur ? `${cur} ${text}` : text);
1704
- if (clearTimer.current != null) clearTimeout(clearTimer.current);
1705
- clearTimer.current = window.setTimeout(() => setAnnounced(""), 3e3);
1706
- }, []);
1707
- (0, import_react20.useEffect)(() => () => {
1708
- if (clearTimer.current != null) clearTimeout(clearTimer.current);
1709
- }, []);
1710
- const ctx = (0, import_react20.useMemo)(() => ({ announce }), [announce]);
1711
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ToasterContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: cx("uix-toaster", className), role: "region", "aria-label": "Notifications", ...props, children: [
1712
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "uix-visually-hidden", role: "status", "aria-live": "polite", children: announced }),
1713
- children
1714
- ] }) });
1483
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cx("uix-toaster", className), ...props, children });
1715
1484
  }
1716
1485
 
1717
1486
  // src/components/Table.tsx
1487
+ var import_react18 = require("react");
1718
1488
  var import_jsx_runtime25 = require("react/jsx-runtime");
1719
- function TableWrap({ announcement, children, className, ...props }) {
1720
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: cx("uix-table-wrap", className), ...props, children: [
1721
- announcement != null && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
1722
- children
1723
- ] });
1724
- }
1489
+ var TableWrap = (0, import_react18.forwardRef)(function TableWrap2({ children, className, ...props }, ref) {
1490
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { ref, className: cx("uix-table-wrap", className), ...props, children });
1491
+ });
1725
1492
  function Table({
1726
1493
  density = "standard",
1727
1494
  zebra = true,
@@ -1759,9 +1526,9 @@ function Th({ sortable, sortDirection, sortOrder, onSort, className, children, o
1759
1526
  "data-sort": sortable || void 0,
1760
1527
  "data-sort-order": sortOrder != null && sortOrder > 0 ? sortOrder : void 0,
1761
1528
  "aria-sort": sortDirection && sortDirection !== "none" ? sortDirection : void 0,
1762
- onClick: sortable && onSort ? void 0 : onClick,
1529
+ onClick: sortable ? onSort ?? onClick : onClick,
1763
1530
  ...props,
1764
- children: sortable ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", className: "uix-th__sortbtn", onClick: onSort, children }) : children
1531
+ children
1765
1532
  }
1766
1533
  );
1767
1534
  }
@@ -1773,7 +1540,7 @@ function Tr({ selected, pinned, className, children, ...props }) {
1773
1540
  "tr",
1774
1541
  {
1775
1542
  className,
1776
- "data-selected": selected || void 0,
1543
+ "aria-selected": selected || void 0,
1777
1544
  "data-pinned": pinned || void 0,
1778
1545
  ...props,
1779
1546
  children
@@ -1796,14 +1563,13 @@ function RowActions({ children, className, ...props }) {
1796
1563
  function RowAction({ children, className, type = "button", ...props }) {
1797
1564
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type, className: cx("uix-rowact", className), ...props, children });
1798
1565
  }
1799
- function ExpandToggle({ expanded, controls, className, type = "button", ...props }) {
1566
+ function ExpandToggle({ expanded, className, type = "button", ...props }) {
1800
1567
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1801
1568
  "button",
1802
1569
  {
1803
1570
  type,
1804
1571
  className: cx("uix-table__expand", className),
1805
1572
  "aria-expanded": expanded,
1806
- "aria-controls": controls,
1807
1573
  "aria-label": expanded ? "Collapse row" : "Expand row",
1808
1574
  ...props,
1809
1575
  children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("path", { d: "M6 4l4 4-4 4" }) })
@@ -1894,17 +1660,15 @@ function StatusPill({ tone = "neutral", treatment = "filled", dot, children, cla
1894
1660
 
1895
1661
  // src/components/Progress.tsx
1896
1662
  var import_jsx_runtime28 = require("react/jsx-runtime");
1897
- function Progress({ value = 0, max = 100, indeterminate, label, className, ...props }) {
1898
- const clamped = Math.max(0, Math.min(max || 100, value));
1899
- const pct = clamped / (max || 100) * 100;
1663
+ function Progress({ value = 0, max = 100, indeterminate, className, ...props }) {
1664
+ const pct = Math.max(0, Math.min(100, value / (max || 100) * 100));
1900
1665
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1901
1666
  "div",
1902
1667
  {
1903
1668
  role: "progressbar",
1904
1669
  "aria-valuemin": 0,
1905
1670
  "aria-valuemax": max,
1906
- "aria-valuenow": indeterminate ? void 0 : clamped,
1907
- "aria-label": label,
1671
+ "aria-valuenow": indeterminate ? void 0 : value,
1908
1672
  className: cx("uix-progress", indeterminate && "uix-progress--indeterminate", className),
1909
1673
  ...props,
1910
1674
  children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "uix-progress__bar", style: indeterminate ? void 0 : { width: `${pct}%` } })
@@ -1914,13 +1678,7 @@ function Progress({ value = 0, max = 100, indeterminate, label, className, ...pr
1914
1678
 
1915
1679
  // src/components/Meter.tsx
1916
1680
  var import_jsx_runtime29 = require("react/jsx-runtime");
1917
- var toneText = {
1918
- warning: "warning",
1919
- danger: "critical",
1920
- attention: "needs attention",
1921
- overdue: "overdue"
1922
- };
1923
- function Meter({ value = 0, tone, label, className, ...props }) {
1681
+ function Meter({ value = 0, tone, className, ...props }) {
1924
1682
  const pct = Math.max(0, Math.min(100, value));
1925
1683
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1926
1684
  "div",
@@ -1929,8 +1687,6 @@ function Meter({ value = 0, tone, label, className, ...props }) {
1929
1687
  "aria-valuenow": pct,
1930
1688
  "aria-valuemin": 0,
1931
1689
  "aria-valuemax": 100,
1932
- "aria-valuetext": tone && tone !== "success" ? `${pct}%, ${toneText[tone]}` : void 0,
1933
- "aria-label": label,
1934
1690
  className: cx("uix-meter", className),
1935
1691
  ...props,
1936
1692
  children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
@@ -1955,34 +1711,22 @@ function Stat({ label, value, icon, meta, trend, className, ...props }) {
1955
1711
  ] }),
1956
1712
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "uix-stat__value", children: value }),
1957
1713
  (meta != null || trend != null) && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "uix-stat__meta", children: [
1958
- trend != null && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: cx("uix-stat__trend", `uix-stat__trend--${trend.direction}`), children: [
1959
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: trend.direction === "up" ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "M12 19V5m-6 6 6-6 6 6" }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "M12 5v14m6-6-6 6-6-6" }) }),
1960
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "uix-visually-hidden", children: trend.direction === "up" ? "up" : "down" }),
1961
- trend.label
1962
- ] }),
1714
+ trend != null && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: cx("uix-stat__trend", `uix-stat__trend--${trend.direction}`), children: trend.label }),
1963
1715
  meta
1964
1716
  ] })
1965
1717
  ] });
1966
1718
  }
1967
1719
 
1968
1720
  // src/components/Tooltip.tsx
1969
- var import_react21 = require("react");
1721
+ var import_react19 = require("react");
1970
1722
  var import_jsx_runtime31 = require("react/jsx-runtime");
1971
1723
  function Tooltip({ label, placement = "top", className, children, ...props }) {
1972
- const triggerRef = (0, import_react21.useRef)(null);
1973
- const bubbleRef = (0, import_react21.useRef)(null);
1974
- const closeTimer = (0, import_react21.useRef)(null);
1975
- const [open, setOpen] = (0, import_react21.useState)(false);
1976
- const id = (0, import_react21.useId)();
1724
+ const triggerRef = (0, import_react19.useRef)(null);
1725
+ const bubbleRef = (0, import_react19.useRef)(null);
1726
+ const [open, setOpen] = (0, import_react19.useState)(false);
1727
+ const id = (0, import_react19.useId)();
1977
1728
  useAnchoredPosition(triggerRef, bubbleRef, { open, placement, offset: 6 });
1978
- const cancelClose = () => {
1979
- if (closeTimer.current != null) {
1980
- clearTimeout(closeTimer.current);
1981
- closeTimer.current = null;
1982
- }
1983
- };
1984
1729
  const show = () => {
1985
- cancelClose();
1986
1730
  const b = bubbleRef.current;
1987
1731
  if (b && !b.matches(":popover-open")) {
1988
1732
  try {
@@ -1993,7 +1737,6 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
1993
1737
  setOpen(true);
1994
1738
  };
1995
1739
  const hide = () => {
1996
- cancelClose();
1997
1740
  const b = bubbleRef.current;
1998
1741
  if (b && b.matches(":popover-open")) {
1999
1742
  try {
@@ -2003,20 +1746,7 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
2003
1746
  }
2004
1747
  setOpen(false);
2005
1748
  };
2006
- const scheduleHide = () => {
2007
- cancelClose();
2008
- closeTimer.current = window.setTimeout(hide, 100);
2009
- };
2010
- (0, import_react21.useEffect)(() => {
2011
- if (!open) return;
2012
- const onKey = (e) => {
2013
- if (e.key === "Escape") hide();
2014
- };
2015
- document.addEventListener("keydown", onKey);
2016
- return () => document.removeEventListener("keydown", onKey);
2017
- }, [open]);
2018
- (0, import_react21.useEffect)(() => () => {
2019
- if (closeTimer.current != null) clearTimeout(closeTimer.current);
1749
+ (0, import_react19.useEffect)(() => () => {
2020
1750
  const b = bubbleRef.current;
2021
1751
  if (b && b.matches(":popover-open")) {
2022
1752
  try {
@@ -2026,19 +1756,14 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
2026
1756
  }
2027
1757
  }, []);
2028
1758
  const bubbleAttr = { popover: "manual" };
2029
- const single = (0, import_react21.isValidElement)(children);
2030
- const child = single ? (0, import_react21.cloneElement)(children, {
2031
- "aria-describedby": [children.props["aria-describedby"], id].filter(Boolean).join(" ")
2032
- }) : children;
2033
1759
  return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
2034
1760
  "span",
2035
1761
  {
2036
1762
  ref: triggerRef,
2037
1763
  className: cx("uix-tooltip", className),
2038
- "aria-describedby": single ? void 0 : id,
2039
- tabIndex: single ? void 0 : 0,
1764
+ "aria-describedby": id,
2040
1765
  onMouseEnter: show,
2041
- onMouseLeave: scheduleHide,
1766
+ onMouseLeave: hide,
2042
1767
  onFocus: show,
2043
1768
  onBlur: hide,
2044
1769
  onKeyDown: (e) => {
@@ -2046,20 +1771,8 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
2046
1771
  },
2047
1772
  ...props,
2048
1773
  children: [
2049
- child,
2050
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
2051
- "span",
2052
- {
2053
- ref: bubbleRef,
2054
- id,
2055
- role: "tooltip",
2056
- className: "uix-tooltip__bubble",
2057
- onMouseEnter: cancelClose,
2058
- onMouseLeave: scheduleHide,
2059
- ...bubbleAttr,
2060
- children: label
2061
- }
2062
- )
1774
+ children,
1775
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { ref: bubbleRef, id, role: "tooltip", className: "uix-tooltip__bubble", ...bubbleAttr, children: label })
2063
1776
  ]
2064
1777
  }
2065
1778
  );
@@ -2099,14 +1812,8 @@ function Skeleton({ variant = "block", width, height, className, style, ...props
2099
1812
  }
2100
1813
  );
2101
1814
  }
2102
- function LoadingState({ rows = 3, label, className, ...props }) {
2103
- return (
2104
- // no aria-busy: busy=true tells AT the region isn't ready and suppresses the announcement (UIX-A11Y-4)
2105
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: cx("uix-stack", className), role: "status", ...props, children: [
2106
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "uix-visually-hidden", children: label ?? "Loading" }),
2107
- Array.from({ length: rows }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Skeleton, { variant: "text", width: `${92 - i % 3 * 14}%` }, i))
2108
- ] })
2109
- );
1815
+ function LoadingState({ rows = 3, className, ...props }) {
1816
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: cx("uix-stack", className), role: "status", "aria-busy": "true", ...props, children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Skeleton, { variant: "text", width: `${92 - i % 3 * 14}%` }, i)) });
2110
1817
  }
2111
1818
 
2112
1819
  // src/components/Label.tsx
@@ -2124,8 +1831,7 @@ var import_jsx_runtime34 = require("react/jsx-runtime");
2124
1831
  function Avatar({ size = "md", src, alt, children, status, className, ...props }) {
2125
1832
  return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: cx("uix-avatar", size !== "md" && `uix-avatar--${size}`, className), ...props, children: [
2126
1833
  src ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("img", { src, alt: alt ?? "" }) : children,
2127
- status && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "uix-avatar__status", "aria-hidden": "true" }),
2128
- status && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "uix-visually-hidden", children: "online" })
1834
+ status && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "uix-avatar__status" })
2129
1835
  ] });
2130
1836
  }
2131
1837
  function AvatarGroup({ children, className, ...props }) {
@@ -2145,7 +1851,7 @@ function UserChip({ avatar, name, sub, className, children, ...props }) {
2145
1851
  // src/components/Comments.tsx
2146
1852
  var import_jsx_runtime35 = require("react/jsx-runtime");
2147
1853
  function Comments({ children, className, ...props }) {
2148
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { role: "log", className: cx("uix-comments", className), ...props, children });
1854
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: cx("uix-comments", className), ...props, children });
2149
1855
  }
2150
1856
  function Comment({ avatar, author, meta, children, className, ...props }) {
2151
1857
  return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: cx("uix-comment", className), ...props, children: [
@@ -2162,25 +1868,21 @@ function Comment({ avatar, author, meta, children, className, ...props }) {
2162
1868
  }
2163
1869
 
2164
1870
  // src/components/Composer.tsx
2165
- var import_react22 = require("react");
2166
1871
  var import_jsx_runtime36 = require("react/jsx-runtime");
2167
- function Composer({ children, className, label = "Add a comment", ...props }) {
2168
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: cx("uix-composer", className), ...props, children: import_react22.Children.map(children, (child) => {
2169
- if (!(0, import_react22.isValidElement)(child) || child.type !== "textarea") return child;
2170
- const p = child.props;
2171
- return p["aria-label"] || p["aria-labelledby"] || p.id ? child : (0, import_react22.cloneElement)(child, { "aria-label": label });
2172
- }) });
1872
+ function Composer({ children, className, ...props }) {
1873
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: cx("uix-composer", className), ...props, children });
2173
1874
  }
2174
1875
  function ComposerBar({ children, className, ...props }) {
2175
1876
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: cx("uix-composer__bar", className), ...props, children });
2176
1877
  }
2177
1878
 
2178
1879
  // src/components/Segmented.tsx
2179
- var import_react23 = require("react");
1880
+ var import_react20 = require("react");
2180
1881
  var import_jsx_runtime37 = require("react/jsx-runtime");
2181
- var SegmentedCtx = (0, import_react23.createContext)({});
1882
+ var SegmentedCtx = (0, import_react20.createContext)({});
2182
1883
  function Segmented({ value, onChange, children, className, ...props }) {
2183
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(SegmentedCtx.Provider, { value: { value, onChange }, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { role: "group", className: cx("uix-segmented", className), ...props, children }) });
1884
+ const contextValue = (0, import_react20.useMemo)(() => ({ value, onChange }), [value, onChange]);
1885
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(SegmentedCtx.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { role: "group", className: cx("uix-segmented", className), ...props, children }) });
2184
1886
  }
2185
1887
  function SegmentedOption({
2186
1888
  value,
@@ -2189,7 +1891,7 @@ function SegmentedOption({
2189
1891
  onClick,
2190
1892
  ...props
2191
1893
  }) {
2192
- const ctx = (0, import_react23.useContext)(SegmentedCtx);
1894
+ const ctx = (0, import_react20.useContext)(SegmentedCtx);
2193
1895
  const selected = ctx.value === value;
2194
1896
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2195
1897
  "button",
@@ -2210,11 +1912,11 @@ function SegmentedOption({
2210
1912
  // src/components/Timeline.tsx
2211
1913
  var import_jsx_runtime38 = require("react/jsx-runtime");
2212
1914
  function Timeline({ children, className, ...props }) {
2213
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ol", { className: cx("uix-timeline", className), ...props, children });
1915
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: cx("uix-timeline", className), ...props, children });
2214
1916
  }
2215
1917
  function TimelineItem({ node, meta, children, className, ...props }) {
2216
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("li", { className: cx("uix-timeline__item", className), ...props, children: [
2217
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "uix-timeline__rail", "aria-hidden": "true", children: [
1918
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: cx("uix-timeline__item", className), ...props, children: [
1919
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "uix-timeline__rail", children: [
2218
1920
  node ?? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "uix-timeline__node" }),
2219
1921
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "uix-timeline__line" })
2220
1922
  ] }),
@@ -2238,7 +1940,7 @@ function Note({ tone, icon, children, className, ...props }) {
2238
1940
  }
2239
1941
 
2240
1942
  // src/components/Popover.tsx
2241
- var import_react24 = require("react");
1943
+ var import_react21 = require("react");
2242
1944
  var import_jsx_runtime40 = require("react/jsx-runtime");
2243
1945
  function Popover({
2244
1946
  popover = "auto",
@@ -2249,15 +1951,15 @@ function Popover({
2249
1951
  children,
2250
1952
  ...props
2251
1953
  }) {
2252
- const ref = (0, import_react24.useRef)(null);
2253
- const [open, setOpen] = (0, import_react24.useState)(false);
1954
+ const ref = (0, import_react21.useRef)(null);
1955
+ const [open, setOpen] = (0, import_react21.useState)(false);
2254
1956
  const popoverAttr = { popover };
2255
1957
  const reposition = useAnchoredPosition(anchor ?? null, ref, {
2256
1958
  open: open && !!anchor,
2257
1959
  placement,
2258
1960
  offset
2259
1961
  });
2260
- (0, import_react24.useEffect)(() => {
1962
+ (0, import_react21.useEffect)(() => {
2261
1963
  const el = ref.current;
2262
1964
  if (!el || !anchor) return;
2263
1965
  const sync = () => {
@@ -2272,138 +1974,34 @@ function Popover({
2272
1974
  }
2273
1975
 
2274
1976
  // src/components/CommandPalette.tsx
2275
- var import_react25 = require("react");
2276
1977
  var import_jsx_runtime41 = require("react/jsx-runtime");
2277
- var CmdkContext = (0, import_react25.createContext)(null);
2278
1978
  function CommandPalette({ inputProps, children, className, ...props }) {
2279
- const listId = (0, import_react25.useId)();
2280
- const [activeId, setActiveId] = (0, import_react25.useState)(null);
2281
- const [count, setCount] = (0, import_react25.useState)(0);
2282
- const [announced, setAnnounced] = (0, import_react25.useState)("");
2283
- const setActive = (0, import_react25.useCallback)((id, active) => {
2284
- setActiveId((cur) => active ? id : cur === id ? null : cur);
2285
- }, []);
2286
- const registerItem = (0, import_react25.useCallback)(() => {
2287
- setCount((c) => c + 1);
2288
- return () => setCount((c) => c - 1);
2289
- }, []);
2290
- (0, import_react25.useEffect)(() => {
2291
- const t = window.setTimeout(() => setAnnounced(`${count} result${count === 1 ? "" : "s"}`), 150);
2292
- return () => clearTimeout(t);
2293
- }, [count]);
2294
- const ctx = (0, import_react25.useMemo)(() => ({ listId, setActive, registerItem }), [listId, setActive, registerItem]);
2295
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CmdkContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: cx("uix-cmdk", className), ...props, children: [
2296
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2297
- "input",
2298
- {
2299
- className: "uix-cmdk__input",
2300
- role: "combobox",
2301
- "aria-expanded": "true",
2302
- "aria-autocomplete": "list",
2303
- "aria-controls": listId,
2304
- "aria-activedescendant": activeId ?? void 0,
2305
- "aria-label": "Command palette",
2306
- ...inputProps
2307
- }
2308
- ),
2309
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uix-cmdk__list", role: "listbox", id: listId, children }),
2310
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "uix-visually-hidden", role: "status", children: announced })
2311
- ] }) });
1979
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: cx("uix-cmdk", className), ...props, children: [
1980
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("input", { className: "uix-cmdk__input", ...inputProps }),
1981
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uix-cmdk__list", children })
1982
+ ] });
2312
1983
  }
2313
1984
  function CommandGroup({ label, children }) {
2314
- const labelId = (0, import_react25.useId)();
2315
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { role: "group", "aria-labelledby": label != null ? labelId : void 0, children: [
2316
- label != null && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uix-cmdk__group", id: labelId, children: label }),
1985
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
1986
+ label != null && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uix-cmdk__group", children: label }),
2317
1987
  children
2318
1988
  ] });
2319
1989
  }
2320
- function CommandItem({ icon, shortcut, active, children, className, id, ...props }) {
2321
- const ctx = (0, import_react25.useContext)(CmdkContext);
2322
- const autoId = (0, import_react25.useId)();
2323
- const itemId = id ?? autoId;
2324
- (0, import_react25.useEffect)(() => ctx?.registerItem(), [ctx]);
2325
- (0, import_react25.useEffect)(() => {
2326
- if (!ctx || !active) return;
2327
- ctx.setActive(itemId, true);
2328
- return () => ctx.setActive(itemId, false);
2329
- }, [ctx, itemId, active]);
2330
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
2331
- "div",
2332
- {
2333
- className: cx("uix-cmdk__item", className),
2334
- "data-active": active || void 0,
2335
- id: itemId,
2336
- role: "option",
2337
- "aria-selected": !!active,
2338
- ...props,
2339
- children: [
2340
- icon,
2341
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children }),
2342
- shortcut
2343
- ]
2344
- }
2345
- );
1990
+ function CommandItem({ icon, shortcut, active, children, className, ...props }) {
1991
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: cx("uix-cmdk__item", className), "data-active": active || void 0, ...props, children: [
1992
+ icon,
1993
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children }),
1994
+ shortcut
1995
+ ] });
2346
1996
  }
2347
1997
 
2348
1998
  // src/components/Inbox.tsx
2349
- var import_react26 = require("react");
2350
1999
  var import_jsx_runtime42 = require("react/jsx-runtime");
2351
- function Inbox({ view, children, className, ...props }) {
2352
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: cx("uix-inbox", className), "data-view": view, ...props, children });
2000
+ function Inbox({ children, className, ...props }) {
2001
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: cx("uix-inbox", className), ...props, children });
2353
2002
  }
2354
- var InboxListCtx = (0, import_react26.createContext)({});
2355
2003
  function InboxList({ children, className, ...props }) {
2356
- const listRef = (0, import_react26.useRef)(null);
2357
- const [activeId, setActiveId] = (0, import_react26.useState)(void 0);
2358
- const ctx = (0, import_react26.useMemo)(() => ({ activeId }), [activeId]);
2359
- const onKeyDown = (e) => {
2360
- const opts = Array.from(listRef.current?.querySelectorAll('[role="option"]') ?? []);
2361
- if (opts.length === 0) return;
2362
- const idx = opts.findIndex((el2) => el2.id === activeId);
2363
- let next;
2364
- switch (e.key) {
2365
- case "ArrowDown":
2366
- next = Math.min(idx + 1, opts.length - 1);
2367
- break;
2368
- case "ArrowUp":
2369
- next = idx < 0 ? 0 : Math.max(idx - 1, 0);
2370
- break;
2371
- case "Home":
2372
- next = 0;
2373
- break;
2374
- case "End":
2375
- next = opts.length - 1;
2376
- break;
2377
- case "Enter":
2378
- case " ":
2379
- if (idx >= 0) {
2380
- e.preventDefault();
2381
- opts[idx]?.click();
2382
- }
2383
- return;
2384
- default:
2385
- return;
2386
- }
2387
- e.preventDefault();
2388
- const el = opts[next];
2389
- if (!el || !el.id) return;
2390
- setActiveId(el.id);
2391
- el.scrollIntoView({ block: "nearest" });
2392
- };
2393
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(InboxListCtx.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2394
- "div",
2395
- {
2396
- ref: listRef,
2397
- className: cx("uix-inbox__list", className),
2398
- role: "listbox",
2399
- tabIndex: 0,
2400
- "aria-label": "Inbox",
2401
- "aria-activedescendant": activeId,
2402
- onKeyDown,
2403
- ...props,
2404
- children
2405
- }
2406
- ) });
2004
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: cx("uix-inbox__list", className), role: "listbox", ...props, children });
2407
2005
  }
2408
2006
  function InboxItem({
2409
2007
  subject,
@@ -2413,19 +2011,13 @@ function InboxItem({
2413
2011
  selected,
2414
2012
  children,
2415
2013
  className,
2416
- id,
2417
2014
  ...props
2418
2015
  }) {
2419
- const autoId = (0, import_react26.useId)();
2420
- const itemId = id ?? autoId;
2421
- const { activeId } = (0, import_react26.useContext)(InboxListCtx);
2422
2016
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2423
2017
  "div",
2424
2018
  {
2425
2019
  role: "option",
2426
- id: itemId,
2427
2020
  "aria-selected": selected ?? false,
2428
- "data-active": activeId === itemId ? "" : void 0,
2429
2021
  className: cx("uix-inbox__item", className),
2430
2022
  ...unread ? { "data-unread": "" } : {},
2431
2023
  ...props,
@@ -2443,60 +2035,29 @@ function InboxDetail({ children, className, ...props }) {
2443
2035
  }
2444
2036
 
2445
2037
  // src/components/Kanban.tsx
2446
- var import_react27 = require("react");
2447
2038
  var import_jsx_runtime43 = require("react/jsx-runtime");
2448
- function Kanban({ announcement, children, className, ...props }) {
2449
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: cx("uix-kanban", className), ...props, children: [
2450
- announcement != null && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
2451
- children
2452
- ] });
2039
+ function Kanban({ children, className, ...props }) {
2040
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: cx("uix-kanban", className), ...props, children });
2453
2041
  }
2454
2042
  function KanbanColumn({ title, count, children, className, ...props }) {
2455
- const headId = (0, import_react27.useId)();
2456
- const hasHead = title != null || count != null;
2457
2043
  return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: cx("uix-kanban__col", className), ...props, children: [
2458
- hasHead && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "uix-kanban__head", id: headId, children: [
2044
+ (title != null || count != null) && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "uix-kanban__head", children: [
2459
2045
  title,
2460
2046
  count != null && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uix-kanban__count", children: count })
2461
2047
  ] }),
2462
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__body", role: "list", "aria-labelledby": hasHead ? headId : void 0, children })
2048
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__body", children })
2463
2049
  ] });
2464
2050
  }
2465
- var MOVE_KEYS = {
2466
- ArrowUp: "up",
2467
- ArrowDown: "down",
2468
- ArrowLeft: "left",
2469
- ArrowRight: "right"
2470
- };
2471
- function KanbanCard({ title, meta, children, className, onMove, onKeyDown, ...props }) {
2472
- const handleKeyDown = (e) => {
2473
- onKeyDown?.(e);
2474
- if (!onMove || !e.altKey || e.defaultPrevented) return;
2475
- const dir = MOVE_KEYS[e.key];
2476
- if (dir) {
2477
- e.preventDefault();
2478
- onMove(dir);
2479
- }
2480
- };
2481
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2482
- "div",
2483
- {
2484
- role: "listitem",
2485
- tabIndex: 0,
2486
- className: cx("uix-kanban__card", className),
2487
- onKeyDown: handleKeyDown,
2488
- ...props,
2489
- children: [
2490
- title && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__card-title", children: title }),
2491
- meta && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__card-meta", children: meta }),
2492
- children
2493
- ]
2494
- }
2495
- );
2051
+ function KanbanCard({ title, meta, children, className, ...props }) {
2052
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: cx("uix-kanban__card", className), ...props, children: [
2053
+ title && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__card-title", children: title }),
2054
+ meta && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__card-meta", children: meta }),
2055
+ children
2056
+ ] });
2496
2057
  }
2497
2058
 
2498
2059
  // src/components/Tree.tsx
2499
- var import_react28 = require("react");
2060
+ var import_react22 = require("react");
2500
2061
 
2501
2062
  // src/tree-model.ts
2502
2063
  function flattenTree(nodes, expanded, level = 1) {
@@ -2541,14 +2102,6 @@ function treeNav(flat, currentId, key) {
2541
2102
  case " ":
2542
2103
  return cur.hasChildren ? { toggleId: cur.node.id, selectId: cur.node.id } : { selectId: cur.node.id };
2543
2104
  default:
2544
- if (key.length === 1) {
2545
- const q = key.toLowerCase();
2546
- for (let step = 1; step <= flat.length; step++) {
2547
- const f = flat[(idx + step) % flat.length];
2548
- const label = f.node.typeaheadLabel ?? (typeof f.node.label === "string" ? f.node.label : void 0);
2549
- if (label != null && label.toLowerCase().startsWith(q)) return { focusId: f.node.id };
2550
- }
2551
- }
2552
2105
  return {};
2553
2106
  }
2554
2107
  }
@@ -2616,12 +2169,12 @@ function Tree({
2616
2169
  className,
2617
2170
  ...props
2618
2171
  }) {
2619
- const [internalExpanded, setInternalExpanded] = (0, import_react28.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
2172
+ const [internalExpanded, setInternalExpanded] = (0, import_react22.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
2620
2173
  const expanded = controlledExpanded ?? internalExpanded;
2621
- const rootRef = (0, import_react28.useRef)(null);
2622
- const [focusedId, setFocusedId] = (0, import_react28.useState)(void 0);
2174
+ const rootRef = (0, import_react22.useRef)(null);
2175
+ const [focusedId, setFocusedId] = (0, import_react22.useState)(void 0);
2623
2176
  const selectable = selected !== void 0 || onSelect != null;
2624
- const handleToggle = (0, import_react28.useCallback)((id) => {
2177
+ const handleToggle = (0, import_react22.useCallback)((id) => {
2625
2178
  if (!controlledExpanded) {
2626
2179
  setInternalExpanded((prev) => {
2627
2180
  const next = new Set(prev);
@@ -2632,7 +2185,7 @@ function Tree({
2632
2185
  }
2633
2186
  onToggle?.(id);
2634
2187
  }, [controlledExpanded, onToggle]);
2635
- const flat = (0, import_react28.useMemo)(() => flattenTree(nodes, expanded), [nodes, expanded]);
2188
+ const flat = (0, import_react22.useMemo)(() => flattenTree(nodes, expanded), [nodes, expanded]);
2636
2189
  const useVirtual = virtualize ?? shouldVirtualize(flat.length);
2637
2190
  const tabbableId = focusedId ?? selected ?? nodes[0]?.id;
2638
2191
  if (useVirtual) {
@@ -2703,13 +2256,6 @@ function Tree({
2703
2256
  setFocusedId(id);
2704
2257
  break;
2705
2258
  default:
2706
- if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
2707
- const act = treeNav(flat, id, e.key);
2708
- if (act.focusId !== void 0) {
2709
- e.preventDefault();
2710
- focusItem(rootRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(act.focusId)}"]`));
2711
- }
2712
- }
2713
2259
  break;
2714
2260
  }
2715
2261
  };
@@ -2743,14 +2289,12 @@ function VirtualTreeView({
2743
2289
  className,
2744
2290
  ...props
2745
2291
  }) {
2746
- const scrollRef = (0, import_react28.useRef)(null);
2747
- const [scrollTop, setScrollTop] = (0, import_react28.useState)(0);
2748
- const pendingFocus = (0, import_react28.useRef)(false);
2749
- const hadFocus = (0, import_react28.useRef)(false);
2292
+ const scrollRef = (0, import_react22.useRef)(null);
2293
+ const [scrollTop, setScrollTop] = (0, import_react22.useState)(0);
2294
+ const pendingFocus = (0, import_react22.useRef)(false);
2750
2295
  const win = virtualWindow(scrollTop, maxHeight, rowHeight, flat.length);
2751
2296
  const rows = flat.slice(win.start, win.end);
2752
- const windowHasTabbable = tabbableId != null && rows.some((f) => f.node.id === tabbableId);
2753
- (0, import_react28.useEffect)(() => {
2297
+ (0, import_react22.useEffect)(() => {
2754
2298
  if (!pendingFocus.current || focusedId == null) return;
2755
2299
  const el = scrollRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(focusedId)}"]`);
2756
2300
  if (el) {
@@ -2758,11 +2302,6 @@ function VirtualTreeView({
2758
2302
  pendingFocus.current = false;
2759
2303
  }
2760
2304
  }, [focusedId, win.start, win.end]);
2761
- (0, import_react28.useEffect)(() => {
2762
- if (!hadFocus.current || pendingFocus.current) return;
2763
- const active = document.activeElement;
2764
- if (active === document.body || active == null) scrollRef.current?.focus({ preventScroll: true });
2765
- });
2766
2305
  const moveTo = (id) => {
2767
2306
  const i = flat.findIndex((f) => f.node.id === id);
2768
2307
  if (i < 0) return;
@@ -2778,7 +2317,6 @@ function VirtualTreeView({
2778
2317
  setFocusedId(id);
2779
2318
  };
2780
2319
  const onKeyDown = (e) => {
2781
- if (e.key.length === 1 && (e.ctrlKey || e.metaKey || e.altKey)) return;
2782
2320
  const curId = e.target.closest('[role="treeitem"]')?.dataset.id ?? focusedId;
2783
2321
  const act = treeNav(flat, curId, e.key);
2784
2322
  if (act.focusId === void 0 && act.toggleId === void 0 && act.selectId === void 0) return;
@@ -2799,20 +2337,13 @@ function VirtualTreeView({
2799
2337
  ref: scrollRef,
2800
2338
  className: cx("uix-tree", "uix-tree--virtual", className),
2801
2339
  role: "tree",
2802
- tabIndex: -1,
2803
2340
  style: { maxHeight, overflow: "auto" },
2804
2341
  onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
2805
2342
  onKeyDown,
2806
- onFocus: () => {
2807
- hadFocus.current = true;
2808
- },
2809
- onBlur: (e) => {
2810
- if (!e.currentTarget.contains(e.relatedTarget)) hadFocus.current = false;
2811
- },
2812
2343
  ...props,
2813
2344
  children: [
2814
- win.padTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { role: "presentation", "aria-hidden": "true", style: { height: win.padTop } }),
2815
- rows.map((f, i) => {
2345
+ win.padTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { "aria-hidden": "true", style: { height: win.padTop } }),
2346
+ rows.map((f) => {
2816
2347
  const rowStyle = { height: rowHeight, paddingLeft: `calc(${f.level - 1} * var(--uix-space-5))` };
2817
2348
  return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2818
2349
  "li",
@@ -2824,7 +2355,7 @@ function VirtualTreeView({
2824
2355
  "aria-expanded": f.hasChildren ? f.isExpanded : void 0,
2825
2356
  "aria-selected": selectable ? selected === f.node.id : void 0,
2826
2357
  "data-id": f.node.id,
2827
- tabIndex: tabbableId === f.node.id || !windowHasTabbable && i === 0 ? 0 : -1,
2358
+ tabIndex: tabbableId === f.node.id ? 0 : -1,
2828
2359
  className: "uix-tree__item",
2829
2360
  onFocus: (e) => {
2830
2361
  if (e.target === e.currentTarget) setFocusedId(f.node.id);
@@ -2844,7 +2375,7 @@ function VirtualTreeView({
2844
2375
  f.node.id
2845
2376
  );
2846
2377
  }),
2847
- win.padBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { role: "presentation", "aria-hidden": "true", style: { height: win.padBottom } })
2378
+ win.padBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { "aria-hidden": "true", style: { height: win.padBottom } })
2848
2379
  ]
2849
2380
  }
2850
2381
  );
@@ -3103,7 +2634,6 @@ function StatusRow({ indicator, name, meta, className, ...props }) {
3103
2634
  SubNavItem,
3104
2635
  Switch,
3105
2636
  Tab,
3106
- TabPanel,
3107
2637
  Table,
3108
2638
  TableWrap,
3109
2639
  Tabs,
@@ -3141,5 +2671,6 @@ function StatusRow({ indicator, name, meta, className, ...props }) {
3141
2671
  useAnchoredPosition,
3142
2672
  useDialog,
3143
2673
  useTable,
2674
+ useVirtualRows,
3144
2675
  virtualWindow
3145
2676
  });