182 lines
4.6 KiB
JavaScript
182 lines
4.6 KiB
JavaScript
/**
|
|
* 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:
|
|
* <input type="text" name="date" id="date" />
|
|
* <script type="text/javascript">
|
|
* Calendar.set("date");
|
|
* </script>
|
|
*/
|
|
|
|
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 {...props} />, 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);
|
|
}
|