This commit is contained in:
FURRO404
2026-06-21 02:53:20 -07:00
parent 52a8cd2023
commit 993b9e4e2f
2 changed files with 41 additions and 6 deletions
+17 -4
View File
@@ -3934,6 +3934,8 @@ function BracketViewport({ children }) {
start.moved = true start.moved = true
bracketPan.dragged = true bracketPan.dragged = true
el.classList.add('is-grabbing') el.classList.add('is-grabbing')
// Drop any text selection the initial press may have started.
window.getSelection()?.removeAllRanges()
} }
if (start.moved) { if (start.moved) {
el.scrollLeft = start.sl - dx el.scrollLeft = start.sl - dx
@@ -3979,6 +3981,7 @@ function TournamentBracketSide({ side, navigate, highlight, onHover }) {
// `tops` places each match; once placed we read back the DOM to draw connectors. // `tops` places each match; once placed we read back the DOM to draw connectors.
const [layout, setLayout] = useState({ tops: new Map(), height: 0 }) const [layout, setLayout] = useState({ tops: new Map(), height: 0 })
const [connectors, setConnectors] = useState({ width: 0, height: 0, lines: [], byes: [] }) 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. // Pass 1: measure card heights, then position every match centred on its feeders.
useLayoutEffect(() => { useLayoutEffect(() => {
@@ -3994,7 +3997,7 @@ function TournamentBracketSide({ side, navigate, highlight, onHover }) {
const observer = new ResizeObserver(relayout) const observer = new ResizeObserver(relayout)
observer.observe(grid) observer.observe(grid)
return () => observer.disconnect() return () => observer.disconnect()
}, [side]) }, [side, collapsed])
// Pass 2: with matches positioned, read real edges and draw elbow connectors. // 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 // Each connector carries the teams on either end so a hovered team's whole run
@@ -4040,13 +4043,22 @@ function TournamentBracketSide({ side, navigate, highlight, onHover }) {
} }
} }
setConnectors({ width: grid.scrollWidth, height: grid.scrollHeight, lines, byes }) setConnectors({ width: grid.scrollWidth, height: grid.scrollHeight, lines, byes })
}, [side, layout]) }, [side, layout, collapsed])
const active = Boolean(highlight) const active = Boolean(highlight)
return ( return (
<div> <div className="bracket-side">
<h3 className="mb-3 text-sm font-semibold uppercase tracking-wide text-fury-violet">{side.label}</h3> <button
aria-expanded={!collapsed}
className="mb-3 flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-fury-violet transition hover:text-text"
onClick={() => setCollapsed((value) => !value)}
type="button"
>
<span aria-hidden="true" className={`bracket-caret${collapsed ? ' is-collapsed' : ''}`}></span>
{side.label}
</button>
{collapsed ? null : (
<BracketViewport> <BracketViewport>
<div className={`tournament-bracket-grid${active ? ' has-trace' : ''}`} ref={gridRef}> <div className={`tournament-bracket-grid${active ? ' has-trace' : ''}`} ref={gridRef}>
<svg <svg
@@ -4101,6 +4113,7 @@ function TournamentBracketSide({ side, navigate, highlight, onHover }) {
))} ))}
</div> </div>
</BracketViewport> </BracketViewport>
)}
</div> </div>
) )
} }
+24 -2
View File
@@ -834,10 +834,26 @@ h3 {
/* The bracket reads as a campaign map you drag around. The viewport breaks the /* 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 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). */ tactical grid that pans with the bracket (it lives on the scrolling content). */
.bracket-viewport { /* The whole side (heading + canvas) breaks out to 85vw and centres on the
position: relative; viewport, so the WINNER/LOSER heading aligns with the canvas's left edge. */
.bracket-side {
width: 85vw; width: 85vw;
margin-left: calc(50% - 42.5vw); 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: 100%;
max-height: 78vh; max-height: 78vh;
overflow: auto; overflow: auto;
overscroll-behavior: contain; overscroll-behavior: contain;
@@ -849,6 +865,12 @@ h3 {
.bracket-viewport.is-grabbing { .bracket-viewport.is-grabbing {
cursor: 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 { .tournament-bracket-grid {