Make internal drag and drop robust

This commit is contained in:
Mikei386
2026-06-22 23:15:08 +02:00
parent f5d28c403c
commit b449a74cad
8 changed files with 78 additions and 13 deletions
+48 -8
View File
@@ -7,6 +7,7 @@ const state = {
focusedId: null,
selectedEntry: null,
contextMenu: null,
draggedEntry: null,
jobs: new Map(),
nextWindowId: 1,
zIndex: 20
@@ -196,11 +197,20 @@ function renderExplorer(body, win) {
body.querySelector('[data-action="up"]').addEventListener('click', () => loadExplorer(win, parentPath(data.path)));
const dropZone = body.querySelector('.drop-zone');
dropZone.addEventListener('dragenter', (event) => {
if (hasDropPayload(event)) {
event.preventDefault();
data.dragOver = true;
dropZone.classList.add('drag-over');
}
});
dropZone.addEventListener('dragover', (event) => {
event.preventDefault();
event.dataTransfer.dropEffect = event.altKey ? 'copy' : 'move';
data.dragOver = true;
dropZone.classList.add('drag-over');
if (hasDropPayload(event)) {
event.preventDefault();
event.dataTransfer.dropEffect = event.altKey ? 'copy' : 'move';
data.dragOver = true;
dropZone.classList.add('drag-over');
}
});
dropZone.addEventListener('dragleave', () => {
data.dragOver = false;
@@ -210,7 +220,11 @@ function renderExplorer(body, win) {
event.preventDefault();
data.dragOver = false;
dropZone.classList.remove('drag-over');
await handleDrop(event, win, data.path);
try {
await handleDrop(event, win, data.path);
} catch (error) {
showError(error);
}
});
dropZone.addEventListener('contextmenu', (event) => {
event.preventDefault();
@@ -227,6 +241,7 @@ function renderExplorer(body, win) {
}
});
row.addEventListener('dragstart', (event) => {
state.draggedEntry = { path: entry.path, name: entry.name, sourceWindowId: win.id };
event.dataTransfer.setData('application/x-u-navigator-path', entry.path);
event.dataTransfer.setData('text/plain', entry.path);
event.dataTransfer.effectAllowed = 'copyMove';
@@ -234,6 +249,9 @@ function renderExplorer(body, win) {
});
row.addEventListener('dragend', () => {
row.classList.remove('dragging');
setTimeout(() => {
state.draggedEntry = null;
}, 0);
});
row.addEventListener('dragover', (event) => {
if (entry.type === 'directory') {
@@ -246,7 +264,11 @@ function renderExplorer(body, win) {
if (entry.type !== 'directory') return;
event.preventDefault();
event.stopPropagation();
await handleDrop(event, win, entry.path);
try {
await handleDrop(event, win, entry.path);
} catch (error) {
showError(error);
}
});
row.addEventListener('contextmenu', (event) => {
event.preventDefault();
@@ -337,12 +359,15 @@ async function handleDrop(event, win, targetPath) {
return;
}
const internalPath = event.dataTransfer.getData('application/x-u-navigator-path') || event.dataTransfer.getData('text/plain');
const internalPath = event.dataTransfer.getData('application/x-u-navigator-path') || event.dataTransfer.getData('text/plain') || state.draggedEntry?.path;
if (internalPath) {
const name = internalPath.split('/').filter(Boolean).at(-1);
const destination = `${targetPath.replace(/\/$/, '')}/${name}`;
if (internalPath === destination) {
throw new Error('Quelle und Ziel sind identisch.');
}
await createJob(event.altKey ? 'copy' : 'move', internalPath, destination);
await loadExplorer(win, win.data.path);
await reloadExplorerWindows([win.id, state.draggedEntry?.sourceWindowId].filter(Boolean));
return;
}
@@ -370,6 +395,21 @@ async function createJob(type, source, destination) {
render();
}
async function reloadExplorerWindows(ids) {
const uniqueIds = new Set(ids);
const targets = state.windows.filter((win) => win.kind === 'explorer' && (uniqueIds.has(win.id) || !uniqueIds.size));
await Promise.all(targets.map((win) => loadExplorer(win, win.data.path)));
}
function hasDropPayload(event) {
return Boolean(state.draggedEntry || event.dataTransfer?.types?.includes('Files') || event.dataTransfer?.types?.includes('application/x-u-navigator-path') || event.dataTransfer?.types?.includes('text/plain'));
}
function showError(error) {
const message = error?.message || String(error);
alert(message);
}
async function renameEntry(win, entry) {
const nextName = prompt('Neuer Name', entry.name);
if (!nextName || nextName === entry.name) {
+3 -1
View File
@@ -191,8 +191,10 @@
}
.u-nav .drop-zone.drag-over {
background: color-mix(in srgb, var(--accent) 10%, transparent);
background: color-mix(in srgb, var(--accent) 18%, transparent);
border-color: var(--accent);
border-style: solid;
box-shadow: inset 0 0 0 2px var(--accent);
}
.u-nav .file-table {