@amirhossein-shk/tournament-bracket-js 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +821 -0
- package/dist/tournament-bracket.css +117 -0
- package/dist/tournament-bracket.css.map +1 -0
- package/dist/tournament-bracket.esm.js +445 -0
- package/dist/tournament-bracket.esm.min.js +1 -0
- package/dist/tournament-bracket.js +449 -0
- package/dist/tournament-bracket.min.css +1 -0
- package/dist/tournament-bracket.min.css.map +1 -0
- package/dist/tournament-bracket.min.js +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,821 @@
|
|
|
1
|
+
# Tournament Bracket
|
|
2
|
+
|
|
3
|
+
A lightweight JavaScript library for rendering and managing single-elimination tournament brackets in the browser.
|
|
4
|
+
|
|
5
|
+
It supports both classic browser usage with a `<script>` tag and modern ESM usage with `import`. The library renders bracket rounds, matches, players, scores, avatars, and SVG connector lines, while also exposing a small API for updating matches, finishing matches, reading state, and handling events.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Render single-elimination tournament brackets
|
|
12
|
+
- Use with a normal browser `<script>` tag
|
|
13
|
+
- Use with modern ESM `import`
|
|
14
|
+
- Support player names, scores, avatars, and fallback avatars
|
|
15
|
+
- Automatically normalize missing player data
|
|
16
|
+
- Validate bracket structure before rendering
|
|
17
|
+
- Update match data after initialization
|
|
18
|
+
- Set individual player scores
|
|
19
|
+
- Finish matches and propagate winners
|
|
20
|
+
- Listen to match click, update, and finish events
|
|
21
|
+
- Ship separate JavaScript and CSS builds
|
|
22
|
+
- Include both minified and non-minified output files
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Demo
|
|
27
|
+
|
|
28
|
+
Try the live demo:
|
|
29
|
+
|
|
30
|
+
[https://amirhossein-shk.github.io/tournament-bracket/demo/](https://amirhossein-shk.github.io/tournament-bracket/demo/)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## File Structure
|
|
35
|
+
|
|
36
|
+
Recommended project structure:
|
|
37
|
+
|
|
38
|
+
```txt
|
|
39
|
+
project/
|
|
40
|
+
src/
|
|
41
|
+
tournament-bracket.js
|
|
42
|
+
tournament-bracket.scss
|
|
43
|
+
entry-browser.js
|
|
44
|
+
|
|
45
|
+
dist/
|
|
46
|
+
tournament-bracket.js
|
|
47
|
+
tournament-bracket.min.js
|
|
48
|
+
tournament-bracket.esm.js
|
|
49
|
+
tournament-bracket.esm.min.js
|
|
50
|
+
tournament-bracket.css
|
|
51
|
+
tournament-bracket.min.css
|
|
52
|
+
|
|
53
|
+
demo/
|
|
54
|
+
index.html
|
|
55
|
+
demo.js
|
|
56
|
+
demo.css
|
|
57
|
+
|
|
58
|
+
README.md
|
|
59
|
+
package.json
|
|
60
|
+
vite.config.js
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Folder Purpose
|
|
64
|
+
|
|
65
|
+
| Folder | Purpose |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `src/` | Source code of the library |
|
|
68
|
+
| `dist/` | Final build output for users |
|
|
69
|
+
| `demo/` | Live demo and usage reference |
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Build Files
|
|
74
|
+
|
|
75
|
+
The `dist` folder contains the final files that users should consume.
|
|
76
|
+
|
|
77
|
+
```txt
|
|
78
|
+
dist/
|
|
79
|
+
tournament-bracket.js
|
|
80
|
+
tournament-bracket.min.js
|
|
81
|
+
tournament-bracket.esm.js
|
|
82
|
+
tournament-bracket.esm.min.js
|
|
83
|
+
tournament-bracket.css
|
|
84
|
+
tournament-bracket.min.css
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### JavaScript Files
|
|
88
|
+
|
|
89
|
+
| File | Usage |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `tournament-bracket.js` | Browser build, non-minified |
|
|
92
|
+
| `tournament-bracket.min.js` | Browser build, minified |
|
|
93
|
+
| `tournament-bracket.esm.js` | ESM build, non-minified |
|
|
94
|
+
| `tournament-bracket.esm.min.js` | ESM build, minified |
|
|
95
|
+
|
|
96
|
+
### CSS Files
|
|
97
|
+
|
|
98
|
+
| File | Usage |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `tournament-bracket.css` | Stylesheet, non-minified |
|
|
101
|
+
| `tournament-bracket.min.css` | Stylesheet, minified |
|
|
102
|
+
|
|
103
|
+
The CSS is intentionally shipped separately from JavaScript. This makes styling easier to override, easier to debug, and easier to use with bundlers or CDNs.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Browser Usage
|
|
108
|
+
|
|
109
|
+
Use this option when you want to include the library directly in an HTML page.
|
|
110
|
+
|
|
111
|
+
```html
|
|
112
|
+
<link rel="stylesheet" href="./dist/tournament-bracket.min.css" />
|
|
113
|
+
<script src="./dist/tournament-bracket.min.js"></script>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
After loading the browser build, the global function is available as:
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
tournamentBracket(...)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Complete Browser Example
|
|
123
|
+
|
|
124
|
+
```html
|
|
125
|
+
<!doctype html>
|
|
126
|
+
<html lang="en">
|
|
127
|
+
<head>
|
|
128
|
+
<meta charset="UTF-8" />
|
|
129
|
+
<title>Tournament Bracket</title>
|
|
130
|
+
<link rel="stylesheet" href="./dist/tournament-bracket.min.css" />
|
|
131
|
+
</head>
|
|
132
|
+
<body>
|
|
133
|
+
<div id="tournament-bracket"></div>
|
|
134
|
+
|
|
135
|
+
<script src="./dist/tournament-bracket.min.js"></script>
|
|
136
|
+
<script>
|
|
137
|
+
const tb = tournamentBracket({
|
|
138
|
+
targetId: "tournament-bracket",
|
|
139
|
+
rounds: [
|
|
140
|
+
{
|
|
141
|
+
roundId: "r1",
|
|
142
|
+
name: "Quarter Finals",
|
|
143
|
+
matches: [
|
|
144
|
+
{
|
|
145
|
+
matchId: "m1",
|
|
146
|
+
isFinished: true,
|
|
147
|
+
players: [
|
|
148
|
+
{ name: "Player 1", score: 2, avatarUrl: "" },
|
|
149
|
+
{ name: "Player 2", score: 1, avatarUrl: "" }
|
|
150
|
+
]
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
matchId: "m2",
|
|
154
|
+
players: [
|
|
155
|
+
{ name: "Player 3", score: "-" },
|
|
156
|
+
{ name: "Player 4", score: "-" }
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
roundId: "r2",
|
|
163
|
+
name: "Semi Finals",
|
|
164
|
+
matches: [
|
|
165
|
+
{
|
|
166
|
+
matchId: "m3",
|
|
167
|
+
players: [{}, {}]
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
tb.init();
|
|
175
|
+
</script>
|
|
176
|
+
</body>
|
|
177
|
+
</html>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## ESM Usage
|
|
183
|
+
|
|
184
|
+
Use this option when working with a module-based setup.
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
import tournamentBracket from "./dist/tournament-bracket.esm.js";
|
|
188
|
+
import "./dist/tournament-bracket.css";
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Complete ESM Example
|
|
192
|
+
|
|
193
|
+
```js
|
|
194
|
+
import tournamentBracket from "./dist/tournament-bracket.esm.js";
|
|
195
|
+
import "./dist/tournament-bracket.css";
|
|
196
|
+
|
|
197
|
+
const tb = tournamentBracket({
|
|
198
|
+
targetId: "tournament-bracket",
|
|
199
|
+
rounds: [
|
|
200
|
+
{
|
|
201
|
+
roundId: "r1",
|
|
202
|
+
name: "Quarter Finals",
|
|
203
|
+
matches: [
|
|
204
|
+
{
|
|
205
|
+
matchId: "m1",
|
|
206
|
+
isFinished: true,
|
|
207
|
+
players: [
|
|
208
|
+
{ name: "Player 1", score: 2, avatarUrl: "" },
|
|
209
|
+
{ name: "Player 2", score: 1, avatarUrl: "" }
|
|
210
|
+
]
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
matchId: "m2",
|
|
214
|
+
players: [
|
|
215
|
+
{ name: "Player 3", score: "-" },
|
|
216
|
+
{ name: "Player 4", score: "-" }
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
roundId: "r2",
|
|
223
|
+
name: "Semi Finals",
|
|
224
|
+
matches: [
|
|
225
|
+
{
|
|
226
|
+
matchId: "m3",
|
|
227
|
+
players: [{}, {}]
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
}
|
|
231
|
+
]
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
tb.init();
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
If you use ESM directly in the browser, load your code with:
|
|
238
|
+
|
|
239
|
+
```html
|
|
240
|
+
<script type="module" src="./your-file.js"></script>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Do not open ESM files directly with `file://`. Use a local development server instead.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Required HTML
|
|
248
|
+
|
|
249
|
+
The page must contain a target container element:
|
|
250
|
+
|
|
251
|
+
```html
|
|
252
|
+
<div id="tournament-bracket"></div>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The `targetId` option must match this element ID:
|
|
256
|
+
|
|
257
|
+
```js
|
|
258
|
+
const tb = tournamentBracket({
|
|
259
|
+
targetId: "tournament-bracket",
|
|
260
|
+
rounds: []
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Configuration
|
|
267
|
+
|
|
268
|
+
Create a bracket instance by calling:
|
|
269
|
+
|
|
270
|
+
```js
|
|
271
|
+
const tb = tournamentBracket(config);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Available Options
|
|
275
|
+
|
|
276
|
+
| Option | Type | Default | Description |
|
|
277
|
+
|---|---|---:|---|
|
|
278
|
+
| `targetId` | `string` | `"tournament-bracket"` | ID of the container element |
|
|
279
|
+
| `rounds` | `Array` | required | Tournament rounds data |
|
|
280
|
+
| `distance` | `number` | `42` | Internal spacing used for layout and connectors |
|
|
281
|
+
| `width` | `number` | `196` | Width of each match card |
|
|
282
|
+
| `matchHeight` | `number` | `94` | Height of each match card |
|
|
283
|
+
| `roundGap` | `number` | `96` | Horizontal gap between rounds |
|
|
284
|
+
| `avatarFallbackUrl` | `string` | `"./images/avatar.png"` | Fallback avatar when a player avatar is missing |
|
|
285
|
+
| `connectorColor` | `string` | `"white"` | Color of SVG connector lines |
|
|
286
|
+
| `onMatchClick` | `function` | noop | Called when a match is clicked |
|
|
287
|
+
| `onMatchUpdate` | `function` | noop | Called when a match is updated |
|
|
288
|
+
| `onMatchFinish` | `function` | noop | Called when a match is finished |
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Full Configuration Example
|
|
293
|
+
|
|
294
|
+
```js
|
|
295
|
+
const sampleConfig = {
|
|
296
|
+
targetId: "tournament-bracket",
|
|
297
|
+
distance: 80,
|
|
298
|
+
width: 196,
|
|
299
|
+
matchHeight: 72,
|
|
300
|
+
roundGap: 80,
|
|
301
|
+
connectorColor: "#64748b",
|
|
302
|
+
rounds: [
|
|
303
|
+
{
|
|
304
|
+
roundId: "r1",
|
|
305
|
+
name: "Quarter Finals",
|
|
306
|
+
matches: [
|
|
307
|
+
{
|
|
308
|
+
matchId: "m1",
|
|
309
|
+
isFinished: true,
|
|
310
|
+
players: [
|
|
311
|
+
{
|
|
312
|
+
name: "Player 1",
|
|
313
|
+
score: 2,
|
|
314
|
+
avatarUrl: ""
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: "Player 2",
|
|
318
|
+
score: 1,
|
|
319
|
+
avatarUrl: ""
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
matchId: "m2",
|
|
325
|
+
players: [
|
|
326
|
+
{
|
|
327
|
+
name: "Player 3",
|
|
328
|
+
score: "-"
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
name: "Player 4",
|
|
332
|
+
score: "-"
|
|
333
|
+
}
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
]
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
roundId: "r2",
|
|
340
|
+
name: "Semi Finals",
|
|
341
|
+
matches: [
|
|
342
|
+
{
|
|
343
|
+
matchId: "m3",
|
|
344
|
+
players: [{}, {}]
|
|
345
|
+
}
|
|
346
|
+
]
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
};
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Data Structure
|
|
355
|
+
|
|
356
|
+
### Rounds
|
|
357
|
+
|
|
358
|
+
The `rounds` array defines the complete tournament structure.
|
|
359
|
+
|
|
360
|
+
```js
|
|
361
|
+
rounds: [
|
|
362
|
+
{
|
|
363
|
+
roundId: "r1",
|
|
364
|
+
name: "Quarter Finals",
|
|
365
|
+
matches: [
|
|
366
|
+
{
|
|
367
|
+
matchId: "m1",
|
|
368
|
+
players: [
|
|
369
|
+
{ name: "Player 1", score: 2 },
|
|
370
|
+
{ name: "Player 2", score: 1 }
|
|
371
|
+
]
|
|
372
|
+
}
|
|
373
|
+
]
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Round Object
|
|
379
|
+
|
|
380
|
+
| Field | Type | Required | Description |
|
|
381
|
+
|---|---|---|---|
|
|
382
|
+
| `roundId` | `string` | no | Optional custom round ID |
|
|
383
|
+
| `name` | `string` | no | Optional round name |
|
|
384
|
+
| `matches` | `Array` | yes | List of matches in this round |
|
|
385
|
+
|
|
386
|
+
### Match Object
|
|
387
|
+
|
|
388
|
+
| Field | Type | Required | Description |
|
|
389
|
+
|---|---|---|---|
|
|
390
|
+
| `matchId` | `string` | no | Optional custom match ID |
|
|
391
|
+
| `isFinished` | `boolean` | no | Marks the match as completed |
|
|
392
|
+
| `players` | `Array` | yes | Exactly two players |
|
|
393
|
+
|
|
394
|
+
### Player Object
|
|
395
|
+
|
|
396
|
+
| Field | Type | Required | Description |
|
|
397
|
+
|---|---|---|---|
|
|
398
|
+
| `name` | `string` | no | Player display name |
|
|
399
|
+
| `score` | `number \| string` | no | Player score, or `"-"` for pending score |
|
|
400
|
+
| `avatarUrl` | `string` | no | Player avatar URL |
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## Default Player Values
|
|
405
|
+
|
|
406
|
+
If a player is missing or partially defined, the library fills missing fields with default values.
|
|
407
|
+
|
|
408
|
+
```js
|
|
409
|
+
{
|
|
410
|
+
name: "-",
|
|
411
|
+
avatarUrl: "",
|
|
412
|
+
score: "-"
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
This is valid:
|
|
417
|
+
|
|
418
|
+
```js
|
|
419
|
+
players: [{}, {}]
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
This is also valid:
|
|
423
|
+
|
|
424
|
+
```js
|
|
425
|
+
players: [
|
|
426
|
+
{ name: "Player 1" },
|
|
427
|
+
{}
|
|
428
|
+
]
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Empty player objects are useful for future rounds where participants are not known yet.
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Validation Rules
|
|
436
|
+
|
|
437
|
+
The library validates the tournament structure before rendering.
|
|
438
|
+
|
|
439
|
+
The rules are:
|
|
440
|
+
|
|
441
|
+
- `rounds` must be a non-empty array
|
|
442
|
+
- every round must be an object
|
|
443
|
+
- every round must contain a `matches` array
|
|
444
|
+
- every match must be an object
|
|
445
|
+
- every match must contain exactly `2` players
|
|
446
|
+
- the first round match count must be a power of two
|
|
447
|
+
- each next round must contain exactly half the matches of the previous round
|
|
448
|
+
- the final round must contain exactly `1` match
|
|
449
|
+
|
|
450
|
+
### Valid Bracket Shape
|
|
451
|
+
|
|
452
|
+
```txt
|
|
453
|
+
Round 1: 8 matches
|
|
454
|
+
Round 2: 4 matches
|
|
455
|
+
Round 3: 2 matches
|
|
456
|
+
Round 4: 1 match
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Invalid Bracket Shape
|
|
460
|
+
|
|
461
|
+
```txt
|
|
462
|
+
Round 1: 3 matches
|
|
463
|
+
Round 2: 2 matches
|
|
464
|
+
Round 3: 1 match
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
This is invalid because the first round has `3` matches, and `3` is not a power of two.
|
|
468
|
+
|
|
469
|
+
Tournament math is strict. Sadly, it does not accept emotional arguments.
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
## Instance API
|
|
474
|
+
|
|
475
|
+
`tournamentBracket(config)` returns an object with these methods:
|
|
476
|
+
|
|
477
|
+
```js
|
|
478
|
+
{
|
|
479
|
+
init,
|
|
480
|
+
updateMatch,
|
|
481
|
+
setMatchScore,
|
|
482
|
+
finishMatch,
|
|
483
|
+
destroy,
|
|
484
|
+
getState,
|
|
485
|
+
onMatchClick,
|
|
486
|
+
onMatchUpdate,
|
|
487
|
+
onMatchFinish
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## Methods
|
|
494
|
+
|
|
495
|
+
### `init()`
|
|
496
|
+
|
|
497
|
+
Initializes and renders the bracket.
|
|
498
|
+
|
|
499
|
+
```js
|
|
500
|
+
tb.init();
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
Call this after creating the instance.
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
### `updateMatch(matchId, patch)`
|
|
508
|
+
|
|
509
|
+
Updates an existing match by ID.
|
|
510
|
+
|
|
511
|
+
```js
|
|
512
|
+
tb.updateMatch("m2", {
|
|
513
|
+
players: [
|
|
514
|
+
{ name: "Player 3", score: 1 },
|
|
515
|
+
{ name: "Player 4", score: 2 }
|
|
516
|
+
]
|
|
517
|
+
});
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
Use this when you want to update match data after initialization.
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
### `setMatchScore(matchId, playerIndex, score)`
|
|
525
|
+
|
|
526
|
+
Updates the score of one player in a match.
|
|
527
|
+
|
|
528
|
+
```js
|
|
529
|
+
tb.setMatchScore("m2", 0, 3);
|
|
530
|
+
tb.setMatchScore("m2", 1, 1);
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
Arguments:
|
|
534
|
+
|
|
535
|
+
| Argument | Description |
|
|
536
|
+
|---|---|
|
|
537
|
+
| `matchId` | ID of the match |
|
|
538
|
+
| `playerIndex` | Player index, either `0` or `1` |
|
|
539
|
+
| `score` | New player score |
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
### `finishMatch(matchId)`
|
|
544
|
+
|
|
545
|
+
Marks a match as finished.
|
|
546
|
+
|
|
547
|
+
```js
|
|
548
|
+
tb.finishMatch("m2");
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
When a match is finished, the library can resolve the winner and move the winner forward according to the bracket structure.
|
|
552
|
+
|
|
553
|
+
---
|
|
554
|
+
|
|
555
|
+
### `destroy()`
|
|
556
|
+
|
|
557
|
+
Clears the rendered bracket from the target container.
|
|
558
|
+
|
|
559
|
+
```js
|
|
560
|
+
tb.destroy();
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
Use this when you want to remove the bracket or re-render from scratch.
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
### `getState()`
|
|
568
|
+
|
|
569
|
+
Returns the current internal bracket state.
|
|
570
|
+
|
|
571
|
+
```js
|
|
572
|
+
const state = tb.getState();
|
|
573
|
+
console.log(state);
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
This is useful for debugging, inspection, or syncing with external application state.
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## Event Callbacks
|
|
581
|
+
|
|
582
|
+
Callbacks can be passed in the initial configuration.
|
|
583
|
+
|
|
584
|
+
```js
|
|
585
|
+
const tb = tournamentBracket({
|
|
586
|
+
...sampleConfig,
|
|
587
|
+
onMatchClick: function (match) {
|
|
588
|
+
console.log("clicked", match);
|
|
589
|
+
},
|
|
590
|
+
onMatchUpdate: function (match) {
|
|
591
|
+
console.log("updated", match);
|
|
592
|
+
},
|
|
593
|
+
onMatchFinish: function (match) {
|
|
594
|
+
console.log("finished", match);
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
They can also be registered or replaced later using the instance API.
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
### `onMatchClick(fn)`
|
|
604
|
+
|
|
605
|
+
Registers a callback for match click events.
|
|
606
|
+
|
|
607
|
+
```js
|
|
608
|
+
tb.onMatchClick(function (match) {
|
|
609
|
+
console.log("clicked", match);
|
|
610
|
+
});
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
---
|
|
614
|
+
|
|
615
|
+
### `onMatchUpdate(fn)`
|
|
616
|
+
|
|
617
|
+
Registers a callback for match update events.
|
|
618
|
+
|
|
619
|
+
```js
|
|
620
|
+
tb.onMatchUpdate(function (match) {
|
|
621
|
+
console.log("updated", match);
|
|
622
|
+
});
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
### `onMatchFinish(fn)`
|
|
628
|
+
|
|
629
|
+
Registers a callback for match finish events.
|
|
630
|
+
|
|
631
|
+
```js
|
|
632
|
+
tb.onMatchFinish(function (match) {
|
|
633
|
+
console.log("finished", match);
|
|
634
|
+
});
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
## Complete Event Example
|
|
640
|
+
|
|
641
|
+
```js
|
|
642
|
+
const tb = tournamentBracket({
|
|
643
|
+
...sampleConfig,
|
|
644
|
+
onMatchClick: function (match) {
|
|
645
|
+
console.log("config.onMatchClick", {
|
|
646
|
+
matchId: match.matchId,
|
|
647
|
+
players: match.players
|
|
648
|
+
});
|
|
649
|
+
},
|
|
650
|
+
onMatchUpdate: function (match) {
|
|
651
|
+
console.log("config.onMatchUpdate", {
|
|
652
|
+
matchId: match.matchId,
|
|
653
|
+
players: match.players,
|
|
654
|
+
isFinished: match.isFinished
|
|
655
|
+
});
|
|
656
|
+
},
|
|
657
|
+
onMatchFinish: function (match) {
|
|
658
|
+
console.log("config.onMatchFinish", {
|
|
659
|
+
matchId: match.matchId,
|
|
660
|
+
players: match.players
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
tb.init();
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
---
|
|
669
|
+
|
|
670
|
+
## Typical Usage Flow
|
|
671
|
+
|
|
672
|
+
A common usage flow looks like this:
|
|
673
|
+
|
|
674
|
+
```js
|
|
675
|
+
const tb = tournamentBracket(sampleConfig);
|
|
676
|
+
|
|
677
|
+
tb.init();
|
|
678
|
+
|
|
679
|
+
tb.setMatchScore("m2", 0, 2);
|
|
680
|
+
tb.setMatchScore("m2", 1, 0);
|
|
681
|
+
|
|
682
|
+
tb.finishMatch("m2");
|
|
683
|
+
|
|
684
|
+
console.log(tb.getState());
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
---
|
|
688
|
+
|
|
689
|
+
## Demo
|
|
690
|
+
|
|
691
|
+
The `demo/` folder can contain a live example of the library.
|
|
692
|
+
|
|
693
|
+
Recommended demo structure:
|
|
694
|
+
|
|
695
|
+
```txt
|
|
696
|
+
demo/
|
|
697
|
+
index.html
|
|
698
|
+
demo.js
|
|
699
|
+
demo.css
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
The demo should show both:
|
|
703
|
+
|
|
704
|
+
- the rendered tournament bracket
|
|
705
|
+
- the code used to create it
|
|
706
|
+
|
|
707
|
+
This way, users can see how the library works and copy the usage code from the same place.
|
|
708
|
+
|
|
709
|
+
---
|
|
710
|
+
|
|
711
|
+
## Styling
|
|
712
|
+
|
|
713
|
+
The library requires CSS for proper visual presentation.
|
|
714
|
+
|
|
715
|
+
### Browser Usage
|
|
716
|
+
|
|
717
|
+
```html
|
|
718
|
+
<link rel="stylesheet" href="./dist/tournament-bracket.min.css" />
|
|
719
|
+
<script src="./dist/tournament-bracket.min.js"></script>
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
### ESM Usage
|
|
723
|
+
|
|
724
|
+
```js
|
|
725
|
+
import "./dist/tournament-bracket.css";
|
|
726
|
+
import tournamentBracket from "./dist/tournament-bracket.esm.js";
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
If the JavaScript works but the bracket looks broken or unstyled, the CSS file is probably missing.
|
|
730
|
+
|
|
731
|
+
Without CSS, the bracket technically works, but visually it may look like it lost a fight with a spreadsheet.
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
735
|
+
## Troubleshooting
|
|
736
|
+
|
|
737
|
+
### The bracket does not render
|
|
738
|
+
|
|
739
|
+
Check that the target container exists:
|
|
740
|
+
|
|
741
|
+
```html
|
|
742
|
+
<div id="tournament-bracket"></div>
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
Also check that the `targetId` matches:
|
|
746
|
+
|
|
747
|
+
```js
|
|
748
|
+
targetId: "tournament-bracket"
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
---
|
|
752
|
+
|
|
753
|
+
### The browser says `tournamentBracket is not defined`
|
|
754
|
+
|
|
755
|
+
Make sure the browser build is loaded before your script:
|
|
756
|
+
|
|
757
|
+
```html
|
|
758
|
+
<script src="./dist/tournament-bracket.min.js"></script>
|
|
759
|
+
<script>
|
|
760
|
+
const tb = tournamentBracket(config);
|
|
761
|
+
</script>
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
### ESM import does not work
|
|
767
|
+
|
|
768
|
+
Make sure you are using the ESM build:
|
|
769
|
+
|
|
770
|
+
```js
|
|
771
|
+
import tournamentBracket from "./dist/tournament-bracket.esm.js";
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
If you are testing directly in the browser, use a local server and a module script:
|
|
775
|
+
|
|
776
|
+
```html
|
|
777
|
+
<script type="module" src="./demo.js"></script>
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
Do not rely on `file://` for ESM testing.
|
|
781
|
+
|
|
782
|
+
---
|
|
783
|
+
|
|
784
|
+
### Styles are missing
|
|
785
|
+
|
|
786
|
+
Include the CSS file:
|
|
787
|
+
|
|
788
|
+
```html
|
|
789
|
+
<link rel="stylesheet" href="./dist/tournament-bracket.min.css" />
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
or import it in your module setup:
|
|
793
|
+
|
|
794
|
+
```js
|
|
795
|
+
import "./dist/tournament-bracket.css";
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
---
|
|
799
|
+
|
|
800
|
+
### Invalid rounds error
|
|
801
|
+
|
|
802
|
+
Check these structure rules:
|
|
803
|
+
|
|
804
|
+
- first round match count must be a power of two
|
|
805
|
+
- each next round must have half the matches of the previous round
|
|
806
|
+
- final round must have exactly one match
|
|
807
|
+
- every match must have exactly two players
|
|
808
|
+
|
|
809
|
+
---
|
|
810
|
+
|
|
811
|
+
## Links
|
|
812
|
+
|
|
813
|
+
- [Live Demo](https://YOUR_USERNAME.github.io/tournament-bracket/demo/)
|
|
814
|
+
- [GitHub Repository](https://github.com/YOUR_USERNAME/tournament-bracket)
|
|
815
|
+
- [NPM Package](https://www.npmjs.com/package/tournament-bracket)
|
|
816
|
+
|
|
817
|
+
---
|
|
818
|
+
|
|
819
|
+
## License
|
|
820
|
+
|
|
821
|
+
MIT
|