feature: Implement drag-and-drop functionality for file downloads

This commit is contained in:
Keith Solomon
2026-01-04 17:56:04 -06:00
parent 861b7b63cb
commit 5eece32dbc
2 changed files with 76 additions and 15 deletions

View File

@@ -128,6 +128,23 @@ function renderLocal() {
state.localDir = entry.path; state.localDir = entry.path;
await refreshLocal(); await refreshLocal();
}); });
row.addEventListener('dragover', (event) => {
if (!entry.isDir) return;
event.preventDefault();
row.classList.add('drop-target');
});
row.addEventListener('dragleave', () => {
row.classList.remove('drop-target');
});
row.addEventListener('drop', async (event) => {
if (!entry.isDir) return;
event.preventDefault();
row.classList.remove('drop-target');
const payloadRaw = event.dataTransfer.getData('application/oddl');
if (!payloadRaw) return;
const payload = JSON.parse(payloadRaw);
await triggerDownload(payload, entry.path);
});
el.localListBody.appendChild(row); el.localListBody.appendChild(row);
}); });
} }
@@ -160,6 +177,15 @@ function renderRemote() {
row.dataset.href = entry.href; row.dataset.href = entry.href;
row.dataset.isDir = entry.isDir ? '1' : '0'; row.dataset.isDir = entry.isDir ? '1' : '0';
}); });
row.setAttribute('draggable', 'true');
row.addEventListener('dragstart', (event) => {
const payload = {
url: entry.href,
isDir: entry.isDir,
};
event.dataTransfer.setData('application/oddl', JSON.stringify(payload));
event.dataTransfer.effectAllowed = 'copy';
});
el.remoteListBody.appendChild(row); el.remoteListBody.appendChild(row);
}); });
} }
@@ -324,6 +350,23 @@ async function refreshRemote() {
renderRemoteTree(); renderRemoteTree();
} }
async function triggerDownload(payload, destinationDir) {
if (!destinationDir) {
log('Choose a local destination first.');
return;
}
if (!payload?.url) return;
log(`Queueing ${payload.url}`);
await window.oddl.downloadItem({
url: payload.url,
destDir: destinationDir,
isDir: Boolean(payload.isDir),
useWget: el.useWget.checked,
});
log('Done.');
await refreshLocal();
}
function setupResizer(listEl, resizerEl) { function setupResizer(listEl, resizerEl) {
let startX = 0; let startX = 0;
let startWidth = 0; let startWidth = 0;
@@ -416,21 +459,29 @@ el.downloadBtn.addEventListener('click', async () => {
log('Select a remote file or folder first.'); log('Select a remote file or folder first.');
return; return;
} }
if (!state.localDir) { const payload = {
log('Choose a local destination first.'); url: selected.dataset.href,
return; isDir: selected.dataset.isDir === '1',
} };
const href = selected.dataset.href; await triggerDownload(payload, state.localDir);
const isDir = selected.dataset.isDir === '1'; });
log(`Queueing ${href}`);
await window.oddl.downloadItem({ el.localListBody.addEventListener('dragover', (event) => {
url: href, event.preventDefault();
destDir: state.localDir, el.localListBody.closest('.panel-body')?.classList.add('drop-target');
isDir, });
useWget: el.useWget.checked,
}); el.localListBody.addEventListener('dragleave', () => {
log('Done.'); el.localListBody.closest('.panel-body')?.classList.remove('drop-target');
await refreshLocal(); });
el.localListBody.addEventListener('drop', async (event) => {
event.preventDefault();
el.localListBody.closest('.panel-body')?.classList.remove('drop-target');
const payloadRaw = event.dataTransfer.getData('application/oddl');
if (!payloadRaw) return;
const payload = JSON.parse(payloadRaw);
await triggerDownload(payload, state.localDir);
}); });
window.oddl.onLog((message) => log(message)); window.oddl.onLog((message) => log(message));

View File

@@ -277,6 +277,16 @@ body {
background: var(--selected); background: var(--selected);
} }
.row.drop-target {
outline: 2px dashed var(--progress-fill-start);
outline-offset: -2px;
}
.panel-body.drop-target {
outline: 2px dashed var(--progress-fill-start);
outline-offset: -6px;
}
.log { .log {
margin: 0 24px 24px; margin: 0 24px 24px;
border: 1px solid var(--border); border: 1px solid var(--border);