@djangocfg/ui-tools 2.1.415 → 2.1.417
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/audio-player/index.cjs +2099 -0
- package/dist/audio-player/index.cjs.map +1 -0
- package/dist/audio-player/index.css +65 -0
- package/dist/audio-player/index.css.map +1 -0
- package/dist/audio-player/index.d.cts +174 -0
- package/dist/audio-player/index.d.ts +174 -0
- package/dist/audio-player/index.mjs +2076 -0
- package/dist/audio-player/index.mjs.map +1 -0
- package/dist/composer-registry/index.cjs +45 -0
- package/dist/composer-registry/index.cjs.map +1 -0
- package/dist/composer-registry/index.d.cts +73 -0
- package/dist/composer-registry/index.d.ts +73 -0
- package/dist/composer-registry/index.mjs +39 -0
- package/dist/composer-registry/index.mjs.map +1 -0
- 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 +2019 -279
- package/dist/tree/index.cjs.map +1 -1
- package/dist/tree/index.d.cts +731 -72
- package/dist/tree/index.d.ts +731 -72
- package/dist/tree/index.mjs +2009 -282
- package/dist/tree/index.mjs.map +1 -1
- package/package.json +18 -9
- package/src/tools/chat/README.md +111 -1
- package/src/tools/chat/composer/Composer.tsx +146 -25
- 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/context/ChatProvider.tsx +13 -78
- package/src/tools/chat/hooks/useAutoFocusOnStreamEnd.ts +12 -15
- package/src/tools/chat/hooks/useFocusOnEmptyClick.ts +4 -5
- package/src/tools/chat/launcher/header/ChatHeader.tsx +14 -19
- package/src/tools/chat/launcher/header/ChatHeaderActionButton.tsx +8 -12
- 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 +111 -72
- 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 +103 -8
- package/src/tools/data/Tree/components/index.ts +6 -0
- package/src/tools/data/Tree/context/TreeContext.tsx +223 -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 +11 -0
- package/src/tools/data/Tree/context/menu/render.tsx +75 -0
- package/src/tools/data/Tree/context/menu/use-resolved-menu.ts +141 -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 +26 -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 +158 -0
- package/src/tools/data/Tree/types/selection.ts +3 -0
- package/src/tools/data/Tree/types/slots.ts +64 -0
- package/src/tools/dev/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +6 -9
- package/src/tools/dev/OpenapiViewer/components/DocsLayout/index.tsx +2 -4
- 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/src/tools/input/SpeechRecognition/widgets/VoiceComposerSlot.tsx +11 -12
- package/src/tools/integration/ComposerRegistry/index.ts +105 -0
- package/src/tools/media/AudioPlayer/Player.tsx +2 -0
- package/src/tools/media/AudioPlayer/PlayerShell.tsx +37 -22
- package/src/tools/media/AudioPlayer/lazy.tsx +30 -42
- package/src/tools/media/AudioPlayer/parts/Controls/IconButton.tsx +10 -11
- package/src/tools/media/AudioPlayer/parts/Controls/VolumeControl.tsx +52 -115
- package/src/tools/media/AudioPlayer/types.ts +15 -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.cjs
CHANGED
|
@@ -5,9 +5,11 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var chunkPK6SKIKE_cjs = require('../chunk-PK6SKIKE.cjs');
|
|
6
6
|
var React = require('react');
|
|
7
7
|
var lib = require('@djangocfg/ui-core/lib');
|
|
8
|
+
var dialogService = require('@djangocfg/ui-core/lib/dialog-service');
|
|
9
|
+
var lucideReact = require('lucide-react');
|
|
8
10
|
var components = require('@djangocfg/ui-core/components');
|
|
9
11
|
var jsxRuntime = require('react/jsx-runtime');
|
|
10
|
-
var
|
|
12
|
+
var core = require('@dnd-kit/core');
|
|
11
13
|
var hooks = require('@djangocfg/ui-core/hooks');
|
|
12
14
|
|
|
13
15
|
function _interopNamespace(e) {
|
|
@@ -30,16 +32,6 @@ function _interopNamespace(e) {
|
|
|
30
32
|
|
|
31
33
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
32
34
|
|
|
33
|
-
// src/tools/data/Tree/types.ts
|
|
34
|
-
var DEFAULT_TREE_LABELS = {
|
|
35
|
-
loading: "Loading\u2026",
|
|
36
|
-
empty: "Nothing to show",
|
|
37
|
-
error: "Failed to load",
|
|
38
|
-
searchPlaceholder: "Search\u2026",
|
|
39
|
-
searchMatches: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((n) => `${n} match${n === 1 ? "" : "es"}`, "searchMatches"),
|
|
40
|
-
ariaLabel: "Tree"
|
|
41
|
-
};
|
|
42
|
-
|
|
43
35
|
// src/tools/data/Tree/data/childCache.ts
|
|
44
36
|
var createChildCache = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => /* @__PURE__ */ new Map(), "createChildCache");
|
|
45
37
|
var resolveChildren = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((cache, node) => {
|
|
@@ -228,7 +220,44 @@ function rowStateClasses(a) {
|
|
|
228
220
|
].join(" ");
|
|
229
221
|
}
|
|
230
222
|
chunkPK6SKIKE_cjs.__name(rowStateClasses, "rowStateClasses");
|
|
231
|
-
|
|
223
|
+
|
|
224
|
+
// src/tools/data/Tree/types/labels.ts
|
|
225
|
+
var DEFAULT_TREE_LABELS = {
|
|
226
|
+
loading: "Loading\u2026",
|
|
227
|
+
empty: "Nothing to show",
|
|
228
|
+
error: "Failed to load",
|
|
229
|
+
searchPlaceholder: "Search\u2026",
|
|
230
|
+
searchMatches: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((n) => `${n} match${n === 1 ? "" : "es"}`, "searchMatches"),
|
|
231
|
+
ariaLabel: "Tree",
|
|
232
|
+
actionOpen: "Open",
|
|
233
|
+
actionRename: "Rename",
|
|
234
|
+
actionDuplicate: "Duplicate",
|
|
235
|
+
actionCut: "Cut",
|
|
236
|
+
actionCopy: "Copy",
|
|
237
|
+
actionPaste: "Paste",
|
|
238
|
+
actionDelete: "Delete",
|
|
239
|
+
actionNewFile: "New file",
|
|
240
|
+
actionNewFolder: "New folder",
|
|
241
|
+
confirmDeleteTitle: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((n) => n === 1 ? "Delete item?" : `Delete ${n} items?`, "confirmDeleteTitle"),
|
|
242
|
+
confirmDeleteMessage: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((names) => names.length === 1 ? `"${names[0]}" will be removed. This action cannot be undone.` : `${names.length} items will be removed. This action cannot be undone.`, "confirmDeleteMessage"),
|
|
243
|
+
confirmDeleteOk: "Delete",
|
|
244
|
+
confirmDeleteCancel: "Cancel",
|
|
245
|
+
newFileTitle: "New file",
|
|
246
|
+
newFileMessage: "File name",
|
|
247
|
+
newFilePlaceholder: "untitled.txt",
|
|
248
|
+
newFileDefault: "untitled.txt",
|
|
249
|
+
newFolderTitle: "New folder",
|
|
250
|
+
newFolderMessage: "Folder name",
|
|
251
|
+
newFolderPlaceholder: "untitled folder",
|
|
252
|
+
newFolderDefault: "untitled folder",
|
|
253
|
+
renameTitle: "Rename",
|
|
254
|
+
renameMessage: "New name",
|
|
255
|
+
invalidNameEmpty: "Name cannot be empty",
|
|
256
|
+
duplicateSuffix: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((name) => `${name} copy`, "duplicateSuffix")
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// src/tools/data/Tree/context/state/reducer.ts
|
|
260
|
+
function reducer(state, action) {
|
|
232
261
|
switch (action.type) {
|
|
233
262
|
case "expand": {
|
|
234
263
|
if (state.expanded.has(action.id)) return state;
|
|
@@ -253,100 +282,92 @@ var reducer = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((state, action) => {
|
|
|
253
282
|
case "select": {
|
|
254
283
|
if (action.mode === "none") return state;
|
|
255
284
|
if (action.mode === "single") {
|
|
256
|
-
return {
|
|
285
|
+
return {
|
|
286
|
+
...state,
|
|
287
|
+
selected: /* @__PURE__ */ new Set([action.id]),
|
|
288
|
+
anchor: action.id,
|
|
289
|
+
focused: action.id
|
|
290
|
+
};
|
|
257
291
|
}
|
|
258
292
|
const next = new Set(state.selected);
|
|
259
293
|
if (next.has(action.id)) next.delete(action.id);
|
|
260
294
|
else next.add(action.id);
|
|
261
|
-
return { ...state, selected: next, focused: action.id };
|
|
295
|
+
return { ...state, selected: next, anchor: action.id, focused: action.id };
|
|
262
296
|
}
|
|
263
297
|
case "select-many":
|
|
264
298
|
return { ...state, selected: new Set(action.ids) };
|
|
265
299
|
case "clear-selection":
|
|
266
|
-
return { ...state, selected: /* @__PURE__ */ new Set() };
|
|
300
|
+
return { ...state, selected: /* @__PURE__ */ new Set(), anchor: null };
|
|
301
|
+
case "selection-replace":
|
|
302
|
+
return {
|
|
303
|
+
...state,
|
|
304
|
+
selected: new Set(action.selected),
|
|
305
|
+
anchor: action.anchor,
|
|
306
|
+
focused: action.focused
|
|
307
|
+
};
|
|
308
|
+
case "set-anchor":
|
|
309
|
+
return { ...state, anchor: action.id };
|
|
267
310
|
case "focus":
|
|
268
311
|
return { ...state, focused: action.id };
|
|
269
312
|
case "set-query":
|
|
270
313
|
return { ...state, query: action.q };
|
|
314
|
+
case "start-rename":
|
|
315
|
+
return { ...state, renaming: action.id };
|
|
316
|
+
case "stop-rename":
|
|
317
|
+
return state.renaming === null ? state : { ...state, renaming: null };
|
|
318
|
+
case "clipboard-set":
|
|
319
|
+
return { ...state, clipboard: action.payload };
|
|
271
320
|
case "cache-tick":
|
|
272
321
|
return { ...state, cacheTick: state.cacheTick + 1 };
|
|
273
322
|
default:
|
|
274
323
|
return state;
|
|
275
324
|
}
|
|
276
|
-
}, "reducer");
|
|
277
|
-
var TreeContext = React.createContext(null);
|
|
278
|
-
function useTreeContext() {
|
|
279
|
-
const ctx = React__namespace.useContext(TreeContext);
|
|
280
|
-
if (!ctx) {
|
|
281
|
-
throw new Error("useTreeContext must be used inside <TreeProvider>");
|
|
282
|
-
}
|
|
283
|
-
return ctx;
|
|
284
325
|
}
|
|
285
|
-
chunkPK6SKIKE_cjs.__name(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
326
|
+
chunkPK6SKIKE_cjs.__name(reducer, "reducer");
|
|
327
|
+
|
|
328
|
+
// src/tools/data/Tree/context/state/initial.ts
|
|
329
|
+
function createInitialState(input) {
|
|
330
|
+
const { persisted, initialExpandedIds, initialSelectedIds, persistSelection } = input;
|
|
331
|
+
const initialSelected = new Set(
|
|
332
|
+
(persistSelection ? persisted?.selectedItems : void 0) ?? initialSelectedIds ?? []
|
|
333
|
+
);
|
|
334
|
+
const initialAnchor = initialSelected.size > 0 ? initialSelected.values().next().value : null;
|
|
335
|
+
return {
|
|
336
|
+
expanded: new Set(persisted?.expandedItems ?? initialExpandedIds ?? []),
|
|
337
|
+
selected: initialSelected,
|
|
338
|
+
anchor: initialAnchor,
|
|
339
|
+
focused: null,
|
|
340
|
+
query: "",
|
|
341
|
+
renaming: null,
|
|
342
|
+
clipboard: null,
|
|
343
|
+
cacheTick: 0
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
chunkPK6SKIKE_cjs.__name(createInitialState, "createInitialState");
|
|
347
|
+
|
|
348
|
+
// src/tools/data/Tree/context/async-children/collect-ids.ts
|
|
349
|
+
function collectAllFolderIds(roots, cache, out) {
|
|
292
350
|
for (const node of roots) {
|
|
293
351
|
if (Array.isArray(node.children)) {
|
|
294
352
|
out.push(node.id);
|
|
295
|
-
|
|
353
|
+
collectAllFolderIds(node.children, cache, out);
|
|
296
354
|
} else if (node.isFolder) {
|
|
297
355
|
out.push(node.id);
|
|
298
356
|
const entry = cache.get(node.id);
|
|
299
|
-
if (entry?.children)
|
|
357
|
+
if (entry?.children) collectAllFolderIds(entry.children, cache, out);
|
|
300
358
|
}
|
|
301
359
|
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
appearance,
|
|
314
|
-
onSelectionChange,
|
|
315
|
-
onExpansionChange,
|
|
316
|
-
onActivate,
|
|
317
|
-
filterNode,
|
|
318
|
-
enableSearch = false,
|
|
319
|
-
showIndentGuides = false,
|
|
320
|
-
renderIcon,
|
|
321
|
-
renderLabel,
|
|
322
|
-
renderActions,
|
|
323
|
-
renderContextMenu,
|
|
324
|
-
labels: labelsOverride,
|
|
325
|
-
persistKey,
|
|
326
|
-
persistSelection = false,
|
|
327
|
-
children
|
|
328
|
-
} = props;
|
|
329
|
-
const labels = React.useMemo(
|
|
330
|
-
() => ({ ...DEFAULT_TREE_LABELS, ...labelsOverride }),
|
|
331
|
-
[labelsOverride]
|
|
332
|
-
);
|
|
333
|
-
const resolvedAppearance = React.useMemo(
|
|
334
|
-
() => resolveAppearance(appearance, indent),
|
|
335
|
-
[appearance, indent]
|
|
336
|
-
);
|
|
337
|
-
const persisted = React.useMemo(
|
|
338
|
-
() => persistKey ? loadTreeState(persistKey) : null,
|
|
339
|
-
[persistKey]
|
|
340
|
-
);
|
|
341
|
-
const [state, dispatch] = React.useReducer(reducer, void 0, () => ({
|
|
342
|
-
expanded: new Set(persisted?.expandedItems ?? initialExpandedIds ?? []),
|
|
343
|
-
selected: new Set(
|
|
344
|
-
(persistSelection ? persisted?.selectedItems : void 0) ?? initialSelectedIds ?? []
|
|
345
|
-
),
|
|
346
|
-
focused: null,
|
|
347
|
-
query: "",
|
|
348
|
-
cacheTick: 0
|
|
349
|
-
}));
|
|
360
|
+
}
|
|
361
|
+
chunkPK6SKIKE_cjs.__name(collectAllFolderIds, "collectAllFolderIds");
|
|
362
|
+
|
|
363
|
+
// src/tools/data/Tree/context/async-children/use-async-children.ts
|
|
364
|
+
function useAsyncChildren({
|
|
365
|
+
data,
|
|
366
|
+
loadChildren,
|
|
367
|
+
expanded,
|
|
368
|
+
cacheTick,
|
|
369
|
+
bumpCacheTick
|
|
370
|
+
}) {
|
|
350
371
|
const cacheRef = React.useRef(createChildCache());
|
|
351
372
|
const inflightRef = React.useRef(/* @__PURE__ */ new Map());
|
|
352
373
|
const fetchChildren = React.useCallback(
|
|
@@ -358,11 +379,11 @@ function TreeProvider(props) {
|
|
|
358
379
|
const inflight = inflightRef.current.get(node.id);
|
|
359
380
|
if (inflight) return inflight;
|
|
360
381
|
cacheRef.current.set(node.id, { status: "loading", children: [] });
|
|
361
|
-
|
|
382
|
+
bumpCacheTick();
|
|
362
383
|
const promise = (async () => {
|
|
363
384
|
try {
|
|
364
|
-
const
|
|
365
|
-
cacheRef.current.set(node.id, { status: "loaded", children
|
|
385
|
+
const children = await loadChildren(node);
|
|
386
|
+
cacheRef.current.set(node.id, { status: "loaded", children });
|
|
366
387
|
} catch (err) {
|
|
367
388
|
cacheRef.current.set(node.id, {
|
|
368
389
|
status: "error",
|
|
@@ -371,13 +392,13 @@ function TreeProvider(props) {
|
|
|
371
392
|
});
|
|
372
393
|
} finally {
|
|
373
394
|
inflightRef.current.delete(node.id);
|
|
374
|
-
|
|
395
|
+
bumpCacheTick();
|
|
375
396
|
}
|
|
376
397
|
})();
|
|
377
398
|
inflightRef.current.set(node.id, promise);
|
|
378
399
|
return promise;
|
|
379
400
|
},
|
|
380
|
-
[loadChildren]
|
|
401
|
+
[loadChildren, bumpCacheTick]
|
|
381
402
|
);
|
|
382
403
|
const nodeById = React.useMemo(() => {
|
|
383
404
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -393,141 +414,1111 @@ function TreeProvider(props) {
|
|
|
393
414
|
}, "walk");
|
|
394
415
|
walk(data);
|
|
395
416
|
return map;
|
|
396
|
-
}, [data,
|
|
417
|
+
}, [data, cacheTick]);
|
|
397
418
|
React.useEffect(() => {
|
|
398
419
|
if (!loadChildren) return;
|
|
399
|
-
for (const id of
|
|
420
|
+
for (const id of expanded) {
|
|
400
421
|
const node = nodeById.get(id);
|
|
401
422
|
if (!node) continue;
|
|
402
423
|
void fetchChildren(node);
|
|
403
424
|
}
|
|
404
|
-
}, [loadChildren,
|
|
405
|
-
const
|
|
406
|
-
() =>
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
425
|
+
}, [loadChildren, expanded, cacheTick, nodeById, fetchChildren]);
|
|
426
|
+
const refresh = React.useCallback(
|
|
427
|
+
async (id) => {
|
|
428
|
+
const node = nodeById.get(id);
|
|
429
|
+
if (!node || !loadChildren) return;
|
|
430
|
+
cacheRef.current.delete(id);
|
|
431
|
+
bumpCacheTick();
|
|
432
|
+
await fetchChildren(node);
|
|
433
|
+
},
|
|
434
|
+
[nodeById, loadChildren, fetchChildren, bumpCacheTick]
|
|
413
435
|
);
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
436
|
+
const refreshAll = React.useCallback(async () => {
|
|
437
|
+
cacheRef.current.clear();
|
|
438
|
+
bumpCacheTick();
|
|
439
|
+
if (!loadChildren) return;
|
|
440
|
+
await Promise.all(
|
|
441
|
+
[...expanded].map((id) => {
|
|
442
|
+
const node = nodeById.get(id);
|
|
443
|
+
return node ? fetchChildren(node) : void 0;
|
|
444
|
+
})
|
|
445
|
+
);
|
|
446
|
+
}, [loadChildren, expanded, nodeById, fetchChildren, bumpCacheTick]);
|
|
447
|
+
const collectFolderIds = React.useCallback(() => {
|
|
448
|
+
const ids = [];
|
|
449
|
+
collectAllFolderIds(data, cacheRef.current, ids);
|
|
450
|
+
return ids;
|
|
451
|
+
}, [data]);
|
|
452
|
+
return {
|
|
453
|
+
cache: cacheRef.current,
|
|
454
|
+
nodeById,
|
|
455
|
+
fetchChildren,
|
|
456
|
+
refresh,
|
|
457
|
+
refreshAll,
|
|
458
|
+
collectFolderIds
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
chunkPK6SKIKE_cjs.__name(useAsyncChildren, "useAsyncChildren");
|
|
462
|
+
function useExpansion({
|
|
463
|
+
dispatch,
|
|
464
|
+
collectFolderIds
|
|
465
|
+
}) {
|
|
466
|
+
const expand = React.useCallback(
|
|
467
|
+
(id) => dispatch({ type: "expand", id }),
|
|
468
|
+
[dispatch]
|
|
469
|
+
);
|
|
470
|
+
const collapse = React.useCallback(
|
|
471
|
+
(id) => dispatch({ type: "collapse", id }),
|
|
472
|
+
[dispatch]
|
|
473
|
+
);
|
|
474
|
+
const toggle = React.useCallback(
|
|
475
|
+
(id) => dispatch({ type: "toggle", id }),
|
|
476
|
+
[dispatch]
|
|
477
|
+
);
|
|
478
|
+
const expandAll = React.useCallback(() => {
|
|
479
|
+
dispatch({ type: "set-expanded", ids: collectFolderIds() });
|
|
480
|
+
}, [dispatch, collectFolderIds]);
|
|
481
|
+
const collapseAll = React.useCallback(
|
|
482
|
+
() => dispatch({ type: "set-expanded", ids: [] }),
|
|
483
|
+
[dispatch]
|
|
484
|
+
);
|
|
485
|
+
return { expand, collapse, toggle, expandAll, collapseAll };
|
|
486
|
+
}
|
|
487
|
+
chunkPK6SKIKE_cjs.__name(useExpansion, "useExpansion");
|
|
488
|
+
|
|
489
|
+
// src/tools/data/Tree/data/selection.ts
|
|
490
|
+
function indexOf(rows, id) {
|
|
491
|
+
if (id == null) return -1;
|
|
492
|
+
for (let i = 0; i < rows.length; i++) {
|
|
493
|
+
if (rows[i].node.id === id) return i;
|
|
494
|
+
}
|
|
495
|
+
return -1;
|
|
496
|
+
}
|
|
497
|
+
chunkPK6SKIKE_cjs.__name(indexOf, "indexOf");
|
|
498
|
+
function computeRange(rows, fromId, toId) {
|
|
499
|
+
if (fromId == null || toId == null) return [];
|
|
500
|
+
const a = indexOf(rows, fromId);
|
|
501
|
+
const b = indexOf(rows, toId);
|
|
502
|
+
if (a < 0 || b < 0) return [];
|
|
503
|
+
const [lo, hi] = a <= b ? [a, b] : [b, a];
|
|
504
|
+
const out = [];
|
|
505
|
+
for (let i = lo; i <= hi; i++) out.push(rows[i].node.id);
|
|
506
|
+
return out;
|
|
507
|
+
}
|
|
508
|
+
chunkPK6SKIKE_cjs.__name(computeRange, "computeRange");
|
|
509
|
+
function selectionFromClick(state, rows, id, mods, multi) {
|
|
510
|
+
if (!multi) {
|
|
511
|
+
return { selected: /* @__PURE__ */ new Set([id]), anchor: id, focused: id };
|
|
512
|
+
}
|
|
513
|
+
if (mods.shift && mods.meta) {
|
|
514
|
+
const anchor = state.anchor ?? state.focused ?? id;
|
|
515
|
+
const range = computeRange(rows, anchor, id);
|
|
516
|
+
const next = new Set(state.selected);
|
|
517
|
+
for (const r of range) next.add(r);
|
|
518
|
+
return { selected: next, anchor: state.anchor ?? id, focused: id };
|
|
519
|
+
}
|
|
520
|
+
if (mods.shift) {
|
|
521
|
+
const anchor = state.anchor ?? state.focused ?? id;
|
|
522
|
+
const range = computeRange(rows, anchor, id);
|
|
523
|
+
return {
|
|
524
|
+
selected: new Set(range.length > 0 ? range : [id]),
|
|
525
|
+
anchor: state.anchor ?? anchor,
|
|
526
|
+
focused: id
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
if (mods.meta) {
|
|
530
|
+
const next = new Set(state.selected);
|
|
531
|
+
if (next.has(id)) next.delete(id);
|
|
532
|
+
else next.add(id);
|
|
533
|
+
return { selected: next, anchor: id, focused: id };
|
|
534
|
+
}
|
|
535
|
+
return { selected: /* @__PURE__ */ new Set([id]), anchor: id, focused: id };
|
|
536
|
+
}
|
|
537
|
+
chunkPK6SKIKE_cjs.__name(selectionFromClick, "selectionFromClick");
|
|
538
|
+
function selectionFromMove(state, rows, nextFocusedId, extend, multi) {
|
|
539
|
+
if (!multi || !extend) {
|
|
540
|
+
return { selected: /* @__PURE__ */ new Set([nextFocusedId]), anchor: nextFocusedId, focused: nextFocusedId };
|
|
541
|
+
}
|
|
542
|
+
const anchor = state.anchor ?? state.focused ?? nextFocusedId;
|
|
543
|
+
const range = computeRange(rows, anchor, nextFocusedId);
|
|
544
|
+
return {
|
|
545
|
+
selected: new Set(range.length > 0 ? range : [nextFocusedId]),
|
|
546
|
+
anchor,
|
|
547
|
+
focused: nextFocusedId
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
chunkPK6SKIKE_cjs.__name(selectionFromMove, "selectionFromMove");
|
|
551
|
+
function selectionSelectAll(rows, focused) {
|
|
552
|
+
if (rows.length === 0) {
|
|
553
|
+
return { selected: /* @__PURE__ */ new Set(), anchor: null, focused };
|
|
554
|
+
}
|
|
555
|
+
const ids = rows.map((r) => r.node.id);
|
|
556
|
+
return {
|
|
557
|
+
selected: new Set(ids),
|
|
558
|
+
anchor: ids[0],
|
|
559
|
+
focused: focused ?? ids[0]
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
chunkPK6SKIKE_cjs.__name(selectionSelectAll, "selectionSelectAll");
|
|
563
|
+
|
|
564
|
+
// src/tools/data/Tree/context/selection/use-selection.ts
|
|
565
|
+
function useSelection({
|
|
566
|
+
dispatch,
|
|
567
|
+
selectionMode,
|
|
568
|
+
flatRows,
|
|
569
|
+
selected,
|
|
570
|
+
anchor,
|
|
571
|
+
focused
|
|
572
|
+
}) {
|
|
573
|
+
const flatRowsRef = React.useRef(flatRows);
|
|
574
|
+
flatRowsRef.current = flatRows;
|
|
575
|
+
const selectionRef = React.useRef({ selected, anchor, focused });
|
|
576
|
+
selectionRef.current = { selected, anchor, focused };
|
|
577
|
+
const select = React.useCallback(
|
|
578
|
+
(id) => dispatch({ type: "select", id, mode: selectionMode }),
|
|
579
|
+
[dispatch, selectionMode]
|
|
580
|
+
);
|
|
581
|
+
const setSelectedIds = React.useCallback(
|
|
582
|
+
(ids) => dispatch({ type: "select-many", ids }),
|
|
583
|
+
[dispatch]
|
|
584
|
+
);
|
|
585
|
+
const clearSelection = React.useCallback(
|
|
586
|
+
() => dispatch({ type: "clear-selection" }),
|
|
587
|
+
[dispatch]
|
|
588
|
+
);
|
|
589
|
+
const setFocus = React.useCallback(
|
|
590
|
+
(id) => dispatch({ type: "focus", id }),
|
|
591
|
+
[dispatch]
|
|
592
|
+
);
|
|
593
|
+
const clickSelect = React.useCallback(
|
|
594
|
+
(id, mods) => {
|
|
595
|
+
if (selectionMode === "none") return;
|
|
596
|
+
if (selectionMode === "single") {
|
|
597
|
+
dispatch({ type: "select", id, mode: "single" });
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
const next = selectionFromClick(
|
|
601
|
+
selectionRef.current,
|
|
602
|
+
flatRowsRef.current,
|
|
603
|
+
id,
|
|
604
|
+
mods,
|
|
605
|
+
true
|
|
606
|
+
);
|
|
607
|
+
dispatch({
|
|
608
|
+
type: "selection-replace",
|
|
609
|
+
selected: [...next.selected],
|
|
610
|
+
anchor: next.anchor,
|
|
611
|
+
focused: next.focused
|
|
612
|
+
});
|
|
613
|
+
},
|
|
614
|
+
[dispatch, selectionMode]
|
|
615
|
+
);
|
|
616
|
+
const moveSelect = React.useCallback(
|
|
617
|
+
(id, opts) => {
|
|
618
|
+
if (selectionMode !== "multiple" || !opts.extend) {
|
|
619
|
+
dispatch({ type: "focus", id });
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
const next = selectionFromMove(
|
|
623
|
+
selectionRef.current,
|
|
624
|
+
flatRowsRef.current,
|
|
625
|
+
id,
|
|
626
|
+
true,
|
|
627
|
+
true
|
|
628
|
+
);
|
|
629
|
+
dispatch({
|
|
630
|
+
type: "selection-replace",
|
|
631
|
+
selected: [...next.selected],
|
|
632
|
+
anchor: next.anchor,
|
|
633
|
+
focused: next.focused
|
|
634
|
+
});
|
|
635
|
+
},
|
|
636
|
+
[dispatch, selectionMode]
|
|
637
|
+
);
|
|
638
|
+
const selectAll = React.useCallback(() => {
|
|
639
|
+
if (selectionMode !== "multiple") return;
|
|
640
|
+
const next = selectionSelectAll(
|
|
641
|
+
flatRowsRef.current,
|
|
642
|
+
selectionRef.current.focused
|
|
643
|
+
);
|
|
644
|
+
dispatch({
|
|
645
|
+
type: "selection-replace",
|
|
646
|
+
selected: [...next.selected],
|
|
647
|
+
anchor: next.anchor,
|
|
648
|
+
focused: next.focused
|
|
649
|
+
});
|
|
650
|
+
}, [dispatch, selectionMode]);
|
|
651
|
+
return {
|
|
652
|
+
select,
|
|
653
|
+
setSelectedIds,
|
|
654
|
+
clearSelection,
|
|
655
|
+
clickSelect,
|
|
656
|
+
moveSelect,
|
|
657
|
+
selectAll,
|
|
658
|
+
setFocus
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
chunkPK6SKIKE_cjs.__name(useSelection, "useSelection");
|
|
662
|
+
function useRename({
|
|
663
|
+
dispatch,
|
|
664
|
+
adapter,
|
|
665
|
+
enableInlineRename,
|
|
666
|
+
nodeById,
|
|
667
|
+
getItemName,
|
|
668
|
+
labels
|
|
669
|
+
}) {
|
|
670
|
+
const enabled = enableInlineRename && !!adapter?.rename;
|
|
671
|
+
const startRename = React.useCallback(
|
|
672
|
+
(id) => {
|
|
673
|
+
if (!enableInlineRename) return;
|
|
674
|
+
if (!adapter?.rename) {
|
|
675
|
+
if (process.env.NODE_ENV !== "production") {
|
|
676
|
+
console.warn(
|
|
677
|
+
"[Tree] startRename called but adapter.rename is not defined."
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
return;
|
|
421
681
|
}
|
|
682
|
+
dispatch({ type: "start-rename", id });
|
|
683
|
+
},
|
|
684
|
+
[dispatch, enableInlineRename, adapter]
|
|
685
|
+
);
|
|
686
|
+
const cancelRename = React.useCallback(
|
|
687
|
+
() => dispatch({ type: "stop-rename" }),
|
|
688
|
+
[dispatch]
|
|
689
|
+
);
|
|
690
|
+
const commitRename = React.useCallback(
|
|
691
|
+
async (id, nextName) => {
|
|
692
|
+
if (!adapter?.rename) {
|
|
693
|
+
dispatch({ type: "stop-rename" });
|
|
694
|
+
return false;
|
|
695
|
+
}
|
|
696
|
+
const node = nodeById.get(id);
|
|
697
|
+
if (!node) {
|
|
698
|
+
dispatch({ type: "stop-rename" });
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
const trimmed = nextName.trim();
|
|
702
|
+
if (trimmed === getItemName(node)) {
|
|
703
|
+
dispatch({ type: "stop-rename" });
|
|
704
|
+
return true;
|
|
705
|
+
}
|
|
706
|
+
let err = null;
|
|
707
|
+
if (trimmed === "") err = labels.invalidNameEmpty;
|
|
708
|
+
else err = adapter.validateName?.(trimmed, { node }) ?? null;
|
|
709
|
+
if (err) {
|
|
710
|
+
const dialog = dialogService.getDialog();
|
|
711
|
+
await dialog?.alert({ title: labels.error, message: err });
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
try {
|
|
715
|
+
await adapter.rename(node, trimmed);
|
|
716
|
+
} catch (e) {
|
|
717
|
+
const dialog = dialogService.getDialog();
|
|
718
|
+
await dialog?.alert({
|
|
719
|
+
title: labels.error,
|
|
720
|
+
message: e instanceof Error ? e.message : String(e)
|
|
721
|
+
});
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
724
|
+
dispatch({ type: "stop-rename" });
|
|
725
|
+
return true;
|
|
726
|
+
},
|
|
727
|
+
[dispatch, adapter, nodeById, getItemName, labels]
|
|
728
|
+
);
|
|
729
|
+
return { enabled, startRename, cancelRename, commitRename };
|
|
730
|
+
}
|
|
731
|
+
chunkPK6SKIKE_cjs.__name(useRename, "useRename");
|
|
732
|
+
function useClipboard({
|
|
733
|
+
dispatch,
|
|
734
|
+
clipboard,
|
|
735
|
+
adapter,
|
|
736
|
+
nodeById,
|
|
737
|
+
labels
|
|
738
|
+
}) {
|
|
739
|
+
const clipboardRef = React.useRef(clipboard);
|
|
740
|
+
clipboardRef.current = clipboard;
|
|
741
|
+
const cutToClipboard = React.useCallback(
|
|
742
|
+
(ids) => {
|
|
743
|
+
if (ids.length === 0) return;
|
|
744
|
+
dispatch({ type: "clipboard-set", payload: { kind: "cut", ids } });
|
|
745
|
+
},
|
|
746
|
+
[dispatch]
|
|
747
|
+
);
|
|
748
|
+
const copyToClipboard = React.useCallback(
|
|
749
|
+
(ids) => {
|
|
750
|
+
if (ids.length === 0) return;
|
|
751
|
+
dispatch({ type: "clipboard-set", payload: { kind: "copy", ids } });
|
|
752
|
+
},
|
|
753
|
+
[dispatch]
|
|
754
|
+
);
|
|
755
|
+
const clearClipboard = React.useCallback(
|
|
756
|
+
() => dispatch({ type: "clipboard-set", payload: null }),
|
|
757
|
+
[dispatch]
|
|
758
|
+
);
|
|
759
|
+
const pasteFromClipboard = React.useCallback(
|
|
760
|
+
async (target, position = "inside") => {
|
|
761
|
+
const cb = clipboardRef.current;
|
|
762
|
+
if (!cb || cb.ids.length === 0) return;
|
|
763
|
+
if (!adapter) return;
|
|
764
|
+
const nodes = cb.ids.map((id) => nodeById.get(id)).filter((n) => !!n);
|
|
765
|
+
if (nodes.length === 0) {
|
|
766
|
+
dispatch({ type: "clipboard-set", payload: null });
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
try {
|
|
770
|
+
if (cb.kind === "cut") {
|
|
771
|
+
if (!adapter.move) return;
|
|
772
|
+
await adapter.move(nodes, target, position);
|
|
773
|
+
dispatch({ type: "clipboard-set", payload: null });
|
|
774
|
+
} else {
|
|
775
|
+
if (!adapter.copy) return;
|
|
776
|
+
await adapter.copy(nodes, target, position);
|
|
777
|
+
}
|
|
778
|
+
} catch (e) {
|
|
779
|
+
const dialog = dialogService.getDialog();
|
|
780
|
+
await dialog?.alert({
|
|
781
|
+
title: labels.error,
|
|
782
|
+
message: e instanceof Error ? e.message : String(e)
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
},
|
|
786
|
+
[dispatch, adapter, nodeById, labels]
|
|
787
|
+
);
|
|
788
|
+
return {
|
|
789
|
+
cutToClipboard,
|
|
790
|
+
copyToClipboard,
|
|
791
|
+
pasteFromClipboard,
|
|
792
|
+
clearClipboard
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
chunkPK6SKIKE_cjs.__name(useClipboard, "useClipboard");
|
|
796
|
+
async function confirmDelete(ctx) {
|
|
797
|
+
const dialog = dialogService.getDialog();
|
|
798
|
+
if (!dialog) return false;
|
|
799
|
+
const { selectedNodes, labels, getName } = ctx;
|
|
800
|
+
return dialog.confirm({
|
|
801
|
+
title: labels.confirmDeleteTitle(selectedNodes.length),
|
|
802
|
+
message: labels.confirmDeleteMessage(selectedNodes.map(getName)),
|
|
803
|
+
confirmText: labels.confirmDeleteOk,
|
|
804
|
+
cancelText: labels.confirmDeleteCancel,
|
|
805
|
+
variant: "destructive"
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
chunkPK6SKIKE_cjs.__name(confirmDelete, "confirmDelete");
|
|
809
|
+
async function promptName(ctx, spec) {
|
|
810
|
+
const dialog = dialogService.getDialog();
|
|
811
|
+
if (!dialog) return null;
|
|
812
|
+
return dialog.prompt({
|
|
813
|
+
title: spec.title,
|
|
814
|
+
message: spec.message,
|
|
815
|
+
placeholder: spec.placeholder,
|
|
816
|
+
defaultValue: spec.defaultValue
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
chunkPK6SKIKE_cjs.__name(promptName, "promptName");
|
|
820
|
+
async function alertError(ctx, message) {
|
|
821
|
+
const dialog = dialogService.getDialog();
|
|
822
|
+
if (!dialog) return;
|
|
823
|
+
await dialog.alert({ message, title: ctx.labels.error });
|
|
824
|
+
}
|
|
825
|
+
chunkPK6SKIKE_cjs.__name(alertError, "alertError");
|
|
826
|
+
function validateName(ctx, name, validateCtx) {
|
|
827
|
+
if (name.trim() === "") return ctx.labels.invalidNameEmpty;
|
|
828
|
+
return ctx.adapter.validateName?.(name, validateCtx) ?? null;
|
|
829
|
+
}
|
|
830
|
+
chunkPK6SKIKE_cjs.__name(validateName, "validateName");
|
|
831
|
+
var BUILTIN_ACTIONS = [
|
|
832
|
+
{
|
|
833
|
+
id: "open",
|
|
834
|
+
label: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => ctx.labels.actionOpen, "label"),
|
|
835
|
+
icon: lucideReact.CornerUpLeft,
|
|
836
|
+
available: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => false, "available"),
|
|
837
|
+
// wired by Tree on activate; not in menu by default
|
|
838
|
+
run: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => {
|
|
839
|
+
}, "run")
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
id: "rename",
|
|
843
|
+
label: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => ctx.labels.actionRename, "label"),
|
|
844
|
+
icon: lucideReact.Pencil,
|
|
845
|
+
shortcut: "F2",
|
|
846
|
+
available: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => !!ctx.adapter.rename && ctx.selectedNodes.length === 1 && !ctx.selectedNodes[0].disabled, "available"),
|
|
847
|
+
run: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(async (ctx) => {
|
|
848
|
+
const node = ctx.selectedNodes[0];
|
|
849
|
+
if (ctx.startInlineRename) {
|
|
850
|
+
ctx.startInlineRename(node.id);
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
const name = await promptName(ctx, {
|
|
854
|
+
title: ctx.labels.renameTitle,
|
|
855
|
+
message: ctx.labels.renameMessage,
|
|
856
|
+
placeholder: ctx.getName(node),
|
|
857
|
+
defaultValue: ctx.getName(node)
|
|
858
|
+
});
|
|
859
|
+
if (name === null) return;
|
|
860
|
+
const err = validateName(ctx, name, { node });
|
|
861
|
+
if (err) return alertError(ctx, err);
|
|
862
|
+
await ctx.adapter.rename(node, name);
|
|
863
|
+
}, "run")
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
id: "duplicate",
|
|
867
|
+
label: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => ctx.labels.actionDuplicate, "label"),
|
|
868
|
+
icon: lucideReact.Copy,
|
|
869
|
+
shortcut: "\u2318D",
|
|
870
|
+
available: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => !!ctx.adapter.duplicate && ctx.selectedNodes.length > 0, "available"),
|
|
871
|
+
run: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => ctx.adapter.duplicate(ctx.selectedNodes), "run")
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
id: "cut",
|
|
875
|
+
label: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => ctx.labels.actionCut, "label"),
|
|
876
|
+
icon: lucideReact.Scissors,
|
|
877
|
+
shortcut: "\u2318X",
|
|
878
|
+
// Only meaningful when the adapter supports `move` (paste-after-cut)
|
|
879
|
+
// AND Tree provided a clipboard binding.
|
|
880
|
+
available: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => !!ctx.adapter.move && !!ctx.clipboard && ctx.selectedNodes.length > 0, "available"),
|
|
881
|
+
run: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => {
|
|
882
|
+
ctx.clipboard?.cut(ctx.selectedNodes.map((n) => n.id));
|
|
883
|
+
}, "run")
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
id: "copy",
|
|
887
|
+
label: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => ctx.labels.actionCopy, "label"),
|
|
888
|
+
icon: lucideReact.Copy,
|
|
889
|
+
shortcut: "\u2318C",
|
|
890
|
+
available: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => !!ctx.adapter.copy && !!ctx.clipboard && ctx.selectedNodes.length > 0, "available"),
|
|
891
|
+
run: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => {
|
|
892
|
+
ctx.clipboard?.copy(ctx.selectedNodes.map((n) => n.id));
|
|
893
|
+
}, "run")
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
id: "paste",
|
|
897
|
+
label: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => ctx.labels.actionPaste, "label"),
|
|
898
|
+
icon: lucideReact.CornerUpLeft,
|
|
899
|
+
shortcut: "\u2318V",
|
|
900
|
+
available: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => !!ctx.clipboard?.hasItems, "available"),
|
|
901
|
+
run: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(async (ctx) => {
|
|
902
|
+
await ctx.clipboard?.paste();
|
|
903
|
+
}, "run")
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
id: "delete",
|
|
907
|
+
label: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => ctx.labels.actionDelete, "label"),
|
|
908
|
+
icon: lucideReact.Trash2,
|
|
909
|
+
shortcut: "\u2318\u232B",
|
|
910
|
+
destructive: true,
|
|
911
|
+
available: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => !!ctx.adapter.remove && ctx.selectedNodes.length > 0, "available"),
|
|
912
|
+
run: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(async (ctx) => {
|
|
913
|
+
const ok = await confirmDelete(ctx);
|
|
914
|
+
if (!ok) return;
|
|
915
|
+
await ctx.adapter.remove(ctx.selectedNodes);
|
|
916
|
+
}, "run")
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
id: "new-file",
|
|
920
|
+
label: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => ctx.labels.actionNewFile, "label"),
|
|
921
|
+
icon: lucideReact.FilePlus,
|
|
922
|
+
available: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => !!ctx.adapter.createFile, "available"),
|
|
923
|
+
run: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(async (ctx) => {
|
|
924
|
+
const parent = resolveParentForCreate(ctx);
|
|
925
|
+
const name = await promptName(ctx, {
|
|
926
|
+
title: ctx.labels.newFileTitle,
|
|
927
|
+
message: ctx.labels.newFileMessage,
|
|
928
|
+
placeholder: ctx.labels.newFilePlaceholder,
|
|
929
|
+
defaultValue: ctx.labels.newFileDefault
|
|
930
|
+
});
|
|
931
|
+
if (name === null) return;
|
|
932
|
+
const err = validateName(ctx, name, { parent });
|
|
933
|
+
if (err) return alertError(ctx, err);
|
|
934
|
+
await ctx.adapter.createFile(parent, name);
|
|
935
|
+
}, "run")
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
id: "new-folder",
|
|
939
|
+
label: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => ctx.labels.actionNewFolder, "label"),
|
|
940
|
+
icon: lucideReact.FolderPlus,
|
|
941
|
+
shortcut: "\u2318\u21E7N",
|
|
942
|
+
available: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((ctx) => !!ctx.adapter.createFolder, "available"),
|
|
943
|
+
run: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(async (ctx) => {
|
|
944
|
+
const parent = resolveParentForCreate(ctx);
|
|
945
|
+
const name = await promptName(ctx, {
|
|
946
|
+
title: ctx.labels.newFolderTitle,
|
|
947
|
+
message: ctx.labels.newFolderMessage,
|
|
948
|
+
placeholder: ctx.labels.newFolderPlaceholder,
|
|
949
|
+
defaultValue: ctx.labels.newFolderDefault
|
|
950
|
+
});
|
|
951
|
+
if (name === null) return;
|
|
952
|
+
const err = validateName(ctx, name, { parent });
|
|
953
|
+
if (err) return alertError(ctx, err);
|
|
954
|
+
await ctx.adapter.createFolder(parent, name);
|
|
955
|
+
}, "run")
|
|
956
|
+
}
|
|
957
|
+
];
|
|
958
|
+
var DEFAULT_BUILTIN_MENU_ORDER = [
|
|
959
|
+
"rename",
|
|
960
|
+
"duplicate",
|
|
961
|
+
"separator",
|
|
962
|
+
"cut",
|
|
963
|
+
"copy",
|
|
964
|
+
"paste",
|
|
965
|
+
"separator",
|
|
966
|
+
"new-file",
|
|
967
|
+
"new-folder",
|
|
968
|
+
"separator",
|
|
969
|
+
"delete"
|
|
970
|
+
];
|
|
971
|
+
function resolveParentForCreate(ctx) {
|
|
972
|
+
const { targetNode } = ctx;
|
|
973
|
+
if (!targetNode) return null;
|
|
974
|
+
const isFolder = Array.isArray(targetNode.children) || !!targetNode.isFolder;
|
|
975
|
+
return isFolder ? targetNode : null;
|
|
976
|
+
}
|
|
977
|
+
chunkPK6SKIKE_cjs.__name(resolveParentForCreate, "resolveParentForCreate");
|
|
978
|
+
function buildDefaultMenuItems(ctx, order = DEFAULT_BUILTIN_MENU_ORDER) {
|
|
979
|
+
const items = [];
|
|
980
|
+
let pendingSeparator = false;
|
|
981
|
+
for (const entry of order) {
|
|
982
|
+
if (entry === "separator") {
|
|
983
|
+
pendingSeparator = items.length > 0;
|
|
984
|
+
continue;
|
|
422
985
|
}
|
|
423
|
-
|
|
424
|
-
|
|
986
|
+
const desc = BUILTIN_ACTIONS.find((a) => a.id === entry);
|
|
987
|
+
if (!desc) continue;
|
|
988
|
+
if (!desc.available(ctx)) continue;
|
|
989
|
+
if (pendingSeparator) {
|
|
990
|
+
items.push("separator");
|
|
991
|
+
pendingSeparator = false;
|
|
992
|
+
}
|
|
993
|
+
items.push({
|
|
994
|
+
id: desc.id,
|
|
995
|
+
label: desc.label(ctx),
|
|
996
|
+
icon: desc.icon,
|
|
997
|
+
shortcut: desc.shortcut,
|
|
998
|
+
destructive: desc.destructive,
|
|
999
|
+
onSelect: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => void desc.run(ctx), "onSelect")
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
return items.length > 0 ? items : null;
|
|
1003
|
+
}
|
|
1004
|
+
chunkPK6SKIKE_cjs.__name(buildDefaultMenuItems, "buildDefaultMenuItems");
|
|
1005
|
+
async function runBuiltinAction(id, ctx) {
|
|
1006
|
+
const desc = BUILTIN_ACTIONS.find((a) => a.id === id);
|
|
1007
|
+
if (!desc) return false;
|
|
1008
|
+
if (!desc.available(ctx)) return false;
|
|
1009
|
+
await desc.run(ctx);
|
|
1010
|
+
return true;
|
|
1011
|
+
}
|
|
1012
|
+
chunkPK6SKIKE_cjs.__name(runBuiltinAction, "runBuiltinAction");
|
|
1013
|
+
function useResolvedMenu(opts) {
|
|
1014
|
+
const {
|
|
1015
|
+
adapter,
|
|
1016
|
+
contextMenuActions,
|
|
1017
|
+
defaultMenuItems,
|
|
1018
|
+
labels,
|
|
1019
|
+
selected,
|
|
1020
|
+
clipboard,
|
|
1021
|
+
nodeById,
|
|
1022
|
+
getItemName,
|
|
1023
|
+
enableInlineRename,
|
|
1024
|
+
startRename,
|
|
1025
|
+
cutToClipboard,
|
|
1026
|
+
copyToClipboard,
|
|
1027
|
+
pasteFromClipboard
|
|
1028
|
+
} = opts;
|
|
1029
|
+
return React.useMemo(() => {
|
|
1030
|
+
if (!adapter && !contextMenuActions) return void 0;
|
|
1031
|
+
return (rowProps) => {
|
|
1032
|
+
const selectedIds = selected.has(rowProps.node.id) ? [...selected] : [rowProps.node.id];
|
|
1033
|
+
const selectedNodes = selectedIds.map((id) => nodeById.get(id)).filter((n) => !!n);
|
|
1034
|
+
const builtin = adapter ? buildDefaultMenuItems(
|
|
1035
|
+
{
|
|
1036
|
+
adapter,
|
|
1037
|
+
labels,
|
|
1038
|
+
selectedNodes,
|
|
1039
|
+
targetNode: rowProps.node,
|
|
1040
|
+
getName: getItemName,
|
|
1041
|
+
startInlineRename: enableInlineRename && adapter.rename ? startRename : void 0,
|
|
1042
|
+
clipboard: {
|
|
1043
|
+
hasItems: !!clipboard && clipboard.ids.length > 0,
|
|
1044
|
+
cut: cutToClipboard,
|
|
1045
|
+
copy: copyToClipboard,
|
|
1046
|
+
paste: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => pasteFromClipboard(rowProps.node, "inside"), "paste")
|
|
1047
|
+
}
|
|
1048
|
+
},
|
|
1049
|
+
defaultMenuItems ? defaultMenuItems : void 0
|
|
1050
|
+
) : null;
|
|
1051
|
+
const user = contextMenuActions?.({ ...rowProps, selectedNodes }) ?? null;
|
|
1052
|
+
if (!builtin && !user) return null;
|
|
1053
|
+
if (!user) return builtin;
|
|
1054
|
+
if (!builtin) return user;
|
|
1055
|
+
return [...builtin, "separator", ...user];
|
|
1056
|
+
};
|
|
1057
|
+
}, [
|
|
1058
|
+
adapter,
|
|
1059
|
+
contextMenuActions,
|
|
1060
|
+
defaultMenuItems,
|
|
1061
|
+
labels,
|
|
1062
|
+
selected,
|
|
1063
|
+
clipboard,
|
|
1064
|
+
nodeById,
|
|
1065
|
+
getItemName,
|
|
1066
|
+
enableInlineRename,
|
|
1067
|
+
startRename,
|
|
1068
|
+
cutToClipboard,
|
|
1069
|
+
copyToClipboard,
|
|
1070
|
+
pasteFromClipboard
|
|
1071
|
+
]);
|
|
1072
|
+
}
|
|
1073
|
+
chunkPK6SKIKE_cjs.__name(useResolvedMenu, "useResolvedMenu");
|
|
1074
|
+
function renderItemsAsContextMenu(rowProps, items, trigger) {
|
|
1075
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(components.ContextMenu, { children: [
|
|
1076
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuTrigger, { asChild: true, children: trigger }),
|
|
1077
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuContent, { children: items.map((item, idx) => {
|
|
1078
|
+
if (item === "separator") {
|
|
1079
|
+
return /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuSeparator, {}, `sep-${idx}`);
|
|
1080
|
+
}
|
|
1081
|
+
const Icon = item.icon;
|
|
1082
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1083
|
+
components.ContextMenuItem,
|
|
1084
|
+
{
|
|
1085
|
+
disabled: item.disabled,
|
|
1086
|
+
variant: item.destructive ? "destructive" : void 0,
|
|
1087
|
+
onSelect: () => item.onSelect(rowProps),
|
|
1088
|
+
children: [
|
|
1089
|
+
Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) : null,
|
|
1090
|
+
item.label,
|
|
1091
|
+
item.shortcut ? /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuShortcut, { children: item.shortcut }) : null
|
|
1092
|
+
]
|
|
1093
|
+
},
|
|
1094
|
+
item.id
|
|
1095
|
+
);
|
|
1096
|
+
}) })
|
|
1097
|
+
] });
|
|
1098
|
+
}
|
|
1099
|
+
chunkPK6SKIKE_cjs.__name(renderItemsAsContextMenu, "renderItemsAsContextMenu");
|
|
1100
|
+
function tidyMenuItems(items) {
|
|
1101
|
+
const out = [];
|
|
1102
|
+
for (const it of items) {
|
|
1103
|
+
if (it === "separator") {
|
|
1104
|
+
if (out.length === 0) continue;
|
|
1105
|
+
if (out[out.length - 1] === "separator") continue;
|
|
1106
|
+
out.push(it);
|
|
1107
|
+
} else {
|
|
1108
|
+
out.push(it);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
while (out.length > 0 && out[out.length - 1] === "separator") out.pop();
|
|
1112
|
+
return out;
|
|
1113
|
+
}
|
|
1114
|
+
chunkPK6SKIKE_cjs.__name(tidyMenuItems, "tidyMenuItems");
|
|
1115
|
+
|
|
1116
|
+
// src/tools/data/Tree/data/dnd.ts
|
|
1117
|
+
function resolveDropZone(input) {
|
|
1118
|
+
const { pointerY, rowRect, isFolder } = input;
|
|
1119
|
+
const offset = pointerY - rowRect.top;
|
|
1120
|
+
const ratio = rowRect.height > 0 ? offset / rowRect.height : 0.5;
|
|
1121
|
+
if (isFolder) {
|
|
1122
|
+
if (ratio < 0.33) return "before";
|
|
1123
|
+
if (ratio > 0.66) return "after";
|
|
1124
|
+
return "inside";
|
|
1125
|
+
}
|
|
1126
|
+
return ratio < 0.5 ? "before" : "after";
|
|
1127
|
+
}
|
|
1128
|
+
chunkPK6SKIKE_cjs.__name(resolveDropZone, "resolveDropZone");
|
|
1129
|
+
function defaultCanDrop(input) {
|
|
1130
|
+
const { source, target, position } = input;
|
|
1131
|
+
if (source.length === 0) return false;
|
|
1132
|
+
if (!target) return true;
|
|
1133
|
+
if (position === "inside") {
|
|
1134
|
+
const isFolder = Array.isArray(target.children) || !!target.isFolder;
|
|
1135
|
+
if (!isFolder) return false;
|
|
1136
|
+
}
|
|
1137
|
+
for (const node of source) {
|
|
1138
|
+
if (node.id === target.id) return false;
|
|
1139
|
+
if (isDescendant(node, target.id)) return false;
|
|
1140
|
+
}
|
|
1141
|
+
return true;
|
|
1142
|
+
}
|
|
1143
|
+
chunkPK6SKIKE_cjs.__name(defaultCanDrop, "defaultCanDrop");
|
|
1144
|
+
function isDescendant(root, id) {
|
|
1145
|
+
if (!Array.isArray(root.children)) return false;
|
|
1146
|
+
for (const child of root.children) {
|
|
1147
|
+
if (child.id === id) return true;
|
|
1148
|
+
if (isDescendant(child, id)) return true;
|
|
1149
|
+
}
|
|
1150
|
+
return false;
|
|
1151
|
+
}
|
|
1152
|
+
chunkPK6SKIKE_cjs.__name(isDescendant, "isDescendant");
|
|
1153
|
+
var TREE_DND_MIME = "application/x-djangocfg-tree";
|
|
1154
|
+
var TREE_ROOT_DROP_ID = "__tree_root_drop__";
|
|
1155
|
+
|
|
1156
|
+
// src/tools/data/Tree/context/dnd/use-dnd.ts
|
|
1157
|
+
function useDnd({
|
|
1158
|
+
enabled,
|
|
1159
|
+
adapter,
|
|
1160
|
+
nodeById,
|
|
1161
|
+
selected,
|
|
1162
|
+
labels,
|
|
1163
|
+
canDrop
|
|
1164
|
+
}) {
|
|
1165
|
+
const active = enabled && !!adapter?.move;
|
|
1166
|
+
const [draggingIds, setDraggingIds] = React.useState(
|
|
1167
|
+
() => /* @__PURE__ */ new Set()
|
|
1168
|
+
);
|
|
1169
|
+
const [dropTarget, setDropTarget] = React.useState(null);
|
|
1170
|
+
const beginDrag = React.useCallback(
|
|
1171
|
+
(rowId) => {
|
|
1172
|
+
if (!active) return;
|
|
1173
|
+
const ids = selected.has(rowId) ? new Set(selected) : /* @__PURE__ */ new Set([rowId]);
|
|
1174
|
+
setDraggingIds(ids);
|
|
1175
|
+
},
|
|
1176
|
+
[active, selected]
|
|
1177
|
+
);
|
|
1178
|
+
const cancelDrag = React.useCallback(() => {
|
|
1179
|
+
setDraggingIds(/* @__PURE__ */ new Set());
|
|
1180
|
+
setDropTarget(null);
|
|
1181
|
+
}, []);
|
|
1182
|
+
const resolveSourceNodes = React.useCallback(() => {
|
|
1183
|
+
const out = [];
|
|
1184
|
+
for (const id of draggingIds) {
|
|
1185
|
+
const node = nodeById.get(id);
|
|
1186
|
+
if (node) out.push(node);
|
|
1187
|
+
}
|
|
1188
|
+
return out;
|
|
1189
|
+
}, [draggingIds, nodeById]);
|
|
1190
|
+
const isAllowedDrop = React.useCallback(
|
|
1191
|
+
(target, position) => {
|
|
1192
|
+
if (!active) return false;
|
|
1193
|
+
if (draggingIds.size === 0) return false;
|
|
1194
|
+
const source = resolveSourceNodes();
|
|
1195
|
+
if (!defaultCanDrop({
|
|
1196
|
+
source,
|
|
1197
|
+
target,
|
|
1198
|
+
position})) {
|
|
1199
|
+
return false;
|
|
1200
|
+
}
|
|
1201
|
+
return canDrop?.({ source, target, position }) ?? true;
|
|
1202
|
+
},
|
|
1203
|
+
[active, draggingIds, resolveSourceNodes, nodeById, canDrop]
|
|
1204
|
+
);
|
|
1205
|
+
const commitDrop = React.useCallback(async () => {
|
|
1206
|
+
if (!active || !adapter?.move) {
|
|
1207
|
+
cancelDrag();
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
const t = dropTarget;
|
|
1211
|
+
if (!t) {
|
|
1212
|
+
cancelDrag();
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
const targetNode = t.id ? nodeById.get(t.id) ?? null : null;
|
|
1216
|
+
const source = resolveSourceNodes();
|
|
1217
|
+
if (source.length === 0) {
|
|
1218
|
+
cancelDrag();
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
if (!isAllowedDrop(targetNode, t.position)) {
|
|
1222
|
+
cancelDrag();
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
try {
|
|
1226
|
+
await adapter.move(source, targetNode, t.position);
|
|
1227
|
+
} catch (e) {
|
|
1228
|
+
const dialog = dialogService.getDialog();
|
|
1229
|
+
await dialog?.alert({
|
|
1230
|
+
title: labels.error,
|
|
1231
|
+
message: e instanceof Error ? e.message : String(e)
|
|
1232
|
+
});
|
|
1233
|
+
} finally {
|
|
1234
|
+
cancelDrag();
|
|
1235
|
+
}
|
|
1236
|
+
}, [
|
|
1237
|
+
active,
|
|
1238
|
+
adapter,
|
|
1239
|
+
cancelDrag,
|
|
1240
|
+
dropTarget,
|
|
1241
|
+
isAllowedDrop,
|
|
1242
|
+
labels,
|
|
1243
|
+
nodeById,
|
|
1244
|
+
resolveSourceNodes
|
|
1245
|
+
]);
|
|
1246
|
+
return React.useMemo(
|
|
1247
|
+
() => ({
|
|
1248
|
+
active,
|
|
1249
|
+
draggingIds,
|
|
1250
|
+
dropTarget,
|
|
1251
|
+
beginDrag,
|
|
1252
|
+
setDropTarget,
|
|
1253
|
+
commitDrop,
|
|
1254
|
+
cancelDrag,
|
|
1255
|
+
isAllowedDrop
|
|
1256
|
+
}),
|
|
1257
|
+
[
|
|
1258
|
+
active,
|
|
1259
|
+
draggingIds,
|
|
1260
|
+
dropTarget,
|
|
1261
|
+
beginDrag,
|
|
1262
|
+
commitDrop,
|
|
1263
|
+
cancelDrag,
|
|
1264
|
+
isAllowedDrop
|
|
1265
|
+
]
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
chunkPK6SKIKE_cjs.__name(useDnd, "useDnd");
|
|
1269
|
+
function usePersistSync({
|
|
1270
|
+
expanded,
|
|
1271
|
+
selected,
|
|
1272
|
+
persistKey,
|
|
1273
|
+
persistSelection,
|
|
1274
|
+
onSelectionChange,
|
|
1275
|
+
onExpansionChange
|
|
1276
|
+
}) {
|
|
425
1277
|
const onSelectionChangeRef = React.useRef(onSelectionChange);
|
|
426
1278
|
const onExpansionChangeRef = React.useRef(onExpansionChange);
|
|
427
|
-
const onActivateRef = React.useRef(onActivate);
|
|
428
1279
|
onSelectionChangeRef.current = onSelectionChange;
|
|
429
1280
|
onExpansionChangeRef.current = onExpansionChange;
|
|
430
|
-
|
|
431
|
-
const
|
|
432
|
-
const lastExpandedArrRef = React.useRef([...state.expanded]);
|
|
1281
|
+
const lastSelectedArrRef = React.useRef([...selected]);
|
|
1282
|
+
const lastExpandedArrRef = React.useRef([...expanded]);
|
|
433
1283
|
React.useEffect(() => {
|
|
434
|
-
const arr = [...
|
|
435
|
-
if (!setEqualsArr(
|
|
1284
|
+
const arr = [...expanded];
|
|
1285
|
+
if (!setEqualsArr(expanded, lastExpandedArrRef.current)) {
|
|
436
1286
|
lastExpandedArrRef.current = arr;
|
|
437
1287
|
onExpansionChangeRef.current?.(arr);
|
|
438
1288
|
if (persistKey) {
|
|
439
1289
|
saveTreeState(persistKey, {
|
|
440
1290
|
expandedItems: arr,
|
|
441
|
-
selectedItems: persistSelection ? [...
|
|
1291
|
+
selectedItems: persistSelection ? [...selected] : []
|
|
442
1292
|
});
|
|
443
1293
|
}
|
|
444
1294
|
}
|
|
445
|
-
}, [
|
|
1295
|
+
}, [expanded, persistKey, persistSelection, selected]);
|
|
446
1296
|
React.useEffect(() => {
|
|
447
|
-
const arr = [...
|
|
448
|
-
if (!setEqualsArr(
|
|
1297
|
+
const arr = [...selected];
|
|
1298
|
+
if (!setEqualsArr(selected, lastSelectedArrRef.current)) {
|
|
449
1299
|
lastSelectedArrRef.current = arr;
|
|
450
1300
|
onSelectionChangeRef.current?.(arr);
|
|
451
1301
|
if (persistKey && persistSelection) {
|
|
452
1302
|
saveTreeState(persistKey, {
|
|
453
|
-
expandedItems: [...
|
|
1303
|
+
expandedItems: [...expanded],
|
|
454
1304
|
selectedItems: arr
|
|
455
1305
|
});
|
|
456
1306
|
}
|
|
457
1307
|
}
|
|
458
|
-
}, [
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
)
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
1308
|
+
}, [selected, persistKey, persistSelection, expanded]);
|
|
1309
|
+
}
|
|
1310
|
+
chunkPK6SKIKE_cjs.__name(usePersistSync, "usePersistSync");
|
|
1311
|
+
function setEqualsArr(set, arr) {
|
|
1312
|
+
if (set.size !== arr.length) return false;
|
|
1313
|
+
for (const id of arr) if (!set.has(id)) return false;
|
|
1314
|
+
return true;
|
|
1315
|
+
}
|
|
1316
|
+
chunkPK6SKIKE_cjs.__name(setEqualsArr, "setEqualsArr");
|
|
1317
|
+
var TreeContext = React.createContext(null);
|
|
1318
|
+
function useTreeContext() {
|
|
1319
|
+
const ctx = React__namespace.useContext(TreeContext);
|
|
1320
|
+
if (!ctx) {
|
|
1321
|
+
throw new Error("useTreeContext must be used inside <TreeProvider>");
|
|
1322
|
+
}
|
|
1323
|
+
return ctx;
|
|
1324
|
+
}
|
|
1325
|
+
chunkPK6SKIKE_cjs.__name(useTreeContext, "useTreeContext");
|
|
1326
|
+
function TreeProvider(props) {
|
|
1327
|
+
const {
|
|
1328
|
+
data,
|
|
1329
|
+
getItemName,
|
|
1330
|
+
loadChildren,
|
|
1331
|
+
selectionMode = "single",
|
|
1332
|
+
activationMode = "single-click",
|
|
1333
|
+
initialExpandedIds,
|
|
1334
|
+
initialSelectedIds,
|
|
1335
|
+
indent,
|
|
1336
|
+
appearance,
|
|
1337
|
+
onSelectionChange,
|
|
1338
|
+
onExpansionChange,
|
|
1339
|
+
onActivate,
|
|
1340
|
+
filterNode,
|
|
1341
|
+
enableSearch = false,
|
|
1342
|
+
showIndentGuides = false,
|
|
1343
|
+
renderIcon,
|
|
1344
|
+
renderLabel,
|
|
1345
|
+
renderActions,
|
|
1346
|
+
renderContextMenu,
|
|
1347
|
+
contextMenuActions,
|
|
1348
|
+
labels: labelsOverride,
|
|
1349
|
+
persistKey,
|
|
1350
|
+
persistSelection = false,
|
|
1351
|
+
adapter,
|
|
1352
|
+
defaultMenuItems,
|
|
1353
|
+
enableInlineRename = false,
|
|
1354
|
+
enableDnD = false,
|
|
1355
|
+
canDrop,
|
|
1356
|
+
children
|
|
1357
|
+
} = props;
|
|
1358
|
+
const labels = React.useMemo(
|
|
1359
|
+
() => ({ ...DEFAULT_TREE_LABELS, ...labelsOverride }),
|
|
1360
|
+
[labelsOverride]
|
|
1361
|
+
);
|
|
1362
|
+
const resolvedAppearance = React.useMemo(
|
|
1363
|
+
() => resolveAppearance(appearance, indent),
|
|
1364
|
+
[appearance, indent]
|
|
1365
|
+
);
|
|
1366
|
+
const persisted = React.useMemo(
|
|
1367
|
+
() => persistKey ? loadTreeState(persistKey) : null,
|
|
1368
|
+
[persistKey]
|
|
1369
|
+
);
|
|
1370
|
+
const [state, dispatch] = React.useReducer(
|
|
1371
|
+
reducer,
|
|
1372
|
+
void 0,
|
|
1373
|
+
() => createInitialState({
|
|
1374
|
+
persisted,
|
|
1375
|
+
initialExpandedIds,
|
|
1376
|
+
initialSelectedIds,
|
|
1377
|
+
persistSelection
|
|
1378
|
+
})
|
|
1379
|
+
);
|
|
1380
|
+
const bumpCacheTick = React.useCallback(() => dispatch({ type: "cache-tick" }), []);
|
|
1381
|
+
const {
|
|
1382
|
+
nodeById,
|
|
1383
|
+
refresh,
|
|
1384
|
+
refreshAll,
|
|
1385
|
+
collectFolderIds,
|
|
1386
|
+
cache
|
|
1387
|
+
} = useAsyncChildren({
|
|
1388
|
+
data,
|
|
1389
|
+
loadChildren,
|
|
1390
|
+
expanded: state.expanded,
|
|
1391
|
+
cacheTick: state.cacheTick,
|
|
1392
|
+
bumpCacheTick
|
|
1393
|
+
});
|
|
1394
|
+
const flatRows = React.useMemo(
|
|
1395
|
+
() => flattenTree({
|
|
1396
|
+
roots: data,
|
|
1397
|
+
expandedIds: state.expanded,
|
|
1398
|
+
cache,
|
|
1399
|
+
filterNode
|
|
1400
|
+
}),
|
|
1401
|
+
[data, state.expanded, state.cacheTick, cache, filterNode]
|
|
1402
|
+
);
|
|
1403
|
+
const matchingIds = React.useMemo(() => {
|
|
1404
|
+
const set = /* @__PURE__ */ new Set();
|
|
1405
|
+
if (!enableSearch || state.query.trim() === "") return set;
|
|
1406
|
+
const q = state.query.trim().toLowerCase();
|
|
1407
|
+
for (const row of flatRows) {
|
|
1408
|
+
if (getItemName(row.node).toLowerCase().includes(q)) {
|
|
1409
|
+
set.add(row.node.id);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
return set;
|
|
1413
|
+
}, [enableSearch, state.query, flatRows, getItemName]);
|
|
1414
|
+
const expansion = useExpansion({ dispatch, collectFolderIds });
|
|
1415
|
+
const selection = useSelection({
|
|
1416
|
+
dispatch,
|
|
1417
|
+
selectionMode,
|
|
1418
|
+
flatRows,
|
|
1419
|
+
selected: state.selected,
|
|
1420
|
+
anchor: state.anchor,
|
|
1421
|
+
focused: state.focused
|
|
1422
|
+
});
|
|
1423
|
+
const rename = useRename({
|
|
1424
|
+
dispatch,
|
|
1425
|
+
adapter,
|
|
1426
|
+
enableInlineRename,
|
|
1427
|
+
nodeById,
|
|
1428
|
+
getItemName,
|
|
1429
|
+
labels
|
|
1430
|
+
});
|
|
1431
|
+
const clipboard = useClipboard({
|
|
1432
|
+
dispatch,
|
|
1433
|
+
clipboard: state.clipboard,
|
|
1434
|
+
adapter,
|
|
1435
|
+
nodeById,
|
|
1436
|
+
labels
|
|
1437
|
+
});
|
|
1438
|
+
const dnd = useDnd({
|
|
1439
|
+
enabled: enableDnD,
|
|
1440
|
+
adapter,
|
|
1441
|
+
nodeById,
|
|
1442
|
+
selected: state.selected,
|
|
1443
|
+
labels,
|
|
1444
|
+
canDrop
|
|
1445
|
+
});
|
|
1446
|
+
const onActivateRef = React.useRef(onActivate);
|
|
1447
|
+
onActivateRef.current = onActivate;
|
|
1448
|
+
const activate = React.useCallback(
|
|
1449
|
+
(node, opts = { preview: false }) => onActivateRef.current?.(node, opts),
|
|
477
1450
|
[]
|
|
478
1451
|
);
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
(id) => dispatch({ type: "focus", id }),
|
|
1452
|
+
const setQuery = React.useCallback(
|
|
1453
|
+
(q) => dispatch({ type: "set-query", q }),
|
|
482
1454
|
[]
|
|
483
1455
|
);
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
1456
|
+
usePersistSync({
|
|
1457
|
+
expanded: state.expanded,
|
|
1458
|
+
selected: state.selected,
|
|
1459
|
+
persistKey,
|
|
1460
|
+
persistSelection,
|
|
1461
|
+
onSelectionChange,
|
|
1462
|
+
onExpansionChange
|
|
1463
|
+
});
|
|
1464
|
+
const resolvedContextMenuActions = useResolvedMenu({
|
|
1465
|
+
adapter,
|
|
1466
|
+
contextMenuActions,
|
|
1467
|
+
defaultMenuItems,
|
|
1468
|
+
labels,
|
|
1469
|
+
selected: state.selected,
|
|
1470
|
+
clipboard: state.clipboard,
|
|
1471
|
+
nodeById,
|
|
1472
|
+
getItemName,
|
|
1473
|
+
enableInlineRename,
|
|
1474
|
+
startRename: rename.startRename,
|
|
1475
|
+
cutToClipboard: clipboard.cutToClipboard,
|
|
1476
|
+
copyToClipboard: clipboard.copyToClipboard,
|
|
1477
|
+
pasteFromClipboard: clipboard.pasteFromClipboard
|
|
1478
|
+
});
|
|
1479
|
+
const finalRenderContextMenu = React.useMemo(
|
|
1480
|
+
() => {
|
|
1481
|
+
if (renderContextMenu) return renderContextMenu;
|
|
1482
|
+
const resolve = resolvedContextMenuActions;
|
|
1483
|
+
if (!resolve) return void 0;
|
|
1484
|
+
return (rowProps, trigger) => {
|
|
1485
|
+
const items = resolve(rowProps);
|
|
1486
|
+
const cleaned = items ? tidyMenuItems(items) : null;
|
|
1487
|
+
if (!cleaned || cleaned.length === 0) return trigger;
|
|
1488
|
+
return renderItemsAsContextMenu(rowProps, cleaned, trigger);
|
|
1489
|
+
};
|
|
492
1490
|
},
|
|
493
|
-
[
|
|
494
|
-
);
|
|
495
|
-
const refreshAll = React.useCallback(async () => {
|
|
496
|
-
cacheRef.current.clear();
|
|
497
|
-
dispatch({ type: "cache-tick" });
|
|
498
|
-
if (!loadChildren) return;
|
|
499
|
-
await Promise.all(
|
|
500
|
-
[...state.expanded].map((id) => {
|
|
501
|
-
const node = nodeById.get(id);
|
|
502
|
-
return node ? fetchChildren(node) : void 0;
|
|
503
|
-
})
|
|
504
|
-
);
|
|
505
|
-
}, [loadChildren, state.expanded, nodeById, fetchChildren]);
|
|
506
|
-
const activate = React.useCallback(
|
|
507
|
-
(node, opts = { preview: false }) => onActivateRef.current?.(node, opts),
|
|
508
|
-
[]
|
|
1491
|
+
[renderContextMenu, resolvedContextMenuActions]
|
|
509
1492
|
);
|
|
510
1493
|
const value = React.useMemo(
|
|
511
1494
|
() => ({
|
|
1495
|
+
// state
|
|
512
1496
|
expanded: state.expanded,
|
|
513
1497
|
selected: state.selected,
|
|
1498
|
+
anchor: state.anchor,
|
|
514
1499
|
focused: state.focused,
|
|
515
1500
|
query: state.query,
|
|
1501
|
+
renamingId: state.renaming,
|
|
1502
|
+
inlineRenameEnabled: rename.enabled,
|
|
1503
|
+
clipboard: state.clipboard,
|
|
516
1504
|
flatRows,
|
|
517
1505
|
matchingIds,
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
collapseAll,
|
|
523
|
-
select,
|
|
524
|
-
setSelectedIds,
|
|
525
|
-
clearSelection,
|
|
526
|
-
setFocus,
|
|
1506
|
+
// expansion
|
|
1507
|
+
...expansion,
|
|
1508
|
+
// selection (note: `select` is from selection hook; expansion exports no `select`)
|
|
1509
|
+
...selection,
|
|
527
1510
|
setQuery,
|
|
1511
|
+
// clipboard
|
|
1512
|
+
...clipboard,
|
|
1513
|
+
// rename
|
|
1514
|
+
startRename: rename.startRename,
|
|
1515
|
+
cancelRename: rename.cancelRename,
|
|
1516
|
+
commitRename: rename.commitRename,
|
|
1517
|
+
// async
|
|
528
1518
|
refresh,
|
|
529
1519
|
refreshAll,
|
|
530
1520
|
activate,
|
|
1521
|
+
// config
|
|
531
1522
|
labels,
|
|
532
1523
|
appearance: resolvedAppearance,
|
|
533
1524
|
indent: resolvedAppearance.indent,
|
|
@@ -536,28 +1527,34 @@ function TreeProvider(props) {
|
|
|
536
1527
|
enableSearch,
|
|
537
1528
|
showIndentGuides,
|
|
538
1529
|
getItemName,
|
|
1530
|
+
// slots
|
|
539
1531
|
renderIcon,
|
|
540
1532
|
renderLabel,
|
|
541
1533
|
renderActions,
|
|
542
|
-
renderContextMenu
|
|
1534
|
+
renderContextMenu: finalRenderContextMenu,
|
|
1535
|
+
adapter,
|
|
1536
|
+
resolvedContextMenuActions,
|
|
1537
|
+
getNodeById: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((id) => nodeById.get(id), "getNodeById"),
|
|
1538
|
+
dnd
|
|
543
1539
|
}),
|
|
544
1540
|
[
|
|
545
1541
|
state.expanded,
|
|
546
1542
|
state.selected,
|
|
1543
|
+
state.anchor,
|
|
547
1544
|
state.focused,
|
|
548
1545
|
state.query,
|
|
1546
|
+
state.renaming,
|
|
1547
|
+
state.clipboard,
|
|
1548
|
+
rename.enabled,
|
|
1549
|
+
rename.startRename,
|
|
1550
|
+
rename.cancelRename,
|
|
1551
|
+
rename.commitRename,
|
|
549
1552
|
flatRows,
|
|
550
1553
|
matchingIds,
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
toggle,
|
|
554
|
-
expandAll,
|
|
555
|
-
collapseAll,
|
|
556
|
-
select,
|
|
557
|
-
setSelectedIds,
|
|
558
|
-
clearSelection,
|
|
559
|
-
setFocus,
|
|
1554
|
+
expansion,
|
|
1555
|
+
selection,
|
|
560
1556
|
setQuery,
|
|
1557
|
+
clipboard,
|
|
561
1558
|
refresh,
|
|
562
1559
|
refreshAll,
|
|
563
1560
|
activate,
|
|
@@ -571,12 +1568,98 @@ function TreeProvider(props) {
|
|
|
571
1568
|
renderIcon,
|
|
572
1569
|
renderLabel,
|
|
573
1570
|
renderActions,
|
|
574
|
-
|
|
1571
|
+
finalRenderContextMenu,
|
|
1572
|
+
adapter,
|
|
1573
|
+
resolvedContextMenuActions,
|
|
1574
|
+
nodeById,
|
|
1575
|
+
dnd
|
|
575
1576
|
]
|
|
576
1577
|
);
|
|
577
1578
|
return /* @__PURE__ */ jsxRuntime.jsx(TreeContext.Provider, { value, children });
|
|
578
1579
|
}
|
|
579
1580
|
chunkPK6SKIKE_cjs.__name(TreeProvider, "TreeProvider");
|
|
1581
|
+
function TreeDndProvider({ children }) {
|
|
1582
|
+
const ctx = useTreeContext();
|
|
1583
|
+
const { dnd } = ctx;
|
|
1584
|
+
const sensors = core.useSensors(
|
|
1585
|
+
core.useSensor(core.PointerSensor, { activationConstraint: { distance: 4 } }),
|
|
1586
|
+
core.useSensor(core.KeyboardSensor)
|
|
1587
|
+
);
|
|
1588
|
+
const cursorYRef = React.useRef(0);
|
|
1589
|
+
const handleDragStart = React.useCallback(
|
|
1590
|
+
(e) => {
|
|
1591
|
+
dnd.beginDrag(e.active.id);
|
|
1592
|
+
},
|
|
1593
|
+
[dnd]
|
|
1594
|
+
);
|
|
1595
|
+
const handleDragMove = React.useCallback(
|
|
1596
|
+
(e) => {
|
|
1597
|
+
const overId = e.over?.id;
|
|
1598
|
+
if (overId === TREE_ROOT_DROP_ID) {
|
|
1599
|
+
const current2 = dnd.dropTarget;
|
|
1600
|
+
if (current2?.id !== null || current2?.position !== "inside") {
|
|
1601
|
+
dnd.setDropTarget({ id: null, position: "inside" });
|
|
1602
|
+
}
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
if (typeof overId !== "string") {
|
|
1606
|
+
if (dnd.dropTarget !== null) dnd.setDropTarget(null);
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
const rect = e.over?.rect;
|
|
1610
|
+
if (!rect) return;
|
|
1611
|
+
const el = document.querySelector(
|
|
1612
|
+
`[data-tree-row][data-id="${CSS.escape(overId)}"]`
|
|
1613
|
+
);
|
|
1614
|
+
const isFolder = el?.dataset.folder === "true";
|
|
1615
|
+
const position = resolveDropZone({
|
|
1616
|
+
pointerY: cursorYRef.current,
|
|
1617
|
+
rowRect: { top: rect.top, bottom: rect.top + rect.height, height: rect.height },
|
|
1618
|
+
isFolder
|
|
1619
|
+
});
|
|
1620
|
+
const current = dnd.dropTarget;
|
|
1621
|
+
if (current?.id !== overId || current.position !== position) {
|
|
1622
|
+
dnd.setDropTarget({ id: overId, position });
|
|
1623
|
+
}
|
|
1624
|
+
},
|
|
1625
|
+
[dnd]
|
|
1626
|
+
);
|
|
1627
|
+
const handleDragEnd = React.useCallback(
|
|
1628
|
+
async (_e) => {
|
|
1629
|
+
await dnd.commitDrop();
|
|
1630
|
+
},
|
|
1631
|
+
[dnd]
|
|
1632
|
+
);
|
|
1633
|
+
const handleDragCancel = React.useCallback(() => {
|
|
1634
|
+
dnd.cancelDrag();
|
|
1635
|
+
}, [dnd]);
|
|
1636
|
+
const handlePointerMove = React.useCallback((e) => {
|
|
1637
|
+
cursorYRef.current = e.clientY;
|
|
1638
|
+
}, []);
|
|
1639
|
+
if (!dnd.active) {
|
|
1640
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
1641
|
+
}
|
|
1642
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1643
|
+
core.DndContext,
|
|
1644
|
+
{
|
|
1645
|
+
sensors,
|
|
1646
|
+
onDragStart: handleDragStart,
|
|
1647
|
+
onDragMove: handleDragMove,
|
|
1648
|
+
onDragEnd: handleDragEnd,
|
|
1649
|
+
onDragCancel: handleDragCancel,
|
|
1650
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1651
|
+
"div",
|
|
1652
|
+
{
|
|
1653
|
+
onPointerMove: handlePointerMove,
|
|
1654
|
+
className: "contents",
|
|
1655
|
+
"data-tree-dnd-surface": "",
|
|
1656
|
+
children
|
|
1657
|
+
}
|
|
1658
|
+
)
|
|
1659
|
+
}
|
|
1660
|
+
);
|
|
1661
|
+
}
|
|
1662
|
+
chunkPK6SKIKE_cjs.__name(TreeDndProvider, "TreeDndProvider");
|
|
580
1663
|
function TreeChevronRaw({ isExpanded, isFolder, className }) {
|
|
581
1664
|
const { appearance } = useTreeContext();
|
|
582
1665
|
const size = { width: "var(--tree-icon-size)", height: "var(--tree-icon-size)" };
|
|
@@ -606,6 +1689,48 @@ function TreeChevronRaw({ isExpanded, isFolder, className }) {
|
|
|
606
1689
|
}
|
|
607
1690
|
chunkPK6SKIKE_cjs.__name(TreeChevronRaw, "TreeChevronRaw");
|
|
608
1691
|
var TreeChevron = React.memo(TreeChevronRaw);
|
|
1692
|
+
function TreeDropIndicator({
|
|
1693
|
+
position,
|
|
1694
|
+
indent,
|
|
1695
|
+
invalid = false
|
|
1696
|
+
}) {
|
|
1697
|
+
if (position === "inside") {
|
|
1698
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1699
|
+
"span",
|
|
1700
|
+
{
|
|
1701
|
+
"aria-hidden": true,
|
|
1702
|
+
"data-tree-drop": "inside",
|
|
1703
|
+
className: lib.cn(
|
|
1704
|
+
"pointer-events-none absolute inset-0 rounded-sm ring-1",
|
|
1705
|
+
invalid ? "bg-destructive/10 ring-destructive/40" : "bg-primary/10 ring-primary/40"
|
|
1706
|
+
)
|
|
1707
|
+
}
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
const isBefore = position === "before";
|
|
1711
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1712
|
+
"span",
|
|
1713
|
+
{
|
|
1714
|
+
"aria-hidden": true,
|
|
1715
|
+
"data-tree-drop": position,
|
|
1716
|
+
style: { paddingLeft: indent },
|
|
1717
|
+
className: lib.cn(
|
|
1718
|
+
"pointer-events-none absolute right-0 left-0 h-px",
|
|
1719
|
+
isBefore ? "top-0" : "bottom-0"
|
|
1720
|
+
),
|
|
1721
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1722
|
+
"span",
|
|
1723
|
+
{
|
|
1724
|
+
className: lib.cn(
|
|
1725
|
+
"block h-0.5 rounded-full",
|
|
1726
|
+
invalid ? "bg-destructive/70" : "bg-primary"
|
|
1727
|
+
)
|
|
1728
|
+
}
|
|
1729
|
+
)
|
|
1730
|
+
}
|
|
1731
|
+
);
|
|
1732
|
+
}
|
|
1733
|
+
chunkPK6SKIKE_cjs.__name(TreeDropIndicator, "TreeDropIndicator");
|
|
609
1734
|
function TreeIconRaw({ isFolder, isExpanded, className }) {
|
|
610
1735
|
const { appearance } = useTreeContext();
|
|
611
1736
|
const Icon = isFolder ? isExpanded ? lucideReact.FolderOpen : lucideReact.Folder : lucideReact.File;
|
|
@@ -662,6 +1787,96 @@ function TreeLabelRaw({ children, isMatchingSearch, className }) {
|
|
|
662
1787
|
}
|
|
663
1788
|
chunkPK6SKIKE_cjs.__name(TreeLabelRaw, "TreeLabelRaw");
|
|
664
1789
|
var TreeLabel = React.memo(TreeLabelRaw);
|
|
1790
|
+
|
|
1791
|
+
// src/tools/data/Tree/data/renameUtils.ts
|
|
1792
|
+
function splitFileName(name) {
|
|
1793
|
+
if (name.length === 0) return { base: "", ext: "" };
|
|
1794
|
+
if (name.startsWith(".")) {
|
|
1795
|
+
const rest = name.slice(1);
|
|
1796
|
+
const dot2 = rest.lastIndexOf(".");
|
|
1797
|
+
if (dot2 < 0) return { base: name, ext: "" };
|
|
1798
|
+
return { base: "." + rest.slice(0, dot2), ext: rest.slice(dot2) };
|
|
1799
|
+
}
|
|
1800
|
+
const dot = name.lastIndexOf(".");
|
|
1801
|
+
if (dot <= 0) return { base: name, ext: "" };
|
|
1802
|
+
return { base: name.slice(0, dot), ext: name.slice(dot) };
|
|
1803
|
+
}
|
|
1804
|
+
chunkPK6SKIKE_cjs.__name(splitFileName, "splitFileName");
|
|
1805
|
+
function autoSelectRange(name, isFolder) {
|
|
1806
|
+
if (isFolder) return [0, name.length];
|
|
1807
|
+
const { base } = splitFileName(name);
|
|
1808
|
+
return [0, base.length];
|
|
1809
|
+
}
|
|
1810
|
+
chunkPK6SKIKE_cjs.__name(autoSelectRange, "autoSelectRange");
|
|
1811
|
+
function TreeRenameInput({
|
|
1812
|
+
initialValue,
|
|
1813
|
+
isFolder,
|
|
1814
|
+
onCommit,
|
|
1815
|
+
onCancel,
|
|
1816
|
+
className
|
|
1817
|
+
}) {
|
|
1818
|
+
const [value, setValue] = React.useState(initialValue);
|
|
1819
|
+
const inputRef = React.useRef(null);
|
|
1820
|
+
const settledRef = React.useRef(false);
|
|
1821
|
+
React.useEffect(() => {
|
|
1822
|
+
const el = inputRef.current;
|
|
1823
|
+
if (!el) return;
|
|
1824
|
+
el.focus();
|
|
1825
|
+
const [start, end] = autoSelectRange(initialValue, isFolder);
|
|
1826
|
+
requestAnimationFrame(() => {
|
|
1827
|
+
try {
|
|
1828
|
+
el.setSelectionRange(start, end);
|
|
1829
|
+
} catch {
|
|
1830
|
+
}
|
|
1831
|
+
});
|
|
1832
|
+
}, []);
|
|
1833
|
+
const commit = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => {
|
|
1834
|
+
if (settledRef.current) return;
|
|
1835
|
+
settledRef.current = true;
|
|
1836
|
+
void onCommit(value);
|
|
1837
|
+
}, "commit");
|
|
1838
|
+
const cancel = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => {
|
|
1839
|
+
if (settledRef.current) return;
|
|
1840
|
+
settledRef.current = true;
|
|
1841
|
+
onCancel();
|
|
1842
|
+
}, "cancel");
|
|
1843
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1844
|
+
"input",
|
|
1845
|
+
{
|
|
1846
|
+
ref: inputRef,
|
|
1847
|
+
type: "text",
|
|
1848
|
+
value,
|
|
1849
|
+
onKeyDown: (e) => {
|
|
1850
|
+
e.stopPropagation();
|
|
1851
|
+
if (e.key === "Enter") {
|
|
1852
|
+
e.preventDefault();
|
|
1853
|
+
commit();
|
|
1854
|
+
} else if (e.key === "Escape") {
|
|
1855
|
+
e.preventDefault();
|
|
1856
|
+
cancel();
|
|
1857
|
+
}
|
|
1858
|
+
},
|
|
1859
|
+
onChange: (e) => setValue(e.target.value),
|
|
1860
|
+
onBlur: commit,
|
|
1861
|
+
onClick: (e) => e.stopPropagation(),
|
|
1862
|
+
onDoubleClick: (e) => e.stopPropagation(),
|
|
1863
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
1864
|
+
onContextMenu: (e) => e.stopPropagation(),
|
|
1865
|
+
className: lib.cn(
|
|
1866
|
+
"min-w-0 flex-1 rounded-sm border border-primary/50 bg-background",
|
|
1867
|
+
"px-1 py-0 text-foreground outline-none",
|
|
1868
|
+
"focus:ring-1 focus:ring-primary/40",
|
|
1869
|
+
className
|
|
1870
|
+
),
|
|
1871
|
+
style: {
|
|
1872
|
+
// Match the row's font metrics so the input doesn't visibly jolt.
|
|
1873
|
+
fontSize: "var(--tree-font-size)",
|
|
1874
|
+
height: "calc(var(--tree-row-height) - 4px)"
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
);
|
|
1878
|
+
}
|
|
1879
|
+
chunkPK6SKIKE_cjs.__name(TreeRenameInput, "TreeRenameInput");
|
|
665
1880
|
function TreeRowRaw({ row, className }) {
|
|
666
1881
|
const ctx = useTreeContext();
|
|
667
1882
|
const {
|
|
@@ -673,6 +1888,7 @@ function TreeRowRaw({ row, className }) {
|
|
|
673
1888
|
matchingIds,
|
|
674
1889
|
select,
|
|
675
1890
|
setSelectedIds,
|
|
1891
|
+
clickSelect,
|
|
676
1892
|
toggle,
|
|
677
1893
|
setFocus,
|
|
678
1894
|
activate,
|
|
@@ -680,13 +1896,19 @@ function TreeRowRaw({ row, className }) {
|
|
|
680
1896
|
renderIcon,
|
|
681
1897
|
renderLabel,
|
|
682
1898
|
renderActions,
|
|
683
|
-
renderContextMenu
|
|
1899
|
+
renderContextMenu,
|
|
1900
|
+
renamingId,
|
|
1901
|
+
commitRename,
|
|
1902
|
+
cancelRename,
|
|
1903
|
+
clipboard,
|
|
1904
|
+
dnd
|
|
684
1905
|
} = ctx;
|
|
685
1906
|
const { node, level, isFolder, isExpanded, isLoading, posInSet, setSize } = row;
|
|
686
1907
|
const isSelected = selected.has(node.id);
|
|
687
1908
|
const isFocused = focused === node.id;
|
|
688
1909
|
const isMatchingSearch = matchingIds.has(node.id);
|
|
689
1910
|
const isMultiSelect = ctx.selectionMode === "multiple";
|
|
1911
|
+
const isCut = clipboard?.kind === "cut" && clipboard.ids.includes(node.id);
|
|
690
1912
|
const slot = {
|
|
691
1913
|
node,
|
|
692
1914
|
level,
|
|
@@ -697,30 +1919,57 @@ function TreeRowRaw({ row, className }) {
|
|
|
697
1919
|
isLoading,
|
|
698
1920
|
isMatchingSearch
|
|
699
1921
|
};
|
|
1922
|
+
const isRenaming = renamingId === node.id;
|
|
1923
|
+
const isDragging = dnd.draggingIds.has(node.id);
|
|
1924
|
+
const dropTarget = dnd.dropTarget;
|
|
1925
|
+
const isDropTarget = dropTarget?.id === node.id;
|
|
1926
|
+
const dropPosition = isDropTarget ? dropTarget.position : null;
|
|
1927
|
+
const dndDisabled = !dnd.active || isRenaming || node.disabled;
|
|
1928
|
+
const draggable = core.useDraggable({ id: node.id, disabled: dndDisabled });
|
|
1929
|
+
const droppable = core.useDroppable({ id: node.id, disabled: dndDisabled });
|
|
1930
|
+
const setRowEl = React.useCallback(
|
|
1931
|
+
(el) => {
|
|
1932
|
+
draggable.setNodeRef(el);
|
|
1933
|
+
droppable.setNodeRef(el);
|
|
1934
|
+
},
|
|
1935
|
+
[draggable, droppable]
|
|
1936
|
+
);
|
|
1937
|
+
const isAllowedDrop = dropPosition && !isDragging ? dnd.isAllowedDrop(node, dropPosition) : true;
|
|
700
1938
|
const handleClick = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((e) => {
|
|
701
|
-
if (node.disabled) return;
|
|
1939
|
+
if (node.disabled || isRenaming) return;
|
|
702
1940
|
setFocus(node.id);
|
|
703
|
-
if (isMultiSelect
|
|
704
|
-
|
|
1941
|
+
if (isMultiSelect) {
|
|
1942
|
+
clickSelect(node.id, { shift: e.shiftKey, meta: e.metaKey || e.ctrlKey });
|
|
705
1943
|
} else {
|
|
706
1944
|
select(node.id);
|
|
707
1945
|
}
|
|
708
1946
|
if (isFolder) {
|
|
1947
|
+
if (e.shiftKey || e.metaKey || e.ctrlKey) return;
|
|
709
1948
|
toggle(node.id);
|
|
710
1949
|
} else if (activationMode === "single-click") {
|
|
1950
|
+
if (e.shiftKey || e.metaKey || e.ctrlKey) return;
|
|
711
1951
|
activate(node, { preview: false });
|
|
712
1952
|
} else if (activationMode === "single-click-preview") {
|
|
1953
|
+
if (e.shiftKey || e.metaKey || e.ctrlKey) return;
|
|
713
1954
|
activate(node, { preview: true });
|
|
714
1955
|
}
|
|
715
1956
|
}, "handleClick");
|
|
716
1957
|
const handleDoubleClick = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => {
|
|
717
|
-
if (node.disabled) return;
|
|
1958
|
+
if (node.disabled || isRenaming) return;
|
|
718
1959
|
if (isFolder) return;
|
|
719
1960
|
activate(node, { preview: false });
|
|
720
1961
|
}, "handleDoubleClick");
|
|
1962
|
+
const handleContextMenu = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => {
|
|
1963
|
+
if (node.disabled || isRenaming) return;
|
|
1964
|
+
setFocus(node.id);
|
|
1965
|
+
if (!isSelected) {
|
|
1966
|
+
setSelectedIds([node.id]);
|
|
1967
|
+
}
|
|
1968
|
+
}, "handleContextMenu");
|
|
721
1969
|
const trigger = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
722
1970
|
"div",
|
|
723
1971
|
{
|
|
1972
|
+
ref: dnd.active ? setRowEl : void 0,
|
|
724
1973
|
id: treeRowDomId(node.id),
|
|
725
1974
|
role: "treeitem",
|
|
726
1975
|
"aria-level": level + 1,
|
|
@@ -733,6 +1982,8 @@ function TreeRowRaw({ row, className }) {
|
|
|
733
1982
|
"data-id": node.id,
|
|
734
1983
|
"data-activation-mode": activationMode,
|
|
735
1984
|
"data-selected": isSelected ? "true" : void 0,
|
|
1985
|
+
"data-clipboard": isCut ? "cut" : void 0,
|
|
1986
|
+
"data-dragging": isDragging ? "true" : void 0,
|
|
736
1987
|
"data-focused": isFocused && !isSelected ? "true" : void 0,
|
|
737
1988
|
"data-folder": isFolder || void 0,
|
|
738
1989
|
"data-expanded": isExpanded || void 0,
|
|
@@ -742,8 +1993,14 @@ function TreeRowRaw({ row, className }) {
|
|
|
742
1993
|
height: "var(--tree-row-height)",
|
|
743
1994
|
gap: "var(--tree-gap)"
|
|
744
1995
|
},
|
|
1996
|
+
...dnd.active ? draggable.listeners : {},
|
|
1997
|
+
...dnd.active ? draggable.attributes : {},
|
|
1998
|
+
onMouseDown: (e) => {
|
|
1999
|
+
if (e.button === 0) e.preventDefault();
|
|
2000
|
+
},
|
|
745
2001
|
onClick: handleClick,
|
|
746
2002
|
onDoubleClick: handleDoubleClick,
|
|
2003
|
+
onContextMenu: handleContextMenu,
|
|
747
2004
|
onFocus: () => setFocus(node.id),
|
|
748
2005
|
className: lib.cn(
|
|
749
2006
|
"group/row relative flex w-full select-none items-center pr-2 text-left",
|
|
@@ -753,6 +2010,8 @@ function TreeRowRaw({ row, className }) {
|
|
|
753
2010
|
rowStateClasses(appearance),
|
|
754
2011
|
"focus-visible:ring-1 focus-visible:ring-ring/50",
|
|
755
2012
|
isMatchingSearch && "ring-1 ring-primary/30",
|
|
2013
|
+
isCut && "opacity-60",
|
|
2014
|
+
isDragging && "opacity-40",
|
|
756
2015
|
node.disabled && "opacity-50",
|
|
757
2016
|
className
|
|
758
2017
|
),
|
|
@@ -767,6 +2026,14 @@ function TreeRowRaw({ row, className }) {
|
|
|
767
2026
|
)
|
|
768
2027
|
}
|
|
769
2028
|
) : null,
|
|
2029
|
+
dropPosition && !isDragging ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2030
|
+
TreeDropIndicator,
|
|
2031
|
+
{
|
|
2032
|
+
position: dropPosition,
|
|
2033
|
+
indent: 6 + level * appearance.indent,
|
|
2034
|
+
invalid: !isAllowedDrop
|
|
2035
|
+
}
|
|
2036
|
+
) : null,
|
|
770
2037
|
showIndentGuides && level > 0 ? /* @__PURE__ */ jsxRuntime.jsx(TreeIndentGuides, { level, indent: appearance.indent }) : null,
|
|
771
2038
|
/* @__PURE__ */ jsxRuntime.jsx(TreeChevron, { isExpanded, isFolder }),
|
|
772
2039
|
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -783,7 +2050,15 @@ function TreeRowRaw({ row, className }) {
|
|
|
783
2050
|
{
|
|
784
2051
|
className: "flex min-w-0 flex-1 items-center",
|
|
785
2052
|
style: { gap: "var(--tree-gap)" },
|
|
786
|
-
children:
|
|
2053
|
+
children: renamingId === node.id ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2054
|
+
TreeRenameInput,
|
|
2055
|
+
{
|
|
2056
|
+
initialValue: getItemName(node),
|
|
2057
|
+
isFolder,
|
|
2058
|
+
onCommit: (next) => commitRename(node.id, next),
|
|
2059
|
+
onCancel: cancelRename
|
|
2060
|
+
}
|
|
2061
|
+
) : renderLabel ? renderLabel(slot) : /* @__PURE__ */ jsxRuntime.jsx(TreeLabel, { isMatchingSearch, children: getItemName(node) })
|
|
787
2062
|
}
|
|
788
2063
|
),
|
|
789
2064
|
renderActions ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -855,6 +2130,105 @@ function TreeContent({
|
|
|
855
2130
|
);
|
|
856
2131
|
}
|
|
857
2132
|
chunkPK6SKIKE_cjs.__name(TreeContent, "TreeContent");
|
|
2133
|
+
function TreeEmptyArea({ className }) {
|
|
2134
|
+
const ctx = useTreeContext();
|
|
2135
|
+
const {
|
|
2136
|
+
adapter,
|
|
2137
|
+
labels,
|
|
2138
|
+
getItemName,
|
|
2139
|
+
clipboard,
|
|
2140
|
+
cutToClipboard,
|
|
2141
|
+
copyToClipboard,
|
|
2142
|
+
pasteFromClipboard,
|
|
2143
|
+
startRename,
|
|
2144
|
+
inlineRenameEnabled,
|
|
2145
|
+
dnd
|
|
2146
|
+
} = ctx;
|
|
2147
|
+
const { setNodeRef: setDroppableRef, isOver } = core.useDroppable({
|
|
2148
|
+
id: TREE_ROOT_DROP_ID,
|
|
2149
|
+
disabled: !dnd.active
|
|
2150
|
+
});
|
|
2151
|
+
const items = React.useMemo(() => {
|
|
2152
|
+
if (!adapter) return null;
|
|
2153
|
+
const builtinCtx = {
|
|
2154
|
+
adapter,
|
|
2155
|
+
labels,
|
|
2156
|
+
selectedNodes: [],
|
|
2157
|
+
targetNode: null,
|
|
2158
|
+
getName: getItemName,
|
|
2159
|
+
startInlineRename: inlineRenameEnabled ? startRename : void 0,
|
|
2160
|
+
clipboard: {
|
|
2161
|
+
hasItems: !!clipboard && clipboard.ids.length > 0,
|
|
2162
|
+
cut: cutToClipboard,
|
|
2163
|
+
copy: copyToClipboard,
|
|
2164
|
+
paste: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => pasteFromClipboard(null, "inside"), "paste")
|
|
2165
|
+
}
|
|
2166
|
+
};
|
|
2167
|
+
return buildDefaultMenuItems(builtinCtx);
|
|
2168
|
+
}, [
|
|
2169
|
+
adapter,
|
|
2170
|
+
labels,
|
|
2171
|
+
getItemName,
|
|
2172
|
+
inlineRenameEnabled,
|
|
2173
|
+
startRename,
|
|
2174
|
+
clipboard,
|
|
2175
|
+
cutToClipboard,
|
|
2176
|
+
copyToClipboard,
|
|
2177
|
+
pasteFromClipboard
|
|
2178
|
+
]);
|
|
2179
|
+
const surface = /* @__PURE__ */ jsxRuntime.jsx(
|
|
2180
|
+
"div",
|
|
2181
|
+
{
|
|
2182
|
+
ref: dnd.active ? setDroppableRef : void 0,
|
|
2183
|
+
"data-tree-empty-area": "",
|
|
2184
|
+
"data-drop-active": dnd.active && isOver ? "true" : void 0,
|
|
2185
|
+
className: lib.cn(
|
|
2186
|
+
// Soaks up the remaining vertical space inside the scroll
|
|
2187
|
+
// container so right-click on whitespace lands here, not on
|
|
2188
|
+
// the scroll viewport.
|
|
2189
|
+
"min-h-[2.5rem] flex-1",
|
|
2190
|
+
dnd.active && isOver && "bg-primary/5 rounded-sm ring-1 ring-primary/30",
|
|
2191
|
+
className
|
|
2192
|
+
)
|
|
2193
|
+
}
|
|
2194
|
+
);
|
|
2195
|
+
if (!items || items.length === 0) {
|
|
2196
|
+
return surface;
|
|
2197
|
+
}
|
|
2198
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(components.ContextMenu, { children: [
|
|
2199
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuTrigger, { asChild: true, children: surface }),
|
|
2200
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuContent, { children: items.map((item, idx) => {
|
|
2201
|
+
if (item === "separator") {
|
|
2202
|
+
return /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuSeparator, {}, `sep-${idx}`);
|
|
2203
|
+
}
|
|
2204
|
+
const Icon = item.icon;
|
|
2205
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2206
|
+
components.ContextMenuItem,
|
|
2207
|
+
{
|
|
2208
|
+
disabled: item.disabled,
|
|
2209
|
+
variant: item.destructive ? "destructive" : void 0,
|
|
2210
|
+
onSelect: () => item.onSelect({
|
|
2211
|
+
node: void 0,
|
|
2212
|
+
level: 0,
|
|
2213
|
+
isSelected: false,
|
|
2214
|
+
isExpanded: false,
|
|
2215
|
+
isFocused: false,
|
|
2216
|
+
isFolder: false,
|
|
2217
|
+
isLoading: false,
|
|
2218
|
+
isMatchingSearch: false
|
|
2219
|
+
}),
|
|
2220
|
+
children: [
|
|
2221
|
+
Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) : null,
|
|
2222
|
+
item.label,
|
|
2223
|
+
item.shortcut ? /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuShortcut, { children: item.shortcut }) : null
|
|
2224
|
+
]
|
|
2225
|
+
},
|
|
2226
|
+
item.id
|
|
2227
|
+
);
|
|
2228
|
+
}) })
|
|
2229
|
+
] });
|
|
2230
|
+
}
|
|
2231
|
+
chunkPK6SKIKE_cjs.__name(TreeEmptyArea, "TreeEmptyArea");
|
|
858
2232
|
function useTreeLabels() {
|
|
859
2233
|
return useTreeContext().labels;
|
|
860
2234
|
}
|
|
@@ -873,12 +2247,26 @@ function useTreeSelection() {
|
|
|
873
2247
|
return React.useMemo(
|
|
874
2248
|
() => ({
|
|
875
2249
|
selectedIds,
|
|
2250
|
+
anchor: ctx.anchor,
|
|
876
2251
|
select: ctx.select,
|
|
877
2252
|
setSelectedIds: ctx.setSelectedIds,
|
|
878
2253
|
clear: ctx.clearSelection,
|
|
2254
|
+
clickSelect: ctx.clickSelect,
|
|
2255
|
+
moveSelect: ctx.moveSelect,
|
|
2256
|
+
selectAll: ctx.selectAll,
|
|
879
2257
|
isSelected
|
|
880
2258
|
}),
|
|
881
|
-
[
|
|
2259
|
+
[
|
|
2260
|
+
selectedIds,
|
|
2261
|
+
ctx.anchor,
|
|
2262
|
+
ctx.select,
|
|
2263
|
+
ctx.setSelectedIds,
|
|
2264
|
+
ctx.clearSelection,
|
|
2265
|
+
ctx.clickSelect,
|
|
2266
|
+
ctx.moveSelect,
|
|
2267
|
+
ctx.selectAll,
|
|
2268
|
+
isSelected
|
|
2269
|
+
]
|
|
882
2270
|
);
|
|
883
2271
|
}
|
|
884
2272
|
chunkPK6SKIKE_cjs.__name(useTreeSelection, "useTreeSelection");
|
|
@@ -933,6 +2321,59 @@ function useTreeSearch() {
|
|
|
933
2321
|
);
|
|
934
2322
|
}
|
|
935
2323
|
chunkPK6SKIKE_cjs.__name(useTreeSearch, "useTreeSearch");
|
|
2324
|
+
function useTreeDnd() {
|
|
2325
|
+
const ctx = useTreeContext();
|
|
2326
|
+
return ctx.dnd;
|
|
2327
|
+
}
|
|
2328
|
+
chunkPK6SKIKE_cjs.__name(useTreeDnd, "useTreeDnd");
|
|
2329
|
+
function useTreeClipboard() {
|
|
2330
|
+
const ctx = useTreeContext();
|
|
2331
|
+
const isCut = React.useCallback(
|
|
2332
|
+
(id) => ctx.clipboard?.kind === "cut" && ctx.clipboard.ids.includes(id),
|
|
2333
|
+
[ctx.clipboard]
|
|
2334
|
+
);
|
|
2335
|
+
return React.useMemo(
|
|
2336
|
+
() => ({
|
|
2337
|
+
clipboard: ctx.clipboard,
|
|
2338
|
+
isCut,
|
|
2339
|
+
cut: ctx.cutToClipboard,
|
|
2340
|
+
copy: ctx.copyToClipboard,
|
|
2341
|
+
paste: ctx.pasteFromClipboard,
|
|
2342
|
+
clear: ctx.clearClipboard
|
|
2343
|
+
}),
|
|
2344
|
+
[
|
|
2345
|
+
ctx.clipboard,
|
|
2346
|
+
isCut,
|
|
2347
|
+
ctx.cutToClipboard,
|
|
2348
|
+
ctx.copyToClipboard,
|
|
2349
|
+
ctx.pasteFromClipboard,
|
|
2350
|
+
ctx.clearClipboard
|
|
2351
|
+
]
|
|
2352
|
+
);
|
|
2353
|
+
}
|
|
2354
|
+
chunkPK6SKIKE_cjs.__name(useTreeClipboard, "useTreeClipboard");
|
|
2355
|
+
function useTreeRename() {
|
|
2356
|
+
const ctx = useTreeContext();
|
|
2357
|
+
return React.useMemo(
|
|
2358
|
+
() => ({
|
|
2359
|
+
/** True when the host allowed inline rename AND the adapter exposes `rename`. */
|
|
2360
|
+
enabled: ctx.inlineRenameEnabled,
|
|
2361
|
+
/** Currently renaming id, or `null`. */
|
|
2362
|
+
renamingId: ctx.renamingId,
|
|
2363
|
+
startRename: ctx.startRename,
|
|
2364
|
+
cancelRename: ctx.cancelRename,
|
|
2365
|
+
commitRename: ctx.commitRename
|
|
2366
|
+
}),
|
|
2367
|
+
[
|
|
2368
|
+
ctx.inlineRenameEnabled,
|
|
2369
|
+
ctx.renamingId,
|
|
2370
|
+
ctx.startRename,
|
|
2371
|
+
ctx.cancelRename,
|
|
2372
|
+
ctx.commitRename
|
|
2373
|
+
]
|
|
2374
|
+
);
|
|
2375
|
+
}
|
|
2376
|
+
chunkPK6SKIKE_cjs.__name(useTreeRename, "useTreeRename");
|
|
936
2377
|
function useTreeActions() {
|
|
937
2378
|
const ctx = useTreeContext();
|
|
938
2379
|
return React.useMemo(
|
|
@@ -997,16 +2438,78 @@ function TreeSearchInput({ className, showMatches = true }) {
|
|
|
997
2438
|
);
|
|
998
2439
|
}
|
|
999
2440
|
chunkPK6SKIKE_cjs.__name(TreeSearchInput, "TreeSearchInput");
|
|
2441
|
+
|
|
2442
|
+
// src/tools/data/Tree/hooks/keyboard/arrow-nav.ts
|
|
2443
|
+
function nextRowId(rows, idx) {
|
|
2444
|
+
if (rows.length === 0) return null;
|
|
2445
|
+
const next = rows[Math.min(idx + 1, rows.length - 1)] ?? rows[0];
|
|
2446
|
+
return next.node.id;
|
|
2447
|
+
}
|
|
2448
|
+
chunkPK6SKIKE_cjs.__name(nextRowId, "nextRowId");
|
|
2449
|
+
function prevRowId(rows, idx) {
|
|
2450
|
+
if (rows.length === 0) return null;
|
|
2451
|
+
const prev = rows[Math.max(idx - 1, 0)] ?? rows[0];
|
|
2452
|
+
return prev.node.id;
|
|
2453
|
+
}
|
|
2454
|
+
chunkPK6SKIKE_cjs.__name(prevRowId, "prevRowId");
|
|
2455
|
+
function edgeRowId(rows, edge) {
|
|
2456
|
+
if (rows.length === 0) return null;
|
|
2457
|
+
return edge === "first" ? rows[0].node.id : rows[rows.length - 1].node.id;
|
|
2458
|
+
}
|
|
2459
|
+
chunkPK6SKIKE_cjs.__name(edgeRowId, "edgeRowId");
|
|
2460
|
+
|
|
2461
|
+
// src/tools/data/Tree/hooks/keyboard/expand-collapse.ts
|
|
2462
|
+
function resolveRightArrow(current, rows, idx) {
|
|
2463
|
+
if (!current) return { kind: "noop" };
|
|
2464
|
+
if (current.isFolder && !current.isExpanded) {
|
|
2465
|
+
return { kind: "expand", id: current.node.id };
|
|
2466
|
+
}
|
|
2467
|
+
if (current.isFolder && current.isExpanded) {
|
|
2468
|
+
const next = rows[idx + 1];
|
|
2469
|
+
return next ? { kind: "focus", id: next.node.id } : { kind: "noop" };
|
|
2470
|
+
}
|
|
2471
|
+
return { kind: "noop" };
|
|
2472
|
+
}
|
|
2473
|
+
chunkPK6SKIKE_cjs.__name(resolveRightArrow, "resolveRightArrow");
|
|
2474
|
+
function resolveLeftArrow(current) {
|
|
2475
|
+
if (!current) return { kind: "noop" };
|
|
2476
|
+
if (current.isFolder && current.isExpanded) {
|
|
2477
|
+
return { kind: "collapse", id: current.node.id };
|
|
2478
|
+
}
|
|
2479
|
+
if (current.parentId) {
|
|
2480
|
+
return { kind: "focus", id: current.parentId };
|
|
2481
|
+
}
|
|
2482
|
+
return { kind: "noop" };
|
|
2483
|
+
}
|
|
2484
|
+
chunkPK6SKIKE_cjs.__name(resolveLeftArrow, "resolveLeftArrow");
|
|
2485
|
+
|
|
2486
|
+
// src/tools/data/Tree/hooks/keyboard/activation.ts
|
|
2487
|
+
function resolveActivate(current) {
|
|
2488
|
+
if (!current) return { kind: "noop" };
|
|
2489
|
+
if (current.isFolder) {
|
|
2490
|
+
return {
|
|
2491
|
+
kind: "toggle-folder",
|
|
2492
|
+
id: current.node.id,
|
|
2493
|
+
willExpand: !current.isExpanded
|
|
2494
|
+
};
|
|
2495
|
+
}
|
|
2496
|
+
return { kind: "activate-leaf", id: current.node.id };
|
|
2497
|
+
}
|
|
2498
|
+
chunkPK6SKIKE_cjs.__name(resolveActivate, "resolveActivate");
|
|
2499
|
+
|
|
2500
|
+
// src/tools/data/Tree/hooks/keyboard/use-tree-keyboard.ts
|
|
1000
2501
|
function useTreeKeyboard({
|
|
1001
2502
|
rows,
|
|
1002
2503
|
focusedId,
|
|
1003
2504
|
enabled = true,
|
|
2505
|
+
multiSelect = false,
|
|
1004
2506
|
onFocus,
|
|
1005
2507
|
onSelect,
|
|
1006
2508
|
onActivate,
|
|
1007
2509
|
onExpand,
|
|
1008
2510
|
onCollapse,
|
|
1009
|
-
onClearSelection
|
|
2511
|
+
onClearSelection,
|
|
2512
|
+
onSelectAll
|
|
1010
2513
|
}) {
|
|
1011
2514
|
const rowsRef = React.useRef(rows);
|
|
1012
2515
|
const focusedIdRef = React.useRef(focusedId);
|
|
@@ -1019,53 +2522,65 @@ function useTreeKeyboard({
|
|
|
1019
2522
|
return { rows: r, idx, current: idx >= 0 ? r[idx] : null };
|
|
1020
2523
|
}, "getCurrent");
|
|
1021
2524
|
const refDown = hooks.useHotkey(
|
|
1022
|
-
"down",
|
|
1023
|
-
() => {
|
|
2525
|
+
["down", "shift+down"],
|
|
2526
|
+
(e) => {
|
|
1024
2527
|
const { rows: r, idx } = getCurrent();
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
onFocus(next.node.id);
|
|
2528
|
+
const id = nextRowId(r, idx);
|
|
2529
|
+
if (id) onFocus(id, { extend: multiSelect && e.shiftKey });
|
|
1028
2530
|
},
|
|
1029
|
-
{ enabled, preventDefault: true, description: "Next row" }
|
|
2531
|
+
{ enabled, preventDefault: true, description: "Next row (Shift extends)" }
|
|
1030
2532
|
);
|
|
1031
2533
|
const refUp = hooks.useHotkey(
|
|
1032
|
-
"up",
|
|
1033
|
-
() => {
|
|
2534
|
+
["up", "shift+up"],
|
|
2535
|
+
(e) => {
|
|
1034
2536
|
const { rows: r, idx } = getCurrent();
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
onFocus(prev.node.id);
|
|
2537
|
+
const id = prevRowId(r, idx);
|
|
2538
|
+
if (id) onFocus(id, { extend: multiSelect && e.shiftKey });
|
|
1038
2539
|
},
|
|
1039
|
-
{ enabled, preventDefault: true, description: "Previous row" }
|
|
2540
|
+
{ enabled, preventDefault: true, description: "Previous row (Shift extends)" }
|
|
1040
2541
|
);
|
|
1041
2542
|
const refHome = hooks.useHotkey(
|
|
1042
|
-
"home",
|
|
1043
|
-
() => {
|
|
1044
|
-
const
|
|
1045
|
-
if (
|
|
1046
|
-
onFocus(r[0].node.id);
|
|
2543
|
+
["home", "shift+home"],
|
|
2544
|
+
(e) => {
|
|
2545
|
+
const id = edgeRowId(rowsRef.current, "first");
|
|
2546
|
+
if (id) onFocus(id, { extend: multiSelect && e.shiftKey });
|
|
1047
2547
|
},
|
|
1048
|
-
{ enabled, preventDefault: true, description: "First row" }
|
|
2548
|
+
{ enabled, preventDefault: true, description: "First row (Shift extends)" }
|
|
1049
2549
|
);
|
|
1050
2550
|
const refEnd = hooks.useHotkey(
|
|
1051
|
-
"end",
|
|
2551
|
+
["end", "shift+end"],
|
|
2552
|
+
(e) => {
|
|
2553
|
+
const id = edgeRowId(rowsRef.current, "last");
|
|
2554
|
+
if (id) onFocus(id, { extend: multiSelect && e.shiftKey });
|
|
2555
|
+
},
|
|
2556
|
+
{ enabled, preventDefault: true, description: "Last row (Shift extends)" }
|
|
2557
|
+
);
|
|
2558
|
+
const refSelectAll = hooks.useHotkey(
|
|
2559
|
+
"mod+a",
|
|
1052
2560
|
() => {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
onFocus(r[r.length - 1].node.id);
|
|
2561
|
+
if (!multiSelect) return;
|
|
2562
|
+
onSelectAll?.();
|
|
1056
2563
|
},
|
|
1057
|
-
{
|
|
2564
|
+
{
|
|
2565
|
+
enabled: enabled && multiSelect,
|
|
2566
|
+
preventDefault: true,
|
|
2567
|
+
description: "Select all visible rows"
|
|
2568
|
+
}
|
|
1058
2569
|
);
|
|
1059
2570
|
const refRight = hooks.useHotkey(
|
|
1060
2571
|
"right",
|
|
1061
2572
|
() => {
|
|
1062
2573
|
const { rows: r, idx, current } = getCurrent();
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
2574
|
+
const out = resolveRightArrow(current, r, idx);
|
|
2575
|
+
switch (out.kind) {
|
|
2576
|
+
case "expand":
|
|
2577
|
+
onExpand(out.id);
|
|
2578
|
+
return;
|
|
2579
|
+
case "focus":
|
|
2580
|
+
onFocus(out.id, { extend: false });
|
|
2581
|
+
return;
|
|
2582
|
+
case "noop":
|
|
2583
|
+
return;
|
|
1069
2584
|
}
|
|
1070
2585
|
},
|
|
1071
2586
|
{ enabled, preventDefault: true, description: "Expand / first child" }
|
|
@@ -1074,11 +2589,16 @@ function useTreeKeyboard({
|
|
|
1074
2589
|
"left",
|
|
1075
2590
|
() => {
|
|
1076
2591
|
const { current } = getCurrent();
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
2592
|
+
const out = resolveLeftArrow(current);
|
|
2593
|
+
switch (out.kind) {
|
|
2594
|
+
case "collapse":
|
|
2595
|
+
onCollapse(out.id);
|
|
2596
|
+
return;
|
|
2597
|
+
case "focus":
|
|
2598
|
+
onFocus(out.id, { extend: false });
|
|
2599
|
+
return;
|
|
2600
|
+
case "noop":
|
|
2601
|
+
return;
|
|
1082
2602
|
}
|
|
1083
2603
|
},
|
|
1084
2604
|
{ enabled, preventDefault: true, description: "Collapse / parent" }
|
|
@@ -1087,13 +2607,14 @@ function useTreeKeyboard({
|
|
|
1087
2607
|
["enter", "space"],
|
|
1088
2608
|
() => {
|
|
1089
2609
|
const { current } = getCurrent();
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
2610
|
+
const out = resolveActivate(current);
|
|
2611
|
+
if (out.kind === "noop") return;
|
|
2612
|
+
onSelect(out.kind === "activate-leaf" ? out.id : out.id);
|
|
2613
|
+
if (out.kind === "toggle-folder") {
|
|
2614
|
+
if (out.willExpand) onExpand(out.id);
|
|
2615
|
+
else onCollapse(out.id);
|
|
1095
2616
|
} else {
|
|
1096
|
-
onActivate(
|
|
2617
|
+
onActivate(out.id);
|
|
1097
2618
|
}
|
|
1098
2619
|
},
|
|
1099
2620
|
{ enabled, preventDefault: true, description: "Activate / toggle" }
|
|
@@ -1113,12 +2634,44 @@ function useTreeKeyboard({
|
|
|
1113
2634
|
refLeft(instance);
|
|
1114
2635
|
refActivate(instance);
|
|
1115
2636
|
refEscape(instance);
|
|
2637
|
+
refSelectAll(instance);
|
|
1116
2638
|
},
|
|
1117
|
-
[
|
|
2639
|
+
[
|
|
2640
|
+
refDown,
|
|
2641
|
+
refUp,
|
|
2642
|
+
refHome,
|
|
2643
|
+
refEnd,
|
|
2644
|
+
refRight,
|
|
2645
|
+
refLeft,
|
|
2646
|
+
refActivate,
|
|
2647
|
+
refEscape,
|
|
2648
|
+
refSelectAll
|
|
2649
|
+
]
|
|
1118
2650
|
);
|
|
1119
2651
|
return { ref };
|
|
1120
2652
|
}
|
|
1121
2653
|
chunkPK6SKIKE_cjs.__name(useTreeKeyboard, "useTreeKeyboard");
|
|
2654
|
+
|
|
2655
|
+
// src/tools/data/Tree/hooks/type-ahead/match-prefix.ts
|
|
2656
|
+
function findRowByPrefix(rows, getName, prefix) {
|
|
2657
|
+
if (prefix.length === 0) return void 0;
|
|
2658
|
+
return rows.find((row) => getName(row.node).toLowerCase().startsWith(prefix));
|
|
2659
|
+
}
|
|
2660
|
+
chunkPK6SKIKE_cjs.__name(findRowByPrefix, "findRowByPrefix");
|
|
2661
|
+
function isResetKey(key) {
|
|
2662
|
+
return key === "Escape" || key === "Enter" || key === "Tab" || key.startsWith("Arrow") || key === "Home" || key === "End" || key === "PageUp" || key === "PageDown";
|
|
2663
|
+
}
|
|
2664
|
+
chunkPK6SKIKE_cjs.__name(isResetKey, "isResetKey");
|
|
2665
|
+
function isTypingTarget(target) {
|
|
2666
|
+
const el = target;
|
|
2667
|
+
if (!el) return false;
|
|
2668
|
+
const tag = el.tagName;
|
|
2669
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
2670
|
+
return el.isContentEditable === true;
|
|
2671
|
+
}
|
|
2672
|
+
chunkPK6SKIKE_cjs.__name(isTypingTarget, "isTypingTarget");
|
|
2673
|
+
|
|
2674
|
+
// src/tools/data/Tree/hooks/type-ahead/use-tree-type-ahead.ts
|
|
1122
2675
|
var FLUSH_MS = 600;
|
|
1123
2676
|
function useTreeTypeAhead({
|
|
1124
2677
|
rows,
|
|
@@ -1147,11 +2700,9 @@ function useTreeTypeAhead({
|
|
|
1147
2700
|
}
|
|
1148
2701
|
}, "reset");
|
|
1149
2702
|
const handler = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((e) => {
|
|
1150
|
-
|
|
1151
|
-
if (tag === "INPUT" || tag === "TEXTAREA") return;
|
|
1152
|
-
if (e.target?.isContentEditable) return;
|
|
2703
|
+
if (isTypingTarget(e.target)) return;
|
|
1153
2704
|
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
|
1154
|
-
if (
|
|
2705
|
+
if (isResetKey(e.key)) {
|
|
1155
2706
|
reset();
|
|
1156
2707
|
return;
|
|
1157
2708
|
}
|
|
@@ -1159,9 +2710,10 @@ function useTreeTypeAhead({
|
|
|
1159
2710
|
bufferRef.current += e.key.toLowerCase();
|
|
1160
2711
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
1161
2712
|
timerRef.current = setTimeout(reset, FLUSH_MS);
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1164
|
-
|
|
2713
|
+
const hit = findRowByPrefix(
|
|
2714
|
+
rowsRef.current,
|
|
2715
|
+
getNameRef.current,
|
|
2716
|
+
bufferRef.current
|
|
1165
2717
|
);
|
|
1166
2718
|
if (hit) {
|
|
1167
2719
|
e.preventDefault();
|
|
@@ -1176,36 +2728,125 @@ function useTreeTypeAhead({
|
|
|
1176
2728
|
}, [containerRef, enabled]);
|
|
1177
2729
|
}
|
|
1178
2730
|
chunkPK6SKIKE_cjs.__name(useTreeTypeAhead, "useTreeTypeAhead");
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) : null,
|
|
1198
|
-
item.label,
|
|
1199
|
-
item.shortcut ? /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuShortcut, { children: item.shortcut }) : null
|
|
1200
|
-
]
|
|
1201
|
-
},
|
|
1202
|
-
item.id
|
|
1203
|
-
);
|
|
1204
|
-
}) })
|
|
1205
|
-
] });
|
|
2731
|
+
|
|
2732
|
+
// src/tools/data/Tree/hooks/finder-hotkeys/build-ctx.ts
|
|
2733
|
+
function buildBuiltinCtx(input) {
|
|
2734
|
+
if (!input.adapter) return null;
|
|
2735
|
+
const selectedNodes = [];
|
|
2736
|
+
for (const id of input.selected) {
|
|
2737
|
+
const n = input.getNodeById(id);
|
|
2738
|
+
if (n) selectedNodes.push(n);
|
|
2739
|
+
}
|
|
2740
|
+
const targetNode = input.focused ? input.getNodeById(input.focused) ?? null : null;
|
|
2741
|
+
return {
|
|
2742
|
+
adapter: input.adapter,
|
|
2743
|
+
labels: input.labels,
|
|
2744
|
+
selectedNodes,
|
|
2745
|
+
targetNode,
|
|
2746
|
+
getName: input.getItemName,
|
|
2747
|
+
startInlineRename: input.startInlineRename,
|
|
2748
|
+
clipboard: input.clipboard
|
|
1206
2749
|
};
|
|
1207
2750
|
}
|
|
1208
|
-
chunkPK6SKIKE_cjs.__name(
|
|
2751
|
+
chunkPK6SKIKE_cjs.__name(buildBuiltinCtx, "buildBuiltinCtx");
|
|
2752
|
+
|
|
2753
|
+
// src/tools/data/Tree/hooks/finder-hotkeys/use-tree-finder-hotkeys.ts
|
|
2754
|
+
function useTreeFinderHotkeys(opts) {
|
|
2755
|
+
const optsRef = React.useRef(opts);
|
|
2756
|
+
optsRef.current = opts;
|
|
2757
|
+
const run = React.useCallback(async (action) => {
|
|
2758
|
+
const o = optsRef.current;
|
|
2759
|
+
if (o.paused) return;
|
|
2760
|
+
const ctx = buildBuiltinCtx({
|
|
2761
|
+
adapter: o.adapter,
|
|
2762
|
+
labels: o.labels,
|
|
2763
|
+
selected: o.selected,
|
|
2764
|
+
focused: o.focused,
|
|
2765
|
+
getNodeById: o.getNodeById,
|
|
2766
|
+
getItemName: o.getItemName,
|
|
2767
|
+
startInlineRename: o.startInlineRename,
|
|
2768
|
+
clipboard: o.clipboard
|
|
2769
|
+
});
|
|
2770
|
+
if (!ctx) return;
|
|
2771
|
+
await runBuiltinAction(action, ctx);
|
|
2772
|
+
}, []);
|
|
2773
|
+
const refDelete = hooks.useHotkey(
|
|
2774
|
+
["mod+backspace", "delete"],
|
|
2775
|
+
() => void run("delete"),
|
|
2776
|
+
{
|
|
2777
|
+
enabled: opts.enabled,
|
|
2778
|
+
preventDefault: true,
|
|
2779
|
+
description: "Delete selected items",
|
|
2780
|
+
scope: "tree"
|
|
2781
|
+
}
|
|
2782
|
+
);
|
|
2783
|
+
const refRename = hooks.useHotkey("f2", () => void run("rename"), {
|
|
2784
|
+
enabled: opts.enabled,
|
|
2785
|
+
preventDefault: true,
|
|
2786
|
+
description: "Rename selected item",
|
|
2787
|
+
scope: "tree"
|
|
2788
|
+
});
|
|
2789
|
+
const refDuplicate = hooks.useHotkey("mod+d", () => void run("duplicate"), {
|
|
2790
|
+
enabled: opts.enabled,
|
|
2791
|
+
preventDefault: true,
|
|
2792
|
+
description: "Duplicate selected items",
|
|
2793
|
+
scope: "tree"
|
|
2794
|
+
});
|
|
2795
|
+
const refNewFolder = hooks.useHotkey("mod+shift+n", () => void run("new-folder"), {
|
|
2796
|
+
enabled: opts.enabled,
|
|
2797
|
+
preventDefault: true,
|
|
2798
|
+
description: "New folder",
|
|
2799
|
+
scope: "tree"
|
|
2800
|
+
});
|
|
2801
|
+
const refNewFile = hooks.useHotkey("mod+n", () => void run("new-file"), {
|
|
2802
|
+
enabled: opts.enabled,
|
|
2803
|
+
preventDefault: true,
|
|
2804
|
+
description: "New file",
|
|
2805
|
+
scope: "tree"
|
|
2806
|
+
});
|
|
2807
|
+
const refCut = hooks.useHotkey("mod+x", () => void run("cut"), {
|
|
2808
|
+
enabled: opts.enabled,
|
|
2809
|
+
preventDefault: true,
|
|
2810
|
+
description: "Cut",
|
|
2811
|
+
scope: "tree"
|
|
2812
|
+
});
|
|
2813
|
+
const refCopy = hooks.useHotkey("mod+c", () => void run("copy"), {
|
|
2814
|
+
enabled: opts.enabled,
|
|
2815
|
+
preventDefault: true,
|
|
2816
|
+
description: "Copy",
|
|
2817
|
+
scope: "tree"
|
|
2818
|
+
});
|
|
2819
|
+
const refPaste = hooks.useHotkey("mod+v", () => void run("paste"), {
|
|
2820
|
+
enabled: opts.enabled,
|
|
2821
|
+
preventDefault: true,
|
|
2822
|
+
description: "Paste",
|
|
2823
|
+
scope: "tree"
|
|
2824
|
+
});
|
|
2825
|
+
const ref = React.useCallback(
|
|
2826
|
+
(instance) => {
|
|
2827
|
+
refDelete(instance);
|
|
2828
|
+
refRename(instance);
|
|
2829
|
+
refDuplicate(instance);
|
|
2830
|
+
refNewFolder(instance);
|
|
2831
|
+
refNewFile(instance);
|
|
2832
|
+
refCut(instance);
|
|
2833
|
+
refCopy(instance);
|
|
2834
|
+
refPaste(instance);
|
|
2835
|
+
},
|
|
2836
|
+
[
|
|
2837
|
+
refDelete,
|
|
2838
|
+
refRename,
|
|
2839
|
+
refDuplicate,
|
|
2840
|
+
refNewFolder,
|
|
2841
|
+
refNewFile,
|
|
2842
|
+
refCut,
|
|
2843
|
+
refCopy,
|
|
2844
|
+
refPaste
|
|
2845
|
+
]
|
|
2846
|
+
);
|
|
2847
|
+
return { ref };
|
|
2848
|
+
}
|
|
2849
|
+
chunkPK6SKIKE_cjs.__name(useTreeFinderHotkeys, "useTreeFinderHotkeys");
|
|
1209
2850
|
function TreeRoot(props) {
|
|
1210
2851
|
const {
|
|
1211
2852
|
data,
|
|
@@ -1224,6 +2865,10 @@ function TreeRoot(props) {
|
|
|
1224
2865
|
enableSearch = false,
|
|
1225
2866
|
enableTypeAhead = true,
|
|
1226
2867
|
showIndentGuides = false,
|
|
2868
|
+
enableInlineRename = false,
|
|
2869
|
+
enableFinderHotkeys = false,
|
|
2870
|
+
enableDnD = false,
|
|
2871
|
+
canDrop,
|
|
1227
2872
|
renderRow,
|
|
1228
2873
|
renderIcon,
|
|
1229
2874
|
renderLabel,
|
|
@@ -1233,14 +2878,12 @@ function TreeRoot(props) {
|
|
|
1233
2878
|
labels,
|
|
1234
2879
|
persistKey,
|
|
1235
2880
|
persistSelection = false,
|
|
2881
|
+
adapter,
|
|
2882
|
+
defaultMenuItems,
|
|
2883
|
+
actionsRef,
|
|
1236
2884
|
className,
|
|
1237
2885
|
style
|
|
1238
2886
|
} = props;
|
|
1239
|
-
const resolvedRenderContextMenu = React.useMemo(() => {
|
|
1240
|
-
if (renderContextMenu) return renderContextMenu;
|
|
1241
|
-
if (contextMenuActions) return actionsToRenderContextMenu(contextMenuActions);
|
|
1242
|
-
return void 0;
|
|
1243
|
-
}, [renderContextMenu, contextMenuActions]);
|
|
1244
2887
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1245
2888
|
TreeProvider,
|
|
1246
2889
|
{
|
|
@@ -1262,7 +2905,13 @@ function TreeRoot(props) {
|
|
|
1262
2905
|
renderIcon,
|
|
1263
2906
|
renderLabel,
|
|
1264
2907
|
renderActions,
|
|
1265
|
-
renderContextMenu
|
|
2908
|
+
renderContextMenu,
|
|
2909
|
+
contextMenuActions,
|
|
2910
|
+
adapter,
|
|
2911
|
+
defaultMenuItems,
|
|
2912
|
+
enableInlineRename,
|
|
2913
|
+
enableDnD,
|
|
2914
|
+
canDrop,
|
|
1266
2915
|
labels,
|
|
1267
2916
|
persistKey,
|
|
1268
2917
|
persistSelection,
|
|
@@ -1273,7 +2922,9 @@ function TreeRoot(props) {
|
|
|
1273
2922
|
style,
|
|
1274
2923
|
enableSearch,
|
|
1275
2924
|
enableTypeAhead,
|
|
1276
|
-
|
|
2925
|
+
enableFinderHotkeys,
|
|
2926
|
+
renderRow,
|
|
2927
|
+
actionsRef
|
|
1277
2928
|
}
|
|
1278
2929
|
)
|
|
1279
2930
|
}
|
|
@@ -1285,14 +2936,46 @@ function TreeRootShell({
|
|
|
1285
2936
|
style,
|
|
1286
2937
|
enableSearch,
|
|
1287
2938
|
enableTypeAhead,
|
|
1288
|
-
|
|
2939
|
+
enableFinderHotkeys,
|
|
2940
|
+
renderRow,
|
|
2941
|
+
actionsRef
|
|
1289
2942
|
}) {
|
|
1290
2943
|
const containerRef = React.useRef(null);
|
|
1291
2944
|
const ctx = useTreeContext();
|
|
2945
|
+
React.useEffect(() => {
|
|
2946
|
+
if (!actionsRef) return;
|
|
2947
|
+
actionsRef.current = {
|
|
2948
|
+
refresh: ctx.refresh,
|
|
2949
|
+
refreshAll: ctx.refreshAll,
|
|
2950
|
+
expandAll: ctx.expandAll,
|
|
2951
|
+
collapseAll: ctx.collapseAll
|
|
2952
|
+
};
|
|
2953
|
+
return () => {
|
|
2954
|
+
if (actionsRef.current) actionsRef.current = null;
|
|
2955
|
+
};
|
|
2956
|
+
}, [
|
|
2957
|
+
actionsRef,
|
|
2958
|
+
ctx.refresh,
|
|
2959
|
+
ctx.refreshAll,
|
|
2960
|
+
ctx.expandAll,
|
|
2961
|
+
ctx.collapseAll
|
|
2962
|
+
]);
|
|
2963
|
+
const isMulti = ctx.selectionMode === "multiple";
|
|
1292
2964
|
const { ref: keyboardRef } = useTreeKeyboard({
|
|
1293
2965
|
rows: ctx.flatRows,
|
|
1294
2966
|
focusedId: ctx.focused,
|
|
1295
|
-
|
|
2967
|
+
multiSelect: isMulti,
|
|
2968
|
+
// Pause container hotkeys while inline rename is active so the
|
|
2969
|
+
// user can type freely (TreeRenameInput stops bubbling already, but
|
|
2970
|
+
// gating here is the cleaner second line of defence).
|
|
2971
|
+
enabled: ctx.renamingId === null,
|
|
2972
|
+
onFocus: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((id, { extend }) => {
|
|
2973
|
+
if (extend && isMulti) {
|
|
2974
|
+
ctx.moveSelect(id, { extend: true });
|
|
2975
|
+
} else {
|
|
2976
|
+
ctx.setFocus(id);
|
|
2977
|
+
}
|
|
2978
|
+
}, "onFocus"),
|
|
1296
2979
|
onSelect: ctx.select,
|
|
1297
2980
|
onActivate: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((id) => {
|
|
1298
2981
|
const row = ctx.flatRows.find((r) => r.node.id === id);
|
|
@@ -1300,14 +2983,37 @@ function TreeRootShell({
|
|
|
1300
2983
|
}, "onActivate"),
|
|
1301
2984
|
onExpand: ctx.expand,
|
|
1302
2985
|
onCollapse: ctx.collapse,
|
|
1303
|
-
onClearSelection: ctx.clearSelection
|
|
2986
|
+
onClearSelection: ctx.clearSelection,
|
|
2987
|
+
onSelectAll: ctx.selectAll
|
|
2988
|
+
});
|
|
2989
|
+
const { ref: finderHotkeysRef } = useTreeFinderHotkeys({
|
|
2990
|
+
enabled: enableFinderHotkeys,
|
|
2991
|
+
paused: ctx.renamingId !== null,
|
|
2992
|
+
adapter: ctx.adapter,
|
|
2993
|
+
labels: ctx.labels,
|
|
2994
|
+
selected: ctx.selected,
|
|
2995
|
+
focused: ctx.focused,
|
|
2996
|
+
getNodeById: ctx.getNodeById,
|
|
2997
|
+
getItemName: ctx.getItemName,
|
|
2998
|
+
startInlineRename: ctx.inlineRenameEnabled ? ctx.startRename : void 0,
|
|
2999
|
+
clipboard: {
|
|
3000
|
+
hasItems: !!ctx.clipboard && ctx.clipboard.ids.length > 0,
|
|
3001
|
+
cut: ctx.cutToClipboard,
|
|
3002
|
+
copy: ctx.copyToClipboard,
|
|
3003
|
+
// Hotkey paste targets the currently focused row (or null = root).
|
|
3004
|
+
paste: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => {
|
|
3005
|
+
const target = ctx.focused ? ctx.getNodeById(ctx.focused) ?? null : null;
|
|
3006
|
+
return ctx.pasteFromClipboard(target, "inside");
|
|
3007
|
+
}, "paste")
|
|
3008
|
+
}
|
|
1304
3009
|
});
|
|
1305
3010
|
const setContainerRef = React.useCallback(
|
|
1306
3011
|
(instance) => {
|
|
1307
3012
|
containerRef.current = instance;
|
|
1308
3013
|
keyboardRef(instance);
|
|
3014
|
+
finderHotkeysRef(instance);
|
|
1309
3015
|
},
|
|
1310
|
-
[keyboardRef]
|
|
3016
|
+
[keyboardRef, finderHotkeysRef]
|
|
1311
3017
|
);
|
|
1312
3018
|
const focusedId = ctx.focused;
|
|
1313
3019
|
React.useEffect(() => {
|
|
@@ -1330,7 +3036,7 @@ function TreeRootShell({
|
|
|
1330
3036
|
onMatch: onTypeAheadMatch,
|
|
1331
3037
|
enabled: enableTypeAhead
|
|
1332
3038
|
});
|
|
1333
|
-
|
|
3039
|
+
const treeBody = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1334
3040
|
"div",
|
|
1335
3041
|
{
|
|
1336
3042
|
ref: setContainerRef,
|
|
@@ -1348,13 +3054,34 @@ function TreeRootShell({
|
|
|
1348
3054
|
"data-tree-root": "",
|
|
1349
3055
|
children: [
|
|
1350
3056
|
enableSearch ? /* @__PURE__ */ jsxRuntime.jsx(TreeSearchInput, { className: "mx-2 mt-2" }) : null,
|
|
1351
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3057
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 flex-col overflow-auto px-1", children: [
|
|
3058
|
+
/* @__PURE__ */ jsxRuntime.jsx(TreeContent, { role: "group", children: renderRow }),
|
|
3059
|
+
/* @__PURE__ */ jsxRuntime.jsx(TreeEmptyArea, {})
|
|
3060
|
+
] })
|
|
1352
3061
|
]
|
|
1353
3062
|
}
|
|
1354
3063
|
);
|
|
3064
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TreeDndProvider, { children: treeBody });
|
|
1355
3065
|
}
|
|
1356
3066
|
chunkPK6SKIKE_cjs.__name(TreeRootShell, "TreeRootShell");
|
|
1357
3067
|
var TreeRoot_default = TreeRoot;
|
|
3068
|
+
function FinderTree(props) {
|
|
3069
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3070
|
+
TreeRoot,
|
|
3071
|
+
{
|
|
3072
|
+
selectionMode: "multiple",
|
|
3073
|
+
activationMode: "double-click",
|
|
3074
|
+
enableInlineRename: true,
|
|
3075
|
+
enableFinderHotkeys: true,
|
|
3076
|
+
enableDnD: true,
|
|
3077
|
+
enableTypeAhead: true,
|
|
3078
|
+
showIndentGuides: true,
|
|
3079
|
+
appearance: { density: "cozy" },
|
|
3080
|
+
...props
|
|
3081
|
+
}
|
|
3082
|
+
);
|
|
3083
|
+
}
|
|
3084
|
+
chunkPK6SKIKE_cjs.__name(FinderTree, "FinderTree");
|
|
1358
3085
|
function TreeSkeleton({ rows = 6, className }) {
|
|
1359
3086
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: lib.cn("flex flex-col gap-1 p-2", className), "aria-hidden": true, children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", style: { paddingLeft: i % 3 * 16 }, children: [
|
|
1360
3087
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-4 shrink-0 animate-pulse rounded bg-muted" }),
|
|
@@ -1408,35 +3135,48 @@ chunkPK6SKIKE_cjs.__name(createDemoTree, "createDemoTree");
|
|
|
1408
3135
|
|
|
1409
3136
|
exports.DEFAULT_TREE_APPEARANCE = DEFAULT_TREE_APPEARANCE;
|
|
1410
3137
|
exports.DEFAULT_TREE_LABELS = DEFAULT_TREE_LABELS;
|
|
3138
|
+
exports.FinderTree = FinderTree;
|
|
3139
|
+
exports.TREE_DND_MIME = TREE_DND_MIME;
|
|
1411
3140
|
exports.Tree = TreeRoot;
|
|
1412
3141
|
exports.TreeChevron = TreeChevron;
|
|
1413
3142
|
exports.TreeContent = TreeContent;
|
|
3143
|
+
exports.TreeDropIndicator = TreeDropIndicator;
|
|
1414
3144
|
exports.TreeEmpty = TreeEmpty;
|
|
3145
|
+
exports.TreeEmptyArea = TreeEmptyArea;
|
|
1415
3146
|
exports.TreeError = TreeError;
|
|
1416
3147
|
exports.TreeIcon = TreeIcon;
|
|
1417
3148
|
exports.TreeIndentGuides = TreeIndentGuides;
|
|
1418
3149
|
exports.TreeLabel = TreeLabel;
|
|
1419
3150
|
exports.TreeProvider = TreeProvider;
|
|
3151
|
+
exports.TreeRenameInput = TreeRenameInput;
|
|
1420
3152
|
exports.TreeRoot = TreeRoot;
|
|
1421
3153
|
exports.TreeRow = TreeRow;
|
|
1422
3154
|
exports.TreeSearchInput = TreeSearchInput;
|
|
1423
3155
|
exports.TreeSkeleton = TreeSkeleton;
|
|
1424
3156
|
exports.appearanceToStyle = appearanceToStyle;
|
|
3157
|
+
exports.autoSelectRange = autoSelectRange;
|
|
1425
3158
|
exports.clearTreeState = clearTreeState;
|
|
1426
3159
|
exports.createChildCache = createChildCache;
|
|
1427
3160
|
exports.createDemoTree = createDemoTree;
|
|
1428
3161
|
exports.default = TreeRoot_default;
|
|
3162
|
+
exports.defaultCanDrop = defaultCanDrop;
|
|
1429
3163
|
exports.flattenTree = flattenTree;
|
|
1430
3164
|
exports.loadTreeState = loadTreeState;
|
|
1431
3165
|
exports.resolveAppearance = resolveAppearance;
|
|
1432
3166
|
exports.resolveChildren = resolveChildren;
|
|
3167
|
+
exports.resolveDropZone = resolveDropZone;
|
|
1433
3168
|
exports.saveTreeState = saveTreeState;
|
|
3169
|
+
exports.splitFileName = splitFileName;
|
|
1434
3170
|
exports.useTreeActions = useTreeActions;
|
|
3171
|
+
exports.useTreeClipboard = useTreeClipboard;
|
|
1435
3172
|
exports.useTreeContext = useTreeContext;
|
|
3173
|
+
exports.useTreeDnd = useTreeDnd;
|
|
1436
3174
|
exports.useTreeExpansion = useTreeExpansion;
|
|
3175
|
+
exports.useTreeFinderHotkeys = useTreeFinderHotkeys;
|
|
1437
3176
|
exports.useTreeFocus = useTreeFocus;
|
|
1438
3177
|
exports.useTreeKeyboard = useTreeKeyboard;
|
|
1439
3178
|
exports.useTreeLabels = useTreeLabels;
|
|
3179
|
+
exports.useTreeRename = useTreeRename;
|
|
1440
3180
|
exports.useTreeRows = useTreeRows;
|
|
1441
3181
|
exports.useTreeSearch = useTreeSearch;
|
|
1442
3182
|
exports.useTreeSelection = useTreeSelection;
|