@tensor_1/react 2.10.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/chart.cjs +3 -12
  2. package/dist/components/Alert.js +7 -15
  3. package/dist/components/AppShell.d.ts +1 -3
  4. package/dist/components/AppShell.js +1 -3
  5. package/dist/components/Avatar.js +1 -2
  6. package/dist/components/Button.js +16 -32
  7. package/dist/components/Card.d.ts +1 -3
  8. package/dist/components/Card.js +2 -2
  9. package/dist/components/Chart.js +3 -12
  10. package/dist/components/CommandPalette.d.ts +2 -4
  11. package/dist/components/CommandPalette.js +13 -66
  12. package/dist/components/Comments.d.ts +1 -5
  13. package/dist/components/Comments.js +1 -1
  14. package/dist/components/Composer.d.ts +1 -8
  15. package/dist/components/Composer.js +2 -7
  16. package/dist/components/DetailLayout.js +1 -1
  17. package/dist/components/Drawer.d.ts +4 -3
  18. package/dist/components/Drawer.js +4 -6
  19. package/dist/components/Field.js +3 -6
  20. package/dist/components/Inbox.d.ts +3 -13
  21. package/dist/components/Inbox.js +3 -61
  22. package/dist/components/Input.d.ts +2 -13
  23. package/dist/components/Input.js +5 -20
  24. package/dist/components/Kanban.d.ts +3 -15
  25. package/dist/components/Kanban.js +10 -42
  26. package/dist/components/Meter.d.ts +1 -3
  27. package/dist/components/Meter.js +1 -9
  28. package/dist/components/Modal.d.ts +5 -3
  29. package/dist/components/Modal.js +4 -6
  30. package/dist/components/NavFavourites.js +0 -11
  31. package/dist/components/PageHeader.d.ts +1 -3
  32. package/dist/components/PageHeader.js +2 -2
  33. package/dist/components/Peek.d.ts +4 -3
  34. package/dist/components/Peek.js +6 -9
  35. package/dist/components/Progress.d.ts +1 -3
  36. package/dist/components/Progress.js +3 -5
  37. package/dist/components/Radio.d.ts +1 -3
  38. package/dist/components/Radio.js +2 -6
  39. package/dist/components/Sidebar.js +2 -4
  40. package/dist/components/Spinner.d.ts +1 -3
  41. package/dist/components/Spinner.js +3 -3
  42. package/dist/components/Stat.js +1 -5
  43. package/dist/components/States.d.ts +1 -3
  44. package/dist/components/States.js +2 -8
  45. package/dist/components/Table.d.ts +2 -9
  46. package/dist/components/Table.js +6 -10
  47. package/dist/components/Tabs.d.ts +1 -13
  48. package/dist/components/Tabs.js +3 -78
  49. package/dist/components/Timeline.d.ts +3 -3
  50. package/dist/components/Timeline.js +3 -3
  51. package/dist/components/Toast.js +5 -42
  52. package/dist/components/Tooltip.d.ts +1 -3
  53. package/dist/components/Tooltip.js +5 -44
  54. package/dist/components/Tree.d.ts +0 -2
  55. package/dist/components/Tree.js +4 -26
  56. package/dist/hooks/useDialog.d.ts +1 -1
  57. package/dist/hooks/useDialog.js +2 -17
  58. package/dist/hooks/useTable.d.ts +0 -5
  59. package/dist/hooks/useTable.js +1 -20
  60. package/dist/index.cjs +162 -616
  61. package/dist/index.d.cts +37 -127
  62. package/dist/index.d.ts +2 -2
  63. package/dist/index.js +1 -2
  64. package/dist/tree-model.d.ts +0 -4
  65. package/dist/tree-model.js +0 -8
  66. package/package.json +1 -1
@@ -1,11 +1,8 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { cx } from "../cx.js";
3
3
  import { highlightSegments } from "../table-engine.js";
4
- function TableWrap({ announcement, children, className, ...props }) {
5
- return /* @__PURE__ */ jsxs("div", { className: cx("uix-table-wrap", className), ...props, children: [
6
- announcement != null && /* @__PURE__ */ jsx("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
7
- children
8
- ] });
4
+ function TableWrap({ children, className, ...props }) {
5
+ return /* @__PURE__ */ jsx("div", { className: cx("uix-table-wrap", className), ...props, children });
9
6
  }
10
7
  function Table({
11
8
  density = "standard",
@@ -44,9 +41,9 @@ function Th({ sortable, sortDirection, sortOrder, onSort, className, children, o
44
41
  "data-sort": sortable || void 0,
45
42
  "data-sort-order": sortOrder != null && sortOrder > 0 ? sortOrder : void 0,
46
43
  "aria-sort": sortDirection && sortDirection !== "none" ? sortDirection : void 0,
47
- onClick: sortable && onSort ? void 0 : onClick,
44
+ onClick: sortable ? onSort ?? onClick : onClick,
48
45
  ...props,
49
- children: sortable ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-th__sortbtn", onClick: onSort, children }) : children
46
+ children
50
47
  }
51
48
  );
52
49
  }
@@ -58,7 +55,7 @@ function Tr({ selected, pinned, className, children, ...props }) {
58
55
  "tr",
59
56
  {
60
57
  className,
61
- "data-selected": selected || void 0,
58
+ "aria-selected": selected || void 0,
62
59
  "data-pinned": pinned || void 0,
63
60
  ...props,
64
61
  children
@@ -81,14 +78,13 @@ function RowActions({ children, className, ...props }) {
81
78
  function RowAction({ children, className, type = "button", ...props }) {
82
79
  return /* @__PURE__ */ jsx("button", { type, className: cx("uix-rowact", className), ...props, children });
83
80
  }
84
- function ExpandToggle({ expanded, controls, className, type = "button", ...props }) {
81
+ function ExpandToggle({ expanded, className, type = "button", ...props }) {
85
82
  return /* @__PURE__ */ jsx(
86
83
  "button",
87
84
  {
88
85
  type,
89
86
  className: cx("uix-table__expand", className),
90
87
  "aria-expanded": expanded,
91
- "aria-controls": controls,
92
88
  "aria-label": expanded ? "Collapse row" : "Expand row",
93
89
  ...props,
94
90
  children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M6 4l4 4-4 4" }) })
@@ -15,17 +15,5 @@ interface TabProps extends Omit<HTMLAttributes<HTMLButtonElement>, 'onChange'> {
15
15
  disabled?: boolean;
16
16
  }
17
17
  declare function Tab({ value, children, disabled, className, onClick, ...props }: TabProps): react.JSX.Element;
18
- interface TabPanelProps extends HTMLAttributes<HTMLDivElement> {
19
- /** The owning `<Tab>`'s value — the panel renders only while that tab is selected. */
20
- value: string;
21
- children?: ReactNode;
22
- }
23
- /**
24
- * The tabpanel paired with a `<Tab>` by `value` (UIX-A11Y-2): labelled by its tab,
25
- * `tabIndex 0` so Tab from the tablist lands in the content, rendered only while
26
- * selected. Place it as a direct child of `<Tabs>` — it is hoisted out of the tablist —
27
- * or anywhere the Tabs context reaches.
28
- */
29
- declare function TabPanel({ value, children, ...props }: TabPanelProps): react.JSX.Element | null;
30
18
 
31
- export { Tab, TabPanel, type TabPanelProps, type TabProps, Tabs, type TabsProps };
19
+ export { Tab, type TabProps, Tabs, type TabsProps };
@@ -1,66 +1,10 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import { Children, createContext, isValidElement, useContext, useEffect, useId, useRef } from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { createContext, useContext } from "react";
4
4
  import { cx } from "../cx.js";
5
5
  const TabsCtx = createContext({});
6
6
  function Tabs({ variant = "line", value, onChange, children, className }) {
7
- const baseId = useId();
8
- const listRef = useRef(null);
9
- const kids = Children.toArray(children);
10
- const panels = kids.filter((k) => isValidElement(k) && k.type === TabPanel);
11
- const tabs = kids.filter((k) => !(isValidElement(k) && k.type === TabPanel));
12
- useEffect(() => {
13
- const list = listRef.current;
14
- if (!list) return;
15
- const all = Array.from(list.querySelectorAll('[role="tab"]'));
16
- const enabled = all.filter((t) => !t.matches(":disabled"));
17
- const active = enabled.find((t) => t.getAttribute("aria-selected") === "true") ?? enabled[0];
18
- for (const t of all) t.tabIndex = t === active ? 0 : -1;
19
- });
20
- const onKeyDown = (e) => {
21
- const items = Array.from(
22
- listRef.current?.querySelectorAll('[role="tab"]:not(:disabled)') ?? []
23
- );
24
- const cur = e.target.closest('[role="tab"]');
25
- const idx = cur ? items.indexOf(cur) : -1;
26
- if (idx < 0 || items.length === 0) return;
27
- let next;
28
- switch (e.key) {
29
- case "ArrowRight":
30
- next = (idx + 1) % items.length;
31
- break;
32
- case "ArrowLeft":
33
- next = (idx - 1 + items.length) % items.length;
34
- break;
35
- case "Home":
36
- next = 0;
37
- break;
38
- case "End":
39
- next = items.length - 1;
40
- break;
41
- default:
42
- return;
43
- }
44
- e.preventDefault();
45
- const target = items[next];
46
- if (target) {
47
- target.focus();
48
- target.click();
49
- }
50
- };
51
- return /* @__PURE__ */ jsxs(TabsCtx.Provider, { value: { value, onChange, baseId }, children: [
52
- /* @__PURE__ */ jsx(
53
- "div",
54
- {
55
- ref: listRef,
56
- role: "tablist",
57
- className: cx("uix-tabs", `uix-tabs--${variant}`, className),
58
- onKeyDown,
59
- children: tabs
60
- }
61
- ),
62
- panels
63
- ] });
7
+ return /* @__PURE__ */ jsx(TabsCtx.Provider, { value: { value, onChange }, children: /* @__PURE__ */ jsx("div", { role: "tablist", className: cx("uix-tabs", `uix-tabs--${variant}`, className), children }) });
64
8
  }
65
9
  function Tab({ value, children, disabled, className, onClick, ...props }) {
66
10
  const ctx = useContext(TabsCtx);
@@ -69,11 +13,8 @@ function Tab({ value, children, disabled, className, onClick, ...props }) {
69
13
  "button",
70
14
  {
71
15
  role: "tab",
72
- id: ctx.baseId ? `${ctx.baseId}-tab-${value}` : void 0,
73
- "aria-controls": ctx.baseId ? `${ctx.baseId}-panel-${value}` : void 0,
74
16
  className: cx("uix-tab", className),
75
17
  "aria-selected": selected,
76
- tabIndex: selected ? 0 : -1,
77
18
  disabled,
78
19
  onClick: (e) => {
79
20
  ctx.onChange?.(value);
@@ -84,23 +25,7 @@ function Tab({ value, children, disabled, className, onClick, ...props }) {
84
25
  }
85
26
  );
86
27
  }
87
- function TabPanel({ value, children, ...props }) {
88
- const ctx = useContext(TabsCtx);
89
- if (ctx.value !== value) return null;
90
- return /* @__PURE__ */ jsx(
91
- "div",
92
- {
93
- role: "tabpanel",
94
- id: ctx.baseId ? `${ctx.baseId}-panel-${value}` : void 0,
95
- "aria-labelledby": ctx.baseId ? `${ctx.baseId}-tab-${value}` : void 0,
96
- tabIndex: 0,
97
- ...props,
98
- children
99
- }
100
- );
101
- }
102
28
  export {
103
29
  Tab,
104
- TabPanel,
105
30
  Tabs
106
31
  };
@@ -1,12 +1,12 @@
1
1
  import * as react from 'react';
2
2
  import { HTMLAttributes, ReactNode } from 'react';
3
3
 
4
- interface TimelineProps extends HTMLAttributes<HTMLOListElement> {
4
+ interface TimelineProps extends HTMLAttributes<HTMLDivElement> {
5
5
  children?: ReactNode;
6
6
  }
7
- /** Activity timeline container over `.uix-timeline`. Ordered list so AT reads entry count + sequence (UIX-A11Y-4). */
7
+ /** Activity timeline container over `.uix-timeline`. */
8
8
  declare function Timeline({ children, className, ...props }: TimelineProps): react.JSX.Element;
9
- interface TimelineItemProps extends HTMLAttributes<HTMLLIElement> {
9
+ interface TimelineItemProps extends HTMLAttributes<HTMLDivElement> {
10
10
  /**
11
11
  * Custom rail node — an icon puck, an avatar, a tinted dot. Defaults to the
12
12
  * accent `.uix-timeline__node` dot. Supply this to make a heterogeneous feed
@@ -1,11 +1,11 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { cx } from "../cx.js";
3
3
  function Timeline({ children, className, ...props }) {
4
- return /* @__PURE__ */ jsx("ol", { className: cx("uix-timeline", className), ...props, children });
4
+ return /* @__PURE__ */ jsx("div", { className: cx("uix-timeline", className), ...props, children });
5
5
  }
6
6
  function TimelineItem({ node, meta, children, className, ...props }) {
7
- return /* @__PURE__ */ jsxs("li", { className: cx("uix-timeline__item", className), ...props, children: [
8
- /* @__PURE__ */ jsxs("div", { className: "uix-timeline__rail", "aria-hidden": "true", children: [
7
+ return /* @__PURE__ */ jsxs("div", { className: cx("uix-timeline__item", className), ...props, children: [
8
+ /* @__PURE__ */ jsxs("div", { className: "uix-timeline__rail", children: [
9
9
  node ?? /* @__PURE__ */ jsx("span", { className: "uix-timeline__node" }),
10
10
  /* @__PURE__ */ jsx("span", { className: "uix-timeline__line" })
11
11
  ] }),
@@ -1,65 +1,28 @@
1
- "use client";
2
1
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
4
2
  import { cx } from "../cx.js";
5
- const ToasterContext = createContext(null);
6
3
  function Toast({ title, message, tone, icon, onClose, leaving, className, ...props }) {
7
4
  const assertive = tone === "danger";
8
- const toaster = useContext(ToasterContext);
9
- const bodyRef = useRef(null);
10
- useEffect(() => {
11
- if (assertive || !toaster) return;
12
- const text = bodyRef.current?.textContent?.trim();
13
- if (text) toaster.announce(text);
14
- }, []);
15
- const handleClose = (e) => {
16
- const btn = e.currentTarget;
17
- if (document.activeElement === btn) {
18
- const toasterEl = btn.closest(".uix-toaster");
19
- if (toasterEl) {
20
- const closes = Array.from(toasterEl.querySelectorAll(".uix-toast__close"));
21
- const i = closes.indexOf(btn);
22
- (closes[i + 1] ?? closes[i - 1])?.focus();
23
- }
24
- }
25
- onClose?.();
26
- };
27
- const ownLiveRegion = assertive || !toaster;
28
5
  return /* @__PURE__ */ jsxs(
29
6
  "div",
30
7
  {
31
8
  className: cx("uix-toast", tone && `uix-toast--${tone}`, className),
32
9
  "data-leaving": leaving || void 0,
33
- role: ownLiveRegion ? assertive ? "alert" : "status" : void 0,
34
- "aria-live": ownLiveRegion ? assertive ? "assertive" : "polite" : void 0,
10
+ role: assertive ? "alert" : "status",
11
+ "aria-live": assertive ? "assertive" : "polite",
35
12
  ...props,
36
13
  children: [
37
14
  icon && /* @__PURE__ */ jsx("div", { className: "uix-toast__icon", "aria-hidden": "true", children: icon }),
38
- /* @__PURE__ */ jsxs("div", { className: "uix-toast__body", ref: bodyRef, children: [
15
+ /* @__PURE__ */ jsxs("div", { className: "uix-toast__body", children: [
39
16
  title && /* @__PURE__ */ jsx("div", { className: "uix-toast__title", children: title }),
40
17
  message && /* @__PURE__ */ jsx("div", { className: "uix-toast__msg", children: message })
41
18
  ] }),
42
- onClose && /* @__PURE__ */ jsx("button", { className: "uix-toast__close", onClick: handleClose, "aria-label": "Dismiss", children: "\xD7" })
19
+ onClose && /* @__PURE__ */ jsx("button", { className: "uix-toast__close", onClick: onClose, "aria-label": "Dismiss", children: "\xD7" })
43
20
  ]
44
21
  }
45
22
  );
46
23
  }
47
24
  function Toaster({ children, className, ...props }) {
48
- const [announced, setAnnounced] = useState("");
49
- const clearTimer = useRef(null);
50
- const announce = useCallback((text) => {
51
- setAnnounced((cur) => cur ? `${cur} ${text}` : text);
52
- if (clearTimer.current != null) clearTimeout(clearTimer.current);
53
- clearTimer.current = window.setTimeout(() => setAnnounced(""), 3e3);
54
- }, []);
55
- useEffect(() => () => {
56
- if (clearTimer.current != null) clearTimeout(clearTimer.current);
57
- }, []);
58
- const ctx = useMemo(() => ({ announce }), [announce]);
59
- return /* @__PURE__ */ jsx(ToasterContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs("div", { className: cx("uix-toaster", className), role: "region", "aria-label": "Notifications", ...props, children: [
60
- /* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", role: "status", "aria-live": "polite", children: announced }),
61
- children
62
- ] }) });
25
+ return /* @__PURE__ */ jsx("div", { className: cx("uix-toaster", className), ...props, children });
63
26
  }
64
27
  export {
65
28
  Toast,
@@ -13,9 +13,7 @@ interface TooltipProps extends HTMLAttributes<HTMLSpanElement> {
13
13
  * Tooltip whose bubble renders in the top layer via the native Popover API, so it
14
14
  * escapes any `overflow: hidden/scroll` ancestor that would clip the old CSS-only
15
15
  * `[data-uix-tip]` tooltip (UIX-FIX-02). Positioned by the shared overlay engine
16
- * (flip/shift), shown on hover and keyboard focus, dismissed on blur or Escape
17
- * (document-level, per WCAG 1.4.13), and hoverable: the bubble stays open while
18
- * the pointer is over it (UIX-A11Y-1).
16
+ * (flip/shift), shown on hover and keyboard focus, dismissed on blur or Escape.
19
17
  */
20
18
  declare function Tooltip({ label, placement, className, children, ...props }: TooltipProps): react.JSX.Element;
21
19
 
@@ -1,23 +1,15 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { cloneElement, isValidElement, useEffect, useId, useRef, useState } from "react";
3
+ import { useEffect, useId, useRef, useState } from "react";
4
4
  import { cx } from "../cx.js";
5
5
  import { useAnchoredPosition } from "../hooks/useAnchoredPosition.js";
6
6
  function Tooltip({ label, placement = "top", className, children, ...props }) {
7
7
  const triggerRef = useRef(null);
8
8
  const bubbleRef = useRef(null);
9
- const closeTimer = useRef(null);
10
9
  const [open, setOpen] = useState(false);
11
10
  const id = useId();
12
11
  useAnchoredPosition(triggerRef, bubbleRef, { open, placement, offset: 6 });
13
- const cancelClose = () => {
14
- if (closeTimer.current != null) {
15
- clearTimeout(closeTimer.current);
16
- closeTimer.current = null;
17
- }
18
- };
19
12
  const show = () => {
20
- cancelClose();
21
13
  const b = bubbleRef.current;
22
14
  if (b && !b.matches(":popover-open")) {
23
15
  try {
@@ -28,7 +20,6 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
28
20
  setOpen(true);
29
21
  };
30
22
  const hide = () => {
31
- cancelClose();
32
23
  const b = bubbleRef.current;
33
24
  if (b && b.matches(":popover-open")) {
34
25
  try {
@@ -38,20 +29,7 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
38
29
  }
39
30
  setOpen(false);
40
31
  };
41
- const scheduleHide = () => {
42
- cancelClose();
43
- closeTimer.current = window.setTimeout(hide, 100);
44
- };
45
- useEffect(() => {
46
- if (!open) return;
47
- const onKey = (e) => {
48
- if (e.key === "Escape") hide();
49
- };
50
- document.addEventListener("keydown", onKey);
51
- return () => document.removeEventListener("keydown", onKey);
52
- }, [open]);
53
32
  useEffect(() => () => {
54
- if (closeTimer.current != null) clearTimeout(closeTimer.current);
55
33
  const b = bubbleRef.current;
56
34
  if (b && b.matches(":popover-open")) {
57
35
  try {
@@ -61,19 +39,14 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
61
39
  }
62
40
  }, []);
63
41
  const bubbleAttr = { popover: "manual" };
64
- const single = isValidElement(children);
65
- const child = single ? cloneElement(children, {
66
- "aria-describedby": [children.props["aria-describedby"], id].filter(Boolean).join(" ")
67
- }) : children;
68
42
  return /* @__PURE__ */ jsxs(
69
43
  "span",
70
44
  {
71
45
  ref: triggerRef,
72
46
  className: cx("uix-tooltip", className),
73
- "aria-describedby": single ? void 0 : id,
74
- tabIndex: single ? void 0 : 0,
47
+ "aria-describedby": id,
75
48
  onMouseEnter: show,
76
- onMouseLeave: scheduleHide,
49
+ onMouseLeave: hide,
77
50
  onFocus: show,
78
51
  onBlur: hide,
79
52
  onKeyDown: (e) => {
@@ -81,20 +54,8 @@ function Tooltip({ label, placement = "top", className, children, ...props }) {
81
54
  },
82
55
  ...props,
83
56
  children: [
84
- child,
85
- /* @__PURE__ */ jsx(
86
- "span",
87
- {
88
- ref: bubbleRef,
89
- id,
90
- role: "tooltip",
91
- className: "uix-tooltip__bubble",
92
- onMouseEnter: cancelClose,
93
- onMouseLeave: scheduleHide,
94
- ...bubbleAttr,
95
- children: label
96
- }
97
- )
57
+ children,
58
+ /* @__PURE__ */ jsx("span", { ref: bubbleRef, id, role: "tooltip", className: "uix-tooltip__bubble", ...bubbleAttr, children: label })
98
59
  ]
99
60
  }
100
61
  );
@@ -4,8 +4,6 @@ import { HTMLAttributes, ReactNode } from 'react';
4
4
  interface TreeNodeData {
5
5
  id: string;
6
6
  label: ReactNode;
7
- /** Plain-text label for keyboard typeahead when `label` is not a string (UIX-A11Y-2). */
8
- typeaheadLabel?: string;
9
7
  icon?: ReactNode;
10
8
  children?: TreeNodeData[];
11
9
  }
@@ -152,13 +152,6 @@ function Tree({
152
152
  setFocusedId(id);
153
153
  break;
154
154
  default:
155
- if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
156
- const act = treeNav(flat, id, e.key);
157
- if (act.focusId !== void 0) {
158
- e.preventDefault();
159
- focusItem(rootRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(act.focusId)}"]`));
160
- }
161
- }
162
155
  break;
163
156
  }
164
157
  };
@@ -195,10 +188,8 @@ function VirtualTreeView({
195
188
  const scrollRef = useRef(null);
196
189
  const [scrollTop, setScrollTop] = useState(0);
197
190
  const pendingFocus = useRef(false);
198
- const hadFocus = useRef(false);
199
191
  const win = virtualWindow(scrollTop, maxHeight, rowHeight, flat.length);
200
192
  const rows = flat.slice(win.start, win.end);
201
- const windowHasTabbable = tabbableId != null && rows.some((f) => f.node.id === tabbableId);
202
193
  useEffect(() => {
203
194
  if (!pendingFocus.current || focusedId == null) return;
204
195
  const el = scrollRef.current?.querySelector(`[role="treeitem"][data-id="${CSS.escape(focusedId)}"]`);
@@ -207,11 +198,6 @@ function VirtualTreeView({
207
198
  pendingFocus.current = false;
208
199
  }
209
200
  }, [focusedId, win.start, win.end]);
210
- useEffect(() => {
211
- if (!hadFocus.current || pendingFocus.current) return;
212
- const active = document.activeElement;
213
- if (active === document.body || active == null) scrollRef.current?.focus({ preventScroll: true });
214
- });
215
201
  const moveTo = (id) => {
216
202
  const i = flat.findIndex((f) => f.node.id === id);
217
203
  if (i < 0) return;
@@ -227,7 +213,6 @@ function VirtualTreeView({
227
213
  setFocusedId(id);
228
214
  };
229
215
  const onKeyDown = (e) => {
230
- if (e.key.length === 1 && (e.ctrlKey || e.metaKey || e.altKey)) return;
231
216
  const curId = e.target.closest('[role="treeitem"]')?.dataset.id ?? focusedId;
232
217
  const act = treeNav(flat, curId, e.key);
233
218
  if (act.focusId === void 0 && act.toggleId === void 0 && act.selectId === void 0) return;
@@ -248,20 +233,13 @@ function VirtualTreeView({
248
233
  ref: scrollRef,
249
234
  className: cx("uix-tree", "uix-tree--virtual", className),
250
235
  role: "tree",
251
- tabIndex: -1,
252
236
  style: { maxHeight, overflow: "auto" },
253
237
  onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
254
238
  onKeyDown,
255
- onFocus: () => {
256
- hadFocus.current = true;
257
- },
258
- onBlur: (e) => {
259
- if (!e.currentTarget.contains(e.relatedTarget)) hadFocus.current = false;
260
- },
261
239
  ...props,
262
240
  children: [
263
- win.padTop > 0 && /* @__PURE__ */ jsx("li", { role: "presentation", "aria-hidden": "true", style: { height: win.padTop } }),
264
- rows.map((f, i) => {
241
+ win.padTop > 0 && /* @__PURE__ */ jsx("li", { "aria-hidden": "true", style: { height: win.padTop } }),
242
+ rows.map((f) => {
265
243
  const rowStyle = { height: rowHeight, paddingLeft: `calc(${f.level - 1} * var(--uix-space-5))` };
266
244
  return /* @__PURE__ */ jsx(
267
245
  "li",
@@ -273,7 +251,7 @@ function VirtualTreeView({
273
251
  "aria-expanded": f.hasChildren ? f.isExpanded : void 0,
274
252
  "aria-selected": selectable ? selected === f.node.id : void 0,
275
253
  "data-id": f.node.id,
276
- tabIndex: tabbableId === f.node.id || !windowHasTabbable && i === 0 ? 0 : -1,
254
+ tabIndex: tabbableId === f.node.id ? 0 : -1,
277
255
  className: "uix-tree__item",
278
256
  onFocus: (e) => {
279
257
  if (e.target === e.currentTarget) setFocusedId(f.node.id);
@@ -293,7 +271,7 @@ function VirtualTreeView({
293
271
  f.node.id
294
272
  );
295
273
  }),
296
- win.padBottom > 0 && /* @__PURE__ */ jsx("li", { role: "presentation", "aria-hidden": "true", style: { height: win.padBottom } })
274
+ win.padBottom > 0 && /* @__PURE__ */ jsx("li", { "aria-hidden": "true", style: { height: win.padBottom } })
297
275
  ]
298
276
  }
299
277
  );
@@ -1,3 +1,3 @@
1
- declare function useDialog(open: boolean, onClose?: () => void): React.RefObject<HTMLDialogElement>;
1
+ declare function useDialog(open: boolean): React.RefObject<HTMLDialogElement>;
2
2
 
3
3
  export { useDialog };
@@ -30,10 +30,8 @@ function unlockBodyScroll() {
30
30
  saved = null;
31
31
  }
32
32
  }
33
- function useDialog(open, onClose) {
33
+ function useDialog(open) {
34
34
  const ref = useRef(null);
35
- const onCloseRef = useRef(onClose);
36
- onCloseRef.current = onClose;
37
35
  useEffect(() => {
38
36
  const el = ref.current;
39
37
  if (!el || !open) {
@@ -42,21 +40,8 @@ function useDialog(open, onClose) {
42
40
  }
43
41
  if (!el.open) el.showModal();
44
42
  lockBodyScroll();
45
- let locked = true;
46
- const handleClose = () => {
47
- if (locked) {
48
- locked = false;
49
- unlockBodyScroll();
50
- }
51
- onCloseRef.current?.();
52
- };
53
- el.addEventListener("close", handleClose);
54
43
  return () => {
55
- el.removeEventListener("close", handleClose);
56
- if (locked) {
57
- locked = false;
58
- unlockBodyScroll();
59
- }
44
+ unlockBodyScroll();
60
45
  if (el.open) el.close();
61
46
  };
62
47
  }, [open]);
@@ -21,11 +21,6 @@ interface UseTableResult<T> {
21
21
  pinned: ReadonlySet<string>;
22
22
  /** header select-all tri-state over the current derived rows. */
23
23
  selectAll: SelectAllState;
24
- /**
25
- * Screen-reader status text reflecting the latest change — "Sorted by {field}, {dir}",
26
- * "{n} results" (filter/search), "{n} selected". Pass to `<TableWrap announcement>` (UIX-A11Y-2).
27
- */
28
- announcement: string;
29
24
  /** cycle a column's sort; pass additive (⇧-click) to keep it as a secondary key. */
30
25
  toggleSort: (field: string, additive?: boolean) => void;
31
26
  setSort: (keys: SortKey[]) => void;
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { useCallback, useMemo, useState } from "react";
3
3
  import {
4
4
  multiSort,
5
5
  toggleSort,
@@ -34,24 +34,6 @@ function useTable(data, options = {}) {
34
34
  const toggleAllRows = useCallback(() => setSelected((s) => togglePage(s, pageIds)), [pageIds]);
35
35
  const clearSelection = useCallback(() => setSelected(/* @__PURE__ */ new Set()), []);
36
36
  const togglePin = useCallback((id) => setPinned((s) => toggleId(s, id)), []);
37
- const [announcement, setAnnouncement] = useState("");
38
- const mounted = useRef(false);
39
- useEffect(() => {
40
- if (!mounted.current) return;
41
- const key = sort[0];
42
- setAnnouncement(key ? `Sorted by ${key.field}, ${key.dir}` : "Sort cleared");
43
- }, [sort]);
44
- useEffect(() => {
45
- if (!mounted.current) return;
46
- setAnnouncement(`${rows.length} result${rows.length === 1 ? "" : "s"}`);
47
- }, [filters, query]);
48
- useEffect(() => {
49
- if (!mounted.current) return;
50
- setAnnouncement(`${selected.size} selected`);
51
- }, [selected]);
52
- useEffect(() => {
53
- mounted.current = true;
54
- }, []);
55
37
  const view = useMemo(() => ({ sort, filters, q: query }), [sort, filters, query]);
56
38
  const viewQueryString = useMemo(() => serializeView(view), [view]);
57
39
  const applyView = useCallback((queryString) => {
@@ -68,7 +50,6 @@ function useTable(data, options = {}) {
68
50
  selected,
69
51
  pinned,
70
52
  selectAll,
71
- announcement,
72
53
  toggleSort: doToggleSort,
73
54
  setSort,
74
55
  setFilters,