changes
This commit is contained in:
@@ -1,20 +1,32 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useCallback } from "react";
|
||||
import { GroupTable, Match, Team, ThirdPlaceRow } from "@/lib/types";
|
||||
import { ThirdAssignment, orderedRound } from "@/lib/bracket";
|
||||
import { resolveBracket, ResolvedSide, ResolvedTie } from "@/lib/resolve-bracket";
|
||||
import Flag from "./Flag";
|
||||
|
||||
interface TipState { x: number; y: number; text: string }
|
||||
|
||||
function Side({
|
||||
s, prob, teams,
|
||||
}: { s: ResolvedSide; prob?: number | null; teams: Team[] }) {
|
||||
s, prob, teams, onShowTip, onHideTip,
|
||||
}: {
|
||||
s: ResolvedSide; prob?: number | null; teams: Team[];
|
||||
onShowTip: (text: string, x: number, y: number) => void;
|
||||
onHideTip: () => void;
|
||||
}) {
|
||||
const team = s.teamId ? teams.find((t) => t.id === s.teamId) : undefined;
|
||||
// fix = ein echtes Team steht fest (nicht vorläufig). Eigene Klasse für klares Styling.
|
||||
const fix = s.teamId != null && !s.provisional;
|
||||
const hasTip = !!s.tooltip;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`side ${s.isWinner ? "win" : ""} ${s.provisional ? "prov" : ""} ${fix ? "fix" : ""}`}
|
||||
title={s.tooltip ?? undefined}
|
||||
{...(hasTip ? {
|
||||
onMouseEnter: (e: React.MouseEvent<HTMLDivElement>) => onShowTip(s.tooltip!, e.clientX, e.clientY),
|
||||
onMouseMove: (e: React.MouseEvent<HTMLDivElement>) => onShowTip(s.tooltip!, e.clientX, e.clientY),
|
||||
onMouseLeave: onHideTip,
|
||||
} : {})}
|
||||
>
|
||||
<span className="nm">
|
||||
{s.teamId ? (
|
||||
@@ -35,12 +47,18 @@ function Side({
|
||||
);
|
||||
}
|
||||
|
||||
function Tie({ tie, teams, isFinal }: { tie: ResolvedTie; teams: Team[]; isFinal?: boolean }) {
|
||||
function Tie({
|
||||
tie, teams, isFinal, onShowTip, onHideTip,
|
||||
}: {
|
||||
tie: ResolvedTie; teams: Team[]; isFinal?: boolean;
|
||||
onShowTip: (text: string, x: number, y: number) => void;
|
||||
onHideTip: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className={`tie ${isFinal ? "final-tie" : ""}`}>
|
||||
<div className="tie-head">Spiel {tie.matchNumber}</div>
|
||||
<Side s={tie.home} prob={tie.prob?.home} teams={teams} />
|
||||
<Side s={tie.away} prob={tie.prob?.away} teams={teams} />
|
||||
<Side s={tie.home} prob={tie.prob?.home} teams={teams} onShowTip={onShowTip} onHideTip={onHideTip} />
|
||||
<Side s={tie.away} prob={tie.prob?.away} teams={teams} onShowTip={onShowTip} onHideTip={onHideTip} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -57,8 +75,6 @@ export default function Bracket({
|
||||
|
||||
const r32ByNum = new Map(r32Array.map((t) => [t.matchNumber, t]));
|
||||
|
||||
// Korrekte Baum-Reihenfolge: von der Wurzel (Finale) rekursiv die Quellen
|
||||
// jeder Runde ermitteln, damit die Spalten vertikal ausgerichtet sind.
|
||||
const sfOrder = orderedRound([104]);
|
||||
const qfOrder = orderedRound(sfOrder);
|
||||
const r16Order = orderedRound(qfOrder);
|
||||
@@ -71,6 +87,10 @@ export default function Bracket({
|
||||
const fin = later[104];
|
||||
const third = later[103];
|
||||
|
||||
const [tip, setTip] = useState<TipState | null>(null);
|
||||
const showTip = useCallback((text: string, x: number, y: number) => setTip({ x, y, text }), []);
|
||||
const hideTip = useCallback(() => setTip(null), []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="bracket-banner">
|
||||
@@ -87,35 +107,35 @@ export default function Bracket({
|
||||
<div className="round">
|
||||
<div className="round-label">Letzte 32</div>
|
||||
<div className="round-matches">
|
||||
{r32.map((t) => <Tie key={t.matchNumber} tie={t} teams={teams} />)}
|
||||
{r32.map((t) => <Tie key={t.matchNumber} tie={t} teams={teams} onShowTip={showTip} onHideTip={hideTip} />)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="round">
|
||||
<div className="round-label">Achtelfinale</div>
|
||||
<div className="round-matches">
|
||||
{r16.map((t) => <Tie key={t.matchNumber} tie={t} teams={teams} />)}
|
||||
{r16.map((t) => <Tie key={t.matchNumber} tie={t} teams={teams} onShowTip={showTip} onHideTip={hideTip} />)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="round">
|
||||
<div className="round-label">Viertelfinale</div>
|
||||
<div className="round-matches">
|
||||
{qf.map((t) => <Tie key={t.matchNumber} tie={t} teams={teams} />)}
|
||||
{qf.map((t) => <Tie key={t.matchNumber} tie={t} teams={teams} onShowTip={showTip} onHideTip={hideTip} />)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="round">
|
||||
<div className="round-label">Halbfinale</div>
|
||||
<div className="round-matches">
|
||||
{sf.map((t) => <Tie key={t.matchNumber} tie={t} teams={teams} />)}
|
||||
{sf.map((t) => <Tie key={t.matchNumber} tie={t} teams={teams} onShowTip={showTip} onHideTip={hideTip} />)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="round">
|
||||
<div className="round-label">Finale</div>
|
||||
<div className="round-matches">
|
||||
{fin && <Tie tie={fin} teams={teams} isFinal />}
|
||||
{fin && <Tie tie={fin} teams={teams} isFinal onShowTip={showTip} onHideTip={hideTip} />}
|
||||
{third && (
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<div className="round-label" style={{ marginBottom: 8 }}>Spiel um Platz 3</div>
|
||||
<Tie tie={third} teams={teams} />
|
||||
<Tie tie={third} teams={teams} onShowTip={showTip} onHideTip={hideTip} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -131,6 +151,12 @@ export default function Bracket({
|
||||
<span><i style={{ background: "var(--ink-faint)" }} />Platzhalter offen</span>
|
||||
<span>%-Werte: Polymarket-Wahrscheinlichkeit (falls verfügbar)</span>
|
||||
</div>
|
||||
|
||||
{tip && (
|
||||
<div className="kobaum-tip" style={{ left: tip.x + 12, top: tip.y + 12 }}>
|
||||
{tip.text}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,9 +190,13 @@ table.standings { width: 100%; border-collapse: collapse; }
|
||||
}
|
||||
.bracket-banner .v { font-family: var(--font-mono); font-size: 12px; color: var(--turf); }
|
||||
.bracket-scroll {
|
||||
max-height: calc(100vh - 260px);
|
||||
overflow: auto;
|
||||
padding-bottom: 8px;
|
||||
margin-left: -20px;
|
||||
margin-right: -20px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: visible;
|
||||
padding-bottom: 10px;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
.bracket {
|
||||
@@ -236,6 +240,22 @@ table.standings { width: 100%; border-collapse: collapse; }
|
||||
.legend span { display: inline-flex; align-items: center; gap: 6px; }
|
||||
.legend i { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }
|
||||
|
||||
/* Tooltip im K.o.-Baum (position: fixed, damit overflow-x den Tooltip nicht abschneidet) */
|
||||
.kobaum-tip {
|
||||
position: fixed;
|
||||
z-index: 200;
|
||||
pointer-events: none;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--line);
|
||||
color: var(--ink);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
padding: 6px 9px;
|
||||
border-radius: var(--radius-sm);
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 6px 20px rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
/* ---------- Zustände ---------- */
|
||||
.notice {
|
||||
background: var(--bg-card); border: 1px solid var(--line);
|
||||
|
||||
Reference in New Issue
Block a user