# CalendarView

> El calendario de iCloud en vista de mes, semana y día: hoy en el círculo del acento, chips de todo el día, eventos con hora y la línea roja de ahora.

```tsx
import { CalendarView } from "sebs7n-ui/calendar-view"
```

## Ejemplos

### Mes

La vista de mes de iCloud: hoy en el círculo del acento, los eventos de todo el día como chips y los que tienen hora con su punto. Hacé foco en un día y probá las flechas, Home/End y PageUp/PageDown.

```tsx
import { CalendarView } from "sebs7n-ui/calendar-view"

function Mes() {
  const events = useSampleEvents()
  return (
    <div className="h-[640px] w-full overflow-hidden rounded-surface border border-separator">
      <CalendarView events={events} hour12={false} locale="es-AR" />
    </div>
  )
}
```

### Semana

Filas de una hora de 61, bloques con el borde izquierdo del color del calendario, la fila «Todo el día» arriba y la línea roja de ahora. Los eventos que se pisan se reparten el ancho.

```tsx
import { CalendarView } from "sebs7n-ui/calendar-view"

function Semana() {
  const events = useSampleEvents()
  return (
    <div className="h-[560px] w-full overflow-hidden rounded-surface border border-separator">
      <CalendarView defaultView="week" events={events} hour12={false} locale="es-AR" />
    </div>
  )
}
```

### Día

La semana con una sola columna: la fila «Todo el día», las horas de 61, los eventos con su borde de color y la línea de ahora. ‹ › pasan de a un día y el teclado es el mismo.

```tsx
import { CalendarView } from "sebs7n-ui/calendar-view"

function Dia() {
  const events = useSampleEvents()
  return (
    <div className="h-[560px] w-full overflow-hidden rounded-surface border border-separator">
      <CalendarView defaultView="day" events={events} hour12={false} locale="es-AR" />
    </div>
  )
}
```

### Idioma global

Con `LabelsProvider` `dates` (`en-US`, semana del domingo) no hace falta pasarle `locale` ni `weekStartsOn`.

```tsx
import { CalendarView } from "sebs7n-ui/calendar-view"
import { LabelsProvider } from "sebs7n-ui/labels"

function IdiomaGlobal() {
  const events = useSampleEvents()
  return (
    <LabelsProvider value={{ dates: { locale: "en-US", weekStartsOn: 0 } }}>
      <div className="h-[640px] w-full overflow-hidden rounded-surface border border-separator">
        <CalendarView events={events} />
      </div>
    </LabelsProvider>
  )
}
```

## Props

### CalendarView

Hereda las props de `<div>`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `date` | `Date` | — | El día activo: el mes o la semana que se ve y dónde está el foco. Pasarlo lo vuelve controlado. |
| `defaultDate` | `Date` | — | El día activo al arrancar. Por defecto, hoy. |
| `defaultView` | `"month" \| "week" \| "day"` | `"month"` | La vista al arrancar. Por defecto `month`. |
| `events` | `CalendarEvent[]` | `[]` | Los eventos: `{ id, title, start, end?, allDay?, color? }` (`color` de la paleta de `Badge`, por defecto la marca). |
| `hour12` | `boolean` | — | `false` fuerza 24 horas; `true`, 12. Por defecto, el locale. |
| `labels` | `Partial<Labels["calendarView"]>` | — | Textos: `today`, `view`, `month`, `week`, `day`, `previous*`/`next*` de mes, semana y día, `allDay`, `weekOf` y `more`. Los de Día, por defecto, son `calendarViewDayLabels`. |
| `locale` | `string` | — | El locale de los nombres de meses y días y de las horas. |
| `now` | `Date` | — | El momento actual, para hoy y la línea de ahora. Por defecto, el reloj del navegador. |
| `onDateChange` | `(date: Date) => void` | — | Se llama con el día activo nuevo (flechas, ‹ ›, Hoy, click). |
| `onDayOpen` | `(date: Date) => void` | — | Enter o doble click sobre un día. |
| `onEventClick` | `(event: CalendarEvent) => void` | — | Click en un evento. |
| `onViewChange` | `(view: CalendarViewMode) => void` | — | Se llama con la vista nueva. |
| `scrollToHour` | `number` | `8` | La hora que queda arriba al abrir una semana (o un día) que no incluye hoy. Por defecto, 8. |
| `view` | `"month" \| "week" \| "day"` | — | `month`, `week` o `day`. Pasarla la vuelve controlada. |
| `weekStartsOn` | `0 \| 1` | — | `1` lunes (default) o `0` domingo. |
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |

## Teclado

| Tecla | Qué hace |
|---|---|
| F2 | Con `onEventClick`, entra a los eventos del día activo: ↓ ↑ los recorren, Enter o Espacio abren el evento, Escape (o F2) vuelve al día. En la semana entran los de todo el día; los de hora y los que quedan en «+N más» se llegan con Enter (`onDayOpen`). |
| Tab | Recorre el segmentado de la vista, ‹ Hoy › y entra a la grilla en el día activo (una sola parada). |
| ← → (en el segmentado) | Mueven entre Día, Semana y Mes; Enter o Espacio la elige. Es un `tablist` de una sola opción, el segmentado gris de Calendar. |
| ← → | El día anterior o siguiente (cruza de mes o de semana; en el día, cambia de día). |
| ↑ ↓ | En el mes, la misma fecha una semana antes o después. |
| Home End | El primer y el último día de la semana (también en el día). |
| PageUp PageDown | El mes anterior o siguiente (en la semana y el día, una semana). Con Shift, un año (en la semana y el día, un mes). |
| Enter · Espacio | Abre el día (`onDayOpen`): mostrar sus eventos o crear uno. |

## Accesibilidad

- El mes o la semana nueva se anuncia (región `status`) al cambiarla con ‹ Hoy ›; con el teclado en la grilla no, porque el foco ya dice la fecha de la celda.
- Los días son un `role="grid"` nombrado por el mes («Septiembre 2026»), la semana o el día («martes, 29 de septiembre de 2026»), con `columnheader` de los días y `gridcell` con foco itinerante. El día es la misma grilla que la semana, con una sola columna.
- Cada celda se lee con la fecha completa («martes, 29 de septiembre de 2026») y sus eventos; el número grande es decorativo. Hoy lleva `aria-current="date"` y el activo `aria-selected`.
- En la semana, los bloques con hora están fuera de las celdas (en la escala de horas, `aria-hidden`): cada celda de «Todo el día» repite para el lector los eventos del día con su hora.
- El título del mes es un `h2` con `aria-live="polite"`: al cambiar de mes, el lector lo anuncia.
- Los eventos son botones (con `onEventClick`) **fuera del orden de Tab**: la grilla es una sola parada. Con teclado, Enter en el día llama a `onDayOpen`, que tiene que mostrar los eventos de ese día (un `Popover` con una `List`, por ejemplo).
- El color del calendario nunca es el único dato: el título del evento está siempre escrito.
- ‹ y › se llaman «Mes anterior»/«Mes siguiente» (o semana, o día), desde el `LabelsProvider` (`calendarView`). Los de la vista Día (`day`, `previousDay`, `nextDay`) son opcionales en el tipo: sus valores por defecto son `calendarViewDayLabels`. Un provider armado antes de R9, sin esas claves (o con ellas en `undefined`), no rompe nada: la vista Día cae al español por defecto hasta que las traduzcas. Lo mismo vale para cualquier clave en `undefined`, en el provider o en `labels`: no pisa el texto de abajo.

## Reglas de uso

- Idioma y semana para toda la app: `<LabelsProvider value={{ dates: { locale: "en-US", weekStartsOn: 0 } }}>` (también `format` para el campo de `DatePicker`). Lo leen `Calendar`, `DatePicker`, `DateTimePicker` y `CalendarView`; la prop de cada uno gana.
- **Con SSR, pasá `now` o `defaultDate`** (la fecha del request): sin ellas el calendario no puede saber qué día es hoy hasta montar, y se dibuja invisible hasta entonces para que el server y el cliente coincidan.
- **Para ver fechas con cosas encima**: vencimientos, cobros, turnos. Para elegir una fecha, es `Calendar` o `DatePicker`.
- Los eventos son datos (`events`): `{ id, title, start, end?, allDay?, color? }`. El componente no crea ni arrastra eventos: `onDayOpen` y `onEventClick` son para que la app lo haga.
- `now` para tests y para servidores con otro huso: sin él, hoy y la línea de ahora salen del reloj del navegador después de montar (no hay desfase de hidratación).
- `hour12={false}` fuerza 24 horas; por defecto manda el locale.
- El alto lo pone quien lo contiene: el mes reparte las filas y la semana y el día scrollean las horas (abren en la hora de ahora, o en `scrollToHour`).
- **Día para una agenda apretada**: los turnos o cobros de hoy con su horario. Es la semana con una columna: los eventos que se pisan se reparten el ancho igual.
- Arrastrar eventos no está. Solo por subpath (`sebs7n-ui/calendar-view`): no está en el barrel, por peso.

## Relacionados

[calendar](/docs/components/calendar.md) · [date-picker](/docs/components/date-picker.md) · [list-row](/docs/components/list-row.md)
