feat: implement Phase 40.1 - basic cursor with hjkl movement
Add cursor navigation with hjkl keys (vim-style): New Module Structure: - src/useCursor/types.ts: Coord and CursorManager types - src/useCursor/useCursor.ts: Cursor movement logic - src/useCursor/useCursorRender.ts: Visual cursor highlight - src/useCursor/index.ts: Public exports Cursor Implementation: - Position tracking with useRef (row/col from 0-8) - Basic movement: moveLeft, moveRight, moveUp, moveDown - Bounds checking (prevents moving outside 9x9 grid) - Keyboard listener for h/j/k/l keys - Only active when gameStatus === 'started' Cursor Rendering: - useCursorRender hook tracks cursor position - Adds 'cursor' CSS class to current cell - Removes from previous cell on movement - Blue highlight with border (rgba(59, 130, 246, 0.3)) Integration: - src/useGame.ts: Integrate useCursor and useCursorRender - src/types.ts: Export CursorManager and Coord types - src/useBoard/types.ts: NEW - BoardManager type - src/game.css: Add cursor highlight styles Game Flow: - Press Space → Game starts → Cursor appears at (0,0) - Press h/j/k/l → Cursor moves (bounded to grid) - Visual feedback with blue highlight - Press Q → Return to waiting state Phase 40.1 complete. Next: Add number prefixes (5j) and other vim motions.
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import { useRef, useEffect } from 'react';
|
||||
import type { Coord, CursorManager } from './types';
|
||||
import type { GameStatusManager } from '../useGameStatus/types';
|
||||
|
||||
export function useCursor(gameStatusManager: GameStatusManager) {
|
||||
const position = useRef<Coord>({ row: 0, col: 0 });
|
||||
const { gameStatus } = gameStatusManager;
|
||||
|
||||
function move(dRows: number, dCols: number, count: number = 1): void {
|
||||
const currentPos = position.current;
|
||||
const newRow = currentPos.row + (dRows * count);
|
||||
const newCol = currentPos.col + (dCols * count);
|
||||
|
||||
if (newRow < 0 || newRow > 8 || newCol < 0 || newCol > 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
position.current = { row: newRow, col: newCol };
|
||||
}
|
||||
|
||||
function moveLeft(count = 1): void {
|
||||
move(0, -1, count);
|
||||
}
|
||||
|
||||
function moveRight(count = 1): void {
|
||||
move(0, 1, count);
|
||||
}
|
||||
|
||||
function moveUp(count = 1): void {
|
||||
move(-1, 0, count);
|
||||
}
|
||||
|
||||
function moveDown(count = 1): void {
|
||||
move(1, 0, count);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (gameStatus !== 'started') return;
|
||||
|
||||
function handleKeyDown(e: KeyboardEvent) {
|
||||
const key = e.key;
|
||||
|
||||
if (key === 'h') {
|
||||
moveLeft();
|
||||
} else if (key === 'j') {
|
||||
moveDown();
|
||||
} else if (key === 'k') {
|
||||
moveUp();
|
||||
} else if (key === 'l') {
|
||||
moveRight();
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [gameStatus]);
|
||||
|
||||
const cursor: CursorManager = {
|
||||
position: () => position.current,
|
||||
moveLeft,
|
||||
moveRight,
|
||||
moveUp,
|
||||
moveDown,
|
||||
};
|
||||
|
||||
return cursor;
|
||||
}
|
||||
Reference in New Issue
Block a user