diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index cbb3c60a62..1e6c1a969a 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -7,8 +7,7 @@ assignees: "gpbl"
## Description
Please provide a detailed description of the issue. Include any relevant context or steps to reproduce the problem.
-
-Fork this CodeSandbox: https://codesandbox.io/p/sandbox/react-day-picker-v8-eg8mw and add to it the code to reproduce the issue.
+ssue.
## Expected Behavior
@@ -41,3 +40,7 @@ If applicable, add screenshots or GIFs to help explain your problem.
- Version [e.g. 22]:
- Operating System [e.g. iOS, Windows]:
- Other relevant information:
+
+## Checklist
+
+- [ ] I included a [CodeSandbox](https://codesandbox.io/p/sandbox/react-day-picker-v8-eg8mw) link that helps maintainer to replicate the bug
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000000..c04a04909f
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,38 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "type": "typescript",
+ "tsconfig": "tsconfig-esm.json",
+ "option": "watch",
+ "problemMatcher": ["$tsc-watch"],
+ "group": "build",
+ "label": "tsc: watch - tsconfig-esm.json",
+ "runOptions": {
+ "runOn": "folderOpen"
+ }
+ },
+ {
+ "type": "typescript",
+ "tsconfig": "tsconfig-cjs.json",
+ "option": "watch",
+ "problemMatcher": ["$tsc-watch"],
+ "group": "build",
+ "label": "tsc: watch - tsconfig-cjs.json",
+ "runOptions": {
+ "runOn": "folderOpen"
+ }
+ },
+ {
+ "type": "npm",
+ "script": "typecheck-watch",
+ "group": "build",
+ "problemMatcher": [],
+ "label": "npm: typecheck-watch",
+ "detail": "tsc --project ./tsconfig.json --noEmit --watch",
+ "runOptions": {
+ "runOn": "folderOpen"
+ }
+ }
+ ]
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d6f01ea09..158e61b3da 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,7 +21,6 @@ This release includes important updates related to accessibility, styles and per
#### At a glance
- Enhanced accessibility to better comply with [WCAG21](https://www.w3.org/TR/WCAG21/) recommendations.
-- New HTML output using `div` grids instead of tables.
- Simplified styles and selectors with new CSS variables.
- Added support for UTC dates.
- Improved selection logic for range mode.
diff --git a/README.md b/README.md
index 29867b45c1..20b6f36b1d 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@ import { DayPicker } from "react-day-picker";
import "react-day-picker/dist/style.css";
function MyDatePicker() {
- const [selected, setSelected] = useState();
+ const [selected, setSelected] = useState();
return ;
}
```
diff --git a/examples/AccessibleDatePicker.tsx b/examples/AccessibleDatePicker.tsx
index 1f911972ca..f2c282cd2f 100644
--- a/examples/AccessibleDatePicker.tsx
+++ b/examples/AccessibleDatePicker.tsx
@@ -6,28 +6,22 @@ import { DayPicker } from "react-day-picker";
export function AccessibleDatePicker() {
const [meetingDate, setMeetingDate] = useState(undefined);
return (
-
-
Meeting Date
-
"Select a date for the meeting",
- labelDay: (date, modifiers) => {
- return modifiers.selected
- ? `Selected Meeting Date: ${format(date, "PPP")}`
- : "";
- }
- }}
- footer={
-
- {meetingDate
- ? `Meeting date is set to ${format(meetingDate, "PPPP")}`
- : "Please pick a date for the meeting."}
-
+ {
+ return modifiers.selected
+ ? `Selected Meeting Date: ${format(date, "PPP")}`
+ : "";
}
- />
-
+ }}
+ footer={
+ meetingDate
+ ? `Meeting date is set to ${format(meetingDate, "PPPP")}`
+ : "Please pick a date for the meeting."
+ }
+ />
);
}
diff --git a/examples/AutoFocus.tsx b/examples/AutoFocus.tsx
new file mode 100644
index 0000000000..82aad429ea
--- /dev/null
+++ b/examples/AutoFocus.tsx
@@ -0,0 +1,12 @@
+import React from "react";
+
+import { DayPicker } from "react-day-picker";
+
+/** Test for the next focus day to not cause an infinite recursion. */
+export function AutoFocus() {
+ return (
+
+
+
+ );
+}
diff --git a/examples/ContainerAttributes.tsx b/examples/ContainerAttributes.tsx
index 2e844d720b..02f69afa91 100644
--- a/examples/ContainerAttributes.tsx
+++ b/examples/ContainerAttributes.tsx
@@ -11,7 +11,6 @@ export function ContainerAttributes() {
nonce="foo_nonce"
title="foo_title"
lang="it"
- mode="multiple"
/>
);
}
diff --git a/examples/Controlled.tsx b/examples/Controlled.tsx
index fb64c63365..b814373024 100644
--- a/examples/Controlled.tsx
+++ b/examples/Controlled.tsx
@@ -8,9 +8,11 @@ export function Controlled() {
const nextMonth = addMonths(new Date(), 1);
const [month, setMonth] = React.useState(nextMonth);
- const footer = (
+ return (
+
setMonth(today)}
>
@@ -18,6 +20,4 @@ export function Controlled() {
);
-
- return ;
}
diff --git a/examples/CustomCaption.tsx b/examples/CustomCaption.tsx
index 103e872665..c10813a2b2 100644
--- a/examples/CustomCaption.tsx
+++ b/examples/CustomCaption.tsx
@@ -4,33 +4,38 @@ import { format } from "date-fns";
import {
type MonthCaptionProps,
DayPicker,
- useCalendar
+ useDayPicker
} from "react-day-picker";
function CustomMonthCaption(props: MonthCaptionProps) {
- const { goToMonth, nextMonth, previousMonth } = useCalendar();
+ const { goToMonth, nextMonth, previousMonth } = useDayPicker();
return (
-
- {format(props.month.date, "MMM yyy")}
- previousMonth && goToMonth(previousMonth)}
- >
- Previous
-
- nextMonth && goToMonth(nextMonth)}
- >
- Next
-
-
+ <>
+ {format(props.calendarMonth.date, "MMM yyy")}
+
+ previousMonth && goToMonth(previousMonth)}
+ >
+ Previous
+
+ nextMonth && goToMonth(nextMonth)}
+ >
+ Next
+
+
+ >
);
}
export function CustomCaption() {
return (
+ {props.day.date.getDate() === 19 ? `🎉` : props.children}
+
+ );
+}
+
+export function CustomDayButton() {
+ return ;
+}
diff --git a/examples/CustomDayDate.test.tsx b/examples/CustomDayDate.test.tsx
index 39c9b28c5b..a9d0eaca1b 100644
--- a/examples/CustomDayDate.test.tsx
+++ b/examples/CustomDayDate.test.tsx
@@ -2,13 +2,13 @@ import React from "react";
import { render, screen } from "@/test/render";
-import { CustomDayDate } from "./CustomDayDate";
+import { CustomDayButton } from "./CustomDayButton";
beforeAll(() => jest.setSystemTime(new Date(2021, 10, 25)));
afterAll(() => jest.useRealTimers());
beforeEach(() => {
- render( );
+ render( );
});
test("should render the emoji", () => {
diff --git a/examples/CustomDayDate.tsx b/examples/CustomDayDate.tsx
deleted file mode 100644
index 9c0b830c97..0000000000
--- a/examples/CustomDayDate.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from "react";
-
-import { DayPicker, type DayDateProps } from "react-day-picker";
-
-function HighlightDay(props: DayDateProps) {
- return (
-
- {props.day.date.getDate() === 19 ? `🎉` : props.formattedDate}
-
- );
-}
-
-export function CustomDayDate() {
- return ;
-}
diff --git a/examples/CustomMultiple.test.tsx b/examples/CustomMultiple.test.tsx
index 368af9bde8..e2be39024d 100644
--- a/examples/CustomMultiple.test.tsx
+++ b/examples/CustomMultiple.test.tsx
@@ -1,6 +1,6 @@
import React from "react";
-import { gridcell } from "@/test/elements";
+import { dateButton, gridcell } from "@/test/elements";
import { render, screen } from "@/test/render";
import { user } from "@/test/user";
@@ -18,10 +18,10 @@ beforeEach(() => {
describe("when a day is clicked", () => {
const day1 = new Date(2021, 10, 1);
beforeEach(async () => {
- await user.click(gridcell(day1));
+ await user.click(dateButton(day1));
});
test("should appear as selected", () => {
- expect(gridcell(day1)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day1, true)).toHaveAttribute("aria-selected", "true");
});
test("should update the footer", () => {
expect(screen.getByText("You selected 1 days.")).toBeInTheDocument();
@@ -29,13 +29,13 @@ describe("when a day is clicked", () => {
describe("when a second day is clicked", () => {
const day2 = new Date(2021, 10, 2);
beforeEach(async () => {
- await user.click(gridcell(day2));
+ await user.click(dateButton(day2));
});
test("the first day should appear as selected", () => {
- expect(gridcell(day1)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day1, true)).toHaveAttribute("aria-selected", "true");
});
test("the second day should appear as selected", () => {
- expect(gridcell(day2)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day2, true)).toHaveAttribute("aria-selected", "true");
});
test("should update the footer", () => {
expect(screen.getByText("You selected 2 days.")).toBeInTheDocument();
diff --git a/examples/CustomSingle.test.tsx b/examples/CustomSingle.test.tsx
index 07a21d23dd..dda3704ca9 100644
--- a/examples/CustomSingle.test.tsx
+++ b/examples/CustomSingle.test.tsx
@@ -1,15 +1,12 @@
import React from "react";
-import { gridcell } from "@/test/elements";
+import { dateButton, gridcell } from "@/test/elements";
import { render, screen } from "@/test/render";
import { user } from "@/test/user";
import { CustomSingle } from "./CustomSingle";
-const today = new Date(2021, 10, 25);
-
-beforeAll(() => jest.setSystemTime(today));
-afterAll(() => jest.useRealTimers());
+const today = new Date();
beforeEach(() => {
render( );
@@ -17,22 +14,25 @@ beforeEach(() => {
describe("when a day is clicked", () => {
beforeEach(async () => {
- await user.click(gridcell(today));
+ await user.click(dateButton(today));
});
- test("should appear as selected", () => {
- expect(gridcell(today)).toHaveAttribute("aria-selected", "true");
+ test("the gridcell should appear as selected", () => {
+ expect(gridcell(today, true)).toHaveAttribute("aria-selected", "true");
});
test("should update the footer", () => {
expect(
- screen.getByText("You selected Thu Nov 25 2021")
+ screen.getByText("You selected " + today.toDateString())
).toBeInTheDocument();
});
describe("when clicking the day again", () => {
beforeEach(async () => {
- await user.click(gridcell(today));
+ await user.click(dateButton(today));
});
test("should not appear as selected", () => {
- expect(gridcell(today)).not.toHaveAttribute("aria-selected", "true");
+ expect(gridcell(today, true)).not.toHaveAttribute(
+ "aria-selected",
+ "true"
+ );
});
test("should update the footer", () => {
expect(
diff --git a/examples/CustomWeek.test.tsx b/examples/CustomWeek.test.tsx
index 38b688f07b..2afab45f23 100644
--- a/examples/CustomWeek.test.tsx
+++ b/examples/CustomWeek.test.tsx
@@ -1,6 +1,6 @@
import React from "react";
-import { gridcell } from "@/test/elements";
+import { dateButton, gridcell } from "@/test/elements";
import { render } from "@/test/render";
import { user } from "@/test/user";
@@ -17,17 +17,17 @@ beforeEach(() => {
describe("when a day is clicked", () => {
beforeEach(async () => {
- await user.click(gridcell(today));
+ await user.click(dateButton(today));
});
test("the whole week should appear selected", () => {
const week = [
- gridcell(new Date(2021, 10, 21)),
- gridcell(new Date(2021, 10, 22)),
- gridcell(new Date(2021, 10, 23)),
- gridcell(new Date(2021, 10, 24)),
- gridcell(new Date(2021, 10, 25)),
- gridcell(new Date(2021, 10, 26)),
- gridcell(new Date(2021, 10, 27))
+ gridcell(new Date(2021, 10, 21), true),
+ gridcell(new Date(2021, 10, 22), true),
+ gridcell(new Date(2021, 10, 23), true),
+ gridcell(new Date(2021, 10, 24), true),
+ gridcell(new Date(2021, 10, 25), true),
+ gridcell(new Date(2021, 10, 26), true),
+ gridcell(new Date(2021, 10, 27), true)
];
week.forEach((day) => {
expect(day).toHaveAttribute("aria-selected", "true");
@@ -35,17 +35,17 @@ describe("when a day is clicked", () => {
});
describe("when clicking the day again", () => {
beforeEach(async () => {
- await user.click(gridcell(today));
+ await user.click(dateButton(today));
});
test("the whole week should not be selected", () => {
const week = [
- gridcell(new Date(2021, 10, 21)),
- gridcell(new Date(2021, 10, 22)),
- gridcell(new Date(2021, 10, 23)),
- gridcell(new Date(2021, 10, 24)),
- gridcell(new Date(2021, 10, 25)),
- gridcell(new Date(2021, 10, 26)),
- gridcell(new Date(2021, 10, 27))
+ dateButton(new Date(2021, 10, 21)),
+ dateButton(new Date(2021, 10, 22)),
+ dateButton(new Date(2021, 10, 23)),
+ dateButton(new Date(2021, 10, 24)),
+ dateButton(new Date(2021, 10, 25)),
+ dateButton(new Date(2021, 10, 26)),
+ dateButton(new Date(2021, 10, 27))
];
week.forEach((day) => {
expect(day).not.toHaveAttribute("aria-selected", "true");
diff --git a/examples/CustomWeek.tsx b/examples/CustomWeek.tsx
index df56e80058..f49cbff2f7 100644
--- a/examples/CustomWeek.tsx
+++ b/examples/CustomWeek.tsx
@@ -1,6 +1,6 @@
import React, { useState } from "react";
-import { endOfWeek, isSameWeek, startOfWeek } from "date-fns";
+import { endOfWeek, startOfWeek } from "date-fns";
import { DateRange, DayPicker } from "react-day-picker";
/** Select the whole week when the day is clicked. */
@@ -23,23 +23,10 @@ export function CustomWeek() {
to: endOfWeek(day)
});
}}
- onWeekNumberClick={(weekNumber, dates) => {
- if (selectedWeek?.from && isSameWeek(dates[0], selectedWeek.from)) {
- setSelectedWeek(undefined); // clear the selection if the week is already selected
- return;
- }
- setSelectedWeek({
- from: startOfWeek(dates[0]),
- to: endOfWeek(dates[dates.length - 1])
- });
- }}
footer={
- selectedWeek && (
-
- Week from {selectedWeek?.from?.toLocaleDateString()} to
- {selectedWeek?.to?.toLocaleDateString()}
-
- )
+ selectedWeek &&
+ `Week from ${selectedWeek?.from?.toLocaleDateString()} to
+ {selectedWeek?.to?.toLocaleDateString()}`
}
/>
);
diff --git a/examples/Dialog.tsx b/examples/Dialog.tsx
index a535e965ec..ee7213e6c5 100644
--- a/examples/Dialog.tsx
+++ b/examples/Dialog.tsx
@@ -23,22 +23,16 @@ export function Dialog() {
// Function to toggle the dialog visibility
const toggleDialog = () => setIsDialogOpen(!isDialogOpen);
- // Hook to handle the body scroll behavior and focus trapping.
+ // Hook to handle the body scroll behavior and focus trapping. You may want to
+ // use your own trapping library as the body.style overflow will break the
+ // scroll position.
useEffect(() => {
- const handleBodyScroll = (isOpen: boolean) => {
- document.body.style.overflow = isOpen ? "hidden" : "";
- };
if (!dialogRef.current) return;
if (isDialogOpen) {
- handleBodyScroll(true);
dialogRef.current.showModal();
} else {
- handleBodyScroll(false);
dialogRef.current.close();
}
- return () => {
- handleBodyScroll(false);
- };
}, [isDialogOpen]);
/**
@@ -108,20 +102,21 @@ export function Dialog() {
aria-labelledby={headerId}
onClose={() => setIsDialogOpen(false)}
>
-
- {selectedDate !== undefined && (
+ {isDialogOpen && (
+ Selected: {selectedDate.toDateString()}>
- )}
-
- }
- />
+ )
+ }
+ />
+ )}
);
diff --git a/examples/Disabled.test.tsx b/examples/Disabled.test.tsx
index 27bdffb01e..126ce5695b 100644
--- a/examples/Disabled.test.tsx
+++ b/examples/Disabled.test.tsx
@@ -1,13 +1,12 @@
import React from "react";
-import { app, grid, gridcell } from "@/test/elements";
-import { act, render, screen } from "@/test/render";
+import { app, grid } from "@/test/elements";
+import { render, screen } from "@/test/render";
import { user } from "@/test/user";
import { Disabled } from "./Disabled";
const today = new Date(2022, 5, 10);
-const firstOfMonth = new Date(2022, 5, 1);
beforeAll(() => jest.setSystemTime(today));
afterAll(() => jest.useRealTimers());
@@ -18,7 +17,7 @@ beforeEach(() => {
);
- return act(() => gridcell(firstOfMonth).focus());
+ // return act(() => dateButton(firstOfMonth).focus());
});
test("should not display the previous button", () => {
diff --git a/examples/FocusRecursive.test.tsx b/examples/FocusRecursive.test.tsx
index 5f34413fa0..d1c3dabddb 100644
--- a/examples/FocusRecursive.test.tsx
+++ b/examples/FocusRecursive.test.tsx
@@ -1,6 +1,6 @@
import React from "react";
-import { activeElement, gridcell } from "@/test/elements";
+import { activeElement, dateButton } from "@/test/elements";
import { render } from "@/test/render";
import { user } from "@/test/user";
@@ -20,5 +20,5 @@ test("the first selected day should have focus", async () => {
await user.type(activeElement(), "{arrowdown}");
await user.type(activeElement(), "{arrowdown}");
await user.type(activeElement(), "{arrowdown}");
- expect(gridcell(new Date(2022, 5, 22))).toHaveFocus();
+ expect(dateButton(new Date(2022, 5, 22))).toHaveFocus();
});
diff --git a/examples/Footer.tsx b/examples/Footer.tsx
index ae9270d037..99a45629f7 100644
--- a/examples/Footer.tsx
+++ b/examples/Footer.tsx
@@ -10,11 +10,9 @@ export function Footer() {
selected={selected}
onSelect={setSelected}
footer={
- selected ? (
- You picked {selected.toLocaleDateString()}.
- ) : (
- Please pick a date.
- )
+ selected
+ ? `You picked ${selected.toLocaleDateString()}.`
+ : "Please pick a date."
}
/>
);
diff --git a/examples/Formatters.tsx b/examples/Formatters.tsx
index eb983b56d6..6e82605785 100644
--- a/examples/Formatters.tsx
+++ b/examples/Formatters.tsx
@@ -1,7 +1,7 @@
import React from "react";
-import { FormatOptions, format } from "date-fns";
-import { DayPicker } from "react-day-picker";
+import { format } from "date-fns";
+import { DayPicker, FormatOptions } from "react-day-picker";
const seasonEmoji: Record = {
winter: "⛄️",
diff --git a/examples/Input.test.tsx b/examples/Input.test.tsx
index a0f638198c..99e40077ce 100644
--- a/examples/Input.test.tsx
+++ b/examples/Input.test.tsx
@@ -29,7 +29,6 @@ test("updates the calendar when a date is typed in", async () => {
render( );
const testDate = new Date(2022, 11, 31); // Dec 31, 2022
await user.type(textbox(), format(testDate, "MM/dd/yyyy"));
-
expect(
screen.getByText(`Selected: ${testDate.toDateString()}`)
).toBeInTheDocument();
diff --git a/examples/Input.tsx b/examples/Input.tsx
index 76257b954c..cc8b08852e 100644
--- a/examples/Input.tsx
+++ b/examples/Input.tsx
@@ -68,11 +68,7 @@ export function Input() {
mode="single"
selected={selectedDate}
onSelect={handleDayPickerSelect}
- footer={
-
- Selected: {selectedDate?.toDateString()}
-
- }
+ footer={`Selected: ${selectedDate?.toDateString()}`}
/>
diff --git a/examples/InputRange.tsx b/examples/InputRange.tsx
index bbb6542063..a8e7979cb7 100644
--- a/examples/InputRange.tsx
+++ b/examples/InputRange.tsx
@@ -56,27 +56,27 @@ export function InputRange() {
};
return (
-
-
- {" – "}
-
-
- }
- />
+
+
+
+
);
}
diff --git a/examples/InputTime.tsx b/examples/InputTime.tsx
index 62eefc40c4..73aabd63d5 100644
--- a/examples/InputTime.tsx
+++ b/examples/InputTime.tsx
@@ -1,5 +1,6 @@
import React, { ChangeEventHandler, useState } from "react";
+import { setHours, setMinutes } from "date-fns";
import { DayPicker } from "react-day-picker";
export function InputTime() {
@@ -13,13 +14,7 @@ export function InputTime() {
return;
}
const [hours, minutes] = time.split(":").map((str) => parseInt(str, 10));
- const newSelectedDate = new Date(
- selected.getFullYear(),
- selected.getMonth(),
- selected.getDate(),
- hours,
- minutes
- );
+ const newSelectedDate = setHours(setMinutes(selected, minutes), hours);
setSelected(newSelectedDate);
setTimeValue(time);
};
@@ -43,27 +38,19 @@ export function InputTime() {
};
return (
- <>
+
+
-
- Pick a time:{" "}
-
-
-
- Selected date: {selected ? selected.toLocaleString() : "none"}
-
- >
- }
+ footer={`Selected date: ${selected ? selected.toLocaleString() : "none"}`}
/>
- >
+
);
}
diff --git a/examples/Keyboard.test.tsx b/examples/Keyboard.test.tsx
index 4ef643d0a1..286b98d200 100644
--- a/examples/Keyboard.test.tsx
+++ b/examples/Keyboard.test.tsx
@@ -14,11 +14,11 @@ import {
import {
grid,
nextButton,
- gridcell,
+ dateButton,
activeElement,
previousButton
} from "@/test/elements";
-import { renderApp } from "@/test/renderApp";
+import { render } from "@/test/render";
import { user } from "@/test/user";
import { Keyboard } from "./Keyboard";
@@ -30,7 +30,7 @@ afterAll(() => jest.useRealTimers());
describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
beforeEach(() => {
- renderApp( );
+ render( );
});
describe("when clicking the previous month button", () => {
beforeEach(() => user.click(previousButton()));
@@ -59,22 +59,22 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
const startOfWeekDay = startOfWeek(day);
const endOfWeekDay = endOfWeek(day);
- beforeEach(() => act(() => gridcell(day).focus()));
+ beforeEach(() => act(() => dateButton(day).focus()));
test("the day button should be focused", () => {
- expect(activeElement()).toBe(gridcell(day));
+ expect(activeElement()).toBe(dateButton(day));
});
describe("when the Arrow Left is pressed", () => {
beforeEach(() => user.type(activeElement(), "{arrowleft}"));
if (dir === "rtl") {
test("should focus the next day", () => {
- expect(gridcell(nextDay)).toHaveFocus();
+ expect(dateButton(nextDay)).toHaveFocus();
});
} else {
test("should display the previous month", () => {
expect(grid("May 2022")).toBeInTheDocument();
});
test("should focus the previous day", () => {
- expect(gridcell(prevDay)).toHaveFocus();
+ expect(dateButton(prevDay)).toHaveFocus();
});
}
});
@@ -85,11 +85,11 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
expect(grid("May 2022")).toBeInTheDocument();
});
test("should focus the previous day", () => {
- expect(gridcell(prevDay)).toHaveFocus();
+ expect(dateButton(prevDay)).toHaveFocus();
});
} else {
test("should focus the next day", () => {
- expect(gridcell(nextDay)).toHaveFocus();
+ expect(dateButton(nextDay)).toHaveFocus();
});
}
});
@@ -99,7 +99,7 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
expect(grid("May 2022")).toBeInTheDocument();
});
test("should focus the day in the previous week", () => {
- expect(gridcell(prevWeekDay)).toHaveFocus();
+ expect(dateButton(prevWeekDay)).toHaveFocus();
});
});
describe("when the Arrow Down is pressed", () => {
@@ -108,7 +108,7 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
expect(grid("June 2022")).toBeInTheDocument();
});
test("should focus the day in the next week", () => {
- expect(gridcell(nextWeekDay)).toHaveFocus();
+ expect(dateButton(nextWeekDay)).toHaveFocus();
});
});
describe("when Page Up is pressed", () => {
@@ -119,7 +119,7 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
expect(grid("May 2022")).toBeInTheDocument();
});
it("should focus the day in the previous month", () => {
- expect(gridcell(prevMonth)).toHaveFocus();
+ expect(dateButton(prevMonth)).toHaveFocus();
});
});
describe("when Page Down is pressed", () => {
@@ -128,7 +128,7 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
expect(grid("July 2022")).toBeInTheDocument();
});
it("should focus the day in the next month", () => {
- expect(gridcell(nextMonth)).toHaveFocus();
+ expect(dateButton(nextMonth)).toHaveFocus();
});
});
describe("when Shift + Page Up is pressed", () => {
@@ -137,7 +137,7 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
expect(grid("June 2021")).toBeInTheDocument();
});
it("should focus the day in the previous year", () => {
- expect(gridcell(prevYear)).toHaveFocus();
+ expect(dateButton(prevYear)).toHaveFocus();
});
});
describe("when Shift + Page Down is pressed", () => {
@@ -148,19 +148,19 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
expect(grid("June 2023")).toBeInTheDocument();
});
it("should focus the day in the next yeaer", () => {
- expect(gridcell(nextYear)).toHaveFocus();
+ expect(dateButton(nextYear)).toHaveFocus();
});
});
describe("when Home is pressed", () => {
beforeEach(() => user.type(activeElement(), "{home}"));
it("should focus the start of the week", () => {
- expect(gridcell(startOfWeekDay)).toHaveFocus();
+ expect(dateButton(startOfWeekDay)).toHaveFocus();
});
});
describe("when End is pressed", () => {
beforeEach(() => user.type(activeElement(), "{end}"));
it("should focus the end of the week", () => {
- expect(gridcell(endOfWeekDay)).toHaveFocus();
+ expect(dateButton(endOfWeekDay)).toHaveFocus();
});
});
});
@@ -171,13 +171,13 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
const prevDay = addDays(day, -1);
beforeEach(() => {
- return act(() => gridcell(day).focus());
+ return act(() => dateButton(day).focus());
});
describe("when the Arrow Right is pressed", () => {
beforeEach(() => user.type(activeElement(), "{arrowright}"));
if (dir === "rtl") {
test("should focus the previous day", () => {
- expect(gridcell(prevDay)).toHaveFocus();
+ expect(dateButton(prevDay)).toHaveFocus();
});
} else {
test("should display the next month", () => {
@@ -185,7 +185,7 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
});
test("should focus the next day", () => {
const nextDay = addDays(day, 1);
- expect(gridcell(nextDay)).toHaveFocus();
+ expect(dateButton(nextDay)).toHaveFocus();
});
}
});
@@ -196,7 +196,7 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
expect(grid("July 2022")).toBeInTheDocument();
});
test("should focus the next day", () => {
- expect(gridcell(nextDay)).toHaveFocus();
+ expect(dateButton(nextDay)).toHaveFocus();
});
} else {
test("should display the same month", () => {
@@ -204,7 +204,7 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
});
test("should focus the previous day", () => {
const prevDay = addDays(day, -1);
- expect(gridcell(prevDay)).toHaveFocus();
+ expect(dateButton(prevDay)).toHaveFocus();
});
}
});
@@ -215,7 +215,7 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
});
test("should focus the day in the previous week", () => {
const prevDay = addWeeks(day, -1);
- expect(gridcell(prevDay)).toHaveFocus();
+ expect(dateButton(prevDay)).toHaveFocus();
});
});
describe("when the Arrow Down is pressed", () => {
@@ -225,7 +225,7 @@ describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
});
test("should focus the day in the next week", () => {
const nextDay = addWeeks(day, 1);
- expect(gridcell(nextDay)).toHaveFocus();
+ expect(dateButton(nextDay)).toHaveFocus();
});
});
});
@@ -237,20 +237,20 @@ describe("when week is set to start on a Monday", () => {
const endOfWeekDay = endOfWeek(day, { weekStartsOn: 1 });
beforeEach(() => {
- renderApp( );
- act(() => gridcell(day).focus());
+ render( );
+ act(() => dateButton(day).focus());
});
describe("when Home is pressed", () => {
beforeEach(() => user.type(activeElement(), "{home}"));
it("should focus the start of the week being Monday", () => {
- expect(gridcell(startOfWeekDay)).toHaveFocus();
+ expect(dateButton(startOfWeekDay)).toHaveFocus();
});
});
describe("when End is pressed", () => {
beforeEach(() => user.type(activeElement(), "{end}"));
it("should focus the end of the week being Sunday", () => {
- expect(gridcell(endOfWeekDay)).toHaveFocus();
+ expect(dateButton(endOfWeekDay)).toHaveFocus();
});
});
});
diff --git a/examples/ModifiersCustom.test.tsx b/examples/ModifiersCustom.test.tsx
index 9dcfe5438e..f442fdfba5 100644
--- a/examples/ModifiersCustom.test.tsx
+++ b/examples/ModifiersCustom.test.tsx
@@ -12,10 +12,10 @@ beforeEach(() => {
test.each([new Date(2024, 5, 8), new Date(2024, 5, 9), new Date(2021, 5, 10)])(
"%s should have the booked style",
(day) => {
- expect(gridcell(day)).toHaveClass("booked");
+ expect(gridcell(day, true)).toHaveClass("booked");
}
);
test.each([new Date(2024, 5, 1)])("%s should have the booked style", (day) => {
- expect(gridcell(day)).not.toHaveClass("booked");
+ expect(gridcell(day, true)).not.toHaveClass("booked");
});
diff --git a/examples/ModifiersDisabled.test.tsx b/examples/ModifiersDisabled.test.tsx
index d39ac31a86..ecd5c3705c 100644
--- a/examples/ModifiersDisabled.test.tsx
+++ b/examples/ModifiersDisabled.test.tsx
@@ -1,6 +1,6 @@
import React from "react";
-import { gridcell } from "@/test/elements";
+import { dateButton } from "@/test/elements";
import { render } from "@/test/render";
import { ModifiersDisabled } from "./ModifiersDisabled";
@@ -13,5 +13,5 @@ const days = [
test.each(days)("the day %s should be disabled", (day) => {
render( );
- expect(gridcell(day)).toHaveAttribute("aria-disabled", "true");
+ expect(dateButton(day)).toBeDisabled();
});
diff --git a/examples/ModifiersStyle.test.tsx b/examples/ModifiersStyle.test.tsx
index 2579307e18..20214757b8 100644
--- a/examples/ModifiersStyle.test.tsx
+++ b/examples/ModifiersStyle.test.tsx
@@ -20,5 +20,5 @@ const style = {
color: "lightgreen"
};
test.each(days)("The day %s should have the proper inline style", (day) => {
- expect(gridcell(day)).toHaveStyle(style);
+ expect(gridcell(day, true)).toHaveStyle(style);
});
diff --git a/examples/ModifiersToday.test.tsx b/examples/ModifiersToday.test.tsx
index 253ace059b..938bcef79f 100644
--- a/examples/ModifiersToday.test.tsx
+++ b/examples/ModifiersToday.test.tsx
@@ -2,7 +2,7 @@ import React from "react";
import { addDays } from "date-fns";
-import { gridcell, app } from "@/test/elements";
+import { dateButton, app, gridcell } from "@/test/elements";
import { renderApp } from "@/test/renderApp";
import { user } from "@/test/user";
@@ -19,7 +19,7 @@ beforeEach(() => {
describe("when rendering a month that contains today", () => {
test("it should add the default class name for today", () => {
- expect(gridcell(today)).toHaveClass("rdp-today");
+ expect(gridcell(today, true)).toHaveClass("rdp-today");
});
test('it should have exactly one ".day_today" class', () => {
const todays = app().querySelectorAll(".rdp-today");
@@ -29,7 +29,7 @@ describe("when rendering a month that contains today", () => {
describe("when the today date is clicked", () => {
beforeEach(async () => {
- await user.click(gridcell(today));
+ await user.click(dateButton(today));
});
test("should update the footer", () => {
expect(app()).toHaveTextContent("You clicked the today’s date");
@@ -38,8 +38,8 @@ describe("when the today date is clicked", () => {
describe("when another date is clicked", () => {
const date = addDays(today, 1);
- beforeEach(async () => user.click(gridcell(date)));
+ beforeEach(async () => user.click(dateButton(date)));
test("should update the footer", () => {
- expect(app()).toHaveTextContent("Try clicking the today’s date.");
+ expect(app()).toHaveTextContent("This is not the today’s date.");
});
});
diff --git a/examples/ModifiersToday.tsx b/examples/ModifiersToday.tsx
index c69badf3b3..2d1a86bb91 100644
--- a/examples/ModifiersToday.tsx
+++ b/examples/ModifiersToday.tsx
@@ -1,16 +1,19 @@
import React, { useState } from "react";
-import { DayMouseEventHandler, DayPicker } from "react-day-picker";
+import { DayEventHandler, DayPicker } from "react-day-picker";
export function ModifiersToday() {
- const initialFooter = Try clicking the today’s date.
;
+ const initialFooter = "Try clicking the today’s date.";
const [footer, setFooter] = useState(initialFooter);
- const handleDayClick: DayMouseEventHandler = (day, modifiers) => {
+ const handleDayClick: DayEventHandler = (
+ day,
+ modifiers
+ ) => {
if (modifiers.today) {
- setFooter(You clicked the today’s date.
);
+ setFooter("You clicked the today’s date.");
} else {
- setFooter(initialFooter);
+ setFooter("This is not the today’s date.");
}
};
return ;
diff --git a/examples/Multiple.test.tsx b/examples/Multiple.test.tsx
index 9a4a5aa99e..2d57b29fee 100644
--- a/examples/Multiple.test.tsx
+++ b/examples/Multiple.test.tsx
@@ -1,6 +1,6 @@
import React from "react";
-import { gridcell } from "@/test/elements";
+import { dateButton, gridcell } from "@/test/elements";
import { render } from "@/test/render";
import { user } from "@/test/user";
@@ -18,29 +18,29 @@ beforeEach(() => {
describe("when a day is clicked", () => {
const day1 = new Date(2021, 10, 1);
beforeEach(async () => {
- await user.click(gridcell(day1));
+ await user.click(dateButton(day1));
});
test("should appear as selected", () => {
- expect(gridcell(day1)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day1, true)).toHaveAttribute("aria-selected", "true");
});
describe("when the same day is clicked again", () => {
beforeEach(async () => {
- await user.click(gridcell(day1));
+ await user.click(dateButton(day1));
});
test("should appear as not selected", () => {
- expect(gridcell(day1)).not.toHaveAttribute("aria-selected");
+ expect(gridcell(day1, true)).not.toHaveAttribute("aria-selected");
});
});
describe("when a second day is clicked", () => {
const day2 = new Date(2021, 10, 2);
beforeEach(async () => {
- await user.click(gridcell(day2));
+ await user.click(dateButton(day2));
});
test("the first day should appear as selected", () => {
- expect(gridcell(day1)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day1, true)).toHaveAttribute("aria-selected", "true");
});
test("the second day should appear as selected", () => {
- expect(gridcell(day2)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day2, true)).toHaveAttribute("aria-selected", "true");
});
});
});
diff --git a/examples/MultipleMinMax.test.tsx b/examples/MultipleMinMax.test.tsx
index 40fcb5dcd6..a1dfe10f42 100644
--- a/examples/MultipleMinMax.test.tsx
+++ b/examples/MultipleMinMax.test.tsx
@@ -2,7 +2,7 @@ import React from "react";
import { addDays } from "date-fns";
-import { gridcell } from "@/test/elements";
+import { dateButton, gridcell } from "@/test/elements";
import { render } from "@/test/render";
import { user } from "@/test/user";
@@ -27,30 +27,36 @@ beforeEach(() => {
describe("when a day is clicked", () => {
beforeEach(async () => {
- await user.click(gridcell(days[0]));
+ await user.click(dateButton(days[0]));
});
test("should appear as selected", () => {
- expect(gridcell(days[0])).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(days[0], true)).toHaveAttribute("aria-selected", "true");
});
describe("when a second day is clicked", () => {
beforeEach(async () => {
- await user.click(gridcell(days[1]));
+ await user.click(dateButton(days[1]));
});
test("the first day should appear as selected", () => {
- expect(gridcell(days[0])).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(days[0], true)).toHaveAttribute("aria-selected", "true");
});
test("the second day should appear as selected", () => {
- expect(gridcell(days[1])).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(days[1], true)).toHaveAttribute("aria-selected", "true");
});
describe("when clicked again", () => {
beforeEach(async () => {
- await user.click(gridcell(days[1]));
+ await user.click(dateButton(days[1]));
});
test("the first day should still appear as selected", () => {
- expect(gridcell(days[0])).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(days[0], true)).toHaveAttribute(
+ "aria-selected",
+ "true"
+ );
});
test("the second day should still appear as selected", () => {
- expect(gridcell(days[1])).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(days[1], true)).toHaveAttribute(
+ "aria-selected",
+ "true"
+ );
});
});
});
@@ -58,17 +64,17 @@ describe("when a day is clicked", () => {
describe("when the first 5 days are clicked", () => {
beforeEach(async () => {
- await user.click(gridcell(days[0]));
- await user.click(gridcell(days[1]));
- await user.click(gridcell(days[2]));
- await user.click(gridcell(days[3]));
- await user.click(gridcell(days[4]));
+ await user.click(dateButton(days[0]));
+ await user.click(dateButton(days[1]));
+ await user.click(dateButton(days[2]));
+ await user.click(dateButton(days[3]));
+ await user.click(dateButton(days[4]));
});
test.each(days)("the %s day should appear as selected", (day) => {
- expect(gridcell(day)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day, true)).toHaveAttribute("aria-selected", "true");
});
test("the sixth day should not appear as selected", () => {
const day6 = addDays(today, 5);
- expect(gridcell(day6)).not.toHaveAttribute("aria-selected");
+ expect(gridcell(day6, true)).not.toHaveAttribute("aria-selected");
});
});
diff --git a/examples/MultipleMonthsId.test.tsx b/examples/MultipleMonthsId.test.tsx
deleted file mode 100644
index 4c20436a59..0000000000
--- a/examples/MultipleMonthsId.test.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from "react";
-
-import { grid } from "@/test/elements";
-import { render } from "@/test/render";
-
-import { MultipleMonthsId } from "./MultipleMonthsId";
-
-const today = new Date(2021, 10, 25);
-
-beforeAll(() => jest.setSystemTime(today));
-afterAll(() => jest.useRealTimers());
-
-test("the table ids should include the display index", () => {
- render( );
- expect(grid("November 2021")).toHaveAttribute(
- "id",
- "calendar_example-grid-0"
- );
- expect(grid("December 2021")).toHaveAttribute(
- "id",
- "calendar_example-grid-1"
- );
-});
diff --git a/examples/MultipleMonthsId.tsx b/examples/MultipleMonthsId.tsx
deleted file mode 100644
index 65ceceaf17..0000000000
--- a/examples/MultipleMonthsId.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import React from "react";
-
-import { DayPicker } from "react-day-picker";
-
-export function MultipleMonthsId() {
- return ;
-}
diff --git a/examples/None.tsx b/examples/None.tsx
deleted file mode 100644
index ec60f8c7b6..0000000000
--- a/examples/None.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import React from "react";
-
-import { DayPicker } from "react-day-picker";
-
-export function None() {
- return ;
-}
diff --git a/examples/NumberingSystem.test.tsx b/examples/NumberingSystem.test.tsx
index 63f34c4619..a8c78b4c38 100644
--- a/examples/NumberingSystem.test.tsx
+++ b/examples/NumberingSystem.test.tsx
@@ -1,6 +1,5 @@
import React from "react";
-import { grid } from "@/test/elements";
import { render, screen } from "@/test/render";
import { NumberingSystem } from "./NumberingSystem";
@@ -14,9 +13,6 @@ beforeEach(() => {
render( );
});
-test("should localize the year", () => {
- expect(grid("نوفمبر ٢٬٠٢١")).toBeInTheDocument();
-});
test("should localize the days", () => {
expect(screen.getByText("أحد")).toBeInTheDocument();
});
diff --git a/examples/NumberingSystem.tsx b/examples/NumberingSystem.tsx
index 4251096847..109ab57580 100644
--- a/examples/NumberingSystem.tsx
+++ b/examples/NumberingSystem.tsx
@@ -1,8 +1,8 @@
import React from "react";
-import { FormatOptions, format } from "date-fns";
+import { format } from "date-fns";
import { arSA } from "date-fns/locale";
-import { DayPicker } from "react-day-picker";
+import { DayPicker, FormatOptions } from "react-day-picker";
const NU_LOCALE = "ar-u-nu-arab";
diff --git a/examples/OutsideDays.test.tsx b/examples/OutsideDays.test.tsx
index 48cf8c0491..dd6abea27e 100644
--- a/examples/OutsideDays.test.tsx
+++ b/examples/OutsideDays.test.tsx
@@ -1,6 +1,7 @@
import React from "react";
-import { render, screen } from "@/test/render";
+import { gridcell } from "@/test/elements";
+import { render } from "@/test/render";
import { OutsideDays } from "./OutsideDays";
@@ -15,7 +16,6 @@ beforeEach(() => {
describe("when displaying a month with outside days", () => {
test("should display the outside day", () => {
- // TODO: verify this test actually works
- expect(screen.getByRole("gridcell", { name: "31" })).toBeInTheDocument();
+ expect(gridcell(new Date(2021, 9, 31))).toBeInTheDocument();
});
});
diff --git a/examples/OutsideDays.tsx b/examples/OutsideDays.tsx
index abc7021c69..b68860026e 100644
--- a/examples/OutsideDays.tsx
+++ b/examples/OutsideDays.tsx
@@ -3,5 +3,5 @@ import React from "react";
import { DayPicker } from "react-day-picker";
export function OutsideDays() {
- return ;
+ return ;
}
diff --git a/examples/Range.test.tsx b/examples/Range.test.tsx
index ff175ae887..40e3e50316 100644
--- a/examples/Range.test.tsx
+++ b/examples/Range.test.tsx
@@ -2,20 +2,21 @@ import React from "react";
import { addDays } from "date-fns";
-import { gridcell } from "@/test/elements";
+import { dateButton, gridcell } from "@/test/elements";
import { render, screen } from "@/test/render";
import { user } from "@/test/user";
import { Range } from "./Range";
-const pastMonth = new Date(2020, 10, 15);
+const defaultMonth = new Date(2020, 5, 15);
+
let container: HTMLElement;
const days = [
- pastMonth,
- addDays(pastMonth, 1),
- addDays(pastMonth, 2),
- addDays(pastMonth, 3),
- addDays(pastMonth, 4)
+ defaultMonth,
+ addDays(defaultMonth, 1),
+ addDays(defaultMonth, 2),
+ addDays(defaultMonth, 3),
+ addDays(defaultMonth, 4)
];
beforeEach(() => (container = render( ).container));
@@ -24,33 +25,33 @@ test("should match the snapshot", () => {
expect(container).toMatchSnapshot();
});
test.each(days)("%s should be selected", (day) => {
- expect(gridcell(day)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day, true)).toHaveAttribute("aria-selected", "true");
});
describe("when a day in the range is clicked", () => {
const day = days[2];
- beforeEach(async () => user.click(gridcell(day)));
+ beforeEach(async () => user.click(dateButton(day)));
test.each([days[0], days[1], day])("%s should be selected", (day) => {
- expect(gridcell(day)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day, true)).toHaveAttribute("aria-selected", "true");
});
test.each([days[3], days[4]])("%s should not be selected", (day) => {
- expect(gridcell(day)).not.toHaveAttribute("aria-selected");
+ expect(gridcell(day, true)).not.toHaveAttribute("aria-selected");
});
describe("when the day is clicked again", () => {
const day = days[2];
- beforeEach(async () => user.click(gridcell(day)));
+ beforeEach(async () => user.click(dateButton(day)));
test("only one day should be selected", () => {
- expect(getAllSelectedDays()).toHaveLength(1);
+ expect(getAllSelected()).toHaveLength(1);
});
test("only a day in the range should be selected", () => {
- expect(gridcell(day)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day, true)).toHaveAttribute("aria-selected", "true");
});
describe("when a day in the range is clicked again", () => {
const day = days[2];
- beforeEach(async () => user.click(gridcell(day)));
+ beforeEach(async () => user.click(dateButton(day)));
test("no day should be selected", () => {
- expect(getAllSelectedDays()).toHaveLength(0);
+ expect(getAllSelected()).toHaveLength(0);
});
test("should match the snapshot", () => {
expect(container).toMatchSnapshot();
@@ -59,10 +60,10 @@ describe("when a day in the range is clicked", () => {
});
});
-function getAllSelectedDays() {
- const buttons = screen.getAllByRole("gridcell");
+function getAllSelected() {
+ const gridcells = screen.getAllByRole("gridcell");
- return Array.from(buttons).filter(
- (button) => button.getAttribute("aria-selected") === "true"
+ return Array.from(gridcells).filter(
+ (gridcell) => gridcell.getAttribute("aria-selected") === "true"
);
}
diff --git a/examples/Range.tsx b/examples/Range.tsx
index 4dc59b97ea..d56ef15254 100644
--- a/examples/Range.tsx
+++ b/examples/Range.tsx
@@ -12,16 +12,12 @@ export function Range() {
};
const [range, setRange] = useState(defaultSelected);
- let footer = Please pick the first day.
;
+ let footer = `Please pick the first day.`;
if (range?.from) {
if (!range.to) {
- footer = {format(range.from, "PPP")}
;
+ footer = format(range.from, "PPP");
} else if (range.to) {
- footer = (
-
- {format(range.from, "PPP")}–{format(range.to, "PPP")}
-
- );
+ footer = `${format(range.from, "PPP")}–${format(range.to, "PPP")}`;
}
}
diff --git a/examples/RangeMinMax.test.tsx b/examples/RangeMinMax.test.tsx
index c290111326..abf2d750f7 100644
--- a/examples/RangeMinMax.test.tsx
+++ b/examples/RangeMinMax.test.tsx
@@ -2,7 +2,7 @@ import React from "react";
import { addDays } from "date-fns";
-import { gridcell } from "@/test/elements";
+import { dateButton, gridcell } from "@/test/elements";
import { render } from "@/test/render";
import { user } from "@/test/user";
@@ -13,40 +13,43 @@ beforeEach(() => render( ));
describe("when a day is clicked", () => {
const firstDay = new Date(2022, 8, 13);
beforeEach(async () => {
- await user.click(gridcell(firstDay));
+ await user.click(dateButton(firstDay));
});
test("should be selected", () => {
- expect(gridcell(firstDay)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(firstDay, true)).toHaveAttribute("aria-selected", "true");
});
describe("when the day before min is clicked", () => {
const dayAfter = addDays(firstDay, 1);
beforeEach(async () => {
- await user.click(gridcell(dayAfter));
+ await user.click(dateButton(dayAfter));
});
test("the first day should not be selected", () => {
- expect(gridcell(firstDay)).not.toHaveAttribute("aria-selected", "true");
+ expect(gridcell(firstDay, true)).not.toHaveAttribute(
+ "aria-selected",
+ "true"
+ );
});
test("the day after should be selected", () => {
- expect(gridcell(dayAfter)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(dayAfter, true)).toHaveAttribute("aria-selected", "true");
});
});
describe("when the day after min is clicked", () => {
const dayAfter = addDays(firstDay, 4);
beforeEach(async () => {
- await user.click(gridcell(dayAfter));
+ await user.click(dateButton(dayAfter));
});
test("a range should be selected", () => {
- expect(gridcell(firstDay)).toHaveAttribute("aria-selected", "true");
- expect(gridcell(addDays(firstDay, 1))).toHaveAttribute(
+ expect(gridcell(firstDay, true)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(addDays(firstDay, 1), true)).toHaveAttribute(
"aria-selected",
"true"
);
- expect(gridcell(addDays(firstDay, 2))).toHaveAttribute(
+ expect(gridcell(addDays(firstDay, 2), true)).toHaveAttribute(
"aria-selected",
"true"
);
- expect(gridcell(addDays(firstDay, 3))).toHaveAttribute(
+ expect(gridcell(addDays(firstDay, 3), true)).toHaveAttribute(
"aria-selected",
"true"
);
diff --git a/examples/RangeMinMax.tsx b/examples/RangeMinMax.tsx
index a750cce99e..5e8b8edc20 100644
--- a/examples/RangeMinMax.tsx
+++ b/examples/RangeMinMax.tsx
@@ -6,16 +6,12 @@ import { DateRange, DayPicker } from "react-day-picker";
export function RangeMinMax() {
const [range, setRange] = useState();
- let footer = Please pick the first day.
;
+ let footer = `Please pick the first day.`;
if (range?.from) {
if (!range.to) {
- footer = {format(range.from, "PPP")}
;
+ footer = format(range.from, "PPP");
} else if (range.to) {
- footer = (
-
- {format(range.from, "PPP")}–{format(range.to, "PPP")}
-
- );
+ footer = `${format(range.from, "PPP")}–${format(range.to, "PPP")}`;
}
}
diff --git a/examples/RangeShiftKey.test.tsx b/examples/RangeShiftKey.test.tsx
index 1c22a565d9..6883c3bad6 100644
--- a/examples/RangeShiftKey.test.tsx
+++ b/examples/RangeShiftKey.test.tsx
@@ -1,6 +1,6 @@
import React from "react";
-import { gridcell } from "@/test/elements";
+import { dateButton, gridcell } from "@/test/elements";
import { render } from "@/test/render";
import { user } from "@/test/user";
@@ -11,34 +11,34 @@ const today = new Date(2021, 10, 25);
beforeAll(() => jest.setSystemTime(today));
afterAll(() => jest.useRealTimers());
-beforeEach(() => render( ).container);
+beforeEach(() => render( ));
describe("when displaying November 2021", () => {
describe("when clicking on the 11th", () => {
const day1 = new Date(2021, 10, 11);
- beforeEach(async () => user.click(gridcell(day1)));
+ beforeEach(async () => user.click(dateButton(day1)));
test("the 11th day should have aria-selected true", () => {
- expect(gridcell(day1)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day1, true)).toHaveAttribute("aria-selected", "true");
});
describe("when clicking on the 13th", () => {
const day2 = new Date(2021, 10, 13);
- beforeEach(async () => user.click(gridcell(day2)));
+ beforeEach(async () => user.click(dateButton(day2)));
test("the 11th day should still have aria-selected true", () => {
- expect(gridcell(day1)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day1, true)).toHaveAttribute("aria-selected", "true");
});
test("the 13th day not should not have aria-selected", () => {
- expect(gridcell(day2)).not.toHaveAttribute("aria-selected");
+ expect(gridcell(day2, true)).not.toHaveAttribute("aria-selected");
});
});
describe("when pressing the Shift key", () => {
const day2 = new Date(2021, 10, 13);
beforeEach(async () => {
user.keyboard("{Shift>}");
- await user.click(gridcell(day2));
+ await user.click(dateButton(day2));
});
test("the 13th day should have aria-selected true", () => {
- expect(gridcell(day2)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day2, true)).toHaveAttribute("aria-selected", "true");
});
});
});
diff --git a/examples/RangeShiftKey.tsx b/examples/RangeShiftKey.tsx
index e79d47058d..53084e219d 100644
--- a/examples/RangeShiftKey.tsx
+++ b/examples/RangeShiftKey.tsx
@@ -3,16 +3,15 @@ import React, { MouseEventHandler } from "react";
import { isSameDay } from "date-fns";
import {
DateRange,
+ DayButtonProps,
DayPicker,
- useRange,
- type DayProps
+ useDayPicker
} from "react-day-picker";
-function DayWithShiftKey(props: DayProps) {
- const { selected } = useRange();
- const onClick = props.rootProps?.onClick;
+function DayWithShiftKey(props: DayButtonProps) {
+ const { selected } = useDayPicker({ mode: "range" });
- const handleClick: MouseEventHandler = (e) => {
+ const handleClick: MouseEventHandler = (e) => {
if (
selected?.from &&
!selected.to &&
@@ -21,12 +20,12 @@ function DayWithShiftKey(props: DayProps) {
) {
return;
}
- onClick?.(e);
+ props.onClick?.(e);
};
return (
-
+
{props.children}
-
+
);
}
@@ -35,21 +34,19 @@ export function RangeShiftKey() {
from: undefined
});
- let footer = Please pick a day.
;
+ let footer = "Please pick a day.";
if (range?.from && !range?.to) {
- footer = Press Shift to choose more days.
;
+ footer = "Press Shift to choose more days.";
} else if (range?.to) {
- footer = (
-
- {range?.from?.toLocaleDateString()}—{range.to.toLocaleDateString()}.
-
- );
+ const formattedFrom = range.from?.toDateString();
+ const formattedTo = range.to.toDateString();
+ footer = `You selected the days between ${formattedFrom} and ${formattedTo}`;
}
return (
{
describe("when a day is clicked", () => {
const day = new Date(2021, 10, 1);
beforeEach(async () => {
- await user.click(gridcell(day));
+ await user.click(dateButton(day));
});
test("should appear as selected", () => {
- expect(gridcell(day)).toHaveAttribute("aria-selected", "true");
- expect(gridcell(day)).toHaveFocus();
- expect(gridcell(day)).toHaveClass("rdp-selected");
+ expect(gridcell(day, true)).toHaveAttribute("aria-selected", "true");
+ expect(dateButton(day)).toHaveFocus();
+ expect(gridcell(day, true)).toHaveClass("rdp-selected");
});
describe("when the day is clicked again", () => {
beforeEach(async () => {
- await user.click(gridcell(day));
+ await user.click(dateButton(day));
});
test("should not appear as selected", () => {
- expect(gridcell(day)).not.toHaveAttribute("aria-selected");
- expect(gridcell(day)).not.toHaveClass("rdp-selected");
+ expect(gridcell(day, true)).not.toHaveAttribute("aria-selected");
});
});
});
diff --git a/examples/SingleRequired.test.tsx b/examples/SingleRequired.test.tsx
index eb9f8fae1e..fd3def454c 100644
--- a/examples/SingleRequired.test.tsx
+++ b/examples/SingleRequired.test.tsx
@@ -1,29 +1,29 @@
import React from "react";
-import { gridcell } from "@/test/elements";
-import { renderApp } from "@/test/renderApp";
+import { dateButton, gridcell } from "@/test/elements";
+import { render } from "@/test/render";
import { user } from "@/test/user";
import { SingleRequired } from "./SingleRequired";
beforeEach(() => {
- renderApp( );
+ render( );
});
describe("when a day is clicked", () => {
const day = new Date();
beforeEach(async () => {
- await user.click(gridcell(day));
+ await user.click(dateButton(day));
});
test("should appear as selected", () => {
- expect(gridcell(day)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day, true)).toHaveAttribute("aria-selected", "true");
});
describe("when the day is clicked again", () => {
beforeEach(async () => {
- await user.click(gridcell(day));
+ await user.click(dateButton(day));
});
test("should appear as selected", () => {
- expect(gridcell(day)).toHaveAttribute("aria-selected", "true");
+ expect(gridcell(day, true)).toHaveAttribute("aria-selected", "true");
});
});
});
diff --git a/examples/Start.test.tsx b/examples/Start.test.tsx
index c58e29fa0e..636484c504 100644
--- a/examples/Start.test.tsx
+++ b/examples/Start.test.tsx
@@ -1,6 +1,11 @@
import React from "react";
-import { nextButton, gridcell, previousButton } from "@/test/elements";
+import {
+ nextButton,
+ dateButton,
+ previousButton,
+ gridcell
+} from "@/test/elements";
import { render } from "@/test/render";
import { user } from "@/test/user";
@@ -31,7 +36,7 @@ const day = new Date(2021, 10, 1);
describe("when a day is clicked", () => {
test("should appear as selected", async () => {
- await user.click(gridcell(day));
- expect(gridcell(day)).toHaveAttribute("aria-selected", "true");
+ await user.click(dateButton(day));
+ expect(gridcell(day, true)).toHaveAttribute("aria-selected", "true");
});
});
diff --git a/examples/Start.tsx b/examples/Start.tsx
index 19eb39c41e..f9fbc62007 100644
--- a/examples/Start.tsx
+++ b/examples/Start.tsx
@@ -5,9 +5,5 @@ import { DayPicker } from "react-day-picker";
export function Start() {
const [selected, setSelected] = useState();
- return (
-
-
-
- );
+ return ;
}
diff --git a/examples/TailwindCSS.tsx b/examples/TailwindCSS.tsx
index 6672676414..48fed9c607 100644
--- a/examples/TailwindCSS.tsx
+++ b/examples/TailwindCSS.tsx
@@ -21,7 +21,7 @@ export function TailwindCSS() {
classNames={{
today: `border-amber-500`,
selected: `bg-amber-500 border-amber-500 text-white`,
- calendar: `${defaultClassNames.calendar} bg-white shadow-lg p-5`,
+ root: `${defaultClassNames.root} bg-white shadow-lg p-5`,
chevron: `${defaultClassNames.chevron} fill-amber-500`
}}
/>
diff --git a/examples/TestCase2047.test.tsx b/examples/TestCase2047.test.tsx
index f2911e2530..1c9094a6c0 100644
--- a/examples/TestCase2047.test.tsx
+++ b/examples/TestCase2047.test.tsx
@@ -1,6 +1,6 @@
import React from "react";
-import { gridcell } from "@/test/elements";
+import { dateButton, gridcell } from "@/test/elements";
import { render } from "@/test/render";
import { user } from "@/test/user";
@@ -11,14 +11,18 @@ beforeEach(async () => {
});
test("disabled date is not selected", () => {
- expect(gridcell(new Date(2024, 5, 10))).not.toHaveAttribute("aria-selected");
+ expect(gridcell(new Date(2024, 5, 10), true)).not.toHaveAttribute(
+ "aria-selected"
+ );
});
describe("when the calendar is focused", () => {
beforeEach(async () => {
- await user.click(gridcell(new Date(2024, 5, 10)));
+ await user.click(dateButton(new Date(2024, 5, 10)));
});
test("the disabled day should not have focused modifier", () => {
- expect(gridcell(new Date(2024, 5, 10))).not.toHaveClass("rdp-focused");
+ expect(gridcell(new Date(2024, 5, 10), true)).not.toHaveClass(
+ "rdp-focused"
+ );
});
});
diff --git a/examples/Weeknumber.test.tsx b/examples/Weeknumber.test.tsx
index c1ffeebc91..abe7de8117 100644
--- a/examples/Weeknumber.test.tsx
+++ b/examples/Weeknumber.test.tsx
@@ -1,7 +1,6 @@
import React from "react";
import { render, screen } from "@/test/render";
-import { user } from "@/test/user";
import { Weeknumber } from "./Weeknumber";
@@ -22,14 +21,4 @@ describe("when displaying November 2021", () => {
test("should display the 45th week number", () => {
expect(getWeekButton(45)).toBeInTheDocument();
});
- describe("when the week button is clicked", () => {
- beforeEach(async () => {
- return user.click(getWeekButton(45));
- });
- test("should update the footer", () => {
- expect(
- screen.getByText("You clicked the week n. 45.")
- ).toBeInTheDocument();
- });
- });
});
diff --git a/examples/Weeknumber.tsx b/examples/Weeknumber.tsx
index e91b939efa..2989346b7a 100644
--- a/examples/Weeknumber.tsx
+++ b/examples/Weeknumber.tsx
@@ -1,19 +1,7 @@
-import React, { useState } from "react";
+import React from "react";
import { DayPicker } from "react-day-picker";
export function Weeknumber() {
- const [weekNumber, setWeekNumber] = useState();
-
- const footer = weekNumber
- ? `You clicked the week n. ${weekNumber}.`
- : "Try clicking a week number.";
-
- return (
-
- );
+ return ;
}
diff --git a/examples/WeeknumberCustom.tsx b/examples/WeeknumberCustom.tsx
index 6430d355cb..065c3e96eb 100644
--- a/examples/WeeknumberCustom.tsx
+++ b/examples/WeeknumberCustom.tsx
@@ -1,17 +1,23 @@
+/* eslint-disable no-console */
import React from "react";
import { addMonths } from "date-fns";
-import { DayPicker } from "react-day-picker";
+import { DayPicker, WeekNumberProps } from "react-day-picker";
const today = new Date(2021, 0, 1);
export function WeeknumberCustom() {
return (
(
+
+ console.log(week)}>{props.children}
+
+ )
+ }}
labels={{
labelWeekNumber: (weekNumber: number) => `W${weekNumber}`
}}
diff --git a/examples/__snapshots__/Range.test.tsx.snap b/examples/__snapshots__/Range.test.tsx.snap
index d9bb2bfeab..dbec639685 100644
--- a/examples/__snapshots__/Range.test.tsx.snap
+++ b/examples/__snapshots__/Range.test.tsx.snap
@@ -3,13 +3,16 @@
exports[`should match the snapshot 1`] = `
@@ -17,7 +20,6 @@ exports[`should match the snapshot 1`] = `
aria-controls="test"
aria-label="Previous Month"
class="rdp-button_previous"
- name="previous-month"
type="button"
>
-
-
-
- Su
-
-
- Mo
-
-
- Tu
-
-
- We
-
-
- Th
-
-
- Fr
-
-
+
- Sa
-
-
-
+ Su
+
+
+ Mo
+
+
+ Tu
+
+
+ We
+
+
+ Th
+
+
+ Fr
+
+
+ Sa
+
+
+
+
-
-
-
31
-
-
-
+
+
-
1
-
-
-
+
+
-
2
-
-
-
+
+
-
3
-
-
-
+
+
-
4
-
-
-
+
+
-
5
-
-
-
+
+
-
6
-
-
-
-
+
+
+
-
-
7
-
-
-
+
+
-
8
-
-
-
+
+
-
9
-
-
-
+
+
-
10
-
-
-
+
+
-
11
-
-
-
+
+
-
12
-
-
-
+
+
-
13
-
-
-
-
+
+
+
-
-
14
-
-
-
+
+
-
15
-
-
-
+
+
-
16
-
-
-
+
+
-
17
-
-
-
+
+
-
18
-
-
-
+
+
-
19
-
-
-
+
+
-
20
-
-
-
-
+
+
+
-
-
21
-
-
-
+
+
-
22
-
-
-
+
+
-
23
-
-
-
+
+
-
24
-
-
-
+
+
-
25
-
-
-
+
+
-
26
-
-
-
+
+
-
27
-
-
-
-
+
+
+
-
-
28
-
-
-
+
+
-
29
-
-
-
+
+
-
30
-
-
-
+
+
-
1
-
-
-
+
+
-
2
-
-
-
+
+
-
3
-
-
-
+
+
-
4
-
-
-
-
-
+
+
+
+
+
@@ -654,13 +645,16 @@ exports[`should match the snapshot 1`] = `
exports[`when a day in the range is clicked when the day is clicked again when a day in the range is clicked again should match the snapshot 1`] = `
@@ -668,7 +662,6 @@ exports[`when a day in the range is clicked when the day is clicked again when a
aria-controls="test"
aria-label="Previous Month"
class="rdp-button_previous"
- name="previous-month"
type="button"
>
-
-
-
- Su
-
-
- Mo
-
-
- Tu
-
-
- We
-
-
- Th
-
-
- Fr
-
-
+
- Sa
-
-
-
+ Su
+
+
+ Mo
+
+
+ Tu
+
+
+ We
+
+
+ Th
+
+
+ Fr
+
+
+ Sa
+
+
+
+
-
-
-
31
-
-
-
+
+
-
1
-
-
-
+
+
-
2
-
-
-
+
+
-
3
-
-
-
+
+
-
4
-
-
-
+
+
-
5
-
-
-
+
+
-
6
-
-
-
-
+
+
+
-
-
7
-
-
-
+
+
-
8
-
-
-
+
+
-
9
-
-
-
+
+
-
10
-
-
-
+
+
-
11
-
-
-
+
+
-
12
-
-
-
+
+
-
13
-
-
-
-
+
+
+
-
-
14
-
-
-
+
+
-
15
-
-
-
+
+
-
16
-
-
-
+
+
-
17
-
-
-
+
+
-
18
-
-
-
+
+
-
19
-
-
-
+
+
-
20
-
-
-
-
+
+
+
-
-
21
-
-
-
+
+
-
22
-
-
-
+
+
-
23
-
-
-
+
+
-
24
-
-
-
+
+
-
25
-
-
-
+
+
-
26
-
-
-
+
+
-
27
-
-
-
-
+
+
+
-
-
28
-
-
-
+
+
-
29
-
-
-
+
+
-
30
-
-
-
+
+
-
1
-
-
-
+
+
-
2
-
-
-
+
+
-
3
-
-
-
+
+
-
4
-
-
-
-
-
+
+
+
+
+
diff --git a/examples/__snapshots__/StylingCssModules.test.tsx.snap b/examples/__snapshots__/StylingCssModules.test.tsx.snap
index e797fe97d0..61284e82e9 100644
--- a/examples/__snapshots__/StylingCssModules.test.tsx.snap
+++ b/examples/__snapshots__/StylingCssModules.test.tsx.snap
@@ -5,13 +5,15 @@ exports[`should match the snapshot 1`] = `
role="application"
>
@@ -19,7 +21,6 @@ exports[`should match the snapshot 1`] = `
aria-controls=":r0:"
aria-label="Previous Month"
class="rdp-button_previous"
- name="previous-month"
type="button"
>
-
-
-
- Su
-
-
- Mo
-
-
- Tu
-
-
- We
-
-
- Th
-
-
- Fr
-
-
+
- Sa
-
-
-
+ Su
+
+
+ Mo
+
+
+ Tu
+
+
+ We
+
+
+ Th
+
+
+ Fr
+
+
+ Sa
+
+
+
+
-
-
-
31
-
-
-
+
+
-
1
-
-
-
+
+
-
2
-
-
-
+
+
-
3
-
-
-
+
+
-
4
-
-
-
+
+
-
5
-
-
-
+
+
-
6
-
-
-
-
+
+
+
-
-
7
-
-
-
+
+
-
8
-
-
-
+
+
-
9
-
-
-
+
+
-
10
-
-
-
+
+
-
11
-
-
-
+
+
-
12
-
-
-
+
+
-
13
-
-
-
-
+
+
+
-
-
14
-
-
-
+
+
-
15
-
-
-
+
+
-
16
-
-
-
+
+
-
17
-
-
-
+
+
-
18
-
-
-
+
+
-
19
-
-
-
+
+
-
20
-
-
-
-
+
+
+
-
-
21
-
-
-
+
+
-
22
-
-
-
+
+
-
23
-
-
-
+
+
-
24
-
-
-
+
+
-
25
-
-
-
+
+
-
26
-
-
-
+
+
-
27
-
-
-
-
+
+
+
-
-
28
-
-
-
+
+
-
29
-
-
-
+
+
-
30
-
-
-
+
+
-
1
-
-
-
+
+
-
2
-
-
-
+
+
-
3
-
-
-
+
+
-
4
-
-
-
-
-
+
+
+
+
+
diff --git a/examples/index.ts b/examples/index.ts
index 07755f3685..34d9be9cc4 100644
--- a/examples/index.ts
+++ b/examples/index.ts
@@ -4,7 +4,7 @@ export * from "./Controlled";
export * from "./CssModules";
export * from "./CssVariables";
export * from "./CustomCaption";
-export * from "./CustomDayDate";
+export * from "./CustomDayButton";
export * from "./CustomMultiple";
export * from "./CustomSingle";
export * from "./CustomWeek";
@@ -15,6 +15,7 @@ export * from "./DisableNavigation";
export * from "./Dropdown";
export * from "./DropdownMultipleMonths";
export * from "./Fixedweeks";
+export * from "./AutoFocus";
export * from "./FocusRecursive";
export * from "./Footer";
export * from "./Formatters";
@@ -36,9 +37,7 @@ export * from "./ModifiersToday";
export * from "./Multiple";
export * from "./MultipleMinMax";
export * from "./MultipleMonths";
-export * from "./MultipleMonthsId";
export * from "./MultipleMonthsPaged";
-export * from "./None";
export * from "./NumberingSystem";
export * from "./OutsideDays";
export * from "./Range";
diff --git a/package.json b/package.json
index e6f284dbeb..356d71d68b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-day-picker",
- "version": "9.0.0-rc.2",
+ "version": "9.0.0-rc.4",
"description": "Customizable Date Picker for React",
"author": "Giampaolo Bellavite
",
"homepage": "http://daypicker.dev",
@@ -98,10 +98,10 @@
},
"scripts": {
"prepublish": "pnpm build",
- "build": "pnpm build:cjs && pnpm build:esm",
+ "build": "pnpm build:cjs && pnpm build:esm && pnpm build:css",
"build:cjs": "tsc --project tsconfig-cjs.json",
"build:esm": "tsc --project tsconfig-esm.json",
- "build:css": "tcm src",
+ "build:css": "./scripts/build-css.sh ./src/style.css ./src/style.module.css",
"lint": "eslint .",
"test": "jest",
"test-watch": "jest --watch",
@@ -121,17 +121,17 @@
"devDependencies": {
"@date-fns/utc": "^1.2.0",
"@jest/types": "^29.6.3",
- "@testing-library/dom": "^10.1.0",
+ "@testing-library/dom": "^10.3.1",
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/jest": "^29.5.12",
- "@types/node": "^20.14.2",
+ "@types/node": "^20.14.10",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
- "@typescript-eslint/eslint-plugin": "^7.13.1",
- "@typescript-eslint/parser": "^7.12.0",
+ "@typescript-eslint/eslint-plugin": "^7.15.0",
+ "@typescript-eslint/parser": "^7.16.0",
"date-fns-jalali": "3.6.0-1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 49c6585eee..84df851472 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -19,17 +19,17 @@ importers:
specifier: ^29.6.3
version: 29.6.3
'@testing-library/dom':
- specifier: ^10.1.0
- version: 10.2.0
+ specifier: ^10.3.1
+ version: 10.3.1
'@testing-library/jest-dom':
specifier: ^6.4.5
- version: 6.4.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3)))
+ version: 6.4.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))
'@testing-library/react':
specifier: ^16.0.0
- version: 16.0.0(@testing-library/dom@10.2.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 16.0.0(@testing-library/dom@10.3.1)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@testing-library/user-event':
specifier: ^14.5.2
- version: 14.5.2(@testing-library/dom@10.2.0)
+ version: 14.5.2(@testing-library/dom@10.3.1)
'@trivago/prettier-plugin-sort-imports':
specifier: ^4.3.0
version: 4.3.0(prettier@3.3.2)
@@ -37,8 +37,8 @@ importers:
specifier: ^29.5.12
version: 29.5.12
'@types/node':
- specifier: ^20.14.2
- version: 20.14.9
+ specifier: ^20.14.10
+ version: 20.14.10
'@types/react':
specifier: ^18.3.3
version: 18.3.3
@@ -46,11 +46,11 @@ importers:
specifier: ^18.3.0
version: 18.3.0
'@typescript-eslint/eslint-plugin':
- specifier: ^7.13.1
- version: 7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)
+ specifier: ^7.15.0
+ version: 7.15.0(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)
'@typescript-eslint/parser':
- specifier: ^7.12.0
- version: 7.14.1(eslint@8.57.0)(typescript@5.5.3)
+ specifier: ^7.16.0
+ version: 7.16.0(eslint@8.57.0)(typescript@5.5.3)
date-fns-jalali:
specifier: 3.6.0-1
version: 3.6.0-1
@@ -62,13 +62,13 @@ importers:
version: 9.1.0(eslint@8.57.0)
eslint-import-resolver-typescript:
specifier: ^3.6.1
- version: 3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+ version: 3.6.1(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0)
eslint-plugin-import:
specifier: ^2.29.1
- version: 2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+ version: 2.29.1(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
eslint-plugin-jest:
specifier: ^28.6.0
- version: 28.6.0(@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3)))(typescript@5.5.3)
+ version: 28.6.0(@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))(typescript@5.5.3)
eslint-plugin-prettier:
specifier: ^5.1.3
version: 5.1.3(@types/eslint@8.56.10)(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.2)
@@ -86,7 +86,7 @@ importers:
version: 6.2.2(eslint@8.57.0)(typescript@5.5.3)
jest:
specifier: ^29.7.0
- version: 29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3))
+ version: 29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
jest-environment-jsdom:
specifier: ^29.7.0
version: 29.7.0
@@ -110,10 +110,10 @@ importers:
version: 18.3.1(react@18.3.1)
ts-jest:
specifier: ^29.1.4
- version: 29.1.5(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3)))(typescript@5.5.3)
+ version: 29.1.5(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))(typescript@5.5.3)
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@20.14.9)(typescript@5.5.3)
+ version: 10.9.2(@types/node@20.14.10)(typescript@5.5.3)
tslib:
specifier: ^2.6.2
version: 2.6.3
@@ -133,7 +133,7 @@ importers:
specifier: ^18.3.1
version: 18.3.1
react-day-picker:
- specifier: workspace:9.0.0-rc.2
+ specifier: workspace:*
version: link:..
react-dom:
specifier: ^18.3.1
@@ -146,14 +146,14 @@ importers:
specifier: ^18.3.0
version: 18.3.0
'@typescript-eslint/eslint-plugin':
- specifier: ^7.13.1
- version: 7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)
+ specifier: ^7.15.0
+ version: 7.15.0(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)
'@typescript-eslint/parser':
- specifier: ^7.13.1
- version: 7.14.1(eslint@8.57.0)(typescript@5.5.3)
+ specifier: ^7.16.0
+ version: 7.16.0(eslint@8.57.0)(typescript@5.5.3)
'@vitejs/plugin-react':
specifier: ^4.3.1
- version: 4.3.1(vite@5.3.1(@types/node@20.14.9)(terser@5.31.1))
+ version: 4.3.1(vite@5.3.3(@types/node@20.14.10)(terser@5.31.1))
eslint:
specifier: ^8.57.0
version: 8.57.0
@@ -167,8 +167,8 @@ importers:
specifier: ^5.5.3
version: 5.5.3
vite:
- specifier: ^5.3.1
- version: 5.3.1(@types/node@20.14.9)(terser@5.31.1)
+ specifier: ^5.3.3
+ version: 5.3.3(@types/node@20.14.10)(terser@5.31.1)
website:
dependencies:
@@ -206,7 +206,7 @@ importers:
specifier: workspace:^
version: link:..
react-day-picker-v8:
- specifier: npm:react-day-picker@8
+ specifier: npm:react-day-picker@8.10.1
version: react-day-picker@8.10.1(date-fns@3.6.0)(react@18.3.1)
react-dom:
specifier: ^18.3.1
@@ -225,8 +225,8 @@ importers:
specifier: 3.4.0
version: 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
docusaurus-plugin-typedoc:
- specifier: ^1.0.1
- version: 1.0.1(typedoc-plugin-markdown@4.1.0(typedoc@0.26.3(typescript@5.5.3)))
+ specifier: ^1.0.2
+ version: 1.0.2(typedoc-plugin-markdown@4.1.2(typedoc@0.26.3(typescript@5.5.3)))
identity-obj-proxy:
specifier: ^3.0.0
version: 3.0.0
@@ -235,10 +235,10 @@ importers:
version: 0.26.3(typescript@5.5.3)
typedoc-plugin-frontmatter:
specifier: ^1.0.0
- version: 1.0.0(typedoc-plugin-markdown@4.1.0(typedoc@0.26.3(typescript@5.5.3)))
+ version: 1.0.0(typedoc-plugin-markdown@4.1.2(typedoc@0.26.3(typescript@5.5.3)))
typedoc-plugin-markdown:
- specifier: ^4.1.0
- version: 4.1.0(typedoc@0.26.3(typescript@5.5.3))
+ specifier: ^4.1.2
+ version: 4.1.2(typedoc@0.26.3(typescript@5.5.3))
typescript:
specifier: ~5.5.3
version: 5.5.3
@@ -1700,8 +1700,8 @@ packages:
resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
engines: {node: '>=14.16'}
- '@testing-library/dom@10.2.0':
- resolution: {integrity: sha512-CytIvb6tVOADRngTHGWNxH8LPgO/3hi/BdCEHOf7Qd2GvZVClhVP0Wo/QHzWhpki49Bk0b4VT6xpt3fx8HTSIw==}
+ '@testing-library/dom@10.3.1':
+ resolution: {integrity: sha512-q/WL+vlXMpC0uXDyfsMtc1rmotzLV8Y0gq6q1gfrrDjQeHoeLrqHbxdPvPNAh1i+xuJl7+BezywcXArz7vLqKQ==}
engines: {node: '>=18'}
'@testing-library/jest-dom@6.4.6':
@@ -1892,6 +1892,9 @@ packages:
'@types/node@17.0.45':
resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==}
+ '@types/node@20.14.10':
+ resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==}
+
'@types/node@20.14.9':
resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==}
@@ -1976,8 +1979,8 @@ packages:
'@types/yargs@17.0.32':
resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==}
- '@typescript-eslint/eslint-plugin@7.14.1':
- resolution: {integrity: sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==}
+ '@typescript-eslint/eslint-plugin@7.15.0':
+ resolution: {integrity: sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==}
engines: {node: ^18.18.0 || >=20.0.0}
peerDependencies:
'@typescript-eslint/parser': ^7.0.0
@@ -1987,8 +1990,8 @@ packages:
typescript:
optional: true
- '@typescript-eslint/parser@7.14.1':
- resolution: {integrity: sha512-8lKUOebNLcR0D7RvlcloOacTOWzOqemWEWkKSVpMZVF/XVcwjPR+3MD08QzbW9TCGJ+DwIc6zUSGZ9vd8cO1IA==}
+ '@typescript-eslint/parser@7.16.0':
+ resolution: {integrity: sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==}
engines: {node: ^18.18.0 || >=20.0.0}
peerDependencies:
eslint: ^8.56.0
@@ -2005,8 +2008,16 @@ packages:
resolution: {integrity: sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==}
engines: {node: ^18.18.0 || >=20.0.0}
- '@typescript-eslint/type-utils@7.14.1':
- resolution: {integrity: sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ==}
+ '@typescript-eslint/scope-manager@7.15.0':
+ resolution: {integrity: sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+
+ '@typescript-eslint/scope-manager@7.16.0':
+ resolution: {integrity: sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+
+ '@typescript-eslint/type-utils@7.15.0':
+ resolution: {integrity: sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==}
engines: {node: ^18.18.0 || >=20.0.0}
peerDependencies:
eslint: ^8.56.0
@@ -2023,6 +2034,14 @@ packages:
resolution: {integrity: sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==}
engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/types@7.15.0':
+ resolution: {integrity: sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+
+ '@typescript-eslint/types@7.16.0':
+ resolution: {integrity: sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+
'@typescript-eslint/typescript-estree@5.62.0':
resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -2041,6 +2060,24 @@ packages:
typescript:
optional: true
+ '@typescript-eslint/typescript-estree@7.15.0':
+ resolution: {integrity: sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/typescript-estree@7.16.0':
+ resolution: {integrity: sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
'@typescript-eslint/utils@5.62.0':
resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -2053,6 +2090,12 @@ packages:
peerDependencies:
eslint: ^8.56.0
+ '@typescript-eslint/utils@7.15.0':
+ resolution: {integrity: sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+ peerDependencies:
+ eslint: ^8.56.0
+
'@typescript-eslint/visitor-keys@5.62.0':
resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -2061,6 +2104,14 @@ packages:
resolution: {integrity: sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==}
engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/visitor-keys@7.15.0':
+ resolution: {integrity: sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+
+ '@typescript-eslint/visitor-keys@7.16.0':
+ resolution: {integrity: sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+
'@ungap/structured-clone@1.2.0':
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
@@ -3014,8 +3065,8 @@ packages:
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'}
- docusaurus-plugin-typedoc@1.0.1:
- resolution: {integrity: sha512-q3e/XHmnMNdP361/0SMBbCQyr7oUwbSs5QMhu1BkEUvM45oKG7i1qxcKKabOefFDVDW9cmbBISWxB8XZGJAVFg==}
+ docusaurus-plugin-typedoc@1.0.2:
+ resolution: {integrity: sha512-V7ZOjsFVByfVuhIIFSVJCWKBQzIHLxl3W+sLVDUqMo/fwPZrHFFD0WropfPdZ7MR7AA697+0BeUWMEpLyO/QPw==}
peerDependencies:
typedoc-plugin-markdown: '>=4.0.0'
@@ -5309,6 +5360,10 @@ packages:
resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
engines: {node: ^10 || ^12 || >=14}
+ postcss@8.4.39:
+ resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==}
+ engines: {node: ^10 || ^12 || >=14}
+
prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@@ -6242,8 +6297,8 @@ packages:
peerDependencies:
typedoc-plugin-markdown: '>=4.0.0'
- typedoc-plugin-markdown@4.1.0:
- resolution: {integrity: sha512-sUiEJVaa6+MOFShRy14j1OP/VXC5OLyHNecJ2nKeGuBy2M3YiMatSLoIiddFAqVptSuILJTZiJzCBIY6yzAVyg==}
+ typedoc-plugin-markdown@4.1.2:
+ resolution: {integrity: sha512-jZt8jmQLbmg9jmFQyfJrjLf6ljRwJ5fKMeqmFr0oPXmeX5ZRYYtCe6y/058vDESE/R+TwEvNua6SuG43UBbszw==}
engines: {node: '>= 18'}
peerDependencies:
typedoc: 0.26.x
@@ -6403,8 +6458,8 @@ packages:
vfile@6.0.1:
resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==}
- vite@5.3.1:
- resolution: {integrity: sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==}
+ vite@5.3.3:
+ resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -7791,9 +7846,9 @@ snapshots:
'@docusaurus/cssnano-preset@3.4.0':
dependencies:
- cssnano-preset-advanced: 6.1.2(postcss@8.4.38)
- postcss: 8.4.38
- postcss-sort-media-queries: 5.2.0(postcss@8.4.38)
+ cssnano-preset-advanced: 6.1.2(postcss@8.4.39)
+ postcss: 8.4.39
+ postcss-sort-media-queries: 5.2.0(postcss@8.4.39)
tslib: 2.6.3
'@docusaurus/logger@3.4.0':
@@ -8174,7 +8229,7 @@ snapshots:
infima: 0.2.0-alpha.43
lodash: 4.17.21
nprogress: 0.2.0
- postcss: 8.4.38
+ postcss: 8.4.39
prism-react-renderer: 2.3.1(react@18.3.1)
prismjs: 1.29.0
react: 18.3.1
@@ -8489,27 +8544,27 @@ snapshots:
'@jest/console@29.7.0':
dependencies:
'@jest/types': 29.6.3
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
chalk: 4.1.2
jest-message-util: 29.7.0
jest-util: 29.7.0
slash: 3.0.0
- '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3))':
+ '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))':
dependencies:
'@jest/console': 29.7.0
'@jest/reporters': 29.7.0
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
ansi-escapes: 4.3.2
chalk: 4.1.2
ci-info: 3.9.0
exit: 0.1.2
graceful-fs: 4.2.11
jest-changed-files: 29.7.0
- jest-config: 29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3))
+ jest-config: 29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
jest-haste-map: 29.7.0
jest-message-util: 29.7.0
jest-regex-util: 29.6.3
@@ -8534,7 +8589,7 @@ snapshots:
dependencies:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
jest-mock: 29.7.0
'@jest/expect-utils@29.7.0':
@@ -8552,7 +8607,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
jest-message-util: 29.7.0
jest-mock: 29.7.0
jest-util: 29.7.0
@@ -8574,7 +8629,7 @@ snapshots:
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@jridgewell/trace-mapping': 0.3.25
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
chalk: 4.1.2
collect-v8-coverage: 1.0.2
exit: 0.1.2
@@ -8644,7 +8699,7 @@ snapshots:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
'@types/yargs': 17.0.32
chalk: 4.1.2
@@ -8914,7 +8969,7 @@ snapshots:
dependencies:
defer-to-connect: 2.0.1
- '@testing-library/dom@10.2.0':
+ '@testing-library/dom@10.3.1':
dependencies:
'@babel/code-frame': 7.24.7
'@babel/runtime': 7.24.7
@@ -8925,7 +8980,7 @@ snapshots:
lz-string: 1.5.0
pretty-format: 27.5.1
- '@testing-library/jest-dom@6.4.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3)))':
+ '@testing-library/jest-dom@6.4.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))':
dependencies:
'@adobe/css-tools': 4.4.0
'@babel/runtime': 7.24.7
@@ -8938,21 +8993,21 @@ snapshots:
optionalDependencies:
'@jest/globals': 29.7.0
'@types/jest': 29.5.12
- jest: 29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3))
+ jest: 29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
- '@testing-library/react@16.0.0(@testing-library/dom@10.2.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@testing-library/react@16.0.0(@testing-library/dom@10.3.1)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.7
- '@testing-library/dom': 10.2.0
+ '@testing-library/dom': 10.3.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.3
'@types/react-dom': 18.3.0
- '@testing-library/user-event@14.5.2(@testing-library/dom@10.2.0)':
+ '@testing-library/user-event@14.5.2(@testing-library/dom@10.3.1)':
dependencies:
- '@testing-library/dom': 10.2.0
+ '@testing-library/dom': 10.3.1
'@tootallnate/once@2.0.0': {}
@@ -9061,7 +9116,7 @@ snapshots:
'@types/graceful-fs@4.1.9':
dependencies:
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
'@types/gtag.js@0.0.12': {}
@@ -9098,7 +9153,7 @@ snapshots:
'@types/jsdom@20.0.1':
dependencies:
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
'@types/tough-cookie': 4.0.5
parse5: 7.1.2
@@ -9122,6 +9177,10 @@ snapshots:
'@types/node@17.0.45': {}
+ '@types/node@20.14.10':
+ dependencies:
+ undici-types: 5.26.5
+
'@types/node@20.14.9':
dependencies:
undici-types: 5.26.5
@@ -9205,7 +9264,7 @@ snapshots:
'@types/webpack@3.8.27':
dependencies:
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
'@types/tapable': 0.2.9
'@types/uglify-js': 3.17.5
source-map: 0.6.1
@@ -9220,14 +9279,14 @@ snapshots:
dependencies:
'@types/yargs-parser': 21.0.3
- '@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)':
+ '@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)':
dependencies:
'@eslint-community/regexpp': 4.10.1
- '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.5.3)
- '@typescript-eslint/scope-manager': 7.14.1
- '@typescript-eslint/type-utils': 7.14.1(eslint@8.57.0)(typescript@5.5.3)
- '@typescript-eslint/utils': 7.14.1(eslint@8.57.0)(typescript@5.5.3)
- '@typescript-eslint/visitor-keys': 7.14.1
+ '@typescript-eslint/parser': 7.16.0(eslint@8.57.0)(typescript@5.5.3)
+ '@typescript-eslint/scope-manager': 7.15.0
+ '@typescript-eslint/type-utils': 7.15.0(eslint@8.57.0)(typescript@5.5.3)
+ '@typescript-eslint/utils': 7.15.0(eslint@8.57.0)(typescript@5.5.3)
+ '@typescript-eslint/visitor-keys': 7.15.0
eslint: 8.57.0
graphemer: 1.4.0
ignore: 5.3.1
@@ -9238,12 +9297,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3)':
+ '@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3)':
dependencies:
- '@typescript-eslint/scope-manager': 7.14.1
- '@typescript-eslint/types': 7.14.1
- '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.3)
- '@typescript-eslint/visitor-keys': 7.14.1
+ '@typescript-eslint/scope-manager': 7.16.0
+ '@typescript-eslint/types': 7.16.0
+ '@typescript-eslint/typescript-estree': 7.16.0(typescript@5.5.3)
+ '@typescript-eslint/visitor-keys': 7.16.0
debug: 4.3.5
eslint: 8.57.0
optionalDependencies:
@@ -9261,10 +9320,20 @@ snapshots:
'@typescript-eslint/types': 7.14.1
'@typescript-eslint/visitor-keys': 7.14.1
- '@typescript-eslint/type-utils@7.14.1(eslint@8.57.0)(typescript@5.5.3)':
+ '@typescript-eslint/scope-manager@7.15.0':
dependencies:
- '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.3)
- '@typescript-eslint/utils': 7.14.1(eslint@8.57.0)(typescript@5.5.3)
+ '@typescript-eslint/types': 7.15.0
+ '@typescript-eslint/visitor-keys': 7.15.0
+
+ '@typescript-eslint/scope-manager@7.16.0':
+ dependencies:
+ '@typescript-eslint/types': 7.16.0
+ '@typescript-eslint/visitor-keys': 7.16.0
+
+ '@typescript-eslint/type-utils@7.15.0(eslint@8.57.0)(typescript@5.5.3)':
+ dependencies:
+ '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
+ '@typescript-eslint/utils': 7.15.0(eslint@8.57.0)(typescript@5.5.3)
debug: 4.3.5
eslint: 8.57.0
ts-api-utils: 1.3.0(typescript@5.5.3)
@@ -9277,6 +9346,10 @@ snapshots:
'@typescript-eslint/types@7.14.1': {}
+ '@typescript-eslint/types@7.15.0': {}
+
+ '@typescript-eslint/types@7.16.0': {}
+
'@typescript-eslint/typescript-estree@5.62.0(typescript@5.5.3)':
dependencies:
'@typescript-eslint/types': 5.62.0
@@ -9306,6 +9379,36 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@typescript-eslint/typescript-estree@7.15.0(typescript@5.5.3)':
+ dependencies:
+ '@typescript-eslint/types': 7.15.0
+ '@typescript-eslint/visitor-keys': 7.15.0
+ debug: 4.3.5
+ globby: 11.1.0
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.6.2
+ ts-api-utils: 1.3.0(typescript@5.5.3)
+ optionalDependencies:
+ typescript: 5.5.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/typescript-estree@7.16.0(typescript@5.5.3)':
+ dependencies:
+ '@typescript-eslint/types': 7.16.0
+ '@typescript-eslint/visitor-keys': 7.16.0
+ debug: 4.3.5
+ globby: 11.1.0
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.6.2
+ ts-api-utils: 1.3.0(typescript@5.5.3)
+ optionalDependencies:
+ typescript: 5.5.3
+ transitivePeerDependencies:
+ - supports-color
+
'@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.5.3)':
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
@@ -9332,6 +9435,17 @@ snapshots:
- supports-color
- typescript
+ '@typescript-eslint/utils@7.15.0(eslint@8.57.0)(typescript@5.5.3)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
+ '@typescript-eslint/scope-manager': 7.15.0
+ '@typescript-eslint/types': 7.15.0
+ '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
+ eslint: 8.57.0
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
'@typescript-eslint/visitor-keys@5.62.0':
dependencies:
'@typescript-eslint/types': 5.62.0
@@ -9342,16 +9456,26 @@ snapshots:
'@typescript-eslint/types': 7.14.1
eslint-visitor-keys: 3.4.3
+ '@typescript-eslint/visitor-keys@7.15.0':
+ dependencies:
+ '@typescript-eslint/types': 7.15.0
+ eslint-visitor-keys: 3.4.3
+
+ '@typescript-eslint/visitor-keys@7.16.0':
+ dependencies:
+ '@typescript-eslint/types': 7.16.0
+ eslint-visitor-keys: 3.4.3
+
'@ungap/structured-clone@1.2.0': {}
- '@vitejs/plugin-react@4.3.1(vite@5.3.1(@types/node@20.14.9)(terser@5.31.1))':
+ '@vitejs/plugin-react@4.3.1(vite@5.3.3(@types/node@20.14.10)(terser@5.31.1))':
dependencies:
'@babel/core': 7.24.7
'@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.7)
'@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.7)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
- vite: 5.3.1(@types/node@20.14.9)(terser@5.31.1)
+ vite: 5.3.3(@types/node@20.14.10)(terser@5.31.1)
transitivePeerDependencies:
- supports-color
@@ -9663,6 +9787,16 @@ snapshots:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ autoprefixer@10.4.19(postcss@8.4.39):
+ dependencies:
+ browserslist: 4.23.1
+ caniuse-lite: 1.0.30001636
+ fraction.js: 4.3.7
+ normalize-range: 0.1.2
+ picocolors: 1.0.1
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.0.0
@@ -10115,13 +10249,13 @@ snapshots:
optionalDependencies:
typescript: 5.5.3
- create-jest@29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3)):
+ create-jest@29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)):
dependencies:
'@jest/types': 29.6.3
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
- jest-config: 29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3))
+ jest-config: 29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
jest-util: 29.7.0
prompts: 2.4.2
transitivePeerDependencies:
@@ -10146,14 +10280,18 @@ snapshots:
dependencies:
postcss: 8.4.38
+ css-declaration-sorter@7.2.0(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+
css-loader@6.11.0(webpack@5.92.1):
dependencies:
- icss-utils: 5.1.0(postcss@8.4.38)
- postcss: 8.4.38
- postcss-modules-extract-imports: 3.1.0(postcss@8.4.38)
- postcss-modules-local-by-default: 4.0.5(postcss@8.4.38)
- postcss-modules-scope: 3.2.0(postcss@8.4.38)
- postcss-modules-values: 4.0.0(postcss@8.4.38)
+ icss-utils: 5.1.0(postcss@8.4.39)
+ postcss: 8.4.39
+ postcss-modules-extract-imports: 3.1.0(postcss@8.4.39)
+ postcss-modules-local-by-default: 4.0.5(postcss@8.4.39)
+ postcss-modules-scope: 3.2.0(postcss@8.4.39)
+ postcss-modules-values: 4.0.0(postcss@8.4.39)
postcss-value-parser: 4.2.0
semver: 7.6.2
optionalDependencies:
@@ -10162,9 +10300,9 @@ snapshots:
css-minimizer-webpack-plugin@5.0.1(clean-css@5.3.3)(webpack@5.92.1):
dependencies:
'@jridgewell/trace-mapping': 0.3.25
- cssnano: 6.1.2(postcss@8.4.38)
+ cssnano: 6.1.2(postcss@8.4.39)
jest-worker: 29.7.0
- postcss: 8.4.38
+ postcss: 8.4.39
schema-utils: 4.2.0
serialize-javascript: 6.0.2
webpack: 5.92.1
@@ -10210,16 +10348,16 @@ snapshots:
cssesc@3.0.0: {}
- cssnano-preset-advanced@6.1.2(postcss@8.4.38):
+ cssnano-preset-advanced@6.1.2(postcss@8.4.39):
dependencies:
- autoprefixer: 10.4.19(postcss@8.4.38)
+ autoprefixer: 10.4.19(postcss@8.4.39)
browserslist: 4.23.1
- cssnano-preset-default: 6.1.2(postcss@8.4.38)
- postcss: 8.4.38
- postcss-discard-unused: 6.0.5(postcss@8.4.38)
- postcss-merge-idents: 6.0.3(postcss@8.4.38)
- postcss-reduce-idents: 6.0.3(postcss@8.4.38)
- postcss-zindex: 6.0.2(postcss@8.4.38)
+ cssnano-preset-default: 6.1.2(postcss@8.4.39)
+ postcss: 8.4.39
+ postcss-discard-unused: 6.0.5(postcss@8.4.39)
+ postcss-merge-idents: 6.0.3(postcss@8.4.39)
+ postcss-reduce-idents: 6.0.3(postcss@8.4.39)
+ postcss-zindex: 6.0.2(postcss@8.4.39)
cssnano-preset-default@6.1.2(postcss@8.4.38):
dependencies:
@@ -10255,16 +10393,60 @@ snapshots:
postcss-svgo: 6.0.3(postcss@8.4.38)
postcss-unique-selectors: 6.0.4(postcss@8.4.38)
+ cssnano-preset-default@6.1.2(postcss@8.4.39):
+ dependencies:
+ browserslist: 4.23.1
+ css-declaration-sorter: 7.2.0(postcss@8.4.39)
+ cssnano-utils: 4.0.2(postcss@8.4.39)
+ postcss: 8.4.39
+ postcss-calc: 9.0.1(postcss@8.4.39)
+ postcss-colormin: 6.1.0(postcss@8.4.39)
+ postcss-convert-values: 6.1.0(postcss@8.4.39)
+ postcss-discard-comments: 6.0.2(postcss@8.4.39)
+ postcss-discard-duplicates: 6.0.3(postcss@8.4.39)
+ postcss-discard-empty: 6.0.3(postcss@8.4.39)
+ postcss-discard-overridden: 6.0.2(postcss@8.4.39)
+ postcss-merge-longhand: 6.0.5(postcss@8.4.39)
+ postcss-merge-rules: 6.1.1(postcss@8.4.39)
+ postcss-minify-font-values: 6.1.0(postcss@8.4.39)
+ postcss-minify-gradients: 6.0.3(postcss@8.4.39)
+ postcss-minify-params: 6.1.0(postcss@8.4.39)
+ postcss-minify-selectors: 6.0.4(postcss@8.4.39)
+ postcss-normalize-charset: 6.0.2(postcss@8.4.39)
+ postcss-normalize-display-values: 6.0.2(postcss@8.4.39)
+ postcss-normalize-positions: 6.0.2(postcss@8.4.39)
+ postcss-normalize-repeat-style: 6.0.2(postcss@8.4.39)
+ postcss-normalize-string: 6.0.2(postcss@8.4.39)
+ postcss-normalize-timing-functions: 6.0.2(postcss@8.4.39)
+ postcss-normalize-unicode: 6.1.0(postcss@8.4.39)
+ postcss-normalize-url: 6.0.2(postcss@8.4.39)
+ postcss-normalize-whitespace: 6.0.2(postcss@8.4.39)
+ postcss-ordered-values: 6.0.2(postcss@8.4.39)
+ postcss-reduce-initial: 6.1.0(postcss@8.4.39)
+ postcss-reduce-transforms: 6.0.2(postcss@8.4.39)
+ postcss-svgo: 6.0.3(postcss@8.4.39)
+ postcss-unique-selectors: 6.0.4(postcss@8.4.39)
+
cssnano-utils@4.0.2(postcss@8.4.38):
dependencies:
postcss: 8.4.38
+ cssnano-utils@4.0.2(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+
cssnano@6.1.2(postcss@8.4.38):
dependencies:
cssnano-preset-default: 6.1.2(postcss@8.4.38)
lilconfig: 3.1.2
postcss: 8.4.38
+ cssnano@6.1.2(postcss@8.4.39):
+ dependencies:
+ cssnano-preset-default: 6.1.2(postcss@8.4.39)
+ lilconfig: 3.1.2
+ postcss: 8.4.39
+
csso@5.0.5:
dependencies:
css-tree: 2.2.1
@@ -10428,9 +10610,9 @@ snapshots:
dependencies:
esutils: 2.0.3
- docusaurus-plugin-typedoc@1.0.1(typedoc-plugin-markdown@4.1.0(typedoc@0.26.3(typescript@5.5.3))):
+ docusaurus-plugin-typedoc@1.0.2(typedoc-plugin-markdown@4.1.2(typedoc@0.26.3(typescript@5.5.3))):
dependencies:
- typedoc-plugin-markdown: 4.1.0(typedoc@0.26.3(typescript@5.5.3))
+ typedoc-plugin-markdown: 4.1.2(typedoc@0.26.3(typescript@5.5.3))
dom-accessibility-api@0.5.16: {}
@@ -10676,13 +10858,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0):
+ eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0):
dependencies:
debug: 4.3.5
enhanced-resolve: 5.17.0
eslint: 8.57.0
- eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
- eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+ eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
+ eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
fast-glob: 3.3.2
get-tsconfig: 4.7.5
is-core-module: 2.14.0
@@ -10693,18 +10875,18 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
- eslint-module-utils@2.8.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
+ eslint-module-utils@2.8.1(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
dependencies:
debug: 3.2.7
optionalDependencies:
- '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.5.3)
+ '@typescript-eslint/parser': 7.16.0(eslint@8.57.0)(typescript@5.5.3)
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+ eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0)
transitivePeerDependencies:
- supports-color
- eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
+ eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
dependencies:
array-includes: 3.1.8
array.prototype.findlastindex: 1.2.5
@@ -10714,7 +10896,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
+ eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
hasown: 2.0.2
is-core-module: 2.14.0
is-glob: 4.0.3
@@ -10725,19 +10907,19 @@ snapshots:
semver: 6.3.1
tsconfig-paths: 3.15.0
optionalDependencies:
- '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.5.3)
+ '@typescript-eslint/parser': 7.16.0(eslint@8.57.0)(typescript@5.5.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
- eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3)))(typescript@5.5.3):
+ eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(jest@29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))(typescript@5.5.3):
dependencies:
'@typescript-eslint/utils': 7.14.1(eslint@8.57.0)(typescript@5.5.3)
eslint: 8.57.0
optionalDependencies:
- '@typescript-eslint/eslint-plugin': 7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)
- jest: 29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3))
+ '@typescript-eslint/eslint-plugin': 7.15.0(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)
+ jest: 29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
transitivePeerDependencies:
- supports-color
- typescript
@@ -11552,9 +11734,9 @@ snapshots:
dependencies:
safer-buffer: 2.1.2
- icss-utils@5.1.0(postcss@8.4.38):
+ icss-utils@5.1.0(postcss@8.4.39):
dependencies:
- postcss: 8.4.38
+ postcss: 8.4.39
identity-obj-proxy@3.0.0:
dependencies:
@@ -11854,7 +12036,7 @@ snapshots:
'@jest/expect': 29.7.0
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
chalk: 4.1.2
co: 4.6.0
dedent: 1.5.3
@@ -11874,16 +12056,16 @@ snapshots:
- babel-plugin-macros
- supports-color
- jest-cli@29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3)):
+ jest-cli@29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)):
dependencies:
- '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3))
+ '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
chalk: 4.1.2
- create-jest: 29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3))
+ create-jest: 29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
exit: 0.1.2
import-local: 3.1.0
- jest-config: 29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3))
+ jest-config: 29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
jest-util: 29.7.0
jest-validate: 29.7.0
yargs: 17.7.2
@@ -11893,7 +12075,7 @@ snapshots:
- supports-color
- ts-node
- jest-config@29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3)):
+ jest-config@29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)):
dependencies:
'@babel/core': 7.24.7
'@jest/test-sequencer': 29.7.0
@@ -11918,8 +12100,8 @@ snapshots:
slash: 3.0.0
strip-json-comments: 3.1.1
optionalDependencies:
- '@types/node': 20.14.9
- ts-node: 10.9.2(@types/node@20.14.9)(typescript@5.5.3)
+ '@types/node': 20.14.10
+ ts-node: 10.9.2(@types/node@20.14.10)(typescript@5.5.3)
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@@ -11949,7 +12131,7 @@ snapshots:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/jsdom': 20.0.1
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
jest-mock: 29.7.0
jest-util: 29.7.0
jsdom: 20.0.3
@@ -11963,7 +12145,7 @@ snapshots:
'@jest/environment': 29.7.0
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
jest-mock: 29.7.0
jest-util: 29.7.0
@@ -11973,7 +12155,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@types/graceful-fs': 4.1.9
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@@ -12012,7 +12194,7 @@ snapshots:
jest-mock@29.7.0:
dependencies:
'@jest/types': 29.6.3
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
jest-util: 29.7.0
jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
@@ -12047,7 +12229,7 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
chalk: 4.1.2
emittery: 0.13.1
graceful-fs: 4.2.11
@@ -12075,7 +12257,7 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
chalk: 4.1.2
cjs-module-lexer: 1.3.1
collect-v8-coverage: 1.0.2
@@ -12121,7 +12303,7 @@ snapshots:
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@@ -12140,7 +12322,7 @@ snapshots:
dependencies:
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.13.1
@@ -12160,12 +12342,12 @@ snapshots:
merge-stream: 2.0.0
supports-color: 8.1.1
- jest@29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3)):
+ jest@29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)):
dependencies:
- '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3))
+ '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
'@jest/types': 29.6.3
import-local: 3.1.0
- jest-cli: 29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3))
+ jest-cli: 29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
@@ -13205,6 +13387,12 @@ snapshots:
postcss-selector-parser: 6.1.0
postcss-value-parser: 4.2.0
+ postcss-calc@9.0.1(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-selector-parser: 6.1.0
+ postcss-value-parser: 4.2.0
+
postcss-colormin@6.1.0(postcss@8.4.38):
dependencies:
browserslist: 4.23.1
@@ -13213,31 +13401,61 @@ snapshots:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-colormin@6.1.0(postcss@8.4.39):
+ dependencies:
+ browserslist: 4.23.1
+ caniuse-api: 3.0.0
+ colord: 2.9.3
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-convert-values@6.1.0(postcss@8.4.38):
dependencies:
browserslist: 4.23.1
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-convert-values@6.1.0(postcss@8.4.39):
+ dependencies:
+ browserslist: 4.23.1
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-discard-comments@6.0.2(postcss@8.4.38):
dependencies:
postcss: 8.4.38
+ postcss-discard-comments@6.0.2(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+
postcss-discard-duplicates@6.0.3(postcss@8.4.38):
dependencies:
postcss: 8.4.38
+ postcss-discard-duplicates@6.0.3(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+
postcss-discard-empty@6.0.3(postcss@8.4.38):
dependencies:
postcss: 8.4.38
+ postcss-discard-empty@6.0.3(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+
postcss-discard-overridden@6.0.2(postcss@8.4.38):
dependencies:
postcss: 8.4.38
- postcss-discard-unused@6.0.5(postcss@8.4.38):
+ postcss-discard-overridden@6.0.2(postcss@8.4.39):
dependencies:
- postcss: 8.4.38
+ postcss: 8.4.39
+
+ postcss-discard-unused@6.0.5(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
postcss-selector-parser: 6.1.0
postcss-loader@7.3.4(postcss@8.4.38)(typescript@5.5.3)(webpack@5.92.1):
@@ -13250,10 +13468,10 @@ snapshots:
transitivePeerDependencies:
- typescript
- postcss-merge-idents@6.0.3(postcss@8.4.38):
+ postcss-merge-idents@6.0.3(postcss@8.4.39):
dependencies:
- cssnano-utils: 4.0.2(postcss@8.4.38)
- postcss: 8.4.38
+ cssnano-utils: 4.0.2(postcss@8.4.39)
+ postcss: 8.4.39
postcss-value-parser: 4.2.0
postcss-merge-longhand@6.0.5(postcss@8.4.38):
@@ -13262,6 +13480,12 @@ snapshots:
postcss-value-parser: 4.2.0
stylehacks: 6.1.1(postcss@8.4.38)
+ postcss-merge-longhand@6.0.5(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+ stylehacks: 6.1.1(postcss@8.4.39)
+
postcss-merge-rules@6.1.1(postcss@8.4.38):
dependencies:
browserslist: 4.23.1
@@ -13270,11 +13494,24 @@ snapshots:
postcss: 8.4.38
postcss-selector-parser: 6.1.0
+ postcss-merge-rules@6.1.1(postcss@8.4.39):
+ dependencies:
+ browserslist: 4.23.1
+ caniuse-api: 3.0.0
+ cssnano-utils: 4.0.2(postcss@8.4.39)
+ postcss: 8.4.39
+ postcss-selector-parser: 6.1.0
+
postcss-minify-font-values@6.1.0(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-minify-font-values@6.1.0(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-minify-gradients@6.0.3(postcss@8.4.38):
dependencies:
colord: 2.9.3
@@ -13282,6 +13519,13 @@ snapshots:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-minify-gradients@6.0.3(postcss@8.4.39):
+ dependencies:
+ colord: 2.9.3
+ cssnano-utils: 4.0.2(postcss@8.4.39)
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-minify-params@6.1.0(postcss@8.4.38):
dependencies:
browserslist: 4.23.1
@@ -13289,86 +13533,149 @@ snapshots:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-minify-params@6.1.0(postcss@8.4.39):
+ dependencies:
+ browserslist: 4.23.1
+ cssnano-utils: 4.0.2(postcss@8.4.39)
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-minify-selectors@6.0.4(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-selector-parser: 6.1.0
- postcss-modules-extract-imports@3.1.0(postcss@8.4.38):
+ postcss-minify-selectors@6.0.4(postcss@8.4.39):
dependencies:
- postcss: 8.4.38
+ postcss: 8.4.39
+ postcss-selector-parser: 6.1.0
- postcss-modules-local-by-default@4.0.5(postcss@8.4.38):
+ postcss-modules-extract-imports@3.1.0(postcss@8.4.39):
dependencies:
- icss-utils: 5.1.0(postcss@8.4.38)
- postcss: 8.4.38
+ postcss: 8.4.39
+
+ postcss-modules-local-by-default@4.0.5(postcss@8.4.39):
+ dependencies:
+ icss-utils: 5.1.0(postcss@8.4.39)
+ postcss: 8.4.39
postcss-selector-parser: 6.1.0
postcss-value-parser: 4.2.0
- postcss-modules-scope@3.2.0(postcss@8.4.38):
+ postcss-modules-scope@3.2.0(postcss@8.4.39):
dependencies:
- postcss: 8.4.38
+ postcss: 8.4.39
postcss-selector-parser: 6.1.0
- postcss-modules-values@4.0.0(postcss@8.4.38):
+ postcss-modules-values@4.0.0(postcss@8.4.39):
dependencies:
- icss-utils: 5.1.0(postcss@8.4.38)
- postcss: 8.4.38
+ icss-utils: 5.1.0(postcss@8.4.39)
+ postcss: 8.4.39
postcss-normalize-charset@6.0.2(postcss@8.4.38):
dependencies:
postcss: 8.4.38
+ postcss-normalize-charset@6.0.2(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+
postcss-normalize-display-values@6.0.2(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-normalize-display-values@6.0.2(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-normalize-positions@6.0.2(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-normalize-positions@6.0.2(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-normalize-repeat-style@6.0.2(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-normalize-repeat-style@6.0.2(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-normalize-string@6.0.2(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-normalize-string@6.0.2(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-normalize-timing-functions@6.0.2(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-normalize-timing-functions@6.0.2(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-normalize-unicode@6.1.0(postcss@8.4.38):
dependencies:
browserslist: 4.23.1
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-normalize-unicode@6.1.0(postcss@8.4.39):
+ dependencies:
+ browserslist: 4.23.1
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-normalize-url@6.0.2(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-normalize-url@6.0.2(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-normalize-whitespace@6.0.2(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-normalize-whitespace@6.0.2(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-ordered-values@6.0.2(postcss@8.4.38):
dependencies:
cssnano-utils: 4.0.2(postcss@8.4.38)
postcss: 8.4.38
postcss-value-parser: 4.2.0
- postcss-reduce-idents@6.0.3(postcss@8.4.38):
+ postcss-ordered-values@6.0.2(postcss@8.4.39):
dependencies:
- postcss: 8.4.38
+ cssnano-utils: 4.0.2(postcss@8.4.39)
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
+ postcss-reduce-idents@6.0.3(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
postcss-value-parser: 4.2.0
postcss-reduce-initial@6.1.0(postcss@8.4.38):
@@ -13377,19 +13684,30 @@ snapshots:
caniuse-api: 3.0.0
postcss: 8.4.38
+ postcss-reduce-initial@6.1.0(postcss@8.4.39):
+ dependencies:
+ browserslist: 4.23.1
+ caniuse-api: 3.0.0
+ postcss: 8.4.39
+
postcss-reduce-transforms@6.0.2(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-value-parser: 4.2.0
+ postcss-reduce-transforms@6.0.2(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+
postcss-selector-parser@6.1.0:
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
- postcss-sort-media-queries@5.2.0(postcss@8.4.38):
+ postcss-sort-media-queries@5.2.0(postcss@8.4.39):
dependencies:
- postcss: 8.4.38
+ postcss: 8.4.39
sort-css-media-queries: 2.2.0
postcss-svgo@6.0.3(postcss@8.4.38):
@@ -13398,16 +13716,27 @@ snapshots:
postcss-value-parser: 4.2.0
svgo: 3.3.2
+ postcss-svgo@6.0.3(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-value-parser: 4.2.0
+ svgo: 3.3.2
+
postcss-unique-selectors@6.0.4(postcss@8.4.38):
dependencies:
postcss: 8.4.38
postcss-selector-parser: 6.1.0
+ postcss-unique-selectors@6.0.4(postcss@8.4.39):
+ dependencies:
+ postcss: 8.4.39
+ postcss-selector-parser: 6.1.0
+
postcss-value-parser@4.2.0: {}
- postcss-zindex@6.0.2(postcss@8.4.38):
+ postcss-zindex@6.0.2(postcss@8.4.39):
dependencies:
- postcss: 8.4.38
+ postcss: 8.4.39
postcss@8.4.38:
dependencies:
@@ -13415,6 +13744,12 @@ snapshots:
picocolors: 1.0.1
source-map-js: 1.2.0
+ postcss@8.4.39:
+ dependencies:
+ nanoid: 3.3.7
+ picocolors: 1.0.1
+ source-map-js: 1.2.0
+
prelude-ls@1.2.1: {}
prettier-linter-helpers@1.0.0:
@@ -13909,7 +14244,7 @@ snapshots:
dependencies:
escalade: 3.1.2
picocolors: 1.0.1
- postcss: 8.4.38
+ postcss: 8.4.39
strip-json-comments: 3.1.1
run-parallel@1.2.0:
@@ -14302,6 +14637,12 @@ snapshots:
postcss: 8.4.38
postcss-selector-parser: 6.1.0
+ stylehacks@6.1.1(postcss@8.4.39):
+ dependencies:
+ browserslist: 4.23.1
+ postcss: 8.4.39
+ postcss-selector-parser: 6.1.0
+
supports-color@5.5.0:
dependencies:
has-flag: 3.0.0
@@ -14404,11 +14745,11 @@ snapshots:
dependencies:
typescript: 5.5.3
- ts-jest@29.1.5(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3)))(typescript@5.5.3):
+ ts-jest@29.1.5(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))(typescript@5.5.3):
dependencies:
bs-logger: 0.2.6
fast-json-stable-stringify: 2.1.0
- jest: 29.7.0(@types/node@20.14.9)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3))
+ jest: 29.7.0(@types/node@20.14.10)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
jest-util: 29.7.0
json5: 2.2.3
lodash.memoize: 4.1.2
@@ -14422,14 +14763,14 @@ snapshots:
'@jest/types': 29.6.3
babel-jest: 29.7.0(@babel/core@7.24.7)
- ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.3):
+ ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
acorn: 8.12.0
acorn-walk: 8.3.3
arg: 4.1.3
@@ -14511,12 +14852,12 @@ snapshots:
dependencies:
is-typedarray: 1.0.0
- typedoc-plugin-frontmatter@1.0.0(typedoc-plugin-markdown@4.1.0(typedoc@0.26.3(typescript@5.5.3))):
+ typedoc-plugin-frontmatter@1.0.0(typedoc-plugin-markdown@4.1.2(typedoc@0.26.3(typescript@5.5.3))):
dependencies:
- typedoc-plugin-markdown: 4.1.0(typedoc@0.26.3(typescript@5.5.3))
+ typedoc-plugin-markdown: 4.1.2(typedoc@0.26.3(typescript@5.5.3))
yaml: 2.4.5
- typedoc-plugin-markdown@4.1.0(typedoc@0.26.3(typescript@5.5.3)):
+ typedoc-plugin-markdown@4.1.2(typedoc@0.26.3(typescript@5.5.3)):
dependencies:
typedoc: 0.26.3(typescript@5.5.3)
@@ -14694,13 +15035,13 @@ snapshots:
unist-util-stringify-position: 4.0.0
vfile-message: 4.0.2
- vite@5.3.1(@types/node@20.14.9)(terser@5.31.1):
+ vite@5.3.3(@types/node@20.14.10)(terser@5.31.1):
dependencies:
esbuild: 0.21.5
- postcss: 8.4.38
+ postcss: 8.4.39
rollup: 4.18.0
optionalDependencies:
- '@types/node': 20.14.9
+ '@types/node': 20.14.10
fsevents: 2.3.3
terser: 5.31.1
diff --git a/scripts/build-css.sh b/scripts/build-css.sh
new file mode 100755
index 0000000000..9cf40f8858
--- /dev/null
+++ b/scripts/build-css.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Check if the correct number of arguments are provided
+if [ "$#" -ne 2 ]; then
+ echo "Usage: $0 input_css_file output_css_file"
+ exit 1
+fi
+
+INPUT_FILE=$1
+OUTPUT_FILE=$2
+
+# Check if the input file exists
+if [ ! -f "$INPUT_FILE" ]; then
+ echo "Input file not found!"
+ exit 1
+fi
+
+# Read the input file, remove all occurrences of '.rdp-', and write to the output file
+sed 's/\.rdp-/\./g' "$INPUT_FILE" > "$OUTPUT_FILE"
diff --git a/src/DayPicker.test.tsx b/src/DayPicker.test.tsx
index 4b55b1e35f..df83a9868d 100644
--- a/src/DayPicker.test.tsx
+++ b/src/DayPicker.test.tsx
@@ -1,10 +1,110 @@
import React from "react";
import { render, screen } from "@testing-library/react";
+import { startOfMonth } from "date-fns";
+
+import {
+ activeElement,
+ dateButton,
+ grid,
+ nav,
+ previousButton
+} from "@/test/elements";
+import { user } from "@/test/user";
import { DayPicker } from "./DayPicker";
+import { MonthProps } from "./components/Month";
+import { MonthsProps } from "./components/Months";
+
+const testId = "test";
+const dayPicker = () => screen.getByTestId(testId);
test("should render a date picker component", () => {
- render( );
- expect(screen.getByTestId("test")).toBeInTheDocument();
+ render( );
+ expect(dayPicker()).toBeInTheDocument();
+});
+
+it("should render the navigation and month grids", () => {
+ render( );
+
+ expect(nav()).toBeInTheDocument();
+ expect(grid()).toBeInTheDocument();
+});
+
+it("should apply classnames and style according to props", () => {
+ render(
+
+ );
+
+ expect(dayPicker()).toHaveClass("rdp-root");
+ expect(dayPicker()).toHaveClass("custom-class");
+ expect(dayPicker()).toHaveStyle({ color: "red" });
+});
+
+it("should use custom components", () => {
+ render(
+ Custom Navigation
,
+ Month: (props: MonthProps) => Custom Month
,
+ Months: (props: MonthsProps) => (
+
+ Custom Months
{props.children}
+
+ ),
+ Footer: () => Custom Footer
+ }}
+ footer="Footer"
+ />
+ );
+
+ expect(dayPicker()).toHaveTextContent("Custom Navigation");
+ expect(dayPicker()).toHaveTextContent("Custom Months");
+ expect(dayPicker()).toHaveTextContent("Custom Month");
+ expect(dayPicker()).toHaveTextContent("Custom Footer");
+});
+
+describe("when the date picker is focused", () => {
+ test("focus the previous button", async () => {
+ render( );
+ await user.tab();
+ expect(activeElement()).toBe(previousButton());
+ });
+
+ test("on RTL, focus the previous button", async () => {
+ render( );
+ await user.tab();
+ expect(activeElement()).toBe(previousButton());
+ });
+});
+
+describe("when the grid is focused", () => {
+ const today = new Date();
+
+ beforeAll(() => jest.setSystemTime(today));
+ afterAll(() => jest.useRealTimers());
+
+ test("should focus the today's date", async () => {
+ render( );
+ await user.tab();
+ await user.tab();
+ await user.tab();
+ expect(activeElement()).toBe(dateButton(today));
+ });
+ describe("when the today’s date is disabled", () => {
+ test("should focus the first day of the month", async () => {
+ render( );
+ await user.tab();
+ await user.tab();
+ await user.tab();
+ expect(activeElement()).toBe(dateButton(startOfMonth(today)));
+ });
+ });
});
diff --git a/src/DayPicker.tsx b/src/DayPicker.tsx
index fd6ecc8d03..bf88b01515 100644
--- a/src/DayPicker.tsx
+++ b/src/DayPicker.tsx
@@ -1,8 +1,32 @@
-import React from "react";
+import React, { useCallback, useMemo } from "react";
+import type {
+ ChangeEventHandler,
+ MouseEvent,
+ FocusEvent,
+ KeyboardEvent
+} from "react";
-import { Calendar } from "./components/Calendar.js";
-import { ContextProviders } from "./contexts/providers.js";
-import type { DayPickerProps } from "./types/index.js";
+import { UI, DayFlag, SelectionState } from "./UI.js";
+import type { CalendarDay } from "./classes/CalendarDay.js";
+import { getClassNamesForModifiers } from "./helpers/getClassNamesForModifiers.js";
+import { getComponents } from "./helpers/getComponents.js";
+import { getDataAttributes } from "./helpers/getDataAttributes.js";
+import { getDateLib } from "./helpers/getDateLib.js";
+import { getDefaultClassNames } from "./helpers/getDefaultClassNames.js";
+import { getDropdownMonths } from "./helpers/getDropdownMonths.js";
+import { getDropdownYears } from "./helpers/getDropdownYears.js";
+import { getFormatters } from "./helpers/getFormatters.js";
+import { getStyleForModifiers } from "./helpers/getStyleForModifiers.js";
+import { getWeekdays } from "./helpers/getWeekdays.js";
+import * as defaultLabels from "./labels/index.js";
+import { FormatOptions, LabelOptions } from "./lib/dateLib.js";
+import { UseRange } from "./selection/useRange.js";
+import type { DayPickerProps, Modifiers } from "./types/index.js";
+import { useCalendar } from "./useCalendar.js";
+import { dayPickerContext } from "./useDayPicker.js";
+import { useFocus } from "./useFocus.js";
+import { useModifiers } from "./useModifiers.js";
+import { useSelection } from "./useSelection.js";
/**
* Render the date picker calendar.
@@ -11,9 +35,608 @@ import type { DayPickerProps } from "./types/index.js";
* @see http://daypicker.dev
*/
export function DayPicker(props: DayPickerProps) {
+ const reactId = React.useId();
+ const id = props.id ?? reactId;
+
+ const { components, formatters, labels, dateLib, classNames } = useMemo(
+ () => ({
+ dateLib: getDateLib(props.dateLib),
+ components: getComponents(props.components),
+ formatters: getFormatters(props.formatters),
+ labels: { ...defaultLabels, ...props.labels },
+ classNames: { ...getDefaultClassNames(), ...props.classNames }
+ }),
+ [
+ props.classNames,
+ props.components,
+ props.dateLib,
+ props.formatters,
+ props.labels
+ ]
+ );
+
+ const numberOfMonths = props.numberOfMonths ?? 1;
+
+ const calendar = useCalendar(props, dateLib);
+
+ const modifiers = useModifiers(props, calendar, dateLib);
+ const selection = useSelection(props, dateLib);
+ const focus = useFocus(props, calendar, modifiers, selection, dateLib);
+
+ const { previousMonth, nextMonth, goToPreviousMonth, goToNextMonth } =
+ calendar;
+ const { isSelected, handleSelect } = selection;
+ const {
+ isFocusTarget,
+ focused: focusedDay,
+ setFocused,
+ blur,
+ focusDayBefore,
+ focusDayAfter,
+ focusWeekBefore,
+ focusWeekAfter,
+ focusMonthBefore,
+ focusMonthAfter,
+ focusYearBefore,
+ focusYearAfter,
+ focusStartOfWeek,
+ focusEndOfWeek
+ } = focus;
+
+ const {
+ captionLayout,
+ dir,
+ locale,
+ ISOWeek,
+ mode,
+ modifiersClassNames,
+ modifiersStyles,
+ onDayBlur,
+ onDayClick,
+ onDayFocus,
+ onDayKeyDown,
+ onPrevClick,
+ onNextClick,
+ showWeekNumber,
+ styles,
+ weekStartsOn,
+ firstWeekContainsDate,
+ useAdditionalWeekYearTokens,
+ useAdditionalDayOfYearTokens
+ } = props;
+
+ const {
+ formatCaption,
+ formatDay,
+ formatMonthDropdown,
+ formatWeekNumber,
+ formatWeekNumberHeader,
+ formatWeekdayName,
+ formatYearDropdown
+ } = formatters;
+
+ const {
+ labelDayButton,
+ labelGridcell,
+ labelGrid,
+ labelMonthDropdown,
+ labelNav,
+ labelNext,
+ labelPrevious,
+ labelWeekday,
+ labelWeekNumber,
+ labelWeekNumberHeader,
+ labelYearDropdown
+ } = labels;
+
+ const weekdays = useMemo(
+ () => getWeekdays(locale, weekStartsOn, ISOWeek, dateLib),
+ [ISOWeek, dateLib, locale, weekStartsOn]
+ );
+
+ const isInteractive = mode !== undefined || onDayClick !== undefined;
+
+ const handlePreviousClick = useCallback(() => {
+ if (!previousMonth) return;
+ goToPreviousMonth();
+ onPrevClick?.(previousMonth);
+ }, [goToPreviousMonth, onPrevClick, previousMonth]);
+
+ const handleNextClick = useCallback(() => {
+ if (!nextMonth) return;
+ goToNextMonth();
+ onNextClick?.(nextMonth);
+ }, [goToNextMonth, nextMonth, onNextClick]);
+
+ const handleDayClick = useCallback(
+ (day: CalendarDay, m: Modifiers) => {
+ return (e: MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ handleSelect(day.date, m, e);
+ setFocused(day);
+ onDayClick?.(day.date, m, e);
+ };
+ },
+ [handleSelect, onDayClick, setFocused]
+ );
+
+ const handleDayFocus = useCallback(
+ (day: CalendarDay, m: Modifiers) => {
+ return (e: FocusEvent) => {
+ setFocused(day);
+ onDayFocus?.(day.date, m, e);
+ };
+ },
+ [onDayFocus, setFocused]
+ );
+
+ const handleDayBlur = useCallback(
+ (day: CalendarDay, m: Modifiers) => {
+ return (e: FocusEvent) => {
+ blur();
+ onDayBlur?.(day.date, m, e);
+ };
+ },
+ [blur, onDayBlur]
+ );
+
+ const handleDayKeyDown = useCallback(
+ (day: CalendarDay, modifiers: Modifiers) => {
+ return (e: KeyboardEvent) => {
+ switch (e.key) {
+ case "ArrowLeft":
+ e.preventDefault();
+ e.stopPropagation();
+ dir === "rtl" ? focusDayAfter() : focusDayBefore();
+ break;
+ case "ArrowRight":
+ e.preventDefault();
+ e.stopPropagation();
+ dir === "rtl" ? focusDayBefore() : focusDayAfter();
+ break;
+ case "ArrowDown":
+ e.preventDefault();
+ e.stopPropagation();
+ focusWeekAfter();
+ break;
+ case "ArrowUp":
+ e.preventDefault();
+ e.stopPropagation();
+ focusWeekBefore();
+ break;
+ case " ":
+ case "Enter":
+ e.preventDefault();
+ e.stopPropagation();
+ selection?.handleSelect(day.date, modifiers, e);
+ break;
+ case "PageUp":
+ e.preventDefault();
+ e.stopPropagation();
+ e.shiftKey ? focusYearBefore() : focusMonthBefore();
+ break;
+ case "PageDown":
+ e.preventDefault();
+ e.stopPropagation();
+ e.shiftKey ? focusYearAfter() : focusMonthAfter();
+ break;
+ case "Home":
+ e.preventDefault();
+ e.stopPropagation();
+ focusStartOfWeek();
+ break;
+ case "End":
+ e.preventDefault();
+ e.stopPropagation();
+ focusEndOfWeek();
+ break;
+ }
+ onDayKeyDown?.(day.date, modifiers, e);
+ };
+ },
+ [
+ dir,
+ focusDayAfter,
+ focusDayBefore,
+ focusEndOfWeek,
+ focusMonthAfter,
+ focusMonthBefore,
+ focusStartOfWeek,
+ focusWeekAfter,
+ focusWeekBefore,
+ focusYearAfter,
+ focusYearBefore,
+ onDayKeyDown,
+ selection
+ ]
+ );
+
+ const formatOptions: FormatOptions = {
+ locale,
+ weekStartsOn,
+ firstWeekContainsDate,
+ useAdditionalWeekYearTokens,
+ useAdditionalDayOfYearTokens
+ };
+
+ const labelOptions: LabelOptions = formatOptions;
+
+ const { className, style } = useMemo(
+ () => ({
+ className: [classNames[UI.Root], props.className]
+ .filter(Boolean)
+ .join(" "),
+ style: { ...styles?.[UI.Root], ...props.style }
+ }),
+ [classNames, props.className, props.style, styles]
+ );
+
+ const dataAttributes = useMemo(() => getDataAttributes(props), [props]);
return (
-
-
-
+
+ 1 || undefined}
+ data-week-numbers={showWeekNumber || undefined}
+ {...dataAttributes}
+ >
+
+ {!props.hideNavigation && (
+
+
+
+
+
+
+
+
+ )}
+ {calendar.months.map((calendarMonth, displayIndex) => {
+ const captionId = `${id}-caption-${displayIndex}`;
+
+ const handleMonthChange: ChangeEventHandler = (
+ e
+ ) => {
+ const selectedMonth = Number(
+ (e.target as HTMLSelectElement).value
+ );
+ const month = dateLib.setMonth(
+ dateLib.startOfMonth(calendarMonth.date),
+ selectedMonth
+ );
+ calendar.goToMonth(month);
+ };
+
+ const handleYearChange: ChangeEventHandler = (
+ e
+ ) => {
+ const month = dateLib.setYear(
+ dateLib.startOfMonth(calendarMonth.date),
+ Number(e.target.value)
+ );
+ calendar.goToMonth(month);
+ };
+
+ const dropdownMonths = getDropdownMonths(
+ calendarMonth.date,
+ calendar.navigationStartMonth,
+ calendar.navigationEndMonth,
+ formatters,
+ locale,
+ dateLib
+ );
+ const dropdownYears = getDropdownYears(
+ calendar.months[0].date,
+ calendar.navigationStartMonth,
+ calendar.navigationEndMonth,
+ formatters,
+ dateLib
+ );
+
+ return (
+
+
+ {captionLayout?.startsWith("dropdown") ? (
+
+ {captionLayout === "dropdown" ||
+ captionLayout === "dropdown-months" ? (
+
+ ) : (
+
+ {formatMonthDropdown(calendarMonth.date.getMonth())}
+
+ )}
+ {captionLayout === "dropdown" ||
+ captionLayout === "dropdown-years" ? (
+
+ ) : (
+
+ {formatYearDropdown(calendarMonth.date.getFullYear())}
+
+ )}
+
+ ) : (
+
+ {formatCaption(
+ calendarMonth.date,
+ formatOptions,
+ dateLib
+ )}
+
+ )}
+
+
+
+ {showWeekNumber && (
+
+ {!props.hideWeekdayRow && formatWeekNumberHeader()}
+
+ )}
+ {weekdays.map((weekday, i) => (
+
+ {formatWeekdayName(weekday, formatOptions, dateLib)}
+
+ ))}
+
+
+ {calendarMonth.weeks.map((week, weekIndex) => {
+ return (
+
+ {showWeekNumber && (
+
+ {formatWeekNumber(week.weekNumber)}
+
+ )}
+ {week.days.map((day: CalendarDay) => {
+ const { date } = day;
+ const dayModifiers = modifiers.getModifiers(day);
+ const focused = focusedDay?.isEqualTo(day);
+
+ if (!dayModifiers.hidden && focused)
+ dayModifiers[DayFlag.focused] = true;
+
+ const selected = isSelected(date);
+
+ dayModifiers[SelectionState.selected] =
+ !dayModifiers.disabled && selected;
+
+ if (!dayModifiers.disabled && mode === "range") {
+ const range = selection as UseRange;
+ dayModifiers[SelectionState.range_start] =
+ range.isRangeStart(date);
+ dayModifiers[SelectionState.range_end] =
+ range.isRangeEnd(date);
+ dayModifiers[SelectionState.range_middle] =
+ range.isRangeMiddle(date);
+ }
+
+ const style = {
+ ...getStyleForModifiers(
+ dayModifiers,
+ modifiersStyles
+ ),
+ ...styles?.[UI.Day]
+ };
+
+ const className = [
+ classNames[UI.Day],
+ ...getClassNamesForModifiers(
+ dayModifiers,
+ classNames,
+ modifiersClassNames
+ )
+ ];
+
+ const ariaLabel = !isInteractive
+ ? labelGridcell(
+ date,
+ dayModifiers,
+ labelOptions,
+ dateLib
+ )
+ : undefined;
+
+ const dataAttributes = {
+ "data-day": dateLib.format(date, "yyyy-MM-dd"),
+ "data-month": day.outside
+ ? dateLib.format(date, "yyyy-MM")
+ : undefined
+ };
+ return (
+
+ {isInteractive ? (
+
+ {formatDay(date, formatOptions, dateLib)}
+
+ ) : (
+ formatDay(day.date, formatOptions, dateLib)
+ )}
+
+ );
+ })}
+
+ );
+ })}
+
+
+
+ );
+ })}
+
+ {props.footer && (
+
+ {props.footer}
+
+ )}
+
+
);
}
diff --git a/src/UI.ts b/src/UI.ts
index eabddd2dbe..27ebd4875b 100644
--- a/src/UI.ts
+++ b/src/UI.ts
@@ -10,13 +10,10 @@ import type { CustomComponents, ClassNames, Styles } from "./types/index.js";
export enum UI {
/** The previous button in the navigation. */
ButtonPrevious = "button_previous",
- /** The next button the navigation */
+ /** The next button the navigation. */
ButtonNext = "button_next",
- /**
- * The calendar element: the root component displaying the months and the
- * navigation bar. Extended by {@link CalendarFlag}.
- */
- Calendar = "calendar",
+ /** The root component displaying the months and the navigation bar. */
+ Root = "root",
/** The Chevron SVG element used by navigation buttons and dropdowns. */
Chevron = "chevron",
/**
@@ -24,8 +21,8 @@ export enum UI {
* {@link SelectionFlag}.
*/
Day = "day",
- /** The element containing the formatted day's date, inside the grid cell. */
- DayDate = "day_date",
+ /** The button containing the formatted day's date, inside the grid cell. */
+ DayButton = "day_button",
/** The caption label of the month (when not showing the dropdown navigation). */
CaptionLabel = "caption_label",
/** The container of the dropdown navigation (when enabled). */
@@ -37,16 +34,16 @@ export enum UI {
/** The root element of the footer. */
Footer = "footer",
/** The month grid. */
- Month = "month",
+ MonthGrid = "month_grid",
/** Contains the dropdown navigation or the caption label. */
MonthCaption = "month_caption",
/** The dropdown with the months. */
MonthsDropdown = "months_dropdown",
- /** Wrapper of the {@link} grid. */
- MonthWrapper = "month_wrapper",
+ /** Wrapper of the month grid. */
+ Month = "month",
/** The container of the displayed months. */
Months = "months",
- /** The navigation bar with the previous and next buttons */
+ /** The navigation bar with the previous and next buttons. */
Nav = "nav",
/** The row containing the week. */
Week = "week",
@@ -56,25 +53,22 @@ export enum UI {
Weekday = "weekday",
/** The row grouping the weekdays in the column headers. */
Weekdays = "weekdays",
- /**
- * The row header containing the week number. Extended by
- * {@link WeekNumberFlag}.
- */
+ /** The row header containing the week number. */
WeekNumber = "week_number",
+ /** The row header containing the week number. */
+ WeekNumberHeader = "week_number_header",
/** The dropdown with the years. */
YearsDropdown = "years_dropdown"
}
/** The flags for the {@link UI.Day}. */
export enum DayFlag {
- /** The day is disabled */
+ /** The day is disabled. */
disabled = "disabled",
- /** The day is hidden */
+ /** The day is hidden. */
hidden = "hidden",
- /** The day is outside the current month */
+ /** The day is outside the current month. */
outside = "outside",
- /** The day is focusable. */
- focusable = "focusable",
/** The day is focused. */
focused = "focused",
/** The day is today. */
@@ -95,28 +89,3 @@ export enum SelectionState {
/** The day is selected. */
selected = "selected"
}
-
-/** Flags that can be applied to the {@link UI.Calendar} element. */
-export enum CalendarFlag {
- /** Assigned when the week numbers are show. */
- has_week_numbers = "has_week_numbers",
- /** Assigned when the weekdays are hidden. */
- no_weekdays = "no_weekdays",
- /** Assigned when the calendar has multiple months. */
- has_multiple_months = "has_multiple_months"
-}
-
-/** Flags that can be applied to the {@link UI.Chevron} element. */
-export enum ChevronFlag {
- /** Assigned when the week numbers are show. */
- disabled = "chevron_disabled"
-}
-
-/** Flags that can be applied to the {@link UI.WeekNumber} element. */
-export enum WeekNumberFlag {
- /**
- * Assigned when the week number is interactive, i.e. has an
- * `onWeekNumberClick` event attached to it.
- */
- week_number_interactive = "week_number_interactive"
-}
diff --git a/src/classes/CalendarDay.ts b/src/classes/CalendarDay.ts
index 7a4ba57910..d889d46c6f 100644
--- a/src/classes/CalendarDay.ts
+++ b/src/classes/CalendarDay.ts
@@ -20,9 +20,17 @@ export class CalendarDay {
displayMonth && !dateLib.isSameMonth(date, displayMonth)
);
this.dateLib = dateLib;
+ const { format } = dateLib;
+ this.uid =
+ format(date, "yyyyMMdd") +
+ (this.outside ? `-` + format(displayMonth, "yyyyMMdd") : "");
}
- /** The utility functions to manipulate dates. */
+ /**
+ * The utility functions to manipulate dates.
+ *
+ * @private
+ */
readonly dateLib: DateLib;
/**
@@ -45,6 +53,9 @@ export class CalendarDay {
/** The date represented by this day. */
readonly date: Date;
+ /** A unique identifier for the day. */
+ readonly uid: string;
+
/**
* Check if the day is the same as the given day: considering if it is in the
* same display month.
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 05ca2e41d1..0a9707f6c0 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -1,4 +1,4 @@
-import React, { type ButtonHTMLAttributes } from "react";
+import React from "react";
/**
* Render the button elements in the calendar.
@@ -6,9 +6,9 @@ import React, { type ButtonHTMLAttributes } from "react";
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function Button(props: ButtonHTMLAttributes) {
+export function Button(props: JSX.IntrinsicElements["button"]) {
return ;
}
diff --git a/src/components/Calendar.test.tsx b/src/components/Calendar.test.tsx
deleted file mode 100644
index 847564f121..0000000000
--- a/src/components/Calendar.test.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import React from "react";
-
-import { grid, nav } from "@/test/elements";
-import { render } from "@/test/render";
-
-import { Calendar } from "./Calendar";
-
-it("should render the navigation and month grids", () => {
- render( );
-
- expect(nav()).toBeInTheDocument();
- expect(grid()).toBeInTheDocument();
-});
-
-it("should apply classnames and style according to props", () => {
- const { container } = render( , {
- className: "custom-class",
- numberOfMonths: 2,
- showWeekNumber: true,
- style: { color: "red" }
- });
-
- expect(container.firstChild).toHaveClass("rdp-calendar");
- expect(container.firstChild).toHaveClass("rdp-has_multiple_months");
- expect(container.firstChild).toHaveClass("rdp-has_week_numbers");
- expect(container.firstChild).toHaveClass("custom-class");
- expect(container.firstChild).toHaveStyle({ color: "red" });
-});
-
-it("should use custom components", () => {
- const { container } = render( , {
- footer: "foo",
- components: {
- Nav: () => Custom Navigation
,
- Month: () => Custom Month
,
- Months: (props) => (
-
- Custom Months
{props.children}
-
- ),
- Footer: () => Custom Footer
- }
- });
-
- expect(container).toHaveTextContent("Custom Navigation");
- expect(container).toHaveTextContent("Custom Months");
- expect(container).toHaveTextContent("Custom Month");
- expect(container).toHaveTextContent("Custom Footer");
-});
diff --git a/src/components/Calendar.tsx b/src/components/Calendar.tsx
deleted file mode 100644
index a408a93afb..0000000000
--- a/src/components/Calendar.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import React from "react";
-
-import { UI, CalendarFlag } from "../UI.js";
-import { useCalendar, useProps } from "../contexts/index.js";
-
-import { Footer as DefaultFooter } from "./Footer.js";
-import { Month as DefaultMonth } from "./Month.js";
-import { Months as DefaultMonths } from "./Months.js";
-import { Nav as DefaultNav } from "./Nav.js";
-
-/**
- * Render the DayPicker Calendar with navigation and the month grids.
- *
- * Use the `components` prop to swap this component with a custom one.
- *
- * @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
- */
-export function Calendar() {
- const {
- className,
- classNames,
- components,
- dataAttributes,
- dir,
- footer,
- hideNavigation,
- hideWeekdayRow,
- id,
- lang,
- nonce,
- numberOfMonths,
- showWeekNumber,
- style,
- styles,
- title
- } = useProps();
-
- const calendar = useCalendar();
-
- // Apply classnames according to props
- const cssClassNames = [classNames[UI.Calendar]];
- if (className) {
- cssClassNames.push(className);
- }
- if (numberOfMonths > 1) {
- cssClassNames.push(classNames[CalendarFlag.has_multiple_months]);
- }
- if (showWeekNumber) {
- cssClassNames.push(classNames[CalendarFlag.has_week_numbers]);
- }
- if (hideWeekdayRow) {
- cssClassNames.push(classNames[CalendarFlag.no_weekdays]);
- }
-
- const Nav = components?.Nav ?? DefaultNav;
- const Months = components?.Months ?? DefaultMonths;
- const Month = components?.Month ?? DefaultMonth;
- const Footer = components?.Footer ?? DefaultFooter;
-
- return (
-
-
- {!hideNavigation && }
- {calendar.months.map((month, i) => (
-
- ))}
-
- {footer && (
-
- )}
-
- );
-}
diff --git a/src/components/CaptionLabel.tsx b/src/components/CaptionLabel.tsx
new file mode 100644
index 0000000000..d024ae76a1
--- /dev/null
+++ b/src/components/CaptionLabel.tsx
@@ -0,0 +1,15 @@
+import React from "react";
+
+/**
+ * Render the label in the month caption.
+ *
+ * Use the `components` prop to swap this component with a custom one.
+ *
+ * @group Components
+ * @see https://daypicker.dev/next/guides/custom-components
+ */
+export function CaptionLabel(props: JSX.IntrinsicElements["span"]) {
+ return ;
+}
+
+export type CaptionLabelProps = Parameters[0];
diff --git a/src/components/Chevron.tsx b/src/components/Chevron.tsx
index 09f8b70e6a..9bfbd2a453 100644
--- a/src/components/Chevron.tsx
+++ b/src/components/Chevron.tsx
@@ -1,36 +1,30 @@
import React from "react";
-import { ChevronFlag, UI } from "../UI.js";
-import { useProps } from "../contexts/index.js";
-
/**
* Render the chevron icon used in the navigation buttons and dropdowns.
*
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
export function Chevron(props: {
+ className?: string;
+ /**
+ * The size of the chevron.
+ *
+ * @defaultValue 24
+ */
size?: number;
+ /** Set to `true` to disable the chevron. */
+ disabled?: boolean;
+ /** The orientation of the chevron. */
orientation?: "up" | "down" | "left" | "right";
}) {
- const { size = 24, orientation = "left" } = props;
- const { classNames, disableNavigation } = useProps();
+ const { size = 24, orientation = "left", className } = props;
- const svgClassName = [
- classNames[UI.Chevron],
- disableNavigation ? classNames[ChevronFlag.disabled] : ""
- ]
- .join(" ")
- .trim();
return (
-
+
{orientation === "up" && (
)}
diff --git a/src/components/Day.tsx b/src/components/Day.tsx
index 91031b7bfa..7835b4e850 100644
--- a/src/components/Day.tsx
+++ b/src/components/Day.tsx
@@ -1,5 +1,4 @@
import React from "react";
-import type { ReactNode } from "react";
import type { CalendarDay } from "../classes/index.js";
import type { Modifiers } from "../types/index.js";
@@ -13,41 +12,16 @@ import type { Modifiers } from "../types/index.js";
* `DayDate` component instead.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function Day(props: {
- day: CalendarDay;
- modifiers: Modifiers;
- children?: ReactNode;
- rootProps: Pick<
- JSX.IntrinsicElements["div"],
- | "className"
- | "style"
- | "tabIndex"
- | "aria-colindex"
- | "aria-disabled"
- | "aria-hidden"
- | "aria-label"
- | "aria-selected"
- | "onClick"
- | "onBlur"
- | "onFocus"
- | "onKeyDown"
- | "onKeyPress"
- | "onKeyUp"
- | "onMouseEnter"
- | "onMouseLeave"
- | "onPointerEnter"
- | "onPointerLeave"
- | "onTouchCancel"
- | "onTouchEnd"
- | "onTouchMove"
- | "onTouchStart"
- | "ref"
- | "role"
- >;
-}) {
- return {props.children}
;
+export function Day(
+ props: {
+ day: CalendarDay;
+ modifiers: Modifiers;
+ } & JSX.IntrinsicElements["td"]
+) {
+ const { day, modifiers, ...tdProps } = props;
+ return ;
}
export type DayProps = Parameters[0];
diff --git a/src/components/DayButton.tsx b/src/components/DayButton.tsx
new file mode 100644
index 0000000000..5e29fa0059
--- /dev/null
+++ b/src/components/DayButton.tsx
@@ -0,0 +1,34 @@
+import React from "react";
+
+import type { CalendarDay } from "../classes/index.js";
+import type { Modifiers } from "../types/index.js";
+
+/**
+ * Render the button for a day in the calendar.
+ *
+ * When not interactive, DayPicker will render a `DayContent` component instead
+ * of a `DayButton` component.
+ *
+ * Use the `components` prop to swap this component with a custom one.
+ *
+ * @group Components
+ * @see https://daypicker.dev/next/guides/custom-components
+ */
+export function DayButton(
+ props: {
+ /** The day to render. */
+ day: CalendarDay;
+ /** The modifiers for the day. */
+ modifiers: Modifiers;
+ } & JSX.IntrinsicElements["button"]
+) {
+ const { day, modifiers, ...buttonProps } = props;
+
+ const ref = React.useRef(null);
+ React.useEffect(() => {
+ if (modifiers.focused) ref.current?.focus();
+ }, [modifiers.focused]);
+ return ;
+}
+
+export type DayButtonProps = Parameters[0];
diff --git a/src/components/DayDate.tsx b/src/components/DayDate.tsx
deleted file mode 100644
index cbcf3e1414..0000000000
--- a/src/components/DayDate.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import React from "react";
-
-import type { CalendarDay } from "../classes/index.js";
-import type { Modifiers } from "../types/index.js";
-
-/**
- * Render the date as string inside the day grid cell.
- *
- * Use the `components` prop to swap this component with a custom one.
- *
- * @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
- */
-export function DayDate(props: {
- day: CalendarDay;
- /** The date to display. */
- formattedDate: string;
- /** The modifiers for the day. */
- modifiers: Modifiers;
- /** The HTML attributes for the root element. */
- rootProps: {
- className: string;
- style?: React.CSSProperties;
- };
-}) {
- return {props.formattedDate} ;
-}
-
-export type DayDateProps = Parameters[0];
-
-/**
- * @deprecated The component has been renamed. Use `DayDate` instead.
- * @protected
- */
-export const DayContent = DayDate;
-/**
- * @deprecated The type has been renamed. Use `DayDateProps` instead.
- * @protected
- */
-export type DayContentProps = DayDateProps;
diff --git a/src/components/DayWrapper.tsx b/src/components/DayWrapper.tsx
deleted file mode 100644
index a6eb336e49..0000000000
--- a/src/components/DayWrapper.tsx
+++ /dev/null
@@ -1,293 +0,0 @@
-import React from "react";
-
-import { UI, DayFlag } from "../UI.js";
-import { CalendarDay } from "../classes/CalendarDay.js";
-import {
- useCalendar,
- useFocus,
- useModifiers,
- useProps
-} from "../contexts/index.js";
-import { debounce } from "../helpers/debounce.js";
-import { getClassNamesForModifiers } from "../helpers/getClassNamesForModifiers.js";
-import { getStyleForModifiers } from "../helpers/getStyleForModifiers.js";
-import { useMulti, useRange, useSingle } from "../selection/index.js";
-
-import { type DayProps, Day as DefaultDay } from "./Day.js";
-import { type DayDateProps, DayDate as DefaultDayDate } from "./DayDate.js";
-
-/**
- * Provides a `Day` the day state and the html attributes. Developers may use a
- * `Day` component without the need to use hooks.
- *
- * @internal
- */
-export function DayWrapper(props: {
- "aria-colindex": number;
- /** The day to be rendered in the gridcell. */
- day: CalendarDay;
-}) {
- const cellRef = React.useRef(null);
-
- const {
- classNames,
- components,
- dateLib,
- dir,
- formatters: { formatDay },
- labels: { labelDay },
- locale,
- mode,
- modifiersClassNames = {},
- modifiersStyles = {},
- onDayFocus,
- onDayBlur,
- onDayClick,
- onDayKeyDown,
- onDayKeyPress,
- onDayKeyUp,
- onDayMouseEnter,
- onDayMouseLeave,
- onDayPointerEnter,
- onDayPointerLeave,
- onDayTouchCancel,
- onDayTouchEnd,
- onDayTouchMove,
- onDayTouchStart,
- styles = {}
- } = useProps();
-
- const { isInteractive } = useCalendar();
- const { getModifiers } = useModifiers();
-
- const single = useSingle();
- const multi = useMulti();
- const range = useRange();
-
- const {
- autoFocusTarget,
- focused,
- focus,
- blur,
- focusDayBefore,
- focusDayAfter,
- focusWeekBefore,
- focusWeekAfter,
- focusMonthBefore,
- focusMonthAfter,
- focusYearBefore,
- focusYearAfter,
- focusStartOfWeek,
- focusEndOfWeek
- } = useFocus();
-
- const modifiers = getModifiers(props.day);
-
- const onClick: React.MouseEventHandler = (e) => {
- if (modifiers.disabled) {
- e.preventDefault();
- e.stopPropagation();
- return;
- }
-
- switch (mode) {
- case "single":
- single.setSelected(props.day.date, modifiers, e);
- break;
- case "multiple":
- multi.setSelected(props.day.date, modifiers, e);
- break;
- case "range":
- range.setSelected(props.day.date, modifiers, e);
- break;
- }
-
- if (modifiers.focusable) {
- focus(props.day);
- }
-
- onDayClick?.(props.day.date, modifiers, e);
- };
-
- const onFocus: React.FocusEventHandler = (e) => {
- if (modifiers.disabled) {
- e.preventDefault();
- e.stopPropagation();
- return;
- }
- focus(props.day);
- onDayFocus?.(props.day.date, modifiers, e);
- };
-
- const onBlur: React.FocusEventHandler = (e) => {
- blur();
- onDayBlur?.(props.day.date, modifiers, e);
- };
-
- const onMouseEnter: React.MouseEventHandler = (e) => {
- onDayMouseEnter?.(props.day.date, modifiers, e);
- };
- const onMouseLeave: React.MouseEventHandler = (e) => {
- onDayMouseLeave?.(props.day.date, modifiers, e);
- };
- const onPointerEnter: React.PointerEventHandler = (e) => {
- onDayPointerEnter?.(props.day.date, modifiers, e);
- };
- const onPointerLeave: React.PointerEventHandler = (e) => {
- onDayPointerLeave?.(props.day.date, modifiers, e);
- };
- const onTouchCancel: React.TouchEventHandler = (e) => {
- onDayTouchCancel?.(props.day.date, modifiers, e);
- };
- const onTouchEnd: React.TouchEventHandler = (e) => {
- onDayTouchEnd?.(props.day.date, modifiers, e);
- };
- const onTouchMove: React.TouchEventHandler = (e) => {
- onDayTouchMove?.(props.day.date, modifiers, e);
- };
- const onTouchStart: React.TouchEventHandler = (e) => {
- onDayTouchStart?.(props.day.date, modifiers, e);
- };
-
- const onKeyUp: React.KeyboardEventHandler = (e) => {
- onDayKeyUp?.(props.day.date, modifiers, e);
- };
-
- const onKeyPress: React.KeyboardEventHandler = (e) => {
- onDayKeyPress?.(props.day.date, modifiers, e);
- };
-
- const onKeyDown: React.KeyboardEventHandler = (e) => {
- switch (e.key) {
- case "ArrowLeft":
- e.preventDefault();
- e.stopPropagation();
- dir === "rtl" ? focusDayAfter() : focusDayBefore();
- break;
- case "ArrowRight":
- e.preventDefault();
- e.stopPropagation();
- dir === "rtl" ? focusDayBefore() : focusDayAfter();
- break;
- case "ArrowDown":
- e.preventDefault();
- e.stopPropagation();
- focusWeekAfter();
- break;
- case "ArrowUp":
- e.preventDefault();
- e.stopPropagation();
- focusWeekBefore();
- break;
- case " ":
- case "Enter":
- e.preventDefault();
- e.stopPropagation();
- if (mode === "single" && !modifiers.disabled) {
- single.setSelected(props.day.date, modifiers, e);
- }
- if (mode === "multiple" && !modifiers.disabled) {
- multi.setSelected(props.day.date, modifiers, e);
- }
- if (mode === "range" && !modifiers.disabled) {
- range.setSelected(props.day.date, modifiers, e);
- }
- break;
- case "PageUp":
- e.preventDefault();
- e.stopPropagation();
- e.shiftKey ? focusYearBefore() : focusMonthBefore();
- break;
- case "PageDown":
- e.preventDefault();
- e.stopPropagation();
- e.shiftKey ? focusYearAfter() : focusMonthAfter();
- break;
- case "Home":
- e.preventDefault();
- e.stopPropagation();
- focusStartOfWeek();
- break;
- case "End":
- e.preventDefault();
- e.stopPropagation();
- focusEndOfWeek();
- break;
- }
- onDayKeyDown?.(props.day.date, modifiers, e);
- };
-
- const isAutoFocusTarget = Boolean(autoFocusTarget?.isEqualTo(props.day));
- const isFocused = Boolean(focused?.isEqualTo(props.day));
-
- const style = getStyleForModifiers(modifiers, modifiersStyles, styles);
-
- const classNameForModifiers = getClassNamesForModifiers(
- modifiers,
- modifiersClassNames,
- classNames
- );
-
- const className = [classNames[UI.Day], ...classNameForModifiers];
-
- if (isFocused) {
- className.push(classNames[DayFlag.focused]);
- }
-
- const dayRootProps: DayProps["rootProps"] = {
- role: "gridcell",
- className: className.join(" "),
- style,
- tabIndex: isFocused || isAutoFocusTarget ? 0 : -1,
- ["aria-colindex"]: props["aria-colindex"],
- ["aria-label"]:
- labelDay(props.day.date, modifiers, { locale }, dateLib) ?? undefined,
- ["aria-disabled"]: modifiers.disabled || undefined,
- ["aria-hidden"]: modifiers.hidden || undefined,
- ["aria-selected"]: modifiers.selected || undefined,
- onClick: isInteractive ? onClick : undefined,
- onBlur,
- onFocus,
- onKeyDown,
- onKeyPress: debounce(onKeyPress, 300),
- onKeyUp,
- onMouseEnter,
- onMouseLeave,
- onPointerEnter,
- onPointerLeave,
- onTouchCancel,
- onTouchEnd,
- onTouchMove,
- onTouchStart,
- ref: cellRef
- };
-
- React.useEffect(() => {
- if (!cellRef.current) return; // no element to focus
- if (!focused) return; // no day to focus
- if (!props.day.isEqualTo(focused)) return; // not this day`
- if (modifiers.disabled || modifiers.hidden) return; // cannot focus
-
- cellRef.current.focus();
- }, [focused, modifiers.disabled, modifiers.hidden, props.day]);
-
- const Day = components?.Day ?? DefaultDay;
-
- const dayDateRootProps: DayDateProps["rootProps"] = {
- className: classNames[UI.DayDate],
- style: styles[UI.DayDate]
- };
-
- const DayDate = components?.DayDate ?? DefaultDayDate;
-
- return (
-
-
-
- );
-}
diff --git a/src/components/Dropdown.tsx b/src/components/Dropdown.tsx
index 0a12873902..54fa620684 100644
--- a/src/components/Dropdown.tsx
+++ b/src/components/Dropdown.tsx
@@ -1,11 +1,7 @@
-import React, { type SelectHTMLAttributes } from "react";
+import React from "react";
import { UI } from "../UI.js";
-import { useProps } from "../contexts/index.js";
-
-import { Chevron as DefaultChevron } from "./Chevron.js";
-import { Option as DefaultOption } from "./Option.js";
-import { Select as DefaultSelect } from "./Select.js";
+import type { ClassNames, CustomComponents } from "../types/index.js";
/** An option to use in the dropdown. Maps to the `` HTML element. */
export type DropdownOption = {
@@ -26,39 +22,45 @@ export type DropdownOption = {
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
export function Dropdown(
props: {
+ components: Pick<
+ Required,
+ "Select" | "Option" | "Chevron"
+ >;
+ classNames: Pick<
+ ClassNames,
+ UI.DropdownRoot | UI.Dropdown | UI.CaptionLabel | UI.Chevron
+ >;
options?: DropdownOption[] | undefined;
- rootClassName?: string;
- } & Omit, "children">
+ } & Omit
) {
- const { options, rootClassName, className, ...selectProps } = props;
- const { classNames, components } = useProps();
+ const { options, className, components, classNames, ...selectProps } = props;
- const cssClassRoot = [classNames[UI.DropdownRoot], rootClassName].join(" ");
+ const cssClassRoot = [classNames[UI.DropdownRoot]].join(" ");
const cssClassSelect = [classNames[UI.Dropdown], className].join(" ");
- const Select = components?.Select ?? DefaultSelect;
- const Option = components?.Option ?? DefaultOption;
- const Chevron = components?.Chevron ?? DefaultChevron;
-
const selectedOption = options?.find(
({ value }) => value === selectProps.value
);
return (
-
+
{options?.map(({ value, label, disabled }) => (
-
+
{label}
-
+
))}
-
+
{selectedOption?.label}
-
+
);
diff --git a/src/components/DropdownNav.tsx b/src/components/DropdownNav.tsx
index fbdd33da2e..1ff5992953 100644
--- a/src/components/DropdownNav.tsx
+++ b/src/components/DropdownNav.tsx
@@ -1,57 +1,15 @@
import React from "react";
-import { UI } from "../UI.js";
-import type { CalendarMonth } from "../classes/index.js";
-import { useProps } from "../contexts/index.js";
-
-import { MonthsDropdown } from "./MonthsDropdown.js";
-import { YearsDropdown } from "./YearsDropdown.js";
-
/**
- * Render the dropdowns to navigate between months.
+ * Render the the navigation dropdowns.
*
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function DropdownNav(props: {
- /** The month where the dropdown navigation is displayed. */
- month: CalendarMonth;
- /** Whether the user can change the month. */
- showMonths?: boolean;
- /** Whether the user can change the year. */
- showYears?: boolean;
- /** The index where this month is displayed. */
- index: number;
-}) {
- const {
- classNames,
- styles,
- formatters: { formatMonthDropdown, formatYearDropdown }
- } = useProps();
-
- return (
-
- {props.showMonths ? (
-
- ) : (
-
- {formatMonthDropdown(props.month.date.getMonth())}
-
- )}
- {props.showYears ? (
-
- ) : (
-
- {formatYearDropdown(props.month.date.getFullYear())}
-
- )}
-
- );
+export function DropdownNav(props: JSX.IntrinsicElements["div"]) {
+ return
;
}
export type DropdownNavProps = Parameters[0];
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index 8e77fd6dfc..d113916eb1 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -1,4 +1,4 @@
-import React, { type HTMLProps } from "react";
+import React from "react";
/**
* Component wrapping the footer.
@@ -6,12 +6,10 @@ import React, { type HTMLProps } from "react";
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function Footer(
- props: Pick, "children" | "className" | "style">
-) {
- return {props.children}
;
+export function Footer(props: JSX.IntrinsicElements["div"]) {
+ return
;
}
export type FooterProps = Parameters[0];
diff --git a/src/components/Month.tsx b/src/components/Month.tsx
index d7e4904afe..0d99c27d65 100644
--- a/src/components/Month.tsx
+++ b/src/components/Month.tsx
@@ -1,12 +1,6 @@
import React from "react";
-import { UI } from "../UI.js";
import type { CalendarMonth } from "../classes/CalendarMonth.js";
-import { useProps } from "../contexts/index.js";
-
-import { MonthCaption as DefaultMonthCaption } from "./MonthCaption.js";
-import { Week as DefaultWeek } from "./Week.js";
-import { Weekdays as DefaultWeekdays } from "./Weekdays.js";
/**
* Render the grid with the weekday header row and the weeks for the given
@@ -15,56 +9,18 @@ import { Weekdays as DefaultWeekdays } from "./Weekdays.js";
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function Month(props: {
- /** The month where the grid is displayed. */
- month: CalendarMonth;
- /** The index where this month is displayed. */
- index: number;
-}) {
- const { id, mode, hideWeekdayRow, components, classNames, styles } =
- useProps();
-
- const reactId = React.useId();
- const captionId = id ? `${id}-caption-${props.index}` : reactId;
- const gridId = id ? `${id}-grid-${props.index}` : reactId;
-
- const Weekdays = components?.Weekdays ?? DefaultWeekdays;
- const MonthCaption = components?.MonthCaption ?? DefaultMonthCaption;
- const Week = components?.Week ?? DefaultWeek;
-
- return (
-
-
-
-
-
- {props.month.weeks.map((week, i) => (
-
- ))}
-
-
-
- );
+export function Month(
+ props: {
+ /** The month where the grid is displayed. */
+ calendarMonth: CalendarMonth;
+ /** The index where this month is displayed. */
+ displayIndex: number;
+ } & JSX.IntrinsicElements["div"]
+) {
+ const { calendarMonth, displayIndex, ...divProps } = props;
+ return {props.children}
;
}
export type MonthProps = Parameters[0];
diff --git a/src/components/MonthCaption.tsx b/src/components/MonthCaption.tsx
index bab1d117b7..1d6a80753f 100644
--- a/src/components/MonthCaption.tsx
+++ b/src/components/MonthCaption.tsx
@@ -1,10 +1,6 @@
import React from "react";
-import { UI } from "../UI.js";
import type { CalendarMonth } from "../classes/index.js";
-import { useProps } from "../contexts/index.js";
-
-import { DropdownNav } from "./DropdownNav.js";
/**
* Render the caption of a month in the calendar.
@@ -12,57 +8,18 @@ import { DropdownNav } from "./DropdownNav.js";
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function MonthCaption(props: {
- /** The month where the grid is displayed. */
- month: CalendarMonth;
- /** Used for the aria-label. */
- id: string;
- /** The index where this month is displayed. */
- index: number;
-}) {
- const {
- classNames,
- captionLayout,
- dateLib,
- formatters: { formatCaption },
- labels: { labelCaption },
- locale,
- styles
- } = useProps();
-
- return (
-
- {captionLayout?.startsWith("dropdown") ? (
-
- ) : (
-
- {formatCaption(props.month.date, { locale }, dateLib)}
-
- )}
-
- );
+export function MonthCaption(
+ props: {
+ /** The month where the grid is displayed. */
+ calendarMonth: CalendarMonth;
+ /** The index where this month is displayed. */
+ displayIndex: number;
+ } & JSX.IntrinsicElements["div"]
+) {
+ const { calendarMonth, displayIndex, ...divProps } = props;
+ return
;
}
export type MonthCaptionProps = Parameters[0];
diff --git a/src/components/MonthGrid.tsx b/src/components/MonthGrid.tsx
new file mode 100644
index 0000000000..e8db98b269
--- /dev/null
+++ b/src/components/MonthGrid.tsx
@@ -0,0 +1,15 @@
+import React from "react";
+
+/**
+ * Render the grid of days in a month.
+ *
+ * Use the `components` prop to swap this component with a custom one.
+ *
+ * @group Components
+ * @see https://daypicker.dev/next/guides/custom-components
+ */
+export function MonthGrid(props: JSX.IntrinsicElements["table"]) {
+ return ;
+}
+
+export type MonthGridProps = Parameters[0];
diff --git a/src/components/Months.tsx b/src/components/Months.tsx
index 84463d2e48..d707a54b0a 100644
--- a/src/components/Months.tsx
+++ b/src/components/Months.tsx
@@ -1,18 +1,13 @@
import React from "react";
-import type { HTMLProps } from "react";
/**
* Component wrapping the month grids.
*
- * Use the `components` prop to swap this component with a custom one.
- *
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function Months(
- props: Pick, "children" | "className" | "style">
-) {
- return {props.children}
;
+export function Months(props: JSX.IntrinsicElements["div"]) {
+ return
;
}
export type MonthsProps = Parameters[0];
diff --git a/src/components/MonthsDropdown.tsx b/src/components/MonthsDropdown.tsx
deleted file mode 100644
index 398fc62a1e..0000000000
--- a/src/components/MonthsDropdown.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import React from "react";
-import type { ChangeEventHandler } from "react";
-
-import { UI } from "../UI.js";
-import type { CalendarMonth } from "../classes/index.js";
-import { useCalendar, useProps } from "../contexts/index.js";
-
-import { Dropdown as DefaultDropdown } from "./Dropdown.js";
-
-/**
- * Render the dropdown to change the month.
- *
- * Use the `components` prop to swap this component with a custom one.
- *
- * @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
- */
-export function MonthsDropdown(props: {
- /** The month where the dropdown is displayed. */
- month: CalendarMonth;
-}) {
- const {
- classNames,
- components,
- disableNavigation,
- dateLib: { setMonth, startOfMonth },
- labels: { labelMonthDropdown }
- } = useProps();
-
- const { dropdownOptions, goToMonth } = useCalendar();
-
- const Dropdown = components?.Dropdown ?? DefaultDropdown;
-
- const handleChange: ChangeEventHandler = (e) => {
- const selectedMonth = Number((e.target as HTMLSelectElement).value);
- const month = setMonth(startOfMonth(props.month.date), selectedMonth);
- goToMonth(month);
- };
-
- return (
-
- );
-}
-
-export type MonthsDropdownProps = Parameters[0];
diff --git a/src/components/Nav.test.tsx b/src/components/Nav.test.tsx
deleted file mode 100644
index e0a8c7c89b..0000000000
--- a/src/components/Nav.test.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from "react";
-
-import { nextButton, previousButton } from "@/test/elements";
-import { render } from "@/test/render";
-import { user } from "@/test/user";
-
-import { Nav } from "./Nav";
-
-describe("when clicking the next button", () => {
- test("should call the onNextClick callback", async () => {
- const onNextClick = jest.fn();
- render( , { onNextClick });
- await user.click(nextButton());
- expect(onNextClick).toHaveBeenCalled();
- });
-});
-
-describe("when clicking the previous button", () => {
- test("should call the onPrevClick callback", async () => {
- const onPrevClick = jest.fn();
- render( , { onPrevClick });
- await user.click(previousButton());
- expect(onPrevClick).toHaveBeenCalled();
- });
-});
diff --git a/src/components/Nav.tsx b/src/components/Nav.tsx
index 6d50c0d709..f3813e7170 100644
--- a/src/components/Nav.tsx
+++ b/src/components/Nav.tsx
@@ -1,74 +1,15 @@
import React from "react";
-import { UI } from "../UI.js";
-import { useCalendar, useProps } from "../contexts/index.js";
-
-import { Button as DefaultButton } from "./Button.js";
-import { Chevron as DefaultChevron } from "./Chevron.js";
-
/**
- * Render the navigation buttons to change the month.
+ * Render the toolbar with the navigation button.
*
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function Nav() {
- const {
- classNames,
- styles,
- labels: { labelNext, labelPrevious },
- locale,
- components,
- id,
- onNextClick,
- onPrevClick
- } = useProps();
-
- const calendar = useCalendar();
-
- const handlePreviousClick = () => {
- if (!calendar.previousMonth) return;
- calendar.goToPreviousMonth();
- onPrevClick?.(calendar.previousMonth);
- };
-
- const handleNextClick = () => {
- if (!calendar.nextMonth) return;
- calendar.goToNextMonth();
- onNextClick?.(calendar.nextMonth);
- };
-
- const Button = components?.Button ?? DefaultButton;
- const Chevron = components?.Chevron ?? DefaultChevron;
-
- return (
-
-
-
-
-
-
-
-
- );
+export function Nav(props: JSX.IntrinsicElements["div"]) {
+ return
;
}
+
+export type NavProps = Parameters[0];
diff --git a/src/components/Option.tsx b/src/components/Option.tsx
index 12d85876db..c97a237006 100644
--- a/src/components/Option.tsx
+++ b/src/components/Option.tsx
@@ -1,5 +1,4 @@
import React from "react";
-import type { OptionHTMLAttributes } from "react";
/**
* Render the `option` element.
@@ -7,9 +6,9 @@ import type { OptionHTMLAttributes } from "react";
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function Option(props: OptionHTMLAttributes) {
+export function Option(props: JSX.IntrinsicElements["option"]) {
return ;
}
diff --git a/src/components/Root.tsx b/src/components/Root.tsx
new file mode 100644
index 0000000000..5c2dc79b88
--- /dev/null
+++ b/src/components/Root.tsx
@@ -0,0 +1,15 @@
+import React from "react";
+
+/**
+ * Render the root element.
+ *
+ * Use the `components` prop to swap this component with a custom one.
+ *
+ * @group Components
+ * @see https://daypicker.dev/next/guides/custom-components
+ */
+export function Root(props: JSX.IntrinsicElements["div"]) {
+ return
;
+}
+
+export type RootProps = Parameters[0];
diff --git a/src/components/Select.tsx b/src/components/Select.tsx
index 8c6ee7da25..93a8da9efe 100644
--- a/src/components/Select.tsx
+++ b/src/components/Select.tsx
@@ -1,5 +1,4 @@
import React from "react";
-import type { SelectHTMLAttributes } from "react";
/**
* Render the `select` element.
@@ -7,9 +6,9 @@ import type { SelectHTMLAttributes } from "react";
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function Select(props: SelectHTMLAttributes) {
+export function Select(props: JSX.IntrinsicElements["select"]) {
return ;
}
diff --git a/src/components/Week.tsx b/src/components/Week.tsx
index 3e1087a434..3b14f0842f 100644
--- a/src/components/Week.tsx
+++ b/src/components/Week.tsx
@@ -1,11 +1,6 @@
import React from "react";
-import { UI } from "../UI.js";
-import type { CalendarDay, CalendarWeek } from "../classes/index.js";
-import { useProps } from "../contexts/index.js";
-
-import { DayWrapper } from "./DayWrapper.js";
-import { WeekNumber as DefaultWeekNumber } from "./WeekNumber.js";
+import type { CalendarWeek } from "../classes/index.js";
/**
* Render a row in the calendar, with the days and the week number.
@@ -13,36 +8,15 @@ import { WeekNumber as DefaultWeekNumber } from "./WeekNumber.js";
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function Week(props: { ["aria-rowindex"]: number; week: CalendarWeek }) {
- const {
- styles,
- classNames,
- showWeekNumber,
- components,
- dateLib: { getUnixTime }
- } = useProps();
-
- const WeekNumber = components?.WeekNumber ?? DefaultWeekNumber;
-
- return (
-
- {showWeekNumber && }
- {props.week.days.map((day: CalendarDay, i: number) => (
-
- ))}
-
- );
+export function Week(
+ props: {
+ week: CalendarWeek;
+ } & JSX.IntrinsicElements["tr"]
+) {
+ const { week, ...trProps } = props;
+ return ;
}
export type WeekProps = Parameters[0];
diff --git a/src/components/WeekNumber.tsx b/src/components/WeekNumber.tsx
index f4817cdcce..a0ba218e9c 100644
--- a/src/components/WeekNumber.tsx
+++ b/src/components/WeekNumber.tsx
@@ -1,8 +1,6 @@
import React from "react";
-import { UI, WeekNumberFlag } from "../UI.js";
import type { CalendarWeek } from "../classes/index.js";
-import { useProps } from "../contexts/index.js";
/**
* Render the cell with the number of the week.
@@ -10,42 +8,16 @@ import { useProps } from "../contexts/index.js";
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function WeekNumber(props: { week: CalendarWeek }) {
- const {
- classNames,
- formatters: { formatWeekNumber },
- labels: { labelWeekNumber },
- locale,
- styles,
- onWeekNumberClick
- } = useProps();
-
- const isInteractive = Boolean(onWeekNumberClick);
-
- return (
-
- onWeekNumberClick?.(
- props.week.weekNumber,
- props.week.days.map((day) => day.date),
- e
- )
- }
- >
- {formatWeekNumber(props.week.weekNumber)}
-
- );
+export function WeekNumber(
+ props: {
+ /** The week to render. */
+ week: CalendarWeek;
+ } & JSX.IntrinsicElements["td"]
+) {
+ const { week, ...tdProps } = props;
+ return ;
}
export type WeekNumberProps = Parameters[0];
diff --git a/src/components/WeekNumberHeader.tsx b/src/components/WeekNumberHeader.tsx
new file mode 100644
index 0000000000..52c8d5afd3
--- /dev/null
+++ b/src/components/WeekNumberHeader.tsx
@@ -0,0 +1,15 @@
+import React from "react";
+
+/**
+ * Render the column header for the week numbers.
+ *
+ * Use the `components` prop to swap this component with a custom one.
+ *
+ * @group Components
+ * @see https://daypicker.dev/next/guides/custom-components
+ */
+export function WeekNumberHeader(props: JSX.IntrinsicElements["th"]) {
+ return ;
+}
+
+export type WeekNumberHeaderProps = Parameters[0];
diff --git a/src/components/Weekday.tsx b/src/components/Weekday.tsx
index 5d9f3f3076..1fdebf4fc6 100644
--- a/src/components/Weekday.tsx
+++ b/src/components/Weekday.tsx
@@ -1,48 +1,15 @@
import React from "react";
-import { UI } from "../UI.js";
-import { useProps } from "../contexts/index.js";
-
/**
* Render the column header with the weekday name (e.g. "Mo", "Tu", etc.).
*
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function Weekday(props: {
- ["aria-colindex"]?: number | undefined;
- ["aria-label"]?: string | undefined;
- weekday?: Date;
-}) {
- const {
- classNames,
- dateLib,
- formatters: { formatWeekdayName },
- labels: { labelWeekday, labelWeekNumberHeader },
- locale,
- hideWeekdayRow,
- styles
- } = useProps();
- return (
-
- {!hideWeekdayRow &&
- (props.weekday
- ? formatWeekdayName(props.weekday, { locale }, dateLib)
- : "#")}
-
- );
+export function Weekday(props: JSX.IntrinsicElements["th"]) {
+ return ;
}
export type WeekdayProps = Parameters[0];
diff --git a/src/components/Weekdays.tsx b/src/components/Weekdays.tsx
index ed27b28c56..f374f189c9 100644
--- a/src/components/Weekdays.tsx
+++ b/src/components/Weekdays.tsx
@@ -1,52 +1,19 @@
import React from "react";
-import { UI } from "../UI.js";
-import { useProps } from "../contexts/index.js";
-import { getWeekdays } from "../helpers/getWeekdays.js";
-
-import { Weekday as DefaultWeekday } from "./Weekday.js";
-
/**
* Render the row with the weekday names.
*
* Use the `components` prop to swap this component with a custom one.
*
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
-export function Weekdays() {
- const {
- classNames,
- components,
- dateLib,
- hideWeekdayRow,
- ISOWeek,
- locale,
- showWeekNumber,
- styles,
- weekStartsOn
- } = useProps();
-
- const weekdays = getWeekdays(locale, weekStartsOn, ISOWeek, dateLib);
- const Weekday = components?.Weekday ?? DefaultWeekday;
-
+export function Weekdays(props: JSX.IntrinsicElements["tr"]) {
return (
- e.stopPropagation()}
- >
- {showWeekNumber && }
- {weekdays.map((weekday, i) => (
-
- ))}
-
+
+
+
);
}
+
+export type WeekdaysProps = Parameters[0];
diff --git a/src/components/Weeks.tsx b/src/components/Weeks.tsx
new file mode 100644
index 0000000000..fef6d7f805
--- /dev/null
+++ b/src/components/Weeks.tsx
@@ -0,0 +1,15 @@
+import React from "react";
+
+/**
+ * Render the label in the month caption.
+ *
+ * Use the `components` prop to swap this component with a custom one.
+ *
+ * @group Components
+ * @see https://daypicker.dev/next/guides/custom-components
+ */
+export function Weeks(props: JSX.IntrinsicElements["tbody"]) {
+ return ;
+}
+
+export type WeeksProps = Parameters[0];
diff --git a/src/components/YearsDropdown.tsx b/src/components/YearsDropdown.tsx
deleted file mode 100644
index dd291e30ca..0000000000
--- a/src/components/YearsDropdown.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import React from "react";
-import type { ChangeEventHandler } from "react";
-
-import { UI } from "../UI.js";
-import type { CalendarMonth } from "../classes/CalendarMonth.js";
-import { useCalendar, useProps } from "../contexts/index.js";
-
-import { Dropdown as DefaultDropdown } from "./Dropdown.js";
-
-/**
- * Render the dropdown to change the year.
- *
- * Use the `components` prop to swap this component with a custom one.
- *
- * @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
- */
-export function YearsDropdown(props: {
- /** The month where the dropdown is displayed. */
- month: CalendarMonth;
-}) {
- const {
- classNames,
- components,
- dateLib: { startOfMonth, setYear },
- disableNavigation,
- labels: { labelYearDropdown }
- } = useProps();
-
- const { dropdownOptions, goToMonth } = useCalendar();
-
- const handleChange: ChangeEventHandler = (e) => {
- const month = setYear(
- startOfMonth(props.month.date),
- Number(e.target.value)
- );
- goToMonth(month);
- };
-
- const Dropdown = components?.Dropdown ?? DefaultDropdown;
- return (
-
- );
-}
diff --git a/src/components/custom-components.ts b/src/components/custom-components.tsx
similarity index 71%
rename from src/components/custom-components.ts
rename to src/components/custom-components.tsx
index 331a1d87ea..849a6e3223 100644
--- a/src/components/custom-components.ts
+++ b/src/components/custom-components.tsx
@@ -1,19 +1,22 @@
export * from "./Button.js";
export * from "./Chevron.js";
+export * from "./CaptionLabel.js";
export * from "./Day.js";
-export * from "./DayDate.js";
+export * from "./DayButton.js";
export * from "./Dropdown.js";
export * from "./DropdownNav.js";
export * from "./Footer.js";
export * from "./Month.js";
export * from "./MonthCaption.js";
+export * from "./MonthGrid.js";
export * from "./Months.js";
-export * from "./MonthsDropdown.js";
export * from "./Nav.js";
export * from "./Option.js";
+export * from "./Root.js";
export * from "./Select.js";
+export * from "./Weeks.js";
export * from "./Week.js";
export * from "./Weekday.js";
export * from "./Weekdays.js";
export * from "./WeekNumber.js";
-export * from "./YearsDropdown.js";
+export * from "./WeekNumberHeader.js";
diff --git a/src/contexts/index.ts b/src/contexts/index.ts
deleted file mode 100644
index edf91675c8..0000000000
--- a/src/contexts/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from "./useCalendar.js";
-export * from "./useFocus.js";
-export * from "./useModifiers.js";
-export * from "./useProps.js";
diff --git a/src/contexts/providers.tsx b/src/contexts/providers.tsx
deleted file mode 100644
index b6560e9798..0000000000
--- a/src/contexts/providers.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import React from "react";
-import type { PropsWithChildren } from "react";
-
-import {
- MultiProvider,
- RangeProvider,
- SingleProvider
-} from "../selection/index.js";
-import type {
- DayPickerProps,
- PropsMulti,
- PropsRange,
- PropsSingle
-} from "../types/index.js";
-
-import { CalendarContextProvider } from "./useCalendar.js";
-import { FocusContextProvider } from "./useFocus.js";
-import { ModifiersContextProvider } from "./useModifiers.js";
-import { PropsContextProvider, useProps } from "./useProps.js";
-
-function SelectionProviders({ children }: PropsWithChildren) {
- const props = useProps();
- return (
-
-
- {children}
-
-
- );
-}
-
-/**
- * Provide the value for all the contexts used by DayPicker.
- *
- * @private
- */
-export function ContextProviders(props: PropsWithChildren) {
- const { children, ...initialProps } = props;
- return (
-
-
-
-
- {children}
-
-
-
-
- );
-}
diff --git a/src/contexts/useCalendar.test.ts b/src/contexts/useCalendar.test.ts
deleted file mode 100644
index f1f2f0cc6c..0000000000
--- a/src/contexts/useCalendar.test.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { renderHook } from "@/test/renderHook";
-
-import { useCalendar } from "./useCalendar";
-
-it("should return the next month", () => {
- const { result } = renderHook(useCalendar, {
- month: new Date(2020, 0, 1)
- });
- expect(result.current.nextMonth).toEqual(new Date(2020, 1, 1));
-});
-
-it("should return the previous month", () => {
- const { result } = renderHook(useCalendar, {
- month: new Date(2020, 0, 1)
- });
- expect(result.current.previousMonth).toEqual(new Date(2019, 11, 1));
-});
-
-describe("dropdown", () => {
- it("should return undefined if no fromMonth is provided", () => {
- const { result } = renderHook(useCalendar, {
- fromMonth: undefined
- });
- expect(result.current.dropdownOptions.months).toBeUndefined();
- });
-
- it("should return undefined if no toMonth is provided", () => {
- const { result } = renderHook(useCalendar, { toMonth: undefined });
- expect(result.current.dropdownOptions.months).toBeUndefined();
- });
-
- it("should return an array of months between the fromMonth and toMonth", () => {
- const dayPicker = {
- fromMonth: new Date(2020, 1, 1),
- toMonth: new Date(2023, 2, 1)
- };
- const { result } = renderHook(useCalendar, dayPicker);
- const months = result.current.dropdownOptions.months;
- expect(months).toHaveLength(12);
- expect(months?.[0]).toEqual({
- value: 0,
- label: "January",
- disabled: false
- });
- expect(months?.[months.length - 1]).toEqual({
- value: 11,
- label: "December",
- disabled: true
- });
- });
-});
diff --git a/src/contexts/useCalendar.tsx b/src/contexts/useCalendar.tsx
deleted file mode 100644
index 5efa3eadb6..0000000000
--- a/src/contexts/useCalendar.tsx
+++ /dev/null
@@ -1,233 +0,0 @@
-import React, { type ReactElement, createContext, useContext } from "react";
-
-import type {
- CalendarWeek,
- CalendarDay,
- CalendarMonth
-} from "../classes/index.js";
-import type { DropdownOption } from "../components/Dropdown.js";
-import { getDates } from "../helpers/getDates.js";
-import { getDays } from "../helpers/getDays.js";
-import { getDisplayMonths } from "../helpers/getDisplayMonths.js";
-import { getDropdownMonths } from "../helpers/getDropdownMonths.js";
-import { getDropdownYears } from "../helpers/getDropdownYears.js";
-import { getInitialMonth } from "../helpers/getInitialMonth.js";
-import { getMonths } from "../helpers/getMonths.js";
-import { getNextMonth } from "../helpers/getNextMonth.js";
-import { getPreviousMonth } from "../helpers/getPreviousMonth.js";
-import { getWeeks } from "../helpers/getWeeks.js";
-import { useControlledValue } from "../helpers/useControlledValue.js";
-
-import { useProps } from "./useProps.js";
-
-/** @private */
-const CalendarContext = createContext(
- undefined
-);
-
-export type CalendarContextValue = {
- today: Date;
- /** All the unique dates displayed to the calendar. */
- dates: Date[];
- /**
- * All the days displayed in the calendar. As opposite from
- * {@link CalendarContext.dates}, it may return duplicated dates when shown
- * outside the month.
- */
- days: CalendarDay[];
- /** The months displayed in the calendar. */
- weeks: CalendarWeek[];
- /** The months displayed in the calendar. */
- months: CalendarMonth[];
- /**
- * The month displayed as first the calendar. When `numberOfMonths` is greater
- * than `1`, it is the first of the displayed months.
- */
- firstMonth: Date;
- /** The month displayed as last the calendar. */
- lastMonth: Date;
- /** The next month to display. */
- nextMonth: Date | undefined;
- /** The previous month to display. */
- previousMonth: Date | undefined;
- /** The options to use in the years or months dropdowns. */
- dropdownOptions: {
- /** The options to use in the months dropdown. */
- months: DropdownOption[] | undefined;
- /** The options to use in the years dropdown. */
- years: DropdownOption[] | undefined;
- };
-
- /** Set the first month displayed in the calendar. */
- setFirstMonth: (date: Date) => void;
-
- /**
- * Whether the calendar is interactive, i.e. DayPicker has a selection `mode`
- * set or the `onDayClick` prop is set.
- */
- isInteractive: boolean;
- /** Navigate to the specified month. Will fire the `onMonthChange` callback. */
- goToMonth: (month: Date) => void;
- /** Navigate to the next month. */
- goToNextMonth: () => void;
- /** Navigate to the previous month. */
- goToPreviousMonth: () => void;
- /**
- * Navigate to the specified date. If the second parameter (refDate) is
- * provided and the date is before the refDate, then the month is set to one
- * month before the date.
- *
- * @param day - The date to navigate to.
- * @param dateToCompare - Optional. If `date` is before `dateToCompare`, the
- * month is set to one month before the date.
- */
- goToDay: (day: CalendarDay) => void;
- /** Whether the given date is included in the displayed months. */
- isDayDisplayed: (day: CalendarDay) => boolean;
-};
-
-function useCalendarContextValue(): CalendarContextValue {
- const props = useProps();
- const { startOfMonth } = props.dateLib;
-
- const initialDisplayMonth = getInitialMonth(props);
-
- // The first month displayed in the calendar
- const [firstDisplayedMonth, setFirstMonth] = useControlledValue(
- initialDisplayMonth,
- props.month ? startOfMonth(props.month) : undefined
- );
-
- /** An array of the months displayed in the calendar. */
- const displayMonths = getDisplayMonths(firstDisplayedMonth, props);
-
- /** The last month displayed in the calendar. */
- const lastMonth = displayMonths[displayMonths.length - 1];
-
- /** An array of the dates displayed in the calendar. */
- const dates = getDates(displayMonths, props.endMonth, props);
-
- /** An array of the Months displayed in the calendar. */
- const months = getMonths(displayMonths, dates, props);
-
- /** An array of the Weeks displayed in the calendar. */
- const weeks = getWeeks(months);
-
- /** An array of the Days displayed in the calendar. */
- const days = getDays(months);
-
- const previousMonth = getPreviousMonth(firstDisplayedMonth, props);
- const nextMonth = getNextMonth(firstDisplayedMonth, props);
-
- const isInteractive =
- props.mode !== undefined || props.onDayClick !== undefined;
-
- const { disableNavigation, onMonthChange, startMonth, endMonth } = props;
-
- function isDayDisplayed(day: CalendarDay) {
- return weeks.some((week: CalendarWeek) => {
- return week.days.some((d) => {
- return d.isEqualTo(day);
- });
- });
- }
-
- function goToMonth(date: Date) {
- if (disableNavigation) {
- return;
- }
- let newMonth = startOfMonth(date);
- // if month is before startMonth, use the first month instead
- if (startMonth && newMonth < startOfMonth(startMonth)) {
- newMonth = startOfMonth(startMonth);
- }
- // if month is after endMonth, use the last month instead
- if (endMonth && newMonth > startOfMonth(endMonth)) {
- newMonth = startOfMonth(endMonth);
- }
- setFirstMonth(newMonth);
- onMonthChange?.(newMonth);
- }
-
- function goToDay(day: CalendarDay) {
- if (isDayDisplayed(day)) {
- return;
- }
-
- // TODO:??
- // if (refDate && isBefore(date, refDate)) {
- // console.log('date is before refDate');
- // const month = addMonths(date, 1 + dayPicker.numberOfMonths * -1);
- // console.log('going to month', month);
- // goToMonth(month);
- // } else {
- // console.log('going to month', date);
- goToMonth(day.date);
- // }
- }
-
- function goToNextMonth() {
- return nextMonth ? goToMonth(nextMonth) : undefined;
- }
- function goToPreviousMonth() {
- return previousMonth ? goToMonth(previousMonth) : undefined;
- }
-
- const calendarContextValue: CalendarContextValue = {
- dates,
- months,
- weeks,
- days,
-
- today: props.today,
-
- firstMonth: firstDisplayedMonth,
- lastMonth,
- previousMonth,
- nextMonth,
-
- setFirstMonth,
-
- isInteractive,
-
- dropdownOptions: {
- months: getDropdownMonths(firstDisplayedMonth, props),
- years: getDropdownYears(firstDisplayedMonth, props)
- },
- isDayDisplayed,
- goToMonth,
- goToDay,
- goToNextMonth,
- goToPreviousMonth
- };
-
- return calendarContextValue;
-}
-
-/** @private */
-export function CalendarContextProvider(props: { children: ReactElement }) {
- const calendarContextValue = useCalendarContextValue();
- return (
-
- {props.children}
-
- );
-}
-
-/**
- * Access to the props passed to `DayPicker`, with some meaningful defaults.
- *
- * Use this hook from the custom components passed via the `components` prop.
- *
- * @group Hooks
- * @see https://daypicker.dev/advanced-guides/custom-components
- */
-export function useCalendar() {
- const calendarContext = useContext(CalendarContext);
- if (!calendarContext) {
- throw new Error(
- "useCalendar() must be used within a CalendarContextProvider"
- );
- }
- return calendarContext;
-}
diff --git a/src/contexts/useFocus.test.tsx b/src/contexts/useFocus.test.tsx
deleted file mode 100644
index c3b27f0f36..0000000000
--- a/src/contexts/useFocus.test.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { renderHook } from "@/test/renderHook";
-
-import { useFocus } from "./useFocus";
-
-const month = new Date(2020, 0, 1);
-const today = new Date(2020, 0, 14);
-
-describe("autoFocusTarget", () => {
- describe("when not in interactive", () => {
- test("the auto focus target is undefined", () => {
- const { result } = renderHook(useFocus, { month, today });
- expect(result.current.autoFocusTarget).toBeUndefined();
- });
- });
- describe("when in selection mode", () => {
- test("the autofocus target should be today", () => {
- const { result } = renderHook(useFocus, {
- month,
- today,
- mode: "single"
- });
- expect(result.current.autoFocusTarget?.date).toEqual(
- new Date(2020, 0, 14)
- );
- });
- describe("if today is disabled", () => {
- test("the autofocus target should be the first focusable day (the 1st of month)", () => {
- const { result } = renderHook(useFocus, {
- month,
- today,
- mode: "single",
- disabled: [today]
- });
- expect(result.current.autoFocusTarget?.date).toEqual(month);
- });
- });
- });
-});
diff --git a/src/contexts/useFocus.tsx b/src/contexts/useFocus.tsx
deleted file mode 100644
index 997114b6cd..0000000000
--- a/src/contexts/useFocus.tsx
+++ /dev/null
@@ -1,186 +0,0 @@
-import React, {
- ReactNode,
- createContext,
- useContext,
- useEffect,
- useState
-} from "react";
-
-import { DayFlag } from "../UI.js";
-import type { CalendarDay } from "../classes/index.js";
-import { getNextFocus } from "../helpers/getNextFocus.js";
-import type { MoveFocusBy, MoveFocusDir, Mode } from "../types/index.js";
-
-import { useCalendar } from "./useCalendar.js";
-import { useModifiers } from "./useModifiers.js";
-import { useProps } from "./useProps.js";
-
-const FocusContext = createContext(undefined);
-
-export type FocusContextValue = {
- /** The date that is currently focused. */
- focused: CalendarDay | undefined;
- /**
- * The date that is target of the focus when tabbing into the month grid. The
- * focus target is the selected date first, then the today date, then the
- * first focusable date.
- *
- * @private
- */
- autoFocusTarget: CalendarDay | undefined;
- /**
- * True if the focus is set by the autoFocus prop.
- *
- * @private
- */
- initiallyFocused: boolean;
- /** Focus the given day. */
- focus: (day: CalendarDay | undefined) => void;
- /**
- * Set the last focused day.
- *
- * @private
- */
- setLastFocused: (day: CalendarDay | undefined) => void;
-
- /** Blur the focused day. */
- blur: () => void;
- /** Focus the day after the focused day. */
- focusDayAfter: () => void;
- /** Focus the day before the focused day. */
- focusDayBefore: () => void;
- /** Focus the day in the week before the focused day. */
- focusWeekBefore: () => void;
- /** Focus the day in the week after the focused day. */
- focusWeekAfter: () => void;
- /* Focus the day in the month before the focused day. */
- focusMonthBefore: () => void;
- /* Focus the day in the month after the focused day. */
- focusMonthAfter: () => void;
- /* Focus the day in the year before the focused day. */
- focusYearBefore: () => void;
- /* Focus the day in the year after the focused day. */
- focusYearAfter: () => void;
- /* Focus the day at the start of the week of the focused day. */
- focusStartOfWeek: () => void;
- /* Focus the day at the end of the week of focused day. */
- focusEndOfWeek: () => void;
-};
-
-function useFocusContextValue(): FocusContextValue {
- const { goToDay, isDayDisplayed, isInteractive } = useCalendar();
-
- const props = useProps();
- const { autoFocus } = props;
- const {
- dayFlags: internal,
- selectionStates: selection,
- getModifiers
- } = useModifiers();
-
- const [focused, focus] = useState();
- const [lastFocused, setLastFocused] = useState();
- const [initiallyFocused, setInitiallyFocused] = useState(false);
-
- const today = internal.today[0];
-
- let autoFocusTarget: CalendarDay | undefined;
-
- const isValidFocusTarget = (day: CalendarDay) => {
- return isDayDisplayed(day) && !getModifiers(day)[DayFlag.disabled];
- };
-
- if (isInteractive) {
- if (focused) {
- autoFocusTarget = focused;
- } else if (lastFocused) {
- autoFocusTarget = lastFocused;
- } else if (
- selection.selected[0] &&
- isValidFocusTarget(selection.selected[0])
- ) {
- autoFocusTarget = selection.selected[0];
- } else if (today && isValidFocusTarget(today)) {
- autoFocusTarget = today;
- } else if (internal.focusable[0]) {
- autoFocusTarget = internal.focusable[0];
- }
- }
-
- // Focus the focus target when autoFocus is passed in
- useEffect(() => {
- if (!autoFocus) return;
- if (!autoFocusTarget) return;
- if (!initiallyFocused) return;
- // TODO: bug here?
- setInitiallyFocused(true);
- focus(autoFocusTarget);
- }, [autoFocus, autoFocusTarget, focused, initiallyFocused]);
-
- const blur = () => {
- setLastFocused(focused);
- focus(undefined);
- };
-
- function moveFocus(moveBy: MoveFocusBy, moveDir: MoveFocusDir) {
- if (!focused) return;
- const nextFocus = getNextFocus(moveBy, moveDir, focused, props);
- if (!nextFocus) return;
-
- goToDay(nextFocus);
- focus(nextFocus);
- }
-
- const contextValue: FocusContextValue = {
- autoFocusTarget,
- initiallyFocused,
- focus,
- focused,
- setLastFocused,
- blur,
- focusDayAfter: () => moveFocus("day", "after"),
- focusDayBefore: () => moveFocus("day", "before"),
- focusWeekAfter: () => moveFocus("week", "after"),
- focusWeekBefore: () => moveFocus("week", "before"),
- focusMonthBefore: () => moveFocus("month", "before"),
- focusMonthAfter: () => moveFocus("month", "after"),
- focusYearBefore: () => moveFocus("year", "before"),
- focusYearAfter: () => moveFocus("year", "after"),
- focusStartOfWeek: () => moveFocus("startOfWeek", "before"),
- focusEndOfWeek: () => moveFocus("endOfWeek", "after")
- };
-
- return contextValue;
-}
-
-/** @private */
-export function FocusContextProvider<
- ModeType extends Mode | undefined = undefined,
- IsRequired extends boolean = false
->({ children }: { children: ReactNode }) {
- const focusContextValue = useFocusContextValue();
-
- return (
-
- {children}
-
- );
-}
-
-/**
- * Share the focused day and the methods to move the focus.
- *
- * Use this hook from the custom components passed via the `components` prop.
- *
- * @group Hooks
- * @see https://daypicker.dev/advanced-guides/custom-components
- */
-export function useFocus() {
- const propsContext = useContext(FocusContext);
- if (!propsContext) {
- throw new Error(
- "useFocusContext() must be used within a FocusContextProvider"
- );
- }
- return propsContext;
-}
diff --git a/src/contexts/useProps.tsx b/src/contexts/useProps.tsx
deleted file mode 100644
index 07acfd2217..0000000000
--- a/src/contexts/useProps.tsx
+++ /dev/null
@@ -1,128 +0,0 @@
-import React from "react";
-
-import * as customComponents from "../components/custom-components.js";
-import { getDataAttributes } from "../helpers/getDataAttributes.js";
-import { getDefaultClassNames } from "../helpers/getDefaultClassNames.js";
-import { getFormatters } from "../helpers/getFormatters.js";
-import { getStartEndMonths } from "../helpers/getStartEndMonths.js";
-import * as defaultLabels from "../labels/index.js";
-import { dateLib as defaultDateLib } from "../lib/index.js";
-import type {
- ClassNames,
- CustomComponents,
- DataAttributes,
- Formatters,
- Labels,
- Mode,
- DayPickerProps,
- DateLib
-} from "../types/index.js";
-
-const PropsContext = React.createContext(
- undefined
-);
-
-export type PropsContextValue = DayPickerProps & {
- /** The mode of the selection. */
- mode: Mode | undefined;
- /** The class names to add to the UI. */
- classNames: ClassNames;
- /** The unique ID of the DayPicker instance. */
- id: string;
- dateLib: DateLib;
- /** The data attributes to add to the calendar. */
- dataAttributes: DataAttributes;
- /** The components used in the UI. */
- components: CustomComponents;
- /** The formatters used in the UI. */
- formatters: Formatters;
- /** The labels used in the UI. */
- labels: Labels;
- /** The number of months displayed in the calendar. */
- numberOfMonths: number;
- /** The date of today. */
- today: Date;
- /** The month where the navigation starts. */
- startMonth: Date | undefined;
- /** The month where the navigation ends. */
- endMonth: Date | undefined;
-};
-
-function usePropsContextValue(initialProps: DayPickerProps) {
- const reactId = React.useId();
-
- const { startMonth, endMonth } = getStartEndMonths(initialProps);
-
- const dateLib = {
- ...defaultDateLib,
- ...initialProps.dateLib
- };
-
- const propsContext: PropsContextValue = {
- ...initialProps,
- startMonth,
- endMonth,
- classNames: {
- ...getDefaultClassNames(),
- ...initialProps.classNames
- },
- components: {
- ...customComponents,
- ...initialProps.components
- },
- dataAttributes: getDataAttributes(initialProps),
- formatters: getFormatters(initialProps.formatters),
- id: initialProps.id ?? reactId,
- labels: {
- ...defaultLabels,
- ...initialProps.labels
- },
- numberOfMonths: initialProps.numberOfMonths ?? 1,
- today: initialProps.today ?? new dateLib.Date(),
- dateLib
- };
-
- return propsContext;
-}
-
-/**
- * Provide the shared props to the DayPicker components. Must be used as root of
- * the other providers.
- *
- * @private
- */
-export function PropsContextProvider<
- ModeType extends Mode | undefined = undefined,
- IsRequired extends boolean = false
->({
- initialProps,
- children
-}: React.PropsWithChildren<{
- initialProps: DayPickerProps;
-}>) {
- const propsContextValue = usePropsContextValue(initialProps);
-
- return (
-
- {children}
-
- );
-}
-
-/**
- * Access to the props passed to `DayPicker`, with some meaningful defaults.
- *
- * Use this hook from the custom components passed via the `components` prop.
- *
- * @group Hooks
- * @see https://daypicker.dev/advanced-guides/custom-components
- */
-export function useProps() {
- const propsContext = React.useContext(PropsContext);
- if (!propsContext) {
- throw new Error(
- "usePropsContext() must be used within a PropsContextProvider"
- );
- }
- return propsContext;
-}
diff --git a/src/formatters/formatWeekNumber.ts b/src/formatters/formatWeekNumber.ts
index 15dede6e7c..fc3bb86ef4 100644
--- a/src/formatters/formatWeekNumber.ts
+++ b/src/formatters/formatWeekNumber.ts
@@ -1,8 +1,16 @@
/**
* The default formatter for the week numbers.
*
+ * @param weekNumber - The week number to format. `0` when heading of the week
+ * numbers.
* @group Formatters
*/
-export function formatWeekNumber(weekNumber: number) {
+export function formatWeekNumber(weekNumber: number | 0) {
+ if (weekNumber === 0) {
+ return `#`;
+ }
+ if (weekNumber < 10) {
+ return `0${weekNumber.toLocaleString()}`;
+ }
return `${weekNumber.toLocaleString()}`;
}
diff --git a/src/formatters/formatWeekNumberHeader.ts b/src/formatters/formatWeekNumberHeader.ts
new file mode 100644
index 0000000000..f429bc9a09
--- /dev/null
+++ b/src/formatters/formatWeekNumberHeader.ts
@@ -0,0 +1,8 @@
+/**
+ * The default formatter for the week numbers header.
+ *
+ * @group Formatters
+ */
+export function formatWeekNumberHeader() {
+ return ``;
+}
diff --git a/src/formatters/index.ts b/src/formatters/index.ts
index 2221e54317..9e29719d40 100644
--- a/src/formatters/index.ts
+++ b/src/formatters/index.ts
@@ -2,5 +2,6 @@ export * from "./formatCaption.js";
export * from "./formatDay.js";
export * from "./formatMonthDropdown.js";
export * from "./formatWeekNumber.js";
+export * from "./formatWeekNumberHeader.js";
export * from "./formatWeekdayName.js";
export * from "./formatYearDropdown.js";
diff --git a/src/helpers/calculateFocusTarget.ts b/src/helpers/calculateFocusTarget.ts
new file mode 100644
index 0000000000..343454eed6
--- /dev/null
+++ b/src/helpers/calculateFocusTarget.ts
@@ -0,0 +1,48 @@
+import { DayFlag } from "../UI.js";
+import type { CalendarDay } from "../classes/index.js";
+import type { Modifiers } from "../types/index.js";
+import { UseCalendar } from "../useCalendar.js";
+
+export function calculateFocusTarget(
+ calendar: UseCalendar,
+ getModifiers: (day: CalendarDay) => Modifiers,
+ isSelected: (date: Date) => boolean,
+ lastFocused: CalendarDay | undefined
+) {
+ let focusTarget: CalendarDay | undefined;
+
+ let index = 0;
+ let found = false;
+
+ while (index < calendar.days.length && !found) {
+ const day = calendar.days[index];
+ const m = getModifiers(day);
+
+ if (!m[DayFlag.disabled] && !m[DayFlag.hidden] && !m[DayFlag.outside]) {
+ if (m[DayFlag.focused]) {
+ focusTarget = day;
+ found = true;
+ } else if (lastFocused?.isEqualTo(day)) {
+ focusTarget = day;
+ found = true;
+ } else if (isSelected(day.date)) {
+ focusTarget = day;
+ found = true;
+ } else if (m[DayFlag.today]) {
+ focusTarget = day;
+ found = true;
+ }
+ }
+
+ index++;
+ }
+
+ if (!focusTarget) {
+ // return the first day that is focusable
+ focusTarget = calendar.days.find((day) => {
+ const m = getModifiers(day);
+ return !m[DayFlag.disabled] && !m[DayFlag.hidden] && !m[DayFlag.outside];
+ });
+ }
+ return focusTarget;
+}
diff --git a/src/helpers/debounce.test.ts b/src/helpers/debounce.test.ts
deleted file mode 100644
index 97f7eddb3a..0000000000
--- a/src/helpers/debounce.test.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { debounce } from "./debounce";
-
-describe("debounce", () => {
- jest.useFakeTimers();
- let functionToDebounce: jest.Mock;
-
- beforeEach(() => {
- functionToDebounce = jest.fn();
- });
-
- test("only execute the function after the specified wait time", () => {
- const debouncedFunction = debounce(functionToDebounce, 1000);
- debouncedFunction();
- expect(functionToDebounce).not.toHaveBeenCalled();
- jest.runAllTimers();
- expect(functionToDebounce).toHaveBeenCalled();
- });
-
- test("call the debounced function with the correct arguments", () => {
- const debouncedFunction = debounce(functionToDebounce, 1000);
- const args = ["arg1", "arg2"];
-
- debouncedFunction(...args);
- jest.runAllTimers();
-
- expect(functionToDebounce).toHaveBeenCalledWith(...args);
- });
-
- test("cancel the previous timer and start a new one on subsequent calls", () => {
- const debouncedFunction = debounce(functionToDebounce, 1000);
- debouncedFunction();
- debouncedFunction();
- jest.advanceTimersByTime(500);
- expect(functionToDebounce).not.toHaveBeenCalled();
- jest.advanceTimersByTime(1000);
- expect(functionToDebounce).toHaveBeenCalledTimes(1);
- });
- afterEach(() => {
- jest.clearAllTimers();
- });
-});
diff --git a/src/helpers/debounce.ts b/src/helpers/debounce.ts
deleted file mode 100644
index 8a455df0ca..0000000000
--- a/src/helpers/debounce.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// eslint-disable-next-line @typescript-eslint/ban-types
-export function debounce(func: Function, wait: number) {
- let timeout: NodeJS.Timeout;
-
- return (...args: unknown[]) => {
- clearTimeout(timeout);
- timeout = setTimeout(() => func(...args), wait);
- };
-}
diff --git a/src/helpers/getClassNamesForModifiers.ts b/src/helpers/getClassNamesForModifiers.ts
index fbeee8f4c6..055ff04d07 100644
--- a/src/helpers/getClassNamesForModifiers.ts
+++ b/src/helpers/getClassNamesForModifiers.ts
@@ -3,8 +3,8 @@ import type { ModifiersClassNames, ClassNames } from "../types/index.js";
export function getClassNamesForModifiers(
modifiers: Record,
- modifiersClassNames: ModifiersClassNames,
- classNames: ClassNames
+ classNames: ClassNames,
+ modifiersClassNames: ModifiersClassNames = {}
) {
const modifierClassNames = Object.entries(modifiers)
.filter(([, active]) => active === true)
diff --git a/src/helpers/getComponents.ts b/src/helpers/getComponents.ts
new file mode 100644
index 0000000000..3df5dc180a
--- /dev/null
+++ b/src/helpers/getComponents.ts
@@ -0,0 +1,11 @@
+import * as components from "../components/custom-components.js";
+import type { CustomComponents, DayPickerProps } from "../types/index.js";
+
+export function getComponents(
+ customComponents: DayPickerProps["components"]
+): Required {
+ return {
+ ...components,
+ ...customComponents
+ };
+}
diff --git a/src/helpers/getDataAttributes.tsx b/src/helpers/getDataAttributes.tsx
index 04ef1d5e4d..4311c6923c 100644
--- a/src/helpers/getDataAttributes.tsx
+++ b/src/helpers/getDataAttributes.tsx
@@ -4,7 +4,10 @@ import type { DayPickerProps } from "../types/index.js";
export function getDataAttributes(
props: DayPickerProps
): Record {
- const dataAttributes: Record = {};
+ const dataAttributes: Record = {
+ "data-mode": props.mode ?? undefined,
+ "data-required": "required" in props ? props.required : undefined
+ };
Object.entries(props).forEach(([key, val]) => {
if (key.startsWith("data-")) {
dataAttributes[key] = val;
diff --git a/src/helpers/getDateLib.ts b/src/helpers/getDateLib.ts
new file mode 100644
index 0000000000..c236f7bc98
--- /dev/null
+++ b/src/helpers/getDateLib.ts
@@ -0,0 +1,9 @@
+import { dateLib } from "../lib/dateLib.js";
+import type { DateLib, DayPickerProps } from "../types/index.js";
+
+export function getDateLib(customLib: DayPickerProps["dateLib"]): DateLib {
+ return {
+ ...dateLib,
+ ...customLib
+ };
+}
diff --git a/src/helpers/getDates.test.ts b/src/helpers/getDates.test.ts
index f4773814f9..40cd1a05bc 100644
--- a/src/helpers/getDates.test.ts
+++ b/src/helpers/getDates.test.ts
@@ -7,10 +7,14 @@ describe("when the first month and the last month are the same", () => {
const month = new Date(2023, 11, 1);
describe("when not using fixed weeks", () => {
it("should return 42 dates", () => {
- const dates = getDates([month], undefined, {
- fixedWeeks: false,
+ const dates = getDates(
+ [month],
+ undefined,
+ {
+ fixedWeeks: false
+ },
dateLib
- });
+ );
expect(dates).toHaveLength(42);
expect(dates[0]).toEqual(new Date(2023, 10, 26));
expect(dates[dates.length - 1]).toEqual(new Date(2024, 0, 6));
@@ -18,10 +22,14 @@ describe("when the first month and the last month are the same", () => {
});
describe("when using fixed weeks", () => {
it("should return 42 dates", () => {
- const dates = getDates([month], undefined, {
- fixedWeeks: true,
+ const dates = getDates(
+ [month],
+ undefined,
+ {
+ fixedWeeks: true
+ },
dateLib
- });
+ );
expect(dates).toHaveLength(42);
expect(dates[0]).toEqual(new Date(2023, 10, 26));
expect(dates[dates.length - 1]).toEqual(new Date(2024, 0, 6));
@@ -32,10 +40,14 @@ describe("when the first month and the last month are the same", () => {
const month = new Date(2023, 4, 1);
describe("when not using fixed weeks", () => {
it("should return 35 dates", () => {
- const dates = getDates([month], undefined, {
- fixedWeeks: false,
+ const dates = getDates(
+ [month],
+ undefined,
+ {
+ fixedWeeks: false
+ },
dateLib
- });
+ );
expect(dates).toHaveLength(35);
expect(dates[0]).toEqual(new Date(2023, 3, 30));
expect(dates[dates.length - 1]).toEqual(new Date(2023, 5, 3));
@@ -43,10 +55,14 @@ describe("when the first month and the last month are the same", () => {
});
describe("when using fixed weeks", () => {
it("should return 42 dates", () => {
- const dates = getDates([month], undefined, {
- fixedWeeks: true,
+ const dates = getDates(
+ [month],
+ undefined,
+ {
+ fixedWeeks: true
+ },
dateLib
- });
+ );
expect(dates).toHaveLength(42);
expect(dates[0]).toEqual(new Date(2023, 3, 30));
expect(dates[dates.length - 1]).toEqual(new Date(2023, 5, 10));
@@ -56,10 +72,14 @@ describe("when the first month and the last month are the same", () => {
describe("when using Monday as first day of the week", () => {
const month = new Date(2023, 4, 1);
it("the first day should be Monday", () => {
- const dates = getDates([month], undefined, {
- weekStartsOn: 1,
+ const dates = getDates(
+ [month],
+ undefined,
+ {
+ weekStartsOn: 1
+ },
dateLib
- });
+ );
expect(dates[0]).toBeMonday();
expect(dates[0]).toEqual(new Date(2023, 4, 1));
expect(dates[dates.length - 1]).toEqual(new Date(2023, 5, 4));
@@ -70,7 +90,7 @@ describe("when the first month and the last month are the same", () => {
const maxDate = new Date(2023, 4, 15);
it("the last day should be the max date", () => {
- const dates = getDates([month], maxDate, { weekStartsOn: 1, dateLib });
+ const dates = getDates([month], maxDate, { weekStartsOn: 1 }, dateLib);
expect(dates).toHaveLength(15);
expect(dates[dates.length - 1]).toEqual(maxDate);
});
@@ -78,7 +98,7 @@ describe("when the first month and the last month are the same", () => {
describe("when using ISO weeks", () => {
const month = new Date(2023, 4, 1);
it("the first day should be Monday", () => {
- const dates = getDates([month], undefined, { ISOWeek: true, dateLib });
+ const dates = getDates([month], undefined, { ISOWeek: true }, dateLib);
expect(dates[0]).toBeMonday();
expect(dates[0]).toEqual(new Date(2023, 4, 1));
expect(dates[dates.length - 1]).toEqual(new Date(2023, 5, 4));
@@ -91,10 +111,14 @@ describe("when the first month and the last month are different", () => {
const lastMonth = new Date(2023, 11, 1);
describe("when not using fixed weeks", () => {
it("should return an array of dates", () => {
- const dates = getDates([firstMonth, lastMonth], undefined, {
- fixedWeeks: false,
+ const dates = getDates(
+ [firstMonth, lastMonth],
+ undefined,
+ {
+ fixedWeeks: false
+ },
dateLib
- });
+ );
expect(dates).toHaveLength(252);
expect(dates[0]).toEqual(new Date(2023, 3, 30));
expect(dates[dates.length - 1]).toEqual(new Date(2024, 0, 6));
@@ -106,10 +130,14 @@ describe("when the first month and the last month are different", () => {
const maxDate = new Date(2023, 5, 15);
it("the last day should be the max date", () => {
- const dates = getDates([firstMonth, lastMonth], maxDate, {
- weekStartsOn: 1,
+ const dates = getDates(
+ [firstMonth, lastMonth],
+ maxDate,
+ {
+ weekStartsOn: 1
+ },
dateLib
- });
+ );
expect(dates).toHaveLength(46);
expect(dates[dates.length - 1]).toEqual(maxDate);
});
@@ -117,7 +145,7 @@ describe("when the first month and the last month are different", () => {
describe("when using ISO weeks", () => {
const month = new Date(2023, 4, 1);
it("the first day should be Monday", () => {
- const dates = getDates([month], undefined, { ISOWeek: true, dateLib });
+ const dates = getDates([month], undefined, { ISOWeek: true }, dateLib);
expect(dates[0]).toBeMonday();
expect(dates[0]).toEqual(new Date(2023, 4, 1));
expect(dates[dates.length - 1]).toEqual(new Date(2023, 5, 4));
diff --git a/src/helpers/getDates.ts b/src/helpers/getDates.ts
index aacea768b5..8de46d5e52 100644
--- a/src/helpers/getDates.ts
+++ b/src/helpers/getDates.ts
@@ -1,4 +1,4 @@
-import { PropsContextValue } from "../contexts/index.js";
+import { DateLib, DayPickerProps } from "../index.js";
/** The number of days in a month when having 6 weeks. */
const NrOfDaysWithFixedWeeks = 42;
@@ -8,9 +8,10 @@ export function getDates(
displayMonths: Date[],
maxDate: Date | undefined,
props: Pick<
- PropsContextValue,
- "ISOWeek" | "fixedWeeks" | "locale" | "weekStartsOn" | "dateLib"
- >
+ DayPickerProps,
+ "ISOWeek" | "fixedWeeks" | "locale" | "weekStartsOn"
+ >,
+ dateLib: DateLib
): Date[] {
const firstMonth = displayMonths[0];
const lastMonth = displayMonths[displayMonths.length - 1];
@@ -27,7 +28,7 @@ export function getDates(
isAfter,
endOfMonth,
Date
- } = props.dateLib;
+ } = dateLib;
const startWeekFirstDate = ISOWeek
? startOfISOWeek(firstMonth)
diff --git a/src/helpers/getDefaultClassNames.test.ts b/src/helpers/getDefaultClassNames.test.ts
index caa9bb59b0..142f5b52f2 100644
--- a/src/helpers/getDefaultClassNames.test.ts
+++ b/src/helpers/getDefaultClassNames.test.ts
@@ -4,30 +4,25 @@ test("should return the default classnames", () => {
expect(getDefaultClassNames()).toStrictEqual({
button_next: "rdp-button_next",
button_previous: "rdp-button_previous",
- calendar: "rdp-calendar",
+ root: "rdp-root",
caption_label: "rdp-caption_label",
chevron: "rdp-chevron",
- chevron_disabled: "rdp-chevron_disabled",
day: "rdp-day",
- day_date: "rdp-day_date",
+ day_button: "rdp-day_button",
disabled: "rdp-disabled",
dropdown_nav: "rdp-dropdown_nav",
dropdown_root: "rdp-dropdown_root",
dropdown: "rdp-dropdown",
- focusable: "rdp-focusable",
focused: "rdp-focused",
footer: "rdp-footer",
- has_multiple_months: "rdp-has_multiple_months",
- has_week_numbers: "rdp-has_week_numbers",
hidden: "rdp-hidden",
month_caption: "rdp-month_caption",
- month_wrapper: "rdp-month_wrapper",
+ month_grid: "rdp-month_grid",
month: "rdp-month",
weeks: "rdp-weeks",
months_dropdown: "rdp-months_dropdown",
months: "rdp-months",
nav: "rdp-nav",
- no_weekdays: "rdp-no_weekdays",
outside: "rdp-outside",
range_end: "rdp-range_end",
range_middle: "rdp-range_middle",
@@ -35,7 +30,7 @@ test("should return the default classnames", () => {
selected: "rdp-selected",
today: "rdp-today",
week_number: "rdp-week_number",
- week_number_interactive: "rdp-week_number_interactive",
+ week_number_header: "rdp-week_number_header",
week: "rdp-week",
weekday: "rdp-weekday",
weekdays: "rdp-weekdays",
diff --git a/src/helpers/getDefaultClassNames.ts b/src/helpers/getDefaultClassNames.ts
index 1c216293a4..49a0ced59b 100644
--- a/src/helpers/getDefaultClassNames.ts
+++ b/src/helpers/getDefaultClassNames.ts
@@ -1,11 +1,4 @@
-import {
- UI,
- DayFlag,
- CalendarFlag,
- ChevronFlag,
- WeekNumberFlag,
- SelectionState
-} from "../UI.js";
+import { UI, DayFlag, SelectionState } from "../UI.js";
import type { ClassNames } from "../types/index.js";
/**
@@ -21,21 +14,6 @@ export function getDefaultClassNames(): Required {
`rdp-${UI[key as keyof typeof UI]}`;
}
- for (const key in CalendarFlag) {
- classNames[CalendarFlag[key as keyof typeof CalendarFlag]] =
- `rdp-${CalendarFlag[key as keyof typeof CalendarFlag]}`;
- }
-
- for (const key in ChevronFlag) {
- classNames[ChevronFlag[key as keyof typeof ChevronFlag]] =
- `rdp-${ChevronFlag[key as keyof typeof ChevronFlag]}`;
- }
-
- for (const key in WeekNumberFlag) {
- classNames[WeekNumberFlag[key as keyof typeof WeekNumberFlag]] =
- `rdp-${WeekNumberFlag[key as keyof typeof WeekNumberFlag]}`;
- }
-
for (const key in DayFlag) {
classNames[DayFlag[key as keyof typeof DayFlag]] =
`rdp-${DayFlag[key as keyof typeof DayFlag]}`;
diff --git a/src/helpers/getDisplayMonths.test.ts b/src/helpers/getDisplayMonths.test.ts
index ff96e0cfcb..320851d3e5 100644
--- a/src/helpers/getDisplayMonths.test.ts
+++ b/src/helpers/getDisplayMonths.test.ts
@@ -6,11 +6,7 @@ describe("getDisplayMonths", () => {
it("should return the months to display in the calendar", () => {
const firstMonth = new Date(2020, 0);
const expectedResult = [new Date(2020, 0)];
- const result = getDisplayMonths(firstMonth, {
- numberOfMonths: 1,
- endMonth: undefined,
- dateLib
- });
+ const result = getDisplayMonths(firstMonth, undefined, {}, dateLib);
expect(result).toEqual(expectedResult);
});
@@ -21,22 +17,28 @@ describe("getDisplayMonths", () => {
new Date(2020, 1),
new Date(2020, 2)
];
- const result = getDisplayMonths(firstMonth, {
- numberOfMonths: 3,
- endMonth: undefined,
+ const result = getDisplayMonths(
+ firstMonth,
+ undefined,
+ {
+ numberOfMonths: 3
+ },
dateLib
- });
+ );
expect(result).toEqual(expectedResult);
});
it("should return the months to display in the calendar when passing a max date", () => {
const firstMonth = new Date(2020, 0);
const expectedResult = [new Date(2020, 0), new Date(2020, 1)];
- const result = getDisplayMonths(firstMonth, {
- numberOfMonths: 3,
- endMonth: new Date(2020, 1, 10),
+ const result = getDisplayMonths(
+ firstMonth,
+ new Date(2020, 1, 10),
+ {
+ numberOfMonths: 3
+ },
dateLib
- });
+ );
expect(result).toEqual(expectedResult);
});
});
diff --git a/src/helpers/getDisplayMonths.ts b/src/helpers/getDisplayMonths.ts
index e03e2da854..e9faa52aa5 100644
--- a/src/helpers/getDisplayMonths.ts
+++ b/src/helpers/getDisplayMonths.ts
@@ -1,13 +1,16 @@
-import type { PropsContextValue } from "../contexts/index.js";
+import type { DateLib, DayPickerProps } from "../types/index.js";
export function getDisplayMonths(
firstDisplayedMonth: Date,
- props: Pick
+ calendarEndMonth: Date | undefined,
+ props: Pick,
+ dateLib: DateLib
) {
+ const { numberOfMonths = 1 } = props;
const months: Date[] = [];
- for (let i = 0; i < props.numberOfMonths; i++) {
- const month = props.dateLib.addMonths(firstDisplayedMonth, i);
- if (props.endMonth && month > props.endMonth) {
+ for (let i = 0; i < numberOfMonths; i++) {
+ const month = dateLib.addMonths(firstDisplayedMonth, i);
+ if (calendarEndMonth && month > calendarEndMonth) {
break;
}
months.push(month);
diff --git a/src/helpers/getDropdownMonths.test.ts b/src/helpers/getDropdownMonths.test.ts
index 213645eb75..38a6de7183 100644
--- a/src/helpers/getDropdownMonths.test.ts
+++ b/src/helpers/getDropdownMonths.test.ts
@@ -13,13 +13,14 @@ test("return correct dropdown options", () => {
formatMonthDropdown: (month: number, locale?: Locale) =>
format(new Date(2022, month), "MMMM", { locale })
});
- const result = getDropdownMonths(displayMonth, {
- formatters,
- locale,
+ const result = getDropdownMonths(
+ displayMonth,
startMonth,
endMonth,
+ formatters,
+ locale,
dateLib
- });
+ );
expect(result).toEqual([
{ value: 0, label: "January", disabled: false },
diff --git a/src/helpers/getDropdownMonths.ts b/src/helpers/getDropdownMonths.ts
index 87b313c543..9016a011e6 100644
--- a/src/helpers/getDropdownMonths.ts
+++ b/src/helpers/getDropdownMonths.ts
@@ -1,23 +1,24 @@
import { DropdownOption } from "../components/Dropdown.js";
-import { PropsContextValue } from "../contexts/index.js";
+import type { Locale } from "../lib/dateLib.js";
+import type { DateLib, Formatters } from "../types/index.js";
/** Return the months to show in the dropdown. */
export function getDropdownMonths(
displayMonth: Date,
- props: Pick<
- PropsContextValue,
- "formatters" | "locale" | "startMonth" | "endMonth" | "dateLib"
- >
+ calendarStartMonth: Date | undefined,
+ calendarEndMonth: Date | undefined,
+ formatters: Pick,
+ locale: Locale | undefined,
+ dateLib: DateLib
): DropdownOption[] | undefined {
- const { startMonth, endMonth } = props;
- if (!startMonth) return undefined;
- if (!endMonth) return undefined;
+ if (!calendarStartMonth) return undefined;
+ if (!calendarEndMonth) return undefined;
- const { addMonths, startOfMonth, isBefore, Date } = props.dateLib;
+ const { addMonths, startOfMonth, isBefore, Date } = dateLib;
const year = displayMonth.getFullYear();
- const navStartMonth = startOfMonth(startMonth);
- const navEndMonth = startOfMonth(endMonth);
+ const navStartMonth = startOfMonth(calendarStartMonth);
+ const navEndMonth = startOfMonth(calendarEndMonth);
const months: number[] = [];
let month = navStartMonth;
@@ -29,10 +30,12 @@ export function getDropdownMonths(
return a - b;
});
const options = sortedMonths.map((value) => {
- const label = props.formatters.formatMonthDropdown(value, props.locale);
+ const label = formatters.formatMonthDropdown(value, locale);
const disabled =
- (startMonth && new Date(year, value) < startOfMonth(startMonth)) ||
- (endMonth && new Date(year, value) > startOfMonth(endMonth)) ||
+ (calendarStartMonth &&
+ new Date(year, value) < startOfMonth(calendarStartMonth)) ||
+ (calendarEndMonth &&
+ new Date(year, value) > startOfMonth(calendarEndMonth)) ||
false;
return { value, label, disabled };
});
diff --git a/src/helpers/getDropdownYears.test.ts b/src/helpers/getDropdownYears.test.ts
index 939e09d7e2..a97888b66a 100644
--- a/src/helpers/getDropdownYears.test.ts
+++ b/src/helpers/getDropdownYears.test.ts
@@ -1,4 +1,3 @@
-import { enUS as locale } from "date-fns/locale";
import { dateLib } from "react-day-picker";
import { getDropdownYears } from "./getDropdownYears";
@@ -9,20 +8,20 @@ test("return undefined if startMonth or endMonth is not provided", () => {
const formatters = getFormatters({
formatYearDropdown: (year: number) => `${year}`
});
- const result1 = getDropdownYears(displayMonth, {
+ const result1 = getDropdownYears(
+ displayMonth,
+ undefined,
+ new Date(2022, 11, 31),
formatters,
- locale,
- startMonth: undefined,
- endMonth: new Date(2022, 11, 31),
dateLib
- });
- const result2 = getDropdownYears(displayMonth, {
+ );
+ const result2 = getDropdownYears(
+ displayMonth,
+ new Date(2022, 0, 1),
+ undefined,
formatters,
- locale,
- startMonth: new Date(2022, 0, 1),
- endMonth: undefined,
dateLib
- });
+ );
expect(result1).toBeUndefined();
expect(result2).toBeUndefined();
@@ -36,13 +35,13 @@ test("return correct dropdown options", () => {
formatYearDropdown: (year: number) => `${year}`
});
- const result = getDropdownYears(displayMonth, {
- formatters,
- locale,
+ const result = getDropdownYears(
+ displayMonth,
startMonth,
endMonth,
+ formatters,
dateLib
- });
+ );
expect(result).toEqual([
{ value: 2022, label: "2022", disabled: false },
diff --git a/src/helpers/getDropdownYears.ts b/src/helpers/getDropdownYears.ts
index dc598fee3a..427e3096e6 100644
--- a/src/helpers/getDropdownYears.ts
+++ b/src/helpers/getDropdownYears.ts
@@ -1,17 +1,16 @@
import { DropdownOption } from "../components/Dropdown.js";
-import { PropsContextValue } from "../contexts/index.js";
+import type { DateLib, Formatters } from "../types/index.js";
/** Return the years to show in the dropdown. */
export function getDropdownYears(
displayMonth: Date,
- props: Pick<
- PropsContextValue,
- "formatters" | "locale" | "startMonth" | "endMonth" | "dateLib"
- >
+ calendarStart: Date | undefined,
+ calendarEnd: Date | undefined,
+ formatters: Pick,
+ dateLib: DateLib
): DropdownOption[] | undefined {
- const { startMonth, endMonth } = props;
- if (!startMonth) return undefined;
- if (!endMonth) return undefined;
+ if (!calendarStart) return undefined;
+ if (!calendarEnd) return undefined;
const {
startOfMonth,
startOfYear,
@@ -20,10 +19,10 @@ export function getDropdownYears(
isBefore,
isSameYear,
Date
- } = props.dateLib;
+ } = dateLib;
const month = displayMonth.getMonth();
- const firstNavYear = startOfYear(startMonth);
- const lastNavYear = endOfYear(endMonth);
+ const firstNavYear = startOfYear(calendarStart);
+ const lastNavYear = endOfYear(calendarEnd);
const years: number[] = [];
let year = firstNavYear;
@@ -34,10 +33,12 @@ export function getDropdownYears(
return years.map((value) => {
const disabled =
- (startMonth && new Date(value, month) < startOfMonth(startMonth)) ||
- (month && endMonth && new Date(value, month) > startOfMonth(endMonth)) ||
+ (calendarStart && new Date(value, month) < startOfMonth(calendarStart)) ||
+ (month &&
+ calendarEnd &&
+ new Date(value, month) > startOfMonth(calendarEnd)) ||
false;
- const label = props.formatters.formatYearDropdown(value);
+ const label = formatters.formatYearDropdown(value);
return {
value,
label,
diff --git a/src/helpers/getFocusableDate.ts b/src/helpers/getFocusableDate.ts
new file mode 100644
index 0000000000..648766f495
--- /dev/null
+++ b/src/helpers/getFocusableDate.ts
@@ -0,0 +1,51 @@
+import type {
+ DateLib,
+ DayPickerProps,
+ MoveFocusBy,
+ MoveFocusDir
+} from "../types/index.js";
+
+/** Return the next date that should be focused. */
+export function getFocusableDate(
+ moveBy: MoveFocusBy,
+ moveDir: MoveFocusDir,
+ refDate: Date,
+ navStart: Date | undefined,
+ navEnd: Date | undefined,
+ props: Pick,
+ dateLib: DateLib
+): Date {
+ const { weekStartsOn, locale, ISOWeek } = props;
+ const {
+ addDays,
+ addMonths,
+ addYears,
+ addWeeks,
+ startOfISOWeek,
+ endOfISOWeek,
+ startOfWeek,
+ endOfWeek,
+ max,
+ min
+ } = dateLib;
+ const moveFns = {
+ day: addDays,
+ week: addWeeks,
+ month: addMonths,
+ year: addYears,
+ startOfWeek: (date: Date) =>
+ ISOWeek
+ ? startOfISOWeek(date)
+ : startOfWeek(date, { locale, weekStartsOn }),
+ endOfWeek: (date: Date) =>
+ ISOWeek ? endOfISOWeek(date) : endOfWeek(date, { locale, weekStartsOn })
+ };
+
+ let focusableDate = moveFns[moveBy](refDate, moveDir === "after" ? 1 : -1);
+ if (moveDir === "before" && navStart) {
+ focusableDate = max([navStart, focusableDate]);
+ } else if (moveDir === "after" && navEnd) {
+ focusableDate = min([navEnd, focusableDate]);
+ }
+ return focusableDate;
+}
diff --git a/src/helpers/getInitialMonth.test.ts b/src/helpers/getInitialMonth.test.ts
index 02c6ea5e07..e208102ab3 100644
--- a/src/helpers/getInitialMonth.test.ts
+++ b/src/helpers/getInitialMonth.test.ts
@@ -7,41 +7,21 @@ describe("when no endMonth is given", () => {
describe("when month is in context", () => {
const month = new Date(2010, 11, 12);
it("return that month", () => {
- const startMonth = getInitialMonth({
- month,
- dateLib,
- startMonth: undefined,
- endMonth: undefined,
- today: new Date(),
- numberOfMonths: 0
- });
+ const startMonth = getInitialMonth({ month }, dateLib);
expect(isSameMonth(startMonth, month)).toBe(true);
});
});
describe("when defaultMonth is in context", () => {
const defaultMonth = new Date(2010, 11, 12);
it("return that month", () => {
- const startMonth = getInitialMonth({
- defaultMonth,
- startMonth: undefined,
- endMonth: undefined,
- today: new Date(),
- numberOfMonths: 0,
- dateLib
- });
+ const startMonth = getInitialMonth({ defaultMonth }, dateLib);
expect(isSameMonth(startMonth, defaultMonth)).toBe(true);
});
});
- describe("when no month or defaultMonth are in context", () => {
+ describe("when no month or defaultMonth", () => {
const today = new Date(2010, 11, 12);
it("return the today month", () => {
- const startMonth = getInitialMonth({
- today,
- startMonth: undefined,
- endMonth: undefined,
- numberOfMonths: 0,
- dateLib
- });
+ const startMonth = getInitialMonth({ today }, dateLib);
expect(isSameMonth(startMonth, today)).toBe(true);
});
});
@@ -51,31 +31,18 @@ describe("when endMonth is given", () => {
const month = new Date(2010, 11, 12);
const endMonth = addMonths(month, -2);
describe("when the number of month is 1", () => {
- const numberOfMonths = 1;
it("return the endMonth", () => {
- const startMonth = getInitialMonth({
- month,
- endMonth,
- numberOfMonths,
- startMonth: undefined,
- today: new Date(),
- dateLib
- });
+ const startMonth = getInitialMonth({ month, endMonth }, dateLib);
expect(isSameMonth(startMonth, endMonth)).toBe(true);
});
});
describe("when the number of month is 3", () => {
- const numberOfMonths = 3;
it("return the endMonth plus the number of months", () => {
- const startMonth = getInitialMonth({
- month,
- endMonth,
- numberOfMonths,
- startMonth: undefined,
- today: new Date(),
+ const startMonth = getInitialMonth(
+ { month, numberOfMonths: 3, endMonth },
dateLib
- });
- const expectedMonth = addMonths(endMonth, -1 * (numberOfMonths - 1));
+ );
+ const expectedMonth = addMonths(endMonth, -1 * (3 - 1));
expect(isSameMonth(startMonth, expectedMonth)).toBe(true);
});
});
diff --git a/src/helpers/getInitialMonth.ts b/src/helpers/getInitialMonth.ts
index 5362ebf215..ef699d2e1b 100644
--- a/src/helpers/getInitialMonth.ts
+++ b/src/helpers/getInitialMonth.ts
@@ -1,9 +1,9 @@
-import type { PropsContextValue } from "../contexts/index.js";
+import type { DateLib, DayPickerProps } from "../index.js";
/** Return the start month based on the props passed to DayPicker. */
export function getInitialMonth(
props: Pick<
- PropsContextValue,
+ DayPickerProps,
| "fromYear"
| "toYear"
| "startMonth"
@@ -12,20 +12,19 @@ export function getInitialMonth(
| "defaultMonth"
| "today"
| "numberOfMonths"
- | "dateLib"
- >
+ >,
+ dateLib: DateLib
): Date {
const {
month,
defaultMonth,
- dateLib: { Date },
- today,
+ today = new dateLib.Date(),
numberOfMonths = 1,
endMonth,
startMonth
} = props;
- let initialMonth = month || defaultMonth || today || new Date();
- const { differenceInCalendarMonths, addMonths, startOfMonth } = props.dateLib;
+ let initialMonth = month || defaultMonth || today;
+ const { differenceInCalendarMonths, addMonths, startOfMonth } = dateLib;
// Fix the initialMonth if is after the to-date
if (endMonth && differenceInCalendarMonths(endMonth, initialMonth) < 0) {
diff --git a/src/helpers/getLabels.ts b/src/helpers/getLabels.ts
new file mode 100644
index 0000000000..2f655e8823
--- /dev/null
+++ b/src/helpers/getLabels.ts
@@ -0,0 +1,10 @@
+import * as defaultLabels from "../labels/index.js";
+import type { DayPickerProps, Labels } from "../types/index.js";
+
+/** Return the formatters from the props merged with the default formatters. */
+export function getLabels(customLabels: DayPickerProps["labels"]): Labels {
+ return {
+ ...defaultLabels,
+ ...customLabels
+ };
+}
diff --git a/src/helpers/getMonths.test.ts b/src/helpers/getMonths.test.ts
index 1d04ca8267..482fbde292 100644
--- a/src/helpers/getMonths.test.ts
+++ b/src/helpers/getMonths.test.ts
@@ -1,5 +1,6 @@
+import { DayPickerProps } from "react-day-picker";
+
import { CalendarMonth } from "../classes";
-import type { PropsContextValue } from "../contexts/useProps";
import { dateLib } from "../lib";
import { getMonths } from "./getMonths";
@@ -15,28 +16,26 @@ const mockDates = [
];
const mockProps: Pick<
- PropsContextValue,
+ DayPickerProps,
| "fixedWeeks"
| "ISOWeek"
| "locale"
| "weekStartsOn"
| "reverseMonths"
| "firstWeekContainsDate"
- | "dateLib"
> = {
fixedWeeks: false,
ISOWeek: false,
locale: undefined,
weekStartsOn: 0, // Sunday
reverseMonths: false,
- firstWeekContainsDate: 1,
- dateLib
+ firstWeekContainsDate: 1
};
it("should return the correct months without ISO weeks and reverse months", () => {
const displayMonths = [new Date(2023, 5, 1)]; // June 2023
- const result = getMonths(displayMonths, mockDates, mockProps);
+ const result = getMonths(displayMonths, mockDates, mockProps, dateLib);
expect(result).toHaveLength(1);
expect(result[0]).toBeInstanceOf(CalendarMonth);
@@ -48,7 +47,7 @@ it("should handle ISO weeks", () => {
const isoProps = { ...mockProps, ISOWeek: true };
- const result = getMonths(displayMonths, mockDates, isoProps);
+ const result = getMonths(displayMonths, mockDates, isoProps, dateLib);
expect(result).toHaveLength(1);
expect(result[0]).toBeInstanceOf(CalendarMonth);
@@ -63,7 +62,7 @@ it("should handle reverse months", () => {
const reverseProps = { ...mockProps, reverseMonths: true };
- const result = getMonths(displayMonths, mockDates, reverseProps);
+ const result = getMonths(displayMonths, mockDates, reverseProps, dateLib);
expect(result).toHaveLength(2);
expect(result[0].date).toEqual(new Date(2023, 5, 1)); // June 2023
@@ -75,7 +74,7 @@ it("should handle fixed weeks", () => {
const fixedWeeksProps = { ...mockProps, fixedWeeks: true };
- const result = getMonths(displayMonths, mockDates, fixedWeeksProps);
+ const result = getMonths(displayMonths, mockDates, fixedWeeksProps, dateLib);
expect(result).toHaveLength(1);
expect(result[0]).toBeInstanceOf(CalendarMonth);
@@ -85,7 +84,7 @@ it("should handle fixed weeks", () => {
it("should handle months with no dates", () => {
const displayMonths = [new Date(2023, 5, 1)]; // June 2023
- const result = getMonths(displayMonths, [], mockProps);
+ const result = getMonths(displayMonths, [], mockProps, dateLib);
expect(result).toHaveLength(1);
expect(result[0]).toBeInstanceOf(CalendarMonth);
diff --git a/src/helpers/getMonths.ts b/src/helpers/getMonths.ts
index cc6e4f3ac8..706a1797bf 100644
--- a/src/helpers/getMonths.ts
+++ b/src/helpers/getMonths.ts
@@ -1,5 +1,5 @@
import { CalendarWeek, CalendarDay, CalendarMonth } from "../classes/index.js";
-import type { PropsContextValue } from "../contexts/index.js";
+import type { DateLib, DayPickerProps } from "../types/index.js";
/** Return the months to display in the calendar. */
export function getMonths(
@@ -8,16 +8,16 @@ export function getMonths(
/** The dates to display in the calendar. */
dates: Date[],
/** Options from the props context. */
- props: Pick<
- PropsContextValue,
- | "dateLib"
+ options: Pick<
+ DayPickerProps,
| "fixedWeeks"
| "ISOWeek"
| "locale"
| "weekStartsOn"
| "reverseMonths"
| "firstWeekContainsDate"
- >
+ >,
+ dateLib: DateLib
): CalendarMonth[] {
const {
startOfWeek,
@@ -28,21 +28,21 @@ export function getMonths(
addDays,
getWeek,
getISOWeek
- } = props.dateLib;
+ } = dateLib;
const dayPickerMonths = displayMonths.reduce(
(months, month) => {
- const firstDateOfFirstWeek = props.ISOWeek
+ const firstDateOfFirstWeek = options.ISOWeek
? startOfISOWeek(month)
: startOfWeek(month, {
- locale: props.locale,
- weekStartsOn: props.weekStartsOn
+ locale: options.locale,
+ weekStartsOn: options.weekStartsOn
});
- const lastDateOfLastWeek = props.ISOWeek
+ const lastDateOfLastWeek = options.ISOWeek
? endOfISOWeek(endOfMonth(month))
: endOfWeek(endOfMonth(month), {
- locale: props.locale,
- weekStartsOn: props.weekStartsOn
+ locale: options.locale,
+ weekStartsOn: options.weekStartsOn
});
/** The dates to display in the month. */
@@ -50,7 +50,7 @@ export function getMonths(
return date >= firstDateOfFirstWeek && date <= lastDateOfLastWeek;
});
- if (props.fixedWeeks && monthDates.length < 42) {
+ if (options.fixedWeeks && monthDates.length < 42) {
const extraDates = dates.filter((date) => {
return (
date > lastDateOfLastWeek && date <= addDays(lastDateOfLastWeek, 7)
@@ -61,16 +61,16 @@ export function getMonths(
const weeks: CalendarWeek[] = monthDates.reduce(
(weeks, date) => {
- const weekNumber = props.ISOWeek
+ const weekNumber = options.ISOWeek
? getISOWeek(date)
: getWeek(date, {
- locale: props.locale,
- weekStartsOn: props.weekStartsOn,
- firstWeekContainsDate: props.firstWeekContainsDate
+ locale: options.locale,
+ weekStartsOn: options.weekStartsOn,
+ firstWeekContainsDate: options.firstWeekContainsDate
});
const week = weeks.find((week) => week.weekNumber === weekNumber);
- const day = new CalendarDay(date, month, props.dateLib);
+ const day = new CalendarDay(date, month, dateLib);
if (!week) {
weeks.push(new CalendarWeek(weekNumber, [day]));
} else {
@@ -89,7 +89,7 @@ export function getMonths(
[]
);
- if (!props.reverseMonths) {
+ if (!options.reverseMonths) {
return dayPickerMonths;
} else {
return dayPickerMonths.reverse();
diff --git a/src/helpers/getStartEndMonths.test.ts b/src/helpers/getNavMonth.test.ts
similarity index 51%
rename from src/helpers/getStartEndMonths.test.ts
rename to src/helpers/getNavMonth.test.ts
index 805d734399..850dcfa208 100644
--- a/src/helpers/getStartEndMonths.test.ts
+++ b/src/helpers/getNavMonth.test.ts
@@ -1,38 +1,48 @@
-import { getStartEndMonths } from "./getStartEndMonths";
+import { dateLib } from "../lib";
+
+import { getNavMonths } from "./getNavMonth";
describe('when "startMonth" is not passed in', () => {
test('"startMonth" should be undefined', () => {
- const { startMonth } = getStartEndMonths({});
- expect(startMonth).toBeUndefined();
+ const [navStartMonth] = getNavMonths({}, dateLib);
+ expect(navStartMonth).toBeUndefined();
});
});
describe('when "startMonth" is passed in', () => {
- const { startMonth } = getStartEndMonths({
- startMonth: new Date(2021, 4, 3)
- });
+ const [navStartMonth] = getNavMonths(
+ {
+ startMonth: new Date(2021, 4, 3)
+ },
+ dateLib
+ );
test('"startMonth" should be the start of that month', () => {
- expect(startMonth).toEqual(new Date(2021, 4, 1));
+ expect(navStartMonth).toEqual(new Date(2021, 4, 1));
});
describe('when "fromYear" is passed in', () => {
test('"startMonth" should be the start of that month', () => {
- expect(startMonth).toEqual(new Date(2021, 4, 1));
+ expect(navStartMonth).toEqual(new Date(2021, 4, 1));
});
});
});
describe('when "fromYear" is passed in', () => {
- const { startMonth } = getStartEndMonths({ fromYear: 2021 });
+ const [navStartMonth] = getNavMonths({ fromYear: 2021 }, dateLib);
test('"startMonth" should be the start of that year', () => {
- expect(startMonth).toEqual(new Date(2021, 0, 1));
+ expect(navStartMonth).toEqual(new Date(2021, 0, 1));
});
});
describe('when "endMonth" is passed in', () => {
- const { endMonth } = getStartEndMonths({ endMonth: new Date(2021, 4, 3) });
+ const [, navEndMonth] = getNavMonths(
+ {
+ endMonth: new Date(2021, 4, 3)
+ },
+ dateLib
+ );
test('"endMonth" should be the end of that month', () => {
- expect(endMonth).toEqual(new Date(2021, 4, 31));
+ expect(navEndMonth).toEqual(new Date(2021, 4, 31));
});
describe('when "fromYear" is passed in', () => {
test('"endMonth" should be the end of that month', () => {
- expect(endMonth).toEqual(new Date(2021, 4, 31));
+ expect(navEndMonth).toEqual(new Date(2021, 4, 31));
});
});
});
@@ -40,52 +50,61 @@ describe('when "endMonth" is passed in', () => {
describe('when "toYear" is passed in', () => {
const toYear = 2021;
const expectedendMonth = new Date(2021, 11, 31);
- const { endMonth } = getStartEndMonths({ toYear });
+ const [, navEndMonth] = getNavMonths({ toYear }, dateLib);
test('"endMonth" should be the end of that year', () => {
- expect(endMonth).toEqual(expectedendMonth);
+ expect(navEndMonth).toEqual(expectedendMonth);
});
});
describe('when "captionLayout" is dropdown', () => {
const today = new Date(2024, 4, 3);
- const { startMonth, endMonth } = getStartEndMonths({
- captionLayout: "dropdown",
- today
- });
+ const [navStartMonth, navEndMonth] = getNavMonths(
+ {
+ captionLayout: "dropdown",
+ today
+ },
+ dateLib
+ );
test('"startMonth" should be 100 years ago', () => {
- expect(startMonth).toEqual(new Date(1924, 0, 1));
+ expect(navStartMonth).toEqual(new Date(1924, 0, 1));
});
test('"endMonth" should be the end of this year', () => {
- expect(endMonth).toEqual(new Date(2024, 11, 31));
+ expect(navEndMonth).toEqual(new Date(2024, 11, 31));
});
describe('when "fromYear" is set', () => {
const today = new Date(2024, 4, 3);
const fromYear = 2022;
- const { startMonth, endMonth } = getStartEndMonths({
- captionLayout: "dropdown",
- fromYear,
- today
- });
+ const [navStartMonth, navEndMonth] = getNavMonths(
+ {
+ captionLayout: "dropdown",
+ fromYear,
+ today
+ },
+ dateLib
+ );
test('"startMonth" should be equal to the "fromYear"', () => {
- expect(startMonth).toEqual(new Date(2022, 0, 1));
+ expect(navStartMonth).toEqual(new Date(2022, 0, 1));
});
test('"endMonth" should be the end of this year', () => {
- expect(endMonth).toEqual(new Date(2024, 11, 31));
+ expect(navEndMonth).toEqual(new Date(2024, 11, 31));
});
});
describe('when "toYear" is set', () => {
const today = new Date(2021, 4, 3);
const toYear = 2022;
- const { endMonth, startMonth } = getStartEndMonths({
- captionLayout: "dropdown",
- toYear,
- today
- });
+ const [navStartMonth, navEndMonth] = getNavMonths(
+ {
+ captionLayout: "dropdown",
+ toYear,
+ today
+ },
+ dateLib
+ );
test('"startMonth" should be 100 years ago', () => {
- expect(startMonth).toEqual(new Date(1921, 0, 1));
+ expect(navStartMonth).toEqual(new Date(1921, 0, 1));
});
test('"endMonth" should be equal to "toYear"', () => {
- expect(endMonth).toEqual(new Date(2022, 11, 31));
+ expect(navEndMonth).toEqual(new Date(2022, 11, 31));
});
});
});
diff --git a/src/helpers/getStartEndMonths.ts b/src/helpers/getNavMonth.ts
similarity index 71%
rename from src/helpers/getStartEndMonths.ts
rename to src/helpers/getNavMonth.ts
index 8474493a0e..56b520447b 100644
--- a/src/helpers/getStartEndMonths.ts
+++ b/src/helpers/getNavMonth.ts
@@ -1,26 +1,23 @@
-import { dateLib } from "../lib/index.js";
-import type { DayPickerProps } from "../types/index.js";
+import type { DateLib, DayPickerProps } from "../types/index.js";
-/**
- * Return the `fromMonth` and `toMonth` prop values values parsing the DayPicker
- * props.
- */
-export function getStartEndMonths(
+/** Return the start and end months for the calendar navigation. */
+export function getNavMonths(
props: Pick<
DayPickerProps,
| "startMonth"
| "endMonth"
| "today"
| "captionLayout"
- | "dateLib"
// Deprecated:
| "fromYear"
| "toYear"
| "fromMonth"
| "toMonth"
- >
-): Pick {
+ >,
+ dateLib: DateLib
+): [navStartMonth: Date | undefined, navEndMonth: Date | undefined] {
let { startMonth, endMonth } = props;
+
const {
startOfYear,
startOfDay,
@@ -29,10 +26,8 @@ export function getStartEndMonths(
addYears,
endOfYear,
Date
- } = {
- ...dateLib,
- ...props.dateLib
- };
+ } = dateLib;
+
// Handle deprecated code
const { fromYear, toYear, fromMonth, toMonth } = props;
if (!startMonth && fromMonth) {
@@ -63,8 +58,8 @@ export function getStartEndMonths(
} else if (!endMonth && hasDropdowns) {
endMonth = endOfYear(props.today ?? new Date());
}
- return {
- startMonth: startMonth ? startOfDay(startMonth) : startMonth,
- endMonth: endMonth ? startOfDay(endMonth) : endMonth
- };
+ return [
+ startMonth ? startOfDay(startMonth) : startMonth,
+ endMonth ? startOfDay(endMonth) : endMonth
+ ];
}
diff --git a/src/helpers/getNextFocus.test.tsx b/src/helpers/getNextFocus.test.tsx
index fe5f2b16cb..364133a3e8 100644
--- a/src/helpers/getNextFocus.test.tsx
+++ b/src/helpers/getNextFocus.test.tsx
@@ -1,18 +1,15 @@
import { CalendarDay } from "../classes";
-import type { PropsContextValue } from "../contexts/useProps";
import { dateLib } from "../lib";
-import type { MoveFocusBy, MoveFocusDir } from "../types";
+import type { DayPickerProps, MoveFocusBy, MoveFocusDir } from "../types";
import { getNextFocus } from "./getNextFocus";
const props: Pick<
- PropsContextValue,
+ DayPickerProps,
"disabled" | "hidden" | "startMonth" | "endMonth" | "dateLib"
> = {
disabled: [],
hidden: [],
- startMonth: undefined,
- endMonth: undefined,
dateLib
};
@@ -24,7 +21,16 @@ it("should return `undefined` if `attempt` exceeds 365", () => {
);
const moveBy: MoveFocusBy = "day";
const moveDir: MoveFocusDir = "after";
- const result = getNextFocus(moveBy, moveDir, focusedDay, props, 366);
+ const result = getNextFocus(
+ moveBy,
+ moveDir,
+ focusedDay,
+ undefined,
+ undefined,
+ props,
+ dateLib,
+ 366
+ );
expect(result).toBeUndefined();
});
@@ -35,7 +41,15 @@ it("should return the focus date if it is not disabled or hidden", () => {
dateLib
);
const expectedDate = new Date(2020, 0, 2);
- const result = getNextFocus("day", "after", focusedDay, props);
+ const result = getNextFocus(
+ "day",
+ "after",
+ focusedDay,
+ undefined,
+ undefined,
+ props,
+ dateLib
+ );
expect(result?.date).toEqual(expectedDate);
});
@@ -47,10 +61,18 @@ it("should return the next focus date if it is disabled", () => {
);
const disabledDate = new Date(2020, 0, 2);
const expectedDate = new Date(2020, 0, 3);
- const result = getNextFocus("day", "after", focusedDay, {
- ...props,
- disabled: [disabledDate]
- });
+ const result = getNextFocus(
+ "day",
+ "after",
+ focusedDay,
+ undefined,
+ undefined,
+ {
+ ...props,
+ disabled: [disabledDate]
+ },
+ dateLib
+ );
expect(result?.date).toEqual(expectedDate);
});
@@ -62,9 +84,17 @@ it("should return the next focus date if it is hidden", () => {
);
const hiddenDate = new Date(2020, 0, 2);
const expectedDate = new Date(2020, 0, 3);
- const result = getNextFocus("day", "after", focusedDay, {
- ...props,
- hidden: [hiddenDate]
- });
+ const result = getNextFocus(
+ "day",
+ "after",
+ focusedDay,
+ undefined,
+ undefined,
+ {
+ ...props,
+ hidden: [hiddenDate]
+ },
+ dateLib
+ );
expect(result?.date).toEqual(expectedDate);
});
diff --git a/src/helpers/getNextFocus.tsx b/src/helpers/getNextFocus.tsx
index 2913bf86ee..670803699e 100644
--- a/src/helpers/getNextFocus.tsx
+++ b/src/helpers/getNextFocus.tsx
@@ -1,37 +1,26 @@
import { CalendarDay } from "../classes/index.js";
-import type { PropsContextValue } from "../contexts/index.js";
-import type { MoveFocusBy, MoveFocusDir } from "../types/index.js";
+import type {
+ DateLib,
+ DayPickerProps,
+ MoveFocusBy,
+ MoveFocusDir
+} from "../types/index.js";
import { dateMatchModifiers } from "../utils/dateMatchModifiers.js";
-import { getPossibleFocusDate } from "./getPossibleFocusDate.js";
-
-export type Options = Pick<
- PropsContextValue,
- | "modifiers"
- | "locale"
- | "ISOWeek"
- | "weekStartsOn"
- | "startMonth"
- | "endMonth"
->;
+import { getFocusableDate } from "./getFocusableDate.js";
export function getNextFocus(
moveBy: MoveFocusBy,
moveDir: MoveFocusDir,
/** The date that is currently focused. */
- focused: CalendarDay,
+ refDay: CalendarDay,
+ calendarStartMonth: Date | undefined,
+ calendarEndMonth: Date | undefined,
options: Pick<
- PropsContextValue,
- | "dateLib"
- | "disabled"
- | "hidden"
- | "modifiers"
- | "locale"
- | "ISOWeek"
- | "weekStartsOn"
- | "startMonth"
- | "endMonth"
+ DayPickerProps,
+ "disabled" | "hidden" | "modifiers" | "locale" | "ISOWeek" | "weekStartsOn"
>,
+ dateLib: DateLib,
attempt: number = 0
): CalendarDay | undefined {
if (attempt > 365) {
@@ -39,33 +28,40 @@ export function getNextFocus(
return undefined;
}
- const possibleFocusDate = getPossibleFocusDate(
+ const focusableDate = getFocusableDate(
moveBy,
moveDir,
- focused.date,
- options
+ refDay.date, // should be refDay? or refDay.date?
+ calendarStartMonth,
+ calendarEndMonth,
+ options,
+ dateLib
);
const isDisabled = Boolean(
options.disabled &&
- dateMatchModifiers(possibleFocusDate, options.disabled, options.dateLib)
+ dateMatchModifiers(focusableDate, options.disabled, dateLib)
);
const isHidden = Boolean(
- options.hidden &&
- dateMatchModifiers(possibleFocusDate, options.hidden, options.dateLib)
+ options.hidden && dateMatchModifiers(focusableDate, options.hidden, dateLib)
);
- const targetMonth = possibleFocusDate;
- const focusDay = new CalendarDay(
- possibleFocusDate,
- targetMonth,
- options.dateLib
- );
+ const targetMonth = focusableDate;
+ const focusDay = new CalendarDay(focusableDate, targetMonth, dateLib);
if (!isDisabled && !isHidden) {
return focusDay;
}
// Recursively attempt to find the next focusable date
- return getNextFocus(moveBy, moveDir, focusDay, options, attempt + 1);
+ return getNextFocus(
+ moveBy,
+ moveDir,
+ focusDay,
+ calendarStartMonth,
+ calendarEndMonth,
+ options,
+ dateLib,
+ attempt + 1
+ );
}
diff --git a/src/helpers/getNextMonth.test.ts b/src/helpers/getNextMonth.test.ts
index 2b7033347c..616b1a3f1e 100644
--- a/src/helpers/getNextMonth.test.ts
+++ b/src/helpers/getNextMonth.test.ts
@@ -7,27 +7,22 @@ const startingMonth = new Date(2020, 4, 31);
describe("when number of months is 1", () => {
describe("when the navigation is disabled", () => {
- const disableNavigation = true;
it("the next month is undefined", () => {
- const result = getNextMonth(startingMonth, {
- numberOfMonths: 1,
- disableNavigation,
- endMonth: undefined,
- dateLib,
- startMonth: undefined
- });
+ const result = getNextMonth(
+ startingMonth,
+ undefined,
+ {
+ disableNavigation: true
+ },
+ dateLib
+ );
expect(result).toBe(undefined);
});
});
describe("when in the navigable range", () => {
const endMonth = addMonths(startingMonth, 3);
it("the next month is not undefined", () => {
- const result = getNextMonth(startingMonth, {
- numberOfMonths: 1,
- endMonth,
- startMonth: undefined,
- dateLib
- });
+ const result = getNextMonth(startingMonth, endMonth, {}, dateLib);
const expectedNextMonth = addMonths(startingMonth, 1);
expect(result && isSameMonth(result, expectedNextMonth)).toBeTruthy();
});
@@ -35,12 +30,7 @@ describe("when number of months is 1", () => {
describe("when not in the navigable range", () => {
const endMonth = startingMonth;
it("the next month is undefined", () => {
- const result = getNextMonth(startingMonth, {
- numberOfMonths: 1,
- endMonth,
- startMonth: undefined,
- dateLib
- });
+ const result = getNextMonth(startingMonth, endMonth, {}, dateLib);
expect(result).toBe(undefined);
});
});
@@ -50,25 +40,29 @@ describe("when displaying 3 months", () => {
describe("when the navigation is paged", () => {
const pagedNavigation = true;
it("the next month is 3 months ahead", () => {
- const result = getNextMonth(startingMonth, {
- numberOfMonths,
- pagedNavigation,
- startMonth: undefined,
- endMonth: undefined,
+ const result = getNextMonth(
+ startingMonth,
+ undefined,
+ {
+ numberOfMonths,
+ pagedNavigation
+ },
dateLib
- });
+ );
const expectedNextMonth = addMonths(startingMonth, 3);
expect(result && isSameMonth(result, expectedNextMonth)).toBeTruthy();
});
describe("when the to-date is ahead less than 3 months", () => {
it("the next month is undefined", () => {
- const result = getNextMonth(startingMonth, {
- numberOfMonths,
- pagedNavigation,
- startMonth: undefined,
- endMonth: addMonths(startingMonth, 1),
+ const result = getNextMonth(
+ startingMonth,
+ addMonths(startingMonth, 1),
+ {
+ numberOfMonths,
+ pagedNavigation
+ },
dateLib
- });
+ );
expect(result).toBe(undefined);
});
});
@@ -76,25 +70,29 @@ describe("when displaying 3 months", () => {
describe("when the navigation is not paged", () => {
const pagedNavigation = false;
it("the next month is 1 months ahead", () => {
- const result = getNextMonth(startingMonth, {
- numberOfMonths,
- pagedNavigation,
- endMonth: undefined,
- startMonth: undefined,
+ const result = getNextMonth(
+ startingMonth,
+ undefined,
+ {
+ numberOfMonths,
+ pagedNavigation
+ },
dateLib
- });
+ );
const expectedNextMonth = addMonths(startingMonth, 1);
expect(result && isSameMonth(result, expectedNextMonth)).toBeTruthy();
});
describe("when the to-date is ahead less than 3 months", () => {
it("the next month is undefined", () => {
- const result = getNextMonth(startingMonth, {
- numberOfMonths,
- pagedNavigation,
- startMonth: undefined,
- endMonth: addMonths(startingMonth, 2),
+ const result = getNextMonth(
+ startingMonth,
+ addMonths(startingMonth, 2),
+ {
+ numberOfMonths,
+ pagedNavigation
+ },
dateLib
- });
+ );
expect(result).toBe(undefined);
});
});
diff --git a/src/helpers/getNextMonth.ts b/src/helpers/getNextMonth.ts
index b62471aaf6..2ed409272b 100644
--- a/src/helpers/getNextMonth.ts
+++ b/src/helpers/getNextMonth.ts
@@ -1,4 +1,4 @@
-import { PropsContextValue } from "../contexts/index.js";
+import type { DateLib, DayPickerProps } from "../types/index.js";
/**
* Return the next month the user can navigate to according to the given
@@ -6,39 +6,36 @@ import { PropsContextValue } from "../contexts/index.js";
*
* Please note that the next month is not always the next calendar month:
*
- * - If after the `endMonth` range, is `undefined`;
+ * - If after the `calendarEndMonth` range, is `undefined`;
* - If the navigation is paged , is the number of months displayed ahead.
*/
export function getNextMonth(
firstDisplayedMonth: Date,
- props: Pick<
- PropsContextValue,
- | "startMonth"
- | "endMonth"
- | "numberOfMonths"
- | "pagedNavigation"
- | "disableNavigation"
- | "dateLib"
- >
+ calendarEndMonth: Date | undefined,
+ options: Pick<
+ DayPickerProps,
+ "numberOfMonths" | "pagedNavigation" | "disableNavigation"
+ >,
+ dateLib: DateLib
): Date | undefined {
- if (props.disableNavigation) {
+ if (options.disableNavigation) {
return undefined;
}
- const { pagedNavigation, numberOfMonths } = props;
- const { startOfMonth, addMonths, differenceInCalendarMonths } = props.dateLib;
+ const { pagedNavigation, numberOfMonths = 1 } = options;
+ const { startOfMonth, addMonths, differenceInCalendarMonths } = dateLib;
const offset = pagedNavigation ? numberOfMonths : 1;
const month = startOfMonth(firstDisplayedMonth);
- if (!props.endMonth) {
+ if (!calendarEndMonth) {
return addMonths(month, offset);
}
const monthsDiff = differenceInCalendarMonths(
- props.endMonth,
+ calendarEndMonth,
firstDisplayedMonth
);
- if (monthsDiff < numberOfMonths) {
+ if (monthsDiff < numberOfMonths ?? 1) {
return undefined;
}
diff --git a/src/helpers/getPossibleFocusDate.test.ts b/src/helpers/getPossibleFocusDate.test.ts
index 6a7c2c09f1..9d00eb88e9 100644
--- a/src/helpers/getPossibleFocusDate.test.ts
+++ b/src/helpers/getPossibleFocusDate.test.ts
@@ -11,25 +11,21 @@ import {
endOfWeek
} from "date-fns";
-import type { PropsContextValue } from "../contexts/useProps";
-import type { MoveFocusBy, MoveFocusDir } from "../types";
+import type { DayPickerProps, MoveFocusBy, MoveFocusDir } from "../types";
import { dateLib } from "..";
-import { getPossibleFocusDate } from "./getPossibleFocusDate";
+import { getFocusableDate } from "./getFocusableDate";
-const baseDate = new Date(2023, 0, 1); // Jan 1, 2023
-const options: Pick<
- PropsContextValue,
- "locale" | "ISOWeek" | "weekStartsOn" | "startMonth" | "endMonth" | "dateLib"
-> = {
+const focusedDate = new Date(2023, 0, 1); // Jan 1, 2023
+const options: Pick = {
locale: undefined,
ISOWeek: false,
- weekStartsOn: 0, // Sunday
- startMonth: new Date(2022, 0, 1), // Jan 1, 2022
- endMonth: new Date(2024, 0, 1), // Jan 1, 2024
- dateLib
+ weekStartsOn: 0 // Sunday
};
+const calendarStartMonth = new Date(2022, 0, 1); // Jan 1, 2022
+const calendarEndMonth = new Date(2024, 0, 1); // Jan 1, 2024
+
const testCases: {
moveBy: MoveFocusBy;
moveDir: MoveFocusDir;
@@ -47,8 +43,16 @@ const testCases: {
testCases.forEach(({ moveBy, moveDir, expectedFn }) => {
test(`should move ${moveDir} by ${moveBy}`, () => {
- const expectedDate = expectedFn(baseDate, moveDir === "after" ? 1 : -1);
- const result = getPossibleFocusDate(moveBy, moveDir, baseDate, options);
+ const expectedDate = expectedFn(focusedDate, moveDir === "after" ? 1 : -1);
+ const result = getFocusableDate(
+ moveBy,
+ moveDir,
+ focusedDate,
+ calendarStartMonth,
+ calendarEndMonth,
+ options,
+ dateLib
+ );
expect(result).toEqual(expectedDate);
});
});
@@ -72,8 +76,16 @@ const weekTestCases: {
weekTestCases.forEach(({ moveBy, moveDir, expectedFn }) => {
test(`should move ${moveDir} by ${moveBy}`, () => {
- const expectedDate = expectedFn(baseDate);
- const result = getPossibleFocusDate(moveBy, moveDir, baseDate, options);
+ const expectedDate = expectedFn(focusedDate);
+ const result = getFocusableDate(
+ moveBy,
+ moveDir,
+ focusedDate,
+ calendarStartMonth,
+ calendarEndMonth,
+ options,
+ dateLib
+ );
expect(result).toEqual(expectedDate);
});
@@ -90,31 +102,42 @@ const ISOWeekTestCases: {
ISOWeekTestCases.forEach(({ moveBy, moveDir, expectedFn }) => {
test(`should move ${moveDir} by ${moveBy} when ISOWeek is true`, () => {
- const expectedDate = expectedFn(baseDate);
- const result = getPossibleFocusDate(moveBy, moveDir, baseDate, {
- ...options,
- ISOWeek: true
- });
+ const expectedDate = expectedFn(focusedDate);
+ const result = getFocusableDate(
+ moveBy,
+ moveDir,
+ focusedDate,
+ calendarStartMonth,
+ calendarEndMonth,
+ { ...options, ISOWeek: true },
+ dateLib
+ );
expect(result).toEqual(expectedDate);
});
});
test("should not move before startMonth", () => {
- const result = getPossibleFocusDate(
+ const result = getFocusableDate(
"day",
"before",
new Date(2022, 0, 2),
- options
+ calendarStartMonth,
+ calendarEndMonth,
+ options,
+ dateLib
);
- expect(result).toEqual(options.startMonth);
+ expect(result).toEqual(calendarStartMonth);
});
test("should not move after endMonth", () => {
- const result = getPossibleFocusDate(
+ const result = getFocusableDate(
"day",
"after",
new Date(2023, 11, 31),
- options
+ calendarStartMonth,
+ calendarEndMonth,
+ options,
+ dateLib
);
- expect(result).toEqual(options.endMonth);
+ expect(result).toEqual(calendarEndMonth);
});
diff --git a/src/helpers/getPossibleFocusDate.ts b/src/helpers/getPossibleFocusDate.ts
deleted file mode 100644
index db33118419..0000000000
--- a/src/helpers/getPossibleFocusDate.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import type { PropsContextValue } from "../contexts/index.js";
-import type { MoveFocusBy, MoveFocusDir } from "../types/index.js";
-
-/** Return the next date that should be focused. */
-export function getPossibleFocusDate(
- moveBy: MoveFocusBy,
- moveDir: MoveFocusDir,
- focusedDate: Date,
- options: Pick<
- PropsContextValue,
- | "locale"
- | "ISOWeek"
- | "weekStartsOn"
- | "startMonth"
- | "endMonth"
- | "dateLib"
- >
-): Date {
- const { weekStartsOn, startMonth, endMonth, locale, ISOWeek } = options;
- const {
- addDays,
- addMonths,
- addYears,
- addWeeks,
- startOfISOWeek,
- endOfISOWeek,
- startOfWeek,
- endOfWeek,
- max,
- min
- } = options.dateLib;
- const moveFns = {
- day: addDays,
- week: addWeeks,
- month: addMonths,
- year: addYears,
- startOfWeek: (date: Date) =>
- ISOWeek
- ? startOfISOWeek(date)
- : startOfWeek(date, { locale, weekStartsOn }),
- endOfWeek: (date: Date) =>
- ISOWeek ? endOfISOWeek(date) : endOfWeek(date, { locale, weekStartsOn })
- };
-
- let nextFocusedDate = moveFns[moveBy](
- focusedDate,
- moveDir === "after" ? 1 : -1
- );
- if (moveDir === "before" && startMonth) {
- nextFocusedDate = max([startMonth, nextFocusedDate]);
- } else if (moveDir === "after" && endMonth) {
- nextFocusedDate = min([endMonth, nextFocusedDate]);
- }
- return nextFocusedDate;
-}
diff --git a/src/helpers/getPreviousMonth.test.ts b/src/helpers/getPreviousMonth.test.ts
index 9aef4eb45c..eecca21f71 100644
--- a/src/helpers/getPreviousMonth.test.ts
+++ b/src/helpers/getPreviousMonth.test.ts
@@ -3,15 +3,19 @@ import { getPreviousMonth } from "./getPreviousMonth";
it("should return undefined if navigation is disabled", () => {
const firstDisplayedMonth = new Date(2022, 0, 1); // January 2022
+ const calendarStartMonth = new Date(2022, 0, 1); // January 2022
const props = {
disableNavigation: true,
pagedNavigation: false,
- numberOfMonths: 1,
- startMonth: new Date(2022, 0, 1),
- dateLib
+ numberOfMonths: 1
};
- const result = getPreviousMonth(firstDisplayedMonth, props);
+ const result = getPreviousMonth(
+ firstDisplayedMonth,
+ calendarStartMonth,
+ props,
+ dateLib
+ );
expect(result).toBeUndefined();
});
@@ -21,33 +25,39 @@ it("should return the previous month if startMonth is not provided", () => {
const props = {
disableNavigation: false,
pagedNavigation: false,
- numberOfMonths: 1,
- startMonth: undefined,
- dateLib
+ numberOfMonths: 1
};
- const result = getPreviousMonth(firstDisplayedMonth, props);
+ const result = getPreviousMonth(
+ firstDisplayedMonth,
+ undefined,
+ props,
+ dateLib
+ );
expect(result).toEqual(new Date(2022, 0, 1)); // January 2022
});
it("should return undefined if the previous month is before the startMonth", () => {
const firstDisplayedMonth = new Date(2022, 0, 1); // January 2022
+ const calendarStartMonth = new Date(2022, 0, 1); // January 2022
const props = {
disableNavigation: false,
pagedNavigation: false,
- numberOfMonths: 1,
- startMonth: new Date(2022, 0, 1),
- dateLib: dateLib
+ numberOfMonths: 1
};
-
- const result = getPreviousMonth(firstDisplayedMonth, props);
-
+ const result = getPreviousMonth(
+ firstDisplayedMonth,
+ calendarStartMonth,
+ props,
+ dateLib
+ );
expect(result).toBeUndefined();
});
it("should return the correct previous month when pagedNavigation is true", () => {
const firstDisplayedMonth = new Date(2022, 2, 1); // March 2022
+ const calendarStartMonth = new Date(2022, 0, 1); // January 2022
const props = {
disableNavigation: false,
pagedNavigation: true,
@@ -56,7 +66,12 @@ it("should return the correct previous month when pagedNavigation is true", () =
dateLib
};
- const result = getPreviousMonth(firstDisplayedMonth, props);
+ const result = getPreviousMonth(
+ firstDisplayedMonth,
+ calendarStartMonth,
+ props,
+ dateLib
+ );
expect(result).toEqual(new Date(2022, 0, 1)); // January 2022
});
diff --git a/src/helpers/getPreviousMonth.ts b/src/helpers/getPreviousMonth.ts
index e5a516e9e8..84b5f73391 100644
--- a/src/helpers/getPreviousMonth.ts
+++ b/src/helpers/getPreviousMonth.ts
@@ -1,4 +1,4 @@
-import type { PropsContextValue } from "../contexts/index.js";
+import type { DateLib, DayPickerProps } from "../types/index.js";
/**
* Return the next previous the user can navigate to, according to the given
@@ -7,31 +7,29 @@ import type { PropsContextValue } from "../contexts/index.js";
* Please note that the previous month is not always the previous calendar
* month:
*
- * - If before the `startMonth` date, is `undefined`;
+ * - If before the `calendarStartMonth` date, is `undefined`;
* - If the navigation is paged, is the number of months displayed before.
*/
export function getPreviousMonth(
firstDisplayedMonth: Date,
- props: Pick<
- PropsContextValue,
- | "startMonth"
- | "numberOfMonths"
- | "pagedNavigation"
- | "disableNavigation"
- | "dateLib"
- >
+ calendarStartMonth: Date | undefined,
+ options: Pick<
+ DayPickerProps,
+ "numberOfMonths" | "pagedNavigation" | "disableNavigation"
+ >,
+ dateLib: DateLib
): Date | undefined {
- if (props.disableNavigation) {
+ if (options.disableNavigation) {
return undefined;
}
- const { pagedNavigation, numberOfMonths } = props;
- const { startOfMonth, addMonths, differenceInCalendarMonths } = props.dateLib;
- const offset = pagedNavigation ? numberOfMonths : 1;
+ const { pagedNavigation, numberOfMonths } = options;
+ const { startOfMonth, addMonths, differenceInCalendarMonths } = dateLib;
+ const offset = pagedNavigation ? numberOfMonths ?? 1 : 1;
const month = startOfMonth(firstDisplayedMonth);
- if (!props.startMonth) {
+ if (!calendarStartMonth) {
return addMonths(month, -offset);
}
- const monthsDiff = differenceInCalendarMonths(month, props.startMonth);
+ const monthsDiff = differenceInCalendarMonths(month, calendarStartMonth);
if (monthsDiff <= 0) {
return undefined;
diff --git a/src/helpers/getStyleForModifiers.test.ts b/src/helpers/getStyleForModifiers.test.ts
index a0fc7666dc..0284e05d56 100644
--- a/src/helpers/getStyleForModifiers.test.ts
+++ b/src/helpers/getStyleForModifiers.test.ts
@@ -1,16 +1,10 @@
import type { CSSProperties } from "react";
// Update the path as needed
-import { UI } from "../UI";
-import type { Modifiers, ModifiersStyles, Styles } from "../types";
+import type { Modifiers, ModifiersStyles } from "../types";
import { getStyleForModifiers } from "./getStyleForModifiers";
-const baseDayStyle: CSSProperties = {
- backgroundColor: "white",
- color: "black"
-};
-const styles: Partial = { [UI.Day]: baseDayStyle };
const defaultModifiers: Modifiers = {
disabled: false,
hidden: false,
@@ -23,14 +17,6 @@ const defaultModifiers: Modifiers = {
focused: false,
today: false
};
-test("returns base style when no modifiers are provided", () => {
- const dayModifiers = defaultModifiers;
- const modifiersStyles: Partial = {};
-
- const style = getStyleForModifiers(dayModifiers, modifiersStyles, styles);
-
- expect(style).toEqual(baseDayStyle);
-});
test("applies modifier styles to the base style", () => {
const dayModifiers: Modifiers = {
@@ -42,11 +28,10 @@ test("applies modifier styles to the base style", () => {
selected: { backgroundColor: "blue", color: "white" }
};
const expectedStyle: CSSProperties = {
- ...baseDayStyle,
...modifiersStyles.selected
};
- const style = getStyleForModifiers(dayModifiers, modifiersStyles, styles);
+ const style = getStyleForModifiers(dayModifiers, modifiersStyles);
expect(style).toEqual(expectedStyle);
});
@@ -61,9 +46,9 @@ test("ignores modifiers that are not active", () => {
disabled: { opacity: 0.5 }
};
- const style = getStyleForModifiers(dayModifiers, modifiersStyles, styles);
+ const style = getStyleForModifiers(dayModifiers, modifiersStyles);
- expect(style).toEqual({ ...baseDayStyle, opacity: 0.5 }); // should not have applied the disabled style
+ expect(style).toEqual({ opacity: 0.5 }); // should not have applied the disabled style
});
test("combines multiple active modifier styles", () => {
@@ -77,12 +62,11 @@ test("combines multiple active modifier styles", () => {
highlighted: { borderColor: "yellow" }
};
const expectedStyle: CSSProperties = {
- ...baseDayStyle,
...modifiersStyles.selected,
...modifiersStyles.highlighted
};
- const style = getStyleForModifiers(dayModifiers, modifiersStyles, styles);
+ const style = getStyleForModifiers(dayModifiers, modifiersStyles);
expect(style).toEqual(expectedStyle);
});
@@ -98,12 +82,11 @@ test("applies the most recent modifier style when there are conflicts", () => {
highlighted: { backgroundColor: "yellow", color: "green" }
};
const expectedStyle: CSSProperties = {
- ...baseDayStyle,
backgroundColor: "yellow", // from 'highlighted'
color: "green" // from 'highlighted', overriding 'selected'
};
- const style = getStyleForModifiers(dayModifiers, modifiersStyles, styles);
+ const style = getStyleForModifiers(dayModifiers, modifiersStyles);
expect(style).toEqual(expectedStyle);
});
diff --git a/src/helpers/getStyleForModifiers.ts b/src/helpers/getStyleForModifiers.ts
index 2e4cebea60..8d909568e7 100644
--- a/src/helpers/getStyleForModifiers.ts
+++ b/src/helpers/getStyleForModifiers.ts
@@ -1,14 +1,12 @@
import type { CSSProperties } from "react";
-import { UI } from "../UI.js";
-import type { Modifiers, ModifiersStyles, Styles } from "../types/index.js";
+import type { Modifiers, ModifiersStyles } from "../types/index.js";
export function getStyleForModifiers(
dayModifiers: Modifiers,
- modifiersStyles: Partial,
- styles: Partial
+ modifiersStyles: Partial = {}
): CSSProperties {
- let style: CSSProperties = { ...styles[UI.Day] };
+ let style: CSSProperties = {};
Object.entries(dayModifiers)
.filter(([, active]) => active === true)
.forEach(([modifier]) => {
diff --git a/src/index.ts b/src/index.ts
index 001d5d6996..c3b80e64c6 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,9 +4,6 @@ export * from "./types/index.js";
export * from "./classes/index.js";
export * from "./components/custom-components.js";
-export * from "./contexts/index.js";
-export * from "./selection/index.js";
-
export * from "./lib/index.js";
export * from "./formatters/index.js";
@@ -14,3 +11,5 @@ export * from "./helpers/index.js";
export * from "./labels/index.js";
export * from "./utils/index.js";
export * from "./UI.js";
+
+export * from "./useDayPicker.js";
diff --git a/src/labels/index.ts b/src/labels/index.ts
index afd033741d..9a7469c165 100644
--- a/src/labels/index.ts
+++ b/src/labels/index.ts
@@ -1,5 +1,8 @@
-export * from "./labelDay.js";
-export * from "./labelCaption.js";
+export * from "./labelGrid.js";
+export * from "./labelGridcell.js";
+export * from "./labelDayButton.js";
+export * from "./labelNav.js";
+export * from "./labelGrid.js";
export * from "./labelMonthDropdown.js";
export * from "./labelNext.js";
export * from "./labelPrevious.js";
diff --git a/src/labels/labelCaption.ts b/src/labels/labelCaption.ts
deleted file mode 100644
index 1af1ab21c0..0000000000
--- a/src/labels/labelCaption.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import type { DateLib } from "../index.js";
-import type { FormatOptions } from "../lib/dateLib.js";
-import { dateLib as defaultDateLib } from "../lib/index.js";
-
-/**
- * Return an ARIA label for the month caption. The label is used in an aria-live
- * region and will be announced wnen the month changes. from the caption.
- *
- * @group Labels
- */
-export function labelCaption(
- date: Date,
- options?: FormatOptions,
- dateLib: DateLib = defaultDateLib
-) {
- return "";
-}
diff --git a/src/labels/labelDay.ts b/src/labels/labelDay.ts
deleted file mode 100644
index 9eed24af92..0000000000
--- a/src/labels/labelDay.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { DateLib } from "../index.js";
-import type { FormatOptions } from "../lib/dateLib.js";
-import { dateLib as defaultDateLib } from "../lib/index.js";
-import type { Modifiers } from "../types/index.js";
-
-/**
- * Return an ARIA label for the day button. By default, it returns an empty
- * label since the screen readers will announce the date from the grid cell.
- *
- * Use this function to provide a custom label for the day gridcell, e.g. for
- * announcing that is selected or booked.
- *
- * @group Labels
- */
-export function labelDay(
- date: Date,
- modifiers: Modifiers,
- options?: FormatOptions,
- dateLib: DateLib = defaultDateLib
-) {
- return "";
-}
diff --git a/src/labels/labelDayButton.test.ts b/src/labels/labelDayButton.test.ts
new file mode 100644
index 0000000000..70970101fc
--- /dev/null
+++ b/src/labels/labelDayButton.test.ts
@@ -0,0 +1,24 @@
+import { es } from "date-fns/locale/es";
+
+import type { Modifiers } from "../types";
+
+import { labelDayButton } from "./labelDayButton";
+
+const day = new Date(2022, 10, 21);
+const dayModifiers: Modifiers = {
+ disabled: false,
+ focusable: false,
+ focused: false,
+ hidden: false,
+ outside: false,
+ range_end: false,
+ range_middle: false,
+ range_start: false,
+ selected: false,
+ today: false
+};
+test("should return the localized label", () => {
+ expect(labelDayButton(day, dayModifiers, { locale: es })).toEqual(
+ "lunes, 21 de noviembre de 2022"
+ );
+});
diff --git a/src/labels/labelDayButton.ts b/src/labels/labelDayButton.ts
new file mode 100644
index 0000000000..7be51c4de4
--- /dev/null
+++ b/src/labels/labelDayButton.ts
@@ -0,0 +1,28 @@
+import type { DateLib } from "../index.js";
+import type { LabelOptions } from "../lib/dateLib.js";
+import { dateLib as defaultDateLib } from "../lib/index.js";
+import type { Modifiers } from "../types/index.js";
+
+/**
+ * Return an ARIA label for the day button.
+ *
+ * @group Labels
+ */
+export function labelDayButton(
+ date: Date,
+ modifiers: Modifiers,
+ options?: LabelOptions,
+ dateLib: DateLib = defaultDateLib
+) {
+ let label = dateLib.format(date, "PPPP", options);
+ if (modifiers.today) {
+ label = `Today, ${label}`;
+ }
+ if (modifiers.selected) {
+ label = `${label}, selected`;
+ }
+ return label;
+}
+
+/** @deprecated Use `labelDayButton` instead. */
+export const labelDay = labelDayButton;
diff --git a/src/labels/labelDay.test.ts b/src/labels/labelDayContent.test.ts
similarity index 62%
rename from src/labels/labelDay.test.ts
rename to src/labels/labelDayContent.test.ts
index f66ea3214a..b2de0bbdfc 100644
--- a/src/labels/labelDay.test.ts
+++ b/src/labels/labelDayContent.test.ts
@@ -2,7 +2,7 @@ import { es } from "date-fns/locale/es";
import type { Modifiers } from "../types";
-import { labelDay } from "./labelDay";
+import { labelGridcell } from "./labelGridcell";
const day = new Date(2022, 10, 21);
const dayModifiers: Modifiers = {
@@ -17,6 +17,8 @@ const dayModifiers: Modifiers = {
selected: false,
today: false
};
-test("should return an empty day label", () => {
- expect(labelDay(day, dayModifiers, { locale: es })).toEqual("");
+test("should return the localized label", () => {
+ expect(labelGridcell(day, dayModifiers, { locale: es })).toEqual(
+ "lunes, 21 de noviembre de 2022"
+ );
});
diff --git a/src/labels/labelGrid.ts b/src/labels/labelGrid.ts
index df0dcccad1..5a08289715 100644
--- a/src/labels/labelGrid.ts
+++ b/src/labels/labelGrid.ts
@@ -1,17 +1,23 @@
-import type { FormatOptions } from "date-fns";
-
+import type { LabelOptions } from "../lib/dateLib.js";
import { dateLib as defaultDateLib } from "../lib/index.js";
-import type { DateLib } from "../types/index.js";
+import { DateLib } from "../types/index.js";
/**
- * Return the default ARIA label for the month grid.
+ * Return an ARIA label for the month grid, that will be announced when entering
+ * the grid.
*
* @group Labels
*/
export function labelGrid(
- month: Date,
- options?: FormatOptions,
+ date: Date,
+ options?: LabelOptions,
dateLib: DateLib = defaultDateLib
) {
- return dateLib.format(month, "LLLL y", options);
+ return dateLib.format(date, "LLLL y", options);
}
+
+/**
+ * @deprecated Use {@link labelGrid} instead.
+ * @protected
+ */
+export const labelCaption = labelGrid;
diff --git a/src/labels/labelGridcell.ts b/src/labels/labelGridcell.ts
new file mode 100644
index 0000000000..d7345e45b7
--- /dev/null
+++ b/src/labels/labelGridcell.ts
@@ -0,0 +1,22 @@
+import type { DateLib } from "../index.js";
+import type { LabelOptions } from "../lib/dateLib.js";
+import { dateLib as defaultDateLib } from "../lib/index.js";
+import type { Modifiers } from "../types/index.js";
+
+/**
+ * The label for the day gridcell when the calendar is not interactive.
+ *
+ * @group Labels
+ */
+export function labelGridcell(
+ date: Date,
+ modifiers?: Modifiers,
+ options?: LabelOptions,
+ dateLib: DateLib = defaultDateLib
+) {
+ let label = dateLib.format(date, "PPPP", options);
+ if (modifiers?.today) {
+ label = `Today, ${label}`;
+ }
+ return label;
+}
diff --git a/src/labels/labelMonthDropdown.ts b/src/labels/labelMonthDropdown.ts
index 19a06ed90c..b4f46fa63f 100644
--- a/src/labels/labelMonthDropdown.ts
+++ b/src/labels/labelMonthDropdown.ts
@@ -1,11 +1,11 @@
-import type { FormatOptions } from "../lib/dateLib.js";
+import type { LabelOptions } from "../lib/dateLib.js";
/**
- * Return the default ARIA label for the WeekNumber element.
+ * Return the default ARIA label for the months dropdown element.
*
* @group Labels
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export function labelMonthDropdown(options?: FormatOptions) {
+export function labelMonthDropdown(options?: LabelOptions) {
return "Month: ";
}
diff --git a/src/labels/labelNav.ts b/src/labels/labelNav.ts
new file mode 100644
index 0000000000..bc80ea2a57
--- /dev/null
+++ b/src/labels/labelNav.ts
@@ -0,0 +1,9 @@
+/**
+ * Return an ARIA label for the navigation toolbar, that will be announced when
+ * entering it.
+ *
+ * @group Labels
+ */
+export function labelNav(): string {
+ return "";
+}
diff --git a/src/labels/labelNext.ts b/src/labels/labelNext.ts
index b0a80bbef3..9a4a93e690 100644
--- a/src/labels/labelNext.ts
+++ b/src/labels/labelNext.ts
@@ -1,4 +1,4 @@
-import type { FormatOptions } from "../lib/dateLib.js";
+import type { LabelOptions } from "../lib/dateLib.js";
/**
* Return the default ARIA label for next month button.
@@ -10,7 +10,7 @@ export function labelNext(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
month: Date | undefined,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- options?: FormatOptions
+ options?: LabelOptions
) {
return "Next Month";
}
diff --git a/src/labels/labelPrevious.ts b/src/labels/labelPrevious.ts
index 1dca861ab7..e176862ec0 100644
--- a/src/labels/labelPrevious.ts
+++ b/src/labels/labelPrevious.ts
@@ -1,4 +1,4 @@
-import type { FormatOptions } from "../lib/dateLib.js";
+import type { LabelOptions } from "../lib/dateLib.js";
/**
* Return the default ARIA label for next month button.
@@ -10,7 +10,7 @@ export function labelPrevious(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
month: Date | undefined,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- options?: FormatOptions
+ options?: LabelOptions
) {
return "Previous Month";
}
diff --git a/src/labels/labelWeekNumber.ts b/src/labels/labelWeekNumber.ts
index 562835aa83..a21e77fa62 100644
--- a/src/labels/labelWeekNumber.ts
+++ b/src/labels/labelWeekNumber.ts
@@ -1,4 +1,4 @@
-import type { FormatOptions } from "../lib/dateLib.js";
+import type { LabelOptions } from "../lib/dateLib.js";
/**
* Return the default ARIA label for the week number element.
@@ -8,7 +8,7 @@ import type { FormatOptions } from "../lib/dateLib.js";
export function labelWeekNumber(
weekNumber: number,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- options?: FormatOptions
+ options?: LabelOptions
): string {
return `Week ${weekNumber}`;
}
diff --git a/src/labels/labelWeekNumberHeader.ts b/src/labels/labelWeekNumberHeader.ts
index ace58c8b0e..e19337e48d 100644
--- a/src/labels/labelWeekNumberHeader.ts
+++ b/src/labels/labelWeekNumberHeader.ts
@@ -1,4 +1,4 @@
-import type { FormatOptions } from "../lib/dateLib.js";
+import type { LabelOptions } from "../lib/dateLib.js";
/**
* Return the default ARIA label for the week number header element.
@@ -7,7 +7,7 @@ import type { FormatOptions } from "../lib/dateLib.js";
*/
export function labelWeekNumberHeader(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- options?: FormatOptions
+ options?: LabelOptions
): string {
return "Week Number";
}
diff --git a/src/labels/labelWeekday.ts b/src/labels/labelWeekday.ts
index fa60444d18..eb9f9ca707 100644
--- a/src/labels/labelWeekday.ts
+++ b/src/labels/labelWeekday.ts
@@ -1,4 +1,4 @@
-import type { FormatOptions } from "../lib/dateLib.js";
+import type { LabelOptions } from "../lib/dateLib.js";
import { dateLib as defaultDateLib } from "../lib/index.js";
import type { DateLib } from "../types/index.js";
@@ -9,7 +9,7 @@ import type { DateLib } from "../types/index.js";
*/
export function labelWeekday(
date: Date,
- options?: FormatOptions,
+ options?: LabelOptions,
dateLib: DateLib = defaultDateLib
): string {
return dateLib.format(date, "cccc", options);
diff --git a/src/labels/labelYearDropdown.ts b/src/labels/labelYearDropdown.ts
index 8e30ba7fd4..1603593e19 100644
--- a/src/labels/labelYearDropdown.ts
+++ b/src/labels/labelYearDropdown.ts
@@ -1,4 +1,4 @@
-import type { FormatOptions } from "../lib/dateLib.js";
+import type { LabelOptions } from "../lib/dateLib.js";
/**
* Return the default ARIA label for the years dropdown.
@@ -6,6 +6,6 @@ import type { FormatOptions } from "../lib/dateLib.js";
* @group Labels
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export function labelYearDropdown(options?: FormatOptions) {
+export function labelYearDropdown(options?: LabelOptions) {
return "Year: ";
}
diff --git a/src/lib/dateLib.ts b/src/lib/dateLib.ts
index 0b1d6d07a2..49f3e02198 100644
--- a/src/lib/dateLib.ts
+++ b/src/lib/dateLib.ts
@@ -1,4 +1,8 @@
import { GenericDateConstructor } from "date-fns";
+import type {
+ FormatOptions as DateFnsFormatOptions,
+ Locale as DateFnsLocale
+} from "date-fns";
import { addDays } from "date-fns/addDays";
import { addMonths } from "date-fns/addMonths";
import { addWeeks } from "date-fns/addWeeks";
@@ -11,7 +15,6 @@ import { endOfWeek } from "date-fns/endOfWeek";
import { endOfYear } from "date-fns/endOfYear";
import { format } from "date-fns/format";
import { getISOWeek } from "date-fns/getISOWeek";
-import { getUnixTime } from "date-fns/getUnixTime";
import { getWeek } from "date-fns/getWeek";
import { isAfter } from "date-fns/isAfter";
import { isBefore } from "date-fns/isBefore";
@@ -29,14 +32,23 @@ import { startOfMonth } from "date-fns/startOfMonth";
import { startOfWeek } from "date-fns/startOfWeek";
import { startOfYear } from "date-fns/startOfYear";
-/** @private */
-export type { Locale } from "date-fns";
-/** @private */
-export type { FormatOptions } from "date-fns";
-/** @private */
+/** The options for the {@link Formatters}. */
+export type FormatOptions = DateFnsFormatOptions;
+
+/** The options for the {@link Labels}. */
+export type LabelOptions = DateFnsFormatOptions;
+
+/** The locale used within DayPicker. */
+export type Locale = DateFnsLocale;
+
export type { Month as DateFnsMonth } from "date-fns";
-/** The default date library to use with the date picker. */
+/**
+ * The default date library to use with the date picker.
+ *
+ * @private
+ * @internal
+ */
export const dateLib = {
/** The constructor of the date object. */
Date: Date as GenericDateConstructor,
@@ -52,7 +64,6 @@ export const dateLib = {
endOfYear,
format,
getISOWeek,
- getUnixTime,
getWeek,
isAfter,
isBefore,
diff --git a/src/selection/index.ts b/src/selection/index.ts
deleted file mode 100644
index 8330934a7c..0000000000
--- a/src/selection/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from "./useSingle.js";
-export * from "./useMulti.js";
-export * from "./useRange.js";
diff --git a/src/selection/useMulti.tsx b/src/selection/useMulti.tsx
index 90cbf41f7b..fa55d84b20 100644
--- a/src/selection/useMulti.tsx
+++ b/src/selection/useMulti.tsx
@@ -1,10 +1,15 @@
import React from "react";
-import { useProps } from "../contexts/index.js";
-import type { Modifiers, PropsMulti } from "../types/index.js";
+import type {
+ DateLib,
+ DayPickerProps,
+ Modifiers,
+ PropsMulti,
+ PropsMultiRequired
+} from "../types/index.js";
-export type MultiContextValue = {
- setSelected: (
+export type UseMulti = {
+ handleSelect: (
triggerDate: Date,
modifiers: Modifiers,
e: React.MouseEvent | React.KeyboardEvent
@@ -18,24 +23,18 @@ export type MultiContextValue = {
selected: Date[] | undefined;
});
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-const MultiContext = React.createContext | undefined>(
- undefined
-);
-
-function useMultiContextValue({
- required = false,
- min = undefined,
- max = undefined,
- selected,
- onSelect
-}: T): MultiContextValue {
- const {
- mode,
- dateLib: { isSameDay, Date }
- } = useProps();
+export function useMulti(
+ props: T extends { mode: "multiple" }
+ ? PropsMulti | PropsMultiRequired
+ : object,
+ dateLib: DateLib
+): UseMulti {
+ const { selected, required, onSelect, mode } = props as PropsMulti;
+ const [dates, setDates] = React.useState(
+ mode !== "multiple" ? undefined : selected
+ );
- const [dates, setDates] = React.useState(selected);
+ const { isSameDay, Date } = dateLib;
// Update the selected date if the required flag is set.
React.useEffect(() => {
@@ -55,6 +54,8 @@ function useMultiContextValue({
return dates?.some((d) => isSameDay(d, date)) ?? false;
};
+ const { min, max } = props as PropsMulti;
+
const setSelected = (
triggerDate: Date,
modifiers: Modifiers,
@@ -80,7 +81,6 @@ function useMultiContextValue({
newDates = [...newDates, triggerDate];
}
}
-
onSelect?.(newDates, triggerDate, modifiers, e);
setDates(newDates);
return newDates;
@@ -88,33 +88,7 @@ function useMultiContextValue({
return {
selected: dates,
- setSelected,
+ handleSelect: setSelected,
isSelected
- } as MultiContextValue;
-}
-
-/** @private */
-export function MultiProvider(props: React.PropsWithChildren) {
- const value = useMultiContextValue(props);
- return (
-
- {props.children}
-
- );
-}
-
-/**
- * Access to the multi context to get the selected dates or update them.
- *
- * Use this hook from the custom components passed via the `components` prop.
- *
- * @group Hooks
- * @see https://daypicker.dev/advanced-guides/custom-components
- */
-export function useMulti() {
- const context = React.useContext(MultiContext);
- if (!context) {
- throw new Error("useMulti() must be used within a MultiContextProvider.");
- }
- return context as MultiContextValue;
+ } as UseMulti;
}
diff --git a/src/selection/useRange.tsx b/src/selection/useRange.tsx
index 91d9911fcf..e7eca835de 100644
--- a/src/selection/useRange.tsx
+++ b/src/selection/useRange.tsx
@@ -1,17 +1,26 @@
import React from "react";
-import { useProps } from "../contexts/index.js";
-import { DateRange, Modifiers, PropsRange } from "../types/index.js";
+import {
+ DateLib,
+ DateRange,
+ DayPickerProps,
+ Modifiers,
+ PropsRange,
+ PropsRangeRequired
+} from "../types/index.js";
import { addToRange, dateMatchModifiers } from "../utils/index.js";
import { isDateInRange } from "../utils/isDateInRange.js";
-export type RangeContextValue = {
- setSelected: (
+export type UseRange = {
+ handleSelect: (
triggerDate: Date,
modifiers: Modifiers,
e: React.MouseEvent | React.KeyboardEvent
) => DateRange | undefined;
isSelected: (date: Date) => boolean;
+ isRangeStart: (date: Date) => boolean;
+ isRangeEnd: (date: Date) => boolean;
+ isRangeMiddle: (date: Date) => boolean;
} & (T extends { required: true }
? {
selected: DateRange;
@@ -20,25 +29,20 @@ export type RangeContextValue = {
selected: DateRange | undefined;
});
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-const RangeContext = React.createContext | undefined>(
- undefined
-);
-
-function useRangeContextValue({
- required,
- min,
- max,
- selected,
- onSelect
-}: T): RangeContextValue {
- const { dateLib, mode, disabled } = useProps();
+export function useRange(
+ props: T extends { mode: "range" } ? PropsRange | PropsRangeRequired : object,
+ dateLib: DateLib
+): UseRange {
+ const { mode, disabled, selected, required, onSelect } = props as PropsRange;
+
const { differenceInCalendarDays } = dateLib;
- const [range, setRange] = React.useState(selected);
+ const [range, setRange] = React.useState(
+ mode === "range" ? selected : undefined
+ );
// Update the selected date if the required flag is set.
React.useEffect(() => {
- if (mode !== "multiple") return;
+ if (mode !== "range") return;
if (required && range === undefined) {
setRange({ from: undefined, to: undefined });
}
@@ -47,21 +51,23 @@ function useRangeContextValue({
// Update the selected date if the selected changes.
React.useEffect(() => {
if (mode !== "range") return;
+ if (range === selected) return;
setRange(selected);
- }, [mode, selected]);
+ }, [mode, range, selected]);
- const isSelected = required
- ? (date: Date) => isDateInRange(date, range as DateRange, dateLib)
- : (date: Date) => range && isDateInRange(date, range, dateLib);
+ const isSelected = (date: Date) =>
+ range && isDateInRange(date, range, dateLib);
const setSelected = (
triggerDate: Date,
modifiers: Modifiers,
e: React.MouseEvent | React.KeyboardEvent
) => {
+ if (mode !== "range") return;
const newRange = triggerDate
? addToRange(triggerDate, range, dateLib)
: undefined;
+ const { min, max } = props as PropsRange;
if (min) {
if (
@@ -89,7 +95,7 @@ function useRangeContextValue({
let newDate = newRange.from;
while (dateLib.differenceInCalendarDays(newRange.to, newDate) > 0) {
newDate = dateLib.addDays(newDate, 1);
- if (disabled && dateMatchModifiers(newDate, disabled)) {
+ if (disabled && dateMatchModifiers(newDate, disabled, dateLib)) {
newRange.from = triggerDate;
newRange.to = undefined;
break;
@@ -103,35 +109,33 @@ function useRangeContextValue({
return newRange;
};
- return {
- selected: range,
- setSelected,
- isSelected
- } as RangeContextValue;
-}
+ const isRangeStart = (date: Date) => {
+ return (
+ range && range.from && range.to && dateLib.isSameDay(date, range.from)
+ );
+ };
-/** @private */
-export function RangeProvider(props: React.PropsWithChildren) {
- const value = useRangeContextValue(props);
- return (
-
- {props.children}
-
- );
-}
+ const isRangeEnd = (date: Date) => {
+ return range && range.to && dateLib.isSameDay(date, range.to);
+ };
-/**
- * Access to the range context to get the selected range or update it.
- *
- * Use this hook from the custom components passed via the `components` prop.
- *
- * @group Hooks
- * @see https://daypicker.dev/advanced-guides/custom-components
- */
-export function useRange() {
- const context = React.useContext(RangeContext);
- if (!context) {
- throw new Error("useRange() must be used within a RangeContextProvider.");
- }
- return context as RangeContextValue;
+ const isRangeMiddle = (date: Date) => {
+ return (
+ range &&
+ range.from &&
+ range.to &&
+ isSelected(date) &&
+ !isRangeStart(date) &&
+ !isRangeEnd(date)
+ );
+ };
+
+ return {
+ selected: range,
+ handleSelect: setSelected,
+ isSelected,
+ isRangeStart,
+ isRangeEnd,
+ isRangeMiddle
+ } as UseRange;
}
diff --git a/src/selection/useSingle.tsx b/src/selection/useSingle.tsx
index ac5a1246f1..843a7b9783 100644
--- a/src/selection/useSingle.tsx
+++ b/src/selection/useSingle.tsx
@@ -1,10 +1,15 @@
import React from "react";
-import { useProps } from "../contexts/index.js";
-import { Modifiers, PropsSingle } from "../types/index.js";
+import type {
+ DateLib,
+ DayPickerProps,
+ Modifiers,
+ PropsSingle,
+ PropsSingleRequired
+} from "../types/index.js";
-export type SingleContextValue = {
- setSelected: (
+export type UseSingle = {
+ handleSelect: (
triggerDate: Date,
modifiers: Modifiers,
e: React.MouseEvent | React.KeyboardEvent
@@ -18,28 +23,26 @@ export type SingleContextValue = {
selected: Date | undefined;
});
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-const SingleContext = React.createContext | undefined>(
- undefined
-);
+export function useSingle(
+ props: T extends { mode: "single" }
+ ? PropsSingle | PropsSingleRequired
+ : object,
+ dateLib: DateLib
+): UseSingle {
+ const { selected, required, onSelect, mode } = props as PropsSingle;
-function useSingleContextValue({
- required = false,
- selected,
- onSelect
-}: T): SingleContextValue {
- const [date, setDate] = React.useState(selected);
- const {
- dateLib: { isSameDay },
- today,
- mode
- } = useProps();
+ const [date, setDate] = React.useState(
+ mode !== "single" ? undefined : selected
+ );
+
+ const { isSameDay, Date, startOfDay } = dateLib;
// Update the selected date if the required flag is set.
React.useEffect(() => {
+ if (mode !== "single") return;
if (required && date === undefined) {
- setDate(today);
+ setDate(startOfDay(new Date()));
}
- }, [required, date, today]);
+ }, [required, date, Date, startOfDay, mode]);
// Update the selected date if the `selected` value changes.
React.useEffect(() => {
@@ -56,41 +59,24 @@ function useSingleContextValue({
modifiers: Modifiers,
e: React.MouseEvent | React.KeyboardEvent
) => {
+ if (mode !== "single") return;
let newDate: Date | undefined = triggerDate;
if (!required && date && date && isSameDay(triggerDate, date)) {
// If the date is the same, clear the selection.
newDate = undefined;
}
setDate(newDate);
- onSelect?.(newDate, triggerDate, modifiers, e);
+ if (required) {
+ onSelect?.(newDate as Date, triggerDate, modifiers, e);
+ } else {
+ onSelect?.(newDate, triggerDate, modifiers, e);
+ }
return newDate;
};
- return { selected: date, setSelected, isSelected } as SingleContextValue;
-}
-
-/** @private */
-export function SingleProvider(props: React.PropsWithChildren) {
- const value = useSingleContextValue(props);
- return (
-
- {props.children}
-
- );
-}
-
-/**
- * Access to the single context to get the selected date or update it.
- *
- * Use this hook from the custom components passed via the `components` prop.
- *
- * @group Hooks
- * @see https://daypicker.dev/advanced-guides/custom-components
- */
-export function useSingle() {
- const context = React.useContext(SingleContext);
- if (!context) {
- throw new Error("useSingle() must be used within a SingleContextProvider");
- }
- return context;
+ return {
+ selected: date,
+ handleSelect: setSelected,
+ isSelected
+ } as UseSingle;
}
diff --git a/src/style.css b/src/style.css
index 3695205772..ef57fa9c8f 100644
--- a/src/style.css
+++ b/src/style.css
@@ -1,6 +1,6 @@
/* Variables declaration */
/* prettier-ignore */
-.rdp-calendar {
+.rdp-root {
--rdp-accent-color: blue; /* The accent color used for selected days and UI elements. */
--rdp-accent-background-color: #f0f0ff; /* The accent background color used for selected days and UI elements. */
--rdp-font-family: system-ui; /* The font family used by the calendar. Note that `inherit`does not work here. */
@@ -9,12 +9,10 @@
--rdp-day-height: 2.75rem; /* The height of the day cells. */
--rdp-day-width: 2.75rem; /* The width of the day cells. */
- --rdp-chevron-disabled-opacity: 0.5; /* The opacity of the chevron when its container is disabled. */
-
- --rdp-day_date-border-radius: 100%; /* The border radius of the day cells. */
- --rdp-day_date-border: 2px solid transparent; /* The border of the day cells. */
- --rdp-day_date-height: var(--rdp-day-width); /* The height of the day cells. */
- --rdp-day_date-width: var(--rdp-day-height); /* The width of the day cells. */
+ --rdp-day_button-border-radius: 100%; /* The border radius of the day cells. */
+ --rdp-day_button-border: 2px solid transparent; /* The border of the day cells. */
+ --rdp-day_button-height: var(--rdp-day-width); /* The height of the day cells. */
+ --rdp-day_button-width: var(--rdp-day-height); /* The width of the day cells. */
--rdp-selected-border: 2px solid var(--rdp-accent-color); /* The border of the selected days. */
--rdp-selected-font: bold large var(--rdp-font-family); /* The font of the selected days. */
@@ -34,6 +32,8 @@
--rdp-range_middle-background-color: var(--rdp-accent-background-color); /* The color of the background for days in the middle of a range. */
--rdp-range_middle-font: normal medium var(--rdp-font-family); /* The font for days in the middle of a range. */
+ --rdp-range_middle-foreground-color: white; /* The font for days in the middle of a range. */
+ --rdp-range_middle-color: inherit;/* The color of the range text. */
--rdp-range_start-color: white; /* The color of the range text. */
--rdp-range_start-background: linear-gradient(var(--rdp-gradient-direction), transparent 50%, var(--rdp-range_middle-background-color) 50%); /* Used for the background of the start of the selected range. */
@@ -52,27 +52,37 @@
--rdp-weekday-font: 500 smaller var(--rdp-font-family); /* The font of the weekday. */
--rdp-weekday-opacity: 0.75; /* The opacity of the weekday. */
- --rdp-weekday-padding: 0.25rem 0.75rem; /* THe padding of the weekday. */
+ --rdp-weekday-padding: 0.5rem 0.75rem; /* THe padding of the weekday. */
--rdp-weekday-text-align: center; /* The text alignment of the weekday cells. */
--rdp-gradient-direction: 90deg;
}
-.rdp-calendar[dir="rtl"] {
+.rdp-root[dir="rtl"] {
--rdp-gradient-direction: -90deg;
}
/* Root of the component. */
-.rdp-calendar {
- display: inline-block;
- position: relative;
+.rdp-root {
+ position: relative; /* Required to position the navigation toolbar. */
box-sizing: border-box;
}
-.rdp-calendar * {
+.rdp-root * {
box-sizing: border-box;
}
+/* Reset buttons */
+.rdp-root button {
+ border: none;
+ background: none;
+ padding: 0;
+ margin: 0;
+ cursor: pointer;
+ font: inherit;
+ color: inherit;
+}
+
.rdp-day {
justify-content: center;
align-items: center;
@@ -83,15 +93,19 @@
font: var(--rdp-day-font);
}
-.rdp-day_date {
+.rdp-day_button {
justify-content: center;
align-items: center;
display: flex;
- width: var(--rdp-day_date-width);
- height: var(--rdp-day_date-height);
- border: var(--rdp-day_date-border);
- border-radius: var(--rdp-day_date-border-radius);
+ width: var(--rdp-day_button-width);
+ height: var(--rdp-day_button-height);
+ border: var(--rdp-day_button-border);
+ border-radius: var(--rdp-day_button-border-radius);
+}
+
+.rdp-day_button:disabled {
+ cursor: revert;
}
.rdp-caption_label {
@@ -138,15 +152,11 @@
fill: var(--rdp-accent-color);
}
-.rdp-chevron_disabled {
- opacity: var(--rdp-chevron-disabled-opacity);
-}
-
-.rdp-calendar[dir="rtl"] .rdp-nav .rdp-chevron {
+.rdp-root[dir="rtl"] .rdp-nav .rdp-chevron {
transform: rotate(180deg);
}
-.rdp-calendar[dir="rtl"] .rdp-nav .rdp-chevron {
+.rdp-root[dir="rtl"] .rdp-nav .rdp-chevron {
transform: rotate(180deg);
transform-origin: 50%;
}
@@ -206,7 +216,7 @@
display: flex;
flex-wrap: wrap;
gap: var(--rdp-months-gap);
- justify-content: center;
+ max-width: fit-content;
}
.rdp-nav {
@@ -225,13 +235,13 @@
grid-template-columns: repeat(7, 1fr);
}
-.rdp-has_week_numbers .rdp-week {
+.rdp-root[data-week-numbers="true"] .rdp-week {
grid-template-columns: repeat(8, 1fr);
}
.rdp-weekday {
opacity: var(--rdp-weekday-opacity);
- padding-block: var(--rdp-weekday-padding);
+ padding: var(--rdp-weekday-padding);
font: var(--rdp-weekday-font);
text-align: var(--rdp-weekday-text-align);
text-transform: var(--rdp-weekday-text-transform);
@@ -242,7 +252,7 @@
grid-template-columns: repeat(7, 1fr);
}
-.rdp-has_week_numbers .rdp-weekdays {
+.rdp-root[data-week-numbers="true"] .rdp-weekdays {
grid-template-columns: repeat(8, 1fr);
}
@@ -272,7 +282,7 @@
font: var(--rdp-selected-font);
}
-.rdp-selected .rdp-day_date {
+.rdp-selected .rdp-day_button {
border: var(--rdp-selected-border);
}
@@ -282,7 +292,6 @@
.rdp-disabled {
opacity: var(--rdp-disabled-opacity);
- cursor: revert;
}
.rdp-hidden {
@@ -292,11 +301,11 @@
.rdp-range_start {
background: var(--rdp-range_start-background);
- color: var(--rdp-range_start-color);
}
-.rdp-range_start .rdp-day_date {
+.rdp-range_start .rdp-day_button {
background-color: var(--rdp-range_start-date-background-color);
+ color: var(--rdp-range_start-color);
}
.rdp-range_middle {
@@ -304,10 +313,11 @@
font: var(--rdp-range_middle-font);
}
-.rdp-range_middle .rdp-day_date {
+.rdp-range_middle .rdp-day_button {
border-color: transparent;
border: unset;
border-radius: unset;
+ color: var(--rdp-range_middle-color);
}
.rdp-range_end {
@@ -315,7 +325,8 @@
color: var(--rdp-range_end-color);
}
-.rdp-range_end .rdp-day_date {
+.rdp-range_end .rdp-day_button {
+ color: var(--rdp-range_start-color);
background-color: var(--rdp-range_end-date-background-color);
}
@@ -323,13 +334,7 @@
cursor: pointer;
}
-.rdp-footer,
-.rdp-months_dropdown,
-.rdp-month_wrapper,
-.rdp-month,
-.rdp-weeks,
-.rdp-no_weekdays,
-.rdp-years_dropdown {
- /* These classes are not used by the default style, but are available to use */
- /* Keep anyway for the CSS type definition. */
+.rdp-day_button:focus-visible {
+ /* outline: 2px solid var(--rdp-accent-color); */
+ /* You may want to add focus-outlines here. */
}
diff --git a/src/style.css.d.ts b/src/style.css.d.ts
index 24caf6d408..9a6d714c5c 100644
--- a/src/style.css.d.ts
+++ b/src/style.css.d.ts
@@ -1,12 +1,11 @@
declare const styles: {
readonly "rdp-button_next": string;
readonly "rdp-button_previous": string;
- readonly "rdp-calendar": string;
+ readonly "rdp-root": string;
readonly "rdp-caption_label": string;
readonly "rdp-chevron": string;
- readonly "rdp-chevron_disabled": string;
readonly "rdp-day": string;
- readonly "rdp-day_date": string;
+ readonly "rdp-day_button": string;
readonly "rdp-disabled": string;
readonly "rdp-dropdown": string;
readonly "rdp-dropdown_nav": string;
@@ -17,7 +16,7 @@ declare const styles: {
readonly "rdp-hidden": string;
readonly "rdp-month": string;
readonly "rdp-month_caption": string;
- readonly "rdp-month_wrapper": string;
+ readonly "rdp-month_grid": string;
readonly "rdp-months": string;
readonly "rdp-months_dropdown": string;
readonly "rdp-nav": string;
@@ -37,4 +36,3 @@ declare const styles: {
readonly "rdp-years_dropdown": string;
};
export = styles;
-
diff --git a/src/style.module.css b/src/style.module.css
index 34b77b2282..2cfbc2dab5 100644
--- a/src/style.module.css
+++ b/src/style.module.css
@@ -1,6 +1,6 @@
/* Variables declaration */
/* prettier-ignore */
-.calendar {
+.root {
--rdp-accent-color: blue; /* The accent color used for selected days and UI elements. */
--rdp-accent-background-color: #f0f0ff; /* The accent background color used for selected days and UI elements. */
--rdp-font-family: system-ui; /* The font family used by the calendar. Note that `inherit`does not work here. */
@@ -9,12 +9,10 @@
--rdp-day-height: 2.75rem; /* The height of the day cells. */
--rdp-day-width: 2.75rem; /* The width of the day cells. */
- --rdp-chevron-disabled-opacity: 0.5; /* The opacity of the chevron when its container is disabled. */
-
- --rdp-day_date-border-radius: 100%; /* The border radius of the day cells. */
- --rdp-day_date-border: 2px solid transparent; /* The border of the day cells. */
- --rdp-day_date-height: var(--rdp-day-width); /* The height of the day cells. */
- --rdp-day_date-width: var(--rdp-day-height); /* The width of the day cells. */
+ --rdp-day_button-border-radius: 100%; /* The border radius of the day cells. */
+ --rdp-day_button-border: 2px solid transparent; /* The border of the day cells. */
+ --rdp-day_button-height: var(--rdp-day-width); /* The height of the day cells. */
+ --rdp-day_button-width: var(--rdp-day-height); /* The width of the day cells. */
--rdp-selected-border: 2px solid var(--rdp-accent-color); /* The border of the selected days. */
--rdp-selected-font: bold large var(--rdp-font-family); /* The font of the selected days. */
@@ -34,6 +32,8 @@
--rdp-range_middle-background-color: var(--rdp-accent-background-color); /* The color of the background for days in the middle of a range. */
--rdp-range_middle-font: normal medium var(--rdp-font-family); /* The font for days in the middle of a range. */
+ --rdp-range_middle-foreground-color: white; /* The font for days in the middle of a range. */
+ --rdp-range_middle-color: inherit;/* The color of the range text. */
--rdp-range_start-color: white; /* The color of the range text. */
--rdp-range_start-background: linear-gradient(var(--rdp-gradient-direction), transparent 50%, var(--rdp-range_middle-background-color) 50%); /* Used for the background of the start of the selected range. */
@@ -52,26 +52,37 @@
--rdp-weekday-font: 500 smaller var(--rdp-font-family); /* The font of the weekday. */
--rdp-weekday-opacity: 0.75; /* The opacity of the weekday. */
- --rdp-weekday-padding: 0.25rem 0.75rem; /* THe padding of the weekday. */
+ --rdp-weekday-padding: 0.5rem 0.75rem; /* THe padding of the weekday. */
--rdp-weekday-text-align: center; /* The text alignment of the weekday cells. */
+
--rdp-gradient-direction: 90deg;
}
-.calendar[dir="rtl"] {
+.root[dir="rtl"] {
--rdp-gradient-direction: -90deg;
}
/* Root of the component. */
-.calendar {
- display: inline-block;
- position: relative;
+.root {
+ position: relative; /* Required to position the navigation toolbar. */
box-sizing: border-box;
}
-.calendar * {
+.root * {
box-sizing: border-box;
}
+/* Reset buttons */
+.root button {
+ border: none;
+ background: none;
+ padding: 0;
+ margin: 0;
+ cursor: pointer;
+ font: inherit;
+ color: inherit;
+}
+
.day {
justify-content: center;
align-items: center;
@@ -82,15 +93,19 @@
font: var(--rdp-day-font);
}
-.day_date {
+.day_button {
justify-content: center;
align-items: center;
display: flex;
- width: var(--rdp-day_date-width);
- height: var(--rdp-day_date-height);
- border: var(--rdp-day_date-border);
- border-radius: var(--rdp-day_date-border-radius);
+ width: var(--rdp-day_button-width);
+ height: var(--rdp-day_button-height);
+ border: var(--rdp-day_button-border);
+ border-radius: var(--rdp-day_button-border-radius);
+}
+
+.day_button:disabled {
+ cursor: revert;
}
.caption_label {
@@ -137,15 +152,11 @@
fill: var(--rdp-accent-color);
}
-.chevron_disabled {
- opacity: var(--rdp-chevron-disabled-opacity);
-}
-
-.calendar[dir="rtl"] .nav .chevron {
+.root[dir="rtl"] .nav .chevron {
transform: rotate(180deg);
}
-.calendar[dir="rtl"] .nav .chevron {
+.root[dir="rtl"] .nav .chevron {
transform: rotate(180deg);
transform-origin: 50%;
}
@@ -205,7 +216,7 @@
display: flex;
flex-wrap: wrap;
gap: var(--rdp-months-gap);
- justify-content: center;
+ max-width: fit-content;
}
.nav {
@@ -224,13 +235,13 @@
grid-template-columns: repeat(7, 1fr);
}
-.has_week_numbers .week {
+.root[data-week-numbers="true"] .week {
grid-template-columns: repeat(8, 1fr);
}
.weekday {
opacity: var(--rdp-weekday-opacity);
- padding-block: var(--rdp-weekday-padding);
+ padding: var(--rdp-weekday-padding);
font: var(--rdp-weekday-font);
text-align: var(--rdp-weekday-text-align);
text-transform: var(--rdp-weekday-text-transform);
@@ -241,7 +252,7 @@
grid-template-columns: repeat(7, 1fr);
}
-.has_week_numbers .weekdays {
+.root[data-week-numbers="true"] .weekdays {
grid-template-columns: repeat(8, 1fr);
}
@@ -271,7 +282,7 @@
font: var(--rdp-selected-font);
}
-.selected .day_date {
+.selected .day_button {
border: var(--rdp-selected-border);
}
@@ -281,7 +292,6 @@
.disabled {
opacity: var(--rdp-disabled-opacity);
- cursor: revert;
}
.hidden {
@@ -291,11 +301,11 @@
.range_start {
background: var(--rdp-range_start-background);
- color: var(--rdp-range_start-color);
}
-.range_start .day_date {
+.range_start .day_button {
background-color: var(--rdp-range_start-date-background-color);
+ color: var(--rdp-range_start-color);
}
.range_middle {
@@ -303,10 +313,11 @@
font: var(--rdp-range_middle-font);
}
-.range_middle .day_date {
+.range_middle .day_button {
border-color: transparent;
border: unset;
border-radius: unset;
+ color: var(--rdp-range_middle-color);
}
.range_end {
@@ -314,7 +325,8 @@
color: var(--rdp-range_end-color);
}
-.range_end .day_date {
+.range_end .day_button {
+ color: var(--rdp-range_start-color);
background-color: var(--rdp-range_end-date-background-color);
}
@@ -322,13 +334,7 @@
cursor: pointer;
}
-.footer,
-.months_dropdown,
-.month_wrapper,
-.month,
-.weeks,
-.no_weekdays,
-.years_dropdown {
- /* These classes are not used by the default style, but are available to use */
- /* Keep anyway for the CSS type definition. */
+.day_button:focus-visible {
+ /* outline: 2px solid var(--rdp-accent-color); */
+ /* You may want to add focus-outlines here. */
}
diff --git a/src/style.module.css.d.ts b/src/style.module.css.d.ts
deleted file mode 100644
index 99658c7b54..0000000000
--- a/src/style.module.css.d.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-declare const styles: {
- readonly "button_next": string;
- readonly "button_previous": string;
- readonly "calendar": string;
- readonly "caption_label": string;
- readonly "chevron": string;
- readonly "chevron_disabled": string;
- readonly "day": string;
- readonly "day_date": string;
- readonly "disabled": string;
- readonly "dropdown": string;
- readonly "dropdown_nav": string;
- readonly "dropdown_root": string;
- readonly "focusable": string;
- readonly "footer": string;
- readonly "has_week_numbers": string;
- readonly "hidden": string;
- readonly "month": string;
- readonly "month_caption": string;
- readonly "month_wrapper": string;
- readonly "months": string;
- readonly "months_dropdown": string;
- readonly "nav": string;
- readonly "no_weekdays": string;
- readonly "outside": string;
- readonly "range_end": string;
- readonly "range_middle": string;
- readonly "range_start": string;
- readonly "selected": string;
- readonly "today": string;
- readonly "week": string;
- readonly "week_number": string;
- readonly "week_number_interactive": string;
- readonly "weekday": string;
- readonly "weekdays": string;
- readonly "weeks": string;
- readonly "years_dropdown": string;
-};
-export = styles;
-
diff --git a/src/types/deprecated.ts b/src/types/deprecated.ts
index c8ac2726ff..07e537aa3d 100644
--- a/src/types/deprecated.ts
+++ b/src/types/deprecated.ts
@@ -1,18 +1,16 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
-import { Calendar } from "../components/Calendar.js";
import {
MonthCaption,
type MonthCaptionProps
} from "../components/MonthCaption.js";
import { Week, type WeekProps } from "../components/Week.js";
-import { useCalendar } from "../contexts/index.js";
-import { useProps, type PropsContextValue } from "../contexts/index.js";
import {
- labelDay,
+ labelDayButton,
labelNext,
labelWeekday,
labelWeekNumber
} from "../labels/index.js";
+import { useDayPicker } from "../useDayPicker.js";
import type { PropsMulti, PropsRange, PropsSingle } from "./props.js";
import type { Mode, DayEventHandler } from "./shared.js";
@@ -29,19 +27,11 @@ export type RootProvider = any;
*/
export type RootProviderProps = any;
-/**
- * @deprecated This type has been renamed. Use `Calendar` instead.
- * @protected
- * @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
- */
-export const Root = Calendar;
-
/**
* @deprecated This component has been renamed. Use `MonthCaption` instead.
* @protected
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
export const Caption = MonthCaption;
@@ -55,7 +45,7 @@ export type CaptionProps = MonthCaptionProps;
* @deprecated This component has been removed.
* @protected
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
export type HeadRow = any;
@@ -63,7 +53,7 @@ export type HeadRow = any;
* @deprecated This component has been renamed. Use `Week` instead.
* @protected
* @group Components
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
export const Row = Week;
@@ -135,24 +125,17 @@ export type SelectRangeEventHandler = PropsRange["onSelect"];
export type DayPickerProviderProps = any;
/**
- * @deprecated This type has been renamed to `useProps`.
- * @protected
- * @group Hooks
- */
-export const useDayPicker = useProps;
-
-/**
- * @deprecated This type has been renamed to `useCalendar`.
+ * @deprecated This type has been removed to `useDayPicker`.
* @protected
* @group Hooks
*/
-export const useNavigation = useCalendar;
+export const useNavigation = useDayPicker;
/**
* @deprecated This hook has been removed. Use a custom `Day` component instead.
* @protected
* @group Hooks
- * @see https://daypicker.dev/advanced-guides/custom-components
+ * @see https://daypicker.dev/next/guides/custom-components
*/
export type useDayRender = any;
@@ -163,10 +146,10 @@ export type useDayRender = any;
export type ContextProvidersProps = any;
/**
- * @deprecated Use `typeof labelDay` instead.
+ * @deprecated Use `typeof labelDayButton` instead.
* @protected
*/
-export type DayLabel = typeof labelDay;
+export type DayLabel = typeof labelDayButton;
/**
* @deprecated Use `typeof labelNext` or `typeof labelPrevious` instead.
@@ -228,9 +211,3 @@ export type DayPointerEventHandler = DayEventHandler;
* @protected
*/
export type DayTouchEventHandler = DayEventHandler;
-
-/**
- * @deprecated The type has been renamed. Use `PropsContext` instead.
- * @protected
- */
-export type DayPickerContext = PropsContextValue;
diff --git a/src/types/props.test.tsx b/src/types/props.test.tsx
index b9a5b15568..8e2940d48a 100644
--- a/src/types/props.test.tsx
+++ b/src/types/props.test.tsx
@@ -2,7 +2,6 @@ import React from "react";
import { DayPicker } from "../DayPicker";
-// @ts-expect-error THis is just for testing the types.
const Test = () => {
return (
<>
@@ -61,5 +60,5 @@ const Test = () => {
};
it("should type-check", () => {
- expect(true).toBeTruthy();
+ expect(Test).toBeTruthy();
});
diff --git a/src/types/props.ts b/src/types/props.ts
index dac97c0fcc..afdfcf018d 100644
--- a/src/types/props.ts
+++ b/src/types/props.ts
@@ -13,7 +13,6 @@ import type {
Formatters,
MonthChangeEventHandler,
DayEventHandler,
- WeekNumberMouseEventHandler,
Modifiers,
DateRange,
Mode,
@@ -50,6 +49,8 @@ export interface PropsBase {
* - `single`: a single day
* - `multiple`: multiple days
* - `range`: a range of days
+ *
+ * @see https://daypicker.dev/next/docs/selection-modes
*/
mode?: Mode | undefined;
@@ -60,15 +61,29 @@ export interface PropsBase {
*
* Use this prop when you need to change the default class names — for example
* when importing the style via CSS modules or when using a CSS framework.
+ *
+ * @see https://daypicker.dev/next/docs/styling
*/
classNames?: Partial;
- /** Change the class name for the day matching the `modifiers`. */
+ /**
+ * Change the class name for the day matching the `modifiers`.
+ *
+ * @see https://daypicker.dev/next/guides/custom-modifiers
+ */
modifiersClassNames?: ModifiersClassNames;
/** Style to apply to the root element. */
style?: React.CSSProperties;
- /** Change the inline styles of the HTML elements. */
+ /**
+ * Change the inline styles of the HTML elements.
+ *
+ * @see https://daypicker.dev/next/docs/styling
+ */
styles?: Partial;
- /** Change the class name for the day matching the {@link modifiers}. */
+ /**
+ * Change the class name for the day matching the {@link modifiers}.
+ *
+ * @see https://daypicker.dev/next/guides/custom-modifiers
+ */
modifiersStyles?: ModifiersStyles;
/** A unique id to replace the React generated id. Used for ARIA labels. */
id?: string;
@@ -79,6 +94,7 @@ export interface PropsBase {
* set the month programmatically, use {@link month} and {@link onMonthChange}.
*
* @defaultValue The current month
+ * @see https://daypicker.dev/next/docs/navigation
*/
defaultMonth?: Date;
/**
@@ -86,35 +102,42 @@ export interface PropsBase {
*
* As opposed to `defaultMonth`, use this prop with `onMonthChange` to change
* the month programmatically.
+ *
+ * @see https://daypicker.dev/next/docs/navigation
*/
month?: Date;
/**
* The number of displayed months.
*
* @defaultValue 1
+ * @see https://daypicker.dev/next/docs/customization#multiplemonths
*/
numberOfMonths?: number;
/**
* The earliest month to start the month navigation.
*
* @since 9.0.0
+ * @see https://daypicker.dev/next/docs/navigation#start-and-end-dates
*/
startMonth?: Date | undefined;
/**
* @private
* @deprecated This prop has been removed. Use `hidden={{ before: date }}`
* instead.
+ * @see https://daypicker.dev/next/docs/navigation#start-and-end-dates
*/
fromDate?: Date | undefined;
/**
* @private
* @deprecated This prop has been renamed to `startMonth`.
+ * @see https://daypicker.dev/next/docs/navigation#start-and-end-dates
*/
fromMonth?: Date | undefined;
/**
* @private
* @deprecated Use `startMonth` instead. E.g. `startMonth={new Date(year,
* 0)}`.
+ * @see https://daypicker.dev/next/docs/navigation#start-and-end-dates
*/
fromYear?: number | undefined;
@@ -122,30 +145,40 @@ export interface PropsBase {
* The latest month to end the month navigation.
*
* @since 9.0.0
+ * @see https://daypicker.dev/next/docs/navigation#start-and-end-dates
*/
endMonth?: Date;
/**
* @private
* @deprecated This prop has been removed. Use `hidden={{ after: date }}`
* instead.
+ * @see https://daypicker.dev/next/docs/navigation#start-and-end-dates
*/
toDate?: Date;
/**
* @private
* @deprecated This prop has been renamed to `endMonth`.
+ * @see https://daypicker.dev/next/docs/navigation#start-and-end-dates
*/
toMonth?: Date;
/**
* @private
* @deprecated Use `endMonth` instead. E.g. `endMonth={new Date(year, 0)}`.
+ * @see https://daypicker.dev/next/docs/navigation#start-and-end-dates
*/
toYear?: number;
- /** Paginate the month navigation displaying the `numberOfMonths` at time. */
+ /**
+ * Paginate the month navigation displaying the `numberOfMonths` at time.
+ *
+ * @see https://daypicker.dev/next/docs/customization#multiplemonths
+ */
pagedNavigation?: boolean;
/**
* Render the months in reversed order (when {@link numberOfMonths} is set) to
* display the most recent month first.
+ *
+ * @see https://daypicker.dev/next/docs/customization#multiplemonths
*/
reverseMonths?: boolean;
/**
@@ -153,11 +186,14 @@ export interface PropsBase {
* disable the navigation, use {@link disableNavigation}.
*
* @since 9.0.0
+ * @see https://daypicker.dev/next/docs/navigation#hidenavigation
*/
hideNavigation?: boolean;
/**
* Disable the navigation between months. This prop won't hide the navigation:
* to hide the navigation, use {@link hideNavigation}.
+ *
+ * @see https://daypicker.dev/next/docs/navigation#disablenavigation
*/
disableNavigation?: boolean;
/**
@@ -172,11 +208,15 @@ export interface PropsBase {
* **Note:** showing the dropdown will set the start/end months
* {@link fromYear} to the 100 years ago, and {@link toYear} to the current
* year.
+ *
+ * @see https://daypicker.dev/next/docs/customization#caption-layouts
*/
captionLayout?: "label" | "dropdown" | "dropdown-months" | "dropdown-years";
/**
* Display always 6 weeks per each month, regardless the month’s number of
* weeks. Weeks will be filled with the days from the next month.
+ *
+ * @see https://daypicker.dev/next/docs/customization#fixed-weeks
*/
fixedWeeks?: boolean;
/**
@@ -185,7 +225,11 @@ export interface PropsBase {
* @since 9.0.0
*/
hideWeekdayRow?: boolean;
- /** Show the outside days (days falling in the next or the previous month). */
+ /**
+ * Show the outside days (days falling in the next or the previous month).
+ *
+ * @see https://daypicker.dev/next/docs/customization#outside-days
+ */
showOutsideDays?: boolean;
/**
* Show the week numbers column. Weeks are numbered according to the local
@@ -193,54 +237,92 @@ export interface PropsBase {
*
* - To use ISO week numbering, use the `ISOWeek` prop.
* - To change how the week numbers are displayed, use the `formatters` prop.
+ *
+ * @see https://daypicker.dev/next/docs/customization#showweeknumber
*/
showWeekNumber?: boolean;
/**
* Use ISO week dates instead of the locale setting. Setting this prop will
* ignore `weekStartsOn` and `firstWeekContainsDate`.
*
+ * Use the
+ * [react-day-picker/utc](https://daypicker.dev/next/docs/localization#utc-dates)
+ * to set the calendar to UTC.
+ *
+ * @see https://daypicker.dev/next/docs/localization#iso-week-dates
* @see https://en.wikipedia.org/wiki/ISO_week_date
*/
ISOWeek?: boolean;
- /** Change the components used for rendering the calendar elements. */
+ /**
+ * Change the components used for rendering the calendar elements.
+ *
+ * @see https://daypicker.dev/next/guides/custom-components
+ */
components?: CustomComponents;
- /** Content to add to the grid as footer element. */
- footer?: React.ReactNode;
+ /**
+ * Add a footer to the calendar, acting as live region.
+ *
+ * Use this prop to communicate the calendar's status to screen readers.
+ * Prefer strings over complex UI elements.
+ *
+ * @see https://daypicker.dev/next/docs/accessibility#footer
+ */
+ footer?: React.ReactNode | string;
/**
* When a selection mode is set, DayPicker will focus the first selected day
* (if set) or the today's date (if not disabled).
*
* Use this prop when you need to focus DayPicker after a user actions, for
* improved accessibility.
+ *
+ * @see https://daypicker.dev/next/docs/accessibility#autofocus
*/
autoFocus?: boolean;
- /** Apply the `disabled` modifier to the matching days. */
+ /**
+ * Apply the `disabled` modifier to the matching days.
+ *
+ * @see https://daypicker.dev/next/docs/selection-modes#disabling-dates
+ */
disabled?: Matcher | Matcher[] | undefined;
/**
* Apply the `hidden` modifier to the matching days. Will hide them from the
* calendar.
+ *
+ * @see https://daypicker.dev/next/guides/custom-modifiers#hidden-modifier
*/
hidden?: Matcher | Matcher[] | undefined;
/**
* The today’s date. Default is the current date. This date will get the
* `today` modifier to style the day.
+ *
+ * @see https://daypicker.dev/next/guides/custom-modifiers#today-modifier
*/
today?: Date;
- /** Add modifiers to the matching days. */
+ /**
+ * Add modifiers to the matching days.
+ *
+ * @see https://daypicker.dev/next/guides/custom-modifiers
+ */
modifiers?: Record | undefined;
/**
* Labels creators to override the defaults. Use this prop to customize the
* aria-label attributes in DayPicker.
+ *
+ * @see https://daypicker.dev/next/docs/localization#aria-labels
*/
labels?: Partial;
/**
* Formatters used to format dates to strings. Use this prop to override the
* default functions.
+ *
+ * @see https://daypicker.dev/next/docs/localization#custom-formatters
*/
formatters?: Partial;
/**
* The text direction of the calendar. Use `ltr` for left-to-right (default)
* or `rtl` for right-to-left.
+ *
+ * @see https://daypicker.dev/next/docs/localization#rtl-text-direction
*/
dir?: HTMLDivElement["dir"];
/**
@@ -256,19 +338,20 @@ export interface PropsBase {
* The date-fns locale object used to localize dates.
*
* @defaultValue en-US
- * @see https://date-fns.org/docs/Locale
+ * @see https://daypicker.dev/next/docs/localization
*/
locale?: Locale | undefined;
/**
* The index of the first day of the week (0 - Sunday). Overrides the locale's
* one.
+ *
+ * @see https://daypicker.dev/next/docs/localization#first-date-of-the-week
*/
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined;
/**
* The day of January, which is always in the first week of the year.
*
- * @see https://date-fns.org/docs/getWeek
- * @see https://en.wikipedia.org/wiki/Week#Numbering
+ * @see https://daypicker.dev/next/docs/localization#first-week-contains-date
*/
firstWeekContainsDate?: 1 | 4;
/**
@@ -286,57 +369,98 @@ export interface PropsBase {
*/
useAdditionalDayOfYearTokens?: boolean | undefined;
- /* EVENT HANDLERS */
- /** Event fired when the user navigates between months. */
+ /**
+ * Event fired when the user navigates between months.
+ *
+ * @see https://daypicker.dev/next/docs/navigation#onmonthchange
+ */
onMonthChange?: MonthChangeEventHandler;
+
+ /**
+ * Event handler when the next month button is clicked.
+ *
+ * @see https://daypicker.dev/next/docs/navigation
+ */
+ onNextClick?: MonthChangeEventHandler;
+ /**
+ * Event handler when the previous month button is clicked.
+ *
+ * @see https://daypicker.dev/next/docs/navigation
+ */
+ onPrevClick?: MonthChangeEventHandler;
+ /**
+ * Event handler when a week number is clicked
+ *
+ * @deprecated Use a custom `WeekNumber` component instead.
+ * @see http://daypicker.dev/next/docs/customization#showweeknumber
+ */
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ onWeekNumberClick?: any;
+
/** Event handler when a day is clicked. */
onDayClick?: DayEventHandler;
/** Event handler when a day is focused. */
onDayFocus?: DayEventHandler;
/** Event handler when a day is blurred. */
onDayBlur?: DayEventHandler;
- /** Event handler when the mouse enters a day. */
- onDayMouseEnter?: DayEventHandler;
- /** Event handler when the mouse leaves a day. */
- onDayMouseLeave?: DayEventHandler;
/** Event handler when a key is pressed on a day. */
onDayKeyDown?: DayEventHandler;
- /** Event handler when a key is released on a day. */
+
+ /**
+ * Replace the default date library with a custom one.
+ *
+ * @private
+ * @since 9.0.0
+ * @experimental
+ */
+ dateLib?: Partial | undefined;
+
+ /**
+ * @private
+ * @deprecated Use a custom `DayButton` component instead.
+ */
onDayKeyUp?: DayEventHandler;
- /** Event handler when a key is pressed and released on a day. */
+ /**
+ * @private
+ * @deprecated Use a custom `DayButton` component instead.
+ */
onDayKeyPress?: DayEventHandler;
- /** Event handler when a pointer enters a day. */
+ /**
+ * @private
+ * @deprecated Use a custom `DayButton` component instead.
+ */
onDayPointerEnter?: DayEventHandler;
- /** Event handler when a pointer leaves a day. */
+ /**
+ * @private
+ * @deprecated Use a custom `DayButton` component instead.
+ */
onDayPointerLeave?: DayEventHandler;
- /** Event handler when a touch is cancelled on a day. */
+ /**
+ * @private
+ * @deprecated Use a custom `DayButton` component instead.
+ */
onDayTouchCancel?: DayEventHandler;
- /** Event handler when a touch ends on a day. */
+ /**
+ * @private
+ * @deprecated Use a custom `DayButton` component instead.
+ */
onDayTouchEnd?: DayEventHandler;
- /** Event handler when a touch moves on a day. */
+ /**
+ * @private
+ * @deprecated Use a custom `DayButton` component instead.
+ */
onDayTouchMove?: DayEventHandler;
- /** Event handler when a touch starts on a day. */
- onDayTouchStart?: DayEventHandler;
- /** Event handler when the next month button is clicked. */
- onNextClick?: MonthChangeEventHandler;
- /** Event handler when the previous month button is clicked. */
- onPrevClick?: MonthChangeEventHandler;
- /** Event handler when a week number is clicked */
- onWeekNumberClick?: WeekNumberMouseEventHandler;
-
/**
- * Replace the default date library with a custom one.
- *
* @private
- * @since 9.0.0
- * @experimental
+ * @deprecated Use a custom `DayButton` component instead.
*/
- dateLib?: Partial | undefined;
+ onDayTouchStart?: DayEventHandler;
}
/**
* The props when the single selection is required.
*
* @group Props
+ * @see https://daypicker.dev/next/docs/selection-modes#single-mode
*/
export interface PropsSingleRequired {
mode: "single";
@@ -353,6 +477,7 @@ export interface PropsSingleRequired {
* The props when the single selection is optional.
*
* @group Props
+ * @see https://daypicker.dev/next/docs/selection-modes#single-mode
*/
export interface PropsSingle {
mode: "single";
@@ -369,6 +494,7 @@ export interface PropsSingle {
* The props when the multiple selection is required.
*
* @group Props
+ * @see https://daypicker.dev/next/docs/selection-modes#multiple-mode
*/
export interface PropsMultiRequired {
mode: "multiple";
@@ -387,6 +513,7 @@ export interface PropsMultiRequired {
* The props when the multiple selection is optional.
*
* @group Props
+ * @see https://daypicker.dev/next/docs/selection-modes#multiple-mode
*/
export interface PropsMulti {
mode: "multiple";
@@ -405,6 +532,7 @@ export interface PropsMulti {
* The props when the range selection is required.
*
* @group Props
+ * @see https://daypicker.dev/next/docs/selection-modes#range-mode
*/
export interface PropsRangeRequired {
mode: "range";
@@ -423,11 +551,13 @@ export interface PropsRangeRequired {
* The props when the range selection is optional.
*
* @group Props
+ * @see https://daypicker.dev/next/docs/selection-modes#range-mode
*/
export interface PropsRange {
mode: "range";
required?: false | undefined;
selected?: DateRange | undefined;
+ disabled?: Matcher | Matcher[] | undefined;
onSelect?: (
selected: DateRange | undefined,
triggerDate: Date,
diff --git a/src/types/shared.ts b/src/types/shared.ts
index 9fe8483c2a..1c81d418ff 100644
--- a/src/types/shared.ts
+++ b/src/types/shared.ts
@@ -1,14 +1,7 @@
/* eslint-disable @typescript-eslint/ban-types */
-import type { MouseEvent, CSSProperties } from "react";
+import type { CSSProperties } from "react";
-import {
- UI,
- DayFlag,
- CalendarFlag,
- ChevronFlag,
- WeekNumberFlag,
- SelectionState
-} from "../UI.js";
+import { UI, DayFlag, SelectionState } from "../UI.js";
import * as components from "../components/custom-components.js";
import {
formatCaption,
@@ -21,8 +14,10 @@ import {
formatYearDropdown
} from "../formatters/index.js";
import {
- labelDay,
- labelCaption,
+ labelDayButton,
+ labelNav,
+ labelGrid,
+ labelGridcell,
labelMonthDropdown,
labelNext,
labelPrevious,
@@ -40,7 +35,7 @@ import { dateLib } from "../lib/index.js";
* - `multiple`: allow selecting multiple days.
* - `range`: use DayPicker to select a range of days.
*
- * @see https://daypicker.dev/next/using-daypicker/selection-modes
+ * @see https://daypicker.dev/next/docs/selection-modes
*/
export type Mode = "single" | "multiple" | "range";
@@ -71,27 +66,33 @@ export type Formatters = {
formatDay: typeof formatDay;
/** Format the week number. */
formatWeekNumber: typeof formatWeekNumber;
- /** Format the week day name in the header */
+ /** Format the week day name in the header. */
formatWeekdayName: typeof formatWeekdayName;
};
/** Map of functions to translate ARIA labels for the relative elements. */
export type Labels = {
- /** Return the label for the month dropdown. */
- labelCaption: typeof labelCaption;
- /** Return the label for the month dropdown. */
+ /** The label for the navigation toolbar. */
+ labelNav: typeof labelNav;
+ /** The label for the month grid. */
+ labelGrid: typeof labelGrid;
+ /** The label for the gridcell, when the calendar is not interactive. */
+ labelGridcell: typeof labelGridcell;
+ /** The label for the month dropdown. */
labelMonthDropdown: typeof labelMonthDropdown;
- /** Return the label for the year dropdown. */
+ /** The label for the year dropdown. */
labelYearDropdown: typeof labelYearDropdown;
- /** Return the label for the next month button. */
+ /** The label for the "next month" button. */
labelNext: typeof labelNext;
- /** Return the label for the previous month button. */
+ /** The label for the "previous month" button. */
labelPrevious: typeof labelPrevious;
- /** Return the label for the day cell. */
- labelDay: typeof labelDay;
- /** Return the label for the weekday. */
+ /** The label for the day button.. */
+ labelDayButton: typeof labelDayButton;
+ /** @deprecated Use {@link labelDayButton} instead. */
+ labelDay: typeof labelDayButton;
+ /** The label for the weekday. */
labelWeekday: typeof labelWeekday;
- /** Return the label for the week number. */
+ /** The label for the week number. */
labelWeekNumber: typeof labelWeekNumber;
/**
* Return the label for the column of the week number.
@@ -208,36 +209,14 @@ export type DayEventHandler = (
/** The event handler when a month is changed in the calendar. */
export type MonthChangeEventHandler = (month: Date) => void;
-/** The event handler when the week number is clicked. */
-export type WeekNumberMouseEventHandler = (
- /** The week number that has been clicked. */
- weekNumber: number,
- /** The dates in the clicked week. */
- dates: Date[],
- /** The mouse event that triggered this event. */
- e: MouseEvent
-) => void;
-
/** Maps user interface elements, selection states, and flags to a CSS style. */
export type Styles = {
- [key in
- | UI
- | SelectionState
- | DayFlag
- | CalendarFlag
- | ChevronFlag
- | WeekNumberFlag]: CSSProperties | undefined;
+ [key in UI | SelectionState | DayFlag]: CSSProperties | undefined;
};
/** Defines the class names for various UI components and states. */
export type ClassNames = {
- [key in
- | UI
- | SelectionState
- | DayFlag
- | CalendarFlag
- | ChevronFlag
- | WeekNumberFlag]: string;
+ [key in UI | SelectionState | DayFlag]: string;
};
/** The flags that are matching a day in the calendar. */
diff --git a/src/useCalendar.ts b/src/useCalendar.ts
new file mode 100644
index 0000000000..a2eb47a65d
--- /dev/null
+++ b/src/useCalendar.ts
@@ -0,0 +1,225 @@
+import type {
+ CalendarWeek,
+ CalendarDay,
+ CalendarMonth
+} from "./classes/index.js";
+import { getDates } from "./helpers/getDates.js";
+import { getDays } from "./helpers/getDays.js";
+import { getDisplayMonths } from "./helpers/getDisplayMonths.js";
+import { getInitialMonth } from "./helpers/getInitialMonth.js";
+import { getMonths } from "./helpers/getMonths.js";
+import { getNavMonths } from "./helpers/getNavMonth.js";
+import { getNextMonth } from "./helpers/getNextMonth.js";
+import { getPreviousMonth } from "./helpers/getPreviousMonth.js";
+import { getWeeks } from "./helpers/getWeeks.js";
+import { useControlledValue } from "./helpers/useControlledValue.js";
+import type { DayPickerProps } from "./types/props.js";
+import type { DateLib } from "./types/shared.js";
+
+/**
+ * The hook to get and handle the calendar state.
+ *
+ * @see https://daypicker.dev/next/guides/custom-components
+ */
+export interface UseCalendar {
+ today: Date;
+ /** All the unique dates displayed to the calendar. */
+ dates: Date[];
+ /**
+ * All the days displayed in the calendar. As opposite from
+ * {@link CalendarContext.dates}, it may return duplicated dates when shown
+ * outside the month.
+ */
+ days: CalendarDay[];
+ /** The months displayed in the calendar. */
+ weeks: CalendarWeek[];
+ /** The months displayed in the calendar. */
+ months: CalendarMonth[];
+ /**
+ * The month displayed as first the calendar. When `numberOfMonths` is greater
+ * than `1`, it is the first of the displayed months.
+ */
+ firstMonth: Date;
+ /**
+ * The month displayed as last the calendar. When `numberOfMonths` is greater
+ * than `1`, it is the last of the displayed months.
+ */
+ lastMonth: Date;
+
+ /** The next month to display. */
+ nextMonth: Date | undefined;
+ /** The previous month to display. */
+ previousMonth: Date | undefined;
+
+ /**
+ * The month where the navigation starts. `undefined` if the calendar can be
+ * navigated indefinitely to the past.
+ */
+ navigationStartMonth: Date | undefined;
+ /**
+ * The month where the navigation ends. `undefined` if the calendar can be
+ * navigated indefinitely to the past.
+ */
+ navigationEndMonth: Date | undefined;
+
+ /** Set the first month displayed in the calendar. */
+ setFirstMonth: (date: Date) => void;
+
+ /** Navigate to the specified month. Will fire the `onMonthChange` callback. */
+ goToMonth: (month: Date) => void;
+ /** Navigate to the next month. */
+ goToNextMonth: () => void;
+ /** Navigate to the previous month. */
+ goToPreviousMonth: () => void;
+ /**
+ * Navigate to the specified date. If the second parameter (refDate) is
+ * provided and the date is before the refDate, then the month is set to one
+ * month before the date.
+ *
+ * @param day - The date to navigate to.
+ * @param dateToCompare - Optional. If `date` is before `dateToCompare`, the
+ * month is set to one month before the date.
+ */
+ goToDay: (day: CalendarDay) => void;
+ /** Whether the given date is included in the displayed months. */
+ isDayDisplayed: (day: CalendarDay) => boolean;
+}
+
+/** @private */
+export function useCalendar(
+ props: Pick<
+ DayPickerProps,
+ | "fromYear"
+ | "toYear"
+ | "startMonth"
+ | "endMonth"
+ | "month"
+ | "defaultMonth"
+ | "today"
+ | "numberOfMonths"
+ | "disableNavigation"
+ | "onMonthChange"
+ | "ISOWeek"
+ >,
+ dateLib: DateLib
+) {
+ const today = dateLib.startOfDay(props.today ?? new dateLib.Date());
+
+ const [navigationStartMonth, navigationEndMonth] = getNavMonths(
+ props,
+ dateLib
+ );
+
+ const { startOfMonth } = dateLib;
+
+ const initialDisplayMonth = getInitialMonth(props, dateLib);
+
+ // The first month displayed in the calendar
+ const [firstMonth, setFirstMonth] = useControlledValue(
+ initialDisplayMonth,
+ props.month ? startOfMonth(props.month) : undefined
+ );
+
+ /** An array of the months displayed in the calendar. */
+ const displayMonths = getDisplayMonths(
+ firstMonth,
+ navigationEndMonth,
+ props,
+ dateLib
+ );
+
+ /** The last month displayed in the calendar. */
+ const lastMonth = displayMonths[displayMonths.length - 1];
+
+ /** An array of the dates displayed in the calendar. */
+ const dates = getDates(displayMonths, props.endMonth, props, dateLib);
+
+ /** An array of the Months displayed in the calendar. */
+ const months = getMonths(displayMonths, dates, props, dateLib);
+
+ /** An array of the Weeks displayed in the calendar. */
+ const weeks = getWeeks(months);
+
+ /** An array of the Days displayed in the calendar. */
+ const days = getDays(months);
+
+ const previousMonth = getPreviousMonth(
+ firstMonth,
+ navigationStartMonth,
+ props,
+ dateLib
+ );
+ const nextMonth = getNextMonth(
+ firstMonth,
+ navigationEndMonth,
+ props,
+ dateLib
+ );
+
+ const { disableNavigation, onMonthChange } = props;
+
+ function isDayDisplayed(day: CalendarDay) {
+ return weeks.some((week: CalendarWeek) => {
+ return week.days.some((d) => {
+ return d.isEqualTo(day);
+ });
+ });
+ }
+
+ function goToMonth(date: Date) {
+ if (disableNavigation) {
+ return;
+ }
+ let newMonth = startOfMonth(date);
+ // if month is before start, use the first month instead
+ if (navigationStartMonth && newMonth < startOfMonth(navigationStartMonth)) {
+ newMonth = startOfMonth(navigationStartMonth);
+ }
+ // if month is after endMonth, use the last month instead
+ if (navigationEndMonth && newMonth > startOfMonth(navigationEndMonth)) {
+ newMonth = startOfMonth(navigationEndMonth);
+ }
+ setFirstMonth(newMonth);
+ onMonthChange?.(newMonth);
+ }
+
+ function goToDay(day: CalendarDay) {
+ if (isDayDisplayed(day)) {
+ return;
+ }
+ goToMonth(day.date);
+ }
+
+ function goToNextMonth() {
+ return nextMonth ? goToMonth(nextMonth) : undefined;
+ }
+ function goToPreviousMonth() {
+ return previousMonth ? goToMonth(previousMonth) : undefined;
+ }
+
+ const calendar: UseCalendar = {
+ dates,
+ months,
+ weeks,
+ days,
+ today,
+
+ navigationStartMonth: navigationStartMonth,
+ navigationEndMonth: navigationEndMonth,
+
+ firstMonth: firstMonth,
+ lastMonth,
+ previousMonth,
+ nextMonth,
+
+ setFirstMonth,
+
+ isDayDisplayed,
+ goToMonth,
+ goToDay,
+ goToNextMonth,
+ goToPreviousMonth
+ };
+
+ return calendar;
+}
diff --git a/src/useDayPicker.ts b/src/useDayPicker.ts
new file mode 100644
index 0000000000..2013c5299e
--- /dev/null
+++ b/src/useDayPicker.ts
@@ -0,0 +1,39 @@
+import { createContext, useContext } from "react";
+
+import type { DayPickerProps } from "./types/props.js";
+import type { UseCalendar } from "./useCalendar.js";
+import type { UseModifiers } from "./useModifiers.js";
+import type {
+ UseMulti,
+ UseRange,
+ UseSelection,
+ UseSingle
+} from "./useSelection.js";
+
+export type {
+ UseCalendar,
+ UseModifiers,
+ UseSelection,
+ UseMulti,
+ UseRange,
+ UseSingle
+};
+
+// Create a context with a default value
+export const dayPickerContext = createContext<
+ (UseCalendar & UseModifiers & UseSelection) | undefined
+>(undefined);
+
+/**
+ * Return API to work with ` ` inside custom components.
+ *
+ * @group Hooks
+ * @see https://daypicker.dev/next/guides/custom-components
+ */
+export function useDayPicker(props?: T) {
+ const context = useContext(dayPickerContext);
+ if (context === undefined) {
+ throw new Error("useDayPicker() must be used within a custom component.");
+ }
+ return context as UseCalendar & UseModifiers & UseSelection;
+}
diff --git a/src/useFocus.ts b/src/useFocus.ts
new file mode 100644
index 0000000000..f0ad029dfa
--- /dev/null
+++ b/src/useFocus.ts
@@ -0,0 +1,132 @@
+import { useState } from "react";
+
+import type { CalendarDay } from "./classes/index.js";
+import { calculateFocusTarget } from "./helpers/calculateFocusTarget.js";
+import { getNextFocus } from "./helpers/getNextFocus.js";
+import type {
+ MoveFocusBy,
+ MoveFocusDir,
+ DateLib,
+ DayPickerProps,
+ Mode
+} from "./types/index.js";
+import { UseCalendar } from "./useCalendar.js";
+import { UseModifiers } from "./useModifiers.js";
+import { UseSelection } from "./useSelection.js";
+
+export type UseFocus = {
+ /** The date that is currently focused. */
+ focused: CalendarDay | undefined;
+ /** The date that is target of the focus when tabbing into the month grid. */
+ // focusTarget: CalendarDay | undefined;
+
+ isFocusTarget: (day: CalendarDay) => boolean;
+
+ /** Focus the given day. */
+ setFocused: (day: CalendarDay | undefined) => void;
+
+ /** Set the last focused day. */
+ setLastFocused: (day: CalendarDay | undefined) => void;
+
+ /** Blur the focused day. */
+ blur: () => void;
+ /** Focus the day after the focused day. */
+ focusDayAfter: () => void;
+ /** Focus the day before the focused day. */
+ focusDayBefore: () => void;
+ /** Focus the day in the week before the focused day. */
+ focusWeekBefore: () => void;
+ /** Focus the day in the week after the focused day. */
+ focusWeekAfter: () => void;
+ /* Focus the day in the month before the focused day. */
+ focusMonthBefore: () => void;
+ /* Focus the day in the month after the focused day. */
+ focusMonthAfter: () => void;
+ /* Focus the day in the year before the focused day. */
+ focusYearBefore: () => void;
+ /* Focus the day in the year after the focused day. */
+ focusYearAfter: () => void;
+ /* Focus the day at the start of the week of the focused day. */
+ focusStartOfWeek: () => void;
+ /* Focus the day at the end of the week of focused day. */
+ focusEndOfWeek: () => void;
+};
+
+/** @private */
+export function useFocus(
+ props: Pick<
+ DayPickerProps,
+ | "autoFocus"
+ | "disabled"
+ | "hidden"
+ | "modifiers"
+ | "numberOfMonths"
+ | "locale"
+ | "ISOWeek"
+ | "weekStartsOn"
+ >,
+ calendar: UseCalendar,
+ modifiers: UseModifiers,
+ selection: UseSelection<{ mode: Mode }>,
+ dateLib: DateLib
+): UseFocus {
+ const { getModifiers } = modifiers;
+ const { autoFocus } = props;
+ const [lastFocused, setLastFocused] = useState();
+
+ const focusTarget = calculateFocusTarget(
+ calendar,
+ getModifiers,
+ selection.isSelected,
+ lastFocused
+ );
+ const [focusedDay, setFocused] = useState(
+ autoFocus ? focusTarget : undefined
+ );
+
+ const blur = () => {
+ setLastFocused(focusedDay);
+ setFocused(undefined);
+ };
+
+ const moveFocus = (moveBy: MoveFocusBy, moveDir: MoveFocusDir) => {
+ if (!focusedDay) return;
+ const nextFocus = getNextFocus(
+ moveBy,
+ moveDir,
+ focusedDay,
+ calendar.navigationStartMonth,
+ calendar.navigationEndMonth,
+ props,
+ dateLib
+ );
+ if (!nextFocus) return;
+
+ calendar.goToDay(nextFocus);
+ setFocused(nextFocus);
+ };
+
+ const isFocusTarget = (day: CalendarDay) => {
+ return Boolean(focusTarget?.isEqualTo(day));
+ };
+
+ const useFocus: UseFocus = {
+ isFocusTarget,
+ setFocused,
+ focused: focusedDay,
+ setLastFocused,
+ blur,
+ focusDayAfter: () => moveFocus("day", "after"),
+ focusDayBefore: () => moveFocus("day", "before"),
+ focusWeekAfter: () => moveFocus("week", "after"),
+ focusWeekBefore: () => moveFocus("week", "before"),
+ focusMonthBefore: () => moveFocus("month", "before"),
+ focusMonthAfter: () => moveFocus("month", "after"),
+ focusYearBefore: () => moveFocus("year", "before"),
+ focusYearAfter: () => moveFocus("year", "after"),
+ focusStartOfWeek: () => moveFocus("startOfWeek", "before"),
+ focusEndOfWeek: () => moveFocus("endOfWeek", "after")
+ };
+
+ return useFocus;
+}
diff --git a/src/contexts/useModifiers.tsx b/src/useModifiers.tsx
similarity index 51%
rename from src/contexts/useModifiers.tsx
rename to src/useModifiers.tsx
index c570c95476..e5a28bff01 100644
--- a/src/contexts/useModifiers.tsx
+++ b/src/useModifiers.tsx
@@ -1,28 +1,17 @@
-import React, { type ReactElement, createContext, useContext } from "react";
-
-import { DayFlag, SelectionState } from "../UI.js";
-import { CalendarDay } from "../classes/index.js";
-import { useMulti } from "../selection/useMulti.js";
-import { useRange } from "../selection/useRange.js";
-import { useSingle } from "../selection/useSingle.js";
+import { DayFlag, SelectionState } from "./UI.js";
+import { CalendarDay } from "./classes/index.js";
import type {
CustomModifiers,
+ DateLib,
DayFlags,
+ DayPickerProps,
Modifiers,
SelectionStates
-} from "../types/index.js";
-import { dateMatchModifiers } from "../utils/dateMatchModifiers.js";
-import { isDateInRange } from "../utils/index.js";
-
-import { useCalendar } from "./useCalendar.js";
-import { useProps } from "./useProps.js";
+} from "./types/index.js";
+import type { UseCalendar } from "./useCalendar.js";
+import { dateMatchModifiers } from "./utils/dateMatchModifiers.js";
-/** @private */
-const ModifiersContext = createContext(
- undefined
-);
-
-export type ModifiersContextValue = {
+export type UseModifiers = {
/** List the days with custom modifiers passed via the `modifiers` prop. */
customModifiers: Record;
/** List the days with the internal modifiers. */
@@ -33,31 +22,28 @@ export type ModifiersContextValue = {
getModifiers: (day: CalendarDay) => Modifiers;
};
-function useModifiersContextValue(): ModifiersContextValue {
- const {
- dateLib,
- disabled,
- hidden,
- modifiers,
- mode,
- onDayClick,
- showOutsideDays,
- today
- } = useProps();
-
- const calendar = useCalendar();
- const single = useSingle();
- const multi = useMulti();
- const range = useRange();
-
- const { isSameDay, isSameMonth } = dateLib;
+/** @private */
+export function useModifiers(
+ props: Pick<
+ DayPickerProps,
+ "disabled" | "hidden" | "modifiers" | "showOutsideDays" | "today"
+ >,
+ calendar: UseCalendar,
+ dateLib: DateLib
+): UseModifiers {
+ // const single = useSingle();
+ // const multi = useMulti();
+ // const range = useRange();
+
+ const { disabled, hidden, modifiers, showOutsideDays, today } = props;
+
+ const { isSameDay, isSameMonth, Date } = dateLib;
const internal: Record = {
[DayFlag.focused]: [],
[DayFlag.outside]: [],
[DayFlag.disabled]: [],
[DayFlag.hidden]: [],
- [DayFlag.today]: [],
- [DayFlag.focusable]: []
+ [DayFlag.today]: []
};
const custom: Record = {};
@@ -82,50 +68,13 @@ function useModifiersContextValue(): ModifiersContextValue {
Boolean(hidden && dateMatchModifiers(date, hidden, dateLib)) ||
(!showOutsideDays && isOutside);
- const isElementInteractive = mode || onDayClick !== undefined;
-
- const isFocusable = isElementInteractive && !isDisabled && !isHidden;
-
- const isToday = isSameDay(date, today);
+ const isToday = isSameDay(date, today ?? new Date());
if (isOutside) internal.outside.push(day);
if (isDisabled) internal.disabled.push(day);
if (isHidden) internal.hidden.push(day);
- if (isFocusable) internal.focusable.push(day);
if (isToday) internal.today.push(day);
- // Add the selection modifiers
- if (mode === "single" && !isDisabled) {
- if (single.isSelected(day.date)) {
- selection[SelectionState.selected].push(day);
- }
- }
- if (mode === "multiple" && !isDisabled) {
- if (multi.isSelected(day.date)) {
- selection[SelectionState.selected].push(day);
- }
- }
-
- if (mode === "range" && !isDisabled) {
- if (range.isSelected(day.date)) {
- selection[SelectionState.selected].push(day);
- if (range.selected?.from && isSameDay(day.date, range.selected.from)) {
- if (range.selected?.to)
- selection[SelectionState.range_start].push(day);
- } else if (
- range.selected?.to &&
- isSameDay(day.date, range.selected.to)
- ) {
- selection[SelectionState.range_end].push(day);
- } else if (
- range.selected &&
- isDateInRange(day.date, range.selected, dateLib)
- ) {
- selection[SelectionState.range_middle].push(day);
- }
- }
- }
-
// Add custom modifiers
if (modifiers) {
Object.keys(modifiers).forEach((name) => {
@@ -148,7 +97,6 @@ function useModifiersContextValue(): ModifiersContextValue {
const dayFlags: DayFlags = {
[DayFlag.focused]: false,
[DayFlag.disabled]: false,
- [DayFlag.focusable]: false,
[DayFlag.hidden]: false,
[DayFlag.outside]: false,
[DayFlag.today]: false
@@ -189,33 +137,3 @@ function useModifiersContextValue(): ModifiersContextValue {
getModifiers
};
}
-
-/** @private */
-export function ModifiersContextProvider({
- children
-}: {
- children: ReactElement;
-}) {
- const modifiers = useModifiersContextValue();
-
- return (
-
- {children}
-
- );
-}
-
-/**
- * Access to the modifiers for the days in the calendar.
- *
- * @group Hooks
- */
-export function useModifiers() {
- const modifiersContext = useContext(ModifiersContext);
- if (!modifiersContext) {
- throw new Error(
- "useModifiersContext() must be used within a ModifiersContextProvider"
- );
- }
- return modifiersContext;
-}
diff --git a/src/useSelection.ts b/src/useSelection.ts
new file mode 100644
index 0000000000..5ada49714b
--- /dev/null
+++ b/src/useSelection.ts
@@ -0,0 +1,58 @@
+import { dateLib as defaultDateLib } from "./lib/dateLib.js";
+import { type UseMulti, useMulti } from "./selection/useMulti.js";
+import { type UseRange, useRange } from "./selection/useRange.js";
+import { type UseSingle, useSingle } from "./selection/useSingle.js";
+import type {
+ DateLib,
+ DayPickerProps,
+ Mode,
+ PropsMulti,
+ PropsMultiRequired,
+ PropsRange,
+ PropsRangeRequired,
+ PropsSingle,
+ PropsSingleRequired
+} from "./types/index.js";
+
+export type { UseMulti, UseRange, UseSingle };
+
+export function useSelection(
+ props: T,
+ dateLib?: DateLib
+): UseSelection {
+ const lib = { ...defaultDateLib, ...dateLib };
+ const single = useSingle(props as PropsSingle | PropsSingleRequired, lib);
+ const multi = useMulti(props as PropsMulti | PropsMultiRequired, lib);
+ const range = useRange(props as PropsRange | PropsRangeRequired, lib);
+
+ switch (props.mode) {
+ case "single":
+ // @ts-expect-error The type of `single` is `UseSingle`.
+ return single;
+ case "multiple":
+ // @ts-expect-error The type of `multi` is `UseMulti`.
+ return multi;
+ case "range":
+ // @ts-expect-error The type of `range` is `UseRange`.
+ return range;
+ default:
+ // @ts-expect-error The type of the return value correct
+ return {
+ handleSelect: () => undefined,
+ isSelected: () => false,
+ selected: undefined
+ };
+ }
+}
+
+export type UseSelection = T extends { mode: Mode }
+ ? {
+ single: UseSingle;
+ multiple: UseMulti;
+ range: UseRange;
+ }[T["mode"]]
+ : {
+ handleSelect: () => undefined;
+ isSelected: () => false;
+ selected: undefined;
+ };
diff --git a/src/utils/dateMatchModifiers.ts b/src/utils/dateMatchModifiers.ts
index 73d1a5a1e4..6fc0df7453 100644
--- a/src/utils/dateMatchModifiers.ts
+++ b/src/utils/dateMatchModifiers.ts
@@ -1,4 +1,3 @@
-import { dateLib as defaultDateLib } from "../lib/index.js";
import type { DateLib, Matcher } from "../types/index.js";
import { isDateInRange } from "./isDateInRange.js";
@@ -10,18 +9,17 @@ import {
isDayOfWeekType
} from "./typeguards.js";
-/** Returns true if `value` is a Date type. */
-function isDateType(value: unknown): value is Date {
- return defaultDateLib.isDate(value);
-}
-
/** Returns true if `value` is an array of valid dates. */
-function isArrayOfDates(value: unknown): value is Date[] {
- return Array.isArray(value) && value.every(defaultDateLib.isDate);
+export function isDatesArray(
+ value: unknown,
+ dateLib: DateLib
+): value is Date[] {
+ return Array.isArray(value) && value.every(dateLib.isDate);
}
/**
- * Returns whether a day matches against at least one of the given Matchers.
+ * Returns whether a day matches against at least one of the given
+ * {@link Matcher}`.
*
* ```tsx
* const day = new Date(2022, 5, 19);
@@ -41,22 +39,22 @@ function isArrayOfDates(value: unknown): value is Date[] {
export function dateMatchModifiers(
date: Date,
matchers: Matcher | Matcher[],
- dateUtils: DateLib = defaultDateLib
+ dateLib: DateLib
): boolean {
const matchersArr = !Array.isArray(matchers) ? [matchers] : matchers;
- const { isSameDay, differenceInCalendarDays, isAfter } = dateUtils;
+ const { isSameDay, differenceInCalendarDays, isAfter } = dateLib;
return matchersArr.some((matcher: Matcher) => {
if (typeof matcher === "boolean") {
return matcher;
}
- if (isDateType(matcher)) {
+ if (dateLib.isDate(matcher)) {
return isSameDay(date, matcher);
}
- if (isArrayOfDates(matcher)) {
+ if (isDatesArray(matcher, dateLib)) {
return matcher.includes(date);
}
if (isDateRange(matcher)) {
- return isDateInRange(date, matcher, dateUtils);
+ return isDateInRange(date, matcher, dateLib);
}
if (isDayOfWeekType(matcher)) {
return matcher.dayOfWeek.includes(date.getDay());
diff --git a/src/utils/typeguards.ts b/src/utils/typeguards.ts
index b48ae5e5de..13f7b34f47 100644
--- a/src/utils/typeguards.ts
+++ b/src/utils/typeguards.ts
@@ -51,15 +51,6 @@ export function isDateBeforeType(value: unknown): value is DateBefore {
return Boolean(value && typeof value === "object" && "before" in value);
}
-/**
- * Returns true if `value` is a `DayOfWeek` type.
- *
- * @group Utilities
- */
-export function isDayOfWeekType(value: unknown): value is DayOfWeek {
- return Boolean(value && typeof value === "object" && "dayOfWeek" in value);
-}
-
/**
* Returns true if the props are for a single selection mode.
*
@@ -105,6 +96,15 @@ export function isRange(
return props.mode === "range";
}
+/**
+ * Returns true if `value` is a `DayOfWeek` type.
+ *
+ * @group Utilities
+ */
+export function isDayOfWeekType(value: unknown): value is DayOfWeek {
+ return Boolean(value && typeof value === "object" && "dayOfWeek" in value);
+}
+
/**
* @deprecated This function has been renamed Use `isRange` instead.
* @protected
diff --git a/test-website/package.json b/test-website/package.json
index 99c5d0c1c1..5a525e6f35 100644
--- a/test-website/package.json
+++ b/test-website/package.json
@@ -12,19 +12,19 @@
"dependencies": {
"date-fns-tz": "^3.1.3",
"react": "^18.3.1",
- "react-day-picker": "workspace:9.0.0-rc.2",
+ "react-day-picker": "workspace:*",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
- "@typescript-eslint/eslint-plugin": "^7.13.1",
- "@typescript-eslint/parser": "^7.13.1",
+ "@typescript-eslint/eslint-plugin": "^7.15.0",
+ "@typescript-eslint/parser": "^7.16.0",
"@vitejs/plugin-react": "^4.3.1",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7",
"typescript": "^5.5.3",
- "vite": "^5.3.1"
+ "vite": "^5.3.3"
}
}
diff --git a/test-website/src/App.css b/test-website/src/App.css
index b9d355df2a..b6c5022ba0 100644
--- a/test-website/src/App.css
+++ b/test-website/src/App.css
@@ -2,7 +2,7 @@
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
- text-align: center;
+ /* text-align: center; */
}
.logo {
@@ -33,6 +33,13 @@
}
}
+.rdp-root {
+ /* --rdp-accent-color: var(--ifm-color-primary); */
+ --rdp-accent-background-color: #073845;
+ --rdp-range_end-color: black;
+ --rdp-range_start-color: black;
+}
+
.card {
padding: 2em;
}
diff --git a/test-website/src/App.tsx b/test-website/src/App.tsx
index 3f98795a8e..52492de484 100644
--- a/test-website/src/App.tsx
+++ b/test-website/src/App.tsx
@@ -1,12 +1,54 @@
-import { DayPicker } from "react-day-picker";
+import React, { useEffect, useState } from "react";
+
+import * as Examples from "react-day-picker/examples";
import "react-day-picker/style.css";
import "./App.css";
+const updateQueryString = (key: string, value: string) => {
+ const url = new URL(window.location.href);
+ const params = new URLSearchParams(url.search);
+ params.set(key, value);
+ window.history.pushState({}, "", `${url.pathname}?${params.toString()}`);
+};
+
function App() {
+ const [example, setExample] = useState("Start");
+
+ useEffect(() => {
+ const params = new URLSearchParams(window.location.search);
+ const value = params.get("example");
+ if (value) {
+ setExample(value);
+ }
+ }, []);
+
+ const Component: React.ComponentType = Examples[
+ example as keyof typeof Examples
+ ] as React.ComponentType;
return (
<>
-
+ {/* create a select that allows to choose between the examples */}
+
+ Example:{" "}
+ {
+ updateQueryString("example", e.target.value);
+ setExample(e.target.value);
+ }}
+ value={example}
+ >
+ {Object.keys(Examples).map((name) => (
+
+ {name}
+
+ ))}
+
+
+
+
+
>
);
}
diff --git a/test-website/src/index.css b/test-website/src/index.css
index 6119ad9a8f..0b58245cb0 100644
--- a/test-website/src/index.css
+++ b/test-website/src/index.css
@@ -24,8 +24,8 @@ a:hover {
body {
margin: 0;
- display: flex;
- place-items: center;
+ /* display: flex;
+ place-items: center; */
min-width: 320px;
min-height: 100vh;
}
@@ -34,7 +34,7 @@ h1 {
font-size: 3.2em;
line-height: 1.1;
}
-
+/*
button {
border-radius: 8px;
border: 1px solid transparent;
@@ -52,7 +52,7 @@ button:hover {
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
-}
+} */
@media (prefers-color-scheme: light) {
:root {
diff --git a/test/elements.ts b/test/elements.ts
index ee836afa27..88c1e46d3f 100644
--- a/test/elements.ts
+++ b/test/elements.ts
@@ -1,6 +1,10 @@
-import { ByRoleOptions, act, fireEvent, screen } from "@testing-library/react";
-
-import { user } from "./user";
+import { ByRoleOptions, screen } from "@testing-library/react";
+import {
+ DayFlag,
+ SelectionState,
+ labelDayButton,
+ labelGridcell
+} from "react-day-picker";
/** Return the application element from the screen. */
export function app() {
@@ -44,13 +48,45 @@ export function nav() {
return nextButton().parentElement;
}
+/**
+ * Return the button element with the date
+ *
+ * @param {Date} date - The date to match the button name.
+ */
+export function dateButton(date: Date) {
+ return screen.getByRole("button", {
+ name: new RegExp(
+ labelDayButton(date, {
+ [DayFlag.disabled]: false,
+ [DayFlag.hidden]: false,
+ [DayFlag.outside]: false,
+ [DayFlag.focused]: false,
+ [DayFlag.today]: false,
+ [SelectionState.range_end]: false,
+ [SelectionState.range_middle]: false,
+ [SelectionState.range_start]: false,
+ [SelectionState.selected]: false
+ }),
+ "s"
+ )
+ });
+}
+
/**
* Return the gridcell element from the screen.
*
* @param {Date} date - The date to match the gridcell name.
+ * @param {boolean} interactive - If the gridcell is interactive (e.g. in
+ * selection mode).
*/
-export function gridcell(date: Date) {
- return screen.getByRole("gridcell", { name: String(date.getDate()) });
+export function gridcell(date: Date, interactive?: boolean) {
+ if (interactive)
+ return screen.getByRole("gridcell", {
+ name: date.getDate().toString()
+ });
+ return screen.getByRole("gridcell", {
+ name: labelGridcell(date)
+ });
}
/**
@@ -76,15 +112,5 @@ export function monthDropdown() {
export function activeElement() {
if (!document.activeElement)
throw new Error("Could not find any focused element");
-
return document.activeElement;
}
-
-/** Focuses the days grid. */
-export async function focusDaysGrid() {
- // TODO: are these `act` calls necessary?
- await act(() => fireEvent.blur(activeElement())); // Make sure nothing is focused
- await user.tab(); // By pressing tab 3 tim;
- await user.tab();
- await user.tab();
-}
diff --git a/test/render.tsx b/test/render.tsx
index 11131105e4..de85b0ce1b 100644
--- a/test/render.tsx
+++ b/test/render.tsx
@@ -1,23 +1 @@
-import React, { ReactElement } from "react";
-
-import { render as testingLibraryRender } from "@testing-library/react";
-import type { DayPickerProps } from "react-day-picker";
-
-import { ContextProviders } from "../src/contexts/providers";
-
-/** Render a React Element wrapped with the Root Provider. */
-export function render(
- /** The element to render. */
- element: ReactElement,
- /** The initial DayPicker props to pass to the Root Provider. */
- context?: DayPickerProps,
- /** The options to pass to the testing library render function. */
- options?: Parameters[1]
-): ReturnType {
- return testingLibraryRender(
- {element} ,
- options
- );
-}
-
-export { screen, act, within } from "@testing-library/react";
+export { screen, act, within, render } from "@testing-library/react";
diff --git a/test/renderHook.tsx b/test/renderHook.tsx
index 9b401b8471..63e3f612ab 100644
--- a/test/renderHook.tsx
+++ b/test/renderHook.tsx
@@ -1,26 +1,17 @@
-import React from "react";
+// import React from "react";
-import { renderHook as testingLibraryRenderHook } from "@testing-library/react";
+// import { renderHook as testingLibraryRenderHook } from "@testing-library/react";
-import type { DayPickerProps } from "../src";
-import { ContextProviders } from "../src/contexts/providers";
+// import type { DayPickerProps } from "../src";
-/** Render a hook wrapped with the {@link ContextProviders} Provider. */
-export function renderHook(
- /** The hook to render. */
- hook: () => TResult,
- /** The props to pass to the {@link ContextProviders}. */
- props?: DayPickerProps,
- /** The options to pass to the testing library render function. */
- options?: Omit[1], "wrapper">
-) {
- return testingLibraryRenderHook(hook, {
- wrapper: ({ children }) => {
- if (!props) {
- return children;
- }
- return {children} ;
- },
- ...options
- });
-}
+// /** Render a hook wrapped with the {@link ContextProviders} Provider. */
+// export function renderHook(
+// /** The hook to render. */
+// hook: () => TResult,
+// /** The options to pass to the testing library render function. */
+// options?: Omit[1], "wrapper">
+// ) {
+// return testingLibraryRenderHook(hook, {
+// ...options
+// });
+// }
diff --git a/test/setup.ts b/test/setup.ts
index 50d96d2705..75ddf97b05 100644
--- a/test/setup.ts
+++ b/test/setup.ts
@@ -1,3 +1,13 @@
+import { configure } from "@testing-library/dom";
import "@testing-library/jest-dom";
import "./dateMatchers";
+
+configure({
+ getElementError: (message, container) => {
+ const error = new Error(message as string | undefined);
+ error.name = "TestingLibraryElementError";
+ error.stack = undefined;
+ return error;
+ }
+});
diff --git a/website/.vscode/tasks.json b/website/.vscode/tasks.json
new file mode 100644
index 0000000000..c3018ee57c
--- /dev/null
+++ b/website/.vscode/tasks.json
@@ -0,0 +1,15 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "type": "npm",
+ "script": "start",
+ "problemMatcher": [],
+ "label": "npm: start",
+ "detail": "PORT=2001 TYPEDOC_WATCH=false docusaurus start",
+ "runOptions": {
+ "runOn": "folderOpen"
+ }
+ }
+ ]
+}
diff --git a/website/docs/advanced-guides/custom-components.mdx b/website/docs/advanced-guides/custom-components.mdx
deleted file mode 100644
index 3ce36291f1..0000000000
--- a/website/docs/advanced-guides/custom-components.mdx
+++ /dev/null
@@ -1,143 +0,0 @@
----
-sidebar_position: 4
----
-
-# Custom Components
-
-Use the `components` prop to swap the components used to render DayPicker.
-
-:::warning Advanced Feature
-
-- This feature requires basic understanding of the output generated by DayPicker.
-- Get familiar with the [API Reference](../api#components) and the [DayPicker Anatomy](../using-daypicker/anatomy.mdx) first.
-- Make sure you don't break [accessibility](../using-daypicker/accessibility.mdx) when customizing components.
-- Custom components may not have a stable API yet and may break in a minor release.
-
-:::
-
-## List of Custom Components
-
-See the [Components API Reference](../api#components) for a list of components you can customize.
-
-## Example: Custom DayDate component
-
-For example, if you need to customize the component displaying the date, replace the [`DayDate`](../api/functions//DayDate.md) component:
-
-```tsx title="./MyDatePicker.tsx"
-import { DayPicker, type DayDateProps } from "react-day-picker";
-
-/** When the date is 19, will display an emoji. */
-function HighlightDay(props: DayDateProps) {
- return (
-
- {props.day.date.getDate() === 19 ? `🎉` : props.formattedDate}
-
- );
-}
-
-export function MyDatePicker() {
- return ;
-}
-```
-
-
-
-
-
-### Extending the Default Components
-
-You can also import the default components to add custom behavior. Just make sure you pass the default component to the root.
-
-For example, to add a custom class to the `Day` component:
-
-```tsx title="./CustomDay.tsx"
-import { DayPicker, Day, type DayProps } from "react-day-picker";
-
-function CustomDay(props: DayProps) {
- return (
-
- {props.children}
-
- );
-}
-
-export function MyDatePicker() {
- return ;
-}
-```
-
-## DayPicker Hooks
-
-When creating custom components, you will find useful the [DayPicker hooks](../api/index.md#hooks). These utilities provide access to the internal state and methods of the DayPicker component.
-
-| Function | Description |
-| :----------------------------------------------- | :------------------------------------------------------------------------ |
-| [useCalendar](../api/functions/useCalendar.md) | Access to the props passed to `DayPicker`, with some meaningful defaults. |
-| [useFocus](../api/functions/useFocus.md) | Share the focused day and the methods to move the focus. |
-| [useModifiers](../api/functions/useModifiers.md) | - |
-| [useMulti](../api/functions/useMulti.md) | Access to the multi context to get the selected dates or update them. |
-| [useProps](../api/functions/useProps.md) | Access to the props passed to `DayPicker`, with some meaningful defaults. |
-| [useRange](../api/functions/useRange.md) | Access to the range context to get the selected range or update it. |
-| [useSingle](../api/functions/useSingle.md) | Access to the single context to get the selected date or update it. |
-
-### Example: Range with Shift Key
-
-Implement a custom `Day` component to select ranges while pressing the Shift key.
-
-```tsx
-import React, { MouseEventHandler } from "react";
-
-import {
- DateRange,
- DayPicker,
- type DayProps,
- useRange
-} from "react-day-picker";
-
-function DayWithShiftKey(props: DayProps) {
- const { selected } = useRange();
- const onClick = props.htmlAttributes?.onClick;
-
- const handleClick: MouseEventHandler = (e) => {
- if (selected.from && !selected.to && !isSameDay(props.day.date, selected.from) && !e.shiftKey && ) {
- return;
- }
- onClick?.(e);
- };
- return (
-
- {props.children}
-
- );
-}
-
-export function RangeShiftKey() {
- const [range, setRange] = React.useState();
-
- let footer = Please pick a day.
;
-
- if (range?.from && !range?.to) {
- footer = Press Shift to choose more days.
;
- } else if (range?.to) {
- footer = (
-
- {range.from.toLocaleDateString()}—{range.to.toLocaleDateString()}.
-
- );
- }
- return (
-
- );
-}
-```
-
-
-
-
diff --git a/website/docs/using-daypicker/accessibility.mdx b/website/docs/docs/accessibility.mdx
similarity index 82%
rename from website/docs/using-daypicker/accessibility.mdx
rename to website/docs/docs/accessibility.mdx
index b107ca07b0..e7b7685efe 100644
--- a/website/docs/using-daypicker/accessibility.mdx
+++ b/website/docs/docs/accessibility.mdx
@@ -6,12 +6,12 @@ sidebar_position: 6
DayPicker is designed to adhere to the [ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/datepicker-dialog/) for date pickers. This includes features such as keyboard navigation, focus management, and labeling.
-Depending on your design, you may need to add new accessibility features. For instance, when working with [Input Fields](../advanced-guides/input-fields), there could be some restrictions depending on the level of accessibility you aim to achieve. Stay updated with the best practices by following the [ARIA Patterns](https://www.w3.org/WAI/ARIA/apg/patterns/).
+Depending on your design, you may need to add new accessibility features. For instance, when working with [Input Fields](../guides/input-fields), there could be some restrictions depending on the level of accessibility you aim to achieve. Stay updated with the best practices by following the [ARIA Patterns](https://www.w3.org/WAI/ARIA/apg/patterns/).
## Accessibility Tips
- Regularly test your date picker with a screen reader to ensure it's accessible to all users.
-- Use an `aria-live` region in your user interface to audibly announce when a date is selected.
+- Use an `aria-live` region in your user interface to audibly announce when a date is selected. For this purpose, you can use the `footer` prop.
- Specify the ARIA role of the date picker with the `role` prop to provide context to assistive technologies.
- Customize the ARIA labels with the [`labels`](../api/interfaces/PropsBase.md#labels) prop to improve the clarity of instructions and feedback provided to the user.
- Ensure that the date picker is fully navigable using only the keyboard. This includes selection, navigation between months, and closing the date picker.
@@ -20,9 +20,9 @@ Depending on your design, you may need to add new accessibility features. For in
- Ensure sufficient color contrast between text and background colors to assist users with visual impairments.
- Consider providing instructions for how to use the date picker for **first-time users** or those unfamiliar with the interface.
-### Example
+### Announcing the Selected Date {#footer}
-An accessible date picker with a live region that announces the selected date.
+An accessible date picker with a live region that announces the selected date using the `footer` prop.
```tsx title="./AccessibleDatePicker.tsx"
import React, { useState } from "react";
@@ -64,6 +64,18 @@ export function AccessibleDatePicker() {
+## Autofocusing the Calendar {#autofocus}
+
+DayPicker manages focus automatically when the user interacts with the calendar. However, for better accessibility you may need to autofocus the calendar when it opens. To do this, you can use the `autofocus` prop:
+
+```tsx
+
+```
+
+
+
+
+
## Keyboard Navigation
DayPicker supports keyboard navigation to make it easier for users to navigate the calendar. The following keys are supported:
@@ -87,3 +99,7 @@ DayPicker supports keyboard navigation to make it easier for users to navigate t
Accessibility is an evolving field. If you find any issues with DayPicker, please [open an issue](https://github.com/gpbl/react-day-picker/issues/new/choose). Your feedback helps improve our library's accessibility.
Check out the [current accessibility issues](https://github.com/gpbl/react-day-picker/issues?q=is%3Aopen+label%3Aaccessibility+sort%3Aupdated-desc).
+
+```
+
+```
diff --git a/website/docs/using-daypicker/anatomy.mdx b/website/docs/docs/anatomy.mdx
similarity index 94%
rename from website/docs/using-daypicker/anatomy.mdx
rename to website/docs/docs/anatomy.mdx
index 178e0d353f..04771fe353 100644
--- a/website/docs/using-daypicker/anatomy.mdx
+++ b/website/docs/docs/anatomy.mdx
@@ -15,6 +15,7 @@ To better follow the docs, get familiar with the elements composing DayPicker.
- `disabled`: the day is disabled.
- `today`: the day is today.
- `outside`: the day is outside the current month.
+- **Footer**: an ARIA live region that can be used to announce the selected date.
The UI elements are mapped to CSS classes. A complete list can be found in the [`UI`](../api//enumerations/UI.md) enum.
diff --git a/website/docs/using-daypicker/customization.mdx b/website/docs/docs/customization.mdx
similarity index 57%
rename from website/docs/using-daypicker/customization.mdx
rename to website/docs/docs/customization.mdx
index 16248aaa61..362951f6cd 100644
--- a/website/docs/using-daypicker/customization.mdx
+++ b/website/docs/docs/customization.mdx
@@ -10,15 +10,15 @@ Use the customization props to customize the appearance of the calendar.
See below for more details on each prop.
-| Prop Name | Type | Description |
-| ----------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
-| `captionLayout` | \| `"label"` \| `"dropdown"` \| `"dropdown-months"` \| `"dropdown-years"` | Choose the layout of the month caption. Default is `label`. |
-| `fixedWeeks` | `boolean` | Display 6 weeks per months. |
-| `footer` | `ReactNode` | Displays a footer below the calendar. |
-| `hideWeekdayRow` | `boolean` | Hide the row displaying the weekday names. |
-| `numberOfMonths` | `number` | The number of displayed months. Default is `1`. |
-| `showOutsideDays` | `boolean` | Display the days falling into the other months. |
-| `showWeekNumber` | `boolean` | Display the column with the week numbers. |
+| Prop Name | Type | Description |
+| ----------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
+| `captionLayout` | \| `"label"` \| `"dropdown"` \| `"dropdown-months"` \| `"dropdown-years"` | Choose the layout of the month caption. Default is `label`. |
+| `fixedWeeks` | `boolean` | Display 6 weeks per months. |
+| `footer` | `ReactNode` \| `string` | Add a footer to the calendar, acting as a [live region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions). |
+| `hideWeekdayRow` | `boolean` | Hide the row displaying the weekday names. |
+| `numberOfMonths` | `number` | The number of displayed months. Default is `1`. |
+| `showOutsideDays` | `boolean` | Display the days falling into the other months. |
+| `showWeekNumber` | `boolean` | Display the column with the [week numbers](#showweeknumber). |
## Caption Layouts
@@ -81,7 +81,7 @@ In a year, months can have between 4 an 6 weeks. Use `fixedWeeks` to always disp
-## Multiple Months Props
+## Multiple Months {#multiplemonths}
To display multiple months in the calendar, use the `numberOfMonths` prop.
@@ -111,29 +111,61 @@ With `pagedNavigation`, the navigation jumps by the specified number of months a
-## Week Numbers Props
+## Week Numbers {#showweeknumber}
-To display the column with the week numbers, use the `showWeekNumber` prop. Use the `onWeekNumberClick` to handle the click event.
+To display the column with the week numbers, use the `showWeekNumber` prop.
-| Prop Name | Type | Description |
-| ------------------- | ----------------------------- | ---------------------------------------------- |
-| `showWeekNumber` | `boolean` | Display the week numbers. |
-| `onWeekNumberClick` | `WeekNumberClickEventHandler` | Event handler when the week number is clicked. |
+| Prop Name | Type | Description |
+| ---------------- | --------- | ------------------------- |
+| `showWeekNumber` | `boolean` | Display the week numbers. |
```tsx
-
+
```
-## Footer
+### Handling Week Numbers Click
-Use the `footer` prop to display a footer below the calendar.
+To handle the click on the week numbers, you can set a `WeekNumber` [custom component](../guides/custom-components):
```tsx
-Please pick a date.} />
+ (
+ alert(`Week ${weekNumber}`)}>{weekNumber}
+ )
+ }}
+/>
+```
+
+### Selecting the Whole Week
+
+When in [selection mode](./selection-modes.mdx), create a [Custom Selection](../guides//custom-selections.mdx) that selects the whole week when a day is clicked.
+
+## Footer as Live Region
+
+Use the `footer` prop to display a footer below the calendar. The footer works as a [live region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions) to announce changes to screen readers. See also the [Accessibility guide](./accessibility.mdx) for making the calendar accessible.
+
+```tsx
+export function Footer() {
+ const [selected, setSelected] = React.useState();
+ return (
+
+ );
+}
```
@@ -142,9 +174,9 @@ Use the `footer` prop to display a footer below the calendar.
## Custom Components
-In DayPicker, you can replace the components used internally to render the calendar. See the [Custom Components](../advanced-guides/custom-components.mdx) guide for more information.
+In DayPicker, you can replace the components used internally to render the calendar. See the [Custom Components](../guides/custom-components.mdx) guide for more information.
-| Prop Name | Type | Description |
-| ------------ | ------------------------------------------------------------- | ----------------------------------------------------------- |
-| `components` | [`CustomComponents`](../api/type-aliases/CustomComponents.md) | Change the internal components used to render the calendar. |
-| `classNames` | [`ClassNames`](../api/type-aliases/ClassNames.md) | Use custom class names instead of the default ones. |
+| Prop Name | Type | Description |
+| ------------ | ------------------------------------------------- | ----------------------------------------------------------- |
+| `components` | [`CustomComponents`](../api/index.md#components) | Change the internal components used to render the calendar. |
+| `classNames` | [`ClassNames`](../api/type-aliases/ClassNames.md) | Use custom class names instead of the default ones. |
diff --git a/website/docs/using-daypicker/localization.mdx b/website/docs/docs/localization.mdx
similarity index 67%
rename from website/docs/using-daypicker/localization.mdx
rename to website/docs/docs/localization.mdx
index be9697c3fb..c0bd21b00f 100644
--- a/website/docs/using-daypicker/localization.mdx
+++ b/website/docs/docs/localization.mdx
@@ -47,7 +47,8 @@ Utilize the `weekStartsOn` prop to specify the starting day of the week.
The `firstWeekContainsDate` prop can be used to change the first day of the week for the year's initial week, which is used in the calculation of week numbers.
-Acceptable values are `1` for Monday and `4` for Thursday.
+- Acceptable values are `1` for Monday and `4` for Thursday.
+- See also [Week Numbering](https://en.wikipedia.org/wiki/Week#Numbering) and the [getWeek function](https://date-fns.org/docs/getWeek).
```tsx caption="render:WeeknumberCustom"
```
-| Label | Type | Description |
-| :---------------------- | :---------------------------------------------------------------------------- | :-------------------------------------------------- |
-| `labelCaption` | _typeof_ [`labelCaption`](../api/functions/labelCaption.md) | Return the label for the month dropdown. |
-| `labelDay` | _typeof_ [`labelDay`](../api/functions/labelDay.md) | Return the label for the day cell. |
-| `labelMonthDropdown` | _typeof_ [`labelMonthDropdown`](../api/functions/labelMonthDropdown.md) | Return the label for the month dropdown. |
-| `labelNext` | _typeof_ [`labelNext`](../api/functions/labelNext.md) | Return the label for the next month button. |
-| `labelPrevious` | _typeof_ [`labelPrevious`](../api/functions/labelPrevious.md) | Return the label for the previous month button. |
-| `labelWeekNumber` | _typeof_ [`labelWeekNumber`](../api/functions/labelWeekNumber.md) | Return the label for the week number. |
-| `labelWeekNumberHeader` | _typeof_ [`labelWeekNumberHeader`](../api/functions/labelWeekNumberHeader.md) | Return the label for the column of the week number. |
-| `labelWeekday` | _typeof_ [`labelWeekday`](../api/functions/labelWeekday.md) | Return the label for the weekday. |
-| `labelYearDropdown` | _typeof_ [`labelYearDropdown`](../api/functions/labelYearDropdown.md) | Return the label for the year dropdown. |
-
-### Formatters
-
-The `formatters` prop can be used to format dates and week numbers:
-
-```tsx
- format(date, "LLLL yyyy", options)
- }}
-/>
-```
-
-| Formatter | Type | Description |
-| :-------------------- | :------------------------------------------------------------------------ | :-------------------------------------- |
-| `formatCaption` | _typeof_ [`formatCaption`](../api/functions/formatCaption.md) | Format the caption of a month grid. |
-| `formatDay` | _typeof_ [`formatDay`](../api/functions/formatDay.md) | Format the day in the day cell. |
-| `formatMonthDropdown` | _typeof_ [`formatMonthDropdown`](../api/functions/formatMonthDropdown.md) | Format the label in the month dropdown. |
-| `formatWeekNumber` | _typeof_ [`formatWeekNumber`](../api/functions/formatWeekNumber.md) | Format the week number. |
-| `formatWeekdayName` | _typeof_ [`formatWeekdayName`](../api/functions/formatWeekdayName.md) | Format the week day name in the header |
-| `formatYearDropdown` | _typeof_ [`formatYearDropdown`](../api/functions/formatYearDropdown.md) | Format the label in the year dropdown. |
+| Function | Description |
+| -------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
+| [`labelDay`](../api/functions/labelDay.md) | The label for the day button. |
+| [`labelDayButton`](../api/functions/labelDayButton.md) | The label for the day button. |
+| [`labelGrid`](../api/functions/labelGrid.md) | The label for the month grid, that will be announced when entering the grid. |
+| [`labelGridcell`](../api/functions/labelGridcell.md) | The label for the day gridcell when the calendar is not interactive. |
+| [`labelMonthDropdown`](../api/functions/labelMonthDropdown.md) | The label for the months dropdown. |
+| [`labelNav`](../api/functions/labelNav.md) | The label for the navigation toolbar, that will be announced when entering it. |
+| [`labelNext`](../api/functions/labelNext.md) | The label for next month button. |
+| [`labelPrevious`](../api/functions/labelPrevious.md) | The label for next month button. |
+| [`labelWeekNumber`](../api/functions/labelWeekNumber.md) | The label for the week number element. |
+| [`labelWeekNumberHeader`](../api/functions/labelWeekNumberHeader.md) | The label for the week number header element. |
+| [`labelWeekday`](../api/functions/labelWeekday.md) | The label for the Weekday element. |
+| [`labelYearDropdown`](../api/functions/labelYearDropdown.md) | The label for the years dropdown. |
### RTL Text Direction
@@ -196,6 +179,28 @@ import { arSA } from "date-fns/locale";
+### Custom Formatters
+
+The `formatters` prop can be used to further format dates, week numbers, day names, and more.
+
+```tsx
+ format(date, "LLLL yyyy", options)
+ }}
+/>
+```
+
+| Function | Description |
+| ---------------------------------------------------------------------- | ---------------------------------------------------- |
+| [`formatCaption`](../api/functions/formatCaption.md) | The default formatter for the caption element. |
+| [`formatDay`](../api/functions/formatDay.md) | The default formatter for the day grid cell element. |
+| [`formatMonthDropdown`](../api/functions/formatMonthDropdown.md) | The default formatter for the month dropdown value. |
+| [`formatWeekNumber`](../api/functions/formatWeekNumber.md) | The default formatter for the week numbers. |
+| [`formatWeekNumberHeader`](../api/functions/formatWeekNumberHeader.md) | The default formatter for the week numbers header. |
+| [`formatWeekdayName`](../api/functions/formatWeekdayName.md) | The default formatter for the name of the weekday. |
+| [`formatYearDropdown`](../api/functions/formatYearDropdown.md) | The default formatter for the Year caption. |
+
### Numbering System
Use the proper `formatters` to change the [numbering system](https://en.wikipedia.org/wiki/Numeral_system) used in the calendar.
diff --git a/website/docs/using-daypicker/navigation.mdx b/website/docs/docs/navigation.mdx
similarity index 96%
rename from website/docs/using-daypicker/navigation.mdx
rename to website/docs/docs/navigation.mdx
index 1ffe883634..d997643f67 100644
--- a/website/docs/using-daypicker/navigation.mdx
+++ b/website/docs/docs/navigation.mdx
@@ -22,7 +22,7 @@ For example, to render a calendar starting from September 1979:
-## Controlling the Month
+## Controlling the Month {#onmonthchange}
To programmatically control the month displayed when navigating, use the `month` and `onMonthChange` props.
@@ -77,7 +77,7 @@ Limit the dates the user can navigate to by using the `startMonth` and `endMonth
-## Disabling the Navigation
+## Disabling the Navigation {#disablenavigation}
To prevent the user from navigating between months, set the `disableNavigation` prop to `true`.
@@ -93,7 +93,7 @@ To prevent the user from navigating between months, set the `disableNavigation`
-## Hiding the Navigation
+## Hiding the Navigation {#hidenavigation}
To hide the navigation completely, set the `hideNavigation` prop to `true`. Useful when setting the [caption layout](./customization.mdx#caption-layouts) to `"dropdown"`.
diff --git a/website/docs/using-daypicker/selection-modes.mdx b/website/docs/docs/selection-modes.mdx
similarity index 96%
rename from website/docs/using-daypicker/selection-modes.mdx
rename to website/docs/docs/selection-modes.mdx
index 4982ba9ae4..3180208fb9 100644
--- a/website/docs/using-daypicker/selection-modes.mdx
+++ b/website/docs/docs/selection-modes.mdx
@@ -199,7 +199,7 @@ To disable specific days, use the `disabled` prop. The prop accepts a [`Matcher`
```tsx
```
@@ -217,7 +217,7 @@ import { useState } from "react";
import { DayPicker } from "react-day-picker";
export function App() {
- const [selected, setSelected] = useState();
+ const [selected, setSelected] = useState();
const handleSelect = (newSelected) => {
// Update the selected dates
setSelected(newSelected);
@@ -228,4 +228,4 @@ export function App() {
}
```
-You can also toggle the selection mode to `default` and implement your own mode by using `modifiers` and `onDayClick`. Read the [Custom Selections](../advanced-guides/custom-selections.mdx) guide for more information.
+You can also toggle the selection mode to `default` and implement your own mode by using `modifiers` and `onDayClick`. Read the [Custom Selections](../guides/custom-selections.mdx) guide for more information.
diff --git a/website/docs/using-daypicker/styling.mdx b/website/docs/docs/styling.mdx
similarity index 92%
rename from website/docs/using-daypicker/styling.mdx
rename to website/docs/docs/styling.mdx
index 0c076f253f..8aa681a4e1 100644
--- a/website/docs/using-daypicker/styling.mdx
+++ b/website/docs/docs/styling.mdx
@@ -40,17 +40,17 @@ If you are not using a bundler, you can copy the CSS file to your project. See [
The default styles use CSS variables that can be overridden to customize the appearance of the calendar.
-Define the CSS variables in your app's CSS file, after importing the DayPicker CSS file, under the `.rdp-calendar` class.
+Define the CSS variables in your app's CSS file, after importing the DayPicker CSS file, under the `.rdp-root` class.
```css title="./app/global.css"
-.rdp-calendar {
+.rdp-root {
--rdp-accent-color: indigo; /* Change the accent color to indigo. */
--rdp-accent-background-color: #f0f0f0; /* Change the accent background color. */
/* Add more CSS variables here. */
}
```
-The following table lists the CSS variables used by DayPicker inside the `.rdp-calendar` class:
+The following table lists the CSS variables used by DayPicker inside the `.rdp-root` class:
| CSS Variable | Description |
| ----------------------------------------- | ----------------------------------------------------------------------------- |
@@ -61,10 +61,10 @@ The following table lists the CSS variables used by DayPicker inside the `.rdp-c
| `--rdp-day-height` | The height of the day cells. |
| `--rdp-day-width` | The width of the day cells. |
| `--rdp-chevron-disabled-opacity` | The opacity of the chevron when its container is disabled. |
-| `--rdp-day_date-border-radius` | The border radius of the day cells. |
-| `--rdp-day_date-border` | The border of the day cells. |
-| `--rdp-day_date-height` | The height of the day cells. |
-| `--rdp-day_date-width` | The width of the day cells. |
+| `--rdp-day_button-border-radius` | The border radius of the day cells. |
+| `--rdp-day_button-border` | The border of the day cells. |
+| `--rdp-day_button-height` | The height of the day cells. |
+| `--rdp-day_button-width` | The width of the day cells. |
| `--rdp-selected-border` | The border of the selected days. |
| `--rdp-selected-font` | The font of the selected days. |
| `--rdp-disabled-opacity` | The opacity of the disabled days. |
@@ -79,6 +79,7 @@ The following table lists the CSS variables used by DayPicker inside the `.rdp-c
| `--rdp-nav-height` | The height of the navigation bar. |
| `--rdp-range_middle-background-color` | The color of the background for days in the middle of a range. |
| `--rdp-range_middle-font` | The font for days in the middle of a range. |
+| `-- ` | The color of the text for days in the middle of a range. |
| `--rdp-range_start-color` | The color of the range text at the start of the range. |
| `--rdp-range_start-background` | Used for the background of the start of the selected range. |
| `--rdp-range_start-date-background-color` | The background color of the date at the start of the selected range. |
@@ -100,10 +101,10 @@ The following table lists the CSS variables used by DayPicker inside the `.rdp-c
To toggle between dark and light appearance, override the accent color with the color to use in the dark mode.
```css
-.rdp-calendar {
+.rdp-root {
--rdp-accent-color: blue; /* Use blue as the accent color. */
}
-[data-theme="dark"] .rdp-calendar {
+[data-theme="dark"] .rdp-root {
--rdp-accent-color: yellow; /* Use yellow as the accent color in dark mode. */
}
```
@@ -131,7 +132,7 @@ export function MyDatePicker() {
Use the `classNames` prop to use other classnames instead of the default ones. The [`ClassNames`](../api/type-aliases/ClassNames.md) type lists all the class names used by DayPicker.
-They are the value of the [`UI`](../api/enumerations/UI.md), [`DayFlag`](../api/enumerations/DayFlag.md), [`SelectionState`](../api/enumerations/SelectionState.md), and [`CalendarFlag`](../api/enumerations/CalendarFlag.md) enums.
+They are the value of the [`UI`](../api/enumerations/UI.md), [`DayFlag`](../api/enumerations/DayFlag.md) and [`SelectionState`](../api/enumerations/SelectionState.md) enums.
For example, to change the class name of the calendar container:
@@ -151,8 +152,8 @@ If you are including [Tailwind CSS](https://tailwindcss.com) in your project, us
- Add to the `classNames` the class names you want to override.
- Extend the default class names with [`getDefaultClassNames`](../api/functions//getDefaultClassNames.md).
-- Read [`style.css`](https://github.com/gpbl/react-day-picker/blob/main/src/style.css) from source and get familiar with the [UI elements](../using-daypicker/anatomy.mdx).
-- Adopt [custom components](../advanced-guides/custom-components.mdx) to further customize the HTML elements.
+- Read [`style.css`](https://github.com/gpbl/react-day-picker/blob/main/src/style.css) from source and get familiar with the [UI elements](../docs/anatomy.mdx).
+- Adopt [custom components](../guides/custom-components.mdx) to further customize the HTML elements.
```tsx
import { DayPicker, getDefaultClassNames } from "react-day-picker";
diff --git a/website/docs/guides/custom-components.mdx b/website/docs/guides/custom-components.mdx
new file mode 100644
index 0000000000..e1c61ad9cc
--- /dev/null
+++ b/website/docs/guides/custom-components.mdx
@@ -0,0 +1,116 @@
+---
+sidebar_position: 4
+---
+
+# Custom Components
+
+Use the `components` prop to swap the components used to render DayPicker.
+
+:::warning Advanced Feature
+
+- This feature requires basic understanding of the output generated by DayPicker.
+- Get familiar with the [API Reference](../api#components) and the [DayPicker Anatomy](../docs/anatomy.mdx) first.
+- Make sure you don't break [accessibility](../docs/accessibility.mdx) when customizing components.
+- Custom components may not have a stable API yet and may break in a minor release.
+
+:::
+
+## List of Custom Components
+
+See the [Components API Reference](../api#components) for a list of components you can customize.
+
+## Example: Custom DayButton component
+
+For example, if you need to customize the component displaying the date, replace the [`DayButton`](../api/functions/DayButton.md) component:
+
+```tsx title="./MyDatePicker.tsx"
+import { DayPicker, type DayButtonProps } from "react-day-picker";
+
+function HighlightDay(props: DayButtonProps) {
+ const { day, modifiers, ...buttonProps } = props;
+ return (
+
+ {props.day.date.getDate() === 19 ? `🎉` : props.children}
+
+ );
+}
+
+export function CustomDayButton() {
+ return ;
+}
+```
+
+
+
+
+
+## DayPicker Hook
+
+When creating custom components, you will find useful the `useDayPicker` hook, providing access to the internal state and methods of the DayPicker component.
+
+| Function | Description |
+| :------------------------------------------------- | :------------------------------ |
+| [`useDayPicker`](../api/functions/useDayPicker.md) | Access to the DayPicker context |
+
+### Example: Range with Shift Key
+
+Implement a custom `Day` component to select ranges while pressing the Shift key.
+
+```tsx
+import React, { MouseEventHandler } from "react";
+
+import {
+ DateRange,
+ DayPicker,
+ type DayProps,
+ useRange
+} from "react-day-picker";
+
+function DayWithShiftKey(props: DayProps) {
+ const { selected } = useRange();
+ const onClick = props.htmlAttributes?.onClick;
+
+ const handleClick: MouseEventHandler = (e) => {
+ if (selected.from && !selected.to && !isSameDay(props.day.date, selected.from) && !e.shiftKey && ) {
+ return;
+ }
+ onClick?.(e);
+ };
+ return (
+
+ {props.children}
+
+ );
+}
+
+export function RangeShiftKey() {
+ const [range, setRange] = React.useState({
+ from: undefined
+ });
+
+ let footer = "Please pick a day.";
+
+ if (range?.from && !range?.to) {
+ footer = "Press Shift to choose more days.";
+ } else if (range?.to) {
+ const formattedFrom = range.from?.toDateString();
+ const formattedTo = range.to.toDateString();
+ footer = `You selected the days between ${formattedFrom} and ${formattedTo}`;
+ }
+ return (
+
+ );
+}
+```
+
+
+
+
diff --git a/website/docs/advanced-guides/custom-modifiers.mdx b/website/docs/guides/custom-modifiers.mdx
similarity index 96%
rename from website/docs/advanced-guides/custom-modifiers.mdx
rename to website/docs/guides/custom-modifiers.mdx
index c12400d641..58cd2e7809 100644
--- a/website/docs/advanced-guides/custom-modifiers.mdx
+++ b/website/docs/guides/custom-modifiers.mdx
@@ -42,7 +42,7 @@ For example, you can use a custom modifier to mark days as already booked in a b
```
-When in selection mode, use the `selected` prop to add the `selected` modifier to the selected dates, and style them accordingly. To see how to implement the `selected` modifier, see the [Selecting days guide](../using-daypicker/selection-modes.mdx).
+When in selection mode, use the `selected` prop to add the `selected` modifier to the selected dates, and style them accordingly. To see how to implement the `selected` modifier, see the [Selecting days guide](../docs/selection-modes.mdx).
### `disabled` Modifier
@@ -98,7 +98,7 @@ function Example() {
## Styling Modifiers
-A day can be styled according to its modifiers – using CSS or inline styles. See [Styling DayPicker](../using-daypicker/styling.mdx) for more details.
+A day can be styled according to its modifiers – using CSS or inline styles. See [Styling DayPicker](../docs/styling.mdx) for more details.
```tsx
const bookedDays = [
diff --git a/website/docs/advanced-guides/custom-selections.mdx b/website/docs/guides/custom-selections.mdx
similarity index 100%
rename from website/docs/advanced-guides/custom-selections.mdx
rename to website/docs/guides/custom-selections.mdx
diff --git a/website/docs/advanced-guides/input-fields.mdx b/website/docs/guides/input-fields.mdx
similarity index 94%
rename from website/docs/advanced-guides/input-fields.mdx
rename to website/docs/guides/input-fields.mdx
index 614636e749..8e1c241b6c 100644
--- a/website/docs/advanced-guides/input-fields.mdx
+++ b/website/docs/guides/input-fields.mdx
@@ -8,8 +8,7 @@ Binding a DayPicker with an input field requires to handle of user interactions,
:::info Native Date Pickers
-Browsers implement [native date pickers](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date) that provides a simple, built-in method for users to select a date.
-However, the appearance and format of the date picker can vary between different browsers and may not offer the level of customization you require.
+Browsers offer [native date pickers](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date) for easy date selection. However, their look and functionality differ across browsers and might not meet your customization needs. Use DayPicker for a tailored date picker that aligns with your app's design and accessibility standards.
:::
@@ -186,7 +185,8 @@ export function Dialog() {
value={inputValue}
placeholder={"MM/dd/yyyy"}
onChange={handleInputChange}
- /> {" "}
+
+
+
+
+### Code
+
+```tsx
+import React, { ChangeEventHandler, useState } from "react";
+
+import { setHours, setMinutes } from "date-fns";
+import { DayPicker } from "react-day-picker";
+
+export function InputTime() {
+ const [selected, setSelected] = useState();
+ const [timeValue, setTimeValue] = useState("00:00");
+
+ const handleTimeChange: ChangeEventHandler = (e) => {
+ const time = e.target.value;
+ if (!selected) {
+ setTimeValue(time);
+ return;
+ }
+ const [hours, minutes] = time.split(":").map((str) => parseInt(str, 10));
+ const newSelectedDate = setHours(setMinutes(selected, minutes), hours);
+ setSelected(newSelectedDate);
+ setTimeValue(time);
+ };
+
+ const handleDaySelect = (date: Date | undefined) => {
+ if (!timeValue || !date) {
+ setSelected(date);
+ return;
+ }
+ const [hours, minutes] = timeValue
+ .split(":")
+ .map((str) => parseInt(str, 10));
+ const newDate = new Date(
+ date.getFullYear(),
+ date.getMonth(),
+ date.getDate(),
+ hours,
+ minutes
+ );
+ setSelected(newDate);
+ };
+
+ return (
+
+
+
+
+ );
+}
+```
diff --git a/website/docs/intro.mdx b/website/docs/intro.mdx
index 1ea352d792..9fc574e8a3 100644
--- a/website/docs/intro.mdx
+++ b/website/docs/intro.mdx
@@ -11,13 +11,13 @@ DayPicker is a [React](https://react.dev) component to create date pickers, cale
## Features
-- 🛠 An extensive set of props for [customizing](./using-daypicker/customization.mdx) the calendar.
-- 🎨 Minimal design that can be [easily styled](./using-daypicker/styling.mdx) with CSS or any CSS framework.
-- 📅 Supports [selections](./using-daypicker/selection-modes.mdx) of single day, multiple days, ranges of days, or [custom selections](./advanced-guides/custom-selections.mdx).
-- 🌍 Can be [localized](./using-daypicker/localization.mdx) into any language, supports [ISO 8601 dates](./using-daypicker/localization.mdx#iso-week-dates), [UTC dates](./using-daypicker/localization.mdx#utc-dates), and [Jalali calendar](./using-daypicker/localization.mdx#jalali-calendar).
-- ♿ Complies with WCAG 2.1 AA requirements for [accessibility](./using-daypicker/accessibility.mdx).
-- ⚙️ [Customizable components](./advanced-guides/custom-components.mdx) for more complex use cases.
-- 🔤 Unopinionated integration [with input fields](./advanced-guides/input-fields.mdx).
+- 🛠 An extensive set of props for [customizing](./docs/customization.mdx) the calendar.
+- 🎨 Minimal design that can be [easily styled](./docs/styling.mdx) with CSS or any CSS framework.
+- 📅 Supports [selections](./docs/selection-modes.mdx) of single day, multiple days, ranges of days, or [custom selections](./guides/custom-selections.mdx).
+- 🌍 Can be [localized](./docs/localization.mdx) into any language, supports [ISO 8601 dates](./docs/localization.mdx#iso-week-dates), [UTC dates](./docs/localization.mdx#utc-dates), and [Jalali calendar](./docs/localization.mdx#jalali-calendar).
+- ♿ Complies with WCAG 2.1 AA requirements for [accessibility](./docs/accessibility.mdx).
+- ⚙️ [Customizable components](./guides/custom-components.mdx) for more complex use cases.
+- 🔤 Unopinionated integration [with input fields](./guides/input-fields.mdx).
DayPicker is written in TypeScript and compiled to CommonJS and ESM. It requires [date-fns](https://date-fns.org) as a peer dependency.
diff --git a/website/docs/start.mdx b/website/docs/start.mdx
index 2610f7a296..22b74aad51 100644
--- a/website/docs/start.mdx
+++ b/website/docs/start.mdx
@@ -1,5 +1,5 @@
---
-pagination_next: using-daypicker/styling
+pagination_next: docs/styling
---
# Getting Started
@@ -20,7 +20,7 @@ npm install react-day-picker@next
To create a simple date picker calendar,
1. Import the component and its default style from `react-day-picker`.
-2. Choose a [selection mode](./using-daypicker/selection-modes) using the `mode` prop.
+2. Choose a [selection mode](./docs/selection-modes) using the `mode` prop.
3. Assign the `selected` and `onSelect` props to manage the selected date.
```tsx
@@ -30,7 +30,7 @@ import { DayPicker } from "react-day-picker";
import "react-day-picker/style.css";
export function MyDatePicker() {
- const [selected, setSelected] = useState();
+ const [selected, setSelected] = useState();
return ;
}
```
@@ -41,20 +41,20 @@ export function MyDatePicker() {
## Learn More
-{/* - [DayPicker Anatomy](./using-daypicker/anatomy) - Understand the parts composing a DayPicker component. */}
+{/* - [DayPicker Anatomy](./docs/anatomy) - Understand the parts composing a DayPicker component. */}
{/* - [DayPicker Playground](./playground) - Play with the props to try the different customization options. */}
### Using DayPicker
-- [Styling](./using-daypicker/styling) - Change the style to make DayPicker match your app's look and feel.
-- [Customization](./using-daypicker/customization) - Explore the options available to customize the calendar and the months navigation.
-- [Selection Modes](./using-daypicker/selection-modes) - Enable users to select days with single, multiple or range selections.
-- [Localization](./using-daypicker/localization) - Configure DayPicker to display the calendar in different languages and date formats.
-- [Accessible Date Pickers](./using-daypicker/accessibility) - Make your date picker accessible to all users.
+- [Styling](./docs/styling) - Change the style to make DayPicker match your app's look and feel.
+- [Customization](./docs/customization) - Explore the options available to customize the calendar and the months navigation.
+- [Selection Modes](./docs/selection-modes) - Enable users to select days with single, multiple or range selections.
+- [Localization](./docs/localization) - Configure DayPicker to display the calendar in different languages and date formats.
+- [Accessible Date Pickers](./docs/accessibility) - Make your date picker accessible to all users.
-### Advanced Guides
+### Guides
-- [Input Fields](./advanced-guides/input-fields) - Learn how to use DayPicker with input fields and form libraries.
-- [Custom Selections](./advanced-guides/custom-selections) - Create custom selection rules.
-- [Custom Components](./advanced-guides/custom-components) - Use custom components to create a fully customized date picker.
-- [Custom Modifiers](./advanced-guides/custom-modifiers) - Create custom modifiers to highlight specific days in the calendar.
+- [Input Fields](./guides/input-fields) - Learn how to use DayPicker with input fields and form libraries.
+- [Custom Selections](./guides/custom-selections) - Create custom selection rules.
+- [Custom Components](./guides/custom-components) - Use custom components to create a fully customized date picker.
+- [Custom Modifiers](./guides/custom-modifiers) - Create custom modifiers to highlight specific days in the calendar.
diff --git a/website/docs/upgrading.mdx b/website/docs/upgrading.mdx
index 2ad9c42c7e..9ddc89ac89 100644
--- a/website/docs/upgrading.mdx
+++ b/website/docs/upgrading.mdx
@@ -68,7 +68,7 @@ You may need to update your test selectors — for example:
The `formatters` prop now requires functions returning a `string` instead of a `ReactNode`.
-If you were [using the formatters](./using-daypicker/localization.mdx#formatters), you may need to update your code or use a `DayDate` component to render a `ReactElement` again: see [custom components guide](./advanced-guides//custom-components.mdx).
+If you were [using the formatters](./docs/localization.mdx#formatters), you may need to update your code or use a `DayDate` component to render a `ReactElement` again: see [custom components guide](./guides//custom-components.mdx).
```diff
- const MyComponent = () => My caption }} />;
@@ -79,11 +79,10 @@ If you were [using the formatters](./using-daypicker/localization.mdx#formatters
In case you are using DayPicker hooks in your custom components, you need to update your code:
-| Hook | Upgrade Note |
-| ----------------- | ----------------------------------------------------------------------------------------------------------------------- |
-| ~`useDayPicker`~ | Renamed to [`useProps`](./api/functions/useProps.md). |
-| ~`useNavigation`~ | Renamed to [`useCalendar`](./api/functions/useCalendar.md). |
-| ~`useDayPicker`~ | Removed in favor of the `Day` custom component. See [custom components guide](./advanced-guides/custom-components.mdx). |
+| Hook | Upgrade Note |
+| ----------------- | -------------------------------------------------------------------------------------------------------------- |
+| ~`useNavigation`~ | Included into [`useDayPicker`](./api/functions/useDayPicker.md). |
+| ~`useDayRender`~ | Removed in favor of the `Day` custom component. See [custom components guide](./guides/custom-components.mdx). |
### 7. TypeScript: check for deprecated types
@@ -111,8 +110,7 @@ See also the source of [types-deprecated.ts](https://github.com/gpbl/react-day-p
| ~`SelectMultipleEventHandler`~ | This type will be removed. Use [`PropsMulti["onSelect]`](./api/interfaces/PropsMulti.md) instead. |
| ~`SelectRangeEventHandler`~ | This type will be removed. Use [`PropsRange["onSelect]`](./api/interfaces/PropsRange.md) instead. |
| ~`DayPickerProviderProps`~ | This type is not used anymore. |
-| ~`useDayPicker`~ | This type has been renamed to [`useProps`](./api/functions/useProps.md). |
-| ~`useNavigation`~ | This type has been renamed to [`useCalendar`](./api/functions/useCalendar.md). |
+| ~`useNavigation`~ | This type has been included in [`useDayPicker`](./api/functions/useDayPicker.md). |
| ~`useDayRender`~ | This hook has been removed. To customize the rendering of a day, use the `htmlAttributes` prop in a custom `Day` component. |
| ~`ContextProvidersProps`~ | This type is not used anymore. |
| ~`DayLabel`~ | Use `typeof labelDay` instead. |
diff --git a/website/examples-v8/index.ts b/website/examples-v8/index.ts
index d6b75fcc4a..32d4e7cc1f 100644
--- a/website/examples-v8/index.ts
+++ b/website/examples-v8/index.ts
@@ -32,7 +32,6 @@ export * from "./MultipleMinMax";
export * from "./MultipleMonths";
export * from "./MultipleMonthsId";
export * from "./MultipleMonthsPaged";
-export * from "./None";
export * from "./NumberingSystem";
export * from "./OutsideDays";
export * from "./Range";
diff --git a/website/package.json b/website/package.json
index 7e9df0b126..ba2e60142f 100644
--- a/website/package.json
+++ b/website/package.json
@@ -26,7 +26,7 @@
"prism-react-renderer": "^2.3.1",
"react": "^18.3.1",
"react-day-picker": "workspace:^",
- "react-day-picker-v8": "npm:react-day-picker@8",
+ "react-day-picker-v8": "npm:react-day-picker@8.10.1",
"react-dom": "^18.3.1",
"remark-github": "^12.0.0"
},
@@ -34,11 +34,11 @@
"@docusaurus/module-type-aliases": "3.4.0",
"@docusaurus/tsconfig": "3.4.0",
"@docusaurus/types": "3.4.0",
- "docusaurus-plugin-typedoc": "^1.0.1",
+ "docusaurus-plugin-typedoc": "^1.0.2",
"identity-obj-proxy": "^3.0.0",
"typedoc": "^0.26.3",
"typedoc-plugin-frontmatter": "^1.0.0",
- "typedoc-plugin-markdown": "^4.1.0",
+ "typedoc-plugin-markdown": "^4.1.2",
"typescript": "~5.5.3"
},
"browserslist": {
diff --git a/website/sidebars.ts b/website/sidebars.ts
index aa941f719d..0653e655cd 100644
--- a/website/sidebars.ts
+++ b/website/sidebars.ts
@@ -14,18 +14,18 @@ const sidebars: SidebarsConfig = {
items: [
{
type: "autogenerated",
- dirName: "using-daypicker"
+ dirName: "docs"
}
]
},
{
type: "category",
- label: "Advanced Guides",
+ label: "Guides",
collapsed: false,
items: [
{
type: "autogenerated",
- dirName: "advanced-guides"
+ dirName: "guides"
}
]
}
diff --git a/website/src/components/BrowserWindow.module.css b/website/src/components/BrowserWindow.module.css
index d03b9095ef..043fb3697a 100644
--- a/website/src/components/BrowserWindow.module.css
+++ b/website/src/components/BrowserWindow.module.css
@@ -87,9 +87,8 @@ html[data-theme="dark"] .browserWindow {
background-color: var(--ifm-background-color);
border-bottom-left-radius: inherit;
border-bottom-right-radius: inherit;
- padding: 2rem 1rem;
+ padding: 2rem 2rem;
display: flex;
- justify-content: center;
}
.overflow {
diff --git a/website/src/css/custom.css b/website/src/css/custom.css
index afeba205d6..63e5598248 100644
--- a/website/src/css/custom.css
+++ b/website/src/css/custom.css
@@ -64,11 +64,11 @@ html[data-theme="dark"] {
--ifm-toc-border-color: transparent !important;
}
-.rdp-calendar {
+.rdp-root {
--rdp-accent-color: var(--ifm-color-primary);
}
-html[data-theme="dark"] .rdp-calendar {
+html[data-theme="dark"] .rdp-root {
--rdp-accent-color: var(--ifm-color-primary);
--rdp-accent-background-color: #073845;
--rdp-range_end-color: black;
diff --git a/website/src/css/docusaurus-reset.css b/website/src/css/docusaurus-reset.css
index 649c2faf46..87d73cb21f 100644
--- a/website/src/css/docusaurus-reset.css
+++ b/website/src/css/docusaurus-reset.css
@@ -138,8 +138,8 @@
background-color: revert;
}
-.docusaurus-reset table th,
-.docusaurus-reset table td {
+/* .docusaurus-reset table.rdp-month_grid th, */
+.docusaurus-reset table.rdp-month_grid td {
border: revert;
padding: revert;
}
@@ -147,7 +147,6 @@
.docusaurus-reset table th {
background-color: revert;
color: revert;
- font-weight: revert;
}
.docusaurus-reset table td {
diff --git a/website/src/pages/playground.tsx b/website/src/pages/playground.tsx
index 4289f54b0c..ee8fd5c866 100644
--- a/website/src/pages/playground.tsx
+++ b/website/src/pages/playground.tsx
@@ -55,8 +55,9 @@ export default function Playground() {
const [utc, setUtc] = React.useState(false);
const [accentColor, setAccentColor] = React.useState();
- const [backgroundAccentColor, setAccentBackgroundColor] =
+ const [backgroundAccentColor, setBackgroundAccountColor] =
React.useState();
+ const [rangeMiddleColor, setrangeMiddleColor] = React.useState();
const Component = utc ? DayPickerUtc : DayPicker;
const formattedProps = ` `;
@@ -65,10 +66,11 @@ export default function Playground() {
@@ -406,15 +408,26 @@ export default function Playground() {
) : null}
{props.mode === "range" && (
-
- Range Color:
- setAccentBackgroundColor(e.target.value)}
- />
-
+ <>
+
+ Range Background:
+
+ setBackgroundAccountColor(e.target.value)
+ }
+ />
+
+
+ Range Foreground:
+ setrangeMiddleColor(e.target.value)}
+ />
+
+ >
)}
@@ -427,35 +440,37 @@ export default function Playground() {
Selection
- {selected ? (
- <>
-
- {props.mode === "single" && selected && selected.toString()}
- {props.mode === "multiple" &&
- (selected as Date[] | undefined)?.map((date) => {
- return (
- <>
- {date.toString()}
-
- >
- );
- })}
- {props.mode === "range" && isDateRange(selected) && (
- <>
- From: {selected.from && selected.from.toString()}
-
- To: {" "}
- {selected.to && selected.to.toString()}
- >
- )}
-
- >
- ) : props.mode ? (
-
Pick on a day to start selection.
- ) : (
-
Pick a selection mode to enable selections.
- )}
-
Props
+
+ {selected ? (
+
+
+ {props.mode === "single" && selected && selected.toString()}
+ {props.mode === "multiple" &&
+ (selected as Date[] | undefined)?.map((date) => {
+ return (
+ <>
+ {date.toString()}
+
+ >
+ );
+ })}
+ {props.mode === "range" && isDateRange(selected) && (
+ <>
+ From: {selected.from && selected.from.toString()}
+
+ To: {" "}
+ {selected.to && selected.to.toString()}
+ >
+ )}
+
+
+ ) : props.mode ? (
+ "Pick on a day to start selection."
+ ) : (
+ "Choose a selection mode to enable selections."
+ )}
+
+
Code
{({ className, style, tokens, getTokenProps }) => (
diff --git a/website/typedoc.mjs b/website/typedoc.mjs
index 177ad8f1ff..19db194826 100644
--- a/website/typedoc.mjs
+++ b/website/typedoc.mjs
@@ -27,7 +27,10 @@ const options = {
],
readme: "none",
+ suppressCommentWarningsInDeclarationFiles: true,
+
excludePrivate: true,
+ excludeExternals: true,
excludeProtected: true,
sort: ["alphabetical"],
@@ -38,7 +41,6 @@ const options = {
hidePageHeader: true,
preserveAnchorCasing: false,
- maxTypeConversionDepth: 1,
typeDeclarationFormat: "list",
parametersFormat: "table",
propertiesFormat: "list",
diff --git a/website/versioned_docs/version-8.10.1/using-daypicker/styling.mdx b/website/versioned_docs/version-8.10.1/using-daypicker/styling.mdx
index fcf63c1e97..9d4d9e4e8d 100644
--- a/website/versioned_docs/version-8.10.1/using-daypicker/styling.mdx
+++ b/website/versioned_docs/version-8.10.1/using-daypicker/styling.mdx
@@ -30,7 +30,7 @@ When using CSS modules, you can import `style.module.css`. Pass the class names
```tsx title="./DatePicker.jsx"
import { DayPicker } from "react-day-picker";
-import { default as defaultStyles } from "react-day-picker/style.module.css";
+import { default as defaultStyles } from "react-day-picker/dist/style.module.css";
export function DatePicker() {
return ;