@djangocfg/ui-tools 2.1.415 → 2.1.416
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/file-icon/index.d.cts +1 -1
- package/dist/file-icon/index.d.ts +1 -1
- package/dist/slots-ClRpIzoh.d.cts +88 -0
- package/dist/slots-ClRpIzoh.d.ts +88 -0
- package/dist/tree/index.cjs +1994 -276
- package/dist/tree/index.cjs.map +1 -1
- package/dist/tree/index.d.cts +717 -72
- package/dist/tree/index.d.ts +717 -72
- package/dist/tree/index.mjs +1984 -279
- package/dist/tree/index.mjs.map +1 -1
- package/package.json +10 -6
- package/src/tools/chat/README.md +111 -1
- package/src/tools/chat/composer/Composer.tsx +138 -17
- package/src/tools/chat/composer/ComposerRichTextarea.tsx +25 -0
- package/src/tools/chat/composer/index.ts +22 -0
- package/src/tools/chat/composer/slash/README.md +187 -0
- package/src/tools/chat/composer/slash/SlashHighlightTextarea.tsx +144 -0
- package/src/tools/chat/composer/slash/SlashMenu.tsx +142 -0
- package/src/tools/chat/composer/slash/SlashToken.tsx +57 -0
- package/src/tools/chat/composer/slash/index.ts +44 -0
- package/src/tools/chat/composer/slash/labels.ts +19 -0
- package/src/tools/chat/composer/slash/state.ts +168 -0
- package/src/tools/chat/composer/slash/types.ts +64 -0
- package/src/tools/chat/composer/slash/useSlashCommands.ts +204 -0
- package/src/tools/chat/composer/types.ts +8 -0
- package/src/tools/chat/shell/SuggestedPrompts.tsx +194 -0
- package/src/tools/chat/shell/index.ts +6 -0
- package/src/tools/data/Listbox/lazy.tsx +1 -1
- package/src/tools/data/Masonry/lazy.tsx +1 -1
- package/src/tools/data/Timeline/lazy.tsx +1 -1
- package/src/tools/data/Tree/FinderTree.tsx +42 -0
- package/src/tools/data/Tree/README.md +337 -208
- package/src/tools/data/Tree/TreeDndProvider.tsx +137 -0
- package/src/tools/data/Tree/TreeRoot.tsx +170 -55
- package/src/tools/data/Tree/__tests__/dnd.test.ts +160 -0
- package/src/tools/data/Tree/__tests__/keyboard.test.ts +137 -0
- package/src/tools/data/Tree/__tests__/renameUtils.test.ts +52 -0
- package/src/tools/data/Tree/__tests__/selection.test.ts +227 -0
- package/src/tools/data/Tree/components/TreeDropIndicator.tsx +65 -0
- package/src/tools/data/Tree/components/TreeEmptyArea.tsx +160 -0
- package/src/tools/data/Tree/components/TreeRenameInput.tsx +114 -0
- package/src/tools/data/Tree/components/TreeRow.tsx +92 -8
- package/src/tools/data/Tree/components/index.ts +6 -0
- package/src/tools/data/Tree/context/TreeContext.tsx +204 -363
- package/src/tools/data/Tree/context/TreeContextValue.ts +139 -0
- package/src/tools/data/Tree/context/async-children/collect-ids.ts +27 -0
- package/src/tools/data/Tree/context/async-children/index.ts +8 -0
- package/src/tools/data/Tree/context/async-children/use-async-children.ts +157 -0
- package/src/tools/data/Tree/context/clipboard/index.ts +4 -0
- package/src/tools/data/Tree/context/clipboard/use-clipboard.ts +115 -0
- package/src/tools/data/Tree/context/dnd/index.ts +8 -0
- package/src/tools/data/Tree/context/dnd/use-dnd.ts +194 -0
- package/src/tools/data/Tree/context/expansion/index.ts +4 -0
- package/src/tools/data/Tree/context/expansion/use-expansion.ts +55 -0
- package/src/tools/data/Tree/context/hooks.ts +68 -1
- package/src/tools/data/Tree/context/index.ts +3 -0
- package/src/tools/data/Tree/context/menu/builtin-actions.ts +357 -0
- package/src/tools/data/Tree/context/menu/index.ts +10 -0
- package/src/tools/data/Tree/context/menu/use-resolved-menu.ts +127 -0
- package/src/tools/data/Tree/context/persist/index.ts +4 -0
- package/src/tools/data/Tree/context/persist/use-persist-sync.ts +74 -0
- package/src/tools/data/Tree/context/rename/index.ts +4 -0
- package/src/tools/data/Tree/context/rename/use-rename.ts +113 -0
- package/src/tools/data/Tree/context/selection/index.ts +4 -0
- package/src/tools/data/Tree/context/selection/use-selection.ts +146 -0
- package/src/tools/data/Tree/context/state/index.ts +6 -0
- package/src/tools/data/Tree/context/state/initial.ts +41 -0
- package/src/tools/data/Tree/context/state/reducer.ts +76 -0
- package/src/tools/data/Tree/context/state/types.ts +46 -0
- package/src/tools/data/Tree/data/clipboard.ts +33 -0
- package/src/tools/data/Tree/data/dnd.ts +123 -0
- package/src/tools/data/Tree/data/finderShortcuts.ts +67 -0
- package/src/tools/data/Tree/data/index.ts +19 -0
- package/src/tools/data/Tree/data/renameUtils.ts +51 -0
- package/src/tools/data/Tree/data/selection.ts +157 -0
- package/src/tools/data/Tree/hooks/finder-hotkeys/build-ctx.ts +48 -0
- package/src/tools/data/Tree/hooks/finder-hotkeys/index.ts +8 -0
- package/src/tools/data/Tree/hooks/finder-hotkeys/use-tree-finder-hotkeys.ts +166 -0
- package/src/tools/data/Tree/hooks/index.ts +23 -4
- package/src/tools/data/Tree/hooks/keyboard/activation.ts +27 -0
- package/src/tools/data/Tree/hooks/keyboard/arrow-nav.ts +26 -0
- package/src/tools/data/Tree/hooks/keyboard/expand-collapse.ts +54 -0
- package/src/tools/data/Tree/hooks/keyboard/index.ts +10 -0
- package/src/tools/data/Tree/hooks/keyboard/types.ts +39 -0
- package/src/tools/data/Tree/hooks/keyboard/use-tree-keyboard.ts +196 -0
- package/src/tools/data/Tree/hooks/type-ahead/index.ts +5 -0
- package/src/tools/data/Tree/hooks/type-ahead/match-prefix.ts +42 -0
- package/src/tools/data/Tree/hooks/{useTreeTypeAhead.ts → type-ahead/use-tree-type-ahead.ts} +8 -19
- package/src/tools/data/Tree/index.tsx +25 -2
- package/src/tools/data/Tree/types/activation.ts +30 -0
- package/src/tools/data/Tree/types/adapter.ts +70 -0
- package/src/tools/data/Tree/types/index.ts +27 -0
- package/src/tools/data/Tree/types/labels.ts +97 -0
- package/src/tools/data/Tree/types/loader.ts +9 -0
- package/src/tools/data/Tree/types/node.ts +38 -0
- package/src/tools/data/Tree/types/root-props.ts +142 -0
- package/src/tools/data/Tree/types/selection.ts +3 -0
- package/src/tools/data/Tree/types/slots.ts +64 -0
- package/src/tools/forms/MarkdownEditor/MarkdownEditor.tsx +85 -0
- package/src/tools/forms/MarkdownEditor/index.ts +1 -0
- package/src/tools/forms/MarkdownEditor/lazy.tsx +6 -0
- package/src/tools/forms/MarkdownEditor/slash/SlashCommandNode.ts +162 -0
- package/src/tools/forms/MarkdownEditor/slash/index.ts +4 -0
- package/src/tools/forms/MarkdownEditor/slash/syncSlashNode.ts +97 -0
- package/src/tools/forms/MarkdownEditor/slash/types.ts +13 -0
- package/src/tools/forms/MarkdownEditor/styles.css +18 -0
- package/dist/types-j2vhn4Kv.d.cts +0 -241
- package/dist/types-j2vhn4Kv.d.ts +0 -241
- package/src/tools/data/Tree/hooks/useTreeKeyboard.ts +0 -171
- package/src/tools/data/Tree/types.ts +0 -217
package/dist/tree/index.d.cts
CHANGED
|
@@ -1,7 +1,308 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { T as TreeRootProps, a as TreeItemId, b as TreeNode, F as FlatRow, c as TreeActivateOptions, d as TreeLabels, R as ResolvedAppearance, e as TreeSelectionMode, f as TreeActivationMode, g as TreeRowSlot, h as TreeContextMenuSlot } from '../types-j2vhn4Kv.cjs';
|
|
3
|
-
export { D as DEFAULT_TREE_APPEARANCE, n as DEFAULT_TREE_LABELS, l as TreeAccentIntensity, j as TreeAppearance, p as TreeContextMenuAction, s as TreeContextMenuActionsResolver, q as TreeContextMenuItem, k as TreeDensity, t as TreeLoadChildren, m as TreeRadius, o as TreeRowRenderProps, i as appearanceToStyle, r as resolveAppearance } from '../types-j2vhn4Kv.cjs';
|
|
4
2
|
import * as react from 'react';
|
|
3
|
+
import { CSSProperties } from 'react';
|
|
4
|
+
import { T as TreeNode, a as TreeItemId, b as TreeRowSlot, c as TreeContextMenuSlot, d as TreeContextMenuActionsResolver, F as FlatRow, e as TreeRowRenderProps, f as TreeContextMenuItem } from '../slots-ClRpIzoh.cjs';
|
|
5
|
+
export { g as TreeContextMenuAction, h as TreeContextMenuActionsContext } from '../slots-ClRpIzoh.cjs';
|
|
6
|
+
|
|
7
|
+
type TreeSelectionMode = 'none' | 'single' | 'multiple';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* How a node becomes "activated" (i.e. opened) on pointer interaction.
|
|
11
|
+
*
|
|
12
|
+
* - `'single-click'` (default): single click activates a leaf immediately;
|
|
13
|
+
* double-click also activates. Folders always toggle on single click.
|
|
14
|
+
* - `'double-click'`: single click only selects + focuses; double-click is
|
|
15
|
+
* required to activate. Mirrors classic file-manager behaviour.
|
|
16
|
+
* - `'single-click-preview'`: VSCode Explorer / Cursor behaviour. Single
|
|
17
|
+
* click activates with `{ preview: true }` (consumer renders a preview
|
|
18
|
+
* tab); double-click activates with `{ preview: false }` (pinned tab).
|
|
19
|
+
*
|
|
20
|
+
* Folders ignore this setting — they always toggle on single click and
|
|
21
|
+
* never call `onActivate`.
|
|
22
|
+
*/
|
|
23
|
+
type TreeActivationMode = 'single-click' | 'double-click' | 'single-click-preview';
|
|
24
|
+
interface TreeActivateOptions {
|
|
25
|
+
/**
|
|
26
|
+
* `true` when the activation came from a single click in
|
|
27
|
+
* `'single-click-preview'` mode. `false` for double-click and for
|
|
28
|
+
* non-preview modes. Consumers typically map this to a
|
|
29
|
+
* preview-tab vs pinned-tab distinction.
|
|
30
|
+
*/
|
|
31
|
+
preview: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Async loader: called the first time a folder is expanded with no inline
|
|
36
|
+
* `children`. Result is cached; concurrent expansions are de-duplicated.
|
|
37
|
+
*/
|
|
38
|
+
type TreeLoadChildren<T> = (node: TreeNode<T>) => Promise<TreeNode<T>[]>;
|
|
39
|
+
|
|
40
|
+
interface TreeLabels {
|
|
41
|
+
loading: string;
|
|
42
|
+
empty: string;
|
|
43
|
+
error: string;
|
|
44
|
+
searchPlaceholder: string;
|
|
45
|
+
searchMatches: (count: number) => string;
|
|
46
|
+
ariaLabel: string;
|
|
47
|
+
/** Default context-menu item labels. */
|
|
48
|
+
actionOpen: string;
|
|
49
|
+
actionRename: string;
|
|
50
|
+
actionDuplicate: string;
|
|
51
|
+
actionCut: string;
|
|
52
|
+
actionCopy: string;
|
|
53
|
+
actionPaste: string;
|
|
54
|
+
actionDelete: string;
|
|
55
|
+
actionNewFile: string;
|
|
56
|
+
actionNewFolder: string;
|
|
57
|
+
/** Delete confirmation dialog. */
|
|
58
|
+
confirmDeleteTitle: (count: number) => string;
|
|
59
|
+
confirmDeleteMessage: (names: string[]) => string;
|
|
60
|
+
confirmDeleteOk: string;
|
|
61
|
+
confirmDeleteCancel: string;
|
|
62
|
+
/** New file prompt. */
|
|
63
|
+
newFileTitle: string;
|
|
64
|
+
newFileMessage: string;
|
|
65
|
+
newFilePlaceholder: string;
|
|
66
|
+
newFileDefault: string;
|
|
67
|
+
/** New folder prompt. */
|
|
68
|
+
newFolderTitle: string;
|
|
69
|
+
newFolderMessage: string;
|
|
70
|
+
newFolderPlaceholder: string;
|
|
71
|
+
newFolderDefault: string;
|
|
72
|
+
/** Rename prompt (used when inline rename is unavailable / disabled). */
|
|
73
|
+
renameTitle: string;
|
|
74
|
+
renameMessage: string;
|
|
75
|
+
/** Name validation. */
|
|
76
|
+
invalidNameEmpty: string;
|
|
77
|
+
/** Suffix used by the default `duplicate` flow when the consumer's adapter
|
|
78
|
+
* needs a hint name. Receives the source name. */
|
|
79
|
+
duplicateSuffix: (name: string) => string;
|
|
80
|
+
}
|
|
81
|
+
declare const DEFAULT_TREE_LABELS: TreeLabels;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Position of a drop / move relative to the target row. `inside` means
|
|
85
|
+
* "drop into this folder"; `before`/`after` are sibling reorder hints.
|
|
86
|
+
*/
|
|
87
|
+
type TreeMovePosition = 'before' | 'inside' | 'after';
|
|
88
|
+
/**
|
|
89
|
+
* CRUD adapter. Every method is optional — Tree only surfaces built-in
|
|
90
|
+
* menu items / hotkeys whose adapter method is defined. So an
|
|
91
|
+
* inspection-only tree just passes `{}` (or no adapter) and gets no
|
|
92
|
+
* destructive menu actions.
|
|
93
|
+
*
|
|
94
|
+
* Dialogs (`alert` / `confirm` / `prompt`) are taken from `window.dialog`
|
|
95
|
+
* exposed by `<DialogProvider />` in `@djangocfg/ui-core`. The host app
|
|
96
|
+
* is expected to mount that provider once at the layout level — Tree
|
|
97
|
+
* never re-implements its own dialogs.
|
|
98
|
+
*/
|
|
99
|
+
interface TreeAdapter<T = unknown> {
|
|
100
|
+
/** Delete the given nodes. Tree calls `window.dialog.confirm` first. */
|
|
101
|
+
remove?: (nodes: TreeNode<T>[]) => Promise<void>;
|
|
102
|
+
/** Inline rename — node + new name (already non-empty, post-validate). */
|
|
103
|
+
rename?: (node: TreeNode<T>, nextName: string) => Promise<void>;
|
|
104
|
+
/** Create a file under `parent` (null → root). Tree prompts for name. */
|
|
105
|
+
createFile?: (parent: TreeNode<T> | null, name: string) => Promise<void>;
|
|
106
|
+
/** Create a folder under `parent` (null → root). Tree prompts for name. */
|
|
107
|
+
createFolder?: (parent: TreeNode<T> | null, name: string) => Promise<void>;
|
|
108
|
+
/** Duplicate the given nodes in-place. */
|
|
109
|
+
duplicate?: (nodes: TreeNode<T>[]) => Promise<void>;
|
|
110
|
+
/** Move nodes (drag-and-drop, cut+paste). */
|
|
111
|
+
move?: (nodes: TreeNode<T>[], target: TreeNode<T> | null, position: TreeMovePosition) => Promise<void>;
|
|
112
|
+
/** Copy nodes (copy+paste, drop with modifier). */
|
|
113
|
+
copy?: (nodes: TreeNode<T>[], target: TreeNode<T> | null, position: TreeMovePosition) => Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* Optional name validator. Return a non-empty string to surface as an
|
|
116
|
+
* error via `window.dialog.alert`. Return `null` to accept.
|
|
117
|
+
*/
|
|
118
|
+
validateName?: (name: string, ctx: {
|
|
119
|
+
node?: TreeNode<T>;
|
|
120
|
+
parent?: TreeNode<T> | null;
|
|
121
|
+
}) => string | null;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Built-in action ids. Used by `defaultMenuItems` and the internal
|
|
125
|
+
* built-in action registry. Each id maps 1:1 to an adapter method
|
|
126
|
+
* (plus a few selection helpers).
|
|
127
|
+
*/
|
|
128
|
+
type TreeBuiltinAction = 'open' | 'rename' | 'duplicate' | 'cut' | 'copy' | 'paste' | 'delete' | 'new-file' | 'new-folder';
|
|
129
|
+
|
|
130
|
+
type TreeDensity = 'compact' | 'cozy' | 'comfortable';
|
|
131
|
+
type TreeAccentIntensity = 'subtle' | 'default' | 'strong';
|
|
132
|
+
type TreeRadius = 'none' | 'sm' | 'md';
|
|
133
|
+
/**
|
|
134
|
+
* Cosmetic configuration. Every field is optional; missing values fall
|
|
135
|
+
* back to the `cozy` preset (a comfortable VSCode-Explorer-like density).
|
|
136
|
+
*
|
|
137
|
+
* Customize the look without re-implementing slots.
|
|
138
|
+
*/
|
|
139
|
+
interface TreeAppearance {
|
|
140
|
+
/** Built-in size preset. Default: `'cozy'`. */
|
|
141
|
+
density?: TreeDensity;
|
|
142
|
+
/** Override row height in px (wins over density). */
|
|
143
|
+
rowHeight?: number;
|
|
144
|
+
/** Override icon + chevron size in px (wins over density). */
|
|
145
|
+
iconSize?: number;
|
|
146
|
+
/** Lucide stroke width for icon + chevron. Default: 1.5. */
|
|
147
|
+
iconStrokeWidth?: number;
|
|
148
|
+
/** Override label font size in px (wins over density). */
|
|
149
|
+
fontSize?: number;
|
|
150
|
+
/** Pixels between chevron / icon / label. Default depends on density. */
|
|
151
|
+
gap?: number;
|
|
152
|
+
/** Pixels between nesting levels. Default: 16. */
|
|
153
|
+
indent?: number;
|
|
154
|
+
/** Hover / selected highlight intensity. Default: `'default'`. */
|
|
155
|
+
accent?: TreeAccentIntensity;
|
|
156
|
+
/** Row corner radius. Default: `'sm'`. */
|
|
157
|
+
radius?: TreeRadius;
|
|
158
|
+
/** Indent-guide line opacity (0..1). Default: 0.4. */
|
|
159
|
+
indentGuideOpacity?: number;
|
|
160
|
+
/**
|
|
161
|
+
* Show a 2px primary-tinted bar on the left of the selected row.
|
|
162
|
+
* Mimics the VSCode active-tab indicator. Default: `true`.
|
|
163
|
+
*/
|
|
164
|
+
showActiveIndicator?: boolean;
|
|
165
|
+
}
|
|
166
|
+
interface ResolvedAppearance {
|
|
167
|
+
density: TreeDensity;
|
|
168
|
+
rowHeight: number;
|
|
169
|
+
iconSize: number;
|
|
170
|
+
iconStrokeWidth: number;
|
|
171
|
+
fontSize: number;
|
|
172
|
+
gap: number;
|
|
173
|
+
indent: number;
|
|
174
|
+
accent: TreeAccentIntensity;
|
|
175
|
+
radius: TreeRadius;
|
|
176
|
+
indentGuideOpacity: number;
|
|
177
|
+
showActiveIndicator: boolean;
|
|
178
|
+
}
|
|
179
|
+
declare const DEFAULT_TREE_APPEARANCE: ResolvedAppearance;
|
|
180
|
+
/**
|
|
181
|
+
* Merge a partial appearance with the default + density preset.
|
|
182
|
+
*
|
|
183
|
+
* Explicit numeric overrides (e.g. `rowHeight`) win over the density preset.
|
|
184
|
+
*/
|
|
185
|
+
declare function resolveAppearance(input?: TreeAppearance,
|
|
186
|
+
/** Outer `indent` prop (kept on TreeRoot for back-compat). */
|
|
187
|
+
outerIndent?: number): ResolvedAppearance;
|
|
188
|
+
/**
|
|
189
|
+
* Build the `style` object that exposes the resolved appearance to any
|
|
190
|
+
* descendant via CSS variables. Set on `<TreeRoot>`'s outer div.
|
|
191
|
+
*/
|
|
192
|
+
declare function appearanceToStyle(a: ResolvedAppearance): CSSProperties;
|
|
193
|
+
|
|
194
|
+
interface TreeRootProps<T> {
|
|
195
|
+
/** Root nodes. Top-level items are rendered directly (no synthetic root). */
|
|
196
|
+
data: TreeNode<T>[];
|
|
197
|
+
/** Returns the human-readable name for a node (used by search/type-ahead). */
|
|
198
|
+
getItemName: (node: TreeNode<T>) => string;
|
|
199
|
+
/** Async loader for folders without inline `children`. */
|
|
200
|
+
loadChildren?: TreeLoadChildren<T>;
|
|
201
|
+
/** Selection behaviour. Default: `'single'`. */
|
|
202
|
+
selectionMode?: TreeSelectionMode;
|
|
203
|
+
/** Pointer activation behaviour. Default: `'single-click'`. */
|
|
204
|
+
activationMode?: TreeActivationMode;
|
|
205
|
+
/** Initially expanded ids. */
|
|
206
|
+
initialExpandedIds?: TreeItemId[];
|
|
207
|
+
/** Initially selected ids. */
|
|
208
|
+
initialSelectedIds?: TreeItemId[];
|
|
209
|
+
/** Pixels of indent per nesting level. Default: 16. (Shortcut for `appearance.indent`.) */
|
|
210
|
+
indent?: number;
|
|
211
|
+
/** Cosmetic configuration: density, sizes, accent intensity, radius. */
|
|
212
|
+
appearance?: TreeAppearance;
|
|
213
|
+
/** Triggered when selection changes. */
|
|
214
|
+
onSelectionChange?: (selectedIds: TreeItemId[]) => void;
|
|
215
|
+
/** Triggered when expanded set changes. */
|
|
216
|
+
onExpansionChange?: (expandedIds: TreeItemId[]) => void;
|
|
217
|
+
/**
|
|
218
|
+
* Triggered when a leaf is activated (Enter / dblclick / click depending
|
|
219
|
+
* on `activationMode`). Folders never call this — they toggle instead.
|
|
220
|
+
*/
|
|
221
|
+
onActivate?: (node: TreeNode<T>, opts: TreeActivateOptions) => void;
|
|
222
|
+
/**
|
|
223
|
+
* Optional predicate. Nodes returning `false` (and their descendants) are
|
|
224
|
+
* not rendered and not searchable. Use this to hide dot-files, system
|
|
225
|
+
* entries, or anything else the consumer wants to filter out.
|
|
226
|
+
*/
|
|
227
|
+
filterNode?: (node: TreeNode<T>) => boolean;
|
|
228
|
+
/** Show built-in search input. Default: false. */
|
|
229
|
+
enableSearch?: boolean;
|
|
230
|
+
/** Type printable letters to jump to a matching name. Default: true. */
|
|
231
|
+
enableTypeAhead?: boolean;
|
|
232
|
+
/** Render vertical indent guides under expanded folders. Default: false. */
|
|
233
|
+
showIndentGuides?: boolean;
|
|
234
|
+
/**
|
|
235
|
+
* Allow inline rename. When true, F2 starts an inline `<input>`; the
|
|
236
|
+
* value is committed through `adapter.rename`. Requires `adapter.rename`.
|
|
237
|
+
* Default: false.
|
|
238
|
+
*/
|
|
239
|
+
enableInlineRename?: boolean;
|
|
240
|
+
/**
|
|
241
|
+
* Enable Finder/Explorer keyboard shortcuts (delete, rename, duplicate,
|
|
242
|
+
* new file/folder, cut/copy/paste). Bindings only fire when the tree
|
|
243
|
+
* container has focus. Individual shortcuts are still gated by the
|
|
244
|
+
* adapter — `⌘⌫` does nothing if `adapter.remove` is undefined.
|
|
245
|
+
* Default: false.
|
|
246
|
+
*/
|
|
247
|
+
enableFinderHotkeys?: boolean;
|
|
248
|
+
/**
|
|
249
|
+
* Enable drag-and-drop reorder + move-into-folder. Requires
|
|
250
|
+
* `adapter.move`. Powered by `@dnd-kit/core` — pointer + keyboard
|
|
251
|
+
* sensors, accessible.
|
|
252
|
+
* Default: false.
|
|
253
|
+
*/
|
|
254
|
+
enableDnD?: boolean;
|
|
255
|
+
/**
|
|
256
|
+
* Custom drop validation. Returns `true` to allow, `false` to forbid.
|
|
257
|
+
* The default validator rejects self-drops and cycles (dropping a
|
|
258
|
+
* folder into its own descendant). Use this to add domain rules
|
|
259
|
+
* (read-only branches, type matching, …).
|
|
260
|
+
*/
|
|
261
|
+
canDrop?: (ctx: {
|
|
262
|
+
source: TreeNode<T>[];
|
|
263
|
+
target: TreeNode<T> | null;
|
|
264
|
+
position: TreeMovePosition;
|
|
265
|
+
}) => boolean;
|
|
266
|
+
/** Custom row renderer. Falls back to the default <TreeRow />. */
|
|
267
|
+
renderRow?: TreeRowSlot<T>;
|
|
268
|
+
/** Replace default folder/file icon. */
|
|
269
|
+
renderIcon?: TreeRowSlot<T>;
|
|
270
|
+
/** Replace default label rendering. */
|
|
271
|
+
renderLabel?: TreeRowSlot<T>;
|
|
272
|
+
/** Right-side actions slot (per row). */
|
|
273
|
+
renderActions?: TreeRowSlot<T>;
|
|
274
|
+
/** Wrap each row in a context menu (right-click). Receives the row meta + trigger element. */
|
|
275
|
+
renderContextMenu?: TreeContextMenuSlot<T>;
|
|
276
|
+
/**
|
|
277
|
+
* Declarative right-click menu — short-form. Pass `(row) => [items]` and the
|
|
278
|
+
* Tree builds a `<ContextMenu>` for you with sensible defaults. Ignored if
|
|
279
|
+
* `renderContextMenu` is also set. Return `null`/`undefined`/`[]` to skip
|
|
280
|
+
* the menu for that row.
|
|
281
|
+
*/
|
|
282
|
+
contextMenuActions?: TreeContextMenuActionsResolver<T>;
|
|
283
|
+
/** Override built-in copy in your locale. */
|
|
284
|
+
labels?: Partial<TreeLabels>;
|
|
285
|
+
/** Persist expanded + (optional) selected ids in localStorage under this key. */
|
|
286
|
+
persistKey?: string;
|
|
287
|
+
/** Persist selection alongside expansion. Default: false. */
|
|
288
|
+
persistSelection?: boolean;
|
|
289
|
+
/**
|
|
290
|
+
* CRUD adapter. When set, Tree builds default context-menu items and
|
|
291
|
+
* Finder hotkeys for every method the adapter defines. Methods that
|
|
292
|
+
* are not provided produce no UI — no greyed-out items.
|
|
293
|
+
*/
|
|
294
|
+
adapter?: TreeAdapter<T>;
|
|
295
|
+
/**
|
|
296
|
+
* Which built-in actions to expose in the auto-built context menu. If
|
|
297
|
+
* omitted, every action whose adapter method exists is shown.
|
|
298
|
+
*
|
|
299
|
+
* Pass `[]` to suppress the auto-built menu entirely while keeping the
|
|
300
|
+
* adapter for hotkeys / DnD.
|
|
301
|
+
*/
|
|
302
|
+
defaultMenuItems?: TreeBuiltinAction[];
|
|
303
|
+
className?: string;
|
|
304
|
+
style?: CSSProperties;
|
|
305
|
+
}
|
|
5
306
|
|
|
6
307
|
/**
|
|
7
308
|
* High-level entry point. Wraps Provider + (optional) search bar + content.
|
|
@@ -11,33 +312,88 @@ import * as react from 'react';
|
|
|
11
312
|
*/
|
|
12
313
|
declare function TreeRoot<T>(props: TreeRootProps<T>): react_jsx_runtime.JSX.Element;
|
|
13
314
|
|
|
14
|
-
type ChildEntryStatus = 'idle' | 'loading' | 'loaded' | 'error';
|
|
15
|
-
interface ChildEntry<T> {
|
|
16
|
-
status: ChildEntryStatus;
|
|
17
|
-
children: TreeNode<T>[];
|
|
18
|
-
error?: string;
|
|
19
|
-
}
|
|
20
|
-
type ChildCache<T> = Map<TreeItemId, ChildEntry<T>>;
|
|
21
|
-
declare const createChildCache: <T>() => ChildCache<T>;
|
|
22
315
|
/**
|
|
23
|
-
*
|
|
316
|
+
* `<FinderTree>` — opinionated Finder/Explorer-style preset.
|
|
24
317
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
318
|
+
* Equivalent to `<TreeRoot>` with multi-selection, double-click activation,
|
|
319
|
+
* inline rename, indent guides, and a cozy appearance turned on. Pass an
|
|
320
|
+
* `adapter` to get the built-in CRUD menu wired to `window.dialog.*`.
|
|
27
321
|
*
|
|
28
|
-
*
|
|
322
|
+
* Override any preset default by simply passing the same prop:
|
|
323
|
+
*
|
|
324
|
+
* ```tsx
|
|
325
|
+
* <FinderTree<FsNode>
|
|
326
|
+
* data={data}
|
|
327
|
+
* getItemName={(n) => n.data.name}
|
|
328
|
+
* adapter={fsAdapter}
|
|
329
|
+
* // override one preset default — everything else stays Finder-y:
|
|
330
|
+
* activationMode="single-click-preview"
|
|
331
|
+
* />
|
|
332
|
+
* ```
|
|
29
333
|
*/
|
|
30
|
-
declare
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
334
|
+
declare function FinderTree<T>(props: TreeRootProps<T>): react_jsx_runtime.JSX.Element;
|
|
335
|
+
|
|
336
|
+
type ClipboardKind = 'cut' | 'copy';
|
|
337
|
+
interface ClipboardEntry {
|
|
338
|
+
kind: ClipboardKind;
|
|
339
|
+
ids: TreeItemId[];
|
|
340
|
+
}
|
|
341
|
+
type ClipboardState = ClipboardEntry | null;
|
|
342
|
+
|
|
343
|
+
interface DropTargetState {
|
|
344
|
+
id: TreeItemId | null;
|
|
345
|
+
position: TreeMovePosition;
|
|
346
|
+
}
|
|
347
|
+
interface UseDndReturn<T> {
|
|
348
|
+
/** True when the host enabled DnD AND adapter.move is defined. */
|
|
349
|
+
active: boolean;
|
|
350
|
+
/** Ids currently being dragged (empty when not dragging). */
|
|
351
|
+
draggingIds: ReadonlySet<TreeItemId>;
|
|
352
|
+
/** Live drop target (`null` when nothing under the pointer). */
|
|
353
|
+
dropTarget: DropTargetState | null;
|
|
354
|
+
/** Called by row sensors on dragstart. */
|
|
355
|
+
beginDrag: (rowId: TreeItemId) => void;
|
|
356
|
+
/**
|
|
357
|
+
* Called on dragover. `null` clears the indicator. Tree already
|
|
358
|
+
* filters self/cycle drops via `defaultCanDrop` — the row component
|
|
359
|
+
* decides the position from pointer geometry first.
|
|
360
|
+
*/
|
|
361
|
+
setDropTarget: (target: DropTargetState | null) => void;
|
|
362
|
+
/** Commit drop — calls `adapter.move` and resets transient state. */
|
|
363
|
+
commitDrop: () => Promise<void>;
|
|
364
|
+
/** Cancel without committing (Esc, drop outside). */
|
|
365
|
+
cancelDrag: () => void;
|
|
366
|
+
/**
|
|
367
|
+
* Validate a candidate drop in real time. Combines `defaultCanDrop`
|
|
368
|
+
* with the consumer's `canDrop`. Used by the row component to
|
|
369
|
+
* suppress the indicator on invalid hovers.
|
|
370
|
+
*/
|
|
371
|
+
isAllowedDrop: (target: TreeNode<T> | null, position: TreeMovePosition) => boolean;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Click selection — derives next `{selected, anchor, focused}` from a
|
|
376
|
+
* pointer event's modifier keys. `rows` is the current flat view (used
|
|
377
|
+
* for shift-range computation).
|
|
378
|
+
*/
|
|
379
|
+
interface ClickModifiers {
|
|
380
|
+
shift: boolean;
|
|
381
|
+
meta: boolean;
|
|
382
|
+
}
|
|
35
383
|
|
|
36
384
|
interface TreeContextValue<T> {
|
|
37
385
|
expanded: ReadonlySet<TreeItemId>;
|
|
38
386
|
selected: ReadonlySet<TreeItemId>;
|
|
387
|
+
/** Anchor for shift-range. `null` when nothing has been clicked yet. */
|
|
388
|
+
anchor: TreeItemId | null;
|
|
39
389
|
focused: TreeItemId | null;
|
|
40
390
|
query: string;
|
|
391
|
+
/** Id of the row currently in inline-rename mode (or `null`). */
|
|
392
|
+
renamingId: TreeItemId | null;
|
|
393
|
+
/** Is inline rename allowed by the host? */
|
|
394
|
+
inlineRenameEnabled: boolean;
|
|
395
|
+
/** Tree-local clipboard (cut / copy) — P5. `null` when empty. */
|
|
396
|
+
clipboard: ClipboardState;
|
|
41
397
|
flatRows: FlatRow<T>[];
|
|
42
398
|
/** Search-matching node ids (subset of all flatRows). */
|
|
43
399
|
matchingIds: ReadonlySet<TreeItemId>;
|
|
@@ -49,8 +405,44 @@ interface TreeContextValue<T> {
|
|
|
49
405
|
select: (id: TreeItemId) => void;
|
|
50
406
|
setSelectedIds: (ids: TreeItemId[]) => void;
|
|
51
407
|
clearSelection: () => void;
|
|
408
|
+
/**
|
|
409
|
+
* Finder/Explorer-style click selection. Reads modifier keys from the
|
|
410
|
+
* passed event and decides between replace / toggle / range / union.
|
|
411
|
+
*/
|
|
412
|
+
clickSelect: (id: TreeItemId, mods: ClickModifiers) => void;
|
|
413
|
+
/**
|
|
414
|
+
* Move-focus with optional range-extend (shift+arrows).
|
|
415
|
+
*/
|
|
416
|
+
moveSelect: (id: TreeItemId, opts: {
|
|
417
|
+
extend: boolean;
|
|
418
|
+
}) => void;
|
|
419
|
+
/** Select every currently visible row (mod+a). */
|
|
420
|
+
selectAll: () => void;
|
|
52
421
|
setFocus: (id: TreeItemId | null) => void;
|
|
53
422
|
setQuery: (q: string) => void;
|
|
423
|
+
/** Put the given ids on Tree's clipboard as `cut`. */
|
|
424
|
+
cutToClipboard: (ids: TreeItemId[]) => void;
|
|
425
|
+
/** Put the given ids on Tree's clipboard as `copy`. */
|
|
426
|
+
copyToClipboard: (ids: TreeItemId[]) => void;
|
|
427
|
+
/**
|
|
428
|
+
* Apply clipboard onto `target` (a row, or `null` for the root). Cut →
|
|
429
|
+
* `adapter.move`; copy → `adapter.copy`. After a successful cut+paste
|
|
430
|
+
* the clipboard is cleared. No-op when clipboard is empty or the
|
|
431
|
+
* matching adapter method is undefined.
|
|
432
|
+
*/
|
|
433
|
+
pasteFromClipboard: (target: TreeNode<T> | null, position?: TreeMovePosition) => Promise<void>;
|
|
434
|
+
/** Clear the clipboard without pasting. */
|
|
435
|
+
clearClipboard: () => void;
|
|
436
|
+
/** Begin inline rename on the given row. */
|
|
437
|
+
startRename: (id: TreeItemId) => void;
|
|
438
|
+
/** Cancel inline rename without committing. */
|
|
439
|
+
cancelRename: () => void;
|
|
440
|
+
/**
|
|
441
|
+
* Commit inline rename. Tree calls `adapter.rename` and then clears
|
|
442
|
+
* the renaming state. On validation failure surfaces an error via
|
|
443
|
+
* `window.dialog.alert` and keeps the input open.
|
|
444
|
+
*/
|
|
445
|
+
commitRename: (id: TreeItemId, nextName: string) => Promise<boolean>;
|
|
54
446
|
refresh: (id: TreeItemId) => Promise<void>;
|
|
55
447
|
refreshAll: () => Promise<void>;
|
|
56
448
|
activate: (node: TreeNode<T>, opts?: TreeActivateOptions) => void;
|
|
@@ -68,20 +460,155 @@ interface TreeContextValue<T> {
|
|
|
68
460
|
renderLabel?: TreeRowSlot<T>;
|
|
69
461
|
renderActions?: TreeRowSlot<T>;
|
|
70
462
|
renderContextMenu?: TreeContextMenuSlot<T>;
|
|
463
|
+
/** CRUD adapter (P2). May be undefined — Tree still renders normally. */
|
|
464
|
+
adapter?: TreeAdapter<T>;
|
|
465
|
+
/**
|
|
466
|
+
* Final, merged declarative menu resolver. Combines built-in adapter
|
|
467
|
+
* actions (filtered by `defaultMenuItems`) with the consumer's
|
|
468
|
+
* `contextMenuActions` resolver, and injects the current
|
|
469
|
+
* `selectedNodes` before delegating.
|
|
470
|
+
*/
|
|
471
|
+
resolvedContextMenuActions?: (row: TreeRowRenderProps<T>) => TreeContextMenuItem<T>[] | null | undefined;
|
|
472
|
+
/**
|
|
473
|
+
* Imperative lookup for any node currently known to Tree (root +
|
|
474
|
+
* cached async children).
|
|
475
|
+
*/
|
|
476
|
+
getNodeById: (id: TreeItemId) => TreeNode<T> | undefined;
|
|
477
|
+
/**
|
|
478
|
+
* Drag-and-drop state and handlers (P6). `dnd.active` is `false`
|
|
479
|
+
* when the host didn't enable DnD or `adapter.move` is missing — in
|
|
480
|
+
* that case `TreeRow` skips all drag setup.
|
|
481
|
+
*/
|
|
482
|
+
dnd: UseDndReturn<T>;
|
|
71
483
|
}
|
|
484
|
+
|
|
485
|
+
type ChildEntryStatus = 'idle' | 'loading' | 'loaded' | 'error';
|
|
486
|
+
interface ChildEntry<T> {
|
|
487
|
+
status: ChildEntryStatus;
|
|
488
|
+
children: TreeNode<T>[];
|
|
489
|
+
error?: string;
|
|
490
|
+
}
|
|
491
|
+
type ChildCache<T> = Map<TreeItemId, ChildEntry<T>>;
|
|
492
|
+
declare const createChildCache: <T>() => ChildCache<T>;
|
|
493
|
+
/**
|
|
494
|
+
* Resolve a node's children for the current render.
|
|
495
|
+
*
|
|
496
|
+
* - If the node carries inline `children`, those win (no async fetch).
|
|
497
|
+
* - Otherwise we look in the cache.
|
|
498
|
+
*
|
|
499
|
+
* Returns `null` when nothing is loaded yet (caller may show a skeleton).
|
|
500
|
+
*/
|
|
501
|
+
declare const resolveChildren: <T>(cache: ChildCache<T>, node: TreeNode<T>) => {
|
|
502
|
+
children: TreeNode<T>[] | null;
|
|
503
|
+
status: ChildEntryStatus;
|
|
504
|
+
error?: string;
|
|
505
|
+
};
|
|
506
|
+
|
|
72
507
|
declare function useTreeContext<T>(): TreeContextValue<T>;
|
|
73
|
-
interface TreeProviderProps<T> extends Pick<TreeRootProps<T>, 'data' | 'getItemName' | 'loadChildren' | 'selectionMode' | 'activationMode' | 'initialExpandedIds' | 'initialSelectedIds' | 'indent' | 'appearance' | 'onSelectionChange' | 'onExpansionChange' | 'onActivate' | 'filterNode' | 'enableSearch' | 'showIndentGuides' | 'renderIcon' | 'renderLabel' | 'renderActions' | 'renderContextMenu' | 'labels' | 'persistKey' | 'persistSelection'> {
|
|
508
|
+
interface TreeProviderProps<T> extends Pick<TreeRootProps<T>, 'data' | 'getItemName' | 'loadChildren' | 'selectionMode' | 'activationMode' | 'initialExpandedIds' | 'initialSelectedIds' | 'indent' | 'appearance' | 'onSelectionChange' | 'onExpansionChange' | 'onActivate' | 'filterNode' | 'enableSearch' | 'showIndentGuides' | 'renderIcon' | 'renderLabel' | 'renderActions' | 'renderContextMenu' | 'contextMenuActions' | 'labels' | 'persistKey' | 'persistSelection' | 'adapter' | 'defaultMenuItems' | 'enableInlineRename' | 'enableDnD' | 'canDrop'> {
|
|
74
509
|
children: react.ReactNode;
|
|
75
510
|
}
|
|
76
511
|
declare function TreeProvider<T>(props: TreeProviderProps<T>): react_jsx_runtime.JSX.Element;
|
|
77
512
|
|
|
513
|
+
interface FlattenInput<T> {
|
|
514
|
+
roots: TreeNode<T>[];
|
|
515
|
+
expandedIds: ReadonlySet<TreeItemId>;
|
|
516
|
+
cache: ChildCache<T>;
|
|
517
|
+
/** Optional predicate. Nodes returning `false` (and their descendants) are excluded. */
|
|
518
|
+
filterNode?: (node: TreeNode<T>) => boolean;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Walk the tree top-to-bottom and produce a flat list of visible rows.
|
|
522
|
+
*
|
|
523
|
+
* Visibility rule: a child row appears only when every ancestor is in
|
|
524
|
+
* `expandedIds`. The output is ordered exactly as it should render,
|
|
525
|
+
* which keeps keyboard navigation (next/prev row) trivial.
|
|
526
|
+
*/
|
|
527
|
+
declare function flattenTree<T>({ roots, expandedIds, cache, filterNode, }: FlattenInput<T>): FlatRow<T>[];
|
|
528
|
+
|
|
529
|
+
interface PersistedTreeState {
|
|
530
|
+
expandedItems: TreeItemId[];
|
|
531
|
+
selectedItems: TreeItemId[];
|
|
532
|
+
}
|
|
533
|
+
declare function loadTreeState(key: string): PersistedTreeState | null;
|
|
534
|
+
declare function saveTreeState(key: string, state: PersistedTreeState): void;
|
|
535
|
+
declare function clearTreeState(key: string): void;
|
|
536
|
+
|
|
537
|
+
interface DemoNode {
|
|
538
|
+
name: string;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Build a deterministic synthetic tree for stories and tests.
|
|
542
|
+
*
|
|
543
|
+
* @example
|
|
544
|
+
* const data = createDemoTree({ depth: 4, breadth: 3 });
|
|
545
|
+
* <TreeRoot data={data} getItemName={(n) => n.data.name} />
|
|
546
|
+
*/
|
|
547
|
+
declare function createDemoTree({ depth, breadth, rootPrefix, }?: {
|
|
548
|
+
depth?: number;
|
|
549
|
+
breadth?: number;
|
|
550
|
+
rootPrefix?: string;
|
|
551
|
+
}): TreeNode<DemoNode>[];
|
|
552
|
+
|
|
553
|
+
interface SplitName {
|
|
554
|
+
base: string;
|
|
555
|
+
ext: string;
|
|
556
|
+
}
|
|
557
|
+
declare function splitFileName(name: string): SplitName;
|
|
558
|
+
/**
|
|
559
|
+
* Returns the `[selectionStart, selectionEnd]` pair to use on focus of an
|
|
560
|
+
* `<input>` so only the base name is highlighted (Finder behaviour).
|
|
561
|
+
*
|
|
562
|
+
* Folders pass `isFolder=true` to skip extension detection and select
|
|
563
|
+
* the entire name — folders don't have file extensions semantically.
|
|
564
|
+
*/
|
|
565
|
+
declare function autoSelectRange(name: string, isFolder: boolean): [number, number];
|
|
566
|
+
|
|
567
|
+
interface DropZoneInput {
|
|
568
|
+
/** Pointer Y in viewport coordinates. */
|
|
569
|
+
pointerY: number;
|
|
570
|
+
/** Row bounding box (`getBoundingClientRect()`). */
|
|
571
|
+
rowRect: {
|
|
572
|
+
top: number;
|
|
573
|
+
bottom: number;
|
|
574
|
+
height: number;
|
|
575
|
+
};
|
|
576
|
+
/** Folders accept `inside` drops; leaves only reorder via before/after. */
|
|
577
|
+
isFolder: boolean;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Translate pointer geometry into a drop position relative to the row.
|
|
581
|
+
*
|
|
582
|
+
* For folders the row is split into three zones (top third / middle /
|
|
583
|
+
* bottom third). For leaves it's split in half (before / after).
|
|
584
|
+
*/
|
|
585
|
+
declare function resolveDropZone(input: DropZoneInput): TreeMovePosition;
|
|
586
|
+
interface CanDropInput<T> {
|
|
587
|
+
/** Nodes being dragged. */
|
|
588
|
+
source: TreeNode<T>[];
|
|
589
|
+
/** Row under the pointer (`null` = root drop zone). */
|
|
590
|
+
target: TreeNode<T> | null;
|
|
591
|
+
/** Resolved drop position. */
|
|
592
|
+
position: TreeMovePosition;
|
|
593
|
+
/** Tree's id→node lookup, used to walk descendants. */
|
|
594
|
+
getNodeById: (id: TreeItemId) => TreeNode<T> | undefined;
|
|
595
|
+
}
|
|
596
|
+
declare function defaultCanDrop<T>(input: CanDropInput<T>): boolean;
|
|
597
|
+
declare const TREE_DND_MIME = "application/x-djangocfg-tree";
|
|
598
|
+
|
|
78
599
|
declare function useTreeLabels(): TreeLabels;
|
|
79
600
|
declare function useTreeRows<T>(): FlatRow<T>[];
|
|
80
601
|
declare function useTreeSelection<T>(): {
|
|
81
602
|
selectedIds: string[];
|
|
603
|
+
anchor: string;
|
|
82
604
|
select: (id: TreeItemId) => void;
|
|
83
605
|
setSelectedIds: (ids: TreeItemId[]) => void;
|
|
84
606
|
clear: () => void;
|
|
607
|
+
clickSelect: (id: TreeItemId, mods: ClickModifiers) => void;
|
|
608
|
+
moveSelect: (id: TreeItemId, opts: {
|
|
609
|
+
extend: boolean;
|
|
610
|
+
}) => void;
|
|
611
|
+
selectAll: () => void;
|
|
85
612
|
isSelected: (id: TreeItemId) => boolean;
|
|
86
613
|
};
|
|
87
614
|
declare function useTreeExpansion<T>(): {
|
|
@@ -104,6 +631,24 @@ declare function useTreeSearch<T>(): {
|
|
|
104
631
|
matchingIds: ReadonlySet<string>;
|
|
105
632
|
matchCount: number;
|
|
106
633
|
};
|
|
634
|
+
declare function useTreeDnd<T>(): UseDndReturn<T>;
|
|
635
|
+
declare function useTreeClipboard<T>(): {
|
|
636
|
+
clipboard: ClipboardEntry;
|
|
637
|
+
isCut: (id: TreeItemId) => boolean;
|
|
638
|
+
cut: (ids: TreeItemId[]) => void;
|
|
639
|
+
copy: (ids: TreeItemId[]) => void;
|
|
640
|
+
paste: (target: TreeNode<T>, position?: TreeMovePosition) => Promise<void>;
|
|
641
|
+
clear: () => void;
|
|
642
|
+
};
|
|
643
|
+
declare function useTreeRename<T>(): {
|
|
644
|
+
/** True when the host allowed inline rename AND the adapter exposes `rename`. */
|
|
645
|
+
enabled: boolean;
|
|
646
|
+
/** Currently renaming id, or `null`. */
|
|
647
|
+
renamingId: string;
|
|
648
|
+
startRename: (id: TreeItemId) => void;
|
|
649
|
+
cancelRename: () => void;
|
|
650
|
+
commitRename: (id: TreeItemId, nextName: string) => Promise<boolean>;
|
|
651
|
+
};
|
|
107
652
|
declare function useTreeActions<T>(): {
|
|
108
653
|
expand: (id: TreeItemId) => void;
|
|
109
654
|
collapse: (id: TreeItemId) => void;
|
|
@@ -115,6 +660,53 @@ declare function useTreeActions<T>(): {
|
|
|
115
660
|
activate: (node: TreeNode<T>, opts?: TreeActivateOptions) => void;
|
|
116
661
|
};
|
|
117
662
|
|
|
663
|
+
interface UseTreeKeyboardOptions<T> {
|
|
664
|
+
rows: FlatRow<T>[];
|
|
665
|
+
focusedId: TreeItemId | null;
|
|
666
|
+
enabled?: boolean;
|
|
667
|
+
/**
|
|
668
|
+
* `true` when `selectionMode === 'multiple'` — enables shift-extend on
|
|
669
|
+
* arrow keys, Home / End, and `Cmd/Ctrl+A` select-all. Without it the
|
|
670
|
+
* shift-modifier just moves focus.
|
|
671
|
+
*/
|
|
672
|
+
multiSelect?: boolean;
|
|
673
|
+
/**
|
|
674
|
+
* Move focus to `id`. When `extend` is true and multi-select is enabled,
|
|
675
|
+
* the consumer should extend the selection range from anchor through id.
|
|
676
|
+
*/
|
|
677
|
+
onFocus: (id: TreeItemId, opts: {
|
|
678
|
+
extend: boolean;
|
|
679
|
+
}) => void;
|
|
680
|
+
onSelect: (id: TreeItemId) => void;
|
|
681
|
+
onActivate: (id: TreeItemId) => void;
|
|
682
|
+
onExpand: (id: TreeItemId) => void;
|
|
683
|
+
onCollapse: (id: TreeItemId) => void;
|
|
684
|
+
onClearSelection: () => void;
|
|
685
|
+
/** Cmd/Ctrl+A — select all visible rows. Ignored if multiSelect is false. */
|
|
686
|
+
onSelectAll?: () => void;
|
|
687
|
+
}
|
|
688
|
+
interface UseTreeKeyboardReturn {
|
|
689
|
+
/** Attach to the tree container. Hotkeys only fire when focus is inside. */
|
|
690
|
+
ref: (instance: HTMLElement | null) => void;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Standard tree keyboard navigation, scoped to the container ref.
|
|
695
|
+
*
|
|
696
|
+
* - ↑ / ↓ : prev / next visible row (Shift extends range)
|
|
697
|
+
* - Home / End : first / last visible row (Shift extends range)
|
|
698
|
+
* - → / ← : expand-or-jump-to-child / collapse-or-jump-to-parent
|
|
699
|
+
* - Enter / Space : activate (folder → toggle, leaf → onActivate)
|
|
700
|
+
* - Esc : clear selection
|
|
701
|
+
* - Cmd/Ctrl + A : select all (multi-select only)
|
|
702
|
+
*
|
|
703
|
+
* Pure decision-making lives in the sibling helpers (`arrow-nav.ts`,
|
|
704
|
+
* `expand-collapse.ts`, `activation.ts`) so it's unit-testable without
|
|
705
|
+
* a DOM. This file only wires up `useHotkey` bindings and dispatches
|
|
706
|
+
* the helper outcomes back to the consumer's callbacks.
|
|
707
|
+
*/
|
|
708
|
+
declare function useTreeKeyboard<T>({ rows, focusedId, enabled, multiSelect, onFocus, onSelect, onActivate, onExpand, onCollapse, onClearSelection, onSelectAll, }: UseTreeKeyboardOptions<T>): UseTreeKeyboardReturn;
|
|
709
|
+
|
|
118
710
|
interface UseTreeTypeAheadOptions<T> {
|
|
119
711
|
/** Visible flat rows in render order. */
|
|
120
712
|
rows: FlatRow<T>[];
|
|
@@ -136,35 +728,66 @@ interface UseTreeTypeAheadOptions<T> {
|
|
|
136
728
|
*/
|
|
137
729
|
declare function useTreeTypeAhead<T>({ rows, getItemName, containerRef, onMatch, enabled, }: UseTreeTypeAheadOptions<T>): void;
|
|
138
730
|
|
|
139
|
-
interface
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
731
|
+
interface BuiltinActionContext<T> {
|
|
732
|
+
adapter: TreeAdapter<T>;
|
|
733
|
+
labels: TreeLabels;
|
|
734
|
+
/** Currently selected nodes (full objects, resolved from ids). */
|
|
735
|
+
selectedNodes: TreeNode<T>[];
|
|
736
|
+
/** Row the user right-clicked / triggered the action on. May be null
|
|
737
|
+
* for empty-area actions (paste / new file / new folder at root). */
|
|
738
|
+
targetNode: TreeNode<T> | null;
|
|
739
|
+
/** Returns the human-readable name for a node (uses `getItemName`). */
|
|
740
|
+
getName: (node: TreeNode<T>) => string;
|
|
741
|
+
/** Imperative: start inline rename on this id (no-op if disabled). */
|
|
742
|
+
startInlineRename?: (id: TreeItemId) => void;
|
|
743
|
+
/** Clipboard hooks (P5). Provided by Tree's context; pure forwarding. */
|
|
744
|
+
clipboard?: {
|
|
745
|
+
/** Current clipboard kind, if any (so we can hide "Paste" when empty). */
|
|
746
|
+
hasItems: boolean;
|
|
747
|
+
cut: (ids: TreeItemId[]) => void;
|
|
748
|
+
copy: (ids: TreeItemId[]) => void;
|
|
749
|
+
paste: () => void | Promise<void>;
|
|
750
|
+
};
|
|
149
751
|
}
|
|
150
|
-
|
|
151
|
-
|
|
752
|
+
|
|
753
|
+
interface UseTreeFinderHotkeysOptions<T> {
|
|
754
|
+
/** Off by default — Tree opt-ins via `enableFinderHotkeys`. */
|
|
755
|
+
enabled: boolean;
|
|
756
|
+
/** Adapter — used both for action availability and dispatch. */
|
|
757
|
+
adapter?: TreeAdapter<T>;
|
|
758
|
+
/** Labels (passed through into adapter action context for dialogs). */
|
|
759
|
+
labels: TreeLabels;
|
|
760
|
+
/** Live selection (set of ids). */
|
|
761
|
+
selected: ReadonlySet<TreeItemId>;
|
|
762
|
+
/** Live focused id (used as "target" for new-file/new-folder actions). */
|
|
763
|
+
focused: TreeItemId | null;
|
|
764
|
+
/** Id → node lookup. */
|
|
765
|
+
getNodeById: (id: TreeItemId) => TreeNode<T> | undefined;
|
|
766
|
+
/** Display name resolver. */
|
|
767
|
+
getItemName: (node: TreeNode<T>) => string;
|
|
768
|
+
/** Open inline rename on the row (P3). Falls back to a prompt otherwise. */
|
|
769
|
+
startInlineRename?: (id: TreeItemId) => void;
|
|
770
|
+
/** Clipboard bindings (P5). When undefined, ⌘C/X/V are no-ops. */
|
|
771
|
+
clipboard?: BuiltinActionContext<T>['clipboard'];
|
|
772
|
+
/** Whether typing is currently in inline-rename input — pauses bindings. */
|
|
773
|
+
paused?: boolean;
|
|
774
|
+
}
|
|
775
|
+
interface UseTreeFinderHotkeysReturn {
|
|
776
|
+
/** Attach to the tree container ref so hotkeys only fire when it has focus. */
|
|
152
777
|
ref: (instance: HTMLElement | null) => void;
|
|
153
778
|
}
|
|
154
779
|
/**
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
* - Home / End : first / last visible row
|
|
159
|
-
* - → : expand folder; if already expanded, jump to first child
|
|
160
|
-
* - ← : collapse folder; if already collapsed (or leaf), jump to parent
|
|
161
|
-
* - Enter / Space : activate (folder => toggle, leaf => onActivate)
|
|
162
|
-
* - Esc : clear selection
|
|
780
|
+
* Wire the platform-aware Finder/Explorer shortcuts to the built-in
|
|
781
|
+
* adapter actions. Bindings are scoped to the container ref via
|
|
782
|
+
* `useHotkey`, so they don't leak to the rest of the page.
|
|
163
783
|
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
784
|
+
* Each shortcut is bound by an explicit `useHotkey` call (no `.map(useHotkey)`
|
|
785
|
+
* loop, so the rules-of-hooks lint passes cleanly). The handler routes
|
|
786
|
+
* through `runBuiltinAction`, which silently no-ops when the adapter
|
|
787
|
+
* doesn't expose the matching method — so a Tree with `adapter = { remove }`
|
|
788
|
+
* only effectively reacts to ⌘⌫ / Delete.
|
|
166
789
|
*/
|
|
167
|
-
declare function
|
|
790
|
+
declare function useTreeFinderHotkeys<T>(opts: UseTreeFinderHotkeysOptions<T>): UseTreeFinderHotkeysReturn;
|
|
168
791
|
|
|
169
792
|
interface TreeChevronProps {
|
|
170
793
|
isExpanded: boolean;
|
|
@@ -273,44 +896,66 @@ interface TreeIndentGuidesProps {
|
|
|
273
896
|
*/
|
|
274
897
|
declare function TreeIndentGuides({ level, indent }: TreeIndentGuidesProps): react_jsx_runtime.JSX.Element;
|
|
275
898
|
|
|
276
|
-
interface
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
899
|
+
interface TreeRenameInputProps {
|
|
900
|
+
initialValue: string;
|
|
901
|
+
isFolder: boolean;
|
|
902
|
+
/** Called with the new (trimmed) name when the user presses Enter / blurs. */
|
|
903
|
+
onCommit: (nextName: string) => void | Promise<unknown>;
|
|
904
|
+
/** Called when the user presses Escape. */
|
|
905
|
+
onCancel: () => void;
|
|
906
|
+
className?: string;
|
|
282
907
|
}
|
|
283
908
|
/**
|
|
284
|
-
*
|
|
909
|
+
* Inline rename input rendered in place of `<TreeLabel>` while a row is
|
|
910
|
+
* being renamed. Mounts focused with the *base* portion of the name
|
|
911
|
+
* pre-selected (Finder behaviour — `foo.txt` selects `foo`).
|
|
285
912
|
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
913
|
+
* Behaviour:
|
|
914
|
+
* - Enter → commit
|
|
915
|
+
* - Escape → cancel (no adapter call)
|
|
916
|
+
* - blur → commit (matches Finder; intentional even for empty
|
|
917
|
+
* names — the host validates and re-opens on error)
|
|
918
|
+
* - all other keys are stopped from bubbling so Tree's container
|
|
919
|
+
* hotkeys (↑↓ delete F2 etc.) don't fire while typing.
|
|
289
920
|
*/
|
|
290
|
-
declare function
|
|
921
|
+
declare function TreeRenameInput({ initialValue, isFolder, onCommit, onCancel, className, }: TreeRenameInputProps): react_jsx_runtime.JSX.Element;
|
|
291
922
|
|
|
292
|
-
interface
|
|
293
|
-
|
|
294
|
-
|
|
923
|
+
interface TreeDropIndicatorProps {
|
|
924
|
+
position: TreeMovePosition;
|
|
925
|
+
/** Indent in pixels — keeps the line aligned with the row's text. */
|
|
926
|
+
indent: number;
|
|
927
|
+
/** Render a "rejected" style (red wash) when the drop is forbidden. */
|
|
928
|
+
invalid?: boolean;
|
|
295
929
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
930
|
+
/**
|
|
931
|
+
* Visual hint for an in-progress drag-and-drop. Three modes:
|
|
932
|
+
*
|
|
933
|
+
* - `before` / `after` → a 2px horizontal line above / below the row
|
|
934
|
+
* - `inside` → a translucent primary wash filling the row
|
|
935
|
+
*
|
|
936
|
+
* Indents the line by the row's depth so it visually aligns with the
|
|
937
|
+
* target's content (rather than running edge-to-edge).
|
|
938
|
+
*
|
|
939
|
+
* Positioned absolutely — the parent `TreeRow` provides `position: relative`.
|
|
940
|
+
*/
|
|
941
|
+
declare function TreeDropIndicator({ position, indent, invalid, }: TreeDropIndicatorProps): react_jsx_runtime.JSX.Element;
|
|
299
942
|
|
|
300
|
-
interface
|
|
301
|
-
|
|
943
|
+
interface TreeEmptyAreaProps {
|
|
944
|
+
className?: string;
|
|
302
945
|
}
|
|
303
946
|
/**
|
|
304
|
-
*
|
|
947
|
+
* Fills the remaining vertical space below `<TreeContent>` so the user
|
|
948
|
+
* can right-click "into nothing" to get a Finder/Explorer-style empty
|
|
949
|
+
* area menu (paste / new file / new folder at root), and so DnD has a
|
|
950
|
+
* root drop target.
|
|
305
951
|
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
952
|
+
* Built-in actions are derived the same way as for rows — through
|
|
953
|
+
* `buildDefaultMenuItems`, with `targetNode = null` (root) and
|
|
954
|
+
* `selectedNodes = []` (nothing under the right-click). Items whose
|
|
955
|
+
* `available()` predicate fails are simply skipped, so a tree without
|
|
956
|
+
* `adapter.createFile/Folder` and without a clipboard payload shows
|
|
957
|
+
* no menu at all.
|
|
309
958
|
*/
|
|
310
|
-
declare function
|
|
311
|
-
depth?: number;
|
|
312
|
-
breadth?: number;
|
|
313
|
-
rootPrefix?: string;
|
|
314
|
-
}): TreeNode<DemoNode>[];
|
|
959
|
+
declare function TreeEmptyArea({ className }: TreeEmptyAreaProps): react_jsx_runtime.JSX.Element;
|
|
315
960
|
|
|
316
|
-
export { type ChildCache, type ChildEntry, type ChildEntryStatus, type DemoNode, FlatRow, type FlattenInput, type PersistedTreeState, ResolvedAppearance, TreeRoot as Tree, TreeActivateOptions, TreeActivationMode, TreeChevron, type TreeChevronProps, TreeContent, type TreeContentProps, TreeContextMenuSlot, type TreeContextValue, TreeEmpty, type TreeEmptyProps, TreeError, type TreeErrorProps, TreeIcon, type TreeIconProps, TreeIndentGuides, type TreeIndentGuidesProps, TreeItemId, TreeLabel, type TreeLabelProps, TreeLabels, TreeNode, TreeProvider, type TreeProviderProps, TreeRoot, TreeRootProps, TreeRow, type TreeRowProps, TreeRowSlot, TreeSearchInput, type TreeSearchInputProps, TreeSelectionMode, TreeSkeleton, type TreeSkeletonProps, type UseTreeKeyboardOptions, type UseTreeTypeAheadOptions, clearTreeState, createChildCache, createDemoTree, TreeRoot as default, flattenTree, loadTreeState, resolveChildren, saveTreeState, useTreeActions, useTreeContext, useTreeExpansion, useTreeFocus, useTreeKeyboard, useTreeLabels, useTreeRows, useTreeSearch, useTreeSelection, useTreeTypeAhead };
|
|
961
|
+
export { type ChildCache, type ChildEntry, type ChildEntryStatus, DEFAULT_TREE_APPEARANCE, DEFAULT_TREE_LABELS, type DemoNode, FinderTree, FlatRow, type FlattenInput, type PersistedTreeState, type ResolvedAppearance, TREE_DND_MIME, TreeRoot as Tree, type TreeAccentIntensity, type TreeActivateOptions, type TreeActivationMode, type TreeAdapter, type TreeAppearance, type TreeBuiltinAction, TreeChevron, type TreeChevronProps, TreeContent, type TreeContentProps, TreeContextMenuActionsResolver, TreeContextMenuItem, TreeContextMenuSlot, type TreeContextValue, type TreeDensity, TreeDropIndicator, type TreeDropIndicatorProps, TreeEmpty, TreeEmptyArea, type TreeEmptyAreaProps, type TreeEmptyProps, TreeError, type TreeErrorProps, TreeIcon, type TreeIconProps, TreeIndentGuides, type TreeIndentGuidesProps, TreeItemId, TreeLabel, type TreeLabelProps, type TreeLabels, type TreeLoadChildren, type TreeMovePosition, TreeNode, TreeProvider, type TreeProviderProps, type TreeRadius, TreeRenameInput, type TreeRenameInputProps, TreeRoot, type TreeRootProps, TreeRow, type TreeRowProps, TreeRowRenderProps, TreeRowSlot, TreeSearchInput, type TreeSearchInputProps, type TreeSelectionMode, TreeSkeleton, type TreeSkeletonProps, type UseTreeFinderHotkeysOptions, type UseTreeKeyboardOptions, type UseTreeTypeAheadOptions, appearanceToStyle, autoSelectRange, clearTreeState, createChildCache, createDemoTree, TreeRoot as default, defaultCanDrop, flattenTree, loadTreeState, resolveAppearance, resolveChildren, resolveDropZone, saveTreeState, splitFileName, useTreeActions, useTreeClipboard, useTreeContext, useTreeDnd, useTreeExpansion, useTreeFinderHotkeys, useTreeFocus, useTreeKeyboard, useTreeLabels, useTreeRename, useTreeRows, useTreeSearch, useTreeSelection, useTreeTypeAhead };
|