diff --git a/frontend/src/components/Bracket/BracketView.tsx b/frontend/src/components/Bracket/BracketView.tsx index 0226a72..c9ab10d 100644 --- a/frontend/src/components/Bracket/BracketView.tsx +++ b/frontend/src/components/Bracket/BracketView.tsx @@ -1,6 +1,7 @@ // frontend/src/components/Bracket/BracketView.tsx import React, { useEffect, useRef, useState } from 'react'; +import { ZoomIn, ZoomOut } from 'lucide-react'; import { type MatchData } from '../../types'; import Podium from "../Tournament/Podium"; import MatchCard from "./MatchCard"; @@ -13,10 +14,19 @@ interface BracketViewProps { export default function BracketView({ matches, onMatchClick }: BracketViewProps) { const containerRef = useRef(null); const [lines, setLines] = useState([]); + const [zoom, setZoom] = useState(1); + const [contentSize, setContentSize] = useState({ width: 0, height: 0 }); useEffect(() => { const draw = () => { if (!containerRef.current) return; + + // Measure the unscaled bracket size to fix scrollbars later + setContentSize({ + width: containerRef.current.scrollWidth, + height: containerRef.current.scrollHeight + }); + const container = containerRef.current.getBoundingClientRect(); const newLines: React.ReactElement[] = []; @@ -26,9 +36,13 @@ export default function BracketView({ matches, onMatchClick }: BracketViewProps) const eEl = document.getElementById(`match-${m.winner_next_match_id}`); if (sEl && eEl) { const r1 = sEl.getBoundingClientRect(), r2 = eEl.getBoundingClientRect(); - const sx = r1.right - container.left, sy = r1.top + r1.height / 2 - container.top; - const ex = r2.left - container.left, ey = r2.top + r2.height / 2 - container.top; + + const sx = (r1.right - container.left) / zoom; + const sy = (r1.top + r1.height / 2 - container.top) / zoom; + const ex = (r2.left - container.left) / zoom; + const ey = (r2.top + r2.height / 2 - container.top) / zoom; const c1 = sx + (ex - sx) / 2; + newLines.push( ); @@ -42,9 +56,13 @@ export default function BracketView({ matches, onMatchClick }: BracketViewProps) const eEl = document.getElementById(`match-${targetMatch.id}`); if (sEl && eEl) { const r1 = sEl.getBoundingClientRect(), r2 = eEl.getBoundingClientRect(); - const sx = r1.right - container.left, sy = r1.top + r1.height / 2 - container.top; - const ex = r2.left - container.left, ey = r2.top + r2.height / 2 - container.top; + + const sx = (r1.right - container.left) / zoom; + const sy = (r1.top + r1.height / 2 - container.top) / zoom; + const ex = (r2.left - container.left) / zoom; + const ey = (r2.top + r2.height / 2 - container.top) / zoom; const c1 = sx + (ex - sx) / 2; + newLines.push( ); @@ -55,7 +73,7 @@ export default function BracketView({ matches, onMatchClick }: BracketViewProps) setLines(newLines); }; - const t = setTimeout(draw, 100); + const t = setTimeout(draw, 50); window.addEventListener('resize', draw); const handlePrint = () => { draw(); setTimeout(draw, 100); }; @@ -71,7 +89,7 @@ export default function BracketView({ matches, onMatchClick }: BracketViewProps) window.removeEventListener('beforeprint', handlePrint); window.removeEventListener('afterprint', draw); }; - }, [matches]); + }, [matches, zoom]); const renderRound = (list: MatchData[]) => { const rounds: Record = {}; @@ -109,44 +127,81 @@ export default function BracketView({ matches, onMatchClick }: BracketViewProps) } return ( -
- +
-
- - {lines} - + {/* FLOATING ZOOM CONTROLS (Moved to top-6 to completely avoid theme button) */} +
+ + +
-
-
-
Winners Bracket
-
{renderRound(displayWb)}
-
- {isDoubleElim && ( -
-
Losers Bracket
-
{renderRound(lb)}
-
- )} -
+
+ -
- {displayFinals.length > 0 && ( -
-
- Championship + {/* SIZER WRAPPER (Dynamically scales width/height to fix the scrollbar ghost space) */} +
+ {/* SCALING WRAPPER */} +
+ {/* Added pt-16 md:pt-20 to ensure absolute titles aren't clipped + */} +
+ + {lines} + + +
+
+
Winners Bracket
+
{renderRound(displayWb)}
+
+ {isDoubleElim && ( +
+
Losers Bracket
+
{renderRound(lb)}
+
+ )} +
+ +
+ {displayFinals.length > 0 && ( +
+
+ Championship +
+ {displayFinals.map(m => )} +
+ )} +
+
+
- {displayFinals.map(m => )}
- )} -
-
- +