Files
familienarchiv/frontend/src/lib/document/UploadZone.svelte
Marcel e7f8aa5894 refactor: move document domain core to lib/document/
Moves ~25 components, utils (search, filename, groupDocuments,
documentStatusLabel, validateFile), bulkSelection store, and
TranscriptionSection sub-component. Fixes broken relative imports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 13:56:36 +02:00

116 lines
3.3 KiB
Svelte

<script lang="ts">
const ALLOWED_TYPES = new Set(['application/pdf', 'image/jpeg', 'image/png', 'image/tiff']);
const MAX_SIZE_BYTES = 50 * 1024 * 1024;
let {
filename,
isUploading,
isDragging = $bindable(false),
error,
onFile,
onCancel
}: {
filename: string;
isUploading: boolean;
isDragging?: boolean;
error: string | null;
onFile?: (file: File) => void;
onCancel?: () => void;
} = $props();
let validationError = $state<string | null>(null);
const displayError = $derived(error ?? validationError);
function handleFile(file: File | undefined) {
if (!file) return;
validationError = null;
if (!ALLOWED_TYPES.has(file.type)) {
validationError = 'Dieser Dateityp wird nicht unterstützt (PDF, JPG, PNG, TIFF).';
return;
}
if (file.size > MAX_SIZE_BYTES) {
validationError = 'Die Datei ist zu groß (max. 50 MB).';
return;
}
onFile?.(file);
}
function handleChange(e: Event) {
const input = e.currentTarget as HTMLInputElement;
handleFile(input.files?.[0]);
}
function handleDrop(e: DragEvent) {
e.preventDefault();
isDragging = false;
handleFile(e.dataTransfer?.files[0]);
}
</script>
<div
class="flex flex-1 items-center justify-center bg-pdf-bg"
aria-live="polite"
aria-label={isUploading ? 'Datei wird hochgeladen' : 'Dateiupload-Bereich'}
>
{#if isUploading}
<div role="status" class="flex flex-col items-center gap-3 text-center">
<div class="h-0.5 w-48 overflow-hidden rounded-full bg-white/10">
<div
class="h-full bg-brand-mint/70 motion-safe:animate-[slide_1.4s_ease-in-out_infinite]"
></div>
</div>
<p class="max-w-[200px] truncate text-xs font-medium text-brand-mint/70">{filename}</p>
<p class="text-xs text-white/40">Wird hochgeladen …</p>
<button
class="min-h-[44px] px-3 text-xs text-white/40 transition-colors hover:text-white/60"
onclick={onCancel}
>
Abbrechen
</button>
</div>
{:else}
<div
role="region"
aria-label="Datei ablegen"
class="flex flex-col items-center gap-3 rounded-sm border border-dashed p-8 text-center transition-colors
{isDragging ? 'border-brand-mint bg-brand-mint/5' : 'border-white/20'}"
ondragover={(e) => {
e.preventDefault();
isDragging = true;
}}
ondragleave={() => (isDragging = false)}
ondrop={handleDrop}
>
<div
class="upload-zone-icon flex h-8 w-8 items-center justify-center rounded-full bg-white/10 text-white/40"
>
<svg
width="16"
height="16"
viewBox="0 0 32 32"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<polygon
fill="currentColor"
points="6 12.5 16 2 26 12.5 24.5714286 14 16.999 6.049 17 30 15 30 14.999 6.051 7.42857143 14"
/>
</svg>
</div>
<p class="max-w-[200px] truncate text-xs font-medium text-white/50">{filename}</p>
<p class="text-xs text-white/30">Noch keine Datei hochgeladen</p>
{#if displayError}
<p class="text-xs text-red-400">{displayError}</p>
{/if}
<label
class="flex min-h-[44px] cursor-pointer items-center rounded-sm bg-brand-navy px-4 py-1.5 text-xs font-bold tracking-widest text-white/90 uppercase"
aria-label="Datei auswählen"
>
Datei auswählen
<input type="file" class="sr-only" onchange={handleChange} />
</label>
<p class="text-xs text-white/20">oder Datei hier ablegen</p>
</div>
{/if}
</div>