@tensor_1/react 2.10.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +10 -1
  2. package/dist/ChartCore-Bt1C84ty.d.cts +22 -0
  3. package/dist/chart-preset.cjs +168 -0
  4. package/dist/chart-preset.d.cts +9 -0
  5. package/dist/chart-preset.d.ts +9 -0
  6. package/dist/chart-preset.js +35 -0
  7. package/dist/chart.cjs +58 -41
  8. package/dist/chart.d.cts +6 -21
  9. package/dist/chart.d.ts +2 -1
  10. package/dist/components/Alert.js +7 -15
  11. package/dist/components/AppShell.d.ts +1 -3
  12. package/dist/components/AppShell.js +1 -3
  13. package/dist/components/Avatar.js +1 -2
  14. package/dist/components/Button.js +16 -32
  15. package/dist/components/Card.d.ts +1 -3
  16. package/dist/components/Card.js +2 -2
  17. package/dist/components/Chart.d.ts +6 -21
  18. package/dist/components/Chart.js +5 -85
  19. package/dist/components/ChartCore.d.ts +33 -0
  20. package/dist/components/ChartCore.js +100 -0
  21. package/dist/components/CommandPalette.d.ts +2 -4
  22. package/dist/components/CommandPalette.js +13 -66
  23. package/dist/components/Comments.d.ts +1 -5
  24. package/dist/components/Comments.js +1 -1
  25. package/dist/components/Composer.d.ts +1 -8
  26. package/dist/components/Composer.js +2 -7
  27. package/dist/components/DetailLayout.js +1 -1
  28. package/dist/components/Drawer.d.ts +4 -3
  29. package/dist/components/Drawer.js +4 -6
  30. package/dist/components/Field.js +3 -6
  31. package/dist/components/Inbox.d.ts +3 -13
  32. package/dist/components/Inbox.js +3 -62
  33. package/dist/components/Input.d.ts +2 -13
  34. package/dist/components/Input.js +5 -20
  35. package/dist/components/Kanban.d.ts +3 -15
  36. package/dist/components/Kanban.js +10 -42
  37. package/dist/components/Meter.d.ts +1 -3
  38. package/dist/components/Meter.js +1 -10
  39. package/dist/components/Modal.d.ts +5 -3
  40. package/dist/components/Modal.js +4 -6
  41. package/dist/components/NavFavourites.js +35 -119
  42. package/dist/components/PageHeader.d.ts +1 -3
  43. package/dist/components/PageHeader.js +2 -2
  44. package/dist/components/Peek.d.ts +4 -3
  45. package/dist/components/Peek.js +6 -9
  46. package/dist/components/Progress.d.ts +1 -3
  47. package/dist/components/Progress.js +3 -5
  48. package/dist/components/Radio.d.ts +1 -3
  49. package/dist/components/Radio.js +2 -6
  50. package/dist/components/Segmented.js +3 -2
  51. package/dist/components/Sidebar.js +2 -4
  52. package/dist/components/Spinner.d.ts +1 -3
  53. package/dist/components/Spinner.js +3 -3
  54. package/dist/components/Stat.js +1 -5
  55. package/dist/components/States.d.ts +1 -3
  56. package/dist/components/States.js +2 -8
  57. package/dist/components/Table.d.ts +2 -9
  58. package/dist/components/Table.js +8 -11
  59. package/dist/components/Tabs.d.ts +1 -13
  60. package/dist/components/Tabs.js +4 -78
  61. package/dist/components/Timeline.d.ts +3 -3
  62. package/dist/components/Timeline.js +3 -3
  63. package/dist/components/Toast.js +5 -42
  64. package/dist/components/Tooltip.d.ts +1 -3
  65. package/dist/components/Tooltip.js +5 -44
  66. package/dist/components/Tree.d.ts +0 -2
  67. package/dist/components/Tree.js +4 -26
  68. package/dist/hooks/useDialog.d.ts +1 -1
  69. package/dist/hooks/useDialog.js +2 -17
  70. package/dist/hooks/useTable.d.ts +0 -5
  71. package/dist/hooks/useTable.js +12 -27
  72. package/dist/hooks/useVirtualRows.d.ts +27 -0
  73. package/dist/hooks/useVirtualRows.js +50 -0
  74. package/dist/index.cjs +291 -760
  75. package/dist/index.d.cts +61 -127
  76. package/dist/index.d.ts +3 -2
  77. package/dist/index.js +4 -3
  78. package/dist/table-engine.js +8 -6
  79. package/dist/tree-model.d.ts +0 -4
  80. package/dist/tree-model.js +0 -8
  81. package/package.json +16 -2
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { cloneElement, forwardRef, isValidElement } from "react";
2
+ import { forwardRef } 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,22 +14,7 @@ const Input = forwardRef(
14
14
  )
15
15
  );
16
16
  Input.displayName = "Input";
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;
17
+ function InputGroup({ children, leadingIcon, trailingIcon, className }) {
33
18
  return /* @__PURE__ */ jsxs(
34
19
  "div",
35
20
  {
@@ -40,9 +25,9 @@ function InputGroup({
40
25
  className
41
26
  ),
42
27
  children: [
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 })
28
+ leadingIcon && /* @__PURE__ */ jsx("span", { className: "uix-input-group__icon", children: leadingIcon }),
29
+ children,
30
+ trailingIcon && /* @__PURE__ */ jsx("span", { className: "uix-input-group__icon uix-input-group__icon--trailing", children: trailingIcon })
46
31
  ]
47
32
  }
48
33
  );
@@ -2,32 +2,20 @@ 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;
10
5
  children?: ReactNode;
11
6
  }
12
- declare function Kanban({ announcement, children, className, ...props }: KanbanProps): react.JSX.Element;
7
+ declare function Kanban({ children, className, ...props }: KanbanProps): react.JSX.Element;
13
8
  interface KanbanColumnProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
14
9
  title?: ReactNode;
15
10
  count?: number;
16
11
  children?: ReactNode;
17
12
  }
18
13
  declare function KanbanColumn({ title, count, children, className, ...props }: KanbanColumnProps): react.JSX.Element;
19
- type KanbanMoveDirection = 'up' | 'down' | 'left' | 'right';
20
14
  interface KanbanCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
21
15
  title?: ReactNode;
22
16
  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;
29
17
  children?: ReactNode;
30
18
  }
31
- declare function KanbanCard({ title, meta, children, className, onMove, onKeyDown, ...props }: KanbanCardProps): react.JSX.Element;
19
+ declare function KanbanCard({ title, meta, children, className, ...props }: KanbanCardProps): react.JSX.Element;
32
20
 
33
- export { Kanban, KanbanCard, type KanbanCardProps, KanbanColumn, type KanbanColumnProps, type KanbanMoveDirection, type KanbanProps };
21
+ export { Kanban, KanbanCard, type KanbanCardProps, KanbanColumn, type KanbanColumnProps, type KanbanProps };
@@ -1,55 +1,23 @@
1
- "use client";
2
1
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useId } from "react";
4
2
  import { cx } from "../cx.js";
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
- ] });
3
+ function Kanban({ children, className, ...props }) {
4
+ return /* @__PURE__ */ jsx("div", { className: cx("uix-kanban", className), ...props, children });
10
5
  }
11
6
  function KanbanColumn({ title, count, children, className, ...props }) {
12
- const headId = useId();
13
- const hasHead = title != null || count != null;
14
7
  return /* @__PURE__ */ jsxs("div", { className: cx("uix-kanban__col", className), ...props, children: [
15
- hasHead && /* @__PURE__ */ jsxs("div", { className: "uix-kanban__head", id: headId, children: [
8
+ (title != null || count != null) && /* @__PURE__ */ jsxs("div", { className: "uix-kanban__head", children: [
16
9
  title,
17
10
  count != null && /* @__PURE__ */ jsx("span", { className: "uix-kanban__count", children: count })
18
11
  ] }),
19
- /* @__PURE__ */ jsx("div", { className: "uix-kanban__body", role: "list", "aria-labelledby": hasHead ? headId : void 0, children })
12
+ /* @__PURE__ */ jsx("div", { className: "uix-kanban__body", children })
20
13
  ] });
21
14
  }
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
- );
15
+ function KanbanCard({ title, meta, children, className, ...props }) {
16
+ return /* @__PURE__ */ jsxs("div", { className: cx("uix-kanban__card", className), ...props, children: [
17
+ title && /* @__PURE__ */ jsx("div", { className: "uix-kanban__card-title", children: title }),
18
+ meta && /* @__PURE__ */ jsx("div", { className: "uix-kanban__card-meta", children: meta }),
19
+ children
20
+ ] });
53
21
  }
54
22
  export {
55
23
  Kanban,
@@ -8,10 +8,8 @@ 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;
13
11
  }
14
12
  /** Horizontal utilization / threshold bar backed by `.uix-meter`. */
15
- declare function Meter({ value, tone, label, className, ...props }: MeterProps): react.JSX.Element;
13
+ declare function Meter({ value, tone, className, ...props }: MeterProps): react.JSX.Element;
16
14
 
17
15
  export { Meter, type MeterProps, type MeterTone };
@@ -1,13 +1,6 @@
1
- "use client";
2
1
  import { jsx } from "react/jsx-runtime";
3
2
  import { cx } from "../cx.js";
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 }) {
3
+ function Meter({ value = 0, tone, className, ...props }) {
11
4
  const pct = Math.max(0, Math.min(100, value));
12
5
  return /* @__PURE__ */ jsx(
13
6
  "div",
@@ -16,8 +9,6 @@ function Meter({ value = 0, tone, label, className, ...props }) {
16
9
  "aria-valuenow": pct,
17
10
  "aria-valuemin": 0,
18
11
  "aria-valuemax": 100,
19
- "aria-valuetext": tone && tone !== "success" ? `${pct}%, ${toneText[tone]}` : void 0,
20
- "aria-label": label,
21
12
  className: cx("uix-meter", className),
22
13
  ...props,
23
14
  children: /* @__PURE__ */ jsx(
@@ -1,13 +1,15 @@
1
1
  import * as react from 'react';
2
- import { HTMLAttributes, ReactNode } from 'react';
2
+ import { ReactNode, CSSProperties } from 'react';
3
3
 
4
- interface ModalProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
4
+ interface ModalProps {
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;
10
12
  }
11
- declare function Modal({ open, onClose, title, children, footer, className, ...rest }: ModalProps): react.JSX.Element;
13
+ declare function Modal({ open, onClose, title, children, footer, className, style }: ModalProps): react.JSX.Element;
12
14
 
13
15
  export { Modal, type ModalProps };
@@ -1,15 +1,13 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useId } from "react";
4
3
  import { cx } from "../cx.js";
5
4
  import { useDialog } from "../hooks/useDialog.js";
6
5
  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" }) });
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: [
6
+ function Modal({ open, onClose, title, children, footer, className, style }) {
7
+ const ref = useDialog(open);
8
+ return /* @__PURE__ */ jsxs("dialog", { ref, className: cx("uix-dialog", className), style, onClose, children: [
11
9
  (title != null || onClose) && /* @__PURE__ */ jsxs("div", { className: "uix-dialog__header", children: [
12
- title && /* @__PURE__ */ jsx("h2", { className: "uix-dialog__title", id: titleId, children: title }),
10
+ title && /* @__PURE__ */ jsx("div", { className: "uix-dialog__title", children: title }),
13
11
  onClose && /* @__PURE__ */ jsx("button", { className: "uix-dialog__close", onClick: onClose, "aria-label": "Close dialog", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
14
12
  ] }),
15
13
  children != null && /* @__PURE__ */ jsx("div", { className: "uix-dialog__body", children }),
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { useEffect, useId, useRef, useState } from "react";
3
+ import { memo, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
4
4
  import { cx } from "../cx.js";
5
5
  const ChevronDownIcon = () => /* @__PURE__ */ jsx(
6
6
  "svg",
@@ -14,7 +14,7 @@ const ChevronDownIcon = () => /* @__PURE__ */ jsx(
14
14
  strokeLinecap: "round",
15
15
  strokeLinejoin: "round",
16
16
  "aria-hidden": "true",
17
- style: { flexShrink: 0 },
17
+ className: "uix-favourites__chevron",
18
18
  children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
19
19
  }
20
20
  );
@@ -30,7 +30,7 @@ const ChevronRightIcon = () => /* @__PURE__ */ jsx(
30
30
  strokeLinecap: "round",
31
31
  strokeLinejoin: "round",
32
32
  "aria-hidden": "true",
33
- style: { flexShrink: 0 },
33
+ className: "uix-favourites__chevron",
34
34
  children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
35
35
  }
36
36
  );
@@ -53,7 +53,6 @@ const MoreIcon = () => /* @__PURE__ */ jsxs(
53
53
  ]
54
54
  }
55
55
  );
56
- const MUTED = "color-mix(in srgb, var(--uix-text) 50%, transparent)";
57
56
  function NavFavourites({
58
57
  items,
59
58
  collapsed = false,
@@ -67,10 +66,9 @@ function NavFavourites({
67
66
  const regionId = useId();
68
67
  const headerRef = useRef(null);
69
68
  const regionRef = useRef(null);
70
- const [announcement, setAnnouncement] = useState("");
71
69
  const focusAfterRef = useRef(null);
72
70
  const triggerRefs = useRef(/* @__PURE__ */ new Map());
73
- const order = items.map((i) => i.id);
71
+ const order = useMemo(() => items.map((item) => item.id), [items]);
74
72
  useEffect(() => {
75
73
  const id = focusAfterRef.current;
76
74
  if (!id) return;
@@ -78,7 +76,7 @@ function NavFavourites({
78
76
  const el = triggerRefs.current.get(id);
79
77
  if (el) el.focus();
80
78
  else headerRef.current?.focus();
81
- }, [order.join("|")]);
79
+ }, [order]);
82
80
  const handleToggle = () => {
83
81
  if (!collapsed) {
84
82
  const active = document.activeElement;
@@ -88,7 +86,7 @@ function NavFavourites({
88
86
  }
89
87
  onToggleCollapsed?.();
90
88
  };
91
- const move = (id, dir) => {
89
+ const move = useCallback((id, dir) => {
92
90
  const idx = order.indexOf(id);
93
91
  const next = idx + dir;
94
92
  if (idx < 0 || next < 0 || next >= order.length) return;
@@ -97,18 +95,19 @@ function NavFavourites({
97
95
  if (it === void 0) return;
98
96
  reordered.splice(next, 0, it);
99
97
  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}`);
102
98
  onReorder(reordered);
103
- };
104
- const remove = (id) => {
99
+ }, [onReorder, order]);
100
+ const remove = useCallback((id) => {
105
101
  const idx = order.indexOf(id);
106
102
  const neighbour = order[idx + 1] ?? order[idx - 1] ?? null;
107
103
  focusAfterRef.current = neighbour;
108
104
  onRemove(id);
109
- };
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 }),
105
+ }, [onRemove, order]);
106
+ const registerTrigger = useCallback((id, element) => {
107
+ if (element) triggerRefs.current.set(id, element);
108
+ else triggerRefs.current.delete(id);
109
+ }, []);
110
+ return /* @__PURE__ */ jsxs("div", { className: cx("uix-favourites", className), children: [
112
111
  /* @__PURE__ */ jsxs(
113
112
  "button",
114
113
  {
@@ -117,18 +116,7 @@ function NavFavourites({
117
116
  "aria-expanded": !collapsed,
118
117
  "aria-controls": regionId,
119
118
  onClick: handleToggle,
120
- className: "uix-text-eyebrow",
121
- style: {
122
- display: "flex",
123
- width: "100%",
124
- alignItems: "center",
125
- gap: "6px",
126
- padding: "8px 4px 4px",
127
- border: 0,
128
- background: "transparent",
129
- cursor: "pointer",
130
- color: "inherit"
131
- },
119
+ className: "uix-favourites__heading uix-text-eyebrow",
132
120
  children: [
133
121
  collapsed ? /* @__PURE__ */ jsx(ChevronRightIcon, {}) : /* @__PURE__ */ jsx(ChevronDownIcon, {}),
134
122
  labels.heading
@@ -141,20 +129,12 @@ function NavFavourites({
141
129
  ref: regionRef,
142
130
  id: regionId,
143
131
  hidden: collapsed,
144
- style: {
145
- display: "flex",
146
- flexDirection: "column",
147
- gap: "1px",
148
- listStyle: "none",
149
- margin: 0,
150
- padding: 0
151
- },
132
+ className: "uix-favourites__list",
152
133
  children: items.length === 0 ? /* @__PURE__ */ jsx(
153
134
  "li",
154
135
  {
155
- className: "uix-text-meta",
136
+ className: "uix-favourites__empty uix-text-meta",
156
137
  "data-empty-reason": emptyReason,
157
- style: { padding: "6px 8px", color: MUTED },
158
138
  children: labels.empty
159
139
  }
160
140
  ) : items.map((item, idx) => /* @__PURE__ */ jsx(
@@ -164,10 +144,9 @@ function NavFavourites({
164
144
  isFirst: idx === 0,
165
145
  isLast: idx === items.length - 1,
166
146
  labels,
167
- onMoveUp: () => move(item.id, -1),
168
- onMoveDown: () => move(item.id, 1),
169
- onRemove: () => remove(item.id),
170
- registerTrigger: (el) => triggerRefs.current.set(item.id, el)
147
+ onMove: move,
148
+ onRemove: remove,
149
+ registerTrigger
171
150
  },
172
151
  item.id
173
152
  ))
@@ -175,26 +154,24 @@ function NavFavourites({
175
154
  )
176
155
  ] });
177
156
  }
178
- function FavouriteRow({
157
+ const FavouriteRow = memo(function FavouriteRow2({
179
158
  item,
180
159
  isFirst,
181
160
  isLast,
182
161
  labels,
183
- onMoveUp,
184
- onMoveDown,
162
+ onMove,
185
163
  onRemove,
186
164
  registerTrigger
187
165
  }) {
188
166
  const [open, setOpen] = useState(false);
189
- const [revealed, setRevealed] = useState(false);
190
167
  const menuId = useId();
191
168
  const triggerRef = useRef(null);
192
169
  const itemRefs = useRef([]);
193
170
  const optionsTemplate = labels.options ?? "{label} favourite options";
194
171
  const actions = [
195
- ...isFirst ? [] : [{ label: labels.moveUp, onSelect: onMoveUp }],
196
- ...isLast ? [] : [{ label: labels.moveDown, onSelect: onMoveDown }],
197
- { label: labels.remove.replace("{label}", item.label), onSelect: onRemove }
172
+ ...isFirst ? [] : [{ label: labels.moveUp, onSelect: () => onMove(item.id, -1) }],
173
+ ...isLast ? [] : [{ label: labels.moveDown, onSelect: () => onMove(item.id, 1) }],
174
+ { label: labels.remove.replace("{label}", item.label), onSelect: () => onRemove(item.id) }
198
175
  ];
199
176
  useEffect(() => {
200
177
  if (open) itemRefs.current[0]?.focus();
@@ -212,12 +189,6 @@ function FavouriteRow({
212
189
  } else if (e.key === "ArrowUp") {
213
190
  e.preventDefault();
214
191
  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();
221
192
  } else if (e.key === "Escape") {
222
193
  e.preventDefault();
223
194
  closeAndReturnFocus();
@@ -229,34 +200,27 @@ function FavouriteRow({
229
200
  if (!e.altKey) return;
230
201
  if (e.key === "ArrowUp" && !isFirst) {
231
202
  e.preventDefault();
232
- onMoveUp();
203
+ onMove(item.id, -1);
233
204
  } else if (e.key === "ArrowDown" && !isLast) {
234
205
  e.preventDefault();
235
- onMoveDown();
206
+ onMove(item.id, 1);
236
207
  }
237
208
  };
238
- const triggerVisible = revealed || open;
239
209
  return (
240
210
  // biome-ignore lint/a11y/noStaticElementInteractions: keyboard accelerators on the row; the focusable controls inside carry their own semantics.
241
211
  /* @__PURE__ */ jsxs(
242
212
  "li",
243
213
  {
244
- style: { position: "relative", display: "flex", alignItems: "center" },
214
+ className: "uix-favourites__row",
245
215
  onKeyDown: onRowKeyDown,
246
- onMouseEnter: () => setRevealed(true),
247
- onMouseLeave: () => setRevealed(false),
248
- onFocus: () => setRevealed(true),
249
- onBlur: (e) => {
250
- if (!e.currentTarget.contains(e.relatedTarget)) setRevealed(false);
251
- },
252
216
  children: [
253
- /* @__PURE__ */ jsx("div", { style: { minWidth: 0, flex: 1 }, children: item.link }),
217
+ /* @__PURE__ */ jsx("div", { className: "uix-favourites__link", children: item.link }),
254
218
  /* @__PURE__ */ jsx(
255
219
  "button",
256
220
  {
257
221
  ref: (el) => {
258
222
  triggerRef.current = el;
259
- registerTrigger(el);
223
+ registerTrigger(item.id, el);
260
224
  },
261
225
  type: "button",
262
226
  "aria-haspopup": "menu",
@@ -264,22 +228,7 @@ function FavouriteRow({
264
228
  "aria-controls": open ? menuId : void 0,
265
229
  "aria-label": optionsTemplate.replace("{label}", item.label),
266
230
  onClick: () => setOpen((v) => !v),
267
- style: {
268
- marginLeft: "auto",
269
- display: "inline-flex",
270
- height: "20px",
271
- width: "20px",
272
- flexShrink: 0,
273
- alignItems: "center",
274
- justifyContent: "center",
275
- border: 0,
276
- borderRadius: "2px",
277
- background: "transparent",
278
- cursor: "pointer",
279
- color: MUTED,
280
- opacity: triggerVisible ? 1 : 0,
281
- transition: "opacity var(--uix-dur-fast) var(--uix-ease-out)"
282
- },
231
+ className: "uix-favourites__menu-trigger",
283
232
  children: /* @__PURE__ */ jsx(MoreIcon, {})
284
233
  }
285
234
  ),
@@ -290,7 +239,7 @@ function FavouriteRow({
290
239
  "aria-hidden": "true",
291
240
  role: "presentation",
292
241
  onClick: () => setOpen(false),
293
- style: { position: "fixed", inset: 0, cursor: "default", background: "transparent", zIndex: 40 }
242
+ className: "uix-favourites__backdrop"
294
243
  }
295
244
  ),
296
245
  /* @__PURE__ */ jsx(
@@ -298,23 +247,8 @@ function FavouriteRow({
298
247
  {
299
248
  id: menuId,
300
249
  role: "menu",
301
- "aria-label": optionsTemplate.replace("{label}", item.label),
302
250
  onKeyDown: onMenuKeyDown,
303
- style: {
304
- position: "absolute",
305
- right: 0,
306
- top: "calc(100% + 2px)",
307
- zIndex: 50,
308
- minWidth: "160px",
309
- overflow: "hidden",
310
- margin: 0,
311
- padding: "4px 0",
312
- listStyle: "none",
313
- borderRadius: "6px",
314
- border: "1px solid var(--uix-border)",
315
- background: "var(--uix-surface)",
316
- boxShadow: "var(--uix-shadow-popover)"
317
- },
251
+ className: "uix-favourites__menu",
318
252
  children: actions.map((action, i) => /* @__PURE__ */ jsx("li", { role: "presentation", children: /* @__PURE__ */ jsx(
319
253
  "button",
320
254
  {
@@ -328,25 +262,7 @@ function FavouriteRow({
328
262
  setOpen(false);
329
263
  action.onSelect();
330
264
  },
331
- style: {
332
- display: "flex",
333
- width: "100%",
334
- alignItems: "center",
335
- padding: "6px 12px",
336
- border: 0,
337
- background: "transparent",
338
- cursor: "pointer",
339
- textAlign: "left",
340
- fontSize: "13px",
341
- color: "color-mix(in srgb, var(--uix-text) 85%, transparent)",
342
- transition: "background-color var(--uix-dur-fast) var(--uix-ease-out)"
343
- },
344
- onMouseEnter: (e) => {
345
- e.currentTarget.style.background = "var(--uix-bg-hover)";
346
- },
347
- onMouseLeave: (e) => {
348
- e.currentTarget.style.background = "transparent";
349
- },
265
+ className: "uix-favourites__menu-item",
350
266
  children: action.label
351
267
  }
352
268
  ) }, action.label))
@@ -357,7 +273,7 @@ function FavouriteRow({
357
273
  }
358
274
  )
359
275
  );
360
- }
276
+ });
361
277
  export {
362
278
  NavFavourites
363
279
  };
@@ -4,8 +4,6 @@ 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';
9
7
  /** Supporting copy under the title. */
10
8
  subtitle?: ReactNode;
11
9
  /** Right-aligned action area (buttons, menus). */
@@ -15,6 +13,6 @@ interface PageHeaderProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
15
13
  * Page header — eyebrow / title / subtitle + actions row, over `.uix-page-header`.
16
14
  * The hairline + breathing room below come from the component CSS.
17
15
  */
18
- declare function PageHeader({ eyebrow, title, subtitle, actions, as: TitleTag, className, ...props }: PageHeaderProps): react.JSX.Element;
16
+ declare function PageHeader({ eyebrow, title, subtitle, actions, className, ...props }: PageHeaderProps): react.JSX.Element;
19
17
 
20
18
  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, as: TitleTag = "h1", className, ...props }) {
3
+ function PageHeader({ eyebrow, title, subtitle, actions, 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(TitleTag, { className: "uix-page-header__title", children: title }),
7
+ /* @__PURE__ */ jsx("h1", { 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 { HTMLAttributes, ReactNode } from 'react';
2
+ import { ReactNode } from 'react';
3
3
 
4
- interface PeekProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
4
+ interface PeekProps {
5
5
  open: boolean;
6
6
  onClose?: () => void;
7
7
  title?: ReactNode;
@@ -10,7 +10,8 @@ interface PeekProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
10
10
  onNavPrev?: () => void;
11
11
  onNavNext?: () => void;
12
12
  hint?: string;
13
+ className?: string;
13
14
  }
14
- declare function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className, onClick, ...rest }: PeekProps): react.JSX.Element;
15
+ declare function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className }: PeekProps): react.JSX.Element;
15
16
 
16
17
  export { Peek, type PeekProps };
@@ -1,30 +1,27 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useId } from "react";
4
3
  import { cx } from "../cx.js";
5
4
  import { useDialog } from "../hooks/useDialog.js";
6
5
  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" }) });
7
6
  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" }) });
8
7
  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" }) });
9
- function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className, onClick, ...rest }) {
10
- const ref = useDialog(open, onClose);
8
+ function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hint, className }) {
9
+ const ref = useDialog(open);
11
10
  const hasNav = onNavPrev != null || onNavNext != null;
12
- const titleId = useId();
13
11
  const onBackdropClick = (e) => {
14
- onClick?.(e);
15
12
  if (!onClose || e.target !== e.currentTarget) return;
16
13
  const r = e.currentTarget.getBoundingClientRect();
17
14
  if (r.width === 0) return;
18
15
  const outside = e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom;
19
16
  if (outside) onClose();
20
17
  };
21
- return /* @__PURE__ */ jsxs("dialog", { ref, className: cx("uix-peek", className), "aria-labelledby": title ? titleId : void 0, ...rest, onClick: onBackdropClick, children: [
18
+ return /* @__PURE__ */ jsxs("dialog", { ref, className: cx("uix-peek", className), onClose, onClick: onBackdropClick, children: [
22
19
  /* @__PURE__ */ jsxs("div", { className: "uix-peek__header", children: [
23
20
  hasNav && /* @__PURE__ */ jsxs("div", { className: "uix-peek__nav", children: [
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, {}) })
21
+ /* @__PURE__ */ jsx("button", { className: "uix-peek__navbtn", onClick: onNavPrev, disabled: !onNavPrev, "aria-label": "Previous record", children: /* @__PURE__ */ jsx(ChevronUp, {}) }),
22
+ /* @__PURE__ */ jsx("button", { className: "uix-peek__navbtn", onClick: onNavNext, disabled: !onNavNext, "aria-label": "Next record", children: /* @__PURE__ */ jsx(ChevronDown, {}) })
26
23
  ] }),
27
- title && /* @__PURE__ */ jsx("h2", { className: "uix-peek__title", id: titleId, children: title }),
24
+ title && /* @__PURE__ */ jsx("div", { className: "uix-peek__title", children: title }),
28
25
  hint && /* @__PURE__ */ jsx("span", { className: "uix-peek__hint", children: hint }),
29
26
  onClose && /* @__PURE__ */ jsx("button", { className: "uix-peek__close", onClick: onClose, "aria-label": "Close preview", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
30
27
  ] }),
@@ -8,9 +8,7 @@ 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;
13
11
  }
14
- declare function Progress({ value, max, indeterminate, label, className, ...props }: ProgressProps): react.JSX.Element;
12
+ declare function Progress({ value, max, indeterminate, className, ...props }: ProgressProps): react.JSX.Element;
15
13
 
16
14
  export { Progress, type ProgressProps };