メニュー
ボタンで開く、操作のメニューです。
実行結果
menu.rs
use hypersig::html::{div, p};
use hypersig::style::pattern::stack;
use hypersig::style::value::rem;
use hypersig::{Page, Signals};
use hypersig_ui::{ButtonSize, ButtonVariant, Menu, MenuItem};
use serde::{Deserialize, Serialize};
#[derive(Signals, Serialize, Deserialize)]
pub struct Project {
archived: bool,
status: String,
}
const ACTIONS: Menu<Project> = Menu::new("actions");
pub fn page() -> Page<Project> {
let archived = Project::archived();
let status = Project::status();
let initial = Project {
archived: false,
status: String::new(),
};
Page::new(
"プロジェクト",
initial,
)
.child(
div()
.css(stack(rem(1)))
.child(
div()
.child(
ACTIONS
.trigger(ButtonVariant::Outline, ButtonSize::Default)
.text("プロジェクトの操作"),
)
.child(
ACTIONS.menu([
MenuItem::new(
"名前を変更",
status.set("プロジェクトの名前を変更しました。"),
),
MenuItem::new(
"複製",
status.set("プロジェクトを複製しました。"),
),
MenuItem::new(
"アーカイブ",
archived.set(true).then(status.set("プロジェクトをアーカイブしました。")),
)
.disabled(archived),
MenuItem::new(
"削除",
status.set("プロジェクトを削除しました。"),
)
.destructive(),
]),
),
)
.child(p().attr("role", "status").bind_text(status)),
)
}アクセシビリティ
WAI-ARIA menu button パターンに従います。ボタンは aria-haspopup="menu" を、メニューは role="menu" を、各操作は role="menuitem" を持ちます。メニューは popover なので、ブラウザがページの上に重ねて表示し、Escape か外側のクリックで閉じます。メニューは CSS の anchor positioning でボタンの下に置かれ、下に場所がなければボタンの上に移ります。
無効な操作はその位置に残り、矢印キーでは飛ばされます。
| キー | 動作 |
|---|---|
| Enter、Space | ボタンにフォーカスがあるときに押すと、メニューを開いて最初の操作にフォーカスを移します。操作にフォーカスがあるときに押すと、その操作を実行してメニューを閉じます。 |
| 上矢印、下矢印 | 前か次の操作に移ります。 |
| Home、End | 最初か最後の操作に移ります。 |
| Escape | メニューを閉じ、ボタンにフォーカスを戻します。 |
| Tab | メニューを閉じます。 |
API
| 項目 | 概要 |
|---|---|
Menu::new | id が name のメニュー |
trigger | メニューを開閉するボタン |
menu | items を順に並べたメニュー |
MenuItem::new | label をラベルに持ち、選ばれると run を実行する操作 |
destructive | 操作を token::destructive の色で描きます。削除する操作に使います。 |
disabled | when が true の間、操作を無効にします。無効な操作は矢印キーで飛ばされます。 |