diff --git a/packages/datetime/src/dateRangePicker.tsx b/packages/datetime/src/dateRangePicker.tsx index e609ccdf3e..db6db8baa0 100644 --- a/packages/datetime/src/dateRangePicker.tsx +++ b/packages/datetime/src/dateRangePicker.tsx @@ -4,20 +4,10 @@ * Licensed under the terms of the LICENSE file distributed with this project. */ -import { - AbstractPureComponent, - Boundary, - Classes, - DISPLAYNAME_PREFIX, - Divider, - IProps, - Menu, - MenuItem, - Utils, -} from "@blueprintjs/core"; +import { AbstractPureComponent, Boundary, DISPLAYNAME_PREFIX, Divider, IProps, Utils } from "@blueprintjs/core"; import classNames from "classnames"; import * as React from "react"; -import ReactDayPicker from "react-day-picker"; +import DayPicker from "react-day-picker"; import { DayModifiers } from "react-day-picker/types/common"; import { CaptionElementProps, DayPickerProps, NavbarElementProps } from "react-day-picker/types/props"; @@ -40,6 +30,7 @@ import { } from "./datePickerCore"; import { DatePickerNavbar } from "./datePickerNavbar"; import { DateRangeSelectionStrategy } from "./dateRangeSelectionStrategy"; +import { Shortcuts } from "./shortcuts"; export interface IDateRangeShortcut { label: string; @@ -200,85 +191,21 @@ export class DateRangePicker extends AbstractPureComponent - {shortcuts} - - - ); - } else { - return ( -
- {shortcuts} - - -
- ); - } + const classes = classNames(DateClasses.DATEPICKER, DateClasses.DATERANGEPICKER, className, { + [DateClasses.DATERANGEPICKER_CONTIGUOUS]: contiguousCalendarMonths, + [DateClasses.DATERANGEPICKER_SINGLE_MONTH]: isShowingOneMonth, + }); + + // use the left DayPicker when we only need one + return ( +
+ {this.maybeRenderShortcuts()} + {this.renderCalendars(isShowingOneMonth)} +
+ ); } public componentWillReceiveProps(nextProps: IDateRangePickerProps) { @@ -329,34 +256,80 @@ export class DateRangePicker extends AbstractPureComponent ( - - )); - + const { allowSingleDayRange, maxDate, minDate } = this.props; return [ - - {shortcutElements} - , + , , ]; } + private renderCalendars(isShowingOneMonth: boolean) { + const { contiguousCalendarMonths, dayPickerProps, locale, localeUtils, maxDate, minDate } = this.props; + const dayPickerBaseProps: DayPickerProps = { + locale, + localeUtils, + modifiers: combineModifiers(this.modifiers, this.props.modifiers), + showOutsideDays: true, + ...dayPickerProps, + disabledDays: this.getDisabledDaysModifier(), + onDayClick: this.handleDayClick, + onDayMouseEnter: this.handleDayMouseEnter, + onDayMouseLeave: this.handleDayMouseLeave, + selectedDays: this.state.value, + }; + + if (contiguousCalendarMonths || isShowingOneMonth) { + return ( + + ); + } else { + return [ + , + , + ]; + } + } + private renderNavbar = (navbarProps: NavbarElementProps) => ( ); @@ -442,11 +415,7 @@ export class DateRangePicker extends AbstractPureComponent this.handleNextState(nextValue); - } - - private handleNextState(nextValue: DateRange) { + private handleNextState = (nextValue: DateRange) => { const { value } = this.state; const nextState = getStateChange(value, nextValue, this.state, this.props.contiguousCalendarMonths); @@ -455,7 +424,7 @@ export class DateRangePicker extends AbstractPureComponent { const leftView = MonthAndYear.fromDate(newDate); @@ -553,10 +522,6 @@ export class DateRangePicker extends AbstractPureComponent void) => { - const returnVal = DateUtils.clone(today); - action(returnVal); - returnVal.setDate(returnVal.getDate() + 1); - return returnVal; - }; - - const yesterday = makeDate(d => d.setDate(d.getDate() - 2)); - const oneWeekAgo = makeDate(d => d.setDate(d.getDate() - 7)); - const oneMonthAgo = makeDate(d => d.setMonth(d.getMonth() - 1)); - const threeMonthsAgo = makeDate(d => d.setMonth(d.getMonth() - 3)); - const sixMonthsAgo = makeDate(d => d.setMonth(d.getMonth() - 6)); - const oneYearAgo = makeDate(d => d.setFullYear(d.getFullYear() - 1)); - const twoYearsAgo = makeDate(d => d.setFullYear(d.getFullYear() - 2)); - - const singleDayShortcuts = allowSingleDayRange - ? [createShortcut("Today", [today, today]), createShortcut("Yesterday", [yesterday, yesterday])] - : []; - - return [ - ...singleDayShortcuts, - createShortcut("Past week", [oneWeekAgo, today]), - createShortcut("Past month", [oneMonthAgo, today]), - createShortcut("Past 3 months", [threeMonthsAgo, today]), - createShortcut("Past 6 months", [sixMonthsAgo, today]), - createShortcut("Past year", [oneYearAgo, today]), - createShortcut("Past 2 years", [twoYearsAgo, today]), - ]; -} - function getInitialValue(props: IDateRangePickerProps): DateRange | null { if (props.value != null) { return props.value; diff --git a/packages/datetime/src/shortcuts.tsx b/packages/datetime/src/shortcuts.tsx new file mode 100644 index 0000000000..b668a5b7a5 --- /dev/null +++ b/packages/datetime/src/shortcuts.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2018 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the terms of the LICENSE file distributed with this project. + */ + +import { Classes, Menu, MenuItem } from "@blueprintjs/core"; +import React from "react"; +import { DATERANGEPICKER_SHORTCUTS } from "./common/classes"; +import { clone, DateRange, isDayRangeInRange } from "./common/dateUtils"; + +export interface IDateRangeShortcut { + label: string; + dateRange: DateRange; +} + +export interface IShortcutsProps { + allowSingleDayRange: boolean; + minDate: Date; + maxDate: Date; + shortcuts: IDateRangeShortcut[] | true; + onShortcutClick: (shortcut: DateRange) => void; +} + +export class Shortcuts extends React.PureComponent { + public render() { + const shortcuts = + this.props.shortcuts === true + ? createDefaultShortcuts(this.props.allowSingleDayRange) + : this.props.shortcuts; + + const shortcutElements = shortcuts.map((s, i) => ( + + )); + + return {shortcutElements}; + } + + private getShorcutClickHandler(nextValue: DateRange) { + return () => this.props.onShortcutClick(nextValue); + } + + private isShortcutInRange(shortcutDateRange: DateRange) { + return isDayRangeInRange(shortcutDateRange, [this.props.minDate, this.props.maxDate]); + } +} + +function createShortcut(label: string, dateRange: DateRange): IDateRangeShortcut { + return { dateRange, label }; +} + +function createDefaultShortcuts(allowSingleDayRange: boolean) { + const today = new Date(); + const makeDate = (action: (d: Date) => void) => { + const returnVal = clone(today); + action(returnVal); + returnVal.setDate(returnVal.getDate() + 1); + return returnVal; + }; + + const yesterday = makeDate(d => d.setDate(d.getDate() - 2)); + const oneWeekAgo = makeDate(d => d.setDate(d.getDate() - 7)); + const oneMonthAgo = makeDate(d => d.setMonth(d.getMonth() - 1)); + const threeMonthsAgo = makeDate(d => d.setMonth(d.getMonth() - 3)); + const sixMonthsAgo = makeDate(d => d.setMonth(d.getMonth() - 6)); + const oneYearAgo = makeDate(d => d.setFullYear(d.getFullYear() - 1)); + const twoYearsAgo = makeDate(d => d.setFullYear(d.getFullYear() - 2)); + + const singleDayShortcuts = allowSingleDayRange + ? [createShortcut("Today", [today, today]), createShortcut("Yesterday", [yesterday, yesterday])] + : []; + + return [ + ...singleDayShortcuts, + createShortcut("Past week", [oneWeekAgo, today]), + createShortcut("Past month", [oneMonthAgo, today]), + createShortcut("Past 3 months", [threeMonthsAgo, today]), + createShortcut("Past 6 months", [sixMonthsAgo, today]), + createShortcut("Past year", [oneYearAgo, today]), + createShortcut("Past 2 years", [twoYearsAgo, today]), + ]; +}