Skip to content

Commit

Permalink
docs: Added tournament bracket example
Browse files Browse the repository at this point in the history
  • Loading branch information
siarheihuzarevich committed Oct 11, 2024
1 parent fd1465d commit a934e8c
Show file tree
Hide file tree
Showing 23 changed files with 507 additions and 18 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"@cypress/angular": "^2.1.0",
"@foblex/2d": "1.1.0",
"@foblex/drag-toolkit": "1.1.0",
"@foblex/f-docs": "1.4.7",
"@foblex/f-docs": "1.4.9",
"@foblex/mediator": "1.1.0",
"@foblex/platform": "1.0.4",
"@foblex/utils": "1.1.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { IMap } from '@foblex/flow';
import { ITournamentBracketItem } from './i-tournament-bracket-item';

export function calculateMaximumItemsInColumn(columns: IMap<number>, items: ITournamentBracketItem[]): number {
const result: IMap<number> = {};

Object.entries(columns).forEach((value: [ phase: string, columnIndex: number ]) => {
if (!result[ value[ 1 ] ]) {
result[ value[ 1 ] ] = 0;
}
result[ value[ 1 ] ] += items.filter((item) => item.competitionPhase.toLowerCase() === value[ 0 ]).length;
});

return Math.max(...Object.values(result))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ITournamentBracketItem } from './i-tournament-bracket-item';

export function getItemsInPhase(phase: string, items: ITournamentBracketItem[]): ITournamentBracketItem[] {
return items.filter((item) => item.competitionPhase.toLowerCase() === phase.toLowerCase());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { IMap } from '@foblex/flow';

export function getPhasesByColumnIndex(columns: IMap<number>, columnIndex: number): string[] {
return Object.entries(columns).filter((value) => value[ 1 ] === columnIndex).map((value) => value[ 0 ]);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface ITournamentBracketCompetitor {

title: string;

score: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ITournamentBracketCompetitor } from './i-tournament-bracket-competitor';
import { IPoint } from '@foblex/2d';

export interface ITournamentBracketItem {

position?: IPoint;

id: string;

competitionPhase: string;

color: string;

date: Date;

competitors: ITournamentBracketCompetitor[];

nextMatchId: string;
}
36 changes: 36 additions & 0 deletions projects/f-pro-examples/tournament-bracket/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ITournamentBracketItem } from './i-tournament-bracket-item';

export const TOURNAMENT_BRACKET_STORE: ITournamentBracketItem[] = [
{ id: '1', color: 'blue', competitionPhase: 'UB Quarterfinal', date: new Date(), competitors: [{ title: 'Xtreme gaming', score: 0 }, { title: 'Team liquid', score: 2 }], nextMatchId: '5' },
{ id: '2', color: 'blue', competitionPhase: 'UB Quarterfinal', date: new Date(), competitors: [{ title: 'Cloud9', score: 2 }, { title: 'Aurora gaming', score: 0 }], nextMatchId: '5' },
{ id: '3', color: 'blue', competitionPhase: 'UB Quarterfinal', date: new Date(), competitors: [{ title: 'Team Falcons', score: 0 }, { title: 'Tundra Esport', score: 2 }], nextMatchId: '6' },
{ id: '4', color: 'blue', competitionPhase: 'UB Quarterfinal', date: new Date(), competitors: [{ title: 'Nouns', score: 1 }, { title: 'Gaimin Gladiators', score: 2 }], nextMatchId: '6' },

{ id: '5', color: 'yellow', competitionPhase: 'UB Semifinal', date: new Date(), competitors: [{ title: 'Team liquid', score: 2 }, { title: 'Cloud9', score: 0 }], nextMatchId: '7' },
{ id: '6', color: 'yellow', competitionPhase: 'UB Semifinal', date: new Date(), competitors: [{ title: 'Tundra Esport', score: 0 }, { title: 'Gaimin Gladiators', score: 2 }], nextMatchId: '7' },

{ id: '7', color: 'purple', competitionPhase: 'UB Final', date: new Date(), competitors: [{ title: 'Team liquid', score: 2 }, { title: 'Gaimin Gladiators', score: 0 }], nextMatchId: 'grand-final' },


{ id: '8', color: 'red', competitionPhase: 'LB Round 1', date: new Date(), competitors: [{ title: 'Talon', score: 1 }, { title: 'Betboom Team', score: 2 }], nextMatchId: '12' },
{ id: '9', color: 'red', competitionPhase: 'LB Round 1', date: new Date(), competitors: [{ title: '1w Team', score: 2 }, { title: 'Team Zero', score: 0 }], nextMatchId: '13' },
{ id: '10', color: 'red', competitionPhase: 'LB Round 1', date: new Date(), competitors: [{ title: 'Beastcoast', score: 1 }, { title: 'Heroic', score: 2 }], nextMatchId: '14' },
{ id: '11', color: 'red', competitionPhase: 'LB Round 1', date: new Date(), competitors: [{ title: 'Team spirit', score: 2 }, { title: 'Invictus gaming', score: 0 }], nextMatchId: '15' },

{ id: '12', color: 'blue', competitionPhase: 'LB Round 2', date: new Date(), competitors: [{ title: 'Nouns', score: 0 }, { title: 'Betboom Team', score: 2 }], nextMatchId: '16' },
{ id: '13', color: 'blue', competitionPhase: 'LB Round 2', date: new Date(), competitors: [{ title: 'Team Falcons', score: 2 }, { title: '1w Team', score: 0 }], nextMatchId: '16' },
{ id: '14', color: 'blue', competitionPhase: 'LB Round 2', date: new Date(), competitors: [{ title: 'Aurora gaming', score: 2 }, { title: 'Heroic', score: 1 }], nextMatchId: '17' },
{ id: '15', color: 'blue', competitionPhase: 'LB Round 2', date: new Date(), competitors: [{ title: 'Xtreme gaming', score: 2 }, { title: 'Team spirit', score: 1 }], nextMatchId: '17' },

{ id: '16', color: 'yellow', competitionPhase: 'LB Round 3', date: new Date(), competitors: [{ title: 'Betboom Team', score: 0 }, { title: 'Team Falcons', score: 2 }], nextMatchId: '18' },
{ id: '17', color: 'yellow', competitionPhase: 'LB Round 3', date: new Date(), competitors: [{ title: 'Aurora gaming', score: 0 }, { title: 'Xtreme gaming', score: 2 }], nextMatchId: '19' },

{ id: '18', color: 'yellow', competitionPhase: 'LB Round 4', date: new Date(), competitors: [{ title: 'Cloud9', score: 0 }, { title: 'Team Falcons', score: 2 }], nextMatchId: '20' },
{ id: '19', color: 'yellow', competitionPhase: 'LB Round 4', date: new Date(), competitors: [{ title: 'Tundra Esport', score: 2 }, { title: 'Xtreme gaming', score: 0 }], nextMatchId: '20' },

{ id: '20', color: 'purple', competitionPhase: 'LB Round 5', date: new Date(), competitors: [{ title: 'Team Falcons', score: 0 }, { title: 'Tundra Esport', score: 2 }], nextMatchId: '21' },

{ id: '21', color: 'purple', competitionPhase: 'LB Final', date: new Date(), competitors: [{ title: 'Gaimin Gladiators', score: 2 }, { title: 'Tundra Esport', score: 1 }], nextMatchId: 'grand-final' },

{ id: 'grand-final', color: 'green', competitionPhase: 'Grand Final', date: new Date(), competitors: [{ title: 'Team liquid', score: 3 }, { title: 'Gaimin Gladiators', score: 0 }], nextMatchId: '' },
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<f-flow fDraggable
(fLoaded)="onInitialized()">
<f-canvas fZoom>

@for (item of viewModel; track item.id) {
<f-connection [fReassignDisabled]="true"
[fSelectionDisabled]="true"
fBehavior="fixed_center"
fType="segment"
[fOutputId]="item.id" [fInputId]="item.nextMatchId">
</f-connection>
}

@for (item of viewModel; track item.id) {
<div fNode [fNodeId]="item.id" [fNodePosition]="item.position!"
fDragHandle
fNodeOutput [fOutputId]="item.id" fOutputConnectableSide="right"
fNodeInput [fInputId]="item.id" fInputConnectableSide="left"
[fNodeDraggingDisabled]="true">
<div class="header" [class]="item.color">
<div class="title">{{ item.competitionPhase }}</div>
<div class="date">{{ item.date | date }}</div>
</div>
<div class="body">
@for (competitor of item.competitors; track competitor.title) {
<div class="competitor">
<div class="name">{{ competitor.title }}</div>
<div class="score">{{ competitor.score }}</div>
</div>
}
</div>
</div>
}
</f-canvas>
</f-flow>

Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
:host {
position: relative;
display: flex;
width: 100%;
height: 100%;
}

::ng-deep :root {
--node-background-color: #ffffff;
--node-border-color: rgba(60, 60, 67, 0.36);
--node-color: rgba(60, 60, 67, 0.78);

--connection-color: rgba(60, 60, 67, 0.78);

--blue: #3451b2;
--blue-soft: rgba(100, 108, 255, 0.14);

--purple: #6f42c1;
--purple-soft: rgba(159, 122, 234, 0.14);

--green: #18794e;
--green-soft: rgba(16, 185, 129, 0.14);

--yellow: #915930;
--yellow-soft: rgba(234, 179, 8, 0.14);

--red: #b8272c;
--red-soft: rgba(244, 63, 94, 0.14);


&.dark {
--node-background-color: #000000;
--node-border-color: rgba(235, 235, 245, 0.38);
--node-color: rgba(235, 235, 245, 0.6);

--connection-color: rgba(235, 235, 245, 0.6);

--indigo: #a8b1ff;
--indigo-soft: rgba(100, 108, 255, 0.16);

--purple: #c8abfa;
--purple-soft: rgba(159, 122, 234, 0.16);

--green-1: #3dd68c;
--green-soft: rgba(16, 185, 129, 0.16);

--yellow: #f9b44e;
--yellow-soft: rgba(234, 179, 8, 0.16);

--red: #f66f81;
--red-soft: rgba(244, 63, 94, 0.16);
}
}

::ng-deep tournament-bracket {
.f-connection {
.f-connection-drag-handle {
fill: transparent;
}

.f-connection-selection {
fill: none;
}

.f-connection-path {
fill: none;
stroke: var(--connection-color);
stroke-width: 2;
}
}
}

.f-node {
width: 200px;
color: var(--node-color);
background: var(--node-background-color);
border-radius: var(--node-border-radius);
border: 0.2px solid var(--node-border-color);
cursor: move;
box-shadow: var(--node-shadow);

.header {
padding: 4px 12px;


.title {
font-size: 14px;
font-weight: 700;
}

.date {
font-size: 12px;
font-weight: 400;
}

&.blue {
color: var(--blue);
background-color: var(--blue-soft);
}

&.red {
color: var(--red);
background-color: var(--red-soft);
}

&.green {
color: var(--green);
background-color: var(--green-soft);
}

&.yellow {
color: var(--yellow);
background-color: var(--yellow-soft);
}

&.purple {
color: var(--purple);
background-color: var(--purple-soft);
}
}

.competitor {
display: flex;
align-items: center;
justify-content: space-between;
padding: 2px 12px;
font-size: 14px;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core';
import {
FCanvasComponent,
FFlowModule, FNodeBase,
} from '@foblex/flow';
import { ITournamentBracketItem } from './i-tournament-bracket-item';
import { TournamentBracket } from './tournament-bracket';
import { TOURNAMENT_BRACKET_STORE } from './storage';
import { DatePipe } from '@angular/common';
import { PointExtensions } from '@foblex/2d';

@Component({
selector: 'tournament-bracket',
templateUrl: './tournament-bracket.component.html',
styleUrls: [ './tournament-bracket.component.scss' ],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
FFlowModule,
DatePipe,
]
})
export class TournamentBracketComponent implements OnInit {

@ViewChild(FCanvasComponent, { static: true })
public fCanvasComponent!: FCanvasComponent;

public viewModel: ITournamentBracketItem[] = [];

public ngOnInit(): void {
this.viewModel = new TournamentBracket(TOURNAMENT_BRACKET_STORE, 200, 113, 100, 150).calculate();
}

public onInitialized(): void {
this.fCanvasComponent.fitToScreen(PointExtensions.initialize(100, 100), false);
}
}
Loading

0 comments on commit a934e8c

Please sign in to comment.