Ir al contenido
Formularios

Calendar

Un mes en una grilla —o varios, uno al lado del otro—, para elegir una fecha o un rango. Es el calendario de DatePicker, suelto.

import { Calendar } from "sebs7n-ui/calendar"

Ejemplos

Una fecha

No dibuja superficie: acá el vidrio lo pone la `Card`. Recorrelo con las flechas; `Re Pág` y `Av Pág` cambian de mes.

import { Calendar } from "sebs7n-ui/calendar"
import { Card, CardContent } from "sebs7n-ui/card"
import { useState } from "react"

function Basico() {
  const [fecha, setFecha] = useState<Date | null>(new Date(2026, 8, 27))
  return (
    <Card size="sm">
      <CardContent>
        <Calendar onValueChange={setFecha} value={fecha} />
      </CardContent>
    </Card>
  )
}

Un rango

El primer clic es el desde y el segundo el hasta. Mientras se elige, el día bajo el puntero muestra cómo quedaría.

import { Calendar } from "sebs7n-ui/calendar"
import { Card, CardContent } from "sebs7n-ui/card"
import { DateRange } from "sebs7n-ui/lib/dates"
import { useState } from "react"

function Rango() {
  const [rango, setRango] = useState<DateRange>({ from: new Date(2026, 8, 14), to: new Date(2026, 8, 18) })
  return (
    <Card size="sm">
      <CardContent>
        <Calendar mode="range" onValueChange={setRango} value={rango} />
      </CardContent>
    </Card>
  )
}

Dos meses

Para un rango que cruza de un mes al otro. Cada fecha aparece una sola vez: los huecos de un mes quedan vacíos, porque esos días están en el de al lado.

import { Calendar } from "sebs7n-ui/calendar"
import { Card, CardContent } from "sebs7n-ui/card"
import { DateRange } from "sebs7n-ui/lib/dates"
import { useState } from "react"

function DosMeses() {
  const [rango, setRango] = useState<DateRange>({ from: new Date(2026, 8, 28), to: new Date(2026, 9, 9) })
  return (
    <Card size="sm">
      <CardContent>
        <Calendar defaultMonth={new Date(2026, 8, 1)} mode="range" numberOfMonths={2} onValueChange={setRango} value={rango} />
      </CardContent>
    </Card>
  )
}

Con límites y días apagados

`min` y `max` apagan lo que queda afuera y los botones de mes; `isDateDisabled`, fechas sueltas. Acá no hay turnos los fines de semana.

import { Calendar } from "sebs7n-ui/calendar"
import { Card, CardContent } from "sebs7n-ui/card"

function Limites() {
  return (
    <Card size="sm">
      <CardContent>
        <Calendar
          defaultValue={new Date(2026, 8, 15)}
          isDateDisabled={(fecha) => fecha.getDay() === 0 || fecha.getDay() === 6}
          max={new Date(2026, 9, 16)}
          min={new Date(2026, 8, 7)}
        />
      </CardContent>
    </Card>
  )
}

Una fecha lejana

El título abre una grilla de meses, y su año una de doce años: el alta de un cliente de hace veinte años está a cinco clics. `max` apaga los meses y años que todavía no llegaron.

import { Calendar } from "sebs7n-ui/calendar"
import { Card, CardContent } from "sebs7n-ui/card"
import { useState } from "react"

function FechaLejana() {
  const [alta, setAlta] = useState<Date | null>(new Date(2006, 3, 12))
  return (
    <Card size="sm">
      <CardContent>
        <Calendar aria-label="Fecha de alta del cliente" max={new Date(2026, 8, 29)} onValueChange={setAlta} value={alta} />
      </CardContent>
    </Card>
  )
}

Props

Generadas del TypeScript del paquete. Las propias del componente, más las heredadas del primitivo que tienen algo que explicar —marcadas «heredada de Base UI»—. El resto está en la línea «hereda de».

Calendar

PropTipoPor defectoDescripción
defaultMonthDate—El mes con el que arranca. Por defecto, el de la fecha elegida, y si no hay, el actual.
defaultValueDate | DateRange—La fecha elegida al montar, sin controlar.
isDateDisabled(date: Date) => boolean—Apaga fechas sueltas: feriados, fines de semana, días sin turno.
labelsPartial<CalendarLabels>—Los nombres de los botones de mes anterior y siguiente.
localestring—El idioma de los nombres de mes y de día, como lo entiende Intl. Por defecto, es-AR.
maxDate—La última fecha que se puede elegir.
minDate—La primera fecha que se puede elegir. Las anteriores se ven apagadas.
mode"single" | "range"—single elige una fecha; range, un desde y un hasta en el mismo calendario.
monthDate—El mes a la vista, controlado. Cualquier día de ese mes sirve.
numberOfMonthsnumber—Cuántos meses se ven a la vez, uno al lado del otro. Por defecto, 1. month es el primero. Con más de uno, los días de los meses vecinos no se dibujan: ya están en el mes de al lado.
onMonthChange(month: Date) => void—Avisa cuando cambia el mes a la vista, con su primer día.
onValueChange((value: Date | null) => void) | ((value: DateRange) => void)—Avisa la fecha elegida.
valueDate | DateRange—La fecha elegida, controlada. null es ninguna.
weekStartsOn0 | 1—Con qué día arranca la semana: 1 lunes (el default), 0 domingo.
classNamestring—Heredada de Base UI. Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

Teclado

← → ↑ ↓
Un día o una semana. Al salir de los meses a la vista, la vista se corre.
Inicio · Fin
Primer y último día de la semana.
Re Pág · Av Pág
Un mes atrás o adelante. Con Shift, un año.
Enter · Espacio
Elige el día enfocado. En el título, abre la grilla de meses; en su año, la de años.
← → ↑ ↓ (meses y años)
Una celda o una fila de tres. Al pasar el borde, cambia el año o la página de doce años.
Re Pág · Av Pág (meses y años)
Un año, o doce, atrás o adelante.
Escape (meses y años)
Vuelve a los días sin cambiar nada, con el foco en el título. Adentro de un DatePicker no cierra el panel.
Tab
Entra a la grilla y sale: adentro es una sola parada. El título es una parada propia.

Accesibilidad

  • Sigue el patrón de grilla de fechas de WAI-ARIA: role="grid", una sola parada de tabulación y flechas adentro.
  • Cada día se llama por su fecha entera —«domingo, 27 de septiembre de 2026»— y los encabezados de columna llevan el nombre del día en abbr.
  • Hoy lleva aria-current="date"; el título del mes, aria-live, así cambiar de mes con los botones se anuncia.
  • El título es un botón con aria-expanded y se llama «septiembre de 2026, Elegir mes y año». La grilla de meses y la de años son role="grid" de 4 filas por 3, con una sola parada; el mes de hoy lleva aria-current y el que está a la vista, aria-selected.
  • Una fecha apagada usa aria-disabled y no disabled: sigue siendo enfocable, que es lo que deja pasar por arriba con las flechas.
  • Los días de los meses vecinos completan las semanas pero están fuera del árbol de accesibilidad.
  • Con varios meses hay una grilla por mes, cada una con su nombre, y siguen siendo una sola parada de tabulación entre todas.

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.
  • No dibuja superficie: va adentro de un Popover, de una Card o suelto. El fondo lo pone quien lo contiene.
  • Para un campo de formulario, usá DatePicker, que ya lo trae adentro.
  • Siempre son seis semanas, aunque el mes entre en cinco: con un alto fijo, lo que está debajo no se mueve al cambiar de mes.
  • numberOfMonths={2} para un rango que suele cruzar de un mes al otro: una estadía, un alquiler. Cada fecha aparece una sola vez; los huecos de un mes quedan vacíos.
  • En un teléfono va un solo mes: dos no entran a lo ancho y quedan uno debajo del otro.
  • Para una fecha lejana —el alta de un cliente de hace años—, el título abre una grilla de meses y su año una de doce años: veinte años atrás son cinco clics, no 240. Con varios meses, solo el título del primero la abre, porque la vista se mueve entera. La grilla tapa a los días sin cambiar el tamaño.

Relacionados