datepicker-benoitmouly
Version:
Custom datepicker made for React, using css and js
495 lines (426 loc) • 15.8 kB
JSX
import { useEffect, useRef } from "react";
import dayjs from "dayjs";
import customParseFormat from "dayjs/plugin/customParseFormat";
import "./style/DatePicker.css";
dayjs.extend(customParseFormat);
/**
* Custom DatePicker by Benoît M
* @component react
* @param {Object} getDate props, function returning value to the parent elt
* @returns {JsxElement}
*/
const DatePickerCustom = (props) => {
useEffect(() => {
createTable();
hydrateMonthScroller();
hydrateYearScroller();
eventOnDays();
switchDisplayDays();
displayCurrentDate(currentDate);
// eslint-disable-next-line
}, []);
const inputRef = useRef("");
const tableref = useRef("");
const datepicker = useRef("");
const monthRef = useRef(null);
const yearRef = useRef(null);
const { getDate } = props;
const displayCurrentDate = (b) => {
inputRef.current.value = b;
if(!getDate){
return
}
getDate(b);
};
let date = dayjs();
let months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
let currentDate = date.format("DD/MM/YYYY");
let showDatePicker = datepicker.current;
let swipeDate;
let currentMonth = date.format("MM");
let currentYear = date.format("YYYY");
/*
Create Table
*/
const createTable = () => {
let table = tableref.current;
let b = dayjs().format("DD/MM/YYYY");
for (let i = 0; i < 6; i++) {
// 5 lines
let row = table.insertRow();
for (let t = 0; t < 7; t++) {
// 7 columns
let cell = row.insertCell();
let btn = document.createElement("button");
btn.classList.add("choosingDay");
cell.appendChild(btn);
}
}
switchDisplayDays(table, b);
};
/*
Hydrate scrollers selectMonth
*/
const hydrateMonthScroller = () => {
let selectMonth = monthRef.current;
let defaultMonthOption = document.createElement("option");
defaultMonthOption.value = date.format("MM");
defaultMonthOption.innerHTML = date.format("MMMM");
selectMonth.appendChild(defaultMonthOption);
for (const [index, w] of months.entries()) {
let createOption = document.createElement("option");
createOption.innerHTML = w;
selectMonth.appendChild(createOption);
createOption.setAttribute("value", index + 1);
createOption.classList.add("monthOption");
}
};
/*
Hydrate scrollers selectYear
*/
const hydrateYearScroller = () => {
let selectYear = yearRef.current;
let defaultYearOption = document.createElement("option");
defaultYearOption.value = date.format("YYYY");
defaultYearOption.innerHTML = date.format("YYYY");
selectYear.appendChild(defaultYearOption);
let startDate = 1949;
let endDate = 2049;
while (startDate < endDate) {
startDate++;
let createOption = document.createElement("option");
createOption.innerHTML = startDate;
createOption.value = startDate;
createOption.classList.add("yearOption");
selectYear.appendChild(createOption);
}
};
/*
Toggle calendar appearance
*/
const toggleDateOpen = (e) => {
switchDisplayDays(e, date);
let showDatePicker = datepicker.current;
showDatePicker.classList.toggle("dateHided");
document.addEventListener("click", function (event) {
if (!event.target.closest(".inputpicker")) {
// IF le closest qui remonte au parent n'a pas la classe dateofbirth, alors on est pas dessus
showDatePicker.classList.add("dateHided");
}
});
};
/*
CurrentDate into input
*/
const homeDate = (event, e, b) => {
event.preventDefault();
swipeDate = dayjs().format("DD/MM/YYYY");
b = dayjs().format("DD/MM/YYYY");
displayCurrentDate(b);
let monthsOption = tableref.current.querySelectorAll(".monthOption");
let yearsOption = tableref.current.querySelectorAll(".yearOption");
monthRef.current.value = currentMonth;
yearRef.current.value = currentYear;
updateMonth(swipeDate, monthsOption); // input select follows the current month selected
updateYear(swipeDate, yearsOption);
switchDisplayDays(e, b);
};
/*
Playing with btn months
*/
const updateNextMonth = (e, b) => {
e.preventDefault();
let currentdayjs = inputRef.current.value;
let nextMonthTimeStamp = dayjs(currentdayjs, "DD/MM/YYY").toDate();
swipeDate = dayjs(nextMonthTimeStamp).add(1, "month");
let year = dayjs(swipeDate).format("YYYY"); // return string, we need int
year = parseInt(year) + 100; // dayjs begins at 1900
yearRef.current.value = year;
let sanitizedMonth = dayjs(swipeDate).format("MM"); // return string, we need int
monthRef.current.value = parseInt(sanitizedMonth);
b = swipeDate.format("DD/" + sanitizedMonth + "/" + year);
let monthsOption = tableref.current.querySelectorAll(".monthOption");
let yearsOption = tableref.current.querySelectorAll(".yearOption");
updateMonth(b, monthsOption); // input select follows the current month selected
updateYear(b, yearsOption);
displayCurrentDate(b);
switchDisplayDays(e, b);
};
const updatePreviousMonth = (e, b) => {
e.preventDefault();
let currentdayjs = inputRef.current.value;
let previousMonthTimeStamp = dayjs(currentdayjs, "DD/MM/YYYY").toDate();
swipeDate = dayjs(previousMonthTimeStamp).subtract(1, "month");
let year = dayjs(swipeDate).format("YYYY");
yearRef.current.value = parseInt(year);
let sanitizedMonth = swipeDate.format("MM");
monthRef.current.value = parseInt(sanitizedMonth);
b = swipeDate.format("DD/" + sanitizedMonth + "/" + year);
let monthsOption = tableref.current.querySelectorAll(".monthOption");
let yearsOption = tableref.current.querySelectorAll(".yearOption");
updateMonth(b, monthsOption);
updateYear(b, yearsOption);
displayCurrentDate(b);
switchDisplayDays(e, b);
};
/*
Event on scrollers
*/
const changeMonth = (e, b) => {
let previousTimeStamp = dayjs(b, "DD/MM/YYYY").toDate(); // Converting string to date
swipeDate = dayjs(previousTimeStamp);
let year = yearRef.current.value;
let month = monthRef.current.value;
let sanitizedMonth = month.padStart(2, "0");
b = swipeDate.format("DD/" + sanitizedMonth + "/" + year);
displayCurrentDate(b);
switchDisplayDays(e, b);
};
const changeYear = (e, b) => {
let previousTimeStamp = dayjs(b, "DD/MM/YYYY").toDate();
swipeDate = dayjs(previousTimeStamp);
let sanitizedYear = e.padStart(2, "0"); // adding 0 when number < 10
let month = monthRef.current.value;
let sanitizedMonth = month.padStart(2, "0");
b = swipeDate.format("DD/" + sanitizedMonth + "/" + sanitizedYear);
displayCurrentDate(b);
switchDisplayDays(e, b);
};
/*
Event on days
*/
const eventOnDays = (e, b) => {
let btnDayValueFill;
let btnDays = tableref.current.querySelectorAll(".choosingDay");
for (let i = 0; i < btnDays.length; i++) {
// eslint-disable-next-line no-loop-func
btnDays[i].addEventListener("click", function (e) {
e.preventDefault();
let btnDayValue = btnDays[i].innerHTML;
let monthChoosed = monthRef.current.value;
let sanitizedMonth = monthChoosed.padStart(2, "0");
let yearChoosed = yearRef.current.value;
btnDayValueFill = btnDayValue.padStart(2, "0");
e = date.format(
btnDayValueFill + "/" + sanitizedMonth + "/" + yearChoosed
);
toggleDateOpen(showDatePicker);
displayCurrentDate(e);
});
}
};
/*
Update current month index
*/
function updateMonth(date, elts) {
let previousTimeStamp = dayjs(date, "DD/MM/YYYY").toDate();
swipeDate = dayjs(previousTimeStamp);
let monthIndex = swipeDate.format("M");
for (let i = 0; i < elts.length; i++) {
if (elts[i].value === monthIndex) {
elts[i].setAttribute("selected", "true");
} else {
elts[i].removeAttribute("selected", "true");
}
}
}
/*
Update current year index
*/
function updateYear(date, elts) {
let previousTimeStamp = dayjs(date, "DD/MM/YYYY").toDate();
swipeDate = dayjs(previousTimeStamp);
let yearIndex = swipeDate.format("YYYY");
for (let i = 0; i < elts.length; i++) {
if (elts[i].value === yearIndex) {
elts[i].setAttribute("selected", "true");
} else {
elts[i].removeAttribute("selected", "true");
}
}
}
/*
Update current display
*/
const switchDisplayDays = (e, b) => {
let lastMonthDate = [];
let nextMonthDate = [];
let previousMonthTimeStamp = dayjs(b, "DD/MM/YYYY").toDate(); // b is date passed and works with everything because daysInMonth gives a date format
let previousMonthSanitize = dayjs(previousMonthTimeStamp)
.subtract(1, "month")
.format("DD/MM/YYYY"); // INDEX last day of last month
let lastDayPreviousMonthNumber = dayjs(previousMonthSanitize, "DD/MM/YYYY")
.endOf("month")
.format("DD"); // INT
let currentMonthTimeStamp = dayjs(b, "DD/MM/YYYY").toDate();
let firstDay = dayjs(currentMonthTimeStamp).startOf("month").format("d");
let firstDayNumber = dayjs(currentMonthTimeStamp)
.startOf("month")
.format("DD"); // number of current day
let lastDayNumber = dayjs(currentMonthTimeStamp)
.endOf("month")
.format("DD"); // number date of the day
// If month began at THURSDAY, then the first day of the month has to begin on the index of THURSDAY
// We fill days until then end, if the last one is SUNDAY, then we have to finish on the index of SUNDAY
// Then we fill the index of the previous and next month
let dayz = tableref.current.querySelectorAll(".day-value");
let btns = tableref.current.querySelectorAll(".choosingDay");
for (let i = 0; i < btns.length; i++) {
btns[i].classList.remove("beforeFirstDay");
btns[i].classList.remove("lastDayOfCurrentMonth");
btns[i].classList.remove("afterLastDayOfCurrentMonth");
btns[i].classList.remove("afterFirstDay");
}
let firstDayFound = 0;
let daytobegin;
// eslint-disable-next-line
let daytoend;
for (let i = 0; i < 7; i++) {
if (firstDay === dayz[i].getAttribute("datavalue")) {
btns[i].innerHTML = firstDayNumber;
firstDayFound = i; // Found the day of the first week from current month
}
}
btns.forEach((element, index) => {
if (index === firstDayFound) {
element.innerHTML = parseInt(firstDayNumber);
element.classList.add("firstDay");
daytobegin = index;
} else if (index < firstDayFound) {
lastMonthDate.push(lastDayPreviousMonthNumber - index);
lastMonthDate.sort();
element.classList.add("beforeFirstDay");
} else if (index > firstDayFound) {
if (parseInt(daytobegin) > parseInt(lastDayNumber)) {
element.classList.add("afterLastDayOfCurrentMonth");
element.innerHTML = parseInt(daytobegin) - parseInt(lastDayNumber);
} else {
nextMonthDate.push(parseInt(index) - parseInt(firstDayFound) + 1);
element.classList.add("afterFirstDay");
}
}
});
let beforeDays = tableref.current.querySelectorAll(".beforeFirstDay");
let afterDays = tableref.current.querySelectorAll(".afterFirstDay");
lastMonthDate.forEach((lastDates, index) => {
beforeDays[index].innerHTML = lastDates;
});
nextMonthDate.forEach((afterDates, index) => {
if (parseInt(afterDates) === parseInt(lastDayNumber)) {
afterDays[index].classList.add("lastDayOfCurrentMonth");
afterDays[index].innerHTML = afterDates;
daytoend = afterDates;
} else if (parseInt(afterDates) < parseInt(lastDayNumber)) {
afterDays[index].innerHTML = afterDates;
} else {
afterDays[index].classList.add("afterLastDayOfCurrentMonth");
}
});
let afterLastDays = tableref.current.querySelectorAll(
".afterLastDayOfCurrentMonth"
);
for (let i = 0; i < afterLastDays.length; i++) {
afterLastDays[i].innerHTML = i + 1;
}
};
if(!props.getDate){
console.error('Vous devez ajouter un état a votre composant')
}
return (
<>
<div className="inputpicker">
<label className='custom-date-picker-label' htmlFor={props.className}>{props.label ? props.label : null}</label>
<input
typeof="text"
className="displayDate"
ref={inputRef}
defaultValue={currentDate}
onClick={(e) => toggleDateOpen(e.target.value, currentDate)}
name={props.className}
id={props.className}
></input> <br/>
{!props.getDate && ('Vous devez ajouter un état "getDate" en props de votre composant' )}
<div id="datepicker" ref={datepicker} className="dateHided">
<div className="first-row btns-spec">
<button
className="previousMonth btnUtils"
onClick={(e) => updatePreviousMonth(e)}
>
{" "}
<{" "}
</button>
<button
className="homeDay btnUtils"
onClick={(e) => homeDate(e, e.target.value, currentDate)}
>
<i className="fa fa-home"></i>
</button>
<select
className="selectMonth btnUtils"
ref={monthRef}
defaultValue={currentMonth}
onChange={(e) => changeMonth(e.target.value, currentDate)}
></select>
<select
className="selectYear btnUtils"
ref={yearRef}
defaultValue={currentYear}
onChange={(e) => changeYear(e.target.value, currentDate)}
></select>
<button
className="nextMonth btnUtils"
onClick={(e) => updateNextMonth(e)}
>
{" "}
>{" "}
</button>
</div>
<div className="second-row">
<table ref={tableref}>
<tbody>
<tr className="days-label">
<th className="day-value" datavalue="0">
Sun
</th>
<th className="day-value" datavalue="1">
Mon
</th>
<th className="day-value" datavalue="2">
Tues
</th>
<th className="day-value" datavalue="3">
Wed
</th>
<th className="day-value" datavalue="4">
Thur
</th>
<th className="day-value" datavalue="5">
Fri
</th>
<th className="day-value" datavalue="6">
Sat
</th>
</tr>
<tr className="days-btn"></tr>
</tbody>
</table>
</div>
</div>
</div>
</>
);
};
export default DatePickerCustom;