@mngh/jalali-datepicker 1.1.7 → 1.2.0
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 +636 -630
- package/dist/components/DayCell.d.ts.map +1 -1
- package/dist/components/Header.d.ts +2 -0
- package/dist/components/Header.d.ts.map +1 -1
- package/dist/components/JalaliDatePicker.d.ts +2 -1
- package/dist/components/JalaliDatePicker.d.ts.map +1 -1
- package/dist/components/MonthYearPicker.d.ts +4 -1
- package/dist/components/MonthYearPicker.d.ts.map +1 -1
- package/dist/components/Weekdays.d.ts.map +1 -1
- package/dist/components/dual-calendar/DualMonthCalendar.d.ts.map +1 -1
- package/dist/components/footer/CalendarFooter.d.ts.map +1 -1
- package/dist/components/masked-input/MaskedDateInput.d.ts.map +1 -1
- package/dist/core/date-availability.d.ts +10 -0
- package/dist/core/date-availability.d.ts.map +1 -0
- package/dist/hooks/useJalaliDatePicker.d.ts +1 -0
- package/dist/hooks/useJalaliDatePicker.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +524 -335
- package/dist/index.mjs.map +1 -1
- package/dist/plugins/time-picker/TimePicker.d.ts.map +1 -1
- package/dist/theme/style-slots.d.ts +6 -0
- package/dist/theme/style-slots.d.ts.map +1 -1
- package/package.json +78 -78
package/README.md
CHANGED
|
@@ -1,630 +1,636 @@
|
|
|
1
|
-
# @mngh/jalali-datepicker
|
|
2
|
-
|
|
3
|
-
A modern, headless-friendly Jalali (Persian/Shamsi) Date & Time Picker for React — zero date-library runtime dependencies, fully typed with native `Date` objects, WAI-ARIA accessible, and themeable with Tailwind CSS or CSS variables.
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/@mngh/jalali-datepicker)
|
|
6
|
-
[](https://bundlephobia.com/package/@mngh/jalali-datepicker)
|
|
7
|
-
[](https://github.com/mngh/jalali-datepicker/blob/main/LICENSE)
|
|
8
|
-
[](https://github.com/mngh/jalali-datepicker)
|
|
9
|
-
|
|
10
|
-
> **Repository:** [github.com/mngh/jalali-datepicker](https://github.com/mngh/jalali-datepicker)
|
|
11
|
-
> **Bundle size:** ~38 kB (CJS, unminified) / ~51 kB (ESM, unminified), highly tree-shakeable
|
|
12
|
-
> **Peer dependencies:** React 18 or 19
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
##
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
```
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
{
|
|
304
|
-
id: "
|
|
305
|
-
date: { year: 1405, month: 0, day:
|
|
306
|
-
color: "
|
|
307
|
-
title: "
|
|
308
|
-
},
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
<button onClick={
|
|
372
|
-
|
|
373
|
-
</button>
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
{
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
{cell.
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
|
411
|
-
|
|
|
412
|
-
| `
|
|
413
|
-
| `
|
|
414
|
-
| `
|
|
415
|
-
| `
|
|
416
|
-
| `
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
|
431
|
-
|
|
|
432
|
-
| `
|
|
433
|
-
| `
|
|
434
|
-
| `
|
|
435
|
-
| `
|
|
436
|
-
| `
|
|
437
|
-
| `
|
|
438
|
-
| `
|
|
439
|
-
| `
|
|
440
|
-
| `
|
|
441
|
-
| `
|
|
442
|
-
| `
|
|
443
|
-
| `
|
|
444
|
-
| `
|
|
445
|
-
| `
|
|
446
|
-
| `
|
|
447
|
-
| `
|
|
448
|
-
| `
|
|
449
|
-
| `
|
|
450
|
-
| `
|
|
451
|
-
| `
|
|
452
|
-
| `
|
|
453
|
-
| `
|
|
454
|
-
| `
|
|
455
|
-
| `
|
|
456
|
-
| `
|
|
457
|
-
| `
|
|
458
|
-
| `
|
|
459
|
-
| `
|
|
460
|
-
| `
|
|
461
|
-
| `
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
|
474
|
-
|
|
|
475
|
-
| `
|
|
476
|
-
| `
|
|
477
|
-
| `
|
|
478
|
-
| `
|
|
479
|
-
| `
|
|
480
|
-
| `
|
|
481
|
-
| `
|
|
482
|
-
| `
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
export type
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
export interface
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
export interface
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
export interface
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
|
624
|
-
|
|
|
625
|
-
| `
|
|
626
|
-
| `
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
1
|
+
# @mngh/jalali-datepicker
|
|
2
|
+
|
|
3
|
+
A modern, headless-friendly Jalali (Persian/Shamsi) Date & Time Picker for React — zero date-library runtime dependencies, fully typed with native `Date` objects, WAI-ARIA accessible, and themeable with Tailwind CSS or CSS variables.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@mngh/jalali-datepicker)
|
|
6
|
+
[](https://bundlephobia.com/package/@mngh/jalali-datepicker)
|
|
7
|
+
[](https://github.com/mngh/jalali-datepicker/blob/main/LICENSE)
|
|
8
|
+
[](https://github.com/mngh/jalali-datepicker)
|
|
9
|
+
|
|
10
|
+
> **Repository:** [github.com/mngh/jalali-datepicker](https://github.com/mngh/jalali-datepicker)
|
|
11
|
+
> **Bundle size:** ~38 kB (CJS, unminified) / ~51 kB (ESM, unminified), highly tree-shakeable
|
|
12
|
+
> **Peer dependencies:** React 18 or 19
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 🌐 Live Interactive Demo
|
|
17
|
+
|
|
18
|
+
Try all features, props, themes, and picker modes interactively in your browser:
|
|
19
|
+
|
|
20
|
+
👉 **[Explore Live Demo & Playground](https://mngh-27.github.io/jalali-datepicker-package/)**
|
|
21
|
+
|
|
22
|
+
## Table of Contents
|
|
23
|
+
|
|
24
|
+
1. [Introduction & Quick Start](#1-introduction--quick-start)
|
|
25
|
+
2. [Core Concepts & Data Flow](#2-core-concepts--data-flow)
|
|
26
|
+
3. [Display Variants & Selection Modes](#3-display-variants--selection-modes)
|
|
27
|
+
4. [Built-in Plugins & Advanced Features](#4-built-in-plugins--advanced-features)
|
|
28
|
+
5. [Headless Hook Architecture](#5-headless-hook-architecture-usejalalidatepicker)
|
|
29
|
+
6. [Complete API Reference](#6-complete-api-reference)
|
|
30
|
+
7. [Theming & Styling](#7-theming--styling-integration)
|
|
31
|
+
8. [Accessibility & Keyboard Shortcuts](#8-accessibility--keyboard-shortcuts)
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. Introduction & Quick Start
|
|
36
|
+
|
|
37
|
+
### Why `@mngh/jalali-datepicker`?
|
|
38
|
+
|
|
39
|
+
- **Zero date-library overhead** — no Moment.js, no date-fns, no dayjs. The Jalali/Gregorian conversion math is implemented internally with plain arithmetic.
|
|
40
|
+
- **Native `Date` in, native `Date` out** — every prop and callback speaks standard JavaScript `Date` objects. You never touch a custom calendar object.
|
|
41
|
+
- **Three display variants** — inline, popover, and modal — and three selection modes — single, range, and multiple.
|
|
42
|
+
- **Headless-first** — the entire UI is built on top of a public hook (`useJalaliDatePicker`) that you can use to build your own component from scratch.
|
|
43
|
+
- **Accessible by default** — full keyboard navigation, roving tabindex, and ARIA roles (`grid`, `gridcell`, `dialog`) out of the box.
|
|
44
|
+
- **Themeable** — Tailwind CSS class slots and CSS custom properties, with a built-in `DatePickerThemeProvider` for light/dark mode.
|
|
45
|
+
|
|
46
|
+
### Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install @mngh/jalali-datepicker
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pnpm add @mngh/jalali-datepicker
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
yarn add @mngh/jalali-datepicker
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
bun add @mngh/jalali-datepicker
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Quick Start
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { useState } from "react";
|
|
68
|
+
import {
|
|
69
|
+
JalaliDatePicker,
|
|
70
|
+
DatePickerThemeProvider,
|
|
71
|
+
} from "@mngh/jalali-datepicker";
|
|
72
|
+
|
|
73
|
+
export default function App() {
|
|
74
|
+
const [date, setDate] = useState<Date | null>(null);
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<DatePickerThemeProvider mode="light">
|
|
78
|
+
<JalaliDatePicker
|
|
79
|
+
variant="popover"
|
|
80
|
+
mode="single"
|
|
81
|
+
value={date}
|
|
82
|
+
onChange={setDate}
|
|
83
|
+
placeholder="Select a date"
|
|
84
|
+
/>
|
|
85
|
+
</DatePickerThemeProvider>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
That's it — `date` is always a plain JavaScript `Date` object (or `null`). No conversion helpers, no adapters.
|
|
91
|
+
The package ships with self-contained component styles, so there is no separate CSS file to import.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 2. Core Concepts & Data Flow
|
|
96
|
+
|
|
97
|
+
### Standard JavaScript `Date` only
|
|
98
|
+
|
|
99
|
+
`@mngh/jalali-datepicker` never asks the consumer to construct or parse a custom Jalali object. Every value that crosses the public API boundary — `value`, `defaultValue`, `minDate`, `maxDate`, the `onChange` payload — is a native `Date`, or one of the following shapes depending on `mode`:
|
|
100
|
+
|
|
101
|
+
| `mode` | Value shape | Example |
|
|
102
|
+
| ------------ | ------------------------------ | ----------------------------------------------- |
|
|
103
|
+
| `'single'` | `Date \| null` | `new Date(2026, 2, 21)` |
|
|
104
|
+
| `'range'` | `[Date \| null, Date \| null]` | `[new Date(2026, 2, 21), new Date(2026, 3, 1)]` |
|
|
105
|
+
| `'multiple'` | `Date[]` | `[new Date(2026, 2, 21), new Date(2026, 5, 1)]` |
|
|
106
|
+
|
|
107
|
+
Internally, the picker converts a `Date` to a Jalali year/month/day triple purely for rendering the grid, and converts back to `Date` the instant a value leaves the component. The consumer's state never has to know Jalali math exists.
|
|
108
|
+
|
|
109
|
+
### Under-the-hood math
|
|
110
|
+
|
|
111
|
+
The Jalali↔Gregorian conversion is implemented with a self-contained arithmetic algorithm (based on the 33-year leap-year cycle of the Jalali calendar), so there's no dependency on `Intl`, ICU data, or a third-party calendar library. This keeps the bundle small and behavior consistent across browsers and server-side rendering environments.
|
|
112
|
+
|
|
113
|
+
### Digit presentation
|
|
114
|
+
|
|
115
|
+
Calendar cells, headers, and the masked text input can render either Persian (`۰-۹`) or Latin (`0-9`) digits:
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
<JalaliDatePicker digitType="persian" /> // ۱۴۰۵/۰۱/۰۱
|
|
119
|
+
<JalaliDatePicker digitType="latin" /> // 1405/01/01
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`digitType` defaults to `'persian'`.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 3. Display Variants & Selection Modes
|
|
127
|
+
|
|
128
|
+
### Display variants (`variant`)
|
|
129
|
+
|
|
130
|
+
#### `'inline'`
|
|
131
|
+
|
|
132
|
+
Renders the calendar permanently in the page flow — ideal for embedding inside a form or sidebar without a trigger input.
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
<JalaliDatePicker
|
|
136
|
+
variant="inline"
|
|
137
|
+
mode="single"
|
|
138
|
+
value={date}
|
|
139
|
+
onChange={setDate}
|
|
140
|
+
/>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### `'popover'`
|
|
144
|
+
|
|
145
|
+
The default — a text input that opens a floating calendar panel on focus/click and closes on outside click or `Escape`.
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
<JalaliDatePicker
|
|
149
|
+
variant="popover"
|
|
150
|
+
mode="single"
|
|
151
|
+
value={date}
|
|
152
|
+
onChange={setDate}
|
|
153
|
+
placeholder="YYYY/MM/DD"
|
|
154
|
+
/>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### `'modal'`
|
|
158
|
+
|
|
159
|
+
Opens the calendar in a full-screen dialog with a backdrop blur, scroll locking on `<body>`, and an `Escape` key listener that closes the dialog and returns focus to the trigger.
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
<JalaliDatePicker
|
|
163
|
+
variant="modal"
|
|
164
|
+
mode="single"
|
|
165
|
+
value={date}
|
|
166
|
+
onChange={setDate}
|
|
167
|
+
/>
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Selection modes (`mode`)
|
|
171
|
+
|
|
172
|
+
#### `'single'`
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
const [date, setDate] = useState<Date | null>(null);
|
|
176
|
+
|
|
177
|
+
<JalaliDatePicker mode="single" value={date} onChange={setDate} />;
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### `'range'`
|
|
181
|
+
|
|
182
|
+
Start/end selection with a live hover preview that highlights the would-be range as the pointer moves before the end date is confirmed.
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
const [range, setRange] = useState<[Date | null, Date | null]>([null, null]);
|
|
186
|
+
|
|
187
|
+
<JalaliDatePicker
|
|
188
|
+
mode="range"
|
|
189
|
+
value={range}
|
|
190
|
+
onChange={setRange}
|
|
191
|
+
numberOfMonths={2}
|
|
192
|
+
/>;
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### `'multiple'`
|
|
196
|
+
|
|
197
|
+
Select any number of non-contiguous dates; clicking a selected date removes it.
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
const [dates, setDates] = useState<Date[]>([]);
|
|
201
|
+
|
|
202
|
+
<JalaliDatePicker mode="multiple" value={dates} onChange={setDates} />;
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 4. Built-in Plugins & Advanced Features
|
|
208
|
+
|
|
209
|
+
### Dual month view
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
<JalaliDatePicker
|
|
213
|
+
mode="range"
|
|
214
|
+
numberOfMonths={2}
|
|
215
|
+
value={range}
|
|
216
|
+
onChange={setRange}
|
|
217
|
+
/>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Renders two synchronized month grids side by side (or stacked on narrow viewports), sharing a single hover-preview state — the standard pattern for range pickers.
|
|
221
|
+
|
|
222
|
+
### Time picker integration
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
const [date, setDate] = useState<Date | null>(null);
|
|
226
|
+
const [time, setTime] = useState<{
|
|
227
|
+
hour: number;
|
|
228
|
+
minute: number;
|
|
229
|
+
second?: number;
|
|
230
|
+
}>({
|
|
231
|
+
hour: 12,
|
|
232
|
+
minute: 0,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
<JalaliDatePicker
|
|
236
|
+
mode="single"
|
|
237
|
+
value={date}
|
|
238
|
+
onChange={setDate}
|
|
239
|
+
enableTime
|
|
240
|
+
timeValue={time}
|
|
241
|
+
onTimeChange={setTime}
|
|
242
|
+
hourStep={1}
|
|
243
|
+
minuteStep={5}
|
|
244
|
+
showSeconds={false}
|
|
245
|
+
/>;
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
When `enableTime` is set, the resolved `Date` passed to `onChange` already has the selected hour/minute/second merged in — you don't need to combine `date` and `time` yourself.
|
|
249
|
+
|
|
250
|
+
| Prop | Type | Default | Description |
|
|
251
|
+
| -------------- | --------------------------------------------------- | ----------- | ----------------------------------------------- |
|
|
252
|
+
| `enableTime` | `boolean` | `false` | Shows the time picker panel below the calendar. |
|
|
253
|
+
| `timeValue` | `{ hour: number; minute: number; second?: number }` | `undefined` | Controlled time value. |
|
|
254
|
+
| `defaultTimeValue` | `{ hour: number; minute: number; second?: number }` | Date/current time | Initial uncontrolled time. |
|
|
255
|
+
| `onTimeChange` | `(time) => void` | `undefined` | Fires when the time inputs change. |
|
|
256
|
+
| `hourStep` | `number` | `1` | Increment for the hour control. |
|
|
257
|
+
| `minuteStep` | `number` | `1` | Increment for the minute control. |
|
|
258
|
+
| `secondStep` | `number` | `1` | Increment for the seconds control. |
|
|
259
|
+
| `showSeconds` | `boolean` | `false` | Shows a seconds column. |
|
|
260
|
+
|
|
261
|
+
### Live masked input
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
<JalaliDatePicker
|
|
265
|
+
variant="popover"
|
|
266
|
+
mode="single"
|
|
267
|
+
value={date}
|
|
268
|
+
onChange={setDate}
|
|
269
|
+
useMaskedInput
|
|
270
|
+
/>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
`useMaskedInput` turns the trigger `<input>` into a real-time Persian date mask: digits are inserted into the correct segment as the user types, slashes are auto-inserted, and an invalid segment (e.g. month `13`) is rejected without corrupting the rest of the string.
|
|
274
|
+
|
|
275
|
+
### Iranian solar holidays & Fridays
|
|
276
|
+
|
|
277
|
+
```tsx
|
|
278
|
+
<JalaliDatePicker
|
|
279
|
+
mode="single"
|
|
280
|
+
value={date}
|
|
281
|
+
onChange={setDate}
|
|
282
|
+
showHolidays
|
|
283
|
+
customHolidays={[
|
|
284
|
+
{
|
|
285
|
+
date: { year: 1405, month: 0, day: 1 },
|
|
286
|
+
title: "شرکت تعطیل است",
|
|
287
|
+
isOff: true,
|
|
288
|
+
},
|
|
289
|
+
]}
|
|
290
|
+
/>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
When `showHolidays` is enabled, official Iranian solar-calendar holidays and every Friday are rendered in red with a hover/focus tooltip describing the occasion. `customHolidays` merges additional organization-specific dates into the same highlighting and tooltip system.
|
|
294
|
+
|
|
295
|
+
### Calendar events & badges
|
|
296
|
+
|
|
297
|
+
```tsx
|
|
298
|
+
<JalaliDatePicker
|
|
299
|
+
mode="single"
|
|
300
|
+
value={date}
|
|
301
|
+
onChange={setDate}
|
|
302
|
+
events={[
|
|
303
|
+
{
|
|
304
|
+
id: "standup",
|
|
305
|
+
date: { year: 1405, month: 0, day: 1 },
|
|
306
|
+
color: "blue",
|
|
307
|
+
title: "Team standup",
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
id: "deadline",
|
|
311
|
+
date: { year: 1405, month: 0, day: 5 },
|
|
312
|
+
color: "red",
|
|
313
|
+
title: "Deadline",
|
|
314
|
+
},
|
|
315
|
+
]}
|
|
316
|
+
/>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Each entry in `events` renders a small colored dot under the corresponding day cell. Its title is included in the cell's accessible label and native tooltip. Up to three event dots are shown per day.
|
|
320
|
+
|
|
321
|
+
### Footer status & action buttons
|
|
322
|
+
|
|
323
|
+
```tsx
|
|
324
|
+
<JalaliDatePicker
|
|
325
|
+
mode="single"
|
|
326
|
+
value={date}
|
|
327
|
+
onChange={setDate}
|
|
328
|
+
showFooter
|
|
329
|
+
showStatusText
|
|
330
|
+
showActions
|
|
331
|
+
onConfirm={(value) => console.log("confirmed:", value)}
|
|
332
|
+
/>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
| Prop | Type | Default | Description |
|
|
336
|
+
| ---------------- | ----------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
337
|
+
| `showFooter` | `boolean` | `false` | Master switch for the footer row. |
|
|
338
|
+
| `showStatusText` | `boolean` | `true` (when footer shown) | Shows a human-readable summary of the current selection, e.g. "5 days selected". |
|
|
339
|
+
| `showActions` | `boolean` | `true` (when footer shown) | Shows the **Today**, **Clear**, and **Confirm** buttons. |
|
|
340
|
+
| `onConfirm` | `(value) => void` | `undefined` | Fires when **Confirm** is clicked, with the currently pending selection. Useful when you want selection to be provisional until confirmed, especially in `modal` variant. |
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## 5. Headless Hook Architecture (`useJalaliDatePicker`)
|
|
345
|
+
|
|
346
|
+
For teams that need a completely custom UI — a bespoke calendar layout, a non-standard interaction pattern, or integration into an existing design system — `@mngh/jalali-datepicker` exposes the same state machine that powers its default components as a standalone hook.
|
|
347
|
+
|
|
348
|
+
```tsx
|
|
349
|
+
import { useJalaliDatePicker } from "@mngh/jalali-datepicker";
|
|
350
|
+
|
|
351
|
+
function CustomCalendar() {
|
|
352
|
+
const {
|
|
353
|
+
viewYear,
|
|
354
|
+
viewMonth,
|
|
355
|
+
grid,
|
|
356
|
+
goToPrevMonth,
|
|
357
|
+
goToNextMonth,
|
|
358
|
+
goToToday,
|
|
359
|
+
selectDate,
|
|
360
|
+
setHoverDate,
|
|
361
|
+
clear,
|
|
362
|
+
} = useJalaliDatePicker({
|
|
363
|
+
mode: "single",
|
|
364
|
+
value: null,
|
|
365
|
+
onChange: (date) => console.log(date),
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
return (
|
|
369
|
+
<div role="grid" aria-label={`${viewYear}/${viewMonth + 1}`}>
|
|
370
|
+
<header>
|
|
371
|
+
<button onClick={goToPrevMonth} aria-label="Previous month">
|
|
372
|
+
‹
|
|
373
|
+
</button>
|
|
374
|
+
<span>
|
|
375
|
+
{viewYear}/{viewMonth + 1}
|
|
376
|
+
</span>
|
|
377
|
+
<button onClick={goToNextMonth} aria-label="Next month">
|
|
378
|
+
›
|
|
379
|
+
</button>
|
|
380
|
+
</header>
|
|
381
|
+
|
|
382
|
+
<div className="grid grid-cols-7">
|
|
383
|
+
{grid.map((cell) => (
|
|
384
|
+
<button
|
|
385
|
+
key={`${cell.jalali.year}-${cell.jalali.month}-${cell.jalali.day}`}
|
|
386
|
+
role="gridcell"
|
|
387
|
+
disabled={cell.isDisabled || !cell.isCurrentMonth}
|
|
388
|
+
aria-selected={cell.isSelected}
|
|
389
|
+
data-today={cell.isToday}
|
|
390
|
+
onMouseEnter={() => setHoverDate(cell.jalali)}
|
|
391
|
+
onMouseLeave={() => setHoverDate(null)}
|
|
392
|
+
onClick={() => selectDate(cell.jalali)}
|
|
393
|
+
>
|
|
394
|
+
{cell.dayNumber}
|
|
395
|
+
</button>
|
|
396
|
+
))}
|
|
397
|
+
</div>
|
|
398
|
+
|
|
399
|
+
<footer>
|
|
400
|
+
<button onClick={goToToday}>Today</button>
|
|
401
|
+
<button onClick={clear}>Clear</button>
|
|
402
|
+
</footer>
|
|
403
|
+
</div>
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Hook return values
|
|
409
|
+
|
|
410
|
+
| Value | Type | Description |
|
|
411
|
+
| --------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------ |
|
|
412
|
+
| `viewYear` | `number` | The Jalali year currently displayed. |
|
|
413
|
+
| `viewMonth` | `number` | The Jalali month (0-indexed) currently displayed. |
|
|
414
|
+
| `grid` | `JalaliCalendarCell[]` | Flattened 42-cell array, including leading/trailing adjacent-month days. |
|
|
415
|
+
| `selected` | `InternalSelectedValue` | Current Jalali selection, shaped according to `mode`. |
|
|
416
|
+
| `hoverDate` | `JalaliDate \| null` | Date currently under the pointer, used for range-preview rendering. |
|
|
417
|
+
| `goToPrevMonth` / `goToNextMonth` | `() => void` | Step the view by one month. |
|
|
418
|
+
| `goToToday` | `() => void` | Reset the view to the month containing today. |
|
|
419
|
+
| `setView` | `(year, month) => void` | Jump to a specific Jalali year and zero-based month. |
|
|
420
|
+
| `selectDate` | `(date: JalaliDate) => void` | Commit a date into the current selection according to `mode`. |
|
|
421
|
+
| `setHoverDate` | `(date: JalaliDate \| null) => void` | Update the hover-preview date (used for range mode). |
|
|
422
|
+
| `clear` | `() => void` | Reset the selection to its empty state (`null`, `[null, null]`, or `[]`). |
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## 6. Complete API Reference
|
|
427
|
+
|
|
428
|
+
### `JalaliDatePickerProps`
|
|
429
|
+
|
|
430
|
+
| Prop | Type | Default | Description |
|
|
431
|
+
| -------------------- | -------------------------------------------------------- | ------------------------------ | ----------------------------------------------------------- |
|
|
432
|
+
| `variant` | `'inline' \| 'popover' \| 'modal'` | `'inline'` | How the calendar is presented. |
|
|
433
|
+
| `mode` | `'single' \| 'range' \| 'multiple'` | `'single'` | Selection strategy. |
|
|
434
|
+
| `value` | `Date \| null \| [Date \| null, Date \| null] \| Date[]` | — | Controlled value, shaped by `mode`. |
|
|
435
|
+
| `defaultValue` | same as `value` | `null` / `[null, null]` / `[]` | Uncontrolled initial value. |
|
|
436
|
+
| `onChange` | `(value) => void` | — | Fires whenever the selection changes. |
|
|
437
|
+
| `minDate` | `Date` | `undefined` | Earliest selectable date. |
|
|
438
|
+
| `maxDate` | `Date` | `undefined` | Latest selectable date. |
|
|
439
|
+
| `isDateDisabled` | `(date: Date) => boolean` | `undefined` | Custom predicate to disable arbitrary dates. |
|
|
440
|
+
| `digitType` | `'persian' \| 'latin'` | `'persian'` | Digit glyphs used throughout the UI. |
|
|
441
|
+
| `direction` | `'rtl' \| 'ltr'` | `'rtl'` | Layout and horizontal keyboard direction. |
|
|
442
|
+
| `firstDayOfWeek` | `0 \| 1 \| ... \| 6` | `0` (Saturday) | First day shown in each week. |
|
|
443
|
+
| `initialViewDate` | `Date \| JalaliDate` | selected date / today | Initial visible Jalali month. |
|
|
444
|
+
| `numberOfMonths` | `1 \| 2` | `1` | Number of side-by-side month grids. |
|
|
445
|
+
| `enableTime` | `boolean` | `false` | Enables the time picker panel. |
|
|
446
|
+
| `timeValue` | `JalaliTime \| null` | `undefined` | Controlled time-of-day value; `null` resolves to `00:00`. |
|
|
447
|
+
| `defaultTimeValue` | `JalaliTime` | date time / current time | Initial uncontrolled time value. |
|
|
448
|
+
| `onTimeChange` | `(time: JalaliTime) => void` | `undefined` | Fires when the time changes. |
|
|
449
|
+
| `hourStep` | `number` | `1` | Hour increment step. |
|
|
450
|
+
| `minuteStep` | `number` | `1` | Minute increment step. |
|
|
451
|
+
| `secondStep` | `number` | `1` | Second increment step. |
|
|
452
|
+
| `showSeconds` | `boolean` | `false` | Show a seconds column in the time picker. |
|
|
453
|
+
| `useMaskedInput` | `boolean` | `false` | Enables the live typing mask on the trigger input. |
|
|
454
|
+
| `showHolidays` | `boolean` | `false` | Highlights official holidays and Fridays. |
|
|
455
|
+
| `customHolidays` | `CustomHolidayRule[]` | `[]` | Additional holiday rules to highlight. |
|
|
456
|
+
| `events` | `CalendarEvent[]` | `[]` | Event dots/badges rendered on matching day cells. |
|
|
457
|
+
| `showFooter` | `boolean` | `false` | Shows the footer row. |
|
|
458
|
+
| `showStatusText` | `boolean` | `true` | Shows the selection-summary text in the footer. |
|
|
459
|
+
| `showActions` | `boolean` | `true` | Shows Today/Clear/Confirm buttons in the footer. |
|
|
460
|
+
| `onConfirm` | `(value) => void` | `undefined` | Fires when Confirm is pressed. |
|
|
461
|
+
| `placeholder` | `string` | `'انتخاب تاریخ...'` | Placeholder text for the trigger input (`popover`/`modal`). |
|
|
462
|
+
| `closeOnOutsideClick`| `boolean` | `true` | Closes popover/modal when its outside area is clicked. |
|
|
463
|
+
| `closeOnEscape` | `boolean` | `true` | Closes popover/modal when Escape is pressed. |
|
|
464
|
+
| `onOpenChange` | `(open: boolean) => void` | `undefined` | Reports open-state changes. |
|
|
465
|
+
| `className` / `style`| `string` / `CSSProperties` | `undefined` | Root wrapper overrides. |
|
|
466
|
+
| `classNames` | `DatePickerClassNames` | `{}` | Per-slot class names. |
|
|
467
|
+
| `styles` | `DatePickerStyles` | `{}` | Per-slot inline style overrides. |
|
|
468
|
+
|
|
469
|
+
### Style slots (`classNames` & `styles`)
|
|
470
|
+
|
|
471
|
+
Both `classNames` and `styles` accept the same set of slot keys, letting you target any part of the picker with Tailwind classes or inline styles respectively.
|
|
472
|
+
|
|
473
|
+
| Slot key | Targets |
|
|
474
|
+
| -------- | ------- |
|
|
475
|
+
| `root` | Outermost wrapper element. |
|
|
476
|
+
| `input`, `inputWrapper`, `inputClearButton` | Trigger input and masked-input controls. |
|
|
477
|
+
| `calendar`, `calendarBody`, `calendarPanes`, `calendarPane`, `paneDivider` | Calendar panel and one/two-month layout. |
|
|
478
|
+
| `header`, `headerTitle`, `navButton` | Month/year header controls. |
|
|
479
|
+
| `weekdays`, `weekdayCell`, `grid` | Weekday row and date grid. |
|
|
480
|
+
| `dayCell`, `outsideMonthCell`, `todayCell`, `selectedCell`, `disabledCell`, `holidayCell` | Base and state-specific day cells. |
|
|
481
|
+
| `rangeBetweenCell`, `rangeStartCell`, `rangeEndCell` | Range-selection states. |
|
|
482
|
+
| `eventBadges`, `eventBadge` | Event indicator wrapper and dots. |
|
|
483
|
+
| `monthYearPicker`, `yearList`, `yearButton`, `selectedYearButton` | Year picker elements. |
|
|
484
|
+
| `monthGrid`, `monthButton`, `selectedMonthButton` | Month picker elements. |
|
|
485
|
+
| `timePicker`, `timeLabel`, `timeControls`, `timeSelect`, `timeSeparator` | Time-picker elements. |
|
|
486
|
+
| `footer`, `footerStatus`, `footerActions` | Footer layout and status. |
|
|
487
|
+
| `todayButton`, `clearButton`, `confirmButton` | Footer action buttons. |
|
|
488
|
+
| `modalBackdrop`, `closeButton` | Modal backdrop and close control. |
|
|
489
|
+
|
|
490
|
+
```tsx
|
|
491
|
+
<JalaliDatePicker
|
|
492
|
+
classNames={{
|
|
493
|
+
root: "font-sans",
|
|
494
|
+
input: "rounded-lg border-gray-300 focus:ring-2 focus:ring-blue-500",
|
|
495
|
+
selectedCell: "bg-blue-600 text-white",
|
|
496
|
+
todayCell: "ring-1 ring-blue-400",
|
|
497
|
+
holidayCell: "text-red-500",
|
|
498
|
+
}}
|
|
499
|
+
/>
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### Exported TypeScript interfaces & types
|
|
503
|
+
|
|
504
|
+
```ts
|
|
505
|
+
export type SelectedDateValue =
|
|
506
|
+
| Date
|
|
507
|
+
| null
|
|
508
|
+
| [Date | null, Date | null]
|
|
509
|
+
| Date[];
|
|
510
|
+
|
|
511
|
+
export type DateRange = [Date | null, Date | null];
|
|
512
|
+
|
|
513
|
+
export interface JalaliDate {
|
|
514
|
+
year: number;
|
|
515
|
+
month: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11;
|
|
516
|
+
day: number;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
export interface JalaliTime {
|
|
520
|
+
hour: number;
|
|
521
|
+
minute: number;
|
|
522
|
+
second?: number;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export interface CalendarEvent {
|
|
526
|
+
id: string;
|
|
527
|
+
date: JalaliDate;
|
|
528
|
+
title: string;
|
|
529
|
+
description?: string;
|
|
530
|
+
color?: string;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export interface CustomHolidayRule {
|
|
534
|
+
date: JalaliDate;
|
|
535
|
+
title: string;
|
|
536
|
+
isOff?: boolean;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export interface JalaliCalendarCell {
|
|
540
|
+
jalali: JalaliDate;
|
|
541
|
+
gregorianDate: Date;
|
|
542
|
+
dayNumber: number;
|
|
543
|
+
isCurrentMonth: boolean;
|
|
544
|
+
isToday: boolean;
|
|
545
|
+
isSelected: boolean;
|
|
546
|
+
isDisabled: boolean;
|
|
547
|
+
isInRange?: boolean;
|
|
548
|
+
isRangeStart?: boolean;
|
|
549
|
+
isRangeEnd?: boolean;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
export type DatePickerClassNames = DatePickerSlots<string>;
|
|
553
|
+
export type DatePickerStyles = DatePickerSlots<React.CSSProperties>;
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## 7. Theming & Styling Integration
|
|
559
|
+
|
|
560
|
+
### `DatePickerThemeProvider`
|
|
561
|
+
|
|
562
|
+
Wrap your app (or just the picker) in `DatePickerThemeProvider` to switch between light and dark palettes:
|
|
563
|
+
|
|
564
|
+
```tsx
|
|
565
|
+
import { DatePickerThemeProvider } from "@mngh/jalali-datepicker";
|
|
566
|
+
|
|
567
|
+
<DatePickerThemeProvider mode="dark">
|
|
568
|
+
<JalaliDatePicker mode="single" value={date} onChange={setDate} />
|
|
569
|
+
</DatePickerThemeProvider>;
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
| Prop | Type | Default | Description |
|
|
573
|
+
| ------------- | -------------------- | --------- | ---------------------------------------- |
|
|
574
|
+
| `mode` | `'light' \| 'dark'` | `'light'` | Selects the built-in color palette. |
|
|
575
|
+
| `customTheme` | `DeepPartial<Theme>` | `undefined` | Overrides colors, radii, or shadows. |
|
|
576
|
+
|
|
577
|
+
### Sizing variables
|
|
578
|
+
|
|
579
|
+
The root exposes three layout variables. Override them with `style` or `styles.root` to resize the date grid without breaking its column math:
|
|
580
|
+
|
|
581
|
+
```css
|
|
582
|
+
.large-picker {
|
|
583
|
+
--pdp-cell-size: 42px;
|
|
584
|
+
--pdp-cell-gap: 5px;
|
|
585
|
+
--pdp-calendar-pane-width: 324px; /* 7 × 42 + 6 × 5 */
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Use `customTheme` for palette changes:
|
|
590
|
+
|
|
591
|
+
```tsx
|
|
592
|
+
<DatePickerThemeProvider
|
|
593
|
+
customTheme={{
|
|
594
|
+
colors: { primary: "#0f766e", primaryHover: "#115e59" },
|
|
595
|
+
radii: { lg: "12px" },
|
|
596
|
+
}}
|
|
597
|
+
>
|
|
598
|
+
<JalaliDatePicker className="large-picker" />
|
|
599
|
+
</DatePickerThemeProvider>
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
### Tailwind CSS via `classNames`
|
|
603
|
+
|
|
604
|
+
Every meaningful element has a class slot for Tailwind or regular CSS. Built-in defaults are inline styles, so use `styles` when overriding the same CSS property; use `classNames` for selectors, states, and properties not already set inline.
|
|
605
|
+
|
|
606
|
+
```tsx
|
|
607
|
+
<JalaliDatePicker
|
|
608
|
+
classNames={{
|
|
609
|
+
calendar: "shadow-xl border border-slate-200 rounded-2xl p-4",
|
|
610
|
+
header: "flex items-center justify-between mb-2",
|
|
611
|
+
dayCell: "h-9 w-9 rounded-full hover:bg-slate-100 transition-colors",
|
|
612
|
+
selectedCell: "bg-indigo-600 text-white hover:bg-indigo-600",
|
|
613
|
+
}}
|
|
614
|
+
/>
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
---
|
|
618
|
+
|
|
619
|
+
## 8. Accessibility & Keyboard Shortcuts
|
|
620
|
+
|
|
621
|
+
The calendar grid uses `role="grid"` / `role="gridcell"` with a roving `tabindex`. Escape closes popover/modal variants and restores focus to the trigger.
|
|
622
|
+
|
|
623
|
+
| Key | Action |
|
|
624
|
+
| ----------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
625
|
+
| `Arrow Left` / `Arrow Right` | Move focus one day (respects the `direction` prop). |
|
|
626
|
+
| `Arrow Up` / `Arrow Down` | Move focus one week. |
|
|
627
|
+
| `Page Up` / `Page Down` | Move focus to the same day in the previous/next month. |
|
|
628
|
+
| `Home` | Move focus to the first day of the current month. |
|
|
629
|
+
| `End` | Move focus to the last day of the current month. |
|
|
630
|
+
| `Enter` / `Space` | Select the focused date. |
|
|
631
|
+
| `Escape` | Close the `popover` or `modal` panel and return focus to the trigger. |
|
|
632
|
+
| `Tab` / `Shift + Tab` | Move focus through the available controls in document order. |
|
|
633
|
+
|
|
634
|
+
> **Note:** With `direction="rtl"`, `Arrow Left`/`Arrow Right` are swapped automatically so the keys match visual travel.
|
|
635
|
+
|
|
636
|
+
> **Tip:** `modal` variant applies `aria-modal="true"` and `role="dialog"`, and locks background scroll via a `overflow: hidden` toggle on `<body>` while open.
|