Radix UI предоставляет компоненты с высокой степенью
доступности и кастомизации, что делает работу с подменю в
интерфейсах гибкой и предсказуемой. Для построения подменю используется
компонент DropdownMenu вместе с его дочерними
элементами.
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
Подменю строится по иерархии:
DropdownMenu.Root – корневой компонент, обеспечивающий
управление состоянием.DropdownMenu.Trigger – кнопка или другой элемент,
открывающий меню.DropdownMenu.Content – контейнер, в котором
располагаются элементы меню.DropdownMenu.Item – отдельный пункт меню.DropdownMenu.Sub – обёртка для подменю, если оно
вложенное.DropdownMenu.SubTrigger – элемент, открывающий
подменю.DropdownMenu.SubContent – контент подменю.Простейший пример подменю выглядит так:
<DropdownMenu.Root>
<DropdownMenu.Trigger>Файл</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item>Создать</DropdownMenu.Item>
<DropdownMenu.Item>Открыть</DropdownMenu.Item>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>Экспорт</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.Item>PDF</DropdownMenu.Item>
<DropdownMenu.Item>SVG</DropdownMenu.Item>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
</DropdownMenu.Content>
</DropdownMenu.Root>
Здесь DropdownMenu.Sub позволяет вложить подменю внутрь
основного меню. Важно правильно использовать SubTrigger и
SubContent, чтобы обеспечить корректное отображение
вложенной структуры и правильную работу с клавиатурой.
Radix UI автоматически управляет открытием и закрытием подменю, но
при необходимости можно контролировать состояние через пропсы
open и onOpenChange:
const [open, setOpen] = useState(false);
<DropdownMenu.Root open={open} onOpenCha nge={setOpen}>
<DropdownMenu.Trigger>Меню</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item>Пункт 1</DropdownMenu.Item>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>Подменю</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.Item>Подпункт A</DropdownMenu.Item>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
</DropdownMenu.Content>
</DropdownMenu.Root>
Такой подход позволяет интегрировать подменю с внешними состояниями приложения, например, с Redux или Zustand.
DropdownMenu.SubContent поддерживает автоматическое
позиционирование относительно родительского пункта через пропсы
side, align и sideOffset:
side – указывает сторону появления (top,
bottom, left, right).align – выравнивание (start,
center, end).sideOffset – смещение от родителя в пикселях.Пример:
<DropdownMenu.SubContent side="right" align="start" sideOffset={5}>
<DropdownMenu.Item>Подпункт 1</DropdownMenu.Item>
<DropdownMenu.Item>Подпункт 2</DropdownMenu.Item>
</DropdownMenu.SubContent>
Это позволяет точно управлять внешним видом подменю и предотвращает наложение на другие элементы интерфейса.
Radix UI автоматически добавляет ARIA-атрибуты для подменю, но есть нюансы при кастомизации:
SubTrigger получает
aria-haspopup="menu" и управляет
aria-expanded.SubContent получает роль menu и
связывается с SubTrigger через
aria-labelledby.Важно не нарушать DOM-иерархию, иначе навигация клавиатурой может перестать работать корректно.
Radix UI не содержит встроенных анимаций, но интеграция с CSS или
библиотекой framer-motion проста. Пример с CSS:
.subContent {
opacity: 0;
transform: translateX(-10px);
transition: opacity 0.2s, transform 0.2s;
}
.subContent[data-state='open'] {
opacity: 1;
transform: translateX(0);
}
<DropdownMenu.SubContent className="subContent">
<DropdownMenu.Item>Подпункт с анимацией</DropdownMenu.Item>
</DropdownMenu.SubContent>
Использование data-state='open' позволяет отслеживать
состояние подменю и плавно анимировать его появление и исчезновение.
Radix UI предоставляет полностью контролируемый внешний вид, поэтому элементы можно стилизовать через класс или CSS-in-JS:
<DropdownMenu.Item className="menuItem">
Настройки
</DropdownMenu.Item>
.menuItem {
padding: 8px 16px;
cursor: pointer;
border-radius: 4px;
}
.menuItem[data-disabled] {
opacity: 0.5;
cursor: not-allowed;
}
.menuItem[data-highlighted] {
background-color: #e2e8f0;
}
Использование data-disabled и
data-highlighted позволяет реагировать на состояние
элемента без вмешательства в JS-код.
Radix UI поддерживает неограниченную вложенность
подменю. Каждое новое подменю создаётся через Sub
и правильно управляет фокусом, если структура соблюдена:
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>Экспорт</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>Форматы</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.Item>PDF</DropdownMenu.Item>
<DropdownMenu.Item>SVG</DropdownMenu.Item>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
<DropdownMenu.Item>HTML</DropdownMenu.Item>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
Соблюдение иерархии и правильное использование
SubTrigger и SubContent обеспечивает
стабильное поведение и доступность даже при сложной
структуре меню.