TimeCalendar

A calendar component for date selection with support for single, range, and multiple date picking

Installation

Terminal
pnpm add @choice-ui/calendar

Import

Component.tsx
import { type MonthCalendarProps, MonthCalendar, DateInput, DateRangeInput, type TimeCalendarProps, TimeCalendar, TimeInput, TimeRangeInput, type QuarterCalendarProps, QuarterCalendar, type YearCalendarProps, YearCalendar, dateUtils, generateWeekdayNames, generateCalendarDays, formatMonthTitle, calculateWeekNumbers, inferSelectionMode, inferMonthFromValue, isCalendarValueEqual, LOCALE_MAP, resolveLocale, getSupportedLocales, isChineseLocale, type DateComparisonMode, type DateParts, createTimeZoneContext, isSameDayInTimeZone, isSameMonthInTimeZone, isSameYearInTimeZone, isSameWeekInTimeZone, isTodayInTimeZone, isWithinRange, getDateParts, getDateKey, areDatesEqual, clearTimeZoneCache, parseMonthName, englishMonths, chineseMonths, validateDateRange, validateTimeRange, isValidDateExists, getLastDayOfMonth, smartCorrectDate, smartCorrectYear, quickValidateDate, handleShortcuts, parseRelativeDate, parseExtendedRelativeDate, parseNaturalLanguage, getLocaleKey, parseEnglishDate, getLocale, detectDateFormat, type ParseResult, type DetailedParseResult, type ParseOptions, parseDate, parseDate, parseDate, getPredictionInfo, tryRelaxedParsing, smartParseDate, parseDate, tryRelaxedParsing, smartParseDate, getPredictionInfo, type ParseOptions, type ParseResult, type DetailedParseResult, getEnhancedPrediction, type PredictionResult, validateDateRange, validateTimeRange, isValidDateExists, smartCorrectDate, smartCorrectYear, quickValidateDate, getLastDayOfMonth, getLocale, detectDateFormat, parseMonthName, parseEnglishDate, parseRelativeDate, parseExtendedRelativeDate, parseNaturalLanguage, getLocaleKey, handleShortcuts, extractDigits, convertTwoDigitYear, isValidMonthDay, canBeMonthDay, isReasonableYear, type NumericParseResult, parseYYMMDD, parseYYYYMMDD, parse3Digits, yearUtils, quarterUtils, getQuarterMonths, createQuarter, getCurrentQuarter, getYearQuarters, isQuarterEqual, formatQuarter, getQuarterDateRange, dateToTimeString, timeStringToDate, normalizeTimeValue, smartParseTimeValue, tryRelaxedTimeParsing, createTimeValue, timeToDate, dateToTime, formatTimeValue, formatHourValue, formatMinuteValue, generateHourOptions, generateMinuteOptions, isTimeEqual, findClosestValidTime, generateTimeOptions, createTimeToday, defaultLocaleMap, naturalLanguageMap, relativeDatePatterns, commonDateFormats, commonTimeFormats, parserConfig, parseCache, useDateInput, useTimeInput, type BaseYearProps, type YearItem, type YearLayoutProps, type YearNavigationProps, type BaseQuarterProps, type Quarter, type QuarterItem, type QuarterLayoutProps, type QuarterNavigationProps, type BaseCalendarProps, type CalendarValue, type CalendarState, type CalendarLayoutProps, type BaseDateProps, type DateInputValue, type DateDataFormat, type DateRange, type DateParserOptions, type DateInteractionProps, type BaseTimeProps, type Time, type TimeInputValue, type TimeDataFormat, type TimeParserOptions, type TimeLayout, type TimeInteractionProps, type TimeOptionItem, type SmartInputOptions, type StepProps, type NaturalLanguageMap, type RelativeDatePattern } from "@choice-ui/calendar"

Basic

`TimeCalendar` is a specialized calendar component for time selection with customizable time steps and formats.
Features:
- Scrollable time list with configurable step intervals (1, 5, 15, 30 minutes) - Support for both 12-hour and 24-hour time formats - Intelligent handling of values outside step ranges - Smooth scrolling and keyboard navigation - Controlled and uncontrolled modes - Customizable time format strings - Compact vertical layout optimized for popovers - Accessibility support with proper ARIA labels - Today's current time highlighting
Usage:
- Use in time picker popovers and dropdowns - Ideal for scheduling applications and time-based forms - Combine with time input components for complete time selection - Perfect for appointment booking and event scheduling
Best Practices:
- Choose appropriate step intervals based on use case precision - Use 24-hour format for international applications - Consider 12-hour format for US-focused applications - Provide reasonable default values for better UX
Accessibility:
- Full keyboard navigation with arrow keys and page up/down - Screen reader friendly with proper time announcements - Focus management and proper ARIA roles - High contrast support for all time entries Default: Shows the basic TimeCalendar with 24-hour format and 15-minute steps. - Demonstrates standard time selection with scrollable list interface. - Uses 24-hour format (HH:mm) with 15-minute intervals. - Shows selected time display and state management.
Selected time: 00:30

Twelve Hour Format

TwelveHourFormat: Demonstrates 12-hour time format with AM/PM indicators.
- Shows time selection using 12-hour format with AM/PM suffix. - Maintains 15-minute step intervals in 12-hour display. - Useful for applications targeting US users or 12-hour time preferences.
Selected time: 00:30

Custom Steps

CustomSteps: Demonstrates 30-minute step intervals for broader time selection.
- Shows time selection with 30-minute intervals instead of default 15-minute. - Useful for applications with less precise time requirements. - Reduces list length for faster navigation in broad time ranges.
Selected time: 00:30

Precise Minutes

PreciseMinutes: Demonstrates 5-minute step intervals for precise time selection.
- Shows time selection with 5-minute intervals for high precision. - Ideal for applications requiring detailed time scheduling. - Provides more granular control over time selection.
Selected time: 00:30

Special Value

SpecialValue: Demonstrates handling of times outside the configured step range.
- Shows how the component handles 14:37 with 15-minute steps. - Displays special values that don't align with step intervals. - Useful for understanding flexible time value handling.
Selected time: 14:37
Note: 14:37 is not in the 15-minute step range, but it will still be displayed in the list

Uncontrolled

Uncontrolled: Demonstrates uncontrolled mode with default value.
- Shows TimeCalendar operating in uncontrolled mode with defaultValue. - Useful for simple implementations without external state management. - Demonstrates internal state handling and default value setting.
Uncontrolled mode, default value: 10:45

Trigger Ref

TriggerRef: Demonstrates using an external trigger element via triggerRef.
- Shows how to use triggerRef to connect an external button to TimeCalendar. - Useful when the trigger element is outside the TimeCalendar component. - The dropdown will position relative to the referenced element.
Using triggerRef pattern - trigger is external to TimeCalendar

With Time Input

WithTimeInput: TimeInput as trigger with focus-to-open behavior.
- TimeInput and TimeCalendar share the same value state - Dropdown opens on TimeInput focus - Selecting from dropdown updates TimeInput value - Typing in TimeInput updates the selected time
Selected: 14:30
Click the dropdown icon to open. Type or select from list.

API reference

MonthCalendarTypeDefault
className
string
|
undefined
-
currentMonth
Date
|
undefined
-
dateComparisonMode
undefined
|
"exact-time"
|
"date-only"
-
defaultValue
CalendarValue
|
undefined
-
disabledDates
Date[]
|
undefined
-
fixedGrid
boolean
|
undefined
-
highlightDates
Date[]
|
undefined
-
highlightToday
boolean
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxDate
Date
|
undefined
-
minDate
Date
|
undefined
-
onChange
((value: CalendarValue) => void)
|
undefined
-
onMonthChange
((month: Date) => void)
|
undefined
-
readOnly
boolean
|
undefined
-
selectionMode
undefined
|
"single"
|
"multiple"
|
"range"
-
showOutsideDays
boolean
|
undefined
-
showWeekNumbers
boolean
|
undefined
-
timeZone
string
|
undefined
-
value
CalendarValue
|
undefined
-
weekStartsOn
undefined
|
0
|
1
|
2
|
3
|
4
|
5
|
6
-
weekdayNames
string[]
|
undefined
-
direction
undefined
|
"horizontal"
|
"vertical"
-
variant
undefined
|
"default"
|
"dark"
-
DateInputValueTypeDefault
value
Date
|
null
|
undefined
-
onChange
((date: Date
|
null) => void)
|
undefined
-
format
string
|
undefined
-
size
undefined
|
"default"
|
"large"
-
selected
boolean
|
undefined
-
className
string
|
undefined
-
focusSelection
undefined
|
"none"
|
"all"
|
"end"
-
onIsEditingChange
((isEditing: boolean) => void)
|
undefined
-
variant
undefined
|
"default"
|
"light"
|
"dark"
|
"reset"
-
enableCache
boolean
|
undefined
true
enableKeyboardNavigation
boolean
|
undefined
true
enablePrediction
boolean
|
undefined
false
enableProfiling
boolean
|
undefined
false
locale
string
|
Locale
|
undefined
-
maxDate
Date
|
undefined
-
minDate
Date
|
undefined
-
onEnterKeyDown
(() => void)
|
undefined
-
prefixElement
ReactNode -
suffixElement
ReactNode -
DateRangeInputTypeDefault
endDisabled
boolean
|
undefined
-
endPlaceholder
string
|
undefined
-
endSuffixElement
ReactNode -
endValue
Date
|
null
|
undefined
-
format
string
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxDate
Date
|
undefined
-
minDate
Date
|
undefined
-
onEndChange
((date: Date
|
null) => void)
|
undefined
-
onEndFocus
(() => void)
|
undefined
-
onEnterKeyDown
(() => void)
|
undefined
-
onStartChange
((date: Date
|
null) => void)
|
undefined
-
onStartFocus
(() => void)
|
undefined
-
rangePrecision
number
|
undefined
1 - Minimum unit is 1 day
startDisabled
boolean
|
undefined
-
startPlaceholder
string
|
undefined
-
startSuffixElement
ReactNode -
startValue
Date
|
null
|
undefined
-
size
undefined
|
"default"
|
"large"
-
selected
boolean
|
undefined
-
className
string
|
undefined
-
focusSelection
undefined
|
"none"
|
"all"
|
"end"
-
onIsEditingChange
((isEditing: boolean) => void)
|
undefined
-
variant
undefined
|
"default"
|
"light"
|
"dark"
|
"reset"
-
TimeCalendarTypeDefault
className
string
|
undefined
-
hourStep
number
|
undefined
-
minuteStep
number
|
undefined
-
open
boolean
|
undefined
-
onOpenChange
((open: boolean) => void)
|
undefined
-
closeOnSelect
boolean
|
undefined
-
triggerRef
RefObject<HTMLElement>
|
undefined
-
triggerSelector
string
|
undefined
-
defaultValue
Date
|
null
|
undefined
-
disabled
boolean
|
undefined
-
format
string
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxTime
Date
|
undefined
-
minTime
Date
|
undefined
-
onChange
((time: Date
|
null) => void)
|
undefined
-
readOnly
boolean
|
undefined
-
value
Date
|
null
|
undefined
-
metaStep
number
|
undefined
-
shiftStep
number
|
undefined
-
step
number
|
undefined
-
offset
number
|
undefined
-
placement
undefined
|
"top"
|
"right"
|
"bottom"
|
"left"
|
"top-start"
|
"top-end"
|
"right-start"
|
"right-end"
|
"bottom-start"
|
"bottom-end"
|
"left-start"
|
"left-end"
-
matchTriggerWidth
boolean
|
undefined
-
variant
undefined
|
"default"
|
"light"
|
"reset"
-
TimeInputValueTypeDefault
value
Date
|
null
|
undefined
-
onChange
((time: Date
|
null) => void)
|
undefined
-
format
string
|
undefined
-
defaultValue
Date
|
null
|
undefined
-
step
number
|
undefined
-
size
undefined
|
"default"
|
"large"
-
selected
boolean
|
undefined
-
className
string
|
undefined
-
focusSelection
undefined
|
"none"
|
"all"
|
"end"
-
onIsEditingChange
((isEditing: boolean) => void)
|
undefined
-
variant
undefined
|
"default"
|
"light"
|
"dark"
|
"reset"
-
prefixElement
ReactNode -
suffixElement
ReactNode -
locale
string
|
Locale
|
undefined
-
maxTime
Date
|
undefined
-
minTime
Date
|
undefined
-
metaStep
number
|
undefined
-
shiftStep
number
|
undefined
-
enableCache
boolean
|
undefined
-
enableKeyboardNavigation
boolean
|
undefined
-
enableProfiling
boolean
|
undefined
-
onEnterKeyDown
(() => void)
|
undefined
-
TimeRangeInputTypeDefault
endDisabled
boolean
|
undefined
-
endPlaceholder
string
|
undefined
-
endSuffixElement
ReactNode -
endValue
Date
|
null
|
undefined
-
format
string
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxTime
Date
|
undefined
-
minTime
Date
|
undefined
-
onEndChange
((time: Date
|
null) => void)
|
undefined
-
onEndFocus
(() => void)
|
undefined
-
onEnterKeyDown
(() => void)
|
undefined
-
onStartChange
((time: Date
|
null) => void)
|
undefined
-
onStartFocus
(() => void)
|
undefined
-
startDisabled
boolean
|
undefined
-
startPlaceholder
string
|
undefined
-
startSuffixElement
ReactNode -
startValue
Date
|
null
|
undefined
-
size
undefined
|
"default"
|
"large"
-
selected
boolean
|
undefined
-
className
string
|
undefined
-
focusSelection
undefined
|
"none"
|
"all"
|
"end"
-
onIsEditingChange
((isEditing: boolean) => void)
|
undefined
-
variant
undefined
|
"default"
|
"light"
|
"dark"
|
"reset"
-
QuarterCalendarTypeDefault
currentYear
number
|
undefined
-
defaultValue
Quarter
|
undefined
-
disabled
boolean
|
undefined
-
disabledQuarters
{ quarter: number; year: number; }[]
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxYear
number
|
undefined
-
minYear
number
|
undefined
-
onChange
((quarter: Quarter
|
null) => void)
|
undefined
-
readOnly
boolean
|
undefined
-
startYear
number
|
undefined
-
value
Quarter
|
null
|
undefined
-
onNavigate
((direction: "prev"
|
"next", newYear: number) => void)
|
undefined
-
className
string
|
undefined
-
variant
undefined
|
"default"
|
"dark"
-
YearCalendarTypeDefault
currentYear
Date
|
undefined
-
defaultValue
Date
|
undefined
-
disabled
boolean
|
undefined
-
disabledYears
Date[]
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxYear
Date
|
undefined
-
minYear
Date
|
undefined
-
onChange
((year: Date
|
null) => void)
|
undefined
-
readOnly
boolean
|
undefined
-
startYear
Date
|
undefined
-
value
Date
|
null
|
undefined
-
yearCount
number
|
undefined
-
onNavigate
((direction: "prev"
|
"next", newStartYear: Date) => void)
|
undefined
-
className
string
|
undefined
-
variant
undefined
|
"default"
|
"dark"
-
inferSelectionModeTypeDefault
end
Date -
start
Date -
inferMonthFromValueTypeDefault
end
Date -
start
Date -
formatQuarterTypeDefault
label
string -
months
string[] -
quarter
1
|
2
|
3
|
4
-
year
number -
getQuarterDateRangeTypeDefault
label
string -
months
string[] -
quarter
1
|
2
|
3
|
4
-
year
number -
timeToDateTypeDefault
hour
number -
minute
number -
useDateInputTypeDefault
onPressEnd
(((event: PressEvent) => void) & ((e: PointerEvent) => void))
|
undefined
-
onPressStart
(((event: PressEvent) => void) & ((e: PointerEvent) => void))
|
undefined
-
readOnly
boolean
|
undefined
-
ref
Ref<HTMLInputElement>
|
undefined
-
defaultValue
Date
|
null
|
undefined
-
disabled
boolean
|
undefined
-
format
string
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxDate
Date
|
undefined
-
minDate
Date
|
undefined
-
onChange
((date: Date
|
null) => void)
|
undefined
-
value
Date
|
null
|
undefined
-
metaStep
number
|
undefined
-
shiftStep
number
|
undefined
-
step
number
|
undefined
-
enableCache
boolean
|
undefined
-
enableKeyboardNavigation
boolean
|
undefined
-
enablePrediction
boolean
|
undefined
-
enableProfiling
boolean
|
undefined
-
onEnterKeyDown
(() => void)
|
undefined
-
useTimeInputTypeDefault
onPressEnd
(((event: PressEvent) => void) & ((e: PointerEvent) => void))
|
undefined
-
onPressStart
(((event: PressEvent) => void) & ((e: PointerEvent) => void))
|
undefined
-
readOnly
boolean
|
undefined
-
ref
Ref<HTMLInputElement>
|
undefined
-
defaultValue
Date
|
null
|
undefined
-
disabled
boolean
|
undefined
-
format
string
|
undefined
-
locale
string
|
Locale
|
undefined
-
maxTime
Date
|
undefined
-
minTime
Date
|
undefined
-
onChange
((time: Date
|
null) => void)
|
undefined
-
value
Date
|
null
|
undefined
-
metaStep
number
|
undefined
-
shiftStep
number
|
undefined
-
step
number
|
undefined
-
enableCache
boolean
|
undefined
-
enableKeyboardNavigation
boolean
|
undefined
-
enableProfiling
boolean
|
undefined
-
onEnterKeyDown
(() => void)
|
undefined
-