datepicker-benoitmouly
Version:
Custom datepicker made for React, using css and js
438 lines (414 loc) • 16 kB
JavaScript
import { useEffect, useRef } from "react";
import dayjs from "dayjs";
import customParseFormat from "dayjs/plugin/customParseFormat";
import "./style/DatePicker.css";
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
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 /*#__PURE__*/_jsx(_Fragment, {
children: /*#__PURE__*/_jsxs("div", {
className: "inputpicker",
children: [/*#__PURE__*/_jsx("label", {
className: "custom-date-picker-label",
htmlFor: props.className,
children: props.label ? props.label : null
}), /*#__PURE__*/_jsx("input", {
typeof: "text",
className: "displayDate",
ref: inputRef,
defaultValue: currentDate,
onClick: e => toggleDateOpen(e.target.value, currentDate),
name: props.className,
id: props.className
}), " ", /*#__PURE__*/_jsx("br", {}), !props.getDate && 'Vous devez ajouter un état "getDate" en props de votre composant', /*#__PURE__*/_jsxs("div", {
id: "datepicker",
ref: datepicker,
className: "dateHided",
children: [/*#__PURE__*/_jsxs("div", {
className: "first-row btns-spec",
children: [/*#__PURE__*/_jsxs("button", {
className: "previousMonth btnUtils",
onClick: e => updatePreviousMonth(e),
children: [" ", "<", " "]
}), /*#__PURE__*/_jsx("button", {
className: "homeDay btnUtils",
onClick: e => homeDate(e, e.target.value, currentDate),
children: /*#__PURE__*/_jsx("i", {
className: "fa fa-home"
})
}), /*#__PURE__*/_jsx("select", {
className: "selectMonth btnUtils",
ref: monthRef,
defaultValue: currentMonth,
onChange: e => changeMonth(e.target.value, currentDate)
}), /*#__PURE__*/_jsx("select", {
className: "selectYear btnUtils",
ref: yearRef,
defaultValue: currentYear,
onChange: e => changeYear(e.target.value, currentDate)
}), /*#__PURE__*/_jsxs("button", {
className: "nextMonth btnUtils",
onClick: e => updateNextMonth(e),
children: [" ", ">", " "]
})]
}), /*#__PURE__*/_jsx("div", {
className: "second-row",
children: /*#__PURE__*/_jsx("table", {
ref: tableref,
children: /*#__PURE__*/_jsxs("tbody", {
children: [/*#__PURE__*/_jsxs("tr", {
className: "days-label",
children: [/*#__PURE__*/_jsx("th", {
className: "day-value",
datavalue: "0",
children: "Sun"
}), /*#__PURE__*/_jsx("th", {
className: "day-value",
datavalue: "1",
children: "Mon"
}), /*#__PURE__*/_jsx("th", {
className: "day-value",
datavalue: "2",
children: "Tues"
}), /*#__PURE__*/_jsx("th", {
className: "day-value",
datavalue: "3",
children: "Wed"
}), /*#__PURE__*/_jsx("th", {
className: "day-value",
datavalue: "4",
children: "Thur"
}), /*#__PURE__*/_jsx("th", {
className: "day-value",
datavalue: "5",
children: "Fri"
}), /*#__PURE__*/_jsx("th", {
className: "day-value",
datavalue: "6",
children: "Sat"
})]
}), /*#__PURE__*/_jsx("tr", {
className: "days-btn"
})]
})
})
})]
})]
})
});
};
export default DatePickerCustom;