react-day-picker 9.6.2 → 9.6.4

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 (181) hide show
  1. package/dist/cjs/DayPicker.js +15 -3
  2. package/dist/cjs/DayPicker.js.map +1 -1
  3. package/dist/cjs/classes/DateLib.js +4 -4
  4. package/dist/cjs/classes/DateLib.js.map +1 -1
  5. package/dist/cjs/useAnimation.js +74 -42
  6. package/dist/cjs/useAnimation.js.map +1 -1
  7. package/dist/esm/DayPicker.js +15 -3
  8. package/dist/esm/DayPicker.js.map +1 -1
  9. package/dist/esm/classes/DateLib.js +1 -1
  10. package/dist/esm/classes/DateLib.js.map +1 -1
  11. package/dist/esm/useAnimation.js +74 -42
  12. package/dist/esm/useAnimation.js.map +1 -1
  13. package/package.json +2 -4
  14. package/src/.eslintignore +1 -0
  15. package/src/.eslintrc.cjs +27 -0
  16. package/src/DayPicker.test.tsx +199 -0
  17. package/src/DayPicker.tsx +630 -0
  18. package/src/UI.ts +365 -0
  19. package/src/classes/CalendarDay.test.ts +17 -0
  20. package/src/classes/CalendarDay.ts +61 -0
  21. package/src/classes/CalendarMonth.test.ts +28 -0
  22. package/src/classes/CalendarMonth.ts +15 -0
  23. package/src/classes/CalendarWeek.test.ts +21 -0
  24. package/src/classes/CalendarWeek.ts +13 -0
  25. package/src/classes/DateLib.ts +615 -0
  26. package/src/classes/index.ts +4 -0
  27. package/src/components/Button.tsx +13 -0
  28. package/src/components/CaptionLabel.tsx +13 -0
  29. package/src/components/Chevron.tsx +42 -0
  30. package/src/components/Day.tsx +28 -0
  31. package/src/components/DayButton.tsx +29 -0
  32. package/src/components/Dropdown.tsx +71 -0
  33. package/src/components/DropdownNav.tsx +13 -0
  34. package/src/components/Footer.tsx +13 -0
  35. package/src/components/Month.tsx +24 -0
  36. package/src/components/MonthCaption.tsx +23 -0
  37. package/src/components/MonthGrid.tsx +13 -0
  38. package/src/components/Months.tsx +13 -0
  39. package/src/components/MonthsDropdown.tsx +16 -0
  40. package/src/components/Nav.tsx +90 -0
  41. package/src/components/NextMonthButton.tsx +18 -0
  42. package/src/components/Option.tsx +13 -0
  43. package/src/components/PreviousMonthButton.tsx +20 -0
  44. package/src/components/Root.tsx +19 -0
  45. package/src/components/Select.tsx +13 -0
  46. package/src/components/Week.tsx +20 -0
  47. package/src/components/WeekNumber.tsx +21 -0
  48. package/src/components/WeekNumberHeader.tsx +15 -0
  49. package/src/components/Weekday.tsx +13 -0
  50. package/src/components/Weekdays.tsx +17 -0
  51. package/src/components/Weeks.tsx +13 -0
  52. package/src/components/YearsDropdown.tsx +16 -0
  53. package/src/components/custom-components.tsx +26 -0
  54. package/src/formatters/formatCaption.test.ts +27 -0
  55. package/src/formatters/formatCaption.ts +23 -0
  56. package/src/formatters/formatDay.test.ts +7 -0
  57. package/src/formatters/formatDay.ts +16 -0
  58. package/src/formatters/formatMonthDropdown.test.ts +19 -0
  59. package/src/formatters/formatMonthDropdown.ts +15 -0
  60. package/src/formatters/formatWeekNumber.test.ts +5 -0
  61. package/src/formatters/formatWeekNumber.ts +13 -0
  62. package/src/formatters/formatWeekNumberHeader.ts +10 -0
  63. package/src/formatters/formatWeekdayName.test.ts +15 -0
  64. package/src/formatters/formatWeekdayName.ts +16 -0
  65. package/src/formatters/formatYearDropdown.test.ts +7 -0
  66. package/src/formatters/formatYearDropdown.ts +21 -0
  67. package/src/formatters/index.ts +7 -0
  68. package/src/helpers/broadcastCalendar.test.ts +43 -0
  69. package/src/helpers/calculateFocusTarget.ts +51 -0
  70. package/src/helpers/endOfBroadcastWeek.test.ts +25 -0
  71. package/src/helpers/endOfBroadcastWeek.ts +16 -0
  72. package/src/helpers/getBroadcastWeeksInMonth.test.ts +23 -0
  73. package/src/helpers/getBroadcastWeeksInMonth.ts +31 -0
  74. package/src/helpers/getClassNamesForModifiers.ts +26 -0
  75. package/src/helpers/getComponents.ts +11 -0
  76. package/src/helpers/getDataAttributes.test.tsx +48 -0
  77. package/src/helpers/getDataAttributes.tsx +21 -0
  78. package/src/helpers/getDates.test.ts +190 -0
  79. package/src/helpers/getDates.ts +64 -0
  80. package/src/helpers/getDays.test.ts +30 -0
  81. package/src/helpers/getDays.ts +16 -0
  82. package/src/helpers/getDefaultClassNames.test.ts +47 -0
  83. package/src/helpers/getDefaultClassNames.ts +33 -0
  84. package/src/helpers/getDisplayMonths.test.ts +44 -0
  85. package/src/helpers/getDisplayMonths.ts +20 -0
  86. package/src/helpers/getFocusableDate.ts +59 -0
  87. package/src/helpers/getFormatters.test.ts +48 -0
  88. package/src/helpers/getFormatters.ts +19 -0
  89. package/src/helpers/getInitialMonth.test.ts +79 -0
  90. package/src/helpers/getInitialMonth.ts +41 -0
  91. package/src/helpers/getLabels.ts +10 -0
  92. package/src/helpers/getMonthOptions.test.ts +226 -0
  93. package/src/helpers/getMonthOptions.ts +37 -0
  94. package/src/helpers/getMonths.test.ts +88 -0
  95. package/src/helpers/getMonths.ts +90 -0
  96. package/src/helpers/getNavMonth.test.ts +253 -0
  97. package/src/helpers/getNavMonth.ts +70 -0
  98. package/src/helpers/getNextFocus.test.tsx +99 -0
  99. package/src/helpers/getNextFocus.tsx +67 -0
  100. package/src/helpers/getNextMonth.test.ts +101 -0
  101. package/src/helpers/getNextMonth.ts +45 -0
  102. package/src/helpers/getPossibleFocusDate.test.ts +144 -0
  103. package/src/helpers/getPreviousMonth.test.ts +77 -0
  104. package/src/helpers/getPreviousMonth.ts +40 -0
  105. package/src/helpers/getStyleForModifiers.test.ts +92 -0
  106. package/src/helpers/getStyleForModifiers.ts +21 -0
  107. package/src/helpers/getWeekdays.test.ts +44 -0
  108. package/src/helpers/getWeekdays.ts +29 -0
  109. package/src/helpers/getWeeks.test.ts +30 -0
  110. package/src/helpers/getWeeks.ts +9 -0
  111. package/src/helpers/getYearOptions.test.ts +46 -0
  112. package/src/helpers/getYearOptions.ts +34 -0
  113. package/src/helpers/index.ts +2 -0
  114. package/src/helpers/startOfBroadcastWeek.test.ts +24 -0
  115. package/src/helpers/startOfBroadcastWeek.ts +19 -0
  116. package/src/helpers/useControlledValue.test.ts +45 -0
  117. package/src/helpers/useControlledValue.ts +33 -0
  118. package/src/index.ts +15 -0
  119. package/src/jalali.tsx +2 -0
  120. package/src/labels/index.ts +12 -0
  121. package/src/labels/labelDayButton.test.ts +41 -0
  122. package/src/labels/labelDayButton.ts +31 -0
  123. package/src/labels/labelGrid.test.ts +7 -0
  124. package/src/labels/labelGrid.ts +23 -0
  125. package/src/labels/labelGridcell.test.ts +7 -0
  126. package/src/labels/labelGridcell.ts +22 -0
  127. package/src/labels/labelMonthDropdown.test.ts +5 -0
  128. package/src/labels/labelMonthDropdown.ts +12 -0
  129. package/src/labels/labelNav.test.ts +5 -0
  130. package/src/labels/labelNav.ts +10 -0
  131. package/src/labels/labelNext.test.ts +5 -0
  132. package/src/labels/labelNext.ts +13 -0
  133. package/src/labels/labelPrevious.test.ts +5 -0
  134. package/src/labels/labelPrevious.ts +13 -0
  135. package/src/labels/labelWeekNumber.test.ts +5 -0
  136. package/src/labels/labelWeekNumber.ts +15 -0
  137. package/src/labels/labelWeekNumberHeader.test.ts +5 -0
  138. package/src/labels/labelWeekNumberHeader.ts +12 -0
  139. package/src/labels/labelWeekday.test.ts +15 -0
  140. package/src/labels/labelWeekday.ts +16 -0
  141. package/src/labels/labelYearDropdown.test.ts +5 -0
  142. package/src/labels/labelYearDropdown.ts +12 -0
  143. package/src/locale.ts +1 -0
  144. package/src/persian.tsx +86 -0
  145. package/src/selection/useMulti.test.tsx +41 -0
  146. package/src/selection/useMulti.tsx +74 -0
  147. package/src/selection/useRange.test.tsx +154 -0
  148. package/src/selection/useRange.tsx +73 -0
  149. package/src/selection/useSingle.test.tsx +38 -0
  150. package/src/selection/useSingle.tsx +69 -0
  151. package/src/types/deprecated.ts +230 -0
  152. package/src/types/index.ts +4 -0
  153. package/src/types/props.test.tsx +71 -0
  154. package/src/types/props.ts +675 -0
  155. package/src/types/selection.ts +57 -0
  156. package/src/types/shared.ts +442 -0
  157. package/src/useAnimation.test.tsx +190 -0
  158. package/src/useAnimation.ts +236 -0
  159. package/src/useCalendar.ts +178 -0
  160. package/src/useDayPicker.test.tsx +142 -0
  161. package/src/useDayPicker.ts +93 -0
  162. package/src/useFocus.ts +87 -0
  163. package/src/useGetModifiers.test.tsx +154 -0
  164. package/src/useGetModifiers.tsx +122 -0
  165. package/src/useSelection.ts +26 -0
  166. package/src/utc.tsx +10 -0
  167. package/src/utils/addToRange.test.ts +117 -0
  168. package/src/utils/addToRange.ts +87 -0
  169. package/src/utils/dateMatchModifiers.test.ts +120 -0
  170. package/src/utils/dateMatchModifiers.ts +88 -0
  171. package/src/utils/index.ts +7 -0
  172. package/src/utils/rangeContainsDayOfWeek.test.ts +48 -0
  173. package/src/utils/rangeContainsDayOfWeek.ts +35 -0
  174. package/src/utils/rangeContainsModifiers.test.ts +230 -0
  175. package/src/utils/rangeContainsModifiers.ts +125 -0
  176. package/src/utils/rangeIncludesDate.test.ts +46 -0
  177. package/src/utils/rangeIncludesDate.ts +43 -0
  178. package/src/utils/rangeOverlaps.test.ts +60 -0
  179. package/src/utils/rangeOverlaps.ts +22 -0
  180. package/src/utils/typeguards.test.ts +83 -0
  181. package/src/utils/typeguards.ts +70 -0
@@ -0,0 +1,87 @@
1
+ import { useState } from "react";
2
+
3
+ import type { CalendarDay, DateLib } from "./classes/index.js";
4
+ import { calculateFocusTarget } from "./helpers/calculateFocusTarget.js";
5
+ import { getNextFocus } from "./helpers/getNextFocus.js";
6
+ import type {
7
+ MoveFocusBy,
8
+ MoveFocusDir,
9
+ DayPickerProps,
10
+ Modifiers
11
+ } from "./types/index.js";
12
+ import { Calendar } from "./useCalendar.js";
13
+
14
+ export type UseFocus = {
15
+ /** The date that is currently focused. */
16
+ focused: CalendarDay | undefined;
17
+
18
+ /** Check if the given day is the focus target when entering the calendar. */
19
+ isFocusTarget: (day: CalendarDay) => boolean;
20
+
21
+ /** Focus the given day. */
22
+ setFocused: (day: CalendarDay | undefined) => void;
23
+
24
+ /** Blur the focused day. */
25
+ blur: () => void;
26
+
27
+ /** Move the current focus to the next day according to the given direction. */
28
+ moveFocus: (moveBy: MoveFocusBy, moveDir: MoveFocusDir) => void;
29
+ };
30
+
31
+ /** @private */
32
+ export function useFocus<T extends DayPickerProps>(
33
+ props: T,
34
+ calendar: Calendar,
35
+ getModifiers: (day: CalendarDay) => Modifiers,
36
+ isSelected: (date: Date) => boolean,
37
+ dateLib: DateLib
38
+ ): UseFocus {
39
+ const { autoFocus } = props;
40
+ const [lastFocused, setLastFocused] = useState<CalendarDay | undefined>();
41
+
42
+ const focusTarget = calculateFocusTarget(
43
+ calendar.days,
44
+ getModifiers,
45
+ isSelected || (() => false),
46
+ lastFocused
47
+ );
48
+ const [focusedDay, setFocused] = useState<CalendarDay | undefined>(
49
+ autoFocus ? focusTarget : undefined
50
+ );
51
+
52
+ const blur = () => {
53
+ setLastFocused(focusedDay);
54
+ setFocused(undefined);
55
+ };
56
+
57
+ const moveFocus = (moveBy: MoveFocusBy, moveDir: MoveFocusDir) => {
58
+ if (!focusedDay) return;
59
+ const nextFocus = getNextFocus(
60
+ moveBy,
61
+ moveDir,
62
+ focusedDay,
63
+ calendar.navStart,
64
+ calendar.navEnd,
65
+ props,
66
+ dateLib
67
+ );
68
+ if (!nextFocus) return;
69
+
70
+ calendar.goToDay(nextFocus);
71
+ setFocused(nextFocus);
72
+ };
73
+
74
+ const isFocusTarget = (day: CalendarDay) => {
75
+ return Boolean(focusTarget?.isEqualTo(day));
76
+ };
77
+
78
+ const useFocus: UseFocus = {
79
+ isFocusTarget,
80
+ setFocused,
81
+ focused: focusedDay,
82
+ blur,
83
+ moveFocus
84
+ };
85
+
86
+ return useFocus;
87
+ }
@@ -0,0 +1,154 @@
1
+ import { DayFlag } from "./UI";
2
+ import { CalendarDay, defaultDateLib } from "./classes/index";
3
+ import { useGetModifiers } from "./useGetModifiers";
4
+
5
+ const dateLib = defaultDateLib;
6
+
7
+ const displayedMonth = new Date(2022, 10, 1);
8
+
9
+ const date1 = new Date(2022, 9, 30);
10
+ const date2 = new Date(2022, 10, 10);
11
+ const date3 = new Date(2022, 10, 11);
12
+ const date4 = new Date(2022, 10, 12);
13
+ const date5 = new Date(2022, 10, 13);
14
+ const date6 = new Date(2022, 10, 14);
15
+ const date7 = new Date(2022, 11, 1);
16
+
17
+ const day1 = new CalendarDay(date1, displayedMonth);
18
+ const day2 = new CalendarDay(date2, displayedMonth);
19
+ const day3 = new CalendarDay(date3, displayedMonth);
20
+ const day4 = new CalendarDay(date4, displayedMonth);
21
+ const day5 = new CalendarDay(date5, displayedMonth);
22
+ const day6 = new CalendarDay(date6, displayedMonth);
23
+ const day7 = new CalendarDay(date7, displayedMonth);
24
+
25
+ const days: CalendarDay[] = [day1, day2, day3, day4, day5, day6, day7];
26
+
27
+ const props = {
28
+ disabled: [date2],
29
+ hidden: [date3],
30
+ modifiers: {
31
+ custom: [date4],
32
+ selected: [date6]
33
+ },
34
+ selected: date7,
35
+ showOutsideDays: true,
36
+ today: date5,
37
+ timeZone: "UTC"
38
+ };
39
+
40
+ describe("useGetModifiers", () => {
41
+ describe("default props", () => {
42
+ // eslint-disable-next-line react-hooks/rules-of-hooks
43
+ const getModifiers = useGetModifiers(days, props, dateLib);
44
+
45
+ test("return the modifiers for a given day", () => {
46
+ const modifiers = getModifiers(day2);
47
+
48
+ expect(modifiers[DayFlag.focused]).toBe(false);
49
+ expect(modifiers[DayFlag.disabled]).toBe(true);
50
+ expect(modifiers[DayFlag.hidden]).toBe(false);
51
+ expect(modifiers[DayFlag.outside]).toBe(false);
52
+ expect(modifiers[DayFlag.today]).toBe(false);
53
+ expect(modifiers.custom).toBe(false);
54
+ });
55
+
56
+ test("return the custom modifiers for a given day", () => {
57
+ const modifiers = getModifiers(day4);
58
+ expect(modifiers.custom).toBe(true);
59
+ });
60
+
61
+ test("return the custom `selected` modifier for a given day", () => {
62
+ const modifiers = getModifiers(day6);
63
+ expect(modifiers.selected).toBe(true);
64
+ });
65
+
66
+ test("return the today modifier for a given day", () => {
67
+ const modifiers = getModifiers(day5);
68
+
69
+ expect(modifiers[DayFlag.today]).toBe(true);
70
+ expect(modifiers[DayFlag.focused]).toBe(false);
71
+ expect(modifiers[DayFlag.disabled]).toBe(false);
72
+ expect(modifiers[DayFlag.outside]).toBe(false);
73
+ expect(modifiers[DayFlag.hidden]).toBe(false);
74
+ });
75
+
76
+ test("return the hidden modifier for a given day", () => {
77
+ const modifiers = getModifiers(day3);
78
+
79
+ expect(modifiers[DayFlag.hidden]).toBe(true);
80
+ expect(modifiers[DayFlag.focused]).toBe(false);
81
+ expect(modifiers[DayFlag.disabled]).toBe(false);
82
+ expect(modifiers[DayFlag.outside]).toBe(false);
83
+ expect(modifiers[DayFlag.today]).toBe(false);
84
+ });
85
+
86
+ test("return the modifiers for a given day before the displayed month", () => {
87
+ const modifiers = getModifiers(day1);
88
+
89
+ expect(modifiers[DayFlag.focused]).toBe(false);
90
+ expect(modifiers[DayFlag.disabled]).toBe(false);
91
+ expect(modifiers[DayFlag.hidden]).toBe(false);
92
+ expect(modifiers[DayFlag.outside]).toBe(true);
93
+ expect(modifiers[DayFlag.today]).toBe(false);
94
+ expect(modifiers.selected).toBe(false);
95
+ });
96
+
97
+ test("return the modifiers for a given day after the displayed month", () => {
98
+ const modifiers = getModifiers(day7);
99
+
100
+ expect(modifiers[DayFlag.focused]).toBe(false);
101
+ expect(modifiers[DayFlag.disabled]).toBe(false);
102
+ expect(modifiers[DayFlag.hidden]).toBe(false);
103
+ expect(modifiers[DayFlag.outside]).toBe(true);
104
+ expect(modifiers[DayFlag.today]).toBe(false);
105
+ expect(modifiers.selected).toBe(false);
106
+ });
107
+ });
108
+
109
+ describe("with startMonth and endMonth props", () => {
110
+ const startMonth = new Date(displayedMonth);
111
+ startMonth.setDate(30);
112
+ const endMonth = new Date(displayedMonth);
113
+ endMonth.setDate(1);
114
+
115
+ // eslint-disable-next-line react-hooks/rules-of-hooks
116
+ const getModifiers = useGetModifiers(
117
+ days,
118
+ { ...props, startMonth, endMonth },
119
+ dateLib
120
+ );
121
+ test("return the modifiers for a given day", () => {
122
+ const modifiers = getModifiers(day2);
123
+
124
+ expect(modifiers[DayFlag.focused]).toBe(false);
125
+ expect(modifiers[DayFlag.disabled]).toBe(true);
126
+ expect(modifiers[DayFlag.hidden]).toBe(false);
127
+ expect(modifiers[DayFlag.outside]).toBe(false);
128
+ expect(modifiers[DayFlag.today]).toBe(false);
129
+ expect(modifiers.custom).toBe(false);
130
+ });
131
+
132
+ test("return the modifiers for a given day before the displayed month", () => {
133
+ const modifiers = getModifiers(day1);
134
+
135
+ expect(modifiers[DayFlag.focused]).toBe(false);
136
+ expect(modifiers[DayFlag.disabled]).toBe(false);
137
+ expect(modifiers[DayFlag.hidden]).toBe(true);
138
+ expect(modifiers[DayFlag.outside]).toBe(true);
139
+ expect(modifiers[DayFlag.today]).toBe(false);
140
+ expect(modifiers.selected).toBe(false);
141
+ });
142
+
143
+ test("return the modifiers for a given day after the displayed month", () => {
144
+ const modifiers = getModifiers(day7);
145
+
146
+ expect(modifiers[DayFlag.focused]).toBe(false);
147
+ expect(modifiers[DayFlag.disabled]).toBe(false);
148
+ expect(modifiers[DayFlag.hidden]).toBe(true);
149
+ expect(modifiers[DayFlag.outside]).toBe(true);
150
+ expect(modifiers[DayFlag.today]).toBe(false);
151
+ expect(modifiers.selected).toBe(false);
152
+ });
153
+ });
154
+ });
@@ -0,0 +1,122 @@
1
+ import { DayFlag } from "./UI.js";
2
+ import type { CalendarDay, DateLib } from "./classes/index.js";
3
+ import type { DayPickerProps, Modifiers } from "./types/index.js";
4
+ import { dateMatchModifiers } from "./utils/dateMatchModifiers.js";
5
+
6
+ /**
7
+ * Return a function to get the modifiers for a given day.
8
+ *
9
+ * NOTE: this is not an hook, but a factory for `getModifiers`.
10
+ *
11
+ * @private
12
+ */
13
+ export function useGetModifiers(
14
+ days: CalendarDay[],
15
+ props: DayPickerProps,
16
+ dateLib: DateLib
17
+ ) {
18
+ const {
19
+ disabled,
20
+ hidden,
21
+ modifiers,
22
+ showOutsideDays,
23
+ broadcastCalendar,
24
+ today
25
+ } = props;
26
+
27
+ const {
28
+ isSameDay,
29
+ isSameMonth,
30
+ startOfMonth,
31
+ isBefore,
32
+ endOfMonth,
33
+ isAfter
34
+ } = dateLib;
35
+
36
+ const startMonth = props.startMonth && startOfMonth(props.startMonth);
37
+ const endMonth = props.endMonth && endOfMonth(props.endMonth);
38
+
39
+ const internalModifiersMap: Record<DayFlag, CalendarDay[]> = {
40
+ [DayFlag.focused]: [],
41
+ [DayFlag.outside]: [],
42
+ [DayFlag.disabled]: [],
43
+ [DayFlag.hidden]: [],
44
+ [DayFlag.today]: []
45
+ };
46
+
47
+ const customModifiersMap: Record<string, CalendarDay[]> = {};
48
+
49
+ for (const day of days) {
50
+ const { date, displayMonth } = day;
51
+
52
+ const isOutside = Boolean(displayMonth && !isSameMonth(date, displayMonth));
53
+
54
+ const isBeforeStartMonth = Boolean(
55
+ startMonth && isBefore(date, startMonth)
56
+ );
57
+
58
+ const isAfterEndMonth = Boolean(endMonth && isAfter(date, endMonth));
59
+
60
+ const isDisabled = Boolean(
61
+ disabled && dateMatchModifiers(date, disabled, dateLib)
62
+ );
63
+
64
+ const isHidden =
65
+ Boolean(hidden && dateMatchModifiers(date, hidden, dateLib)) ||
66
+ isBeforeStartMonth ||
67
+ isAfterEndMonth ||
68
+ // Broadcast calendar will show outside days as default
69
+ (!broadcastCalendar && !showOutsideDays && isOutside) ||
70
+ (broadcastCalendar && showOutsideDays === false && isOutside);
71
+
72
+ const isToday = isSameDay(date, today ?? dateLib.today());
73
+
74
+ if (isOutside) internalModifiersMap.outside.push(day);
75
+ if (isDisabled) internalModifiersMap.disabled.push(day);
76
+ if (isHidden) internalModifiersMap.hidden.push(day);
77
+ if (isToday) internalModifiersMap.today.push(day);
78
+
79
+ // Add custom modifiers
80
+ if (modifiers) {
81
+ Object.keys(modifiers).forEach((name) => {
82
+ const modifierValue = modifiers?.[name];
83
+ const isMatch = modifierValue
84
+ ? dateMatchModifiers(date, modifierValue, dateLib)
85
+ : false;
86
+ if (!isMatch) return;
87
+ if (customModifiersMap[name]) {
88
+ customModifiersMap[name].push(day);
89
+ } else {
90
+ customModifiersMap[name] = [day];
91
+ }
92
+ });
93
+ }
94
+ }
95
+
96
+ return (day: CalendarDay): Modifiers => {
97
+ // Initialize all the modifiers to false
98
+ const dayFlags: Record<DayFlag, boolean> = {
99
+ [DayFlag.focused]: false,
100
+ [DayFlag.disabled]: false,
101
+ [DayFlag.hidden]: false,
102
+ [DayFlag.outside]: false,
103
+ [DayFlag.today]: false
104
+ };
105
+ const customModifiers: Modifiers = {};
106
+
107
+ // Find the modifiers for the given day
108
+ for (const name in internalModifiersMap) {
109
+ const days = internalModifiersMap[name as DayFlag];
110
+ dayFlags[name as DayFlag] = days.some((d) => d === day);
111
+ }
112
+ for (const name in customModifiersMap) {
113
+ customModifiers[name] = customModifiersMap[name].some((d) => d === day);
114
+ }
115
+
116
+ return {
117
+ ...dayFlags,
118
+ // custom modifiers should override all the previous ones
119
+ ...customModifiers
120
+ };
121
+ };
122
+ }
@@ -0,0 +1,26 @@
1
+ import { type DateLib } from "./classes/DateLib.js";
2
+ import { useMulti } from "./selection/useMulti.js";
3
+ import { useRange } from "./selection/useRange.js";
4
+ import { useSingle } from "./selection/useSingle.js";
5
+ import type { DayPickerProps } from "./types/index.js";
6
+ import { Selection } from "./types/selection.js";
7
+
8
+ export function useSelection<T extends DayPickerProps>(
9
+ props: T,
10
+ dateLib: DateLib
11
+ ): Selection<T> | undefined {
12
+ const single = useSingle(props, dateLib);
13
+ const multi = useMulti(props, dateLib);
14
+ const range = useRange(props, dateLib);
15
+
16
+ switch (props.mode) {
17
+ case "single":
18
+ return single;
19
+ case "multiple":
20
+ return multi;
21
+ case "range":
22
+ return range;
23
+ default:
24
+ return undefined;
25
+ }
26
+ }
package/src/utc.tsx ADDED
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+
3
+ import {
4
+ DayPicker as DayPickerComponent,
5
+ type DayPickerProps
6
+ } from "./index.js";
7
+
8
+ export function DayPicker(props: DayPickerProps) {
9
+ return <DayPickerComponent timeZone="utc" {...props} />;
10
+ }
@@ -0,0 +1,117 @@
1
+ import { addToRange } from "./addToRange";
2
+
3
+ describe("addToRange", () => {
4
+ test("add a date to an undefined range", () => {
5
+ const date = new Date(2022, 0, 1);
6
+ const range = addToRange(date, undefined);
7
+ expect(range).toEqual({ from: date, to: date });
8
+ });
9
+
10
+ test("add a date to an empty range", () => {
11
+ const date = new Date(2022, 0, 1);
12
+ const range = addToRange(date, { from: undefined, to: undefined });
13
+ expect(range).toEqual({ from: date, to: date });
14
+ });
15
+
16
+ test("add a date to an incomplete range with same start date", () => {
17
+ const date = new Date(2022, 0, 1);
18
+ const range = addToRange(date, { from: date, to: undefined });
19
+ expect(range).toEqual(undefined);
20
+ });
21
+
22
+ test("add a date to an incomplete range with earlier date", () => {
23
+ const from = new Date(2022, 0, 1);
24
+ const earlierDate = new Date(2021, 11, 31);
25
+ const range = addToRange(earlierDate, { from: from, to: undefined });
26
+ expect(range).toEqual({ from: earlierDate, to: from });
27
+ });
28
+
29
+ test("add a date to an incomplete range with later date", () => {
30
+ const from = new Date(2022, 0, 1);
31
+ const date = new Date(2022, 0, 2);
32
+ const range = addToRange(date, { from: from, to: undefined });
33
+ expect(range).toEqual({ from: from, to: date });
34
+ });
35
+
36
+ test("add a date to a complete range with same start and end date", () => {
37
+ const date = new Date(2022, 0, 1);
38
+ const from = date;
39
+ const to = date;
40
+ const range = addToRange(date, { from, to }, 0, 0, false);
41
+ expect(range).toEqual(undefined);
42
+ });
43
+
44
+ test("add a date to a complete range with same start date", () => {
45
+ const date = new Date(2022, 0, 1);
46
+ const to = new Date(2022, 0, 2);
47
+ const range = addToRange(date, { from: date, to: to });
48
+ expect(range).toEqual({ from: date, to: date });
49
+ });
50
+
51
+ test("add a date to a complete range with same end date", () => {
52
+ const date = new Date(2022, 0, 2);
53
+ const from = new Date(2022, 0, 1);
54
+ const range = addToRange(date, { from: from, to: date });
55
+ expect(range).toEqual({ from: date, to: date });
56
+ });
57
+
58
+ test("add a date when inside the range", () => {
59
+ const date = new Date(2022, 0, 1);
60
+ const from = new Date(2021, 11, 31);
61
+ const to = new Date(2022, 0, 2);
62
+ const range = addToRange(date, { from, to });
63
+ expect(range).toEqual({ from, to: date });
64
+ });
65
+
66
+ test("add an earlier date to a complete range", () => {
67
+ const from = new Date(2022, 0, 1);
68
+ const to = new Date(2022, 0, 2);
69
+ const date = new Date(2021, 11, 31);
70
+ const range = addToRange(date, { from, to });
71
+ expect(range).toEqual({ from: date, to: to });
72
+ });
73
+
74
+ test("add a later date to a complete range", () => {
75
+ const date = new Date(2022, 0, 2);
76
+ const from = new Date(2021, 11, 31);
77
+ const to = new Date(2022, 0, 1);
78
+ const range = addToRange(date, { from, to });
79
+ expect(range).toEqual({ from: from, to: date });
80
+ });
81
+
82
+ test("add a date with min > 0", () => {
83
+ const date = new Date(2022, 0, 1);
84
+ const range = addToRange(date, undefined, 1, 0, false);
85
+ expect(range).toEqual({ from: date, to: undefined });
86
+ });
87
+
88
+ test("add a date with max > 0", () => {
89
+ const date = new Date(2022, 0, 1);
90
+ const range = addToRange(date, undefined, 0, 1, false);
91
+ expect(range).toEqual({ from: date, to: date });
92
+ });
93
+
94
+ test("add a date with required set to true", () => {
95
+ const date = new Date(2022, 0, 1);
96
+ const range = addToRange(date, undefined, 0, 0, true);
97
+ expect(range).toEqual({ from: date, to: date });
98
+ });
99
+
100
+ test("when exceeding max, set the start of the range", () => {
101
+ const from = new Date(2022, 0, 1);
102
+ const to = new Date(2022, 0, 2);
103
+ const date = new Date(2022, 0, 4);
104
+ const max = 2;
105
+ const range = addToRange(date, { from, to }, 0, max, false);
106
+ expect(range).toEqual({ from: date, to: undefined });
107
+ });
108
+
109
+ test("when below min, set the start of the range", () => {
110
+ const from = new Date(2021, 11, 20);
111
+ const to = new Date(2022, 0, 2);
112
+ const date = new Date(2021, 11, 21);
113
+ const min = 5;
114
+ const range = addToRange(date, { from, to }, min, 0, false);
115
+ expect(range).toEqual({ from: date, to: undefined });
116
+ });
117
+ });
@@ -0,0 +1,87 @@
1
+ import { defaultDateLib, type DateLib } from "../classes/DateLib.js";
2
+ import type { DateRange } from "../types/index.js";
3
+
4
+ /**
5
+ * Add a day to an existing range.
6
+ *
7
+ * The returned range takes in account the `undefined` values and if the added
8
+ * day is already present in the range.
9
+ *
10
+ * @group Utilities
11
+ */
12
+ export function addToRange(
13
+ /** The date to add to the range. */
14
+ date: Date,
15
+ /** The range where to add `date`. */
16
+ initialRange: DateRange | undefined,
17
+ min = 0,
18
+ max = 0,
19
+ required = false,
20
+ /** @ignore */
21
+ dateLib: DateLib = defaultDateLib
22
+ ): DateRange | undefined {
23
+ const { from, to } = initialRange || {};
24
+ const { isSameDay, isAfter, isBefore } = dateLib;
25
+
26
+ let range: DateRange | undefined;
27
+
28
+ if (!from && !to) {
29
+ // the range is empty, add the date
30
+ range = { from: date, to: min > 0 ? undefined : date };
31
+ } else if (from && !to) {
32
+ // adding date to an incomplete range
33
+ if (isSameDay(from, date)) {
34
+ // adding a date equal to the start of the range
35
+ if (required) {
36
+ range = { from, to: undefined };
37
+ } else {
38
+ range = undefined;
39
+ }
40
+ } else if (isBefore(date, from)) {
41
+ // adding a date before the start of the range
42
+ range = { from: date, to: from };
43
+ } else {
44
+ // adding a date after the start of the range
45
+ range = { from, to: date };
46
+ }
47
+ } else if (from && to) {
48
+ // adding date to a complete range
49
+ if (isSameDay(from, date) && isSameDay(to, date)) {
50
+ // adding a date that is equal to both start and end of the range
51
+ if (required) {
52
+ range = { from, to };
53
+ } else {
54
+ range = undefined;
55
+ }
56
+ } else if (isSameDay(from, date)) {
57
+ // adding a date equal to the the start of the range
58
+ range = { from, to: min > 0 ? undefined : date };
59
+ } else if (isSameDay(to, date)) {
60
+ // adding a dare equal to the end of the range
61
+ range = { from: date, to: min > 0 ? undefined : date };
62
+ } else if (isBefore(date, from)) {
63
+ // adding a date before the start of the range
64
+ range = { from: date, to: to };
65
+ } else if (isAfter(date, from)) {
66
+ // adding a date after the start of the range
67
+ range = { from, to: date };
68
+ } else if (isAfter(date, to)) {
69
+ // adding a date after the end of the range
70
+ range = { from, to: date };
71
+ } else {
72
+ throw new Error("Invalid range");
73
+ }
74
+ }
75
+
76
+ // check for min / max
77
+ if (range?.from && range?.to) {
78
+ const diff = dateLib.differenceInCalendarDays(range.to, range.from);
79
+ if (max > 0 && diff > max) {
80
+ range = { from: date, to: undefined };
81
+ } else if (min > 1 && diff < min) {
82
+ range = { from: date, to: undefined };
83
+ }
84
+ }
85
+
86
+ return range;
87
+ }