vert/src/lib/components/functional/Uploader.svelte

116 lines
3.0 KiB
Svelte

<script lang="ts">
import { Upload } from "lucide-svelte";
import clsx from "clsx";
import { onMount } from "svelte";
let fileList = $state<FileList>();
let dragBtn = $state<HTMLButtonElement>();
interface Props {
files: File[] | undefined;
}
$effect(() => {
if (!fileList) return;
files = Array.from(fileList);
});
let fileInput = $state<HTMLInputElement>();
let dragOver = $state(false);
let { files = $bindable() }: Props = $props();
function upload() {
if (!fileInput) return;
fileInput.click();
}
onMount(() => {
const handler = (e: Event) => e.preventDefault();
if (!dragBtn) return;
dragBtn.addEventListener("dragenter", handler);
dragBtn.addEventListener("dragstart", handler);
dragBtn.addEventListener("dragend", handler);
dragBtn.addEventListener("dragleave", handler);
dragBtn.addEventListener("dragover", handler);
dragBtn.addEventListener("drag", handler);
dragBtn.addEventListener("drop", handler);
return () => {
if (!dragBtn) return;
dragBtn.removeEventListener("dragenter", handler);
dragBtn.removeEventListener("dragstart", handler);
dragBtn.removeEventListener("dragend", handler);
dragBtn.removeEventListener("dragleave", handler);
dragBtn.removeEventListener("dragover", handler);
dragBtn.removeEventListener("drag", handler);
dragBtn.removeEventListener("drop", handler);
};
});
function drop(event: DragEvent) {
event.preventDefault();
dragOver = false;
if (!event.dataTransfer) return;
if (!files) files = Array.from(event.dataTransfer.files);
else files.push(...Array.from(event.dataTransfer.files));
return true;
}
function addFiles() {
if (!fileInput) return;
if (!fileInput.files) return;
if (!files) files = Array.from(fileInput.files);
else files.push(...Array.from(fileInput.files));
}
</script>
<button
bind:this={dragBtn}
onclick={upload}
ondragover={() => (dragOver = true)}
ondragleave={() => (dragOver = false)}
class={clsx(
"file-uploader",
"w-full h-80 flex items-center justify-center cursor-pointer",
"border-2 border-solid border-foreground-muted-alt rounded-2xl",
"hover:scale-95 hover:opacity-70 transition-all duration-150 ease-out",
{
"scale-95 opacity-70 blur-xs": dragOver,
},
)}
class:_drag-over={dragOver}
ondrop={drop}
>
<div
class="file-uploader-center flex items-center justify-center flex-col transition-all duration-150 ease-out"
>
<div
class="size-16 rounded-full text-accent-foreground bg-accent-background flex items-center justify-center"
>
<Upload class="size-8" />
</div>
<h2 class="font-display text-2xl mt-6">
Drop or click to upload files
</h2>
<p class="text-foreground-muted mt-4">
All processing is done on your device. No file or size limit.
</p>
</div>
</button>
<input
type="file"
class="hidden"
bind:this={fileInput}
onchange={addFiles}
multiple
/>
<style>
.file-uploader:hover .file-uploader-center,
.file-uploader._drag-over .file-uploader-center {
@apply scale-105;
}
</style>