oscript-ui
oscript-ui

Единое меню

Единое меню — одна навигация приложения на любой ширине экрана. Приложение описывает свои дороги один раз, слотом в обрамлении, а кит показывает их по месту: на широком экране — в шапке и выпадающим меню у кнопки-учётки, на телефоне — бургером и шторкой во всю высоту. Этот сайт пользуется им сам: сузьте окно, и дороги документации уедут в шторку за бургером, а страницы текущего раздела встанут в ней первой группой.

Когда использовать#

  • У приложения несколько крупных частей (дорог), и попасть в каждую нужно с любой страницы.
  • Приложение открывают и с телефона, где ряду дорог в шапке места нет.
  • У вошедшего есть личное: кабинет, уведомления с числом непрочитанного, выход.

Не берите единое меню для разделов одного объекта — это вкладки или дерево разделов на самой странице. Действия над строкой таблицы живут в меню строки: у него своя кнопка и своя панель. Обычно компонент вручную не вызывают вовсе: его печатает каркас страницы из слота обрамления.

Одна модель — три части#

Компонент возвращает не строку, а структуру из трёх фрагментов одной модели: Бургер, Учётка и Панель. Бургер и кнопка-учётка открывают одну и ту же панель; второй копии пунктов в разметке нет, поэтому разъехаться им негде.

  • Широкий экран (от 901 пикселя). Шапка выглядит как обычно: знак, дороги рядом, переключатель оформления и учётка справа. Учётка и открывает панель — компактное выпадающее меню Material у правого края. В нём только своё: блок вошедшего, дела, настройки и выход. Дороги стоят в шапке, а разделы экрана — колонкой на странице, поэтому эти группы в выпадающем меню спрятаны листом.
  • Телефон (до 900 пикселей). Ряд дорог прячется, слева появляется бургер, и панель выезжает шторкой из-за левого края, со скримом и крестом. В шторке всё: разделы экрана, дороги, дела, настройки, тема и выход. Меню на телефоне одно — второго списка на странице нет.
  • Браузер без верхнего слоя. Кнопок-открывашек нет вовсе, а панель стоит обычным блоком перед содержимым страницы: каждая дорога достижима.

Открытие, закрытие крестом, клавишей Esc и щелчком мимо, возврат фокуса — работа движка браузера по атрибутам popover и popovertarget. Скрипта в меню нет ни строки.

Порядок групп#

Группы панели стоят в одном и том же порядке на любой странице:

  1. шапка панели — знак и крест;
  2. блок вошедшего — он же ход в кабинет;
  3. разделы текущего экрана — заголовок группы — имя экрана (у дома настроек — его заголовок), внутри рубрики и пункты; текущий залит, опасный — цветом ошибки;
  4. дороги приложения (Навигация);
  5. дела вошедшего (Дела) — каждое со своим числом;
  6. настройки (Настройки) — ходы в настройки вошедшего и приложения;
  7. подвал — тема, действия приложения, выход.

Группа, которой нечего показать, не печатается, а остальные не двигаются: пункт, найденный пальцем на одной странице, на соседней стоит там же. Разделы экрана стоят первыми, потому что ради них шторку на телефоне открывают чаще всего: колонки разделов там нет.

Бургер#

Бургер — кнопка-значок узкой шапки. Сумма дел печатается на нём бейджем: ради неё на кнопку смотрят, не открывая меню. На широком экране бургер скрыт, поэтому здесь показана его разметка.

Бургер узкой шапки: сумма дел бейджем

<div class="hubmenu__opener"><button type="button" class="button button--ghost button--icon" title="Меню, ждёт внимания: 5" aria-label="Меню, ждёт внимания: 5" popovertarget="hubmenu"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"></path></svg><span class="button__count" aria-hidden="true">5</span></button></div>
Дела = Новый Массив(); Дела.Добавить(Новый Структура("Подпись, Адрес, Иконка", "Кабинет", "/кабинет", "аккаунт")); Дела.Добавить(Новый Структура("Подпись, Адрес, Иконка, Счётчик, ПодписьСчётчика", "Уведомления", "/кабинет/уведомления", "конверт", 3, "3 непрочитанных")); Дела.Добавить(Новый Структура("Подпись, Адрес, Иконка, Счётчик, ПодписьСчётчика", "Заявки", "/кабинет/заявки", "весы", 2, "2 заявки")); Меню = Набор.МенюХаба().Отрисовать(Новый Структура("Дела", Дела)); HTML = Кит.БургерМеню(Меню.Бургер).Рендер();

Счётчики#

У пункта меню — своё число: поля Счётчик и ПодписьСчётчика у дела, настройки, дороги или раздела экрана. Сумма — бейджем на бургере и на кнопке-учётке. Её кит складывает сам из числовых счётчиков дел; подпись суммы для речи берётся шаблоном словаря chrome.menu.count («ждёт внимания: %1»). Инсталляция, которая считает то, чего в списке дел нет, задаёт сумму сама — полями Счётчик и ПодписьСчётчика модели; заданная сумма старше сложенной. Ноль и пустое число бейджа не печатают вовсе. Строка вроде «99+» в сумму не входит: её не сложить.

Кнопка-учётка: сумма дел сложена китом

Дела = Новый Массив(); Дела.Добавить(Новый Структура("Подпись, Адрес, Иконка, Счётчик, ПодписьСчётчика", "Уведомления", "/кабинет/уведомления", "конверт", 3, "3 непрочитанных")); Дела.Добавить(Новый Структура("Подпись, Адрес, Иконка, Счётчик, ПодписьСчётчика", "Заявки", "/кабинет/заявки", "весы", 2, "2 заявки")); Меню = Набор.МенюХаба().Отрисовать(Новый Структура("Учётка, Дела", Новый Структура("Логин, Роль, Адрес", "а.иванова", "участник", "/кабинет"), Дела)); HTML = Кит.УчёткаМеню(Меню.Учётка).Рендер();

Ноль — ни бейджа, ни подписи

Меню = Набор.МенюХаба().Отрисовать(Новый Структура("Счётчик, Учётка", 0, Новый Структура("Логин", "а.иванова"))); HTML = Кит.УчёткаМеню(Меню.Учётка).Рендер();

Кнопка-учётка#

На широком экране панель открывает кнопка с именем вошедшего: значок, логин текстом, число дел бейджем и шеврон. Шеврон обязателен: без него кнопка неотличима от ссылки, и человек не знает, что она раскроется. Гостю кнопки нет — дороги и оформление стоят у него прямо в шапке.

Нажмите кнопку в образце выше — откроется меню этого сайта. Идентификатор панели постоянен («hubmenu»), потому что ответ страницы обязан повторяться байт в байт. Отсюда правило: меню на странице одно, и печатает его каркас. Второе, напечатанное руками, дало бы кнопку, которая открывает чужую панель.

Панель#

Панель — единственное место, где живут пункты меню. Сверху блок вошедшего — кружок с первой буквой логина, логин и роль. Дальше группы в постоянном порядке: разделы экрана, дороги, дела. В подвале — переключатель оформления и выход. Закрытая панель на странице не видна, поэтому показана её разметка.

Панель: учётка, разделы экрана с рубриками, дороги, дела, выход

<div class="hubmenu" id="hubmenu" popover><div class="hubmenu__head"><span class="hubmenu__brand"></span><span class="hubmenu__close"><button type="button" class="button button--ghost button--icon" title="Закрыть меню" aria-label="Закрыть меню" popovertarget="hubmenu"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"></path></svg></button></span></div><a href="/кабинет" class="hubmenu__account"><span class="hubmenu__avatar" aria-hidden="true">А</span><span class="hubmenu__who"><span class="hubmenu__login">а.иванова</span><span class="hubmenu__role">участник</span></span></a><nav class="hubmenu__section hubmenu__section--screen" aria-label="Настройки проекта"><p class="hubmenu__title">Настройки проекта</p><a href="/проекты/альфа/настройки" class="button button--ghost button--block" aria-current="true"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"></path></svg><span>Общее</span></a><p class="hubmenu__rubric">Доступ</p><a href="/проекты/альфа/настройки/участники" class="button button--ghost button--block"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M234-276q51-39 114-61.5T480-360q69 0 132 22.5T726-276q35-41 54.5-93T800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 59 19.5 111t54.5 93Zm246-164q-59 0-99.5-40.5T340-580q0-59 40.5-99.5T480-720q59 0 99.5 40.5T620-580q0 59-40.5 99.5T480-440Zm0 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q53 0 100-15.5t86-44.5q-39-29-86-44.5T480-280q-53 0-100 15.5T294-220q39 29 86 44.5T480-160Zm0-360q26 0 43-17t17-43q0-26-17-43t-43-17q-26 0-43 17t-17 43q0 26 17 43t43 17Zm0-60Zm0 360Z"></path></svg><span>Участники</span><span class="button__count" aria-hidden="true">2</span><span class="visually-hidden">2 заявки</span></a><a href="/проекты/альфа/настройки/ключи" class="button button--ghost button--block"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M280-400q-33 0-56.5-23.5T200-480q0-33 23.5-56.5T280-560q33 0 56.5 23.5T360-480q0 33-23.5 56.5T280-400Zm0 160q-100 0-170-70T40-480q0-100 70-170t170-70q67 0 121.5 33t86.5 87h352l120 120-180 180-80-60-80 60-85-60h-47q-32 54-86.5 87T280-240Zm0-80q56 0 98.5-34t56.5-86h125l58 41 82-61 71 55 75-75-40-40H435q-14-52-56.5-86T280-640q-66 0-113 47t-47 113q0 66 47 113t113 47Z"></path></svg><span>Ключи</span></a><a href="/проекты/альфа/настройки/удаление" class="button button--danger button--block"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"></path></svg><span>Удаление</span></a></nav><nav class="hubmenu__section hubmenu__section--roads" aria-label="Документация"><p class="hubmenu__title">Документация</p><a href="/проекты" class="button button--ghost button--block" aria-current="true"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M440-183v-274L200-596v274l240 139Zm80 0 240-139v-274L520-457v274Zm-40-343 237-137-237-137-237 137 237 137ZM160-252q-19-11-29.5-29T120-321v-318q0-22 10.5-40t29.5-29l280-161q19-11 40-11t40 11l280 161q19 11 29.5 29t10.5 40v318q0 22-10.5 40T800-252L520-91q-19 11-40 11t-40-11L160-252Zm320-228Z"></path></svg><span>Проекты</span></a><a href="/команды" class="button button--ghost button--block"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M480-400 40-640l440-240 440 240-440 240Zm0 160L63-467l84-46 333 182 333-182 84 46-417 227Zm0 160L63-307l84-46 333 182 333-182 84 46L480-80Zm0-411 273-149-273-149-273 149 273 149Zm0-149Z"></path></svg><span>Команды</span></a></nav><nav class="hubmenu__section" aria-label="Дела"><p class="hubmenu__title">Дела</p><a href="/кабинет/уведомления" class="button button--ghost button--block"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm320-280L160-640v400h640v-400L480-440Zm0-80 320-200H160l320 200ZM160-640v-80 480-400Z"></path></svg><span>Уведомления</span><span class="button__count" aria-hidden="true">3</span><span class="visually-hidden">3 непрочитанных</span></a></nav><div class="hubmenu__foot"><div class="hubmenu__theme"></div><form method="post" action="/выход" class="menu__form"><button type="submit" class="button button--ghost"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h280v80H200Zm440-160-55-58 102-102H360v-80h327L585-622l55-58 200 200-200 200Z"></path></svg><span>Выйти</span></button></form></div></div>
Разделы = Новый Массив(); Разделы.Добавить(Новый Структура("Подпись, Адрес, Иконка, Активен", "Общее", "/проекты/альфа/настройки", "шестерёнка", Истина)); Разделы.Добавить(Новый Структура("Подпись, Адрес, Иконка, Рубрика, Счётчик, ПодписьСчётчика", "Участники", "/проекты/альфа/настройки/участники", "аккаунт", "Доступ", 2, "2 заявки")); Разделы.Добавить(Новый Структура("Подпись, Адрес, Иконка, Рубрика", "Ключи", "/проекты/альфа/настройки/ключи", "ключ", "Доступ")); Разделы.Добавить(Новый Структура("Подпись, Адрес, Иконка, Опасный", "Удаление", "/проекты/альфа/настройки/удаление", "корзина", Истина)); Дороги = Новый Массив(); Дороги.Добавить(Новый Структура("Подпись, Адрес, Иконка, Активен", "Проекты", "/проекты", "куб", Истина)); Дороги.Добавить(Новый Структура("Подпись, Адрес, Иконка", "Команды", "/команды", "стопка")); Дела = Новый Массив(); Дела.Добавить(Новый Структура("Подпись, Адрес, Иконка, Счётчик, ПодписьСчётчика", "Уведомления", "/кабинет/уведомления", "конверт", 3, "3 непрочитанных")); Выход = Кит.ФормаБезТокена("/выход", Кит.Кнопка("Выйти").Вторичная().Иконка("выход")).Вид("меню"); Модель = Новый Структура( "Учётка, Разделы, ЗаголовокРазделов, Навигация, Дела, Знак, Выход", Новый Структура("Логин, Роль, Адрес", "а.иванова", "участник", "/кабинет"), Разделы, "Настройки проекта", Дороги, Дела, Кит.Ссылка("/").Вид("знак").Рендер(), Выход.Рендер()); Меню = Набор.МенюХаба().Отрисовать(Модель); HTML = Кит.ПанельМеню(Меню.Панель).Рендер();

Панель выглядит по месту. На широком экране это меню Material: строки по 48 пикселей, рубрики групп мелкой подписью над разделителем; разделы экрана, дороги и переключатель оформления там спрятаны — они уже стоят на самой странице и в шапке. На телефоне это ящик навигации Material: пункты-пилюли под палец, текущий пункт залит цветом secondary-container, рубрики разделов — ступенью ниже имени группы: мелкой подписью label-medium обычным регистром над своими разделами, левым краем с их текстом (у пунктов со значком — за значком), чтобы рубрика читалась частью группы, а не соседней группой; опасный раздел — цветом ошибки, крест на месте бургера.

Порядок разделов экрана и их рубрики решает дерево разделов — тем же правилом, что колонку дома на широком экране: рубрика над первым разделом своей группы, опасный раздел последним и без рубрики.

Заголовки групп и подпись бургера («Меню») берутся из словаря интерфейса приложения, ключи chrome.menu.*; заголовок группы разделов — имя экрана, а у экрана без имени — ключ chrome.menu.section.screen. В образцах на этой странице это словарь самого сайта документации.

Настройки#

Ходы в настройки — свои и приложения — стоят отдельной группой после дел, перед подвалом: это тоже своё, но ходят туда реже, чем к делам. Пункты той же формы, что дела: Подпись, Адрес, Иконка, Активен, Счётчик, ПодписьСчётчика. Заголовок группы — ключ словаря chrome.menu.section.settings. На широком экране группа видна в выпадающем меню, как и дела. Числа настроек в сумму на бургере не входят: сумма — это дела. Нет поля — нет группы, а панель та же байт в байт.

Панель: дела и группа настроек после них

<div class="hubmenu" id="hubmenu" popover><div class="hubmenu__head"><span class="hubmenu__brand"></span><span class="hubmenu__close"><button type="button" class="button button--ghost button--icon" title="Закрыть меню" aria-label="Закрыть меню" popovertarget="hubmenu"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"></path></svg></button></span></div><a href="/кабинет" class="hubmenu__account"><span class="hubmenu__avatar" aria-hidden="true">А</span><span class="hubmenu__who"><span class="hubmenu__login">а.иванова</span><span class="hubmenu__role">участник</span></span></a><nav class="hubmenu__section" aria-label="Дела"><p class="hubmenu__title">Дела</p><a href="/кабинет/уведомления" class="button button--ghost button--block"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm320-280L160-640v400h640v-400L480-440Zm0-80 320-200H160l320 200ZM160-640v-80 480-400Z"></path></svg><span>Уведомления</span><span class="button__count" aria-hidden="true">3</span><span class="visually-hidden">3 непрочитанных</span></a></nav><nav class="hubmenu__section" aria-label="Настройки"><p class="hubmenu__title">Настройки</p><a href="/кабинет/профиль" class="button button--ghost button--block" aria-current="true"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M234-276q51-39 114-61.5T480-360q69 0 132 22.5T726-276q35-41 54.5-93T800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 59 19.5 111t54.5 93Zm246-164q-59 0-99.5-40.5T340-580q0-59 40.5-99.5T480-720q59 0 99.5 40.5T620-580q0 59-40.5 99.5T480-440Zm0 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q53 0 100-15.5t86-44.5q-39-29-86-44.5T480-280q-53 0-100 15.5T294-220q39 29 86 44.5T480-160Zm0-360q26 0 43-17t17-43q0-26-17-43t-43-17q-26 0-43 17t-17 43q0 26 17 43t43 17Zm0-60Zm0 360Z"></path></svg><span>Профиль</span></a><a href="/кабинет/ключи" class="button button--ghost button--block"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M280-400q-33 0-56.5-23.5T200-480q0-33 23.5-56.5T280-560q33 0 56.5 23.5T360-480q0 33-23.5 56.5T280-400Zm0 160q-100 0-170-70T40-480q0-100 70-170t170-70q67 0 121.5 33t86.5 87h352l120 120-180 180-80-60-80 60-85-60h-47q-32 54-86.5 87T280-240Zm0-80q56 0 98.5-34t56.5-86h125l58 41 82-61 71 55 75-75-40-40H435q-14-52-56.5-86T280-640q-66 0-113 47t-47 113q0 66 47 113t113 47Z"></path></svg><span>Ключи доступа</span></a><a href="/настройки" class="button button--ghost button--block"><svg class="icon" viewBox="0 -960 960 960" aria-hidden="true" focusable="false" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"></path></svg><span>Приложение</span></a></nav><div class="hubmenu__foot"><div class="hubmenu__theme"></div></div></div>
Дела = Новый Массив(); Дела.Добавить(Новый Структура("Подпись, Адрес, Иконка, Счётчик, ПодписьСчётчика", "Уведомления", "/кабинет/уведомления", "конверт", 3, "3 непрочитанных")); ПунктыНастроек = Новый Массив(); ПунктыНастроек.Добавить(Новый Структура("Подпись, Адрес, Иконка, Активен", "Профиль", "/кабинет/профиль", "аккаунт", Истина)); ПунктыНастроек.Добавить(Новый Структура("Подпись, Адрес, Иконка", "Ключи доступа", "/кабинет/ключи", "ключ")); ПунктыНастроек.Добавить(Новый Структура("Подпись, Адрес, Иконка", "Приложение", "/настройки", "шестерёнка")); Меню = Набор.МенюХаба().Отрисовать(Новый Структура("Учётка, Дела, Настройки", Новый Структура("Логин, Роль, Адрес", "а.иванова", "участник", "/кабинет"), Дела, ПунктыНастроек)); HTML = Кит.ПанельМеню(Меню.Панель).Рендер();

Пустой слот#

Меню, которому нечего показать — ни дел, ни настроек, ни дорог, ни вошедшего, — даёт три пустые строки. Каркас тогда печатает прежнюю шапку целиком: кнопка, открывающая пустоту, хуже старой навигации.

Пустой слот — ни бургера, ни учётки, ни панели

этот вызов не печатает ничего

Меню = Набор.МенюХаба().Отрисовать(Новый Структура()); HTML = Меню.Бургер + Меню.Учётка + Меню.Панель;

Слот в обрамлении#

Приложение заполняет слот МенюХаба в своём обрамлении — реализации контракта «ОбрамлениеИнсталляции». В слот входят Учётка, Дела, Настройки, Навигация и Выход, а при своей сумме — Счётчик и ПодписьСчётчика. Знак и переключатель оформления добавляет каркас, а действия шапки (вход, ссылки) приходят полем Действия обрамления и повторяются в подвале панели. Активен у дороги вычисляет приложение, сравнивая свои адреса с путём запроса, — кит адресов не знает.

Разделы текущего экрана знает страница, а не приложение, поэтому они приходят четвёртым параметром Каркас.Страница: массивом — тогда группа подписана словарём, — или структурой Заголовок, Разделы, чтобы назвать группу именем экрана. Когда содержимое страницы — дом настроек, каркас берёт разделы у дома сам и называет группу его заголовком.

Каркас.Страница("Участники", Содержимое, Контекст,
	Новый Структура("Заголовок, Разделы", "Проект альфа", РазделыПроекта));

Обрамление этого сайта заполняет одну навигацию: вошедших здесь нет.

Результат.Вставить("МенюХаба", Новый Структура("Навигация", Дороги));

Приложение с вошедшим заполняет слот целиком; сумму на бургере кит сложит из чисел дел. Выход — узел холста: форма выхода с токеном сессии, собранная деревом (дев-сборка 10); прежний вид — готовый фрагмент строкой — тоже принимается.

ФормаВыхода = Кит.Форма("/выход", Кит.Кнопка("Выйти").Ссылкой().Малая().Иконка("выход"))
	.Вид("меню").Токен(Сессия.Токен);
Результат.Вставить("МенюХаба", Новый Структура("Учётка, Дела, Навигация, Выход",
	Новый Структура("Логин, Роль, Адрес", Пользователь.Логин, Пользователь.Роль, "/кабинет"),
	Дела, Дороги, ФормаВыхода));

Что проверяет кит#

Меню стоит на каждой странице приложения, поэтому ошибки модели кит ловит сразу и называет поле контракта:

  • пункт дел, настроек, дорог или разделов без Подписи — пустая строка меню, ведущая неизвестно куда;
  • Счётчик без ПодписьСчётчика — у заданной суммы и у числа пункта: скринридер услышал бы голую цифру;
  • Учётка с ролью или адресом, но без Логина — блок вошедшего не из чего подписать.

Ноль и пустой счётчик не печатаются вовсе, подпись им не нужна.

Число без подписи — исключение с объяснением

Дела = Новый Массив(); Дела.Добавить(Новый Структура("Подпись, Адрес, Счётчик", "Уведомления", "/кабинет/уведомления", 3)); Попытка HTML = Набор.МенюХаба().Отрисовать(Новый Структура("Дела", Дела)).Бургер; Исключение HTML = Набор.Тост().Отрисовать(Новый Структура("Текст, Вид", ИнформацияОбОшибке().Описание, "ошибка")); КонецПопытки;

Гайдлайны#

На широком экране дороги слота стоят в шапке рядом — тем же деревом разделов, что и в образцах ниже. Число на кнопке-учётке видно всегда.

ДелайтеДорог немного, подписи короткие: шапка в одну строку
Код
Дороги = Новый Массив();
Дороги.Добавить(Новый Структура("Подпись, Адрес, Иконка, Активен", "Проекты", "/проекты", "куб", Истина));
Дороги.Добавить(Новый Структура("Подпись, Адрес, Иконка", "Команды", "/команды", "стопка"));
Дороги.Добавить(Новый Структура("Подпись, Адрес, Иконка", "Настройки", "/настройки", "шестерёнка"));
HTML = Набор.ДеревоРазделов().Отрисовать(Новый Структура("Разделы, Ориентация, Класс",
	Дороги, "горизонталь", "tree--top"));
Не делайтеКаждый экран — дорогой: шапка переносится, глаз не находит главное
Код
Дороги = Новый Массив();
Для Каждого Подпись Из СтрРазделить("Проекты,Команды,Версии,Ключи,Журнал,Метки,Квоты,Вебхуки,Отчёты,Настройки", ",") Цикл
	Дороги.Добавить(Новый Структура("Подпись, Адрес, Активен", Подпись, "/" + Подпись, Подпись = "Проекты"));
КонецЦикла;
HTML = Набор.ДеревоРазделов().Отрисовать(Новый Структура("Разделы, Ориентация, Класс",
	Дороги, "горизонталь", "tree--top"));
ДелайтеЧисло — то, что ждёт человека: непрочитанное
Код
Меню = Набор.МенюХаба().Отрисовать(Новый Структура("Счётчик, ПодписьСчётчика, Учётка",
	3, "3 непрочитанных", Новый Структура("Логин", "а.иванова")));
HTML = Кит.УчёткаМеню(Меню.Учётка).Рендер();
Не делайтеЧисло всего подряд: бейдж, который никогда не гаснет, перестают замечать
Код
Меню = Набор.МенюХаба().Отрисовать(Новый Структура("Счётчик, ПодписьСчётчика, Учётка",
	1284, "1284 документа", Новый Структура("Логин", "а.иванова")));
HTML = Кит.УчёткаМеню(Меню.Учётка).Рендер();
ДелайтеЛогин — коротко, как человек узнаёт себя
Код
Меню = Набор.МенюХаба().Отрисовать(Новый Структура("Учётка",
	Новый Структура("Логин, Роль", "а.иванова", "администратор")));
HTML = Кит.УчёткаМеню(Меню.Учётка).Рендер();
Не делайтеДлинная почта вместо логина распирает шапку
Код
Меню = Набор.МенюХаба().Отрисовать(Новый Структура("Учётка",
	Новый Структура("Логин, Роль", "anna.ivanova.finance-department@example.com", "администратор")));
HTML = Кит.УчёткаМеню(Меню.Учётка).Рендер();

Не ставьте на странице второе меню рядом с бургером — строку чипов разделов, выпадашку «Разделы»: на телефоне разделы экрана уже стоят первой группой шторки, и два меню спорили бы друг с другом.

Доступность#

  • Бургер — кнопка-значок с aria-label «Меню»; сумма дел дописывается к имени его подписью («Меню, 3 непрочитанных»), а цифра бейджа скрыта от скринридера.
  • Кнопка-учётка называется логином; число дел прочитается подписью счётчика из скрытого текста, шеврон декоративен.
  • Связь кнопки с панелью — popovertarget: развёрнутость, Esc, щелчок мимо и возврат фокуса на кнопку после закрытия делает движок браузера.
  • Каждая группа панели — <nav> с именем своего заголовка; группа разделов названа именем экрана. Рубрика внутри группы — абзац с текстом, не пункт: в обход клавиатурой она не встаёт.
  • Текущая дорога и текущий раздел несут aria-current; опасный раздел отличается не только цветом — он последний, и подпись называет необратимое.
  • Аватар с буквой декоративен (aria-hidden), имя блока вошедшего собирается из текста: логин и роль.
  • Браузер без верхнего слоя не получает мёртвых кнопок: открывашки спрятаны, а панель стоит в потоке перед содержимым.

Модель#

МенюХаба.Отрисовать()src/Классы/компоненты/МенюХаба.os

Отрисовывает единое меню: бургер, рейку и панель — три части ОДНОЙ модели с общим
идентификатором связи.

Параметры:
 Модель - Структура - Счётчик (сумма дел на бургере и кнопке-учётке; ноль и пусто не
          печатаются; не задан — кит складывает числовые Счётчики дел сам),
          ПодписьСчётчика (чем эта сумма зовётся в речи; обязательна у заданного Счётчика,
          у сложенного без неё берётся шаблон словаря chrome.menu.count с суммой),
          Учётка (Структура: Логин, Роль, Адрес — блок вошедшего и ход в его кабинет;
          пустой Логин — блока нет), Дела (Массив: Подпись, Адрес, Иконка, Счётчик,
          ПодписьСчётчика), Настройки (Массив пунктов той же формы, что Дела, и с полем
          Активен: ходы в настройки — свои и приложения; группа идёт после дел под
          подписью словаря chrome.menu.section.settings, пусто — группы нет; на широком
          экране видна, как и дела), Навигация (Массив: Подпись, Адрес, Иконка, Активен, Счётчик,
          ПодписьСчётчика), Разделы (Массив разделов ТЕКУЩЕГО экрана той же формы
          и с полями Рубрика и Опасный, как у ДеревоРазделов; пусто — группы нет),
          ЗаголовокРазделов (имя экрана над его разделами — например, заголовок дома
          настроек; пусто — подпись словаря chrome.menu.section.screen),
          Знак, Действия, Тема, Выход (ДОВЕРЕННЫЕ фрагменты: ход на главную, действия
          инсталляции из её обрамления, форма переключателя оформления и форма выхода —
          их собирает каркас страницы). Тема и действия стоят строками подвала: на широком
          экране лист их прячет — там они живут в самой шапке.

Возвращаемое значение:
 Структура - Бургер, Учётка, Панель: три фрагмента HTML. Учётка пуста, когда вошедшего
             нет: открывать панель на широком экране гостю нечем и незачем — дороги
             и оформление стоят у него прямо в шапке. Пустое меню (ни дел, ни настроек,
             ни навигации, ни учётки) даёт три пустые строки.

МенюХаба.Пустое()src/Классы/компоненты/МенюХаба.os

Меню, которому нечего показать. Инсталляция, не заполнившая слот, получает прежнюю
шапку целиком — кнопка, открывающая пустоту, хуже старой навигации.

Параметры:
 Модель - Структура, Неопределено - модель меню, см. Отрисовать.

Возвращаемое значение:
 Булево