From bab4aa1ddbc8874f1fecbd528c27b45f82d8f9d5 Mon Sep 17 00:00:00 2001 From: Sergey Elpashev Date: Thu, 3 Apr 2025 16:17:49 +0300 Subject: [PATCH] feat: calendar screen --- src/pages/calendar.module.scss | 1 + src/pages/calendar.tsx | 211 +++++++++++++++++++++++++++++++++ src/pages/profile.tsx | 4 +- 3 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 src/pages/calendar.module.scss create mode 100644 src/pages/calendar.tsx diff --git a/src/pages/calendar.module.scss b/src/pages/calendar.module.scss new file mode 100644 index 0000000..0be3cf1 --- /dev/null +++ b/src/pages/calendar.module.scss @@ -0,0 +1 @@ +@reference "../index.scss"; diff --git a/src/pages/calendar.tsx b/src/pages/calendar.tsx new file mode 100644 index 0000000..64e3ca4 --- /dev/null +++ b/src/pages/calendar.tsx @@ -0,0 +1,211 @@ +import { FunctionComponent, h } from "preact"; +import { useState } from "preact/hooks"; + +type MarkedDateType = "event" | "holiday" | "important" | string; +type MarkedDates = Record; + +interface BigCalendarProps { + onDateSelect?: (date: Date) => void; + markedDates?: MarkedDates; + className?: string; +} + +const BigCalendar: FunctionComponent = ({ + onDateSelect, + markedDates = {}, + className = "", +}: BigCalendarProps) => { + const [currentDate, setCurrentDate] = useState(new Date()); + const [selectedDate, setSelectedDate] = useState(null); + + const monthNames: string[] = [ + "Январь", + "Февраль", + "Март", + "Апрель", + "Май", + "Июнь", + "Июль", + "Август", + "Сентябрь", + "Октябрь", + "Ноябрь", + "Декабрь", + ]; + + const dayNames: string[] = ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"]; + + const getDaysInMonth = (year: number, month: number): number => { + return new Date(year, month + 1, 0).getDate(); + }; + + const getFirstDayOfMonth = (year: number, month: number): number => { + return new Date(year, month, 1).getDay(); + }; + + const handlePrevMonth = (): void => { + setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1)); + }; + + const handleNextMonth = (): void => { + setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1)); + }; + + const handleDateClick = (date: Date, isCurrentMonth: boolean): void => { + if (!isCurrentMonth) { + setCurrentDate(new Date(date.getFullYear(), date.getMonth(), 1)); + } + setSelectedDate(date); + onDateSelect?.(date); + }; + + const isDateMarked = (date: Date): MarkedDateType | undefined => { + const dateStr = date.toISOString().split("T")[0]; + return markedDates[dateStr]; + }; + + const renderDays = (): h.JSX.Element[] => { + const year: number = currentDate.getFullYear(); + const month: number = currentDate.getMonth(); + + const daysInMonth: number = getDaysInMonth(year, month); + const firstDayOfMonth: number = getFirstDayOfMonth(year, month); + const lastDayOfMonth: number = new Date(year, month, daysInMonth).getDay(); + + const days: h.JSX.Element[] = []; + + // Дни предыдущего месяца + const prevMonthDays = getDaysInMonth(year, month - 1); + for (let i = firstDayOfMonth - 1; i >= 0; i--) { + const day = prevMonthDays - i; + const date = new Date(year, month - 1, day); + const dateStr = date.toISOString().split("T")[0]; + const isSelected = selectedDate?.toISOString().split("T")[0] === dateStr; + const markType = isDateMarked(date); + + days.push( +
handleDateClick(date, false)} + > +
{day}
+ {markType && ( +
+ {markType} +
+ )} +
+ ); + } + + // Дни текущего месяца + for (let day = 1; day <= daysInMonth; day++) { + const date = new Date(year, month, day); + const dateStr = date.toISOString().split("T")[0]; + const isSelected = selectedDate?.toISOString().split("T")[0] === dateStr; + const markType = isDateMarked(date); + const isToday = new Date().toISOString().split("T")[0] === dateStr; + + days.push( +
handleDateClick(date, true)} + > +
+ {day} +
+ + {markType && ( +
+ {markType} +
+ )} +
+ ); + } + + // Дни следующего месяца + const daysToAdd = 6 - lastDayOfMonth; + for (let day = 1; day <= daysToAdd; day++) { + const date = new Date(year, month + 1, day); + const dateStr = date.toISOString().split("T")[0]; + const isSelected = selectedDate?.toISOString().split("T")[0] === dateStr; + const markType = isDateMarked(date); + + days.push( +
handleDateClick(date, false)} + > +
{day}
+ {markType && ( +
+ {markType} +
+ )} +
+ ); + } + + return days; + }; + + return ( +
+
+ +

+ {monthNames[currentDate.getMonth()]} {currentDate.getFullYear()} +

+ +
+ +
+ {dayNames.map((day) => ( +
+ {day} +
+ ))} +
+ +
{renderDays()}
+
+ ); +}; + +export default BigCalendar; diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx index 91c0c90..5a60198 100644 --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -1,6 +1,6 @@ import Menu from "@/components/menu"; import { FunctionComponent } from "preact"; -import { Route, Router, useLocation } from "preact-iso"; +import { lazy, Route, Router, useLocation } from "preact-iso"; const ProfilePage: FunctionComponent = () => { const { route } = useLocation(); @@ -82,7 +82,7 @@ const ProfilePage: FunctionComponent = () => { )} />

Tasks

} /> -

Calendar

} /> + import("./calendar"))} /> {