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-controllers.mddocs/

0

# Calendar Controllers

1

2

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.

3

4

## Capabilities

5

6

### DayPickerRangeController

7

8

Calendar-only component for date range selection without input fields. Perfect for building custom date range interfaces or embedded calendar displays.

9

10

```jsx { .api }

11

/**

12

* Headless date range calendar controller without input fields

13

* @param props - DayPickerRangeController configuration

14

* @returns Calendar component for date range selection

15

*/

16

function DayPickerRangeController(props: DayPickerRangeControllerProps): ReactElement;

17

18

interface DayPickerRangeControllerProps {

19

// Date state

20

startDate?: moment.Moment | null;

21

endDate?: moment.Moment | null;

22

focusedInput?: 'startDate' | 'endDate' | null;

23

24

// Required callbacks

25

onDatesChange: ({ startDate, endDate }: {

26

startDate: moment.Moment | null;

27

endDate: moment.Moment | null;

28

}) => void;

29

onFocusChange: (focusedInput: 'startDate' | 'endDate' | null) => void;

30

31

// Calendar presentation

32

orientation?: 'horizontal' | 'vertical';

33

numberOfMonths?: number;

34

enableOutsideDays?: boolean;

35

daySize?: number;

36

isRTL?: boolean;

37

firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6;

38

verticalHeight?: number;

39

transitionDuration?: number;

40

verticalSpacing?: number;

41

horizontalMonthPadding?: number;

42

43

// Navigation

44

navPosition?: 'navPositionTop' | 'navPositionBottom';

45

navPrev?: ReactNode;

46

navNext?: ReactNode;

47

renderNavPrevButton?: (props: any) => ReactNode;

48

renderNavNextButton?: (props: any) => ReactNode;

49

onPrevMonthClick?: (newMonth: moment.Moment) => void;

50

onNextMonthClick?: (newMonth: moment.Moment) => void;

51

52

// Day customization

53

renderCalendarDay?: (props: any) => ReactNode;

54

renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode;

55

renderMonthElement?: (props: any) => ReactNode;

56

renderMonthText?: (month: moment.Moment) => ReactNode;

57

renderWeekHeaderElement?: (day: string) => ReactNode;

58

minimumNights?: number;

59

isDayBlocked?: (day: moment.Moment) => boolean;

60

isOutsideRange?: (day: moment.Moment) => boolean;

61

isDayHighlighted?: (day: moment.Moment) => boolean;

62

63

// Interaction

64

keepOpenOnDateSelect?: boolean;

65

onDayClick?: (day: moment.Moment, event: SyntheticEvent) => void;

66

onDayMouseEnter?: (day: moment.Moment, event: SyntheticEvent) => void;

67

onDayMouseLeave?: (day: moment.Moment, event: SyntheticEvent) => void;

68

69

// Internationalization

70

monthFormat?: string;

71

weekDayFormat?: string;

72

phrases?: object;

73

dayAriaLabelFormat?: string;

74

75

// Initial state

76

initialVisibleMonth?: () => moment.Moment;

77

78

// Focus management

79

isFocused?: boolean;

80

showKeyboardShortcuts?: boolean;

81

onBlur?: () => void;

82

onTab?: (event: SyntheticEvent) => void;

83

onShiftTab?: (event: SyntheticEvent) => void;

84

}

85

```

86

87

**Usage Examples:**

88

89

```jsx

90

import React, { useState } from "react";

91

import { DayPickerRangeController } from "react-dates";

92

import moment from "moment";

93

94

// Basic embedded calendar

95

function EmbeddedRangeCalendar() {

96

const [startDate, setStartDate] = useState(null);

97

const [endDate, setEndDate] = useState(null);

98

const [focusedInput, setFocusedInput] = useState('startDate');

99

100

return (

101

<div style={{ width: '100%', maxWidth: 600 }}>

102

<DayPickerRangeController

103

startDate={startDate}

104

endDate={endDate}

105

onDatesChange={({ startDate, endDate }) => {

106

setStartDate(startDate);

107

setEndDate(endDate);

108

}}

109

focusedInput={focusedInput}

110

onFocusChange={setFocusedInput}

111

numberOfMonths={2}

112

minimumNights={1}

113

/>

114

</div>

115

);

116

}

117

118

// Custom calendar with business logic

119

function BookingCalendar({ unavailableDates, rates }) {

120

const [startDate, setStartDate] = useState(null);

121

const [endDate, setEndDate] = useState(null);

122

const [focusedInput, setFocusedInput] = useState('startDate');

123

124

const isDayBlocked = (day) => {

125

return unavailableDates.some(blockedDay =>

126

moment(blockedDay).isSame(day, 'day')

127

);

128

};

129

130

const renderDayContents = (day, modifiers) => {

131

const rate = rates[day.format('YYYY-MM-DD')];

132

return (

133

<div>

134

<div>{day.format('D')}</div>

135

{rate && <div style={{ fontSize: '10px' }}>${rate}</div>}

136

</div>

137

);

138

};

139

140

return (

141

<div>

142

<DayPickerRangeController

143

startDate={startDate}

144

endDate={endDate}

145

onDatesChange={({ startDate, endDate }) => {

146

setStartDate(startDate);

147

setEndDate(endDate);

148

149

// Custom business logic

150

if (startDate && endDate) {

151

console.log('Total nights:', endDate.diff(startDate, 'days'));

152

}

153

}}

154

focusedInput={focusedInput}

155

onFocusChange={setFocusedInput}

156

isDayBlocked={isDayBlocked}

157

renderDayContents={renderDayContents}

158

minimumNights={2}

159

numberOfMonths={3}

160

/>

161

</div>

162

);

163

}

164

```

165

166

### DayPickerSingleDateController

167

168

Calendar-only component for single date selection without input fields. Ideal for embedded date pickers and custom interfaces.

169

170

```jsx { .api }

171

/**

172

* Headless single date calendar controller without input fields

173

* @param props - DayPickerSingleDateController configuration

174

* @returns Calendar component for single date selection

175

*/

176

function DayPickerSingleDateController(props: DayPickerSingleDateControllerProps): ReactElement;

177

178

interface DayPickerSingleDateControllerProps {

179

// Date state

180

date?: moment.Moment | null;

181

focused?: boolean;

182

183

// Required callbacks

184

onDateChange: (date: moment.Moment | null) => void;

185

onFocusChange: ({ focused }: { focused: boolean }) => void;

186

187

// Calendar presentation

188

orientation?: 'horizontal' | 'vertical';

189

numberOfMonths?: number;

190

enableOutsideDays?: boolean;

191

daySize?: number;

192

isRTL?: boolean;

193

firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6;

194

verticalHeight?: number;

195

transitionDuration?: number;

196

verticalSpacing?: number;

197

horizontalMonthPadding?: number;

198

199

// Navigation

200

navPosition?: 'navPositionTop' | 'navPositionBottom';

201

navPrev?: ReactNode;

202

navNext?: ReactNode;

203

renderNavPrevButton?: (props: any) => ReactNode;

204

renderNavNextButton?: (props: any) => ReactNode;

205

onPrevMonthClick?: (newMonth: moment.Moment) => void;

206

onNextMonthClick?: (newMonth: moment.Moment) => void;

207

208

// Day customization

209

renderCalendarDay?: (props: any) => ReactNode;

210

renderDayContents?: (day: moment.Moment, modifiers: Set<string>) => ReactNode;

211

renderMonthElement?: (props: any) => ReactNode;

212

renderMonthText?: (month: moment.Moment) => ReactNode;

213

renderWeekHeaderElement?: (day: string) => ReactNode;

214

isDayBlocked?: (day: moment.Moment) => boolean;

215

isOutsideRange?: (day: moment.Moment) => boolean;

216

isDayHighlighted?: (day: moment.Moment) => boolean;

217

218

// Interaction

219

keepOpenOnDateSelect?: boolean;

220

onDayClick?: (day: moment.Moment, event: SyntheticEvent) => void;

221

onDayMouseEnter?: (day: moment.Moment, event: SyntheticEvent) => void;

222

onDayMouseLeave?: (day: moment.Moment, event: SyntheticEvent) => void;

223

224

// Internationalization

225

monthFormat?: string;

226

weekDayFormat?: string;

227

phrases?: object;

228

dayAriaLabelFormat?: string;

229

230

// Initial state

231

initialVisibleMonth?: () => moment.Moment;

232

233

// Focus management

234

isFocused?: boolean;

235

showKeyboardShortcuts?: boolean;

236

onBlur?: () => void;

237

onTab?: (event: SyntheticEvent) => void;

238

onShiftTab?: (event: SyntheticEvent) => void;

239

}

240

```

241

242

**Usage Examples:**

243

244

```jsx

245

import React, { useState } from "react";

246

import { DayPickerSingleDateController } from "react-dates";

247

import moment from "moment";

248

249

// Simple embedded calendar

250

function EmbeddedSingleCalendar() {

251

const [date, setDate] = useState(null);

252

const [focused, setFocused] = useState(true);

253

254

return (

255

<div style={{ width: 300 }}>

256

<DayPickerSingleDateController

257

date={date}

258

onDateChange={setDate}

259

focused={focused}

260

onFocusChange={({ focused }) => setFocused(focused)}

261

numberOfMonths={1}

262

/>

263

</div>

264

);

265

}

266

267

// Appointment scheduler calendar

268

function AppointmentCalendar({ availableSlots, onDateSelect }) {

269

const [selectedDate, setSelectedDate] = useState(null);

270

const [focused, setFocused] = useState(true);

271

272

const isDayBlocked = (day) => {

273

// Block days without available slots

274

const dayKey = day.format('YYYY-MM-DD');

275

return !availableSlots[dayKey] || availableSlots[dayKey].length === 0;

276

};

277

278

const renderDayContents = (day, modifiers) => {

279

const dayKey = day.format('YYYY-MM-DD');

280

const slots = availableSlots[dayKey];

281

const availableCount = slots ? slots.length : 0;

282

283

return (

284

<div style={{ textAlign: 'center' }}>

285

<div>{day.format('D')}</div>

286

{availableCount > 0 && (

287

<div style={{ fontSize: '10px', color: 'green' }}>

288

{availableCount} slots

289

</div>

290

)}

291

</div>

292

);

293

};

294

295

const handleDateChange = (date) => {

296

setSelectedDate(date);

297

onDateSelect(date);

298

};

299

300

return (

301

<div>

302

<h3>Select Appointment Date</h3>

303

<DayPickerSingleDateController

304

date={selectedDate}

305

onDateChange={handleDateChange}

306

focused={focused}

307

onFocusChange={({ focused }) => setFocused(focused)}

308

isDayBlocked={isDayBlocked}

309

renderDayContents={renderDayContents}

310

isOutsideRange={(day) => moment().diff(day) > 0} // No past dates

311

numberOfMonths={2}

312

/>

313

</div>

314

);

315

}

316

```

317

318

## Public Methods

319

320

Both controller components expose these public methods via refs:

321

322

```jsx { .api }

323

// Navigation methods

324

onDayClick(day: moment.Moment, event: SyntheticEvent): void;

325

onDayMouseEnter(day: moment.Moment, event?: SyntheticEvent): void;

326

onDayMouseLeave(day: moment.Moment, event?: SyntheticEvent): void;

327

onPrevMonthClick(newMonth?: moment.Moment): void;

328

onNextMonthClick(newMonth?: moment.Moment): void;

329

330

// Focus management

331

getFirstFocusableDay(newMonth: moment.Moment): moment.Moment;

332

```

333

334

## Common Patterns

335

336

### Custom Date Validation

337

338

```jsx

339

// Combined validation function

340

const isDateUnavailable = (day) => {

341

const isPastDate = moment().diff(day) > 0;

342

const isWeekend = day.day() === 0 || day.day() === 6;

343

const isHoliday = holidays.includes(day.format('YYYY-MM-DD'));

344

345

return isPastDate || isWeekend || isHoliday;

346

};

347

348

// Usage

349

<DayPickerSingleDateController

350

isDayBlocked={isDateUnavailable}

351

// ... other props

352

/>

353

```

354

355

### Custom Day Rendering

356

357

```jsx

358

// Complex day rendering with multiple data types

359

const renderDayContents = (day, modifiers) => {

360

const hasEvents = events[day.format('YYYY-MM-DD')];

361

const isHighlighted = modifiers.has('highlighted');

362

363

return (

364

<div className={`custom-day ${isHighlighted ? 'highlighted' : ''}`}>

365

<span className="day-number">{day.format('D')}</span>

366

{hasEvents && <div className="event-indicator" />}

367

</div>

368

);

369

};

370

```

371

372

### Month Navigation Handling

373

374

```jsx

375

// Custom month navigation with logging

376

const handlePrevMonth = (newMonth) => {

377

console.log('Navigated to:', newMonth.format('MMMM YYYY'));

378

// Custom logic here

379

};

380

381

const handleNextMonth = (newMonth) => {

382

console.log('Navigated to:', newMonth.format('MMMM YYYY'));

383

// Custom logic here

384

};

385

```