import Mousetrap, { MousetrapInstance } from 'mousetrap'; import React, { Component, ComponentType } from 'react'; import translate from 'Utilities/String/translate'; export interface Shortcut { key: string; name: string; } interface BindingOptions { isGlobal?: boolean; } interface KeyboardShortcutsProps { bindShortcut: ( key: string, callback: (e: Mousetrap.ExtendedKeyboardEvent, combo: string) => void, options?: BindingOptions ) => void; unbindShortcut: (key: string) => void; } export const shortcuts: Record = { OPEN_KEYBOARD_SHORTCUTS_MODAL: { key: '?', get name() { return translate('KeyboardShortcutsOpenModal'); }, }, CLOSE_MODAL: { key: 'Esc', get name() { return translate('KeyboardShortcutsCloseModal'); }, }, ACCEPT_CONFIRM_MODAL: { key: 'Enter', get name() { return translate('KeyboardShortcutsConfirmModal'); }, }, MOVIE_SEARCH_INPUT: { key: 's', get name() { return translate('KeyboardShortcutsFocusSearchBox'); }, }, SAVE_SETTINGS: { key: 'mod+s', get name() { return translate('KeyboardShortcutsSaveSettings'); }, }, SCROLL_TOP: { key: 'mod+home', get name() { return translate('KeyboardShortcutsMovieIndexScrollTop'); }, }, SCROLL_BOTTOM: { key: 'mod+end', get name() { return translate('KeyboardShortcutsMovieIndexScrollBottom'); }, }, DETAILS_NEXT: { key: '→', get name() { return translate('KeyboardShortcutsMovieDetailsNextMovie'); }, }, DETAILS_PREVIOUS: { key: '←', get name() { return translate('KeyboardShortcutsMovieDetailsPreviousMovie'); }, }, }; function keyboardShortcuts( WrappedComponent: ComponentType ) { class KeyboardShortcuts extends Component { // // Lifecycle constructor(props: never) { super(props); this._mousetrapBindings = {}; this._mousetrap = new Mousetrap(); this._mousetrap.stopCallback = this.stopCallback; } componentWillUnmount() { this.unbindAllShortcuts(); this._mousetrap = null; } _mousetrap: MousetrapInstance | null; _mousetrapBindings: Record; // // Control bindShortcut = ( key: string, callback: (e: Mousetrap.ExtendedKeyboardEvent, combo: string) => void, options: BindingOptions = {} ) => { this._mousetrap?.bind(key, callback); this._mousetrapBindings[key] = options; }; unbindShortcut = (key: string) => { if (this._mousetrap != null) { delete this._mousetrapBindings[key]; this._mousetrap.unbind(key); } }; unbindAllShortcuts = () => { const keys = Object.keys(this._mousetrapBindings); if (!keys.length) { return; } keys.forEach((binding) => { this._mousetrap?.unbind(binding); }); this._mousetrapBindings = {}; }; stopCallback = ( _e: Mousetrap.ExtendedKeyboardEvent, element: Element, combo: string ) => { const binding = this._mousetrapBindings[combo]; if (!binding || binding.isGlobal) { return false; } return ( element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA' || ('contentEditable' in element && element.contentEditable === 'true') ); }; // // Render render() { return ( ); } } return KeyboardShortcuts; } export default keyboardShortcuts;