dash-core-components
Version:
Core component suite for Dash
393 lines (346 loc) • 12.3 kB
JavaScript
import React, { Component, PropTypes } from 'react';
import { DateRangePicker } from 'react-dates';
import moment from 'moment';
/**
* DatePickerRange is a tailor made component designed for selecting
* timespan across multiple days off of a calendar.
*
* The DatePicker integrates well with the Python datetime module with the
* startDate and endDate being returned in a string format suitable for
* creating datetime objects.
*
* This component is based off of Airbnb's react-dates react component
* which can be found here: https://github.com/airbnb/react-dates
*/
export default class DatePickerRange extends Component {
constructor(props) {
super(props);
const propObj = {
startDate: this.props.start_date,
endDate: this.props.end_date,
initialVisibleMonth: this.props.initial_visible_month,
minDateAllowed: this.props.min_date_allowed,
maxDateAllowed: this.props.max_date_allowed
};
const momentProps = this.convertPropsToMoment(propObj);
this.state = {
focusedInput: props.autoFocusEndDate ? momentProps.startDate : momentProps.endDate,
startDate: momentProps.startDate,
endDate: momentProps.endDate,
initialVisibleMonth: momentProps.initialVisibleMonth,
minDateAllowed: momentProps.min,
maxDateAllowed: momentProps.max,
prevStartDate: this.props.start_date,
prevEndDate: this.props.end_date,
prevInitialVisibleMonth: this.props.initial_visible_month,
prevMinDateAllowed: this.props.min_date_allowed,
prevMaxDateAllowed: this.props.max_date_allowed
};
}
convertPropsToMoment(props) {
let startDate = null; let endDate = null;
let initialVisibleMonth = moment(props.startDate);
if (typeof props.startDate !== 'undefined') {
startDate = moment(props.startDate);
}
if (typeof props.endDate !== 'undefined') {
endDate = moment(props.endDate);
}
if (typeof props.initialVisibleMonth !== 'undefined') {
initialVisibleMonth = moment(props.initialVisibleMonth);
}
let min; let max;
if (typeof props.minDateAllowed !== 'undefined') {
min = moment(props.minDateAllowed);
}
if (typeof props.maxDateAllowed !== 'undefined') {
max = moment(props.maxDateAllowed);
max.add(1, 'days');
}
try {
if (startDate.isAfter(endDate)) {
endDate = null;
}
} catch (TypeError) {
// Continue regardless of error
}
return { startDate, endDate, initialVisibleMonth, min, max };
}
componentWillReceiveProps(newProps) {
const propObj = {
startDate: newProps.start_date,
endDate: newProps.end_date,
initialVisibleMonth: newProps.initial_visible_month,
minDateAllowed: newProps.min_date_allowed,
maxDateAllowed: newProps.max_date_allowed
};
const momentProps = this.convertPropsToMoment(propObj);
if (this.state.prevStartDate !== newProps.start_date) {
this.setState({
prevStartDate: newProps.start_date,
startDate: momentProps.startDate
});
}
if (this.state.prevEndDate !== newProps.end_date) {
this.setState({
prevEndDate: newProps.end_date,
endDate: momentProps.endDate
});
}
if (this.state.prevInitialVisibleMonth !== newProps.initial_visible_month) {
this.setState({
prevInitialVisibleMonth: newProps.initial_visible_month,
initialVisibleMonth: momentProps.initialVisibleMonth
});
}
if (this.state.prevMinDateAllowed !== newProps.min_date_allowed) {
this.setState({
prevMinDateAllowed: newProps.min_date_allowed,
minDateRange: momentProps.minDateAllowed
});
}
if (this.state.prevMaxDateAllowed !== newProps.max_date_range) {
this.setState({
prevMaxDateAllowed: newProps.max_date_allowed,
maxDateRange: momentProps.maxDateRange
});
}
try {
if (this.state.startDate.isAfter(this.state.endDate)) {
this.setState({
endDate: null
});
}
} catch (TypeError) {
// Continue regardless of error
}
}
render() {
const { setProps, fireEvent } = this.props;
let verticalFlag = true;
if (this.props.calendar_orientation === 'vertical') {
verticalFlag = false;
}
return (
<DateRangePicker
startDate={this.state.startDate}
startDatePlaceholderText={this.props.start_date_placeholder_text}
endDate={this.state.endDate}
endDatePlaceholderText={this.props.end_date_placeholder_text}
onDatesChange={({ startDate, endDate }) => {
this.setState({ startDate, endDate });
if (startDate !== null) {
const startDateStr = startDate.format('YYYY-MM-DD');
if (setProps) {
setProps({
start_date: startDateStr
});
}
if (fireEvent) {
fireEvent('change');
}
}
if (endDate !== null) {
const endDateStr = endDate.format('YYYY-MM-DD');
if (setProps) {
setProps({
end_date: endDateStr
});
}
if (fireEvent) {
fireEvent('change');
}
}
}
}
focusedInput={this.state.focusedInput}
onFocusChange={focusedInput => this.setState({ focusedInput })}
isOutsideRange={(date) => {
if (typeof this.state.minDateAllowed !== 'undefined' &&
typeof this.state.maxDateAllowed !== 'undefined') {
return date < this.state.minDateAllowed ||
date >= this.state.maxDateAllowed;
} else if (typeof this.state.minDateAllowed === 'undefined' &&
typeof this.state.maxDateAllowed !== 'undefined') {
return date >= this.state.maxDateAllowed;
} else if (typeof this.state.minDateAllowed !== 'undefined' &&
typeof this.state.maxDateAllowed === 'undefined') {
return date < this.state.minDateAllowed;
} else {
return false;
}
}
}
showClearDates={this.props.clearable}
disabled={this.props.disabled}
keepOpenOnDateSelect={this.props.stay_open_on_select}
reopenPickerOnClearDates={this.props.reopen_calendar_on_clear}
initialVisibleMonth={() => {
if (this.state.startDate !== null) {
return this.state.startDate;
} else {
return this.state.initialVisibleMonth;
}
}
}
numberOfMonths={this.props.number_of_months_shown}
withPortal={this.props.with_portal && verticalFlag}
withFullScreenPortal={this.props.with_full_screen_portal && verticalFlag}
firstDayOfWeek={this.props.first_day_of_week}
minimumNights={this.props.minimum_nights}
enableOutsideDays={this.props.show_outside_days}
monthFormat={this.props.month_format}
displayFormat={this.props.display_format}
isRTL={this.props.is_RTL}
orientation={this.props.calendar_orientation}
daySize={this.props.day_size}
/>
);
}
}
DatePickerRange.propTypes = {
id: PropTypes.string,
/**
* Specifies the starting date for the component.
* Accepts datetime.datetime objects or strings
* in the format 'YYYY-MM-DD'
*/
start_date: PropTypes.string,
/**
* Specifies the ending date for the component.
* Accepts datetime.datetime objects or strings
* in the format 'YYYY-MM-DD'
*/
end_date: PropTypes.string,
/**
* Specifies the lowest selectable date for the component.
* Accepts datetime.datetime objects or strings
* in the format 'YYYY-MM-DD'
*/
min_date_allowed: PropTypes.string,
/**
* Specifies the highest selectable date for the component.
* Accepts datetime.datetime objects or strings
* in the format 'YYYY-MM-DD'
*/
max_date_allowed: PropTypes.string,
/**
* Specifies the month that is initially presented when the user
* opens the calendar. Accepts datetime.datetime objects or strings
* in the format 'YYYY-MM-DD'
*
*/
initial_visible_month: PropTypes.string,
/**
* Text that will be displayed in the first input
* box of the date picker when no date is selected. Default value is 'Start Date'
*/
start_date_placeholder_text: PropTypes.string,
/**
* Text that will be displayed in the second input
* box of the date picker when no date is selected. Default value is 'End Date'
*/
end_date_placeholder_text: PropTypes.string,
/**
* Size of rendered calendar days, higher number
* means bigger day size and larger calendar overall
*/
day_size: PropTypes.number,
/**
* Orientation of calendar, either vertical or horizontal.
* Valid options are 'vertical' or 'horizontal'.
*/
calendar_orientation: PropTypes.oneOf(['vertical', 'horizontal']),
/**
* Determines whether the calendar and days operate
* from left to right or from right to left
*/
is_RTL: PropTypes.bool,
/**
* If True, the calendar will automatically open when cleared
*/
reopen_calendar_on_clear: PropTypes.bool,
/**
* Number of calendar months that are shown when calendar is opened
*/
number_of_months_shown: PropTypes.number,
/**
* If True, calendar will open in a screen overlay portal,
* not supported on vertical calendar
*/
with_portal: PropTypes.bool,
/**
* If True, calendar will open in a full screen overlay portal, will
* take precedent over 'withPortal' if both are set to true,
* not supported on vertical calendar
*/
with_full_screen_portal: PropTypes.bool,
/**
* Specifies what day is the first day of the week, values must be
* from [0, ..., 6] with 0 denoting Sunday and 6 denoting Saturday
*/
first_day_of_week: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6]),
/**
* Specifies a minimum number of nights that must be selected between
* the startDate and the endDate
*/
minimum_nights: PropTypes.number,
/**
* If True the calendar will not close when the user has selected a value
* and will wait until the user clicks off the calendar
*/
stay_open_on_select: PropTypes.bool,
/**
* If True the calendar will display days that rollover into
* the next month
*/
show_outside_days: PropTypes.bool,
/**
* Specifies the format that the month will be displayed in the calendar,
* valid formats are variations of "MM YY". For example:
* "MM YY" renders as '05 97' for May 1997
* "MMMM, YYYY" renders as 'May, 1997' for May 1997
* "MMM, YY" renders as 'Sep, 97' for September 1997
*/
month_format: PropTypes.string,
/**
* Specifies the format that the selected dates will be displayed
* valid formats are variations of "MM YY DD". For example:
* "MM YY DD" renders as '05 10 97' for May 10th 1997
* "MMMM, YY" renders as 'May, 1997' for May 10th 1997
* "M, D, YYYY" renders as '07, 10, 1997' for September 10th 1997
* "MMMM" renders as 'May' for May 10 1997
*/
display_format: PropTypes.string,
/**
* If True, no dates can be selected.
*/
disabled: PropTypes.bool,
/**
* Whether or not the dropdown is "clearable", that is, whether or
* not a small "x" appears on the right of the dropdown that removes
* the selected value.
*/
clearable: PropTypes.bool,
/**
* Dash-assigned callback that gets fired when the value changes.
*/
setProps: PropTypes.func,
/**
* Dash-assigned callback that gets fired when the value changes.
*/
dashEvents: PropTypes.oneOf(['change'])
};
DatePickerRange.defaultProps = {
calendar_orientation: 'horizontal',
is_RTL: false,
day_size: 39,
with_portal: false,
with_full_screen_portal: false,
first_day_of_week: 0,
number_of_months_shown: 1,
stay_open_on_select: false,
reopen_calendar_on_clear: false,
clearable: false,
disabled: false
};