Закрытие по Escape и клику вне

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


Управление закрытием с помощью клавиши Escape

В React Aria для обработки клавиш используется хук useKeyboard или специализированные хуки, такие как useOverlay. Основная идея заключается в том, чтобы слушать события клавиатуры на уровне компонента и реагировать на конкретные клавиши.

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

import {useOverlay, usePreventScroll} from '@react-aria/overlays';
import {useRef} from 'react';

function Modal({isOpen, onClose, children}) {
  const ref = useRef();
  usePreventScroll(); // предотвращает скролл фона при открытом модальном окне

  const {overlayProps, underlayProps} = useOverlay(
    {
      isOpen,
      onClose,
      isDismissable: true, // включает закрытие по Escape и клику вне
    },
    ref
  );

  return isOpen ? (
    
{children}
) : null; }

Ключевой момент: свойство isDismissable: true автоматически добавляет обработку закрытия по клавише Escape и клику вне компонента. Это освобождает от необходимости вручную подписываться на события клавиатуры или клика.


Закрытие при клике вне элемента

Для реализации закрытия при клике вне компонента используется тот же хук useOverlay. React Aria отслеживает клики по документу и сравнивает целевой элемент с корневым элементом оверлея. Если клик произошёл вне, вызывается функция onClose.

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

  • Срабатывает только для элементов, переданных в ref.
  • Не требует ручного добавления глобальных слушателей.
  • Обеспечивает корректное управление фокусом и предотвращает взаимодействие с элементами за оверлеем.

Если нужно более тонкое управление кликами, можно комбинировать useOverlayTrigger и useOverlay для управления состоянием:

import {useOverlayTriggerState} from '@react-stately/overlays';
import {useOverlayTrigger, useOverlay} from '@react-aria/overlays';
import {useRef} from 'react';

function Dropdown() {
  const state = useOverlayTriggerState({});
  const triggerRef = useRef();
  const overlayRef = useRef();

  const {triggerProps, overlayProps} = useOverlayTrigger(
    {type: 'menu'},
    state,
    triggerRef
  );

  const {overlayProps: overlayEvents} = useOverlay(
    {isOpen: state.isOpen, onClose: state.close, isDismissable: true},
    overlayRef
  );

  return (
    <>
      
      {state.isOpen && (
        
Пункт 1
Пункт 2
)} ); }

Здесь isDismissable: true объединяет обработку Escape и кликов вне, а useOverlayTriggerState управляет состоянием открытого меню.


Работа с фокусом и доступностью

При закрытии оверлея важно вернуть фокус на элемент, вызвавший его открытие. React Aria делает это автоматически, если используется useOverlayTriggerState совместно с useOverlayTrigger. Фокусирование выполняется корректно даже при закрытии через Escape или клик вне.

Дополнительные рекомендации:

  • usePreventScroll предотвращает прокрутку фона при открытом модальном окне.
  • Использование role=“dialog” или role=“menu” вместе с оверлеем повышает доступность.
  • Поддержка keyboard dismissal и клика вне должна быть включена только там, где это логически оправдано.

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

React Aria позволяет легко сочетать оверлеи с компонентами типа Dialog, Tooltip, Menu:

  • Dialog: используется для модальных окон с фокусом внутри.
  • Tooltip: всплывающие подсказки могут закрываться при клике вне.
  • Menu: списки опций закрываются при Escape или клике по пустому пространству.

Сочетание хуков useOverlay, useOverlayTrigger, useOverlayTriggerState обеспечивает универсальный подход для всех этих компонентов.