/** * Calendar Script * Creates a calendar widget which can be used to select the date more easily than using just a text box * * (c) Vitaliy Filippov 2018+ * Repository: http://yourcmc.ru/git/vitalif-js/calendar * Version: 2021-09-19 * License: Dual-license MPL 2.0+ or GNU LGPL 3.0+ * * Example: * * */ import ReactCalendar from './calendar-react.js'; import preact from 'preact'; /** @jsx preact.h */ export class Calendar extends ReactCalendar { componentDidMount() { this.componentDidUpdate(); } componentDidUpdate() { // Position the div in the correct location... var input = this.props.input; var div = Calendar.box; div.style.position = "absolute"; var xy = getOffset(input); var height = input.clientHeight||input.offsetHeight; var ww = document.body.clientWidth||document.documentElement.clientWidth; var wh = document.body.clientHeight||document.documentElement.clientHeight; if (xy.left-1+div.offsetWidth > ww) div.style.left = (ww-div.offsetWidth-1)+"px"; else div.style.left = (xy.left-1)+"px"; if (div.offsetHeight + xy.top+height-1 >= wh && xy.top-div.offsetHeight >= 0) div.style.top = (xy.top-div.offsetHeight)+'px'; else div.style.top = (xy.top+height-1)+"px"; } static onBlur() { if (!Calendar.stopBlur || Calendar.stopBlur < Date.now()-200) Calendar.hideCalendar(); } /// Called when the user clicks on a date in the calendar. static onChange(i, date) { i.value = date; if ("Event" in window) { var evt = document.createEvent('Event'); evt.initEvent('change', true, true); i.dispatchEvent(evt); } else i.fireEvent("onchange"); Calendar.hideCalendar(); } /// Display the calendar - if a date exists in the input box, that will be selected in the calendar. static showCalendar(input, options) { // Show the calendar with the date in the input as the selected date const props = { ...options, input, value: input.value, hide: Calendar.hideCalendar, onChange: date => Calendar.onChange(input, date), }; Calendar.init(); Calendar.box.style.display = "block"; Calendar.stopBlur = Date.now(); preact.render(, Calendar.box.parentNode, Calendar.box); } /// Hides the currently shown calendar. static hideCalendar() { if (!Calendar.box) return; Calendar.box.style.display = "none"; } /// Setup a text input box to be a calendar box. static set(input_or_id, options) { if (typeof input_or_id == 'string') { input_or_id = document.getElementById(input_or_id); } if (!input_or_id) { return; // If the input field is not there, exit. } input_or_id.addEventListener('blur', Calendar.onBlur); input_or_id.addEventListener('focus', function(ev) { Calendar.showCalendar(input_or_id, options); }); // FIXME: Add change listener to enable interactive date selection in calendar while typing } // Will be called once when the first input is set. static init() { if (!Calendar.box || !Calendar.box.parentNode) { var div = document.createElement('div'); if (!Calendar.box) Calendar.box = div; div.className = "calendar-box"; div.addEventListener("mousedown", function(ev) { ev = ev || window.event; if (ev.stopPropagation) ev.stopPropagation(); else ev.cancelBubble = true; Calendar.stopBlur = Date.now(); return true; }); document.getElementsByTagName("body")[0].insertBefore(div, document.getElementsByTagName("body")[0].firstChild); if (!Calendar.addedListener) { document.addEventListener("mousedown", function() { Calendar.hideCalendar(); }); Calendar.addedListener = true; } } } } window.Calendar = Calendar; function getOffsetRect(elem) { var box = elem.getBoundingClientRect(); var body = document.body; var docElem = document.documentElement; var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop; var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft; var clientTop = docElem.clientTop || body.clientTop || 0; var clientLeft = docElem.clientLeft || body.clientLeft || 0; var top = box.top + scrollTop - clientTop; var left = box.left + scrollLeft - clientLeft; return { top: Math.round(top), left: Math.round(left) }; } function getOffsetSum(elem) { var top = 0, left = 0; while(elem) { top = top + parseInt(elem.offsetTop); left = left + parseInt(elem.offsetLeft); elem = elem.offsetParent; } return { top: top, left: left }; } function getOffset(elem) { if (elem.getBoundingClientRect) return getOffsetRect(elem); else return getOffsetSum(elem); }