zarm-web
Version:
基于 React 的桌面端UI库
454 lines (385 loc) • 11.8 kB
JavaScript
import React, { Component } from 'react';
import classnames from 'classnames';
import Events from '../utils/events';
import Format from '../utils/format';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import Dropdown from '../dropdown/index';
import RangeCalendar from '../calendar/Calendar';
import Icon from '../icon/index';
// 比较时间大小
const compareTime = (v1, v2) => new Date(v1) > new Date(v2); // 获取当前时间 Date 对象
const now = () => new Date(); // 获取原生 Date 对象
const getDate = v => v ? new Date(v) : now(); // 获取下月 1 号
const nextMonthFirstDay = v => {
let date = `${v.getFullYear()}/${v.getMonth() + 2}/1`;
if (v.getMonth() + 2 > 12) {
date = `${v.getFullYear() + 1}/1/1`;
}
return getDate(date);
}; // 获取日期的年、月、日
const getYearMonthDay = v => [v.getFullYear(), v.getMonth(), v.getDate()]; // 判断是否为空对象
const isEmptyArray = arr => Array.isArray(arr) && (arr.length === 0 || arr.every(i => !i)); //
const getValueFromSelectedValue = (v = []) => {
if (isEmptyArray(v)) {
return [now(), nextMonthFirstDay(now())];
}
const [start, end] = v.map(getDate);
const newEnd = start.getMonth() === end.getMonth() && start.getFullYear() === end.getFullYear() ? nextMonthFirstDay(end) : end;
return [start, newEnd];
}; // 空方法
const fn = () => {};
class RangeDatePicker extends Component {
constructor(props) {
super(props);
this.unmounted = void 0;
this.onSelectClick = e => {
e.preventDefault();
const disabled = 'disabled' in this.props || this.props.isDisabled;
if (!disabled) {
this.setDropdown(!this.state.isShowDropdown);
}
};
this.getStartDate = () => {
const {
current
} = this.state;
return getDate(current[0]);
};
this.getEndDate = () => {
const {
current
} = this.state;
const [start, end] = current.map(getDate); // 当结束时间不存在时,结束时间初始值为开始时间月份加 1
return getDate(end || nextMonthFirstDay(start));
};
this.disabledStartMonth = month => {
const {
current,
selectedValue
} = this.state;
const end = getDate(current[1] || selectedValue[1] || nextMonthFirstDay(now()));
const [endYear, endMonth] = getYearMonthDay(end);
const [_year, _month] = getYearMonthDay(getDate(month));
if (_year < endYear) {
return true;
}
if (_year === endYear) {
return _month < endMonth;
}
return false;
};
this.disabledEndMonth = month => {
const {
current,
selectedValue
} = this.state;
const start = getDate(current[0] || selectedValue[0]);
const [startYear, startMonth] = getYearMonthDay(start);
const [_year, _month] = getYearMonthDay(getDate(month));
if (_year > startYear) {
return true;
}
if (_year === startYear) {
return _month > startMonth;
}
return false;
};
this.handleLeftPanelChange = v => {
const {
current: stateCurrent
} = this.state;
const current = [...stateCurrent];
current[0] = v;
this.setState({
current
});
};
this.handleRightPanelChange = v => {
const {
current: stateCurrent
} = this.state;
const current = [...stateCurrent];
current[1] = v;
this.setState({
current
});
};
this.handleLeftDateChange = (v, _dropdown, isTime) => {
const {
selectedValue
} = this.state;
const start = getDate(v);
const end = getDate(selectedValue[1]); // 同年同月,表示一个月选了两个日期
if (!(start.getMonth() === end.getMonth() && start.getFullYear() === end.getFullYear())) {
this.handleLeftPanelChange(v);
}
this.handleDateChange(v, isTime, true);
};
this.handleRightDateChange = (v, _dropdown, isTime) => {
const {
selectedValue
} = this.state;
const start = getDate(selectedValue[0]);
const end = getDate(v); // 同年同月,表示一个月选了两个日期
if (!(start.getMonth() === end.getMonth() && start.getFullYear() === end.getFullYear())) {
this.handleRightPanelChange(v);
}
this.handleDateChange(v, isTime, false);
};
this.handleDateChange = (v, isTime, isLeft) => {
const {
selectedValue
} = this.state;
if (isTime) {
if (isLeft) {
this.setState({
selectedValue: [v, selectedValue[1]]
});
} else {
this.setState({
selectedValue: [selectedValue[0], v]
});
}
return;
}
if (selectedValue.length === 1) {
if (compareTime(selectedValue[0], v)) {
this.setState({
selectedValue: [v, selectedValue[0]]
}, () => this.handleCloseDropDownAndChangeDate());
} else {
this.setState({
selectedValue: [selectedValue[0], v]
}, () => this.handleCloseDropDownAndChangeDate());
}
} else {
this.setState({
selectedValue: [v]
});
}
};
this.handleCloseDropDown = () => {
this.setDropdown(false);
this.props.onChange([]);
};
this.handleConfirm = () => {
this.handleCloseDropDownAndChangeDate(true);
};
this.handleCloseDropDownAndChangeDate = isConfirm => {
const {
showTime,
onChange,
format
} = this.props;
const {
selectedValue
} = this.state;
if (isConfirm || !showTime) {
this.setDropdown(false, () => onChange(selectedValue.map(v => Format.date(v, format))));
}
};
this.handleKeyup = e => {
if (e.keyCode === 27) {
this.setDropdown(false);
}
};
this.handleDropDownVisibleChange = visible => {
const {
isDisabled
} = this.props;
const disabled = 'disabled' in this.props || isDisabled;
if (disabled) {
return;
}
this.setState({
isShowDropdown: visible
});
};
this.renderPlaceholder = () => {
const {
placeholder,
isDisabled,
isRadius,
size,
style,
value = [],
locale
} = this.props;
const {
isShowDropdown
} = this.state;
let valueText = placeholder || locale.placeholder;
let hasValue = false;
if (value.length) {
if (typeof value !== 'string') {
valueText = value.join(' —— ');
}
hasValue = true;
}
const disabled = 'disabled' in this.props || isDisabled;
const radius = 'radius' in this.props || isRadius;
const cls = classnames('za-select', {
'za-select--open': isShowDropdown,
disabled,
radius,
[`size-${size}`]: !!size
});
const textCls = classnames('za-select__text', {
'za-select__text-placeholder': !hasValue
});
return React.createElement("span", {
className: cls,
style: style
}, React.createElement("span", {
className: "za-select__selection",
"aria-autocomplete": "list",
"aria-haspopup": "true",
"aria-expanded": "false",
onClick: this.onSelectClick
}, React.createElement("span", {
className: textCls
}, valueText), React.createElement(Icon, {
className: "za-select__icon",
type: "date"
})));
};
this.unmounted = false;
this.state = {
selectedValue: props.value.map(getDate),
current: getValueFromSelectedValue(props.value),
isShowDropdown: false
};
}
componentDidMount() {
this.unmounted = true;
}
componentWillReceiveProps(nextProps) {
if ('value' in nextProps) {
this.setState({
selectedValue: nextProps.value.map(getDate),
current: getValueFromSelectedValue(nextProps.value)
});
}
}
componentWillUnmount() {
this.unmounted = false;
this.unbindOuterHandlers();
}
setDropdown(isOpen, callback) {
if (!this.unmounted) {
return;
}
if (isOpen) {
this.bindOuterHandlers();
} else {
this.unbindOuterHandlers();
}
this.setState({
isShowDropdown: isOpen
}, () => {
if (callback) {
callback();
}
});
}
bindOuterHandlers() {
Events.on(document, 'keyup', this.handleKeyup);
}
unbindOuterHandlers() {
Events.off(document, 'keyup', this.handleKeyup);
}
render() {
const {
defaultValue,
isRadius,
format,
min,
max,
showTime,
locale
} = this.props;
const {
selectedValue,
current,
isShowDropdown
} = this.state;
const radius = 'radius' in this.props || isRadius;
const startDate = this.getStartDate();
const endDate = this.getEndDate(); // 开始时间月份加 1 且 年份与结束时间相同时,不允许点击左日历月份加 1,不允许右日历月份减 1
const isAllowJumpMonth = !(startDate.getMonth() + 1 === endDate.getMonth() && startDate.getFullYear() === endDate.getFullYear());
const compRangeCalendar = React.createElement(React.Fragment, null, React.createElement("div", {
style: {
float: 'left',
width: '50%'
}
}, React.createElement(RangeCalendar, {
isLeftCalendar: true,
defaultValue: defaultValue,
selectedValue: selectedValue,
current: current[0] || startDate,
value: selectedValue[0] || current[0],
format: format,
min: min,
max: max,
showTime: showTime,
isShowPrev: true,
isShowNext: isAllowJumpMonth,
disabledMonth: this.disabledStartMonth,
onPanelChange: this.handleLeftPanelChange,
onChange: this.handleLeftDateChange
})), React.createElement("div", {
style: {
float: 'right',
width: '50%'
}
}, React.createElement(RangeCalendar, {
isRightCalendar: true,
defaultValue: defaultValue,
selectedValue: selectedValue,
current: current[1] || endDate,
value: selectedValue[1] || current[1],
format: format,
min: min,
max: max,
showTime: showTime,
isShowPrev: isAllowJumpMonth,
isShowNext: true,
disabledMonth: this.disabledEndMonth,
onPanelChange: this.handleRightPanelChange,
onChange: this.handleRightDateChange
})));
const timeFooter = React.createElement("div", {
className: "za-range__date-picker-footer"
}, React.createElement("span", {
className: "za-range__date-picker-footer-btn",
onClick: this.handleCloseDropDown
}, locale.clear), React.createElement("span", {
onClick: this.handleConfirm
}, locale.confirm));
const rangeDatePickerStyle = classnames('za-range__date-picker', {
'za-range__date-time-picker': showTime
});
const rangeDatePicker = React.createElement("div", {
className: rangeDatePickerStyle
}, React.createElement("div", {
className: "za-range__date-picker-table"
}, compRangeCalendar), showTime && timeFooter);
return React.createElement(Dropdown, {
onVisibleChange: this.handleDropDownVisibleChange,
overlay: rangeDatePicker,
isRadius: radius,
visible: isShowDropdown,
hideOnClick: !showTime
}, this.renderPlaceholder());
}
}
RangeDatePicker.defaultProps = {
isRange: false,
isDisabled: false,
format: 'yyyy-MM-dd',
min: '',
max: '',
value: [],
onChange: fn
};
export default LocaleReceiver('Calendar')(RangeDatePicker);