import { keys } from 'ramda'; import * as React from 'react'; import { Component, ComponentClass, KeyboardEvent, SFC } from 'react'; import * as KEY_MATCHERS from '../../utils/keyMatchers'; export interface WithKeysProps { handlers: IHandlers; matchers?: IMatchers; useKeys?: boolean; } export type WithKeysHOC = ( decorated: ComponentClass | SFC, ) => ComponentClass>; export interface IHandlers { [key: string]: (event: KeyboardEvent, props: T) => void | string; } export interface IMatchers { [key: string]: (event: KeyboardEvent) => boolean; } export default function withKeys(): WithKeysHOC { return function(DecoratedComponent) { return class WithKeysComponent extends Component, {}> { componentDidMount() { if (this.props.useKeys) { document.addEventListener('keydown', this.handleKeyDown); } } componentDidUpdate() { if (this.props.useKeys) { document.addEventListener('keydown', this.handleKeyDown); } else { document.removeEventListener('keydown', this.handleKeyDown); } } componentWillUnmount() { if (this.props.useKeys) { document.removeEventListener('keydown', this.handleKeyDown); } } getMatcher = (bindingKey: string) => { const matchers = this.props.matchers ? { ...KEY_MATCHERS, ...(this.props.matchers as {}) } : KEY_MATCHERS; return matchers[bindingKey]; }; handleKeyDown = event => { keys(this.props.handlers).forEach(bindingKey => { const binding = this.getMatcher(bindingKey); const handler = this.props.handlers[bindingKey]; if (binding && binding(event) && handler) { handler.call(this, event, this.props); } }); }; render() { const { useKeys, ...other } = this.props as any; return ; } }; }; }