GrFormFile
Берут, когда файл — значение поля.
Обзор
Когда брать
- файл — значение поля — резюме, скан, вложение: отправляет форма, а не компонент;
- файл проверяется до отправки — размер, тип, количество через
validators; - файлов несколько —
multipleиlimitвместе со списком выбранного; - нужен предпросмотр —
previewпоказывает миниатюру изображения до отправки.
Когда взять другое
| Нужно | Берите |
|---|---|
| Файл уходит на сервер сразу, с прогрессом | GrFileUpload |
| Изображение нужно рассмотреть | GrImageViewer |
| Значение — не файл | GrInput |
Граница с GrFileUpload проходит по тому, кто отправляет. Здесь файл —
обычное значение v-model, и оно уезжает вместе с остальной формой; там
компонент грузит сам и показывает прогресс каждого файла.
Playground контролы выводятся из API, а не пишутся руками
Загружается…
0 изменено· состояние — в адресе
<GrFormFile uploadText="Attach contract" placeholder="Signed contract, PDF up to 10 MB" />Примеры 3 у каждого — живое демо и его код
Резюме, которое уходит вместе с откликом
Поле только собирает файл: это значение модели, как и имя, и ничего не загружается, пока не отправлен весь отклик. Правило
fileу GrForm проверяет тип и размер на отправке рядом с остальными правилами, аacceptна поле фильтрует системный диалог.Senior product designer · Dublin or remoteNorthwind Traders · Apply in two minutes Фото объявления с обложкой
Первое фото становится обложкой, поэтому порядок принадлежит пользователю:
reorderableдаёт строке ручку, и перенос меняет модель. Новый выбор добавляется к набору — кнопка говорит об этом черезadd-text, — аlimitпроверяет весь набор: седьмое фото отбивается, а не обрезается молча.previewрисует квадратные миниатюры, чтобы строки не прыгали по пропорциям снимков.2-room flat in Alfama · draftCover photo: none yet Drag to reorder — the first one is the cover
Файлы не выбраныЧек, который уже заявлен
validateвыполняется после встроенных проверок и может спросить сервер — здесь, не заявлен ли чек раньше.v-model:errors— единый канал для найденного проверками и сказанного сервером, а слотerrorформулирует каждую претензию по её коду, со ссылкой на прежнюю заявку.Taxi, 16 Oct · €24.60Client visit, Lisbon Photo or PDF of the receipt
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
disabled | boolean | undefined | false | Недоступен: выбор, перетаскивание и удаление выключены. Складывается по «или» с `GrFormField` и формой. |
multiple | boolean | undefined | false | Набор из нескольких файлов: модель — массив, у каждой строки своё удаление. |
readonly | boolean | undefined | false | Только для чтения: значение видно и уходит в форму, но не редактируется. |
invalid | boolean | undefined | false | Визуальное и ARIA-состояние ошибки. |
required | boolean | undefined | false | Обязательное поле (`aria-required`). |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | Размер кнопок, иконок и подписей. |
placeholder | string | undefined | undefined | Текст у кнопки, пока ничего не выбрано. По умолчанию — переведённое «No files selected». |
ariaLabel | string | undefined | undefined | Доступное имя вне `GrFormField`. |
replace | boolean | undefined | false | Новый выбор в `multiple` **заменяет** набор, а не добавляется к нему — старое поведение. Без него набор растёт, дубликаты (то же имя, размер и время изменения) отбрасываются, а `limit` и валидаторы проверяют набор целиком. |
limit | number | undefined | undefined | Максимум файлов в наборе. Лишние не обрезаются молча — набор отбивается ошибкой. |
validators | FileValidator[] | undefined | undefined | Проверки набора (`maxFileSize`, `allowedExtensionsValidator` и т. п.) — после `accept` и `limit`, до `validate`. Отбитый набор в модель не попадает. |
accept | string | undefined | undefined | W3C `accept` для `<input type="file">` + sugar к `acceptValidator(...)`. |
preview | boolean | undefined | false | Миниатюры для картинок в наборе. Файлы других типов остаются строкой. |
validate |
| undefined | Дополнительная (кастомная) валидация на стороне потребителя. |
uploadText | string | undefined | undefined | Подпись кнопки выбора, пока набор пуст. По умолчанию — переведённое «Upload file». |
changeText | string | undefined | undefined | Подпись кнопки выбора, когда файлы уже есть. По умолчанию — переведённое «Change file». |
addText | string | undefined | undefined | Подпись кнопки выбора в `multiple`, когда файлы уже есть: новый выбор добавляется к набору. По умолчанию — переведённое «Add files». |
removeText | string | undefined | undefined | Подпись удаления у одиночного файла и у строк набора. По умолчанию — переведённое «Remove». |
clearAllText | string | undefined | undefined | Подпись сброса всего набора при `multiple`. По умолчанию — переведённое «Clear all». |
reorderable | boolean | undefined | false | Порядок набора задаёт пользователь: у строки появляется ручка переноса. Нужно там, где последовательность файлов что-то значит, — галерея, страницы документа, очередь вложений. Осмысленно только с `multiple`. |
errorsv-model | FileValidationIssue[] | undefined | undefined | Контролируемый список ошибок: `v-model:errors`. Задан — показывается он, и внутренняя валидация его не перетирает. Сюда же кладутся ошибки, пришедшие с сервера. Не задан — компонент держит свои ошибки сам. |
modelValueобязательныйv-model | File | File[] | null | — | Выбранный файл или `null`; при `multiple` — массив файлов (`v-model`). |
Слоты
| Slot | Type | Описание |
|---|---|---|
error | { errors: FileValidationIssue[]; } | Собственный вывод ошибок вместо списка по умолчанию. |
События
| Event | Сигнатура | Описание |
|---|---|---|
update:modelValue | (event: "update:modelValue", value: File | File[] | null): void | — |
change | (event: "change", value: File | File[] | null): void | — |
clear | (event: "clear"): void | — |
focus | (event: "focus", event: FocusEvent): void | — |
blur | (event: "blur", event: FocusEvent): void | — |
update:errors | (event: "update:errors", errors: FileValidationIssue[]): void | — |
Методы
| Methods | Сигнатура | Описание |
|---|---|---|
focus | () => void | Поставить фокус на кнопку выбора файла. |
blur | () => void | Снять фокус с кнопки выбора файла. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrFormFileError, GrFormFileProps, GrFormFileEmits } from '@feugene/granularity/components/GrFormFile'Файлdist/types/components/GrFormFile/config.d.ts
const grFormFileConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrFormFile">;Файлdist/types/components/GrFormFile/GrFormFile.vue.d.ts
type GrFormFileError = FileValidationIssue;Пропы GrFormFile — форма-поле для выбора файлов с встроенной валидацией,
drag&drop (через v-dropzone) и списком выбранных файлов.
Все *Text/placeholder — i18n-friendly, принимают готовые строки локали.
interface GrFormFileProps {
/** Выбранный файл или `null`; при `multiple` — массив файлов (`v-model`). */
modelValue: File | File[] | null;
/** Набор из нескольких файлов: модель — массив, у каждой строки своё удаление. */
multiple?: boolean;
/**
* Недоступен: выбор, перетаскивание и удаление выключены. Складывается по «или» с
* `GrFormField` и формой.
*/
disabled?: boolean;
/** Только для чтения: значение видно и уходит в форму, но не редактируется. */
readonly?: boolean;
/** Визуальное и ARIA-состояние ошибки. */
invalid?: boolean;
/** Обязательное поле (`aria-required`). */
required?: boolean;
/** Доступное имя вне `GrFormField`. */
ariaLabel?: string;
/** W3C `accept` для `<input type="file">` + sugar к `acceptValidator(...)`. */
accept?: string;
/**
* Проверки набора (`maxFileSize`, `allowedExtensionsValidator` и т. п.) — после `accept` и
* `limit`, до `validate`. Отбитый набор в модель не попадает.
*/
validators?: FileValidator[];
/** Максимум файлов в наборе. Лишние не обрезаются молча — набор отбивается ошибкой. */
limit?: number;
/** Подпись кнопки выбора, пока набор пуст. По умолчанию — переведённое «Upload file». */
uploadText?: string;
/** Подпись кнопки выбора, когда файлы уже есть. По умолчанию — переведённое «Change file». */
changeText?: string;
/**
* Подпись кнопки выбора в `multiple`, когда файлы уже есть: новый выбор
* добавляется к набору. По умолчанию — переведённое «Add files».
*/
addText?: string;
/**
* Новый выбор в `multiple` **заменяет** набор, а не добавляется к нему —
* старое поведение. Без него набор растёт, дубликаты (то же имя, размер и
* время изменения) отбрасываются, а `limit` и валидаторы проверяют набор
* целиком.
*/
replace?: boolean;
/** Подпись удаления у одиночного файла и у строк набора. По умолчанию — переведённое «Remove». */
removeText?: string;
/** Подпись сброса всего набора при `multiple`. По умолчанию — переведённое «Clear all». */
clearAllText?: string;
/** Текст у кнопки, пока ничего не выбрано. По умолчанию — переведённое «No files selected». */
placeholder?: string;
/** Размер кнопок, иконок и подписей. */
size?: GrFormFileSize;
/** Миниатюры для картинок в наборе. Файлы других типов остаются строкой. */
preview?: boolean;
/**
* Порядок набора задаёт пользователь: у строки появляется ручка переноса.
*
* Нужно там, где последовательность файлов что-то значит, — галерея,
* страницы документа, очередь вложений. Осмысленно только с `multiple`.
*/
reorderable?: boolean;
/** Дополнительная (кастомная) валидация на стороне потребителя. */
validate?: (files: File[]) => GrFormFileError[] | Promise<GrFormFileError[]>;interface GrFormFileEmits {
/**
* Новый набор для `v-model`: выбор, перетаскивание, удаление, перестановка. Набор, отбитый
* валидацией, сюда не приходит.
*/
(e: 'update:modelValue', value: File | File[] | null): void;
/** Набор изменился — тот же payload, что у `update:modelValue`. */
(e: 'change', value: File | File[] | null): void;
/** Набор сброшен целиком: «Очистить всё» или удаление единственного файла; модель — следом. */
(e: 'clear'): void;
/** Результат валидации. Канал один: `validation` дублировал эту же нагрузку. */
(e: 'update:errors', errors: GrFormFileError[]): void;
/** Фокус вошёл в поле. Переходы между его кнопками событием не считаются. */
(e: 'focus', event: FocusEvent): void;
/** Фокус ушёл из поля целиком. */
(e: 'blur', event: FocusEvent): void;
}Файлdist/types/components/GrFormFile/defaults.d.ts
Пропы GrFormFile, настраиваемые глобально через componentDefaults.
interface GrFormFileConfigurableProps {
size: GrComponentSize;
}Файлdist/types/components/GrFormFile/grFormFileStyles.d.ts
type GrFormFileSize = GrComponentSize;Файлdist/types/components/GrFormFile/safelist.d.ts
const grFormFileSafelist: string[];Файлdist/types/components/GrFormFile/index.d.ts
type GrFormFileInstance = ComponentExposed<typeof GrFormFileComponent>;Использование из прозы библиотеки
Ошибки — контролируемое значение
v-model:errors — двусторонний канал: туда пишет внутренняя валидация, и туда
же потребитель кладёт ошибки, пришедшие с сервера. Пока проп задан, он сильнее
внутреннего списка (та же схема, что sortKey у GrDataTable).
<GrFormFile v-model="files" v-model:errors="errors" accept="application/pdf" multiple :limit="3" />Канал один: эмит validation дублировал update:errors той же нагрузкой и снят.
Список ошибок объявляется role="alert" и связан с кнопкой выбора через
aria-describedby — вместе с aria-describedby от GrFormField, если поле
внутри него. Пока ошибки есть, кнопка несёт aria-invalid. До этого «уронил
файл не того типа» выглядело для скринридера как «ничего не произошло».
Валидация одна на оба пути ввода
Набор валидаторов собирается в одном месте и уходит и в выбор через диалог, и в
v-dropzone: две копии этой сборки разъезжаются при первой же правке, и
перетаскивание начинает вести себя не так, как диалог.
Порядок: accept → limit → validators потребителя → validate.
limit — сахар к maxCountValidator: лишние файлы не обрезаются молча, набор
отбивается ошибкой, как любым другим правилом.
Выбор в multiple добавляется к набору
Новый выбор — диалогом или перетаскиванием — дописывается к уже выбранным:
галерея и список вложений растут, а не теряют выбранное при каждом диалоге.
Повтор уже выбранного файла (то же имя, размер и время изменения)
отбрасывается. Кнопка у непустого набора — «Add files» (gr.formFile.add,
свой текст — addText), начать заново — «Clear all».
Валидаторы видят набор целиком: limit, maxTotalSizeBytesValidator и
validate проверяют то, что получится после добавления, а не последний выбор.
Отбитый выбор набор не трогает — остаётся прежний и ошибка.
Заменять набор при каждом выборе — старое поведение — можно явно: replace.
Кнопка тогда снова «Change file».
Список файлов
В multiple каждая строка показывает имя и размер, а кнопка удаления называет
свой файл (aria-label) — три подряд кнопки «Удалить» для скринридера
неразличимы. Строка — во всю ширину колонки: длинное имя усекается
многоточием (полное — в title), размер и «Удалить» стоят у общего правого
края, и в узком контейнере кнопку не выталкивает.
Disabled гасится курсором и состоянием самих кнопок: opacity на контейнере
разбавляла бы и подписи, и имена файлов.
Порядок задаёт пользователь
reorderable добавляет строке ручку переноса. Нужно там, где последовательность
файлов что-то значит: галерея, страницы документа, очередь вложений. Осмысленно
только с multiple.
Механика берётся у GrSortableList, а не пишется заново:
перенос указателем, клавиатура (Space берёт строку, стрелки двигают, Esc
отменяет), автопрокрутка и объявления у него уже есть. Цена честная — его стили
приезжают каждому, кто берёт GrFormFile.
Тянется ручка, а не строка: в строке стоит кнопка удаления, и перетаскивание всей строки отняло бы у неё клик.
Перестановка отдаёт update:modelValue и change — как удаление и добавление.
Без второго порядок остался бы только на экране, а в форму ушёл бы прежний.
При readonly и disabled перенос запрещён.
Превью картинок
preview включает миниатюры: они появляются у файлов image/*, файл любого
другого типа остаётся обычной строкой. Миниатюра квадратная и обрезается по
object-cover — иначе строки списка скакали бы по высоте вслед за пропорциями
снимков.
<GrFormFile v-model="gallery" multiple preview accept="image/*" />alt у миниатюры пустой: имя файла стоит вплотную, и озвучивать его дважды
незачем. object URL живёт ровно столько, сколько файл в наборе, — он
отзывается, как только файл из набора ушёл, чем бы его ни убрало.
readonly
Набор виден и уходит в форму, но не меняется ничем: ни диалогом выбора, ни
перетаскиванием, ни кнопками — они в этом состоянии не рендерятся. Кнопка
выбора остаётся в таб-порядке и объявляет aria-readonly: поле должно быть
достижимо с клавиатуры и уметь объяснить, почему не поддаётся.
Отличие от disabled: тот выключает и саму кнопку, то есть поле выпадает из
обхода целиком.
Внутри GrForm
Те же ограничения можно объявить правилом формы — рядом с остальными:
const rules: GrFormRules = {
contract: [{ required: true, file: { accept: '.pdf', maxSizeMb: 1 } }],
}Проверку ведут те же валидаторы, поэтому текст ошибки не меняется — меняется
момент: правило поля не пускает плохой файл в модель сразу, правило формы
отбивает submit и попадает в invalid и в скролл к первой ошибке. Подробности
и полный список ключей — GrForm.md.
Типичное разделение: ограничения в rules, а на поле accept как фильтр
диалога выбора.
Чего нет
Загрузки на сервер: GrFormFile — поле формы, файлы уходят вместе с ней.
Прогресс и отмену закрывает GrFileUpload.
Пропы этого примера