Menu
Випадне меню, телепортоване в body, з фліпом і притисканням до вікна.
Меню телепортується в body, а не рендериться в потоці. Це не оптимізація:
всередині контейнера з overflow: hidden або overflow-x: auto — а це будь-яка
таблиця — випадайка в потоці була б обрізана краєм контейнера.
Приклад
Обрано: —
API
Props
| Назва | Тип | Типово | Опис |
|---|---|---|---|
placement | bottom-endbottom-startbottomtopright | "bottom-end" | Куди відкривати відносно тригера. Автоматично фліпається, якщо не влазить. |
panelRole | menudialognone | "menu" | Семантика панелі: menu для команд, dialog для складених контролів. |
ariaLabel | string | undefined | Доступна назва панелі, особливо для `panelRole="dialog"`. |
width | string | — | Ширина панелі, напр. `"14rem"`. Без неї — за вмістом. |
disabled | boolean | — | — |
Події
| Назва | Payload | Опис |
|---|---|---|
update:open | [value: boolean] | Меню відкрито або закрито. |
Слоти
| Назва | Опис |
|---|---|
trigger | Тригер. `triggerAttrs` потрібно передати реальній кнопці через `v-bind`. |
content | Вміст панелі. `toggle` дає змогу закрити меню з пункту. |
Доступно через ref
| Назва | Тип | Опис |
|---|---|---|
open | () => Promise<void> | Відкрити меню програмно. |
close | (restoreFocus?: boolean) => void | Закрити меню програмно. |
Коли використовувати
Для набору дій над однією сутністю, які не варті окремих кнопок: перейменувати, дублювати, експортувати, видалити.
Тригер малюєте самі через слот — компонент не нав'язує вигляду кнопки.
Передайте triggerAttrs реальній кнопці через v-bind, щоб ARIA-стан
належав фокусованому елементу, а не wrapper-у.
Коли НЕ використовувати
Не робіть меню з вибором значення. Для цього є UiSelect — він має
role="combobox", працює з клавіатури і повідомляє обране значення.
Не ховайте в меню головну дію екрана. Те, що потрібно всім, має бути кнопкою.
Доступність
Тригер отримує aria-haspopup і aria-expanded через triggerAttrs,
панель — role="menu".
Пунктам додавайте role="menuitem" самі: компонент не знає, чим ви їх
наповните.
Стрілки, Home та End переміщують фокус між пунктами. Для складеного вмісту
на кшталт налаштувань таблиці використовуйте panelRole="dialog" і
ariaLabel, а не прикидайте форму меню-командою.
Після закриття фокус повертається на тригер. Без цього Tab починав би обхід з початку сторінки, і користувач втрачав би місце.
Escape викликає stopPropagation — інакше той самий натиск закрив би ще й
модалку, у якій меню стоїть.
Деталі реалізації
Позиція рахується власним кодом, без @floating-ui — компонент має
копіюватись у чужий проєкт без нової залежності.
Вимірювання відбувається двічі: перший раз із запасними габаритами (панелі ще немає в DOM), другий — коли вона їх отримала. Без другого проходу вибір напрямку відкриття інколи помилявся.
Слухач прокрутки навішено з capture: true. Подія scroll не спливає, тож без
цього відкрита панель лишалася б висіти на місці, поки контейнер під нею
від'їжджає.
Базовий z-index панелі — 1100; усередині глибоко вкладеного оверлея він
автоматично піднімається над шаром найближчого Modal або Drawer.