@sportsdataverse/sdvplot 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/NOTICE.md +109 -0
- package/README.md +984 -2
- package/dist/_web-D7Aa5Rd8.d.ts +69 -0
- package/dist/aaf-GJ3SH34T.js +13 -0
- package/dist/aaf-HCYN5T6Y.js +37 -0
- package/dist/aaf-ZAE5KS2Y.js +13 -0
- package/dist/ahl-E66MDVCU.js +66 -0
- package/dist/ahl-NPQQWZEV.js +249 -0
- package/dist/ahl-QRDU73XX.js +309 -0
- package/dist/axis-CraWQRUJ.d.ts +49 -0
- package/dist/bins.d.ts +174 -0
- package/dist/bins.js +4 -0
- package/dist/bins.js.map +1 -0
- package/dist/cfb-F42SVS4X.js +5000 -0
- package/dist/cfb-P3SHEYRZ.js +692 -0
- package/dist/cfb-WXRZANLP.js +10226 -0
- package/dist/chartjs-surface.d.ts +42 -0
- package/dist/chartjs-surface.js +65 -0
- package/dist/chartjs-surface.js.map +1 -0
- package/dist/chartjs.d.ts +162 -0
- package/dist/chartjs.js +342 -0
- package/dist/chartjs.js.map +1 -0
- package/dist/chunk-4C3GX6QO.js +92 -0
- package/dist/chunk-4C3GX6QO.js.map +1 -0
- package/dist/chunk-57WFSI4Q.js +128 -0
- package/dist/chunk-57WFSI4Q.js.map +1 -0
- package/dist/chunk-7ULOCCFL.js +313 -0
- package/dist/chunk-7ULOCCFL.js.map +1 -0
- package/dist/chunk-AMXASQHT.js +120 -0
- package/dist/chunk-AMXASQHT.js.map +1 -0
- package/dist/chunk-B2MP37SR.js +175 -0
- package/dist/chunk-B2MP37SR.js.map +1 -0
- package/dist/chunk-C2CDY5OG.js +137 -0
- package/dist/chunk-C2CDY5OG.js.map +1 -0
- package/dist/chunk-DYMUVE6K.js +127 -0
- package/dist/chunk-DYMUVE6K.js.map +1 -0
- package/dist/chunk-E7G7VSLV.js +43 -0
- package/dist/chunk-E7G7VSLV.js.map +1 -0
- package/dist/chunk-H2C3FMMA.js +300 -0
- package/dist/chunk-H2C3FMMA.js.map +1 -0
- package/dist/chunk-J4P4HDBF.js +175 -0
- package/dist/chunk-J4P4HDBF.js.map +1 -0
- package/dist/chunk-J6R2LEJ7.js +58 -0
- package/dist/chunk-J6R2LEJ7.js.map +1 -0
- package/dist/chunk-M3XD2OSN.js +132 -0
- package/dist/chunk-M3XD2OSN.js.map +1 -0
- package/dist/chunk-OIILARXI.js +30 -0
- package/dist/chunk-OIILARXI.js.map +1 -0
- package/dist/chunk-RPVVJY74.js +170 -0
- package/dist/chunk-RPVVJY74.js.map +1 -0
- package/dist/chunk-UL5M6KDE.js +205 -0
- package/dist/chunk-UL5M6KDE.js.map +1 -0
- package/dist/chunk-WQT4IVLX.js +10 -0
- package/dist/chunk-WQT4IVLX.js.map +1 -0
- package/dist/chunk-ZAEQXFJM.js +556 -0
- package/dist/chunk-ZAEQXFJM.js.map +1 -0
- package/dist/cricket-KPCYG2HL.js +493 -0
- package/dist/cricket-NDXLE25B.js +116 -0
- package/dist/cricket-YUUF7P6M.js +160 -0
- package/dist/cytoscape.d.ts +188 -0
- package/dist/cytoscape.js +219 -0
- package/dist/cytoscape.js.map +1 -0
- package/dist/d3.d.ts +234 -0
- package/dist/d3.js +198 -0
- package/dist/d3.js.map +1 -0
- package/dist/deckgl.d.ts +232 -0
- package/dist/deckgl.js +197 -0
- package/dist/deckgl.js.map +1 -0
- package/dist/echarts.d.ts +121 -0
- package/dist/echarts.js +228 -0
- package/dist/echarts.js.map +1 -0
- package/dist/echl-3CR4RGFC.js +241 -0
- package/dist/echl-NPC32C23.js +107 -0
- package/dist/echl-PI33DGJC.js +64 -0
- package/dist/export.d.ts +130 -0
- package/dist/export.js +258 -0
- package/dist/export.js.map +1 -0
- package/dist/g2.d.ts +251 -0
- package/dist/g2.js +102 -0
- package/dist/g2.js.map +1 -0
- package/dist/headshots-B2J_LOO8.d.ts +40 -0
- package/dist/index.d.ts +183 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/interact.d.ts +567 -0
- package/dist/interact.js +809 -0
- package/dist/interact.js.map +1 -0
- package/dist/lightweight-charts.d.ts +217 -0
- package/dist/lightweight-charts.js +155 -0
- package/dist/lightweight-charts.js.map +1 -0
- package/dist/maplibre.d.ts +271 -0
- package/dist/maplibre.js +175 -0
- package/dist/maplibre.js.map +1 -0
- package/dist/marks-DH-tQIP-.d.ts +75 -0
- package/dist/mbb-3TRLVW3H.js +3619 -0
- package/dist/mbb-DQUHCHZP.js +5111 -0
- package/dist/mbb-PRAIO2P5.js +371 -0
- package/dist/milb-6IQ6NCEW.js +291 -0
- package/dist/milb-EYY5MD4T.js +1752 -0
- package/dist/milb-EYZ4SZLI.js +797 -0
- package/dist/mlb-4PBUJSVQ.js +35 -0
- package/dist/mlb-FHPY3GQJ.js +701 -0
- package/dist/mlb-REVGL3N2.js +665 -0
- package/dist/mui.d.ts +338 -0
- package/dist/mui.js +184 -0
- package/dist/mui.js.map +1 -0
- package/dist/nba-ATT775GS.js +485 -0
- package/dist/nba-LKM4UHVU.js +482 -0
- package/dist/nba-YX7PXM3R.js +35 -0
- package/dist/nbagl-3WGZCZ4S.js +37 -0
- package/dist/nbagl-LP4XJRAL.js +229 -0
- package/dist/nbagl-VTTOU7LN.js +499 -0
- package/dist/ncaa_baseball-UV75LNVR.js +2670 -0
- package/dist/ncaa_baseball-VD5J5C72.js +419 -0
- package/dist/ncaa_baseball-YRBS32D6.js +4592 -0
- package/dist/ncaa_mhockey-IMYHS7QS.js +1033 -0
- package/dist/ncaa_mhockey-TXTKWHE3.js +115 -0
- package/dist/ncaa_mhockey-U3MFW5VT.js +663 -0
- package/dist/ncaa_softball-6WR6KS64.js +420 -0
- package/dist/ncaa_softball-RZCTIE56.js +2654 -0
- package/dist/ncaa_softball-VOD4PVGY.js +4599 -0
- package/dist/ncaa_whockey-6XXGUTBK.js +607 -0
- package/dist/ncaa_whockey-AKKYKY67.js +53 -0
- package/dist/ncaa_whockey-KBEKXFBB.js +293 -0
- package/dist/nfl-BDB2HYAR.js +625 -0
- package/dist/nfl-EHYHDLJD.js +37 -0
- package/dist/nfl-VF4NJCHY.js +514 -0
- package/dist/nfl_gsis-UY64JO37.js +4 -0
- package/dist/nhl-6WPHME7H.js +511 -0
- package/dist/nhl-EMIJFZHY.js +884 -0
- package/dist/nhl-ISFUKRFU.js +37 -0
- package/dist/nivo.d.ts +193 -0
- package/dist/nivo.js +8 -0
- package/dist/nivo.js.map +1 -0
- package/dist/ohl-2FEM554G.js +113 -0
- package/dist/ohl-C5Q4W6JB.js +32 -0
- package/dist/ohl-TQ4J46VC.js +66 -0
- package/dist/perspective.d.ts +198 -0
- package/dist/perspective.js +171 -0
- package/dist/perspective.js.map +1 -0
- package/dist/phf-H7POU2KW.js +32 -0
- package/dist/phf-K5IFMZUM.js +13 -0
- package/dist/phf-TWJI3BOW.js +20 -0
- package/dist/placement-UWkqUzpa.d.ts +32 -0
- package/dist/plot.d.ts +451 -0
- package/dist/plot.js +712 -0
- package/dist/plot.js.map +1 -0
- package/dist/plotly.d.ts +129 -0
- package/dist/plotly.js +360 -0
- package/dist/plotly.js.map +1 -0
- package/dist/pwhl-OX3BO4JI.js +53 -0
- package/dist/pwhl-W44OC3C6.js +17 -0
- package/dist/pwhl-ZJGG3H7W.js +23 -0
- package/dist/qmjhl-5HISEHLL.js +35 -0
- package/dist/qmjhl-DA3UHKE5.js +125 -0
- package/dist/qmjhl-YG4BOQPF.js +614 -0
- package/dist/react.d.ts +377 -0
- package/dist/react.js +522 -0
- package/dist/react.js.map +1 -0
- package/dist/recharts.d.ts +162 -0
- package/dist/recharts.js +8 -0
- package/dist/recharts.js.map +1 -0
- package/dist/resolve-CQW-jzvr.d.ts +28 -0
- package/dist/selection-D4T3PkRt.d.ts +140 -0
- package/dist/shadcn.d.ts +83 -0
- package/dist/shadcn.js +88 -0
- package/dist/shadcn.js.map +1 -0
- package/dist/shots.d.ts +364 -0
- package/dist/shots.js +86 -0
- package/dist/shots.js.map +1 -0
- package/dist/soccer-7BH3ZLVL.js +7898 -0
- package/dist/soccer-MRU25OGE.js +2636 -0
- package/dist/soccer-PF4IPPVA.js +12342 -0
- package/dist/surface-TR7YFJW3.js +4 -0
- package/dist/surface-TR7YFJW3.js.map +1 -0
- package/dist/surfaces-MIG7HOFU.js +6 -0
- package/dist/surfaces-MIG7HOFU.js.map +1 -0
- package/dist/surfaces-W-odWLpT.d.ts +47 -0
- package/dist/svelte/HeadshotMark.svelte +25 -0
- package/dist/svelte/HeadshotMark.svelte.d.ts +10 -0
- package/dist/svelte/HeadshotMark.svelte.d.ts.map +1 -0
- package/dist/svelte/LogoMark.svelte +25 -0
- package/dist/svelte/LogoMark.svelte.d.ts +10 -0
- package/dist/svelte/LogoMark.svelte.d.ts.map +1 -0
- package/dist/svelte/Marks.svelte +25 -0
- package/dist/svelte/Marks.svelte.d.ts +10 -0
- package/dist/svelte/Marks.svelte.d.ts.map +1 -0
- package/dist/svelte/Surface.svelte +29 -0
- package/dist/svelte/Surface.svelte.d.ts +9 -0
- package/dist/svelte/Surface.svelte.d.ts.map +1 -0
- package/dist/svelte/SurfaceBackdrop.svelte +26 -0
- package/dist/svelte/SurfaceBackdrop.svelte.d.ts +11 -0
- package/dist/svelte/SurfaceBackdrop.svelte.d.ts.map +1 -0
- package/dist/svelte/index.d.ts +13 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +11 -0
- package/dist/svelte-core.d.ts +139 -0
- package/dist/svelte-core.js +123 -0
- package/dist/svelte-core.js.map +1 -0
- package/dist/tanstack.d.ts +240 -0
- package/dist/tanstack.js +110 -0
- package/dist/tanstack.js.map +1 -0
- package/dist/testing.d.ts +75 -0
- package/dist/testing.js +239 -0
- package/dist/testing.js.map +1 -0
- package/dist/three.d.ts +223 -0
- package/dist/three.js +50 -0
- package/dist/three.js.map +1 -0
- package/dist/tiers-BN4md-GB.d.ts +65 -0
- package/dist/types-C7n6gJF3.d.ts +62 -0
- package/dist/ufl-7VLZTLTT.js +143 -0
- package/dist/ufl-CCMBLPSW.js +100 -0
- package/dist/ufl-F2BJRXME.js +16 -0
- package/dist/unovis.d.ts +127 -0
- package/dist/unovis.js +108 -0
- package/dist/unovis.js.map +1 -0
- package/dist/uplot.d.ts +120 -0
- package/dist/uplot.js +110 -0
- package/dist/uplot.js.map +1 -0
- package/dist/usfl-4CR5RMZJ.js +32 -0
- package/dist/usfl-LD7HP2YK.js +14 -0
- package/dist/usfl-VU4YSJQD.js +19 -0
- package/dist/ushl-CPG2LB4L.js +73 -0
- package/dist/ushl-PQL6HJAD.js +88 -0
- package/dist/ushl-ZT2ATPHK.js +22 -0
- package/dist/vega.d.ts +85 -0
- package/dist/vega.js +362 -0
- package/dist/vega.js.map +1 -0
- package/dist/victory.d.ts +107 -0
- package/dist/victory.js +38 -0
- package/dist/victory.js.map +1 -0
- package/dist/wbb-3KHCDYTK.js +5087 -0
- package/dist/wbb-BMX53Q3Q.js +369 -0
- package/dist/wbb-QNRN2CJJ.js +3620 -0
- package/dist/whl-4ATYZWUB.js +97 -0
- package/dist/whl-RVFPL6MH.js +79 -0
- package/dist/whl-Y7ASKYMM.js +28 -0
- package/dist/wnba-FWX5SFVI.js +232 -0
- package/dist/wnba-GX6HZR4B.js +220 -0
- package/dist/wnba-NTMWIPP6.js +20 -0
- package/dist/xfl-43R2JMJK.js +30 -0
- package/dist/xfl-H3BXQPZZ.js +16 -0
- package/dist/xfl-XON7A7FI.js +69 -0
- package/package.json +366 -3
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { L as League, S as SeasonInput, I as IdSystem, V as Variant } from './types-C7n6gJF3.js';
|
|
2
|
+
|
|
3
|
+
/** A row: column name to value, as `perspective.table(rows)` takes them. */
|
|
4
|
+
type PerspectiveRow = Readonly<Record<string, unknown>>;
|
|
5
|
+
/** A Perspective column type, as `await table.schema()` gives it. */
|
|
6
|
+
type PerspectiveType = "integer" | "float" | "string" | "boolean" | "date" | "datetime";
|
|
7
|
+
/** A Perspective schema: column name to type, as `await table.schema()` gives it. */
|
|
8
|
+
type PerspectiveSchema = Readonly<Record<string, PerspectiveType>>;
|
|
9
|
+
/** A `number_format`: Intl.NumberFormat options, e.g. `{ maximumFractionDigits: 2 }`. */
|
|
10
|
+
type PerspectiveNumberFormat = {
|
|
11
|
+
readonly [option: string]: string | number | boolean;
|
|
12
|
+
};
|
|
13
|
+
/** One column's entry in `columns_config`: the keys sdvplot sets. */
|
|
14
|
+
type PerspectiveColumnConfig = {
|
|
15
|
+
/** The Datagrid's cell fill: "series", one palette colour per distinct value. */
|
|
16
|
+
bg_mode?: "series";
|
|
17
|
+
/** The Datagrid's series palette: `linear-gradient(to right, #rrggbb, …)`. */
|
|
18
|
+
bg_color?: string;
|
|
19
|
+
/** A chart's series palette (on a value column) or a scatter's Color slot palette (on the team column). */
|
|
20
|
+
palette?: string;
|
|
21
|
+
number_format?: PerspectiveNumberFormat;
|
|
22
|
+
/** The Datagrid shows a URL column as links. */
|
|
23
|
+
link?: true;
|
|
24
|
+
/** The Datagrid's number colours (a registry theme's): `linear-gradient(to right, <neg> 0%, <pos> 100%)`. */
|
|
25
|
+
fg_color?: string;
|
|
26
|
+
/** The Datagrid's font for a number column: a registry theme's numeric face, when it is not the body face. */
|
|
27
|
+
font_family?: string;
|
|
28
|
+
};
|
|
29
|
+
/** The Datagrid's own settings: a registry theme's body face and size. */
|
|
30
|
+
type PerspectivePluginConfig = {
|
|
31
|
+
font_family: string;
|
|
32
|
+
font_size: number;
|
|
33
|
+
};
|
|
34
|
+
/** What `perspectiveConfig` returns: pass it to `viewer.restore()`, with your own view keys merged in. */
|
|
35
|
+
type PerspectiveConfig = {
|
|
36
|
+
plugin: string;
|
|
37
|
+
/** Perspective's own theme: "Pro Light" or "Pro Dark" (load `@perspective-dev/viewer/dist/css/pro.css` / `pro-dark.css`). */
|
|
38
|
+
theme: "Pro Light" | "Pro Dark";
|
|
39
|
+
/** The Datagrid with a registry `theme` only. */
|
|
40
|
+
plugin_config?: PerspectivePluginConfig;
|
|
41
|
+
columns_config: Record<string, PerspectiveColumnConfig>;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* The parts of a registry table theme `perspectiveConfig` reads. A `TableThemeSpec` (sdvtables'
|
|
45
|
+
* `tableThemeSpec(name, mode)`, Python's `table_theme_spec`) is one; sdvplot does not ship the registry itself.
|
|
46
|
+
*/
|
|
47
|
+
interface PerspectiveTheme {
|
|
48
|
+
/** `body`: the grid's face. `numeric`: the number columns' face, when it is not `body`. */
|
|
49
|
+
readonly fonts: {
|
|
50
|
+
readonly body?: string;
|
|
51
|
+
readonly numeric?: string;
|
|
52
|
+
};
|
|
53
|
+
/** `body`: the grid's font size in px. */
|
|
54
|
+
readonly sizes: {
|
|
55
|
+
readonly body: number;
|
|
56
|
+
};
|
|
57
|
+
/** The number colours: `neg` (low) to `pos` (high). */
|
|
58
|
+
readonly palette: {
|
|
59
|
+
readonly neg: string;
|
|
60
|
+
readonly pos: string;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** `perspectiveConfig` options. */
|
|
64
|
+
interface PerspectiveConfigOptions {
|
|
65
|
+
/** The column holding the team identifiers (any id system `resolve` reads). */
|
|
66
|
+
teamCol: string;
|
|
67
|
+
/** The SDV league key, e.g. "nfl". */
|
|
68
|
+
league: League;
|
|
69
|
+
/** The plugin: "Datagrid" (the default) or a chart's name ("Y Bar", "X Bar", "Y Line", "X/Y Scatter", …). */
|
|
70
|
+
plugin?: string;
|
|
71
|
+
/** "light" (default) or "dark": the Perspective theme, "Pro Light" or "Pro Dark". */
|
|
72
|
+
mode?: "light" | "dark";
|
|
73
|
+
/**
|
|
74
|
+
* A registry table theme: best as a function of the mode, `(m) => tableThemeSpec(name, m)` from
|
|
75
|
+
* `@sportsdataverse/sdvtables`, called with this call's `mode` on every plugin (so a light-only theme in dark mode
|
|
76
|
+
* throws, as in Python). A spec object works too; it does not say its mode, so resolve it in the same `mode`.
|
|
77
|
+
* The Datagrid takes its body face and size (`plugin_config`), its sign colours on every number column (`fg_color`;
|
|
78
|
+
* Perspective reads only `#hex` and `rgb()` colours there) and, when the numeric face is not the body face, that face
|
|
79
|
+
* on the number columns. Perspective takes each face as one family name, so the page loads the font (Google Fonts)
|
|
80
|
+
* or the browser draws its default. A chart takes none of it.
|
|
81
|
+
*/
|
|
82
|
+
theme?: PerspectiveTheme | ((mode: "light" | "dark") => PerspectiveTheme);
|
|
83
|
+
/** A logo URL column (`perspectiveLogoColumn`) to show as links on the Datagrid, which draws no images. */
|
|
84
|
+
logoCol?: string;
|
|
85
|
+
/** The colour the teams must read against. Default: the Perspective theme's (#ffffff Pro Light, #242526 Pro Dark). */
|
|
86
|
+
background?: string;
|
|
87
|
+
/** Per column, a `number_format` to use instead of the default, or null for none. */
|
|
88
|
+
formats?: Readonly<Record<string, PerspectiveNumberFormat | null>>;
|
|
89
|
+
/** One season for every team. */
|
|
90
|
+
season?: SeasonInput;
|
|
91
|
+
/** How the team values are read. Default "auto". */
|
|
92
|
+
idSystem?: IdSystem;
|
|
93
|
+
/** Throw UnresolvedTeamError for a team that does not resolve, instead of one warning and #808080. */
|
|
94
|
+
strict?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* The column types, as `await table.schema()` gives them. Default: worked out from the rows as Perspective does
|
|
97
|
+
* (numbers, and strings that read as numbers, are "integer" or "float"; anything else is not a number). Pass it when
|
|
98
|
+
* you load the table with a schema of your own.
|
|
99
|
+
*/
|
|
100
|
+
schema?: PerspectiveSchema;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A Perspective viewer config that colours each team by its own colours, for the Datagrid or a chart: the argument of
|
|
104
|
+
* `viewer.restore()`, as Python's `perspective_sdv_config` builds it (same keys, same colours, same order).
|
|
105
|
+
*
|
|
106
|
+
* The colours are the shared team palette (every adapter's, and Python's), taken over the teams in sorted order: each
|
|
107
|
+
* team's colour, made 3:1 against the background and ΔE 20 from every earlier team's, so a team keeps one colour
|
|
108
|
+
* whatever the rows' order. Perspective takes a palette, not a colour per value, so each palette lists those colours in
|
|
109
|
+
* the order Perspective hands colours out (checked in Chromium on Perspective 5.5.1):
|
|
110
|
+
*
|
|
111
|
+
* - `plugin: "Datagrid"`: the team column's cells are filled (`bg_mode` "series"), in the order the grid first draws
|
|
112
|
+
* each value: the rows' order. Keep the grid in the rows' order (no `sort`) and to about one row per team. Null
|
|
113
|
+
* cells take no colour and no slot. Only a text team column is filled: Perspective fills no number column, so
|
|
114
|
+
* numeric team ids get none, with one warning.
|
|
115
|
+
* - a chart: split a bar, line or area chart by the team (`split_by: [teamCol]`) and each series takes its colour
|
|
116
|
+
* from every number column's `palette`, in sorted order (Perspective sorts split values; a null series comes first,
|
|
117
|
+
* so the palette then starts with the fallback grey). Put the team column in an "X/Y Scatter"'s Color slot (its
|
|
118
|
+
* third column) and each team's points take theirs from the team column's `palette`, in the rows' order.
|
|
119
|
+
*
|
|
120
|
+
* Every number column also gets a `number_format` (Intl.NumberFormat options): 3 decimals for a float, 1 for a float
|
|
121
|
+
* named as a percentage (`pct`, `percent`), and an integer plain (2024, not 2,024). `formats` overrides them.
|
|
122
|
+
*
|
|
123
|
+
* @param rows - the rows the viewer shows (what `perspective.table(rows)` loads). With no rows and no `schema` no column is
|
|
124
|
+
* known: the config has `plugin` and `theme` and an empty `columns_config`, without an error (pass the table's
|
|
125
|
+
* `schema` for its number formats, as Python's empty frame keeps its columns).
|
|
126
|
+
* @returns `plugin`, `theme` and `columns_config`; merge your own `group_by`, `split_by`, `columns` and the rest in.
|
|
127
|
+
* @throws InputError for a column the rows do not have (`teamCol`, `logoCol` or a `formats` key), a team column that
|
|
128
|
+
* mixes numbers and strings, an unknown `mode`, a `theme` that is not a spec (or a function returning one), or a
|
|
129
|
+
* season outside the league's. UnresolvedTeamError with `strict: true`. A `theme` function's own errors pass through
|
|
130
|
+
* (sdvtables' TableSpecError for a light-only theme in dark mode).
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```ts
|
|
134
|
+
* import { loadLeague } from "@sportsdataverse/sdvplot";
|
|
135
|
+
* import { perspectiveConfig } from "@sportsdataverse/sdvplot/perspective";
|
|
136
|
+
*
|
|
137
|
+
* await loadLeague("nfl");
|
|
138
|
+
* // 2024 regular season, offence: EPA per play (nflverse play-by-play)
|
|
139
|
+
* const epa = [
|
|
140
|
+
* { posteam: "BAL", epa_per_play: 0.218 },
|
|
141
|
+
* { posteam: "BUF", epa_per_play: 0.1885 },
|
|
142
|
+
* { posteam: "DET", epa_per_play: 0.1592 },
|
|
143
|
+
* ];
|
|
144
|
+
* const grid = perspectiveConfig(epa, { teamCol: "posteam", league: "nfl" });
|
|
145
|
+
* const bars = {
|
|
146
|
+
* ...perspectiveConfig(epa, { teamCol: "posteam", league: "nfl", plugin: "Y Bar" }),
|
|
147
|
+
* group_by: ["posteam"],
|
|
148
|
+
* split_by: ["posteam"],
|
|
149
|
+
* columns: ["epa_per_play"],
|
|
150
|
+
* };
|
|
151
|
+
* // in the browser: await viewer.load(await worker.table(epa)); await viewer.restore(grid)
|
|
152
|
+
* [grid.columns_config.posteam?.bg_color, bars.columns_config.epa_per_play?.palette];
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
declare function perspectiveConfig(rows: readonly PerspectiveRow[], o: PerspectiveConfigOptions): PerspectiveConfig;
|
|
156
|
+
/** `perspectiveLogoColumn` options. */
|
|
157
|
+
interface PerspectiveLogoOptions {
|
|
158
|
+
/** The SDV league key, e.g. "nfl". */
|
|
159
|
+
league: League;
|
|
160
|
+
/** The new column's name. Default: `${teamCol}_logo`. It must not be a column already. */
|
|
161
|
+
name?: string;
|
|
162
|
+
/** One season for every team: the logo of that season (relocations and rebrands). */
|
|
163
|
+
season?: SeasonInput;
|
|
164
|
+
/** "default", "dark", or a named variant from `marks()`. */
|
|
165
|
+
variant?: Variant;
|
|
166
|
+
/** How the team values are read. Default "auto". */
|
|
167
|
+
idSystem?: IdSystem;
|
|
168
|
+
/** Throw UnresolvedTeamError for a team that does not resolve, instead of warning. */
|
|
169
|
+
strict?: boolean;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The rows with a column of each team's logo URL, right after the team column, as Python's `perspective_logo_column`
|
|
173
|
+
* adds it. Each distinct team is resolved once. A team that does not resolve, or has no archived logo, gets a null URL,
|
|
174
|
+
* with one warning for each reason. Show the column as links with `perspectiveConfig(rows, { logoCol })`: Perspective
|
|
175
|
+
* 5.5's Datagrid draws no images. The rows given are not changed.
|
|
176
|
+
*
|
|
177
|
+
* @returns new rows, each with the URL (a string, or null) in `name`.
|
|
178
|
+
* @throws InputError for a `teamCol` no row has, a `name` a row already has, or a season outside the league's.
|
|
179
|
+
* UnresolvedTeamError with `strict: true`.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* import { loadLeague } from "@sportsdataverse/sdvplot";
|
|
184
|
+
* import { perspectiveConfig, perspectiveLogoColumn } from "@sportsdataverse/sdvplot/perspective";
|
|
185
|
+
*
|
|
186
|
+
* await loadLeague("nfl");
|
|
187
|
+
* const epa = [
|
|
188
|
+
* { posteam: "BAL", epa_per_play: 0.218 },
|
|
189
|
+
* { posteam: "BUF", epa_per_play: 0.1885 },
|
|
190
|
+
* ];
|
|
191
|
+
* const rows = perspectiveLogoColumn(epa, "posteam", { league: "nfl" });
|
|
192
|
+
* const config = perspectiveConfig(rows, { teamCol: "posteam", league: "nfl", logoCol: "posteam_logo" });
|
|
193
|
+
* [Object.keys(rows[0] ?? {}), config.columns_config.posteam_logo];
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
declare function perspectiveLogoColumn<R extends PerspectiveRow>(rows: readonly R[], teamCol: string, o: PerspectiveLogoOptions): (R & Record<string, string | null>)[];
|
|
197
|
+
|
|
198
|
+
export { type PerspectiveColumnConfig, type PerspectiveConfig, type PerspectiveConfigOptions, type PerspectiveLogoOptions, type PerspectiveNumberFormat, type PerspectivePluginConfig, type PerspectiveRow, type PerspectiveSchema, type PerspectiveTheme, type PerspectiveType, perspectiveConfig, perspectiveLogoColumn };
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { colorPalette } from './chunk-UL5M6KDE.js';
|
|
2
|
+
import { placeSync } from './chunk-7ULOCCFL.js';
|
|
3
|
+
import { normSeason } from './chunk-ZAEQXFJM.js';
|
|
4
|
+
import { hex6 } from './chunk-J6R2LEJ7.js';
|
|
5
|
+
import { InputError, warn } from './chunk-E7G7VSLV.js';
|
|
6
|
+
|
|
7
|
+
// src/perspective.ts
|
|
8
|
+
var PRO = { light: ["Pro Light", "#ffffff"], dark: ["Pro Dark", "#242526"] };
|
|
9
|
+
var FALLBACK = "#808080";
|
|
10
|
+
var PERCENT = /(^|_)(pct|percent|perc)(_|$)/i;
|
|
11
|
+
function numberFormat(name, type) {
|
|
12
|
+
if (type === "integer") return { useGrouping: false, maximumFractionDigits: 0 };
|
|
13
|
+
const n = PERCENT.test(name) ? 1 : 3;
|
|
14
|
+
return { minimumFractionDigits: n, maximumFractionDigits: n };
|
|
15
|
+
}
|
|
16
|
+
var INT = /^\s*-?\d+\s*$/;
|
|
17
|
+
var FLOAT = /^\s*-?(\d+\.?\d*|\.\d+)([eE][-+]?\d+)?\s*$/;
|
|
18
|
+
function kindOf(v) {
|
|
19
|
+
if (v === null || v === void 0) return null;
|
|
20
|
+
if (typeof v === "bigint") return "integer";
|
|
21
|
+
if (typeof v === "number") {
|
|
22
|
+
if (!Number.isFinite(v)) return "other";
|
|
23
|
+
return Number.isInteger(v) && Math.abs(v) < 2 ** 31 ? "integer" : "float";
|
|
24
|
+
}
|
|
25
|
+
if (typeof v === "string") return INT.test(v) ? "integer" : FLOAT.test(v) ? "float" : "other";
|
|
26
|
+
return "other";
|
|
27
|
+
}
|
|
28
|
+
function inferSchema(rows) {
|
|
29
|
+
const kinds = /* @__PURE__ */ new Map();
|
|
30
|
+
for (const r of rows)
|
|
31
|
+
for (const [k, v] of Object.entries(r)) {
|
|
32
|
+
const s = kinds.get(k) ?? kinds.set(k, /* @__PURE__ */ new Set()).get(k);
|
|
33
|
+
const kind = kindOf(v);
|
|
34
|
+
if (kind !== null) s.add(kind);
|
|
35
|
+
}
|
|
36
|
+
return Object.fromEntries(
|
|
37
|
+
[...kinds].map(([k, s]) => [
|
|
38
|
+
k,
|
|
39
|
+
s.size === 0 || s.has("other") ? "string" : s.has("float") ? "float" : "integer"
|
|
40
|
+
])
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
var isNumber = (t) => t === "integer" || t === "float";
|
|
44
|
+
var quote = (s) => `'${s}'`;
|
|
45
|
+
var isObj = (v) => typeof v === "object" && v !== null;
|
|
46
|
+
var isFace = (v) => v === void 0 || typeof v === "string";
|
|
47
|
+
var isTheme = (t) => isObj(t) && isObj(t.fonts) && isFace(t.fonts.body) && isFace(t.fonts.numeric) && isObj(t.sizes) && typeof t.sizes.body === "number" && isObj(t.palette) && typeof t.palette.neg === "string" && typeof t.palette.pos === "string";
|
|
48
|
+
function checkColumns(schema, cols) {
|
|
49
|
+
const names = Object.keys(schema);
|
|
50
|
+
for (const c of cols)
|
|
51
|
+
if (c !== void 0 && !Object.hasOwn(schema, c))
|
|
52
|
+
throw new InputError(`the rows have no column ${quote(c)} (columns: ${names.join(", ")})`);
|
|
53
|
+
}
|
|
54
|
+
function teamOrders(values, numeric, col) {
|
|
55
|
+
const first = [...new Set(values.filter((v) => v !== null && v !== void 0))];
|
|
56
|
+
const types = new Set(first.map((v) => typeof v === "bigint" ? "number" : typeof v));
|
|
57
|
+
if (types.size > 1)
|
|
58
|
+
throw new InputError(
|
|
59
|
+
`the team column ${quote(col)} mixes ${[...types].sort().join(" and ")} values: use one id system`
|
|
60
|
+
);
|
|
61
|
+
const key = numeric ? (v) => Number(v) : (v) => String(v);
|
|
62
|
+
const sorted = [...first].sort((a, b) => {
|
|
63
|
+
const [x, y] = [key(a), key(b)];
|
|
64
|
+
return x < y ? -1 : x > y ? 1 : 0;
|
|
65
|
+
});
|
|
66
|
+
return [first, sorted];
|
|
67
|
+
}
|
|
68
|
+
var gradient = (colors) => `linear-gradient(to right, ${colors.join(", ")})`;
|
|
69
|
+
function perspectiveConfig(rows, o) {
|
|
70
|
+
const mode = o.mode ?? "light";
|
|
71
|
+
if (mode !== "light" && mode !== "dark")
|
|
72
|
+
throw new InputError(`mode must be 'light' or 'dark', not ${quote(String(mode))}`);
|
|
73
|
+
const resolved = typeof o.theme === "function" ? o.theme(mode) : o.theme;
|
|
74
|
+
if (o.theme !== void 0 && !isTheme(resolved))
|
|
75
|
+
throw new InputError(
|
|
76
|
+
`theme must be a table theme spec or a function of the mode, not ${quote(String(resolved))}: pass (m) => tableThemeSpec(name, m) from @sportsdataverse/sdvtables`
|
|
77
|
+
);
|
|
78
|
+
const schema = o.schema ?? inferSchema(rows);
|
|
79
|
+
const { teamCol, logoCol } = o;
|
|
80
|
+
if (o.schema !== void 0 || rows.length > 0)
|
|
81
|
+
checkColumns(schema, [teamCol, logoCol, ...Object.keys(o.formats ?? {})]);
|
|
82
|
+
const [pro, proBg] = PRO[mode];
|
|
83
|
+
const bg = o.background === void 0 ? proBg : hex6(o.background);
|
|
84
|
+
const season = normSeason(o.season, o.league);
|
|
85
|
+
const text = !isNumber(schema[teamCol]);
|
|
86
|
+
const numeric = Object.keys(schema).filter((c) => c !== teamCol && isNumber(schema[c]));
|
|
87
|
+
const grid = (o.plugin ?? "Datagrid") === "Datagrid";
|
|
88
|
+
const values = rows.map((r) => r[teamCol]);
|
|
89
|
+
const [first, ascending] = teamOrders(values, !text, teamCol);
|
|
90
|
+
const pal = colorPalette(o.league, ascending, {
|
|
91
|
+
...season === null ? {} : { season },
|
|
92
|
+
background: bg,
|
|
93
|
+
idSystem: o.idSystem ?? "auto",
|
|
94
|
+
strict: o.strict ?? false,
|
|
95
|
+
fallback: FALLBACK
|
|
96
|
+
});
|
|
97
|
+
const color = new Map(ascending.map((v, i) => [v, pal[i]]));
|
|
98
|
+
const nulls = values.some((v) => v === null || v === void 0) ? [FALLBACK] : [];
|
|
99
|
+
const byAppearance = gradient(first.map((v) => color.get(v)));
|
|
100
|
+
const byValue = gradient([...nulls, ...pal]);
|
|
101
|
+
let columns = {};
|
|
102
|
+
for (const c of numeric) {
|
|
103
|
+
const fmt = o.formats !== void 0 && Object.hasOwn(o.formats, c) ? o.formats[c] : numberFormat(c, schema[c]);
|
|
104
|
+
columns[c] = fmt === null || fmt === void 0 ? {} : { number_format: fmt };
|
|
105
|
+
}
|
|
106
|
+
if (first.length > 0 && !grid) {
|
|
107
|
+
for (const c of numeric) columns[c].palette = byValue;
|
|
108
|
+
if (text) columns = { [teamCol]: { palette: byAppearance }, ...columns };
|
|
109
|
+
} else if (first.length > 0 && text) {
|
|
110
|
+
columns = { [teamCol]: { bg_mode: "series", bg_color: byAppearance }, ...columns };
|
|
111
|
+
} else if (first.length > 0) {
|
|
112
|
+
warn(
|
|
113
|
+
`perspective:numeric-team:${teamCol}`,
|
|
114
|
+
`no team colors on the Datagrid: the team column ${quote(teamCol)} is numeric, and team colors need a string or categorical team column (Perspective fills only text cells)`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const theme = grid ? resolved : void 0;
|
|
118
|
+
if (theme !== void 0) {
|
|
119
|
+
const { body, numeric: face } = theme.fonts;
|
|
120
|
+
const sign = `linear-gradient(to right, ${theme.palette.neg} 0%, ${theme.palette.pos} 100%)`;
|
|
121
|
+
for (const c of numeric) {
|
|
122
|
+
columns[c].fg_color = sign;
|
|
123
|
+
if (face && face !== body) columns[c].font_family = face;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (grid && logoCol !== void 0) columns[logoCol] = { link: true };
|
|
127
|
+
const gridFace = theme?.fonts.body;
|
|
128
|
+
return {
|
|
129
|
+
plugin: o.plugin ?? "Datagrid",
|
|
130
|
+
theme: pro,
|
|
131
|
+
...theme !== void 0 && gridFace ? { plugin_config: { font_family: gridFace, font_size: theme.sizes.body } } : {},
|
|
132
|
+
columns_config: Object.fromEntries(Object.entries(columns).filter(([, v]) => Object.keys(v).length > 0))
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function perspectiveLogoColumn(rows, teamCol, o) {
|
|
136
|
+
const name = o.name ?? `${teamCol}_logo`;
|
|
137
|
+
if (rows.length > 0 && !rows.some((r) => Object.hasOwn(r, teamCol)))
|
|
138
|
+
throw new InputError(`the rows have no column ${quote(teamCol)}`);
|
|
139
|
+
if (rows.some((r) => Object.hasOwn(r, name)))
|
|
140
|
+
throw new InputError(`the rows already have a column ${quote(name)}: pass another name`);
|
|
141
|
+
const values = [...new Set(rows.map((r) => r[teamCol]).filter((v) => v !== null && v !== void 0))];
|
|
142
|
+
const at = values.map((_, i) => i);
|
|
143
|
+
const placed = placeSync(
|
|
144
|
+
at,
|
|
145
|
+
at.map(() => 0),
|
|
146
|
+
values,
|
|
147
|
+
{
|
|
148
|
+
league: o.league,
|
|
149
|
+
season: normSeason(o.season, o.league),
|
|
150
|
+
kind: "logo",
|
|
151
|
+
variant: o.variant ?? "default",
|
|
152
|
+
idSystem: o.idSystem ?? "auto",
|
|
153
|
+
strict: o.strict ?? false
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
const urls = new Map(placed.map((p) => [values[p.index], p.url]));
|
|
157
|
+
return rows.map((r) => {
|
|
158
|
+
const out = {};
|
|
159
|
+
const url = urls.get(r[teamCol]) ?? null;
|
|
160
|
+
for (const [k, v] of Object.entries(r)) {
|
|
161
|
+
out[k] = v;
|
|
162
|
+
if (k === teamCol) out[name] = url;
|
|
163
|
+
}
|
|
164
|
+
if (!Object.hasOwn(out, name)) out[name] = url;
|
|
165
|
+
return out;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { perspectiveConfig, perspectiveLogoColumn };
|
|
170
|
+
//# sourceMappingURL=perspective.js.map
|
|
171
|
+
//# sourceMappingURL=perspective.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/perspective.ts"],"names":[],"mappings":"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","file":"perspective.js","sourcesContent":["/**\n * Perspective 5.5 (`@perspective-dev/viewer`, with the `-datagrid` and `-charts` plugins): team colours, number formats,\n * a registry table theme's fonts and sign colours, and a logo column for a `<perspective-viewer>`, the JavaScript side of sdvplot's Python `sdvplot.perspective`. Both\n * functions return plain data: a viewer config (the argument of `viewer.restore()`) and rows with a logo URL column.\n * Given the same rows and options, `perspectiveConfig` returns what Python's `perspective_sdv_config` returns\n * (fixtures/perspective/oracle.json holds the Python answers; packages/sdvplot/test/perspective-parity.test.ts compares).\n * Pure: Perspective is never imported, not even its types. Call after `loadLeague(league)` / `preloadAll()`.\n */\nimport { colorPalette } from \"./_web.js\";\nimport { hex6 } from \"./contrast.js\";\nimport { InputError, warn } from \"./errors.js\";\nimport { normSeason } from \"./normalize.js\";\nimport { placeSync } from \"./placement.js\";\nimport type { Value } from \"./resolve.js\";\nimport type { IdSystem, League, SeasonInput, Variant } from \"./types.js\";\n\n/** A row: column name to value, as `perspective.table(rows)` takes them. */\nexport type PerspectiveRow = Readonly<Record<string, unknown>>;\n/** A Perspective column type, as `await table.schema()` gives it. */\nexport type PerspectiveType = \"integer\" | \"float\" | \"string\" | \"boolean\" | \"date\" | \"datetime\";\n/** A Perspective schema: column name to type, as `await table.schema()` gives it. */\nexport type PerspectiveSchema = Readonly<Record<string, PerspectiveType>>;\n/** A `number_format`: Intl.NumberFormat options, e.g. `{ maximumFractionDigits: 2 }`. */\nexport type PerspectiveNumberFormat = { readonly [option: string]: string | number | boolean };\n/** One column's entry in `columns_config`: the keys sdvplot sets. */\nexport type PerspectiveColumnConfig = {\n /** The Datagrid's cell fill: \"series\", one palette colour per distinct value. */\n bg_mode?: \"series\";\n /** The Datagrid's series palette: `linear-gradient(to right, #rrggbb, …)`. */\n bg_color?: string;\n /** A chart's series palette (on a value column) or a scatter's Color slot palette (on the team column). */\n palette?: string;\n number_format?: PerspectiveNumberFormat;\n /** The Datagrid shows a URL column as links. */\n link?: true;\n /** The Datagrid's number colours (a registry theme's): `linear-gradient(to right, <neg> 0%, <pos> 100%)`. */\n fg_color?: string;\n /** The Datagrid's font for a number column: a registry theme's numeric face, when it is not the body face. */\n font_family?: string;\n};\n/** The Datagrid's own settings: a registry theme's body face and size. */\nexport type PerspectivePluginConfig = { font_family: string; font_size: number };\n/** What `perspectiveConfig` returns: pass it to `viewer.restore()`, with your own view keys merged in. */\nexport type PerspectiveConfig = {\n plugin: string;\n /** Perspective's own theme: \"Pro Light\" or \"Pro Dark\" (load `@perspective-dev/viewer/dist/css/pro.css` / `pro-dark.css`). */\n theme: \"Pro Light\" | \"Pro Dark\";\n /** The Datagrid with a registry `theme` only. */\n plugin_config?: PerspectivePluginConfig;\n columns_config: Record<string, PerspectiveColumnConfig>;\n};\n/**\n * The parts of a registry table theme `perspectiveConfig` reads. A `TableThemeSpec` (sdvtables'\n * `tableThemeSpec(name, mode)`, Python's `table_theme_spec`) is one; sdvplot does not ship the registry itself.\n */\nexport interface PerspectiveTheme {\n /** `body`: the grid's face. `numeric`: the number columns' face, when it is not `body`. */\n readonly fonts: { readonly body?: string; readonly numeric?: string };\n /** `body`: the grid's font size in px. */\n readonly sizes: { readonly body: number };\n /** The number colours: `neg` (low) to `pos` (high). */\n readonly palette: { readonly neg: string; readonly pos: string };\n}\n\n/** `perspectiveConfig` options. */\nexport interface PerspectiveConfigOptions {\n /** The column holding the team identifiers (any id system `resolve` reads). */\n teamCol: string;\n /** The SDV league key, e.g. \"nfl\". */\n league: League;\n /** The plugin: \"Datagrid\" (the default) or a chart's name (\"Y Bar\", \"X Bar\", \"Y Line\", \"X/Y Scatter\", …). */\n plugin?: string;\n /** \"light\" (default) or \"dark\": the Perspective theme, \"Pro Light\" or \"Pro Dark\". */\n mode?: \"light\" | \"dark\";\n /**\n * A registry table theme: best as a function of the mode, `(m) => tableThemeSpec(name, m)` from\n * `@sportsdataverse/sdvtables`, called with this call's `mode` on every plugin (so a light-only theme in dark mode\n * throws, as in Python). A spec object works too; it does not say its mode, so resolve it in the same `mode`.\n * The Datagrid takes its body face and size (`plugin_config`), its sign colours on every number column (`fg_color`;\n * Perspective reads only `#hex` and `rgb()` colours there) and, when the numeric face is not the body face, that face\n * on the number columns. Perspective takes each face as one family name, so the page loads the font (Google Fonts)\n * or the browser draws its default. A chart takes none of it.\n */\n theme?: PerspectiveTheme | ((mode: \"light\" | \"dark\") => PerspectiveTheme);\n /** A logo URL column (`perspectiveLogoColumn`) to show as links on the Datagrid, which draws no images. */\n logoCol?: string;\n /** The colour the teams must read against. Default: the Perspective theme's (#ffffff Pro Light, #242526 Pro Dark). */\n background?: string;\n /** Per column, a `number_format` to use instead of the default, or null for none. */\n formats?: Readonly<Record<string, PerspectiveNumberFormat | null>>;\n /** One season for every team. */\n season?: SeasonInput;\n /** How the team values are read. Default \"auto\". */\n idSystem?: IdSystem;\n /** Throw UnresolvedTeamError for a team that does not resolve, instead of one warning and #808080. */\n strict?: boolean;\n /**\n * The column types, as `await table.schema()` gives them. Default: worked out from the rows as Perspective does\n * (numbers, and strings that read as numbers, are \"integer\" or \"float\"; anything else is not a number). Pass it when\n * you load the table with a schema of your own.\n */\n schema?: PerspectiveSchema;\n}\n\n// Perspective's two built-in themes and their --psp--background-color (5.5.1's pro.css and pro-dark.css)\nconst PRO = { light: [\"Pro Light\", \"#ffffff\"], dark: [\"Pro Dark\", \"#242526\"] } as const;\nconst FALLBACK = \"#808080\";\n// A float column whose name says it is a percentage (fg_pct, pct_complete, win_percent) shows 1 decimal; any other\n// float (a rate, EPA) shows 3; an integer is shown as typed (1039 and 2024, not 1,039 or 2,024)\nconst PERCENT = /(^|_)(pct|percent|perc)(_|$)/i;\n\nfunction numberFormat(name: string, type: PerspectiveType): PerspectiveNumberFormat {\n if (type === \"integer\") return { useGrouping: false, maximumFractionDigits: 0 };\n const n = PERCENT.test(name) ? 1 : 3;\n return { minimumFractionDigits: n, maximumFractionDigits: n };\n}\n\nconst INT = /^\\s*-?\\d+\\s*$/;\nconst FLOAT = /^\\s*-?(\\d+\\.?\\d*|\\.\\d+)([eE][-+]?\\d+)?\\s*$/;\n/** A value's Perspective number type, or \"other\" (null for a missing value). */\nfunction kindOf(v: unknown): \"integer\" | \"float\" | \"other\" | null {\n if (v === null || v === undefined) return null;\n if (typeof v === \"bigint\") return \"integer\";\n if (typeof v === \"number\") {\n if (!Number.isFinite(v)) return \"other\";\n return Number.isInteger(v) && Math.abs(v) < 2 ** 31 ? \"integer\" : \"float\";\n }\n if (typeof v === \"string\") return INT.test(v) ? \"integer\" : FLOAT.test(v) ? \"float\" : \"other\";\n return \"other\";\n}\n\n/**\n * The rows' schema, as `perspective.table(rows)` works it out for the number columns (checked against Perspective\n * 5.5.1 in test/perspective.test.ts). ponytail: a non-number column is \"string\" here, whatever Perspective makes of it\n * (a boolean, a date): sdvplot only asks whether a column is a number.\n */\nfunction inferSchema(rows: readonly PerspectiveRow[]): Record<string, PerspectiveType> {\n const kinds = new Map<string, Set<string>>();\n for (const r of rows)\n for (const [k, v] of Object.entries(r)) {\n const s = kinds.get(k) ?? kinds.set(k, new Set()).get(k)!;\n const kind = kindOf(v);\n if (kind !== null) s.add(kind);\n }\n return Object.fromEntries(\n [...kinds].map(([k, s]) => [\n k,\n s.size === 0 || s.has(\"other\") ? \"string\" : s.has(\"float\") ? \"float\" : \"integer\",\n ]),\n );\n}\n\nconst isNumber = (t: PerspectiveType | undefined): boolean => t === \"integer\" || t === \"float\";\nconst quote = (s: string): string => `'${s}'`;\nconst isObj = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null;\nconst isFace = (v: unknown): boolean => v === undefined || typeof v === \"string\";\n/** A given theme (or what its function returned) must have every field perspectiveConfig reads. */\nconst isTheme = (t: unknown): t is PerspectiveTheme =>\n isObj(t) &&\n isObj(t.fonts) &&\n isFace(t.fonts.body) &&\n isFace(t.fonts.numeric) &&\n isObj(t.sizes) &&\n typeof t.sizes.body === \"number\" &&\n isObj(t.palette) &&\n typeof t.palette.neg === \"string\" &&\n typeof t.palette.pos === \"string\";\n\nfunction checkColumns(schema: PerspectiveSchema, cols: readonly (string | undefined)[]): void {\n const names = Object.keys(schema);\n for (const c of cols)\n if (c !== undefined && !Object.hasOwn(schema, c))\n throw new InputError(`the rows have no column ${quote(c)} (columns: ${names.join(\", \")})`);\n}\n\n/**\n * The distinct non-null team values in the order a grid or a scatter's Color slot meets them (first appearance), and in\n * a chart's split order: sorted, numerically for a number column, else by code unit as Perspective sorts strings\n * (upper case first). InputError for numbers and strings mixed.\n */\nfunction teamOrders(values: readonly unknown[], numeric: boolean, col: string): [unknown[], unknown[]] {\n const first = [...new Set(values.filter((v) => v !== null && v !== undefined))];\n const types = new Set(first.map((v) => (typeof v === \"bigint\" ? \"number\" : typeof v)));\n if (types.size > 1)\n throw new InputError(\n `the team column ${quote(col)} mixes ${[...types].sort().join(\" and \")} values: use one id system`,\n );\n const key = numeric ? (v: unknown) => Number(v) : (v: unknown) => String(v);\n const sorted = [...first].sort((a, b) => {\n const [x, y] = [key(a), key(b)];\n return x < y ? -1 : x > y ? 1 : 0;\n });\n return [first, sorted];\n}\n\nconst gradient = (colors: readonly string[]): string => `linear-gradient(to right, ${colors.join(\", \")})`;\n\n/**\n * A Perspective viewer config that colours each team by its own colours, for the Datagrid or a chart: the argument of\n * `viewer.restore()`, as Python's `perspective_sdv_config` builds it (same keys, same colours, same order).\n *\n * The colours are the shared team palette (every adapter's, and Python's), taken over the teams in sorted order: each\n * team's colour, made 3:1 against the background and ΔE 20 from every earlier team's, so a team keeps one colour\n * whatever the rows' order. Perspective takes a palette, not a colour per value, so each palette lists those colours in\n * the order Perspective hands colours out (checked in Chromium on Perspective 5.5.1):\n *\n * - `plugin: \"Datagrid\"`: the team column's cells are filled (`bg_mode` \"series\"), in the order the grid first draws\n * each value: the rows' order. Keep the grid in the rows' order (no `sort`) and to about one row per team. Null\n * cells take no colour and no slot. Only a text team column is filled: Perspective fills no number column, so\n * numeric team ids get none, with one warning.\n * - a chart: split a bar, line or area chart by the team (`split_by: [teamCol]`) and each series takes its colour\n * from every number column's `palette`, in sorted order (Perspective sorts split values; a null series comes first,\n * so the palette then starts with the fallback grey). Put the team column in an \"X/Y Scatter\"'s Color slot (its\n * third column) and each team's points take theirs from the team column's `palette`, in the rows' order.\n *\n * Every number column also gets a `number_format` (Intl.NumberFormat options): 3 decimals for a float, 1 for a float\n * named as a percentage (`pct`, `percent`), and an integer plain (2024, not 2,024). `formats` overrides them.\n *\n * @param rows - the rows the viewer shows (what `perspective.table(rows)` loads). With no rows and no `schema` no column is\n * known: the config has `plugin` and `theme` and an empty `columns_config`, without an error (pass the table's\n * `schema` for its number formats, as Python's empty frame keeps its columns).\n * @returns `plugin`, `theme` and `columns_config`; merge your own `group_by`, `split_by`, `columns` and the rest in.\n * @throws InputError for a column the rows do not have (`teamCol`, `logoCol` or a `formats` key), a team column that\n * mixes numbers and strings, an unknown `mode`, a `theme` that is not a spec (or a function returning one), or a\n * season outside the league's. UnresolvedTeamError with `strict: true`. A `theme` function's own errors pass through\n * (sdvtables' TableSpecError for a light-only theme in dark mode).\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { perspectiveConfig } from \"@sportsdataverse/sdvplot/perspective\";\n *\n * await loadLeague(\"nfl\");\n * // 2024 regular season, offence: EPA per play (nflverse play-by-play)\n * const epa = [\n * { posteam: \"BAL\", epa_per_play: 0.218 },\n * { posteam: \"BUF\", epa_per_play: 0.1885 },\n * { posteam: \"DET\", epa_per_play: 0.1592 },\n * ];\n * const grid = perspectiveConfig(epa, { teamCol: \"posteam\", league: \"nfl\" });\n * const bars = {\n * ...perspectiveConfig(epa, { teamCol: \"posteam\", league: \"nfl\", plugin: \"Y Bar\" }),\n * group_by: [\"posteam\"],\n * split_by: [\"posteam\"],\n * columns: [\"epa_per_play\"],\n * };\n * // in the browser: await viewer.load(await worker.table(epa)); await viewer.restore(grid)\n * [grid.columns_config.posteam?.bg_color, bars.columns_config.epa_per_play?.palette];\n * ```\n */\nexport function perspectiveConfig(\n rows: readonly PerspectiveRow[],\n o: PerspectiveConfigOptions,\n): PerspectiveConfig {\n const mode = o.mode ?? \"light\";\n if (mode !== \"light\" && mode !== \"dark\")\n throw new InputError(`mode must be 'light' or 'dark', not ${quote(String(mode))}`);\n // resolved before the plugin is known, as Python resolves theme= on every plugin (a light-only theme in dark throws)\n const resolved = typeof o.theme === \"function\" ? o.theme(mode) : o.theme;\n if (o.theme !== undefined && !isTheme(resolved))\n throw new InputError(\n `theme must be a table theme spec or a function of the mode, not ${quote(String(resolved))}: pass (m) => tableThemeSpec(name, m) from @sportsdataverse/sdvtables`,\n );\n const schema = o.schema ?? inferSchema(rows);\n const { teamCol, logoCol } = o;\n // no rows and no schema: no column is known, so none is missing (Python's empty frame still has its columns)\n if (o.schema !== undefined || rows.length > 0)\n checkColumns(schema, [teamCol, logoCol, ...Object.keys(o.formats ?? {})]);\n const [pro, proBg] = PRO[mode];\n const bg = o.background === undefined ? proBg : hex6(o.background);\n const season = normSeason(o.season, o.league);\n const text = !isNumber(schema[teamCol]); // Perspective fills a string column's cells, never a number column's\n const numeric = Object.keys(schema).filter((c) => c !== teamCol && isNumber(schema[c]));\n const grid = (o.plugin ?? \"Datagrid\") === \"Datagrid\";\n const values = rows.map((r) => r[teamCol]);\n const [first, ascending] = teamOrders(values, !text, teamCol);\n // the shared team palette every adapter uses (Python's perspective._palette): unresolved values grey, out of it\n const pal = colorPalette(o.league, ascending, {\n ...(season === null ? {} : { season }),\n background: bg,\n idSystem: o.idSystem ?? \"auto\",\n strict: o.strict ?? false,\n fallback: FALLBACK,\n });\n const color = new Map(ascending.map((v, i) => [v, pal[i] as string]));\n // a null split series comes first: palette entry 0\n const nulls = values.some((v) => v === null || v === undefined) ? [FALLBACK] : [];\n const byAppearance = gradient(first.map((v) => color.get(v) as string));\n const byValue = gradient([...nulls, ...pal]);\n let columns: Record<string, PerspectiveColumnConfig> = {};\n for (const c of numeric) {\n const fmt =\n o.formats !== undefined && Object.hasOwn(o.formats, c) ? o.formats[c] : numberFormat(c, schema[c]!);\n columns[c] = fmt === null || fmt === undefined ? {} : { number_format: fmt };\n }\n // no teams (no rows) means no palette at all: an empty palette is not a valid CSS value\n if (first.length > 0 && !grid) {\n for (const c of numeric) columns[c]!.palette = byValue;\n if (text) columns = { [teamCol]: { palette: byAppearance }, ...columns }; // a number Color slot is a gradient\n } else if (first.length > 0 && text) {\n columns = { [teamCol]: { bg_mode: \"series\", bg_color: byAppearance }, ...columns };\n } else if (first.length > 0) {\n warn(\n `perspective:numeric-team:${teamCol}`,\n `no team colors on the Datagrid: the team column ${quote(teamCol)} is numeric, and team colors need a string or categorical team column (Perspective fills only text cells)`,\n );\n }\n const theme = grid ? resolved : undefined; // a chart takes no fonts from a config\n if (theme !== undefined) {\n const { body, numeric: face } = theme.fonts;\n const sign = `linear-gradient(to right, ${theme.palette.neg} 0%, ${theme.palette.pos} 100%)`;\n for (const c of numeric) {\n columns[c]!.fg_color = sign;\n if (face && face !== body) columns[c]!.font_family = face;\n }\n }\n if (grid && logoCol !== undefined) columns[logoCol] = { link: true };\n const gridFace = theme?.fonts.body;\n return {\n plugin: o.plugin ?? \"Datagrid\",\n theme: pro,\n ...(theme !== undefined && gridFace\n ? { plugin_config: { font_family: gridFace, font_size: theme.sizes.body } }\n : {}),\n columns_config: Object.fromEntries(Object.entries(columns).filter(([, v]) => Object.keys(v).length > 0)),\n };\n}\n\n/** `perspectiveLogoColumn` options. */\nexport interface PerspectiveLogoOptions {\n /** The SDV league key, e.g. \"nfl\". */\n league: League;\n /** The new column's name. Default: `${teamCol}_logo`. It must not be a column already. */\n name?: string;\n /** One season for every team: the logo of that season (relocations and rebrands). */\n season?: SeasonInput;\n /** \"default\", \"dark\", or a named variant from `marks()`. */\n variant?: Variant;\n /** How the team values are read. Default \"auto\". */\n idSystem?: IdSystem;\n /** Throw UnresolvedTeamError for a team that does not resolve, instead of warning. */\n strict?: boolean;\n}\n\n/**\n * The rows with a column of each team's logo URL, right after the team column, as Python's `perspective_logo_column`\n * adds it. Each distinct team is resolved once. A team that does not resolve, or has no archived logo, gets a null URL,\n * with one warning for each reason. Show the column as links with `perspectiveConfig(rows, { logoCol })`: Perspective\n * 5.5's Datagrid draws no images. The rows given are not changed.\n *\n * @returns new rows, each with the URL (a string, or null) in `name`.\n * @throws InputError for a `teamCol` no row has, a `name` a row already has, or a season outside the league's.\n * UnresolvedTeamError with `strict: true`.\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { perspectiveConfig, perspectiveLogoColumn } from \"@sportsdataverse/sdvplot/perspective\";\n *\n * await loadLeague(\"nfl\");\n * const epa = [\n * { posteam: \"BAL\", epa_per_play: 0.218 },\n * { posteam: \"BUF\", epa_per_play: 0.1885 },\n * ];\n * const rows = perspectiveLogoColumn(epa, \"posteam\", { league: \"nfl\" });\n * const config = perspectiveConfig(rows, { teamCol: \"posteam\", league: \"nfl\", logoCol: \"posteam_logo\" });\n * [Object.keys(rows[0] ?? {}), config.columns_config.posteam_logo];\n * ```\n */\nexport function perspectiveLogoColumn<R extends PerspectiveRow>(\n rows: readonly R[],\n teamCol: string,\n o: PerspectiveLogoOptions,\n): (R & Record<string, string | null>)[] {\n const name = o.name ?? `${teamCol}_logo`;\n if (rows.length > 0 && !rows.some((r) => Object.hasOwn(r, teamCol)))\n throw new InputError(`the rows have no column ${quote(teamCol)}`);\n if (rows.some((r) => Object.hasOwn(r, name)))\n throw new InputError(`the rows already have a column ${quote(name)}: pass another name`);\n const values = [...new Set(rows.map((r) => r[teamCol]).filter((v) => v !== null && v !== undefined))];\n const at = values.map((_, i) => i);\n const placed = placeSync(\n at,\n at.map(() => 0),\n values as Value[],\n {\n league: o.league,\n season: normSeason(o.season, o.league),\n kind: \"logo\",\n variant: o.variant ?? \"default\",\n idSystem: o.idSystem ?? \"auto\",\n strict: o.strict ?? false,\n },\n );\n const urls = new Map<unknown, string>(placed.map((p) => [values[p.index], p.url]));\n return rows.map((r) => {\n const out: Record<string, unknown> = {};\n const url = urls.get(r[teamCol]) ?? null;\n for (const [k, v] of Object.entries(r)) {\n out[k] = v;\n if (k === teamCol) out[name] = url;\n }\n if (!Object.hasOwn(out, name)) out[name] = url;\n return out as R & Record<string, string | null>;\n });\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/data/aliases/phf.ts
|
|
2
|
+
var aliases = [
|
|
3
|
+
{ "id_system": "mark", "value": "nwhl.co:6335", "team_id": "61638", "valid_from": null, "valid_to": null },
|
|
4
|
+
{ "id_system": "mark", "value": "nwhl.co:6336", "team_id": "61635", "valid_from": null, "valid_to": null },
|
|
5
|
+
{ "id_system": "mark", "value": "nwhl.co:6337", "team_id": "61637", "valid_from": null, "valid_to": null },
|
|
6
|
+
{ "id_system": "mark", "value": "shiftstats:124984", "team_id": "124984", "valid_from": null, "valid_to": null },
|
|
7
|
+
{ "id_system": "mark", "value": "shiftstats:207478", "team_id": "207478", "valid_from": null, "valid_to": null },
|
|
8
|
+
{ "id_system": "mark", "value": "shiftstats:61635", "team_id": "61635", "valid_from": null, "valid_to": null },
|
|
9
|
+
{ "id_system": "mark", "value": "shiftstats:61636", "team_id": "61636", "valid_from": null, "valid_to": null },
|
|
10
|
+
{ "id_system": "mark", "value": "shiftstats:61637", "team_id": "61637", "valid_from": null, "valid_to": null },
|
|
11
|
+
{ "id_system": "mark", "value": "shiftstats:61638", "team_id": "61638", "valid_from": null, "valid_to": null },
|
|
12
|
+
{ "id_system": "mark", "value": "shiftstats:61639", "team_id": "61639", "valid_from": null, "valid_to": null },
|
|
13
|
+
{ "id_system": "mark", "value": "shiftstats:79963", "team_id": "79963", "valid_from": null, "valid_to": null },
|
|
14
|
+
{ "id_system": "name", "value": "Boston Pride", "team_id": "61638", "valid_from": null, "valid_to": null },
|
|
15
|
+
{ "id_system": "name", "value": "Buffalo Beauts", "team_id": "61635", "valid_from": null, "valid_to": null },
|
|
16
|
+
{ "id_system": "name", "value": "Connecticut Whale", "team_id": "61637", "valid_from": null, "valid_to": null },
|
|
17
|
+
{ "id_system": "name", "value": "Metropolitan Riveters", "team_id": "124984", "valid_from": null, "valid_to": null },
|
|
18
|
+
{ "id_system": "name", "value": "Metropolitan Riveters", "team_id": "61636", "valid_from": null, "valid_to": null },
|
|
19
|
+
{ "id_system": "name", "value": "Minnesota Whitecaps", "team_id": "61639", "valid_from": null, "valid_to": null },
|
|
20
|
+
{ "id_system": "name", "value": "Montreal Force", "team_id": "207478", "valid_from": null, "valid_to": null },
|
|
21
|
+
{ "id_system": "name", "value": "Toronto Six", "team_id": "79963", "valid_from": null, "valid_to": null },
|
|
22
|
+
{ "id_system": "team_id", "value": "124984", "team_id": "124984", "valid_from": null, "valid_to": null },
|
|
23
|
+
{ "id_system": "team_id", "value": "207478", "team_id": "207478", "valid_from": null, "valid_to": null },
|
|
24
|
+
{ "id_system": "team_id", "value": "61635", "team_id": "61635", "valid_from": null, "valid_to": null },
|
|
25
|
+
{ "id_system": "team_id", "value": "61636", "team_id": "61636", "valid_from": null, "valid_to": null },
|
|
26
|
+
{ "id_system": "team_id", "value": "61637", "team_id": "61637", "valid_from": null, "valid_to": null },
|
|
27
|
+
{ "id_system": "team_id", "value": "61638", "team_id": "61638", "valid_from": null, "valid_to": null },
|
|
28
|
+
{ "id_system": "team_id", "value": "61639", "team_id": "61639", "valid_from": null, "valid_to": null },
|
|
29
|
+
{ "id_system": "team_id", "value": "79963", "team_id": "79963", "valid_from": null, "valid_to": null }
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export { aliases };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/data/teams/phf.ts
|
|
2
|
+
var teams = [
|
|
3
|
+
{ "league": "phf", "team_id": "124984", "abbr": null, "name": "Metropolitan Riveters", "short_name": null, "location": null, "program": "pro", "conference_id": null, "conference": null, "color_primary": "#0e2039", "color_secondary": "#47485b", "color_source": "logo" },
|
|
4
|
+
{ "league": "phf", "team_id": "207478", "abbr": null, "name": "Montreal Force", "short_name": null, "location": null, "program": "pro", "conference_id": null, "conference": null, "color_primary": "#901b39", "color_secondary": "#a46d86", "color_source": "logo" },
|
|
5
|
+
{ "league": "phf", "team_id": "61635", "abbr": null, "name": "Buffalo Beauts", "short_name": null, "location": null, "program": "pro", "conference_id": null, "conference": null, "color_primary": "#7f7f7f", "color_secondary": "#000000", "color_source": "logo" },
|
|
6
|
+
{ "league": "phf", "team_id": "61636", "abbr": null, "name": "Metropolitan Riveters", "short_name": null, "location": null, "program": "pro", "conference_id": null, "conference": null, "color_primary": "#f6dfa4", "color_secondary": "#00609c", "color_source": "logo" },
|
|
7
|
+
{ "league": "phf", "team_id": "61637", "abbr": null, "name": "Connecticut Whale", "short_name": null, "location": null, "program": "pro", "conference_id": null, "conference": null, "color_primary": "#1a6492", "color_secondary": "#006f3c", "color_source": "logo" },
|
|
8
|
+
{ "league": "phf", "team_id": "61638", "abbr": null, "name": "Boston Pride", "short_name": null, "location": null, "program": "pro", "conference_id": null, "conference": null, "color_primary": "#897d61", "color_secondary": "#bda87d", "color_source": "logo" },
|
|
9
|
+
{ "league": "phf", "team_id": "61639", "abbr": null, "name": "Minnesota Whitecaps", "short_name": null, "location": null, "program": "pro", "conference_id": null, "conference": null, "color_primary": "#1b449c", "color_secondary": "#7b91c1", "color_source": "logo" },
|
|
10
|
+
{ "league": "phf", "team_id": "79963", "abbr": null, "name": "Toronto Six", "short_name": null, "location": null, "program": "pro", "conference_id": null, "conference": null, "color_primary": "#ed2229", "color_secondary": null, "color_source": "logo" }
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export { teams };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// src/data/marks/phf.ts
|
|
2
|
+
var marks = [
|
|
3
|
+
{ "team_id": "79963", "mark_type": "logo", "variant": "medium", "valid_from": null, "valid_to": null, "source": "shiftstats", "first_seen": "2026-09-30", "sha256": "099ff3ddfda22129777813def6c25a066ddffbfd34c26467d0fbd85c2794684d", "ext": "png", "width": 55, "height": 45, "source_rank": 4 },
|
|
4
|
+
{ "team_id": "61637", "mark_type": "logo", "variant": "alternate", "valid_from": null, "valid_to": null, "source": "nwhl.co", "first_seen": "2026-09-30", "sha256": "f004fc4b1a48c3bad2f40457fbd27a7df9c593b527e4dae1e900d5f4488ddba5", "ext": "png", "width": 680, "height": 616, "source_rank": 4 },
|
|
5
|
+
{ "team_id": "207478", "mark_type": "logo", "variant": "large", "valid_from": 2023, "valid_to": 2023, "source": "shiftstats", "first_seen": "2026-09-30", "sha256": "cb447e8c1bbc1e3ba14b259a1ec2ec2145bc4a88ea58b67ea9b239bc57d1f729", "ext": "png", "width": 115, "height": 95, "source_rank": 4 },
|
|
6
|
+
{ "team_id": "124984", "mark_type": "logo", "variant": "large", "valid_from": 2022, "valid_to": 2023, "source": "shiftstats", "first_seen": "2026-09-30", "sha256": "f0070a6a350f4551dc439d37a4350c0a53c5c8579e9c1c7f2be4f05ec5a3f5ad", "ext": "png", "width": 115, "height": 95, "source_rank": 4 },
|
|
7
|
+
{ "team_id": "79963", "mark_type": "logo", "variant": "large", "valid_from": 2021, "valid_to": 2023, "source": "shiftstats", "first_seen": "2026-09-30", "sha256": "518f97a7fa5277f1ea25305b6f4b3b50b77ff35adf3bb9a91d2b8006beaca018", "ext": "png", "width": 115, "height": 95, "source_rank": 4 },
|
|
8
|
+
{ "team_id": "61639", "mark_type": "logo", "variant": "large", "valid_from": 2019, "valid_to": 2023, "source": "shiftstats", "first_seen": "2026-09-30", "sha256": "b589e3a1705ded8554666ec4675bf78be889f7d4bd8f840c667d9f59640f0b4d", "ext": "svg", "width": null, "height": null, "source_rank": 4 },
|
|
9
|
+
{ "team_id": "61637", "mark_type": "logo", "variant": "medium", "valid_from": 2017, "valid_to": 2023, "source": "shiftstats", "first_seen": "2026-09-30", "sha256": "47ca405c65b3eccf3b115cf163925e04998ff4dd011925ce8eee1c69112ff359", "ext": "svg", "width": null, "height": null, "source_rank": 4 },
|
|
10
|
+
{ "team_id": "61635", "mark_type": "logo", "variant": "medium", "valid_from": 2017, "valid_to": 2023, "source": "shiftstats", "first_seen": "2026-09-30", "sha256": "6cf0a82ec98b3332e4476a9e920117782d686785474da9d220e3955c757aa3b9", "ext": "svg", "width": null, "height": null, "source_rank": 4 },
|
|
11
|
+
{ "team_id": "61638", "mark_type": "logo", "variant": "medium", "valid_from": 2017, "valid_to": 2023, "source": "shiftstats", "first_seen": "2026-09-30", "sha256": "a4fc8fc96dd81c2e29da89dab6371c2c36db86528e0d23b9452147ba59febf63", "ext": "svg", "width": null, "height": null, "source_rank": 4 },
|
|
12
|
+
{ "team_id": "61636", "mark_type": "logo", "variant": "medium", "valid_from": 2017, "valid_to": 2021, "source": "shiftstats", "first_seen": "2026-09-30", "sha256": "e934e1421bf1aad308318e391195418bfc5d7b207e663b555cb7f088bb769410", "ext": "svg", "width": null, "height": null, "source_rank": 4 },
|
|
13
|
+
{ "team_id": "61635", "mark_type": "wordmark", "variant": "default", "valid_from": 2016, "valid_to": 2016, "source": "nwhl.co", "first_seen": "2026-09-30", "sha256": "88db9e8018962a2b5cff735fe5857107bf586cdd257d4aa2f70417b86853cc55", "ext": "png", "width": 680, "height": 267, "source_rank": 4 },
|
|
14
|
+
{ "team_id": "61637", "mark_type": "logo", "variant": "default", "valid_from": 2016, "valid_to": 2016, "source": "nwhl.co", "first_seen": "2026-09-30", "sha256": "b6417b2623d6c9d12e173efefc43fa85f3acaac5ef5761fdc2dd95f8004a1b82", "ext": "png", "width": 680, "height": 639, "source_rank": 4 },
|
|
15
|
+
{ "team_id": "61635", "mark_type": "logo", "variant": "default", "valid_from": 2016, "valid_to": 2016, "source": "nwhl.co", "first_seen": "2026-09-30", "sha256": "cacf3c1c989af45ee947eea0304a3fb3f9b632a8a9c0028afeee75bab5feef79", "ext": "png", "width": 586, "height": 498, "source_rank": 4 },
|
|
16
|
+
{ "team_id": "61638", "mark_type": "logo", "variant": "default", "valid_from": 2016, "valid_to": 2016, "source": "nwhl.co", "first_seen": "2026-09-30", "sha256": "cf2e36cc271ca9af358ae5629aa48832098df389cc9c496d98edcde469c0003a", "ext": "png", "width": 487, "height": 271, "source_rank": 4 },
|
|
17
|
+
{ "team_id": "61635", "mark_type": "logo", "variant": "dark", "valid_from": 2016, "valid_to": 2016, "source": "nwhl.co", "first_seen": "2026-09-30", "sha256": "fb60f47920f3b8d18ab63d75a314d1505e967f9fd62c7e7d453df50cab409c0b", "ext": "png", "width": 617, "height": 478, "source_rank": 4 }
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
export { marks };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { L as League, S as SeasonInput, V as Variant, I as IdSystem, H as HeadshotIdSystem, a as MarkRow } from './types-C7n6gJF3.js';
|
|
2
|
+
import { V as Value } from './resolve-CQW-jzvr.js';
|
|
3
|
+
|
|
4
|
+
type Kind = "logo" | "wordmark" | "headshot";
|
|
5
|
+
/** One point to draw: the caller's own x/y, the image URL and its width / height ratio (null when unknown). `index` is the row's position in the input arrays (skipped rows leave gaps). */
|
|
6
|
+
interface Placement {
|
|
7
|
+
index: number;
|
|
8
|
+
id: string;
|
|
9
|
+
x: Value;
|
|
10
|
+
y: Value;
|
|
11
|
+
url: string;
|
|
12
|
+
aspect: number | null;
|
|
13
|
+
mark: MarkRow | null;
|
|
14
|
+
}
|
|
15
|
+
interface PlaceOptions {
|
|
16
|
+
league: League;
|
|
17
|
+
season?: SeasonInput | readonly SeasonInput[];
|
|
18
|
+
kind?: Kind;
|
|
19
|
+
variant?: Variant;
|
|
20
|
+
/** Team id system for logos/wordmarks (default "auto"); "espn" or "gsis" for headshots (default "espn"). */
|
|
21
|
+
idSystem?: IdSystem | HeadshotIdSystem;
|
|
22
|
+
strict?: boolean;
|
|
23
|
+
warn?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
declare function checkHeight(height: unknown): number;
|
|
27
|
+
declare function checkAlpha(alpha: unknown): number;
|
|
28
|
+
/** Port of `_placement.place` (post-preload): pairs each (x, y) with its team's mark or player's headshot, dropping and warning (once per reason) on rows that cannot be drawn. Needs `loadLeague(league)` (or `preloadAll()`) first; gsis headshots also need `loadGsis()`. */
|
|
29
|
+
declare function placeSync(xs: readonly Value[], ys: readonly Value[], teams: readonly Value[], o: PlaceOptions): Placement[];
|
|
30
|
+
declare function place(xs: readonly Value[], ys: readonly Value[], teams: readonly Value[], o: PlaceOptions): Promise<Placement[]>;
|
|
31
|
+
|
|
32
|
+
export { type Kind as K, type Placement as P, type PlaceOptions as a, checkHeight as b, checkAlpha as c, placeSync as d, place as p };
|