# Combobox

A text input that filters a list of fixed options as you type.

[View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/Combobox.tsx)

## Overview

A combobox is a text input with a listbox popup. It filters a long list of fixed options as you type.

Use `Select` for a short list, and `Autocomplete` when free text is a valid value.

## Examples

### Basic

```tsx
import { Combobox } from '@tempoxyz/ds'
import { Search } from '@tempoxyz/ds/icons'
import { useState } from 'react'
import cbBtc from '../../coins/CbBTC.svg'
import ethereumEth from '../../coins/EthereumETH.svg'
import pathUsd from '../../coins/PathUSD.svg'
import usdce from '../../coins/USDCe.svg'
import usdt0 from '../../coins/USDT0.svg'

const coins: Record<string, string> = {
  'Bridged ETH': ethereumEth,
  pathUSD: pathUsd,
  Tether: usdt0,
  'USDC.e': usdce,
  USDT0: usdt0,
  'Wrapped BTC': cbBtc,
}

const assets = ['pathUSD', 'USDC.e', 'Tether', 'USDT0', 'Bridged ETH', 'Wrapped BTC']

function Coin(props: { asset: string }) {
  return <img alt="" height={20} src={coins[props.asset]} width={20} />
}

function Single(props: Pick<Combobox.InputGroup.Props, 'invalid' | 'size' | 'variant'>) {
  const [value, setValue] = useState<string | null>('pathUSD')

  return (
    <Combobox.Root items={assets} onValueChange={setValue} value={value}>
      <Combobox.InputGroup {...props}>
        <Combobox.Prefix>{value ? <Coin asset={value} /> : <Search />}</Combobox.Prefix>
        <Combobox.Input aria-label="Asset" placeholder="Search assets" />
        <Combobox.Clear />
        <Combobox.Trigger />
      </Combobox.InputGroup>
      <Combobox.Popup>
        <Combobox.Empty>No results</Combobox.Empty>
        <Combobox.List>
          {(asset: string) => (
            <Combobox.Item key={asset} value={asset}>
              <Combobox.ItemPrefix>
                <Coin asset={asset} />
              </Combobox.ItemPrefix>
              <Combobox.ItemText>{asset}</Combobox.ItemText>
            </Combobox.Item>
          )}
        </Combobox.List>
      </Combobox.Popup>
    </Combobox.Root>
  )
}

export function Demo() {
  return <Single />
}
```

[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Combobox/Basic.tsx)

### Size

Set `size` on `Combobox.InputGroup` to `small`, `medium`, or `large`. It defaults to `medium`.

```tsx
import { Combobox, style } from '@tempoxyz/ds'
import { Search } from '@tempoxyz/ds/icons'
import { useState } from 'react'
import cbBtc from '../../coins/CbBTC.svg'
import ethereumEth from '../../coins/EthereumETH.svg'
import pathUsd from '../../coins/PathUSD.svg'
import usdce from '../../coins/USDCe.svg'
import usdt0 from '../../coins/USDT0.svg'

const coins: Record<string, string> = {
  'Bridged ETH': ethereumEth,
  pathUSD: pathUsd,
  Tether: usdt0,
  'USDC.e': usdce,
  USDT0: usdt0,
  'Wrapped BTC': cbBtc,
}

const assets = ['pathUSD', 'USDC.e', 'Tether', 'USDT0', 'Bridged ETH', 'Wrapped BTC']

function Coin(props: { asset: string }) {
  return <img alt="" height={20} src={coins[props.asset]} width={20} />
}

function Single(props: Pick<Combobox.InputGroup.Props, 'invalid' | 'size' | 'variant'>) {
  const [value, setValue] = useState<string | null>('pathUSD')

  return (
    <Combobox.Root items={assets} onValueChange={setValue} value={value}>
      <Combobox.InputGroup {...props}>
        <Combobox.Prefix>{value ? <Coin asset={value} /> : <Search />}</Combobox.Prefix>
        <Combobox.Input aria-label="Asset" placeholder="Search assets" />
        <Combobox.Clear />
        <Combobox.Trigger />
      </Combobox.InputGroup>
      <Combobox.Popup>
        <Combobox.Empty>No results</Combobox.Empty>
        <Combobox.List>
          {(asset: string) => (
            <Combobox.Item key={asset} value={asset}>
              <Combobox.ItemPrefix>
                <Coin asset={asset} />
              </Combobox.ItemPrefix>
              <Combobox.ItemText>{asset}</Combobox.ItemText>
            </Combobox.Item>
          )}
        </Combobox.List>
      </Combobox.Popup>
    </Combobox.Root>
  )
}

export function Demo() {
  return (
    <div {...styles.grid()}>
      <Single size="small" />
      <Single />
      <Single size="large" />
    </div>
  )
}

namespace styles {
  export const grid = style({
    alignItems: 'start',
    display: 'grid',
    gap: '3',
    gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr)) !custom',
  })
}
```

[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Combobox/Size.tsx)

### Variant

Set `variant` on `Combobox.InputGroup` to `filled` or `outlined`. It defaults to `filled`.

```tsx
import { Combobox, style } from '@tempoxyz/ds'
import { Search } from '@tempoxyz/ds/icons'
import { useState } from 'react'
import cbBtc from '../../coins/CbBTC.svg'
import ethereumEth from '../../coins/EthereumETH.svg'
import pathUsd from '../../coins/PathUSD.svg'
import usdce from '../../coins/USDCe.svg'
import usdt0 from '../../coins/USDT0.svg'

const coins: Record<string, string> = {
  'Bridged ETH': ethereumEth,
  pathUSD: pathUsd,
  Tether: usdt0,
  'USDC.e': usdce,
  USDT0: usdt0,
  'Wrapped BTC': cbBtc,
}

const assets = ['pathUSD', 'USDC.e', 'Tether', 'USDT0', 'Bridged ETH', 'Wrapped BTC']

function Coin(props: { asset: string }) {
  return <img alt="" height={20} src={coins[props.asset]} width={20} />
}

function Single(props: Pick<Combobox.InputGroup.Props, 'invalid' | 'size' | 'variant'>) {
  const [value, setValue] = useState<string | null>('pathUSD')

  return (
    <Combobox.Root items={assets} onValueChange={setValue} value={value}>
      <Combobox.InputGroup {...props}>
        <Combobox.Prefix>{value ? <Coin asset={value} /> : <Search />}</Combobox.Prefix>
        <Combobox.Input aria-label="Asset" placeholder="Search assets" />
        <Combobox.Clear />
        <Combobox.Trigger />
      </Combobox.InputGroup>
      <Combobox.Popup>
        <Combobox.Empty>No results</Combobox.Empty>
        <Combobox.List>
          {(asset: string) => (
            <Combobox.Item key={asset} value={asset}>
              <Combobox.ItemPrefix>
                <Coin asset={asset} />
              </Combobox.ItemPrefix>
              <Combobox.ItemText>{asset}</Combobox.ItemText>
            </Combobox.Item>
          )}
        </Combobox.List>
      </Combobox.Popup>
    </Combobox.Root>
  )
}

export function Demo() {
  return (
    <div {...styles.grid()}>
      <Single />
      <Single variant="outlined" />
    </div>
  )
}

namespace styles {
  export const grid = style({
    alignItems: 'start',
    display: 'grid',
    gap: '3',
    gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr)) !custom',
  })
}
```

[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Combobox/Variant.tsx)

### Invalid

Set `invalid` on `Combobox.InputGroup` to style the frame, and `aria-invalid` on the input to expose the state.

```tsx
import { Combobox } from '@tempoxyz/ds'
import { Search } from '@tempoxyz/ds/icons'
import { useState } from 'react'
import cbBtc from '../../coins/CbBTC.svg'
import ethereumEth from '../../coins/EthereumETH.svg'
import pathUsd from '../../coins/PathUSD.svg'
import usdce from '../../coins/USDCe.svg'
import usdt0 from '../../coins/USDT0.svg'

const coins: Record<string, string> = {
  'Bridged ETH': ethereumEth,
  pathUSD: pathUsd,
  Tether: usdt0,
  'USDC.e': usdce,
  USDT0: usdt0,
  'Wrapped BTC': cbBtc,
}

const assets = ['pathUSD', 'USDC.e', 'Tether', 'USDT0', 'Bridged ETH', 'Wrapped BTC']

function Coin(props: { asset: string }) {
  return <img alt="" height={20} src={coins[props.asset]} width={20} />
}

function Single(props: Pick<Combobox.InputGroup.Props, 'invalid' | 'size' | 'variant'>) {
  const [value, setValue] = useState<string | null>('pathUSD')

  return (
    <Combobox.Root items={assets} onValueChange={setValue} value={value}>
      <Combobox.InputGroup {...props}>
        <Combobox.Prefix>{value ? <Coin asset={value} /> : <Search />}</Combobox.Prefix>
        <Combobox.Input aria-label="Asset" placeholder="Search assets" />
        <Combobox.Clear />
        <Combobox.Trigger />
      </Combobox.InputGroup>
      <Combobox.Popup>
        <Combobox.Empty>No results</Combobox.Empty>
        <Combobox.List>
          {(asset: string) => (
            <Combobox.Item key={asset} value={asset}>
              <Combobox.ItemPrefix>
                <Coin asset={asset} />
              </Combobox.ItemPrefix>
              <Combobox.ItemText>{asset}</Combobox.ItemText>
            </Combobox.Item>
          )}
        </Combobox.List>
      </Combobox.Popup>
    </Combobox.Root>
  )
}

export function Demo() {
  return <Single invalid />
}
```

[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Combobox/Invalid.tsx)

### Multiple

Set `multiple` on `Combobox.Root` to select several values, shown as chips.

```tsx
import { Combobox } from '@tempoxyz/ds'
import { useState } from 'react'

const assets = ['pathUSD', 'USDC.e', 'Tether', 'USDT0', 'Bridged ETH', 'Wrapped BTC']

function Multiple() {
  const [value, setValue] = useState(['pathUSD', 'USDC.e'])

  return (
    <Combobox.Root items={assets} multiple onValueChange={setValue} value={value}>
      <Combobox.InputGroup>
        <Combobox.Value>
          {(selected: string[]) => (
            <Combobox.Chips aria-label="Selected assets">
              {selected.map((asset) => (
                <Combobox.Chip key={asset}>
                  {asset}
                  <Combobox.ChipRemove aria-label={`Remove ${asset}`} />
                </Combobox.Chip>
              ))}
              <Combobox.Input
                aria-label="Assets"
                placeholder={selected.length ? '' : 'Add assets'}
              />
            </Combobox.Chips>
          )}
        </Combobox.Value>
      </Combobox.InputGroup>
      <Combobox.Popup>
        <Combobox.Empty>No results</Combobox.Empty>
        <Combobox.List>
          {(asset: string) => (
            <Combobox.Item key={asset} value={asset}>
              <Combobox.ItemText>{asset}</Combobox.ItemText>
            </Combobox.Item>
          )}
        </Combobox.List>
      </Combobox.Popup>
    </Combobox.Root>
  )
}

export function Demo() {
  return <Multiple />
}
```

[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Combobox/Multiple.tsx)

## Guidelines

TODO: Write guidelines for Combobox.
