updated bracket
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Flex } from "@mantine/core";
|
||||
import { Flex, Box } from "@mantine/core";
|
||||
import { Match } from "@/features/matches/types";
|
||||
import { MatchCard } from "./match-card";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
interface BracketProps {
|
||||
rounds: Match[][];
|
||||
@@ -13,33 +14,131 @@ export const Bracket: React.FC<BracketProps> = ({
|
||||
orders,
|
||||
showControls,
|
||||
}) => {
|
||||
return (
|
||||
<Flex direction="row" gap={24} justify="left">
|
||||
{rounds.map((round, roundIndex) => (
|
||||
<Flex
|
||||
key={roundIndex}
|
||||
direction="column"
|
||||
align="center"
|
||||
pos="relative"
|
||||
gap={24}
|
||||
justify="space-around"
|
||||
p={24}
|
||||
>
|
||||
{round.map((match) =>
|
||||
match.bye ? (
|
||||
<div key={match.lid}></div>
|
||||
) : (
|
||||
<div key={match.lid}>
|
||||
<MatchCard
|
||||
match={match}
|
||||
orders={orders}
|
||||
showControls={showControls}
|
||||
/>
|
||||
</div>
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const svgRef = useRef<SVGSVGElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const updateConnectorLines = () => {
|
||||
if (!containerRef.current || !svgRef.current) return;
|
||||
|
||||
const svg = svgRef.current;
|
||||
const container = containerRef.current;
|
||||
const flexContainer = container.querySelector('.bracket-flex-container') as HTMLElement;
|
||||
if (!flexContainer) return;
|
||||
|
||||
svg.innerHTML = '';
|
||||
|
||||
const flexRect = flexContainer.getBoundingClientRect();
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
|
||||
svg.style.width = `${flexContainer.scrollWidth}px`;
|
||||
svg.style.height = `${flexContainer.scrollHeight}px`;
|
||||
|
||||
rounds.forEach((round, roundIndex) => {
|
||||
if (roundIndex === rounds.length - 1) return;
|
||||
|
||||
const nextRound = rounds[roundIndex + 1];
|
||||
|
||||
round.forEach((match, matchIndex) => {
|
||||
if (match.bye) return;
|
||||
|
||||
const matchElement = container.querySelector(`[data-match-lid="${match.lid}"]`) as HTMLElement;
|
||||
if (!matchElement) return;
|
||||
|
||||
const nextMatches = nextRound.filter(nextMatch =>
|
||||
!nextMatch.bye && (
|
||||
orders[nextMatch.home_from_lid] === match.order ||
|
||||
orders[nextMatch.away_from_lid] === match.order
|
||||
)
|
||||
)}
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
);
|
||||
|
||||
nextMatches.forEach(nextMatch => {
|
||||
const nextMatchElement = container.querySelector(`[data-match-lid="${nextMatch.lid}"]`) as HTMLElement;
|
||||
if (!nextMatchElement) return;
|
||||
|
||||
const matchRect = matchElement.getBoundingClientRect();
|
||||
const nextMatchRect = nextMatchElement.getBoundingClientRect();
|
||||
|
||||
const startX = matchRect.right - flexRect.left;
|
||||
const startY = matchRect.top + matchRect.height / 2 - flexRect.top;
|
||||
const endX = nextMatchRect.left - flexRect.left;
|
||||
const endY = nextMatchRect.top + nextMatchRect.height / 2 - flexRect.top;
|
||||
|
||||
const midX = startX + (endX - startX) * 0.5;
|
||||
|
||||
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
const pathData = `M ${startX} ${startY} L ${midX} ${startY} L ${midX} ${endY} L ${endX} ${endY}`;
|
||||
|
||||
path.setAttribute('d', pathData);
|
||||
path.setAttribute('stroke', 'var(--mantine-color-default-border)');
|
||||
path.setAttribute('stroke-width', '2');
|
||||
path.setAttribute('fill', 'none');
|
||||
path.setAttribute('stroke-linecap', 'round');
|
||||
path.setAttribute('stroke-linejoin', 'round');
|
||||
|
||||
svg.appendChild(path);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
updateConnectorLines();
|
||||
|
||||
const handleUpdate = () => {
|
||||
requestAnimationFrame(updateConnectorLines);
|
||||
};
|
||||
|
||||
const scrollContainer = containerRef.current?.closest('.mantine-ScrollArea-viewport');
|
||||
scrollContainer?.addEventListener('scroll', handleUpdate);
|
||||
window.addEventListener('resize', handleUpdate);
|
||||
|
||||
return () => {
|
||||
scrollContainer?.removeEventListener('scroll', handleUpdate);
|
||||
window.removeEventListener('resize', handleUpdate);
|
||||
};
|
||||
}, [rounds, orders]);
|
||||
|
||||
return (
|
||||
<Box pos="relative" ref={containerRef}>
|
||||
<svg
|
||||
ref={svgRef}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
pointerEvents: 'none',
|
||||
zIndex: 0,
|
||||
}}
|
||||
/>
|
||||
<Flex direction="row" gap={24} justify="left" pos="relative" style={{ zIndex: 1 }} className="bracket-flex-container">
|
||||
{rounds.map((round, roundIndex) => (
|
||||
<Flex
|
||||
key={roundIndex}
|
||||
direction="column"
|
||||
align="center"
|
||||
pos="relative"
|
||||
gap={24}
|
||||
justify="space-around"
|
||||
p={24}
|
||||
>
|
||||
{round.map((match) =>
|
||||
match.bye ? (
|
||||
<div key={match.lid}></div>
|
||||
) : (
|
||||
<div key={match.lid}>
|
||||
<MatchCard
|
||||
match={match}
|
||||
orders={orders}
|
||||
showControls={showControls}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user