Open uvarov-frontend opened 4 months ago
Hi @uvarov-frontend,
I encountered an issue where the entire calendar (not just a popup) was overflowing outside the body container at a specific breakpoint (1070px).
I tried fixing it with CSS initially, but wasn't successful. I then implemented a JavaScript solution that resolved the problem.
See the original issue in this example: website (1070px breakpoint).
Here's the fixed version for your reference: website
Here is the JavaScript code I used for the fix.
const options = {
input: true,
actions: {
changeToInput(e, calendar, self) {
if (!self.HTMLInputElement) return;
if (self.selectedDates[0]) {
self.HTMLInputElement.innerHTML = self.selectedDates[0];
// if you want to hide the calendar after picking a date
calendar.hide();
} else {
self.HTMLInputElement.innerHTML = "Select Date";
}
},
showCalendar(self) {
const setPositionCalendar = (
input,
calendar,
position,
css,
) => {
const getPosition = {
top: -calendar.offsetHeight,
bottom: input.offsetHeight,
left: 0,
center: input.offsetWidth / 2 - calendar.offsetWidth / 2,
right: input.offsetWidth - calendar.offsetWidth,
};
const YPosition = !Array.isArray(position)
? "bottom"
: position[0];
const XPosition = !Array.isArray(position)
? position
: position[1];
let left = input.offsetLeft;
let top = input.offsetTop;
// Calculate document dimensions
const docWidth = document.documentElement.clientWidth;
const docHeight = document.documentElement.clientHeight;
// Calculate window scroll offsets
const scrollLeft =
window.scrollX || document.documentElement.scrollLeft;
const scrollTop =
window.scrollY || document.documentElement.scrollTop;
// Calculate calendar width and set maximum width to 100%
const calendarWidth = Math.min(
calendar.offsetWidth,
docWidth,
);
// Check if there's horizontal overflow
const rightOverflow =
left + calendarWidth > docWidth + scrollLeft;
const leftOverflow = left < scrollLeft;
// Check if there's vertical overflow
const verticalOverflow =
(YPosition === "bottom" &&
top + input.offsetHeight + calendar.offsetHeight >
docHeight + scrollTop) ||
(YPosition === "top" &&
top - calendar.offsetHeight < scrollTop);
// Adjust positions accordingly
if (rightOverflow && XPosition !== "left") {
left = input.offsetLeft + input.offsetWidth - calendarWidth;
} else if (leftOverflow && XPosition !== "right") {
left = input.offsetLeft;
}
// Center the calendar if the left or right value is negative
if (left < 0 || left + calendarWidth > docWidth) {
left =
document.documentElement.offsetLeft +
(document.documentElement.offsetWidth - calendarWidth) /
2;
}
if (verticalOverflow) {
top =
YPosition === "bottom"
? input.offsetTop - calendar.offsetHeight
: input.offsetTop + input.offsetHeight;
} else {
top =
YPosition === "bottom"
? top + input.offsetHeight
: top - calendar.offsetHeight;
}
calendar.classList.add(
YPosition === "bottom"
? css.calendarToInputBottom
: css.calendarToInputTop,
);
Object.assign(calendar.style, {
left: `${left}px`,
top: `${top}px`,
maxWidth: "100%",
});
};
setPositionCalendar(
self.HTMLInputElement,
self.HTMLElement,
"auto",
self.CSSClasses,
);
const actionsInput = (self) => ({
hide() {
self.HTMLElement.classList.add(
self.CSSClasses.calendarHidden,
);
if (self.actions.hideCalendar)
self.actions.hideCalendar(self);
},
show() {
self.HTMLElement.classList.remove(
self.CSSClasses.calendarHidden,
);
if (self.actions.showCalendar)
self.actions.showCalendar(self);
},
self,
});
const documentClickEvent = (e) => {
if (
!self ||
e.target === self.HTMLInputElement ||
self.HTMLElement?.contains(e.target)
)
return;
if (self.HTMLInputElement && self.HTMLElement)
actionsInput(self).hide();
window.removeEventListener("resize", handleResize);
document.removeEventListener("click", documentClickEvent, {
capture: true,
});
};
const handleResize = () =>
setPositionCalendar(
self.HTMLInputElement,
self.HTMLElement,
"auto",
self.CSSClasses,
);
self.HTMLInputElement.addEventListener("click", () => {
window.addEventListener("resize", handleResize);
document.addEventListener("click", documentClickEvent, {
capture: true,
});
});
document.addEventListener("click", documentClickEvent, {
capture: true,
});
window.addEventListener("resize", handleResize);
window.addEventListener("scroll", handleResize);
},
},
Probably easier to use Floating UI for positioning.
@tfsumon Thanks for sharing, I’ll think about making the calendar auto-position depending on the breakpoint. But it was easier for you to use Floating UI, as already written above.
I have the code for auto-positioning in a fork, I can submit a PR for auto-positioning after all my other PRs are reviewed/merged :)
I also assume that my code could help with the original issue since I have a new function that given an element, will calculate the best possible side to reposition itself (by available space depending on element's position in the viewport).
Cheers
Discussed in https://github.com/uvarov-frontend/vanilla-calendar-pro/discussions/176