fix: datepicker doesn't recalculate position when changing view mode

This commit is contained in:
Herculino Trotta
2025-12-19 22:21:03 -03:00
parent 3dce9e1c55
commit 94f5c25829

View File

@@ -1,5 +1,5 @@
import AirDatepicker from 'air-datepicker'; import AirDatepicker from 'air-datepicker';
import {createPopper} from '@popperjs/core'; import { createPopper } from '@popperjs/core';
import '../styles/_datepicker.scss' import '../styles/_datepicker.scss'
// --- Static Locale Imports --- // --- Static Locale Imports ---
@@ -40,58 +40,58 @@ import localeZh from 'air-datepicker/locale/zh.js';
// Map language codes to their imported locale objects // Map language codes to their imported locale objects
const allLocales = { const allLocales = {
'ar': localeAr, 'ar': localeAr,
'bg': localeBg, 'bg': localeBg,
'ca': localeCa, 'ca': localeCa,
'cs': localeCs, 'cs': localeCs,
'da': localeDa, 'da': localeDa,
'de': localeDe, 'de': localeDe,
'el': localeEl, 'el': localeEl,
'en': localeEn, 'en': localeEn,
'es': localeEs, 'es': localeEs,
'eu': localeEu, 'eu': localeEu,
'fi': localeFi, 'fi': localeFi,
'fr': localeFr, 'fr': localeFr,
'hr': localeHr, 'hr': localeHr,
'hu': localeHu, 'hu': localeHu,
'id': localeId, 'id': localeId,
'it': localeIt, 'it': localeIt,
'ja': localeJa, 'ja': localeJa,
'ko': localeKo, 'ko': localeKo,
'nb': localeNb, 'nb': localeNb,
'nl': localeNl, 'nl': localeNl,
'pl': localePl, 'pl': localePl,
'pt-BR': localePtBr, 'pt-BR': localePtBr,
'pt': localePt, 'pt': localePt,
'ro': localeRo, 'ro': localeRo,
'ru': localeRu, 'ru': localeRu,
'si': localeSi, 'si': localeSi,
'sk': localeSk, 'sk': localeSk,
'sl': localeSl, 'sl': localeSl,
'sv': localeSv, 'sv': localeSv,
'th': localeTh, 'th': localeTh,
'tr': localeTr, 'tr': localeTr,
'uk': localeUk, 'uk': localeUk,
'zh': localeZh 'zh': localeZh
}; };
// --- End of Locale Imports --- // --- End of Locale Imports ---
/** /**
 * Selects a pre-imported language file from the locale map. * Selects a pre-imported language file from the locale map.
 * *
 * @param {string} langCode - The two-letter language code (e.g., 'en', 'es'). * @param {string} langCode - The two-letter language code (e.g., 'en', 'es').
 * @returns {Promise<object>} A promise that resolves with the locale object. * @returns {Promise<object>} A promise that resolves with the locale object.
 */ */
export const getLocale = async (langCode) => { export const getLocale = async (langCode) => {
const locale = allLocales[langCode]; const locale = allLocales[langCode];
if (locale) { if (locale) {
return locale; return locale;
} }
console.warn(`Could not find locale for '${langCode}'. Defaulting to English.`); console.warn(`Could not find locale for '${langCode}'. Defaulting to English.`);
return allLocales['en']; // Default to English return allLocales['en']; // Default to English
}; };
function isMobileDevice() { function isMobileDevice() {
@@ -112,7 +112,7 @@ window.DatePicker = async function createDynamicDatePicker(element) {
content: element.dataset.nowButtonTxt, content: element.dataset.nowButtonTxt,
onClick: (dp) => { onClick: (dp) => {
let date = new Date(); let date = new Date();
dp.selectDate(date, {updateTime: true}); dp.selectDate(date, { updateTime: true });
dp.setViewDate(date); dp.setViewDate(date);
} }
}; };
@@ -126,16 +126,18 @@ window.DatePicker = async function createDynamicDatePicker(element) {
autoClose: element.dataset.autoClose === 'true', autoClose: element.dataset.autoClose === 'true',
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton], buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
locale: await getLocale(element.dataset.language), locale: await getLocale(element.dataset.language),
onSelect: ({date, formattedDate, datepicker}) => { onSelect: ({ date, formattedDate, datepicker }) => {
const _event = new CustomEvent("change", { const _event = new CustomEvent("change", {
bubbles: true, bubbles: true,
}); });
datepicker.$el.dispatchEvent(_event); datepicker.$el.dispatchEvent(_event);
} }
}; };
// Store popper instance for updating on view changes
let popperInstance = null;
const positionConfig = !isOnMobile ? { const positionConfig = !isOnMobile ? {
position({$datepicker, $target, $pointer, done}) { position({ $datepicker, $target, $pointer, done }) {
let popper = createPopper($target, $datepicker, { popperInstance = createPopper($target, $datepicker, {
placement: 'bottom', placement: 'bottom',
modifiers: [ modifiers: [
{ {
@@ -157,16 +159,24 @@ window.DatePicker = async function createDynamicDatePicker(element) {
options: { options: {
element: $pointer element: $pointer
} }
} }
] ]
}); });
return function completeHide() { return function completeHide() {
popper.destroy(); popperInstance.destroy();
popperInstance = null;
done(); done();
}; };
},
onChangeView() {
// Update popper position when view changes (e.g., clicking year)
// Use setTimeout to allow the DOM to update before recalculating
if (popperInstance) {
setTimeout(() => popperInstance.update(), 0);
}
} }
} : {}; } : {};
let opts = {...baseOpts, ...positionConfig}; let opts = { ...baseOpts, ...positionConfig };
if (element.dataset.value) { if (element.dataset.value) {
opts["selectedDates"] = [element.dataset.value]; opts["selectedDates"] = [element.dataset.value];
opts["startDate"] = [element.dataset.value]; opts["startDate"] = [element.dataset.value];
@@ -179,7 +189,7 @@ window.MonthYearPicker = async function createDynamicDatePicker(element) {
content: element.dataset.nowButtonTxt, content: element.dataset.nowButtonTxt,
onClick: (dp) => { onClick: (dp) => {
let date = new Date(); let date = new Date();
dp.selectDate(date, {updateTime: true}); dp.selectDate(date, { updateTime: true });
dp.setViewDate(date); dp.setViewDate(date);
} }
}; };
@@ -193,16 +203,18 @@ window.MonthYearPicker = async function createDynamicDatePicker(element) {
autoClose: element.dataset.autoClose === 'true', autoClose: element.dataset.autoClose === 'true',
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton], buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
locale: await getLocale(element.dataset.language), locale: await getLocale(element.dataset.language),
onSelect: ({date, formattedDate, datepicker}) => { onSelect: ({ date, formattedDate, datepicker }) => {
const _event = new CustomEvent("change", { const _event = new CustomEvent("change", {
bubbles: true, bubbles: true,
}); });
datepicker.$el.dispatchEvent(_event); datepicker.$el.dispatchEvent(_event);
} }
}; };
// Store popper instance for updating on view changes
let popperInstance = null;
const positionConfig = !isOnMobile ? { const positionConfig = !isOnMobile ? {
position({$datepicker, $target, $pointer, done}) { position({ $datepicker, $target, $pointer, done }) {
let popper = createPopper($target, $datepicker, { popperInstance = createPopper($target, $datepicker, {
placement: 'bottom', placement: 'bottom',
modifiers: [ modifiers: [
{ {
@@ -228,12 +240,19 @@ window.MonthYearPicker = async function createDynamicDatePicker(element) {
] ]
}); });
return function completeHide() { return function completeHide() {
popper.destroy(); popperInstance.destroy();
popperInstance = null;
done(); done();
}; };
},
onChangeView() {
// Update popper position when view changes (e.g., clicking year)
if (popperInstance) {
setTimeout(() => popperInstance.update(), 0);
}
} }
} : {}; } : {};
let opts = {...baseOpts, ...positionConfig}; let opts = { ...baseOpts, ...positionConfig };
if (element.dataset.value) { if (element.dataset.value) {
opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")]; opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")]; opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];
@@ -246,7 +265,7 @@ window.YearPicker = async function createDynamicDatePicker(element) {
content: element.dataset.nowButtonTxt, content: element.dataset.nowButtonTxt,
onClick: (dp) => { onClick: (dp) => {
let date = new Date(); let date = new Date();
dp.selectDate(date, {updateTime: true}); dp.selectDate(date, { updateTime: true });
dp.setViewDate(date); dp.setViewDate(date);
} }
}; };
@@ -260,16 +279,18 @@ window.YearPicker = async function createDynamicDatePicker(element) {
autoClose: element.dataset.autoClose === 'true', autoClose: element.dataset.autoClose === 'true',
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton], buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
locale: await getLocale(element.dataset.language), locale: await getLocale(element.dataset.language),
onSelect: ({date, formattedDate, datepicker}) => { onSelect: ({ date, formattedDate, datepicker }) => {
const _event = new CustomEvent("change", { const _event = new CustomEvent("change", {
bubbles: true, bubbles: true,
}); });
datepicker.$el.dispatchEvent(_event); datepicker.$el.dispatchEvent(_event);
} }
}; };
// Store popper instance for updating on view changes
let popperInstance = null;
const positionConfig = !isOnMobile ? { const positionConfig = !isOnMobile ? {
position({$datepicker, $target, $pointer, done}) { position({ $datepicker, $target, $pointer, done }) {
let popper = createPopper($target, $datepicker, { popperInstance = createPopper($target, $datepicker, {
placement: 'bottom', placement: 'bottom',
modifiers: [ modifiers: [
{ {
@@ -295,12 +316,19 @@ window.YearPicker = async function createDynamicDatePicker(element) {
] ]
}); });
return function completeHide() { return function completeHide() {
popper.destroy(); popperInstance.destroy();
popperInstance = null;
done(); done();
}; };
},
onChangeView() {
// Update popper position when view changes (e.g., clicking year)
if (popperInstance) {
setTimeout(() => popperInstance.update(), 0);
}
} }
} : {}; } : {};
let opts = {...baseOpts, ...positionConfig}; let opts = { ...baseOpts, ...positionConfig };
if (element.dataset.value) { if (element.dataset.value) {
opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")]; opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")]; opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];