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.
- package/dist/cjs/DayPicker.js +15 -3
- package/dist/cjs/DayPicker.js.map +1 -1
- package/dist/cjs/classes/DateLib.js +4 -4
- package/dist/cjs/classes/DateLib.js.map +1 -1
- package/dist/cjs/useAnimation.js +74 -42
- package/dist/cjs/useAnimation.js.map +1 -1
- package/dist/esm/DayPicker.js +15 -3
- package/dist/esm/DayPicker.js.map +1 -1
- package/dist/esm/classes/DateLib.js +1 -1
- package/dist/esm/classes/DateLib.js.map +1 -1
- package/dist/esm/useAnimation.js +74 -42
- package/dist/esm/useAnimation.js.map +1 -1
- package/package.json +2 -4
- package/src/.eslintignore +1 -0
- package/src/.eslintrc.cjs +27 -0
- package/src/DayPicker.test.tsx +199 -0
- package/src/DayPicker.tsx +630 -0
- package/src/UI.ts +365 -0
- package/src/classes/CalendarDay.test.ts +17 -0
- package/src/classes/CalendarDay.ts +61 -0
- package/src/classes/CalendarMonth.test.ts +28 -0
- package/src/classes/CalendarMonth.ts +15 -0
- package/src/classes/CalendarWeek.test.ts +21 -0
- package/src/classes/CalendarWeek.ts +13 -0
- package/src/classes/DateLib.ts +615 -0
- package/src/classes/index.ts +4 -0
- package/src/components/Button.tsx +13 -0
- package/src/components/CaptionLabel.tsx +13 -0
- package/src/components/Chevron.tsx +42 -0
- package/src/components/Day.tsx +28 -0
- package/src/components/DayButton.tsx +29 -0
- package/src/components/Dropdown.tsx +71 -0
- package/src/components/DropdownNav.tsx +13 -0
- package/src/components/Footer.tsx +13 -0
- package/src/components/Month.tsx +24 -0
- package/src/components/MonthCaption.tsx +23 -0
- package/src/components/MonthGrid.tsx +13 -0
- package/src/components/Months.tsx +13 -0
- package/src/components/MonthsDropdown.tsx +16 -0
- package/src/components/Nav.tsx +90 -0
- package/src/components/NextMonthButton.tsx +18 -0
- package/src/components/Option.tsx +13 -0
- package/src/components/PreviousMonthButton.tsx +20 -0
- package/src/components/Root.tsx +19 -0
- package/src/components/Select.tsx +13 -0
- package/src/components/Week.tsx +20 -0
- package/src/components/WeekNumber.tsx +21 -0
- package/src/components/WeekNumberHeader.tsx +15 -0
- package/src/components/Weekday.tsx +13 -0
- package/src/components/Weekdays.tsx +17 -0
- package/src/components/Weeks.tsx +13 -0
- package/src/components/YearsDropdown.tsx +16 -0
- package/src/components/custom-components.tsx +26 -0
- package/src/formatters/formatCaption.test.ts +27 -0
- package/src/formatters/formatCaption.ts +23 -0
- package/src/formatters/formatDay.test.ts +7 -0
- package/src/formatters/formatDay.ts +16 -0
- package/src/formatters/formatMonthDropdown.test.ts +19 -0
- package/src/formatters/formatMonthDropdown.ts +15 -0
- package/src/formatters/formatWeekNumber.test.ts +5 -0
- package/src/formatters/formatWeekNumber.ts +13 -0
- package/src/formatters/formatWeekNumberHeader.ts +10 -0
- package/src/formatters/formatWeekdayName.test.ts +15 -0
- package/src/formatters/formatWeekdayName.ts +16 -0
- package/src/formatters/formatYearDropdown.test.ts +7 -0
- package/src/formatters/formatYearDropdown.ts +21 -0
- package/src/formatters/index.ts +7 -0
- package/src/helpers/broadcastCalendar.test.ts +43 -0
- package/src/helpers/calculateFocusTarget.ts +51 -0
- package/src/helpers/endOfBroadcastWeek.test.ts +25 -0
- package/src/helpers/endOfBroadcastWeek.ts +16 -0
- package/src/helpers/getBroadcastWeeksInMonth.test.ts +23 -0
- package/src/helpers/getBroadcastWeeksInMonth.ts +31 -0
- package/src/helpers/getClassNamesForModifiers.ts +26 -0
- package/src/helpers/getComponents.ts +11 -0
- package/src/helpers/getDataAttributes.test.tsx +48 -0
- package/src/helpers/getDataAttributes.tsx +21 -0
- package/src/helpers/getDates.test.ts +190 -0
- package/src/helpers/getDates.ts +64 -0
- package/src/helpers/getDays.test.ts +30 -0
- package/src/helpers/getDays.ts +16 -0
- package/src/helpers/getDefaultClassNames.test.ts +47 -0
- package/src/helpers/getDefaultClassNames.ts +33 -0
- package/src/helpers/getDisplayMonths.test.ts +44 -0
- package/src/helpers/getDisplayMonths.ts +20 -0
- package/src/helpers/getFocusableDate.ts +59 -0
- package/src/helpers/getFormatters.test.ts +48 -0
- package/src/helpers/getFormatters.ts +19 -0
- package/src/helpers/getInitialMonth.test.ts +79 -0
- package/src/helpers/getInitialMonth.ts +41 -0
- package/src/helpers/getLabels.ts +10 -0
- package/src/helpers/getMonthOptions.test.ts +226 -0
- package/src/helpers/getMonthOptions.ts +37 -0
- package/src/helpers/getMonths.test.ts +88 -0
- package/src/helpers/getMonths.ts +90 -0
- package/src/helpers/getNavMonth.test.ts +253 -0
- package/src/helpers/getNavMonth.ts +70 -0
- package/src/helpers/getNextFocus.test.tsx +99 -0
- package/src/helpers/getNextFocus.tsx +67 -0
- package/src/helpers/getNextMonth.test.ts +101 -0
- package/src/helpers/getNextMonth.ts +45 -0
- package/src/helpers/getPossibleFocusDate.test.ts +144 -0
- package/src/helpers/getPreviousMonth.test.ts +77 -0
- package/src/helpers/getPreviousMonth.ts +40 -0
- package/src/helpers/getStyleForModifiers.test.ts +92 -0
- package/src/helpers/getStyleForModifiers.ts +21 -0
- package/src/helpers/getWeekdays.test.ts +44 -0
- package/src/helpers/getWeekdays.ts +29 -0
- package/src/helpers/getWeeks.test.ts +30 -0
- package/src/helpers/getWeeks.ts +9 -0
- package/src/helpers/getYearOptions.test.ts +46 -0
- package/src/helpers/getYearOptions.ts +34 -0
- package/src/helpers/index.ts +2 -0
- package/src/helpers/startOfBroadcastWeek.test.ts +24 -0
- package/src/helpers/startOfBroadcastWeek.ts +19 -0
- package/src/helpers/useControlledValue.test.ts +45 -0
- package/src/helpers/useControlledValue.ts +33 -0
- package/src/index.ts +15 -0
- package/src/jalali.tsx +2 -0
- package/src/labels/index.ts +12 -0
- package/src/labels/labelDayButton.test.ts +41 -0
- package/src/labels/labelDayButton.ts +31 -0
- package/src/labels/labelGrid.test.ts +7 -0
- package/src/labels/labelGrid.ts +23 -0
- package/src/labels/labelGridcell.test.ts +7 -0
- package/src/labels/labelGridcell.ts +22 -0
- package/src/labels/labelMonthDropdown.test.ts +5 -0
- package/src/labels/labelMonthDropdown.ts +12 -0
- package/src/labels/labelNav.test.ts +5 -0
- package/src/labels/labelNav.ts +10 -0
- package/src/labels/labelNext.test.ts +5 -0
- package/src/labels/labelNext.ts +13 -0
- package/src/labels/labelPrevious.test.ts +5 -0
- package/src/labels/labelPrevious.ts +13 -0
- package/src/labels/labelWeekNumber.test.ts +5 -0
- package/src/labels/labelWeekNumber.ts +15 -0
- package/src/labels/labelWeekNumberHeader.test.ts +5 -0
- package/src/labels/labelWeekNumberHeader.ts +12 -0
- package/src/labels/labelWeekday.test.ts +15 -0
- package/src/labels/labelWeekday.ts +16 -0
- package/src/labels/labelYearDropdown.test.ts +5 -0
- package/src/labels/labelYearDropdown.ts +12 -0
- package/src/locale.ts +1 -0
- package/src/persian.tsx +86 -0
- package/src/selection/useMulti.test.tsx +41 -0
- package/src/selection/useMulti.tsx +74 -0
- package/src/selection/useRange.test.tsx +154 -0
- package/src/selection/useRange.tsx +73 -0
- package/src/selection/useSingle.test.tsx +38 -0
- package/src/selection/useSingle.tsx +69 -0
- package/src/types/deprecated.ts +230 -0
- package/src/types/index.ts +4 -0
- package/src/types/props.test.tsx +71 -0
- package/src/types/props.ts +675 -0
- package/src/types/selection.ts +57 -0
- package/src/types/shared.ts +442 -0
- package/src/useAnimation.test.tsx +190 -0
- package/src/useAnimation.ts +236 -0
- package/src/useCalendar.ts +178 -0
- package/src/useDayPicker.test.tsx +142 -0
- package/src/useDayPicker.ts +93 -0
- package/src/useFocus.ts +87 -0
- package/src/useGetModifiers.test.tsx +154 -0
- package/src/useGetModifiers.tsx +122 -0
- package/src/useSelection.ts +26 -0
- package/src/utc.tsx +10 -0
- package/src/utils/addToRange.test.ts +117 -0
- package/src/utils/addToRange.ts +87 -0
- package/src/utils/dateMatchModifiers.test.ts +120 -0
- package/src/utils/dateMatchModifiers.ts +88 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/rangeContainsDayOfWeek.test.ts +48 -0
- package/src/utils/rangeContainsDayOfWeek.ts +35 -0
- package/src/utils/rangeContainsModifiers.test.ts +230 -0
- package/src/utils/rangeContainsModifiers.ts +125 -0
- package/src/utils/rangeIncludesDate.test.ts +46 -0
- package/src/utils/rangeIncludesDate.ts +43 -0
- package/src/utils/rangeOverlaps.test.ts +60 -0
- package/src/utils/rangeOverlaps.ts +22 -0
- package/src/utils/typeguards.test.ts +83 -0
- package/src/utils/typeguards.ts +70 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import type { DateLib } from "../classes/DateLib.js";
|
|
4
|
+
import { useControlledValue } from "../helpers/useControlledValue.js";
|
|
5
|
+
import type {
|
|
6
|
+
DayPickerProps,
|
|
7
|
+
Modifiers,
|
|
8
|
+
PropsMulti,
|
|
9
|
+
Selection
|
|
10
|
+
} from "../types/index.js";
|
|
11
|
+
|
|
12
|
+
export function useMulti<T extends DayPickerProps>(
|
|
13
|
+
props: T,
|
|
14
|
+
dateLib: DateLib
|
|
15
|
+
): Selection<T> {
|
|
16
|
+
const {
|
|
17
|
+
selected: initiallySelected,
|
|
18
|
+
required,
|
|
19
|
+
onSelect
|
|
20
|
+
} = props as PropsMulti;
|
|
21
|
+
|
|
22
|
+
const [internallySelected, setSelected] = useControlledValue(
|
|
23
|
+
initiallySelected,
|
|
24
|
+
onSelect ? initiallySelected : undefined
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const selected = !onSelect ? internallySelected : initiallySelected;
|
|
28
|
+
|
|
29
|
+
const { isSameDay } = dateLib;
|
|
30
|
+
|
|
31
|
+
const isSelected = (date: Date) => {
|
|
32
|
+
return selected?.some((d) => isSameDay(d, date)) ?? false;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const { min, max } = props as PropsMulti;
|
|
36
|
+
|
|
37
|
+
const select = (
|
|
38
|
+
triggerDate: Date,
|
|
39
|
+
modifiers: Modifiers,
|
|
40
|
+
e: React.MouseEvent | React.KeyboardEvent
|
|
41
|
+
) => {
|
|
42
|
+
let newDates: Date[] | undefined = [...(selected ?? [])];
|
|
43
|
+
if (isSelected(triggerDate)) {
|
|
44
|
+
if (selected?.length === min) {
|
|
45
|
+
// Min value reached, do nothing
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (required && selected?.length === 1) {
|
|
49
|
+
// Required value already selected do nothing
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
newDates = selected?.filter((d) => !isSameDay(d, triggerDate));
|
|
53
|
+
} else {
|
|
54
|
+
if (selected?.length === max) {
|
|
55
|
+
// Max value reached, reset the selection to date
|
|
56
|
+
newDates = [triggerDate];
|
|
57
|
+
} else {
|
|
58
|
+
// Add the date to the selection
|
|
59
|
+
newDates = [...newDates, triggerDate];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (!onSelect) {
|
|
63
|
+
setSelected(newDates);
|
|
64
|
+
}
|
|
65
|
+
onSelect?.(newDates, triggerDate, modifiers, e);
|
|
66
|
+
return newDates;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
selected,
|
|
71
|
+
select,
|
|
72
|
+
isSelected
|
|
73
|
+
} as Selection<T>;
|
|
74
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { act, renderHook } from "@/test/render";
|
|
3
|
+
|
|
4
|
+
import { defaultDateLib } from "../classes/DateLib";
|
|
5
|
+
import type { DayPickerProps } from "../types";
|
|
6
|
+
|
|
7
|
+
import { useRange } from "./useRange";
|
|
8
|
+
|
|
9
|
+
describe("useRange", () => {
|
|
10
|
+
test("initialize with initiallySelected date range", () => {
|
|
11
|
+
const initiallySelected = {
|
|
12
|
+
from: new Date(2023, 6, 1),
|
|
13
|
+
to: new Date(2023, 6, 5)
|
|
14
|
+
};
|
|
15
|
+
const { result } = renderHook(() =>
|
|
16
|
+
useRange(
|
|
17
|
+
{ mode: "range", selected: initiallySelected, required: false },
|
|
18
|
+
defaultDateLib
|
|
19
|
+
)
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
expect(result.current.selected).toEqual(initiallySelected);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("update the selected range on select", () => {
|
|
26
|
+
const initiallySelected = {
|
|
27
|
+
from: new Date(2023, 6, 1),
|
|
28
|
+
to: new Date(2023, 6, 5)
|
|
29
|
+
};
|
|
30
|
+
const { result } = renderHook(() =>
|
|
31
|
+
useRange(
|
|
32
|
+
{ mode: "range", selected: initiallySelected, required: false },
|
|
33
|
+
defaultDateLib
|
|
34
|
+
)
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
act(() => {
|
|
38
|
+
result.current.select?.(new Date(2023, 6, 10), {}, {} as any);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(result.current.selected).toEqual({
|
|
42
|
+
from: new Date(2023, 6, 1),
|
|
43
|
+
to: new Date(2023, 6, 10)
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("reset range if new range exceeds max days", () => {
|
|
48
|
+
const { result } = renderHook(() =>
|
|
49
|
+
useRange(
|
|
50
|
+
{
|
|
51
|
+
mode: "range",
|
|
52
|
+
selected: undefined,
|
|
53
|
+
required: false,
|
|
54
|
+
max: 5
|
|
55
|
+
},
|
|
56
|
+
defaultDateLib
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
act(() => {
|
|
61
|
+
result.current.select?.(new Date(2023, 6, 1), {}, {} as any);
|
|
62
|
+
result.current.select?.(new Date(2023, 6, 10), {}, {} as any);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(result.current.selected).toEqual({
|
|
66
|
+
from: new Date(2023, 6, 10),
|
|
67
|
+
to: new Date(2023, 6, 10)
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("reset range if new range is less than min days", () => {
|
|
72
|
+
const { result } = renderHook(() =>
|
|
73
|
+
useRange(
|
|
74
|
+
{ mode: "range", selected: undefined, required: false, min: 5 },
|
|
75
|
+
defaultDateLib
|
|
76
|
+
)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
act(() => {
|
|
80
|
+
result.current.select?.(new Date(2023, 6, 1), {}, {} as any);
|
|
81
|
+
result.current.select?.(new Date(2023, 6, 3), {}, {} as any);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
expect(result.current.selected).toEqual({
|
|
85
|
+
from: new Date(2023, 6, 3),
|
|
86
|
+
to: undefined
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("exclude disabled dates when selecting range", () => {
|
|
91
|
+
const disabled = [{ from: new Date(2023, 6, 5), to: new Date(2023, 6, 7) }];
|
|
92
|
+
const { result } = renderHook(() =>
|
|
93
|
+
useRange(
|
|
94
|
+
{
|
|
95
|
+
mode: "range",
|
|
96
|
+
selected: undefined,
|
|
97
|
+
required: false,
|
|
98
|
+
excludeDisabled: true,
|
|
99
|
+
disabled
|
|
100
|
+
},
|
|
101
|
+
defaultDateLib
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
act(() => {
|
|
106
|
+
result.current.select?.(new Date(2023, 6, 1), {}, {} as any);
|
|
107
|
+
result.current.select?.(new Date(2023, 6, 10), {}, {} as any);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(result.current.selected).toEqual({
|
|
111
|
+
from: new Date(2023, 6, 10),
|
|
112
|
+
to: new Date(2023, 6, 10)
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
it("uses the selected value from props when onSelect is provided", () => {
|
|
116
|
+
const mockOnSelect = jest.fn();
|
|
117
|
+
const selectedRange = {
|
|
118
|
+
from: new Date(2023, 9, 1),
|
|
119
|
+
to: new Date(2023, 9, 5)
|
|
120
|
+
};
|
|
121
|
+
const props: DayPickerProps = {
|
|
122
|
+
mode: "range",
|
|
123
|
+
|
|
124
|
+
selected: selectedRange,
|
|
125
|
+
onSelect: mockOnSelect
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const { result } = renderHook(() => useRange(props, defaultDateLib));
|
|
129
|
+
|
|
130
|
+
expect(result.current.selected).toBe(selectedRange);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("uses the internally selected value when onSelect is not provided", () => {
|
|
134
|
+
const initialSelectedRange = {
|
|
135
|
+
from: new Date(2023, 9, 1),
|
|
136
|
+
to: new Date(2023, 9, 5)
|
|
137
|
+
};
|
|
138
|
+
const props: DayPickerProps = {
|
|
139
|
+
mode: "range",
|
|
140
|
+
selected: initialSelectedRange
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const { result } = renderHook(() => useRange(props, defaultDateLib));
|
|
144
|
+
|
|
145
|
+
act(() => {
|
|
146
|
+
result.current.select?.(new Date(2023, 9, 6), {}, {} as React.MouseEvent);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(result.current.selected).toEqual({
|
|
150
|
+
from: new Date(2023, 9, 1),
|
|
151
|
+
to: new Date(2023, 9, 6)
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import type { DateLib } from "../classes/DateLib.js";
|
|
4
|
+
import { useControlledValue } from "../helpers/useControlledValue.js";
|
|
5
|
+
import type {
|
|
6
|
+
DayPickerProps,
|
|
7
|
+
Modifiers,
|
|
8
|
+
PropsRange,
|
|
9
|
+
Selection
|
|
10
|
+
} from "../types/index.js";
|
|
11
|
+
import { addToRange, rangeContainsModifiers } from "../utils/index.js";
|
|
12
|
+
import { rangeIncludesDate } from "../utils/rangeIncludesDate.js";
|
|
13
|
+
|
|
14
|
+
export function useRange<T extends DayPickerProps>(
|
|
15
|
+
props: T,
|
|
16
|
+
dateLib: DateLib
|
|
17
|
+
): Selection<T> {
|
|
18
|
+
const {
|
|
19
|
+
disabled,
|
|
20
|
+
excludeDisabled,
|
|
21
|
+
selected: initiallySelected,
|
|
22
|
+
required,
|
|
23
|
+
onSelect
|
|
24
|
+
} = props as PropsRange;
|
|
25
|
+
|
|
26
|
+
const [internallySelected, setSelected] = useControlledValue(
|
|
27
|
+
initiallySelected,
|
|
28
|
+
onSelect ? initiallySelected : undefined
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const selected = !onSelect ? internallySelected : initiallySelected;
|
|
32
|
+
|
|
33
|
+
const isSelected = (date: Date) =>
|
|
34
|
+
selected && rangeIncludesDate(selected, date, false, dateLib);
|
|
35
|
+
|
|
36
|
+
const select = (
|
|
37
|
+
triggerDate: Date,
|
|
38
|
+
modifiers: Modifiers,
|
|
39
|
+
e: React.MouseEvent | React.KeyboardEvent
|
|
40
|
+
) => {
|
|
41
|
+
const { min, max } = props as PropsRange;
|
|
42
|
+
const newRange = triggerDate
|
|
43
|
+
? addToRange(triggerDate, selected, min, max, required, dateLib)
|
|
44
|
+
: undefined;
|
|
45
|
+
|
|
46
|
+
if (excludeDisabled && disabled && newRange?.from && newRange.to) {
|
|
47
|
+
if (
|
|
48
|
+
rangeContainsModifiers(
|
|
49
|
+
{ from: newRange.from, to: newRange.to },
|
|
50
|
+
disabled,
|
|
51
|
+
dateLib
|
|
52
|
+
)
|
|
53
|
+
) {
|
|
54
|
+
// if a disabled days is found, the range is reset
|
|
55
|
+
newRange.from = triggerDate;
|
|
56
|
+
newRange.to = undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!onSelect) {
|
|
61
|
+
setSelected(newRange);
|
|
62
|
+
}
|
|
63
|
+
onSelect?.(newRange, triggerDate, modifiers, e);
|
|
64
|
+
|
|
65
|
+
return newRange;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
selected,
|
|
70
|
+
select,
|
|
71
|
+
isSelected
|
|
72
|
+
} as Selection<T>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { act, renderHook } from "@/test/render";
|
|
2
|
+
|
|
3
|
+
import { defaultDateLib } from "../classes/DateLib";
|
|
4
|
+
import { DayPickerProps } from "../types";
|
|
5
|
+
|
|
6
|
+
import { useSingle } from "./useSingle";
|
|
7
|
+
|
|
8
|
+
describe("useSingle", () => {
|
|
9
|
+
it("uses the selected value from props when onSelect is provided", () => {
|
|
10
|
+
const mockOnSelect = jest.fn();
|
|
11
|
+
const selectedDate = new Date(2023, 9, 1);
|
|
12
|
+
const props: DayPickerProps = {
|
|
13
|
+
mode: "single",
|
|
14
|
+
selected: selectedDate,
|
|
15
|
+
onSelect: mockOnSelect
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const { result } = renderHook(() => useSingle(props, defaultDateLib));
|
|
19
|
+
|
|
20
|
+
expect(result.current.selected).toBe(selectedDate);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("uses the internally selected value when onSelect is not provided", () => {
|
|
24
|
+
const initialSelectedDate = new Date(2023, 9, 1);
|
|
25
|
+
const props: DayPickerProps = {
|
|
26
|
+
mode: "single",
|
|
27
|
+
selected: initialSelectedDate
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const { result } = renderHook(() => useSingle(props, defaultDateLib));
|
|
31
|
+
|
|
32
|
+
act(() => {
|
|
33
|
+
result.current.select?.(new Date(2023, 9, 2), {}, {} as React.MouseEvent);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
expect(result.current.selected).toEqual(new Date(2023, 9, 2));
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import type { DateLib } from "../classes/DateLib.js";
|
|
4
|
+
import { useControlledValue } from "../helpers/useControlledValue.js";
|
|
5
|
+
import type {
|
|
6
|
+
DayPickerProps,
|
|
7
|
+
Modifiers,
|
|
8
|
+
PropsSingle,
|
|
9
|
+
SelectHandler,
|
|
10
|
+
SelectedValue,
|
|
11
|
+
Selection
|
|
12
|
+
} from "../types/index.js";
|
|
13
|
+
|
|
14
|
+
export type UseSingle<T extends DayPickerProps> = {
|
|
15
|
+
select: SelectHandler<T>;
|
|
16
|
+
isSelected: (date: Date) => boolean;
|
|
17
|
+
selected: SelectedValue<T>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function useSingle<T extends DayPickerProps>(
|
|
21
|
+
props: DayPickerProps,
|
|
22
|
+
dateLib: DateLib
|
|
23
|
+
): Selection<T> {
|
|
24
|
+
const {
|
|
25
|
+
selected: initiallySelected,
|
|
26
|
+
required,
|
|
27
|
+
onSelect
|
|
28
|
+
} = props as PropsSingle;
|
|
29
|
+
|
|
30
|
+
const [internallySelected, setSelected] = useControlledValue(
|
|
31
|
+
initiallySelected,
|
|
32
|
+
onSelect ? initiallySelected : undefined
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const selected = !onSelect ? internallySelected : initiallySelected;
|
|
36
|
+
|
|
37
|
+
const { isSameDay } = dateLib;
|
|
38
|
+
|
|
39
|
+
const isSelected = (compareDate: Date) => {
|
|
40
|
+
return selected ? isSameDay(selected, compareDate) : false;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const select = (
|
|
44
|
+
triggerDate: Date,
|
|
45
|
+
modifiers: Modifiers,
|
|
46
|
+
e: React.MouseEvent | React.KeyboardEvent
|
|
47
|
+
) => {
|
|
48
|
+
let newDate: Date | undefined = triggerDate;
|
|
49
|
+
if (!required && selected && selected && isSameDay(triggerDate, selected)) {
|
|
50
|
+
// If the date is the same, clear the selection.
|
|
51
|
+
newDate = undefined;
|
|
52
|
+
}
|
|
53
|
+
if (!onSelect) {
|
|
54
|
+
setSelected(newDate);
|
|
55
|
+
}
|
|
56
|
+
if (required) {
|
|
57
|
+
onSelect?.(newDate as Date, triggerDate, modifiers, e);
|
|
58
|
+
} else {
|
|
59
|
+
onSelect?.(newDate, triggerDate, modifiers, e);
|
|
60
|
+
}
|
|
61
|
+
return newDate;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
selected,
|
|
66
|
+
select,
|
|
67
|
+
isSelected
|
|
68
|
+
} as Selection<T>;
|
|
69
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { DayFlag, SelectionState } from "../UI.js";
|
|
3
|
+
import {
|
|
4
|
+
MonthCaption,
|
|
5
|
+
type MonthCaptionProps
|
|
6
|
+
} from "../components/MonthCaption.js";
|
|
7
|
+
import { Week, type WeekProps } from "../components/Week.js";
|
|
8
|
+
import {
|
|
9
|
+
labelDayButton,
|
|
10
|
+
labelNext,
|
|
11
|
+
labelWeekday,
|
|
12
|
+
labelWeekNumber
|
|
13
|
+
} from "../labels/index.js";
|
|
14
|
+
import { useDayPicker } from "../useDayPicker.js";
|
|
15
|
+
|
|
16
|
+
import type { PropsMulti, PropsRange, PropsSingle } from "./props.js";
|
|
17
|
+
import type { Mode, DayEventHandler } from "./shared.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @ignore
|
|
21
|
+
* @deprecated This type will be removed.
|
|
22
|
+
*/
|
|
23
|
+
export type RootProvider = any;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @ignore
|
|
27
|
+
* @deprecated This type will be removed.
|
|
28
|
+
*/
|
|
29
|
+
export type RootProviderProps = any;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @ignore
|
|
33
|
+
* @deprecated This component has been renamed. Use `MonthCaption` instead.
|
|
34
|
+
* @group Components
|
|
35
|
+
* @see https://daypicker.dev/guides/custom-components
|
|
36
|
+
*/
|
|
37
|
+
export const Caption = MonthCaption;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @ignore
|
|
41
|
+
* @deprecated This type has been renamed. Use `MonthCaptionProps` instead.
|
|
42
|
+
*/
|
|
43
|
+
export type CaptionProps = MonthCaptionProps;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @ignore
|
|
47
|
+
* @deprecated This component has been removed.
|
|
48
|
+
* @group Components
|
|
49
|
+
* @see https://daypicker.dev/guides/custom-components
|
|
50
|
+
*/
|
|
51
|
+
export type HeadRow = any;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @ignore
|
|
55
|
+
* @deprecated This component has been renamed. Use `Week` instead.
|
|
56
|
+
* @group Components
|
|
57
|
+
* @see https://daypicker.dev/guides/custom-components
|
|
58
|
+
*/
|
|
59
|
+
export const Row = Week;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @ignore
|
|
63
|
+
* @deprecated This type has been removed. Use `WeekProps` instead.
|
|
64
|
+
*/
|
|
65
|
+
export type RowProps = WeekProps;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @ignore
|
|
69
|
+
* @deprecated This type has been renamed. Use `PropsSingle` instead.
|
|
70
|
+
*/
|
|
71
|
+
export type DayPickerSingleProps = PropsSingle;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @ignore
|
|
75
|
+
* @deprecated This type has been renamed. Use `PropsMulti` instead.
|
|
76
|
+
*/
|
|
77
|
+
export type DayPickerMultipleProps = PropsMulti;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @ignore
|
|
81
|
+
* @deprecated This type has been renamed. Use `PropsRange` instead.
|
|
82
|
+
*/
|
|
83
|
+
export type DayPickerRangeProps = PropsRange;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @ignore
|
|
87
|
+
* @deprecated This type will be removed. Use `NonNullable<unknown>` instead
|
|
88
|
+
*/
|
|
89
|
+
export type DayPickerDefaultProps = NonNullable<unknown>;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @ignore
|
|
93
|
+
* @deprecated This type has been renamed. Use `Mode` instead.
|
|
94
|
+
*/
|
|
95
|
+
export type DaySelectionMode = Mode;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @ignore
|
|
99
|
+
* @deprecated This type will be removed. Use `string` instead;
|
|
100
|
+
*/
|
|
101
|
+
export type Modifier = string;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @ignore
|
|
105
|
+
* @deprecated This type will be removed. Use {@link DayFlag} or
|
|
106
|
+
* {@link SelectionState} instead.
|
|
107
|
+
*/
|
|
108
|
+
export type InternalModifier =
|
|
109
|
+
| DayFlag.disabled
|
|
110
|
+
| DayFlag.hidden
|
|
111
|
+
| DayFlag.focused
|
|
112
|
+
| SelectionState.range_end
|
|
113
|
+
| SelectionState.range_middle
|
|
114
|
+
| SelectionState.range_start
|
|
115
|
+
| SelectionState.selected;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @ignore
|
|
119
|
+
* @deprecated This type will be removed. Use `SelectHandler<{mode: "single"}>`
|
|
120
|
+
* instead.
|
|
121
|
+
*/
|
|
122
|
+
export type SelectSingleEventHandler = PropsSingle["onSelect"];
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @ignore
|
|
126
|
+
* @deprecated This type will be removed. Use `SelectHandler<{mode:
|
|
127
|
+
* "multiple"}>` instead.
|
|
128
|
+
*/
|
|
129
|
+
export type SelectMultipleEventHandler = PropsMulti["onSelect"];
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @ignore
|
|
133
|
+
* @deprecated This type will be removed. Use `SelectHandler<{mode: "range"}>`
|
|
134
|
+
* instead.
|
|
135
|
+
*/
|
|
136
|
+
export type SelectRangeEventHandler = PropsRange["onSelect"];
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @ignore
|
|
140
|
+
* @deprecated This type is not used anymore.
|
|
141
|
+
*/
|
|
142
|
+
export type DayPickerProviderProps = any;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @ignore
|
|
146
|
+
* @deprecated This type has been removed to `useDayPicker`.
|
|
147
|
+
* @group Hooks
|
|
148
|
+
*/
|
|
149
|
+
export const useNavigation = useDayPicker;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @ignore
|
|
153
|
+
* @deprecated This hook has been removed. Use a custom `Day` component instead.
|
|
154
|
+
* @group Hooks
|
|
155
|
+
* @see https://daypicker.dev/guides/custom-components
|
|
156
|
+
*/
|
|
157
|
+
export type useDayRender = any;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @ignore
|
|
161
|
+
* @deprecated This type is not used anymore.
|
|
162
|
+
*/
|
|
163
|
+
export type ContextProvidersProps = any;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @ignore
|
|
167
|
+
* @deprecated Use `typeof labelDayButton` instead.
|
|
168
|
+
*/
|
|
169
|
+
export type DayLabel = typeof labelDayButton;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @ignore
|
|
173
|
+
* @deprecated Use `typeof labelNext` or `typeof labelPrevious` instead.
|
|
174
|
+
*/
|
|
175
|
+
export type NavButtonLabel = typeof labelNext;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* @ignore
|
|
179
|
+
* @deprecated Use `typeof labelWeekday` instead.
|
|
180
|
+
*/
|
|
181
|
+
export type WeekdayLabel = typeof labelWeekday;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @ignore
|
|
185
|
+
* @deprecated Use `typeof labelWeekNumber` instead.
|
|
186
|
+
*/
|
|
187
|
+
export type WeekNumberLabel = typeof labelWeekNumber;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @ignore
|
|
191
|
+
* @deprecated Use {@link DayMouseEventHandler} instead.
|
|
192
|
+
*/
|
|
193
|
+
export type DayClickEventHandler = DayEventHandler<React.MouseEvent>;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @ignore
|
|
197
|
+
* @deprecated This type will be removed. Use `DayEventHandler<React.FocusEvent
|
|
198
|
+
* | React.KeyboardEvent>` instead.
|
|
199
|
+
*/
|
|
200
|
+
export type DayFocusEventHandler = DayEventHandler<
|
|
201
|
+
React.FocusEvent | React.KeyboardEvent
|
|
202
|
+
>;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @ignore
|
|
206
|
+
* @deprecated This type will be removed. Use
|
|
207
|
+
* `DayEventHandler<React.KeyboardEvent>` instead.
|
|
208
|
+
*/
|
|
209
|
+
export type DayKeyboardEventHandler = DayEventHandler<React.KeyboardEvent>;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @ignore
|
|
213
|
+
* @deprecated This type will be removed. Use
|
|
214
|
+
* `DayEventHandler<React.MouseEvent>` instead.
|
|
215
|
+
*/
|
|
216
|
+
export type DayMouseEventHandler = DayEventHandler<React.MouseEvent>;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @ignore
|
|
220
|
+
* @deprecated This type will be removed. Use
|
|
221
|
+
* `DayEventHandler<React.PointerEvent>` instead.
|
|
222
|
+
*/
|
|
223
|
+
export type DayPointerEventHandler = DayEventHandler<React.PointerEvent>;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* @ignore
|
|
227
|
+
* @deprecated This type will be removed. Use
|
|
228
|
+
* `DayEventHandler<React.TouchEvent>` instead.
|
|
229
|
+
*/
|
|
230
|
+
export type DayTouchEventHandler = DayEventHandler<React.TouchEvent>;
|