@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.
Files changed (135) hide show
  1. package/NOTICE.md +15 -0
  2. package/README.md +84 -8
  3. package/dist/chartjs-surface.d.ts +2 -2
  4. package/dist/chartjs-surface.js +2 -2
  5. package/dist/chartjs-surface.js.map +1 -1
  6. package/dist/chartjs.d.ts +32 -2
  7. package/dist/chartjs.js +7 -4
  8. package/dist/chartjs.js.map +1 -1
  9. package/dist/chunk-57WFSI4Q.js +128 -0
  10. package/dist/chunk-57WFSI4Q.js.map +1 -0
  11. package/dist/{chunk-KKH6ABMA.js → chunk-7ULOCCFL.js} +94 -4
  12. package/dist/chunk-7ULOCCFL.js.map +1 -0
  13. package/dist/chunk-AMXASQHT.js +120 -0
  14. package/dist/chunk-AMXASQHT.js.map +1 -0
  15. package/dist/chunk-B2MP37SR.js +175 -0
  16. package/dist/chunk-B2MP37SR.js.map +1 -0
  17. package/dist/{chunk-NLPH2VZT.js → chunk-DYMUVE6K.js} +4 -4
  18. package/dist/{chunk-NLPH2VZT.js.map → chunk-DYMUVE6K.js.map} +1 -1
  19. package/dist/{chunk-QY65DUEF.js → chunk-H2C3FMMA.js} +4 -10
  20. package/dist/chunk-H2C3FMMA.js.map +1 -0
  21. package/dist/{chunk-JJFTD4VB.js → chunk-J4P4HDBF.js} +4 -4
  22. package/dist/{chunk-JJFTD4VB.js.map → chunk-J4P4HDBF.js.map} +1 -1
  23. package/dist/chunk-M3XD2OSN.js +132 -0
  24. package/dist/chunk-M3XD2OSN.js.map +1 -0
  25. package/dist/{chunk-OKEE4AH6.js → chunk-OIILARXI.js} +3 -3
  26. package/dist/{chunk-OKEE4AH6.js.map → chunk-OIILARXI.js.map} +1 -1
  27. package/dist/chunk-RPVVJY74.js +170 -0
  28. package/dist/chunk-RPVVJY74.js.map +1 -0
  29. package/dist/chunk-UL5M6KDE.js +205 -0
  30. package/dist/chunk-UL5M6KDE.js.map +1 -0
  31. package/dist/chunk-WQT4IVLX.js +10 -0
  32. package/dist/chunk-WQT4IVLX.js.map +1 -0
  33. package/dist/{chunk-TAXO3VXH.js → chunk-ZAEQXFJM.js} +3 -3
  34. package/dist/{chunk-TAXO3VXH.js.map → chunk-ZAEQXFJM.js.map} +1 -1
  35. package/dist/cytoscape.d.ts +188 -0
  36. package/dist/cytoscape.js +219 -0
  37. package/dist/cytoscape.js.map +1 -0
  38. package/dist/d3.d.ts +3 -3
  39. package/dist/d3.js +6 -6
  40. package/dist/d3.js.map +1 -1
  41. package/dist/deckgl.d.ts +232 -0
  42. package/dist/deckgl.js +197 -0
  43. package/dist/deckgl.js.map +1 -0
  44. package/dist/echarts.js +5 -5
  45. package/dist/g2.d.ts +251 -0
  46. package/dist/g2.js +102 -0
  47. package/dist/g2.js.map +1 -0
  48. package/dist/index.d.ts +2 -1
  49. package/dist/index.js +4 -5
  50. package/dist/lightweight-charts.d.ts +217 -0
  51. package/dist/lightweight-charts.js +155 -0
  52. package/dist/lightweight-charts.js.map +1 -0
  53. package/dist/maplibre.d.ts +271 -0
  54. package/dist/maplibre.js +175 -0
  55. package/dist/maplibre.js.map +1 -0
  56. package/dist/mui.d.ts +338 -0
  57. package/dist/mui.js +184 -0
  58. package/dist/mui.js.map +1 -0
  59. package/dist/nivo.d.ts +193 -0
  60. package/dist/nivo.js +8 -0
  61. package/dist/nivo.js.map +1 -0
  62. package/dist/perspective.d.ts +198 -0
  63. package/dist/perspective.js +171 -0
  64. package/dist/perspective.js.map +1 -0
  65. package/dist/placement-UWkqUzpa.d.ts +32 -0
  66. package/dist/plot.d.ts +7 -5
  67. package/dist/plot.js +9 -8
  68. package/dist/plot.js.map +1 -1
  69. package/dist/plotly.js +5 -5
  70. package/dist/react.d.ts +279 -3
  71. package/dist/react.js +364 -6
  72. package/dist/react.js.map +1 -1
  73. package/dist/recharts.d.ts +162 -0
  74. package/dist/recharts.js +8 -0
  75. package/dist/recharts.js.map +1 -0
  76. package/dist/shadcn.d.ts +83 -0
  77. package/dist/shadcn.js +88 -0
  78. package/dist/shadcn.js.map +1 -0
  79. package/dist/shots.js +3 -2
  80. package/dist/shots.js.map +1 -1
  81. package/dist/surface-TR7YFJW3.js +4 -0
  82. package/dist/surface-TR7YFJW3.js.map +1 -0
  83. package/dist/surfaces-MIG7HOFU.js +6 -0
  84. package/dist/surfaces-MIG7HOFU.js.map +1 -0
  85. package/dist/surfaces-W-odWLpT.d.ts +47 -0
  86. package/dist/svelte/HeadshotMark.svelte +25 -0
  87. package/dist/svelte/HeadshotMark.svelte.d.ts +10 -0
  88. package/dist/svelte/HeadshotMark.svelte.d.ts.map +1 -0
  89. package/dist/svelte/LogoMark.svelte +25 -0
  90. package/dist/svelte/LogoMark.svelte.d.ts +10 -0
  91. package/dist/svelte/LogoMark.svelte.d.ts.map +1 -0
  92. package/dist/svelte/Marks.svelte +25 -0
  93. package/dist/svelte/Marks.svelte.d.ts +10 -0
  94. package/dist/svelte/Marks.svelte.d.ts.map +1 -0
  95. package/dist/svelte/Surface.svelte +29 -0
  96. package/dist/svelte/Surface.svelte.d.ts +9 -0
  97. package/dist/svelte/Surface.svelte.d.ts.map +1 -0
  98. package/dist/svelte/SurfaceBackdrop.svelte +26 -0
  99. package/dist/svelte/SurfaceBackdrop.svelte.d.ts +11 -0
  100. package/dist/svelte/SurfaceBackdrop.svelte.d.ts.map +1 -0
  101. package/dist/svelte/index.d.ts +13 -0
  102. package/dist/svelte/index.d.ts.map +1 -0
  103. package/dist/svelte/index.js +11 -0
  104. package/dist/svelte-core.d.ts +139 -0
  105. package/dist/svelte-core.js +123 -0
  106. package/dist/svelte-core.js.map +1 -0
  107. package/dist/tanstack.d.ts +240 -0
  108. package/dist/tanstack.js +110 -0
  109. package/dist/tanstack.js.map +1 -0
  110. package/dist/testing.js +4 -5
  111. package/dist/testing.js.map +1 -1
  112. package/dist/three.d.ts +223 -0
  113. package/dist/three.js +50 -0
  114. package/dist/three.js.map +1 -0
  115. package/dist/{tiers-B6pUBPcE.d.ts → tiers-BN4md-GB.d.ts} +2 -30
  116. package/dist/unovis.d.ts +127 -0
  117. package/dist/unovis.js +108 -0
  118. package/dist/unovis.js.map +1 -0
  119. package/dist/uplot.d.ts +120 -0
  120. package/dist/uplot.js +110 -0
  121. package/dist/uplot.js.map +1 -0
  122. package/dist/vega.js +5 -5
  123. package/dist/victory.d.ts +107 -0
  124. package/dist/victory.js +38 -0
  125. package/dist/victory.js.map +1 -0
  126. package/package.json +218 -6
  127. package/dist/chunk-KKH6ABMA.js.map +0 -1
  128. package/dist/chunk-QY65DUEF.js.map +0 -1
  129. package/dist/chunk-RMT2GMFH.js +0 -97
  130. package/dist/chunk-RMT2GMFH.js.map +0 -1
  131. package/dist/chunk-SEBO35DV.js +0 -76
  132. package/dist/chunk-SEBO35DV.js.map +0 -1
  133. package/dist/chunk-TDNBAEQB.js +0 -105
  134. package/dist/chunk-TDNBAEQB.js.map +0 -1
  135. package/dist/surfaces-ChaD9nei.d.ts +0 -38
@@ -0,0 +1,217 @@
1
+ import { M as MarkOptions, a as DrawnMark, R as Row } from './_web-D7Aa5Rd8.js';
2
+ import { V as Value } from './resolve-CQW-jzvr.js';
3
+ import { L as League, S as SeasonInput, W as Which, I as IdSystem } from './types-C7n6gJF3.js';
4
+
5
+ /**
6
+ * The TradingView Lightweight Charts adapter: chart options that keep TradingView's attribution on, team-coloured
7
+ * series options with prices read as probabilities or odds, and team logos drawn at points by a series primitive.
8
+ * Pure: lightweight-charts is never imported (the types here are structural, read from lightweight-charts 5.2.1), so
9
+ * everything can be built anywhere, a server included; only `createChart(...)` needs a browser. Call after
10
+ * `loadLeague(league)` / `preloadAll()`.
11
+ *
12
+ * Lightweight Charts is Apache-2.0 and asks every page that shows it for TradingView's attribution notice
13
+ * (`TRADINGVIEW_NOTICE`) and a link to https://www.tradingview.com/. `withAttribution` keeps the library's own
14
+ * attribution logo on, which is that link, and `attributionNotice` puts the notice on the page.
15
+ */
16
+
17
+ /**
18
+ * TradingView's attribution notice: the NOTICE file of tradingview/lightweight-charts at v5.2.1, verbatim (its "с" is
19
+ * the Cyrillic letter, as upstream writes it). Lightweight Charts' licence asks for it, and a link to
20
+ * https://www.tradingview.com/, on the page that shows the chart.
21
+ */
22
+ declare const TRADINGVIEW_NOTICE = "TradingView Lightweight Charts\u2122\nCopyright (\u0441) 2025 TradingView, Inc. https://www.tradingview.com/";
23
+ /** `withAttribution` options. */
24
+ interface AttributionOptions {
25
+ /**
26
+ * `true` turns TradingView's attribution logo off. Pass it only when the page already shows TradingView's
27
+ * attribution notice and a link to https://www.tradingview.com/ elsewhere: meeting Lightweight Charts' licence is
28
+ * then on you. Anything but `true` (or `false`, the default) throws.
29
+ */
30
+ attributedElsewhere?: boolean;
31
+ }
32
+ /**
33
+ * Chart options for `createChart` with TradingView's attribution logo on (`layout.attributionLogo: true`), the
34
+ * library's own link to https://www.tradingview.com/ that its licence asks for. The options are copied, never changed;
35
+ * the rest of them is kept. A `layout.attributionLogo: false` in them is turned back on, with one warning: only the
36
+ * explicit `{ attributedElsewhere: true }` turns it off, and the licence is then the caller's to meet.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { attributionNotice, withAttribution } from "@sportsdataverse/sdvplot/lightweight-charts";
41
+ * import { createChart } from "lightweight-charts";
42
+ *
43
+ * const el = document.body.appendChild(document.createElement("div"));
44
+ * // the chart, with TradingView's logo (its link) on it, and TradingView's notice on the page
45
+ * const chart = createChart(el, withAttribution({ width: 640, height: 320 }));
46
+ * const notice = attributionNotice(el.parentElement ?? el);
47
+ * [chart.options().layout.attributionLogo, notice.textContent];
48
+ * ```
49
+ */
50
+ declare function withAttribution<F extends object = Record<never, never>>(chartOptions?: F, o?: AttributionOptions): F & {
51
+ layout: {
52
+ attributionLogo: boolean;
53
+ };
54
+ };
55
+ /**
56
+ * Puts TradingView's attribution notice on the page, as Lightweight Charts' licence asks: a `<p>` appended to
57
+ * `container`, holding `TRADINGVIEW_NOTICE` (its URL a link, its two lines kept), and returned. Idempotent: called
58
+ * again on the same container it returns the notice already there rather than adding a second. Give it the element
59
+ * around the chart (`el.parentElement ?? el` for the chart's own `el`), so the notice sits under the chart. On a
60
+ * server (no `document`) it does nothing and returns `container`. `withAttribution`'s example draws a chart with it.
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * import { TRADINGVIEW_NOTICE, attributionNotice } from "@sportsdataverse/sdvplot/lightweight-charts";
65
+ *
66
+ * const figure = document.body.appendChild(document.createElement("figure"));
67
+ * const notice = attributionNotice(figure);
68
+ * // once per container, and exactly TradingView's notice
69
+ * [attributionNotice(figure) === notice, notice.textContent === TRADINGVIEW_NOTICE];
70
+ * ```
71
+ */
72
+ declare function attributionNotice(container: HTMLElement): HTMLElement;
73
+ /** What a price is: a probability (a fraction in [0, 1], read as a percentage) or American odds (-110, +150). */
74
+ type PriceUnits = "probability" | "american";
75
+ /** A Lightweight Charts custom price format. */
76
+ interface LwcPriceFormat {
77
+ /** Always "custom": the formatter reads the prices. */
78
+ type: "custom";
79
+ /** A price as text: "58.5%" for a probability, "+150" for American odds. */
80
+ formatter: (price: number) => string;
81
+ /** The smallest price step: 0.001 for a probability (0.1%), 1 for American odds. */
82
+ minMove: number;
83
+ }
84
+ /** What `teamSeriesOptions` returns: a LineSeries' options. */
85
+ interface LwcTeamSeriesOptions {
86
+ /** The line's colour, `#rrggbb`. */
87
+ color: string;
88
+ /** With `units`: how the price scale, the crosshair label and the last-value label read the prices. */
89
+ priceFormat?: LwcPriceFormat;
90
+ }
91
+ /** `teamSeriesOptions` options. */
92
+ interface TeamSeriesOptions {
93
+ /** The league the team belongs to, e.g. "nfl". */
94
+ league: League;
95
+ /** Every team on the chart, in order (the team itself among them), so that with `background` each gets its own
96
+ * colour. Default: the team alone. */
97
+ teams?: readonly Value[];
98
+ /** What the prices are: "probability" (a fraction in [0, 1], read "58.5%") or "american" (odds, read "+150").
99
+ * Default: Lightweight Charts' own number format. */
100
+ units?: PriceUnits;
101
+ /** The season the team is resolved (and coloured) for. */
102
+ season?: SeasonInput;
103
+ /** "primary" (default) or "secondary". */
104
+ which?: Which;
105
+ /** How the team values are read. Default "auto". */
106
+ idSystem?: IdSystem;
107
+ /** The colour of a team that does not resolve. Default "#808080". */
108
+ fallback?: string;
109
+ /**
110
+ * The chart's background. Given, every colour keeps 3:1 contrast against it (dark mode): a team colour under that
111
+ * is swapped for the team's other colour, else moved towards white (on a dark background) or black (a light one)
112
+ * until it reads. A colour that would then sit too close to an earlier team's in `teams` (CIE76 ΔE under 20) takes
113
+ * the team's other colour, or the nearest colour to its own (lightness, hue or chroma stepped) that is distinct and
114
+ * still reads; failing that it is kept, with one warning.
115
+ */
116
+ background?: string;
117
+ }
118
+ /**
119
+ * A LineSeries' options in a team's colour, for `chart.addSeries(LineSeries, ...)`. With `units`, the prices read as
120
+ * probabilities ("58.5%") or American odds ("+150", "-120") on the price scale, the crosshair and the last-value line.
121
+ * With `background`, the colour keeps 3:1 contrast against it and aims to stay distinct from the other `teams` on the
122
+ * chart (when it cannot, it keeps the readable colour and warns once).
123
+ * A team that does not resolve takes `fallback` and warns; it never moves a real team's colour.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * import { loadLeague } from "@sportsdataverse/sdvplot";
128
+ * import { teamSeriesOptions } from "@sportsdataverse/sdvplot/lightweight-charts";
129
+ *
130
+ * await loadLeague("nfl");
131
+ * const teams = ["PHI", "KC"];
132
+ * const phi = teamSeriesOptions("PHI", { league: "nfl", teams, units: "probability", background: "#181a1b" });
133
+ * // in the browser: chart.addSeries(LineSeries, phi)
134
+ * [phi.color, phi.priceFormat?.formatter(0.5846)];
135
+ * ```
136
+ */
137
+ declare function teamSeriesOptions(team: Value, o: TeamSeriesOptions): LwcTeamSeriesOptions;
138
+ /** A Lightweight Charts time: a UTC timestamp in seconds, a "yyyy-mm-dd" string or a business day. */
139
+ type LwcTime = number | string | {
140
+ year: number;
141
+ month: number;
142
+ day: number;
143
+ };
144
+ /** The parts of what Lightweight Charts attaches a series primitive with that `logoMarkers` reads. */
145
+ interface LwcAttached {
146
+ /** The chart: its time scale places `x`. */
147
+ chart: {
148
+ timeScale(): {
149
+ timeToCoordinate(time: LwcTime): number | null;
150
+ };
151
+ };
152
+ /** The series the primitive is attached to: its price scale places `y`. */
153
+ series: {
154
+ priceToCoordinate(price: number): number | null;
155
+ };
156
+ /** Asks the chart to redraw. */
157
+ requestUpdate: () => void;
158
+ }
159
+ /** The drawing target Lightweight Charts hands a primitive's renderer (fancy-canvas' `CanvasRenderingTarget2D`). */
160
+ interface LwcRenderTarget {
161
+ /** Calls `f` with the 2D context scaled to CSS pixels and the pane's size in them. */
162
+ useMediaCoordinateSpace<T>(f: (scope: {
163
+ context: CanvasRenderingContext2D;
164
+ mediaSize: {
165
+ width: number;
166
+ height: number;
167
+ };
168
+ }) => T): T;
169
+ }
170
+ /** A Lightweight Charts series primitive: attach it with `series.attachPrimitive(primitive)`. */
171
+ interface LwcSeriesPrimitive {
172
+ /** Lightweight Charts calls this on `attachPrimitive`: the logos start loading. */
173
+ attached(param: LwcAttached): void;
174
+ /** Lightweight Charts calls this on `detachPrimitive`: the chart is forgotten, nothing more is drawn, and
175
+ * `drawnMarks` reads empty. */
176
+ detached(): void;
177
+ /** Lightweight Charts calls this when the view changes; the logos are placed at each draw, so it does nothing. */
178
+ updateAllViews(): void;
179
+ /** The one pane view, whose renderer paints the logos (the same array every call, as the library asks). */
180
+ paneViews(): readonly {
181
+ renderer(): {
182
+ draw(target: LwcRenderTarget): void;
183
+ } | null;
184
+ }[];
185
+ }
186
+ /** `logoMarkers` options: the spec adapters' mark options, less `embed`, plus what the prices are. */
187
+ interface LogoMarkersOptions extends Omit<MarkOptions, "embed"> {
188
+ /** What `y` is: "probability" values must be fractions in [0, 1], "american" values -100 or less or +100 or more. */
189
+ units?: PriceUnits;
190
+ }
191
+ /**
192
+ * Team logos on a Lightweight Charts series: a series primitive (`series.attachPrimitive(...)`) whose renderer paints
193
+ * each row's logo centred on its `x` (a time, as in the series' data) and `y` (a price on the series' scale), placed
194
+ * with the chart's own `timeToCoordinate` / `priceToCoordinate`, `height` of the pane tall (a fraction, as in the spec
195
+ * adapters). Lightweight Charts' series markers draw only shapes and text, so logos take a primitive. The images start
196
+ * loading when it is attached, and the chart redraws as each one lands; building it fetches nothing. A team with no
197
+ * logo draws nothing and warns once, as `sdvplot/echarts` does; an image that fails to load is never drawn and warns
198
+ * once; a row at a time the chart has no point for, or off the pane, is not drawn.
199
+ *
200
+ * @example
201
+ * ```ts
202
+ * import { loadLeague } from "@sportsdataverse/sdvplot";
203
+ * import { logoMarkers } from "@sportsdataverse/sdvplot/lightweight-charts";
204
+ *
205
+ * await loadLeague("nfl");
206
+ * // Super Bowl LIX's final reading (ESPN): 3600 seconds played, Philadelphia's win probability 1
207
+ * const rows = [{ t: 3600, wp: 1, team: "PHI" }];
208
+ * const logos = logoMarkers(rows, { x: "t", y: "wp", team: "team", league: "nfl", units: "probability" });
209
+ * // in the browser: series.attachPrimitive(logos)
210
+ * typeof logos.paneViews;
211
+ * ```
212
+ */
213
+ declare function logoMarkers(rows: readonly Row[], o: LogoMarkersOptions): LwcSeriesPrimitive;
214
+ /** Test hook: [teamId, x, y, height, url] per logo the primitive last drew, `height` a fraction of the pane. */
215
+ declare function drawnMarks(primitive: LwcSeriesPrimitive): DrawnMark[];
216
+
217
+ export { type AttributionOptions, DrawnMark, type LogoMarkersOptions, type LwcAttached, type LwcPriceFormat, type LwcRenderTarget, type LwcSeriesPrimitive, type LwcTeamSeriesOptions, type LwcTime, MarkOptions, type PriceUnits, Row, TRADINGVIEW_NOTICE, type TeamSeriesOptions, attributionNotice, drawnMarks, logoMarkers, teamSeriesOptions, withAttribution };
@@ -0,0 +1,155 @@
1
+ import { colorList, teamPalette, markPlacements, aspect } from './chunk-UL5M6KDE.js';
2
+ import { checkHeight, checkAlpha } from './chunk-7ULOCCFL.js';
3
+ import { resolveSync } from './chunk-ZAEQXFJM.js';
4
+ import { hex6 } from './chunk-J6R2LEJ7.js';
5
+ import { InputError, warn } from './chunk-E7G7VSLV.js';
6
+
7
+ // src/lightweight-charts.ts
8
+ var TRADINGVIEW_NOTICE = "TradingView Lightweight Charts\u2122\nCopyright (\u0441) 2025 TradingView, Inc. https://www.tradingview.com/";
9
+ function withAttribution(chartOptions, o = {}) {
10
+ const off = o.attributedElsewhere;
11
+ if (off !== void 0 && typeof off !== "boolean")
12
+ throw new InputError(
13
+ `attributedElsewhere is true (your page attributes TradingView itself) or left out, not ${JSON.stringify(off)}`
14
+ );
15
+ const opts = chartOptions ?? {};
16
+ if (off !== true && opts.layout?.attributionLogo === false)
17
+ warn(
18
+ "lightweight-charts:attribution",
19
+ "layout.attributionLogo: false was turned back on: Lightweight Charts' licence asks for TradingView's notice and link. Pass { attributedElsewhere: true } only if your page shows both itself"
20
+ );
21
+ return { ...opts, layout: { ...opts.layout, attributionLogo: off !== true } };
22
+ }
23
+ function attributionNotice(container) {
24
+ if (typeof document === "undefined") return container;
25
+ for (const c of Array.from(container.children))
26
+ if (c instanceof HTMLElement && c.dataset.sdvAttribution === "tradingview") return c;
27
+ const doc = container.ownerDocument;
28
+ const url = "https://www.tradingview.com/";
29
+ const p = doc.createElement("p");
30
+ p.dataset.sdvAttribution = "tradingview";
31
+ p.style.whiteSpace = "pre-line";
32
+ const a = doc.createElement("a");
33
+ a.href = url;
34
+ a.textContent = url;
35
+ p.append(TRADINGVIEW_NOTICE.slice(0, -url.length), a);
36
+ container.append(p);
37
+ return p;
38
+ }
39
+ var UNITS = ["probability", "american"];
40
+ var checkUnits = (u) => {
41
+ if (u === void 0 || UNITS.includes(u)) return u;
42
+ throw new InputError(`units is "probability" or "american", not ${JSON.stringify(u)}`);
43
+ };
44
+ var percent = (p) => `${Number((p * 100).toFixed(1))}%`;
45
+ var american = (v) => {
46
+ const r = Math.sign(v) * Math.round(Math.abs(v));
47
+ return r === 0 ? "0" : `${r > 0 ? "+" : ""}${r}`;
48
+ };
49
+ function teamSeriesOptions(team, o) {
50
+ const units = checkUnits(o.units);
51
+ const teams = o.teams ?? [team];
52
+ const at = teams.indexOf(team);
53
+ if (at < 0) throw new InputError(`team ${JSON.stringify(team)} is not in teams ${JSON.stringify(teams)}`);
54
+ const fallback = hex6(o.fallback ?? "#808080", { dropAlpha: true });
55
+ const season = o.season === void 0 ? {} : { season: o.season };
56
+ const ids = resolveSync(teams, o.league, {
57
+ ...season,
58
+ ...o.idSystem === void 0 ? {} : { idSystem: o.idSystem }
59
+ });
60
+ const id = ids[at];
61
+ let color = fallback;
62
+ if (id !== void 0 && id !== null) {
63
+ const known = [...new Set(ids.filter((x) => x !== void 0 && x !== null))];
64
+ const which = o.which ?? "primary";
65
+ const co = { ...season, idSystem: "team_id" };
66
+ const main = colorList(o.league, known, { ...co, which });
67
+ const other = colorList(o.league, known, { ...co, which: which === "primary" ? "secondary" : "primary" });
68
+ let colors = main.map((c, i) => hex6(c ?? other[i] ?? fallback, { dropAlpha: true }));
69
+ if (o.background !== void 0) colors = teamPalette(colors, other, hex6(o.background), known);
70
+ color = colors[known.indexOf(id)];
71
+ }
72
+ if (units === void 0) return { color };
73
+ return {
74
+ color,
75
+ priceFormat: units === "probability" ? { type: "custom", formatter: percent, minMove: 1e-3 } : { type: "custom", formatter: american, minMove: 1 }
76
+ };
77
+ }
78
+ var drawn = /* @__PURE__ */ new WeakMap();
79
+ function logoMarkers(rows, o) {
80
+ const h = checkHeight(o.height ?? 0.1);
81
+ const a = checkAlpha(o.alpha ?? 1);
82
+ const units = checkUnits(o.units);
83
+ if (units !== void 0)
84
+ for (const r of rows) {
85
+ if (r[o.y] === void 0 || r[o.y] === null) continue;
86
+ const v = Number(r[o.y]);
87
+ if (units === "probability" && !(v >= 0 && v <= 1))
88
+ throw new InputError(
89
+ `a probability is a fraction in [0, 1], not ${r[o.y]} (a percent? divide it by 100)`
90
+ );
91
+ if (units === "american" && !(Math.abs(v) >= 100))
92
+ throw new InputError(`American odds are -100 or less or +100 or more, not ${r[o.y]} (decimal odds?)`);
93
+ }
94
+ const ps = markPlacements(rows, "logo", o);
95
+ const images = /* @__PURE__ */ new Map();
96
+ let at;
97
+ const load = (url) => {
98
+ if (images.has(url) || typeof Image === "undefined") return;
99
+ const img = new Image();
100
+ img.crossOrigin = "anonymous";
101
+ images.set(url, img);
102
+ img.onload = () => at?.requestUpdate();
103
+ img.onerror = () => {
104
+ images.set(url, null);
105
+ warn(`lightweight-charts:image:${url}`, `could not load ${url}, so it is not drawn`);
106
+ };
107
+ img.src = url;
108
+ };
109
+ const draw = (target) => {
110
+ const c = at;
111
+ if (c === void 0) return;
112
+ target.useMediaCoordinateSpace(({ context: ctx, mediaSize }) => {
113
+ const out = [];
114
+ const ph = h * mediaSize.height;
115
+ ctx.save();
116
+ ctx.globalAlpha = a;
117
+ for (const p of ps) {
118
+ const img = images.get(p.url);
119
+ if (!img?.complete || img.naturalWidth === 0) continue;
120
+ const cx = c.chart.timeScale().timeToCoordinate(p.x);
121
+ const cy = c.series.priceToCoordinate(Number(p.y));
122
+ if (cx === null || cy === null) continue;
123
+ if (!(cx >= 0 && cx <= mediaSize.width && cy >= 0 && cy <= mediaSize.height)) continue;
124
+ const pw = ph * aspect(p);
125
+ ctx.drawImage(img, cx - pw / 2, cy - ph / 2, pw, ph);
126
+ out.push([p.id, p.x, p.y, h, p.url]);
127
+ }
128
+ ctx.restore();
129
+ drawn.set(primitive, out);
130
+ });
131
+ };
132
+ const views = [{ renderer: () => ({ draw }) }];
133
+ const primitive = {
134
+ attached(param) {
135
+ at = param;
136
+ for (const p of ps) load(p.url);
137
+ param.requestUpdate();
138
+ },
139
+ detached() {
140
+ at = void 0;
141
+ drawn.delete(primitive);
142
+ },
143
+ updateAllViews() {
144
+ },
145
+ paneViews: () => views
146
+ };
147
+ return primitive;
148
+ }
149
+ function drawnMarks(primitive) {
150
+ return [...drawn.get(primitive) ?? []];
151
+ }
152
+
153
+ export { TRADINGVIEW_NOTICE, attributionNotice, drawnMarks, logoMarkers, teamSeriesOptions, withAttribution };
154
+ //# sourceMappingURL=lightweight-charts.js.map
155
+ //# sourceMappingURL=lightweight-charts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lightweight-charts.ts"],"names":[],"mappings":"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","file":"lightweight-charts.js","sourcesContent":["/**\n * The TradingView Lightweight Charts adapter: chart options that keep TradingView's attribution on, team-coloured\n * series options with prices read as probabilities or odds, and team logos drawn at points by a series primitive.\n * Pure: lightweight-charts is never imported (the types here are structural, read from lightweight-charts 5.2.1), so\n * everything can be built anywhere, a server included; only `createChart(...)` needs a browser. Call after\n * `loadLeague(league)` / `preloadAll()`.\n *\n * Lightweight Charts is Apache-2.0 and asks every page that shows it for TradingView's attribution notice\n * (`TRADINGVIEW_NOTICE`) and a link to https://www.tradingview.com/. `withAttribution` keeps the library's own\n * attribution logo on, which is that link, and `attributionNotice` puts the notice on the page.\n */\nimport {\n type MarkOptions,\n type Row,\n aspect,\n checkAlpha,\n checkHeight,\n colorList,\n markPlacements,\n} from \"./_web.js\";\nimport type { DrawnMark } from \"./_web.js\";\nimport { hex6 } from \"./contrast.js\";\nimport { InputError, warn } from \"./errors.js\";\nimport { resolveSync } from \"./resolve.js\";\nimport type { Value } from \"./resolve.js\";\nimport { teamPalette } from \"./team-palette.js\";\nimport type { IdSystem, League, SeasonInput, Which } from \"./types.js\";\nexport type { DrawnMark, MarkOptions, Row } from \"./_web.js\";\n\n/**\n * TradingView's attribution notice: the NOTICE file of tradingview/lightweight-charts at v5.2.1, verbatim (its \"с\" is\n * the Cyrillic letter, as upstream writes it). Lightweight Charts' licence asks for it, and a link to\n * https://www.tradingview.com/, on the page that shows the chart.\n */\nexport const TRADINGVIEW_NOTICE =\n \"TradingView Lightweight Charts™\\nCopyright (с) 2025 TradingView, Inc. https://www.tradingview.com/\";\n\n/** `withAttribution` options. */\nexport interface AttributionOptions {\n /**\n * `true` turns TradingView's attribution logo off. Pass it only when the page already shows TradingView's\n * attribution notice and a link to https://www.tradingview.com/ elsewhere: meeting Lightweight Charts' licence is\n * then on you. Anything but `true` (or `false`, the default) throws.\n */\n attributedElsewhere?: boolean;\n}\n\n/**\n * Chart options for `createChart` with TradingView's attribution logo on (`layout.attributionLogo: true`), the\n * library's own link to https://www.tradingview.com/ that its licence asks for. The options are copied, never changed;\n * the rest of them is kept. A `layout.attributionLogo: false` in them is turned back on, with one warning: only the\n * explicit `{ attributedElsewhere: true }` turns it off, and the licence is then the caller's to meet.\n *\n * @example\n * ```ts\n * import { attributionNotice, withAttribution } from \"@sportsdataverse/sdvplot/lightweight-charts\";\n * import { createChart } from \"lightweight-charts\";\n *\n * const el = document.body.appendChild(document.createElement(\"div\"));\n * // the chart, with TradingView's logo (its link) on it, and TradingView's notice on the page\n * const chart = createChart(el, withAttribution({ width: 640, height: 320 }));\n * const notice = attributionNotice(el.parentElement ?? el);\n * [chart.options().layout.attributionLogo, notice.textContent];\n * ```\n */\nexport function withAttribution<F extends object = Record<never, never>>(\n chartOptions?: F,\n o: AttributionOptions = {},\n): F & { layout: { attributionLogo: boolean } } {\n const off = o.attributedElsewhere;\n if (off !== undefined && typeof off !== \"boolean\")\n throw new InputError(\n `attributedElsewhere is true (your page attributes TradingView itself) or left out, not ${JSON.stringify(off)}`,\n );\n const opts = (chartOptions ?? {}) as F & { layout?: { attributionLogo?: unknown } };\n if (off !== true && opts.layout?.attributionLogo === false)\n warn(\n \"lightweight-charts:attribution\",\n \"layout.attributionLogo: false was turned back on: Lightweight Charts' licence asks for TradingView's notice and link. Pass { attributedElsewhere: true } only if your page shows both itself\",\n );\n return { ...opts, layout: { ...opts.layout, attributionLogo: off !== true } };\n}\n\n/**\n * Puts TradingView's attribution notice on the page, as Lightweight Charts' licence asks: a `<p>` appended to\n * `container`, holding `TRADINGVIEW_NOTICE` (its URL a link, its two lines kept), and returned. Idempotent: called\n * again on the same container it returns the notice already there rather than adding a second. Give it the element\n * around the chart (`el.parentElement ?? el` for the chart's own `el`), so the notice sits under the chart. On a\n * server (no `document`) it does nothing and returns `container`. `withAttribution`'s example draws a chart with it.\n *\n * @example\n * ```ts\n * import { TRADINGVIEW_NOTICE, attributionNotice } from \"@sportsdataverse/sdvplot/lightweight-charts\";\n *\n * const figure = document.body.appendChild(document.createElement(\"figure\"));\n * const notice = attributionNotice(figure);\n * // once per container, and exactly TradingView's notice\n * [attributionNotice(figure) === notice, notice.textContent === TRADINGVIEW_NOTICE];\n * ```\n */\nexport function attributionNotice(container: HTMLElement): HTMLElement {\n if (typeof document === \"undefined\") return container;\n for (const c of Array.from(container.children))\n if (c instanceof HTMLElement && c.dataset.sdvAttribution === \"tradingview\") return c;\n const doc = container.ownerDocument;\n const url = \"https://www.tradingview.com/\";\n const p = doc.createElement(\"p\");\n p.dataset.sdvAttribution = \"tradingview\";\n p.style.whiteSpace = \"pre-line\"; // the notice's two lines\n const a = doc.createElement(\"a\");\n a.href = url;\n a.textContent = url;\n p.append(TRADINGVIEW_NOTICE.slice(0, -url.length), a);\n container.append(p);\n return p;\n}\n\n/** What a price is: a probability (a fraction in [0, 1], read as a percentage) or American odds (-110, +150). */\nexport type PriceUnits = \"probability\" | \"american\";\nconst UNITS: readonly PriceUnits[] = [\"probability\", \"american\"];\nconst checkUnits = (u: unknown): PriceUnits | undefined => {\n if (u === undefined || UNITS.includes(u as PriceUnits)) return u as PriceUnits | undefined;\n throw new InputError(`units is \"probability\" or \"american\", not ${JSON.stringify(u)}`);\n};\nconst percent = (p: number): string => `${Number((p * 100).toFixed(1))}%`;\n/** American odds as text: rounded half away from zero, signed, and \"0\" (never \"+0\" or \"-0\") for a zero. */\nconst american = (v: number): string => {\n const r = Math.sign(v) * Math.round(Math.abs(v));\n return r === 0 ? \"0\" : `${r > 0 ? \"+\" : \"\"}${r}`;\n};\n\n/** A Lightweight Charts custom price format. */\nexport interface LwcPriceFormat {\n /** Always \"custom\": the formatter reads the prices. */\n type: \"custom\";\n /** A price as text: \"58.5%\" for a probability, \"+150\" for American odds. */\n formatter: (price: number) => string;\n /** The smallest price step: 0.001 for a probability (0.1%), 1 for American odds. */\n minMove: number;\n}\n/** What `teamSeriesOptions` returns: a LineSeries' options. */\nexport interface LwcTeamSeriesOptions {\n /** The line's colour, `#rrggbb`. */\n color: string;\n /** With `units`: how the price scale, the crosshair label and the last-value label read the prices. */\n priceFormat?: LwcPriceFormat;\n}\n/** `teamSeriesOptions` options. */\nexport interface TeamSeriesOptions {\n /** The league the team belongs to, e.g. \"nfl\". */\n league: League;\n /** Every team on the chart, in order (the team itself among them), so that with `background` each gets its own\n * colour. Default: the team alone. */\n teams?: readonly Value[];\n /** What the prices are: \"probability\" (a fraction in [0, 1], read \"58.5%\") or \"american\" (odds, read \"+150\").\n * Default: Lightweight Charts' own number format. */\n units?: PriceUnits;\n /** The season the team is resolved (and coloured) for. */\n season?: SeasonInput;\n /** \"primary\" (default) or \"secondary\". */\n which?: Which;\n /** How the team values are read. Default \"auto\". */\n idSystem?: IdSystem;\n /** The colour of a team that does not resolve. Default \"#808080\". */\n fallback?: string;\n /**\n * The chart's background. Given, every colour keeps 3:1 contrast against it (dark mode): a team colour under that\n * is swapped for the team's other colour, else moved towards white (on a dark background) or black (a light one)\n * until it reads. A colour that would then sit too close to an earlier team's in `teams` (CIE76 ΔE under 20) takes\n * the team's other colour, or the nearest colour to its own (lightness, hue or chroma stepped) that is distinct and\n * still reads; failing that it is kept, with one warning.\n */\n background?: string;\n}\n\n/**\n * A LineSeries' options in a team's colour, for `chart.addSeries(LineSeries, ...)`. With `units`, the prices read as\n * probabilities (\"58.5%\") or American odds (\"+150\", \"-120\") on the price scale, the crosshair and the last-value line.\n * With `background`, the colour keeps 3:1 contrast against it and aims to stay distinct from the other `teams` on the\n * chart (when it cannot, it keeps the readable colour and warns once).\n * A team that does not resolve takes `fallback` and warns; it never moves a real team's colour.\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { teamSeriesOptions } from \"@sportsdataverse/sdvplot/lightweight-charts\";\n *\n * await loadLeague(\"nfl\");\n * const teams = [\"PHI\", \"KC\"];\n * const phi = teamSeriesOptions(\"PHI\", { league: \"nfl\", teams, units: \"probability\", background: \"#181a1b\" });\n * // in the browser: chart.addSeries(LineSeries, phi)\n * [phi.color, phi.priceFormat?.formatter(0.5846)];\n * ```\n */\nexport function teamSeriesOptions(team: Value, o: TeamSeriesOptions): LwcTeamSeriesOptions {\n const units = checkUnits(o.units);\n const teams = o.teams ?? [team];\n const at = teams.indexOf(team);\n if (at < 0) throw new InputError(`team ${JSON.stringify(team)} is not in teams ${JSON.stringify(teams)}`);\n const fallback = hex6(o.fallback ?? \"#808080\", { dropAlpha: true });\n const season = o.season === undefined ? {} : { season: o.season };\n const ids = resolveSync(teams, o.league, {\n ...season,\n ...(o.idSystem === undefined ? {} : { idSystem: o.idSystem }),\n });\n const id = ids[at];\n let color = fallback;\n if (id !== undefined && id !== null) {\n // the palette runs over the resolved teams only: an unresolved one never moves a real team's colour\n const known = [...new Set(ids.filter((x): x is NonNullable<typeof x> => x !== undefined && x !== null))];\n const which = o.which ?? \"primary\";\n const co = { ...season, idSystem: \"team_id\" as const };\n const main = colorList(o.league, known, { ...co, which });\n const other = colorList(o.league, known, { ...co, which: which === \"primary\" ? \"secondary\" : \"primary\" });\n let colors = main.map((c, i) => hex6(c ?? other[i] ?? fallback, { dropAlpha: true }));\n if (o.background !== undefined) colors = teamPalette(colors, other, hex6(o.background), known);\n color = colors[known.indexOf(id)] as string;\n }\n if (units === undefined) return { color };\n return {\n color,\n priceFormat:\n units === \"probability\"\n ? { type: \"custom\", formatter: percent, minMove: 0.001 }\n : { type: \"custom\", formatter: american, minMove: 1 },\n };\n}\n\n/** A Lightweight Charts time: a UTC timestamp in seconds, a \"yyyy-mm-dd\" string or a business day. */\nexport type LwcTime = number | string | { year: number; month: number; day: number };\n/** The parts of what Lightweight Charts attaches a series primitive with that `logoMarkers` reads. */\nexport interface LwcAttached {\n /** The chart: its time scale places `x`. */\n chart: { timeScale(): { timeToCoordinate(time: LwcTime): number | null } };\n /** The series the primitive is attached to: its price scale places `y`. */\n series: { priceToCoordinate(price: number): number | null };\n /** Asks the chart to redraw. */\n requestUpdate: () => void;\n}\n/** The drawing target Lightweight Charts hands a primitive's renderer (fancy-canvas' `CanvasRenderingTarget2D`). */\nexport interface LwcRenderTarget {\n /** Calls `f` with the 2D context scaled to CSS pixels and the pane's size in them. */\n useMediaCoordinateSpace<T>(\n f: (scope: { context: CanvasRenderingContext2D; mediaSize: { width: number; height: number } }) => T,\n ): T;\n}\n/** A Lightweight Charts series primitive: attach it with `series.attachPrimitive(primitive)`. */\nexport interface LwcSeriesPrimitive {\n /** Lightweight Charts calls this on `attachPrimitive`: the logos start loading. */\n attached(param: LwcAttached): void;\n /** Lightweight Charts calls this on `detachPrimitive`: the chart is forgotten, nothing more is drawn, and\n * `drawnMarks` reads empty. */\n detached(): void;\n /** Lightweight Charts calls this when the view changes; the logos are placed at each draw, so it does nothing. */\n updateAllViews(): void;\n /** The one pane view, whose renderer paints the logos (the same array every call, as the library asks). */\n paneViews(): readonly { renderer(): { draw(target: LwcRenderTarget): void } | null }[];\n}\n/** `logoMarkers` options: the spec adapters' mark options, less `embed`, plus what the prices are. */\nexport interface LogoMarkersOptions extends Omit<MarkOptions, \"embed\"> {\n /** What `y` is: \"probability\" values must be fractions in [0, 1], \"american\" values -100 or less or +100 or more. */\n units?: PriceUnits;\n}\n\n// ponytail: a WeakMap per primitive of what it last drew, so drawnMarks reads what reached the canvas\nconst drawn = new WeakMap<LwcSeriesPrimitive, DrawnMark[]>();\n\n/**\n * Team logos on a Lightweight Charts series: a series primitive (`series.attachPrimitive(...)`) whose renderer paints\n * each row's logo centred on its `x` (a time, as in the series' data) and `y` (a price on the series' scale), placed\n * with the chart's own `timeToCoordinate` / `priceToCoordinate`, `height` of the pane tall (a fraction, as in the spec\n * adapters). Lightweight Charts' series markers draw only shapes and text, so logos take a primitive. The images start\n * loading when it is attached, and the chart redraws as each one lands; building it fetches nothing. A team with no\n * logo draws nothing and warns once, as `sdvplot/echarts` does; an image that fails to load is never drawn and warns\n * once; a row at a time the chart has no point for, or off the pane, is not drawn.\n *\n * @example\n * ```ts\n * import { loadLeague } from \"@sportsdataverse/sdvplot\";\n * import { logoMarkers } from \"@sportsdataverse/sdvplot/lightweight-charts\";\n *\n * await loadLeague(\"nfl\");\n * // Super Bowl LIX's final reading (ESPN): 3600 seconds played, Philadelphia's win probability 1\n * const rows = [{ t: 3600, wp: 1, team: \"PHI\" }];\n * const logos = logoMarkers(rows, { x: \"t\", y: \"wp\", team: \"team\", league: \"nfl\", units: \"probability\" });\n * // in the browser: series.attachPrimitive(logos)\n * typeof logos.paneViews;\n * ```\n */\nexport function logoMarkers(rows: readonly Row[], o: LogoMarkersOptions): LwcSeriesPrimitive {\n const h = checkHeight(o.height ?? 0.1);\n const a = checkAlpha(o.alpha ?? 1);\n const units = checkUnits(o.units);\n if (units !== undefined)\n for (const r of rows) {\n // a missing y is left to markPlacements, which skips the row with one warning (Number(null) is 0, a \"probability\")\n if (r[o.y] === undefined || r[o.y] === null) continue;\n const v = Number(r[o.y]);\n if (units === \"probability\" && !(v >= 0 && v <= 1))\n throw new InputError(\n `a probability is a fraction in [0, 1], not ${r[o.y]} (a percent? divide it by 100)`,\n );\n if (units === \"american\" && !(Math.abs(v) >= 100))\n throw new InputError(`American odds are -100 or less or +100 or more, not ${r[o.y]} (decimal odds?)`);\n }\n const ps = markPlacements(rows, \"logo\", o); // validates the rows (and warns on a team with no logo) now\n const images = new Map<string, HTMLImageElement | null>(); // null: it failed to load\n let at: LwcAttached | undefined;\n const load = (url: string): void => {\n if (images.has(url) || typeof Image === \"undefined\") return;\n const img = new Image();\n img.crossOrigin = \"anonymous\"; // keeps the canvas exportable\n images.set(url, img);\n img.onload = () => at?.requestUpdate();\n img.onerror = () => {\n images.set(url, null);\n warn(`lightweight-charts:image:${url}`, `could not load ${url}, so it is not drawn`);\n };\n img.src = url;\n };\n const draw = (target: LwcRenderTarget): void => {\n const c = at;\n if (c === undefined) return;\n target.useMediaCoordinateSpace(({ context: ctx, mediaSize }) => {\n const out: DrawnMark[] = [];\n const ph = h * mediaSize.height;\n ctx.save();\n ctx.globalAlpha = a;\n for (const p of ps) {\n const img = images.get(p.url);\n if (!img?.complete || img.naturalWidth === 0) continue;\n const cx = c.chart.timeScale().timeToCoordinate(p.x as LwcTime);\n const cy = c.series.priceToCoordinate(Number(p.y));\n if (cx === null || cy === null) continue;\n if (!(cx >= 0 && cx <= mediaSize.width && cy >= 0 && cy <= mediaSize.height)) continue; // also drops NaN\n const pw = ph * aspect(p);\n ctx.drawImage(img, cx - pw / 2, cy - ph / 2, pw, ph);\n out.push([p.id, p.x, p.y, h, p.url]);\n }\n ctx.restore();\n drawn.set(primitive, out);\n });\n };\n const views = [{ renderer: () => ({ draw }) }] as const; // the same array every time, as the library asks\n const primitive: LwcSeriesPrimitive = {\n attached(param) {\n at = param;\n for (const p of ps) load(p.url);\n param.requestUpdate(); // logos loaded before (attached again) draw now, not at the next unrelated redraw\n },\n detached() {\n at = undefined;\n drawn.delete(primitive);\n },\n updateAllViews() {},\n paneViews: () => views,\n };\n return primitive;\n}\n\n/** Test hook: [teamId, x, y, height, url] per logo the primitive last drew, `height` a fraction of the pane. */\nexport function drawnMarks(primitive: LwcSeriesPrimitive): DrawnMark[] {\n return [...(drawn.get(primitive) ?? [])];\n}\n"]}