Home Reference Source Repository

src/calendar/Panel.js

/**
 * @file melon/CalendarPanel
 * @author leon([email protected])
 */

import React, {Component, PropTypes} from 'react';
import {create} from 'melon-core/classname/cxBuilder';

import Header from './Header';
import Selector from './Selector';
import Pager from './Pager';
import Month from './Month';

import * as DateTime from '../util';

const cx = create('CalendarPanel');

/**
 * melon-calendar 日期选择主面板
 *
 * @class
 * @extends {React.Component}
 */
export default class CalendarPanel extends Component {

    /**
     * 构造函数
     *
     * @param  {Object} props   组件属性
     * @public
     */
    constructor(props) {

        super(props);

        this.onHeaderClick = this.onHeaderClick.bind(this);
        this.onSelectorChange = this.onSelectorChange.bind(this);
        this.onPagerChange = this.onPagerChange.bind(this);
        this.onDateChange = this.onDateChange.bind(this);

        /**
         * 组件状态
         *
         * @type {Object}
         */
        this.state = {
            selectorType: 'main',
            month: props.date,
            date: props.date
        };

    }

    /**
     * 组件每次更新属性时判断是否需要同步状态
     *
     * @param  {Object} nextProps 新属性
     * @public
     */
    componentWillReceiveProps(nextProps) {

        const date = nextProps.date;

        if (!DateTime.isEqualDate(date, this.props.date)) {
            this.setState({
                date,
                month: date
            });
        }
    }

    /**
     * 判断是否需要更新,性能优化
     *
     * @param  {Object} nextProps  组件新的属性
     * @param  {Object} nextState  组件新的状态
     * @return {bool}  是否需要更新
     * @public
     */
    shouldComponentUpdate(nextProps, nextState) {
        return !DateTime.isEqualDate(nextState.date, this.state.date)
            || !DateTime.isEqualMonth(nextState.month, this.state.month)
            || nextState.selectorType !== this.state.selectorType
            || (nextProps.begin && this.props.begin && !DateTime.isEqualDate(nextProps.begin, this.props.begin))
            || (nextProps.end && this.props.end && !DateTime.isEqualDate(nextProps.end, this.props.end))
            || (!nextProps.begin && this.props.begin)
            || (!nextProps.end && this.props.end)
            || (nextProps.begin && !this.props.begin)
            || (nextProps.end && !this.props.end);
    }

    /**
     * 点击 Header 切换日期、年/月选择
     *
     * @param  {Object} e 事件对象
     * @protected
     */
    onHeaderClick(e) {

        const selectorType = this.state.selectorType;

        this.setState({
            selectorType: selectorType === 'main' ? 'year' : 'main'
        });
    }

    /**
     * 年/月选择改变的事件
     *
     * @param  {Object} e 事件对象
     * @protected
     */
    onSelectorChange(e) {

        let {
            mode,
            date
        } = e;

        const {
            end,
            begin
        } = this.props;

        mode = mode === 'year' ? 'month' : 'main';

        if (begin && DateTime.isBeforeDate(date, begin)) {
            date = begin;
        }
        else if (end && DateTime.isAfterDate(date, end)) {
            date = end;
        }

        this.setState({
            date: date,
            month: date,
            selectorType: mode
        });

    }

    /**
     * 月份翻页器改变
     *
     * @param  {Object} e 事件对象
     * @protected
     */
    onPagerChange(e) {
        this.setState({month: e.month});
    }

    /**
     * 选择某一天时触发
     *
     * @param  {Object} e 事件对象
     */
    onDateChange(e) {

        const date = e.date;

        const month = this.state.month;
        const monthDiff = DateTime.monthDiff(date, month);

        if (monthDiff !== 0) {
            this.setState({
                month: DateTime.addMonths(month, monthDiff)
            });
        }

        this.props.onChange({
            value: date
        });
    }

    /**
     * 渲染
     *
     * @public
     * @return {React.Element}
     */
    render() {

        const {
            lang,
            begin,
            end
        } = this.props;

        const date = this.state.date;

        const {selectorType, month} = this.state;

        return (
            <div className={cx(this.props).build()}>
                <Header
                    date={date}
                    onClick={this.onHeaderClick} />
                <div className={cx().part('main').build()}>
                    <Pager
                        minDate={begin}
                        maxDate={end}
                        onChange={this.onPagerChange}
                        month={month} />
                    <Month
                        minDate={begin}
                        maxDate={end}
                        lang={lang}
                        month={month}
                        date={date}
                        onChange={this.onDateChange} />
                    <Selector
                        style={{display: selectorType === 'main' ? 'none' : null}}
                        date={date}
                        mode={selectorType === 'year' ? 'year' : 'month'}
                        minDate={begin}
                        maxDate={end}
                        onChange={this.onSelectorChange} />
                </div>
            </div>
        );

    }

}

CalendarPanel.displayName = 'CalendarPanel';

CalendarPanel.defaultProps = {
    date: new Date()
};

CalendarPanel.propTypes = {
    date: PropTypes.instanceOf(Date),
    begin: PropTypes.instanceOf(Date),
    end: PropTypes.instanceOf(Date)
};