usePopover для поповеров

usePopover — это хук из библиотеки React Aria, предназначенный для управления поведением поповеров, всплывающих подсказок, меню и других оверлейных компонентов с учетом доступности (accessibility) и правильного взаимодействия с пользователем. Он обеспечивает корректную работу клавиатурной навигации, фокусировки и автоматическое управление атрибутами ARIA.


Инициализация хука

usePopover импортируется из пакета @react-aria/overlays и обычно используется совместно с useOverlayPosition для позиционирования:

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

function PopoverExample({ isOpen, onClose, targetRef }) {
  const popoverRef = useRef();

  const { popoverProps, underlayProps } = usePopover({
    isOpen,
    onClose,
    targetRef,
    placement: 'bottom'
  }, popoverRef);

  return isOpen ? (
    <>
      <div {...underlayProps} style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0 }} />
      <div {...popoverProps} ref={popoverRef} style={{ background: 'white', boxShadow: '0 0 10px rgba(0,0,0,0.2)' }}>
        Контент поповера
      </div>
    </>
  ) : null;
}

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

  • popoverRef — ссылка на сам поповер, необходима для управления фокусом.
  • underlayProps — свойства для подложки, которая блокирует взаимодействие с остальной страницей.
  • popoverProps — свойства, которые нужно применять к корневому элементу поповера.

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

  1. isOpen: булево значение, определяющее, открыт ли поповер.
  2. onClose: функция, вызываемая при закрытии (например, при клике вне поповера или нажатии клавиши Escape).
  3. targetRef: ссылка на элемент, к которому привязан поповер (например, кнопка, вызывающая открытие).
  4. placement: предпочтительное расположение поповера относительно цели (top, bottom, left, right, с вариациями start и end).
  5. shouldCloseOnBlur: булево значение, определяющее, будет ли поповер закрываться при потере фокуса. По умолчанию true.
  6. isNonModal: если true, поповер позволяет взаимодействовать с другими элементами страницы, не блокируя их.

Интеграция с useOverlayPosition

Для корректного позиционирования часто используется хук useOverlayPosition:

const { overlayProps, placement } = useOverlayPosition({
  targetRef,
  overlayRef: popoverRef,
  placement: 'bottom',
  offset: 8,
  crossOffset: 0,
  isOpen
});

Параметры:

  • targetRef — ссылка на элемент, относительно которого позиционируется оверлей.
  • overlayRef — ссылка на поповер.
  • placement — предпочтительное расположение.
  • offset — смещение по основной оси.
  • crossOffset — смещение по перпендикулярной оси.
  • isOpen — контролирует вычисление позиции, если поповер закрыт, позиция не вычисляется.

Результат можно объединять с popoverProps для передачи в элемент:

<div {...popoverProps} {...overlayProps} ref={popoverRef}>
  Контент
</div>

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

usePopover автоматически управляет фокусом при открытии и закрытии.

  • Фокус перемещается внутрь поповера при открытии.
  • ESC закрывает поповер.
  • Tab и Shift+Tab навигация ограничена элементами внутри, если поповер модальный.
  • Если isNonModal равен true, фокус может покидать поповер.

Для элементов управления внутри поповера рекомендуется использовать useButton или useMenuItem, чтобы клавиатурная навигация и ARIA-свойства оставались корректными.


Подложка (Underlay)

Подложка служит для блокировки взаимодействия с остальной страницей и обработки клика вне поповера:

<div {...underlayProps} style={{
  position: 'fixed',
  inset: 0,
  background: 'transparent'
}} />
  • Обычно прозрачная, чтобы не мешать визуально.
  • Обрабатывает клики и события фокуса.
  • При клике на подложку вызывается onClose.

Пример полноценного модального поповера

function ModalPopover({ isOpen, onClose, targetRef }) {
  const popoverRef = useRef();
  const { popoverProps, underlayProps } = usePopover({ isOpen, onClose, targetRef, isNonModal: false }, popoverRef);
  const { overlayProps } = useOverlayPosition({ targetRef, overlayRef: popoverRef, placement: 'bottom', offset: 8, isOpen });

  if (!isOpen) return null;

  return (
    <>
      <div {...underlayProps} />
      <div {...popoverProps} {...overlayProps} ref={popoverRef} style={{
        background: 'white',
        borderRadius: 8,
        padding: 16,
        boxShadow: '0 4px 12px rgba(0,0,0,0.15)'
      }}>
        <h2>Заголовок поповера</h2>
        <p>Подробный контент с кнопками и ссылками.</p>
        <button onCl ick={onClose}>Закрыть</button>
      </div>
    </>
  );
}

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

  • Сочетание popoverProps и overlayProps обеспечивает корректное позиционирование и управление доступностью.
  • Подложка обрабатывает клик вне поповера.
  • Использование ref обязательно для правильного фокусирования.

Рекомендации по использованию

  • Всегда передавать targetRef для корректного позиционирования.
  • Использовать underlayProps, если поповер должен быть модальным.
  • Объединять usePopover с useOverlayPosition для точного управления смещением и положением.
  • Включать ARIA-атрибуты автоматически через popoverProps, не управлять ими вручную.
  • Использовать isNonModal для всплывающих подсказок, которые не блокируют взаимодействие с остальной страницей.

Эта комбинация хуков и подходов делает usePopover мощным инструментом для создания доступных и удобных поповеров, полностью соответствующих стандартам WAI-ARIA.