@ceed/ads 1.23.3 → 1.23.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/data-display/Badge.md +71 -39
- package/dist/components/data-display/InfoSign.md +74 -98
- package/dist/components/data-display/Typography.md +310 -61
- package/dist/components/feedback/CircularProgress.md +257 -0
- package/dist/components/feedback/Skeleton.md +280 -0
- package/dist/components/feedback/llms.txt +2 -0
- package/dist/components/inputs/ButtonGroup.md +115 -106
- package/dist/components/inputs/Calendar.md +98 -459
- package/dist/components/inputs/CurrencyInput.md +181 -8
- package/dist/components/inputs/DatePicker.md +108 -436
- package/dist/components/inputs/DateRangePicker.md +130 -496
- package/dist/components/inputs/FilterMenu.md +169 -19
- package/dist/components/inputs/FilterableCheckboxGroup.md +119 -24
- package/dist/components/inputs/FormControl.md +368 -0
- package/dist/components/inputs/IconButton.md +137 -88
- package/dist/components/inputs/MonthPicker.md +95 -427
- package/dist/components/inputs/MonthRangePicker.md +89 -471
- package/dist/components/inputs/PercentageInput.md +183 -19
- package/dist/components/inputs/RadioButton.md +163 -35
- package/dist/components/inputs/RadioList.md +241 -0
- package/dist/components/inputs/RadioTileGroup.md +146 -62
- package/dist/components/inputs/Select.md +219 -328
- package/dist/components/inputs/Slider.md +334 -0
- package/dist/components/inputs/Switch.md +136 -376
- package/dist/components/inputs/Textarea.md +209 -11
- package/dist/components/inputs/Uploader/Uploader.md +145 -66
- package/dist/components/inputs/llms.txt +3 -0
- package/dist/components/navigation/Breadcrumbs.md +80 -322
- package/dist/components/navigation/Dropdown.md +92 -221
- package/dist/components/navigation/IconMenuButton.md +40 -502
- package/dist/components/navigation/InsetDrawer.md +68 -738
- package/dist/components/navigation/Link.md +39 -298
- package/dist/components/navigation/Menu.md +92 -285
- package/dist/components/navigation/MenuButton.md +55 -448
- package/dist/components/navigation/Pagination.md +47 -338
- package/dist/components/navigation/ProfileMenu.md +45 -268
- package/dist/components/navigation/Stepper.md +160 -28
- package/dist/components/navigation/Tabs.md +57 -316
- package/dist/components/surfaces/Sheet.md +150 -333
- package/dist/guides/ThemeProvider.md +116 -0
- package/dist/guides/llms.txt +9 -0
- package/dist/llms.txt +8 -0
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# DatePicker
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
DatePicker is a form input component that allows users to select a single date from a calendar popup or by typing directly into the input field. It provides flexible date formatting options, date range restrictions, and supports both controlled and uncontrolled modes.
|
|
4
4
|
|
|
5
|
-
DatePicker is
|
|
5
|
+
DatePicker is essential for any form that requires date input -- booking systems, scheduling interfaces, data filtering, and record keeping. It handles the complexity of date parsing, formatting, and validation so consumers can focus on business logic.
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
8
|
<DatePicker onChange={fn()} />
|
|
@@ -28,20 +28,6 @@ DatePicker is a form input component that allows users to select a single date f
|
|
|
28
28
|
| disableFuture | — | — |
|
|
29
29
|
| disablePast | — | — |
|
|
30
30
|
|
|
31
|
-
> ⚠️ **Usage Warning** ⚠️
|
|
32
|
-
>
|
|
33
|
-
> DatePicker involves complex date handling logic:
|
|
34
|
-
>
|
|
35
|
-
> - **Format vs DisplayFormat**: `format` affects the value in `onChange`, while `displayFormat` affects what users see
|
|
36
|
-
> - **Value format consistency**: The `value` and `defaultValue` props must match the `format` prop
|
|
37
|
-
> - **Date validation**: Invalid date strings can cause unexpected behavior
|
|
38
|
-
> - **Timezone considerations**: Be aware of timezone issues when working with dates
|
|
39
|
-
|
|
40
|
-
> 💡 **Use built-in form props**
|
|
41
|
-
>
|
|
42
|
-
> This component natively supports form elements such as `label` and `helperText` props.
|
|
43
|
-
> When building forms, use these built-in props instead of manually composing labels and helper text with Typography.
|
|
44
|
-
|
|
45
31
|
## Usage
|
|
46
32
|
|
|
47
33
|
```tsx
|
|
@@ -60,19 +46,14 @@ function DateForm() {
|
|
|
60
46
|
}
|
|
61
47
|
```
|
|
62
48
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Interactive example with all controls.
|
|
68
|
-
|
|
69
|
-
```tsx
|
|
70
|
-
<DatePicker onChange={fn()} />
|
|
71
|
-
```
|
|
49
|
+
> **Use built-in form props**
|
|
50
|
+
>
|
|
51
|
+
> This component natively supports form elements such as `label` and `helperText` props.
|
|
52
|
+
> When building forms, use these built-in props instead of manually composing labels and helper text with Typography.
|
|
72
53
|
|
|
73
|
-
|
|
54
|
+
## Sizes
|
|
74
55
|
|
|
75
|
-
DatePicker supports three sizes
|
|
56
|
+
DatePicker supports three sizes (`sm`, `md`, `lg`) to fit different layout densities.
|
|
76
57
|
|
|
77
58
|
```tsx
|
|
78
59
|
<Stack gap={2}>
|
|
@@ -82,9 +63,9 @@ DatePicker supports three sizes for different layouts.
|
|
|
82
63
|
</Stack>
|
|
83
64
|
```
|
|
84
65
|
|
|
85
|
-
|
|
66
|
+
## Label and Helper Text
|
|
86
67
|
|
|
87
|
-
|
|
68
|
+
Use the `label` prop to provide a visible label, and `helperText` to give users additional guidance about expected input.
|
|
88
69
|
|
|
89
70
|
```tsx
|
|
90
71
|
<DatePicker
|
|
@@ -93,10 +74,6 @@ Add a label above the date picker.
|
|
|
93
74
|
/>
|
|
94
75
|
```
|
|
95
76
|
|
|
96
|
-
### With Helper Text
|
|
97
|
-
|
|
98
|
-
Provide additional guidance below the input.
|
|
99
|
-
|
|
100
77
|
```tsx
|
|
101
78
|
<DatePicker
|
|
102
79
|
onChange={fn()}
|
|
@@ -105,9 +82,9 @@ Provide additional guidance below the input.
|
|
|
105
82
|
/>
|
|
106
83
|
```
|
|
107
84
|
|
|
108
|
-
|
|
85
|
+
## Validation and Error State
|
|
109
86
|
|
|
110
|
-
|
|
87
|
+
Set `error` to `true` and combine with `helperText` to communicate validation failures. Use the `required` prop to indicate mandatory fields.
|
|
111
88
|
|
|
112
89
|
```tsx
|
|
113
90
|
<DatePicker
|
|
@@ -118,10 +95,6 @@ Show validation errors with error styling.
|
|
|
118
95
|
/>
|
|
119
96
|
```
|
|
120
97
|
|
|
121
|
-
### Required Field
|
|
122
|
-
|
|
123
|
-
Mark the field as required in forms.
|
|
124
|
-
|
|
125
98
|
```tsx
|
|
126
99
|
<DatePicker
|
|
127
100
|
onChange={fn()}
|
|
@@ -131,9 +104,9 @@ Mark the field as required in forms.
|
|
|
131
104
|
/>
|
|
132
105
|
```
|
|
133
106
|
|
|
134
|
-
|
|
107
|
+
## Disabled and Read-Only States
|
|
135
108
|
|
|
136
|
-
|
|
109
|
+
The `disabled` prop prevents all interaction. The `readOnly` prop displays the value but blocks changes, while `inputReadOnly` disables keyboard typing but still allows calendar selection -- useful for mobile scenarios.
|
|
137
110
|
|
|
138
111
|
```tsx
|
|
139
112
|
<DatePicker
|
|
@@ -142,31 +115,39 @@ Prevent user interaction when disabled.
|
|
|
142
115
|
/>
|
|
143
116
|
```
|
|
144
117
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
118
|
+
```tsx
|
|
119
|
+
<DatePicker
|
|
120
|
+
onChange={fn()}
|
|
121
|
+
value="2024/04/01"
|
|
122
|
+
readOnly
|
|
123
|
+
/>
|
|
124
|
+
```
|
|
148
125
|
|
|
149
126
|
```tsx
|
|
150
127
|
<DatePicker
|
|
151
128
|
onChange={fn()}
|
|
152
|
-
|
|
129
|
+
value="2024/04/01"
|
|
130
|
+
inputReadOnly
|
|
153
131
|
/>
|
|
154
132
|
```
|
|
155
133
|
|
|
156
|
-
|
|
134
|
+
## Date Restrictions
|
|
157
135
|
|
|
158
|
-
Restrict
|
|
136
|
+
Restrict selectable dates using `minDate`, `maxDate`, `disablePast`, `disableFuture`, or the flexible `shouldDisableDate` callback for custom logic such as disabling weekends or holidays.
|
|
159
137
|
|
|
160
138
|
```tsx
|
|
161
139
|
<DatePicker
|
|
162
140
|
onChange={fn()}
|
|
163
|
-
|
|
141
|
+
minDate="2024-04-10"
|
|
164
142
|
/>
|
|
165
143
|
```
|
|
166
144
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
145
|
+
```tsx
|
|
146
|
+
<DatePicker
|
|
147
|
+
onChange={fn()}
|
|
148
|
+
maxDate="2024-04-10"
|
|
149
|
+
/>
|
|
150
|
+
```
|
|
170
151
|
|
|
171
152
|
```tsx
|
|
172
153
|
<DatePicker
|
|
@@ -175,20 +156,24 @@ Prevent selection of dates in the future.
|
|
|
175
156
|
/>
|
|
176
157
|
```
|
|
177
158
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
159
|
+
```tsx
|
|
160
|
+
<DatePicker
|
|
161
|
+
onChange={fn()}
|
|
162
|
+
disablePast
|
|
163
|
+
/>
|
|
164
|
+
```
|
|
181
165
|
|
|
182
166
|
```tsx
|
|
183
167
|
<DatePicker
|
|
184
168
|
onChange={fn()}
|
|
185
169
|
disablePast
|
|
170
|
+
shouldDisableDate={date => [0, 6].includes(new Date(date).getDay()) || new Date(date).getTime() >= new Date().getTime() + 7 * 24 * 60 * 60 * 1000}
|
|
186
171
|
/>
|
|
187
172
|
```
|
|
188
173
|
|
|
189
|
-
|
|
174
|
+
## Controlled vs Uncontrolled
|
|
190
175
|
|
|
191
|
-
|
|
176
|
+
DatePicker works in both controlled mode (you manage `value` via state) and uncontrolled mode (component manages its own state via `defaultValue`).
|
|
192
177
|
|
|
193
178
|
```tsx
|
|
194
179
|
<Stack gap={2}>
|
|
@@ -206,10 +191,6 @@ Parent component manages the date state.
|
|
|
206
191
|
</Stack>
|
|
207
192
|
```
|
|
208
193
|
|
|
209
|
-
### Uncontrolled
|
|
210
|
-
|
|
211
|
-
Component manages its own state internally.
|
|
212
|
-
|
|
213
194
|
```tsx
|
|
214
195
|
<DatePicker
|
|
215
196
|
onChange={fn()}
|
|
@@ -219,9 +200,9 @@ Component manages its own state internally.
|
|
|
219
200
|
/>
|
|
220
201
|
```
|
|
221
202
|
|
|
222
|
-
|
|
203
|
+
## Format and Display Format
|
|
223
204
|
|
|
224
|
-
|
|
205
|
+
The `format` prop determines the shape of the value emitted through `onChange`, while `displayFormat` controls what users see in the input field. This separation lets you keep a consistent API format (e.g. `YYYY-MM-DD`) while displaying a locale-appropriate format (e.g. `MM/DD/YYYY`).
|
|
225
206
|
|
|
226
207
|
```tsx
|
|
227
208
|
<Stack gap={2}>
|
|
@@ -234,10 +215,6 @@ Different value formats for the `onChange` event.
|
|
|
234
215
|
</Stack>
|
|
235
216
|
```
|
|
236
217
|
|
|
237
|
-
### With Display Formats
|
|
238
|
-
|
|
239
|
-
Different display formats shown in the input field.
|
|
240
|
-
|
|
241
218
|
```tsx
|
|
242
219
|
<Stack gap={2}>
|
|
243
220
|
<DatePicker {...args} value={value1} label="YYYY.MM.DD" name="YYYY.MM.DD" displayFormat="YYYY.MM.DD" onChange={e => {
|
|
@@ -267,33 +244,38 @@ Different display formats shown in the input field.
|
|
|
267
244
|
</Stack>
|
|
268
245
|
```
|
|
269
246
|
|
|
270
|
-
|
|
247
|
+
**Supported format tokens:**
|
|
271
248
|
|
|
272
|
-
|
|
249
|
+
| Token | Description | Example |
|
|
250
|
+
| ------ | ------------- | ------- |
|
|
251
|
+
| `YYYY` | 4-digit year | 2024 |
|
|
252
|
+
| `MM` | 2-digit month | 04 |
|
|
253
|
+
| `DD` | 2-digit day | 15 |
|
|
273
254
|
|
|
274
|
-
|
|
275
|
-
<DatePicker
|
|
276
|
-
onChange={fn()}
|
|
277
|
-
value="2024/04/01"
|
|
278
|
-
inputReadOnly
|
|
279
|
-
/>
|
|
280
|
-
```
|
|
255
|
+
Common patterns: `YYYY/MM/DD` (default), `YYYY-MM-DD` (ISO 8601), `MM/DD/YYYY` (US), `DD/MM/YYYY` (EU), `DD.MM.YYYY` (German), `YYYY.MM.DD` (East Asian).
|
|
281
256
|
|
|
282
|
-
|
|
257
|
+
## Additional Features
|
|
283
258
|
|
|
284
|
-
|
|
259
|
+
### Reset Button
|
|
260
|
+
|
|
261
|
+
Pair DatePicker with an external reset button to programmatically clear the value.
|
|
285
262
|
|
|
286
263
|
```tsx
|
|
287
|
-
<
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
264
|
+
<div style={{
|
|
265
|
+
display: 'flex',
|
|
266
|
+
gap: '10px'
|
|
267
|
+
}}>
|
|
268
|
+
<DatePicker {...props} value={value} onChange={event => {
|
|
269
|
+
onChange?.(event);
|
|
270
|
+
setValue(event.target.value);
|
|
271
|
+
}} />
|
|
272
|
+
<Button onClick={() => setValue('')}>Reset</Button>
|
|
273
|
+
</div>
|
|
292
274
|
```
|
|
293
275
|
|
|
294
276
|
### Hide Clear Button
|
|
295
277
|
|
|
296
|
-
Remove the clear button from the calendar popup.
|
|
278
|
+
Remove the built-in clear button from the calendar popup when clearing is not desired.
|
|
297
279
|
|
|
298
280
|
```tsx
|
|
299
281
|
<DatePicker
|
|
@@ -303,21 +285,9 @@ Remove the clear button from the calendar popup.
|
|
|
303
285
|
/>
|
|
304
286
|
```
|
|
305
287
|
|
|
306
|
-
### Custom Date Disabling
|
|
307
|
-
|
|
308
|
-
Use `shouldDisableDate` to disable specific dates.
|
|
309
|
-
|
|
310
|
-
```tsx
|
|
311
|
-
<DatePicker
|
|
312
|
-
onChange={fn()}
|
|
313
|
-
disablePast
|
|
314
|
-
shouldDisableDate={date => [0, 6].includes(new Date(date).getDay()) || new Date(date).getTime() >= new Date().getTime() + 7 * 24 * 60 * 60 * 1000}
|
|
315
|
-
/>
|
|
316
|
-
```
|
|
317
|
-
|
|
318
288
|
### Synchronized Date Pickers
|
|
319
289
|
|
|
320
|
-
Multiple
|
|
290
|
+
Multiple DatePicker instances can share the same value for dependent field scenarios.
|
|
321
291
|
|
|
322
292
|
```tsx
|
|
323
293
|
<Stack gap={2}>
|
|
@@ -329,25 +299,6 @@ Multiple date pickers sharing the same value.
|
|
|
329
299
|
</Stack>
|
|
330
300
|
```
|
|
331
301
|
|
|
332
|
-
## When to Use
|
|
333
|
-
|
|
334
|
-
### ✅ Good Use Cases
|
|
335
|
-
|
|
336
|
-
- **Form date fields**: Birthdays, appointment dates, due dates
|
|
337
|
-
- **Booking systems**: Hotel check-in/out, flight dates, reservations
|
|
338
|
-
- **Scheduling**: Event dates, meeting times, deadlines
|
|
339
|
-
- **Data filtering**: Date range filters in reports and dashboards
|
|
340
|
-
- **Historical data**: Recording when events occurred
|
|
341
|
-
- **Deadline selection**: Task due dates, project milestones
|
|
342
|
-
|
|
343
|
-
### ❌ When Not to Use
|
|
344
|
-
|
|
345
|
-
- **Date ranges**: Use DateRangePicker for start/end date pairs
|
|
346
|
-
- **Month/Year only**: Use MonthPicker for month-level granularity
|
|
347
|
-
- **Time selection**: Use a dedicated TimePicker component
|
|
348
|
-
- **Relative dates**: For "last 7 days" style filters, use dropdown selection
|
|
349
|
-
- **Known date sets**: For selecting from predefined dates, consider Select or RadioButton
|
|
350
|
-
|
|
351
302
|
## Common Use Cases
|
|
352
303
|
|
|
353
304
|
### Form with Validation
|
|
@@ -362,7 +313,6 @@ function BookingForm() {
|
|
|
362
313
|
setError('Please select a check-in date');
|
|
363
314
|
return;
|
|
364
315
|
}
|
|
365
|
-
// Submit form
|
|
366
316
|
};
|
|
367
317
|
|
|
368
318
|
return (
|
|
@@ -379,62 +329,21 @@ function BookingForm() {
|
|
|
379
329
|
disablePast
|
|
380
330
|
required
|
|
381
331
|
/>
|
|
382
|
-
<Button type="submit">Book Now</Button>
|
|
383
332
|
</form>
|
|
384
333
|
);
|
|
385
334
|
}
|
|
386
335
|
```
|
|
387
336
|
|
|
388
|
-
### Date
|
|
389
|
-
|
|
390
|
-
```tsx
|
|
391
|
-
function EventScheduler() {
|
|
392
|
-
const [eventDate, setEventDate] = useState('');
|
|
393
|
-
|
|
394
|
-
// Event must be within the next 30 days
|
|
395
|
-
const today = new Date();
|
|
396
|
-
const maxDate = new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000);
|
|
397
|
-
|
|
398
|
-
const formatDate = (date) => {
|
|
399
|
-
const year = date.getFullYear();
|
|
400
|
-
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
401
|
-
const day = String(date.getDate()).padStart(2, '0');
|
|
402
|
-
return `${year}/${month}/${day}`;
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
return (
|
|
406
|
-
<DatePicker
|
|
407
|
-
label="Event Date"
|
|
408
|
-
value={eventDate}
|
|
409
|
-
onChange={(e) => setEventDate(e.target.value)}
|
|
410
|
-
minDate={formatDate(today)}
|
|
411
|
-
maxDate={formatDate(maxDate)}
|
|
412
|
-
helperText="Select a date within the next 30 days"
|
|
413
|
-
/>
|
|
414
|
-
);
|
|
415
|
-
}
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
### Custom Date Validation
|
|
337
|
+
### Custom Date Disabling (Weekdays Only)
|
|
419
338
|
|
|
420
339
|
```tsx
|
|
421
340
|
function AppointmentPicker() {
|
|
422
341
|
const [date, setDate] = useState('');
|
|
423
342
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
// Disable weekends (Saturday = 6, Sunday = 0)
|
|
430
|
-
if (day === 0 || day === 6) return true;
|
|
431
|
-
|
|
432
|
-
// Disable specific holidays
|
|
433
|
-
const holidays = ['2024/01/01', '2024/12/25'];
|
|
434
|
-
if (holidays.includes(dateString)) return true;
|
|
435
|
-
|
|
436
|
-
return false;
|
|
437
|
-
};
|
|
343
|
+
const shouldDisableDate = useCallback((dateString: string) => {
|
|
344
|
+
const day = new Date(dateString).getDay();
|
|
345
|
+
return day === 0 || day === 6; // Disable weekends
|
|
346
|
+
}, []);
|
|
438
347
|
|
|
439
348
|
return (
|
|
440
349
|
<DatePicker
|
|
@@ -443,326 +352,89 @@ function AppointmentPicker() {
|
|
|
443
352
|
onChange={(e) => setDate(e.target.value)}
|
|
444
353
|
shouldDisableDate={shouldDisableDate}
|
|
445
354
|
disablePast
|
|
446
|
-
helperText="Weekdays only
|
|
355
|
+
helperText="Weekdays only"
|
|
447
356
|
/>
|
|
448
357
|
);
|
|
449
358
|
}
|
|
450
359
|
```
|
|
451
360
|
|
|
452
|
-
###
|
|
361
|
+
### Locale-Aware Display
|
|
453
362
|
|
|
454
363
|
```tsx
|
|
455
|
-
function
|
|
364
|
+
function RegionalDateField({ locale }: { locale: string }) {
|
|
456
365
|
const [date, setDate] = useState('');
|
|
457
366
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
case 'en-US':
|
|
462
|
-
return 'MM/DD/YYYY';
|
|
463
|
-
case 'en-GB':
|
|
464
|
-
return 'DD/MM/YYYY';
|
|
465
|
-
case 'de-DE':
|
|
466
|
-
return 'DD.MM.YYYY';
|
|
467
|
-
default:
|
|
468
|
-
return 'YYYY/MM/DD';
|
|
469
|
-
}
|
|
470
|
-
};
|
|
367
|
+
const displayFormat = locale === 'en-US' ? 'MM/DD/YYYY'
|
|
368
|
+
: locale === 'en-GB' ? 'DD/MM/YYYY'
|
|
369
|
+
: 'YYYY-MM-DD';
|
|
471
370
|
|
|
472
371
|
return (
|
|
473
372
|
<DatePicker
|
|
474
373
|
label="Date"
|
|
475
374
|
value={date}
|
|
476
375
|
onChange={(e) => setDate(e.target.value)}
|
|
477
|
-
format="YYYY/MM/DD"
|
|
478
|
-
displayFormat={
|
|
479
|
-
/>
|
|
480
|
-
);
|
|
481
|
-
}
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
### Mobile-Friendly Input
|
|
485
|
-
|
|
486
|
-
```tsx
|
|
487
|
-
function MobileDatePicker() {
|
|
488
|
-
const [date, setDate] = useState('');
|
|
489
|
-
|
|
490
|
-
return (
|
|
491
|
-
<DatePicker
|
|
492
|
-
label="Select Date"
|
|
493
|
-
value={date}
|
|
494
|
-
onChange={(e) => setDate(e.target.value)}
|
|
495
|
-
inputReadOnly // Prevent keyboard on mobile, use calendar only
|
|
496
|
-
size="lg" // Larger touch targets
|
|
376
|
+
format="YYYY/MM/DD"
|
|
377
|
+
displayFormat={displayFormat}
|
|
497
378
|
/>
|
|
498
379
|
);
|
|
499
380
|
}
|
|
500
381
|
```
|
|
501
382
|
|
|
502
|
-
### Form Integration with React Hook Form
|
|
503
|
-
|
|
504
|
-
```tsx
|
|
505
|
-
function DateFormWithHookForm() {
|
|
506
|
-
const { register, handleSubmit, setValue, watch } = useForm();
|
|
507
|
-
const birthDate = watch('birthDate');
|
|
508
|
-
|
|
509
|
-
return (
|
|
510
|
-
<form onSubmit={handleSubmit(onSubmit)}>
|
|
511
|
-
<DatePicker
|
|
512
|
-
label="Birth Date"
|
|
513
|
-
value={birthDate || ''}
|
|
514
|
-
onChange={(e) => setValue('birthDate', e.target.value)}
|
|
515
|
-
disableFuture
|
|
516
|
-
required
|
|
517
|
-
/>
|
|
518
|
-
</form>
|
|
519
|
-
);
|
|
520
|
-
}
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
## Props and Customization
|
|
524
|
-
|
|
525
|
-
### Key Props
|
|
526
|
-
|
|
527
|
-
| Prop | Type | Default | Description |
|
|
528
|
-
| ------------------- | ----------------------------------------------------------- | ---------------- | ------------------------------------------- |
|
|
529
|
-
| `value` | `string` | - | Controlled date value (must match `format`) |
|
|
530
|
-
| `defaultValue` | `string` | - | Default value for uncontrolled mode |
|
|
531
|
-
| `onChange` | `(e: { target: { name?: string; value: string } }) => void` | - | Change handler |
|
|
532
|
-
| `format` | `string` | `'YYYY/MM/DD'` | Format for `value` and `onChange` |
|
|
533
|
-
| `displayFormat` | `string` | Same as `format` | Format displayed in the input |
|
|
534
|
-
| `label` | `string` | - | Label text |
|
|
535
|
-
| `helperText` | `string` | - | Helper text below input |
|
|
536
|
-
| `error` | `boolean` | `false` | Error state |
|
|
537
|
-
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Component size |
|
|
538
|
-
| `disabled` | `boolean` | `false` | Disabled state |
|
|
539
|
-
| `required` | `boolean` | `false` | Required field indicator |
|
|
540
|
-
| `minDate` | `string` | - | Minimum selectable date |
|
|
541
|
-
| `maxDate` | `string` | - | Maximum selectable date |
|
|
542
|
-
| `disableFuture` | `boolean` | `false` | Disable all future dates |
|
|
543
|
-
| `disablePast` | `boolean` | `false` | Disable all past dates |
|
|
544
|
-
| `shouldDisableDate` | `(date: string) => boolean` | - | Custom date disable function |
|
|
545
|
-
| `inputReadOnly` | `boolean` | `false` | Prevent typing, calendar only |
|
|
546
|
-
| `readOnly` | `boolean` | `false` | Fully read-only |
|
|
547
|
-
| `hideClearButton` | `boolean` | `false` | Hide clear button in calendar |
|
|
548
|
-
|
|
549
|
-
### Format vs DisplayFormat
|
|
550
|
-
|
|
551
|
-
Understanding the difference between `format` and `displayFormat`:
|
|
552
|
-
|
|
553
|
-
```tsx
|
|
554
|
-
// format: Affects the value in onChange
|
|
555
|
-
// displayFormat: Affects what users see in the input
|
|
556
|
-
|
|
557
|
-
<DatePicker
|
|
558
|
-
format="YYYY-MM-DD" // onChange returns "2024-04-15"
|
|
559
|
-
displayFormat="MM/DD/YYYY" // Input shows "04/15/2024"
|
|
560
|
-
onChange={(e) => {
|
|
561
|
-
console.log(e.target.value); // "2024-04-15"
|
|
562
|
-
}}
|
|
563
|
-
/>
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
### Supported Format Tokens
|
|
567
|
-
|
|
568
|
-
| Token | Description | Example |
|
|
569
|
-
| ------ | ------------- | ------- |
|
|
570
|
-
| `YYYY` | 4-digit year | 2024 |
|
|
571
|
-
| `MM` | 2-digit month | 04 |
|
|
572
|
-
| `DD` | 2-digit day | 15 |
|
|
573
|
-
|
|
574
|
-
Common format patterns:
|
|
575
|
-
|
|
576
|
-
- `YYYY/MM/DD` - ISO-like (default)
|
|
577
|
-
- `YYYY-MM-DD` - ISO 8601
|
|
578
|
-
- `MM/DD/YYYY` - US format
|
|
579
|
-
- `DD/MM/YYYY` - European format
|
|
580
|
-
- `DD.MM.YYYY` - German format
|
|
581
|
-
- `YYYY.MM.DD` - East Asian format
|
|
582
|
-
|
|
583
|
-
### Controlled vs Uncontrolled
|
|
584
|
-
|
|
585
|
-
```tsx
|
|
586
|
-
// Uncontrolled - component manages state
|
|
587
|
-
<DatePicker
|
|
588
|
-
defaultValue="2024/04/01"
|
|
589
|
-
onChange={(e) => console.log(e.target.value)}
|
|
590
|
-
/>
|
|
591
|
-
|
|
592
|
-
// Controlled - you manage state
|
|
593
|
-
const [date, setDate] = useState('2024/04/01');
|
|
594
|
-
<DatePicker
|
|
595
|
-
value={date}
|
|
596
|
-
onChange={(e) => setDate(e.target.value)}
|
|
597
|
-
/>
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
## Accessibility
|
|
601
|
-
|
|
602
|
-
DatePicker includes built-in accessibility features:
|
|
603
|
-
|
|
604
|
-
### ARIA Attributes
|
|
605
|
-
|
|
606
|
-
- Input has proper `role="textbox"`
|
|
607
|
-
- Calendar button has `aria-label="Toggle Calendar"`
|
|
608
|
-
- Calendar popup uses `role="tooltip"` with proper labeling
|
|
609
|
-
- Date buttons announce the full date to screen readers
|
|
610
|
-
|
|
611
|
-
### Keyboard Navigation
|
|
612
|
-
|
|
613
|
-
- **Tab**: Move focus between input and calendar button
|
|
614
|
-
- **Enter/Space**: Open calendar when focused on button
|
|
615
|
-
- **Arrow Keys**: Navigate within calendar
|
|
616
|
-
- **Escape**: Close calendar popup
|
|
617
|
-
- **Enter**: Select focused date
|
|
618
|
-
|
|
619
|
-
### Screen Reader Support
|
|
620
|
-
|
|
621
|
-
```tsx
|
|
622
|
-
// Dates are announced with full context
|
|
623
|
-
<button aria-label="April 15, 2024">15</button>
|
|
624
|
-
|
|
625
|
-
// Navigation buttons are descriptive
|
|
626
|
-
<button aria-label="Previous Month">←</button>
|
|
627
|
-
<button aria-label="Next Month">→</button>
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
### Focus Management
|
|
631
|
-
|
|
632
|
-
- Focus moves to calendar when opened
|
|
633
|
-
- Focus returns to input when calendar closes
|
|
634
|
-
- Clear visual focus indicators on all interactive elements
|
|
635
|
-
|
|
636
383
|
## Best Practices
|
|
637
384
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
1. **Use appropriate date restrictions**: Set min/max dates when applicable
|
|
641
|
-
|
|
642
|
-
```tsx
|
|
643
|
-
// ✅ Good: Reasonable date constraints
|
|
644
|
-
<DatePicker
|
|
645
|
-
label="Birth Date"
|
|
646
|
-
disableFuture
|
|
647
|
-
minDate="1900/01/01"
|
|
648
|
-
/>
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
2. **Provide clear labels and helper text**: Guide users on expected input
|
|
385
|
+
1. **Always provide a label**
|
|
652
386
|
|
|
653
387
|
```tsx
|
|
654
|
-
|
|
655
|
-
<DatePicker
|
|
656
|
-
label="Preferred Delivery Date"
|
|
657
|
-
helperText="Select a weekday within the next 2 weeks"
|
|
658
|
-
/>
|
|
659
|
-
```
|
|
660
|
-
|
|
661
|
-
3. **Use consistent formats**: Match your application's locale conventions
|
|
388
|
+
{/* Do */}
|
|
389
|
+
<DatePicker label="Birth Date" />
|
|
662
390
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
<DatePicker
|
|
666
|
-
format="YYYY-MM-DD" // API format
|
|
667
|
-
displayFormat="MM/DD/YYYY" // US locale display
|
|
668
|
-
/>
|
|
391
|
+
{/* Don't */}
|
|
392
|
+
<DatePicker placeholder="Birth Date" />
|
|
669
393
|
```
|
|
670
394
|
|
|
671
|
-
|
|
395
|
+
2. **Keep value format consistent with the `format` prop**
|
|
672
396
|
|
|
673
397
|
```tsx
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
{date ? <span>Selected: {date}</span> : <span>No date selected</span>}
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
### ❌ Don't
|
|
398
|
+
{/* Do */}
|
|
399
|
+
<DatePicker format="YYYY/MM/DD" value="2024/04/15" />
|
|
680
400
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
```tsx
|
|
684
|
-
// ❌ Bad: Mismatched formats
|
|
685
|
-
<DatePicker
|
|
686
|
-
format="YYYY/MM/DD"
|
|
687
|
-
value="04/15/2024" // Wrong! Should be "2024/04/15"
|
|
688
|
-
/>
|
|
401
|
+
{/* Don't -- mismatched format causes unexpected behavior */}
|
|
402
|
+
<DatePicker format="YYYY/MM/DD" value="04/15/2024" />
|
|
689
403
|
```
|
|
690
404
|
|
|
691
|
-
|
|
405
|
+
3. **Use `displayFormat` to separate API format from UI format**
|
|
692
406
|
|
|
693
407
|
```tsx
|
|
694
|
-
|
|
695
|
-
<DatePicker
|
|
408
|
+
{/* Do -- consistent API format, locale-appropriate display */}
|
|
409
|
+
<DatePicker format="YYYY-MM-DD" displayFormat="MM/DD/YYYY" />
|
|
696
410
|
|
|
697
|
-
|
|
698
|
-
<DatePicker
|
|
411
|
+
{/* Don't -- changing format just for display affects onChange values */}
|
|
412
|
+
<DatePicker format="MM/DD/YYYY" />
|
|
699
413
|
```
|
|
700
414
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
4. **Don't use placeholder as label**: Use the `label` prop instead
|
|
415
|
+
4. **Set appropriate date boundaries for your use case**
|
|
704
416
|
|
|
705
417
|
```tsx
|
|
706
|
-
|
|
707
|
-
<DatePicker
|
|
418
|
+
{/* Do */}
|
|
419
|
+
<DatePicker label="Birth Date" disableFuture minDate="1900/01/01" />
|
|
708
420
|
|
|
709
|
-
|
|
421
|
+
{/* Don't -- unrestricted date range for a birth date field */}
|
|
710
422
|
<DatePicker label="Birth Date" />
|
|
711
423
|
```
|
|
712
424
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
### Memoize shouldDisableDate
|
|
716
|
-
|
|
717
|
-
For complex date validation logic, memoize the function:
|
|
718
|
-
|
|
719
|
-
```tsx
|
|
720
|
-
const shouldDisableDate = useCallback((dateString) => {
|
|
721
|
-
const date = new Date(dateString);
|
|
722
|
-
// Complex validation logic
|
|
723
|
-
return isHoliday(date) || isWeekend(date);
|
|
724
|
-
}, [holidays]); // Only recreate when holidays change
|
|
725
|
-
|
|
726
|
-
<DatePicker shouldDisableDate={shouldDisableDate} />
|
|
727
|
-
```
|
|
728
|
-
|
|
729
|
-
### Controlled Component Optimization
|
|
730
|
-
|
|
731
|
-
When using controlled mode, avoid unnecessary state updates:
|
|
425
|
+
5. **Show validation feedback with error and helperText**
|
|
732
426
|
|
|
733
427
|
```tsx
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}, []);
|
|
428
|
+
{/* Do */}
|
|
429
|
+
<DatePicker error={!isValid} helperText={errorMessage} />
|
|
737
430
|
|
|
738
|
-
|
|
431
|
+
{/* Don't -- silently ignore invalid state */}
|
|
432
|
+
<DatePicker value={invalidDate} />
|
|
739
433
|
```
|
|
740
434
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
When working with APIs that expect different date formats:
|
|
744
|
-
|
|
745
|
-
```tsx
|
|
746
|
-
function DateField({ value, onChange, apiFormat = 'YYYY-MM-DD' }) {
|
|
747
|
-
// Convert from API format to display
|
|
748
|
-
const displayValue = useMemo(() =>
|
|
749
|
-
convertFormat(value, apiFormat, 'YYYY/MM/DD'),
|
|
750
|
-
[value, apiFormat]
|
|
751
|
-
);
|
|
752
|
-
|
|
753
|
-
const handleChange = useCallback((e) => {
|
|
754
|
-
// Convert back to API format
|
|
755
|
-
onChange(convertFormat(e.target.value, 'YYYY/MM/DD', apiFormat));
|
|
756
|
-
}, [onChange, apiFormat]);
|
|
757
|
-
|
|
758
|
-
return (
|
|
759
|
-
<DatePicker
|
|
760
|
-
value={displayValue}
|
|
761
|
-
onChange={handleChange}
|
|
762
|
-
format="YYYY/MM/DD"
|
|
763
|
-
/>
|
|
764
|
-
);
|
|
765
|
-
}
|
|
766
|
-
```
|
|
435
|
+
## Accessibility
|
|
767
436
|
|
|
768
|
-
|
|
437
|
+
- **Keyboard navigation**: Tab moves focus between the input and the calendar toggle button. Arrow keys navigate dates within the open calendar. Enter selects the focused date, and Escape closes the calendar popup.
|
|
438
|
+
- **ARIA attributes**: The calendar toggle button has `aria-label="Toggle Calendar"`. The calendar popup uses `role="tooltip"` with descriptive labeling. Each date button announces the full date to screen readers.
|
|
439
|
+
- **Focus management**: Focus moves into the calendar when opened and returns to the input when the calendar closes. All interactive elements have clear focus indicators.
|
|
440
|
+
- **Use `inputReadOnly` on mobile**: When targeting touch devices, set `inputReadOnly` to prevent the virtual keyboard from appearing and guide users toward the calendar picker instead.
|