0
# Calendar Components
1
2
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.
3
4
## Capabilities
5
6
### DayPicker
7
8
Complete calendar component with navigation controls, keyboard shortcuts, and accessibility features.
9
10
```jsx { .api }
11
/**
12
* Complete calendar component with navigation and accessibility
13
* @param props - DayPicker configuration
14
* @returns Full calendar component with navigation
15
*/
16
function DayPicker(props: DayPickerProps): ReactElement;
17
18
interface DayPickerProps {
19
// Calendar presentation
20
enableOutsideDays?: boolean; // Default: false
21
numberOfMonths?: number; // Default: 2
22
orientation?: 'horizontal' | 'vertical' | 'verticalScrollable'; // Default: 'horizontal'
23
withPortal?: boolean; // Default: false
24
onOutsideClick?: () => void; // Default: () => {}
25
hidden?: boolean; // Default: false
26
initialVisibleMonth?: () => moment.Moment; // Default: () => moment()
27
firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | null; // Default: null (uses moment locale)
28
renderCalendarInfo?: () => ReactNode; // Default: null
29
calendarInfoPosition?: 'top' | 'bottom' | 'before' | 'after'; // Default: 'bottom'
30
hideKeyboardShortcutsPanel?: boolean; // Default: false
31
daySize?: number; // Default: 39 (non-negative integer)
32
isRTL?: boolean; // Default: false
33
verticalHeight?: number; // Default: null (non-negative integer)
34
noBorder?: boolean; // Default: false
35
transitionDuration?: number; // Default: undefined (non-negative integer)
36
verticalBorderSpacing?: number; // Default: undefined (non-negative integer)
37
horizontalMonthPadding?: number; // Default: 13 (non-negative integer)
38
39
// Navigation
40
dayPickerNavigationInlineStyles?: object; // Default: null
41
disablePrev?: boolean; // Default: false
42
disableNext?: boolean; // Default: false
43
navPosition?: 'navPositionTop' | 'navPositionBottom'; // Default: 'navPositionTop'
44
navPrev?: ReactNode; // Default: null
45
navNext?: ReactNode; // Default: null
46
renderNavPrevButton?: (props: any) => ReactNode; // Default: null
47
renderNavNextButton?: (props: any) => ReactNode; // Default: null
48
noNavButtons?: boolean; // Default: false
49
noNavNextButton?: boolean; // Default: false
50
noNavPrevButton?: boolean; // Default: false
51
onPrevMonthClick?: (newMonth: moment.Moment) => void; // Default: () => {}
52
onNextMonthClick?: (newMonth: moment.Moment) => void; // Default: () => {}
53
onMonthChange?: (newMonth: moment.Moment) => void; // Default: () => {}
54
onYearChange?: (newMonth: moment.Moment) => void; // Default: () => {}
55
onGetNextScrollableMonths?: (e: Event) => void; // Default: () => {} (VERTICAL_SCROLLABLE only)
56
onGetPrevScrollableMonths?: (e: Event) => void; // Default: () => {} (VERTICAL_SCROLLABLE only)
57
58
// Month customization (mutually exclusive)
59
renderMonthText?: (month: moment.Moment) => ReactNode; // Default: null (mutually exclusive with renderMonthElement)
60
renderMonthElement?: (props: any) => ReactNode; // Default: null (mutually exclusive with renderMonthText)
61
renderWeekHeaderElement?: (day: string) => ReactNode; // Default: null
62
63
// Day customization
64
modifiers?: { [date: string]: Set<string> }; // Default: {} (objectOf ModifiersShape)
65
renderCalendarDay?: (props: any) => ReactNode; // Default: undefined
66
renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode; // Default: null
67
onDayClick?: (day: moment.Moment, e: SyntheticEvent) => void; // Default: () => {}
68
onDayMouseEnter?: (day: moment.Moment, e: SyntheticEvent) => void; // Default: () => {}
69
onDayMouseLeave?: (day: moment.Moment, e: SyntheticEvent) => void; // Default: () => {}
70
71
// Accessibility
72
isFocused?: boolean; // Default: false
73
getFirstFocusableDay?: (month: moment.Moment) => moment.Moment; // Default: null
74
onBlur?: (e: SyntheticEvent) => void; // Default: () => {}
75
showKeyboardShortcuts?: boolean; // Default: false
76
onTab?: (e: SyntheticEvent) => void; // Default: () => {}
77
onShiftTab?: () => void; // Default: () => {}
78
renderKeyboardShortcutsButton?: (props: any) => ReactNode; // Default: undefined
79
renderKeyboardShortcutsPanel?: (props: any) => ReactNode; // Default: undefined
80
81
// Internationalization
82
monthFormat?: string; // Default: 'MMMM YYYY'
83
weekDayFormat?: string; // Default: 'dd'
84
phrases?: DayPickerPhrasesShape; // Default: DayPickerPhrases
85
dayAriaLabelFormat?: string; // Default: undefined
86
}
87
```
88
89
**Usage Examples:**
90
91
```jsx
92
import React, { useState } from "react";
93
import { DayPicker } from "react-dates";
94
import moment from "moment";
95
96
// Basic calendar display
97
function BasicCalendar() {
98
const [currentMonth, setCurrentMonth] = useState(moment());
99
100
return (
101
<DayPicker
102
month={currentMonth}
103
onPrevMonthClick={setCurrentMonth}
104
onNextMonthClick={setCurrentMonth}
105
numberOfMonths={1}
106
onDayClick={(day) => console.log('Day clicked:', day.format('YYYY-MM-DD'))}
107
/>
108
);
109
}
110
111
// Custom calendar with events
112
function EventCalendar({ events }) {
113
const [currentMonth, setCurrentMonth] = useState(moment());
114
115
const renderDayContents = (day, modifiers) => {
116
const dayKey = day.format('YYYY-MM-DD');
117
const dayEvents = events[dayKey] || [];
118
119
return (
120
<div className="calendar-day">
121
<span className="day-number">{day.format('D')}</span>
122
{dayEvents.length > 0 && (
123
<div className="event-dots">
124
{dayEvents.slice(0, 3).map((event, index) => (
125
<div
126
key={index}
127
className="event-dot"
128
style={{ backgroundColor: event.color }}
129
/>
130
))}
131
{dayEvents.length > 3 && <span className="more-events">+{dayEvents.length - 3}</span>}
132
</div>
133
)}
134
</div>
135
);
136
};
137
138
const handleDayClick = (day) => {
139
const dayEvents = events[day.format('YYYY-MM-DD')] || [];
140
if (dayEvents.length > 0) {
141
console.log(`Events for ${day.format('MMM D')}:`, dayEvents);
142
}
143
};
144
145
return (
146
<DayPicker
147
month={currentMonth}
148
onPrevMonthClick={setCurrentMonth}
149
onNextMonthClick={setCurrentMonth}
150
numberOfMonths={1}
151
renderDayContents={renderDayContents}
152
onDayClick={handleDayClick}
153
renderCalendarInfo={() => (
154
<div className="calendar-legend">
155
<h4>Event Calendar</h4>
156
<p>Click on dates with dots to see events</p>
157
</div>
158
)}
159
/>
160
);
161
}
162
```
163
164
### CalendarMonthGrid
165
166
Grid layout component for displaying multiple calendar months with transitions and animations.
167
168
```jsx { .api }
169
/**
170
* Grid layout for multiple calendar months
171
* @param props - CalendarMonthGrid configuration
172
* @returns Month grid component with transitions
173
*/
174
function CalendarMonthGrid(props: CalendarMonthGridProps): ReactElement;
175
176
interface CalendarMonthGridProps {
177
// Layout and presentation
178
enableOutsideDays?: boolean; // Default: false - Show days from adjacent months
179
firstVisibleMonthIndex?: number; // Default: 0 - Index of first visible month in grid
180
horizontalMonthPadding?: number; // Default: 13 (non-negative integer) - Horizontal padding for months
181
numberOfMonths?: number; // Default: 1 - Number of months to display
182
orientation?: 'horizontal' | 'vertical' | 'verticalScrollable'; // Default: 'horizontal' - Layout orientation
183
daySize?: number; // Default: 39 (non-negative integer) - Size of each day cell
184
isRTL?: boolean; // Default: false - Right-to-left layout
185
verticalBorderSpacing?: number; // Default: undefined (non-negative integer) - Vertical spacing between day rows
186
187
// Animation and transitions
188
isAnimating?: boolean; // Default: false - Whether grid is currently animating
189
translationValue?: number; // Default: null - Translation offset for animations
190
transitionDuration?: number; // Default: 200 (non-negative integer) - Animation duration in milliseconds
191
onMonthTransitionEnd?: () => void; // Default: () => {} - Called when month transition completes
192
193
// Month state and navigation
194
initialMonth?: moment.Moment; // Default: moment() - Initial month to display
195
onMonthChange?: (newMonth: moment.Moment) => void; // Default: () => {} - Called when month changes
196
onYearChange?: (newMonth: moment.Moment) => void; // Default: () => {} - Called when year changes
197
198
// Day interaction
199
onDayClick?: (day: moment.Moment, event: SyntheticEvent) => void; // Default: () => {} - Day click handler
200
onDayMouseEnter?: (day: moment.Moment, event: SyntheticEvent) => void; // Default: () => {} - Day mouse enter handler
201
onDayMouseLeave?: (day: moment.Moment, event: SyntheticEvent) => void; // Default: () => {} - Day mouse leave handler
202
203
// Day modifiers and styling
204
modifiers?: { [monthString: string]: { [dateString: string]: Set<string> } }; // Default: {} - Object mapping month strings to date modifiers
205
206
// Custom rendering (mutually exclusive)
207
renderMonthText?: (month: moment.Moment) => ReactNode; // Default: null - Custom month header text renderer (mutually exclusive with renderMonthElement)
208
renderMonthElement?: (props: { // Default: null - Custom month header element renderer (mutually exclusive with renderMonthText)
209
month: moment.Moment;
210
onMonthSelect: (currentMonth: moment.Moment, newMonthVal: string | number) => void;
211
onYearSelect: (currentMonth: moment.Moment, newYearVal: string | number) => void;
212
isVisible: boolean;
213
}) => ReactNode;
214
215
// Day rendering customization
216
renderCalendarDay?: (props: CalendarDayProps) => ReactNode; // Default: undefined - Custom day cell renderer
217
renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode; // Default: null - Custom day contents renderer
218
219
// Focus management
220
focusedDate?: moment.Moment; // Default: null - Currently focused date for keyboard navigation
221
isFocused?: boolean; // Default: false - Whether to move focus to focusable day
222
firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | null; // Default: null (uses moment locale) - First day of week (0=Sunday, 6=Saturday)
223
224
// Layout callbacks
225
setMonthTitleHeight?: (height: number) => void; // Default: null - Callback to report month title height
226
227
// Internationalization
228
monthFormat?: string; // Default: 'MMMM YYYY' - Moment.js format for month header
229
dayAriaLabelFormat?: string; // Default: undefined - Moment.js format for day ARIA labels
230
phrases?: { // Default: CalendarDayPhrases - Accessibility and i18n text
231
chooseAvailableDate?: (args: { date: string }) => string;
232
dateIsUnavailable?: (args: { date: string }) => string;
233
dateIsSelected?: (args: { date: string }) => string;
234
dateIsSelectedAsStartDate?: (args: { date: string }) => string;
235
dateIsSelectedAsEndDate?: (args: { date: string }) => string;
236
};
237
}
238
```
239
240
**Public Methods:**
241
242
```jsx { .api }
243
// Navigation and animation methods (accessible via ref)
244
onTransitionEnd(): void;
245
onMonthSelect(currentMonth: moment.Moment, newMonthVal: string | number): void;
246
onYearSelect(currentMonth: moment.Moment, newYearVal: string | number): void;
247
```
248
249
**Usage Examples:**
250
251
```jsx
252
import React, { useState } from "react";
253
import { CalendarMonthGrid } from "react-dates";
254
import moment from "moment";
255
256
// Multi-month calendar display
257
function MultiMonthCalendar() {
258
const [isAnimating, setIsAnimating] = useState(false);
259
const [currentMonth, setCurrentMonth] = useState(moment());
260
261
const handleMonthSelect = (currentMonth, newMonthVal) => {
262
setIsAnimating(true);
263
const newMonth = currentMonth.clone().month(parseInt(newMonthVal));
264
setCurrentMonth(newMonth);
265
};
266
267
const handleTransitionEnd = () => {
268
setIsAnimating(false);
269
};
270
271
return (
272
<CalendarMonthGrid
273
numberOfMonths={3}
274
orientation="horizontal"
275
initialMonth={currentMonth}
276
isAnimating={isAnimating}
277
onMonthSelect={handleMonthSelect}
278
onMonthTransitionEnd={handleTransitionEnd}
279
onDayClick={(day) => console.log('Selected:', day.format('YYYY-MM-DD'))}
280
/>
281
);
282
}
283
284
// Responsive calendar grid
285
function ResponsiveCalendarGrid() {
286
const [numberOfMonths, setNumberOfMonths] = useState(
287
window.innerWidth > 768 ? 2 : 1
288
);
289
const [orientation, setOrientation] = useState(
290
window.innerWidth > 768 ? 'horizontal' : 'vertical'
291
);
292
293
React.useEffect(() => {
294
const handleResize = () => {
295
const isMobile = window.innerWidth <= 768;
296
setNumberOfMonths(isMobile ? 1 : 2);
297
setOrientation(isMobile ? 'vertical' : 'horizontal');
298
};
299
300
window.addEventListener('resize', handleResize);
301
return () => window.removeEventListener('resize', handleResize);
302
}, []);
303
304
return (
305
<CalendarMonthGrid
306
numberOfMonths={numberOfMonths}
307
orientation={orientation}
308
transitionDuration={200}
309
onDayClick={(day) => console.log('Date selected:', day.format('YYYY-MM-DD'))}
310
/>
311
);
312
}
313
```
314
315
### CalendarMonth
316
317
Individual month view component containing the calendar day grid for a single month.
318
319
```jsx { .api }
320
/**
321
* Individual month view with calendar day grid
322
* @param props - CalendarMonth configuration
323
* @returns Single month calendar component
324
*/
325
function CalendarMonth(props: CalendarMonthProps): ReactElement;
326
327
interface CalendarMonthProps {
328
// Core month data
329
month?: moment.Moment; // Default: moment() - The month to display
330
331
// Layout and presentation
332
horizontalMonthPadding?: number; // Default: 13 (non-negative integer) - Horizontal padding for month container
333
daySize?: number; // Default: 39 (non-negative integer) - Size of each day cell
334
isVisible?: boolean; // Default: true - Whether month is visible
335
orientation?: 'horizontal' | 'vertical' | 'verticalScrollable'; // Default: 'horizontal' - Layout orientation
336
verticalBorderSpacing?: number; // Default: undefined (non-negative integer) - Vertical spacing between day rows
337
338
// Day behavior and outside days
339
enableOutsideDays?: boolean; // Default: false - Show days from adjacent months
340
firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | null; // Default: null (uses moment locale) - First day of week (0=Sunday, 6=Saturday)
341
342
// Day interaction handlers
343
onDayClick?: (day: moment.Moment, event: SyntheticEvent) => void; // Default: () => {} - Day click handler
344
onDayMouseEnter?: (day: moment.Moment, event: SyntheticEvent) => void; // Default: () => {} - Day mouse enter handler
345
onDayMouseLeave?: (day: moment.Moment, event: SyntheticEvent) => void; // Default: () => {} - Day mouse leave handler
346
347
// Month/Year selection handlers
348
onMonthSelect?: (currentMonth: moment.Moment, newMonthVal: string | number) => void; // Default: () => {} - Month selection handler
349
onYearSelect?: (currentMonth: moment.Moment, newYearVal: string | number) => void; // Default: () => {} - Year selection handler
350
351
// Rendering customization (mutually exclusive)
352
renderMonthText?: (month: moment.Moment) => ReactNode; // Default: null - Custom month header text renderer (mutually exclusive with renderMonthElement)
353
renderMonthElement?: (props: { // Default: null - Custom month header element renderer (mutually exclusive with renderMonthText)
354
month: moment.Moment;
355
onMonthSelect: function;
356
onYearSelect: function;
357
isVisible: boolean;
358
}) => ReactNode;
359
360
// Day rendering customization
361
renderCalendarDay?: (props: CalendarDayProps) => ReactNode; // Default: (props) => <CalendarDay {...props} /> - Custom day cell renderer
362
renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode; // Default: null - Custom day contents renderer
363
364
// Day modifiers and styling
365
modifiers?: { [dateString: string]: Set<string> }; // Default: {} - Object mapping ISO date strings to Sets of modifier strings
366
367
// Focus management
368
focusedDate?: moment.Moment; // Default: null - Currently focused date for keyboard navigation
369
isFocused?: boolean; // Default: false - Whether to move focus to focusable day
370
371
// Layout callback
372
setMonthTitleHeight?: (height: number) => void; // Default: null - Callback to report month title height
373
374
// Internationalization
375
monthFormat?: string; // Default: 'MMMM YYYY' - Moment.js format for month header
376
dayAriaLabelFormat?: string; // Default: undefined - Moment.js format for day ARIA labels
377
phrases?: { // Default: CalendarDayPhrases - Accessibility and i18n text
378
chooseAvailableDate?: ({ date }: { date: string }) => string;
379
dateIsUnavailable?: ({ date }: { date: string }) => string;
380
dateIsSelected?: ({ date }: { date: string }) => string;
381
dateIsSelectedAsStartDate?: ({ date }: { date: string }) => string;
382
dateIsSelectedAsEndDate?: ({ date }: { date: string }) => string;
383
};
384
}
385
```
386
387
**Public Methods:**
388
389
```jsx { .api }
390
// Layout and reference methods (accessible via ref)
391
setMonthTitleHeight(): void;
392
setCaptionRef(ref: HTMLElement): void;
393
```
394
395
**Usage Examples:**
396
397
```jsx
398
import React, { useState, useRef } from "react";
399
import { CalendarMonth } from "react-dates";
400
import moment from "moment";
401
402
// Single month display
403
function MonthView({ selectedMonth }) {
404
const monthRef = useRef(null);
405
406
const handleDayClick = (day) => {
407
console.log('Day selected:', day.format('YYYY-MM-DD'));
408
};
409
410
const renderMonthHeader = (month) => {
411
return (
412
<div className="custom-month-header">
413
<h2>{month.format('MMMM YYYY')}</h2>
414
<p>Select a date below</p>
415
</div>
416
);
417
};
418
419
return (
420
<CalendarMonth
421
ref={monthRef}
422
month={selectedMonth}
423
onDayClick={handleDayClick}
424
renderMonthText={renderMonthHeader}
425
enableOutsideDays={true}
426
/>
427
);
428
}
429
430
// Custom styled month
431
function StyledMonthView({ month, holidays, events }) {
432
const getModifiers = (day) => {
433
const modifiers = new Set();
434
const dayKey = day.format('YYYY-MM-DD');
435
436
if (holidays.includes(dayKey)) {
437
modifiers.add('holiday');
438
}
439
if (events[dayKey]) {
440
modifiers.add('has-events');
441
}
442
if (day.day() === 0 || day.day() === 6) {
443
modifiers.add('weekend');
444
}
445
446
return modifiers;
447
};
448
449
const renderDayContents = (day, modifiers) => {
450
const hasEvents = modifiers.has('has-events');
451
const isHoliday = modifiers.has('holiday');
452
453
return (
454
<div className={`day-cell ${isHoliday ? 'holiday' : ''}`}>
455
<span>{day.format('D')}</span>
456
{hasEvents && <div className="event-indicator" />}
457
</div>
458
);
459
};
460
461
return (
462
<CalendarMonth
463
month={month}
464
renderDayContents={renderDayContents}
465
modifiers={{ getModifiers }}
466
onDayClick={(day) => {
467
const dayEvents = events[day.format('YYYY-MM-DD')];
468
if (dayEvents) {
469
console.log('Events for this day:', dayEvents);
470
}
471
}}
472
/>
473
);
474
}
475
```
476
477
### CalendarDay
478
479
Individual day cell component in the calendar grid, handling day selection and customization.
480
481
```jsx { .api }
482
/**
483
* Individual day cell in calendar grid
484
* @param props - CalendarDay configuration
485
* @returns Single day cell component
486
*/
487
function CalendarDay(props: CalendarDayProps): ReactElement;
488
489
interface CalendarDayProps {
490
// Day data and presentation
491
day: moment.Moment; // Required: momentPropTypes.momentObj - The moment object representing the day
492
daySize?: number; // Default: 39 (DAY_SIZE) - nonNegativeInteger - Size of the day cell in pixels
493
isOutsideDay?: boolean; // Default: false - Whether this day belongs to an adjacent month
494
495
// Modifiers and styling
496
modifiers?: Set<string>; // Default: new Set() - ModifiersShape (Set of strings) - Visual state modifiers
497
498
// Focus and interaction state
499
isFocused?: boolean; // Default: false - Whether the day should receive focus
500
tabIndex?: 0 | -1; // Default: -1 - Tab navigation index (0 = focusable, -1 = not focusable)
501
502
// Event handlers
503
onDayClick?: (day: moment.Moment, event: SyntheticEvent) => void; // Default: () => {} - Day click handler
504
onDayMouseEnter?: (day: moment.Moment, event: SyntheticEvent) => void; // Default: () => {} - Mouse enter handler
505
onDayMouseLeave?: (day: moment.Moment, event: SyntheticEvent) => void; // Default: () => {} - Mouse leave handler
506
507
// Custom rendering
508
renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode | null; // Default: null - Custom day contents renderer
509
510
// Accessibility
511
ariaLabelFormat?: string; // Default: 'dddd, LL' - Moment.js format string for ARIA labels
512
513
// Internationalization
514
phrases?: { // Default: CalendarDayPhrases - Accessibility and i18n text
515
chooseAvailableDate?: ({ date }: { date: string }) => string;
516
dateIsUnavailable?: ({ date }: { date: string }) => string;
517
dateIsSelected?: ({ date }: { date: string }) => string;
518
dateIsSelectedAsStartDate?: ({ date }: { date: string }) => string;
519
dateIsSelectedAsEndDate?: ({ date }: { date: string }) => string;
520
};
521
}
522
```
523
524
**Public Methods:**
525
526
```jsx { .api }
527
// Event handling methods (accessible via ref)
528
onDayClick(event: SyntheticEvent): void;
529
onDayMouseEnter(event: SyntheticEvent): void;
530
onDayMouseLeave(event: SyntheticEvent): void;
531
onKeyDown(event: KeyboardEvent): void;
532
```
533
534
**Usage Examples:**
535
536
```jsx
537
import React from "react";
538
import { CalendarDay } from "react-dates";
539
import moment from "moment";
540
541
// Custom day rendering
542
function CustomCalendarDay({ day, isSelected, isBlocked, events }) {
543
const modifiers = new Set();
544
if (isSelected) modifiers.add('selected');
545
if (isBlocked) modifiers.add('blocked');
546
if (events?.length > 0) modifiers.add('has-events');
547
548
const renderDayContents = (day, modifiers) => {
549
const dayNumber = day.format('D');
550
const hasEvents = modifiers.has('has-events');
551
552
return (
553
<div className="custom-day-contents">
554
<span className="day-number">{dayNumber}</span>
555
{hasEvents && (
556
<div className="event-indicators">
557
{events.slice(0, 2).map((event, index) => (
558
<div
559
key={index}
560
className="event-dot"
561
style={{ backgroundColor: event.color }}
562
title={event.title}
563
/>
564
))}
565
{events.length > 2 && (
566
<span className="more-events">+{events.length - 2}</span>
567
)}
568
</div>
569
)}
570
</div>
571
);
572
};
573
574
const handleDayClick = (day, event) => {
575
if (!isBlocked) {
576
console.log('Day clicked:', day.format('YYYY-MM-DD'));
577
if (events?.length > 0) {
578
console.log('Day events:', events);
579
}
580
}
581
};
582
583
return (
584
<CalendarDay
585
day={day}
586
modifiers={modifiers}
587
onDayClick={handleDayClick}
588
renderDayContents={renderDayContents}
589
onDayMouseEnter={(day) => {
590
if (events?.length > 0) {
591
// Show event preview on hover
592
console.log('Hovering day with events:', events);
593
}
594
}}
595
/>
596
);
597
}
598
599
// Minimal day cell
600
function MinimalCalendarDay({ day, onClick }) {
601
const handleClick = (day, event) => {
602
onClick?.(day, event);
603
};
604
605
return (
606
<CalendarDay
607
day={day}
608
onDayClick={handleClick}
609
renderDayContents={(day) => (
610
<span className="simple-day">{day.format('D')}</span>
611
)}
612
/>
613
);
614
}
615
```
616
617
## Common Patterns
618
619
### Custom Day Modifiers
620
621
```jsx
622
// Define custom modifiers for day states
623
const getCustomModifiers = (day, selectedDates, blockedDates, highlightedDates) => {
624
const modifiers = new Set();
625
const dayKey = day.format('YYYY-MM-DD');
626
627
// Standard modifiers
628
if (selectedDates.includes(dayKey)) modifiers.add('selected');
629
if (blockedDates.includes(dayKey)) modifiers.add('blocked');
630
if (highlightedDates.includes(dayKey)) modifiers.add('highlighted');
631
632
// Custom business logic modifiers
633
if (day.day() === 0 || day.day() === 6) modifiers.add('weekend');
634
if (day.isSame(moment(), 'day')) modifiers.add('today');
635
if (day.isBefore(moment(), 'day')) modifiers.add('past');
636
637
return modifiers;
638
};
639
640
// Usage in calendar components
641
<CalendarMonth
642
month={currentMonth}
643
modifiers={{ getCustomModifiers }}
644
renderDayContents={(day, modifiers) => {
645
let className = 'calendar-day';
646
if (modifiers.has('weekend')) className += ' weekend';
647
if (modifiers.has('today')) className += ' today';
648
if (modifiers.has('blocked')) className += ' blocked';
649
650
return <div className={className}>{day.format('D')}</div>;
651
}}
652
/>
653
```
654
655
### Dynamic Month Navigation
656
657
```jsx
658
// Advanced month navigation with constraints
659
function ConstrainedCalendar({ minDate, maxDate }) {
660
const [currentMonth, setCurrentMonth] = useState(moment());
661
662
const handlePrevMonth = (newMonth) => {
663
if (!minDate || newMonth.isAfter(minDate, 'month')) {
664
setCurrentMonth(newMonth);
665
}
666
};
667
668
const handleNextMonth = (newMonth) => {
669
if (!maxDate || newMonth.isBefore(maxDate, 'month')) {
670
setCurrentMonth(newMonth);
671
}
672
};
673
674
return (
675
<DayPicker
676
month={currentMonth}
677
onPrevMonthClick={handlePrevMonth}
678
onNextMonthClick={handleNextMonth}
679
renderNavPrevButton={(props) => (
680
<button
681
{...props}
682
disabled={minDate && currentMonth.clone().subtract(1, 'month').isBefore(minDate, 'month')}
683
>
684
← Previous
685
</button>
686
)}
687
renderNavNextButton={(props) => (
688
<button
689
{...props}
690
disabled={maxDate && currentMonth.clone().add(1, 'month').isAfter(maxDate, 'month')}
691
>
692
Next →
693
</button>
694
)}
695
/>
696
);
697
}
698
```
699
700
### Accessibility Integration
701
702
All calendar components include comprehensive accessibility features:
703
704
- **ARIA Labels**: Automatic ARIA labeling for screen readers
705
- **Keyboard Navigation**: Arrow keys, Tab, Enter, Escape support
706
- **Focus Management**: Logical focus order and visual indicators
707
- **Screen Reader Announcements**: Date selection and navigation feedback
708
- **Customizable Text**: Override all accessibility text through `phrases` prop