@nexub/react 6.2.0 → 6.4.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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "6.2.0",
2
+ "version": "6.4.0",
3
3
  "standard": "WCAG 2.2 Level AA",
4
4
  "method": {
5
5
  "automated": "axe-core rules tagged wcag2a, wcag2aa, wcag21a, wcag21aa and wcag22aa, run on every Storybook story in light and dark, and on open popups (menus, listboxes, dialogs), on every pull request; a new violation fails the build.",
@@ -42,6 +42,15 @@
42
42
  "keyboard": [],
43
43
  "screenReader": []
44
44
  },
45
+ {
46
+ "slug": "action-bar",
47
+ "name": "Action Bar",
48
+ "package": "@nexub/react",
49
+ "automated": "pass",
50
+ "exceptions": [],
51
+ "keyboard": [],
52
+ "screenReader": []
53
+ },
45
54
  {
46
55
  "slug": "alert-dialog",
47
56
  "name": "Alert Dialog",
@@ -925,6 +934,19 @@
925
934
  "keyboard": [],
926
935
  "screenReader": []
927
936
  },
937
+ {
938
+ "slug": "resizable-panels",
939
+ "name": "Resizable Panels",
940
+ "package": "@nexub/react",
941
+ "automated": "pass",
942
+ "exceptions": [],
943
+ "keyboard": [
944
+ "Arrow keys: Move the focused separator by 5%. Mirrored in RTL.",
945
+ "Home / End: To the smallest or largest size.",
946
+ "Enter: Back to the default split."
947
+ ],
948
+ "screenReader": []
949
+ },
928
950
  {
929
951
  "slug": "saved-views",
930
952
  "name": "Saved Views",
@@ -1067,6 +1089,15 @@
1067
1089
  "keyboard": [],
1068
1090
  "screenReader": []
1069
1091
  },
1092
+ {
1093
+ "slug": "stepper",
1094
+ "name": "Stepper",
1095
+ "package": "@nexub/react",
1096
+ "automated": "pass",
1097
+ "exceptions": [],
1098
+ "keyboard": [],
1099
+ "screenReader": []
1100
+ },
1070
1101
  {
1071
1102
  "slug": "streaming-text",
1072
1103
  "name": "Streaming Text",
@@ -1204,6 +1235,24 @@
1204
1235
  "keyboard": [],
1205
1236
  "screenReader": []
1206
1237
  },
1238
+ {
1239
+ "slug": "tree-view",
1240
+ "name": "Tree View",
1241
+ "package": "@nexub/react",
1242
+ "automated": "pass",
1243
+ "exceptions": [],
1244
+ "keyboard": [
1245
+ "ArrowDown / ArrowUp: Next or previous visible item.",
1246
+ "ArrowRight: Opens a closed item, or moves to its first child. Mirrored in RTL.",
1247
+ "ArrowLeft: Closes an open item, or moves to its parent. Mirrored in RTL.",
1248
+ "Home / End: First or last visible item.",
1249
+ "Enter / Space: Selects the item, or toggles its checkbox.",
1250
+ "Type a letter: Moves to the next item starting with the typed text."
1251
+ ],
1252
+ "screenReader": [
1253
+ "The tree is one tab stop (roving tabindex); it returns to the last focused or the selected item."
1254
+ ]
1255
+ },
1207
1256
  {
1208
1257
  "slug": "activity-feed",
1209
1258
  "name": "ActivityFeed",
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ export interface ActionBarProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ /** Status on the start side, e.g. "Unsaved changes". Announced politely. */
4
+ message?: React.ReactNode;
5
+ /** Names the region. @default the message when it's a string */
6
+ 'aria-label'?: string;
7
+ }
8
+ /**
9
+ * Save / cancel that stay in view at the bottom of a long form or settings
10
+ * page. Put it last inside the scrolling area.
11
+ */
12
+ export declare const ActionBar: React.ForwardRefExoticComponent<ActionBarProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,27 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../../utils/cn.js";
4
+ import { floatingSurface } from "../../utils/surface.js";
5
+ const ActionBar = React.forwardRef(function ActionBar2({ message, className, children, "aria-label": ariaLabel, ...props }, ref) {
6
+ return /* @__PURE__ */ jsxs(
7
+ "div",
8
+ {
9
+ ref,
10
+ role: "region",
11
+ "aria-label": ariaLabel ?? (typeof message === "string" ? message : void 0),
12
+ className: cn(
13
+ floatingSurface,
14
+ "sticky bottom-4 z-10 flex flex-wrap items-center gap-3 px-4 py-2",
15
+ className
16
+ ),
17
+ ...props,
18
+ children: [
19
+ message !== void 0 && /* @__PURE__ */ jsx("span", { "aria-live": "polite", className: "me-auto text-label-lg text-[var(--color-on-surface)]", children: message }),
20
+ /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap items-center gap-2", message === void 0 && "ms-auto"), children })
21
+ ]
22
+ }
23
+ );
24
+ });
25
+ export {
26
+ ActionBar
27
+ };
@@ -0,0 +1,2 @@
1
+ export { ActionBar } from './ActionBar.js';
2
+ export type { ActionBarProps } from './ActionBar.js';
@@ -0,0 +1,4 @@
1
+ import { ActionBar } from "./ActionBar.js";
2
+ export {
3
+ ActionBar
4
+ };
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { type ColumnDef, type RowSelectionState, type RowData, type VisibilityState, type ColumnSizingState, type ColumnOrderState, type ColumnPinningState } from '@tanstack/react-table';
2
+ import { type ColumnDef, type RowSelectionState, type RowData, type VisibilityState, type ColumnSizingState, type ColumnOrderState, type ColumnPinningState, type ExpandedState } from '@tanstack/react-table';
3
3
  declare module '@tanstack/react-table' {
4
4
  interface ColumnMeta<TData extends RowData, TValue> {
5
5
  /** Horizontal alignment of the header and cells. Use 'end' for numbers. @default 'start' */
@@ -75,5 +75,14 @@ export interface DataTableProps<TData> extends React.HTMLAttributes<HTMLDivEleme
75
75
  pagination?: {
76
76
  pageSize?: number;
77
77
  };
78
+ /**
79
+ * Nested rows (a tree table): return a row's children. The first column gets
80
+ * an expand toggle and indents each level. Sorting sorts within each level.
81
+ */
82
+ getSubRows?: (row: TData, index: number) => TData[] | undefined;
83
+ /** Expanded rows by id, or `true` for all. */
84
+ expanded?: ExpandedState;
85
+ defaultExpanded?: ExpandedState;
86
+ onExpandedChange?: (expanded: ExpandedState) => void;
78
87
  }
79
- export declare function DataTable<TData>({ columns, data, enableSorting, enableRowSelection, selectionColumn, rowSelection: rowSelectionProp, onRowSelectionChange, getRowId, bulkActions, density: densityProp, defaultDensity, onDensityChange, columnVisibility: columnVisibilityProp, onColumnVisibilityChange, viewOptions, toolbar, enableColumnResizing, columnSizing: columnSizingProp, onColumnSizingChange, enableColumnReordering, columnOrder: columnOrderProp, onColumnOrderChange, columnPinning: columnPinningProp, onColumnPinningChange, maxHeight, grid, rowWindow, pagination, caption, captionHidden, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }: DataTableProps<TData>): React.JSX.Element;
88
+ export declare function DataTable<TData>({ columns, data, enableSorting, enableRowSelection, selectionColumn, rowSelection: rowSelectionProp, onRowSelectionChange, getRowId, bulkActions, density: densityProp, defaultDensity, onDensityChange, columnVisibility: columnVisibilityProp, onColumnVisibilityChange, viewOptions, toolbar, enableColumnResizing, columnSizing: columnSizingProp, onColumnSizingChange, enableColumnReordering, columnOrder: columnOrderProp, onColumnOrderChange, columnPinning: columnPinningProp, onColumnPinningChange, maxHeight, grid, rowWindow, pagination, getSubRows, expanded: expandedProp, defaultExpanded, onExpandedChange, caption, captionHidden, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }: DataTableProps<TData>): React.JSX.Element;
@@ -7,9 +7,10 @@ import {
7
7
  getSortedRowModel,
8
8
  getFilteredRowModel,
9
9
  getPaginationRowModel,
10
+ getExpandedRowModel,
10
11
  flexRender
11
12
  } from "@tanstack/react-table";
12
- import { ArrowUp, ArrowDown, ChevronsUpDown, SlidersHorizontal, X } from "@nexub/icons";
13
+ import { ArrowUp, ArrowDown, ChevronDown, ChevronRight, ChevronsUpDown, SlidersHorizontal, X } from "@nexub/icons";
13
14
  import { cn } from "../../utils/cn.js";
14
15
  import { disabledContainer, pressable } from "../../utils/control.js";
15
16
  import { floatingSurface } from "../../utils/surface.js";
@@ -102,6 +103,34 @@ const useAllRows = (count) => ({
102
103
  before: 0,
103
104
  after: 0
104
105
  });
106
+ function TreeCell({
107
+ row,
108
+ expandLabel,
109
+ collapseLabel,
110
+ children
111
+ }) {
112
+ const open = row.getIsExpanded();
113
+ const ref = React.useCallback(
114
+ (el) => {
115
+ if (el) el.style.paddingInlineStart = `${row.depth * 1.5}rem`;
116
+ },
117
+ [row.depth]
118
+ );
119
+ return /* @__PURE__ */ jsxs("div", { ref, className: "flex items-center gap-1", children: [
120
+ row.getCanExpand() ? /* @__PURE__ */ jsx(
121
+ "button",
122
+ {
123
+ type: "button",
124
+ "aria-expanded": open,
125
+ "aria-label": open ? collapseLabel : expandLabel,
126
+ onClick: row.toggleExpanded,
127
+ className: "-ms-1 inline-flex size-6 shrink-0 items-center justify-center rounded-shape-min text-[var(--color-on-surface-variant)] hover:bg-[var(--color-surface-container-high)]",
128
+ children: open ? /* @__PURE__ */ jsx(ChevronDown, { size: "xs", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronRight, { size: "xs", "aria-hidden": "true", className: "rtl:-scale-x-100" })
129
+ }
130
+ ) : /* @__PURE__ */ jsx("span", { className: "-ms-1 size-6 shrink-0", "aria-hidden": "true" }),
131
+ children
132
+ ] });
133
+ }
105
134
  const px = (n) => typeof n === "number" ? `${n}px` : n;
106
135
  const apply = (u, prev) => typeof u === "function" ? u(prev) : u;
107
136
  const columnLabel = (c) => typeof c.columnDef.header === "string" ? c.columnDef.header : c.id;
@@ -134,6 +163,10 @@ function DataTable({
134
163
  grid = false,
135
164
  rowWindow,
136
165
  pagination,
166
+ getSubRows,
167
+ expanded: expandedProp,
168
+ defaultExpanded,
169
+ onExpandedChange,
137
170
  caption,
138
171
  captionHidden = false,
139
172
  className,
@@ -174,6 +207,11 @@ function DataTable({
174
207
  defaultValue: {},
175
208
  onChange: onColumnPinningChange
176
209
  });
210
+ const [expanded = {}, setExpanded] = useControllableState({
211
+ value: expandedProp,
212
+ defaultValue: defaultExpanded ?? {},
213
+ onChange: onExpandedChange
214
+ });
177
215
  const [announcement, setAnnouncement] = React.useState("");
178
216
  const tableRef = React.useRef(null);
179
217
  const gridNav = React.useMemo(() => createGridNavigation(dir), [dir]);
@@ -223,9 +261,12 @@ function DataTable({
223
261
  columnVisibility,
224
262
  columnSizing,
225
263
  columnOrder,
226
- columnPinning
264
+ columnPinning,
265
+ expanded: getSubRows ? expanded : void 0
227
266
  }),
228
267
  [
268
+ getSubRows,
269
+ expanded,
229
270
  enableSorting,
230
271
  sorting,
231
272
  enableRowSelection,
@@ -257,6 +298,12 @@ function DataTable({
257
298
  getSortedRowModel: enableSorting ? getSortedRowModel() : void 0,
258
299
  getFilteredRowModel: getFilteredRowModel(),
259
300
  getPaginationRowModel: pagination ? getPaginationRowModel() : void 0,
301
+ getSubRows,
302
+ getExpandedRowModel: getSubRows ? getExpandedRowModel() : void 0,
303
+ onExpandedChange: getSubRows ? (u) => setExpanded(apply(u, expanded)) : void 0,
304
+ // With pages, count top-level rows so a parent and its children stay together
305
+ // (TanStack then expands inside the page model; without pages it must not).
306
+ paginateExpandedRows: !pagination,
260
307
  enableRowSelection,
261
308
  manualPagination: false
262
309
  });
@@ -466,7 +513,7 @@ function DataTable({
466
513
  "data-index": i,
467
514
  "aria-rowindex": rowWin.virtual ? i + 2 : void 0,
468
515
  "data-state": enableRowSelection && row.getIsSelected() ? "selected" : void 0,
469
- children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(
516
+ children: row.getVisibleCells().map((cell, _i, cells) => /* @__PURE__ */ jsx(
470
517
  TableCell,
471
518
  {
472
519
  ref: pinned || enableColumnResizing ? placeCell(cell.column) : void 0,
@@ -476,7 +523,7 @@ function DataTable({
476
523
  pinClass(cell.column),
477
524
  enableColumnResizing && "truncate"
478
525
  ),
479
- children: flexRender(cell.column.columnDef.cell, cell.getContext())
526
+ children: getSubRows && cell === cells.find((c) => c.column.id !== "__select") ? /* @__PURE__ */ jsx(TreeCell, { row, expandLabel: messages.expandRow, collapseLabel: messages.collapseRow, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }) : flexRender(cell.column.columnDef.cell, cell.getContext())
480
527
  },
481
528
  cell.id
482
529
  ))
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+ export interface ResizablePanelsProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
3
+ /** Side by side (`horizontal`) or stacked (`vertical`). @default 'horizontal' */
4
+ direction?: 'horizontal' | 'vertical';
5
+ /** One percentage per panel, adding up to 100. Persist them yourself. */
6
+ sizes?: number[];
7
+ defaultSizes?: number[];
8
+ onSizesChange?: (sizes: number[]) => void;
9
+ /** Smallest size per panel, in percent. @default 10 each */
10
+ minSizes?: number[];
11
+ /** Names each separator: "Resize panels" by default. */
12
+ handleLabel?: string;
13
+ /** The panels; a separator goes between each pair. */
14
+ children: React.ReactNode;
15
+ }
16
+ /**
17
+ * Panels with draggable separators (ARIA window splitter). Arrow keys move a
18
+ * focused separator by 5%, Home/End to its limits, and Enter resets the pair.
19
+ */
20
+ export declare function ResizablePanels({ direction, sizes: sizesProp, defaultSizes, onSizesChange, minSizes, handleLabel, className, children, ...props }: ResizablePanelsProps): React.JSX.Element;
@@ -0,0 +1,121 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../../utils/cn.js";
5
+ import { useControllableState } from "../../hooks/useControllableState.js";
6
+ import { useLocale, useNexubMessages } from "../NexubProvider/NexubProvider.js";
7
+ const STEP = 5;
8
+ function ResizablePanels({
9
+ direction = "horizontal",
10
+ sizes: sizesProp,
11
+ defaultSizes,
12
+ onSizesChange,
13
+ minSizes,
14
+ handleLabel,
15
+ className,
16
+ children,
17
+ ...props
18
+ }) {
19
+ const t = useNexubMessages();
20
+ const { dir } = useLocale();
21
+ const panels = React.Children.toArray(children);
22
+ const n = panels.length;
23
+ const even = Array.from({ length: n }, () => 100 / n);
24
+ const initial = defaultSizes ?? even;
25
+ const [sizes = even, setSizes] = useControllableState({
26
+ value: sizesProp,
27
+ defaultValue: initial,
28
+ onChange: onSizesChange
29
+ });
30
+ const rootRef = React.useRef(null);
31
+ const baseId = React.useId();
32
+ const horizontal = direction === "horizontal";
33
+ const min = (i) => minSizes?.[i] ?? 10;
34
+ const resize = (current, i, to) => {
35
+ const pair = current[i] + current[i + 1];
36
+ const a = Math.min(pair - min(i + 1), Math.max(min(i), to));
37
+ const next = [...current];
38
+ next[i] = Math.round(a * 100) / 100;
39
+ next[i + 1] = Math.round((pair - a) * 100) / 100;
40
+ setSizes(next);
41
+ };
42
+ const startDrag = (i, e) => {
43
+ const root = rootRef.current;
44
+ if (!root) return;
45
+ e.preventDefault();
46
+ const rect = root.getBoundingClientRect();
47
+ const total = horizontal ? rect.width : rect.height;
48
+ const origin = horizontal ? e.clientX : e.clientY;
49
+ const start = [...sizes];
50
+ const flip = horizontal && dir === "rtl" ? -1 : 1;
51
+ const onMove = (ev) => {
52
+ const pos = horizontal ? ev.clientX : ev.clientY;
53
+ resize(start, i, start[i] + (pos - origin) * flip / total * 100);
54
+ };
55
+ const onUp = () => {
56
+ window.removeEventListener("pointermove", onMove);
57
+ window.removeEventListener("pointerup", onUp);
58
+ };
59
+ window.addEventListener("pointermove", onMove);
60
+ window.addEventListener("pointerup", onUp);
61
+ };
62
+ const onKeyDown = (i, e) => {
63
+ const fwd = horizontal ? dir === "rtl" ? "ArrowLeft" : "ArrowRight" : "ArrowDown";
64
+ const back = horizontal ? dir === "rtl" ? "ArrowRight" : "ArrowLeft" : "ArrowUp";
65
+ const pair = sizes[i] + sizes[i + 1];
66
+ let to;
67
+ if (e.key === fwd) to = sizes[i] + STEP;
68
+ else if (e.key === back) to = sizes[i] - STEP;
69
+ else if (e.key === "Home") to = min(i);
70
+ else if (e.key === "End") to = pair - min(i + 1);
71
+ else if (e.key === "Enter") to = initial[i] / (initial[i] + initial[i + 1]) * pair;
72
+ if (to === void 0) return;
73
+ e.preventDefault();
74
+ resize(sizes, i, to);
75
+ };
76
+ return /* @__PURE__ */ jsx(
77
+ "div",
78
+ {
79
+ ref: rootRef,
80
+ className: cn("flex min-h-0 min-w-0", horizontal ? "flex-row" : "flex-col", className),
81
+ ...props,
82
+ children: panels.map((panel, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
83
+ /* @__PURE__ */ jsx(
84
+ "div",
85
+ {
86
+ id: `${baseId}-${i}`,
87
+ ref: (el) => {
88
+ if (el) el.style.flex = `${sizes[i] ?? 0} 1 0px`;
89
+ },
90
+ className: "min-h-0 min-w-0 overflow-auto",
91
+ children: panel
92
+ }
93
+ ),
94
+ i < n - 1 && /* @__PURE__ */ jsx(
95
+ "div",
96
+ {
97
+ role: "separator",
98
+ tabIndex: 0,
99
+ "aria-label": handleLabel ?? t.resizePanels,
100
+ "aria-controls": `${baseId}-${i}`,
101
+ "aria-orientation": horizontal ? "vertical" : "horizontal",
102
+ "aria-valuenow": Math.round(sizes[i]),
103
+ "aria-valuemin": min(i),
104
+ "aria-valuemax": Math.round(sizes[i] + sizes[i + 1] - min(i + 1)),
105
+ onPointerDown: (e) => startDrag(i, e),
106
+ onKeyDown: (e) => onKeyDown(i, e),
107
+ className: cn(
108
+ "relative shrink-0 touch-none bg-[var(--color-outline-variant)] outline-none",
109
+ // A wider invisible hit area around the 1px line.
110
+ horizontal ? "w-px cursor-col-resize before:absolute before:-inset-x-1.5 before:inset-y-0" : "h-px cursor-row-resize before:absolute before:inset-x-0 before:-inset-y-1.5",
111
+ "hover:bg-[var(--color-primary)] focus-visible:bg-[var(--color-primary)] focus-visible:shadow-[0_0_0_1px_var(--color-primary)]"
112
+ )
113
+ }
114
+ )
115
+ ] }, i))
116
+ }
117
+ );
118
+ }
119
+ export {
120
+ ResizablePanels
121
+ };
@@ -0,0 +1,2 @@
1
+ export { ResizablePanels } from './ResizablePanels.js';
2
+ export type { ResizablePanelsProps } from './ResizablePanels.js';
@@ -0,0 +1,4 @@
1
+ import { ResizablePanels } from "./ResizablePanels.js";
2
+ export {
3
+ ResizablePanels
4
+ };
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ export interface StepperStep {
3
+ id: string;
4
+ label: string;
5
+ description?: string;
6
+ }
7
+ export interface StepperProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {
8
+ steps: StepperStep[];
9
+ /** Index of the step being shown. */
10
+ current: number;
11
+ /** Indexes of finished steps. @default every step before `current` */
12
+ completed?: number[];
13
+ /**
14
+ * Makes steps clickable. Linear by default: finished steps, the current one
15
+ * and the first unfinished one can be reached; the rest wait.
16
+ */
17
+ onStepChange?: (index: number) => void;
18
+ /** Any step can be reached. */
19
+ nonLinear?: boolean;
20
+ /** @default 'horizontal' */
21
+ orientation?: 'horizontal' | 'vertical';
22
+ }
23
+ /**
24
+ * Progress through a multi-step flow. Validate each step (a `Form` per step
25
+ * works well) and move `current` forward from its submit.
26
+ */
27
+ export declare const Stepper: React.ForwardRefExoticComponent<StepperProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,74 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Check } from "@nexub/icons";
5
+ import { cn } from "../../utils/cn.js";
6
+ import { useNexubMessages } from "../NexubProvider/NexubProvider.js";
7
+ const Stepper = React.forwardRef(function Stepper2({ steps, current, completed: completedProp, onStepChange, nonLinear = false, orientation = "horizontal", className, ...props }, ref) {
8
+ const t = useNexubMessages();
9
+ const completed = new Set(completedProp ?? steps.slice(0, current).map((_, i) => i));
10
+ const firstOpen = steps.findIndex((_, i) => !completed.has(i));
11
+ const vertical = orientation === "vertical";
12
+ return /* @__PURE__ */ jsx("nav", { ref, className, ...props, children: /* @__PURE__ */ jsx("ol", { className: cn("m-0 flex list-none p-0", vertical ? "flex-col" : "flex-row items-start"), children: steps.map((step, i) => {
13
+ const done = completed.has(i);
14
+ const isCurrent = i === current;
15
+ const reachable = Boolean(onStepChange) && !isCurrent && (nonLinear || done || i === firstOpen);
16
+ const marker = /* @__PURE__ */ jsx(
17
+ "span",
18
+ {
19
+ "aria-hidden": "true",
20
+ className: cn(
21
+ "inline-flex size-8 shrink-0 items-center justify-center rounded-full text-label-md font-semibold",
22
+ done && !isCurrent ? "bg-[var(--color-primary)] text-[var(--color-on-primary)]" : isCurrent ? "border-2 border-[var(--color-primary)] bg-[var(--color-surface)] text-[var(--color-primary)]" : "border-2 border-[var(--color-outline-variant)] bg-[var(--color-surface)] text-[var(--color-on-surface-muted)]"
23
+ ),
24
+ children: done && !isCurrent ? /* @__PURE__ */ jsx(Check, { size: "xs" }) : i + 1
25
+ }
26
+ );
27
+ const text = /* @__PURE__ */ jsxs("span", { className: cn("flex min-w-0 flex-col gap-0.5", vertical ? "items-start text-start" : "items-center text-center"), children: [
28
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
29
+ t.stepOf(i + 1, steps.length),
30
+ ":"
31
+ ] }),
32
+ " ",
33
+ /* @__PURE__ */ jsx("span", { className: cn("text-label-lg font-medium", isCurrent || done ? "text-[var(--color-on-surface)]" : "text-[var(--color-on-surface-muted)]"), children: step.label }),
34
+ done && /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
35
+ ", ",
36
+ t.stepCompleted
37
+ ] }),
38
+ step.description && /* @__PURE__ */ jsx("span", { className: "text-body-sm text-[var(--color-on-surface-muted)]", children: step.description })
39
+ ] });
40
+ const body = vertical ? /* @__PURE__ */ jsxs("span", { className: "flex gap-3", children: [
41
+ marker,
42
+ /* @__PURE__ */ jsx("span", { className: "pt-1", children: text })
43
+ ] }) : /* @__PURE__ */ jsxs("span", { className: "flex flex-col items-center gap-2 text-center", children: [
44
+ marker,
45
+ text
46
+ ] });
47
+ return /* @__PURE__ */ jsx(
48
+ "li",
49
+ {
50
+ "aria-current": isCurrent ? "step" : void 0,
51
+ className: cn(
52
+ "relative flex",
53
+ vertical ? "pb-6 last:pb-0" : "flex-1 justify-center",
54
+ // Connector to the next step.
55
+ i < steps.length - 1 && (vertical ? "after:absolute after:start-4 after:top-9 after:bottom-1 after:w-px" : "after:absolute after:top-4 after:start-[calc(50%+1.25rem)] after:end-[calc(-50%+1.25rem)] after:h-px"),
56
+ i < steps.length - 1 && (done ? "after:bg-[var(--color-primary)]" : "after:bg-[var(--color-outline-variant)]")
57
+ ),
58
+ children: reachable ? /* @__PURE__ */ jsx(
59
+ "button",
60
+ {
61
+ type: "button",
62
+ onClick: () => onStepChange(i),
63
+ className: "rounded-shape-control p-1 hover:bg-[var(--color-surface-container-high)]",
64
+ children: body
65
+ }
66
+ ) : /* @__PURE__ */ jsx("span", { className: "p-1", children: body })
67
+ },
68
+ step.id
69
+ );
70
+ }) }) });
71
+ });
72
+ export {
73
+ Stepper
74
+ };
@@ -0,0 +1,2 @@
1
+ export { Stepper } from './Stepper.js';
2
+ export type { StepperProps, StepperStep } from './Stepper.js';
@@ -0,0 +1,4 @@
1
+ import { Stepper } from "./Stepper.js";
2
+ export {
3
+ Stepper
4
+ };
@@ -0,0 +1,38 @@
1
+ import * as React from 'react';
2
+ export interface TreeNode {
3
+ id: string;
4
+ label: React.ReactNode;
5
+ /** Text for type-ahead when `label` isn't a string. */
6
+ textValue?: string;
7
+ icon?: React.ReactNode;
8
+ children?: TreeNode[];
9
+ /** Children exist but aren't loaded yet: expanding calls `onLoadChildren`. */
10
+ hasChildren?: boolean;
11
+ disabled?: boolean;
12
+ }
13
+ export interface TreeViewProps {
14
+ nodes: TreeNode[];
15
+ 'aria-label'?: string;
16
+ 'aria-labelledby'?: string;
17
+ expanded?: string[];
18
+ defaultExpanded?: string[];
19
+ onExpandedChange?: (ids: string[]) => void;
20
+ /** Single selection (ignored when `checkable`). */
21
+ selected?: string | null;
22
+ defaultSelected?: string | null;
23
+ onSelectedChange?: (id: string | null) => void;
24
+ /** Checkboxes with tri-state parents; `checked` lists every checked id, parents included. */
25
+ checkable?: boolean;
26
+ checked?: string[];
27
+ defaultChecked?: string[];
28
+ onCheckedChange?: (ids: string[]) => void;
29
+ /** Load a `hasChildren` node's children, then pass them in through `nodes`. */
30
+ onLoadChildren?: (node: TreeNode) => Promise<unknown>;
31
+ className?: string;
32
+ }
33
+ /**
34
+ * A hierarchy people can browse with the keyboard (ARIA tree): Arrow keys move
35
+ * and expand, Home/End jump, typing finds an item. Optional lazy children and
36
+ * tri-state checkboxes.
37
+ */
38
+ export declare function TreeView({ nodes, expanded: expandedProp, defaultExpanded, onExpandedChange, selected: selectedProp, defaultSelected, onSelectedChange, checkable, checked: checkedProp, defaultChecked, onCheckedChange, onLoadChildren, className, ...aria }: TreeViewProps): React.JSX.Element;