68 lines
2.1 KiB
TypeScript
68 lines
2.1 KiB
TypeScript
|
import { Source } from "./source";
|
||
|
|
||
|
export type KeyName = "up" | "down" | "left" | "right" | "a" | "b" | "menu";
|
||
|
|
||
|
const KEY_NAMES: Record<string, KeyName> = {
|
||
|
// 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<KeyEvent> {
|
||
|
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<KeyEvent>;
|
||
|
}
|