Home Reference Source Repository

src/components/picker/InlineDatePicker.js

/**
 * @author haw
 */

import React, {
  PropTypes,
  Component
} from 'react';
import Options from './Options';
import {
  classNames,
  generateOptions,
  indexOfOptions
} from '../util';

const prefix = 'picker-body';
let now = new Date();

/**
 * 內联日期选择器
 */
export default class InlineDatePicker extends Component {

  /**
   * 构造函数
   * @param {Object} props 组件所使用的属性
   * @param {Date} [props.selectedDate=new Date()] 当前选中的日期
   * @param {Date} [props.minDate=new Date()] 最小可选择的日期
   * @param {Date} [props.maxDate=new Date()] 最大可选择的日期
   * @param {string} [props.yearUnit=''] 年份的单位文案
   * @param {string} [props.monthUnit=''] 月份的单位文案
   * @param {string} [props.dateUnit=''] 天的单位文案
   * @param {function} props.onChange 选中某一个项时触发的函数回调
   * @param {Object} context
   */
  constructor(props, context) {
    super(props, context);
  }

  _setYear(yearOptions, selectedIndex) {
    const {
      selectedDate
    } = this.props;
    const year = yearOptions[selectedIndex].value;
    const month = selectedDate.getMonth();
    const newDate = copyDate(selectedDate);

    newDate.setFullYear(year);
    this.onChange(adjustDate(newDate, month));
  }

  _setMonth(monthOptions, selectedIndex) {
    const {
      selectedDate
    } = this.props;
    const month = monthOptions[selectedIndex].value - 1;
    const newDate = copyDate(selectedDate);

    newDate.setMonth(month);
    this.onChange(adjustDate(newDate, month));
  }

  _setDate(dateOptions, selectedIndex) {
    const {
      selectedDate
    } = this.props;
    const date = dateOptions[selectedIndex].value;
    const newDate = copyDate(selectedDate);

    newDate.setDate(date);
    this.onChange(newDate);
  }

  onChange(newDate) {
    const {
      onChange
    } = this.props;

    onChange && onChange(newDate);
  }

  render() {
    const {
      selectedDate,
      minDate,
      maxDate,
      yearUnit,
      monthUnit,
      dateUnit,
      className,
      ...rest
    } = this.props;
    const clazz = classNames(prefix, {
      [className]: className
    });
    const yearOptions = createYearOptions(maxDate, minDate, yearUnit);
    const monthOptions = createMonthOptions(maxDate, minDate, selectedDate, monthUnit);
    const dateOptions = createDateOptions(maxDate, minDate, selectedDate, dateUnit);
    const yearIndex = indexOfOptions(selectedDate.getFullYear(), yearOptions);
    const monthIndex = indexOfOptions(getMonth(selectedDate), monthOptions);
    const dateIndex = indexOfOptions(selectedDate.getDate(), dateOptions);

    return (
      <div className={clazz} {...rest}>
        <Options
          selectedIndex={yearIndex}
          options={yearOptions}
          key={'year'}
          onChange={this._setYear.bind(this, yearOptions)}
        />
        <Options
          selectedIndex={monthIndex}
          options={monthOptions}
          key={'month'}
          onChange={this._setMonth.bind(this, monthOptions)}
        />
        <Options
          selectedIndex={dateIndex}
          options={dateOptions}
          key={'date'}
          onChange={this._setDate.bind(this, dateOptions)}
        />
      </div>
    );
  }
}

function adjustDate(newDate, month) {
  if (newDate.getMonth() > month) {
    newDate.setMonth(month);
    newDate.setDate(getTotalDateOfMonth(newDate));
  }

  return newDate;
}

function copyDate(date) {
  return new Date(date.getTime());
}

function createYearOptions(maxDate, minDate, yearUnit) {
  const max = maxDate.getFullYear();
  const min = minDate.getFullYear();

  return generateOptions(max, min, yearUnit);
}

function createMonthOptions(maxDate, minDate, selectedDate, monthUnit) {
  let max = isEqualYear(maxDate, selectedDate) ? getMonth(maxDate) : 12;
  let min = isEqualYear(minDate, selectedDate) ? getMonth(minDate) : 1;

  return generateOptions(max, min, monthUnit);
}

function createDateOptions(maxDate, minDate, selectedDate, dateUnit) {
  let max = isEqualYearMonth(maxDate, selectedDate) ? maxDate.getDate()
    : getTotalDateOfMonth(selectedDate);
  let min = isEqualYearMonth(minDate, selectedDate) ? minDate.getDate() : 1;

  return generateOptions(max, min, dateUnit);
}

function getMonth(date) {
  return date.getMonth() + 1;
}

function getTotalDateOfMonth(date) {
  let newDate = new Date(date.getTime());

  newDate.setDate(1);
  newDate.setMonth(getMonth(newDate));
  newDate.setDate(0);

  return newDate.getDate();
}

function isEqualYear(d1, d2) {
  return d1.getFullYear() === d2.getFullYear();
}

function isEqualYearMonth(d1, d2) {
  return isEqualYear(d1, d2) && (getMonth(d1) === getMonth(d2));
}

InlineDatePicker.propTypes = {
  selectedDate: PropTypes.instanceOf(Date),
  minDate: PropTypes.instanceOf(Date),
  maxDate: PropTypes.instanceOf(Date),
  yearUnit: PropTypes.string,
  monthUnit: PropTypes.string,
  dateUnit: PropTypes.string,
  onChange: PropTypes.func,
  className: PropTypes.string
};

InlineDatePicker.defaultProps = {
  selectedDate: now,
  maxDate: now,
  minDate: now,
  yearUnit: '',
  monthUnit: '',
  dateUnit: ''
};