# TimePicker

> Una hora en 24 h: se tipea («930») o se elige de una lista cada 15 minutos, con mínimo y máximo.

```tsx
import { TimePicker } from "sebs7n-ui/time-picker"
```

## Ejemplos

### Básico

Se tipea («930») o se elige de la lista, cada 15 minutos. Al salir del campo queda como «09:30».

```tsx
import { Field, FieldLabel } from "sebs7n-ui"
import { TimePicker } from "sebs7n-ui/time-picker"
import { useState } from "react"

function Basic() {
  const [time, setTime] = useState<string | null>("09:30")
  return (
    <Field className="w-full max-w-40">
      <FieldLabel>Envío del resumen</FieldLabel>
      <TimePicker name="send-time" onValueChange={setTime} value={time} />
    </Field>
  )
}
```

### Con horario

`min`, `max` y `step`: la lista va de 08:00 a 18:00 cada media hora y lo tipeado fuera del horario se lleva al borde.

```tsx
import { Field, FieldDescription, FieldLabel } from "sebs7n-ui"
import { TimePicker } from "sebs7n-ui/time-picker"

function WithBusinessHours() {
  return (
    <Field className="w-full max-w-40">
      <FieldLabel>Débito automático</FieldLabel>
      <TimePicker max="18:00" min="08:00" step={30} />
      <FieldDescription>En horario bancario.</FieldDescription>
    </Field>
  )
}
```

### Sin vacío

`required`: el horario de atención siempre tiene apertura y cierre. Borrar la hora y salir vuelve a la anterior; nunca avisa `null`.

```tsx
import { TimePicker } from "sebs7n-ui/time-picker"
import { useState } from "react"

function Required() {
  const [opens, setOpens] = useState("09:00")
  const [closes, setCloses] = useState("18:00")
  return (
    <div className="flex items-center gap-2 text-callout text-label-secondary">
      <TimePicker aria-label="Abre" onValueChange={(time) => time && setOpens(time)} required size="sm" value={opens} />
      a
      <TimePicker aria-label="Cierra" min={opens} onValueChange={(time) => time && setCloses(time)} required size="sm" value={closes} />
    </div>
  )
}
```

## Props

### TimePicker

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `aria-describedby` | `string` | — | El `id` de la ayuda o del error del campo. |
| `aria-invalid` | `boolean` | — | Marca el campo inválido (borde rojo). |
| `aria-label` | `string` | — | Nombre accesible del elemento. |
| `aria-labelledby` | `string` | — | El `id` del elemento que nombra el campo, si no es un `<label>`. |
| `defaultValue` | `string` | `null` | La hora al montar, sin controlar. |
| `disabled` | `boolean` | — | Apaga el campo. |
| `id` | `string` | — | El `id` del campo, para un `<Label htmlFor>`. |
| `labels` | `Partial<Labels["timePicker"]>` | — | Textos: `placeholder`, `invalid` (lo que se anuncia cuando lo tipeado no es una hora) y `required` (cuando un campo obligatorio se vacía y vuelve a la hora anterior). |
| `max` | `string` | — | La última hora posible, «HH:MM». Lo tipeado después se lleva a esta. |
| `min` | `string` | — | La primera hora posible, «HH:MM». Lo tipeado antes se lleva a esta. |
| `name` | `string` | — | El nombre con el que la hora viaja en un formulario, como «09:30» (vacío sin hora). |
| `onValueChange` | `(value: string \| null) => void` | — | Avisa la hora elegida («HH:MM») o `null` al vaciar el campo. |
| `placeholder` | `string` | — | Lo que dice el campo vacío. Por defecto, `labels.placeholder` («hh:mm»). |
| `required` | `boolean` | `false` | Una hora que no puede quedar vacía (la apertura de un horario): vaciar el texto y salir vuelve a la hora anterior (y se anuncia) y nunca avisa `null`. Pone `aria-required`. Sin hora inicial, dentro de un `Form` no deja enviar: el campo queda inválido y `Form` lo enfoca. |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | 28, 36 (default) o 40, como los campos. |
| `step` | `number` | `15` | Cada cuántos minutos hay una hora en la lista. Por defecto, 15. Se puede tipear cualquiera. |
| `value` | `string` | — | La hora elegida, «HH:MM» en 24 h. `null` es ninguna. Pasarlo lo vuelve controlado. |
| `className` | `string` | — | Clases de la superficie. |

## Teclado

| Tecla | Qué hace |
|---|---|
| 0–9 · : | Escriben la hora y filtran la lista: «9» muestra las 09:xx. |
| ↓ / ↑ | Abren la lista y la recorren. |
| Enter | Elige la hora resaltada; sin una resaltada, toma lo tipeado. |
| Tab | Sale y toma lo tipeado como «HH:MM». |
| Escape | Cierra la lista. |

## Accesibilidad

- Es un `combobox` (Autocomplete de Base UI) con `listbox`: nombralo con `aria-label`, `aria-labelledby` o un `<Label htmlFor>` al `id`.
- La hora elegida lleva `aria-selected` y el círculo de acento de los menús.
- Si lo tipeado no es una hora, el campo vuelve a la anterior y una región viva dice «Hora no válida».

## Reglas de uso

- No depende del idioma: la hora es siempre de 24 h, «HH:MM», y no lee `dates` de `LabelsProvider`. Con `required`, vaciar el texto y salir vuelve a la hora anterior (y se anuncia) y nunca avisa `null` (una franja horaria sin hora no existe); sin hora inicial, `Form` no envía y enfoca el campo.
- Dentro de un `Field` se engancha solo: `FieldLabel`, `FieldDescription`, `FieldError`, y con el `name` del `Field` la hora viaja con ese nombre (el `name` propio se ignora, para no mandarla dos veces).
- Para una hora suelta: un envío programado, un horario de débito. Con fecha, `DateTimePicker`.
- `step` arma la lista pero no limita lo tipeado: con `step={15}` se puede escribir 09:37. `min`/`max` sí: lo de afuera se lleva al borde.
- `value` es «HH:MM» o `null`; con `name`, un `<input type="hidden">` lo manda a la Server Action.
- Mide lo que la hora, como `NumberField`: el reloj, «09:15» y su aire. `className="w-full"` lo estira al ancho del contenedor.
- Solo por subpath (`sebs7n-ui/time-picker`): no está en el barrel, por peso.

## Relacionados

[date-time-picker](/docs/components/date-time-picker.md) · [date-picker](/docs/components/date-picker.md) · [autocomplete](/docs/components/autocomplete.md)
