MFCalendarPopup / beta / js / mf-calendar-popup.js file last updated : 2024-07-04 |
---|
/*! mf-calendar-popup.js (@ https://methodfish.com/Projects/MFCalendarPopup)
* Version: 1.0
* Licence: MIT
*/
// Calendar popup
// getPopup(document.querySelector('button'), new Date('2024-05-08'), setDate);
//
// function setDate(selectedDt) {
// alert('Set to '+selectedDt);
// }
//
let _mfCalendarPopupEvents=false;
function MFCalendarPopup() {
const _today = new Date();
var _selectedYearN, _selectedMonthN, _selectedDayN;
let _popup;
function getPopup(elem, selectedDt, cb) {
function generateDaysGrid(year, month) {
let parts = document.querySelector('#cal-dt').value.split('-');
_selectedYearN = parts[0];
_selectedMonthN = parts[1];
_selectedDayN = parts[2];
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');
if ( year==_selectedYearN && month==_selectedMonthN && day==_selectedDayN ) dayCell.classList.add('selectedDate');
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 removePX(x){ // Return "a" from inside "apx"
if ( x=='' ) return parseInt(0);
var x2=''+x;
return parseInt(x2.replace(/px/, ''),10);
}
function isnull(tx) {
if ( ! tx ) return "[null]";
return tx;
}
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
function getComputedStyleX(elem,attr,doRemovePX,nulls) {
if ( nulls==undefined ) nulls='[null]';
if ( doRemovePX==undefined ) doRemovePX=false;
if ( typeof elem==='string' ) {
let elems = document.querySelectorAll(elem);
for(let i=elems.length-1; i>=0; i-- ) getComputedStyleX(elems[i],attr,doRemovePX,nulls);
return;
}
if ( !elem ) return "Null given in getComputedStyleX(NULL,'"+attr+"')";
var style = window.getComputedStyle(elem);
var result = style.getPropertyValue(attr);
if ( doRemovePX ) {
var r = parseFloat(removePX(result));
if (!r) return nulls;
else return r;
}
return isnull(result);
}
function getMaxZIndex() {
var elems= document.getElementsByTagName('div');
var max=0;
for(var i=0;i<elems.length;i++) {
var t=getComputedStyleX(elems[i],'z-index'); // elems[i].style.zIndex;
if ( t!='auto' ) {
if (isNumeric(t) && parseInt(t) > max) {
//console.log('16249:'+elems[i].className+' ('+elems[i].id+')='+t);
max = parseInt(t);
}
}
}
var elems=document.getElementsByTagName('canvas');
for(var i=0;i<elems.length;i++) {
var t= getComputedStyleX(elems[i],'z-index'); // elems[i].style.zIndex;
if ( isNumeric(t) && parseInt(t)>max ) {
max = parseInt(t);
}
}
return parseInt(max);
}
function moveDivOnscreen(elem, container) {
// Get the dimensions of the viewport
let viewportWidth, viewportHeight, containerLeft = 0, containerTop = 0, containerScrollLeft = 0, containerScrollTop = 0;
if (container === undefined) container = window;
if (container === window) {
viewportWidth = window.innerWidth - 30;
viewportHeight = window.innerHeight - 30;
containerScrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
containerScrollTop = (window.pageYOffset || document.documentElement.scrollTop);
} else {
const containerRect = container.getBoundingClientRect();
containerLeft = containerRect.left;
containerTop = containerRect.top;
viewportWidth = containerRect.width - 30;
viewportHeight = containerRect.height - 30;
containerScrollLeft = container.scrollLeft;
containerScrollTop = container.scrollTop;
}
// 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 + containerScrollTop;
let newLeft = elemRect.left + containerScrollLeft;
// Check if the element would exceed the viewport width
if ((elemRect.right - containerLeft) > viewportWidth) {
newLeft -= (elemRect.right - containerLeft - viewportWidth);
}
// Check if the element would exceed the viewport height
if (elemRect.bottom > viewportHeight + containerTop) {
newTop -= (elemRect.bottom - viewportHeight - containerTop);
}
// Set the new position of the element
elem.style.position = 'absolute';
elem.style.top = newTop + 'px';
elem.style.left = newLeft + 'px';
// Ensure the element doesn't go off the left side of the viewport
if (elem.getBoundingClientRect().left < 5) {
elem.style.left = (5 + containerScrollLeft) + 'px';
}
}
// 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 = selectedDt.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 = selectedDt.getFullYear();
yearSelect.addEventListener('change', () => {
generateDaysGrid(parseInt(yearSelect.value), parseInt(monthSelect.value));
}, _selectedYearN, _selectedMonthN, _selectedDayN);
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);
const hidden = document.createElement('input');
hidden.type='hidden';
hidden.id = 'cal-dt';
hidden.value = _selectedYearN+'-'+_selectedMonthN+'-'+_selectedDayN;
row1.appendChild(hidden);
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;
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');
_popup.querySelector('.month').value = _selectedMonthN;
_popup.querySelector('.year').value = _selectedYearN;
let hidden = document.querySelector('#cal-dt');
hidden.value = _selectedYearN+'-'+_selectedMonthN+'-'+_selectedDayN;
}
_popup.style.zIndex = getMaxZIndex()+1;
generateDaysGrid(_selectedYearN, _selectedMonthN);
const rect = elem.getBoundingClientRect();
_popup.style.top = `${rect.bottom}px`;
_popup.style.left = `${rect.left}px`;
moveDivOnscreen(_popup, undefined, elem);
if ( !_mfCalendarPopupEvents ) {
_mfCalendarPopupEvents = true;
window.addEventListener('resize', function() {
hidePopup(); // _popup.style.display = 'none';
});
window.addEventListener('scroll', function() {
hidePopup(); // _popup.style.display = 'none';
});
}
}
// Function to show the popup each time it's called
this.getDate = function(elem, selectedDt, cb) {
if (!_popup || _popup.style.display === 'none') {
_selectedYearN = parseInt(selectedDt.getFullYear());
_selectedMonthN = parseInt(selectedDt.getMonth()); // 0 - 11
_selectedDayN = parseInt(selectedDt.getDate());
getPopup(elem, selectedDt, cb);
} else {
_popup.style.display = 'block';
}
return _popup;
};
this.removePopup = function() {
var element = document.querySelector('.mf-calendar-popup');
if (element) {
element.remove();
}
_popup = null;
}
}
Also see
articles/Implementing-a-Monaco-Editorsquarearticles/iConnectionTestsquarearticles/javascript-camerasquarearticles/list-editsquarearticles/minifierssquarearticles/opayosquarearticles/table-drag-sortersquarearticles/typewatchsquareprojects/MFCalendarPopupsquareprojects/MFChartColumnsquareprojects/MFColorPickersquareprojects/MFColorPickerBasicsquareprojects/MFColumnGradientsquareprojects/MFFloatawayMsgsquareprojects/MFPanelssquareprojects/MFSelectorsquare
Comments
New Comment