calendar
This commit is contained in:
parent
69956257a1
commit
cc00025341
|
|
@ -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<SetStateAction<number>>];
|
||||
type monthSelectType = [boolean, React.Dispatch<SetStateAction<boolean>>];
|
||||
type dayStateType = [dayType, React.Dispatch<SetStateAction<dayType>>];
|
||||
|
||||
// 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<dataItem> = data[0];
|
||||
const dataYear: dateMatrix[1] = data[1];
|
||||
const sorted = matchDateToDay(dataDays[currentMonth].data);
|
||||
console.log(sorted);
|
||||
return (
|
||||
<div className="calendar">
|
||||
<div className="calendar-top">
|
||||
<h4>{`${matchLocaleLang(currentMonth)} ${data[1] as number}`}</h4>
|
||||
<div
|
||||
className="calendar-prev"
|
||||
onClick={() => {
|
||||
setCurrentMonth((initial) => {
|
||||
if (initial - 1 < 0) {
|
||||
return 11;
|
||||
} else {
|
||||
return initial - 1;
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img src={prev} alt="" />
|
||||
</div>
|
||||
|
||||
<h4>
|
||||
<span
|
||||
onClick={() => {
|
||||
setMonthSelect((initial) => !initial);
|
||||
setYearSelect(false);
|
||||
setSelectedDay({ day: -1, month: -1 });
|
||||
}}
|
||||
>
|
||||
{matchLocaleLang(currentMonth)}
|
||||
</span>{" "}
|
||||
<span
|
||||
onClick={() => {
|
||||
setYearSelect((initial) => !initial);
|
||||
setMonthSelect(false);
|
||||
setSelectedDay({ day: -1, month: -1 });
|
||||
}}
|
||||
>
|
||||
{data[1] as number}
|
||||
</span>
|
||||
</h4>
|
||||
<div
|
||||
className="calendar-next"
|
||||
onClick={() => {
|
||||
setCurrentMonth((initial) => {
|
||||
if (initial + 1 > 11) {
|
||||
return 0;
|
||||
} else {
|
||||
return initial + 1;
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img src={next} alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="calendar-bottom">
|
||||
<div
|
||||
className={
|
||||
monthSelect
|
||||
? "calendar-month-select active"
|
||||
: "calendar-month-select"
|
||||
}
|
||||
>
|
||||
{monthsLocale.map((month, i) => {
|
||||
return (
|
||||
<span
|
||||
className={i === currentMonth ? "active" : ""}
|
||||
key={uuidv4()}
|
||||
onClick={() => {
|
||||
setMonthSelect(false);
|
||||
setCurrentMonth(i);
|
||||
}}
|
||||
>
|
||||
{month}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
yearSelect
|
||||
? "calendar-month-select calendar-year-select active"
|
||||
: "calendar-month-select calendar-year-select"
|
||||
}
|
||||
>
|
||||
{years.map((year) => {
|
||||
return (
|
||||
<span
|
||||
className={year === currentYear ? "active" : ""}
|
||||
key={uuidv4()}
|
||||
onClick={() => {
|
||||
setYearSelect(false);
|
||||
setCurrentYear(year);
|
||||
}}
|
||||
>
|
||||
{year}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<table className="calendar-table">
|
||||
<tbody>
|
||||
<tr className="calendar-table-header">
|
||||
|
|
@ -40,11 +169,107 @@ const Calendar = () => {
|
|||
<th>{daysLocale[5]}</th>
|
||||
<th>{daysLocale[6]}</th>
|
||||
</tr>
|
||||
{/* {sorted.map(arr => {
|
||||
})
|
||||
})} */}
|
||||
{sorted
|
||||
? sorted.map((arr) => {
|
||||
if (arr.filter((x) => x === 0).length !== 7) {
|
||||
return (
|
||||
<tr key={uuidv4()}>
|
||||
{arr.map((el) => {
|
||||
if (el !== 0) {
|
||||
if (
|
||||
today === el &&
|
||||
currentMonth === date.getMonth() &&
|
||||
currentYear === date.getFullYear()
|
||||
) {
|
||||
return (
|
||||
<td
|
||||
className={
|
||||
selectedDay.day === el &&
|
||||
selectedDay.month === currentMonth
|
||||
? "active selected"
|
||||
: "active"
|
||||
}
|
||||
key={uuidv4()}
|
||||
onClick={(
|
||||
e: React.MouseEvent<HTMLElement>
|
||||
) => {
|
||||
const target = e.target as HTMLElement;
|
||||
const span = target.innerText;
|
||||
setSelectedDay({
|
||||
day: parseInt(span),
|
||||
month: currentMonth,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<span>{el}</span>
|
||||
<div className="event">
|
||||
<div className="sharp">
|
||||
<img src={sharp} alt="" />
|
||||
</div>
|
||||
<h5>{`${el} ${matchLocaleLang(
|
||||
currentMonth
|
||||
)} ${currentYear}`}</h5>
|
||||
<span>Нет событий</span>
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<td
|
||||
className={
|
||||
selectedDay.day === el &&
|
||||
selectedDay.month === currentMonth
|
||||
? "selected"
|
||||
: ""
|
||||
}
|
||||
key={uuidv4()}
|
||||
onClick={(
|
||||
e: React.MouseEvent<HTMLElement>
|
||||
) => {
|
||||
const target = e.target as HTMLElement;
|
||||
const span = target.innerText;
|
||||
setSelectedDay({
|
||||
day: parseInt(span),
|
||||
month: currentMonth,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<span>{el}</span>
|
||||
<div className="event">
|
||||
<div className="sharp">
|
||||
<img src={sharp} alt="" />
|
||||
</div>
|
||||
<h5>{`${el} ${matchLocaleLang(
|
||||
currentMonth
|
||||
)} ${currentYear}`}</h5>
|
||||
<span>Нет событий</span>
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return <td key={uuidv4()}></td>;
|
||||
}
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
})
|
||||
: ""}
|
||||
</tbody>
|
||||
</table>
|
||||
<button
|
||||
type="button"
|
||||
className="calendar-return"
|
||||
onClick={() => {
|
||||
setCurrentMonth(date.getMonth());
|
||||
setCurrentYear(date.getFullYear());
|
||||
}}
|
||||
>
|
||||
Сегодня
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="12" height="22" viewBox="0 0 12 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.390524 21.5973C-0.130175 21.0603 -0.130175 20.1897 0.390524 19.6527L8.78105 11L0.390523 2.34727C-0.130176 1.8103 -0.130176 0.939699 0.390523 0.402729C0.911222 -0.134243 1.75544 -0.134243 2.27614 0.402729L11.6095 10.0277C12.1302 10.5647 12.1302 11.4353 11.6095 11.9723L2.27614 21.5973C1.75544 22.1342 0.911223 22.1342 0.390524 21.5973Z" fill="#1C1717"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 508 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="12" height="22" viewBox="0 0 12 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.6095 0.402728C12.1302 0.939699 12.1302 1.8103 11.6095 2.34727L3.21895 11L11.6095 19.6527C12.1302 20.1897 12.1302 21.0603 11.6095 21.5973C11.0888 22.1342 10.2446 22.1342 9.72386 21.5973L0.390523 11.9723C-0.130176 11.4353 -0.130176 10.5647 0.390523 10.0277L9.72386 0.402728C10.2446 -0.134243 11.0888 -0.134243 11.6095 0.402728Z" fill="#1C1717"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 500 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="8" height="6" viewBox="0 0 8 6" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 6L0.535898 -1.75695e-07L7.4641 4.29987e-07L4 6Z" fill="#01815E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 177 B |
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue