/* * SonarQube * Copyright (C) 2009-2019 SonarSource SA * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; import * as classNames from 'classnames'; import CalendarIcon from 'sonar-ui-common/components/icons/CalendarIcon'; import ChevronLeftIcon from 'sonar-ui-common/components/icons/ChevronLeftIcon'; import ChevronRightIcon from 'sonar-ui-common/components/icons/ChevronRightIcon'; import { DayModifiers, Modifier, Modifiers } from 'react-day-picker'; import { InjectedIntlProps, injectIntl } from 'react-intl'; import { range } from 'lodash'; import * as addMonths from 'date-fns/add_months'; import * as setMonth from 'date-fns/set_month'; import * as setYear from 'date-fns/set_year'; import * as subMonths from 'date-fns/sub_months'; import { getShortMonthName, getWeekDayName, getShortWeekDayName } from 'sonar-ui-common/helpers/l10n'; import { ButtonIcon, ClearButton } from 'sonar-ui-common/components/controls/buttons'; import OutsideClickHandler from 'sonar-ui-common/components/controls/OutsideClickHandler'; import { lazyLoad } from 'sonar-ui-common/components/lazyLoad'; import Select from 'sonar-ui-common/components/controls/Select'; import './DayPicker.css'; import './styles.css'; const DayPicker = lazyLoad(() => import('react-day-picker')); interface Props { className?: string; currentMonth?: Date; highlightFrom?: Date; highlightTo?: Date; inputClassName?: string; maxDate?: Date; minDate?: Date; name?: string; onChange: (date: Date | undefined) => void; placeholder: string; value?: Date; } interface State { currentMonth: Date; open: boolean; lastHovered?: Date; } type Week = [string, string, string, string, string, string, string]; export default class DateInput extends React.PureComponent { input?: HTMLInputElement | null; constructor(props: Props) { super(props); this.state = { currentMonth: props.value || props.currentMonth || new Date(), open: false }; } focus = () => { if (this.input) { this.input.focus(); } this.openCalendar(); }; handleResetClick = () => { this.closeCalendar(); this.props.onChange(undefined); }; openCalendar = () => { this.setState({ currentMonth: this.props.value || this.props.currentMonth || new Date(), lastHovered: undefined, open: true }); }; closeCalendar = () => { this.setState({ open: false }); }; handleDayClick = (day: Date, modifiers: DayModifiers) => { if (!modifiers.disabled) { this.closeCalendar(); this.props.onChange(day); } }; handleDayMouseEnter = (day: Date, modifiers: DayModifiers) => { this.setState({ lastHovered: modifiers.disabled ? undefined : day }); }; handleCurrentMonthChange = ({ value }: { value: number }) => { this.setState((state: State) => ({ currentMonth: setMonth(state.currentMonth, value) })); }; handleCurrentYearChange = ({ value }: { value: number }) => { this.setState(state => ({ currentMonth: setYear(state.currentMonth, value) })); }; handlePreviousMonthClick = () => { this.setState(state => ({ currentMonth: subMonths(state.currentMonth, 1) })); }; handleNextMonthClick = () => { this.setState(state => ({ currentMonth: addMonths(state.currentMonth, 1) })); }; render() { const { highlightFrom, highlightTo, minDate, value } = this.props; const { lastHovered } = this.state; const after = this.props.maxDate || new Date(); const months = range(12); const years = range(new Date().getFullYear() - 10, new Date().getFullYear() + 1); const selectedDays: Modifier[] = value ? [value] : []; let modifiers: Partial | undefined; const lastHoveredOrValue = lastHovered || value; if (highlightFrom && lastHoveredOrValue) { modifiers = { highlighted: { from: highlightFrom, to: lastHoveredOrValue } }; selectedDays.push(highlightFrom); } if (highlightTo && lastHoveredOrValue) { modifiers = { highlighted: { from: lastHoveredOrValue, to: highlightTo } }; selectedDays.push(highlightTo); } const weekdaysLong = range(7).map(getWeekDayName) as Week; const weekdaysShort = range(7).map(getShortWeekDayName) as Week; return ( (this.input = node)} name={this.props.name} onFocus={this.openCalendar} placeholder={this.props.placeholder} readOnly={true} type="text" value={value} /> {this.props.value !== undefined && ( )} {this.state.open && (
} disabledDays={{ after, before: minDate }} firstDayOfWeek={1} modifiers={modifiers} month={this.state.currentMonth} navbarElement={} onDayClick={this.handleDayClick} onDayMouseEnter={this.handleDayMouseEnter} selectedDays={selectedDays} weekdaysLong={weekdaysLong} weekdaysShort={weekdaysShort} />
)}
); } } function NullComponent() { return null; } type InputWrapperProps = T.Omit, 'value'> & InjectedIntlProps & { innerRef: React.Ref; value: Date | undefined; }; const InputWrapper = injectIntl(({ innerRef, intl, value, ...other }: InputWrapperProps) => { const formattedValue = value && intl.formatDate(value, { year: 'numeric', month: 'short', day: 'numeric' }); return ; });