A responsive and accessible date range picker component built with React
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Headless calendar components that provide date selection functionality without input fields. These are ideal for custom implementations, embedded calendars, and advanced use cases where you need full control over the user interface.
Calendar-only component for date range selection without input fields. Perfect for building custom date range interfaces or embedded calendar displays.
/**
* Headless date range calendar controller without input fields
* @param props - DayPickerRangeController configuration
* @returns Calendar component for date range selection
*/
function DayPickerRangeController(props: DayPickerRangeControllerProps): ReactElement;
interface DayPickerRangeControllerProps {
// Date state
startDate?: moment.Moment | null;
endDate?: moment.Moment | null;
focusedInput?: 'startDate' | 'endDate' | null;
// Required callbacks
onDatesChange: ({ startDate, endDate }: {
startDate: moment.Moment | null;
endDate: moment.Moment | null;
}) => void;
onFocusChange: (focusedInput: 'startDate' | 'endDate' | null) => void;
// Calendar presentation
orientation?: 'horizontal' | 'vertical';
numberOfMonths?: number;
enableOutsideDays?: boolean;
daySize?: number;
isRTL?: boolean;
firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
verticalHeight?: number;
transitionDuration?: number;
verticalSpacing?: number;
horizontalMonthPadding?: number;
// Navigation
navPosition?: 'navPositionTop' | 'navPositionBottom';
navPrev?: ReactNode;
navNext?: ReactNode;
renderNavPrevButton?: (props: any) => ReactNode;
renderNavNextButton?: (props: any) => ReactNode;
onPrevMonthClick?: (newMonth: moment.Moment) => void;
onNextMonthClick?: (newMonth: moment.Moment) => void;
// Day customization
renderCalendarDay?: (props: any) => ReactNode;
renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode;
renderMonthElement?: (props: any) => ReactNode;
renderMonthText?: (month: moment.Moment) => ReactNode;
renderWeekHeaderElement?: (day: string) => ReactNode;
minimumNights?: number;
isDayBlocked?: (day: moment.Moment) => boolean;
isOutsideRange?: (day: moment.Moment) => boolean;
isDayHighlighted?: (day: moment.Moment) => boolean;
// Interaction
keepOpenOnDateSelect?: boolean;
onDayClick?: (day: moment.Moment, event: SyntheticEvent) => void;
onDayMouseEnter?: (day: moment.Moment, event: SyntheticEvent) => void;
onDayMouseLeave?: (day: moment.Moment, event: SyntheticEvent) => void;
// Internationalization
monthFormat?: string;
weekDayFormat?: string;
phrases?: object;
dayAriaLabelFormat?: string;
// Initial state
initialVisibleMonth?: () => moment.Moment;
// Focus management
isFocused?: boolean;
showKeyboardShortcuts?: boolean;
onBlur?: () => void;
onTab?: (event: SyntheticEvent) => void;
onShiftTab?: (event: SyntheticEvent) => void;
}Usage Examples:
import React, { useState } from "react";
import { DayPickerRangeController } from "react-dates";
import moment from "moment";
// Basic embedded calendar
function EmbeddedRangeCalendar() {
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
const [focusedInput, setFocusedInput] = useState('startDate');
return (
<div style={{ width: '100%', maxWidth: 600 }}>
<DayPickerRangeController
startDate={startDate}
endDate={endDate}
onDatesChange={({ startDate, endDate }) => {
setStartDate(startDate);
setEndDate(endDate);
}}
focusedInput={focusedInput}
onFocusChange={setFocusedInput}
numberOfMonths={2}
minimumNights={1}
/>
</div>
);
}
// Custom calendar with business logic
function BookingCalendar({ unavailableDates, rates }) {
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
const [focusedInput, setFocusedInput] = useState('startDate');
const isDayBlocked = (day) => {
return unavailableDates.some(blockedDay =>
moment(blockedDay).isSame(day, 'day')
);
};
const renderDayContents = (day, modifiers) => {
const rate = rates[day.format('YYYY-MM-DD')];
return (
<div>
<div>{day.format('D')}</div>
{rate && <div style={{ fontSize: '10px' }}>${rate}</div>}
</div>
);
};
return (
<div>
<DayPickerRangeController
startDate={startDate}
endDate={endDate}
onDatesChange={({ startDate, endDate }) => {
setStartDate(startDate);
setEndDate(endDate);
// Custom business logic
if (startDate && endDate) {
console.log('Total nights:', endDate.diff(startDate, 'days'));
}
}}
focusedInput={focusedInput}
onFocusChange={setFocusedInput}
isDayBlocked={isDayBlocked}
renderDayContents={renderDayContents}
minimumNights={2}
numberOfMonths={3}
/>
</div>
);
}Calendar-only component for single date selection without input fields. Ideal for embedded date pickers and custom interfaces.
/**
* Headless single date calendar controller without input fields
* @param props - DayPickerSingleDateController configuration
* @returns Calendar component for single date selection
*/
function DayPickerSingleDateController(props: DayPickerSingleDateControllerProps): ReactElement;
interface DayPickerSingleDateControllerProps {
// Date state
date?: moment.Moment | null;
focused?: boolean;
// Required callbacks
onDateChange: (date: moment.Moment | null) => void;
onFocusChange: ({ focused }: { focused: boolean }) => void;
// Calendar presentation
orientation?: 'horizontal' | 'vertical';
numberOfMonths?: number;
enableOutsideDays?: boolean;
daySize?: number;
isRTL?: boolean;
firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
verticalHeight?: number;
transitionDuration?: number;
verticalSpacing?: number;
horizontalMonthPadding?: number;
// Navigation
navPosition?: 'navPositionTop' | 'navPositionBottom';
navPrev?: ReactNode;
navNext?: ReactNode;
renderNavPrevButton?: (props: any) => ReactNode;
renderNavNextButton?: (props: any) => ReactNode;
onPrevMonthClick?: (newMonth: moment.Moment) => void;
onNextMonthClick?: (newMonth: moment.Moment) => void;
// Day customization
renderCalendarDay?: (props: any) => ReactNode;
renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode;
renderMonthElement?: (props: any) => ReactNode;
renderMonthText?: (month: moment.Moment) => ReactNode;
renderWeekHeaderElement?: (day: string) => ReactNode;
isDayBlocked?: (day: moment.Moment) => boolean;
isOutsideRange?: (day: moment.Moment) => boolean;
isDayHighlighted?: (day: moment.Moment) => boolean;
// Interaction
keepOpenOnDateSelect?: boolean;
onDayClick?: (day: moment.Moment, event: SyntheticEvent) => void;
onDayMouseEnter?: (day: moment.Moment, event: SyntheticEvent) => void;
onDayMouseLeave?: (day: moment.Moment, event: SyntheticEvent) => void;
// Internationalization
monthFormat?: string;
weekDayFormat?: string;
phrases?: object;
dayAriaLabelFormat?: string;
// Initial state
initialVisibleMonth?: () => moment.Moment;
// Focus management
isFocused?: boolean;
showKeyboardShortcuts?: boolean;
onBlur?: () => void;
onTab?: (event: SyntheticEvent) => void;
onShiftTab?: (event: SyntheticEvent) => void;
}Usage Examples:
import React, { useState } from "react";
import { DayPickerSingleDateController } from "react-dates";
import moment from "moment";
// Simple embedded calendar
function EmbeddedSingleCalendar() {
const [date, setDate] = useState(null);
const [focused, setFocused] = useState(true);
return (
<div style={{ width: 300 }}>
<DayPickerSingleDateController
date={date}
onDateChange={setDate}
focused={focused}
onFocusChange={({ focused }) => setFocused(focused)}
numberOfMonths={1}
/>
</div>
);
}
// Appointment scheduler calendar
function AppointmentCalendar({ availableSlots, onDateSelect }) {
const [selectedDate, setSelectedDate] = useState(null);
const [focused, setFocused] = useState(true);
const isDayBlocked = (day) => {
// Block days without available slots
const dayKey = day.format('YYYY-MM-DD');
return !availableSlots[dayKey] || availableSlots[dayKey].length === 0;
};
const renderDayContents = (day, modifiers) => {
const dayKey = day.format('YYYY-MM-DD');
const slots = availableSlots[dayKey];
const availableCount = slots ? slots.length : 0;
return (
<div style={{ textAlign: 'center' }}>
<div>{day.format('D')}</div>
{availableCount > 0 && (
<div style={{ fontSize: '10px', color: 'green' }}>
{availableCount} slots
</div>
)}
</div>
);
};
const handleDateChange = (date) => {
setSelectedDate(date);
onDateSelect(date);
};
return (
<div>
<h3>Select Appointment Date</h3>
<DayPickerSingleDateController
date={selectedDate}
onDateChange={handleDateChange}
focused={focused}
onFocusChange={({ focused }) => setFocused(focused)}
isDayBlocked={isDayBlocked}
renderDayContents={renderDayContents}
isOutsideRange={(day) => moment().diff(day) > 0} // No past dates
numberOfMonths={2}
/>
</div>
);
}Both controller components expose these public methods via refs:
// Navigation methods
onDayClick(day: moment.Moment, event: SyntheticEvent): void;
onDayMouseEnter(day: moment.Moment, event?: SyntheticEvent): void;
onDayMouseLeave(day: moment.Moment, event?: SyntheticEvent): void;
onPrevMonthClick(newMonth?: moment.Moment): void;
onNextMonthClick(newMonth?: moment.Moment): void;
// Focus management
getFirstFocusableDay(newMonth: moment.Moment): moment.Moment;// Combined validation function
const isDateUnavailable = (day) => {
const isPastDate = moment().diff(day) > 0;
const isWeekend = day.day() === 0 || day.day() === 6;
const isHoliday = holidays.includes(day.format('YYYY-MM-DD'));
return isPastDate || isWeekend || isHoliday;
};
// Usage
<DayPickerSingleDateController
isDayBlocked={isDateUnavailable}
// ... other props
/>// Complex day rendering with multiple data types
const renderDayContents = (day, modifiers) => {
const hasEvents = events[day.format('YYYY-MM-DD')];
const isHighlighted = modifiers.has('highlighted');
return (
<div className={`custom-day ${isHighlighted ? 'highlighted' : ''}`}>
<span className="day-number">{day.format('D')}</span>
{hasEvents && <div className="event-indicator" />}
</div>
);
};// Custom month navigation with logging
const handlePrevMonth = (newMonth) => {
console.log('Navigated to:', newMonth.format('MMMM YYYY'));
// Custom logic here
};
const handleNextMonth = (newMonth) => {
console.log('Navigated to:', newMonth.format('MMMM YYYY'));
// Custom logic here
};Install with Tessl CLI
npx tessl i tessl/npm-react-dates