All files / utils responsiveOrientationConstants.ts

100% Statements 5/5
100% Branches 6/6
100% Functions 1/1
100% Lines 5/5

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                                            5x                       5x                                 5x                                                         28x               28x    
/**
 * Shared constants for orientation-aware responsive layout.
 *
 * Extracted so that `useCombatLayout` and `useTrainingLayout` compute the
 * same portrait "bottom band" and share a single source of truth for the
 * portrait-forcing hysteresis and the reserved control-column heights.
 *
 * 반응형 레이아웃 상수
 *
 * @module utils/responsiveOrientationConstants
 * @category Layout
 */
 
/**
 * Hysteresis factor for portrait detection.
 *
 * A viewport is treated as portrait when `height > width × FACTOR`. The
 * factor is < 1.0 so near-square viewports (e.g. `1024×1000`) settle into
 * one orientation and don't flap on every pixel of resize.
 *
 * @public
 */
export const PORTRAIT_HYSTERESIS_FACTOR = 0.9;
 
/**
 * Maximum width at which a portrait viewport is force-promoted to the mobile
 * layout branch even if the user-agent reports a desktop browser. Matches the
 * tablet breakpoint used elsewhere in the codebase (1024px).
 *
 * This makes devtools emulation of a rotated phone/tablet behave identically
 * to a real device.
 *
 * @public
 */
export const PORTRAIT_FORCE_MAX_WIDTH_PX = 1024;
 
/**
 * Height reserved at the bottom of a mobile portrait viewport for the
 * on-screen virtual controls (D-Pad + action buttons rendered by
 * `MobileControlsWrapper`). Used as a conservative upper bound so the
 * 3D arena never ends up drawn behind the controls.
 *
 * Two values are provided so that very small phones (iPhone SE class,
 * width < 380) can still fit a playable arena.
 *
 * Combat uses the larger 200/160 band because its Mobile controls stack
 * D-Pad + action buttons + the persistent technique bar. Training uses
 * the smaller 180/140 band because its on-screen controls are lighter.
 *
 * @public
 */
export const MOBILE_CONTROLS_RESERVED_HEIGHT_PX = {
  /** D-Pad + action buttons + technique bar on combat (standard phones) */
  combatStandard: 200,
  /** Combat controls on extra-small phones (width < 380) */
  combatExtraSmall: 160,
  /** Training on-screen controls (standard phones) */
  trainingStandard: 180,
  /** Training on-screen controls (extra-small phones) */
  trainingExtraSmall: 140,
} as const;
 
/**
 * Total bottom clearance to reserve in portrait mode = control/technique
 * bar height + footer height + the on-screen virtual controls band.
 *
 * @param controlsHeight - layout constant for technique/control bar
 * @param footerHeight - layout constant for footer
 * @param isExtraSmall - true when the viewport is < 380px wide
 * @param variant - "combat" or "training" (differs in control band size)
 *
 * @public
 */
export function portraitMobileControlsBottomBand(
  controlsHeight: number,
  footerHeight: number,
  isExtraSmall: boolean,
  variant: "combat" | "training",
): number {
  const band =
    variant === "combat"
      ? isExtraSmall
        ? MOBILE_CONTROLS_RESERVED_HEIGHT_PX.combatExtraSmall
        : MOBILE_CONTROLS_RESERVED_HEIGHT_PX.combatStandard
      : isExtraSmall
        ? MOBILE_CONTROLS_RESERVED_HEIGHT_PX.trainingExtraSmall
        : MOBILE_CONTROLS_RESERVED_HEIGHT_PX.trainingStandard;
 
  return controlsHeight + footerHeight + band;
}