@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { InputHTMLAttributes, ReactNode, AriaAttributes } from 'react';
|
|
3
3
|
|
|
4
4
|
interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
5
5
|
size?: 'sm' | 'md';
|
|
@@ -12,7 +12,18 @@ interface InputGroupProps {
|
|
|
12
12
|
leadingIcon?: ReactNode;
|
|
13
13
|
trailingIcon?: ReactNode;
|
|
14
14
|
className?: string;
|
|
15
|
+
/** Injected by `<Field>` when it wraps the group; forwarded onto the child control. */
|
|
16
|
+
id?: string;
|
|
17
|
+
'aria-describedby'?: string;
|
|
18
|
+
'aria-invalid'?: AriaAttributes['aria-invalid'];
|
|
19
|
+
'aria-required'?: AriaAttributes['aria-required'];
|
|
15
20
|
}
|
|
16
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Icon-adorned wrapper for a child `<Input>`. The control is passed as `children`, so
|
|
23
|
+
* `<Field>`'s injected `id`/`aria-*` land on this wrapper — they're forwarded onto the
|
|
24
|
+
* single child element (the child's own props win) so label/message association survives
|
|
25
|
+
* the extra div (UIX-A11Y-3).
|
|
26
|
+
*/
|
|
27
|
+
declare function InputGroup({ children, leadingIcon, trailingIcon, className, id, 'aria-describedby': describedBy, 'aria-invalid': invalid, 'aria-required': required, }: InputGroupProps): react.JSX.Element;
|
|
17
28
|
|
|
18
29
|
export { Input, InputGroup, type InputGroupProps, type InputProps };
|
package/dist/components/Input.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef } from "react";
|
|
2
|
+
import { cloneElement, forwardRef, isValidElement } from "react";
|
|
3
3
|
import { cx } from "../cx.js";
|
|
4
4
|
const Input = forwardRef(
|
|
5
5
|
({ size = "md", invalid, valid, className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
@@ -14,7 +14,22 @@ const Input = forwardRef(
|
|
|
14
14
|
)
|
|
15
15
|
);
|
|
16
16
|
Input.displayName = "Input";
|
|
17
|
-
function InputGroup({
|
|
17
|
+
function InputGroup({
|
|
18
|
+
children,
|
|
19
|
+
leadingIcon,
|
|
20
|
+
trailingIcon,
|
|
21
|
+
className,
|
|
22
|
+
id,
|
|
23
|
+
"aria-describedby": describedBy,
|
|
24
|
+
"aria-invalid": invalid,
|
|
25
|
+
"aria-required": required
|
|
26
|
+
}) {
|
|
27
|
+
const control = isValidElement(children) && (id ?? describedBy ?? invalid ?? required) != null ? cloneElement(children, {
|
|
28
|
+
id: children.props.id ?? id,
|
|
29
|
+
"aria-describedby": children.props["aria-describedby"] ?? describedBy,
|
|
30
|
+
"aria-invalid": children.props["aria-invalid"] ?? invalid,
|
|
31
|
+
"aria-required": children.props["aria-required"] ?? required
|
|
32
|
+
}) : children;
|
|
18
33
|
return /* @__PURE__ */ jsxs(
|
|
19
34
|
"div",
|
|
20
35
|
{
|
|
@@ -25,9 +40,9 @@ function InputGroup({ children, leadingIcon, trailingIcon, className }) {
|
|
|
25
40
|
className
|
|
26
41
|
),
|
|
27
42
|
children: [
|
|
28
|
-
leadingIcon && /* @__PURE__ */ jsx("span", { className: "uix-input-group__icon", children: leadingIcon }),
|
|
29
|
-
|
|
30
|
-
trailingIcon && /* @__PURE__ */ jsx("span", { className: "uix-input-group__icon uix-input-group__icon--trailing", children: trailingIcon })
|
|
43
|
+
leadingIcon && /* @__PURE__ */ jsx("span", { className: "uix-input-group__icon", "aria-hidden": "true", children: leadingIcon }),
|
|
44
|
+
control,
|
|
45
|
+
trailingIcon && /* @__PURE__ */ jsx("span", { className: "uix-input-group__icon uix-input-group__icon--trailing", "aria-hidden": "true", children: trailingIcon })
|
|
31
46
|
]
|
|
32
47
|
}
|
|
33
48
|
);
|
|
@@ -2,20 +2,32 @@ import * as react from 'react';
|
|
|
2
2
|
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
3
|
|
|
4
4
|
interface KanbanProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/**
|
|
6
|
+
* Polite live-region text for keyboard moves (UIX-A11Y-2). `onMove` implementations
|
|
7
|
+
* should set it, e.g. "Card X moved to Doing, position 2 of 4".
|
|
8
|
+
*/
|
|
9
|
+
announcement?: string;
|
|
5
10
|
children?: ReactNode;
|
|
6
11
|
}
|
|
7
|
-
declare function Kanban({ children, className, ...props }: KanbanProps): react.JSX.Element;
|
|
12
|
+
declare function Kanban({ announcement, children, className, ...props }: KanbanProps): react.JSX.Element;
|
|
8
13
|
interface KanbanColumnProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
9
14
|
title?: ReactNode;
|
|
10
15
|
count?: number;
|
|
11
16
|
children?: ReactNode;
|
|
12
17
|
}
|
|
13
18
|
declare function KanbanColumn({ title, count, children, className, ...props }: KanbanColumnProps): react.JSX.Element;
|
|
19
|
+
type KanbanMoveDirection = 'up' | 'down' | 'left' | 'right';
|
|
14
20
|
interface KanbanCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
15
21
|
title?: ReactNode;
|
|
16
22
|
meta?: ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Keyboard move contract (UIX-A11Y-2): when provided, Alt+Arrow on the focused card
|
|
25
|
+
* calls it — the same accelerator convention as NavFavourites row moves. Pair with
|
|
26
|
+
* the board-level `announcement` so SR users hear the result.
|
|
27
|
+
*/
|
|
28
|
+
onMove?: (dir: KanbanMoveDirection) => void;
|
|
17
29
|
children?: ReactNode;
|
|
18
30
|
}
|
|
19
|
-
declare function KanbanCard({ title, meta, children, className, ...props }: KanbanCardProps): react.JSX.Element;
|
|
31
|
+
declare function KanbanCard({ title, meta, children, className, onMove, onKeyDown, ...props }: KanbanCardProps): react.JSX.Element;
|
|
20
32
|
|
|
21
|
-
export { Kanban, KanbanCard, type KanbanCardProps, KanbanColumn, type KanbanColumnProps, type KanbanProps };
|
|
33
|
+
export { Kanban, KanbanCard, type KanbanCardProps, KanbanColumn, type KanbanColumnProps, type KanbanMoveDirection, type KanbanProps };
|
|
@@ -1,23 +1,55 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from "react";
|
|
2
4
|
import { cx } from "../cx.js";
|
|
3
|
-
function Kanban({ children, className, ...props }) {
|
|
4
|
-
return /* @__PURE__ */
|
|
5
|
+
function Kanban({ announcement, children, className, ...props }) {
|
|
6
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("uix-kanban", className), ...props, children: [
|
|
7
|
+
announcement != null && /* @__PURE__ */ jsx("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
|
|
8
|
+
children
|
|
9
|
+
] });
|
|
5
10
|
}
|
|
6
11
|
function KanbanColumn({ title, count, children, className, ...props }) {
|
|
12
|
+
const headId = useId();
|
|
13
|
+
const hasHead = title != null || count != null;
|
|
7
14
|
return /* @__PURE__ */ jsxs("div", { className: cx("uix-kanban__col", className), ...props, children: [
|
|
8
|
-
|
|
15
|
+
hasHead && /* @__PURE__ */ jsxs("div", { className: "uix-kanban__head", id: headId, children: [
|
|
9
16
|
title,
|
|
10
17
|
count != null && /* @__PURE__ */ jsx("span", { className: "uix-kanban__count", children: count })
|
|
11
18
|
] }),
|
|
12
|
-
/* @__PURE__ */ jsx("div", { className: "uix-kanban__body", children })
|
|
19
|
+
/* @__PURE__ */ jsx("div", { className: "uix-kanban__body", role: "list", "aria-labelledby": hasHead ? headId : void 0, children })
|
|
13
20
|
] });
|
|
14
21
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
const MOVE_KEYS = {
|
|
23
|
+
ArrowUp: "up",
|
|
24
|
+
ArrowDown: "down",
|
|
25
|
+
ArrowLeft: "left",
|
|
26
|
+
ArrowRight: "right"
|
|
27
|
+
};
|
|
28
|
+
function KanbanCard({ title, meta, children, className, onMove, onKeyDown, ...props }) {
|
|
29
|
+
const handleKeyDown = (e) => {
|
|
30
|
+
onKeyDown?.(e);
|
|
31
|
+
if (!onMove || !e.altKey || e.defaultPrevented) return;
|
|
32
|
+
const dir = MOVE_KEYS[e.key];
|
|
33
|
+
if (dir) {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
onMove(dir);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
return /* @__PURE__ */ jsxs(
|
|
39
|
+
"div",
|
|
40
|
+
{
|
|
41
|
+
role: "listitem",
|
|
42
|
+
tabIndex: 0,
|
|
43
|
+
className: cx("uix-kanban__card", className),
|
|
44
|
+
onKeyDown: handleKeyDown,
|
|
45
|
+
...props,
|
|
46
|
+
children: [
|
|
47
|
+
title && /* @__PURE__ */ jsx("div", { className: "uix-kanban__card-title", children: title }),
|
|
48
|
+
meta && /* @__PURE__ */ jsx("div", { className: "uix-kanban__card-meta", children: meta }),
|
|
49
|
+
children
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
);
|
|
21
53
|
}
|
|
22
54
|
export {
|
|
23
55
|
Kanban,
|
|
@@ -8,8 +8,10 @@ interface MeterProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
8
8
|
value?: number;
|
|
9
9
|
/** Threshold tone applied via `data-tone` on the fill element. `success` is the implicit default (no attribute). */
|
|
10
10
|
tone?: MeterTone;
|
|
11
|
+
/** Accessible name — rendered as `aria-label` (an explicit `aria-label` prop wins). */
|
|
12
|
+
label?: string;
|
|
11
13
|
}
|
|
12
14
|
/** Horizontal utilization / threshold bar backed by `.uix-meter`. */
|
|
13
|
-
declare function Meter({ value, tone, className, ...props }: MeterProps): react.JSX.Element;
|
|
15
|
+
declare function Meter({ value, tone, label, className, ...props }: MeterProps): react.JSX.Element;
|
|
14
16
|
|
|
15
17
|
export { Meter, type MeterProps, type MeterTone };
|
package/dist/components/Meter.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { cx } from "../cx.js";
|
|
4
|
-
|
|
4
|
+
const toneText = {
|
|
5
|
+
warning: "warning",
|
|
6
|
+
danger: "critical",
|
|
7
|
+
attention: "needs attention",
|
|
8
|
+
overdue: "overdue"
|
|
9
|
+
};
|
|
10
|
+
function Meter({ value = 0, tone, label, className, ...props }) {
|
|
5
11
|
const pct = Math.max(0, Math.min(100, value));
|
|
6
12
|
return /* @__PURE__ */ jsx(
|
|
7
13
|
"div",
|
|
@@ -10,6 +16,8 @@ function Meter({ value = 0, tone, className, ...props }) {
|
|
|
10
16
|
"aria-valuenow": pct,
|
|
11
17
|
"aria-valuemin": 0,
|
|
12
18
|
"aria-valuemax": 100,
|
|
19
|
+
"aria-valuetext": tone && tone !== "success" ? `${pct}%, ${toneText[tone]}` : void 0,
|
|
20
|
+
"aria-label": label,
|
|
13
21
|
className: cx("uix-meter", className),
|
|
14
22
|
...props,
|
|
15
23
|
children: /* @__PURE__ */ jsx(
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
-
interface ModalProps {
|
|
4
|
+
interface ModalProps 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
|
-
style?: CSSProperties;
|
|
12
10
|
}
|
|
13
|
-
declare function Modal({ open, onClose, title, children, footer, className,
|
|
11
|
+
declare function Modal({ open, onClose, title, children, footer, className, ...rest }: ModalProps): react.JSX.Element;
|
|
14
12
|
|
|
15
13
|
export { Modal, type ModalProps };
|
package/dist/components/Modal.js
CHANGED
|
@@ -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 Modal({ open, onClose, title, children, footer, className,
|
|
7
|
-
const ref = useDialog(open);
|
|
8
|
-
|
|
7
|
+
function Modal({ 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-dialog", className), "aria-labelledby": title ? titleId : void 0, ...rest, children: [
|
|
9
11
|
(title != null || onClose) && /* @__PURE__ */ jsxs("div", { className: "uix-dialog__header", children: [
|
|
10
|
-
title && /* @__PURE__ */ jsx("
|
|
12
|
+
title && /* @__PURE__ */ jsx("h2", { className: "uix-dialog__title", id: titleId, children: title }),
|
|
11
13
|
onClose && /* @__PURE__ */ jsx("button", { className: "uix-dialog__close", onClick: onClose, "aria-label": "Close dialog", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
|
|
12
14
|
] }),
|
|
13
15
|
children != null && /* @__PURE__ */ jsx("div", { className: "uix-dialog__body", children }),
|
|
@@ -67,6 +67,7 @@ function NavFavourites({
|
|
|
67
67
|
const regionId = useId();
|
|
68
68
|
const headerRef = useRef(null);
|
|
69
69
|
const regionRef = useRef(null);
|
|
70
|
+
const [announcement, setAnnouncement] = useState("");
|
|
70
71
|
const focusAfterRef = useRef(null);
|
|
71
72
|
const triggerRefs = useRef(/* @__PURE__ */ new Map());
|
|
72
73
|
const order = items.map((i) => i.id);
|
|
@@ -96,6 +97,8 @@ function NavFavourites({
|
|
|
96
97
|
if (it === void 0) return;
|
|
97
98
|
reordered.splice(next, 0, it);
|
|
98
99
|
focusAfterRef.current = id;
|
|
100
|
+
const moved = items.find((i) => i.id === id);
|
|
101
|
+
if (moved) setAnnouncement(`${moved.label} moved to position ${next + 1} of ${order.length}`);
|
|
99
102
|
onReorder(reordered);
|
|
100
103
|
};
|
|
101
104
|
const remove = (id) => {
|
|
@@ -105,6 +108,7 @@ function NavFavourites({
|
|
|
105
108
|
onRemove(id);
|
|
106
109
|
};
|
|
107
110
|
return /* @__PURE__ */ jsxs("div", { className: cx("uix-favourites", className), style: { marginBottom: "var(--uix-space-1)" }, children: [
|
|
111
|
+
/* @__PURE__ */ jsx("div", { role: "status", className: "uix-visually-hidden", children: announcement }),
|
|
108
112
|
/* @__PURE__ */ jsxs(
|
|
109
113
|
"button",
|
|
110
114
|
{
|
|
@@ -208,6 +212,12 @@ function FavouriteRow({
|
|
|
208
212
|
} else if (e.key === "ArrowUp") {
|
|
209
213
|
e.preventDefault();
|
|
210
214
|
menuItems[(currentIdx - 1 + menuItems.length) % menuItems.length]?.focus();
|
|
215
|
+
} else if (e.key === "Home") {
|
|
216
|
+
e.preventDefault();
|
|
217
|
+
menuItems[0]?.focus();
|
|
218
|
+
} else if (e.key === "End") {
|
|
219
|
+
e.preventDefault();
|
|
220
|
+
menuItems[menuItems.length - 1]?.focus();
|
|
211
221
|
} else if (e.key === "Escape") {
|
|
212
222
|
e.preventDefault();
|
|
213
223
|
closeAndReturnFocus();
|
|
@@ -288,6 +298,7 @@ function FavouriteRow({
|
|
|
288
298
|
{
|
|
289
299
|
id: menuId,
|
|
290
300
|
role: "menu",
|
|
301
|
+
"aria-label": optionsTemplate.replace("{label}", item.label),
|
|
291
302
|
onKeyDown: onMenuKeyDown,
|
|
292
303
|
style: {
|
|
293
304
|
position: "absolute",
|
|
@@ -4,6 +4,8 @@ import { HTMLAttributes, ReactNode } from 'react';
|
|
|
4
4
|
interface PageHeaderProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
|
5
5
|
eyebrow?: ReactNode;
|
|
6
6
|
title: ReactNode;
|
|
7
|
+
/** Heading level for the title — drop to `h2` when the page already owns an `h1` (UIX-A11Y-4). */
|
|
8
|
+
as?: 'h1' | 'h2';
|
|
7
9
|
/** Supporting copy under the title. */
|
|
8
10
|
subtitle?: ReactNode;
|
|
9
11
|
/** Right-aligned action area (buttons, menus). */
|
|
@@ -13,6 +15,6 @@ interface PageHeaderProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
|
|
13
15
|
* Page header — eyebrow / title / subtitle + actions row, over `.uix-page-header`.
|
|
14
16
|
* The hairline + breathing room below come from the component CSS.
|
|
15
17
|
*/
|
|
16
|
-
declare function PageHeader({ eyebrow, title, subtitle, actions, className, ...props }: PageHeaderProps): react.JSX.Element;
|
|
18
|
+
declare function PageHeader({ eyebrow, title, subtitle, actions, as: TitleTag, className, ...props }: PageHeaderProps): react.JSX.Element;
|
|
17
19
|
|
|
18
20
|
export { PageHeader, type PageHeaderProps };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from "../cx.js";
|
|
3
|
-
function PageHeader({ eyebrow, title, subtitle, actions, className, ...props }) {
|
|
3
|
+
function PageHeader({ eyebrow, title, subtitle, actions, as: TitleTag = "h1", className, ...props }) {
|
|
4
4
|
return /* @__PURE__ */ jsxs("header", { className: cx("uix-page-header", className), ...props, children: [
|
|
5
5
|
/* @__PURE__ */ jsxs("div", { className: "uix-page-header__titles", children: [
|
|
6
6
|
eyebrow != null && /* @__PURE__ */ jsx("div", { className: "uix-page-header__eyebrow", children: eyebrow }),
|
|
7
|
-
/* @__PURE__ */ jsx(
|
|
7
|
+
/* @__PURE__ */ jsx(TitleTag, { className: "uix-page-header__title", children: title }),
|
|
8
8
|
subtitle != null && /* @__PURE__ */ jsx("p", { className: "uix-page-header__subtitle", children: subtitle })
|
|
9
9
|
] }),
|
|
10
10
|
actions != null && /* @__PURE__ */ jsx("div", { className: "uix-page-header__actions", children: actions })
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
2
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
-
interface PeekProps {
|
|
4
|
+
interface PeekProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
|
|
5
5
|
open: boolean;
|
|
6
6
|
onClose?: () => void;
|
|
7
7
|
title?: ReactNode;
|
|
@@ -10,8 +10,7 @@ interface PeekProps {
|
|
|
10
10
|
onNavPrev?: () => void;
|
|
11
11
|
onNavNext?: () => void;
|
|
12
12
|
hint?: string;
|
|
13
|
-
className?: string;
|
|
14
13
|
}
|
|
15
|
-
declare function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className }: PeekProps): react.JSX.Element;
|
|
14
|
+
declare function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className, onClick, ...rest }: PeekProps): react.JSX.Element;
|
|
16
15
|
|
|
17
16
|
export { Peek, type PeekProps };
|
package/dist/components/Peek.js
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
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
7
|
const ChevronUp = () => /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M2 8l4-4 4 4" }) });
|
|
7
8
|
const ChevronDown = () => /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M2 4l4 4 4-4" }) });
|
|
8
|
-
function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className }) {
|
|
9
|
-
const ref = useDialog(open);
|
|
9
|
+
function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className, onClick, ...rest }) {
|
|
10
|
+
const ref = useDialog(open, onClose);
|
|
10
11
|
const hasNav = onNavPrev != null || onNavNext != null;
|
|
12
|
+
const titleId = useId();
|
|
11
13
|
const onBackdropClick = (e) => {
|
|
14
|
+
onClick?.(e);
|
|
12
15
|
if (!onClose || e.target !== e.currentTarget) return;
|
|
13
16
|
const r = e.currentTarget.getBoundingClientRect();
|
|
14
17
|
if (r.width === 0) return;
|
|
15
18
|
const outside = e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom;
|
|
16
19
|
if (outside) onClose();
|
|
17
20
|
};
|
|
18
|
-
return /* @__PURE__ */ jsxs("dialog", { ref, className: cx("uix-peek", className),
|
|
21
|
+
return /* @__PURE__ */ jsxs("dialog", { ref, className: cx("uix-peek", className), "aria-labelledby": title ? titleId : void 0, ...rest, onClick: onBackdropClick, children: [
|
|
19
22
|
/* @__PURE__ */ jsxs("div", { className: "uix-peek__header", children: [
|
|
20
23
|
hasNav && /* @__PURE__ */ jsxs("div", { className: "uix-peek__nav", children: [
|
|
21
|
-
/* @__PURE__ */ jsx("button", { className: "uix-peek__navbtn", onClick: onNavPrev, disabled:
|
|
22
|
-
/* @__PURE__ */ jsx("button", { className: "uix-peek__navbtn", onClick: onNavNext, disabled:
|
|
24
|
+
/* @__PURE__ */ jsx("button", { className: "uix-peek__navbtn", onClick: () => onNavPrev?.(), "aria-disabled": onNavPrev ? void 0 : true, "aria-label": "Previous record", children: /* @__PURE__ */ jsx(ChevronUp, {}) }),
|
|
25
|
+
/* @__PURE__ */ jsx("button", { className: "uix-peek__navbtn", onClick: () => onNavNext?.(), "aria-disabled": onNavNext ? void 0 : true, "aria-label": "Next record", children: /* @__PURE__ */ jsx(ChevronDown, {}) })
|
|
23
26
|
] }),
|
|
24
|
-
title && /* @__PURE__ */ jsx("
|
|
27
|
+
title && /* @__PURE__ */ jsx("h2", { className: "uix-peek__title", id: titleId, children: title }),
|
|
25
28
|
hint && /* @__PURE__ */ jsx("span", { className: "uix-peek__hint", children: hint }),
|
|
26
29
|
onClose && /* @__PURE__ */ jsx("button", { className: "uix-peek__close", onClick: onClose, "aria-label": "Close preview", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
|
|
27
30
|
] }),
|
|
@@ -8,7 +8,9 @@ interface ProgressProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
8
8
|
max?: number;
|
|
9
9
|
/** Continuous slide animation when the amount of work is unknown. */
|
|
10
10
|
indeterminate?: boolean;
|
|
11
|
+
/** Accessible name — rendered as `aria-label` (an explicit `aria-label` prop wins). */
|
|
12
|
+
label?: string;
|
|
11
13
|
}
|
|
12
|
-
declare function Progress({ value, max, indeterminate, className, ...props }: ProgressProps): react.JSX.Element;
|
|
14
|
+
declare function Progress({ value, max, indeterminate, label, className, ...props }: ProgressProps): react.JSX.Element;
|
|
13
15
|
|
|
14
16
|
export { Progress, type ProgressProps };
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from "../cx.js";
|
|
3
|
-
function Progress({ value = 0, max = 100, indeterminate, className, ...props }) {
|
|
4
|
-
const
|
|
3
|
+
function Progress({ value = 0, max = 100, indeterminate, label, className, ...props }) {
|
|
4
|
+
const clamped = Math.max(0, Math.min(max || 100, value));
|
|
5
|
+
const pct = clamped / (max || 100) * 100;
|
|
5
6
|
return /* @__PURE__ */ jsx(
|
|
6
7
|
"div",
|
|
7
8
|
{
|
|
8
9
|
role: "progressbar",
|
|
9
10
|
"aria-valuemin": 0,
|
|
10
11
|
"aria-valuemax": max,
|
|
11
|
-
"aria-valuenow": indeterminate ? void 0 :
|
|
12
|
+
"aria-valuenow": indeterminate ? void 0 : clamped,
|
|
13
|
+
"aria-label": label,
|
|
12
14
|
className: cx("uix-progress", indeterminate && "uix-progress--indeterminate", className),
|
|
13
15
|
...props,
|
|
14
16
|
children: /* @__PURE__ */ jsx("div", { className: "uix-progress__bar", style: indeterminate ? void 0 : { width: `${pct}%` } })
|
|
@@ -8,7 +8,9 @@ declare const Radio: react.ForwardRefExoticComponent<RadioProps & react.RefAttri
|
|
|
8
8
|
interface RadioGroupProps {
|
|
9
9
|
children: ReactNode;
|
|
10
10
|
className?: string;
|
|
11
|
+
/** Group name; when set the group renders as fieldset/legend so it's announced with each radio. */
|
|
12
|
+
label?: ReactNode;
|
|
11
13
|
}
|
|
12
|
-
declare function RadioGroup({ children, className }: RadioGroupProps): react.JSX.Element;
|
|
14
|
+
declare function RadioGroup({ children, className, label }: RadioGroupProps): react.JSX.Element;
|
|
13
15
|
|
|
14
16
|
export { Radio, RadioGroup, type RadioGroupProps, type RadioProps };
|
package/dist/components/Radio.js
CHANGED
|
@@ -9,8 +9,12 @@ const Radio = forwardRef(
|
|
|
9
9
|
] })
|
|
10
10
|
);
|
|
11
11
|
Radio.displayName = "Radio";
|
|
12
|
-
function RadioGroup({ children, className }) {
|
|
13
|
-
return /* @__PURE__ */ jsx("div", { className: cx("uix-radio-group", className), children });
|
|
12
|
+
function RadioGroup({ children, className, label }) {
|
|
13
|
+
if (label == null) return /* @__PURE__ */ jsx("div", { className: cx("uix-radio-group", className), children });
|
|
14
|
+
return /* @__PURE__ */ jsxs("fieldset", { className: cx("uix-radio-group", className), children: [
|
|
15
|
+
/* @__PURE__ */ jsx("legend", { className: "uix-radio-group__legend", children: label }),
|
|
16
|
+
children
|
|
17
|
+
] });
|
|
14
18
|
}
|
|
15
19
|
export {
|
|
16
20
|
Radio,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from "react";
|
|
3
|
+
import { useId, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
const ChevronIcon = () => /* @__PURE__ */ 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__ */ jsx("path", { d: "M2 4l4 4 4-4" }) });
|
|
6
6
|
function Sidebar({ collapsed, onToggle, brand, toggleIcon, children, className, ...props }) {
|
|
@@ -55,12 +55,14 @@ function NavItem({ icon, badge, active, as: Tag = "a", children, className, ...p
|
|
|
55
55
|
}
|
|
56
56
|
function NavGroup({ icon, label, children, defaultExpanded = true }) {
|
|
57
57
|
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
58
|
+
const panelId = useId();
|
|
58
59
|
return /* @__PURE__ */ jsxs("div", { className: "uix-navgroup", children: [
|
|
59
60
|
/* @__PURE__ */ jsxs(
|
|
60
61
|
"button",
|
|
61
62
|
{
|
|
62
63
|
className: "uix-navitem uix-navgroup__trigger",
|
|
63
64
|
"aria-expanded": expanded,
|
|
65
|
+
"aria-controls": panelId,
|
|
64
66
|
onClick: () => setExpanded((v) => !v),
|
|
65
67
|
children: [
|
|
66
68
|
icon && /* @__PURE__ */ jsx("span", { className: "uix-navitem__icon", "aria-hidden": "true", children: icon }),
|
|
@@ -69,7 +71,7 @@ function NavGroup({ icon, label, children, defaultExpanded = true }) {
|
|
|
69
71
|
]
|
|
70
72
|
}
|
|
71
73
|
),
|
|
72
|
-
/* @__PURE__ */ jsx("div", { className: "uix-navgroup__panel", children: /* @__PURE__ */ jsx("div", { children }) })
|
|
74
|
+
/* @__PURE__ */ jsx("div", { className: "uix-navgroup__panel", id: panelId, children: /* @__PURE__ */ jsx("div", { children }) })
|
|
73
75
|
] });
|
|
74
76
|
}
|
|
75
77
|
function SubNavItem({ active, as: Tag = "a", children, className, ...props }) {
|
|
@@ -4,7 +4,9 @@ import { HTMLAttributes } from 'react';
|
|
|
4
4
|
interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {
|
|
5
5
|
size?: 'md' | 'lg';
|
|
6
6
|
accent?: boolean;
|
|
7
|
+
/** Announced loading text (default "Loading…"). */
|
|
8
|
+
label?: string;
|
|
7
9
|
}
|
|
8
|
-
declare function Spinner({ size, accent, className, ...props }: SpinnerProps): react.JSX.Element;
|
|
10
|
+
declare function Spinner({ size, accent, label, className, ...props }: SpinnerProps): react.JSX.Element;
|
|
9
11
|
|
|
10
12
|
export { Spinner, type SpinnerProps };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from "../cx.js";
|
|
3
|
-
function Spinner({ size = "md", accent, className, ...props }) {
|
|
3
|
+
function Spinner({ size = "md", accent, label, className, ...props }) {
|
|
4
4
|
return /* @__PURE__ */ jsx(
|
|
5
5
|
"span",
|
|
6
6
|
{
|
|
7
7
|
className: cx("uix-spinner", accent && "uix-spinner--accent", size === "lg" && "uix-spinner--lg", className),
|
|
8
8
|
role: "status",
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
...props,
|
|
10
|
+
children: /* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: label ?? "Loading\u2026" })
|
|
11
11
|
}
|
|
12
12
|
);
|
|
13
13
|
}
|
package/dist/components/Stat.js
CHANGED
|
@@ -8,7 +8,11 @@ function Stat({ label, value, icon, meta, trend, className, ...props }) {
|
|
|
8
8
|
] }),
|
|
9
9
|
/* @__PURE__ */ jsx("div", { className: "uix-stat__value", children: value }),
|
|
10
10
|
(meta != null || trend != null) && /* @__PURE__ */ jsxs("div", { className: "uix-stat__meta", children: [
|
|
11
|
-
trend != null && /* @__PURE__ */
|
|
11
|
+
trend != null && /* @__PURE__ */ jsxs("span", { className: cx("uix-stat__trend", `uix-stat__trend--${trend.direction}`), children: [
|
|
12
|
+
/* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: trend.direction === "up" ? /* @__PURE__ */ jsx("path", { d: "M12 19V5m-6 6 6-6 6 6" }) : /* @__PURE__ */ jsx("path", { d: "M12 5v14m6-6-6 6-6-6" }) }),
|
|
13
|
+
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: trend.direction === "up" ? "up" : "down" }),
|
|
14
|
+
trend.label
|
|
15
|
+
] }),
|
|
12
16
|
meta
|
|
13
17
|
] })
|
|
14
18
|
] });
|
|
@@ -31,8 +31,10 @@ declare function Skeleton({ variant, width, height, className, style, ...props }
|
|
|
31
31
|
interface LoadingStateProps extends HTMLAttributes<HTMLDivElement> {
|
|
32
32
|
/** Number of skeleton rows to render. */
|
|
33
33
|
rows?: number;
|
|
34
|
+
/** Announced loading text (default "Loading"). */
|
|
35
|
+
label?: string;
|
|
34
36
|
}
|
|
35
37
|
/** A stack of skeleton rows for list/section loading. `role="status"`. */
|
|
36
|
-
declare function LoadingState({ rows, className, ...props }: LoadingStateProps): react.JSX.Element;
|
|
38
|
+
declare function LoadingState({ rows, label, className, ...props }: LoadingStateProps): react.JSX.Element;
|
|
37
39
|
|
|
38
40
|
export { EmptyState, type EmptyStateProps, ErrorState, type ErrorStateProps, LoadingState, type LoadingStateProps, Skeleton, type SkeletonProps };
|
|
@@ -32,8 +32,14 @@ function Skeleton({ variant = "block", width, height, className, style, ...props
|
|
|
32
32
|
}
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
|
-
function LoadingState({ rows = 3, className, ...props }) {
|
|
36
|
-
return
|
|
35
|
+
function LoadingState({ rows = 3, label, className, ...props }) {
|
|
36
|
+
return (
|
|
37
|
+
// no aria-busy: busy=true tells AT the region isn't ready and suppresses the announcement (UIX-A11Y-4)
|
|
38
|
+
/* @__PURE__ */ jsxs("div", { className: cx("uix-stack", className), role: "status", ...props, children: [
|
|
39
|
+
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: label ?? "Loading" }),
|
|
40
|
+
Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsx(Skeleton, { variant: "text", width: `${92 - i % 3 * 14}%` }, i))
|
|
41
|
+
] })
|
|
42
|
+
);
|
|
37
43
|
}
|
|
38
44
|
export {
|
|
39
45
|
EmptyState,
|
|
@@ -2,9 +2,14 @@ import * as react from 'react';
|
|
|
2
2
|
import { HTMLAttributes, ReactNode, ButtonHTMLAttributes, TdHTMLAttributes, TableHTMLAttributes, ThHTMLAttributes } from 'react';
|
|
3
3
|
|
|
4
4
|
interface TableWrapProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/**
|
|
6
|
+
* Polite live-region text for table state changes — "Sorted by …", "N results",
|
|
7
|
+
* "N selected". Pass `useTable().announcement` straight through (UIX-A11Y-2).
|
|
8
|
+
*/
|
|
9
|
+
announcement?: string;
|
|
5
10
|
children?: ReactNode;
|
|
6
11
|
}
|
|
7
|
-
declare function TableWrap({ children, className, ...props }: TableWrapProps): react.JSX.Element;
|
|
12
|
+
declare function TableWrap({ announcement, children, className, ...props }: TableWrapProps): react.JSX.Element;
|
|
8
13
|
type TableDensity = 'compact' | 'standard' | 'comfortable';
|
|
9
14
|
type SortDirection = 'ascending' | 'descending' | 'none';
|
|
10
15
|
interface TableProps extends TableHTMLAttributes<HTMLTableElement> {
|
|
@@ -53,9 +58,11 @@ interface RowActionProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
53
58
|
declare function RowAction({ children, className, type, ...props }: RowActionProps): react.JSX.Element;
|
|
54
59
|
interface ExpandToggleProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
55
60
|
expanded?: boolean;
|
|
61
|
+
/** id of the detail row this toggle controls → `aria-controls` (UIX-A11Y-2). */
|
|
62
|
+
controls?: string;
|
|
56
63
|
}
|
|
57
64
|
/** Chevron button that expands/collapses an inline detail row. */
|
|
58
|
-
declare function ExpandToggle({ expanded, className, type, ...props }: ExpandToggleProps): react.JSX.Element;
|
|
65
|
+
declare function ExpandToggle({ expanded, controls, className, type, ...props }: ExpandToggleProps): react.JSX.Element;
|
|
59
66
|
/** Emphasised primary cell text. */
|
|
60
67
|
declare function CellStrong({ children, className, ...props }: HTMLAttributes<HTMLSpanElement>): react.JSX.Element;
|
|
61
68
|
/** Secondary line under a primary cell value (muted, meta size). */
|