Выделение диапазонов

React Aria предоставляет мощный набор хуков и утилит для создания доступных пользовательских интерфейсов. Одной из ключевых задач в интерактивных приложениях является выделение диапазонов элементов, например, дат в календаре, строк в таблице или диапазонов значений в слайдере. Использование React Aria позволяет реализовать это с соблюдением стандартов доступности, включая поддержку клавиатуры и экранных читалок.


Основные хуки для управления диапазонами

useRangeSelection

Этот хук предназначен для управления состоянием выделения диапазонов. Он предоставляет следующие возможности:

  • Поддержка выделения одного элемента или диапазона элементов.
  • Управление состоянием выделения через selectedKeys и setSelectedKeys.
  • Настройка типа выделения: одиночное (single) или множественное (multiple).

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

import {useRangeSelection} from '@react-aria/selection';
import {useState} from 'react';

function DateRangeSelector({items}) {
  const [selectedKeys, setSelectedKeys] = useState(new Set());
  const {selectionProps} = useRangeSelection({
    selectedKeys,
    onSelectionChange: setSelectedKeys,
    selectionMode: 'multiple'
  });

  return (
    <ul {...selectionProps}>
      {items.map(item => (
        <li key={item.key} id={item.key}>
          {item.label}
        </li>
      ))}
    </ul>
  );
}

В этом примере selectionProps автоматически добавляет необходимые атрибуты ARIA и обработчики событий, позволяя пользователю выделять элементы с клавиатуры и мышью.


Типы выделения

React Aria поддерживает несколько режимов выделения:

  1. Одиночное (single) – можно выбрать только один элемент в списке.
  2. Множественное (multiple) – пользователь может выделять несколько несвязанных элементов.
  3. Диапазон (range) – выделение непрерывного диапазона элементов с помощью Shift-клавиши или жестов мыши.

Для работы с диапазоном особенно важна корректная обработка клавиатуры:

  • ArrowUp / ArrowDown – перемещение фокуса между элементами.
  • Shift + Arrow – расширение текущего диапазона выделения.
  • Ctrl + Click / Cmd + Click – добавление или снятие отдельных элементов при множественном выделении.

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

Для реализации визуального выделения важно синхронизировать состояние selectedKeys с компонентами интерфейса. Например:

<ul {...selectionProps}>
  {items.map(item => {
    const isSelected = selectedKeys.has(item.key);
    return (
      <li
        key={item.key}
        id={item.key}
        style={{
          background: isSelected ? 'lightblue' : 'transparent'
        }}
      >
        {item.label}
      </li>
    );
  })}
</ul>

Здесь выделенные элементы получают визуальный фон, а React Aria обеспечивает корректное поведение для доступности.


Использование useSelectableCollection для сложных структур

Когда элементы находятся в сложной иерархии или требуется динамическая коллекция, рекомендуется комбинировать useRangeSelection с useSelectableCollection. Это позволяет:

  • Автоматически управлять фокусом.
  • Поддерживать вложенные коллекции элементов.
  • Обеспечивать ARIA-атрибуты aria-selected, aria-multiselectable, aria-activedescendant.

Пример:

import {useSelectableCollection} from '@react-aria/selection';

function NestedList({sections}) {
  const [selectedKeys, setSelectedKeys] = useState(new Set());
  const {collectionProps} = useSelectableCollection({
    items: sections.flatMap(section => section.items),
    selectionMode: 'multiple',
    selectedKeys,
    onSelectionChange: setSelectedKeys
  });

  return (
    <div {...collectionProps}>
      {sections.map(section => (
        <div key={section.id}>
          <h3>{section.title}</h3>
          <ul>
            {section.items.map(item => (
              <li
                key={item.key}
                style={{
                  background: selectedKeys.has(item.key) ? 'lightgreen' : 'transparent'
                }}
              >
                {item.label}
              </li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
}

Доступность выделения диапазонов

React Aria обеспечивает соответствие WCAG следующим образом:

  • Экранные читалки получают корректные уведомления о состоянии выбранных элементов через aria-selected.
  • Фокусировка осуществляется с помощью aria-activedescendant, что позволяет навигацию без потери контекста.
  • Мышь и клавиатура полностью синхронизированы: действия клавиатуры повторяют действия мыши.

Настройка кастомного поведения

React Aria позволяет настраивать поведение выделения через параметры хука:

  • disallowEmptySelection – запрещает снятие всех выделений.
  • defaultSelectedKeys – начальная конфигурация выбранных элементов.
  • selectionBehavior – определяет, расширяется ли выделение при Shift или перезаписывается.

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

const {selectionProps} = useRangeSelection({
  selectedKeys,
  onSelectionChange: setSelectedKeys,
  selectionMode: 'range',
  disallowEmptySelection: true
});

Итоговое сочетание хуков

Для сложных компонентов, например календарей с выбором диапазона дат, рекомендуется использовать комбинацию:

  1. useRangeSelection – управление состоянием выделения.
  2. useSelectableCollection – поддержка вложенных или динамических списков.
  3. ARIA-атрибуты для визуальной обратной связи (aria-selected, aria-multiselectable).

Такое сочетание обеспечивает:

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

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