Combobox
チップ、グループ、非同期の結果に対応した、絞り込み可能なセレクトです。入力に合わせてポップアップのサイズが変わります。
pnpm dlx shadcn@latest add https://hextaui.com/r/combobox.jsonコンポーネント、HextaUIのテーマトークン、依存するHextaUIコンポーネントを追加します。
まだ追加していない場合は、グローバルCSSにテーマトークンを追加してください。
依存関係をインストールします。
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cn次のコードをコピーしてプロジェクトに貼り付けてください。
components/ui/combobox.tsx インポートパスは、お使いのプロジェクト構成に合わせて更新してください。
選択肢を items に渡し、<ComboboxList /> の内側の関数でそれぞれをレンダリングします。combobox は入力に合わせてフィルタリングし、一致したものだけをレンダリングします。オブジェクトも使え、その label が入力欄に表示され、value が送信されます。
フィールドに入力して、リストを絞り込みます。
ボタンが値を表示し、検索フィールドはポップアップ内に移動します。
multiple を指定すると、選択された各項目が入力欄の前にチップとして表示されます。
クリアボタン
showClear は、値がある間、山形アイコンの位置に入れ替わるクリアボタンを追加するため、フィールドが大きくなることはありません。
アイコンつき
項目内のアイコンは、サイズと色味が自動で調整されます。autoHighlight は入力中に最初の一致をハイライトするため、Enter でそれを選択できます。
グループとセパレーター
{ value, items } の形のグループを渡し、それぞれを <ComboboxGroup />、<ComboboxLabel />、<ComboboxCollection /> でレンダリングします。空のグループはフィルタリング中は非表示になります。
複数
multiple を指定すると、選択内容は <ComboboxChips /> の中でチップになります。選択している間もポップアップは開いたままで、空の入力欄で Backspace を押すと最後のチップが削除され、矢印キーでチップ間を移動できます。
ポップアップ内で検索
select のようなフィールドには <ComboboxTrigger /> を使います。入力欄を <ComboboxContent /> の内側に置くと、アイコン付きの検索ボックスになり、ポップアップは少なくとも 15rem の幅に広がります。
Button としてレンダリングされたトリガー
トリガーに render を渡すと、任意のボタンを使えます。ポップアップはそれにアンカーされ、少なくともその幅を保ちます。
制御
選択は value と onValueChange、ポップアップは open と onOpenChange で制御します。クリアすると値は null になります。
無効、不正、無効な項目
ルートの disabled はフィールドとそのボタンを暗くします。入力欄の aria-invalid はエラーのリングを描画します。無効な項目は、矢印キーでスキップされます。
長いコンテンツと大きなリスト
長いラベルや区切りのないラベルは、ポップアップを広げずに折り返されます。limit はレンダリングする一致の数を制限するため、500 件のリストも高速に保たれます。
非同期検索
filter={null} で組み込みのフィルタリングをオフにし、onInputValueChange で取得を行い、進行状況は <ComboboxStatus /> で表示します。これはスクリーンリーダーにも通知されます。結果が変わると、ポップアップの高さがアニメーションします。
シートの中
ポップアップはシートの上に重なり、Escape はシートより先にポップアップを閉じます。
右から左
ポップアップはフィールドの方向を引き継ぐため、クリアボタン、チップ、項目は、追加の props なしで反転します。
| キー | アクション |
|---|---|
| ↓↑ | ポップアップを開き、ハイライトを一致の間で移動します。無効な項目はスキップされます。 |
| Enter | ハイライトされた項目を選択します。何もハイライトされていない場合は、ポップアップを閉じてフォームを送信できる状態にします。 |
| Escape | ポップアップを閉じます。すでに閉じている場合は、値と入力内容をクリアします。 |
| HomeEnd | テキストカーソルを入力欄の先頭または末尾に移動します。 |
| Backspace | 空のチップ入力欄では、最後のチップを削除します。フォーカス中のチップでは、そのチップを削除します。 |
| ←→ | チップがある場合、フォーカスをチップ間で、また入力欄へ戻る方向に移動します。右から左のレイアウトでは反転します。 |
| Tab | ポップアップを閉じ、フォーカスを次へ移動します。 |
idとhtmlForで入力欄に見える<label>を付けるか、aria-labelを付けてください。表示テキストのない<ComboboxTrigger />にもaria-labelが必要です。- 山形アイコンのボタンには「Show options」、クリアボタンには「Clear selection」、各チップの削除ボタンには「Remove」というラベルが付きます。
- ハイライトは
aria-activedescendantで移動するため、閲覧中もフォーカスは入力欄に残ります。 - タッチスクリーンでは、iOS でズームされないよう入力欄に 16px のフォントを使い、項目は 44px のタップターゲットに拡大されます。
Base UI の combobox をベースにしています。各パーツは、ラップしているプリミティブの props を受け付けます。表には、よく使うものを掲載しています。
| プロパティ | 型 | デフォルト |
|---|---|---|
items選択肢。入力に合わせてフィルタリングされ、リストの render 関数に渡されます。 | Item[] | Group[] | – |
multiple複数の値を選択し、チップとして表示します。 | boolean | false |
value | Value | Value[] | null | – |
defaultValue | Value | Value[] | null | – |
onValueChange | (value, details) => void | – |
open | boolean | – |
defaultOpen | boolean | false |
onOpenChange | (open: boolean, details) => void | – |
inputValue | string | – |
defaultInputValue | string | – |
onInputValueChange | (inputValue: string, details) => void | – |
filterカスタムの一致判定。null を指定すると、サーバー側検索向けにフィルタリングをオフにします。 | ((item, query, itemToString) => boolean) | null | – |
limitレンダリングする一致の最大数。-1 はすべてを意味します。 | number | -1 |
autoHighlight入力中に最初の一致をハイライトします。 | boolean | false |
highlightItemOnHover | boolean | true |
openOnInputClick | boolean | true |
loopFocusハイライトを最後の項目から最初の項目へ回り込ませます。 | boolean | true |
itemToStringLabelオブジェクトの項目について、入力欄に表示されるテキスト。 | (item) => string | – |
itemToStringValueオブジェクトの項目について、フォームと一緒に送信される値。 | (item) => string | – |
isItemEqualToValue | (item, value) => boolean | – |
name | string | – |
required | boolean | false |
disabled | boolean | false |
readOnly | boolean | false |
modal開いている間、ページのスクロールと外側のクリックをロックします。 | boolean | false |
virtualized仮想化ライブラリで項目をレンダリングするときに設定します。 | boolean | false |
locale一致判定に使われるロケール。 | Intl.LocalesArgument | – |
ポップアップの外側では完全なフィールドをレンダリングします。<ComboboxContent /> の内側では、コンパクトな検索ボックスになります。
| プロパティ | 型 | デフォルト |
|---|---|---|
showTrigger山形アイコンのボタンを表示します。ポップアップ内では、設定しない限り常にオフです。 | boolean | true outside the popup |
showClear値がある間、山形アイコンの位置にクリアボタンを表示します。 | boolean | false |
className入力欄を囲む input group に適用されます。 | string | – |
disabled | boolean | false |
placeholder | string | – |
| 属性 | 説明 |
|---|---|
data-slot="combobox-input-group" | 入力欄を囲むフィールド。 |
data-slot="combobox-input" | テキスト入力欄。 |
data-slot="combobox-input-actions" | 山形アイコンとクリアボタンを、重ねた 1 つのセルに収めます。 |
data-popup-open | ポップアップが開いている間、入力欄に付与されます。 |
data-popup-side | ポップアップが開いた側。 |
data-list-empty | 一致するものがないときに付与されます。 |
data-disabled | 無効のときに存在します。 |
data-invalid | Base UI の Field 内で無効なときに付与されます。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
children通常は <ComboboxValue /> です。山形アイコンはその後ろに追加されます。 | ReactNode | – |
render設定すると、組み込みのフィールドスタイルがスキップされます。 | ReactElement | (props, state) => ReactElement | <button> |
| 属性 | 説明 |
|---|---|
data-slot="combobox-trigger" | トリガーボタン。 |
data-slot="combobox-trigger-value" | 省略された値を包みます。 |
data-slot="combobox-trigger-icon" | 山形アイコン。開いている間は反転します。 |
data-popup-open | ポップアップが開いている間、付与されます。 |
data-placeholder | 値が選択されていない間、付与されます。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
children選択された値を、たとえばチップとして、自分でレンダリングします。 | ReactNode | (value) => ReactNode | – |
placeholder何も選択されていない間に表示されます。 | ReactNode | – |
| プロパティ | 型 | デフォルト |
|---|---|---|
side | "top" | "bottom" | "left" | "right" | "inline-start" | "inline-end" | "bottom" |
align | "start" | "center" | "end" | "start" |
sideOffset | number | 6 |
alignOffset | number | 0 |
anchor別の要素を基準に配置します。デフォルトはフィールドです。useComboboxAnchor を参照してください。 | Element | RefObject<Element | null> | VirtualElement | (() => Element | VirtualElement | null) | null | – |
dirデフォルトは、フィールドの方向です。 | "ltr" | "rtl" | – |
| 属性 | 説明 |
|---|---|
data-slot="combobox-positioner" | ポップアップの位置を決めます。 |
data-slot="combobox-content" | ポップアップの面。 |
data-slot="combobox-content-sizer" | 一致の変化に合わせてポップアップの高さをアニメーションさせるために計測されます。 |
data-open | 開いている間存在します。 |
data-side | 開いた側。 |
data-align | その配置。 |
data-empty | 一致するものがないときに付与されます。 |
data-starting-style | 表示アニメーション中に付与されます。 |
data-ending-style | 非表示アニメーション中に付与されます。 |
--combobox-item-radius | 項目の角丸の半径。ポップアップの角丸からパディングを引いた値です。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
childrenitems の一致ごとに呼ばれます。 | ReactNode | (item, index) => ReactNode | – |
| 属性 | 説明 |
|---|---|
data-slot="combobox-list" | スクロールするリスト。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
valueこの行が表す項目。 | Item | – |
disabled | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
| 属性 | 説明 |
|---|---|
data-slot="combobox-item" | 選択肢。 |
data-slot="combobox-item-indicator" | 選択時にスケールインして表示されるチェックマーク。 |
data-highlighted | ハイライトされている間存在します。 |
data-selected | 選択されているときに付与されます。 |
data-disabled | 無効のときに存在します。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
itemsComboboxGroup では、グループ自身の項目。 | Item[] | – |
childrenComboboxCollection では、各一致をレンダリングします。 | (item, index) => ReactNode | – |
| 属性 | 説明 |
|---|---|
data-slot="combobox-group" | 項目のグループ。 |
data-slot="combobox-label" | グループの見出し。 |
<ComboboxEmpty /> は、一致するものがないときだけ子要素を表示します。<ComboboxStatus /> は、読み込み中や結果のメッセージ用のライブリージョンです。どちらも空のときは何も表示されません。
| 属性 | 説明 |
|---|---|
data-slot="combobox-empty" | 結果なしのメッセージ。 |
data-slot="combobox-status" | ライブのステータスメッセージ。 |
data-slot="combobox-separator" | グループ間の区切り線。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
children | ReactNode | <IconX /> |
| 属性 | 説明 |
|---|---|
data-slot="combobox-clear" | 「Clear selection」というラベルが付きます。 |
data-visible | クリアできるものがある間、付与されます。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
classNameチップを囲むフィールドに適用されます。 | string | – |
| 属性 | 説明 |
|---|---|
data-slot="combobox-chips" | チップと入力欄を保持するフィールド。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
showRemove削除ボタンを表示します。 | boolean | true |
| 属性 | 説明 |
|---|---|
data-slot="combobox-chip" | 選択された値。 |
data-slot="combobox-chip-label" | 省略されたラベル。 |
data-slot="combobox-chip-remove" | 「Remove」というラベルが付きます。 |
チップの後ろに置かれるテキスト入力欄。Base UI の input と同じ props を受け付けます。
| 属性 | 説明 |
|---|---|
data-slot="combobox-chips-input" | チップの入力欄。 |
useComboboxAnchor()は、要素と、コンテンツのanchorに渡す ref を返します。useComboboxFilter()は、filter向けに、ロケールに対応したcontains、startsWith、endsWithのマッチャーを返します。useComboboxFilteredItems()は、件数表示や仮想化リスト向けに、現在の一致を読み取ります。createComboboxItems(data, { getValue })は、選択値がオブジェクト全体ではなく、データベースのキーのようなプリミティブな id になる項目コレクションを作ります。comboboxFieldVariantsは、カスタムのフィールドを作るためのフィールドスタイルを公開します。
- Calendar単一、範囲、複数選択に対応する日付グリッドです。月のスライド、範囲のプレビュー、タッチしやすいサイズの日付セルを備えています。
- Checkboxチェックが描かれるように表示されるチェックボックスです。中間状態の親、グループ、ホバーを共有するラベルに対応します。
- Date picker押すとポップオーバーでカレンダーを開くボタンです。スマートフォンではボトムシートになり、単一の日付も範囲も選べます。
- Fieldコントロールに紐付いたラベル、説明、エラーです。バリデーション状態とフォーム向けのレイアウトを備えています。
- Input3つのサイズ、無効状態と読み取り専用状態、ネイティブのバリデーションスタイル、スマートフォンでズームされない16pxのタッチ用フォントを備えたテキスト入力です。
- Input groupアイコン、テキスト、ボタン、キーボードのヒントを付けられる入力欄です。1つのボーダーとフォーカスリングを共有します。