react-modular-datepicker 0.0.5-canary.0 → 0.0.6
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/README.md +54 -468
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +30 -1313
- package/dist/index.js +322 -1
- package/dist/index.js.map +1 -1
- package/dist/{useDates-CpcW1GpF.mjs → useDates-CjPuu-VA.js} +2 -2
- package/dist/{useDates-CCrn42eQ.js.map → useDates-CjPuu-VA.js.map} +1 -1
- package/dist/useDates-c17u4Qfs.cjs +2 -0
- package/dist/{useDates-CpcW1GpF.mjs.map → useDates-c17u4Qfs.cjs.map} +1 -1
- package/dist/useDates.cjs +1 -0
- package/dist/useDates.js +3 -1
- package/dist/utils.cjs +2 -0
- package/dist/{utils.mjs.map → utils.cjs.map} +1 -1
- package/dist/utils.js +168 -1
- package/dist/utils.js.map +1 -1
- package/package.json +10 -10
- package/dist/index.mjs +0 -323
- package/dist/index.mjs.map +0 -1
- package/dist/useDates-CCrn42eQ.js +0 -2
- package/dist/useDates.mjs +0 -3
- package/dist/utils.mjs +0 -169
package/README.md
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
A modular, lightweight, and type-safe React datepicker library. Whether you need a fully-featured calendar component or a headless hook to build your own UI, `react-modular-datepicker` has you covered.
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<img src="
|
|
6
|
+
<img src="./packages/docs/docs/images/hero-calendar.png" alt="react-modular-datepicker preview" width="320" />
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Features
|
|
10
10
|
|
|
11
|
-
- 🏗️ **Headless & Modular**: Use the `useDates` hook for complete control over your UI, or the `Calendar` component for a
|
|
11
|
+
- 🏗️ **Headless & Modular**: Use the `useDates` hook for complete control over your UI, or the `Calendar` component for a ready-to-use setup.
|
|
12
12
|
- 🪶 **Lightweight**: Small footprint with a pluggable adapter system (defaults to Day.js).
|
|
13
13
|
- 🛡️ **Type-safe**: Written in TypeScript for a great developer experience.
|
|
14
14
|
- 🎨 **Fully Customizable**: Style everything via Tailwind CSS or custom class names.
|
|
@@ -27,515 +27,101 @@ pnpm add react-modular-datepicker dayjs
|
|
|
27
27
|
yarn add react-modular-datepicker dayjs
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
> **Note
|
|
30
|
+
> **Note**: `dayjs` is an optional peer dependency used as the default date adapter. You can also implement a custom `DateAdapter` (e.g., using `date-fns` or Luxon).
|
|
31
31
|
|
|
32
32
|
---
|
|
33
33
|
|
|
34
|
-
##
|
|
35
|
-
|
|
36
|
-
### Imports
|
|
37
|
-
|
|
38
|
-
```tsx
|
|
39
|
-
import {
|
|
40
|
-
Calendar,
|
|
41
|
-
useDates,
|
|
42
|
-
Dates,
|
|
43
|
-
Day,
|
|
44
|
-
DayjsAdapter,
|
|
45
|
-
defaultAdapter
|
|
46
|
-
} from 'react-modular-datepicker';
|
|
47
|
-
```
|
|
34
|
+
## Quick Start
|
|
48
35
|
|
|
49
36
|
### `Calendar` Component
|
|
50
37
|
|
|
51
|
-
The primary component for a ready-to-use calendar.
|
|
52
|
-
|
|
53
|
-
| Prop | Type | Default | Description |
|
|
54
|
-
| :--- | :--- | :--- | :--- |
|
|
55
|
-
| `date` | `Date` | `new Date()` | The initial date/month to display. |
|
|
56
|
-
| `selected` | `Date \| Date[] \| Range` | `undefined` | The currently selected date(s). |
|
|
57
|
-
| `selectionMode` | `'single' \| 'range' \| 'multiple'` | `'single'` | The selection behavior. |
|
|
58
|
-
| `onChange` | `(selected: any) => void` | `undefined` | Callback triggered when the selection changes. |
|
|
59
|
-
| `minDate` | `Date` | `undefined` | The earliest selectable date. |
|
|
60
|
-
| `maxDate` | `Date` | `undefined` | The latest selectable date. |
|
|
61
|
-
| `disabledDates` | `Date[]` | `undefined` | An array of dates that should be disabled. |
|
|
62
|
-
| `monthsToDisplay` | `number` | `1` | Number of months to show simultaneously. |
|
|
63
|
-
| `firstDayOfWeek` | `number` | `0` | The first day of the week (0=Sun, 1=Mon, ...). |
|
|
64
|
-
| `showOutsideDays` | `boolean` | `false` | Whether to show days from the previous/next months. |
|
|
65
|
-
| `classNames` | `CalendarClassNames` | `undefined` | Custom classes for styling various parts of the UI. |
|
|
66
|
-
| `locale` | `string` | `undefined` | Locale for date formatting (e.g., 'fr', 'es'). |
|
|
67
|
-
| `translations` | `Partial<Translations>` | `undefined` | Custom translation strings for "Back" and "Forward". |
|
|
68
|
-
| `adapter` | `DateAdapter` | `defaultAdapter` | The date management adapter (e.g., DayjsAdapter). |
|
|
69
|
-
|
|
70
|
-
### `useDates` Hook
|
|
71
|
-
|
|
72
|
-
The logic core of the library. It handles selection, navigation, and grid generation.
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
const {
|
|
76
|
-
calendars,
|
|
77
|
-
getDateProps,
|
|
78
|
-
getBackProps,
|
|
79
|
-
getForwardProps,
|
|
80
|
-
setOffset
|
|
81
|
-
} = useDates(props: UseDatesProps);
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
**`UseDatesProps`** includes all props from `Calendar` (except `classNames`, `locale`, `translations`) plus:
|
|
85
|
-
- `onDateSelected`: `(dateObj: DateObj, event: any) => void`
|
|
86
|
-
- `onOffsetChanged`: `(newOffset: number) => void`
|
|
87
|
-
- `modifiers`: `Record<string, (date: Date) => boolean>`
|
|
88
|
-
|
|
89
|
-
### Main Types
|
|
90
|
-
|
|
91
|
-
#### `DateObj`
|
|
92
|
-
Represents a single day in the calendar grid.
|
|
93
|
-
- `date`: `Date`
|
|
94
|
-
- `selected`: `boolean`
|
|
95
|
-
- `selectable`: `boolean`
|
|
96
|
-
- `today`: `boolean`
|
|
97
|
-
- `prevMonth`/`nextMonth`: `boolean` (if outside current month)
|
|
98
|
-
- `isRangeStart`/`isRangeEnd`/`isRangeBetween`/`isRangeHovering`: `boolean`
|
|
99
|
-
- `modifiers`: `string[]`
|
|
100
|
-
|
|
101
|
-
#### `Calendar`
|
|
102
|
-
Represents a month grid.
|
|
103
|
-
- `month`: `number` (0-11)
|
|
104
|
-
- `year`: `number`
|
|
105
|
-
- `weeks`: `(DateObj | null)[][]`
|
|
106
|
-
|
|
107
|
-
#### `DateAdapter`
|
|
108
|
-
Pluggable interface for date operations:
|
|
109
|
-
```typescript
|
|
110
|
-
export interface DateAdapter<T = any> {
|
|
111
|
-
date(value?: any): T;
|
|
112
|
-
add(date: T, amount: number, unit: 'day' | 'month' | 'year'): T;
|
|
113
|
-
subtract(date: T, amount: number, unit: 'day' | 'month' | 'year'): T;
|
|
114
|
-
startOf(date: T, unit: 'day' | 'month' | 'year'): T;
|
|
115
|
-
endOf(date: T, unit: 'day' | 'month' | 'year'): T;
|
|
116
|
-
isBefore(date: T, comparison: T, unit?: 'day' | 'month' | 'year'): boolean;
|
|
117
|
-
isAfter(date: T, comparison: T, unit?: 'day' | 'month' | 'year'): boolean;
|
|
118
|
-
isSame(date: T, comparison: T, unit?: 'day' | 'month' | 'year'): boolean;
|
|
119
|
-
set(date: T, unit: 'day' | 'month' | 'year', value: number): T;
|
|
120
|
-
get(date: T, unit: 'day' | 'month' | 'year'): number;
|
|
121
|
-
format(date: T, formatStr: string, locale?: string): string;
|
|
122
|
-
getDaysInMonth(date: T): number;
|
|
123
|
-
toDate(date: T): Date;
|
|
124
|
-
diff(date: T, comparison: T, unit: 'month' | 'year'): number;
|
|
125
|
-
getMonths(locale?: string): string[];
|
|
126
|
-
getWeekdays(locale?: string): string[];
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## Recipes
|
|
133
|
-
|
|
134
|
-
<details>
|
|
135
|
-
<summary><b>Basic Selection</b></summary>
|
|
136
|
-
A simple single-date selection calendar.
|
|
137
|
-
|
|
138
38
|
```tsx
|
|
139
39
|
import { Calendar } from 'react-modular-datepicker';
|
|
140
40
|
import 'react-modular-datepicker/dist/index.css';
|
|
141
41
|
|
|
142
|
-
function
|
|
42
|
+
export function App() {
|
|
143
43
|
return <Calendar onChange={(date) => console.log(date)} />;
|
|
144
44
|
}
|
|
145
45
|
```
|
|
146
46
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
<img src="https://6pcvnpdosyar2uqr.public.blob.vercel-storage.com/docs/images/basic.png" alt="Basic Selection" width="500" />
|
|
150
|
-
|
|
151
|
-
</details>
|
|
152
|
-
|
|
153
|
-
<details>
|
|
154
|
-
<summary><b>Range Selection</b></summary>
|
|
155
|
-
Select a start and end date with a hover preview.
|
|
156
|
-
|
|
157
|
-
```tsx
|
|
158
|
-
<Calendar
|
|
159
|
-
selectionMode="range"
|
|
160
|
-
monthsToDisplay={2}
|
|
161
|
-
onChange={(range) => console.log(range)}
|
|
162
|
-
/>
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
<br />
|
|
166
|
-
|
|
167
|
-
<img src="https://6pcvnpdosyar2uqr.public.blob.vercel-storage.com/docs/images/range.png" alt="Range Selection" width="500" />
|
|
168
|
-
|
|
169
|
-
</details>
|
|
170
|
-
|
|
171
|
-
<details>
|
|
172
|
-
<summary><b>Multiple Selection</b></summary>
|
|
173
|
-
Select as many dates as you want.
|
|
174
|
-
|
|
175
|
-
```tsx
|
|
176
|
-
<Calendar
|
|
177
|
-
selectionMode="multiple"
|
|
178
|
-
onChange={(dates) => console.log(dates)}
|
|
179
|
-
/>
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
<br />
|
|
183
|
-
|
|
184
|
-
<img src="https://6pcvnpdosyar2uqr.public.blob.vercel-storage.com/docs/images/multiple.png" alt="Multiple Selection" width="500" />
|
|
185
|
-
|
|
186
|
-
</details>
|
|
187
|
-
|
|
188
|
-
<details>
|
|
189
|
-
<summary><b>Headless Usage</b></summary>
|
|
190
|
-
Complete freedom over your UI using the `useDates` hook.
|
|
191
|
-
|
|
192
|
-
```tsx
|
|
193
|
-
const { calendars, getDateProps } = useDates({ selectionMode: 'single' });
|
|
194
|
-
|
|
195
|
-
return (
|
|
196
|
-
<div className="grid grid-cols-7">
|
|
197
|
-
{calendars[0].weeks.flat().map((dateObj, i) => (
|
|
198
|
-
dateObj ? (
|
|
199
|
-
<button key={i} {...getDateProps({ dateObj })}>
|
|
200
|
-
{dateObj.date.getDate()}
|
|
201
|
-
</button>
|
|
202
|
-
) : <div key={i} />
|
|
203
|
-
))}
|
|
204
|
-
</div>
|
|
205
|
-
);
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
<br />
|
|
209
|
-
|
|
210
|
-
<img src="https://6pcvnpdosyar2uqr.public.blob.vercel-storage.com/docs/images/headless.png" alt="Headless Usage" width="500" />
|
|
211
|
-
|
|
212
|
-
</details>
|
|
213
|
-
|
|
214
|
-
<details>
|
|
215
|
-
<summary><b>Custom Styling</b></summary>
|
|
216
|
-
Easily customize the look and feel using the `classNames` prop.
|
|
217
|
-
|
|
218
|
-
```tsx
|
|
219
|
-
<Calendar
|
|
220
|
-
classNames={{
|
|
221
|
-
root: 'bg-stone-50 p-6 rounded-2xl border border-stone-200 shadow-lg text-stone-800',
|
|
222
|
-
monthYearLabel: 'font-serif text-stone-900 text-lg font-bold',
|
|
223
|
-
day: {
|
|
224
|
-
unselected: 'bg-white border border-stone-200 hover:bg-amber-100/50 text-stone-800',
|
|
225
|
-
selected: 'bg-amber-800 text-amber-50 font-bold',
|
|
226
|
-
}
|
|
227
|
-
}}
|
|
228
|
-
/>
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
<br />
|
|
232
|
-
|
|
233
|
-
<img src="https://6pcvnpdosyar2uqr.public.blob.vercel-storage.com/docs/images/custom-styling.png" alt="Custom Styling" width="500" />
|
|
234
|
-
|
|
235
|
-
</details>
|
|
236
|
-
|
|
237
|
-
<details>
|
|
238
|
-
<summary><b>Localization & i18n</b></summary>
|
|
239
|
-
Easily customize language, month/weekday labels, and the first day of week.
|
|
47
|
+
### `useDates` Hook (Headless)
|
|
240
48
|
|
|
241
49
|
```tsx
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
<
|
|
254
|
-
|
|
255
|
-
</
|
|
256
|
-
|
|
257
|
-
<
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
<div className="absolute top-full left-0 mt-2 z-50 invisible group-focus-within:visible">
|
|
272
|
-
<Calendar
|
|
273
|
-
selected={date}
|
|
274
|
-
onChange={(d) => setDate(d as Date)}
|
|
275
|
-
/>
|
|
276
|
-
</div>
|
|
277
|
-
</div>
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
<br />
|
|
281
|
-
|
|
282
|
-
<img src="https://6pcvnpdosyar2uqr.public.blob.vercel-storage.com/docs/images/form-integration.png" alt="Form Integration" width="500" />
|
|
283
|
-
|
|
284
|
-
</details>
|
|
285
|
-
|
|
286
|
-
<details>
|
|
287
|
-
<summary><b>Custom Header & Footer</b></summary>
|
|
288
|
-
Customize header, footer, and day tooltips.
|
|
289
|
-
|
|
290
|
-
```tsx
|
|
291
|
-
<Calendar
|
|
292
|
-
selected={selected}
|
|
293
|
-
onChange={(val) => setSelected(val as Date)}
|
|
294
|
-
monthsToDisplay={2}
|
|
295
|
-
header={<div className="p-2 bg-blue-100 text-blue-800 font-bold text-center rounded-t-lg">My Header</div>}
|
|
296
|
-
footer={<div className="p-2 bg-gray-100 text-gray-600 text-sm text-center rounded-b-lg border-t">My Custom Footer</div>}
|
|
297
|
-
renderDayTooltip={(dateObj) => (
|
|
298
|
-
dateObj.date.getDate() === 15 ? 'Middle of the month!' : null
|
|
299
|
-
)}
|
|
300
|
-
/>
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
<br />
|
|
304
|
-
|
|
305
|
-
<img src="https://6pcvnpdosyar2uqr.public.blob.vercel-storage.com/docs/images/custom-header-footer.png" alt="Custom Header & Footer" width="500" />
|
|
306
|
-
|
|
307
|
-
</details>
|
|
308
|
-
|
|
309
|
-
<details>
|
|
310
|
-
<summary><b>Min, Max & Disabled Dates</b></summary>
|
|
311
|
-
Restrict date selection with min/max bounds or specific disabled dates.
|
|
312
|
-
|
|
313
|
-
```tsx
|
|
314
|
-
<Calendar
|
|
315
|
-
selected={selected}
|
|
316
|
-
onChange={(val) => setSelected(val as Date)}
|
|
317
|
-
minDate={minDate}
|
|
318
|
-
maxDate={maxDate}
|
|
319
|
-
disabledDates={[disabledDate1, disabledDate2]}
|
|
320
|
-
/>
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
<br />
|
|
324
|
-
|
|
325
|
-
<img src="https://6pcvnpdosyar2uqr.public.blob.vercel-storage.com/docs/images/min-max-disabled.png" alt="Min, Max & Disabled Dates" width="500" />
|
|
326
|
-
|
|
327
|
-
</details>
|
|
328
|
-
|
|
329
|
-
<details>
|
|
330
|
-
<summary><b>Yearly View</b></summary>
|
|
331
|
-
Display an entire year at once by customizing the grid and `monthsToDisplay`.
|
|
332
|
-
|
|
333
|
-
```tsx
|
|
334
|
-
<Calendar
|
|
335
|
-
selected={selected}
|
|
336
|
-
onChange={(val) => setSelected(val as Date)}
|
|
337
|
-
monthsToDisplay={12}
|
|
338
|
-
classNames={{
|
|
339
|
-
calendarsContainer: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"
|
|
340
|
-
}}
|
|
341
|
-
/>
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
<br />
|
|
345
|
-
|
|
346
|
-
<img src="https://6pcvnpdosyar2uqr.public.blob.vercel-storage.com/docs/images/yearly.png" alt="Yearly View" width="500" />
|
|
347
|
-
|
|
348
|
-
</details>
|
|
349
|
-
|
|
350
|
-
<details>
|
|
351
|
-
<summary><b>Availability & Async Data</b></summary>
|
|
352
|
-
Fetch and display availability dynamically when the month changes.
|
|
353
|
-
|
|
354
|
-
```tsx
|
|
355
|
-
<Calendar
|
|
356
|
-
selected={selected}
|
|
357
|
-
onChange={(val) => setSelected(val as Date)}
|
|
358
|
-
onMonthChange={(date) => fetchAvailabilities(date)}
|
|
359
|
-
modifiers={{
|
|
360
|
-
available: (date) => availabilities[date.toDateString()] === true,
|
|
361
|
-
unavailable: (date) => availabilities[date.toDateString()] === false,
|
|
362
|
-
}}
|
|
363
|
-
disabledDates={
|
|
364
|
-
Object.keys(availabilities)
|
|
365
|
-
.filter(key => !availabilities[key])
|
|
366
|
-
.map(key => new Date(key))
|
|
367
|
-
}
|
|
368
|
-
classNames={{
|
|
369
|
-
day: {
|
|
370
|
-
available: "bg-green-100 text-green-800 hover:bg-green-200",
|
|
371
|
-
unavailable: "bg-red-50 text-red-300 line-through cursor-not-allowed",
|
|
372
|
-
}
|
|
373
|
-
}}
|
|
374
|
-
/>
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
<br />
|
|
378
|
-
|
|
379
|
-
<img src="https://6pcvnpdosyar2uqr.public.blob.vercel-storage.com/docs/images/availability.png" alt="Availability Demo" width="500" />
|
|
380
|
-
|
|
381
|
-
</details>
|
|
382
|
-
|
|
383
|
-
<details>
|
|
384
|
-
<summary><b>Full-Featured / Events Calendar (Google Calendar style)</b></summary>
|
|
385
|
-
Build a full-page events calendar using the `useDates` hook.
|
|
386
|
-
|
|
387
|
-
```tsx
|
|
388
|
-
const { calendars, getBackProps, getForwardProps, getDateProps } = useDates({
|
|
389
|
-
showOutsideDays: true,
|
|
390
|
-
selected: selectedDate,
|
|
391
|
-
onChange: (d) => setSelectedDate(d as Date),
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
// Render custom full-grid layout with event badges...
|
|
50
|
+
import { useDates } from 'react-modular-datepicker';
|
|
51
|
+
|
|
52
|
+
export function CustomCalendar() {
|
|
53
|
+
const { calendars, getDateProps, getBackProps, getForwardProps } = useDates({
|
|
54
|
+
selectionMode: 'single',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const calendar = calendars[0];
|
|
58
|
+
if (!calendar) return null;
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div>
|
|
62
|
+
<div className="flex justify-between">
|
|
63
|
+
<button {...getBackProps({ calendars })}>Prev</button>
|
|
64
|
+
<span>{calendar.year} - {calendar.month + 1}</span>
|
|
65
|
+
<button {...getForwardProps({ calendars })}>Next</button>
|
|
66
|
+
</div>
|
|
67
|
+
<div className="grid grid-cols-7">
|
|
68
|
+
{calendar.weeks.flat().map((dateObj, i) => (
|
|
69
|
+
dateObj ? (
|
|
70
|
+
<button key={i} {...getDateProps({ dateObj })}>
|
|
71
|
+
{dateObj.date.getDate()}
|
|
72
|
+
</button>
|
|
73
|
+
) : <div key={i} />
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
395
79
|
```
|
|
396
80
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
<img src="https://6pcvnpdosyar2uqr.public.blob.vercel-storage.com/docs/images/events.png" alt="Events Calendar" width="500" />
|
|
400
|
-
|
|
401
|
-
</details>
|
|
402
|
-
|
|
403
|
-
<details>
|
|
404
|
-
<summary><b>Custom Date Adapter (e.g. date-fns)</b></summary>
|
|
405
|
-
Replace the default `DayjsAdapter` with a custom adapter using `date-fns` (or any date library).
|
|
81
|
+
---
|
|
406
82
|
|
|
407
|
-
|
|
408
|
-
import { Calendar, DateAdapter } from 'react-modular-datepicker';
|
|
409
|
-
import {
|
|
410
|
-
addDays, addMonths, addYears,
|
|
411
|
-
subDays, subMonths, subYears,
|
|
412
|
-
startOfDay, startOfMonth, startOfYear,
|
|
413
|
-
endOfDay, endOfMonth, endOfYear,
|
|
414
|
-
isBefore, isAfter, isSameDay, isSameMonth, isSameYear,
|
|
415
|
-
setDate, setMonth, setYear,
|
|
416
|
-
getDate, getMonth, getYear,
|
|
417
|
-
format, getDaysInMonth,
|
|
418
|
-
differenceInCalendarMonths, differenceInCalendarYears
|
|
419
|
-
} from 'date-fns';
|
|
420
|
-
|
|
421
|
-
class DateFnsAdapter implements DateAdapter<Date> {
|
|
422
|
-
date(value?: any): Date { return value ? new Date(value) : new Date(); }
|
|
423
|
-
add(date: Date, amount: number, unit: 'day' | 'month' | 'year'): Date {
|
|
424
|
-
if (unit === 'day') return addDays(date, amount);
|
|
425
|
-
if (unit === 'month') return addMonths(date, amount);
|
|
426
|
-
return addYears(date, amount);
|
|
427
|
-
}
|
|
428
|
-
subtract(date: Date, amount: number, unit: 'day' | 'month' | 'year'): Date {
|
|
429
|
-
if (unit === 'day') return subDays(date, amount);
|
|
430
|
-
if (unit === 'month') return subMonths(date, amount);
|
|
431
|
-
return subYears(date, amount);
|
|
432
|
-
}
|
|
433
|
-
startOf(date: Date, unit: 'day' | 'month' | 'year'): Date {
|
|
434
|
-
if (unit === 'day') return startOfDay(date);
|
|
435
|
-
if (unit === 'month') return startOfMonth(date);
|
|
436
|
-
return startOfYear(date);
|
|
437
|
-
}
|
|
438
|
-
endOf(date: Date, unit: 'day' | 'month' | 'year'): Date {
|
|
439
|
-
if (unit === 'day') return endOfDay(date);
|
|
440
|
-
if (unit === 'month') return endOfMonth(date);
|
|
441
|
-
return endOfYear(date);
|
|
442
|
-
}
|
|
443
|
-
isBefore(date: Date, comparison: Date, unit?: 'day' | 'month' | 'year'): boolean {
|
|
444
|
-
if (unit === 'day') return startOfDay(date) < startOfDay(comparison);
|
|
445
|
-
if (unit === 'month') return startOfMonth(date) < startOfMonth(comparison);
|
|
446
|
-
if (unit === 'year') return startOfYear(date) < startOfYear(comparison);
|
|
447
|
-
return date < comparison;
|
|
448
|
-
}
|
|
449
|
-
isAfter(date: Date, comparison: Date, unit?: 'day' | 'month' | 'year'): boolean {
|
|
450
|
-
if (unit === 'day') return startOfDay(date) > startOfDay(comparison);
|
|
451
|
-
if (unit === 'month') return startOfMonth(date) > startOfMonth(comparison);
|
|
452
|
-
if (unit === 'year') return startOfYear(date) > startOfYear(comparison);
|
|
453
|
-
return date > comparison;
|
|
454
|
-
}
|
|
455
|
-
isSame(date: Date, comparison: Date, unit?: 'day' | 'month' | 'year'): boolean {
|
|
456
|
-
if (unit === 'day') return isSameDay(date, comparison);
|
|
457
|
-
if (unit === 'month') return isSameMonth(date, comparison);
|
|
458
|
-
if (unit === 'year') return isSameYear(date, comparison);
|
|
459
|
-
return date.getTime() === comparison.getTime();
|
|
460
|
-
}
|
|
461
|
-
set(date: Date, unit: 'day' | 'month' | 'year', value: number): Date {
|
|
462
|
-
if (unit === 'day') return setDate(date, value);
|
|
463
|
-
if (unit === 'month') return setMonth(date, value);
|
|
464
|
-
return setYear(date, value);
|
|
465
|
-
}
|
|
466
|
-
get(date: Date, unit: 'day' | 'month' | 'year'): number {
|
|
467
|
-
if (unit === 'day') return getDate(date);
|
|
468
|
-
if (unit === 'month') return getMonth(date);
|
|
469
|
-
return getYear(date);
|
|
470
|
-
}
|
|
471
|
-
format(date: Date, formatStr: string): string {
|
|
472
|
-
const f = formatStr.replace(/YYYY/g, 'yyyy').replace(/YY/g, 'yy').replace(/D/g, 'd');
|
|
473
|
-
return format(date, f);
|
|
474
|
-
}
|
|
475
|
-
getDaysInMonth(date: Date): number { return getDaysInMonth(date); }
|
|
476
|
-
toDate(date: Date): Date { return date; }
|
|
477
|
-
diff(date: Date, comparison: Date, unit: 'month' | 'year'): number {
|
|
478
|
-
if (unit === 'month') return differenceInCalendarMonths(date, comparison);
|
|
479
|
-
return differenceInCalendarYears(date, comparison);
|
|
480
|
-
}
|
|
481
|
-
getMonths(): string[] {
|
|
482
|
-
return ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
|
483
|
-
}
|
|
484
|
-
getWeekdays(): string[] {
|
|
485
|
-
return ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'];
|
|
486
|
-
}
|
|
487
|
-
}
|
|
83
|
+
## Documentation & Recipes
|
|
488
84
|
|
|
489
|
-
|
|
85
|
+
For full API reference, recipes, and interactive demos, check out the documentation package in `packages/docs`.
|
|
490
86
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
87
|
+
To run the documentation site locally:
|
|
88
|
+
```bash
|
|
89
|
+
pnpm run dev:docs
|
|
494
90
|
```
|
|
495
91
|
|
|
496
|
-
</details>
|
|
497
|
-
|
|
498
92
|
---
|
|
499
93
|
|
|
500
|
-
##
|
|
501
|
-
|
|
502
|
-
We welcome contributions! Here’s how you can get started:
|
|
94
|
+
## Development
|
|
503
95
|
|
|
504
96
|
### Installation
|
|
505
97
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
pnpm install
|
|
510
|
-
```
|
|
98
|
+
```bash
|
|
99
|
+
pnpm install
|
|
100
|
+
```
|
|
511
101
|
|
|
512
|
-
### Development
|
|
102
|
+
### Development Server
|
|
513
103
|
|
|
514
|
-
Start
|
|
104
|
+
Start watching for library changes and run the documentation app:
|
|
515
105
|
```bash
|
|
516
106
|
pnpm run dev
|
|
517
107
|
```
|
|
518
|
-
This runs `vite` in watch mode for the library and starts the Next.js example app at `http://localhost:3000`.
|
|
519
108
|
|
|
520
109
|
### Testing
|
|
521
110
|
|
|
522
|
-
Run end-to-end tests
|
|
111
|
+
Run unit and end-to-end tests:
|
|
523
112
|
```bash
|
|
524
|
-
pnpm run test
|
|
113
|
+
pnpm run test
|
|
525
114
|
```
|
|
526
115
|
|
|
527
|
-
### Documentation & Examples
|
|
528
|
-
|
|
529
|
-
- To update documentation, edit the `README.md`.
|
|
530
|
-
- To add or modify examples, check the `examples/` directory.
|
|
531
|
-
|
|
532
116
|
### Building
|
|
533
117
|
|
|
534
|
-
Build the package
|
|
118
|
+
Build the package and documentation:
|
|
535
119
|
```bash
|
|
536
120
|
pnpm run build
|
|
537
121
|
```
|
|
538
122
|
|
|
539
123
|
---
|
|
540
124
|
|
|
541
|
-
License
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./useDates-c17u4Qfs.cjs"),t=require("./utils.cjs");let n=require("react");n=e.i(n,1);let r=require("react/jsx-runtime");var i=({dateObj:e,getDateProps:t,classNames:i,tooltip:a})=>{let[o,s]=(0,n.useState)(!1);if(!e)return(0,r.jsx)(`div`,{className:`aspect-square bg-white`});let{date:c,selected:l,selectable:u,isRangeStart:d,isRangeEnd:f,isRangeBetween:p,isRangeHovering:m,isRangeActive:h}=e,g=i.day,_=`rounded-full`;_=d&&f?`${g.selected} rounded-full`:d?`${g.rangeStart} ${h?`rounded-l-full rounded-r-none`:`rounded-full`}`:f?`${g.rangeEnd} ${h?`rounded-r-full rounded-l-none`:`rounded-full`}`:p?g.rangeBetween||``:m?g.rangeHovering||``:l?g.selected||``:u?g.unselected||``:g.disabled||``;let v=[g.day,_,...(e.modifiers||[]).map(e=>g[e]||e)].filter(Boolean).join(` `);return(0,r.jsxs)(`button`,{...t({dateObj:e,onMouseEnter:()=>s(!0),onMouseLeave:()=>s(!1)}),className:v,children:[c.getDate(),a&&o&&(0,r.jsx)(`div`,{className:`absolute bottom-full left-1/2 -translate-x-1/2 mb-2 hidden group-hover:block z-10`,children:(0,r.jsxs)(`div`,{className:`bg-gray-800 text-white text-xs py-1 px-2 rounded whitespace-nowrap shadow-lg`,children:[a,(0,r.jsx)(`div`,{className:`absolute top-full left-1/2 -translate-x-1/2 border-8 border-transparent border-t-gray-800`})]})})]})};function a(t=e.r,n){return{months:t.getMonths(n),weekdays:t.getWeekdays(n),back:`Previous month`,forward:`Next month`}}function o(t=e.r,n,r){return{...a(t,n),...r}}var s=()=>(0,r.jsx)(`svg`,{width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:(0,r.jsx)(`polyline`,{points:`15 18 9 12 15 6`})}),c=()=>(0,r.jsx)(`svg`,{width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:(0,r.jsx)(`polyline`,{points:`9 18 15 12 9 6`})}),l=({calendars:e,getBackProps:t,getForwardProps:n,setView:i,monthNames:a,t:o,classNames:l,slideDirection:u,currentView:d})=>(0,r.jsxs)(`div`,{className:l.header,children:[(0,r.jsx)(`button`,{...t({calendars:e}),onMouseDown:e=>e.preventDefault(),className:`${l.navButton} ${d&&d!==`days`?`invisible pointer-events-none`:``}`,"aria-label":o.back,children:(0,r.jsx)(s,{})}),(0,r.jsx)(`div`,{className:l.monthYearContainer,children:e.length===1?(0,r.jsxs)(`div`,{className:`${l.monthYearLabel} ${u===`left`?`animate-slide-in-left`:u===`right`?`animate-slide-in-right`:``}`,children:[(0,r.jsx)(`button`,{onMouseDown:e=>e.preventDefault(),onClick:()=>i(d===`months`?`days`:`months`),className:l.monthYearButton,children:a[e[0].month]}),(0,r.jsx)(`button`,{onMouseDown:e=>e.preventDefault(),onClick:()=>i(d===`years`?`days`:`years`),className:l.monthYearButton,children:e[0].year})]},`${e[0].month}-${e[0].year}`):(0,r.jsx)(`div`,{className:`${l.monthYearLabel} ${u===`left`?`animate-slide-in-left`:u===`right`?`animate-slide-in-right`:``}`,children:(()=>{let t=e[0],n=e[e.length-1];return e.length===12&&t.month===0&&n.month===11&&t.year===n.year?(0,r.jsx)(`span`,{children:t.year}):t.year===n.year?(0,r.jsxs)(`span`,{children:[a[t.month],` - `,a[n.month],` `,t.year]}):(0,r.jsxs)(`span`,{children:[a[t.month],` `,t.year,` - `,a[n.month],` `,n.year]})})()})}),(0,r.jsx)(`button`,{...n({calendars:e}),onMouseDown:e=>e.preventDefault(),className:`${l.navButton} ${d&&d!==`days`?`invisible pointer-events-none`:``}`,"aria-label":o.forward,children:(0,r.jsx)(c,{})})]}),u={root:`w-fit flex flex-col p-4 bg-white rounded-lg shadow-lg`,header:`flex items-center justify-between border-b-2 border-brand-gray-light/40 pb-2`,calendarsContainer:`flex flex-wrap gap-4 mt-3`,calendarContainer:`flex-1 min-w-[280px] max-w-full`,weekdayGrid:`grid grid-cols-7 gap-1 mb-1 border-b border-brand-gray-light/40`,weekday:`text-center text-xs text-gray-400 py-2`,daysGrid:`grid grid-cols-7 gap-px bg-brand-gray-light/20`,footer:`mt-4`,navButton:`p-2 text-brand-text hover:bg-gray-100 rounded-full transition-colors cursor-pointer disabled:opacity-30 disabled:pointer-events-none`,monthYearContainer:`flex gap-8`,monthYearLabel:`flex gap-1 items-center font-semibold text-brand-text`,monthYearButton:`hover:bg-gray-100 px-2 py-1 rounded cursor-pointer`,day:{day:`aspect-square flex items-center justify-center text-sm font-medium transition-all relative group cursor-pointer p-px`,selected:`bg-brand-gold text-white rounded-full`,unselected:`bg-white hover:bg-brand-gray-light text-brand-text`,disabled:`bg-white text-gray-300 cursor-not-allowed`,rangeStart:`bg-brand-gold text-white`,rangeEnd:`bg-brand-gold text-white`,rangeBetween:`bg-brand-gold/25 text-brand-text`,rangeHovering:`bg-brand-gold/25 text-brand-text`},monthsGrid:`grid grid-cols-3 gap-2 animate-slide-in-top`,monthButton:`py-3 rounded-lg hover:bg-brand-gray-light transition-colors cursor-pointer disabled:opacity-30 disabled:pointer-events-none`,monthButtonSelected:`bg-brand-gold text-white`,monthButtonUnselected:`text-brand-text`,yearsGrid:`grid grid-cols-3 gap-2 max-h-[300px] overflow-y-auto pr-2 animate-slide-in-top`,yearButton:`py-3 rounded-lg hover:bg-brand-gray-light transition-colors cursor-pointer disabled:opacity-30 disabled:pointer-events-none`,yearButtonSelected:`bg-brand-gold text-white`,yearButtonUnselected:`text-brand-text`},d=e=>{let t={...u};if(!e)return t;for(let n in e){let r=n;r===`day`&&e.day?t.day={...u.day,...e.day}:e[r]&&(t[r]=e[r])}return t},f=({year:e,month:t,monthNames:n,minDate:i,maxDate:a,onMonthSelect:o,classNames:s})=>{let c=t=>!!(i&&e===i.getFullYear()&&t<i.getMonth()||a&&e===a.getFullYear()&&t>a.getMonth());return(0,r.jsx)(`div`,{className:s.monthsGrid,children:n.map((e,n)=>{let i=c(n);return(0,r.jsx)(`button`,{onMouseDown:e=>e.preventDefault(),onClick:()=>!i&&o(n),className:`${s.monthButton} ${n===t?s.monthButtonSelected:s.monthButtonUnselected}`,disabled:i,children:e},e)})})},p=({year:e,minDate:t,maxDate:i,adapter:a,onYearSelect:o,classNames:s})=>{let c=(0,n.useRef)(null);(0,n.useEffect)(()=>{if(c.current){let e=c.current.querySelector(`[data-selected="true"]`);e&&e.scrollIntoView({block:`center`})}},[]);let l=t?a.get(a.date(t),`year`):e-50,u=i?a.get(a.date(i),`year`):e+50,d=[];for(let e=l;e<=u;e++)d.push(e);return(0,r.jsx)(`div`,{ref:c,className:s.yearsGrid,children:d.map(t=>(0,r.jsx)(`button`,{"data-selected":t===e,onMouseDown:e=>e.preventDefault(),onClick:()=>o(t),className:`${s.yearButton} ${t===e?s.yearButtonSelected:s.yearButtonUnselected}`,children:t},t))})},m=t=>{let{classNames:a,locale:s,translations:c,adapter:u=e.r,firstDayOfWeek:m=0,header:h,footer:g,renderDayTooltip:_,...v}=t,y=d(a),[b,x]=(0,n.useState)(`days`),[S,C]=(0,n.useState)(null),w=o(u,s,c),T=w.weekdays,E=w.months,{calendars:D,getBackProps:O,getForwardProps:k,getDateProps:A,setOffset:j}=e.t({...v,adapter:u,firstDayOfWeek:m}),M=[...T.slice(m),...T.slice(0,m)],{month:N,year:P}=D[0],F=e=>{let n=u.set(u.set(u.date(t.date||new Date),`year`,P),`month`,N),r=u.set(n,`month`,e),i=u.startOf(u.date(t.date||new Date),`month`),a=u.diff(r,i,`month`);C(null),j(a),x(`days`)},I=e=>{let n=u.set(u.set(u.date(t.date||new Date),`year`,P),`month`,N),r=u.set(n,`year`,e),i=u.startOf(u.date(t.date||new Date),`month`),a=u.diff(r,i,`month`);C(null),j(a),x(`days`)},L=e=>{let t=O(e);return{...t,onClick:e=>{C(`left`),t.onClick?.(e)}}},R=e=>{let t=k(e);return{...t,onClick:e=>{C(`right`),t.onClick?.(e)}}};return(0,r.jsxs)(`div`,{className:`rmdp ${y.root}`,children:[typeof h==`function`?h({calendars:D,getBackProps:L,getForwardProps:R,setView:x,monthNames:E,t:w,slideDirection:S,currentView:b}):h||(0,r.jsx)(l,{calendars:D,getBackProps:L,getForwardProps:R,setView:x,monthNames:E,t:w,classNames:y,slideDirection:S,currentView:b}),(0,r.jsxs)(`div`,{className:y.calendarsContainer,children:[b===`months`&&D.map(e=>(0,r.jsx)(`div`,{className:y.calendarContainer,children:(0,r.jsx)(f,{year:e.year,month:e.month,monthNames:E,minDate:t.minDate,maxDate:t.maxDate,onMonthSelect:F,classNames:y})},`months-${e.year}`)),b===`years`&&D.map(e=>(0,r.jsx)(`div`,{className:y.calendarContainer,children:(0,r.jsx)(p,{year:e.year,minDate:t.minDate,maxDate:t.maxDate,adapter:u,onYearSelect:I,classNames:y})},`years-${e.year}`)),b===`days`&&D.map(e=>(0,r.jsxs)(`div`,{className:y.calendarContainer,children:[D.length>1&&(0,r.jsxs)(`button`,{type:`button`,"aria-label":E[e.month],className:`block w-full text-center font-semibold mb-2 text-brand-text`,children:[E[e.month],` `,D[0].year===D[D.length-1].year?``:e.year]}),(0,r.jsx)(`div`,{className:y.weekdayGrid,children:M.map(e=>(0,r.jsx)(`div`,{className:y.weekday,children:e},e))}),(0,r.jsx)(`div`,{className:`${y.daysGrid} ${S===`left`?`animate-slide-in-left`:S===`right`?`animate-slide-in-right`:``}`,children:e.weeks.map((e,t)=>e.map((e,n)=>(0,r.jsx)(i,{dateObj:e,getDateProps:A,classNames:y,tooltip:e&&_?.(e)},`${t}-${n}`)))})]},`${e.month}-${e.year}`))]}),g&&(0,r.jsx)(`div`,{className:y.footer,children:g})]})};exports.Calendar=m,exports.CalendarHeader=l,exports.ChevronLeftIcon=s,exports.ChevronRightIcon=c,exports.Day=i,exports.DayjsAdapter=e.n,exports.MonthSelection=f,exports.YearSelection=p,exports.addMonth=t.addMonth,exports.composeEventHandlers=t.composeEventHandlers,exports.defaultAdapter=e.r,exports.defaultClassNames=u,exports.getCalendars=t.getCalendars,exports.getDefaults=a,exports.getTranslations=o,exports.isBackDisabled=t.isBackDisabled,exports.isForwardDisabled=t.isForwardDisabled,exports.mergeClassNames=d,exports.subtractMonth=t.subtractMonth,exports.useDates=e.t;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|