Тема
SDropdown
Обзор
Выпадающая панель (floating overlay) с позиционированием относительно якоря.
Usage
Открытие по клику на trigger-слот.
API
Краткий справочник props, slots и events — см. API Reference.
Guide
SDropdown позиционирует floating-панель относительно якоря из слота #trigger или внешнего reference.
- Управление открытием — через
v-model(boolean). - Слот
#triggerполучаетtoggle,open,triggerPropsдля a11y-связки с панелью. - Trigger может быть любым интерактивным элементом — кнопка, иконка, chip; компонент не привязан к
SButton. - Для hover-режима включите
showOnHover— панель открывается по наведению на trigger и content. - Общие паттерны overlay — см. Overlays.
Фокус и клавиатура
Панель телепортирована в конец body, поэтому браузерный Tab до неё не доходит — фокусом управляет сам SDropdown (WAI-ARIA menu button и немодальный dialog):
- Открытие с якоря переносит фокус в панель. С клавиатуры — на первый пункт меню, иначе на первый интерактивный элемент, а если его нет — на саму панель; мышью — на саму панель (без кольца фокуса, но стрелки,
EscapeиTabуже работают). Якорь-поле ввода (input,combobox) и открытие наведением фокус не уводят;focusOnOpen: falseотдаёт фокус потребителю (так делаетSSelect). - Закрытие, пока фокус в панели (
Escape, выбор пункта, клик снаружи по пустому месту), возвращает фокус на элемент, с которого панель открыли, — обычно на якорь. ↑↓HomeEndпереходят между пунктами: строкамиSListItemвнеSListи элементами с рольюmenuitem*.Tabс последнего элемента панели закрывает её и продолжает обход от якоря,Shift+Tabс первого — закрывает и встаёт на якорь: панель ведёт себя так, будто стоит в DOM сразу за якорем.- Панель —
role="dialog"с именем якоря (aria-labelledbyнаtriggerProps.id, без него —aria-labelякоря) или явнымpanelLabel.
Меню — проп menu: панель role="menu", строки SListItem в ней получают роль menuitem, якорь — aria-haspopup="menu"; ↓/↑ на якоре открывают меню на первом/последнем пункте, Tab закрывает меню и уходит дальше.
vue
<SDropdown v-model="isOpen" menu>
<template #trigger="{ toggle, triggerProps }">
<SButton v-bind="triggerProps" @click="toggle">Действия</SButton>
</template>
<template #content>
<SDropdownMenu :width="220">
<SListItem title="Переименовать" @click="rename" />
<SListItem title="Удалить" @click="remove" />
</SDropdownMenu>
</template>
</SDropdown>Bottom sheet на мобильных
На viewport уже sheetBreakpoint (по умолчанию 768) содержимое показывается не floating-панелью у якоря, а bottom sheet — это поведение по умолчанию. Отключается пропом sheet: false для случаев, где панель должна оставаться привязанной к якорю.
Слот #content не меняется — он получает isSheet, чтобы содержимое могло адаптироваться, и close. Заголовок шапки задаётся через sheetTitle, дополнительные элементы — слотами #sheet-header-left / #sheet-header-right.
Закрытие: свайп вниз по шапке, клик по затемнению (dismissOnClickOutside), Escape (dismissOnEscape). Пока sheet открыт, скролл страницы заблокирован, фокус переходит на панель и возвращается на trigger при закрытии. В режиме sheet showOnHover не действует.
Размеры настраиваются CSS-переменными на .s-ui-dropdown-sheet: --s-ui-dropdown-sheet-max-height (по умолчанию 80svh) и --s-ui-dropdown-sheet-overdrag.
vue
<SDropdown v-model="isOpen" sheet-title="Выберите действие">
<template #trigger="{ toggle, triggerProps }">
<button type="button" v-bind="triggerProps" @click="toggle">Menu</button>
</template>
<template #content="{ isSheet, close }">…</template>
</SDropdown>vue
<SDropdown v-model="isOpen">
<template #trigger="{ toggle, triggerProps }">
<button type="button" v-bind="triggerProps" @click="toggle">Menu</button>
</template>
<template #content>…</template>
</SDropdown>Вложенные dropdown
SDropdown внутри слота #content другого SDropdown связывается с ним автоматически. Панели телепортируются в body, но родитель знает о вложенных и не считает их «снаружи». Связь строится по дереву компонентов: вложенный SDropdown должен быть потомком родителя во Vue-дереве, положение его панели в DOM не важно.
- Клик и наведение внутри вложенной панели не закрывают родителя, в том числе при
showOnHover. - Клик по панели родителя вне вложенной закрывает только вложенную.
- Escape закрывает самую глубокую открытую панель, следующий Escape — её родителя.
- Родитель с
showOnHoverостаётся открытым, пока открыта вложенная панель.
Trigger подменю удобно делать из SListItem; для подменю по наведению включите showOnHover и placement: right-start, а block растянет обёртку trigger на ширину строки.
vue
<SDropdown v-model="isMenuOpen">
<template #trigger="{ toggle, triggerProps }">
<SButton v-bind="triggerProps" @click="toggle">Действия</SButton>
</template>
<template #content>
<SDropdownMenu :width="220">
<SListItem title="Переименовать" />
<SDropdown
v-model="isMoveOpen"
:placement="ESDropdownPlacement.RIGHT_START"
show-on-hover
block
>
<template #trigger="{ triggerProps }">
<SListItem v-bind="triggerProps" title="Переместить в…" />
</template>
<template #content>…</template>
</SDropdown>
</SDropdownMenu>
</template>
</SDropdown>Props
Со стрелкой
Placement top
Match reference width
Show on hover
Menu
Меню с клавиатуры: Enter, Space или ↓ открывают его на первом пункте, ↑/↓, Home и End — по пунктам, Escape возвращает фокус на кнопку, Tab закрывает меню и идёт дальше.
Fit viewport
Панель не помещается ни снизу, ни сверху: открывается там, где места больше, и ограничивается по высоте с прокруткой вместо выхода за край.
Bottom sheet
Содержимое как bottom sheet: закрытие свайпом вниз по шапке, кликом по затемнению или Escape.
Examples
Вложенные dropdown
Подменю по наведению и по клику внутри панели: родитель открыт, пока используется вложенная панель, Escape закрывает только самую глубокую.
API Reference
Свойства
| Имя | Тип | Описание | По умолчанию |
|---|---|---|---|
placement | ESDropdownPlacement | Предпочтительное положение панели относительно якоря. | 'bottom-start' |
offset | number | Отступ панели от якоря в пикселях. | 8 |
flip | boolean | Переворот панели при нехватке места; если не помещается ни одна сторона, берётся та, где места больше. | true |
shift | boolean | Сдвиг панели вдоль оси, чтобы оставаться в viewport (по умолчанию включён: иначе панель у края уходит за него). | true |
matchReferenceWidth | boolean | Ширина панели равна ширине якоря. | false |
fitViewport | boolean | Ограничение высоты панели свободным местом с выбранной стороны: панель прокручивается, а не уходит за край. | false |
surfaceClass | string | Дополнительный CSS-класс поверхности панели. | — |
teleportTo | string | false | Селектор контейнера для Teleport; false — без телепорта. | 'body' |
dismissOnClickOutside | boolean | Закрытие по клику вне панели и якоря; вложенные панели считаются внутри. | true |
dismissOnEscape | boolean | Закрытие по Escape; при открытой вложенной панели закрывается только она. | true |
disabled | boolean | Блокирует открытие и взаимодействие. | false |
reference | HTMLElement | null | DOM-элемент якоря; если не задан, используется слот trigger. | null |
contentId | string | id панели для aria-controls. | — |
viewportPadding | number | Внутренний отступ от краёв viewport при позиционировании. | 8 |
boundary | ESDropdownBoundary | Границы позиционирования и готовности панели: clipping-ancestors — пересечение viewport с overflow-предками якоря, viewport — только viewport (панель может выходить за скролл-контейнер якоря; скрытие при уходе якоря из его клип-границ сохраняется). | 'clipping-ancestors' |
block | boolean | Растягивает обёртку триггера на всю ширину контейнера. | false |
arrow | boolean | Показывает стрелку-указатель к якорю. | false |
showOnHover | boolean | Открывает и закрывает панель по наведению на якорь и содержимое. | false |
hoverLeaveDelay | number | Задержка перед закрытием при уходе курсора с якоря или панели, мс. | 100 |
sheet | boolean | Bottom sheet вместо floating-панели на мобильном viewport. | true |
sheetBreakpoint | number | Ширина viewport в px, ниже которой включается bottom sheet. | S_DROPDOWN_SHEET_BREAKPOINT |
sheetTitle | string | Заголовок шапки bottom sheet. | '' |
menu | boolean | Панель — меню (WAI-ARIA menu button): role="menu", строки SListItem в ней — menuitem, ↓/↑ на якоре открывают меню на первом/последнем пункте, Tab закрывает меню и идёт дальше. Без него панель — немодальный dialog, названный своим якорем. | false |
focusOnOpen | boolean | Переносить фокус в панель при открытии с якоря: с клавиатуры — на первый пункт меню, иначе на первый интерактивный элемент или на саму панель; мышью — на саму панель. Якорь-поле ввода (combobox, input) фокус держит всегда; false — фокусом панели управляет потребитель. | true |
panelLabel | string | Доступное имя панели; по умолчанию панель называется якорем (aria-labelledby). | — |
modelValue / v-model | boolean | Двустороннее значение компонента (v-model). | false |
Слоты
| Имя | Описание |
|---|---|
trigger | Якорь открытия: open, toggle, close, isSheet, triggerProps для a11y |
sheet-header-left | Область шапки bottom sheet слева от заголовка |
sheet-header-right | Область шапки bottom sheet справа от заголовка |
content | Содержимое панели; resolvedPlacement — итоговая позиция, isSheet — режим bottom sheet |