feat: Implement a global upload dialog and refactor the upload UI/UX, including manifest size tracking.

This commit is contained in:
2026-02-27 03:49:54 +07:00
parent ff1d4902bc
commit a5b4028bc8
19 changed files with 538 additions and 432 deletions

View File

@@ -1,55 +1,59 @@
<script setup lang="ts">
const emit = defineEmits<{
filesSelected: [files: FileList];
}>();
const props = defineProps<{ maxFiles?: number }>();
const emit = defineEmits<{ filesSelected: [files: FileList] }>();
const handleFileChange = (event: Event) => {
const input = event.target as HTMLInputElement;
if (input.files && input.files.length > 0) {
if (!input.files || input.files.length === 0) return;
const limit = props.maxFiles ?? 5;
if (input.files.length > limit) {
// Create a DataTransfer to slice to the limit
const dt = new DataTransfer();
Array.from(input.files).slice(0, limit).forEach(f => dt.items.add(f));
emit('filesSelected', dt.files);
} else {
emit('filesSelected', input.files);
}
};
</script>
<template>
<div class="relative group cursor-pointer">
<div class="relative group cursor-pointer flex-1 flex flex-col h-full">
<input type="file" multiple accept="video/*"
class="absolute inset-0 w-full h-full opacity-0 z-20 cursor-pointer" @change="handleFileChange">
<div
class="bg-surface rounded-2xl p-16 text-center border border-dashed border-border group-hover:border-success/50 group-hover:shadow-soft transition-all duration-300 relative overflow-hidden">
<div class="flex-1 flex flex-col items-center justify-center gap-4 rounded-xl border-2 border-dashed
border-slate-200 group-hover:border-accent/60 group-hover:bg-accent/[0.03]
transition-all duration-300 py-6 px-4 h-full">
<div
class="absolute top-0 left-0 w-64 h-64 bg-primary/10 rounded-full blur-3xl -translate-x-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-700">
</div>
<div
class="absolute bottom-0 right-0 w-64 h-64 bg-primary/10 rounded-full blur-3xl translate-x-1/2 translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-700">
</div>
<div class="relative z-10 flex flex-col items-center">
<div
class="w-24 h-24 mb-8 rounded-3xl bg-page shadow-soft flex items-center justify-center text-accent transition-all duration-300 ring-4 ring-gray-100 group-hover:(ring-primary/10 scale-110 shadow-md)">
<!-- Animated icon -->
<div class="relative">
<div class="w-20 h-20 rounded-2xl bg-slate-100 group-hover:bg-accent/10 flex items-center justify-center transition-all duration-300 group-hover:scale-105 group-hover:shadow-md">
<svg xmlns="http://www.w3.org/2000/svg"
class="w-10 h-10 stroke-primary/60 group-hover:stroke-primary transition-all duration-300"
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
class="w-10 h-10 text-slate-400 group-hover:text-accent transition-colors duration-300"
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="17 8 12 3 7 8" />
<line x1="12" x2="12" y1="3" y2="15" />
</svg>
</div>
<div class="absolute inset-0 rounded-2xl ring-4 ring-accent/0 group-hover:ring-accent/20 transition-all duration-300"></div>
</div>
<h3 class="text-2xl font-semibold text-slate-900 mb-3">Drag and drop your videos here</h3>
<p class="text-slate-500 text-base mb-8 max-w-md mx-auto leading-relaxed">
Supports uploading multiple files at once. Formats MP4, MOV, MKV. Up to 10GB per file.
<div class="text-center">
<p class="text-base font-semibold text-slate-700 group-hover:text-slate-900 transition-colors">
Drop videos here
</p>
<span class="px-8 py-3.5 btn-lg btn-primary flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path
d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
</svg>
Choose Files
<p class="text-sm text-slate-400 mt-1.5">or click anywhere to browse</p>
</div>
<!-- Format badges -->
<div class="flex items-center gap-2">
<span v-for="fmt in ['MP4', 'MOV', 'MKV']" :key="fmt"
class="text-xs font-semibold px-3 py-1 bg-slate-100 text-slate-500 rounded-lg tracking-wide">
{{ fmt }}
</span>
</div>
</div>