import { Source } from "./source"; export type KeyName = "up" | "down" | "left" | "right" | "a" | "b" | "menu"; const KEY_NAMES: Record = { // compact keys (WASD+ZXC) KeyZ: "a", KeyX: "b", KeyC: "menu", KeyW: "up", KeyS: "down", KeyA: "left", KeyD: "right", // full-board keys (arrows+space/shift/enter) Space: "a", ShiftLeft: "b", ShiftRight: "b", Enter: "menu", ArrowUp: "up", ArrowDown: "down", ArrowLeft: "left", ArrowRight: "right", }; /** A keypress/release event for an abstract button, or else ["focus", "release"] if for some reason future release events might not be registered. */ export type KeyEvent = [KeyName | "focus", "press" | "release"]; export function keyControl(source: HTMLElement): Source { const tabIndex = source.getAttribute("tabIndex"); source.setAttribute( "tabindex", tabIndex == "" || tabIndex == null ? "-1" : tabIndex ); return ((callback?: (keyEvent: KeyEvent) => void) => { if (callback) { const handle = (evt: KeyboardEvent, action: "press" | "release") => { const keyName = KEY_NAMES[evt.code]; if (keyName != undefined) { evt.preventDefault(); evt.stopPropagation(); callback([keyName, action]); } }; const keyUp = (evt: KeyboardEvent) => handle(evt, "release"); const keyDown = (evt: KeyboardEvent) => handle(evt, "press"); const focus = () => callback(["focus", "press"]); const blur = () => callback(["focus", "release"]); source.addEventListener("keyup", keyUp, false); source.addEventListener("keydown", keyDown, false); source.addEventListener("focus", focus, false); source.addEventListener("blur", blur, false); source.focus({ focusVisible: true } as FocusOptions); return () => { source.removeEventListener("keyup", keyUp, false); source.removeEventListener("keydown", keyDown, false); source.removeEventListener("focus", focus, false); source.removeEventListener("blur", blur, false); }; } else { return ["blur", "release"]; } }) as Source; }