コンボボックス

コンボボックスは、アクセシビリティの高いオートコンプリートとアプリケーションのコマンドパレットの基盤であり、キーボードナビゲーションを強力にサポートしています。

開始するには、npm 経由で Headless UI をインストールします。

npm install @headlessui/react

コンボボックスは、ComboboxComboboxInputComboboxButtonComboboxOptionsComboboxOption コンポーネントを使用して構築されています。

結果のフィルタリング方法は完全にユーザーが制御できます。クライアントサイドでファジー検索ライブラリを使用するか、API へのサーバーサイドリクエストを行うかを選択できます。この例では、デモ目的でロジックを単純化します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
        aria-label="Assignee"
        displayValue={(person) => person?.name}
        onChange={(event) => setQuery(event.target.value)}
      />
      <ComboboxOptions anchor="bottom" className="border empty:invisible">
        {filteredPeople.map((person) => (
          <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100">
            {person.name}
          </ComboboxOption>
        ))}
      </ComboboxOptions>
    </Combobox>
  )
}

Headless UI は、各コンポーネントに関する多くの状態(現在選択されているコンボボックスオプション、ポップオーバーが開いているか閉じているか、キーボードで現在フォーカスされているメニューのアイテムなど)を追跡します。

しかし、コンポーネントはヘッドレスで、すぐに使用できる状態では完全にスタイルが適用されていないため、ユーザー自身が各状態に望ましいスタイルを適用するまで、UIではこの情報は表示されません。

Headless UI コンポーネントのさまざまな状態をスタイリングする最も簡単な方法は、各コンポーネントが公開するdata-*属性を使用することです。

たとえば、ComboboxOptionコンポーネントは、マウスまたはキーボードでオプションが現在フォーカスされているかどうかを示すdata-focus属性と、そのオプションがComboboxの現在のvalueと一致するかどうかを示すdata-selected属性を公開します。

<!-- Rendered `ComboboxOptions` -->
<div data-open>
  <div>Wade Cooper</div>
  <div data-focus data-selected>Arlene Mccoy</div>
  <div>Devon Webb</div>
</div>

これらのデータ属性の存在に基づいて条件付きでスタイルを適用するには、CSS 属性セレクターを使用します。Tailwind CSS を使用している場合、データ属性修飾子を使用すると簡単にできます。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { CheckIcon } from '@heroicons/react/20/solid'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
        aria-label="Assignee"
        displayValue={(person) => person?.name}
        onChange={(event) => setQuery(event.target.value)}
      />
      <ComboboxOptions anchor="bottom" className="border empty:invisible">
        {filteredPeople.map((person) => (
<ComboboxOption key={person.id} value={person} className="group flex gap-2 bg-white data-[focus]:bg-blue-100">
<CheckIcon className="invisible size-5 group-data-[selected]:visible" />
{person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

利用可能なすべてのデータ属性のリストについては、コンポーネントAPIを参照してください。

各コンポーネントは、レンダープロップスを介して現在の状態に関する情報を公開しており、これを使用して条件付きで異なるスタイルを適用したり、異なるコンテンツをレンダリングしたりできます。

たとえば、ComboboxOptionコンポーネントは、マウスまたはキーボードでオプションが現在フォーカスされているかどうかを示すfocus状態と、そのオプションがComboboxの現在のvalueと一致するかどうかを示すselected状態を公開します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { CheckIcon } from '@heroicons/react/20/solid'
import clsx from 'clsx'
import { Fragment, useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
        aria-label="Assignee"
        displayValue={(person) => person?.name}
        onChange={(event) => setQuery(event.target.value)}
      />
      <ComboboxOptions anchor="bottom" className="border empty:invisible">
        {filteredPeople.map((person) => (
<ComboboxOption as={Fragment} key={person.id} value={person} className="data-[focus]:bg-blue-100">
{({ focus, selected }) => (
<div className={clsx('group flex gap-2', focus && 'bg-blue-100')}>
{selected && <CheckIcon className="size-5" />}
{person.name}
</div>
)}
</ComboboxOption>
))} </ComboboxOptions> </Combobox> ) }

利用可能なすべてのレンダープロップスのリストについては、コンポーネントAPIを参照してください。

生成されたIDを使用して自動的に関連付けるには、LabelComboboxFieldコンポーネントでラップします。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions, Field, Label } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
<Field>
<Label>Assignee:</Label>
<Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}> <ComboboxInput displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox>
</Field>
) }

aria-describedby属性を使用してComboboxと自動的に関連付けるには、Field内にDescriptionコンポーネントを使用します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions, Description, Field, Label } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
<Field>
<Label>Assignee:</Label>
<Description>This person will have full access to this project.</Description>
<Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}> <ComboboxInput displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox>
</Field>
) }

Comboboxとその関連するLabelDescriptionを無効化するには、Fieldコンポーネントにdisabledプロップを追加します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions, Field, Label } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
<Field disabled>
<Label>Assignee:</Label> <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}> <ComboboxInput displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> </Field> ) }

Combobox自体にdisabledプロップを直接追加することで、`Field`の外でもコンボボックスを無効化することもできます。

disabledプロップを使用してComboboxOptionを無効化し、選択できないようにします。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds', available: true },
  { id: 2, name: 'Kenton Towne', available: true },
  { id: 3, name: 'Therese Wunsch', available: true },
{ id: 4, name: 'Benedict Kessler', available: false },
{ id: 5, name: 'Katelyn Rohan', available: true }, ] function Example() { const [selectedPerson, setSelectedPerson] = useState(people[0]) const [query, setQuery] = useState('') const filteredPeople = query === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.toLowerCase()) }) return ( <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}> <ComboboxInput aria-label="Assignee" displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person}
disabled={!person.available}
className="data-[focus]:bg-blue-100 data-[disabled]:opacity-50"
>
{person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

query値に基づいて動的なComboboxOptionを含めることで、リストに存在しない独自の値を入力できるようにします。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
        aria-label="Assignee"
        displayValue={(person) => person?.name}
        onChange={(event) => setQuery(event.target.value)}
      />
      <ComboboxOptions anchor="bottom" className="border empty:invisible">
{query.length > 0 && (
<ComboboxOption value={{ id: null, name: query }} className="data-[focus]:bg-blue-100">
Create <span className="font-bold">"{query}"</span>
</ComboboxOption>
)}
{filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

Comboboxnameプロップを追加すると、非表示のinput要素がレンダリングされ、コンボボックスの状態と同期されます。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <form action="/projects/1/assignee" method="post">
<Combobox name="assignee" value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
<ComboboxInput aria-label="Assignee" displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> <button>Submit</button> </form> ) }

これにより、ネイティブHTML<form>内にコンボボックスを使用し、コンボボックスがネイティブHTMLフォームコントロールであるかのように従来のフォーム送信を行うことができます。

文字列などの基本的な値は、その値を含む単一の非表示入力としてレンダリングされますが、オブジェクトなどの複雑な値は、名前の角括弧表記を使用して複数の入力にエンコードされます。

<!-- Rendered hidden inputs -->
<input type="hidden" name="assignee[id]" value="1" />
<input type="hidden" name="assignee[name]" value="Durward Reynolds" />

valueプロップを省略すると、Headless UI は内部的に状態を追跡するため、非制御コンポーネントとして使用できます。

非制御の場合、defaultValueプロップを使用してComboboxに初期値を提供します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <form action="/projects/1/assignee" method="post">
<Combobox name="assignee" defaultValue={people[0]} onClose={() => setQuery('')}>
<ComboboxInput aria-label="Assignee" displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> <button>Submit</button> </form> ) }

これは、HTMLフォームまたはFormDataを使用して状態を収集するフォームAPIとともにコンボボックスを使用する場合に、コードを簡素化できます。Reactの状態を使用して追跡する必要はありません。

コンポーネントの値が変更された場合、提供されたonChangeプロップは引き続き呼び出されます(副作用を実行する必要がある場合)。ただし、コンポーネントの状態を自分で追跡するために使用する必要はありません。

ComboboxInputを基準にドロップダウンを自動的に配置するには、ComboboxOptionsanchorプロップを追加します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
        aria-label="Assignee"
        displayValue={(person) => person?.name}
        onChange={(event) => setQuery(event.target.value)}
      />
<ComboboxOptions anchor="bottom start" className="border empty:invisible">
{filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

toprightbottom、またはleftの値を使用して、適切なエッジに沿ってドロップダウンを中心揃えするか、startまたはendと組み合わせて、top startまたはbottom endなど、特定のコーナーにドロップダウンを配置します。

入力とドロップダウンの間の間隔を制御するには、--anchor-gap CSS 変数を使用します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
        aria-label="Assignee"
        displayValue={(person) => person?.name}
        onChange={(event) => setQuery(event.target.value)}
      />
<ComboboxOptions
anchor="bottom start" className="border [--anchor-gap:4px] empty:invisible sm:[--anchor-gap:8px]" > {filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

さらに、--anchor-offsetを使用してドロップダウンを元の位置から移動する距離を制御し、--anchor-paddingを使用してドロップダウンとビューポートの間にある必要のある最小スペースを制御できます。

anchorプロップは、JavaScriptを使用してgapoffsetpaddingの値を制御できるオブジェクトAPIもサポートしています。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
        aria-label="Assignee"
        displayValue={(person) => person?.name}
        onChange={(event) => setQuery(event.target.value)}
      />
<ComboboxOptions anchor={{ to: 'bottom start', gap: '4px' }} className="border empty:invisible">
{filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

これらのオプションの詳細については、ComboboxOptions APIを参照してください。

ComboboxOptionsドロップダウンには、デフォルトでは幅が設定されていませんが、CSSを使用して追加できます。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
        aria-label="Assignee"
        displayValue={(person) => person?.name}
        onChange={(event) => setQuery(event.target.value)}
      />
<ComboboxOptions anchor="bottom" className="w-52 border empty:invisible">
{filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

ドロップダウンの幅をComboboxInputまたはComboboxButtonの幅と一致させるには、ComboboxOptions要素で公開される--input-width--button-widthのCSS変数を使用します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
        aria-label="Assignee"
        displayValue={(person) => person?.name}
        onChange={(event) => setQuery(event.target.value)}
      />
<ComboboxOptions anchor="bottom" className="w-[var(--input-width)] border empty:invisible">
{filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

コンボボックスパネルの開閉をアニメーション化するには、ComboboxOptionsコンポーネントにtransitionプロップを追加し、CSSを使用してトランジションのさまざまな段階をスタイル設定します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
        aria-label="Assignee"
        displayValue={(person) => person?.name}
        onChange={(event) => setQuery(event.target.value)}
      />
      <ComboboxOptions
        anchor="bottom"
transition
className="origin-top border transition duration-200 ease-out empty:invisible data-[closed]:scale-95 data-[closed]:opacity-0"
>
{filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

内部的には、transitionプロップはTransitionコンポーネントとまったく同じ方法で実装されています。詳細については、トランジションのドキュメントを参照してください。

Headless UI は、Framer MotionReact SpringなどのReactエコシステムの他のアニメーションライブラリとも連携します。これらのライブラリに状態を公開するだけです。

例えば、Framer Motion を使用してコンボボックスをアニメーション化するには、ComboboxOptions コンポーネントにstatic プロップを追加し、open レンダリングプロップに基づいて条件付きでレンダリングします。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { AnimatePresence, motion } from 'framer-motion'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson}>
{({ open }) => (
<> <ComboboxInput aria-label="Assignee" displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <AnimatePresence>
{open && (
<ComboboxOptions
static
as={motion.div} initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }} anchor="bottom" className="origin-top border empty:invisible" onAnimationComplete={() => setQuery('')} >
{filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions>
)}
</AnimatePresence> </>
)}
</Combobox> ) }

文字列のみを値として提供できるネイティブのHTMLフォームコントロールとは異なり、Headless UI は複雑なオブジェクトのバインドもサポートしています。

オブジェクトをバインドする場合は、選択されたオプションの文字列表現を入力にレンダリングできるように、ComboboxInputdisplayValue を設定してください。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
{ id: 1, name: 'Durward Reynolds' },
{ id: 2, name: 'Kenton Towne' },
{ id: 3, name: 'Therese Wunsch' },
{ id: 4, name: 'Benedict Kessler' },
{ id: 5, name: 'Katelyn Rohan' },
]
function Example() { const [selectedPerson, setSelectedPerson] = useState(people[0]) const [query, setQuery] = useState('') const filteredPeople = query === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.toLowerCase()) }) return (
<Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
<ComboboxInput aria-label="Assignee"
displayValue={(person) => person?.name}
onChange={(event) => setQuery(event.target.value)} />
<ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100">
{person.name}
</ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

値としてオブジェクトをバインドする場合は、Comboboxvalue と対応するComboboxOption の両方で、オブジェクトの同じインスタンスを使用することが重要です。そうでない場合、等しくなくなり、コンボボックスが正しく動作しなくなります。

同じオブジェクトの異なるインスタンスを簡単に操作するために、by プロップを使用して、オブジェクトの同一性を比較する代わりに、特定のフィールドでオブジェクトを比較することができます。

オブジェクトをvalue プロップに渡すと、by は存在する場合はid をデフォルトにしますが、任意のフィールドに設定できます。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const departments = [
  { name: 'Marketing', contact: 'Durward Reynolds' },
  { name: 'HR', contact: 'Kenton Towne' },
  { name: 'Sales', contact: 'Therese Wunsch' },
  { name: 'Finance', contact: 'Benedict Kessler' },
  { name: 'Customer service', contact: 'Katelyn Rohan' },
]

function DepartmentPicker({ selectedDepartment, onChange }) {
const [query, setQuery] = useState('') const filteredDepartments = query === '' ? departments : departments.filter((department) => { return department.name.toLowerCase().includes(query.toLowerCase()) }) return (
<Combobox value={selectedDepartment} by="name" onChange={onChange} onClose={() => setQuery('')}>
<ComboboxInput aria-label="Department" displayValue={(department) => department?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredDepartments.map((department) => ( <ComboboxOption key={department.id} value={department} className="data-[focus]:bg-blue-100"> {department.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

オブジェクトの比較方法を完全に制御したい場合は、独自の比較関数をby プロップに渡すこともできます。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const departments = [
  { id: 1, name: 'Marketing', contact: 'Durward Reynolds' },
  { id: 2, name: 'HR', contact: 'Kenton Towne' },
  { id: 3, name: 'Sales', contact: 'Therese Wunsch' },
  { id: 4, name: 'Finance', contact: 'Benedict Kessler' },
  { id: 5, name: 'Customer service', contact: 'Katelyn Rohan' },
]

function compareDepartments(a, b) {
return a.name.toLowerCase() === b.name.toLowerCase()
}
function DepartmentPicker({ selectedDepartment, onChange }) { const [query, setQuery] = useState('') const filteredDepartments = query === '' ? departments : departments.filter((department) => { return department.name.toLowerCase().includes(query.toLowerCase()) }) return (
<Combobox value={selectedDepartment} by={compareDepartments} onChange={onChange} onClose={() => setQuery('')}>
<ComboboxInput aria-label="Department" displayValue={(department) => department?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredDepartments.map((department) => ( <ComboboxOption key={department.id} value={department} className="data-[focus]:bg-blue-100"> {department.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

値としてオブジェクトをバインドすることは非常に一般的ですが、単純な文字列値を提供することもできます。

この場合、ComboboxInput からdisplayValue プロップを省略できます。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = ['Durward Reynolds', 'Kenton Towne', 'Therese Wunsch', 'Benedict Kessler', 'Katelyn Rohan']
function Example() { const [selectedPerson, setSelectedPerson] = useState(people[0]) const [query, setQuery] = useState('') const filteredPeople = query === '' ? people : people.filter((person) => { return person.toLowerCase().includes(query.toLowerCase()) }) return ( <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}> <ComboboxInput aria-label="Assignee" onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredPeople.map((person) => (
<ComboboxOption key={person} value={person} className="data-[focus]:bg-blue-100">
{person}
</ComboboxOption>
))} </ComboboxOptions> </Combobox> ) }

コンボボックスで複数の値を選択できるようにするには、multiple プロップを使用し、単一のオプションではなく配列をvalue に渡します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
const [selectedPeople, setSelectedPeople] = useState([people[0], people[1]])
const [query, setQuery] = useState('') const filteredPeople = query === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.toLowerCase()) }) return (
<Combobox multiple value={selectedPeople} onChange={setSelectedPeople} onClose={() => setQuery('')}>
{selectedPeople.length > 0 && ( <ul> {selectedPeople.map((person) => ( <li key={person.id}>{person.name}</li> ))} </ul> )} <ComboboxInput aria-label="Assignees" onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

オプションが追加または削除されるたびに、選択されたすべてのオプションを含む配列を使用して、onChange ハンドラーが呼び出されます。

immediate プロップを使用して、コンボボックス入力がフォーカスされたときにコンボボックスオプションをすぐに開きます。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
<Combobox immediate value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
<ComboboxInput aria-label="Assignee" displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

デフォルトでは、Combobox とそのサブコンポーネントはそれぞれ、そのコンポーネントに適したデフォルトの要素をレンダリングします。

例えば、ComboboxInputinput を、ComboboxButtonbutton を、ComboboxOptionsdiv を、ComboboxOptiondiv をレンダリングします。対照的に、Combobox要素をレンダリングしません。代わりに、その子要素を直接レンダリングします。

as プロップを使用して、コンポーネントを異なる要素または独自のカスタムコンポーネントとしてレンダリングします。カスタムコンポーネントがフォワードリファレンスされていることを確認して、Headless UI が正しく接続できるようにします。

import { Combobox, ComboboxButton, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { forwardRef, useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

let MyCustomButton = forwardRef(function (props, ref) {
return <button className="..." ref={ref} {...props} />
})
function Example() { const [selectedPerson, setSelectedPerson] = useState(people[0]) const [query, setQuery] = useState('') const filteredPeople = query === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.toLowerCase()) }) return (
<Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
<ComboboxInput aria-label="Assignee" displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxButton as={MyCustomButton}>Open</ComboboxButton>
<ComboboxOptions as="ul" anchor="bottom" className="border empty:invisible">
{filteredPeople.map((person) => (
<ComboboxOption as="li" key={person.id} value={person} className="data-[focus]:bg-blue-100">
{person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

ラッパー要素なしで子要素を直接レンダリングする要素を指定するには、Fragment を使用します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { Fragment, useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
      <ComboboxInput
as={Fragment}
aria-label="Assignee" displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} >
<input /> </ComboboxInput> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions> </Combobox> ) }

構築しているものによっては、ComboboxOptions の外部でアクティブなオプションに関する追加情報をレンダリングすることが理にかなう場合があります。例えば、コマンドパレットのコンテキスト内でのアクティブなオプションのプレビューなどです。このような状況では、activeOption レンダリングプロップ引数を読み取って、この情報にアクセスできます。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox value={selectedPerson} onChange={setSelectedPerson} onClose={() => setQuery('')}>
{({ activeOption }) => (
<> <ComboboxInput aria-label="Assignee" displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="border empty:invisible"> {filteredPeople.map((person) => ( <ComboboxOption key={person.id} value={person} className="data-[focus]:bg-blue-100"> {person.name} </ComboboxOption> ))} </ComboboxOptions>
{activeOption && <div>The currently focused user is: {activeOption.name}</div>}
</> )} </Combobox> ) }

activeOption は、現在フォーカスされているComboboxOptionvalue になります。

デフォルトでは、Combobox はすべてのオプションをDOMにレンダリングします。これは優れたデフォルトですが、非常に多くのオプションが与えられると、パフォーマンスの問題を引き起こす可能性があります。このような状況のために、仮想スクロールAPIを提供しています。

仮想スクロールを有効にするには、virtual.options プロップを介してオプションのリストをCombobox に提供し、各オプションのテンプレートとして機能するレンダリングプロップをComboboxOptions に提供します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
  // +1000 more people
]

function Example() {
  const [selectedPerson, setSelectedPerson] = useState(people[0])
  const [query, setQuery] = useState('')

  const filteredPeople =
    query === ''
      ? people
      : people.filter((person) => {
          return person.name.toLowerCase().includes(query.toLowerCase())
        })

  return (
    <Combobox
      value={selectedPerson}
virtual={{ options: filteredPeople }}
onChange={setSelectedPerson} onClose={() => setQuery('')} >
<ComboboxInput aria-label="Assignee" displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="w-[var(--input-width)] border empty:invisible">
{({ option: person }) => (
<ComboboxOption value={person} className="data-[focus]:bg-blue-100">
{person.name}
</ComboboxOption>
)}
</ComboboxOptions> </Combobox> ) }

特定のオプションが無効かどうかを指定するには、virtual.disabled プロップにコールバックを提供します。

import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from '@headlessui/react'
import { useState } from 'react'

const people = [
  { id: 1, name: 'Durward Reynolds', available: true },
  { id: 2, name: 'Kenton Towne', available: true },
  { id: 3, name: 'Therese Wunsch', available: true },
{ id: 4, name: 'Benedict Kessler', available: false },
{ id: 5, name: 'Katelyn Rohan', available: true }, // +1000 more people ] function Example() { const [selectedPerson, setSelectedPerson] = useState(people[0]) const [query, setQuery] = useState('') const filteredPeople = query === '' ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.toLowerCase()) }) return ( <Combobox value={selectedPerson} virtual={{ options: filteredPeople,
disabled: (person) => !person.available,
}}
onChange={setSelectedPerson} onClose={() => setQuery('')} >
<ComboboxInput aria-label="Assignee" displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxOptions anchor="bottom" className="w-[var(--input-width)] border empty:invisible"> {({ option: person }) => (
<ComboboxOption value={person} className="data-[focus]:bg-blue-100 data-[disabled]:opacity-50">
{person.name} </ComboboxOption> )} </ComboboxOptions> </Combobox> ) }

コマンド説明

下矢印または上矢印ComboboxInput がフォーカスされている場合

コンボボックスを開き、選択した項目にフォーカスします。

EnterSpace下矢印または上矢印ComboboxButton がフォーカスされている場合

コンボボックスを開き、入力にフォーカスして選択した項目を選択します。

Escコンボボックスが開いている場合

コンボボックスを閉じ、入力フィールドに選択した項目を復元します。

下矢印または上矢印コンボボックスが開いている場合

無効でない前の/次の項目にフォーカスします。

HomeまたはPageUpコンボボックスが開いている場合

無効でない最初の項目にフォーカスします。

EndまたはPageDownコンボボックスが開いている場合

無効でない最後の項目にフォーカスします。

Enterコンボボックスが開いている場合

現在の項目を選択します。

Enterコンボボックスが閉じられていてフォーム内にある場合

フォームを送信します。

Tabコンボボックスが開いている場合

現在フォーカスされている項目を選択し、コンボボックスを閉じます。

A~Zまたはa~zコンボボックスが開いている場合

リストをフィルタリングできるonChange を呼び出します。

メインのコンボボックスコンポーネント。

プロップデフォルト説明
asFragment
文字列 | コンポーネント

コンボボックスがレンダリングされるべき要素またはコンポーネント。comboboxです。

disabledfalse
ブール値

これを使用して、コンボボックスコンポーネント全体と関連する子要素を無効にします。

value
T

選択された値。

defaultValue
T

制御されていないコンポーネントとして使用する場合のデフォルト値。

by
keyof T | ((a: T, z: T) => boolean)

特定のフィールドでオブジェクトを比較するためにこれを使用するか、オブジェクトの比較方法を完全に制御するために独自の比較関数を渡します。

オブジェクトをvalue プロップに渡すと、by は存在する場合はid をデフォルトにします。

onChange
(value: T) => void

新しいオプションが選択されたときに呼び出す関数。

onClose
() => void

ドロップダウンが閉じられたときに呼び出す関数。

multiplefalse
ブール値

複数のオプションを選択できるかどうか。

name
文字列

フォーム内で使用する場合の名前。combobox内部。

form
文字列

が属するフォームのID。comboboxです。

name が提供されているがform が提供されていない場合、comboboxは、その状態を最も近い祖先のform 要素に追加します。

immediatefalse
ブール値

コンボボックス入力がフォーカスされたときに、コンボボックスがそのオプションをすぐに開くかどうか。

virtualnull
オブジェクト

仮想スクロールを構成します。

virtual.options
配列

仮想スクロールモードで表示するオプションのコレクション。

virtual.disablednull
(value: T) => boolean

仮想スクロールモードで特定のオプションが無効かどうかを判断するためのコールバック。

データ属性レンダリングプロップ説明
value

T

選択された値。

data-openopen

ブール値

かどうかcomboboxが開いています。

activeOption

T | null

フォーカスされているオプション、またはフォーカスされていない場合はnull

data-disableddisabled

ブール値

かどうかcombobox無効化されています。

activeIndex

Number | null

フォーカスされているオプションのインデックス、またはフォーカスされていない場合はnull

コンボボックスの入力欄。

プロップデフォルト説明
asinput
文字列 | コンポーネント

コンボボックスがレンダリングされるべき要素またはコンポーネント。コンボボックス入力です。

displayValue
(item: T) => string

value の文字列表現。

onChange
(event: Event) => void

入力値が変更されたときに呼び出される関数。

autoFocusfalse
ブール値

かどうかコンボボックス入力最初にレンダリングされたときにフォーカスを受け取るべきかどうか。

データ属性レンダリングプロップ説明
data-openopen

ブール値

かどうかcomboboxが開いています。

data-disableddisabled

ブール値

かどうかcombobox無効化されています。

data-focusfocus

ブール値

かどうかコンボボックス入力フォーカスされている。

data-hoverhover

ブール値

かどうかコンボボックス入力ホバーされている。

data-autofocusautofocus

ブール値

autoFocus プロパティがtrueに設定されていたかどうか。

コンボボックスのボタン。

プロップデフォルト説明
asbutton
文字列 | コンポーネント

コンボボックスがレンダリングされるべき要素またはコンポーネント。コンボボックスボタンです。

autoFocusfalse
ブール値

かどうかコンボボックスボタン最初にレンダリングされたときにフォーカスを受け取るべきかどうか。

データ属性レンダリングプロップ説明
value

T

選択された値。

data-openopen

ブール値

かどうかcomboboxが開いています。

data-disableddisabled

ブール値

かどうかコンボボックスボタン無効化されています。

data-focusfocus

ブール値

かどうかコンボボックスボタンフォーカスされている。

data-hoverhover

ブール値

かどうかコンボボックスボタンホバーされている。

data-activeactive

ブール値

かどうかコンボボックスボタンアクティブまたは押された状態である。

カスタムコンボボックスのオプションリストを直接ラップするコンポーネント。

プロップデフォルト説明
asdiv
文字列 | コンポーネント

コンボボックスがレンダリングされるべき要素またはコンポーネント。コンボボックスオプションです。

transitionfalse
ブール値

data-closed data-enterdata-leaveなどのトランジション属性を要素がレンダリングするかどうか。

anchor
オブジェクト

ドロップダウンが入力欄にどのようにアンカーされるかを設定します。

anchor.tobottom
文字列

トリガーに対してコンボボックスオプションどこに配置するか。

toprightbottomleft の値を使用して、適切なエッジに沿って中央揃えするか、startまたはendと組み合わせて、 コンボボックスオプションを特定のコーナー(例:top startまたはbottom end)に揃えます。コンボボックスオプション

anchor.gap0
Number | String

コンボボックス入力の間隔。コンボボックスオプション.

--anchor-gap CSS変数でも制御できます。

anchor.offset0
Number | String

を元の位置からコンボボックスオプションずらすべき距離。

--anchor-offset CSS変数でも制御できます。

anchor.padding0
Number | String

とビューポートの間の最小間隔。コンボボックスオプション

--anchor-padding CSS変数でも制御できます。

staticfalse
ブール値

要素が内部的に管理されている開閉状態を無視するかどうか。

unmounttrue
ブール値

開閉状態に基づいて、要素をアンマウントするか、非表示にするかどうか。

portalfalse
ブール値

要素をポータルでレンダリングするかどうか。

anchorプロパティが設定されている場合、自動的にtrueに設定されます。

modaltrue
ブール値

スクロールロック、フォーカストラップ、他の要素を不活性にするなどのアクセシビリティ機能を有効にするかどうか。 inert.

データ属性レンダリングプロップ説明
data-openopen

ブール値

かどうかcomboboxが開いています。

コンボボックス内の各アイテムをラップするために使用します。

プロップデフォルト説明
asdiv
文字列 | コンポーネント

コンボボックスがレンダリングされるべき要素またはコンポーネント。コンボボックスオプションです。

value
T

オプションの値。

disabledfalse
ブール値

かどうかコンボボックスオプション無効化されていますキーボードナビゲーションとARIAの目的で。.

order
Number

オプションリストにおけるオプションの順序。パフォーマンス向上に使用されます。

仮想スクロールを使用する場合は関係ありません。

データ属性レンダリングプロップ説明
data-selectedselected

ブール値

かどうかコンボボックスオプション選択されています。

data-disableddisabled

ブール値

かどうかコンボボックスオプション無効化されています。

data-focusfocus

ブール値

かどうかコンボボックスオプションフォーカスされている。

Headless UI を使用した、事前にデザインされた Tailwind CSS コンボボックスコンポーネントの例 に興味がある場合はTailwind UI をご覧ください。これは、私たちによって構築された、美しくデザインされ、専門的に作られたコンポーネントのコレクションです。

これは、このようなオープンソースプロジェクトへの私たちの活動を支援する素晴らしい方法であり、それらを改善し、維持することを可能にします。