Parcourir la source

feat: drag and drop document anywhere

Timothy J. Baek il y a 1 an
Parent
commit
485236624f
2 fichiers modifiés avec 170 ajouts et 126 suppressions
  1. 5 3
      src/lib/components/AddFilesPlaceholder.svelte
  2. 165 123
      src/routes/(app)/documents/+page.svelte

+ 5 - 3
src/lib/components/AddFilesPlaceholder.svelte

@@ -1,6 +1,8 @@
 <div class="  text-center text-6xl mb-3">📄</div>
 <div class="text-center dark:text-white text-2xl font-semibold z-50">Add Files</div>
 
-<div class=" mt-2 text-center text-sm dark:text-gray-200 w-full">
-	Drop any files here to add to the conversation
-</div>
+<slot
+	><div class=" mt-2 text-center text-sm dark:text-gray-200 w-full">
+		Drop any files here to add to the conversation
+	</div>
+</slot>

+ 165 - 123
src/routes/(app)/documents/+page.svelte

@@ -12,6 +12,7 @@
 	import { transformFileName } from '$lib/utils';
 
 	import EditDocModal from '$lib/components/documents/EditDocModal.svelte';
+	import AddFilesPlaceholder from '$lib/components/AddFilesPlaceholder.svelte';
 
 	let importFiles = '';
 
@@ -49,44 +50,94 @@
 		}
 	};
 
-	const onDragOver = (e) => {
-		e.preventDefault();
-		dragged = true;
-	};
+	onMount(() => {
+		const dropZone = document.querySelector('body');
 
-	const onDragLeave = () => {
-		dragged = false;
-	};
+		const onDragOver = (e) => {
+			e.preventDefault();
+			dragged = true;
+		};
+
+		const onDragLeave = () => {
+			dragged = false;
+		};
 
-	const onDrop = async (e) => {
-		e.preventDefault();
-		console.log(e);
+		const onDrop = async (e) => {
+			e.preventDefault();
+			console.log(e);
 
-		if (e.dataTransfer?.files) {
-			const inputFiles = e.dataTransfer?.files;
+			if (e.dataTransfer?.files) {
+				let reader = new FileReader();
 
-			if (inputFiles && inputFiles.length > 0) {
-				const file = inputFiles[0];
-				if (
-					SUPPORTED_FILE_TYPE.includes(file['type']) ||
-					SUPPORTED_FILE_EXTENSIONS.includes(file.name.split('.').at(-1))
-				) {
-					uploadDoc(file);
+				reader.onload = (event) => {
+					files = [
+						...files,
+						{
+							type: 'image',
+							url: `${event.target.result}`
+						}
+					];
+				};
+
+				const inputFiles = e.dataTransfer?.files;
+
+				if (inputFiles && inputFiles.length > 0) {
+					const file = inputFiles[0];
+					console.log(file, file.name.split('.').at(-1));
+					if (['image/gif', 'image/jpeg', 'image/png'].includes(file['type'])) {
+						reader.readAsDataURL(file);
+					} else if (
+						SUPPORTED_FILE_TYPE.includes(file['type']) ||
+						SUPPORTED_FILE_EXTENSIONS.includes(file.name.split('.').at(-1))
+					) {
+						uploadDoc(file);
+					} else {
+						toast.error(
+							`Unknown File Type '${file['type']}', but accepting and treating as plain text`
+						);
+						uploadDoc(file);
+					}
 				} else {
-					toast.error(
-						`Unknown File Type '${file['type']}', but accepting and treating as plain text`
-					);
-					uploadDoc(file);
+					toast.error(`File not found.`);
 				}
-			} else {
-				toast.error(`File not found.`);
 			}
-		}
 
-		dragged = false;
-	};
+			dragged = false;
+		};
+
+		dropZone?.addEventListener('dragover', onDragOver);
+		dropZone?.addEventListener('drop', onDrop);
+		dropZone?.addEventListener('dragleave', onDragLeave);
+
+		return () => {
+			dropZone?.removeEventListener('dragover', onDragOver);
+			dropZone?.removeEventListener('drop', onDrop);
+			dropZone?.removeEventListener('dragleave', onDragLeave);
+		};
+	});
 </script>
 
+{#if dragged}
+	<div
+		class="fixed w-full h-full flex z-50 touch-none pointer-events-none"
+		id="dropzone"
+		role="region"
+		aria-label="Drag and Drop Container"
+	>
+		<div class="absolute rounded-xl w-full h-full backdrop-blur bg-gray-800/40 flex justify-center">
+			<div class="m-auto pt-64 flex flex-col justify-center">
+				<div class="max-w-md">
+					<AddFilesPlaceholder>
+						<div class=" mt-2 text-center text-sm dark:text-gray-200 w-full">
+							Drop any files here to add to my documents
+						</div>
+					</AddFilesPlaceholder>
+				</div>
+			</div>
+		</div>
+	</div>
+{/if}
+
 {#key selectedDoc}
 	<EditDocModal bind:show={showEditDocModal} {selectedDoc} />
 {/key}
@@ -170,7 +221,7 @@
 				}}
 			/>
 
-			<div>
+			<!-- <div>
 				<div
 					class="my-3 py-16 rounded-lg border-2 border-dashed dark:border-gray-600 {dragged &&
 						' dark:bg-gray-700'} "
@@ -187,7 +238,7 @@
 						</div>
 					</div>
 				</div>
-			</div>
+			</div> -->
 
 			{#each $documents.filter((p) => query === '' || p.name.includes(query)) as doc}
 				<hr class=" dark:border-gray-700 my-2.5" />
@@ -330,106 +381,97 @@
 					</div>
 				</div>
 			{/each}
-			{#if $documents.length != 0}
-				<hr class=" dark:border-gray-700 my-2.5" />
 
-				<div class=" flex justify-between w-full mb-3">
-					<div class="flex space-x-2">
-						<input
-							id="documents-import-input"
-							bind:files={importFiles}
-							type="file"
-							accept=".json"
-							hidden
-							on:change={() => {
-								console.log(importFiles);
-
-								const reader = new FileReader();
-								reader.onload = async (event) => {
-									const savedDocs = JSON.parse(event.target.result);
-									console.log(savedDocs);
-
-									for (const doc of savedDocs) {
-										await createNewDoc(
-											localStorage.token,
-											doc.collection_name,
-											doc.filename,
-											doc.name,
-											doc.title
-										).catch((error) => {
-											toast.error(error);
-											return null;
-										});
-									}
-
-									await documents.set(await getDocs(localStorage.token));
-								};
-
-								reader.readAsText(importFiles[0]);
-							}}
-						/>
+			<hr class=" dark:border-gray-700 my-2.5" />
 
-						<button
-							class="self-center w-fit text-sm px-3 py-1 border dark:border-gray-600 rounded-xl flex"
-							on:click={async () => {
-								document.getElementById('documents-import-input')?.click();
-							}}
-						>
-							<div class=" self-center mr-2 font-medium">Import Documents Mapping</div>
-
-							<div class=" self-center">
-								<svg
-									xmlns="http://www.w3.org/2000/svg"
-									viewBox="0 0 16 16"
-									fill="currentColor"
-									class="w-4 h-4"
-								>
-									<path
-										fill-rule="evenodd"
-										d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 9.5a.75.75 0 0 1-.75-.75V8.06l-.72.72a.75.75 0 0 1-1.06-1.06l2-2a.75.75 0 0 1 1.06 0l2 2a.75.75 0 1 1-1.06 1.06l-.72-.72v2.69a.75.75 0 0 1-.75.75Z"
-										clip-rule="evenodd"
-									/>
-								</svg>
-							</div>
-						</button>
+			<div class=" flex justify-between w-full mb-3">
+				<div class="flex space-x-2">
+					<input
+						id="documents-import-input"
+						bind:files={importFiles}
+						type="file"
+						accept=".json"
+						hidden
+						on:change={() => {
+							console.log(importFiles);
+
+							const reader = new FileReader();
+							reader.onload = async (event) => {
+								const savedDocs = JSON.parse(event.target.result);
+								console.log(savedDocs);
+
+								for (const doc of savedDocs) {
+									await createNewDoc(
+										localStorage.token,
+										doc.collection_name,
+										doc.filename,
+										doc.name,
+										doc.title
+									).catch((error) => {
+										toast.error(error);
+										return null;
+									});
+								}
+
+								await documents.set(await getDocs(localStorage.token));
+							};
+
+							reader.readAsText(importFiles[0]);
+						}}
+					/>
 
-						<button
-							class="self-center w-fit text-sm px-3 py-1 border dark:border-gray-600 rounded-xl flex"
-							on:click={async () => {
-								let blob = new Blob([JSON.stringify($documents)], {
-									type: 'application/json'
-								});
-								saveAs(blob, `documents-mapping-export-${Date.now()}.json`);
-							}}
-						>
-							<div class=" self-center mr-2 font-medium">Export Documents Mapping</div>
-
-							<div class=" self-center">
-								<svg
-									xmlns="http://www.w3.org/2000/svg"
-									viewBox="0 0 16 16"
-									fill="currentColor"
-									class="w-4 h-4"
-								>
-									<path
-										fill-rule="evenodd"
-										d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
-										clip-rule="evenodd"
-									/>
-								</svg>
-							</div>
-						</button>
+					<button
+						class="self-center w-fit text-sm px-3 py-1 border dark:border-gray-600 rounded-xl flex"
+						on:click={async () => {
+							document.getElementById('documents-import-input')?.click();
+						}}
+					>
+						<div class=" self-center mr-2 font-medium">Import Documents Mapping</div>
 
-						<!-- <button
-						on:click={() => {
-							loadDefaultPrompts();
+						<div class=" self-center">
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 16 16"
+								fill="currentColor"
+								class="w-4 h-4"
+							>
+								<path
+									fill-rule="evenodd"
+									d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 9.5a.75.75 0 0 1-.75-.75V8.06l-.72.72a.75.75 0 0 1-1.06-1.06l2-2a.75.75 0 0 1 1.06 0l2 2a.75.75 0 1 1-1.06 1.06l-.72-.72v2.69a.75.75 0 0 1-.75.75Z"
+									clip-rule="evenodd"
+								/>
+							</svg>
+						</div>
+					</button>
+
+					<button
+						class="self-center w-fit text-sm px-3 py-1 border dark:border-gray-600 rounded-xl flex"
+						on:click={async () => {
+							let blob = new Blob([JSON.stringify($documents)], {
+								type: 'application/json'
+							});
+							saveAs(blob, `documents-mapping-export-${Date.now()}.json`);
 						}}
 					>
-						dd
-					</button> -->
-					</div>
+						<div class=" self-center mr-2 font-medium">Export Documents Mapping</div>
+
+						<div class=" self-center">
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 16 16"
+								fill="currentColor"
+								class="w-4 h-4"
+							>
+								<path
+									fill-rule="evenodd"
+									d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
+									clip-rule="evenodd"
+								/>
+							</svg>
+						</div>
+					</button>
 				</div>
-			{/if}
+			</div>
 
 			<div class="text-xs flex items-center space-x-1">
 				<div>