UNPKG

framework7

Version:

Full featured mobile HTML framework for building iOS & Android apps

1,345 lines (1,335 loc) 67.1 kB
import { extend, nextTick, deleteProps } from '../../shared/utils.js'; import Framework7Class from '../../shared/class.js'; import $ from '../../shared/dom7.js'; import { getDevice } from '../../shared/get-device.js'; import { getSupport } from '../../shared/get-support.js'; /** @jsx $jsx */ import $jsx from '../../shared/$jsx.js'; class Calendar extends Framework7Class { constructor(app, params) { if (params === void 0) { params = {}; } super(params, [app]); const calendar = this; calendar.params = extend({}, app.params.calendar, params); let $containerEl; if (calendar.params.containerEl) { $containerEl = $(calendar.params.containerEl); if ($containerEl.length === 0) return calendar; } let $inputEl; if (calendar.params.inputEl) { $inputEl = $(calendar.params.inputEl); } const isHorizontal = calendar.params.direction === 'horizontal'; let inverter = 1; if (isHorizontal) { inverter = app.rtl ? -1 : 1; } extend(calendar, { app, $containerEl, containerEl: $containerEl && $containerEl[0], inline: $containerEl && $containerEl.length > 0, $inputEl, inputEl: $inputEl && $inputEl[0], initialized: false, opened: false, url: calendar.params.url, isHorizontal, inverter, animating: false, allowTouchMove: true, hasTimePicker: calendar.params.timePicker && !calendar.params.rangePicker && !calendar.params.multiple }); calendar.dayFormatter = date => { const formatter = new Intl.DateTimeFormat(calendar.params.locale, { day: 'numeric' }); return formatter.format(date).replace(/日/, ''); }; calendar.monthFormatter = date => { const formatter = new Intl.DateTimeFormat(calendar.params.locale, { month: 'long' }); return formatter.format(date); }; calendar.yearFormatter = date => { const formatter = new Intl.DateTimeFormat(calendar.params.locale, { year: 'numeric' }); return formatter.format(date); }; calendar.timeSelectorFormatter = date => { const formatter = new Intl.DateTimeFormat(calendar.params.locale, calendar.params.timePickerFormat); return formatter.format(date); }; const timeFormatCheckDate = calendar.timeSelectorFormatter(new Date()).toLowerCase(); calendar.is12HoursFormat = timeFormatCheckDate.indexOf('pm') >= 0 || timeFormatCheckDate.indexOf('am') >= 0; // Auto names let { monthNames, monthNamesShort, dayNames, dayNamesShort } = calendar.params; const { monthNamesIntl, monthNamesShortIntl, dayNamesIntl, dayNamesShortIntl } = calendar.getIntlNames(); if (monthNames === 'auto') monthNames = monthNamesIntl; if (monthNamesShort === 'auto') monthNamesShort = monthNamesShortIntl; if (dayNames === 'auto') dayNames = dayNamesIntl; if (dayNamesShort === 'auto') dayNamesShort = dayNamesShortIntl; extend(calendar, { monthNames, monthNamesShort, dayNames, dayNamesShort }); function onInputClick() { calendar.open(); } function onInputFocus(e) { e.preventDefault(); } function onInputClear() { calendar.setValue([]); if (calendar.opened) { calendar.update(); } } function onHtmlClick(e) { const $targetEl = $(e.target); if (calendar.destroyed || !calendar.params) return; if (calendar.isPopover()) return; if (!calendar.opened || calendar.closing) return; if ($targetEl.closest('[class*="backdrop"]').length) return; if (calendar.monthPickerPopover || calendar.yearPickerPopover || calendar.timePickerPopover) return; if ($inputEl && $inputEl.length > 0) { if ($targetEl[0] !== $inputEl[0] && $targetEl.closest('.sheet-modal, .calendar-modal').length === 0) { calendar.close(); } } else if ($(e.target).closest('.sheet-modal, .calendar-modal').length === 0) { calendar.close(); } } // Events extend(calendar, { attachInputEvents() { calendar.$inputEl.on('click', onInputClick); calendar.$inputEl.on('input:clear', onInputClear); if (calendar.params.inputReadOnly) { calendar.$inputEl.on('focus mousedown', onInputFocus); if (calendar.$inputEl[0]) { calendar.$inputEl[0].f7ValidateReadonly = true; } } }, detachInputEvents() { calendar.$inputEl.off('click', onInputClick); calendar.$inputEl.off('input:clear', onInputClear); if (calendar.params.inputReadOnly) { calendar.$inputEl.off('focus mousedown', onInputFocus); if (calendar.$inputEl[0]) { delete calendar.$inputEl[0].f7ValidateReadonly; } } }, attachHtmlEvents() { app.on('click', onHtmlClick); }, detachHtmlEvents() { app.off('click', onHtmlClick); } }); calendar.attachCalendarEvents = function attachCalendarEvents() { let allowItemClick = true; let isTouched; let isMoved; let touchStartX; let touchStartY; let touchCurrentX; let touchCurrentY; let touchStartTime; let touchEndTime; let currentTranslate; let wrapperWidth; let wrapperHeight; let percentage; let touchesDiff; let isScrolling; const { $el, $wrapperEl } = calendar; function handleTouchStart(e) { if (isMoved || isTouched || !e.isTrusted) return; isTouched = true; touchStartX = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX; touchCurrentX = touchStartX; touchStartY = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY; touchCurrentY = touchStartY; touchStartTime = new Date().getTime(); percentage = 0; allowItemClick = true; isScrolling = undefined; currentTranslate = calendar.monthsTranslate; } function handleTouchMove(e) { if (!isTouched || !e.isTrusted) return; const { isHorizontal: isH } = calendar; touchCurrentX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX; touchCurrentY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY; if (typeof isScrolling === 'undefined') { isScrolling = !!(isScrolling || Math.abs(touchCurrentY - touchStartY) > Math.abs(touchCurrentX - touchStartX)); } if (isH && isScrolling || !calendar.allowTouchMove) { isTouched = false; return; } e.preventDefault(); if (calendar.animating) { isTouched = false; return; } allowItemClick = false; if (!isMoved) { // First move isMoved = true; wrapperWidth = $wrapperEl[0].offsetWidth; wrapperHeight = $wrapperEl[0].offsetHeight; $wrapperEl.transition(0); } touchesDiff = isH ? touchCurrentX - touchStartX : touchCurrentY - touchStartY; percentage = touchesDiff / (isH ? wrapperWidth : wrapperHeight); currentTranslate = (calendar.monthsTranslate * calendar.inverter + percentage) * 100; // Transform wrapper $wrapperEl.transform(`translate3d(${isH ? currentTranslate : 0}%, ${isH ? 0 : currentTranslate}%, 0)`); } function handleTouchEnd(e) { if (!isTouched || !isMoved || !e.isTrusted) { isTouched = false; isMoved = false; return; } isTouched = false; isMoved = false; touchEndTime = new Date().getTime(); if (touchEndTime - touchStartTime < 300) { if (Math.abs(touchesDiff) < 10) { calendar.resetMonth(); } else if (touchesDiff >= 10) { if (app.rtl) calendar.nextMonth();else calendar.prevMonth(); } else if (app.rtl) calendar.prevMonth();else calendar.nextMonth(); } else if (percentage <= -0.5) { if (app.rtl) calendar.prevMonth();else calendar.nextMonth(); } else if (percentage >= 0.5) { if (app.rtl) calendar.nextMonth();else calendar.prevMonth(); } else { calendar.resetMonth(); } // Allow click setTimeout(() => { allowItemClick = true; }, 100); } function handleDayClick(e) { if (!allowItemClick) return; let $dayEl = $(e.target).parents('.calendar-day'); if ($dayEl.length === 0 && $(e.target).hasClass('calendar-day')) { $dayEl = $(e.target); } if ($dayEl.length === 0) return; if ($dayEl.hasClass('calendar-day-disabled')) return; if (!calendar.params.rangePicker) { if ($dayEl.hasClass('calendar-day-next')) calendar.nextMonth(); if ($dayEl.hasClass('calendar-day-prev')) calendar.prevMonth(); } const dateYear = parseInt($dayEl.attr('data-year'), 10); const dateMonth = parseInt($dayEl.attr('data-month'), 10); const dateDay = parseInt($dayEl.attr('data-day'), 10); calendar.emit('local::dayClick calendarDayClick', calendar, $dayEl[0], dateYear, dateMonth, dateDay); if (!$dayEl.hasClass('calendar-day-selected') || calendar.params.multiple || calendar.params.rangePicker) { const valueToAdd = new Date(dateYear, dateMonth, dateDay, 0, 0, 0); if (calendar.hasTimePicker) { if (calendar.value && calendar.value[0]) { valueToAdd.setHours(calendar.value[0].getHours(), calendar.value[0].getMinutes()); } else { valueToAdd.setHours(new Date().getHours(), new Date().getMinutes()); } } calendar.addValue(valueToAdd); } if (calendar.params.closeOnSelect) { if (calendar.params.rangePicker && calendar.value.length === 2 || !calendar.params.rangePicker) { calendar.close(); } } } function onNextMonthClick() { calendar.nextMonth(); } function onPrevMonthClick() { calendar.prevMonth(); } function onNextYearClick() { calendar.nextYear(); } function onPrevYearClick() { calendar.prevYear(); } function onMonthSelectorClick() { calendar.openMonthPicker(); } function onYearSelectorClick() { calendar.openYearPicker(); } function onTimeSelectorClick() { calendar.openTimePicker(); } const passiveListener = app.touchEvents.start === 'touchstart' && getSupport().passiveListener ? { passive: true, capture: false } : false; // Selectors clicks $el.find('.calendar-prev-month-button').on('click', onPrevMonthClick); $el.find('.calendar-next-month-button').on('click', onNextMonthClick); $el.find('.calendar-prev-year-button').on('click', onPrevYearClick); $el.find('.calendar-next-year-button').on('click', onNextYearClick); if (calendar.params.monthPicker) { $el.find('.current-month-value').on('click', onMonthSelectorClick); } if (calendar.params.yearPicker) { $el.find('.current-year-value').on('click', onYearSelectorClick); } if (calendar.hasTimePicker) { $el.find('.calendar-time-selector a').on('click', onTimeSelectorClick); } // Day clicks $wrapperEl.on('click', handleDayClick); // Touch events if (calendar.params.touchMove) { $wrapperEl.on(app.touchEvents.start, handleTouchStart, passiveListener); app.on('touchmove:active', handleTouchMove); app.on('touchend:passive', handleTouchEnd); } calendar.detachCalendarEvents = function detachCalendarEvents() { $el.find('.calendar-prev-month-button').off('click', onPrevMonthClick); $el.find('.calendar-next-month-button').off('click', onNextMonthClick); $el.find('.calendar-prev-year-button').off('click', onPrevYearClick); $el.find('.calendar-next-year-button').off('click', onNextYearClick); if (calendar.params.monthPicker) { $el.find('.current-month-value').off('click', onMonthSelectorClick); } if (calendar.params.yearPicker) { $el.find('.current-year-value').off('click', onYearSelectorClick); } if (calendar.hasTimePicker) { $el.find('.calendar-time-selector a').off('click', onTimeSelectorClick); } $wrapperEl.off('click', handleDayClick); if (calendar.params.touchMove) { $wrapperEl.off(app.touchEvents.start, handleTouchStart, passiveListener); app.off('touchmove:active', handleTouchMove); app.off('touchend:passive', handleTouchEnd); } }; }; calendar.init(); return calendar; } get view() { const { $inputEl, app, params } = this; let view; if (params.view) { view = params.view; } else if ($inputEl) { view = $inputEl.parents('.view').length && $inputEl.parents('.view')[0].f7View; } if (!view) view = app.views.main; return view; } getIntlNames() { const calendar = this; const locale = calendar.params.locale; const monthNamesIntl = []; const monthNamesShortIntl = []; const dayNamesIntl = []; const dayNamesShortIntl = []; const formatterMonthNames = new Intl.DateTimeFormat(locale, { month: 'long' }); const formatterMonthNamesShort = new Intl.DateTimeFormat(locale, { month: 'short' }); const formatterDayNames = new Intl.DateTimeFormat(locale, { weekday: 'long' }); const formatterDayNamesShort = new Intl.DateTimeFormat(locale, { weekday: 'short' }); let year; let yearStarted; let yearEnded; for (let i = 0; i < 24; i += 1) { const date = new Date().setMonth(i, 1); const currentYear = calendar.yearFormatter(date); if (year && currentYear !== year) { if (yearStarted) yearEnded = true; yearStarted = true; year = currentYear; } if (!year) { year = currentYear; } if (yearStarted && year === currentYear && !yearEnded) { monthNamesIntl.push(formatterMonthNames.format(date)); monthNamesShortIntl.push(formatterMonthNamesShort.format(date)); } } const weekDay = new Date().getDay(); for (let i = 0; i < 7; i += 1) { const date = new Date().getTime() + (i - weekDay) * 24 * 60 * 60 * 1000; dayNamesIntl.push(formatterDayNames.format(date)); dayNamesShortIntl.push(formatterDayNamesShort.format(date)); } return { monthNamesIntl, monthNamesShortIntl, dayNamesIntl, dayNamesShortIntl }; } normalizeDate(date) { const calendar = this; const d = new Date(date); if (calendar.hasTimePicker) { return new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes()); } return new Date(d.getFullYear(), d.getMonth(), d.getDate()); } normalizeValues(values) { const calendar = this; let newValues = []; if (values && Array.isArray(values)) { newValues = values.map(val => calendar.normalizeDate(val)); } return newValues; } initInput() { const calendar = this; if (!calendar.$inputEl) return; if (calendar.params.inputReadOnly) calendar.$inputEl.prop('readOnly', true); } isPopover() { const calendar = this; const { app, modal, params } = calendar; const device = getDevice(); if (params.openIn === 'sheet') return false; if (modal && modal.type !== 'popover') return false; if (!calendar.inline && calendar.inputEl) { if (params.openIn === 'popover') return true; if (device.ios) { return !!device.ipad; } if (app.width >= 768) { return true; } } return false; } formatDate(d) { const calendar = this; const date = new Date(d); const year = date.getFullYear(); const month = date.getMonth(); const month1 = month + 1; const day = date.getDate(); const weekDay = date.getDay(); const { monthNames, monthNamesShort, dayNames, dayNamesShort } = calendar; const { dateFormat, locale } = calendar.params; function twoDigits(number) { return number < 10 ? `0${number}` : number; } if (typeof dateFormat === 'string') { const tokens = { yyyy: year, yy: String(year).substring(2), mm: twoDigits(month1), m: month1, MM: monthNames[month], M: monthNamesShort[month], dd: twoDigits(day), d: day, DD: dayNames[weekDay], D: dayNamesShort[weekDay] }; if (calendar.params.timePicker) { const hours = date.getHours(); const minutes = date.getMinutes(); const seconds = date.getSeconds(); let hours12 = hours; if (hours > 12) hours12 = hours - 12; if (hours === 0) hours12 = 12; const a = hours >= 12 && hours !== 0 ? 'pm' : 'am'; Object.assign(tokens, { HH: twoDigits(hours), H: hours, hh: twoDigits(hours12), h: hours12, ss: twoDigits(seconds), s: seconds, ':mm': twoDigits(minutes), ':m': minutes, a, A: a.toUpperCase() }); } const regexp = new RegExp(Object.keys(tokens).map(t => `(${t})`).join('|'), 'g'); return dateFormat.replace(regexp, token => { if (token in tokens) return tokens[token]; return token; }); } if (typeof dateFormat === 'function') { return dateFormat(date); } // Intl Object const formatter = new Intl.DateTimeFormat(locale, dateFormat); return formatter.format(date); } formatValue() { const calendar = this; const { value } = calendar; if (calendar.params.formatValue) { return calendar.params.formatValue.call(calendar, value); } return value.map(v => calendar.formatDate(v)).join(calendar.params.rangePicker ? ' - ' : ', '); } addValue(newValue) { const calendar = this; const { multiple, rangePicker, rangePickerMinDays, rangePickerMaxDays } = calendar.params; if (multiple) { if (!calendar.value) calendar.value = []; let inValuesIndex; for (let i = 0; i < calendar.value.length; i += 1) { if (new Date(newValue).getTime() === new Date(calendar.value[i]).getTime()) { inValuesIndex = i; } } if (typeof inValuesIndex === 'undefined') { calendar.value.push(newValue); } else { calendar.value.splice(inValuesIndex, 1); } calendar.updateValue(); } else if (rangePicker) { if (!calendar.value) calendar.value = []; if (calendar.value.length === 2 || calendar.value.length === 0) { calendar.value = []; } if (calendar.value.length === 0 || Math.abs(calendar.value[0].getTime() - newValue.getTime()) >= (rangePickerMinDays - 1) * 60 * 60 * 24 * 1000 && (rangePickerMaxDays === 0 || Math.abs(calendar.value[0].getTime() - newValue.getTime()) <= (rangePickerMaxDays - 1) * 60 * 60 * 24 * 1000)) calendar.value.push(newValue);else calendar.value = []; calendar.value.sort((a, b) => a - b); calendar.updateValue(); } else { calendar.value = [newValue]; calendar.updateValue(); } } setValue(values) { const calendar = this; const currentValue = calendar.value; if (Array.isArray(currentValue) && Array.isArray(values) && currentValue.length === values.length) { let equal = true; currentValue.forEach((v, index) => { if (v !== values[index]) equal = false; }); if (equal) return; } calendar.value = values; calendar.updateValue(); } getValue() { const calendar = this; return calendar.value; } updateValue(onlyHeader) { const calendar = this; const { $el, $wrapperEl, $inputEl, value, params } = calendar; let i; if ($el && $el.length > 0) { $wrapperEl.find('.calendar-day-selected').removeClass('calendar-day-selected calendar-day-selected-range calendar-day-selected-left calendar-day-selected-right'); let valueDate; if (params.rangePicker && value.length === 2) { const leftDate = new Date(value[0]).getTime(); const rightDate = new Date(value[1]).getTime(); for (i = leftDate; i <= rightDate; i += 24 * 60 * 60 * 1000) { valueDate = new Date(i); let addClass = 'calendar-day-selected'; if (leftDate !== rightDate) { if (i !== leftDate && i !== rightDate) { addClass += ' calendar-day-selected-range'; } if (i === leftDate) { addClass += ' calendar-day-selected-left'; } if (i === rightDate) { addClass += ' calendar-day-selected-right'; } } $wrapperEl.find(`.calendar-day[data-date="${valueDate.getFullYear()}-${valueDate.getMonth()}-${valueDate.getDate()}"]`).addClass(addClass); } valueDate = new Date(leftDate); $wrapperEl.find(`.calendar-day[data-date="${valueDate.getFullYear()}-${valueDate.getMonth()}-${valueDate.getDate()}"]`).removeClass('calendar-day-selected-range').addClass('calendar-day-selected calendar-day-selected-left'); valueDate = new Date(rightDate); $wrapperEl.find(`.calendar-day[data-date="${valueDate.getFullYear()}-${valueDate.getMonth()}-${valueDate.getDate()}"]`).removeClass('calendar-day-selected-range').addClass('calendar-day-selected calendar-day-selected-right'); } else { for (i = 0; i < calendar.value.length; i += 1) { valueDate = new Date(value[i]); $wrapperEl.find(`.calendar-day[data-date="${valueDate.getFullYear()}-${valueDate.getMonth()}-${valueDate.getDate()}"]`).addClass('calendar-day-selected'); } } } if (!onlyHeader) { calendar.emit('local::change calendarChange', calendar, value); } if ($el && $el.length > 0 && calendar.hasTimePicker) { $el.find('.calendar-time-selector a').text(value && value.length ? calendar.timeSelectorFormatter(value[0]) : calendar.params.timePickerPlaceholder); } if ($inputEl && $inputEl.length || params.header) { const inputValue = calendar.formatValue(value); if (params.header && $el && $el.length) { $el.find('.calendar-selected-date').text(inputValue); } if ($inputEl && $inputEl.length && !onlyHeader) { $inputEl.val(inputValue); $inputEl.trigger('change'); } } } updateCurrentMonthYear(dir) { const calendar = this; const { $months, $el, monthNames } = calendar; let currentLocaleMonth; let currentLocaleYear; if (typeof dir === 'undefined') { calendar.currentMonth = parseInt($months.eq(1).attr('data-month'), 10); calendar.currentYear = parseInt($months.eq(1).attr('data-year'), 10); currentLocaleMonth = $months.eq(1).attr('data-locale-month'); currentLocaleYear = $months.eq(1).attr('data-locale-year'); } else { calendar.currentMonth = parseInt($months.eq(dir === 'next' ? $months.length - 1 : 0).attr('data-month'), 10); calendar.currentYear = parseInt($months.eq(dir === 'next' ? $months.length - 1 : 0).attr('data-year'), 10); currentLocaleMonth = $months.eq(dir === 'next' ? $months.length - 1 : 0).attr('data-locale-month'); currentLocaleYear = $months.eq(dir === 'next' ? $months.length - 1 : 0).attr('data-locale-year'); } $el.find('.current-month-value').text(monthNames[currentLocaleMonth]); $el.find('.current-year-value').text(currentLocaleYear); } update() { const calendar = this; const { currentYear, currentMonth, $wrapperEl } = calendar; const currentDate = new Date(currentYear, currentMonth); const prevMonthHtml = calendar.renderMonth(currentDate, 'prev'); const currentMonthHtml = calendar.renderMonth(currentDate); const nextMonthHtml = calendar.renderMonth(currentDate, 'next'); $wrapperEl.transition(0).html(`${prevMonthHtml}${currentMonthHtml}${nextMonthHtml}`).transform('translate3d(0,0,0)'); calendar.$months = $wrapperEl.find('.calendar-month'); calendar.monthsTranslate = 0; calendar.setMonthsTranslate(); calendar.$months.each(monthEl => { calendar.emit('local::monthAdd calendarMonthAdd', monthEl); }); } onMonthChangeStart(dir) { const calendar = this; const { $months, currentYear, currentMonth } = calendar; calendar.updateCurrentMonthYear(dir); $months.removeClass('calendar-month-current calendar-month-prev calendar-month-next'); const currentIndex = dir === 'next' ? $months.length - 1 : 0; $months.eq(currentIndex).addClass('calendar-month-current'); $months.eq(dir === 'next' ? currentIndex - 1 : currentIndex + 1).addClass(dir === 'next' ? 'calendar-month-prev' : 'calendar-month-next'); calendar.emit('local::monthYearChangeStart calendarMonthYearChangeStart', calendar, currentYear, currentMonth); } onMonthChangeEnd(dir, rebuildBoth) { const calendar = this; const { currentYear, currentMonth, $wrapperEl, monthsTranslate } = calendar; calendar.animating = false; let nextMonthHtml; let prevMonthHtml; let currentMonthHtml; $wrapperEl.find('.calendar-month:not(.calendar-month-prev):not(.calendar-month-current):not(.calendar-month-next)').remove(); if (typeof dir === 'undefined') { dir = 'next'; // eslint-disable-line rebuildBoth = true; // eslint-disable-line } if (!rebuildBoth) { currentMonthHtml = calendar.renderMonth(new Date(currentYear, currentMonth), dir); } else { $wrapperEl.find('.calendar-month-next, .calendar-month-prev').remove(); prevMonthHtml = calendar.renderMonth(new Date(currentYear, currentMonth), 'prev'); nextMonthHtml = calendar.renderMonth(new Date(currentYear, currentMonth), 'next'); } if (dir === 'next' || rebuildBoth) { $wrapperEl.append(currentMonthHtml || nextMonthHtml); } if (dir === 'prev' || rebuildBoth) { $wrapperEl.prepend(currentMonthHtml || prevMonthHtml); } const $months = $wrapperEl.find('.calendar-month'); calendar.$months = $months; calendar.setMonthsTranslate(monthsTranslate); calendar.emit('local::monthAdd calendarMonthAdd', calendar, dir === 'next' ? $months.eq($months.length - 1)[0] : $months.eq(0)[0]); calendar.emit('local::monthYearChangeEnd calendarMonthYearChangeEnd', calendar, currentYear, currentMonth); } setMonthsTranslate(translate) { const calendar = this; const { $months, isHorizontal: isH, inverter } = calendar; // eslint-disable-next-line translate = translate || calendar.monthsTranslate || 0; if (typeof calendar.monthsTranslate === 'undefined') { calendar.monthsTranslate = translate; } $months.removeClass('calendar-month-current calendar-month-prev calendar-month-next'); const prevMonthTranslate = -(translate + 1) * 100 * inverter; const currentMonthTranslate = -translate * 100 * inverter; const nextMonthTranslate = -(translate - 1) * 100 * inverter; $months.eq(0).transform(`translate3d(${isH ? prevMonthTranslate : 0}%, ${isH ? 0 : prevMonthTranslate}%, 0)`).addClass('calendar-month-prev'); $months.eq(1).transform(`translate3d(${isH ? currentMonthTranslate : 0}%, ${isH ? 0 : currentMonthTranslate}%, 0)`).addClass('calendar-month-current'); $months.eq(2).transform(`translate3d(${isH ? nextMonthTranslate : 0}%, ${isH ? 0 : nextMonthTranslate}%, 0)`).addClass('calendar-month-next'); } nextMonth(transition) { const calendar = this; const { params, $wrapperEl, inverter, isHorizontal: isH } = calendar; if (typeof transition === 'undefined' || typeof transition === 'object') { transition = ''; // eslint-disable-line if (!params.animate) transition = 0; // eslint-disable-line } const nextMonth = parseInt(calendar.$months.eq(calendar.$months.length - 1).attr('data-month'), 10); const nextYear = parseInt(calendar.$months.eq(calendar.$months.length - 1).attr('data-year'), 10); const nextDate = new Date(nextYear, nextMonth); const nextDateTime = nextDate.getTime(); const transitionEndCallback = !calendar.animating; if (params.maxDate) { if (nextDateTime > new Date(params.maxDate).getTime()) { calendar.resetMonth(); return; } } calendar.monthsTranslate -= 1; if (nextMonth === calendar.currentMonth) { const nextMonthTranslate = -calendar.monthsTranslate * 100 * inverter; const nextMonthHtml = $(calendar.renderMonth(nextDateTime, 'next')).transform(`translate3d(${isH ? nextMonthTranslate : 0}%, ${isH ? 0 : nextMonthTranslate}%, 0)`).addClass('calendar-month-next'); $wrapperEl.append(nextMonthHtml[0]); calendar.$months = $wrapperEl.find('.calendar-month'); calendar.emit('local::monthAdd calendarMonthAdd', calendar.$months.eq(calendar.$months.length - 1)[0]); } calendar.animating = true; calendar.onMonthChangeStart('next'); const translate = calendar.monthsTranslate * 100 * inverter; $wrapperEl.transition(transition).transform(`translate3d(${isH ? translate : 0}%, ${isH ? 0 : translate}%, 0)`); if (transitionEndCallback) { $wrapperEl.transitionEnd(() => { calendar.onMonthChangeEnd('next'); }); } if (!params.animate) { calendar.onMonthChangeEnd('next'); } } prevMonth(transition) { const calendar = this; const { params, $wrapperEl, inverter, isHorizontal: isH } = calendar; if (typeof transition === 'undefined' || typeof transition === 'object') { transition = ''; // eslint-disable-line if (!params.animate) transition = 0; // eslint-disable-line } const prevMonth = parseInt(calendar.$months.eq(0).attr('data-month'), 10); const prevYear = parseInt(calendar.$months.eq(0).attr('data-year'), 10); const prevDate = new Date(prevYear, prevMonth + 1, -1); const prevDateTime = prevDate.getTime(); const transitionEndCallback = !calendar.animating; if (params.minDate) { let minDate = new Date(params.minDate); minDate = new Date(minDate.getFullYear(), minDate.getMonth(), 1); if (prevDateTime < minDate.getTime()) { calendar.resetMonth(); return; } } calendar.monthsTranslate += 1; if (prevMonth === calendar.currentMonth) { const prevMonthTranslate = -calendar.monthsTranslate * 100 * inverter; const prevMonthHtml = $(calendar.renderMonth(prevDateTime, 'prev')).transform(`translate3d(${isH ? prevMonthTranslate : 0}%, ${isH ? 0 : prevMonthTranslate}%, 0)`).addClass('calendar-month-prev'); $wrapperEl.prepend(prevMonthHtml[0]); calendar.$months = $wrapperEl.find('.calendar-month'); calendar.emit('local::monthAdd calendarMonthAdd', calendar.$months.eq(0)[0]); } calendar.animating = true; calendar.onMonthChangeStart('prev'); const translate = calendar.monthsTranslate * 100 * inverter; $wrapperEl.transition(transition).transform(`translate3d(${isH ? translate : 0}%, ${isH ? 0 : translate}%, 0)`); if (transitionEndCallback) { $wrapperEl.transitionEnd(() => { calendar.onMonthChangeEnd('prev'); }); } if (!params.animate) { calendar.onMonthChangeEnd('prev'); } } resetMonth(transition) { if (transition === void 0) { transition = ''; } const calendar = this; const { $wrapperEl, inverter, isHorizontal: isH, monthsTranslate } = calendar; const translate = monthsTranslate * 100 * inverter; $wrapperEl.transition(transition).transform(`translate3d(${isH ? translate : 0}%, ${isH ? 0 : translate}%, 0)`); } // eslint-disable-next-line setYearMonth(year, month, transition) { const calendar = this; const { params, isHorizontal: isH, $wrapperEl, inverter } = calendar; // eslint-disable-next-line if (typeof year === 'undefined') year = calendar.currentYear; // eslint-disable-next-line if (typeof month === 'undefined') month = calendar.currentMonth; if (typeof transition === 'undefined' || typeof transition === 'object') { // eslint-disable-next-line transition = ''; // eslint-disable-next-line if (!params.animate) transition = 0; } let targetDate; if (year < calendar.currentYear) { targetDate = new Date(year, month + 1, -1).getTime(); } else { targetDate = new Date(year, month).getTime(); } if (params.maxDate && targetDate > new Date(params.maxDate).getTime()) { return false; } if (params.minDate) { let minDate = new Date(params.minDate); minDate = new Date(minDate.getFullYear(), minDate.getMonth(), 1); if (targetDate < minDate.getTime()) { return false; } } const currentDate = new Date(calendar.currentYear, calendar.currentMonth).getTime(); const dir = targetDate > currentDate ? 'next' : 'prev'; const newMonthHTML = calendar.renderMonth(new Date(year, month)); calendar.monthsTranslate = calendar.monthsTranslate || 0; const prevTranslate = calendar.monthsTranslate; let monthTranslate; const transitionEndCallback = !calendar.animating && transition !== 0; if (targetDate > currentDate) { // To next calendar.monthsTranslate -= 1; if (!calendar.animating) calendar.$months.eq(calendar.$months.length - 1).remove(); $wrapperEl.append(newMonthHTML); calendar.$months = $wrapperEl.find('.calendar-month'); monthTranslate = -(prevTranslate - 1) * 100 * inverter; calendar.$months.eq(calendar.$months.length - 1).transform(`translate3d(${isH ? monthTranslate : 0}%, ${isH ? 0 : monthTranslate}%, 0)`).addClass('calendar-month-next'); } else { // To prev calendar.monthsTranslate += 1; if (!calendar.animating) calendar.$months.eq(0).remove(); $wrapperEl.prepend(newMonthHTML); calendar.$months = $wrapperEl.find('.calendar-month'); monthTranslate = -(prevTranslate + 1) * 100 * inverter; calendar.$months.eq(0).transform(`translate3d(${isH ? monthTranslate : 0}%, ${isH ? 0 : monthTranslate}%, 0)`).addClass('calendar-month-prev'); } calendar.emit('local::monthAdd calendarMonthAdd', dir === 'next' ? calendar.$months.eq(calendar.$months.length - 1)[0] : calendar.$months.eq(0)[0]); calendar.animating = true; calendar.onMonthChangeStart(dir); const wrapperTranslate = calendar.monthsTranslate * 100 * inverter; $wrapperEl.transition(transition).transform(`translate3d(${isH ? wrapperTranslate : 0}%, ${isH ? 0 : wrapperTranslate}%, 0)`); if (transitionEndCallback) { $wrapperEl.transitionEnd(() => { calendar.onMonthChangeEnd(dir, true); }); } if (!params.animate || transition === 0) { calendar.onMonthChangeEnd(dir, true); } } nextYear() { const calendar = this; calendar.setYearMonth(calendar.currentYear + 1); } prevYear() { const calendar = this; calendar.setYearMonth(calendar.currentYear - 1); } // eslint-disable-next-line dateInRange(dayDate, range) { let match = false; let i; if (!range) return false; if (Array.isArray(range)) { for (i = 0; i < range.length; i += 1) { if (range[i].from || range[i].to) { if (range[i].from && range[i].to) { if (dayDate <= new Date(range[i].to).getTime() && dayDate >= new Date(range[i].from).getTime()) { match = true; } } else if (range[i].from) { if (dayDate >= new Date(range[i].from).getTime()) { match = true; } } else if (range[i].to) { if (dayDate <= new Date(range[i].to).getTime()) { match = true; } } } else if (range[i].date) { if (dayDate === new Date(range[i].date).getTime()) { match = true; } } else if (dayDate === new Date(range[i]).getTime()) { match = true; } } } else if (range.from || range.to) { if (range.from && range.to) { if (dayDate <= new Date(range.to).getTime() && dayDate >= new Date(range.from).getTime()) { match = true; } } else if (range.from) { if (dayDate >= new Date(range.from).getTime()) { match = true; } } else if (range.to) { if (dayDate <= new Date(range.to).getTime()) { match = true; } } } else if (range.date) { match = dayDate === new Date(range.date).getTime(); } else if (typeof range === 'function') { match = range(new Date(dayDate)); } return match; } // eslint-disable-next-line daysInMonth(date) { const d = new Date(date); return new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate(); } renderMonths(date) { const calendar = this; if (calendar.params.renderMonths) { return calendar.params.renderMonths.call(calendar, date); } return $jsx("div", { class: "calendar-months-wrapper" }, calendar.renderMonth(date, 'prev'), calendar.renderMonth(date), calendar.renderMonth(date, 'next')); } renderMonth(d, offset) { const calendar = this; const { params, value } = calendar; if (params.renderMonth) { return params.renderMonth.call(calendar, d, offset); } let date = new Date(d); let year = date.getFullYear(); let month = date.getMonth(); let localeMonth = calendar.monthNames.indexOf(calendar.monthFormatter(date)); if (localeMonth < 0) localeMonth = month; let localeYear = calendar.yearFormatter(date); if (offset === 'next') { if (month === 11) date = new Date(year + 1, 0);else date = new Date(year, month + 1, 1); } if (offset === 'prev') { if (month === 0) date = new Date(year - 1, 11);else date = new Date(year, month - 1, 1); } if (offset === 'next' || offset === 'prev') { month = date.getMonth(); year = date.getFullYear(); localeMonth = calendar.monthNames.indexOf(calendar.monthFormatter(date)); if (localeMonth < 0) localeMonth = month; localeYear = calendar.yearFormatter(date); } const currentValues = []; const today = new Date().setHours(0, 0, 0, 0); const minDate = params.minDate ? new Date(params.minDate).getTime() : null; const maxDate = params.maxDate ? new Date(params.maxDate).getTime() : null; const rows = 6; const cols = 7; const daysInPrevMonth = calendar.daysInMonth(new Date(date.getFullYear(), date.getMonth()).getTime() - 10 * 24 * 60 * 60 * 1000); const daysInMonth = calendar.daysInMonth(date); const minDayNumber = params.firstDay === 6 ? 0 : 1; let monthHtml = ''; let dayIndex = 0 + (params.firstDay - 1); let disabled; let hasEvents; let firstDayOfMonthIndex = new Date(date.getFullYear(), date.getMonth()).getDay(); if (firstDayOfMonthIndex === 0) firstDayOfMonthIndex = 7; if (value && value.length) { for (let i = 0; i < value.length; i += 1) { currentValues.push(new Date(value[i]).setHours(0, 0, 0, 0)); } } for (let row = 1; row <= rows; row += 1) { let rowHtml = ''; for (let col = 1; col <= cols; col += 1) { dayIndex += 1; let dayDate; let dayNumber = dayIndex - firstDayOfMonthIndex; let addClass = ''; if (row === 1 && col === 1 && dayNumber > minDayNumber && params.firstDay !== 1) { dayIndex -= 7; dayNumber = dayIndex - firstDayOfMonthIndex; } const weekDayIndex = col - 1 + params.firstDay > 6 ? col - 1 - 7 + params.firstDay : col - 1 + params.firstDay; if (dayNumber < 0) { dayNumber = daysInPrevMonth + dayNumber + 1; addClass += ' calendar-day-prev'; dayDate = new Date(month - 1 < 0 ? year - 1 : year, month - 1 < 0 ? 11 : month - 1, dayNumber).getTime(); } else { dayNumber += 1; if (dayNumber > daysInMonth) { dayNumber -= daysInMonth; addClass += ' calendar-day-next'; dayDate = new Date(month + 1 > 11 ? year + 1 : year, month + 1 > 11 ? 0 : month + 1, dayNumber).getTime(); } else { dayDate = new Date(year, month, dayNumber).getTime(); } } // Today if (dayDate === today) addClass += ' calendar-day-today'; // Selected if (params.rangePicker && currentValues.length === 2) { if (dayDate >= currentValues[0] && dayDate <= currentValues[1]) { addClass += ' calendar-day-selected'; } if (currentValues[0] !== currentValues[1]) { if (dayDate > currentValues[0] && dayDate < currentValues[1]) { addClass += ' calendar-day-selected-range'; } if (dayDate === currentValues[0]) { addClass += ' calendar-day-selected-left'; } if (dayDate === currentValues[1]) { addClass += ' calendar-day-selected-right'; } } } else if (currentValues.indexOf(dayDate) >= 0) addClass += ' calendar-day-selected'; // Weekend if (params.weekendDays.indexOf(weekDayIndex) >= 0) { addClass += ' calendar-day-weekend'; } // Events let eventsHtml = ''; hasEvents = false; if (params.events) { if (calendar.dateInRange(dayDate, params.events)) { hasEvents = true; } } if (hasEvents) { addClass += ' calendar-day-has-events'; // prettier-ignore eventsHtml = ` <span class="calendar-day-events"> <span class="calendar-day-event"></span> </span> `; if (Array.isArray(params.events)) { const eventDots = []; params.events.forEach(ev => { const color = ev.color || ''; if (eventDots.indexOf(color) < 0 && calendar.dateInRange(dayDate, ev)) { eventDots.push(color); } }); // prettier-ignore eventsHtml = ` <span class="calendar-day-events"> ${eventDots.map(color => ` <span class="calendar-day-event" style="${color ? `background-color: ${color}` : ''}"></span> `.trim()).join('')} </span> `; } } // Custom Ranges if (params.rangesClasses) { for (let k = 0; k < params.rangesClasses.length; k += 1) { if (calendar.dateInRange(dayDate, params.rangesClasses[k].range)) { addClass += ` ${params.rangesClasses[k].cssClass}`; } } } // Disabled disabled = false; if (minDate && dayDate < minDate || maxDate && dayDate > maxDate) { disabled = true; } if (params.disabled) { if (calendar.dateInRange(dayDate, params.disabled)) { disabled = true; } } if (disabled) { addClass += ' calendar-day-disabled'; } dayDate = new Date(dayDate); const dayYear = dayDate.getFullYear(); const dayMonth = dayDate.getMonth(); const dayNumberDisplay = calendar.dayFormatter(dayDate); // prettier-ignore rowHtml += ` <div data-year="${dayYear}" data-month="${dayMonth}" data-day="${dayNumber}" class="calendar-day${addClass}" data-date="${dayYear}-${dayMonth}-${dayNumber}"> <span class="calendar-day-number">${dayNumberDisplay}${eventsHtml}</span> </div>`.trim(); } monthHtml += `<div class="calendar-row">${rowHtml}</div>`; } monthHtml = `<div class="calendar-month" data-year="${year}" data-month="${month}" data-locale-year="${localeYear}" data-locale-month="${localeMonth}">${monthHtml}</div>`; return monthHtml; } renderWeekHeader() { const calendar = this; if (calendar.params.renderWeekHeader) { return calendar.params.renderWeekHeader.call(calendar); } const { params } = calendar; let weekDaysHtml = ''; for (let i = 0; i < 7; i += 1) { const dayIndex = i + params.firstDay > 6 ? i - 7 + params.firstDay : i + params.firstDay; const dayName = calendar.dayNamesShort[dayIndex]; weekDaysHtml += `<div class="calendar-week-day">${dayName}</div>`; } return $jsx("div", { class: "calendar-week-header" }, weekDaysHtml); } renderMonthSelector() { const calendar = this; if (calendar.params.renderMonthSelector) { return calendar.params.renderMonthSelector.call(calendar); } return $jsx("div", { class: "calendar-month-selector" }, $jsx("a", { class: "link icon-only calendar-prev-month-button" }, $jsx("i", { class: "icon icon-prev" })), calendar.params.monthPicker ? $jsx("a", { class: "current-month-value link" }) : $jsx("span", { class: "current-month-value" }), $jsx("a", { class: "link icon-only calendar-next-month-button" }, $jsx("i", { class: "icon icon-next" }))); } renderYearSelector() { const calendar = this; if (calendar.params.renderYearSelector) { return calendar.params.renderYearSelector.call(calendar); } return $jsx("div", { class: "calendar-year-selector" }, $jsx("a", { class: "link icon-only calendar-prev-year-button" }, $jsx("i", { class: "icon icon-prev" })), calendar.params.yearPicker ? $jsx("a", { class: "current-year-value link" }) : $jsx("span", { class: "current-year-value" }), $jsx("a", { class: "link icon-only calendar-next-year-button" }, $jsx("i", { class: "icon icon-next" }))); } // eslint-disable-next-line renderTimeSelector() { const calendar = this; const value = calendar.value && calendar.value[0]; let timeString; if (value) timeString = calendar.timeSelectorFormatter(value); return $jsx("div", { class: "calendar-time-selector" }, $jsx("span", null, calendar.params.timePickerLabel), $jsx("a", { class: "link" }, timeString || calendar.params.timePickerPlaceholder)); } renderHeader() { const calendar = this; if (calendar.params.renderHeader) { return calendar.params.renderHeader.call(calendar); } return $jsx("div", { class: "calendar-header" }, $jsx("div", { class: "calendar-selected-date" }, calendar.params.headerPlaceholder)); } renderFooter() { const calendar = this; const app = calendar.app; if (calendar.params.renderFooter) { return calendar.params.renderFooter.call(calendar); } return $jsx("div", { class: "calendar-footer" }, $jsx("a", { class: `${app.theme === 'md' ? 'button button-round' : 'link'} calendar-close sheet-close popover-close` }, calendar.params.toolbarCloseText)); } renderToolbar() { const calendar = this; if (calendar.params.renderToolbar) { return calendar.params.renderToolbar.call(calendar, calendar); } // prettier-ignore return $jsx("div", { class: "toolbar toolbar-top" }, $jsx("div", { class: "toolbar-inner" }, calendar.params.monthSelector ? calendar.renderMonthSelector() : '', calendar.params.yearSelector ? calendar.renderYearSelector() : '')); } // eslint-disable-next-line renderInline() { const calendar = this; const { cssClass, toolbar, header, footer, rangePicker, weekHeader } = calendar.params; const { value, hasTimePicker } = calendar; const date = value && value.length ? value[0] : new Date().setHours(0, 0, 0); return $jsx("div", { class: `calendar calendar-inline ${rangePicker ? 'calendar-range' : ''} ${cssClass || ''}` }, header && calendar.renderHeader(), toolbar && calendar.renderToolbar(), weekHeader && calendar.renderWeekHeader(), $jsx("div", { class: "calendar-months" }, calendar.renderMonths(date)), hasTimePicker && calendar.renderTimeSelector(), footer && calendar.renderFooter()); } renderCustomModal() { const calendar = this; const { cssClass, toolbar, header, footer, rangePicker, weekHeader } = calendar.params; const { value, hasTimePicker } = calendar; const date = value && value.length ? value[0] : new Date().setHours(0, 0, 0); return $jsx("div", { class: `calendar calendar-modal ${rangePicker ? 'calendar-range' : ''} ${cssClass || ''}` }, header && calendar.renderHeader(), toolbar && calendar.renderToolbar(), weekHeader && calendar.renderWeekHeader(), $jsx("div", { class: "calendar-months" }, calendar.renderMonths(date)), hasTimePicker && calendar.renderTimeSelector(), footer && calendar.renderFooter()); } renderSheet() { const calendar = this; const { cssClass, toolbar, header, footer, rangePicker, weekHeader } = calendar.params; const { value, hasTimePicker