js-calendar-strip
Version:
<div align="center"> <h1 align="center"> js-calendar-strip </h1> </div> <div align="center"> Crafted with passion by <a href="https://github.com/ravisoni01">Ravi </a> </div>
196 lines (158 loc) • 5.38 kB
JavaScript
import { useEffect, useRef, useState } from 'react';
import { addDays, format, subDays } from 'date-fns';
const useViewPort = () => {
const [width, setWidth] = useState(window.innerWidth);
const [height, setHeight] = useState(window.innerHeight);
const handleWindowResize = () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
};
useEffect(() => {
window.addEventListener('resize', handleWindowResize);
return () => window.removeEventListener('resize', handleWindowResize);
}, []);
return { width, height };
};
function useHorizontalScroll(handlePrevious, handleNext, visibleDates) {
const elRef = useRef();
useEffect(() => {
const el = elRef.current;
if (el) {
const onWheel = e => {
if (e.deltaY === 0) return;
e.preventDefault();
el.scrollTo({
left: el.scrollLeft + e.deltaY,
behavior: 'smooth',
});
if (el.scrollLeft === 0 && visibleDates.length > 0) {
handlePrevious();
} else if (
Math.ceil(el.scrollLeft) ===
el.scrollWidth - el.clientWidth
) {
handleNext();
}
};
const onScroll = () => {
const scrollDiff = el.scrollWidth - el.clientWidth - el.scrollLeft;
if (scrollDiff <= 1 && visibleDates.length > 0) {
handleNext();
} else if (el.scrollLeft === 0) {
handlePrevious();
}
};
el.addEventListener('wheel', onWheel);
el.addEventListener('scroll', onScroll);
return () => {
el.removeEventListener('wheel', onWheel);
el.removeEventListener('scroll', onScroll);
};
}
}, [visibleDates]);
return elRef;
}
const isPreviousDate = (currentDate, startDate) => {
const date1 = new Date(format(currentDate, 'yyyy-MM-dd'));
const date2 = new Date(format(startDate, 'yyyy-MM-dd'));
return date1 < date2;
};
const isNextDate = (currentDate, startDate) => {
const date1 = new Date(format(currentDate, 'yyyy-MM-dd'));
const date2 = new Date(format(startDate, 'yyyy-MM-dd'));
return date1 > date2;
};
const CalendarLogic = (startDate, endDate) => {
const { width } = useViewPort();
const generateNextOrPreviousDateCount = width > 500 ? 5 : 2;
const [visibleDates, setVisibleDates] = useState([]);
const [firstMonthVisible, setFirstMonthVisible] = useState('');
const [secondMonthVisible, setSecondMonthVisible] = useState('');
const generateDates = start => {
let dates = [];
let lastRenderValue = Math.floor(width / 90);
for (let i = 0; i <= lastRenderValue; i++) {
dates.push(addDays(start, i));
}
return dates;
};
const handlePrevious = () => {
const newVisibleDates = [...visibleDates];
const start = new Date(newVisibleDates[0]);
for (let i = 1; i <= generateNextOrPreviousDateCount; i++) {
const newDate = subDays(start, i);
if (!startDate) {
newVisibleDates.pop();
newVisibleDates.unshift(newDate);
}
if (startDate && !isPreviousDate(newDate, startDate)) {
newVisibleDates.pop();
newVisibleDates.unshift(newDate);
}
}
setVisibleDates(newVisibleDates);
};
const handleNext = () => {
const newVisibleDates = [...visibleDates];
const lastDate = new Date(newVisibleDates[newVisibleDates.length - 1]);
for (let i = 1; i <= generateNextOrPreviousDateCount; i++) {
const newDate = addDays(lastDate, i);
if (!endDate) {
newVisibleDates.shift();
newVisibleDates.push(newDate);
}
if (endDate && !isNextDate(newDate, endDate)) {
newVisibleDates.shift();
newVisibleDates.push(newDate);
}
}
setVisibleDates(newVisibleDates);
};
const scrollRef = useHorizontalScroll(
handlePrevious,
handleNext,
visibleDates
);
useEffect(() => {
const initialDate = startDate ? startDate : new Date();
const initialDates = generateDates(initialDate);
setVisibleDates(initialDates);
}, [startDate]);
useEffect(() => {
if (typeof window !== 'undefined') {
const observer = new IntersectionObserver(
entries => {
let tempFirstMonthVisible = entries[0].target.getAttribute('value');
let tempSecondMonthVisible =
entries[entries.length - 1].target.getAttribute('value');
if (tempFirstMonthVisible === tempSecondMonthVisible) {
setFirstMonthVisible(tempFirstMonthVisible);
setSecondMonthVisible('');
} else {
setFirstMonthVisible(tempFirstMonthVisible);
setSecondMonthVisible(tempSecondMonthVisible);
}
},
{ threshold: 0.5 }
);
const cards = document.querySelectorAll('.calendar_strip_card');
cards.forEach(card => {
observer.observe(card);
});
return () => {
cards.forEach(card => {
observer.unobserve(card);
});
};
}
}, [visibleDates]);
return {
handlePrevious,
handleNext,
visibleDates,
scrollRef,
firstMonthVisible,
secondMonthVisible,
};
};
export default CalendarLogic;