@djangocfg/ui-tools 2.1.413 → 2.1.416
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/file-icon/index.d.cts +1 -1
- package/dist/file-icon/index.d.ts +1 -1
- package/dist/slots-ClRpIzoh.d.cts +88 -0
- package/dist/slots-ClRpIzoh.d.ts +88 -0
- package/dist/tree/index.cjs +1994 -276
- package/dist/tree/index.cjs.map +1 -1
- package/dist/tree/index.d.cts +717 -72
- package/dist/tree/index.d.ts +717 -72
- package/dist/tree/index.mjs +1984 -279
- package/dist/tree/index.mjs.map +1 -1
- package/package.json +10 -6
- package/src/tools/chat/README.md +111 -1
- package/src/tools/chat/composer/Composer.tsx +138 -17
- package/src/tools/chat/composer/ComposerRichTextarea.tsx +25 -0
- package/src/tools/chat/composer/index.ts +22 -0
- package/src/tools/chat/composer/slash/README.md +187 -0
- package/src/tools/chat/composer/slash/SlashHighlightTextarea.tsx +144 -0
- package/src/tools/chat/composer/slash/SlashMenu.tsx +142 -0
- package/src/tools/chat/composer/slash/SlashToken.tsx +57 -0
- package/src/tools/chat/composer/slash/index.ts +44 -0
- package/src/tools/chat/composer/slash/labels.ts +19 -0
- package/src/tools/chat/composer/slash/state.ts +168 -0
- package/src/tools/chat/composer/slash/types.ts +64 -0
- package/src/tools/chat/composer/slash/useSlashCommands.ts +204 -0
- package/src/tools/chat/composer/types.ts +8 -0
- package/src/tools/chat/shell/SuggestedPrompts.tsx +194 -0
- package/src/tools/chat/shell/index.ts +6 -0
- package/src/tools/data/Listbox/lazy.tsx +1 -1
- package/src/tools/data/Masonry/lazy.tsx +1 -1
- package/src/tools/data/Timeline/lazy.tsx +1 -1
- package/src/tools/data/Tree/FinderTree.tsx +42 -0
- package/src/tools/data/Tree/README.md +337 -208
- package/src/tools/data/Tree/TreeDndProvider.tsx +137 -0
- package/src/tools/data/Tree/TreeRoot.tsx +170 -55
- package/src/tools/data/Tree/__tests__/dnd.test.ts +160 -0
- package/src/tools/data/Tree/__tests__/keyboard.test.ts +137 -0
- package/src/tools/data/Tree/__tests__/renameUtils.test.ts +52 -0
- package/src/tools/data/Tree/__tests__/selection.test.ts +227 -0
- package/src/tools/data/Tree/components/TreeDropIndicator.tsx +65 -0
- package/src/tools/data/Tree/components/TreeEmptyArea.tsx +160 -0
- package/src/tools/data/Tree/components/TreeRenameInput.tsx +114 -0
- package/src/tools/data/Tree/components/TreeRow.tsx +92 -8
- package/src/tools/data/Tree/components/index.ts +6 -0
- package/src/tools/data/Tree/context/TreeContext.tsx +204 -363
- package/src/tools/data/Tree/context/TreeContextValue.ts +139 -0
- package/src/tools/data/Tree/context/async-children/collect-ids.ts +27 -0
- package/src/tools/data/Tree/context/async-children/index.ts +8 -0
- package/src/tools/data/Tree/context/async-children/use-async-children.ts +157 -0
- package/src/tools/data/Tree/context/clipboard/index.ts +4 -0
- package/src/tools/data/Tree/context/clipboard/use-clipboard.ts +115 -0
- package/src/tools/data/Tree/context/dnd/index.ts +8 -0
- package/src/tools/data/Tree/context/dnd/use-dnd.ts +194 -0
- package/src/tools/data/Tree/context/expansion/index.ts +4 -0
- package/src/tools/data/Tree/context/expansion/use-expansion.ts +55 -0
- package/src/tools/data/Tree/context/hooks.ts +68 -1
- package/src/tools/data/Tree/context/index.ts +3 -0
- package/src/tools/data/Tree/context/menu/builtin-actions.ts +357 -0
- package/src/tools/data/Tree/context/menu/index.ts +10 -0
- package/src/tools/data/Tree/context/menu/use-resolved-menu.ts +127 -0
- package/src/tools/data/Tree/context/persist/index.ts +4 -0
- package/src/tools/data/Tree/context/persist/use-persist-sync.ts +74 -0
- package/src/tools/data/Tree/context/rename/index.ts +4 -0
- package/src/tools/data/Tree/context/rename/use-rename.ts +113 -0
- package/src/tools/data/Tree/context/selection/index.ts +4 -0
- package/src/tools/data/Tree/context/selection/use-selection.ts +146 -0
- package/src/tools/data/Tree/context/state/index.ts +6 -0
- package/src/tools/data/Tree/context/state/initial.ts +41 -0
- package/src/tools/data/Tree/context/state/reducer.ts +76 -0
- package/src/tools/data/Tree/context/state/types.ts +46 -0
- package/src/tools/data/Tree/data/clipboard.ts +33 -0
- package/src/tools/data/Tree/data/dnd.ts +123 -0
- package/src/tools/data/Tree/data/finderShortcuts.ts +67 -0
- package/src/tools/data/Tree/data/index.ts +19 -0
- package/src/tools/data/Tree/data/renameUtils.ts +51 -0
- package/src/tools/data/Tree/data/selection.ts +157 -0
- package/src/tools/data/Tree/hooks/finder-hotkeys/build-ctx.ts +48 -0
- package/src/tools/data/Tree/hooks/finder-hotkeys/index.ts +8 -0
- package/src/tools/data/Tree/hooks/finder-hotkeys/use-tree-finder-hotkeys.ts +166 -0
- package/src/tools/data/Tree/hooks/index.ts +23 -4
- package/src/tools/data/Tree/hooks/keyboard/activation.ts +27 -0
- package/src/tools/data/Tree/hooks/keyboard/arrow-nav.ts +26 -0
- package/src/tools/data/Tree/hooks/keyboard/expand-collapse.ts +54 -0
- package/src/tools/data/Tree/hooks/keyboard/index.ts +10 -0
- package/src/tools/data/Tree/hooks/keyboard/types.ts +39 -0
- package/src/tools/data/Tree/hooks/keyboard/use-tree-keyboard.ts +196 -0
- package/src/tools/data/Tree/hooks/type-ahead/index.ts +5 -0
- package/src/tools/data/Tree/hooks/type-ahead/match-prefix.ts +42 -0
- package/src/tools/data/Tree/hooks/{useTreeTypeAhead.ts → type-ahead/use-tree-type-ahead.ts} +8 -19
- package/src/tools/data/Tree/index.tsx +25 -2
- package/src/tools/data/Tree/types/activation.ts +30 -0
- package/src/tools/data/Tree/types/adapter.ts +70 -0
- package/src/tools/data/Tree/types/index.ts +27 -0
- package/src/tools/data/Tree/types/labels.ts +97 -0
- package/src/tools/data/Tree/types/loader.ts +9 -0
- package/src/tools/data/Tree/types/node.ts +38 -0
- package/src/tools/data/Tree/types/root-props.ts +142 -0
- package/src/tools/data/Tree/types/selection.ts +3 -0
- package/src/tools/data/Tree/types/slots.ts +64 -0
- package/src/tools/dev/OpenapiViewer/components/DocsLayout/EndpointDoc/Responses/ResponseBody.tsx +1 -1
- package/src/tools/dev/OpenapiViewer/components/shared/ResponsePanel/PrettyView.tsx +1 -1
- 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/index.ts +2 -2
- 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
|
@@ -6,8 +6,10 @@ var chunkPK6SKIKE_cjs = require('../chunk-PK6SKIKE.cjs');
|
|
|
6
6
|
var React = require('react');
|
|
7
7
|
var lib = require('@djangocfg/ui-core/lib');
|
|
8
8
|
var components = require('@djangocfg/ui-core/components');
|
|
9
|
-
var
|
|
9
|
+
var dialogService = require('@djangocfg/ui-core/lib/dialog-service');
|
|
10
10
|
var lucideReact = require('lucide-react');
|
|
11
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
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,1056 @@ 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;
|
|
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;
|
|
985
|
+
}
|
|
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
|
+
|
|
1075
|
+
// src/tools/data/Tree/data/dnd.ts
|
|
1076
|
+
function resolveDropZone(input) {
|
|
1077
|
+
const { pointerY, rowRect, isFolder } = input;
|
|
1078
|
+
const offset = pointerY - rowRect.top;
|
|
1079
|
+
const ratio = rowRect.height > 0 ? offset / rowRect.height : 0.5;
|
|
1080
|
+
if (isFolder) {
|
|
1081
|
+
if (ratio < 0.33) return "before";
|
|
1082
|
+
if (ratio > 0.66) return "after";
|
|
1083
|
+
return "inside";
|
|
1084
|
+
}
|
|
1085
|
+
return ratio < 0.5 ? "before" : "after";
|
|
1086
|
+
}
|
|
1087
|
+
chunkPK6SKIKE_cjs.__name(resolveDropZone, "resolveDropZone");
|
|
1088
|
+
function defaultCanDrop(input) {
|
|
1089
|
+
const { source, target, position } = input;
|
|
1090
|
+
if (source.length === 0) return false;
|
|
1091
|
+
if (!target) return true;
|
|
1092
|
+
if (position === "inside") {
|
|
1093
|
+
const isFolder = Array.isArray(target.children) || !!target.isFolder;
|
|
1094
|
+
if (!isFolder) return false;
|
|
1095
|
+
}
|
|
1096
|
+
for (const node of source) {
|
|
1097
|
+
if (node.id === target.id) return false;
|
|
1098
|
+
if (isDescendant(node, target.id)) return false;
|
|
1099
|
+
}
|
|
1100
|
+
return true;
|
|
1101
|
+
}
|
|
1102
|
+
chunkPK6SKIKE_cjs.__name(defaultCanDrop, "defaultCanDrop");
|
|
1103
|
+
function isDescendant(root, id) {
|
|
1104
|
+
if (!Array.isArray(root.children)) return false;
|
|
1105
|
+
for (const child of root.children) {
|
|
1106
|
+
if (child.id === id) return true;
|
|
1107
|
+
if (isDescendant(child, id)) return true;
|
|
1108
|
+
}
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1111
|
+
chunkPK6SKIKE_cjs.__name(isDescendant, "isDescendant");
|
|
1112
|
+
var TREE_DND_MIME = "application/x-djangocfg-tree";
|
|
1113
|
+
var TREE_ROOT_DROP_ID = "__tree_root_drop__";
|
|
1114
|
+
|
|
1115
|
+
// src/tools/data/Tree/context/dnd/use-dnd.ts
|
|
1116
|
+
function useDnd({
|
|
1117
|
+
enabled,
|
|
1118
|
+
adapter,
|
|
1119
|
+
nodeById,
|
|
1120
|
+
selected,
|
|
1121
|
+
labels,
|
|
1122
|
+
canDrop
|
|
1123
|
+
}) {
|
|
1124
|
+
const active = enabled && !!adapter?.move;
|
|
1125
|
+
const [draggingIds, setDraggingIds] = React.useState(
|
|
1126
|
+
() => /* @__PURE__ */ new Set()
|
|
1127
|
+
);
|
|
1128
|
+
const [dropTarget, setDropTarget] = React.useState(null);
|
|
1129
|
+
const beginDrag = React.useCallback(
|
|
1130
|
+
(rowId) => {
|
|
1131
|
+
if (!active) return;
|
|
1132
|
+
const ids = selected.has(rowId) ? new Set(selected) : /* @__PURE__ */ new Set([rowId]);
|
|
1133
|
+
setDraggingIds(ids);
|
|
1134
|
+
},
|
|
1135
|
+
[active, selected]
|
|
1136
|
+
);
|
|
1137
|
+
const cancelDrag = React.useCallback(() => {
|
|
1138
|
+
setDraggingIds(/* @__PURE__ */ new Set());
|
|
1139
|
+
setDropTarget(null);
|
|
1140
|
+
}, []);
|
|
1141
|
+
const resolveSourceNodes = React.useCallback(() => {
|
|
1142
|
+
const out = [];
|
|
1143
|
+
for (const id of draggingIds) {
|
|
1144
|
+
const node = nodeById.get(id);
|
|
1145
|
+
if (node) out.push(node);
|
|
1146
|
+
}
|
|
1147
|
+
return out;
|
|
1148
|
+
}, [draggingIds, nodeById]);
|
|
1149
|
+
const isAllowedDrop = React.useCallback(
|
|
1150
|
+
(target, position) => {
|
|
1151
|
+
if (!active) return false;
|
|
1152
|
+
if (draggingIds.size === 0) return false;
|
|
1153
|
+
const source = resolveSourceNodes();
|
|
1154
|
+
if (!defaultCanDrop({
|
|
1155
|
+
source,
|
|
1156
|
+
target,
|
|
1157
|
+
position})) {
|
|
1158
|
+
return false;
|
|
421
1159
|
}
|
|
1160
|
+
return canDrop?.({ source, target, position }) ?? true;
|
|
1161
|
+
},
|
|
1162
|
+
[active, draggingIds, resolveSourceNodes, nodeById, canDrop]
|
|
1163
|
+
);
|
|
1164
|
+
const commitDrop = React.useCallback(async () => {
|
|
1165
|
+
if (!active || !adapter?.move) {
|
|
1166
|
+
cancelDrag();
|
|
1167
|
+
return;
|
|
422
1168
|
}
|
|
423
|
-
|
|
424
|
-
|
|
1169
|
+
const t = dropTarget;
|
|
1170
|
+
if (!t) {
|
|
1171
|
+
cancelDrag();
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const targetNode = t.id ? nodeById.get(t.id) ?? null : null;
|
|
1175
|
+
const source = resolveSourceNodes();
|
|
1176
|
+
if (source.length === 0) {
|
|
1177
|
+
cancelDrag();
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
if (!isAllowedDrop(targetNode, t.position)) {
|
|
1181
|
+
cancelDrag();
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
try {
|
|
1185
|
+
await adapter.move(source, targetNode, t.position);
|
|
1186
|
+
} catch (e) {
|
|
1187
|
+
const dialog = dialogService.getDialog();
|
|
1188
|
+
await dialog?.alert({
|
|
1189
|
+
title: labels.error,
|
|
1190
|
+
message: e instanceof Error ? e.message : String(e)
|
|
1191
|
+
});
|
|
1192
|
+
} finally {
|
|
1193
|
+
cancelDrag();
|
|
1194
|
+
}
|
|
1195
|
+
}, [
|
|
1196
|
+
active,
|
|
1197
|
+
adapter,
|
|
1198
|
+
cancelDrag,
|
|
1199
|
+
dropTarget,
|
|
1200
|
+
isAllowedDrop,
|
|
1201
|
+
labels,
|
|
1202
|
+
nodeById,
|
|
1203
|
+
resolveSourceNodes
|
|
1204
|
+
]);
|
|
1205
|
+
return React.useMemo(
|
|
1206
|
+
() => ({
|
|
1207
|
+
active,
|
|
1208
|
+
draggingIds,
|
|
1209
|
+
dropTarget,
|
|
1210
|
+
beginDrag,
|
|
1211
|
+
setDropTarget,
|
|
1212
|
+
commitDrop,
|
|
1213
|
+
cancelDrag,
|
|
1214
|
+
isAllowedDrop
|
|
1215
|
+
}),
|
|
1216
|
+
[
|
|
1217
|
+
active,
|
|
1218
|
+
draggingIds,
|
|
1219
|
+
dropTarget,
|
|
1220
|
+
beginDrag,
|
|
1221
|
+
commitDrop,
|
|
1222
|
+
cancelDrag,
|
|
1223
|
+
isAllowedDrop
|
|
1224
|
+
]
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
chunkPK6SKIKE_cjs.__name(useDnd, "useDnd");
|
|
1228
|
+
function usePersistSync({
|
|
1229
|
+
expanded,
|
|
1230
|
+
selected,
|
|
1231
|
+
persistKey,
|
|
1232
|
+
persistSelection,
|
|
1233
|
+
onSelectionChange,
|
|
1234
|
+
onExpansionChange
|
|
1235
|
+
}) {
|
|
425
1236
|
const onSelectionChangeRef = React.useRef(onSelectionChange);
|
|
426
1237
|
const onExpansionChangeRef = React.useRef(onExpansionChange);
|
|
427
|
-
const onActivateRef = React.useRef(onActivate);
|
|
428
1238
|
onSelectionChangeRef.current = onSelectionChange;
|
|
429
1239
|
onExpansionChangeRef.current = onExpansionChange;
|
|
430
|
-
|
|
431
|
-
const
|
|
432
|
-
const lastExpandedArrRef = React.useRef([...state.expanded]);
|
|
1240
|
+
const lastSelectedArrRef = React.useRef([...selected]);
|
|
1241
|
+
const lastExpandedArrRef = React.useRef([...expanded]);
|
|
433
1242
|
React.useEffect(() => {
|
|
434
|
-
const arr = [...
|
|
435
|
-
if (!setEqualsArr(
|
|
1243
|
+
const arr = [...expanded];
|
|
1244
|
+
if (!setEqualsArr(expanded, lastExpandedArrRef.current)) {
|
|
436
1245
|
lastExpandedArrRef.current = arr;
|
|
437
1246
|
onExpansionChangeRef.current?.(arr);
|
|
438
1247
|
if (persistKey) {
|
|
439
1248
|
saveTreeState(persistKey, {
|
|
440
1249
|
expandedItems: arr,
|
|
441
|
-
selectedItems: persistSelection ? [...
|
|
1250
|
+
selectedItems: persistSelection ? [...selected] : []
|
|
442
1251
|
});
|
|
443
1252
|
}
|
|
444
1253
|
}
|
|
445
|
-
}, [
|
|
1254
|
+
}, [expanded, persistKey, persistSelection, selected]);
|
|
446
1255
|
React.useEffect(() => {
|
|
447
|
-
const arr = [...
|
|
448
|
-
if (!setEqualsArr(
|
|
1256
|
+
const arr = [...selected];
|
|
1257
|
+
if (!setEqualsArr(selected, lastSelectedArrRef.current)) {
|
|
449
1258
|
lastSelectedArrRef.current = arr;
|
|
450
1259
|
onSelectionChangeRef.current?.(arr);
|
|
451
1260
|
if (persistKey && persistSelection) {
|
|
452
1261
|
saveTreeState(persistKey, {
|
|
453
|
-
expandedItems: [...
|
|
1262
|
+
expandedItems: [...expanded],
|
|
454
1263
|
selectedItems: arr
|
|
455
1264
|
});
|
|
456
1265
|
}
|
|
457
1266
|
}
|
|
458
|
-
}, [
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
)
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
1267
|
+
}, [selected, persistKey, persistSelection, expanded]);
|
|
1268
|
+
}
|
|
1269
|
+
chunkPK6SKIKE_cjs.__name(usePersistSync, "usePersistSync");
|
|
1270
|
+
function setEqualsArr(set, arr) {
|
|
1271
|
+
if (set.size !== arr.length) return false;
|
|
1272
|
+
for (const id of arr) if (!set.has(id)) return false;
|
|
1273
|
+
return true;
|
|
1274
|
+
}
|
|
1275
|
+
chunkPK6SKIKE_cjs.__name(setEqualsArr, "setEqualsArr");
|
|
1276
|
+
var TreeContext = React.createContext(null);
|
|
1277
|
+
function useTreeContext() {
|
|
1278
|
+
const ctx = React__namespace.useContext(TreeContext);
|
|
1279
|
+
if (!ctx) {
|
|
1280
|
+
throw new Error("useTreeContext must be used inside <TreeProvider>");
|
|
1281
|
+
}
|
|
1282
|
+
return ctx;
|
|
1283
|
+
}
|
|
1284
|
+
chunkPK6SKIKE_cjs.__name(useTreeContext, "useTreeContext");
|
|
1285
|
+
function TreeProvider(props) {
|
|
1286
|
+
const {
|
|
1287
|
+
data,
|
|
1288
|
+
getItemName,
|
|
1289
|
+
loadChildren,
|
|
1290
|
+
selectionMode = "single",
|
|
1291
|
+
activationMode = "single-click",
|
|
1292
|
+
initialExpandedIds,
|
|
1293
|
+
initialSelectedIds,
|
|
1294
|
+
indent,
|
|
1295
|
+
appearance,
|
|
1296
|
+
onSelectionChange,
|
|
1297
|
+
onExpansionChange,
|
|
1298
|
+
onActivate,
|
|
1299
|
+
filterNode,
|
|
1300
|
+
enableSearch = false,
|
|
1301
|
+
showIndentGuides = false,
|
|
1302
|
+
renderIcon,
|
|
1303
|
+
renderLabel,
|
|
1304
|
+
renderActions,
|
|
1305
|
+
renderContextMenu,
|
|
1306
|
+
contextMenuActions,
|
|
1307
|
+
labels: labelsOverride,
|
|
1308
|
+
persistKey,
|
|
1309
|
+
persistSelection = false,
|
|
1310
|
+
adapter,
|
|
1311
|
+
defaultMenuItems,
|
|
1312
|
+
enableInlineRename = false,
|
|
1313
|
+
enableDnD = false,
|
|
1314
|
+
canDrop,
|
|
1315
|
+
children
|
|
1316
|
+
} = props;
|
|
1317
|
+
const labels = React.useMemo(
|
|
1318
|
+
() => ({ ...DEFAULT_TREE_LABELS, ...labelsOverride }),
|
|
1319
|
+
[labelsOverride]
|
|
478
1320
|
);
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
[]
|
|
1321
|
+
const resolvedAppearance = React.useMemo(
|
|
1322
|
+
() => resolveAppearance(appearance, indent),
|
|
1323
|
+
[appearance, indent]
|
|
483
1324
|
);
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const node = nodeById.get(id);
|
|
488
|
-
if (!node || !loadChildren) return;
|
|
489
|
-
cacheRef.current.delete(id);
|
|
490
|
-
dispatch({ type: "cache-tick" });
|
|
491
|
-
await fetchChildren(node);
|
|
492
|
-
},
|
|
493
|
-
[nodeById, loadChildren, fetchChildren]
|
|
1325
|
+
const persisted = React.useMemo(
|
|
1326
|
+
() => persistKey ? loadTreeState(persistKey) : null,
|
|
1327
|
+
[persistKey]
|
|
494
1328
|
);
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
1329
|
+
const [state, dispatch] = React.useReducer(
|
|
1330
|
+
reducer,
|
|
1331
|
+
void 0,
|
|
1332
|
+
() => createInitialState({
|
|
1333
|
+
persisted,
|
|
1334
|
+
initialExpandedIds,
|
|
1335
|
+
initialSelectedIds,
|
|
1336
|
+
persistSelection
|
|
1337
|
+
})
|
|
1338
|
+
);
|
|
1339
|
+
const bumpCacheTick = React.useCallback(() => dispatch({ type: "cache-tick" }), []);
|
|
1340
|
+
const {
|
|
1341
|
+
nodeById,
|
|
1342
|
+
refresh,
|
|
1343
|
+
refreshAll,
|
|
1344
|
+
collectFolderIds,
|
|
1345
|
+
cache
|
|
1346
|
+
} = useAsyncChildren({
|
|
1347
|
+
data,
|
|
1348
|
+
loadChildren,
|
|
1349
|
+
expanded: state.expanded,
|
|
1350
|
+
cacheTick: state.cacheTick,
|
|
1351
|
+
bumpCacheTick
|
|
1352
|
+
});
|
|
1353
|
+
const flatRows = React.useMemo(
|
|
1354
|
+
() => flattenTree({
|
|
1355
|
+
roots: data,
|
|
1356
|
+
expandedIds: state.expanded,
|
|
1357
|
+
cache,
|
|
1358
|
+
filterNode
|
|
1359
|
+
}),
|
|
1360
|
+
[data, state.expanded, state.cacheTick, cache, filterNode]
|
|
1361
|
+
);
|
|
1362
|
+
const matchingIds = React.useMemo(() => {
|
|
1363
|
+
const set = /* @__PURE__ */ new Set();
|
|
1364
|
+
if (!enableSearch || state.query.trim() === "") return set;
|
|
1365
|
+
const q = state.query.trim().toLowerCase();
|
|
1366
|
+
for (const row of flatRows) {
|
|
1367
|
+
if (getItemName(row.node).toLowerCase().includes(q)) {
|
|
1368
|
+
set.add(row.node.id);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
return set;
|
|
1372
|
+
}, [enableSearch, state.query, flatRows, getItemName]);
|
|
1373
|
+
const expansion = useExpansion({ dispatch, collectFolderIds });
|
|
1374
|
+
const selection = useSelection({
|
|
1375
|
+
dispatch,
|
|
1376
|
+
selectionMode,
|
|
1377
|
+
flatRows,
|
|
1378
|
+
selected: state.selected,
|
|
1379
|
+
anchor: state.anchor,
|
|
1380
|
+
focused: state.focused
|
|
1381
|
+
});
|
|
1382
|
+
const rename = useRename({
|
|
1383
|
+
dispatch,
|
|
1384
|
+
adapter,
|
|
1385
|
+
enableInlineRename,
|
|
1386
|
+
nodeById,
|
|
1387
|
+
getItemName,
|
|
1388
|
+
labels
|
|
1389
|
+
});
|
|
1390
|
+
const clipboard = useClipboard({
|
|
1391
|
+
dispatch,
|
|
1392
|
+
clipboard: state.clipboard,
|
|
1393
|
+
adapter,
|
|
1394
|
+
nodeById,
|
|
1395
|
+
labels
|
|
1396
|
+
});
|
|
1397
|
+
const dnd = useDnd({
|
|
1398
|
+
enabled: enableDnD,
|
|
1399
|
+
adapter,
|
|
1400
|
+
nodeById,
|
|
1401
|
+
selected: state.selected,
|
|
1402
|
+
labels,
|
|
1403
|
+
canDrop
|
|
1404
|
+
});
|
|
1405
|
+
const onActivateRef = React.useRef(onActivate);
|
|
1406
|
+
onActivateRef.current = onActivate;
|
|
506
1407
|
const activate = React.useCallback(
|
|
507
1408
|
(node, opts = { preview: false }) => onActivateRef.current?.(node, opts),
|
|
508
1409
|
[]
|
|
509
1410
|
);
|
|
1411
|
+
const setQuery = React.useCallback(
|
|
1412
|
+
(q) => dispatch({ type: "set-query", q }),
|
|
1413
|
+
[]
|
|
1414
|
+
);
|
|
1415
|
+
usePersistSync({
|
|
1416
|
+
expanded: state.expanded,
|
|
1417
|
+
selected: state.selected,
|
|
1418
|
+
persistKey,
|
|
1419
|
+
persistSelection,
|
|
1420
|
+
onSelectionChange,
|
|
1421
|
+
onExpansionChange
|
|
1422
|
+
});
|
|
1423
|
+
const resolvedContextMenuActions = useResolvedMenu({
|
|
1424
|
+
adapter,
|
|
1425
|
+
contextMenuActions,
|
|
1426
|
+
defaultMenuItems,
|
|
1427
|
+
labels,
|
|
1428
|
+
selected: state.selected,
|
|
1429
|
+
clipboard: state.clipboard,
|
|
1430
|
+
nodeById,
|
|
1431
|
+
getItemName,
|
|
1432
|
+
enableInlineRename,
|
|
1433
|
+
startRename: rename.startRename,
|
|
1434
|
+
cutToClipboard: clipboard.cutToClipboard,
|
|
1435
|
+
copyToClipboard: clipboard.copyToClipboard,
|
|
1436
|
+
pasteFromClipboard: clipboard.pasteFromClipboard
|
|
1437
|
+
});
|
|
510
1438
|
const value = React.useMemo(
|
|
511
1439
|
() => ({
|
|
1440
|
+
// state
|
|
512
1441
|
expanded: state.expanded,
|
|
513
1442
|
selected: state.selected,
|
|
1443
|
+
anchor: state.anchor,
|
|
514
1444
|
focused: state.focused,
|
|
515
1445
|
query: state.query,
|
|
1446
|
+
renamingId: state.renaming,
|
|
1447
|
+
inlineRenameEnabled: rename.enabled,
|
|
1448
|
+
clipboard: state.clipboard,
|
|
516
1449
|
flatRows,
|
|
517
1450
|
matchingIds,
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
collapseAll,
|
|
523
|
-
select,
|
|
524
|
-
setSelectedIds,
|
|
525
|
-
clearSelection,
|
|
526
|
-
setFocus,
|
|
1451
|
+
// expansion
|
|
1452
|
+
...expansion,
|
|
1453
|
+
// selection (note: `select` is from selection hook; expansion exports no `select`)
|
|
1454
|
+
...selection,
|
|
527
1455
|
setQuery,
|
|
1456
|
+
// clipboard
|
|
1457
|
+
...clipboard,
|
|
1458
|
+
// rename
|
|
1459
|
+
startRename: rename.startRename,
|
|
1460
|
+
cancelRename: rename.cancelRename,
|
|
1461
|
+
commitRename: rename.commitRename,
|
|
1462
|
+
// async
|
|
528
1463
|
refresh,
|
|
529
1464
|
refreshAll,
|
|
530
1465
|
activate,
|
|
1466
|
+
// config
|
|
531
1467
|
labels,
|
|
532
1468
|
appearance: resolvedAppearance,
|
|
533
1469
|
indent: resolvedAppearance.indent,
|
|
@@ -536,28 +1472,34 @@ function TreeProvider(props) {
|
|
|
536
1472
|
enableSearch,
|
|
537
1473
|
showIndentGuides,
|
|
538
1474
|
getItemName,
|
|
1475
|
+
// slots
|
|
539
1476
|
renderIcon,
|
|
540
1477
|
renderLabel,
|
|
541
1478
|
renderActions,
|
|
542
|
-
renderContextMenu
|
|
1479
|
+
renderContextMenu,
|
|
1480
|
+
adapter,
|
|
1481
|
+
resolvedContextMenuActions,
|
|
1482
|
+
getNodeById: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((id) => nodeById.get(id), "getNodeById"),
|
|
1483
|
+
dnd
|
|
543
1484
|
}),
|
|
544
1485
|
[
|
|
545
1486
|
state.expanded,
|
|
546
1487
|
state.selected,
|
|
1488
|
+
state.anchor,
|
|
547
1489
|
state.focused,
|
|
548
1490
|
state.query,
|
|
1491
|
+
state.renaming,
|
|
1492
|
+
state.clipboard,
|
|
1493
|
+
rename.enabled,
|
|
1494
|
+
rename.startRename,
|
|
1495
|
+
rename.cancelRename,
|
|
1496
|
+
rename.commitRename,
|
|
549
1497
|
flatRows,
|
|
550
1498
|
matchingIds,
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
toggle,
|
|
554
|
-
expandAll,
|
|
555
|
-
collapseAll,
|
|
556
|
-
select,
|
|
557
|
-
setSelectedIds,
|
|
558
|
-
clearSelection,
|
|
559
|
-
setFocus,
|
|
1499
|
+
expansion,
|
|
1500
|
+
selection,
|
|
560
1501
|
setQuery,
|
|
1502
|
+
clipboard,
|
|
561
1503
|
refresh,
|
|
562
1504
|
refreshAll,
|
|
563
1505
|
activate,
|
|
@@ -571,12 +1513,98 @@ function TreeProvider(props) {
|
|
|
571
1513
|
renderIcon,
|
|
572
1514
|
renderLabel,
|
|
573
1515
|
renderActions,
|
|
574
|
-
renderContextMenu
|
|
1516
|
+
renderContextMenu,
|
|
1517
|
+
adapter,
|
|
1518
|
+
resolvedContextMenuActions,
|
|
1519
|
+
nodeById,
|
|
1520
|
+
dnd
|
|
575
1521
|
]
|
|
576
1522
|
);
|
|
577
1523
|
return /* @__PURE__ */ jsxRuntime.jsx(TreeContext.Provider, { value, children });
|
|
578
1524
|
}
|
|
579
1525
|
chunkPK6SKIKE_cjs.__name(TreeProvider, "TreeProvider");
|
|
1526
|
+
function TreeDndProvider({ children }) {
|
|
1527
|
+
const ctx = useTreeContext();
|
|
1528
|
+
const { dnd } = ctx;
|
|
1529
|
+
const sensors = core.useSensors(
|
|
1530
|
+
core.useSensor(core.PointerSensor, { activationConstraint: { distance: 4 } }),
|
|
1531
|
+
core.useSensor(core.KeyboardSensor)
|
|
1532
|
+
);
|
|
1533
|
+
const cursorYRef = React.useRef(0);
|
|
1534
|
+
const handleDragStart = React.useCallback(
|
|
1535
|
+
(e) => {
|
|
1536
|
+
dnd.beginDrag(e.active.id);
|
|
1537
|
+
},
|
|
1538
|
+
[dnd]
|
|
1539
|
+
);
|
|
1540
|
+
const handleDragMove = React.useCallback(
|
|
1541
|
+
(e) => {
|
|
1542
|
+
const overId = e.over?.id;
|
|
1543
|
+
if (overId === TREE_ROOT_DROP_ID) {
|
|
1544
|
+
const current2 = dnd.dropTarget;
|
|
1545
|
+
if (current2?.id !== null || current2?.position !== "inside") {
|
|
1546
|
+
dnd.setDropTarget({ id: null, position: "inside" });
|
|
1547
|
+
}
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
if (typeof overId !== "string") {
|
|
1551
|
+
if (dnd.dropTarget !== null) dnd.setDropTarget(null);
|
|
1552
|
+
return;
|
|
1553
|
+
}
|
|
1554
|
+
const rect = e.over?.rect;
|
|
1555
|
+
if (!rect) return;
|
|
1556
|
+
const el = document.querySelector(
|
|
1557
|
+
`[data-tree-row][data-id="${CSS.escape(overId)}"]`
|
|
1558
|
+
);
|
|
1559
|
+
const isFolder = el?.dataset.folder === "true";
|
|
1560
|
+
const position = resolveDropZone({
|
|
1561
|
+
pointerY: cursorYRef.current,
|
|
1562
|
+
rowRect: { top: rect.top, bottom: rect.top + rect.height, height: rect.height },
|
|
1563
|
+
isFolder
|
|
1564
|
+
});
|
|
1565
|
+
const current = dnd.dropTarget;
|
|
1566
|
+
if (current?.id !== overId || current.position !== position) {
|
|
1567
|
+
dnd.setDropTarget({ id: overId, position });
|
|
1568
|
+
}
|
|
1569
|
+
},
|
|
1570
|
+
[dnd]
|
|
1571
|
+
);
|
|
1572
|
+
const handleDragEnd = React.useCallback(
|
|
1573
|
+
async (_e) => {
|
|
1574
|
+
await dnd.commitDrop();
|
|
1575
|
+
},
|
|
1576
|
+
[dnd]
|
|
1577
|
+
);
|
|
1578
|
+
const handleDragCancel = React.useCallback(() => {
|
|
1579
|
+
dnd.cancelDrag();
|
|
1580
|
+
}, [dnd]);
|
|
1581
|
+
const handlePointerMove = React.useCallback((e) => {
|
|
1582
|
+
cursorYRef.current = e.clientY;
|
|
1583
|
+
}, []);
|
|
1584
|
+
if (!dnd.active) {
|
|
1585
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
1586
|
+
}
|
|
1587
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1588
|
+
core.DndContext,
|
|
1589
|
+
{
|
|
1590
|
+
sensors,
|
|
1591
|
+
onDragStart: handleDragStart,
|
|
1592
|
+
onDragMove: handleDragMove,
|
|
1593
|
+
onDragEnd: handleDragEnd,
|
|
1594
|
+
onDragCancel: handleDragCancel,
|
|
1595
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1596
|
+
"div",
|
|
1597
|
+
{
|
|
1598
|
+
onPointerMove: handlePointerMove,
|
|
1599
|
+
className: "contents",
|
|
1600
|
+
"data-tree-dnd-surface": "",
|
|
1601
|
+
children
|
|
1602
|
+
}
|
|
1603
|
+
)
|
|
1604
|
+
}
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
chunkPK6SKIKE_cjs.__name(TreeDndProvider, "TreeDndProvider");
|
|
580
1608
|
function TreeChevronRaw({ isExpanded, isFolder, className }) {
|
|
581
1609
|
const { appearance } = useTreeContext();
|
|
582
1610
|
const size = { width: "var(--tree-icon-size)", height: "var(--tree-icon-size)" };
|
|
@@ -606,6 +1634,48 @@ function TreeChevronRaw({ isExpanded, isFolder, className }) {
|
|
|
606
1634
|
}
|
|
607
1635
|
chunkPK6SKIKE_cjs.__name(TreeChevronRaw, "TreeChevronRaw");
|
|
608
1636
|
var TreeChevron = React.memo(TreeChevronRaw);
|
|
1637
|
+
function TreeDropIndicator({
|
|
1638
|
+
position,
|
|
1639
|
+
indent,
|
|
1640
|
+
invalid = false
|
|
1641
|
+
}) {
|
|
1642
|
+
if (position === "inside") {
|
|
1643
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1644
|
+
"span",
|
|
1645
|
+
{
|
|
1646
|
+
"aria-hidden": true,
|
|
1647
|
+
"data-tree-drop": "inside",
|
|
1648
|
+
className: lib.cn(
|
|
1649
|
+
"pointer-events-none absolute inset-0 rounded-sm ring-1",
|
|
1650
|
+
invalid ? "bg-destructive/10 ring-destructive/40" : "bg-primary/10 ring-primary/40"
|
|
1651
|
+
)
|
|
1652
|
+
}
|
|
1653
|
+
);
|
|
1654
|
+
}
|
|
1655
|
+
const isBefore = position === "before";
|
|
1656
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1657
|
+
"span",
|
|
1658
|
+
{
|
|
1659
|
+
"aria-hidden": true,
|
|
1660
|
+
"data-tree-drop": position,
|
|
1661
|
+
style: { paddingLeft: indent },
|
|
1662
|
+
className: lib.cn(
|
|
1663
|
+
"pointer-events-none absolute right-0 left-0 h-px",
|
|
1664
|
+
isBefore ? "top-0" : "bottom-0"
|
|
1665
|
+
),
|
|
1666
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1667
|
+
"span",
|
|
1668
|
+
{
|
|
1669
|
+
className: lib.cn(
|
|
1670
|
+
"block h-0.5 rounded-full",
|
|
1671
|
+
invalid ? "bg-destructive/70" : "bg-primary"
|
|
1672
|
+
)
|
|
1673
|
+
}
|
|
1674
|
+
)
|
|
1675
|
+
}
|
|
1676
|
+
);
|
|
1677
|
+
}
|
|
1678
|
+
chunkPK6SKIKE_cjs.__name(TreeDropIndicator, "TreeDropIndicator");
|
|
609
1679
|
function TreeIconRaw({ isFolder, isExpanded, className }) {
|
|
610
1680
|
const { appearance } = useTreeContext();
|
|
611
1681
|
const Icon = isFolder ? isExpanded ? lucideReact.FolderOpen : lucideReact.Folder : lucideReact.File;
|
|
@@ -662,6 +1732,96 @@ function TreeLabelRaw({ children, isMatchingSearch, className }) {
|
|
|
662
1732
|
}
|
|
663
1733
|
chunkPK6SKIKE_cjs.__name(TreeLabelRaw, "TreeLabelRaw");
|
|
664
1734
|
var TreeLabel = React.memo(TreeLabelRaw);
|
|
1735
|
+
|
|
1736
|
+
// src/tools/data/Tree/data/renameUtils.ts
|
|
1737
|
+
function splitFileName(name) {
|
|
1738
|
+
if (name.length === 0) return { base: "", ext: "" };
|
|
1739
|
+
if (name.startsWith(".")) {
|
|
1740
|
+
const rest = name.slice(1);
|
|
1741
|
+
const dot2 = rest.lastIndexOf(".");
|
|
1742
|
+
if (dot2 < 0) return { base: name, ext: "" };
|
|
1743
|
+
return { base: "." + rest.slice(0, dot2), ext: rest.slice(dot2) };
|
|
1744
|
+
}
|
|
1745
|
+
const dot = name.lastIndexOf(".");
|
|
1746
|
+
if (dot <= 0) return { base: name, ext: "" };
|
|
1747
|
+
return { base: name.slice(0, dot), ext: name.slice(dot) };
|
|
1748
|
+
}
|
|
1749
|
+
chunkPK6SKIKE_cjs.__name(splitFileName, "splitFileName");
|
|
1750
|
+
function autoSelectRange(name, isFolder) {
|
|
1751
|
+
if (isFolder) return [0, name.length];
|
|
1752
|
+
const { base } = splitFileName(name);
|
|
1753
|
+
return [0, base.length];
|
|
1754
|
+
}
|
|
1755
|
+
chunkPK6SKIKE_cjs.__name(autoSelectRange, "autoSelectRange");
|
|
1756
|
+
function TreeRenameInput({
|
|
1757
|
+
initialValue,
|
|
1758
|
+
isFolder,
|
|
1759
|
+
onCommit,
|
|
1760
|
+
onCancel,
|
|
1761
|
+
className
|
|
1762
|
+
}) {
|
|
1763
|
+
const [value, setValue] = React.useState(initialValue);
|
|
1764
|
+
const inputRef = React.useRef(null);
|
|
1765
|
+
const settledRef = React.useRef(false);
|
|
1766
|
+
React.useEffect(() => {
|
|
1767
|
+
const el = inputRef.current;
|
|
1768
|
+
if (!el) return;
|
|
1769
|
+
el.focus();
|
|
1770
|
+
const [start, end] = autoSelectRange(initialValue, isFolder);
|
|
1771
|
+
requestAnimationFrame(() => {
|
|
1772
|
+
try {
|
|
1773
|
+
el.setSelectionRange(start, end);
|
|
1774
|
+
} catch {
|
|
1775
|
+
}
|
|
1776
|
+
});
|
|
1777
|
+
}, []);
|
|
1778
|
+
const commit = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => {
|
|
1779
|
+
if (settledRef.current) return;
|
|
1780
|
+
settledRef.current = true;
|
|
1781
|
+
void onCommit(value);
|
|
1782
|
+
}, "commit");
|
|
1783
|
+
const cancel = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => {
|
|
1784
|
+
if (settledRef.current) return;
|
|
1785
|
+
settledRef.current = true;
|
|
1786
|
+
onCancel();
|
|
1787
|
+
}, "cancel");
|
|
1788
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1789
|
+
"input",
|
|
1790
|
+
{
|
|
1791
|
+
ref: inputRef,
|
|
1792
|
+
type: "text",
|
|
1793
|
+
value,
|
|
1794
|
+
onKeyDown: (e) => {
|
|
1795
|
+
e.stopPropagation();
|
|
1796
|
+
if (e.key === "Enter") {
|
|
1797
|
+
e.preventDefault();
|
|
1798
|
+
commit();
|
|
1799
|
+
} else if (e.key === "Escape") {
|
|
1800
|
+
e.preventDefault();
|
|
1801
|
+
cancel();
|
|
1802
|
+
}
|
|
1803
|
+
},
|
|
1804
|
+
onChange: (e) => setValue(e.target.value),
|
|
1805
|
+
onBlur: commit,
|
|
1806
|
+
onClick: (e) => e.stopPropagation(),
|
|
1807
|
+
onDoubleClick: (e) => e.stopPropagation(),
|
|
1808
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
1809
|
+
onContextMenu: (e) => e.stopPropagation(),
|
|
1810
|
+
className: lib.cn(
|
|
1811
|
+
"min-w-0 flex-1 rounded-sm border border-primary/50 bg-background",
|
|
1812
|
+
"px-1 py-0 text-foreground outline-none",
|
|
1813
|
+
"focus:ring-1 focus:ring-primary/40",
|
|
1814
|
+
className
|
|
1815
|
+
),
|
|
1816
|
+
style: {
|
|
1817
|
+
// Match the row's font metrics so the input doesn't visibly jolt.
|
|
1818
|
+
fontSize: "var(--tree-font-size)",
|
|
1819
|
+
height: "calc(var(--tree-row-height) - 4px)"
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1824
|
+
chunkPK6SKIKE_cjs.__name(TreeRenameInput, "TreeRenameInput");
|
|
665
1825
|
function TreeRowRaw({ row, className }) {
|
|
666
1826
|
const ctx = useTreeContext();
|
|
667
1827
|
const {
|
|
@@ -673,6 +1833,7 @@ function TreeRowRaw({ row, className }) {
|
|
|
673
1833
|
matchingIds,
|
|
674
1834
|
select,
|
|
675
1835
|
setSelectedIds,
|
|
1836
|
+
clickSelect,
|
|
676
1837
|
toggle,
|
|
677
1838
|
setFocus,
|
|
678
1839
|
activate,
|
|
@@ -680,13 +1841,19 @@ function TreeRowRaw({ row, className }) {
|
|
|
680
1841
|
renderIcon,
|
|
681
1842
|
renderLabel,
|
|
682
1843
|
renderActions,
|
|
683
|
-
renderContextMenu
|
|
1844
|
+
renderContextMenu,
|
|
1845
|
+
renamingId,
|
|
1846
|
+
commitRename,
|
|
1847
|
+
cancelRename,
|
|
1848
|
+
clipboard,
|
|
1849
|
+
dnd
|
|
684
1850
|
} = ctx;
|
|
685
1851
|
const { node, level, isFolder, isExpanded, isLoading, posInSet, setSize } = row;
|
|
686
1852
|
const isSelected = selected.has(node.id);
|
|
687
1853
|
const isFocused = focused === node.id;
|
|
688
1854
|
const isMatchingSearch = matchingIds.has(node.id);
|
|
689
1855
|
const isMultiSelect = ctx.selectionMode === "multiple";
|
|
1856
|
+
const isCut = clipboard?.kind === "cut" && clipboard.ids.includes(node.id);
|
|
690
1857
|
const slot = {
|
|
691
1858
|
node,
|
|
692
1859
|
level,
|
|
@@ -697,30 +1864,57 @@ function TreeRowRaw({ row, className }) {
|
|
|
697
1864
|
isLoading,
|
|
698
1865
|
isMatchingSearch
|
|
699
1866
|
};
|
|
1867
|
+
const isRenaming = renamingId === node.id;
|
|
1868
|
+
const isDragging = dnd.draggingIds.has(node.id);
|
|
1869
|
+
const dropTarget = dnd.dropTarget;
|
|
1870
|
+
const isDropTarget = dropTarget?.id === node.id;
|
|
1871
|
+
const dropPosition = isDropTarget ? dropTarget.position : null;
|
|
1872
|
+
const dndDisabled = !dnd.active || isRenaming || node.disabled;
|
|
1873
|
+
const draggable = core.useDraggable({ id: node.id, disabled: dndDisabled });
|
|
1874
|
+
const droppable = core.useDroppable({ id: node.id, disabled: dndDisabled });
|
|
1875
|
+
const setRowEl = React.useCallback(
|
|
1876
|
+
(el) => {
|
|
1877
|
+
draggable.setNodeRef(el);
|
|
1878
|
+
droppable.setNodeRef(el);
|
|
1879
|
+
},
|
|
1880
|
+
[draggable, droppable]
|
|
1881
|
+
);
|
|
1882
|
+
const isAllowedDrop = dropPosition && !isDragging ? dnd.isAllowedDrop(node, dropPosition) : true;
|
|
700
1883
|
const handleClick = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((e) => {
|
|
701
|
-
if (node.disabled) return;
|
|
1884
|
+
if (node.disabled || isRenaming) return;
|
|
702
1885
|
setFocus(node.id);
|
|
703
|
-
if (isMultiSelect
|
|
704
|
-
|
|
1886
|
+
if (isMultiSelect) {
|
|
1887
|
+
clickSelect(node.id, { shift: e.shiftKey, meta: e.metaKey || e.ctrlKey });
|
|
705
1888
|
} else {
|
|
706
1889
|
select(node.id);
|
|
707
1890
|
}
|
|
708
1891
|
if (isFolder) {
|
|
1892
|
+
if (e.shiftKey || e.metaKey || e.ctrlKey) return;
|
|
709
1893
|
toggle(node.id);
|
|
710
1894
|
} else if (activationMode === "single-click") {
|
|
1895
|
+
if (e.shiftKey || e.metaKey || e.ctrlKey) return;
|
|
711
1896
|
activate(node, { preview: false });
|
|
712
1897
|
} else if (activationMode === "single-click-preview") {
|
|
1898
|
+
if (e.shiftKey || e.metaKey || e.ctrlKey) return;
|
|
713
1899
|
activate(node, { preview: true });
|
|
714
1900
|
}
|
|
715
1901
|
}, "handleClick");
|
|
716
1902
|
const handleDoubleClick = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => {
|
|
717
|
-
if (node.disabled) return;
|
|
1903
|
+
if (node.disabled || isRenaming) return;
|
|
718
1904
|
if (isFolder) return;
|
|
719
1905
|
activate(node, { preview: false });
|
|
720
1906
|
}, "handleDoubleClick");
|
|
1907
|
+
const handleContextMenu = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => {
|
|
1908
|
+
if (node.disabled || isRenaming) return;
|
|
1909
|
+
setFocus(node.id);
|
|
1910
|
+
if (!isSelected) {
|
|
1911
|
+
setSelectedIds([node.id]);
|
|
1912
|
+
}
|
|
1913
|
+
}, "handleContextMenu");
|
|
721
1914
|
const trigger = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
722
1915
|
"div",
|
|
723
1916
|
{
|
|
1917
|
+
ref: dnd.active ? setRowEl : void 0,
|
|
724
1918
|
id: treeRowDomId(node.id),
|
|
725
1919
|
role: "treeitem",
|
|
726
1920
|
"aria-level": level + 1,
|
|
@@ -733,6 +1927,8 @@ function TreeRowRaw({ row, className }) {
|
|
|
733
1927
|
"data-id": node.id,
|
|
734
1928
|
"data-activation-mode": activationMode,
|
|
735
1929
|
"data-selected": isSelected ? "true" : void 0,
|
|
1930
|
+
"data-clipboard": isCut ? "cut" : void 0,
|
|
1931
|
+
"data-dragging": isDragging ? "true" : void 0,
|
|
736
1932
|
"data-focused": isFocused && !isSelected ? "true" : void 0,
|
|
737
1933
|
"data-folder": isFolder || void 0,
|
|
738
1934
|
"data-expanded": isExpanded || void 0,
|
|
@@ -742,8 +1938,11 @@ function TreeRowRaw({ row, className }) {
|
|
|
742
1938
|
height: "var(--tree-row-height)",
|
|
743
1939
|
gap: "var(--tree-gap)"
|
|
744
1940
|
},
|
|
1941
|
+
...dnd.active ? draggable.listeners : {},
|
|
1942
|
+
...dnd.active ? draggable.attributes : {},
|
|
745
1943
|
onClick: handleClick,
|
|
746
1944
|
onDoubleClick: handleDoubleClick,
|
|
1945
|
+
onContextMenu: handleContextMenu,
|
|
747
1946
|
onFocus: () => setFocus(node.id),
|
|
748
1947
|
className: lib.cn(
|
|
749
1948
|
"group/row relative flex w-full select-none items-center pr-2 text-left",
|
|
@@ -753,6 +1952,8 @@ function TreeRowRaw({ row, className }) {
|
|
|
753
1952
|
rowStateClasses(appearance),
|
|
754
1953
|
"focus-visible:ring-1 focus-visible:ring-ring/50",
|
|
755
1954
|
isMatchingSearch && "ring-1 ring-primary/30",
|
|
1955
|
+
isCut && "opacity-60",
|
|
1956
|
+
isDragging && "opacity-40",
|
|
756
1957
|
node.disabled && "opacity-50",
|
|
757
1958
|
className
|
|
758
1959
|
),
|
|
@@ -767,6 +1968,14 @@ function TreeRowRaw({ row, className }) {
|
|
|
767
1968
|
)
|
|
768
1969
|
}
|
|
769
1970
|
) : null,
|
|
1971
|
+
dropPosition && !isDragging ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1972
|
+
TreeDropIndicator,
|
|
1973
|
+
{
|
|
1974
|
+
position: dropPosition,
|
|
1975
|
+
indent: 6 + level * appearance.indent,
|
|
1976
|
+
invalid: !isAllowedDrop
|
|
1977
|
+
}
|
|
1978
|
+
) : null,
|
|
770
1979
|
showIndentGuides && level > 0 ? /* @__PURE__ */ jsxRuntime.jsx(TreeIndentGuides, { level, indent: appearance.indent }) : null,
|
|
771
1980
|
/* @__PURE__ */ jsxRuntime.jsx(TreeChevron, { isExpanded, isFolder }),
|
|
772
1981
|
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -783,7 +1992,15 @@ function TreeRowRaw({ row, className }) {
|
|
|
783
1992
|
{
|
|
784
1993
|
className: "flex min-w-0 flex-1 items-center",
|
|
785
1994
|
style: { gap: "var(--tree-gap)" },
|
|
786
|
-
children:
|
|
1995
|
+
children: renamingId === node.id ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1996
|
+
TreeRenameInput,
|
|
1997
|
+
{
|
|
1998
|
+
initialValue: getItemName(node),
|
|
1999
|
+
isFolder,
|
|
2000
|
+
onCommit: (next) => commitRename(node.id, next),
|
|
2001
|
+
onCancel: cancelRename
|
|
2002
|
+
}
|
|
2003
|
+
) : renderLabel ? renderLabel(slot) : /* @__PURE__ */ jsxRuntime.jsx(TreeLabel, { isMatchingSearch, children: getItemName(node) })
|
|
787
2004
|
}
|
|
788
2005
|
),
|
|
789
2006
|
renderActions ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -855,6 +2072,105 @@ function TreeContent({
|
|
|
855
2072
|
);
|
|
856
2073
|
}
|
|
857
2074
|
chunkPK6SKIKE_cjs.__name(TreeContent, "TreeContent");
|
|
2075
|
+
function TreeEmptyArea({ className }) {
|
|
2076
|
+
const ctx = useTreeContext();
|
|
2077
|
+
const {
|
|
2078
|
+
adapter,
|
|
2079
|
+
labels,
|
|
2080
|
+
getItemName,
|
|
2081
|
+
clipboard,
|
|
2082
|
+
cutToClipboard,
|
|
2083
|
+
copyToClipboard,
|
|
2084
|
+
pasteFromClipboard,
|
|
2085
|
+
startRename,
|
|
2086
|
+
inlineRenameEnabled,
|
|
2087
|
+
dnd
|
|
2088
|
+
} = ctx;
|
|
2089
|
+
const { setNodeRef: setDroppableRef, isOver } = core.useDroppable({
|
|
2090
|
+
id: TREE_ROOT_DROP_ID,
|
|
2091
|
+
disabled: !dnd.active
|
|
2092
|
+
});
|
|
2093
|
+
const items = React.useMemo(() => {
|
|
2094
|
+
if (!adapter) return null;
|
|
2095
|
+
const builtinCtx = {
|
|
2096
|
+
adapter,
|
|
2097
|
+
labels,
|
|
2098
|
+
selectedNodes: [],
|
|
2099
|
+
targetNode: null,
|
|
2100
|
+
getName: getItemName,
|
|
2101
|
+
startInlineRename: inlineRenameEnabled ? startRename : void 0,
|
|
2102
|
+
clipboard: {
|
|
2103
|
+
hasItems: !!clipboard && clipboard.ids.length > 0,
|
|
2104
|
+
cut: cutToClipboard,
|
|
2105
|
+
copy: copyToClipboard,
|
|
2106
|
+
paste: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => pasteFromClipboard(null, "inside"), "paste")
|
|
2107
|
+
}
|
|
2108
|
+
};
|
|
2109
|
+
return buildDefaultMenuItems(builtinCtx);
|
|
2110
|
+
}, [
|
|
2111
|
+
adapter,
|
|
2112
|
+
labels,
|
|
2113
|
+
getItemName,
|
|
2114
|
+
inlineRenameEnabled,
|
|
2115
|
+
startRename,
|
|
2116
|
+
clipboard,
|
|
2117
|
+
cutToClipboard,
|
|
2118
|
+
copyToClipboard,
|
|
2119
|
+
pasteFromClipboard
|
|
2120
|
+
]);
|
|
2121
|
+
const surface = /* @__PURE__ */ jsxRuntime.jsx(
|
|
2122
|
+
"div",
|
|
2123
|
+
{
|
|
2124
|
+
ref: dnd.active ? setDroppableRef : void 0,
|
|
2125
|
+
"data-tree-empty-area": "",
|
|
2126
|
+
"data-drop-active": dnd.active && isOver ? "true" : void 0,
|
|
2127
|
+
className: lib.cn(
|
|
2128
|
+
// Soaks up the remaining vertical space inside the scroll
|
|
2129
|
+
// container so right-click on whitespace lands here, not on
|
|
2130
|
+
// the scroll viewport.
|
|
2131
|
+
"min-h-[2.5rem] flex-1",
|
|
2132
|
+
dnd.active && isOver && "bg-primary/5 rounded-sm ring-1 ring-primary/30",
|
|
2133
|
+
className
|
|
2134
|
+
)
|
|
2135
|
+
}
|
|
2136
|
+
);
|
|
2137
|
+
if (!items || items.length === 0) {
|
|
2138
|
+
return surface;
|
|
2139
|
+
}
|
|
2140
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(components.ContextMenu, { children: [
|
|
2141
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuTrigger, { asChild: true, children: surface }),
|
|
2142
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuContent, { children: items.map((item, idx) => {
|
|
2143
|
+
if (item === "separator") {
|
|
2144
|
+
return /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuSeparator, {}, `sep-${idx}`);
|
|
2145
|
+
}
|
|
2146
|
+
const Icon = item.icon;
|
|
2147
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2148
|
+
components.ContextMenuItem,
|
|
2149
|
+
{
|
|
2150
|
+
disabled: item.disabled,
|
|
2151
|
+
variant: item.destructive ? "destructive" : void 0,
|
|
2152
|
+
onSelect: () => item.onSelect({
|
|
2153
|
+
node: void 0,
|
|
2154
|
+
level: 0,
|
|
2155
|
+
isSelected: false,
|
|
2156
|
+
isExpanded: false,
|
|
2157
|
+
isFocused: false,
|
|
2158
|
+
isFolder: false,
|
|
2159
|
+
isLoading: false,
|
|
2160
|
+
isMatchingSearch: false
|
|
2161
|
+
}),
|
|
2162
|
+
children: [
|
|
2163
|
+
Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) : null,
|
|
2164
|
+
item.label,
|
|
2165
|
+
item.shortcut ? /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuShortcut, { children: item.shortcut }) : null
|
|
2166
|
+
]
|
|
2167
|
+
},
|
|
2168
|
+
item.id
|
|
2169
|
+
);
|
|
2170
|
+
}) })
|
|
2171
|
+
] });
|
|
2172
|
+
}
|
|
2173
|
+
chunkPK6SKIKE_cjs.__name(TreeEmptyArea, "TreeEmptyArea");
|
|
858
2174
|
function useTreeLabels() {
|
|
859
2175
|
return useTreeContext().labels;
|
|
860
2176
|
}
|
|
@@ -873,12 +2189,26 @@ function useTreeSelection() {
|
|
|
873
2189
|
return React.useMemo(
|
|
874
2190
|
() => ({
|
|
875
2191
|
selectedIds,
|
|
2192
|
+
anchor: ctx.anchor,
|
|
876
2193
|
select: ctx.select,
|
|
877
2194
|
setSelectedIds: ctx.setSelectedIds,
|
|
878
2195
|
clear: ctx.clearSelection,
|
|
2196
|
+
clickSelect: ctx.clickSelect,
|
|
2197
|
+
moveSelect: ctx.moveSelect,
|
|
2198
|
+
selectAll: ctx.selectAll,
|
|
879
2199
|
isSelected
|
|
880
2200
|
}),
|
|
881
|
-
[
|
|
2201
|
+
[
|
|
2202
|
+
selectedIds,
|
|
2203
|
+
ctx.anchor,
|
|
2204
|
+
ctx.select,
|
|
2205
|
+
ctx.setSelectedIds,
|
|
2206
|
+
ctx.clearSelection,
|
|
2207
|
+
ctx.clickSelect,
|
|
2208
|
+
ctx.moveSelect,
|
|
2209
|
+
ctx.selectAll,
|
|
2210
|
+
isSelected
|
|
2211
|
+
]
|
|
882
2212
|
);
|
|
883
2213
|
}
|
|
884
2214
|
chunkPK6SKIKE_cjs.__name(useTreeSelection, "useTreeSelection");
|
|
@@ -933,6 +2263,59 @@ function useTreeSearch() {
|
|
|
933
2263
|
);
|
|
934
2264
|
}
|
|
935
2265
|
chunkPK6SKIKE_cjs.__name(useTreeSearch, "useTreeSearch");
|
|
2266
|
+
function useTreeDnd() {
|
|
2267
|
+
const ctx = useTreeContext();
|
|
2268
|
+
return ctx.dnd;
|
|
2269
|
+
}
|
|
2270
|
+
chunkPK6SKIKE_cjs.__name(useTreeDnd, "useTreeDnd");
|
|
2271
|
+
function useTreeClipboard() {
|
|
2272
|
+
const ctx = useTreeContext();
|
|
2273
|
+
const isCut = React.useCallback(
|
|
2274
|
+
(id) => ctx.clipboard?.kind === "cut" && ctx.clipboard.ids.includes(id),
|
|
2275
|
+
[ctx.clipboard]
|
|
2276
|
+
);
|
|
2277
|
+
return React.useMemo(
|
|
2278
|
+
() => ({
|
|
2279
|
+
clipboard: ctx.clipboard,
|
|
2280
|
+
isCut,
|
|
2281
|
+
cut: ctx.cutToClipboard,
|
|
2282
|
+
copy: ctx.copyToClipboard,
|
|
2283
|
+
paste: ctx.pasteFromClipboard,
|
|
2284
|
+
clear: ctx.clearClipboard
|
|
2285
|
+
}),
|
|
2286
|
+
[
|
|
2287
|
+
ctx.clipboard,
|
|
2288
|
+
isCut,
|
|
2289
|
+
ctx.cutToClipboard,
|
|
2290
|
+
ctx.copyToClipboard,
|
|
2291
|
+
ctx.pasteFromClipboard,
|
|
2292
|
+
ctx.clearClipboard
|
|
2293
|
+
]
|
|
2294
|
+
);
|
|
2295
|
+
}
|
|
2296
|
+
chunkPK6SKIKE_cjs.__name(useTreeClipboard, "useTreeClipboard");
|
|
2297
|
+
function useTreeRename() {
|
|
2298
|
+
const ctx = useTreeContext();
|
|
2299
|
+
return React.useMemo(
|
|
2300
|
+
() => ({
|
|
2301
|
+
/** True when the host allowed inline rename AND the adapter exposes `rename`. */
|
|
2302
|
+
enabled: ctx.inlineRenameEnabled,
|
|
2303
|
+
/** Currently renaming id, or `null`. */
|
|
2304
|
+
renamingId: ctx.renamingId,
|
|
2305
|
+
startRename: ctx.startRename,
|
|
2306
|
+
cancelRename: ctx.cancelRename,
|
|
2307
|
+
commitRename: ctx.commitRename
|
|
2308
|
+
}),
|
|
2309
|
+
[
|
|
2310
|
+
ctx.inlineRenameEnabled,
|
|
2311
|
+
ctx.renamingId,
|
|
2312
|
+
ctx.startRename,
|
|
2313
|
+
ctx.cancelRename,
|
|
2314
|
+
ctx.commitRename
|
|
2315
|
+
]
|
|
2316
|
+
);
|
|
2317
|
+
}
|
|
2318
|
+
chunkPK6SKIKE_cjs.__name(useTreeRename, "useTreeRename");
|
|
936
2319
|
function useTreeActions() {
|
|
937
2320
|
const ctx = useTreeContext();
|
|
938
2321
|
return React.useMemo(
|
|
@@ -997,16 +2380,78 @@ function TreeSearchInput({ className, showMatches = true }) {
|
|
|
997
2380
|
);
|
|
998
2381
|
}
|
|
999
2382
|
chunkPK6SKIKE_cjs.__name(TreeSearchInput, "TreeSearchInput");
|
|
2383
|
+
|
|
2384
|
+
// src/tools/data/Tree/hooks/keyboard/arrow-nav.ts
|
|
2385
|
+
function nextRowId(rows, idx) {
|
|
2386
|
+
if (rows.length === 0) return null;
|
|
2387
|
+
const next = rows[Math.min(idx + 1, rows.length - 1)] ?? rows[0];
|
|
2388
|
+
return next.node.id;
|
|
2389
|
+
}
|
|
2390
|
+
chunkPK6SKIKE_cjs.__name(nextRowId, "nextRowId");
|
|
2391
|
+
function prevRowId(rows, idx) {
|
|
2392
|
+
if (rows.length === 0) return null;
|
|
2393
|
+
const prev = rows[Math.max(idx - 1, 0)] ?? rows[0];
|
|
2394
|
+
return prev.node.id;
|
|
2395
|
+
}
|
|
2396
|
+
chunkPK6SKIKE_cjs.__name(prevRowId, "prevRowId");
|
|
2397
|
+
function edgeRowId(rows, edge) {
|
|
2398
|
+
if (rows.length === 0) return null;
|
|
2399
|
+
return edge === "first" ? rows[0].node.id : rows[rows.length - 1].node.id;
|
|
2400
|
+
}
|
|
2401
|
+
chunkPK6SKIKE_cjs.__name(edgeRowId, "edgeRowId");
|
|
2402
|
+
|
|
2403
|
+
// src/tools/data/Tree/hooks/keyboard/expand-collapse.ts
|
|
2404
|
+
function resolveRightArrow(current, rows, idx) {
|
|
2405
|
+
if (!current) return { kind: "noop" };
|
|
2406
|
+
if (current.isFolder && !current.isExpanded) {
|
|
2407
|
+
return { kind: "expand", id: current.node.id };
|
|
2408
|
+
}
|
|
2409
|
+
if (current.isFolder && current.isExpanded) {
|
|
2410
|
+
const next = rows[idx + 1];
|
|
2411
|
+
return next ? { kind: "focus", id: next.node.id } : { kind: "noop" };
|
|
2412
|
+
}
|
|
2413
|
+
return { kind: "noop" };
|
|
2414
|
+
}
|
|
2415
|
+
chunkPK6SKIKE_cjs.__name(resolveRightArrow, "resolveRightArrow");
|
|
2416
|
+
function resolveLeftArrow(current) {
|
|
2417
|
+
if (!current) return { kind: "noop" };
|
|
2418
|
+
if (current.isFolder && current.isExpanded) {
|
|
2419
|
+
return { kind: "collapse", id: current.node.id };
|
|
2420
|
+
}
|
|
2421
|
+
if (current.parentId) {
|
|
2422
|
+
return { kind: "focus", id: current.parentId };
|
|
2423
|
+
}
|
|
2424
|
+
return { kind: "noop" };
|
|
2425
|
+
}
|
|
2426
|
+
chunkPK6SKIKE_cjs.__name(resolveLeftArrow, "resolveLeftArrow");
|
|
2427
|
+
|
|
2428
|
+
// src/tools/data/Tree/hooks/keyboard/activation.ts
|
|
2429
|
+
function resolveActivate(current) {
|
|
2430
|
+
if (!current) return { kind: "noop" };
|
|
2431
|
+
if (current.isFolder) {
|
|
2432
|
+
return {
|
|
2433
|
+
kind: "toggle-folder",
|
|
2434
|
+
id: current.node.id,
|
|
2435
|
+
willExpand: !current.isExpanded
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
return { kind: "activate-leaf", id: current.node.id };
|
|
2439
|
+
}
|
|
2440
|
+
chunkPK6SKIKE_cjs.__name(resolveActivate, "resolveActivate");
|
|
2441
|
+
|
|
2442
|
+
// src/tools/data/Tree/hooks/keyboard/use-tree-keyboard.ts
|
|
1000
2443
|
function useTreeKeyboard({
|
|
1001
2444
|
rows,
|
|
1002
2445
|
focusedId,
|
|
1003
2446
|
enabled = true,
|
|
2447
|
+
multiSelect = false,
|
|
1004
2448
|
onFocus,
|
|
1005
2449
|
onSelect,
|
|
1006
2450
|
onActivate,
|
|
1007
2451
|
onExpand,
|
|
1008
2452
|
onCollapse,
|
|
1009
|
-
onClearSelection
|
|
2453
|
+
onClearSelection,
|
|
2454
|
+
onSelectAll
|
|
1010
2455
|
}) {
|
|
1011
2456
|
const rowsRef = React.useRef(rows);
|
|
1012
2457
|
const focusedIdRef = React.useRef(focusedId);
|
|
@@ -1019,53 +2464,65 @@ function useTreeKeyboard({
|
|
|
1019
2464
|
return { rows: r, idx, current: idx >= 0 ? r[idx] : null };
|
|
1020
2465
|
}, "getCurrent");
|
|
1021
2466
|
const refDown = hooks.useHotkey(
|
|
1022
|
-
"down",
|
|
1023
|
-
() => {
|
|
2467
|
+
["down", "shift+down"],
|
|
2468
|
+
(e) => {
|
|
1024
2469
|
const { rows: r, idx } = getCurrent();
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
onFocus(next.node.id);
|
|
2470
|
+
const id = nextRowId(r, idx);
|
|
2471
|
+
if (id) onFocus(id, { extend: multiSelect && e.shiftKey });
|
|
1028
2472
|
},
|
|
1029
|
-
{ enabled, preventDefault: true, description: "Next row" }
|
|
2473
|
+
{ enabled, preventDefault: true, description: "Next row (Shift extends)" }
|
|
1030
2474
|
);
|
|
1031
2475
|
const refUp = hooks.useHotkey(
|
|
1032
|
-
"up",
|
|
1033
|
-
() => {
|
|
2476
|
+
["up", "shift+up"],
|
|
2477
|
+
(e) => {
|
|
1034
2478
|
const { rows: r, idx } = getCurrent();
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
onFocus(prev.node.id);
|
|
2479
|
+
const id = prevRowId(r, idx);
|
|
2480
|
+
if (id) onFocus(id, { extend: multiSelect && e.shiftKey });
|
|
1038
2481
|
},
|
|
1039
|
-
{ enabled, preventDefault: true, description: "Previous row" }
|
|
2482
|
+
{ enabled, preventDefault: true, description: "Previous row (Shift extends)" }
|
|
1040
2483
|
);
|
|
1041
2484
|
const refHome = hooks.useHotkey(
|
|
1042
|
-
"home",
|
|
1043
|
-
() => {
|
|
1044
|
-
const
|
|
1045
|
-
if (
|
|
1046
|
-
onFocus(r[0].node.id);
|
|
2485
|
+
["home", "shift+home"],
|
|
2486
|
+
(e) => {
|
|
2487
|
+
const id = edgeRowId(rowsRef.current, "first");
|
|
2488
|
+
if (id) onFocus(id, { extend: multiSelect && e.shiftKey });
|
|
1047
2489
|
},
|
|
1048
|
-
{ enabled, preventDefault: true, description: "First row" }
|
|
2490
|
+
{ enabled, preventDefault: true, description: "First row (Shift extends)" }
|
|
1049
2491
|
);
|
|
1050
2492
|
const refEnd = hooks.useHotkey(
|
|
1051
|
-
"end",
|
|
2493
|
+
["end", "shift+end"],
|
|
2494
|
+
(e) => {
|
|
2495
|
+
const id = edgeRowId(rowsRef.current, "last");
|
|
2496
|
+
if (id) onFocus(id, { extend: multiSelect && e.shiftKey });
|
|
2497
|
+
},
|
|
2498
|
+
{ enabled, preventDefault: true, description: "Last row (Shift extends)" }
|
|
2499
|
+
);
|
|
2500
|
+
const refSelectAll = hooks.useHotkey(
|
|
2501
|
+
"mod+a",
|
|
1052
2502
|
() => {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
onFocus(r[r.length - 1].node.id);
|
|
2503
|
+
if (!multiSelect) return;
|
|
2504
|
+
onSelectAll?.();
|
|
1056
2505
|
},
|
|
1057
|
-
{
|
|
2506
|
+
{
|
|
2507
|
+
enabled: enabled && multiSelect,
|
|
2508
|
+
preventDefault: true,
|
|
2509
|
+
description: "Select all visible rows"
|
|
2510
|
+
}
|
|
1058
2511
|
);
|
|
1059
2512
|
const refRight = hooks.useHotkey(
|
|
1060
2513
|
"right",
|
|
1061
2514
|
() => {
|
|
1062
2515
|
const { rows: r, idx, current } = getCurrent();
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
2516
|
+
const out = resolveRightArrow(current, r, idx);
|
|
2517
|
+
switch (out.kind) {
|
|
2518
|
+
case "expand":
|
|
2519
|
+
onExpand(out.id);
|
|
2520
|
+
return;
|
|
2521
|
+
case "focus":
|
|
2522
|
+
onFocus(out.id, { extend: false });
|
|
2523
|
+
return;
|
|
2524
|
+
case "noop":
|
|
2525
|
+
return;
|
|
1069
2526
|
}
|
|
1070
2527
|
},
|
|
1071
2528
|
{ enabled, preventDefault: true, description: "Expand / first child" }
|
|
@@ -1074,11 +2531,16 @@ function useTreeKeyboard({
|
|
|
1074
2531
|
"left",
|
|
1075
2532
|
() => {
|
|
1076
2533
|
const { current } = getCurrent();
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
2534
|
+
const out = resolveLeftArrow(current);
|
|
2535
|
+
switch (out.kind) {
|
|
2536
|
+
case "collapse":
|
|
2537
|
+
onCollapse(out.id);
|
|
2538
|
+
return;
|
|
2539
|
+
case "focus":
|
|
2540
|
+
onFocus(out.id, { extend: false });
|
|
2541
|
+
return;
|
|
2542
|
+
case "noop":
|
|
2543
|
+
return;
|
|
1082
2544
|
}
|
|
1083
2545
|
},
|
|
1084
2546
|
{ enabled, preventDefault: true, description: "Collapse / parent" }
|
|
@@ -1087,13 +2549,14 @@ function useTreeKeyboard({
|
|
|
1087
2549
|
["enter", "space"],
|
|
1088
2550
|
() => {
|
|
1089
2551
|
const { current } = getCurrent();
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
2552
|
+
const out = resolveActivate(current);
|
|
2553
|
+
if (out.kind === "noop") return;
|
|
2554
|
+
onSelect(out.kind === "activate-leaf" ? out.id : out.id);
|
|
2555
|
+
if (out.kind === "toggle-folder") {
|
|
2556
|
+
if (out.willExpand) onExpand(out.id);
|
|
2557
|
+
else onCollapse(out.id);
|
|
1095
2558
|
} else {
|
|
1096
|
-
onActivate(
|
|
2559
|
+
onActivate(out.id);
|
|
1097
2560
|
}
|
|
1098
2561
|
},
|
|
1099
2562
|
{ enabled, preventDefault: true, description: "Activate / toggle" }
|
|
@@ -1113,12 +2576,44 @@ function useTreeKeyboard({
|
|
|
1113
2576
|
refLeft(instance);
|
|
1114
2577
|
refActivate(instance);
|
|
1115
2578
|
refEscape(instance);
|
|
2579
|
+
refSelectAll(instance);
|
|
1116
2580
|
},
|
|
1117
|
-
[
|
|
2581
|
+
[
|
|
2582
|
+
refDown,
|
|
2583
|
+
refUp,
|
|
2584
|
+
refHome,
|
|
2585
|
+
refEnd,
|
|
2586
|
+
refRight,
|
|
2587
|
+
refLeft,
|
|
2588
|
+
refActivate,
|
|
2589
|
+
refEscape,
|
|
2590
|
+
refSelectAll
|
|
2591
|
+
]
|
|
1118
2592
|
);
|
|
1119
2593
|
return { ref };
|
|
1120
2594
|
}
|
|
1121
2595
|
chunkPK6SKIKE_cjs.__name(useTreeKeyboard, "useTreeKeyboard");
|
|
2596
|
+
|
|
2597
|
+
// src/tools/data/Tree/hooks/type-ahead/match-prefix.ts
|
|
2598
|
+
function findRowByPrefix(rows, getName, prefix) {
|
|
2599
|
+
if (prefix.length === 0) return void 0;
|
|
2600
|
+
return rows.find((row) => getName(row.node).toLowerCase().startsWith(prefix));
|
|
2601
|
+
}
|
|
2602
|
+
chunkPK6SKIKE_cjs.__name(findRowByPrefix, "findRowByPrefix");
|
|
2603
|
+
function isResetKey(key) {
|
|
2604
|
+
return key === "Escape" || key === "Enter" || key === "Tab" || key.startsWith("Arrow") || key === "Home" || key === "End" || key === "PageUp" || key === "PageDown";
|
|
2605
|
+
}
|
|
2606
|
+
chunkPK6SKIKE_cjs.__name(isResetKey, "isResetKey");
|
|
2607
|
+
function isTypingTarget(target) {
|
|
2608
|
+
const el = target;
|
|
2609
|
+
if (!el) return false;
|
|
2610
|
+
const tag = el.tagName;
|
|
2611
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
2612
|
+
return el.isContentEditable === true;
|
|
2613
|
+
}
|
|
2614
|
+
chunkPK6SKIKE_cjs.__name(isTypingTarget, "isTypingTarget");
|
|
2615
|
+
|
|
2616
|
+
// src/tools/data/Tree/hooks/type-ahead/use-tree-type-ahead.ts
|
|
1122
2617
|
var FLUSH_MS = 600;
|
|
1123
2618
|
function useTreeTypeAhead({
|
|
1124
2619
|
rows,
|
|
@@ -1147,11 +2642,9 @@ function useTreeTypeAhead({
|
|
|
1147
2642
|
}
|
|
1148
2643
|
}, "reset");
|
|
1149
2644
|
const handler = /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((e) => {
|
|
1150
|
-
|
|
1151
|
-
if (tag === "INPUT" || tag === "TEXTAREA") return;
|
|
1152
|
-
if (e.target?.isContentEditable) return;
|
|
2645
|
+
if (isTypingTarget(e.target)) return;
|
|
1153
2646
|
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
|
1154
|
-
if (
|
|
2647
|
+
if (isResetKey(e.key)) {
|
|
1155
2648
|
reset();
|
|
1156
2649
|
return;
|
|
1157
2650
|
}
|
|
@@ -1159,9 +2652,10 @@ function useTreeTypeAhead({
|
|
|
1159
2652
|
bufferRef.current += e.key.toLowerCase();
|
|
1160
2653
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
1161
2654
|
timerRef.current = setTimeout(reset, FLUSH_MS);
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1164
|
-
|
|
2655
|
+
const hit = findRowByPrefix(
|
|
2656
|
+
rowsRef.current,
|
|
2657
|
+
getNameRef.current,
|
|
2658
|
+
bufferRef.current
|
|
1165
2659
|
);
|
|
1166
2660
|
if (hit) {
|
|
1167
2661
|
e.preventDefault();
|
|
@@ -1176,36 +2670,166 @@ function useTreeTypeAhead({
|
|
|
1176
2670
|
}, [containerRef, enabled]);
|
|
1177
2671
|
}
|
|
1178
2672
|
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
|
-
] });
|
|
2673
|
+
|
|
2674
|
+
// src/tools/data/Tree/hooks/finder-hotkeys/build-ctx.ts
|
|
2675
|
+
function buildBuiltinCtx(input) {
|
|
2676
|
+
if (!input.adapter) return null;
|
|
2677
|
+
const selectedNodes = [];
|
|
2678
|
+
for (const id of input.selected) {
|
|
2679
|
+
const n = input.getNodeById(id);
|
|
2680
|
+
if (n) selectedNodes.push(n);
|
|
2681
|
+
}
|
|
2682
|
+
const targetNode = input.focused ? input.getNodeById(input.focused) ?? null : null;
|
|
2683
|
+
return {
|
|
2684
|
+
adapter: input.adapter,
|
|
2685
|
+
labels: input.labels,
|
|
2686
|
+
selectedNodes,
|
|
2687
|
+
targetNode,
|
|
2688
|
+
getName: input.getItemName,
|
|
2689
|
+
startInlineRename: input.startInlineRename,
|
|
2690
|
+
clipboard: input.clipboard
|
|
1206
2691
|
};
|
|
1207
2692
|
}
|
|
1208
|
-
chunkPK6SKIKE_cjs.__name(
|
|
2693
|
+
chunkPK6SKIKE_cjs.__name(buildBuiltinCtx, "buildBuiltinCtx");
|
|
2694
|
+
|
|
2695
|
+
// src/tools/data/Tree/hooks/finder-hotkeys/use-tree-finder-hotkeys.ts
|
|
2696
|
+
function useTreeFinderHotkeys(opts) {
|
|
2697
|
+
const optsRef = React.useRef(opts);
|
|
2698
|
+
optsRef.current = opts;
|
|
2699
|
+
const run = React.useCallback(async (action) => {
|
|
2700
|
+
const o = optsRef.current;
|
|
2701
|
+
if (o.paused) return;
|
|
2702
|
+
const ctx = buildBuiltinCtx({
|
|
2703
|
+
adapter: o.adapter,
|
|
2704
|
+
labels: o.labels,
|
|
2705
|
+
selected: o.selected,
|
|
2706
|
+
focused: o.focused,
|
|
2707
|
+
getNodeById: o.getNodeById,
|
|
2708
|
+
getItemName: o.getItemName,
|
|
2709
|
+
startInlineRename: o.startInlineRename,
|
|
2710
|
+
clipboard: o.clipboard
|
|
2711
|
+
});
|
|
2712
|
+
if (!ctx) return;
|
|
2713
|
+
await runBuiltinAction(action, ctx);
|
|
2714
|
+
}, []);
|
|
2715
|
+
const refDelete = hooks.useHotkey(
|
|
2716
|
+
["mod+backspace", "delete"],
|
|
2717
|
+
() => void run("delete"),
|
|
2718
|
+
{
|
|
2719
|
+
enabled: opts.enabled,
|
|
2720
|
+
preventDefault: true,
|
|
2721
|
+
description: "Delete selected items",
|
|
2722
|
+
scope: "tree"
|
|
2723
|
+
}
|
|
2724
|
+
);
|
|
2725
|
+
const refRename = hooks.useHotkey("f2", () => void run("rename"), {
|
|
2726
|
+
enabled: opts.enabled,
|
|
2727
|
+
preventDefault: true,
|
|
2728
|
+
description: "Rename selected item",
|
|
2729
|
+
scope: "tree"
|
|
2730
|
+
});
|
|
2731
|
+
const refDuplicate = hooks.useHotkey("mod+d", () => void run("duplicate"), {
|
|
2732
|
+
enabled: opts.enabled,
|
|
2733
|
+
preventDefault: true,
|
|
2734
|
+
description: "Duplicate selected items",
|
|
2735
|
+
scope: "tree"
|
|
2736
|
+
});
|
|
2737
|
+
const refNewFolder = hooks.useHotkey("mod+shift+n", () => void run("new-folder"), {
|
|
2738
|
+
enabled: opts.enabled,
|
|
2739
|
+
preventDefault: true,
|
|
2740
|
+
description: "New folder",
|
|
2741
|
+
scope: "tree"
|
|
2742
|
+
});
|
|
2743
|
+
const refNewFile = hooks.useHotkey("mod+n", () => void run("new-file"), {
|
|
2744
|
+
enabled: opts.enabled,
|
|
2745
|
+
preventDefault: true,
|
|
2746
|
+
description: "New file",
|
|
2747
|
+
scope: "tree"
|
|
2748
|
+
});
|
|
2749
|
+
const refCut = hooks.useHotkey("mod+x", () => void run("cut"), {
|
|
2750
|
+
enabled: opts.enabled,
|
|
2751
|
+
preventDefault: true,
|
|
2752
|
+
description: "Cut",
|
|
2753
|
+
scope: "tree"
|
|
2754
|
+
});
|
|
2755
|
+
const refCopy = hooks.useHotkey("mod+c", () => void run("copy"), {
|
|
2756
|
+
enabled: opts.enabled,
|
|
2757
|
+
preventDefault: true,
|
|
2758
|
+
description: "Copy",
|
|
2759
|
+
scope: "tree"
|
|
2760
|
+
});
|
|
2761
|
+
const refPaste = hooks.useHotkey("mod+v", () => void run("paste"), {
|
|
2762
|
+
enabled: opts.enabled,
|
|
2763
|
+
preventDefault: true,
|
|
2764
|
+
description: "Paste",
|
|
2765
|
+
scope: "tree"
|
|
2766
|
+
});
|
|
2767
|
+
const ref = React.useCallback(
|
|
2768
|
+
(instance) => {
|
|
2769
|
+
refDelete(instance);
|
|
2770
|
+
refRename(instance);
|
|
2771
|
+
refDuplicate(instance);
|
|
2772
|
+
refNewFolder(instance);
|
|
2773
|
+
refNewFile(instance);
|
|
2774
|
+
refCut(instance);
|
|
2775
|
+
refCopy(instance);
|
|
2776
|
+
refPaste(instance);
|
|
2777
|
+
},
|
|
2778
|
+
[
|
|
2779
|
+
refDelete,
|
|
2780
|
+
refRename,
|
|
2781
|
+
refDuplicate,
|
|
2782
|
+
refNewFolder,
|
|
2783
|
+
refNewFile,
|
|
2784
|
+
refCut,
|
|
2785
|
+
refCopy,
|
|
2786
|
+
refPaste
|
|
2787
|
+
]
|
|
2788
|
+
);
|
|
2789
|
+
return { ref };
|
|
2790
|
+
}
|
|
2791
|
+
chunkPK6SKIKE_cjs.__name(useTreeFinderHotkeys, "useTreeFinderHotkeys");
|
|
2792
|
+
function renderItemsAsContextMenu(rowProps, items, trigger) {
|
|
2793
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(components.ContextMenu, { children: [
|
|
2794
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuTrigger, { asChild: true, children: trigger }),
|
|
2795
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuContent, { children: items.map((item, idx) => {
|
|
2796
|
+
if (item === "separator") {
|
|
2797
|
+
return /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuSeparator, {}, `sep-${idx}`);
|
|
2798
|
+
}
|
|
2799
|
+
const Icon = item.icon;
|
|
2800
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2801
|
+
components.ContextMenuItem,
|
|
2802
|
+
{
|
|
2803
|
+
disabled: item.disabled,
|
|
2804
|
+
variant: item.destructive ? "destructive" : void 0,
|
|
2805
|
+
onSelect: () => item.onSelect(rowProps),
|
|
2806
|
+
children: [
|
|
2807
|
+
Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) : null,
|
|
2808
|
+
item.label,
|
|
2809
|
+
item.shortcut ? /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuShortcut, { children: item.shortcut }) : null
|
|
2810
|
+
]
|
|
2811
|
+
},
|
|
2812
|
+
item.id
|
|
2813
|
+
);
|
|
2814
|
+
}) })
|
|
2815
|
+
] });
|
|
2816
|
+
}
|
|
2817
|
+
chunkPK6SKIKE_cjs.__name(renderItemsAsContextMenu, "renderItemsAsContextMenu");
|
|
2818
|
+
function tidyMenuItems(items) {
|
|
2819
|
+
const out = [];
|
|
2820
|
+
for (const it of items) {
|
|
2821
|
+
if (it === "separator") {
|
|
2822
|
+
if (out.length === 0) continue;
|
|
2823
|
+
if (out[out.length - 1] === "separator") continue;
|
|
2824
|
+
out.push(it);
|
|
2825
|
+
} else {
|
|
2826
|
+
out.push(it);
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
while (out.length > 0 && out[out.length - 1] === "separator") out.pop();
|
|
2830
|
+
return out;
|
|
2831
|
+
}
|
|
2832
|
+
chunkPK6SKIKE_cjs.__name(tidyMenuItems, "tidyMenuItems");
|
|
1209
2833
|
function TreeRoot(props) {
|
|
1210
2834
|
const {
|
|
1211
2835
|
data,
|
|
@@ -1224,6 +2848,10 @@ function TreeRoot(props) {
|
|
|
1224
2848
|
enableSearch = false,
|
|
1225
2849
|
enableTypeAhead = true,
|
|
1226
2850
|
showIndentGuides = false,
|
|
2851
|
+
enableInlineRename = false,
|
|
2852
|
+
enableFinderHotkeys = false,
|
|
2853
|
+
enableDnD = false,
|
|
2854
|
+
canDrop,
|
|
1227
2855
|
renderRow,
|
|
1228
2856
|
renderIcon,
|
|
1229
2857
|
renderLabel,
|
|
@@ -1233,14 +2861,11 @@ function TreeRoot(props) {
|
|
|
1233
2861
|
labels,
|
|
1234
2862
|
persistKey,
|
|
1235
2863
|
persistSelection = false,
|
|
2864
|
+
adapter,
|
|
2865
|
+
defaultMenuItems,
|
|
1236
2866
|
className,
|
|
1237
2867
|
style
|
|
1238
2868
|
} = 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
2869
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1245
2870
|
TreeProvider,
|
|
1246
2871
|
{
|
|
@@ -1262,7 +2887,13 @@ function TreeRoot(props) {
|
|
|
1262
2887
|
renderIcon,
|
|
1263
2888
|
renderLabel,
|
|
1264
2889
|
renderActions,
|
|
1265
|
-
renderContextMenu
|
|
2890
|
+
renderContextMenu,
|
|
2891
|
+
contextMenuActions,
|
|
2892
|
+
adapter,
|
|
2893
|
+
defaultMenuItems,
|
|
2894
|
+
enableInlineRename,
|
|
2895
|
+
enableDnD,
|
|
2896
|
+
canDrop,
|
|
1266
2897
|
labels,
|
|
1267
2898
|
persistKey,
|
|
1268
2899
|
persistSelection,
|
|
@@ -1273,6 +2904,7 @@ function TreeRoot(props) {
|
|
|
1273
2904
|
style,
|
|
1274
2905
|
enableSearch,
|
|
1275
2906
|
enableTypeAhead,
|
|
2907
|
+
enableFinderHotkeys,
|
|
1276
2908
|
renderRow
|
|
1277
2909
|
}
|
|
1278
2910
|
)
|
|
@@ -1285,14 +2917,27 @@ function TreeRootShell({
|
|
|
1285
2917
|
style,
|
|
1286
2918
|
enableSearch,
|
|
1287
2919
|
enableTypeAhead,
|
|
2920
|
+
enableFinderHotkeys,
|
|
1288
2921
|
renderRow
|
|
1289
2922
|
}) {
|
|
1290
2923
|
const containerRef = React.useRef(null);
|
|
1291
2924
|
const ctx = useTreeContext();
|
|
2925
|
+
const isMulti = ctx.selectionMode === "multiple";
|
|
1292
2926
|
const { ref: keyboardRef } = useTreeKeyboard({
|
|
1293
2927
|
rows: ctx.flatRows,
|
|
1294
2928
|
focusedId: ctx.focused,
|
|
1295
|
-
|
|
2929
|
+
multiSelect: isMulti,
|
|
2930
|
+
// Pause container hotkeys while inline rename is active so the
|
|
2931
|
+
// user can type freely (TreeRenameInput stops bubbling already, but
|
|
2932
|
+
// gating here is the cleaner second line of defence).
|
|
2933
|
+
enabled: ctx.renamingId === null,
|
|
2934
|
+
onFocus: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((id, { extend }) => {
|
|
2935
|
+
if (extend && isMulti) {
|
|
2936
|
+
ctx.moveSelect(id, { extend: true });
|
|
2937
|
+
} else {
|
|
2938
|
+
ctx.setFocus(id);
|
|
2939
|
+
}
|
|
2940
|
+
}, "onFocus"),
|
|
1296
2941
|
onSelect: ctx.select,
|
|
1297
2942
|
onActivate: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((id) => {
|
|
1298
2943
|
const row = ctx.flatRows.find((r) => r.node.id === id);
|
|
@@ -1300,14 +2945,37 @@ function TreeRootShell({
|
|
|
1300
2945
|
}, "onActivate"),
|
|
1301
2946
|
onExpand: ctx.expand,
|
|
1302
2947
|
onCollapse: ctx.collapse,
|
|
1303
|
-
onClearSelection: ctx.clearSelection
|
|
2948
|
+
onClearSelection: ctx.clearSelection,
|
|
2949
|
+
onSelectAll: ctx.selectAll
|
|
2950
|
+
});
|
|
2951
|
+
const { ref: finderHotkeysRef } = useTreeFinderHotkeys({
|
|
2952
|
+
enabled: enableFinderHotkeys,
|
|
2953
|
+
paused: ctx.renamingId !== null,
|
|
2954
|
+
adapter: ctx.adapter,
|
|
2955
|
+
labels: ctx.labels,
|
|
2956
|
+
selected: ctx.selected,
|
|
2957
|
+
focused: ctx.focused,
|
|
2958
|
+
getNodeById: ctx.getNodeById,
|
|
2959
|
+
getItemName: ctx.getItemName,
|
|
2960
|
+
startInlineRename: ctx.inlineRenameEnabled ? ctx.startRename : void 0,
|
|
2961
|
+
clipboard: {
|
|
2962
|
+
hasItems: !!ctx.clipboard && ctx.clipboard.ids.length > 0,
|
|
2963
|
+
cut: ctx.cutToClipboard,
|
|
2964
|
+
copy: ctx.copyToClipboard,
|
|
2965
|
+
// Hotkey paste targets the currently focused row (or null = root).
|
|
2966
|
+
paste: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name(() => {
|
|
2967
|
+
const target = ctx.focused ? ctx.getNodeById(ctx.focused) ?? null : null;
|
|
2968
|
+
return ctx.pasteFromClipboard(target, "inside");
|
|
2969
|
+
}, "paste")
|
|
2970
|
+
}
|
|
1304
2971
|
});
|
|
1305
2972
|
const setContainerRef = React.useCallback(
|
|
1306
2973
|
(instance) => {
|
|
1307
2974
|
containerRef.current = instance;
|
|
1308
2975
|
keyboardRef(instance);
|
|
2976
|
+
finderHotkeysRef(instance);
|
|
1309
2977
|
},
|
|
1310
|
-
[keyboardRef]
|
|
2978
|
+
[keyboardRef, finderHotkeysRef]
|
|
1311
2979
|
);
|
|
1312
2980
|
const focusedId = ctx.focused;
|
|
1313
2981
|
React.useEffect(() => {
|
|
@@ -1330,7 +2998,22 @@ function TreeRootShell({
|
|
|
1330
2998
|
onMatch: onTypeAheadMatch,
|
|
1331
2999
|
enabled: enableTypeAhead
|
|
1332
3000
|
});
|
|
1333
|
-
|
|
3001
|
+
const finalRenderContextMenu = React.useMemo(() => {
|
|
3002
|
+
if (ctx.renderContextMenu) return ctx.renderContextMenu;
|
|
3003
|
+
const resolve = ctx.resolvedContextMenuActions;
|
|
3004
|
+
if (!resolve) return void 0;
|
|
3005
|
+
return (rowProps, trigger) => {
|
|
3006
|
+
const items = resolve(rowProps);
|
|
3007
|
+
const cleaned = items ? tidyMenuItems(items) : null;
|
|
3008
|
+
if (!cleaned || cleaned.length === 0) return trigger;
|
|
3009
|
+
return renderItemsAsContextMenu(rowProps, cleaned, trigger);
|
|
3010
|
+
};
|
|
3011
|
+
}, [ctx.renderContextMenu, ctx.resolvedContextMenuActions]);
|
|
3012
|
+
const childCtx = React.useMemo(
|
|
3013
|
+
() => ({ ...ctx, renderContextMenu: finalRenderContextMenu }),
|
|
3014
|
+
[ctx, finalRenderContextMenu]
|
|
3015
|
+
);
|
|
3016
|
+
const treeBody = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1334
3017
|
"div",
|
|
1335
3018
|
{
|
|
1336
3019
|
ref: setContainerRef,
|
|
@@ -1348,13 +3031,35 @@ function TreeRootShell({
|
|
|
1348
3031
|
"data-tree-root": "",
|
|
1349
3032
|
children: [
|
|
1350
3033
|
enableSearch ? /* @__PURE__ */ jsxRuntime.jsx(TreeSearchInput, { className: "mx-2 mt-2" }) : null,
|
|
1351
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3034
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 flex-col overflow-auto px-1", children: [
|
|
3035
|
+
/* @__PURE__ */ jsxRuntime.jsx(TreeContent, { role: "group", children: renderRow }),
|
|
3036
|
+
/* @__PURE__ */ jsxRuntime.jsx(TreeEmptyArea, {})
|
|
3037
|
+
] })
|
|
1352
3038
|
]
|
|
1353
3039
|
}
|
|
1354
3040
|
);
|
|
3041
|
+
const body = finalRenderContextMenu === ctx.renderContextMenu ? treeBody : /* @__PURE__ */ jsxRuntime.jsx(TreeContext.Provider, { value: childCtx, children: treeBody });
|
|
3042
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TreeDndProvider, { children: body });
|
|
1355
3043
|
}
|
|
1356
3044
|
chunkPK6SKIKE_cjs.__name(TreeRootShell, "TreeRootShell");
|
|
1357
3045
|
var TreeRoot_default = TreeRoot;
|
|
3046
|
+
function FinderTree(props) {
|
|
3047
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3048
|
+
TreeRoot,
|
|
3049
|
+
{
|
|
3050
|
+
selectionMode: "multiple",
|
|
3051
|
+
activationMode: "double-click",
|
|
3052
|
+
enableInlineRename: true,
|
|
3053
|
+
enableFinderHotkeys: true,
|
|
3054
|
+
enableDnD: true,
|
|
3055
|
+
enableTypeAhead: true,
|
|
3056
|
+
showIndentGuides: true,
|
|
3057
|
+
appearance: { density: "cozy" },
|
|
3058
|
+
...props
|
|
3059
|
+
}
|
|
3060
|
+
);
|
|
3061
|
+
}
|
|
3062
|
+
chunkPK6SKIKE_cjs.__name(FinderTree, "FinderTree");
|
|
1358
3063
|
function TreeSkeleton({ rows = 6, className }) {
|
|
1359
3064
|
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
3065
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-4 shrink-0 animate-pulse rounded bg-muted" }),
|
|
@@ -1408,35 +3113,48 @@ chunkPK6SKIKE_cjs.__name(createDemoTree, "createDemoTree");
|
|
|
1408
3113
|
|
|
1409
3114
|
exports.DEFAULT_TREE_APPEARANCE = DEFAULT_TREE_APPEARANCE;
|
|
1410
3115
|
exports.DEFAULT_TREE_LABELS = DEFAULT_TREE_LABELS;
|
|
3116
|
+
exports.FinderTree = FinderTree;
|
|
3117
|
+
exports.TREE_DND_MIME = TREE_DND_MIME;
|
|
1411
3118
|
exports.Tree = TreeRoot;
|
|
1412
3119
|
exports.TreeChevron = TreeChevron;
|
|
1413
3120
|
exports.TreeContent = TreeContent;
|
|
3121
|
+
exports.TreeDropIndicator = TreeDropIndicator;
|
|
1414
3122
|
exports.TreeEmpty = TreeEmpty;
|
|
3123
|
+
exports.TreeEmptyArea = TreeEmptyArea;
|
|
1415
3124
|
exports.TreeError = TreeError;
|
|
1416
3125
|
exports.TreeIcon = TreeIcon;
|
|
1417
3126
|
exports.TreeIndentGuides = TreeIndentGuides;
|
|
1418
3127
|
exports.TreeLabel = TreeLabel;
|
|
1419
3128
|
exports.TreeProvider = TreeProvider;
|
|
3129
|
+
exports.TreeRenameInput = TreeRenameInput;
|
|
1420
3130
|
exports.TreeRoot = TreeRoot;
|
|
1421
3131
|
exports.TreeRow = TreeRow;
|
|
1422
3132
|
exports.TreeSearchInput = TreeSearchInput;
|
|
1423
3133
|
exports.TreeSkeleton = TreeSkeleton;
|
|
1424
3134
|
exports.appearanceToStyle = appearanceToStyle;
|
|
3135
|
+
exports.autoSelectRange = autoSelectRange;
|
|
1425
3136
|
exports.clearTreeState = clearTreeState;
|
|
1426
3137
|
exports.createChildCache = createChildCache;
|
|
1427
3138
|
exports.createDemoTree = createDemoTree;
|
|
1428
3139
|
exports.default = TreeRoot_default;
|
|
3140
|
+
exports.defaultCanDrop = defaultCanDrop;
|
|
1429
3141
|
exports.flattenTree = flattenTree;
|
|
1430
3142
|
exports.loadTreeState = loadTreeState;
|
|
1431
3143
|
exports.resolveAppearance = resolveAppearance;
|
|
1432
3144
|
exports.resolveChildren = resolveChildren;
|
|
3145
|
+
exports.resolveDropZone = resolveDropZone;
|
|
1433
3146
|
exports.saveTreeState = saveTreeState;
|
|
3147
|
+
exports.splitFileName = splitFileName;
|
|
1434
3148
|
exports.useTreeActions = useTreeActions;
|
|
3149
|
+
exports.useTreeClipboard = useTreeClipboard;
|
|
1435
3150
|
exports.useTreeContext = useTreeContext;
|
|
3151
|
+
exports.useTreeDnd = useTreeDnd;
|
|
1436
3152
|
exports.useTreeExpansion = useTreeExpansion;
|
|
3153
|
+
exports.useTreeFinderHotkeys = useTreeFinderHotkeys;
|
|
1437
3154
|
exports.useTreeFocus = useTreeFocus;
|
|
1438
3155
|
exports.useTreeKeyboard = useTreeKeyboard;
|
|
1439
3156
|
exports.useTreeLabels = useTreeLabels;
|
|
3157
|
+
exports.useTreeRename = useTreeRename;
|
|
1440
3158
|
exports.useTreeRows = useTreeRows;
|
|
1441
3159
|
exports.useTreeSearch = useTreeSearch;
|
|
1442
3160
|
exports.useTreeSelection = useTreeSelection;
|