96 lines · 2.5 KB · TypeScript
RawDownload
import { useEffect, useState } from "react"; export interface ShortcutModifierState {  metaKey: boolean;  ctrlKey: boolean;  altKey: boolean;  shiftKey: boolean;} const EMPTY_SHORTCUT_MODIFIER_STATE: ShortcutModifierState = {  metaKey: false,  ctrlKey: false,  altKey: false,  shiftKey: false,}; export function areShortcutModifierStatesEqual(  left: ShortcutModifierState,  right: ShortcutModifierState,): boolean {  return (    left.metaKey === right.metaKey &&    left.ctrlKey === right.ctrlKey &&    left.altKey === right.altKey &&    left.shiftKey === right.shiftKey  );} export function useShortcutModifierState(): ShortcutModifierState {  const [state, setState] = useState(EMPTY_SHORTCUT_MODIFIER_STATE);   useEffect(() => {    const onKeyboardEvent = (event: KeyboardEvent) => {      setState((current) => shortcutModifierStateAfterKeyboardEvent(current, event));    };    const onWindowBlur = () => {      setState((current) =>        areShortcutModifierStatesEqual(current, EMPTY_SHORTCUT_MODIFIER_STATE)          ? current          : EMPTY_SHORTCUT_MODIFIER_STATE,      );    };     window.addEventListener("keydown", onKeyboardEvent, true);    window.addEventListener("keyup", onKeyboardEvent, true);    window.addEventListener("blur", onWindowBlur);    return () => {      window.removeEventListener("keydown", onKeyboardEvent, true);      window.removeEventListener("keyup", onKeyboardEvent, true);      window.removeEventListener("blur", onWindowBlur);    };  }, []);   return state;} function normalizeModifierKey(key: string): keyof ShortcutModifierState | null {  switch (key) {    case "Meta":    case "OS":    case "Command":      return "metaKey";    case "Control":      return "ctrlKey";    case "Alt":    case "Option":      return "altKey";    case "Shift":      return "shiftKey";    default:      return null;  }} export function shortcutModifierStateAfterKeyboardEvent(  currentState: ShortcutModifierState,  event: KeyboardEvent,): ShortcutModifierState {  const normalizedModifierKey = normalizeModifierKey(event.key);  let nextState: ShortcutModifierState;  if (normalizedModifierKey) {    nextState = {      ...currentState,      [normalizedModifierKey]: event.type === "keydown",    };  } else {    nextState = {      metaKey: event.metaKey,      ctrlKey: event.ctrlKey,      altKey: event.altKey,      shiftKey: event.shiftKey,    };  }   return areShortcutModifierStatesEqual(currentState, nextState) ? currentState : nextState;}