UNPKG

react-key-handler

Version:

React component to handle keyboard events

410 lines (335 loc) 11.2 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('prop-types'), require('exenv')) : typeof define === 'function' && define.amd ? define(['exports', 'react', 'prop-types', 'exenv'], factory) : (factory((global.ReactKeyHandler = {}),global.React,global.PropTypes,global.exenv)); }(this, (function (exports,React,PropTypes,exenv) { 'use strict'; React = React && React.hasOwnProperty('default') ? React['default'] : React; PropTypes = PropTypes && PropTypes.hasOwnProperty('default') ? PropTypes['default'] : PropTypes; /** * Key event names. */ var KEYDOWN = 'keydown'; var KEYPRESS = 'keypress'; var KEYUP = 'keyup'; var NORMALIZED_KEYS = { Esc: 'Escape', Spacebar: ' ', Left: 'ArrowLeft', Up: 'ArrowUp', Right: 'ArrowRight', Down: 'ArrowDown', Del: 'Delete', Win: 'OS', Menu: 'ContextMenu', Apps: 'ContextMenu', Scroll: 'ScrollLock', MozPrintableKey: 'Unidentified' }; var KEY_CODE_KEYS = { '8': 'Backspace', '9': 'Tab', '12': 'Clear', '13': 'Enter', '16': 'Shift', '17': 'Control', '18': 'Alt', '19': 'Pause', '20': 'CapsLock', '27': 'Escape', '32': ' ', '33': 'PageUp', '34': 'PageDown', '35': 'End', '36': 'Home', '37': 'ArrowLeft', '38': 'ArrowUp', '39': 'ArrowRight', '40': 'ArrowDown', '45': 'Insert', '46': 'Delete', '112': 'F1', '113': 'F2', '114': 'F3', '115': 'F4', '116': 'F5', '117': 'F6', '118': 'F7', '119': 'F8', '120': 'F9', '121': 'F10', '122': 'F11', '123': 'F12', '144': 'NumLock', '145': 'ScrollLock', '224': 'Meta' }; /** * Check if `given` element is an input / textarea form element or acts as one. */ function isInput(element) { if (!element) { return false; } var tagName = element.tagName; var editable = isContentEditable(element); return tagName === 'INPUT' || tagName === 'TEXTAREA' || editable; } function isContentEditable(element) { if (typeof element.getAttribute !== 'function') { return false; } return !!element.getAttribute('contenteditable'); } /** * Matches an event against a given keyboard key. */ function matchesElementOrArray(a, b) { if (Array.isArray(a)) { return a.includes(b); } return a === b; } function matchesKeyboardEvent(event, _ref) { var keyCode = _ref.keyCode, keyValue = _ref.keyValue, code = _ref.code; if (!isNullOrUndefined(keyValue)) { var value = eventKey(event); if (matchesElementOrArray(keyValue, value)) { return true; } } if (!isNullOrUndefined(code)) { if (matchesElementOrArray(code, event.code)) { return true; } } if (!isNullOrUndefined(keyCode)) { // Firefox handles keyCode through which var keyCodeTarget = event.keyCode || event.which; if (matchesElementOrArray(keyCode, keyCodeTarget)) { return true; } } return false; } function isNullOrUndefined(value) { return value === undefined || value === null; } function eventKey(event) { var key = event.key, keyCode = event.keyCode, type = event.type; if (key) { var normalizedKey = NORMALIZED_KEYS[key] || key; if (normalizedKey !== 'Unidentified') { return normalizedKey; } } if (type === KEYPRESS) { var charCode = eventCharCode(event); return charCode === 13 ? 'Enter' : String.fromCharCode(charCode); } if (type === KEYDOWN || type === KEYUP) { return KEY_CODE_KEYS[String(keyCode)] || 'Unidentified'; } return ''; } function eventCharCode(event) { var charCode = event.charCode, keyCode = event.keyCode; if ('charCode' in event) { if (charCode === 0 && keyCode === 13) { return 13; } } else { charCode = keyCode; } if (charCode >= 32 || charCode === 13) { return charCode; } return 0; } var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }; var possibleConstructorReturn = function (self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }; var KeyHandler = function (_React$Component) { inherits(KeyHandler, _React$Component); createClass(KeyHandler, [{ key: 'shouldComponentUpdate', value: function shouldComponentUpdate() { return false; } }]); function KeyHandler(props) { classCallCheck(this, KeyHandler); /* eslint-disable no-console */ var _this = possibleConstructorReturn(this, (KeyHandler.__proto__ || Object.getPrototypeOf(KeyHandler)).call(this, props)); _this.handleKey = function (event) { var _this$props = _this.props, keyValue = _this$props.keyValue, keyCode = _this$props.keyCode, code = _this$props.code, onKeyHandle = _this$props.onKeyHandle; if (!onKeyHandle) { return; } var target = event.target; if (target instanceof window.HTMLElement && isInput(target)) { return; } if (!matchesKeyboardEvent(event, { keyValue: keyValue, keyCode: keyCode, code: code })) { return; } onKeyHandle(event); }; if (props.keyCode) { console.warn('Warning: Deprecated propType: `keyCode` is deprecated in favour of `code` for `KeyHandler`.'); } if (!props.keyValue && !props.keyCode && !props.code) { console.error('Warning: Failed propType: Missing prop `code`, `keyValue` or `keyCode` for `KeyHandler`.'); } /* eslint-enable */ return _this; } createClass(KeyHandler, [{ key: 'componentDidMount', value: function componentDidMount() { if (!exenv.canUseDOM) return; window.document.addEventListener(this.props.keyEventName, this.handleKey); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { if (!exenv.canUseDOM) return; window.document.removeEventListener(this.props.keyEventName, this.handleKey); } }, { key: 'render', value: function render() { return null; } }]); return KeyHandler; }(React.Component); KeyHandler.propTypes = { keyValue: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]), keyCode: PropTypes.oneOfType([PropTypes.number, PropTypes.arrayOf(PropTypes.number)]), code: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]), keyEventName: PropTypes.oneOf([KEYDOWN, KEYPRESS, KEYUP]), onKeyHandle: PropTypes.func }; KeyHandler.defaultProps = { keyEventName: KEYUP }; function keyHandleDecorator(matcher) { return function (props) { var _ref = props || {}, keyValue = _ref.keyValue, keyCode = _ref.keyCode, code = _ref.code, keyEventName = _ref.keyEventName; return function (Component) { return function (_React$Component) { inherits(KeyHandleDecorator, _React$Component); function KeyHandleDecorator() { var _ref2; var _temp, _this, _ret; classCallCheck(this, KeyHandleDecorator); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref2 = KeyHandleDecorator.__proto__ || Object.getPrototypeOf(KeyHandleDecorator)).call.apply(_ref2, [this].concat(args))), _this), _this.state = { keyCode: null, keyValue: null, code: null }, _this.handleKey = function (event) { if (matcher && matcher(event, _this.state)) { _this.setState({ keyValue: null, keyCode: null }); return; } _this.setState({ keyValue: eventKey(event), keyCode: event.keyCode }); }, _temp), possibleConstructorReturn(_this, _ret); } createClass(KeyHandleDecorator, [{ key: 'render', value: function render() { return React.createElement( React.Fragment, null, React.createElement(KeyHandler, { keyValue: keyValue, keyCode: keyCode, code: code, keyEventName: keyEventName, onKeyHandle: this.handleKey }), React.createElement(Component, _extends({}, this.props, this.state)) ); } }]); return KeyHandleDecorator; }(React.Component); }; }; } var keyHandler = keyHandleDecorator(); var keyToggleHandler = keyHandleDecorator(matchesKeyboardEvent); exports.default = KeyHandler; exports.KEYDOWN = KEYDOWN; exports.KEYPRESS = KEYPRESS; exports.KEYUP = KEYUP; exports.keyHandleDecorator = keyHandleDecorator; exports.keyToggleHandler = keyToggleHandler; exports.keyHandler = keyHandler; Object.defineProperty(exports, '__esModule', { value: true }); })));