# TimePicker

O `TimeField` com o painel de escolher a hora.

Mesmo valor, mesmo formato, mesmo teclado: tudo que a página do `TimeField` diz
sobre `"14:30"`, sobre `25:99`, sobre `step`, `min` e `max` continua valendo
aqui. O que esta peça acrescenta é o relógio no canto do campo, e o que ele
abre.

```tsx
const [entrega, setEntrega] = useState('09:00')

<TimePicker value={entrega} onValueChange={setEntrega} min="08:00" max="18:00" step={30} />
```

## Duas colunas, e não uma lista

O painel tem uma coluna de horas e uma de minutos, e não uma lista única de
horários. Uma lista só parece mais simples até `step={5}`, quando ela vira 288
linhas para rolar; em duas colunas são 24 e 12, e a hora é sempre a primeira
decisão.

Os minutos vêm de `60 / step`, então o passo é mais legível quando divide 60:
1, 5, 15 e 30 são os que a peça foi desenhada para servir.

O texto acima de cada coluna é o nome dela: a coluna o toma por
`aria-labelledby`, e o texto fica `aria-hidden` para não ser lido solto logo
antes de a caixa ser anunciada com a mesma palavra. Antes eram duas: um
`<span>Hora</span>` e um `aria-label="Hora"`, ditos em seguida, nas duas
colunas. Trocar `labels.hours` ou `labels.minutes` troca os dois de uma vez,
porque agora é um texto só.

**A hora não fecha o painel; o minuto fecha.** O minuto é a última decisão, e
fechar antes dela obrigaria a reabrir o painel na metade das escolhas. Escolher
outra hora preserva o minuto que já estava escolhido: quem troca 14:30 por 16
quer 16:30, e não 16:00, inclusive quando o minuto foi digitado fora da grade,
porque `14:07` foi escolha de alguém. Se a hora nova jogar o horário para fora
da janela, ele encosta no limite dela em vez de sair.

A seta do teclado anda a coluna e leva a seleção junto, sem fechar nada; Enter
e o clique fecham quando estão no minuto. Ao abrir, a coluna já rola até a hora
escolhida.

## A janela recorta o painel

`min` e `max` tiram do painel o que está fora: com `min="08:00"` e `max="10:00"`
a coluna de horas vai de 08 a 10, e às 10 sobra só o minuto `00`. Recortar é
melhor do que oferecer e reclamar depois: a janela de entrega é uma regra da
loja, e não um erro da pessoa.

A janela **recorta a grade, não a desloca**: com `min="08:10"` e `step={15}`, o
primeiro horário oferecido é `08:15`. A grade sai sempre da meia-noite, para
que `08:15` signifique a mesma coisa em toda a tela. Quem precisa de `08:10`
digita no campo, que aceita.

## No celular, o painel é folha

A 390px o painel não cabe ancorado no campo, então ele sobe de baixo como
`Sheet`, pelo mesmo `CalendarPanel` que o `DatePicker` usa. As duas colunas
dividem a largura inteira, e cada opção tem a altura de um controle da casa:
alvo de dedo, e não de mouse. Da largura `sm` para cima o painel volta a ser
`Popover` ancorado no campo, alinhado pela direita.

## Partes

O `className` veste a moldura que junta campo e relógio, porque é ela que tem a
largura. O resto entra por parte:

| Parte | O que é |
|---|---|
| `field` | o campo de digitar |
| `trigger` | o botão do relógio, dentro do campo |
| `panel` | o painel flutuante, ou a folha no celular |
| `column` | cada uma das duas colunas roláveis |
| `option` | cada hora e cada minuto |

```tsx
<TimePicker className="w-56" classNames={{ column: 'max-h-40', option: 'font-mono' }} />
```

Os textos que o leitor de tela ouve entram por `labels`, e cada um tem o próprio
padrão. Trocar um não apaga os outros:

```tsx
<TimePicker labels={{ open: 'Escolher o horário da coleta', title: 'Horário da coleta' }} />
```

## Quando não usar

Se a tela é de operação (ponto eletrônico, apontamento de horas, importação
conferida linha a linha), use `TimeField`. Quem digita o dia inteiro não abre
painel, e o relógio no canto só ocupa a largura do campo.

Se os horários são poucos e fixos ("manhã, tarde, noite", ou as quatro janelas
que a transportadora atende), use `Select`: ali as opções têm nome, e nome diz
mais do que `08:00 - 12:00` escrito em duas colunas.

Para data, `DatePicker`; para intervalo de datas, `DateRangePicker`. Esta peça
não conhece dia nenhum, de propósito.

## No React Native

Traduz como gatilho mais **folha de baixo**, que é a decisão da casa para painel no celular. Duas colunas roláveis pela mesma razão do web, que pesa mais aqui: `step={5}` numa lista única são 288 linhas para rolar com o polegar. Cada opção tem 48pt, acima dos 44pt exigidos, e a coluna rola até a hora escolhida a cada abertura.

**A diferença de estrutura, e ela não é estética:** no web o relógio mora DENTRO do campo; aqui não. Um `TextInput` dentro de um `Pressable` engole o toque do pai, e o gatilho precisa ser um alvo único para o leitor de tela. Todo picker nativo da casa (`DatePicker`, `DateRangePicker`, `Select`, `Combobox`, `TreeSelect`) já é gatilho mais folha, e a divisão sai mais limpa do que no web: `TimeField` é digitação, `TimePicker` é toque.

A hora não fecha a folha e preserva o minuto; o minuto fecha. O `labels` perde `open` e `title`, porque aqui o `label` obrigatório já nomeia o gatilho E titula a folha, o mesmo arranjo do `DateRangePicker`.

## Importação

```tsx
import { TimePicker } from '@rivocode/ui'
```

## Exemplos

### Com rótulo

```tsx
import { Field, FieldDescription, FieldLabel, TimePicker } from '@rivocode/ui'
import { useState } from 'react'

export function WithLabel() {
  return (
    <Field className="w-48">
      <FieldLabel htmlFor="consulta">Horário da consulta</FieldLabel>
      <TimePicker id="consulta" defaultValue="14:30" />
    </Field>
  )
}
```

### Janela de entrega

```tsx
import { Field, FieldDescription, FieldLabel, TimePicker } from '@rivocode/ui'
import { useState } from 'react'

export function DeliveryWindow() {
  const [at, setAt] = useState('09:00')

  return (
    <Field className="w-56">
      <FieldLabel>Horário da entrega</FieldLabel>
      <TimePicker value={at} onValueChange={setAt} min="08:00" max="18:00" step={30} />
      <FieldDescription>Escolhido: {at || 'nenhum ainda'}.</FieldDescription>
    </Field>
  )
}
```

### Passo de cinco minutos

```tsx
import { Field, FieldDescription, FieldLabel, TimePicker } from '@rivocode/ui'
import { useState } from 'react'

export function FineStep() {
  return (
    <Field className="w-48">
      <FieldLabel>Início da corrida</FieldLabel>
      <TimePicker defaultValue="06:45" step={5} />
    </Field>
  )
}
```

### Vazio

```tsx
import { Field, FieldDescription, FieldLabel, TimePicker } from '@rivocode/ui'
import { useState } from 'react'

export function Empty() {
  return <TimePicker aria-label="Horário" className="w-48" />
}
```

### Desabilitado

```tsx
import { Field, FieldDescription, FieldLabel, TimePicker } from '@rivocode/ui'
import { useState } from 'react'

export function Disabled() {
  return <TimePicker aria-label="Horário" defaultValue="14:30" className="w-48" disabled />
}
```

## Props

| Prop | Tipo | Obrigatória | Desde | O que faz |
| --- | --- | --- | --- | --- |
| `classNames` | `Partial<Record<"column" \| "field" \| "option" \| "panel" \| "trigger", string>>` |  | - | Classe por parte: `field`, `trigger`, `panel`, `column`, `option`. |
| `defaultValue` | `string` |  | - | A hora inicial de quem nao controla o valor de fora. |
| `labels` | `TimePickerLabels` |  | - | Os textos que o leitor de tela ouve. |
| `max` | `string` |  | - | Ultima hora da janela, em `"HH:MM"`. |
| `min` | `string` |  | - | Primeira hora da janela, em `"HH:MM"`. |
| `onValueChange` | `((value: string) => void)` |  | - | Chamado so com hora inteira: `"08:30"`, ou `""` quando o campo esvazia. |
| `render` | `ComponentRenderFn<HTMLProps, FieldControlState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` |  | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |
| `size` | `"lg" \| "md" \| "sm"` |  | - | Tamanho do campo, o mesmo vocabulario do Input. |
| `step` | `number` |  | - | Quantos minutos o passo anda, pousando na grade. |
| `value` | `string` |  | - | A hora escolhida, em 24h e sempre `"HH:MM"`. |

Além dessas: repassa `className`, `style`, `id` e os demais atributos do elemento raiz.

## Ver também

- [Autocomplete](/componentes/autocomplete.md)
- [Calendar](/componentes/calendar.md)
- [Checkbox](/componentes/checkbox.md)
- [CheckboxGroup](/componentes/checkbox-group.md)
- [ColorPicker](/componentes/color-picker.md)
- [Combobox](/componentes/combobox.md)
- [Convenções da biblioteca](/convencoes.md): Provider, tokens e as regras que valem para toda peça
- [Índice completo](/llms.txt)
