コンテンツにスキップ

マルチコンボボックス

hc-multicombobox は、タグ入力コントロールを備えた複数選択の コンボボックスです。選択された値は 1 枚の視覚面の中にインラインの チップとして描画され、フィルタ入力はその隣に住み、リストボックスは aria-multiselectable="true" を持つため選択のたびに閉じません。

hc-combobox と同じ アーキテクチャのプリミティブです — WAI-ARIA 1.2 コンボボックス パターン、HTML popover、CSS Anchor Positioning、DOM フォーカスを 入力に留めたままハイライトを追跡する aria-activedescendant

別名: マルチセレクト、タグ入力、複数選択コンボボックス。

  • JavaScript
  • TypeScript
  • Python
  • Go
  • Rust (coming soon)
import { installMulticombobox } from '@hypermedia-components/core';
installMulticombobox();

事前に aria-selected="true" が付いたオプションは、シードされたタグに なります。

パーツ必須用途
hc-multicomboboxはいラッパー。data-name / data-allow-create を持ちます。
hc-multicombobox__controlはい視覚的なコントロールボックス(hc-input と併用)。
hc-multicombobox__tagsはいビヘイビアがタグチップで満たす空のコンテナ。
hc-multicombobox__inputはいフィルタ入力 — role="combobox"
hc-multicombobox__listboxはいオプションリスト — role="listbox"
hc-multicombobox__optionはいオプションごとに 1 つ — role="option" + data-value
hc-multicombobox__tag / __tag-remove生成タグチップとその × 削除ボタン。
hc-multicombobox__hidden生成data-name フォーム統合用の hidden input コンテナ。
hc-multicombobox__empty生成フィルタに一致がない間の「No matches」プレースホルダー。
hc-multicombobox__create生成合成の「Add …」オプション(data-allow-create)。

はいのパーツは著者が書きます。生成のパーツは installMulticombobox() が作成・削除するので、自分では書かないで ください。空プレースホルダーのテキストは、リストボックスdata-hc-empty があればそれ、なければ i18n キー multicombobox.empty (i18n カタログ) です:

<ul class="hc-multicombobox__listbox" id="lang-list" role="listbox"
data-hc-empty="No matching language">
  • ARIA: リストボックスに aria-multiselectable="true"aria-haspopup="listbox"aria-autocomplete="list"aria-expanded、(なければ)aria-controls、ハイライト中の オプションを追跡する aria-activedescendant
  • アンカー結合: 入力にインライン anchor-name、リストボックスに position-anchor。CSS Anchor Positioning のないブラウザには JS の 配置フォールバック。
  • シード: インストール時点で aria-selected="true" を持つすべての オプションがタグチップになります — SSR で事前選択された状態が そのまま使えます。
  • フィルタリング: キーストロークごとの大文字小文字を無視した 部分文字列一致。何も一致しなければ .hc-multicombobox__empty の プレースホルダー。
  • トグル選択: ハイライト中のオプションのクリックまたは Enter で 選択状態をトグル。リストボックスは開いたままなので、開き直さずに 複数選べます。
  • タグの削除:
    • タグの × ボタンをクリック。
    • 入力が空の状態で Backspace を押すと最後のタグを削除(タグ入力の 標準的な作法)。
  • フォーム統合: ラッパーに data-name="X" があると、ビヘイビアは 選択値ごとに <input type="hidden" name="X" value="…"> をラッパー 内に書きます。フォームはネイティブの <select multiple name="X"> のようにシリアライズされます。
  • イベント: すべての状態変化が、入力上で detail.{values, added, removed, input} を持つ hc:multicomboboxchange を発火します。
キー動作
/ リストボックスを開く / activedescendant を移動。
Home / End最初 / 最後の有効な見えるオプション。
Enterハイライト中のオプションをトグル。
Backspace(入力が空)最後のタグを削除。
Escapeリストボックスを閉じる。選択は保持。
Tabリストボックスを閉じる。通常のタブ順。

ネイティブなフォームシリアライズには、ラッパーに data-name を設定 します:

<form action="/save" method="post">
<div class="hc-multicombobox" data-name="languages"></div>
<button class="hc-button">Save</button>
</form>

languages=js&languages=ts&languages=py として送信されます — ネイティブの <select multiple name="languages"> が生むのと同じ形 です。PHP / Rails / Python のフレームワークは設定なしでこれをパース します。

変更イベントは全状態を運ぶため、編集のたびの htmx スワップは 1 つの トリガー宣言で済みます:

<div class="hc-multicombobox"
data-name="languages"
data-hx-post="/profile/languages"
data-hx-trigger="hc:multicomboboxchange from:closest .hc-multicombobox"
data-hx-include="this"
data-hx-target="#status">
</div>

サーバは値を永続化し、#status 向けの小さな確認フラグメント (「Saved」の表示や検証メッセージ)を返します — コントロール自体は 再描画しません。タグと hidden input はクライアント状態です。

非同期オプション — オプションリストをサーバから読み込むには、 代わりにリストボックスの子をスワップします: ビヘイビアはオプションを DOM からライブに読むため、スワップで入った行も静的な行と同じように フィルタ・ハイライト・トグルされます。

<input class="hc-multicombobox__input" type="text" name="q"
role="combobox" aria-controls="lang-list" aria-label="Languages"
data-hx-get="/languages/options"
data-hx-trigger="input changed delay:300ms"
data-hx-target="#lang-list" data-hx-swap="innerHTML">

サーバは、入力された q に一致する <li class="hc-multicombobox__option" role="option" data-value="…"> の行だけを返します。

data-allow-create を足すと、ユーザーはリストにないタグを追加でき ます。入力したテキストに完全一致がないとき、合成の 「Add …」 オプションが現れます。それを選ぶ(クリックまたは Enter)と 生のテキストからタグが作られ、新しい値を added に載せた hc:multicomboboxchange が発火します。

<div class="hc-multicombobox" data-name="tags" data-allow-create></div>

作られたタグのラベルは値そのもので、他のタグと同様に隠し name input が追加されます。オプションのラベルは i18n カタログ (multicombobox.create)で翻訳できます。

オプションには任意の HTML(アイコン + ラベル + 説明)を入れられます。 タグのラベルと一致値には data-label を、フィルタの照合対象 (エイリアス / キーワード)には data-search を使い、リッチ マークアップのテキストがどちらも汚さないようにします:

  • Python scripting
  • JavaScript web
  • Go services

リッチレイアウトのためにオプションへ display を設定するなら、 フィルタの非表示を上書きしないよう :not([hidden]) にスコープして ください。

  • DOM フォーカスは入力に留まり、タイプアヘッドのアンカーが一貫します — aria-activedescendant が動かすのはハイライトであり、フォーカス ではありません。
  • 各タグは aria-label="Remove …" を持つ本物のフォーカス可能な ボタンなので、スクリーンリーダーの利用者はそこに着地して削除を 実行できます。
  • 利用不可の行は disabled ではなく aria-disabled="true" で印を 付けてください(<li> はフォームコントロールではありません)。
  • 見える activedescendant のハイライトは --hc-color-focus-ring を 使い、data-color テーマに従います。
  • タグのドラッグによる並べ替え
  • 非同期のオプション読み込み — 上のドキュメントは htmx スワップの パターンを示しています。組み込みのデバウンス / キャンセルヘルパーは 同梱されません。

component トークン(component.tokens.json):

トークンパス用途
multicombobox.control.padding-x / padding-y / gap / min-heightタグ入力コントロールのボックス。
multicombobox.input.min-width / fgインラインのテキスト入力。
multicombobox.tag.bg / fg / border / padding-x / padding-y / radius / font-size / gapタグチップ。
multicombobox.tag.remove-fg / remove-hover-fg× ボタン。
multicombobox.listbox.*コンボボックスのリストボックストークンのミラー。
multicombobox.option.{padding-x, padding-y, font-size, fg, hover-bg, active-bg, disabled-fg, indicator-size}オプションのレイアウト + チェックマーク。
multicombobox.option.check-color選択チェックマークの色(マスク描画なのでアクセントに追従 — 強制カラーでは SelectedItem)。
multicombobox.empty-fg「No matches」プレースホルダーの色。
生成される CSS 変数を表示
--hc-multicombobox-control-padding-x | -control-padding-y | -control-gap | -control-min-height
--hc-multicombobox-input-min-width | -input-fg
--hc-multicombobox-tag-bg | -tag-fg | -tag-border | -tag-radius | -tag-gap | -tag-font-size
--hc-multicombobox-tag-padding-x | -tag-padding-y
--hc-multicombobox-tag-remove-fg | -tag-remove-hover-fg
--hc-multicombobox-listbox-bg | -listbox-fg | -listbox-border | -listbox-radius
--hc-multicombobox-listbox-max-height | -listbox-padding-block | -listbox-min-width | -listbox-offset
--hc-multicombobox-option-padding-x | -option-padding-y | -option-font-size | -option-fg
--hc-multicombobox-option-hover-bg | -option-active-bg
--hc-multicombobox-option-disabled-fg | -option-indicator-size | -option-check-color
--hc-multicombobox-empty-fg