Структура контекстного меню

Radix UI предоставляет мощный набор компонентов для создания контекстных меню в React-приложениях, которые полностью управляются состоянием и доступны для пользователей с различными потребностями (accessibility-first). Основной модуль для работы с контекстными меню — это @radix-ui/react-context-menu.

Контекстное меню состоит из нескольких ключевых элементов: корневого контейнера, триггера, контента и пунктов меню.

import * as ContextMenu from '@radix-ui/react-context-menu';
  • ContextMenu.Root — корневой компонент, который оборачивает все элементы контекстного меню. Он управляет состоянием открытия/закрытия и обеспечивает корректное взаимодействие с пользователем.
  • ContextMenu.Trigger — элемент, с которого вызывается меню, обычно это правый клик или определённое действие.
  • ContextMenu.Content — визуальный контейнер для пунктов меню. Поддерживает стилизацию и позиционирование с помощью CSS или Tailwind.
  • ContextMenu.Item — отдельный пункт меню. Поддерживает события onSelect, управление фокусом и состояния disabled и highlighted.
  • ContextMenu.Separator — визуальный разделитель между группами пунктов.
  • ContextMenu.Sub и ContextMenu.SubContent — для вложенных подменю.

Создание базового контекстного меню

Простейшая структура меню выглядит следующим образом:

<ContextMenu.Root>
  <ContextMenu.Trigger>
    <div className="box">Клик правой кнопкой</div>
  </ContextMenu.Trigger>

  <ContextMenu.Content className="menu">
    <ContextMenu.Item onSel ect={() => console.log('Скопировано')}>Копировать</ContextMenu.Item>
    <ContextMenu.Item onSel ect={() => console.log('Вставлено')}>Вставить</ContextMenu.Item>
    <ContextMenu.Separator />
    <ContextMenu.Item disabled>Удалить</ContextMenu.Item>
  </ContextMenu.Content>
</ContextMenu.Root>

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

  1. ContextMenu.Trigger может быть любым React-элементом. Меню будет отображаться при взаимодействии с этим элементом.
  2. ContextMenu.Content поддерживает позиционирование через props side (top, right, bottom, left) и align (start, center, end).
  3. Каждый ContextMenu.Item может быть интерактивным или неактивным (disabled). При использовании onSelect можно привязать любую функцию.

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

Radix UI использует внутреннее управление состоянием, что упрощает работу с фокусом и доступностью. Дополнительно можно контролировать состояние через prop open:

const [open, setOpen] = React.useState(false);

<ContextMenu.Root open={open} onOpenCha nge={setOpen}>
  <ContextMenu.Trigger>Меню</ContextMenu.Trigger>
  <ContextMenu.Content>
    <ContextMenu.Item onSel ect={() => setOpen(false)}>Закрыть</ContextMenu.Item>
  </ContextMenu.Content>
</ContextMenu.Root>

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


Вложенные подменю

Radix UI поддерживает создание многоуровневых контекстных меню с помощью ContextMenu.Sub и ContextMenu.SubContent.

<ContextMenu.Item>
  Дополнительно
  <ContextMenu.Sub>
    <ContextMenu.SubTrigger>Открыть подменю</ContextMenu.SubTrigger>
    <ContextMenu.SubContent>
      <ContextMenu.Item onSel ect={() => console.log('Подпункт 1')}>Подпункт 1</ContextMenu.Item>
      <ContextMenu.Item onSel ect={() => console.log('Подпункт 2')}>Подпункт 2</ContextMenu.Item>
    </ContextMenu.SubContent>
  </ContextMenu.Sub>
</ContextMenu.Item>

Особенности вложенных меню:

  • SubTrigger — пункт, при наведении или клике на который открывается подменю.
  • SubContent — контейнер для подменю с возможностью полного контроля позиционирования.
  • Подменю автоматически управляет фокусом и закрытием при клике вне элемента.

Стилизация и анимации

Radix UI не накладывает стили по умолчанию, предоставляя полную свободу кастомизации:

.menu {
  background-color: white;
  border: 1px solid #ccc;
  border-radius: 6px;
  padding: 5px;
  min-width: 180px;
}

.menu-item {
  padding: 5px 10px;
  cursor: pointer;
}

.menu-item:hover {
  background-color: #f0f0f0;
}

Для анимации открытия можно использовать CSS-переходы:

.menu[data-state='open'] {
  animation: fadeIn 150ms ease-out;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(-5px); }
  to { opacity: 1; transform: translateY(0); }
}

Доступность и клавиатурная навигация

Radix UI автоматически обрабатывает:

  • Фокус на первом элементе при открытии.
  • Переключение стрелками вверх/вниз.
  • Закрытие при Escape.
  • Поддержку атрибутов aria-disabled и aria-haspopup для подменю.

Это позволяет создавать контекстные меню, соответствующие WCAG без дополнительной реализации логики взаимодействия с клавиатурой.


Продвинутые возможности

  1. Custom Items — любой элемент можно использовать как ContextMenu.Item, включая компоненты с иконками, чекбоксы или переключатели.
  2. PortalsContextMenu.Content можно рендерить через портал для управления z-index и расположением вне основного DOM.
  3. Controlled Focus — можно программно управлять фокусом через ref и методы focus().
  4. Event Hooks — события onOpenChange, onSelect, onEscapeKeyDown позволяют интегрировать меню с другими компонентами приложения.

Эта структура позволяет создавать гибкие, доступные и полностью настраиваемые контекстные меню, которые легко интегрируются в современные React-приложения и поддерживают любые UI-требования.