Эта страница переведена с английского. Читать страницу на английском
Оболочка (shell) это рамка вокруг каждого экрана: меню, выбор языка, темы и часового пояса, всплывающие уведомления (toast) и палитра команд. Приложения, работающие как настольные инструменты, добавляют строку меню, панель инструментов, инспектор свойств, строку состояния и закрепляемые панели.
Меню
Меню строится из вызовов .menu(...) у features. Пункты, которые пользователю нельзя открывать, не попадают в HTML, а feature, чьи защитные проверки (guards) отказывают пользователю, ничего не показывает. Ссылка текущего экрана имеет aria-current="page".
Оболочка сайта
Рабочие экраны открываются в оболочке приложения с боковым меню. Публичные страницы, например магазин или каталог товаров, могут вместо этого открываться в оболочке сайта: название приложения как ссылка бренда, горизонтальное меню, кнопка меню на узких экранах, необязательные действия и подвал. Её выбирает feature; экраны других features сохраняют оболочку приложения, поэтому обе живут в одном приложении.
Feature::new("shop")
.screen(shop_home)
.screen(shop_delivery)
.screen(shop_returns)
.menu(|m| {
m.group(t!("shop.menu.home"), Icon::Home)
.item(t!("shop.menu.home"), shop_home)
.group(t!("shop.menu.help"), Icon::Box)
.item(t!("shop.delivery.title"), shop_delivery)
.item(t!("shop.returns.title"), shop_returns)
})
.shell(Shell::site().header(|h| h.sign_in().language().theme()).footer(|f| {
f.column(t!("shop.menu.help"), |c| {
c.link(t!("shop.delivery.title"), shop_delivery).url(t!("shop.footer.source"), "https://example.com")
})
.text(t!("shop.footer.note"))
})
.bottom_nav([
BottomNavItem::new(t!("shop.menu.home"), shop_home).icon(Icon::Home),
BottomNavItem::new(t!("shop.menu.help"), shop_delivery).icon(Icon::Box),
]))- Меню. Верхнее меню перечисляет меню каждой feature в оболочке сайта. Группа из одного пункта это обычная ссылка; большая группа это кнопка, открывающая её ссылки (шаблон W3C APG disclosure navigation). Enter, пробел или щелчок переключают группу, стрелка вниз открывает её и переходит к первой ссылке, стрелки вверх и вниз, Home и End перемещают между её ссылками, Escape закрывает её и возвращает на кнопку, а Tab из группы или щелчок снаружи закрывает её. Стрелки влево и вправо перемещают между верхними пунктами.
- Узкие экраны. Ниже 800 px меню прячется за кнопку меню с
aria-expanded; Escape закрывает его. Без скрипта меню остаётся открытым, поэтому ничего не становится недоступным. - Действия.
sign_in()показывает посетителям ссылку входа, а вошедшим пользователям ссылку выхода, когда у приложения есть страница входа;language()иtheme()показывают те же выборы, что и оболочка приложения. - Нижняя панель.
bottom_nav([...])добавляет до пяти ссылок, закреплённых внизу экрана на телефонах (шириной 800 px и меньше), для мест, куда люди заходят чаще всего; больше пяти не компилируется. Каждый элемент являетсяBottomNavItemс подписью, экраном с маршрутом и необязательным значком. У ссылки показанной страницы естьaria-current="page"(его поддерживаетnav.js), ссылки на экраны, которые пользователь не может открыть, опускаются, панель держится в стороне от индикатора «домой» с помощьюenv(safe-area-inset-bottom), а внизу страницы остаётся место, чтобы панель ничего не закрывала. Верхнее меню остаётся для остального. Панели скрипт не нужен. - Подвал. Колонки ссылок на экраны (
link, не показываются пользователям, которым их нельзя открывать) или адреса (url: путь,https:,http:илиmailto:;App::checkотклоняет другие схемы и ссылки на незарегистрированные экраны) и строка текста.
Переход между экраном оболочки сайта и экраном оболочки приложения идёт через nav.js без загрузки страницы: частичный ответ сообщает, какой оболочке он принадлежит (x-zeyn-shell), и когда она отличается от текущей рамки, runtime заменяет всю рамку так же, как при смене языка, и загружает таблицу стилей и скрипт другой оболочки. Ссылки на /_zeyn/route/{screen} (например, у breadcrumb) обрабатываются так же.
Команды
Команда объявляется один раз в Rust со своей подписью, сочетанием клавиш и действием. Зарегистрированная в feature, она появляется в палитре, а её сочетание клавиш работает на каждой странице.
pub fn new_product() -> Command {
Command::open("stock.new_product", t!("pro.cmd_new_product"), product_editor, ProductParams { id: None })
.shortcut("Ctrl+Alt+N")
.icon(Icon::Plus)
}
pub fn restock() -> Command {
Command::action("stock.restock_low", t!("pro.cmd_restock"), restock_low, ()).shortcut("Ctrl+Alt+R")
}
Feature::new("pro").command(new_product()).command(restock())| Конструктор | Выполняет |
|---|---|
Command::open(id, label, screen, params) | Открывает экран как окно |
Command::navigate(id, label, screen, params) | Переходит на экран |
Command::effect(id, label, effect) | Эффект в браузере, например toast |
Command::action(id, label, action, input) | Действие на сервере; входные данные никогда не попадают в браузер |
App::check сообщает о сочетаниях клавиш, которые нельзя прочитать, которые используют две команды или которые палитра оставляет себе.
Палитра команд
Alt+Shift+P по умолчанию открывает палитру (App::palette_shortcut меняет его, в том числе нарочно на клавишу браузера, например Ctrl+Shift+P): это ARIA combobox, который ищет по командам приложения, панели инструментов и контекстному меню текущего экрана и экранам меню. Недавно использованные команды идут первыми.
Feature может добавить к ней поиск на сервере. По мере ввода палитра вызывает действие и перечисляет его результаты под командами; выбор одного открывает страницу на месте. Так этот сайт ищет по своим страницам:
#[action(public)]
pub async fn search_docs(query: String, docs: Docs) -> Result<Vec<SearchHit>> {
Ok(docs.search(&query))
}
Feature::new("docs").search(search_docs)Сервер выполняет каждый поиск, который пользователю разрешено выполнять, с защитными проверками feature, разрешением действия и перехватчиками (interceptors), обрезает запрос до 100 символов, отбрасывает результаты, выходящие за пределы приложения, и возвращает не более 20.
Строка меню, панель инструментов и строка состояния
MenuBar::new(t!("pro.menubar"))
.menu(t!("pro.menu_file"), |m| m.command(new_product()).separator().command(restock()))
.menu(t!("pro.menu_view"), |m| m.panels())
Toolbox::new("Tools")
.tool("select", "Select", Some(Icon::Search))
.tool("draw", "Draw", Some(Icon::Edit))
StatusBar::new("Status").left("Ready").right("UTF-8")MenuBarследует шаблону ARIA menubar и содержит те же команды, что и палитра.Toolboxэто вертикальная группа кнопок-переключателей; выбранный инструмент отправляется как событиеzeyn:tool.PropertyInspector::new(title, form)редактирует выбранный объект формой, разбитой на группы.section(...).- У
StatusBarесть живая область (live region), поэтому сообщения, отправленные в неё, читают программы чтения с экрана.
Закрепляемые панели
Dock::new(t!("pro.workspace"))
.left("explorer", t!("pro.panel_explorer"), explorer())
.center("products", t!("pro.panel_products"), products)
.bottom("output", t!("pro.panel_output"), output())
.right("properties", t!("pro.properties"), shape_inspector())Пользователи перетаскивают вкладки между областями, изменяют размер областей указателем или клавиатурой и закрывают панели; MenuList::panels в меню возвращает их. Раскладка сохраняется для каждого пользователя и экрана в хранилище представлений приложения или в браузере, если у приложения его нет.