flex-template-web/src/components/FieldDateRangeController/DateRangeController.js
Vesa Luusua fbd26bdfec SearchPage.duck.js: endDate should not expand end date for night
DateRangeController: unittype day should allow 0 night
EditListingPage.duck.js booking state should be passed to query
2019-01-29 14:48:08 +02:00

190 lines
5.5 KiB
JavaScript

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
DayPickerRangeController,
isInclusivelyAfterDay,
isInclusivelyBeforeDay,
} from 'react-dates';
import classNames from 'classnames';
import moment from 'moment';
import { START_DATE } from '../../util/dates';
import config from '../../config';
import { IconArrowHead } from '../../components';
import css from './DateRangeController.css';
export const HORIZONTAL_ORIENTATION = 'horizontal';
export const ANCHOR_LEFT = 'left';
// IconArrowHead component might not be defined if exposed directly to the file.
// This component is called before IconArrowHead component in components/index.js
const PrevIcon = props => (
<IconArrowHead {...props} direction="left" rootClassName={css.arrowIcon} />
);
const NextIcon = props => (
<IconArrowHead {...props} direction="right" rootClassName={css.arrowIcon} />
);
const defaultProps = {
startDateOffset: undefined,
endDateOffset: undefined,
// calendar presentation and interaction related props
orientation: HORIZONTAL_ORIENTATION,
verticalHeight: undefined,
withPortal: false,
isRTL: false,
initialVisibleMonth: null,
firstDayOfWeek: config.i18n.firstDayOfWeek,
numberOfMonths: 1,
daySize: 38,
keepOpenOnDateSelect: false,
renderCalendarInfo: null,
hideKeyboardShortcutsPanel: true,
// navigation related props
navPrev: <PrevIcon />,
navNext: <NextIcon />,
onPrevMonthClick() {},
onNextMonthClick() {},
transitionDuration: 200, // milliseconds between next month changes etc.
renderCalendarDay: undefined, // If undefined, renders react-dates/lib/components/CalendarDay
// day presentation and interaction related props
renderDayContents: day => {
return <span className="renderedDay">{day.format('D')}</span>;
},
minimumNights: config.bookingUnitType === 'line-item/night' ? 1 : 0,
enableOutsideDays: false,
isDayBlocked: () => false,
// outside range -><- today ... today+available days -1 -><- outside range
isOutsideRange: day => {
const endOfRange = config.dayCountAvailableForBooking - 1;
return (
!isInclusivelyAfterDay(day, moment()) ||
!isInclusivelyBeforeDay(day, moment().add(endOfRange, 'days'))
);
},
isDayHighlighted: () => {},
// Internationalization props
// Multilocale support can be achieved with displayFormat like moment.localeData.longDateFormat('L')
// https://momentjs.com/
// displayFormat: 'ddd, MMM D',
monthFormat: 'MMMM YYYY',
weekDayFormat: 'dd',
phrases: {}, // Add overwrites to default phrases used by react-dates
};
class DateRangeController extends Component {
constructor(props) {
super(props);
this.state = {
startDate: props.value && props.value.startDate ? moment(props.value.startDate) : null,
endDate: props.value && props.value.endDate ? moment(props.value.endDate) : null,
focusedInput: START_DATE,
};
this.onDatesChange = this.onDatesChange.bind(this);
this.onFocusChange = this.onFocusChange.bind(this);
this.onReset = this.onReset.bind(this);
}
onDatesChange(values) {
const { startDate, endDate } = values;
const start = startDate ? startDate.toDate() : null;
const end = endDate ? endDate.toDate() : null;
this.setState({ startDate, endDate });
if (startDate && endDate) {
this.props.onChange({ startDate: start, endDate: end });
}
}
onFocusChange(focusedInput) {
this.setState({
// Force the focusedInput to always be truthy so that dates are always selectable
focusedInput: !focusedInput ? START_DATE : focusedInput,
});
}
onReset(startDate, endDate) {
if (startDate && endDate) {
this.setState({
startDate: moment(startDate),
endDate: moment(endDate),
focusedInput: START_DATE,
});
} else {
this.setState({
startDate: null,
endDate: null,
focusedInput: START_DATE,
});
}
}
render() {
// Removing Final Form field props: name, value, onChange, onFocus, meta, children, render
const {
rootClassName,
className,
name,
value,
onChange,
onFocus,
meta,
children,
render,
...controllerProps
} = this.props;
const classes = classNames(rootClassName || css.inputRoot, className);
const startDateFromState = this.state.startDate;
const endDateFromState = this.state.endDate;
const startDateFromForm = value && value.startDate ? moment(value.startDate) : null;
const endDateFromForm = value && value.endDate ? moment(value.endDate) : null;
const isSelected = startDateFromState && endDateFromState;
// Value given by Final Form reflects url params and is valid if both dates are set.
// If only one date is selected state should be used to get the correct date.
const startDate = isSelected ? startDateFromForm : startDateFromState;
const endDate = isSelected ? endDateFromForm : endDateFromState;
return (
<div className={classes}>
<DayPickerRangeController
{...controllerProps}
startDate={startDate}
endDate={endDate}
onDatesChange={this.onDatesChange}
focusedInput={this.state.focusedInput}
onFocusChange={this.onFocusChange}
/>
</div>
);
}
}
DateRangeController.defaultProps = {
rootClassName: null,
className: null,
...defaultProps,
};
const { string } = PropTypes;
DateRangeController.propTypes = {
rootClassName: string,
className: string,
};
export default DateRangeController;