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 { Match } from "@/features/matches/types";
|
||||||
import { MatchCard } from "./match-card";
|
import { MatchCard } from "./match-card";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
interface BracketProps {
|
interface BracketProps {
|
||||||
rounds: Match[][];
|
rounds: Match[][];
|
||||||
@@ -13,33 +14,131 @@ export const Bracket: React.FC<BracketProps> = ({
|
|||||||
orders,
|
orders,
|
||||||
showControls,
|
showControls,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
<Flex direction="row" gap={24} justify="left">
|
const svgRef = useRef<SVGSVGElement>(null);
|
||||||
{rounds.map((round, roundIndex) => (
|
|
||||||
<Flex
|
useEffect(() => {
|
||||||
key={roundIndex}
|
const updateConnectorLines = () => {
|
||||||
direction="column"
|
if (!containerRef.current || !svgRef.current) return;
|
||||||
align="center"
|
|
||||||
pos="relative"
|
const svg = svgRef.current;
|
||||||
gap={24}
|
const container = containerRef.current;
|
||||||
justify="space-around"
|
const flexContainer = container.querySelector('.bracket-flex-container') as HTMLElement;
|
||||||
p={24}
|
if (!flexContainer) return;
|
||||||
>
|
|
||||||
{round.map((match) =>
|
svg.innerHTML = '';
|
||||||
match.bye ? (
|
|
||||||
<div key={match.lid}></div>
|
const flexRect = flexContainer.getBoundingClientRect();
|
||||||
) : (
|
const containerRect = container.getBoundingClientRect();
|
||||||
<div key={match.lid}>
|
|
||||||
<MatchCard
|
svg.style.width = `${flexContainer.scrollWidth}px`;
|
||||||
match={match}
|
svg.style.height = `${flexContainer.scrollHeight}px`;
|
||||||
orders={orders}
|
|
||||||
showControls={showControls}
|
rounds.forEach((round, roundIndex) => {
|
||||||
/>
|
if (roundIndex === rounds.length - 1) return;
|
||||||
</div>
|
|
||||||
|
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>
|
|
||||||
))}
|
nextMatches.forEach(nextMatch => {
|
||||||
</Flex>
|
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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -199,7 +199,15 @@ export const MatchCard: React.FC<MatchCardProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="row" align="center" justify="end" gap={8}>
|
<Flex direction="row" align="center" justify="end" gap={8}>
|
||||||
<Text c="dimmed" fw="bolder">
|
<Text
|
||||||
|
c="dimmed"
|
||||||
|
fw="bolder"
|
||||||
|
px={6}
|
||||||
|
py={2}
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--mantine-color-body)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
{match.order}
|
{match.order}
|
||||||
</Text>
|
</Text>
|
||||||
<Flex align="stretch">
|
<Flex align="stretch">
|
||||||
@@ -214,7 +222,12 @@ export const MatchCard: React.FC<MatchCardProps> = ({
|
|||||||
w={showToolbar || showEditButton ? 200 : 220}
|
w={showToolbar || showEditButton ? 200 : 220}
|
||||||
withBorder
|
withBorder
|
||||||
pos="relative"
|
pos="relative"
|
||||||
style={{ overflow: "visible" }}
|
style={{
|
||||||
|
overflow: "visible",
|
||||||
|
backgroundColor: 'var(--mantine-color-body)',
|
||||||
|
borderColor: 'var(--mantine-color-default-border)',
|
||||||
|
boxShadow: 'var(--mantine-shadow-sm)',
|
||||||
|
}}
|
||||||
data-match-lid={match.lid}
|
data-match-lid={match.lid}
|
||||||
>
|
>
|
||||||
<Card.Section withBorder p={0}>
|
<Card.Section withBorder p={0}>
|
||||||
|
|||||||
@@ -21,9 +21,16 @@ export const MatchSlot: React.FC<MatchSlotProps> = ({
|
|||||||
cups,
|
cups,
|
||||||
isWinner
|
isWinner
|
||||||
}) => (
|
}) => (
|
||||||
<Flex align="stretch">
|
<Flex
|
||||||
|
align="stretch"
|
||||||
|
style={{
|
||||||
|
backgroundColor: isWinner ? 'var(--mantine-color-green-light)' : 'transparent',
|
||||||
|
borderRadius: 'var(--mantine-radius-sm)',
|
||||||
|
transition: 'background-color 200ms ease',
|
||||||
|
}}
|
||||||
|
>
|
||||||
{(seed && seed > 0) ? <SeedBadge seed={seed} /> : undefined}
|
{(seed && seed > 0) ? <SeedBadge seed={seed} /> : undefined}
|
||||||
<Flex p="4px 8px" w='100%' align="center">
|
<Flex p="6px 10px" w='100%' align="center">
|
||||||
<Flex align="center" gap={4} flex={1}>
|
<Flex align="center" gap={4} flex={1}>
|
||||||
{team ? (
|
{team ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ export const SeedBadge: React.FC<SeedBadgeProps> = ({ seed }) => {
|
|||||||
color: "var(--mantine-color-text)",
|
color: "var(--mantine-color-text)",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center"
|
||||||
borderTopLeftRadius: "var(--mantine-radius-default)",
|
|
||||||
borderBottomLeftRadius: "var(--mantine-radius-default)",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{seed}
|
{seed}
|
||||||
|
|||||||
Reference in New Issue
Block a user