Newer
Older
PixelPaintWar / apps / client / src / scenes / game / input / minimap / presentation / MiniMapPanel.tsx
/**
 * MiniMapPanel
 * ミニマップの開閉操作と表示を担うプレゼンテーションコンポーネント
 * 全体マップ枠とローカルプレイヤー現在地のみを描画する
 */
import { useMemo, useState } from "react";
import { config } from "@client/config";
import {
  buildMiniMapDotStyle,
  buildMiniMapToggleButtonStyle,
  MINIMAP_FRAME_STYLE,
  MINIMAP_PANEL_ROOT_STYLE,
} from "./MiniMapPanel.styles";

const MINIMAP_FRAME_SIZE_PX = 128;

/** ミニマップの入力プロパティ */
export type MiniMapPanelProps = {
  localPlayerPosition: { x: number; y: number } | null;
};

const clamp = (value: number, min: number, max: number): number => {
  return Math.max(min, Math.min(value, max));
};

/** 全体マップ上のローカル位置を示すミニマップを描画する */
export const MiniMapPanel = ({ localPlayerPosition }: MiniMapPanelProps) => {
  const [isOpen, setIsOpen] = useState(false);

  const markerPosition = useMemo(() => {
    if (!localPlayerPosition) {
      return null;
    }

    const width = config.GAME_CONFIG.MAP_WIDTH_PX;
    const height = config.GAME_CONFIG.MAP_HEIGHT_PX;
    if (width <= 0 || height <= 0) {
      return null;
    }

    const normalizedX = clamp(localPlayerPosition.x / width, 0, 1);
    const normalizedY = clamp(localPlayerPosition.y / height, 0, 1);

    return {
      leftPx: normalizedX * MINIMAP_FRAME_SIZE_PX,
      topPx: normalizedY * MINIMAP_FRAME_SIZE_PX,
    };
  }, [localPlayerPosition]);

  const buttonStyle = buildMiniMapToggleButtonStyle(isOpen);

  return (
    <div style={MINIMAP_PANEL_ROOT_STYLE}>
      <button
        type="button"
        style={buttonStyle}
        onClick={() => {
          setIsOpen((prev) => !prev);
        }}
      >
        {isOpen ? "閉じる" : "ミニマップ"}
      </button>

      {isOpen && (
        <div style={MINIMAP_FRAME_STYLE}>
          {markerPosition && (
            <div
              style={buildMiniMapDotStyle(
                markerPosition.leftPx,
                markerPosition.topPx,
              )}
            />
          )}
        </div>
      )}
    </div>
  );
};