All files / components/screens/combat/components/hud MobileControlsWrapper.tsx

100% Statements 2/2
100% Branches 0/0
100% Functions 1/1
100% Lines 2/2

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 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144                                                                                                                                                                                  1x                     7x                                                                                      
/**
 * MobileControlsWrapper - Mobile touch controls container
 *
 * Wraps all mobile-specific touch controls including:
 * - Virtual D-Pad for movement
 * - Action buttons (attack/block)
 * - Stance wheel for trigram stance selection
 * - Gesture recognizer for swipe actions
 *
 * PRIORITY SUPPORT: Optimized for high-end mobile devices (Super HD, 2K+)
 * - Enhanced touch targets (56px on Super HD vs 48px standard)
 * - Larger fonts and better spacing on high-resolution displays
 * - Full feature parity with desktop experience
 *
 * Integrated with:
 * - CombatScreen3D: Full combat gameplay with mobile controls
 * - TrainingScreen3D: Training mode with mobile controls
 * - deviceDetection.ts: Robust mobile detection (handles 2712x1220 Super HD)
 * - mobileUIUtils.ts: Touch target and font sizing utilities
 *
 * Only rendered on mobile devices (isMobile=true)
 *
 * @module components/combat/components/MobileControlsWrapper
 * @category Combat UI
 * @korean 모바일컨트롤래퍼
 */
 
import React from "react";
import {
  ActionButtons,
  GestureRecognizerPure,
  StanceWheelPure,
  VirtualDPad,
} from "../../../../shared/mobile";
import { ButtonEventType } from "../../../../shared/mobile/ActionButtons";
import { Direction, DPadEventType } from "../../../../shared/mobile/VirtualDPad";
import { GestureEvent } from "../../../../../hooks/useTouchControls";
import { getMobileControlsBottom } from "../../../../../types/constants/layout";
 
export interface MobileControlsWrapperProps {
  /** Whether mobile controls are enabled */
  readonly enabled: boolean;
  /** Current stance index (0-7) */
  readonly currentStanceIndex: number;
  /** Whether stance wheel is expanded */
  readonly stanceWheelExpanded: boolean;
  /** D-Pad movement handler */
  readonly onMove: (direction: Direction | null, eventType: DPadEventType) => void;
  /** Attack button handler */
  readonly onAttack: () => void;
  /** Block button handler */
  readonly onBlock: (eventType: ButtonEventType) => void;
  /** Stance change handler */
  readonly onStanceChange: (stanceIndex: number) => void;
  /** Stance wheel toggle handler */
  readonly onStanceWheelToggle: () => void;
  /** Gesture handler */
  readonly onGesture: (gesture: GestureEvent) => void;
}
 
/**
 * MobileControlsWrapper - Container for all mobile touch controls
 *
 * Provides virtual controls for mobile combat gameplay:
 * - Virtual D-Pad (bottom-left) for 8-direction movement
 * - Action Buttons (bottom-right) for attack/block
 * - Stance Wheel (right-center) for trigram stance selection
 * - Gesture Recognition for swipe-based actions
 *
 * Positioning Strategy:
 * - All controls positioned using centralized layout constants
 * - D-Pad and ActionButtons use getMobileControlsBottom() for consistency
 * - Z-index: MOBILE_CONTROLS (50) to ensure visibility over HUD elements
 *
 * @example
 * ```tsx
 * <MobileControlsWrapper
 *   enabled={mobileControlsEnabled}
 *   currentStanceIndex={currentStanceIndex}
 *   stanceWheelExpanded={stanceWheelExpanded}
 *   onMove={handleMobileMove}
 *   onAttack={handleMobileAttack}
 *   onBlock={handleMobileBlock}
 *   onStanceChange={handleMobileStanceChange}
 *   onStanceWheelToggle={() => setStanceWheelExpanded(!stanceWheelExpanded)}
 *   onGesture={handleMobileGesture}
 * />
 * ```
 */
export const MobileControlsWrapper: React.FC<MobileControlsWrapperProps> = ({
  enabled,
  currentStanceIndex,
  stanceWheelExpanded,
  onMove,
  onAttack,
  onBlock,
  onStanceChange,
  onStanceWheelToggle,
  onGesture,
}) => {
  return (
    <>
      {/* Virtual D-Pad - Bottom-left for movement */}
      {/* Positioned using centralized constant for consistency */}
      <VirtualDPad
        onMove={onMove}
        disabled={!enabled}
        opacity={0.8}
        bottom={getMobileControlsBottom()}
      />
 
      {/* Action Buttons - Bottom-right for attack/block */}
      {/* Positioned using centralized constant for consistency */}
      <ActionButtons
        onAttack={onAttack}
        onBlock={onBlock}
        disabled={!enabled}
        opacity={0.8}
        bottom={getMobileControlsBottom()}
      />
 
      {/* Stance Wheel - Right-center for trigram stance selection */}
      <StanceWheelPure
        currentStance={currentStanceIndex}
        onStanceChange={onStanceChange}
        expanded={stanceWheelExpanded}
        onToggle={onStanceWheelToggle}
        disabled={!enabled}
        opacity={0.8}
      />
 
      {/* Gesture Recognizer - Full-screen swipe detection */}
      <GestureRecognizerPure
        onGesture={onGesture}
        enabled={enabled}
        showFeedback={true}
        minSwipeDistance={50}
      />
    </>
  );
};
 
export default MobileControlsWrapper;