fix(datepicker): keep the mobile overlay attached after boosted navigation

On touch devices AirDatepicker opens pickers in mobile mode, which closes
them through a shared overlay rather than on blur. The library keeps that
overlay in its module scope, inside a global container appended to the
body. Boosted navigation swaps the body, so the container and overlay are
removed: the library notices the missing container and builds a new one
for the next picker, but keeps reusing the detached overlay. From then on
nothing receives the outside click, and an open picker stays on screen
over the form until the page is fully reloaded.

Create every picker through a helper that remembers the overlay and moves
it back into the current global container whenever it was left detached.

Fixes #227
This commit is contained in:
Asish Kumar committed 2026-11-07 14:45:49 +05:30
1 parent c05bdcf2bf
commit 9f4ea846fd
1 file changed
+18 -3
+18 -3
View File
@@ -108,6 +108,21 @@ function isMobile() {
return isMobileDevice() || isTouchDevice();
}
// AirDatepicker creates a single overlay for all mobile pickers and keeps it in
// its module scope, inside a global container on the body. A boosted navigation
// swaps the body: AirDatepicker then rebuilds the container, but keeps the old
// overlay, which is left detached and can't receive the clicks that close a picker.
let mobileOverlay = null;
function createAirDatepicker(element, opts) {
const datepicker = new AirDatepicker(element, opts);
mobileOverlay ??= document.querySelector('.air-datepicker-overlay');
if (mobileOverlay && !mobileOverlay.isConnected) {
document.getElementById(AirDatepicker.defaultGlobalContainerId).appendChild(mobileOverlay);
}
return datepicker;
}
window.DatePicker = async function createDynamicDatePicker(element) {
let todayButton = {
content: element.dataset.nowButtonTxt,
@@ -182,7 +197,7 @@ window.DatePicker = async function createDynamicDatePicker(element) {
opts["selectedDates"] = [element.dataset.value];
opts["startDate"] = [element.dataset.value];
}
return new AirDatepicker(element, opts);
return createAirDatepicker(element, opts);
};
window.MonthYearPicker = async function createDynamicDatePicker(element) {
@@ -258,7 +273,7 @@ window.MonthYearPicker = async function createDynamicDatePicker(element) {
opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];
}
return new AirDatepicker(element, opts);
return createAirDatepicker(element, opts);
};
window.YearPicker = async function createDynamicDatePicker(element) {
@@ -334,5 +349,5 @@ window.YearPicker = async function createDynamicDatePicker(element) {
opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];
}
return new AirDatepicker(element, opts);
return createAirDatepicker(element, opts);
};