@burtson-labs/ui 0.8.1 → 0.9.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.
@@ -0,0 +1,278 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { Spinner } from "./spinner.js";
3
+ import * as React from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import ChevronRight from "@burtson-labs/icons/react/chevron-right";
6
+ //#region src/components/tree-view.tsx
7
+ var isExpandable = (n) => (n.children?.length ?? 0) > 0 || Boolean(n.hasChildren);
8
+ /** The rows a person can see, in order, with the ARIA position data for each. */
9
+ function flattenTree(nodes, expanded) {
10
+ const rows = [];
11
+ const walk = (list, level, parentId) => {
12
+ list.forEach((node, i) => {
13
+ const expandable = isExpandable(node);
14
+ const open = expandable && expanded.has(node.id);
15
+ rows.push({
16
+ node,
17
+ level,
18
+ parentId,
19
+ setSize: list.length,
20
+ posInSet: i + 1,
21
+ expandable,
22
+ expanded: open
23
+ });
24
+ if (open && node.children) walk(node.children, level + 1, node.id);
25
+ });
26
+ };
27
+ walk(nodes, 1, null);
28
+ return rows;
29
+ }
30
+ var isTypingTarget = (target) => target instanceof HTMLElement && (target.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName));
31
+ /**
32
+ * A file-explorer style tree. Everything is controlled: the app owns which
33
+ * nodes are expanded and selected, and file operations are callbacks. Rows
34
+ * render flat (role="treeitem" with aria-level), one tab stop for the tree,
35
+ * arrows to move, Right/Left to open and close, Home/End, typeahead.
36
+ */
37
+ function TreeView({ nodes, expanded, onExpandedChange, selected, onSelectedChange, selectionMode = "single", selectionFollowsFocus = false, onAction, onLoadChildren, renderLabel, density = "default", empty, onRowContextMenu, className, ...props }) {
38
+ const expandedSet = React.useMemo(() => new Set(expanded), [expanded]);
39
+ const selectedSet = React.useMemo(() => new Set(selected), [selected]);
40
+ const rows = React.useMemo(() => flattenTree(nodes, expandedSet), [nodes, expandedSet]);
41
+ const index = React.useMemo(() => new Map(rows.map((r, i) => [r.node.id, i])), [rows]);
42
+ const [focusedId, setFocusedId] = React.useState(null);
43
+ const anchorId = React.useRef(null);
44
+ const rowRefs = React.useRef(/* @__PURE__ */ new Map());
45
+ const treeRef = React.useRef(null);
46
+ const hadFocus = React.useRef(false);
47
+ const lastIds = React.useRef([]);
48
+ const lastParents = React.useRef(/* @__PURE__ */ new Map());
49
+ const typeahead = React.useRef({
50
+ text: "",
51
+ at: 0
52
+ });
53
+ const tabStopId = (focusedId && index.has(focusedId) ? focusedId : null) ?? rows.find((r) => selectedSet.has(r.node.id))?.node.id ?? rows[0]?.node.id ?? null;
54
+ const focusRow = React.useCallback((id) => {
55
+ setFocusedId(id);
56
+ rowRefs.current.get(id)?.focus();
57
+ rowRefs.current.get(id)?.scrollIntoView?.({ block: "nearest" });
58
+ }, []);
59
+ React.useLayoutEffect(() => {
60
+ if (focusedId && !index.has(focusedId)) {
61
+ let next = null;
62
+ let parent = lastParents.current.get(focusedId) ?? null;
63
+ while (parent && !index.has(parent)) parent = lastParents.current.get(parent) ?? null;
64
+ const was = Math.max(0, lastIds.current.indexOf(focusedId));
65
+ next = parent ?? rows[Math.min(was, rows.length - 1)]?.node.id ?? null;
66
+ setFocusedId(next);
67
+ const active = document.activeElement;
68
+ if (next && hadFocus.current && (!active || active === document.body)) rowRefs.current.get(next)?.focus();
69
+ }
70
+ lastIds.current = rows.map((r) => r.node.id);
71
+ lastParents.current = new Map(rows.map((r) => [r.node.id, r.parentId]));
72
+ }, [
73
+ rows,
74
+ index,
75
+ focusedId
76
+ ]);
77
+ const setExpanded = (row, open) => {
78
+ if (!row.expandable || row.expanded === open) return;
79
+ const id = row.node.id;
80
+ onExpandedChange(open ? [...expanded, id] : expanded.filter((e) => e !== id));
81
+ if (open && !row.node.children && row.node.hasChildren) onLoadChildren?.(id);
82
+ };
83
+ const selectRange = (toId) => {
84
+ const from = index.get(anchorId.current ?? toId) ?? 0;
85
+ const to = index.get(toId) ?? 0;
86
+ const [a, b] = from < to ? [from, to] : [to, from];
87
+ onSelectedChange(rows.slice(a, b + 1).filter((r) => !r.node.disabled).map((r) => r.node.id));
88
+ };
89
+ const select = (id, mode) => {
90
+ if (rows[index.get(id) ?? -1]?.node.disabled) return;
91
+ if (selectionMode === "single" || mode === "replace") {
92
+ anchorId.current = id;
93
+ onSelectedChange([id]);
94
+ } else if (mode === "toggle") {
95
+ anchorId.current = id;
96
+ onSelectedChange(selectedSet.has(id) ? selected.filter((s) => s !== id) : [...selected, id]);
97
+ } else selectRange(id);
98
+ };
99
+ const moveTo = (i, extend) => {
100
+ const row = rows[Math.max(0, Math.min(rows.length - 1, i))];
101
+ if (!row) return;
102
+ focusRow(row.node.id);
103
+ if (selectionMode === "multiple" && extend) selectRange(row.node.id);
104
+ else if (selectionFollowsFocus && !row.node.disabled) select(row.node.id, "replace");
105
+ };
106
+ const onKeyDown = (event) => {
107
+ props.onKeyDown?.(event);
108
+ if (event.defaultPrevented || isTypingTarget(event.target)) return;
109
+ const id = tabStopId;
110
+ if (!id) return;
111
+ const i = index.get(id) ?? 0;
112
+ const row = rows[i];
113
+ if (!row) return;
114
+ const mod = event.metaKey || event.ctrlKey;
115
+ let handled = true;
116
+ switch (event.key) {
117
+ case "ArrowDown":
118
+ moveTo(i + 1, event.shiftKey);
119
+ break;
120
+ case "ArrowUp":
121
+ moveTo(i - 1, event.shiftKey);
122
+ break;
123
+ case "Home":
124
+ moveTo(0, event.shiftKey);
125
+ break;
126
+ case "End":
127
+ moveTo(rows.length - 1, event.shiftKey);
128
+ break;
129
+ case "ArrowRight":
130
+ if (row.expandable && !row.expanded) setExpanded(row, true);
131
+ else if (row.expanded && rows[i + 1]?.parentId === row.node.id) moveTo(i + 1, event.shiftKey);
132
+ break;
133
+ case "ArrowLeft":
134
+ if (row.expanded) setExpanded(row, false);
135
+ else if (row.parentId) moveTo(index.get(row.parentId) ?? i, event.shiftKey);
136
+ break;
137
+ case "Enter":
138
+ if (row.node.disabled) break;
139
+ select(row.node.id, "replace");
140
+ onAction?.(row.node.id);
141
+ break;
142
+ case " ":
143
+ select(row.node.id, selectionMode === "multiple" ? event.shiftKey ? "range" : "toggle" : "replace");
144
+ break;
145
+ case "a":
146
+ if (mod && selectionMode === "multiple") onSelectedChange(rows.filter((r) => !r.node.disabled).map((r) => r.node.id));
147
+ else handled = false;
148
+ break;
149
+ case "ContextMenu":
150
+ onRowContextMenu?.(row.node.id, event);
151
+ break;
152
+ default: handled = false;
153
+ }
154
+ if (!handled && event.key.length === 1 && !mod && !event.altKey) {
155
+ const now = event.timeStamp;
156
+ const t = typeahead.current;
157
+ t.text = now - t.at > 600 ? event.key : t.text + event.key;
158
+ t.at = now;
159
+ const query = t.text.toLowerCase();
160
+ const start = t.text.length === 1 ? i + 1 : i;
161
+ for (let k = 0; k < rows.length; k++) {
162
+ const r = rows[(start + k) % rows.length];
163
+ if (r && r.node.label.toLowerCase().startsWith(query)) {
164
+ moveTo(index.get(r.node.id) ?? 0, false);
165
+ break;
166
+ }
167
+ }
168
+ handled = true;
169
+ }
170
+ if (handled) {
171
+ event.preventDefault();
172
+ event.stopPropagation();
173
+ }
174
+ };
175
+ if (rows.length === 0 && empty) return /* @__PURE__ */ jsx("div", {
176
+ "data-slot": "tree-view",
177
+ "data-empty": true,
178
+ className: cn("text-[13px]", className),
179
+ ...props,
180
+ children: empty
181
+ });
182
+ return /* @__PURE__ */ jsx("div", {
183
+ ref: treeRef,
184
+ role: "tree",
185
+ tabIndex: -1,
186
+ "data-slot": "tree-view",
187
+ "data-density": density,
188
+ "aria-multiselectable": selectionMode === "multiple" || void 0,
189
+ className: cn("group/tree flex flex-col py-1 text-[13px] select-none", className),
190
+ ...props,
191
+ onKeyDown,
192
+ onFocus: (event) => {
193
+ hadFocus.current = true;
194
+ if (event.target === event.currentTarget && tabStopId) rowRefs.current.get(tabStopId)?.focus();
195
+ props.onFocus?.(event);
196
+ },
197
+ onBlur: (event) => {
198
+ if (!treeRef.current?.contains(event.relatedTarget)) hadFocus.current = false;
199
+ props.onBlur?.(event);
200
+ },
201
+ children: rows.map((row) => {
202
+ const { node } = row;
203
+ const isSelected = selectedSet.has(node.id);
204
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [/* @__PURE__ */ jsxs("div", {
205
+ ref: (el) => {
206
+ if (el) rowRefs.current.set(node.id, el);
207
+ else rowRefs.current.delete(node.id);
208
+ },
209
+ role: "treeitem",
210
+ "data-slot": "tree-item",
211
+ "data-id": node.id,
212
+ "aria-level": row.level,
213
+ "aria-setsize": row.setSize,
214
+ "aria-posinset": row.posInSet,
215
+ "aria-expanded": row.expandable ? row.expanded : void 0,
216
+ "aria-selected": isSelected,
217
+ "aria-disabled": node.disabled || void 0,
218
+ "aria-busy": row.expanded && node.loading || void 0,
219
+ "aria-label": node.label,
220
+ tabIndex: node.id === tabStopId ? 0 : -1,
221
+ "data-state": isSelected ? "selected" : void 0,
222
+ style: { paddingInlineStart: `${(row.level - 1) * 12 + 4}px` },
223
+ className: cn("flex h-7 shrink-0 cursor-default items-center gap-1 rounded-sm pe-2 outline-none group-data-[density=compact]/tree:h-6", "hover:bg-muted/70 data-[state=selected]:bg-brand-soft/70 data-[state=selected]:text-foreground", "focus-visible:shadow-[inset_0_0_0_1px_var(--ring)] aria-disabled:opacity-50"),
224
+ onFocus: () => setFocusedId(node.id),
225
+ onClick: (event) => {
226
+ if (isTypingTarget(event.target)) return;
227
+ select(node.id, event.shiftKey ? "range" : event.metaKey || event.ctrlKey ? "toggle" : "replace");
228
+ if (!event.shiftKey && !event.metaKey && !event.ctrlKey && row.expandable && selectionMode === "single") setExpanded(row, !row.expanded);
229
+ },
230
+ onDoubleClick: (event) => {
231
+ if (!isTypingTarget(event.target) && !node.disabled) onAction?.(node.id);
232
+ },
233
+ onContextMenu: onRowContextMenu ? (event) => onRowContextMenu(node.id, event) : void 0,
234
+ children: [
235
+ /* @__PURE__ */ jsx("span", {
236
+ "aria-hidden": true,
237
+ className: "flex size-4 shrink-0 items-center justify-center text-muted-foreground",
238
+ onClick: (event) => {
239
+ if (!row.expandable) return;
240
+ event.stopPropagation();
241
+ setExpanded(row, !row.expanded);
242
+ },
243
+ children: row.expandable && /* @__PURE__ */ jsx(ChevronRight, { className: cn("size-3.5 motion-safe:transition-transform", row.expanded && "rotate-90") })
244
+ }),
245
+ node.icon && /* @__PURE__ */ jsx("span", {
246
+ "aria-hidden": true,
247
+ className: "flex shrink-0 text-muted-foreground [&_svg]:size-4",
248
+ children: node.icon
249
+ }),
250
+ /* @__PURE__ */ jsx("span", {
251
+ className: "min-w-0 flex-1 truncate",
252
+ children: renderLabel ? renderLabel(node) : node.label
253
+ }),
254
+ node.meta && /* @__PURE__ */ jsx("span", {
255
+ className: "shrink-0 text-xs text-muted-foreground",
256
+ children: node.meta
257
+ })
258
+ ]
259
+ }), row.expanded && node.loading && !node.children?.length && /* @__PURE__ */ jsxs("div", {
260
+ role: "presentation",
261
+ "data-slot": "tree-loading",
262
+ style: { paddingInlineStart: `${row.level * 12 + 8}px` },
263
+ className: "flex h-7 items-center gap-2 text-muted-foreground group-data-[density=compact]/tree:h-6",
264
+ children: [/* @__PURE__ */ jsx(Spinner, {
265
+ className: "size-3.5",
266
+ label: `Loading ${node.label}`
267
+ }), /* @__PURE__ */ jsx("span", {
268
+ "aria-hidden": true,
269
+ children: "Loading…"
270
+ })]
271
+ })] }, node.id);
272
+ })
273
+ });
274
+ }
275
+ //#endregion
276
+ export { TreeView, flattenTree };
277
+
278
+ //# sourceMappingURL=tree-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-view.js","names":[],"sources":["../../src/components/tree-view.tsx"],"sourcesContent":["import ChevronRight from '@burtson-labs/icons/react/chevron-right';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport { Spinner } from './spinner';\n\nexport interface TreeNode {\n /** Stable identity. Keep it across renders (a path, a database id). */\n id: string;\n /** Plain text: the accessible name and the typeahead key. */\n label: string;\n icon?: React.ReactNode;\n /** Undefined: a leaf, unless `hasChildren` says the children load later. */\n children?: TreeNode[];\n /** Expandable before its children are known; expanding calls onLoadChildren. */\n hasChildren?: boolean;\n /** Children are being fetched; shows a loading row while expanded. */\n loading?: boolean;\n disabled?: boolean;\n /** Trailing detail, such as a git status letter or a count. */\n meta?: React.ReactNode;\n}\n\nexport interface TreeViewProps extends Omit<\n React.ComponentProps<'div'>,\n 'children' | 'onSelect' | 'aria-label'\n> {\n nodes: TreeNode[];\n /** Required: what the tree holds, e.g. \"Files\". */\n 'aria-label': string;\n expanded: string[];\n onExpandedChange: (ids: string[]) => void;\n selected: string[];\n onSelectedChange: (ids: string[]) => void;\n /** `multiple`: Shift extends a range, Ctrl/⌘ toggles, Space toggles. */\n selectionMode?: 'single' | 'multiple';\n /** Move selection with focus on arrow keys (a file explorer); default off. */\n selectionFollowsFocus?: boolean;\n /** Enter or double-click: open the file, run the item. */\n onAction?: (id: string) => void;\n /** Called once when a node with `hasChildren` and no children expands. */\n onLoadChildren?: (id: string) => void;\n /** Replace the label, e.g. with a rename input. Keys typed in an input never move focus. */\n renderLabel?: (node: TreeNode) => React.ReactNode;\n /** `compact` 24px rows for desktop explorers, `default` 28px. */\n density?: 'compact' | 'default';\n /** Shown when `nodes` is empty. */\n empty?: React.ReactNode;\n /** Right-click or the context-menu key on a row. */\n onRowContextMenu?: (id: string, event: React.MouseEvent | React.KeyboardEvent) => void;\n}\n\ninterface Row {\n node: TreeNode;\n level: number;\n parentId: string | null;\n setSize: number;\n posInSet: number;\n expandable: boolean;\n expanded: boolean;\n}\n\nconst isExpandable = (n: TreeNode) => (n.children?.length ?? 0) > 0 || Boolean(n.hasChildren);\n\n/** The rows a person can see, in order, with the ARIA position data for each. */\nexport function flattenTree(nodes: TreeNode[], expanded: ReadonlySet<string>): Row[] {\n const rows: Row[] = [];\n const walk = (list: TreeNode[], level: number, parentId: string | null) => {\n list.forEach((node, i) => {\n const expandable = isExpandable(node);\n const open = expandable && expanded.has(node.id);\n rows.push({\n node,\n level,\n parentId,\n setSize: list.length,\n posInSet: i + 1,\n expandable,\n expanded: open,\n });\n if (open && node.children) walk(node.children, level + 1, node.id);\n });\n };\n walk(nodes, 1, null);\n return rows;\n}\n\nconst isTypingTarget = (target: EventTarget | null) =>\n target instanceof HTMLElement &&\n (target.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName));\n\n/**\n * A file-explorer style tree. Everything is controlled: the app owns which\n * nodes are expanded and selected, and file operations are callbacks. Rows\n * render flat (role=\"treeitem\" with aria-level), one tab stop for the tree,\n * arrows to move, Right/Left to open and close, Home/End, typeahead.\n */\nfunction TreeView({\n nodes,\n expanded,\n onExpandedChange,\n selected,\n onSelectedChange,\n selectionMode = 'single',\n selectionFollowsFocus = false,\n onAction,\n onLoadChildren,\n renderLabel,\n density = 'default',\n empty,\n onRowContextMenu,\n className,\n ...props\n}: TreeViewProps) {\n const expandedSet = React.useMemo(() => new Set(expanded), [expanded]);\n const selectedSet = React.useMemo(() => new Set(selected), [selected]);\n const rows = React.useMemo(() => flattenTree(nodes, expandedSet), [nodes, expandedSet]);\n const index = React.useMemo(() => new Map(rows.map((r, i) => [r.node.id, i])), [rows]);\n\n const [focusedId, setFocusedId] = React.useState<string | null>(null);\n const anchorId = React.useRef<string | null>(null);\n const rowRefs = React.useRef(new Map<string, HTMLDivElement>());\n const treeRef = React.useRef<HTMLDivElement>(null);\n const hadFocus = React.useRef(false);\n const lastIds = React.useRef<string[]>([]);\n const lastParents = React.useRef(new Map<string, string | null>());\n const typeahead = React.useRef({ text: '', at: 0 });\n\n // The tab stop: the focused row if it is still visible, else the first\n // selected visible row, else the first row.\n const tabStopId =\n (focusedId && index.has(focusedId) ? focusedId : null) ??\n rows.find((r) => selectedSet.has(r.node.id))?.node.id ??\n rows[0]?.node.id ??\n null;\n\n const focusRow = React.useCallback((id: string) => {\n setFocusedId(id);\n rowRefs.current.get(id)?.focus();\n rowRefs.current.get(id)?.scrollIntoView?.({ block: 'nearest' });\n }, []);\n\n // Focus recovery: when the focused row disappears (deleted, or a parent\n // collapsed), move to its nearest visible ancestor, else the row now at its\n // old position. Only takes DOM focus if the tree had it.\n React.useLayoutEffect(() => {\n if (focusedId && !index.has(focusedId)) {\n let next: string | null = null;\n let parent = lastParents.current.get(focusedId) ?? null;\n while (parent && !index.has(parent)) parent = lastParents.current.get(parent) ?? null;\n const was = Math.max(0, lastIds.current.indexOf(focusedId));\n next = parent ?? rows[Math.min(was, rows.length - 1)]?.node.id ?? null;\n setFocusedId(next);\n const active = document.activeElement;\n if (next && hadFocus.current && (!active || active === document.body))\n rowRefs.current.get(next)?.focus();\n }\n lastIds.current = rows.map((r) => r.node.id);\n lastParents.current = new Map(rows.map((r) => [r.node.id, r.parentId]));\n }, [rows, index, focusedId]);\n\n const setExpanded = (row: Row, open: boolean) => {\n if (!row.expandable || row.expanded === open) return;\n const id = row.node.id;\n onExpandedChange(open ? [...expanded, id] : expanded.filter((e) => e !== id));\n if (open && !row.node.children && row.node.hasChildren) onLoadChildren?.(id);\n };\n\n const selectRange = (toId: string) => {\n const from = index.get(anchorId.current ?? toId) ?? 0;\n const to = index.get(toId) ?? 0;\n const [a, b] = from < to ? [from, to] : [to, from];\n onSelectedChange(\n rows\n .slice(a, b + 1)\n .filter((r) => !r.node.disabled)\n .map((r) => r.node.id),\n );\n };\n\n const select = (id: string, mode: 'replace' | 'toggle' | 'range') => {\n if (rows[index.get(id) ?? -1]?.node.disabled) return;\n if (selectionMode === 'single' || mode === 'replace') {\n anchorId.current = id;\n onSelectedChange([id]);\n } else if (mode === 'toggle') {\n anchorId.current = id;\n onSelectedChange(selectedSet.has(id) ? selected.filter((s) => s !== id) : [...selected, id]);\n } else selectRange(id);\n };\n\n const moveTo = (i: number, extend: boolean) => {\n const row = rows[Math.max(0, Math.min(rows.length - 1, i))];\n if (!row) return;\n focusRow(row.node.id);\n if (selectionMode === 'multiple' && extend) selectRange(row.node.id);\n else if (selectionFollowsFocus && !row.node.disabled) select(row.node.id, 'replace');\n };\n\n const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n props.onKeyDown?.(event);\n if (event.defaultPrevented || isTypingTarget(event.target)) return;\n const id = tabStopId;\n if (!id) return;\n const i = index.get(id) ?? 0;\n const row = rows[i];\n if (!row) return;\n const mod = event.metaKey || event.ctrlKey;\n let handled = true;\n switch (event.key) {\n case 'ArrowDown':\n moveTo(i + 1, event.shiftKey);\n break;\n case 'ArrowUp':\n moveTo(i - 1, event.shiftKey);\n break;\n case 'Home':\n moveTo(0, event.shiftKey);\n break;\n case 'End':\n moveTo(rows.length - 1, event.shiftKey);\n break;\n case 'ArrowRight':\n if (row.expandable && !row.expanded) setExpanded(row, true);\n else if (row.expanded && rows[i + 1]?.parentId === row.node.id)\n moveTo(i + 1, event.shiftKey);\n break;\n case 'ArrowLeft':\n if (row.expanded) setExpanded(row, false);\n else if (row.parentId) moveTo(index.get(row.parentId) ?? i, event.shiftKey);\n break;\n case 'Enter':\n if (row.node.disabled) break;\n select(row.node.id, 'replace');\n onAction?.(row.node.id);\n break;\n case ' ':\n select(\n row.node.id,\n selectionMode === 'multiple' ? (event.shiftKey ? 'range' : 'toggle') : 'replace',\n );\n break;\n case 'a':\n if (mod && selectionMode === 'multiple') {\n onSelectedChange(rows.filter((r) => !r.node.disabled).map((r) => r.node.id));\n } else handled = false;\n break;\n case 'ContextMenu':\n onRowContextMenu?.(row.node.id, event);\n break;\n default:\n handled = false;\n }\n if (!handled && event.key.length === 1 && !mod && !event.altKey) {\n // Typeahead: jump to the next row whose label starts with what was typed.\n const now = event.timeStamp;\n const t = typeahead.current;\n t.text = now - t.at > 600 ? event.key : t.text + event.key;\n t.at = now;\n const query = t.text.toLowerCase();\n const start = t.text.length === 1 ? i + 1 : i;\n for (let k = 0; k < rows.length; k++) {\n const r = rows[(start + k) % rows.length];\n if (r && r.node.label.toLowerCase().startsWith(query)) {\n moveTo(index.get(r.node.id) ?? 0, false);\n break;\n }\n }\n handled = true;\n }\n if (handled) {\n event.preventDefault();\n event.stopPropagation();\n }\n };\n\n if (rows.length === 0 && empty) {\n return (\n <div data-slot=\"tree-view\" data-empty className={cn('text-[13px]', className)} {...props}>\n {empty}\n </div>\n );\n }\n\n return (\n <div\n ref={treeRef}\n role=\"tree\"\n tabIndex={-1}\n data-slot=\"tree-view\"\n data-density={density}\n aria-multiselectable={selectionMode === 'multiple' || undefined}\n className={cn('group/tree flex flex-col py-1 text-[13px] select-none', className)}\n {...props}\n onKeyDown={onKeyDown}\n onFocus={(event) => {\n hadFocus.current = true;\n // Focus landing on the tree itself (a click on empty space) goes to the tab stop.\n if (event.target === event.currentTarget && tabStopId)\n rowRefs.current.get(tabStopId)?.focus();\n props.onFocus?.(event);\n }}\n onBlur={(event) => {\n if (!treeRef.current?.contains(event.relatedTarget)) hadFocus.current = false;\n props.onBlur?.(event);\n }}\n >\n {rows.map((row) => {\n const { node } = row;\n const isSelected = selectedSet.has(node.id);\n return (\n <React.Fragment key={node.id}>\n {/* Keys are handled once on the tree, for whichever row has focus. */}\n {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}\n <div\n ref={(el) => {\n if (el) rowRefs.current.set(node.id, el);\n else rowRefs.current.delete(node.id);\n }}\n role=\"treeitem\"\n data-slot=\"tree-item\"\n data-id={node.id}\n aria-level={row.level}\n aria-setsize={row.setSize}\n aria-posinset={row.posInSet}\n aria-expanded={row.expandable ? row.expanded : undefined}\n aria-selected={isSelected}\n aria-disabled={node.disabled || undefined}\n aria-busy={(row.expanded && node.loading) || undefined}\n aria-label={node.label}\n tabIndex={node.id === tabStopId ? 0 : -1}\n data-state={isSelected ? 'selected' : undefined}\n style={{ paddingInlineStart: `${(row.level - 1) * 12 + 4}px` }}\n className={cn(\n 'flex h-7 shrink-0 cursor-default items-center gap-1 rounded-sm pe-2 outline-none group-data-[density=compact]/tree:h-6',\n 'hover:bg-muted/70 data-[state=selected]:bg-brand-soft/70 data-[state=selected]:text-foreground',\n 'focus-visible:shadow-[inset_0_0_0_1px_var(--ring)] aria-disabled:opacity-50',\n )}\n onFocus={() => setFocusedId(node.id)}\n onClick={(event) => {\n if (isTypingTarget(event.target)) return;\n select(\n node.id,\n event.shiftKey ? 'range' : event.metaKey || event.ctrlKey ? 'toggle' : 'replace',\n );\n if (\n !event.shiftKey &&\n !event.metaKey &&\n !event.ctrlKey &&\n row.expandable &&\n selectionMode === 'single'\n )\n setExpanded(row, !row.expanded);\n }}\n onDoubleClick={(event) => {\n if (!isTypingTarget(event.target) && !node.disabled) onAction?.(node.id);\n }}\n onContextMenu={\n onRowContextMenu ? (event) => onRowContextMenu(node.id, event) : undefined\n }\n >\n {/* A pointer shortcut only: Right and Left expand and collapse from the keyboard. */}\n <span\n aria-hidden\n className=\"flex size-4 shrink-0 items-center justify-center text-muted-foreground\"\n onClick={(event) => {\n if (!row.expandable) return;\n event.stopPropagation();\n setExpanded(row, !row.expanded);\n }}\n >\n {row.expandable && (\n <ChevronRight\n className={cn(\n 'size-3.5 motion-safe:transition-transform',\n row.expanded && 'rotate-90',\n )}\n />\n )}\n </span>\n {node.icon && (\n <span aria-hidden className=\"flex shrink-0 text-muted-foreground [&_svg]:size-4\">\n {node.icon}\n </span>\n )}\n <span className=\"min-w-0 flex-1 truncate\">\n {renderLabel ? renderLabel(node) : node.label}\n </span>\n {node.meta && (\n <span className=\"shrink-0 text-xs text-muted-foreground\">{node.meta}</span>\n )}\n </div>\n {row.expanded && node.loading && !node.children?.length && (\n <div\n role=\"presentation\"\n data-slot=\"tree-loading\"\n style={{ paddingInlineStart: `${row.level * 12 + 8}px` }}\n className=\"flex h-7 items-center gap-2 text-muted-foreground group-data-[density=compact]/tree:h-6\"\n >\n <Spinner className=\"size-3.5\" label={`Loading ${node.label}`} />\n <span aria-hidden>Loading…</span>\n </div>\n )}\n </React.Fragment>\n );\n })}\n </div>\n );\n}\n\nexport { TreeView };\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -15,10 +15,12 @@ export * from './components/collapsible';
15
15
  export * from './components/combobox';
16
16
  export * from './components/command';
17
17
  export * from './components/composer';
18
+ export * from './components/context-menu';
18
19
  export * from './components/conversation';
19
20
  export * from './components/copy-button';
20
21
  export * from './components/dialog';
21
22
  export * from './components/dropdown-menu';
23
+ export * from './components/editor-tabs';
22
24
  export * from './components/empty-state';
23
25
  export * from './components/field';
24
26
  export * from './components/icon-button';
@@ -26,14 +28,17 @@ export * from './components/input';
26
28
  export * from './components/kbd';
27
29
  export * from './components/label';
28
30
  export * from './components/markdown';
31
+ export * from './components/menubar';
29
32
  export * from './components/message';
30
33
  export * from './components/navigation-menu';
34
+ export * from './components/onboarding-checklist';
31
35
  export * from './components/page-header';
32
36
  export * from './components/pagination';
33
37
  export * from './components/popover';
34
38
  export * from './components/progress';
35
39
  export * from './components/radio-group';
36
40
  export * from './components/reasoning';
41
+ export * from './components/resizable';
37
42
  export * from './components/scroll-area';
38
43
  export * from './components/secret-input';
39
44
  export * from './components/select';
@@ -54,3 +59,5 @@ export * from './components/toaster';
54
59
  export * from './components/tool-call';
55
60
  export * from './components/toolbar';
56
61
  export * from './components/tooltip';
62
+ export * from './components/tour';
63
+ export * from './components/tree-view';
package/dist/index.js CHANGED
@@ -15,28 +15,33 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./component
15
15
  import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from "./components/dialog.js";
16
16
  import { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from "./components/command.js";
17
17
  import { Input, fieldClasses } from "./components/input.js";
18
- import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger, surfaceClasses } from "./components/popover.js";
18
+ import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger, menuItemClasses, surfaceClasses } from "./components/popover.js";
19
19
  import { Spinner } from "./components/spinner.js";
20
20
  import { Combobox } from "./components/combobox.js";
21
21
  import { Composer, Suggestions } from "./components/composer.js";
22
+ import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from "./components/context-menu.js";
22
23
  import { Conversation } from "./components/conversation.js";
23
24
  import { IconButton } from "./components/icon-button.js";
24
25
  import { Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, toastVariants } from "./components/toast.js";
25
26
  import { Toaster, toast } from "./components/toaster.js";
26
27
  import { CopyButton } from "./components/copy-button.js";
27
28
  import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./components/dropdown-menu.js";
29
+ import { EditorTabs } from "./components/editor-tabs.js";
28
30
  import { EmptyState, EmptyStateActions, EmptyStateDescription, EmptyStateIcon, EmptyStateTitle } from "./components/empty-state.js";
29
31
  import { Field, FieldDescription, FieldError, FieldHeader, FieldHint, FieldLabel } from "./components/field.js";
30
32
  import { Kbd, KbdGroup } from "./components/kbd.js";
31
33
  import { Label } from "./components/label.js";
32
34
  import { CodeBlock, Markdown, parseMarkdown } from "./components/markdown.js";
35
+ import { Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger } from "./components/menubar.js";
33
36
  import { Message } from "./components/message.js";
34
37
  import { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, navigationMenuTriggerStyle } from "./components/navigation-menu.js";
38
+ import { Progress } from "./components/progress.js";
39
+ import { OnboardingChecklist } from "./components/onboarding-checklist.js";
35
40
  import { PageHeader, PageHeaderActions, PageHeaderDescription, PageHeaderEyebrow, PageHeaderMain, PageHeaderTitle } from "./components/page-header.js";
36
41
  import { Pagination, paginationRange } from "./components/pagination.js";
37
- import { Progress } from "./components/progress.js";
38
42
  import { RadioGroup, RadioGroupItem } from "./components/radio-group.js";
39
43
  import { Reasoning, StreamingIndicator } from "./components/reasoning.js";
44
+ import { ResizablePanel, ResizablePanelGroup, ResizeHandle, useGroupRef, usePanelRef } from "./components/resizable.js";
40
45
  import { ScrollArea, ScrollBar } from "./components/scroll-area.js";
41
46
  import { SecretInput } from "./components/secret-input.js";
42
47
  import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue } from "./components/select.js";
@@ -54,4 +59,6 @@ import { Textarea } from "./components/textarea.js";
54
59
  import { ToolApproval, ToolCall } from "./components/tool-call.js";
55
60
  import { Toolbar, ToolbarGroup, ToolbarSeparator, ToolbarSpacer } from "./components/toolbar.js";
56
61
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./components/tooltip.js";
57
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, AppShellBody, AppShellContent, AppShellHeader, AppShellMain, AppShellSidebar, Avatar, AvatarFallback, AvatarImage, Badge, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Card, CardAction, CardContent, CardDescription, CardEyebrow, CardFooter, CardHeader, CardTitle, Checkbox, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, Composer, Conversation, CopyButton, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, EmptyStateActions, EmptyStateDescription, EmptyStateIcon, EmptyStateTitle, Field, FieldDescription, FieldError, FieldHeader, FieldHint, FieldLabel, IconButton, Input, Kbd, KbdGroup, Label, Markdown, Message, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, PageHeader, PageHeaderActions, PageHeaderDescription, PageHeaderEyebrow, PageHeaderMain, PageHeaderTitle, Pagination, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, Reasoning, ScrollArea, ScrollBar, SecretInput, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Skeleton, Slider, Spinner, StatCard, Status, StatusDot, Steps, StreamingIndicator, Suggestions, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, ToolApproval, ToolCall, Toolbar, ToolbarGroup, ToolbarSeparator, ToolbarSpacer, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, alertVariants, badgeVariants, brand, buttonVariants, cardVariants, cn, dark, fieldClasses, fontMono, fontSans, isApplePlatform, light, modKeyLabel, motion, navigationMenuTriggerStyle, paginationRange, parseMarkdown, radius, shadow, shortcutLabel, statusDotVariants, surfaceClasses, toast, toastVariants, tokens };
62
+ import { Spotlight, Tour, TourAnchor, findTourTarget, tourTarget, waitForTourTarget } from "./components/tour.js";
63
+ import { TreeView, flattenTree } from "./components/tree-view.js";
64
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, AppShellBody, AppShellContent, AppShellHeader, AppShellMain, AppShellSidebar, Avatar, AvatarFallback, AvatarImage, Badge, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Card, CardAction, CardContent, CardDescription, CardEyebrow, CardFooter, CardHeader, CardTitle, Checkbox, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, Composer, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, Conversation, CopyButton, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EditorTabs, EmptyState, EmptyStateActions, EmptyStateDescription, EmptyStateIcon, EmptyStateTitle, Field, FieldDescription, FieldError, FieldHeader, FieldHint, FieldLabel, IconButton, Input, Kbd, KbdGroup, Label, Markdown, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, Message, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, OnboardingChecklist, PageHeader, PageHeaderActions, PageHeaderDescription, PageHeaderEyebrow, PageHeaderMain, PageHeaderTitle, Pagination, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, Reasoning, ResizablePanel, ResizablePanelGroup, ResizeHandle, ScrollArea, ScrollBar, SecretInput, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Skeleton, Slider, Spinner, Spotlight, StatCard, Status, StatusDot, Steps, StreamingIndicator, Suggestions, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, ToolApproval, ToolCall, Toolbar, ToolbarGroup, ToolbarSeparator, ToolbarSpacer, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Tour, TourAnchor, TreeView, alertVariants, badgeVariants, brand, buttonVariants, cardVariants, cn, dark, fieldClasses, findTourTarget, flattenTree, fontMono, fontSans, isApplePlatform, light, menuItemClasses, modKeyLabel, motion, navigationMenuTriggerStyle, paginationRange, parseMarkdown, radius, shadow, shortcutLabel, statusDotVariants, surfaceClasses, toast, toastVariants, tokens, tourTarget, useGroupRef, usePanelRef, waitForTourTarget };