@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 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/chartjs.ts"],"names":["redraws","paint"],"mappings":"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","file":"chartjs.js","sourcesContent":["/**\n * Chart.js 4 helpers: team logo / wordmark / headshot point styles, axis-tick logos, faint logo watermarks, team\n * colours and a sporty surface background. `chart.js` is a type-only import (optional peer, 4.4 or later): everything\n * here is a dataset option or a plugin object, so Svelte, Astro, React or plain scripts pass it to `new Chart(...)`\n * unchanged.\n * The sporty court background lives in `sdvplot/chartjs/surface`, so this entry never needs `@sportsdataverse/sporty`.\n * Call after `loadLeague(league)` / `preloadAll()`; point styles and plugins need a DOM (build them client-side) unless\n * given `loadImage`, which in Node is `@napi-rs/canvas`'s.\n */\nimport type { Chart, Plugin, PointStyle, Scale, Tick } from \"chart.js\";\nimport { teamColorsSync } from \"./colors.js\";\nimport { contrast, hex6, onColor } from \"./contrast.js\";\nimport { InputError, UnsupportedTargetError, warn } from \"./errors.js\";\nimport type { EspnHeadshotLeague } from \"./headshots.js\";\nimport { type Kind, type PlaceOptions, checkAlpha, placeSync } from \"./placement.js\";\nimport type { Value } from \"./resolve.js\";\nimport type { HeadshotIdSystem, IdSystem, League, SeasonInput, Variant, Which } from \"./types.js\";\n\nexport interface PointOptions {\n league: League;\n /** Half the image height in px; also the dataset's `pointRadius` (hit area). Default 12. */\n radius?: number;\n season?: SeasonInput | readonly SeasonInput[];\n variant?: Variant;\n idSystem?: IdSystem;\n /** What an unresolved value draws: `\"text\"` (its own label, the default) or any Chart.js point style. */\n fallback?: PointStyle | \"text\";\n /** The chart's background colour, so the text fallback reads on it (default white, or black with `variant: \"dark\"`). */\n background?: string;\n /**\n * Loads each image instead of a DOM image element, so this also runs in Node: pass `@napi-rs/canvas`'s `loadImage`.\n * See {@link ImageLike}.\n */\n loadImage?: (url: string) => Promise<ImageLike>;\n}\nexport interface HeadshotPointOptions {\n league: EspnHeadshotLeague;\n radius?: number;\n idSystem?: HeadshotIdSystem;\n fallback?: PointStyle | \"text\";\n /** The chart's background colour, so the text fallback reads on it (default white). */\n background?: string;\n /**\n * Loads each image instead of a DOM image element, so this also runs in Node: pass `@napi-rs/canvas`'s `loadImage`.\n * See {@link ImageLike}.\n */\n loadImage?: (url: string) => Promise<ImageLike>;\n}\n/** Spread into a scatter / bubble / line dataset: one style per data point, in data order. */\nexport interface PointStyles {\n pointStyle: PointStyle[];\n pointRadius: number;\n}\n\n/**\n * An image a `loadImage` option resolves: anything the chart's 2D context can `drawImage`, such as `@napi-rs/canvas`'s\n * `Image` in Node or an `HTMLImageElement` in the browser. `loadImage` is called once per URL and drawn size, and the\n * image's `width` and `height` are then set to that size (Chart.js draws an image point style at its own size), so\n * resolve a new image on every call, as `@napi-rs/canvas`'s `loadImage` does.\n */\nexport interface ImageLike {\n readonly width: number;\n readonly height: number;\n}\n\nfunction needDom(what: string): Document {\n if (typeof document === \"undefined\")\n throw new UnsupportedTargetError(\n `${what} needs a DOM: build it in the browser (onMount, $effect, an Astro client:only island), not during SSR`,\n );\n return document;\n}\nfunction checkSize(size: number, what: string): number {\n if (!(Number.isFinite(size) && size > 0))\n throw new InputError(`${what} is an image height in px > 0, got ${size}`);\n return size;\n}\n// ponytail: unbounded cache keyed by size + url; a page holds a few hundred marks at most.\nconst images = new Map<string, HTMLImageElement>();\nfunction image(url: string, h: number, aspect: number): HTMLImageElement {\n const w = Math.round(h * aspect);\n const key = `${w}x${h} ${url}`;\n let img = images.get(key);\n if (img === undefined) {\n img = needDom(\"an image point style\").createElement(\"img\");\n img.width = w; // Chart.js draws an image point style at the element's own width x height\n img.height = h;\n img.crossOrigin = \"anonymous\"; // keeps chart.toBase64Image() usable\n img.src = url;\n images.set(key, img);\n }\n return img;\n}\ntype LoadImage = (url: string) => Promise<ImageLike>;\n/** A `loadImage` image at its drawn size: `img` is undefined while it loads and null if it failed. */\nclass Loading {\n img: ImageLike | null | undefined = undefined;\n /** Point-style slots to fill, then charts to redraw, all in the first reaction to the loader's promise. */\n readonly slots: [PointStyle[], number][] = [];\n readonly on = new Set<() => void>();\n constructor(\n readonly width: number,\n readonly height: number,\n ) {}\n}\ntype Mark = HTMLImageElement | Loading;\n/** The size each loaded image was set to, to catch a loader that hands the same image out for two sizes. */\nconst sizes = new WeakMap<object, { width: number; height: number }>();\nconst DRAWN = [\"[object HTMLImageElement]\", \"[object HTMLCanvasElement]\"];\n/** Chart.js draws a point-style image at its own width x height, and only one that stringifies as a DOM image or canvas. */\nfunction sized(img: ImageLike, w: number, h: number): ImageLike | null {\n const was = sizes.get(img);\n if (was !== undefined && (was.width !== w || was.height !== h)) {\n warn(\n \"chartjs:loadImage:shared\",\n \"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)\",\n );\n return null;\n }\n const own = (value: unknown): PropertyDescriptor => ({ value, writable: true, configurable: true });\n const tag: PropertyDescriptorMap = DRAWN.includes(String(img)) ? {} : { toString: own(() => DRAWN[0]) };\n try {\n Object.defineProperties(img, { width: own(w), height: own(h), ...tag });\n } catch (e) {\n warn(\n \"chartjs:loadImage:unsizable\",\n `loadImage returned an image that cannot be sized (${String(e)}), so it is not drawn`,\n );\n return null;\n }\n sizes.set(img, { width: w, height: h });\n return img;\n}\n// ponytail: unbounded like `images`; per loader, so two loaders never share an image.\nconst loads = new WeakMap<LoadImage, Map<string, Loading>>();\nfunction loading(load: LoadImage, url: string, h: number, aspect: number): Loading {\n const w = Math.round(h * aspect);\n const key = `${w}x${h} ${url}`;\n const byKey = loads.get(load) ?? new Map<string, Loading>();\n loads.set(load, byKey);\n let m = byKey.get(key);\n if (m === undefined) {\n const it = new Loading(w, h);\n // everything runs here, in the promise's first reaction, so a caller awaiting the same promises sees it all done\n const land = (img: ImageLike | null): void => {\n it.img = img;\n if (img !== null) for (const [styles, i] of it.slots) styles[i] = img as unknown as PointStyle;\n it.slots.length = 0;\n const redraws = [...it.on];\n it.on.clear();\n for (const f of redraws) f();\n };\n void load(url).then(\n (img) => land(sized(img, w, h)),\n (e: unknown) => {\n warn(`chartjs:loadImage:${url}`, `loadImage failed for ${url} (${String(e)}), so it is not drawn`);\n land(null);\n },\n );\n m = it;\n byKey.set(key, m);\n }\n return m;\n}\n/** Game on Paper's light-theme or dark-theme canvas text colour, whichever reads better on `background`. */\nfunction textInk(background: string): string {\n return contrast(\"#555555\", background) >= contrast(\"#e8e6e3\", background) ? \"#555555\" : \"#e8e6e3\";\n}\nfunction textStyle(label: string, h: number, ink: string): HTMLCanvasElement | \"circle\" {\n // ponytail: Node (only reachable with loadImage) has no canvas to letter the label on, so it draws a circle\n if (typeof document === \"undefined\") return \"circle\";\n const c = document.createElement(\"canvas\");\n const g = c.getContext(\"2d\");\n if (g === null) return \"circle\";\n const font = `bold ${Math.round(h * 0.45)}px sans-serif`;\n g.font = font;\n c.width = Math.max(h, Math.ceil(g.measureText(label).width) + 4);\n c.height = h;\n g.font = font; // resizing a canvas resets its context\n g.textAlign = \"center\";\n g.textBaseline = \"middle\";\n g.fillStyle = ink;\n g.fillText(label, c.width / 2, h / 2);\n return c;\n}\n/** One image per resolvable value at `h` px tall, indexed like `values` (gaps where nothing can be drawn); warns once per call (J28). */\nfunction imagesFor(\n values: readonly Value[],\n h: number,\n place: PlaceOptions,\n load: LoadImage | undefined,\n): (Mark | undefined)[] {\n const out = new Array<Mark | undefined>(values.length);\n // The row index as x keeps data order; placeSync drops what it cannot draw and warns once per call (J28).\n const idx = values.map((_, i) => i);\n for (const p of placeSync(idx, idx, values, place))\n out[p.index] =\n load === undefined ? image(p.url, h, p.aspect ?? 1) : loading(load, p.url, h, p.aspect ?? 1);\n return out;\n}\n/** The point-style arrays still waiting on a `loadImage` image, for `pointImages` to refresh. */\nconst pending = new WeakMap<object, Loading[]>();\nfunction points(\n values: readonly Value[],\n kind: Kind,\n radius: number | undefined,\n fallback: PointStyle | \"text\" | undefined,\n background: string | undefined,\n place: PlaceOptions,\n load: LoadImage | undefined,\n): PointStyles {\n if (load === undefined) needDom(\"point styles\"); // before resolving, so SSR fails without a resolve warning\n const r = radius ?? 12;\n if (!(Number.isFinite(r) && r > 0))\n throw new InputError(`radius is a point radius in px > 0, got ${String(radius)}`);\n const ink = textInk(background ?? (place.variant === \"dark\" ? \"#000000\" : \"#ffffff\")); // a bad colour throws here\n const styles = new Array<PointStyle>(values.length);\n const waiting: Loading[] = [];\n imagesFor(values, 2 * r, { ...place, kind }, load).forEach((m, i) => {\n if (m === undefined) return;\n if (!(m instanceof Loading)) {\n styles[i] = m;\n return;\n }\n // until it lands the slot draws nothing, as a loading image does; Chart.js re-reads the array on update\n styles[i] = (m.img ?? false) as PointStyle;\n if (m.img !== undefined) return;\n m.slots.push([styles, i]);\n waiting.push(m);\n });\n if (waiting.length > 0) pending.set(styles, waiting);\n for (let i = 0; i < values.length; i++)\n styles[i] ??=\n fallback === undefined || fallback === \"text\"\n ? textStyle(String(values[i] ?? \"\"), 2 * r, ink)\n : fallback;\n return { pointStyle: styles, pointRadius: r };\n}\nfunction teamPoints(teams: readonly Value[], kind: \"logo\" | \"wordmark\", o: PointOptions): PointStyles {\n const { league, radius, fallback, background, loadImage, ...rest } = o;\n return points(teams, kind, radius, fallback, background, { league, ...rest }, loadImage);\n}\n/** Team logos as point styles: `{ data, ...logoPoints(teams, { league: \"nfl\", radius: 12 }) }`. Add `pointImages` to `plugins`. */\nexport function logoPoints(teams: readonly Value[], o: PointOptions): PointStyles {\n return teamPoints(teams, \"logo\", o);\n}\n/** As `logoPoints`, with wordmarks. */\nexport function wordmarkPoints(teams: readonly Value[], o: PointOptions): PointStyles {\n return teamPoints(teams, \"wordmark\", o);\n}\n/** ESPN player headshots as point styles; `idSystem: \"gsis\"` needs `loadGsis()` first. */\nexport function headshotPoints(players: readonly Value[], o: HeadshotPointOptions): PointStyles {\n return points(\n players,\n \"headshot\",\n o.radius,\n o.fallback,\n o.background,\n { league: o.league, idSystem: o.idSystem ?? \"espn\" },\n o.loadImage,\n );\n}\n\ntype Redraws = { redraw: () => void; refresh: () => void; stop: AbortController };\nconst redraws = new WeakMap<Chart, Redraws>();\nconst isImage = (v: unknown): v is HTMLImageElement =>\n Object.prototype.toString.call(v) === \"[object HTMLImageElement]\"; // Chart.js's own test (drawPointLegend)\nfunction redrawsOf(chart: Chart): Redraws {\n let r = redraws.get(chart);\n if (r === undefined) {\n r = {\n redraw: () => {\n if (chart.ctx) chart.draw(); // null once destroyed\n },\n refresh: () => {\n if (chart.ctx) chart.update(\"none\"); // re-reads the point styles, which a draw does not\n },\n stop: new AbortController(),\n };\n redraws.set(chart, r);\n }\n return r;\n}\n/** Redraw `chart` once `img` loads; one listener per (image, chart) because addEventListener ignores a repeat. */\nfunction redrawOnLoad(chart: Chart, img: HTMLImageElement): void {\n if (img.complete) return;\n const r = redrawsOf(chart);\n img.addEventListener(\"load\", r.redraw, { once: true, signal: r.stop.signal });\n}\n/** As `redrawOnLoad`, for a `loadImage` image still loading; the same per-chart abort drops it on destroy. */\nfunction redrawOnLanding(chart: Chart, m: Loading, how: \"redraw\" | \"refresh\"): void {\n const r = redrawsOf(chart);\n const f = r[how];\n if (m.on.has(f)) return;\n m.on.add(f);\n r.stop.signal.addEventListener(\"abort\", () => m.on.delete(f), { once: true });\n}\n/** The image to paint now, or undefined with a redraw arranged for when it loads (a broken image is skipped). */\nfunction paint(chart: Chart, m: Mark | undefined): HTMLImageElement | undefined {\n if (m === undefined) return undefined;\n if (m instanceof Loading) {\n if (m.img === undefined) redrawOnLanding(chart, m, \"redraw\");\n return (m.img ?? undefined) as HTMLImageElement | undefined; // drawImage takes it as one; only its size is read\n }\n if (!m.complete || m.naturalWidth === 0) {\n redrawOnLoad(chart, m);\n return undefined;\n }\n return m;\n}\n/** `afterDestroy`: drop the chart's pending load listeners, so a late (or never-loading) image cannot hold or redraw it. */\nfunction forget(chart: Chart): void {\n redraws.get(chart)?.stop.abort();\n redraws.delete(chart);\n}\n/** Redraws the chart as each image point style finishes loading (Chart.js does not watch them). */\nexport const pointImages: Plugin = {\n id: \"sdvplotPointImages\",\n afterDestroy: forget,\n afterDraw(chart) {\n for (const ds of chart.data.datasets) {\n const s = (ds as { pointStyle?: unknown }).pointStyle;\n for (const v of Array.isArray(s) ? s : [s]) if (isImage(v)) redrawOnLoad(chart, v);\n for (const m of (Array.isArray(s) && pending.get(s)) || [])\n if (m.img === undefined) redrawOnLanding(chart, m, \"refresh\");\n }\n },\n};\n\nexport interface TeamColorOptions {\n which?: Which;\n season?: SeasonInput;\n idSystem?: IdSystem;\n /** Opacity in [0, 1]; below 1 the colour is written `rgba(r, g, b, a)`. */\n alpha?: number;\n /** The colour of an unresolved team (default `#808080`). */\n fallback?: string;\n /** Return black or white, whichever reads on the team colour (labels, point borders). */\n onColor?: boolean;\n}\nexport type ColorsFor<T> = T extends readonly Value[] ? string[] : string;\n/** Team colour(s) for `backgroundColor` / `borderColor`: one team gives one string, an array gives one per team. */\nexport function teamColor<T extends Value | readonly Value[]>(\n teams: T,\n league: League,\n o: TeamColorOptions = {},\n): ColorsFor<T> {\n const { alpha = 1, fallback = \"#808080\", onColor: ink = false, ...co } = o;\n const a = checkAlpha(alpha);\n const paint = (c: unknown): string => {\n const hex = hex6(typeof c === \"string\" ? c : fallback, { dropAlpha: true });\n if (ink) return onColor(hex);\n if (a === 1) return hex;\n const n = Number.parseInt(hex.slice(1), 16);\n return `rgba(${n >> 16}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;\n };\n const got: unknown = teamColorsSync(league, teams, co);\n return (Array.isArray(got) ? got.map(paint) : paint(got)) as ColorsFor<T>;\n}\n/** `teamColor` at 0.2 opacity by default: the fill under a radar or area dataset. */\nexport function teamFill<T extends Value | readonly Value[]>(\n teams: T,\n league: League,\n o: TeamColorOptions = {},\n): ColorsFor<T> {\n return teamColor(teams, league, { alpha: 0.2, ...o });\n}\n\nexport interface AxisLogoOptions {\n league: League;\n /** Image height in px (default 24); the width follows the mark's aspect. */\n size?: number;\n season?: SeasonInput;\n variant?: Variant;\n markType?: \"logo\" | \"wordmark\";\n idSystem?: IdSystem;\n /** The category scale's id (default: the axis letter). */\n scaleId?: string;\n /**\n * Loads each image instead of a DOM image element, so this also runs in Node: pass `@napi-rs/canvas`'s `loadImage`.\n * See {@link ImageLike}.\n */\n loadImage?: (url: string) => Promise<ImageLike>;\n}\ntype TickCallback = (this: Scale, value: number | string, index: number, ticks: Tick[]) => unknown;\n/**\n * Swap a category axis's tick labels for team logos: resolved labels are painted as images (after the datasets, under\n * the tooltip) and their text is blanked; unresolved labels keep their text, with one warning per distinct label set\n * (J28). Any other scale type is left as it is, with one warning.\n */\nexport function axisLogos(axis: \"x\" | \"y\", o: AxisLogoOptions): Plugin {\n if (axis !== \"x\" && axis !== \"y\")\n throw new InputError(`axis must be \"x\" or \"y\", got ${JSON.stringify(axis)}`);\n const { league, size = 24, markType = \"logo\", scaleId = axis, loadImage, ...rest } = o;\n checkSize(size, \"size\");\n const byChart = new WeakMap<Chart, { m: Map<string, Mark>; band: number }>();\n const bases = new WeakMap<TickCallback, number>(); // each wired callback -> the padding it was wired over\n return {\n id: `sdvplotAxisLogos${axis.toUpperCase()}`,\n afterDestroy: forget,\n beforeUpdate(chart) {\n // Chart.js merges options.scales into a fresh copy on every update (and `chart.options = next` replaces it), so\n // the tick wiring is (re)applied to that copy here, once per copy: never in beforeInit, never the caller's object.\n const conf = chart.config as unknown as {\n options: {\n scales?: Record<string, { type?: string; ticks?: { callback?: TickCallback; padding?: number } }>;\n };\n };\n const scale = conf.options.scales?.[scaleId];\n if (scale?.type !== \"category\") {\n byChart.delete(chart);\n warn(\n `chartjs:axisLogos:${scaleId}`,\n `axisLogos needs a category scale; scale ${JSON.stringify(scaleId)} is ${scale?.type ?? \"missing\"}, so its ticks are left as they are`,\n );\n return;\n }\n const labels = (chart.data.labels ?? []).map(String);\n const m = new Map<string, Mark>();\n imagesFor(labels, size, { league, kind: markType, ...rest }, loadImage).forEach((img, i) => {\n if (img !== undefined) m.set(labels[i] ?? \"\", img);\n });\n // the band the images fill: their height on x, the widest mark on y (a wordmark is several times its height)\n const band = axis === \"y\" ? Math.max(size, ...[...m.values()].map((img) => img.width)) : size;\n byChart.set(chart, { m, band });\n scale.ticks ??= {};\n const ticks = scale.ticks;\n const user = ticks.callback;\n let base = user === undefined ? undefined : bases.get(user);\n if (base === undefined) {\n base = ticks.padding ?? 3; // 3 = Chart.js's default padding\n const callback: TickCallback = function (this: Scale, value, index, all) {\n const label = this.getLabelForValue(Number(value));\n if (byChart.get(chart)?.m.has(label)) return \"\";\n return user === undefined ? label : user.call(this, value, index, all);\n };\n bases.set(callback, base);\n ticks.callback = callback;\n }\n ticks.padding = base + band;\n },\n afterDatasetsDraw(chart) {\n const scale = chart.scales[scaleId];\n const got = byChart.get(chart);\n if (scale === undefined || got === undefined) return;\n const { m, band } = got;\n // resolved options: Chart.js has filled every default by draw time\n const { grid, ticks } = scale.options as unknown as {\n grid: { display: boolean; drawTicks: boolean; tickLength: number };\n ticks: { padding: number };\n };\n const gap = (grid.display && grid.drawTicks ? grid.tickLength : 0) + ticks.padding - band;\n scale.ticks.forEach((t, i) => {\n const img = paint(chart, m.get(scale.getLabelForValue(t.value)));\n if (img === undefined) return;\n const at = scale.getPixelForTick(i);\n const { width: w, height: h } = img;\n const x =\n axis === \"y\" ? (scale.position === \"right\" ? scale.left + gap : scale.right - gap - w) : at - w / 2;\n const y =\n axis === \"x\" ? (scale.position === \"top\" ? scale.bottom - gap - h : scale.top + gap) : at - h / 2;\n chart.ctx.drawImage(img, x, y, w, h);\n });\n },\n };\n}\n\nexport interface WatermarkOptions {\n league: League;\n /** Image height in px (default 75, Game on Paper's win-probability chart); the width follows the mark's aspect. */\n size?: number;\n /** Opacity of the watermarks (default 0.4). */\n alpha?: number;\n /** Gap in px from the chart area's left edge and from its top / bottom (default 8). */\n inset?: number;\n season?: SeasonInput | readonly SeasonInput[];\n variant?: Variant;\n idSystem?: IdSystem;\n /**\n * Loads each image instead of a DOM image element, so this also runs in Node: pass `@napi-rs/canvas`'s `loadImage`.\n * See {@link ImageLike}.\n */\n loadImage?: (url: string) => Promise<ImageLike>;\n}\n/**\n * Faint team logos behind the datasets (Game on Paper's win-probability chart): the first team sits top-left of the\n * chart area, the last bottom-left, any between spaced evenly down the left edge, so pass `[home, away]` under a home\n * win-probability line. Unknown teams are skipped with one warning per call (J28); `variant: \"dark\"` falls back to a\n * light mark by polarity, so no `onerror` retry is needed.\n */\nexport function logoWatermarks(teams: readonly Value[], o: WatermarkOptions): Plugin {\n if (o.loadImage === undefined) needDom(\"logo watermarks\"); // before resolving, so SSR fails without a resolve warning\n const { league, size = 75, alpha = 0.4, inset = 8, loadImage, ...rest } = o;\n checkSize(size, \"size\");\n const a = checkAlpha(alpha);\n const imgs = imagesFor(teams, size, { league, kind: \"logo\", ...rest }, loadImage);\n const n = imgs.length;\n return {\n id: \"sdvplotLogoWatermarks\",\n afterDestroy: forget,\n beforeDatasetsDraw(chart) {\n const { ctx, chartArea: ar } = chart;\n if (!ar) return; // a draw before the first layout\n ctx.save();\n ctx.globalAlpha = a;\n imgs.forEach((m, i) => {\n const img = paint(chart, m);\n if (img === undefined) return;\n const t = n > 1 ? i / (n - 1) : 0;\n const y = ar.top + inset + t * (ar.bottom - ar.top - 2 * inset - img.height);\n ctx.drawImage(img, ar.left + inset, y, img.width, img.height);\n });\n ctx.restore();\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { InputError } from './chunk-E7G7VSLV.js';
|
|
2
|
+
|
|
3
|
+
// src/selection.ts
|
|
4
|
+
var sealed = (set) => {
|
|
5
|
+
const deny = () => {
|
|
6
|
+
throw new TypeError("a selection state is read-only: pass the new ids to store.set");
|
|
7
|
+
};
|
|
8
|
+
for (const m of ["add", "delete", "clear"]) Object.defineProperty(set, m, { value: deny });
|
|
9
|
+
return set;
|
|
10
|
+
};
|
|
11
|
+
var EMPTY = sealed(/* @__PURE__ */ new Set());
|
|
12
|
+
function toId(v) {
|
|
13
|
+
return v === null || v === void 0 || v === "" || typeof v === "number" && Number.isNaN(v) ? "" : String(v);
|
|
14
|
+
}
|
|
15
|
+
function sameIds(a, b) {
|
|
16
|
+
if (a === b) return true;
|
|
17
|
+
if (a.size !== b.size) return false;
|
|
18
|
+
for (const id of a) if (!b.has(id)) return false;
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
function sameCursor(a, b) {
|
|
22
|
+
return a === b || a !== null && b !== null && a.field === b.field && a.value === b.value;
|
|
23
|
+
}
|
|
24
|
+
function focusIds(s) {
|
|
25
|
+
if (s.hover.size === 0 && s.selected.size === 0 && s.predicate === null) return null;
|
|
26
|
+
if (s.hover.size === 0) return s.selected;
|
|
27
|
+
if (s.selected.size === 0) return s.hover;
|
|
28
|
+
return /* @__PURE__ */ new Set([...s.selected, ...s.hover]);
|
|
29
|
+
}
|
|
30
|
+
function createSelection() {
|
|
31
|
+
let state = Object.freeze({
|
|
32
|
+
hover: EMPTY,
|
|
33
|
+
selected: EMPTY,
|
|
34
|
+
predicate: null,
|
|
35
|
+
cursor: null
|
|
36
|
+
});
|
|
37
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
38
|
+
const ids = (next, prev) => {
|
|
39
|
+
if (next === void 0) return prev;
|
|
40
|
+
if (typeof next === "string")
|
|
41
|
+
throw new InputError(
|
|
42
|
+
`selection ids are a collection; pass [${JSON.stringify(next)}], not a bare string`
|
|
43
|
+
);
|
|
44
|
+
const set = /* @__PURE__ */ new Set();
|
|
45
|
+
for (const v of next) {
|
|
46
|
+
const id = toId(v);
|
|
47
|
+
if (id !== "") set.add(id);
|
|
48
|
+
}
|
|
49
|
+
return sameIds(set, prev) ? prev : sealed(set);
|
|
50
|
+
};
|
|
51
|
+
const store = {
|
|
52
|
+
getState: () => state,
|
|
53
|
+
set(patch) {
|
|
54
|
+
const c = patch.cursor;
|
|
55
|
+
if (c != null && (typeof c.field !== "string" || c.field === "" || !Number.isFinite(c.value)))
|
|
56
|
+
throw new InputError(
|
|
57
|
+
`selection cursor needs a non-empty field and a finite value, got field ${JSON.stringify(c.field)}, value ${String(c.value)}`
|
|
58
|
+
);
|
|
59
|
+
const hover = ids(patch.hover, state.hover);
|
|
60
|
+
const selected = ids(patch.selected, state.selected);
|
|
61
|
+
const predicate = patch.predicate === void 0 ? state.predicate : patch.predicate;
|
|
62
|
+
const cursor = c === void 0 || sameCursor(c, state.cursor) ? state.cursor : c === null ? null : Object.freeze({ field: c.field, value: c.value });
|
|
63
|
+
if (hover === state.hover && selected === state.selected && predicate === state.predicate && cursor === state.cursor)
|
|
64
|
+
return;
|
|
65
|
+
state = Object.freeze({ hover, selected, predicate, cursor });
|
|
66
|
+
for (const fn of [...listeners]) {
|
|
67
|
+
if (!listeners.has(fn)) continue;
|
|
68
|
+
try {
|
|
69
|
+
fn(state);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
queueMicrotask(() => {
|
|
72
|
+
throw e;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
clear() {
|
|
78
|
+
store.set({ hover: EMPTY, selected: EMPTY, predicate: null, cursor: null });
|
|
79
|
+
},
|
|
80
|
+
subscribe(fn) {
|
|
81
|
+
listeners.add(fn);
|
|
82
|
+
return () => {
|
|
83
|
+
listeners.delete(fn);
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
return store;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export { createSelection, focusIds, sameCursor, sameIds, toId };
|
|
91
|
+
//# sourceMappingURL=chunk-4C3GX6QO.js.map
|
|
92
|
+
//# sourceMappingURL=chunk-4C3GX6QO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/selection.ts"],"names":[],"mappings":"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","file":"chunk-4C3GX6QO.js","sourcesContent":["import { InputError } from \"./errors.js\";\n\n/**\n * A row test, true to keep the row: the brush region (or any caller's) as a predicate. The one definition: sdvtables\n * re-exports this type for `setExternalFilter`, so a linked table applies a store's predicate as is (not a\n * `ValuePredicate`, which tests one column's cell). Both compare it by identity: the same function again is a no-op.\n */\nexport type RowFilter<Row> = (row: Row) => boolean;\n\n/**\n * A shared hover VALUE (J34): a number along one data field, such as the shot distance under the pointer, that every\n * linked chart draws through its own scale (a rule, a band, a ring). Not an id: it never dims marks or filters a table.\n * Unrelated to sdvtables' `TableCursor` (a grid's keyboard row).\n */\nexport interface Cursor {\n /** The data field the value is in, e.g. `\"shot_distance\"`. Never `\"\"`. */\n readonly field: string;\n /** The value along that field. Always finite. */\n readonly value: number;\n}\n\n/**\n * What a {@link SelectionStore} holds. Immutable: every change replaces it, and a snapshot is read-only at runtime too\n * (its sets' `add`, `delete` and `clear`, and a write to it or its cursor, throw a `TypeError`): pass changes to\n * `store.set`, or copy a set with `new Set(ids)`.\n */\nexport interface SelectionState<Row = unknown> {\n /** Ids under the pointer right now (transient). */\n readonly hover: ReadonlySet<string>;\n /** Ids picked by a table-row click or a brush (sticky). */\n readonly selected: ReadonlySet<string>;\n /** The brush region as a row test; null when nothing is brushed. */\n readonly predicate: RowFilter<Row> | null;\n /** The shared hover value; null when the pointer is on no linked chart. */\n readonly cursor: Cursor | null;\n}\n/**\n * A partial update for {@link SelectionStore.set}: an absent field keeps its value. Each id goes through\n * {@link toId}: a number is stringified (`12` and `\"12\"` are the same id) and null, undefined, NaN and `\"\"` are\n * dropped. A bare string throws `InputError` rather than being split into letters: pass `[\"KC\"]`, not `\"KC\"`.\n */\nexport interface SelectionPatch<Row = unknown> {\n /** The new hover ids (replaces the set; `[]` clears it). */\n readonly hover?: Iterable<string>;\n /** The new selected ids (replaces the set; `[]` clears it). */\n readonly selected?: Iterable<string>;\n /** The new brush region; `null` clears it. */\n readonly predicate?: RowFilter<Row> | null;\n /** The new hover value; `null` clears it. An equal cursor (same field, same value) is a no-op. */\n readonly cursor?: Cursor | null;\n}\n/** The linked-interactivity hub that {@link createSelection} returns. */\nexport interface SelectionStore<Row = unknown> {\n /** Referentially stable until the next change: what `useSyncExternalStore` reads. */\n getState(): SelectionState<Row>;\n /** Apply a patch. ONE notification when anything changed, none when nothing did, so linked components cannot ping-pong. */\n set(patch: SelectionPatch<Row>): void;\n /** Clear hover, selected, predicate and cursor. */\n clear(): void;\n /**\n * Call `fn` after every change; returns the unsubscribe function. A listener that throws stops neither the others\n * nor `set`: its error is rethrown in a microtask, as an `EventTarget` listener's is, so it still reaches\n * `window.onerror` (or Node's `uncaughtException`) but one broken view cannot freeze the rest. A listener removed\n * during a notification is not called for it.\n */\n subscribe(fn: (state: SelectionState<Row>) => void): () => void;\n}\n\n/**\n * Seal one of the store's id sets: read-only at runtime as well as in its type, since a JavaScript caller's `add`\n * would change the state behind the listeners' backs (and EMPTY is shared by every store). The mutators become own,\n * non-enumerable throwing methods, so it stays a plain `Set` to `instanceof` and to deep equality; a copy,\n * `new Set(ids)`, is the caller's own.\n */\nconst sealed = (set: Set<string>): ReadonlySet<string> => {\n const deny = (): never => {\n throw new TypeError(\"a selection state is read-only: pass the new ids to store.set\");\n };\n for (const m of [\"add\", \"delete\", \"clear\"]) Object.defineProperty(set, m, { value: deny });\n return set;\n};\nconst EMPTY: ReadonlySet<string> = sealed(new Set<string>());\n\n/**\n * The link id of a value: `String(v)`, or `\"\"` (never matched) for null, undefined, NaN and `\"\"`. A figure and a table\n * link only when they hold the same ids, so an ESPN team id (`\"12\"`) never matches an abbreviation (`\"KC\"`).\n *\n * @example\n * ```ts\n * import { toId } from \"@sportsdataverse/sdvplot\";\n *\n * [toId(12), toId(\"KC\"), toId(null), toId(Number.NaN)];\n * ```\n */\nexport function toId(v: unknown): string {\n return v === null || v === undefined || v === \"\" || (typeof v === \"number\" && Number.isNaN(v))\n ? \"\"\n : String(v);\n}\n\n/**\n * Whether two id sets hold the same ids, in any order.\n *\n * @example\n * ```ts\n * import { sameIds } from \"@sportsdataverse/sdvplot\";\n *\n * sameIds(new Set([\"KC\", \"BUF\"]), new Set([\"BUF\", \"KC\"]));\n * ```\n */\nexport function sameIds(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {\n if (a === b) return true;\n if (a.size !== b.size) return false;\n for (const id of a) if (!b.has(id)) return false;\n return true;\n}\n\n/**\n * Whether two cursors are the same: the same field and an `===`-equal value, so `0` and `-0` are one hover value\n * (NaN never gets into the store). `null` equals only `null`.\n *\n * @example\n * ```ts\n * import { sameCursor } from \"@sportsdataverse/sdvplot\";\n *\n * const rim = { field: \"shot_distance\", value: 0.5 }; // the centre of the 0-1 ft bin\n * [\n * sameCursor(rim, { field: \"shot_distance\", value: 0.5 }), // a new object, the same value: true\n * sameCursor(rim, { field: \"shot_distance\", value: 1.5 }), // the next bin: false\n * sameCursor(rim, null), // the pointer left: false\n * sameCursor(null, null),\n * ];\n * ```\n */\nexport function sameCursor(a: Cursor | null, b: Cursor | null): boolean {\n return a === b || (a !== null && b !== null && a.field === b.field && a.value === b.value);\n}\n\n/**\n * What a figure should emphasise. `null`: nothing is active, draw everything normally. A set, possibly EMPTY\n * (a brush that encloses no point): emphasise those ids and dim the rest. Hover and selected are unioned. The\n * cursor is ignored: a shared hover value never dims anything.\n *\n * @example\n * ```ts\n * import { createSelection, focusIds } from \"@sportsdataverse/sdvplot\";\n *\n * const store = createSelection();\n * const idle = focusIds(store.getState()); // null: draw everything normally\n * store.set({ selected: [\"KC\"], hover: [\"BUF\"] });\n * ({ idle, focus: [...(focusIds(store.getState()) ?? [])] });\n * ```\n */\nexport function focusIds<Row>(s: SelectionState<Row>): ReadonlySet<string> | null {\n if (s.hover.size === 0 && s.selected.size === 0 && s.predicate === null) return null;\n if (s.hover.size === 0) return s.selected;\n if (s.selected.size === 0) return s.hover;\n return new Set([...s.selected, ...s.hover]);\n}\n\n/**\n * The linked-interactivity hub (J31): hover and selected id sets plus a row predicate. Pure data, zero deps, no DOM:\n * creating or updating one in Node changes nothing a server renders. Ids are strings, normalised by {@link toId}\n * (see {@link SelectionPatch}).\n *\n * @example\n * ```ts\n * import { createSelection } from \"@sportsdataverse/sdvplot\";\n *\n * const store = createSelection();\n * let updates = 0;\n * store.subscribe(() => updates++);\n * store.set({ selected: [\"KC\", \"BUF\"] });\n * store.set({ selected: [\"BUF\", \"KC\"] }); // the same ids: silent, so linked views cannot ping-pong\n * ({ updates, selected: [...store.getState().selected] });\n * ```\n */\nexport function createSelection<Row = unknown>(): SelectionStore<Row> {\n let state: SelectionState<Row> = Object.freeze({\n hover: EMPTY,\n selected: EMPTY,\n predicate: null,\n cursor: null,\n });\n const listeners = new Set<(s: SelectionState<Row>) => void>();\n const ids = (next: Iterable<string> | undefined, prev: ReadonlySet<string>): ReadonlySet<string> => {\n if (next === undefined) return prev;\n if (typeof next === \"string\")\n throw new InputError(\n `selection ids are a collection; pass [${JSON.stringify(next)}], not a bare string`,\n );\n const set = new Set<string>();\n for (const v of next) {\n const id = toId(v);\n if (id !== \"\") set.add(id);\n }\n return sameIds(set, prev) ? prev : sealed(set);\n };\n const store: SelectionStore<Row> = {\n getState: () => state,\n set(patch) {\n const c = patch.cursor;\n if (c != null && (typeof c.field !== \"string\" || c.field === \"\" || !Number.isFinite(c.value)))\n throw new InputError(\n `selection cursor needs a non-empty field and a finite value, got field ${JSON.stringify(c.field)}, value ${String(c.value)}`,\n );\n const hover = ids(patch.hover, state.hover);\n const selected = ids(patch.selected, state.selected);\n const predicate = patch.predicate === undefined ? state.predicate : patch.predicate;\n // a copy, so a caller mutating its object later cannot change the state behind the listeners' backs\n const cursor =\n c === undefined || sameCursor(c, state.cursor)\n ? state.cursor\n : c === null\n ? null\n : Object.freeze({ field: c.field, value: c.value });\n if (\n hover === state.hover &&\n selected === state.selected &&\n predicate === state.predicate &&\n cursor === state.cursor\n )\n return;\n state = Object.freeze({ hover, selected, predicate, cursor });\n for (const fn of [...listeners]) {\n if (!listeners.has(fn)) continue; // removed by an earlier listener during this notification\n try {\n fn(state);\n } catch (e) {\n // reported, not swallowed, and not thrown at the caller (a pointer handler that cannot fix another view)\n queueMicrotask(() => {\n throw e;\n });\n }\n }\n },\n clear() {\n store.set({ hover: EMPTY, selected: EMPTY, predicate: null, cursor: null });\n },\n subscribe(fn) {\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n },\n };\n return store;\n}\n"]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { InputError } from './chunk-E7G7VSLV.js';
|
|
2
|
+
|
|
3
|
+
// src/bins/hexbin.ts
|
|
4
|
+
var THIRD_PI = Math.PI / 3;
|
|
5
|
+
var ANGLES = [0, THIRD_PI, 2 * THIRD_PI, 3 * THIRD_PI, 4 * THIRD_PI, 5 * THIRD_PI];
|
|
6
|
+
function hexbin(points, o) {
|
|
7
|
+
if (!(Number.isFinite(o.radius) && o.radius > 0))
|
|
8
|
+
throw new InputError(`hexbin radius must be a finite number > 0, got ${String(o.radius)}`);
|
|
9
|
+
const dx = o.radius * 2 * Math.sin(THIRD_PI);
|
|
10
|
+
const dy = o.radius * 1.5;
|
|
11
|
+
const byId = /* @__PURE__ */ new Map();
|
|
12
|
+
const bins = [];
|
|
13
|
+
for (const p of points) {
|
|
14
|
+
let px = +o.x(p);
|
|
15
|
+
let py = +o.y(p);
|
|
16
|
+
if (Number.isNaN(px) || Number.isNaN(py)) continue;
|
|
17
|
+
py = py / dy;
|
|
18
|
+
let pj = Math.round(py);
|
|
19
|
+
px = px / dx - (pj & 1) / 2;
|
|
20
|
+
let pi = Math.round(px);
|
|
21
|
+
const py1 = py - pj;
|
|
22
|
+
if (Math.abs(py1) * 3 > 1) {
|
|
23
|
+
const px1 = px - pi;
|
|
24
|
+
const pi2 = pi + (px < pi ? -1 : 1) / 2;
|
|
25
|
+
const pj2 = pj + (py < pj ? -1 : 1);
|
|
26
|
+
const px2 = px - pi2;
|
|
27
|
+
const py2 = py - pj2;
|
|
28
|
+
if (px1 * px1 + py1 * py1 > px2 * px2 + py2 * py2) {
|
|
29
|
+
pi = pi2 + (pj & 1 ? 1 : -1) / 2;
|
|
30
|
+
pj = pj2;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const id = `${pi}-${pj}`;
|
|
34
|
+
let bin = byId.get(id);
|
|
35
|
+
if (bin === void 0) {
|
|
36
|
+
bin = Object.assign([], { x: (pi + (pj & 1) / 2) * dx || 0, y: pj * dy || 0 });
|
|
37
|
+
byId.set(id, bin);
|
|
38
|
+
bins.push(bin);
|
|
39
|
+
}
|
|
40
|
+
bin.push(p);
|
|
41
|
+
}
|
|
42
|
+
return bins;
|
|
43
|
+
}
|
|
44
|
+
function hexagonPoints(r) {
|
|
45
|
+
return ANGLES.map((a) => [Math.sin(a) * r, -Math.cos(a) * r]);
|
|
46
|
+
}
|
|
47
|
+
function hexagonPath(r) {
|
|
48
|
+
let x0 = 0;
|
|
49
|
+
let y0 = 0;
|
|
50
|
+
const steps = ANGLES.map((a) => {
|
|
51
|
+
const x1 = Math.sin(a) * r;
|
|
52
|
+
const y1 = -Math.cos(a) * r;
|
|
53
|
+
const step = `${x1 - x0},${y1 - y0}`;
|
|
54
|
+
x0 = x1;
|
|
55
|
+
y0 = y1;
|
|
56
|
+
return step;
|
|
57
|
+
});
|
|
58
|
+
return `m${steps.join("l")}z`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/bins/squarebin.ts
|
|
62
|
+
function positive(name, v) {
|
|
63
|
+
if (!(Number.isFinite(v) && v > 0))
|
|
64
|
+
throw new InputError(`binner ${name} must be a finite number > 0, got ${String(v)}`);
|
|
65
|
+
return v;
|
|
66
|
+
}
|
|
67
|
+
var EQUAL_AREA = Math.sqrt(1.5 * Math.sqrt(3));
|
|
68
|
+
function squarebin(points, o) {
|
|
69
|
+
const s = o.side;
|
|
70
|
+
if (!(Number.isFinite(s) && s > 0))
|
|
71
|
+
throw new InputError(`squarebin side must be a finite number > 0, got ${String(s)}`);
|
|
72
|
+
const byId = /* @__PURE__ */ new Map();
|
|
73
|
+
const bins = [];
|
|
74
|
+
for (const p of points) {
|
|
75
|
+
const px = +o.x(p);
|
|
76
|
+
const py = +o.y(p);
|
|
77
|
+
if (Number.isNaN(px) || Number.isNaN(py)) continue;
|
|
78
|
+
const i = Math.round(px / s);
|
|
79
|
+
const j = Math.round(py / s);
|
|
80
|
+
const id = `${i}-${j}`;
|
|
81
|
+
let bin = byId.get(id);
|
|
82
|
+
if (bin === void 0) {
|
|
83
|
+
bin = Object.assign([], { x: i * s || 0, y: j * s || 0 });
|
|
84
|
+
byId.set(id, bin);
|
|
85
|
+
bins.push(bin);
|
|
86
|
+
}
|
|
87
|
+
bin.push(p);
|
|
88
|
+
}
|
|
89
|
+
return bins;
|
|
90
|
+
}
|
|
91
|
+
function squarePoints(s) {
|
|
92
|
+
const h = s / 2;
|
|
93
|
+
return [
|
|
94
|
+
[-h, -h],
|
|
95
|
+
[h, -h],
|
|
96
|
+
[h, h],
|
|
97
|
+
[-h, h]
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
function squarePath(s) {
|
|
101
|
+
return `m${-s / 2},${-s / 2}h${s}v${s}h${-s}z`;
|
|
102
|
+
}
|
|
103
|
+
function cellPoints(shape, size) {
|
|
104
|
+
return shape === "square" ? squarePoints(size) : hexagonPoints(size);
|
|
105
|
+
}
|
|
106
|
+
function cellPath(shape, size) {
|
|
107
|
+
return shape === "square" ? squarePath(size) : hexagonPath(size);
|
|
108
|
+
}
|
|
109
|
+
function binner(o) {
|
|
110
|
+
const shape = o.shape;
|
|
111
|
+
if (shape !== void 0 && shape !== "hex" && shape !== "square")
|
|
112
|
+
throw new InputError(`binner shape must be "hex" or "square", got ${String(shape)}`);
|
|
113
|
+
if (o.shape !== "square") {
|
|
114
|
+
const radius = positive("radius", o.radius);
|
|
115
|
+
return {
|
|
116
|
+
shape: "hex",
|
|
117
|
+
size: radius,
|
|
118
|
+
lattice: { radius },
|
|
119
|
+
bins: (points, a) => hexbin(points, { radius, x: a.x, y: a.y }),
|
|
120
|
+
cell: (s = radius) => hexagonPath(s)
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (!("side" in o) && o.equalArea !== true)
|
|
124
|
+
throw new InputError("binner square needs side, or radius with equalArea: true");
|
|
125
|
+
const side = "side" in o ? positive("side", o.side) : positive("radius", o.radius) * EQUAL_AREA;
|
|
126
|
+
return {
|
|
127
|
+
shape: "square",
|
|
128
|
+
size: side,
|
|
129
|
+
lattice: { shape: "square", side },
|
|
130
|
+
bins: (points, a) => squarebin(points, { side, x: a.x, y: a.y }),
|
|
131
|
+
cell: (s = side) => squarePath(s)
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { binner, cellPath, cellPoints, hexagonPath, hexbin, squarePath, squarebin };
|
|
136
|
+
//# sourceMappingURL=chunk-C2CDY5OG.js.map
|
|
137
|
+
//# sourceMappingURL=chunk-C2CDY5OG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bins/hexbin.ts","../src/bins/squarebin.ts"],"names":[],"mappings":"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","file":"chunk-C2CDY5OG.js","sourcesContent":["// hexbin() and hexagonPath() port d3-hexbin 0.2.2 src/hexbin.js:1-2, :23-72 (Copyright 2012-2016 Mike Bostock,\n// BSD-3-Clause; full notice in NOTICE.md). Same lattice, bin order and centres, so bins equal d3-hexbin's.\nimport { InputError } from \"../errors.js\";\n\nconst THIRD_PI = Math.PI / 3;\nconst ANGLES = [0, THIRD_PI, 2 * THIRD_PI, 3 * THIRD_PI, 4 * THIRD_PI, 5 * THIRD_PI];\n\n/** One bin of either shape (`hexbin`, `squarebin`): the points in it, plus its centre in the points' own units. */\nexport type BinOf<T> = T[] & { x: number; y: number };\n\n/**\n * Bin points into pointy-top hexagons of circumradius `radius`, in the points' OWN units (data space, not\n * pixels). Points with a NaN coordinate are skipped. Centres of `-0` are folded to `0` (blazing-the-nets\n * `main` `lib/data/aggregate.ts:101`), so equal hexes share one key. Throws `InputError` unless `radius` is a finite\n * number greater than 0.\n *\n * Each point goes to the centre upstream d3-hexbin picks, which is close to but not exactly the nearest one: it\n * compares two candidates' squared distances in lattice units (x over √3·radius, y over 1.5·radius; `:43`), so\n * about 1-2% of points sit just outside the hexagon drawn for their bin (on the real BKN fixture, 22 of 2000 shots\n * at radius 10 and 37 of 2000 at radius 15). That is faithful to d3-hexbin and to blazing-the-nets, which draw the\n * same; do not \"fix\" it. Squares (`squarebin`) contain every point they bin.\n *\n * @example\n * ```ts\n * import { hexbin } from \"@sportsdataverse/sdvplot/bins\";\n *\n * // three Brooklyn shots of 2025-26 in tenths of a foot from the hoop (sportsdataverse-data nba_stats_shots): the\n * // two at the rim share a 1 ft hexagon, so this is 2\n * const shots = [\n * { x_legacy: 0, y_legacy: 0 },\n * { x_legacy: -1, y_legacy: 7 },\n * { x_legacy: -44, y_legacy: 252 },\n * ];\n * hexbin(shots, { radius: 10, x: (s) => s.x_legacy, y: (s) => s.y_legacy }).length;\n * ```\n */\nexport function hexbin<T>(\n points: readonly T[],\n o: { radius: number; x: (p: T) => number; y: (p: T) => number },\n): BinOf<T>[] {\n if (!(Number.isFinite(o.radius) && o.radius > 0))\n throw new InputError(`hexbin radius must be a finite number > 0, got ${String(o.radius)}`);\n const dx = o.radius * 2 * Math.sin(THIRD_PI);\n const dy = o.radius * 1.5;\n const byId = new Map<string, BinOf<T>>();\n const bins: BinOf<T>[] = [];\n for (const p of points) {\n let px = +o.x(p);\n let py = +o.y(p);\n if (Number.isNaN(px) || Number.isNaN(py)) continue;\n py = py / dy;\n let pj = Math.round(py);\n px = px / dx - (pj & 1) / 2;\n let pi = Math.round(px);\n const py1 = py - pj;\n if (Math.abs(py1) * 3 > 1) {\n const px1 = px - pi;\n const pi2 = pi + (px < pi ? -1 : 1) / 2;\n const pj2 = pj + (py < pj ? -1 : 1);\n const px2 = px - pi2;\n const py2 = py - pj2;\n if (px1 * px1 + py1 * py1 > px2 * px2 + py2 * py2) {\n pi = pi2 + (pj & 1 ? 1 : -1) / 2;\n pj = pj2;\n }\n }\n const id = `${pi}-${pj}`;\n let bin = byId.get(id);\n if (bin === undefined) {\n bin = Object.assign([] as T[], { x: (pi + (pj & 1) / 2) * dx || 0, y: pj * dy || 0 });\n byId.set(id, bin);\n bins.push(bin);\n }\n bin.push(p);\n }\n return bins;\n}\n\n/** The six vertices of a pointy-top hexagon of circumradius `r` around (0, 0), d3-hexbin's order. */\nexport function hexagonPoints(r: number): [number, number][] {\n return ANGLES.map((a): [number, number] => [Math.sin(a) * r, -Math.cos(a) * r]);\n}\n\n/**\n * An SVG path for a hexagon of circumradius `r` centred on the current point, byte-identical to d3-hexbin's\n * `hexbin.hexagon(r)`: `selection.attr(\"transform\", translate(cx, cy)).attr(\"d\", hexagonPath(r))`.\n *\n * @example\n * ```ts\n * import { hexagonPath } from \"@sportsdataverse/sdvplot/bins\";\n *\n * hexagonPath(10);\n * ```\n */\nexport function hexagonPath(r: number): string {\n let x0 = 0;\n let y0 = 0;\n const steps = ANGLES.map((a) => {\n const x1 = Math.sin(a) * r;\n const y1 = -Math.cos(a) * r;\n const step = `${x1 - x0},${y1 - y0}`;\n x0 = x1;\n y0 = y1;\n return step;\n });\n return `m${steps.join(\"l\")}z`;\n}\n","// squarebin() is the square twin of hexbin(): the same bin record (BinOf), first-seen order, NaN skipped, -0\n// folded. binner() picks either lattice from one plain options object, so everything downstream of the bins is\n// shape-agnostic (spec J38).\nimport { InputError } from \"../errors.js\";\nimport { type BinOf, hexagonPath, hexagonPoints, hexbin } from \"./hexbin.js\";\n\n/** The cell shape of a lattice. */\nexport type BinShape = \"hex\" | \"square\";\n\n/**\n * A cell lattice as plain JSON: pointy-top hexagons of circumradius `radius` (d3-hexbin's), squares of side `side`,\n * or (`equalArea: true`) the square with the same area as a hexagon of circumradius `radius`.\n */\nexport type BinnerOptions =\n | { readonly shape?: \"hex\"; readonly radius: number }\n | { readonly shape: \"square\"; readonly side: number }\n | { readonly shape: \"square\"; readonly radius: number; readonly equalArea: true };\n\n/** A lattice with `equalArea` resolved: what an index stores so a later binning reproduces the same cells. */\nexport type Lattice =\n | { readonly shape?: \"hex\"; readonly radius: number }\n | { readonly shape: \"square\"; readonly side: number };\n\n/** One lattice, either shape. */\nexport interface Binner {\n /** `\"hex\"` or `\"square\"`. */\n readonly shape: BinShape;\n /** The full cell size in the points' units: the hexagon's circumradius, or the square's side. */\n readonly size: number;\n /** The resolved options: `{ radius }` for hexagons, `{ shape: \"square\", side }` for squares. */\n readonly lattice: Lattice;\n /** Bin points (`hexbin` or `squarebin`). */\n bins<T>(points: readonly T[], o: { x: (p: T) => number; y: (p: T) => number }): BinOf<T>[];\n /** An SVG path for one cell of size `s` (default `size`) centred on the current point. */\n cell(s?: number): string;\n}\n\n/** The option the caller passed, if it is a positive finite number; otherwise an InputError naming it. */\nfunction positive(name: \"radius\" | \"side\", v: number): number {\n if (!(Number.isFinite(v) && v > 0))\n throw new InputError(`binner ${name} must be a finite number > 0, got ${String(v)}`);\n return v;\n}\n\n/** √(3√3/2) ≈ 1.6119: a hexagon of circumradius r has area (3√3/2)·r², so the equal-area square has side r·√(3√3/2). */\nconst EQUAL_AREA = Math.sqrt(1.5 * Math.sqrt(3));\n\n/**\n * Bin points into axis-aligned squares of side `side`, in the points' OWN units. As in d3-hexbin's lattice, a cell\n * is centred on the origin, so centres are `(i·side, j·side)`. Cells are half-open, `[c − side/2, c + side/2)` on\n * each axis: a point exactly on an edge goes to the higher cell, because the cell index is `Math.round(x / side)`,\n * the rounding d3-hexbin uses (`hexbin.js:33-34`). Bins come out in first-seen order; points with a NaN coordinate\n * are skipped; centres of `-0` fold to `0`. Throws `InputError` unless `side` is a finite number greater than 0.\n *\n * @example\n * ```ts\n * import { squarebin } from \"@sportsdataverse/sdvplot/bins\";\n *\n * // three Brooklyn shots of 2025-26 (sportsdataverse-data nba_stats_shots); x = 5 sits on a cell edge and goes to\n * // the higher cell: [[0, 0, 1], [0, 10, 1], [10, 90, 1]]\n * const shots = [\n * { x_legacy: 0, y_legacy: 0 },\n * { x_legacy: -1, y_legacy: 7 },\n * { x_legacy: 5, y_legacy: 91 },\n * ];\n * squarebin(shots, { side: 10, x: (s) => s.x_legacy, y: (s) => s.y_legacy }).map((b) => [b.x, b.y, b.length]);\n * ```\n */\nexport function squarebin<T>(\n points: readonly T[],\n o: { side: number; x: (p: T) => number; y: (p: T) => number },\n): BinOf<T>[] {\n const s = o.side;\n if (!(Number.isFinite(s) && s > 0))\n throw new InputError(`squarebin side must be a finite number > 0, got ${String(s)}`);\n const byId = new Map<string, BinOf<T>>();\n const bins: BinOf<T>[] = [];\n for (const p of points) {\n const px = +o.x(p);\n const py = +o.y(p);\n if (Number.isNaN(px) || Number.isNaN(py)) continue;\n const i = Math.round(px / s);\n const j = Math.round(py / s);\n const id = `${i}-${j}`;\n let bin = byId.get(id);\n if (bin === undefined) {\n bin = Object.assign([] as T[], { x: i * s || 0, y: j * s || 0 });\n byId.set(id, bin);\n bins.push(bin);\n }\n bin.push(p);\n }\n return bins;\n}\n\n/** The four corners of a square of side `s` around (0, 0), in the same turning order as `hexagonPoints`. */\nexport function squarePoints(s: number): [number, number][] {\n const h = s / 2;\n return [\n [-h, -h],\n [h, -h],\n [h, h],\n [-h, h],\n ];\n}\n\n/**\n * An SVG path for a square of side `s` centred on the current point, drawn the way `hexagonPath` is:\n * `selection.attr(\"transform\", translate(cx, cy)).attr(\"d\", squarePath(s))`.\n *\n * @example\n * ```ts\n * import { squarePath } from \"@sportsdataverse/sdvplot/bins\";\n *\n * squarePath(10); // \"m-5,-5h10v10h-10z\"\n * ```\n */\nexport function squarePath(s: number): string {\n return `m${-s / 2},${-s / 2}h${s}v${s}h${-s}z`;\n}\n\n/**\n * A cell's vertices around (0, 0), in the points' units: a hexagon of circumradius `size` (d3-hexbin's vertex\n * order) or a square of side `size`. Add a bin's centre to each to outline that cell as a polygon.\n *\n * @example\n * ```ts\n * import { cellPoints } from \"@sportsdataverse/sdvplot/bins\";\n *\n * cellPoints(\"square\", 10); // [[-5, -5], [5, -5], [5, 5], [-5, 5]]\n * ```\n */\nexport function cellPoints(shape: BinShape, size: number): [number, number][] {\n return shape === \"square\" ? squarePoints(size) : hexagonPoints(size);\n}\n/**\n * A cell's relative SVG path, `hexagonPath(size)` or `squarePath(size)`, drawn with a `translate` to each bin centre.\n *\n * @example\n * ```ts\n * import { cellPath } from \"@sportsdataverse/sdvplot/bins\";\n *\n * cellPath(\"square\", 10); // \"m-5,-5h10v10h-10z\"\n * ```\n */\nexport function cellPath(shape: BinShape, size: number): string {\n return shape === \"square\" ? squarePath(size) : hexagonPath(size);\n}\n\n/**\n * One binning API for both lattices: `binner({ radius: 15 })` is d3-hexbin's, `binner({ shape: \"square\", side: 15 })`\n * squares, and `binner({ shape: \"square\", radius: 15, equalArea: true })` squares of side 15·√(3√3/2) ≈ 24.18,\n * each the area of a radius-15 hexagon. Works on any x/y data, not just shots. Throws `InputError` on an unknown\n * `shape` or a size that is not a positive finite number, naming the option passed.\n *\n * @example\n * ```ts\n * import { binner } from \"@sportsdataverse/sdvplot/bins\";\n *\n * const b = binner({ shape: \"square\", radius: 10, equalArea: true }); // side 16.1185…: a radius-10 hexagon's area\n * // three Brooklyn shots of 2025-26 (sportsdataverse-data nba_stats_shots): the two at the rim share a square\n * const shots = [\n * { x_legacy: 0, y_legacy: 0 },\n * { x_legacy: -1, y_legacy: 7 },\n * { x_legacy: -44, y_legacy: 252 },\n * ];\n * b.bins(shots, { x: (s) => s.x_legacy, y: (s) => s.y_legacy }).map((c) => [c.x, c.y, c.length]);\n * ```\n */\nexport function binner(o: BinnerOptions): Binner {\n // Runtime checks for JS callers: the types already rule these out.\n const shape: unknown = o.shape;\n if (shape !== undefined && shape !== \"hex\" && shape !== \"square\")\n throw new InputError(`binner shape must be \"hex\" or \"square\", got ${String(shape)}`);\n if (o.shape !== \"square\") {\n const radius = positive(\"radius\", o.radius);\n return {\n shape: \"hex\",\n size: radius,\n lattice: { radius },\n bins: (points, a) => hexbin(points, { radius, x: a.x, y: a.y }),\n cell: (s = radius) => hexagonPath(s),\n };\n }\n if (!(\"side\" in o) && o.equalArea !== true)\n throw new InputError(\"binner square needs side, or radius with equalArea: true\");\n const side = \"side\" in o ? positive(\"side\", o.side) : positive(\"radius\", o.radius) * EQUAL_AREA;\n return {\n shape: \"square\",\n size: side,\n lattice: { shape: \"square\", side },\n bins: (points, a) => squarebin(points, { side, x: a.x, y: a.y }),\n cell: (s = side) => squarePath(s),\n };\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var SdvplotError = class extends Error {
|
|
3
|
+
constructor(message, options) {
|
|
4
|
+
super(message, options);
|
|
5
|
+
this.name = new.target.name;
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
var InputError = class extends SdvplotError {
|
|
9
|
+
};
|
|
10
|
+
var UnresolvedTeamError = class extends SdvplotError {
|
|
11
|
+
};
|
|
12
|
+
var UnsupportedTargetError = class extends SdvplotError {
|
|
13
|
+
};
|
|
14
|
+
var OptionalDependencyError = class extends SdvplotError {
|
|
15
|
+
};
|
|
16
|
+
var OfflineError = class extends SdvplotError {
|
|
17
|
+
};
|
|
18
|
+
var DownloadError = class extends OfflineError {
|
|
19
|
+
constructor(message, url, status) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.url = url;
|
|
22
|
+
this.status = status;
|
|
23
|
+
}
|
|
24
|
+
url;
|
|
25
|
+
status;
|
|
26
|
+
};
|
|
27
|
+
var handler = null;
|
|
28
|
+
var seen = /* @__PURE__ */ new Set();
|
|
29
|
+
function setWarningHandler(fn) {
|
|
30
|
+
handler = fn;
|
|
31
|
+
}
|
|
32
|
+
function resetWarnings() {
|
|
33
|
+
seen.clear();
|
|
34
|
+
}
|
|
35
|
+
function warn(key, message) {
|
|
36
|
+
if (seen.has(key)) return;
|
|
37
|
+
seen.add(key);
|
|
38
|
+
(handler ?? ((m) => console.warn(`[sdvplot] ${m}`)))(message);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { DownloadError, InputError, OfflineError, OptionalDependencyError, SdvplotError, UnresolvedTeamError, UnsupportedTargetError, resetWarnings, setWarningHandler, warn };
|
|
42
|
+
//# sourceMappingURL=chunk-E7G7VSLV.js.map
|
|
43
|
+
//# sourceMappingURL=chunk-E7G7VSLV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts"],"names":[],"mappings":";AAAO,IAAM,YAAA,GAAN,cAA2B,KAAA,CAAM;AAAA,EACtC,WAAA,CAAY,SAAiB,OAAA,EAAwB;AACnD,IAAA,KAAA,CAAM,SAAS,OAAO,CAAA;AACtB,IAAA,IAAA,CAAK,OAAO,GAAA,CAAA,MAAA,CAAW,IAAA;AAAA,EACzB;AACF;AACO,IAAM,UAAA,GAAN,cAAyB,YAAA,CAAa;AAAC;AACvC,IAAM,mBAAA,GAAN,cAAkC,YAAA,CAAa;AAAC;AAChD,IAAM,sBAAA,GAAN,cAAqC,YAAA,CAAa;AAAC;AAEnD,IAAM,uBAAA,GAAN,cAAsC,YAAA,CAAa;AAAC;AACpD,IAAM,YAAA,GAAN,cAA2B,YAAA,CAAa;AAAC;AACzC,IAAM,aAAA,GAAN,cAA4B,YAAA,CAAa;AAAA,EAC9C,WAAA,CACE,OAAA,EACS,GAAA,EACA,MAAA,EACT;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AAHJ,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAGX;AAAA,EAJW,GAAA;AAAA,EACA,MAAA;AAIb;AAGA,IAAI,OAAA,GAAiC,IAAA;AACrC,IAAM,IAAA,uBAAW,GAAA,EAAY;AACtB,SAAS,kBAAkB,EAAA,EAAiC;AACjE,EAAA,OAAA,GAAU,EAAA;AACZ;AACO,SAAS,aAAA,GAAsB;AACpC,EAAA,IAAA,CAAK,KAAA,EAAM;AACb;AAEO,SAAS,IAAA,CAAK,KAAa,OAAA,EAAuB;AACvD,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,EAAG;AACnB,EAAA,IAAA,CAAK,IAAI,GAAG,CAAA;AACZ,EAAA,CAAC,OAAA,KAAY,CAAC,CAAA,KAAc,OAAA,CAAQ,KAAK,CAAA,UAAA,EAAa,CAAC,CAAA,CAAE,CAAA,CAAA,EAAI,OAAO,CAAA;AACtE","file":"chunk-E7G7VSLV.js","sourcesContent":["export class SdvplotError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n }\n}\nexport class InputError extends SdvplotError {}\nexport class UnresolvedTeamError extends SdvplotError {}\nexport class UnsupportedTargetError extends SdvplotError {}\n/** An optional peer dependency (resvg, playwright, react) is not installed. */\nexport class OptionalDependencyError extends SdvplotError {}\nexport class OfflineError extends SdvplotError {}\nexport class DownloadError extends OfflineError {\n constructor(\n message: string,\n readonly url: string,\n readonly status?: number,\n ) {\n super(message);\n }\n}\n\nexport type WarningHandler = (message: string) => void;\nlet handler: WarningHandler | null = null;\nconst seen = new Set<string>();\nexport function setWarningHandler(fn: WarningHandler | null): void {\n handler = fn;\n}\nexport function resetWarnings(): void {\n seen.clear();\n}\n/** One warning per `key` per process (spec §4.3): a 10k-row join must not print 10k lines. */\nexport function warn(key: string, message: string): void {\n if (seen.has(key)) return;\n seen.add(key);\n (handler ?? ((m: string) => console.warn(`[sdvplot] ${m}`)))(message);\n}\n"]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { InputError } from './chunk-E7G7VSLV.js';
|
|
2
|
+
|
|
3
|
+
// src/contrast.ts
|
|
4
|
+
function hex6(color, opts = {}) {
|
|
5
|
+
let c = String(color).trim().replace(/^#+/, "");
|
|
6
|
+
if (c.length === 3 || c.length === 4) c = [...c].map((ch) => ch + ch).join("");
|
|
7
|
+
if (c.length !== 6 && c.length !== 8 || !/^[0-9a-fA-F]+$/.test(c))
|
|
8
|
+
throw new InputError(`not a hex color: ${JSON.stringify(color)}`);
|
|
9
|
+
if (c.length === 8 && c.slice(6).toLowerCase() !== "ff" && !opts.dropAlpha)
|
|
10
|
+
throw new InputError(
|
|
11
|
+
`${JSON.stringify(color)} has transparency; give an opaque color such as '#${c.slice(0, 6).toLowerCase()}'`
|
|
12
|
+
);
|
|
13
|
+
return `#${c.slice(0, 6).toLowerCase()}`;
|
|
14
|
+
}
|
|
15
|
+
var channels = (c) => [
|
|
16
|
+
Number.parseInt(c.slice(1, 3), 16),
|
|
17
|
+
Number.parseInt(c.slice(3, 5), 16),
|
|
18
|
+
Number.parseInt(c.slice(5, 7), 16)
|
|
19
|
+
];
|
|
20
|
+
function luminance(color) {
|
|
21
|
+
const [r, g, b] = channels(hex6(color)).map((v) => {
|
|
22
|
+
const s = v / 255;
|
|
23
|
+
return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
24
|
+
});
|
|
25
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
26
|
+
}
|
|
27
|
+
function contrast(a, b) {
|
|
28
|
+
const la = luminance(a);
|
|
29
|
+
const lb = luminance(b);
|
|
30
|
+
return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
|
|
31
|
+
}
|
|
32
|
+
function onColor(background) {
|
|
33
|
+
return contrast("#000000", background) >= contrast("#ffffff", background) ? "#000000" : "#ffffff";
|
|
34
|
+
}
|
|
35
|
+
function roundHalfEven(x) {
|
|
36
|
+
const f = Math.floor(x);
|
|
37
|
+
const d = x - f;
|
|
38
|
+
return d > 0.5 || d === 0.5 && f % 2 !== 0 ? f + 1 : f;
|
|
39
|
+
}
|
|
40
|
+
function mix(a, b, t) {
|
|
41
|
+
if (!(t >= 0 && t <= 1)) throw new InputError(`t must be in [0, 1], got ${t}`);
|
|
42
|
+
const ca = channels(hex6(a));
|
|
43
|
+
const cb = channels(hex6(b));
|
|
44
|
+
return `#${ca.map(
|
|
45
|
+
(v, i) => roundHalfEven(v * (1 - t) + cb[i] * t).toString(16).padStart(2, "0")
|
|
46
|
+
).join("")}`;
|
|
47
|
+
}
|
|
48
|
+
function solid(color, background = "#ffffff") {
|
|
49
|
+
const opaque = hex6(color, { dropAlpha: true });
|
|
50
|
+
let c = String(color).trim().replace(/^#+/, "");
|
|
51
|
+
if (c.length === 4) c = [...c].map((ch) => ch + ch).join("");
|
|
52
|
+
const alpha = c.length === 8 ? Number.parseInt(c.slice(6), 16) / 255 : 1;
|
|
53
|
+
return mix(background, opaque, alpha);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { contrast, hex6, luminance, mix, onColor, roundHalfEven, solid };
|
|
57
|
+
//# sourceMappingURL=chunk-J6R2LEJ7.js.map
|
|
58
|
+
//# sourceMappingURL=chunk-J6R2LEJ7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/contrast.ts"],"names":[],"mappings":"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","file":"chunk-J6R2LEJ7.js","sourcesContent":["import { InputError } from \"./errors.js\";\n\n/** A color as lowercase `#rrggbb`: accepts `#rgb`, `#rrggbb`, `#rgba`, `#rrggbbaa` (`#` optional). Transparency throws unless `dropAlpha`. */\nexport function hex6(color: string, opts: { dropAlpha?: boolean } = {}): string {\n let c = String(color).trim().replace(/^#+/, \"\");\n if (c.length === 3 || c.length === 4) c = [...c].map((ch) => ch + ch).join(\"\");\n if ((c.length !== 6 && c.length !== 8) || !/^[0-9a-fA-F]+$/.test(c))\n throw new InputError(`not a hex color: ${JSON.stringify(color)}`);\n if (c.length === 8 && c.slice(6).toLowerCase() !== \"ff\" && !opts.dropAlpha)\n throw new InputError(\n `${JSON.stringify(color)} has transparency; give an opaque color such as '#${c.slice(0, 6).toLowerCase()}'`,\n );\n return `#${c.slice(0, 6).toLowerCase()}`;\n}\n\nconst channels = (c: string): [number, number, number] => [\n Number.parseInt(c.slice(1, 3), 16),\n Number.parseInt(c.slice(3, 5), 16),\n Number.parseInt(c.slice(5, 7), 16),\n];\n\n/** WCAG relative luminance, 0 (black) to 1 (white). */\nexport function luminance(color: string): number {\n const [r, g, b] = channels(hex6(color)).map((v) => {\n const s = v / 255;\n return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;\n }) as [number, number, number];\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** WCAG contrast ratio, 1 (same) to 21 (black on white). */\nexport function contrast(a: string, b: string): number {\n const la = luminance(a);\n const lb = luminance(b);\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n}\n\n/** Black or white, whichever reads better on `background`. */\nexport function onColor(background: string): string {\n return contrast(\"#000000\", background) >= contrast(\"#ffffff\", background) ? \"#000000\" : \"#ffffff\";\n}\n\n/** Python's builtin round(): half to even (inputs are non-negative). */\nexport function roundHalfEven(x: number): number {\n const f = Math.floor(x);\n const d = x - f;\n return d > 0.5 || (d === 0.5 && f % 2 !== 0) ? f + 1 : f;\n}\n\n/** The color `t` of the way from `a` to `b` in sRGB (`t` in [0, 1]). */\nexport function mix(a: string, b: string, t: number): string {\n if (!(t >= 0 && t <= 1)) throw new InputError(`t must be in [0, 1], got ${t}`);\n const ca = channels(hex6(a));\n const cb = channels(hex6(b));\n return `#${ca\n .map((v, i) =>\n roundHalfEven(v * (1 - t) + (cb[i] as number) * t)\n .toString(16)\n .padStart(2, \"0\"),\n )\n .join(\"\")}`;\n}\n\n/** The opaque `#rrggbb` a hex color shows as over `background`: a translucent color is alpha-composited onto it. */\nexport function solid(color: string, background = \"#ffffff\"): string {\n const opaque = hex6(color, { dropAlpha: true });\n let c = String(color).trim().replace(/^#+/, \"\");\n if (c.length === 4) c = [...c].map((ch) => ch + ch).join(\"\");\n const alpha = c.length === 8 ? Number.parseInt(c.slice(6), 16) / 255 : 1;\n return mix(background, opaque, alpha);\n}\n"]}
|