@ceed/ads 1.29.1 → 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/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 +289 -288
- package/dist/index.d.ts +1 -1
- package/dist/index.js +426 -369
- 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
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
MonthPicker is a form input component that allows users to select a specific month and year from a calendar popup. Unlike DatePicker which
|
|
6
|
-
|
|
7
|
-
It is ideal for scenarios like billing periods, report months, subscription dates, or any use case where day-level precision is unnecessary. The component supports multiple value formats and display formats, controlled and uncontrolled modes, and date range constraints.
|
|
5
|
+
MonthPicker is a form input component that allows users to select a specific month and year from a calendar popup. Unlike DatePicker which allows day-level selection, MonthPicker focuses on month-level granularity. It displays months in a grid format for easy selection and is ideal for scenarios like billing periods, report months, subscription dates, or any use case where day-level precision is unnecessary.
|
|
8
6
|
|
|
9
7
|
```tsx
|
|
10
8
|
<MonthPicker />
|
|
@@ -27,6 +25,15 @@ It is ideal for scenarios like billing periods, report months, subscription date
|
|
|
27
25
|
| format | — | — |
|
|
28
26
|
| onChange | — | — |
|
|
29
27
|
|
|
28
|
+
> ⚠️ **Usage Warning** ⚠️
|
|
29
|
+
>
|
|
30
|
+
> MonthPicker has some unique formatting behaviors:
|
|
31
|
+
>
|
|
32
|
+
> - **Internal value format**: Values internally use full date format (`"YYYY/MM/DD"`) with day set to `01`
|
|
33
|
+
> - **Display vs Value**: Input displays `YYYY/MM` but `onChange` receives `YYYY/MM/01`
|
|
34
|
+
> - **Format consistency**: Ensure your `value` prop matches the `format` prop
|
|
35
|
+
> - **displayFormat options**: Supports special token `MMMM` for full month names
|
|
36
|
+
|
|
30
37
|
## Usage
|
|
31
38
|
|
|
32
39
|
```tsx
|
|
@@ -45,14 +52,19 @@ function MonthForm() {
|
|
|
45
52
|
}
|
|
46
53
|
```
|
|
47
54
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
## Examples
|
|
56
|
+
|
|
57
|
+
### Playground
|
|
58
|
+
|
|
59
|
+
Interactive example with all controls.
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<MonthPicker />
|
|
63
|
+
```
|
|
52
64
|
|
|
53
|
-
|
|
65
|
+
### Sizes
|
|
54
66
|
|
|
55
|
-
MonthPicker supports three sizes
|
|
67
|
+
MonthPicker supports three sizes for different layouts.
|
|
56
68
|
|
|
57
69
|
```tsx
|
|
58
70
|
<Stack gap={2}>
|
|
@@ -62,11 +74,9 @@ MonthPicker supports three sizes (`sm`, `md`, `lg`) for different layouts and co
|
|
|
62
74
|
</Stack>
|
|
63
75
|
```
|
|
64
76
|
|
|
65
|
-
## Form Field Features
|
|
66
|
-
|
|
67
77
|
### With Label
|
|
68
78
|
|
|
69
|
-
Add a label above the month picker
|
|
79
|
+
Add a label above the month picker.
|
|
70
80
|
|
|
71
81
|
```tsx
|
|
72
82
|
<MonthPicker label="Date" />
|
|
@@ -74,7 +84,7 @@ Add a label above the month picker to indicate the field purpose.
|
|
|
74
84
|
|
|
75
85
|
### With Helper Text
|
|
76
86
|
|
|
77
|
-
Provide additional guidance below the input
|
|
87
|
+
Provide additional guidance below the input.
|
|
78
88
|
|
|
79
89
|
```tsx
|
|
80
90
|
<MonthPicker
|
|
@@ -83,43 +93,41 @@ Provide additional guidance below the input to help users understand the expecte
|
|
|
83
93
|
/>
|
|
84
94
|
```
|
|
85
95
|
|
|
86
|
-
###
|
|
96
|
+
### Error State
|
|
87
97
|
|
|
88
|
-
|
|
98
|
+
Show validation errors with error styling.
|
|
89
99
|
|
|
90
100
|
```tsx
|
|
91
101
|
<MonthPicker
|
|
92
|
-
label="
|
|
93
|
-
helperText="
|
|
94
|
-
|
|
102
|
+
label="Date"
|
|
103
|
+
helperText="Please select a date"
|
|
104
|
+
error
|
|
95
105
|
/>
|
|
96
106
|
```
|
|
97
107
|
|
|
98
|
-
###
|
|
108
|
+
### Required Field
|
|
99
109
|
|
|
100
|
-
|
|
110
|
+
Mark the field as required in forms.
|
|
101
111
|
|
|
102
112
|
```tsx
|
|
103
113
|
<MonthPicker
|
|
104
|
-
label="
|
|
105
|
-
helperText="
|
|
106
|
-
|
|
114
|
+
label="Label"
|
|
115
|
+
helperText="I'm helper text"
|
|
116
|
+
required
|
|
107
117
|
/>
|
|
108
118
|
```
|
|
109
119
|
|
|
110
120
|
### Disabled
|
|
111
121
|
|
|
112
|
-
Prevent user interaction when
|
|
122
|
+
Prevent user interaction when disabled.
|
|
113
123
|
|
|
114
124
|
```tsx
|
|
115
125
|
<MonthPicker disabled />
|
|
116
126
|
```
|
|
117
127
|
|
|
118
|
-
## Date Constraints
|
|
119
|
-
|
|
120
128
|
### Minimum Date
|
|
121
129
|
|
|
122
|
-
Restrict selection to months on or after a minimum date.
|
|
130
|
+
Restrict selection to months on or after a minimum date.
|
|
123
131
|
|
|
124
132
|
```tsx
|
|
125
133
|
<MonthPicker minDate="2024-04-10" />
|
|
@@ -127,7 +135,7 @@ Restrict selection to months on or after a minimum date. Months before the limit
|
|
|
127
135
|
|
|
128
136
|
### Maximum Date
|
|
129
137
|
|
|
130
|
-
Restrict selection to months on or before a maximum date.
|
|
138
|
+
Restrict selection to months on or before a maximum date.
|
|
131
139
|
|
|
132
140
|
```tsx
|
|
133
141
|
<MonthPicker maxDate="2024-04-10" />
|
|
@@ -135,7 +143,7 @@ Restrict selection to months on or before a maximum date. Months after the limit
|
|
|
135
143
|
|
|
136
144
|
### Disable Future
|
|
137
145
|
|
|
138
|
-
Prevent selection of
|
|
146
|
+
Prevent selection of months in the future.
|
|
139
147
|
|
|
140
148
|
```tsx
|
|
141
149
|
<MonthPicker disableFuture />
|
|
@@ -143,17 +151,15 @@ Prevent selection of any month in the future, relative to the current date.
|
|
|
143
151
|
|
|
144
152
|
### Disable Past
|
|
145
153
|
|
|
146
|
-
Prevent selection of
|
|
154
|
+
Prevent selection of months in the past.
|
|
147
155
|
|
|
148
156
|
```tsx
|
|
149
157
|
<MonthPicker disablePast />
|
|
150
158
|
```
|
|
151
159
|
|
|
152
|
-
## Controlled vs Uncontrolled
|
|
153
|
-
|
|
154
160
|
### Controlled
|
|
155
161
|
|
|
156
|
-
|
|
162
|
+
Parent component manages the month state.
|
|
157
163
|
|
|
158
164
|
```tsx
|
|
159
165
|
<Stack gap={2}>
|
|
@@ -172,7 +178,7 @@ The parent component manages the month state via the `value` and `onChange` prop
|
|
|
172
178
|
|
|
173
179
|
### Uncontrolled
|
|
174
180
|
|
|
175
|
-
|
|
181
|
+
Component manages its own state internally.
|
|
176
182
|
|
|
177
183
|
```tsx
|
|
178
184
|
<MonthPicker
|
|
@@ -182,11 +188,9 @@ The component manages its own state internally using `defaultValue`. The parent
|
|
|
182
188
|
/>
|
|
183
189
|
```
|
|
184
190
|
|
|
185
|
-
|
|
191
|
+
### With Formats
|
|
186
192
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
The `format` prop determines the shape of the value emitted by `onChange`. Each format includes a day component (set to `01`). The display always defaults to `YYYY/MM` unless `displayFormat` is also set.
|
|
193
|
+
Different value formats for the `onChange` event.
|
|
190
194
|
|
|
191
195
|
```tsx
|
|
192
196
|
<Stack gap={2}>
|
|
@@ -199,9 +203,9 @@ The `format` prop determines the shape of the value emitted by `onChange`. Each
|
|
|
199
203
|
</Stack>
|
|
200
204
|
```
|
|
201
205
|
|
|
202
|
-
### Display Formats
|
|
206
|
+
### With Display Formats
|
|
203
207
|
|
|
204
|
-
|
|
208
|
+
Different display formats including full month names.
|
|
205
209
|
|
|
206
210
|
```tsx
|
|
207
211
|
<Stack gap={2}>
|
|
@@ -232,21 +236,24 @@ The `displayFormat` prop controls what users see in the input field, independent
|
|
|
232
236
|
</Stack>
|
|
233
237
|
```
|
|
234
238
|
|
|
235
|
-
|
|
239
|
+
## When to Use
|
|
236
240
|
|
|
237
|
-
|
|
241
|
+
### ✅ Good Use Cases
|
|
238
242
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
243
|
+
- **Billing periods**: Monthly invoices, subscription billing cycles
|
|
244
|
+
- **Report filtering**: Monthly reports, quarterly summaries
|
|
245
|
+
- **Fiscal periods**: Fiscal months, budget allocation periods
|
|
246
|
+
- **Expiration dates**: Credit card expiry (month/year)
|
|
247
|
+
- **Historical data**: Selecting past months for data analysis
|
|
248
|
+
- **Scheduling**: Monthly recurring events
|
|
249
|
+
|
|
250
|
+
### ❌ When Not to Use
|
|
251
|
+
|
|
252
|
+
- **Specific dates**: Use DatePicker when day selection matters
|
|
253
|
+
- **Date ranges**: Use DateRangePicker or MonthRangePicker
|
|
254
|
+
- **Week selection**: Consider a custom week picker
|
|
255
|
+
- **Quarter selection**: Consider a dropdown with Q1-Q4 options
|
|
256
|
+
- **Year only**: Use a year picker or simple dropdown
|
|
250
257
|
|
|
251
258
|
## Common Use Cases
|
|
252
259
|
|
|
@@ -256,32 +263,55 @@ A controlled example with an external reset button to clear the selected value.
|
|
|
256
263
|
function BillingPeriodSelector() {
|
|
257
264
|
const [billingMonth, setBillingMonth] = useState('');
|
|
258
265
|
|
|
266
|
+
const handleGenerate = () => {
|
|
267
|
+
const [year, month] = billingMonth.split('/');
|
|
268
|
+
generateInvoice({ year: parseInt(year), month: parseInt(month) });
|
|
269
|
+
};
|
|
270
|
+
|
|
259
271
|
return (
|
|
260
|
-
<
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
272
|
+
<Stack gap={2}>
|
|
273
|
+
<MonthPicker
|
|
274
|
+
label="Billing Period"
|
|
275
|
+
value={billingMonth}
|
|
276
|
+
onChange={(e) => setBillingMonth(e.target.value)}
|
|
277
|
+
disableFuture
|
|
278
|
+
helperText="Select the month for invoice generation"
|
|
279
|
+
/>
|
|
280
|
+
<Button onClick={handleGenerate} disabled={!billingMonth}>
|
|
281
|
+
Generate Invoice
|
|
282
|
+
</Button>
|
|
283
|
+
</Stack>
|
|
267
284
|
);
|
|
268
285
|
}
|
|
269
286
|
```
|
|
270
287
|
|
|
271
|
-
### Report Month Filter
|
|
288
|
+
### Report Month Filter
|
|
272
289
|
|
|
273
290
|
```tsx
|
|
274
|
-
function
|
|
291
|
+
function ReportFilters({ onFilter }) {
|
|
275
292
|
const [reportMonth, setReportMonth] = useState('');
|
|
276
293
|
|
|
294
|
+
// Get current month as default
|
|
295
|
+
useEffect(() => {
|
|
296
|
+
const now = new Date();
|
|
297
|
+
const year = now.getFullYear();
|
|
298
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
299
|
+
setReportMonth(`${year}/${month}/01`);
|
|
300
|
+
}, []);
|
|
301
|
+
|
|
277
302
|
return (
|
|
278
|
-
<
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
303
|
+
<Stack direction="row" gap={2} alignItems="flex-end">
|
|
304
|
+
<MonthPicker
|
|
305
|
+
label="Report Month"
|
|
306
|
+
value={reportMonth}
|
|
307
|
+
onChange={(e) => setReportMonth(e.target.value)}
|
|
308
|
+
disableFuture
|
|
309
|
+
displayFormat="MMMM YYYY" // Shows "January 2024"
|
|
310
|
+
/>
|
|
311
|
+
<Button onClick={() => onFilter(reportMonth)}>
|
|
312
|
+
Generate Report
|
|
313
|
+
</Button>
|
|
314
|
+
</Stack>
|
|
285
315
|
);
|
|
286
316
|
}
|
|
287
317
|
```
|
|
@@ -292,6 +322,7 @@ function ReportFilter() {
|
|
|
292
322
|
function CreditCardExpiry() {
|
|
293
323
|
const [expiry, setExpiry] = useState('');
|
|
294
324
|
|
|
325
|
+
// Get current date for minimum
|
|
295
326
|
const today = new Date();
|
|
296
327
|
const minDate = `${today.getFullYear()}/${String(today.getMonth() + 1).padStart(2, '0')}/01`;
|
|
297
328
|
|
|
@@ -301,7 +332,7 @@ function CreditCardExpiry() {
|
|
|
301
332
|
value={expiry}
|
|
302
333
|
onChange={(e) => setExpiry(e.target.value)}
|
|
303
334
|
minDate={minDate}
|
|
304
|
-
displayFormat="MM/YYYY"
|
|
335
|
+
displayFormat="MM/YYYY" // Common credit card format
|
|
305
336
|
helperText="Enter card expiration date"
|
|
306
337
|
required
|
|
307
338
|
/>
|
|
@@ -309,59 +340,355 @@ function CreditCardExpiry() {
|
|
|
309
340
|
}
|
|
310
341
|
```
|
|
311
342
|
|
|
312
|
-
|
|
343
|
+
### Fiscal Period Selector
|
|
344
|
+
|
|
345
|
+
```tsx
|
|
346
|
+
function FiscalPeriodSelector({ fiscalYearStart = 4 }) { // April
|
|
347
|
+
const [selectedMonth, setSelectedMonth] = useState('');
|
|
348
|
+
const [fiscalPeriod, setFiscalPeriod] = useState('');
|
|
349
|
+
|
|
350
|
+
// Calculate fiscal period from selected month
|
|
351
|
+
useEffect(() => {
|
|
352
|
+
if (!selectedMonth) return;
|
|
353
|
+
|
|
354
|
+
const [year, month] = selectedMonth.split('/').map(Number);
|
|
355
|
+
const fiscalMonth = month >= fiscalYearStart
|
|
356
|
+
? month - fiscalYearStart + 1
|
|
357
|
+
: month + (12 - fiscalYearStart + 1);
|
|
358
|
+
const fiscalYear = month >= fiscalYearStart ? year : year - 1;
|
|
359
|
+
|
|
360
|
+
setFiscalPeriod(`FY${fiscalYear} P${fiscalMonth}`);
|
|
361
|
+
}, [selectedMonth, fiscalYearStart]);
|
|
362
|
+
|
|
363
|
+
return (
|
|
364
|
+
<Stack gap={2}>
|
|
365
|
+
<MonthPicker
|
|
366
|
+
label="Select Period"
|
|
367
|
+
value={selectedMonth}
|
|
368
|
+
onChange={(e) => setSelectedMonth(e.target.value)}
|
|
369
|
+
displayFormat="MMMM YYYY"
|
|
370
|
+
/>
|
|
371
|
+
{fiscalPeriod && (
|
|
372
|
+
<Typography level="body-sm">Fiscal Period: {fiscalPeriod}</Typography>
|
|
373
|
+
)}
|
|
374
|
+
</Stack>
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Monthly Budget Allocation
|
|
380
|
+
|
|
381
|
+
```tsx
|
|
382
|
+
function BudgetAllocation({ departments }) {
|
|
383
|
+
const [selectedMonth, setSelectedMonth] = useState('');
|
|
384
|
+
const [budgets, setBudgets] = useState({});
|
|
385
|
+
|
|
386
|
+
// Restrict to future months only
|
|
387
|
+
const today = new Date();
|
|
388
|
+
const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
|
|
389
|
+
const minDate = `${nextMonth.getFullYear()}/${String(nextMonth.getMonth() + 1).padStart(2, '0')}/01`;
|
|
390
|
+
|
|
391
|
+
return (
|
|
392
|
+
<form>
|
|
393
|
+
<MonthPicker
|
|
394
|
+
label="Budget Month"
|
|
395
|
+
value={selectedMonth}
|
|
396
|
+
onChange={(e) => setSelectedMonth(e.target.value)}
|
|
397
|
+
minDate={minDate}
|
|
398
|
+
displayFormat="MMMM YYYY"
|
|
399
|
+
helperText="Select month for budget allocation"
|
|
400
|
+
required
|
|
401
|
+
/>
|
|
402
|
+
|
|
403
|
+
{selectedMonth && departments.map((dept) => (
|
|
404
|
+
<CurrencyInput
|
|
405
|
+
key={dept.id}
|
|
406
|
+
label={`${dept.name} Budget`}
|
|
407
|
+
value={budgets[dept.id] || ''}
|
|
408
|
+
onChange={(value) => setBudgets(prev => ({
|
|
409
|
+
...prev,
|
|
410
|
+
[dept.id]: value
|
|
411
|
+
}))}
|
|
412
|
+
/>
|
|
413
|
+
))}
|
|
414
|
+
|
|
415
|
+
<Button type="submit">Allocate Budget</Button>
|
|
416
|
+
</form>
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
```
|
|
313
420
|
|
|
314
|
-
|
|
421
|
+
### Year-Month Comparison
|
|
315
422
|
|
|
316
423
|
```tsx
|
|
317
|
-
|
|
318
|
-
|
|
424
|
+
function MonthComparison() {
|
|
425
|
+
const [month1, setMonth1] = useState('');
|
|
426
|
+
const [month2, setMonth2] = useState('');
|
|
319
427
|
|
|
320
|
-
|
|
321
|
-
<
|
|
428
|
+
return (
|
|
429
|
+
<Stack gap={2}>
|
|
430
|
+
<Stack direction="row" gap={2}>
|
|
431
|
+
<MonthPicker
|
|
432
|
+
label="Compare From"
|
|
433
|
+
value={month1}
|
|
434
|
+
onChange={(e) => setMonth1(e.target.value)}
|
|
435
|
+
disableFuture
|
|
436
|
+
/>
|
|
437
|
+
<MonthPicker
|
|
438
|
+
label="Compare To"
|
|
439
|
+
value={month2}
|
|
440
|
+
onChange={(e) => setMonth2(e.target.value)}
|
|
441
|
+
disableFuture
|
|
442
|
+
minDate={month1} // Must be after first month
|
|
443
|
+
/>
|
|
444
|
+
</Stack>
|
|
445
|
+
<Button disabled={!month1 || !month2}>
|
|
446
|
+
Compare Months
|
|
447
|
+
</Button>
|
|
448
|
+
</Stack>
|
|
449
|
+
);
|
|
450
|
+
}
|
|
322
451
|
```
|
|
323
452
|
|
|
324
|
-
|
|
453
|
+
## Props and Customization
|
|
454
|
+
|
|
455
|
+
### Key Props
|
|
456
|
+
|
|
457
|
+
| Prop | Type | Default | Description |
|
|
458
|
+
| --------------- | ----------------------------------------------------------- | -------------- | --------------------------------------- |
|
|
459
|
+
| `value` | `string` | - | Controlled value (format: `YYYY/MM/DD`) |
|
|
460
|
+
| `defaultValue` | `string` | - | Default value for uncontrolled mode |
|
|
461
|
+
| `onChange` | `(e: { target: { name?: string; value: string } }) => void` | - | Change handler |
|
|
462
|
+
| `format` | `string` | `'YYYY/MM/DD'` | Format for `value` and `onChange` |
|
|
463
|
+
| `displayFormat` | `string` | `'YYYY/MM'` | Format displayed in the input |
|
|
464
|
+
| `label` | `string` | - | Label text |
|
|
465
|
+
| `helperText` | `string` | - | Helper text below input |
|
|
466
|
+
| `error` | `boolean` | `false` | Error state |
|
|
467
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Component size |
|
|
468
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
469
|
+
| `required` | `boolean` | `false` | Required field indicator |
|
|
470
|
+
| `minDate` | `string` | - | Minimum selectable month |
|
|
471
|
+
| `maxDate` | `string` | - | Maximum selectable month |
|
|
472
|
+
| `disableFuture` | `boolean` | `false` | Disable all future months |
|
|
473
|
+
| `disablePast` | `boolean` | `false` | Disable all past months |
|
|
474
|
+
|
|
475
|
+
### Display Format Tokens
|
|
476
|
+
|
|
477
|
+
| Token | Description | Example |
|
|
478
|
+
| ------ | --------------- | ------- |
|
|
479
|
+
| `YYYY` | 4-digit year | 2024 |
|
|
480
|
+
| `MM` | 2-digit month | 04 |
|
|
481
|
+
| `DD` | 2-digit day | 01 |
|
|
482
|
+
| `MMMM` | Full month name | January |
|
|
483
|
+
|
|
484
|
+
Common display format patterns:
|
|
485
|
+
|
|
486
|
+
- `YYYY/MM` - Default (2024/04)
|
|
487
|
+
- `YYYY-MM` - ISO-like (2024-04)
|
|
488
|
+
- `MM/YYYY` - European (04/2024)
|
|
489
|
+
- `MMMM YYYY` - Full name (April 2024)
|
|
490
|
+
- `YYYY.MM` - Period separator (2024.04)
|
|
491
|
+
|
|
492
|
+
### Value Format Understanding
|
|
325
493
|
|
|
326
494
|
```tsx
|
|
327
|
-
//
|
|
495
|
+
// MonthPicker always uses day=01 in values
|
|
496
|
+
// Even though display shows "2024/04", the value is "2024/04/01"
|
|
497
|
+
|
|
328
498
|
<MonthPicker
|
|
329
|
-
|
|
330
|
-
displayFormat="
|
|
499
|
+
value="2024/04/01" // Must include day
|
|
500
|
+
displayFormat="YYYY/MM" // Shows "2024/04"
|
|
501
|
+
onChange={(e) => {
|
|
502
|
+
console.log(e.target.value); // "2024/04/01"
|
|
503
|
+
// Extract just year and month
|
|
504
|
+
const [year, month] = e.target.value.split('/');
|
|
505
|
+
console.log(`${year}-${month}`); // "2024-04"
|
|
506
|
+
}}
|
|
331
507
|
/>
|
|
332
508
|
```
|
|
333
509
|
|
|
334
|
-
|
|
510
|
+
### Format vs DisplayFormat
|
|
335
511
|
|
|
336
512
|
```tsx
|
|
337
|
-
//
|
|
513
|
+
// format: Affects the value in onChange
|
|
514
|
+
// displayFormat: Affects what users see in the input
|
|
515
|
+
|
|
338
516
|
<MonthPicker
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
517
|
+
format="YYYY-MM-DD" // onChange returns "2024-04-01"
|
|
518
|
+
displayFormat="MMMM YYYY" // Input shows "April 2024"
|
|
519
|
+
onChange={(e) => {
|
|
520
|
+
console.log(e.target.value); // "2024-04-01"
|
|
521
|
+
}}
|
|
342
522
|
/>
|
|
343
523
|
```
|
|
344
524
|
|
|
345
|
-
|
|
525
|
+
### Controlled vs Uncontrolled
|
|
346
526
|
|
|
347
527
|
```tsx
|
|
348
|
-
//
|
|
349
|
-
<MonthPicker
|
|
528
|
+
// Uncontrolled - component manages state
|
|
529
|
+
<MonthPicker
|
|
530
|
+
defaultValue="2024/04/01"
|
|
531
|
+
onChange={(e) => console.log(e.target.value)}
|
|
532
|
+
/>
|
|
533
|
+
|
|
534
|
+
// Controlled - you manage state
|
|
535
|
+
const [month, setMonth] = useState('2024/04/01');
|
|
536
|
+
<MonthPicker
|
|
537
|
+
value={month}
|
|
538
|
+
onChange={(e) => setMonth(e.target.value)}
|
|
539
|
+
/>
|
|
350
540
|
```
|
|
351
541
|
|
|
352
|
-
|
|
542
|
+
## Accessibility
|
|
543
|
+
|
|
544
|
+
MonthPicker includes built-in accessibility features:
|
|
545
|
+
|
|
546
|
+
### ARIA Attributes
|
|
547
|
+
|
|
548
|
+
- Input has proper `role="textbox"`
|
|
549
|
+
- Calendar button has `aria-label="Toggle Calendar"`
|
|
550
|
+
- Month grid uses proper navigation roles
|
|
551
|
+
- Selected month marked with `aria-selected`
|
|
552
|
+
|
|
553
|
+
### Keyboard Navigation
|
|
554
|
+
|
|
555
|
+
- **Tab**: Move focus between input and calendar button
|
|
556
|
+
- **Enter/Space**: Open calendar when focused on button
|
|
557
|
+
- **Arrow Keys**: Navigate between months in calendar
|
|
558
|
+
- **Escape**: Close calendar popup
|
|
559
|
+
- **Enter**: Select focused month
|
|
560
|
+
|
|
561
|
+
### Screen Reader Support
|
|
562
|
+
|
|
563
|
+
```tsx
|
|
564
|
+
// Months are announced with full context
|
|
565
|
+
<button aria-label="April 2024">Apr</button>
|
|
566
|
+
|
|
567
|
+
// Navigation buttons are descriptive
|
|
568
|
+
<button aria-label="Previous Year">←</button>
|
|
569
|
+
<button aria-label="Next Year">→</button>
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### Focus Management
|
|
573
|
+
|
|
574
|
+
- Focus moves to current year's month grid when opened
|
|
575
|
+
- Focus returns to input when calendar closes
|
|
576
|
+
- Clear visual focus indicators on all interactive elements
|
|
577
|
+
|
|
578
|
+
## Best Practices
|
|
579
|
+
|
|
580
|
+
### ✅ Do
|
|
581
|
+
|
|
582
|
+
1. **Use appropriate display formats**: Match the display format to user expectations
|
|
583
|
+
|
|
584
|
+
```tsx
|
|
585
|
+
// ✅ Good: User-friendly display format
|
|
586
|
+
<MonthPicker
|
|
587
|
+
displayFormat="MMMM YYYY" // "January 2024" is clearer
|
|
588
|
+
label="Billing Month"
|
|
589
|
+
/>
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
2. **Set reasonable date limits**: Restrict months based on context
|
|
593
|
+
|
|
594
|
+
```tsx
|
|
595
|
+
// ✅ Good: Logical constraints for billing
|
|
596
|
+
<MonthPicker
|
|
597
|
+
disableFuture // Can't bill for future months
|
|
598
|
+
minDate="2020/01/01" // Company started in 2020
|
|
599
|
+
/>
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
3. **Provide context in helper text**: Guide users on selection purpose
|
|
603
|
+
|
|
604
|
+
```tsx
|
|
605
|
+
// ✅ Good: Clear guidance
|
|
606
|
+
<MonthPicker
|
|
607
|
+
label="Statement Month"
|
|
608
|
+
helperText="Select the month for your account statement"
|
|
609
|
+
/>
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
4. **Handle the day value**: Remember values include day set to 01
|
|
353
613
|
|
|
354
614
|
```tsx
|
|
355
|
-
// ✅ Good: Extract month/year
|
|
615
|
+
// ✅ Good: Extract month/year when needed
|
|
356
616
|
const handleChange = (e) => {
|
|
357
617
|
const [year, month] = e.target.value.split('/');
|
|
358
618
|
setSelectedPeriod({ year: parseInt(year), month: parseInt(month) });
|
|
359
619
|
};
|
|
360
620
|
```
|
|
361
621
|
|
|
362
|
-
|
|
622
|
+
### ❌ Don't
|
|
623
|
+
|
|
624
|
+
1. **Don't expect day-only display in values**: Values always include day
|
|
625
|
+
|
|
626
|
+
```tsx
|
|
627
|
+
// ❌ Bad: Expecting value without day
|
|
628
|
+
<MonthPicker
|
|
629
|
+
value="2024/04" // Wrong! Needs "2024/04/01"
|
|
630
|
+
/>
|
|
631
|
+
|
|
632
|
+
// ✅ Good: Full value format
|
|
633
|
+
<MonthPicker
|
|
634
|
+
value="2024/04/01"
|
|
635
|
+
/>
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
2. **Don't use inconsistent formats**: Match value format to format prop
|
|
639
|
+
|
|
640
|
+
```tsx
|
|
641
|
+
// ❌ Bad: Mismatched formats
|
|
642
|
+
<MonthPicker
|
|
643
|
+
format="YYYY/MM/DD"
|
|
644
|
+
value="04/2024" // Wrong format
|
|
645
|
+
/>
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
3. **Don't forget to handle empty states**: Validate before processing
|
|
649
|
+
|
|
650
|
+
4. **Don't use for day-level selection**: Use DatePicker when days matter
|
|
651
|
+
|
|
652
|
+
## Performance Considerations
|
|
653
|
+
|
|
654
|
+
### Memoize Handlers
|
|
655
|
+
|
|
656
|
+
When using MonthPicker in complex forms:
|
|
657
|
+
|
|
658
|
+
```tsx
|
|
659
|
+
const handleChange = useCallback((e) => {
|
|
660
|
+
setMonth(e.target.value);
|
|
661
|
+
}, []);
|
|
662
|
+
|
|
663
|
+
<MonthPicker value={month} onChange={handleChange} />
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
### Extract Month/Year Efficiently
|
|
667
|
+
|
|
668
|
+
When you need just the month and year:
|
|
669
|
+
|
|
670
|
+
```tsx
|
|
671
|
+
const { year, month } = useMemo(() => {
|
|
672
|
+
if (!selectedMonth) return { year: null, month: null };
|
|
673
|
+
const [year, month] = selectedMonth.split('/').map(Number);
|
|
674
|
+
return { year, month };
|
|
675
|
+
}, [selectedMonth]);
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
### Date Calculations
|
|
679
|
+
|
|
680
|
+
For fiscal year or period calculations:
|
|
681
|
+
|
|
682
|
+
```tsx
|
|
683
|
+
const fiscalData = useMemo(() => {
|
|
684
|
+
if (!selectedMonth) return null;
|
|
685
|
+
|
|
686
|
+
const [year, month] = selectedMonth.split('/').map(Number);
|
|
687
|
+
const fiscalYear = month >= 4 ? year : year - 1;
|
|
688
|
+
const fiscalQuarter = Math.ceil((month >= 4 ? month - 3 : month + 9) / 3);
|
|
689
|
+
|
|
690
|
+
return { fiscalYear, fiscalQuarter };
|
|
691
|
+
}, [selectedMonth]);
|
|
692
|
+
```
|
|
363
693
|
|
|
364
|
-
-
|
|
365
|
-
- **Keyboard navigation**: Use **Tab** to move focus between the input and calendar button, **Enter/Space** to open the calendar, **Arrow Keys** to navigate months, and **Escape** to close the popup.
|
|
366
|
-
- Each month button in the calendar grid has a descriptive `aria-label` (e.g., "April 2024") so screen readers announce the full context.
|
|
367
|
-
- Focus management moves focus into the calendar grid when opened and returns focus to the input when the calendar is closed.
|
|
694
|
+
MonthPicker simplifies month-level date selection for billing, reporting, and scheduling scenarios. Remember that values always include day (set to 01), and use displayFormat to show user-friendly month representations like full month names.
|