# Select

Opens a list of options for choosing one value.

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

## Overview

A select is a trigger that opens a listbox of options, for choosing one value from a short list.

Prefer `ChoiceGroup` when the options fit on screen.

## Examples

### Basic

```tsx
import { Field, Select, style } from '@tempoxyz/ds'
import { Coins, Ethereum, Wallet } from '@tempoxyz/ds/icons'
import { useState } from 'react'

const assets = [
  { balance: '1,240.50', icon: <Coins />, label: 'pathUSD', value: 'pathusd' },
  { balance: '845.00', icon: <Ethereum />, label: 'USDC.e', value: 'usdce' },
  { balance: '0.00', icon: <Wallet />, label: 'Tether', value: 'usdt' },
]

const items = Object.fromEntries(assets.map((asset) => [asset.value, asset.label]))

function Example(props: Pick<Select.Trigger.Props, 'disabled' | 'invalid' | 'size' | 'variant'>) {
  const [value, setValue] = useState<string | null>('pathusd')

  return (
    <Select.Root items={items} onValueChange={setValue} value={value}>
      <Select.Trigger aria-label="Asset" {...props} {...styles.trigger()}>
        <Select.Value placeholder="Select an asset" />
        <Select.Icon />
      </Select.Trigger>
      <Select.Popup size={props.size}>
        {assets.map((asset) => (
          <Select.Item key={asset.value} label={asset.label} value={asset.value}>
            <Select.ItemPrefix>{asset.icon}</Select.ItemPrefix>
            <Select.ItemText>{asset.label}</Select.ItemText>
            <Select.ItemSuffix>{asset.balance}</Select.ItemSuffix>
          </Select.Item>
        ))}
        <Select.Separator />
        <Select.Group>
          <Select.GroupLabel>Coming soon</Select.GroupLabel>
          <Select.Item disabled value="other">
            <Select.ItemText>Other</Select.ItemText>
          </Select.Item>
        </Select.Group>
      </Select.Popup>
    </Select.Root>
  )
}

export function Demo() {
  return (
    <Field.Root>
      <Field.Label>Asset</Field.Label>
      <Example />
    </Field.Root>
  )
}

namespace styles {
  export const trigger = style({ maxWidth: '320px !custom' })
}
```

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

### Size

Set `size` on `Select.Trigger` and `Select.Popup` to `small`, `medium`, or `large`. It defaults to `medium` and matches an `Input` of the same size.

```tsx
import { Input, Select, style } from '@tempoxyz/ds'
import { Coins, Ethereum, Wallet } from '@tempoxyz/ds/icons'
import { useState } from 'react'

const assets = [
  { balance: '1,240.50', icon: <Coins />, label: 'pathUSD', value: 'pathusd' },
  { balance: '845.00', icon: <Ethereum />, label: 'USDC.e', value: 'usdce' },
  { balance: '0.00', icon: <Wallet />, label: 'Tether', value: 'usdt' },
]

const items = Object.fromEntries(assets.map((asset) => [asset.value, asset.label]))

function Example(props: Pick<Select.Trigger.Props, 'disabled' | 'invalid' | 'size' | 'variant'>) {
  const [value, setValue] = useState<string | null>('pathusd')

  return (
    <Select.Root items={items} onValueChange={setValue} value={value}>
      <Select.Trigger aria-label="Asset" {...props} {...styles.trigger()}>
        <Select.Value placeholder="Select an asset" />
        <Select.Icon />
      </Select.Trigger>
      <Select.Popup size={props.size}>
        {assets.map((asset) => (
          <Select.Item key={asset.value} label={asset.label} value={asset.value}>
            <Select.ItemPrefix>{asset.icon}</Select.ItemPrefix>
            <Select.ItemText>{asset.label}</Select.ItemText>
            <Select.ItemSuffix>{asset.balance}</Select.ItemSuffix>
          </Select.Item>
        ))}
        <Select.Separator />
        <Select.Group>
          <Select.GroupLabel>Coming soon</Select.GroupLabel>
          <Select.Item disabled value="other">
            <Select.ItemText>Other</Select.ItemText>
          </Select.Item>
        </Select.Group>
      </Select.Popup>
    </Select.Root>
  )
}

export function Demo() {
  return (
    <div {...styles.grid()}>
      {(['small', 'medium', 'large'] as const).map((size) => (
        <div key={size} {...styles.row()}>
          <Example size={size} />
          <Input.Group size={size}>
            <Input.Control aria-label="Amount" placeholder="Amount" />
          </Input.Group>
        </div>
      ))}
    </div>
  )
}

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

  export const row = style({
    display: 'grid',
    gap: '3',
    gridColumn: '1 / -1 !custom',
    gridTemplateColumns: 'repeat(2, minmax(0, 320px)) !custom',
  })

  export const trigger = style({ maxWidth: '320px !custom' })
}
```

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

### Variant

Set `variant` on `Select.Trigger` to `default` or `search`. It defaults to `default`.

```tsx
import { Select, style } from '@tempoxyz/ds'
import { Coins, Ethereum, Wallet } from '@tempoxyz/ds/icons'
import { useState } from 'react'

const assets = [
  { balance: '1,240.50', icon: <Coins />, label: 'pathUSD', value: 'pathusd' },
  { balance: '845.00', icon: <Ethereum />, label: 'USDC.e', value: 'usdce' },
  { balance: '0.00', icon: <Wallet />, label: 'Tether', value: 'usdt' },
]

const items = Object.fromEntries(assets.map((asset) => [asset.value, asset.label]))

function Example(props: Pick<Select.Trigger.Props, 'disabled' | 'invalid' | 'size' | 'variant'>) {
  const [value, setValue] = useState<string | null>('pathusd')

  return (
    <Select.Root items={items} onValueChange={setValue} value={value}>
      <Select.Trigger aria-label="Asset" {...props} {...styles.trigger()}>
        <Select.Value placeholder="Select an asset" />
        <Select.Icon />
      </Select.Trigger>
      <Select.Popup size={props.size}>
        {assets.map((asset) => (
          <Select.Item key={asset.value} label={asset.label} value={asset.value}>
            <Select.ItemPrefix>{asset.icon}</Select.ItemPrefix>
            <Select.ItemText>{asset.label}</Select.ItemText>
            <Select.ItemSuffix>{asset.balance}</Select.ItemSuffix>
          </Select.Item>
        ))}
        <Select.Separator />
        <Select.Group>
          <Select.GroupLabel>Coming soon</Select.GroupLabel>
          <Select.Item disabled value="other">
            <Select.ItemText>Other</Select.ItemText>
          </Select.Item>
        </Select.Group>
      </Select.Popup>
    </Select.Root>
  )
}

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

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

  export const trigger = style({ maxWidth: '320px !custom' })
}
```

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

### Field

Wrap the select in a `Field.Root` to label it. The field's `variant` sets the layout.

```tsx
import { Field, Select, style } from '@tempoxyz/ds'
import { Coins, Ethereum, Wallet } from '@tempoxyz/ds/icons'
import { useState } from 'react'

const assets = [
  { balance: '1,240.50', icon: <Coins />, label: 'pathUSD', value: 'pathusd' },
  { balance: '845.00', icon: <Ethereum />, label: 'USDC.e', value: 'usdce' },
  { balance: '0.00', icon: <Wallet />, label: 'Tether', value: 'usdt' },
]

const items = Object.fromEntries(assets.map((asset) => [asset.value, asset.label]))

function Example(props: Pick<Select.Trigger.Props, 'disabled' | 'invalid' | 'size' | 'variant'>) {
  const [value, setValue] = useState<string | null>('pathusd')

  return (
    <Select.Root items={items} onValueChange={setValue} value={value}>
      <Select.Trigger aria-label="Asset" {...props} {...styles.trigger()}>
        <Select.Value placeholder="Select an asset" />
        <Select.Icon />
      </Select.Trigger>
      <Select.Popup size={props.size}>
        {assets.map((asset) => (
          <Select.Item key={asset.value} label={asset.label} value={asset.value}>
            <Select.ItemPrefix>{asset.icon}</Select.ItemPrefix>
            <Select.ItemText>{asset.label}</Select.ItemText>
            <Select.ItemSuffix>{asset.balance}</Select.ItemSuffix>
          </Select.Item>
        ))}
        <Select.Separator />
        <Select.Group>
          <Select.GroupLabel>Coming soon</Select.GroupLabel>
          <Select.Item disabled value="other">
            <Select.ItemText>Other</Select.ItemText>
          </Select.Item>
        </Select.Group>
      </Select.Popup>
    </Select.Root>
  )
}

export function Demo() {
  return (
    <div {...styles.grid()}>
      <Field.Root>
        <Field.Label>Asset</Field.Label>
        <Example />
      </Field.Root>
      <Field.Root variant="stacked">
        <Field.Label>Asset</Field.Label>
        <Example />
      </Field.Root>
      <Field.Root variant="card">
        <Field.Label>You send</Field.Label>
        <Example />
      </Field.Root>
    </div>
  )
}

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

  export const trigger = style({ maxWidth: '320px !custom' })
}
```

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

### Invalid

Set `invalid` on `Select.Trigger` to mark the value as invalid.

```tsx
import { Select, style } from '@tempoxyz/ds'
import { Coins, Ethereum, Wallet } from '@tempoxyz/ds/icons'
import { useState } from 'react'

const assets = [
  { balance: '1,240.50', icon: <Coins />, label: 'pathUSD', value: 'pathusd' },
  { balance: '845.00', icon: <Ethereum />, label: 'USDC.e', value: 'usdce' },
  { balance: '0.00', icon: <Wallet />, label: 'Tether', value: 'usdt' },
]

const items = Object.fromEntries(assets.map((asset) => [asset.value, asset.label]))

function Example(props: Pick<Select.Trigger.Props, 'disabled' | 'invalid' | 'size' | 'variant'>) {
  const [value, setValue] = useState<string | null>('pathusd')

  return (
    <Select.Root items={items} onValueChange={setValue} value={value}>
      <Select.Trigger aria-label="Asset" {...props} {...styles.trigger()}>
        <Select.Value placeholder="Select an asset" />
        <Select.Icon />
      </Select.Trigger>
      <Select.Popup size={props.size}>
        {assets.map((asset) => (
          <Select.Item key={asset.value} label={asset.label} value={asset.value}>
            <Select.ItemPrefix>{asset.icon}</Select.ItemPrefix>
            <Select.ItemText>{asset.label}</Select.ItemText>
            <Select.ItemSuffix>{asset.balance}</Select.ItemSuffix>
          </Select.Item>
        ))}
        <Select.Separator />
        <Select.Group>
          <Select.GroupLabel>Coming soon</Select.GroupLabel>
          <Select.Item disabled value="other">
            <Select.ItemText>Other</Select.ItemText>
          </Select.Item>
        </Select.Group>
      </Select.Popup>
    </Select.Root>
  )
}

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

namespace styles {
  export const trigger = style({ maxWidth: '320px !custom' })
}
```

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

### Disabled

```tsx
import { Select, style } from '@tempoxyz/ds'
import { Coins, Ethereum, Wallet } from '@tempoxyz/ds/icons'
import { useState } from 'react'

const assets = [
  { balance: '1,240.50', icon: <Coins />, label: 'pathUSD', value: 'pathusd' },
  { balance: '845.00', icon: <Ethereum />, label: 'USDC.e', value: 'usdce' },
  { balance: '0.00', icon: <Wallet />, label: 'Tether', value: 'usdt' },
]

const items = Object.fromEntries(assets.map((asset) => [asset.value, asset.label]))

function Example(props: Pick<Select.Trigger.Props, 'disabled' | 'invalid' | 'size' | 'variant'>) {
  const [value, setValue] = useState<string | null>('pathusd')

  return (
    <Select.Root items={items} onValueChange={setValue} value={value}>
      <Select.Trigger aria-label="Asset" {...props} {...styles.trigger()}>
        <Select.Value placeholder="Select an asset" />
        <Select.Icon />
      </Select.Trigger>
      <Select.Popup size={props.size}>
        {assets.map((asset) => (
          <Select.Item key={asset.value} label={asset.label} value={asset.value}>
            <Select.ItemPrefix>{asset.icon}</Select.ItemPrefix>
            <Select.ItemText>{asset.label}</Select.ItemText>
            <Select.ItemSuffix>{asset.balance}</Select.ItemSuffix>
          </Select.Item>
        ))}
        <Select.Separator />
        <Select.Group>
          <Select.GroupLabel>Coming soon</Select.GroupLabel>
          <Select.Item disabled value="other">
            <Select.ItemText>Other</Select.ItemText>
          </Select.Item>
        </Select.Group>
      </Select.Popup>
    </Select.Root>
  )
}

export function Demo() {
  return <Example disabled />
}

namespace styles {
  export const trigger = style({ maxWidth: '320px !custom' })
}
```

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

## Guidelines

TODO: Write guidelines for Select.
