Одиночное выделение (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 запрещает снять выбор
полностью.
Для визуализации одиночного выбора используются компоненты из
react-aria и react-stately:
ListBox – контейнер для элементов
выбора.Option – отдельный элемент выбора
внутри ListBox.Пример интеграции:
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"
Для одиночного выбора React Aria устанавливает следующие ARIA-атрибуты:
role="listbox" на контейнере.role="option" на каждом элементе.aria-selected="true|false" для отражения текущего
состояния.aria-disabled="true" для недоступных элементов.aria-activedescendant для указания текущего элемента с
фокусом.Это позволяет скринридерам корректно сообщать пользователю о доступных опциях и текущем выборе.
useComboBoxState для создания комбинированного компонента
поиска с одиночным выбором.key для элементов списка, чтобы
React корректно отслеживал состояние.state.selectedKey должно быть
единственным источником истины.hover, focus,
selected) должны быть согласованы с ARIA-пропсами для
поддержания доступности.Section и useListBoxSection для поддержания
структуры иерархий.useSingleSelectState.ListBox с ARIA-пропсами.Option с
useOption.state.selectedKey и
state.setSelectedKey.Этот подход обеспечивает полностью управляемый, доступный и расширяемый компонент одиночного выбора в React-приложении.