preskok/ui

Calendar

Advanced date selection calendar with month/year navigation, date ranges, and internationalization support.

Installation

pnpm dlx @preskok-org/ui@latest add calendar

Usage

import { getLocalTimeZone, today } from "@internationalized/date"
 
import { Calendar } from "@/registry/preskok/ui/preskok-ui/calendar"
 
export function CalendarDemo() {
  const [date, setDate] = useState(today(getLocalTimeZone()))
 
  return <Calendar value={date} onChange={setDate} aria-label="Select a date" />
}

Props

PropTypeDefaultDescription
valueDateValue | null-Selected date value
onChange(date: DateValue | null) => void-Date change handler
minValueDateValue-Minimum selectable date
maxValueDateValue-Maximum selectable date
isDisabledbooleanfalseDisable the entire calendar
isReadOnlybooleanfalseMake calendar read-only
isDateUnavailable(date: DateValue) => boolean-Function to disable specific dates
errorMessagestring-Error message to display
classNamestring-Additional CSS classes

Examples

Basic Calendar

import { useState } from "react"
import { getLocalTimeZone, today } from "@internationalized/date"
 
function BasicCalendar() {
  const [date, setDate] = useState(today(getLocalTimeZone()))
 
  return <Calendar value={date} onChange={setDate} aria-label="Select a date" />
}

With Date Range

import { useState } from "react"
import { getLocalTimeZone, today } from "@internationalized/date"
 
function CalendarWithRange() {
  const [range, setRange] = useState({
    start: today(getLocalTimeZone()),
    end: today(getLocalTimeZone()).add({ days: 7 }),
  })
 
  return (
    <Calendar
      selectionMode="range"
      value={range}
      onChange={setRange}
      aria-label="Select date range"
    />
  )
}

With Disabled Dates

function CalendarWithDisabledDates() {
  const [date, setDate] = useState(null)
 
  const isDateUnavailable = (date) => {
    // Disable weekends
    const dayOfWeek = date.toDate(getLocalTimeZone()).getDay()
    return dayOfWeek === 0 || dayOfWeek === 6
  }
 
  return (
    <Calendar
      value={date}
      onChange={setDate}
      isDateUnavailable={isDateUnavailable}
      aria-label="Select a weekday"
    />
  )
}

With Min/Max Dates

function CalendarWithLimits() {
  const [date, setDate] = useState(null)
  const today = today(getLocalTimeZone())
 
  return (
    <Calendar
      value={date}
      onChange={setDate}
      minValue={today}
      maxValue={today.add({ months: 3 })}
      aria-label="Select future date"
    />
  )
}

Multiple Months

function MultiMonthCalendar() {
  const [date, setDate] = useState(null)
 
  return (
    <Calendar
      value={date}
      onChange={setDate}
      visibleDuration={{ months: 2 }}
      aria-label="Select a date"
    />
  )
}

Controlled Month/Year

function CalendarWithControls() {
  const [date, setDate] = useState(null)
  const [focusedDate, setFocusedDate] = useState(today(getLocalTimeZone()))
 
  return (
    <Calendar
      value={date}
      onChange={setDate}
      focusedValue={focusedDate}
      onFocusChange={setFocusedDate}
      aria-label="Select a date"
    />
  )
}

Features

  • Flexible date selection - Single dates, ranges, or multiple dates
  • Month/year navigation - Dropdown selectors for quick navigation
  • Date validation - Min/max dates and custom unavailable dates
  • Internationalization - Full i18n support with locale-aware formatting
  • Keyboard navigation - Complete keyboard accessibility
  • Custom styling - Extensive customization options
  • Multiple months - Display multiple months simultaneously
  • Time zones - Proper time zone handling with @internationalized/date
  • Screen reader support - Comprehensive accessibility features

Accessibility

The Calendar component follows WAI-ARIA guidelines:

  • Proper grid structure with row/column headers
  • Keyboard navigation with arrow keys
  • Focus management and visual indicators
  • Screen reader announcements for date changes
  • Support for disabled and unavailable dates