diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 5d41b2c..4853bf2 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -33,12 +33,15 @@ const apiEndpoints = { searchPlayers: (name) => `/api/tss/players/search?q=${encodeURIComponent(name)}&limit=10`, } -const navItems = [ +const primaryNavItems = [ { path: '/', label: 'Home' }, - { path: '/teams', label: 'Team Leaderboard' }, - { path: '/players', label: 'Player Leaderboard' }, - { path: '/battle-logs', label: 'Battle Logs' }, + { path: '/teams', label: 'Team' }, + { path: '/players', label: 'Players' }, + { path: '/battle-logs', label: 'Battles' }, { path: '/tournaments', label: 'Tournaments' }, +] + +const utilityNavItems = [ { path: '/viewers', label: 'Viewers' }, { path: '/docs', label: 'Setup' }, ] @@ -883,7 +886,7 @@ function themeToggleDockPosition(navPill) { const top = 16 return { - x: Math.round(left + width - 48), + x: Math.round(left + width - 42), y: Math.round(top + (height - 36) / 2), } } @@ -1880,26 +1883,44 @@ function AppContent() { }`} >
-
-
+
-
+
@@ -3934,6 +3956,8 @@ function BracketViewport({ children }) { start.moved = true bracketPan.dragged = true el.classList.add('is-grabbing') + // Drop any text selection the initial press may have started. + window.getSelection()?.removeAllRanges() } if (start.moved) { el.scrollLeft = start.sl - dx @@ -3979,6 +4003,7 @@ function TournamentBracketSide({ side, navigate, highlight, onHover }) { // `tops` places each match; once placed we read back the DOM to draw connectors. const [layout, setLayout] = useState({ tops: new Map(), height: 0 }) const [connectors, setConnectors] = useState({ width: 0, height: 0, lines: [], byes: [] }) + const [collapsed, setCollapsed] = useState(false) // Pass 1: measure card heights, then position every match centred on its feeders. useLayoutEffect(() => { @@ -3994,7 +4019,7 @@ function TournamentBracketSide({ side, navigate, highlight, onHover }) { const observer = new ResizeObserver(relayout) observer.observe(grid) return () => observer.disconnect() - }, [side]) + }, [side, collapsed]) // Pass 2: with matches positioned, read real edges and draw elbow connectors. // Each connector carries the teams on either end so a hovered team's whole run @@ -4040,13 +4065,22 @@ function TournamentBracketSide({ side, navigate, highlight, onHover }) { } } setConnectors({ width: grid.scrollWidth, height: grid.scrollHeight, lines, byes }) - }, [side, layout]) + }, [side, layout, collapsed]) const active = Boolean(highlight) return ( -
-

{side.label}

+
+ + {collapsed ? null : (
+ )}
) } @@ -4263,7 +4298,7 @@ function TournamentDetailPage({ tournamentId, navigate }) { ) : null} {bracketSides.length ? ( -
+
{hasStandings || listSides.length ? (

Playoffs

) : null} diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 00d4897..71d013e 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -834,20 +834,43 @@ h3 { /* The bracket reads as a campaign map you drag around. The viewport breaks the page's max-width so there's room to manoeuvre; the surface carries a faint tactical grid that pans with the bracket (it lives on the scrolling content). */ +/* The whole side (heading + canvas) breaks out to 85vw and centres on the + viewport, so the WINNER/LOSER heading aligns with the canvas's left edge. */ +.bracket-side { + width: 85vw; + margin-left: calc(50% - 42.5vw); +} + +.bracket-caret { + display: inline-block; + font-size: 0.7em; + transition: transform 0.18s ease; +} + +.bracket-caret.is-collapsed { + transform: rotate(-90deg); +} + .bracket-viewport { position: relative; - width: 100vw; - margin-left: calc(50% - 50vw); + width: 100%; max-height: 78vh; overflow: auto; overscroll-behavior: contain; cursor: grab; - border-block: 1px solid var(--color-border); + border: 1px solid var(--color-border); + border-radius: 0.5rem; background-color: color-mix(in srgb, var(--color-bg) 86%, #000); } .bracket-viewport.is-grabbing { cursor: grabbing; + user-select: none; +} + +/* While panning, don't let the drag turn into a text selection. */ +.bracket-viewport.is-grabbing * { + user-select: none; } .tournament-bracket-grid {