diff --git a/src/components/global/Calendar.tsx b/src/components/global/Calendar.tsx index 4d0ff7e..a17e1aa 100644 --- a/src/components/global/Calendar.tsx +++ b/src/components/global/Calendar.tsx @@ -1,34 +1,163 @@ // Modules +import { v4 as uuidv4 } from "uuid"; +import React, { SetStateAction, useMemo, useState } from "react"; // Import helpers import { generateDateMatrix, matchLocaleLang, daysLocale, - stringSep, - matchDayNumber, matchDateToDay, + monthsLocale, + years, } from "../../helpers/calendar"; +// Icons +import prev from "../../icons/arrow-prev.svg"; +import next from "../../icons/arrow-next.svg"; +import sharp from "../../icons/sharp.svg"; + // Types -import { dateMatrix, dataItem } from "../../helpers/calendar"; +import { dataItem, dateMatrix } from "../../helpers/calendar"; const Calendar = () => { + // Static const date = new Date(); - const data = generateDateMatrix(date.getFullYear()); - const currentMonth = date.getMonth(); - // console.log(data); + const today = date.getDate(); + + // Inner Types + interface dayType { + day: number; + month: number; + } + type monthType = [number, React.Dispatch>]; + type monthSelectType = [boolean, React.Dispatch>]; + type dayStateType = [dayType, React.Dispatch>]; + + // State + const [currentYear, setCurrentYear]: monthType = useState(date.getFullYear()); // Ability to change the year + const [currentMonth, setCurrentMonth]: monthType = useState(date.getMonth()); // Ability to change the month + const [monthSelect, setMonthSelect]: monthSelectType = useState(false); // Month select controller + const [yearSelect, setYearSelect]: monthSelectType = useState(false); // Year select controller + const [selectedDay, setSelectedDay]: dayStateType = useState({ + day: -1, + month: -1, + }); // Ability to select a day + + // Memoized + + const data: dateMatrix = useMemo( + () => generateDateMatrix(currentYear), + [currentYear] + ); // For better performance: generateDateMatrix() is a demanding algorithm + + const dataDays: dataItem[] = useMemo(() => data[0], [data]); // Same + + const sorted: number[][] = useMemo( + () => matchDateToDay(dataDays[currentMonth].data), + [dataDays, currentMonth] + ); // For better performance: matchDateToDay() is a demanding algorithm + + // Chain memo reaction when currentYear changes - const dataDays: Array = data[0]; - const dataYear: dateMatrix[1] = data[1]; - const sorted = matchDateToDay(dataDays[currentMonth].data); - console.log(sorted); return (
-

{`${matchLocaleLang(currentMonth)} ${data[1] as number}`}

+
{ + setCurrentMonth((initial) => { + if (initial - 1 < 0) { + return 11; + } else { + return initial - 1; + } + }); + }} + > + +
+ +

+ { + setMonthSelect((initial) => !initial); + setYearSelect(false); + setSelectedDay({ day: -1, month: -1 }); + }} + > + {matchLocaleLang(currentMonth)} + {" "} + { + setYearSelect((initial) => !initial); + setMonthSelect(false); + setSelectedDay({ day: -1, month: -1 }); + }} + > + {data[1] as number} + +

+
{ + setCurrentMonth((initial) => { + if (initial + 1 > 11) { + return 0; + } else { + return initial + 1; + } + }); + }} + > + +
+
+ {monthsLocale.map((month, i) => { + return ( + { + setMonthSelect(false); + setCurrentMonth(i); + }} + > + {month} + + ); + })} +
+
+ {years.map((year) => { + return ( + { + setYearSelect(false); + setCurrentYear(year); + }} + > + {year} + + ); + })} +
@@ -40,11 +169,107 @@ const Calendar = () => { - {/* {sorted.map(arr => { - }) - })} */} + {sorted + ? sorted.map((arr) => { + if (arr.filter((x) => x === 0).length !== 7) { + return ( + + {arr.map((el) => { + if (el !== 0) { + if ( + today === el && + currentMonth === date.getMonth() && + currentYear === date.getFullYear() + ) { + return ( + + ); + } else { + return ( + + ); + } + } else { + return ; + } + })} + + ); + } else { + return ""; + } + }) + : ""}
{daysLocale[5]} {daysLocale[6]}
+ ) => { + const target = e.target as HTMLElement; + const span = target.innerText; + setSelectedDay({ + day: parseInt(span), + month: currentMonth, + }); + }} + > + {el} +
+
+ +
+
{`${el} ${matchLocaleLang( + currentMonth + )} ${currentYear}`}
+ Нет событий +
+
+ ) => { + const target = e.target as HTMLElement; + const span = target.innerText; + setSelectedDay({ + day: parseInt(span), + month: currentMonth, + }); + }} + > + {el} +
+
+ +
+
{`${el} ${matchLocaleLang( + currentMonth + )} ${currentYear}`}
+ Нет событий +
+
+
); diff --git a/src/helpers/calendar.ts b/src/helpers/calendar.ts index df8b41e..c4b884a 100644 --- a/src/helpers/calendar.ts +++ b/src/helpers/calendar.ts @@ -129,7 +129,16 @@ const days: string[] = [ "Sunday", ]; -export const daysLocale: string[] = ["Пн", "Вт", "Ср", "Чт", "Пн", "Сб", "Вс"]; +export const years: number[] = [ + date.getFullYear() - 2, + date.getFullYear() - 1, + date.getFullYear(), + date.getFullYear() + 1, + date.getFullYear() + 2, + date.getFullYear() + 3, +]; + +export const daysLocale: string[] = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]; const getFirstDayOfMonth = (year: number, month: number) => { return new Date(year, month, 1).getDay(); @@ -139,12 +148,19 @@ export const generateDateMatrix = (year: number) => { let out: Object[] = []; months.forEach((month) => { let values: string[] = []; + Array.from({ length: month.days }, (_, i) => i + 1).forEach((el, i) => { values.push( `${el}-${ getFirstDayOfMonth(year, month.number - 1) !== 0 ? days[(getFirstDayOfMonth(year, month.number - 1) - 1 + i) % 7] : days[(days.length - 1 + i) % 7] + }-${ + getFirstDayOfMonth(year, month.number - 1) === 0 + ? Math.floor((i + 6) / 7) + : Math.floor( + (i + getFirstDayOfMonth(year, month.number - 1) - 1) / 7 + ) }` ); }); @@ -161,51 +177,27 @@ export const stringSep = (string: string) => { return string.split("-") as string[]; }; +export const matchMonthNumber = (month: string): number => { + return monthsEng.indexOf(month); +}; + export const matchDayNumber = (day: string): number => { - if (day === days[0]) { - return 1; - } else if (day === days[1]) { - return 2; - } else if (day === days[2]) { - return 3; - } else if (day === days[3]) { - return 4; - } else if (day === days[4]) { - return 5; - } else if (day === days[5]) { - return 6; - } else if (day === days[6]) { - return 7; - } else { - return 0; - } + return days.indexOf(day) + 1; }; export const matchDateToDay = (array: string[]) => { - const placeholder = [0, 0, 0, 0, 0, 0, 0]; - const mondays: number[] = Array.from(placeholder); - const tuesdays: number[] = Array.from(placeholder); - const wednesdays: number[] = Array.from(placeholder); - const thursdays: number[] = Array.from(placeholder); - const fridays: number[] = Array.from(placeholder); - const saturdays: number[] = Array.from(placeholder); - const sundays: number[] = Array.from(placeholder); - const months: number[][] = [ - mondays, - tuesdays, - wednesdays, - thursdays, - fridays, - saturdays, - sundays, - ]; + const placeholder: number[] = [0, 0, 0, 0, 0, 0, 0]; + const row1: number[] = Array.from(placeholder); + const row2: number[] = Array.from(placeholder); + const row3: number[] = Array.from(placeholder); + const row4: number[] = Array.from(placeholder); + const row5: number[] = Array.from(placeholder); + const row6: number[] = Array.from(placeholder); + const months: number[][] = [row1, row2, row3, row4, row5, row6]; - array.forEach((el, i) => { + array.forEach((el) => { const data = stringSep(el); - console.log(i % 7); - // console.log(data); - // console.log(matchDayNumber(data[1])); - months[matchDayNumber(data[1]) - 1][i % 7] = parseInt(data[0]); + months[parseInt(data[2])][matchDayNumber(data[1]) - 1] = parseInt(data[0]); }); return months; diff --git a/src/icons/arrow-next.svg b/src/icons/arrow-next.svg new file mode 100644 index 0000000..a59673f --- /dev/null +++ b/src/icons/arrow-next.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/arrow-prev.svg b/src/icons/arrow-prev.svg new file mode 100644 index 0000000..c6bee60 --- /dev/null +++ b/src/icons/arrow-prev.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/sharp.svg b/src/icons/sharp.svg new file mode 100644 index 0000000..162495d --- /dev/null +++ b/src/icons/sharp.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/styles/_calendar.scss b/src/styles/_calendar.scss index 5d922cd..ee8422c 100644 --- a/src/styles/_calendar.scss +++ b/src/styles/_calendar.scss @@ -1,4 +1,6 @@ .calendar { + padding: 10rem; + max-width: 62rem; display: flex; flex-direction: column; justify-content: center; @@ -6,6 +8,182 @@ gap: 3rem; } -.calendar-top { - font-size: 2.4rem; +.calendar-bottom { + position: relative; + display: flex; + flex-direction: column; + gap: 3rem; +} + +.calendar-month-select { + opacity: 0; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 1rem; + overflow: hidden; + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 0; + background: #fff; + z-index: 3; + box-shadow: 0px 0px; + @include transition-std; + + &.active { + padding: 3rem; + height: 37rem; + box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.25); + @include transition-std; + opacity: 1; + } + + span { + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + font-size: 1.8rem; + border: 0.1rem solid rgba(0, 0, 0, 0.25); + background: #fff; + @include transition-std; + + &.active { + background: $base-green; + color: #fff; + cursor: pointer; + @include transition-std; + } + } +} + +.calendar-top { + width: 100%; + font-size: 2.4rem; + display: flex; + justify-content: space-between; + align-items: center; + + h4 { + text-align: center; + } + + span { + cursor: pointer; + } +} + +.calendar-prev, +.calendar-next { + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + img { + pointer-events: none; + } +} + +.calendar-table { + tbody { + display: flex; + flex-direction: column; + gap: 2.4rem; + } + th { + color: $base-green; + } + tr { + pointer-events: none; + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 3.2rem; + + * { + font-size: 1.8rem; + } + } + + td { + position: relative; + pointer-events: all; + @include transition-std; + display: flex; + align-items: center; + justify-content: center; + padding: 0.4rem; + cursor: pointer; + min-width: 3.2rem; + min-height: 3.2rem; + border-radius: 50%; + pointer-events: all; + background: transparent; + + &.active { + background: #e53131; + color: white; + @include transition-std; + } + + &.selected { + background: $base-green; + color: white; + @include transition-std; + + .event { + padding: 2.4rem; + opacity: 1; + width: 24.3rem; + min-height: 13rem; + height: fit-content; + @include transition-std; + z-index: 5; + } + } + + span { + pointer-events: none; + display: flex; + align-items: center; + justify-content: center; + } + + .event { + pointer-events: none; + position: absolute; + overflow: visible; + opacity: 0; + width: 0; + height: 0; + background: $base-green; + left: -10rem; + bottom: calc(100% + 1.4rem); + padding: 0; + border-radius: 1rem; + @include transition-std; + + .sharp { + display: flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 2rem; + position: absolute; + bottom: -1.4rem; + left: 43%; + + img { + width: 0.8rem; + height: 0.8rem; + } + } + } + } +} + +.calendar-return { + width: 100%; + @include button-std(0, 1.4rem, true); }