Индикаторы вставки

Индикаторы вставки (Insertion Indicators) являются ключевым элементом доступного взаимодействия с компонентами, где требуется динамическая организация элементов, например, в списках с перетаскиванием (drag-and-drop), редакторах контента или сложных интерактивных таблицах. React Aria предоставляет набор хуков и утилит для правильного отображения и управления такими индикаторами, обеспечивая поддержку экранных читалок и клавиатурной навигации.


Основные концепции

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

Ключевые концепты:

  • useDroppable — хук для контейнера, в который можно вставлять элементы. Отвечает за определение доступных зон вставки и генерацию событий для обновления состояния индикатора.
  • useDraggable — хук для элементов, которые можно перетаскивать. Обеспечивает передачу текущей позиции и взаимодействие с Droppable.
  • useDropIndicator — хук, который напрямую связывает визуальный индикатор с контекстом дропа. Позволяет задать, где отображается линия вставки, и реагировать на состояние hovered, dragging и insertPosition.

Настройка контейнера для вставки

Создание контейнера начинается с применения useDroppable:

import { useDroppable } from '@react-aria/dnd';

function ListContainer({ id, children }) {
  let { droppableProps } = useDroppable({ id });
  
  return (
    <ul {...droppableProps}>
      {children}
    </ul>
  );
}

Ключевой момент: droppableProps включает aria-атрибуты и обработчики событий для взаимодействия с экранными читалками и мышью. Это обеспечивает правильное оповещение о зоне вставки.


Создание перетаскиваемого элемента

Хук useDraggable возвращает свойства для управления состоянием перетаскивания:

import { useDraggable } from '@react-aria/dnd';

function ListItem({ item }) {
  let { dragProps, isDragging } = useDraggable({ id: item.id });
  
  return (
    <li {...dragProps} style={{ opacity: isDragging ? 0.5 : 1 }}>
      {item.content}
    </li>
  );
}

Особенности:

  • dragProps содержит события onDragStart, onDragEnd и атрибуты aria-grabbed.
  • isDragging используется для визуальной обратной связи, например, полупрозрачного отображения перетаскиваемого элемента.

Индикация позиции вставки

Для визуального индикатора вставки применяется useDropIndicator:

import { useDropIndicator } from '@react-aria/dnd';

function DropIndicator({ position }) {
  let { dropIndicatorProps } = useDropIndicator({ position });
  
  return (
    <div {...dropIndicatorProps} className="drop-indicator" />
  );
}

Обратите внимание на:

  • position определяет точку вставки относительно соседних элементов (before, after, on).
  • Класс drop-indicator должен содержать CSS для отображения линии или маркера. Например:
.drop-indicator {
  height: 2px;
  background-color: blue;
  margin: 0;
  transition: all 0.2s ease;
}
  • dropIndicatorProps автоматически содержит ARIA-атрибуты для оповещения о текущем состоянии (aria-hidden, role="presentation"), что делает индикатор доступным для экранных читалок.

Управление состоянием вставки

React Aria не управляет глобальным состоянием дропа; это делегируется через контекст. Обычно создаётся контекст DndContext:

import { createContext, useState } from 'react';

export const DndContext = createContext();

export function DndProvider({ children }) {
  const [draggedItem, setDraggedItem] = useState(null);
  const [dropPosition, setDropPosition] = useState(null);

  return (
    <DndContext.Provider value={{ draggedItem, setDraggedItem, dropPosition, setDropPosition }}>
      {children}
    </DndContext.Provider>
  );
}

Преимущества:

  • Любой компонент может подписаться на текущее состояние дропа.
  • Индикатор вставки обновляется автоматически при изменении dropPosition.
  • Поддерживаются сложные сценарии: вложенные списки, динамическое изменение порядка элементов, drag-and-drop с клавиатуры.

Поддержка клавиатуры и экранных читалок

  • ARIA-атрибуты: aria-dropeffect, aria-grabbed, aria-describedby для описания текущей позиции вставки.
  • Навигация с клавиатуры: React Aria обеспечивает возможность перемещения элемента стрелками и вставки через Enter или Space.
  • Обратная связь: визуальный индикатор всегда синхронизирован с фокусом и событием hover, что делает интерфейс доступным для пользователей с ограничениями.

Практический пример интеграции

<DndProvider>
  <ListContainer id="my-list">
    {items.map((item, index) => (
      <>
        {dropPosition === index && <DropIndicator position="before" />}
        <ListItem key={item.id} item={item} />
        {dropPosition === index + 1 && <DropIndicator position="after" />}
      </>
    ))}
  </ListContainer>
</DndProvider>

Особенности реализации:

  • Индикатор вставки вставляется между элементами, в зависимости от состояния dropPosition.
  • Можно динамически менять цвет, толщину или анимацию линии при наведении.
  • Поддерживается бесшовная интеграция с другими ARIA-компонентами React.

Рекомендации по дизайну

  • Использовать тонкую линию или полупрозрачный блок для индикатора, чтобы не отвлекать внимание от контента.
  • Всегда синхронизировать состояние dropPosition с событиями мыши и клавиатуры.
  • Включать визуальный и программный сигнал одновременно, чтобы обеспечить полную доступность.
  • При сложных списках с вложенными уровнями, комбинировать position: before/after/on с контекстным отступом для точного отображения.

Использование React Aria для индикаторов вставки обеспечивает строгую доступность, предсказуемое взаимодействие и лёгкую интеграцию с другими компонентами, делая drag-and-drop интерфейсы безопасными и удобными для всех пользователей.