From 0c659ec6af74d2d8667fe73c122967dcc220d85f Mon Sep 17 00:00:00 2001 From: "Kalle (Sendou)" <38327916+Sendouc@users.noreply.github.com> Date: Thu, 23 Dec 2021 08:47:09 +0200 Subject: [PATCH] Import component --- README.md | 2 +- .../tournament/EliminationBracket.tsx | 145 ++++++++++++++++++ app/hooks/useTournamentRounds/index.ts | 1 + .../$organization.$tournament/bracket.$id.tsx | 15 ++ app/styles/tournament-bracket.css | 120 +++++++++++++++ 5 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 app/components/tournament/EliminationBracket.tsx create mode 100644 app/routes/to/$organization.$tournament/bracket.$id.tsx create mode 100644 app/styles/tournament-bracket.css diff --git a/README.md b/README.md index 2791aeb3b..66fb19b90 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ You can give a variation as a flag to the seeding script changing what exactly i - [ ] Admin can change team captain - [x] Admin can randomize and rerandomize maps - [x] Admin can change seeding -- [ ] Admin can start the tournament +- [x] Admin can start the tournament - [ ] Table design to support arbitrary brackets - [x] Generate SE bracket from seeds - [x] Generate DE bracket from seeds diff --git a/app/components/tournament/EliminationBracket.tsx b/app/components/tournament/EliminationBracket.tsx new file mode 100644 index 000000000..ee76a18c4 --- /dev/null +++ b/app/components/tournament/EliminationBracket.tsx @@ -0,0 +1,145 @@ +import classNames from "classnames"; + +const rounds = [ + { + title: "Round 1", + bestOf: 3, + status: "DONE", + }, + { + title: "Round 2", + bestOf: 5, + status: "INPROGRESS", + }, + { + title: "Semifinals", + bestOf: 5, + status: "UPCOMING", + }, + { + title: "Finals", + bestOf: 7, + status: "UPCOMING", + }, +] as const; + +export function EliminationBracket() { + return ( +
+
+ {rounds.map((round, i) => ( + + ))} +
+
+ + + + +
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+ ); +} + +function RoundInfo(props: { + round: { + title: string; + bestOf: number; + status: "DONE" | "INPROGRESS" | "UPCOMING"; + }; + isLast?: boolean; +}) { + return ( +
+
+ {props.round.title} +
+ {props.round.status !== "DONE" && ( +
+ Bo{props.round.bestOf} +
+ )} +
+ ); +} + +export function Match() { + return ( +
+
1
+
+ Team Olive 1 +
+
+ Chimera 1 +
+
+ ); +} diff --git a/app/hooks/useTournamentRounds/index.ts b/app/hooks/useTournamentRounds/index.ts index 2947245fd..ad0e0ca67 100644 --- a/app/hooks/useTournamentRounds/index.ts +++ b/app/hooks/useTournamentRounds/index.ts @@ -8,6 +8,7 @@ import type { UseTournamentRoundsState, } from "./types"; +// TODO: could save this to local storage but need to handle amount of rounds changing export function useTournamentRounds(args: UseTournamentRoundsArgs) { return React.useReducer( (oldState: UseTournamentRoundsState, action: UseTournamentRoundsAction) => { diff --git a/app/routes/to/$organization.$tournament/bracket.$id.tsx b/app/routes/to/$organization.$tournament/bracket.$id.tsx new file mode 100644 index 000000000..5bd4580f3 --- /dev/null +++ b/app/routes/to/$organization.$tournament/bracket.$id.tsx @@ -0,0 +1,15 @@ +import type { LinksFunction } from "remix"; +import { EliminationBracket } from "~/components/tournament/EliminationBracket"; +import styles from "~/styles/tournament-bracket.css"; + +export const links: LinksFunction = () => { + return [{ rel: "stylesheet", href: styles }]; +}; + +export default function BracketTabWrapper() { + return ( +
+ +
+ ); +} diff --git a/app/styles/tournament-bracket.css b/app/styles/tournament-bracket.css new file mode 100644 index 000000000..adf64726d --- /dev/null +++ b/app/styles/tournament-bracket.css @@ -0,0 +1,120 @@ +.tournament-bracket__container { + width: 100%; +} + +.tournament-bracket__elim__container { + display: flex; + width: 100%; + justify-content: center; + margin: 0 auto; +} + +.tournament-bracket__elim__bracket { + display: grid; + column-gap: 2px; + grid-template-columns: repeat(var(--brackets-columns), 250px); + overflow-x: auto; + + --brackets-match-height: 100px; +} + +.tournament-bracket__elim__roundInfo { + padding: 1.5rem 1rem; + background-color: var(--bg-lighter); + + /* TODO: transition: background-color 0.5s; */ +} + +.tournament-bracket__elim__roundInfo:first-of-type { + border-radius: var(--rounded) 0 0 var(--rounded); +} + +.tournament-bracket__elim__roundInfo.highlighted { + background-color: var(--theme); + color: var(--button-text); +} + +.tournament-bracket__elim__roundInfo.last { + border-radius: 0 var(--rounded) var(--rounded) 0; +} + +.tournament-bracket__elim__roundTitle { + font-size: var(--fonts-sm); + font-weight: 500; +} + +.tournament-bracket__elim__bestOf { + font-size: var(--fonts-xs); +} + +.tournament-bracket__elim__match { + display: grid; + margin: 1rem 0; + column-gap: 5px; + grid-template-areas: + "round-number team-one" + "round-number team-two"; + grid-template-columns: 10px 1fr; + place-items: center; + row-gap: 2px; +} + +.tournament-bracket__elim__roundNumber { + font-size: var(--fonts-xs); + grid-area: round-number; +} + +.tournament-bracket__elim__team { + display: flex; + width: 200px; + justify-content: space-between; + padding: 0.25rem 0.75rem; + background-color: var(--bg-lighter); + font-size: var(--fonts-sm); + place-self: flex-start; +} + +.tournament-bracket__elim__teamOne { + border-radius: var(--rounded) var(--rounded) 0 0; + grid-area: team-one; +} + +.tournament-bracket__elim__teamTwo { + border-radius: 0 0 var(--rounded) var(--rounded); + grid-area: team-two; +} + +.tournament-bracket__elim__score { + font-weight: bold; +} + +.tournament-bracket__elim__column { + display: flex; +} + +.tournament-bracket__elim__matches { + display: flex; + height: calc(var(--brackets-match-height) * var(--brackets-matches)); + flex-direction: column; + justify-content: space-around; +} + +.tournament-bracket__elim__lines { + display: flex; + flex-direction: column; + justify-content: space-around; + padding-left: 7px; +} + +.tournament-bracket__elim__lines > div { + width: calc(var(--brackets-match-height) / 4); + height: calc( + 2px + var(--brackets-match-height) * + (var(--brackets-matches) / var(--brackets-column-matches)) + ); + border-top: 2px solid var(--theme-transparent); + border-bottom: var(--brackets-bottom-border-length, 2px) solid + var(--theme-transparent); + border-inline-end: 2px solid var(--theme-transparent); + border-radius: 0 var(--rounded) var(--rounded) 0; +}