?
This commit is contained in:
93
node_modules/vue-tournament-bracket/.eslintrc.js
generated
vendored
Normal file
93
node_modules/vue-tournament-bracket/.eslintrc.js
generated
vendored
Normal 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
6
node_modules/vue-tournament-bracket/.prettierrc
generated
vendored
Normal 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
238
node_modules/vue-tournament-bracket/README.md
generated
vendored
Normal 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).
|
||||
|
||||

|
||||
|
||||
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 },
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||

|
||||
|
||||
#### 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.
|
||||
|
||||

|
||||
|
||||
### 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:
|
||||
|
||||

|
||||
|
||||
|
||||
### 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
8
node_modules/vue-tournament-bracket/babel.config.js
generated
vendored
Normal 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"
|
||||
};
|
||||
5980
node_modules/vue-tournament-bracket/dist/vue-tournament-bracket.common.js
generated
vendored
Normal file
5980
node_modules/vue-tournament-bracket/dist/vue-tournament-bracket.common.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/vue-tournament-bracket/dist/vue-tournament-bracket.common.js.map
generated
vendored
Normal file
1
node_modules/vue-tournament-bracket/dist/vue-tournament-bracket.common.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
6000
node_modules/vue-tournament-bracket/dist/vue-tournament-bracket.umd.js
generated
vendored
Normal file
6000
node_modules/vue-tournament-bracket/dist/vue-tournament-bracket.umd.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/vue-tournament-bracket/dist/vue-tournament-bracket.umd.js.map
generated
vendored
Normal file
1
node_modules/vue-tournament-bracket/dist/vue-tournament-bracket.umd.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/vue-tournament-bracket/dist/vue-tournament-bracket.umd.min.js
generated
vendored
Normal file
2
node_modules/vue-tournament-bracket/dist/vue-tournament-bracket.umd.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/vue-tournament-bracket/dist/vue-tournament-bracket.umd.min.js.map
generated
vendored
Normal file
1
node_modules/vue-tournament-bracket/dist/vue-tournament-bracket.umd.min.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
node_modules/vue-tournament-bracket/docs/bottom-slot.png
generated
vendored
Normal file
BIN
node_modules/vue-tournament-bracket/docs/bottom-slot.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
node_modules/vue-tournament-bracket/docs/example.png
generated
vendored
Normal file
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
BIN
node_modules/vue-tournament-bracket/docs/flat-tree.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
node_modules/vue-tournament-bracket/docs/thirdplaceplayoff.png
generated
vendored
Normal file
BIN
node_modules/vue-tournament-bracket/docs/thirdplaceplayoff.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
43
node_modules/vue-tournament-bracket/package.json
generated
vendored
Normal file
43
node_modules/vue-tournament-bracket/package.json
generated
vendored
Normal 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
16
node_modules/vue-tournament-bracket/public/index.html
generated
vendored
Normal 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
69
node_modules/vue-tournament-bracket/src/App.vue
generated
vendored
Normal 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
58
node_modules/vue-tournament-bracket/src/Bracket.vue
generated
vendored
Normal 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>
|
||||
201
node_modules/vue-tournament-bracket/src/components/BracketNode.vue
generated
vendored
Normal file
201
node_modules/vue-tournament-bracket/src/components/BracketNode.vue
generated
vendored
Normal 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>
|
||||
55
node_modules/vue-tournament-bracket/src/components/GamePlayers.vue
generated
vendored
Normal file
55
node_modules/vue-tournament-bracket/src/components/GamePlayers.vue
generated
vendored
Normal 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>
|
||||
86
node_modules/vue-tournament-bracket/src/components/recursiveBracket.js
generated
vendored
Normal file
86
node_modules/vue-tournament-bracket/src/components/recursiveBracket.js
generated
vendored
Normal 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
4
node_modules/vue-tournament-bracket/src/main.js
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
|
||||
createApp(App).mount("#app");
|
||||
223
node_modules/vue-tournament-bracket/test/components/recursiveBracket.test.js
generated
vendored
Normal file
223
node_modules/vue-tournament-bracket/test/components/recursiveBracket.test.js
generated
vendored
Normal 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();
|
||||
});
|
||||
|
||||
});
|
||||
72
node_modules/vue-tournament-bracket/test/components/recursiveBracketFromFlatTree.test.js
generated
vendored
Normal file
72
node_modules/vue-tournament-bracket/test/components/recursiveBracketFromFlatTree.test.js
generated
vendored
Normal 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
15
node_modules/vue-tournament-bracket/vue.config.js
generated
vendored
Normal 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
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user