@logicamente.info/input-month-polyfill
Version:
Enables the use of inputs with month type in browsers that does not support it nativelly
259 lines (240 loc) • 9.74 kB
JavaScript
import Locales from './Locales';
export default class InputMonth {
constructor(input) {
this.original = input;
const lang = input.getAttribute('data-lang') ? input.getAttribute('data-lang') : 'en';
if (Locales[lang] === undefined) throw 'Language not implemented.';
this.locales = Locales[lang];
this.createStructures();
this.bindInputEvents();
this.loadOriginalValue();
}
loadOriginalValue() {
if (this.original.value !== '') {
const sentence = this.original.value.match(/(\d{4})\-(\d{2})/);
const year = sentence[1];
const monthNumber = parseInt(sentence[2]) - 1;
const months = Object.keys(this.locales);
this.input.value = `${this.locales[months[monthNumber]]} ${year}`;
}
}
createStructures() {
this.container = document.createElement('div');
this.container.classList.add('imp--container');
this.input = document.createElement('input');
this.input.classList.add('imp--input');
this.original.classList.forEach(c => this.input.classList.add(c));
const iReadOnly = this.original.getAttribute('readonly');
if (iReadOnly)
this.input.setAttribute('readonly', iReadOnly);
this.input.setAttribute('type', 'text');
this.input.setAttribute('data-month', '');
this.input.setAttribute('data-year', '');
this.input.value = '---------- ----';
this.viewers = document.createElement('div');
this.viewers.classList.add('imp--viewers');
this.monthViewer = document.createElement('div');
this.monthViewer.style = 'display: none';
this.monthViewer.classList.add('imp--month--viewer');
this.drawMonthButtons().map(m => this.monthViewer.appendChild(m));
this.yearViewer = document.createElement('div');
this.yearViewer.style = 'display: none';
this.yearViewer.classList.add('imp--year--viewer');
this.drawYearButtons(new Date().getFullYear() - 4).map(y => this.yearViewer.appendChild(y));
this.container.appendChild(this.input);
this.viewers.appendChild(this.monthViewer);
this.viewers.appendChild(this.yearViewer);
this.container.append(this.viewers);
this.original.style = 'display: none';
this.original.classList.add('input-month-polyfill');
this.original.parentNode.insertBefore(this.container, this.original.nextSibling);
}
bindInputEvents() {
this.input.addEventListener('click', this.onInputClick.bind(this));
this.input.addEventListener('blur', this.onInputBlur.bind(this));
this.input.addEventListener('keypress', e => e.preventDefault());
this.input.addEventListener('keydown', this.onInputKeyDown.bind(this));
this.input.addEventListener('change', this.onInputChange.bind(this));
}
drawMonthButtons() {
const mB = [];
Object.keys(this.locales).filter(m => !m.includes('ABBR')).forEach((monthName, i) => {
const monthButton = document.createElement('button');
monthButton.classList.add('imp--month--button');
monthButton.classList.add('imp--button');
monthButton.setAttribute('type', 'button');
monthButton.setAttribute('data-month-name', this.locales[monthName]);
monthButton.setAttribute('data-month-number', (i + 1));
monthButton.innerHTML = this.locales[`ABBR_${monthName.substr(0, 3)}`];
monthButton.addEventListener('click', this.onMonthClick.bind(this));
mB.push(monthButton);
});
return mB;
}
drawYearButtons(startYear) {
const yB = [];
const controls = document.createElement('div');
controls.classList.add('imp--viewer--controls');
const prevButton = document.createElement('button');
prevButton.classList.add('imp--year--button--prev');
prevButton.setAttribute('type', 'button');
prevButton.addEventListener('click', (e) => {
this.isInContainer = true;
this.yearViewer.textContent = '';
this.drawYearButtons(startYear - 9).map(y => this.yearViewer.appendChild(y));
this.showYearViewer();
});
prevButton.innerHTML = '<';
const nextButton = document.createElement('button');
nextButton.classList.add('imp--year--button--next');
nextButton.setAttribute('type', 'button');
nextButton.innerHTML = '>';
nextButton.addEventListener('click', (e) => {
this.isInContainer = true;
this.yearViewer.textContent = '';
this.drawYearButtons(startYear + 9).map(y => this.yearViewer.appendChild(y));
this.showYearViewer();
});
controls.appendChild(prevButton);
controls.appendChild(nextButton);
yB.push(controls);
for (let i = startYear; i < startYear + 9; i++) {
const yearButton = document.createElement('button');
yearButton.classList.add('imp--year--button');
yearButton.classList.add('imp--button');
yearButton.setAttribute('type', 'button');
yearButton.setAttribute('data-year', i);
yearButton.innerHTML = i;
yearButton.addEventListener('click', this.onYearClick.bind(this));
yB.push(yearButton);
}
return yB;
}
onMonthClick(e) {
const sentence = this.input.value.match(/(.+) (.+)/);
this.input.value = `${e.target.getAttribute('data-month-name')} ${sentence[2]}`;
this.input.setAttribute('data-month', e.target.getAttribute('data-month-number'));
this.input.dispatchEvent(new Event('change'));
if (e.explicitOriginalTarget === e.target)
this.showYearViewer();
}
onYearClick(e) {
const sentence = this.input.value.match(/(.+) (.+)/);
this.input.value = `${sentence[1]} ${e.target.getAttribute('data-year')}`;
this.input.setAttribute('data-year', e.target.getAttribute('data-year'));
this.input.dispatchEvent(new Event('change'));
this.input.dispatchEvent(new Event('blur'));
}
onInputClick(e) {
const sentence = e.target.value.match(/(.+) (.+)/);
if (e.target.selectionStart <= sentence[1].length) {
e.target.selectionStart = 0;
e.target.selectionEnd = sentence[1].length;
this.monthViewer.style = 'display: block';
this.yearViewer.style = 'display: none';
}
else {
e.target.selectionStart = sentence[1].length + 1;
e.target.selectionEnd = e.target.selectionStart + 4;
this.monthViewer.style = 'display: none';
this.yearViewer.style = 'display: block';
}
}
onInputChange(e) {
const month = e.target.getAttribute('data-month');
const year = e.target.getAttribute('data-year');
if (month !== '' && year !== '')
this.original.value = `${year}-${month.toString().padStart(2, '0')}`;
else
this.original.value = '';
this.original.dispatchEvent(new Event('change'));
this.original.dispatchEvent(new Event('blur'));
}
onInputBlur() {
setTimeout(() => {
if (!this.isInContainer) {
this.monthViewer.style = 'display: none';
this.yearViewer.style = 'display: none';
}
this.isInContainer = false;
}, 150);
}
onInputKeyDown(e) {
e.preventDefault();
this.onInputBlur();
if (this.input.selectionStart === 0)
this.keyboardMonthSelect(e.keyCode);
else
this.keyboardYearSelect(e.keyCode);
}
keyboardMonthSelect(code) {
const cMonth = this.input.getAttribute('data-month');
switch (code) {
case 38: // up
if (cMonth === '' || cMonth === '12')
this.monthViewer.childNodes[0].click();
else
this.monthViewer.childNodes[cMonth].click();
this.selectInputMonth();
break;
case 39: // right
this.selectInputYear();
break;
case 40: // down
if (cMonth === '' || cMonth === '1')
this.monthViewer.childNodes[11].click();
else
this.monthViewer.childNodes[parseInt(cMonth) - 2].click();
this.selectInputMonth();
break;
}
}
selectInputMonth() {
const sentence = this.input.value.match(/(.+) (.+)/);
this.input.selectionStart = 0;
this.input.selectionEnd = sentence[1].length;
}
selectInputYear() {
this.input.selectionStart = this.input.value.length - 4;
this.input.selectionEnd = this.input.value.length;
}
keyboardYearSelect(code) {
const sentence = this.input.value.match(/(.+) (.+)/);
const cYear = this.input.getAttribute('data-year');
switch (code) {
case 37: // left
this.selectInputMonth();
break;
case 38: // up
if (cYear === '') {
this.input.value = `${sentence[1]} ${new Date().getFullYear()}`;
this.input.setAttribute('data-year', new Date().getFullYear());
}
else {
this.input.value = `${sentence[1]} ${parseInt(cYear) + 1}`;
this.input.setAttribute('data-year', parseInt(cYear) + 1);
}
this.selectInputYear();
break;
case 40: // down
if (cYear === '') {
this.input.value = `${sentence[1]} ${new Date().getFullYear() - 1}`;
this.input.setAttribute('data-year', new Date().getFullYear() - 1);
}
else {
this.input.value = `${sentence[1]} ${parseInt(cYear) - 1}`;
this.input.setAttribute('data-year', parseInt(cYear) - 1);
}
this.selectInputYear();
break;
}
}
showYearViewer() {
setTimeout(() => {
const sentence = this.input.value.match(/(.+) (.+)/);
this.input.selectionStart = sentence[1].length + 1;
this.input.selectionEnd = this.input.selectionStart + 4;
this.input.dispatchEvent(new Event('click'));
}, 200);
}
}