@ustack/uskin
Version:
A graceful framework which provides developers another chance to build an amazing site.
284 lines (228 loc) • 6.36 kB
JSX
import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import Screen from './screen';
import Header from './header';
import DatePicker from './datepicker';
import helper from './helper';
function noop() {}
class Calendar extends React.Component {
constructor(props) {
super(props);
this.state = this.initialState(props);
['onToggle', 'onChange', 'onSelected', 'onConfirmScreen', 'destroyCalendar'].forEach((func) => {
this[func] = this[func].bind(this);
});
}
initialState(props) {
let selected = {};
let page;
if (props.selectedDate && helper.isValidDate(props.selectedDate)){
selected = helper.formatDate(new Date(props.selectedDate));
if (!this.isSelectable(props.disabled, selected)) {
selected = {};
}
}
if (selected.date) {
page = selected;
} else if (props.page) {
page = helper.formatDate(new Date(props.page));
} else {
page = helper.formatDate(new Date());
}
let state = {
page: page,
selected: selected,
unfold: false
};
return state;
}
updateState(state) {
this.setState(state, () => {
this.updateCalendar();
});
}
onToggle(e) {
const unfold = this.state.unfold;
if (unfold) {
this.destroyCalendar(e);
} else {
this.updateCalendar();
}
}
createCalendar() {
if (!this.CalenderDOM) {
this.CalenderDOM = document.createElement('div');
const root = this.CalenderDOM;
root.className = 'calendar-container';
document.body.appendChild(root);
ReactDOM.render(this.getCalendar(this.props, this.state), root);
document.addEventListener('click', this.destroyCalendar, false);
}
this.setState({
unfold: true
});
}
updateCalendar() {
if (!this.CalenderDOM) {
this.createCalendar();
} else {
ReactDOM.render(this.getCalendar(this.props, this.state), this.CalenderDOM);
}
this.setState({
unfold: true
});
}
destroyCalendar(e) {
if (this.CalenderDOM) {
const root = this.CalenderDOM;
ReactDOM.unmountComponentAtNode(root);
root.parentNode.removeChild(root);
document.removeEventListener('click', this.destroyCalendar, false);
delete this.CalenderDOM;
}
this.setState({
unfold: false
});
}
onPreventFold(e) {
e.nativeEvent.stopImmediatePropagation();
}
onChange(date) {
this.setPage(date);
}
setPage(date) {
this.updateState({
page: date
});
}
onConfirmScreen(date) {
let d = date;
if (!this.isSelectable(this.props.disabled, date)) {
d = this.state.selected;
}
this.onChange(d);
this.onSelected(d);
}
isSelectable(disabled, date) {
if (disabled) {
let currentDate = new Date(date.year, date.month, date.date);
let isAvaiableMin = true;
if (disabled.min) {
isAvaiableMin = helper.compareFullDate(currentDate, new Date(disabled.min)) > 0;
}
let isAvailableMax = true;
if (disabled.max) {
isAvailableMax = helper.compareFullDate(new Date(disabled.max), currentDate) > 0;
}
let isAvailableWeek = true;
if (disabled.weeks && disabled.weeks.length > 0) {
let currentWeek = currentDate.getDay();
isAvailableWeek = disabled.weeks.indexOf(currentWeek) < 0;
}
let isAvailableDate = true;
if (disabled.dates && disabled.dates.length > 0) {
isAvailableDate = !disabled.dates.some((d) => (
helper.compareFullDate(new Date(d), currentDate) === 0
));
}
return isAvaiableMin && isAvailableMax && isAvailableWeek && isAvailableDate;
}
return true;
}
onSelected(date) {
let isValid = this.isSelectable(this.props.disabled, date);
if (isValid) {
this.updateState({
selected: date
});
this.alertChange(date);
} else {
this.updateState({
selected: this.state.selected
});
}
}
alertChange(date) {
let d = {
year: date.year,
month: date.month + 1,
date: date.date
};
this.beforeChange(d);
this.onChangeDate(d);
this.afterChange(d);
}
onChangeDate(date) {
this.props.onChange(date);
}
beforeChange(date) {
this.props.beforeChange(date);
}
afterChange(date) {
this.props.afterChange(date);
}
getCalendar(props, state) {
const calendarBox = this.refs['calendar-box'];
const position = calendarBox.getBoundingClientRect();
const scrollTop = position.top + 32 + window.pageYOffset;
const scrollLeft = position.left + window.pageXOffset;
const style = {
position: 'absolute',
top: scrollTop,
left: scrollLeft
};
return (
<div className="calendar" style={style}>
<Header {...props} {...state}
onPreventFold={this.onPreventFold}
onChange={this.onChange} />
<DatePicker {...props} {...state}
onSelected={this.onSelected}
isSelectable={this.isSelectable} />
</div>
);
}
render() {
const props = this.props;
const state = this.state;
const style = props.width ? {width: props.width} : {width: '161px'};
return (
<div style={style} className="calendar-box" ref="calendar-box" onClick={this.onToggle}>
<Screen {...state}
unfold={state.unfold}
placeholder={props.placeholder}
onSelected={this.onSelected}
onChange={this.onChange}
onConfirm={this.onConfirmScreen} />
</div>
);
}
}
Calendar.propTypes = {
onChange: PropTypes.func,
beforeChange: PropTypes.func,
afterChange: PropTypes.func,
page: PropTypes.string,
selectedDate: PropTypes.string,
placeholder: PropTypes.string,
startWeek: PropTypes.number,
disabled: PropTypes.shape({
min: PropTypes.string,
max: PropTypes.string,
weeks: PropTypes.arrayOf(PropTypes.number),
dates: PropTypes.arrayOf(PropTypes.string)
}),
local: PropTypes.shape({
weeks: PropTypes.arrayOf(PropTypes.string),
months: PropTypes.arrayOf(PropTypes.string)
}),
width: PropTypes.number
};
Calendar.defaultProps = {
onChange: noop,
beforeChange: noop,
afterChange: noop,
startWeek: 0
};
export default Calendar;