Клики и фокус

Radix UI предоставляет низкоуровневые, но полностью управляемые компоненты для React, позволяя создавать интерфейсы с продуманным поведением фокуса и кликов. Важнейшими аспектами при работе с интерактивными элементами являются корректное управление фокусом, обработка кликов вне компонента и поддержка клавиатурной навигации.

Контроль фокуса

Radix UI использует систему focus management, обеспечивающую правильное взаимодействие с клавиатурой и скринридерами. Ключевые компоненты предоставляют следующие возможности:

  • FocusScope – оборачивает группу элементов, ограничивая фокус внутри нее. Это полезно для модальных окон или всплывающих меню, где важно удерживать фокус внутри компонента до его закрытия.
  • autoFocus и defaultFocus – свойства, позволяющие управлять тем, какой элемент получает фокус при монтировании компонента.
  • tabIndex – стандартное HTML-свойство интегрируется с Radix, позволяя расширять или ограничивать доступ к элементам через клавишу Tab.

Пример использования FocusScope:

import { FocusScope } from '@radix-ui/react-focus-scope';

<FocusScope trapped>
  <input placeholder="Имя" />
  <input placeholder="Фамилия" />
  <button>Отправить</button>
</FocusScope>

В этом примере фокус не покидает область компонентов при цикличной навигации через Tab.

Обработка кликов вне компонента

Radix UI предоставляет инструмент DismissableLayer для реализации закрытия элементов при клике вне них, что критично для дропдаунов, тултипов и модальных окон.

Ключевые свойства:

  • onDismiss – вызывается при клике вне области компонента.
  • disableOutsidePointerEvents – предотвращает случайное взаимодействие с элементами за пределами слоя, пока он активен.
  • modal – если включено, фокус автоматически удерживается внутри компонента до закрытия.

Пример закрытия дропдауна при клике вне:

import { DismissableLayer } from '@radix-ui/react-dismissable-layer';

<DismissableLayer onDism iss={() => setOpen(false)}>
  <div className="dropdown">
    <button>Выбрать</button>
    <ul>
      <li>Опция 1</li>
      <li>Опция 2</li>
    </ul>
  </div>
</DismissableLayer>

События клика и взаимодействие с клавиатурой

Radix UI обеспечивает консистентное поведение элементов независимо от способа взаимодействия пользователя:

  • onOpenChange – событие изменения состояния открытого компонента, используется для синхронизации UI.
  • onClickOutside – специализированное событие для закрытия компонентов при клике вне области.
  • onFocusOutside – позволяет реагировать на уход фокуса за пределы компонента.

Для элементов меню и аккордеонов важна поддержка навигации с клавиатуры:

import { RovingFocusGroup, RovingFocusItem } from '@radix-ui/react-roving-focus';

<RovingFocusGroup orientation="horizontal">
  <RovingFocusItem><button>Кнопка 1</button></RovingFocusItem>
  <RovingFocusItem><button>Кнопка 2</button></RovingFocusItem>
  <RovingFocusItem><button>Кнопка 3</button></RovingFocusItem>
</RovingFocusGroup>

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

Интеграция с модальными окнами и всплывающими элементами

Radix UI строится вокруг контролируемого состояния компонентов. Для модальных окон и всплывающих элементов используются комбинации FocusScope, DismissableLayer и событий клика:

  1. Фокус автоматически устанавливается на первый интерактивный элемент.
  2. При клике вне компонента срабатывает onDismiss.
  3. Навигация через Tab циклична, исключая уход фокуса за пределы модального окна.

Пример модального окна:

import { Dialog, DialogTrigger, DialogContent } from '@radix-ui/react-dialog';

<Dialog open={open} onOpenCha nge={setOpen}>
  <DialogTrigger>Открыть окно</DialogTrigger>
  <DialogContent>
    <FocusScope trapped>
      <h2>Модальное окно</h2>
      <input placeholder="Введите текст" />
      <button onCl ick={() => setOpen(false)}>Закрыть</button>
    </FocusScope>
  </DialogContent>
</Dialog>

Практические советы по кликам и фокусу

  • Всегда использовать FocusScope для компонентов, где фокус должен оставаться внутри.
  • Для дропдаунов и модальных окон сочетать DismissableLayer с FocusScope.
  • Учитывать клавиатурные события при построении кастомных меню.
  • Проверять доступность с помощью скринридеров и навигации через Tab/Arrow.

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