Перейти к содержимому
v2.5.0
English
Русский
Документация

Select

Select — составной компонент из корневого контейнера состояния и экспортируемых дочерних частей для триггера, значения, вариантов и нативного участия в форме.

Паттерны

Select с поиском

Когда использовать:

  • список вариантов длинный
  • пользователям нужна текстовая фильтрация перед выбором
  • компактность поля всё ещё важна

Заметки о композиции:

  • рендерите текстовое поле внутри SelectListbox
  • отключите встроенный поиск при наборе через typeahead={false}
  • фильтруйте отрендеренный список SelectOption по внешнему состоянию
  • передавайте название выбранного варианта в SelectValue дочерним элементом: отфильтрованный вариант не рендерится, и копировать нечего
  • обрабатывайте Enter в поле поиска сами (здесь он выбирает первое совпадение): стрелки и Enter по списку работают, только пока фокус на триггере

Компромиссы:

  • облегчает поиск в длинных списках
  • усложняет работу с клавиатуры по сравнению с обычным select
searchable-select.tsx · 109 строк
import {
  Label,
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
  TextField,
  TextFieldInput,

Контролируемый Select с очисткой

Когда использовать:

  • выбранное значение можно сбросить
  • поле должно синхронизироваться с состоянием URL, фильтрами или внешней логикой формы

Заметки о композиции:

  • храните выбор в состоянии React
  • передавайте value и onChangeValue
  • предусмотрите отдельное действие очистки, которое ставит null, а не undefined

Компромиссы:

  • состояние остаётся явным и предсказуемым
  • добавляет сложности в состояние родителя по сравнению с неконтролируемым использованием
controlled-value-with-clear-action.tsx · 56 строк
import {
  Button,
  Label,
  Select,
  SelectCombobox,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';
import { useId, useState } from 'react';

Select внутри оверлея

Когда использовать:

  • модальное окно, выдвижная панель (Drawer) или шторка (Sheet) содержат поле одиночного выбора
  • список должен сосуществовать с другой системой оверлеев

Заметки о композиции:

  • рендерите Select как обычно внутри содержимого оверлея
  • портал не нужен: список рендерится в top layer поверх оверлея
  • перепроверьте восстановление фокуса и закрытие в реальном оверлее

Компромиссы:

  • сохраняет обычное поведение поля внутри оверлеев
  • требует больше интеграционного тестирования, чем изолированное использование компонента
select-inside-dialog.tsx · 80 строк
import type { CSSProperties, FormEvent } from 'react';
import {
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogTrigger,
  Heading,
  Icon,
  IconButton,
  Label,
  Select,

Select внутри предложения

Когда использовать:

  • элемент встроен в читаемый текст в виде предложения
  • выбор лёгкий и должен ощущаться встроенным

Заметки о композиции:

  • используйте variant='inline'
  • окружайте поле осмысленным текстом
  • не используйте вариант inline без контекста
  • дайте триггеру собственный aria-label: предложение вокруг не является меткой

Компромиссы:

  • ощущается лёгким и встроенным
  • ясность и подпись зависят от окружающего текста
inline-variant-inside-sentence-like-ui.tsx · 31 строка
import {
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';

export default function InlineVariantInsideSentenceLikeUi() {

Насыщенное содержимое вариантов

Когда использовать:

  • вариантам нужны иконки или дополнительный текст
  • визуальный просмотр важнее минимальной плотности строк

Заметки о композиции:

  • оставляйте value скалярным
  • делайте единицей выбора всю строку варианта
  • не разбивайте вариант на несколько интерактивных элементов
  • передайте label, чтобы typeahead искал по названию, и короткий дочерний элемент SelectValue, чтобы триггер не копировал всю строку
  • у вариантов фиксированная высота, поэтому многострочному содержимому нужен height: 'auto' на варианте

Компромиссы:

  • облегчает просмотр сложных вариантов
  • увеличивает высоту строк и семантическую сложность
rich-option-content.tsx · 54 строки
import {
  Label,
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';
import { useId, useState } from 'react';

Select в нативной форме

Когда использовать:

  • выбранное значение должно отправляться обычными механизмами формы
  • интеграция опирается на FormData

Заметки о композиции:

  • добавьте SelectHiddenSelect
  • держите select внутри границ формы

Компромиссы:

  • улучшает совместимость с нативными формами
  • добавляет в композицию ещё одну обязательную часть
native-form-participation.tsx · 68 строк
import {
  Button,
  Label,
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';

Примечания:

  • без SelectHiddenSelect элемент может рендериться корректно, но контракт нативной формы становится слабее

Своя иконка

Когда использовать:

  • иконка триггера должна подсказывать вид значения, а не только наличие списка
  • стандартный шеврон не подходит к окружающему интерфейсу

Заметки о композиции:

  • передайте свою иконку дочерним элементом SelectIcon; она заменяет стандартный шеврон
  • держите SelectIcon внутри SelectCombobox, после SelectValue

Компромиссы:

  • придаёт триггеру смысл с первого взгляда
  • без шеврона менее очевидно, что поле открывает список
custom-icon.tsx · 27 строк
import {
  Select,
  SelectCombobox,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
} from '@vega-ui/react';
import { GlobeIcon } from '@vega-ui/icons';

export default function CustomIcon() {
  return (

Сравнение

Быстрое правило выбора

Выбирайте Select, если верно всё перечисленное:

  • выбор одиночный
  • значение — string или number
  • набор вариантов лучше скрыть, чем показывать постоянно
  • важны участие в нативной форме или семантика поля

Выбирайте другой элемент, если видимость, бинарное состояние или более богатое содержимое с действиями важнее компактного поведения поля.

Select и SegmentedControl

Используйте Select, когда:

  • вариантов много
  • места мало
  • варианты не обязательно показывать всё время

Используйте SegmentedControl, когда:

  • вариантов всего несколько
  • пользователю полезно видеть все варианты сразу
  • переключение между вариантами должно ощущаться мгновенным

Компромисс:

  • Select экономит место, но скрывает варианты
  • SegmentedControl облегчает обнаружение вариантов, но занимает ширину макета

Select и Radio

Используйте Select, когда:

  • список вариантов длинный
  • поле находится в плотной форме
  • элемент должен оставаться компактным

Используйте Radio, когда:

  • набор вариантов короткий
  • пользователи должны напрямую сравнивать все доступные варианты
  • явная видимость одиночного выбора важнее компактности

Компромисс:

  • Select плотнее
  • Radio нагляднее для сравнения

Select и Checkbox

Используйте Select, когда:

  • пользователь должен выбрать ровно один вариант
  • значение должно вести себя в форме как скалярное поле

Используйте Checkbox, когда:

  • взаимодействие бинарное
  • каждый элемент представляет независимый выбор «да» или «нет»

Не заменяйте группы из нескольких чекбоксов одним Select, если модель продукта на самом деле не подразумевает одиночный выбор.

Select и Switch

Используйте Select, когда:

  • пользователь выбирает из нескольких значений
  • выбранное значение должно сохраняться как одно поле

Используйте Switch, когда:

  • взаимодействие — мгновенное включение или выключение
  • настройка меняется напрямую, без выбора из списка

Компромисс:

  • Select — для выбора из перечисления
  • Switch — для бинарного состояния

Select и Popover

Используйте Select, когда:

  • основная задача — выбрать значение
  • нужны навигация по вариантам с клавиатуры и семантика значения

Используйте Popover, когда:

  • содержимое — не поле одиночного выбора в первую очередь
  • оверлей содержит действия, справочный текст или собственный интерфейс, а не семантику списка

Выбирайте этот компонент, когда

  • выбор одиночный
  • значение должно вести себя как одно компактное поле
  • набор вариантов настолько длинный, что постоянно видимые элементы добавили бы шума
  • важны участие в форме или плотный макет

Не выбирайте этот компонент, когда

  • пользователю полезно видеть все варианты сразу
  • взаимодействие бинарное, а не выбор из перечисления
  • содержимое оверлея на самом деле не является полем
  • модель значения опирается на объекты, а не на скалярные ключи

Решение проблем

Выбранное значение не отправляется с формой

Симптом

Элемент рендерится и визуально обновляется, но выбранного значения нет в нативной отправке формы.

Вероятная причина

В композиции отсутствует SelectHiddenSelect.

Как проверить

  • отправьте окружающую форму
  • изучите получившийся FormData
  • убедитесь, что ожидаемого имени поля или значения там нет

Решение

Рендерите SelectHiddenSelect с name внутри корневого Select.

native-form-participation.tsx · 68 строк
import {
  Button,
  Label,
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';

С клавиатуры нельзя выбрать отфильтрованный вариант

Симптом

Поле поиска внутри SelectListbox фильтрует варианты, но после ввода выбрать вариант без мыши невозможно.

Вероятная причина

Стрелки и Enter перемещают по списку, только пока фокус на триггере. Когда фокус в поле поиска, Select не обрабатывает его клавиши, а Tab уводит фокус из списка и закрывает его.

Как проверить

  • откройте Select и введите текст в поле поиска
  • нажмите стрелки и Enter
  • проверьте, выбирается ли какой-нибудь вариант

Решение

Обрабатывайте Enter в поле поиска сами: выбирайте первое совпадение, закрывайте список и возвращайте фокус на триггер. Передавайте название выбранного варианта в SelectValue дочерним элементом, потому что отфильтрованный вариант не рендерится.

searchable-select.tsx · 109 строк
import {
  Label,
  Select,
  SelectCombobox,
  SelectHiddenSelect,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
  TextField,
  TextFieldInput,

Select визуально работает, но в диалоге поведение клавиатуры кажется сломанным

Симптом

Select открывается, но перемещение фокуса, закрытие или навигация с клавиатуры внутри оверлея ведут себя непоследовательно.

Вероятная причина

Поведение в составе оверлея проверяли изолированно, но не перепроверили внутри Dialog, Drawer или Sheet.

Как проверить

  • отрендерите точную интеграцию внутри реального оверлея
  • проверьте с клавиатуры открытие, закрытие, возврат фокуса и навигацию по вариантам

Решение

  • сохраняйте стандартную композицию Select нетронутой
  • перепроверьте с реальным стеком оверлеев
  • избегайте собственных структур триггера или вариантов, мешающих движению фокуса

Плейсхолдер никогда не появляется

Симптом

placeholder задан, но триггер всегда показывает какое-то другое содержимое.

Вероятная причина

Начальное значение уже выбрано, или собственные дочерние элементы SelectValue всегда рендерят содержимое.

Как проверить

  • уберите value и defaultValue
  • уберите собственные дочерние элементы из SelectValue
  • проверьте, становится ли плейсхолдер видимым

Решение

  • используйте placeholder только когда выбранного значения нет
  • убедитесь, что собственный рендер значения учитывает пустое состояние

Очистка значения не обновляет интерфейс

Симптом

Родитель логически очищает значение, но интерфейс всё ещё показывает старый выбор.

Вероятная причина

Родитель ставит значение в undefined. Для Select undefined означает неконтролируемый режим, и компонент возвращается к собственному внутреннему состоянию, где хранится старый выбор; контролируемое пустое значение — null.

Как проверить

  • изучите текущий проп value
  • убедитесь, что onChangeValue обновляет тот же источник истины

Решение

Очищайте контролируемое значение через null и преобразуйте undefined, который может прийти в onChangeValue: onChangeValue={(next) => setValue(next ?? null)}.

controlled-value-with-clear-action.tsx · 56 строк
import {
  Button,
  Label,
  Select,
  SelectCombobox,
  SelectIcon,
  SelectListbox,
  SelectOption,
  SelectValue,
  Text,
} from '@vega-ui/react';
import { useId, useState } from 'react';

Варианты рендерятся корректно, но выбор не меняется

Симптом

Варианты видны и кликабельны, но выбранное значение не обновляется как ожидается.

Вероятная причина

Значения SelectOption не соответствуют ожидаемой скалярной модели значений, или собственная композиция варианта сломала выбираемую строку.

Как проверить

  • убедитесь, что у каждого SelectOption значение типа string или number
  • проверьте, остаётся ли вся строка варианта единицей выбора

Решение

  • оставляйте значения вариантов скалярными
  • не разбивайте один вариант на несколько конкурирующих интерактивных элементов

Только для чтения и отключённое состояние ведут себя не так, как ожидалось

Симптом

Состояние элемента визуально выглядит правильно, но фокусируемость или взаимодействие не соответствуют ожиданиям продукта.

Вероятная причина

readOnly и disabled восприняли как визуальные варианты, а не как варианты поведения.

Как проверить

  • проверьте фокусируемость и взаимодействие в обоих состояниях
  • уточните, должен ли элемент по-прежнему получать фокус

Решение

  • используйте disabled, когда взаимодействие должно полностью прекратиться
  • используйте readOnly, когда поле должно оставаться фокусируемым, но неизменяемым

Значения-объекты вызывают проблемы с типами или поведением

Симптом

При передаче целых объектов в качестве значений вариантов появляются ошибки типов, нестабильный выбор или несовпадающие значения.

Вероятная причина

Публичный API рассчитан на значения string и number, а не на объекты.

Как проверить

  • изучите value, переданный в Select
  • убедитесь, что SelectOption value={...} скалярный

Решение

Храните в Select скалярный ключ, а полный объект получайте на уровне родителя.

Select выглядит правильно, но композиция всё равно хрупкая

Симптом

Компонент вроде бы работает, но небольшие изменения в настройке постоянно ломают поведение.

Вероятная причина

Слишком много поведения перенесено в собственные обёртки вместо готовых частей.

Как проверить

  • сравните текущую композицию со стандартной из index.md и anatomy.md
  • убирайте собственные обёртки постепенно

Решение

  • сначала вернитесь к стандартной композиции
  • добавляйте собственные обёртки обратно только там, где встроенных частей недостаточно
  • используйте useSelectContext только для продвинутых случаев