baublet/w8mngr

View on GitHub
client/helpers/useKeyPressHandler.ts

Summary

Maintainability
A
1 hr
Test Coverage
import React from "react";

type Options = {
  /**
   * To prevent double-taps, we use a default throttle of 100ms.
   */
  throttleTimeMs?: number;
};

export function useKeyPressHandler(
  key: "up" | "down" | "left" | "right" | "enter" | "esc",
  passedHandler: (event: KeyboardEvent) => void
): void {
  const handlerRef = React.useRef(passedHandler);
  const [, setLastMs] = React.useState(() => Date.now());

  React.useEffect(() => {
    handlerRef.current = passedHandler;
  }, [passedHandler]);

  const handler = React.useCallback<(event: KeyboardEvent) => void>((event) => {
    setLastMs((lastMs) => {
      const now = Date.now();
      const shouldSkip = now - lastMs < 100;
      if (shouldSkip) {
        return lastMs;
      }
      runHandlerIfKeyMatches(key, event, handlerRef.current);
      return now;
    });
  }, []);

  React.useEffect(() => {
    document.addEventListener("keydown", handler);
    return () => document.removeEventListener("keydown", handler);
  }, []);
}

function runHandlerIfKeyMatches(
  userKey: "up" | "down" | "left" | "right" | "enter" | "esc",
  event: KeyboardEvent,
  handler: (event: KeyboardEvent) => void
) {
  const eventKey = event.key;

  if (userKey === "up" && eventKey !== "ArrowUp") return;
  if (userKey === "down" && eventKey !== "ArrowDown") return;
  if (userKey === "enter" && eventKey !== "Enter") return;
  if (userKey === "esc" && eventKey !== "Escape") return;
  if (userKey === "left" && eventKey !== "ArrowLeft") return;
  if (userKey === "right" && eventKey !== "ArrowRight") return;

  handler(event);
}