File Upload
File Upload объединяет нативный file-input, dropzone и удаляемый список выбранных файлов. Компонент не загружает данные в сеть и не показывает прогресс.
Статус: experimental · в системе с 2026-07 · интерактивность: always (всегда)
Когда использовать
- Нужно выбрать локальный файл и видеть его имя до отправки формы.
- Нужно собрать несколько локальных файлов и убрать отдельный ошибочный выбор.
- Drag-and-drop дополняет, но не заменяет нативный системный picker.
Не использовать:
- Нужен URL удалённого ресурса — это строковое поле.
- Нужны отправка, прогресс, повтор, преобразование или серверная проверка — это продуктовый поток поверх компонента.
- Файл должен отправляться сразу после выбора без отдельного решения пользователя.
Альтернативы:
Один файл
Нативный picker и dropzone возвращают один File.
Выберите файл или перетащите его сюда
<!--
🟢
Базовый пример File Upload принимает один PDF через системный picker или
dropzone. Выбранный File остаётся доступен потребителю через bind:files.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import FileUpload from "$lib/ui/FileUpload.svelte";
let files = $state([]);
</script>
<FileUpload
label={t("Бриф кампании")}
description={t("PDF, один файл.")}
accept=".pdf,application/pdf"
name="brief"
bind:files
/>
Несколько файлов
Выбранные изображения остаются видимыми и удаляемыми по одному.
Выберите файлы или перетащите их сюда
<!--
🟢
Множественный пример File Upload собирает изображения в один массив.
Каждый выбранный файл остаётся видимым и удаляется независимо от остальных.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import FileUpload from "$lib/ui/FileUpload.svelte";
let files = $state([]);
</script>
<FileUpload
label={t("Креативы")}
description={t("Можно выбрать несколько изображений.")}
accept="image/*"
name="creatives"
multiple
bind:files
/>
Поле
Подсказка, обязательность, описание и ошибка формы.
Выберите файл или перетащите его сюда
Выберите файл или перетащите его сюда
<!--
🟢
Пример поля File Upload показывает обязательность, подсказку и ошибку.
Ошибка занимает место описания и не превращает выбор файла в upload-flow.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import FileUpload from "$lib/ui/FileUpload.svelte";
let requiredFiles = $state([]);
let invalidFiles = $state([]);
</script>
<div style="display: grid; gap: var(--sp-3);">
<FileUpload
label={t("Медиаплан")}
hint={t("Файл будет отправлен вместе с формой")}
description={t("Таблица в формате XLSX.")}
accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
name="media-plan"
required
bind:files={requiredFiles}
/>
<FileUpload
label={t("Подтверждение")}
error={t("Выберите файл подтверждения.")}
name="confirmation"
required
bind:files={invalidFiles}
/>
</div>
Превью картинки
preview с previewUrl: картинка заполняет зону, по наведению затемнение с корзиной — как imageAttach легаси.
Выберите файл или перетащите его сюда
<!--
🟢
Превью картинки (эталон: Vue-легаси blocks/imageAttach): уже загруженный
креатив заполняет зону, по наведению — затемнение с корзиной; выбор нового
файла заменяет превью.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import FileUpload from "$lib/ui/FileUpload.svelte";
const creative =
"data:image/svg+xml;utf8," +
encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" width="300" height="250"><rect width="300" height="250" fill="#ededf1"/><circle cx="150" cy="110" r="60" fill="#ed7f6e"/><text x="150" y="215" text-anchor="middle" font-family="sans-serif" font-size="20" fill="#4c5866">300 × 250</text></svg>',
);
let files = $state([]);
let previewUrl = $state(creative);
</script>
<div style="width: 300px;">
<FileUpload label={t("Креатив")} accept="image/*" preview bind:previewUrl bind:files description={t("PNG или JPG до 2 МБ, 300 × 250")} />
</div>
Кадрирование и размер
crop с width × height: картинка кадрируется в диалоге; подпись размера и пропорций под зоной.
Выберите файл или перетащите его сюда
Размер: 300 × 250·Пропорции 6:5
<!--
🟢
Кадрирование и размер (эталон: Vue-легаси blocks/imageAttach crop +
sizeAndAspect): выбранная картинка открывается в диалоге с холстом и
слайдером масштаба, результат — файл 300 × 250; под зоной подпись размера.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import FileUpload from "$lib/ui/FileUpload.svelte";
</script>
<div style="width: 320px;">
<FileUpload label={t("Баннер")} accept="image/*" crop width={300} height={250} aspect="6:5" preview />
</div>