# Input

A text field with optional prefix, suffix, and clear button.

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

## Overview

An input is a filled text field composed from a group, an optional prefix and suffix, a control, and a clear button.

Give every control a visible `Field.Label`. A placeholder is not a label.

## Examples

### Basic

```tsx
import { Field, Input } from '@tempoxyz/ds'

export function Demo() {
  return (
    <Field.Root>
      <Field.Label>Amount</Field.Label>
      <Input.Group>
        <Input.Prefix>$</Input.Prefix>
        <Input.Control placeholder="0.00" />
        <Input.Suffix>USD</Input.Suffix>
      </Input.Group>
    </Field.Root>
  )
}
```

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

### Size

Set `size` on `Input.Group` to `small`, `medium`, or `large`. It defaults to `medium` and matches a `Button` or `Textarea` of the same size.

```tsx
import { Button, Field, Input, style, Textarea } from '@tempoxyz/ds'
import { Search } from '@tempoxyz/ds/icons'

export function Demo() {
  return (
    <div {...styles.sections()}>
      {(['small', 'medium', 'large'] as const).map((size) => (
        <div key={size} {...styles.grid()}>
          <Field.Root>
            <Field.Label>Amount</Field.Label>
            <Input.Group size={size}>
              <Input.Prefix>$</Input.Prefix>
              <Input.Control placeholder="0.00" />
              <Input.Suffix>USD</Input.Suffix>
            </Input.Group>
          </Field.Root>
          <Field.Root>
            <Field.Label>Search</Field.Label>
            <Input.Group size={size} variant="search">
              <Input.Prefix>
                <Search />
              </Input.Prefix>
              <Input.Control aria-label="Search" defaultValue="tempo" type="search" />
              <Input.Clear />
            </Input.Group>
          </Field.Root>
          <Field.Root>
            <Field.Label>Notes</Field.Label>
            <Textarea placeholder="Add a note" resize="none" rows={1} size={size} />
          </Field.Root>
          <Field.Root>
            <Field.Label>Email</Field.Label>
            <div {...styles.row()}>
              <Input.Group size={size} {...styles.fill()}>
                <Input.Control placeholder="alice@example.com" type="email" />
              </Input.Group>
              <Button size={size}>Subscribe</Button>
            </div>
          </Field.Root>
        </div>
      ))}
    </div>
  )
}

namespace styles {
  export const fill = style({ flex: '1 !custom', minWidth: '0 !custom' })

  export const grid = style({
    alignItems: 'start',
    display: 'grid',
    gap: '6',
    gridTemplateColumns: 'repeat(auto-fill, minmax(min(280px, 100%), 1fr)) !custom',
  })

  export const row = style({ alignItems: 'center', display: 'flex', gap: '2' })

  export const sections = style({ display: 'grid', gap: '10' })
}
```

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

### Variant

Set `variant` on `Input.Group` to `default`, `search`, or `card`. It defaults to `default`, and a `card` or `stacked` `Field.Root` styles the group inside.

```tsx
import { Field, Input, style, Textarea } from '@tempoxyz/ds'
import { Search } from '@tempoxyz/ds/icons'

export function Demo() {
  return (
    <div {...styles.grid()}>
      <Field.Root>
        <Field.Label>Search</Field.Label>
        <Input.Group variant="search">
          <Input.Prefix>
            <Search />
          </Input.Prefix>
          <Input.Control aria-label="Search" defaultValue="tempo" type="search" />
          <Input.Clear />
        </Input.Group>
      </Field.Root>
      <Field.Root variant="stacked">
        <Field.Label>Email</Field.Label>
        <Input.Group>
          <Input.Control placeholder="alice@example.com" type="email" />
        </Input.Group>
      </Field.Root>
      <Field.Root variant="card">
        <Field.Label>You send</Field.Label>
        <Input.Group>
          <Input.Prefix>$</Input.Prefix>
          <Input.Control defaultValue="250.00" />
          <Input.Suffix>USDC</Input.Suffix>
        </Input.Group>
      </Field.Root>
      <Field.Root variant="card">
        <Field.Label>Memo</Field.Label>
        <Textarea defaultValue="Rent for May" />
      </Field.Root>
    </div>
  )
}

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

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

### Invalid

Set `invalid` or `success` on `Input.Group`, or inherit either state from `Field.Root`. Entering a state after mount plays a one-shot nudge or pop.

```tsx
import { Field, Input, style } from '@tempoxyz/ds'
import { useState } from 'react'

export function Demo() {
  return (
    <div {...styles.grid()}>
      <Send />
      <Field.Root
        validate={(value) => (String(value).includes('@') ? null : 'Enter a valid email.')}
        validationMode="onChange"
      >
        <Field.Label>Email</Field.Label>
        <Input.Group>
          <Input.Control defaultValue="alice@example.com" type="email" />
        </Input.Group>
        <Field.Description>Remove the @ to swap in the error.</Field.Description>
        <Field.Error />
      </Field.Root>
      <Field.Root invalid variant="stacked">
        <Field.Label>Email</Field.Label>
        <Input.Group>
          <Input.Control defaultValue="alice" type="email" />
        </Input.Group>
        <Field.Description>Used for receipts.</Field.Description>
        <Field.Error match>Enter a valid email.</Field.Error>
      </Field.Root>
    </div>
  )
}

function Send() {
  const [amount, setAmount] = useState('')
  const invalid = Number(amount) > 1240.5

  return (
    <Field.Root invalid={invalid} success={amount !== '' && !invalid}>
      <Field.Label>You send</Field.Label>
      <Input.Group>
        <Input.Prefix>$</Input.Prefix>
        <Input.Control inputMode="decimal" onValueChange={setAmount} placeholder="0.00" />
        <Input.Suffix>USDC</Input.Suffix>
      </Input.Group>
      <Field.Description>Balance 1,240.50 USDC</Field.Description>
      <Field.Error match={invalid}>Exceeds your balance.</Field.Error>
    </Field.Root>
  )
}

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

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

### Disabled

```tsx
import { Field, Input } from '@tempoxyz/ds'

export function Demo() {
  return (
    <Field.Root disabled>
      <Field.Label>Disabled</Field.Label>
      <Input.Group>
        <Input.Control defaultValue="Locked" />
      </Input.Group>
    </Field.Root>
  )
}
```

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

## Guidelines

TODO: Write guidelines for Input.
