@octanejs/day-picker 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -0
  3. package/UPSTREAM.md +24 -0
  4. package/package.json +77 -0
  5. package/src/DayPicker.tsrx +717 -0
  6. package/src/UI.ts +121 -0
  7. package/src/classes/CalendarDay.ts +82 -0
  8. package/src/classes/CalendarMonth.ts +20 -0
  9. package/src/classes/CalendarWeek.ts +19 -0
  10. package/src/classes/DateLib.ts +695 -0
  11. package/src/classes/index.ts +4 -0
  12. package/src/components/custom-components.tsrx +237 -0
  13. package/src/formatters/formatCaption.ts +18 -0
  14. package/src/formatters/formatDay.ts +17 -0
  15. package/src/formatters/formatMonthDropdown.ts +16 -0
  16. package/src/formatters/formatWeekNumber.ts +19 -0
  17. package/src/formatters/formatWeekNumberHeader.ts +11 -0
  18. package/src/formatters/formatWeekdayName.ts +17 -0
  19. package/src/formatters/formatYearDropdown.ts +15 -0
  20. package/src/formatters/index.ts +7 -0
  21. package/src/helpers/calculateFocusTarget.ts +79 -0
  22. package/src/helpers/createGetModifiers.ts +117 -0
  23. package/src/helpers/endOfBroadcastWeek.ts +22 -0
  24. package/src/helpers/getBroadcastWeeksInMonth.ts +32 -0
  25. package/src/helpers/getClassNamesForModifiers.ts +38 -0
  26. package/src/helpers/getComponents.ts +19 -0
  27. package/src/helpers/getDataAttributes.ts +17 -0
  28. package/src/helpers/getDates.ts +87 -0
  29. package/src/helpers/getDays.ts +19 -0
  30. package/src/helpers/getDefaultClassNames.ts +36 -0
  31. package/src/helpers/getDisplayMonths.ts +30 -0
  32. package/src/helpers/getFocusableDate.ts +66 -0
  33. package/src/helpers/getFormatters.ts +16 -0
  34. package/src/helpers/getInitialMonth.ts +35 -0
  35. package/src/helpers/getLabels.ts +93 -0
  36. package/src/helpers/getMonthOptions.ts +45 -0
  37. package/src/helpers/getMonths.ts +89 -0
  38. package/src/helpers/getNavMonth.ts +35 -0
  39. package/src/helpers/getNextFocus.ts +45 -0
  40. package/src/helpers/getNextMonth.ts +46 -0
  41. package/src/helpers/getPreviousMonth.ts +45 -0
  42. package/src/helpers/getStyleForModifiers.ts +31 -0
  43. package/src/helpers/getWeekdays.ts +33 -0
  44. package/src/helpers/getWeeks.ts +14 -0
  45. package/src/helpers/getYearOptions.ts +43 -0
  46. package/src/helpers/index.ts +2 -0
  47. package/src/helpers/startOfBroadcastWeek.ts +26 -0
  48. package/src/helpers/useControlledValue.ts +38 -0
  49. package/src/index.ts +13 -0
  50. package/src/labels/index.ts +12 -0
  51. package/src/labels/labelDayButton.ts +29 -0
  52. package/src/labels/labelGrid.ts +18 -0
  53. package/src/labels/labelGridcell.ts +26 -0
  54. package/src/labels/labelMonthDropdown.ts +14 -0
  55. package/src/labels/labelNav.ts +11 -0
  56. package/src/labels/labelNext.ts +17 -0
  57. package/src/labels/labelPrevious.ts +13 -0
  58. package/src/labels/labelWeekNumber.ts +15 -0
  59. package/src/labels/labelWeekNumberHeader.ts +14 -0
  60. package/src/labels/labelWeekday.ts +16 -0
  61. package/src/labels/labelYearDropdown.ts +14 -0
  62. package/src/locale/af.ts +48 -0
  63. package/src/locale/ar-DZ.ts +48 -0
  64. package/src/locale/ar-EG.ts +48 -0
  65. package/src/locale/ar-MA.ts +48 -0
  66. package/src/locale/ar-SA.ts +48 -0
  67. package/src/locale/ar-TN.ts +48 -0
  68. package/src/locale/ar.ts +48 -0
  69. package/src/locale/az.ts +48 -0
  70. package/src/locale/be-tarask.ts +51 -0
  71. package/src/locale/be.ts +48 -0
  72. package/src/locale/bg.ts +48 -0
  73. package/src/locale/bn.ts +48 -0
  74. package/src/locale/bs.ts +48 -0
  75. package/src/locale/ca.ts +48 -0
  76. package/src/locale/ckb.ts +51 -0
  77. package/src/locale/cs.ts +48 -0
  78. package/src/locale/cy.ts +48 -0
  79. package/src/locale/da.ts +48 -0
  80. package/src/locale/de-AT.ts +48 -0
  81. package/src/locale/de.ts +48 -0
  82. package/src/locale/el.ts +48 -0
  83. package/src/locale/en-AU.ts +48 -0
  84. package/src/locale/en-CA.ts +48 -0
  85. package/src/locale/en-GB.ts +51 -0
  86. package/src/locale/en-IE.ts +48 -0
  87. package/src/locale/en-IN.ts +48 -0
  88. package/src/locale/en-NZ.ts +48 -0
  89. package/src/locale/en-US.ts +72 -0
  90. package/src/locale/en-ZA.ts +48 -0
  91. package/src/locale/eo.ts +48 -0
  92. package/src/locale/es.ts +48 -0
  93. package/src/locale/et.ts +48 -0
  94. package/src/locale/eu.ts +48 -0
  95. package/src/locale/fa-IR.ts +48 -0
  96. package/src/locale/fi.ts +48 -0
  97. package/src/locale/fr-CA.ts +48 -0
  98. package/src/locale/fr-CH.ts +48 -0
  99. package/src/locale/fr.ts +48 -0
  100. package/src/locale/fy.ts +48 -0
  101. package/src/locale/gd.ts +48 -0
  102. package/src/locale/gl.ts +48 -0
  103. package/src/locale/gu.ts +48 -0
  104. package/src/locale/he.ts +48 -0
  105. package/src/locale/hi.ts +48 -0
  106. package/src/locale/hr.ts +48 -0
  107. package/src/locale/ht.ts +48 -0
  108. package/src/locale/hu.ts +48 -0
  109. package/src/locale/hy.ts +48 -0
  110. package/src/locale/id.ts +48 -0
  111. package/src/locale/is.ts +48 -0
  112. package/src/locale/it-CH.ts +48 -0
  113. package/src/locale/it.ts +48 -0
  114. package/src/locale/ja-Hira.ts +48 -0
  115. package/src/locale/ja.ts +48 -0
  116. package/src/locale/ka.ts +48 -0
  117. package/src/locale/kk.ts +48 -0
  118. package/src/locale/km.ts +48 -0
  119. package/src/locale/kn.ts +48 -0
  120. package/src/locale/ko.ts +48 -0
  121. package/src/locale/lb.ts +48 -0
  122. package/src/locale/lt.ts +48 -0
  123. package/src/locale/lv.ts +48 -0
  124. package/src/locale/mk.ts +48 -0
  125. package/src/locale/mn.ts +48 -0
  126. package/src/locale/ms.ts +48 -0
  127. package/src/locale/mt.ts +48 -0
  128. package/src/locale/nb.ts +48 -0
  129. package/src/locale/nl-BE.ts +48 -0
  130. package/src/locale/nl.ts +48 -0
  131. package/src/locale/nn.ts +48 -0
  132. package/src/locale/oc.ts +48 -0
  133. package/src/locale/pl.ts +48 -0
  134. package/src/locale/pt-BR.ts +48 -0
  135. package/src/locale/pt.ts +48 -0
  136. package/src/locale/ro.ts +48 -0
  137. package/src/locale/ru.ts +48 -0
  138. package/src/locale/se.ts +48 -0
  139. package/src/locale/sk.ts +48 -0
  140. package/src/locale/sl.ts +48 -0
  141. package/src/locale/sq.ts +48 -0
  142. package/src/locale/sr-Latn.ts +48 -0
  143. package/src/locale/sr.ts +48 -0
  144. package/src/locale/sv.ts +48 -0
  145. package/src/locale/ta.ts +48 -0
  146. package/src/locale/te.ts +48 -0
  147. package/src/locale/th.ts +48 -0
  148. package/src/locale/tr.ts +48 -0
  149. package/src/locale/ug.ts +48 -0
  150. package/src/locale/uk.ts +48 -0
  151. package/src/locale/uz-Cyrl.ts +48 -0
  152. package/src/locale/uz.ts +48 -0
  153. package/src/locale/vi.ts +48 -0
  154. package/src/locale/zh-CN.ts +48 -0
  155. package/src/locale/zh-HK.ts +51 -0
  156. package/src/locale/zh-TW.ts +51 -0
  157. package/src/locale.ts +108 -0
  158. package/src/noonDateLib.ts +202 -0
  159. package/src/selection/useMulti.ts +74 -0
  160. package/src/selection/useRange.ts +89 -0
  161. package/src/selection/useSingle.ts +77 -0
  162. package/src/style.css +451 -0
  163. package/src/style.css.d.ts +3 -0
  164. package/src/style.module.css +451 -0
  165. package/src/style.module.css.d.ts +29 -0
  166. package/src/types/index.ts +3 -0
  167. package/src/types/props.ts +671 -0
  168. package/src/types/selection.ts +97 -0
  169. package/src/types/shared.ts +346 -0
  170. package/src/useAnimation.ts +242 -0
  171. package/src/useCalendar.ts +233 -0
  172. package/src/useDayPicker.ts +91 -0
  173. package/src/useFocus.ts +109 -0
  174. package/src/useSelection.ts +36 -0
  175. package/src/utils/addToRange.ts +91 -0
  176. package/src/utils/convertMatchersToTimeZone.ts +88 -0
  177. package/src/utils/dateMatchModifiers.ts +72 -0
  178. package/src/utils/index.ts +7 -0
  179. package/src/utils/rangeContainsDayOfWeek.ts +33 -0
  180. package/src/utils/rangeContainsModifiers.ts +106 -0
  181. package/src/utils/rangeIncludesDate.ts +40 -0
  182. package/src/utils/rangeOverlaps.ts +26 -0
  183. package/src/utils/toTimeZone.ts +13 -0
  184. package/src/utils/typeguards.ts +71 -0
  185. package/status.json +13 -0
@@ -0,0 +1,17 @@
1
+ import type { DayPickerProps } from '../types/index.js';
2
+
3
+ /** Extract the public `data-` attributes and DayPicker configuration flags. */
4
+ export function getDataAttributes(props: DayPickerProps): Record<string, unknown> {
5
+ const dataAttributes: Record<string, unknown> = {
6
+ 'data-mode': props.mode ?? undefined,
7
+ 'data-required': 'required' in props ? props.required : undefined,
8
+ 'data-multiple-months': (props.numberOfMonths && props.numberOfMonths > 1) || undefined,
9
+ 'data-week-numbers': props.showWeekNumber || undefined,
10
+ 'data-broadcast-calendar': props.broadcastCalendar || undefined,
11
+ 'data-nav-layout': props.navLayout || undefined,
12
+ };
13
+ Object.entries(props).forEach(([key, value]) => {
14
+ if (key.startsWith('data-')) dataAttributes[key] = value;
15
+ });
16
+ return dataAttributes;
17
+ }
@@ -0,0 +1,87 @@
1
+ import type { DateLib } from '../classes/DateLib.js';
2
+ import type { DayPickerProps } from '../types/props.js';
3
+
4
+ /**
5
+ * Returns all the dates to display in the calendar.
6
+ *
7
+ * This function calculates the range of dates to display based on the provided
8
+ * display months, constraints, and calendar configuration.
9
+ *
10
+ * @param displayMonths The months to display in the calendar.
11
+ * @param maxDate The maximum date to include in the range.
12
+ * @param props The DayPicker props, including calendar configuration options.
13
+ * @param dateLib The date library to use for date manipulation.
14
+ * @returns An array of dates to display in the calendar.
15
+ */
16
+ export function getDates(
17
+ displayMonths: Date[],
18
+ maxDate: Date | undefined,
19
+ props: Pick<DayPickerProps, 'ISOWeek' | 'fixedWeeks' | 'broadcastCalendar'>,
20
+ dateLib: DateLib,
21
+ ): Date[] {
22
+ const firstMonth = displayMonths[0];
23
+ const lastMonth = displayMonths[displayMonths.length - 1];
24
+
25
+ const { ISOWeek, fixedWeeks, broadcastCalendar } = props ?? {};
26
+ const {
27
+ addDays,
28
+ differenceInCalendarDays,
29
+ differenceInCalendarMonths,
30
+ endOfBroadcastWeek,
31
+ endOfISOWeek,
32
+ endOfMonth,
33
+ endOfWeek,
34
+ isAfter,
35
+ startOfBroadcastWeek,
36
+ startOfISOWeek,
37
+ startOfWeek,
38
+ } = dateLib;
39
+
40
+ const startWeekFirstDate = broadcastCalendar
41
+ ? startOfBroadcastWeek(firstMonth, dateLib)
42
+ : ISOWeek
43
+ ? startOfISOWeek(firstMonth)
44
+ : startOfWeek(firstMonth);
45
+
46
+ const displayMonthsWeekEnd = broadcastCalendar
47
+ ? endOfBroadcastWeek(lastMonth)
48
+ : ISOWeek
49
+ ? endOfISOWeek(endOfMonth(lastMonth))
50
+ : endOfWeek(endOfMonth(lastMonth));
51
+
52
+ // If maxDate is set, clamp the grid to the end of that week.
53
+ const constraintWeekEnd =
54
+ maxDate &&
55
+ (broadcastCalendar
56
+ ? endOfBroadcastWeek(maxDate)
57
+ : ISOWeek
58
+ ? endOfISOWeek(maxDate)
59
+ : endOfWeek(maxDate));
60
+
61
+ // Pick the earliest week end between the displayed months and the constraint.
62
+ const gridEndDate =
63
+ constraintWeekEnd && isAfter(displayMonthsWeekEnd, constraintWeekEnd)
64
+ ? constraintWeekEnd
65
+ : displayMonthsWeekEnd;
66
+
67
+ const nOfDays = differenceInCalendarDays(gridEndDate, startWeekFirstDate);
68
+ const nOfMonths = differenceInCalendarMonths(lastMonth, firstMonth) + 1;
69
+
70
+ const dates: Date[] = [];
71
+ for (let i = 0; i <= nOfDays; i++) {
72
+ const date = addDays(startWeekFirstDate, i);
73
+ dates.push(date);
74
+ }
75
+
76
+ // If fixed weeks is enabled, add the extra dates to the array
77
+ const nrOfDaysWithFixedWeeks = broadcastCalendar ? 35 : 42;
78
+ const extraDates = nrOfDaysWithFixedWeeks * nOfMonths;
79
+ if (fixedWeeks && dates.length < extraDates) {
80
+ const daysToAdd = extraDates - dates.length;
81
+ for (let i = 0; i < daysToAdd; i++) {
82
+ const date = addDays(dates[dates.length - 1], 1);
83
+ dates.push(date);
84
+ }
85
+ }
86
+ return dates;
87
+ }
@@ -0,0 +1,19 @@
1
+ import type { CalendarDay, CalendarMonth } from '../classes/index.js';
2
+
3
+ /**
4
+ * Returns all the days belonging to the calendar by merging the days in the
5
+ * weeks for each month.
6
+ *
7
+ * @param calendarMonths The array of calendar months.
8
+ * @returns An array of `CalendarDay` objects representing all the days in the
9
+ * calendar.
10
+ */
11
+ export function getDays(calendarMonths: CalendarMonth[]) {
12
+ const initialDays: CalendarDay[] = [];
13
+ return calendarMonths.reduce((days, month) => {
14
+ const weekDays: CalendarDay[] = month.weeks.reduce((weekDays, week) => {
15
+ return weekDays.concat(week.days.slice());
16
+ }, initialDays.slice());
17
+ return days.concat(weekDays.slice());
18
+ }, initialDays.slice());
19
+ }
@@ -0,0 +1,36 @@
1
+ import type { ClassNames } from '../types/index.js';
2
+ import { Animation, DayFlag, SelectionState, UI } from '../UI.js';
3
+
4
+ /**
5
+ * Returns the default class names for the UI elements.
6
+ *
7
+ * This function generates a mapping of default class names for various UI
8
+ * elements, day flags, selection states, and animations.
9
+ *
10
+ * @returns An object containing the default class names.
11
+ * @group Utilities
12
+ */
13
+ export function getDefaultClassNames(): ClassNames {
14
+ const classNames: Partial<Required<ClassNames>> = {};
15
+
16
+ for (const key in UI) {
17
+ classNames[UI[key as keyof typeof UI]] = `rdp-${UI[key as keyof typeof UI]}`;
18
+ }
19
+
20
+ for (const key in DayFlag) {
21
+ classNames[DayFlag[key as keyof typeof DayFlag]] =
22
+ `rdp-${DayFlag[key as keyof typeof DayFlag]}`;
23
+ }
24
+
25
+ for (const key in SelectionState) {
26
+ classNames[SelectionState[key as keyof typeof SelectionState]] =
27
+ `rdp-${SelectionState[key as keyof typeof SelectionState]}`;
28
+ }
29
+
30
+ for (const key in Animation) {
31
+ classNames[Animation[key as keyof typeof Animation]] =
32
+ `rdp-${Animation[key as keyof typeof Animation]}`;
33
+ }
34
+
35
+ return classNames as Required<ClassNames>;
36
+ }
@@ -0,0 +1,30 @@
1
+ import type { DateLib } from '../classes/DateLib.js';
2
+ import type { DayPickerProps } from '../types/index.js';
3
+
4
+ /**
5
+ * Returns the months to display in the calendar.
6
+ *
7
+ * @param firstDisplayedMonth The first month currently displayed in the
8
+ * calendar.
9
+ * @param calendarEndMonth The latest month the user can navigate to.
10
+ * @param props The DayPicker props, including `numberOfMonths`.
11
+ * @param dateLib The date library to use for date manipulation.
12
+ * @returns An array of dates representing the months to display.
13
+ */
14
+ export function getDisplayMonths(
15
+ firstDisplayedMonth: Date,
16
+ calendarEndMonth: Date | undefined,
17
+ props: Pick<DayPickerProps, 'numberOfMonths'>,
18
+ dateLib: DateLib,
19
+ ): Date[] {
20
+ const { numberOfMonths = 1 } = props;
21
+ const months: Date[] = [];
22
+ for (let i = 0; i < numberOfMonths; i++) {
23
+ const month = dateLib.addMonths(firstDisplayedMonth, i);
24
+ if (calendarEndMonth && month > calendarEndMonth) {
25
+ break;
26
+ }
27
+ months.push(month);
28
+ }
29
+ return months;
30
+ }
@@ -0,0 +1,66 @@
1
+ import type { DateLib } from '../classes/DateLib.js';
2
+ import type { DayPickerProps, MoveFocusBy, MoveFocusDir } from '../types/index.js';
3
+
4
+ /**
5
+ * Calculates the next date that should be focused in the calendar.
6
+ *
7
+ * This function determines the next focusable date based on the movement
8
+ * direction, constraints, and calendar configuration.
9
+ *
10
+ * @param moveBy The unit of movement (e.g., "day", "week").
11
+ * @param moveDir The direction of movement ("before" or "after").
12
+ * @param refDate The reference date from which to calculate the next focusable
13
+ * date.
14
+ * @param navStart The earliest date the user can navigate to.
15
+ * @param navEnd The latest date the user can navigate to.
16
+ * @param props The DayPicker props, including calendar configuration options.
17
+ * @param dateLib The date library to use for date manipulation.
18
+ * @returns The next focusable date.
19
+ */
20
+ export function getFocusableDate(
21
+ moveBy: MoveFocusBy,
22
+ moveDir: MoveFocusDir,
23
+ refDate: Date,
24
+ navStart: Date | undefined,
25
+ navEnd: Date | undefined,
26
+ props: Pick<DayPickerProps, 'ISOWeek' | 'broadcastCalendar'>,
27
+ dateLib: DateLib,
28
+ ): Date {
29
+ const { ISOWeek, broadcastCalendar } = props;
30
+ const {
31
+ addDays,
32
+ addMonths,
33
+ addWeeks,
34
+ addYears,
35
+ endOfBroadcastWeek,
36
+ endOfISOWeek,
37
+ endOfWeek,
38
+ max,
39
+ min,
40
+ startOfBroadcastWeek,
41
+ startOfISOWeek,
42
+ startOfWeek,
43
+ } = dateLib;
44
+ const moveFns = {
45
+ day: addDays,
46
+ week: addWeeks,
47
+ month: addMonths,
48
+ year: addYears,
49
+ startOfWeek: (date: Date) =>
50
+ broadcastCalendar
51
+ ? startOfBroadcastWeek(date, dateLib)
52
+ : ISOWeek
53
+ ? startOfISOWeek(date)
54
+ : startOfWeek(date),
55
+ endOfWeek: (date: Date) =>
56
+ broadcastCalendar ? endOfBroadcastWeek(date) : ISOWeek ? endOfISOWeek(date) : endOfWeek(date),
57
+ };
58
+
59
+ let focusableDate = moveFns[moveBy](refDate, moveDir === 'after' ? 1 : -1);
60
+ if (moveDir === 'before' && navStart) {
61
+ focusableDate = max([navStart, focusableDate]);
62
+ } else if (moveDir === 'after' && navEnd) {
63
+ focusableDate = min([navEnd, focusableDate]);
64
+ }
65
+ return focusableDate;
66
+ }
@@ -0,0 +1,16 @@
1
+ import * as defaultFormatters from '../formatters/index.js';
2
+ import type { DayPickerProps } from '../types/index.js';
3
+
4
+ /**
5
+ * Merges custom formatters from the props with the default formatters.
6
+ *
7
+ * @param customFormatters The custom formatters provided in the DayPicker
8
+ * props.
9
+ * @returns The merged formatters object.
10
+ */
11
+ export function getFormatters(customFormatters: DayPickerProps['formatters']) {
12
+ return {
13
+ ...defaultFormatters,
14
+ ...customFormatters,
15
+ };
16
+ }
@@ -0,0 +1,35 @@
1
+ import type { DateLib } from '../classes/DateLib.js';
2
+ import type { DayPickerProps } from '../types/props.js';
3
+
4
+ /**
5
+ * Determines the initial month to display in the calendar based on the provided
6
+ * props.
7
+ *
8
+ * This function calculates the starting month, considering constraints such as
9
+ * `startMonth`, `endMonth`, and the number of months to display.
10
+ *
11
+ * @param props The DayPicker props, including navigation and date constraints.
12
+ * @param dateLib The date library to use for date manipulation.
13
+ * @returns The initial month to display.
14
+ */
15
+ export function getInitialMonth(
16
+ props: Pick<DayPickerProps, 'month' | 'defaultMonth' | 'today' | 'numberOfMonths' | 'timeZone'>,
17
+ navStart: Date | undefined,
18
+ navEnd: Date | undefined,
19
+ dateLib: DateLib,
20
+ ): Date {
21
+ const { month, defaultMonth, today = dateLib.today(), numberOfMonths = 1 } = props;
22
+ let initialMonth = month || defaultMonth || today;
23
+ const { differenceInCalendarMonths, addMonths, startOfMonth } = dateLib;
24
+
25
+ if (navEnd && differenceInCalendarMonths(navEnd, initialMonth) < numberOfMonths - 1) {
26
+ const offset = -1 * (numberOfMonths - 1);
27
+ initialMonth = addMonths(navEnd, offset);
28
+ }
29
+
30
+ if (navStart && differenceInCalendarMonths(initialMonth, navStart) < 0) {
31
+ initialMonth = navStart;
32
+ }
33
+
34
+ return startOfMonth(initialMonth);
35
+ }
@@ -0,0 +1,93 @@
1
+ import type { DateLibOptions } from '../classes/DateLib.js';
2
+ import * as defaultLabels from '../labels/index.js';
3
+ import type { DayPickerProps, Labels } from '../types/index.js';
4
+
5
+ // Use a bivariant callable type to accept both the exact label signatures and
6
+ // compatible overrides. This matches how React types event handlers, allowing
7
+ // consumers to pass narrower or slightly different function shapes without
8
+ // losing type safety.
9
+ type BivariantLabel<Args extends unknown[]> = {
10
+ bivarianceHack(...args: Args): string;
11
+ }['bivarianceHack'];
12
+
13
+ const resolveLabel = <Args extends unknown[], T extends BivariantLabel<Args>>(
14
+ defaultLabel: T,
15
+ customLabel: T | undefined,
16
+ localeLabel: string | T | undefined,
17
+ ): T => {
18
+ if (customLabel) return customLabel;
19
+ if (localeLabel) {
20
+ return (typeof localeLabel === 'function' ? localeLabel : (..._args: Args) => localeLabel) as T;
21
+ }
22
+ return defaultLabel;
23
+ };
24
+
25
+ /**
26
+ * Merges custom labels from the props with the default labels.
27
+ *
28
+ * When available, uses the locale-provided translation for `labelNext`.
29
+ *
30
+ * @param customLabels The custom labels provided in the DayPicker props.
31
+ * @param options Options from the date library, used to resolve locale
32
+ * translations.
33
+ * @returns The merged labels object with locale-aware defaults.
34
+ */
35
+ export function getLabels(customLabels: DayPickerProps['labels'], options: DateLibOptions): Labels {
36
+ const localeLabels = options.locale?.labels ?? {};
37
+
38
+ return {
39
+ ...defaultLabels,
40
+ ...(customLabels ?? {}),
41
+ labelDayButton: resolveLabel(
42
+ defaultLabels.labelDayButton,
43
+ customLabels?.labelDayButton,
44
+ localeLabels.labelDayButton,
45
+ ),
46
+ labelMonthDropdown: resolveLabel(
47
+ defaultLabels.labelMonthDropdown,
48
+ customLabels?.labelMonthDropdown,
49
+ localeLabels.labelMonthDropdown,
50
+ ),
51
+ labelNext: resolveLabel(
52
+ defaultLabels.labelNext,
53
+ customLabels?.labelNext,
54
+ localeLabels.labelNext,
55
+ ),
56
+ labelPrevious: resolveLabel(
57
+ defaultLabels.labelPrevious,
58
+ customLabels?.labelPrevious,
59
+ localeLabels.labelPrevious,
60
+ ),
61
+ labelWeekNumber: resolveLabel(
62
+ defaultLabels.labelWeekNumber,
63
+ customLabels?.labelWeekNumber,
64
+ localeLabels.labelWeekNumber,
65
+ ),
66
+ labelYearDropdown: resolveLabel(
67
+ defaultLabels.labelYearDropdown,
68
+ customLabels?.labelYearDropdown,
69
+ localeLabels.labelYearDropdown,
70
+ ),
71
+ labelGrid: resolveLabel(
72
+ defaultLabels.labelGrid,
73
+ customLabels?.labelGrid,
74
+ localeLabels.labelGrid,
75
+ ),
76
+ labelGridcell: resolveLabel(
77
+ defaultLabels.labelGridcell,
78
+ customLabels?.labelGridcell,
79
+ localeLabels.labelGridcell,
80
+ ),
81
+ labelNav: resolveLabel(defaultLabels.labelNav, customLabels?.labelNav, localeLabels.labelNav),
82
+ labelWeekNumberHeader: resolveLabel(
83
+ defaultLabels.labelWeekNumberHeader,
84
+ customLabels?.labelWeekNumberHeader,
85
+ localeLabels.labelWeekNumberHeader,
86
+ ),
87
+ labelWeekday: resolveLabel(
88
+ defaultLabels.labelWeekday,
89
+ customLabels?.labelWeekday,
90
+ localeLabels.labelWeekday,
91
+ ),
92
+ };
93
+ }
@@ -0,0 +1,45 @@
1
+ import type { DateLib } from '../classes/DateLib.js';
2
+ import type { DropdownOption } from '../components/custom-components.tsrx';
3
+ import type { Formatters } from '../types/index.js';
4
+
5
+ /**
6
+ * Returns the months to show in the dropdown.
7
+ *
8
+ * This function generates a list of months for the current year, formatted
9
+ * using the provided formatter, and determines whether each month should be
10
+ * disabled based on the navigation range.
11
+ *
12
+ * @param displayMonth The currently displayed month.
13
+ * @param navStart The start date for navigation.
14
+ * @param navEnd The end date for navigation.
15
+ * @param formatters The formatters to use for formatting the month labels.
16
+ * @param dateLib The date library to use for date manipulation.
17
+ * @returns An array of dropdown options representing the months, or `undefined`
18
+ * if no months are available.
19
+ */
20
+ export function getMonthOptions(
21
+ displayMonth: Date,
22
+ navStart: Date | undefined,
23
+ navEnd: Date | undefined,
24
+ formatters: Pick<Formatters, 'formatMonthDropdown'>,
25
+ dateLib: DateLib,
26
+ ): DropdownOption[] | undefined {
27
+ const { startOfMonth, startOfYear, endOfYear, eachMonthOfInterval, getMonth } = dateLib;
28
+
29
+ const months = eachMonthOfInterval({
30
+ start: startOfYear(displayMonth),
31
+ end: endOfYear(displayMonth),
32
+ });
33
+
34
+ const options = months.map((month) => {
35
+ const label = formatters.formatMonthDropdown(month, dateLib);
36
+ const value = getMonth(month);
37
+ const disabled =
38
+ (navStart && month < startOfMonth(navStart)) ||
39
+ (navEnd && month > startOfMonth(navEnd)) ||
40
+ false;
41
+ return { value, label, disabled };
42
+ });
43
+
44
+ return options;
45
+ }
@@ -0,0 +1,89 @@
1
+ import type { DateLib } from '../classes/DateLib.js';
2
+ import { CalendarDay, CalendarMonth, CalendarWeek } from '../classes/index.js';
3
+ import type { DayPickerProps } from '../types/index.js';
4
+
5
+ /**
6
+ * Returns the months to display in the calendar.
7
+ *
8
+ * This function generates `CalendarMonth` objects for each month to be
9
+ * displayed, including their weeks and days, based on the provided display
10
+ * months and dates.
11
+ *
12
+ * @param displayMonths The months (as dates) to display in the calendar.
13
+ * @param dates The dates to display in the calendar.
14
+ * @param props Options from the DayPicker props context.
15
+ * @param dateLib The date library to use for date manipulation.
16
+ * @returns An array of `CalendarMonth` objects representing the months to
17
+ * display.
18
+ */
19
+ export function getMonths(
20
+ displayMonths: Date[],
21
+ dates: Date[],
22
+ props: Pick<DayPickerProps, 'broadcastCalendar' | 'fixedWeeks' | 'ISOWeek' | 'reverseMonths'>,
23
+ dateLib: DateLib,
24
+ ): CalendarMonth[] {
25
+ const {
26
+ addDays,
27
+ endOfBroadcastWeek,
28
+ endOfISOWeek,
29
+ endOfMonth,
30
+ endOfWeek,
31
+ getISOWeek,
32
+ getWeek,
33
+ startOfBroadcastWeek,
34
+ startOfISOWeek,
35
+ startOfWeek,
36
+ } = dateLib;
37
+
38
+ const dayPickerMonths = displayMonths.reduce<CalendarMonth[]>((months, month) => {
39
+ const firstDateOfFirstWeek = props.broadcastCalendar
40
+ ? startOfBroadcastWeek(month, dateLib)
41
+ : props.ISOWeek
42
+ ? startOfISOWeek(month)
43
+ : startOfWeek(month);
44
+
45
+ const lastDateOfLastWeek = props.broadcastCalendar
46
+ ? endOfBroadcastWeek(month)
47
+ : props.ISOWeek
48
+ ? endOfISOWeek(endOfMonth(month))
49
+ : endOfWeek(endOfMonth(month));
50
+
51
+ /** The dates to display in the month. */
52
+ const monthDates = dates.filter((date) => {
53
+ return date >= firstDateOfFirstWeek && date <= lastDateOfLastWeek;
54
+ });
55
+
56
+ const nrOfDaysWithFixedWeeks = props.broadcastCalendar ? 35 : 42;
57
+
58
+ if (props.fixedWeeks && monthDates.length < nrOfDaysWithFixedWeeks) {
59
+ const extraDates = dates.filter((date) => {
60
+ const daysToAdd = nrOfDaysWithFixedWeeks - monthDates.length;
61
+ return date > lastDateOfLastWeek && date <= addDays(lastDateOfLastWeek, daysToAdd);
62
+ });
63
+ monthDates.push(...extraDates);
64
+ }
65
+
66
+ const weeks: CalendarWeek[] = monthDates.reduce<CalendarWeek[]>((weeks, date) => {
67
+ const weekNumber = props.ISOWeek ? getISOWeek(date) : getWeek(date);
68
+ const week = weeks.find((week) => week.weekNumber === weekNumber);
69
+
70
+ const day = new CalendarDay(date, month, dateLib);
71
+ if (!week) {
72
+ weeks.push(new CalendarWeek(weekNumber, [day]));
73
+ } else {
74
+ week.days.push(day);
75
+ }
76
+ return weeks;
77
+ }, []);
78
+
79
+ const dayPickerMonth = new CalendarMonth(month, weeks);
80
+ months.push(dayPickerMonth);
81
+ return months;
82
+ }, []);
83
+
84
+ if (!props.reverseMonths) {
85
+ return dayPickerMonths;
86
+ } else {
87
+ return dayPickerMonths.reverse();
88
+ }
89
+ }
@@ -0,0 +1,35 @@
1
+ import type { DateLib } from '../classes/DateLib.js';
2
+ import type { DayPickerProps } from '../types/index.js';
3
+
4
+ /**
5
+ * Returns the start and end months for calendar navigation.
6
+ *
7
+ * @param props The DayPicker props, including navigation and layout options.
8
+ * @param dateLib The date library to use for date manipulation.
9
+ * @returns A tuple containing the start and end months for navigation.
10
+ */
11
+ export function getNavMonths(
12
+ props: Pick<DayPickerProps, 'captionLayout' | 'endMonth' | 'startMonth' | 'today' | 'timeZone'>,
13
+ dateLib: DateLib,
14
+ ): [start: Date | undefined, end: Date | undefined] {
15
+ let { startMonth, endMonth } = props;
16
+
17
+ const { startOfYear, startOfDay, startOfMonth, endOfMonth, addYears, endOfYear, today } = dateLib;
18
+
19
+ const hasYearDropdown =
20
+ props.captionLayout === 'dropdown' || props.captionLayout === 'dropdown-years';
21
+ if (startMonth) {
22
+ startMonth = startOfMonth(startMonth);
23
+ } else if (!startMonth && hasYearDropdown) {
24
+ startMonth = startOfYear(addYears(props.today ?? today(), -100));
25
+ }
26
+ if (endMonth) {
27
+ endMonth = endOfMonth(endMonth);
28
+ } else if (!endMonth && hasYearDropdown) {
29
+ endMonth = endOfYear(props.today ?? today());
30
+ }
31
+ return [
32
+ startMonth ? startOfDay(startMonth) : startMonth,
33
+ endMonth ? startOfDay(endMonth) : endMonth,
34
+ ];
35
+ }
@@ -0,0 +1,45 @@
1
+ import type { DateLib } from '../classes/DateLib.js';
2
+ import { CalendarDay } from '../classes/index.js';
3
+ import type { DayPickerProps, MoveFocusBy, MoveFocusDir } from '../types/index.js';
4
+ import { dateMatchModifiers } from '../utils/dateMatchModifiers.js';
5
+ import { getFocusableDate } from './getFocusableDate.js';
6
+
7
+ export function getNextFocus(
8
+ moveBy: MoveFocusBy,
9
+ moveDir: MoveFocusDir,
10
+ refDay: CalendarDay,
11
+ calendarStartMonth: Date | undefined,
12
+ calendarEndMonth: Date | undefined,
13
+ props: Pick<DayPickerProps, 'disabled' | 'hidden' | 'modifiers' | 'ISOWeek' | 'timeZone'>,
14
+ dateLib: DateLib,
15
+ attempt = 0,
16
+ ): CalendarDay | undefined {
17
+ if (attempt > 365) return undefined;
18
+ const focusableDate = getFocusableDate(
19
+ moveBy,
20
+ moveDir,
21
+ refDay.date,
22
+ calendarStartMonth,
23
+ calendarEndMonth,
24
+ props,
25
+ dateLib,
26
+ );
27
+ const isDisabled = Boolean(
28
+ props.disabled && dateMatchModifiers(focusableDate, props.disabled, dateLib),
29
+ );
30
+ const isHidden = Boolean(
31
+ props.hidden && dateMatchModifiers(focusableDate, props.hidden, dateLib),
32
+ );
33
+ const focusDay = new CalendarDay(focusableDate, focusableDate, dateLib);
34
+ if (!isDisabled && !isHidden) return focusDay;
35
+ return getNextFocus(
36
+ moveBy,
37
+ moveDir,
38
+ focusDay,
39
+ calendarStartMonth,
40
+ calendarEndMonth,
41
+ props,
42
+ dateLib,
43
+ attempt + 1,
44
+ );
45
+ }
@@ -0,0 +1,46 @@
1
+ import type { DateLib } from '../classes/DateLib.js';
2
+ import type { DayPickerProps } from '../types/index.js';
3
+
4
+ /**
5
+ * Returns the next month the user can navigate to, based on the given options.
6
+ *
7
+ * The next month is not always the next calendar month:
8
+ *
9
+ * - If it is after the `calendarEndMonth`, it returns `undefined`.
10
+ * - If paged navigation is enabled, it skips forward by the number of displayed
11
+ * months.
12
+ *
13
+ * @param firstDisplayedMonth The first month currently displayed in the
14
+ * calendar.
15
+ * @param calendarEndMonth The latest month the user can navigate to.
16
+ * @param options Navigation options, including `numberOfMonths` and
17
+ * `pagedNavigation`.
18
+ * @param dateLib The date library to use for date manipulation.
19
+ * @returns The next month, or `undefined` if navigation is not possible.
20
+ */
21
+ export function getNextMonth(
22
+ firstDisplayedMonth: Date,
23
+ calendarEndMonth: Date | undefined,
24
+ options: Pick<DayPickerProps, 'numberOfMonths' | 'pagedNavigation' | 'disableNavigation'>,
25
+ dateLib: DateLib,
26
+ ): Date | undefined {
27
+ if (options.disableNavigation) {
28
+ return undefined;
29
+ }
30
+ const { pagedNavigation, numberOfMonths = 1 } = options;
31
+ const { startOfMonth, addMonths, differenceInCalendarMonths } = dateLib;
32
+ const offset = pagedNavigation ? numberOfMonths : 1;
33
+ const month = startOfMonth(firstDisplayedMonth);
34
+
35
+ if (!calendarEndMonth) {
36
+ return addMonths(month, offset);
37
+ }
38
+
39
+ const monthsDiff = differenceInCalendarMonths(calendarEndMonth, firstDisplayedMonth);
40
+
41
+ if (monthsDiff < numberOfMonths) {
42
+ return undefined;
43
+ }
44
+
45
+ return addMonths(month, offset);
46
+ }