best-react-datepicker 0.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.
Files changed (46) hide show
  1. package/dist/chunk-A3NQUSC5.cjs +1155 -0
  2. package/dist/chunk-A3NQUSC5.cjs.map +1 -0
  3. package/dist/chunk-FBN3SFQJ.js +1140 -0
  4. package/dist/chunk-FBN3SFQJ.js.map +1 -0
  5. package/dist/chunk-HI4J64KK.cjs +592 -0
  6. package/dist/chunk-HI4J64KK.cjs.map +1 -0
  7. package/dist/chunk-NF5M43IO.js +49 -0
  8. package/dist/chunk-NF5M43IO.js.map +1 -0
  9. package/dist/chunk-QW2CUEX4.js +117 -0
  10. package/dist/chunk-QW2CUEX4.js.map +1 -0
  11. package/dist/chunk-R7HLYS5V.cjs +120 -0
  12. package/dist/chunk-R7HLYS5V.cjs.map +1 -0
  13. package/dist/chunk-W6KEY2SO.js +526 -0
  14. package/dist/chunk-W6KEY2SO.js.map +1 -0
  15. package/dist/chunk-WBWQ5RKN.cjs +51 -0
  16. package/dist/chunk-WBWQ5RKN.cjs.map +1 -0
  17. package/dist/hooks.cjs +66 -0
  18. package/dist/hooks.cjs.map +1 -0
  19. package/dist/hooks.d.cts +555 -0
  20. package/dist/hooks.d.ts +555 -0
  21. package/dist/hooks.js +5 -0
  22. package/dist/hooks.js.map +1 -0
  23. package/dist/index.cjs +1502 -0
  24. package/dist/index.cjs.map +1 -0
  25. package/dist/index.d.cts +239 -0
  26. package/dist/index.d.ts +239 -0
  27. package/dist/index.js +1303 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/locales.cjs +16 -0
  30. package/dist/locales.cjs.map +1 -0
  31. package/dist/locales.d.cts +6 -0
  32. package/dist/locales.d.ts +6 -0
  33. package/dist/locales.js +3 -0
  34. package/dist/locales.js.map +1 -0
  35. package/dist/presets-B8wfaoTj.d.cts +54 -0
  36. package/dist/presets-CB5Rt4Li.d.ts +54 -0
  37. package/dist/styles.css +751 -0
  38. package/dist/types-DA2PcEiy.d.cts +104 -0
  39. package/dist/types-DA2PcEiy.d.ts +104 -0
  40. package/dist/utils.cjs +282 -0
  41. package/dist/utils.cjs.map +1 -0
  42. package/dist/utils.d.cts +57 -0
  43. package/dist/utils.d.ts +57 -0
  44. package/dist/utils.js +5 -0
  45. package/dist/utils.js.map +1 -0
  46. package/package.json +79 -0
@@ -0,0 +1,1140 @@
1
+ import { generateCalendarMonths, isDateInRange, isDateDisabled, isDateHighlighted, subYears, subMonths, addYears, addMonths, endOfWeek, startOfWeek, subDays, addDays, formatDate, isSameDay, parseDate, differenceInDays, isBefore, isBetween, isAfter, to12Hour, clone, to24Hour, generateHours, generateMinutes, generateSeconds, formatTime, generateMonthGrid, createDate, startOfMonth, isSameMonth, generateYearGrid, getWeekNumber, getWeekDates } from './chunk-W6KEY2SO.js';
2
+ import { defaultLocale } from './chunk-QW2CUEX4.js';
3
+ import { useState, useRef, useCallback, useMemo, useEffect } from 'react';
4
+
5
+ function useControllable(options) {
6
+ const { value: controlledValue, defaultValue, onChange } = options;
7
+ const isControlled = controlledValue !== void 0;
8
+ const [internalValue, setInternalValue] = useState(defaultValue);
9
+ const onChangeRef = useRef(onChange);
10
+ onChangeRef.current = onChange;
11
+ const value = isControlled ? controlledValue : internalValue;
12
+ const setValue = useCallback(
13
+ (newValue) => {
14
+ if (!isControlled) {
15
+ setInternalValue(newValue);
16
+ }
17
+ onChangeRef.current?.(newValue);
18
+ },
19
+ [isControlled]
20
+ );
21
+ return [value, setValue];
22
+ }
23
+ function useCalendar(options = {}) {
24
+ const {
25
+ numberOfMonths = 1,
26
+ weekStartsOn: weekStartsOnProp,
27
+ fixedWeeks = false,
28
+ minDate,
29
+ maxDate,
30
+ disabled,
31
+ highlighted,
32
+ locale = defaultLocale
33
+ } = options;
34
+ const weekStartsOn = weekStartsOnProp ?? locale.weekStartsOn;
35
+ const now = /* @__PURE__ */ new Date();
36
+ const [viewingDate, setViewingDate] = useControllable({
37
+ value: options.month,
38
+ defaultValue: options.defaultMonth ?? new Date(now.getFullYear(), now.getMonth(), 1),
39
+ onChange: (date) => options.onMonthChange?.(date)
40
+ });
41
+ const [focusedDate, setFocusedDate] = useState(null);
42
+ const viewingYear = viewingDate.getFullYear();
43
+ const viewingMonth = viewingDate.getMonth();
44
+ const months = useMemo(
45
+ () => generateCalendarMonths(viewingYear, viewingMonth, numberOfMonths, {
46
+ weekStartsOn,
47
+ fixedWeeks
48
+ }),
49
+ [viewingYear, viewingMonth, numberOfMonths, weekStartsOn, fixedWeeks]
50
+ );
51
+ const navigate = useCallback(
52
+ (year, month) => {
53
+ const adjusted = new Date(year, month, 1);
54
+ setViewingDate(adjusted);
55
+ },
56
+ [setViewingDate]
57
+ );
58
+ const goToNextMonth = useCallback(() => {
59
+ navigate(viewingYear, viewingMonth + 1);
60
+ }, [navigate, viewingYear, viewingMonth]);
61
+ const goToPrevMonth = useCallback(() => {
62
+ navigate(viewingYear, viewingMonth - 1);
63
+ }, [navigate, viewingYear, viewingMonth]);
64
+ const goToNextYear = useCallback(() => {
65
+ navigate(viewingYear + 1, viewingMonth);
66
+ }, [navigate, viewingYear, viewingMonth]);
67
+ const goToPrevYear = useCallback(() => {
68
+ navigate(viewingYear - 1, viewingMonth);
69
+ }, [navigate, viewingYear, viewingMonth]);
70
+ const goToMonth = useCallback(
71
+ (month) => {
72
+ navigate(viewingYear, month);
73
+ },
74
+ [navigate, viewingYear]
75
+ );
76
+ const goToYear = useCallback(
77
+ (year) => {
78
+ navigate(year, viewingMonth);
79
+ },
80
+ [navigate, viewingMonth]
81
+ );
82
+ const goToDate = useCallback(
83
+ (date) => {
84
+ navigate(date.getFullYear(), date.getMonth());
85
+ },
86
+ [navigate]
87
+ );
88
+ const goToToday = useCallback(() => {
89
+ const t = /* @__PURE__ */ new Date();
90
+ navigate(t.getFullYear(), t.getMonth());
91
+ }, [navigate]);
92
+ const checkDisabled = useCallback(
93
+ (date) => {
94
+ if (!isDateInRange(date, minDate, maxDate)) return true;
95
+ return isDateDisabled(date, disabled);
96
+ },
97
+ [minDate, maxDate, disabled]
98
+ );
99
+ const checkHighlighted = useCallback(
100
+ (date) => isDateHighlighted(date, highlighted),
101
+ [highlighted]
102
+ );
103
+ const checkOutsideRange = useCallback(
104
+ (date) => !isDateInRange(date, minDate, maxDate),
105
+ [minDate, maxDate]
106
+ );
107
+ return {
108
+ months,
109
+ focusedDate,
110
+ viewingYear,
111
+ viewingMonth,
112
+ goToNextMonth,
113
+ goToPrevMonth,
114
+ goToNextYear,
115
+ goToPrevYear,
116
+ goToMonth,
117
+ goToYear,
118
+ goToDate,
119
+ goToToday,
120
+ setFocusedDate,
121
+ isDateDisabled: checkDisabled,
122
+ isDateHighlighted: checkHighlighted,
123
+ isOutsideRange: checkOutsideRange,
124
+ locale
125
+ };
126
+ }
127
+ function useKeyboardNav(options) {
128
+ const {
129
+ focusedDate,
130
+ onFocusChange,
131
+ onSelect,
132
+ onClose,
133
+ weekStartsOn = 0,
134
+ minDate,
135
+ maxDate,
136
+ disabled
137
+ } = options;
138
+ const isValid = (date) => {
139
+ return isDateInRange(date, minDate, maxDate) && !isDateDisabled(date, disabled);
140
+ };
141
+ const moveFocus = (newDate) => {
142
+ if (isDateInRange(newDate, minDate, maxDate)) {
143
+ onFocusChange(newDate);
144
+ }
145
+ };
146
+ const handleKeyDown = useCallback(
147
+ (event) => {
148
+ let handled = true;
149
+ switch (event.key) {
150
+ case "ArrowRight":
151
+ moveFocus(addDays(focusedDate, 1));
152
+ break;
153
+ case "ArrowLeft":
154
+ moveFocus(subDays(focusedDate, 1));
155
+ break;
156
+ case "ArrowDown":
157
+ moveFocus(addDays(focusedDate, 7));
158
+ break;
159
+ case "ArrowUp":
160
+ moveFocus(subDays(focusedDate, 7));
161
+ break;
162
+ case "Home":
163
+ moveFocus(startOfWeek(focusedDate, weekStartsOn));
164
+ break;
165
+ case "End":
166
+ moveFocus(endOfWeek(focusedDate, weekStartsOn));
167
+ break;
168
+ case "PageDown":
169
+ if (event.shiftKey) {
170
+ moveFocus(addYears(focusedDate, 1));
171
+ } else {
172
+ moveFocus(addMonths(focusedDate, 1));
173
+ }
174
+ break;
175
+ case "PageUp":
176
+ if (event.shiftKey) {
177
+ moveFocus(subYears(focusedDate, 1));
178
+ } else {
179
+ moveFocus(subMonths(focusedDate, 1));
180
+ }
181
+ break;
182
+ case "Enter":
183
+ case " ":
184
+ if (isValid(focusedDate)) {
185
+ onSelect(focusedDate);
186
+ }
187
+ break;
188
+ case "Escape":
189
+ onClose?.();
190
+ break;
191
+ default:
192
+ handled = false;
193
+ }
194
+ if (handled) {
195
+ event.preventDefault();
196
+ event.stopPropagation();
197
+ }
198
+ },
199
+ [focusedDate, onFocusChange, onSelect, onClose, weekStartsOn, minDate, maxDate, disabled]
200
+ );
201
+ return { handleKeyDown };
202
+ }
203
+ function useDatePicker(options = {}) {
204
+ const {
205
+ closeOnSelect = true,
206
+ clearable = false,
207
+ required = false,
208
+ readOnly = false,
209
+ placeholder,
210
+ name,
211
+ locale = defaultLocale,
212
+ ...calendarOptions
213
+ } = options;
214
+ const format = options.format ?? locale.dateFormat;
215
+ const calendar = useCalendar({ ...calendarOptions, locale });
216
+ const [selectedDate, setSelectedDate] = useControllable({
217
+ value: options.value,
218
+ defaultValue: options.defaultValue ?? null,
219
+ onChange: options.onChange
220
+ });
221
+ const [isOpen, setIsOpen] = useControllable({
222
+ value: options.open,
223
+ defaultValue: options.defaultOpen ?? false,
224
+ onChange: options.onOpenChange
225
+ });
226
+ const [inputValue, setInputValue] = useState("");
227
+ const [isInputFocused, setIsInputFocused] = useState(false);
228
+ const formattedValue = useMemo(
229
+ () => selectedDate ? formatDate(selectedDate, format, locale.code) : "",
230
+ [selectedDate, format, locale.code]
231
+ );
232
+ const open = useCallback(() => {
233
+ if (!readOnly) setIsOpen(true);
234
+ }, [readOnly, setIsOpen]);
235
+ const close = useCallback(() => {
236
+ setIsOpen(false);
237
+ setInputValue("");
238
+ }, [setIsOpen]);
239
+ const toggle = useCallback(() => {
240
+ if (isOpen) close();
241
+ else open();
242
+ }, [isOpen, open, close]);
243
+ const selectDate = useCallback(
244
+ (date) => {
245
+ if (calendar.isDateDisabled(date)) return;
246
+ setSelectedDate(date);
247
+ if (closeOnSelect) close();
248
+ },
249
+ [calendar.isDateDisabled, setSelectedDate, closeOnSelect, close]
250
+ );
251
+ const clearDate = useCallback(() => {
252
+ setSelectedDate(null);
253
+ setInputValue("");
254
+ }, [setSelectedDate]);
255
+ const isSelected = useCallback(
256
+ (date) => selectedDate ? isSameDay(date, selectedDate) : false,
257
+ [selectedDate]
258
+ );
259
+ const handleInputChange = useCallback(
260
+ (value) => {
261
+ setInputValue(value);
262
+ const parsed = parseDate(value, format, locale.code);
263
+ if (parsed && !calendar.isDateDisabled(parsed)) {
264
+ setSelectedDate(parsed);
265
+ calendar.goToDate(parsed);
266
+ }
267
+ },
268
+ [format, locale.code, calendar, setSelectedDate]
269
+ );
270
+ const handleInputBlur = useCallback(() => {
271
+ setIsInputFocused(false);
272
+ setInputValue("");
273
+ }, []);
274
+ const handleInputFocus = useCallback(() => {
275
+ setIsInputFocused(true);
276
+ }, []);
277
+ const getInputProps = useCallback(
278
+ () => ({
279
+ type: "text",
280
+ value: isInputFocused && inputValue ? inputValue : formattedValue,
281
+ placeholder: placeholder ?? format,
282
+ readOnly,
283
+ "aria-label": placeholder ?? "Select date",
284
+ "aria-haspopup": "dialog",
285
+ "aria-expanded": isOpen,
286
+ autoComplete: "off",
287
+ onChange: (e) => handleInputChange(e.target.value),
288
+ onFocus: () => {
289
+ handleInputFocus();
290
+ open();
291
+ },
292
+ onBlur: handleInputBlur,
293
+ onClick: () => open()
294
+ }),
295
+ [formattedValue, inputValue, isInputFocused, placeholder, format, readOnly, isOpen, handleInputChange, handleInputFocus, handleInputBlur, open]
296
+ );
297
+ const getPopoverProps = useCallback(
298
+ () => ({
299
+ role: "dialog",
300
+ "aria-modal": true,
301
+ "aria-label": "Choose date"
302
+ }),
303
+ []
304
+ );
305
+ const getHiddenInputProps = useCallback(
306
+ () => ({
307
+ type: "hidden",
308
+ name,
309
+ value: selectedDate ? selectedDate.toISOString() : ""
310
+ }),
311
+ [name, selectedDate]
312
+ );
313
+ return {
314
+ ...calendar,
315
+ selectedDate,
316
+ selectDate,
317
+ clearDate,
318
+ formattedValue,
319
+ isOpen,
320
+ open,
321
+ close,
322
+ toggle,
323
+ isSelected,
324
+ getInputProps,
325
+ getPopoverProps,
326
+ getHiddenInputProps,
327
+ clearable,
328
+ required
329
+ };
330
+ }
331
+ function useRangePicker(options = {}) {
332
+ const {
333
+ minDays,
334
+ maxDays,
335
+ presets = [],
336
+ allowSameDay = true,
337
+ separator = " \u2013 ",
338
+ startName,
339
+ endName,
340
+ startPlaceholder,
341
+ endPlaceholder,
342
+ closeOnSelect = false,
343
+ locale = defaultLocale,
344
+ ...calendarOptions
345
+ } = options;
346
+ const format = options.format ?? locale.dateFormat;
347
+ const calendar = useCalendar({
348
+ ...calendarOptions,
349
+ numberOfMonths: calendarOptions.numberOfMonths ?? 2,
350
+ locale
351
+ });
352
+ const emptyRange = { from: null, to: null };
353
+ const [range, setRange] = useControllable({
354
+ value: options.value,
355
+ defaultValue: options.defaultValue ?? emptyRange,
356
+ onChange: options.onChange
357
+ });
358
+ const [isOpen, setIsOpen] = useControllable({
359
+ value: options.open,
360
+ defaultValue: options.defaultOpen ?? false,
361
+ onChange: options.onOpenChange
362
+ });
363
+ const [hoveredDate, setHoveredDate] = useState(null);
364
+ const [activeInput, setActiveInput] = useState("start");
365
+ const isValidRange = useCallback(
366
+ (from, to) => {
367
+ const days = Math.abs(differenceInDays(to, from)) + 1;
368
+ if (!allowSameDay && days === 1) return false;
369
+ if (minDays && days < minDays) return false;
370
+ if (maxDays && days > maxDays) return false;
371
+ return true;
372
+ },
373
+ [allowSameDay, minDays, maxDays]
374
+ );
375
+ const handleDayClick = useCallback(
376
+ (date) => {
377
+ if (calendar.isDateDisabled(date)) return;
378
+ if (!range.from || range.from && range.to) {
379
+ setRange({ from: date, to: null });
380
+ setActiveInput("end");
381
+ } else {
382
+ let from = range.from;
383
+ let to = date;
384
+ if (isBefore(to, from)) {
385
+ [from, to] = [to, from];
386
+ }
387
+ if (isValidRange(from, to)) {
388
+ setRange({ from, to });
389
+ setActiveInput("start");
390
+ if (closeOnSelect) {
391
+ setIsOpen(false);
392
+ }
393
+ } else {
394
+ setRange({ from: date, to: null });
395
+ setActiveInput("end");
396
+ }
397
+ }
398
+ },
399
+ [range, calendar.isDateDisabled, isValidRange, setRange, closeOnSelect, setIsOpen]
400
+ );
401
+ const applyPreset = useCallback(
402
+ (preset) => {
403
+ const value = typeof preset.value === "function" ? preset.value() : preset.value;
404
+ setRange(value);
405
+ if (closeOnSelect) {
406
+ setIsOpen(false);
407
+ }
408
+ },
409
+ [setRange, closeOnSelect, setIsOpen]
410
+ );
411
+ const activePresetIndex = useMemo(() => {
412
+ if (!range.from || !range.to) return -1;
413
+ return presets.findIndex((preset) => {
414
+ const resolved = typeof preset.value === "function" ? preset.value() : preset.value;
415
+ if (!resolved.from || !resolved.to) return false;
416
+ return isSameDay(range.from, resolved.from) && isSameDay(range.to, resolved.to);
417
+ });
418
+ }, [range.from, range.to, presets]);
419
+ const clearRange = useCallback(() => {
420
+ setRange(emptyRange);
421
+ setActiveInput("start");
422
+ }, [setRange]);
423
+ const isRangeStart = useCallback(
424
+ (date) => range.from ? isSameDay(date, range.from) : false,
425
+ [range.from]
426
+ );
427
+ const isRangeEnd = useCallback(
428
+ (date) => range.to ? isSameDay(date, range.to) : false,
429
+ [range.to]
430
+ );
431
+ const isInRange = useCallback(
432
+ (date) => {
433
+ if (!range.from || !range.to) return false;
434
+ return isBetween(date, range.from, range.to, false) && !isSameDay(date, range.from) && !isSameDay(date, range.to);
435
+ },
436
+ [range.from, range.to]
437
+ );
438
+ const isInPreviewRange = useCallback(
439
+ (date) => {
440
+ if (!range.from || range.to || !hoveredDate) return false;
441
+ const start = isBefore(hoveredDate, range.from) ? hoveredDate : range.from;
442
+ const end = isAfter(hoveredDate, range.from) ? hoveredDate : range.from;
443
+ return isBetween(date, start, end);
444
+ },
445
+ [range.from, range.to, hoveredDate]
446
+ );
447
+ const isSelected = useCallback(
448
+ (date) => isRangeStart(date) || isRangeEnd(date),
449
+ [isRangeStart, isRangeEnd]
450
+ );
451
+ const formattedStart = useMemo(
452
+ () => range.from ? formatDate(range.from, format, locale.code) : "",
453
+ [range.from, format, locale.code]
454
+ );
455
+ const formattedEnd = useMemo(
456
+ () => range.to ? formatDate(range.to, format, locale.code) : "",
457
+ [range.to, format, locale.code]
458
+ );
459
+ const open = useCallback(() => setIsOpen(true), [setIsOpen]);
460
+ const close = useCallback(() => setIsOpen(false), [setIsOpen]);
461
+ const toggle = useCallback(() => setIsOpen(!isOpen), [isOpen, setIsOpen]);
462
+ const getStartInputProps = useCallback(
463
+ () => ({
464
+ type: "text",
465
+ value: formattedStart,
466
+ placeholder: startPlaceholder ?? locale.labels.startDate,
467
+ readOnly: true,
468
+ "aria-label": locale.labels.startDate,
469
+ onClick: () => {
470
+ setActiveInput("start");
471
+ open();
472
+ }
473
+ }),
474
+ [formattedStart, startPlaceholder, locale.labels.startDate, open]
475
+ );
476
+ const getEndInputProps = useCallback(
477
+ () => ({
478
+ type: "text",
479
+ value: formattedEnd,
480
+ placeholder: endPlaceholder ?? locale.labels.endDate,
481
+ readOnly: true,
482
+ "aria-label": locale.labels.endDate,
483
+ onClick: () => {
484
+ setActiveInput("end");
485
+ open();
486
+ }
487
+ }),
488
+ [formattedEnd, endPlaceholder, locale.labels.endDate, open]
489
+ );
490
+ const getPopoverProps = useCallback(
491
+ () => ({
492
+ role: "dialog",
493
+ "aria-modal": true,
494
+ "aria-label": "Choose date range"
495
+ }),
496
+ []
497
+ );
498
+ const getPresetButtonProps = useCallback(
499
+ (preset) => ({
500
+ type: "button",
501
+ onClick: () => applyPreset(preset),
502
+ "aria-label": preset.label
503
+ }),
504
+ [applyPreset]
505
+ );
506
+ return {
507
+ ...calendar,
508
+ range,
509
+ hoveredDate,
510
+ activeInput,
511
+ handleDayClick,
512
+ applyPreset,
513
+ clearRange,
514
+ setHoveredDate,
515
+ isRangeStart,
516
+ isRangeEnd,
517
+ isInRange,
518
+ isInPreviewRange,
519
+ isSelected,
520
+ formattedStart,
521
+ formattedEnd,
522
+ isOpen,
523
+ open,
524
+ close,
525
+ toggle,
526
+ presets,
527
+ separator,
528
+ activePresetIndex,
529
+ getStartInputProps,
530
+ getEndInputProps,
531
+ getPopoverProps,
532
+ getPresetButtonProps
533
+ };
534
+ }
535
+ function useMultiPicker(options = {}) {
536
+ const {
537
+ maxSelections,
538
+ locale = defaultLocale,
539
+ ...calendarOptions
540
+ } = options;
541
+ const format = options.format ?? locale.dateFormat;
542
+ const calendar = useCalendar({ ...calendarOptions, locale });
543
+ const [selectedDates, setSelectedDates] = useControllable({
544
+ value: options.value,
545
+ defaultValue: options.defaultValue ?? [],
546
+ onChange: options.onChange
547
+ });
548
+ const [isOpen, setIsOpen] = useControllable({
549
+ value: options.open,
550
+ defaultValue: options.defaultOpen ?? false,
551
+ onChange: options.onOpenChange
552
+ });
553
+ const toggleDate = useCallback(
554
+ (date) => {
555
+ if (calendar.isDateDisabled(date)) return;
556
+ const index = selectedDates.findIndex((d) => isSameDay(d, date));
557
+ if (index >= 0) {
558
+ setSelectedDates(selectedDates.filter((_, i) => i !== index));
559
+ } else {
560
+ if (maxSelections && selectedDates.length >= maxSelections) return;
561
+ setSelectedDates([...selectedDates, date]);
562
+ }
563
+ },
564
+ [selectedDates, maxSelections, calendar.isDateDisabled, setSelectedDates]
565
+ );
566
+ const removeDate = useCallback(
567
+ (date) => {
568
+ setSelectedDates(selectedDates.filter((d) => !isSameDay(d, date)));
569
+ },
570
+ [selectedDates, setSelectedDates]
571
+ );
572
+ const clearAll = useCallback(() => {
573
+ setSelectedDates([]);
574
+ }, [setSelectedDates]);
575
+ const isSelected = useCallback(
576
+ (date) => selectedDates.some((d) => isSameDay(d, date)),
577
+ [selectedDates]
578
+ );
579
+ const formattedValues = useMemo(
580
+ () => selectedDates.map((d) => formatDate(d, format, locale.code)),
581
+ [selectedDates, format, locale.code]
582
+ );
583
+ const open = useCallback(() => setIsOpen(true), [setIsOpen]);
584
+ const close = useCallback(() => setIsOpen(false), [setIsOpen]);
585
+ const toggle = useCallback(() => setIsOpen(!isOpen), [isOpen, setIsOpen]);
586
+ return {
587
+ ...calendar,
588
+ selectedDates,
589
+ toggleDate,
590
+ removeDate,
591
+ clearAll,
592
+ isSelected,
593
+ formattedValues,
594
+ isOpen,
595
+ open,
596
+ close,
597
+ toggle
598
+ };
599
+ }
600
+ function useTimePicker(options = {}) {
601
+ const {
602
+ format = "12h",
603
+ minuteStep = 1,
604
+ secondStep = 1,
605
+ showSeconds = false,
606
+ minTime,
607
+ maxTime,
608
+ name,
609
+ placeholder
610
+ } = options;
611
+ const [selectedTime, setSelectedTime] = useControllable({
612
+ value: options.value,
613
+ defaultValue: options.defaultValue ?? null,
614
+ onChange: options.onChange
615
+ });
616
+ const [isOpen, setIsOpen] = useControllable({
617
+ value: options.open,
618
+ defaultValue: options.defaultOpen ?? false,
619
+ onChange: options.onOpenChange
620
+ });
621
+ const hours24 = selectedTime?.getHours() ?? 0;
622
+ const minutes = selectedTime?.getMinutes() ?? 0;
623
+ const seconds = selectedTime?.getSeconds() ?? 0;
624
+ const { hours: display12h, meridiem } = to12Hour(hours24);
625
+ const displayHours = format === "12h" ? display12h : hours24;
626
+ const updateTime = useCallback(
627
+ (h, m, s) => {
628
+ const base = selectedTime ? clone(selectedTime) : /* @__PURE__ */ new Date();
629
+ base.setHours(h, m, s, 0);
630
+ setSelectedTime(base);
631
+ },
632
+ [selectedTime, setSelectedTime]
633
+ );
634
+ const setHours = useCallback(
635
+ (h) => {
636
+ const h24 = format === "12h" ? to24Hour(h, meridiem) : h;
637
+ updateTime(h24, minutes, seconds);
638
+ },
639
+ [format, meridiem, minutes, seconds, updateTime]
640
+ );
641
+ const setMinutes = useCallback(
642
+ (m) => updateTime(hours24, m, seconds),
643
+ [hours24, seconds, updateTime]
644
+ );
645
+ const setSeconds = useCallback(
646
+ (s) => updateTime(hours24, minutes, s),
647
+ [hours24, minutes, updateTime]
648
+ );
649
+ const setMeridiem = useCallback(
650
+ (m) => {
651
+ const h = to24Hour(display12h, m);
652
+ updateTime(h, minutes, seconds);
653
+ },
654
+ [display12h, minutes, seconds, updateTime]
655
+ );
656
+ const availableHours = useMemo(() => generateHours(format), [format]);
657
+ const availableMinutes = useMemo(() => generateMinutes(minuteStep), [minuteStep]);
658
+ const availableSeconds = useMemo(() => generateSeconds(secondStep), [secondStep]);
659
+ const formattedValue = useMemo(
660
+ () => selectedTime ? formatTime(selectedTime, format) : "",
661
+ [selectedTime, format]
662
+ );
663
+ const open = useCallback(() => setIsOpen(true), [setIsOpen]);
664
+ const close = useCallback(() => setIsOpen(false), [setIsOpen]);
665
+ const toggle = useCallback(() => setIsOpen(!isOpen), [isOpen, setIsOpen]);
666
+ const getInputProps = useCallback(
667
+ () => ({
668
+ type: "text",
669
+ value: formattedValue,
670
+ placeholder: placeholder ?? (format === "12h" ? "hh:mm AM" : "HH:mm"),
671
+ readOnly: true,
672
+ "aria-label": placeholder ?? "Select time",
673
+ "aria-haspopup": "listbox",
674
+ "aria-expanded": isOpen,
675
+ onClick: () => open()
676
+ }),
677
+ [formattedValue, placeholder, format, isOpen, open]
678
+ );
679
+ const getTimeOptionProps = useCallback(
680
+ (type, value) => {
681
+ const isActive = type === "hours" ? displayHours === value : type === "minutes" ? minutes === value : type === "seconds" ? seconds === value : meridiem === value;
682
+ return {
683
+ type: "button",
684
+ role: "option",
685
+ "aria-selected": isActive,
686
+ "data-active": isActive || void 0,
687
+ onClick: () => {
688
+ switch (type) {
689
+ case "hours":
690
+ setHours(value);
691
+ break;
692
+ case "minutes":
693
+ setMinutes(value);
694
+ break;
695
+ case "seconds":
696
+ setSeconds(value);
697
+ break;
698
+ case "meridiem":
699
+ setMeridiem(value);
700
+ break;
701
+ }
702
+ }
703
+ };
704
+ },
705
+ [displayHours, minutes, seconds, meridiem, setHours, setMinutes, setSeconds, setMeridiem]
706
+ );
707
+ return {
708
+ hours: displayHours,
709
+ minutes,
710
+ seconds,
711
+ meridiem,
712
+ setHours,
713
+ setMinutes,
714
+ setSeconds,
715
+ setMeridiem,
716
+ formattedValue,
717
+ availableHours,
718
+ availableMinutes,
719
+ availableSeconds,
720
+ showSeconds,
721
+ format,
722
+ isOpen,
723
+ open,
724
+ close,
725
+ toggle,
726
+ getInputProps,
727
+ getTimeOptionProps,
728
+ selectedTime
729
+ };
730
+ }
731
+ function useMonthPicker(options = {}) {
732
+ const { minDate, maxDate, locale = defaultLocale } = options;
733
+ const format = options.format ?? "MMM YYYY";
734
+ const [selectedDate, setSelectedDate] = useControllable({
735
+ value: options.value,
736
+ defaultValue: options.defaultValue ?? null,
737
+ onChange: options.onChange
738
+ });
739
+ const [isOpen, setIsOpen] = useControllable({
740
+ value: options.open,
741
+ defaultValue: options.defaultOpen ?? false,
742
+ onChange: options.onOpenChange
743
+ });
744
+ const [viewingYear, setViewingYear] = useState(
745
+ () => selectedDate?.getFullYear() ?? (/* @__PURE__ */ new Date()).getFullYear()
746
+ );
747
+ const monthGrid = useMemo(() => {
748
+ const grid = generateMonthGrid(viewingYear);
749
+ return grid.map((item) => {
750
+ const date = createDate(viewingYear, item.month, 1);
751
+ const isDisabled = (minDate ? isBefore(date, startOfMonth(minDate)) : false) || (maxDate ? isAfter(date, maxDate) : false);
752
+ const isSelected = selectedDate ? isSameMonth(date, selectedDate) : false;
753
+ return { ...item, isDisabled, isSelected, date };
754
+ });
755
+ }, [viewingYear, minDate, maxDate, selectedDate]);
756
+ const goToNextYear = useCallback(() => setViewingYear((y) => y + 1), []);
757
+ const goToPrevYear = useCallback(() => setViewingYear((y) => y - 1), []);
758
+ const selectMonth = useCallback(
759
+ (month) => {
760
+ const date = createDate(viewingYear, month, 1);
761
+ setSelectedDate(date);
762
+ },
763
+ [viewingYear, setSelectedDate]
764
+ );
765
+ const formattedValue = useMemo(
766
+ () => selectedDate ? formatDate(selectedDate, format, locale.code) : "",
767
+ [selectedDate, format, locale.code]
768
+ );
769
+ const open = useCallback(() => setIsOpen(true), [setIsOpen]);
770
+ const close = useCallback(() => setIsOpen(false), [setIsOpen]);
771
+ const toggle = useCallback(() => setIsOpen(!isOpen), [isOpen, setIsOpen]);
772
+ const getInputProps = useCallback(
773
+ () => ({
774
+ type: "text",
775
+ value: formattedValue,
776
+ readOnly: true,
777
+ "aria-label": locale.labels.selectMonth,
778
+ "aria-haspopup": "dialog",
779
+ "aria-expanded": isOpen,
780
+ onClick: () => open()
781
+ }),
782
+ [formattedValue, locale.labels.selectMonth, isOpen, open]
783
+ );
784
+ const getMonthButtonProps = useCallback(
785
+ (month) => {
786
+ const item = monthGrid.find((m) => m.month === month);
787
+ return {
788
+ type: "button",
789
+ "aria-selected": item?.isSelected ?? false,
790
+ "aria-disabled": item?.isDisabled ?? false,
791
+ disabled: item?.isDisabled ?? false,
792
+ onClick: () => {
793
+ if (!item?.isDisabled) {
794
+ selectMonth(month);
795
+ close();
796
+ }
797
+ }
798
+ };
799
+ },
800
+ [monthGrid, selectMonth, close]
801
+ );
802
+ return {
803
+ viewingYear,
804
+ selectedDate,
805
+ monthGrid,
806
+ goToNextYear,
807
+ goToPrevYear,
808
+ selectMonth,
809
+ formattedValue,
810
+ isOpen,
811
+ open,
812
+ close,
813
+ toggle,
814
+ getInputProps,
815
+ getMonthButtonProps,
816
+ locale
817
+ };
818
+ }
819
+ function useYearPicker(options = {}) {
820
+ const { minYear, maxYear, yearsPerPage = 12 } = options;
821
+ const [selectedYear, setSelectedYear] = useControllable({
822
+ value: options.value,
823
+ defaultValue: options.defaultValue ?? null,
824
+ onChange: options.onChange
825
+ });
826
+ const [isOpen, setIsOpen] = useControllable({
827
+ value: options.open,
828
+ defaultValue: options.defaultOpen ?? false,
829
+ onChange: options.onOpenChange
830
+ });
831
+ const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
832
+ const [centerYear, setCenterYear] = useState(selectedYear ?? currentYear);
833
+ const years = useMemo(() => {
834
+ const grid = generateYearGrid(centerYear, yearsPerPage);
835
+ return grid.map((item) => ({
836
+ ...item,
837
+ isDisabled: minYear != null && item.year < minYear || maxYear != null && item.year > maxYear,
838
+ isSelected: selectedYear === item.year,
839
+ isCurrentYear: item.year === currentYear
840
+ }));
841
+ }, [centerYear, yearsPerPage, minYear, maxYear, selectedYear, currentYear]);
842
+ const decadeStart = years[0]?.year ?? centerYear;
843
+ const goToNextDecade = useCallback(
844
+ () => setCenterYear((y) => y + yearsPerPage),
845
+ [yearsPerPage]
846
+ );
847
+ const goToPrevDecade = useCallback(
848
+ () => setCenterYear((y) => y - yearsPerPage),
849
+ [yearsPerPage]
850
+ );
851
+ const selectYear = useCallback(
852
+ (year) => setSelectedYear(year),
853
+ [setSelectedYear]
854
+ );
855
+ const open = useCallback(() => setIsOpen(true), [setIsOpen]);
856
+ const close = useCallback(() => setIsOpen(false), [setIsOpen]);
857
+ const toggle = useCallback(() => setIsOpen(!isOpen), [isOpen, setIsOpen]);
858
+ const getInputProps = useCallback(
859
+ () => ({
860
+ type: "text",
861
+ value: selectedYear != null ? String(selectedYear) : "",
862
+ readOnly: true,
863
+ "aria-label": "Select year",
864
+ "aria-haspopup": "dialog",
865
+ "aria-expanded": isOpen,
866
+ onClick: () => open()
867
+ }),
868
+ [selectedYear, isOpen, open]
869
+ );
870
+ const getYearButtonProps = useCallback(
871
+ (year) => {
872
+ const item = years.find((y) => y.year === year);
873
+ return {
874
+ type: "button",
875
+ "aria-selected": item?.isSelected ?? false,
876
+ "aria-disabled": item?.isDisabled ?? false,
877
+ disabled: item?.isDisabled ?? false,
878
+ onClick: () => {
879
+ if (!item?.isDisabled) {
880
+ selectYear(year);
881
+ close();
882
+ }
883
+ }
884
+ };
885
+ },
886
+ [years, selectYear, close]
887
+ );
888
+ return {
889
+ decadeStart,
890
+ selectedYear,
891
+ years,
892
+ goToNextDecade,
893
+ goToPrevDecade,
894
+ selectYear,
895
+ isOpen,
896
+ open,
897
+ close,
898
+ toggle,
899
+ getInputProps,
900
+ getYearButtonProps
901
+ };
902
+ }
903
+ function useWeekPicker(options = {}) {
904
+ const { locale = defaultLocale, ...calendarOptions } = options;
905
+ const format = options.format ?? locale.dateFormat;
906
+ const weekStartsOn = options.weekStartsOn ?? locale.weekStartsOn;
907
+ const calendar = useCalendar({
908
+ ...calendarOptions,
909
+ locale,
910
+ showWeekNumbers: true,
911
+ weekStartsOn
912
+ });
913
+ const [selectedWeekDate, setSelectedWeekDate] = useControllable({
914
+ value: options.value,
915
+ defaultValue: options.defaultValue ?? null,
916
+ onChange: void 0
917
+ });
918
+ const [isOpen, setIsOpen] = useControllable({
919
+ value: options.open,
920
+ defaultValue: options.defaultOpen ?? false,
921
+ onChange: options.onOpenChange
922
+ });
923
+ const selectedWeek = useMemo(() => {
924
+ if (!selectedWeekDate) return null;
925
+ const start = startOfWeek(selectedWeekDate, weekStartsOn);
926
+ const end = endOfWeek(selectedWeekDate, weekStartsOn);
927
+ const weekNumber = getWeekNumber(selectedWeekDate, weekStartsOn);
928
+ return { start, end, weekNumber };
929
+ }, [selectedWeekDate, weekStartsOn]);
930
+ const selectWeek = useCallback(
931
+ (date) => {
932
+ if (calendar.isDateDisabled(date)) return;
933
+ setSelectedWeekDate(date);
934
+ const start = startOfWeek(date, weekStartsOn);
935
+ const end = endOfWeek(date, weekStartsOn);
936
+ const weekNumber = getWeekNumber(date, weekStartsOn);
937
+ options.onChange?.(start, end, weekNumber);
938
+ },
939
+ [calendar.isDateDisabled, weekStartsOn, options.onChange, setSelectedWeekDate]
940
+ );
941
+ const isInSelectedWeek = useCallback(
942
+ (date) => {
943
+ if (!selectedWeek) return false;
944
+ const weekDates = getWeekDates(selectedWeek.start, weekStartsOn);
945
+ return weekDates.some((d) => isSameDay(d, date));
946
+ },
947
+ [selectedWeek, weekStartsOn]
948
+ );
949
+ const formattedValue = useMemo(() => {
950
+ if (!selectedWeek) return "";
951
+ return `${formatDate(selectedWeek.start, format, locale.code)} \u2013 ${formatDate(selectedWeek.end, format, locale.code)}`;
952
+ }, [selectedWeek, format, locale.code]);
953
+ const open = useCallback(() => setIsOpen(true), [setIsOpen]);
954
+ const close = useCallback(() => setIsOpen(false), [setIsOpen]);
955
+ const toggle = useCallback(() => setIsOpen(!isOpen), [isOpen, setIsOpen]);
956
+ const getInputProps = useCallback(
957
+ () => ({
958
+ type: "text",
959
+ value: formattedValue,
960
+ readOnly: true,
961
+ "aria-label": "Select week",
962
+ "aria-haspopup": "dialog",
963
+ "aria-expanded": isOpen,
964
+ onClick: () => open()
965
+ }),
966
+ [formattedValue, isOpen, open]
967
+ );
968
+ return {
969
+ ...calendar,
970
+ selectedWeek,
971
+ selectWeek,
972
+ isInSelectedWeek,
973
+ formattedValue,
974
+ isOpen,
975
+ open,
976
+ close,
977
+ toggle,
978
+ getInputProps
979
+ };
980
+ }
981
+ function useDateTimePicker(options = {}) {
982
+ const {
983
+ timeFormat = "12h",
984
+ minuteStep = 1,
985
+ showSeconds = false,
986
+ locale = defaultLocale,
987
+ ...dateOptions
988
+ } = options;
989
+ const dateFormat = options.dateFormat ?? locale.dateFormat;
990
+ const datePicker = useDatePicker({
991
+ ...dateOptions,
992
+ format: dateFormat,
993
+ closeOnSelect: false,
994
+ locale
995
+ });
996
+ const [activeTab, setActiveTab] = useState("date");
997
+ const selectedDate = datePicker.selectedDate;
998
+ const hours24 = selectedDate?.getHours() ?? 0;
999
+ const minutes = selectedDate?.getMinutes() ?? 0;
1000
+ const seconds = selectedDate?.getSeconds() ?? 0;
1001
+ const { hours: display12h, meridiem } = to12Hour(hours24);
1002
+ const displayHours = timeFormat === "12h" ? display12h : hours24;
1003
+ const updateTime = useCallback(
1004
+ (h, m, s) => {
1005
+ const base = selectedDate ? clone(selectedDate) : /* @__PURE__ */ new Date();
1006
+ base.setHours(h, m, s, 0);
1007
+ datePicker.selectDate(base);
1008
+ },
1009
+ [selectedDate, datePicker.selectDate]
1010
+ );
1011
+ const setHours = useCallback(
1012
+ (h) => {
1013
+ const h24 = timeFormat === "12h" ? to24Hour(h, meridiem) : h;
1014
+ updateTime(h24, minutes, seconds);
1015
+ },
1016
+ [timeFormat, meridiem, minutes, seconds, updateTime]
1017
+ );
1018
+ const setMinutes = useCallback(
1019
+ (m) => updateTime(hours24, m, seconds),
1020
+ [hours24, seconds, updateTime]
1021
+ );
1022
+ const setSeconds = useCallback(
1023
+ (s) => updateTime(hours24, minutes, s),
1024
+ [hours24, minutes, updateTime]
1025
+ );
1026
+ const setMeridiem = useCallback(
1027
+ (m) => {
1028
+ const h = to24Hour(display12h, m);
1029
+ updateTime(h, minutes, seconds);
1030
+ },
1031
+ [display12h, minutes, seconds, updateTime]
1032
+ );
1033
+ const formattedValue = useMemo(() => {
1034
+ if (!selectedDate) return "";
1035
+ const datePart = formatDate(selectedDate, dateFormat, locale.code);
1036
+ const timePart = formatTime(selectedDate, timeFormat);
1037
+ return `${datePart} ${timePart}`;
1038
+ }, [selectedDate, dateFormat, timeFormat, locale.code]);
1039
+ return {
1040
+ ...datePicker,
1041
+ formattedValue,
1042
+ hours: displayHours,
1043
+ minutes,
1044
+ seconds,
1045
+ meridiem,
1046
+ setHours,
1047
+ setMinutes,
1048
+ setSeconds,
1049
+ setMeridiem,
1050
+ activeTab,
1051
+ setActiveTab,
1052
+ timeFormat,
1053
+ minuteStep,
1054
+ showSeconds
1055
+ };
1056
+ }
1057
+ function useClickOutside(refs, handler, enabled = true) {
1058
+ const handlerRef = useRef(handler);
1059
+ handlerRef.current = handler;
1060
+ useEffect(() => {
1061
+ if (!enabled) return;
1062
+ const listener = (event) => {
1063
+ const target = event.target;
1064
+ const isOutside = refs.every((ref) => {
1065
+ return !ref.current || !ref.current.contains(target);
1066
+ });
1067
+ if (isOutside) {
1068
+ handlerRef.current(event);
1069
+ }
1070
+ };
1071
+ document.addEventListener("mousedown", listener);
1072
+ document.addEventListener("touchstart", listener);
1073
+ return () => {
1074
+ document.removeEventListener("mousedown", listener);
1075
+ document.removeEventListener("touchstart", listener);
1076
+ };
1077
+ }, [refs, enabled]);
1078
+ }
1079
+ function usePortal(options) {
1080
+ const { containerId = "brdp-portal", enabled = true } = options ?? {};
1081
+ const [portalTarget, setPortalTarget] = useState(null);
1082
+ useEffect(() => {
1083
+ if (!enabled) {
1084
+ setPortalTarget(null);
1085
+ return;
1086
+ }
1087
+ let container = document.getElementById(containerId);
1088
+ if (!container) {
1089
+ container = document.createElement("div");
1090
+ container.id = containerId;
1091
+ document.body.appendChild(container);
1092
+ }
1093
+ setPortalTarget(container);
1094
+ }, [containerId, enabled]);
1095
+ return {
1096
+ portalTarget,
1097
+ isPortalReady: !!portalTarget
1098
+ };
1099
+ }
1100
+ function useFocusTrap(containerRef, enabled = true) {
1101
+ const previousFocusRef = useRef(null);
1102
+ useEffect(() => {
1103
+ if (!enabled || !containerRef.current) return;
1104
+ previousFocusRef.current = document.activeElement;
1105
+ const container = containerRef.current;
1106
+ const focusableSelector = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
1107
+ const focusFirst = () => {
1108
+ const firstFocusable = container.querySelector(focusableSelector);
1109
+ firstFocusable?.focus();
1110
+ };
1111
+ const handleKeyDown = (event) => {
1112
+ if (event.key !== "Tab") return;
1113
+ const focusableElements = container.querySelectorAll(focusableSelector);
1114
+ if (focusableElements.length === 0) return;
1115
+ const firstFocusable = focusableElements[0];
1116
+ const lastFocusable = focusableElements[focusableElements.length - 1];
1117
+ if (event.shiftKey) {
1118
+ if (document.activeElement === firstFocusable) {
1119
+ event.preventDefault();
1120
+ lastFocusable.focus();
1121
+ }
1122
+ } else {
1123
+ if (document.activeElement === lastFocusable) {
1124
+ event.preventDefault();
1125
+ firstFocusable.focus();
1126
+ }
1127
+ }
1128
+ };
1129
+ focusFirst();
1130
+ container.addEventListener("keydown", handleKeyDown);
1131
+ return () => {
1132
+ container.removeEventListener("keydown", handleKeyDown);
1133
+ previousFocusRef.current?.focus();
1134
+ };
1135
+ }, [containerRef, enabled]);
1136
+ }
1137
+
1138
+ export { useCalendar, useClickOutside, useControllable, useDatePicker, useDateTimePicker, useFocusTrap, useKeyboardNav, useMonthPicker, useMultiPicker, usePortal, useRangePicker, useTimePicker, useWeekPicker, useYearPicker };
1139
+ //# sourceMappingURL=chunk-FBN3SFQJ.js.map
1140
+ //# sourceMappingURL=chunk-FBN3SFQJ.js.map