Одиночное выделение

Одиночное выделение (Single Selection) в интерфейсах подразумевает, что пользователь может выбрать только один элемент из набора доступных. В React Aria для управления такими компонентами используется хук useSingleSelectState совместно с компонентами ListBox, Option и вспомогательными ARIA-пропсами для обеспечения доступности.


Хук useSingleSelectState

Хук useSingleSelectState отвечает за управление состоянием выбранного элемента и взаимодействие с событиями выбора. Его ключевые свойства:

  • selectedKey – идентификатор выбранного элемента.
  • setSelectedKey(key) – функция для изменения выбора.
  • collection – коллекция элементов, переданная через items или children.
  • disabledKeys – массив ключей, которые нельзя выбрать.
  • allowsEmptySelection – флаг, разрешающий отсутствие выбранного элемента.

Пример инициализации состояния:

import { useSingleSelectState } from "@react-stately/select";

const state = useSingleSelectState({
  items: [
    { name: "Красный", id: "red" },
    { name: "Зелёный", id: "green" },
    { name: "Синий", id: "blue" }
  ],
  defaultSelectedKey: "green",
  allowsEmptySelection: false
});

Здесь defaultSelectedKey определяет изначально выбранный элемент, а allowsEmptySelection запрещает снять выбор полностью.


Компоненты и ARIA-пропсы

Для визуализации одиночного выбора используются компоненты из react-aria и react-stately:

  1. ListBox – контейнер для элементов выбора.
  2. Option – отдельный элемент выбора внутри ListBox.
  3. ARIA-пропсы – обеспечивают доступность для вспомогательных технологий.

Пример интеграции:

import { ListBox, Option } from "@react-aria/listbox";
import { useListBox, useOption } from "@react-aria/listbox";

function SingleSelect({ state }) {
  let ref = React.useRef();
  let { listBoxProps } = useListBox({ selectionMode: "single" }, state, ref);

  return (
    <ul {...listBoxProps} ref={ref}>
      {[...state.collection].map(item => (
        <OptionItem key={item.id} item={item} state={state} />
      ))}
    </ul>
  );
}

function OptionItem({ item, state }) {
  let ref = React.useRef();
  let { optionProps, isSelected, isDisabled } = useOption(
    { key: item.id },
    state,
    ref
  );

  return (
    <li
      {...optionProps}
      ref={ref}
      style={{
        background: isSelected ? "lightblue" : "transparent",
        color: isDisabled ? "gray" : "black",
        padding: "8px",
        cursor: isDisabled ? "not-allowed" : "pointer"
      }}
    >
      {item.name}
    </li>
  );
}

Ключевые моменты:

  • useListBox предоставляет все необходимые ARIA-пропсы для контейнера.
  • useOption управляет состоянием отдельного элемента и вычисляет isSelected и isDisabled.
  • Визуальное оформление можно настроить через style или CSS-классы, но выбор должен оставаться доступным через ARIA.

Обработка событий и взаимодействие

React Aria автоматически синхронизирует состояние выбора с событиями клавиатуры и мыши. Важные аспекты:

  • Клавиши навигации: стрелки вверх/вниз перемещают фокус между элементами.
  • Выбор элемента: Enter или Space устанавливает selectedKey.
  • Снятие выбора: если allowsEmptySelection = true, повторный выбор того же элемента снимает выбор.

Для полной интеграции с кастомными элементами можно использовать onSelectionChange:

state.setSelectedKey("red");
console.log(state.selectedKey); // "red"

Доступность и ARIA

Для одиночного выбора React Aria устанавливает следующие ARIA-атрибуты:

  • role="listbox" на контейнере.
  • role="option" на каждом элементе.
  • aria-selected="true|false" для отражения текущего состояния.
  • aria-disabled="true" для недоступных элементов.
  • aria-activedescendant для указания текущего элемента с фокусом.

Это позволяет скринридерам корректно сообщать пользователю о доступных опциях и текущем выборе.


Расширенные возможности

  1. Поддержка клавиатуры: стрелки, Home/End для быстрого перехода, Space/Enter для выбора.
  2. Фильтрация и поиск: интеграция с useComboBoxState для создания комбинированного компонента поиска с одиночным выбором.
  3. Динамические данные: добавление или удаление элементов из коллекции не нарушает управление состоянием.
  4. Доступность на мобильных устройствах: тач-события также корректно обрабатываются через React Aria.

Практические советы по реализации

  • Всегда использовать key для элементов списка, чтобы React корректно отслеживал состояние.
  • Выделение через state.selectedKey должно быть единственным источником истины.
  • Визуальные эффекты (hover, focus, selected) должны быть согласованы с ARIA-пропсами для поддержания доступности.
  • Для сложных списков с группировкой рекомендуется использовать Section и useListBoxSection для поддержания структуры иерархий.

Итоговая структура компонента одиночного выбора

  1. Создание состояния через useSingleSelectState.
  2. Рендер контейнера через ListBox с ARIA-пропсами.
  3. Рендер каждого элемента через Option с useOption.
  4. Управление выбором через state.selectedKey и state.setSelectedKey.
  5. Настройка визуальных эффектов через CSS или inline-стили с учётом доступности.
  6. Поддержка событий клавиатуры и мыши автоматически через хуки React Aria.

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