mirror of
https://github.com/Radarr/Radarr
synced 2025-12-06 08:28:50 +01:00
179 lines
3.7 KiB
TypeScript
179 lines
3.7 KiB
TypeScript
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<string, Shortcut> = {
|
|
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<KeyboardShortcutsProps>
|
|
) {
|
|
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<string, BindingOptions>;
|
|
|
|
//
|
|
// 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 (
|
|
<WrappedComponent
|
|
{...this.props}
|
|
bindShortcut={this.bindShortcut}
|
|
unbindShortcut={this.unbindShortcut}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
return KeyboardShortcuts;
|
|
}
|
|
|
|
export default keyboardShortcuts;
|