Подменю и вложенность

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

Основы вложенных меню

В Radix UI компонент Menu построен на контролируемой модели состояния, где каждый уровень меню управляется через отдельные элементы: Menu.Root, Menu.Trigger, Menu.Content, Menu.Item, Menu.Sub, Menu.SubTrigger, Menu.SubContent.

  • Menu.Root — корневой контейнер меню, который управляет его открытием и закрытием.
  • Menu.Trigger — элемент, отвечающий за открытие меню (обычно кнопка или иконка).
  • Menu.Content — контейнер, в котором располагаются пункты меню.
  • Menu.Item — отдельный пункт меню.
  • Menu.Sub — контейнер для подменю, включающий собственный SubTrigger и SubContent.
  • Menu.SubTrigger — пункт меню, при наведении или клике открывающий подменю.
  • Menu.SubContent — контейнер подменю с вложенными элементами.

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

Создание подменю

Для создания подменю внутри основного меню используется комбинация Menu.Sub, Menu.SubTrigger и Menu.SubContent:

<Menu.Root>
  <Menu.Trigger>Открыть меню</Menu.Trigger>
  <Menu.Content>
    <Menu.Item>Пункт 1</Menu.Item>
    <Menu.Sub>
      <Menu.SubTrigger>Пункт 2 с подменю</Menu.SubTrigger>
      <Menu.SubContent>
        <Menu.Item>Подпункт 2.1</Menu.Item>
        <Menu.Item>Подпункт 2.2</Menu.Item>
      </Menu.SubContent>
    </Menu.Sub>
    <Menu.Item>Пункт 3</Menu.Item>
  </Menu.Content>
</Menu.Root>

В этой структуре:

  • Menu.SubTrigger автоматически получает стрелку, указывающую на наличие подменю.
  • Подменю появляется рядом с родительским пунктом при наведении или клике (зависит от конфигурации open и onOpenChange).
  • Вложенные пункты могут содержать свои собственные подменю, что обеспечивает многослойную навигацию.

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

Radix UI позволяет контролировать открытие и закрытие подменю через свойства open и onOpenChange:

const [openSubMenu, setOpenSubMenu] = React.useState(false);

<Menu.Sub open={openSubMenu} onOpenCha nge={setOpenSubMenu}>
  <Menu.SubTrigger>Пункт с подменю</Menu.SubTrigger>
  <Menu.SubContent>
    <Menu.Item>Подпункт 1</Menu.Item>
    <Menu.Item>Подпункт 2</Menu.Item>
  </Menu.SubContent>
</Menu.Sub>

Такой подход позволяет реализовать:

  • Анимации открытия подменю.
  • Синхронизацию состояния с внешними компонентами.
  • Программное закрытие или открытие подменю.

Клавиатурная навигация и доступность

Radix UI обеспечивает полностью доступные подменю:

  • ArrowRight открывает подменю, ArrowLeft закрывает его.
  • Enter и Space активируют элементы меню.
  • Фокус автоматически перемещается к первому элементу подменю при открытии.
  • При закрытии подменю фокус возвращается к родительскому пункту.

Стилизация подменю

Подменю и их триггеры можно стилизовать как обычные элементы Menu.Item:

.menuItem {
  padding: 8px 12px;
  cursor: pointer;
}

.subTrigger {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.subContent {
  background-color: #fff;
  border: 1px solid #ccc;
  min-width: 150px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

Дополнительно можно использовать sideOffset и alignOffset для точного позиционирования подменю относительно родительского пункта:

<Menu.SubContent sideOffset={5} alignOffset={10}>
  ...
</Menu.SubContent>

Вложенность нескольких уровней

Radix UI поддерживает бесконечную вложенность меню:

<Menu.Sub>
  <Menu.SubTrigger>Уровень 1</Menu.SubTrigger>
  <Menu.SubContent>
    <Menu.Item>Пункт A</Menu.Item>
    <Menu.Sub>
      <Menu.SubTrigger>Уровень 2</Menu.SubTrigger>
      <Menu.SubContent>
        <Menu.Item>Пункт B</Menu.Item>
        <Menu.Sub>
          <Menu.SubTrigger>Уровень 3</Menu.SubTrigger>
          <Menu.SubContent>
            <Menu.Item>Пункт C</Menu.Item>
          </Menu.SubContent>
        </Menu.Sub>
      </Menu.SubContent>
    </Menu.Sub>
  </Menu.SubContent>
</Menu.Sub>

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

Практические рекомендации

  • Для подменю с большим количеством элементов использовать виртуализацию, чтобы избежать проблем с производительностью.
  • Комбинировать sideOffset и alignOffset для корректного отображения на разных экранах.
  • Всегда проверять доступность клавиатуры, чтобы гарантировать корректное взаимодействие с экранными читалками.
  • Использовать Portal для Menu.Content и SubContent, чтобы избежать обрезания контента в контейнерах с overflow: hidden.

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