web: replace react-day-picker with custom built day picker

it uses `@rehookify/datepicker`
This commit is contained in:
Abdullah Atta
2024-11-12 13:36:39 +05:00
committed by Abdullah Atta
parent 9281f9a390
commit 2d226db271
3 changed files with 204 additions and 29 deletions

View File

@@ -34,6 +34,7 @@
"@notesnook/web-clipper": "file:../../extensions/web-clipper", "@notesnook/web-clipper": "file:../../extensions/web-clipper",
"@react-pdf-viewer/core": "^3.12.0", "@react-pdf-viewer/core": "^3.12.0",
"@react-pdf-viewer/toolbar": "^3.12.0", "@react-pdf-viewer/toolbar": "^3.12.0",
"@rehookify/datepicker": "^6.6.7",
"@streetwriters/kysely": "^0.27.4", "@streetwriters/kysely": "^0.27.4",
"@tanstack/react-query": "^4.36.1", "@tanstack/react-query": "^4.36.1",
"@tanstack/react-virtual": "^3.0.1", "@tanstack/react-virtual": "^3.0.1",
@@ -48,7 +49,6 @@
"clipboard-polyfill": "4.0.0", "clipboard-polyfill": "4.0.0",
"comlink": "^4.3.1", "comlink": "^4.3.1",
"cronosjs": "^1.7.1", "cronosjs": "^1.7.1",
"date-fns": "^2.30.0",
"dayjs": "1.11.9", "dayjs": "1.11.9",
"diffblazer": "^1.0.1", "diffblazer": "^1.0.1",
"electron-trpc": "0.5.2", "electron-trpc": "0.5.2",

View File

@@ -0,0 +1,195 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useDatePicker } from "@rehookify/datepicker";
import { Box, Button, Flex, Text } from "@theme-ui/components";
import { useState } from "react";
import { ChevronLeft, ChevronRight } from "../icons";
import { SxProp } from "@theme-ui/core";
type DayPickerProps = {
selected: Date;
minDate?: Date;
maxDate?: Date;
onSelect: (date: Date) => void;
} & SxProp;
export function DayPicker(props: DayPickerProps) {
const { selected, sx, maxDate, minDate, onSelect } = props;
const [selectedDates, onDatesChange] = useState<Date[]>([selected]);
const {
data: { weekDays, months, years, calendars },
propGetters: { dayButton, addOffset, subtractOffset, setOffset }
} = useDatePicker({
selectedDates,
onDatesChange: (dates) => {
onDatesChange(dates);
onSelect?.(dates[0]);
},
dates: {
maxDate,
minDate
},
years: { numberOfYears: 99 },
calendar: {
startDay: 0
}
});
const { month, year, days } = calendars[0];
return (
<Flex
sx={{
flexDirection: "column",
...sx
}}
>
<Flex
sx={{ alignItems: "center", justifyContent: "space-between", gap: 1 }}
>
<Flex sx={{ alignItems: "center", justifyContent: "start", gap: 1 }}>
<Button
variant="icon"
sx={{ p: 0 }}
{...subtractOffset({ months: 1 })}
>
<ChevronLeft />
</Button>
<select
style={{
backgroundColor: "var(--background-secondary)",
outline: "none",
border: "1px solid var(--border-secondary)",
borderRadius: "5px",
color: "var(--paragraph)",
padding: "5px",
overflow: "hidden"
}}
value={month}
onChange={(e) => {
const selectedOption = e.target.selectedOptions[0];
setOffset(new Date(selectedOption.dataset.date || ""))?.onClick?.(
e
);
}}
>
{months.map((month) => (
<option
key={month.month + year}
value={month.month}
data-date={month.$date.toDateString()}
>
{month.month}
</option>
))}
</select>
<select
style={{
backgroundColor: "var(--background-secondary)",
outline: "none",
border: "1px solid var(--border-secondary)",
borderRadius: "5px",
color: "var(--paragraph)",
padding: "5px",
overflow: "hidden"
}}
value={year}
onChange={(e) => {
const selectedOption = e.target.selectedOptions[0];
setOffset(new Date(selectedOption.dataset.date || ""))?.onClick?.(
e
);
}}
>
{years.map((year) => (
<option
key={year.year}
value={year.year}
data-date={year.$date.toDateString()}
>
{year.year}
</option>
))}
</select>
</Flex>
<Button variant="icon" sx={{ p: 0 }} {...addOffset({ months: 1 })}>
<ChevronRight />
</Button>
</Flex>
<Box
sx={{
display: "grid",
gridTemplateColumns: "repeat(7, 1fr)",
gap: 1,
mt: 2
}}
>
{weekDays.map((day) => (
<Text
variant="body"
key={day}
sx={{ fontWeight: "bold", textAlign: "center" }}
>
{day}
</Text>
))}
</Box>
<Box
sx={{
display: "grid",
gridTemplateColumns: "repeat(7, 1fr)",
mt: 2
}}
>
{days.map((day) => (
<Button
key={day.$date.toString()}
variant="icon"
sx={{
p: 0,
m: 0,
borderRadius: 100,
height: 35,
width: 35,
bg: day.selected ? "background-selected" : "transparent",
border: day.selected ? "1px solid var(--border-selected)" : "none"
}}
{...dayButton(day)}
>
<Text
variant="body"
sx={{
fontWeight: day.now ? "bold" : "normal",
color: day.now
? "accent"
: day.selected
? "accent-selected"
: day.inCurrentMonth
? "paragraph"
: "paragraph-secondary",
textAlign: "center"
}}
>
{day.day}
</Text>
</Button>
))}
</Box>
</Flex>
);
}

View File

@@ -29,8 +29,7 @@ import { showToast } from "../utils/toast";
import { useIsUserPremium } from "../hooks/use-is-user-premium"; import { useIsUserPremium } from "../hooks/use-is-user-premium";
import { Calendar, Pro } from "../components/icons"; import { Calendar, Pro } from "../components/icons";
import { usePersistentState } from "../hooks/use-persistent-state"; import { usePersistentState } from "../hooks/use-persistent-state";
import { DayPicker } from "react-day-picker"; import { DayPicker } from "../components/day-picker";
import "react-day-picker/dist/style.css";
import { PopupPresenter } from "@notesnook/ui"; import { PopupPresenter } from "@notesnook/ui";
import { useStore as useThemeStore } from "../stores/theme-store"; import { useStore as useThemeStore } from "../stores/theme-store";
import { getFormattedDate } from "@notesnook/common"; import { getFormattedDate } from "@notesnook/common";
@@ -387,45 +386,26 @@ export const AddReminderDialog = DialogManager.register(
target: dateInputRef.current, target: dateInputRef.current,
location: "top" location: "top"
}} }}
sx={{ >
".rdp": { <DayPicker
sx={{
bg: "background", bg: "background",
p: 1, p: 2,
boxShadow: `0px 0px 25px 5px ${ boxShadow: `0px 0px 25px 5px ${
theme === "dark" ? "#000000aa" : "#0000004e" theme === "dark" ? "#000000aa" : "#0000004e"
}`, }`,
borderRadius: "dialog", borderRadius: "dialog",
fontFamily: "body", width: 300
"--rdp-accent-color": "var(--accent)",
"--rdp-background-color": "var(--background)",
"--rdp-background-color-dark": "var(--background)",
"--rdp-selected-color": "var(--paragraph-selected)",
color: "paragraph"
},
".rdp-caption_label": { fontWeight: "heading" }
}}
>
<DayPicker
mode="single"
captionLayout="dropdown-buttons"
fromYear={new Date().getFullYear()}
toYear={new Date().getFullYear() + 99}
modifiers={{
disabled: { before: new Date() }
}} }}
selected={dayjs(date).toDate()} selected={dayjs(date).toDate()}
minDate={new Date()}
maxDate={new Date(new Date().getFullYear() + 99, 11, 31)}
onSelect={(day) => { onSelect={(day) => {
if (!day) return; if (!day) return;
const date = getFormattedDate(day, "date"); const date = getFormattedDate(day, "date");
setDate((d) => setDateOnly(date, d)); setDate((d) => setDateOnly(date, d));
if (dateInputRef.current) dateInputRef.current.value = date; if (dateInputRef.current) dateInputRef.current.value = date;
}} }}
styles={{
day: {
fontFamily: "inherit",
fontSize: 14
}
}}
/> />
</PopupPresenter> </PopupPresenter>
</> </>