# DateField

> Organism — orn-ui

> ⚠️ Requiere <UIProvider>: Este componente (como todos los de orn-ui) debe renderizarse dentro de un ancestro <UIProvider>, o lanza un error en runtime. See https://orn-ui-docs.vercel.app/getting-started.md

> Corre en Expo SDK 54, 55, 56 y 57, y en React Native puro >=0.81 con react >=19.1. Sin módulos nativos — funciona en Expo Go, sin prebuild.

## Instalación

Instala solo este componente (copia el código fuente a tu proyecto, sin dependencia npm)
```bash
npx orn-ui add date-field
```

O instala el paquete completo e impórtalo
```tsx
pnpm add orn-ui
import { DateField } from 'orn-ui/date-field';
```

Depende de: core (theme + icons), modal, title, icon-button, pressable-scale, transition, date-picker, button

## Variantes

### DateField — input that opens the calendar

```tsx
<DateField label="Due date" value={fieldDate} onChange={setFieldDate} maxDate={inAMonth} />
```

### DateField — custom format

```tsx
<DateField
  label="ISO format"
  value={fieldDate}
  onChange={setFieldDate}
  format={(d) => d.toISOString().slice(0, 10)}
/>
```

### DateField — onClear renders the clear button

```tsx
<DateField
  label="Due date"
  value={fieldDate}
  onChange={setFieldDate}
  onClear={() => setFieldDate(undefined)}
/>
```

### DateField — mode="range", closes when the range closes

```tsx
<DateField label="Stay" mode="range" range={fieldRange} onRangeChange={setFieldRange} />
```

### DateField — required + error

```tsx
<DateField label="Due date" required onChange={() => {}} error="Pick a date to continue" />
```

### DateField — disabled

```tsx
<DateField label="Locked" value={today} onChange={() => {}} disabled />
```

## Código completo del demo

```tsx
const variants: VariantDef[] = [
  {
    label: 'Inline calendar',
    content: <DatePicker value={date} onChange={setDate} />,
  },
  {
    label: 'mode="range" — tap start, then end',
    content: (
      <View style={{ gap: 8 }}>
        <DatePicker mode="range" range={range} onRangeChange={setRange} />
        <Caption>
          {range?.start
            ? `${range.start.toDateString()} → ${range.end ? range.end.toDateString() : '…'}`
            : 'No range selected'}
        </Caption>
      </View>
    ),
  },
  {
    label: 'mode="range" + minDate/maxDate',
    content: <DatePicker mode="range" range={range} onRangeChange={setRange} minDate={today} maxDate={inAMonth} />,
  },
  {
    label: 'minDate / maxDate (next 7 days only)',
    content: <DatePicker value={ranged} onChange={setRanged} minDate={today} maxDate={inAWeek} />,
  },
  {
    label: 'Week starts on Monday',
    content: <DatePicker value={date} onChange={setDate} firstDayOfWeek={1} />,
  },
  {
    label: 'Localized month/weekday names',
    content: (
      <DatePicker
        value={date}
        onChange={setDate}
        monthNames={SPANISH_MONTHS}
        weekdayNames={SPANISH_WEEKDAYS}
        firstDayOfWeek={1}
      />
    ),
  },
  {
    label: 'DateField — input that opens the calendar',
    content: <DateField label="Due date" value={fieldDate} onChange={setFieldDate} maxDate={inAMonth} />,
  },
  {
    label: 'DateField — custom format',
    content: (
      <DateField
        label="ISO format"
        value={fieldDate}
        onChange={setFieldDate}
        format={(d) => d.toISOString().slice(0, 10)}
      />
    ),
  },
  {
    label: 'DateField — onClear renders the clear button',
    content: (
      <DateField
        label="Due date"
        value={fieldDate}
        onChange={setFieldDate}
        onClear={() => setFieldDate(undefined)}
      />
    ),
  },
  {
    label: 'DateField — mode="range", closes when the range closes',
    content: <DateField label="Stay" mode="range" range={fieldRange} onRangeChange={setFieldRange} />,
  },
  {
    label: 'DateField — required + error',
    content: <DateField label="Due date" required onChange={() => {}} error="Pick a date to continue" />,
  },
  {
    label: 'DateField — disabled',
    content: <DateField label="Locked" value={today} onChange={() => {}} disabled />,
  },
];
return <VariantList variants={variants} />;
```

## Props

| Nombre | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `label?` | `string` | — | — |
| `required?` | `boolean` | false | — |
| `mode?` | `'single' \| 'range'` | single | 'single' elige una fecha; 'range' elige un intervalo con dos toques. |
| `value?` | `Date` | — | Fecha seleccionada en mode="single". |
| `onChange?` | `(date: Date) => void` | — | Se llama en mode="single". |
| `range?` | `DateRange` | — | Rango seleccionado en mode="range". |
| `onRangeChange?` | `(range: DateRange) => void` | — | Se llama en mode="range", tanto al fijar el inicio como al cerrar el rango. |
| `placeholder?` | `string` | Select a date | — |
| `error?` | `string` | — | — |
| `disabled?` | `boolean` | false | — |
| `format?` | `(date: Date) => string` | Month D, YYYY | Formatea la fecha del campo. |
| `modalTitle?` | `string` | the `selectDate` from `useLabels()` | Título del modal del calendario. |
| `onClear?` | `() => void` | — | Si se pasa, el modal muestra un botón que borra la selección y cierra. Sin esto no hay botón: el "Clear" anterior solo cerraba, sin limpiar nada. |
| `clearLabel?` | `string` | the `clear` from `useLabels()` | Texto de ese botón. |
| `containerStyle?` | `StyleProp<ViewStyle>` | — | — |
| `minDate?` | `Date` | — | — |
| `maxDate?` | `Date` | — | — |
| `monthNames?` | `string[]` | those from `useLabels()` | Nombres de los meses. |
| `weekdayNames?` | `string[]` | those from `useLabels()` | Iniciales de los días, índice 0 = domingo. |
| `firstDayOfWeek?` | `0 \| 1` | 0 | 0 = domingo, 1 = lunes. |
