This commit is contained in:
2025-05-28 17:55:30 +08:00
parent 7217c2dfcd
commit e3db03f6ad
285 changed files with 181217 additions and 271 deletions

93
node_modules/vue-tournament-bracket/.eslintrc.js generated vendored Normal file
View File

@@ -0,0 +1,93 @@
module.exports = {
env: {
browser: true,
commonjs: true,
es6: true,
mocha: true,
node: true,
},
extends: ["eslint:recommended", "plugin:vue/recommended", "prettier"],
parserOptions: {
sourceType: "module",
},
plugins: ["vue", "mocha"],
rules: {
"no-extra-boolean-cast": "off",
"max-params": ["error", 4],
"max-statements": ["error", 30],
"max-depth": ["error", 3],
complexity: ["error", 10],
curly: ["error", "all"],
"prefer-const": "error",
eqeqeq: "error",
"require-yield": "error",
"no-caller": "error",
"no-eval": "error",
"no-implied-eval": "error",
"no-eq-null": "error",
"no-multi-str": "error",
"no-shadow": "error",
"no-shadow-restricted-names": "error",
"no-sync": "warn",
"no-iterator": "error",
"no-proto": "error",
"no-script-url": "error",
"no-new-func": "error",
"newline-per-chained-call": "off",
"no-new-object": "error",
"no-self-compare": "error",
"vue/v-on-style": ["error", "shorthand"],
"vue/v-bind-style": ["error", "shorthand"],
"vue/name-property-casing": ["error", "kebab-case"],
"vue/html-quotes": ["error", "double"],
"vue/max-attributes-per-line": [
"error",
{
singleline: 4,
multiline: {
max: 1,
},
},
],
"vue/first-attribute-linebreak": [
"error",
{
singleline: "ignore",
multiline: "below",
},
],
"vue/no-multi-spaces": "error",
"vue/mustache-interpolation-spacing": ["error", "always"],
"vue/attribute-hyphenation": ["error", "always"],
"vue/this-in-template": ["error", "never"],
"vue/no-side-effects-in-computed-properties": "error",
"vue/no-duplicate-attributes": "error",
"vue/return-in-computed-property": "error",
"vue/no-template-key": "error",
"vue/no-dupe-keys": "error",
"vue/html-indent": [
"error",
4,
{
attribute: 1,
baseIndent: 1,
closeBracket: 0,
alignAttributesVertically: true,
ignores: [],
},
],
"vue/multi-word-component-names": ["off"],
"vue/prop-name-casing": "error",
"vue/component-definition-name-casing": ["error", "kebab-case"],
"vue/require-prop-types": "off",
"vue/order-in-components": "error",
"vue/attributes-order": "off",
"mocha/no-exclusive-tests": "error",
"vue/singleline-html-element-content-newline": "off",
"vue/html-closing-bracket-spacing": "off",
"vue/component-name-in-template-casing": ["error", "kebab-case"],
"vue/multiline-html-element-content-newline": "off",
"vue/html-closing-bracket-newline": "off",
"vue/no-v-html": "off",
},
};

6
node_modules/vue-tournament-bracket/.prettierrc generated vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"tabWidth": 4,
"semi": true,
"printWidth": 120,
"vueIndentScriptAndStyle": true
}

238
node_modules/vue-tournament-bracket/README.md generated vendored Normal file
View File

@@ -0,0 +1,238 @@
# vue-tournament-bracket
Vue component for rendering single elimination tournament brackets. Compatible with Vue 3. For Vue 2 support install 2.* version of the package.
Based on: [https://codepen.io/sdudnyk/pen/bWbqMb](https://codepen.io/sdudnyk/pen/bWbqMb).
Rendering is based on **flex**, see [browser support](https://caniuse.com/#feat=flexbox).
![Example](https://github.com/kamilwylegala/vue-tournament-bracket/raw/master/docs/example.png)
Real life example can be found here: [martialmatch.com](https://martialmatch.com/).
## Installation and component usage
Install component via:
```
npm install vue-tournament-bracket
```
Example:
```html
<template>
<bracket :rounds="rounds">
<template slot="player" slot-scope="{{ player }}">
{{ player.name }}
</template>
</bracket>
<template>
<script>
import Bracket from "vue-tournament-bracket";
const rounds = [
//Semi finals
{
games: [
{
player1: { id: "1", name: "Competitor 1", winner: false },
player2: { id: "4", name: "Competitor 4", winner: true },
},
{
player1: { id: "5", name: "Competitor 5", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
}
]
},
//Final
{
games: [
{
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
}
]
}
];
export default {
components: {
Bracket
},
data() {
return {
rounds: rounds
}
}
}
</script>
```
#### Third place play-off
Here is the way to represent third place play-off:
```javascript
const rounds = [
//Semi finals
{
games: [
{
player1: { id: "1", name: "Competitor 1", winner: false },
player2: { id: "4", name: "Competitor 4", winner: true },
},
{
player1: { id: "5", name: "Competitor 5", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
}
]
},
//Third place play off
{
games: [
{
player1: { id: "1", name: "Competitor 1", winner: false },
player2: { id: "5", name: "Competitor 5", winner: true },
}
]
},
//Final
{
games: [
{
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
}
]
}
];
```
![Third place play-off](https://github.com/kamilwylegala/vue-tournament-bracket/raw/master/docs/thirdplaceplayoff.png)
#### Bottom slot
There is slot with whole match props, use it in following way:
```html
<bracket :rounds="rounds">
<template #player="{ player }">
{{ player.name }}
</template>
<template #player-extension-bottom="{ match }">
Extra info: {{ match.title }}
</template>
</bracket>
```
May be useful for example for showing tooltips etc.
![Bottom slot for match](https://github.com/kamilwylegala/vue-tournament-bracket/raw/master/docs/bottom-slot.png)
### Game object
Game object requires `player1` and `player2` objects. You can also add your own and e.g. reuse it in `players-extension-bottom` slot.
Following properties are forbidden and are going to be replaced with `undefined`:
- `games`
- `hasParent`
See `matchProperties` in `GamePlayers` for details.
### Alternative input - flat tree
Version 2.1 introduces option to build bracket tree from flat tree structure where child node points to its parent node. Structure is a bit simple but requires two additional properties on each game object: `id`, `next`. It might be helpful in generating double elimination brackets with repechages. Example:
```
const rounds = [
//Repechage 1 of 3
{
id: 1,
next: 5, //Will be connected to game with id 5
player1: { id: "4", name: "Competitor 4", winner: true },
player2: { id: "5", name: "Competitor 5", winner: false },
},
{
id: 2,
next: 6,
player1: { id: "7", name: "Competitor 7", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
},
//Repechage 2 of 3
{
id: 5,
next: 7,
player1: { id: "1", name: "Competitor 1", winner: false },
player2: { id: "4", name: "Competitor 4", winner: true },
},
{
id: 6,
next: 7,
player1: { id: "3", name: "Competitor 3", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
},
//Repechage 3 of 3 - 3rd place match
{
id: 7,
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
},
];
```
Initialize bracket with `flatTree` props:
```html
<bracket :flat-tree="rounds">
```
It generates following bracket:
![Repechage](https://github.com/kamilwylegala/vue-tournament-bracket/raw/master/docs/flat-tree.png)
### Player object
Player object requires: `id` property, `winner` is optional, rest is up to you - rendering is customizable via scoped slot.
- `id` is being used for highlighting
- `winner` property applies color for player accordingly, can be also `null` - player's color will be gray
### Styling
Apply your custom style by overriding `Bracket` component CSS. See [BracketNode.vue](./src/components/BracketNode.vue) for all styles being used.
## Development
Required Node.js version is **14.0.0** (eslint).
Checkout repository and:
```
npm install
npm run serve
```
Then open browser and test your changes using `App.vue` main component for development purposes.
See `package.json` to discover available commands.
## Releasing
```
npm test
npm run eslint
npm run build
git commit
npm version <version>
git push
npm publish --access=public
```
## License
MIT

8
node_modules/vue-tournament-bracket/babel.config.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
module.exports = {
presets: ["@vue/app"],
/**
* https://stackoverflow.com/a/56283408/4520203
* Problem with requiring recursiveBracket for test (commonjs require) and for build (es6 import)
*/
sourceType: "unambiguous"
};

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
node_modules/vue-tournament-bracket/docs/example.png generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
node_modules/vue-tournament-bracket/docs/flat-tree.png generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

43
node_modules/vue-tournament-bracket/package.json generated vendored Normal file
View File

@@ -0,0 +1,43 @@
{
"name": "vue-tournament-bracket",
"version": "3.0.0",
"license": "MIT",
"repository": "https://github.com/kamilwylegala/vue-tournament-bracket",
"author": "Kamil Wylegała <kamil.wylegala@gmail.com>",
"description": "Vue component for rendering single elemination tournament brackets.",
"main": "dist/vue-tournament-bracket.common.js",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build --target lib --name vue-tournament-bracket ./src/Bracket.vue",
"lint": "vue-cli-service lint",
"eslint": "eslint src test --ignore-pattern *bundle* --ext js,vue",
"test": "mocha --require should test/**/*.test.js"
},
"dependencies": {
"vue": "^3.2.0"
},
"devDependencies": {
"@vue/babel-preset-app": "^5.0.4",
"@vue/cli-plugin-babel": "^5.0.4",
"@vue/cli-plugin-eslint": "^5.0.4",
"@vue/cli-service": "^5.0.4",
"@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^8.12.0",
"eslint-plugin-mocha": "^10.0.3",
"eslint-plugin-vue": "^8.5.0",
"mocha": "^7.1.1",
"prettier": "^2.6.1",
"should": "^13.2.3"
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

16
node_modules/vue-tournament-bracket/public/index.html generated vendored Normal file
View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>vue-tournament-bracket</title>
</head>
<body>
<noscript>
<strong>We're sorry but vue-tournament-bracket doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

69
node_modules/vue-tournament-bracket/src/App.vue generated vendored Normal file
View File

@@ -0,0 +1,69 @@
<template>
<bracket :rounds="rounds">
<template #player="{ player }">
{{ player.name }}
</template>
</bracket>
</template>
<script>
import Bracket from "./Bracket";
const rounds = [
//Quarter
{
games: [
{
player1: { id: "1", name: "Competitor 1", winner: true },
player2: { id: "2", name: "Competitor 2", winner: false }
},
{
player1: { id: "3", name: "Competitor 3", winner: false },
player2: { id: "4", name: "Competitor 4", winner: true }
},
{
player1: { id: "5", name: "Competitor 5", winner: true },
player2: { id: "6", name: "Competitor 6", winner: false }
},
{
player1: { id: "7", name: "Competitor 7", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true }
}
]
},
//Semi
{
games: [
{
player1: { id: "1", name: "Competitor 1", winner: false },
player2: { id: "4", name: "Competitor 4", winner: true }
},
{
player1: { id: "5", name: "Competitor 5", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true }
}
]
},
//Final
{
games: [
{
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true }
}
]
}
];
export default {
name: "app",
components: {
Bracket
},
data() {
return {
rounds: rounds
};
}
};
</script>

58
node_modules/vue-tournament-bracket/src/Bracket.vue generated vendored Normal file
View File

@@ -0,0 +1,58 @@
<template>
<div class="vtb-wrapper" v-if="recursiveBracket">
<bracket-node
:bracket-node="recursiveBracket"
@onSelectedPlayer="highlightPlayer"
@onDeselectedPlayer="unhighlightPlayer"
:highlighted-player-id="highlightedPlayerId"
>
<template #player="{ player }">
<slot name="player" :player="player" />
</template>
<template #player-extension-bottom="{ match }">
<slot name="player-extension-bottom" :match="match" />
</template>
</bracket-node>
</div>
</template>
<script>
import BracketNode from "./components/BracketNode";
import recursiveBracket from "./components/recursiveBracket";
export default {
name: "bracket",
components: {
"bracket-node": BracketNode,
},
props: ["rounds", "flatTree"],
data() {
return {
highlightedPlayerId: null,
};
},
computed: {
recursiveBracket() {
if (this.flatTree) {
return recursiveBracket.transformFlatTree(this.flatTree);
}
return recursiveBracket.transform(this.rounds);
},
},
methods: {
highlightPlayer(id) {
this.highlightedPlayerId = id;
},
unhighlightPlayer() {
this.highlightedPlayerId = null;
},
},
};
</script>
<style>
.vtb-wrapper {
display: flex;
}
</style>

View File

@@ -0,0 +1,201 @@
<template>
<div class="vtb-item" v-if="playersArePresent">
<div :class="getBracketNodeClass(bracketNode)">
<game-players
:bracket-node="bracketNode"
:highlighted-player-id="highlightedPlayerId"
@onSelectedPlayer="highlightPlayer"
@onDeselectedPlayer="unhighlightPlayer"
>
<template #player="{ player }">
<slot name="player" :player="player" />
</template>
<template #player-extension-bottom="{ match }">
<slot name="player-extension-bottom" :match="match" />
</template>
</game-players>
</div>
<div v-if="bracketNode.games[0] || bracketNode.games[1]" class="vtb-item-children">
<div class="vtb-item-child" v-if="bracketNode.games[0]">
<bracket-node
:bracket-node="bracketNode.games[0]"
:highlighted-player-id="highlightedPlayerId"
@onSelectedPlayer="highlightPlayer"
@onDeselectedPlayer="unhighlightPlayer"
>
<template #player="{ player }">
<slot name="player" :player="player" />
</template>
<template #player-extension-bottom="{ match }">
<slot name="player-extension-bottom" :match="match" />
</template>
</bracket-node>
</div>
<div class="vtb-item-child" v-if="bracketNode.games[1]">
<bracket-node
:bracket-node="bracketNode.games[1]"
:highlighted-player-id="highlightedPlayerId"
@onSelectedPlayer="highlightPlayer"
@onDeselectedPlayer="unhighlightPlayer"
>
<template #player="{ player }">
<slot name="player" :player="player" />
</template>
<template #player-extension-bottom="{ match }">
<slot name="player-extension-bottom" :match="match" />
</template>
</bracket-node>
</div>
</div>
</div>
</template>
<script>
import GamePlayers from "./GamePlayers";
export default {
name: "bracket-node",
components: { GamePlayers },
props: ["bracketNode", "highlightedPlayerId"],
computed: {
playersArePresent() {
return this.bracketNode.player1 && this.bracketNode.player1;
},
},
methods: {
getBracketNodeClass(bracketNode) {
if (bracketNode.games[0] || bracketNode.games[1]) {
return "vtb-item-parent";
}
if (bracketNode.hasParent) {
return "vtb-item-child";
}
return "";
},
getPlayerClass(player) {
if (player.winner === null || player.winner === undefined) {
return "";
}
let clazz = player.winner ? "winner" : "defeated";
if (this.highlightedPlayerId === player.id) {
clazz += " highlight";
}
return clazz;
},
highlightPlayer(playerId) {
this.$emit("onSelectedPlayer", playerId);
},
unhighlightPlayer() {
this.$emit("onDeselectedPlayer");
},
},
};
</script>
<style>
.vtb-item {
display: flex;
flex-direction: row-reverse;
}
.vtb-item p {
padding: 20px;
margin: 0;
background-color: #999999;
}
.vtb-item-parent {
position: relative;
margin-left: 50px;
display: flex;
align-items: center;
}
.vtb-item-players {
flex-direction: column;
background-color: #999999;
margin: 0;
color: white;
}
.vtb-item-players .vtb-player {
padding: 10px;
}
.vtb-item-players .winner {
background-color: darkgreen;
}
.vtb-item-players .defeated {
background-color: firebrick;
}
.vtb-item-players .winner.highlight {
background-color: darkseagreen;
}
.vtb-item-players .defeated.highlight {
background-color: indianred;
}
.vtb-item-parent:after {
position: absolute;
content: "";
width: 25px;
height: 2px;
left: 0;
top: 50%;
background-color: gray;
transform: translateX(-100%);
}
.vtb-item-children {
display: flex;
flex-direction: column;
justify-content: center;
}
.vtb-item-child {
display: flex;
align-items: flex-start;
justify-content: flex-end;
margin-top: 10px;
margin-bottom: 10px;
position: relative;
}
.vtb-item-child:before {
content: "";
position: absolute;
background-color: gray;
right: 0;
top: 50%;
transform: translateX(100%);
width: 25px;
height: 2px;
}
.vtb-item-child:after {
content: "";
position: absolute;
background-color: gray;
right: -25px;
height: calc(50% + 22px);
width: 2px;
top: 50%;
}
.vtb-item-child:last-child:after {
transform: translateY(-100%);
}
.vtb-item-child:only-child:after {
display: none;
}
</style>

View File

@@ -0,0 +1,55 @@
<template>
<div class="vtb-item-players">
<div>
<div
:class="['vtb-player', 'vtb-player1', getPlayerClass(bracketNode.player1)]"
@mouseover="highlightPlayer(bracketNode.player1.id)"
@mouseleave="unhighlightPlayer"
>
<slot :player="bracketNode.player1" name="player" />
</div>
<div
:class="['vtb-player', 'vtb-player2', getPlayerClass(bracketNode.player2)]"
@mouseover="highlightPlayer(bracketNode.player2.id)"
@mouseleave="unhighlightPlayer"
>
<slot :player="bracketNode.player2" name="player" />
</div>
</div>
<slot name="player-extension-bottom" :match="matchProperties" />
</div>
</template>
<script>
export default {
name: "game-players",
props: ["bracketNode", "highlightedPlayerId"],
computed: {
matchProperties() {
return Object.assign({}, this.bracketNode, { games: undefined, hasParent: undefined });
}
},
methods: {
getPlayerClass(player) {
if (player.winner === null || player.winner === undefined) {
return "";
}
let clazz = player.winner ? "winner" : "defeated";
if (this.highlightedPlayerId === player.id) {
clazz += " highlight";
}
return clazz;
},
highlightPlayer(playerId) {
this.$emit("onSelectedPlayer", playerId);
},
unhighlightPlayer() {
this.$emit("onDeselectedPlayer");
}
}
};
</script>

View File

@@ -0,0 +1,86 @@
module.exports = {
transform(rounds) {
if (!rounds) {
return null;
}
const totalRounds = rounds.length;
let currentRound = [];
let previousRound = [];
for (let i = 0; i < totalRounds; i++) {
currentRound = rounds[i].games.map((game) => {
return {
...game,
title: "round " + i,
games: [],
hasParent: !!rounds[i + 1],
};
});
if (previousRound.length === 0) {
previousRound = currentRound;
continue;
}
for (let j = 0; j < previousRound.length; j++) {
const matchForCurrentGame = Math.floor(j / 2);
currentRound[matchForCurrentGame].games.push(previousRound[j]);
}
previousRound = currentRound;
}
return currentRound[0] || null;
},
transformFlatTree(games) {
const mapOfGamesPerParent = {};
let root = null;
games.forEach((game) => {
if (!game.next && !root) {
root = game;
return;
}
if (!mapOfGamesPerParent[game.next]) {
mapOfGamesPerParent[game.next] = [];
}
mapOfGamesPerParent[game.next].push(game);
});
const tree = {
...root,
title: "round",
games: [],
hasParent: false,
};
return constructTree(tree, mapOfGamesPerParent, Object.keys(mapOfGamesPerParent).length);
},
};
function constructTree(tree, mapOfChildren, processedRound) {
const totalChildren = mapOfChildren[tree.id] || [];
tree.title = "round " + processedRound;
for (let i = 0; i < totalChildren.length; i++) {
const childGame = totalChildren[i];
const treeChild = {
...childGame,
title: `round ${[processedRound]}`,
hasParent: true,
games: [],
};
constructTree(treeChild, mapOfChildren, processedRound - 1);
tree.games.push(treeChild);
}
return tree;
}

4
node_modules/vue-tournament-bracket/src/main.js generated vendored Normal file
View File

@@ -0,0 +1,4 @@
import { createApp } from "vue";
import App from "./App.vue";
createApp(App).mount("#app");

View File

@@ -0,0 +1,223 @@
const should = require("should");
const recursiveBracket = require("../../src/components/recursiveBracket");
describe("recursive bracket", () => {
it("should provide all properties for game object from input so it's available in players-extension slot", () => {
const result = recursiveBracket.transform([
{
games: [
{
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
matchResult: "Winner by points"
}
]
}
]);
result.should.be.eql({
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
matchResult: "Winner by points",
title: "round 0",
hasParent: false,
games: []
});
});
it("should transform one game bracket", () => {
const result = recursiveBracket.transform([
{
games: [
{
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
}
]
}
]);
result.should.be.eql({
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
title: "round 0",
hasParent: false,
games: []
});
});
it("should transform game with semifinals and final", () => {
const result = recursiveBracket.transform([
//Semi
{
games: [
{
player1: { id: "1", name: "Competitor 1", winner: false },
player2: { id: "4", name: "Competitor 4", winner: true },
},
{
player1: { id: "5", name: "Competitor 5", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
}
]
},
//Final
{
games: [
{
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
}
]
}
]);
result.should.be.eql({
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
title: "round 1",
hasParent: false,
games: [
{
games: [],
hasParent: true,
player1: {
id: "1",
name: "Competitor 1",
winner: false
},
player2: {
id: "4",
name: "Competitor 4",
winner: true
},
title: "round 0"
},
{
games: [],
hasParent: true,
player1: {
id: "5",
name: "Competitor 5",
winner: false
},
player2: {
id: "8",
name: "Competitor 8",
winner: true
},
title: "round 0"
}
]
});
});
it("should transform game with semifinals, consolidation round and final", () => {
const result = recursiveBracket.transform([
//Semi
{
games: [
{
player1: { id: "1", name: "Competitor 1", winner: false },
player2: { id: "4", name: "Competitor 4", winner: true },
},
{
player1: { id: "5", name: "Competitor 5", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
}
]
},
//Third place play off
{
games: [
{
player1: { id: "1", name: "Competitor 1", winner: false },
player2: { id: "5", name: "Competitor 5", winner: true },
}
]
},
//Final
{
games: [
{
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
}
]
}
]);
result.should.be.eql({
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
title: "round 2",
hasParent: false,
games: [
{
player1: {
id: "1",
name: "Competitor 1",
winner: false
},
player2: {
id: "5",
name: "Competitor 5",
winner: true
},
hasParent: true,
title: "round 1",
games: [
{
player1: {
id: "1",
name: "Competitor 1",
winner: false
},
player2: {
id: "4",
name: "Competitor 4",
winner: true
},
hasParent: true,
title: "round 0",
games: []
},
{
player1: {
id: "5",
name: "Competitor 5",
winner: false
},
player2: {
id: "8",
name: "Competitor 8",
winner: true
},
hasParent: true,
title: "round 0",
games: []
}
]
}
]
});
});
it("should return null output for empty rounds array", () => {
const result = recursiveBracket.transform([]);
should(result).be.null();
});
it("should return null output for empty null rounds", () => {
const result = recursiveBracket.transform(null);
should(result).be.null();
});
});

View File

@@ -0,0 +1,72 @@
const recursiveBracket = require("../../src/components/recursiveBracket");
describe("recursive bracket with parent/child marker", () => {
it("should transform flat tree", () => {
const result = recursiveBracket.transformFlatTree([
//Semi
{
id: 1,
next: 3,
player1: { id: "1", name: "Competitor 1", winner: false },
player2: { id: "4", name: "Competitor 4", winner: true },
},
{
id: 2,
next: 3,
player1: { id: "5", name: "Competitor 5", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
},
//Final
{
id: 3,
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
},
]);
result.should.be.eql({
id: 3,
player1: { id: "4", name: "Competitor 4", winner: false },
player2: { id: "8", name: "Competitor 8", winner: true },
title: "round 1",
hasParent: false,
games: [
{
id: 1,
next: 3,
games: [],
hasParent: true,
player1: {
id: "1",
name: "Competitor 1",
winner: false,
},
player2: {
id: "4",
name: "Competitor 4",
winner: true,
},
title: "round 0",
},
{
id: 2,
next: 3,
games: [],
hasParent: true,
player1: {
id: "5",
name: "Competitor 5",
winner: false,
},
player2: {
id: "8",
name: "Competitor 8",
winner: true,
},
title: "round 0",
},
],
});
});
});

15
node_modules/vue-tournament-bracket/vue.config.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
module.exports = {
css: {
extract: false
},
chainWebpack: (config) => {
config.plugin('define').tap((definitions) => {
Object.assign(definitions[0], {
__VUE_OPTIONS_API__: 'true',
__VUE_PROD_DEVTOOLS__: 'false',
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false'
})
return definitions
})
}
}