0
# Date Field State
1
2
State management for date field components with individually editable segments. Each part of a date value (year, month, day, hour, minute, second) is displayed in a separate editable segment, providing precise keyboard-based date and time input.
3
4
## Capabilities
5
6
### useDateFieldState Hook
7
8
Creates a state object for managing date field component state with segment-based editing, validation, and formatting.
9
10
```typescript { .api }
11
/**
12
* Provides state management for a date field component.
13
* A date field allows users to enter and edit date and time values using a keyboard.
14
* Each part of a date value is displayed in an individually editable segment.
15
* @param props - Configuration options including locale and calendar system
16
* @returns DateFieldState object with segment management and validation
17
*/
18
function useDateFieldState<T extends DateValue = DateValue>(
19
props: DateFieldStateOptions<T>
20
): DateFieldState;
21
22
interface DateFieldStateOptions<T extends DateValue = DateValue> extends DatePickerProps<T> {
23
/**
24
* The maximum unit to display in the date field.
25
* @default 'year'
26
*/
27
maxGranularity?: 'year' | 'month' | Granularity;
28
/** The locale to display and edit the value according to. */
29
locale: string;
30
/**
31
* A function that creates a Calendar object for a given calendar identifier.
32
* Such a function may be imported from the @internationalized/date package.
33
*/
34
createCalendar: (name: CalendarIdentifier) => Calendar;
35
}
36
37
interface DateFieldState extends FormValidationState {
38
/** The current field value. */
39
value: DateValue | null;
40
/** The default field value. */
41
defaultValue: DateValue | null;
42
/** The current value, converted to a native JavaScript Date object. */
43
dateValue: Date;
44
/** The calendar system currently in use. */
45
calendar: Calendar;
46
/** Sets the field's value. */
47
setValue(value: DateValue | null): void;
48
/** A list of segments for the current value. */
49
segments: DateSegment[];
50
/** A date formatter configured for the current locale and format. */
51
dateFormatter: DateFormatter;
52
/** The current validation state of the date field, based on the `validationState`, `minValue`, and `maxValue` props. @deprecated Use `isInvalid` instead. */
53
validationState: ValidationState | null;
54
/** Whether the date field is invalid, based on the `isInvalid`, `minValue`, and `maxValue` props. */
55
isInvalid: boolean;
56
/** The granularity for the field, based on the `granularity` prop and current value. */
57
granularity: Granularity;
58
/** The maximum date or time unit that is displayed in the field. */
59
maxGranularity: 'year' | 'month' | Granularity;
60
/** Whether the field is disabled. */
61
isDisabled: boolean;
62
/** Whether the field is read only. */
63
isReadOnly: boolean;
64
/** Whether the field is required. */
65
isRequired: boolean;
66
/** Increments the given segment. Upon reaching the minimum or maximum value, the value wraps around to the opposite limit. */
67
increment(type: SegmentType): void;
68
/** Decrements the given segment. Upon reaching the minimum or maximum value, the value wraps around to the opposite limit. */
69
decrement(type: SegmentType): void;
70
/** Increments the given segment by a larger amount, rounding it to the nearest increment. The amount to increment by depends on the field, for example 15 minutes, 7 days, and 5 years. Upon reaching the minimum or maximum value, the value wraps around to the opposite limit. */
71
incrementPage(type: SegmentType): void;
72
/** Decrements the given segment by a larger amount, rounding it to the nearest increment. The amount to decrement by depends on the field, for example 15 minutes, 7 days, and 5 years. Upon reaching the minimum or maximum value, the value wraps around to the opposite limit. */
73
decrementPage(type: SegmentType): void;
74
/** Sets the value of the given segment. */
75
setSegment(type: 'era', value: string): void;
76
setSegment(type: SegmentType, value: number): void;
77
/** Updates the remaining unfilled segments with the placeholder value. */
78
confirmPlaceholder(): void;
79
/** Clears the value of the given segment, reverting it to the placeholder. */
80
clearSegment(type: SegmentType): void;
81
/** Formats the current date value using the given options. */
82
formatValue(fieldOptions: FieldOptions): string;
83
/** Gets a formatter based on state's props. */
84
getDateFormatter(locale: string, formatOptions: FormatterOptions): DateFormatter;
85
}
86
```
87
88
**Usage Examples:**
89
90
```typescript
91
import { useDateFieldState } from "@react-stately/datepicker";
92
import { CalendarDate, createCalendar } from "@internationalized/date";
93
94
// Basic date field
95
function BasicDateField() {
96
const state = useDateFieldState({
97
locale: 'en-US',
98
createCalendar,
99
defaultValue: new CalendarDate(2023, 6, 15),
100
onChange: (value) => console.log("Date changed:", value?.toString())
101
});
102
103
return (
104
<div>
105
{state.segments.map((segment, index) => (
106
<span
107
key={index}
108
style={{
109
padding: '2px',
110
backgroundColor: segment.isPlaceholder ? '#f0f0f0' : 'white',
111
border: segment.isEditable ? '1px solid #ccc' : 'none'
112
}}
113
>
114
{segment.text}
115
</span>
116
))}
117
<div>Current value: {state.value?.toString()}</div>
118
</div>
119
);
120
}
121
122
// Time field with segment manipulation
123
function TimeField() {
124
const state = useDateFieldState({
125
locale: 'en-US',
126
createCalendar,
127
granularity: 'second',
128
maxGranularity: 'hour',
129
onChange: (value) => console.log("Time:", value)
130
});
131
132
const handleSegmentIncrement = (segment: DateSegment) => {
133
if (segment.isEditable) {
134
state.increment(segment.type);
135
}
136
};
137
138
return (
139
<div>
140
{state.segments.map((segment, index) => (
141
<button
142
key={index}
143
disabled={!segment.isEditable}
144
onClick={() => handleSegmentIncrement(segment)}
145
style={{
146
margin: '1px',
147
padding: '4px',
148
backgroundColor: segment.isPlaceholder ? '#f0f0f0' : 'white'
149
}}
150
>
151
{segment.text}
152
</button>
153
))}
154
<div>
155
<button onClick={() => state.confirmPlaceholder()}>
156
Confirm Placeholder
157
</button>
158
</div>
159
</div>
160
);
161
}
162
163
// Date field with validation and custom formatting
164
function ValidatedDateField() {
165
const state = useDateFieldState({
166
locale: 'fr-FR',
167
createCalendar,
168
minValue: new CalendarDate(2020, 1, 1),
169
maxValue: new CalendarDate(2030, 12, 31),
170
shouldForceLeadingZeros: true,
171
isRequired: true,
172
onChange: (value) => {
173
if (value) {
174
console.log("Valid date:", state.formatValue({
175
year: 'numeric',
176
month: 'long',
177
day: 'numeric'
178
}));
179
}
180
}
181
});
182
183
return (
184
<div>
185
<div style={{ border: state.isInvalid ? '2px solid red' : '1px solid #ccc' }}>
186
{state.segments.map((segment, index) => (
187
<input
188
key={index}
189
value={segment.text}
190
placeholder={segment.placeholder}
191
readOnly={!segment.isEditable}
192
onChange={(e) => {
193
if (segment.type === 'era') {
194
state.setSegment(segment.type, e.target.value);
195
} else {
196
const num = parseInt(e.target.value);
197
if (!isNaN(num)) {
198
state.setSegment(segment.type, num);
199
}
200
}
201
}}
202
style={{
203
width: `${segment.text.length + 2}ch`,
204
border: 'none',
205
textAlign: 'center'
206
}}
207
/>
208
))}
209
</div>
210
{state.isInvalid && <div style={{ color: 'red' }}>Invalid date range</div>}
211
</div>
212
);
213
}
214
```
215
216
### Date Segment Interface
217
218
Each segment represents an individual editable part of the date/time value.
219
220
```typescript { .api }
221
interface DateSegment {
222
/** The type of segment. */
223
type: SegmentType;
224
/** The formatted text for the segment. */
225
text: string;
226
/** The numeric value for the segment, if applicable. */
227
value?: number;
228
/** The minimum numeric value for the segment, if applicable. */
229
minValue?: number;
230
/** The maximum numeric value for the segment, if applicable. */
231
maxValue?: number;
232
/** Whether the value is a placeholder. */
233
isPlaceholder: boolean;
234
/** A placeholder string for the segment. */
235
placeholder: string;
236
/** Whether the segment is editable. */
237
isEditable: boolean;
238
}
239
240
type SegmentType = 'era' | 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'dayPeriod' | 'literal' | 'timeZoneName';
241
```
242
243
### Segment Manipulation Methods
244
245
Methods for incrementing, decrementing, and setting individual segments.
246
247
```typescript { .api }
248
/**
249
* Increments the given segment by 1. Upon reaching the maximum value, wraps to minimum.
250
* @param type - The segment type to increment
251
*/
252
increment(type: SegmentType): void;
253
254
/**
255
* Decrements the given segment by 1. Upon reaching the minimum value, wraps to maximum.
256
* @param type - The segment type to decrement
257
*/
258
decrement(type: SegmentType): void;
259
260
/**
261
* Increments the given segment by a larger amount (page step).
262
* Page steps: year=5, month=2, day=7, hour=2, minute=15, second=15
263
* @param type - The segment type to increment
264
*/
265
incrementPage(type: SegmentType): void;
266
267
/**
268
* Decrements the given segment by a larger amount (page step).
269
* @param type - The segment type to decrement
270
*/
271
decrementPage(type: SegmentType): void;
272
273
/**
274
* Sets the value of a specific segment
275
* @param type - The segment type to set
276
* @param value - The value to set (string for era, number for others)
277
*/
278
setSegment(type: 'era', value: string): void;
279
setSegment(type: SegmentType, value: number): void;
280
281
/**
282
* Clears the value of the given segment, reverting it to placeholder
283
* @param type - The segment type to clear
284
*/
285
clearSegment(type: SegmentType): void;
286
287
/**
288
* Updates remaining unfilled segments with the placeholder value
289
* Useful for completing partial date entry
290
*/
291
confirmPlaceholder(): void;
292
```
293
294
### Calendar Integration
295
296
The date field integrates with different calendar systems through the @internationalized/date library.
297
298
```typescript { .api }
299
interface DateFieldStateOptions<T> {
300
/** The locale to display and edit the value according to. */
301
locale: string;
302
/**
303
* A function that creates a Calendar object for a given calendar identifier.
304
* Such a function may be imported from the @internationalized/date package.
305
*/
306
createCalendar: (name: CalendarIdentifier) => Calendar;
307
}
308
```
309
310
Example calendar setup:
311
312
```typescript
313
import { createCalendar } from "@internationalized/date";
314
315
const state = useDateFieldState({
316
locale: 'ar-SA', // Arabic Saudi Arabia
317
createCalendar, // Supports Gregory, Islamic, Buddhist, etc.
318
// ... other props
319
});
320
```
321
322
### Granularity Control
323
324
Control which date/time segments are displayed and editable.
325
326
```typescript { .api }
327
interface DateFieldStateOptions<T> {
328
/**
329
* The maximum unit to display in the date field.
330
* @default 'year'
331
*/
332
maxGranularity?: 'year' | 'month' | Granularity;
333
/** Determines the smallest unit that is displayed in the date picker. */
334
granularity?: Granularity;
335
}
336
```
337
338
Examples:
339
- `granularity: 'day'` - Shows year, month, day
340
- `granularity: 'minute', maxGranularity: 'day'` - Shows day, hour, minute
341
- `granularity: 'second'` - Shows all segments including seconds