@hydrationlabs/react-monthrange-picker
Version:
ReactJS Month range picker
223 lines (192 loc) • 6.82 kB
JSX
import PropTypes from 'prop-types';
import React from 'react';
import $ from 'jquery';
import _ from 'lodash';
import CustomPropTypes from './utils/custom_prop_types';
import { YearStart, YearEnd } from './year';
class Calendar extends React.Component {
constructor(props) {
super(props);
this.selectMonthFn = this.selectMonth.bind(this);
let positionWidth;
try {
positionWidth = props.position.width;
} catch (e) {
positionWidth = 700;
}
let positionTop;
try {
positionTop = props.position.top;
} catch (e) {
positionTop = 0;
}
let positionLeft;
try {
positionLeft = props.position.left;
} catch (e) {
positionLeft = 0;
}
this.calStyle = {
width: `${positionWidth}px`,
top: `${positionTop}px`,
left: `${positionLeft}px`,
display: props.display || props.static ? 'block' : 'none',
};
this.arrowStyle = {};
const { selectedDateRange, restrictionRange } = props;
// using state here because on month selection
// both yearstart and yearend gets re-rendered
// rather than propagating to the App.
// App component stores the current select so
// that on apply it can just change the state
// to the current stored selection.
this.state = { selectedDateRange, restrictionRange };
}
componentDidMount() {
this.$el = $(this.node);
this.setStyle(this.props);
}
setStyle(props) {
let positionTop;
try {
positionTop = props.position.top;
} catch (e) {
positionTop = 0;
}
let positionLeft;
try {
positionLeft = props.position.left;
} catch (e) {
positionLeft = 0;
}
const calStyle = _.cloneDeep(this.calStyle);
const arrowStyle = _.cloneDeep(this.arrowStyle);
const picker = $(this.el).siblings('.picker');
const direction = this.props.direction;
const adjustmentConstant = 10;
const calDim = {
height: $(this.el).height(),
width: $(this.el).width(),
};
const pickerDim = {
height: picker.height(),
width: picker.width(),
};
if (direction === 'left' || direction === 'right') {
calStyle.top = positionTop ? `${calStyle.top}px` : `-${calDim.height / 2}px`;
if (direction === 'left') {
const leftWidth = calDim.width + adjustmentConstant;
calStyle.left = positionLeft ? `${calStyle.left}px` : `-${leftWidth}px`;
} else {
const rightWidth = pickerDim.width + adjustmentConstant;
calStyle.left = positionLeft ? `${calStyle.left}px` : `${rightWidth}px`;
}
const arrowTop = Math.abs(parseInt(calStyle.top, 10)) + (pickerDim.height / 2);
arrowStyle.top = `${arrowTop}px`;
} else if (direction === 'top' || direction === 'bottom') {
calStyle.left = positionLeft ? `${calStyle.left}px` : `-${(calDim.width - pickerDim.width) / 2}px`;
if (direction === 'top') {
const top = calDim.height + pickerDim.height;
calStyle.top = positionTop ? `${calStyle.top}px` : `-${top}px`;
} else {
const top = pickerDim.height + adjustmentConstant;
calStyle.top = positionTop ? `${calStyle.top}px` : `${top}px`;
}
const arrowLeft = Math.abs(parseInt(calStyle.left, 10)) + (pickerDim.width / 2);
arrowStyle.left = `${arrowLeft}px`;
}
calStyle.display = props.display ? 'block' : 'none';
Object.assign(calStyle, props.calStyle);
this.calStyle = calStyle;
this.arrowStyle = arrowStyle;
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setStyle(nextProps);
const { selectedDateRange, restrictionRange } = _.cloneDeep(nextProps);
this.state = { selectedDateRange, restrictionRange };
this.setState(this.state);
}
selectMonth(newDateRange) {
const newDateRangeClone = newDateRange.clone();
if (newDateRangeClone.start > newDateRangeClone.end) {
newDateRangeClone.end.month(newDateRangeClone.start.month());
newDateRangeClone.end.year(newDateRangeClone.start.year());
}
if (this.props.onSelect) {
this.props.onSelect(newDateRangeClone);
}
this.state.selectedDateRange = newDateRangeClone;
this.setState(this.state);
}
render() {
const selectedRange = this.state.selectedDateRange.clone();
const startDate = selectedRange.start;
const endDate = selectedRange.end;
const popOverClass = `${this.props.direction} popover`;
return (
<div ref={node => (this.node = node)} className={popOverClass} style={this.calStyle}>
{this.props.static || <div className="arrow" style={this.arrowStyle} />}
<div className="clearfix sec-wrap">
<div className="calendar col-xs-10" style={this.props.hideButtons ? { width: '100%', paddingRight: '0px' } : null}>
<div className="clearfix">
<div className="col-xs-6 year-start year">
<YearStart
restrictionRange={this.props.restrictionRange}
onYearChange={this.props.onYearChange}
onSelect={this.selectMonthFn}
currYear={startDate.clone()}
selectedDateRange={selectedRange}
/>
</div>
<div className="col-xs-6 year-end year">
<YearEnd
restrictionRange={this.props.restrictionRange}
onYearChange={this.props.onYearChange}
onSelect={this.selectMonthFn}
currYear={endDate.clone()}
selectedDateRange={selectedRange}
/>
</div>
</div>
</div>
{this.props.hideButtons ||
<div className="shortcuts col-xs-2">
<button
onClick={this.props.onApply}
type="button"
className="btn btn-block btn-success"
>
Apply
</button>
<button
onClick={this.props.onCancel}
type="button"
className="btn btn-default btn-block"
>
Cancel
</button>
</div>
}
</div>
</div>
);
}
}
Calendar.propTypes = {
selectedDateRange: CustomPropTypes.MomentRangeType.isRequired,
restrictionRange: CustomPropTypes.MomentRangeType.isRequired,
direction: PropTypes.oneOf(['top', 'left', 'right', 'bottom']).isRequired,
display: PropTypes.bool.isRequired,
onSelect: PropTypes.func.isRequired,
onApply: PropTypes.func,
onCancel: PropTypes.func,
onYearChange: PropTypes.func,
position: PropTypes.shape({
width: PropTypes.string,
top: PropTypes.string,
left: PropTypes.string,
}),
static: PropTypes.bool,
hideButtons: PropTypes.bool,
};
export default Calendar;