@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.
- package/dist/chart.cjs +3 -12
- package/dist/components/Alert.js +7 -15
- package/dist/components/AppShell.d.ts +1 -3
- package/dist/components/AppShell.js +1 -3
- package/dist/components/Avatar.js +1 -2
- package/dist/components/Button.js +16 -32
- package/dist/components/Card.d.ts +1 -3
- package/dist/components/Card.js +2 -2
- package/dist/components/Chart.js +3 -12
- package/dist/components/CommandPalette.d.ts +2 -4
- package/dist/components/CommandPalette.js +13 -66
- package/dist/components/Comments.d.ts +1 -5
- package/dist/components/Comments.js +1 -1
- package/dist/components/Composer.d.ts +1 -8
- package/dist/components/Composer.js +2 -7
- package/dist/components/DetailLayout.js +1 -1
- package/dist/components/Drawer.d.ts +4 -3
- package/dist/components/Drawer.js +4 -6
- package/dist/components/Field.js +3 -6
- package/dist/components/Inbox.d.ts +3 -13
- package/dist/components/Inbox.js +3 -61
- package/dist/components/Input.d.ts +2 -13
- package/dist/components/Input.js +5 -20
- package/dist/components/Kanban.d.ts +3 -15
- package/dist/components/Kanban.js +10 -42
- package/dist/components/Meter.d.ts +1 -3
- package/dist/components/Meter.js +1 -9
- package/dist/components/Modal.d.ts +5 -3
- package/dist/components/Modal.js +4 -6
- package/dist/components/NavFavourites.js +0 -11
- package/dist/components/PageHeader.d.ts +1 -3
- package/dist/components/PageHeader.js +2 -2
- package/dist/components/Peek.d.ts +4 -3
- package/dist/components/Peek.js +6 -9
- package/dist/components/Progress.d.ts +1 -3
- package/dist/components/Progress.js +3 -5
- package/dist/components/Radio.d.ts +1 -3
- package/dist/components/Radio.js +2 -6
- package/dist/components/Sidebar.js +2 -4
- package/dist/components/Spinner.d.ts +1 -3
- package/dist/components/Spinner.js +3 -3
- package/dist/components/Stat.js +1 -5
- package/dist/components/States.d.ts +1 -3
- package/dist/components/States.js +2 -8
- package/dist/components/Table.d.ts +2 -9
- package/dist/components/Table.js +6 -10
- package/dist/components/Tabs.d.ts +1 -13
- package/dist/components/Tabs.js +3 -78
- package/dist/components/Timeline.d.ts +3 -3
- package/dist/components/Timeline.js +3 -3
- package/dist/components/Toast.js +5 -42
- package/dist/components/Tooltip.d.ts +1 -3
- package/dist/components/Tooltip.js +5 -44
- package/dist/components/Tree.d.ts +0 -2
- package/dist/components/Tree.js +4 -26
- package/dist/hooks/useDialog.d.ts +1 -1
- package/dist/hooks/useDialog.js +2 -17
- package/dist/hooks/useTable.d.ts +0 -5
- package/dist/hooks/useTable.js +1 -20
- package/dist/index.cjs +162 -616
- package/dist/index.d.cts +37 -127
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -2
- package/dist/tree-model.d.ts +0 -4
- package/dist/tree-model.js +0 -8
- package/package.json +1 -1
package/dist/components/Table.js
CHANGED
|
@@ -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({
|
|
5
|
-
return /* @__PURE__ */
|
|
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
|
|
44
|
+
onClick: sortable ? onSort ?? onClick : onClick,
|
|
48
45
|
...props,
|
|
49
|
-
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
|
-
"
|
|
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,
|
|
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,
|
|
19
|
+
export { Tab, type TabProps, Tabs, type TabsProps };
|
package/dist/components/Tabs.js
CHANGED
|
@@ -1,66 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx
|
|
3
|
-
import {
|
|
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
|
-
|
|
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<
|
|
4
|
+
interface TimelineProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
5
|
children?: ReactNode;
|
|
6
6
|
}
|
|
7
|
-
/** Activity timeline container over `.uix-timeline`.
|
|
7
|
+
/** Activity timeline container over `.uix-timeline`. */
|
|
8
8
|
declare function Timeline({ children, className, ...props }: TimelineProps): react.JSX.Element;
|
|
9
|
-
interface TimelineItemProps extends HTMLAttributes<
|
|
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("
|
|
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("
|
|
8
|
-
/* @__PURE__ */ jsxs("div", { className: "uix-timeline__rail",
|
|
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
|
] }),
|
package/dist/components/Toast.js
CHANGED
|
@@ -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:
|
|
34
|
-
"aria-live":
|
|
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",
|
|
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:
|
|
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
|
-
|
|
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 {
|
|
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":
|
|
74
|
-
tabIndex: single ? void 0 : 0,
|
|
47
|
+
"aria-describedby": id,
|
|
75
48
|
onMouseEnter: show,
|
|
76
|
-
onMouseLeave:
|
|
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
|
-
|
|
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
|
}
|
package/dist/components/Tree.js
CHANGED
|
@@ -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", {
|
|
264
|
-
rows.map((f
|
|
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
|
|
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", {
|
|
274
|
+
win.padBottom > 0 && /* @__PURE__ */ jsx("li", { "aria-hidden": "true", style: { height: win.padBottom } })
|
|
297
275
|
]
|
|
298
276
|
}
|
|
299
277
|
);
|
package/dist/hooks/useDialog.js
CHANGED
|
@@ -30,10 +30,8 @@ function unlockBodyScroll() {
|
|
|
30
30
|
saved = null;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
function useDialog(open
|
|
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
|
-
|
|
56
|
-
if (locked) {
|
|
57
|
-
locked = false;
|
|
58
|
-
unlockBodyScroll();
|
|
59
|
-
}
|
|
44
|
+
unlockBodyScroll();
|
|
60
45
|
if (el.open) el.close();
|
|
61
46
|
};
|
|
62
47
|
}, [open]);
|
package/dist/hooks/useTable.d.ts
CHANGED
|
@@ -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;
|
package/dist/hooks/useTable.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useCallback,
|
|
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,
|