ink-tree-view 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 John Costa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,383 @@
1
+ # ink-tree-view
2
+
3
+ [![CI](https://github.com/costajohnt/ink-tree-view/actions/workflows/ci.yml/badge.svg)](https://github.com/costajohnt/ink-tree-view/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/ink-tree-view)](https://www.npmjs.com/package/ink-tree-view)
5
+ [![license](https://img.shields.io/npm/l/ink-tree-view)](https://github.com/costajohnt/ink-tree-view/blob/main/LICENSE)
6
+
7
+ A tree view component for [Ink](https://github.com/vadimdemedes/ink) (React for CLIs). Display hierarchical data with expand/collapse, keyboard navigation, selection modes, custom rendering, virtual scrolling, and async lazy loading.
8
+
9
+ ## Features
10
+
11
+ - Hierarchical data display with expand/collapse
12
+ - Full keyboard navigation (arrows, Home/End, Enter, Space)
13
+ - Selection modes: none, single, and multiple (with checkboxes)
14
+ - Custom node rendering via `renderNode` prop
15
+ - Virtual scrolling for large trees (`visibleNodeCount`)
16
+ - Async/lazy-loaded children via `loadChildren` + `isParent`
17
+ - Error handling for failed async loads via `onLoadError`
18
+ - Headless hooks (`useTreeViewState`, `useTreeView`) for full control
19
+ - TypeScript-first with complete type exports
20
+
21
+ ## Install
22
+
23
+ ```
24
+ npm install ink-tree-view
25
+ ```
26
+
27
+ Peer dependencies: `ink` (>=5.0.0), `react` (>=18.0.0)
28
+
29
+ ## Quick Start
30
+
31
+ ```tsx
32
+ import {render} from 'ink';
33
+ import {TreeView} from 'ink-tree-view';
34
+
35
+ const data = [
36
+ {
37
+ id: 'src',
38
+ label: 'src',
39
+ children: [
40
+ {id: 'index', label: 'index.ts'},
41
+ {
42
+ id: 'components',
43
+ label: 'components',
44
+ children: [
45
+ {id: 'button', label: 'button.tsx'},
46
+ {id: 'input', label: 'input.tsx'},
47
+ ],
48
+ },
49
+ ],
50
+ },
51
+ {id: 'readme', label: 'README.md'},
52
+ {id: 'package', label: 'package.json'},
53
+ ];
54
+
55
+ render(<TreeView data={data} />);
56
+ ```
57
+
58
+ Use arrow keys to navigate, Right to expand, Left to collapse, and Enter to toggle.
59
+
60
+ ## Data Model
61
+
62
+ Tree data is an array of `TreeNode<T>` objects. Each node must have a unique `id` across the entire tree.
63
+
64
+ ```ts
65
+ type TreeNode<T = Record<string, unknown>> = {
66
+ /** Unique identifier. Must be unique across the entire tree. */
67
+ id: string;
68
+ /** Display label used by the default renderer. */
69
+ label: string;
70
+ /** Arbitrary user data attached to this node. */
71
+ data?: T;
72
+ /** Child nodes. Undefined or empty array means leaf node. */
73
+ children?: Array<TreeNode<T>>;
74
+ /** Mark as a parent whose children will be loaded via loadChildren. */
75
+ isParent?: boolean;
76
+ };
77
+ ```
78
+
79
+ ### Example with custom data
80
+
81
+ ```tsx
82
+ type FileInfo = {size: number; modified: string};
83
+
84
+ const data: TreeNode<FileInfo>[] = [
85
+ {
86
+ id: 'doc',
87
+ label: 'document.pdf',
88
+ data: {size: 1024, modified: '2025-01-15'},
89
+ },
90
+ ];
91
+ ```
92
+
93
+ ## Props
94
+
95
+ | Prop | Type | Default | Description |
96
+ |------|------|---------|-------------|
97
+ | `data` | `TreeNode<T>[]` | *required* | Array of root-level tree nodes. |
98
+ | `selectionMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | Selection behavior. `'single'` allows one selected node; `'multiple'` shows checkboxes. |
99
+ | `defaultExpanded` | `ReadonlySet<string> \| 'all'` | `undefined` | Node IDs expanded on mount, or `'all'` to expand everything. |
100
+ | `defaultSelected` | `ReadonlySet<string>` | `undefined` | Node IDs selected on mount (ignored in `'none'` mode). |
101
+ | `visibleNodeCount` | `number` | `Infinity` | Max visible rows. Enables virtual scrolling when finite. |
102
+ | `renderNode` | `(props: TreeNodeRendererProps<T>) => ReactNode` | `undefined` | Custom renderer for each node. Receives `{node, state}`. |
103
+ | `loadChildren` | `(node: TreeNode<T>) => Promise<TreeNode<T>[]>` | `undefined` | Async loader called when expanding an `isParent: true` node. |
104
+ | `onLoadError` | `(nodeId: string, error: Error) => void` | `undefined` | Called when `loadChildren` rejects. Loading state is cleared so the user can retry. |
105
+ | `onFocusChange` | `(nodeId: string) => void` | `undefined` | Called when the focused node changes (not on initial mount). |
106
+ | `onExpandChange` | `(expandedIds: ReadonlySet<string>) => void` | `undefined` | Called when the set of expanded nodes changes. |
107
+ | `onSelectChange` | `(selectedIds: ReadonlySet<string>) => void` | `undefined` | Called when the selection changes. |
108
+ | `isDisabled` | `boolean` | `false` | When true, all keyboard input is ignored. |
109
+
110
+ ## Keyboard Shortcuts
111
+
112
+ | Key | Action |
113
+ |-----|--------|
114
+ | Up Arrow | Move focus to the previous visible node |
115
+ | Down Arrow | Move focus to the next visible node |
116
+ | Right Arrow | Expand focused node, or move to first child if already expanded. Triggers async load for `isParent` nodes. |
117
+ | Left Arrow | Collapse focused node, or move to parent if already collapsed |
118
+ | Enter | Toggle expand/collapse (`'none'` mode) or select (`'single'`/`'multiple'` mode) |
119
+ | Space | Toggle expand/collapse (`'none'`/`'single'` mode) or toggle selection (`'multiple'` mode) |
120
+ | Home | Jump to the first node |
121
+ | End | Jump to the last node |
122
+
123
+ ## Custom Rendering
124
+
125
+ Use the `renderNode` prop to completely control how each node looks.
126
+
127
+ ```tsx
128
+ import {Box, Text} from 'ink';
129
+ import {TreeView, type TreeNodeRendererProps} from 'ink-tree-view';
130
+
131
+ type FileData = {size: number};
132
+
133
+ function CustomNode({node, state}: TreeNodeRendererProps<FileData>) {
134
+ const prefix = state.hasChildren
135
+ ? state.isExpanded ? 'v ' : '> '
136
+ : ' ';
137
+
138
+ return (
139
+ <Box>
140
+ <Text dimColor={!state.isFocused}>
141
+ {' '.repeat(state.depth)}
142
+ {prefix}
143
+ {node.label}
144
+ </Text>
145
+ {node.data && (
146
+ <Text color="gray"> ({node.data.size} bytes)</Text>
147
+ )}
148
+ {state.isSelected && <Text color="green"> [selected]</Text>}
149
+ </Box>
150
+ );
151
+ }
152
+
153
+ render(
154
+ <TreeView<FileData>
155
+ data={data}
156
+ selectionMode="single"
157
+ renderNode={CustomNode}
158
+ />
159
+ );
160
+ ```
161
+
162
+ `TreeNodeRendererProps<T>` includes:
163
+
164
+ - `node` -- the `TreeNode<T>` data
165
+ - `state` -- a `TreeNodeState` with: `isFocused`, `isExpanded`, `isSelected`, `depth`, `hasChildren`, `isLoading`
166
+
167
+ ## Selection Modes
168
+
169
+ ### No selection (default)
170
+
171
+ ```tsx
172
+ <TreeView data={data} />
173
+ ```
174
+
175
+ Enter and Space toggle expand/collapse.
176
+
177
+ ### Single selection
178
+
179
+ ```tsx
180
+ <TreeView
181
+ data={data}
182
+ selectionMode="single"
183
+ onSelectChange={(selectedIds) => {
184
+ const selected = [...selectedIds][0];
185
+ console.log('Selected:', selected);
186
+ }}
187
+ />
188
+ ```
189
+
190
+ Enter selects the focused node. Only one node can be selected at a time.
191
+
192
+ ### Multiple selection
193
+
194
+ ```tsx
195
+ <TreeView
196
+ data={data}
197
+ selectionMode="multiple"
198
+ defaultSelected={new Set(['node-1', 'node-3'])}
199
+ onSelectChange={(selectedIds) => {
200
+ console.log('Selected:', [...selectedIds]);
201
+ }}
202
+ />
203
+ ```
204
+
205
+ Enter and Space toggle selection on the focused node. Checkboxes appear next to each node.
206
+
207
+ ## Virtual Scrolling
208
+
209
+ For large trees, set `visibleNodeCount` to limit the number of visible rows. The viewport scrolls to keep the focused node in view, and scroll indicators appear when content extends beyond the viewport.
210
+
211
+ ```tsx
212
+ <TreeView
213
+ data={largeTree}
214
+ defaultExpanded="all"
215
+ visibleNodeCount={15}
216
+ />
217
+ ```
218
+
219
+ ## Async Children
220
+
221
+ Use `loadChildren` to lazily load children when a node is first expanded. Mark on-demand nodes with `isParent: true`. A loading indicator is shown while the request is in progress.
222
+
223
+ ```tsx
224
+ async function fetchChildren(node) {
225
+ const response = await fetch(`/api/tree/${node.id}/children`);
226
+ return response.json();
227
+ }
228
+
229
+ const data = [
230
+ {id: 'root', label: 'Root', isParent: true},
231
+ {id: 'leaf', label: 'Leaf'},
232
+ ];
233
+
234
+ render(
235
+ <TreeView
236
+ data={data}
237
+ loadChildren={fetchChildren}
238
+ onLoadError={(nodeId, error) => {
239
+ console.error(`Failed to load children for ${nodeId}:`, error.message);
240
+ }}
241
+ />
242
+ );
243
+ ```
244
+
245
+ When `loadChildren` rejects, `onLoadError` fires and the loading state is cleared so the user can retry by pressing Right Arrow again.
246
+
247
+ ## Hooks API
248
+
249
+ For headless/custom usage, two hooks are exported directly.
250
+
251
+ ### `useTreeViewState<T>(props)`
252
+
253
+ Manages all tree state: focus, expansion, selection, viewport scrolling, and loading.
254
+
255
+ ```tsx
256
+ import {useTreeViewState} from 'ink-tree-view';
257
+
258
+ const state = useTreeViewState({
259
+ data,
260
+ selectionMode: 'multiple',
261
+ defaultExpanded: new Set(['root']),
262
+ visibleNodeCount: 10,
263
+ onFocusChange: (id) => { /* ... */ },
264
+ onExpandChange: (ids) => { /* ... */ },
265
+ onSelectChange: (ids) => { /* ... */ },
266
+ });
267
+ ```
268
+
269
+ **Returned state:**
270
+
271
+ | Property | Type | Description |
272
+ |----------|------|-------------|
273
+ | `focusedId` | `string \| undefined` | Currently focused node ID |
274
+ | `expandedIds` | `ReadonlySet<string>` | Set of expanded node IDs |
275
+ | `selectedIds` | `ReadonlySet<string>` | Set of selected node IDs |
276
+ | `viewportNodes` | `Array<{node, state}>` | Nodes in current viewport |
277
+ | `visibleCount` | `number` | Total visible node count |
278
+ | `hasScrollUp` | `boolean` | Nodes exist above the viewport |
279
+ | `hasScrollDown` | `boolean` | Nodes exist below the viewport |
280
+ | `loadingIds` | `ReadonlySet<string>` | Currently loading node IDs |
281
+ | `nodeMap` | `TreeNodeMap<T>` | Underlying data structure |
282
+
283
+ **Actions:**
284
+
285
+ | Method | Description |
286
+ |--------|-------------|
287
+ | `focusNext()` | Move focus down |
288
+ | `focusPrevious()` | Move focus up |
289
+ | `focusFirst()` | Jump to first node |
290
+ | `focusLast()` | Jump to last node |
291
+ | `expand()` | Expand focused node |
292
+ | `expandNode(id)` | Expand a specific node |
293
+ | `collapse()` | Collapse focused node |
294
+ | `collapseNode(id)` | Collapse a specific node |
295
+ | `toggleExpanded()` | Toggle expand/collapse on focused node |
296
+ | `expandAll()` | Expand all nodes |
297
+ | `collapseAll()` | Collapse all nodes |
298
+ | `select()` | Select/deselect focused node |
299
+ | `focusParent()` | Move focus to parent |
300
+ | `focusFirstChild()` | Move focus to first child |
301
+ | `setLoading(id, bool)` | Mark a node as loading |
302
+ | `setChildrenError(id)` | Clear loading state after failure |
303
+ | `insertChildren(parentId, children)` | Insert children under a parent |
304
+
305
+ ### `useTreeView<T>(props)`
306
+
307
+ Wires keyboard input to a `TreeViewState` instance. Call this after `useTreeViewState` to enable keyboard navigation.
308
+
309
+ ```tsx
310
+ import {Box, Text} from 'ink';
311
+ import {useTreeViewState, useTreeView} from 'ink-tree-view';
312
+
313
+ function MyTree({data}) {
314
+ const state = useTreeViewState({data});
315
+
316
+ useTreeView({
317
+ state,
318
+ selectionMode: 'none',
319
+ loadChildren: async (node) => {
320
+ // fetch children...
321
+ },
322
+ });
323
+
324
+ return (
325
+ <Box flexDirection="column">
326
+ {state.viewportNodes.map(({node, state: ns}) => (
327
+ <Text key={node.id} bold={ns.isFocused}>
328
+ {' '.repeat(ns.depth)}{node.label}
329
+ </Text>
330
+ ))}
331
+ </Box>
332
+ );
333
+ }
334
+ ```
335
+
336
+ ## TypeScript
337
+
338
+ All types are exported from the package entry point:
339
+
340
+ ```ts
341
+ import type {
342
+ TreeNode,
343
+ TreeNodeState,
344
+ SelectionMode,
345
+ AsyncChildrenFn,
346
+ TreeViewProps,
347
+ TreeNodeRendererProps,
348
+ TreeViewState,
349
+ UseTreeViewStateProps,
350
+ UseTreeViewProps,
351
+ TreeViewTheme,
352
+ FlatNode,
353
+ } from 'ink-tree-view';
354
+
355
+ import {
356
+ TreeView,
357
+ useTreeViewState,
358
+ useTreeView,
359
+ treeViewTheme,
360
+ TreeNodeMap,
361
+ } from 'ink-tree-view';
362
+ ```
363
+
364
+ ## Contributing
365
+
366
+ Contributions are welcome. Please open an issue to discuss your idea before submitting a PR.
367
+
368
+ ```bash
369
+ git clone https://github.com/costajohnt/ink-tree-view.git
370
+ cd ink-tree-view
371
+ npm install
372
+ npm test
373
+ ```
374
+
375
+ Run `npm run build` to compile and `npm run typecheck` to verify types.
376
+
377
+ ## Changelog
378
+
379
+ See [GitHub Releases](https://github.com/costajohnt/ink-tree-view/releases) for a list of changes.
380
+
381
+ ## License
382
+
383
+ [MIT](LICENSE) -- Copyright (c) 2024-2026 John Costa
@@ -0,0 +1,258 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { BoxProps, TextProps } from 'ink';
4
+
5
+ /**
6
+ * A single node in the tree. Generic over the user's data shape.
7
+ * The `id` field MUST be unique across the entire tree.
8
+ */
9
+ type TreeNode<T = Record<string, unknown>> = {
10
+ /** Unique identifier for this node. Used as the key for all lookups. */
11
+ id: string;
12
+ /** Display label. Used by the default renderer. */
13
+ label: string;
14
+ /** Arbitrary user data attached to this node. */
15
+ data?: T;
16
+ /** Child nodes. Undefined or empty array means leaf node. */
17
+ children?: Array<TreeNode<T>>;
18
+ /**
19
+ * Explicitly marks this node as a parent that can have children loaded
20
+ * asynchronously via `loadChildren`. When true, the node shows an expand
21
+ * indicator even if `children` is empty or undefined, and expanding it
22
+ * triggers the `loadChildren` callback.
23
+ */
24
+ isParent?: boolean;
25
+ };
26
+ /**
27
+ * For async/lazy loading: a function that resolves children on demand.
28
+ */
29
+ type AsyncChildrenFn<T = Record<string, unknown>> = (node: TreeNode<T>) => Promise<Array<TreeNode<T>>>;
30
+ /**
31
+ * State passed to custom renderers and theme functions.
32
+ */
33
+ type TreeNodeState = {
34
+ /** Whether this node is currently focused (cursor is on it). */
35
+ isFocused: boolean;
36
+ /** Whether this node is expanded (children visible). */
37
+ isExpanded: boolean;
38
+ /** Whether this node is selected (in select/multi-select mode). */
39
+ isSelected: boolean;
40
+ /** Depth of this node in the tree (root = 0). */
41
+ depth: number;
42
+ /** Whether this node has children (or a lazy loader). */
43
+ hasChildren: boolean;
44
+ /** Whether children are currently loading (async mode). */
45
+ isLoading: boolean;
46
+ };
47
+ /**
48
+ * Props received by a custom node renderer.
49
+ */
50
+ type TreeNodeRendererProps<T = Record<string, unknown>> = {
51
+ /** The tree node data. */
52
+ node: TreeNode<T>;
53
+ /** Current state of this node. */
54
+ state: TreeNodeState;
55
+ };
56
+ /**
57
+ * Selection mode for the tree view.
58
+ */
59
+ type SelectionMode = 'none' | 'single' | 'multiple';
60
+ /**
61
+ * Props for the TreeView component.
62
+ */
63
+ type TreeViewProps<T = Record<string, unknown>> = {
64
+ /** The tree data. Array of root-level nodes. */
65
+ readonly data: Array<TreeNode<T>>;
66
+ /**
67
+ * Selection mode.
68
+ * - 'none': No selection behavior (default).
69
+ * - 'single': One node can be selected at a time.
70
+ * - 'multiple': Multiple nodes can be selected (checkboxes shown).
71
+ * @default 'none'
72
+ */
73
+ readonly selectionMode?: SelectionMode;
74
+ /**
75
+ * Set of node IDs that are expanded by default.
76
+ * If not provided, all nodes start collapsed.
77
+ */
78
+ readonly defaultExpanded?: ReadonlySet<string> | 'all';
79
+ /**
80
+ * Set of node IDs that are selected by default.
81
+ */
82
+ readonly defaultSelected?: ReadonlySet<string>;
83
+ /**
84
+ * Number of visible nodes in the viewport (for virtualization).
85
+ * @default Infinity (no virtualization)
86
+ */
87
+ readonly visibleNodeCount?: number;
88
+ /**
89
+ * Custom node renderer. Receives node + state, returns Ink JSX.
90
+ */
91
+ readonly renderNode?: (props: TreeNodeRendererProps<T>) => ReactNode;
92
+ /**
93
+ * Async function to load children on demand.
94
+ */
95
+ readonly loadChildren?: AsyncChildrenFn<T>;
96
+ /**
97
+ * Called when `loadChildren` rejects. Receives the node ID and error.
98
+ * The node's loading state is automatically cleared so the user can retry.
99
+ */
100
+ readonly onLoadError?: (nodeId: string, error: Error) => void;
101
+ /** Called when the focused node changes. */
102
+ readonly onFocusChange?: (nodeId: string) => void;
103
+ /** Called when expanded set changes. */
104
+ readonly onExpandChange?: (expandedIds: ReadonlySet<string>) => void;
105
+ /** Called when selection changes. */
106
+ readonly onSelectChange?: (selectedIds: ReadonlySet<string>) => void;
107
+ /**
108
+ * When disabled, user input is ignored.
109
+ * @default false
110
+ */
111
+ readonly isDisabled?: boolean;
112
+ };
113
+
114
+ declare function TreeView<T = Record<string, unknown>>({ data, selectionMode, defaultExpanded, defaultSelected, visibleNodeCount, renderNode, loadChildren, onLoadError, onFocusChange, onExpandChange, onSelectChange, isDisabled, }: TreeViewProps<T>): react_jsx_runtime.JSX.Element;
115
+
116
+ /**
117
+ * A flattened representation of a tree node with navigation links.
118
+ */
119
+ type FlatNode<T = Record<string, unknown>> = {
120
+ /** The original tree node. */
121
+ node: TreeNode<T>;
122
+ /** Depth in the tree (0 for roots). */
123
+ depth: number;
124
+ /** Index in the flattened DFS order (across ALL nodes, not just visible). */
125
+ flatIndex: number;
126
+ /** Parent's id, or undefined if root. */
127
+ parentId: string | undefined;
128
+ /** Whether this node has children. */
129
+ hasChildren: boolean;
130
+ /** Ordered list of direct children IDs. */
131
+ childrenIds: string[];
132
+ /** Previous sibling's ID, or undefined. */
133
+ previousSiblingId: string | undefined;
134
+ /** Next sibling's ID, or undefined. */
135
+ nextSiblingId: string | undefined;
136
+ };
137
+ /**
138
+ * A flattened map of all tree nodes built via DFS traversal.
139
+ * Stores parent/child/sibling links for O(1) navigation.
140
+ */
141
+ declare class TreeNodeMap<T = Record<string, unknown>> {
142
+ /** Map from node ID to FlatNode. */
143
+ private readonly map;
144
+ /** All node IDs in DFS order. */
145
+ readonly orderedIds: string[];
146
+ /** Root-level node IDs. */
147
+ readonly rootIds: string[];
148
+ constructor(data: Array<TreeNode<T>>);
149
+ private buildFromData;
150
+ /**
151
+ * Get a flat node by ID.
152
+ */
153
+ get(id: string): FlatNode<T> | undefined;
154
+ /**
155
+ * Total number of nodes in the tree.
156
+ */
157
+ get size(): number;
158
+ /**
159
+ * Iterate over all entries.
160
+ */
161
+ entries(): IterableIterator<[string, FlatNode<T>]>;
162
+ /**
163
+ * Check if a node is a descendant of another node.
164
+ */
165
+ isDescendantOf(nodeId: string, ancestorId: string): boolean;
166
+ /**
167
+ * Given a set of expanded node IDs, return the ordered list of
168
+ * VISIBLE node IDs (i.e., a node is visible if all its ancestors
169
+ * are expanded).
170
+ *
171
+ * Uses iterative DFS, skipping collapsed subtrees.
172
+ */
173
+ getVisibleIds(expandedIds: ReadonlySet<string>): string[];
174
+ /**
175
+ * Insert dynamically-loaded children under a parent node.
176
+ * Returns a new TreeNodeMap (immutable operation).
177
+ */
178
+ withChildren(parentId: string, children: Array<TreeNode<T>>): TreeNodeMap<T>;
179
+ }
180
+
181
+ type UseTreeViewStateProps<T = Record<string, unknown>> = {
182
+ data: Array<TreeNode<T>>;
183
+ selectionMode?: SelectionMode;
184
+ defaultExpanded?: ReadonlySet<string> | 'all';
185
+ defaultSelected?: ReadonlySet<string>;
186
+ visibleNodeCount?: number;
187
+ onFocusChange?: (nodeId: string) => void;
188
+ onExpandChange?: (expandedIds: ReadonlySet<string>) => void;
189
+ onSelectChange?: (selectedIds: ReadonlySet<string>) => void;
190
+ };
191
+ type TreeViewState<T = Record<string, unknown>> = {
192
+ focusedId: string | undefined;
193
+ expandedIds: ReadonlySet<string>;
194
+ selectedIds: ReadonlySet<string>;
195
+ viewportNodes: Array<{
196
+ node: TreeNode<T>;
197
+ state: TreeNodeState;
198
+ }>;
199
+ visibleCount: number;
200
+ hasScrollUp: boolean;
201
+ hasScrollDown: boolean;
202
+ viewportFromIndex: number;
203
+ viewportToIndex: number;
204
+ loadingIds: ReadonlySet<string>;
205
+ nodeMap: TreeNodeMap<T>;
206
+ focusNext: () => void;
207
+ focusPrevious: () => void;
208
+ focusFirst: () => void;
209
+ focusLast: () => void;
210
+ expand: () => void;
211
+ expandNode: (nodeId: string) => void;
212
+ collapse: () => void;
213
+ collapseNode: (nodeId: string) => void;
214
+ toggleExpanded: () => void;
215
+ expandAll: () => void;
216
+ collapseAll: () => void;
217
+ select: () => void;
218
+ focusParent: () => void;
219
+ focusFirstChild: () => void;
220
+ setLoading: (nodeId: string, isLoading: boolean) => void;
221
+ setChildrenError: (nodeId: string) => void;
222
+ insertChildren: (parentId: string, children: Array<TreeNode<T>>) => void;
223
+ };
224
+ declare function useTreeViewState<T = Record<string, unknown>>({ data, selectionMode, defaultExpanded, defaultSelected, visibleNodeCount, onFocusChange, onExpandChange, onSelectChange, }: UseTreeViewStateProps<T>): TreeViewState<T>;
225
+
226
+ type UseTreeViewProps<T = Record<string, unknown>> = {
227
+ isDisabled?: boolean;
228
+ selectionMode: SelectionMode;
229
+ state: TreeViewState<T>;
230
+ loadChildren?: AsyncChildrenFn<T>;
231
+ onLoadError?: (nodeId: string, error: Error) => void;
232
+ };
233
+ declare function useTreeView<T>({ isDisabled, selectionMode, state, loadChildren, onLoadError, }: UseTreeViewProps<T>): void;
234
+
235
+ type StyleFnProps = {
236
+ isFocused?: boolean;
237
+ isExpanded?: boolean;
238
+ isSelected?: boolean;
239
+ depth?: number;
240
+ hasChildren?: boolean;
241
+ isLoading?: boolean;
242
+ };
243
+ declare const theme: {
244
+ styles: {
245
+ container: () => BoxProps;
246
+ node: ({ isFocused }: StyleFnProps) => BoxProps;
247
+ indent: ({ depth }: StyleFnProps) => BoxProps;
248
+ focusIndicator: () => TextProps;
249
+ expandIndicator: (_props: StyleFnProps) => TextProps;
250
+ label: ({ isFocused, isSelected }: StyleFnProps) => TextProps;
251
+ selectedIndicator: () => TextProps;
252
+ loadingIndicator: () => TextProps;
253
+ };
254
+ };
255
+
256
+ type Theme = typeof theme;
257
+
258
+ export { type AsyncChildrenFn, type FlatNode, type SelectionMode, type TreeNode, TreeNodeMap, type TreeNodeRendererProps, type TreeNodeState, TreeView, type TreeViewProps, type TreeViewState, type Theme as TreeViewTheme, type UseTreeViewProps, type UseTreeViewStateProps, theme as treeViewTheme, useTreeView, useTreeViewState };