Позиционирование относительно триггера

В библиотеке React Aria позиционирование элементов интерфейса относительно триггера (trigger) играет ключевую роль в создании доступных и предсказуемых пользовательских интерфейсов. Под триггером понимается элемент, который инициирует отображение всплывающего компонента, такого как Tooltip, Popover, Menu или Dialog. React Aria предоставляет набор утилит для корректного вычисления позиции и предотвращения конфликтов с границами экрана.


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

  1. Trigger и overlay

    • Trigger — это элемент, взаимодействие с которым вызывает появление overlay.
    • Overlay — компонент, который отображается поверх основного контента. Overlay может быть Popup, Menu, Tooltip или другим всплывающим элементом.
  2. Anchor и placement

    • Anchor — точка привязки overlay относительно trigger. В большинстве случаев это сам trigger или его bounding box.
    • Placement — направление отображения overlay относительно trigger. React Aria поддерживает стандартные позиции: top, bottom, left, right и их вариации (top-start, bottom-end и т.д.).
  3. Edge detection и collision handling Overlay автоматически корректирует своё положение, чтобы не выйти за пределы viewport. React Aria использует collision detection, предотвращая обрезание или скрытие контента.


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

Основной хук для позиционирования — useOverlayPosition. Он вычисляет координаты для overlay и передаёт необходимые свойства в компонент.

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

import {useOverlayPosition} from '@react-aria/overlays';
import {useRef} from 'react';

function Tooltip({triggerRef, isOpen, children}) {
  const overlayRef = useRef(null);
  const {overlayProps, placement} = useOverlayPosition({
    targetRef: triggerRef,
    overlayRef,
    placement: 'top',
    offset: 8
  });

  if (!isOpen) return null;

  return (
    <div {...overlayProps} ref={overlayRef} style={{position: 'absolute'}}>
      {children}
    </div>
  );
}

Ключевые параметры:

  • targetRef — ссылка на trigger.
  • overlayRef — ссылка на overlay, используется для расчёта позиции.
  • placement — предпочтительное направление отображения.
  • offset — расстояние между trigger и overlay.
  • isOpen — определяет, отображается ли overlay.

useOverlayPosition возвращает объект с overlayProps, которые включают стили позиции (top, left) и атрибуты для управления фокусом и взаимодействием.


Управление изменением позиции при прокрутке и ресайзе

React Aria автоматически отслеживает изменения размеров viewport и scroll, обновляя позицию overlay. Для этого достаточно использовать ref overlay и target, передавая их в useOverlayPosition. Внутренние слушатели resize и scroll вызывают перерасчёт координат overlay, что предотвращает смещение относительно trigger.

const {overlayProps} = useOverlayPosition({
  targetRef,
  overlayRef,
  placement: 'bottom',
  shouldFlip: true // позволяет менять позицию при недостатке места
});
  • shouldFlip — если overlay не помещается, позиция изменяется на противоположную сторону.
  • containerPadding — задаёт отступ от границ контейнера или окна для предотвращения выхода overlay за границы.

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

Overlay в React Aria не только визуально позиционируется, но и интегрируется с управлением фокусом и клавиатурными событиями. Используются атрибуты:

  • aria-labelledby и aria-describedby — для связи overlay с trigger.
  • role — определяет семантику overlay (menu, dialog, tooltip).

При использовании useOverlayTriggerState позиция overlay автоматически синхронизируется с состоянием открытия и закрытия, что обеспечивает доступность через клавиатуру и экранные читалки.


Комбинация с useOverlayTriggerState

Часто useOverlayPosition используют вместе с useOverlayTriggerState для управления видимостью overlay:

import {useOverlayTriggerState} from '@react-stately/overlays';

const state = useOverlayTriggerState({isOpen: false});
const {triggerProps, overlayProps} = useOverlay({
  isOpen: state.isOpen,
  onClose: state.close
});

Это позволяет:

  • Автоматически закрывать overlay при клике вне его области.
  • Сохранять правильное позиционирование при изменении состояния.
  • Интегрировать overlay с другими компонентами React Aria, такими как Menu или Popover.

Особенности позиционирования для разных overlay

  • Tooltip — обычно маленький, используется offset: 4–8px и top/bottom placement.
  • Popover/Menu — может быть больше, часто используют shouldFlip: true и containerPadding: 12–16px.
  • Dialog — центрируется относительно viewport, но при anchor-based позиционировании можно использовать useOverlayPosition.

Советы по оптимизации

  1. Всегда указывайте overlayRef и targetRef, чтобы React Aria корректно рассчитывал позиции.
  2. Используйте shouldFlip и containerPadding для предотвращения обрезания overlay.
  3. Для сложных случаев, где overlay может изменять размер динамически, оборачивайте overlay в ResizeObserver или используйте встроенные механизмы React Aria для пересчёта позиции.
  4. Разделяйте визуальное и семантическое управление: позиция определяется useOverlayPosition, а доступность и фокус — через useOverlayTriggerState и соответствующие aria-атрибуты.

Итоговый паттерн

Позиционирование overlay в React Aria строится на трёх столпах:

  1. Привязка к triggertargetRef.
  2. Расчёт позиции и корректировкаuseOverlayPosition, placement, offset, shouldFlip.
  3. Управление состоянием и доступностьюuseOverlayTriggerState, aria-атрибуты.

Правильное сочетание этих инструментов позволяет создавать предсказуемые, доступные и устойчивые к изменению интерфейса overlay в React-приложениях.