import supportDom from '../decorators/supportDom' import getFloatedTargetPos from '../utils/getFloatedTargetPos' import isTouchDevice from '../utils/isTouchDevice' import { DEFAULT_TIMEZONE, DEFAULT_LOCALE } from '../consts' import {
chunk, format, range, isFuture, setYear, setMonth, getYear, getMonth, addYears, toPixel, subYears, noop
} from '../utils'
@supportDom export default class MonthMenu {
constructor(options = {}) { const { dom, date } = options this.container = dom this.date = date this.menuDate = this.date || new Date() this.options = options this.tz = options.tz || DEFAULT_TIMEZONE this.locale = options.locale || DEFAULT_LOCALE this.noFuture = options.noFuture || true this.change = options.change || noop this.isVisible = false this.loopIndex = 0 this.init() } init() { this.addMenu() this.addEvents() } renderTableContent() { const { date, menuDate, locale, noFuture } = this const currentYear = date ? getYear(date) : null const currentMonth = date ? getMonth(date) : null return chunk(range(0, 12), 3) .map(months => { const tds = months.map(month => { const d = setMonth(menuDate, month) const text = format(d, 'MMM', { locale }) const isCurrentMonth = (currentYear === getYear(d)) && (currentMonth === getMonth(d)) let classname = 'cell' if (isCurrentMonth) { classname = 'cell selected-ex' } else if (noFuture && isFuture(d)) { classname = 'cell js-disabled' } return `<td class="${classname}" data-month-td="${month}">${text}</td>` }).join('') return `<tr>${tds}</tr>` }) .join('') } updateTableContent() { this.table.innerHTML = this.renderTableContent() } addMenu() { const { container } = this const dom = document.createElement('div') dom.classList.add('month-menu') if (container) { dom.classList.add('static') } const title = getYear(this.menuDate) dom.innerHTML = ` <div class="month-menu-content"> <div class="month-menu-caption"> <button class="month-menu-caption-btn" type="button" data-prev-month-btn> <i class="icon-chevron-left"></i> </button> <div data-month-menu-title>${title}</div> <button class="month-menu-caption-btn" type="button" data-next-month-btn> <i class="icon-chevron-right"></i> </button> </div> <table class="month-menu-table" data-month-table> ${this.renderTableContent()} </table> </div> ` if (container) { container.appendChild(dom) } else { document.body.appendChild(dom) } this.menuTitle = dom.querySelector('[data-month-menu-title]') this.prevBtn = dom.querySelector('[data-prev-month-btn]') this.nextBtn = dom.querySelector('[data-next-month-btn]') this.table = dom.querySelector('[data-month-table]') this.dom = dom } setTitle(date) { this.menuTitle.textContent = getYear(date) } addYear(year = 1) { this.menuDate = addYears(this.menuDate, year) this.setTitle(this.menuDate) this.updateTableContent() } addYearLoop() { const duration = (this.loopIndex === 0) ? 500 : 100 this.addYearTimer = setTimeout(() => { this.loopIndex += 1 const year = parseInt((this.loopIndex / 5) + 1, 10) this.addYear(year) this.addYearLoop() }, duration) } clearAddYearLoop() { clearTimeout(this.addYearTimer) this.loopIndex = 0 } subYear(year = 1) { this.menuDate = subYears(this.menuDate, year) this.setTitle(this.menuDate) this.updateTableContent() } subYearLoop() { const duration = (this.loopIndex === 0) ? 500 : 100 this.subYearTimer = setTimeout(() => { this.loopIndex += 1 const year = parseInt((this.loopIndex / 5) + 1, 10) const currentYear = getYear(this.menuDate) if ((currentYear - year) > 0) { this.subYear(year) this.subYearLoop() } }, duration) } clearSubYearLoop() { clearTimeout(this.subYearTimer) this.loopIndex = 0 } stop(event) { event.preventDefault() event.stopPropagation() } addEvents() { const isTouch = isTouchDevice() const downEvent = isTouch ? 'touchstart' : 'mousedown' this.addEvent(this.prevBtn, downEvent, event => { this.stop(event) const currentYear = getYear(this.menuDate) if ((currentYear - 1) <= 0) { return } this.subYear() this.subYearLoop() }) this.addEvent(this.nextBtn, 'click', event => event.stopPropagation()) this.addEvent(this.prevBtn, 'click', event => event.stopPropagation()) this.addEvent(this.nextBtn, downEvent, event => { this.stop(event) this.addYear() this.addYearLoop() }) const upEvent = isTouch ? 'touchend' : 'mouseup' this.addEvent(this.prevBtn, upEvent, event => { this.stop(event) this.clearSubYearLoop() }) this.addEvent(this.nextBtn, upEvent, event => { this.stop(event) this.clearAddYearLoop() }) this.addEvent(this.table, 'click', event => { this.stop(event) const { target } = event if ('monthTd' in target.dataset) { const year = getYear(this.menuDate) const month = parseInt(target.dataset.monthTd, 10) if (target.classList.contains('js-disabled')) { return } if (! this.date) { this.date = new Date(this.menuDate.getTime()) } this.date = setYear(this.date, year) this.date = setMonth(this.date, month) this.updateTableContent() this.emitChange() } }) } setDate(date) { this.date = date this.menuDate = date this.setTitle(date) this.updateTableContent() } emitChange() { this.change(this.date) } pos(src) { const { dom } = this const { pos } = getFloatedTargetPos({ src, target: dom, place: 'bottom', align: 'left', offset: 4 }) dom.style.left = toPixel(pos.left) dom.style.top = toPixel(pos.top) } show(src) { const { dom } = this dom.style.display = 'block' if (src) { this.pos(src) } dom.style.opacity = 1 this.isVisible = true } hide() { this.dom.style.display = 'none' this.isVisible = false } destroy() { this.menuTitle = null this.prevBtn = null this.nextBtn = null this.table = null this.dom.remove() }
}