terra-date-picker
Version:
The terra-date-picker component provides users a way to enter or select a date from the date picker.
928 lines (870 loc) • 28.8 kB
JSX
import Calendar from './calendar';
import React from 'react';
import PropTypes from 'prop-types';
import FocusTrap from 'focus-trap-react';
import { Portal } from 'react-portal';
import * as KeyCode from 'keycode-js';
import Popup from 'terra-popup';
import classNames from 'classnames/bind';
import { injectIntl } from 'react-intl';
import VisuallyHiddenText from 'terra-visually-hidden-text';
import PopupContainer from './PopupContainer';
import DateUtil from '../DateUtil';
import {
newDate,
now,
isMoment,
isDate,
isBefore,
isAfter,
getMonth,
addDays,
addMonths,
addWeeks,
addYears,
subtractDays,
subtractMonths,
subtractWeeks,
subtractYears,
isSameDay,
isDayDisabled,
isDayInRange,
getEffectiveMinDate,
getEffectiveMaxDate,
parseDate,
safeDateFormat,
getHightLightDaysMap,
getLocalizedDateForScreenReader,
getMonthFromDate,
dateValues
} from './date_utils'
import onClickOutside from 'react-onclickoutside'
import styles from './stylesheets/react_datepicker.module.scss'
const cx = classNames.bind(styles);
const outsideClickIgnoreClass = 'react-datepicker-ignore-onclickoutside'
const WrappedCalendar = onClickOutside(Calendar)
/**
* General datepicker component.
*/
class DatePicker extends React.Component {
static propTypes = {
/**
* Prop to change date when a valid date is selected.
*/
adjustDateOnChange: PropTypes.bool,
/**
* Whether or not to allow same days to be selected.
*/
allowSameDay: PropTypes.bool,
/**
* Prop to auto complete date.
*/
autoComplete: PropTypes.string,
/**
* Whether or not to auto focus on date picker.
*/
autoFocus: PropTypes.bool,
/**
* Classname for styling calendar.
*/
calendarClassName: PropTypes.string,
/**
* Components to render within date picker.
*/
children: PropTypes.node,
/**
* Classname for styling date picker.
*/
className: PropTypes.string,
/**
* Prop for custom input in date picker.
*/
customInput: PropTypes.element,
/**
* Prop for custom input reference in date picker.
*/
customInputRef: PropTypes.string,
/**
* Format of the date selected.
*/
dateFormat: PropTypes.oneOfType([ // eslint-disable-line react/no-unused-prop-types
PropTypes.string,
PropTypes.array
]),
/**
* Date format displayed on calendar header.
*/
dateFormatCalendar: PropTypes.string,
/**
* A callback function for custom day class names.
*/
dayClassName: PropTypes.func,
/**
* Prop to disable dates on calendar.
*/
disabled: PropTypes.bool,
/**
* Prop to disable keyboard navigation.
*/
disabledKeyboardNavigation: PropTypes.bool,
/**
* Whether the year and month dropdowns should be in the scroll or select mode.
*/
dropdownMode: PropTypes.oneOf(['scroll', 'select']).isRequired,
/**
* Maximum Date value for a date range.
*/
endDate: PropTypes.object,
/**
* Array to store values of date that are disabled to pick .
*/
excludeDates: PropTypes.array,
/**
* A callback function to be executed to determine if a given date should be filtered.
*/
filterDate: PropTypes.func,
/**
* Specifies whether the height of calendar dom fixed or variable.
*/
fixedHeight: PropTypes.bool,
/**
* A callback function to format week number .
*/
formatWeekNumber: PropTypes.func,
/**
* Highlight range of dates with custom classes.
*/
highlightDates: PropTypes.instanceOf(WeakMap),
/**
* Value of custom input id.
*/
id: PropTypes.string,
/**
* Show dates only in the given array.
*/
includeDates: PropTypes.array,
/**
* Timezone value to indicate in which timezone the date-time component is rendered.
* The value provided should be a valid [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) string, else will default to browser/local timezone.
*/
initialTimeZone: PropTypes.string,
/**
* Prop to render Inline version of datepicker component.
*/
inline: PropTypes.bool,
/**
* @private
* Internationalization object with translation APIs. Provided by `injectIntl`.
*/
intl: PropTypes.shape({ formatMessage: PropTypes.func }),
/**
* Prop to determine whether or not the date picker is clearable.
*/
isClearable: PropTypes.bool,
/**
* Name of locale data for different international formatting.
*/
locale: PropTypes.string,
/**
* Maximum value of date that can be selected by user.
*/
maxDate: PropTypes.object,
/**
* Minimum value of date that can be selected by user.
*/
minDate: PropTypes.object,
/**
* Prop to show multiple months on date picker.
*/
monthsShown: PropTypes.number,
/**
* Value for name of custom input.
*/
name: PropTypes.string,
/**
* A callback function to execute when object loses focus.
*/
onBlur: PropTypes.func,
/**
* A callback function to execute when date is selected.
*/
onChange: PropTypes.func.isRequired,
/**
* A callback function to execute when value is selected from dropdown.
*/
onSelect: PropTypes.func,
/**
* A callback function to execute when week is selected.
*/
onWeekSelect: PropTypes.func,
/**
* **Deprecated**, A callback function to execute when the mouse cursor leaves the element. Resolves to `onRequestClose`.
*/
onClickOutside: PropTypes.func,
/**
* A callback function to execute when date is entered.
*/
onChangeRaw: PropTypes.func,
/**
* A callback function to execute when object focused.
*/
onFocus: PropTypes.func,
/**
* A callback function to execute when a key is pressed.
*/
onKeyDown: PropTypes.func,
/**
* A callback function to execute when month is selected.
*/
onMonthChange: PropTypes.func,
/**
* A callback function to execute when picker is dismissed. onRequestClose(event)
*/
onRequestClose: PropTypes.func,
/**
* Prop to open calendar on a particular date.
*/
openToDate: PropTypes.object,
/**
* A prop to check next month dates.
*/
peekNextMonth: PropTypes.bool,
/**
* Prop to prevent date picker from opening on focus.
*/
preventOpenOnFocus: PropTypes.bool,
/**
* Prop to make date picker as read only.
*/
readOnly: PropTypes.bool,
/**
*Prop to make the date picker as a required field.
*/
required: PropTypes.bool,
/**
* Prop to allow scrollable year dropdown.
*/
scrollableYearDropdown: PropTypes.bool,
/**
* Selected Date Value.
*/
selected: PropTypes.object,
/**
* Prop to select end date on a date picker.
*/
selectsEnd: PropTypes.bool,
/**
* Prop to select start date on a date picker.
*/
selectsStart: PropTypes.bool,
/**
* Prop to show month drop down in calendar.
*/
showMonthDropdown: PropTypes.bool,
/**
* Prop to show week numbers .
*/
showWeekNumbers: PropTypes.bool,
/**
* Prop to show a dropdown to select year in date picker calendar .
*/
showYearDropdown: PropTypes.bool,
/**
* Prop to show a month navigation in date picker .
*/
forceShowMonthNavigation: PropTypes.bool,
/**
* Minimum date for a given range .
*/
startDate: PropTypes.object,
/**
* Prop to open calendar dropdown .
*/
startOpen: PropTypes.bool,
/**
* Prop to specify tabbing order of elements .
*/
tabIndex: PropTypes.number,
/**
* Prop to specify title attribute for date picker .
*/
title: PropTypes.string,
/**
* Name of button to select current date .
*/
todayButton: PropTypes.string,
/**
* Prop to show short names for weekdays on calendar .
*/
useWeekdaysShort: PropTypes.bool,
/**
* Value of the date picked by user .
*/
value: PropTypes.string,
/**
* Label value for weeks on date picker.
*/
weekLabel: PropTypes.string,
/**
* Prop to show a separate portal version for date picker.
*/
withPortal: PropTypes.bool,
/**
* Year Values to show on dropdown +/- the given value.
*/
yearDropdownItemNumber: PropTypes.number,
/**
* Prop to close calendar dropdown after date is selected.
*/
shouldCloseOnSelect: PropTypes.bool,
}
static get defaultProps() {
return {
allowSameDay: false,
dateFormat: 'L',
dateFormatCalendar: 'MMMM YYYY',
onChange() { },
disabled: false,
disabledKeyboardNavigation: false,
dropdownMode: 'scroll',
maxDate: newDate(DateUtil.MAX_DATE),
minDate: newDate(DateUtil.MIN_DATE),
onFocus() { },
onBlur() { },
onKeyDown() { },
onSelect() { },
onClickOutside() { },
onMonthChange() { },
preventOpenOnFocus: false,
monthsShown: 1,
withPortal: false,
shouldCloseOnSelect: true,
}
}
constructor(props) {
super(props)
this.state = this.calcInitialState();
this.handleKeydown = this.handleKeydown.bind(this);
this.datePickerContainer = React.createRef();
this.datePickerPopupContainer = React.createRef();
this.datePickerOverlayContainer = React.createRef();
this.handleCalendarKeyDown = this.handleCalendarKeyDown.bind(this);
this.handleOnRequestClose = this.handleOnRequestClose.bind(this);
this.updateAriaLiveStatus = this.updateAriaLiveStatus.bind(this);
this.handleOnDayMouseDown = this.handleOnDayMouseDown.bind(this);
this.boundedPreSelection = this.boundedPreSelection.bind(this);
this.handleMonthBlur = this.handleMonthBlur.bind(this);
}
componentDidMount() {
document.addEventListener('keydown', this.handleKeydown);
}
componentDidUpdate(prevProps) {
const currentMonth = prevProps.selected && getMonth(prevProps.selected)
const nextMonth = this.props.selected && getMonth(this.props.selected)
if (prevProps.inline && currentMonth !== nextMonth) {
this.setPreSelection(this.props.selected)
}
if (prevProps.highlightDates !== this.props.highlightDates) {
this.setState({ 'highlightDates': getHightLightDaysMap(this.props.highlightDates) })
}
// Shift focus into popup date-picker if it exists
if (this.datePickerPopupContainer.current) {
this.datePickerPopupContainer.current.focus();
}
// Shift focus into overlay date-picker if it exists
if (this.datePickerOverlayContainer.current) {
this.datePickerOverlayContainer.current.focus();
}
}
componentWillUnmount() {
document.removeEventListener('keydown', this.handleKeydown);
this.clearPreventFocusTimeout()
}
handleKeydown(event) {
if (event.keyCode === KeyCode.KEY_ESCAPE) {
// If date picker is open in overlay
if (this.datePickerOverlayContainer.current) {
if (event.target === this.datePickerOverlayContainer.current || this.datePickerOverlayContainer.current.contains(event.target)) {
this.setOpen(false, event);
}
}
}
}
handleOnDayMouseDown() {
if (this.props.inline) {
// prevents focus border on pre-selected day on mouseDown when calendar is inline.
this.setState({ preSelection: null })
}
}
boundedPreSelection() {
const defaultPreSelection = this.getPreSelection()
const minDate = getEffectiveMinDate(this.props)
const maxDate = getEffectiveMaxDate(this.props)
return minDate && isBefore(defaultPreSelection, minDate) ? minDate
: maxDate && isAfter(defaultPreSelection, maxDate) ? maxDate
: defaultPreSelection;
}
handleMonthBlur() {
if (this.props.inline) {
// resets previous selected day to selected or current day from previous focused day ( non-selected ) when calendar is inline.
this.setState({ preSelection: this.props.selected ? newDate(this.props.selected) : this.boundedPreSelection() })
}
}
handleOnRequestClose() {
this.setState({ isCalendarKeyboardFocused: false, isCalendarOpenedViaKeyboard: false });
this.setOpen(false, event);
}
getPreSelection = () => (
this.props.openToDate ? newDate(this.props.openToDate)
: this.props.selectsEnd && this.props.startDate ? newDate(this.props.startDate)
: this.props.selectsStart && this.props.endDate ? newDate(this.props.endDate)
: now(this.props.initialTimeZone)
)
calcInitialState = () => {
return {
isCalendarOpenedViaKeyboard: false,
isCalendarKeyboardFocused: false,
open: this.props.startOpen || false,
preventFocus: false,
preSelection: this.props.selected ? newDate(this.props.selected) : this.boundedPreSelection(),
// transforming highlighted days (perhaps nested array)
// to flat Map for faster access in day.jsx
highlightDates: getHightLightDaysMap(this.props.highlightDates)
}
}
clearPreventFocusTimeout = () => {
if (this.preventFocusTimeout) {
clearTimeout(this.preventFocusTimeout)
}
}
setFocus = () => {
if (this.input.focus) {
this.input.focus()
}
}
setOpen = (open) => {
if (!open) {
this.setState({
isCalendarOpenedViaKeyboard: false,
isCalendarKeyboardFocused: false
})
}
this.setState({
open: open,
preSelection: open && this.state.open ? this.state.preSelection : this.calcInitialState().preSelection
})
if (this.props.onRequestClose && (!open)) {
this.props.onRequestClose(event);
}
}
handleFocus = (event) => {
if (!this.state.preventFocus) {
this.props.onFocus(event)
if (!this.props.preventOpenOnFocus) {
this.setOpen(true, event)
}
}
}
cancelFocusInput = () => {
clearTimeout(this.inputFocusTimeout)
this.inputFocusTimeout = null
}
deferFocusInput = () => {
this.cancelFocusInput()
this.inputFocusTimeout = setTimeout(() => this.setFocus(), 1)
}
handleDropdownFocus = () => {
this.cancelFocusInput()
}
handleBlur = (event) => {
if (this.state.open) {
this.deferFocusInput()
} else {
this.props.onBlur(event)
}
}
handleCalendarClickOutside = (event) => {
if (!this.props.inline) {
this.setOpen(false, event)
}
this.props.onClickOutside(event)
if (this.props.withPortal) { event.preventDefault() }
}
handleChange = (event, value) => {
if (this.props.onChangeRaw) {
this.props.onChangeRaw(event, value)
if (event.isDefaultPrevented()) {
return
}
}
this.setState({ inputValue: value })
const date = parseDate(value, this.props)
if (date || !value) {
this.setSelected(date, event, value, true)
}
}
handleSelect = (date, event) => {
// Preventing onFocus event to fix issue
// https://github.com/Hacker0x01/react-datepicker/issues/628
this.setState({ preventFocus: true },
() => {
this.preventFocusTimeout = setTimeout(() => this.setState({ preventFocus: false }), 50)
return this.preventFocusTimeout
}
)
if (this.props.onChangeRaw && !isSameDay(this.props.selected, date) || this.props.allowSameDay) {
const value = DateUtil.formatISODate(date, DateUtil.getFormatByLocale(this.props.intl.locale));
this.props.onChangeRaw(event, value);
}
this.setSelected(date, event)
if (!this.props.shouldCloseOnSelect) {
this.setPreSelection(date)
} else if (!this.props.inline) {
this.setOpen(false, event)
}
}
setSelected = (date, event, value, keepInput) => {
let changedDate = date
if (changedDate !== null && isDayDisabled(changedDate, this.props)) {
return
}
let hasChanged = false;
if (!isSameDay(this.props.selected, changedDate) || this.props.allowSameDay) {
if (changedDate !== null) {
this.setState({
preSelection: changedDate
})
}
hasChanged = true;
}
this.props.onSelect(changedDate, event)
if (hasChanged) {
this.props.onChange(changedDate, event, value)
}
if (!keepInput) {
this.setState({ inputValue: null })
}
}
setPreSelection = (date,type,value) => {
const isValidDateSelection = date ? isDayInRange(date, this.props.minDate, this.props.maxDate) : true
if (isValidDateSelection) {
this.setState({
preSelection: date
})
type === dateValues.MONTH ? this.updateAriaLiveStatus(getMonthFromDate(date, this.props)) :
type === dateValues.YEAR ? this.updateAriaLiveStatus(value) :
this.updateAriaLiveStatus(getLocalizedDateForScreenReader(date, this.props));
}
}
updateAriaLiveStatus(message) {
this.visuallyHiddenText.innerText = message;
}
onInputClick = () => {
if (!this.props.disabled) {
this.setOpen(true)
}
}
onInputKeyDown = (event) => {
if (event.keyCode === KeyCode.KEY_RETURN || event.keyCode === KeyCode.KEY_SPACE) {
this.setState({ isCalendarOpenedViaKeyboard: true })
}
}
handleCalendarKeyDown = (event) => {
const keyboardNavKeys = [
'ArrowLeft',
'ArrowRight',
'ArrowUp',
'ArrowDown',
'PageUp',
'PageDown',
'Home',
'End',
];
this.props.onKeyDown(event)
const eventKey = event.key
const copy = newDate(this.state.preSelection)
if (eventKey === 'Enter') {
event.preventDefault()
if (isMoment(this.state.preSelection) || isDate(this.state.preSelection)) {
this.handleSelect(copy, event)
!this.props.shouldCloseOnSelect && this.setPreSelection(copy)
} else {
this.setOpen(false, event)
}
} else if (eventKey === 'Escape') {
event.preventDefault()
this.setOpen(false, event)
} else if (!this.props.disabledKeyboardNavigation && keyboardNavKeys.indexOf(eventKey) !== -1) {
let newSelection
switch (eventKey) {
case 'ArrowLeft':
this.setState({ isCalendarKeyboardFocused: true })
event.preventDefault()
newSelection = subtractDays(copy, 1)
break
case 'ArrowRight':
this.setState({ isCalendarKeyboardFocused: true })
event.preventDefault()
newSelection = addDays(copy, 1)
break
case 'ArrowUp':
this.setState({ isCalendarKeyboardFocused: true })
event.preventDefault()
newSelection = subtractWeeks(copy, 1)
break
case 'ArrowDown':
this.setState({ isCalendarKeyboardFocused: true })
event.preventDefault()
newSelection = addWeeks(copy, 1)
break
case 'PageUp':
this.setState({ isCalendarKeyboardFocused: true })
event.preventDefault()
newSelection = subtractMonths(copy, 1)
break
case 'PageDown':
this.setState({ isCalendarKeyboardFocused: true })
event.preventDefault()
newSelection = addMonths(copy, 1)
break
case 'Home':
this.setState({ isCalendarKeyboardFocused: true })
event.preventDefault()
newSelection = subtractYears(copy, 1)
break
case 'End':
this.setState({ isCalendarKeyboardFocused: true })
event.preventDefault()
newSelection = addYears(copy, 1)
break
}
if (this.props.adjustDateOnChange) {
this.setSelected(newSelection)
}
this.setPreSelection(newSelection)
}
}
onClearClick = (event) => {
event.preventDefault()
this.props.onChange(null, event)
this.setState({ inputValue: null })
}
renderCalendar = () => {
if (!this.props.inline && (!this.state.open || this.props.disabled)) {
return null
}
if (this.props.withPortal) {
return <WrappedCalendar
ref={(elem) => { this.calendar = elem }}
locale={this.props.locale}
adjustDateOnChange={this.props.adjustDateOnChange}
setOpen={this.setOpen}
dateFormat={this.props.dateFormatCalendar}
useWeekdaysShort={this.props.useWeekdaysShort}
dropdownMode={this.props.dropdownMode}
selected={this.props.selected}
preSelection={this.state.preSelection}
onSelect={this.handleSelect}
onWeekSelect={this.props.onWeekSelect}
openToDate={this.props.openToDate}
minDate={this.props.minDate}
maxDate={this.props.maxDate}
selectsStart={this.props.selectsStart}
selectsEnd={this.props.selectsEnd}
startDate={this.props.startDate}
endDate={this.props.endDate}
excludeDates={this.props.excludeDates}
filterDate={this.props.filterDate}
onClickOutside={this.handleCalendarClickOutside}
formatWeekNumber={this.props.formatWeekNumber}
highlightDates={this.state.highlightDates}
includeDates={this.props.includeDates}
inline={this.props.inline}
peekNextMonth={this.props.peekNextMonth}
showMonthDropdown={this.props.showMonthDropdown}
showWeekNumbers={this.props.showWeekNumbers}
showYearDropdown={this.props.showYearDropdown}
forceShowMonthNavigation={this.props.forceShowMonthNavigation}
scrollableYearDropdown={this.props.scrollableYearDropdown}
todayButton={this.props.todayButton}
weekLabel={this.props.weekLabel}
outsideClickIgnoreClass={outsideClickIgnoreClass}
fixedHeight={this.props.fixedHeight}
monthsShown={this.props.monthsShown}
onDropdownFocus={this.handleDropdownFocus}
onMonthChange={this.props.onMonthChange}
dayClassName={this.props.dayClassName}
className={this.props.calendarClassName}
yearDropdownItemNumber={this.props.yearDropdownItemNumber}
onRequestClose={this.handleOnRequestClose}
handleCalendarKeyDown={this.handleCalendarKeyDown}
setPreSelection={this.setPreSelection}
isCalendarKeyboardFocused={this.state.isCalendarKeyboardFocused}
isCalendarOpenedViaKeyboard={this.state.isCalendarOpenedViaKeyboard}
initialTimeZone={this.props.initialTimeZone}
>
{this.props.children}
<VisuallyHiddenText aria-atomic="true" aria-live="assertive" refCallback={(ref) => { this.visuallyHiddenText = ref; }} />
</WrappedCalendar>
}
return (
<Calendar
ref={(elem) => { this.calendar = elem }}
locale={this.props.locale}
adjustDateOnChange={this.props.adjustDateOnChange}
setOpen={this.setOpen}
dateFormat={this.props.dateFormatCalendar}
useWeekdaysShort={this.props.useWeekdaysShort}
dropdownMode={this.props.dropdownMode}
selected={this.props.selected}
preSelection={this.state.preSelection}
onSelect={this.handleSelect}
onWeekSelect={this.props.onWeekSelect}
onDayMouseDown={this.handleOnDayMouseDown}
onMonthBlur={this.handleMonthBlur}
openToDate={this.props.openToDate}
minDate={this.props.minDate}
maxDate={this.props.maxDate}
selectsStart={this.props.selectsStart}
selectsEnd={this.props.selectsEnd}
startDate={this.props.startDate}
endDate={this.props.endDate}
excludeDates={this.props.excludeDates}
filterDate={this.props.filterDate}
formatWeekNumber={this.props.formatWeekNumber}
highlightDates={this.state.highlightDates}
includeDates={this.props.includeDates}
inline={this.props.inline}
peekNextMonth={this.props.peekNextMonth}
showMonthDropdown={this.props.showMonthDropdown}
showWeekNumbers={this.props.showWeekNumbers}
showYearDropdown={this.props.showYearDropdown}
forceShowMonthNavigation={this.props.forceShowMonthNavigation}
scrollableYearDropdown={this.props.scrollableYearDropdown}
todayButton={this.props.todayButton}
weekLabel={this.props.weekLabel}
fixedHeight={this.props.fixedHeight}
monthsShown={this.props.monthsShown}
onDropdownFocus={this.handleDropdownFocus}
onMonthChange={this.props.onMonthChange}
dayClassName={this.props.dayClassName}
className={this.props.calendarClassName}
yearDropdownItemNumber={this.props.yearDropdownItemNumber}
onRequestClose={this.handleOnRequestClose}
handleCalendarKeyDown={this.handleCalendarKeyDown}
setPreSelection={this.setPreSelection}
isCalendarKeyboardFocused={this.state.isCalendarKeyboardFocused}
isCalendarOpenedViaKeyboard={this.state.isCalendarOpenedViaKeyboard}
initialTimeZone={this.props.initialTimeZone}
>
{this.props.children}
<VisuallyHiddenText aria-atomic="true" aria-live="assertive" refCallback={(ref) => { this.visuallyHiddenText = ref; }} />
</Calendar>
);
}
renderDateInput = () => {
const classNameList = cx(this.props.className, {
[outsideClickIgnoreClass]: this.state.open
})
const customInput = this.props.customInput || <input type="text" />
const customInputRef = this.props.customInputRef || 'ref'
const inputValue =
typeof this.props.value === 'string' ? this.props.value
: typeof this.state.inputValue === 'string' ? this.state.inputValue
: safeDateFormat(this.props.selected, this.props)
return React.cloneElement(customInput, {
[customInputRef]: (input) => { this.input = input },
value: inputValue,
onBlur: this.handleBlur,
onChange: this.handleChange,
onClick: this.onInputClick,
onFocus: this.handleFocus,
onKeyDown: this.onInputKeyDown,
id: this.props.id,
name: this.props.name,
autoFocus: this.props.autoFocus,
disabled: this.props.disabled,
autoComplete: this.props.autoComplete,
className: classNameList,
title: this.props.title,
readOnly: this.props.readOnly,
required: this.props.required,
tabIndex: this.props.tabIndex
})
}
renderClearButton = () => {
if (this.props.isClearable && this.props.selected != null) {
return <a className={cx('react-datepicker-close-icon')} href="#" onClick={this.onClearClick} />
} else {
return null
}
}
render() {
const calendar = this.renderCalendar()
if (this.props.inline && !this.props.withPortal) {
return calendar
}
if (this.props.withPortal) {
return (
<div>
{
!this.props.inline ? (
<div className={cx('react-datepicker-input-container')}>
{this.renderDateInput()}
{this.renderClearButton()}
</div>
) : null
}
{
(this.state.open && !this.props.disabled) || this.props.inline
? (<Portal isOpened={true}>
<FocusTrap focusTrapOptions={{ returnFocusOnDeactivate: false, clickOutsideDeactivates: true }}>
<div
ref={this.datePickerOverlayContainer}
className={cx('react-datepicker-portal')}
>
{calendar}
</div>
</FocusTrap>
</Portal>)
: null
}
</div>
)
}
return (
<React.Fragment>
<div
ref={this.datePickerContainer}
className={cx('react-datepicker-input-container')}
>
{this.renderDateInput()}
{this.renderClearButton()}
</div>
{calendar && <Popup
attachmentBehavior="flip"
contentAttachment="top center"
isOpen={(this.state.open && !this.props.disabled)}
targetAttachment="bottom center"
targetRef={() => this.datePickerContainer.current}
onPosition={this.handleOnPosition}
onRequestClose={this.handleOnRequestClose}
classNameArrow={cx('react-datepicker-arrow')}
contentWidth="auto"
contentHeight="auto"
isArrowDisplayed
isHeaderDisabled
isContentFocusDisabled
popupContentRole={null}
>
<PopupContainer
ref={this.datePickerPopupContainer}
>
{calendar}
</PopupContainer>
</Popup>}
</React.Fragment>
)
}
}
export default injectIntl(DatePicker);