CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-dates

A responsive and accessible date range picker component built with React

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

calendar-components.mddocs/

Calendar Components

Low-level calendar rendering components for building custom date picker interfaces. These components handle the visual presentation and interaction of calendar grids, months, and individual days.

Capabilities

DayPicker

Complete calendar component with navigation controls, keyboard shortcuts, and accessibility features.

/**
 * Complete calendar component with navigation and accessibility
 * @param props - DayPicker configuration
 * @returns Full calendar component with navigation
 */
function DayPicker(props: DayPickerProps): ReactElement;

interface DayPickerProps {
  // Calendar presentation
  enableOutsideDays?: boolean;                    // Default: false
  numberOfMonths?: number;                        // Default: 2  
  orientation?: 'horizontal' | 'vertical' | 'verticalScrollable'; // Default: 'horizontal'
  withPortal?: boolean;                          // Default: false
  onOutsideClick?: () => void;                   // Default: () => {}
  hidden?: boolean;                              // Default: false
  initialVisibleMonth?: () => moment.Moment;     // Default: () => moment()
  firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | null; // Default: null (uses moment locale)
  renderCalendarInfo?: () => ReactNode;          // Default: null
  calendarInfoPosition?: 'top' | 'bottom' | 'before' | 'after'; // Default: 'bottom'
  hideKeyboardShortcutsPanel?: boolean;          // Default: false
  daySize?: number;                              // Default: 39 (non-negative integer)
  isRTL?: boolean;                              // Default: false
  verticalHeight?: number;                       // Default: null (non-negative integer)
  noBorder?: boolean;                           // Default: false
  transitionDuration?: number;                   // Default: undefined (non-negative integer)
  verticalBorderSpacing?: number;                // Default: undefined (non-negative integer)
  horizontalMonthPadding?: number;               // Default: 13 (non-negative integer)

  // Navigation
  dayPickerNavigationInlineStyles?: object;      // Default: null
  disablePrev?: boolean;                        // Default: false
  disableNext?: boolean;                        // Default: false
  navPosition?: 'navPositionTop' | 'navPositionBottom'; // Default: 'navPositionTop'
  navPrev?: ReactNode;                          // Default: null
  navNext?: ReactNode;                          // Default: null
  renderNavPrevButton?: (props: any) => ReactNode; // Default: null
  renderNavNextButton?: (props: any) => ReactNode; // Default: null
  noNavButtons?: boolean;                       // Default: false
  noNavNextButton?: boolean;                    // Default: false
  noNavPrevButton?: boolean;                    // Default: false
  onPrevMonthClick?: (newMonth: moment.Moment) => void; // Default: () => {}
  onNextMonthClick?: (newMonth: moment.Moment) => void; // Default: () => {}
  onMonthChange?: (newMonth: moment.Moment) => void;    // Default: () => {}
  onYearChange?: (newMonth: moment.Moment) => void;     // Default: () => {}
  onGetNextScrollableMonths?: (e: Event) => void;       // Default: () => {} (VERTICAL_SCROLLABLE only)
  onGetPrevScrollableMonths?: (e: Event) => void;       // Default: () => {} (VERTICAL_SCROLLABLE only)

  // Month customization (mutually exclusive)
  renderMonthText?: (month: moment.Moment) => ReactNode; // Default: null (mutually exclusive with renderMonthElement)
  renderMonthElement?: (props: any) => ReactNode;         // Default: null (mutually exclusive with renderMonthText)
  renderWeekHeaderElement?: (day: string) => ReactNode;   // Default: null

  // Day customization
  modifiers?: { [date: string]: Set<string> };    // Default: {} (objectOf ModifiersShape)
  renderCalendarDay?: (props: any) => ReactNode;   // Default: undefined
  renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode; // Default: null
  onDayClick?: (day: moment.Moment, e: SyntheticEvent) => void;         // Default: () => {}
  onDayMouseEnter?: (day: moment.Moment, e: SyntheticEvent) => void;    // Default: () => {}
  onDayMouseLeave?: (day: moment.Moment, e: SyntheticEvent) => void;    // Default: () => {}

  // Accessibility
  isFocused?: boolean;                           // Default: false
  getFirstFocusableDay?: (month: moment.Moment) => moment.Moment; // Default: null
  onBlur?: (e: SyntheticEvent) => void;         // Default: () => {}
  showKeyboardShortcuts?: boolean;               // Default: false
  onTab?: (e: SyntheticEvent) => void;          // Default: () => {}
  onShiftTab?: () => void;                      // Default: () => {}
  renderKeyboardShortcutsButton?: (props: any) => ReactNode; // Default: undefined
  renderKeyboardShortcutsPanel?: (props: any) => ReactNode;  // Default: undefined

  // Internationalization
  monthFormat?: string;                         // Default: 'MMMM YYYY'
  weekDayFormat?: string;                       // Default: 'dd'
  phrases?: DayPickerPhrasesShape;              // Default: DayPickerPhrases
  dayAriaLabelFormat?: string;                  // Default: undefined
}

Usage Examples:

import React, { useState } from "react";
import { DayPicker } from "react-dates";
import moment from "moment";

// Basic calendar display
function BasicCalendar() {
  const [currentMonth, setCurrentMonth] = useState(moment());

  return (
    <DayPicker
      month={currentMonth}
      onPrevMonthClick={setCurrentMonth}
      onNextMonthClick={setCurrentMonth}
      numberOfMonths={1}
      onDayClick={(day) => console.log('Day clicked:', day.format('YYYY-MM-DD'))}
    />
  );
}

// Custom calendar with events
function EventCalendar({ events }) {
  const [currentMonth, setCurrentMonth] = useState(moment());

  const renderDayContents = (day, modifiers) => {
    const dayKey = day.format('YYYY-MM-DD');
    const dayEvents = events[dayKey] || [];
    
    return (
      <div className="calendar-day">
        <span className="day-number">{day.format('D')}</span>
        {dayEvents.length > 0 && (
          <div className="event-dots">
            {dayEvents.slice(0, 3).map((event, index) => (
              <div 
                key={index} 
                className="event-dot" 
                style={{ backgroundColor: event.color }}
              />
            ))}
            {dayEvents.length > 3 && <span className="more-events">+{dayEvents.length - 3}</span>}
          </div>
        )}
      </div>
    );
  };

  const handleDayClick = (day) => {
    const dayEvents = events[day.format('YYYY-MM-DD')] || [];
    if (dayEvents.length > 0) {
      console.log(`Events for ${day.format('MMM D')}:`, dayEvents);
    }
  };

  return (
    <DayPicker
      month={currentMonth}
      onPrevMonthClick={setCurrentMonth}
      onNextMonthClick={setCurrentMonth}
      numberOfMonths={1}
      renderDayContents={renderDayContents}
      onDayClick={handleDayClick}
      renderCalendarInfo={() => (
        <div className="calendar-legend">
          <h4>Event Calendar</h4>
          <p>Click on dates with dots to see events</p>
        </div>
      )}
    />
  );
}

CalendarMonthGrid

Grid layout component for displaying multiple calendar months with transitions and animations.

/**
 * Grid layout for multiple calendar months
 * @param props - CalendarMonthGrid configuration
 * @returns Month grid component with transitions
 */
function CalendarMonthGrid(props: CalendarMonthGridProps): ReactElement;

interface CalendarMonthGridProps {
  // Layout and presentation
  enableOutsideDays?: boolean;                           // Default: false - Show days from adjacent months
  firstVisibleMonthIndex?: number;                       // Default: 0 - Index of first visible month in grid
  horizontalMonthPadding?: number;                       // Default: 13 (non-negative integer) - Horizontal padding for months
  numberOfMonths?: number;                               // Default: 1 - Number of months to display
  orientation?: 'horizontal' | 'vertical' | 'verticalScrollable'; // Default: 'horizontal' - Layout orientation
  daySize?: number;                                      // Default: 39 (non-negative integer) - Size of each day cell
  isRTL?: boolean;                                      // Default: false - Right-to-left layout
  verticalBorderSpacing?: number;                        // Default: undefined (non-negative integer) - Vertical spacing between day rows
  
  // Animation and transitions
  isAnimating?: boolean;                                 // Default: false - Whether grid is currently animating
  translationValue?: number;                             // Default: null - Translation offset for animations
  transitionDuration?: number;                           // Default: 200 (non-negative integer) - Animation duration in milliseconds
  onMonthTransitionEnd?: () => void;                     // Default: () => {} - Called when month transition completes
  
  // Month state and navigation
  initialMonth?: moment.Moment;                          // Default: moment() - Initial month to display
  onMonthChange?: (newMonth: moment.Moment) => void;     // Default: () => {} - Called when month changes
  onYearChange?: (newMonth: moment.Moment) => void;      // Default: () => {} - Called when year changes
  
  // Day interaction
  onDayClick?: (day: moment.Moment, event: SyntheticEvent) => void;        // Default: () => {} - Day click handler
  onDayMouseEnter?: (day: moment.Moment, event: SyntheticEvent) => void;   // Default: () => {} - Day mouse enter handler  
  onDayMouseLeave?: (day: moment.Moment, event: SyntheticEvent) => void;   // Default: () => {} - Day mouse leave handler
  
  // Day modifiers and styling
  modifiers?: { [monthString: string]: { [dateString: string]: Set<string> } }; // Default: {} - Object mapping month strings to date modifiers
  
  // Custom rendering (mutually exclusive)
  renderMonthText?: (month: moment.Moment) => ReactNode; // Default: null - Custom month header text renderer (mutually exclusive with renderMonthElement)
  renderMonthElement?: (props: {                         // Default: null - Custom month header element renderer (mutually exclusive with renderMonthText)
    month: moment.Moment;
    onMonthSelect: (currentMonth: moment.Moment, newMonthVal: string | number) => void;
    onYearSelect: (currentMonth: moment.Moment, newYearVal: string | number) => void;
    isVisible: boolean;
  }) => ReactNode;
  
  // Day rendering customization
  renderCalendarDay?: (props: CalendarDayProps) => ReactNode;              // Default: undefined - Custom day cell renderer
  renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode; // Default: null - Custom day contents renderer
  
  // Focus management
  focusedDate?: moment.Moment;                          // Default: null - Currently focused date for keyboard navigation
  isFocused?: boolean;                                  // Default: false - Whether to move focus to focusable day
  firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | null; // Default: null (uses moment locale) - First day of week (0=Sunday, 6=Saturday)
  
  // Layout callbacks
  setMonthTitleHeight?: (height: number) => void;       // Default: null - Callback to report month title height
  
  // Internationalization
  monthFormat?: string;                                 // Default: 'MMMM YYYY' - Moment.js format for month header
  dayAriaLabelFormat?: string;                         // Default: undefined - Moment.js format for day ARIA labels
  phrases?: {                                          // Default: CalendarDayPhrases - Accessibility and i18n text
    chooseAvailableDate?: (args: { date: string }) => string;
    dateIsUnavailable?: (args: { date: string }) => string;
    dateIsSelected?: (args: { date: string }) => string;  
    dateIsSelectedAsStartDate?: (args: { date: string }) => string;
    dateIsSelectedAsEndDate?: (args: { date: string }) => string;
  };
}

Public Methods:

// Navigation and animation methods (accessible via ref)
onTransitionEnd(): void;
onMonthSelect(currentMonth: moment.Moment, newMonthVal: string | number): void;
onYearSelect(currentMonth: moment.Moment, newYearVal: string | number): void;

Usage Examples:

import React, { useState } from "react";
import { CalendarMonthGrid } from "react-dates";
import moment from "moment";

// Multi-month calendar display
function MultiMonthCalendar() {
  const [isAnimating, setIsAnimating] = useState(false);
  const [currentMonth, setCurrentMonth] = useState(moment());

  const handleMonthSelect = (currentMonth, newMonthVal) => {
    setIsAnimating(true);
    const newMonth = currentMonth.clone().month(parseInt(newMonthVal));
    setCurrentMonth(newMonth);
  };

  const handleTransitionEnd = () => {
    setIsAnimating(false);
  };

  return (
    <CalendarMonthGrid
      numberOfMonths={3}
      orientation="horizontal"
      initialMonth={currentMonth}
      isAnimating={isAnimating}
      onMonthSelect={handleMonthSelect}
      onMonthTransitionEnd={handleTransitionEnd}
      onDayClick={(day) => console.log('Selected:', day.format('YYYY-MM-DD'))}
    />
  );
}

// Responsive calendar grid
function ResponsiveCalendarGrid() {
  const [numberOfMonths, setNumberOfMonths] = useState(
    window.innerWidth > 768 ? 2 : 1
  );
  const [orientation, setOrientation] = useState(
    window.innerWidth > 768 ? 'horizontal' : 'vertical'
  );

  React.useEffect(() => {
    const handleResize = () => {
      const isMobile = window.innerWidth <= 768;
      setNumberOfMonths(isMobile ? 1 : 2);
      setOrientation(isMobile ? 'vertical' : 'horizontal');
    };

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return (
    <CalendarMonthGrid
      numberOfMonths={numberOfMonths}
      orientation={orientation}
      transitionDuration={200}
      onDayClick={(day) => console.log('Date selected:', day.format('YYYY-MM-DD'))}
    />
  );
}

CalendarMonth

Individual month view component containing the calendar day grid for a single month.

/**
 * Individual month view with calendar day grid
 * @param props - CalendarMonth configuration
 * @returns Single month calendar component
 */
function CalendarMonth(props: CalendarMonthProps): ReactElement;

interface CalendarMonthProps {
  // Core month data
  month?: moment.Moment;                                    // Default: moment() - The month to display
  
  // Layout and presentation
  horizontalMonthPadding?: number;                         // Default: 13 (non-negative integer) - Horizontal padding for month container
  daySize?: number;                                        // Default: 39 (non-negative integer) - Size of each day cell
  isVisible?: boolean;                                     // Default: true - Whether month is visible
  orientation?: 'horizontal' | 'vertical' | 'verticalScrollable'; // Default: 'horizontal' - Layout orientation
  verticalBorderSpacing?: number;                          // Default: undefined (non-negative integer) - Vertical spacing between day rows
  
  // Day behavior and outside days
  enableOutsideDays?: boolean;                            // Default: false - Show days from adjacent months
  firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | null;   // Default: null (uses moment locale) - First day of week (0=Sunday, 6=Saturday)
  
  // Day interaction handlers
  onDayClick?: (day: moment.Moment, event: SyntheticEvent) => void;        // Default: () => {} - Day click handler
  onDayMouseEnter?: (day: moment.Moment, event: SyntheticEvent) => void;   // Default: () => {} - Day mouse enter handler
  onDayMouseLeave?: (day: moment.Moment, event: SyntheticEvent) => void;   // Default: () => {} - Day mouse leave handler
  
  // Month/Year selection handlers
  onMonthSelect?: (currentMonth: moment.Moment, newMonthVal: string | number) => void; // Default: () => {} - Month selection handler
  onYearSelect?: (currentMonth: moment.Moment, newYearVal: string | number) => void;   // Default: () => {} - Year selection handler
  
  // Rendering customization (mutually exclusive)
  renderMonthText?: (month: moment.Moment) => ReactNode;   // Default: null - Custom month header text renderer (mutually exclusive with renderMonthElement)
  renderMonthElement?: (props: {                           // Default: null - Custom month header element renderer (mutually exclusive with renderMonthText)
    month: moment.Moment;
    onMonthSelect: function;
    onYearSelect: function;
    isVisible: boolean;
  }) => ReactNode;
  
  // Day rendering customization
  renderCalendarDay?: (props: CalendarDayProps) => ReactNode;              // Default: (props) => <CalendarDay {...props} /> - Custom day cell renderer
  renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode; // Default: null - Custom day contents renderer
  
  // Day modifiers and styling
  modifiers?: { [dateString: string]: Set<string> };       // Default: {} - Object mapping ISO date strings to Sets of modifier strings
  
  // Focus management
  focusedDate?: moment.Moment;                            // Default: null - Currently focused date for keyboard navigation
  isFocused?: boolean;                                    // Default: false - Whether to move focus to focusable day
  
  // Layout callback
  setMonthTitleHeight?: (height: number) => void;         // Default: null - Callback to report month title height
  
  // Internationalization
  monthFormat?: string;                                   // Default: 'MMMM YYYY' - Moment.js format for month header
  dayAriaLabelFormat?: string;                           // Default: undefined - Moment.js format for day ARIA labels
  phrases?: {                                            // Default: CalendarDayPhrases - Accessibility and i18n text
    chooseAvailableDate?: ({ date }: { date: string }) => string;
    dateIsUnavailable?: ({ date }: { date: string }) => string;
    dateIsSelected?: ({ date }: { date: string }) => string;
    dateIsSelectedAsStartDate?: ({ date }: { date: string }) => string;
    dateIsSelectedAsEndDate?: ({ date }: { date: string }) => string;
  };
}

Public Methods:

// Layout and reference methods (accessible via ref)
setMonthTitleHeight(): void;
setCaptionRef(ref: HTMLElement): void;

Usage Examples:

import React, { useState, useRef } from "react";
import { CalendarMonth } from "react-dates";
import moment from "moment";

// Single month display
function MonthView({ selectedMonth }) {
  const monthRef = useRef(null);

  const handleDayClick = (day) => {
    console.log('Day selected:', day.format('YYYY-MM-DD'));
  };

  const renderMonthHeader = (month) => {
    return (
      <div className="custom-month-header">
        <h2>{month.format('MMMM YYYY')}</h2>
        <p>Select a date below</p>
      </div>
    );
  };

  return (
    <CalendarMonth
      ref={monthRef}
      month={selectedMonth}
      onDayClick={handleDayClick}
      renderMonthText={renderMonthHeader}
      enableOutsideDays={true}
    />
  );
}

// Custom styled month
function StyledMonthView({ month, holidays, events }) {
  const getModifiers = (day) => {
    const modifiers = new Set();
    const dayKey = day.format('YYYY-MM-DD');
    
    if (holidays.includes(dayKey)) {
      modifiers.add('holiday');
    }
    if (events[dayKey]) {
      modifiers.add('has-events');
    }
    if (day.day() === 0 || day.day() === 6) {
      modifiers.add('weekend');
    }
    
    return modifiers;
  };

  const renderDayContents = (day, modifiers) => {
    const hasEvents = modifiers.has('has-events');
    const isHoliday = modifiers.has('holiday');
    
    return (
      <div className={`day-cell ${isHoliday ? 'holiday' : ''}`}>
        <span>{day.format('D')}</span>
        {hasEvents && <div className="event-indicator" />}
      </div>
    );
  };

  return (
    <CalendarMonth
      month={month}
      renderDayContents={renderDayContents}
      modifiers={{ getModifiers }}
      onDayClick={(day) => {
        const dayEvents = events[day.format('YYYY-MM-DD')];
        if (dayEvents) {
          console.log('Events for this day:', dayEvents);
        }
      }}
    />
  );
}

CalendarDay

Individual day cell component in the calendar grid, handling day selection and customization.

/**
 * Individual day cell in calendar grid
 * @param props - CalendarDay configuration
 * @returns Single day cell component
 */
function CalendarDay(props: CalendarDayProps): ReactElement;

interface CalendarDayProps {
  // Day data and presentation
  day: moment.Moment;                                    // Required: momentPropTypes.momentObj - The moment object representing the day
  daySize?: number;                                      // Default: 39 (DAY_SIZE) - nonNegativeInteger - Size of the day cell in pixels
  isOutsideDay?: boolean;                               // Default: false - Whether this day belongs to an adjacent month
  
  // Modifiers and styling
  modifiers?: Set<string>;                              // Default: new Set() - ModifiersShape (Set of strings) - Visual state modifiers
  
  // Focus and interaction state
  isFocused?: boolean;                                  // Default: false - Whether the day should receive focus
  tabIndex?: 0 | -1;                                   // Default: -1 - Tab navigation index (0 = focusable, -1 = not focusable)
  
  // Event handlers
  onDayClick?: (day: moment.Moment, event: SyntheticEvent) => void;        // Default: () => {} - Day click handler
  onDayMouseEnter?: (day: moment.Moment, event: SyntheticEvent) => void;   // Default: () => {} - Mouse enter handler
  onDayMouseLeave?: (day: moment.Moment, event: SyntheticEvent) => void;   // Default: () => {} - Mouse leave handler
  
  // Custom rendering
  renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode | null; // Default: null - Custom day contents renderer
  
  // Accessibility
  ariaLabelFormat?: string;                            // Default: 'dddd, LL' - Moment.js format string for ARIA labels
  
  // Internationalization
  phrases?: {                                          // Default: CalendarDayPhrases - Accessibility and i18n text
    chooseAvailableDate?: ({ date }: { date: string }) => string;
    dateIsUnavailable?: ({ date }: { date: string }) => string;
    dateIsSelected?: ({ date }: { date: string }) => string;
    dateIsSelectedAsStartDate?: ({ date }: { date: string }) => string;
    dateIsSelectedAsEndDate?: ({ date }: { date: string }) => string;
  };
}

Public Methods:

// Event handling methods (accessible via ref)
onDayClick(event: SyntheticEvent): void;
onDayMouseEnter(event: SyntheticEvent): void;
onDayMouseLeave(event: SyntheticEvent): void;
onKeyDown(event: KeyboardEvent): void;

Usage Examples:

import React from "react";
import { CalendarDay } from "react-dates";
import moment from "moment";

// Custom day rendering
function CustomCalendarDay({ day, isSelected, isBlocked, events }) {
  const modifiers = new Set();
  if (isSelected) modifiers.add('selected');
  if (isBlocked) modifiers.add('blocked');
  if (events?.length > 0) modifiers.add('has-events');

  const renderDayContents = (day, modifiers) => {
    const dayNumber = day.format('D');
    const hasEvents = modifiers.has('has-events');
    
    return (
      <div className="custom-day-contents">
        <span className="day-number">{dayNumber}</span>
        {hasEvents && (
          <div className="event-indicators">
            {events.slice(0, 2).map((event, index) => (
              <div 
                key={index}
                className="event-dot"
                style={{ backgroundColor: event.color }}
                title={event.title}
              />
            ))}
            {events.length > 2 && (
              <span className="more-events">+{events.length - 2}</span>
            )}
          </div>
        )}
      </div>
    );
  };

  const handleDayClick = (day, event) => {
    if (!isBlocked) {
      console.log('Day clicked:', day.format('YYYY-MM-DD'));
      if (events?.length > 0) {
        console.log('Day events:', events);
      }
    }
  };

  return (
    <CalendarDay
      day={day}
      modifiers={modifiers}
      onDayClick={handleDayClick}
      renderDayContents={renderDayContents}
      onDayMouseEnter={(day) => {
        if (events?.length > 0) {
          // Show event preview on hover
          console.log('Hovering day with events:', events);
        }
      }}
    />
  );
}

// Minimal day cell
function MinimalCalendarDay({ day, onClick }) {
  const handleClick = (day, event) => {
    onClick?.(day, event);
  };

  return (
    <CalendarDay
      day={day}
      onDayClick={handleClick}
      renderDayContents={(day) => (
        <span className="simple-day">{day.format('D')}</span>
      )}
    />
  );
}

Common Patterns

Custom Day Modifiers

// Define custom modifiers for day states
const getCustomModifiers = (day, selectedDates, blockedDates, highlightedDates) => {
  const modifiers = new Set();
  const dayKey = day.format('YYYY-MM-DD');
  
  // Standard modifiers
  if (selectedDates.includes(dayKey)) modifiers.add('selected');
  if (blockedDates.includes(dayKey)) modifiers.add('blocked');
  if (highlightedDates.includes(dayKey)) modifiers.add('highlighted');
  
  // Custom business logic modifiers
  if (day.day() === 0 || day.day() === 6) modifiers.add('weekend');
  if (day.isSame(moment(), 'day')) modifiers.add('today');
  if (day.isBefore(moment(), 'day')) modifiers.add('past');
  
  return modifiers;
};

// Usage in calendar components
<CalendarMonth
  month={currentMonth}
  modifiers={{ getCustomModifiers }}
  renderDayContents={(day, modifiers) => {
    let className = 'calendar-day';
    if (modifiers.has('weekend')) className += ' weekend';
    if (modifiers.has('today')) className += ' today';
    if (modifiers.has('blocked')) className += ' blocked';
    
    return <div className={className}>{day.format('D')}</div>;
  }}
/>

Dynamic Month Navigation

// Advanced month navigation with constraints
function ConstrainedCalendar({ minDate, maxDate }) {
  const [currentMonth, setCurrentMonth] = useState(moment());

  const handlePrevMonth = (newMonth) => {
    if (!minDate || newMonth.isAfter(minDate, 'month')) {
      setCurrentMonth(newMonth);
    }
  };

  const handleNextMonth = (newMonth) => {
    if (!maxDate || newMonth.isBefore(maxDate, 'month')) {
      setCurrentMonth(newMonth);
    }
  };

  return (
    <DayPicker
      month={currentMonth}
      onPrevMonthClick={handlePrevMonth}
      onNextMonthClick={handleNextMonth}
      renderNavPrevButton={(props) => (
        <button 
          {...props}
          disabled={minDate && currentMonth.clone().subtract(1, 'month').isBefore(minDate, 'month')}
        >
          ← Previous
        </button>
      )}
      renderNavNextButton={(props) => (
        <button 
          {...props}
          disabled={maxDate && currentMonth.clone().add(1, 'month').isAfter(maxDate, 'month')}
        >
          Next →
        </button>
      )}
    />
  );
}

Accessibility Integration

All calendar components include comprehensive accessibility features:

  • ARIA Labels: Automatic ARIA labeling for screen readers
  • Keyboard Navigation: Arrow keys, Tab, Enter, Escape support
  • Focus Management: Logical focus order and visual indicators
  • Screen Reader Announcements: Date selection and navigation feedback
  • Customizable Text: Override all accessibility text through phrases prop

Install with Tessl CLI

npx tessl i tessl/npm-react-dates

docs

calendar-components.md

calendar-controllers.md

index.md

input-components.md

main-pickers.md

utilities.md

tile.json