@coreui/coreui-pro
Version:
The most popular front-end framework for developing responsive, mobile-first projects on the web rewritten by the CoreUI Team
798 lines (775 loc) • 33.5 kB
JavaScript
/*!
* CoreUI calendar.js v5.23.0 (https://coreui.io)
* Copyright 2025 The CoreUI Team (https://github.com/orgs/coreui/people)
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./util/index.js'), require('./util/calendar.js')) :
typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './util/index', './util/calendar'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Calendar = factory(global.BaseComponent, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Index, global.Calendar));
})(this, (function (BaseComponent, EventHandler, Manipulator, SelectorEngine, index_js, calendar_js) { 'use strict';
/* eslint-disable complexity, indent, multiline-ternary, @stylistic/multiline-ternary */
/**
* --------------------------------------------------------------------------
* CoreUI PRO calendar.js
* License (https://coreui.io/pro/license/)
* --------------------------------------------------------------------------
*/
/**
* Constants
*/
const NAME = 'calendar';
const DATA_KEY = 'coreui.calendar';
const EVENT_KEY = `.${DATA_KEY}`;
const DATA_API_KEY = '.data-api';
const ARROW_UP_KEY = 'ArrowUp';
const ARROW_RIGHT_KEY = 'ArrowRight';
const ARROW_DOWN_KEY = 'ArrowDown';
const ARROW_LEFT_KEY = 'ArrowLeft';
const ENTER_KEY = 'Enter';
const SPACE_KEY = 'Space';
const EVENT_BLUR = `blur${EVENT_KEY}`;
const EVENT_CALENDAR_DATE_CHANGE = `calendarDateChange${EVENT_KEY}`;
const EVENT_CALENDAR_MOUSE_LEAVE = `calendarMouseleave${EVENT_KEY}`;
const EVENT_CELL_HOVER = `cellHover${EVENT_KEY}`;
const EVENT_END_DATE_CHANGE = `endDateChange${EVENT_KEY}`;
const EVENT_FOCUS = `focus${EVENT_KEY}`;
const EVENT_KEYDOWN = `keydown${EVENT_KEY}`;
const EVENT_SELECT_END_CHANGE = `selectEndChange${EVENT_KEY}`;
const EVENT_START_DATE_CHANGE = `startDateChange${EVENT_KEY}`;
const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`;
const EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`;
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`;
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
const CLASS_NAME_CALENDAR_CELL = 'calendar-cell';
const CLASS_NAME_CALENDAR_CELL_INNER = 'calendar-cell-inner';
const CLASS_NAME_CALENDAR_ROW = 'calendar-row';
const CLASS_NAME_CALENDARS = 'calendars';
const CLASS_NAME_SHOW_WEEK_NUMBERS = 'show-week-numbers';
const SELECTOR_BTN_DOUBLE_NEXT = '.btn-double-next';
const SELECTOR_BTN_DOUBLE_PREV = '.btn-double-prev';
const SELECTOR_BTN_MONTH = '.btn-month';
const SELECTOR_BTN_NEXT = '.btn-next';
const SELECTOR_BTN_PREV = '.btn-prev';
const SELECTOR_BTN_YEAR = '.btn-year';
const SELECTOR_CALENDAR = '.calendar';
const SELECTOR_CALENDAR_CELL = '.calendar-cell';
const SELECTOR_CALENDAR_CELL_CLICKABLE = `${SELECTOR_CALENDAR_CELL}[tabindex="0"]`;
const SELECTOR_CALENDAR_ROW = '.calendar-row';
const SELECTOR_CALENDAR_ROW_CLICKABLE = `${SELECTOR_CALENDAR_ROW}[tabindex="0"]`;
const SELECTOR_DATA_TOGGLE = '[data-coreui-toggle="calendar"]';
const Default = {
ariaNavNextMonthLabel: 'Next month',
ariaNavNextYearLabel: 'Next year',
ariaNavPrevMonthLabel: 'Previous month',
ariaNavPrevYearLabel: 'Previous year',
calendarDate: null,
calendars: 1,
disabledDates: null,
endDate: null,
firstDayOfWeek: 1,
locale: 'default',
maxDate: null,
minDate: null,
range: false,
selectAdjacementDays: false,
selectEndDate: false,
selectionType: 'day',
showAdjacementDays: true,
showWeekNumber: false,
startDate: null,
weekdayFormat: 2,
weekNumbersLabel: null
};
const DefaultType = {
ariaNavNextMonthLabel: 'string',
ariaNavNextYearLabel: 'string',
ariaNavPrevMonthLabel: 'string',
ariaNavPrevYearLabel: 'string',
calendarDate: '(date|number|string|null)',
calendars: 'number',
disabledDates: '(array|date|function|null)',
endDate: '(date|number|string|null)',
firstDayOfWeek: 'number',
locale: 'string',
maxDate: '(date|number|string|null)',
minDate: '(date|number|string|null)',
range: 'boolean',
selectAdjacementDays: 'boolean',
selectEndDate: 'boolean',
selectionType: 'string',
showAdjacementDays: 'boolean',
showWeekNumber: 'boolean',
startDate: '(date|number|string|null)',
weekdayFormat: '(number|string)',
weekNumbersLabel: '(string|null)'
};
/**
* Class definition
*/
class Calendar extends BaseComponent {
constructor(element, config) {
super(element);
this._config = this._getConfig(config);
this._initializeDates();
this._initializeView();
this._createCalendar();
this._addEventListeners();
}
// Getters
static get Default() {
return Default;
}
static get DefaultType() {
return DefaultType;
}
static get NAME() {
return NAME;
}
// Public
update(config) {
this._config = this._getConfig(config);
this._initializeDates();
this._initializeView();
// Clear the current calendar content
this._element.innerHTML = '';
this._createCalendar();
}
// Private
_focusOnFirstAvailableCell() {
const cell = SelectorEngine.findOne(SELECTOR_CALENDAR_CELL_CLICKABLE, this._element);
if (cell) {
cell.focus();
}
}
_getDate(target) {
if (this._config.selectionType === 'week') {
const firstCell = SelectorEngine.findOne(SELECTOR_CALENDAR_CELL, target.closest(SELECTOR_CALENDAR_ROW));
return new Date(Manipulator.getDataAttribute(firstCell, 'date'));
}
return new Date(Manipulator.getDataAttribute(target, 'date'));
}
_handleCalendarClick(event) {
const target = event.target.classList.contains(CLASS_NAME_CALENDAR_CELL_INNER) ? event.target.parentElement : event.target;
const date = this._getDate(target);
const cloneDate = new Date(date);
const index = Manipulator.getDataAttribute(target.closest(SELECTOR_CALENDAR), 'calendar-index');
if (this._view === 'days') {
this._setCalendarDate(index ? new Date(cloneDate.setMonth(cloneDate.getMonth() - index)) : date);
}
if (this._view === 'months' && this._config.selectionType !== 'month') {
this._setCalendarDate(index ? new Date(cloneDate.setMonth(cloneDate.getMonth() - index)) : date);
this._view = 'days';
this._updateCalendar(this._focusOnFirstAvailableCell.bind(this));
return;
}
if (this._view === 'years' && this._config.selectionType !== 'year') {
this._setCalendarDate(index ? new Date(cloneDate.setFullYear(cloneDate.getFullYear() - index)) : date);
this._view = 'months';
this._updateCalendar(this._focusOnFirstAvailableCell.bind(this));
return;
}
// Allow to change the calendarDate but not startDate or endDate
if (calendar_js.isDateDisabled(date, this._minDate, this._maxDate, this._config.disabledDates)) {
return;
}
this._hoverDate = null;
this._selectDate(date);
this._updateClassNamesAndAriaLabels();
}
_handleCalendarKeydown(event) {
const date = this._getDate(event.target);
if (event.code === SPACE_KEY || event.key === ENTER_KEY) {
event.preventDefault();
this._handleCalendarClick(event);
}
if (event.key === ARROW_RIGHT_KEY || event.key === ARROW_LEFT_KEY || event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY) {
event.preventDefault();
if (this._maxDate && date >= calendar_js.convertToDateObject(this._maxDate, this._config.selectionType) && (event.key === ARROW_RIGHT_KEY || event.key === ARROW_DOWN_KEY)) {
return;
}
if (this._minDate && date <= calendar_js.convertToDateObject(this._minDate, this._config.selectionType) && (event.key === ARROW_LEFT_KEY || event.key === ARROW_UP_KEY)) {
return;
}
let element = event.target;
if (this._config.selectionType === 'week' && element.tabIndex === -1) {
element = element.closest(SELECTOR_CALENDAR_ROW_CLICKABLE);
}
const list = SelectorEngine.find(this._config.selectionType === 'week' ? SELECTOR_CALENDAR_ROW_CLICKABLE : SELECTOR_CALENDAR_CELL_CLICKABLE, this._element);
const index = list.indexOf(element);
const first = index === 0;
const last = index === list.length - 1;
const toBoundary = {
start: index,
end: list.length - (index + 1)
};
const gap = {
ArrowRight: 1,
ArrowLeft: -1,
ArrowUp: this._config.selectionType === 'week' && this._view === 'days' ? -1 : this._view === 'days' ? -7 : -3,
ArrowDown: this._config.selectionType === 'week' && this._view === 'days' ? 1 : this._view === 'days' ? 7 : 3
};
if (event.key === ARROW_RIGHT_KEY && last || event.key === ARROW_DOWN_KEY && toBoundary.end < gap.ArrowDown || event.key === ARROW_LEFT_KEY && first || event.key === ARROW_UP_KEY && toBoundary.start < Math.abs(gap.ArrowUp)) {
const callback = key => {
const _list = SelectorEngine.find(`${SELECTOR_CALENDAR_CELL_CLICKABLE}, ${SELECTOR_CALENDAR_ROW_CLICKABLE}`, this._element);
if (_list.length && key === ARROW_RIGHT_KEY) {
_list[0].focus();
}
if (_list.length && key === ARROW_LEFT_KEY) {
_list[_list.length - 1].focus();
}
if (_list.length && key === ARROW_DOWN_KEY) {
_list[gap.ArrowDown - (list.length - index)].focus();
}
if (_list.length && key === ARROW_UP_KEY) {
_list[_list.length - (Math.abs(gap.ArrowUp) + 1 - (index + 1))].focus();
}
};
if (this._view === 'days') {
this._modifyCalendarDate(0, event.key === ARROW_RIGHT_KEY || event.key === ARROW_DOWN_KEY ? 1 : -1, callback.bind(this, event.key));
}
if (this._view === 'months') {
this._modifyCalendarDate(event.key === ARROW_RIGHT_KEY || event.key === ARROW_DOWN_KEY ? 1 : -1, 0, callback.bind(this, event.key));
}
if (this._view === 'years') {
this._modifyCalendarDate(event.key === ARROW_RIGHT_KEY || event.key === ARROW_DOWN_KEY ? 10 : -10, 0, callback.bind(this, event.key));
}
return;
}
if (list[index + gap[event.key]].tabIndex === 0) {
list[index + gap[event.key]].focus();
return;
}
for (let i = index; i < list.length; event.key === ARROW_RIGHT_KEY || event.key === ARROW_DOWN_KEY ? i++ : i--) {
if (list[i + gap[event.key]].tabIndex === 0) {
list[i + gap[event.key]].focus();
break;
}
}
}
}
_handleCalendarMouseEnter(event) {
const target = event.target.classList.contains(CLASS_NAME_CALENDAR_CELL_INNER) ? event.target.parentElement : event.target;
const date = this._getDate(target);
if (calendar_js.isDateDisabled(date, this._minDate, this._maxDate, this._config.disabledDates)) {
return;
}
this._hoverDate = calendar_js.setTimeFromDate(date, this._selectEndDate ? this._endDate : this._startDate);
EventHandler.trigger(this._element, EVENT_CELL_HOVER, {
date: calendar_js.getDateBySelectionType(this._hoverDate, this._config.selectionType)
});
this._updateClassNamesAndAriaLabels();
}
_handleCalendarMouseLeave() {
this._hoverDate = null;
EventHandler.trigger(this._element, EVENT_CELL_HOVER, {
date: null
});
this._updateClassNamesAndAriaLabels();
}
_addEventListeners() {
EventHandler.on(this._element, EVENT_CLICK_DATA_API, SELECTOR_CALENDAR_CELL_CLICKABLE, event => {
this._handleCalendarClick(event);
});
EventHandler.on(this._element, EVENT_KEYDOWN, SELECTOR_CALENDAR_CELL_CLICKABLE, event => {
this._handleCalendarKeydown(event);
});
EventHandler.on(this._element, EVENT_MOUSEENTER, SELECTOR_CALENDAR_CELL_CLICKABLE, event => {
this._handleCalendarMouseEnter(event);
});
EventHandler.on(this._element, EVENT_MOUSELEAVE, SELECTOR_CALENDAR_CELL_CLICKABLE, () => {
this._handleCalendarMouseLeave();
});
EventHandler.on(this._element, EVENT_FOCUS, SELECTOR_CALENDAR_CELL_CLICKABLE, event => {
this._handleCalendarMouseEnter(event);
});
EventHandler.on(this._element, EVENT_BLUR, SELECTOR_CALENDAR_CELL_CLICKABLE, () => {
this._handleCalendarMouseLeave();
});
EventHandler.on(this._element, EVENT_CLICK_DATA_API, SELECTOR_CALENDAR_ROW_CLICKABLE, event => {
this._handleCalendarClick(event);
});
EventHandler.on(this._element, EVENT_KEYDOWN, SELECTOR_CALENDAR_ROW_CLICKABLE, event => {
this._handleCalendarKeydown(event);
});
EventHandler.on(this._element, EVENT_MOUSEENTER, SELECTOR_CALENDAR_ROW_CLICKABLE, event => {
this._handleCalendarMouseEnter(event);
});
EventHandler.on(this._element, EVENT_MOUSELEAVE, SELECTOR_CALENDAR_ROW_CLICKABLE, () => {
this._handleCalendarMouseLeave();
});
EventHandler.on(this._element, EVENT_FOCUS, SELECTOR_CALENDAR_ROW_CLICKABLE, event => {
this._handleCalendarMouseEnter(event);
});
EventHandler.on(this._element, EVENT_BLUR, SELECTOR_CALENDAR_ROW_CLICKABLE, () => {
this._handleCalendarMouseLeave();
});
// Navigation
this._addNavigationEventListeners();
EventHandler.on(this._element, EVENT_MOUSELEAVE, 'table', () => {
EventHandler.trigger(this._element, EVENT_CALENDAR_MOUSE_LEAVE);
});
}
_addNavigationEventListeners() {
const navigationSelectors = {
[SELECTOR_BTN_PREV]: () => this._modifyCalendarDate(0, -1),
[SELECTOR_BTN_DOUBLE_PREV]: () => this._modifyCalendarDate(this._view === 'years' ? -10 : -1),
[SELECTOR_BTN_NEXT]: () => this._modifyCalendarDate(0, 1),
[SELECTOR_BTN_DOUBLE_NEXT]: () => this._modifyCalendarDate(this._view === 'years' ? 10 : 1),
[SELECTOR_BTN_MONTH]: () => {
this._view = 'months';
this._updateCalendar();
},
[SELECTOR_BTN_YEAR]: () => {
this._view = 'years';
this._updateCalendar();
}
};
for (const [selector, handler] of Object.entries(navigationSelectors)) {
EventHandler.on(this._element, EVENT_CLICK_DATA_API, selector, event => {
event.preventDefault();
const selectors = SelectorEngine.find(selector, this._element);
const selectorIndex = selectors.indexOf(event.target.closest(selector));
handler();
// Retrieve focus to the navigation element
const _selectors = SelectorEngine.find(selector, this._element);
if (_selectors && _selectors[selectorIndex]) {
_selectors[selectorIndex].focus();
}
});
}
}
_setCalendarDate(date) {
this._calendarDate = date;
EventHandler.trigger(this._element, EVENT_CALENDAR_DATE_CHANGE, {
date
});
}
_modifyCalendarDate(years, months = 0, callback) {
const year = this._calendarDate.getFullYear();
const month = this._calendarDate.getMonth();
const d = new Date(year, month, 1);
if (years) {
d.setFullYear(d.getFullYear() + years);
}
if (months) {
d.setMonth(d.getMonth() + months);
}
this._calendarDate = d;
if (this._view === 'days') {
EventHandler.trigger(this._element, EVENT_CALENDAR_DATE_CHANGE, {
date: d
});
}
this._updateCalendar(callback);
}
_setEndDate(date) {
this._endDate = calendar_js.setTimeFromDate(date, this._endDate);
EventHandler.trigger(this._element, EVENT_END_DATE_CHANGE, {
date: calendar_js.getDateBySelectionType(this._endDate, this._config.selectionType)
});
}
_setStartDate(date) {
this._startDate = calendar_js.setTimeFromDate(date, this._startDate);
EventHandler.trigger(this._element, EVENT_START_DATE_CHANGE, {
date: calendar_js.getDateBySelectionType(this._startDate, this._config.selectionType)
});
}
_setSelectEndDate(value) {
this._selectEndDate = value;
EventHandler.trigger(this._element, EVENT_SELECT_END_CHANGE, {
value
});
}
_selectDate(date) {
if (calendar_js.isDateDisabled(date, this._minDate, this._maxDate, this._config.disabledDates)) {
return;
}
if (this._config.range) {
if (this._selectEndDate) {
this._setSelectEndDate(false);
if (this._startDate && this._startDate > date) {
this._setStartDate(null);
this._setEndDate(null);
return;
}
if (calendar_js.isDisableDateInRange(this._startDate, date, this._config.disabledDates)) {
this._setStartDate(null);
this._setEndDate(null);
return;
}
this._setEndDate(date);
return;
}
if (this._endDate && this._endDate < date) {
this._setStartDate(null);
this._setEndDate(null);
return;
}
if (calendar_js.isDisableDateInRange(date, this._endDate, this._config.disabledDates)) {
this._setStartDate(null);
this._setEndDate(null);
return;
}
this._setSelectEndDate(true);
this._setStartDate(date);
return;
}
this._setStartDate(date);
}
_createCalendarPanel(order) {
var _this$_config$weekNum;
const calendarDate = calendar_js.getCalendarDate(this._calendarDate, order, this._view);
const year = calendarDate.getFullYear();
const month = calendarDate.getMonth();
const calendarPanelEl = document.createElement('div');
calendarPanelEl.classList.add('calendar');
Manipulator.setDataAttribute(calendarPanelEl, 'calendar-index', order);
// Create navigation
const navigationElement = document.createElement('div');
navigationElement.classList.add('calendar-nav');
navigationElement.innerHTML = `
<div class="calendar-nav-prev">
<button type="button" class="calendar-nav-btn btn-double-prev" aria-label="${this._config.ariaNavPrevYearLabel}">
<span class="calendar-nav-icon calendar-nav-icon-double-prev"></span>
</button>
${this._view === 'days' ? `<button type="button" class="calendar-nav-btn btn-prev" aria-label="${this._config.ariaNavPrevMonthLabel}">
<span class="calendar-nav-icon calendar-nav-icon-prev"></span>
</button>` : ''}
</div>
<div class="calendar-nav-date" aria-live="polite">
${this._view === 'days' ? `<button type="button" class="calendar-nav-btn btn-sm btn-month">
${calendarDate.toLocaleDateString(this._config.locale, {
month: 'long'
})}
</button>` : ''}
<button type="button" class="calendar-nav-btn btn-year">
${calendarDate.toLocaleDateString(this._config.locale, {
year: 'numeric'
})}
</button>
</div>
<div class="calendar-nav-next">
${this._view === 'days' ? `<button type="button" class="calendar-nav-btn btn-next" aria-label="${this._config.ariaNavNextMonthLabel}">
<span class="calendar-nav-icon calendar-nav-icon-next"></span>
</button>` : ''}
<button type="button" class="calendar-nav-btn btn-double-next" aria-label="${this._config.ariaNavNextYearLabel}">
<span class="calendar-nav-icon calendar-nav-icon-double-next"></span>
</button>
</div>
`;
const monthDetails = calendar_js.getMonthDetails(year, month, this._config.firstDayOfWeek);
const listOfMonths = calendar_js.createGroupsInArray(calendar_js.getMonthsNames(this._config.locale), 4);
const listOfYears = calendar_js.createGroupsInArray(calendar_js.getYears(calendarDate.getFullYear()), 4);
const weekDays = monthDetails[0].days;
const calendarTable = document.createElement('table');
calendarTable.innerHTML = `
${this._view === 'days' ? `
<thead>
<tr>
${this._config.showWeekNumber ? `<th class="${CLASS_NAME_CALENDAR_CELL}">
<div class="calendar-header-cell-inner">
${(_this$_config$weekNum = this._config.weekNumbersLabel) != null ? _this$_config$weekNum : ''}
</div>
</th>` : ''}
${weekDays.map(({
date
}) => `<th class="${CLASS_NAME_CALENDAR_CELL}" abbr="${date.toLocaleDateString(this._config.locale, {
weekday: 'long'
})}">
<div class="calendar-header-cell-inner">
${typeof this._config.weekdayFormat === 'string' ? date.toLocaleDateString(this._config.locale, {
weekday: this._config.weekdayFormat
}) : date.toLocaleDateString(this._config.locale, {
weekday: 'long'
}).slice(0, this._config.weekdayFormat)}
</div>
</th>`).join('')}
</tr>
</thead>` : ''}
<tbody>
${this._view === 'days' ? monthDetails.map(({
week,
days
}) => {
const {
date
} = days[0];
const rowAttributes = this._rowWeekAttributes(date);
return `<tr
class="${rowAttributes.className}"
tabindex="${rowAttributes.tabIndex}"
${rowAttributes.ariaSelected ? 'aria-selected="true"' : ''}
>
${this._config.showWeekNumber ? `<th class="calendar-cell-week-number">${week.number}</td>` : ''}
${days.map(({
date,
month
}) => {
const cellAttributes = this._cellDayAttributes(date, month);
return month === 'current' || this._config.showAdjacementDays ? `<td
class="${cellAttributes.className}"
tabindex="${cellAttributes.tabIndex}"
${cellAttributes.ariaSelected ? 'aria-selected="true"' : ''}
data-coreui-date="${date}"
>
<div class="calendar-cell-inner day">
${date.toLocaleDateString(this._config.locale, {
day: 'numeric'
})}
</div>
</td>` : '<td></td>';
}).join('')}</tr>`;
}).join('') : ''}
${this._view === 'months' ? listOfMonths.map((row, index) => `<tr>
${row.map((month, idx) => {
const date = new Date(calendarDate.getFullYear(), index * 3 + idx, 1);
const cellAttributes = this._cellMonthAttributes(date);
return `<td
class="${cellAttributes.className}"
tabindex="${cellAttributes.tabIndex}"
${cellAttributes.ariaSelected ? 'aria-selected="true"' : ''}
data-coreui-date="${date.toDateString()}"
>
<div class="calendar-cell-inner month">
${month}
</div>
</td>`;
}).join('')}
</tr>`).join('') : ''}
${this._view === 'years' ? listOfYears.map(row => `<tr>
${row.map(year => {
const date = new Date(year, 0, 1);
const cellAttributes = this._cellYearAttributes(date);
return `<td
class="${cellAttributes.className}"
tabindex="${cellAttributes.tabIndex}"
${cellAttributes.ariaSelected ? 'aria-selected="true"' : ''}
data-coreui-date="${date.toDateString()}"
>
<div class="calendar-cell-inner year">
${year}
</div>
</td>`;
}).join('')}
</tr>`).join('') : ''}
</tbody>
`;
calendarPanelEl.append(navigationElement, calendarTable);
return calendarPanelEl;
}
_createCalendar() {
if (this._config.selectionType && this._view === 'days') {
this._element.classList.add(`select-${this._config.selectionType}`);
}
if (this._config.showWeekNumber) {
this._element.classList.add(CLASS_NAME_SHOW_WEEK_NUMBERS);
}
for (const [index, _] of Array.from({
length: this._config.calendars
}).entries()) {
this._element.append(this._createCalendarPanel(index));
}
this._element.classList.add(CLASS_NAME_CALENDARS);
}
_initializeDates() {
// Convert dates to date objects based on the selection type
this._calendarDate = calendar_js.convertToDateObject(this._config.calendarDate || this._config.startDate || this._config.endDate, this._config.selectionType) || new Date();
this._startDate = calendar_js.convertToDateObject(this._config.startDate, this._config.selectionType);
this._endDate = calendar_js.convertToDateObject(this._config.endDate, this._config.selectionType);
this._minDate = calendar_js.convertToDateObject(this._config.minDate, this._config.selectionType);
this._maxDate = calendar_js.convertToDateObject(this._config.maxDate, this._config.selectionType);
this._hoverDate = null;
this._selectEndDate = this._config.selectEndDate;
}
_initializeView() {
const viewMap = {
day: 'days',
week: 'days',
month: 'months',
year: 'years'
};
this._view = viewMap[this._config.selectionType] || 'days';
}
_updateCalendar(callback) {
this._element.innerHTML = '';
this._createCalendar();
if (callback) {
setTimeout(callback, 1);
}
}
_updateClassNamesAndAriaLabels() {
if (this._config.selectionType === 'week') {
const rows = SelectorEngine.find(SELECTOR_CALENDAR_ROW, this._element);
for (const row of rows) {
const firstCell = SelectorEngine.findOne(SELECTOR_CALENDAR_CELL, row);
const date = new Date(Manipulator.getDataAttribute(firstCell, 'date'));
const rowAttributes = this._rowWeekAttributes(date);
row.className = rowAttributes.className;
row.tabIndex = rowAttributes.tabIndex;
if (rowAttributes.ariaSelected) {
row.setAttribute('aria-selected', true);
} else {
row.removeAttribute('aria-selected');
}
}
return;
}
const cells = SelectorEngine.find(SELECTOR_CALENDAR_CELL_CLICKABLE, this._element);
for (const cell of cells) {
const date = new Date(Manipulator.getDataAttribute(cell, 'date'));
let cellAttributes;
if (this._view === 'days') {
cellAttributes = this._cellDayAttributes(date, 'current');
} else if (this._view === 'months') {
cellAttributes = this._cellMonthAttributes(date);
} else {
cellAttributes = this._cellYearAttributes(date);
}
cell.className = cellAttributes.className;
cell.tabIndex = cellAttributes.tabIndex;
if (cellAttributes.ariaSelected) {
cell.setAttribute('aria-selected', true);
} else {
cell.removeAttribute('aria-selected');
}
}
}
_classNames(classNames) {
return Object.entries(classNames).filter(([_, value]) => Boolean(value)).map(([key]) => key).join(' ');
}
_cellDayAttributes(date, month) {
const isCurrentMonth = month === 'current';
const isDisabled = calendar_js.isDateDisabled(date, this._minDate, this._maxDate, this._config.disabledDates);
const isSelected = calendar_js.isDateSelected(date, this._startDate, this._endDate);
const isTodayDate = calendar_js.isToday(date);
if (this._config.selectionType !== 'day' || this._view !== 'days') {
return {
className: this._classNames({
[CLASS_NAME_CALENDAR_CELL]: true,
today: isTodayDate,
[month]: true
}),
tabIndex: -1,
ariaSelected: false
};
}
const isInRange = isCurrentMonth && calendar_js.isDateInRange(date, this._startDate, this._endDate);
const isRangeHover = isCurrentMonth && this._hoverDate && (this._selectEndDate ? calendar_js.isDateInRange(date, this._startDate, this._hoverDate) : calendar_js.isDateInRange(date, this._hoverDate, this._endDate));
const classNames = this._classNames({
[CLASS_NAME_CALENDAR_CELL]: true,
clickable: !isCurrentMonth && this._config.selectAdjacementDays,
disabled: isDisabled,
range: isInRange,
'range-hover': isRangeHover,
selected: isSelected,
today: isTodayDate,
[month]: true
});
return {
className: classNames,
tabIndex: (isCurrentMonth || this._config.selectAdjacementDays) && !isDisabled ? 0 : -1,
ariaSelected: isSelected
};
}
_cellMonthAttributes(date) {
const isDisabled = calendar_js.isMonthDisabled(date, this._minDate, this._maxDate, this._config.disabledDates);
const isSelected = calendar_js.isMonthSelected(date, this._startDate, this._endDate);
const isInRange = calendar_js.isMonthInRange(date, this._startDate, this._endDate);
const isRangeHover = this._config.selectionType === 'month' && this._hoverDate && (this._selectEndDate ? calendar_js.isMonthInRange(date, this._startDate, this._hoverDate) : calendar_js.isMonthInRange(date, this._hoverDate, this._endDate));
const classNames = this._classNames({
[CLASS_NAME_CALENDAR_CELL]: true,
disabled: isDisabled,
'range-hover': isRangeHover,
range: isInRange,
selected: isSelected
});
return {
className: classNames,
tabIndex: isDisabled ? -1 : 0,
ariaSelected: isSelected
};
}
_cellYearAttributes(date) {
const isDisabled = calendar_js.isYearDisabled(date, this._minDate, this._maxDate, this._config.disabledDates);
const isSelected = calendar_js.isYearSelected(date, this._startDate, this._endDate);
const isInRange = calendar_js.isYearInRange(date, this._startDate, this._endDate);
const isRangeHover = this._config.selectionType === 'year' && this._hoverDate && (this._selectEndDate ? calendar_js.isYearInRange(date, this._startDate, this._hoverDate) : calendar_js.isYearInRange(date, this._hoverDate, this._endDate));
const classNames = this._classNames({
[CLASS_NAME_CALENDAR_CELL]: true,
disabled: isDisabled,
'range-hover': isRangeHover,
range: isInRange,
selected: isSelected
});
return {
className: classNames,
tabIndex: isDisabled ? -1 : 0,
ariaSelected: isSelected
};
}
_rowWeekAttributes(date) {
if (this._config.selectionType !== 'week') {
return {
className: this._classNames({
[CLASS_NAME_CALENDAR_ROW]: true
}),
tabIndex: -1,
ariaSelected: false
};
}
const isDisabled = calendar_js.isDateDisabled(date, this._minDate, this._maxDate, this._config.disabledDates);
const isSelected = calendar_js.isDateSelected(date, this._startDate, this._endDate);
const isInRange = calendar_js.isDateInRange(date, this._startDate, this._endDate);
const isRangeHover = this._hoverDate && (this._selectEndDate ? calendar_js.isYearInRange(date, this._startDate, this._hoverDate) : calendar_js.isYearInRange(date, this._hoverDate, this._endDate));
const classNames = this._classNames({
[CLASS_NAME_CALENDAR_ROW]: true,
disabled: isDisabled,
range: isInRange,
'range-hover': isRangeHover,
selected: isSelected
});
return {
className: classNames,
tabIndex: isDisabled ? -1 : 0,
ariaSelected: isSelected
};
}
// Static
static calendarInterface(element, config) {
const data = Calendar.getOrCreateInstance(element, config);
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`);
}
data[config]();
}
}
static jQueryInterface(config) {
return this.each(function () {
const data = Calendar.getOrCreateInstance(this, config);
if (typeof config !== 'string') {
return;
}
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`);
}
data[config]();
});
}
}
/**
* Data API implementation
*/
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
for (const element of Array.from(document.querySelectorAll(SELECTOR_DATA_TOGGLE))) {
Calendar.calendarInterface(element);
}
});
/**
* jQuery
*/
index_js.defineJQueryPlugin(Calendar);
return Calendar;
}));
//# sourceMappingURL=calendar.js.map