@johnmorrisdotca/taisen-brackets 0.2.1 → 0.3.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 +19 -1
- package/NOTICE.md +1 -0
- package/README.md +22 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/marks.d.ts +14 -1
- package/dist/marks.js +36 -0
- package/dist/presets-entry.d.ts +1 -1
- package/dist/presets-entry.js +1 -1
- package/dist/presets.d.ts +7 -0
- package/dist/presets.js +15 -2
- package/dist/render.js +8 -5
- package/dist/theme.js +1 -0
- package/dist/types.d.ts +28 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,23 @@ All notable changes to this project are written here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.3.0] - 2026-10-14
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Emblems for the trophy.** The `trophy` option also takes an emblem id such as `"trophy:hockey-cup"`, drawn by a function passed as the new `emblems` option: any `(id, { style, size, tint, theme }) => svg`, such as `renderEmblem` from `@johnmorrisdotca/dougu-sports/emblems`. The renderer is asked for the size the trophy has in the drawing and for `theme: "light"` or `"dark"` in those looks (`"dark"` for `poster`), else `"inherit"`; `emblemStyle` (`metal`, `line`, `color`) and `emblemTint` are passed on. What it returns is cleaned like any inline SVG, drawn in a nested `<svg class="tb-emblem">` that takes its ink from `--tb-ink`, and its English `<title>` is left out. With no renderer an emblem id draws nothing and takes no room; a renderer that throws or returns something that is not an `<svg>` draws no trophy.
|
|
14
|
+
- `LEAGUE_EMBLEMS` and `PlayoffFormat.emblem`: each league's default emblem (NHL `trophy:hockey-cup`, NBA `trophy:basketball`, MLB `trophy:baseball`, NFL `trophy:football`, CFL `trophy:canadian-bowl`, MLS `trophy:cup`, World Cup `trophy:soccer-globe`), used only when a renderer is plugged in; `isEmblemId(value)`; the types `EmblemRenderer`, `EmblemRequest` and `EmblemStyle`.
|
|
15
|
+
- `@johnmorrisdotca/dougu-sports` as an optional peer dependency (and a pinned development dependency, for the tests and the demo). The package still has no dependencies, imports nothing from it, and an inline SVG or an address given as `trophy` is drawn exactly as before.
|
|
16
|
+
- The demo draws each league's emblem for its champion, with a "Trophy" switch and a Metal, Line or Colour choice; the emblems are loaded one module each from the Dougu Sports files the Pages build copies beside the page.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Every drawing's stylesheet has one more rule, `.tb-emblem{color:var(--tb-ink, …)}`.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- `docs/formats.md` titled the MLS section "undefined".
|
|
25
|
+
|
|
9
26
|
## [0.2.1] - 2026-10-14
|
|
10
27
|
|
|
11
28
|
### Added
|
|
@@ -52,7 +69,8 @@ First version.
|
|
|
52
69
|
- 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.
|
|
53
70
|
- Playoff formats checked against English Wikipedia, each era citing its articles (docs/formats.md). The year is the year the playoffs are played in.
|
|
54
71
|
|
|
55
|
-
[Unreleased]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.
|
|
72
|
+
[Unreleased]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.3.0...HEAD
|
|
73
|
+
[0.3.0]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.2.1...v0.3.0
|
|
56
74
|
[0.2.1]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.2.0...v0.2.1
|
|
57
75
|
[0.2.0]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.1.1...v0.2.0
|
|
58
76
|
[0.1.1]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.1.0...v0.1.1
|
package/NOTICE.md
CHANGED
|
@@ -16,6 +16,7 @@ The code of this package is under the MIT licence (see [LICENSE](LICENSE)). The
|
|
|
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
18
|
| The demo's national flags (in `demo/flags-data.js`) | [Hata](https://github.com/johnmorrisdotca/hata) 1.5.0 (MIT code), a development dependency used only by `scripts/make-flags.mjs`. 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
|
+
| 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 |
|
|
19
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 |
|
|
20
21
|
| 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) |
|
|
21
22
|
| The demo's page | Written for this package, with the family stylesheet `demo/family.css` | MIT |
|
package/README.md
CHANGED
|
@@ -93,7 +93,7 @@ Put the string in a page (`element.innerHTML = svg`), write it to a file, or sen
|
|
|
93
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.
|
|
94
94
|
- **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
95
|
- **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
|
-
- **The champion in the centre**, with a slot for your trophy.
|
|
96
|
+
- **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.
|
|
97
97
|
- **Accessible**: `role="img"` with a title and a description, a tooltip sentence on every tie, and `bracketTable`.
|
|
98
98
|
- **Formats by era**: NHL, NBA, MLB, NFL, CFL, MLS and FIFA World Cup round names, series lengths and byes.
|
|
99
99
|
- **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.
|
|
@@ -288,6 +288,24 @@ const svg = drawBracket([{ sides: ["A", "B"], score: [2, 1] }], { trophy });
|
|
|
288
288
|
console.log(svg.includes('class="tb-gold"')); // true
|
|
289
289
|
```
|
|
290
290
|
|
|
291
|
+
### Emblems for the trophy
|
|
292
|
+
|
|
293
|
+
The trophy can also be an emblem id, such as `"trophy:hockey-cup"`, drawn by a function you plug in as `emblems`: any function from an id and a request (`{ style, size, tint, theme }`) to an SVG string. [`@johnmorrisdotca/dougu-sports`](https://github.com/johnmorrisdotca/dougu-sports) ([gallery](https://johnmorrisdotca.github.io/dougu-sports/)) has one, `renderEmblem`, and a cup, a basketball, a baseball, a football, a Canadian-football bowl and a soccer globe among its trophies. Taisen does not import it: it is an optional peer dependency, and the package has no dependencies of its own.
|
|
294
|
+
|
|
295
|
+
```js
|
|
296
|
+
import { drawBracket } from "@johnmorrisdotca/taisen-brackets";
|
|
297
|
+
import { renderEmblem } from "@johnmorrisdotca/dougu-sports/emblems";
|
|
298
|
+
|
|
299
|
+
const data = [{ sides: ["Aurora", "Harbour"], score: [2, 1] }];
|
|
300
|
+
const svg = drawBracket(data, { emblems: renderEmblem, trophy: "trophy:hockey-cup" });
|
|
301
|
+
console.log(svg.includes('class="tb-emblem"')); // true
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
- **The look follows the bracket.** The renderer is asked for the size the trophy has in the drawing, and for `theme: "light"` or `"dark"` when the bracket is in that look (`"dark"` for `poster`), else `"inherit"`, where the drawing's own colour (`--tb-ink`) is the ink. `emblemStyle` (`"metal"`, the default, `"line"` or `"color"`) and `emblemTint` (a hex colour) are passed on.
|
|
305
|
+
- **Each league has a default.** `LEAGUE_EMBLEMS` names one (`nhl` the hockey cup, `nba` the basketball, `mlb` the baseball, `nfl` the football, `cfl` the Canadian bowl, `mls` a cup, `worldcup` the soccer globe), and `playoffFormat(league, year).emblem` carries it: `trophy: playoffFormat("nhl", 2024).emblem`.
|
|
306
|
+
- **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
|
+
- **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
|
+
|
|
291
309
|
### A table for readers the picture does not serve
|
|
292
310
|
|
|
293
311
|
```js
|
|
@@ -319,14 +337,15 @@ The string is built from your data, with every name escaped; do not put untruste
|
|
|
319
337
|
|
|
320
338
|
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.
|
|
321
339
|
|
|
322
|
-
- `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`, `thirdPlace`, `winnerHighlight`, `locale`, `words`, `teamName`, `groupOrder`, `id`, `className`.
|
|
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`.
|
|
323
341
|
- `layoutBracket(input, options?)`: the rounds, halves, rows, byes and results, without drawing. `shape: "ltr"` and `groupOrder` are its options.
|
|
324
342
|
- `bracketTable(input, options?)`: the HTML table.
|
|
325
343
|
- `describeBracket(input, options?)`: the sentence a screen reader gets.
|
|
326
344
|
- `singleElimination(teams, options?)`, `advanceWinners(matchups)`, `standardSeeds(size)`: brackets for any number of teams.
|
|
327
345
|
- `matchupsFromGames(games, options)`: matchups from a flat list of games.
|
|
328
346
|
- `resolveMatchup`, `collectTeams`, `describeTie`, `noteParts`, `noteLong`: one tie's result, the teams of a bracket, a tie in a sentence.
|
|
329
|
-
- `playoffFormat(league, year)`, `playoffEras(league)`, `LEAGUES`: the formats by era (`nhl`, `nba`, `mlb`, `nfl`, `cfl`, `mls`, `worldcup`).
|
|
347
|
+
- `playoffFormat(league, year)`, `playoffEras(league)`, `LEAGUES`: the formats by era (`nhl`, `nba`, `mlb`, `nfl`, `cfl`, `mls`, `worldcup`). `LEAGUE_EMBLEMS`: each league's default emblem id.
|
|
348
|
+
- `isEmblemId(value)`, and the types `EmblemRenderer`, `EmblemRequest`, `EmblemStyle`: the trophy emblems (see "Emblems for the trophy").
|
|
330
349
|
- `roundName(remaining)`, `roundNamesFor(count)`, `roundLabel(layout, round, names, words)`: round names.
|
|
331
350
|
- `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.
|
|
332
351
|
- `SIZES`, `THEMES`, `WORDS`, `VERSION`: the sizes, the looks' colours, the English words, the version.
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ export { bracketTable } from "./table.ts";
|
|
|
4
4
|
export type { TableOptions } from "./table.ts";
|
|
5
5
|
export { THEMES } from "./theme.ts";
|
|
6
6
|
export type { Palette } from "./theme.ts";
|
|
7
|
-
export { cleanSvg, safeUrl } from "./marks.ts";
|
|
7
|
+
export { cleanSvg, safeUrl, isEmblemId } from "./marks.ts";
|
|
8
8
|
export { abbreviate } from "./text.ts";
|
|
9
9
|
export { VERSION } from "./version.ts";
|
|
10
|
-
export { playoffFormat, playoffEras, LEAGUES } from "./presets.ts";
|
|
10
|
+
export { playoffFormat, playoffEras, LEAGUES, LEAGUE_EMBLEMS } from "./presets.ts";
|
|
11
11
|
export type { League, PlayoffFormat, PlayoffRound } from "./presets.ts";
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ export * from "./layout-entry.js";
|
|
|
2
2
|
export { drawBracket, describeBracket, localizedLayout, SIZES } from "./render.js";
|
|
3
3
|
export { bracketTable } from "./table.js";
|
|
4
4
|
export { THEMES } from "./theme.js";
|
|
5
|
-
export { cleanSvg, safeUrl } from "./marks.js";
|
|
5
|
+
export { cleanSvg, safeUrl, isEmblemId } from "./marks.js";
|
|
6
6
|
export { abbreviate } from "./text.js";
|
|
7
7
|
export { VERSION } from "./version.js";
|
|
8
|
-
export { playoffFormat, playoffEras, LEAGUES } from "./presets.js";
|
|
8
|
+
export { playoffFormat, playoffEras, LEAGUES, LEAGUE_EMBLEMS } from "./presets.js";
|
package/dist/marks.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Team } from "./types.ts";
|
|
1
|
+
import type { DrawOptions, Team } from "./types.ts";
|
|
2
2
|
/**
|
|
3
3
|
* Takes inline SVG markup from a caller and makes it safe to put inside a drawing: the outer `<svg>`'s viewBox is kept,
|
|
4
4
|
* and scripts, event handlers, `<style>`, `<foreignObject>`, comments and `javascript:` addresses are removed. Returns the
|
|
@@ -8,6 +8,19 @@ export declare function cleanSvg(markup: string): {
|
|
|
8
8
|
viewBox: string;
|
|
9
9
|
inner: string;
|
|
10
10
|
} | null;
|
|
11
|
+
/** Whether a `trophy` value names an emblem (`"trophy:hockey-cup"`, `"medal:gold/line"`) rather than being inline SVG or an image address. */
|
|
12
|
+
export declare function isEmblemId(value: string): boolean;
|
|
13
|
+
/** Whether `drawBracket` has something to draw for this `trophy` and `emblems`: an emblem id needs a renderer; SVG and addresses do not. */
|
|
14
|
+
export declare function hasTrophy(o: Pick<DrawOptions, "trophy" | "emblems">): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* The emblem a renderer makes for an id, cleaned like any inline SVG and with its `<title>` removed (the drawing says what it shows in its own
|
|
17
|
+
* words), or `null` when the renderer throws or returns something that is not an `<svg>`. The look decides the ink: `light` and `dark` are
|
|
18
|
+
* forced, `poster` is dark, and `auto` and `none` are `inherit`, which the drawing's stylesheet colours from `--tb-ink`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function drawEmblem(id: string, size: number, o: Pick<DrawOptions, "emblems" | "emblemStyle" | "emblemTint" | "theme">): {
|
|
21
|
+
viewBox: string;
|
|
22
|
+
inner: string;
|
|
23
|
+
} | null;
|
|
11
24
|
/** Whether an image address is one a drawing will load: http(s), a relative path, or a `data:image/`. */
|
|
12
25
|
export declare function safeUrl(url: string): boolean;
|
|
13
26
|
export interface MarkContext {
|
package/dist/marks.js
CHANGED
|
@@ -24,6 +24,42 @@ export function cleanSvg(markup) {
|
|
|
24
24
|
.replace(/(\s(?:xlink:)?href\s*=\s*)(["'])\s*javascript:[^"']*\2/gi, "$1$2#$2");
|
|
25
25
|
return { viewBox: box, inner };
|
|
26
26
|
}
|
|
27
|
+
const EMBLEM_ID = /^[a-z][a-z0-9-]*:[a-z0-9][a-z0-9-]*(?:\/(?:metal|line|color))?$/;
|
|
28
|
+
/** Whether a `trophy` value names an emblem (`"trophy:hockey-cup"`, `"medal:gold/line"`) rather than being inline SVG or an image address. */
|
|
29
|
+
export function isEmblemId(value) {
|
|
30
|
+
return EMBLEM_ID.test(value.trim());
|
|
31
|
+
}
|
|
32
|
+
/** Whether `drawBracket` has something to draw for this `trophy` and `emblems`: an emblem id needs a renderer; SVG and addresses do not. */
|
|
33
|
+
export function hasTrophy(o) {
|
|
34
|
+
const t = o.trophy?.trim();
|
|
35
|
+
if (!t)
|
|
36
|
+
return false;
|
|
37
|
+
return isEmblemId(t) ? typeof o.emblems === "function" : true;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The emblem a renderer makes for an id, cleaned like any inline SVG and with its `<title>` removed (the drawing says what it shows in its own
|
|
41
|
+
* words), or `null` when the renderer throws or returns something that is not an `<svg>`. The look decides the ink: `light` and `dark` are
|
|
42
|
+
* forced, `poster` is dark, and `auto` and `none` are `inherit`, which the drawing's stylesheet colours from `--tb-ink`.
|
|
43
|
+
*/
|
|
44
|
+
export function drawEmblem(id, size, o) {
|
|
45
|
+
if (typeof o.emblems !== "function")
|
|
46
|
+
return null;
|
|
47
|
+
const look = o.theme ?? "auto";
|
|
48
|
+
const request = { style: o.emblemStyle ?? "metal", size, theme: look === "light" ? "light" : look === "dark" || look === "poster" ? "dark" : "inherit" };
|
|
49
|
+
if (o.emblemTint)
|
|
50
|
+
request.tint = o.emblemTint;
|
|
51
|
+
let markup;
|
|
52
|
+
try {
|
|
53
|
+
markup = o.emblems(id.trim(), request);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (typeof markup !== "string")
|
|
59
|
+
return null;
|
|
60
|
+
const svg = cleanSvg(markup);
|
|
61
|
+
return svg ? { viewBox: svg.viewBox, inner: svg.inner.replace(/<title\b[\s\S]*?<\/title\s*>/gi, "") } : null;
|
|
62
|
+
}
|
|
27
63
|
/** Readable text on a team colour: white on a dark one, near-black on a light one; `null` when the colour is not a hex one this can read. */
|
|
28
64
|
function inkOn(color) {
|
|
29
65
|
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color.trim())?.[1];
|
package/dist/presets-entry.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { playoffFormat, playoffEras, LEAGUES } from "./presets.ts";
|
|
1
|
+
export { playoffFormat, playoffEras, LEAGUES, LEAGUE_EMBLEMS } from "./presets.ts";
|
|
2
2
|
export type { League, PlayoffFormat, PlayoffRound } from "./presets.ts";
|
|
3
3
|
export { roundName, roundNamesFor } from "./rounds.ts";
|
package/dist/presets-entry.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { playoffFormat, playoffEras, LEAGUES } from "./presets.js";
|
|
1
|
+
export { playoffFormat, playoffEras, LEAGUES, LEAGUE_EMBLEMS } from "./presets.js";
|
|
2
2
|
export { roundName, roundNamesFor } from "./rounds.js";
|
package/dist/presets.d.ts
CHANGED
|
@@ -29,9 +29,16 @@ export interface PlayoffFormat {
|
|
|
29
29
|
/** The names of the two halves, left first, or null where the bracket is not split in two. */
|
|
30
30
|
groups: readonly [string, string] | null;
|
|
31
31
|
note?: string;
|
|
32
|
+
/** The league's default emblem id (`LEAGUE_EMBLEMS`), for `trophy` with an `emblems` renderer. */
|
|
33
|
+
emblem: string;
|
|
32
34
|
/** The English Wikipedia articles this entry was checked against (CC BY-SA 4.0; only facts are used). */
|
|
33
35
|
sources: readonly string[];
|
|
34
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The emblem each league's champion gets by default: an id of `@johnmorrisdotca/dougu-sports` (generic forms, no league's own mark). Pass it as
|
|
39
|
+
* `trophy` together with an `emblems` renderer; with no renderer it draws nothing. A cup for MLS because the MLS Cup is one.
|
|
40
|
+
*/
|
|
41
|
+
export declare const LEAGUE_EMBLEMS: Readonly<Record<League, string>>;
|
|
35
42
|
/** The leagues that have a format here. */
|
|
36
43
|
export declare const LEAGUES: readonly League[];
|
|
37
44
|
/**
|
package/dist/presets.js
CHANGED
|
@@ -4,6 +4,19 @@
|
|
|
4
4
|
* Wikipedia articles in its `sources` (2026-10-13); where a season did something of its own (a strike, a pandemic) it has
|
|
5
5
|
* its own entry. The year is the year the playoffs are played in (the NHL's and the NBA's 2024 are the 2024 playoffs, of the 2023-24 season), except for the NFL, whose year is its season's: 2024 is the season played in 2024, with playoffs in January and February 2025.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* The emblem each league's champion gets by default: an id of `@johnmorrisdotca/dougu-sports` (generic forms, no league's own mark). Pass it as
|
|
9
|
+
* `trophy` together with an `emblems` renderer; with no renderer it draws nothing. A cup for MLS because the MLS Cup is one.
|
|
10
|
+
*/
|
|
11
|
+
export const LEAGUE_EMBLEMS = Object.freeze({
|
|
12
|
+
nhl: "trophy:hockey-cup",
|
|
13
|
+
nba: "trophy:basketball",
|
|
14
|
+
mlb: "trophy:baseball",
|
|
15
|
+
nfl: "trophy:football",
|
|
16
|
+
cfl: "trophy:canadian-bowl",
|
|
17
|
+
mls: "trophy:cup",
|
|
18
|
+
worldcup: "trophy:soccer-globe",
|
|
19
|
+
});
|
|
7
20
|
/** The address of English Wikipedia articles, by title. */
|
|
8
21
|
const wiki = (...titles) => titles.map((t) => `https://en.wikipedia.org/wiki/${t.replace(/ /g, "_")}`);
|
|
9
22
|
const r = (name, bestOf = 7) => ({ name, bestOf });
|
|
@@ -76,9 +89,9 @@ export function playoffFormat(league, year) {
|
|
|
76
89
|
const { years: _years, except: _except, ...rest } = hit;
|
|
77
90
|
void _years;
|
|
78
91
|
void _except;
|
|
79
|
-
return { ...rest, year };
|
|
92
|
+
return { ...rest, year, emblem: LEAGUE_EMBLEMS[league] };
|
|
80
93
|
}
|
|
81
94
|
/** Every era of a league, oldest first. */
|
|
82
95
|
export function playoffEras(league) {
|
|
83
|
-
return ERAS[league].map(({ years: _y, except: _e, ...rest }) => { void _y; void _e; return { ...rest, year: rest.from }; });
|
|
96
|
+
return ERAS[league].map(({ years: _y, except: _e, ...rest }) => { void _y; void _e; return { ...rest, year: rest.from, emblem: LEAGUE_EMBLEMS[league] }; });
|
|
84
97
|
}
|
package/dist/render.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { layoutBracket } from "./layout.js";
|
|
2
2
|
import { abbrOf, describeTie, noteParts } from "./matchup.js";
|
|
3
|
-
import { cleanSvg, clipId, drawMark, safeUrl } from "./marks.js";
|
|
3
|
+
import { cleanSvg, clipId, drawEmblem, drawMark, hasTrophy as hasTrophyOf, isEmblemId, safeUrl } from "./marks.js";
|
|
4
4
|
import { roundLabel } from "./rounds.js";
|
|
5
5
|
import { styleFor } from "./theme.js";
|
|
6
6
|
import { esc, fit, hash, num, textWidth } from "./text.js";
|
|
@@ -88,7 +88,7 @@ function planBracket(layout, o, v, size, vertical, ctx) {
|
|
|
88
88
|
const halves = layout.shape === "halves";
|
|
89
89
|
const nc = layout.columns.length;
|
|
90
90
|
const rows = layout.rows;
|
|
91
|
-
const hasTrophy =
|
|
91
|
+
const hasTrophy = hasTrophyOf(o) && champ;
|
|
92
92
|
// ----- slots along the rounds' axis (t): the columns of the left half, the centre, the columns of the right half.
|
|
93
93
|
// Horizontal: t is x. Vertical: t is y, and the left half is the top.
|
|
94
94
|
const slotCount = halves ? 2 * nc + 1 : nc + (champ || layout.finalIndex !== null ? 1 : 0);
|
|
@@ -372,10 +372,13 @@ function championBlock(team, cx, top, mark, trophyH, nameFont, o, v, ctx, poster
|
|
|
372
372
|
let y = top;
|
|
373
373
|
if (o.trophy && trophyH) {
|
|
374
374
|
const t = o.trophy.trim();
|
|
375
|
-
const
|
|
376
|
-
|
|
375
|
+
const emblem = isEmblemId(t) ? drawEmblem(t, Number(num(trophyH)), o) : null;
|
|
376
|
+
const svg = emblem ? null : t.startsWith("<") ? cleanSvg(t) : null;
|
|
377
|
+
if (emblem)
|
|
378
|
+
out.push(`<svg class="tb-emblem" x="${num(cx - trophyH / 2)}" y="${num(y)}" width="${num(trophyH)}" height="${num(trophyH)}" viewBox="${esc(emblem.viewBox)}" preserveAspectRatio="xMidYMid meet" aria-hidden="true">${emblem.inner}</svg>`);
|
|
379
|
+
else if (svg)
|
|
377
380
|
out.push(`<svg class="tb-gold" x="${num(cx - trophyH / 2)}" y="${num(y)}" width="${num(trophyH)}" height="${num(trophyH)}" viewBox="${esc(svg.viewBox)}" preserveAspectRatio="xMidYMid meet">${svg.inner}</svg>`);
|
|
378
|
-
else if (safeUrl(t))
|
|
381
|
+
else if (!isEmblemId(t) && safeUrl(t))
|
|
379
382
|
out.push(`<image href="${esc(t)}" x="${num(cx - trophyH / 2)}" y="${num(y)}" width="${num(trophyH)}" height="${num(trophyH)}" preserveAspectRatio="xMidYMid meet"/>`);
|
|
380
383
|
y += trophyH + 6;
|
|
381
384
|
}
|
package/dist/theme.js
CHANGED
|
@@ -23,6 +23,7 @@ function rules(p, poster, withFont) {
|
|
|
23
23
|
[[".tb-initials"], `fill:${v("ink")}`],
|
|
24
24
|
[[".tb-half"], `fill:${v("ink")}`],
|
|
25
25
|
[[".tb-gold"], `fill:${v("gold")}`],
|
|
26
|
+
[[".tb-emblem"], `color:${v("ink")}`],
|
|
26
27
|
[[".tb-champ .tb-ring"], `stroke:${v("gold")};stroke-width:3`],
|
|
27
28
|
[[".tb-champ-label"], `fill:${v("gold")}`],
|
|
28
29
|
[[".tb-halo"], `paint-order:stroke;stroke:${v("bg")};stroke-width:4px;stroke-linejoin:round`],
|
package/dist/types.d.ts
CHANGED
|
@@ -279,6 +279,20 @@ export interface Size {
|
|
|
279
279
|
}
|
|
280
280
|
/** How much a box shows: both names and scores, an abbreviation and the score, or only the marks. */
|
|
281
281
|
export type Detail = "auto" | "full" | "compact" | "mark";
|
|
282
|
+
/** How an emblem is drawn; the names are those of `@johnmorrisdotca/dougu-sports`. */
|
|
283
|
+
export type EmblemStyle = "metal" | "line" | "color";
|
|
284
|
+
/** What `drawBracket` asks an `emblems` renderer for. */
|
|
285
|
+
export interface EmblemRequest {
|
|
286
|
+
style: EmblemStyle;
|
|
287
|
+
/** Width and height in pixels. */
|
|
288
|
+
size: number;
|
|
289
|
+
/** The colour of the main part, when `emblemTint` is set. */
|
|
290
|
+
tint?: string;
|
|
291
|
+
/** `light` or `dark` for a bracket in that look (`poster` is dark); `inherit` for `auto` and `none`, where the drawing's stylesheet or the page sets the ink. */
|
|
292
|
+
theme: "inherit" | "light" | "dark";
|
|
293
|
+
}
|
|
294
|
+
/** A function from an emblem id and a request to an SVG string; `renderEmblem` of `@johnmorrisdotca/dougu-sports/emblems` is one. */
|
|
295
|
+
export type EmblemRenderer = (id: string, options: EmblemRequest) => string;
|
|
282
296
|
export interface DrawOptions extends LayoutOptions {
|
|
283
297
|
/** `wide` 1200 by 675, `square` 1080 by 1080, `phone` 390 wide and as tall as it needs; or a size of your own. Default `wide`. */
|
|
284
298
|
size?: SizeName | {
|
|
@@ -310,8 +324,21 @@ export interface DrawOptions extends LayoutOptions {
|
|
|
310
324
|
markShape?: "round" | "square" | "none";
|
|
311
325
|
/** Show the champion in the centre once the final is played. Default true. */
|
|
312
326
|
champion?: boolean;
|
|
313
|
-
/**
|
|
327
|
+
/**
|
|
328
|
+
* A trophy for the centre, drawn above the champion: inline SVG, an image address, or (with `emblems`) an emblem id such as
|
|
329
|
+
* `"trophy:hockey-cup"`. An emblem id with no `emblems` renderer draws nothing and takes no room.
|
|
330
|
+
*/
|
|
314
331
|
trophy?: string;
|
|
332
|
+
/**
|
|
333
|
+
* Draws the emblem a `trophy` id names: any function from an id and a request to an SVG string, such as `renderEmblem` from
|
|
334
|
+
* `@johnmorrisdotca/dougu-sports/emblems`. Called once per champion, with the size the trophy has in the drawing. Its SVG is cleaned
|
|
335
|
+
* like any inline SVG; a renderer that throws, or returns something that is not an `<svg>`, draws no trophy.
|
|
336
|
+
*/
|
|
337
|
+
emblems?: EmblemRenderer;
|
|
338
|
+
/** The style an emblem is drawn in: `metal` (the default), `line` or `color`. */
|
|
339
|
+
emblemStyle?: EmblemStyle;
|
|
340
|
+
/** A colour for the emblem's main part (a hex colour or `rgb()`), passed to the renderer as `tint`. */
|
|
341
|
+
emblemTint?: string;
|
|
315
342
|
/** Where the third-place match goes: `below` the final (the default) or `hide`. */
|
|
316
343
|
thirdPlace?: "below" | "hide";
|
|
317
344
|
/** Highlight the side that went through. Default true. */
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.3.0";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
1
|
+
export const VERSION = "0.3.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johnmorrisdotca/taisen-brackets",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Taisen Brackets 対戦, knockout brackets as SVG: from a list of ties (games, best-of series, two legs, replays, byes, a third-place match) it works out the rounds and halves and draws the bracket with the halves meeting at the final, or left to right, wide, square or for a phone, in light, dark or a gold-on-navy poster look, with each team's logo or round flag, the champion in the centre, and an accessible table. NHL, NBA, MLB, NFL, CFL, MLS and World Cup formats by era, in English, French, Japanese, Spanish and German. Zero dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bracket",
|
|
@@ -90,8 +90,17 @@
|
|
|
90
90
|
"test:demo": "pnpm site && playwright test",
|
|
91
91
|
"screenshots": "pnpm site && node scripts/screenshots.mjs"
|
|
92
92
|
},
|
|
93
|
+
"peerDependencies": {
|
|
94
|
+
"@johnmorrisdotca/dougu-sports": ">=0.1.0 <1"
|
|
95
|
+
},
|
|
96
|
+
"peerDependenciesMeta": {
|
|
97
|
+
"@johnmorrisdotca/dougu-sports": {
|
|
98
|
+
"optional": true
|
|
99
|
+
}
|
|
100
|
+
},
|
|
93
101
|
"devDependencies": {
|
|
94
102
|
"@eslint/js": "^9.0.0",
|
|
103
|
+
"@johnmorrisdotca/dougu-sports": "0.1.0",
|
|
95
104
|
"@johnmorrisdotca/hata": "1.5.0",
|
|
96
105
|
"@playwright/test": "^1.63.0",
|
|
97
106
|
"@types/node": "^24.0.0",
|