@tensor_1/react 2.8.0 → 2.10.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 +12 -3
- package/dist/components/Alert.js +15 -7
- package/dist/components/AppShell.d.ts +3 -1
- package/dist/components/AppShell.js +3 -1
- package/dist/components/Avatar.js +2 -1
- package/dist/components/Button.js +32 -16
- package/dist/components/Card.d.ts +3 -1
- package/dist/components/Card.js +2 -2
- package/dist/components/Chart.js +12 -3
- package/dist/components/CommandPalette.d.ts +4 -2
- package/dist/components/CommandPalette.js +66 -13
- package/dist/components/Comments.d.ts +5 -1
- package/dist/components/Comments.js +1 -1
- package/dist/components/Composer.d.ts +8 -1
- package/dist/components/Composer.js +7 -2
- package/dist/components/DetailLayout.js +1 -1
- package/dist/components/Drawer.d.ts +3 -4
- package/dist/components/Drawer.js +6 -4
- package/dist/components/Field.js +6 -3
- package/dist/components/Inbox.d.ts +13 -3
- package/dist/components/Inbox.js +61 -3
- package/dist/components/Input.d.ts +13 -2
- package/dist/components/Input.js +20 -5
- package/dist/components/Kanban.d.ts +15 -3
- package/dist/components/Kanban.js +42 -10
- package/dist/components/Meter.d.ts +3 -1
- package/dist/components/Meter.js +9 -1
- package/dist/components/Modal.d.ts +3 -5
- package/dist/components/Modal.js +6 -4
- package/dist/components/NavFavourites.js +11 -0
- package/dist/components/PageHeader.d.ts +3 -1
- package/dist/components/PageHeader.js +2 -2
- package/dist/components/Peek.d.ts +3 -4
- package/dist/components/Peek.js +9 -6
- package/dist/components/Progress.d.ts +3 -1
- package/dist/components/Progress.js +5 -3
- package/dist/components/Radio.d.ts +3 -1
- package/dist/components/Radio.js +6 -2
- package/dist/components/Sidebar.js +4 -2
- package/dist/components/Spinner.d.ts +3 -1
- package/dist/components/Spinner.js +3 -3
- package/dist/components/Stat.js +5 -1
- package/dist/components/States.d.ts +3 -1
- package/dist/components/States.js +8 -2
- package/dist/components/Table.d.ts +9 -2
- package/dist/components/Table.js +10 -6
- package/dist/components/Tabs.d.ts +13 -1
- package/dist/components/Tabs.js +78 -3
- package/dist/components/Timeline.d.ts +3 -3
- package/dist/components/Timeline.js +3 -3
- package/dist/components/Toast.js +42 -5
- package/dist/components/Tooltip.d.ts +3 -1
- package/dist/components/Tooltip.js +44 -5
- package/dist/components/Tree.d.ts +2 -0
- package/dist/components/Tree.js +26 -4
- package/dist/hooks/useDialog.d.ts +1 -1
- package/dist/hooks/useDialog.js +17 -2
- package/dist/hooks/useTable.d.ts +5 -0
- package/dist/hooks/useTable.js +20 -1
- package/dist/index.cjs +616 -162
- package/dist/index.d.cts +127 -37
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/tree-model.d.ts +4 -0
- package/dist/tree-model.js +8 -0
- package/package.json +1 -1
package/dist/chart.cjs
CHANGED
|
@@ -53,6 +53,12 @@ function readToken(name) {
|
|
|
53
53
|
function uixChartPalette() {
|
|
54
54
|
return [1, 2, 3, 4, 5, 6, 7, 8].map((i) => readToken(`--uix-chart-${i}`));
|
|
55
55
|
}
|
|
56
|
+
function motionSafe(option) {
|
|
57
|
+
if (typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
58
|
+
return { ...option, animation: false };
|
|
59
|
+
}
|
|
60
|
+
return option;
|
|
61
|
+
}
|
|
56
62
|
function Chart({
|
|
57
63
|
option,
|
|
58
64
|
title,
|
|
@@ -74,8 +80,11 @@ function Chart({
|
|
|
74
80
|
if (!el) return;
|
|
75
81
|
const chart = echarts.init(el, null, { renderer: "svg" });
|
|
76
82
|
chartRef.current = chart;
|
|
77
|
-
chart.setOption(option);
|
|
83
|
+
chart.setOption(motionSafe(option));
|
|
78
84
|
onReady?.(chart);
|
|
85
|
+
if (!ariaLabel && !title && !(tableData && tableData.length > 0)) {
|
|
86
|
+
console.warn("[uix] Chart has no text alternative \u2014 provide ariaLabel, title, or tableData.");
|
|
87
|
+
}
|
|
79
88
|
const ro = new ResizeObserver(() => chart.resize());
|
|
80
89
|
ro.observe(el);
|
|
81
90
|
return () => {
|
|
@@ -85,7 +94,7 @@ function Chart({
|
|
|
85
94
|
};
|
|
86
95
|
}, []);
|
|
87
96
|
(0, import_react.useEffect)(() => {
|
|
88
|
-
chartRef.current?.setOption(option, { notMerge: false });
|
|
97
|
+
chartRef.current?.setOption(motionSafe(option), { notMerge: false });
|
|
89
98
|
}, [option]);
|
|
90
99
|
const heightVal = typeof height === "number" ? `${height}px` : height;
|
|
91
100
|
const effectiveAriaLabel = ariaLabel ?? title ?? "Chart";
|
|
@@ -110,7 +119,7 @@ function Chart({
|
|
|
110
119
|
"table",
|
|
111
120
|
{
|
|
112
121
|
id: tableId,
|
|
113
|
-
className: "
|
|
122
|
+
className: "uix-visually-hidden",
|
|
114
123
|
"aria-label": `${effectiveAriaLabel} \u2014 data table`,
|
|
115
124
|
children: [
|
|
116
125
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: tableHeaders.map((h) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: h }, h)) }) }),
|
package/dist/components/Alert.js
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from "../cx.js";
|
|
3
3
|
function Alert({ tone, title, icon, children, className, ...props }) {
|
|
4
|
-
return /* @__PURE__ */ jsxs(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
return /* @__PURE__ */ jsxs(
|
|
5
|
+
"div",
|
|
6
|
+
{
|
|
7
|
+
className: cx("uix-alert", tone && tone !== "neutral" && `uix-alert--${tone}`, className),
|
|
8
|
+
role: tone === "danger" || tone === "warning" ? "alert" : "status",
|
|
9
|
+
...props,
|
|
10
|
+
children: [
|
|
11
|
+
icon && /* @__PURE__ */ jsx("div", { className: "uix-alert__icon", children: icon }),
|
|
12
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
13
|
+
title && /* @__PURE__ */ jsx("div", { className: "uix-alert__title", children: title }),
|
|
14
|
+
children && /* @__PURE__ */ jsx("div", { className: "uix-alert__body", children })
|
|
15
|
+
] })
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
);
|
|
11
19
|
}
|
|
12
20
|
export {
|
|
13
21
|
Alert
|
|
@@ -17,10 +17,12 @@ interface AppShellProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
17
17
|
onExitFocus?: () => void;
|
|
18
18
|
/** Drop the content gutter so a genuinely wide table runs edge-to-edge. */
|
|
19
19
|
mainBleed?: boolean;
|
|
20
|
+
/** id given to `<main>` and targeted by the skip link. Override on collision (UIX-A11Y-2). */
|
|
21
|
+
mainId?: string;
|
|
20
22
|
sidebar?: ReactNode;
|
|
21
23
|
topbar?: ReactNode;
|
|
22
24
|
children?: ReactNode;
|
|
23
25
|
}
|
|
24
|
-
declare function AppShell({ nav, collapsed, focus, onExitFocus, mainBleed, sidebar, topbar, children, className, ...props }: AppShellProps): react.JSX.Element;
|
|
26
|
+
declare function AppShell({ nav, collapsed, focus, onExitFocus, mainBleed, mainId, sidebar, topbar, children, className, ...props }: AppShellProps): react.JSX.Element;
|
|
25
27
|
|
|
26
28
|
export { AppShell, type AppShellProps, type ShellNav };
|
|
@@ -8,6 +8,7 @@ function AppShell({
|
|
|
8
8
|
focus,
|
|
9
9
|
onExitFocus,
|
|
10
10
|
mainBleed,
|
|
11
|
+
mainId = "uix-main",
|
|
11
12
|
sidebar,
|
|
12
13
|
topbar,
|
|
13
14
|
children,
|
|
@@ -32,9 +33,10 @@ function AppShell({
|
|
|
32
33
|
"data-focus": focus || void 0,
|
|
33
34
|
...props,
|
|
34
35
|
children: [
|
|
36
|
+
/* @__PURE__ */ jsx("a", { className: "uix-skiplink", href: `#${mainId}`, children: "Skip to content" }),
|
|
35
37
|
sidebar && /* @__PURE__ */ jsx("div", { className: "uix-shell__sidebar", children: sidebar }),
|
|
36
38
|
topbar && /* @__PURE__ */ jsx("div", { className: "uix-shell__topbar", children: topbar }),
|
|
37
|
-
/* @__PURE__ */ jsx("main", { className: cx("uix-shell__main", mainBleed && "uix-shell__main--bleed"), children }),
|
|
39
|
+
/* @__PURE__ */ jsx("main", { id: mainId, tabIndex: -1, className: cx("uix-shell__main", mainBleed && "uix-shell__main--bleed"), children }),
|
|
38
40
|
focus && onExitFocus && /* @__PURE__ */ jsx("div", { className: "uix-shell__focus-exit", children: /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary uix-btn--sm", onClick: onExitFocus, children: "Esc \xB7 Exit focus" }) })
|
|
39
41
|
]
|
|
40
42
|
}
|
|
@@ -3,7 +3,8 @@ import { cx } from "../cx.js";
|
|
|
3
3
|
function Avatar({ size = "md", src, alt, children, status, className, ...props }) {
|
|
4
4
|
return /* @__PURE__ */ jsxs("span", { className: cx("uix-avatar", size !== "md" && `uix-avatar--${size}`, className), ...props, children: [
|
|
5
5
|
src ? /* @__PURE__ */ jsx("img", { src, alt: alt ?? "" }) : children,
|
|
6
|
-
status && /* @__PURE__ */ jsx("span", { className: "uix-avatar__status" })
|
|
6
|
+
status && /* @__PURE__ */ jsx("span", { className: "uix-avatar__status", "aria-hidden": "true" }),
|
|
7
|
+
status && /* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: "online" })
|
|
7
8
|
] });
|
|
8
9
|
}
|
|
9
10
|
function AvatarGroup({ children, className, ...props }) {
|
|
@@ -1,24 +1,40 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef } from "react";
|
|
3
3
|
import { cx } from "../cx.js";
|
|
4
|
+
let warnedIconButton = false;
|
|
4
5
|
const Button = forwardRef(
|
|
5
|
-
({ variant = "secondary", size = "md", icon = false, loading = false, className, children, disabled, ...props }, ref) =>
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
className: cx(
|
|
10
|
-
"uix-btn",
|
|
11
|
-
`uix-btn--${variant}`,
|
|
12
|
-
size !== "md" && `uix-btn--${size}`,
|
|
13
|
-
icon && "uix-btn--icon",
|
|
14
|
-
className
|
|
15
|
-
),
|
|
16
|
-
disabled: disabled || loading,
|
|
17
|
-
"data-loading": loading || void 0,
|
|
18
|
-
...props,
|
|
19
|
-
children
|
|
6
|
+
({ variant = "secondary", size = "md", icon = false, loading = false, className, children, disabled, onClick, ...props }, ref) => {
|
|
7
|
+
if (!warnedIconButton && icon && children == null && !props["aria-label"] && !props["aria-labelledby"] && typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
|
|
8
|
+
warnedIconButton = true;
|
|
9
|
+
console.warn("uix: icon-only <Button> has no accessible name \u2014 pass aria-label or aria-labelledby.");
|
|
20
10
|
}
|
|
21
|
-
|
|
11
|
+
return /* @__PURE__ */ jsx(
|
|
12
|
+
"button",
|
|
13
|
+
{
|
|
14
|
+
ref,
|
|
15
|
+
className: cx(
|
|
16
|
+
"uix-btn",
|
|
17
|
+
`uix-btn--${variant}`,
|
|
18
|
+
size !== "md" && `uix-btn--${size}`,
|
|
19
|
+
icon && "uix-btn--icon",
|
|
20
|
+
className
|
|
21
|
+
),
|
|
22
|
+
disabled,
|
|
23
|
+
"aria-disabled": loading || void 0,
|
|
24
|
+
"aria-busy": loading || void 0,
|
|
25
|
+
"data-loading": loading || void 0,
|
|
26
|
+
onClick: loading || onClick ? (e) => {
|
|
27
|
+
if (loading) {
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
onClick?.(e);
|
|
32
|
+
} : void 0,
|
|
33
|
+
...props,
|
|
34
|
+
children
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
}
|
|
22
38
|
);
|
|
23
39
|
Button.displayName = "Button";
|
|
24
40
|
function ButtonGroup({ children, className }) {
|
|
@@ -3,11 +3,13 @@ import { HTMLAttributes, ReactNode } from 'react';
|
|
|
3
3
|
|
|
4
4
|
interface CardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
5
5
|
title?: ReactNode;
|
|
6
|
+
/** Element for the title — pick a real heading to slot the card into the page outline (UIX-A11Y-4). Default `div` keeps legacy rendering. */
|
|
7
|
+
titleAs?: 'h2' | 'h3' | 'h4' | 'div';
|
|
6
8
|
subtitle?: ReactNode;
|
|
7
9
|
headerAction?: ReactNode;
|
|
8
10
|
footer?: ReactNode;
|
|
9
11
|
children?: ReactNode;
|
|
10
12
|
}
|
|
11
|
-
declare function Card({ title, subtitle, headerAction, footer, children, className, ...props }: CardProps): react.JSX.Element;
|
|
13
|
+
declare function Card({ title, titleAs: TitleTag, subtitle, headerAction, footer, children, className, ...props }: CardProps): react.JSX.Element;
|
|
12
14
|
|
|
13
15
|
export { Card, type CardProps };
|
package/dist/components/Card.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from "../cx.js";
|
|
3
|
-
function Card({ title, subtitle, headerAction, footer, children, className, ...props }) {
|
|
3
|
+
function Card({ title, titleAs: TitleTag = "div", subtitle, headerAction, footer, children, className, ...props }) {
|
|
4
4
|
const hasHeader = title != null || subtitle != null || headerAction != null;
|
|
5
5
|
return /* @__PURE__ */ jsxs("div", { className: cx("uix-card", className), ...props, children: [
|
|
6
6
|
hasHeader && /* @__PURE__ */ jsxs("div", { className: "uix-card__header", children: [
|
|
7
7
|
/* @__PURE__ */ jsxs("div", { style: { flex: 1 }, children: [
|
|
8
|
-
title && /* @__PURE__ */ jsx(
|
|
8
|
+
title && /* @__PURE__ */ jsx(TitleTag, { className: "uix-card__title", children: title }),
|
|
9
9
|
subtitle && /* @__PURE__ */ jsx("div", { className: "uix-card__subtitle", children: subtitle })
|
|
10
10
|
] }),
|
|
11
11
|
headerAction
|
package/dist/components/Chart.js
CHANGED
|
@@ -10,6 +10,12 @@ function readToken(name) {
|
|
|
10
10
|
function uixChartPalette() {
|
|
11
11
|
return [1, 2, 3, 4, 5, 6, 7, 8].map((i) => readToken(`--uix-chart-${i}`));
|
|
12
12
|
}
|
|
13
|
+
function motionSafe(option) {
|
|
14
|
+
if (typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
15
|
+
return { ...option, animation: false };
|
|
16
|
+
}
|
|
17
|
+
return option;
|
|
18
|
+
}
|
|
13
19
|
function Chart({
|
|
14
20
|
option,
|
|
15
21
|
title,
|
|
@@ -31,8 +37,11 @@ function Chart({
|
|
|
31
37
|
if (!el) return;
|
|
32
38
|
const chart = echarts.init(el, null, { renderer: "svg" });
|
|
33
39
|
chartRef.current = chart;
|
|
34
|
-
chart.setOption(option);
|
|
40
|
+
chart.setOption(motionSafe(option));
|
|
35
41
|
onReady?.(chart);
|
|
42
|
+
if (!ariaLabel && !title && !(tableData && tableData.length > 0)) {
|
|
43
|
+
console.warn("[uix] Chart has no text alternative \u2014 provide ariaLabel, title, or tableData.");
|
|
44
|
+
}
|
|
36
45
|
const ro = new ResizeObserver(() => chart.resize());
|
|
37
46
|
ro.observe(el);
|
|
38
47
|
return () => {
|
|
@@ -42,7 +51,7 @@ function Chart({
|
|
|
42
51
|
};
|
|
43
52
|
}, []);
|
|
44
53
|
useEffect(() => {
|
|
45
|
-
chartRef.current?.setOption(option, { notMerge: false });
|
|
54
|
+
chartRef.current?.setOption(motionSafe(option), { notMerge: false });
|
|
46
55
|
}, [option]);
|
|
47
56
|
const heightVal = typeof height === "number" ? `${height}px` : height;
|
|
48
57
|
const effectiveAriaLabel = ariaLabel ?? title ?? "Chart";
|
|
@@ -67,7 +76,7 @@ function Chart({
|
|
|
67
76
|
"table",
|
|
68
77
|
{
|
|
69
78
|
id: tableId,
|
|
70
|
-
className: "
|
|
79
|
+
className: "uix-visually-hidden",
|
|
71
80
|
"aria-label": `${effectiveAriaLabel} \u2014 data table`,
|
|
72
81
|
children: [
|
|
73
82
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: tableHeaders.map((h) => /* @__PURE__ */ jsx("th", { scope: "col", children: h }, h)) }) }),
|
|
@@ -8,7 +8,9 @@ interface CommandPaletteProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* ⌘K command palette surface over `.uix-cmdk`. Presentational: mount it inside a
|
|
11
|
-
* Modal/Popover and wire search + keyboard selection in the consumer.
|
|
11
|
+
* Modal/Popover and wire search + keyboard selection in the consumer. The input is
|
|
12
|
+
* a combobox over the listbox of `CommandItem`s; spreads override every default, so
|
|
13
|
+
* consumers already passing their own roles/labels keep working.
|
|
12
14
|
*/
|
|
13
15
|
declare function CommandPalette({ inputProps, children, className, ...props }: CommandPaletteProps): react.JSX.Element;
|
|
14
16
|
interface CommandGroupProps {
|
|
@@ -25,6 +27,6 @@ interface CommandItemProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
25
27
|
children?: ReactNode;
|
|
26
28
|
}
|
|
27
29
|
/** A single command row over `.uix-cmdk__item`. */
|
|
28
|
-
declare function CommandItem({ icon, shortcut, active, children, className, ...props }: CommandItemProps): react.JSX.Element;
|
|
30
|
+
declare function CommandItem({ icon, shortcut, active, children, className, id, ...props }: CommandItemProps): react.JSX.Element;
|
|
29
31
|
|
|
30
32
|
export { CommandGroup, type CommandGroupProps, CommandItem, type CommandItemProps, CommandPalette, type CommandPaletteProps };
|
|
@@ -1,23 +1,76 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useState } from "react";
|
|
2
4
|
import { cx } from "../cx.js";
|
|
5
|
+
const CmdkContext = createContext(null);
|
|
3
6
|
function CommandPalette({ inputProps, children, className, ...props }) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
]
|
|
7
|
+
const listId = useId();
|
|
8
|
+
const [activeId, setActiveId] = useState(null);
|
|
9
|
+
const [count, setCount] = useState(0);
|
|
10
|
+
const [announced, setAnnounced] = useState("");
|
|
11
|
+
const setActive = useCallback((id, active) => {
|
|
12
|
+
setActiveId((cur) => active ? id : cur === id ? null : cur);
|
|
13
|
+
}, []);
|
|
14
|
+
const registerItem = useCallback(() => {
|
|
15
|
+
setCount((c) => c + 1);
|
|
16
|
+
return () => setCount((c) => c - 1);
|
|
17
|
+
}, []);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const t = window.setTimeout(() => setAnnounced(`${count} result${count === 1 ? "" : "s"}`), 150);
|
|
20
|
+
return () => clearTimeout(t);
|
|
21
|
+
}, [count]);
|
|
22
|
+
const ctx = useMemo(() => ({ listId, setActive, registerItem }), [listId, setActive, registerItem]);
|
|
23
|
+
return /* @__PURE__ */ jsx(CmdkContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs("div", { className: cx("uix-cmdk", className), ...props, children: [
|
|
24
|
+
/* @__PURE__ */ jsx(
|
|
25
|
+
"input",
|
|
26
|
+
{
|
|
27
|
+
className: "uix-cmdk__input",
|
|
28
|
+
role: "combobox",
|
|
29
|
+
"aria-expanded": "true",
|
|
30
|
+
"aria-autocomplete": "list",
|
|
31
|
+
"aria-controls": listId,
|
|
32
|
+
"aria-activedescendant": activeId ?? void 0,
|
|
33
|
+
"aria-label": "Command palette",
|
|
34
|
+
...inputProps
|
|
35
|
+
}
|
|
36
|
+
),
|
|
37
|
+
/* @__PURE__ */ jsx("div", { className: "uix-cmdk__list", role: "listbox", id: listId, children }),
|
|
38
|
+
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", role: "status", children: announced })
|
|
39
|
+
] }) });
|
|
8
40
|
}
|
|
9
41
|
function CommandGroup({ label, children }) {
|
|
10
|
-
|
|
11
|
-
|
|
42
|
+
const labelId = useId();
|
|
43
|
+
return /* @__PURE__ */ jsxs("div", { role: "group", "aria-labelledby": label != null ? labelId : void 0, children: [
|
|
44
|
+
label != null && /* @__PURE__ */ jsx("div", { className: "uix-cmdk__group", id: labelId, children: label }),
|
|
12
45
|
children
|
|
13
46
|
] });
|
|
14
47
|
}
|
|
15
|
-
function CommandItem({ icon, shortcut, active, children, className, ...props }) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
48
|
+
function CommandItem({ icon, shortcut, active, children, className, id, ...props }) {
|
|
49
|
+
const ctx = useContext(CmdkContext);
|
|
50
|
+
const autoId = useId();
|
|
51
|
+
const itemId = id ?? autoId;
|
|
52
|
+
useEffect(() => ctx?.registerItem(), [ctx]);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (!ctx || !active) return;
|
|
55
|
+
ctx.setActive(itemId, true);
|
|
56
|
+
return () => ctx.setActive(itemId, false);
|
|
57
|
+
}, [ctx, itemId, active]);
|
|
58
|
+
return /* @__PURE__ */ jsxs(
|
|
59
|
+
"div",
|
|
60
|
+
{
|
|
61
|
+
className: cx("uix-cmdk__item", className),
|
|
62
|
+
"data-active": active || void 0,
|
|
63
|
+
id: itemId,
|
|
64
|
+
role: "option",
|
|
65
|
+
"aria-selected": !!active,
|
|
66
|
+
...props,
|
|
67
|
+
children: [
|
|
68
|
+
icon,
|
|
69
|
+
/* @__PURE__ */ jsx("span", { children }),
|
|
70
|
+
shortcut
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
);
|
|
21
74
|
}
|
|
22
75
|
export {
|
|
23
76
|
CommandGroup,
|
|
@@ -4,7 +4,11 @@ import { HTMLAttributes, ReactNode } from 'react';
|
|
|
4
4
|
interface CommentsProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
5
|
children?: ReactNode;
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Comment thread container over `.uix-comments`. role="log" is an implicit polite
|
|
9
|
+
* live region: comments appended after initial render are announced, the initial
|
|
10
|
+
* list is not (UIX-A11Y-3). Pass `role` to override.
|
|
11
|
+
*/
|
|
8
12
|
declare function Comments({ children, className, ...props }: CommentsProps): react.JSX.Element;
|
|
9
13
|
interface CommentProps extends HTMLAttributes<HTMLDivElement> {
|
|
10
14
|
/** Usually an `<Avatar />` (the grid reserves the leading column). */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from "../cx.js";
|
|
3
3
|
function Comments({ children, className, ...props }) {
|
|
4
|
-
return /* @__PURE__ */ jsx("div", { className: cx("uix-comments", className), ...props, children });
|
|
4
|
+
return /* @__PURE__ */ jsx("div", { role: "log", className: cx("uix-comments", className), ...props, children });
|
|
5
5
|
}
|
|
6
6
|
function Comment({ avatar, author, meta, children, className, ...props }) {
|
|
7
7
|
return /* @__PURE__ */ jsxs("div", { className: cx("uix-comment", className), ...props, children: [
|
|
@@ -3,6 +3,8 @@ import { HTMLAttributes, ReactNode } from 'react';
|
|
|
3
3
|
|
|
4
4
|
interface ComposerProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
5
|
children?: ReactNode;
|
|
6
|
+
/** Accessible name for the child `<textarea>`; applied as its `aria-label`. */
|
|
7
|
+
label?: string;
|
|
6
8
|
}
|
|
7
9
|
/**
|
|
8
10
|
* Composer surface over `.uix-composer` — a single box that owns the
|
|
@@ -10,8 +12,13 @@ interface ComposerProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
10
12
|
* `<ComposerBar>` inside; the descendant `textarea` picks up the composer's
|
|
11
13
|
* padding/typography automatically, so the input never reads as a second
|
|
12
14
|
* nested box.
|
|
15
|
+
*
|
|
16
|
+
* A placeholder is not an accessible name, so the composer labels a bare child
|
|
17
|
+
* `<textarea>` with `label` (default "Add a comment"); a textarea that brings its
|
|
18
|
+
* own `aria-label`/`aria-labelledby` — or an `id` (likely tied to an external
|
|
19
|
+
* `<label htmlFor>`) — is left untouched (UIX-A11Y-3).
|
|
13
20
|
*/
|
|
14
|
-
declare function Composer({ children, className, ...props }: ComposerProps): react.JSX.Element;
|
|
21
|
+
declare function Composer({ children, className, label, ...props }: ComposerProps): react.JSX.Element;
|
|
15
22
|
interface ComposerBarProps extends HTMLAttributes<HTMLDivElement> {
|
|
16
23
|
children?: ReactNode;
|
|
17
24
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Children, cloneElement, isValidElement } from "react";
|
|
2
3
|
import { cx } from "../cx.js";
|
|
3
|
-
function Composer({ children, className, ...props }) {
|
|
4
|
-
return /* @__PURE__ */ jsx("div", { className: cx("uix-composer", className), ...props, children
|
|
4
|
+
function Composer({ children, className, label = "Add a comment", ...props }) {
|
|
5
|
+
return /* @__PURE__ */ jsx("div", { className: cx("uix-composer", className), ...props, children: Children.map(children, (child) => {
|
|
6
|
+
if (!isValidElement(child) || child.type !== "textarea") return child;
|
|
7
|
+
const p = child.props;
|
|
8
|
+
return p["aria-label"] || p["aria-labelledby"] || p.id ? child : cloneElement(child, { "aria-label": label });
|
|
9
|
+
}) });
|
|
5
10
|
}
|
|
6
11
|
function ComposerBar({ children, className, ...props }) {
|
|
7
12
|
return /* @__PURE__ */ jsx("div", { className: cx("uix-composer__bar", className), ...props, children });
|
|
@@ -3,7 +3,7 @@ import { cx } from "../cx.js";
|
|
|
3
3
|
function DetailLayout({ side, children, className, ...props }) {
|
|
4
4
|
return /* @__PURE__ */ jsxs("div", { className: cx("uix-detail", className), ...props, children: [
|
|
5
5
|
/* @__PURE__ */ jsx("div", { className: "uix-detail__main", children }),
|
|
6
|
-
side != null && /* @__PURE__ */ jsx("
|
|
6
|
+
side != null && /* @__PURE__ */ jsx("aside", { className: "uix-detail__side", children: side })
|
|
7
7
|
] });
|
|
8
8
|
}
|
|
9
9
|
export {
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
2
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
-
interface DrawerProps {
|
|
4
|
+
interface DrawerProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
|
|
5
5
|
open: boolean;
|
|
6
6
|
onClose?: () => void;
|
|
7
7
|
title?: ReactNode;
|
|
8
8
|
children?: ReactNode;
|
|
9
9
|
footer?: ReactNode;
|
|
10
|
-
className?: string;
|
|
11
10
|
}
|
|
12
|
-
declare function Drawer({ open, onClose, title, children, footer, className }: DrawerProps): react.JSX.Element;
|
|
11
|
+
declare function Drawer({ open, onClose, title, children, footer, className, ...rest }: DrawerProps): react.JSX.Element;
|
|
13
12
|
|
|
14
13
|
export { Drawer, type DrawerProps };
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from "react";
|
|
3
4
|
import { cx } from "../cx.js";
|
|
4
5
|
import { useDialog } from "../hooks/useDialog.js";
|
|
5
6
|
const CloseIcon = () => /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M4 4l8 8M12 4l-8 8" }) });
|
|
6
|
-
function Drawer({ open, onClose, title, children, footer, className }) {
|
|
7
|
-
const ref = useDialog(open);
|
|
8
|
-
|
|
7
|
+
function Drawer({ open, onClose, title, children, footer, className, ...rest }) {
|
|
8
|
+
const ref = useDialog(open, onClose);
|
|
9
|
+
const titleId = useId();
|
|
10
|
+
return /* @__PURE__ */ jsxs("dialog", { ref, className: cx("uix-drawer", className), "aria-labelledby": title ? titleId : void 0, ...rest, children: [
|
|
9
11
|
(title != null || onClose) && /* @__PURE__ */ jsxs("div", { className: "uix-drawer__header", children: [
|
|
10
|
-
title && /* @__PURE__ */ jsx("
|
|
12
|
+
title && /* @__PURE__ */ jsx("h2", { id: titleId, style: { fontWeight: 600, fontSize: "var(--uix-text-h3)", fontFamily: "inherit", letterSpacing: "inherit", lineHeight: "inherit" }, children: title }),
|
|
11
13
|
onClose && /* @__PURE__ */ jsx(
|
|
12
14
|
"button",
|
|
13
15
|
{
|
package/dist/components/Field.js
CHANGED
|
@@ -5,14 +5,17 @@ function Field({ label, hint, error, success, required, children, className, htm
|
|
|
5
5
|
const uid = useId();
|
|
6
6
|
const msgId = `${uid}-msg`;
|
|
7
7
|
const describedBy = error || success || hint ? msgId : void 0;
|
|
8
|
+
const controlId = isValidElement(children) ? children.props.id ?? `${uid}-ctl` : void 0;
|
|
8
9
|
const control = isValidElement(children) ? cloneElement(children, {
|
|
10
|
+
id: controlId,
|
|
9
11
|
"aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0,
|
|
10
|
-
"aria-invalid": error ? true : children.props["aria-invalid"]
|
|
12
|
+
"aria-invalid": error ? true : children.props["aria-invalid"],
|
|
13
|
+
"aria-required": children.props["aria-required"] ?? (required || void 0)
|
|
11
14
|
}) : children;
|
|
12
15
|
return /* @__PURE__ */ jsxs("div", { className: cx("uix-field", className), children: [
|
|
13
|
-
label && /* @__PURE__ */ jsx("label", { className: "uix-field__label", htmlFor, "data-required": required || void 0, children: label }),
|
|
16
|
+
label && /* @__PURE__ */ jsx("label", { className: "uix-field__label", htmlFor: htmlFor ?? controlId, "data-required": required || void 0, children: label }),
|
|
14
17
|
control,
|
|
15
|
-
/* @__PURE__ */ jsx("div", { className: "uix-field__msg", id: msgId, children: error ? /* @__PURE__ */ jsx("span", { className: "uix-field__error", role: "alert", children: error }) : success ? /* @__PURE__ */ jsx("span", { className: "uix-field__success", children: success }) : hint ? /* @__PURE__ */ jsx("span", { className: "uix-field__hint", children: hint }) : null })
|
|
18
|
+
/* @__PURE__ */ jsx("div", { className: "uix-field__msg", id: msgId, children: error ? /* @__PURE__ */ jsx("span", { className: "uix-field__error", role: "alert", children: error }) : success ? /* @__PURE__ */ jsx("span", { className: "uix-field__success", role: "status", children: success }) : hint ? /* @__PURE__ */ jsx("span", { className: "uix-field__hint", children: hint }) : null })
|
|
16
19
|
] });
|
|
17
20
|
}
|
|
18
21
|
export {
|
|
@@ -2,14 +2,24 @@ import * as react from 'react';
|
|
|
2
2
|
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
3
|
|
|
4
4
|
interface InboxProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/**
|
|
6
|
+
* Which pane a narrow viewport (<700px) shows: 'list' (default, matches the old
|
|
7
|
+
* behaviour) or 'detail'. Consumers should render a Back control in the detail pane
|
|
8
|
+
* on narrow viewports to return to the list (UIX-A11Y-2).
|
|
9
|
+
*/
|
|
10
|
+
view?: 'list' | 'detail';
|
|
5
11
|
children?: ReactNode;
|
|
6
12
|
}
|
|
7
13
|
/** Two-pane inbox master-detail container backed by `.uix-inbox`. Place `<InboxList>` and `<InboxDetail>` as children. */
|
|
8
|
-
declare function Inbox({ children, className, ...props }: InboxProps): react.JSX.Element;
|
|
14
|
+
declare function Inbox({ view, children, className, ...props }: InboxProps): react.JSX.Element;
|
|
9
15
|
interface InboxListProps extends HTMLAttributes<HTMLDivElement> {
|
|
10
16
|
children?: ReactNode;
|
|
11
17
|
}
|
|
12
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Left-pane item list. Place `<InboxItem>` children here. A keyboard listbox
|
|
20
|
+
* (UIX-A11Y-2): the list is the single tab stop; ArrowUp/ArrowDown/Home/End move the
|
|
21
|
+
* active option via `aria-activedescendant`, Enter/Space click it.
|
|
22
|
+
*/
|
|
13
23
|
declare function InboxList({ children, className, ...props }: InboxListProps): react.JSX.Element;
|
|
14
24
|
interface InboxItemProps extends HTMLAttributes<HTMLDivElement> {
|
|
15
25
|
/** Primary subject / title line. */
|
|
@@ -25,7 +35,7 @@ interface InboxItemProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
25
35
|
children?: ReactNode;
|
|
26
36
|
}
|
|
27
37
|
/** A single inbox row with subject, preview excerpt, optional leading slot, and unread/selected states. */
|
|
28
|
-
declare function InboxItem({ subject, preview, leading, unread, selected, children, className, ...props }: InboxItemProps): react.JSX.Element;
|
|
38
|
+
declare function InboxItem({ subject, preview, leading, unread, selected, children, className, id, ...props }: InboxItemProps): react.JSX.Element;
|
|
29
39
|
interface InboxDetailProps extends HTMLAttributes<HTMLDivElement> {
|
|
30
40
|
children?: ReactNode;
|
|
31
41
|
}
|
package/dist/components/Inbox.js
CHANGED
|
@@ -1,11 +1,63 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useId, useMemo, useRef, useState } from "react";
|
|
3
4
|
import { cx } from "../cx.js";
|
|
4
|
-
function Inbox({ children, className, ...props }) {
|
|
5
|
-
return /* @__PURE__ */ jsx("div", { className: cx("uix-inbox", className), ...props, children });
|
|
5
|
+
function Inbox({ view, children, className, ...props }) {
|
|
6
|
+
return /* @__PURE__ */ jsx("div", { className: cx("uix-inbox", className), "data-view": view, ...props, children });
|
|
6
7
|
}
|
|
8
|
+
const InboxListCtx = createContext({});
|
|
7
9
|
function InboxList({ children, className, ...props }) {
|
|
8
|
-
|
|
10
|
+
const listRef = useRef(null);
|
|
11
|
+
const [activeId, setActiveId] = useState(void 0);
|
|
12
|
+
const ctx = useMemo(() => ({ activeId }), [activeId]);
|
|
13
|
+
const onKeyDown = (e) => {
|
|
14
|
+
const opts = Array.from(listRef.current?.querySelectorAll('[role="option"]') ?? []);
|
|
15
|
+
if (opts.length === 0) return;
|
|
16
|
+
const idx = opts.findIndex((el2) => el2.id === activeId);
|
|
17
|
+
let next;
|
|
18
|
+
switch (e.key) {
|
|
19
|
+
case "ArrowDown":
|
|
20
|
+
next = Math.min(idx + 1, opts.length - 1);
|
|
21
|
+
break;
|
|
22
|
+
case "ArrowUp":
|
|
23
|
+
next = idx < 0 ? 0 : Math.max(idx - 1, 0);
|
|
24
|
+
break;
|
|
25
|
+
case "Home":
|
|
26
|
+
next = 0;
|
|
27
|
+
break;
|
|
28
|
+
case "End":
|
|
29
|
+
next = opts.length - 1;
|
|
30
|
+
break;
|
|
31
|
+
case "Enter":
|
|
32
|
+
case " ":
|
|
33
|
+
if (idx >= 0) {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
opts[idx]?.click();
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
default:
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
const el = opts[next];
|
|
43
|
+
if (!el || !el.id) return;
|
|
44
|
+
setActiveId(el.id);
|
|
45
|
+
el.scrollIntoView({ block: "nearest" });
|
|
46
|
+
};
|
|
47
|
+
return /* @__PURE__ */ jsx(InboxListCtx.Provider, { value: ctx, children: /* @__PURE__ */ jsx(
|
|
48
|
+
"div",
|
|
49
|
+
{
|
|
50
|
+
ref: listRef,
|
|
51
|
+
className: cx("uix-inbox__list", className),
|
|
52
|
+
role: "listbox",
|
|
53
|
+
tabIndex: 0,
|
|
54
|
+
"aria-label": "Inbox",
|
|
55
|
+
"aria-activedescendant": activeId,
|
|
56
|
+
onKeyDown,
|
|
57
|
+
...props,
|
|
58
|
+
children
|
|
59
|
+
}
|
|
60
|
+
) });
|
|
9
61
|
}
|
|
10
62
|
function InboxItem({
|
|
11
63
|
subject,
|
|
@@ -15,13 +67,19 @@ function InboxItem({
|
|
|
15
67
|
selected,
|
|
16
68
|
children,
|
|
17
69
|
className,
|
|
70
|
+
id,
|
|
18
71
|
...props
|
|
19
72
|
}) {
|
|
73
|
+
const autoId = useId();
|
|
74
|
+
const itemId = id ?? autoId;
|
|
75
|
+
const { activeId } = useContext(InboxListCtx);
|
|
20
76
|
return /* @__PURE__ */ jsxs(
|
|
21
77
|
"div",
|
|
22
78
|
{
|
|
23
79
|
role: "option",
|
|
80
|
+
id: itemId,
|
|
24
81
|
"aria-selected": selected ?? false,
|
|
82
|
+
"data-active": activeId === itemId ? "" : void 0,
|
|
25
83
|
className: cx("uix-inbox__item", className),
|
|
26
84
|
...unread ? { "data-unread": "" } : {},
|
|
27
85
|
...props,
|