// src/components/PitchView.jsx import React, { useRef, useState, useEffect, useCallback } from "react"; import { DraggablePlayer } from "./DraggablePlayer"; /* DESIGN DIMENSIONS — single source of truth for the pitch. All cards are laid out at these fixed pixel dimensions and the whole pitch is uniformly transform-scaled to fit the actual container. This keeps proportions identical across every screen size (no element gets relatively bigger or smaller than another when the viewport changes). Card: 88×106px (larger for better legibility) Label strip: ~52px Card slot total ≈ 158px per row DESIGN_WIDTH = 670: fits widest row (5 mids/defs) = 5×88 + 4×24 = 536 ≤ 670 ✓ 4 starter rows + gaps ≈ 780px total height Bench ≈ 230px MAX_SCALE > 1 lets the pitch scale modestly on wide screens without badges and labels becoming disproportionately large. */ const DESIGN_WIDTH = 670; const STARTERS_H = 780; const BENCH_H = 230; // Gap between player card wrappers in px (in design space) const CARD_GAP = 24; // Allow the pitch to scale up to 1.1× on wide screens so cards don't look // undersized on desktop. Below DESIGN_WIDTH it scales down naturally to fit. const MAX_SCALE = 1.0; export const PitchView = ({ teamData, activeDragPlayer, isValidSwap, captainId, viceId, handleCapChange, playerCardGWs, fixtures, activeGW, setSelectedPlayer, handleUndoTransfer, highlightTransferIds, solverTransferPairs, resetHighlightedTransfer, chipsByGw, }) => { const containerRef = useRef(null); const [scale, setScale] = useState(1); const [isFullscreen, setIsFullscreen] = useState(false); const toggleFullscreen = useCallback(() => { if (!containerRef.current) return; if (!document.fullscreenElement) { containerRef.current.requestFullscreen?.(); } else { document.exitFullscreen?.(); } }, []); const handlePlayerClick = useCallback((player) => { setSelectedPlayer(player); }, [setSelectedPlayer]); const updateScale = useCallback(() => { if (!containerRef.current) return; const next = Math.min(MAX_SCALE, containerRef.current.offsetWidth / DESIGN_WIDTH); setScale(prev => Math.abs(prev - next) > 0.005 ? next : prev); }, []); useEffect(() => { updateScale(); const ro = new ResizeObserver(updateScale); if (containerRef.current) ro.observe(containerRef.current); return () => ro.disconnect(); }, [updateScale]); useEffect(() => { const handler = () => { const fs = !!document.fullscreenElement; setIsFullscreen(fs); if (!containerRef.current) return; const w = fs ? window.innerWidth : containerRef.current.offsetWidth; const next = Math.min(MAX_SCALE, w / DESIGN_WIDTH); setScale(prev => Math.abs(prev - next) > 0.005 ? next : prev); }; document.addEventListener("fullscreenchange", handler); return () => document.removeEventListener("fullscreenchange", handler); }, []); const isBBChip = chipsByGw[activeGW] === "bb"; // The outer "height-reserving" divs must use the SCALED height so the // document flow collapses correctly — otherwise content below (bench, etc.) // overlaps the starters area. const scaledStartersH = STARTERS_H * scale; const scaledBenchH = BENCH_H * scale; // Inner transform div: fixed at design size, scaled from top-center const innerStyle = (designH, extraStyle = {}) => ({ width: DESIGN_WIDTH, height: designH, transform: `scale(${scale})`, transformOrigin: "top center", position: "absolute", left: "50%", marginLeft: -(DESIGN_WIDTH / 2), top: 0, willChange: "transform", ...extraStyle, }); return (
{/* Fullscreen toggle — mobile only */} {/* ── PITCH LINES — fill the full container, never scaled ── */}
{/* ── STARTERS ── */} {/* Outer reserves scaled height in document flow */}
{/* Inner is at design dimensions, scaled from top-center */}
{/* top padding */} {["G", "D", "M", "F"].map((pos) => { const rowPlayers = teamData.slice(0, 11).filter((p) => p.Pos === pos); if (rowPlayers.length === 0) return null; return (
{rowPlayers.map((p) => ( resetHighlightedTransfer(p) : undefined} activeChipType={chipsByGw[activeGW]} /> ))}
); })}
{/* bottom padding */}
{/* ── BENCH ── */}
{teamData.slice(11, 15).map((p, benchIndex) => ( resetHighlightedTransfer(p) : undefined} activeChipType={chipsByGw[activeGW]} /> ))}
); };