terra-date-picker
Version:
The terra-date-picker component provides users a way to enter or select a date from the date picker.
271 lines (238 loc) • 8.09 kB
JSX
import React from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames/bind'
import { injectIntl } from 'react-intl';
import VisuallyHiddenText from 'terra-visually-hidden-text';
import {
getDay,
getMonth,
getDate,
now,
isSameDay,
isDayDisabled,
isDayInRange,
getDayOfWeekCode,
getLocalizedDateForScreenReader
} from './date_utils'
import styles from './stylesheets/react_datepicker.module.scss'
const cx = classNames.bind(styles);
class Day extends React.Component {
static propTypes = {
/**
* Day Value Entered.
*/
day: PropTypes.object.isRequired,
/**
* A callback function for custom day class names.
*/
dayClassName: PropTypes.func,
/**
* Maximum Date value for a date range.
*/
endDate: PropTypes.object,
/**
* Highlight range of dates with custom classes.
*/
highlightDates: PropTypes.instanceOf(WeakMap),
/**
* 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 date picker component.
*/
inline: PropTypes.bool,
/**
* @private
* Internationalization object with translation APIs. Provided by `injectIntl`.
*/
intl: PropTypes.shape({ formatMessage: PropTypes.func }),
/**
* Month value for the date entered.
*/
month: PropTypes.number,
/**
* A callback function to execute when a valid date is selected.
*/
onClick: PropTypes.func,
/**
* A callback function to execute when mouse is clicked to select date.
*/
onMouseEnter: PropTypes.func,
/**
* A callback function to execute on mouse down on day.
* requires no parameter.
*/
onDayMouseDown: PropTypes.func,
/**
* Previous Date Value selected .
*/
preSelection: PropTypes.object,
/**
* Selected Date Value.
*/
selected: PropTypes.object,
/**
* Date selected to check if in range.
*/
selectingDate: 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,
/**
* Minimum date for a given range .
*/
startDate: PropTypes.object,
/**
* Whether or not calendar is opened via keyboard
*/
isCalendarKeyboardFocused: PropTypes.bool,
}
handleClick = (event) => {
if (!this.isDisabled() && this.props.onClick) {
this.props.onClick(event)
}
}
handleMouseEnter = (event) => {
// day should not be get focus border on mouse enter when calendar is inline.
if (!this.isDisabled() && this.props.onMouseEnter && !this.props.inline) {
this.props.onMouseEnter(event)
}
}
handleMouseDown = () => {
if (this.props.onDayMouseDown) {
this.props.onDayMouseDown();
}
}
isSameDay = (other) => isSameDay(this.props.day, other)
isKeyboardSelected = () =>
!this.props.inline && !this.isSameDay(this.props.selected) && this.isSameDay(this.props.preSelection)
isDisabled = () => isDayDisabled(this.props.day, this.props)
getHighLightedClass = (defaultClassName) => {
const { day, highlightDates } = this.props
if (!highlightDates) {
return false
}
// Looking for className in the Map of {'day string, 'className'}
const dayStr = day.format('MM.DD.YYYY')
return highlightDates.get(dayStr)
}
isInRange = () => {
const { day, startDate, endDate } = this.props
if (!startDate || !endDate) {
return false
}
return isDayInRange(day, startDate, endDate)
}
isInSelectingRange = () => {
const { day, selectsStart, selectsEnd, selectingDate, startDate, endDate } = this.props
if (!(selectsStart || selectsEnd) || !selectingDate || this.isDisabled()) {
return false
}
if (selectsStart && endDate && selectingDate.isSameOrBefore(endDate)) {
return isDayInRange(day, selectingDate, endDate)
}
if (selectsEnd && startDate && selectingDate.isSameOrAfter(startDate)) {
return isDayInRange(day, startDate, selectingDate)
}
return false
}
isSelectingRangeStart = () => {
if (!this.isInSelectingRange()) {
return false
}
const { day, selectingDate, startDate, selectsStart } = this.props
if (selectsStart) {
return isSameDay(day, selectingDate)
} else {
return isSameDay(day, startDate)
}
}
isSelectingRangeEnd = () => {
if (!this.isInSelectingRange()) {
return false
}
const { day, selectingDate, endDate, selectsEnd } = this.props
if (selectsEnd) {
return isSameDay(day, selectingDate)
} else {
return isSameDay(day, endDate)
}
}
isRangeStart = () => {
const { day, startDate, endDate } = this.props
if (!startDate || !endDate) {
return false
}
return isSameDay(startDate, day)
}
isRangeEnd = () => {
const { day, startDate, endDate } = this.props
if (!startDate || !endDate) {
return false
}
return isSameDay(endDate, day)
}
isWeekend = () => {
const weekday = getDay(this.props.day)
return weekday === 0 || weekday === 6
}
isOutsideMonth = () => {
return this.props.month !== undefined &&
this.props.month !== getMonth(this.props.day)
}
isBorderApplied = () => {
if(this.isSameDay(this.props.preSelection) && !isDayDisabled(this.props.selected, this.props)
&& (document.activeElement)
&& (document.activeElement.hasAttribute('data-terra-open-calendar-button')
|| document.activeElement.tagName === 'DIV'
|| document.activeElement === document.querySelector('[class*="previous"]'))
&& this.props.isCalendarKeyboardFocused) {
return true;
}
return false;
}
getClassNames = (date) => {
const dayClassName = (this.props.dayClassName ? this.props.dayClassName(date) : undefined)
return ['react-datepicker-day', dayClassName, 'react-datepicker-day--' + getDayOfWeekCode(this.props.day), {
'react-datepicker-day--disabled': this.isDisabled(),
'react-datepicker-day--selected': this.isSameDay(this.props.selected) && !isDayDisabled(this.props.selected, this.props),
'react-datepicker-day--selected-border': this.isBorderApplied(),
'react-datepicker-day--range-start': this.isRangeStart(),
'react-datepicker-day--range-end': this.isRangeEnd(),
'react-datepicker-day--in-range': this.isInRange(),
'react-datepicker-day--in-selecting-range': this.isInSelectingRange(),
'react-datepicker-day--selecting-range-start': this.isSelectingRangeStart(),
'react-datepicker-day--selecting-range-end': this.isSelectingRangeEnd(),
'react-datepicker-day--today': this.isSameDay(now(this.props.initialTimeZone)),
'react-datepicker-day--weekend': this.isWeekend(),
'react-datepicker-day--outside-month': this.isOutsideMonth(),
'is-calendar-focused--keyboard-focus': this.props.isCalendarKeyboardFocused && this.isKeyboardSelected() && document.activeElement.tagName === 'DIV',
}, this.getHighLightedClass('react-datepicker-day--highlighted')]
}
render() {
const { day } = this.props;
return (
<div
className={cx(this.getClassNames(day))}
onClick={this.handleClick}
onMouseEnter={this.handleMouseEnter}
onMouseDown={this.handleMouseDown}
aria-disabled={this.isDisabled()}
>
<React.Fragment>
{/* Adding visually hidden text so screen readers like VoiceOver on iOS read the date properly */}
<VisuallyHiddenText text={getLocalizedDateForScreenReader(day, this.props)} />
<span aria-hidden="true">{getDate(day)}</span>
</React.Fragment>
</div>
)
}
}
export default injectIntl(Day);