or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

calendar-components.mdcalendar-controllers.mdindex.mdinput-components.mdmain-pickers.mdutilities.md

calendar-components.mddocs/

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