All files / components/screens/controls/hooks useControlsState.ts

100% Statements 26/26
100% Branches 4/4
100% Functions 9/9
100% Lines 26/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107                                                                                                    88x 88x 88x     88x 54x   37x 2x     35x 35x 35x 35x       54x 17x 17x 17x 17x         54x 54x     54x 54x 54x         88x 8x       88x 6x     88x                
/**
 * useControlsState - State management hook for Controls Screen
 * 
 * Manages keyboard press detection, gamepad state, and category selection
 * 
 * @module components/screens/controls/hooks
 */
 
import { useCallback, useEffect, useState } from "react";
 
/**
 * Controls state interface
 */
export interface ControlsState {
  readonly pressedKeys: Set<string>;
  readonly category: 'keyboard' | 'gamepad';
  readonly selectedTab: 'combat' | 'movement' | 'system';
}
 
/**
 * Hook return type
 */
export interface UseControlsStateReturn {
  readonly pressedKeys: Set<string>;
  readonly category: 'keyboard' | 'gamepad';
  readonly selectedTab: 'combat' | 'movement' | 'system';
  readonly setCategory: (category: 'keyboard' | 'gamepad') => void;
  readonly setSelectedTab: (tab: 'combat' | 'movement' | 'system') => void;
}
 
/**
 * Custom hook for managing controls screen state
 * 
 * Features:
 * - Keyboard press detection with cleanup
 * - Category switching (keyboard/gamepad)
 * - Tab selection for control categories
 * 
 * @example
 * ```tsx
 * const { pressedKeys, category, selectedTab, setCategory, setSelectedTab } = useControlsState();
 * 
 * // Check if key is pressed
 * const isSpacePressed = pressedKeys.has('Space');
 * 
 * // Switch to gamepad view
 * setCategory('gamepad');
 * ```
 */
export function useControlsState(): UseControlsStateReturn {
  const [pressedKeys, setPressedKeys] = useState<Set<string>>(new Set());
  const [category, setCategory] = useState<'keyboard' | 'gamepad'>('keyboard');
  const [selectedTab, setSelectedTab] = useState<'combat' | 'movement' | 'system'>('combat');
 
  // Keyboard event handlers
  useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      // Don't track if user is typing in input field
      if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) {
        return;
      }
 
      setPressedKeys(prev => {
        const next = new Set(prev);
        next.add(event.code);
        return next;
      });
    };
 
    const handleKeyUp = (event: KeyboardEvent) => {
      setPressedKeys(prev => {
        const next = new Set(prev);
        next.delete(event.code);
        return next;
      });
    };
 
    // Add event listeners
    window.addEventListener('keydown', handleKeyDown);
    window.addEventListener('keyup', handleKeyUp);
 
    // Cleanup on unmount
    return () => {
      window.removeEventListener('keydown', handleKeyDown);
      window.removeEventListener('keyup', handleKeyUp);
    };
  }, []);
 
  // Memoized category setter
  const handleSetCategory = useCallback((newCategory: 'keyboard' | 'gamepad') => {
    setCategory(newCategory);
  }, []);
 
  // Memoized tab setter
  const handleSetSelectedTab = useCallback((tab: 'combat' | 'movement' | 'system') => {
    setSelectedTab(tab);
  }, []);
 
  return {
    pressedKeys,
    category,
    selectedTab,
    setCategory: handleSetCategory,
    setSelectedTab: handleSetSelectedTab,
  };
}