/// import * as React from 'react'; import classNames from 'classnames'; import Hammer from 'react-hammerjs'; import Slider from '../slider'; import { setTranslate } from '../util/animation'; import { getDaysInMonth, zellerFormula, valueFormat } from '../util/date'; import WebApi from '../ajax/webapi'; const today = new Date(); /** * 日历起始年份 */ const startYear = 1950; const prefix = 'tsp-component-calendar'; class Calendar extends React.Component { constructor(props: TspComponentCalendarProps, state: any) { super(props, state); this.onPanStart = this.onPanStart.bind(this); this.onPan = this.onPan.bind(this); this.onPanEnd = this.onPanEnd.bind(this); this.onTransitionEnd = this.onTransitionEnd.bind(this); this.onClickWithDay = this.onClickWithDay.bind(this); } public static defaultProps: TspComponentCalendarProps = { width: document.body.clientWidth, height: document.documentElement.clientHeight, year: today.getFullYear(), month: today.getMonth(), day: today.getDate() }; /** * Slider的实例 */ private slider: Slider; /** * 滑动容器 */ private sliderElem: HTMLElement; /** * hammer Element */ private containerElem: HTMLElement; /** * 当前年月显示 */ private dateElem: HTMLElement; /** * slider索引 */ private selectedIndex: number = this.getIndexByDate(this.props.year, this.props.month); /** * 选中的哪天 */ private selectedDay: number[] = [this.selectedIndex, undefined, this.props.day]; /** * 获取上页的年份 */ public getPrevYear(selectedIndex: number): number { return this.getYearByIndex(selectedIndex - 1); } /** * 获取上一页的月份 */ public getPrevMonth(selectedIndex: number): number { return this.getMonthByIndex(selectedIndex - 1); } /** * 获取选中的年份 */ public getSelectedYear(selectedIndex: number): number { return this.getYearByIndex(selectedIndex); } /** * 获取选中的月份 */ public getSelectedMonth(selectedIndex: number): number { return this.getMonthByIndex(selectedIndex); } /** * 获取下一页年份 */ public getNextYear(selectedIndex: number): number { return this.getYearByIndex(selectedIndex + 1); } /** * 获取下一页月份 */ public getNextMonth(selectedIndex: number): number { return this.getMonthByIndex(selectedIndex + 1); } public componentDidMount(): void { this.sliderElem = this.refs['slider'] as HTMLElement; this.containerElem = this.refs['container'] as HTMLElement; this.dateElem = this.refs['date'] as HTMLElement; this.slider = new Slider({ sliderElem: this.sliderElem, containerElem: this.containerElem, count: 1200, direction: 'horizontal', width: this.props.width, height: this.props.height, selectedIndex: this.selectedIndex }); this.calendarInitial(); this.setCurrentDayStyle(); this.setDarkenByDay(this.props.darken); this.sliderElem.addEventListener('webkitTransitionEnd', this.onTransitionEnd); } public componentWillReceiveProps(nextProps: TspComponentCalendarProps): void { if (this.props.darken !== nextProps.darken) { this.setDarkenByDay(nextProps.darken); } } public shouldComponentUpdate(nextProps: TspComponentCalendarProps): boolean { return this.props.refreshId && (this.props.refreshId !== nextProps.refreshId); } public componentWillUnmount(): void { this.sliderElem.removeEventListener('webkitTransitionEnd', this.onTransitionEnd); } /** * 开始滑动 */ public onPanStart(evt: TspComponentSliderEvt): void { evt.preventDefault(); this.slider.panStart(); } /** * 滑动中 */ public onPan(evt: any): void { evt.preventDefault(); this.slider.panMove(evt); } /** * 滑动结束 */ public onPanEnd(evt: any): void { evt.preventDefault(); if (this.slider.isPanEnd) { return; } this.containerElem.classList.add('tsp-compoent-slider-disabled'); this.slider.addPanEndClass(); this.slider.panEnd(evt); } /** * 监听translate完成事件 */ public onTransitionEnd(): void { const { afterSelectedIndex } = this.slider.afterChangeIndexs; this.selectedIndex = afterSelectedIndex; const year = this.getSelectedYear(this.selectedIndex); const month = this.getSelectedMonth(this.selectedIndex) + 1; this.calendarReplace(); this.setDarkenByDay(this.props.darken); if (this.props.afterChange) { this.props.afterChange(this.slider.afterChangeIndexs, [year, month]); } this.slider.removePanEndClass(); this.containerElem.classList.remove('tsp-compoent-slider-disabled'); } /** * 日历点击事件 */ public onClickWithDay(e: any): void { if (e.target.classList.contains(`${prefix}-table-currentMonth`)) { const dayInMonthElem = e.target; const dayCurrentElem = this.sliderElem.querySelector(`.${prefix}-day-current`); const year = this.getSelectedYear(this.selectedIndex); const month = this.getSelectedMonth(this.selectedIndex) + 1; const index = parseInt(dayInMonthElem['dataset'].index); if (dayCurrentElem) { dayCurrentElem.classList.remove(`${prefix}-day-current`); } dayInMonthElem.classList.add(`${prefix}-day-current`); this.selectedDay = [this.selectedIndex, index, parseInt(dayInMonthElem.textContent)]; if (this.props.onClick) { this.props.onClick([year, month, this.selectedDay[2]]); } } } /** * 根据索引获得年份 */ public getYearByIndex(index: number): number { return parseInt((index / 12).toString()) + startYear; } /** * 根据索引获得月数 */ public getMonthByIndex(index: number): number { return index % 12; } /** * 根据日期获得索引 */ public getIndexByDate(year: number, month: number): number { return (year - startYear) * 12 + month; } /** * 渲染日历表需要的数据 */ public getCalendarRenderData(index: number): TspComponentCalendarGetCalendarRenderDataReturn { const selectedYear = this.getSelectedYear(index); const selectedMonth = this.getSelectedMonth(index); const currentMonthDays = getDaysInMonth(selectedYear, selectedMonth, 'array'); const firstDayInMonth = zellerFormula(selectedYear, selectedMonth, 1); const prevSliceIndex = firstDayInMonth === 0 ? 7 : firstDayInMonth; const prevMonthDays = getDaysInMonth(this.getPrevYear(index), this.getPrevMonth(index), 'array'); const prevMonthDisplayDays = prevMonthDays.reverse().slice(0, prevSliceIndex).reverse(); const nextMonthDaysSliceEnd = 42 - currentMonthDays.length - prevMonthDisplayDays.length; const nextMonthDays = getDaysInMonth(this.getNextYear(index), this.getNextMonth(index), 'array').slice(0, nextMonthDaysSliceEnd); return { prevMonthDisplayDays, currentMonthDays, nextMonthDays, table: prevMonthDisplayDays.concat(currentMonthDays, nextMonthDays) }; } /** * 渲染日历表 */ public calendarRender(renderData: TspComponentCalendarGetCalendarRenderDataReturn): DocumentFragment { const fragment = document.createDocumentFragment(); const prevMonthDisplayDaysLength = renderData.prevMonthDisplayDays.length; const currentMonthDaysLength = renderData.currentMonthDays.length; const nextMonthDisplayDaysStart = prevMonthDisplayDaysLength + currentMonthDaysLength; const divElem = document.createElement('div'); const getSelectedYear = this.getSelectedYear(this.selectedIndex); const getSelectedMonth = this.getSelectedMonth(this.selectedIndex) + 1; const yearMonth = `${getSelectedYear}-${valueFormat(getSelectedMonth, 'string')}-`; let i = 0; divElem.classList.add(`${prefix}-table-day`); for (i = 0; i < 42; i++) { const tempElem = divElem.cloneNode(false) as HTMLElement; const day = renderData.table[i].toString(); if (i < prevMonthDisplayDaysLength || i >= nextMonthDisplayDaysStart) { tempElem.classList.add(`${prefix}-table-noCurrentMonth`); tempElem.dataset.date = ''; } else { tempElem.classList.add(`${prefix}-table-currentMonth`); tempElem.dataset.date = yearMonth + valueFormat(day, 'string'); } tempElem.innerText = renderData.table[i].toString(); tempElem.dataset.index = i.toString(); fragment.appendChild(tempElem); } return fragment; } /** * 日历初始化 */ public calendarInitial(): void { const prevMonthCalendar = this.calendarRender(this.getCalendarRenderData(this.selectedIndex - 1)); const currentMonthCalendar = this.calendarRender(this.getCalendarRenderData(this.selectedIndex)); const nextMonthCalendar = this.calendarRender(this.getCalendarRenderData(this.selectedIndex + 1)); const fragment = document.createDocumentFragment(); const wrapElem = document.createElement('div'); const calendarArray = [prevMonthCalendar, currentMonthCalendar, nextMonthCalendar]; let i = 0; wrapElem.classList.add(`${prefix}-table`); wrapElem.style.width = this.props.width + 'px'; for (i = 0; i < 3; i++) { const tempElem = wrapElem.cloneNode(false); tempElem.appendChild(calendarArray[i]); fragment.appendChild(tempElem); } this.sliderElem.appendChild(fragment); this.restPosition(); this.renderYearMonth(); } /** * 替换日历表 */ public calendarReplace(): void { const prevMonthRenderData = this.getCalendarRenderData(this.selectedIndex - 1); const currentMonthRenderData = this.getCalendarRenderData(this.selectedIndex); const nextMonthRenderData = this.getCalendarRenderData(this.selectedIndex + 1); const renderDataArray = [prevMonthRenderData, currentMonthRenderData, nextMonthRenderData]; const children = this.sliderElem.children; const getSelectedYear = this.getSelectedYear(this.selectedIndex); const getSelectedMonth = this.getSelectedMonth(this.selectedIndex) + 1; const yearMonth = `${getSelectedYear}-${valueFormat(getSelectedMonth, 'string')}-`; let [i, j] = [0, 0]; for (i = 0; i < 42; i++) { for (j = 0 ; j < 3; j++) { const prevMonthDisplayDaysLength = renderDataArray[j].prevMonthDisplayDays.length; const currentMonthDaysLength = renderDataArray[j].currentMonthDays.length; const nextMonthDisplayDaysStart = prevMonthDisplayDaysLength + currentMonthDaysLength; const day = renderDataArray[j].table[i].toString(); if (i < prevMonthDisplayDaysLength || i >= nextMonthDisplayDaysStart) { children[j].children[i].classList.add(`${prefix}-table-noCurrentMonth`); children[j].children[i].classList.remove(`${prefix}-table-currentMonth`); children[j].children[i]['dataset'].date = ''; } else { children[j].children[i].classList.add(`${prefix}-table-currentMonth`); children[j].children[i].classList.remove(`${prefix}-table-noCurrentMonth`); children[j].children[i]['dataset'].date = yearMonth + valueFormat(day, 'string'); } if (this.selectedIndex !== this.selectedDay[0]) { if (children[j].children[i].classList.contains(`${prefix}-day-current`)) { children[j].children[i].classList.remove(`${prefix}-day-current`); } } else if (j === 1) { children[1].children[this.selectedDay[1]].classList.add(`${prefix}-day-current`); } children[j].children[i].textContent = day; children[j].children[i]['dataset'].index = i.toString(); } } this.restPosition(); this.renderYearMonth(); } /** * 设置选中日期的样式 */ public setCurrentDayStyle(): void { const currentSliderElem = this.sliderElem.children[1]; for (let i = 0; i < 42; i++) { const children = currentSliderElem.children[i]; if (children.classList.contains(`${prefix}-table-currentMonth`)) { if (children.textContent === this.props.day.toString()) { children.classList.add(`${prefix}-day-current`); this.selectedDay = [this.selectedIndex, i, this.props.day]; break; } } } } /** * 重置滑块的位置 */ public restPosition(): void { const position = this.slider.getMoveBeforePosition(); setTranslate(this.sliderElem, position.x, position.y); } /** * 渲染年月 */ public renderYearMonth(): void { this.dateElem.innerText = `${this.getSelectedYear(this.selectedIndex)}年${this.getSelectedMonth(this.selectedIndex) + 1}月`; } /** * 根据日期设置加深样式 */ public setDarkenByDay(days: string[]): void { const elem = this.sliderElem.children[1]; if (days.length) { const length = days.length; let count = 0; for (let j = 0; j < length; j++) { for (let i = count; i < 42; i++) { const dayElem = elem.children[i] as HTMLElement; if (dayElem.classList.contains('tsp-component-calendar-table-currentMonth')) { if (dayElem.dataset.date === days[j]) { dayElem.dataset.darken = 'true'; count = i + 1; break; } else { dayElem.dataset.darken = 'false'; } } } } } else { for (let i = 0; i < 42; i++) { const dayElem = elem.children[i] as HTMLElement; dayElem.dataset.darken = 'false'; } } } public render(): JSX.Element { const contentStyle = { width: this.props.width }; const flexStyle = { width: this.props.width * 3 }; return (
{this.props.leftArrow ? (
{this.props.leftArrow}
) : null} {this.props.rightArrow ? (
{this.props.rightArrow}
) : null}
); } } export default Calendar;