statebuddy/src/hooks/useShortcuts.ts

30 lines
1.4 KiB
TypeScript

import { useEffect } from "react";
export function useShortcuts(spec: {keys: string[], action: () => void}[], ignoreInputs = true) {
// I don't know if this is efficient, but I decided to just register one event listener for every shortcut, rather than generating one big event listener for all shortcuts.
// The benefit is that we don't have to memoize anything: useEffect will only be called if the action updated, and React allows calling useEffect for every item in a list as long as the list doesn't change.
for (const {keys, action} of spec) {
useEffect(() => {
const handler = (e: KeyboardEvent) => {
if (ignoreInputs) {
// @ts-ignore: don't steal keyboard events while the user is typing in a text box, etc.
if (["INPUT", "TEXTAREA", "SELECT"].includes(e.target?.tagName)) return;
}
if (e.ctrlKey !== keys.includes("Ctrl")) return;
if (e.shiftKey !== keys.includes("Shift")) return;
if (!keys.includes(e.key)) return;
const remainingKeys = keys.filter(key => key !== "Ctrl" && key !== "Shift" && key !== e.key);
if (remainingKeys.length !== 0) {
console.warn("impossible shortcut sequence:", keys.join(' + '));
return;
}
e.preventDefault();
e.stopPropagation();
action();
};
window.addEventListener("keydown", handler);
return () => window.removeEventListener("keydown", handler);
}, [action]);
}
}