MFCalendarPopup / 1.0 / js / mf-calendar-popup.js
file last updated : 2024-05-09
/*! methodfish package release

 Project      : MFCalendarPopup
 Release      : 1.0
 Release date : 2024-05-09 18:50:52
 License      : {{warning}}tba
 For more information please refer to https://methodfish.com/Projects/MFCalendarPopup

*/


// Calendar popup, triggered by calling: 
// new MFCalendarPopup().getDate(e.target, new Date(), setDate);
//
// function setDate(dt) {
//     alert('Set to '+dt);
// }
//
let _mfCalendarPopupEvents=false;
function MFCalendarPopup() {

    const _today = new Date();
    let _popup;

    function getPopup(elem, dt, cb) {

        function generateDaysGrid(year, month) {
            daysContainer.innerHTML = '';
            const daysInMonth = new Date(year, month + 1, 0).getDate();
            const firstDayOfMonth = new Date(year, month, 1).getDay();
            for (let i = 0; i < firstDayOfMonth; i++) {
                const emptyCell = document.createElement('div');
                emptyCell.classList.add('day');
                daysContainer.appendChild(emptyCell);
            }

            for (let day = 1; day <= daysInMonth; day++) {
                const dayCell = document.createElement('div');
                dayCell.classList.add('day');
                dayCell.textContent = day;
                if (year === _today.getFullYear() && month === _today.getMonth() && day === _today.getDate()) {
                    dayCell.classList.add('today');
                }
                const currentDate = new Date(year, month, day);
                if (currentDate.getDay() === 0 || currentDate.getDay() === 6) {
                    dayCell.classList.add('weekend'); // Add 'weekend' class for Saturday and Sunday
                }
                dayCell.addEventListener('click', () => selectDate(year, month, day));
                daysContainer.appendChild(dayCell);
            }

        }

        
        function selectDate(year, month, day) {
            const selectedDt = new Date(year, month, day);
            cb(selectedDt);
            hidePopup();
        }

        
        function hidePopup() {
            _popup.style.display = 'none';
        }


        function updateYearList(yearSelect, selectedYear) {
            yearSelect.innerHTML = '';
            const currentYear = selectedYear;
            for (let i = currentYear - 50; i <= currentYear + 50; i++) {
                const option = document.createElement('option');
                option.value = i;
                option.textContent = i;
                if ( i === selectedYear ) {
                    option.selected = true;
                }
                yearSelect.appendChild(option);
            }
        }


        function moveDivOnscreen(elem, container) {
            // Get the dimensions of the viewport

            let viewportWidth;
            let containerLeft = 0;
            if ( container==undefined ) container = window;
            if (container === window) viewportWidth = window.innerWidth - 30;
            else {
                containerLeft = container.getBoundingClientRect().left;
                viewportWidth = container.getBoundingClientRect().width - 30;
            }

            let viewportHeight;
            if (container === window) viewportHeight = window.innerHeight - 30;
            else viewportHeight = container.getBoundingClientRect().height - 30;

            // Get the bounding rectangle of the element
            const elemRect = elem.getBoundingClientRect();

            // Calculate the desired position of the element relative to the viewport
            let newTop  = elemRect.top; // Maintain the current top position
            let newLeft = elemRect.left; // Maintain the current left position

            // Check if the element would exceed the viewport width
            if ((elemRect.right - containerLeft) > viewportWidth) {
                //console.log('moving from ' + newLeft);
                // Adjust the left position to ensure the element stays within the viewport
                newLeft -= (elemRect.right - containerLeft - viewportWidth);
                //console.log('moved to ' + newLeft);
            }

            // Check if the element would exceed the viewport height
            if (elemRect.bottom > viewportHeight) {
                // Adjust the top position to ensure the element stays within the viewport
                newTop -= (elemRect.bottom - viewportHeight);
            }

            // Set the new position of the element
            elem.style.top  = newTop + 'px';
            elem.style.left = +newLeft + 'px';
            if ( elem.getBoundingClientRect().left < 5 ) elem.style.left='5px';
        }


        // Create popup container
        let daysContainer;
        _popup = document.querySelector('.mf-calendar-popup');
        if ( !_popup ) {
            _popup = document.createElement('div');
            _popup.classList.add('mf-calendar-popup');
            _popup.classList.add('removeOnEscape');

            // Create header container
            const header = document.createElement('div');
            if ( 1 ) {
                header.classList.add('header');


                const row1        = document.createElement('div');

                
                // Create hide button
                const hideBtn       = document.createElement('i');
                hideBtn.textContent = 'close';
                hideBtn.classList.add('material-icons');
                hideBtn.classList.add('hideBtn');
                hideBtn.addEventListener('click', hidePopup);
                row1.appendChild(hideBtn);


                // Create previous month button
                const prevMonthBtn       = document.createElement('i');
                prevMonthBtn.classList.add('prevMonthBtn');
                prevMonthBtn.classList.add('material-icons');
                prevMonthBtn.textContent = 'navigate_before';
                prevMonthBtn.addEventListener('click', () => {
                    const selectedMonth = parseInt(monthSelect.value);
                    if (selectedMonth > 0) {
                        monthSelect.selectedIndex = selectedMonth - 1;
                    }
                    else {
                        monthSelect.selectedIndex = 11; // December
                        yearSelect.value          = parseInt(yearSelect.value) - 1;
                    }
                    generateDaysGrid(parseInt(yearSelect.value), parseInt(monthSelect.value));
                });
                row1.appendChild(prevMonthBtn);


                // Create month selector
                const monthSelect = document.createElement('select');
                monthSelect.classList.add('month');
                const months      = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
                months.forEach((month, index) => {
                    const option       = document.createElement('option');
                    option.value       = index;
                    option.textContent = month;
                    monthSelect.appendChild(option);
                });
                monthSelect.selectedIndex = dt.getMonth();
                monthSelect.addEventListener('change', () => {
                    generateDaysGrid(parseInt(yearSelect.value), parseInt(monthSelect.value));
                });
                row1.appendChild(monthSelect);


                // Create year selector
                const yearSelect  = document.createElement('select');
                yearSelect.classList.add('year');
                const currentYear = new Date().getFullYear();
                for (let i = currentYear - 50; i <= currentYear + 50; i++) {
                    const option       = document.createElement('option');
                    option.value       = i;
                    option.textContent = i;
                    yearSelect.appendChild(option);
                }
                yearSelect.value = dt.getFullYear();
                yearSelect.addEventListener('change', () => {
                    generateDaysGrid(parseInt(yearSelect.value), parseInt(monthSelect.value));
                });
                row1.appendChild(yearSelect);
                yearSelect.addEventListener('change', function() {
                    let selectedYear = parseInt(yearSelect.value); // Update the selected year
                    updateYearList(yearSelect, selectedYear);
                });

                
                // Create next month button
                const nextMonthBtn       = document.createElement('i');
                nextMonthBtn.classList.add('nextMonthBtn');
                nextMonthBtn.classList.add('material-icons');
                nextMonthBtn.textContent = 'navigate_next';
                nextMonthBtn.addEventListener('click', () => {
                    const selectedMonth = parseInt(monthSelect.value);
                    if (selectedMonth < 11) {
                        monthSelect.selectedIndex = selectedMonth + 1;
                    }
                    else {
                        monthSelect.selectedIndex = 0; // January
                        yearSelect.value          = parseInt(yearSelect.value) + 1;
                    }
                    generateDaysGrid(parseInt(yearSelect.value), parseInt(monthSelect.value));
                });
                row1.appendChild(nextMonthBtn);


                header.appendChild(row1);

                
                // Append days of the week
                let dayNamesContainer = document.createElement('div');
                dayNamesContainer.classList.add('header');
                const daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
                daysOfWeek.forEach(day => {
                    const dayName       = document.createElement('div');
                    dayName.textContent = day;
                    console.log(day);
                    dayName.classList.add('day-name');
                    if (day == 'Sat' || day == 'Sun') dayName.classList.add('weekend');
                    dayNamesContainer.appendChild(dayName);
                });

                _popup.appendChild(header);
                _popup.appendChild(dayNamesContainer);

            }
            
            daysContainer = document.createElement('div');
            daysContainer.classList.add('days');
            _popup.appendChild(daysContainer);
            
            document.body.appendChild(_popup);
        }
        else {
            _popup.style.display='';
            daysContainer = _popup.querySelector('.days');
        }
        
        generateDaysGrid(parseInt(dt.getFullYear()), parseInt(dt.getMonth()));

        
        const rect = elem.getBoundingClientRect();
        _popup.style.top = `${rect.bottom}px`;
        _popup.style.left = `${rect.left}px`;

        moveDivOnscreen(_popup);

        if ( !_mfCalendarPopupEvents ) {
            _mfCalendarPopupEvents = true;
            window.addEventListener('resize', function() {
                _popup.style.display = 'none';
            })
        }
    }



    // Function to show the popup each time it's called
    this.getDate = function(elem, dt, cb) {
        if (!_popup || _popup.style.display === 'none') {
            getPopup(elem, dt, cb);
        } else {
            _popup.style.display = 'block';
        }
    };

}

About

License

Latest Release

Version 1.022024-07-04