@sportsdataverse/sdvplot 0.1.0 → 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/NOTICE.md +15 -0
- package/README.md +84 -8
- package/dist/chartjs-surface.d.ts +2 -2
- package/dist/chartjs-surface.js +2 -2
- package/dist/chartjs-surface.js.map +1 -1
- package/dist/chartjs.d.ts +32 -2
- package/dist/chartjs.js +7 -4
- package/dist/chartjs.js.map +1 -1
- package/dist/chunk-57WFSI4Q.js +128 -0
- package/dist/chunk-57WFSI4Q.js.map +1 -0
- package/dist/{chunk-KKH6ABMA.js → chunk-7ULOCCFL.js} +94 -4
- 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-NLPH2VZT.js → chunk-DYMUVE6K.js} +4 -4
- package/dist/{chunk-NLPH2VZT.js.map → chunk-DYMUVE6K.js.map} +1 -1
- package/dist/{chunk-QY65DUEF.js → chunk-H2C3FMMA.js} +4 -10
- package/dist/chunk-H2C3FMMA.js.map +1 -0
- package/dist/{chunk-JJFTD4VB.js → chunk-J4P4HDBF.js} +4 -4
- package/dist/{chunk-JJFTD4VB.js.map → chunk-J4P4HDBF.js.map} +1 -1
- package/dist/chunk-M3XD2OSN.js +132 -0
- package/dist/chunk-M3XD2OSN.js.map +1 -0
- package/dist/{chunk-OKEE4AH6.js → chunk-OIILARXI.js} +3 -3
- package/dist/{chunk-OKEE4AH6.js.map → chunk-OIILARXI.js.map} +1 -1
- 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-TAXO3VXH.js → chunk-ZAEQXFJM.js} +3 -3
- package/dist/{chunk-TAXO3VXH.js.map → chunk-ZAEQXFJM.js.map} +1 -1
- 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 +3 -3
- package/dist/d3.js +6 -6
- package/dist/d3.js.map +1 -1
- package/dist/deckgl.d.ts +232 -0
- package/dist/deckgl.js +197 -0
- package/dist/deckgl.js.map +1 -0
- package/dist/echarts.js +5 -5
- package/dist/g2.d.ts +251 -0
- package/dist/g2.js +102 -0
- package/dist/g2.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -5
- 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/mui.d.ts +338 -0
- package/dist/mui.js +184 -0
- package/dist/mui.js.map +1 -0
- package/dist/nivo.d.ts +193 -0
- package/dist/nivo.js +8 -0
- package/dist/nivo.js.map +1 -0
- package/dist/perspective.d.ts +198 -0
- package/dist/perspective.js +171 -0
- package/dist/perspective.js.map +1 -0
- package/dist/placement-UWkqUzpa.d.ts +32 -0
- package/dist/plot.d.ts +7 -5
- package/dist/plot.js +9 -8
- package/dist/plot.js.map +1 -1
- package/dist/plotly.js +5 -5
- package/dist/react.d.ts +279 -3
- package/dist/react.js +364 -6
- package/dist/react.js.map +1 -1
- package/dist/recharts.d.ts +162 -0
- package/dist/recharts.js +8 -0
- package/dist/recharts.js.map +1 -0
- package/dist/shadcn.d.ts +83 -0
- package/dist/shadcn.js +88 -0
- package/dist/shadcn.js.map +1 -0
- package/dist/shots.js +3 -2
- package/dist/shots.js.map +1 -1
- 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.js +4 -5
- package/dist/testing.js.map +1 -1
- package/dist/three.d.ts +223 -0
- package/dist/three.js +50 -0
- package/dist/three.js.map +1 -0
- package/dist/{tiers-B6pUBPcE.d.ts → tiers-BN4md-GB.d.ts} +2 -30
- 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/vega.js +5 -5
- package/dist/victory.d.ts +107 -0
- package/dist/victory.js +38 -0
- package/dist/victory.js.map +1 -0
- package/package.json +218 -6
- package/dist/chunk-KKH6ABMA.js.map +0 -1
- package/dist/chunk-QY65DUEF.js.map +0 -1
- package/dist/chunk-RMT2GMFH.js +0 -97
- package/dist/chunk-RMT2GMFH.js.map +0 -1
- package/dist/chunk-SEBO35DV.js +0 -76
- package/dist/chunk-SEBO35DV.js.map +0 -1
- package/dist/chunk-TDNBAEQB.js +0 -105
- package/dist/chunk-TDNBAEQB.js.map +0 -1
- package/dist/surfaces-ChaD9nei.d.ts +0 -38
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { E as EspnHeadshotLeague } from './headshots-B2J_LOO8.js';
|
|
3
|
+
import { W as Which, S as SeasonInput, I as IdSystem, H as HeadshotIdSystem, L as League, V as Variant, M as MarkType } from './types-C7n6gJF3.js';
|
|
4
|
+
export { R as Row, e as embedSources } from './_web-D7Aa5Rd8.js';
|
|
5
|
+
import './resolve-CQW-jzvr.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The Recharts adapter: team logos and player headshots as Recharts render props (a series' `dot` or `shape`, an
|
|
9
|
+
* axis' `tick`), and team colours for the series' `stroke` / `fill`. Pure: recharts is never imported (the prop types
|
|
10
|
+
* here are structural, read from recharts 3.8 and later); React's `createElement` builds the SVG `<image>` each prop
|
|
11
|
+
* returns. Call after `loadLeague(league)` / `preloadAll()`. The court / field / rink backdrop is a component,
|
|
12
|
+
* `<SurfaceBackdrop>` in `sdvplot/react`.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** What Recharts hands a series' `dot` (Line, Area, Radar) or `shape` (Scatter) render prop: the subset read here. */
|
|
16
|
+
interface RechartsDotProps {
|
|
17
|
+
/** The point's centre in px. */
|
|
18
|
+
cx?: number | string | undefined;
|
|
19
|
+
cy?: number | string | undefined;
|
|
20
|
+
/** The point's index in its series. */
|
|
21
|
+
index?: number | undefined;
|
|
22
|
+
/** The series' points (a Line's or Area's `dot`), so `last` can find the final one. */
|
|
23
|
+
points?: readonly unknown[] | undefined;
|
|
24
|
+
/** The data row the point was drawn from. */
|
|
25
|
+
payload?: any;
|
|
26
|
+
}
|
|
27
|
+
/** What Recharts hands an axis' `tick` render prop: the subset read here. */
|
|
28
|
+
interface RechartsTickProps {
|
|
29
|
+
/** The tick label's anchor in px. */
|
|
30
|
+
x?: number | string | undefined;
|
|
31
|
+
y?: number | string | undefined;
|
|
32
|
+
/** The tick: `value` is the category the axis shows (a team). */
|
|
33
|
+
payload?: {
|
|
34
|
+
value?: any;
|
|
35
|
+
} | undefined;
|
|
36
|
+
/** Which side of the plot the axis is on. Default "bottom". */
|
|
37
|
+
orientation?: string | undefined;
|
|
38
|
+
/** The axis' tick text colour, kept for a category drawn as text. */
|
|
39
|
+
fill?: string | undefined;
|
|
40
|
+
}
|
|
41
|
+
/** A render prop for a series' `dot` or `shape`: an `<image>`, or nothing where no image can be drawn. */
|
|
42
|
+
type DotRenderer = (props: RechartsDotProps) => ReactElement | null;
|
|
43
|
+
/** A render prop for an axis' `tick`: an `<image>`, or the category's own text where no image can be drawn. */
|
|
44
|
+
type TickRenderer = (props: RechartsTickProps) => ReactElement;
|
|
45
|
+
interface Shared {
|
|
46
|
+
/** Image height in px, 1 or more (Recharts hands a render prop no plot height to take a fraction of). Default 24. */
|
|
47
|
+
size?: number;
|
|
48
|
+
/** Opacity in [0, 1]. Default 1. */
|
|
49
|
+
alpha?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Draw only at the series' last point: an end-dot on a trend line. Default false. It needs the series' `points`,
|
|
52
|
+
* which Recharts hands a Line's or Area's `dot` but not a Scatter's `shape`: there every point is drawn, with one
|
|
53
|
+
* warning.
|
|
54
|
+
*/
|
|
55
|
+
last?: boolean;
|
|
56
|
+
/** url → data URI map from `embedSources`, to inline the images. */
|
|
57
|
+
embed?: ReadonlyMap<string, string>;
|
|
58
|
+
}
|
|
59
|
+
/** `logoDot` options. */
|
|
60
|
+
interface RechartsMarkOptions extends Shared {
|
|
61
|
+
/** Name of the data row column holding the team (abbreviation, name or id; see `idSystem`). */
|
|
62
|
+
team: string;
|
|
63
|
+
/** The league the teams belong to, e.g. "nba". */
|
|
64
|
+
league: League;
|
|
65
|
+
/** A season (number or string) for every row, or the name of a row column holding each row's season. */
|
|
66
|
+
season?: SeasonInput;
|
|
67
|
+
/** Logo variant, e.g. "dark". Default "default". */
|
|
68
|
+
variant?: Variant;
|
|
69
|
+
/** How the `team` values are read. Default "auto". */
|
|
70
|
+
idSystem?: IdSystem;
|
|
71
|
+
}
|
|
72
|
+
/** `headshotDot` options. */
|
|
73
|
+
interface RechartsHeadshotOptions extends Shared {
|
|
74
|
+
/** Name of the data row column holding the player id. */
|
|
75
|
+
player: string;
|
|
76
|
+
/** The league the players belong to, e.g. "nfl". */
|
|
77
|
+
league: EspnHeadshotLeague;
|
|
78
|
+
/** ESPN athlete ids, or nflverse gsis ids (needs `loadGsis()` first). Default "espn". */
|
|
79
|
+
idSystem?: HeadshotIdSystem;
|
|
80
|
+
}
|
|
81
|
+
/** `logoTick` options: the axis' categories are the teams. */
|
|
82
|
+
interface RechartsTickOptions {
|
|
83
|
+
/** The league the teams belong to, e.g. "nba". */
|
|
84
|
+
league: League;
|
|
85
|
+
/** The season the axis' teams are resolved for. */
|
|
86
|
+
season?: SeasonInput;
|
|
87
|
+
/** Image height in px, 1 or more. Default 24. */
|
|
88
|
+
size?: number;
|
|
89
|
+
/** Logo variant, e.g. "dark". Default "default". */
|
|
90
|
+
variant?: Variant;
|
|
91
|
+
/** "logo" (default) or "wordmark". */
|
|
92
|
+
markType?: MarkType;
|
|
93
|
+
/** How the axis categories are read. Default "auto". */
|
|
94
|
+
idSystem?: IdSystem;
|
|
95
|
+
/** url → data URI map from `embedSources`, to inline the images. */
|
|
96
|
+
embed?: ReadonlyMap<string, string>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Team logos as a series' points: the `dot` of a `<Line>` / `<Area>`, or the `shape` of a `<Scatter>`. Each point
|
|
100
|
+
* draws the logo of its data row's `team` column, `size` px tall and centred on the point; `last: true` draws only
|
|
101
|
+
* the series' final point (an end-dot). A team with no logo draws nothing and warns once, as `sdvplot/echarts` does.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```tsx
|
|
105
|
+
* import { loadLeague } from "@sportsdataverse/sdvplot";
|
|
106
|
+
* import { logoDot } from "@sportsdataverse/sdvplot/recharts";
|
|
107
|
+
* import { Scatter, ScatterChart, XAxis, YAxis } from "recharts";
|
|
108
|
+
*
|
|
109
|
+
* await loadLeague("nfl");
|
|
110
|
+
* const rows = [
|
|
111
|
+
* { team: "KC", pf: 385, pa: 326 },
|
|
112
|
+
* { team: "LAC", pf: 402, pa: 301 },
|
|
113
|
+
* ];
|
|
114
|
+
* <ScatterChart width={400} height={300}>
|
|
115
|
+
* <XAxis dataKey="pf" type="number" domain={[350, 450]} />
|
|
116
|
+
* <YAxis dataKey="pa" type="number" domain={[280, 350]} />
|
|
117
|
+
* <Scatter data={rows} shape={logoDot({ team: "team", league: "nfl", size: 32 })} />
|
|
118
|
+
* </ScatterChart>;
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
declare function logoDot(o: RechartsMarkOptions): DotRenderer;
|
|
122
|
+
/** ESPN player headshots as a series' points (`dot` or `shape`), from the data row's `player` column; an id with no
|
|
123
|
+
* headshot draws nothing and warns once. `idSystem: "gsis"` needs `loadGsis()` first. */
|
|
124
|
+
declare function headshotDot(o: RechartsHeadshotOptions): DotRenderer;
|
|
125
|
+
/**
|
|
126
|
+
* Team logos as an axis' tick labels: the `tick` of a category `<XAxis>` / `<YAxis>` whose values are teams. The
|
|
127
|
+
* logo sits where the label would (under a bottom axis, left of a left one). A category that resolves to no logo keeps
|
|
128
|
+
* its text label, as `sdvplot/echarts`' `withAxisLogos` does.
|
|
129
|
+
*/
|
|
130
|
+
declare function logoTick(o: RechartsTickOptions): TickRenderer;
|
|
131
|
+
/** `teamColorPalette` options. */
|
|
132
|
+
interface RechartsColorOptions {
|
|
133
|
+
which?: Which;
|
|
134
|
+
season?: SeasonInput;
|
|
135
|
+
idSystem?: IdSystem;
|
|
136
|
+
/** The colour of an unresolved team. Default "#808080". */
|
|
137
|
+
fallback?: string;
|
|
138
|
+
/**
|
|
139
|
+
* The chart's background. Given, a team colour under 3:1 contrast against it is swapped for the team's other
|
|
140
|
+
* colour, else moved towards white (on a dark background) or black (a light one) until it reads: dark mode. A
|
|
141
|
+
* colour that would then sit too close to an earlier team's (CIE76 ΔE under 20) takes the team's other colour, or
|
|
142
|
+
* the nearest colour to its own (lightness, hue or chroma stepped) that is distinct and still reads; failing that it
|
|
143
|
+
* is kept, with one warning.
|
|
144
|
+
*/
|
|
145
|
+
background?: string;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* One colour per team, in order, for each series' `stroke` / `fill`. With `background`, every colour keeps 3:1
|
|
149
|
+
* contrast against it (dark mode) and stays distinct from the others.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```tsx
|
|
153
|
+
* import { loadLeague } from "@sportsdataverse/sdvplot";
|
|
154
|
+
* import { teamColorPalette } from "@sportsdataverse/sdvplot/recharts";
|
|
155
|
+
*
|
|
156
|
+
* await loadLeague("nba");
|
|
157
|
+
* teamColorPalette("nba", ["Clippers", "Suns", "Lakers"], { background: "#181a1b" });
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
declare function teamColorPalette(league: League, teams: readonly unknown[], o?: RechartsColorOptions): string[];
|
|
161
|
+
|
|
162
|
+
export { type DotRenderer, type RechartsColorOptions, type RechartsDotProps, type RechartsHeadshotOptions, type RechartsMarkOptions, type RechartsTickOptions, type RechartsTickProps, type TickRenderer, headshotDot, logoDot, logoTick, teamColorPalette };
|
package/dist/recharts.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { headshotDot, logoDot, logoTick, teamColorPalette } from './chunk-M3XD2OSN.js';
|
|
2
|
+
export { embedSources } from './chunk-UL5M6KDE.js';
|
|
3
|
+
import './chunk-7ULOCCFL.js';
|
|
4
|
+
import './chunk-ZAEQXFJM.js';
|
|
5
|
+
import './chunk-J6R2LEJ7.js';
|
|
6
|
+
import './chunk-E7G7VSLV.js';
|
|
7
|
+
//# sourceMappingURL=recharts.js.map
|
|
8
|
+
//# sourceMappingURL=recharts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"recharts.js"}
|
package/dist/shadcn.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
import { W as Which, S as SeasonInput, I as IdSystem, L as League } from './types-C7n6gJF3.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The shadcn/ui charts adapter. shadcn's charts are Recharts charts inside its copy-paste `chart.tsx`
|
|
6
|
+
* (`<ChartContainer config>`, `<ChartLegendContent>`, `<ChartTooltipContent>`), configured by a `ChartConfig`:
|
|
7
|
+
* `teamChartConfig` builds that config from teams, with CSS-safe keys, team colours for its light and dark themes,
|
|
8
|
+
* team names as labels and team logos as the legend's and tooltip's icons. Draw the series with `sdvplot/recharts`
|
|
9
|
+
* (`logoTick`, `logoDot`) as for any Recharts chart. Pure: shadcn is not a package (you copy `chart.tsx` into your
|
|
10
|
+
* app) and recharts is never imported, so the config's types are structural (read from shadcn's new-york-v4
|
|
11
|
+
* `chart.tsx`); React's `createElement` builds the icons. Call after `loadLeague(league)` / `preloadAll()`.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* One team's entry in the config: shadcn's `ChartConfig` item with a `theme` (never a single `color`), so
|
|
16
|
+
* `<ChartStyle>` writes the light colour under the chart and the dark one under `.dark`.
|
|
17
|
+
*/
|
|
18
|
+
interface TeamChartConfigItem {
|
|
19
|
+
/** The team's name, e.g. "Kansas City Chiefs" (its own value when it does not resolve). */
|
|
20
|
+
label: string;
|
|
21
|
+
/** The team's colour on a light chart and on a dark one, each 3:1 or more against that theme's background. */
|
|
22
|
+
theme: {
|
|
23
|
+
light: string;
|
|
24
|
+
dark: string;
|
|
25
|
+
};
|
|
26
|
+
/** The team's logo, at legend and tooltip size; absent when the team has no logo (shadcn draws its swatch). */
|
|
27
|
+
icon?: ComponentType;
|
|
28
|
+
}
|
|
29
|
+
/** What `teamChartConfig` returns. */
|
|
30
|
+
interface TeamChartConfig {
|
|
31
|
+
/** shadcn's `ChartConfig`, keyed by `keys`: pass it to `<ChartContainer config>`. */
|
|
32
|
+
config: Record<string, TeamChartConfigItem>;
|
|
33
|
+
/**
|
|
34
|
+
* `keys[i]` is `teams[i]`'s key in `config`: name a series' `dataKey` with it, colour it `var(--color-<key>)`, and
|
|
35
|
+
* put it in a row column for `<ChartTooltipContent nameKey>`. A clash is numbered in the order of `teams` (`lal`,
|
|
36
|
+
* `lal-2`), so read the key here rather than recomputing it, and pass each team once.
|
|
37
|
+
*/
|
|
38
|
+
keys: string[];
|
|
39
|
+
}
|
|
40
|
+
/** `teamChartConfig` options. */
|
|
41
|
+
interface TeamChartConfigOptions {
|
|
42
|
+
/** The team colour taken first: "primary" (default) or "secondary". */
|
|
43
|
+
which?: Which;
|
|
44
|
+
/** The season the teams (their names, colours and logos) are resolved for. */
|
|
45
|
+
season?: SeasonInput;
|
|
46
|
+
/** How the `teams` values are read. Default "auto". */
|
|
47
|
+
idSystem?: IdSystem;
|
|
48
|
+
/**
|
|
49
|
+
* The chart backgrounds the colours must read on (3:1 contrast), as hex. Default shadcn's neutral theme: `light`
|
|
50
|
+
* #ffffff (its page and card), `dark` #171717 (its dark card, `oklch(0.205 0 0)`; a colour that reads on it reads
|
|
51
|
+
* on the darker dark page, #0a0a0a, too).
|
|
52
|
+
*/
|
|
53
|
+
background?: {
|
|
54
|
+
light?: string;
|
|
55
|
+
dark?: string;
|
|
56
|
+
};
|
|
57
|
+
/** `false`: no logos, so shadcn draws its colour swatch for every team. Default true. */
|
|
58
|
+
icon?: boolean;
|
|
59
|
+
/** The logo's height in px, 1 or more, rounded to a whole px. Default 16. */
|
|
60
|
+
iconSize?: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* shadcn's `ChartConfig` for teams: one entry per team, keyed for CSS (team names like "Trail Blazers" are not valid
|
|
64
|
+
* custom property names), with the team's name as `label`, its colour on shadcn's light and dark backgrounds as
|
|
65
|
+
* `theme` (3:1 contrast on each, and distinct from the other teams'), and its logo as `icon`, which
|
|
66
|
+
* `<ChartLegendContent>` and `<ChartTooltipContent>` draw in place of their colour swatch. A team with no logo has
|
|
67
|
+
* no `icon` (shadcn falls back to the swatch) and warns once. The icons are the same components for the same logo
|
|
68
|
+
* on every call, so building the config inside a component's render is safe.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* import { loadLeague } from "@sportsdataverse/sdvplot";
|
|
73
|
+
* import { teamChartConfig } from "@sportsdataverse/sdvplot/shadcn";
|
|
74
|
+
*
|
|
75
|
+
* await loadLeague("nba");
|
|
76
|
+
* // "Trail Blazers" is not a CSS name: its key is "trail-blazers", a series' dataKey and var(--color-trail-blazers)
|
|
77
|
+
* const { config, keys } = teamChartConfig("nba", ["Trail Blazers", "Lakers"]);
|
|
78
|
+
* [keys, config["trail-blazers"]?.label, config["trail-blazers"]?.theme];
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare function teamChartConfig(league: League, teams: readonly unknown[], o?: TeamChartConfigOptions): TeamChartConfig;
|
|
82
|
+
|
|
83
|
+
export { type TeamChartConfig, type TeamChartConfigItem, type TeamChartConfigOptions, teamChartConfig };
|
package/dist/shadcn.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { teamColorPalette } from './chunk-M3XD2OSN.js';
|
|
2
|
+
import './chunk-UL5M6KDE.js';
|
|
3
|
+
import { placeSync } from './chunk-7ULOCCFL.js';
|
|
4
|
+
import { resolveSync, getLeagueSync } from './chunk-ZAEQXFJM.js';
|
|
5
|
+
import './chunk-J6R2LEJ7.js';
|
|
6
|
+
import { InputError, warn } from './chunk-E7G7VSLV.js';
|
|
7
|
+
import { createElement } from 'react';
|
|
8
|
+
|
|
9
|
+
var LIGHT = "#ffffff";
|
|
10
|
+
var DARK = "#171717";
|
|
11
|
+
function chartKeys(values) {
|
|
12
|
+
const used = /* @__PURE__ */ new Set();
|
|
13
|
+
return values.map((v) => {
|
|
14
|
+
const base = String(v).normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "-").replace(/^-+|-+$/g, "") || "team";
|
|
15
|
+
let key = base;
|
|
16
|
+
for (let n = 2; used.has(key); n++) key = `${base}-${n}`;
|
|
17
|
+
used.add(key);
|
|
18
|
+
return key;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
var icons = /* @__PURE__ */ new Map();
|
|
22
|
+
function logoIcon(src, id, h, aspect) {
|
|
23
|
+
const k = `${h}|${src}`;
|
|
24
|
+
let icon = icons.get(k);
|
|
25
|
+
if (icon === void 0) {
|
|
26
|
+
const img = createElement("img", {
|
|
27
|
+
src,
|
|
28
|
+
alt: "",
|
|
29
|
+
width: Math.round(h * aspect),
|
|
30
|
+
height: h,
|
|
31
|
+
style: { height: h, width: "auto", flexShrink: 0 },
|
|
32
|
+
"data-sdv-id": id,
|
|
33
|
+
"data-sdv-kind": "logo"
|
|
34
|
+
});
|
|
35
|
+
const Logo = () => img;
|
|
36
|
+
Logo.displayName = "TeamLogoIcon";
|
|
37
|
+
icon = Logo;
|
|
38
|
+
icons.set(k, icon);
|
|
39
|
+
}
|
|
40
|
+
return icon;
|
|
41
|
+
}
|
|
42
|
+
function teamChartConfig(league, teams, o = {}) {
|
|
43
|
+
const size = o.iconSize ?? 16;
|
|
44
|
+
if (!(typeof size === "number" && Number.isFinite(size) && size >= 1))
|
|
45
|
+
throw new InputError(`iconSize is a logo height in px, 1 or more, got ${String(size)}`);
|
|
46
|
+
const h = Math.round(size);
|
|
47
|
+
const ro = {
|
|
48
|
+
...o.season === void 0 ? {} : { season: o.season },
|
|
49
|
+
...o.idSystem === void 0 ? {} : { idSystem: o.idSystem }
|
|
50
|
+
};
|
|
51
|
+
const co = { ...ro, ...o.which === void 0 ? {} : { which: o.which } };
|
|
52
|
+
const light = teamColorPalette(league, teams, { ...co, background: o.background?.light ?? LIGHT });
|
|
53
|
+
const dark = teamColorPalette(league, teams, { ...co, background: o.background?.dark ?? DARK });
|
|
54
|
+
const ids = resolveSync(teams, league, ro);
|
|
55
|
+
const names = new Map(getLeagueSync(league).teams.map((t) => [t.team_id, t.name ?? t.abbr]));
|
|
56
|
+
const logos = new Map(
|
|
57
|
+
o.icon === false ? [] : placeSync(
|
|
58
|
+
teams.map(() => 0),
|
|
59
|
+
teams.map(() => 0),
|
|
60
|
+
teams,
|
|
61
|
+
{ league, kind: "logo", ...ro, warn: false }
|
|
62
|
+
).map((p) => [p.index, p])
|
|
63
|
+
);
|
|
64
|
+
const keys = chartKeys(teams);
|
|
65
|
+
const noLogo = [];
|
|
66
|
+
const config = {};
|
|
67
|
+
keys.forEach((key, i) => {
|
|
68
|
+
const id = ids[i];
|
|
69
|
+
const item = {
|
|
70
|
+
label: (id === void 0 ? void 0 : names.get(id)) ?? String(teams[i]),
|
|
71
|
+
theme: { light: light[i], dark: dark[i] }
|
|
72
|
+
};
|
|
73
|
+
const p = logos.get(i);
|
|
74
|
+
if (p !== void 0) item.icon = logoIcon(p.url, p.id, h, p.aspect ?? 1);
|
|
75
|
+
else if (o.icon !== false && id !== void 0) noLogo.push(String(teams[i]));
|
|
76
|
+
config[key] = item;
|
|
77
|
+
});
|
|
78
|
+
if (noLogo.length > 0)
|
|
79
|
+
warn(
|
|
80
|
+
`shadcn:${league}:no-logo:${noLogo.join(",")}`,
|
|
81
|
+
`teamChartConfig: no logo for ${noLogo.join(", ")} (${league}); the legend and tooltip show its colour swatch`
|
|
82
|
+
);
|
|
83
|
+
return { config, keys };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { teamChartConfig };
|
|
87
|
+
//# sourceMappingURL=shadcn.js.map
|
|
88
|
+
//# sourceMappingURL=shadcn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shadcn.ts"],"names":[],"mappings":"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","file":"shadcn.js","sourcesContent":["/**\n * The shadcn/ui charts adapter. shadcn's charts are Recharts charts inside its copy-paste `chart.tsx`\n * (`<ChartContainer config>`, `<ChartLegendContent>`, `<ChartTooltipContent>`), configured by a `ChartConfig`:\n * `teamChartConfig` builds that config from teams, with CSS-safe keys, team colours for its light and dark themes,\n * team names as labels and team logos as the legend's and tooltip's icons. Draw the series with `sdvplot/recharts`\n * (`logoTick`, `logoDot`) as for any Recharts chart. Pure: shadcn is not a package (you copy `chart.tsx` into your\n * app) and recharts is never imported, so the config's types are structural (read from shadcn's new-york-v4\n * `chart.tsx`); React's `createElement` builds the icons. Call after `loadLeague(league)` / `preloadAll()`.\n */\nimport { type ComponentType, type ReactElement, createElement } from \"react\";\nimport { place } from \"./_web.js\";\nimport { InputError, warn } from \"./errors.js\";\nimport { getLeagueSync } from \"./index-data.js\";\nimport { teamColorPalette } from \"./recharts.js\";\nimport { type Value, resolveSync } from \"./resolve.js\";\nimport type { IdSystem, League, SeasonInput, Which } from \"./types.js\";\n\n/**\n * One team's entry in the config: shadcn's `ChartConfig` item with a `theme` (never a single `color`), so\n * `<ChartStyle>` writes the light colour under the chart and the dark one under `.dark`.\n */\nexport interface TeamChartConfigItem {\n /** The team's name, e.g. \"Kansas City Chiefs\" (its own value when it does not resolve). */\n label: string;\n /** The team's colour on a light chart and on a dark one, each 3:1 or more against that theme's background. */\n theme: { light: string; dark: string };\n /** The team's logo, at legend and tooltip size; absent when the team has no logo (shadcn draws its swatch). */\n icon?: ComponentType;\n}\n/** What `teamChartConfig` returns. */\nexport interface TeamChartConfig {\n /** shadcn's `ChartConfig`, keyed by `keys`: pass it to `<ChartContainer config>`. */\n config: Record<string, TeamChartConfigItem>;\n /**\n * `keys[i]` is `teams[i]`'s key in `config`: name a series' `dataKey` with it, colour it `var(--color-<key>)`, and\n * put it in a row column for `<ChartTooltipContent nameKey>`. A clash is numbered in the order of `teams` (`lal`,\n * `lal-2`), so read the key here rather than recomputing it, and pass each team once.\n */\n keys: string[];\n}\n/** `teamChartConfig` options. */\nexport interface TeamChartConfigOptions {\n /** The team colour taken first: \"primary\" (default) or \"secondary\". */\n which?: Which;\n /** The season the teams (their names, colours and logos) are resolved for. */\n season?: SeasonInput;\n /** How the `teams` values are read. Default \"auto\". */\n idSystem?: IdSystem;\n /**\n * The chart backgrounds the colours must read on (3:1 contrast), as hex. Default shadcn's neutral theme: `light`\n * #ffffff (its page and card), `dark` #171717 (its dark card, `oklch(0.205 0 0)`; a colour that reads on it reads\n * on the darker dark page, #0a0a0a, too).\n */\n background?: { light?: string; dark?: string };\n /** `false`: no logos, so shadcn draws its colour swatch for every team. Default true. */\n icon?: boolean;\n /** The logo's height in px, 1 or more, rounded to a whole px. Default 16. */\n iconSize?: number;\n}\n\n/** shadcn's neutral theme (new-york-v4 `registry/themes.ts`): light background and card, and the dark card. */\nconst LIGHT = \"#ffffff\";\nconst DARK = \"#171717\";\n\n/**\n * A CSS-safe key for each value, in order: diacritics dropped, lower-cased, each run of anything but letters and\n * digits made one \"-\" (\"Trail Blazers\" -> \"trail-blazers\", \"Montréal\" -> \"montreal\"; other scripts' letters stay,\n * which a custom property name allows), \"team\" when nothing is left, and \"-2\", \"-3\", ... on a key already taken.\n */\nfunction chartKeys(values: readonly unknown[]): string[] {\n const used = new Set<string>();\n return values.map((v) => {\n const base =\n String(v)\n .normalize(\"NFKD\")\n .replace(/\\p{M}/gu, \"\")\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, \"-\")\n .replace(/^-+|-+$/g, \"\") || \"team\";\n let key = base;\n for (let n = 2; used.has(key); n++) key = `${base}-${n}`;\n used.add(key);\n return key;\n });\n}\n\n// ponytail: one icon component per (logo, size) for the process, so a config rebuilt on every render hands shadcn the\n// same component and React keeps the <img>; bounded by the logos a page draws times the whole-px sizes asked for.\nconst icons = new Map<string, ComponentType>();\nfunction logoIcon(src: string, id: string, h: number, aspect: number): ComponentType {\n const k = `${h}|${src}`;\n let icon = icons.get(k);\n if (icon === undefined) {\n // alt \"\": shadcn puts the team's label beside it in the legend and the tooltip\n const img = createElement(\"img\", {\n src,\n alt: \"\",\n width: Math.round(h * aspect),\n height: h,\n style: { height: h, width: \"auto\", flexShrink: 0 },\n \"data-sdv-id\": id,\n \"data-sdv-kind\": \"logo\",\n });\n const Logo = (): ReactElement => img;\n Logo.displayName = \"TeamLogoIcon\";\n icon = Logo;\n icons.set(k, icon);\n }\n return icon;\n}\n\n/**\n * shadcn's `ChartConfig` for teams: one entry per team, keyed for CSS (team names like \"Trail Blazers\" are not valid\n * custom property names), with the team's name as `label`, its colour on shadcn's light and dark backgrounds as\n * `theme` (3:1 contrast on each, and distinct from the other teams'), and its logo as `icon`, which\n * `<ChartLegendContent>` and `<ChartTooltipContent>` draw in place of their colour swatch. A team with no logo has\n * no `icon` (shadcn falls back to the swatch) and warns once. The icons are the same components for the same logo\n * on every call, so building the config inside a component's render is safe.\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { teamChartConfig } from \"@sportsdataverse/sdvplot/shadcn\";\n *\n * await loadLeague(\"nba\");\n * // \"Trail Blazers\" is not a CSS name: its key is \"trail-blazers\", a series' dataKey and var(--color-trail-blazers)\n * const { config, keys } = teamChartConfig(\"nba\", [\"Trail Blazers\", \"Lakers\"]);\n * [keys, config[\"trail-blazers\"]?.label, config[\"trail-blazers\"]?.theme];\n * ```\n */\nexport function teamChartConfig(\n league: League,\n teams: readonly unknown[],\n o: TeamChartConfigOptions = {},\n): TeamChartConfig {\n const size = o.iconSize ?? 16;\n if (!(typeof size === \"number\" && Number.isFinite(size) && size >= 1))\n throw new InputError(`iconSize is a logo height in px, 1 or more, got ${String(size)}`);\n const h = Math.round(size); // whole px: a fractional size would add an icon (and a cache entry) per value\n const ro = {\n ...(o.season === undefined ? {} : { season: o.season }),\n ...(o.idSystem === undefined ? {} : { idSystem: o.idSystem }),\n };\n const co = { ...ro, ...(o.which === undefined ? {} : { which: o.which }) };\n const light = teamColorPalette(league, teams, { ...co, background: o.background?.light ?? LIGHT });\n const dark = teamColorPalette(league, teams, { ...co, background: o.background?.dark ?? DARK });\n const ids = resolveSync(teams as readonly Value[], league, ro);\n const names = new Map(getLeagueSync(league).teams.map((t) => [t.team_id, t.name ?? t.abbr]));\n const logos = new Map(\n o.icon === false\n ? []\n : place(\n teams.map(() => 0),\n teams.map(() => 0),\n teams as readonly Value[],\n { league, kind: \"logo\", ...ro, warn: false },\n ).map((p) => [p.index, p]),\n );\n const keys = chartKeys(teams);\n const noLogo: string[] = [];\n const config: Record<string, TeamChartConfigItem> = {};\n keys.forEach((key, i) => {\n const id = ids[i];\n const item: TeamChartConfigItem = {\n label: (id === undefined ? undefined : names.get(id)) ?? String(teams[i]),\n theme: { light: light[i] as string, dark: dark[i] as string },\n };\n const p = logos.get(i);\n if (p !== undefined) item.icon = logoIcon(p.url, p.id, h, p.aspect ?? 1);\n else if (o.icon !== false && id !== undefined) noLogo.push(String(teams[i])); // unresolved: the resolver warned\n config[key] = item;\n });\n if (noLogo.length > 0)\n warn(\n `shadcn:${league}:no-logo:${noLogo.join(\",\")}`,\n `teamChartConfig: no logo for ${noLogo.join(\", \")} (${league}); the legend and tooltip show its colour swatch`,\n );\n return { config, keys };\n}\n"]}
|
package/dist/shots.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { made, split, binShots } from './chunk-
|
|
2
|
-
export { LEAGUE_PRIOR_ATTEMPTS, binShots, cellsVsLeague, diffScale, leagueIndex, shrunkDiff, signaturePoints, sizeCells, statsByZone } from './chunk-
|
|
1
|
+
import { made, split, binShots } from './chunk-H2C3FMMA.js';
|
|
2
|
+
export { LEAGUE_PRIOR_ATTEMPTS, binShots, cellsVsLeague, diffScale, leagueIndex, shrunkDiff, signaturePoints, sizeCells, statsByZone } from './chunk-H2C3FMMA.js';
|
|
3
|
+
import './chunk-WQT4IVLX.js';
|
|
3
4
|
export { binner, cellPath, cellPoints, hexagonPath, hexbin, squarePath, squarebin } from './chunk-C2CDY5OG.js';
|
|
4
5
|
import { InputError } from './chunk-E7G7VSLV.js';
|
|
5
6
|
|
package/dist/shots.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shots/distance.ts"],"names":[],"mappings":"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","file":"shots.js","sourcesContent":["// Distance and side bins, ported from blazing-the-nets `main` lib/data/aggregate.ts (@31427b8), plus `master`'s\n// distance baseline for cells (src/components/Hexagon/index.js @35dfda6). Pure; the parity gate is fixtures/shots/oracle.json.\nimport type { BinnerOptions } from \"../bins/index.js\";\nimport { InputError } from \"../errors.js\";\nimport { type CellVsLeague, type ShotRow, type Split, binShots, made, split } from \"./aggregate.js\";\n\n/** One `fgPctByDistance` bin: the counts of the shots `distance` to `distance + binFt` ft out, and their share. */\nexport interface DistanceBin extends Split {\n /** First foot of the bin; `shot_distance` is rounded, so a 1-ft bin is centred on it. */\n readonly distance: number;\n /** Share of the attempts within `maxFt`. */\n readonly share: number;\n}\n\n/** The number of `binFt` bins from 0 to `maxFt`; both must be finite numbers above 0 (else InputError, naming it). */\nfunction binCount(binFt: number, maxFt: number): number {\n for (const [name, v] of Object.entries({ binFt, maxFt }))\n if (!(Number.isFinite(v) && v > 0))\n throw new InputError(`${name} must be a finite number > 0, got ${String(v)}`);\n return Math.floor(maxFt / binFt) + 1;\n}\n\n/**\n * FG% by `shot_distance` in `binFt` bins from 0 to `maxFt`; longer attempts are left out (aggregate.ts:177-192).\n * The total within `maxFt` is the sum of `attempts`. `binFt` and `maxFt` must be finite numbers above 0 (else\n * InputError).\n *\n * @example\n * ```ts\n * import { fgPctByDistance } 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 * fgPctByDistance(shots, 3)[0];\n * ```\n */\nexport function fgPctByDistance(shots: readonly ShotRow[], binFt = 1, maxFt = 35): DistanceBin[] {\n const bins = Array.from({ length: binCount(binFt, maxFt) }, () => ({ attempts: 0, makes: 0 }));\n let total = 0;\n for (const s of shots) {\n if (s.shot_distance > maxFt) continue;\n const b = bins[Math.floor(s.shot_distance / binFt)];\n if (b === undefined) continue; // a negative distance\n b.attempts += 1;\n if (made(s)) b.makes += 1;\n total += 1;\n }\n return bins.map((b, i) => ({\n distance: i * binFt,\n ...split(b.attempts, b.makes),\n share: total ? b.attempts / total : 0,\n }));\n}\n\n/** A player distance bin with the league's FG% at the same distance and the difference (`vsLeague`). */\nexport interface DistanceVsLeague extends DistanceBin {\n readonly leagueFgPct: number | null;\n /** Player minus league FG% (a fraction), null when either side has no attempts. */\n readonly diff: number | null;\n}\n\n/**\n * Pair player and league distance bins (same `binFt` and `maxFt`, else InputError) and take the difference\n * (aggregate.ts:200-209).\n *\n * @example\n * ```ts\n * import { fgPctByDistance, vsLeague } from \"@sportsdataverse/sdvplot/shots\";\n *\n * // Brooklyn shots from the sportsdataverse-data nba_stats_shots release, 2025-26\n * const made = { x_legacy: 0, y_legacy: 0, shot_distance: 0, shot_value: 2, shot_result: \"Made\" };\n * const missed = { ...made, shot_result: \"Missed\" };\n * vsLeague(fgPctByDistance([made]), fgPctByDistance([made, missed]))[0];\n * ```\n */\nexport function vsLeague(player: readonly DistanceBin[], league: readonly DistanceBin[]): DistanceVsLeague[] {\n if (player.length !== league.length || player.some((b, i) => b.distance !== league[i]?.distance)) {\n throw new InputError(\"vsLeague: player and league bins differ (use the same binFt and maxFt)\");\n }\n return player.map((b, i) => {\n const leagueFgPct = league[i]?.fgPct ?? null;\n return {\n ...b,\n leagueFgPct,\n diff: b.fgPct !== null && leagueFgPct !== null ? b.fgPct - leagueFgPct : null,\n };\n });\n}\n\n/** One `statsBySide` bin: left, centre and right of the hoop for the shots in the bin that starts `distance` ft out. */\nexport interface SideBin {\n readonly distance: number;\n readonly left: Split;\n readonly centre: Split;\n readonly right: Split;\n}\n\n/**\n * Left / centre / right of the hoop by distance (aggregate.ts:218-240): left is `x < -centreHalfWidth`, right is\n * `x > centreHalfWidth`. The default 0 puts `x == 0` in centre (`main`); `false` drops it, as `master`'s\n * `binLeftRight` did (`src/utils/visuals/bin.ts:36-42`). `binFt` and `maxFt` as for `fgPctByDistance`; a\n * `centreHalfWidth` that is negative or not finite throws InputError.\n *\n * @example\n * ```ts\n * import { statsBySide } 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 * statsBySide(shots, 3, 35, 7.5)[0];\n * ```\n */\nexport function statsBySide(\n shots: readonly ShotRow[],\n binFt = 1,\n maxFt = 35,\n centreHalfWidth: number | false = 0,\n): SideBin[] {\n if (centreHalfWidth !== false && !(Number.isFinite(centreHalfWidth) && centreHalfWidth >= 0))\n throw new InputError(\n `statsBySide centreHalfWidth must be a finite number >= 0 or false, got ${String(centreHalfWidth)}`,\n );\n const half = centreHalfWidth === false ? 0 : centreHalfWidth;\n const acc = Array.from({ length: binCount(binFt, maxFt) }, () => ({\n left: [0, 0],\n centre: [0, 0],\n right: [0, 0],\n }));\n for (const s of shots) {\n if (s.shot_distance > maxFt) continue;\n const bin = acc[Math.floor(s.shot_distance / binFt)];\n if (bin === undefined) continue;\n const x = s.x_legacy;\n const side =\n x < -half ? bin.left : x > half ? bin.right : centreHalfWidth === false ? undefined : bin.centre;\n if (side === undefined) continue;\n side[0] = (side[0] ?? 0) + 1;\n if (made(s)) side[1] = (side[1] ?? 0) + 1;\n }\n return acc.map((b, i) => ({\n distance: i * binFt,\n left: split(b.left[0] ?? 0, b.left[1] ?? 0),\n centre: split(b.centre[0] ?? 0, b.centre[1] ?? 0),\n right: split(b.right[0] ?? 0, b.right[1] ?? 0),\n }));\n}\n\n/**\n * Player cells against the league FG% at the cell centre's distance, `floor(|centre| / 10)` ft, falling back one\n * foot when that bin is empty (blazing-the-nets `master` `src/components/Hexagon/index.js:44-49`). `cell` is a\n * hexagon radius (default 10, master's) or any `binner` lattice, e.g. `{ shape: \"square\", side: 10 }`. `byFoot` is\n * read as feet, so it must be 1-ft bins from 0 (`fgPctByDistance(league)`), else InputError.\n *\n * The distance is `Math.hypot(x, y)`, a deliberate divergence from master's `Math.sqrt(x ** 2 + y ** 2)`\n * (`src/lib/distance.js:3-4`). On a hexagon centre exactly a whole number of feet out, such as (15√3, 15) at 3 ft,\n * master's sqrt returns 29.999…96 and reads the foot below; hypot reads the true foot. On the real BKN fixture at\n * radius 10 that is 13 of 374 cells, among them the cells beside the rim (0.572 at 3 ft, where master reads 0.669\n * at 2 ft). Every other cell reads master's foot.\n *\n * @example\n * ```ts\n * import { cellsVsDistance, fgPctByDistance } from \"@sportsdataverse/sdvplot/shots\";\n *\n * // Brooklyn shots from the sportsdataverse-data nba_stats_shots release, 2025-26\n * const made = { x_legacy: 0, y_legacy: 0, shot_distance: 0, shot_value: 2, shot_result: \"Made\" };\n * const missed = { ...made, shot_result: \"Missed\" };\n * cellsVsDistance([made], fgPctByDistance([made, missed]), { shape: \"square\", side: 10 });\n * ```\n */\nexport function cellsVsDistance(\n player: readonly ShotRow[],\n byFoot: readonly DistanceBin[],\n cell: number | BinnerOptions = 10,\n): CellVsLeague[] {\n const bad = byFoot.findIndex((b, i) => b.distance !== i);\n if (bad >= 0)\n throw new InputError(\n `cellsVsDistance needs 1-ft distance bins from 0 (fgPctByDistance(shots, 1)); bin ${bad} is at ${String(byFoot[bad]?.distance)} ft`,\n );\n return binShots(player, cell).map((h) => {\n // hypot, not master's sqrt(x ** 2 + y ** 2) (distance.js:3-4), which reads a whole-foot centre one foot short\n const d = Math.floor(Math.hypot(h.x, h.y) / 10);\n return { ...h, leagueFgPct: byFoot[d]?.fgPct ?? byFoot[d - 1]?.fgPct ?? null };\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/shots/distance.ts"],"names":[],"mappings":"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","file":"shots.js","sourcesContent":["// Distance and side bins, ported from blazing-the-nets `main` lib/data/aggregate.ts (@31427b8), plus `master`'s\n// distance baseline for cells (src/components/Hexagon/index.js @35dfda6). Pure; the parity gate is fixtures/shots/oracle.json.\nimport type { BinnerOptions } from \"../bins/index.js\";\nimport { InputError } from \"../errors.js\";\nimport { type CellVsLeague, type ShotRow, type Split, binShots, made, split } from \"./aggregate.js\";\n\n/** One `fgPctByDistance` bin: the counts of the shots `distance` to `distance + binFt` ft out, and their share. */\nexport interface DistanceBin extends Split {\n /** First foot of the bin; `shot_distance` is rounded, so a 1-ft bin is centred on it. */\n readonly distance: number;\n /** Share of the attempts within `maxFt`. */\n readonly share: number;\n}\n\n/** The number of `binFt` bins from 0 to `maxFt`; both must be finite numbers above 0 (else InputError, naming it). */\nfunction binCount(binFt: number, maxFt: number): number {\n for (const [name, v] of Object.entries({ binFt, maxFt }))\n if (!(Number.isFinite(v) && v > 0))\n throw new InputError(`${name} must be a finite number > 0, got ${String(v)}`);\n return Math.floor(maxFt / binFt) + 1;\n}\n\n/**\n * FG% by `shot_distance` in `binFt` bins from 0 to `maxFt`; longer attempts are left out (aggregate.ts:177-192).\n * The total within `maxFt` is the sum of `attempts`. `binFt` and `maxFt` must be finite numbers above 0 (else\n * InputError).\n *\n * @example\n * ```ts\n * import { fgPctByDistance } 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 * fgPctByDistance(shots, 3)[0];\n * ```\n */\nexport function fgPctByDistance(shots: readonly ShotRow[], binFt = 1, maxFt = 35): DistanceBin[] {\n const bins = Array.from({ length: binCount(binFt, maxFt) }, () => ({ attempts: 0, makes: 0 }));\n let total = 0;\n for (const s of shots) {\n if (s.shot_distance > maxFt) continue;\n const b = bins[Math.floor(s.shot_distance / binFt)];\n if (b === undefined) continue; // a negative distance\n b.attempts += 1;\n if (made(s)) b.makes += 1;\n total += 1;\n }\n return bins.map((b, i) => ({\n distance: i * binFt,\n ...split(b.attempts, b.makes),\n share: total ? b.attempts / total : 0,\n }));\n}\n\n/** A player distance bin with the league's FG% at the same distance and the difference (`vsLeague`). */\nexport interface DistanceVsLeague extends DistanceBin {\n readonly leagueFgPct: number | null;\n /** Player minus league FG% (a fraction), null when either side has no attempts. */\n readonly diff: number | null;\n}\n\n/**\n * Pair player and league distance bins (same `binFt` and `maxFt`, else InputError) and take the difference\n * (aggregate.ts:200-209).\n *\n * @example\n * ```ts\n * import { fgPctByDistance, vsLeague } from \"@sportsdataverse/sdvplot/shots\";\n *\n * // Brooklyn shots from the sportsdataverse-data nba_stats_shots release, 2025-26\n * const made = { x_legacy: 0, y_legacy: 0, shot_distance: 0, shot_value: 2, shot_result: \"Made\" };\n * const missed = { ...made, shot_result: \"Missed\" };\n * vsLeague(fgPctByDistance([made]), fgPctByDistance([made, missed]))[0];\n * ```\n */\nexport function vsLeague(player: readonly DistanceBin[], league: readonly DistanceBin[]): DistanceVsLeague[] {\n if (player.length !== league.length || player.some((b, i) => b.distance !== league[i]?.distance)) {\n throw new InputError(\"vsLeague: player and league bins differ (use the same binFt and maxFt)\");\n }\n return player.map((b, i) => {\n const leagueFgPct = league[i]?.fgPct ?? null;\n return {\n ...b,\n leagueFgPct,\n diff: b.fgPct !== null && leagueFgPct !== null ? b.fgPct - leagueFgPct : null,\n };\n });\n}\n\n/** One `statsBySide` bin: left, centre and right of the hoop for the shots in the bin that starts `distance` ft out. */\nexport interface SideBin {\n readonly distance: number;\n readonly left: Split;\n readonly centre: Split;\n readonly right: Split;\n}\n\n/**\n * Left / centre / right of the hoop by distance (aggregate.ts:218-240): left is `x < -centreHalfWidth`, right is\n * `x > centreHalfWidth`. The default 0 puts `x == 0` in centre (`main`); `false` drops it, as `master`'s\n * `binLeftRight` did (`src/utils/visuals/bin.ts:36-42`). `binFt` and `maxFt` as for `fgPctByDistance`; a\n * `centreHalfWidth` that is negative or not finite throws InputError.\n *\n * @example\n * ```ts\n * import { statsBySide } 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 * statsBySide(shots, 3, 35, 7.5)[0];\n * ```\n */\nexport function statsBySide(\n shots: readonly ShotRow[],\n binFt = 1,\n maxFt = 35,\n centreHalfWidth: number | false = 0,\n): SideBin[] {\n if (centreHalfWidth !== false && !(Number.isFinite(centreHalfWidth) && centreHalfWidth >= 0))\n throw new InputError(\n `statsBySide centreHalfWidth must be a finite number >= 0 or false, got ${String(centreHalfWidth)}`,\n );\n const half = centreHalfWidth === false ? 0 : centreHalfWidth;\n const acc = Array.from({ length: binCount(binFt, maxFt) }, () => ({\n left: [0, 0],\n centre: [0, 0],\n right: [0, 0],\n }));\n for (const s of shots) {\n if (s.shot_distance > maxFt) continue;\n const bin = acc[Math.floor(s.shot_distance / binFt)];\n if (bin === undefined) continue;\n const x = s.x_legacy;\n const side =\n x < -half ? bin.left : x > half ? bin.right : centreHalfWidth === false ? undefined : bin.centre;\n if (side === undefined) continue;\n side[0] = (side[0] ?? 0) + 1;\n if (made(s)) side[1] = (side[1] ?? 0) + 1;\n }\n return acc.map((b, i) => ({\n distance: i * binFt,\n left: split(b.left[0] ?? 0, b.left[1] ?? 0),\n centre: split(b.centre[0] ?? 0, b.centre[1] ?? 0),\n right: split(b.right[0] ?? 0, b.right[1] ?? 0),\n }));\n}\n\n/**\n * Player cells against the league FG% at the cell centre's distance, `floor(|centre| / 10)` ft, falling back one\n * foot when that bin is empty (blazing-the-nets `master` `src/components/Hexagon/index.js:44-49`). `cell` is a\n * hexagon radius (default 10, master's) or any `binner` lattice, e.g. `{ shape: \"square\", side: 10 }`. `byFoot` is\n * read as feet, so it must be 1-ft bins from 0 (`fgPctByDistance(league)`), else InputError.\n *\n * The distance is `Math.hypot(x, y)`, a deliberate divergence from master's `Math.sqrt(x ** 2 + y ** 2)`\n * (`src/lib/distance.js:3-4`). On a hexagon centre exactly a whole number of feet out, such as (15√3, 15) at 3 ft,\n * master's sqrt returns 29.999…96 and reads the foot below; hypot reads the true foot. On the real BKN fixture at\n * radius 10 that is 13 of 374 cells, among them the cells beside the rim (0.572 at 3 ft, where master reads 0.669\n * at 2 ft). Every other cell reads master's foot.\n *\n * @example\n * ```ts\n * import { cellsVsDistance, fgPctByDistance } from \"@sportsdataverse/sdvplot/shots\";\n *\n * // Brooklyn shots from the sportsdataverse-data nba_stats_shots release, 2025-26\n * const made = { x_legacy: 0, y_legacy: 0, shot_distance: 0, shot_value: 2, shot_result: \"Made\" };\n * const missed = { ...made, shot_result: \"Missed\" };\n * cellsVsDistance([made], fgPctByDistance([made, missed]), { shape: \"square\", side: 10 });\n * ```\n */\nexport function cellsVsDistance(\n player: readonly ShotRow[],\n byFoot: readonly DistanceBin[],\n cell: number | BinnerOptions = 10,\n): CellVsLeague[] {\n const bad = byFoot.findIndex((b, i) => b.distance !== i);\n if (bad >= 0)\n throw new InputError(\n `cellsVsDistance needs 1-ft distance bins from 0 (fgPctByDistance(shots, 1)); bin ${bad} is at ${String(byFoot[bad]?.distance)} ft`,\n );\n return binShots(player, cell).map((h) => {\n // hypot, not master's sqrt(x ** 2 + y ** 2) (distance.js:3-4), which reads a whole-foot centre one foot short\n const d = Math.floor(Math.hypot(h.x, h.y) / 10);\n return { ...h, leagueFgPct: byFoot[d]?.fgPct ?? byFoot[d - 1]?.fgPct ?? null };\n });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"surface-TR7YFJW3.js"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { SURFACES, SURFACE_BASE, colorUpdates, surfaceScene } from './chunk-57WFSI4Q.js';
|
|
2
|
+
import './chunk-ZAEQXFJM.js';
|
|
3
|
+
import './chunk-J6R2LEJ7.js';
|
|
4
|
+
import './chunk-E7G7VSLV.js';
|
|
5
|
+
//# sourceMappingURL=surfaces-MIG7HOFU.js.map
|
|
6
|
+
//# sourceMappingURL=surfaces-MIG7HOFU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"surfaces-MIG7HOFU.js"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BaseballColorKey, BasketballColorKey, CurlingColorKey, FootballColorKey, HockeyColorKey, LacrosseColorKey, SoccerColorKey, SoftballColorKey, TennisColorKey, VolleyballColorKey, BaseballDisplayRange, BasketballDisplayRange, CurlingDisplayRange, FootballDisplayRange, HockeyDisplayRange, LacrosseDisplayRange, SoccerDisplayRange, SoftballDisplayRange, TennisDisplayRange, VolleyballDisplayRange, BaseballParamUpdates, BasketballParamUpdates, CurlingParamUpdates, FootballParamUpdates, HockeyParamUpdates, LacrosseParamUpdates, SoccerParamUpdates, SoftballParamUpdates, TennisParamUpdates, VolleyballParamUpdates, Sport } from '@sportsdataverse/sporty';
|
|
2
|
+
import { V as Value } from './resolve-CQW-jzvr.js';
|
|
3
|
+
import { S as SeasonInput } from './types-C7n6gJF3.js';
|
|
4
|
+
|
|
5
|
+
type SurfaceSport = Sport;
|
|
6
|
+
/**
|
|
7
|
+
* Maps an sdv surface key to `[sporty sport, sporty league key]`: the SDV surface-key table shared with sdvplot (Python
|
|
8
|
+
* `SURFACES`) and sdvplotR (`surface_map`). Keys that are also sdv team leagues (`nfl`, `ncaa_softball`, ...) take a `team`;
|
|
9
|
+
* the rest (`atp`, `fiba`, `nll`, ...) draw the plain surface.
|
|
10
|
+
*/
|
|
11
|
+
declare const SURFACES: Readonly<Record<string, readonly [SurfaceSport, string]>>;
|
|
12
|
+
declare const SURFACE_BASE: {
|
|
13
|
+
readonly basketball: "#d2ab6f";
|
|
14
|
+
readonly football: "#196f0c";
|
|
15
|
+
readonly hockey: "#ffffff";
|
|
16
|
+
};
|
|
17
|
+
type Paint<K extends string> = Partial<Record<K, string>>;
|
|
18
|
+
type AnyColorKey = BaseballColorKey | BasketballColorKey | CurlingColorKey | FootballColorKey | HockeyColorKey | LacrosseColorKey | SoccerColorKey | SoftballColorKey | TennisColorKey | VolleyballColorKey;
|
|
19
|
+
/**
|
|
20
|
+
* Port of `_surface.color_updates`: the team colours a sport's surface takes, keyed by sporty colour key. Sports without a
|
|
21
|
+
* team painting (baseball, softball, soccer and the team-less surfaces) take none, as in sdvplotR.
|
|
22
|
+
*/
|
|
23
|
+
declare function colorUpdates(sport: "basketball", primary: string, secondary?: string): Paint<BasketballColorKey>;
|
|
24
|
+
declare function colorUpdates(sport: "football", primary: string, secondary?: string): Paint<FootballColorKey>;
|
|
25
|
+
declare function colorUpdates(sport: "hockey", primary: string, secondary?: string): Paint<HockeyColorKey>;
|
|
26
|
+
declare function colorUpdates(sport: SurfaceSport, primary: string, secondary?: string): Record<string, string>;
|
|
27
|
+
type AnyRange = BaseballDisplayRange | BasketballDisplayRange | CurlingDisplayRange | FootballDisplayRange | HockeyDisplayRange | LacrosseDisplayRange | SoccerDisplayRange | SoftballDisplayRange | TennisDisplayRange | VolleyballDisplayRange;
|
|
28
|
+
interface SurfaceSceneOptions {
|
|
29
|
+
/** A team of the league, painting the surface; only for keys that are sdv team leagues (otherwise `InputError`). */
|
|
30
|
+
team?: Value;
|
|
31
|
+
season?: SeasonInput;
|
|
32
|
+
/** Overrides on top of the team painting, keyed by sporty colour key. */
|
|
33
|
+
colorUpdates?: Paint<AnyColorKey>;
|
|
34
|
+
rotation?: number;
|
|
35
|
+
/** A sporty display range for the league's sport; one the sport does not know throws sporty's UnknownDisplayRangeError. */
|
|
36
|
+
displayRange?: AnyRange;
|
|
37
|
+
xlim?: readonly [number, number];
|
|
38
|
+
ylim?: readonly [number, number];
|
|
39
|
+
units?: "ft" | "m" | "yd" | "in" | "cm" | "mm";
|
|
40
|
+
/** Sporty parameter overrides (intersected across sports so each sport's own keys type-check). */
|
|
41
|
+
updates?: BaseballParamUpdates & BasketballParamUpdates & CurlingParamUpdates & FootballParamUpdates & HockeyParamUpdates & LacrosseParamUpdates & SoccerParamUpdates & SoftballParamUpdates & TennisParamUpdates & VolleyballParamUpdates;
|
|
42
|
+
arcResolution?: number;
|
|
43
|
+
xTrans?: number;
|
|
44
|
+
yTrans?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { type SurfaceSceneOptions as S, SURFACES as a, SURFACE_BASE as b, colorUpdates as c };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
ESPN player headshots as marks in a LayerChart chart: a child of its `<Svg>` layer. Each row draws its `player`'s
|
|
4
|
+
headshot centred on its scaled (`x`, `y`), `height` (a fraction of the plot area's height) tall. An id with no
|
|
5
|
+
headshot draws nothing and warns once. layerchart loads on first render; missing, it throws an install error then.
|
|
6
|
+
-->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import { type HeadshotMarkProps, type LayerChart, loadLayerChart, raise } from "../svelte-core.js";
|
|
9
|
+
import Marks from "./Marks.svelte";
|
|
10
|
+
|
|
11
|
+
let props: HeadshotMarkProps = $props();
|
|
12
|
+
let lc = $state<LayerChart>();
|
|
13
|
+
let failed = $state<{ error: unknown }>();
|
|
14
|
+
// a failed import is rethrown from the template (an effect), where a <svelte:boundary> catches it
|
|
15
|
+
loadLayerChart("HeadshotMark").then(
|
|
16
|
+
(m) => (lc = m),
|
|
17
|
+
(error: unknown) => (failed = { error }),
|
|
18
|
+
);
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
{#if failed}
|
|
22
|
+
{raise(failed.error)}
|
|
23
|
+
{:else if lc}
|
|
24
|
+
<Marks {lc} kind="headshot" options={props} />
|
|
25
|
+
{/if}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type HeadshotMarkProps } from "../svelte-core.js";
|
|
2
|
+
/**
|
|
3
|
+
* ESPN player headshots as marks in a LayerChart chart: a child of its `<Svg>` layer. Each row draws its `player`'s
|
|
4
|
+
* headshot centred on its scaled (`x`, `y`), `height` (a fraction of the plot area's height) tall. An id with no
|
|
5
|
+
* headshot draws nothing and warns once. layerchart loads on first render; missing, it throws an install error then.
|
|
6
|
+
*/
|
|
7
|
+
declare const HeadshotMark: import("svelte").Component<HeadshotMarkProps, {}, "">;
|
|
8
|
+
type HeadshotMark = ReturnType<typeof HeadshotMark>;
|
|
9
|
+
export default HeadshotMark;
|
|
10
|
+
//# sourceMappingURL=HeadshotMark.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HeadshotMark.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/HeadshotMark.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,iBAAiB,EAA0C,MAAM,mBAAmB,CAAC;AA0BnG;;;;GAIG;AACH,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Team logos (or wordmarks) as marks in a LayerChart chart: a child of its `<Svg>` layer. Each row draws its
|
|
4
|
+
`team`'s logo centred on its scaled (`x`, `y`), `height` (a fraction of the plot area's height) tall. A team with no
|
|
5
|
+
logo draws nothing and warns once. layerchart loads on first render; missing, it throws an install error then.
|
|
6
|
+
-->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import { type LayerChart, type LogoMarkProps, loadLayerChart, raise } from "../svelte-core.js";
|
|
9
|
+
import Marks from "./Marks.svelte";
|
|
10
|
+
|
|
11
|
+
let props: LogoMarkProps = $props();
|
|
12
|
+
let lc = $state<LayerChart>();
|
|
13
|
+
let failed = $state<{ error: unknown }>();
|
|
14
|
+
// a failed import is rethrown from the template (an effect), where a <svelte:boundary> catches it
|
|
15
|
+
loadLayerChart("LogoMark").then(
|
|
16
|
+
(m) => (lc = m),
|
|
17
|
+
(error: unknown) => (failed = { error }),
|
|
18
|
+
);
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
{#if failed}
|
|
22
|
+
{raise(failed.error)}
|
|
23
|
+
{:else if lc}
|
|
24
|
+
<Marks {lc} kind={props.markType ?? "logo"} options={props} />
|
|
25
|
+
{/if}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type LogoMarkProps } from "../svelte-core.js";
|
|
2
|
+
/**
|
|
3
|
+
* Team logos (or wordmarks) as marks in a LayerChart chart: a child of its `<Svg>` layer. Each row draws its
|
|
4
|
+
* `team`'s logo centred on its scaled (`x`, `y`), `height` (a fraction of the plot area's height) tall. A team with no
|
|
5
|
+
* logo draws nothing and warns once. layerchart loads on first render; missing, it throws an install error then.
|
|
6
|
+
*/
|
|
7
|
+
declare const LogoMark: import("svelte").Component<LogoMarkProps, {}, "">;
|
|
8
|
+
type LogoMark = ReturnType<typeof LogoMark>;
|
|
9
|
+
export default LogoMark;
|
|
10
|
+
//# sourceMappingURL=LogoMark.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LogoMark.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/LogoMark.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,aAAa,EAAyB,MAAM,mBAAmB,CAAC;AA0B/F;;;;GAIG;AACH,QAAA,MAAM,QAAQ,mDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<svelte:options namespace="svg" />
|
|
2
|
+
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
// Internal: the images of a LogoMark or HeadshotMark, drawn once layerchart has loaded (its chart context is read
|
|
5
|
+
// here, during this component's setup inside the chart).
|
|
6
|
+
import {
|
|
7
|
+
type HeadshotMarkProps,
|
|
8
|
+
type LayerChart,
|
|
9
|
+
type LogoMarkProps,
|
|
10
|
+
markImages,
|
|
11
|
+
} from "../svelte-core.js";
|
|
12
|
+
|
|
13
|
+
let {
|
|
14
|
+
lc,
|
|
15
|
+
kind,
|
|
16
|
+
options,
|
|
17
|
+
}: { lc: LayerChart; kind: "logo" | "wordmark" | "headshot"; options: LogoMarkProps | HeadshotMarkProps } = $props();
|
|
18
|
+
// svelte-ignore state_referenced_locally
|
|
19
|
+
const ctx = lc.getChartContext(); // once, at setup: a context is only readable then (lc never changes)
|
|
20
|
+
const images = $derived(markImages(ctx, kind, options));
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
{#each images as m (m.key)}
|
|
24
|
+
<image {...m.attrs} />
|
|
25
|
+
{/each}
|