@johnmorrisdotca/taisen-brackets 0.3.0 → 0.5.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/CHANGELOG.md +46 -1
- package/NOTICE.md +5 -2
- package/README.md +195 -23
- package/dist/background.d.ts +19 -0
- package/dist/background.js +66 -0
- package/dist/backgrounds-entry.d.ts +2 -0
- package/dist/backgrounds-entry.js +1 -0
- package/dist/family-backgrounds.d.ts +61 -0
- package/dist/family-backgrounds.js +453 -0
- package/dist/index.d.ts +9 -3
- package/dist/index.js +5 -2
- package/dist/locales.js +30 -0
- package/dist/marks.d.ts +18 -1
- package/dist/marks.js +89 -3
- package/dist/matchup.js +1 -0
- package/dist/render.d.ts +1 -1
- package/dist/render.js +473 -87
- package/dist/road-entry.d.ts +2 -0
- package/dist/road-entry.js +1 -0
- package/dist/road.d.ts +28 -0
- package/dist/road.draw.d.ts +4 -0
- package/dist/road.draw.js +259 -0
- package/dist/road.js +58 -0
- package/dist/road.model.d.ts +63 -0
- package/dist/road.model.js +312 -0
- package/dist/road.types.d.ts +115 -0
- package/dist/road.types.js +1 -0
- package/dist/text.d.ts +14 -1
- package/dist/text.js +50 -4
- package/dist/theme.d.ts +29 -2
- package/dist/theme.js +50 -11
- package/dist/types.d.ts +120 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +18 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,50 @@ All notable changes to this project are written here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.5.0] - 2026-10-14
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **`background`**: a poster background under a bracket or a road chart. A preset's name draws one of **twenty-five**, each drawn in code (gradients, radial glows, patterns, bezier bands, markings) in a dark and a light variant, deterministic and at most 6 kB: `midnight`, `crimson`, `royal`, `emerald`, `pitch`, `ice`, `hardwood`, `infield`, `turf`, `ball-panels`, `swirl`, `waves`, `glow`, `floodlights`, `confetti`, `carbon`, `aurora`, `sunburst`, `chevrons`, `halftone`, `argyle`, `contour`, `stardust`, `mesh` and `spotlight`. Five read as the sport's playing surface: soccer's mowing stripes and markings, hockey's scratched ice with its centre circle, lines and faceoff spots, basketball's planks and court, baseball's grass, dirt and base paths, football's turf, yard lines and end zones. The text, lines, boxes, marks and accent take the colours that read over each (4.5 to 1 against the background's dominant colours, a test over every preset, both variants and every size, and over colours of your own, with a veil where a mid-tone ground needs one). With `theme: "auto"` both variants are drawn and the stylesheet shows the one that fits the page. [docs/backgrounds.md](docs/backgrounds.md).
|
|
14
|
+
- **`background: { preset, colors, angle, intensity, seed, theme }`** tunes a preset, and **`background: { image, fit, dim, theme }`** draws a picture of your own as an `<image>` under a veil (`cover` or `contain`, 0 to 1 deep); the address must be http(s), relative or `data:image/…` (anything else throws), and is escaped. An unknown preset or a bad value throws a `RangeError` that names it. `roadToFinal` takes the same option. The default, the look's plain colour, is drawn exactly as before.
|
|
15
|
+
- **`src/family-backgrounds.ts`**, the drawing of the twenty-five as one dependency-free file that other packages of the family copy byte for byte: `backgroundSvg(preset, { width, height, theme, colors, angle, intensity, seed, id }) => { defs, body, ink }`, `BACKGROUND_PRESETS`, `contrastRatio`, `FAMILY_BACKGROUNDS_VERSION`. Its sha256 and version are held by `test/family-backgrounds.test.ts`; it is also the `/backgrounds` entry point. The copy rule is in docs/backgrounds.md.
|
|
16
|
+
- **Seed tags**: `seeds: true` in the road look writes a small tag (`C1`, `WC2`, `P3`) beside each mark, from the new `Matchup.seeds`, else the new `Team.tag`, else the number `Team.seed`; in a box of marks alone it sits above the mark.
|
|
17
|
+
- **`codes`** and **`vs`** for a bracket's road look: a team's three letters under its mark, and a "v" between the two marks of a box of marks alone.
|
|
18
|
+
- **A team with no flag or logo is a tile** in the road look: a rounded square with its abbreviation, filled with the team's `color` (white or near-black letters, whichever reads) or neutral.
|
|
19
|
+
- **`flagHref(code)`**: a flag as an address to load, for pages that cannot afford an inline flag of 80 kB twice; tried where `flags` gives none, written once as a symbol. **Every `mark` address** is now written once as a symbol and used wherever the team appears, instead of a long `<image href>` per use.
|
|
20
|
+
- **`tieClass(tie)`**: a class word for a tie's group, so that a page's own style can highlight or dim ties (a division); only words of letters, digits, `-` and `_` are kept.
|
|
21
|
+
- The demo has a background picker (all twenty-five as swatches, your own colours, an image read in the browser and never uploaded), a gallery of the twenty-five on the sports they suit, and opens every example on the background of its sport. NHL 2024 carries its seed tags.
|
|
22
|
+
- `pnpm images` redoes every picture of `docs/images` in one command, the two grids of the twenty-five included; `pnpm test:readme` checks that every picture the README and the docs name exists. [docs/roadmap.md](docs/roadmap.md) and [docs/references.md](docs/references.md): what is planned (new layouts and a group-stage table for 0.6.0) and the pictures that prompted it, as mood only.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **Marks, text and round names stuck out of their boxes**, in every size and both looks: in a tall bracket's boxes of marks the score under the marks was not counted when the marks were sized, so the flags crossed the box's top and bottom (Croatia and Japan in the round of 16 on a phone); a shoot-out's score ("1 (3)") crowded the next team's, and now sits beside the score where it fits the column, or under it. A box of marks is laid out as a stack (tag, mark, letters, score, shoot-out) and the marks are made as large as the whole stack allows. Bold text was measured as regular; it is now measured bold, so a name no longer runs under a score or the word "bye".
|
|
27
|
+
- **The names of the rounds met the connector lines.** In a tall bracket the lines now turn at the edge of the box they leave and the names stand on their own strip between the rounds, each moved along it to a place the lines do not cross, set a little smaller and on a snugger pill where the room is tight. (On a phone a round of eight or more boxes across leaves no room: the name then stands over the lines on its own pill.)
|
|
28
|
+
- **Round names were cut with an ellipsis** in a wide column ("Conference Quarterfi…"): a name is set smaller, down to 8, and then on two lines, on a taller pill (road) or two lines of text (classic), and the strip over the boxes grows to hold them. A name is cut only as a last resort.
|
|
29
|
+
- `test/geometry.test.ts` measures every drawing's marks, text, pills and lines from the markup and asserts all of this for every example, size and look.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- The demo's default view is the road look on the background that suits each sport. A bracket's classic look is unchanged apart from the fixes above.
|
|
34
|
+
- The README's pictures are regenerated (JPEG, to keep the repository small) with the new backgrounds and the fixed boxes.
|
|
35
|
+
|
|
36
|
+
## [0.4.0] - 2026-10-14
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- **`roadToFinal(input, options?)`**, also the one export of the new `/road` entry point: the road to the final. The two finalists of a tournament are drawn as large round marks with their names, each one's matches on its side as both teams' round marks with the score between them in large type and FT, AET or the shoot-out under it, the rounds named down the middle on a dotted road, and the group stage as a compact row at the foot, on a `card`, `square`, `wide` or `phone`. It takes what `drawBracket` takes (a list of matchups, `{ matchups, teams }` or a layout), so one tournament draws the bracket and either finalist's road (`team` puts it on the left; a round-robin game, `round: 0`, is the group stage); or the two finalists given with their `path` and `group`, a `logo` being another name for `mark`. It gives `{ options, title, summary, totals, draw(extra?), table(options?) }`. [docs/road.md](docs/road.md) has the fields and the options.
|
|
41
|
+
- **`look: "road"`** for `drawBracket`: large round marks with a wide ring and a hairline, big bold scores in which the loser's is quiet, one size of mark and score in every box, the rounds named on pills, and boxes as high as their width lets the marks be. A box that cannot hold names is its abbreviations, and one that cannot hold those is two marks with each score under its mark. The classic look stays the default and is drawn as before; every option works in both. New custom properties `--tb-mark-ring`, `--tb-mark-edge`, `--tb-panel` and `--tb-spine`, and `ROAD_THEMES` for their fallbacks.
|
|
42
|
+
- **`size: "card"`**, 1080 by 1350, a tall post: the rounds are rows, and in the road look the boxes are made lower, or the room left over is shared between the rounds, until the card is exactly that high.
|
|
43
|
+
- **`highlightPath`**: a team's id; its path to the final is drawn as a dotted line in `--tb-win`, the ties on it ringed and the rest quieter.
|
|
44
|
+
- **`href: (tie, side?) => address`**: a link on each tie's box, or, where that gives none, on each known team's row. Each is a real `<a class="tb-link" href aria-label>` in the SVG with an accessible name, a hit area, and a hover and focus ring. An address must be http(s), relative or a `#` fragment (`safeHref`, new): `javascript:`, `data:` and the rest give no link.
|
|
45
|
+
- **`flags`** and `Team.flag`: a team's country code is drawn by a synchronous function you pass, `(code, { shape, size, theme }) => svg`, the same shape as `emblems`; `@johnmorrisdotca/hata`'s round flags are the one to use (load them first, since its `flag()` is asynchronous). It is an optional peer and a development dependency, never a runtime one. The flag is written once as a `<symbol>`; with no renderer, or one that returns nothing, throws or returns something that is not an `<svg>`, the team's `mark` or its initials are drawn. New types `FlagRenderer`, `FlagRequest`, `FlagShape`.
|
|
46
|
+
- Words for the road in all five languages (the keys starting `road`): the French and Japanese were read by native-language reviewers; the Spanish and German need one (docs/languages.md).
|
|
47
|
+
- The demo draws every sport in the road look with Hata's flags through `flags`, and has a size switcher with the card, a switch between the bracket and the road to the final, a Classic and Road style, the champion's path, and links on the boxes. The README's pictures show the road look.
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- The demo's flags are team `flag` codes drawn through the `flags` option, not marks baked into the data.
|
|
52
|
+
|
|
9
53
|
## [0.3.0] - 2026-10-14
|
|
10
54
|
|
|
11
55
|
### Added
|
|
@@ -69,7 +113,8 @@ First version.
|
|
|
69
113
|
- A demo with real results read from spxn.com (the NHL 2024 playoffs, the World Cups of 2022, 1938 and 2026, the MLB 2022 postseason; sources and terms in NOTICE.md), two made-up examples (two-legged ties, a 32-team field) and a flags-only poster of the 2026 World Cup, in a playground that changes the size, look and shape.
|
|
70
114
|
- Playoff formats checked against English Wikipedia, each era citing its articles (docs/formats.md). The year is the year the playoffs are played in.
|
|
71
115
|
|
|
72
|
-
[Unreleased]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.
|
|
116
|
+
[Unreleased]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.4.0...HEAD
|
|
117
|
+
[0.4.0]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.3.0...v0.4.0
|
|
73
118
|
[0.3.0]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.2.1...v0.3.0
|
|
74
119
|
[0.2.1]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.2.0...v0.2.1
|
|
75
120
|
[0.2.0]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.1.1...v0.2.0
|
package/NOTICE.md
CHANGED
|
@@ -15,12 +15,15 @@ The code of this package is under the MIT licence (see [LICENSE](LICENSE)). The
|
|
|
15
15
|
| The demo's MLB results (`demo/data/mlb-2022.js`) | Every game of the 2022 MLB postseason, read through spxn.com. The information used here was obtained free of charge from and is copyrighted by Retrosheet. Interested parties may contact Retrosheet at www.retrosheet.org | Retrosheet's notice, kept as it asks |
|
|
16
16
|
| How the results were read | spxn.com's public API (a PostgREST service whose `api` schema is readable with a publishable key; the key is not kept in this repository), by `scripts/fetch-demo-data.mjs`, one request at a time, a second apart, retrieved 2026-10-14 | spxn.com is the same owner's site |
|
|
17
17
|
| The demo's two-legged ties and its 32-team generic bracket | Made up for the demo: the clubs do not exist and the teams are "Team 1" to "Team 32" | Not real |
|
|
18
|
-
| The demo's
|
|
18
|
+
| The demo's road to the final (`demo/road-data.js`) | Made up for the demo and the tests: sixteen nations that do not exist, each with a flag of a few plain shapes drawn in code (no real national flag, emblem or colour scheme is copied), and two made-up hockey clubs; the results are invented | Not real; MIT |
|
|
19
|
+
| The demo's national flags (in `demo/flags-data.js`), drawn through the `flags` option | [Hata](https://github.com/johnmorrisdotca/hata) 1.5.0 (MIT code), a development dependency used by `scripts/make-flags.mjs` and the tests, and an optional peer that the package never imports. Each flag is a Wikimedia Commons file that Hata ships only when Commons states it public domain, CC0 or CC BY; the file, author and licence of each are in `FLAG_CREDITS` in that file and in Hata's manifest and NOTICE | Each flag's own licence; none of the flags used asks for a credit. Commons marks flags as official insignia, whose use some countries restrict independently of copyright |
|
|
19
20
|
| The demo's trophies (`demo/emblems.js`, and the `dougu-sports/` files the Pages build copies beside the page) | [Dougu Sports](https://github.com/johnmorrisdotca/dougu-sports) 0.1.0, a development dependency and an optional peer: generic emblems drawn for that package, which carry no league's or federation's name or mark | MIT, same owner; its NOTICE is in the package |
|
|
20
|
-
| The words of the languages (`src/locales.ts`) | The football terms (the rounds of the World Cup, extra time, the shoot-out, a replay, a walkover) were read on 2026-10-14 in the French, Spanish, German and Japanese Wikipedia articles on the World Cups of 2022, 2026 and 1938; the articles are cited in [docs/languages.md](docs/languages.md). The other words are written for this package; the French and Japanese were read by native-language reviewers, the Spanish and German were not | Wikipedia's text is CC BY-SA 4.0; only terms (single words and short phrases) are taken, no sentence or table, and the articles are credited in docs/languages.md |
|
|
21
|
+
| The words of the languages (`src/locales.ts`) | The football terms (the rounds of the World Cup, extra time, the shoot-out, a replay, a walkover) were read on 2026-10-14 in the French, Spanish, German and Japanese Wikipedia articles on the World Cups of 2022, 2026 and 1938; the articles are cited in [docs/languages.md](docs/languages.md). The other words, including those of the road to the final, are written for this package; the French and Japanese were read by native-language reviewers, the Spanish and German were not | Wikipedia's text is CC BY-SA 4.0; only terms (single words and short phrases) are taken, no sentence or table, and the articles are credited in docs/languages.md |
|
|
21
22
|
| The names of countries in each language | Not in the package: `countryName` asks `Intl.DisplayNames`, which holds Unicode CLDR's names; the demo passes it each team's ISO code. The home nations and the two states that no longer exist are written in `src/locales.ts` and `demo/data.js` | CLDR is under the Unicode licence (https://www.unicode.org/license.txt) |
|
|
22
23
|
| The demo's page | Written for this package, with the family stylesheet `demo/family.css` | MIT |
|
|
23
24
|
|
|
24
25
|
The flags in the demo are for the nations of the World Cups of 2022 and 2026: `AR AT AU BA BE BR CA CD CH CI CO CV DE DZ EC EG ES FR GB-ENG GH HR JP KR MA MX NL NO PL PT PY SE SN US ZA`. The package itself ships no flag, no logo and no data of the leagues.
|
|
25
26
|
|
|
26
27
|
No code, text or picture was taken from the bracket libraries compared in [docs/compare.md](docs/compare.md); that page records what each says of itself.
|
|
28
|
+
|
|
29
|
+
**Backgrounds.** The twenty-five backgrounds (`src/family-backgrounds.ts`) are drawn in code from gradients, patterns and paths and are original work, MIT-licensed with the package; none is traced from, or copies, anyone's artwork, logo, trophy or photograph, and no image file is used. The pictures of other brackets that inspired the design are listed, as mood only, in docs/references.md; nothing of them is in the package. The NHL 2024 seed tags in the demo (C1, WC2, P3, A1, M2 and so on) are the divisional places and wild cards of that season's playoff field, as the league names them.
|
package/README.md
CHANGED
|
@@ -11,22 +11,22 @@ Give it the games, series, two-legged ties or replays of a tournament and it wor
|
|
|
11
11
|
<img alt="TypeScript" src="https://img.shields.io/badge/types-TypeScript-3178c6">
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
|
-
<p align="center"><a href="https://johnmorrisdotca.github.io/taisen-brackets/"><strong>Open the demo →</strong></a> · <a href="./docs/layout.md">Layout</a> · <a href="./docs/drawing.md">Drawing</a> · <a href="./docs/formats.md">Formats</a> · <a href="./docs/compare.md">How it compares</a></p>
|
|
14
|
+
<p align="center"><a href="https://johnmorrisdotca.github.io/taisen-brackets/"><strong>Open the demo →</strong></a> · <a href="./docs/layout.md">Layout</a> · <a href="./docs/drawing.md">Drawing</a> · <a href="./docs/road.md">Road to the final</a> · <a href="./docs/backgrounds.md">Backgrounds</a> · <a href="./docs/formats.md">Formats</a> · <a href="./docs/compare.md">How it compares</a></p>
|
|
15
15
|
|
|
16
16
|
<p align="center">
|
|
17
17
|
<picture>
|
|
18
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/hero-desk-dark.
|
|
19
|
-
<img alt="The 2022 World Cup knockout: two halves of eight teams meeting at the final in the centre, with Argentina's round flag and the word Champion above it and the third-place match below. Each box has both teams' round flags, abbreviations and scores, with penalties in brackets." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/hero-desk-light.
|
|
18
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/hero-desk-dark.jpg">
|
|
19
|
+
<img alt="The 2022 World Cup knockout: two halves of eight teams meeting at the final in the centre, with Argentina's round flag and the word Champion above it and the third-place match below. Each box has both teams' round flags, abbreviations and scores, with penalties in brackets." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/hero-desk-light.jpg" width="720">
|
|
20
20
|
</picture>
|
|
21
21
|
</p>
|
|
22
22
|
|
|
23
23
|
<table align="center">
|
|
24
24
|
<tr>
|
|
25
25
|
<td align="center" valign="top">
|
|
26
|
-
<img alt="The poster look: the 2026 World Cup knockout of 32 nations as round flags on dark navy joined by gold lines, Spain's flag in the centre above the word Champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/poster-desk.
|
|
26
|
+
<img alt="The poster look: the 2026 World Cup knockout of 32 nations as round flags on dark navy joined by gold lines, Spain's flag in the centre above the word Champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/poster-desk.jpg" width="470"><br><em>The poster look: flags only, gold on navy (the 2026 World Cup).</em>
|
|
27
27
|
</td>
|
|
28
28
|
<td align="center" valign="top">
|
|
29
|
-
<img alt="The 2022 World Cup on a phone: the champion at the top, then the rounds as rows, the first round at the top and at the bottom, the final in the middle, and the third-place match at the foot." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/phone-light.
|
|
29
|
+
<img alt="The 2022 World Cup on a phone: the champion at the top, then the rounds as rows, the first round at the top and at the bottom, the final in the middle, and the third-place match at the foot." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/phone-light.jpg" width="190"><br><em>On a phone the rounds are rows.</em>
|
|
30
30
|
</td>
|
|
31
31
|
</tr>
|
|
32
32
|
</table>
|
|
@@ -37,27 +37,80 @@ One layout draws them all, each from real results: the demo has a picker for the
|
|
|
37
37
|
|
|
38
38
|
<table>
|
|
39
39
|
<tr>
|
|
40
|
-
<td align="center" valign="top"><img alt="The 2024 NHL playoffs: Western and Eastern conferences, best-of-seven series, Florida Panthers as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/nhl-desk-light.
|
|
41
|
-
<td align="center" valign="top"><img alt="The 1984 Stanley Cup playoffs: the Campbell and Wales conferences, a best-of-five first round, Edmonton Oilers as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/nhl1984-desk-light.
|
|
42
|
-
<td align="center" valign="top"><img alt="The 2024 NBA playoffs: sixteen teams in two conferences, Boston Celtics as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/nba-desk-light.
|
|
40
|
+
<td align="center" valign="top"><img alt="The 2024 NHL playoffs: Western and Eastern conferences, best-of-seven series, Florida Panthers as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/nhl-desk-light.jpg" width="360"><br><em>NHL 2024, series</em></td>
|
|
41
|
+
<td align="center" valign="top"><img alt="The 1984 Stanley Cup playoffs: the Campbell and Wales conferences, a best-of-five first round, Edmonton Oilers as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/nhl1984-desk-light.jpg" width="360"><br><em>NHL 1984, an older era</em></td>
|
|
42
|
+
<td align="center" valign="top"><img alt="The 2024 NBA playoffs: sixteen teams in two conferences, Boston Celtics as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/nba-desk-light.jpg" width="360"><br><em>NBA 2024</em></td>
|
|
43
43
|
</tr>
|
|
44
44
|
<tr>
|
|
45
|
-
<td align="center" valign="top"><img alt="The 2022 MLB postseason: byes for the top two seeds of each league, Houston Astros as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/byes-desk-light.
|
|
46
|
-
<td align="center" valign="top"><img alt="The playoffs of the 2024 NFL season: single games, byes for the top seed of each conference, the Super Bowl at the centre, Philadelphia Eagles as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/nfl-desk-light.
|
|
47
|
-
<td align="center" valign="top"><img alt="The 2024 CFL playoffs: division semi-finals and finals, byes, the Grey Cup at the centre, Toronto Argonauts as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/cfl-desk-light.
|
|
45
|
+
<td align="center" valign="top"><img alt="The 2022 MLB postseason: byes for the top two seeds of each league, Houston Astros as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/byes-desk-light.jpg" width="360"><br><em>MLB 2022, byes</em></td>
|
|
46
|
+
<td align="center" valign="top"><img alt="The playoffs of the 2024 NFL season: single games, byes for the top seed of each conference, the Super Bowl at the centre, Philadelphia Eagles as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/nfl-desk-light.jpg" width="360"><br><em>NFL 2024 season, the Super Bowl</em></td>
|
|
47
|
+
<td align="center" valign="top"><img alt="The 2024 CFL playoffs: division semi-finals and finals, byes, the Grey Cup at the centre, Toronto Argonauts as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/cfl-desk-light.jpg" width="360"><br><em>CFL 2024, the Grey Cup</em></td>
|
|
48
48
|
</tr>
|
|
49
49
|
<tr>
|
|
50
|
-
<td align="center" valign="top"><img alt="The 2022 World Cup knockout stage." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/hero-desk-light.
|
|
51
|
-
<td align="center" valign="top"><img alt="The 1938 World Cup: replays." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/replays-desk-light.
|
|
52
|
-
<td align="center" valign="top"><img alt="The 2026 World Cup as a flags-only poster, in Japanese." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/poster-desk-ja.
|
|
50
|
+
<td align="center" valign="top"><img alt="The 2022 World Cup knockout stage." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/hero-desk-light.jpg" width="360"><br><em>World Cup 2022</em></td>
|
|
51
|
+
<td align="center" valign="top"><img alt="The 1938 World Cup: replays." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/replays-desk-light.jpg" width="360"><br><em>World Cup 1938, replays</em></td>
|
|
52
|
+
<td align="center" valign="top"><img alt="The 2026 World Cup as a flags-only poster, in Japanese." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/poster-desk-ja.jpg" width="360"><br><em>World Cup 2026 poster, in Japanese</em></td>
|
|
53
53
|
</tr>
|
|
54
54
|
<tr>
|
|
55
|
-
<td align="center" valign="top"><img alt="The 2024 MLS Cup Playoffs: a wild card match, a best-of-three round one, then single matches to the MLS Cup, LA Galaxy as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/mls-desk-light.
|
|
55
|
+
<td align="center" valign="top"><img alt="The 2024 MLS Cup Playoffs: a wild card match, a best-of-three round one, then single matches to the MLS Cup, LA Galaxy as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/mls-desk-light.jpg" width="360"><br><em>MLS 2024, best of three</em></td>
|
|
56
56
|
<td></td>
|
|
57
57
|
<td></td>
|
|
58
58
|
</tr>
|
|
59
59
|
</table>
|
|
60
60
|
|
|
61
|
+
## The road look, and the road to the final
|
|
62
|
+
|
|
63
|
+
`look: "road"` draws the bracket with large round flags, big bold scores and a team's path as a dotted line, and `roadToFinal` draws the way each finalist came from the same tournament.
|
|
64
|
+
|
|
65
|
+
<table>
|
|
66
|
+
<tr>
|
|
67
|
+
<td align="center" valign="top"><img alt="The 2022 World Cup in the road look, wide: large round flags, big bold scores, the loser quiet, the rounds on pills, and Argentina's path to the final as a dotted line." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/path-desk-light.jpg" width="460"><br><em>The road look, wide, with Argentina's path.</em></td>
|
|
68
|
+
<td align="center" valign="top"><img alt="The 2022 World Cup in the road look on a card, 1080 by 1350: the champion at the top, then the rounds as rows." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/card-light.jpg" width="260"><br><em>A card, 1080 by 1350.</em></td>
|
|
69
|
+
<td align="center" valign="top"><img alt="The road to the final of the 2022 World Cup on a phone in the dark: Argentina and France as large flags, and each one's matches with the score between the two flags." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/road-phone-dark.jpg" width="190"><br><em>The road to the final, on a phone.</em></td>
|
|
70
|
+
</tr>
|
|
71
|
+
<tr>
|
|
72
|
+
<td align="center" valign="top"><img alt="The same bracket in the classic look, which stays the default." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/classic-desk-light.jpg" width="460"><br><em>The classic look, still the default.</em></td>
|
|
73
|
+
<td align="center" valign="top"><img alt="The road to the final of the 2024 Stanley Cup playoffs: Florida and Edmonton, each one's three series with the games won between the two logos." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/road-wide-nhl-light.jpg" width="460"><br><em>The road to the final, a play-off (NHL 2024).</em></td>
|
|
74
|
+
<td align="center" valign="top"><img alt="The road to the final of the 2022 World Cup on a card." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/road-card-light.jpg" width="260"><br><em>The road to the final, a card.</em></td>
|
|
75
|
+
</tr>
|
|
76
|
+
</table>
|
|
77
|
+
|
|
78
|
+
## Backgrounds
|
|
79
|
+
|
|
80
|
+
`background` draws a poster background under the bracket: a gradient, a glow, a pattern or the markings of a playing surface, all drawn in code, each in a dark and a light variant. The text, the lines, the boxes and the marks take the colours that read over it. The plain colour stays the default.
|
|
81
|
+
|
|
82
|
+
<p align="center">
|
|
83
|
+
<picture>
|
|
84
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/backgrounds-dark.jpg">
|
|
85
|
+
<img alt="The twenty-five backgrounds in a grid of five by five, each behind a different bracket: midnight, crimson, royal, emerald, pitch, ice, hardwood, infield, turf, ball-panels, swirl, waves, glow, floodlights, confetti, carbon, aurora, sunburst, chevrons, halftone, argyle, contour, stardust, mesh and spotlight." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/backgrounds-light.jpg" width="900">
|
|
86
|
+
</picture>
|
|
87
|
+
</p>
|
|
88
|
+
|
|
89
|
+
The twenty-five: `midnight`, `crimson`, `royal`, `emerald`, `pitch` (soccer), `ice` (hockey), `hardwood` (basketball), `infield` (baseball), `turf` (football), `ball-panels`, `swirl`, `waves`, `glow`, `floodlights`, `confetti`, `carbon`, `aurora`, `sunburst`, `chevrons`, `halftone`, `argyle`, `contour`, `stardust`, `mesh` and `spotlight`. [docs/backgrounds.md](./docs/backgrounds.md) says what each is.
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
import { drawBracket } from "@johnmorrisdotca/taisen-brackets";
|
|
93
|
+
|
|
94
|
+
const ties = [
|
|
95
|
+
{ sides: ["VAN", "NSH"], score: [4, 3], round: 1, seeds: ["P1", "WC2"] },
|
|
96
|
+
{ sides: ["EDM", "LAK"], score: [1, 2], round: 1, seeds: ["P2", "P3"] },
|
|
97
|
+
{ sides: ["VAN", "LAK"], score: [2, 1], round: 2 },
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
// A preset by name; with `theme: "auto"` (the default) both variants are drawn and the page's own light or dark shows the right one.
|
|
101
|
+
const ice = drawBracket(ties, { look: "road", background: "ice", seeds: true });
|
|
102
|
+
|
|
103
|
+
// A preset, tuned: your own colours, the angle, how strong the pattern is, a seed for the scattered parts.
|
|
104
|
+
const mine = drawBracket(ties, { look: "road", theme: "dark", background: { preset: "swirl", colors: ["#10203a", "#3a1060", "#ff7ac8", "#6ae0ff"], angle: 135, intensity: 0.8 } });
|
|
105
|
+
|
|
106
|
+
// Your own picture, under a veil (http(s), relative or data:image/ only).
|
|
107
|
+
const photo = drawBracket(ties, { look: "road", theme: "dark", background: { image: "stadium.jpg", fit: "cover", dim: 0.55 } });
|
|
108
|
+
|
|
109
|
+
console.log(ice.includes("tb-bgd"), mine.includes("#ff7ac8"), photo.includes('<image href="stadium.jpg"')); // true true true
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
In the road look, `seeds: true` writes a small tag (`P1`, `WC2`) beside each team's mark, `codes: true` its three letters under the mark in a box of marks, `vs: true` a "v" between the two marks, and a team with no flag or logo is a tile with its letters. [docs/backgrounds.md](./docs/backgrounds.md) has the options, the contrast rules and the module the other packages of the family share.
|
|
113
|
+
|
|
61
114
|
## In 30 seconds
|
|
62
115
|
|
|
63
116
|
```sh
|
|
@@ -90,7 +143,14 @@ Put the string in a page (`element.innerHTML = svg`), write it to a file, or sen
|
|
|
90
143
|
- **Ties of every kind**: a single game (with extra time and a shoot-out), a best-of-N series (by games, or by games won), two or more legs added up, and a drawn game replayed. The winner is worked out from the games, or named.
|
|
91
144
|
- **The layout is worked out**: rounds (from who plays whom, or given), which tie feeds which, two halves meeting at the final (named for the conferences when the groups split them cleanly) or one tree left to right, byes, seeds, a third-place match placed apart, and ties that lead nowhere drawn below.
|
|
92
145
|
- **A bracket that is not played yet**: name the tree with `feeds`, or let `singleElimination` build it for any number of teams, seeded in the standard way, with byes; `advanceWinners` carries results forward.
|
|
93
|
-
- **Wide, square, phone**: 1200 by 675, 1080 by 1080, or 390 wide and as tall as it needs; the rounds are columns, or rows on a phone. Boxes show names and scores, abbreviations, or only the marks, whichever fits.
|
|
146
|
+
- **Wide, square, card, phone**: 1200 by 675, 1080 by 1080, 1080 by 1350 (a tall post), or 390 wide and as tall as it needs; the rounds are columns, or rows on a phone and a card. Boxes show names and scores, abbreviations, or only the marks, whichever fits.
|
|
147
|
+
- **The road look**: `look: "road"` draws large round flags or crests, big bold scores with the loser's quiet, the rounds named on pills, and, for one team, its path through the bracket as a dotted line. It is the look of the road-to-the-final chart below, and every other option works with it.
|
|
148
|
+
- **The road to the final**: `roadToFinal` draws the two finalists of the tournament you already have, with the ties each played on the way and the score between their flags, on a card, a square, wide or a phone.
|
|
149
|
+
- **Poster backgrounds**: twenty-five, drawn in code, each in a dark and a light variant, among them the playing surfaces of hockey, basketball, baseball, football and soccer; your own colours, or your own picture; and the text, lines and boxes take the colours that read over them (4.5 to 1, held by a test).
|
|
150
|
+
- **Seed tags, codes and a "vs"** in the road look, and a team with no picture drawn as a tile with its letters, so that nothing looks missing.
|
|
151
|
+
- **Nothing sticks out**: marks, text and round names stay inside their boxes and off the connector lines, in every size and both looks, held by a geometry test.
|
|
152
|
+
- **A link on every box**: `href: (tie, side?) => address` makes each tie's box (or each team's row) a real `<a>` in the SVG with an accessible name.
|
|
153
|
+
- **Flags from a plug-in**: a team's `flag` code is drawn by the `flags` function you pass (Hata's round flags are one line), with no dependency added.
|
|
94
154
|
- **Light, dark, poster**: every colour is a CSS custom property with a fallback, so the drawing follows the page's `prefers-color-scheme` or `data-theme` and can be restyled from outside.
|
|
95
155
|
- **Team marks you supply**: a logo address or inline SVG, drawn round or square; a team with none is its initials on a colour. Round flags from [Hata](https://github.com/johnmorrisdotca/hata) go in as they are.
|
|
96
156
|
- **The champion in the centre**, with a slot for your trophy: your own SVG or image, or an emblem from [Dougu Sports](https://github.com/johnmorrisdotca/dougu-sports) (a hockey cup, a basketball trophy, a globe) through an optional plug-in, with no dependency added.
|
|
@@ -98,7 +158,7 @@ Put the string in a page (`element.innerHTML = svg`), write it to a file, or sen
|
|
|
98
158
|
- **Formats by era**: NHL, NBA, MLB, NFL, CFL, MLS and FIFA World Cup round names, series lengths and byes.
|
|
99
159
|
- **Five languages**: English, French, Japanese, Spanish and German, for every word the drawing, the tooltips, the description and the table write, with the football terms of each language; countries named by `Intl` from their ISO code; numbers and dates by `Intl`. Add a language of your own.
|
|
100
160
|
- **Deterministic**: no clock, no random number, no counter; the same input gives the same string, and ids come from a hash of the input.
|
|
101
|
-
- **No dependencies**, ESM with types, about
|
|
161
|
+
- **No dependencies**, ESM with types, about 90 kB packed.
|
|
102
162
|
|
|
103
163
|
## Use it in your project
|
|
104
164
|
|
|
@@ -110,7 +170,7 @@ npm install @johnmorrisdotca/taisen-brackets
|
|
|
110
170
|
# or: yarn add @johnmorrisdotca/taisen-brackets
|
|
111
171
|
```
|
|
112
172
|
|
|
113
|
-
It is ESM, and `require` works on Node 22.12 and later. It needs no DOM: the functions return strings.
|
|
173
|
+
It is ESM, and `require` works on Node 22.12 and later. It needs no DOM: the functions return strings. Five entry points: `@johnmorrisdotca/taisen-brackets` (everything), `/layout` (the layout and the helpers, no drawing), `/presets` (the formats), `/road` (the road to the final) and `/backgrounds` (the twenty-five backgrounds on their own).
|
|
114
174
|
|
|
115
175
|
## Examples
|
|
116
176
|
|
|
@@ -238,6 +298,7 @@ const bracket = [
|
|
|
238
298
|
|
|
239
299
|
drawBracket(bracket); // wide: 1200 by 675
|
|
240
300
|
drawBracket(bracket, { size: "square" }); // 1080 by 1080
|
|
301
|
+
drawBracket(bracket, { size: "card" }); // 1080 by 1350, a tall post, the rounds as rows
|
|
241
302
|
drawBracket(bracket, { size: "phone" }); // 390 wide, the rounds as rows
|
|
242
303
|
drawBracket(bracket, { size: { width: 800, height: 500 } });
|
|
243
304
|
drawBracket(bracket, { shape: "ltr" }); // one tree, left to right, the champion at the end
|
|
@@ -249,6 +310,46 @@ drawBracket(bracket, { theme: "none" }); // no <style>: your CSS
|
|
|
249
310
|
|
|
250
311
|
`detail` is `full` (names and scores), `compact` (abbreviations), `mark` (only the marks) or `auto`, which takes the fullest one that fits the box and the longest name.
|
|
251
312
|
|
|
313
|
+
### The road look, and the path of a team
|
|
314
|
+
|
|
315
|
+
`look: "road"` draws the bracket the way the road-to-the-final chart is drawn: large round marks with a wide ring, big bold scores in which the loser's is quiet, the rounds named on pills, and boxes as high as their width lets the marks be. A box that cannot hold names is its abbreviations, and one that cannot hold even those is two marks with each score under its mark. The classic look stays the default, and every option works in both looks. `highlightPath` takes a team's id and draws that team's way to the final as a dotted line, with the other ties quieter.
|
|
316
|
+
|
|
317
|
+
```js
|
|
318
|
+
import { drawBracket } from "@johnmorrisdotca/taisen-brackets";
|
|
319
|
+
|
|
320
|
+
const bracket = [
|
|
321
|
+
{ sides: ["A", "B"], score: [1, 0], date: "1" }, { sides: ["C", "D"], score: [0, 2], date: "1" }, { sides: ["A", "D"], score: [2, 1], date: "2" },
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
const svg = drawBracket(bracket, { look: "road", size: "card", highlightPath: "A" });
|
|
325
|
+
console.log(svg.includes("tb-look-road")); // true
|
|
326
|
+
console.log(svg.includes('viewBox="0 0 1080 1350"')); // true
|
|
327
|
+
console.log((svg.match(/class="tb-line tb-path"/g) ?? []).length); // 1
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
A team that lost on the way has a path that ends where it lost, and the third-place match is never on a path. Colours are custom properties: `--tb-mark-ring`, `--tb-mark-edge`, `--tb-panel` and `--tb-spine` are the road look's own, over the ones in Theming.
|
|
331
|
+
|
|
332
|
+
### A link on every box
|
|
333
|
+
|
|
334
|
+
`href` is called for each tie with the tie alone. An address makes the whole box a link; with none, it is called for each side (`0` and `1`) and a team's row with an address is a link of its own. Each is a real `<a href>` in the SVG with an accessible name (the tooltip's sentence, "Argentina 3, France 3 (…). Winner: Argentina", or the team's name before it), a hover and a focus ring, and a hit area. An address must be `http(s)`, relative or a `#` fragment: `javascript:`, `data:` and the rest are refused (`safeHref`), and the tie is drawn with no link.
|
|
335
|
+
|
|
336
|
+
```js
|
|
337
|
+
import { drawBracket } from "@johnmorrisdotca/taisen-brackets";
|
|
338
|
+
|
|
339
|
+
const bracket = [
|
|
340
|
+
{ sides: ["A", "B"], score: [1, 0], date: "1" }, { sides: ["C", "D"], score: [0, 2], date: "1" }, { sides: ["A", "D"], score: [2, 1], date: "2" },
|
|
341
|
+
];
|
|
342
|
+
|
|
343
|
+
// A link on each box, to the page of its series or match.
|
|
344
|
+
const boxes = drawBracket(bracket, { href: (tie) => `/match/${tie.id}` });
|
|
345
|
+
console.log(boxes.match(/<a /g).length); // 3
|
|
346
|
+
console.log(boxes.includes('href="/match/m3"')); // true
|
|
347
|
+
|
|
348
|
+
// Or a link on each team's row.
|
|
349
|
+
const rows = drawBracket(bracket, { href: (tie, side) => (side === undefined ? undefined : `/team/${tie.sides[side]?.id}`) });
|
|
350
|
+
console.log(rows.match(/<a /g).length); // 6
|
|
351
|
+
```
|
|
352
|
+
|
|
252
353
|
### Team marks, and round flags
|
|
253
354
|
|
|
254
355
|
A team's `mark` is the address of an image (`https://`, `data:image/`, or a relative path) or inline SVG. It is drawn in a circle (`markShape: "round"`, the default), a square, or left out (`"none"`). A team with no mark is its initials on its `color`.
|
|
@@ -306,6 +407,72 @@ console.log(svg.includes('class="tb-emblem"')); // true
|
|
|
306
407
|
- **Without a renderer** an emblem id draws nothing and takes no room, and nothing throws; a trophy that is inline SVG or an address is drawn exactly as before, whatever `emblems` is. A renderer that throws, or returns something that is not an `<svg>`, draws no trophy. What it returns is cleaned like any inline SVG, and the emblem's own `<title>` (English only) is left out; the drawing's description names the champion in the bracket's language. `isEmblemId(value)` tells an emblem id from SVG and from an address.
|
|
307
408
|
- **No global switch.** There is no module-level setting: a drawing is a pure function of its options, so the renderer is passed to each call.
|
|
308
409
|
|
|
410
|
+
### Flags from a plug-in
|
|
411
|
+
|
|
412
|
+
A team can carry a country code, `flag: "ca"`, and the drawing asks the `flags` function you pass for its picture: any synchronous function from a code and a request (`{ shape, size, theme }`) to an SVG string, or to nothing for a code it has no flag for. `shape` is `"round"` (or `"square"` for `markShape: "square"`), `size` the diameter in pixels and `theme` `"light"` or `"dark"` in those looks (`"dark"` for `poster`), else `"inherit"`. [`@johnmorrisdotca/hata`](https://github.com/johnmorrisdotca/hata) has a proper round frame for every flag (`flag(code, { shape: "round" })`), and, like the emblems, it is an optional peer that Taisen never imports. Its `flag()` is asynchronous and a drawing is a pure function of its options, so load the flags first and pass a lookup.
|
|
413
|
+
|
|
414
|
+
```js
|
|
415
|
+
import { drawBracket } from "@johnmorrisdotca/taisen-brackets";
|
|
416
|
+
import { flag } from "@johnmorrisdotca/hata/load";
|
|
417
|
+
|
|
418
|
+
const loaded = { AR: await flag("AR", { shape: "round" }), FR: await flag("FR", { shape: "round" }) };
|
|
419
|
+
const teams = [{ id: "ARG", name: "Argentina", flag: "AR" }, { id: "FRA", name: "France", flag: "FR" }];
|
|
420
|
+
const matchups = [{ sides: ["ARG", "FRA"], score: [3, 3], extraTime: true, pens: [4, 2] }];
|
|
421
|
+
|
|
422
|
+
const svg = drawBracket({ teams, matchups }, { flags: (code) => loaded[code], look: "road" });
|
|
423
|
+
console.log(svg.includes("<symbol")); // true
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
- **Each flag is written once**, as a `<symbol>`, and used wherever the team appears; the renderer is called once for each code, shape and size.
|
|
427
|
+
- **Without a renderer**, or when it returns nothing for a code, throws, or returns something that is not an `<svg>`, the team's `mark` is drawn, or its initials on its `color`. It never throws. What a renderer returns is cleaned like any inline SVG.
|
|
428
|
+
- **`logo` and `mark`**: in the road chart's finalists `logo` is another name for `mark`.
|
|
429
|
+
|
|
430
|
+
### The road to the final
|
|
431
|
+
|
|
432
|
+
`roadToFinal` draws the two finalists of a tournament and the way each came: the two large round marks at the top with their names; down the middle the rounds, the last at the top; on each finalist's side its matches, each as the round mark of both teams with the score between them in large type and FT, AET or the shoot-out under it; and the group stage as a compact row of small marks, codes and scores at the foot. It is the kind of graphic sports sites post for a World Cup final, drawn from your data: the arrangement, the type and the code are the package's own. It takes the same input as `drawBracket`, so one tournament draws the bracket and the road, and `team` puts either finalist on the left.
|
|
433
|
+
|
|
434
|
+
```js
|
|
435
|
+
import { drawBracket, roadToFinal } from "@johnmorrisdotca/taisen-brackets";
|
|
436
|
+
|
|
437
|
+
const teams = [
|
|
438
|
+
{ id: "NED", name: "Netherlands" }, { id: "ARG", name: "Argentina" },
|
|
439
|
+
{ id: "CRO", name: "Croatia" }, { id: "BRA", name: "Brazil" },
|
|
440
|
+
];
|
|
441
|
+
const matchups = [
|
|
442
|
+
{ sides: ["NED", "ARG"], score: [2, 2], extraTime: true, pens: [3, 4], date: "2022-12-09" },
|
|
443
|
+
{ sides: ["CRO", "BRA"], score: [1, 1], extraTime: true, pens: [4, 2], date: "2022-12-09" },
|
|
444
|
+
{ sides: ["ARG", "CRO"], score: [3, 0], date: "2022-12-13" },
|
|
445
|
+
];
|
|
446
|
+
|
|
447
|
+
const bracket = drawBracket({ teams, matchups }, { look: "road" });
|
|
448
|
+
const road = roadToFinal({ teams, matchups }, { title: "Qatar 2022", size: "wide" });
|
|
449
|
+
console.log(road.totals.champion); // Argentina
|
|
450
|
+
console.log(road.summary); // Matches played before the final: Argentina (1) and Croatia (1). The final: Argentina 3–0 Croatia (FT). Argentina won the final.
|
|
451
|
+
console.log(roadToFinal({ teams, matchups }, { team: "CRO" }).totals.finalists[0].name); // Croatia
|
|
452
|
+
console.log(road.draw({ size: "phone" }).startsWith("<svg"), bracket.startsWith("<svg")); // true true
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
A tournament's final names the finalists, each one's played ties are its road (a round-robin game, `round: 0`, is the group stage), and the final's score is written between the marks (a final not played yet says "v"). The rounds are named as the bracket names them (`roundNames`), in the language of `locale`. Or give the two finalists yourself, each a team with a `path` (the matches before the final, the earliest first) and, if you like, its `group`:
|
|
456
|
+
|
|
457
|
+
```js
|
|
458
|
+
import { roadToFinal } from "@johnmorrisdotca/taisen-brackets";
|
|
459
|
+
|
|
460
|
+
const owls = { name: "Northern Owls", abbr: "NOW", color: "#3a2d6b", path: [
|
|
461
|
+
{ round: "quarter-final", opponent: { name: "Coastal Pines", abbr: "CPN" }, score: [2, 0] },
|
|
462
|
+
{ round: "semi-final", opponent: { name: "Valley Stars", abbr: "VAL" }, score: [1, 1], decidedBy: "pens", pens: [4, 3] },
|
|
463
|
+
] };
|
|
464
|
+
const gulls = { name: "Harbour City Gulls", abbr: "HCG", color: "#1d4f91", path: [
|
|
465
|
+
{ round: "quarter-final", opponent: { name: "Summit Peaks", abbr: "SMT" }, score: [3, 1] },
|
|
466
|
+
{ round: "semi-final", opponent: { name: "Metro Bolts", abbr: "MTB" }, score: [2, 1], decidedBy: "aet" },
|
|
467
|
+
] };
|
|
468
|
+
|
|
469
|
+
const chart = roadToFinal([owls, gulls], { title: "Northern Cup 2030", final: { score: [1, 2] }, size: "square" });
|
|
470
|
+
console.log(chart.totals.rounds.join(" "), chart.totals.champion); // quarter-final semi-final Harbour City Gulls
|
|
471
|
+
console.log(chart.table().includes("<td>Semi-final</td>")); // true
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
[docs/road.md](./docs/road.md) has the fields of a match, the options, the sizes and the table.
|
|
475
|
+
|
|
309
476
|
### A table for readers the picture does not serve
|
|
310
477
|
|
|
311
478
|
```js
|
|
@@ -337,7 +504,8 @@ The string is built from your data, with every name escaped; do not put untruste
|
|
|
337
504
|
|
|
338
505
|
Everything is exported from `@johnmorrisdotca/taisen-brackets`; the layout part from `/layout`, the formats from `/presets`. The types are in the package; each function's doc comment has its details.
|
|
339
506
|
|
|
340
|
-
- `drawBracket(input, options?)`: the SVG string. `input` is a list of matchups, `{ matchups, teams }` or a layout. Options: `size`, `orientation`, `shape`, `detail`, `theme`, `background`, `title`, `subtitle`, `roundNames`, `roundLabels`, `scores`, `seeds`, `markShape`, `champion`, `trophy`, `emblems`, `emblemStyle`, `emblemTint`, `thirdPlace`, `winnerHighlight`, `locale`, `words`, `teamName`, `groupOrder`, `id`, `className`.
|
|
507
|
+
- `drawBracket(input, options?)`: the SVG string. `input` is a list of matchups, `{ matchups, teams }` or a layout. Options: `size`, `orientation`, `shape`, `detail`, `theme`, `background` (a preset's name, a tuned preset or your own picture), `vs`, `codes`, `title`, `subtitle`, `roundNames`, `roundLabels`, `scores`, `seeds`, `markShape`, `champion`, `trophy`, `emblems`, `emblemStyle`, `emblemTint`, `thirdPlace`, `winnerHighlight`, `look`, `href`, `tieClass`, `highlightPath`, `flags`, `flagHref`, `locale`, `words`, `teamName`, `groupOrder`, `id`, `className`.
|
|
508
|
+
- `roadToFinal(input, options?)`: the road to the final of a tournament or of two finalists: `{ options, title, summary, totals, draw(extra?), table(options?) }`. `input` is what `drawBracket` takes, or the two finalists with their paths. Options: `team`, `title`, `subtitle`, `final`, `labels`, `groupLabel`, `size`, `winner`, `codes`, `endnote`, and `locale`, `words`, `teamName`, `roundNames`, `markShape`, `flags`, `theme`, `background`, `id`, `className` as for `drawBracket`. The types `RoadChart`, `RoadFinalist`, `RoadMatch`, `RoadOptions` and the rest are exported too; it is also the one export of `/road`.
|
|
341
509
|
- `layoutBracket(input, options?)`: the rounds, halves, rows, byes and results, without drawing. `shape: "ltr"` and `groupOrder` are its options.
|
|
342
510
|
- `bracketTable(input, options?)`: the HTML table.
|
|
343
511
|
- `describeBracket(input, options?)`: the sentence a screen reader gets.
|
|
@@ -348,8 +516,9 @@ Everything is exported from `@johnmorrisdotca/taisen-brackets`; the layout part
|
|
|
348
516
|
- `isEmblemId(value)`, and the types `EmblemRenderer`, `EmblemRequest`, `EmblemStyle`: the trophy emblems (see "Emblems for the trophy").
|
|
349
517
|
- `roundName(remaining)`, `roundNamesFor(count)`, `roundLabel(layout, round, names, words)`: round names.
|
|
350
518
|
- `LOCALES`, `LOCALE_CODES`, `defineLocale`, `localeOf`, `voiceOf`, `countryName`: the languages (see Languages), a language of your own, the formats and the names of countries from `Intl`. `localizedLayout(layout, voice)` is a layout with the team names in a language.
|
|
351
|
-
- `SIZES`, `THEMES`, `WORDS`, `VERSION`: the sizes, the looks' colours, the English words, the version.
|
|
352
|
-
- `
|
|
519
|
+
- `SIZES`, `THEMES`, `ROAD_THEMES`, `WORDS`, `VERSION`: the sizes (`wide`, `square`, `card`, `phone`), the looks' colours, the road look's extra colours, the English words, the version.
|
|
520
|
+
- `BACKGROUND_PRESETS`, `backgroundSvg(preset, options)`, `contrastRatio(a, b)`, `resolveBackground(option, theme, width, height, prefix)`, `FAMILY_BACKGROUNDS_VERSION`: the twenty-five backgrounds, one drawn on its own with the ink that reads over it, the contrast of two colours, the `background` option read, and the version of the file shared with the family (see "Backgrounds"). Also its own entry point, `/backgrounds`.
|
|
521
|
+
- `cleanSvg(markup)`, `safeUrl(url)`, `safeHref(url)`, `abbreviate(name)`: the rules for marks and for the addresses of links, and the short name made from a name. The types `FlagRenderer`, `FlagRequest` and `FlagShape` are the flags plug-in's (see "Flags from a plug-in").
|
|
353
522
|
|
|
354
523
|
## Languages
|
|
355
524
|
|
|
@@ -437,6 +606,7 @@ Any current browser, and Node 22 or later. The package makes no network request
|
|
|
437
606
|
- More languages (Portuguese, Italian, Korean, Chinese), and native review of the Spanish and German words.
|
|
438
607
|
- A custom element and a React helper, as in the other packages of the family.
|
|
439
608
|
- A PNG renderer (through an optional peer).
|
|
609
|
+
- Planned for 0.6.0: new layouts (`rows`, `shields`, `update`, `columns`, `play-in`, a series card, conference headers) and a group-stage table drawing, which John Morris asked for; and a lone final centred, and one stylesheet for several drawings. See [docs/roadmap.md](./docs/roadmap.md); the pictures that prompted them are listed in [docs/references.md](./docs/references.md).
|
|
440
610
|
|
|
441
611
|
## The name
|
|
442
612
|
|
|
@@ -453,7 +623,7 @@ The layout began as the playoff bracket of [spxn.com](https://spxn.com), the his
|
|
|
453
623
|
### The family
|
|
454
624
|
|
|
455
625
|
<!-- family:start (made by scripts/family-readme.mjs from scripts/family-template.mjs; change those, not this) -->
|
|
456
|
-
Taisen Brackets is one of twenty-
|
|
626
|
+
Taisen Brackets is one of twenty-eight packages, each made for the same site, each at
|
|
457
627
|
[github.com/johnmorrisdotca](https://github.com/johnmorrisdotca). The code of every one is MIT.
|
|
458
628
|
|
|
459
629
|
- [Korokoro](https://github.com/johnmorrisdotca/korokoro) (コロコロ): dice, with notation, exact odds, real sounds and the dice of many games. [Demo](https://johnmorrisdotca.github.io/korokoro/).
|
|
@@ -482,8 +652,10 @@ Taisen Brackets is one of twenty-six packages, each made for the same site, each
|
|
|
482
652
|
- [Houseki](https://github.com/johnmorrisdotca/houseki) (宝石): gem and stone matching puzzles: falling triplets, stone collapse, colour chains and gem swap. [Demo](https://johnmorrisdotca.github.io/houseki/).
|
|
483
653
|
- [Kyogijo Stadiums](https://github.com/johnmorrisdotca/kyogijo-stadiums) (競技場): sports fields, rinks, courts and ballparks drawn to scale as SVG, with each team and the score on them. [Demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/).
|
|
484
654
|
- [Taisen Brackets](https://github.com/johnmorrisdotca/taisen-brackets) (対戦): a sport-agnostic tournament bracket, drawn as SVG, from a list of ties. [Demo](https://johnmorrisdotca.github.io/taisen-brackets/).
|
|
655
|
+
- [Dougu Sports](https://github.com/johnmorrisdotca/dougu-sports) (道具): sports emblems as SVG: trophies, medals, crests, and the ball and gear of every sport, with optional 3D turning. [Demo](https://johnmorrisdotca.github.io/dougu-sports/).
|
|
656
|
+
- [Senshu Jerseys](https://github.com/johnmorrisdotca/senshu-jerseys) (選手): sports jerseys, sweaters, kits, caps, helmets and retired-number banners as SVG, front and back, with optional 2.5D turning. [Demo](https://johnmorrisdotca.github.io/senshu-jerseys/).
|
|
485
657
|
|
|
486
|
-
**This package is Taisen Brackets.** The demos of all twenty-
|
|
658
|
+
**This package is Taisen Brackets.** The demos of all twenty-eight share one header and footer, so each links the rest.
|
|
487
659
|
<!-- family:end -->
|
|
488
660
|
|
|
489
661
|
## Development
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BackgroundInks } from "./theme.ts";
|
|
2
|
+
import type { ThemeName } from "./types.ts";
|
|
3
|
+
export interface ResolvedBackground {
|
|
4
|
+
/** For the drawing's `<defs>`. */
|
|
5
|
+
defs: string;
|
|
6
|
+
/** Drawn over the plain `.tb-bg` rectangle, under everything else. */
|
|
7
|
+
body: string;
|
|
8
|
+
/** The colours to draw over it: one for a look that is light or dark, both for `auto`. */
|
|
9
|
+
inks: BackgroundInks;
|
|
10
|
+
/** Both pictures are in the drawing and the stylesheet shows the one that fits. */
|
|
11
|
+
auto: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Whether an option asks for more than the plain colour: a preset, a tuned preset or an image. */
|
|
14
|
+
export declare function hasBackground(option: unknown): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Reads the `background` option for a drawing `width` by `height` in the look `theme`, with `prefix` for the ids. Returns `null` when the option is `true`, `false` or left out
|
|
17
|
+
* (a plain colour, or none). An unknown preset, a bad colour list or an image address that is not http(s), relative or `data:image/…` throws a `RangeError`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveBackground(option: unknown, theme: ThemeName, width: number, height: number, prefix: string): ResolvedBackground | null;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// The `background` option: a preset's name, a preset tuned, or the caller's own picture, turned into the SVG to draw under everything and the colours to draw over it.
|
|
2
|
+
// The drawing of the presets is `family-backgrounds.ts`, which is shared with the other packages of the family; this file only reads the option and writes the picture's frame.
|
|
3
|
+
import { BACKGROUND_PRESETS, backgroundSvg } from "./family-backgrounds.js";
|
|
4
|
+
import { safeUrl } from "./marks.js";
|
|
5
|
+
import { esc, num } from "./text.js";
|
|
6
|
+
const isPreset = (name) => typeof name === "string" && BACKGROUND_PRESETS.includes(name);
|
|
7
|
+
/** Whether an option asks for more than the plain colour: a preset, a tuned preset or an image. */
|
|
8
|
+
export function hasBackground(option) {
|
|
9
|
+
return typeof option === "string" || (typeof option === "object" && option !== null);
|
|
10
|
+
}
|
|
11
|
+
const fail = (message) => {
|
|
12
|
+
throw new RangeError(`taisen-brackets: ${message}`);
|
|
13
|
+
};
|
|
14
|
+
/** The ink for a picture of the caller's own: a plain dark or pale ground, since the image under the veil is not known. */
|
|
15
|
+
function inkForImage(theme, id) {
|
|
16
|
+
const colors = theme === "dark" ? ["#12161c", "#12161c", "#c9d2e0", "#c9d2e0"] : ["#f4f4f1", "#f4f4f1", "#4a5568", "#4a5568"];
|
|
17
|
+
return backgroundSvg("midnight", { width: 8, height: 8, theme, colors, id }).ink;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Reads the `background` option for a drawing `width` by `height` in the look `theme`, with `prefix` for the ids. Returns `null` when the option is `true`, `false` or left out
|
|
21
|
+
* (a plain colour, or none). An unknown preset, a bad colour list or an image address that is not http(s), relative or `data:image/…` throws a `RangeError`.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveBackground(option, theme, width, height, prefix) {
|
|
24
|
+
if (option === undefined || typeof option === "boolean" || option === null)
|
|
25
|
+
return null;
|
|
26
|
+
const spec = typeof option === "string" ? { preset: option } : option;
|
|
27
|
+
if (typeof option !== "string" && typeof option !== "object")
|
|
28
|
+
return fail(`background is a preset's name or an object, not ${typeof option}`);
|
|
29
|
+
const image = spec.image;
|
|
30
|
+
if (image === undefined && spec.preset !== undefined && !isPreset(spec.preset))
|
|
31
|
+
fail(`unknown background "${String(spec.preset)}": use one of ${BACKGROUND_PRESETS.join(", ")}`);
|
|
32
|
+
if (spec.colors !== undefined && (!Array.isArray(spec.colors) || spec.colors.some((c) => typeof c !== "string")))
|
|
33
|
+
fail("background.colors is a list of colours as text");
|
|
34
|
+
for (const key of ["angle", "intensity", "seed", "dim"])
|
|
35
|
+
if (spec[key] !== undefined && !Number.isFinite(spec[key]))
|
|
36
|
+
fail(`background.${key} is a number`);
|
|
37
|
+
// Which pictures to draw: the one the look is, or the one asked for; `auto` draws both.
|
|
38
|
+
const want = spec.theme ? [spec.theme] : theme === "light" ? ["light"] : theme === "dark" || theme === "poster" || theme === "none" ? ["dark"] : ["light", "dark"];
|
|
39
|
+
const auto = want.length === 2;
|
|
40
|
+
const parts = want.map((t) => {
|
|
41
|
+
const id = auto ? `${prefix}${t === "light" ? "l" : "d"}` : `${prefix}b`;
|
|
42
|
+
if (image !== undefined) {
|
|
43
|
+
if (typeof image !== "string" || !safeUrl(image))
|
|
44
|
+
return fail("background.image must be an http(s) address, a relative path or a data:image/ address");
|
|
45
|
+
const dim = Math.min(1, Math.max(0, spec.dim ?? 0.5));
|
|
46
|
+
const fitName = spec.fit === "contain" ? "meet" : "slice";
|
|
47
|
+
return {
|
|
48
|
+
t,
|
|
49
|
+
defs: "",
|
|
50
|
+
body: `<image href="${esc(image)}" x="0" y="0" width="${num(width)}" height="${num(height)}" preserveAspectRatio="xMidYMid ${fitName}"/><rect width="${num(width)}" height="${num(height)}" fill="${t === "dark" ? "#000" : "#fff"}" opacity="${num(dim)}"/>`,
|
|
51
|
+
ink: inkForImage(t, id),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const made = backgroundSvg(spec.preset ?? "midnight", { width, height, theme: t, colors: spec.colors, angle: spec.angle, intensity: spec.intensity, seed: spec.seed, id });
|
|
55
|
+
return { t, ...made };
|
|
56
|
+
});
|
|
57
|
+
const inks = {};
|
|
58
|
+
for (const p of parts)
|
|
59
|
+
inks[p.t] = p.ink;
|
|
60
|
+
return {
|
|
61
|
+
defs: parts.map((p) => p.defs).join(""),
|
|
62
|
+
body: auto ? parts.map((p) => `<g class="${p.t === "light" ? "tb-bgl" : "tb-bgd"}">${p.body}</g>`).join("") : parts[0].body,
|
|
63
|
+
inks,
|
|
64
|
+
auto,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BACKGROUND_PRESETS, FAMILY_BACKGROUNDS_VERSION, backgroundSvg, contrastRatio, luminance, parseColor, toHex } from "./family-backgrounds.js";
|