@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
package/dist/d3.d.ts
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { Selection, BaseType, ScaleOrdinal } from 'd3';
|
|
2
|
+
import { E as EspnHeadshotLeague } from './headshots-B2J_LOO8.js';
|
|
3
|
+
import { V as Value } from './resolve-CQW-jzvr.js';
|
|
4
|
+
import { S as SurfaceSceneOptions } from './surfaces-W-odWLpT.js';
|
|
5
|
+
import { L as League, S as SeasonInput, V as Variant, I as IdSystem, H as HeadshotIdSystem, W as Which } from './types-C7n6gJF3.js';
|
|
6
|
+
import { BinShape } from './bins.js';
|
|
7
|
+
import { DiffScale, SignaturePoint } from './shots.js';
|
|
8
|
+
import '@sportsdataverse/sporty';
|
|
9
|
+
|
|
10
|
+
/** Face options for `appendHeadshots` (blazing-the-nets `main` scatter "faces", `lib/charts/scatterChart.ts:139-191`). */
|
|
11
|
+
interface FaceOptions {
|
|
12
|
+
/**
|
|
13
|
+
* `"circle"`: the headshot inside a circle whose diameter is `height` × `frameHeight`. The image is drawn 2.3 R tall
|
|
14
|
+
* so the head fills the circle, so `drawnMarks` reports 1.15 × `height` for a face.
|
|
15
|
+
*/
|
|
16
|
+
clip?: "circle";
|
|
17
|
+
/** Ring colour per player id (e.g. the team colour; `:183-188`, 1.5 px). Default none. */
|
|
18
|
+
ring?: string | ((id: string) => string);
|
|
19
|
+
/** A disc behind the face at 0.35 opacity, still there if the image fails (`:169-172`). Default none. */
|
|
20
|
+
placeholder?: string | ((id: string) => string);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Any `<g>` selection, whatever its datum (d3's `Selection` is invariant in it; see `Sel` in ./index.ts). */
|
|
24
|
+
type G<D> = Selection<SVGGElement, D, BaseType, unknown>;
|
|
25
|
+
/** Options for `appendLegend`. */
|
|
26
|
+
interface AppendLegendOptions {
|
|
27
|
+
/** Top-left corner of the legend in px; default (0, 0). */
|
|
28
|
+
x?: number;
|
|
29
|
+
/** See `x`. */
|
|
30
|
+
y?: number;
|
|
31
|
+
/** Space available, which the text wraps to; the bar is at most 240 px (theme.ts:122). */
|
|
32
|
+
width?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Diffs the bar spans; default the scale's end stops. `master`'s legend spanned [-0.30, 0.30]
|
|
35
|
+
* (`ShootingSignature/Legend.js:36-38`): `{ domain: [-0.3, 0.3], ticks: [-0.3, -0.15, 0, 0.15, 0.3] }`.
|
|
36
|
+
*/
|
|
37
|
+
domain?: readonly [lo: number, hi: number];
|
|
38
|
+
/** Tick diffs; default the domain's ends, and 0 when it lies inside them. */
|
|
39
|
+
ticks?: readonly number[];
|
|
40
|
+
/** Tick text; default signed points, "0" at 0 (theme.ts:140). */
|
|
41
|
+
format?: (d: number) => string;
|
|
42
|
+
/** Caption and key in words (theme.ts:35, :120); `null` for none. */
|
|
43
|
+
caption?: readonly string[] | null;
|
|
44
|
+
/** More lines under the caption, wrapped to `width` (e.g. a shrinkage note). */
|
|
45
|
+
notes?: readonly string[];
|
|
46
|
+
/** A cell size key under the colour key: pixel size per attempt count and the counts (`sizeCells(...).steps`). */
|
|
47
|
+
size?: {
|
|
48
|
+
readonly px: (attempts: number) => number;
|
|
49
|
+
readonly steps: readonly number[];
|
|
50
|
+
/** The key's cell shape; default `"hex"`. */
|
|
51
|
+
readonly shape?: BinShape;
|
|
52
|
+
/** Default `"hex size: attempts"` or `"square size: attempts"`. */
|
|
53
|
+
readonly note?: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A diff-colour legend for d3 charts: gradient bar, ticks, caption, notes and an optional cell size key
|
|
58
|
+
* (blazing-the-nets `main` `lib/charts/theme.ts:109-143`, `lib/charts/hexShotChart.ts:196-213`). It replaces
|
|
59
|
+
* `master`'s static PNG ramp (`ShootingSignature/Legend.js:45-50`, `rgy5.png`) with a gradient generated from the
|
|
60
|
+
* scale, whose id is a hash of its stops. Nothing is measured (text wraps at an estimated 6.2 px per character), so
|
|
61
|
+
* it renders the same in jsdom and SSR. Returns the height used. Plot users: `Plot.legend({ color: scale.plot })`.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* import { create } from "d3";
|
|
66
|
+
* import { appendLegend } from "@sportsdataverse/sdvplot/d3";
|
|
67
|
+
* import { diffScale } from "@sportsdataverse/sdvplot/shots";
|
|
68
|
+
*
|
|
69
|
+
* const svg = create("svg").attr("viewBox", "0 0 300 80");
|
|
70
|
+
* appendLegend(svg.append("g"), diffScale(), { width: 280 });
|
|
71
|
+
* svg.node();
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
declare function appendLegend<D>(g: G<D>, scale?: DiffScale, o?: AppendLegendOptions): number;
|
|
75
|
+
/** Options for `appendSignature`. */
|
|
76
|
+
interface AppendSignatureOptions {
|
|
77
|
+
/** The caller's scale from distance (ft) to px. */
|
|
78
|
+
x: (distance: number) => number;
|
|
79
|
+
/** The caller's scale from FG% (0-1) to px; a clamped scale clamps the ribbon's centre. */
|
|
80
|
+
y: (fgPct: number) => number;
|
|
81
|
+
/** Colour of `colourDiff`; default `diffScale()`. */
|
|
82
|
+
fill?: DiffScale;
|
|
83
|
+
/** `"monotone-x"` (default, `main`) or `"basis"` (`master` `ShootingSignature/index.js:96`). */
|
|
84
|
+
curve?: "monotone-x" | "basis";
|
|
85
|
+
/** Ribbon half-width in PIXELS; default `main`'s `max(share / maxShare * 20, 0.75)` (`shootingSignature.ts:131-132`). */
|
|
86
|
+
halfWidth?: (p: SignaturePoint, maxShare: number) => number;
|
|
87
|
+
/** The dashed league line, faint everywhere and full strength under the ribbon (`:165-181`); default true. */
|
|
88
|
+
league?: boolean;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The d3 twin of `sdvplot/plot`'s `shootingSignature`: the dashed league line and the ribbon along FG% by distance,
|
|
92
|
+
* as wide as the shot share and coloured along x by the shrunk FG% vs league (blazing-the-nets `main`
|
|
93
|
+
* `lib/charts/shootingSignature.ts:165-211`). The same points give the Plot mark's ribbon and league paths, gradient
|
|
94
|
+
* stops and gradient id when the scales match (with a 190 px [0, 1] y axis for the default half-width). A null
|
|
95
|
+
* `fgPct` is a gap. The gradient id hashes its stops and its pixel x range, so two signatures on a page, or one at
|
|
96
|
+
* two widths, never share one. y goes through the caller's `y` as it is: the ribbon's edges are
|
|
97
|
+
* `y(fgPct) ± halfWidth`, and a clamped scale clamps the centre (as `main` does). Returns the `<g>` it appended.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* import { create, scaleLinear } from "d3";
|
|
102
|
+
* import { appendSignature } from "@sportsdataverse/sdvplot/d3";
|
|
103
|
+
* import { fgPctByDistance, signaturePoints, vsLeague } from "@sportsdataverse/sdvplot/shots";
|
|
104
|
+
*
|
|
105
|
+
* // Brooklyn shots from the sportsdataverse-data nba_stats_shots release, 2025-26
|
|
106
|
+
* const shots = [
|
|
107
|
+
* { x_legacy: -1, y_legacy: 7, shot_distance: 1, shot_value: 2, shot_result: "Made" },
|
|
108
|
+
* { x_legacy: 0, y_legacy: 0, shot_distance: 0, shot_value: 2, shot_result: "Missed" },
|
|
109
|
+
* { x_legacy: -44, y_legacy: 252, shot_distance: 26, shot_value: 3, shot_result: "Made" },
|
|
110
|
+
* ];
|
|
111
|
+
* // the makes against all three attempts, which stand in for the league (the gallery's uses the 2025-26 league)
|
|
112
|
+
* const made = fgPctByDistance(shots.filter((s) => s.shot_result === "Made"));
|
|
113
|
+
* const points = signaturePoints(vsLeague(made, fgPctByDistance(shots)), { minAttempts: 1 });
|
|
114
|
+
* const svg = create("svg").attr("viewBox", "0 0 400 200");
|
|
115
|
+
* appendSignature(svg.append("g"), points, { x: scaleLinear([0, 35], [0, 400]), y: scaleLinear([0, 1], [200, 0]) });
|
|
116
|
+
* svg.node();
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
declare function appendSignature<D>(sel: G<D>, points: readonly SignaturePoint[], o: AppendSignatureOptions): G<D>;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A selection of `G` with any datum `D` and any parent. d3's `Selection` is invariant in its datum, so a helper typed
|
|
123
|
+
* with `unknown` would reject `create("svg").append("g")` (datum `undefined`) and every bound datum.
|
|
124
|
+
*/
|
|
125
|
+
type Sel<G extends Element, D> = Selection<G, D, BaseType, unknown>;
|
|
126
|
+
/** Where d3 draws: the caller's scales, plus the pixel height the `height` fraction refers to (d3 has no figure). */
|
|
127
|
+
interface Frame {
|
|
128
|
+
x: (v: Value) => number;
|
|
129
|
+
y: (v: Value) => number;
|
|
130
|
+
frameHeight: number;
|
|
131
|
+
height?: number;
|
|
132
|
+
alpha?: number;
|
|
133
|
+
/**
|
|
134
|
+
* Each image's accessible name from its input value and index; default the team ("KC logo", whatever id system the
|
|
135
|
+
* values use) or the player id ("3139477 headshot").
|
|
136
|
+
*/
|
|
137
|
+
ariaLabel?: (value: Value, i: number) => string;
|
|
138
|
+
}
|
|
139
|
+
interface D3MarkOptions extends Frame {
|
|
140
|
+
league: League;
|
|
141
|
+
season?: SeasonInput | readonly SeasonInput[];
|
|
142
|
+
variant?: Variant;
|
|
143
|
+
idSystem?: IdSystem;
|
|
144
|
+
}
|
|
145
|
+
interface D3HeadshotOptions extends Frame, FaceOptions {
|
|
146
|
+
league: EspnHeadshotLeague;
|
|
147
|
+
/** "espn" (default) or "gsis". "gsis" needs `loadGsis()` to have run first, else this throws InputError. */
|
|
148
|
+
idSystem?: HeadshotIdSystem;
|
|
149
|
+
}
|
|
150
|
+
type AppendTeamMarks = <G extends SVGElement, D>(sel: Sel<G, D>, xs: ArrayLike<Value>, ys: ArrayLike<Value>, teams: readonly Value[], o: D3MarkOptions) => Sel<SVGGElement, D>;
|
|
151
|
+
/**
|
|
152
|
+
* Append `<g class="sdv-logos">` of team logos at (x, y) through the caller's scale functions. Needs
|
|
153
|
+
* `loadLeague(league)` first.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* import { loadLeague } from "@sportsdataverse/sdvplot";
|
|
158
|
+
* import { appendLogos } from "@sportsdataverse/sdvplot/d3";
|
|
159
|
+
* import * as d3 from "d3";
|
|
160
|
+
*
|
|
161
|
+
* await loadLeague("nfl");
|
|
162
|
+
* const x = d3.scaleLinear([250, 550], [0, 480]);
|
|
163
|
+
* const y = d3.scaleLinear([280, 420], [240, 0]);
|
|
164
|
+
* const svg = d3.select(document.createElement("div")).append("svg").attr("viewBox", [0, 0, 480, 240]);
|
|
165
|
+
* appendLogos(svg, [385, 525, 425], [326, 368, 311], ["KC", "BUF", "DEN"], {
|
|
166
|
+
* league: "nfl",
|
|
167
|
+
* x: (v) => x(Number(v)),
|
|
168
|
+
* y: (v) => y(Number(v)),
|
|
169
|
+
* frameHeight: 240,
|
|
170
|
+
* height: 0.2,
|
|
171
|
+
* });
|
|
172
|
+
* svg.node();
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
declare const appendLogos: AppendTeamMarks;
|
|
176
|
+
/** As `appendLogos`, with wordmarks. */
|
|
177
|
+
declare const appendWordmarks: AppendTeamMarks;
|
|
178
|
+
/**
|
|
179
|
+
* Append `<g class="sdv-headshots">` of ESPN player headshots; `idSystem: "gsis"` needs `loadGsis()` first.
|
|
180
|
+
* `clip: "circle"` makes circular faces (`height` is then the circle's diameter), with an optional `ring` and `placeholder`.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* import { appendHeadshots } from "@sportsdataverse/sdvplot/d3";
|
|
185
|
+
* import * as d3 from "d3";
|
|
186
|
+
*
|
|
187
|
+
* const x = d3.scaleLinear([0, 10], [0, 480]);
|
|
188
|
+
* const y = d3.scaleLinear([0, 10], [240, 0]);
|
|
189
|
+
* const svg = d3.select(document.createElement("div")).append("svg").attr("viewBox", [0, 0, 480, 240]);
|
|
190
|
+
* const team: Record<string, string> = { "3139477": "#e31837", "4374302": "#0076b6", "4429160": "#008e97" };
|
|
191
|
+
* appendHeadshots(svg, [2, 5, 8], [4, 6, 3], ["3139477", "4374302", "4429160"], {
|
|
192
|
+
* league: "nfl",
|
|
193
|
+
* x: (v) => x(Number(v)),
|
|
194
|
+
* y: (v) => y(Number(v)),
|
|
195
|
+
* frameHeight: 240,
|
|
196
|
+
* height: 0.25,
|
|
197
|
+
* clip: "circle",
|
|
198
|
+
* ring: (id) => team[id] ?? "grey",
|
|
199
|
+
* placeholder: (id) => team[id] ?? "grey",
|
|
200
|
+
* });
|
|
201
|
+
* svg.node();
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
declare function appendHeadshots<G extends SVGElement, D>(sel: Sel<G, D>, xs: ArrayLike<Value>, ys: ArrayLike<Value>, players: readonly Value[], o: D3HeadshotOptions): Sel<SVGGElement, D>;
|
|
205
|
+
/**
|
|
206
|
+
* A d3 ordinal scale from team (id or abbr) to team colour; anything unresolved maps to `naValue` (default "grey").
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* import { loadLeague } from "@sportsdataverse/sdvplot";
|
|
211
|
+
* import { teamColorScale } from "@sportsdataverse/sdvplot/d3";
|
|
212
|
+
*
|
|
213
|
+
* await loadLeague("nba");
|
|
214
|
+
* const color = teamColorScale("nba", { values: ["LAL", "BOS"] });
|
|
215
|
+
* ["LAL", "BOS", "not a team"].map(color);
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
declare function teamColorScale(league: League, o?: {
|
|
219
|
+
which?: Which;
|
|
220
|
+
season?: SeasonInput;
|
|
221
|
+
idSystem?: IdSystem;
|
|
222
|
+
naValue?: string;
|
|
223
|
+
values?: readonly Value[];
|
|
224
|
+
}): ScaleOrdinal<string, string, string>;
|
|
225
|
+
interface D3SurfaceOptions extends SurfaceSceneOptions {
|
|
226
|
+
x: (v: number) => number;
|
|
227
|
+
y: (v: number) => number;
|
|
228
|
+
/** Describes the surface for assistive technology; default sporty's ("nba basketball surface" and so on). */
|
|
229
|
+
ariaDescription?: string;
|
|
230
|
+
}
|
|
231
|
+
/** Paint a surface key's surface (in `team`'s colours) into `sel` through the caller's x/y scales. Throws InputError for a key with no surface (see `SURFACES`). */
|
|
232
|
+
declare function appendSurface<G extends SVGGElement | SVGSVGElement, D>(sel: Sel<G, D>, league: League | (string & {}), o: D3SurfaceOptions): Sel<SVGGElement, D>;
|
|
233
|
+
|
|
234
|
+
export { type AppendLegendOptions, type AppendSignatureOptions, type AppendTeamMarks, type D3HeadshotOptions, type D3MarkOptions, type D3SurfaceOptions, type FaceOptions, appendHeadshots, appendLegend, appendLogos, appendSignature, appendSurface, appendWordmarks, teamColorScale };
|
package/dist/d3.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { teamColorDomain, stampImage } from './chunk-OIILARXI.js';
|
|
2
|
+
import { diffScale, signatureGradient, SIGNATURE_NAME } from './chunk-H2C3FMMA.js';
|
|
3
|
+
import { contentId } from './chunk-WQT4IVLX.js';
|
|
4
|
+
import { cellPath } from './chunk-C2CDY5OG.js';
|
|
5
|
+
import { checkHeight, checkAlpha, placeSync, placedName } from './chunk-7ULOCCFL.js';
|
|
6
|
+
import { surfaceScene } from './chunk-57WFSI4Q.js';
|
|
7
|
+
import './chunk-ZAEQXFJM.js';
|
|
8
|
+
import './chunk-J6R2LEJ7.js';
|
|
9
|
+
import { InputError } from './chunk-E7G7VSLV.js';
|
|
10
|
+
import { appendSurface as appendSurface$1 } from '@sportsdataverse/sporty/d3';
|
|
11
|
+
import { curveBasis, curveMonotoneX, line, area, scaleOrdinal } from 'd3';
|
|
12
|
+
|
|
13
|
+
// src/d3/faces.ts
|
|
14
|
+
var NS = "http://www.w3.org/2000/svg";
|
|
15
|
+
var pick = (c, id) => typeof c === "function" ? c(id) : c;
|
|
16
|
+
function clipFaces(g, o) {
|
|
17
|
+
if (o.clip !== "circle") return;
|
|
18
|
+
for (const img of g.selectAll("image").nodes()) {
|
|
19
|
+
const doc = img.ownerDocument;
|
|
20
|
+
const id = img.getAttribute("data-sdv-id") ?? "";
|
|
21
|
+
const w0 = Number(img.getAttribute("width"));
|
|
22
|
+
const h0 = Number(img.getAttribute("height"));
|
|
23
|
+
const cx = Number(img.getAttribute("x")) + w0 / 2;
|
|
24
|
+
const cy = Number(img.getAttribute("y")) + h0 / 2;
|
|
25
|
+
const r = h0 / 2;
|
|
26
|
+
const h = 2.3 * r;
|
|
27
|
+
const w = h0 > 0 ? h * w0 / h0 : 0;
|
|
28
|
+
img.setAttribute("width", String(w));
|
|
29
|
+
img.setAttribute("height", String(h));
|
|
30
|
+
img.setAttribute("x", String(cx - w / 2));
|
|
31
|
+
img.setAttribute("y", String(cy - 1.05 * r));
|
|
32
|
+
const clipId = contentId("sdv-face", `${id}|${cx}|${cy}|${r}`);
|
|
33
|
+
const clip = doc.createElementNS(NS, "clipPath");
|
|
34
|
+
clip.setAttribute("id", clipId);
|
|
35
|
+
const disc = doc.createElementNS(NS, "circle");
|
|
36
|
+
disc.setAttribute("cx", String(cx));
|
|
37
|
+
disc.setAttribute("cy", String(cy));
|
|
38
|
+
disc.setAttribute("r", String(r));
|
|
39
|
+
clip.appendChild(disc);
|
|
40
|
+
img.setAttribute("clip-path", `url(#${clipId})`);
|
|
41
|
+
img.before(clip);
|
|
42
|
+
const fill = pick(o.placeholder, id);
|
|
43
|
+
if (fill !== void 0) {
|
|
44
|
+
const ph = disc.cloneNode();
|
|
45
|
+
ph.setAttribute("fill", fill);
|
|
46
|
+
ph.setAttribute("fill-opacity", "0.35");
|
|
47
|
+
img.before(ph);
|
|
48
|
+
}
|
|
49
|
+
const stroke = pick(o.ring, id);
|
|
50
|
+
if (stroke !== void 0) {
|
|
51
|
+
const ring = disc.cloneNode();
|
|
52
|
+
ring.setAttribute("fill", "none");
|
|
53
|
+
ring.setAttribute("stroke", stroke);
|
|
54
|
+
ring.setAttribute("stroke-width", "1.5");
|
|
55
|
+
img.after(ring);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
var FONT_PX = 11;
|
|
60
|
+
var CHAR_PX = 6.2;
|
|
61
|
+
var LINE_H = 15;
|
|
62
|
+
var MUTED = "var(--sdv-muted, currentColor)";
|
|
63
|
+
var fmtPts = (v) => `${v >= 0 ? "+" : "\u2212"}${Math.abs(v * 100).toFixed(1)}`;
|
|
64
|
+
function wrap(text, maxWidth) {
|
|
65
|
+
const perLine = Math.max(8, Math.floor(maxWidth / CHAR_PX));
|
|
66
|
+
const lines = [];
|
|
67
|
+
let cur = "";
|
|
68
|
+
for (const word of text.split(" ")) {
|
|
69
|
+
if (cur && cur.length + 1 + word.length > perLine) {
|
|
70
|
+
lines.push(cur);
|
|
71
|
+
cur = word;
|
|
72
|
+
} else cur = cur ? `${cur} ${word}` : word;
|
|
73
|
+
}
|
|
74
|
+
if (cur) lines.push(cur);
|
|
75
|
+
return lines;
|
|
76
|
+
}
|
|
77
|
+
function appendLegend(g, scale = diffScale(), o = {}) {
|
|
78
|
+
const x = o.x ?? 0;
|
|
79
|
+
const y = o.y ?? 0;
|
|
80
|
+
const maxWidth = o.width ?? 240;
|
|
81
|
+
const width = Math.min(maxWidth, 240);
|
|
82
|
+
const [lo, hi] = o.domain ?? [scale.stops[0] ?? 0, scale.stops.at(-1) ?? 0];
|
|
83
|
+
if (!(Number.isFinite(lo) && Number.isFinite(hi) && hi > lo))
|
|
84
|
+
throw new InputError(`appendLegend domain must be finite and run low to high, got [${lo}, ${hi}]`);
|
|
85
|
+
const at = [lo, ...scale.stops.filter((d) => d > lo && d < hi), hi];
|
|
86
|
+
const stops = at.map((d) => [(d - lo) / (hi - lo) * 100, scale(d)]);
|
|
87
|
+
const id = contentId("sdv-legend", stops.map((s) => s.join(" ")).join(";"));
|
|
88
|
+
const grad = g.append("defs").append("linearGradient").attr("id", id);
|
|
89
|
+
for (const [offset, colour] of stops)
|
|
90
|
+
grad.append("stop").attr("offset", `${offset}%`).attr("stop-color", colour);
|
|
91
|
+
const format = o.format ?? ((d) => d === 0 ? "0" : fmtPts(d));
|
|
92
|
+
g.append("rect").attr("x", x).attr("y", y).attr("width", width).attr("height", 8).attr("rx", 2).attr("aria-label", `colour key, FG% vs league: ${format(lo)} to ${format(hi)}`).style("fill", `url(#${id})`);
|
|
93
|
+
for (const d of o.ticks ?? (lo < 0 && hi > 0 ? [lo, 0, hi] : [lo, hi])) {
|
|
94
|
+
g.append("text").attr("x", x + (d - lo) / (hi - lo) * width).attr("y", y + 8 + FONT_PX + 2).attr("text-anchor", d === lo ? "start" : d === hi ? "end" : "middle").style("font-size", `${FONT_PX}px`).style("fill", MUTED).text(format(d));
|
|
95
|
+
}
|
|
96
|
+
let used = 8 + FONT_PX + 6;
|
|
97
|
+
const caption = o.caption === void 0 ? ["FG% vs league (points)", "red: above league \xB7 blue: below"] : o.caption ?? [];
|
|
98
|
+
const lines = [...caption, ...o.notes ?? []].flatMap((n) => wrap(n, maxWidth));
|
|
99
|
+
lines.forEach((t, i) => {
|
|
100
|
+
g.append("text").attr("x", x).attr("y", y + used + FONT_PX + i * LINE_H).style("font-size", `${FONT_PX}px`).style("fill", MUTED).text(t);
|
|
101
|
+
});
|
|
102
|
+
used += lines.length * LINE_H;
|
|
103
|
+
const size = o.size;
|
|
104
|
+
if (size) {
|
|
105
|
+
const shape = size.shape ?? "hex";
|
|
106
|
+
const key = g.append("g").attr("transform", `translate(${x},${y + used + 8})`);
|
|
107
|
+
size.steps.forEach((n, i) => {
|
|
108
|
+
const cx = 12 + i * 34;
|
|
109
|
+
key.append("path").attr("transform", `translate(${cx},10)`).attr("d", cellPath(shape, size.px(n))).style("fill", MUTED);
|
|
110
|
+
key.append("text").attr("x", cx).attr("y", 36).attr("text-anchor", "middle").style("font-size", `${FONT_PX}px`).style("fill", MUTED).text(i === size.steps.length - 1 ? `${n}+` : String(n));
|
|
111
|
+
});
|
|
112
|
+
const note = wrap(size.note ?? `${shape} size: attempts`, maxWidth - 118);
|
|
113
|
+
note.forEach((t, i) => {
|
|
114
|
+
key.append("text").attr("x", 118).attr("y", 4 + FONT_PX + i * LINE_H).style("font-size", `${FONT_PX}px`).style("fill", MUTED).text(t);
|
|
115
|
+
});
|
|
116
|
+
used += 8 + Math.max(42, 8 + note.length * LINE_H);
|
|
117
|
+
}
|
|
118
|
+
return used;
|
|
119
|
+
}
|
|
120
|
+
function appendSignature(sel, points, o) {
|
|
121
|
+
const curve = (o.curve ?? "monotone-x") === "basis" ? curveBasis : curveMonotoneX;
|
|
122
|
+
const { maxShare, maxFt, stops, id: gradientId } = signatureGradient(points, o.fill ?? diffScale());
|
|
123
|
+
const half = o.halfWidth ?? ((p, m) => Math.max(p.share / m * 20, 0.75));
|
|
124
|
+
const g = sel.append("g").attr("class", "sdv-signature");
|
|
125
|
+
if (o.league ?? true) {
|
|
126
|
+
for (const strong of [false, true]) {
|
|
127
|
+
const l = line().defined((p) => p.leagueFgPct !== null && (!strong || p.fgPct !== null)).x((p) => o.x(p.distance)).y((p) => o.y(p.leagueFgPct ?? 0)).curve(curve);
|
|
128
|
+
g.append("path").attr("d", l([...points]) ?? "").style("fill", "none").style("stroke", MUTED).style("stroke-opacity", strong ? 1 : 0.4).style("stroke-dasharray", "3 3");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const [x1, x2] = [String(o.x(0)), String(o.x(maxFt))];
|
|
132
|
+
const id = gradientId(x1, x2);
|
|
133
|
+
const grad = g.append("defs").append("linearGradient").attr("id", id).attr("gradientUnits", "userSpaceOnUse").attr("x1", x1).attr("x2", x2).attr("y1", "0").attr("y2", "0");
|
|
134
|
+
for (const [offset, colour] of stops)
|
|
135
|
+
grad.append("stop").attr("offset", `${offset}%`).attr("stop-color", colour);
|
|
136
|
+
const ribbon = area().defined((p) => p.fgPct !== null).x((p) => o.x(p.distance)).y0((p) => o.y(p.fgPct ?? 0) + half(p, maxShare)).y1((p) => o.y(p.fgPct ?? 0) - half(p, maxShare)).curve(curve);
|
|
137
|
+
g.append("path").attr("d", ribbon([...points]) ?? "").attr("aria-label", SIGNATURE_NAME).style("fill", `url(#${id})`).style("stroke", "currentColor").style("stroke-opacity", 0.2).style("stroke-width", 0.5);
|
|
138
|
+
return g;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/d3/index.ts
|
|
142
|
+
function draw(sel, xs, ys, ids, o, kind, place) {
|
|
143
|
+
const h = checkHeight(o.height ?? 0.1);
|
|
144
|
+
const alpha = checkAlpha(o.alpha ?? 1);
|
|
145
|
+
if (!(Number.isFinite(o.frameHeight) && o.frameHeight > 0)) {
|
|
146
|
+
throw new InputError(`frameHeight is the plot height in px, got ${String(o.frameHeight)}`);
|
|
147
|
+
}
|
|
148
|
+
const px = h * o.frameHeight;
|
|
149
|
+
const placed = placeSync(Array.from(xs), Array.from(ys), ids, place);
|
|
150
|
+
const g = sel.append("g").attr("class", `sdv-${kind}s`).attr("opacity", alpha);
|
|
151
|
+
for (const p of placed) {
|
|
152
|
+
const img = g.append("image").attr("href", p.url).attr("preserveAspectRatio", "xMidYMid meet");
|
|
153
|
+
const node = img.node();
|
|
154
|
+
if (node === null) continue;
|
|
155
|
+
stampImage(node, p, kind, px, o.frameHeight, o.x(p.x), o.y(p.y));
|
|
156
|
+
img.attr("data-sdv-x", String(p.x)).attr("data-sdv-y", String(p.y));
|
|
157
|
+
const raw = ids[p.index];
|
|
158
|
+
img.attr("aria-label", o.ariaLabel?.(raw, p.index) ?? `${placedName(p, place.league, raw)} ${kind}`);
|
|
159
|
+
}
|
|
160
|
+
return g;
|
|
161
|
+
}
|
|
162
|
+
var teamMark = (kind) => (sel, xs, ys, teams, o) => draw(sel, xs, ys, teams, o, kind, {
|
|
163
|
+
league: o.league,
|
|
164
|
+
kind,
|
|
165
|
+
variant: o.variant ?? "default",
|
|
166
|
+
...o.idSystem === void 0 ? {} : { idSystem: o.idSystem },
|
|
167
|
+
...o.season === void 0 ? {} : { season: o.season }
|
|
168
|
+
});
|
|
169
|
+
var appendLogos = teamMark("logo");
|
|
170
|
+
var appendWordmarks = teamMark("wordmark");
|
|
171
|
+
function appendHeadshots(sel, xs, ys, players, o) {
|
|
172
|
+
const g = draw(sel, xs, ys, players, o, "headshot", {
|
|
173
|
+
league: o.league,
|
|
174
|
+
kind: "headshot",
|
|
175
|
+
idSystem: o.idSystem ?? "espn"
|
|
176
|
+
});
|
|
177
|
+
clipFaces(g, o);
|
|
178
|
+
return g;
|
|
179
|
+
}
|
|
180
|
+
function teamColorScale(league, o = {}) {
|
|
181
|
+
const na = o.naValue ?? "grey";
|
|
182
|
+
const { domain, colors } = teamColorDomain(league, o);
|
|
183
|
+
return scaleOrdinal().domain(domain).range(colors.map((c) => c ?? na)).unknown(na);
|
|
184
|
+
}
|
|
185
|
+
function appendSurface(sel, league, o) {
|
|
186
|
+
const { x, y, ariaDescription, ...scene } = o;
|
|
187
|
+
return appendSurface$1(
|
|
188
|
+
sel,
|
|
189
|
+
surfaceScene(league, scene),
|
|
190
|
+
x,
|
|
191
|
+
y,
|
|
192
|
+
ariaDescription === void 0 ? {} : { ariaDescription }
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export { appendHeadshots, appendLegend, appendLogos, appendSignature, appendSurface, appendWordmarks, teamColorScale };
|
|
197
|
+
//# sourceMappingURL=d3.js.map
|
|
198
|
+
//# sourceMappingURL=d3.js.map
|
package/dist/d3.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/d3/faces.ts","../src/d3/shots.ts","../src/d3/index.ts"],"names":["sportyAppendSurface"],"mappings":"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","file":"d3.js","sourcesContent":["import type { BaseType, Selection } from \"d3\";\nimport { contentId } from \"../content-id.js\";\n\n/** Face options for `appendHeadshots` (blazing-the-nets `main` scatter \"faces\", `lib/charts/scatterChart.ts:139-191`). */\nexport interface FaceOptions {\n /**\n * `\"circle\"`: the headshot inside a circle whose diameter is `height` × `frameHeight`. The image is drawn 2.3 R tall\n * so the head fills the circle, so `drawnMarks` reports 1.15 × `height` for a face.\n */\n clip?: \"circle\";\n /** Ring colour per player id (e.g. the team colour; `:183-188`, 1.5 px). Default none. */\n ring?: string | ((id: string) => string);\n /** A disc behind the face at 0.35 opacity, still there if the image fails (`:169-172`). Default none. */\n placeholder?: string | ((id: string) => string);\n}\n\nconst NS = \"http://www.w3.org/2000/svg\";\nconst pick = (c: FaceOptions[\"ring\"], id: string): string | undefined =>\n typeof c === \"function\" ? c(id) : c;\n\n/** Turn `appendHeadshots`' images into circular faces in place. */\nexport function clipFaces<D>(g: Selection<SVGGElement, D, BaseType, unknown>, o: FaceOptions): void {\n if (o.clip !== \"circle\") return;\n for (const img of g.selectAll<SVGImageElement, unknown>(\"image\").nodes()) {\n const doc = img.ownerDocument;\n const id = img.getAttribute(\"data-sdv-id\") ?? \"\";\n const w0 = Number(img.getAttribute(\"width\"));\n const h0 = Number(img.getAttribute(\"height\"));\n const cx = Number(img.getAttribute(\"x\")) + w0 / 2;\n const cy = Number(img.getAttribute(\"y\")) + h0 / 2;\n const r = h0 / 2;\n const h = 2.3 * r; // the head fills the circle: image 2.3 R tall, top 1.05 R above centre (:177-181)\n const w = h0 > 0 ? (h * w0) / h0 : 0;\n img.setAttribute(\"width\", String(w));\n img.setAttribute(\"height\", String(h));\n img.setAttribute(\"x\", String(cx - w / 2));\n img.setAttribute(\"y\", String(cy - 1.05 * r));\n const clipId = contentId(\"sdv-face\", `${id}|${cx}|${cy}|${r}`);\n const clip = doc.createElementNS(NS, \"clipPath\");\n clip.setAttribute(\"id\", clipId);\n const disc = doc.createElementNS(NS, \"circle\");\n disc.setAttribute(\"cx\", String(cx));\n disc.setAttribute(\"cy\", String(cy));\n disc.setAttribute(\"r\", String(r));\n clip.appendChild(disc);\n img.setAttribute(\"clip-path\", `url(#${clipId})`);\n img.before(clip);\n const fill = pick(o.placeholder, id);\n if (fill !== undefined) {\n const ph = disc.cloneNode() as Element;\n ph.setAttribute(\"fill\", fill);\n ph.setAttribute(\"fill-opacity\", \"0.35\");\n img.before(ph);\n }\n const stroke = pick(o.ring, id);\n if (stroke !== undefined) {\n const ring = disc.cloneNode() as Element;\n ring.setAttribute(\"fill\", \"none\");\n ring.setAttribute(\"stroke\", stroke);\n ring.setAttribute(\"stroke-width\", \"1.5\");\n img.after(ring);\n }\n }\n}\n","// d3 twins of the shot-chart marks: blazing-the-nets `main` lib/charts/theme.ts (legend), hexShotChart.ts (size key)\n// and shootingSignature.ts (@31427b8), which are d3 themselves. Never imports ../plot/*.\nimport { type BaseType, type Selection, area, curveBasis, curveMonotoneX, line } from \"d3\";\nimport { type BinShape, cellPath } from \"../bins/index.js\";\nimport { contentId } from \"../content-id.js\";\nimport { InputError } from \"../errors.js\";\nimport { type DiffScale, diffScale } from \"../shots/diff.js\";\nimport { SIGNATURE_NAME, type SignaturePoint, signatureGradient } from \"../shots/signature.js\";\n\n/** Any `<g>` selection, whatever its datum (d3's `Selection` is invariant in it; see `Sel` in ./index.ts). */\ntype G<D> = Selection<SVGGElement, D, BaseType, unknown>;\nconst FONT_PX = 11; // blazing-the-nets main lib/charts/theme.ts:24\nconst CHAR_PX = 6.2; // theme.ts:26\nconst LINE_H = 15; // theme.ts:92\nconst MUTED = \"var(--sdv-muted, currentColor)\"; // main's TOKENS.muted where the page sets it, else the page's ink\n\n/** Signed points from a fraction (blazing-the-nets `main` `lib/format.ts:8`): 0.042 → \"+4.2\", -0.042 → \"−4.2\". */\nconst fmtPts = (v: number): string => `${v >= 0 ? \"+\" : \"−\"}${Math.abs(v * 100).toFixed(1)}`;\n\n/** Greedy word wrap at an estimated 6.2 px per character (theme.ts:77-90): no measuring, so SSR-safe. */\nfunction wrap(text: string, maxWidth: number): string[] {\n const perLine = Math.max(8, Math.floor(maxWidth / CHAR_PX));\n const lines: string[] = [];\n let cur = \"\";\n for (const word of text.split(\" \")) {\n if (cur && cur.length + 1 + word.length > perLine) {\n lines.push(cur);\n cur = word;\n } else cur = cur ? `${cur} ${word}` : word;\n }\n if (cur) lines.push(cur);\n return lines;\n}\n\n/** Options for `appendLegend`. */\nexport interface AppendLegendOptions {\n /** Top-left corner of the legend in px; default (0, 0). */\n x?: number;\n /** See `x`. */\n y?: number;\n /** Space available, which the text wraps to; the bar is at most 240 px (theme.ts:122). */\n width?: number;\n /**\n * Diffs the bar spans; default the scale's end stops. `master`'s legend spanned [-0.30, 0.30]\n * (`ShootingSignature/Legend.js:36-38`): `{ domain: [-0.3, 0.3], ticks: [-0.3, -0.15, 0, 0.15, 0.3] }`.\n */\n domain?: readonly [lo: number, hi: number];\n /** Tick diffs; default the domain's ends, and 0 when it lies inside them. */\n ticks?: readonly number[];\n /** Tick text; default signed points, \"0\" at 0 (theme.ts:140). */\n format?: (d: number) => string;\n /** Caption and key in words (theme.ts:35, :120); `null` for none. */\n caption?: readonly string[] | null;\n /** More lines under the caption, wrapped to `width` (e.g. a shrinkage note). */\n notes?: readonly string[];\n /** A cell size key under the colour key: pixel size per attempt count and the counts (`sizeCells(...).steps`). */\n size?: {\n readonly px: (attempts: number) => number;\n readonly steps: readonly number[];\n /** The key's cell shape; default `\"hex\"`. */\n readonly shape?: BinShape;\n /** Default `\"hex size: attempts\"` or `\"square size: attempts\"`. */\n readonly note?: string;\n };\n}\n\n/**\n * A diff-colour legend for d3 charts: gradient bar, ticks, caption, notes and an optional cell size key\n * (blazing-the-nets `main` `lib/charts/theme.ts:109-143`, `lib/charts/hexShotChart.ts:196-213`). It replaces\n * `master`'s static PNG ramp (`ShootingSignature/Legend.js:45-50`, `rgy5.png`) with a gradient generated from the\n * scale, whose id is a hash of its stops. Nothing is measured (text wraps at an estimated 6.2 px per character), so\n * it renders the same in jsdom and SSR. Returns the height used. Plot users: `Plot.legend({ color: scale.plot })`.\n *\n * @example\n * ```ts\n * import { create } from \"d3\";\n * import { appendLegend } from \"@sportsdataverse/sdvplot/d3\";\n * import { diffScale } from \"@sportsdataverse/sdvplot/shots\";\n *\n * const svg = create(\"svg\").attr(\"viewBox\", \"0 0 300 80\");\n * appendLegend(svg.append(\"g\"), diffScale(), { width: 280 });\n * svg.node();\n * ```\n */\nexport function appendLegend<D>(\n g: G<D>,\n scale: DiffScale = diffScale(),\n o: AppendLegendOptions = {},\n): number {\n const x = o.x ?? 0;\n const y = o.y ?? 0;\n const maxWidth = o.width ?? 240;\n const width = Math.min(maxWidth, 240);\n const [lo, hi] = o.domain ?? [scale.stops[0] ?? 0, scale.stops.at(-1) ?? 0];\n if (!(Number.isFinite(lo) && Number.isFinite(hi) && hi > lo))\n throw new InputError(`appendLegend domain must be finite and run low to high, got [${lo}, ${hi}]`);\n // The domain's ends plus every scale stop inside it: exact for a piecewise-linear scale (master), 5 samples of RdBu.\n const at = [lo, ...scale.stops.filter((d) => d > lo && d < hi), hi];\n const stops = at.map((d) => [((d - lo) / (hi - lo)) * 100, scale(d)] as const);\n const id = contentId(\"sdv-legend\", stops.map((s) => s.join(\" \")).join(\";\"));\n const grad = g.append(\"defs\").append(\"linearGradient\").attr(\"id\", id);\n for (const [offset, colour] of stops)\n grad.append(\"stop\").attr(\"offset\", `${offset}%`).attr(\"stop-color\", colour);\n const format = o.format ?? ((d: number) => (d === 0 ? \"0\" : fmtPts(d)));\n g.append(\"rect\")\n .attr(\"x\", x)\n .attr(\"y\", y)\n .attr(\"width\", width)\n .attr(\"height\", 8)\n .attr(\"rx\", 2)\n .attr(\"aria-label\", `colour key, FG% vs league: ${format(lo)} to ${format(hi)}`)\n .style(\"fill\", `url(#${id})`);\n for (const d of o.ticks ?? (lo < 0 && hi > 0 ? [lo, 0, hi] : [lo, hi])) {\n g.append(\"text\")\n .attr(\"x\", x + ((d - lo) / (hi - lo)) * width)\n .attr(\"y\", y + 8 + FONT_PX + 2)\n .attr(\"text-anchor\", d === lo ? \"start\" : d === hi ? \"end\" : \"middle\")\n .style(\"font-size\", `${FONT_PX}px`)\n .style(\"fill\", MUTED)\n .text(format(d));\n }\n let used = 8 + FONT_PX + 6;\n const caption =\n o.caption === undefined\n ? [\"FG% vs league (points)\", \"red: above league · blue: below\"]\n : (o.caption ?? []);\n const lines = [...caption, ...(o.notes ?? [])].flatMap((n) => wrap(n, maxWidth));\n lines.forEach((t, i) => {\n g.append(\"text\")\n .attr(\"x\", x)\n .attr(\"y\", y + used + FONT_PX + i * LINE_H)\n .style(\"font-size\", `${FONT_PX}px`)\n .style(\"fill\", MUTED)\n .text(t);\n });\n used += lines.length * LINE_H;\n const size = o.size;\n if (size) {\n const shape = size.shape ?? \"hex\";\n const key = g.append(\"g\").attr(\"transform\", `translate(${x},${y + used + 8})`);\n size.steps.forEach((n, i) => {\n const cx = 12 + i * 34;\n key\n .append(\"path\")\n .attr(\"transform\", `translate(${cx},10)`)\n .attr(\"d\", cellPath(shape, size.px(n)))\n .style(\"fill\", MUTED);\n key\n .append(\"text\")\n .attr(\"x\", cx)\n .attr(\"y\", 36)\n .attr(\"text-anchor\", \"middle\")\n .style(\"font-size\", `${FONT_PX}px`)\n .style(\"fill\", MUTED)\n .text(i === size.steps.length - 1 ? `${n}+` : String(n));\n });\n // main's drawNotes(key, 118, 4, width - 24 - 118, …) (hexShotChart.ts:212): wrapped, 8 characters a line at the least\n const note = wrap(size.note ?? `${shape} size: attempts`, maxWidth - 118);\n note.forEach((t, i) => {\n key\n .append(\"text\")\n .attr(\"x\", 118)\n .attr(\"y\", 4 + FONT_PX + i * LINE_H)\n .style(\"font-size\", `${FONT_PX}px`)\n .style(\"fill\", MUTED)\n .text(t);\n });\n used += 8 + Math.max(42, 8 + note.length * LINE_H);\n }\n return used;\n}\n\n/** Options for `appendSignature`. */\nexport interface AppendSignatureOptions {\n /** The caller's scale from distance (ft) to px. */\n x: (distance: number) => number;\n /** The caller's scale from FG% (0-1) to px; a clamped scale clamps the ribbon's centre. */\n y: (fgPct: number) => number;\n /** Colour of `colourDiff`; default `diffScale()`. */\n fill?: DiffScale;\n /** `\"monotone-x\"` (default, `main`) or `\"basis\"` (`master` `ShootingSignature/index.js:96`). */\n curve?: \"monotone-x\" | \"basis\";\n /** Ribbon half-width in PIXELS; default `main`'s `max(share / maxShare * 20, 0.75)` (`shootingSignature.ts:131-132`). */\n halfWidth?: (p: SignaturePoint, maxShare: number) => number;\n /** The dashed league line, faint everywhere and full strength under the ribbon (`:165-181`); default true. */\n league?: boolean;\n}\n\n/**\n * The d3 twin of `sdvplot/plot`'s `shootingSignature`: the dashed league line and the ribbon along FG% by distance,\n * as wide as the shot share and coloured along x by the shrunk FG% vs league (blazing-the-nets `main`\n * `lib/charts/shootingSignature.ts:165-211`). The same points give the Plot mark's ribbon and league paths, gradient\n * stops and gradient id when the scales match (with a 190 px [0, 1] y axis for the default half-width). A null\n * `fgPct` is a gap. The gradient id hashes its stops and its pixel x range, so two signatures on a page, or one at\n * two widths, never share one. y goes through the caller's `y` as it is: the ribbon's edges are\n * `y(fgPct) ± halfWidth`, and a clamped scale clamps the centre (as `main` does). Returns the `<g>` it appended.\n *\n * @example\n * ```ts\n * import { create, scaleLinear } from \"d3\";\n * import { appendSignature } from \"@sportsdataverse/sdvplot/d3\";\n * import { fgPctByDistance, signaturePoints, vsLeague } from \"@sportsdataverse/sdvplot/shots\";\n *\n * // Brooklyn shots from the sportsdataverse-data nba_stats_shots release, 2025-26\n * const shots = [\n * { x_legacy: -1, y_legacy: 7, shot_distance: 1, shot_value: 2, shot_result: \"Made\" },\n * { x_legacy: 0, y_legacy: 0, shot_distance: 0, shot_value: 2, shot_result: \"Missed\" },\n * { x_legacy: -44, y_legacy: 252, shot_distance: 26, shot_value: 3, shot_result: \"Made\" },\n * ];\n * // the makes against all three attempts, which stand in for the league (the gallery's uses the 2025-26 league)\n * const made = fgPctByDistance(shots.filter((s) => s.shot_result === \"Made\"));\n * const points = signaturePoints(vsLeague(made, fgPctByDistance(shots)), { minAttempts: 1 });\n * const svg = create(\"svg\").attr(\"viewBox\", \"0 0 400 200\");\n * appendSignature(svg.append(\"g\"), points, { x: scaleLinear([0, 35], [0, 400]), y: scaleLinear([0, 1], [200, 0]) });\n * svg.node();\n * ```\n */\nexport function appendSignature<D>(\n sel: G<D>,\n points: readonly SignaturePoint[],\n o: AppendSignatureOptions,\n): G<D> {\n const curve = (o.curve ?? \"monotone-x\") === \"basis\" ? curveBasis : curveMonotoneX;\n const { maxShare, maxFt, stops, id: gradientId } = signatureGradient(points, o.fill ?? diffScale());\n const half = o.halfWidth ?? ((p: SignaturePoint, m: number) => Math.max((p.share / m) * 20, 0.75));\n const g = sel.append(\"g\").attr(\"class\", \"sdv-signature\");\n if (o.league ?? true) {\n for (const strong of [false, true]) {\n const l = line<SignaturePoint>()\n .defined((p) => p.leagueFgPct !== null && (!strong || p.fgPct !== null))\n .x((p) => o.x(p.distance))\n .y((p) => o.y(p.leagueFgPct ?? 0))\n .curve(curve);\n g.append(\"path\")\n .attr(\"d\", l([...points]) ?? \"\")\n .style(\"fill\", \"none\")\n .style(\"stroke\", MUTED)\n .style(\"stroke-opacity\", strong ? 1 : 0.4)\n .style(\"stroke-dasharray\", \"3 3\");\n }\n }\n const [x1, x2] = [String(o.x(0)), String(o.x(maxFt))];\n const id = gradientId(x1, x2);\n const grad = g\n .append(\"defs\")\n .append(\"linearGradient\")\n .attr(\"id\", id)\n .attr(\"gradientUnits\", \"userSpaceOnUse\")\n .attr(\"x1\", x1)\n .attr(\"x2\", x2)\n .attr(\"y1\", \"0\")\n .attr(\"y2\", \"0\");\n for (const [offset, colour] of stops)\n grad.append(\"stop\").attr(\"offset\", `${offset}%`).attr(\"stop-color\", colour);\n const ribbon = area<SignaturePoint>()\n .defined((p) => p.fgPct !== null)\n .x((p) => o.x(p.distance))\n .y0((p) => o.y(p.fgPct ?? 0) + half(p, maxShare))\n .y1((p) => o.y(p.fgPct ?? 0) - half(p, maxShare))\n .curve(curve);\n g.append(\"path\")\n .attr(\"d\", ribbon([...points]) ?? \"\")\n .attr(\"aria-label\", SIGNATURE_NAME)\n .style(\"fill\", `url(#${id})`)\n .style(\"stroke\", \"currentColor\")\n .style(\"stroke-opacity\", 0.2)\n .style(\"stroke-width\", 0.5);\n return g;\n}\n","import { appendSurface as sportyAppendSurface } from \"@sportsdataverse/sporty/d3\";\nimport { type BaseType, type ScaleOrdinal, type Selection, scaleOrdinal } from \"d3\";\nimport { InputError } from \"../errors.js\";\nimport type { EspnHeadshotLeague } from \"../headshots.js\";\nimport { type Kind, checkAlpha, checkHeight, placeSync, placedName } from \"../placement.js\";\nimport type { Value } from \"../resolve.js\";\nimport { stampImage } from \"../stamp.js\";\nimport { type SurfaceSceneOptions, surfaceScene } from \"../surfaces.js\";\nimport { teamColorDomain } from \"../team-color-domain.js\";\nimport type { HeadshotIdSystem, IdSystem, League, SeasonInput, Variant, Which } from \"../types.js\";\nimport { type FaceOptions, clipFaces } from \"./faces.js\";\n\n/**\n * A selection of `G` with any datum `D` and any parent. d3's `Selection` is invariant in its datum, so a helper typed\n * with `unknown` would reject `create(\"svg\").append(\"g\")` (datum `undefined`) and every bound datum.\n */\ntype Sel<G extends Element, D> = Selection<G, D, BaseType, unknown>;\n/** Where d3 draws: the caller's scales, plus the pixel height the `height` fraction refers to (d3 has no figure). */\ninterface Frame {\n x: (v: Value) => number;\n y: (v: Value) => number;\n frameHeight: number;\n height?: number;\n alpha?: number;\n /**\n * Each image's accessible name from its input value and index; default the team (\"KC logo\", whatever id system the\n * values use) or the player id (\"3139477 headshot\").\n */\n ariaLabel?: (value: Value, i: number) => string;\n}\nexport interface D3MarkOptions extends Frame {\n league: League;\n season?: SeasonInput | readonly SeasonInput[];\n variant?: Variant;\n idSystem?: IdSystem;\n}\nexport interface D3HeadshotOptions extends Frame, FaceOptions {\n league: EspnHeadshotLeague;\n /** \"espn\" (default) or \"gsis\". \"gsis\" needs `loadGsis()` to have run first, else this throws InputError. */\n idSystem?: HeadshotIdSystem;\n}\n\nfunction draw<G extends SVGElement, D>(\n sel: Sel<G, D>,\n xs: ArrayLike<Value>,\n ys: ArrayLike<Value>,\n ids: readonly Value[],\n o: Frame,\n kind: Kind,\n place: Parameters<typeof placeSync>[3],\n): Sel<SVGGElement, D> {\n const h = checkHeight(o.height ?? 0.1);\n const alpha = checkAlpha(o.alpha ?? 1);\n if (!(Number.isFinite(o.frameHeight) && o.frameHeight > 0)) {\n throw new InputError(`frameHeight is the plot height in px, got ${String(o.frameHeight)}`);\n }\n const px = h * o.frameHeight;\n const placed = placeSync(Array.from(xs), Array.from(ys), ids, place);\n const g = sel.append(\"g\").attr(\"class\", `sdv-${kind}s`).attr(\"opacity\", alpha);\n for (const p of placed) {\n const img = g.append(\"image\").attr(\"href\", p.url).attr(\"preserveAspectRatio\", \"xMidYMid meet\");\n const node = img.node();\n if (node === null) continue;\n stampImage(node, p, kind, px, o.frameHeight, o.x(p.x), o.y(p.y));\n img.attr(\"data-sdv-x\", String(p.x)).attr(\"data-sdv-y\", String(p.y));\n const raw = ids[p.index] as Value;\n img.attr(\"aria-label\", o.ariaLabel?.(raw, p.index) ?? `${placedName(p, place.league, raw)} ${kind}`);\n }\n return g;\n}\nexport type AppendTeamMarks = <G extends SVGElement, D>(\n sel: Sel<G, D>,\n xs: ArrayLike<Value>,\n ys: ArrayLike<Value>,\n teams: readonly Value[],\n o: D3MarkOptions,\n) => Sel<SVGGElement, D>;\nconst teamMark =\n (kind: \"logo\" | \"wordmark\"): AppendTeamMarks =>\n (sel, xs, ys, teams, o) =>\n draw(sel, xs, ys, teams, o, kind, {\n league: o.league,\n kind,\n variant: o.variant ?? \"default\",\n ...(o.idSystem === undefined ? {} : { idSystem: o.idSystem }),\n ...(o.season === undefined ? {} : { season: o.season }),\n });\n/**\n * Append `<g class=\"sdv-logos\">` of team logos at (x, y) through the caller's scale functions. Needs\n * `loadLeague(league)` first.\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { appendLogos } from \"@sportsdataverse/sdvplot/d3\";\n * import * as d3 from \"d3\";\n *\n * await loadLeague(\"nfl\");\n * const x = d3.scaleLinear([250, 550], [0, 480]);\n * const y = d3.scaleLinear([280, 420], [240, 0]);\n * const svg = d3.select(document.createElement(\"div\")).append(\"svg\").attr(\"viewBox\", [0, 0, 480, 240]);\n * appendLogos(svg, [385, 525, 425], [326, 368, 311], [\"KC\", \"BUF\", \"DEN\"], {\n * league: \"nfl\",\n * x: (v) => x(Number(v)),\n * y: (v) => y(Number(v)),\n * frameHeight: 240,\n * height: 0.2,\n * });\n * svg.node();\n * ```\n */\nexport const appendLogos: AppendTeamMarks = teamMark(\"logo\");\n/** As `appendLogos`, with wordmarks. */\nexport const appendWordmarks: AppendTeamMarks = teamMark(\"wordmark\");\n/**\n * Append `<g class=\"sdv-headshots\">` of ESPN player headshots; `idSystem: \"gsis\"` needs `loadGsis()` first.\n * `clip: \"circle\"` makes circular faces (`height` is then the circle's diameter), with an optional `ring` and `placeholder`.\n *\n * @example\n * ```ts\n * import { appendHeadshots } from \"@sportsdataverse/sdvplot/d3\";\n * import * as d3 from \"d3\";\n *\n * const x = d3.scaleLinear([0, 10], [0, 480]);\n * const y = d3.scaleLinear([0, 10], [240, 0]);\n * const svg = d3.select(document.createElement(\"div\")).append(\"svg\").attr(\"viewBox\", [0, 0, 480, 240]);\n * const team: Record<string, string> = { \"3139477\": \"#e31837\", \"4374302\": \"#0076b6\", \"4429160\": \"#008e97\" };\n * appendHeadshots(svg, [2, 5, 8], [4, 6, 3], [\"3139477\", \"4374302\", \"4429160\"], {\n * league: \"nfl\",\n * x: (v) => x(Number(v)),\n * y: (v) => y(Number(v)),\n * frameHeight: 240,\n * height: 0.25,\n * clip: \"circle\",\n * ring: (id) => team[id] ?? \"grey\",\n * placeholder: (id) => team[id] ?? \"grey\",\n * });\n * svg.node();\n * ```\n */\nexport function appendHeadshots<G extends SVGElement, D>(\n sel: Sel<G, D>,\n xs: ArrayLike<Value>,\n ys: ArrayLike<Value>,\n players: readonly Value[],\n o: D3HeadshotOptions,\n): Sel<SVGGElement, D> {\n const g = draw(sel, xs, ys, players, o, \"headshot\", {\n league: o.league,\n kind: \"headshot\",\n idSystem: o.idSystem ?? \"espn\",\n });\n clipFaces(g, o);\n return g;\n}\n\n/**\n * A d3 ordinal scale from team (id or abbr) to team colour; anything unresolved maps to `naValue` (default \"grey\").\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { teamColorScale } from \"@sportsdataverse/sdvplot/d3\";\n *\n * await loadLeague(\"nba\");\n * const color = teamColorScale(\"nba\", { values: [\"LAL\", \"BOS\"] });\n * [\"LAL\", \"BOS\", \"not a team\"].map(color);\n * ```\n */\nexport function teamColorScale(\n league: League,\n o: {\n which?: Which;\n season?: SeasonInput;\n idSystem?: IdSystem;\n naValue?: string;\n values?: readonly Value[];\n } = {},\n): ScaleOrdinal<string, string, string> {\n const na = o.naValue ?? \"grey\";\n const { domain, colors } = teamColorDomain(league, o);\n return scaleOrdinal<string, string>()\n .domain(domain)\n .range(colors.map((c) => c ?? na))\n .unknown(na);\n}\n\nexport interface D3SurfaceOptions extends SurfaceSceneOptions {\n x: (v: number) => number;\n y: (v: number) => number;\n /** Describes the surface for assistive technology; default sporty's (\"nba basketball surface\" and so on). */\n ariaDescription?: string;\n}\n/** Paint a surface key's surface (in `team`'s colours) into `sel` through the caller's x/y scales. Throws InputError for a key with no surface (see `SURFACES`). */\nexport function appendSurface<G extends SVGGElement | SVGSVGElement, D>(\n sel: Sel<G, D>,\n league: League | (string & {}),\n o: D3SurfaceOptions,\n): Sel<SVGGElement, D> {\n const { x, y, ariaDescription, ...scene } = o;\n return sportyAppendSurface(\n sel,\n surfaceScene(league, scene),\n x,\n y,\n ariaDescription === undefined ? {} : { ariaDescription },\n );\n}\nexport { appendLegend, appendSignature } from \"./shots.js\";\nexport type { AppendLegendOptions, AppendSignatureOptions } from \"./shots.js\";\nexport type { FaceOptions } from \"./faces.js\";\n"]}
|