@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,237 @@
1
+ // @ts-nocheck -- public props retain React DOM compatibility at the Octane render boundary.
2
+ import { useCallback, useEffect, useRef } from 'octane';
3
+ import type {
4
+ ButtonHTMLAttributes,
5
+ CSSProperties,
6
+ HTMLAttributes,
7
+ MouseEvent as ReactMouseEvent,
8
+ MouseEventHandler,
9
+ OptionHTMLAttributes,
10
+ Ref,
11
+ SelectHTMLAttributes,
12
+ TableHTMLAttributes,
13
+ ThHTMLAttributes,
14
+ } from 'react';
15
+ import type { CalendarDay, CalendarMonth, CalendarWeek } from '../classes/index.js';
16
+ import type { Modifiers } from '../types/index.js';
17
+ import { UI } from '../UI.js';
18
+ import { useDayPicker } from '../useDayPicker.js';
19
+
20
+ export function CaptionLabel(props: HTMLAttributes<HTMLSpanElement>) @{
21
+ <span {...props} />
22
+ }
23
+ export type CaptionLabelProps = Parameters<typeof CaptionLabel>[0];
24
+
25
+ export function Chevron(props: {
26
+ className?: string;
27
+ style?: CSSProperties;
28
+ size?: number;
29
+ disabled?: boolean;
30
+ orientation?: 'up' | 'down' | 'left' | 'right';
31
+ }) @{
32
+ const { size = 24, orientation = 'left', className, style } = props;
33
+ <svg className={className} style={style} width={size} height={size} viewBox="0 0 24 24">
34
+ {orientation === 'up' &&
35
+ <polygon points="6.77 17 12.5 11.43 18.24 17 20 15.28 12.5 8 5 15.28" />}
36
+ {orientation === 'down' &&
37
+ <polygon points="6.77 8 12.5 13.57 18.24 8 20 9.72 12.5 17 5 9.72" />}
38
+ {orientation === 'left' &&
39
+ <polygon points="16 18.112 9.81111111 12 16 5.87733333 14.0888889 4 6 12 14.0888889 20" />}
40
+ {orientation === 'right' &&
41
+ <polygon points="8 18.112 14.18888889 12 8 5.87733333 9.91111111 4 18 12 9.91111111 20" />}
42
+ </svg>
43
+ }
44
+ export type ChevronProps = Parameters<typeof Chevron>[0];
45
+
46
+ export type DayProps = { day: CalendarDay; modifiers: Modifiers } & HTMLAttributes<HTMLDivElement>;
47
+ export function Day(props: DayProps) @{
48
+ const { day: _day, modifiers: _modifiers, ...cellProps } = props;
49
+ <td {...cellProps} />
50
+ }
51
+
52
+ export type DayButtonProps =
53
+ { day: CalendarDay; modifiers: Modifiers } & ButtonHTMLAttributes<HTMLButtonElement>;
54
+ export function DayButton(props: DayButtonProps) @{
55
+ const { day: _day, modifiers, ...buttonProps } = props;
56
+ const ref = useRef<HTMLButtonElement | null>(null);
57
+ useEffect(() => {
58
+ if (modifiers.focused) ref.current?.focus();
59
+ }, [modifiers.focused]);
60
+ <button ref={ref} {...buttonProps} />
61
+ }
62
+
63
+ export type DropdownOption = { value: number; label: string; disabled: boolean };
64
+ export type DropdownProps =
65
+ { options?: DropdownOption[] } & Omit<SelectHTMLAttributes<HTMLSelectElement>, 'children'>;
66
+ export function Dropdown(props: DropdownProps) @{
67
+ const { options, className, ...selectProps } = props;
68
+ const { classNames, components, styles } = useDayPicker();
69
+ const cssClassSelect = [classNames[UI.Dropdown], className].filter(Boolean).join(' ');
70
+ const selectedOption = options?.find(({ value }) => value === selectProps.value);
71
+ <span
72
+ data-disabled={selectProps.disabled}
73
+ className={classNames[UI.DropdownRoot]}
74
+ style={styles?.[UI.DropdownRoot]}
75
+ >
76
+ <components.Select className={cssClassSelect} {...selectProps}>
77
+ {options?.map(
78
+ ({ value, label, disabled }) =>
79
+ <components.Option
80
+ key={value}
81
+ value={value}
82
+ disabled={disabled}
83
+ >{label}</components.Option>,
84
+ )}
85
+ </components.Select>
86
+ <span className={classNames[UI.CaptionLabel]} style={styles?.[UI.CaptionLabel]} aria-hidden>
87
+ {selectedOption?.label}
88
+ <components.Chevron
89
+ orientation="down"
90
+ size={18}
91
+ className={classNames[UI.Chevron]}
92
+ style={styles?.[UI.Chevron]}
93
+ />
94
+ </span>
95
+ </span>
96
+ }
97
+
98
+ export function DropdownNav(props: HTMLAttributes<HTMLDivElement>) @{
99
+ <div {...props} />
100
+ }
101
+ export type DropdownNavProps = Parameters<typeof DropdownNav>[0];
102
+ export function Footer(props: HTMLAttributes<HTMLDivElement>) @{
103
+ <div {...props} />
104
+ }
105
+ export type FooterProps = Parameters<typeof Footer>[0];
106
+
107
+ export type MonthProps =
108
+ { calendarMonth: CalendarMonth; displayIndex: number } & HTMLAttributes<HTMLDivElement>;
109
+ export function Month(props: MonthProps) @{
110
+ const { calendarMonth: _calendarMonth, displayIndex: _displayIndex, ...divProps } = props;
111
+ <div {...divProps}>{props.children}</div>
112
+ }
113
+ export type MonthCaptionProps = MonthProps;
114
+ export function MonthCaption(props: MonthCaptionProps) @{
115
+ const { calendarMonth: _calendarMonth, displayIndex: _displayIndex, ...divProps } = props;
116
+ <div {...divProps} />
117
+ }
118
+ export function MonthGrid(props: TableHTMLAttributes<HTMLTableElement>) @{
119
+ <table {...props} />
120
+ }
121
+ export type MonthGridProps = Parameters<typeof MonthGrid>[0];
122
+ export function Months(props: HTMLAttributes<HTMLDivElement>) @{
123
+ <div {...props} />
124
+ }
125
+ export type MonthsProps = Parameters<typeof Months>[0];
126
+
127
+ export function MonthsDropdown(props: DropdownProps) @{
128
+ const { components } = useDayPicker();
129
+ <components.Dropdown {...props} />
130
+ }
131
+ export function YearsDropdown(props: DropdownProps) @{
132
+ const { components } = useDayPicker();
133
+ <components.Dropdown {...props} />
134
+ }
135
+
136
+ export type NavProps =
137
+ {
138
+ onPreviousClick?: MouseEventHandler<HTMLButtonElement>;
139
+ onNextClick?: MouseEventHandler<HTMLButtonElement>;
140
+ previousMonth?: Date;
141
+ nextMonth?: Date;
142
+ } & HTMLAttributes<HTMLElement>;
143
+ export function Nav(props: NavProps) @{
144
+ const { onPreviousClick, onNextClick, previousMonth, nextMonth, ...navProps } = props;
145
+ const { components, classNames, styles, labels: { labelPrevious, labelNext } } = useDayPicker();
146
+ const handleNextClick = useCallback((event: ReactMouseEvent<HTMLButtonElement>) => {
147
+ if (nextMonth) onNextClick?.(event);
148
+ }, [nextMonth, onNextClick]);
149
+ const handlePreviousClick = useCallback((event: ReactMouseEvent<HTMLButtonElement>) => {
150
+ if (previousMonth) onPreviousClick?.(event);
151
+ }, [previousMonth, onPreviousClick]);
152
+ <nav {...navProps}>
153
+ <components.PreviousMonthButton
154
+ type="button"
155
+ className={classNames[UI.PreviousMonthButton]}
156
+ style={styles?.[UI.PreviousMonthButton]}
157
+ tabIndex={previousMonth ? undefined : -1}
158
+ aria-disabled={previousMonth ? undefined : true}
159
+ aria-label={labelPrevious(previousMonth)}
160
+ onClick={handlePreviousClick}
161
+ >
162
+ <components.Chevron
163
+ disabled={previousMonth ? undefined : true}
164
+ className={classNames[UI.Chevron]}
165
+ style={styles?.[UI.Chevron]}
166
+ orientation="left"
167
+ />
168
+ </components.PreviousMonthButton>
169
+ <components.NextMonthButton
170
+ type="button"
171
+ className={classNames[UI.NextMonthButton]}
172
+ style={styles?.[UI.NextMonthButton]}
173
+ tabIndex={nextMonth ? undefined : -1}
174
+ aria-disabled={nextMonth ? undefined : true}
175
+ aria-label={labelNext(nextMonth)}
176
+ onClick={handleNextClick}
177
+ >
178
+ <components.Chevron
179
+ disabled={nextMonth ? undefined : true}
180
+ className={classNames[UI.Chevron]}
181
+ style={styles?.[UI.Chevron]}
182
+ orientation="right"
183
+ />
184
+ </components.NextMonthButton>
185
+ </nav>
186
+ }
187
+
188
+ export function NextMonthButton(props: ButtonHTMLAttributes<HTMLButtonElement>) @{
189
+ <button {...props} />
190
+ }
191
+ export type NextMonthButtonProps = Parameters<typeof NextMonthButton>[0];
192
+ export function PreviousMonthButton(props: ButtonHTMLAttributes<HTMLButtonElement>) @{
193
+ <button {...props} />
194
+ }
195
+ export type PreviousMonthButtonProps = Parameters<typeof PreviousMonthButton>[0];
196
+ export function Option(props: OptionHTMLAttributes<HTMLOptionElement>) @{
197
+ <option {...props} />
198
+ }
199
+ export type OptionProps = Parameters<typeof Option>[0];
200
+ export function Select(props: SelectHTMLAttributes<HTMLSelectElement>) @{
201
+ <select {...props} />
202
+ }
203
+ export type SelectProps = Parameters<typeof Select>[0];
204
+ export type RootProps = { rootRef?: Ref<HTMLDivElement> } & HTMLAttributes<HTMLDivElement>;
205
+ export function Root(props: RootProps) @{
206
+ const { rootRef, ...rest } = props;
207
+ <div {...rest} ref={rootRef} />
208
+ }
209
+
210
+ export type WeekProps = { week: CalendarWeek } & HTMLAttributes<HTMLTableRowElement>;
211
+ export function Week(props: WeekProps) @{
212
+ const { week: _week, ...rowProps } = props;
213
+ <tr {...rowProps} />
214
+ }
215
+ export type WeekNumberProps = { week: CalendarWeek } & ThHTMLAttributes<HTMLTableCellElement>;
216
+ export function WeekNumber(props: WeekNumberProps) @{
217
+ const { week: _week, ...cellProps } = props;
218
+ <th {...cellProps} />
219
+ }
220
+ export function WeekNumberHeader(props: ThHTMLAttributes<HTMLTableCellElement>) @{
221
+ <th {...props} />
222
+ }
223
+ export type WeekNumberHeaderProps = Parameters<typeof WeekNumberHeader>[0];
224
+ export function Weekday(props: ThHTMLAttributes<HTMLTableCellElement>) @{
225
+ <th {...props} />
226
+ }
227
+ export type WeekdayProps = Parameters<typeof Weekday>[0];
228
+ export function Weekdays(props: HTMLAttributes<HTMLTableRowElement>) @{
229
+ <thead aria-hidden>
230
+ <tr {...props} />
231
+ </thead>
232
+ }
233
+ export type WeekdaysProps = Parameters<typeof Weekdays>[0];
234
+ export function Weeks(props: HTMLAttributes<HTMLTableSectionElement>) @{
235
+ <tbody {...props} />
236
+ }
237
+ export type WeeksProps = Parameters<typeof Weeks>[0];
@@ -0,0 +1,18 @@
1
+ import { DateLib, type DateLibOptions } from '../classes/DateLib.js';
2
+
3
+ /**
4
+ * Formats the caption of the month.
5
+ *
6
+ * @defaultValue Locale-specific month/year order (e.g., "November 2022").
7
+ * @param month The date representing the month.
8
+ * @param options Configuration options for the date library.
9
+ * @param dateLib The date library to use for formatting. If not provided, a new
10
+ * instance is created.
11
+ * @returns The formatted caption as a string.
12
+ * @group Formatters
13
+ * @see https://daypicker.dev/docs/translation#custom-formatters
14
+ */
15
+ export function formatCaption(month: Date, options?: DateLibOptions, dateLib?: DateLib) {
16
+ const lib = dateLib ?? new DateLib(options);
17
+ return lib.formatMonthYear(month);
18
+ }
@@ -0,0 +1,17 @@
1
+ import { DateLib, type DateLibOptions } from '../classes/DateLib.js';
2
+
3
+ /**
4
+ * Formats the day date shown in the day cell.
5
+ *
6
+ * @defaultValue `d` (e.g., "1").
7
+ * @param date The date to format.
8
+ * @param options Configuration options for the date library.
9
+ * @param dateLib The date library to use for formatting. If not provided, a new
10
+ * instance is created.
11
+ * @returns The formatted day as a string.
12
+ * @group Formatters
13
+ * @see https://daypicker.dev/docs/translation#custom-formatters
14
+ */
15
+ export function formatDay(date: Date, options?: DateLibOptions, dateLib?: DateLib) {
16
+ return (dateLib ?? new DateLib(options)).format(date, 'd');
17
+ }
@@ -0,0 +1,16 @@
1
+ import { type DateLib, defaultDateLib } from '../classes/DateLib.js';
2
+
3
+ /**
4
+ * Formats the month for the dropdown option label.
5
+ *
6
+ * @defaultValue The localized full month name.
7
+ * @param month The date representing the month.
8
+ * @param dateLib The date library to use for formatting. Defaults to
9
+ * `defaultDateLib`.
10
+ * @returns The formatted month name as a string.
11
+ * @group Formatters
12
+ * @see https://daypicker.dev/docs/translation#custom-formatters
13
+ */
14
+ export function formatMonthDropdown(month: Date, dateLib: DateLib = defaultDateLib): string {
15
+ return dateLib.format(month, 'LLLL');
16
+ }
@@ -0,0 +1,19 @@
1
+ import { defaultDateLib } from '../classes/DateLib.js';
2
+
3
+ /**
4
+ * Formats the week number.
5
+ *
6
+ * @defaultValue The week number as a string, with a leading zero for single-digit numbers.
7
+ * @param weekNumber The week number to format.
8
+ * @param dateLib The date library to use for formatting. Defaults to
9
+ * `defaultDateLib`.
10
+ * @returns The formatted week number as a string.
11
+ * @group Formatters
12
+ * @see https://daypicker.dev/docs/translation#custom-formatters
13
+ */
14
+ export function formatWeekNumber(weekNumber: number, dateLib = defaultDateLib) {
15
+ if (weekNumber < 10) {
16
+ return dateLib.formatNumber(`0${weekNumber.toLocaleString()}`);
17
+ }
18
+ return dateLib.formatNumber(`${weekNumber.toLocaleString()}`);
19
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Formats the header for the week number column.
3
+ *
4
+ * @defaultValue An empty string `""`.
5
+ * @returns The formatted week number header as a string.
6
+ * @group Formatters
7
+ * @see https://daypicker.dev/docs/translation#custom-formatters
8
+ */
9
+ export function formatWeekNumberHeader() {
10
+ return ``;
11
+ }
@@ -0,0 +1,17 @@
1
+ import { DateLib, type DateLibOptions } from '../classes/DateLib.js';
2
+
3
+ /**
4
+ * Formats the name of a weekday to be displayed in the weekdays header.
5
+ *
6
+ * @defaultValue `cccccc` (e.g., "Mo" for Monday).
7
+ * @param weekday The date representing the weekday.
8
+ * @param options Configuration options for the date library.
9
+ * @param dateLib The date library to use for formatting. If not provided, a new
10
+ * instance is created.
11
+ * @returns The formatted weekday name as a string.
12
+ * @group Formatters
13
+ * @see https://daypicker.dev/docs/translation#custom-formatters
14
+ */
15
+ export function formatWeekdayName(weekday: Date, options?: DateLibOptions, dateLib?: DateLib) {
16
+ return (dateLib ?? new DateLib(options)).format(weekday, 'cccccc');
17
+ }
@@ -0,0 +1,15 @@
1
+ import { type DateLib, defaultDateLib } from '../classes/DateLib.js';
2
+
3
+ /**
4
+ * Formats the year for the dropdown option label.
5
+ *
6
+ * @param year The year to format.
7
+ * @param dateLib The date library to use for formatting. Defaults to
8
+ * `defaultDateLib`.
9
+ * @returns The formatted year as a string.
10
+ * @group Formatters
11
+ * @see https://daypicker.dev/docs/translation#custom-formatters
12
+ */
13
+ export function formatYearDropdown(year: Date, dateLib: DateLib = defaultDateLib): string {
14
+ return dateLib.format(year, 'yyyy');
15
+ }
@@ -0,0 +1,7 @@
1
+ export * from './formatCaption.js';
2
+ export * from './formatDay.js';
3
+ export * from './formatMonthDropdown.js';
4
+ export * from './formatWeekdayName.js';
5
+ export * from './formatWeekNumber.js';
6
+ export * from './formatWeekNumberHeader.js';
7
+ export * from './formatYearDropdown.js';
@@ -0,0 +1,79 @@
1
+ import type { CalendarDay } from '../classes/index.js';
2
+ import type { Modifiers } from '../types/index.js';
3
+ import { DayFlag } from '../UI.js';
4
+
5
+ enum FocusTargetPriority {
6
+ Today = 0,
7
+ Selected,
8
+ LastFocused,
9
+ FocusedModifier,
10
+ }
11
+
12
+ /**
13
+ * Determines if a day is focusable based on its modifiers.
14
+ *
15
+ * A day is considered focusable if it is not disabled, hidden, or outside the
16
+ * displayed month.
17
+ *
18
+ * @param modifiers The modifiers applied to the day.
19
+ * @returns `true` if the day is focusable, otherwise `false`.
20
+ */
21
+ function isFocusableDay(modifiers: Modifiers) {
22
+ return !modifiers[DayFlag.disabled] && !modifiers[DayFlag.hidden] && !modifiers[DayFlag.outside];
23
+ }
24
+
25
+ /**
26
+ * Calculates the focus target day based on priority.
27
+ *
28
+ * This function determines the day that should receive focus in the calendar,
29
+ * prioritizing days with specific modifiers (e.g., "focused", "today") or
30
+ * selection states.
31
+ *
32
+ * @param days The array of `CalendarDay` objects to evaluate.
33
+ * @param getModifiers A function to retrieve the modifiers for a given day.
34
+ * @param isSelected A function to determine if a day is selected.
35
+ * @param lastFocused The last focused day, if any.
36
+ * @returns The `CalendarDay` that should receive focus, or `undefined` if no
37
+ * focusable day is found.
38
+ */
39
+ export function calculateFocusTarget(
40
+ days: CalendarDay[],
41
+ getModifiers: (day: CalendarDay) => Modifiers,
42
+ isSelected: (date: Date) => boolean,
43
+ lastFocused: CalendarDay | undefined,
44
+ ): CalendarDay | undefined {
45
+ let focusTarget: CalendarDay | undefined;
46
+
47
+ let foundFocusTargetPriority: FocusTargetPriority | -1 = -1;
48
+ for (const day of days) {
49
+ const modifiers = getModifiers(day);
50
+
51
+ if (isFocusableDay(modifiers)) {
52
+ if (
53
+ modifiers[DayFlag.focused] &&
54
+ foundFocusTargetPriority < FocusTargetPriority.FocusedModifier
55
+ ) {
56
+ focusTarget = day;
57
+ foundFocusTargetPriority = FocusTargetPriority.FocusedModifier;
58
+ } else if (
59
+ lastFocused?.isEqualTo(day) &&
60
+ foundFocusTargetPriority < FocusTargetPriority.LastFocused
61
+ ) {
62
+ focusTarget = day;
63
+ foundFocusTargetPriority = FocusTargetPriority.LastFocused;
64
+ } else if (isSelected(day.date) && foundFocusTargetPriority < FocusTargetPriority.Selected) {
65
+ focusTarget = day;
66
+ foundFocusTargetPriority = FocusTargetPriority.Selected;
67
+ } else if (modifiers[DayFlag.today] && foundFocusTargetPriority < FocusTargetPriority.Today) {
68
+ focusTarget = day;
69
+ foundFocusTargetPriority = FocusTargetPriority.Today;
70
+ }
71
+ }
72
+ }
73
+
74
+ if (!focusTarget) {
75
+ // Return the first day that is focusable
76
+ focusTarget = days.find((day) => isFocusableDay(getModifiers(day)));
77
+ }
78
+ return focusTarget;
79
+ }
@@ -0,0 +1,117 @@
1
+ import type { CalendarDay, DateLib } from '../classes/index.js';
2
+ import type { DayPickerProps, Modifiers } from '../types/index.js';
3
+ import { DayFlag } from '../UI.js';
4
+ import { dateMatchModifiers } from '../utils/dateMatchModifiers.js';
5
+
6
+ /**
7
+ * Creates a function to retrieve the modifiers for a given day.
8
+ *
9
+ * This function calculates both internal and custom modifiers for each day
10
+ * based on the provided calendar days and DayPicker props.
11
+ *
12
+ * @private
13
+ * @param days The array of `CalendarDay` objects to process.
14
+ * @param props The DayPicker props, including modifiers and configuration
15
+ * options.
16
+ * @param dateLib The date library to use for date manipulation.
17
+ * @returns A function that retrieves the modifiers for a given `CalendarDay`.
18
+ */
19
+ export function createGetModifiers(
20
+ days: CalendarDay[],
21
+ props: DayPickerProps,
22
+ navStart: Date | undefined,
23
+ navEnd: Date | undefined,
24
+ dateLib: DateLib,
25
+ ) {
26
+ const {
27
+ disabled,
28
+ hidden,
29
+ modifiers,
30
+ showOutsideDays,
31
+ broadcastCalendar,
32
+ today = dateLib.today(),
33
+ } = props;
34
+
35
+ const { isSameDay, isSameMonth, startOfMonth, isBefore, endOfMonth, isAfter } = dateLib;
36
+
37
+ const computedNavStart = navStart && startOfMonth(navStart);
38
+ const computedNavEnd = navEnd && endOfMonth(navEnd);
39
+
40
+ const internalModifiersMap: Record<DayFlag, CalendarDay[]> = {
41
+ [DayFlag.focused]: [],
42
+ [DayFlag.outside]: [],
43
+ [DayFlag.disabled]: [],
44
+ [DayFlag.hidden]: [],
45
+ [DayFlag.today]: [],
46
+ };
47
+
48
+ const customModifiersMap: Record<string, CalendarDay[]> = {};
49
+
50
+ for (const day of days) {
51
+ const { date, displayMonth } = day;
52
+
53
+ const isOutside = Boolean(displayMonth && !isSameMonth(date, displayMonth));
54
+
55
+ const isBeforeNavStart = Boolean(computedNavStart && isBefore(date, computedNavStart));
56
+
57
+ const isAfterNavEnd = Boolean(computedNavEnd && isAfter(date, computedNavEnd));
58
+
59
+ const isDisabled = Boolean(disabled && dateMatchModifiers(date, disabled, dateLib));
60
+
61
+ const isHidden =
62
+ Boolean(hidden && dateMatchModifiers(date, hidden, dateLib)) ||
63
+ isBeforeNavStart ||
64
+ isAfterNavEnd ||
65
+ // Broadcast calendar will show outside days as default
66
+ (!broadcastCalendar && !showOutsideDays && isOutside) ||
67
+ (broadcastCalendar && showOutsideDays === false && isOutside);
68
+
69
+ const isToday = isSameDay(date, today);
70
+
71
+ if (isOutside) internalModifiersMap.outside.push(day);
72
+ if (isDisabled) internalModifiersMap.disabled.push(day);
73
+ if (isHidden) internalModifiersMap.hidden.push(day);
74
+ if (isToday) internalModifiersMap.today.push(day);
75
+
76
+ // Add custom modifiers
77
+ if (modifiers) {
78
+ Object.keys(modifiers).forEach((name) => {
79
+ const modifierValue = modifiers?.[name];
80
+ const isMatch = modifierValue ? dateMatchModifiers(date, modifierValue, dateLib) : false;
81
+ if (!isMatch) return;
82
+ if (customModifiersMap[name]) {
83
+ customModifiersMap[name].push(day);
84
+ } else {
85
+ customModifiersMap[name] = [day];
86
+ }
87
+ });
88
+ }
89
+ }
90
+
91
+ return (day: CalendarDay): Modifiers => {
92
+ // Initialize all the modifiers to false
93
+ const dayFlags: Record<DayFlag, boolean> = {
94
+ [DayFlag.focused]: false,
95
+ [DayFlag.disabled]: false,
96
+ [DayFlag.hidden]: false,
97
+ [DayFlag.outside]: false,
98
+ [DayFlag.today]: false,
99
+ };
100
+ const customModifiers: Modifiers = {};
101
+
102
+ // Find the modifiers for the given day
103
+ for (const name in internalModifiersMap) {
104
+ const days = internalModifiersMap[name as DayFlag];
105
+ dayFlags[name as DayFlag] = days.some((d) => d === day);
106
+ }
107
+ for (const name in customModifiersMap) {
108
+ customModifiers[name] = customModifiersMap[name].some((d) => d === day);
109
+ }
110
+
111
+ return {
112
+ ...dayFlags,
113
+ // custom modifiers should override all the previous ones
114
+ ...customModifiers,
115
+ };
116
+ };
117
+ }
@@ -0,0 +1,22 @@
1
+ import type { DateLib } from '../classes/index.js';
2
+
3
+ import { getBroadcastWeeksInMonth } from './getBroadcastWeeksInMonth.js';
4
+ import { startOfBroadcastWeek } from './startOfBroadcastWeek.js';
5
+
6
+ /**
7
+ * Returns the end date of the week in the broadcast calendar.
8
+ *
9
+ * The broadcast week ends on the last day of the last broadcast week for the
10
+ * given date.
11
+ *
12
+ * @since 9.4.0
13
+ * @param date The date for which to calculate the end of the broadcast week.
14
+ * @param dateLib The date library to use for date manipulation.
15
+ * @returns The end date of the broadcast week.
16
+ */
17
+ export function endOfBroadcastWeek(date: Date, dateLib: DateLib): Date {
18
+ const startDate = startOfBroadcastWeek(date, dateLib);
19
+ const numberOfWeeks = getBroadcastWeeksInMonth(date, dateLib);
20
+ const endDate = dateLib.addDays(startDate, numberOfWeeks * 7 - 1);
21
+ return endDate;
22
+ }
@@ -0,0 +1,32 @@
1
+ import type { DateLib } from '../classes/index.js';
2
+
3
+ const FIVE_WEEKS = 5;
4
+ const FOUR_WEEKS = 4;
5
+
6
+ /**
7
+ * Returns the number of weeks to display in the broadcast calendar for a given
8
+ * month.
9
+ *
10
+ * The broadcast calendar may have either 4 or 5 weeks in a month, depending on
11
+ * the start and end dates of the broadcast weeks.
12
+ *
13
+ * @since 9.4.0
14
+ * @param month The month for which to calculate the number of weeks.
15
+ * @param dateLib The date library to use for date manipulation.
16
+ * @returns The number of weeks in the broadcast calendar (4 or 5).
17
+ */
18
+ export function getBroadcastWeeksInMonth(month: Date, dateLib: DateLib): 4 | 5 {
19
+ // Get the first day of the month
20
+ const firstDayOfMonth = dateLib.startOfMonth(month);
21
+
22
+ // Get the day of the week for the first day of the month (1-7, where 1 is Monday)
23
+ const firstDayOfWeek = firstDayOfMonth.getDay() > 0 ? firstDayOfMonth.getDay() : 7;
24
+
25
+ const broadcastStartDate = dateLib.addDays(month, -firstDayOfWeek + 1);
26
+
27
+ const lastDateOfLastWeek = dateLib.addDays(broadcastStartDate, FIVE_WEEKS * 7 - 1);
28
+ const numberOfWeeks =
29
+ dateLib.getMonth(month) === dateLib.getMonth(lastDateOfLastWeek) ? FIVE_WEEKS : FOUR_WEEKS;
30
+
31
+ return numberOfWeeks;
32
+ }
@@ -0,0 +1,38 @@
1
+ import type { ClassNames, ModifiersClassNames } from '../types/index.js';
2
+ import { DayFlag, SelectionState, UI } from '../UI.js';
3
+
4
+ /**
5
+ * Returns the class names for a day based on its modifiers.
6
+ *
7
+ * This function combines the base class name for the day with any class names
8
+ * associated with active modifiers.
9
+ *
10
+ * @param modifiers The modifiers applied to the day.
11
+ * @param classNames The base class names for the calendar elements.
12
+ * @param modifiersClassNames The class names associated with specific
13
+ * modifiers.
14
+ * @returns An array of class names for the day.
15
+ */
16
+ export function getClassNamesForModifiers(
17
+ modifiers: Record<string, boolean>,
18
+ classNames: ClassNames,
19
+ modifiersClassNames: ModifiersClassNames = {},
20
+ ): string[] {
21
+ const modifierClassNames = Object.entries(modifiers)
22
+ .filter(([, active]) => active === true)
23
+ .reduce(
24
+ (previousValue, [key]) => {
25
+ if (modifiersClassNames[key]) {
26
+ previousValue.push(modifiersClassNames[key as string]);
27
+ } else if (classNames[DayFlag[key as DayFlag]]) {
28
+ previousValue.push(classNames[DayFlag[key as DayFlag]]);
29
+ } else if (classNames[SelectionState[key as SelectionState]]) {
30
+ previousValue.push(classNames[SelectionState[key as SelectionState]]);
31
+ }
32
+ return previousValue;
33
+ },
34
+ [classNames[UI.Day]] as string[],
35
+ );
36
+
37
+ return modifierClassNames;
38
+ }
@@ -0,0 +1,19 @@
1
+ import * as components from '../components/custom-components.tsrx';
2
+ import type { CustomComponents, DayPickerProps } from '../types/index.js';
3
+
4
+ /**
5
+ * Merges custom components from the props with the default components.
6
+ *
7
+ * This function ensures that any custom components provided in the props
8
+ * override the default components.
9
+ *
10
+ * @param customComponents The custom components provided in the DayPicker
11
+ * props.
12
+ * @returns An object containing the merged components.
13
+ */
14
+ export function getComponents(customComponents: DayPickerProps['components']): CustomComponents {
15
+ return {
16
+ ...components,
17
+ ...customComponents,
18
+ };
19
+ }