Command
検索入力 + 絞り込み済みリスト + グループ + ショートカット表示 + dialog 内表示を組み合わせたコマンドパレット相当の shadcn/ui Command(cmdk 由来)部品です(参照軸はイシュー #2001、shadcn/ui 追加はイシュー #2004)。fandhe-frontend-headless-ui の command mod が構造・アクセシビリティ(WAI-ARIA)・表示状態(data-*)のみを提供する unstyled 部品で、root / input / list / empty / group / group-heading / item / shortcut / separator / dialog の 10 anatomy パーツを持ちます。
既存の combobox(ARIA 1.2 combobox パターン)・listbox(role="listbox"/role="option")の ARIA 実装を再利用し、新規の意味論は持ち込みません。Command 状態機械(Disclosure + TextInput + SingleSelect を合成)は cmdk のコマンドパレット意味論に合わせ、入力(Input)は dialog を開閉せず、選択(Select)も dialog を閉じません(実行フックはアプリケーションロジックとして UI コンポーネント層の範囲外です)。
アクセシビリティ
inputはrole="combobox"+aria-controls(必須引数)+aria-autocomplete="list"を固定付与します。aria-controlsを必須引数にすることで、aria-controls/aria-expandedの関連付けを型で構造的に満たします。listはrole="listbox"を固定付与し、id/aria-labelを必須引数にすることでアクセシブルネームを型で強制します。itemはrole="option"+aria-selected+data-selected(presence)で選択有無を表します。data-highlighted/data-stateは出力しません(combobox/listboxとの意図的な差分。aria-activedescendantが指すのは「確定選択中の item」であり「キーボードでハイライト中の行」ではないため)。dialogはrole="dialog"+aria-modal="true"+tabindex="-1"を固定付与し、labelが空文字列でないときのみaria-labelを付与します。closed のときhidden存在属性を付与します。groupはlabelledbyがSomeのときのみrole="group"+aria-labelledbyを出力します。group-headingのidが参照先になります。root/list/emptyはいずれもempty: bool引数を取り、絞り込み結果 0 件のときdata-emptyを出力します。表示切替(hidden等)は呼び出し側または pre-styled-ui の CSS の責務です。- キーボード操作(矢印キー・Enter・Escape・Cmd/Ctrl+K)の実 DOM 配線は
fandhe-frontend-wasm-fullのcommandモジュールが実装済みです(入力絞り込み・行選択・実行フック・dialog 開閉、イシュー #2069)。
自前 CSS の最小例:
[data-scope="command"][data-part="root"][data-empty] {
min-height: 4rem;
}
[data-scope="command"][data-part="item"][data-selected] {
background: #eef;
}
[data-scope="command"][data-part="item"][data-disabled] {
opacity: 0.5;
}
[data-scope="command"][data-part="dialog"][hidden] {
display: none;
}fandhe-frontend-pre-styled-ui に対応するスタイル済み部品があります。Themes 版は Command を参照してください。
関連 API: fandhe-frontend-headless-ui API
Demo
枠線・余白は docs サイト側のデモ枠であり、fandhe-frontend-headless-ui 自体はスタイルを持ちません。
Features
- root/input/list/empty/group/group_heading/item/shortcut/separator/dialog の 10 anatomy パーツを持ち、combobox/listbox の既存 ARIA 実装(role="combobox"/role="listbox"/role="option")を再利用する(command.rs 冒頭参照)。
- input は role="combobox" + aria-controls(必須引数)+ aria-autocomplete="list" を固定付与し、docs-site の R1〜R4 契約を型で構造的に満たす(Option opt-in にしない、command.rs「
inputの ARIA」節)。 - item は data-selected(presence)+ aria-selected で選択有無を表し、data-highlighted/data-state は出力しない(combobox/listbox との意図的な差分、command.rs「
itemの選択表現」節)。 - root/list/empty はいずれも empty: bool 引数を取り、絞り込み結果 0 件のとき data-empty を出力する。表示切替(hidden 等)は呼び出し側/pre-styled-ui の CSS の責務(command.rs「
data-emptyの付与先と SSR 決定性」節)。 - Command 状態機械は Input で dialog を開閉せず、Select で dialog を閉じない(cmdk のコマンドパレット意味論。実行フックは UI コンポーネント層の責務外、command.rs参照)。
- filter_items は crate::combobox::filter_options へ全委譲する(新規の絞り込みアルゴリズムを持ち込まない)。
Anatomy
dialog
root
input
list
group
group-heading
item
shortcut
separator
emptyAPI Reference
Arguments
| Name | Type | Default | Description |
|---|---|---|---|
root(state, empty) / dialog(state, label) | OpenState, bool / OpenState, &str | OpenState::Closed, false | dialog 開閉状態と絞り込み結果 0 件かどうか。dialog の label は空文字列のとき aria-label を省略する。 |
input(state, value, list_id, activedescendant) | OpenState, &str, &str, Option<&str> | — | 現在のクエリ・aria-controls の参照先(必須)・選択中 item の id。 |
list(id, label, empty) | &str, &str, bool | — | role="listbox" の id(必須)・aria-label(必須)・絞り込み結果 0 件かどうか。 |
item(selected, disabled, value, id) | bool, bool, &str, Option<&str> | — | role="option" を持つ候補 1 個の選択状態・無効化・value(data-value として出力)・id。 |
group(labelledby) / group_heading(id) | Option<&str> / Option<&str> | — | labelledby が Some のときのみ role="group"/aria-labelledby を出力する。group_heading の id が参照先になる。 |
filter_items(items, query) / Command::filtered_items / is_empty | &[(&str, &str)], &str | — | crate::combobox::filter_options への全委譲(大文字小文字非区別の部分一致・入力順保持)。 |
attrs / children | Vec<(&str, &str)> / Vec<Node> | — | 各パーツ共通の追加属性・子ノード(代表 1 行に集約)。 |
Data Attributes
| Part | Attribute | Observed Values |
|---|---|---|
dialog | data-state | open |
empty | data-empty | (値なし) |
input | data-state | open |
item | data-disabled | (値なし) |
item | data-selected | (値なし) |
item | data-value | calendar, search, settings |
list | data-empty | (値なし) |
root | data-empty | (値なし) |
root | data-state | open |
Examples
Closed dialog
role="dialog" の Command を closed(hidden)状態で組み立てる最小例です。
role="dialog" の Command を closed(hidden)状態で組み立てる最小例です。
利用者スタイルの当て方
data-scope / data-part / data-selected / data-disabled / data-empty / hidden 属性セレクタで自前 CSS を当てる最小例です。
利用者が data-scope / data-part / data-selected / data-disabled / data-empty / hidden 属性セレクタで自前 CSS を当てる最小例です(本 Demo には適用されません)。
[data-scope="command"][data-part="root"][data-empty] {
min-height: 4rem;
}
[data-scope="command"][data-part="item"][data-selected] {
background: #eef;
}
[data-scope="command"][data-part="item"][data-disabled] {
opacity: 0.5;
}
[data-scope="command"][data-part="dialog"][hidden] {
display: none;
}
Accessibility
WAI-ARIA
| Attribute | Description |
|---|---|
| role="combobox" / aria-autocomplete="list" | input パーツに固定付与する。ARIA 1.2 combobox パターンを再利用する(combobox.rs との共通契約)。 |
| aria-controls | input パーツに必須引数として付与する(list_id、Option opt-in にしない)。 |
| aria-activedescendant | input パーツに Some のときのみ付与する。選択中 item の id を指す(ハイライト中の行ではない、command.rs参照)。 |
| role="listbox" / aria-label | list パーツに固定付与する。aria-label は必須引数のため常にアクセシブルネームを持つ。 |
| role="option" / aria-selected | item パーツに固定付与する。 |
| role="dialog" / aria-modal="true" | dialog パーツに固定付与する。tabindex="-1" も固定付与し、プログラム的フォーカスのみを許可する。 |
| role="group" / aria-labelledby | group パーツに labelledby が Some のときのみ付与する。group_heading の id と対にする。 |
| role="separator" / aria-orientation="horizontal" | separator パーツに固定付与する。 |