From: PhiTux Date: Mon, 15 Sep 2025 16:11:22 +0000 (+0200) Subject: drag-n-drop for file upload added X-Git-Url: http://git.99rst.org/?a=commitdiff_plain;h=d2f9eeb874909874da75380f627cde289c88f208;p=DailyTxT.git drag-n-drop for file upload added --- diff --git a/frontend/src/routes/(authed)/write/+page.svelte b/frontend/src/routes/(authed)/write/+page.svelte index 2de6312..09dd005 100644 --- a/frontend/src/routes/(authed)/write/+page.svelte +++ b/frontend/src/routes/(authed)/write/+page.svelte @@ -416,6 +416,104 @@ } let uploadingFiles = $state([]); + let isDragOver = $state(false); + let dragCounter = $state(0); // Track drag enter/leave events + let draggedFileCount = $state(0); // Store file count during drag + + // Drag and drop handlers + function handleDragEnter(event) { + event.preventDefault(); + + // Check if dragging files, not text or other content + if (!hasFiles(event.dataTransfer)) { + return; + } + + dragCounter++; + if (dragCounter === 1) { + isDragOver = true; + // Try to get file count + extractDragInfo(event); + } + } + + function handleDragLeave(event) { + event.preventDefault(); + + // Only handle if we're actually dragging files + if (!isDragOver) return; + + dragCounter--; + if (dragCounter === 0) { + isDragOver = false; + draggedFileCount = 0; + } + } + + function handleDragOver(event) { + event.preventDefault(); + + // Check if dragging files, not text or other content + if (!hasFiles(event.dataTransfer)) { + return; + } + + event.dataTransfer.dropEffect = 'copy'; + // Try again if we haven't got info yet + if (draggedFileCount === 0) { + extractDragInfo(event); + } + } + + function hasFiles(dataTransfer) { + // Check if dataTransfer contains files + return ( + dataTransfer.types && + (dataTransfer.types.includes('Files') || + dataTransfer.types.includes('application/x-moz-file') || + (dataTransfer.items && Array.from(dataTransfer.items).some((item) => item.kind === 'file'))) + ); + } + + function extractDragInfo(event) { + // Double-check that we have files + if (!hasFiles(event.dataTransfer)) { + return; + } + + const items = event.dataTransfer.items; + if (items && items.length > 0) { + let fileCount = 0; + + for (let i = 0; i < items.length; i++) { + const item = items[i]; + if (item.kind === 'file') { + fileCount++; + } + } + + if (fileCount > 0) { + draggedFileCount = fileCount; + } + } + } + + function handleDrop(event) { + event.preventDefault(); + + // Reset drag state + isDragOver = false; + dragCounter = 0; + draggedFileCount = 0; + + // Check if we actually have files to upload + const files = event.dataTransfer.files; + if (files && files.length > 0) { + for (let i = 0; i < files.length; i++) { + uploadFile(files[i]); + } + } + } function uploadFile(f) { let uuid = uuidv4(); @@ -943,7 +1041,33 @@ - + + + +{#if isDragOver} +
+
+ +

{$t('files.drop.title')}

+ +
+

+ 🎯 {$t('files.drop.ready_to_upload', { count: draggedFileCount })} +

+
+

{$t('files.drop.release_to_upload')}

+
+
+
+
+{/if}
@@ -1110,10 +1234,31 @@
{ + e.preventDefault(); + e.currentTarget.classList.add('drag-hover'); + }} + ondragleave={(e) => { + e.preventDefault(); + e.currentTarget.classList.remove('drag-hover'); + }} + ondragover={(e) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'copy'; + }} + ondrop={(e) => { + e.preventDefault(); + e.currentTarget.classList.remove('drag-hover'); + const files = e.dataTransfer.files; + if (files && files.length > 0) { + for (let i = 0; i < files.length; i++) { + uploadFile(files[i]); + } + } + }}>{$t('files.upload')} @@ -1780,4 +1925,79 @@ width: 400px; padding-right: 2rem; } + + /* Drag and Drop Styles */ + .drag-drop-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(4px); + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + pointer-events: none; + } + + .drag-drop-content { + text-align: center; + color: white; + background-color: rgba(255, 255, 255, 0.1); + border: 2px dashed rgba(255, 255, 255, 0.5); + border-radius: 20px; + padding: 3rem; + max-width: 600px; + max-height: 80vh; + overflow-y: auto; + } + + .drag-drop-content h3 { + margin-bottom: 1rem; + font-size: 2rem; + } + + .drag-drop-content p { + font-size: 1.2rem; + opacity: 0.9; + } + + .dragged-files-preview { + margin-top: 1.5rem; + text-align: center; + } + + .files-count { + font-size: 1.3rem; + margin-bottom: 1rem; + font-weight: 500; + color: #90ee90; + } + + .file-drop-info { + background-color: rgba(0, 0, 0, 0.2); + border-radius: 10px; + padding: 1rem; + margin-top: 1rem; + } + + .drop-instruction { + font-size: 1.1rem; + margin: 0; + opacity: 0.9; + } + + :global(.upload-btn.drag-hover) { + background-color: #198754 !important; + border-color: #198754 !important; + transform: scale(1.05); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + } + + .upload-btn { + transition: all 0.2s ease; + border: 2px dashed transparent; + }