0
# @react-stately/datepicker
1
2
@react-stately/datepicker is a React state management library that provides hooks for date picker components as part of Adobe's React Spectrum ecosystem. It offers cross-platform state management functionality through hooks that handle date selection, validation, formatting, and overlay state management with full internationalization support.
3
4
## Package Information
5
6
- **Package Name**: @react-stately/datepicker
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install @react-stately/datepicker`
10
11
## Core Imports
12
13
```typescript
14
import {
15
useDatePickerState,
16
useDateFieldState,
17
useDateRangePickerState,
18
useTimeFieldState,
19
type DatePickerState,
20
type DatePickerStateOptions,
21
type DateFieldState,
22
type DateFieldStateOptions,
23
type DateRangePickerState,
24
type DateRangePickerStateOptions,
25
type TimeFieldState,
26
type TimeFieldStateOptions,
27
type DateSegment,
28
type SegmentType,
29
type FormatterOptions
30
} from "@react-stately/datepicker";
31
```
32
33
For CommonJS:
34
35
```javascript
36
const {
37
useDatePickerState,
38
useDateFieldState,
39
useDateRangePickerState,
40
useTimeFieldState
41
} = require("@react-stately/datepicker");
42
```
43
44
## Basic Usage
45
46
```typescript
47
import { useDatePickerState, useDateFieldState } from "@react-stately/datepicker";
48
import { CalendarDate, createCalendar } from "@internationalized/date";
49
50
function MyDatePicker() {
51
const state = useDatePickerState({
52
defaultValue: new CalendarDate(2023, 6, 15),
53
onChange: (value) => console.log("Selected:", value?.toString()),
54
granularity: 'day'
55
});
56
57
return (
58
<div>
59
<button onClick={() => state.setOpen(!state.isOpen)}>
60
{state.value?.toString() || "Select date"}
61
</button>
62
{state.isOpen && (
63
<div>Calendar would be rendered here</div>
64
)}
65
</div>
66
);
67
}
68
69
function MyDateField() {
70
const state = useDateFieldState({
71
locale: 'en-US',
72
createCalendar,
73
defaultValue: new CalendarDate(2023, 6, 15),
74
onChange: (value) => console.log("Field value:", value?.toString())
75
});
76
77
return (
78
<div>
79
{state.segments.map((segment, i) => (
80
<span key={i} aria-label={segment.type}>
81
{segment.text}
82
</span>
83
))}
84
</div>
85
);
86
}
87
```
88
89
## Architecture
90
91
@react-stately/datepicker is built around several key components:
92
93
- **State Management Hooks**: Four specialized hooks for different date/time input scenarios
94
- **Internationalization Support**: Built on @internationalized/date for cross-calendar system support
95
- **Form Integration**: Extends @react-stately/form for validation and error handling
96
- **Overlay Management**: Integrates with @react-stately/overlays for popover state
97
- **Segment-based Editing**: Individual editable segments for precise date/time input
98
- **Type Safety**: Full TypeScript support with generic date value types
99
100
## Capabilities
101
102
### Date Picker State
103
104
Core state management for date picker components that combine a text field with a calendar popover. Handles date selection, time selection, validation, and overlay state.
105
106
```typescript { .api }
107
function useDatePickerState<T extends DateValue = DateValue>(
108
props: DatePickerStateOptions<T>
109
): DatePickerState;
110
111
interface DatePickerStateOptions<T extends DateValue> extends DatePickerProps<T> {
112
/**
113
* Determines whether the date picker popover should close automatically when a date is selected.
114
* @default true
115
*/
116
shouldCloseOnSelect?: boolean | (() => boolean);
117
}
118
119
interface DatePickerState extends OverlayTriggerState, FormValidationState {
120
/** The currently selected date. */
121
value: DateValue | null;
122
/** The default date. */
123
defaultValue: DateValue | null;
124
/** Sets the selected date. */
125
setValue(value: DateValue | null): void;
126
/**
127
* The date portion of the value. This may be set prior to `value` if the user has
128
* selected a date but has not yet selected a time.
129
*/
130
dateValue: DateValue | null;
131
/** Sets the date portion of the value. */
132
setDateValue(value: DateValue): void;
133
/**
134
* The time portion of the value. This may be set prior to `value` if the user has
135
* selected a time but has not yet selected a date.
136
*/
137
timeValue: TimeValue | null;
138
/** Sets the time portion of the value. */
139
setTimeValue(value: TimeValue): void;
140
/** The granularity for the field, based on the `granularity` prop and current value. */
141
granularity: Granularity;
142
/** Whether the date picker supports selecting a time, according to the `granularity` prop and current value. */
143
hasTime: boolean;
144
/** Whether the calendar popover is currently open. */
145
isOpen: boolean;
146
/** Sets whether the calendar popover is open. */
147
setOpen(isOpen: boolean): void;
148
/**
149
* The current validation state of the date picker, based on the `validationState`, `minValue`, and `maxValue` props.
150
* @deprecated Use `isInvalid` instead.
151
*/
152
validationState: ValidationState | null;
153
/** Whether the date picker is invalid, based on the `isInvalid`, `minValue`, and `maxValue` props. */
154
isInvalid: boolean;
155
/** Formats the selected value using the given options. */
156
formatValue(locale: string, fieldOptions: FieldOptions): string;
157
/** Gets a formatter based on state's props. */
158
getDateFormatter(locale: string, formatOptions: FormatterOptions): DateFormatter;
159
}
160
```
161
162
[Date Picker State](./date-picker-state.md)
163
164
### Date Field State
165
166
State management for date field components with individually editable segments. Each part of a date value is displayed in a separate editable segment (year, month, day, etc.).
167
168
```typescript { .api }
169
function useDateFieldState<T extends DateValue = DateValue>(
170
props: DateFieldStateOptions<T>
171
): DateFieldState;
172
173
interface DateFieldStateOptions<T extends DateValue = DateValue> extends DatePickerProps<T> {
174
/**
175
* The maximum unit to display in the date field.
176
* @default 'year'
177
*/
178
maxGranularity?: 'year' | 'month' | Granularity;
179
/** The locale to display and edit the value according to. */
180
locale: string;
181
/**
182
* A function that creates a Calendar object for a given calendar identifier.
183
* Such a function may be imported from the @internationalized/date package.
184
*/
185
createCalendar: (name: CalendarIdentifier) => Calendar;
186
}
187
188
interface DateFieldState extends FormValidationState {
189
/** The current field value. */
190
value: DateValue | null;
191
/** The default field value. */
192
defaultValue: DateValue | null;
193
/** The current value, converted to a native JavaScript Date object. */
194
dateValue: Date;
195
/** The calendar system currently in use. */
196
calendar: Calendar;
197
/** Sets the field's value. */
198
setValue(value: DateValue | null): void;
199
/** A list of segments for the current value. */
200
segments: DateSegment[];
201
/** A date formatter configured for the current locale and format. */
202
dateFormatter: DateFormatter;
203
/**
204
* The current validation state of the date field, based on the `validationState`, `minValue`, and `maxValue` props.
205
* @deprecated Use `isInvalid` instead.
206
*/
207
validationState: ValidationState | null;
208
/** Whether the date field is invalid, based on the `isInvalid`, `minValue`, and `maxValue` props. */
209
isInvalid: boolean;
210
/** The granularity for the field, based on the `granularity` prop and current value. */
211
granularity: Granularity;
212
/** The maximum date or time unit that is displayed in the field. */
213
maxGranularity: 'year' | 'month' | Granularity;
214
/** Whether the field is disabled. */
215
isDisabled: boolean;
216
/** Whether the field is read only. */
217
isReadOnly: boolean;
218
/** Whether the field is required. */
219
isRequired: boolean;
220
/** Increments the given segment. Upon reaching the minimum or maximum value, the value wraps around to the opposite limit. */
221
increment(type: SegmentType): void;
222
/** Decrements the given segment. Upon reaching the minimum or maximum value, the value wraps around to the opposite limit. */
223
decrement(type: SegmentType): void;
224
/**
225
* Increments the given segment by a larger amount, rounding it to the nearest increment.
226
* The amount to increment by depends on the field, for example 15 minutes, 7 days, and 5 years.
227
* Upon reaching the minimum or maximum value, the value wraps around to the opposite limit.
228
*/
229
incrementPage(type: SegmentType): void;
230
/**
231
* Decrements the given segment by a larger amount, rounding it to the nearest increment.
232
* The amount to decrement by depends on the field, for example 15 minutes, 7 days, and 5 years.
233
* Upon reaching the minimum or maximum value, the value wraps around to the opposite limit.
234
*/
235
decrementPage(type: SegmentType): void;
236
/** Sets the value of the given segment. */
237
setSegment(type: 'era', value: string): void;
238
setSegment(type: SegmentType, value: number): void;
239
/** Updates the remaining unfilled segments with the placeholder value. */
240
confirmPlaceholder(): void;
241
/** Clears the value of the given segment, reverting it to the placeholder. */
242
clearSegment(type: SegmentType): void;
243
/** Formats the current date value using the given options. */
244
formatValue(fieldOptions: FieldOptions): string;
245
/** Gets a formatter based on state's props. */
246
getDateFormatter(locale: string, formatOptions: FormatterOptions): DateFormatter;
247
}
248
```
249
250
[Date Field State](./date-field-state.md)
251
252
### Date Range Picker State
253
254
State management for date range picker components that allow users to select a start and end date. Combines two date fields with a range calendar popover.
255
256
```typescript { .api }
257
function useDateRangePickerState<T extends DateValue = DateValue>(
258
props: DateRangePickerStateOptions<T>
259
): DateRangePickerState;
260
261
interface DateRangePickerStateOptions<T extends DateValue = DateValue> extends DateRangePickerProps<T> {
262
/**
263
* Determines whether the date range picker popover should close automatically when a date range is selected.
264
* @default true
265
*/
266
shouldCloseOnSelect?: boolean | (() => boolean);
267
}
268
269
interface DateRangePickerState extends OverlayTriggerState, FormValidationState {
270
/** The currently selected date range. */
271
value: RangeValue<DateValue | null>;
272
/** The default date range. */
273
defaultValue: DateRange | null;
274
/** Sets the selected date range. */
275
setValue(value: DateRange | null): void;
276
/** The date range portion of the value. This may be set prior to `value` if the user has selected dates but not times. */
277
dateRange: RangeValue<DateValue | null> | null;
278
/** Sets the date range portion of the value. */
279
setDateRange(value: DateRange): void;
280
/** The time range portion of the value. This may be set prior to `value` if the user has selected times but not dates. */
281
timeRange: RangeValue<TimeValue | null> | null;
282
/** Sets the time range portion of the value. */
283
setTimeRange(value: TimeRange): void;
284
/** Sets the date for either the start or end of the range. */
285
setDate(part: 'start' | 'end', value: DateValue | null): void;
286
/** Sets the time for either the start or end of the range. */
287
setTime(part: 'start' | 'end', value: TimeValue | null): void;
288
/** Sets the date and time for either the start or end of the range. */
289
setDateTime(part: 'start' | 'end', value: DateValue | null): void;
290
/** The granularity for the field, based on the `granularity` prop and current value. */
291
granularity: Granularity;
292
/** Whether the date range picker supports selecting a time, according to the `granularity` prop and current value. */
293
hasTime: boolean;
294
/** Whether the calendar popover is currently open. */
295
isOpen: boolean;
296
/** Sets whether the calendar popover is open. */
297
setOpen(isOpen: boolean): void;
298
/**
299
* The current validation state of the date range picker, based on the `validationState`, `minValue`, and `maxValue` props.
300
* @deprecated Use `isInvalid` instead.
301
*/
302
validationState: ValidationState | null;
303
/** Whether the date range picker is invalid, based on the `isInvalid`, `minValue`, and `maxValue` props. */
304
isInvalid: boolean;
305
/** Formats the selected value using the given options. */
306
formatValue(locale: string, fieldOptions: FieldOptions): {start: string, end: string} | null;
307
/** Gets a formatter based on state's props. */
308
getDateFormatter(locale: string, formatOptions: FormatterOptions): DateFormatter;
309
}
310
```
311
312
[Date Range Picker State](./date-range-picker-state.md)
313
314
### Time Field State
315
316
State management for time field components that allow users to enter and edit time values. Each part of a time value is displayed in individually editable segments.
317
318
```typescript { .api }
319
function useTimeFieldState<T extends TimeValue = TimeValue>(
320
props: TimeFieldStateOptions<T>
321
): TimeFieldState;
322
323
interface TimeFieldStateOptions<T extends TimeValue = TimeValue> extends TimePickerProps<T> {
324
/** The locale to display and edit the value according to. */
325
locale: string;
326
}
327
328
interface TimeFieldState extends DateFieldState {
329
/** The current time value as a Time object. */
330
timeValue: Time;
331
}
332
```
333
334
[Time Field State](./time-field-state.md)
335
336
## Props Interfaces
337
338
```typescript { .api }
339
// Base props interfaces from @react-types/datepicker
340
interface DatePickerProps<T extends DateValue> {
341
/** The current value (controlled). */
342
value?: T | null;
343
/** The default value (uncontrolled). */
344
defaultValue?: T | null;
345
/** Handler that is called when the value changes. */
346
onChange?: (value: MappedDateValue<T> | null) => void;
347
/** The minimum allowed date that a user may select. */
348
minValue?: DateValue;
349
/** The maximum allowed date that a user may select. */
350
maxValue?: DateValue;
351
/** Callback that is called for each date of the calendar. If true, the date is unavailable. */
352
isDateUnavailable?: (date: DateValue) => boolean;
353
/** Whether the calendar is disabled. */
354
isDisabled?: boolean;
355
/** Whether the calendar is read only. */
356
isReadOnly?: boolean;
357
/** A placeholder date that influences the format of the placeholder shown when no value is selected. */
358
placeholderValue?: T;
359
/** Determines the smallest unit that is displayed in the date picker. */
360
granularity?: Granularity;
361
/** Whether to hide the time zone abbreviation. */
362
hideTimeZone?: boolean;
363
/** Whether to display the time in 12 or 24 hour format. */
364
hourCycle?: 12 | 24;
365
/** Whether to always show leading zeros in the month, day, and hour fields. */
366
shouldForceLeadingZeros?: boolean;
367
/** Whether the element should receive focus on render. */
368
autoFocus?: boolean;
369
/** The name of the input element, used when submitting an HTML form. */
370
name?: string;
371
/** Whether user input is required on the input before form submission. */
372
isRequired?: boolean;
373
/** Whether the input value is invalid. */
374
isInvalid?: boolean;
375
/** The current validation state of the date picker. */
376
validationState?: ValidationState;
377
/** Controls the behavior of paging. */
378
pageBehavior?: 'visible' | 'single';
379
/** The day that starts the week. */
380
firstDayOfWeek?: 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat';
381
}
382
383
interface DateRangePickerProps<T extends DateValue> {
384
/** The current value (controlled). */
385
value?: RangeValue<T> | null;
386
/** The default value (uncontrolled). */
387
defaultValue?: RangeValue<T> | null;
388
/** Handler that is called when the value changes. */
389
onChange?: (value: RangeValue<MappedDateValue<T>> | null) => void;
390
/** The minimum allowed date that a user may select. */
391
minValue?: DateValue;
392
/** The maximum allowed date that a user may select. */
393
maxValue?: DateValue;
394
/** Callback that is called for each date of the calendar. If true, the date is unavailable. */
395
isDateUnavailable?: (date: DateValue) => boolean;
396
/** Whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected. */
397
allowsNonContiguousRanges?: boolean;
398
/** Whether the calendar is disabled. */
399
isDisabled?: boolean;
400
/** Whether the calendar is read only. */
401
isReadOnly?: boolean;
402
/** A placeholder date that influences the format of the placeholder shown when no value is selected. */
403
placeholderValue?: T;
404
/** Determines the smallest unit that is displayed in the date picker. */
405
granularity?: Granularity;
406
/** Whether to hide the time zone abbreviation. */
407
hideTimeZone?: boolean;
408
/** Whether to display the time in 12 or 24 hour format. */
409
hourCycle?: 12 | 24;
410
/** Whether to always show leading zeros in the month, day, and hour fields. */
411
shouldForceLeadingZeros?: boolean;
412
/** Whether the element should receive focus on render. */
413
autoFocus?: boolean;
414
/** The name of the start date input element, used when submitting an HTML form. */
415
startName?: string;
416
/** The name of the end date input element, used when submitting an HTML form. */
417
endName?: string;
418
/** Whether user input is required on the input before form submission. */
419
isRequired?: boolean;
420
/** Whether the input value is invalid. */
421
isInvalid?: boolean;
422
/** The current validation state of the date range picker. */
423
validationState?: ValidationState;
424
/** Controls the behavior of paging. */
425
pageBehavior?: 'visible' | 'single';
426
/** The day that starts the week. */
427
firstDayOfWeek?: 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat';
428
}
429
430
interface TimePickerProps<T extends TimeValue> {
431
/** The current value (controlled). */
432
value?: T | null;
433
/** The default value (uncontrolled). */
434
defaultValue?: T | null;
435
/** Handler that is called when the value changes. */
436
onChange?: (value: MappedTimeValue<T> | null) => void;
437
/** Whether to display the time in 12 or 24 hour format. */
438
hourCycle?: 12 | 24;
439
/** Determines the smallest unit that is displayed in the time picker. */
440
granularity?: 'hour' | 'minute' | 'second';
441
/** Whether to hide the time zone abbreviation. */
442
hideTimeZone?: boolean;
443
/** Whether to always show leading zeros in the hour field. */
444
shouldForceLeadingZeros?: boolean;
445
/** A placeholder time that influences the format of the placeholder shown when no value is selected. */
446
placeholderValue?: T;
447
/** The minimum allowed time that a user may select. */
448
minValue?: TimeValue | null;
449
/** The maximum allowed time that a user may select. */
450
maxValue?: TimeValue | null;
451
/** Whether the field is disabled. */
452
isDisabled?: boolean;
453
/** Whether the field is read only. */
454
isReadOnly?: boolean;
455
/** Whether the element should receive focus on render. */
456
autoFocus?: boolean;
457
/** The name of the input element, used when submitting an HTML form. */
458
name?: string;
459
/** Whether user input is required on the input before form submission. */
460
isRequired?: boolean;
461
/** Whether the input value is invalid. */
462
isInvalid?: boolean;
463
/** The current validation state of the time picker. */
464
validationState?: ValidationState;
465
}
466
```
467
468
## Core Types
469
470
```typescript { .api }
471
// Date value types from @internationalized/date
472
type DateValue = CalendarDate | CalendarDateTime | ZonedDateTime;
473
type TimeValue = Time | CalendarDateTime | ZonedDateTime;
474
type Granularity = 'day' | 'hour' | 'minute' | 'second';
475
476
// Mapped types for controlled values
477
type MappedDateValue<T> =
478
T extends ZonedDateTime ? ZonedDateTime :
479
T extends CalendarDateTime ? CalendarDateTime :
480
T extends CalendarDate ? CalendarDate :
481
never;
482
483
type MappedTimeValue<T> =
484
T extends ZonedDateTime ? ZonedDateTime :
485
T extends CalendarDateTime ? CalendarDateTime :
486
T extends Time ? Time :
487
never;
488
489
// Calendar types from @internationalized/date
490
type CalendarIdentifier = 'buddhist' | 'ethiopic' | 'ethioaa' | 'coptic' | 'hebrew' | 'indian' | 'islamic-civil' | 'islamic-tbla' | 'islamic-umalqura' | 'japanese' | 'persian' | 'roc' | 'gregory';
491
492
interface Calendar {
493
identifier: CalendarIdentifier;
494
toDate(date: DateValue): Date;
495
fromDate(date: Date): CalendarDate;
496
getDaysInMonth(date: DateValue): number;
497
getMonthsInYear(date: DateValue): number;
498
getYearsInEra(date: DateValue): number;
499
getEras(): string[];
500
balanceDate(date: DateValue): CalendarDate;
501
}
502
503
interface DateFormatter {
504
formatToParts(date: Date): Intl.DateTimeFormatPart[];
505
format(date: Date): string;
506
formatRange(startDate: Date, endDate: Date): string;
507
formatRangeToParts(startDate: Date, endDate: Date): Intl.DateTimeFormatPart[];
508
}
509
510
// Core classes from @internationalized/date
511
interface CalendarDate {
512
readonly calendar: Calendar;
513
readonly era: string;
514
readonly year: number;
515
readonly month: number;
516
readonly day: number;
517
copy(): CalendarDate;
518
add(duration: DateDuration): CalendarDate;
519
subtract(duration: DateDuration): CalendarDate;
520
set(fields: DateFields): CalendarDate;
521
cycle(field: DateField, amount: number): CalendarDate;
522
toDate(timeZone: string): Date;
523
toString(): string;
524
compare(other: DateValue): number;
525
}
526
527
interface CalendarDateTime {
528
readonly calendar: Calendar;
529
readonly era: string;
530
readonly year: number;
531
readonly month: number;
532
readonly day: number;
533
readonly hour: number;
534
readonly minute: number;
535
readonly second: number;
536
readonly millisecond: number;
537
copy(): CalendarDateTime;
538
add(duration: DateTimeDuration): CalendarDateTime;
539
subtract(duration: DateTimeDuration): CalendarDateTime;
540
set(fields: DateTimeFields): CalendarDateTime;
541
cycle(field: DateField, amount: number): CalendarDateTime;
542
toDate(timeZone: string): Date;
543
toString(): string;
544
compare(other: DateValue): number;
545
}
546
547
interface ZonedDateTime {
548
readonly calendar: Calendar;
549
readonly era: string;
550
readonly year: number;
551
readonly month: number;
552
readonly day: number;
553
readonly hour: number;
554
readonly minute: number;
555
readonly second: number;
556
readonly millisecond: number;
557
readonly timeZone: string;
558
readonly offset: number;
559
copy(): ZonedDateTime;
560
add(duration: DateTimeDuration): ZonedDateTime;
561
subtract(duration: DateTimeDuration): ZonedDateTime;
562
set(fields: DateTimeFields): ZonedDateTime;
563
cycle(field: DateField, amount: number): ZonedDateTime;
564
toDate(): Date;
565
toString(): string;
566
compare(other: DateValue): number;
567
}
568
569
interface Time {
570
readonly hour: number;
571
readonly minute: number;
572
readonly second: number;
573
readonly millisecond: number;
574
copy(): Time;
575
add(duration: TimeDuration): Time;
576
subtract(duration: TimeDuration): Time;
577
set(fields: TimeFields): Time;
578
cycle(field: TimeField, amount: number): Time;
579
toString(): string;
580
compare(other: TimeValue): number;
581
}
582
583
// Duration and field types
584
interface DateDuration {
585
years?: number;
586
months?: number;
587
weeks?: number;
588
days?: number;
589
}
590
591
interface TimeDuration {
592
hours?: number;
593
minutes?: number;
594
seconds?: number;
595
milliseconds?: number;
596
}
597
598
interface DateTimeDuration extends DateDuration, TimeDuration {}
599
600
interface DateFields {
601
era?: string;
602
year?: number;
603
month?: number;
604
day?: number;
605
}
606
607
interface TimeFields {
608
hour?: number;
609
minute?: number;
610
second?: number;
611
millisecond?: number;
612
}
613
614
interface DateTimeFields extends DateFields, TimeFields {}
615
616
type DateField = keyof DateFields;
617
type TimeField = keyof TimeFields;
618
619
// Segment types for date field editing
620
type SegmentType = 'era' | 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'dayPeriod' | 'literal' | 'timeZoneName';
621
622
interface DateSegment {
623
type: SegmentType;
624
text: string;
625
value?: number;
626
minValue?: number;
627
maxValue?: number;
628
isPlaceholder: boolean;
629
placeholder: string;
630
isEditable: boolean;
631
}
632
633
// Range types for date range pickers
634
interface RangeValue<T> {
635
start: T;
636
end: T;
637
}
638
639
type DateRange = RangeValue<DateValue>;
640
type TimeRange = RangeValue<TimeValue>;
641
642
// Validation and formatting
643
interface FieldOptions {
644
year?: 'numeric' | '2-digit';
645
month?: 'numeric' | '2-digit' | 'narrow' | 'short' | 'long';
646
day?: 'numeric' | '2-digit';
647
hour?: 'numeric' | '2-digit';
648
minute?: 'numeric' | '2-digit';
649
second?: 'numeric' | '2-digit';
650
}
651
652
interface FormatterOptions {
653
timeZone?: string;
654
hideTimeZone?: boolean;
655
granularity?: Granularity;
656
maxGranularity?: 'year' | 'month' | Granularity;
657
hourCycle?: 12 | 24;
658
showEra?: boolean;
659
shouldForceLeadingZeros?: boolean;
660
}
661
662
// Validation and state types from React Stately ecosystem
663
type ValidationState = 'valid' | 'invalid';
664
665
interface FormValidationState {
666
/** Whether the input value is invalid according to validation rules */
667
isInvalid: boolean;
668
/** The current validation state @deprecated Use isInvalid instead */
669
validationState: ValidationState | null;
670
/** Validation errors */
671
validationErrors: string[];
672
/** Commit validation state changes */
673
commitValidation(): void;
674
/** Update validation state */
675
updateValidation(result: ValidationResult): void;
676
/** Display validation state for UI rendering */
677
displayValidation: {
678
isInvalid: boolean;
679
validationErrors: string[];
680
};
681
}
682
683
interface OverlayTriggerState {
684
/** Whether the overlay is currently open */
685
isOpen: boolean;
686
/** Sets whether the overlay is open */
687
setOpen(isOpen: boolean): void;
688
/** Opens the overlay */
689
open(): void;
690
/** Closes the overlay */
691
close(): void;
692
/** Toggles the overlay open/closed state */
693
toggle(): void;
694
}
695
696
interface ValidationResult {
697
/** Whether the value is invalid */
698
isInvalid: boolean;
699
/** Array of validation error messages */
700
validationErrors: string[];
701
/** Detailed validation state for different error types */
702
validationDetails: ValidityState;
703
}
704
```