react-dates
Version:
A responsive and accessible date range picker component built with React
179 lines (162 loc) • 5.18 kB
JSX
import React from 'react';
import PropTypes from 'prop-types';
import { forbidExtraProps } from 'airbnb-prop-types';
import cx from 'classnames';
import { DayPickerKeyboardShortcutsPhrases } from '../defaultPhrases';
import getPhrasePropTypes from '../utils/getPhrasePropTypes';
import CloseButton from '../svg/close.svg';
export const TOP_LEFT = 'top-left';
export const TOP_RIGHT = 'top-right';
export const BOTTOM_RIGHT = 'bottom-right';
const propTypes = forbidExtraProps({
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,
};
export function KeyboardShortcutRow({ unicode, label, action }) {
return (
<li className="KeyboardShortcutRow">
<div
className="KeyboardShortcutRow__key-container"
>
<span
className="KeyboardShortcutRow__key"
role="img"
aria-label={label}
>
{unicode}
</span>
</div>
<div className="KeyboardShortcutRow__action">
{action}
</div>
</li>
);
}
KeyboardShortcutRow.propTypes = {
unicode: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
action: PropTypes.string.isRequired,
};
export default function DayPickerKeyboardShortcuts({
block,
buttonLocation,
showKeyboardShortcutsPanel,
openKeyboardShortcutsPanel,
closeKeyboardShortcutsPanel,
phrases,
}) {
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;
return (
<div>
<button
ref={(ref) => { this.showKeyboardShortcutsButton = ref; }}
className={cx('DayPickerKeyboardShortcuts__show', {
'DayPickerKeyboardShortcuts__show--bottom-right': buttonLocation === BOTTOM_RIGHT,
'DayPickerKeyboardShortcuts__show--top-right': buttonLocation === TOP_RIGHT,
'DayPickerKeyboardShortcuts__show--top-left': buttonLocation === TOP_LEFT,
})}
type="button"
aria-label={toggleButtonText}
onClick={() => {
// we want to return focus to this button after closing the keyboard shortcuts panel
openKeyboardShortcutsPanel(() => { this.showKeyboardShortcutsButton.focus(); });
}}
onMouseUp={(e) => {
e.currentTarget.blur();
}}
>
<span className="DayPickerKeyboardShortcuts__show_span">?</span>
</button>
{showKeyboardShortcutsPanel &&
<div
className={cx('DayPickerKeyboardShortcuts__panel', {
'DayPickerKeyboardShortcuts__panel--block': block,
})}
role="dialog"
aria-labelledby="DayPickerKeyboardShortcuts__title"
>
<div
id="DayPickerKeyboardShortcuts__title"
className="DayPickerKeyboardShortcuts__title"
>
{phrases.keyboardShortcuts}
</div>
<button
className="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 />
</button>
<ul className="DayPickerKeyboardShortcuts__list">
{keyboardShortcuts.map(({ unicode, label, action }) => (
<KeyboardShortcutRow key={label} unicode={unicode} label={label} action={action} />
))}
</ul>
</div>
}
</div>
);
}
DayPickerKeyboardShortcuts.propTypes = propTypes;
DayPickerKeyboardShortcuts.defaultProps = defaultProps;