ProzillaOS/src/hooks/_utils/keyboard.ts
Prozilla 2ede410bec
Migration to TypeScript - Stage 3
Improved safe typing
2024-05-10 15:43:14 +02:00

100 lines
No EOL
2.7 KiB
TypeScript

import { useCallback, useEffect, useState } from "react";
import { removeFromArray } from "../../features/_utils/array.utils";
interface UseKeyboardListenerParams {
onKeyDown?: (event: KeyboardEvent) => void;
onKeyUp?: (event: KeyboardEvent) => void;
};
export function useKeyboardListener({ onKeyDown, onKeyUp }: UseKeyboardListenerParams) {
useEffect(() => {
if (onKeyDown)
document.addEventListener("keydown", onKeyDown);
if (onKeyUp)
document.addEventListener("keyup", onKeyUp);
return () => {
if (onKeyDown)
document.removeEventListener("keydown", onKeyDown);
if (onKeyUp)
document.removeEventListener("keyup", onKeyUp);
};
}, [onKeyDown, onKeyUp]);
}
interface UseShortcutsParams {
options: Record<string, Record<string, Function>> | Record<string, Function>;
shortcuts?: Record<string, Record<string, string[]>> | Record<string, string[]>;
useCategories?: boolean,
}
export function useShortcuts({ options, shortcuts, useCategories = true }: UseShortcutsParams) {
const [activeKeys, setActiveKeys] = useState<string[]>([]);
const checkShortcuts = useCallback((event: KeyboardEvent, allowExecution = true) => {
if (!shortcuts)
return;
const keys = [...activeKeys];
// Prevent alt-tabbing from messing with alt key registration
if (keys.includes("Alt") && !event.altKey)
removeFromArray("Alt", keys);
const checkGroup = (group: Record<string, string[]>, category?: string) => {
for (const [name, shortcut] of Object.entries(group)) {
let active = true;
shortcut.forEach((key) => {
if (!keys.includes(key) && event.key !== key)
return active = false;
});
if (!active)
continue;
event.preventDefault();
event.stopPropagation();
if (!shortcut.includes(event.key) || !allowExecution)
continue;
if (category != null) {
(options?.[category]?.[name] as Function)?.();
} else {
(options?.[name] as Function)?.();
}
}
};
if (useCategories) {
for (const [category, group] of Object.entries(shortcuts)) {
checkGroup(group as Record<string, string[]>, category);
}
} else {
checkGroup(shortcuts as Record<string, string[]>);
}
setActiveKeys(keys);
}, [activeKeys, options, shortcuts, useCategories]);
const onKeyDown = (event: KeyboardEvent) => {
const isRepeated = activeKeys.includes(event.key);
checkShortcuts(event, isRepeated);
if (!isRepeated)
setActiveKeys(activeKeys.concat([event.key]));
};
const onKeyUp = (event: KeyboardEvent) => {
checkShortcuts(event);
if (activeKeys.includes(event.key)) {
const keys = [...activeKeys];
removeFromArray(event.key, keys);
setActiveKeys(keys);
}
};
useKeyboardListener({ onKeyDown, onKeyUp });
}