Координация порталов

Radix UI предоставляет набор низкоуровневых, но гибких компонентов для построения интерфейсов в React. Одним из ключевых инструментов является портал (Portal) — механизм, позволяющий рендерить компонент в DOM вне иерархии родительского элемента. Это особенно важно для модальных окон, тултипов, поповер-меню и других всплывающих элементов, которые должны визуально выходить за пределы контейнера, сохраняя правильное позиционирование и управление фокусом.

Основы работы с порталами

В Radix UI портал реализуется через компонент <Portal>, который рендерит дочерний элемент в document.body или в указанный контейнер. Пример базового использования:

import * as Portal from '@radix-ui/react-portal';

<Portal.Root>
  <div className="modal-content">
    Содержимое модального окна
  </div>
</Portal.Root>

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

  • Root — основной контейнер портала, который обеспечивает правильное добавление в DOM.
  • Все дочерние элементы внутри <Portal.Root> рендерятся вне родительской структуры компонента.
  • Использование портала позволяет избежать CSS-проблем с overflow и z-index, обеспечивая корректное отображение всплывающих элементов.

Контроль вложенных порталов

При сложных интерфейсах часто возникает необходимость вложенных порталов. Radix UI поддерживает вложенные <Portal> без потери управления фокусом и событий:

<Portal.Root>
  <div className="tooltip">
    <Portal.Root>
      <div className="tooltip-inner">
        Вложенное содержимое
      </div>
    </Portal.Root>
  </div>
</Portal.Root>

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

  • Вложенные порталы автоматически наследуют контекст от родителя, включая стили и обработку событий.
  • Фокус и табуляция корректно работают даже при глубокой вложенности порталов.

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

По умолчанию, порталы рендерятся в document.body, но часто требуется рендеринг внутри специфического контейнера:

const container = document.getElementById('custom-root');

<Portal.Root container={container}>
  <div>Контент в кастомном контейнере</div>
</Portal.Root>

Преимущества кастомного контейнера:

  • Контроль над областью отображения портала.
  • Снижение риска конфликтов стилей и z-index.
  • Возможность интеграции порталов в специфические UI-контейнеры (например, внутри сложных модальных окон или дашбордов).

Анимации и переходы

Radix UI порталы поддерживают анимации через стандартные CSS-переходы или сторонние библиотеки анимаций, например Framer Motion. Важный аспект — анимации должны быть применены к контенту портала, а не к <Portal.Root>, поскольку сам портал является оберткой без визуального рендера:

import { motion } from 'framer-motion';

<Portal.Root>
  <motion.div
    initial={{ opacity: 0, y: -10 }}
    animate={{ opacity: 1, y: 0 }}
    exit={{ opacity: 0, y: -10 }}
  >
    Анимированное содержимое
  </motion.div>
</Portal.Root>

Лучшие практики:

  • Избегать анимаций на самом <Portal.Root>.
  • Управлять временем появления и скрытия через состояние компонента.

Фокус и управление событиями

Radix UI обеспечивает правильное управление фокусом при использовании порталов. Это критично для модальных окон и поповер-меню:

  • Фокус автоматически переводится на первый интерактивный элемент портала при открытии.
  • При закрытии портал освобождает фокус и возвращает его на элемент-триггер.
  • События клавиатуры (например, Esc для закрытия модального окна) обрабатываются корректно даже при рендеринге вне родителя.

Пример с управлением фокусом:

import * as FocusScope from '@radix-ui/react-focus-scope';

<Portal.Root>
  <FocusScope.Root>
    <button>Закрыть</button>
    <input placeholder="Введите текст" />
  </FocusScope.Root>
</Portal.Root>

Совместное использование с другими компонентами Radix UI

Порталы в Radix UI часто используются вместе с компонентами Dialog, Popover, Tooltip, обеспечивая:

  • Корректное наложение поверх всех слоев интерфейса.
  • Управление фокусом и клавиатурой.
  • Сохранение семантики HTML и доступности.

Пример интеграции с диалоговым окном:

import * as Dialog from '@radix-ui/react-dialog';

<Dialog.Root>
  <Dialog.Trigger>Открыть модальное окно</Dialog.Trigger>
  <Dialog.Overlay />
  <Portal.Root>
    <Dialog.Content>
      Содержимое диалога с порталом
    </Dialog.Content>
  </Portal.Root>
</Dialog.Root>

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

  • <Dialog.Content> можно безопасно рендерить через портал, сохраняя доступность (aria-атрибуты) и обработку клавиш.
  • Визуальные слои (overlay, content) корректно накладываются без конфликтов с другими элементами страницы.

Итоговые рекомендации по порталам Radix UI

  • Использовать <Portal.Root> для всех всплывающих элементов, которые выходят за пределы родительского контейнера.
  • Для сложных интерфейсов применять вложенные порталы и кастомные контейнеры.
  • Управлять анимациями на уровне контента, не на уровне корневого портала.
  • Интегрировать порталы с компонентами управления фокусом для обеспечения доступности.
  • Сохранять семантику и обработку событий для всех интерактивных элементов внутри портала.

Порталы Radix UI обеспечивают гибкость, масштабируемость и доступность интерфейсов, делая возможным создание сложных пользовательских элементов без потери контроля над DOM, стилями и событиями.