198 lines
6.8 KiB
JavaScript
198 lines
6.8 KiB
JavaScript
import {hasProperty, pushUnique, createTagRepeat} from '../../lib/utils.js';
|
|
import {dateValue} from '../../lib/date.js';
|
|
import {parseHTML} from '../../lib/dom.js';
|
|
import View from './View.js';
|
|
|
|
function computeMonthRange(range, thisYear) {
|
|
if (!range || !range[0] || !range[1]) {
|
|
return;
|
|
}
|
|
|
|
const [[startY, startM], [endY, endM]] = range;
|
|
if (startY > thisYear || endY < thisYear) {
|
|
return;
|
|
}
|
|
return [
|
|
startY === thisYear ? startM : -1,
|
|
endY === thisYear ? endM : 12,
|
|
];
|
|
}
|
|
|
|
export default class MonthsView extends View {
|
|
constructor(picker) {
|
|
super(picker, {
|
|
id: 1,
|
|
name: 'months',
|
|
cellClass: 'month',
|
|
});
|
|
}
|
|
|
|
init(options, onConstruction = true) {
|
|
if (onConstruction) {
|
|
this.grid = this.element;
|
|
this.element.classList.add('months', 'datepicker-grid', 'w-64', 'grid', 'grid-cols-4');
|
|
this.grid.appendChild(parseHTML(createTagRepeat('span', 12, {'data-month': ix => ix})));
|
|
}
|
|
super.init(options);
|
|
}
|
|
|
|
setOptions(options) {
|
|
if (options.locale) {
|
|
this.monthNames = options.locale.monthsShort;
|
|
}
|
|
if (hasProperty(options, 'minDate')) {
|
|
if (options.minDate === undefined) {
|
|
this.minYear = this.minMonth = this.minDate = undefined;
|
|
} else {
|
|
const minDateObj = new Date(options.minDate);
|
|
this.minYear = minDateObj.getFullYear();
|
|
this.minMonth = minDateObj.getMonth();
|
|
this.minDate = minDateObj.setDate(1);
|
|
}
|
|
}
|
|
if (hasProperty(options, 'maxDate')) {
|
|
if (options.maxDate === undefined) {
|
|
this.maxYear = this.maxMonth = this.maxDate = undefined;
|
|
} else {
|
|
const maxDateObj = new Date(options.maxDate);
|
|
this.maxYear = maxDateObj.getFullYear();
|
|
this.maxMonth = maxDateObj.getMonth();
|
|
this.maxDate = dateValue(this.maxYear, this.maxMonth + 1, 0);
|
|
}
|
|
}
|
|
if (options.beforeShowMonth !== undefined) {
|
|
this.beforeShow = typeof options.beforeShowMonth === 'function'
|
|
? options.beforeShowMonth
|
|
: undefined;
|
|
}
|
|
}
|
|
|
|
// Update view's settings to reflect the viewDate set on the picker
|
|
updateFocus() {
|
|
const viewDate = new Date(this.picker.viewDate);
|
|
this.year = viewDate.getFullYear();
|
|
this.focused = viewDate.getMonth();
|
|
}
|
|
|
|
// Update view's settings to reflect the selected dates
|
|
updateSelection() {
|
|
const {dates, rangepicker} = this.picker.datepicker;
|
|
this.selected = dates.reduce((selected, timeValue) => {
|
|
const date = new Date(timeValue);
|
|
const year = date.getFullYear();
|
|
const month = date.getMonth();
|
|
if (selected[year] === undefined) {
|
|
selected[year] = [month];
|
|
} else {
|
|
pushUnique(selected[year], month);
|
|
}
|
|
return selected;
|
|
}, {});
|
|
if (rangepicker && rangepicker.dates) {
|
|
this.range = rangepicker.dates.map(timeValue => {
|
|
const date = new Date(timeValue);
|
|
return isNaN(date) ? undefined : [date.getFullYear(), date.getMonth()];
|
|
});
|
|
}
|
|
}
|
|
|
|
// Update the entire view UI
|
|
render() {
|
|
// refresh disabled months on every render in order to clear the ones added
|
|
// by beforeShow hook at previous render
|
|
this.disabled = [];
|
|
|
|
this.picker.setViewSwitchLabel(this.year);
|
|
this.picker.setPrevBtnDisabled(this.year <= this.minYear);
|
|
this.picker.setNextBtnDisabled(this.year >= this.maxYear);
|
|
|
|
const selected = this.selected[this.year] || [];
|
|
const yrOutOfRange = this.year < this.minYear || this.year > this.maxYear;
|
|
const isMinYear = this.year === this.minYear;
|
|
const isMaxYear = this.year === this.maxYear;
|
|
const range = computeMonthRange(this.range, this.year);
|
|
|
|
Array.from(this.grid.children).forEach((el, index) => {
|
|
const classList = el.classList;
|
|
const date = dateValue(this.year, index, 1);
|
|
|
|
el.className = `datepicker-cell hover:bg-gray-100 dark:hover:bg-gray-600 block flex-1 leading-9 border-0 rounded-lg cursor-pointer text-center text-gray-900 dark:text-white font-semibold text-sm ${this.cellClass}`;
|
|
if (this.isMinView) {
|
|
el.dataset.date = date;
|
|
}
|
|
// reset text on every render to clear the custom content set
|
|
// by beforeShow hook at previous render
|
|
el.textContent = this.monthNames[index];
|
|
|
|
if (
|
|
yrOutOfRange
|
|
|| isMinYear && index < this.minMonth
|
|
|| isMaxYear && index > this.maxMonth
|
|
) {
|
|
classList.add('disabled');
|
|
}
|
|
if (range) {
|
|
const [rangeStart, rangeEnd] = range;
|
|
if (index > rangeStart && index < rangeEnd) {
|
|
classList.add('range');
|
|
}
|
|
if (index === rangeStart) {
|
|
classList.add('range-start');
|
|
}
|
|
if (index === rangeEnd) {
|
|
classList.add('range-end');
|
|
}
|
|
}
|
|
if (selected.includes(index)) {
|
|
classList.add('selected', 'bg-blue-700', '!bg-primary-700', 'text-white', 'dark:bg-blue-600', 'dark:!bg-primary-600', 'dark:text-white');
|
|
classList.remove('text-gray-900', 'hover:bg-gray-100', 'dark:text-white', 'dark:hover:bg-gray-600');
|
|
}
|
|
if (index === this.focused) {
|
|
classList.add('focused');
|
|
}
|
|
|
|
if (this.beforeShow) {
|
|
this.performBeforeHook(el, index, date);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Update the view UI by applying the changes of selected and focused items
|
|
refresh() {
|
|
const selected = this.selected[this.year] || [];
|
|
const [rangeStart, rangeEnd] = computeMonthRange(this.range, this.year) || [];
|
|
this.grid
|
|
.querySelectorAll('.range, .range-start, .range-end, .selected, .focused')
|
|
.forEach((el) => {
|
|
el.classList.remove('range', 'range-start', 'range-end', 'selected', 'bg-blue-700', '!bg-primary-700', 'dark:bg-blue-600', 'dark:!bg-primary-700', 'dark:text-white', 'text-white', 'focused');
|
|
el.classList.add('text-gray-900', 'hover:bg-gray-100', 'dark:text-white', 'dark:hover:bg-gray-600');
|
|
});
|
|
Array.from(this.grid.children).forEach((el, index) => {
|
|
const classList = el.classList;
|
|
if (index > rangeStart && index < rangeEnd) {
|
|
classList.add('range');
|
|
}
|
|
if (index === rangeStart) {
|
|
classList.add('range-start');
|
|
}
|
|
if (index === rangeEnd) {
|
|
classList.add('range-end');
|
|
}
|
|
if (selected.includes(index)) {
|
|
classList.add('selected', 'bg-blue-700', '!bg-primary-700', 'text-white', 'dark:bg-blue-600', 'dark:!bg-primary-600', 'dark:text-white');
|
|
classList.remove('text-gray-900', 'hover:bg-gray-100', 'dark:text-white', 'dark:hover:bg-gray-600');
|
|
}
|
|
if (index === this.focused) {
|
|
classList.add('focused');
|
|
}
|
|
});
|
|
}
|
|
|
|
// Update the view UI by applying the change of focused item
|
|
refreshFocus() {
|
|
this.grid.querySelectorAll('.focused').forEach((el) => {
|
|
el.classList.remove('focused');
|
|
});
|
|
this.grid.children[this.focused].classList.add('focused');
|
|
}
|
|
} |