UNPKG

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
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);