Множественное выделение

Множественное выделение (multi-selection) — ключевой элемент взаимодействия с компонентами списка, таблицы и меню, где пользователю необходимо выбрать несколько элементов одновременно. В React Aria это реализуется через хуки, обеспечивающие корректную работу с доступностью (accessibility) и управление состоянием без ручного написания сложной логики.

Использование useMultipleSelectionState

Для управления состоянием множественного выбора применяется хук useMultipleSelectionState. Он позволяет хранить текущие выделенные элементы и управлять добавлением или удалением элементов из выделенной группы.

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

import {useMultipleSelectionState} from '@react-stately/selection';

const state = useMultipleSelectionState({
  selectedKeys: new Set(), 
  onSelectionChange: keys => console.log(keys),
  disallowEmptySelection: false
});

Параметры:

  • selectedKeys — множество ключей выбранных элементов.
  • onSelectionChange — callback, который вызывается при изменении выделения.
  • disallowEmptySelection — если true, нельзя снять выделение со всех элементов.

Хук возвращает объект state с методами:

  • isSelected(key) — проверяет, выделен ли элемент.
  • toggleKey(key) — переключает состояние выделения для указанного ключа.
  • selectAll() — выделяет все элементы.
  • clearSelection() — снимает выделение со всех элементов.

Интеграция с компонентами списка

React Aria предлагает использовать useListBox вместе с useMultipleSelectionState для реализации списков с множественным выделением.

Пример базового списка:

import {useListBox, useListBoxSection} from '@react-aria/listbox';
import {useMultipleSelectionState} from '@react-stately/selection';

const items = [
  {id: '1', name: 'Элемент 1'},
  {id: '2', name: 'Элемент 2'},
  {id: '3', name: 'Элемент 3'}
];

function MultiSelectList() {
  const state = useMultipleSelectionState({selectedKeys: new Set()});

  return (
    <ul role="listbox" aria-multiselectable="true">
      {items.map(item => (
        <li 
          key={item.id}
          role="option"
          aria-selected={state.isSelected(item.id)}
          onCl ick={() => state.toggleKey(item.id)}
        >
          {item.name}
        </li>
      ))}
    </ul>
  );
}

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

  • Атрибут aria-multiselectable="true" обязателен для корректной доступности.
  • role="option" у каждого элемента списка.
  • aria-selected отражает текущее состояние выделения.

Управление сложными сценариями

  1. Выделение диапазона с Shift: Для поддержки выделения диапазона необходимо использовать useSelectableCollectionState и обработчики клавиатуры. React Aria предоставляет утилиты, позволяющие отслеживать последнюю выбранную позицию и добавлять элементы между текущей и предыдущей выделенной позицией при зажатом Shift.

  2. Комбинация Ctrl/Cmd + Click: toggleKey автоматически корректно добавляет или убирает элемент из множества выбранных при использовании клавиши модификатора.

  3. Select All / Deselect All: Методы selectAll() и clearSelection() позволяют реализовать кнопки или хоткеи для быстрого управления выделением.

<button onCl ick={() => state.selectAll()}>Выделить все</button>
<button onCl ick={() => state.clearSelection()}>Снять выделение</button>

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

React Aria обеспечивает полную поддержку клавиатурного взаимодействия.

  • Стрелки вверх/вниз: перемещение между элементами.
  • Пробел или Enter: переключение состояния выделения текущего элемента.
  • Shift + стрелки: расширение диапазона выделения.
  • Ctrl/Cmd + A: выделение всех элементов (можно реализовать через selectAll()).

Рекомендации по структуре данных

  • Использовать уникальные ключи для каждого элемента.
  • Структуры данных должны быть неизменяемыми, чтобы хук корректно отслеживал изменения.
  • Для больших списков рекомендуется хранить Set вместо массива для повышения производительности при проверках isSelected.

Стилизация и визуальная индикация

  • Элементы с aria-selected="true" можно стилизовать через CSS, например, добавлять фон или рамку.
  • React Aria не навязывает конкретный стиль, что позволяет создавать кастомные интерфейсы, сохраняя доступность.
li[aria-selected="true"] {
  background-color: #e0f7fa;
}

Совмещение с другими хуками

Множественное выделение можно комбинировать с:

  • useFocusRing — для визуального отображения фокуса.
  • useKeyboard — для расширенной обработки клавиатуры.
  • useOverlay — для списков в выпадающих меню или модальных окнах.

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