@sportsdataverse/sdvplot 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE.md +94 -0
- package/README.md +908 -2
- package/dist/_web-D7Aa5Rd8.d.ts +69 -0
- package/dist/aaf-GJ3SH34T.js +13 -0
- package/dist/aaf-HCYN5T6Y.js +37 -0
- package/dist/aaf-ZAE5KS2Y.js +13 -0
- package/dist/ahl-E66MDVCU.js +66 -0
- package/dist/ahl-NPQQWZEV.js +249 -0
- package/dist/ahl-QRDU73XX.js +309 -0
- package/dist/axis-CraWQRUJ.d.ts +49 -0
- package/dist/bins.d.ts +174 -0
- package/dist/bins.js +4 -0
- package/dist/bins.js.map +1 -0
- package/dist/cfb-F42SVS4X.js +5000 -0
- package/dist/cfb-P3SHEYRZ.js +692 -0
- package/dist/cfb-WXRZANLP.js +10226 -0
- package/dist/chartjs-surface.d.ts +42 -0
- package/dist/chartjs-surface.js +65 -0
- package/dist/chartjs-surface.js.map +1 -0
- package/dist/chartjs.d.ts +132 -0
- package/dist/chartjs.js +339 -0
- package/dist/chartjs.js.map +1 -0
- package/dist/chunk-4C3GX6QO.js +92 -0
- package/dist/chunk-4C3GX6QO.js.map +1 -0
- package/dist/chunk-C2CDY5OG.js +137 -0
- package/dist/chunk-C2CDY5OG.js.map +1 -0
- package/dist/chunk-E7G7VSLV.js +43 -0
- package/dist/chunk-E7G7VSLV.js.map +1 -0
- package/dist/chunk-J6R2LEJ7.js +58 -0
- package/dist/chunk-J6R2LEJ7.js.map +1 -0
- package/dist/chunk-JJFTD4VB.js +175 -0
- package/dist/chunk-JJFTD4VB.js.map +1 -0
- package/dist/chunk-KKH6ABMA.js +223 -0
- package/dist/chunk-KKH6ABMA.js.map +1 -0
- package/dist/chunk-NLPH2VZT.js +127 -0
- package/dist/chunk-NLPH2VZT.js.map +1 -0
- package/dist/chunk-OKEE4AH6.js +30 -0
- package/dist/chunk-OKEE4AH6.js.map +1 -0
- package/dist/chunk-QY65DUEF.js +306 -0
- package/dist/chunk-QY65DUEF.js.map +1 -0
- package/dist/chunk-RMT2GMFH.js +97 -0
- package/dist/chunk-RMT2GMFH.js.map +1 -0
- package/dist/chunk-SEBO35DV.js +76 -0
- package/dist/chunk-SEBO35DV.js.map +1 -0
- package/dist/chunk-TAXO3VXH.js +556 -0
- package/dist/chunk-TAXO3VXH.js.map +1 -0
- package/dist/chunk-TDNBAEQB.js +105 -0
- package/dist/chunk-TDNBAEQB.js.map +1 -0
- package/dist/cricket-KPCYG2HL.js +493 -0
- package/dist/cricket-NDXLE25B.js +116 -0
- package/dist/cricket-YUUF7P6M.js +160 -0
- package/dist/d3.d.ts +234 -0
- package/dist/d3.js +198 -0
- package/dist/d3.js.map +1 -0
- package/dist/echarts.d.ts +121 -0
- package/dist/echarts.js +228 -0
- package/dist/echarts.js.map +1 -0
- package/dist/echl-3CR4RGFC.js +241 -0
- package/dist/echl-NPC32C23.js +107 -0
- package/dist/echl-PI33DGJC.js +64 -0
- package/dist/export.d.ts +130 -0
- package/dist/export.js +258 -0
- package/dist/export.js.map +1 -0
- package/dist/headshots-B2J_LOO8.d.ts +40 -0
- package/dist/index.d.ts +182 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/interact.d.ts +567 -0
- package/dist/interact.js +809 -0
- package/dist/interact.js.map +1 -0
- package/dist/marks-DH-tQIP-.d.ts +75 -0
- package/dist/mbb-3TRLVW3H.js +3619 -0
- package/dist/mbb-DQUHCHZP.js +5111 -0
- package/dist/mbb-PRAIO2P5.js +371 -0
- package/dist/milb-6IQ6NCEW.js +291 -0
- package/dist/milb-EYY5MD4T.js +1752 -0
- package/dist/milb-EYZ4SZLI.js +797 -0
- package/dist/mlb-4PBUJSVQ.js +35 -0
- package/dist/mlb-FHPY3GQJ.js +701 -0
- package/dist/mlb-REVGL3N2.js +665 -0
- package/dist/nba-ATT775GS.js +485 -0
- package/dist/nba-LKM4UHVU.js +482 -0
- package/dist/nba-YX7PXM3R.js +35 -0
- package/dist/nbagl-3WGZCZ4S.js +37 -0
- package/dist/nbagl-LP4XJRAL.js +229 -0
- package/dist/nbagl-VTTOU7LN.js +499 -0
- package/dist/ncaa_baseball-UV75LNVR.js +2670 -0
- package/dist/ncaa_baseball-VD5J5C72.js +419 -0
- package/dist/ncaa_baseball-YRBS32D6.js +4592 -0
- package/dist/ncaa_mhockey-IMYHS7QS.js +1033 -0
- package/dist/ncaa_mhockey-TXTKWHE3.js +115 -0
- package/dist/ncaa_mhockey-U3MFW5VT.js +663 -0
- package/dist/ncaa_softball-6WR6KS64.js +420 -0
- package/dist/ncaa_softball-RZCTIE56.js +2654 -0
- package/dist/ncaa_softball-VOD4PVGY.js +4599 -0
- package/dist/ncaa_whockey-6XXGUTBK.js +607 -0
- package/dist/ncaa_whockey-AKKYKY67.js +53 -0
- package/dist/ncaa_whockey-KBEKXFBB.js +293 -0
- package/dist/nfl-BDB2HYAR.js +625 -0
- package/dist/nfl-EHYHDLJD.js +37 -0
- package/dist/nfl-VF4NJCHY.js +514 -0
- package/dist/nfl_gsis-UY64JO37.js +4 -0
- package/dist/nhl-6WPHME7H.js +511 -0
- package/dist/nhl-EMIJFZHY.js +884 -0
- package/dist/nhl-ISFUKRFU.js +37 -0
- package/dist/ohl-2FEM554G.js +113 -0
- package/dist/ohl-C5Q4W6JB.js +32 -0
- package/dist/ohl-TQ4J46VC.js +66 -0
- package/dist/phf-H7POU2KW.js +32 -0
- package/dist/phf-K5IFMZUM.js +13 -0
- package/dist/phf-TWJI3BOW.js +20 -0
- package/dist/plot.d.ts +449 -0
- package/dist/plot.js +711 -0
- package/dist/plot.js.map +1 -0
- package/dist/plotly.d.ts +129 -0
- package/dist/plotly.js +360 -0
- package/dist/plotly.js.map +1 -0
- package/dist/pwhl-OX3BO4JI.js +53 -0
- package/dist/pwhl-W44OC3C6.js +17 -0
- package/dist/pwhl-ZJGG3H7W.js +23 -0
- package/dist/qmjhl-5HISEHLL.js +35 -0
- package/dist/qmjhl-DA3UHKE5.js +125 -0
- package/dist/qmjhl-YG4BOQPF.js +614 -0
- package/dist/react.d.ts +101 -0
- package/dist/react.js +164 -0
- package/dist/react.js.map +1 -0
- package/dist/resolve-CQW-jzvr.d.ts +28 -0
- package/dist/selection-D4T3PkRt.d.ts +140 -0
- package/dist/shots.d.ts +364 -0
- package/dist/shots.js +85 -0
- package/dist/shots.js.map +1 -0
- package/dist/soccer-7BH3ZLVL.js +7898 -0
- package/dist/soccer-MRU25OGE.js +2636 -0
- package/dist/soccer-PF4IPPVA.js +12342 -0
- package/dist/surfaces-ChaD9nei.d.ts +38 -0
- package/dist/testing.d.ts +75 -0
- package/dist/testing.js +240 -0
- package/dist/testing.js.map +1 -0
- package/dist/tiers-B6pUBPcE.d.ts +93 -0
- package/dist/types-C7n6gJF3.d.ts +62 -0
- package/dist/ufl-7VLZTLTT.js +143 -0
- package/dist/ufl-CCMBLPSW.js +100 -0
- package/dist/ufl-F2BJRXME.js +16 -0
- package/dist/usfl-4CR5RMZJ.js +32 -0
- package/dist/usfl-LD7HP2YK.js +14 -0
- package/dist/usfl-VU4YSJQD.js +19 -0
- package/dist/ushl-CPG2LB4L.js +73 -0
- package/dist/ushl-PQL6HJAD.js +88 -0
- package/dist/ushl-ZT2ATPHK.js +22 -0
- package/dist/vega.d.ts +85 -0
- package/dist/vega.js +362 -0
- package/dist/vega.js.map +1 -0
- package/dist/wbb-3KHCDYTK.js +5087 -0
- package/dist/wbb-BMX53Q3Q.js +369 -0
- package/dist/wbb-QNRN2CJJ.js +3620 -0
- package/dist/whl-4ATYZWUB.js +97 -0
- package/dist/whl-RVFPL6MH.js +79 -0
- package/dist/whl-Y7ASKYMM.js +28 -0
- package/dist/wnba-FWX5SFVI.js +232 -0
- package/dist/wnba-GX6HZR4B.js +220 -0
- package/dist/wnba-NTMWIPP6.js +20 -0
- package/dist/xfl-43R2JMJK.js +30 -0
- package/dist/xfl-H3BXQPZZ.js +16 -0
- package/dist/xfl-XON7A7FI.js +69 -0
- package/package.json +154 -3
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Scene } from '@sportsdataverse/sporty';
|
|
2
|
+
import { Plugin } from 'chart.js';
|
|
3
|
+
import { S as SurfaceSceneOptions } from './surfaces-ChaD9nei.js';
|
|
4
|
+
import { L as League } from './types-C7n6gJF3.js';
|
|
5
|
+
import './resolve-CQW-jzvr.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Chart.js 4 court / field / rink background: `surface(league, o)` paints a team-painted sporty scene under the
|
|
9
|
+
* datasets. Split from `sdvplot/chartjs` because it draws through `@sportsdataverse/sporty/canvas`, which the logo and
|
|
10
|
+
* colour plugins do not need. sporty is an optional peer, but this entry imports it when it loads, so install it to use
|
|
11
|
+
* `sdvplot/chartjs/surface`. `chart.js` is a type-only import (optional peer, 4.4 or later).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface ChartSurfaceOptions extends SurfaceSceneOptions {
|
|
15
|
+
/** The linear x scale's id (default `"x"`). */
|
|
16
|
+
xAxisID?: string;
|
|
17
|
+
/** The linear y scale's id (default `"y"`). */
|
|
18
|
+
yAxisID?: string;
|
|
19
|
+
}
|
|
20
|
+
interface LinearScale {
|
|
21
|
+
type: "linear";
|
|
22
|
+
min: number;
|
|
23
|
+
max: number;
|
|
24
|
+
}
|
|
25
|
+
interface ChartSurface {
|
|
26
|
+
/** Paints the surface before the datasets (`beforeDatasetsDraw`), clipped to the chart area. */
|
|
27
|
+
plugin: Plugin;
|
|
28
|
+
/** Linear scales spanning the surface, for `options.scales` (keep both axes linear). */
|
|
29
|
+
scales: {
|
|
30
|
+
x: LinearScale;
|
|
31
|
+
y: LinearScale;
|
|
32
|
+
};
|
|
33
|
+
scene: Scene;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A team-painted sporty surface as a Chart.js background: a shot chart is a scatter (a hexbin, a bubble chart) over a
|
|
37
|
+
* court. Throws `InputError` for a league without a surface, or for a scene whose bbox is empty (an `xlim` or `ylim` of
|
|
38
|
+
* zero width) here, when it is built, not inside a Chart.js draw. A non-linear axis is left unpainted, with one warning.
|
|
39
|
+
*/
|
|
40
|
+
declare function surface(league: League, o?: ChartSurfaceOptions): ChartSurface;
|
|
41
|
+
|
|
42
|
+
export { type ChartSurface, type ChartSurfaceOptions, type LinearScale, surface };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { surfaceScene } from './chunk-TDNBAEQB.js';
|
|
2
|
+
import './chunk-J6R2LEJ7.js';
|
|
3
|
+
import './chunk-TAXO3VXH.js';
|
|
4
|
+
import { InputError, warn } from './chunk-E7G7VSLV.js';
|
|
5
|
+
import { drawScene } from '@sportsdataverse/sporty/canvas';
|
|
6
|
+
|
|
7
|
+
function surface(league, o = {}) {
|
|
8
|
+
const { xAxisID = "x", yAxisID = "y", ...rest } = o;
|
|
9
|
+
const scene = surfaceScene(league, rest);
|
|
10
|
+
const [x0, y0, x1, y1] = scene.bbox;
|
|
11
|
+
if (!(x1 > x0 && y1 > y0 && Number.isFinite(x1 - x0 + y1 - y0)))
|
|
12
|
+
throw new InputError(
|
|
13
|
+
`surface("${league}"): the scene's bbox [${scene.bbox.join(", ")}] is empty, so there is nothing to draw (check xlim / ylim)`
|
|
14
|
+
);
|
|
15
|
+
return {
|
|
16
|
+
plugin: {
|
|
17
|
+
id: "sdvplotSurface",
|
|
18
|
+
// Chart.js has no plugin priority: inline plugins run in array order, so a watermark listed first is painted over
|
|
19
|
+
start: (chart) => {
|
|
20
|
+
const ps = chart.config.plugins ?? [];
|
|
21
|
+
const own = ps.findIndex((p) => p.id === "sdvplotSurface");
|
|
22
|
+
const wm = ps.findIndex((p) => p.id === "sdvplotLogoWatermarks");
|
|
23
|
+
if (wm !== -1 && (own === -1 || wm < own))
|
|
24
|
+
warn(
|
|
25
|
+
"chartjs:surface:order",
|
|
26
|
+
"logoWatermarks is listed before the surface plugin, so the court is painted over it; list court.plugin first"
|
|
27
|
+
);
|
|
28
|
+
},
|
|
29
|
+
beforeDatasetsDraw: (chart) => paintScene(chart, scene, xAxisID, yAxisID)
|
|
30
|
+
},
|
|
31
|
+
scales: { x: { type: "linear", min: x0, max: x1 }, y: { type: "linear", min: y0, max: y1 } },
|
|
32
|
+
scene
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function paintScene(chart, scene, xId, yId) {
|
|
36
|
+
const { ctx, chartArea: a } = chart;
|
|
37
|
+
const xs = chart.scales[xId];
|
|
38
|
+
const ys = chart.scales[yId];
|
|
39
|
+
if (!a || xs === void 0 || ys === void 0) return;
|
|
40
|
+
if (xs.type !== "linear" || ys.type !== "linear") {
|
|
41
|
+
warn(
|
|
42
|
+
`chartjs:surface:${xId}:${yId}`,
|
|
43
|
+
`surface needs linear scales (pass court.scales); ${xId} is ${xs.type} and ${yId} is ${ys.type}, so it is not drawn`
|
|
44
|
+
);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const ox = xs.getPixelForValue(0);
|
|
48
|
+
const oy = ys.getPixelForValue(0);
|
|
49
|
+
const kx = xs.getPixelForValue(1) - ox;
|
|
50
|
+
const ky = ys.getPixelForValue(1) - oy;
|
|
51
|
+
const s = Math.abs(kx);
|
|
52
|
+
if (!(s > 0 && Number.isFinite(s + ky))) return;
|
|
53
|
+
const [x0, , , y1] = scene.bbox;
|
|
54
|
+
ctx.save();
|
|
55
|
+
ctx.beginPath();
|
|
56
|
+
ctx.rect(a.left, a.top, a.right - a.left, a.bottom - a.top);
|
|
57
|
+
ctx.clip();
|
|
58
|
+
ctx.transform(kx / s, 0, 0, -ky / s, ox + kx * x0, oy + ky * y1);
|
|
59
|
+
drawScene(ctx, scene, { scale: s });
|
|
60
|
+
ctx.restore();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { surface };
|
|
64
|
+
//# sourceMappingURL=chartjs-surface.js.map
|
|
65
|
+
//# sourceMappingURL=chartjs-surface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/chartjs-surface.ts"],"names":[],"mappings":"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","file":"chartjs-surface.js","sourcesContent":["/**\n * Chart.js 4 court / field / rink background: `surface(league, o)` paints a team-painted sporty scene under the\n * datasets. Split from `sdvplot/chartjs` because it draws through `@sportsdataverse/sporty/canvas`, which the logo and\n * colour plugins do not need. sporty is an optional peer, but this entry imports it when it loads, so install it to use\n * `sdvplot/chartjs/surface`. `chart.js` is a type-only import (optional peer, 4.4 or later).\n */\nimport type { Scene } from \"@sportsdataverse/sporty\";\nimport { drawScene } from \"@sportsdataverse/sporty/canvas\";\nimport type { Chart, Plugin } from \"chart.js\";\nimport { InputError, warn } from \"./errors.js\";\nimport { type SurfaceSceneOptions, surfaceScene } from \"./surfaces.js\";\nimport type { League } from \"./types.js\";\n\nexport interface ChartSurfaceOptions extends SurfaceSceneOptions {\n /** The linear x scale's id (default `\"x\"`). */\n xAxisID?: string;\n /** The linear y scale's id (default `\"y\"`). */\n yAxisID?: string;\n}\nexport interface LinearScale {\n type: \"linear\";\n min: number;\n max: number;\n}\nexport interface ChartSurface {\n /** Paints the surface before the datasets (`beforeDatasetsDraw`), clipped to the chart area. */\n plugin: Plugin;\n /** Linear scales spanning the surface, for `options.scales` (keep both axes linear). */\n scales: { x: LinearScale; y: LinearScale };\n scene: Scene;\n}\n/**\n * A team-painted sporty surface as a Chart.js background: a shot chart is a scatter (a hexbin, a bubble chart) over a\n * court. Throws `InputError` for a league without a surface, or for a scene whose bbox is empty (an `xlim` or `ylim` of\n * zero width) here, when it is built, not inside a Chart.js draw. A non-linear axis is left unpainted, with one warning.\n */\nexport function surface(league: League, o: ChartSurfaceOptions = {}): ChartSurface {\n const { xAxisID = \"x\", yAxisID = \"y\", ...rest } = o;\n const scene = surfaceScene(league, rest);\n const [x0, y0, x1, y1] = scene.bbox;\n // drawScene would throw sporty's InputError on the first draw, inside Chart.js's render loop\n if (!(x1 > x0 && y1 > y0 && Number.isFinite(x1 - x0 + y1 - y0)))\n throw new InputError(\n `surface(\"${league}\"): the scene's bbox [${scene.bbox.join(\", \")}] is empty, so there is nothing to draw (check xlim / ylim)`,\n );\n return {\n plugin: {\n id: \"sdvplotSurface\",\n // Chart.js has no plugin priority: inline plugins run in array order, so a watermark listed first is painted over\n start: (chart) => {\n const ps = chart.config.plugins ?? [];\n const own = ps.findIndex((p) => p.id === \"sdvplotSurface\");\n const wm = ps.findIndex((p) => p.id === \"sdvplotLogoWatermarks\");\n if (wm !== -1 && (own === -1 || wm < own))\n warn(\n \"chartjs:surface:order\",\n \"logoWatermarks is listed before the surface plugin, so the court is painted over it; list court.plugin first\",\n );\n },\n beforeDatasetsDraw: (chart) => paintScene(chart, scene, xAxisID, yAxisID),\n },\n scales: { x: { type: \"linear\", min: x0, max: x1 }, y: { type: \"linear\", min: y0, max: y1 } },\n scene,\n };\n}\nfunction paintScene(chart: Chart, scene: Scene, xId: string, yId: string): void {\n const { ctx, chartArea: a } = chart;\n const xs = chart.scales[xId];\n const ys = chart.scales[yId];\n if (!a || xs === undefined || ys === undefined) return; // a draw before the first layout\n if (xs.type !== \"linear\" || ys.type !== \"linear\") {\n warn(\n `chartjs:surface:${xId}:${yId}`,\n `surface needs linear scales (pass court.scales); ${xId} is ${xs.type} and ${yId} is ${ys.type}, so it is not drawn`,\n );\n return;\n }\n const ox = xs.getPixelForValue(0);\n const oy = ys.getPixelForValue(0);\n const kx = xs.getPixelForValue(1) - ox; // px per scene unit; negative on a reversed axis\n const ky = ys.getPixelForValue(1) - oy; // negative: y points up\n const s = Math.abs(kx);\n if (!(s > 0 && Number.isFinite(s + ky))) return; // a zero-size chart area\n const [x0, , , y1] = scene.bbox;\n ctx.save();\n ctx.beginPath();\n ctx.rect(a.left, a.top, a.right - a.left, a.bottom - a.top);\n ctx.clip();\n // drawScene paints (x, y) at (s(x - x0), s(y1 - y)) and composes with the current transform (the chart's own\n // device-pixel-ratio transform stays under both): this map turns that into the chart's scales, axis by axis.\n ctx.transform(kx / s, 0, 0, -ky / s, ox + kx * x0, oy + ky * y1);\n drawScene(ctx, scene, { scale: s }); // strokes are 1/s scene units: one px wide\n ctx.restore();\n}\n"]}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { PointStyle, Plugin } from 'chart.js';
|
|
2
|
+
import { E as EspnHeadshotLeague } from './headshots-B2J_LOO8.js';
|
|
3
|
+
import { V as Value } from './resolve-CQW-jzvr.js';
|
|
4
|
+
import { L as League, S as SeasonInput, V as Variant, I as IdSystem, H as HeadshotIdSystem, W as Which } from './types-C7n6gJF3.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Chart.js 4 helpers: team logo / wordmark / headshot point styles, axis-tick logos, faint logo watermarks, team
|
|
8
|
+
* colours and a sporty surface background. `chart.js` is a type-only import (optional peer, 4.4 or later): everything
|
|
9
|
+
* here is a dataset option or a plugin object, so Svelte, Astro, React or plain scripts pass it to `new Chart(...)`
|
|
10
|
+
* unchanged.
|
|
11
|
+
* The sporty court background lives in `sdvplot/chartjs/surface`, so this entry never needs `@sportsdataverse/sporty`.
|
|
12
|
+
* Call after `loadLeague(league)` / `preloadAll()`; point styles and plugins need a DOM (build them client-side) unless
|
|
13
|
+
* given `loadImage`, which in Node is `@napi-rs/canvas`'s.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
interface PointOptions {
|
|
17
|
+
league: League;
|
|
18
|
+
/** Half the image height in px; also the dataset's `pointRadius` (hit area). Default 12. */
|
|
19
|
+
radius?: number;
|
|
20
|
+
season?: SeasonInput | readonly SeasonInput[];
|
|
21
|
+
variant?: Variant;
|
|
22
|
+
idSystem?: IdSystem;
|
|
23
|
+
/** What an unresolved value draws: `"text"` (its own label, the default) or any Chart.js point style. */
|
|
24
|
+
fallback?: PointStyle | "text";
|
|
25
|
+
/** The chart's background colour, so the text fallback reads on it (default white, or black with `variant: "dark"`). */
|
|
26
|
+
background?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Loads each image instead of a DOM image element, so this also runs in Node: pass `@napi-rs/canvas`'s `loadImage`.
|
|
29
|
+
* See {@link ImageLike}.
|
|
30
|
+
*/
|
|
31
|
+
loadImage?: (url: string) => Promise<ImageLike>;
|
|
32
|
+
}
|
|
33
|
+
interface HeadshotPointOptions {
|
|
34
|
+
league: EspnHeadshotLeague;
|
|
35
|
+
radius?: number;
|
|
36
|
+
idSystem?: HeadshotIdSystem;
|
|
37
|
+
fallback?: PointStyle | "text";
|
|
38
|
+
/** The chart's background colour, so the text fallback reads on it (default white). */
|
|
39
|
+
background?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Loads each image instead of a DOM image element, so this also runs in Node: pass `@napi-rs/canvas`'s `loadImage`.
|
|
42
|
+
* See {@link ImageLike}.
|
|
43
|
+
*/
|
|
44
|
+
loadImage?: (url: string) => Promise<ImageLike>;
|
|
45
|
+
}
|
|
46
|
+
/** Spread into a scatter / bubble / line dataset: one style per data point, in data order. */
|
|
47
|
+
interface PointStyles {
|
|
48
|
+
pointStyle: PointStyle[];
|
|
49
|
+
pointRadius: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* An image a `loadImage` option resolves: anything the chart's 2D context can `drawImage`, such as `@napi-rs/canvas`'s
|
|
53
|
+
* `Image` in Node or an `HTMLImageElement` in the browser. `loadImage` is called once per URL and drawn size, and the
|
|
54
|
+
* image's `width` and `height` are then set to that size (Chart.js draws an image point style at its own size), so
|
|
55
|
+
* resolve a new image on every call, as `@napi-rs/canvas`'s `loadImage` does.
|
|
56
|
+
*/
|
|
57
|
+
interface ImageLike {
|
|
58
|
+
readonly width: number;
|
|
59
|
+
readonly height: number;
|
|
60
|
+
}
|
|
61
|
+
/** Team logos as point styles: `{ data, ...logoPoints(teams, { league: "nfl", radius: 12 }) }`. Add `pointImages` to `plugins`. */
|
|
62
|
+
declare function logoPoints(teams: readonly Value[], o: PointOptions): PointStyles;
|
|
63
|
+
/** As `logoPoints`, with wordmarks. */
|
|
64
|
+
declare function wordmarkPoints(teams: readonly Value[], o: PointOptions): PointStyles;
|
|
65
|
+
/** ESPN player headshots as point styles; `idSystem: "gsis"` needs `loadGsis()` first. */
|
|
66
|
+
declare function headshotPoints(players: readonly Value[], o: HeadshotPointOptions): PointStyles;
|
|
67
|
+
/** Redraws the chart as each image point style finishes loading (Chart.js does not watch them). */
|
|
68
|
+
declare const pointImages: Plugin;
|
|
69
|
+
interface TeamColorOptions {
|
|
70
|
+
which?: Which;
|
|
71
|
+
season?: SeasonInput;
|
|
72
|
+
idSystem?: IdSystem;
|
|
73
|
+
/** Opacity in [0, 1]; below 1 the colour is written `rgba(r, g, b, a)`. */
|
|
74
|
+
alpha?: number;
|
|
75
|
+
/** The colour of an unresolved team (default `#808080`). */
|
|
76
|
+
fallback?: string;
|
|
77
|
+
/** Return black or white, whichever reads on the team colour (labels, point borders). */
|
|
78
|
+
onColor?: boolean;
|
|
79
|
+
}
|
|
80
|
+
type ColorsFor<T> = T extends readonly Value[] ? string[] : string;
|
|
81
|
+
/** Team colour(s) for `backgroundColor` / `borderColor`: one team gives one string, an array gives one per team. */
|
|
82
|
+
declare function teamColor<T extends Value | readonly Value[]>(teams: T, league: League, o?: TeamColorOptions): ColorsFor<T>;
|
|
83
|
+
/** `teamColor` at 0.2 opacity by default: the fill under a radar or area dataset. */
|
|
84
|
+
declare function teamFill<T extends Value | readonly Value[]>(teams: T, league: League, o?: TeamColorOptions): ColorsFor<T>;
|
|
85
|
+
interface AxisLogoOptions {
|
|
86
|
+
league: League;
|
|
87
|
+
/** Image height in px (default 24); the width follows the mark's aspect. */
|
|
88
|
+
size?: number;
|
|
89
|
+
season?: SeasonInput;
|
|
90
|
+
variant?: Variant;
|
|
91
|
+
markType?: "logo" | "wordmark";
|
|
92
|
+
idSystem?: IdSystem;
|
|
93
|
+
/** The category scale's id (default: the axis letter). */
|
|
94
|
+
scaleId?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Loads each image instead of a DOM image element, so this also runs in Node: pass `@napi-rs/canvas`'s `loadImage`.
|
|
97
|
+
* See {@link ImageLike}.
|
|
98
|
+
*/
|
|
99
|
+
loadImage?: (url: string) => Promise<ImageLike>;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Swap a category axis's tick labels for team logos: resolved labels are painted as images (after the datasets, under
|
|
103
|
+
* the tooltip) and their text is blanked; unresolved labels keep their text, with one warning per distinct label set
|
|
104
|
+
* (J28). Any other scale type is left as it is, with one warning.
|
|
105
|
+
*/
|
|
106
|
+
declare function axisLogos(axis: "x" | "y", o: AxisLogoOptions): Plugin;
|
|
107
|
+
interface WatermarkOptions {
|
|
108
|
+
league: League;
|
|
109
|
+
/** Image height in px (default 75, Game on Paper's win-probability chart); the width follows the mark's aspect. */
|
|
110
|
+
size?: number;
|
|
111
|
+
/** Opacity of the watermarks (default 0.4). */
|
|
112
|
+
alpha?: number;
|
|
113
|
+
/** Gap in px from the chart area's left edge and from its top / bottom (default 8). */
|
|
114
|
+
inset?: number;
|
|
115
|
+
season?: SeasonInput | readonly SeasonInput[];
|
|
116
|
+
variant?: Variant;
|
|
117
|
+
idSystem?: IdSystem;
|
|
118
|
+
/**
|
|
119
|
+
* Loads each image instead of a DOM image element, so this also runs in Node: pass `@napi-rs/canvas`'s `loadImage`.
|
|
120
|
+
* See {@link ImageLike}.
|
|
121
|
+
*/
|
|
122
|
+
loadImage?: (url: string) => Promise<ImageLike>;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Faint team logos behind the datasets (Game on Paper's win-probability chart): the first team sits top-left of the
|
|
126
|
+
* chart area, the last bottom-left, any between spaced evenly down the left edge, so pass `[home, away]` under a home
|
|
127
|
+
* win-probability line. Unknown teams are skipped with one warning per call (J28); `variant: "dark"` falls back to a
|
|
128
|
+
* light mark by polarity, so no `onerror` retry is needed.
|
|
129
|
+
*/
|
|
130
|
+
declare function logoWatermarks(teams: readonly Value[], o: WatermarkOptions): Plugin;
|
|
131
|
+
|
|
132
|
+
export { type AxisLogoOptions, type ColorsFor, type HeadshotPointOptions, type ImageLike, type PointOptions, type PointStyles, type TeamColorOptions, type WatermarkOptions, axisLogos, headshotPoints, logoPoints, logoWatermarks, pointImages, teamColor, teamFill, wordmarkPoints };
|
package/dist/chartjs.js
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import { checkAlpha, placeSync } from './chunk-RMT2GMFH.js';
|
|
2
|
+
import './chunk-KKH6ABMA.js';
|
|
3
|
+
import { hex6, onColor, contrast } from './chunk-J6R2LEJ7.js';
|
|
4
|
+
import { teamColorsSync } from './chunk-TAXO3VXH.js';
|
|
5
|
+
import { InputError, warn, UnsupportedTargetError } from './chunk-E7G7VSLV.js';
|
|
6
|
+
|
|
7
|
+
// src/chartjs.ts
|
|
8
|
+
function needDom(what) {
|
|
9
|
+
if (typeof document === "undefined")
|
|
10
|
+
throw new UnsupportedTargetError(
|
|
11
|
+
`${what} needs a DOM: build it in the browser (onMount, $effect, an Astro client:only island), not during SSR`
|
|
12
|
+
);
|
|
13
|
+
return document;
|
|
14
|
+
}
|
|
15
|
+
function checkSize(size, what) {
|
|
16
|
+
if (!(Number.isFinite(size) && size > 0))
|
|
17
|
+
throw new InputError(`${what} is an image height in px > 0, got ${size}`);
|
|
18
|
+
return size;
|
|
19
|
+
}
|
|
20
|
+
var images = /* @__PURE__ */ new Map();
|
|
21
|
+
function image(url, h, aspect) {
|
|
22
|
+
const w = Math.round(h * aspect);
|
|
23
|
+
const key = `${w}x${h} ${url}`;
|
|
24
|
+
let img = images.get(key);
|
|
25
|
+
if (img === void 0) {
|
|
26
|
+
img = needDom("an image point style").createElement("img");
|
|
27
|
+
img.width = w;
|
|
28
|
+
img.height = h;
|
|
29
|
+
img.crossOrigin = "anonymous";
|
|
30
|
+
img.src = url;
|
|
31
|
+
images.set(key, img);
|
|
32
|
+
}
|
|
33
|
+
return img;
|
|
34
|
+
}
|
|
35
|
+
var Loading = class {
|
|
36
|
+
constructor(width, height) {
|
|
37
|
+
this.width = width;
|
|
38
|
+
this.height = height;
|
|
39
|
+
}
|
|
40
|
+
width;
|
|
41
|
+
height;
|
|
42
|
+
img = void 0;
|
|
43
|
+
/** Point-style slots to fill, then charts to redraw, all in the first reaction to the loader's promise. */
|
|
44
|
+
slots = [];
|
|
45
|
+
on = /* @__PURE__ */ new Set();
|
|
46
|
+
};
|
|
47
|
+
var sizes = /* @__PURE__ */ new WeakMap();
|
|
48
|
+
var DRAWN = ["[object HTMLImageElement]", "[object HTMLCanvasElement]"];
|
|
49
|
+
function sized(img, w, h) {
|
|
50
|
+
const was = sizes.get(img);
|
|
51
|
+
if (was !== void 0 && (was.width !== w || was.height !== h)) {
|
|
52
|
+
warn(
|
|
53
|
+
"chartjs:loadImage:shared",
|
|
54
|
+
"loadImage returned an image already sized for another drawn size, so that use is not drawn: loadImage must return a fresh image per call (wrap a cache so each call returns a new image)"
|
|
55
|
+
);
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const own = (value) => ({ value, writable: true, configurable: true });
|
|
59
|
+
const tag = DRAWN.includes(String(img)) ? {} : { toString: own(() => DRAWN[0]) };
|
|
60
|
+
try {
|
|
61
|
+
Object.defineProperties(img, { width: own(w), height: own(h), ...tag });
|
|
62
|
+
} catch (e) {
|
|
63
|
+
warn(
|
|
64
|
+
"chartjs:loadImage:unsizable",
|
|
65
|
+
`loadImage returned an image that cannot be sized (${String(e)}), so it is not drawn`
|
|
66
|
+
);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
sizes.set(img, { width: w, height: h });
|
|
70
|
+
return img;
|
|
71
|
+
}
|
|
72
|
+
var loads = /* @__PURE__ */ new WeakMap();
|
|
73
|
+
function loading(load, url, h, aspect) {
|
|
74
|
+
const w = Math.round(h * aspect);
|
|
75
|
+
const key = `${w}x${h} ${url}`;
|
|
76
|
+
const byKey = loads.get(load) ?? /* @__PURE__ */ new Map();
|
|
77
|
+
loads.set(load, byKey);
|
|
78
|
+
let m = byKey.get(key);
|
|
79
|
+
if (m === void 0) {
|
|
80
|
+
const it = new Loading(w, h);
|
|
81
|
+
const land = (img) => {
|
|
82
|
+
it.img = img;
|
|
83
|
+
if (img !== null) for (const [styles, i] of it.slots) styles[i] = img;
|
|
84
|
+
it.slots.length = 0;
|
|
85
|
+
const redraws2 = [...it.on];
|
|
86
|
+
it.on.clear();
|
|
87
|
+
for (const f of redraws2) f();
|
|
88
|
+
};
|
|
89
|
+
void load(url).then(
|
|
90
|
+
(img) => land(sized(img, w, h)),
|
|
91
|
+
(e) => {
|
|
92
|
+
warn(`chartjs:loadImage:${url}`, `loadImage failed for ${url} (${String(e)}), so it is not drawn`);
|
|
93
|
+
land(null);
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
m = it;
|
|
97
|
+
byKey.set(key, m);
|
|
98
|
+
}
|
|
99
|
+
return m;
|
|
100
|
+
}
|
|
101
|
+
function textInk(background) {
|
|
102
|
+
return contrast("#555555", background) >= contrast("#e8e6e3", background) ? "#555555" : "#e8e6e3";
|
|
103
|
+
}
|
|
104
|
+
function textStyle(label, h, ink) {
|
|
105
|
+
if (typeof document === "undefined") return "circle";
|
|
106
|
+
const c = document.createElement("canvas");
|
|
107
|
+
const g = c.getContext("2d");
|
|
108
|
+
if (g === null) return "circle";
|
|
109
|
+
const font = `bold ${Math.round(h * 0.45)}px sans-serif`;
|
|
110
|
+
g.font = font;
|
|
111
|
+
c.width = Math.max(h, Math.ceil(g.measureText(label).width) + 4);
|
|
112
|
+
c.height = h;
|
|
113
|
+
g.font = font;
|
|
114
|
+
g.textAlign = "center";
|
|
115
|
+
g.textBaseline = "middle";
|
|
116
|
+
g.fillStyle = ink;
|
|
117
|
+
g.fillText(label, c.width / 2, h / 2);
|
|
118
|
+
return c;
|
|
119
|
+
}
|
|
120
|
+
function imagesFor(values, h, place, load) {
|
|
121
|
+
const out = new Array(values.length);
|
|
122
|
+
const idx = values.map((_, i) => i);
|
|
123
|
+
for (const p of placeSync(idx, idx, values, place))
|
|
124
|
+
out[p.index] = load === void 0 ? image(p.url, h, p.aspect ?? 1) : loading(load, p.url, h, p.aspect ?? 1);
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
var pending = /* @__PURE__ */ new WeakMap();
|
|
128
|
+
function points(values, kind, radius, fallback, background, place, load) {
|
|
129
|
+
if (load === void 0) needDom("point styles");
|
|
130
|
+
const r = radius ?? 12;
|
|
131
|
+
if (!(Number.isFinite(r) && r > 0))
|
|
132
|
+
throw new InputError(`radius is a point radius in px > 0, got ${String(radius)}`);
|
|
133
|
+
const ink = textInk(background ?? (place.variant === "dark" ? "#000000" : "#ffffff"));
|
|
134
|
+
const styles = new Array(values.length);
|
|
135
|
+
const waiting = [];
|
|
136
|
+
imagesFor(values, 2 * r, { ...place, kind }, load).forEach((m, i) => {
|
|
137
|
+
if (m === void 0) return;
|
|
138
|
+
if (!(m instanceof Loading)) {
|
|
139
|
+
styles[i] = m;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
styles[i] = m.img ?? false;
|
|
143
|
+
if (m.img !== void 0) return;
|
|
144
|
+
m.slots.push([styles, i]);
|
|
145
|
+
waiting.push(m);
|
|
146
|
+
});
|
|
147
|
+
if (waiting.length > 0) pending.set(styles, waiting);
|
|
148
|
+
for (let i = 0; i < values.length; i++)
|
|
149
|
+
styles[i] ??= fallback === void 0 || fallback === "text" ? textStyle(String(values[i] ?? ""), 2 * r, ink) : fallback;
|
|
150
|
+
return { pointStyle: styles, pointRadius: r };
|
|
151
|
+
}
|
|
152
|
+
function teamPoints(teams, kind, o) {
|
|
153
|
+
const { league, radius, fallback, background, loadImage, ...rest } = o;
|
|
154
|
+
return points(teams, kind, radius, fallback, background, { league, ...rest }, loadImage);
|
|
155
|
+
}
|
|
156
|
+
function logoPoints(teams, o) {
|
|
157
|
+
return teamPoints(teams, "logo", o);
|
|
158
|
+
}
|
|
159
|
+
function wordmarkPoints(teams, o) {
|
|
160
|
+
return teamPoints(teams, "wordmark", o);
|
|
161
|
+
}
|
|
162
|
+
function headshotPoints(players, o) {
|
|
163
|
+
return points(
|
|
164
|
+
players,
|
|
165
|
+
"headshot",
|
|
166
|
+
o.radius,
|
|
167
|
+
o.fallback,
|
|
168
|
+
o.background,
|
|
169
|
+
{ league: o.league, idSystem: o.idSystem ?? "espn" },
|
|
170
|
+
o.loadImage
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
var redraws = /* @__PURE__ */ new WeakMap();
|
|
174
|
+
var isImage = (v) => Object.prototype.toString.call(v) === "[object HTMLImageElement]";
|
|
175
|
+
function redrawsOf(chart) {
|
|
176
|
+
let r = redraws.get(chart);
|
|
177
|
+
if (r === void 0) {
|
|
178
|
+
r = {
|
|
179
|
+
redraw: () => {
|
|
180
|
+
if (chart.ctx) chart.draw();
|
|
181
|
+
},
|
|
182
|
+
refresh: () => {
|
|
183
|
+
if (chart.ctx) chart.update("none");
|
|
184
|
+
},
|
|
185
|
+
stop: new AbortController()
|
|
186
|
+
};
|
|
187
|
+
redraws.set(chart, r);
|
|
188
|
+
}
|
|
189
|
+
return r;
|
|
190
|
+
}
|
|
191
|
+
function redrawOnLoad(chart, img) {
|
|
192
|
+
if (img.complete) return;
|
|
193
|
+
const r = redrawsOf(chart);
|
|
194
|
+
img.addEventListener("load", r.redraw, { once: true, signal: r.stop.signal });
|
|
195
|
+
}
|
|
196
|
+
function redrawOnLanding(chart, m, how) {
|
|
197
|
+
const r = redrawsOf(chart);
|
|
198
|
+
const f = r[how];
|
|
199
|
+
if (m.on.has(f)) return;
|
|
200
|
+
m.on.add(f);
|
|
201
|
+
r.stop.signal.addEventListener("abort", () => m.on.delete(f), { once: true });
|
|
202
|
+
}
|
|
203
|
+
function paint(chart, m) {
|
|
204
|
+
if (m === void 0) return void 0;
|
|
205
|
+
if (m instanceof Loading) {
|
|
206
|
+
if (m.img === void 0) redrawOnLanding(chart, m, "redraw");
|
|
207
|
+
return m.img ?? void 0;
|
|
208
|
+
}
|
|
209
|
+
if (!m.complete || m.naturalWidth === 0) {
|
|
210
|
+
redrawOnLoad(chart, m);
|
|
211
|
+
return void 0;
|
|
212
|
+
}
|
|
213
|
+
return m;
|
|
214
|
+
}
|
|
215
|
+
function forget(chart) {
|
|
216
|
+
redraws.get(chart)?.stop.abort();
|
|
217
|
+
redraws.delete(chart);
|
|
218
|
+
}
|
|
219
|
+
var pointImages = {
|
|
220
|
+
id: "sdvplotPointImages",
|
|
221
|
+
afterDestroy: forget,
|
|
222
|
+
afterDraw(chart) {
|
|
223
|
+
for (const ds of chart.data.datasets) {
|
|
224
|
+
const s = ds.pointStyle;
|
|
225
|
+
for (const v of Array.isArray(s) ? s : [s]) if (isImage(v)) redrawOnLoad(chart, v);
|
|
226
|
+
for (const m of Array.isArray(s) && pending.get(s) || [])
|
|
227
|
+
if (m.img === void 0) redrawOnLanding(chart, m, "refresh");
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
function teamColor(teams, league, o = {}) {
|
|
232
|
+
const { alpha = 1, fallback = "#808080", onColor: ink = false, ...co } = o;
|
|
233
|
+
const a = checkAlpha(alpha);
|
|
234
|
+
const paint2 = (c) => {
|
|
235
|
+
const hex = hex6(typeof c === "string" ? c : fallback, { dropAlpha: true });
|
|
236
|
+
if (ink) return onColor(hex);
|
|
237
|
+
if (a === 1) return hex;
|
|
238
|
+
const n = Number.parseInt(hex.slice(1), 16);
|
|
239
|
+
return `rgba(${n >> 16}, ${n >> 8 & 255}, ${n & 255}, ${a})`;
|
|
240
|
+
};
|
|
241
|
+
const got = teamColorsSync(league, teams, co);
|
|
242
|
+
return Array.isArray(got) ? got.map(paint2) : paint2(got);
|
|
243
|
+
}
|
|
244
|
+
function teamFill(teams, league, o = {}) {
|
|
245
|
+
return teamColor(teams, league, { alpha: 0.2, ...o });
|
|
246
|
+
}
|
|
247
|
+
function axisLogos(axis, o) {
|
|
248
|
+
if (axis !== "x" && axis !== "y")
|
|
249
|
+
throw new InputError(`axis must be "x" or "y", got ${JSON.stringify(axis)}`);
|
|
250
|
+
const { league, size = 24, markType = "logo", scaleId = axis, loadImage, ...rest } = o;
|
|
251
|
+
checkSize(size, "size");
|
|
252
|
+
const byChart = /* @__PURE__ */ new WeakMap();
|
|
253
|
+
const bases = /* @__PURE__ */ new WeakMap();
|
|
254
|
+
return {
|
|
255
|
+
id: `sdvplotAxisLogos${axis.toUpperCase()}`,
|
|
256
|
+
afterDestroy: forget,
|
|
257
|
+
beforeUpdate(chart) {
|
|
258
|
+
const conf = chart.config;
|
|
259
|
+
const scale = conf.options.scales?.[scaleId];
|
|
260
|
+
if (scale?.type !== "category") {
|
|
261
|
+
byChart.delete(chart);
|
|
262
|
+
warn(
|
|
263
|
+
`chartjs:axisLogos:${scaleId}`,
|
|
264
|
+
`axisLogos needs a category scale; scale ${JSON.stringify(scaleId)} is ${scale?.type ?? "missing"}, so its ticks are left as they are`
|
|
265
|
+
);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const labels = (chart.data.labels ?? []).map(String);
|
|
269
|
+
const m = /* @__PURE__ */ new Map();
|
|
270
|
+
imagesFor(labels, size, { league, kind: markType, ...rest }, loadImage).forEach((img, i) => {
|
|
271
|
+
if (img !== void 0) m.set(labels[i] ?? "", img);
|
|
272
|
+
});
|
|
273
|
+
const band = axis === "y" ? Math.max(size, ...[...m.values()].map((img) => img.width)) : size;
|
|
274
|
+
byChart.set(chart, { m, band });
|
|
275
|
+
scale.ticks ??= {};
|
|
276
|
+
const ticks = scale.ticks;
|
|
277
|
+
const user = ticks.callback;
|
|
278
|
+
let base = user === void 0 ? void 0 : bases.get(user);
|
|
279
|
+
if (base === void 0) {
|
|
280
|
+
base = ticks.padding ?? 3;
|
|
281
|
+
const callback = function(value, index, all) {
|
|
282
|
+
const label = this.getLabelForValue(Number(value));
|
|
283
|
+
if (byChart.get(chart)?.m.has(label)) return "";
|
|
284
|
+
return user === void 0 ? label : user.call(this, value, index, all);
|
|
285
|
+
};
|
|
286
|
+
bases.set(callback, base);
|
|
287
|
+
ticks.callback = callback;
|
|
288
|
+
}
|
|
289
|
+
ticks.padding = base + band;
|
|
290
|
+
},
|
|
291
|
+
afterDatasetsDraw(chart) {
|
|
292
|
+
const scale = chart.scales[scaleId];
|
|
293
|
+
const got = byChart.get(chart);
|
|
294
|
+
if (scale === void 0 || got === void 0) return;
|
|
295
|
+
const { m, band } = got;
|
|
296
|
+
const { grid, ticks } = scale.options;
|
|
297
|
+
const gap = (grid.display && grid.drawTicks ? grid.tickLength : 0) + ticks.padding - band;
|
|
298
|
+
scale.ticks.forEach((t, i) => {
|
|
299
|
+
const img = paint(chart, m.get(scale.getLabelForValue(t.value)));
|
|
300
|
+
if (img === void 0) return;
|
|
301
|
+
const at = scale.getPixelForTick(i);
|
|
302
|
+
const { width: w, height: h } = img;
|
|
303
|
+
const x = axis === "y" ? scale.position === "right" ? scale.left + gap : scale.right - gap - w : at - w / 2;
|
|
304
|
+
const y = axis === "x" ? scale.position === "top" ? scale.bottom - gap - h : scale.top + gap : at - h / 2;
|
|
305
|
+
chart.ctx.drawImage(img, x, y, w, h);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function logoWatermarks(teams, o) {
|
|
311
|
+
if (o.loadImage === void 0) needDom("logo watermarks");
|
|
312
|
+
const { league, size = 75, alpha = 0.4, inset = 8, loadImage, ...rest } = o;
|
|
313
|
+
checkSize(size, "size");
|
|
314
|
+
const a = checkAlpha(alpha);
|
|
315
|
+
const imgs = imagesFor(teams, size, { league, kind: "logo", ...rest }, loadImage);
|
|
316
|
+
const n = imgs.length;
|
|
317
|
+
return {
|
|
318
|
+
id: "sdvplotLogoWatermarks",
|
|
319
|
+
afterDestroy: forget,
|
|
320
|
+
beforeDatasetsDraw(chart) {
|
|
321
|
+
const { ctx, chartArea: ar } = chart;
|
|
322
|
+
if (!ar) return;
|
|
323
|
+
ctx.save();
|
|
324
|
+
ctx.globalAlpha = a;
|
|
325
|
+
imgs.forEach((m, i) => {
|
|
326
|
+
const img = paint(chart, m);
|
|
327
|
+
if (img === void 0) return;
|
|
328
|
+
const t = n > 1 ? i / (n - 1) : 0;
|
|
329
|
+
const y = ar.top + inset + t * (ar.bottom - ar.top - 2 * inset - img.height);
|
|
330
|
+
ctx.drawImage(img, ar.left + inset, y, img.width, img.height);
|
|
331
|
+
});
|
|
332
|
+
ctx.restore();
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export { axisLogos, headshotPoints, logoPoints, logoWatermarks, pointImages, teamColor, teamFill, wordmarkPoints };
|
|
338
|
+
//# sourceMappingURL=chartjs.js.map
|
|
339
|
+
//# sourceMappingURL=chartjs.js.map
|