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