Context Menu
<sf-context-menu> — панель с коротким списком действий над объектом:
страницей, строкой таблицы, файлом, профилем. Он оформлен как обычное
контекстное меню и даёт пунктам
роль меню и управление клавиатурой. Пункты задаются данными, а метод
openFor открывает меню у кнопки и дальше управляет им сам; для выбора
значения в форме берите Dropdown.
Пример
Применение
Стандартное меню
Любое меню действий строится одинаково: пункты передаются данными в
свойстве entries, атрибут popup прячет меню до открытия, а
toggleFor(кнопка) открывает его у кнопки или закрывает, если оно уже
открыто у неё. Выбор пункта приходит событием sf-context-menu-select.
Пункты данными
entries — массив записей; его задают свойством или JSON в атрибуте. Если
entries задан, он заменяет items и пункты из разметки. Запись без type
— пункт меню:
idвозвращается в событии выбора,text— подпись;icon— имя иконки Material Symbols перед подписью,caption— вторая строка под подписью;- в конце строки стоят
value— текущее значение, например выбранный язык, иshortcut— сочетание клавиш; оба только показываются; kind: 'check'илиkind: 'radio'делает пункт отмечаемым,checked— его отметка;submenu— массив записей, который открывается сбоку от пункта;dangerотмечает опасное действие,disabled— недоступное,current— место, где человек находится;hrefделает пункт ссылкой,lang— язык, на котором написана подпись;avatar— адрес картинки в начале строки вместо иконки, например логотип сайта или фото человека; без картинки там рисуются инициалыinitials.
Другие записи задаются полем type: {type: 'separator'} — разделитель,
{type: 'heading', text} — заголовок группы пунктов. Если иконка есть хотя
бы у одного пункта, место под иконку остаётся у всех, и подписи начинаются
на одной линии.
Открытие у кнопки
openFor(кнопка, options) открывает меню в верхнем слое страницы: его не
обрезает прокручиваемый блок и не перекрывает таблица. Меню встаёт под
кнопкой по её началу, а у края окна переворачивается вверх или
сдвигается; если окно ниже меню, его строки прокручиваются. Подменю
открывается сбоку, и его первый пункт стоит на одной линии с пунктом,
который его открыл. Ширина меню подстраивается под самый длинный пункт, от
нижней границы --sf-e8 до верхней --sf-g2; меню, которому нужно шире,
задаёт переменную --sf-context-menu--min-inline-size.
options уточняет открытие: side — сторона кнопки (block-end по
умолчанию, block-start, inline-start, inline-end), align —
выравнивание по ней (start по умолчанию, center, end), focus — куда
встаёт фокус: на первый пункт (first, по умолчанию), на последний
(last) или никуда (none). toggleFor(кнопка, options) закрывает меню,
открытое у этой кнопки, а иначе открывает его; close() закрывает меню
вместе с подменю.
Меню, открытое через openFor, управляет собой само: Escape закрывает
текущий уровень, а Tab, нажатие вне меню и выбор пункта закрывают всё
меню и возвращают фокус туда, где он был до открытия, а если фокуса нигде
не было — на кнопку, у которой меню открывалось. Открытие и закрытие
сообщают события sf-context-menu-open и sf-context-menu-close.
openFor и toggleFor возвращают Promise<boolean>: дождитесь его, если
следующий шаг зависит от того, открылось ли меню.
Выбор пункта
Выбор на любом уровне, в том числе в подменю, приходит одним событием
sf-context-menu-select на самом меню. В event.detail — id и запись
entry; для отмечаемого пункта checked — отметка, которую пункт просит
поставить, для обычного — null. После выбора меню закрывается. Чтобы оно
осталось открытым, например в списке с несколькими отметками, отмените
событие через preventDefault() или задайте пункту keepOpen: true.
Отметки check и radio хранит приложение: получив событие, оно
записывает новое checked и значение value в entries, как в примере
выше.
Меню профиля
Меню профиля собирается из тех же записей и ещё трёх типов строк.
{type: 'header'} показывает, о ком меню: имя в text, строки под ним в
caption (строка или массив строк) и фото avatar; без фото рисуются
инициалы из имени или из initials. {type: 'choice'} ставит несколько
вариантов options рядом, например тему: выбранный вариант отмечается
сразу, меню остаётся открытым, а событие несёт id варианта и group —
id строки. keepOpen: false на строке выбора закрывает меню после
выбора. {type: 'note'} — пояснение мелким текстом.
Пункт с search: {placeholder, empty} открывает подменю с полем поиска: оно
сужает пункты по подписи, второй строке и словам keywords, а empty —
текст, когда ничего не найдено. Такое подменю открывается сразу на поле, и
мышью, и с клавиатуры.
Поиск на стороне хоста
Если пунктов слишком много, чтобы передать их все, на поиск отвечает хост.
Свойство search самого меню ставит поле над строками, а
search: {remote: true} отключает поиск по пунктам: на каждый запрос меню
поднимает событие sf-context-menu-search с detail.query, а хост
записывает найденные пункты в entries. Для подменю то же задаёт
search: {remote: true} в записи пункта. Закрытие меню запроса не
поднимает.
menu.search = {placeholder: 'Найти сайт', empty: 'Ничего не найдено', remote: true};
menu.addEventListener('sf-context-menu-search', async (event) => {
menu.entries = await findSites(event.detail.query);
});
Пункты в разметке
Меню без entries показывает пункты, переданные со slot="content":
<sf-button>, нативные <button> и <a href>. Обычно берут <sf-button>
вида link со схемой on-surface и размером 1/2; держите в одном меню
пункты одного размера. Меню выравнивает подписи по началу строки.
Пункты можно передать и данными в свойстве items: каждый элемент массива —
{component, props}. Для component: 'sf-button' в props читаются
text, type (вид кнопки), scheme, size, iconLeft, iconRight,
ariaLabel, disabled и обработчик '@click'. text без пунктов
показывает в меню простой текст.
Меню, которым управляет страница
Меню без popup рисуется там, где стоит в разметке, и не открывается и не
закрывается само. Показывает его страница — по нажатию кнопки или правой
кнопке мыши — и после показа переводит в него фокус методом focus(),
дождавшись отрисовки. По Escape и после выбора пункта страница скрывает
меню и возвращает фокус на кнопку, которая его открыла. Действие пункта —
обычный click на кнопке. Скрывайте обёртку меню: сам <sf-context-menu>
не занимает места в раскладке, и атрибут hidden на нём не действует.
const layer = document.querySelector('#file-menu-layer');
const menu = layer.querySelector('sf-context-menu');
const opener = document.querySelector('#file-menu-button');
opener.addEventListener('click', async () => {
layer.hidden = false;
opener.setAttribute('aria-expanded', 'true');
await menu.whenRendered();
menu.focus();
});
layer.addEventListener('keydown', (event) => {
if (event.key !== 'Escape') return;
layer.hidden = true;
opener.setAttribute('aria-expanded', 'false');
opener.focus();
});
Размер, ширина и подвал
Меню бывает двух размеров: size="1/2" (по умолчанию) и size="1". Строки
меню высотой с поле того же размера; от размера зависят текст, иконки,
отступы, поиск и подвал. Подменю берёт размер родительского меню.
Ширину задаёт атрибут width или параметр openFor(кнопка, {width}):
content(по умолчанию) — по самой длинной строке, в пределах--sf-context-menu--min-inline-sizeи--sf-context-menu--max-inline-size;trigger— точно по кнопке, которая открыла меню, как у выбора сайта или меню профиля; длинная строка обрезается многоточием;- длина CSS, например
16rem.
Окно с собственным содержимым (slot="content") собирается из тех же частей:
.sf-context-menu-top для поиска или заголовка, .sf-context-menu-body для
прокручиваемого списка и подвал в slot="footer" с кнопками вроде
«Сбросить» и «Применить». Подвал стоит под строками и остаётся на месте, пока
список прокручивается; его кнопки делят ширину поровну.
<sf-context-menu popup size="1/2" aria-label="Настройки">
<div slot="content" class="sf-context-menu-body">…</div>
<sf-button slot="footer" size="1/2" appearance="outline" scheme="on-surface" text="Сбросить"></sf-button>
<sf-button slot="footer" size="1/2" text="Применить"></sf-button>
</sf-context-menu>
Панель с вкладками
Окно с вкладками — настройки списка, фильтр — собирается тем же меню.
tabs задаёт вкладки: подписи через |, например "Поля|Вид", или свойство
[{id, label}]. active-tab — id или номер выбранной вкладки, closable
добавляет в шапку кнопку закрытия.
Шапка — отдельная строка высотой с контрол и шагом отступа над ним: вкладки
стоят по её нижнему краю, кнопка закрытия — по центру. Под шапкой одна линия
через всю панель, в том числе под кнопкой закрытия; своей линии у вкладок нет.
Шапка не прокручивается: прокручивается только
содержимое слота content, а подвал в слоте footer остаётся внизу. Ширина у
панели своя — восемь контролов или значение width, — поэтому панель не
меняет размер при смене вкладки или подписи кнопки в подвале.
Выбор вкладки отправляет событие sf-context-menu-tab с {id, index}, и
страница кладёт в content строки этой вкладки. Кнопка закрытия закрывает
меню, открытое через openFor; если меню поставила страница, оно отправляет
sf-context-menu-close, и закрывает его страница. Строка панели —
.sf-context-menu-row: в ней может быть ручка, флажок или кнопка, а подпись
.sf-context-menu-row-text занимает остаток и обрезается многоточием.
Пример панели с вкладками
Доступность
- Меню нужно доступное имя:
aria-labelна<sf-context-menu>. Подменю получает имя по подписи пункта, который его открыл. - Пункты из
entriesполучают ролиmenuitem,menuitemcheckboxилиmenuitemradioсaria-checked; пункт с подменю —aria-haspopupиaria-expanded. Заголовок профиля и пояснение — обычный текст, варианты строки выбора —menuitemradioв группе. - В порядке
Tabостаётся один пункт. Стрелки вверх и вниз переходят по пунктам по кругу,HomeиEnd— к первому и последнему,EnterиSpaceвыбирают пункт. Недоступные и скрытые пункты пропускаются. - В меню, открытом через
openFor,→открывает подменю,←иEscapeзакрывают текущий уровень,Tabзакрывает всё меню. Буквы переводят фокус к следующему пункту, подпись которого с них начинается. Варианты строки выбора перебирают стрелки влево и вправо. - В подменю с поиском фокус стоит в поле: буквы и боковые стрелки остаются
полю,
↓переходит к пунктам. - Кнопка, у которой открыто меню, получает
aria-haspopup="menu"иaria-expanded. Меню, которым управляет страница, сообщает состояние черезaria-expandedна кнопке силами страницы. - Атрибут
roleсо значением, отличным отmenu, отключает клавиатурную модель меню: задавайте его, только если пункты — не команды.
Справочник API
Раздел собран из манифеста компонента выпуска ui-106d5901f8c3 и обновляется вместе с выпуском. Чтобы исправить описание, исправьте манифест.
Каждому атрибуту соответствует свойство элемента с тем же именем в camelCase: search-placeholder — searchPlaceholder. Свойство атрибута template называется templateName.
| Тег | <sf-context-menu> |
| Назначение | Меню действий Framework: пункты данными со значком, подписью, значением, сочетанием клавиш, отметкой, подменю, разделителем и заголовком; openFor() открывает его в верхнем слое с привязкой, клавиатурой, переходом по первой букве и возвратом фокуса. Хост может по-прежнему рисовать свои пункты и сам управлять жизненным циклом. |
| Контракт | smart.context-menu, версия 1.3.0 |
| Поставка | выпуск Framework ui-106d5901f8c3 |
| Жизненный цикл | экспериментальный: API может меняться между выпусками |
| Готовность | описан: манифест и сценарий есть, продуктовой приёмки нет |
| Заготовка до прихода кода | нет: атрибут data-sf-skeleton не пишется; своего скелетона при загрузке данных нет (Заготовка до прихода кода) |
Атрибуты
template—string, по умолчаниюdefault.position—string, по умолчанию пусто, значения: пусто,inline-start,inline-start-top,inline-start-bottom,inline-end,inline-end-top,inline-end-bottom,top,top-inline-start,top-inline-end,bottom,bottom-inline-start,bottom-inline-end,start,end,top-start,top-end,bottom-start,bottom-end,left,left-top,left-bottom,right,right-top,right-bottom,top-left,top-center,top-right,bottom-left,bottom-center,bottom-right,left-start,left-end,right-start,right-end,inline-start-start,inline-start-end,inline-end-start,inline-end-end. Устаревший: у меню нет хвоста; значение сохранено ради его класса и ничего не рисует.tooltip—boolean, по умолчаниюtrue. Устаревший: у меню нет хвоста; значение сохранено ради его класса и ничего не рисует.text—string, по умолчанию пусто.aria-label—string, по умолчанию пусто.items—arrayизobject, по умолчанию[].role—string, по умолчаниюmenu.content-class—string, по умолчанию пусто.root-class—string, по умолчанию пусто.entries—arrayизobject, по умолчанию[]. Меню данными (свойством или JSON в атрибуте). См. item_contract. Если задано, заменяет items и слот содержимого.popup—boolean, по умолчаниюfalse. Не рисуется, пока его не откроет openFor(). Меню без этого атрибута рисуется на месте.search—object, по умолчаниюnull. {placeholder, empty, remote}: поле поиска над строками (задаётся свойством). По умолчанию поиск локальный; с remote: true меню поднимает sf-context-menu-search {query} и показывает пункты, которые задаёт хост. Пункт подменю передаёт его черезsearch.size—string, по умолчанию1/2, значения:1/2,1. Размер меню: строки высотой с поле того же размера, текст, отступы, поиск и подвал. Подменю берёт размер родительского меню.width—string, по умолчаниюcontent. Какой ширины openFor() открывает меню: content — по самой длинной строке, в пределах –sf-context-menu–min-inline-size и –sf-context-menu–max-inline-size; trigger — точно по элементу, который его открыл (длинная строка обрезается многоточием); или длина CSS. openFor(anchor, {width}) переопределяет значение.tabs—string|array, по умолчанию пусто. Панель с вкладками: подписи через | или свойство [{id, label}]. Шапка (вкладки и кнопка закрытия) и подвал не прокручиваются, прокручивается тело; ширина панели постоянна (ступень лестницы размеров или width) и не зависит от содержимого вкладки.active-tab—string, по умолчанию пусто. id или номер выбранной вкладки; хост выводит строки этой вкладки в слот content.closable—boolean, по умолчаниюfalse. Кнопка закрытия в конце шапки панели.
Прежние имена:
| Имя | Прежнее имя | Состояние | Убирается после |
|---|---|---|---|
position="inline-start" |
position="start" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="inline-end" |
position="end" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="top-inline-start" |
position="top-start" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="top-inline-end" |
position="top-end" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="bottom-inline-start" |
position="bottom-start" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="bottom-inline-end" |
position="bottom-end" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="left-top" |
position="left-start" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="left-bottom" |
position="left-end" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="right-top" |
position="right-start" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="right-bottom" |
position="right-end" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="inline-start-top" |
position="inline-start-start" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="inline-start-bottom" |
position="inline-start-end" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="inline-end-top" |
position="inline-end-start" |
работает, отмечается в SF.deprecations |
2026-12-01 |
position="inline-end-bottom" |
position="inline-end-end" |
работает, отмечается в SF.deprecations |
2026-12-01 |
Методы
focus(options?: object)— возвращаетnull. Переводит фокус на текущий доступный пункт. Хост, который рисует меню сам, ждёт отрисовки и сам отвечает за открытие и закрытие.openFor(anchor: Element, options?: object)— возвращаетPromise<boolean>. Открыть рядом с элементом: в верхнем слое, с привязкой через SF.Position с переворотом и сдвигом, фокус на первом пункте (или ‘last’, или ‘none’). Дальше меню управляет собой само: Escape, Tab, нажатие снаружи и выбор пункта закрывают его и возвращают фокус — туда, где он был, или на элемент управления якоря, если фокуса нигде не было.toggleFor(anchor: Element, options?: object)— возвращаетPromise<boolean>. Закрывает меню, если оно уже открыто у этого элемента, иначе вызывает openFor().close(options?: object)— возвращаетboolean. Закрывает это меню и все подменю.chooseTab(index: number)— возвращаетobject. Выбирает вкладку по номеру и отправляет sf-context-menu-tab.requestClose()— возвращаетboolean. Закрывает меню, открытое самим компонентом; для меню, которое поставил хост, отправляет sf-context-menu-close, чтобы его закрыл хост.
События
Все события приходят на сам элемент и всплывают. Общие события базового элемента перечислены в главе Жизненный цикл → Этапы.
sf-context-menu-select—detail:{ id?: string|null, entry?: object, checked?: boolean|null, group?: string|null }. Выбран пункт на любом уровне. Отмените событие, чтобы меню осталось открытым.sf-context-menu-open—detail:null. Открыто через openFor(). Всплывает и с composed: true; detail не передаётся.sf-context-menu-close—detail:null. Меню, открытое через openFor(), закрыто. Всплывает и с composed: true; detail не передаётся.sf-context-menu-search—detail:{ query?: string }. Запрос поиска, на который отвечает хост (search.remote).sf-context-menu-tab—detail:{ id?: string, index?: number }. Выбрана вкладка панели; хост показывает её строки.
Доступность
- Роль: меню (
menu). - Доступное имя: обязательно.
- Клавиши:
ArrowDown,ArrowUp,Home,End,ArrowRight,ArrowLeft,Enter,Space,Escape,Tab, поиск по первым буквам. - Фокус: Строки из entries получают роли menuitem, menuitemcheckbox или menuitemradio с aria-checked, а подменю — aria-haspopup и aria-expanded; одна перемещаемая остановка Tab. При открытии через openFor() элемент получает aria-haspopup=menu и aria-expanded; Escape закрывает один уровень и возвращает фокус на его строку или элемент; Tab закрывает всю цепочку; буква переводит к следующему пункту, который с неё начинается. Хост, который рисует меню сам, сам отвечает за открытие, закрытие по Escape и Tab и возврат фокуса. Заголовок и заметка — текст. Варианты строки выбора — menuitemradio в группе, их перебирают боковые стрелки. Меню с поиском открывается на поле поиска, которое оставляет себе буквы и боковые стрелки; ArrowDown переходит к строкам.
Минимальная разметка
<button id="more" type="button">Ещё</button><sf-context-menu id="menu" popup aria-label="Действия"></sf-context-menu><script>menu.entries=[{id:"rename",text:"Переименовать",icon:"edit"},{type:"separator"},{id:"delete",text:"Удалить",icon:"delete",danger:true}];more.onclick=()=>menu.toggleFor(more);</script>
Источник
simai/ui@106d5901f8c3b4f23131ce0e7ede5dbd8abfe4f4:distr/smart/context-menusimai/ui@106d5901f8c3b4f23131ce0e7ede5dbd8abfe4f4:distr/rule/rule.json#name=cl-context-menu