Menubar
複数の Menu を水平(または垂直)に並べたアプリケーションメニューバーです。fandhe-frontend-headless-ui の menubar mod は Root / Menu / Trigger / Positioner / Content / Arrow / ArrowTip / Item / ItemText / ItemIndicator / ItemGroup / ItemGroupLabel / Separator / SubTrigger / SubContent / CheckboxItem / RadioItemGroup / RadioItem の 18 anatomy パーツを提供する unstyled 部品であり、Themes 版と異なりスタイル(CSS)は一切持ちません(イシュー #1652 で Radix Primitives Menubar と参照突合し、11 → 18 パーツへ拡充しました)。開いている Menu を跨いだ左右移動の状態遷移 API(MenubarAction)を持ちます。矢印キー・Home/End・typeahead の実 DOM 配線は fandhe-frontend-wasm-full が担います。
キーボード操作
| キー | 対象 | 効果 |
|---|---|---|
| ArrowRight / ArrowLeft(垂直配置は ArrowDown / ArrowUp) | Trigger | 隣の Trigger へフォーカスを移動します。ある Menu が開いていれば、開く Menu もフォーカス移動に追随します(open-follows-focus)。 |
| ArrowDown / Enter / Space | Trigger(closed) | Menu を開き、先頭の非 disabled 項目を highlight します。 |
| ArrowUp | Trigger(closed) | Menu を開き、末尾の非 disabled 項目を highlight します。 |
| ArrowDown / ArrowUp / Home / End | Content(open) | highlight 中の項目を次/前/先頭/末尾の非 disabled 項目へ移動します(CheckboxItem/RadioItem も対象)。 |
| Enter / Space | Content(open、highlight 中の項目) | highlight 中の項目へ click を合成します。CheckboxItem は "toggle"、RadioItem は "select" を dispatch します(checked 状態機械は menu の MenuCheckboxItem/MenuRadioItemGroup を流用)。 |
| 印字可能文字 | Content(open) | typeahead。ItemText 子があればそのテキストを優先してマッチします。 |
| ArrowRight | SubTrigger(highlight 中、水平配置) | サブメニューを展開します。 |
| ArrowLeft | サブメニュー内(水平配置) | 親 SubTrigger へ復帰しサブメニューを閉じます。 |
| Escape | — | 開いている Menu を閉じます。フォーカスは元々 Trigger から離れないため、フォーカス復帰は構造的に成立します。 |
| Tab / Shift+Tab | — | 配線はありません(ブラウザ既定)。roving tabindex により tabindex="0" の Trigger のみが Tab 順序に含まれます。 |
参考サイトとの差分
参照基準は Radix Primitives Menubar のみです(ark-ui / chakra-ui / Radix Themes には Menubar 相当が存在しません)。Arrow/ArrowTip/ItemText/ ItemIndicator/CheckboxItem/RadioItemGroup/RadioItem の 7 パーツを新設し(11 → 18 anatomy パーツ)、各パーツ関数の呼び出し側 attrs から固定属性キーの偽装を除去する drop_reserved を全パーツへ適用しました。一方、以下は意図的に合わせていません。
- Radix
Portal: DOM 配置の関心のため不採用です(positionerが配置コンテナとして表現)。 - content/sub-content の
data-side/data-align:positionerへattrs経由で渡す既存設計のため置き換えていません。 - sub-content の
data-orientation: 不採用です。 - RTL
dir: 未対応です。 - Root の
value/defaultValue: 制御値はMenubar状態機械のopen: Option<usize>が担います。 - Trigger の Space/Enter による実 DOM フォーカス移動: Trigger にフォーカスを留め
data-highlighted(+ 実行時のaria-activedescendant)で仮想フォーカスを表現する設計のため未採用です。
スタイル済みの表示例は Menubar を参照してください。
関連 API: fandhe-frontend-headless-ui API
自前 CSS の最小例
Themes 版を使わず本部品を直接使う場合、[data-scope="menubar"][data-part="..."] セレクタでスタイルを当てます。positioner/content/sub-content/item-indicator には [hidden] ガードを必ず含めます。
[data-scope="menubar"][data-part="positioner"][hidden],
[data-scope="menubar"][data-part="content"][hidden],
[data-scope="menubar"][data-part="sub-content"][hidden],
[data-scope="menubar"][data-part="item-indicator"][hidden] {
display: none;
}
[data-scope="menubar"][data-part="trigger"][data-state="open"] {
background: #eff6ff;
}
[data-scope="menubar"][data-part="item"][data-highlighted] {
background: #eff6ff;
}
[data-scope="menubar"][data-part="item"][data-disabled] {
color: #9ca3af;
}
[data-scope="menubar"][data-part="checkbox-item"][data-state="checked"] {
font-weight: 600;
}Demo
枠線・余白は docs サイト側のデモ枠であり、fandhe-frontend-headless-ui 自体はスタイルを持ちません。
Features
- 複数 Menu を水平(または垂直)に並べるコンテナ。Root / Menu / Trigger / Positioner / Content / Arrow / ArrowTip / Item / ItemText / ItemIndicator / ItemGroup / ItemGroupLabel / Separator / SubTrigger / SubContent / CheckboxItem / RadioItemGroup / RadioItem の 18 anatomy パーツを持つ(menubar.rs モジュール doc。Arrow/ArrowTip/ItemText/ItemIndicator/CheckboxItem/RadioItemGroup/RadioItem はイシュー #1652 で Radix Primitives Menubar と突合し追加、11 → 18 パーツ)。
- roving tabindex(focused/trigger_count/open/loop_focus/orientation の複合状態機械 Menubar)。フォーカス対象のトリガーのみ tabindex="0"、それ以外は tabindex="-1" になる。
- 開いている Menu を跨いだ左右移動: ある Menu が開いた状態で Next/Prev/First/Last/Focus を dispatch すると、フォーカス移動と同時に開く Menu も隣へ移る(menubar 特有の挙動、toolbar の roving tabindex にはない)。
- menu パーツは role="none" を固定付与し、role="menubar" の子として menuitem/group 以外の要素を挟まないようにする(WAI-ARIA APG の menubar パターン)。
- 既存の menu モジュールの anatomy はそのまま再利用せず data-scope="menubar" を独自に持つ。状態機械の値語彙(OpenState/aria/data-* ヘルパ)と checkbox_item/radio_item の checked 値語彙(checked_data_state)のみを再利用する。
- 全パーツで呼び出し側 attrs による固定属性(role/aria-*/data-*/tabindex)の偽装を drop_reserved で除去する(イシュー #1652、A05)。
Anatomy
root
menu
trigger
positioner
content
arrow
arrow-tip
item-group
item-group-label
item
separator
checkbox-item
item-indicator
item-text
radio-item-group
radio-item
sub-trigger
sub-contentAPI Reference
Arguments
| Name | Type | Default | Description |
|---|---|---|---|
root(orientation) | Orientation | Orientation::Horizontal | role="menubar" に付与する向き。aria-orientation/data-orientation の両方へ反映される。 |
root(label) | &str | — | root に付与する aria-label(空文字列のときは省略)。 |
trigger(focused) | bool | — | true のとき tabindex="0"、false のとき tabindex="-1"(roving tabindex)。 |
trigger(state) | OpenState | — | この Menu 自身の開閉状態。aria-expanded/data-state へ反映される。 |
checkbox_item(checked) / radio_item(checked) | bool | — | checked/unchecked を role="menuitemcheckbox"/"menuitemradio" の aria-checked と data-state(checked_data_state 経由)へ反映する。 |
item_indicator(checked) | bool | — | data-state(checked_data_state)へ反映し、unchecked のとき hidden 存在属性を付与する。aria-hidden="true" を固定付与。 |
item_text(disabled, highlighted) | bool, bool | — | 親 item 系パーツの状態を data-disabled/data-highlighted として装飾用に複製する。 |
sub_trigger(sub_state) | OpenState | — | このトリガーが開閉するサブメニュー側の開閉状態(親 Menubar の state ではない)。aria-expanded/data-state へ反映する。 |
Data Attributes
| Part | Attribute | Observed Values |
|---|---|---|
checkbox-item | data-state | checked, unchecked |
checkbox-item | data-value | minimap, word-wrap |
content | data-state | closed, open |
item | data-disabled | (値なし) |
item | data-highlighted | (値なし) |
item | data-value | close, new, pdf, png, undo |
item-indicator | data-state | checked, unchecked |
menu | data-state | closed, open |
positioner | data-state | closed, open |
radio-item | data-state | checked, unchecked |
radio-item | data-value | grid, list |
root | data-orientation | horizontal |
sub-content | data-state | open |
sub-trigger | data-state | open |
trigger | data-disabled | (値なし) |
trigger | data-state | closed, open |
trigger | data-value | 0, 1, 2 |
Examples
2 個の Menu を並べた構成
File(open)/ Edit(closed)の 2 つの Menu を並べ、複数 Menu をまたぐ roving tabindex の構造を示します。
checkbox-item / radio-item-group を含む構成
checkbox-item(Word Wrap)と radio-item-group(Grid/List)を含む View メニューの構成例です。
自前 CSS を当てる最小例
data-scope="menubar" セレクタで hover/highlighted/disabled/checked のスタイルを当てる最小 CSS 例です。
[data-scope="menubar"][data-part="positioner"][hidden],
[data-scope="menubar"][data-part="content"][hidden],
[data-scope="menubar"][data-part="sub-content"][hidden],
[data-scope="menubar"][data-part="item-indicator"][hidden] {
display: none;
}
[data-scope="menubar"][data-part="trigger"][data-state="open"] {
background: #eff6ff;
}
[data-scope="menubar"][data-part="item"][data-highlighted] {
background: #eff6ff;
}
[data-scope="menubar"][data-part="item"][data-disabled] {
color: #9ca3af;
}
[data-scope="menubar"][data-part="checkbox-item"][data-state="checked"] {
font-weight: 600;
}
Accessibility
Keyboard Interactions
| Key | Description |
|---|---|
| ArrowRight / ArrowLeft(trigger) | 隣の trigger へフォーカスを移動する(open-follows-focus: ある Menu が開いていれば開く Menu も追随する)。垂直 menubar では ArrowDown/ArrowUp が同じ役割を担う(menubar.rs「開いている Menu を跨いだ左右移動」節)。 |
| ArrowDown / Space / Enter(trigger、closed) | Menu を開き、先頭の非 disabled 項目を data-highlighted で仮想フォーカスする(実 DOM フォーカスは trigger に留まる)。 |
| ArrowUp(trigger、closed) | Menu を開き、末尾の非 disabled 項目を仮想フォーカスする。 |
| ArrowDown / ArrowUp(content 内) | 項目間で data-highlighted を移動する(disabled はスキップする、fandhe-frontend-wasm-full の keynav::highlight_next_index が step_non_disabled へ委譲する)。 |
| ArrowRight / ArrowLeft(sub-trigger) | ArrowRight でサブメニューを展開、ArrowLeft で親トリガーへ復帰する(水平配置時。垂直配置は軸が入れ替わる)。 |
| Home / End | content 内の先頭 / 末尾の非 disabled 項目へ仮想フォーカスを移動する(WAI-ARIA APG 準拠、Radix Menubar にはない拡張)。 |
| 印字可能文字(typeahead) | item-text の子テキストをラベルとして前方一致検索し、一致する項目へ仮想フォーカスを移動する(WAI-ARIA APG 準拠)。 |
| Escape | 開いている Menu を閉じる。trigger からフォーカスを離さない設計のため、フォーカス復帰は構造的に成立する。 |
| Tab / Shift+Tab | 無配線(ブラウザ既定に委ねる)。roving tabindex により tabindex="0" のトリガーのみが Tab 順序に含まれる。 |
WAI-ARIA
| Attribute | Description |
|---|---|
| role="menubar" / aria-orientation | root に固定付与。orientation 引数の値を反映する。 |
| role="none" | menu に固定付与(menubar の子として menuitem/group 以外を挟まないための WAI-ARIA APG 慣行)。 |
| role="menuitem" / aria-haspopup="menu" / aria-expanded | trigger/sub-trigger に付与。開閉状態(この Menu の state、もしくはサブメニューの sub_state)を反映する。 |
| role="menu" | content/sub-content に固定付与。aria-labelledby は labelledby が Some のときのみ出力される。 |
| role="menuitemcheckbox" / role="menuitemradio" / aria-checked | checkbox-item/radio-item に付与。checked/unchecked の 2 値のみ(indeterminate は扱わない)。 |
| aria-hidden="true" | item-indicator に固定付与(装飾アイコンであり、親 checkbox-item/radio-item 自身の aria-checked が checked 状態を既に伝達するため)。 |