@tensor_1/react 2.5.0 → 2.7.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
@@ -24,22 +24,29 @@ __export(src_exports, {
24
24
  AppShell: () => AppShell,
25
25
  Avatar: () => Avatar,
26
26
  AvatarGroup: () => AvatarGroup,
27
+ BulkBar: () => BulkBar,
27
28
  Button: () => Button,
28
29
  ButtonGroup: () => ButtonGroup,
29
30
  Card: () => Card,
31
+ CellStrong: () => CellStrong,
32
+ CellSub: () => CellSub,
30
33
  Checkbox: () => Checkbox,
31
34
  CommandGroup: () => CommandGroup,
32
35
  CommandItem: () => CommandItem,
33
36
  CommandPalette: () => CommandPalette,
34
37
  Comment: () => Comment,
35
38
  Comments: () => Comments,
39
+ Composer: () => Composer,
40
+ ComposerBar: () => ComposerBar,
36
41
  DescriptionItem: () => DescriptionItem,
37
42
  DescriptionList: () => DescriptionList,
38
43
  DetailLayout: () => DetailLayout,
39
44
  Drawer: () => Drawer,
40
45
  EmptyState: () => EmptyState,
41
46
  ErrorState: () => ErrorState,
47
+ ExpandToggle: () => ExpandToggle,
42
48
  Field: () => Field,
49
+ Highlighted: () => Highlighted,
43
50
  Inbox: () => Inbox,
44
51
  InboxDetail: () => InboxDetail,
45
52
  InboxItem: () => InboxItem,
@@ -53,6 +60,7 @@ __export(src_exports, {
53
60
  List: () => List,
54
61
  ListItem: () => ListItem,
55
62
  LoadingState: () => LoadingState,
63
+ Mark: () => Mark,
56
64
  Meter: () => Meter,
57
65
  Modal: () => Modal,
58
66
  NavFavourites: () => NavFavourites,
@@ -67,6 +75,10 @@ __export(src_exports, {
67
75
  Prose: () => Prose,
68
76
  Radio: () => Radio,
69
77
  RadioGroup: () => RadioGroup,
78
+ RowAction: () => RowAction,
79
+ RowActions: () => RowActions,
80
+ Segmented: () => Segmented,
81
+ SegmentedOption: () => SegmentedOption,
70
82
  Select: () => Select,
71
83
  Sidebar: () => Sidebar,
72
84
  SidebarSection: () => SidebarSection,
@@ -92,8 +104,30 @@ __export(src_exports, {
92
104
  Tr: () => Tr,
93
105
  Tree: () => Tree,
94
106
  UserChip: () => UserChip,
107
+ applyFilters: () => applyFilters,
108
+ clampWidth: () => clampWidth,
109
+ computePosition: () => computePosition,
95
110
  cx: () => cx,
96
- useDialog: () => useDialog
111
+ flattenTree: () => flattenTree,
112
+ highlightSegments: () => highlightSegments,
113
+ matchFilter: () => matchFilter,
114
+ mergePinned: () => mergePinned,
115
+ multiSort: () => multiSort,
116
+ nextSortDir: () => nextSortDir,
117
+ parseView: () => parseView,
118
+ reorder: () => reorder,
119
+ searchRows: () => searchRows,
120
+ selectAllState: () => selectAllState,
121
+ serializeView: () => serializeView,
122
+ shouldVirtualize: () => shouldVirtualize,
123
+ toggleId: () => toggleId,
124
+ togglePage: () => togglePage,
125
+ toggleSort: () => toggleSort,
126
+ treeNav: () => treeNav,
127
+ useAnchoredPosition: () => useAnchoredPosition,
128
+ useDialog: () => useDialog,
129
+ useTable: () => useTable,
130
+ virtualWindow: () => virtualWindow
97
131
  });
98
132
  module.exports = __toCommonJS(src_exports);
99
133
 
@@ -104,27 +138,453 @@ function cx(...args) {
104
138
 
105
139
  // src/hooks/useDialog.ts
106
140
  var import_react = require("react");
141
+ var lockCount = 0;
142
+ var saved = null;
143
+ function lockBodyScroll() {
144
+ if (typeof document === "undefined") return;
145
+ if (++lockCount > 1) return;
146
+ const el = document.scrollingElement ?? document.documentElement;
147
+ const scrollbarW = window.innerWidth - document.documentElement.clientWidth;
148
+ saved = {
149
+ el,
150
+ overflow: el.style.overflow,
151
+ paddingRight: el.style.paddingRight,
152
+ overscroll: el.style.overscrollBehavior
153
+ };
154
+ el.style.overflow = "hidden";
155
+ if (scrollbarW > 0) {
156
+ const current = parseFloat(getComputedStyle(el).paddingRight) || 0;
157
+ el.style.paddingRight = `${current + scrollbarW}px`;
158
+ }
159
+ el.style.overscrollBehavior = "contain";
160
+ }
161
+ function unlockBodyScroll() {
162
+ if (typeof document === "undefined" || lockCount === 0) return;
163
+ if (--lockCount > 0) return;
164
+ if (saved) {
165
+ saved.el.style.overflow = saved.overflow;
166
+ saved.el.style.paddingRight = saved.paddingRight;
167
+ saved.el.style.overscrollBehavior = saved.overscroll;
168
+ saved = null;
169
+ }
170
+ }
107
171
  function useDialog(open) {
108
172
  const ref = (0, import_react.useRef)(null);
109
173
  (0, import_react.useEffect)(() => {
110
174
  const el = ref.current;
111
- if (!el) return;
112
- if (open) {
113
- if (!el.open) el.showModal();
114
- } else {
115
- if (el.open) el.close();
175
+ if (!el || !open) {
176
+ if (el && el.open) el.close();
177
+ return;
116
178
  }
179
+ if (!el.open) el.showModal();
180
+ lockBodyScroll();
117
181
  return () => {
182
+ unlockBodyScroll();
118
183
  if (el.open) el.close();
119
184
  };
120
185
  }, [open]);
121
186
  return ref;
122
187
  }
123
188
 
124
- // src/components/Button.tsx
189
+ // src/hooks/useTable.ts
125
190
  var import_react2 = require("react");
191
+
192
+ // src/table-engine.ts
193
+ function compare(a, b) {
194
+ if (a == null && b == null) return 0;
195
+ if (a == null) return -1;
196
+ if (b == null) return 1;
197
+ if (typeof a === "number" && typeof b === "number") return a - b;
198
+ return String(a).localeCompare(String(b), void 0, { numeric: true, sensitivity: "base" });
199
+ }
200
+ function multiSort(rows, keys) {
201
+ if (!keys.length) return rows.slice();
202
+ return rows.map((row, i) => ({ row, i })).sort((x, y) => {
203
+ for (const k of keys) {
204
+ const c = compare(x.row[k.field], y.row[k.field]);
205
+ if (c !== 0) return k.dir === "ascending" ? c : -c;
206
+ }
207
+ return x.i - y.i;
208
+ }).map((w) => w.row);
209
+ }
210
+ function nextSortDir(current) {
211
+ if (current === "ascending") return "descending";
212
+ if (current === "descending") return "none";
213
+ return "ascending";
214
+ }
215
+ function toggleSort(keys, field, additive = false) {
216
+ const existing = keys.find((k) => k.field === field);
217
+ const dir = nextSortDir(existing?.dir);
218
+ if (!additive) return dir === "none" ? [] : [{ field, dir }];
219
+ const rest = keys.filter((k) => k.field !== field);
220
+ return dir === "none" ? rest : [...rest, { field, dir }];
221
+ }
222
+ function asNum(v) {
223
+ return typeof v === "number" ? v : Number(v);
224
+ }
225
+ function asTime(v) {
226
+ if (v == null) return NaN;
227
+ if (v instanceof Date) return v.getTime();
228
+ if (typeof v === "number") return v;
229
+ const s = String(v).trim();
230
+ if (s === "") return NaN;
231
+ const parsed = Date.parse(s);
232
+ return Number.isNaN(parsed) ? Number(s) : parsed;
233
+ }
234
+ function matchDate(cell, f) {
235
+ const c = asTime(cell);
236
+ if (Number.isNaN(c)) return false;
237
+ switch (f.op) {
238
+ case "eq":
239
+ return c === asTime(f.value);
240
+ case "lt":
241
+ return c < asTime(f.value);
242
+ case "gt":
243
+ return c > asTime(f.value);
244
+ case "between": {
245
+ const [lo, hi] = f.value.map(asTime);
246
+ return c >= lo && c <= hi;
247
+ }
248
+ default:
249
+ return true;
250
+ }
251
+ }
252
+ function matchFilter(row, f) {
253
+ const cell = row[f.field];
254
+ if (f.kind === "date") return matchDate(cell, f);
255
+ switch (f.op) {
256
+ case "isAnyOf":
257
+ return Array.isArray(f.value) && f.value.includes(cell);
258
+ case "isNoneOf":
259
+ return Array.isArray(f.value) && !f.value.includes(cell);
260
+ case "contains":
261
+ return String(cell ?? "").toLowerCase().includes(String(f.value ?? "").toLowerCase());
262
+ case "equals":
263
+ return String(cell ?? "") === String(f.value ?? "");
264
+ case "startsWith":
265
+ return String(cell ?? "").toLowerCase().startsWith(String(f.value ?? "").toLowerCase());
266
+ case "eq":
267
+ return asNum(cell) === asNum(f.value);
268
+ case "lt":
269
+ return asNum(cell) < asNum(f.value);
270
+ case "gt":
271
+ return asNum(cell) > asNum(f.value);
272
+ case "between": {
273
+ const [lo, hi] = f.value.map(asNum);
274
+ return asNum(cell) >= lo && asNum(cell) <= hi;
275
+ }
276
+ case "is":
277
+ return Boolean(cell) === Boolean(f.value);
278
+ default:
279
+ return true;
280
+ }
281
+ }
282
+ function applyFilters(rows, filters) {
283
+ if (!filters.length) return rows.slice();
284
+ return rows.filter((r) => filters.every((f) => matchFilter(r, f)));
285
+ }
286
+ function searchRows(rows, query, fields) {
287
+ const q = query.trim().toLowerCase();
288
+ if (!q) return rows.slice();
289
+ return rows.filter((r) => {
290
+ const vals = fields ? fields.map((f) => r[f]) : Object.values(r);
291
+ return vals.some((v) => v != null && String(v).toLowerCase().includes(q));
292
+ });
293
+ }
294
+ function highlightSegments(text, query) {
295
+ const q = query.trim();
296
+ if (!q) return [{ text, match: false }];
297
+ const out = [];
298
+ const lower = text.toLowerCase();
299
+ const ql = q.toLowerCase();
300
+ let i = 0;
301
+ for (; ; ) {
302
+ const hit = lower.indexOf(ql, i);
303
+ if (hit === -1) {
304
+ if (i < text.length) out.push({ text: text.slice(i), match: false });
305
+ break;
306
+ }
307
+ if (hit > i) out.push({ text: text.slice(i, hit), match: false });
308
+ out.push({ text: text.slice(hit, hit + q.length), match: true });
309
+ i = hit + q.length;
310
+ }
311
+ return out;
312
+ }
313
+ var ARRAY_OPS = /* @__PURE__ */ new Set(["isAnyOf", "isNoneOf", "between"]);
314
+ var escapePart = (s) => encodeURIComponent(s).replace(/~/g, "%7E");
315
+ var unescapePart = (s) => decodeURIComponent(s);
316
+ var scalarToStr = (v) => v == null ? "" : String(v);
317
+ function parseScalar(s, kind) {
318
+ switch (kind) {
319
+ case "number":
320
+ return Number(s);
321
+ case "boolean":
322
+ return s === "true";
323
+ case "date":
324
+ return /^-?\d+$/.test(s) ? Number(s) : s;
325
+ // epoch-ms number, else the ISO/date string
326
+ default:
327
+ return s;
328
+ }
329
+ }
330
+ function kindFromOp(op) {
331
+ if (op === "isAnyOf" || op === "isNoneOf") return "enum";
332
+ if (op === "is") return "boolean";
333
+ if (op === "contains" || op === "equals" || op === "startsWith") return "text";
334
+ return "number";
335
+ }
336
+ function serializeFilter(f) {
337
+ const value = Array.isArray(f.value) ? f.value.map((x) => escapePart(scalarToStr(x))).join("|") : escapePart(scalarToStr(f.value));
338
+ return `${escapePart(f.field)}~${f.kind}~${f.op}~${value}`;
339
+ }
340
+ function parseFilter(tok) {
341
+ const parts = tok.split("~");
342
+ let field, kind, op, raw;
343
+ if (parts.length >= 4) {
344
+ [field, kind, op, raw] = [unescapePart(parts[0]), parts[1], parts[2], parts[3] ?? ""];
345
+ } else {
346
+ [field, op, raw] = [unescapePart(parts[0]), parts[1], parts[2] ?? ""];
347
+ kind = kindFromOp(op);
348
+ }
349
+ if (!field || !op) return null;
350
+ const value = ARRAY_OPS.has(op) ? raw.split("|").filter((s) => s !== "").map((s) => parseScalar(unescapePart(s), kind)) : parseScalar(unescapePart(raw), kind);
351
+ return { field, kind, op, value };
352
+ }
353
+ function serializeView(v) {
354
+ const p = new URLSearchParams();
355
+ if (v.sort?.length) p.set("sort", v.sort.map((s) => `${s.field}:${s.dir === "descending" ? "d" : "a"}`).join(","));
356
+ if (v.columns?.length) p.set("cols", v.columns.join(","));
357
+ if (v.density && v.density !== "standard") p.set("density", v.density);
358
+ if (v.q) p.set("q", v.q);
359
+ if (v.filters?.length) p.set("filter", v.filters.map(serializeFilter).join(","));
360
+ return p.toString();
361
+ }
362
+ function parseView(qs) {
363
+ const p = new URLSearchParams(qs.replace(/^\?/, ""));
364
+ const v = {};
365
+ const sort = p.get("sort");
366
+ if (sort) v.sort = sort.split(",").filter(Boolean).map((tok) => {
367
+ const [field, d] = tok.split(":");
368
+ return { field, dir: d === "d" ? "descending" : "ascending" };
369
+ });
370
+ const cols = p.get("cols");
371
+ if (cols) v.columns = cols.split(",").filter(Boolean);
372
+ const density = p.get("density");
373
+ if (density === "compact" || density === "comfortable" || density === "standard") v.density = density;
374
+ const q = p.get("q");
375
+ if (q) v.q = q;
376
+ const filter = p.get("filter");
377
+ if (filter) {
378
+ v.filters = filter.split(",").filter(Boolean).map(parseFilter).filter((f) => f !== null);
379
+ }
380
+ return v;
381
+ }
382
+ function virtualWindow(scrollTop, viewportH, rowH, count, overscan = 6) {
383
+ if (rowH <= 0 || count <= 0) return { start: 0, end: count, padTop: 0, padBottom: 0, total: 0 };
384
+ const first = Math.max(0, Math.floor(scrollTop / rowH) - overscan);
385
+ const visible = Math.ceil(viewportH / rowH) + overscan * 2;
386
+ const end = Math.min(count, first + visible);
387
+ return { start: first, end, padTop: first * rowH, padBottom: (count - end) * rowH, total: count * rowH };
388
+ }
389
+ function shouldVirtualize(count, threshold = 100) {
390
+ return count > threshold;
391
+ }
392
+ function clampWidth(width, min = 64, max = Infinity) {
393
+ return Math.max(min, Math.min(max, Math.round(width)));
394
+ }
395
+ function reorder(arr, from, to) {
396
+ const next = arr.slice();
397
+ if (from < 0 || from >= next.length || to < 0 || to >= next.length) return next;
398
+ const [moved] = next.splice(from, 1);
399
+ next.splice(to, 0, moved);
400
+ return next;
401
+ }
402
+ function toggleId(set, id) {
403
+ const next = new Set(set);
404
+ next.has(id) ? next.delete(id) : next.add(id);
405
+ return next;
406
+ }
407
+ function selectAllState(selected, pageIds) {
408
+ const on = pageIds.filter((id) => selected.has(id)).length;
409
+ if (on === 0) return "none";
410
+ return on === pageIds.length ? "all" : "some";
411
+ }
412
+ function togglePage(selected, pageIds) {
413
+ const next = new Set(selected);
414
+ const all = pageIds.every((id) => next.has(id));
415
+ for (const id of pageIds) all ? next.delete(id) : next.add(id);
416
+ return next;
417
+ }
418
+ function mergePinned(all, visible, pinnedIds, idField = "id") {
419
+ if (!pinnedIds.size) return visible.slice();
420
+ const pinned = all.filter((r) => pinnedIds.has(String(r[idField])));
421
+ const pinnedSet = new Set(pinned.map((r) => String(r[idField])));
422
+ const rest = visible.filter((r) => !pinnedSet.has(String(r[idField])));
423
+ return [...pinned, ...rest];
424
+ }
425
+
426
+ // src/hooks/useTable.ts
427
+ function useTable(data, options = {}) {
428
+ const { idField = "id", searchFields } = options;
429
+ const [sort, setSort] = (0, import_react2.useState)(() => options.initialSort ?? []);
430
+ const [filters, setFilters] = (0, import_react2.useState)(() => options.initialFilters ?? []);
431
+ const [query, setQuery] = (0, import_react2.useState)(() => options.initialQuery ?? "");
432
+ const [selected, setSelected] = (0, import_react2.useState)(() => /* @__PURE__ */ new Set());
433
+ const [pinned, setPinned] = (0, import_react2.useState)(() => new Set(options.initialPinned ?? []));
434
+ const rows = (0, import_react2.useMemo)(() => {
435
+ const filtered = applyFilters(data, filters);
436
+ const searched = searchRows(filtered, query, searchFields);
437
+ const sorted = multiSort(searched, sort);
438
+ return mergePinned(data, sorted, pinned, idField);
439
+ }, [data, filters, query, searchFields, sort, pinned, idField]);
440
+ const pageIds = (0, import_react2.useMemo)(() => rows.map((r) => String(r[idField])), [rows, idField]);
441
+ const selectAll = selectAllState(selected, pageIds);
442
+ const doToggleSort = (0, import_react2.useCallback)((field, additive = false) => {
443
+ setSort((keys) => toggleSort(keys, field, additive));
444
+ }, []);
445
+ const toggleRow = (0, import_react2.useCallback)((id) => setSelected((s) => toggleId(s, id)), []);
446
+ const toggleAllRows = (0, import_react2.useCallback)(() => setSelected((s) => togglePage(s, pageIds)), [pageIds]);
447
+ const clearSelection = (0, import_react2.useCallback)(() => setSelected(/* @__PURE__ */ new Set()), []);
448
+ const togglePin = (0, import_react2.useCallback)((id) => setPinned((s) => toggleId(s, id)), []);
449
+ const view = (0, import_react2.useMemo)(() => ({ sort, filters, q: query }), [sort, filters, query]);
450
+ const viewQueryString = (0, import_react2.useMemo)(() => serializeView(view), [view]);
451
+ const applyView = (0, import_react2.useCallback)((queryString) => {
452
+ const v = parseView(queryString);
453
+ setSort(v.sort ?? []);
454
+ setFilters(v.filters ?? []);
455
+ setQuery(v.q ?? "");
456
+ }, []);
457
+ return {
458
+ rows,
459
+ sort,
460
+ filters,
461
+ query,
462
+ selected,
463
+ pinned,
464
+ selectAll,
465
+ toggleSort: doToggleSort,
466
+ setSort,
467
+ setFilters,
468
+ setQuery,
469
+ isSelected: (id) => selected.has(id),
470
+ toggleRow,
471
+ toggleAllRows,
472
+ clearSelection,
473
+ togglePin,
474
+ view,
475
+ viewQueryString,
476
+ applyView
477
+ };
478
+ }
479
+
480
+ // src/hooks/useAnchoredPosition.ts
481
+ var import_react3 = require("react");
482
+
483
+ // src/overlay-position.ts
484
+ var OPPOSITE = { top: "bottom", bottom: "top", left: "right", right: "left" };
485
+ function parsePlacement(p) {
486
+ const [side, align] = p.split("-");
487
+ return { side, align: align ?? "center" };
488
+ }
489
+ var clamp = (v, min, max) => Math.max(min, Math.min(max, v));
490
+ function spaceOn(side, a, vp, offset) {
491
+ switch (side) {
492
+ case "top":
493
+ return a.y - offset;
494
+ case "bottom":
495
+ return vp.height - (a.y + a.height) - offset;
496
+ case "left":
497
+ return a.x - offset;
498
+ case "right":
499
+ return vp.width - (a.x + a.width) - offset;
500
+ }
501
+ }
502
+ function fitsOn(side, a, f, vp, offset, padding) {
503
+ const need = side === "top" || side === "bottom" ? f.height : f.width;
504
+ return spaceOn(side, a, vp, offset) >= need + padding;
505
+ }
506
+ function computePosition(anchor, floating, viewport, options = {}) {
507
+ const { placement = "bottom-start", offset = 6, padding = 8, flip = true, shift = true } = options;
508
+ const { side: preferred, align } = parsePlacement(placement);
509
+ let side = preferred;
510
+ if (flip && !fitsOn(side, anchor, floating, viewport, offset, padding)) {
511
+ const opp = OPPOSITE[side];
512
+ if (fitsOn(opp, anchor, floating, viewport, offset, padding) || spaceOn(opp, anchor, viewport, offset) > spaceOn(side, anchor, viewport, offset)) {
513
+ side = opp;
514
+ }
515
+ }
516
+ let x, y;
517
+ const horizontal = side === "top" || side === "bottom";
518
+ if (side === "top") y = anchor.y - floating.height - offset;
519
+ else if (side === "bottom") y = anchor.y + anchor.height + offset;
520
+ else if (side === "left") x = anchor.x - floating.width - offset;
521
+ else x = anchor.x + anchor.width + offset;
522
+ if (horizontal) {
523
+ const start = anchor.x;
524
+ const end = anchor.x + anchor.width - floating.width;
525
+ const center = anchor.x + (anchor.width - floating.width) / 2;
526
+ x = align === "start" ? start : align === "end" ? end : center;
527
+ if (shift) x = clamp(x, padding, Math.max(padding, viewport.width - floating.width - padding));
528
+ } else {
529
+ const start = anchor.y;
530
+ const end = anchor.y + anchor.height - floating.height;
531
+ const center = anchor.y + (anchor.height - floating.height) / 2;
532
+ y = align === "start" ? start : align === "end" ? end : center;
533
+ if (shift) y = clamp(y, padding, Math.max(padding, viewport.height - floating.height - padding));
534
+ }
535
+ return {
536
+ x,
537
+ y,
538
+ placement: align === "center" ? side : `${side}-${align}`,
539
+ side,
540
+ align
541
+ };
542
+ }
543
+
544
+ // src/hooks/useAnchoredPosition.ts
545
+ var useIsomorphicLayoutEffect = typeof document !== "undefined" ? import_react3.useLayoutEffect : import_react3.useEffect;
546
+ var resolve = (a) => a && typeof a === "object" && "current" in a ? a.current : a ?? null;
547
+ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-start", offset = 6, padding = 8 }) {
548
+ const opts = (0, import_react3.useRef)({ placement, offset, padding });
549
+ opts.current = { placement, offset, padding };
550
+ const reposition = (0, import_react3.useCallback)(() => {
551
+ const anchorEl = resolve(anchor);
552
+ const floating = floatingRef.current;
553
+ if (!anchorEl || !floating) return;
554
+ const s = floating.style;
555
+ s.position = "fixed";
556
+ s.inset = "auto";
557
+ s.margin = "0";
558
+ const a = anchorEl.getBoundingClientRect();
559
+ const { placement: p, offset: o, padding: pad } = opts.current;
560
+ const pos = computePosition(
561
+ { x: a.x, y: a.y, width: a.width, height: a.height },
562
+ { width: floating.offsetWidth, height: floating.offsetHeight },
563
+ { width: window.innerWidth, height: window.innerHeight },
564
+ { placement: p, offset: o, padding: pad }
565
+ );
566
+ s.left = `${Math.round(pos.x)}px`;
567
+ s.top = `${Math.round(pos.y)}px`;
568
+ floating.dataset.placement = pos.placement;
569
+ }, [anchor, floatingRef]);
570
+ useIsomorphicLayoutEffect(() => {
571
+ if (!open) return;
572
+ reposition();
573
+ const onScroll = () => reposition();
574
+ window.addEventListener("scroll", onScroll, { passive: true, capture: true });
575
+ window.addEventListener("resize", onScroll);
576
+ return () => {
577
+ window.removeEventListener("scroll", onScroll, true);
578
+ window.removeEventListener("resize", onScroll);
579
+ };
580
+ }, [open, reposition]);
581
+ return reposition;
582
+ }
583
+
584
+ // src/components/Button.tsx
585
+ var import_react4 = require("react");
126
586
  var import_jsx_runtime = require("react/jsx-runtime");
127
- var Button = (0, import_react2.forwardRef)(
587
+ var Button = (0, import_react4.forwardRef)(
128
588
  ({ variant = "secondary", size = "md", icon = false, loading = false, className, children, disabled, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
129
589
  "button",
130
590
  {
@@ -149,9 +609,9 @@ function ButtonGroup({ children, className }) {
149
609
  }
150
610
 
151
611
  // src/components/Input.tsx
152
- var import_react3 = require("react");
612
+ var import_react5 = require("react");
153
613
  var import_jsx_runtime2 = require("react/jsx-runtime");
154
- var Input = (0, import_react3.forwardRef)(
614
+ var Input = (0, import_react5.forwardRef)(
155
615
  ({ size = "md", invalid, valid, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
156
616
  "input",
157
617
  {
@@ -184,9 +644,9 @@ function InputGroup({ children, leadingIcon, trailingIcon, className }) {
184
644
  }
185
645
 
186
646
  // src/components/Textarea.tsx
187
- var import_react4 = require("react");
647
+ var import_react6 = require("react");
188
648
  var import_jsx_runtime3 = require("react/jsx-runtime");
189
- var Textarea = (0, import_react4.forwardRef)(
649
+ var Textarea = (0, import_react6.forwardRef)(
190
650
  ({ invalid, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
191
651
  "textarea",
192
652
  {
@@ -200,9 +660,9 @@ var Textarea = (0, import_react4.forwardRef)(
200
660
  Textarea.displayName = "Textarea";
201
661
 
202
662
  // src/components/Select.tsx
203
- var import_react5 = require("react");
663
+ var import_react7 = require("react");
204
664
  var import_jsx_runtime4 = require("react/jsx-runtime");
205
- var Select = (0, import_react5.forwardRef)(
665
+ var Select = (0, import_react7.forwardRef)(
206
666
  ({ size = "md", className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
207
667
  "select",
208
668
  {
@@ -215,14 +675,14 @@ var Select = (0, import_react5.forwardRef)(
215
675
  Select.displayName = "Select";
216
676
 
217
677
  // src/components/Checkbox.tsx
218
- var import_react6 = require("react");
678
+ var import_react8 = require("react");
219
679
  var import_jsx_runtime5 = require("react/jsx-runtime");
220
680
  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" }) });
221
681
  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" }) });
222
- var Checkbox = (0, import_react6.forwardRef)(
682
+ var Checkbox = (0, import_react8.forwardRef)(
223
683
  ({ label, indeterminate, className, ...props }, forwardedRef) => {
224
- const innerRef = (0, import_react6.useRef)(null);
225
- (0, import_react6.useEffect)(() => {
684
+ const innerRef = (0, import_react8.useRef)(null);
685
+ (0, import_react8.useEffect)(() => {
226
686
  const el = innerRef.current;
227
687
  if (el) el.indeterminate = !!indeterminate;
228
688
  }, [indeterminate]);
@@ -247,9 +707,9 @@ var Checkbox = (0, import_react6.forwardRef)(
247
707
  Checkbox.displayName = "Checkbox";
248
708
 
249
709
  // src/components/Radio.tsx
250
- var import_react7 = require("react");
710
+ var import_react9 = require("react");
251
711
  var import_jsx_runtime6 = require("react/jsx-runtime");
252
- var Radio = (0, import_react7.forwardRef)(
712
+ var Radio = (0, import_react9.forwardRef)(
253
713
  ({ label, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: cx("uix-radio", className), children: [
254
714
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "radio", ref, ...props }),
255
715
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "uix-radio__dot" }),
@@ -262,9 +722,9 @@ function RadioGroup({ children, className }) {
262
722
  }
263
723
 
264
724
  // src/components/Switch.tsx
265
- var import_react8 = require("react");
725
+ var import_react10 = require("react");
266
726
  var import_jsx_runtime7 = require("react/jsx-runtime");
267
- var Switch = (0, import_react8.forwardRef)(
727
+ var Switch = (0, import_react10.forwardRef)(
268
728
  ({ label, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: cx("uix-switch", className), children: [
269
729
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", role: "switch", ref, ...props }),
270
730
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "uix-switch__track" }),
@@ -274,14 +734,20 @@ var Switch = (0, import_react8.forwardRef)(
274
734
  Switch.displayName = "Switch";
275
735
 
276
736
  // src/components/Field.tsx
737
+ var import_react11 = require("react");
277
738
  var import_jsx_runtime8 = require("react/jsx-runtime");
278
739
  function Field({ label, hint, error, success, required, children, className, htmlFor }) {
740
+ const uid = (0, import_react11.useId)();
741
+ const msgId = `${uid}-msg`;
742
+ const describedBy = error || success || hint ? msgId : void 0;
743
+ const control = (0, import_react11.isValidElement)(children) ? (0, import_react11.cloneElement)(children, {
744
+ "aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0,
745
+ "aria-invalid": error ? true : children.props["aria-invalid"]
746
+ }) : children;
279
747
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: cx("uix-field", className), children: [
280
748
  label && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "uix-field__label", htmlFor, "data-required": required || void 0, children: label }),
281
- children,
282
- error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__error", children: error }),
283
- success && !error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__success", children: success }),
284
- hint && !error && !success && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-field__hint", children: hint })
749
+ control,
750
+ /* @__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 })
285
751
  ] });
286
752
  }
287
753
 
@@ -411,11 +877,11 @@ function ListItem({ title, meta, trail, leading, children, className, ...props }
411
877
  }
412
878
 
413
879
  // src/components/DescriptionList.tsx
414
- var import_react9 = require("react");
880
+ var import_react12 = require("react");
415
881
  var import_jsx_runtime16 = require("react/jsx-runtime");
416
882
  function DescriptionList({ items, children, className, ...props }) {
417
883
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("dl", { className: cx("uix-dl", className), ...props, children: [
418
- items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react9.Fragment, { children: [
884
+ items?.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react12.Fragment, { children: [
419
885
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dt", { children: item.term }),
420
886
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dd", { children: item.description })
421
887
  ] }, item.id ?? i)),
@@ -430,14 +896,14 @@ function DescriptionItem({ term, children }) {
430
896
  }
431
897
 
432
898
  // src/components/Tabs.tsx
433
- var import_react10 = require("react");
899
+ var import_react13 = require("react");
434
900
  var import_jsx_runtime17 = require("react/jsx-runtime");
435
- var TabsCtx = (0, import_react10.createContext)({});
901
+ var TabsCtx = (0, import_react13.createContext)({});
436
902
  function Tabs({ variant = "line", value, onChange, children, className }) {
437
903
  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 }) });
438
904
  }
439
905
  function Tab({ value, children, disabled, className, onClick, ...props }) {
440
- const ctx = (0, import_react10.useContext)(TabsCtx);
906
+ const ctx = (0, import_react13.useContext)(TabsCtx);
441
907
  const selected = ctx.value === value;
442
908
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
443
909
  "button",
@@ -457,17 +923,49 @@ function Tab({ value, children, disabled, className, onClick, ...props }) {
457
923
  }
458
924
 
459
925
  // src/components/AppShell.tsx
926
+ var import_react14 = require("react");
460
927
  var import_jsx_runtime18 = require("react/jsx-runtime");
461
- function AppShell({ collapsed, sidebar, topbar, children, className, ...props }) {
462
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: cx("uix-shell", className), "data-collapsed": collapsed || void 0, ...props, children: [
463
- sidebar && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "uix-shell__sidebar", children: sidebar }),
464
- topbar && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "uix-shell__topbar", children: topbar }),
465
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("main", { className: "uix-shell__main", children })
466
- ] });
928
+ function AppShell({
929
+ nav,
930
+ collapsed,
931
+ focus,
932
+ onExitFocus,
933
+ mainBleed,
934
+ sidebar,
935
+ topbar,
936
+ children,
937
+ className,
938
+ ...props
939
+ }) {
940
+ const navState = nav ?? (collapsed ? "rail" : "full");
941
+ (0, import_react14.useEffect)(() => {
942
+ if (!focus || !onExitFocus) return;
943
+ const onKey = (e) => {
944
+ if (e.key === "Escape") onExitFocus();
945
+ };
946
+ document.addEventListener("keydown", onKey);
947
+ return () => document.removeEventListener("keydown", onKey);
948
+ }, [focus, onExitFocus]);
949
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
950
+ "div",
951
+ {
952
+ className: cx("uix-shell", className),
953
+ "data-nav": navState !== "full" ? navState : void 0,
954
+ "data-collapsed": collapsed && nav == null ? "" : void 0,
955
+ "data-focus": focus || void 0,
956
+ ...props,
957
+ children: [
958
+ sidebar && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "uix-shell__sidebar", children: sidebar }),
959
+ topbar && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "uix-shell__topbar", children: topbar }),
960
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("main", { className: cx("uix-shell__main", mainBleed && "uix-shell__main--bleed"), children }),
961
+ 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" }) })
962
+ ]
963
+ }
964
+ );
467
965
  }
468
966
 
469
967
  // src/components/Sidebar.tsx
470
- var import_react11 = require("react");
968
+ var import_react15 = require("react");
471
969
  var import_jsx_runtime19 = require("react/jsx-runtime");
472
970
  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" }) });
473
971
  function Sidebar({ collapsed, onToggle, brand, toggleIcon, children, className, ...props }) {
@@ -521,7 +1019,7 @@ function NavItem({ icon, badge, active, as: Tag = "a", children, className, ...p
521
1019
  );
522
1020
  }
523
1021
  function NavGroup({ icon, label, children, defaultExpanded = true }) {
524
- const [expanded, setExpanded] = (0, import_react11.useState)(defaultExpanded);
1022
+ const [expanded, setExpanded] = (0, import_react15.useState)(defaultExpanded);
525
1023
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "uix-navgroup", children: [
526
1024
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
527
1025
  "button",
@@ -589,7 +1087,7 @@ function StarButton({
589
1087
  }
590
1088
 
591
1089
  // src/components/NavFavourites.tsx
592
- var import_react12 = require("react");
1090
+ var import_react16 = require("react");
593
1091
  var import_jsx_runtime21 = require("react/jsx-runtime");
594
1092
  var ChevronDownIcon = () => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
595
1093
  "svg",
@@ -653,13 +1151,13 @@ function NavFavourites({
653
1151
  emptyReason,
654
1152
  className
655
1153
  }) {
656
- const regionId = (0, import_react12.useId)();
657
- const headerRef = (0, import_react12.useRef)(null);
658
- const regionRef = (0, import_react12.useRef)(null);
659
- const focusAfterRef = (0, import_react12.useRef)(null);
660
- const triggerRefs = (0, import_react12.useRef)(/* @__PURE__ */ new Map());
1154
+ const regionId = (0, import_react16.useId)();
1155
+ const headerRef = (0, import_react16.useRef)(null);
1156
+ const regionRef = (0, import_react16.useRef)(null);
1157
+ const focusAfterRef = (0, import_react16.useRef)(null);
1158
+ const triggerRefs = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
661
1159
  const order = items.map((i) => i.id);
662
- (0, import_react12.useEffect)(() => {
1160
+ (0, import_react16.useEffect)(() => {
663
1161
  const id = focusAfterRef.current;
664
1162
  if (!id) return;
665
1163
  focusAfterRef.current = null;
@@ -770,18 +1268,18 @@ function FavouriteRow({
770
1268
  onRemove,
771
1269
  registerTrigger
772
1270
  }) {
773
- const [open, setOpen] = (0, import_react12.useState)(false);
774
- const [revealed, setRevealed] = (0, import_react12.useState)(false);
775
- const menuId = (0, import_react12.useId)();
776
- const triggerRef = (0, import_react12.useRef)(null);
777
- const itemRefs = (0, import_react12.useRef)([]);
1271
+ const [open, setOpen] = (0, import_react16.useState)(false);
1272
+ const [revealed, setRevealed] = (0, import_react16.useState)(false);
1273
+ const menuId = (0, import_react16.useId)();
1274
+ const triggerRef = (0, import_react16.useRef)(null);
1275
+ const itemRefs = (0, import_react16.useRef)([]);
778
1276
  const optionsTemplate = labels.options ?? "{label} favourite options";
779
1277
  const actions = [
780
1278
  ...isFirst ? [] : [{ label: labels.moveUp, onSelect: onMoveUp }],
781
1279
  ...isLast ? [] : [{ label: labels.moveDown, onSelect: onMoveDown }],
782
1280
  { label: labels.remove.replace("{label}", item.label), onSelect: onRemove }
783
1281
  ];
784
- (0, import_react12.useEffect)(() => {
1282
+ (0, import_react16.useEffect)(() => {
785
1283
  if (open) itemRefs.current[0]?.focus();
786
1284
  }, [open]);
787
1285
  const closeAndReturnFocus = () => {
@@ -966,16 +1464,17 @@ function Spinner({ size = "md", accent, className, ...props }) {
966
1464
  // src/components/Toast.tsx
967
1465
  var import_jsx_runtime24 = require("react/jsx-runtime");
968
1466
  function Toast({ title, message, tone, icon, onClose, leaving, className, ...props }) {
1467
+ const assertive = tone === "danger";
969
1468
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
970
1469
  "div",
971
1470
  {
972
1471
  className: cx("uix-toast", tone && `uix-toast--${tone}`, className),
973
1472
  "data-leaving": leaving || void 0,
974
- role: "status",
975
- "aria-live": "polite",
1473
+ role: assertive ? "alert" : "status",
1474
+ "aria-live": assertive ? "assertive" : "polite",
976
1475
  ...props,
977
1476
  children: [
978
- icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__icon", children: icon }),
1477
+ icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__icon", "aria-hidden": "true", children: icon }),
979
1478
  /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "uix-toast__body", children: [
980
1479
  title && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__title", children: title }),
981
1480
  message && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "uix-toast__msg", children: message })
@@ -986,7 +1485,7 @@ function Toast({ title, message, tone, icon, onClose, leaving, className, ...pro
986
1485
  );
987
1486
  }
988
1487
  function Toaster({ children, className, ...props }) {
989
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cx("uix-toaster", className), "aria-live": "polite", "aria-atomic": "false", ...props, children });
1488
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cx("uix-toaster", className), ...props, children });
990
1489
  }
991
1490
 
992
1491
  // src/components/Table.tsx
@@ -998,6 +1497,7 @@ function Table({
998
1497
  density = "standard",
999
1498
  zebra = true,
1000
1499
  pinnedCol,
1500
+ fixed,
1001
1501
  rowPeek,
1002
1502
  v2,
1003
1503
  className,
@@ -1007,7 +1507,13 @@ function Table({
1007
1507
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1008
1508
  "table",
1009
1509
  {
1010
- className: cx("uix-table", !zebra && "uix-table--no-zebra", pinnedCol && "uix-table--pinned-col", className),
1510
+ className: cx(
1511
+ "uix-table",
1512
+ !zebra && "uix-table--no-zebra",
1513
+ pinnedCol && "uix-table--pinned-col",
1514
+ fixed && "uix-table--fixed",
1515
+ className
1516
+ ),
1011
1517
  "data-density": density !== "standard" ? density : void 0,
1012
1518
  "data-uix-rowpeek": rowPeek || void 0,
1013
1519
  "data-uix-table-v2": v2 || void 0,
@@ -1016,12 +1522,13 @@ function Table({
1016
1522
  }
1017
1523
  );
1018
1524
  }
1019
- function Th({ sortable, sortDirection, onSort, className, children, onClick, ...props }) {
1525
+ function Th({ sortable, sortDirection, sortOrder, onSort, className, children, onClick, ...props }) {
1020
1526
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1021
1527
  "th",
1022
1528
  {
1023
1529
  className,
1024
1530
  "data-sort": sortable || void 0,
1531
+ "data-sort-order": sortOrder != null && sortOrder > 0 ? sortOrder : void 0,
1025
1532
  "aria-sort": sortDirection && sortDirection !== "none" ? sortDirection : void 0,
1026
1533
  onClick: sortable ? onSort ?? onClick : onClick,
1027
1534
  ...props,
@@ -1044,6 +1551,49 @@ function Tr({ selected, pinned, className, children, ...props }) {
1044
1551
  }
1045
1552
  );
1046
1553
  }
1554
+ function BulkBar({ count, children, className, ...props }) {
1555
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: cx("uix-bulkbar", className), role: "toolbar", "aria-label": "Bulk actions", ...props, children: [
1556
+ count != null && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "uix-bulkbar__count", children: [
1557
+ count,
1558
+ " selected"
1559
+ ] }),
1560
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "uix-bulkbar__spacer" }),
1561
+ children
1562
+ ] });
1563
+ }
1564
+ function RowActions({ children, className, ...props }) {
1565
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("td", { className: cx("uix-table__actions", className), ...props, children });
1566
+ }
1567
+ function RowAction({ children, className, type = "button", ...props }) {
1568
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type, className: cx("uix-rowact", className), ...props, children });
1569
+ }
1570
+ function ExpandToggle({ expanded, className, type = "button", ...props }) {
1571
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1572
+ "button",
1573
+ {
1574
+ type,
1575
+ className: cx("uix-table__expand", className),
1576
+ "aria-expanded": expanded,
1577
+ "aria-label": expanded ? "Collapse row" : "Expand row",
1578
+ ...props,
1579
+ 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" }) })
1580
+ }
1581
+ );
1582
+ }
1583
+ function CellStrong({ children, className, ...props }) {
1584
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: cx("uix-cell-strong", className), ...props, children });
1585
+ }
1586
+ function CellSub({ children, className, ...props }) {
1587
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: cx("uix-cell-sub", className), ...props, children });
1588
+ }
1589
+ function Mark({ children, className, ...props }) {
1590
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("mark", { className: cx("uix-mark", className), ...props, children });
1591
+ }
1592
+ function Highlighted({ text, query }) {
1593
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children: highlightSegments(text, query).map(
1594
+ (seg, i) => seg.match ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("mark", { className: "uix-mark", children: seg.text }, i) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: seg.text }, i)
1595
+ ) });
1596
+ }
1047
1597
 
1048
1598
  // src/components/Pagination.tsx
1049
1599
  var import_jsx_runtime26 = require("react/jsx-runtime");
@@ -1172,9 +1722,64 @@ function Stat({ label, value, icon, meta, trend, className, ...props }) {
1172
1722
  }
1173
1723
 
1174
1724
  // src/components/Tooltip.tsx
1725
+ var import_react17 = require("react");
1175
1726
  var import_jsx_runtime31 = require("react/jsx-runtime");
1176
- function Tooltip({ label, children, ...props }) {
1177
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { "data-uix-tip": label, ...props, children });
1727
+ function Tooltip({ label, placement = "top", className, children, ...props }) {
1728
+ const triggerRef = (0, import_react17.useRef)(null);
1729
+ const bubbleRef = (0, import_react17.useRef)(null);
1730
+ const [open, setOpen] = (0, import_react17.useState)(false);
1731
+ const id = (0, import_react17.useId)();
1732
+ useAnchoredPosition(triggerRef, bubbleRef, { open, placement, offset: 6 });
1733
+ const show = () => {
1734
+ const b = bubbleRef.current;
1735
+ if (b && !b.matches(":popover-open")) {
1736
+ try {
1737
+ b.showPopover();
1738
+ } catch {
1739
+ }
1740
+ }
1741
+ setOpen(true);
1742
+ };
1743
+ const hide = () => {
1744
+ const b = bubbleRef.current;
1745
+ if (b && b.matches(":popover-open")) {
1746
+ try {
1747
+ b.hidePopover();
1748
+ } catch {
1749
+ }
1750
+ }
1751
+ setOpen(false);
1752
+ };
1753
+ (0, import_react17.useEffect)(() => () => {
1754
+ const b = bubbleRef.current;
1755
+ if (b && b.matches(":popover-open")) {
1756
+ try {
1757
+ b.hidePopover();
1758
+ } catch {
1759
+ }
1760
+ }
1761
+ }, []);
1762
+ const bubbleAttr = { popover: "manual" };
1763
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
1764
+ "span",
1765
+ {
1766
+ ref: triggerRef,
1767
+ className: cx("uix-tooltip", className),
1768
+ "aria-describedby": id,
1769
+ onMouseEnter: show,
1770
+ onMouseLeave: hide,
1771
+ onFocus: show,
1772
+ onBlur: hide,
1773
+ onKeyDown: (e) => {
1774
+ if (e.key === "Escape") hide();
1775
+ },
1776
+ ...props,
1777
+ children: [
1778
+ children,
1779
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { ref: bubbleRef, id, role: "tooltip", className: "uix-tooltip__bubble", ...bubbleAttr, children: label })
1780
+ ]
1781
+ }
1782
+ );
1178
1783
  }
1179
1784
 
1180
1785
  // src/components/States.tsx
@@ -1266,72 +1871,140 @@ function Comment({ avatar, author, meta, children, className, ...props }) {
1266
1871
  ] });
1267
1872
  }
1268
1873
 
1269
- // src/components/Timeline.tsx
1874
+ // src/components/Composer.tsx
1270
1875
  var import_jsx_runtime36 = require("react/jsx-runtime");
1876
+ function Composer({ children, className, ...props }) {
1877
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: cx("uix-composer", className), ...props, children });
1878
+ }
1879
+ function ComposerBar({ children, className, ...props }) {
1880
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: cx("uix-composer__bar", className), ...props, children });
1881
+ }
1882
+
1883
+ // src/components/Segmented.tsx
1884
+ var import_react18 = require("react");
1885
+ var import_jsx_runtime37 = require("react/jsx-runtime");
1886
+ var SegmentedCtx = (0, import_react18.createContext)({});
1887
+ function Segmented({ value, onChange, children, className, ...props }) {
1888
+ 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 }) });
1889
+ }
1890
+ function SegmentedOption({
1891
+ value,
1892
+ children,
1893
+ className,
1894
+ onClick,
1895
+ ...props
1896
+ }) {
1897
+ const ctx = (0, import_react18.useContext)(SegmentedCtx);
1898
+ const selected = ctx.value === value;
1899
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1900
+ "button",
1901
+ {
1902
+ type: "button",
1903
+ className: cx("uix-segmented__option", className),
1904
+ "aria-pressed": selected,
1905
+ onClick: (e) => {
1906
+ ctx.onChange?.(value);
1907
+ onClick?.(e);
1908
+ },
1909
+ ...props,
1910
+ children
1911
+ }
1912
+ );
1913
+ }
1914
+
1915
+ // src/components/Timeline.tsx
1916
+ var import_jsx_runtime38 = require("react/jsx-runtime");
1271
1917
  function Timeline({ children, className, ...props }) {
1272
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: cx("uix-timeline", className), ...props, children });
1918
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: cx("uix-timeline", className), ...props, children });
1273
1919
  }
1274
- function TimelineItem({ meta, children, className, ...props }) {
1275
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: cx("uix-timeline__item", className), ...props, children: [
1276
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "uix-timeline__rail", children: [
1277
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "uix-timeline__node" }),
1278
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "uix-timeline__line" })
1920
+ function TimelineItem({ node, meta, children, className, ...props }) {
1921
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: cx("uix-timeline__item", className), ...props, children: [
1922
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "uix-timeline__rail", children: [
1923
+ node ?? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "uix-timeline__node" }),
1924
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "uix-timeline__line" })
1279
1925
  ] }),
1280
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "uix-timeline__body", children: [
1281
- meta != null && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "uix-timeline__meta", children: meta }),
1926
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "uix-timeline__body", children: [
1927
+ meta != null && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "uix-timeline__meta", children: meta }),
1282
1928
  children
1283
1929
  ] })
1284
1930
  ] });
1285
1931
  }
1286
1932
 
1287
1933
  // src/components/Prose.tsx
1288
- var import_jsx_runtime37 = require("react/jsx-runtime");
1934
+ var import_jsx_runtime39 = require("react/jsx-runtime");
1289
1935
  function Prose({ children, className, ...props }) {
1290
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: cx("uix-prose", className), ...props, children });
1936
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: cx("uix-prose", className), ...props, children });
1291
1937
  }
1292
1938
  function Note({ tone, icon, children, className, ...props }) {
1293
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: cx("uix-note", tone && `uix-note--${tone}`, className), ...props, children: [
1294
- icon != null && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "uix-note__icon", children: icon }),
1295
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "uix-note__body", children })
1939
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: cx("uix-note", tone && `uix-note--${tone}`, className), ...props, children: [
1940
+ icon != null && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "uix-note__icon", children: icon }),
1941
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "uix-note__body", children })
1296
1942
  ] });
1297
1943
  }
1298
1944
 
1299
1945
  // src/components/Popover.tsx
1300
- var import_jsx_runtime38 = require("react/jsx-runtime");
1301
- function Popover({ popover = "auto", children, className, ...props }) {
1946
+ var import_react19 = require("react");
1947
+ var import_jsx_runtime40 = require("react/jsx-runtime");
1948
+ function Popover({
1949
+ popover = "auto",
1950
+ anchor,
1951
+ placement = "bottom-start",
1952
+ offset = 6,
1953
+ className,
1954
+ children,
1955
+ ...props
1956
+ }) {
1957
+ const ref = (0, import_react19.useRef)(null);
1958
+ const [open, setOpen] = (0, import_react19.useState)(false);
1302
1959
  const popoverAttr = { popover };
1303
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: cx("uix-popover", className), ...popoverAttr, ...props, children });
1960
+ const reposition = useAnchoredPosition(anchor ?? null, ref, {
1961
+ open: open && !!anchor,
1962
+ placement,
1963
+ offset
1964
+ });
1965
+ (0, import_react19.useEffect)(() => {
1966
+ const el = ref.current;
1967
+ if (!el || !anchor) return;
1968
+ const sync = () => {
1969
+ const isOpen = el.matches(":popover-open");
1970
+ if (isOpen) reposition();
1971
+ setOpen(isOpen);
1972
+ };
1973
+ el.addEventListener("toggle", sync);
1974
+ return () => el.removeEventListener("toggle", sync);
1975
+ }, [anchor, reposition]);
1976
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { ref, className: cx("uix-popover", className), ...popoverAttr, ...props, children });
1304
1977
  }
1305
1978
 
1306
1979
  // src/components/CommandPalette.tsx
1307
- var import_jsx_runtime39 = require("react/jsx-runtime");
1980
+ var import_jsx_runtime41 = require("react/jsx-runtime");
1308
1981
  function CommandPalette({ inputProps, children, className, ...props }) {
1309
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: cx("uix-cmdk", className), ...props, children: [
1310
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("input", { className: "uix-cmdk__input", ...inputProps }),
1311
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "uix-cmdk__list", children })
1982
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: cx("uix-cmdk", className), ...props, children: [
1983
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("input", { className: "uix-cmdk__input", ...inputProps }),
1984
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uix-cmdk__list", children })
1312
1985
  ] });
1313
1986
  }
1314
1987
  function CommandGroup({ label, children }) {
1315
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
1316
- label != null && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "uix-cmdk__group", children: label }),
1988
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
1989
+ label != null && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uix-cmdk__group", children: label }),
1317
1990
  children
1318
1991
  ] });
1319
1992
  }
1320
1993
  function CommandItem({ icon, shortcut, active, children, className, ...props }) {
1321
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: cx("uix-cmdk__item", className), "data-active": active || void 0, ...props, children: [
1994
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: cx("uix-cmdk__item", className), "data-active": active || void 0, ...props, children: [
1322
1995
  icon,
1323
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { children }),
1996
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children }),
1324
1997
  shortcut
1325
1998
  ] });
1326
1999
  }
1327
2000
 
1328
2001
  // src/components/Inbox.tsx
1329
- var import_jsx_runtime40 = require("react/jsx-runtime");
2002
+ var import_jsx_runtime42 = require("react/jsx-runtime");
1330
2003
  function Inbox({ children, className, ...props }) {
1331
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: cx("uix-inbox", className), ...props, children });
2004
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: cx("uix-inbox", className), ...props, children });
1332
2005
  }
1333
2006
  function InboxList({ children, className, ...props }) {
1334
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: cx("uix-inbox__list", className), role: "listbox", ...props, children });
2007
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: cx("uix-inbox__list", className), role: "listbox", ...props, children });
1335
2008
  }
1336
2009
  function InboxItem({
1337
2010
  subject,
@@ -1343,7 +2016,7 @@ function InboxItem({
1343
2016
  className,
1344
2017
  ...props
1345
2018
  }) {
1346
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2019
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
1347
2020
  "div",
1348
2021
  {
1349
2022
  role: "option",
@@ -1353,82 +2026,158 @@ function InboxItem({
1353
2026
  ...props,
1354
2027
  children: [
1355
2028
  leading,
1356
- subject != null && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "uix-inbox__subject", children: subject }),
1357
- preview != null && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "uix-inbox__preview", children: preview }),
2029
+ subject != null && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "uix-inbox__subject", children: subject }),
2030
+ preview != null && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "uix-inbox__preview", children: preview }),
1358
2031
  children
1359
2032
  ]
1360
2033
  }
1361
2034
  );
1362
2035
  }
1363
2036
  function InboxDetail({ children, className, ...props }) {
1364
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: cx("uix-inbox__detail", className), ...props, children });
2037
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: cx("uix-inbox__detail", className), ...props, children });
1365
2038
  }
1366
2039
 
1367
2040
  // src/components/Kanban.tsx
1368
- var import_jsx_runtime41 = require("react/jsx-runtime");
2041
+ var import_jsx_runtime43 = require("react/jsx-runtime");
1369
2042
  function Kanban({ children, className, ...props }) {
1370
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: cx("uix-kanban", className), ...props, children });
2043
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: cx("uix-kanban", className), ...props, children });
1371
2044
  }
1372
2045
  function KanbanColumn({ title, count, children, className, ...props }) {
1373
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: cx("uix-kanban__col", className), ...props, children: [
1374
- (title != null || count != null) && /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "uix-kanban__head", children: [
2046
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: cx("uix-kanban__col", className), ...props, children: [
2047
+ (title != null || count != null) && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "uix-kanban__head", children: [
1375
2048
  title,
1376
- count != null && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "uix-kanban__count", children: count })
2049
+ count != null && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "uix-kanban__count", children: count })
1377
2050
  ] }),
1378
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uix-kanban__body", children })
2051
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__body", children })
1379
2052
  ] });
1380
2053
  }
1381
2054
  function KanbanCard({ title, meta, children, className, ...props }) {
1382
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: cx("uix-kanban__card", className), ...props, children: [
1383
- title && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uix-kanban__card-title", children: title }),
1384
- meta && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "uix-kanban__card-meta", children: meta }),
2055
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: cx("uix-kanban__card", className), ...props, children: [
2056
+ title && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__card-title", children: title }),
2057
+ meta && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "uix-kanban__card-meta", children: meta }),
1385
2058
  children
1386
2059
  ] });
1387
2060
  }
1388
2061
 
1389
2062
  // src/components/Tree.tsx
1390
- var import_react13 = require("react");
1391
- var import_jsx_runtime42 = require("react/jsx-runtime");
1392
- var ChevronIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime42.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_runtime42.jsx)("path", { d: "M2 4l4 4 4-4" }) });
1393
- function TreeNode({ node, expanded, selected, onToggle, onSelect, level = 0 }) {
2063
+ var import_react20 = require("react");
2064
+
2065
+ // src/tree-model.ts
2066
+ function flattenTree(nodes, expanded, level = 1) {
2067
+ const out = [];
2068
+ const setsize = nodes.length;
2069
+ nodes.forEach((node, i) => {
2070
+ const hasChildren = Array.isArray(node.children) && node.children.length > 0;
2071
+ const isExpanded = hasChildren && expanded.has(node.id);
2072
+ out.push({ node, level, setsize, posinset: i + 1, hasChildren, isExpanded });
2073
+ if (isExpanded) out.push(...flattenTree(node.children, expanded, level + 1));
2074
+ });
2075
+ return out;
2076
+ }
2077
+ function treeNav(flat, currentId, key) {
2078
+ const idx = flat.findIndex((f) => f.node.id === currentId);
2079
+ if (idx < 0) return {};
2080
+ const cur = flat[idx];
2081
+ const focusAt = (i) => {
2082
+ const clamped = Math.max(0, Math.min(flat.length - 1, i));
2083
+ return { focusId: flat[clamped]?.node.id };
2084
+ };
2085
+ switch (key) {
2086
+ case "ArrowDown":
2087
+ return focusAt(idx + 1);
2088
+ case "ArrowUp":
2089
+ return focusAt(idx - 1);
2090
+ case "Home":
2091
+ return focusAt(0);
2092
+ case "End":
2093
+ return focusAt(flat.length - 1);
2094
+ case "ArrowRight":
2095
+ if (cur.hasChildren && !cur.isExpanded) return { toggleId: cur.node.id };
2096
+ if (cur.hasChildren && cur.isExpanded) return focusAt(idx + 1);
2097
+ return {};
2098
+ case "ArrowLeft":
2099
+ if (cur.hasChildren && cur.isExpanded) return { toggleId: cur.node.id };
2100
+ for (let i = idx - 1; i >= 0; i--) {
2101
+ if (flat[i].level === cur.level - 1) return { focusId: flat[i].node.id };
2102
+ }
2103
+ return {};
2104
+ case "Enter":
2105
+ case " ":
2106
+ return cur.hasChildren ? { toggleId: cur.node.id, selectId: cur.node.id } : { selectId: cur.node.id };
2107
+ default:
2108
+ return {};
2109
+ }
2110
+ }
2111
+
2112
+ // src/components/Tree.tsx
2113
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2114
+ 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" }) });
2115
+ function TreeNode({ node, expanded, selected, selectable, tabbableId, onToggle, onSelect, onFocusNode, level = 1 }) {
1394
2116
  const hasChildren = Array.isArray(node.children) && node.children.length > 0;
1395
2117
  const isExpanded = expanded.has(node.id);
1396
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("li", { children: [
1397
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
1398
- "button",
1399
- {
1400
- className: "uix-tree__row",
1401
- "aria-expanded": hasChildren ? isExpanded : void 0,
1402
- "aria-selected": selected === node.id,
1403
- onClick: () => {
1404
- if (hasChildren) onToggle(node.id);
1405
- onSelect?.(node.id);
1406
- },
1407
- children: [
1408
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "uix-tree__toggle", children: hasChildren && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChevronIcon2, {}) }),
1409
- node.icon && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { "aria-hidden": "true", children: node.icon }),
1410
- node.label
1411
- ]
1412
- }
1413
- ),
1414
- hasChildren && isExpanded && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { children: node.children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
1415
- TreeNode,
1416
- {
1417
- node: child,
1418
- expanded,
1419
- selected,
1420
- onToggle,
1421
- onSelect,
1422
- level: level + 1
2118
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2119
+ "li",
2120
+ {
2121
+ role: "treeitem",
2122
+ "aria-level": level,
2123
+ "aria-expanded": hasChildren ? isExpanded : void 0,
2124
+ "aria-selected": selectable ? selected === node.id : void 0,
2125
+ "data-id": node.id,
2126
+ tabIndex: tabbableId === node.id ? 0 : -1,
2127
+ className: "uix-tree__item",
2128
+ onFocus: (e) => {
2129
+ if (e.target === e.currentTarget) onFocusNode(node.id);
1423
2130
  },
1424
- child.id
1425
- )) })
1426
- ] });
2131
+ onClick: (e) => {
2132
+ e.stopPropagation();
2133
+ if (hasChildren) onToggle(node.id);
2134
+ onSelect?.(node.id);
2135
+ onFocusNode(node.id);
2136
+ },
2137
+ children: [
2138
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "uix-tree__row", children: [
2139
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-tree__toggle", children: hasChildren && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChevronIcon2, {}) }),
2140
+ node.icon && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { "aria-hidden": "true", children: node.icon }),
2141
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-tree__label", children: node.label })
2142
+ ] }),
2143
+ 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)(
2144
+ TreeNode,
2145
+ {
2146
+ node: child,
2147
+ expanded,
2148
+ selected,
2149
+ selectable,
2150
+ tabbableId,
2151
+ onToggle,
2152
+ onSelect,
2153
+ onFocusNode,
2154
+ level: level + 1
2155
+ },
2156
+ child.id
2157
+ )) })
2158
+ ]
2159
+ }
2160
+ );
1427
2161
  }
1428
- function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected, onToggle, onSelect, className, ...props }) {
1429
- const [internalExpanded, setInternalExpanded] = (0, import_react13.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
2162
+ function Tree({
2163
+ nodes,
2164
+ expanded: controlledExpanded,
2165
+ defaultExpanded,
2166
+ selected,
2167
+ onToggle,
2168
+ onSelect,
2169
+ virtualize,
2170
+ rowHeight = 32,
2171
+ maxHeight = 384,
2172
+ className,
2173
+ ...props
2174
+ }) {
2175
+ const [internalExpanded, setInternalExpanded] = (0, import_react20.useState)(defaultExpanded ?? /* @__PURE__ */ new Set());
1430
2176
  const expanded = controlledExpanded ?? internalExpanded;
1431
- function handleToggle(id) {
2177
+ const rootRef = (0, import_react20.useRef)(null);
2178
+ const [focusedId, setFocusedId] = (0, import_react20.useState)(void 0);
2179
+ const selectable = selected !== void 0 || onSelect != null;
2180
+ const handleToggle = (0, import_react20.useCallback)((id) => {
1432
2181
  if (!controlledExpanded) {
1433
2182
  setInternalExpanded((prev) => {
1434
2183
  const next = new Set(prev);
@@ -1438,41 +2187,231 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
1438
2187
  });
1439
2188
  }
1440
2189
  onToggle?.(id);
2190
+ }, [controlledExpanded, onToggle]);
2191
+ const flat = (0, import_react20.useMemo)(() => flattenTree(nodes, expanded), [nodes, expanded]);
2192
+ const useVirtual = virtualize ?? shouldVirtualize(flat.length);
2193
+ const tabbableId = focusedId ?? selected ?? nodes[0]?.id;
2194
+ if (useVirtual) {
2195
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2196
+ VirtualTreeView,
2197
+ {
2198
+ flat,
2199
+ rowHeight,
2200
+ maxHeight,
2201
+ selectable,
2202
+ selected,
2203
+ tabbableId,
2204
+ onToggle: handleToggle,
2205
+ onSelect,
2206
+ focusedId,
2207
+ setFocusedId,
2208
+ className,
2209
+ ...props
2210
+ }
2211
+ );
1441
2212
  }
1442
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { className: cx("uix-tree", className), role: "tree", ...props, children: nodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2213
+ const treeItems = () => Array.from(rootRef.current?.querySelectorAll('[role="treeitem"]') ?? []);
2214
+ const focusItem = (el) => {
2215
+ if (el) {
2216
+ el.focus();
2217
+ setFocusedId(el.dataset.id);
2218
+ }
2219
+ };
2220
+ const onKeyDown = (e) => {
2221
+ const cur = e.target.closest('[role="treeitem"]');
2222
+ if (!cur || !rootRef.current?.contains(cur)) return;
2223
+ const list = treeItems();
2224
+ const idx = list.indexOf(cur);
2225
+ const id = cur.dataset.id;
2226
+ const exp = cur.getAttribute("aria-expanded");
2227
+ switch (e.key) {
2228
+ case "ArrowDown":
2229
+ e.preventDefault();
2230
+ focusItem(list[Math.min(idx + 1, list.length - 1)]);
2231
+ break;
2232
+ case "ArrowUp":
2233
+ e.preventDefault();
2234
+ focusItem(list[Math.max(idx - 1, 0)]);
2235
+ break;
2236
+ case "Home":
2237
+ e.preventDefault();
2238
+ focusItem(list[0]);
2239
+ break;
2240
+ case "End":
2241
+ e.preventDefault();
2242
+ focusItem(list[list.length - 1]);
2243
+ break;
2244
+ case "ArrowRight":
2245
+ e.preventDefault();
2246
+ if (exp === "false") handleToggle(id);
2247
+ else if (exp === "true") focusItem(cur.querySelector(':scope > [role="group"] > [role="treeitem"]'));
2248
+ break;
2249
+ case "ArrowLeft":
2250
+ e.preventDefault();
2251
+ if (exp === "true") handleToggle(id);
2252
+ else focusItem(cur.parentElement?.closest('[role="treeitem"]'));
2253
+ break;
2254
+ case "Enter":
2255
+ case " ":
2256
+ e.preventDefault();
2257
+ if (exp !== null) handleToggle(id);
2258
+ onSelect?.(id);
2259
+ setFocusedId(id);
2260
+ break;
2261
+ default:
2262
+ break;
2263
+ }
2264
+ };
2265
+ 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)(
1443
2266
  TreeNode,
1444
2267
  {
1445
2268
  node,
1446
2269
  expanded,
1447
2270
  selected,
2271
+ selectable,
2272
+ tabbableId,
1448
2273
  onToggle: handleToggle,
1449
- onSelect
2274
+ onSelect,
2275
+ onFocusNode: setFocusedId,
2276
+ level: 1
1450
2277
  },
1451
2278
  node.id
1452
2279
  )) });
1453
2280
  }
2281
+ function VirtualTreeView({
2282
+ flat,
2283
+ rowHeight,
2284
+ maxHeight,
2285
+ selectable,
2286
+ selected,
2287
+ tabbableId,
2288
+ onToggle,
2289
+ onSelect,
2290
+ focusedId,
2291
+ setFocusedId,
2292
+ className,
2293
+ ...props
2294
+ }) {
2295
+ const scrollRef = (0, import_react20.useRef)(null);
2296
+ const [scrollTop, setScrollTop] = (0, import_react20.useState)(0);
2297
+ const pendingFocus = (0, import_react20.useRef)(false);
2298
+ const win = virtualWindow(scrollTop, maxHeight, rowHeight, flat.length);
2299
+ const rows = flat.slice(win.start, win.end);
2300
+ (0, import_react20.useEffect)(() => {
2301
+ if (!pendingFocus.current || focusedId == null) return;
2302
+ const el = scrollRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(focusedId)}"]`);
2303
+ if (el) {
2304
+ el.focus();
2305
+ pendingFocus.current = false;
2306
+ }
2307
+ }, [focusedId, win.start, win.end]);
2308
+ const moveTo = (id) => {
2309
+ const i = flat.findIndex((f) => f.node.id === id);
2310
+ if (i < 0) return;
2311
+ const top = i * rowHeight;
2312
+ let next = scrollTop;
2313
+ if (top < scrollTop) next = top;
2314
+ else if (top + rowHeight > scrollTop + maxHeight) next = top + rowHeight - maxHeight;
2315
+ if (next !== scrollTop) {
2316
+ if (scrollRef.current) scrollRef.current.scrollTop = next;
2317
+ setScrollTop(next);
2318
+ }
2319
+ pendingFocus.current = true;
2320
+ setFocusedId(id);
2321
+ };
2322
+ const onKeyDown = (e) => {
2323
+ const curId = e.target.closest('[role="treeitem"]')?.dataset.id ?? focusedId;
2324
+ const act = treeNav(flat, curId, e.key);
2325
+ if (act.focusId === void 0 && act.toggleId === void 0 && act.selectId === void 0) return;
2326
+ e.preventDefault();
2327
+ if (act.toggleId !== void 0) onToggle(act.toggleId);
2328
+ if (act.selectId !== void 0) {
2329
+ onSelect?.(act.selectId);
2330
+ if (act.focusId === void 0) {
2331
+ pendingFocus.current = true;
2332
+ setFocusedId(act.selectId);
2333
+ }
2334
+ }
2335
+ if (act.focusId !== void 0) moveTo(act.focusId);
2336
+ };
2337
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2338
+ "ul",
2339
+ {
2340
+ ref: scrollRef,
2341
+ className: cx("uix-tree", "uix-tree--virtual", className),
2342
+ role: "tree",
2343
+ style: { maxHeight, overflow: "auto" },
2344
+ onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
2345
+ onKeyDown,
2346
+ ...props,
2347
+ children: [
2348
+ win.padTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { "aria-hidden": "true", style: { height: win.padTop } }),
2349
+ rows.map((f) => {
2350
+ const rowStyle = { height: rowHeight, paddingLeft: `calc(${f.level - 1} * var(--uix-space-5))` };
2351
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2352
+ "li",
2353
+ {
2354
+ role: "treeitem",
2355
+ "aria-level": f.level,
2356
+ "aria-setsize": f.setsize,
2357
+ "aria-posinset": f.posinset,
2358
+ "aria-expanded": f.hasChildren ? f.isExpanded : void 0,
2359
+ "aria-selected": selectable ? selected === f.node.id : void 0,
2360
+ "data-id": f.node.id,
2361
+ tabIndex: tabbableId === f.node.id ? 0 : -1,
2362
+ className: "uix-tree__item",
2363
+ onFocus: (e) => {
2364
+ if (e.target === e.currentTarget) setFocusedId(f.node.id);
2365
+ },
2366
+ onClick: (e) => {
2367
+ e.stopPropagation();
2368
+ if (f.hasChildren) onToggle(f.node.id);
2369
+ onSelect?.(f.node.id);
2370
+ setFocusedId(f.node.id);
2371
+ },
2372
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "uix-tree__row", style: rowStyle, children: [
2373
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-tree__toggle", children: f.hasChildren && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ChevronIcon2, {}) }),
2374
+ f.node.icon && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { "aria-hidden": "true", children: f.node.icon }),
2375
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "uix-tree__label", children: f.node.label })
2376
+ ] })
2377
+ },
2378
+ f.node.id
2379
+ );
2380
+ }),
2381
+ win.padBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { "aria-hidden": "true", style: { height: win.padBottom } })
2382
+ ]
2383
+ }
2384
+ );
2385
+ }
1454
2386
  // Annotate the CommonJS export names for ESM import in node:
1455
2387
  0 && (module.exports = {
1456
2388
  Alert,
1457
2389
  AppShell,
1458
2390
  Avatar,
1459
2391
  AvatarGroup,
2392
+ BulkBar,
1460
2393
  Button,
1461
2394
  ButtonGroup,
1462
2395
  Card,
2396
+ CellStrong,
2397
+ CellSub,
1463
2398
  Checkbox,
1464
2399
  CommandGroup,
1465
2400
  CommandItem,
1466
2401
  CommandPalette,
1467
2402
  Comment,
1468
2403
  Comments,
2404
+ Composer,
2405
+ ComposerBar,
1469
2406
  DescriptionItem,
1470
2407
  DescriptionList,
1471
2408
  DetailLayout,
1472
2409
  Drawer,
1473
2410
  EmptyState,
1474
2411
  ErrorState,
2412
+ ExpandToggle,
1475
2413
  Field,
2414
+ Highlighted,
1476
2415
  Inbox,
1477
2416
  InboxDetail,
1478
2417
  InboxItem,
@@ -1486,6 +2425,7 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
1486
2425
  List,
1487
2426
  ListItem,
1488
2427
  LoadingState,
2428
+ Mark,
1489
2429
  Meter,
1490
2430
  Modal,
1491
2431
  NavFavourites,
@@ -1500,6 +2440,10 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
1500
2440
  Prose,
1501
2441
  Radio,
1502
2442
  RadioGroup,
2443
+ RowAction,
2444
+ RowActions,
2445
+ Segmented,
2446
+ SegmentedOption,
1503
2447
  Select,
1504
2448
  Sidebar,
1505
2449
  SidebarSection,
@@ -1525,6 +2469,28 @@ function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected,
1525
2469
  Tr,
1526
2470
  Tree,
1527
2471
  UserChip,
2472
+ applyFilters,
2473
+ clampWidth,
2474
+ computePosition,
1528
2475
  cx,
1529
- useDialog
2476
+ flattenTree,
2477
+ highlightSegments,
2478
+ matchFilter,
2479
+ mergePinned,
2480
+ multiSort,
2481
+ nextSortDir,
2482
+ parseView,
2483
+ reorder,
2484
+ searchRows,
2485
+ selectAllState,
2486
+ serializeView,
2487
+ shouldVirtualize,
2488
+ toggleId,
2489
+ togglePage,
2490
+ toggleSort,
2491
+ treeNav,
2492
+ useAnchoredPosition,
2493
+ useDialog,
2494
+ useTable,
2495
+ virtualWindow
1530
2496
  });