@hackplan/polaris
Version:
Shopify’s product component library
158 lines (157 loc) • 7.57 kB
JavaScript
import React from 'react';
import { ArrowLeftMinor, ArrowRightMinor } from '@shopify/polaris-icons';
import { Months, isDateAfter, isDateBefore, getNextDisplayYear, getNextDisplayMonth, getPreviousDisplayYear, getPreviousDisplayMonth, Weekdays, isSameDay, } from '@shopify/javascript-utilities/dates';
import { withAppProvider } from '../AppProvider';
import Button from '../Button';
import { monthName } from './utilities';
import { Month } from './components';
import styles from './DatePicker.scss';
export { Months };
export class DatePicker extends React.PureComponent {
constructor() {
super(...arguments);
this.state = {
hoverDate: undefined,
focusDate: undefined,
};
this.handleFocus = (date) => {
this.setState({ focusDate: date });
};
this.resetFocus = () => {
this.setState({ focusDate: undefined });
};
this.handleKeyUp = (event) => {
const { key } = event;
const { selected, disableDatesBefore, disableDatesAfter } = this.props;
const { focusDate } = this.state;
const range = deriveRange(selected);
const focusedDate = focusDate || (range && range.start);
if (focusedDate == null) {
return;
}
if (key === 'ArrowUp') {
const previousWeek = new Date(focusedDate);
previousWeek.setDate(focusedDate.getDate() - 7);
if (!(disableDatesBefore && isDateBefore(previousWeek, disableDatesBefore))) {
this.setFocusDateAndHandleMonthChange(previousWeek);
}
}
if (key === 'ArrowDown') {
const nextWeek = new Date(focusedDate);
nextWeek.setDate(focusedDate.getDate() + 7);
if (!(disableDatesAfter && isDateAfter(nextWeek, disableDatesAfter))) {
this.setFocusDateAndHandleMonthChange(nextWeek);
}
}
if (key === 'ArrowRight') {
const tomorrow = new Date(focusedDate);
tomorrow.setDate(focusedDate.getDate() + 1);
if (!(disableDatesAfter && isDateAfter(tomorrow, disableDatesAfter))) {
this.setFocusDateAndHandleMonthChange(tomorrow);
}
}
if (key === 'ArrowLeft') {
const yesterday = new Date(focusedDate);
yesterday.setDate(focusedDate.getDate() - 1);
if (!(disableDatesBefore && isDateBefore(yesterday, disableDatesBefore))) {
this.setFocusDateAndHandleMonthChange(yesterday);
}
}
};
this.setFocusDateAndHandleMonthChange = (date) => {
const { onMonthChange } = this.props;
if (onMonthChange) {
onMonthChange(date.getMonth(), date.getFullYear());
}
this.setState({
hoverDate: date,
focusDate: date,
});
};
this.handleDateSelection = (range) => {
const { end } = range;
const { onChange = noop } = this.props;
this.setState({ hoverDate: end, focusDate: new Date(end) }, () => onChange(range));
};
this.handleMonthChangeClick = (month, year) => {
const { onMonthChange } = this.props;
if (!onMonthChange) {
return;
}
this.setState({
focusDate: undefined,
});
onMonthChange(month, year);
};
this.handleHover = (date) => {
this.setState({
hoverDate: date,
});
};
}
componentDidUpdate(prevProps) {
const selectedPropDidChange = !isSameSelectedDate(prevProps.selected, this.props.selected);
if (selectedPropDidChange) {
this.resetFocus();
}
}
render() {
const { id, selected, month, year, allowRange, multiMonth, disableDatesBefore, disableDatesAfter, weekStartsOn = Weekdays.Sunday, polaris: { intl }, } = this.props;
const { hoverDate, focusDate } = this.state;
const showNextYear = getNextDisplayYear(month, year);
const showNextMonth = getNextDisplayMonth(month);
const showNextToNextYear = getNextDisplayYear(showNextMonth, showNextYear);
const showNextToNextMonth = getNextDisplayMonth(showNextMonth);
const showPreviousYear = getPreviousDisplayYear(month, year);
const showPreviousMonth = getPreviousDisplayMonth(month);
const previousMonthName = intl.translate(`Polaris.DatePicker.months.${monthName(showPreviousMonth)}`);
const nextMonth = multiMonth
? intl.translate(`Polaris.DatePicker.months.${monthName(showNextToNextMonth)}`)
: intl.translate(`Polaris.DatePicker.months.${monthName(showNextMonth)}`);
const nextYear = multiMonth ? showNextToNextYear : showNextYear;
const secondDatePicker = multiMonth ? (<Month onFocus={this.handleFocus} focusedDate={focusDate} month={showNextMonth} year={showNextYear} selected={deriveRange(selected)} hoverDate={hoverDate} onChange={this.handleDateSelection} onHover={this.handleHover} disableDatesBefore={disableDatesBefore} disableDatesAfter={disableDatesAfter} allowRange={allowRange} weekStartsOn={weekStartsOn}/>) : null;
return (<div id={id} className={styles.DatePicker} onKeyDown={handleKeyDown} onKeyUp={this.handleKeyUp}>
<div className={styles.Header}>
<Button plain icon={ArrowLeftMinor} accessibilityLabel={intl.translate('Polaris.DatePicker.previousMonth', {
previousMonthName,
showPreviousYear,
})} onClick={this.handleMonthChangeClick.bind(null, showPreviousMonth, showPreviousYear)}/>
<Button plain icon={ArrowRightMinor} accessibilityLabel={intl.translate('Polaris.DatePicker.nextMonth', {
nextMonth,
nextYear,
})} onClick={this.handleMonthChangeClick.bind(null, showNextMonth, showNextYear)}/>
</div>
<div className={styles.MonthContainer}>
<Month onFocus={this.handleFocus} focusedDate={focusDate} month={month} year={year} selected={deriveRange(selected)} hoverDate={hoverDate} onChange={this.handleDateSelection} onHover={this.handleHover} disableDatesBefore={disableDatesBefore} disableDatesAfter={disableDatesAfter} allowRange={allowRange} weekStartsOn={weekStartsOn}/>
{secondDatePicker}
</div>
</div>);
}
}
function noop() { }
function handleKeyDown(event) {
const { key } = event;
if (key === 'ArrowUp' ||
key === 'ArrowDown' ||
key === 'ArrowLeft' ||
key === 'ArrowRight') {
event.preventDefault();
event.stopPropagation();
}
}
function isSameSelectedDate(previousDate, currentDate) {
if (previousDate == null || currentDate == null) {
return previousDate == null && currentDate == null;
}
if (previousDate instanceof Date || currentDate instanceof Date) {
return (previousDate instanceof Date &&
currentDate instanceof Date &&
isSameDay(previousDate, currentDate));
}
return (isSameDay(previousDate.start, currentDate.start) &&
isSameDay(previousDate.end, currentDate.end));
}
function deriveRange(selected) {
return selected instanceof Date ? { start: selected, end: selected } : selected;
}
export default withAppProvider()(DatePicker);