refactor: integrate key management into cursor module

- Move useKeyBindings functionality into useCursor as useGameKeys
- Flatten GameManager interface using intersection types
- Rename clearLog/getLog to clearKeyLog/getKeyLog for clarity
- Remove separate useKeyBindings module and exports
- Update all documentation to reflect new architecture
- Maintain backward compatibility for platform hooks
This commit is contained in:
2025-10-19 12:56:35 +01:00
parent 81489557c3
commit 7c36f9dc14
9 changed files with 55 additions and 79 deletions
-1
View File
@@ -1,4 +1,3 @@
export * from './types';
export * from './useGame';
export * from './useGameStatus';
export * from './useKeyBindings';
+3 -9
View File
@@ -8,15 +8,9 @@ export type GameStatus = 'waiting' | 'started' | 'hasKey' | 'paused' | 'game-ove
export type GameManager = {
containerRef: RefObject<HTMLDivElement | null>;
gameStatus: GameStatus;
renderBoard: () => void;
startGame: () => void;
togglePause: (pause?: boolean) => void;
quitGame: () => void;
cursor: Cursor;
keyManager: GameKeyManager;
};
} & GameStatusManager & GameKeyManager;
export type BoardManager = {
@@ -55,8 +49,8 @@ export type KeyLogEntry = { key: string; timestamp: number };
export type GameKeyManager = {
keyLog: KeyLogEntry[];
clearLog: () => void;
getLog: () => KeyLogEntry[];
clearKeyLog: () => void;
getKeyLog: () => KeyLogEntry[];
};
export type GameStatusManager = {
+9 -9
View File
@@ -1,17 +1,18 @@
import { useRef, useEffect } from 'react'
import type { CursorMode, Coord, BoardManager, GameStatus } from '../types'
import { useHero } from './useHero'
import type { CursorMode, Coord, BoardManager, GameStatusManager } from '../types'
import { useHero } from './useHero';
import { useGameKeys } from './useGameKeys';
type Motion = { dr: number; dc: number; steps: number };
export function useCursor(board: BoardManager, gameStatus: GameStatus) {
export function useCursor(board: BoardManager, gameStatusManager: GameStatusManager) {
const position = useRef<Coord>({ row: 0, col: 0 })
const mode = useRef<CursorMode>('normal')
const countRef = useRef<string>("");
const lastMotionRef = useRef<Motion | null>(null);
const lastKeyRef = useRef<string>("");
const hero = useHero(board, gameStatus);
const hero = useHero(board, gameStatusManager);
// Sync cursor position with hero position
useEffect(() => {
@@ -219,7 +220,7 @@ export function useCursor(board: BoardManager, gameStatus: GameStatus) {
return lastKeyRef.current;
}
const cursorAPI = {
const cursor = {
position: () => position.current,
mode: () => mode.current,
move,
@@ -237,12 +238,11 @@ export function useCursor(board: BoardManager, gameStatus: GameStatus) {
setCount,
setLastKey,
getLastKey,
hero,
}
const keyManager = useGameKeys({ cursor, gameStatusManager });
return {
...cursorAPI,
hero,
};
return { ...cursor, keyManager };
}
@@ -1,12 +1,10 @@
import type { GameStatus, KeyLogEntry, UseHeroType, Cursor } from "../types";
import type { GameStatusManager, KeyLogEntry, Cursor } from "../types";
import { useEffect, useRef, useState } from "react";
type UseKeyBindingsParams = {
type UseGameKeysParams = {
cursor: Cursor;
hero: UseHeroType;
gameStatus: GameStatus;
setGameStatus: (status: GameStatus) => void;
gameStatusManager: GameStatusManager;
};
const RELEVANT_KEYS = new Set([
@@ -14,12 +12,14 @@ const RELEVANT_KEYS = new Set([
"1", "2", "3", "4", "5", "6", "7", "8", "9"
]);
export const useKeyBindings = ({ cursor, gameStatus, setGameStatus }: UseKeyBindingsParams) => {
export const useGameKeys = (props: UseGameKeysParams) => {
const { cursor, gameStatusManager } = props;
const { gameStatus, setGameStatus } = gameStatusManager;
const [keyLog, setKeyLog] = useState<KeyLogEntry[]>([]);
const logRef = useRef<KeyLogEntry[]>([]);
const { hero } = cursor;
const clearLog = () => {
const clearKeyLog = () => {
logRef.current = [];
setKeyLog([]);
};
@@ -116,8 +116,8 @@ export const useKeyBindings = ({ cursor, gameStatus, setGameStatus }: UseKeyBind
return {
keyLog,
clearLog,
getLog: () => logRef.current,
clearKeyLog,
getKeyLog: () => logRef.current,
};
};
+3 -3
View File
@@ -1,12 +1,12 @@
import { useRef, useState, useEffect } from "react";
import type { BoardManager, GameStatus, Coord } from "../types";
import type { BoardManager, GameStatusManager, GameStatus, Coord } from "../types";
import { useHeroRender } from "./useHeroRender";
export function useHero(board: BoardManager, gameStatus: GameStatus) {
export function useHero(board: BoardManager, gameStatusManager: GameStatusManager) {
const { mazeRef, containerRef } = board;
const [heroPos, setHeroPos] = useState<Coord | null>(null);
const animatingRef = useRef(false);
const { gameStatus } = gameStatusManager;
function getCellEl(r: number, c: number): HTMLElement | null {
const container = containerRef.current;
+3 -12
View File
@@ -1,7 +1,6 @@
import type { GameManager, GameOptions } from './types';
import { useBoard } from './useBoard';
import { useCursor } from './useCursor';
import { useKeyBindings } from './useKeyBindings';
import { useGameStatus } from './useGameStatus';
export function useGame(options: GameOptions, platformHook?: unknown): GameManager {
@@ -9,22 +8,14 @@ export function useGame(options: GameOptions, platformHook?: unknown): GameManag
const board = useBoard(cols, rows);
const { containerRef, renderBoard } = board;
const gameStatusManager = useGameStatus();
const { gameStatus, startGame, togglePause, quitGame, setGameStatus } = gameStatusManager;
const cursor = useCursor(board, gameStatus);
const keyManager = useKeyBindings({ cursor, gameStatus, setGameStatus });
const { keyManager, ...cursor } = useCursor(board, gameStatusManager);
const gameManager = {
containerRef,
gameStatus,
...keyManager,
...gameStatusManager,
renderBoard,
startGame,
togglePause,
quitGame,
cursor,
keyManager,
}
if (typeof platformHook === 'function') {
-1
View File
@@ -1 +0,0 @@
export * from './useKeyBindings';