@tensor_1/react 2.6.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -38,13 +38,19 @@ __export(src_exports, {
38
38
  Comments: () => Comments,
39
39
  Composer: () => Composer,
40
40
  ComposerBar: () => ComposerBar,
41
+ ContentList: () => ContentList,
42
+ ContentListItem: () => ContentListItem,
41
43
  DescriptionItem: () => DescriptionItem,
42
44
  DescriptionList: () => DescriptionList,
43
45
  DetailLayout: () => DetailLayout,
44
46
  Drawer: () => Drawer,
45
47
  EmptyState: () => EmptyState,
46
48
  ErrorState: () => ErrorState,
49
+ EventRow: () => EventRow,
47
50
  ExpandToggle: () => ExpandToggle,
51
+ FeaturedRundown: () => FeaturedRundown,
52
+ FeaturedRundownItem: () => FeaturedRundownItem,
53
+ FeaturedStage: () => FeaturedStage,
48
54
  Field: () => Field,
49
55
  Highlighted: () => Highlighted,
50
56
  Inbox: () => Inbox,
@@ -66,8 +72,11 @@ __export(src_exports, {
66
72
  NavFavourites: () => NavFavourites,
67
73
  NavGroup: () => NavGroup,
68
74
  NavItem: () => NavItem,
75
+ NewsLead: () => NewsLead,
69
76
  Note: () => Note,
77
+ NoticeQueue: () => NoticeQueue,
70
78
  PageHeader: () => PageHeader,
79
+ PageIntro: () => PageIntro,
71
80
  Pagination: () => Pagination,
72
81
  Peek: () => Peek,
73
82
  Popover: () => Popover,
@@ -75,8 +84,10 @@ __export(src_exports, {
75
84
  Prose: () => Prose,
76
85
  Radio: () => Radio,
77
86
  RadioGroup: () => RadioGroup,
87
+ ResourceGrid: () => ResourceGrid,
78
88
  RowAction: () => RowAction,
79
89
  RowActions: () => RowActions,
90
+ SectionHead: () => SectionHead,
80
91
  Segmented: () => Segmented,
81
92
  SegmentedOption: () => SegmentedOption,
82
93
  Select: () => Select,
@@ -86,7 +97,9 @@ __export(src_exports, {
86
97
  Spinner: () => Spinner,
87
98
  StarButton: () => StarButton,
88
99
  Stat: () => Stat,
100
+ StatLine: () => StatLine,
89
101
  StatusPill: () => StatusPill,
102
+ StatusRow: () => StatusRow,
90
103
  SubNavItem: () => SubNavItem,
91
104
  Switch: () => Switch,
92
105
  Tab: () => Tab,
@@ -106,7 +119,9 @@ __export(src_exports, {
106
119
  UserChip: () => UserChip,
107
120
  applyFilters: () => applyFilters,
108
121
  clampWidth: () => clampWidth,
122
+ computePosition: () => computePosition,
109
123
  cx: () => cx,
124
+ flattenTree: () => flattenTree,
110
125
  highlightSegments: () => highlightSegments,
111
126
  matchFilter: () => matchFilter,
112
127
  mergePinned: () => mergePinned,
@@ -121,6 +136,8 @@ __export(src_exports, {
121
136
  toggleId: () => toggleId,
122
137
  togglePage: () => togglePage,
123
138
  toggleSort: () => toggleSort,
139
+ treeNav: () => treeNav,
140
+ useAnchoredPosition: () => useAnchoredPosition,
124
141
  useDialog: () => useDialog,
125
142
  useTable: () => useTable,
126
143
  virtualWindow: () => virtualWindow
@@ -134,17 +151,48 @@ function cx(...args) {
134
151
 
135
152
  // src/hooks/useDialog.ts
136
153
  var import_react = require("react");
154
+ var lockCount = 0;
155
+ var saved = null;
156
+ function lockBodyScroll() {
157
+ if (typeof document === "undefined") return;
158
+ if (++lockCount > 1) return;
159
+ const el = document.scrollingElement ?? document.documentElement;
160
+ const scrollbarW = window.innerWidth - document.documentElement.clientWidth;
161
+ saved = {
162
+ el,
163
+ overflow: el.style.overflow,
164
+ paddingRight: el.style.paddingRight,
165
+ overscroll: el.style.overscrollBehavior
166
+ };
167
+ el.style.overflow = "hidden";
168
+ if (scrollbarW > 0) {
169
+ const current = parseFloat(getComputedStyle(el).paddingRight) || 0;
170
+ el.style.paddingRight = `${current + scrollbarW}px`;
171
+ }
172
+ el.style.overscrollBehavior = "contain";
173
+ }
174
+ function unlockBodyScroll() {
175
+ if (typeof document === "undefined" || lockCount === 0) return;
176
+ if (--lockCount > 0) return;
177
+ if (saved) {
178
+ saved.el.style.overflow = saved.overflow;
179
+ saved.el.style.paddingRight = saved.paddingRight;
180
+ saved.el.style.overscrollBehavior = saved.overscroll;
181
+ saved = null;
182
+ }
183
+ }
137
184
  function useDialog(open) {
138
185
  const ref = (0, import_react.useRef)(null);
139
186
  (0, import_react.useEffect)(() => {
140
187
  const el = ref.current;
141
- if (!el) return;
142
- if (open) {
143
- if (!el.open) el.showModal();
144
- } else {
145
- if (el.open) el.close();
188
+ if (!el || !open) {
189
+ if (el && el.open) el.close();
190
+ return;
146
191
  }
192
+ if (!el.open) el.showModal();
193
+ lockBodyScroll();
147
194
  return () => {
195
+ unlockBodyScroll();
148
196
  if (el.open) el.close();
149
197
  };
150
198
  }, [open]);
@@ -187,8 +235,36 @@ function toggleSort(keys, field, additive = false) {
187
235
  function asNum(v) {
188
236
  return typeof v === "number" ? v : Number(v);
189
237
  }
238
+ function asTime(v) {
239
+ if (v == null) return NaN;
240
+ if (v instanceof Date) return v.getTime();
241
+ if (typeof v === "number") return v;
242
+ const s = String(v).trim();
243
+ if (s === "") return NaN;
244
+ const parsed = Date.parse(s);
245
+ return Number.isNaN(parsed) ? Number(s) : parsed;
246
+ }
247
+ function matchDate(cell, f) {
248
+ const c = asTime(cell);
249
+ if (Number.isNaN(c)) return false;
250
+ switch (f.op) {
251
+ case "eq":
252
+ return c === asTime(f.value);
253
+ case "lt":
254
+ return c < asTime(f.value);
255
+ case "gt":
256
+ return c > asTime(f.value);
257
+ case "between": {
258
+ const [lo, hi] = f.value.map(asTime);
259
+ return c >= lo && c <= hi;
260
+ }
261
+ default:
262
+ return true;
263
+ }
264
+ }
190
265
  function matchFilter(row, f) {
191
266
  const cell = row[f.field];
267
+ if (f.kind === "date") return matchDate(cell, f);
192
268
  switch (f.op) {
193
269
  case "isAnyOf":
194
270
  return Array.isArray(f.value) && f.value.includes(cell);
@@ -247,13 +323,53 @@ function highlightSegments(text, query) {
247
323
  }
248
324
  return out;
249
325
  }
326
+ var ARRAY_OPS = /* @__PURE__ */ new Set(["isAnyOf", "isNoneOf", "between"]);
327
+ var escapePart = (s) => encodeURIComponent(s).replace(/~/g, "%7E");
328
+ var unescapePart = (s) => decodeURIComponent(s);
329
+ var scalarToStr = (v) => v == null ? "" : String(v);
330
+ function parseScalar(s, kind) {
331
+ switch (kind) {
332
+ case "number":
333
+ return Number(s);
334
+ case "boolean":
335
+ return s === "true";
336
+ case "date":
337
+ return /^-?\d+$/.test(s) ? Number(s) : s;
338
+ // epoch-ms number, else the ISO/date string
339
+ default:
340
+ return s;
341
+ }
342
+ }
343
+ function kindFromOp(op) {
344
+ if (op === "isAnyOf" || op === "isNoneOf") return "enum";
345
+ if (op === "is") return "boolean";
346
+ if (op === "contains" || op === "equals" || op === "startsWith") return "text";
347
+ return "number";
348
+ }
349
+ function serializeFilter(f) {
350
+ const value = Array.isArray(f.value) ? f.value.map((x) => escapePart(scalarToStr(x))).join("|") : escapePart(scalarToStr(f.value));
351
+ return `${escapePart(f.field)}~${f.kind}~${f.op}~${value}`;
352
+ }
353
+ function parseFilter(tok) {
354
+ const parts = tok.split("~");
355
+ let field, kind, op, raw;
356
+ if (parts.length >= 4) {
357
+ [field, kind, op, raw] = [unescapePart(parts[0]), parts[1], parts[2], parts[3] ?? ""];
358
+ } else {
359
+ [field, op, raw] = [unescapePart(parts[0]), parts[1], parts[2] ?? ""];
360
+ kind = kindFromOp(op);
361
+ }
362
+ if (!field || !op) return null;
363
+ const value = ARRAY_OPS.has(op) ? raw.split("|").filter((s) => s !== "").map((s) => parseScalar(unescapePart(s), kind)) : parseScalar(unescapePart(raw), kind);
364
+ return { field, kind, op, value };
365
+ }
250
366
  function serializeView(v) {
251
367
  const p = new URLSearchParams();
252
368
  if (v.sort?.length) p.set("sort", v.sort.map((s) => `${s.field}:${s.dir === "descending" ? "d" : "a"}`).join(","));
253
369
  if (v.columns?.length) p.set("cols", v.columns.join(","));
254
370
  if (v.density && v.density !== "standard") p.set("density", v.density);
255
371
  if (v.q) p.set("q", v.q);
256
- if (v.filters?.length) p.set("filter", v.filters.map((f) => `${f.field}~${f.op}~${Array.isArray(f.value) ? f.value.join("|") : f.value ?? ""}`).join(","));
372
+ if (v.filters?.length) p.set("filter", v.filters.map(serializeFilter).join(","));
257
373
  return p.toString();
258
374
  }
259
375
  function parseView(qs) {
@@ -271,11 +387,9 @@ function parseView(qs) {
271
387
  const q = p.get("q");
272
388
  if (q) v.q = q;
273
389
  const filter = p.get("filter");
274
- if (filter) v.filters = filter.split(",").filter(Boolean).map((tok) => {
275
- const [field, op, raw] = tok.split("~");
276
- const value = raw?.includes("|") ? raw.split("|") : raw;
277
- return { field, kind: "text", op, value };
278
- });
390
+ if (filter) {
391
+ v.filters = filter.split(",").filter(Boolean).map(parseFilter).filter((f) => f !== null);
392
+ }
279
393
  return v;
280
394
  }
281
395
  function virtualWindow(scrollTop, viewportH, rowH, count, overscan = 6) {
@@ -376,10 +490,114 @@ function useTable(data, options = {}) {
376
490
  };
377
491
  }
378
492
 
379
- // src/components/Button.tsx
493
+ // src/hooks/useAnchoredPosition.ts
380
494
  var import_react3 = require("react");
495
+
496
+ // src/overlay-position.ts
497
+ var OPPOSITE = { top: "bottom", bottom: "top", left: "right", right: "left" };
498
+ function parsePlacement(p) {
499
+ const [side, align] = p.split("-");
500
+ return { side, align: align ?? "center" };
501
+ }
502
+ var clamp = (v, min, max) => Math.max(min, Math.min(max, v));
503
+ function spaceOn(side, a, vp, offset) {
504
+ switch (side) {
505
+ case "top":
506
+ return a.y - offset;
507
+ case "bottom":
508
+ return vp.height - (a.y + a.height) - offset;
509
+ case "left":
510
+ return a.x - offset;
511
+ case "right":
512
+ return vp.width - (a.x + a.width) - offset;
513
+ }
514
+ }
515
+ function fitsOn(side, a, f, vp, offset, padding) {
516
+ const need = side === "top" || side === "bottom" ? f.height : f.width;
517
+ return spaceOn(side, a, vp, offset) >= need + padding;
518
+ }
519
+ function computePosition(anchor, floating, viewport, options = {}) {
520
+ const { placement = "bottom-start", offset = 6, padding = 8, flip = true, shift = true } = options;
521
+ const { side: preferred, align } = parsePlacement(placement);
522
+ let side = preferred;
523
+ if (flip && !fitsOn(side, anchor, floating, viewport, offset, padding)) {
524
+ const opp = OPPOSITE[side];
525
+ if (fitsOn(opp, anchor, floating, viewport, offset, padding) || spaceOn(opp, anchor, viewport, offset) > spaceOn(side, anchor, viewport, offset)) {
526
+ side = opp;
527
+ }
528
+ }
529
+ let x, y;
530
+ const horizontal = side === "top" || side === "bottom";
531
+ if (side === "top") y = anchor.y - floating.height - offset;
532
+ else if (side === "bottom") y = anchor.y + anchor.height + offset;
533
+ else if (side === "left") x = anchor.x - floating.width - offset;
534
+ else x = anchor.x + anchor.width + offset;
535
+ if (horizontal) {
536
+ const start = anchor.x;
537
+ const end = anchor.x + anchor.width - floating.width;
538
+ const center = anchor.x + (anchor.width - floating.width) / 2;
539
+ x = align === "start" ? start : align === "end" ? end : center;
540
+ if (shift) x = clamp(x, padding, Math.max(padding, viewport.width - floating.width - padding));
541
+ } else {
542
+ const start = anchor.y;
543
+ const end = anchor.y + anchor.height - floating.height;
544
+ const center = anchor.y + (anchor.height - floating.height) / 2;
545
+ y = align === "start" ? start : align === "end" ? end : center;
546
+ if (shift) y = clamp(y, padding, Math.max(padding, viewport.height - floating.height - padding));
547
+ }
548
+ return {
549
+ x,
550
+ y,
551
+ placement: align === "center" ? side : `${side}-${align}`,
552
+ side,
553
+ align
554
+ };
555
+ }
556
+
557
+ // src/hooks/useAnchoredPosition.ts
558
+ var useIsomorphicLayoutEffect = typeof document !== "undefined" ? import_react3.useLayoutEffect : import_react3.useEffect;
559
+ var resolve = (a) => a && typeof a === "object" && "current" in a ? a.current : a ?? null;
560
+ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-start", offset = 6, padding = 8 }) {
561
+ const opts = (0, import_react3.useRef)({ placement, offset, padding });
562
+ opts.current = { placement, offset, padding };
563
+ const reposition = (0, import_react3.useCallback)(() => {
564
+ const anchorEl = resolve(anchor);
565
+ const floating = floatingRef.current;
566
+ if (!anchorEl || !floating) return;
567
+ const s = floating.style;
568
+ s.position = "fixed";
569
+ s.inset = "auto";
570
+ s.margin = "0";
571
+ const a = anchorEl.getBoundingClientRect();
572
+ const { placement: p, offset: o, padding: pad } = opts.current;
573
+ const pos = computePosition(
574
+ { x: a.x, y: a.y, width: a.width, height: a.height },
575
+ { width: floating.offsetWidth, height: floating.offsetHeight },
576
+ { width: window.innerWidth, height: window.innerHeight },
577
+ { placement: p, offset: o, padding: pad }
578
+ );
579
+ s.left = `${Math.round(pos.x)}px`;
580
+ s.top = `${Math.round(pos.y)}px`;
581
+ floating.dataset.placement = pos.placement;
582
+ }, [anchor, floatingRef]);
583
+ useIsomorphicLayoutEffect(() => {
584
+ if (!open) return;
585
+ reposition();
586
+ const onScroll = () => reposition();
587
+ window.addEventListener("scroll", onScroll, { passive: true, capture: true });
588
+ window.addEventListener("resize", onScroll);
589
+ return () => {
590
+ window.removeEventListener("scroll", onScroll, true);
591
+ window.removeEventListener("resize", onScroll);
592
+ };
593
+ }, [open, reposition]);
594
+ return reposition;
595
+ }
596
+
597
+ // src/components/Button.tsx
598
+ var import_react4 = require("react");
381
599
  var import_jsx_runtime = require("react/jsx-runtime");
382
- var Button = (0, import_react3.forwardRef)(
600
+ var Button = (0, import_react4.forwardRef)(
383
601
  ({ variant = "secondary", size = "md", icon = false, loading = false, className, children, disabled, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
384
602
  "button",
385
603
  {
@@ -404,9 +622,9 @@ function ButtonGroup({ children, className }) {
404
622
  }
405
623
 
406
624
  // src/components/Input.tsx
407
- var import_react4 = require("react");
625
+ var import_react5 = require("react");
408
626
  var import_jsx_runtime2 = require("react/jsx-runtime");
409
- var Input = (0, import_react4.forwardRef)(
627
+ var Input = (0, import_react5.forwardRef)(
410
628
  ({ size = "md", invalid, valid, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
411
629
  "input",
412
630
  {
@@ -439,9 +657,9 @@ function InputGroup({ children, leadingIcon, trailingIcon, className }) {
439
657
  }
440
658
 
441
659
  // src/components/Textarea.tsx
442
- var import_react5 = require("react");
660
+ var import_react6 = require("react");
443
661
  var import_jsx_runtime3 = require("react/jsx-runtime");
444
- var Textarea = (0, import_react5.forwardRef)(
662
+ var Textarea = (0, import_react6.forwardRef)(
445
663
  ({ invalid, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
446
664
  "textarea",
447
665
  {
@@ -455,9 +673,9 @@ var Textarea = (0, import_react5.forwardRef)(
455
673
  Textarea.displayName = "Textarea";
456
674
 
457
675
  // src/components/Select.tsx
458
- var import_react6 = require("react");
676
+ var import_react7 = require("react");
459
677
  var import_jsx_runtime4 = require("react/jsx-runtime");
460
- var Select = (0, import_react6.forwardRef)(
678
+ var Select = (0, import_react7.forwardRef)(
461
679
  ({ size = "md", className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
462
680
  "select",
463
681
  {
@@ -470,14 +688,14 @@ var Select = (0, import_react6.forwardRef)(
470
688
  Select.displayName = "Select";
471
689
 
472
690
  // src/components/Checkbox.tsx
473
- var import_react7 = require("react");
691
+ var import_react8 = require("react");
474
692
  var import_jsx_runtime5 = require("react/jsx-runtime");
475
693
  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" }) });
476
694
  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" }) });
477
- var Checkbox = (0, import_react7.forwardRef)(
695
+ var Checkbox = (0, import_react8.forwardRef)(
478
696
  ({ label, indeterminate, className, ...props }, forwardedRef) => {
479
- const innerRef = (0, import_react7.useRef)(null);
480
- (0, import_react7.useEffect)(() => {
697
+ const innerRef = (0, import_react8.useRef)(null);
698
+ (0, import_react8.useEffect)(() => {
481
699
  const el = innerRef.current;
482
700
  if (el) el.indeterminate = !!indeterminate;
483
701
  }, [indeterminate]);
@@ -502,9 +720,9 @@ var Checkbox = (0, import_react7.forwardRef)(
502
720
  Checkbox.displayName = "Checkbox";
503
721
 
504
722
  // src/components/Radio.tsx
505
- var import_react8 = require("react");
723
+ var import_react9 = require("react");
506
724
  var import_jsx_runtime6 = require("react/jsx-runtime");
507
- var Radio = (0, import_react8.forwardRef)(
725
+ var Radio = (0, import_react9.forwardRef)(
508
726
  ({ label, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: cx("uix-radio", className), children: [
509
727
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "radio", ref, ...props }),
510
728
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "uix-radio__dot" }),
@@ -517,9 +735,9 @@ function RadioGroup({ children, className }) {
517
735
  }
518
736
 
519
737
  // src/components/Switch.tsx
520
- var import_react9 = require("react");
738
+ var import_react10 = require("react");
521
739
  var import_jsx_runtime7 = require("react/jsx-runtime");
522
- var Switch = (0, import_react9.forwardRef)(
740
+ var Switch = (0, import_react10.forwardRef)(
523
741
  ({ label, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: cx("uix-switch", className), children: [
524
742
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", role: "switch", ref, ...props }),
525
743
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "uix-switch__track" }),
@@ -529,14 +747,20 @@ var Switch = (0, import_react9.forwardRef)(
529
747
  Switch.displayName = "Switch";
530
748
 
531
749
  // src/components/Field.tsx
750
+ var import_react11 = require("react");
532
751
  var import_jsx_runtime8 = require("react/jsx-runtime");
533
752
  function Field({ label, hint, error, success, required, children, className, htmlFor }) {
753
+ const uid = (0, import_react11.useId)();
754
+ const msgId = `${uid}-msg`;
755
+ const describedBy = error || success || hint ? msgId : void 0;
756
+ const control = (0, import_react11.isValidElement)(children) ? (0, import_react11.cloneElement)(children, {
757
+ "aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0,
758
+ "aria-invalid": error ? true : children.props["aria-invalid"]
759
+ }) : children;
534
760
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: cx("uix-field", className), children: [
535
761
  label && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "uix-field__label", htmlFor, "data-required": required || void 0, children: label }),
536
- children,
537
- error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__error", children: error }),
538
- success && !error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__success", children: success }),
539
- hint && !error && !success && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__hint", children: hint })
762
+ control,
763
+ /* @__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 })
540
764
  ] });
541
765
  }
542
766
 
@@ -666,11 +890,11 @@ function ListItem({ title, meta, trail, leading, children, className, ...props }
666
890
  }
667
891
 
668
892
  // src/components/DescriptionList.tsx
669
- var import_react10 = require("react");
893
+ var import_react12 = require("react");
670
894
  var import_jsx_runtime16 = require("react/jsx-runtime");
671
895
  function DescriptionList({ items, children, className, ...props }) {
672
896
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("dl", { className: cx("uix-dl", className), ...props, children: [
673
- items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react10.Fragment, { children: [
897
+ items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react12.Fragment, { children: [
674
898
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dt", { children: item.term }),
675
899
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dd", { children: item.description })
676
900
  ] }, item.id ?? i)),
@@ -685,14 +909,14 @@ function DescriptionItem({ term, children }) {
685
909
  }
686
910
 
687
911
  // src/components/Tabs.tsx
688
- var import_react11 = require("react");
912
+ var import_react13 = require("react");
689
913
  var import_jsx_runtime17 = require("react/jsx-runtime");
690
- var TabsCtx = (0, import_react11.createContext)({});
914
+ var TabsCtx = (0, import_react13.createContext)({});
691
915
  function Tabs({ variant = "line", value, onChange, children, className }) {
692
916
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TabsCtx.Provider, { value: { value, onChange }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { role: "tablist", className: cx("uix-tabs", `uix-tabs--${variant}`, className), children }) });
693
917
  }
694
918
  function Tab({ value, children, disabled, className, onClick, ...props }) {
695
- const ctx = (0, import_react11.useContext)(TabsCtx);
919
+ const ctx = (0, import_react13.useContext)(TabsCtx);
696
920
  const selected = ctx.value === value;
697
921
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
698
922
  "button",
@@ -712,7 +936,7 @@ function Tab({ value, children, disabled, className, onClick, ...props }) {
712
936
  }
713
937
 
714
938
  // src/components/AppShell.tsx
715
- var import_react12 = require("react");
939
+ var import_react14 = require("react");
716
940
  var import_jsx_runtime18 = require("react/jsx-runtime");
717
941
  function AppShell({
718
942
  nav,
@@ -727,7 +951,7 @@ function AppShell({
727
951
  ...props
728
952
  }) {
729
953
  const navState = nav ?? (collapsed ? "rail" : "full");
730
- (0, import_react12.useEffect)(() => {
954
+ (0, import_react14.useEffect)(() => {
731
955
  if (!focus || !onExitFocus) return;
732
956
  const onKey = (e) => {
733
957
  if (e.key === "Escape") onExitFocus();
@@ -754,7 +978,7 @@ function AppShell({
754
978
  }
755
979
 
756
980
  // src/components/Sidebar.tsx
757
- var import_react13 = require("react");
981
+ var import_react15 = require("react");
758
982
  var import_jsx_runtime19 = require("react/jsx-runtime");
759
983
  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" }) });
760
984
  function Sidebar({ collapsed, onToggle, brand, toggleIcon, children, className, ...props }) {
@@ -808,7 +1032,7 @@ function NavItem({ icon, badge, active, as: Tag = "a", children, className, ...p
808
1032
  );
809
1033
  }
810
1034
  function NavGroup({ icon, label, children, defaultExpanded = true }) {
811
- const [expanded, setExpanded] = (0, import_react13.useState)(defaultExpanded);
1035
+ const [expanded, setExpanded] = (0, import_react15.useState)(defaultExpanded);
812
1036
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "uix-navgroup", children: [
813
1037
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
814
1038
  "button",
@@ -876,7 +1100,7 @@ function StarButton({
876
1100
  }
877
1101
 
878
1102
  // src/components/NavFavourites.tsx
879
- var import_react14 = require("react");
1103
+ var import_react16 = require("react");
880
1104
  var import_jsx_runtime21 = require("react/jsx-runtime");
881
1105
  var ChevronDownIcon = () => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
882
1106
  "svg",
@@ -940,13 +1164,13 @@ function NavFavourites({
940
1164
  emptyReason,
941
1165
  className
942
1166
  }) {
943
- const regionId = (0, import_react14.useId)();
944
- const headerRef = (0, import_react14.useRef)(null);
945
- const regionRef = (0, import_react14.useRef)(null);
946
- const focusAfterRef = (0, import_react14.useRef)(null);
947
- const triggerRefs = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
1167
+ const regionId = (0, import_react16.useId)();
1168
+ const headerRef = (0, import_react16.useRef)(null);
1169
+ const regionRef = (0, import_react16.useRef)(null);
1170
+ const focusAfterRef = (0, import_react16.useRef)(null);
1171
+ const triggerRefs = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
948
1172
  const order = items.map((i) => i.id);
949
- (0, import_react14.useEffect)(() => {
1173
+ (0, import_react16.useEffect)(() => {
950
1174
  const id = focusAfterRef.current;
951
1175
  if (!id) return;
952
1176
  focusAfterRef.current = null;
@@ -1057,18 +1281,18 @@ function FavouriteRow({
1057
1281
  onRemove,
1058
1282
  registerTrigger
1059
1283
  }) {
1060
- const [open, setOpen] = (0, import_react14.useState)(false);
1061
- const [revealed, setRevealed] = (0, import_react14.useState)(false);
1062
- const menuId = (0, import_react14.useId)();
1063
- const triggerRef = (0, import_react14.useRef)(null);
1064
- const itemRefs = (0, import_react14.useRef)([]);
1284
+ const [open, setOpen] = (0, import_react16.useState)(false);
1285
+ const [revealed, setRevealed] = (0, import_react16.useState)(false);
1286
+ const menuId = (0, import_react16.useId)();
1287
+ const triggerRef = (0, import_react16.useRef)(null);
1288
+ const itemRefs = (0, import_react16.useRef)([]);
1065
1289
  const optionsTemplate = labels.options ?? "{label} favourite options";
1066
1290
  const actions = [
1067
1291
  ...isFirst ? [] : [{ label: labels.moveUp, onSelect: onMoveUp }],
1068
1292
  ...isLast ? [] : [{ label: labels.moveDown, onSelect: onMoveDown }],
1069
1293
  { label: labels.remove.replace("{label}", item.label), onSelect: onRemove }
1070
1294
  ];
1071
- (0, import_react14.useEffect)(() => {
1295
+ (0, import_react16.useEffect)(() => {
1072
1296
  if (open) itemRefs.current[0]?.focus();
1073
1297
  }, [open]);
1074
1298
  const closeAndReturnFocus = () => {
@@ -1253,16 +1477,17 @@ function Spinner({ size = "md", accent, className, ...props }) {
1253
1477
  // src/components/Toast.tsx
1254
1478
  var import_jsx_runtime24 = require("react/jsx-runtime");
1255
1479
  function Toast({ title, message, tone, icon, onClose, leaving, className, ...props }) {
1480
+ const assertive = tone === "danger";
1256
1481
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
1257
1482
  "div",
1258
1483
  {
1259
1484
  className: cx("uix-toast", tone && `uix-toast--${tone}`, className),
1260
1485
  "data-leaving": leaving || void 0,
1261
- role: "status",
1262
- "aria-live": "polite",
1486
+ role: assertive ? "alert" : "status",
1487
+ "aria-live": assertive ? "assertive" : "polite",
1263
1488
  ...props,
1264
1489
  children: [
1265
- icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__icon", children: icon }),
1490
+ icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__icon", "aria-hidden": "true", children: icon }),
1266
1491
  /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "uix-toast__body", children: [
1267
1492
  title && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__title", children: title }),
1268
1493
  message && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__msg", children: message })
@@ -1273,7 +1498,7 @@ function Toast({ title, message, tone, icon, onClose, leaving, className, ...pro
1273
1498
  );
1274
1499
  }
1275
1500
  function Toaster({ children, className, ...props }) {
1276
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cx("uix-toaster", className), "aria-live": "polite", "aria-atomic": "false", ...props, children });
1501
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cx("uix-toaster", className), ...props, children });
1277
1502
  }
1278
1503
 
1279
1504
  // src/components/Table.tsx
@@ -1510,9 +1735,64 @@ function Stat({ label, value, icon, meta, trend, className, ...props }) {
1510
1735
  }
1511
1736
 
1512
1737
  // src/components/Tooltip.tsx
1738
+ var import_react17 = require("react");
1513
1739
  var import_jsx_runtime31 = require("react/jsx-runtime");
1514
- function Tooltip({ label, children, ...props }) {
1515
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { "data-uix-tip": label, ...props, children });
1740
+ function Tooltip({ label, placement = "top", className, children, ...props }) {
1741
+ const triggerRef = (0, import_react17.useRef)(null);
1742
+ const bubbleRef = (0, import_react17.useRef)(null);
1743
+ const [open, setOpen] = (0, import_react17.useState)(false);
1744
+ const id = (0, import_react17.useId)();
1745
+ useAnchoredPosition(triggerRef, bubbleRef, { open, placement, offset: 6 });
1746
+ const show = () => {
1747
+ const b = bubbleRef.current;
1748
+ if (b && !b.matches(":popover-open")) {
1749
+ try {
1750
+ b.showPopover();
1751
+ } catch {
1752
+ }
1753
+ }
1754
+ setOpen(true);
1755
+ };
1756
+ const hide = () => {
1757
+ const b = bubbleRef.current;
1758
+ if (b && b.matches(":popover-open")) {
1759
+ try {
1760
+ b.hidePopover();
1761
+ } catch {
1762
+ }
1763
+ }
1764
+ setOpen(false);
1765
+ };
1766
+ (0, import_react17.useEffect)(() => () => {
1767
+ const b = bubbleRef.current;
1768
+ if (b && b.matches(":popover-open")) {
1769
+ try {
1770
+ b.hidePopover();
1771
+ } catch {
1772
+ }
1773
+ }
1774
+ }, []);
1775
+ const bubbleAttr = { popover: "manual" };
1776
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
1777
+ "span",
1778
+ {
1779
+ ref: triggerRef,
1780
+ className: cx("uix-tooltip", className),
1781
+ "aria-describedby": id,
1782
+ onMouseEnter: show,
1783
+ onMouseLeave: hide,
1784
+ onFocus: show,
1785
+ onBlur: hide,
1786
+ onKeyDown: (e) => {
1787
+ if (e.key === "Escape") hide();
1788
+ },
1789
+ ...props,
1790
+ children: [
1791
+ children,
1792
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { ref: bubbleRef, id, role: "tooltip", className: "uix-tooltip__bubble", ...bubbleAttr, children: label })
1793
+ ]
1794
+ }
1795
+ );
1516
1796
  }
1517
1797
 
1518
1798
  // src/components/States.tsx
@@ -1614,9 +1894,9 @@ function ComposerBar({ children, className, ...props }) {
1614
1894
  }
1615
1895
 
1616
1896
  // src/components/Segmented.tsx
1617
- var import_react15 = require("react");
1897
+ var import_react18 = require("react");
1618
1898
  var import_jsx_runtime37 = require("react/jsx-runtime");
1619
- var SegmentedCtx = (0, import_react15.createContext)({});
1899
+ var SegmentedCtx = (0, import_react18.createContext)({});
1620
1900
  function Segmented({ value, onChange, children, className, ...props }) {
1621
1901
  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 }) });
1622
1902
  }
@@ -1627,7 +1907,7 @@ function SegmentedOption({
1627
1907
  onClick,
1628
1908
  ...props
1629
1909
  }) {
1630
- const ctx = (0, import_react15.useContext)(SegmentedCtx);
1910
+ const ctx = (0, import_react18.useContext)(SegmentedCtx);
1631
1911
  const selected = ctx.value === value;
1632
1912
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1633
1913
  "button",
@@ -1676,10 +1956,37 @@ function Note({ tone, icon, children, className, ...props }) {
1676
1956
  }
1677
1957
 
1678
1958
  // src/components/Popover.tsx
1959
+ var import_react19 = require("react");
1679
1960
  var import_jsx_runtime40 = require("react/jsx-runtime");
1680
- function Popover({ popover = "auto", children, className, ...props }) {
1961
+ function Popover({
1962
+ popover = "auto",
1963
+ anchor,
1964
+ placement = "bottom-start",
1965
+ offset = 6,
1966
+ className,
1967
+ children,
1968
+ ...props
1969
+ }) {
1970
+ const ref = (0, import_react19.useRef)(null);
1971
+ const [open, setOpen] = (0, import_react19.useState)(false);
1681
1972
  const popoverAttr = { popover };
1682
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: cx("uix-popover", className), ...popoverAttr, ...props, children });
1973
+ const reposition = useAnchoredPosition(anchor ?? null, ref, {
1974
+ open: open && !!anchor,
1975
+ placement,
1976
+ offset
1977
+ });
1978
+ (0, import_react19.useEffect)(() => {
1979
+ const el = ref.current;
1980
+ if (!el || !anchor) return;
1981
+ const sync = () => {
1982
+ const isOpen = el.matches(":popover-open");
1983
+ if (isOpen) reposition();
1984
+ setOpen(isOpen);
1985
+ };
1986
+ el.addEventListener("toggle", sync);
1987
+ return () => el.removeEventListener("toggle", sync);
1988
+ }, [anchor, reposition]);
1989
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { ref, className: cx("uix-popover", className), ...popoverAttr, ...props, children });
1683
1990
  }
1684
1991
 
1685
1992
  // src/components/CommandPalette.tsx
@@ -1766,48 +2073,124 @@ function KanbanCard({ title, meta, children, className, ...props }) {
1766
2073
  }
1767
2074
 
1768
2075
  // src/components/Tree.tsx
1769
- var import_react16 = require("react");
2076
+ var import_react20 = require("react");
2077
+
2078
+ // src/tree-model.ts
2079
+ function flattenTree(nodes, expanded, level = 1) {
2080
+ const out = [];
2081
+ const setsize = nodes.length;
2082
+ nodes.forEach((node, i) => {
2083
+ const hasChildren = Array.isArray(node.children) && node.children.length > 0;
2084
+ const isExpanded = hasChildren && expanded.has(node.id);
2085
+ out.push({ node, level, setsize, posinset: i + 1, hasChildren, isExpanded });
2086
+ if (isExpanded) out.push(...flattenTree(node.children, expanded, level + 1));
2087
+ });
2088
+ return out;
2089
+ }
2090
+ function treeNav(flat, currentId, key) {
2091
+ const idx = flat.findIndex((f) => f.node.id === currentId);
2092
+ if (idx < 0) return {};
2093
+ const cur = flat[idx];
2094
+ const focusAt = (i) => {
2095
+ const clamped = Math.max(0, Math.min(flat.length - 1, i));
2096
+ return { focusId: flat[clamped]?.node.id };
2097
+ };
2098
+ switch (key) {
2099
+ case "ArrowDown":
2100
+ return focusAt(idx + 1);
2101
+ case "ArrowUp":
2102
+ return focusAt(idx - 1);
2103
+ case "Home":
2104
+ return focusAt(0);
2105
+ case "End":
2106
+ return focusAt(flat.length - 1);
2107
+ case "ArrowRight":
2108
+ if (cur.hasChildren && !cur.isExpanded) return { toggleId: cur.node.id };
2109
+ if (cur.hasChildren && cur.isExpanded) return focusAt(idx + 1);
2110
+ return {};
2111
+ case "ArrowLeft":
2112
+ if (cur.hasChildren && cur.isExpanded) return { toggleId: cur.node.id };
2113
+ for (let i = idx - 1; i >= 0; i--) {
2114
+ if (flat[i].level === cur.level - 1) return { focusId: flat[i].node.id };
2115
+ }
2116
+ return {};
2117
+ case "Enter":
2118
+ case " ":
2119
+ return cur.hasChildren ? { toggleId: cur.node.id, selectId: cur.node.id } : { selectId: cur.node.id };
2120
+ default:
2121
+ return {};
2122
+ }
2123
+ }
2124
+
2125
+ // src/components/Tree.tsx
1770
2126
  var import_jsx_runtime44 = require("react/jsx-runtime");
1771
2127
  var ChevronIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("path", { d: "M2 4l4 4 4-4" }) });
1772
- function TreeNode({ node, expanded, selected, onToggle, onSelect, level = 0 }) {
2128
+ function TreeNode({ node, expanded, selected, selectable, tabbableId, onToggle, onSelect, onFocusNode, level = 1 }) {
1773
2129
  const hasChildren = Array.isArray(node.children) && node.children.length > 0;
1774
2130
  const isExpanded = expanded.has(node.id);
1775
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("li", { children: [
1776
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
1777
- "button",
1778
- {
1779
- className: "uix-tree__row",
1780
- "aria-expanded": hasChildren ? isExpanded : void 0,
1781
- "aria-selected": selected === node.id,
1782
- onClick: () => {
1783
- if (hasChildren) onToggle(node.id);
1784
- onSelect?.(node.id);
1785
- },
1786
- children: [
2131
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2132
+ "li",
2133
+ {
2134
+ role: "treeitem",
2135
+ "aria-level": level,
2136
+ "aria-expanded": hasChildren ? isExpanded : void 0,
2137
+ "aria-selected": selectable ? selected === node.id : void 0,
2138
+ "data-id": node.id,
2139
+ tabIndex: tabbableId === node.id ? 0 : -1,
2140
+ className: "uix-tree__item",
2141
+ onFocus: (e) => {
2142
+ if (e.target === e.currentTarget) onFocusNode(node.id);
2143
+ },
2144
+ onClick: (e) => {
2145
+ e.stopPropagation();
2146
+ if (hasChildren) onToggle(node.id);
2147
+ onSelect?.(node.id);
2148
+ onFocusNode(node.id);
2149
+ },
2150
+ children: [
2151
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "uix-tree__row", children: [
1787
2152
  /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-tree__toggle", children: hasChildren && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChevronIcon2, {}) }),
1788
2153
  node.icon && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { "aria-hidden": "true", children: node.icon }),
1789
- node.label
1790
- ]
1791
- }
1792
- ),
1793
- hasChildren && isExpanded && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { children: node.children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
1794
- TreeNode,
1795
- {
1796
- node: child,
1797
- expanded,
1798
- selected,
1799
- onToggle,
1800
- onSelect,
1801
- level: level + 1
1802
- },
1803
- child.id
1804
- )) })
1805
- ] });
2154
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-tree__label", children: node.label })
2155
+ ] }),
2156
+ hasChildren && isExpanded && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { role: "group", className: "uix-tree__group", children: node.children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2157
+ TreeNode,
2158
+ {
2159
+ node: child,
2160
+ expanded,
2161
+ selected,
2162
+ selectable,
2163
+ tabbableId,
2164
+ onToggle,
2165
+ onSelect,
2166
+ onFocusNode,
2167
+ level: level + 1
2168
+ },
2169
+ child.id
2170
+ )) })
2171
+ ]
2172
+ }
2173
+ );
1806
2174
  }
1807
- function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected, onToggle, onSelect, className, ...props }) {
1808
- const [internalExpanded, setInternalExpanded] = (0, import_react16.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
2175
+ function Tree({
2176
+ nodes,
2177
+ expanded: controlledExpanded,
2178
+ defaultExpanded,
2179
+ selected,
2180
+ onToggle,
2181
+ onSelect,
2182
+ virtualize,
2183
+ rowHeight = 32,
2184
+ maxHeight = 384,
2185
+ className,
2186
+ ...props
2187
+ }) {
2188
+ const [internalExpanded, setInternalExpanded] = (0, import_react20.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
1809
2189
  const expanded = controlledExpanded ?? internalExpanded;
1810
- function handleToggle(id) {
2190
+ const rootRef = (0, import_react20.useRef)(null);
2191
+ const [focusedId, setFocusedId] = (0, import_react20.useState)(void 0);
2192
+ const selectable = selected !== void 0 || onSelect != null;
2193
+ const handleToggle = (0, import_react20.useCallback)((id) => {
1811
2194
  if (!controlledExpanded) {
1812
2195
  setInternalExpanded((prev) => {
1813
2196
  const next = new Set(prev);
@@ -1817,19 +2200,371 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
1817
2200
  });
1818
2201
  }
1819
2202
  onToggle?.(id);
2203
+ }, [controlledExpanded, onToggle]);
2204
+ const flat = (0, import_react20.useMemo)(() => flattenTree(nodes, expanded), [nodes, expanded]);
2205
+ const useVirtual = virtualize ?? shouldVirtualize(flat.length);
2206
+ const tabbableId = focusedId ?? selected ?? nodes[0]?.id;
2207
+ if (useVirtual) {
2208
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2209
+ VirtualTreeView,
2210
+ {
2211
+ flat,
2212
+ rowHeight,
2213
+ maxHeight,
2214
+ selectable,
2215
+ selected,
2216
+ tabbableId,
2217
+ onToggle: handleToggle,
2218
+ onSelect,
2219
+ focusedId,
2220
+ setFocusedId,
2221
+ className,
2222
+ ...props
2223
+ }
2224
+ );
1820
2225
  }
1821
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { className: cx("uix-tree", className), role: "tree", ...props, children: nodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2226
+ const treeItems = () => Array.from(rootRef.current?.querySelectorAll('[role="treeitem"]') ?? []);
2227
+ const focusItem = (el) => {
2228
+ if (el) {
2229
+ el.focus();
2230
+ setFocusedId(el.dataset.id);
2231
+ }
2232
+ };
2233
+ const onKeyDown = (e) => {
2234
+ const cur = e.target.closest('[role="treeitem"]');
2235
+ if (!cur || !rootRef.current?.contains(cur)) return;
2236
+ const list = treeItems();
2237
+ const idx = list.indexOf(cur);
2238
+ const id = cur.dataset.id;
2239
+ const exp = cur.getAttribute("aria-expanded");
2240
+ switch (e.key) {
2241
+ case "ArrowDown":
2242
+ e.preventDefault();
2243
+ focusItem(list[Math.min(idx + 1, list.length - 1)]);
2244
+ break;
2245
+ case "ArrowUp":
2246
+ e.preventDefault();
2247
+ focusItem(list[Math.max(idx - 1, 0)]);
2248
+ break;
2249
+ case "Home":
2250
+ e.preventDefault();
2251
+ focusItem(list[0]);
2252
+ break;
2253
+ case "End":
2254
+ e.preventDefault();
2255
+ focusItem(list[list.length - 1]);
2256
+ break;
2257
+ case "ArrowRight":
2258
+ e.preventDefault();
2259
+ if (exp === "false") handleToggle(id);
2260
+ else if (exp === "true") focusItem(cur.querySelector(':scope > [role="group"] > [role="treeitem"]'));
2261
+ break;
2262
+ case "ArrowLeft":
2263
+ e.preventDefault();
2264
+ if (exp === "true") handleToggle(id);
2265
+ else focusItem(cur.parentElement?.closest('[role="treeitem"]'));
2266
+ break;
2267
+ case "Enter":
2268
+ case " ":
2269
+ e.preventDefault();
2270
+ if (exp !== null) handleToggle(id);
2271
+ onSelect?.(id);
2272
+ setFocusedId(id);
2273
+ break;
2274
+ default:
2275
+ break;
2276
+ }
2277
+ };
2278
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { ref: rootRef, className: cx("uix-tree", className), role: "tree", onKeyDown, ...props, children: nodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
1822
2279
  TreeNode,
1823
2280
  {
1824
2281
  node,
1825
2282
  expanded,
1826
2283
  selected,
2284
+ selectable,
2285
+ tabbableId,
1827
2286
  onToggle: handleToggle,
1828
- onSelect
2287
+ onSelect,
2288
+ onFocusNode: setFocusedId,
2289
+ level: 1
1829
2290
  },
1830
2291
  node.id
1831
2292
  )) });
1832
2293
  }
2294
+ function VirtualTreeView({
2295
+ flat,
2296
+ rowHeight,
2297
+ maxHeight,
2298
+ selectable,
2299
+ selected,
2300
+ tabbableId,
2301
+ onToggle,
2302
+ onSelect,
2303
+ focusedId,
2304
+ setFocusedId,
2305
+ className,
2306
+ ...props
2307
+ }) {
2308
+ const scrollRef = (0, import_react20.useRef)(null);
2309
+ const [scrollTop, setScrollTop] = (0, import_react20.useState)(0);
2310
+ const pendingFocus = (0, import_react20.useRef)(false);
2311
+ const win = virtualWindow(scrollTop, maxHeight, rowHeight, flat.length);
2312
+ const rows = flat.slice(win.start, win.end);
2313
+ (0, import_react20.useEffect)(() => {
2314
+ if (!pendingFocus.current || focusedId == null) return;
2315
+ const el = scrollRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(focusedId)}"]`);
2316
+ if (el) {
2317
+ el.focus();
2318
+ pendingFocus.current = false;
2319
+ }
2320
+ }, [focusedId, win.start, win.end]);
2321
+ const moveTo = (id) => {
2322
+ const i = flat.findIndex((f) => f.node.id === id);
2323
+ if (i < 0) return;
2324
+ const top = i * rowHeight;
2325
+ let next = scrollTop;
2326
+ if (top < scrollTop) next = top;
2327
+ else if (top + rowHeight > scrollTop + maxHeight) next = top + rowHeight - maxHeight;
2328
+ if (next !== scrollTop) {
2329
+ if (scrollRef.current) scrollRef.current.scrollTop = next;
2330
+ setScrollTop(next);
2331
+ }
2332
+ pendingFocus.current = true;
2333
+ setFocusedId(id);
2334
+ };
2335
+ const onKeyDown = (e) => {
2336
+ const curId = e.target.closest('[role="treeitem"]')?.dataset.id ?? focusedId;
2337
+ const act = treeNav(flat, curId, e.key);
2338
+ if (act.focusId === void 0 && act.toggleId === void 0 && act.selectId === void 0) return;
2339
+ e.preventDefault();
2340
+ if (act.toggleId !== void 0) onToggle(act.toggleId);
2341
+ if (act.selectId !== void 0) {
2342
+ onSelect?.(act.selectId);
2343
+ if (act.focusId === void 0) {
2344
+ pendingFocus.current = true;
2345
+ setFocusedId(act.selectId);
2346
+ }
2347
+ }
2348
+ if (act.focusId !== void 0) moveTo(act.focusId);
2349
+ };
2350
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2351
+ "ul",
2352
+ {
2353
+ ref: scrollRef,
2354
+ className: cx("uix-tree", "uix-tree--virtual", className),
2355
+ role: "tree",
2356
+ style: { maxHeight, overflow: "auto" },
2357
+ onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
2358
+ onKeyDown,
2359
+ ...props,
2360
+ children: [
2361
+ win.padTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { "aria-hidden": "true", style: { height: win.padTop } }),
2362
+ rows.map((f) => {
2363
+ const rowStyle = { height: rowHeight, paddingLeft: `calc(${f.level - 1} * var(--uix-space-5))` };
2364
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2365
+ "li",
2366
+ {
2367
+ role: "treeitem",
2368
+ "aria-level": f.level,
2369
+ "aria-setsize": f.setsize,
2370
+ "aria-posinset": f.posinset,
2371
+ "aria-expanded": f.hasChildren ? f.isExpanded : void 0,
2372
+ "aria-selected": selectable ? selected === f.node.id : void 0,
2373
+ "data-id": f.node.id,
2374
+ tabIndex: tabbableId === f.node.id ? 0 : -1,
2375
+ className: "uix-tree__item",
2376
+ onFocus: (e) => {
2377
+ if (e.target === e.currentTarget) setFocusedId(f.node.id);
2378
+ },
2379
+ onClick: (e) => {
2380
+ e.stopPropagation();
2381
+ if (f.hasChildren) onToggle(f.node.id);
2382
+ onSelect?.(f.node.id);
2383
+ setFocusedId(f.node.id);
2384
+ },
2385
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "uix-tree__row", style: rowStyle, children: [
2386
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-tree__toggle", children: f.hasChildren && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChevronIcon2, {}) }),
2387
+ f.node.icon && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { "aria-hidden": "true", children: f.node.icon }),
2388
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-tree__label", children: f.node.label })
2389
+ ] })
2390
+ },
2391
+ f.node.id
2392
+ );
2393
+ }),
2394
+ win.padBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { "aria-hidden": "true", style: { height: win.padBottom } })
2395
+ ]
2396
+ }
2397
+ );
2398
+ }
2399
+
2400
+ // src/components/EditorialHome.tsx
2401
+ var import_jsx_runtime45 = require("react/jsx-runtime");
2402
+ function PageIntro({
2403
+ kicker,
2404
+ title,
2405
+ lede,
2406
+ searchLabel,
2407
+ search,
2408
+ shortcuts,
2409
+ shortcutsLabel,
2410
+ className,
2411
+ children,
2412
+ ...props
2413
+ }) {
2414
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("section", { className: cx("uix-page-intro", className), ...props, children: [
2415
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
2416
+ kicker != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-page-kicker", children: kicker }),
2417
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h1", { className: "uix-page-title", children: title }),
2418
+ lede != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-page-lede", children: lede })
2419
+ ] }),
2420
+ search != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
2421
+ searchLabel != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-intro-search__label", children: searchLabel }),
2422
+ search
2423
+ ] }),
2424
+ shortcuts != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "uix-shortcut-grid", "aria-label": shortcutsLabel, children: shortcuts }),
2425
+ children
2426
+ ] });
2427
+ }
2428
+ function SectionHead({ title, titleId, action, className, ...props }) {
2429
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: cx("uix-section-head", className), ...props, children: [
2430
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h2", { className: "uix-section-title", id: titleId, children: title }),
2431
+ action
2432
+ ] });
2433
+ }
2434
+ function NoticeQueue({ icon, title, summary, meta, position, actions, className, ...props }) {
2435
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("section", { className: cx("uix-notice", className), ...props, children: [
2436
+ icon,
2437
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "uix-notice__content", "aria-live": "polite", children: [
2438
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "uix-notice__copy", children: [
2439
+ title != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("strong", { children: title }),
2440
+ title != null && summary != null && " ",
2441
+ summary
2442
+ ] }),
2443
+ meta != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-notice__meta", children: meta })
2444
+ ] }),
2445
+ (position != null || actions != null) && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "uix-notice__actions", children: [
2446
+ position != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "uix-notice__position", children: position }),
2447
+ actions
2448
+ ] })
2449
+ ] });
2450
+ }
2451
+ function FeaturedStage({
2452
+ visual,
2453
+ visualLabel,
2454
+ ordinal,
2455
+ eyebrow,
2456
+ title,
2457
+ titleId,
2458
+ description,
2459
+ meta,
2460
+ action,
2461
+ className,
2462
+ ...props
2463
+ }) {
2464
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("article", { className: cx("uix-featured__stage", className), ...props, children: [
2465
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2466
+ "div",
2467
+ {
2468
+ className: "uix-featured__visual",
2469
+ role: visualLabel != null ? "img" : void 0,
2470
+ "aria-label": visualLabel,
2471
+ "aria-hidden": visualLabel == null || void 0,
2472
+ children: visual
2473
+ }
2474
+ ),
2475
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "uix-featured__content", "aria-live": "polite", children: [
2476
+ (eyebrow != null || ordinal != null) && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "uix-featured__eyebrow", children: [
2477
+ ordinal != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "uix-featured__now", children: ordinal }),
2478
+ eyebrow
2479
+ ] }),
2480
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h2", { className: "uix-featured__title", id: titleId, children: title }),
2481
+ description != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-featured__description", children: description }),
2482
+ meta != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-featured__meta", children: meta }),
2483
+ action
2484
+ ] })
2485
+ ] });
2486
+ }
2487
+ function FeaturedRundown({ eyebrow, title, titleId, meta, children, className, ...props }) {
2488
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("aside", { className: cx("uix-rundown", className), ...props, children: [
2489
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "uix-rundown__head", children: [
2490
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
2491
+ eyebrow != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-rundown__eyebrow", children: eyebrow }),
2492
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h2", { className: "uix-rundown__title", id: titleId, children: title })
2493
+ ] }),
2494
+ meta
2495
+ ] }),
2496
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "uix-rundown__items", children })
2497
+ ] });
2498
+ }
2499
+ function FeaturedRundownItem({ number, topic, title, selected = false, className, ...props }) {
2500
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2501
+ "button",
2502
+ {
2503
+ type: "button",
2504
+ className: cx("uix-rundown__item", className),
2505
+ "aria-pressed": selected,
2506
+ "data-selected": selected ? "true" : "false",
2507
+ ...props,
2508
+ children: [
2509
+ number != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "uix-rundown__number", children: number }),
2510
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { children: [
2511
+ topic != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "uix-rundown__topic", children: topic }),
2512
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "uix-rundown__item-title", children: title })
2513
+ ] })
2514
+ ]
2515
+ }
2516
+ );
2517
+ }
2518
+ function NewsLead({ meta, title, summary, className, ...props }) {
2519
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("article", { className: cx("uix-news-lead", className), ...props, children: [
2520
+ meta != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-news-lead__meta", children: meta }),
2521
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "uix-news-lead__title", children: title }),
2522
+ summary != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-news-lead__summary", children: summary })
2523
+ ] });
2524
+ }
2525
+ function ContentList({ children, className, ...props }) {
2526
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: cx("uix-content-list", className), ...props, children });
2527
+ }
2528
+ function ContentListItem({ title, meta, trailing, className, ...props }) {
2529
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("li", { className: cx("uix-content-list__item", className), ...props, children: [
2530
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
2531
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-content-list__title", children: title }),
2532
+ meta != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-content-list__meta", children: meta })
2533
+ ] }),
2534
+ trailing
2535
+ ] });
2536
+ }
2537
+ function ResourceGrid({ children, className, ...props }) {
2538
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: cx("uix-resource-grid", className), ...props, children });
2539
+ }
2540
+ function StatLine({ items, children, className, ...props }) {
2541
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: cx("uix-stat-line", className), ...props, children: [
2542
+ items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "uix-stat-line__item", children: [
2543
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "uix-stat-line__value", children: item.value }),
2544
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "uix-stat-line__label", children: item.label })
2545
+ ] }, item.id ?? i)),
2546
+ children
2547
+ ] });
2548
+ }
2549
+ function EventRow({ month, day, title, meta, className, ...props }) {
2550
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: cx("uix-event-row", className), ...props, children: [
2551
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "uix-event-date", children: [
2552
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "uix-event-date__month", children: month }),
2553
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "uix-event-date__day", children: day })
2554
+ ] }),
2555
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
2556
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-event-row__title", children: title }),
2557
+ meta != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "uix-event-row__meta", children: meta })
2558
+ ] })
2559
+ ] });
2560
+ }
2561
+ function StatusRow({ indicator, name, meta, className, ...props }) {
2562
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: cx("uix-status-row", className), ...props, children: [
2563
+ indicator,
2564
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "uix-status-row__name", children: name }),
2565
+ meta != null && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "uix-list-meta", children: meta })
2566
+ ] });
2567
+ }
1833
2568
  // Annotate the CommonJS export names for ESM import in node:
1834
2569
  0 && (module.exports = {
1835
2570
  Alert,
@@ -1850,13 +2585,19 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
1850
2585
  Comments,
1851
2586
  Composer,
1852
2587
  ComposerBar,
2588
+ ContentList,
2589
+ ContentListItem,
1853
2590
  DescriptionItem,
1854
2591
  DescriptionList,
1855
2592
  DetailLayout,
1856
2593
  Drawer,
1857
2594
  EmptyState,
1858
2595
  ErrorState,
2596
+ EventRow,
1859
2597
  ExpandToggle,
2598
+ FeaturedRundown,
2599
+ FeaturedRundownItem,
2600
+ FeaturedStage,
1860
2601
  Field,
1861
2602
  Highlighted,
1862
2603
  Inbox,
@@ -1878,8 +2619,11 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
1878
2619
  NavFavourites,
1879
2620
  NavGroup,
1880
2621
  NavItem,
2622
+ NewsLead,
1881
2623
  Note,
2624
+ NoticeQueue,
1882
2625
  PageHeader,
2626
+ PageIntro,
1883
2627
  Pagination,
1884
2628
  Peek,
1885
2629
  Popover,
@@ -1887,8 +2631,10 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
1887
2631
  Prose,
1888
2632
  Radio,
1889
2633
  RadioGroup,
2634
+ ResourceGrid,
1890
2635
  RowAction,
1891
2636
  RowActions,
2637
+ SectionHead,
1892
2638
  Segmented,
1893
2639
  SegmentedOption,
1894
2640
  Select,
@@ -1898,7 +2644,9 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
1898
2644
  Spinner,
1899
2645
  StarButton,
1900
2646
  Stat,
2647
+ StatLine,
1901
2648
  StatusPill,
2649
+ StatusRow,
1902
2650
  SubNavItem,
1903
2651
  Switch,
1904
2652
  Tab,
@@ -1918,7 +2666,9 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
1918
2666
  UserChip,
1919
2667
  applyFilters,
1920
2668
  clampWidth,
2669
+ computePosition,
1921
2670
  cx,
2671
+ flattenTree,
1922
2672
  highlightSegments,
1923
2673
  matchFilter,
1924
2674
  mergePinned,
@@ -1933,6 +2683,8 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
1933
2683
  toggleId,
1934
2684
  togglePage,
1935
2685
  toggleSort,
2686
+ treeNav,
2687
+ useAnchoredPosition,
1936
2688
  useDialog,
1937
2689
  useTable,
1938
2690
  virtualWindow