2018-05-28 10:55:19 +00:00
|
|
|
/** @format */
|
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
|
|
|
import { Component, Fragment } from '@wordpress/element';
|
|
|
|
import moment from 'moment';
|
|
|
|
import {
|
|
|
|
DayPickerRangeController,
|
|
|
|
isInclusivelyAfterDay,
|
|
|
|
isInclusivelyBeforeDay,
|
|
|
|
} from 'react-dates';
|
|
|
|
import { partial } from 'lodash';
|
|
|
|
import { __, sprintf } from '@wordpress/i18n';
|
|
|
|
import classnames from 'classnames';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import 'react-dates/lib/css/_datepicker.css';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import { toMoment } from 'lib/date';
|
|
|
|
import phrases from './phrases';
|
|
|
|
import './style.scss';
|
|
|
|
|
|
|
|
const START_DATE = 'startDate';
|
|
|
|
const END_DATE = 'endDate';
|
|
|
|
|
|
|
|
// 782px is the width designated by Gutenberg's `</ Popover>` component.
|
|
|
|
// * https://github.com/WordPress/gutenberg/blob/c8f8806d4465a83c1a0bc62d5c61377b56fa7214/components/popover/utils.js#L6
|
|
|
|
const isMobileViewport = () => window.innerWidth < 782;
|
2018-06-27 03:54:01 +00:00
|
|
|
const shortDateFormat = __( 'MM/DD/YYYY', 'woo-dash' );
|
2018-05-28 10:55:19 +00:00
|
|
|
|
|
|
|
class DateRange extends Component {
|
|
|
|
constructor( props ) {
|
|
|
|
super( props );
|
|
|
|
|
2018-07-02 02:51:00 +00:00
|
|
|
const { after, before } = props;
|
2018-05-28 10:55:19 +00:00
|
|
|
this.state = {
|
|
|
|
focusedInput: START_DATE,
|
2018-07-02 02:51:00 +00:00
|
|
|
afterText: after ? after.format( shortDateFormat ) : '',
|
|
|
|
beforeText: before ? before.format( shortDateFormat ) : '',
|
2018-05-28 10:55:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
this.onDatesChange = this.onDatesChange.bind( this );
|
|
|
|
this.onFocusChange = this.onFocusChange.bind( this );
|
|
|
|
this.onInputChange = this.onInputChange.bind( this );
|
|
|
|
this.getOutsideRange = this.getOutsideRange.bind( this );
|
|
|
|
}
|
|
|
|
|
|
|
|
onDatesChange( { startDate, endDate } ) {
|
|
|
|
this.setState( {
|
2018-07-02 02:51:00 +00:00
|
|
|
afterText: startDate ? startDate.format( shortDateFormat ) : '',
|
|
|
|
beforeText: endDate ? endDate.format( shortDateFormat ) : '',
|
2018-05-28 10:55:19 +00:00
|
|
|
} );
|
|
|
|
this.props.onSelect( {
|
2018-07-02 02:51:00 +00:00
|
|
|
after: startDate,
|
|
|
|
before: endDate,
|
2018-05-28 10:55:19 +00:00
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
onFocusChange( focusedInput ) {
|
|
|
|
this.setState( {
|
|
|
|
focusedInput: ! focusedInput ? START_DATE : focusedInput,
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
onInputChange( input, event ) {
|
|
|
|
const value = event.target.value;
|
2018-07-02 02:51:00 +00:00
|
|
|
this.setState( { [ input + 'Text' ]: value } );
|
2018-06-27 03:54:01 +00:00
|
|
|
const date = toMoment( shortDateFormat, value );
|
2018-05-28 10:55:19 +00:00
|
|
|
if ( date ) {
|
2018-07-02 02:51:00 +00:00
|
|
|
this.props.onSelect( {
|
|
|
|
[ input ]: date,
|
|
|
|
} );
|
2018-05-28 10:55:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getOutsideRange() {
|
|
|
|
const { inValidDays } = this.props;
|
|
|
|
if ( 'string' === typeof inValidDays ) {
|
|
|
|
switch ( inValidDays ) {
|
|
|
|
case 'past':
|
|
|
|
return day => isInclusivelyBeforeDay( day, moment() );
|
|
|
|
case 'future':
|
|
|
|
return day => isInclusivelyAfterDay( day, moment() );
|
|
|
|
case 'none':
|
|
|
|
default:
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 'function' === typeof inValidDays ? inValidDays : undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2018-07-02 02:51:00 +00:00
|
|
|
const { focusedInput, afterText, beforeText } = this.state;
|
|
|
|
const { after, before } = this.props;
|
2018-05-28 10:55:19 +00:00
|
|
|
const isOutsideRange = this.getOutsideRange();
|
|
|
|
const isMobile = isMobileViewport();
|
|
|
|
return (
|
|
|
|
<Fragment>
|
|
|
|
<div className="woocommerce-date-picker__date-inputs">
|
|
|
|
<input
|
2018-07-02 02:51:00 +00:00
|
|
|
value={ afterText }
|
2018-05-28 10:55:19 +00:00
|
|
|
type="text"
|
2018-07-02 02:51:00 +00:00
|
|
|
onChange={ partial( this.onInputChange, 'after' ) }
|
2018-05-28 10:55:19 +00:00
|
|
|
aria-label={ __( 'Start Date', 'woo-dash' ) }
|
2018-07-02 02:51:00 +00:00
|
|
|
id="after-date-string"
|
|
|
|
aria-describedby="after-date-string-message"
|
2018-05-28 10:55:19 +00:00
|
|
|
/>
|
2018-07-02 02:51:00 +00:00
|
|
|
<p className="screen-reader-text" id="after-date-string-message">
|
2018-05-28 10:55:19 +00:00
|
|
|
{ sprintf(
|
|
|
|
__(
|
|
|
|
"Date input describing a selected date range's start date in format %s",
|
|
|
|
'woo-dash'
|
|
|
|
),
|
2018-06-27 03:54:01 +00:00
|
|
|
shortDateFormat
|
2018-05-28 10:55:19 +00:00
|
|
|
) }
|
|
|
|
</p>
|
|
|
|
<span>{ __( 'to', 'woo-dash' ) }</span>
|
|
|
|
<input
|
2018-07-02 02:51:00 +00:00
|
|
|
value={ beforeText }
|
2018-05-28 10:55:19 +00:00
|
|
|
type="text"
|
2018-07-02 02:51:00 +00:00
|
|
|
onChange={ partial( this.onInputChange, 'before' ) }
|
2018-05-28 10:55:19 +00:00
|
|
|
aria-label={ __( 'End Date', 'woo-dash' ) }
|
2018-07-02 02:51:00 +00:00
|
|
|
id="before-date-string"
|
|
|
|
aria-describedby="before-date-string-message"
|
2018-05-28 10:55:19 +00:00
|
|
|
/>
|
2018-07-02 02:51:00 +00:00
|
|
|
<p className="screen-reader-text" id="before-date-string-message">
|
2018-05-28 10:55:19 +00:00
|
|
|
{ sprintf(
|
|
|
|
__(
|
|
|
|
"Date input describing a selected date range's end date in format %s",
|
|
|
|
'woo-dash'
|
|
|
|
),
|
2018-06-27 03:54:01 +00:00
|
|
|
shortDateFormat
|
2018-05-28 10:55:19 +00:00
|
|
|
) }
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div
|
|
|
|
className={ classnames( 'woocommerce-calendar', {
|
|
|
|
'is-mobile': isMobile,
|
|
|
|
} ) }
|
|
|
|
>
|
|
|
|
<DayPickerRangeController
|
|
|
|
onDatesChange={ this.onDatesChange }
|
|
|
|
onFocusChange={ this.onFocusChange }
|
|
|
|
focusedInput={ focusedInput }
|
2018-07-02 02:51:00 +00:00
|
|
|
startDate={ after }
|
2018-05-28 10:55:19 +00:00
|
|
|
startDateId={ START_DATE }
|
|
|
|
startDatePlaceholderText={ 'Start Date' }
|
2018-07-02 02:51:00 +00:00
|
|
|
endDate={ before }
|
2018-05-28 10:55:19 +00:00
|
|
|
endDateId={ END_DATE }
|
|
|
|
endDatePlaceholderText={ 'End Date' }
|
|
|
|
orientation={ 'horizontal' }
|
|
|
|
numberOfMonths={ 1 }
|
|
|
|
isOutsideRange={ isOutsideRange }
|
|
|
|
minimumNights={ 0 }
|
|
|
|
hideKeyboardShortcutsPanel
|
|
|
|
noBorder
|
2018-07-02 02:51:00 +00:00
|
|
|
initialVisibleMonth={ () => after || moment() }
|
2018-05-28 10:55:19 +00:00
|
|
|
phrases={ phrases }
|
2018-07-04 01:50:12 +00:00
|
|
|
firstDayOfWeek={ Number( wcSettings.date.dow ) }
|
2018-05-28 10:55:19 +00:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</Fragment>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DateRange.propTypes = {
|
2018-07-02 02:51:00 +00:00
|
|
|
after: PropTypes.object,
|
|
|
|
before: PropTypes.object,
|
2018-05-28 10:55:19 +00:00
|
|
|
onSelect: PropTypes.func.isRequired,
|
|
|
|
inValidDays: PropTypes.oneOfType( [
|
|
|
|
PropTypes.oneOf( [ 'past', 'future', 'none' ] ),
|
|
|
|
PropTypes.func,
|
|
|
|
] ),
|
|
|
|
};
|
|
|
|
|
|
|
|
export { DateRange };
|