TimeCalendar
A calendar component for date selection with support for single, range, and multiple date picking
Installation
Terminal
Import
Component.tsx
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
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
| MonthCalendar | Type | Default |
|---|---|---|
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" | - |
| DateInputValue | Type | Default |
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 | - |
| DateRangeInput | Type | Default |
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" | - |
| TimeCalendar | Type | Default |
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" | - |
| TimeInputValue | Type | Default |
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 | - |
| TimeRangeInput | Type | Default |
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" | - |
| QuarterCalendar | Type | Default |
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" | - |
| YearCalendar | Type | Default |
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" | - |
| inferSelectionMode | Type | Default |
end | Date | - |
start | Date | - |
| inferMonthFromValue | Type | Default |
end | Date | - |
start | Date | - |
| formatQuarter | Type | Default |
label | string | - |
months | string[] | - |
quarter | 1 |2 |3 |4 | - |
year | number | - |
| getQuarterDateRange | Type | Default |
label | string | - |
months | string[] | - |
quarter | 1 |2 |3 |4 | - |
year | number | - |
| timeToDate | Type | Default |
hour | number | - |
minute | number | - |
| useDateInput | Type | Default |
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 | - |
| useTimeInput | Type | Default |
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 | - |