GrInput
Берут, когда вводится строка.
Обзор
Когда брать
- вводится строка — имя, адрес, поисковый запрос, пароль: базовый контрол формы;
- у поля есть аддоны — единица измерения, префикс протокола, иконка поиска через слоты;
- длина ограничена —
maxlengthсо счётчиком символов вместо молчаливого обрезания; - в поле идёт фоновая работа — индикатор занятости, не блокируя ввод.
Когда взять другое
| Нужно | Берите |
|---|---|
| Текст в несколько строк | GrTextarea |
| Вводится число со ступенями | GrNumberInput |
| Значений несколько, они чипами | GrInputTag |
| Выбор из готовых вариантов | GrSelect |
| Поиск с подсказками | GrAutocomplete |
| Дата или время | GrDatePicker |
Варианты все тона и виды — настоящим компонентом
state \ shape | box | pill |
|---|---|---|
default | ||
success | ||
warning | ||
danger |
Размеры
xssmmdlg
Состояния
обычнаяdisabledloadingreadonlyinvalid
Матрица живая: наводите и нажимайте. Состояний наведения, фокуса и нажатия здесь нет — они за браузером, а не за пропом.
Playground контролы выводятся из API, а не пишутся руками
Загружается…
0 изменено· состояние — в адресе
<GrInput
v-model="value"
placeholder="[email protected]"
type="email"
/>Примеры 3 у каждого — живое демо и его код
Поиск в тулбаре
shape="pill"отличает поиск в тулбаре от полей формы,clearableвозвращает к полному списку одним кликом, а значок стоит в аддонеinline, а не в отдельном отсеке. Список фильтруется по ходу ввода и держит высоту, поэтому страница не прыгает.Members · 5Редактор товара с аддонами
Аддоны держат то, что не является значением, — адрес магазина, валюту, единицу, — а
describe-addonsдаёт скринридеру услышать их там, где они несут смысл.suffix-fixedс общимsuffix-min-widthделает EUR и kg одной ширины, и числа встают в колонку, аtext-align="right"выравнивает их цифры.Linen shirt, sand shop.example.com/p/EUREURkgИмя пользователя, проверенное по ходу ввода
loadingпоказывает проверку занятости внутри поля и ставитaria-busy;stateкрасит ответ и добавляет его значок.maxlengthсshow-countдержит лимит на виду, аpassword-toggleдобавляет «глаз» с нормальным доступным именем.Create your account Letters, digits and hyphens.
4 / 2012 characters or more
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
disabled | boolean | undefined | false | Выключить поле: очистка и показ пароля пропадают. Складывается с `GrFormField`. |
type | "number" | "search" | "text" | "email" | "password" | "tel" | "url" | undefined | "text" | Нативный `type`: меняет клавиатуру и браузерную проверку формата. По умолчанию `text`. |
modelValuev-model | string | undefined | "" | Значение поля. Необязательное: без `v-model` поле рисуется пустым. Дефолт — пустая строка, а не `undefined`: длина значения читается напрямую (`showClear`), и `undefined` уронил бы рендер. |
readonly | boolean | undefined | false | Только для чтения: значение видно и выделяемо, но не редактируется. |
invalid | boolean | undefined | false | Ошибка валидации: danger-рамка и `aria-invalid`. Складывается с ошибкой `GrFormField`. |
required | boolean | undefined | false | Обязательное поле (`aria-required`). Складывается с `required` у `GrFormField`. |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | Высота и кегль по шкале контролов. По умолчанию `md`; берётся из `GrConfigProvider`. |
placeholder | string | undefined | undefined | Подсказка в пустом поле. Подпись не заменяет: имя даёт `GrFormField` или `ariaLabel`. |
ariaLabel | string | undefined | undefined | Доступное имя вне `GrFormField`. |
clearable | boolean | undefined | undefined | Показывать кнопку очистки, когда есть значение (и не disabled/readonly). |
loading | boolean | undefined | false | Фоновая работа по полю (проверка занятости логина, автосохранение): спиннер в trailing-области + `aria-busy`. Ввод не блокируется — для этого есть `disabled`/`readonly`. |
clearLabel | string | undefined | undefined | i18n aria-label кнопки очистки. |
name | string | undefined | undefined | Нативный `name` — имя поля при отправке формы. |
prefixMinWidth | string | undefined | undefined | Минимальная ширина префикса. По умолчанию — по ступени размера, у `inline`-аддона — `0`. |
prefixMaxWidth | string | undefined | undefined | Максимальная ширина префикса: содержимое шире обрезается. |
suffixMinWidth | string | undefined | undefined | Минимальная ширина суффикса. По умолчанию — по ступени размера, у `inline`-аддона — `0`. |
suffixMaxWidth | string | undefined | undefined | Максимальная ширина суффикса: содержимое шире обрезается. |
prefixFixed | boolean | undefined | false | Фиксированная ширина у prefix/suffix: аддон получает жёсткую ширину (из `*MaxWidth` → `*MinWidth` → дефолт), а контент обрезается по краю (prefix — справа, suffix — слева). По умолчанию аддоны «растягиваются» под контент (в пределах min/max), а излишек клипается оболочкой. |
suffixFixed | boolean | undefined | false | Жёсткая ширина суффикса — как у `prefixFixed`, только лишнее обрезается слева. |
shape | "box" | "pill" | undefined | undefined | Форма рамки. `box` — скругление шкалы контролов, как было всегда; `pill` — дорожка-пилюля, как у `GrSegmented`. Форма тянет за собой горизонтальный отступ: в пилюле он не меньше половины высоты, иначе текст заезжает в дугу. |
id | string | undefined | undefined | `id` поля. Не задан — берётся из `GrFormField`, чтобы подпись нашла поле. |
state | "default" | "success" | "warning" | "danger" | undefined | "default" | Подсветка рамки по решению разработчика. `success` и `warning` добавляют иконку и скрытую подпись; `invalid` сильнее. По умолчанию `default`. |
autocomplete | string | undefined | undefined | Нативный `autocomplete`: чем браузеру заполнять поле — `email`, `one-time-code` и т. п. |
inputmode |
| undefined | Нативный `inputmode`: какую экранную клавиатуру показать, не меняя `type`. |
maxlength | number | undefined | undefined | Ограничение длины + основа для счётчика символов. |
showCount | boolean | undefined | false | Показывать счётчик символов (`len` или `len/maxlength`). |
describeAddons | boolean | undefined | false | Аддоны `prefix`/`suffix` несут смысл, а не украшают. По умолчанию они скрыты от скринридера: чаще всего там иконка поиска или глиф, повторяющий подпись поля, и второе прочтение мешает. Но `https://` или `₽` бывают единственным местом, где сказано, что вводить, — тогда аддон уезжает в `aria-describedby` поля. Декоративную иконку внутри слота помечайте `aria-hidden` сами: проп снимает скрытие со всей обёртки, а не разбирает её содержимое. |
passwordToggle | boolean | undefined | false | Кнопка показать/скрыть пароль (только при `type="password"`). |
passwordShowLabel | string | undefined | undefined | i18n aria-label кнопки показать/скрыть пароль. |
passwordHideLabel | string | undefined | undefined | i18n aria-label кнопки, которая прячет показанный пароль. Не задан — из локали. |
textAlign | GrInputTextAlign | undefined | "left" | Выравнивание текста в поле. По умолчанию `left`. |
addon | "inline" | "segment" | undefined | "segment" | Как выглядят аддоны `#prefix`/`#suffix`. `segment` (по умолчанию) — отдельный отсек, отрезанный рамкой и выровненный по ступени размера: так поле с «₽» и поле с «USD» стоят в колонку. `inline` — украшение внутри рамки: ни разделителя, ни своей ширины. Разница не косметическая. Поисковая строка с лупой в сегменте читается составным элементом — полем с приклеенной кнопкой, — а не одним полем; именно поэтому иконку внутри рамки нельзя было выразить аддоном, и потребители отказывались от неё вовсе. |
Слоты
| Slot | Type | Описание |
|---|---|---|
prefix | any | Аддон слева от поля: иконка, код валюты, метка. |
suffix | any | Аддон справа от поля: единица измерения, подсказка. |
События
| Event | Сигнатура | Описание |
|---|---|---|
update:modelValue | (event: "update:modelValue", value: string): void | — |
change | (event: "change", value: string): void | — |
clear | (event: "clear"): void | — |
focus | (event: "focus", event: FocusEvent): void | — |
blur | (event: "blur", event: FocusEvent): void | — |
Методы
| Methods | Сигнатура | Описание |
|---|---|---|
focus | () => void | Поставить фокус в поле. |
blur | () => void | Снять фокус с поля. |
select | () => void | Выделить содержимое поля — чтобы подставленное значение перезаписали вводом. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrInputConfigurableProps, GrInputProps, GrInputEmits } from '@feugene/granularity/components/GrInput'Файлdist/types/components/GrInput/config.d.ts
const grInputConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrInput">;Файлdist/types/components/GrInput/defaults.d.ts
Пропы GrInput, настраиваемые глобально через componentDefaults.
Только оформление и поведение по умолчанию — см. GrButton/defaults.ts.
size типизируем через GrComponentSize (та же шкала xs…lg), а не через
GrInputSize: последний объявлен в GrInput.vue, который импортирует
контекст, — получился бы цикл модулей.
interface GrInputConfigurableProps {
size: GrComponentSize;
clearable: boolean;
/** Форма рамки — см. `components/shared/controlShape.ts`. */
shape: GrControlShape;
}Файлdist/types/components/GrInput/GrInput.vue.d.ts
interface GrInputProps {
/**
* Значение поля. Необязательное: без `v-model` поле рисуется пустым.
* Дефолт — пустая строка, а не `undefined`: длина значения читается
* напрямую (`showClear`), и `undefined` уронил бы рендер.
*/
modelValue?: string;
/** Нативный `type`: меняет клавиатуру и браузерную проверку формата. По умолчанию `text`. */
type?: 'text' | 'email' | 'password' | 'number' | 'search' | 'tel' | 'url';
/** Подсказка в пустом поле. Подпись не заменяет: имя даёт `GrFormField` или `ariaLabel`. */
placeholder?: string;
/** Нативный `autocomplete`: чем браузеру заполнять поле — `email`, `one-time-code` и т. п. */
autocomplete?: string;
/** Нативный `inputmode`: какую экранную клавиатуру показать, не меняя `type`. */
inputmode?: InputHTMLAttributes['inputmode'];
/** Выключить поле: очистка и показ пароля пропадают. Складывается с `GrFormField`. */
disabled?: boolean;
/** Только для чтения: значение видно и выделяемо, но не редактируется. */
readonly?: boolean;
/** Ошибка валидации: danger-рамка и `aria-invalid`. Складывается с ошибкой `GrFormField`. */
invalid?: boolean;
/** Обязательное поле (`aria-required`). Складывается с `required` у `GrFormField`. */
required?: boolean;
/** Доступное имя вне `GrFormField`. */
ariaLabel?: string;
/**
* Подсветка рамки по решению разработчика. `success` и `warning` добавляют иконку
* и скрытую подпись; `invalid` сильнее. По умолчанию `default`.
*/
state?: 'default' | 'success' | 'warning' | 'danger';
/** Нативный `name` — имя поля при отправке формы. */
name?: string;
/** `id` поля. Не задан — берётся из `GrFormField`, чтобы подпись нашла поле. */
id?: string;
/** Высота и кегль по шкале контролов. По умолчанию `md`; берётся из `GrConfigProvider`. */
size?: GrInputSize;
/**
* Форма рамки. `box` — скругление шкалы контролов, как было всегда; `pill` —
* дорожка-пилюля, как у `GrSegmented`. Форма тянет за собой горизонтальный
* отступ: в пилюле он не меньше половины высоты, иначе текст заезжает в дугу.
*/
shape?: GrControlShape;
/** Показывать кнопку очистки, когда есть значение (и не disabled/readonly). */
clearable?: boolean;
/** i18n aria-label кнопки очистки. */
clearLabel?: string;
/** Ограничение длины + основа для счётчика символов. */
maxlength?: number;
/** Показывать счётчик символов (`len` или `len/maxlength`). */
showCount?: boolean;
/**
* Аддоны `prefix`/`suffix` несут смысл, а не украшают.
*
* По умолчанию они скрыты от скринридера: чаще всего там иконка поиска или
* глиф, повторяющий подпись поля, и второе прочтение мешает. Но `https://`
* или `₽` бывают единственным местом, где сказано, что вводить, — тогда
* аддон уезжает в `aria-describedby` поля.
*
* Декоративную иконку внутри слота помечайте `aria-hidden` сами: проп снимает
* скрытие со всей обёртки, а не разбирает её содержимое.
*/
describeAddons?: boolean;
/**
* Фоновая работа по полю (проверка занятости логина, автосохранение):
* спиннер в trailing-области + `aria-busy`. Ввод не блокируется —
* для этого есть `disabled`/`readonly`.
*/
loading?: boolean;
/** Кнопка показать/скрыть пароль (только при `type="password"`). */
passwordToggle?: boolean;
/** i18n aria-label кнопки показать/скрыть пароль. */
passwordShowLabel?: string;
/** i18n aria-label кнопки, которая прячет показанный пароль. Не задан — из локали. */
passwordHideLabel?: string;
/** Выравнивание текста в поле. По умолчанию `left`. */
textAlign?: GrInputTextAlign;
/** Минимальная ширина префикса. По умолчанию — по ступени размера, у `inline`-аддона — `0`. */
prefixMinWidth?: string;
/** Максимальная ширина префикса: содержимое шире обрезается. */
prefixMaxWidth?: string;
/** Минимальная ширина суффикса. По умолчанию — по ступени размера, у `inline`-аддона — `0`. */
suffixMinWidth?: string;
/** Максимальная ширина суффикса: содержимое шире обрезается. */
suffixMaxWidth?: string;
/**
* Фиксированная ширина у prefix/suffix: аддон получает жёсткую ширину
* (из `*MaxWidth` → `*MinWidth` → дефолт), а контент обрезается по краю
* (prefix — справа, suffix — слева). По умолчанию аддоны «растягиваются»
* под контент (в пределах min/max), а излишек клипается оболочкой.
*/
prefixFixed?: boolean;
/** Жёсткая ширина суффикса — как у `prefixFixed`, только лишнее обрезается слева. */
suffixFixed?: boolean;
/**
* Как выглядят аддоны `#prefix`/`#suffix`.
*
* `segment` (по умолчанию) — отдельный отсек, отрезанный рамкой и выровненный
* по ступени размера: так поле с «₽» и поле с «USD» стоят в колонку.
* `inline` — украшение внутри рамки: ни разделителя, ни своей ширины.
*
* Разница не косметическая. Поисковая строка с лупой в сегменте читается
* составным элементом — полем с приклеенной кнопкой, — а не одним полем;
* именно поэтому иконку внутри рамки нельзя было выразить аддоном, и
* потребители отказывались от неё вовсе.
*/
addon?: 'segment' | 'inline';
}interface GrInputEmits {
/** Новое значение на каждый ввод (`v-model`). Кнопка очистки шлёт пустую строку. */
(e: 'update:modelValue', value: string): void;
/** Значение зафиксировано нативным `change` — по `blur` или `Enter`. */
(e: 'change', value: string): void;
/** Поле получило фокус — нативный `focus` с `<input>`. */
(e: 'focus', event: FocusEvent): void;
/** Поле потеряло фокус — нативный `blur` с `<input>`. */
(e: 'blur', event: FocusEvent): void;
/**
* Значение стёрто кнопкой очистки. Отдельное событие потому, что по
* `update:modelValue` программную очистку от ручного стирания не отличить.
*/
(e: 'clear'): void;
}type GrInputSize = GrComponentSize;type GrInputTextAlign = 'left' | 'center' | 'right';Файлdist/types/components/GrInput/safelist.d.ts
const grInputSafelist: string[];Файлdist/types/components/GrInput/index.d.ts
type GrInputInstance = ComponentExposed<typeof GrInputComponent>;Доступность
Паттерн APG
—
| Клавиша | Действие |
|---|---|
| — | Tab из поля — на кнопку очистки, затем на переключатель пароля (обе активируются Enter/Space и возвращают фокус в поле) |
Полный контракт пакета: docs/keyboard.md ↗
Использование из прозы библиотеки
Trailing-контролы достижимы с клавиатуры
Кнопки очистки и показа пароля стоят в обычном таб-порядке: Tab из поля ведёт
на крестик, затем на глаз. Раньше на них висел tabindex="-1" — виджет был
объявлен доступным (aria-label, aria-pressed), но воспользоваться им без
мыши было нельзя.
Обе кнопки после нажатия возвращают фокус в поле, поэтому очистка не роняет фокус в никуда: кнопка исчезает вместе с опустевшим значением.
Аддоны: сегмент или украшение
#prefix и #suffix рисуются в двух видах, и это разные сущности, а не
оформление одного.
segment (по умолчанию) — отдельный отсек, отрезанный рамкой и выровненный по
ступени размера: поле с «₽» и поле с «USD» стоят в колонку, а не пляшут по
ширине. Это то, чего ждут от денежного поля и поля с единицей измерения.
addon="inline" — украшение внутри рамки: ни разделителя, ни собственной
ширины. Лупа поисковой строки, символ валюты, счётчик. В сегменте лупа читалась
бы полем с приклеенной кнопкой, поэтому иконку внутри рамки нельзя было выразить
аддоном вовсе — от неё просто отказывались.
<GrInput v-model="query" addon="inline">
<template #prefix>
<GrIcon size="sm"><span class="i-lucide-search" /></GrIcon>
</template>
</GrInput>Отступ поля считается измеренной шириной аддона в обоих режимах: текст начинается
сразу за украшением, а не в пустом отсеке. prefixFixed/suffixFixed и
*MinWidth/*MaxWidth работают как раньше — они про сегмент.
Значащий аддон объявляется
Аддоны скрыты от скринридера по умолчанию: чаще всего там лупа поиска или глиф, повторяющий подпись поля, и второе прочтение мешает.
Но https:// в поле адреса или ₽ при подписи без валюты бывают единственным
местом, где сказано, что вводить. Для них есть describeAddons.
Он не просто снимает скрытие: <div> рядом с полем в его доступное имя не
входит, и диктор наткнулся бы на текст лишь при обходе страницы — не тогда,
когда он нужен. Поэтому аддон уезжает в aria-describedby самого поля.
<GrInput v-model="site" describe-addons>
<template #prefix>
https://
</template>
</GrInput>Декоративную иконку внутри слота помечайте aria-hidden сами: проп снимает
скрытие со всей обёртки, а не разбирает её содержимое.
Форма рамки
shape="pill" даёт то же скругление, что у GrSegmented; дефолт — box,
скругление шкалы контролов.
Форма тянет за собой горизонтальный отступ: у пилюли он не меньше половины
высоты, иначе текст заезжает в дугу. Аддоны идут за ней же — отсек обрезается по
внешнему контуру оболочкой (overflow-hidden), а его разделительная рамка
остаётся прямой.
Ось и её границы — ../sizes.md; там же сказано, каким полям
формы не досталось и почему.
Счётчик символов
showCount рисует 12 / 60 (или просто длину, если maxlength не задан) и
связывает счётчик с полем через aria-describedby — при фокусе диктор читает
остаток вместе с подписью.
Исчерпание лимита дополнительно объявляется живым регионом role="status"
(gr.input.limitReached). Регион молчит, пока лимит не выбран: читать вслух
каждый символ — гарантированный способ сделать поле неюзабельным для SR.
<GrInput v-model="bio" :maxlength="60" show-count clearable />События
| Событие | Когда |
|---|---|
update:modelValue | каждый ввод |
change | значение зафиксировано: нативный change (по blur или Enter) или кнопка очистки |
focus / blur | фокус пришёл/ушёл, аргумент — FocusEvent |
clear | значение стёрто кнопкой очистки |
clear существует отдельно от update:modelValue потому, что по одному лишь
значению программную очистку от ручного стирания не отличить — а реакция формы
на них обычно разная.
Очистка кнопкой шлёт все три события подряд: update:modelValue, change,
clear. Она такая же фиксация значения, как уход фокуса, — подписка ради
«значение установилось» пропускала бы ровно её. Нативный аналог ведёт себя так
же: крестик у <input type="search"> шлёт и input, и change.
Императивный API
<GrInput ref="field" v-model="value" />field.value отдаёт focus(), blur() и select(). Последний — спутник
focus() для сценария «подставили значение, дайте перезаписать».
loading
Спиннер в trailing-области плюс aria-busy на поле: проверка занятости логина,
автосохранение, догрузка справочника. Ввод при этом не блокируется — для
запрета есть disabled и readonly. Спиннер резервирует место справа наравне
с кнопками, поэтому текст под него не уезжает.
Состояния и токены
state (success / warning / danger) красит рамку и кольцо фокуса; invalid
(свой проп или ошибка из GrFormField) всегда приводит к danger-виду.
success и warning вдобавок показывают иконку и скрытую подпись, которая
уезжает в aria-describedby поля. Цвет рамки — единственный носитель смысла —
это WCAG 1.4.1: иконка добавляет форму, подпись добавляет программный канал,
которого у этих двух состояний нет (ARIA-эквивалента aria-invalid для них не
существует). danger иконки не получает: его смысл уже несут aria-invalid и
текст ошибки от GrFormField.
Признак гаснет, когда поле объявлено невалидным: галочка «корректно» рядом с
aria-invalid="true" противоречила бы ему.
Заблокированное поле гасится фоном --gr-muted с текстом --gr-muted-fg, а не
прозрачностью: opacity разбавляет выверенные на AA токены текста и роняет
контраст.
Классы размеров, выравнивания и состояний живут в grInputStyles.ts и целиком
объявлены в safelist — вместе с горизонтальным padding’ом, который тому же
размеру нужен и числом (аддоны задают паддинги инлайн-стилем, а он перекрывает
класс).
type
text, email, password, number, search, tel, url. Последние два
меняют экранную клавиатуру на мобильных и включают браузерную валидацию формата.
Пропы этого примера