@uxf/datepicker 11.111.2 → 11.114.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hooks/use-date-picker-navigation.d.ts +4 -3
- package/hooks/use-date-picker-navigation.js +30 -34
- package/hooks/use-date-picker-navigation.test.js +40 -4
- package/hooks/use-date-picker.js +40 -26
- package/hooks/use-date-picker.test.js +2 -2
- package/hooks/use-date-range-picker.js +103 -76
- package/hooks/use-day.js +12 -11
- package/hooks/use-decade.js +9 -8
- package/jest.dst.config.js +22 -0
- package/jest.dst.global-setup.js +14 -0
- package/package.json +9 -4
- package/utils/can-select-range.d.ts +6 -5
- package/utils/can-select-range.js +7 -19
- package/utils/can-select-range.test.js +30 -27
- package/utils/date-string.d.ts +34 -0
- package/utils/date-string.js +64 -0
- package/utils/date-string.test.d.ts +1 -0
- package/utils/date-string.test.js +63 -0
- package/utils/dayjs-utils.d.ts +0 -1
- package/utils/dayjs-utils.js +1 -5
- package/utils/dst-robustness.test.d.ts +11 -0
- package/utils/dst-robustness.test.js +328 -0
- package/utils/get-current-year-month-and-date.js +1 -2
- package/utils/get-date-invervals.js +2 -5
- package/utils/get-date-month-and-year.js +6 -5
- package/utils/get-days.js +12 -9
- package/utils/get-each.d.ts +2 -1
- package/utils/get-each.js +37 -9
- package/utils/get-each.test.js +34 -19
- package/utils/get-initial-months.js +3 -6
- package/utils/get-months.js +7 -7
- package/utils/get-next-active-month.js +6 -10
- package/utils/get-weekday-labels.js +4 -2
- package/utils/get-years.js +5 -4
- package/utils/is-date-blocked.d.ts +7 -6
- package/utils/is-date-blocked.js +6 -7
- package/utils/is-date-blocked.test.js +108 -11
- package/utils/is-date-hovered.d.ts +6 -5
- package/utils/is-date-hovered.js +17 -24
- package/utils/is-date-hovered.test.js +42 -25
- package/utils/is-date-inside-range.d.ts +2 -1
- package/utils/is-date-inside-range.js +1 -2
- package/utils/is-date-selected.test.js +16 -8
- package/utils/is-end-date.d.ts +2 -1
- package/utils/is-end-date.js +1 -2
- package/utils/is-end-date.test.js +11 -6
- package/utils/is-first-or-last-selected-date.d.ts +2 -1
- package/utils/is-first-or-last-selected-date.js +1 -3
- package/utils/is-first-or-last-selected-date.test.js +9 -6
- package/utils/is-in-unavailable-dates.d.ts +2 -1
- package/utils/is-in-unavailable-dates.js +1 -2
- package/utils/is-in-unavailable-dates.test.js +10 -6
- package/utils/is-start-date.d.ts +2 -1
- package/utils/is-start-date.js +1 -2
- package/utils/is-start-date.test.js +12 -6
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import { DateString } from "@uxf/core/date";
|
|
1
2
|
import { Dispatch, SetStateAction } from "react";
|
|
2
3
|
import { MonthType } from "../utils/types";
|
|
3
4
|
export interface UseDatePickerNavigationProps {
|
|
4
5
|
activeMonths: MonthType[];
|
|
5
6
|
setActiveMonths: Dispatch<SetStateAction<MonthType[]>>;
|
|
6
|
-
setFocusedDate:
|
|
7
|
-
minBookingDate?:
|
|
8
|
-
maxBookingDate?:
|
|
7
|
+
setFocusedDate: (date: Date | null) => void;
|
|
8
|
+
minBookingDate?: DateString;
|
|
9
|
+
maxBookingDate?: DateString;
|
|
9
10
|
numberOfMonths: number;
|
|
10
11
|
}
|
|
11
12
|
export interface UseDatePickerNavigationReturnType {
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.useDatePickerNavigation = useDatePickerNavigation;
|
|
4
4
|
const react_1 = require("react");
|
|
5
|
-
const
|
|
5
|
+
const create_date_string_1 = require("../utils/create-date-string");
|
|
6
|
+
const date_string_1 = require("../utils/date-string");
|
|
6
7
|
const get_initial_months_1 = require("../utils/get-initial-months");
|
|
7
8
|
const get_next_active_month_1 = require("../utils/get-next-active-month");
|
|
8
9
|
function useDatePickerNavigation({ activeMonths, setActiveMonths, setFocusedDate, minBookingDate, maxBookingDate, numberOfMonths, }) {
|
|
9
|
-
var _a, _b
|
|
10
|
+
var _a, _b;
|
|
10
11
|
const goToPrevMonths = (0, react_1.useCallback)(() => {
|
|
11
12
|
const newActiveMonths = (0, get_next_active_month_1.getNextActiveMonth)(activeMonths, numberOfMonths, -1);
|
|
12
13
|
setActiveMonths(newActiveMonths);
|
|
@@ -49,40 +50,35 @@ function useDatePickerNavigation({ activeMonths, setActiveMonths, setFocusedDate
|
|
|
49
50
|
setFocusedDate(null);
|
|
50
51
|
return newActiveMonths;
|
|
51
52
|
}, [activeMonths, numberOfMonths, setActiveMonths, setFocusedDate]);
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
.isBefore((0, dayjs_utils_1.dayjsEnRaw)(minBookingDate))
|
|
65
|
-
: true;
|
|
66
|
-
const canGoToNextYear = maxBookingDate
|
|
67
|
-
? !(0, dayjs_utils_1.dayjsEnWithTz)(activeMonths[activeMonths.length - 1].date)
|
|
68
|
-
.add(1, "year")
|
|
69
|
-
.startOf("year")
|
|
70
|
-
.isAfter((0, dayjs_utils_1.dayjsEnWithTz)(maxBookingDate))
|
|
71
|
-
: true;
|
|
53
|
+
const firstMonthStr = (0, date_string_1.toDateString)((_b = (_a = activeMonths.at(0)) === null || _a === void 0 ? void 0 : _a.date) !== null && _b !== void 0 ? _b : new Date());
|
|
54
|
+
const lastMonthStr = (0, date_string_1.toDateString)(activeMonths[activeMonths.length - 1].date);
|
|
55
|
+
const firstYear = parseInt(firstMonthStr.slice(0, 4), 10);
|
|
56
|
+
const lastYear = parseInt(lastMonthStr.slice(0, 4), 10);
|
|
57
|
+
// Can navigate back if the first active month is not before minBookingDate.
|
|
58
|
+
const canGoToPrevMonth = !minBookingDate || minBookingDate <= firstMonthStr;
|
|
59
|
+
// Can navigate forward if the month after the last active month is still within maxBookingDate.
|
|
60
|
+
const canGoToNextMonth = !maxBookingDate || (0, date_string_1.addMonths)(lastMonthStr, 1) <= maxBookingDate;
|
|
61
|
+
// Can navigate back a year if Dec 31 of (firstYear - 1) is still on or after minBookingDate.
|
|
62
|
+
const canGoToPrevYear = !minBookingDate || (0, create_date_string_1.createDateString)(firstYear - 1, 11, 31) >= minBookingDate;
|
|
63
|
+
// Can navigate forward a year if Jan 1 of (lastYear + 1) is still within maxBookingDate.
|
|
64
|
+
const canGoToNextYear = !maxBookingDate || (0, create_date_string_1.createDateString)(lastYear + 1, 0) <= maxBookingDate;
|
|
72
65
|
const canGoTo = (0, react_1.useCallback)((month, type) => {
|
|
73
|
-
if (minBookingDate
|
|
74
|
-
|
|
75
|
-
return ((0, dayjs_utils_1.dayjsEnWithTz)(month).endOf(type).add(1, "day").isAfter(minBookingDate, "day") &&
|
|
76
|
-
(0, dayjs_utils_1.dayjsEnWithTz)(month).startOf(type).subtract(1, "day").isBefore(maxBookingDate, "day"));
|
|
77
|
-
}
|
|
78
|
-
if (minBookingDate) {
|
|
79
|
-
return (0, dayjs_utils_1.dayjsEnWithTz)(month).endOf(type).add(1, "day").isAfter(minBookingDate, "day");
|
|
80
|
-
}
|
|
81
|
-
if (maxBookingDate) {
|
|
82
|
-
return (0, dayjs_utils_1.dayjsEnWithTz)(month).startOf(type).subtract(1, "day").isBefore(maxBookingDate, "day");
|
|
83
|
-
}
|
|
66
|
+
if (!minBookingDate && !maxBookingDate) {
|
|
67
|
+
return true;
|
|
84
68
|
}
|
|
85
|
-
|
|
69
|
+
const ds = (0, date_string_1.toDateString)(month);
|
|
70
|
+
const year = parseInt(ds.slice(0, 4), 10);
|
|
71
|
+
if (type === "month") {
|
|
72
|
+
// Month is reachable if its range overlaps [minBookingDate, maxBookingDate].
|
|
73
|
+
// "end of this month + 1 day > min" AND "start of this month - 1 day < max"
|
|
74
|
+
const afterMin = !minBookingDate || (0, date_string_1.addMonths)(ds, 1) > minBookingDate;
|
|
75
|
+
const beforeMax = !maxBookingDate || (0, date_string_1.addDays)(ds, -1) < maxBookingDate;
|
|
76
|
+
return afterMin && beforeMax;
|
|
77
|
+
}
|
|
78
|
+
// type === "year"
|
|
79
|
+
const afterMin = !minBookingDate || (0, create_date_string_1.createDateString)(year + 1, 0) > minBookingDate;
|
|
80
|
+
const beforeMax = !maxBookingDate || (0, create_date_string_1.createDateString)(year - 1, 11, 31) < maxBookingDate;
|
|
81
|
+
return afterMin && beforeMax;
|
|
86
82
|
}, [maxBookingDate, minBookingDate]);
|
|
87
83
|
const canGoToMonth = (0, react_1.useCallback)((date) => canGoTo(date, "month"), [canGoTo]);
|
|
88
84
|
const canGoToYear = (0, react_1.useCallback)((date) => canGoTo(date, "year"), [canGoTo]);
|
|
@@ -24,8 +24,8 @@ test("base data with min and max date", () => {
|
|
|
24
24
|
setActiveMonths: () => undefined,
|
|
25
25
|
numberOfMonths: 1,
|
|
26
26
|
setFocusedDate: () => undefined,
|
|
27
|
-
minBookingDate:
|
|
28
|
-
maxBookingDate:
|
|
27
|
+
minBookingDate: "2015-05-05",
|
|
28
|
+
maxBookingDate: "2025-10-11",
|
|
29
29
|
}));
|
|
30
30
|
expect(result.current.canGoToMonth(new Date("2021-08-01"))).toBe(true);
|
|
31
31
|
expect(result.current.canGoToMonth(new Date("2015-04-01"))).toBe(false);
|
|
@@ -34,6 +34,42 @@ test("base data with min and max date", () => {
|
|
|
34
34
|
expect(result.current.canGoToYear(new Date("2014-01-01"))).toBe(false);
|
|
35
35
|
expect(result.current.canGoToYear(new Date("2026-01-01"))).toBe(false);
|
|
36
36
|
});
|
|
37
|
+
test("canGoToNextMonth edge cases with maxBookingDate", () => {
|
|
38
|
+
// Active month: September 2021 (lastMonthStr = "2021-09-01")
|
|
39
|
+
// addMonths("2021-09-01", 1) = "2021-10-01"
|
|
40
|
+
// maxBookingDate = 1st of next month ("2021-10-01"):
|
|
41
|
+
// "2021-10-01" <= "2021-10-01" → true → can navigate forward
|
|
42
|
+
// (October has at least one bookable day)
|
|
43
|
+
const { result: result1 } = (0, react_1.renderHook)(() => (0, use_date_picker_navigation_1.useDatePickerNavigation)({
|
|
44
|
+
activeMonths: ACTIVE_MONTHS,
|
|
45
|
+
setActiveMonths: () => undefined,
|
|
46
|
+
numberOfMonths: 1,
|
|
47
|
+
setFocusedDate: () => undefined,
|
|
48
|
+
maxBookingDate: "2021-10-01",
|
|
49
|
+
}));
|
|
50
|
+
expect(result1.current.canGoToNextMonth).toBe(true);
|
|
51
|
+
// maxBookingDate = last day of current month ("2021-09-30"):
|
|
52
|
+
// "2021-10-01" <= "2021-09-30" → false → cannot navigate forward
|
|
53
|
+
// (October is entirely past maxBookingDate)
|
|
54
|
+
const { result: result2 } = (0, react_1.renderHook)(() => (0, use_date_picker_navigation_1.useDatePickerNavigation)({
|
|
55
|
+
activeMonths: ACTIVE_MONTHS,
|
|
56
|
+
setActiveMonths: () => undefined,
|
|
57
|
+
numberOfMonths: 1,
|
|
58
|
+
setFocusedDate: () => undefined,
|
|
59
|
+
maxBookingDate: "2021-09-30",
|
|
60
|
+
}));
|
|
61
|
+
expect(result2.current.canGoToNextMonth).toBe(false);
|
|
62
|
+
// maxBookingDate = mid-next-month ("2021-10-15"):
|
|
63
|
+
// "2021-10-01" <= "2021-10-15" → true → can navigate forward
|
|
64
|
+
const { result: result3 } = (0, react_1.renderHook)(() => (0, use_date_picker_navigation_1.useDatePickerNavigation)({
|
|
65
|
+
activeMonths: ACTIVE_MONTHS,
|
|
66
|
+
setActiveMonths: () => undefined,
|
|
67
|
+
numberOfMonths: 1,
|
|
68
|
+
setFocusedDate: () => undefined,
|
|
69
|
+
maxBookingDate: "2021-10-15",
|
|
70
|
+
}));
|
|
71
|
+
expect(result3.current.canGoToNextMonth).toBe(true);
|
|
72
|
+
});
|
|
37
73
|
test("actions", () => {
|
|
38
74
|
const { result: state } = (0, react_1.renderHook)(() => (0, react_2.useState)([{ month: 8, year: 2021, date: new Date("2021-09-01") }]));
|
|
39
75
|
const { result, rerender } = (0, react_1.renderHook)(() => {
|
|
@@ -43,8 +79,8 @@ test("actions", () => {
|
|
|
43
79
|
setActiveMonths,
|
|
44
80
|
numberOfMonths: 1,
|
|
45
81
|
setFocusedDate: () => undefined,
|
|
46
|
-
minBookingDate:
|
|
47
|
-
maxBookingDate:
|
|
82
|
+
minBookingDate: "2020-12-25",
|
|
83
|
+
maxBookingDate: "2022-03-11",
|
|
48
84
|
});
|
|
49
85
|
});
|
|
50
86
|
// go to year 2020
|
package/hooks/use-date-picker.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.useDatePicker = useDatePicker;
|
|
4
4
|
const react_1 = require("react");
|
|
5
|
-
const
|
|
5
|
+
const date_string_1 = require("../utils/date-string");
|
|
6
6
|
const get_initial_months_1 = require("../utils/get-initial-months");
|
|
7
7
|
const is_date_blocked_1 = require("../utils/is-date-blocked");
|
|
8
8
|
const is_date_hovered_1 = require("../utils/is-date-hovered");
|
|
@@ -10,38 +10,50 @@ const is_in_unavailable_dates_1 = require("../utils/is-in-unavailable-dates");
|
|
|
10
10
|
const is_start_date_1 = require("../utils/is-start-date");
|
|
11
11
|
const use_date_picker_navigation_1 = require("./use-date-picker-navigation");
|
|
12
12
|
function useDatePicker({ datesConfig = [], firstDayOfWeek = 0, initialVisibleMonth, isDateBlocked: isDateBlockedProps = () => false, maxBookingDate, minBookingDate, numberOfMonths = 1, onDateChange, preventScroll, selectedDate, unavailableDates = [], }) {
|
|
13
|
+
// --- Normalize all Date inputs to DateString at boundary ---
|
|
14
|
+
const normalizedSelectedDate = selectedDate ? (0, date_string_1.toDateString)(selectedDate) : null;
|
|
15
|
+
const normalizedMinBookingDate = minBookingDate ? (0, date_string_1.toDateString)(minBookingDate) : undefined;
|
|
16
|
+
const normalizedMaxBookingDate = maxBookingDate ? (0, date_string_1.toDateString)(maxBookingDate) : undefined;
|
|
13
17
|
const disabledDatesFromConfig = datesConfig.filter((config) => config.isDisabled).flatMap((config) => config.dates);
|
|
14
|
-
const
|
|
18
|
+
const allDisabledDates = [
|
|
19
|
+
...unavailableDates.map(date_string_1.toDateString),
|
|
20
|
+
...disabledDatesFromConfig.map(date_string_1.toDateString),
|
|
21
|
+
];
|
|
15
22
|
const [activeMonths, setActiveMonths] = (0, react_1.useState)(() => selectedDate
|
|
16
23
|
? (0, get_initial_months_1.getInitialMonths)(numberOfMonths, selectedDate)
|
|
17
24
|
: (0, get_initial_months_1.getInitialMonths)(numberOfMonths, initialVisibleMonth || null));
|
|
18
25
|
const [hoveredDate, setHoveredDate] = (0, react_1.useState)(null);
|
|
19
|
-
const [focusedDate, setFocusedDate] = (0, react_1.useState)(
|
|
26
|
+
const [focusedDate, setFocusedDate] = (0, react_1.useState)(normalizedSelectedDate);
|
|
27
|
+
// Consumer's isDateBlocked callback still receives Date via fromDateString
|
|
20
28
|
const disabledDatesByUser = (date) => {
|
|
21
|
-
return (0, is_in_unavailable_dates_1.isInUnavailableDates)(
|
|
29
|
+
return (0, is_in_unavailable_dates_1.isInUnavailableDates)(allDisabledDates, date) || isDateBlockedProps((0, date_string_1.fromDateString)(date));
|
|
22
30
|
};
|
|
23
31
|
const onDateFocus = (0, react_1.useCallback)((date) => {
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
const dateStr = (0, date_string_1.toDateString)(date);
|
|
33
|
+
setFocusedDate(dateStr);
|
|
34
|
+
const focusedYYYYMM = (0, date_string_1.toDateString)(date).slice(0, 7);
|
|
35
|
+
if (!focusedDate ||
|
|
36
|
+
!activeMonths.some((m) => `${m.year}-${String(m.month + 1).padStart(2, "0")}` === focusedYYYYMM)) {
|
|
26
37
|
setActiveMonths((0, get_initial_months_1.getInitialMonths)(numberOfMonths, date));
|
|
27
38
|
}
|
|
28
39
|
}, [activeMonths, focusedDate, numberOfMonths]);
|
|
29
|
-
const _isDateSelected = (date) => (0, is_start_date_1.isStartDate)(date,
|
|
40
|
+
const _isDateSelected = (date) => (0, is_start_date_1.isStartDate)((0, date_string_1.toDateString)(date), normalizedSelectedDate);
|
|
30
41
|
const _isDateBlocked = (date) => (0, is_date_blocked_1.isDateBlocked)({
|
|
31
|
-
date,
|
|
32
|
-
minBookingDate,
|
|
33
|
-
maxBookingDate,
|
|
34
|
-
startDate:
|
|
35
|
-
endDate:
|
|
42
|
+
date: (0, date_string_1.toDateString)(date),
|
|
43
|
+
minBookingDate: normalizedMinBookingDate,
|
|
44
|
+
maxBookingDate: normalizedMaxBookingDate,
|
|
45
|
+
startDate: normalizedSelectedDate,
|
|
46
|
+
endDate: normalizedSelectedDate,
|
|
36
47
|
minBookingDays: 1,
|
|
37
|
-
isDateBlockedFn:
|
|
48
|
+
isDateBlockedFn: isDateBlockedProps,
|
|
49
|
+
unavailableDates: allDisabledDates,
|
|
38
50
|
});
|
|
39
|
-
const isDateFocused = (date) =>
|
|
51
|
+
const isDateFocused = (date) => focusedDate !== null && (0, date_string_1.toDateString)(date) === focusedDate;
|
|
40
52
|
const _isDateHovered = (date) => (0, is_date_hovered_1.isDateHovered)({
|
|
41
|
-
date,
|
|
53
|
+
date: (0, date_string_1.toDateString)(date),
|
|
42
54
|
hoveredDate,
|
|
43
|
-
startDate:
|
|
44
|
-
endDate:
|
|
55
|
+
startDate: normalizedSelectedDate,
|
|
56
|
+
endDate: normalizedSelectedDate,
|
|
45
57
|
minBookingDays: 1,
|
|
46
58
|
exactMinBookingDays: false,
|
|
47
59
|
isDateBlocked: disabledDatesByUser,
|
|
@@ -65,7 +77,8 @@ function useDatePicker({ datesConfig = [], firstDayOfWeek = 0, initialVisibleMon
|
|
|
65
77
|
onDateChange(null);
|
|
66
78
|
}, [onDateChange]);
|
|
67
79
|
function onDateSelect(date) {
|
|
68
|
-
|
|
80
|
+
const dateStr = (0, date_string_1.toDateString)(date);
|
|
81
|
+
if (dateStr !== normalizedSelectedDate) {
|
|
69
82
|
onDateChange(date);
|
|
70
83
|
}
|
|
71
84
|
else {
|
|
@@ -77,9 +90,10 @@ function useDatePicker({ datesConfig = [], firstDayOfWeek = 0, initialVisibleMon
|
|
|
77
90
|
setHoveredDate(null);
|
|
78
91
|
}
|
|
79
92
|
else {
|
|
80
|
-
const
|
|
93
|
+
const dateStr = (0, date_string_1.toDateString)(date);
|
|
94
|
+
const isNotBlocked = !_isDateBlocked(date) || (normalizedSelectedDate && dateStr === normalizedSelectedDate);
|
|
81
95
|
if (isNotBlocked) {
|
|
82
|
-
setHoveredDate(
|
|
96
|
+
setHoveredDate(dateStr);
|
|
83
97
|
}
|
|
84
98
|
else if (hoveredDate !== null) {
|
|
85
99
|
setHoveredDate(null);
|
|
@@ -89,17 +103,17 @@ function useDatePicker({ datesConfig = [], firstDayOfWeek = 0, initialVisibleMon
|
|
|
89
103
|
return {
|
|
90
104
|
...(0, use_date_picker_navigation_1.useDatePickerNavigation)({
|
|
91
105
|
activeMonths,
|
|
92
|
-
maxBookingDate,
|
|
93
|
-
minBookingDate,
|
|
106
|
+
maxBookingDate: normalizedMaxBookingDate,
|
|
107
|
+
minBookingDate: normalizedMinBookingDate,
|
|
94
108
|
numberOfMonths,
|
|
95
109
|
setActiveMonths,
|
|
96
|
-
setFocusedDate,
|
|
110
|
+
setFocusedDate: (date) => setFocusedDate(date ? (0, date_string_1.toDateString)(date) : null),
|
|
97
111
|
}),
|
|
98
112
|
activeMonths,
|
|
99
113
|
datesConfig,
|
|
100
114
|
firstDayOfWeek,
|
|
101
|
-
focusedDate,
|
|
102
|
-
hoveredDate,
|
|
115
|
+
focusedDate: focusedDate ? (0, date_string_1.fromDateString)(focusedDate) : null,
|
|
116
|
+
hoveredDate: hoveredDate ? (0, date_string_1.fromDateString)(hoveredDate) : null,
|
|
103
117
|
isDateBlocked: _isDateBlocked,
|
|
104
118
|
isDateFocused,
|
|
105
119
|
isDateHovered: _isDateHovered,
|
|
@@ -111,6 +125,6 @@ function useDatePicker({ datesConfig = [], firstDayOfWeek = 0, initialVisibleMon
|
|
|
111
125
|
onResetDates: onReset,
|
|
112
126
|
preventScroll,
|
|
113
127
|
selectedDate,
|
|
114
|
-
unavailableDates:
|
|
128
|
+
unavailableDates: allDisabledDates.map(date_string_1.fromDateString),
|
|
115
129
|
};
|
|
116
130
|
}
|
|
@@ -4,11 +4,11 @@ const react_1 = require("@testing-library/react");
|
|
|
4
4
|
const react_2 = require("react");
|
|
5
5
|
const use_date_picker_1 = require("./use-date-picker");
|
|
6
6
|
test("base data", () => {
|
|
7
|
-
const BASE_DATE = new Date("2021-09-15T00:00:
|
|
7
|
+
const BASE_DATE = new Date("2021-09-15T00:00:00Z");
|
|
8
8
|
const { result } = (0, react_1.renderHook)(() => (0, use_date_picker_1.useDatePicker)({
|
|
9
9
|
selectedDate: BASE_DATE,
|
|
10
10
|
onDateChange: () => undefined,
|
|
11
|
-
minBookingDate: new Date("2020-01-
|
|
11
|
+
minBookingDate: new Date("2020-01-01T00:00:00Z"),
|
|
12
12
|
}));
|
|
13
13
|
expect(result.current.firstDayOfWeek).toBe(0);
|
|
14
14
|
expect(result.current.focusedDate).toStrictEqual(BASE_DATE);
|
|
@@ -4,7 +4,7 @@ exports.END_DATE = exports.START_DATE = void 0;
|
|
|
4
4
|
exports.useDateRangePicker = useDateRangePicker;
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const can_select_range_1 = require("../utils/can-select-range");
|
|
7
|
-
const
|
|
7
|
+
const date_string_1 = require("../utils/date-string");
|
|
8
8
|
const get_initial_months_1 = require("../utils/get-initial-months");
|
|
9
9
|
const is_date_blocked_1 = require("../utils/is-date-blocked");
|
|
10
10
|
const is_date_hovered_1 = require("../utils/is-date-hovered");
|
|
@@ -17,44 +17,62 @@ const use_date_picker_navigation_1 = require("./use-date-picker-navigation");
|
|
|
17
17
|
exports.START_DATE = "startDate";
|
|
18
18
|
exports.END_DATE = "endDate";
|
|
19
19
|
function useDateRangePicker({ changeActiveMonthOnSelect = false, datesConfig = [], endDate, exactMinBookingDays = false, firstDayOfWeek = 0, focusedInput, initialVisibleMonth, isDateBlocked: isDateBlockedProps, maxBookingDate, minBookingDate, minBookingDays = 1, numberOfMonths = 2, onDatesChange, startDate, unavailableDates = [], }) {
|
|
20
|
+
// --- Normalize all Date inputs to DateString at boundary ---
|
|
21
|
+
const normalizedStartDate = startDate ? (0, date_string_1.toDateString)(startDate) : null;
|
|
22
|
+
const normalizedEndDate = endDate ? (0, date_string_1.toDateString)(endDate) : null;
|
|
23
|
+
const normalizedMinBookingDate = minBookingDate ? (0, date_string_1.toDateString)(minBookingDate) : undefined;
|
|
24
|
+
const normalizedMaxBookingDate = maxBookingDate ? (0, date_string_1.toDateString)(maxBookingDate) : undefined;
|
|
20
25
|
const disabledDatesFromConfig = datesConfig.filter((config) => config.isDisabled).flatMap((config) => config.dates);
|
|
21
|
-
const
|
|
26
|
+
const allDisabledDates = [
|
|
27
|
+
...unavailableDates.map(date_string_1.toDateString),
|
|
28
|
+
...disabledDatesFromConfig.map(date_string_1.toDateString),
|
|
29
|
+
];
|
|
22
30
|
const [activeMonths, setActiveMonths] = (0, react_1.useState)(() => startDate
|
|
23
31
|
? (0, get_initial_months_1.getInitialMonths)(numberOfMonths, startDate)
|
|
24
32
|
: (0, get_initial_months_1.getInitialMonths)(numberOfMonths, initialVisibleMonth || null));
|
|
25
33
|
const [hoveredDate, setHoveredDate] = (0, react_1.useState)(null);
|
|
26
|
-
const [focusedDate, setFocusedDate] = (0, react_1.useState)(
|
|
27
|
-
|
|
34
|
+
const [focusedDate, setFocusedDate] = (0, react_1.useState)(normalizedStartDate);
|
|
35
|
+
// Consumer's isDateBlockedProps (Date → boolean) wrapped as DateString → boolean
|
|
36
|
+
const disabledDatesByUser = isDateBlockedProps || allDisabledDates.length > 0
|
|
28
37
|
? (date) => {
|
|
29
38
|
var _a;
|
|
30
|
-
return (0, is_in_unavailable_dates_1.isInUnavailableDates)(
|
|
39
|
+
return ((0, is_in_unavailable_dates_1.isInUnavailableDates)(allDisabledDates, date) ||
|
|
40
|
+
((_a = isDateBlockedProps === null || isDateBlockedProps === void 0 ? void 0 : isDateBlockedProps((0, date_string_1.fromDateString)(date))) !== null && _a !== void 0 ? _a : false));
|
|
31
41
|
}
|
|
32
42
|
: null;
|
|
33
43
|
const onDateFocus = (0, react_1.useCallback)((date) => {
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
const dateStr = (0, date_string_1.toDateString)(date);
|
|
45
|
+
setFocusedDate(dateStr);
|
|
46
|
+
const focusedYYYYMM = (0, date_string_1.toDateString)(date).slice(0, 7);
|
|
47
|
+
if (!focusedDate ||
|
|
48
|
+
!activeMonths.some((m) => `${m.year}-${String(m.month + 1).padStart(2, "0")}` === focusedYYYYMM)) {
|
|
36
49
|
setActiveMonths((0, get_initial_months_1.getInitialMonths)(numberOfMonths, date));
|
|
37
50
|
}
|
|
38
51
|
}, [activeMonths, focusedDate, numberOfMonths]);
|
|
39
|
-
const _isDateSelected = (date) =>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
const _isDateSelected = (date) => {
|
|
53
|
+
const dateStr = (0, date_string_1.toDateString)(date);
|
|
54
|
+
return ((0, is_date_inside_range_1.isDateInsideRange)(dateStr, normalizedStartDate, normalizedEndDate) ||
|
|
55
|
+
(0, is_first_or_last_selected_date_1.isFirstOrLastSelectedDate)(dateStr, normalizedStartDate, normalizedEndDate));
|
|
56
|
+
};
|
|
57
|
+
const _isStartDate = (date) => (0, is_start_date_1.isStartDate)((0, date_string_1.toDateString)(date), normalizedStartDate);
|
|
58
|
+
const _isEndDate = (date) => (0, is_end_date_1.isEndDate)((0, date_string_1.toDateString)(date), normalizedEndDate);
|
|
59
|
+
const _isDateInsideRange = (date) => (0, is_date_inside_range_1.isDateInsideRange)((0, date_string_1.toDateString)(date), normalizedStartDate, normalizedEndDate);
|
|
43
60
|
const _isDateBlocked = (date) => (0, is_date_blocked_1.isDateBlocked)({
|
|
44
|
-
date,
|
|
45
|
-
minBookingDate,
|
|
46
|
-
maxBookingDate,
|
|
47
|
-
startDate,
|
|
48
|
-
endDate,
|
|
61
|
+
date: (0, date_string_1.toDateString)(date),
|
|
62
|
+
minBookingDate: normalizedMinBookingDate,
|
|
63
|
+
maxBookingDate: normalizedMaxBookingDate,
|
|
64
|
+
startDate: normalizedStartDate,
|
|
65
|
+
endDate: normalizedEndDate,
|
|
49
66
|
minBookingDays,
|
|
50
|
-
isDateBlockedFn:
|
|
67
|
+
isDateBlockedFn: isDateBlockedProps,
|
|
68
|
+
unavailableDates: allDisabledDates,
|
|
51
69
|
});
|
|
52
|
-
const isDateFocused = (date) =>
|
|
70
|
+
const isDateFocused = (date) => focusedDate !== null && (0, date_string_1.toDateString)(date) === focusedDate;
|
|
53
71
|
const _isDateHovered = (date) => (0, is_date_hovered_1.isDateHovered)({
|
|
54
|
-
date,
|
|
72
|
+
date: (0, date_string_1.toDateString)(date),
|
|
55
73
|
hoveredDate,
|
|
56
|
-
startDate,
|
|
57
|
-
endDate,
|
|
74
|
+
startDate: normalizedStartDate,
|
|
75
|
+
endDate: normalizedEndDate,
|
|
58
76
|
minBookingDays,
|
|
59
77
|
exactMinBookingDays,
|
|
60
78
|
isDateBlocked: disabledDatesByUser,
|
|
@@ -83,41 +101,45 @@ function useDateRangePicker({ changeActiveMonthOnSelect = false, datesConfig = [
|
|
|
83
101
|
}, [onDatesChange]);
|
|
84
102
|
// eslint-disable-next-line complexity
|
|
85
103
|
function onDateSelect(date) {
|
|
104
|
+
const dateStr = (0, date_string_1.toDateString)(date);
|
|
86
105
|
if ((focusedInput === exports.END_DATE || focusedInput === exports.START_DATE) &&
|
|
87
106
|
minBookingDays > 0 &&
|
|
88
107
|
exactMinBookingDays &&
|
|
89
108
|
(0, can_select_range_1.canSelectRange)({
|
|
90
109
|
minBookingDays,
|
|
91
110
|
exactMinBookingDays,
|
|
92
|
-
minBookingDate,
|
|
93
|
-
maxBookingDate,
|
|
111
|
+
minBookingDate: normalizedMinBookingDate,
|
|
112
|
+
maxBookingDate: normalizedMaxBookingDate,
|
|
94
113
|
isDateBlocked: disabledDatesByUser,
|
|
95
|
-
startDate:
|
|
114
|
+
startDate: dateStr,
|
|
96
115
|
endDate: null,
|
|
97
116
|
})) {
|
|
98
117
|
onDatesChange({
|
|
99
|
-
startDate:
|
|
100
|
-
endDate: (0,
|
|
101
|
-
.add(minBookingDays - 1, "day")
|
|
102
|
-
.toDate(),
|
|
118
|
+
startDate: (0, date_string_1.fromDateString)(dateStr),
|
|
119
|
+
endDate: (0, date_string_1.fromDateString)((0, date_string_1.addDays)(dateStr, minBookingDays - 1)),
|
|
103
120
|
focusedInput: null,
|
|
104
121
|
});
|
|
105
122
|
}
|
|
106
|
-
else if ((((focusedInput === exports.END_DATE &&
|
|
107
|
-
(focusedInput === exports.START_DATE &&
|
|
123
|
+
else if ((((focusedInput === exports.END_DATE && normalizedStartDate && dateStr < normalizedStartDate) ||
|
|
124
|
+
(focusedInput === exports.START_DATE && normalizedEndDate && dateStr > normalizedEndDate)) &&
|
|
108
125
|
!exactMinBookingDays &&
|
|
109
126
|
(0, can_select_range_1.canSelectRange)({
|
|
110
127
|
minBookingDays,
|
|
111
128
|
isDateBlocked: disabledDatesByUser,
|
|
112
|
-
startDate:
|
|
129
|
+
startDate: dateStr,
|
|
113
130
|
endDate: null,
|
|
114
131
|
})) ||
|
|
115
132
|
(focusedInput === exports.START_DATE &&
|
|
116
133
|
!exactMinBookingDays &&
|
|
117
|
-
(0, can_select_range_1.canSelectRange)({
|
|
134
|
+
(0, can_select_range_1.canSelectRange)({
|
|
135
|
+
minBookingDays,
|
|
136
|
+
isDateBlocked: disabledDatesByUser,
|
|
137
|
+
endDate: normalizedEndDate,
|
|
138
|
+
startDate: dateStr,
|
|
139
|
+
}))) {
|
|
118
140
|
onDatesChange({
|
|
119
141
|
endDate: null,
|
|
120
|
-
startDate:
|
|
142
|
+
startDate: (0, date_string_1.fromDateString)(dateStr),
|
|
121
143
|
focusedInput: exports.END_DATE,
|
|
122
144
|
});
|
|
123
145
|
}
|
|
@@ -127,51 +149,58 @@ function useDateRangePicker({ changeActiveMonthOnSelect = false, datesConfig = [
|
|
|
127
149
|
minBookingDays,
|
|
128
150
|
isDateBlocked: disabledDatesByUser,
|
|
129
151
|
endDate: null,
|
|
130
|
-
startDate:
|
|
152
|
+
startDate: dateStr,
|
|
131
153
|
})) {
|
|
132
154
|
onDatesChange({
|
|
133
155
|
endDate: null,
|
|
134
|
-
startDate:
|
|
156
|
+
startDate: (0, date_string_1.fromDateString)(dateStr),
|
|
135
157
|
focusedInput: exports.END_DATE,
|
|
136
158
|
});
|
|
137
159
|
}
|
|
138
160
|
else if (focusedInput === exports.END_DATE &&
|
|
139
|
-
|
|
140
|
-
|
|
161
|
+
normalizedStartDate &&
|
|
162
|
+
dateStr >= normalizedStartDate &&
|
|
141
163
|
!exactMinBookingDays &&
|
|
142
|
-
(0, can_select_range_1.canSelectRange)({
|
|
164
|
+
(0, can_select_range_1.canSelectRange)({
|
|
165
|
+
minBookingDays,
|
|
166
|
+
isDateBlocked: disabledDatesByUser,
|
|
167
|
+
startDate: normalizedStartDate,
|
|
168
|
+
endDate: dateStr,
|
|
169
|
+
})) {
|
|
143
170
|
onDatesChange({
|
|
144
|
-
startDate,
|
|
145
|
-
endDate:
|
|
171
|
+
startDate: (0, date_string_1.fromDateString)(normalizedStartDate),
|
|
172
|
+
endDate: (0, date_string_1.fromDateString)(dateStr),
|
|
146
173
|
focusedInput: null,
|
|
147
174
|
});
|
|
148
175
|
}
|
|
149
176
|
if (focusedInput !== exports.END_DATE &&
|
|
150
|
-
(!focusedDate ||
|
|
177
|
+
(!focusedDate || dateStr.slice(0, 7) !== focusedDate.slice(0, 7)) &&
|
|
151
178
|
changeActiveMonthOnSelect) {
|
|
152
|
-
setActiveMonths((0, get_initial_months_1.getInitialMonths)(numberOfMonths,
|
|
179
|
+
setActiveMonths((0, get_initial_months_1.getInitialMonths)(numberOfMonths, (0, date_string_1.fromDateString)(dateStr)));
|
|
153
180
|
}
|
|
154
181
|
}
|
|
155
182
|
function onDatesSelect(start, end) {
|
|
183
|
+
const startStr = (0, date_string_1.toDateString)(start);
|
|
184
|
+
const endStr = end ? (0, date_string_1.toDateString)(end) : null;
|
|
156
185
|
if ((0, can_select_range_1.canSelectRange)({
|
|
157
186
|
minBookingDays,
|
|
158
187
|
exactMinBookingDays,
|
|
159
|
-
minBookingDate,
|
|
160
|
-
maxBookingDate,
|
|
188
|
+
minBookingDate: normalizedMinBookingDate,
|
|
189
|
+
maxBookingDate: normalizedMaxBookingDate,
|
|
161
190
|
isDateBlocked: disabledDatesByUser,
|
|
162
|
-
startDate:
|
|
163
|
-
endDate:
|
|
191
|
+
startDate: startStr,
|
|
192
|
+
endDate: endStr,
|
|
164
193
|
})) {
|
|
165
194
|
onDatesChange({
|
|
166
|
-
endDate:
|
|
167
|
-
startDate:
|
|
195
|
+
endDate: endStr ? (0, date_string_1.fromDateString)(endStr) : null,
|
|
196
|
+
startDate: (0, date_string_1.fromDateString)(startStr),
|
|
168
197
|
focusedInput: exports.START_DATE,
|
|
169
198
|
});
|
|
170
199
|
}
|
|
171
200
|
if (focusedInput !== exports.END_DATE &&
|
|
172
|
-
(!focusedDate ||
|
|
201
|
+
(!focusedDate || startStr.slice(0, 7) !== focusedDate.slice(0, 7)) &&
|
|
173
202
|
changeActiveMonthOnSelect) {
|
|
174
|
-
setActiveMonths((0, get_initial_months_1.getInitialMonths)(numberOfMonths,
|
|
203
|
+
setActiveMonths((0, get_initial_months_1.getInitialMonths)(numberOfMonths, (0, date_string_1.fromDateString)(startStr)));
|
|
175
204
|
}
|
|
176
205
|
}
|
|
177
206
|
// eslint-disable-next-line complexity
|
|
@@ -180,20 +209,20 @@ function useDateRangePicker({ changeActiveMonthOnSelect = false, datesConfig = [
|
|
|
180
209
|
setHoveredDate(null);
|
|
181
210
|
}
|
|
182
211
|
else {
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
|
|
212
|
+
const dateStr = (0, date_string_1.toDateString)(date);
|
|
213
|
+
const isNotBlocked = !_isDateBlocked(date) || (normalizedStartDate && dateStr === normalizedStartDate);
|
|
214
|
+
const isHoveredDateAfterOrEqualMinDate = normalizedMinBookingDate
|
|
215
|
+
? dateStr >= (0, date_string_1.addDays)(normalizedMinBookingDate, -1)
|
|
186
216
|
: true;
|
|
187
|
-
const isHoveredDateBeforeOrEqualMaxDate =
|
|
188
|
-
|
|
189
|
-
const potentialEndDate = (0, dayjs_utils_1.dayjsEnRaw)(date)
|
|
190
|
-
.add(minBookingDays - 1, "day")
|
|
191
|
-
.toDate();
|
|
192
|
-
const isPotentialEndDateAfterOrEqualMinDate = minBookingDate
|
|
193
|
-
? !(0, dayjs_utils_1.dayjsEnRaw)(potentialEndDate).isBefore(minBookingDate)
|
|
217
|
+
const isHoveredDateBeforeOrEqualMaxDate = normalizedMaxBookingDate
|
|
218
|
+
? dateStr <= normalizedMaxBookingDate
|
|
194
219
|
: true;
|
|
195
|
-
const
|
|
196
|
-
|
|
220
|
+
const potentialEndDateStr = (0, date_string_1.addDays)(dateStr, minBookingDays - 1);
|
|
221
|
+
const isPotentialEndDateAfterOrEqualMinDate = normalizedMinBookingDate
|
|
222
|
+
? potentialEndDateStr >= normalizedMinBookingDate
|
|
223
|
+
: true;
|
|
224
|
+
const isPotentialEndDateBeforeOrEqualMaxDate = normalizedMaxBookingDate
|
|
225
|
+
? potentialEndDateStr <= normalizedMaxBookingDate
|
|
197
226
|
: true;
|
|
198
227
|
const isExactAndInRange = exactMinBookingDays &&
|
|
199
228
|
minBookingDays > 1 &&
|
|
@@ -201,19 +230,17 @@ function useDateRangePicker({ changeActiveMonthOnSelect = false, datesConfig = [
|
|
|
201
230
|
isHoveredDateBeforeOrEqualMaxDate &&
|
|
202
231
|
isPotentialEndDateAfterOrEqualMinDate &&
|
|
203
232
|
isPotentialEndDateBeforeOrEqualMaxDate;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
!endDate &&
|
|
233
|
+
const isInRange = normalizedStartDate &&
|
|
234
|
+
!normalizedEndDate &&
|
|
207
235
|
!exactMinBookingDays &&
|
|
208
236
|
isHoveredDateAfterOrEqualMinDate &&
|
|
209
237
|
isHoveredDateBeforeOrEqualMaxDate;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
? (0, dayjs_utils_1.dayjsEnRaw)(date).isBetween(startDate, (0, dayjs_utils_1.dayjsEnRaw)(startDate).add(minBookingDays - 2, "day"))
|
|
238
|
+
const isMinBookingDaysInRange = minBookingDays > 1 && normalizedStartDate
|
|
239
|
+
? dateStr > normalizedStartDate && dateStr < (0, date_string_1.addDays)(normalizedStartDate, minBookingDays - 2)
|
|
213
240
|
: true;
|
|
214
|
-
const isStartDateHoveredAndInRange =
|
|
241
|
+
const isStartDateHoveredAndInRange = normalizedStartDate && dateStr === normalizedStartDate && isMinBookingDaysInRange;
|
|
215
242
|
if (isNotBlocked && (isExactAndInRange || isInRange || isStartDateHoveredAndInRange)) {
|
|
216
|
-
setHoveredDate(
|
|
243
|
+
setHoveredDate(dateStr);
|
|
217
244
|
}
|
|
218
245
|
else if (hoveredDate !== null) {
|
|
219
246
|
setHoveredDate(null);
|
|
@@ -223,17 +250,17 @@ function useDateRangePicker({ changeActiveMonthOnSelect = false, datesConfig = [
|
|
|
223
250
|
return {
|
|
224
251
|
...(0, use_date_picker_navigation_1.useDatePickerNavigation)({
|
|
225
252
|
activeMonths,
|
|
226
|
-
maxBookingDate,
|
|
227
|
-
minBookingDate,
|
|
253
|
+
maxBookingDate: normalizedMaxBookingDate,
|
|
254
|
+
minBookingDate: normalizedMinBookingDate,
|
|
228
255
|
numberOfMonths,
|
|
229
256
|
setActiveMonths,
|
|
230
|
-
setFocusedDate,
|
|
257
|
+
setFocusedDate: (d) => setFocusedDate(d ? (0, date_string_1.toDateString)(d) : null),
|
|
231
258
|
}),
|
|
232
259
|
activeMonths,
|
|
233
260
|
datesConfig,
|
|
234
261
|
firstDayOfWeek,
|
|
235
|
-
focusedDate,
|
|
236
|
-
hoveredDate,
|
|
262
|
+
focusedDate: focusedDate ? (0, date_string_1.fromDateString)(focusedDate) : null,
|
|
263
|
+
hoveredDate: hoveredDate ? (0, date_string_1.fromDateString)(hoveredDate) : null,
|
|
237
264
|
isDateBlocked: _isDateBlocked,
|
|
238
265
|
isDateFocused,
|
|
239
266
|
isDateHovered: _isDateHovered,
|
|
@@ -247,6 +274,6 @@ function useDateRangePicker({ changeActiveMonthOnSelect = false, datesConfig = [
|
|
|
247
274
|
onDateSelect,
|
|
248
275
|
onDatesSelect,
|
|
249
276
|
onResetDates,
|
|
250
|
-
unavailableDates:
|
|
277
|
+
unavailableDates: allDisabledDates.map(date_string_1.fromDateString),
|
|
251
278
|
};
|
|
252
279
|
}
|