src/UnitCalendar.js
/**
* @file UnitCalendar
* @author leon([email protected])
*/
import React, {PropTypes} from 'react';
import {create} from 'melon-core/classname/cxBuilder';
import InputComponent from 'melon-core/InputComponent';
import BoxGroup from 'melon/BoxGroup';
import * as date from './util';
const cx = create('UnitCalendar');
/**
* melon 日期区间选择器
*
* @class
* @extends {melon-core/InputComponent}
*/
export default class UnitCalendar extends InputComponent {
/**
* 构造函数
*
* @param {Object} props 组件属性
* @param {Object} context 组件上下文
* @public
*/
constructor(props, context) {
super(props, context);
this.onChange = this.onChange.bind(this);
this.parse = this.parse.bind(this);
this.format = this.format.bind(this);
}
/**
* BoxGroup 改变时触发
*
* @param {Object} e 事件对象
* @private
*/
onChange(e) {
const nextValue = e.value;
const value = this.state.value;
super.onChange({
target: this,
// 如果是连续的,这里需要算一下,不是连续的就以新值为主
value: this.props.continuous
? this.calculate(value, nextValue).map(this.parse)
: value
});
}
/**
* 时间区间的计算
*
* @param {Array<Date>} current 当前区间
* @param {Array<Date>} next 改变后的区间
* @return {Array<string>}
*/
calculate(current, next) {
current = current.map(this.format).sort();
next = next.sort();
let cLength = current.length;
let nLength = next.length;
let unit = this.props.unit;
if (cLength === nLength) {
return current;
}
if (!cLength || !nLength) {
return next;
}
// fill
if (cLength < nLength) {
let firtNext = new Date(next[0]);
let firstCurrent = new Date(current[0]);
if (firtNext < firstCurrent) {
return getContinuousFragments(firtNext, firstCurrent, unit).map(this.format).concat(current);
}
let lastNext = new Date(next[nLength - 1]);
lastNext.setDate(lastNext.getDate() + 1);
let lastCurrent = new Date(current[cLength - 1]);
return current.concat(getContinuousFragments(lastCurrent, lastNext, unit).slice(1).map(this.format));
}
// cut
for (let i = 0; i < nLength; ++i) {
if (current[i] < next[i]) {
if (i === 0) {
return current.slice(1);
}
return current.slice(0, i);
}
}
return current.slice(0, -1);
}
/**
* 日期字符串格式化
*
* @param {string} time 日期字符串
* @return {Date}
*/
parse(time) {
return date.parse(time, this.props.format);
}
/**
* 日期对象格式化
*
* @param {Date} time 日期对象
* @return {string}
*/
format(time) {
return date.format(time, this.props.format);
}
/**
* 日期区间格式转换
*
* @param {string} value 日期区间字符串
* @return {Array<Date>}
*/
parseValue(value = '') {
return value
.split(',')
.map(function (date) {
return this.parse(date);
});
}
/**
* 日期区间格式转换
*
* @param {Array<Date>} value 日期区间数组
* @return {string}
*/
stringifyValue(value = []) {
return value
.map(function (term) {
return this.format(term);
})
.join(',');
}
/**
* 渲染
*
* @public
* @return {React.Element}
*/
render() {
let {begin, end, unit, format, ...rest} = this.props;
let value = this.state.value;
value = value
.map(function (fragment) {
return date.format(normalize(fragment, unit), format);
})
.sort();
const options = getContinuousFragments(begin, end, unit).map(fragment => {
let begin = this.format(fragment);
let end = getNextTime(fragment, unit);
end.setDate(end.getDate() - 1);
end = this.format(end);
return (<option key={begin} value={begin} label={`${begin} ~ ${end}`} />);
});
return (
<div className={cx(this.props).build()}>
<BoxGroup
{...rest}
boxModel="checkbox"
onChange={this.onChange}
value={value}>
{options}
</BoxGroup>
</div>
);
}
}
UnitCalendar.propTypes = {
...InputComponent.propTypes,
begin: PropTypes.instanceOf(Date),
end: PropTypes.instanceOf(Date),
unit: PropTypes.oneOf(['week', 'month', 'year']).isRequired,
value: PropTypes.arrayOf(Date),
continuous: PropTypes.bool.isRequired,
defaultValue: PropTypes.arrayOf(PropTypes.string)
};
UnitCalendar.defaultProps = {
...InputComponent.defaultProps,
continuous: true,
defaultValue: [],
format: 'YYYY-MM-DD'
};
UnitCalendar.childContextTypes = InputComponent.childContextTypes;
UnitCalendar.contextTypes = InputComponent.contextTypes;
/**
* 处理时间对象,只留下当前单位需要的部分
*
* @param {Date} time 时间
* @param {string} unit 单位
* @return {Date}
*/
export function normalize(time, unit) {
time = new Date(time);
// 得到周一
if (unit === 'week') {
time.setDate(time.getDate() - time.getDay() + 1);
}
// 得到1日
else if (unit === 'month') {
time.setDate(1);
}
// 得到1月1日
else {
time.setMonth(0);
time.setDate(1);
}
return time;
}
/**
* 处理时间对象,返回当前单位下下一个值
*
* @param {Date} time 时间
* @param {string} unit 单位
* @return {Date}
*/
export function getNextTime(time, unit) {
time = normalize(time, unit);
if (unit === 'week') {
time.setDate(time.getDate() + 7);
}
else if (unit === 'month') {
time.setMonth(time.getMonth() + 1);
}
else {
time.setFullYear(time.getFullYear() + 1);
}
return time;
}
/**
* 获取所允许的时间区间
*
* @param {Date} begin 起始时间
* @param {Date} end 结束时间
* @param {string} unit 单位
* @return {Array<Date>}
*/
export function getContinuousFragments(begin, end, unit) {
begin = normalize(begin, unit);
let result = [];
while (begin < end) {
result.push(new Date(begin));
if (unit === 'week') {
begin.setDate(begin.getDate() + 7);
}
else if (unit === 'month') {
begin.setMonth(begin.getMonth() + 1);
}
else {
begin.setFullYear(begin.getFullYear() + 1);
}
}
return result;
}