vanilla-calendar-pro
vanilla-calendar-pro copied to clipboard
Responsive Layout - pop-up negatively interfering
Discussed in https://github.com/uvarov-frontend/vanilla-calendar-pro/discussions/176
Originally posted by pablopereira27 December 30, 2023 For small screens (Boostrap sm ceil breakpoint = 767.98px), I applied the following change to the multiple month calendar.
@media (max-width: 767.98px) { .vanilla-calendar_multiple { width: 100%; max-width: 550px; } }
If it weren't for the pop-ups that can go out the calendar, it would be great.
overflow: hidden
in the .calendar
prevents the entire site from being affected, but prevents the user from seeing pop-ups that exceed the calendar.
I couldn't find a way to solve it via CSS. Could you help me with any tips or any future adjustments to the calendar javascript to avoid this leak of calendar pop-ups?
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
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
Your PRs are merged, thank you for your help in supporting the project!
So after creating new open PR #278, I started to play with that auto-position code to see if I can fix the issue mentioned here with the pop-ups and I'm getting close to a solution to auto-position not just the calendar picker but also these pop-ups. However 1 noticeable problem that I see with my fix is that there's an arrow in the top center position of the pop-up and that middle arrow becomes quite offset with the day element that we're hovering when it is used with my auto-position fix.
The question though is, do we really need this arrow pointer? I would personally just go without it. I think that if we provide this auto-position of the pop-up, we should maybe disable or remove this pop-up arrow pointer completely. @uvarov-frontend what do you think I should do? Perhaps, an extra CSS class like .with-arrow
to go with/without the arrow. But anyway, I can't proceed without PR #278 to be reviewed first.
The arrow CSS positioned exactly set in the middle via this piece of code
https://github.com/uvarov-frontend/vanilla-calendar-pro/blob/501f311faf141414acfd7f9bf17ea52490a15dff/package/src/styles/vanilla-calendar.layout.css#L232-L236
As you can see below, my auto-position of the pop-up seems to work well, except for that arrow that no longer pointing to the correct position.
and just for comparison, below is without the auto-positioning fix that I'm working on
So after creating new open PR #278, I started to play with that auto-position code to see if I can fix the issue mentioned here with the pop-ups and I'm getting close to a solution to auto-position not just the calendar picker but also these pop-ups. However 1 noticeable problem that I see with my fix is that there's an arrow in the top center position of the pop-up and that middle arrow becomes quite offset with the day element that we're hovering when it is used with my auto-position fix.
The question though is, do we really need this arrow pointer? I would personally just go without it. I think that if we provide this auto-position of the pop-up, we should maybe disable or remove this pop-up arrow pointer completely. @uvarov-frontend what do you think I should do? Perhaps, an extra CSS class like
.with-arrow
to go with/without the arrow. But anyway, I can't proceed without PR #278 to be reviewed first.The arrow CSS positioned exactly set in the middle via this piece of code
https://github.com/uvarov-frontend/vanilla-calendar-pro/blob/501f311faf141414acfd7f9bf17ea52490a15dff/package/src/styles/vanilla-calendar.layout.css#L232-L236
As you can see below, my auto-position of the pop-up seems to work well, except for that arrow that no longer pointing to the correct position.
Need to remove the arrow if the window is auto-positioned, or offset this arrow. Add a PR as it was done in the first animation, I will add a couple of commits for this arrow.