From 8fd47d8b180f567502e891e62e43079b7b9918e6 Mon Sep 17 00:00:00 2001 From: vcoppe Date: Sat, 17 Oct 2026 23:39:44 +0200 Subject: [PATCH] drop anywhere in folder --- .../components/explorer/ExplorerRow.svelte | 23 ++++++++++++------- .../lib/components/explorer/state.svelte.ts | 6 +++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/website/src/lib/components/explorer/ExplorerRow.svelte b/website/src/lib/components/explorer/ExplorerRow.svelte index d11f769b8..3fa254a1f 100644 --- a/website/src/lib/components/explorer/ExplorerRow.svelte +++ b/website/src/lib/components/explorer/ExplorerRow.svelte @@ -23,6 +23,7 @@ editableName, explorerSelection, isWithin, + parentOf, renaming, } from './state.svelte'; @@ -206,28 +207,34 @@ dragging.target = undefined; } + /** The folder where what is dropped on the row goes: the folder of a file, among its files. */ + let dropFolder = $derived(kind === 'file' ? parentOf(path) : path); + function ondragover(event: DragEvent) { - if (kind === 'file' || dragging.paths.length === 0) return; - // not into itself - if (dragging.paths.some((dragged) => isWithin(path, dragged))) return; + if (dragging.paths.length === 0) return; + // not into itself, nor where it is already + if (dragging.paths.some((dragged) => isWithin(dropFolder, dragged))) return; + if (dragging.paths.every((dragged) => parentOf(dragged) === dropFolder)) return; event.preventDefault(); if (event.dataTransfer) { event.dataTransfer.dropEffect = 'move'; } - dragging.target = path; + // the row of the folder shows it + dragging.target = dropFolder; } function ondragleave(event: DragEvent) { - if (dragging.target === path && !row?.contains(event.relatedTarget as Node | null)) { - dragging.target = undefined; - } + // to another row, which tells where it would go (it was entered first) + const to = event.relatedTarget as Element | null; + if (to?.closest?.('[data-explorer-path]')) return; + dragging.target = undefined; } async function ondrop(event: DragEvent) { event.preventDefault(); const paths = dragging.paths; ondragend(); - if (paths.length > 0 && !(await engine.movePaths(paths, path))) { + if (paths.length > 0 && !(await engine.movePaths(paths, dropFolder))) { showError(); } } diff --git a/website/src/lib/components/explorer/state.svelte.ts b/website/src/lib/components/explorer/state.svelte.ts index 46c3fe2c2..383c4f60e 100644 --- a/website/src/lib/components/explorer/state.svelte.ts +++ b/website/src/lib/components/explorer/state.svelte.ts @@ -33,6 +33,12 @@ function isSeparator(char: string | undefined) { return char === '/' || char === '\\'; } +/** The folder that holds `path`. */ +export function parentOf(path: string): string { + const index = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')); + return index > 0 ? path.slice(0, index) : path; +} + /** Whether `path` is `folder` or something in it. */ export function isWithin(path: string, folder: string): boolean { return path === folder || (path.startsWith(folder) && isSeparator(path[folder.length]));