コンボボックス
stimeo--combobox
絞り込み可能なリスト(list autocomplete)付きのテキスト入力。キーボード操作にフル対応。
stimeo--combobox コントローラは WAI-ARIA の Combobox パターン(list autocomplete)を実装します。入力すると DOM 上の option を絞り込み(非一致を hidden に)、フォーカスは input に留まったまま、アクティブな option を aria-activedescendant で追跡します。option を選ぶと input にその値(data-value があればそれ)が入り、リストが閉じます。ライブラリは挙動のみを提供し、見た目はこの Playground 側が持ちます。
- Apple
- Apricot
- Banana
- Cherry
キーボード操作
| キー | 動作 |
|---|---|
| ↓ / ↑ | アクティブな option を移動する(端で循環)。 |
| Enter | アクティブな option を選択し、リストを閉じる。 |
| Home / End | 表示中の最初 / 最後の option をアクティブにする。 |
| Esc | リストを閉じる。 |
<%# Markup for the combobox (APG Combobox / list autocomplete) demo.
stimeo--combobox provides list filtering on input, Arrow/Enter/Esc, and
aria-activedescendant. Focus stays on the input and the active option is shown via
aria-selected. On selection, data-value (or the text, if absent) is placed in the input. %>
<div class="combobox" data-controller="stimeo--combobox">
<label class="combobox__label" for="fruit-input"><%= t(
"components.combobox.demo.label"
) %></label>
<input
id="fruit-input"
type="text"
class="combobox__input"
role="combobox"
aria-expanded="false"
aria-autocomplete="list"
aria-controls="fruit-list"
autocomplete="off"
data-stimeo--combobox-target="input"
data-action="input->stimeo--combobox#filter
keydown->stimeo--combobox#onKeydown
focus->stimeo--combobox#open
click->stimeo--combobox#open">
<ul
id="fruit-list"
class="combobox__list"
role="listbox"
data-stimeo--combobox-target="list"
hidden>
<li role="option" id="fruit-apple" class="combobox__option" data-value="Apple"
data-stimeo--combobox-target="option"
data-action="click->stimeo--combobox#selectByClick">Apple</li>
<li role="option" id="fruit-apricot" class="combobox__option" data-value="Apricot"
data-stimeo--combobox-target="option"
data-action="click->stimeo--combobox#selectByClick">Apricot</li>
<li role="option" id="fruit-banana" class="combobox__option" data-value="Banana"
data-stimeo--combobox-target="option"
data-action="click->stimeo--combobox#selectByClick">Banana</li>
<li role="option" id="fruit-cherry" class="combobox__option" data-value="Cherry"
data-stimeo--combobox-target="option"
data-action="click->stimeo--combobox#selectByClick">Cherry</li>
</ul>
<%# Empty state shown when there are no matches. CSS reacts to
data-stimeo--combobox-empty (set on the root by the library) to reveal it. Copy via i18n. %>
<div class="combobox__empty"><%= t("components.combobox.empty") %></div>
</div>
/*
* Presentation-only styles for the combobox demo.
* The active option is highlighted via aria-selected (set by the library).
*/
.combobox {
position: relative;
display: inline-block;
width: min(20rem, 100%);
}
.combobox__label {
display: block;
margin-bottom: 0.25rem;
font-size: 0.875rem;
color: var(--muted);
}
.combobox__input {
width: 100%;
padding: 0.5rem 0.75rem;
font-size: 1rem;
border: 1px solid var(--border-interactive);
border-radius: 0.375rem;
}
.combobox__list {
position: absolute;
left: 0;
right: 0;
margin: 0.25rem 0 0;
padding: 0.25rem;
list-style: none;
max-height: 12rem;
overflow-y: auto;
background: var(--surface-card);
border: 1px solid var(--border-strong);
border-radius: 0.375rem;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.combobox__option {
padding: 0.5rem 0.75rem;
border-radius: 0.25rem;
cursor: pointer;
}
.combobox__option[aria-selected="true"] {
background: var(--accent);
color: var(--white);
}
/*
* When there are no matches, the library sets data-stimeo--combobox-empty on the root.
* Rather than showing an empty list, this is an example of consumer CSS reacting to it
* to show the i18n'd .combobox__empty from the ERB (copy lives in the locale files;
* SKILL: playground-i18n).
*/
.combobox__empty {
position: absolute;
left: 0;
right: 0;
display: none;
margin: 0.25rem 0 0;
padding: 0.5rem 0.75rem;
background: var(--surface-card);
border: 1px solid var(--border-strong);
border-radius: 0.375rem;
color: var(--muted);
font-size: 0.875rem;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.combobox[data-stimeo--combobox-empty] .combobox__list {
display: none;
}
.combobox[data-stimeo--combobox-empty] .combobox__empty {
display: block;
}
このデモに固有の消費側 JS はありません(挙動はコントローラが担います)。
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。 共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--combobox"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
input
必須
|
フィルタを駆動し、aria-activedescendant でアクティブ選択肢を追跡するコンボボックスのテキスト input。 |
data-stimeo--combobox-target="input" |
list
必須
|
hidden 属性で開閉する role=listbox のポップアップ。 |
data-stimeo--combobox-target="list" |
option
|
DOM に記述された role=option。hidden の切替でフィルタされ、アクティブなものが指定される。 |
data-stimeo--combobox-target="option" |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
close
|
リストボックスを閉じ、アクティブ選択肢を解除し、ARIA 状態を更新する。 | stimeo--combobox#close |
filter
|
input 値で選択肢を絞り込み、リストボックスを開く。 | stimeo--combobox#filter |
onKeydown
|
APG コンボボックスモデルに沿ったキーボード操作を処理する(上下矢印の循環、Home/End、Enter、Escape、Tab)。 | stimeo--combobox#onKeydown |
open
|
現在の input 値で再フィルタしつつリストボックスを開く。 | stimeo--combobox#open |
selectByClick
|
クリックされた選択肢を確定し、input を埋めてリストボックスを閉じる。 | stimeo--combobox#selectByClick |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
selected
|
選択肢が確定したときに発火。detail は { value }(選択肢の data-value またはテキスト)。 |
stimeo--combobox:selected |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
aria-expanded |
入力欄 | リストボックスが開いている間は "true"、閉じると "false"。 |
aria-selected |
アクティブな選択肢 | 現在ハイライト中の選択肢で "true"。 |
aria-activedescendant |
入力欄 | 支援技術向けにアクティブな選択肢の id を保持する。 |
hidden |
選択肢 | 入力した文字でフィルタされた選択肢に付与される。 |
data-stimeo--combobox-empty |
ルート要素 | リストが開いているが一致する選択肢が無いとき付与される。 |