react-key-handler
Version:
React component to handle keyboard events
410 lines (335 loc) • 11.2 kB
JavaScript
(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 });
})));