UNPKG

react-dates

Version:

A responsive and accessible date range picker component built with React

323 lines (281 loc) 8.56 kB
import React from 'react'; import PropTypes from 'prop-types'; import { forbidExtraProps } from 'airbnb-prop-types'; import { css, withStyles, withStylesPropTypes } from 'react-with-styles'; import KeyboardShortcutRow from './KeyboardShortcutRow'; import { DayPickerKeyboardShortcutsPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; import CloseButton from './CloseButton'; export const TOP_LEFT = 'top-left'; export const TOP_RIGHT = 'top-right'; export const BOTTOM_RIGHT = 'bottom-right'; const propTypes = forbidExtraProps({ ...withStylesPropTypes, block: PropTypes.bool, buttonLocation: PropTypes.oneOf([TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT]), showKeyboardShortcutsPanel: PropTypes.bool, openKeyboardShortcutsPanel: PropTypes.func, closeKeyboardShortcutsPanel: PropTypes.func, phrases: PropTypes.shape(getPhrasePropTypes(DayPickerKeyboardShortcutsPhrases)), }); const defaultProps = { block: false, buttonLocation: BOTTOM_RIGHT, showKeyboardShortcutsPanel: false, openKeyboardShortcutsPanel() {}, closeKeyboardShortcutsPanel() {}, phrases: DayPickerKeyboardShortcutsPhrases, }; class DayPickerKeyboardShortcuts extends React.Component { constructor(...args) { super(...args); this.onClick = this.onClick.bind(this); this.setShowKeyboardShortcutsButtonRef = this.setShowKeyboardShortcutsButtonRef.bind(this); } onClick() { const { openKeyboardShortcutsPanel } = this.props; // we want to return focus to this button after closing the keyboard shortcuts panel openKeyboardShortcutsPanel(() => { this.showKeyboardShortcutsButton.focus(); }); } setShowKeyboardShortcutsButtonRef(ref) { this.showKeyboardShortcutsButton = ref; } render() { const { block, buttonLocation, showKeyboardShortcutsPanel, closeKeyboardShortcutsPanel, styles, phrases, } = this.props; const keyboardShortcuts = [{ unicode: '↵', label: phrases.enterKey, action: phrases.selectFocusedDate, }, { unicode: '←/→', label: phrases.leftArrowRightArrow, action: phrases.moveFocusByOneDay, }, { unicode: '↑/↓', label: phrases.upArrowDownArrow, action: phrases.moveFocusByOneWeek, }, { unicode: 'PgUp/PgDn', label: phrases.pageUpPageDown, action: phrases.moveFocusByOneMonth, }, { unicode: 'Home/End', label: phrases.homeEnd, action: phrases.moveFocustoStartAndEndOfWeek, }, { unicode: 'Esc', label: phrases.escape, action: phrases.returnFocusToInput, }, { unicode: '?', label: phrases.questionMark, action: phrases.openThisPanel, }, ]; const toggleButtonText = showKeyboardShortcutsPanel ? phrases.hideKeyboardShortcutsPanel : phrases.showKeyboardShortcutsPanel; const bottomRight = buttonLocation === BOTTOM_RIGHT; const topRight = buttonLocation === TOP_RIGHT; const topLeft = buttonLocation === TOP_LEFT; return ( <div> <button ref={this.setShowKeyboardShortcutsButtonRef} {...css( styles.DayPickerKeyboardShortcuts_buttonReset, styles.DayPickerKeyboardShortcuts_show, bottomRight && styles.DayPickerKeyboardShortcuts_show__bottomRight, topRight && styles.DayPickerKeyboardShortcuts_show__topRight, topLeft && styles.DayPickerKeyboardShortcuts_show__topLeft, )} type="button" aria-label={toggleButtonText} onClick={this.onClick} onMouseUp={(e) => { e.currentTarget.blur(); }} > <span {...css( styles.DayPickerKeyboardShortcuts_showSpan, bottomRight && styles.DayPickerKeyboardShortcuts_showSpan__bottomRight, topRight && styles.DayPickerKeyboardShortcuts_showSpan__topRight, topLeft && styles.DayPickerKeyboardShortcuts_showSpan__topLeft, )} > ? </span> </button> {showKeyboardShortcutsPanel && <div {...css( styles.DayPickerKeyboardShortcuts_panel, block && styles.DayPickerKeyboardShortcuts_panel__block, )} role="dialog" aria-labelledby="DayPickerKeyboardShortcuts__title" > <div {...css(styles.DayPickerKeyboardShortcuts_title)} id="DayPickerKeyboardShortcuts__title" > {phrases.keyboardShortcuts} </div> <button {...css( styles.DayPickerKeyboardShortcuts_buttonReset, styles.DayPickerKeyboardShortcuts_close, )} type="button" aria-label={phrases.hideKeyboardShortcutsPanel} onClick={closeKeyboardShortcutsPanel} onKeyDown={(e) => { // Because the close button is the only focusable element inside of the panel, this // amount to a very basic focus trap. The user can exit the panel by "pressing" the // close button or hitting escape if (e.key === 'Tab') { e.preventDefault(); } }} > <CloseButton {...css(styles.DayPickerKeyboardShortcuts_closeSvg)} /> </button> <ul {...css(styles.DayPickerKeyboardShortcuts__list)}> {keyboardShortcuts.map(({ unicode, label, action }) => ( <KeyboardShortcutRow key={label} unicode={unicode} label={label} action={action} block={block} /> ))} </ul> </div> } </div> ); } } DayPickerKeyboardShortcuts.propTypes = propTypes; DayPickerKeyboardShortcuts.defaultProps = defaultProps; export default withStyles(({ reactDates: { color, zIndex } }) => ({ DayPickerKeyboardShortcuts_buttonReset: { background: 'none', border: 0, color: 'inherit', font: 'inherit', lineHeight: 'normal', overflow: 'visible', padding: 0, cursor: 'pointer', ':active': { outline: 'none', }, }, DayPickerKeyboardShortcuts_show: { width: 22, position: 'absolute', zIndex: zIndex + 2, }, DayPickerKeyboardShortcuts_show__bottomRight: { borderTop: '26px solid transparent', borderRight: `33px solid ${color.core.primary}`, bottom: 0, right: 0, ':hover': { borderRight: `33px solid ${color.core.primary_dark}`, }, }, DayPickerKeyboardShortcuts_show__topRight: { borderBottom: '26px solid transparent', borderRight: `33px solid ${color.core.primary}`, top: 0, right: 0, ':hover': { borderRight: `33px solid ${color.core.primary_dark}`, }, }, DayPickerKeyboardShortcuts_show__topLeft: { borderBottom: '26px solid transparent', borderLeft: `33px solid ${color.core.primary}`, top: 0, left: 0, ':hover': { borderLeft: `33px solid ${color.core.primary_dark}`, }, }, DayPickerKeyboardShortcuts_showSpan: { color: color.core.white, position: 'absolute', }, DayPickerKeyboardShortcuts_showSpan__bottomRight: { bottom: 0, right: -28, }, DayPickerKeyboardShortcuts_showSpan__topRight: { top: 1, right: -28, }, DayPickerKeyboardShortcuts_showSpan__topLeft: { top: 1, left: -28, }, DayPickerKeyboardShortcuts_panel: { overflow: 'auto', background: color.background, border: `1px solid ${color.core.border}`, borderRadius: 2, position: 'absolute', top: 0, bottom: 0, right: 0, left: 0, zIndex: zIndex + 2, padding: 22, margin: 33, }, DayPickerKeyboardShortcuts_title: { fontSize: 16, fontWeight: 'bold', margin: 0, }, DayPickerKeyboardShortcuts_list: { listStyle: 'none', padding: 0, }, DayPickerKeyboardShortcuts_close: { position: 'absolute', right: 22, top: 22, zIndex: zIndex + 2, ':active': { outline: 'none', }, }, DayPickerKeyboardShortcuts_closeSvg: { height: 15, width: 15, fill: color.core.grayLighter, ':hover': { fill: color.core.grayLight, }, ':focus': { fill: color.core.grayLight, }, }, }))(DayPickerKeyboardShortcuts);