GrTextarea
Берут, когда текст длиннее строки.
Обзор
Когда брать
- текст длиннее строки — комментарий, описание, письмо: перенос строк — часть значения;
- высота подстраивается —
autosizeсmaxLinesрастёт по содержимому и не съедает экран; - длина ограничена — счётчик символов и строк рядом с полем;
- пользователь меняет размер сам —
resizeвместо фиксированной высоты.
Когда взять другое
| Нужно | Берите |
|---|---|
| Значение в одну строку | GrInput |
| Нужно форматирование текста | rich-text редактора в экосистеме пока нет |
| Вводится число | GrNumberInput |
| Длинный текст запрашивают окном | GrPromptDialog с multiline |
Варианты все тона и виды — настоящим компонентом
state
defaultsuccesswarningdanger
Размеры
xssmmdlg
Состояния
обычнаяdisabledreadonlyinvalid
Матрица живая: наводите и нажимайте. Состояний наведения, фокуса и нажатия здесь нет — они за браузером, а не за пропом.
Playground контролы выводятся из API, а не пишутся руками
Загружается…
0 изменено· состояние — в адресе
<GrTextarea>Текст</GrTextarea>Примеры 3 у каждого — живое демо и его код
Ответ, за который платят частями
SMS уходит частями по 153 символа, и каждая часть оплачивается, поэтому слот
countговорит частями и тем, сколько осталось в текущей, —show-countне нужен, слот сам и есть просьба. Счётчик остаётся связан с полем черезaria-describedby;maxlengthдержит ответ в трёх частях, аclearableстирает черновик одним движением и сообщает об этомclear.Reply to Ana Ribeiro by SMSReplies go to +351 912 004 118 1 part · 47 left in this partЗаметки к выпуску для панели в десять строк
autosizeрастит поле вместе с заметками, чтобы черновик был виден целиком, аresize="none"убирает ручку, которая бы с ним спорила.show-line-countсmax-linesсчитает логические строки — панель в приложении режет на десяти — и никогда не обрезает текст за автора. После десятой страница ставитstate="warning": он добавляет иконку и скрытую подпись — один цвет сообщением не считается.Shown in the app after the update, ten lines at most
5 / 10Адрес доставки и записка курьеру
autocomplete="street-address"даёт браузеру заполнить адрес целиком, а распознанный индекс делает рамкуsuccess, когда поле оставили. После того как курьер забрал посылку, адрес —readonly: его можно выделить, и он уходит с формой, — а записка, поменьше поsizeи с пределомshow-count,disabledи в форму не попадает.
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
disabled | boolean | undefined | false | Выключить поле: значение не редактируется и не уходит в форму. Складывается с `GrFormField`. |
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 | Кнопка очистки значения. Настраивается через `GrConfigProvider`. |
clearLabel | string | undefined | undefined | A11y-подпись кнопки очистки. |
name | string | undefined | undefined | Нативный `name` — имя поля при отправке формы. |
id | string | undefined | undefined | `id` поля. Не задан — берётся из `GrFormField`, чтобы подпись нашла поле. |
rows | number | undefined | 4 | Высота в строках; с `autosize` — стартовая и минимальная. По умолчанию `4`. |
state | "default" | "success" | "warning" | "danger" | undefined | "default" | Подсветка рамки по решению разработчика. `success` и `warning` добавляют иконку и скрытую подпись; `invalid` сильнее. По умолчанию `default`. |
autocomplete | string | undefined | undefined | Нативный `autocomplete`: чем браузеру заполнять поле — например, `street-address`. |
maxlength | number | undefined | undefined | Ограничение длины + основа для счётчика символов. |
showCount | boolean | undefined | false | Показывать счётчик символов (`len` или `len/maxlength`). |
autosize | boolean | undefined | false | Подгонять высоту под содержимое (директива `v-autosize`). |
resize | GrTextareaResize | undefined | "vertical" | Ручное изменение размера пользователем. |
modelValueобязательныйv-model | string | — | Текст поля (`v-model`). |
showLineCount | boolean | undefined | — | Показывать счётчик строк. Считаются **логические** строки (переводы строки), а не визуальные переносы: при `autosize` число не меняется от ширины поля. |
maxLines | number | undefined | — | Ориентир по числу строк для счётчика (`3 / 10`). Ввод не ограничивает: обрезать набранный текст за пользователя компонент не вправе. |
Слоты
| Slot | Type | Описание |
|---|---|---|
count |
| Своя формулировка счётчика символов вместо `12 / 60` — «осталось 48», «почти предел» и что угодно ещё. Встроенных счётчиков два, но оба фиксированы по форме, и вторая готовая формулировка первой не заменяет. |
События
| 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 | Снять фокус с поля. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrTextareaProps, GrTextareaEmits, GrTextareaConfigurableProps } from '@feugene/granularity/components/GrTextarea'Файлdist/types/components/GrTextarea/GrTextarea.vue.d.ts
interface GrTextareaProps {
/** Текст поля (`v-model`). */
modelValue: string;
/** Подсказка в пустом поле. Подпись не заменяет: имя даёт `GrFormField` или `ariaLabel`. */
placeholder?: string;
/** Нативный `autocomplete`: чем браузеру заполнять поле — например, `street-address`. */
autocomplete?: string;
/** Выключить поле: значение не редактируется и не уходит в форму. Складывается с `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?: GrTextareaState;
/** Нативный `name` — имя поля при отправке формы. */
name?: string;
/** `id` поля. Не задан — берётся из `GrFormField`, чтобы подпись нашла поле. */
id?: string;
/** Высота в строках; с `autosize` — стартовая и минимальная. По умолчанию `4`. */
rows?: number;
/** Кегль и отступы по шкале контролов. По умолчанию `md`; берётся из `GrConfigProvider`. */
size?: GrTextareaSize;
/** Ограничение длины + основа для счётчика символов. */
maxlength?: number;
/** Показывать счётчик символов (`len` или `len/maxlength`). */
showCount?: boolean;
/**
* Показывать счётчик строк. Считаются **логические** строки (переводы строки), а
* не визуальные переносы: при `autosize` число не меняется от ширины поля.
*/
showLineCount?: boolean;
/**
* Ориентир по числу строк для счётчика (`3 / 10`). Ввод не ограничивает: обрезать
* набранный текст за пользователя компонент не вправе.
*/
maxLines?: number;
/** Подгонять высоту под содержимое (директива `v-autosize`). */
autosize?: boolean;
/** Кнопка очистки значения. Настраивается через `GrConfigProvider`. */
clearable?: boolean;
/** A11y-подпись кнопки очистки. */
clearLabel?: string;
/** Ручное изменение размера пользователем. */
resize?: GrTextareaResize;
}interface GrTextareaEmits {
/** Текст изменился (`v-model`) — на каждый ввод и при очистке. */
(e: 'update:modelValue', value: string): void;
/** Значение зафиксировано нативным `change` — по `blur`. */
(e: 'change', value: string): void;
/** Значение стёрто кнопкой очистки. */
(e: 'clear'): void;
/** Фокус пришёл в поле. */
(e: 'focus', event: FocusEvent): void;
/** Фокус ушёл из поля. */
(e: 'blur', event: FocusEvent): void;
}Файлdist/types/components/GrTextarea/config.d.ts
const grTextareaConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrTextarea">;Файлdist/types/components/GrTextarea/defaults.d.ts
Пропы GrTextarea, настраиваемые глобально через componentDefaults.
size типизируем через GrComponentSize, а не через GrTextareaSize:
последний объявлен в grTextareaStyles.ts, который тянет контекст, — вышел
бы цикл модулей.
interface GrTextareaConfigurableProps {
size: GrComponentSize;
/** Кнопка очистки значения. */
clearable: boolean;
}Файлdist/types/components/GrTextarea/grTextareaStyles.d.ts
type GrTextareaState = GrControlState;type GrTextareaSize = GrComponentSize;Файлdist/types/components/GrTextarea/safelist.d.ts
const grTextareaSafelist: string[];Файлdist/types/components/GrTextarea/index.d.ts
type GrTextareaInstance = ComponentExposed<typeof GrTextareaComponent>;Доступность
Паттерн APG
—
| Клавиша | Действие |
|---|---|
| — | Tab из поля — на кнопку очистки при clearable; Enter/Space очищает и возвращает фокус в поле |
Полный контракт пакета: docs/keyboard.md ↗
Использование из прозы библиотеки
События
| Событие | Когда |
|---|---|
update:modelValue | на каждый ввод |
change | значение зафиксировано: нативный change (по уходу фокуса) или кнопка очистки |
clear | значение стёрто кнопкой очистки (clearable) |
focus, blur | с объектом FocusEvent |
Набор совпадает с GrInput, чтобы обёртки над контролами
писались одинаково. Нативные события переизлучаются компонентом: объявленный
emit уходит из $attrs, и без этого @change у потребителя перестал бы
работать.
clearable повторяет анатомию GrInput: крестик в правом верхнем углу при
непустом значении, скрыт при disabled/readonly, настраивается через
GrConfigProvider. Поле держит место под крестик справа, пока он может
появиться, — первая строка не уходит под «×».
Обёртка вокруг textarea есть всегда, а кнопка, признак состояния и счётчики
(showCount, showLineCount) — её соседи по условию. Раньше обёртка
появлялась вместе с ними, и <textarea> пересоздавался: стоило state стать
success на лету, фокус уходил на <body>, и следующие нажатия пропадали.
class и style ложатся на обёртку, остальные атрибуты — на сам <textarea>
(как у GrInput, см. form-controls.md). Поле внутри —
блочное: высота с соседями и без них одна.
Паритет с GrInput
size, readonly, maxlength + showCount работают так же, как в поле ввода:
textarea в одной форме рядом с GrInput не должна отличаться ни кеглем, ни
набором возможностей.
Счётчик связан с полем через aria-describedby — иначе «12 / 60» видно
глазами, но не слышно, при том что ограничение длины и есть его смысл.
state="success" и "warning" тем же приёмом получают иконку и скрытую
подпись в aria-describedby: цвет рамки как единственный носитель смысла — это
WCAG 1.4.1, а ARIA-эквивалента aria-invalid у этих двух состояний нет.
danger иконки не получает — его смысл уже несут aria-invalid и текст ошибки
от GrFormField, — и признак гаснет, как только поле объявлено невалидным.
Признак появляется в той же обёртке, что и кнопка очистки: поле при этом не пересоздаётся, и фокус с кареткой остаются на месте.
Счётчик строк
showLineCount печатает вторую подпись в том же ряду: слева строки, справа
символы. Строки считаются логическими — по переводам строки, — поэтому при
autosize число не зависит от ширины поля: визуальный перенос строкой не считается.
Подпись локализована и склоняется (gr.textarea.lines), а maxLines меняет её на
формат 3 / 10. Ограничения ввода у него нет намеренно: обрезать набранный текст
за пользователя компонент не вправе — счётчик показывает перебор (12 / 10), а
решение остаётся за формой.
Оба счётчика включаются независимо и оба связаны с полем через aria-describedby.
Своя формулировка
12 / 60 — не единственный способ сказать про длину, а счётчик строк вторую
формулировку не заменяет: он просто второй готовый счётчик, такой же
фиксированный по форме. Слот #count отдаёт текст потребителю:
<GrTextarea v-model="text" :maxlength="280">
<template #count="{ remaining }">
{{ remaining < 0 ? `перебор на ${-remaining}` : `осталось ${remaining}` }}
</template>
</GrTextarea>Слот получает length, maxlength и remaining; без maxlength последние два
приходят undefined — предел не выдумывается. remaining не зажат в ноль:
maxlength держит ввод с клавиатуры, но значение, пришедшее в v-model из
кода, ограничение перешагивает, и «-3» там честнее нуля.
Отдельный show-count при этом не нужен — заданный слот сам по себе просьба
показать счётчик. Место в ряду, связка с полем через aria-describedby и
оформление остаются те же: слот меняет текст, а не устройство.
Автоподгонка высоты
<GrTextarea v-model="text" autosize :rows="2" />autosize включает директиву v-autosize, которая уже была в пакете и просто
не была подключена к компоненту. rows задаёт стартовую высоту.
resize (vertical по умолчанию, none, both) управляет ручным
растягиванием: вместе с autosize его обычно выключают.
Тип пропов
GrTextareaProps — объявленный interface, а не typeof props. Прежний
экспорт отдавал тип разрешённых пропов: после withDefaults все поля с
дефолтами становились обязательными и readonly, и попытка написать
const p: GrTextareaProps = { modelValue: '' } падала на ровном месте.
Отключённое состояние
Гасится фоном --gr-muted и текстом --gr-muted-fg, а не opacity:
прозрачность разбавляет выверенные на AA токены.
Пропы этого примера