feat: add drag-and-drop file upload to Cloud folders

Drag files over the native file browser area to see a drop zone overlay
with dashed orange border. Dropping files triggers the existing upload
handler. Uses debounced dragleave to prevent flicker between children.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-05 07:08:57 +00:00
parent 37105e6be6
commit 621d74bfb3
3 changed files with 67 additions and 2 deletions

View File

@@ -45,7 +45,7 @@ After getting Claude Max OAuth working on the live server, hardening the deploy
- **Change**: Add `getUsage()` method to filebrowser-client. In Home.vue, replace hardcoded "2.4 GB" and "5" folders with real data from FileBrowser API. Add `formatBytes()` helper. Show loading state while fetching.
- **Verify**: Home Cloud card shows real storage numbers from FileBrowser
### Task 8: Cloud file drag-and-drop upload
### Task 8: Cloud file drag-and-drop upload [DONE]
- **Files**: `neode-ui/src/views/CloudFolder.vue`, `neode-ui/src/style.css`
- **Change**: Add drag-and-drop overlay with `@dragover.prevent` and `@drop.prevent`. Show visual drop zone when dragging. Extract files from `dataTransfer` and call existing `handleUpload()`.
- **Verify**: Drag a file over CloudFolder — drop zone overlay appears, dropping uploads file

View File

@@ -1220,3 +1220,31 @@ html:has(body.video-background-active)::before {
border-radius: 2px;
transition: width 0.3s linear;
}
/* ── Cloud Drag-and-Drop Overlay ──── */
.cloud-drop-overlay {
position: absolute;
inset: 0;
z-index: 30;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border-radius: 0.75rem;
border: 2px dashed rgba(251, 146, 60, 0.6);
animation: drop-overlay-in 0.2s ease-out;
}
.cloud-drop-overlay-inner {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 2rem;
}
@keyframes drop-overlay-in {
from { opacity: 0; }
to { opacity: 1; }
}

View File

@@ -72,7 +72,23 @@
</div>
<!-- Native File Browser (for FileBrowser-backed sections) -->
<div v-else-if="useNativeUI" class="flex-1 min-h-0 flex flex-col">
<div
v-else-if="useNativeUI"
class="flex-1 min-h-0 flex flex-col relative"
@dragover.prevent="onDragOver"
@dragleave="onDragLeave"
@drop.prevent="onDrop"
>
<!-- Drag-and-drop overlay -->
<div v-if="draggingOver" class="cloud-drop-overlay">
<div class="cloud-drop-overlay-inner">
<svg class="w-12 h-12 text-white/80 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
<p class="text-lg font-medium text-white/90">Drop files to upload</p>
<p class="text-sm text-white/50">Files will be added to the current folder</p>
</div>
</div>
<!-- Upload progress -->
<div v-if="uploading" class="glass-card p-3 mb-3 flex items-center gap-3">
<div class="w-5 h-5 border-2 border-white/20 border-t-white/80 rounded-full animate-spin"></div>
@@ -274,6 +290,27 @@ watch(useNativeUI, async (native) => {
}, { immediate: true })
const uploadError = ref<string | null>(null)
const draggingOver = ref(false)
let dragLeaveTimer: ReturnType<typeof setTimeout> | null = null
function onDragOver() {
if (dragLeaveTimer) { clearTimeout(dragLeaveTimer); dragLeaveTimer = null }
draggingOver.value = true
}
function onDragLeave() {
// Debounce to avoid flicker when dragging between child elements
if (dragLeaveTimer) clearTimeout(dragLeaveTimer)
dragLeaveTimer = setTimeout(() => { draggingOver.value = false }, 100)
}
function onDrop(e: DragEvent) {
draggingOver.value = false
if (dragLeaveTimer) { clearTimeout(dragLeaveTimer); dragLeaveTimer = null }
const dt = e.dataTransfer
if (!dt?.files?.length) return
handleUpload(Array.from(dt.files))
}
async function handleUpload(files: File[]) {
uploading.value = true