styling
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { Text, ScrollArea } from "@mantine/core";
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Text, ScrollArea, Box } from "@mantine/core";
|
||||
import { BracketData } from "../types";
|
||||
import { Bracket } from "./bracket";
|
||||
import MatchDock from "./match-dock";
|
||||
import useAppShellHeight from "@/hooks/use-appshell-height";
|
||||
import { Match } from "@/features/matches/types";
|
||||
import styles from "./styles.module.css";
|
||||
@@ -17,6 +18,8 @@ interface BracketViewProps {
|
||||
|
||||
const BracketView: React.FC<BracketViewProps> = ({ bracket, showControls, groupConfig }) => {
|
||||
const height = useAppShellHeight();
|
||||
const viewportRef = useRef<HTMLDivElement>(null);
|
||||
const hasAutoScrolled = useRef(false);
|
||||
const orders = useMemo(() => {
|
||||
const map: Record<number, number> = {};
|
||||
bracket.winners.flat().forEach(match => map[match.lid] = match.order);
|
||||
@@ -24,7 +27,60 @@ const BracketView: React.FC<BracketViewProps> = ({ bracket, showControls, groupC
|
||||
return map;
|
||||
}, [bracket.winners, bracket.losers]);
|
||||
|
||||
return <ScrollArea
|
||||
const [selectedLid, setSelectedLid] = useState<number | null>(null);
|
||||
const handleMatchTap = useCallback((match: Match) => {
|
||||
setSelectedLid((prev) => (prev === match.lid ? null : match.lid));
|
||||
}, []);
|
||||
const closeDock = useCallback(() => setSelectedLid(null), []);
|
||||
|
||||
const selectedMatch = useMemo(() => {
|
||||
if (selectedLid == null) return null;
|
||||
return (
|
||||
[...bracket.winners.flat(), ...bracket.losers.flat()].find(
|
||||
(match) => match.lid === selectedLid
|
||||
) ?? null
|
||||
);
|
||||
}, [bracket, selectedLid]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasAutoScrolled.current) return;
|
||||
|
||||
const matches = [...bracket.winners.flat(), ...bracket.losers.flat()].filter(
|
||||
(match) => !match.bye
|
||||
);
|
||||
const target =
|
||||
matches.find((match) => match.status === "started") ??
|
||||
matches.find((match) => match.status === "ready");
|
||||
if (!target) return;
|
||||
|
||||
const viewport = viewportRef.current;
|
||||
const element = viewport?.querySelector(`[data-match-lid="${target.lid}"]`);
|
||||
if (!viewport || !element) return;
|
||||
|
||||
hasAutoScrolled.current = true;
|
||||
|
||||
const viewportRect = viewport.getBoundingClientRect();
|
||||
const elementRect = element.getBoundingClientRect();
|
||||
const left =
|
||||
elementRect.left - viewportRect.left + viewport.scrollLeft - 40;
|
||||
const top =
|
||||
elementRect.top -
|
||||
viewportRect.top +
|
||||
viewport.scrollTop -
|
||||
(viewport.clientHeight - elementRect.height) / 2;
|
||||
|
||||
viewport.scrollTo({
|
||||
left: Math.max(0, left),
|
||||
top: Math.max(0, top),
|
||||
behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
||||
? "auto"
|
||||
: "smooth",
|
||||
});
|
||||
}, [bracket]);
|
||||
|
||||
return <Box pos="relative">
|
||||
<ScrollArea
|
||||
viewportRef={viewportRef}
|
||||
h={`calc(${height})`}
|
||||
className={styles["bracket-container"]}
|
||||
style={{
|
||||
@@ -37,17 +93,19 @@ const BracketView: React.FC<BracketViewProps> = ({ bracket, showControls, groupC
|
||||
<Text fw={600} size="md" m={16}>
|
||||
Winners Bracket
|
||||
</Text>
|
||||
<Bracket rounds={bracket.winners} orders={orders} showControls={showControls} groupConfig={groupConfig} />
|
||||
<Bracket rounds={bracket.winners} orders={orders} showControls={showControls} groupConfig={groupConfig} onMatchTap={handleMatchTap} selectedMatchLid={selectedLid} />
|
||||
</div>
|
||||
{bracket.losers && bracket.losers.length > 0 && bracket.losers.some(round => round.length > 0) && (
|
||||
<div>
|
||||
<Text fw={600} size="md" m={16}>
|
||||
Losers Bracket
|
||||
</Text>
|
||||
<Bracket rounds={bracket.losers} orders={orders} showControls={showControls} groupConfig={groupConfig} />
|
||||
<Bracket rounds={bracket.losers} orders={orders} showControls={showControls} groupConfig={groupConfig} onMatchTap={handleMatchTap} selectedMatchLid={selectedLid} />
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
<MatchDock match={selectedMatch} onClose={closeDock} />
|
||||
</Box>
|
||||
};
|
||||
|
||||
export default BracketView;
|
||||
|
||||
Reference in New Issue
Block a user