All files / components/shared/three/ui TechniqueBarContainer.tsx

100% Statements 9/9
100% Branches 3/3
100% Functions 3/3
100% Lines 7/7

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                                                                                                      1x       23x       23x                               23x       23x 1x     22x                    
/**
 * TechniqueBarContainer - Positioned container for TechniqueBar
 * 
 * Provides consistent positioning and styling for TechniqueBar across
 * Combat and Training screens. Handles responsive positioning and z-index.
 * 
 * Eliminates 60+ lines of duplicate code between CombatScreen3D and TrainingScreen3D.
 * 
 * @module components/shared/three/ui/TechniqueBarContainer
 * @category Combat UI
 * @korean 기술바컨테이너
 */
 
import React, { useMemo } from "react";
import { TechniqueBar, type TechniqueBarProps } from "./TechniqueBar";
import { getTechniqueBarBottom, LAYOUT_BOTTOM_POSITIONS } from "../../../../types/constants/layout";
import { Z_INDEX } from "../../../../types/LayoutTypes";
 
export interface TechniqueBarContainerProps extends TechniqueBarProps {
  /** Whether to show the TechniqueBar (allows conditional rendering) */
  readonly visible?: boolean;
}
 
/**
 * TechniqueBarContainer - Positioned wrapper for TechniqueBar
 * 
 * Provides consistent positioning, styling, and pointer event handling
 * for the TechniqueBar across different screen contexts (Combat and Training).
 * 
 * Features:
 * - Memoized styles for performance
 * - Centralized positioning using layout constants
 * - Semantic z-index (Z_INDEX.TECHNIQUE_BAR)
 * - Pointer event handling (container non-interactive, inner interactive)
 * 
 * @example
 * ```tsx
 * <TechniqueBarContainer
 *   visible={combatState.roundStarted && !combatState.roundEnded}
 *   techniques={availableTechniques}
 *   player={player}
 *   selectedIndex={selectedIndex}
 *   cooldowns={cooldownsMap}
 *   onTechniqueSelect={handleSelect}
 *   onTechniqueHover={handleHover}
 *   isMobile={isMobile}
 *   screenWidth={width}
 *   screenHeight={height}
 * />
 * ```
 */
export const TechniqueBarContainer: React.FC<TechniqueBarContainerProps> = ({
  visible = true,
  ...techniqueBarProps
}) => {
  const { isMobile } = techniqueBarProps;
 
  // Memoize container styles to prevent unnecessary re-renders
  // Positioned at bottom to minimize arena obstruction (gameplay priority)
  const containerStyle = useMemo(() => ({
    position: "absolute" as const,
    left: 0,
    bottom: getTechniqueBarBottom(isMobile),
    width: "100%",
    height: `${LAYOUT_BOTTOM_POSITIONS.TECHNIQUE_BAR_HEIGHT}px`,
    pointerEvents: "none" as const,
    zIndex: Z_INDEX.TECHNIQUE_BAR,
    display: "flex",
    justifyContent: "center" as const,
    alignItems: "flex-end" as const,
    // Semi-transparent background to not fully obstruct arena
    background: "linear-gradient(to top, rgba(0, 0, 0, 0.4) 0%, rgba(0, 0, 0, 0) 100%)",
  }), [isMobile]);
 
  // Memoize inner div style for pointer events
  const innerStyle = useMemo(() => ({
    pointerEvents: "auto" as const,
  }), []);
 
  if (!visible) {
    return null;
  }
 
  return (
    <div style={containerStyle} data-testid="technique-bar-container">
      <div style={innerStyle}>
        <TechniqueBar {...techniqueBarProps} key={techniqueBarProps.player.currentStance} />
      </div>
    </div>
  );
};
 
export default TechniqueBarContainer;