@sportsdataverse/sdvtables 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -112
- package/data/table_themes.json +12136 -0
- package/data/table_themes.json.sha256 +1 -0
- package/dist/aggrid.d.ts +104 -0
- package/dist/aggrid.js +230 -0
- package/dist/aggrid.js.map +1 -0
- package/dist/chunk-3TJR7TKS.js +223 -0
- package/dist/chunk-3TJR7TKS.js.map +1 -0
- package/dist/{chunk-UCRLJGDL.js → chunk-R375P3MN.js} +963 -276
- package/dist/chunk-R375P3MN.js.map +1 -0
- package/dist/chunk-UTOVFIFT.js +15176 -0
- package/dist/chunk-UTOVFIFT.js.map +1 -0
- package/dist/{chunk-U6W6MWKW.js → chunk-VLF2S6VW.js} +7 -5
- package/dist/chunk-VLF2S6VW.js.map +1 -0
- package/dist/chunk-WBTFIGYK.js +189 -0
- package/dist/chunk-WBTFIGYK.js.map +1 -0
- package/dist/{engine-CkfxYXAL.d.ts → engine-BFIPJUlC.d.ts} +1 -1
- package/dist/export.d.ts +1 -1
- package/dist/export.js +4 -3
- package/dist/export.js.map +1 -1
- package/dist/html.d.ts +8 -5
- package/dist/html.js +4 -3
- package/dist/index.d.ts +121 -8
- package/dist/index.js +51 -3
- package/dist/index.js.map +1 -1
- package/dist/mui.d.ts +134 -0
- package/dist/mui.js +193 -0
- package/dist/mui.js.map +1 -0
- package/dist/react.d.ts +2 -2
- package/dist/react.js +3 -2
- package/dist/react.js.map +1 -1
- package/dist/spec-A88vJX5A.d.ts +748 -0
- package/dist/tanstack-table.d.ts +113 -0
- package/dist/tanstack-table.js +98 -0
- package/dist/tanstack-table.js.map +1 -0
- package/dist/{tokens-CpYFsFtf.d.ts → tokens-CHwBt9gs.d.ts} +1 -1
- package/package.json +40 -3
- package/dist/chunk-4VKYHOHL.js +0 -2081
- package/dist/chunk-4VKYHOHL.js.map +0 -1
- package/dist/chunk-U6W6MWKW.js.map +0 -1
- package/dist/chunk-UCRLJGDL.js.map +0 -1
- package/dist/spec-BEhLDtj_.d.ts +0 -430
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { applyHover,
|
|
2
|
-
import {
|
|
1
|
+
import { applyHover, renderInteractive, handleClick, handleInput, handleHover, handleKeydown, captureFocus, tableRenderOptions, renderToolbar, pagerLabel } from './chunk-3TJR7TKS.js';
|
|
2
|
+
import { loadTeamNames, assemble, renderParts, tableHTML } from './chunk-R375P3MN.js';
|
|
3
|
+
import { TableSpecError, THEME_NAMES } from './chunk-UTOVFIFT.js';
|
|
3
4
|
import { loadLeague, loadGsis } from '@sportsdataverse/sdvplot';
|
|
4
5
|
|
|
5
6
|
// src/html/hydrate.ts
|
|
@@ -121,7 +122,8 @@ function hydrate(el, table) {
|
|
|
121
122
|
// src/html/index.ts
|
|
122
123
|
function leaguesOf(spec) {
|
|
123
124
|
const out = /* @__PURE__ */ new Set();
|
|
124
|
-
for (const c of spec.columns)
|
|
125
|
+
for (const c of spec.columns)
|
|
126
|
+
if ("league" in c && c.kind !== "headshot" && c.league !== void 0) out.add(c.league);
|
|
125
127
|
if (spec.theme.name === "sdvTeam" && spec.theme.options?.league)
|
|
126
128
|
out.add(spec.theme.options.league);
|
|
127
129
|
return [...out];
|
|
@@ -175,5 +177,5 @@ function themePreview(spec, rows, themes, o = {}) {
|
|
|
175
177
|
}
|
|
176
178
|
|
|
177
179
|
export { hydrate, leaguesOf, prepare, renderHTML, renderHTMLAsync, themePreview, toElement };
|
|
178
|
-
//# sourceMappingURL=chunk-
|
|
179
|
-
//# sourceMappingURL=chunk-
|
|
180
|
+
//# sourceMappingURL=chunk-VLF2S6VW.js.map
|
|
181
|
+
//# sourceMappingURL=chunk-VLF2S6VW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/html/hydrate.ts","../src/html/index.ts"],"names":[],"mappings":"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","file":"chunk-VLF2S6VW.js","sourcesContent":["import type { Table } from \"../engine.js\";\nimport { TableSpecError } from \"../errors.js\";\nimport {\n applyHover,\n captureFocus,\n handleClick,\n handleHover,\n handleInput,\n handleKeydown,\n} from \"./controls.js\";\nimport { pagerLabel, renderToolbar, tableRenderOptions } from \"./interactive.js\";\nimport { renderParts, tableHTML } from \"./parts.js\";\n\n/** I2: each element's live binding, so hydrating it again replaces the old one (see `replace`). */\nconst live = new WeakMap<Element, () => void>();\n/** Bodies a teardown left behind the engine (it dropped a render still owed); the next hydrate of one redraws it. */\n// ponytail: exists for React StrictMode's double effect (mount, cleanup, mount); weak, so it never holds a body alive\nconst behind = new WeakSet<Element>();\n\nconst edge = (button: Element | null, atEdge: boolean): void => {\n if (atEdge) button?.setAttribute(\"aria-disabled\", \"true\");\n else button?.removeAttribute(\"aria-disabled\");\n};\n\n/**\n * Progressive enhancement for markup produced by `renderHTML(table)`: one delegated click + input listener drive the\n * engine; each change re-renders ONLY the table block (`[data-sdv-body]`) and the pager, once, on the next animation\n * frame, however many changes land in it (the engine state itself moves synchronously). One exception: a click,\n * pointer move or key on the table while a change to its rows (a sort, filter, page turn or `setRows`) waits for that\n * frame draws it at once, so the event never reads an old row as the one now at its index; a click or key on a row\n * this replaced acts on nothing, and Enter or Space on it is consumed, so the page does not scroll. A change that keeps\n * the rows (a selection, the cursor, a keyed row re-sent as a new object) waits for its frame. Toolbar inputs are\n * rebuilt only when a column is hidden or shown; otherwise their values are synced from the engine state, so an\n * external filter change shows in the box. A row hover (the pointer's, or a linked figure's through `linkSelection`)\n * re-renders nothing: the row gets the `sdvt-hover` class, which survives the next re-render; a hover the engine\n * already holds (`getHover`) shows on attach. A focused control, or a focusable element inside a rendered cell, that\n * the re-render replaced gets focus back (shadow-root mounts included). Attaching does NOT reconcile the SSR markup\n * against the engine state: render the markup from the same table state you hydrate. Hydrating an element again\n * replaces its previous binding (HMR, client-side navigation, an effect without cleanup) after drawing any change that\n * binding still owed, so every control still acts once. The returned teardown is idempotent and drops a render still\n * owed; hydrating that element again redraws it (on the next frame, or at once for an event that comes first), so React\n * StrictMode's mount, cleanup, mount leaves no stale rows. Until the teardown runs, the table's subscriber keeps `el`\n * alive as long as the table lives.\n */\nexport function hydrate<Row>(el: Element, table: Table<Row>): () => void {\n const body = el.querySelector(\"[data-sdv-body]\");\n if (!body)\n throw new TableSpecError(\n \"hydrate: no [data-sdv-body] in element; render it with renderHTML(table) first\",\n );\n live.get(el)?.();\n\n let hidden = table.state.hidden; // the engine replaces this array only when a column is hidden or shown\n // the rows the body shows: the SSR markup's until the first render; unknown (null) on a body a teardown left behind,\n // so an event before its redraw frame redraws first, even when the engine now holds no row\n let drawn: readonly Row[] | null = behind.has(body) ? null : table.rows;\n const render = (): void => {\n cancel = undefined;\n const root = el.getRootNode() as Document | ShadowRoot;\n const restore = captureFocus(el);\n const p = renderParts(table.spec, table.rows, { css: \"none\", ...tableRenderOptions(table) });\n body.innerHTML = tableHTML(p);\n drawn = table.rows; // after the write: a render that throws leaves the rows the body still shows on record\n // M8: a hidden column's filter input leaves the toolbar (and comes back), as in renderHTML(table) and <SdvTable/>\n const toolbar = el.querySelector(\".sdvt-toolbar\");\n if (toolbar && table.state.hidden !== hidden) toolbar.outerHTML = renderToolbar(table, p.labels);\n hidden = table.state.hidden;\n const label = el.querySelector(\"[data-sdv-page-label]\");\n const page = pagerLabel(table);\n if (label && label.textContent !== page) label.textContent = page; // M6: an unchanged live region is not re-announced\n edge(el.querySelector('[data-sdv-page=\"prev\"]'), table.state.page === 0);\n edge(el.querySelector('[data-sdv-page=\"next\"]'), table.state.page >= table.pageCount - 1);\n // I1: an external setFilter / setGlobalFilter / clear moves the engine state, not the box; a focused input wins\n for (const input of Array.from(\n el.querySelectorAll<HTMLInputElement>(\"[data-sdv-filter],[data-sdv-global-filter]\"),\n )) {\n if (input === root.activeElement) continue;\n const key = input.getAttribute(\"data-sdv-filter\");\n const f = key === null ? table.state.globalFilter : table.state.filters[key];\n const want = typeof f === \"string\" ? f : \"\";\n if (input.value !== want) input.value = want;\n }\n applyHover(el, table, table.getHover()); // the body was rebuilt: a linked figure's hover survives it\n restore();\n };\n // M6: one render per animation frame however many changes land in it (typing in an unpaged 1000-row table\n // re-rendered ~660 KB per keystroke); a timer where there is no requestAnimationFrame\n let cancel: (() => void) | undefined;\n const schedule = (): void => {\n if (cancel) return;\n if (typeof requestAnimationFrame === \"function\") {\n const id = requestAnimationFrame(render);\n cancel = () => cancelAnimationFrame(id);\n } else {\n const id = setTimeout(render, 0);\n cancel = () => clearTimeout(id);\n }\n };\n // a row event while a render that changes the rows is pending would read the old body's data-row against the new\n // rows (LV's row as BUF): render first, so the target is a current row, or a replaced one that names no row. A\n // pending render that keeps the rows (a selection, the cursor) leaves data-row right, and the target in place.\n // A keyed row re-sent as a new object (a live feed) keeps its id, so the old body's data-row still names it: it moved\n // only when its object AND its id differ. Never ids saved at render: without a rowKey those are positions, so the\n // old row 3 would read as whatever is at index 3 now; the engine's id of an old object is \"\" (gone) or its new index\n const same = (r: Row, i: number): boolean => {\n const was = drawn?.[i] as Row; // the lengths match\n return r === was || table.rowId(r) === table.rowId(was);\n };\n const flush = (): boolean => {\n const rows = table.rows;\n if (!cancel || (rows.length === drawn?.length && rows.every(same))) return false;\n cancel();\n render();\n return true;\n };\n const onClick = (e: Event): void => {\n flush();\n handleClick(table, e.target);\n };\n const onInput = (e: Event): void => handleInput(table, e.target);\n const onHover = (e: Event): void => {\n flush();\n handleHover(table, e.target); // J31 (A7)\n };\n const onKeydown = (e: Event): void => {\n const k = e as KeyboardEvent;\n // the flush replaced the focused body row: Enter and Space act on nothing, and Space must not scroll the page. A\n // row only: a sort button rebuilt under focus gets it back, and the browser's activation of it still sorts\n if (\n flush() &&\n (k.key === \" \" || k.key === \"Enter\") &&\n (k.target as Element).matches(\".sdvt-row[data-row]\")\n )\n k.preventDefault();\n handleKeydown(table, el, k); // Task 10\n };\n\n el.addEventListener(\"click\", onClick);\n el.addEventListener(\"input\", onInput);\n el.addEventListener(\"mouseover\", onHover);\n el.addEventListener(\"mouseleave\", onHover);\n el.addEventListener(\"keydown\", onKeydown);\n // J31: a hover changes no state, and re-rendering on it would replace the row under the pointer: a class toggle\n const unsubscribe = table.subscribe((e) => {\n if (e.type !== \"hover\") schedule();\n // a pending render means the DOM still shows the old rows, which data-row would misread: render applies it\n else if (cancel === undefined) applyHover(el, table, e.id);\n });\n applyHover(el, table, table.getHover()); // a hover set before this attached (a linked figure's) shows now\n if (behind.delete(body)) schedule(); // a teardown dropped a render this body still owes (StrictMode's cleanup)\n const teardown = (): void => {\n if (live.get(el) === replace) live.delete(el);\n unsubscribe();\n if (cancel) {\n cancel();\n behind.add(body); // the body stays behind the engine until the next hydrate of it redraws\n }\n cancel = undefined;\n el.removeEventListener(\"click\", onClick);\n el.removeEventListener(\"input\", onInput);\n el.removeEventListener(\"mouseover\", onHover);\n el.removeEventListener(\"mouseleave\", onHover);\n el.removeEventListener(\"keydown\", onKeydown);\n };\n // M3: hydrating `el` again first draws a change this binding still owes, so the next one starts from a current body\n // (its `drawn` is the engine's rows); a plain teardown drops it, since the element may no longer be ours to write,\n // and leaves the body `behind` for whichever hydrate comes next\n const replace = (): void => {\n if (cancel) {\n cancel();\n render();\n }\n teardown();\n };\n live.set(el, replace);\n return teardown;\n}\n","// src/html/index.ts — the public renderer entry; the body lives in parts.ts (Phase 5 Task 2).\nimport { loadGsis, loadLeague } from \"@sportsdataverse/sdvplot\";\nimport type { League } from \"@sportsdataverse/sdvplot\";\nimport type { Table } from \"../engine.js\";\nimport { TableSpecError } from \"../errors.js\";\nimport type { Density, TableSpec } from \"../spec.js\";\nimport { loadTeamNames } from \"../team-name.js\";\nimport { THEME_NAMES } from \"../themes/index.js\";\nimport { renderInteractive } from \"./interactive.js\";\nimport { type RenderOptions, assemble, labelOf, renderParts } from \"./parts.js\";\n\nexport type { ColumnScale, RenderContext } from \"./cells.js\";\nexport { fontsLink } from \"./fonts.js\";\nexport { ICON_NAMES } from \"./icons.js\";\nexport type { IconName } from \"../spec.js\";\nexport type { GoogleFont } from \"../themes/tokens.js\";\nexport type { RenderOptions, RenderedParts } from \"./parts.js\";\nexport {\n assemble,\n attrsText,\n fontsLinkFor,\n labelOf,\n renderParts,\n sortAria,\n styleSheet,\n tableHTML,\n themeKey,\n} from \"./parts.js\";\n\n/** Leagues whose shards the spec needs (team-aware columns + the sdvTeam theme). Headshot columns need no shard: ESPN ids are pure URLs. */\nexport function leaguesOf<Row>(spec: TableSpec<Row>): League[] {\n const out = new Set<League>();\n for (const c of spec.columns)\n if (\"league\" in c && c.kind !== \"headshot\" && c.league !== undefined) out.add(c.league);\n if (spec.theme.name === \"sdvTeam\" && spec.theme.options?.league)\n out.add(spec.theme.options.league as League);\n return [...out];\n}\n/** The documented preparation step: every league shard the spec names, plus the gsis map when a headshot column uses gsis ids (J27). Task 7 adds team names. */\nexport async function prepare<Row>(spec: TableSpec<Row>): Promise<void> {\n const gsis = spec.columns.some((c) => c.kind === \"headshot\" && c.idSystem === \"gsis\");\n await Promise.all([...leaguesOf(spec).map((l) => loadLeague(l)), ...(gsis ? [loadGsis()] : [])]);\n await loadTeamNames(leaguesOf(spec));\n}\n\nfunction isTable<Row>(x: TableSpec<Row> | Table<Row>): x is Table<Row> {\n return typeof (x as Table<Row>).subscribe === \"function\";\n}\n/**\n * Static markup from a spec, or the interactive document (toolbar, aria-sort headers, pager) from a `createTable`\n * instance.\n *\n * A spec renders as one HTML string, synchronously: the theme as a scoped `<style>` (with `css: \"none\"` the host\n * page emits `styleSheet(spec)` once instead), then the table. Cells that read league data (logos, team names) need\n * `await prepare(spec)` first, or use `renderHTMLAsync`. Throws `TableSpecError` for a column the rows lack or an\n * unknown theme.\n *\n * @example\n * ```ts\n * import { defineTable } from \"@sportsdataverse/sdvtables\";\n * import { renderHTML } from \"@sportsdataverse/sdvtables/html\";\n *\n * const rows = [\n * { team: \"BUF\", pf: 525, pa: 368 },\n * { team: \"DEN\", pf: 425, pa: 311 },\n * { team: \"LAC\", pf: 402, pa: 301 },\n * ];\n * const spec = defineTable<(typeof rows)[number]>()\n * .columns((c) => [c.text(\"team\"), c.int(\"pf\", { label: \"Points for\" }), c.int(\"pa\", { label: \"Points against\" })])\n * .theme(\"athletic\")\n * .build();\n * renderHTML(spec, rows);\n * ```\n */\nexport function renderHTML<Row>(table: Table<Row>, opts?: RenderOptions): string;\nexport function renderHTML<Row>(spec: TableSpec<Row>, rows: readonly Row[], opts?: RenderOptions): string;\nexport function renderHTML<Row>(\n a: TableSpec<Row> | Table<Row>,\n b?: readonly Row[] | RenderOptions,\n c: RenderOptions = {},\n): string {\n if (isTable(a)) return renderInteractive(a, (b as RenderOptions | undefined) ?? {});\n return assemble(renderParts(a, (b as readonly Row[] | undefined) ?? [], c));\n}\nexport { createTable } from \"../engine.js\";\nexport { handleClick, handleHover, handleInput, handleKeydown, keyAction, rowIdAt } from \"./controls.js\";\nexport type { KeyAction, KeyContext } from \"./controls.js\";\nexport { hydrate } from \"./hydrate.js\";\nexport {\n pagerLabel,\n renderInteractive,\n renderPager,\n renderToolbar,\n tableRenderOptions,\n} from \"./interactive.js\";\nexport async function renderHTMLAsync<Row>(\n spec: TableSpec<Row>,\n rows: readonly Row[],\n opts: RenderOptions = {},\n): Promise<string> {\n await prepare(spec);\n return renderHTML(spec, rows, opts);\n}\nexport function toElement<Row>(\n spec: TableSpec<Row>,\n rows: readonly Row[],\n opts: RenderOptions = {},\n): HTMLElement {\n if (typeof document === \"undefined\")\n throw new TableSpecError(\"toElement needs a DOM (browser, happy-dom or jsdom); use renderHTML in Node\");\n const t = document.createElement(\"template\");\n t.innerHTML = renderHTML(spec, rows, opts).trim();\n const el = t.content.querySelector(\"div.sdvt\");\n if (!(el instanceof HTMLElement)) throw new TableSpecError(\"renderHTML produced no wrapper\");\n // the fonts <link> goes to <head> once per href (A100), so the theme fonts load without a duplicate per table\n const link = t.content.querySelector(\"link\");\n const href = link?.getAttribute(\"href\");\n if (\n link &&\n href &&\n !Array.from(document.head.querySelectorAll(\"link\")).some((l) => l.getAttribute(\"href\") === href)\n )\n document.head.append(link);\n return el;\n}\n/**\n * gt_theme_preview (_themes.py:2058-2064): one HTML string per theme, first n rows, compact; sdvTeam shown with\n * league nfl and no team.\n *\n * @example\n * ```ts\n * import { defineTable } from \"@sportsdataverse/sdvtables\";\n * import { themePreview } from \"@sportsdataverse/sdvtables/html\";\n *\n * const rows = [\n * { team: \"KC\", wins: 15, losses: 2 },\n * { team: \"BUF\", wins: 13, losses: 4 },\n * ];\n * const spec = defineTable<(typeof rows)[number]>()\n * .columns((c) => [c.text(\"team\"), c.int(\"wins\"), c.int(\"losses\")])\n * .build();\n * Object.values(themePreview(spec, rows, [\"sdv\", \"athletic\", \"midnight\"])).join(\"\\n\");\n * ```\n */\nexport function themePreview<Row>(\n spec: TableSpec<Row>,\n rows: readonly Row[],\n themes?: readonly string[],\n o: { n?: number; density?: Density } = {},\n): Record<string, string> {\n const n = o.n ?? 5;\n if (!Number.isInteger(n) || n < 1) throw new TableSpecError(`n must be a positive whole number, got ${n}`);\n const out: Record<string, string> = {};\n for (const name of themes ?? THEME_NAMES)\n out[name] = renderHTML(\n {\n ...spec,\n theme: {\n name,\n density: o.density ?? \"compact\",\n ...(name === \"sdvTeam\" ? { options: { league: \"nfl\" } } : {}),\n },\n },\n rows.slice(0, n),\n );\n return out;\n}\nexport { labelOf as columnLabel };\n"]}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { scaleNoteOf } from './chunk-R375P3MN.js';
|
|
2
|
+
import { isBlank, TableSpecError, toNumber, ordinal, comparatorFor, withMissingLast, formatNumber, scaledNumber } from './chunk-UTOVFIFT.js';
|
|
3
|
+
import { hex6, solid, onColor, logoUrlSync, headshotUrl, warn, InputError, checkAlpha, resolveSync } from '@sportsdataverse/sdvplot';
|
|
4
|
+
|
|
5
|
+
var cell = (v) => isBlank(v) ? "" : String(v).trim();
|
|
6
|
+
var value = (row, key) => row == null ? void 0 : row[key];
|
|
7
|
+
var opaque = (t) => t.bg === "transparent" ? "#ffffff" : hex6(t.bg);
|
|
8
|
+
var alphaHex = (a) => Math.round(checkAlpha(a) * 255).toString(16).padStart(2, "0");
|
|
9
|
+
var tint = (color, alpha, bg) => {
|
|
10
|
+
const fill = solid(`${hex6(color, { dropAlpha: true })}${alphaHex(alpha)}`, bg);
|
|
11
|
+
return { fill, ink: onColor(fill) };
|
|
12
|
+
};
|
|
13
|
+
function teamIds(rows, key, league, o) {
|
|
14
|
+
const raw = [...new Set(rows.map((r) => cell(value(r, key))).filter((v) => v !== ""))];
|
|
15
|
+
const ids = resolveSync(raw, league, {
|
|
16
|
+
...o.season === void 0 ? {} : { season: o.season },
|
|
17
|
+
...o.idSystem === void 0 ? {} : { idSystem: o.idSystem },
|
|
18
|
+
strict: o.strict ?? false
|
|
19
|
+
});
|
|
20
|
+
const out = /* @__PURE__ */ new Map();
|
|
21
|
+
raw.forEach((v, i) => {
|
|
22
|
+
const id = ids[i];
|
|
23
|
+
if (id !== void 0) out.set(v, id);
|
|
24
|
+
});
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
function palette(teamColorPalette, league, ids, which, bg) {
|
|
28
|
+
const colors = teamColorPalette(league, ids, {
|
|
29
|
+
idSystem: "team_id",
|
|
30
|
+
background: bg,
|
|
31
|
+
...which === void 0 ? {} : { which }
|
|
32
|
+
});
|
|
33
|
+
return new Map(ids.map((id, i) => [id, colors[i] ?? "#808080"]));
|
|
34
|
+
}
|
|
35
|
+
function teamColorOf(col, rows, bg, teamColorPalette) {
|
|
36
|
+
const ids = teamIds(rows, col.key, col.league, col);
|
|
37
|
+
const colors = palette(teamColorPalette, col.league, [...new Set(ids.values())], col.which, bg);
|
|
38
|
+
return (raw) => {
|
|
39
|
+
const id = ids.get(cell(raw));
|
|
40
|
+
return (id === void 0 ? void 0 : colors.get(id)) ?? col.naColor;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function teamRowFills(rows, tr, bg, teamColorPalette) {
|
|
44
|
+
const alpha = tr.alpha ?? 0.4;
|
|
45
|
+
const ids = teamIds(rows, tr.key, tr.league, tr);
|
|
46
|
+
const colors = palette(teamColorPalette, tr.league, [...new Set(ids.values())], tr.which, bg);
|
|
47
|
+
const out = /* @__PURE__ */ new Map();
|
|
48
|
+
for (const [v, id] of ids) {
|
|
49
|
+
const color = colors.get(id);
|
|
50
|
+
if (color !== void 0) out.set(v, tint(color, alpha, bg));
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
function checkHeight(h, kind) {
|
|
55
|
+
if (!(Number.isFinite(h) && h >= 1))
|
|
56
|
+
throw new TableSpecError(`${kind} height must be a number of pixels >= 1, got ${String(h)}`);
|
|
57
|
+
return h;
|
|
58
|
+
}
|
|
59
|
+
function logoMarks(col, rows) {
|
|
60
|
+
const marks = /* @__PURE__ */ new Map();
|
|
61
|
+
for (const [v, id] of teamIds(rows, col.key, col.league, col)) {
|
|
62
|
+
const url = logoUrlSync(id, col.league, {
|
|
63
|
+
...col.season === void 0 ? {} : { season: col.season },
|
|
64
|
+
...col.variant === void 0 ? {} : { variant: col.variant },
|
|
65
|
+
markType: col.kind,
|
|
66
|
+
idSystem: "team_id"
|
|
67
|
+
});
|
|
68
|
+
if (url !== void 0) marks.set(v, { id, url });
|
|
69
|
+
}
|
|
70
|
+
return marks;
|
|
71
|
+
}
|
|
72
|
+
function headshotUrls(col, rows, who) {
|
|
73
|
+
const urls = /* @__PURE__ */ new Map();
|
|
74
|
+
for (const v of new Set(rows.map((r) => cell(value(r, col.key))))) {
|
|
75
|
+
if (v === "") continue;
|
|
76
|
+
const url = headshotUrl(v, col.league, { idSystem: col.idSystem });
|
|
77
|
+
if (url === void 0)
|
|
78
|
+
warn(
|
|
79
|
+
`sdvtables:${who.ns}:headshot:${col.league}:${v}`,
|
|
80
|
+
`${who.fn}: no ${col.league} headshot for player ${JSON.stringify(v)}: the cell keeps the id`
|
|
81
|
+
);
|
|
82
|
+
else urls.set(v, url);
|
|
83
|
+
}
|
|
84
|
+
return urls;
|
|
85
|
+
}
|
|
86
|
+
function scaleNotes(spec) {
|
|
87
|
+
const scaled = /* @__PURE__ */ new Map();
|
|
88
|
+
const labelSuffix = /* @__PURE__ */ new Map();
|
|
89
|
+
for (const d of spec.decorations) {
|
|
90
|
+
if (d.type !== "scaleNote") continue;
|
|
91
|
+
const { suffix } = scaleNoteOf(d);
|
|
92
|
+
for (const k of d.columns) {
|
|
93
|
+
scaled.set(k, { divisor: d.divisor, decimals: d.decimals });
|
|
94
|
+
if (d.where !== "sourceNote") labelSuffix.set(k, ` ${suffix}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return { scaled, labelSuffix };
|
|
98
|
+
}
|
|
99
|
+
var numberFormat = (f) => (v) => {
|
|
100
|
+
const n = toNumber(v);
|
|
101
|
+
return n === null ? "" : f(n);
|
|
102
|
+
};
|
|
103
|
+
function formatterFor(col, scaled) {
|
|
104
|
+
switch (col.kind) {
|
|
105
|
+
case "num": {
|
|
106
|
+
const sc = scaled.get(col.key);
|
|
107
|
+
if (sc) return numberFormat((n) => scaledNumber(n, sc));
|
|
108
|
+
return numberFormat(
|
|
109
|
+
(n) => formatNumber(n, {
|
|
110
|
+
...col.digits === void 0 ? {} : { digits: col.digits },
|
|
111
|
+
big: col.big ?? false,
|
|
112
|
+
prefix: col.prefix ?? "",
|
|
113
|
+
suffix: col.suffix ?? "",
|
|
114
|
+
forceSign: col.forceSign ?? false
|
|
115
|
+
})
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
case "int": {
|
|
119
|
+
const sc = scaled.get(col.key);
|
|
120
|
+
return numberFormat((n) => sc ? scaledNumber(n, sc) : formatNumber(n, { digits: 0, big: true }));
|
|
121
|
+
}
|
|
122
|
+
case "pct":
|
|
123
|
+
return numberFormat((n) => `${formatNumber(col.scale ? n * 100 : n, { digits: col.digits })}%`);
|
|
124
|
+
case "rank":
|
|
125
|
+
return numberFormat(ordinal);
|
|
126
|
+
default:
|
|
127
|
+
return void 0;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function rankParts(v) {
|
|
131
|
+
const n = toNumber(v);
|
|
132
|
+
if (n === null) return null;
|
|
133
|
+
const num = String(Math.trunc(n));
|
|
134
|
+
return { num, suffix: ordinal(n).slice(num.length) };
|
|
135
|
+
}
|
|
136
|
+
function missingLast(col, rows) {
|
|
137
|
+
const compare = comparatorFor(col, rows);
|
|
138
|
+
return (dir) => withMissingLast(compare, dir);
|
|
139
|
+
}
|
|
140
|
+
function pxWidth(col, who) {
|
|
141
|
+
if (col.width === void 0) return void 0;
|
|
142
|
+
const m = /^\s*(\d+(?:\.\d+)?)px\s*$/.exec(col.width);
|
|
143
|
+
if (m) return Number(m[1]);
|
|
144
|
+
warn(
|
|
145
|
+
`sdvtables:${who.ns}:width:${col.key}`,
|
|
146
|
+
`${who.fn}: column "${col.key}" width ${JSON.stringify(col.width)} is not in px; ${who.grid} widths are px numbers, so ${who.short}'s default is kept`
|
|
147
|
+
);
|
|
148
|
+
return void 0;
|
|
149
|
+
}
|
|
150
|
+
function unmapped(kind, who) {
|
|
151
|
+
warn(
|
|
152
|
+
`sdvtables:${who.ns}:kind:${kind}`,
|
|
153
|
+
`${who.fn}: ${who.grid} cells for "${kind}" columns are not mapped yet; they show the plain value`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
function rowIds(rows, key, who) {
|
|
157
|
+
const keyOf = (row, i) => {
|
|
158
|
+
const v = value(row, key);
|
|
159
|
+
if (isBlank(v))
|
|
160
|
+
throw new InputError(
|
|
161
|
+
`${who.fn}: row ${i} has no value for rowKey "${String(key)}" (${String(v)}); ${who.grid} needs an id for every row`
|
|
162
|
+
);
|
|
163
|
+
return String(v);
|
|
164
|
+
};
|
|
165
|
+
const index = /* @__PURE__ */ new Map();
|
|
166
|
+
const seen = /* @__PURE__ */ new Map();
|
|
167
|
+
rows.forEach((r, i) => {
|
|
168
|
+
const id = key === void 0 ? String(index.has(r) ? index.get(r) : i) : keyOf(r, i);
|
|
169
|
+
const j = seen.get(id);
|
|
170
|
+
if (j !== void 0)
|
|
171
|
+
throw new InputError(
|
|
172
|
+
key === void 0 ? `${who.fn}: rows ${j} and ${i} are the same object, so ${who.grid} would give them one id and drop one; copy the row or set the spec's rowKey` : `${who.fn}: rows ${j} and ${i} have the same rowKey "${String(key)}" value "${id}"; ${who.grid} needs a unique id per row`
|
|
173
|
+
);
|
|
174
|
+
seen.set(id, i);
|
|
175
|
+
if (!index.has(r)) index.set(r, id);
|
|
176
|
+
});
|
|
177
|
+
return (row) => {
|
|
178
|
+
const known = index.get(row);
|
|
179
|
+
if (known !== void 0) return known;
|
|
180
|
+
if (key !== void 0) return keyOf(row, "(not in rows)");
|
|
181
|
+
throw new TableSpecError(
|
|
182
|
+
`${who.fn}: a row the ${who.built} were not built from has no id; set the spec's rowKey, or build the ${who.built} from the rows you pass the grid`
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export { cell, checkHeight, formatterFor, headshotUrls, logoMarks, missingLast, opaque, pxWidth, rankParts, rowIds, scaleNotes, teamColorOf, teamRowFills, tint, unmapped, value };
|
|
188
|
+
//# sourceMappingURL=chunk-WBTFIGYK.js.map
|
|
189
|
+
//# sourceMappingURL=chunk-WBTFIGYK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/_grid.ts"],"names":[],"mappings":";;;;AAsDO,IAAM,IAAA,GAAO,CAAC,CAAA,KAAwB,OAAA,CAAQ,CAAC,IAAI,EAAA,GAAK,MAAA,CAAO,CAAC,CAAA,CAAE,IAAA;AAClE,IAAM,KAAA,GAAQ,CAAC,GAAA,EAAc,GAAA,KAClC,OAAO,IAAA,GAAO,MAAA,GAAa,IAAgC,GAAG;AAEzD,IAAM,MAAA,GAAS,CAAC,CAAA,KAA4B,CAAA,CAAE,OAAO,aAAA,GAAgB,SAAA,GAAY,IAAA,CAAK,CAAA,CAAE,EAAE;AACjG,IAAM,QAAA,GAAW,CAAC,CAAA,KAChB,IAAA,CAAK,MAAM,UAAA,CAAW,CAAC,CAAA,GAAI,GAAG,EAC3B,QAAA,CAAS,EAAE,CAAA,CACX,QAAA,CAAS,GAAG,GAAG,CAAA;AAEb,IAAM,IAAA,GAAO,CAAC,KAAA,EAAe,KAAA,EAAe,EAAA,KAAqB;AACtE,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,OAAO,EAAE,SAAA,EAAW,IAAA,EAAM,CAAC,CAAA,EAAG,QAAA,CAAS,KAAK,CAAC,IAAI,EAAE,CAAA;AAC9E,EAAA,OAAO,EAAE,IAAA,EAAM,GAAA,EAAK,OAAA,CAAQ,IAAI,CAAA,EAAE;AACpC;AAGO,SAAS,OAAA,CACd,IAAA,EACA,GAAA,EACA,MAAA,EACA,CAAA,EACqB;AACrB,EAAA,MAAM,GAAA,GAAM,CAAC,GAAG,IAAI,IAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM,IAAA,CAAK,KAAA,CAAM,GAAG,GAAG,CAAC,CAAC,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,KAAM,EAAE,CAAC,CAAC,CAAA;AACrF,EAAA,MAAM,GAAA,GAAM,WAAA,CAAY,GAAA,EAAK,MAAA,EAAQ;AAAA,IACnC,GAAI,EAAE,MAAA,KAAW,MAAA,GAAY,EAAC,GAAI,EAAE,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO;AAAA,IACrD,GAAI,EAAE,QAAA,KAAa,MAAA,GAAY,EAAC,GAAI,EAAE,QAAA,EAAU,CAAA,CAAE,QAAA,EAAS;AAAA,IAC3D,MAAA,EAAQ,EAAE,MAAA,IAAU;AAAA,GACrB,CAAA;AACD,EAAA,MAAM,GAAA,uBAAU,GAAA,EAAoB;AACpC,EAAA,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA,KAAM;AACpB,IAAA,MAAM,EAAA,GAAK,IAAI,CAAC,CAAA;AAChB,IAAA,IAAI,EAAA,KAAO,MAAA,EAAW,GAAA,CAAI,GAAA,CAAI,GAAG,EAAE,CAAA;AAAA,EACrC,CAAC,CAAA;AACD,EAAA,OAAO,GAAA;AACT;AAEO,SAAS,OAAA,CACd,gBAAA,EACA,MAAA,EACA,GAAA,EACA,OACA,EAAA,EACqB;AACrB,EAAA,MAAM,MAAA,GAAS,gBAAA,CAAiB,MAAA,EAAQ,GAAA,EAAK;AAAA,IAC3C,QAAA,EAAU,SAAA;AAAA,IACV,UAAA,EAAY,EAAA;AAAA,IACZ,GAAI,KAAA,KAAU,MAAA,GAAY,EAAC,GAAI,EAAE,KAAA;AAAM,GACxC,CAAA;AACD,EAAA,OAAO,IAAI,GAAA,CAAI,GAAA,CAAI,GAAA,CAAI,CAAC,EAAA,EAAI,CAAA,KAAM,CAAC,EAAA,EAAI,MAAA,CAAO,CAAC,CAAA,IAAK,SAAS,CAAC,CAAC,CAAA;AACjE;AAEO,SAAS,WAAA,CACd,GAAA,EACA,IAAA,EACA,EAAA,EACA,gBAAA,EAC0B;AAC1B,EAAA,MAAM,MAAM,OAAA,CAAQ,IAAA,EAAM,IAAI,GAAA,EAAK,GAAA,CAAI,QAAQ,GAAG,CAAA;AAClD,EAAA,MAAM,SAAS,OAAA,CAAQ,gBAAA,EAAkB,GAAA,CAAI,MAAA,EAAQ,CAAC,GAAG,IAAI,GAAA,CAAI,GAAA,CAAI,QAAQ,CAAC,CAAA,EAAG,GAAA,CAAI,OAAO,EAAE,CAAA;AAC9F,EAAA,OAAO,CAAC,GAAA,KAAQ;AACd,IAAA,MAAM,EAAA,GAAK,GAAA,CAAI,GAAA,CAAI,IAAA,CAAK,GAAG,CAAC,CAAA;AAC5B,IAAA,OAAA,CAAQ,OAAO,MAAA,GAAY,MAAA,GAAY,OAAO,GAAA,CAAI,EAAE,MAAM,GAAA,CAAI,OAAA;AAAA,EAChE,CAAA;AACF;AAEO,SAAS,YAAA,CACd,IAAA,EACA,EAAA,EAQA,EAAA,EACA,gBAAA,EACmB;AACnB,EAAA,MAAM,KAAA,GAAQ,GAAG,KAAA,IAAS,GAAA;AAC1B,EAAA,MAAM,MAAM,OAAA,CAAQ,IAAA,EAAM,GAAG,GAAA,EAAK,EAAA,CAAG,QAAQ,EAAE,CAAA;AAC/C,EAAA,MAAM,SAAS,OAAA,CAAQ,gBAAA,EAAkB,EAAA,CAAG,MAAA,EAAQ,CAAC,GAAG,IAAI,GAAA,CAAI,GAAA,CAAI,QAAQ,CAAC,CAAA,EAAG,EAAA,CAAG,OAAO,EAAE,CAAA;AAC5F,EAAA,MAAM,GAAA,uBAAU,GAAA,EAAkB;AAClC,EAAA,KAAA,MAAW,CAAC,CAAA,EAAG,EAAE,CAAA,IAAK,GAAA,EAAK;AACzB,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,GAAA,CAAI,EAAE,CAAA;AAC3B,IAAA,IAAI,KAAA,KAAU,QAAW,GAAA,CAAI,GAAA,CAAI,GAAG,IAAA,CAAK,KAAA,EAAO,KAAA,EAAO,EAAE,CAAC,CAAA;AAAA,EAC5D;AACA,EAAA,OAAO,GAAA;AACT;AAEO,SAAS,WAAA,CAAY,GAAW,IAAA,EAAsB;AAC3D,EAAA,IAAI,EAAE,MAAA,CAAO,QAAA,CAAS,CAAC,KAAK,CAAA,IAAK,CAAA,CAAA;AAC/B,IAAA,MAAM,IAAI,eAAe,CAAA,EAAG,IAAI,gDAAgD,MAAA,CAAO,CAAC,CAAC,CAAA,CAAE,CAAA;AAC7F,EAAA,OAAO,CAAA;AACT;AAEO,SAAS,SAAA,CACd,KACA,IAAA,EAC0C;AAC1C,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAyC;AAC3D,EAAA,KAAA,MAAW,CAAC,CAAA,EAAG,EAAE,CAAA,IAAK,OAAA,CAAQ,IAAA,EAAM,GAAA,CAAI,GAAA,EAAK,GAAA,CAAI,MAAA,EAAQ,GAAG,CAAA,EAAG;AAC7D,IAAA,MAAM,GAAA,GAAM,WAAA,CAAY,EAAA,EAAI,GAAA,CAAI,MAAA,EAAQ;AAAA,MACtC,GAAI,IAAI,MAAA,KAAW,MAAA,GAAY,EAAC,GAAI,EAAE,MAAA,EAAQ,GAAA,CAAI,MAAA,EAAO;AAAA,MACzD,GAAI,IAAI,OAAA,KAAY,MAAA,GAAY,EAAC,GAAI,EAAE,OAAA,EAAS,GAAA,CAAI,OAAA,EAAQ;AAAA,MAC5D,UAAU,GAAA,CAAI,IAAA;AAAA,MACd,QAAA,EAAU;AAAA,KACX,CAAA;AACD,IAAA,IAAI,GAAA,KAAQ,QAAW,KAAA,CAAM,GAAA,CAAI,GAAG,EAAE,EAAA,EAAI,KAAK,CAAA;AAAA,EACjD;AACA,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,YAAA,CACd,GAAA,EACA,IAAA,EACA,GAAA,EACqB;AACrB,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAoB;AACrC,EAAA,KAAA,MAAW,CAAA,IAAK,IAAI,GAAA,CAAI,IAAA,CAAK,IAAI,CAAC,CAAA,KAAM,IAAA,CAAK,KAAA,CAAM,GAAG,GAAA,CAAI,GAAG,CAAC,CAAC,CAAC,CAAA,EAAG;AACjE,IAAA,IAAI,MAAM,EAAA,EAAI;AACd,IAAA,MAAM,GAAA,GAAM,YAAY,CAAA,EAAG,GAAA,CAAI,QAAQ,EAAE,QAAA,EAAU,GAAA,CAAI,QAAA,EAAU,CAAA;AACjE,IAAA,IAAI,GAAA,KAAQ,MAAA;AACV,MAAA,IAAA;AAAA,QACE,aAAa,GAAA,CAAI,EAAE,aAAa,GAAA,CAAI,MAAM,IAAI,CAAC,CAAA,CAAA;AAAA,QAC/C,CAAA,EAAG,GAAA,CAAI,EAAE,CAAA,KAAA,EAAQ,GAAA,CAAI,MAAM,CAAA,qBAAA,EAAwB,IAAA,CAAK,SAAA,CAAU,CAAC,CAAC,CAAA,uBAAA;AAAA,OACtE;AAAA,SACG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,GAAG,CAAA;AAAA,EACtB;AACA,EAAA,OAAO,IAAA;AACT;AAGO,SAAS,WAAgB,IAAA,EAG9B;AACA,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAmD;AACtE,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAoB;AAC5C,EAAA,KAAA,MAAW,CAAA,IAAK,KAAK,WAAA,EAAa;AAChC,IAAA,IAAI,CAAA,CAAE,SAAS,WAAA,EAAa;AAC5B,IAAA,MAAM,EAAE,MAAA,EAAO,GAAI,WAAA,CAAY,CAAC,CAAA;AAChC,IAAA,KAAA,MAAW,CAAA,IAAK,EAAE,OAAA,EAA8B;AAC9C,MAAA,MAAA,CAAO,GAAA,CAAI,GAAG,EAAE,OAAA,EAAS,EAAE,OAAA,EAAS,QAAA,EAAU,CAAA,CAAE,QAAA,EAAU,CAAA;AAC1D,MAAA,IAAI,CAAA,CAAE,UAAU,YAAA,EAAc,WAAA,CAAY,IAAI,CAAA,EAAG,CAAA,CAAA,EAAI,MAAM,CAAA,CAAE,CAAA;AAAA,IAC/D;AAAA,EACF;AACA,EAAA,OAAO,EAAE,QAAQ,WAAA,EAAY;AAC/B;AACA,IAAM,YAAA,GACJ,CAAC,CAAA,KACD,CAAC,CAAA,KAAuB;AACtB,EAAA,MAAM,CAAA,GAAI,SAAS,CAAC,CAAA;AACpB,EAAA,OAAO,CAAA,KAAM,IAAA,GAAO,EAAA,GAAK,CAAA,CAAE,CAAC,CAAA;AAC9B,CAAA;AAEK,SAAS,YAAA,CACd,KACA,MAAA,EACsC;AACtC,EAAA,QAAQ,IAAI,IAAA;AAAM,IAChB,KAAK,KAAA,EAAO;AACV,MAAA,MAAM,EAAA,GAAK,MAAA,CAAO,GAAA,CAAI,GAAA,CAAI,GAAG,CAAA;AAC7B,MAAA,IAAI,EAAA,SAAW,YAAA,CAAa,CAAC,MAAM,YAAA,CAAa,CAAA,EAAG,EAAE,CAAC,CAAA;AACtD,MAAA,OAAO,YAAA;AAAA,QAAa,CAAC,CAAA,KACnB,YAAA,CAAa,CAAA,EAAG;AAAA,UACd,GAAI,IAAI,MAAA,KAAW,MAAA,GAAY,EAAC,GAAI,EAAE,MAAA,EAAQ,GAAA,CAAI,MAAA,EAAO;AAAA,UACzD,GAAA,EAAK,IAAI,GAAA,IAAO,KAAA;AAAA,UAChB,MAAA,EAAQ,IAAI,MAAA,IAAU,EAAA;AAAA,UACtB,MAAA,EAAQ,IAAI,MAAA,IAAU,EAAA;AAAA,UACtB,SAAA,EAAW,IAAI,SAAA,IAAa;AAAA,SAC7B;AAAA,OACH;AAAA,IACF;AAAA,IACA,KAAK,KAAA,EAAO;AACV,MAAA,MAAM,EAAA,GAAK,MAAA,CAAO,GAAA,CAAI,GAAA,CAAI,GAAG,CAAA;AAC7B,MAAA,OAAO,aAAa,CAAC,CAAA,KAAO,EAAA,GAAK,YAAA,CAAa,GAAG,EAAE,CAAA,GAAI,YAAA,CAAa,CAAA,EAAG,EAAE,MAAA,EAAQ,CAAA,EAAG,GAAA,EAAK,IAAA,EAAM,CAAE,CAAA;AAAA,IACnG;AAAA,IACA,KAAK,KAAA;AACH,MAAA,OAAO,aAAa,CAAC,CAAA,KAAM,CAAA,EAAG,YAAA,CAAa,IAAI,KAAA,GAAQ,CAAA,GAAI,GAAA,GAAM,CAAA,EAAG,EAAE,MAAA,EAAQ,GAAA,CAAI,MAAA,EAAQ,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,IAChG,KAAK,MAAA;AACH,MAAA,OAAO,aAAa,OAAO,CAAA;AAAA,IAC7B;AACE,MAAA,OAAO,MAAA;AAAA;AAEb;AAEO,SAAS,UAAU,CAAA,EAAoD;AAC5E,EAAA,MAAM,CAAA,GAAI,SAAS,CAAC,CAAA;AACpB,EAAA,IAAI,CAAA,KAAM,MAAM,OAAO,IAAA;AACvB,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA;AAChC,EAAA,OAAO,EAAE,KAAK,MAAA,EAAQ,OAAA,CAAQ,CAAC,CAAA,CAAE,KAAA,CAAM,GAAA,CAAI,MAAM,CAAA,EAAE;AACrD;AAEO,SAAS,WAAA,CAAiB,KAAsB,IAAA,EAAoD;AACzG,EAAA,MAAM,OAAA,GAAU,aAAA,CAAc,GAAA,EAAK,IAAI,CAAA;AACvC,EAAA,OAAO,CAAC,GAAA,KAAQ,eAAA,CAAgB,OAAA,EAAS,GAAG,CAAA;AAC9C;AAEO,SAAS,OAAA,CAAa,KAAsB,GAAA,EAAmC;AACpF,EAAA,IAAI,GAAA,CAAI,KAAA,KAAU,MAAA,EAAW,OAAO,MAAA;AACpC,EAAA,MAAM,CAAA,GAAI,2BAAA,CAA4B,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA;AACpD,EAAA,IAAI,CAAA,EAAG,OAAO,MAAA,CAAO,CAAA,CAAE,CAAC,CAAC,CAAA;AACzB,EAAA,IAAA;AAAA,IACE,CAAA,UAAA,EAAa,GAAA,CAAI,EAAE,CAAA,OAAA,EAAU,IAAI,GAAG,CAAA,CAAA;AAAA,IACpC,GAAG,GAAA,CAAI,EAAE,CAAA,UAAA,EAAa,GAAA,CAAI,GAAG,CAAA,QAAA,EAAW,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,KAAK,CAAC,CAAA,eAAA,EAAkB,IAAI,IAAI,CAAA,2BAAA,EAA8B,IAAI,KAAK,CAAA,kBAAA;AAAA,GACpI;AACA,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,QAAA,CAAS,MAAc,GAAA,EAAqB;AAC1D,EAAA,IAAA;AAAA,IACE,CAAA,UAAA,EAAa,GAAA,CAAI,EAAE,CAAA,MAAA,EAAS,IAAI,CAAA,CAAA;AAAA,IAChC,GAAG,GAAA,CAAI,EAAE,KAAK,GAAA,CAAI,IAAI,eAAe,IAAI,CAAA,uDAAA;AAAA,GAC3C;AACF;AAOO,SAAS,MAAA,CACd,IAAA,EACA,GAAA,EACA,GAAA,EACsB;AACtB,EAAA,MAAM,KAAA,GAAQ,CAAC,GAAA,EAAU,CAAA,KAA+B;AACtD,IAAA,MAAM,CAAA,GAAI,KAAA,CAAM,GAAA,EAAK,GAAa,CAAA;AAClC,IAAA,IAAI,QAAQ,CAAC,CAAA;AACX,MAAA,MAAM,IAAI,UAAA;AAAA,QACR,CAAA,EAAG,GAAA,CAAI,EAAE,CAAA,MAAA,EAAS,CAAC,CAAA,0BAAA,EAA6B,MAAA,CAAO,GAAG,CAAC,MAAM,MAAA,CAAO,CAAC,CAAC,CAAA,GAAA,EAAM,IAAI,IAAI,CAAA,0BAAA;AAAA,OAC1F;AACF,IAAA,OAAO,OAAO,CAAC,CAAA;AAAA,EACjB,CAAA;AACA,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAiB;AACnC,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAoB;AACrC,EAAA,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA,KAAM;AACrB,IAAA,MAAM,KAAK,GAAA,KAAQ,MAAA,GAAY,MAAA,CAAO,KAAA,CAAM,IAAI,CAAC,CAAA,GAAI,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,GAAI,CAAC,CAAA,GAAI,KAAA,CAAM,GAAG,CAAC,CAAA;AACnF,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA;AACrB,IAAA,IAAI,CAAA,KAAM,MAAA;AACR,MAAA,MAAM,IAAI,UAAA;AAAA,QACR,GAAA,KAAQ,MAAA,GACJ,CAAA,EAAG,GAAA,CAAI,EAAE,CAAA,OAAA,EAAU,CAAC,CAAA,KAAA,EAAQ,CAAC,CAAA,yBAAA,EAA4B,GAAA,CAAI,IAAI,CAAA,2EAAA,CAAA,GACjE,CAAA,EAAG,GAAA,CAAI,EAAE,CAAA,OAAA,EAAU,CAAC,CAAA,KAAA,EAAQ,CAAC,CAAA,uBAAA,EAA0B,MAAA,CAAO,GAAG,CAAC,CAAA,SAAA,EAAY,EAAE,CAAA,GAAA,EAAM,GAAA,CAAI,IAAI,CAAA,0BAAA;AAAA,OACpG;AACF,IAAA,IAAA,CAAK,GAAA,CAAI,IAAI,CAAC,CAAA;AACd,IAAA,IAAI,CAAC,MAAM,GAAA,CAAI,CAAC,GAAG,KAAA,CAAM,GAAA,CAAI,GAAG,EAAE,CAAA;AAAA,EACpC,CAAC,CAAA;AACD,EAAA,OAAO,CAAC,GAAA,KAAQ;AACd,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC3B,IAAA,IAAI,KAAA,KAAU,QAAW,OAAO,KAAA;AAChC,IAAA,IAAI,GAAA,KAAQ,MAAA,EAAW,OAAO,KAAA,CAAM,KAAK,eAAe,CAAA;AACxD,IAAA,MAAM,IAAI,cAAA;AAAA,MACR,CAAA,EAAG,IAAI,EAAE,CAAA,YAAA,EAAe,IAAI,KAAK,CAAA,oEAAA,EAAuE,IAAI,KAAK,CAAA,gCAAA;AAAA,KACnH;AAAA,EACF,CAAA;AACF","file":"chunk-WBTFIGYK.js","sourcesContent":["/**\n * What the data grid adapters (`./mui` for MUI X Data Grid, `./aggrid` for AG Grid) share: reading cells, team ids and\n * colours, row ids and their checks, number formats (with a scaleNote and the rank suffix), the missing-last sort and the\n * image lookups with their warnings. Internal, not a subpath. It imports neither grid: each adapter turns these values\n * into its own grid's shapes (MUI's `sx` classes and React cells, AG Grid's inline styles and DOM cell renderers).\n */\nimport {\n type IdSystem,\n InputError,\n type League,\n type SeasonInput,\n type Which,\n checkAlpha,\n headshotUrl,\n hex6,\n logoUrlSync,\n onColor,\n resolveSync,\n solid,\n warn,\n} from \"@sportsdataverse/sdvplot\";\nimport { type Comparator, type SortDir, comparatorFor, withMissingLast } from \"./engine.js\";\nimport { TableSpecError } from \"./errors.js\";\nimport { formatNumber, isBlank, ordinal, scaledNumber, toNumber } from \"./format.js\";\nimport { scaleNoteOf } from \"./html/decorations.js\";\nimport type { ColumnSpec, TableSpec } from \"./spec.js\";\nimport type { ThemeTokens } from \"./themes/tokens.js\";\n\n/** Which adapter is speaking, for its warnings and errors. */\nexport interface GridName {\n /** The adapter's function, which starts each message: \"dataGridProps\". */\n readonly fn: string;\n /** The warning key's namespace: \"mui\". */\n readonly ns: string;\n /** The grid, in messages: \"MUI X Data Grid\". */\n readonly grid: string;\n /** The grid, in \"<short>'s default is kept\": \"MUI\". */\n readonly short: string;\n /** What the adapter builds, in messages: \"props\". */\n readonly built: string;\n}\n/** sdvplot's team palette for a background (each adapter passes its own subpath's `teamColorPalette`). */\nexport type PaletteFn = (\n league: League,\n teams: readonly unknown[],\n o: { idSystem?: IdSystem; background?: string; which?: Which },\n) => string[];\n/** A colour laid over the background, opaque, and its readable text colour. */\nexport interface Fill {\n readonly fill: string;\n readonly ink: string;\n}\n\n/** A cell's text: trimmed, \"\" for a blank value. Team and image lookups key on it, so 12 and \"12\" are one team. */\nexport const cell = (v: unknown): string => (isBlank(v) ? \"\" : String(v).trim());\nexport const value = (row: unknown, key: string): unknown =>\n row == null ? undefined : (row as Record<string, unknown>)[key];\n/** The theme's background as an opaque hex (white for a transparent theme). */\nexport const opaque = (t: ThemeTokens): string => (t.bg === \"transparent\" ? \"#ffffff\" : hex6(t.bg));\nconst alphaHex = (a: number): string =>\n Math.round(checkAlpha(a) * 255)\n .toString(16)\n .padStart(2, \"0\");\n/** `color` over `bg` at `alpha`, opaque, with its readable text colour. */\nexport const tint = (color: string, alpha: number, bg: string): Fill => {\n const fill = solid(`${hex6(color, { dropAlpha: true })}${alphaHex(alpha)}`, bg);\n return { fill, ink: onColor(fill) };\n};\n\n/** Each distinct cell value of `key` (trimmed text) -> its team id, by ONE resolveSync over the rows (one warning). */\nexport function teamIds(\n rows: readonly unknown[],\n key: string,\n league: League,\n o: { season?: SeasonInput | undefined; idSystem?: IdSystem | undefined; strict?: boolean | undefined },\n): Map<string, string> {\n const raw = [...new Set(rows.map((r) => cell(value(r, key))).filter((v) => v !== \"\"))];\n const ids = resolveSync(raw, league, {\n ...(o.season === undefined ? {} : { season: o.season }),\n ...(o.idSystem === undefined ? {} : { idSystem: o.idSystem }),\n strict: o.strict ?? false,\n });\n const out = new Map<string, string>();\n raw.forEach((v, i) => {\n const id = ids[i];\n if (id !== undefined) out.set(v, id);\n });\n return out;\n}\n/** Each team id -> its palette colour for `bg` (sdvplot's `teamColorPalette`, not a fork of it). */\nexport function palette(\n teamColorPalette: PaletteFn,\n league: League,\n ids: readonly string[],\n which: Which | undefined,\n bg: string,\n): Map<string, string> {\n const colors = teamColorPalette(league, ids, {\n idSystem: \"team_id\",\n background: bg,\n ...(which === undefined ? {} : { which }),\n });\n return new Map(ids.map((id, i) => [id, colors[i] ?? \"#808080\"]));\n}\n/** A `teamColorBg` / `teamColorBar` column: each cell value's colour, its team's palette colour else `naColor`. */\nexport function teamColorOf<Row>(\n col: ColumnSpec<Row> & { kind: \"teamColorBg\" | \"teamColorBar\" },\n rows: readonly Row[],\n bg: string,\n teamColorPalette: PaletteFn,\n): (raw: unknown) => string {\n const ids = teamIds(rows, col.key, col.league, col);\n const colors = palette(teamColorPalette, col.league, [...new Set(ids.values())], col.which, bg);\n return (raw) => {\n const id = ids.get(cell(raw));\n return (id === undefined ? undefined : colors.get(id)) ?? col.naColor;\n };\n}\n/** `teamRows`: each team value (trimmed text) -> its fill and ink; a team that is not found has none. */\nexport function teamRowFills(\n rows: readonly unknown[],\n tr: {\n key: string;\n league: League;\n which?: Which | undefined;\n alpha?: number | undefined;\n season?: SeasonInput | undefined;\n idSystem?: IdSystem | undefined;\n },\n bg: string,\n teamColorPalette: PaletteFn,\n): Map<string, Fill> {\n const alpha = tr.alpha ?? 0.4;\n const ids = teamIds(rows, tr.key, tr.league, tr);\n const colors = palette(teamColorPalette, tr.league, [...new Set(ids.values())], tr.which, bg);\n const out = new Map<string, Fill>();\n for (const [v, id] of ids) {\n const color = colors.get(id);\n if (color !== undefined) out.set(v, tint(color, alpha, bg));\n }\n return out;\n}\n\nexport function checkHeight(h: number, kind: string): number {\n if (!(Number.isFinite(h) && h >= 1))\n throw new TableSpecError(`${kind} height must be a number of pixels >= 1, got ${String(h)}`);\n return h;\n}\n/** A logo or wordmark column: each cell value (trimmed text) -> its team id and image URL; no URL, no entry. */\nexport function logoMarks<Row>(\n col: ColumnSpec<Row> & { kind: \"logo\" | \"wordmark\" },\n rows: readonly Row[],\n): Map<string, { id: string; url: string }> {\n const marks = new Map<string, { id: string; url: string }>();\n for (const [v, id] of teamIds(rows, col.key, col.league, col)) {\n const url = logoUrlSync(id, col.league, {\n ...(col.season === undefined ? {} : { season: col.season }),\n ...(col.variant === undefined ? {} : { variant: col.variant }),\n markType: col.kind,\n idSystem: \"team_id\",\n }); // undefined: no mark archived, and sdvplot warned once\n if (url !== undefined) marks.set(v, { id, url });\n }\n return marks;\n}\n/** A headshot column: each player id (trimmed text) -> its headshot URL; a player with none warns once. */\nexport function headshotUrls<Row>(\n col: ColumnSpec<Row> & { kind: \"headshot\" },\n rows: readonly Row[],\n who: GridName,\n): Map<string, string> {\n const urls = new Map<string, string>();\n for (const v of new Set(rows.map((r) => cell(value(r, col.key))))) {\n if (v === \"\") continue;\n const url = headshotUrl(v, col.league, { idSystem: col.idSystem });\n if (url === undefined)\n warn(\n `sdvtables:${who.ns}:headshot:${col.league}:${v}`,\n `${who.fn}: no ${col.league} headshot for player ${JSON.stringify(v)}: the cell keeps the id`,\n );\n else urls.set(v, url);\n }\n return urls;\n}\n\n/** scaleNote, as the HTML table applies it: num/int cells divided (with its decimals), the label suffixed; last wins. */\nexport function scaleNotes<Row>(spec: TableSpec<Row>): {\n scaled: Map<string, { divisor: number; decimals: number }>;\n labelSuffix: Map<string, string>;\n} {\n const scaled = new Map<string, { divisor: number; decimals: number }>();\n const labelSuffix = new Map<string, string>();\n for (const d of spec.decorations) {\n if (d.type !== \"scaleNote\") continue;\n const { suffix } = scaleNoteOf(d);\n for (const k of d.columns as readonly string[]) {\n scaled.set(k, { divisor: d.divisor, decimals: d.decimals });\n if (d.where !== \"sourceNote\") labelSuffix.set(k, ` ${suffix}`);\n }\n }\n return { scaled, labelSuffix };\n}\nconst numberFormat =\n (f: (n: number) => string) =>\n (v: unknown): string => {\n const n = toNumber(v);\n return n === null ? \"\" : f(n);\n };\n/** A number column's cell text, as the HTML table writes it (\"\" for a blank value); undefined for other kinds. */\nexport function formatterFor<Row>(\n col: ColumnSpec<Row>,\n scaled: ReadonlyMap<string, { divisor: number; decimals: number }>,\n): ((v: unknown) => string) | undefined {\n switch (col.kind) {\n case \"num\": {\n const sc = scaled.get(col.key);\n if (sc) return numberFormat((n) => scaledNumber(n, sc));\n return numberFormat((n) =>\n formatNumber(n, {\n ...(col.digits === undefined ? {} : { digits: col.digits }),\n big: col.big ?? false,\n prefix: col.prefix ?? \"\",\n suffix: col.suffix ?? \"\",\n forceSign: col.forceSign ?? false,\n }),\n );\n }\n case \"int\": {\n const sc = scaled.get(col.key);\n return numberFormat((n) => (sc ? scaledNumber(n, sc) : formatNumber(n, { digits: 0, big: true })));\n }\n case \"pct\":\n return numberFormat((n) => `${formatNumber(col.scale ? n * 100 : n, { digits: col.digits })}%`);\n case \"rank\":\n return numberFormat(ordinal);\n default:\n return undefined;\n }\n}\n/** A rank's number and its suffix, for the HTML table's superscript (`1<sup>st</sup>`); null for a blank value. */\nexport function rankParts(v: unknown): { num: string; suffix: string } | null {\n const n = toNumber(v);\n if (n === null) return null;\n const num = String(Math.trunc(n));\n return { num, suffix: ordinal(n).slice(num.length) };\n}\n/** sdvtables' own sort for a column (its `compare`, else by kind), missing values last in the given direction. */\nexport function missingLast<Row>(col: ColumnSpec<Row>, rows: readonly Row[]): (dir: SortDir) => Comparator {\n const compare = comparatorFor(col, rows);\n return (dir) => withMissingLast(compare, dir);\n}\n/** A column's px `width` as a number; any other CSS width is left to the grid, with one warning. */\nexport function pxWidth<Row>(col: ColumnSpec<Row>, who: GridName): number | undefined {\n if (col.width === undefined) return undefined;\n const m = /^\\s*(\\d+(?:\\.\\d+)?)px\\s*$/.exec(col.width);\n if (m) return Number(m[1]);\n warn(\n `sdvtables:${who.ns}:width:${col.key}`,\n `${who.fn}: column \"${col.key}\" width ${JSON.stringify(col.width)} is not in px; ${who.grid} widths are px numbers, so ${who.short}'s default is kept`,\n );\n return undefined;\n}\n/** A kind the grid draws as its plain value: one warning per kind. */\nexport function unmapped(kind: string, who: GridName): void {\n warn(\n `sdvtables:${who.ns}:kind:${kind}`,\n `${who.fn}: ${who.grid} cells for \"${kind}\" columns are not mapped yet; they show the plain value`,\n );\n}\n\n/**\n * Every row's id, checked once: `String(row[rowKey])`, else the row's index in `rows`. First wins for a row object; a\n * repeated id (the same object with no rowKey, or two rows with one rowKey value) or a blank rowKey value throws, since\n * the grid would drop a row. The returned function also ids a row the grid was not built from, by its rowKey.\n */\nexport function rowIds<Row>(\n rows: readonly Row[],\n key: (keyof Row & string) | undefined,\n who: GridName,\n): (row: Row) => string {\n const keyOf = (row: Row, i: number | string): string => {\n const v = value(row, key as string);\n if (isBlank(v))\n throw new InputError(\n `${who.fn}: row ${i} has no value for rowKey \"${String(key)}\" (${String(v)}); ${who.grid} needs an id for every row`,\n );\n return String(v);\n };\n const index = new Map<Row, string>();\n const seen = new Map<string, number>();\n rows.forEach((r, i) => {\n const id = key === undefined ? String(index.has(r) ? index.get(r) : i) : keyOf(r, i);\n const j = seen.get(id);\n if (j !== undefined)\n throw new InputError(\n key === undefined\n ? `${who.fn}: rows ${j} and ${i} are the same object, so ${who.grid} would give them one id and drop one; copy the row or set the spec's rowKey`\n : `${who.fn}: rows ${j} and ${i} have the same rowKey \"${String(key)}\" value \"${id}\"; ${who.grid} needs a unique id per row`,\n );\n seen.set(id, i);\n if (!index.has(r)) index.set(r, id);\n });\n return (row) => {\n const known = index.get(row);\n if (known !== undefined) return known;\n if (key !== undefined) return keyOf(row, \"(not in rows)\");\n throw new TableSpecError(\n `${who.fn}: a row the ${who.built} were not built from has no id; set the spec's rowKey, or build the ${who.built} from the rows you pass the grid`,\n );\n };\n}\n"]}
|
package/dist/export.d.ts
CHANGED
package/dist/export.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { renderHTML } from './chunk-
|
|
2
|
-
import
|
|
3
|
-
import './chunk-
|
|
1
|
+
import { renderHTML } from './chunk-VLF2S6VW.js';
|
|
2
|
+
import './chunk-3TJR7TKS.js';
|
|
3
|
+
import { escapeHtml, escapeAttr } from './chunk-R375P3MN.js';
|
|
4
|
+
import './chunk-UTOVFIFT.js';
|
|
4
5
|
import { mkdir, writeFile } from 'fs/promises';
|
|
5
6
|
import { join } from 'path';
|
|
6
7
|
import { InputError, SdvplotError, warn, OptionalDependencyError } from '@sportsdataverse/sdvplot';
|
package/dist/export.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/export/compose.ts","../src/export/index.ts"],"names":["n","InputError"],"mappings":"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","file":"export.js","sourcesContent":["import { InputError } from \"@sportsdataverse/sdvplot\";\nimport { escapeHtml } from \"../html/escape.js\";\nimport { renderHTML } from \"../html/index.js\";\nimport type { TableSpec } from \"../spec.js\";\n\nexport type TableItem<Row> = { spec: TableSpec<Row>; rows: readonly Row[] } | string;\nexport interface ComposeOptions {\n title?: string;\n subtitle?: string;\n caption?: string;\n sourceNote?: string;\n captionRule?: boolean;\n}\nexport interface GridOptions extends ComposeOptions {\n ncol?: number;\n gap?: number;\n align?: \"top\" | \"center\" | \"bottom\";\n labels?: readonly string[];\n}\nexport interface StackOptions extends ComposeOptions {\n gap?: number;\n align?: \"left\" | \"center\" | \"right\";\n}\n\n/**\n * Python `_STYLE_DEFAULTS` (_export.py:469-475) as one sheet: title 28px/700/#111111 centred, subtitle 16px/400/#666666\n * centred, caption 12px/400/#8A8A8A centred, source note 12px/400/#8A8A8A right, grid label 12px/600/#555555 left, all in\n * `system-ui, -apple-system, sans-serif` (:467). A title with no subtitle keeps the subtitle's 12px gap (:564-565);\n * `captionRule` is a 1px rule UNDER the caption in its colour, padded 6px (:566-567, :574).\n */\nconst COMPOSE_CSS =\n \".sdvt-compose{display:inline-block;font-family:system-ui,-apple-system,sans-serif}\" +\n \".sdvt-compose-title{font-size:28px;font-weight:700;color:#111111;text-align:center;margin:0 0 4px}\" +\n \".sdvt-compose-title:last-child{margin-bottom:12px}\" +\n \".sdvt-compose-subtitle{font-size:16px;font-weight:400;color:#666666;text-align:center;margin:0 0 12px}\" +\n \".sdvt-compose-caption{font-size:12px;font-weight:400;color:#8A8A8A;text-align:center;margin:10px 0 0}\" +\n \".sdvt-compose-source{font-size:12px;font-weight:400;color:#8A8A8A;text-align:right;margin:6px 0 0}\" +\n \".sdvt-compose-rule{border-bottom:1px solid #8A8A8A;padding-bottom:6px}\" +\n \".sdvt-compose-label{font-size:12px;font-weight:600;color:#555555;text-align:left;margin:0 0 6px}\";\n\n/**\n * A group value as `batchToPNG` puts it in a file name (Python `gt_save_batch`): each run of characters outside\n * `A-Za-z0-9._-` becomes \"-\", the ends are trimmed of \"-\", and it is lower-cased.\n * Export plumbing: it may change before 1.0.\n *\n * @beta\n */\nexport function slug(value: unknown): string {\n return String(value)\n .replace(/[^A-Za-z0-9._-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .toLowerCase();\n}\n\nfunction itemHTML<Row>(item: TableItem<Row>): string {\n return typeof item === \"string\" ? item : renderHTML(item.spec, item.rows);\n}\nfunction checkItems<Row>(items: readonly TableItem<Row>[]): void {\n if (items.length === 0) throw new InputError(\"at least one table is required\");\n}\nfunction checkPixels(name: string, value: number): void {\n if (!(Number.isFinite(value) && value >= 0))\n throw new InputError(`${name} must be a non-negative number of pixels, got ${String(value)}`);\n}\n/** Python's `align not in places` check: the CSS value for a known name, else InputError. */\nfunction placeOf(align: string, places: Readonly<Record<string, string>>): string {\n const place = places[align];\n if (place === undefined)\n throw new InputError(\n `align must be one of ${Object.keys(places).join(\", \")}, got ${JSON.stringify(align)}`,\n );\n return place;\n}\n\n/** A table's wrapper as `assemble` writes it (class, then id); ids match /^[A-Za-z][\\w-]*$/, so no regex escaping. */\nconst WRAPPER = /<div class=\"sdvt [^\"]*\" id=\"([A-Za-z][\\w-]*)\"/g;\n\n/**\n * One spec composed twice renders one wrapper id twice: every repeat gets the first free `-2`, `-3`… suffix, on its\n * wrapper (and the `id`/`for` of its controls) and on the `#id` selectors of its own `<style>`s, so each table\n * keeps its decorations. A table is the markup from its wrapper to the next one.\n */\nfunction uniqueIds(body: string): string {\n const starts = [...body.matchAll(WRAPPER)];\n const taken = new Set(starts.map((m) => m[1] as string));\n if (taken.size === starts.length) return body;\n const seen = new Set<string>();\n let out = body.slice(0, starts[0]?.index);\n starts.forEach((m, i) => {\n const id = m[1] as string;\n let table = body.slice(m.index, starts[i + 1]?.index);\n if (seen.has(id)) {\n let n = 2;\n while (taken.has(`${id}-${n}`)) n++;\n const to = `${id}-${n}`;\n taken.add(to);\n table = table\n .replace(new RegExp(`(\\\\s(?:id|for)=\")${id}(?=[\"-])`, \"g\"), `$1${to}`)\n .replace(/<style>[\\s\\S]*?<\\/style>/g, (css) =>\n // a selector only: the next brace is the rule's `{`, so a color equal to the id (`#fade;`) is left alone\n css.replace(new RegExp(`#${id}(?![\\\\w-])(?=[^{}]*\\\\{)`, \"g\"), `#${to}`),\n );\n }\n seen.add(id);\n out += table;\n });\n return out;\n}\n\n/** Title/subtitle above, caption/source note below — the shared frame of gt_grid and gt_stack_tables. */\nexport function composePage(\n body: string,\n { title, subtitle, caption, sourceNote, captionRule = false }: ComposeOptions = {},\n): string {\n const head =\n title || subtitle\n ? `<header class=\"sdvt-compose-head\">${title ? `<div class=\"sdvt-compose-title\">${escapeHtml(title)}</div>` : \"\"}${subtitle ? `<p class=\"sdvt-compose-subtitle\">${escapeHtml(subtitle)}</p>` : \"\"}</header>`\n : \"\";\n const foot =\n caption || sourceNote\n ? `<footer class=\"sdvt-compose-foot\">${caption ? `<p class=\"sdvt-compose-caption${captionRule ? \" sdvt-compose-rule\" : \"\"}\">${escapeHtml(caption)}</p>` : \"\"}${sourceNote ? `<p class=\"sdvt-compose-source\">${escapeHtml(sourceNote)}</p>` : \"\"}</footer>`\n : \"\";\n return `<div class=\"sdvt-compose\"><style>${COMPOSE_CSS}</style>${head}${uniqueIds(body)}${foot}</div>`;\n}\n\n/** Port of gt_grid: small multiples, `ncol` across, rows follow from the count. */\nexport function gridTables<Row>(\n items: readonly TableItem<Row>[],\n { ncol = 2, gap = 24, align = \"top\", labels, ...compose }: GridOptions = {},\n): string {\n checkItems(items);\n if (!Number.isInteger(ncol) || ncol < 1)\n throw new InputError(`ncol must be an integer of at least 1, got ${String(ncol)}`);\n const place = placeOf(align, { top: \"start\", center: \"center\", bottom: \"end\" });\n checkPixels(\"gap\", gap);\n if (labels !== undefined && labels.length === 0)\n throw new InputError(\"labels must be non-empty; omit it for no labels\");\n const cells = items.map((item, i) => {\n const label =\n labels === undefined\n ? \"\"\n : `<div class=\"sdvt-compose-label\">${escapeHtml(labels[i % labels.length] ?? \"\")}</div>`;\n return `<div class=\"sdvt-compose-cell\">${label}${itemHTML(item)}</div>`;\n });\n const grid = `<div class=\"sdvt-grid\" style=\"display:grid;grid-template-columns:repeat(${ncol},max-content);gap:${gap}px;align-items:${place};justify-content:center\">${cells.join(\"\")}</div>`;\n return composePage(grid, compose);\n}\n\n/** Port of gt_stack_tables: a vertical stack. */\nexport function stackTables<Row>(\n items: readonly TableItem<Row>[],\n { gap = 16, align = \"center\", ...compose }: StackOptions = {},\n): string {\n checkItems(items);\n const place = placeOf(align, { left: \"flex-start\", center: \"center\", right: \"flex-end\" });\n checkPixels(\"gap\", gap);\n const stack = `<div class=\"sdvt-stack\" style=\"display:flex;flex-direction:column;gap:${gap}px;align-items:${place}\">${items.map(itemHTML).join(\"\")}</div>`;\n return composePage(stack, compose);\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { InputError, OptionalDependencyError, SdvplotError, warn } from \"@sportsdataverse/sdvplot\";\nimport {\n type Aspect,\n type Gravity,\n canvasFor,\n checkColor,\n offsetFor,\n parseAspect,\n parseGravity,\n peerMissing,\n} from \"@sportsdataverse/sdvplot/export\";\nimport type { Browser, Page } from \"playwright\";\nimport { escapeAttr } from \"../html/escape.js\";\nimport { renderHTML } from \"../html/index.js\";\nimport type { TableSpec } from \"../spec.js\";\nimport { type TableItem, slug } from \"./compose.js\";\n\nexport { gridTables, stackTables, composePage, slug } from \"./compose.js\";\nexport type { ComposeOptions, GridOptions, StackOptions, TableItem } from \"./compose.js\";\n\n/**\n * Options for {@link htmlToPNG} and {@link tableToPNG}; defaults are Python `gt_save_crop`'s. Its `expand` (the page\n * captured before trimming) is fixed here, as it only has to leave background around the content. (The HTML itself\n * takes `RenderOptions` from `@sportsdataverse/sdvtables/html`.)\n */\nexport interface PNGOptions {\n /** the final image width in pixels, the height following (Python `width`); absent: the rendered width */\n width?: number;\n /** the rendering zoom (Python `zoom`); default 2, a sharp (retina) image */\n deviceScaleFactor?: number;\n /** the color around the content; default \"white\" */\n background?: string;\n /** image pixels of `background` left around the trimmed content (Python `whitespace`, truncated to an integer); default 50 */\n whitespace?: number;\n /** stylesheet URLs (Google Fonts) awaited via document.fonts.ready */\n fontLinks?: readonly string[];\n /** also write the PNG here (must end in .png) */\n file?: string;\n}\n/** Options for {@link socialCrop}; defaults are Python `gt_social_crop`'s (whitespace 60). */\nexport interface SocialCropOptions extends PNGOptions {\n aspect?: Aspect;\n gravity?: Gravity;\n}\n/** Options for {@link batchToPNG}, as Python `gt_save_batch` (no `width`; each file is named by its group). */\nexport interface BatchOptions extends Omit<PNGOptions, \"width\" | \"file\"> {\n dir: string;\n matchWidth?: boolean;\n}\n\ninterface Resolved {\n width: number | undefined;\n deviceScaleFactor: number;\n background: string;\n whitespace: number;\n fontLinks: readonly string[];\n file: string | undefined;\n}\n\n/** The page width tables lay out in; a wider table grows the page rather than being clipped. */\nconst VIEWPORT = 1200;\n/** CSS px of page captured around the content, so the trim finds plain background on every side. */\nconst EXPAND = 5;\n/**\n * Chromium's canvas limits (Blink: 65,535 px a side, 32,768 x 8,192 px in all; measured in playwright 1.64's\n * chromium). Past either, `toDataURL` returns \"data:,\" and `getImageData` reads blank, without an error.\n */\nconst MAX_SIDE = 65_535;\nconst MAX_AREA = 32_768 * 8_192;\nconst REMEDY =\n \"put fewer rows in each image (rows.slice() a page at a time, batchToPNG by a group column, or gridTables to set tables side by side), or lower deviceScaleFactor\";\nconst n = (v: number): string => v.toLocaleString(\"en-US\");\n\n/** I1: a canvas past Chromium's limits draws nothing; say so, with the size, before drawing it. */\nfunction checkCanvas(what: string, w: number, h: number, o: Resolved): void {\n if (w <= MAX_SIDE && h <= MAX_SIDE && w * h <= MAX_AREA) return;\n throw new SdvplotError(\n `${what} ${n(w)} × ${n(h)} px at deviceScaleFactor ${o.deviceScaleFactor}, past Chromium's canvas limit of ${n(MAX_SIDE)} px a side and ${n(MAX_AREA)} px in all; ${REMEDY}`,\n );\n}\n\nconst checkPng = (file: string): void => {\n // Python _check_file: the extension must be a format the writer produces; here that is PNG only\n if (!/\\.png$/i.test(file)) throw new InputError(`file must end in .png, got ${JSON.stringify(file)}`);\n};\n\nfunction resolve({\n width,\n deviceScaleFactor = 2,\n background = \"white\",\n whitespace = 50,\n fontLinks = [],\n file,\n}: PNGOptions): Resolved {\n if (width !== undefined && !(Number.isInteger(width) && width > 0))\n throw new InputError(`width must be a positive integer of pixels, got ${String(width)}`);\n if (!(Number.isFinite(deviceScaleFactor) && deviceScaleFactor > 0))\n throw new InputError(`deviceScaleFactor must be a positive number, got ${String(deviceScaleFactor)}`);\n if (!(Number.isFinite(whitespace) && whitespace >= 0))\n throw new InputError(`whitespace must be a non-negative number of pixels, got ${String(whitespace)}`);\n checkColor(\"background\", background);\n if (file !== undefined) checkPng(file);\n // Python _pixels: int()\n return { width, deviceScaleFactor, background, whitespace: Math.trunc(whitespace), fontLinks, file };\n}\n\nasync function launch(): Promise<Browser> {\n const pw = await import(\"playwright\").catch((e: unknown) => {\n throw new OptionalDependencyError(\n peerMissing(e, \"playwright\")\n ? \"sdvtables/export needs the optional peer playwright, which is not installed: pnpm add -D playwright && pnpm exec playwright install chromium\"\n : `sdvtables/export could not load the optional peer playwright (installed, but it failed to load): ${String(e)}`,\n { cause: e },\n );\n });\n try {\n return await pw.chromium.launch();\n } catch (e) {\n if (e instanceof Error && /Executable doesn't exist/.test(e.message))\n throw new OptionalDependencyError(\n \"sdvtables/export found playwright but not its chromium browser: pnpm exec playwright install chromium\",\n { cause: e },\n );\n throw e;\n }\n}\n\nfunction page(body: string, o: Resolved): string {\n const links = o.fontLinks.map((href) => `<link rel=\"stylesheet\" href=\"${escapeAttr(href)}\">`).join(\"\");\n return `<!doctype html><html><head><meta charset=\"utf-8\">${links}<style>html,body{margin:0;background:${o.background}}#sdv-root{display:inline-block;padding:${EXPAND}px;background:${o.background}}</style></head><body><div id=\"sdv-root\">${body}</div></body></html>`;\n}\n\n/** A screenshot (base64 PNG) and its trim box: x, y, width, height in image pixels. */\ninterface Shot {\n png: string;\n trim: [number, number, number, number];\n}\n\n/** In the page: Python `_trim` (ImageMagick, fuzz 0), the box of the pixels unlike the top-left one; all one color: the whole image. */\nasync function trimBox(png: string): Promise<[number, number, number, number]> {\n const img = new Image();\n img.src = `data:image/png;base64,${png}`;\n await img.decode();\n const c = document.createElement(\"canvas\");\n c.width = img.width;\n c.height = img.height;\n const g = c.getContext(\"2d\") as CanvasRenderingContext2D;\n g.drawImage(img, 0, 0);\n const px = new Uint32Array(g.getImageData(0, 0, c.width, c.height).data.buffer);\n const bg = px[0];\n let [x0, y0, x1, y1] = [c.width, c.height, -1, -1];\n for (let y = 0, i = 0; y < c.height; y++)\n for (let x = 0; x < c.width; x++, i++)\n if (px[i] !== bg) {\n if (x < x0) x0 = x;\n if (x > x1) x1 = x;\n if (y < y0) y0 = y;\n y1 = y;\n }\n return x1 < 0 ? [0, 0, c.width, c.height] : [x0, y0, x1 - x0 + 1, y1 - y0 + 1];\n}\n\n/** In the page: the trimmed shot placed at dx, dy on a w x h canvas of `bg`, then scaled to fw x fh; a base64 PNG. */\nasync function drawPNG(a: {\n png: string;\n trim: [number, number, number, number];\n w: number;\n h: number;\n dx: number;\n dy: number;\n fw: number;\n fh: number;\n bg: string;\n}): Promise<string> {\n const img = new Image();\n img.src = `data:image/png;base64,${a.png}`;\n await img.decode();\n const c = document.createElement(\"canvas\");\n c.width = a.w;\n c.height = a.h;\n const g = c.getContext(\"2d\") as CanvasRenderingContext2D;\n g.fillStyle = a.bg;\n g.fillRect(0, 0, a.w, a.h);\n const [x, y, tw, th] = a.trim;\n g.drawImage(img, x, y, tw, th, a.dx, a.dy, tw, th);\n let out = c;\n if (a.fw !== a.w || a.fh !== a.h) {\n out = document.createElement(\"canvas\");\n out.width = a.fw;\n out.height = a.fh;\n const s = out.getContext(\"2d\") as CanvasRenderingContext2D;\n s.imageSmoothingQuality = \"high\";\n s.drawImage(c, 0, 0, a.fw, a.fh);\n }\n return out.toDataURL(\"image/png\").slice(\"data:image/png;base64,\".length);\n}\n\n/** `html` rendered at the zoom (a wide table is never clipped) and trimmed: Python `_trim(_render_gt(...))`. */\nasync function snap(p: Page, html: string, o: Resolved): Promise<Shot> {\n await p.setViewportSize({ width: VIEWPORT, height: 800 });\n await p.setContent(page(html, o), { waitUntil: \"load\" });\n await p.evaluate(() => document.fonts.ready);\n const root = p.locator(\"#sdv-root\");\n const box = await root.boundingBox();\n if (box) {\n const z = o.deviceScaleFactor;\n checkCanvas(\"the table renders\", Math.ceil(box.width * z), Math.ceil(box.height * z), o);\n }\n if (box && box.width > VIEWPORT) await p.setViewportSize({ width: Math.ceil(box.width), height: 800 });\n const png = Buffer.from(await root.screenshot({ type: \"png\" })).toString(\"base64\");\n return { png, trim: await p.evaluate(trimBox, png) };\n}\n\n/**\n * Python's finishing steps, in image pixels: `_extent` to `minWidth` (centred), `_pad` by `whitespace`, the social\n * `_canvas` + `_extent` by gravity, `_fit_width` to `width`. Writes `o.file` when set.\n */\nasync function finish(\n p: Page,\n shot: Shot,\n o: Resolved,\n { minWidth = 0, ratio, gravity = \"center\" }: { minWidth?: number; ratio?: number; gravity?: Gravity } = {},\n): Promise<Uint8Array> {\n const [, , tw, th] = shot.trim;\n const ws = o.whitespace;\n let [w, h] = [Math.max(tw, minWidth) + 2 * ws, th + 2 * ws];\n let [dx, dy] = [Math.floor((w - 2 * ws - tw) / 2) + ws, ws];\n if (ratio !== undefined) {\n const [cw, ch] = canvasFor(w, h, ratio);\n const [ox, oy] = offsetFor(cw - w, ch - h, gravity);\n [w, h, dx, dy] = [cw, ch, dx + ox, dy + oy];\n }\n // Python _fit_width: the height rounded half up, as ImageMagick\n const [fw, fh] =\n o.width === undefined ? [w, h] : [o.width, Math.max(1, Math.floor((h * o.width) / w + 0.5))];\n checkCanvas(\"the image would be\", w, h, o);\n checkCanvas(\"the image scaled to `width` would be\", fw, fh, o);\n const b64 = await p.evaluate(drawPNG, {\n png: shot.png,\n trim: shot.trim,\n w,\n h,\n dx,\n dy,\n fw,\n fh,\n bg: o.background,\n });\n // \"data:,\" (no base64): a canvas Chromium would not allocate; never return or write an empty image\n if (b64 === \"\")\n throw new SdvplotError(`Chromium returned an empty image for ${n(fw)} × ${n(fh)} px; ${REMEDY}`);\n const png = new Uint8Array(Buffer.from(b64, \"base64\"));\n if (o.file !== undefined) await writeFile(o.file, png);\n return png;\n}\n\nasync function withPage<T>(o: Resolved, fn: (p: Page) => Promise<T>): Promise<T> {\n const browser = await launch();\n try {\n return await fn(await browser.newPage({ deviceScaleFactor: o.deviceScaleFactor }));\n } finally {\n await browser.close();\n }\n}\n\n/**\n * gt_save_crop for any HTML: rendered at `deviceScaleFactor`, trimmed to its content, `whitespace` image pixels of\n * `background` put back around it, then scaled to `width` when given. An image past Chromium's canvas limit (65,535\n * px a side, 268,435,456 px in all; near 1,000 plain rows at zoom 2) throws `SdvplotError` naming its size, before\n * anything is drawn or written.\n */\nexport async function htmlToPNG(html: string, options: PNGOptions = {}): Promise<Uint8Array> {\n const o = resolve(options);\n return withPage(o, async (p) => finish(p, await snap(p, html, o), o));\n}\n\n/** gt_save_crop: {@link htmlToPNG} of the rendered table. */\nexport function tableToPNG<Row>(\n spec: TableSpec<Row>,\n rows: readonly Row[],\n options: PNGOptions = {},\n): Promise<Uint8Array> {\n return htmlToPNG(renderHTML(spec, rows), options);\n}\n\n/** gt_social_crop: the trimmed, padded table on a fixed-ratio canvas placed by `gravity`; the table is never cropped. */\nexport async function socialCrop<Row>(\n spec: TableSpec<Row>,\n rows: readonly Row[],\n { aspect = \"1:1\", gravity = \"center\", ...rest }: SocialCropOptions = {},\n): Promise<Uint8Array> {\n const ratio = parseAspect(aspect);\n const place = parseGravity(gravity);\n const o = resolve({ whitespace: 60, ...rest });\n const html = renderHTML(spec, rows);\n return withPage(o, async (p) => finish(p, await snap(p, html, o), o, { ratio, gravity: place }));\n}\n\n/**\n * gt_save_batch: one image per group value, `{group}` in the file name replaced by `slug(value)`. A group whose table\n * fails to build (`build` or the spec throws) is skipped and named in one warning at the end; when none builds, the\n * error lists every failure. Python's `quiet` progress lines are not ported.\n */\nexport async function batchToPNG<Row, G extends keyof Row>(\n rows: readonly Row[],\n group: G,\n build: (groupRows: Row[], value: Row[G]) => TableItem<Row>,\n filePattern: string,\n { dir, matchWidth = true, ...rest }: BatchOptions,\n): Promise<string[]> {\n if (!filePattern.includes(\"{group}\"))\n throw new InputError(`filePattern must contain \"{group}\", got ${filePattern}`);\n checkPng(filePattern);\n const o = resolve(rest);\n const groups = new Map<Row[G], Row[]>();\n for (const r of rows) {\n const v = r[group];\n if (v === null || v === undefined) continue;\n const bucket = groups.get(v);\n if (bucket) bucket.push(r);\n else groups.set(v, [r]);\n }\n if (groups.size === 0) throw new InputError(`no non-missing values in column ${String(group)}`);\n const names = [...groups.keys()].map((v) => filePattern.replace(\"{group}\", slug(v)));\n const shared = [...new Set(names.filter((n, i) => names.indexOf(n) !== i))].sort();\n if (shared.length > 0)\n // Python gt_save_batch: two values must never write one file\n throw new InputError(\n `group values write to the same file name: ${shared.join(\", \")}; rename the values first`,\n );\n const items: { file: string; html: string }[] = [];\n const failed: string[] = [];\n [...groups].forEach(([v, rs], i) => {\n try {\n const built = build(rs, v);\n items.push({\n file: join(dir, names[i] as string),\n html: typeof built === \"string\" ? built : renderHTML(built.spec, built.rows),\n });\n } catch (e) {\n failed.push(`${String(v)}: ${e instanceof Error ? e.message : String(e)}`);\n }\n });\n if (items.length === 0) throw new SdvplotError(`No group built successfully:\\n${failed.join(\"\\n\")}`);\n await mkdir(dir, { recursive: true });\n const out = await withPage(o, async (p) => {\n const shots: Shot[] = [];\n for (const it of items) shots.push(await snap(p, it.html, o));\n // Python: every trimmed image extended (centred) to the widest one's width, then padded\n const minWidth = matchWidth ? Math.max(...shots.map((s) => s.trim[2])) : 0;\n for (const [i, it] of items.entries())\n await finish(p, shots[i] as Shot, { ...o, file: it.file }, { minWidth });\n return items.map((it) => it.file);\n });\n if (failed.length > 0)\n warn(\n `sdvtables:batch:${filePattern}:${failed.join(\"|\")}`,\n `${failed.length} group(s) failed and were skipped:\\n${failed.join(\"\\n\")}`,\n );\n return out;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/export/compose.ts","../src/export/index.ts"],"names":["n","InputError"],"mappings":";;;;;;;;;AA8BA,IAAM,WAAA,GACJ,2rBAAA;AAgBK,SAAS,KAAK,KAAA,EAAwB;AAC3C,EAAA,OAAO,MAAA,CAAO,KAAK,CAAA,CAChB,OAAA,CAAQ,mBAAA,EAAqB,GAAG,CAAA,CAChC,OAAA,CAAQ,UAAA,EAAY,EAAE,CAAA,CACtB,WAAA,EAAY;AACjB;AAEA,SAAS,SAAc,IAAA,EAA8B;AACnD,EAAA,OAAO,OAAO,SAAS,QAAA,GAAW,IAAA,GAAO,WAAW,IAAA,CAAK,IAAA,EAAM,KAAK,IAAI,CAAA;AAC1E;AACA,SAAS,WAAgB,KAAA,EAAwC;AAC/D,EAAA,IAAI,MAAM,MAAA,KAAW,CAAA,EAAG,MAAM,IAAI,WAAW,gCAAgC,CAAA;AAC/E;AACA,SAAS,WAAA,CAAY,MAAc,KAAA,EAAqB;AACtD,EAAA,IAAI,EAAE,MAAA,CAAO,QAAA,CAAS,KAAK,KAAK,KAAA,IAAS,CAAA,CAAA;AACvC,IAAA,MAAM,IAAI,WAAW,CAAA,EAAG,IAAI,iDAAiD,MAAA,CAAO,KAAK,CAAC,CAAA,CAAE,CAAA;AAChG;AAEA,SAAS,OAAA,CAAQ,OAAe,MAAA,EAAkD;AAChF,EAAA,MAAM,KAAA,GAAQ,OAAO,KAAK,CAAA;AAC1B,EAAA,IAAI,KAAA,KAAU,MAAA;AACZ,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qBAAA,EAAwB,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,MAAA,EAAS,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA;AAAA,KACtF;AACF,EAAA,OAAO,KAAA;AACT;AAGA,IAAM,OAAA,GAAU,gDAAA;AAOhB,SAAS,UAAU,IAAA,EAAsB;AACvC,EAAA,MAAM,SAAS,CAAC,GAAG,IAAA,CAAK,QAAA,CAAS,OAAO,CAAC,CAAA;AACzC,EAAA,MAAM,KAAA,GAAQ,IAAI,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,CAAC,CAAW,CAAC,CAAA;AACvD,EAAA,IAAI,KAAA,CAAM,IAAA,KAAS,MAAA,CAAO,MAAA,EAAQ,OAAO,IAAA;AACzC,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,IAAI,MAAM,IAAA,CAAK,KAAA,CAAM,GAAG,MAAA,CAAO,CAAC,GAAG,KAAK,CAAA;AACxC,EAAA,MAAA,CAAO,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA,KAAM;AACvB,IAAA,MAAM,EAAA,GAAK,EAAE,CAAC,CAAA;AACd,IAAA,IAAI,KAAA,GAAQ,KAAK,KAAA,CAAM,CAAA,CAAE,OAAO,MAAA,CAAO,CAAA,GAAI,CAAC,CAAA,EAAG,KAAK,CAAA;AACpD,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA,EAAG;AAChB,MAAA,IAAIA,EAAAA,GAAI,CAAA;AACR,MAAA,OAAO,KAAA,CAAM,IAAI,CAAA,EAAG,EAAE,IAAIA,EAAC,CAAA,CAAE,GAAGA,EAAAA,EAAAA;AAChC,MAAA,MAAM,EAAA,GAAK,CAAA,EAAG,EAAE,CAAA,CAAA,EAAIA,EAAC,CAAA,CAAA;AACrB,MAAA,KAAA,CAAM,IAAI,EAAE,CAAA;AACZ,MAAA,KAAA,GAAQ,KAAA,CACL,OAAA,CAAQ,IAAI,MAAA,CAAO,CAAA,iBAAA,EAAoB,EAAE,CAAA,QAAA,CAAA,EAAY,GAAG,CAAA,EAAG,CAAA,EAAA,EAAK,EAAE,CAAA,CAAE,CAAA,CACpE,OAAA;AAAA,QAAQ,2BAAA;AAAA,QAA6B,CAAC,GAAA;AAAA;AAAA,UAErC,GAAA,CAAI,OAAA,CAAQ,IAAI,MAAA,CAAO,CAAA,CAAA,EAAI,EAAE,CAAA,uBAAA,CAAA,EAA2B,GAAG,CAAA,EAAG,CAAA,CAAA,EAAI,EAAE,CAAA,CAAE;AAAA;AAAA,OACxE;AAAA,IACJ;AACA,IAAA,IAAA,CAAK,IAAI,EAAE,CAAA;AACX,IAAA,GAAA,IAAO,KAAA;AAAA,EACT,CAAC,CAAA;AACD,EAAA,OAAO,GAAA;AACT;AAGO,SAAS,WAAA,CACd,IAAA,EACA,EAAE,KAAA,EAAO,QAAA,EAAU,OAAA,EAAS,UAAA,EAAY,WAAA,GAAc,KAAA,EAAM,GAAoB,EAAC,EACzE;AACR,EAAA,MAAM,OACJ,KAAA,IAAS,QAAA,GACL,qCAAqC,KAAA,GAAQ,CAAA,gCAAA,EAAmC,WAAW,KAAK,CAAC,WAAW,EAAE,CAAA,EAAG,WAAW,CAAA,iCAAA,EAAoC,UAAA,CAAW,QAAQ,CAAC,CAAA,IAAA,CAAA,GAAS,EAAE,CAAA,SAAA,CAAA,GAC/L,EAAA;AACN,EAAA,MAAM,IAAA,GACJ,WAAW,UAAA,GACP,CAAA,kCAAA,EAAqC,UAAU,CAAA,8BAAA,EAAiC,WAAA,GAAc,oBAAA,GAAuB,EAAE,CAAA,EAAA,EAAK,UAAA,CAAW,OAAO,CAAC,CAAA,IAAA,CAAA,GAAS,EAAE,CAAA,EAAG,UAAA,GAAa,CAAA,+BAAA,EAAkC,WAAW,UAAU,CAAC,CAAA,IAAA,CAAA,GAAS,EAAE,CAAA,SAAA,CAAA,GAC7O,EAAA;AACN,EAAA,OAAO,CAAA,iCAAA,EAAoC,WAAW,CAAA,QAAA,EAAW,IAAI,GAAG,SAAA,CAAU,IAAI,CAAC,CAAA,EAAG,IAAI,CAAA,MAAA,CAAA;AAChG;AAGO,SAAS,UAAA,CACd,KAAA,EACA,EAAE,IAAA,GAAO,GAAG,GAAA,GAAM,EAAA,EAAI,KAAA,GAAQ,KAAA,EAAO,MAAA,EAAQ,GAAG,OAAA,EAAQ,GAAiB,EAAC,EAClE;AACR,EAAA,UAAA,CAAW,KAAK,CAAA;AAChB,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,IAAI,KAAK,IAAA,GAAO,CAAA;AACpC,IAAA,MAAM,IAAI,UAAA,CAAW,CAAA,2CAAA,EAA8C,MAAA,CAAO,IAAI,CAAC,CAAA,CAAE,CAAA;AACnF,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,EAAO,EAAE,GAAA,EAAK,SAAS,MAAA,EAAQ,QAAA,EAAU,MAAA,EAAQ,KAAA,EAAO,CAAA;AAC9E,EAAA,WAAA,CAAY,OAAO,GAAG,CAAA;AACtB,EAAA,IAAI,MAAA,KAAW,MAAA,IAAa,MAAA,CAAO,MAAA,KAAW,CAAA;AAC5C,IAAA,MAAM,IAAI,WAAW,iDAAiD,CAAA;AACxE,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,GAAA,CAAI,CAAC,MAAM,CAAA,KAAM;AACnC,IAAA,MAAM,KAAA,GACJ,MAAA,KAAW,MAAA,GACP,EAAA,GACA,CAAA,gCAAA,EAAmC,UAAA,CAAW,MAAA,CAAO,CAAA,GAAI,MAAA,CAAO,MAAM,CAAA,IAAK,EAAE,CAAC,CAAA,MAAA,CAAA;AACpF,IAAA,OAAO,CAAA,+BAAA,EAAkC,KAAK,CAAA,EAAG,QAAA,CAAS,IAAI,CAAC,CAAA,MAAA,CAAA;AAAA,EACjE,CAAC,CAAA;AACD,EAAA,MAAM,IAAA,GAAO,CAAA,wEAAA,EAA2E,IAAI,CAAA,kBAAA,EAAqB,GAAG,CAAA,eAAA,EAAkB,KAAK,CAAA,yBAAA,EAA4B,KAAA,CAAM,IAAA,CAAK,EAAE,CAAC,CAAA,MAAA,CAAA;AACrL,EAAA,OAAO,WAAA,CAAY,MAAM,OAAO,CAAA;AAClC;AAGO,SAAS,WAAA,CACd,KAAA,EACA,EAAE,GAAA,GAAM,EAAA,EAAI,KAAA,GAAQ,QAAA,EAAU,GAAG,OAAA,EAAQ,GAAkB,EAAC,EACpD;AACR,EAAA,UAAA,CAAW,KAAK,CAAA;AAChB,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,EAAO,EAAE,IAAA,EAAM,cAAc,MAAA,EAAQ,QAAA,EAAU,KAAA,EAAO,UAAA,EAAY,CAAA;AACxF,EAAA,WAAA,CAAY,OAAO,GAAG,CAAA;AACtB,EAAA,MAAM,KAAA,GAAQ,CAAA,sEAAA,EAAyE,GAAG,CAAA,eAAA,EAAkB,KAAK,CAAA,EAAA,EAAK,KAAA,CAAM,GAAA,CAAI,QAAQ,CAAA,CAAE,IAAA,CAAK,EAAE,CAAC,CAAA,MAAA,CAAA;AAClJ,EAAA,OAAO,WAAA,CAAY,OAAO,OAAO,CAAA;AACnC;;;AChGA,IAAM,QAAA,GAAW,IAAA;AAEjB,IAAM,MAAA,GAAS,CAAA;AAKf,IAAM,QAAA,GAAW,KAAA;AACjB,IAAM,WAAW,KAAA,GAAS,IAAA;AAC1B,IAAM,MAAA,GACJ,kKAAA;AACF,IAAM,CAAA,GAAI,CAAC,CAAA,KAAsB,CAAA,CAAE,eAAe,OAAO,CAAA;AAGzD,SAAS,WAAA,CAAY,IAAA,EAAc,CAAA,EAAW,CAAA,EAAW,CAAA,EAAmB;AAC1E,EAAA,IAAI,KAAK,QAAA,IAAY,CAAA,IAAK,QAAA,IAAY,CAAA,GAAI,KAAK,QAAA,EAAU;AACzD,EAAA,MAAM,IAAI,YAAA;AAAA,IACR,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,CAAA,CAAE,CAAC,CAAC,CAAA,MAAA,EAAM,CAAA,CAAE,CAAC,CAAC,CAAA,yBAAA,EAA4B,EAAE,iBAAiB,CAAA,kCAAA,EAAqC,EAAE,QAAQ,CAAC,kBAAkB,CAAA,CAAE,QAAQ,CAAC,CAAA,YAAA,EAAe,MAAM,CAAA;AAAA,GAC5K;AACF;AAEA,IAAM,QAAA,GAAW,CAAC,IAAA,KAAuB;AAEvC,EAAA,IAAI,CAAC,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,EAAG,MAAM,IAAIC,UAAAA,CAAW,CAAA,2BAAA,EAA8B,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA,CAAE,CAAA;AACtG,CAAA;AAEA,SAAS,OAAA,CAAQ;AAAA,EACf,KAAA;AAAA,EACA,iBAAA,GAAoB,CAAA;AAAA,EACpB,UAAA,GAAa,OAAA;AAAA,EACb,UAAA,GAAa,EAAA;AAAA,EACb,YAAY,EAAC;AAAA,EACb;AACF,CAAA,EAAyB;AACvB,EAAA,IAAI,UAAU,MAAA,IAAa,EAAE,OAAO,SAAA,CAAU,KAAK,KAAK,KAAA,GAAQ,CAAA,CAAA;AAC9D,IAAA,MAAM,IAAIA,UAAAA,CAAW,CAAA,gDAAA,EAAmD,MAAA,CAAO,KAAK,CAAC,CAAA,CAAE,CAAA;AACzF,EAAA,IAAI,EAAE,MAAA,CAAO,QAAA,CAAS,iBAAiB,KAAK,iBAAA,GAAoB,CAAA,CAAA;AAC9D,IAAA,MAAM,IAAIA,UAAAA,CAAW,CAAA,iDAAA,EAAoD,MAAA,CAAO,iBAAiB,CAAC,CAAA,CAAE,CAAA;AACtG,EAAA,IAAI,EAAE,MAAA,CAAO,QAAA,CAAS,UAAU,KAAK,UAAA,IAAc,CAAA,CAAA;AACjD,IAAA,MAAM,IAAIA,UAAAA,CAAW,CAAA,wDAAA,EAA2D,MAAA,CAAO,UAAU,CAAC,CAAA,CAAE,CAAA;AACtG,EAAA,UAAA,CAAW,cAAc,UAAU,CAAA;AACnC,EAAA,IAAI,IAAA,KAAS,MAAA,EAAW,QAAA,CAAS,IAAI,CAAA;AAErC,EAAA,OAAO,EAAE,KAAA,EAAO,iBAAA,EAAmB,UAAA,EAAY,UAAA,EAAY,KAAK,KAAA,CAAM,UAAU,CAAA,EAAG,SAAA,EAAW,IAAA,EAAK;AACrG;AAEA,eAAe,MAAA,GAA2B;AACxC,EAAA,MAAM,KAAK,MAAM,OAAO,YAAY,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,KAAe;AAC1D,IAAA,MAAM,IAAI,uBAAA;AAAA,MACR,WAAA,CAAY,GAAG,YAAY,CAAA,GACvB,iJACA,CAAA,iGAAA,EAAoG,MAAA,CAAO,CAAC,CAAC,CAAA,CAAA;AAAA,MACjH,EAAE,OAAO,CAAA;AAAE,KACb;AAAA,EACF,CAAC,CAAA;AACD,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,EAAA,CAAG,QAAA,CAAS,MAAA,EAAO;AAAA,EAClC,SAAS,CAAA,EAAG;AACV,IAAA,IAAI,CAAA,YAAa,KAAA,IAAS,0BAAA,CAA2B,IAAA,CAAK,EAAE,OAAO,CAAA;AACjE,MAAA,MAAM,IAAI,uBAAA;AAAA,QACR,uGAAA;AAAA,QACA,EAAE,OAAO,CAAA;AAAE,OACb;AACF,IAAA,MAAM,CAAA;AAAA,EACR;AACF;AAEA,SAAS,IAAA,CAAK,MAAc,CAAA,EAAqB;AAC/C,EAAA,MAAM,KAAA,GAAQ,CAAA,CAAE,SAAA,CAAU,GAAA,CAAI,CAAC,IAAA,KAAS,CAAA,6BAAA,EAAgC,UAAA,CAAW,IAAI,CAAC,CAAA,EAAA,CAAI,CAAA,CAAE,KAAK,EAAE,CAAA;AACrG,EAAA,OAAO,CAAA,iDAAA,EAAoD,KAAK,CAAA,qCAAA,EAAwC,CAAA,CAAE,UAAU,CAAA,wCAAA,EAA2C,MAAM,CAAA,cAAA,EAAiB,CAAA,CAAE,UAAU,CAAA,yCAAA,EAA4C,IAAI,CAAA,oBAAA,CAAA;AACpP;AASA,eAAe,QAAQ,GAAA,EAAwD;AAC7E,EAAA,MAAM,GAAA,GAAM,IAAI,KAAA,EAAM;AACtB,EAAA,GAAA,CAAI,GAAA,GAAM,yBAAyB,GAAG,CAAA,CAAA;AACtC,EAAA,MAAM,IAAI,MAAA,EAAO;AACjB,EAAA,MAAM,CAAA,GAAI,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AACzC,EAAA,CAAA,CAAE,QAAQ,GAAA,CAAI,KAAA;AACd,EAAA,CAAA,CAAE,SAAS,GAAA,CAAI,MAAA;AACf,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,UAAA,CAAW,IAAI,CAAA;AAC3B,EAAA,CAAA,CAAE,SAAA,CAAU,GAAA,EAAK,CAAA,EAAG,CAAC,CAAA;AACrB,EAAA,MAAM,EAAA,GAAK,IAAI,WAAA,CAAY,CAAA,CAAE,YAAA,CAAa,CAAA,EAAG,CAAA,EAAG,CAAA,CAAE,KAAA,EAAO,CAAA,CAAE,MAAM,CAAA,CAAE,KAAK,MAAM,CAAA;AAC9E,EAAA,MAAM,EAAA,GAAK,GAAG,CAAC,CAAA;AACf,EAAA,IAAI,CAAC,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA,GAAI,CAAC,CAAA,CAAE,KAAA,EAAO,CAAA,CAAE,MAAA,EAAQ,EAAA,EAAI,EAAE,CAAA;AACjD,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,EAAE,MAAA,EAAQ,CAAA,EAAA;AACnC,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,OAAO,CAAA,EAAA,EAAK,CAAA,EAAA;AAChC,MAAA,IAAI,EAAA,CAAG,CAAC,CAAA,KAAM,EAAA,EAAI;AAChB,QAAA,IAAI,CAAA,GAAI,IAAI,EAAA,GAAK,CAAA;AACjB,QAAA,IAAI,CAAA,GAAI,IAAI,EAAA,GAAK,CAAA;AACjB,QAAA,IAAI,CAAA,GAAI,IAAI,EAAA,GAAK,CAAA;AACjB,QAAA,EAAA,GAAK,CAAA;AAAA,MACP;AACJ,EAAA,OAAO,KAAK,CAAA,GAAI,CAAC,GAAG,CAAA,EAAG,CAAA,CAAE,OAAO,CAAA,CAAE,MAAM,CAAA,GAAI,CAAC,IAAI,EAAA,EAAI,EAAA,GAAK,KAAK,CAAA,EAAG,EAAA,GAAK,KAAK,CAAC,CAAA;AAC/E;AAGA,eAAe,QAAQ,CAAA,EAUH;AAClB,EAAA,MAAM,GAAA,GAAM,IAAI,KAAA,EAAM;AACtB,EAAA,GAAA,CAAI,GAAA,GAAM,CAAA,sBAAA,EAAyB,CAAA,CAAE,GAAG,CAAA,CAAA;AACxC,EAAA,MAAM,IAAI,MAAA,EAAO;AACjB,EAAA,MAAM,CAAA,GAAI,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AACzC,EAAA,CAAA,CAAE,QAAQ,CAAA,CAAE,CAAA;AACZ,EAAA,CAAA,CAAE,SAAS,CAAA,CAAE,CAAA;AACb,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,UAAA,CAAW,IAAI,CAAA;AAC3B,EAAA,CAAA,CAAE,YAAY,CAAA,CAAE,EAAA;AAChB,EAAA,CAAA,CAAE,SAAS,CAAA,EAAG,CAAA,EAAG,CAAA,CAAE,CAAA,EAAG,EAAE,CAAC,CAAA;AACzB,EAAA,MAAM,CAAC,CAAA,EAAG,CAAA,EAAG,EAAA,EAAI,EAAE,IAAI,CAAA,CAAE,IAAA;AACzB,EAAA,CAAA,CAAE,SAAA,CAAU,GAAA,EAAK,CAAA,EAAG,CAAA,EAAG,EAAA,EAAI,EAAA,EAAI,CAAA,CAAE,EAAA,EAAI,CAAA,CAAE,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA;AACjD,EAAA,IAAI,GAAA,GAAM,CAAA;AACV,EAAA,IAAI,EAAE,EAAA,KAAO,CAAA,CAAE,KAAK,CAAA,CAAE,EAAA,KAAO,EAAE,CAAA,EAAG;AAChC,IAAA,GAAA,GAAM,QAAA,CAAS,cAAc,QAAQ,CAAA;AACrC,IAAA,GAAA,CAAI,QAAQ,CAAA,CAAE,EAAA;AACd,IAAA,GAAA,CAAI,SAAS,CAAA,CAAE,EAAA;AACf,IAAA,MAAM,CAAA,GAAI,GAAA,CAAI,UAAA,CAAW,IAAI,CAAA;AAC7B,IAAA,CAAA,CAAE,qBAAA,GAAwB,MAAA;AAC1B,IAAA,CAAA,CAAE,UAAU,CAAA,EAAG,CAAA,EAAG,GAAG,CAAA,CAAE,EAAA,EAAI,EAAE,EAAE,CAAA;AAAA,EACjC;AACA,EAAA,OAAO,IAAI,SAAA,CAAU,WAAW,CAAA,CAAE,KAAA,CAAM,yBAAyB,MAAM,CAAA;AACzE;AAGA,eAAe,IAAA,CAAK,CAAA,EAAS,IAAA,EAAc,CAAA,EAA4B;AACrE,EAAA,MAAM,EAAE,eAAA,CAAgB,EAAE,OAAO,QAAA,EAAU,MAAA,EAAQ,KAAK,CAAA;AACxD,EAAA,MAAM,CAAA,CAAE,WAAW,IAAA,CAAK,IAAA,EAAM,CAAC,CAAA,EAAG,EAAE,SAAA,EAAW,MAAA,EAAQ,CAAA;AACvD,EAAA,MAAM,CAAA,CAAE,QAAA,CAAS,MAAM,QAAA,CAAS,MAAM,KAAK,CAAA;AAC3C,EAAA,MAAM,IAAA,GAAO,CAAA,CAAE,OAAA,CAAQ,WAAW,CAAA;AAClC,EAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,WAAA,EAAY;AACnC,EAAA,IAAI,GAAA,EAAK;AACP,IAAA,MAAM,IAAI,CAAA,CAAE,iBAAA;AACZ,IAAA,WAAA,CAAY,mBAAA,EAAqB,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,KAAA,GAAQ,CAAC,CAAA,EAAG,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,MAAA,GAAS,CAAC,GAAG,CAAC,CAAA;AAAA,EACzF;AACA,EAAA,IAAI,OAAO,GAAA,CAAI,KAAA,GAAQ,QAAA,EAAU,MAAM,EAAE,eAAA,CAAgB,EAAE,KAAA,EAAO,IAAA,CAAK,KAAK,GAAA,CAAI,KAAK,CAAA,EAAG,MAAA,EAAQ,KAAK,CAAA;AACrG,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,MAAM,IAAA,CAAK,UAAA,CAAW,EAAE,IAAA,EAAM,KAAA,EAAO,CAAC,CAAA,CAAE,SAAS,QAAQ,CAAA;AACjF,EAAA,OAAO,EAAE,KAAK,IAAA,EAAM,MAAM,EAAE,QAAA,CAAS,OAAA,EAAS,GAAG,CAAA,EAAE;AACrD;AAMA,eAAe,MAAA,CACb,CAAA,EACA,IAAA,EACA,CAAA,EACA,EAAE,QAAA,GAAW,CAAA,EAAG,KAAA,EAAO,OAAA,GAAU,QAAA,EAAS,GAA8D,EAAC,EACpF;AACrB,EAAA,MAAM,KAAK,EAAA,EAAI,EAAE,IAAI,IAAA,CAAK,IAAA;AAC1B,EAAA,MAAM,KAAK,CAAA,CAAE,UAAA;AACb,EAAA,IAAI,CAAC,CAAA,EAAG,CAAC,CAAA,GAAI,CAAC,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,QAAQ,CAAA,GAAI,CAAA,GAAI,EAAA,EAAI,EAAA,GAAK,IAAI,EAAE,CAAA;AAC1D,EAAA,IAAI,CAAC,EAAA,EAAI,EAAE,CAAA,GAAI,CAAC,IAAA,CAAK,KAAA,CAAA,CAAO,CAAA,GAAI,CAAA,GAAI,EAAA,GAAK,EAAA,IAAM,CAAC,CAAA,GAAI,IAAI,EAAE,CAAA;AAC1D,EAAA,IAAI,UAAU,MAAA,EAAW;AACvB,IAAA,MAAM,CAAC,EAAA,EAAI,EAAE,IAAI,SAAA,CAAU,CAAA,EAAG,GAAG,KAAK,CAAA;AACtC,IAAA,MAAM,CAAC,IAAI,EAAE,CAAA,GAAI,UAAU,EAAA,GAAK,CAAA,EAAG,EAAA,GAAK,CAAA,EAAG,OAAO,CAAA;AAClD,IAAA,CAAC,CAAA,EAAG,CAAA,EAAG,EAAA,EAAI,EAAE,CAAA,GAAI,CAAC,EAAA,EAAI,EAAA,EAAI,EAAA,GAAK,EAAA,EAAI,EAAA,GAAK,EAAE,CAAA;AAAA,EAC5C;AAEA,EAAA,MAAM,CAAC,EAAA,EAAI,EAAE,CAAA,GACX,CAAA,CAAE,UAAU,MAAA,GAAY,CAAC,CAAA,EAAG,CAAC,CAAA,GAAI,CAAC,EAAE,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,KAAA,CAAO,CAAA,GAAI,CAAA,CAAE,KAAA,GAAS,CAAA,GAAI,GAAG,CAAC,CAAC,CAAA;AAC7F,EAAA,WAAA,CAAY,oBAAA,EAAsB,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AACzC,EAAA,WAAA,CAAY,sCAAA,EAAwC,EAAA,EAAI,EAAA,EAAI,CAAC,CAAA;AAC7D,EAAA,MAAM,GAAA,GAAM,MAAM,CAAA,CAAE,QAAA,CAAS,OAAA,EAAS;AAAA,IACpC,KAAK,IAAA,CAAK,GAAA;AAAA,IACV,MAAM,IAAA,CAAK,IAAA;AAAA,IACX,CAAA;AAAA,IACA,CAAA;AAAA,IACA,EAAA;AAAA,IACA,EAAA;AAAA,IACA,EAAA;AAAA,IACA,EAAA;AAAA,IACA,IAAI,CAAA,CAAE;AAAA,GACP,CAAA;AAED,EAAA,IAAI,GAAA,KAAQ,EAAA;AACV,IAAA,MAAM,IAAI,YAAA,CAAa,CAAA,qCAAA,EAAwC,CAAA,CAAE,EAAE,CAAC,CAAA,MAAA,EAAM,CAAA,CAAE,EAAE,CAAC,CAAA,KAAA,EAAQ,MAAM,CAAA,CAAE,CAAA;AACjG,EAAA,MAAM,MAAM,IAAI,UAAA,CAAW,OAAO,IAAA,CAAK,GAAA,EAAK,QAAQ,CAAC,CAAA;AACrD,EAAA,IAAI,EAAE,IAAA,KAAS,MAAA,QAAiB,SAAA,CAAU,CAAA,CAAE,MAAM,GAAG,CAAA;AACrD,EAAA,OAAO,GAAA;AACT;AAEA,eAAe,QAAA,CAAY,GAAa,EAAA,EAAyC;AAC/E,EAAA,MAAM,OAAA,GAAU,MAAM,MAAA,EAAO;AAC7B,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,EAAA,CAAG,MAAM,OAAA,CAAQ,OAAA,CAAQ,EAAE,iBAAA,EAAmB,CAAA,CAAE,iBAAA,EAAmB,CAAC,CAAA;AAAA,EACnF,CAAA,SAAE;AACA,IAAA,MAAM,QAAQ,KAAA,EAAM;AAAA,EACtB;AACF;AAQA,eAAsB,SAAA,CAAU,IAAA,EAAc,OAAA,GAAsB,EAAC,EAAwB;AAC3F,EAAA,MAAM,CAAA,GAAI,QAAQ,OAAO,CAAA;AACzB,EAAA,OAAO,QAAA,CAAS,CAAA,EAAG,OAAO,CAAA,KAAM,MAAA,CAAO,CAAA,EAAG,MAAM,IAAA,CAAK,CAAA,EAAG,IAAA,EAAM,CAAC,CAAA,EAAG,CAAC,CAAC,CAAA;AACtE;AAGO,SAAS,UAAA,CACd,IAAA,EACA,IAAA,EACA,OAAA,GAAsB,EAAC,EACF;AACrB,EAAA,OAAO,SAAA,CAAU,UAAA,CAAW,IAAA,EAAM,IAAI,GAAG,OAAO,CAAA;AAClD;AAGA,eAAsB,UAAA,CACpB,IAAA,EACA,IAAA,EACA,EAAE,MAAA,GAAS,KAAA,EAAO,OAAA,GAAU,QAAA,EAAU,GAAG,IAAA,EAAK,GAAuB,EAAC,EACjD;AACrB,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM,CAAA;AAChC,EAAA,MAAM,KAAA,GAAQ,aAAa,OAAO,CAAA;AAClC,EAAA,MAAM,IAAI,OAAA,CAAQ,EAAE,YAAY,EAAA,EAAI,GAAG,MAAM,CAAA;AAC7C,EAAA,MAAM,IAAA,GAAO,UAAA,CAAW,IAAA,EAAM,IAAI,CAAA;AAClC,EAAA,OAAO,SAAS,CAAA,EAAG,OAAO,MAAM,MAAA,CAAO,CAAA,EAAG,MAAM,IAAA,CAAK,CAAA,EAAG,IAAA,EAAM,CAAC,GAAG,CAAA,EAAG,EAAE,OAAO,OAAA,EAAS,KAAA,EAAO,CAAC,CAAA;AACjG;AAOA,eAAsB,UAAA,CACpB,IAAA,EACA,KAAA,EACA,KAAA,EACA,WAAA,EACA,EAAE,GAAA,EAAK,UAAA,GAAa,IAAA,EAAM,GAAG,IAAA,EAAK,EACf;AACnB,EAAA,IAAI,CAAC,WAAA,CAAY,QAAA,CAAS,SAAS,CAAA;AACjC,IAAA,MAAM,IAAIA,UAAAA,CAAW,CAAA,wCAAA,EAA2C,WAAW,CAAA,CAAE,CAAA;AAC/E,EAAA,QAAA,CAAS,WAAW,CAAA;AACpB,EAAA,MAAM,CAAA,GAAI,QAAQ,IAAI,CAAA;AACtB,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAmB;AACtC,EAAA,KAAA,MAAW,KAAK,IAAA,EAAM;AACpB,IAAA,MAAM,CAAA,GAAI,EAAE,KAAK,CAAA;AACjB,IAAA,IAAI,CAAA,KAAM,IAAA,IAAQ,CAAA,KAAM,MAAA,EAAW;AACnC,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA;AAC3B,IAAA,IAAI,MAAA,EAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA;AAAA,SACpB,MAAA,CAAO,GAAA,CAAI,CAAA,EAAG,CAAC,CAAC,CAAC,CAAA;AAAA,EACxB;AACA,EAAA,IAAI,MAAA,CAAO,IAAA,KAAS,CAAA,EAAG,MAAM,IAAIA,WAAW,CAAA,gCAAA,EAAmC,MAAA,CAAO,KAAK,CAAC,CAAA,CAAE,CAAA;AAC9F,EAAA,MAAM,QAAQ,CAAC,GAAG,MAAA,CAAO,IAAA,EAAM,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,YAAY,OAAA,CAAQ,SAAA,EAAW,IAAA,CAAK,CAAC,CAAC,CAAC,CAAA;AACnF,EAAA,MAAM,SAAS,CAAC,GAAG,IAAI,GAAA,CAAI,KAAA,CAAM,OAAO,CAACD,EAAAA,EAAG,CAAA,KAAM,KAAA,CAAM,QAAQA,EAAC,CAAA,KAAM,CAAC,CAAC,CAAC,EAAE,IAAA,EAAK;AACjF,EAAA,IAAI,OAAO,MAAA,GAAS,CAAA;AAElB,IAAA,MAAM,IAAIC,UAAAA;AAAA,MACR,CAAA,0CAAA,EAA6C,MAAA,CAAO,IAAA,CAAK,IAAI,CAAC,CAAA,yBAAA;AAAA,KAChE;AACF,EAAA,MAAM,QAA0C,EAAC;AACjD,EAAA,MAAM,SAAmB,EAAC;AAC1B,EAAA,CAAC,GAAG,MAAM,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAG,EAAE,CAAA,EAAG,CAAA,KAAM;AAClC,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,EAAA,EAAI,CAAC,CAAA;AACzB,MAAA,KAAA,CAAM,IAAA,CAAK;AAAA,QACT,IAAA,EAAM,IAAA,CAAK,GAAA,EAAK,KAAA,CAAM,CAAC,CAAW,CAAA;AAAA,QAClC,IAAA,EAAM,OAAO,KAAA,KAAU,QAAA,GAAW,QAAQ,UAAA,CAAW,KAAA,CAAM,IAAA,EAAM,KAAA,CAAM,IAAI;AAAA,OAC5E,CAAA;AAAA,IACH,SAAS,CAAA,EAAG;AACV,MAAA,MAAA,CAAO,IAAA,CAAK,CAAA,EAAG,MAAA,CAAO,CAAC,CAAC,CAAA,EAAA,EAAK,CAAA,YAAa,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,MAAA,CAAO,CAAC,CAAC,CAAA,CAAE,CAAA;AAAA,IAC3E;AAAA,EACF,CAAC,CAAA;AACD,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,MAAM,IAAI,YAAA,CAAa,CAAA;AAAA,EAAiC,MAAA,CAAO,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AACnG,EAAA,MAAM,KAAA,CAAM,GAAA,EAAK,EAAE,SAAA,EAAW,MAAM,CAAA;AACpC,EAAA,MAAM,GAAA,GAAM,MAAM,QAAA,CAAS,CAAA,EAAG,OAAO,CAAA,KAAM;AACzC,IAAA,MAAM,QAAgB,EAAC;AACvB,IAAA,KAAA,MAAW,EAAA,IAAM,KAAA,EAAO,KAAA,CAAM,IAAA,CAAK,MAAM,KAAK,CAAA,EAAG,EAAA,CAAG,IAAA,EAAM,CAAC,CAAC,CAAA;AAE5D,IAAA,MAAM,QAAA,GAAW,UAAA,GAAa,IAAA,CAAK,GAAA,CAAI,GAAG,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,CAAK,CAAC,CAAC,CAAC,CAAA,GAAI,CAAA;AACzE,IAAA,KAAA,MAAW,CAAC,CAAA,EAAG,EAAE,CAAA,IAAK,MAAM,OAAA,EAAQ;AAClC,MAAA,MAAM,MAAA,CAAO,CAAA,EAAG,KAAA,CAAM,CAAC,GAAW,EAAE,GAAG,CAAA,EAAG,IAAA,EAAM,EAAA,CAAG,IAAA,EAAK,EAAG,EAAE,UAAU,CAAA;AACzE,IAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,EAAA,KAAO,GAAG,IAAI,CAAA;AAAA,EAClC,CAAC,CAAA;AACD,EAAA,IAAI,OAAO,MAAA,GAAS,CAAA;AAClB,IAAA,IAAA;AAAA,MACE,mBAAmB,WAAW,CAAA,CAAA,EAAI,MAAA,CAAO,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA;AAAA,MAClD,CAAA,EAAG,OAAO,MAAM,CAAA;AAAA,EAAuC,MAAA,CAAO,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,KAC1E;AACF,EAAA,OAAO,GAAA;AACT","file":"export.js","sourcesContent":["import { InputError } from \"@sportsdataverse/sdvplot\";\nimport { escapeHtml } from \"../html/escape.js\";\nimport { renderHTML } from \"../html/index.js\";\nimport type { TableSpec } from \"../spec.js\";\n\nexport type TableItem<Row> = { spec: TableSpec<Row>; rows: readonly Row[] } | string;\nexport interface ComposeOptions {\n title?: string;\n subtitle?: string;\n caption?: string;\n sourceNote?: string;\n captionRule?: boolean;\n}\nexport interface GridOptions extends ComposeOptions {\n ncol?: number;\n gap?: number;\n align?: \"top\" | \"center\" | \"bottom\";\n labels?: readonly string[];\n}\nexport interface StackOptions extends ComposeOptions {\n gap?: number;\n align?: \"left\" | \"center\" | \"right\";\n}\n\n/**\n * Python `_STYLE_DEFAULTS` (_export.py:469-475) as one sheet: title 28px/700/#111111 centred, subtitle 16px/400/#666666\n * centred, caption 12px/400/#8A8A8A centred, source note 12px/400/#8A8A8A right, grid label 12px/600/#555555 left, all in\n * `system-ui, -apple-system, sans-serif` (:467). A title with no subtitle keeps the subtitle's 12px gap (:564-565);\n * `captionRule` is a 1px rule UNDER the caption in its colour, padded 6px (:566-567, :574).\n */\nconst COMPOSE_CSS =\n \".sdvt-compose{display:inline-block;font-family:system-ui,-apple-system,sans-serif}\" +\n \".sdvt-compose-title{font-size:28px;font-weight:700;color:#111111;text-align:center;margin:0 0 4px}\" +\n \".sdvt-compose-title:last-child{margin-bottom:12px}\" +\n \".sdvt-compose-subtitle{font-size:16px;font-weight:400;color:#666666;text-align:center;margin:0 0 12px}\" +\n \".sdvt-compose-caption{font-size:12px;font-weight:400;color:#8A8A8A;text-align:center;margin:10px 0 0}\" +\n \".sdvt-compose-source{font-size:12px;font-weight:400;color:#8A8A8A;text-align:right;margin:6px 0 0}\" +\n \".sdvt-compose-rule{border-bottom:1px solid #8A8A8A;padding-bottom:6px}\" +\n \".sdvt-compose-label{font-size:12px;font-weight:600;color:#555555;text-align:left;margin:0 0 6px}\";\n\n/**\n * A group value as `batchToPNG` puts it in a file name (Python `gt_save_batch`): each run of characters outside\n * `A-Za-z0-9._-` becomes \"-\", the ends are trimmed of \"-\", and it is lower-cased.\n * Export plumbing: it may change before 1.0.\n *\n * @beta\n */\nexport function slug(value: unknown): string {\n return String(value)\n .replace(/[^A-Za-z0-9._-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .toLowerCase();\n}\n\nfunction itemHTML<Row>(item: TableItem<Row>): string {\n return typeof item === \"string\" ? item : renderHTML(item.spec, item.rows);\n}\nfunction checkItems<Row>(items: readonly TableItem<Row>[]): void {\n if (items.length === 0) throw new InputError(\"at least one table is required\");\n}\nfunction checkPixels(name: string, value: number): void {\n if (!(Number.isFinite(value) && value >= 0))\n throw new InputError(`${name} must be a non-negative number of pixels, got ${String(value)}`);\n}\n/** Python's `align not in places` check: the CSS value for a known name, else InputError. */\nfunction placeOf(align: string, places: Readonly<Record<string, string>>): string {\n const place = places[align];\n if (place === undefined)\n throw new InputError(\n `align must be one of ${Object.keys(places).join(\", \")}, got ${JSON.stringify(align)}`,\n );\n return place;\n}\n\n/** A table's wrapper as `assemble` writes it (class, then id); ids match /^[A-Za-z][\\w-]*$/, so no regex escaping. */\nconst WRAPPER = /<div class=\"sdvt [^\"]*\" id=\"([A-Za-z][\\w-]*)\"/g;\n\n/**\n * One spec composed twice renders one wrapper id twice: every repeat gets the first free `-2`, `-3`… suffix, on its\n * wrapper (and the `id`/`for` of its controls) and on the `#id` selectors of its own `<style>`s, so each table\n * keeps its decorations. A table is the markup from its wrapper to the next one.\n */\nfunction uniqueIds(body: string): string {\n const starts = [...body.matchAll(WRAPPER)];\n const taken = new Set(starts.map((m) => m[1] as string));\n if (taken.size === starts.length) return body;\n const seen = new Set<string>();\n let out = body.slice(0, starts[0]?.index);\n starts.forEach((m, i) => {\n const id = m[1] as string;\n let table = body.slice(m.index, starts[i + 1]?.index);\n if (seen.has(id)) {\n let n = 2;\n while (taken.has(`${id}-${n}`)) n++;\n const to = `${id}-${n}`;\n taken.add(to);\n table = table\n .replace(new RegExp(`(\\\\s(?:id|for)=\")${id}(?=[\"-])`, \"g\"), `$1${to}`)\n .replace(/<style>[\\s\\S]*?<\\/style>/g, (css) =>\n // a selector only: the next brace is the rule's `{`, so a color equal to the id (`#fade;`) is left alone\n css.replace(new RegExp(`#${id}(?![\\\\w-])(?=[^{}]*\\\\{)`, \"g\"), `#${to}`),\n );\n }\n seen.add(id);\n out += table;\n });\n return out;\n}\n\n/** Title/subtitle above, caption/source note below — the shared frame of gt_grid and gt_stack_tables. */\nexport function composePage(\n body: string,\n { title, subtitle, caption, sourceNote, captionRule = false }: ComposeOptions = {},\n): string {\n const head =\n title || subtitle\n ? `<header class=\"sdvt-compose-head\">${title ? `<div class=\"sdvt-compose-title\">${escapeHtml(title)}</div>` : \"\"}${subtitle ? `<p class=\"sdvt-compose-subtitle\">${escapeHtml(subtitle)}</p>` : \"\"}</header>`\n : \"\";\n const foot =\n caption || sourceNote\n ? `<footer class=\"sdvt-compose-foot\">${caption ? `<p class=\"sdvt-compose-caption${captionRule ? \" sdvt-compose-rule\" : \"\"}\">${escapeHtml(caption)}</p>` : \"\"}${sourceNote ? `<p class=\"sdvt-compose-source\">${escapeHtml(sourceNote)}</p>` : \"\"}</footer>`\n : \"\";\n return `<div class=\"sdvt-compose\"><style>${COMPOSE_CSS}</style>${head}${uniqueIds(body)}${foot}</div>`;\n}\n\n/** Port of gt_grid: small multiples, `ncol` across, rows follow from the count. */\nexport function gridTables<Row>(\n items: readonly TableItem<Row>[],\n { ncol = 2, gap = 24, align = \"top\", labels, ...compose }: GridOptions = {},\n): string {\n checkItems(items);\n if (!Number.isInteger(ncol) || ncol < 1)\n throw new InputError(`ncol must be an integer of at least 1, got ${String(ncol)}`);\n const place = placeOf(align, { top: \"start\", center: \"center\", bottom: \"end\" });\n checkPixels(\"gap\", gap);\n if (labels !== undefined && labels.length === 0)\n throw new InputError(\"labels must be non-empty; omit it for no labels\");\n const cells = items.map((item, i) => {\n const label =\n labels === undefined\n ? \"\"\n : `<div class=\"sdvt-compose-label\">${escapeHtml(labels[i % labels.length] ?? \"\")}</div>`;\n return `<div class=\"sdvt-compose-cell\">${label}${itemHTML(item)}</div>`;\n });\n const grid = `<div class=\"sdvt-grid\" style=\"display:grid;grid-template-columns:repeat(${ncol},max-content);gap:${gap}px;align-items:${place};justify-content:center\">${cells.join(\"\")}</div>`;\n return composePage(grid, compose);\n}\n\n/** Port of gt_stack_tables: a vertical stack. */\nexport function stackTables<Row>(\n items: readonly TableItem<Row>[],\n { gap = 16, align = \"center\", ...compose }: StackOptions = {},\n): string {\n checkItems(items);\n const place = placeOf(align, { left: \"flex-start\", center: \"center\", right: \"flex-end\" });\n checkPixels(\"gap\", gap);\n const stack = `<div class=\"sdvt-stack\" style=\"display:flex;flex-direction:column;gap:${gap}px;align-items:${place}\">${items.map(itemHTML).join(\"\")}</div>`;\n return composePage(stack, compose);\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { InputError, OptionalDependencyError, SdvplotError, warn } from \"@sportsdataverse/sdvplot\";\nimport {\n type Aspect,\n type Gravity,\n canvasFor,\n checkColor,\n offsetFor,\n parseAspect,\n parseGravity,\n peerMissing,\n} from \"@sportsdataverse/sdvplot/export\";\nimport type { Browser, Page } from \"playwright\";\nimport { escapeAttr } from \"../html/escape.js\";\nimport { renderHTML } from \"../html/index.js\";\nimport type { TableSpec } from \"../spec.js\";\nimport { type TableItem, slug } from \"./compose.js\";\n\nexport { gridTables, stackTables, composePage, slug } from \"./compose.js\";\nexport type { ComposeOptions, GridOptions, StackOptions, TableItem } from \"./compose.js\";\n\n/**\n * Options for {@link htmlToPNG} and {@link tableToPNG}; defaults are Python `gt_save_crop`'s. Its `expand` (the page\n * captured before trimming) is fixed here, as it only has to leave background around the content. (The HTML itself\n * takes `RenderOptions` from `@sportsdataverse/sdvtables/html`.)\n */\nexport interface PNGOptions {\n /** the final image width in pixels, the height following (Python `width`); absent: the rendered width */\n width?: number;\n /** the rendering zoom (Python `zoom`); default 2, a sharp (retina) image */\n deviceScaleFactor?: number;\n /** the color around the content; default \"white\" */\n background?: string;\n /** image pixels of `background` left around the trimmed content (Python `whitespace`, truncated to an integer); default 50 */\n whitespace?: number;\n /** stylesheet URLs (Google Fonts) awaited via document.fonts.ready */\n fontLinks?: readonly string[];\n /** also write the PNG here (must end in .png) */\n file?: string;\n}\n/** Options for {@link socialCrop}; defaults are Python `gt_social_crop`'s (whitespace 60). */\nexport interface SocialCropOptions extends PNGOptions {\n aspect?: Aspect;\n gravity?: Gravity;\n}\n/** Options for {@link batchToPNG}, as Python `gt_save_batch` (no `width`; each file is named by its group). */\nexport interface BatchOptions extends Omit<PNGOptions, \"width\" | \"file\"> {\n dir: string;\n matchWidth?: boolean;\n}\n\ninterface Resolved {\n width: number | undefined;\n deviceScaleFactor: number;\n background: string;\n whitespace: number;\n fontLinks: readonly string[];\n file: string | undefined;\n}\n\n/** The page width tables lay out in; a wider table grows the page rather than being clipped. */\nconst VIEWPORT = 1200;\n/** CSS px of page captured around the content, so the trim finds plain background on every side. */\nconst EXPAND = 5;\n/**\n * Chromium's canvas limits (Blink: 65,535 px a side, 32,768 x 8,192 px in all; measured in playwright 1.64's\n * chromium). Past either, `toDataURL` returns \"data:,\" and `getImageData` reads blank, without an error.\n */\nconst MAX_SIDE = 65_535;\nconst MAX_AREA = 32_768 * 8_192;\nconst REMEDY =\n \"put fewer rows in each image (rows.slice() a page at a time, batchToPNG by a group column, or gridTables to set tables side by side), or lower deviceScaleFactor\";\nconst n = (v: number): string => v.toLocaleString(\"en-US\");\n\n/** I1: a canvas past Chromium's limits draws nothing; say so, with the size, before drawing it. */\nfunction checkCanvas(what: string, w: number, h: number, o: Resolved): void {\n if (w <= MAX_SIDE && h <= MAX_SIDE && w * h <= MAX_AREA) return;\n throw new SdvplotError(\n `${what} ${n(w)} × ${n(h)} px at deviceScaleFactor ${o.deviceScaleFactor}, past Chromium's canvas limit of ${n(MAX_SIDE)} px a side and ${n(MAX_AREA)} px in all; ${REMEDY}`,\n );\n}\n\nconst checkPng = (file: string): void => {\n // Python _check_file: the extension must be a format the writer produces; here that is PNG only\n if (!/\\.png$/i.test(file)) throw new InputError(`file must end in .png, got ${JSON.stringify(file)}`);\n};\n\nfunction resolve({\n width,\n deviceScaleFactor = 2,\n background = \"white\",\n whitespace = 50,\n fontLinks = [],\n file,\n}: PNGOptions): Resolved {\n if (width !== undefined && !(Number.isInteger(width) && width > 0))\n throw new InputError(`width must be a positive integer of pixels, got ${String(width)}`);\n if (!(Number.isFinite(deviceScaleFactor) && deviceScaleFactor > 0))\n throw new InputError(`deviceScaleFactor must be a positive number, got ${String(deviceScaleFactor)}`);\n if (!(Number.isFinite(whitespace) && whitespace >= 0))\n throw new InputError(`whitespace must be a non-negative number of pixels, got ${String(whitespace)}`);\n checkColor(\"background\", background);\n if (file !== undefined) checkPng(file);\n // Python _pixels: int()\n return { width, deviceScaleFactor, background, whitespace: Math.trunc(whitespace), fontLinks, file };\n}\n\nasync function launch(): Promise<Browser> {\n const pw = await import(\"playwright\").catch((e: unknown) => {\n throw new OptionalDependencyError(\n peerMissing(e, \"playwright\")\n ? \"sdvtables/export needs the optional peer playwright, which is not installed: pnpm add -D playwright && pnpm exec playwright install chromium\"\n : `sdvtables/export could not load the optional peer playwright (installed, but it failed to load): ${String(e)}`,\n { cause: e },\n );\n });\n try {\n return await pw.chromium.launch();\n } catch (e) {\n if (e instanceof Error && /Executable doesn't exist/.test(e.message))\n throw new OptionalDependencyError(\n \"sdvtables/export found playwright but not its chromium browser: pnpm exec playwright install chromium\",\n { cause: e },\n );\n throw e;\n }\n}\n\nfunction page(body: string, o: Resolved): string {\n const links = o.fontLinks.map((href) => `<link rel=\"stylesheet\" href=\"${escapeAttr(href)}\">`).join(\"\");\n return `<!doctype html><html><head><meta charset=\"utf-8\">${links}<style>html,body{margin:0;background:${o.background}}#sdv-root{display:inline-block;padding:${EXPAND}px;background:${o.background}}</style></head><body><div id=\"sdv-root\">${body}</div></body></html>`;\n}\n\n/** A screenshot (base64 PNG) and its trim box: x, y, width, height in image pixels. */\ninterface Shot {\n png: string;\n trim: [number, number, number, number];\n}\n\n/** In the page: Python `_trim` (ImageMagick, fuzz 0), the box of the pixels unlike the top-left one; all one color: the whole image. */\nasync function trimBox(png: string): Promise<[number, number, number, number]> {\n const img = new Image();\n img.src = `data:image/png;base64,${png}`;\n await img.decode();\n const c = document.createElement(\"canvas\");\n c.width = img.width;\n c.height = img.height;\n const g = c.getContext(\"2d\") as CanvasRenderingContext2D;\n g.drawImage(img, 0, 0);\n const px = new Uint32Array(g.getImageData(0, 0, c.width, c.height).data.buffer);\n const bg = px[0];\n let [x0, y0, x1, y1] = [c.width, c.height, -1, -1];\n for (let y = 0, i = 0; y < c.height; y++)\n for (let x = 0; x < c.width; x++, i++)\n if (px[i] !== bg) {\n if (x < x0) x0 = x;\n if (x > x1) x1 = x;\n if (y < y0) y0 = y;\n y1 = y;\n }\n return x1 < 0 ? [0, 0, c.width, c.height] : [x0, y0, x1 - x0 + 1, y1 - y0 + 1];\n}\n\n/** In the page: the trimmed shot placed at dx, dy on a w x h canvas of `bg`, then scaled to fw x fh; a base64 PNG. */\nasync function drawPNG(a: {\n png: string;\n trim: [number, number, number, number];\n w: number;\n h: number;\n dx: number;\n dy: number;\n fw: number;\n fh: number;\n bg: string;\n}): Promise<string> {\n const img = new Image();\n img.src = `data:image/png;base64,${a.png}`;\n await img.decode();\n const c = document.createElement(\"canvas\");\n c.width = a.w;\n c.height = a.h;\n const g = c.getContext(\"2d\") as CanvasRenderingContext2D;\n g.fillStyle = a.bg;\n g.fillRect(0, 0, a.w, a.h);\n const [x, y, tw, th] = a.trim;\n g.drawImage(img, x, y, tw, th, a.dx, a.dy, tw, th);\n let out = c;\n if (a.fw !== a.w || a.fh !== a.h) {\n out = document.createElement(\"canvas\");\n out.width = a.fw;\n out.height = a.fh;\n const s = out.getContext(\"2d\") as CanvasRenderingContext2D;\n s.imageSmoothingQuality = \"high\";\n s.drawImage(c, 0, 0, a.fw, a.fh);\n }\n return out.toDataURL(\"image/png\").slice(\"data:image/png;base64,\".length);\n}\n\n/** `html` rendered at the zoom (a wide table is never clipped) and trimmed: Python `_trim(_render_gt(...))`. */\nasync function snap(p: Page, html: string, o: Resolved): Promise<Shot> {\n await p.setViewportSize({ width: VIEWPORT, height: 800 });\n await p.setContent(page(html, o), { waitUntil: \"load\" });\n await p.evaluate(() => document.fonts.ready);\n const root = p.locator(\"#sdv-root\");\n const box = await root.boundingBox();\n if (box) {\n const z = o.deviceScaleFactor;\n checkCanvas(\"the table renders\", Math.ceil(box.width * z), Math.ceil(box.height * z), o);\n }\n if (box && box.width > VIEWPORT) await p.setViewportSize({ width: Math.ceil(box.width), height: 800 });\n const png = Buffer.from(await root.screenshot({ type: \"png\" })).toString(\"base64\");\n return { png, trim: await p.evaluate(trimBox, png) };\n}\n\n/**\n * Python's finishing steps, in image pixels: `_extent` to `minWidth` (centred), `_pad` by `whitespace`, the social\n * `_canvas` + `_extent` by gravity, `_fit_width` to `width`. Writes `o.file` when set.\n */\nasync function finish(\n p: Page,\n shot: Shot,\n o: Resolved,\n { minWidth = 0, ratio, gravity = \"center\" }: { minWidth?: number; ratio?: number; gravity?: Gravity } = {},\n): Promise<Uint8Array> {\n const [, , tw, th] = shot.trim;\n const ws = o.whitespace;\n let [w, h] = [Math.max(tw, minWidth) + 2 * ws, th + 2 * ws];\n let [dx, dy] = [Math.floor((w - 2 * ws - tw) / 2) + ws, ws];\n if (ratio !== undefined) {\n const [cw, ch] = canvasFor(w, h, ratio);\n const [ox, oy] = offsetFor(cw - w, ch - h, gravity);\n [w, h, dx, dy] = [cw, ch, dx + ox, dy + oy];\n }\n // Python _fit_width: the height rounded half up, as ImageMagick\n const [fw, fh] =\n o.width === undefined ? [w, h] : [o.width, Math.max(1, Math.floor((h * o.width) / w + 0.5))];\n checkCanvas(\"the image would be\", w, h, o);\n checkCanvas(\"the image scaled to `width` would be\", fw, fh, o);\n const b64 = await p.evaluate(drawPNG, {\n png: shot.png,\n trim: shot.trim,\n w,\n h,\n dx,\n dy,\n fw,\n fh,\n bg: o.background,\n });\n // \"data:,\" (no base64): a canvas Chromium would not allocate; never return or write an empty image\n if (b64 === \"\")\n throw new SdvplotError(`Chromium returned an empty image for ${n(fw)} × ${n(fh)} px; ${REMEDY}`);\n const png = new Uint8Array(Buffer.from(b64, \"base64\"));\n if (o.file !== undefined) await writeFile(o.file, png);\n return png;\n}\n\nasync function withPage<T>(o: Resolved, fn: (p: Page) => Promise<T>): Promise<T> {\n const browser = await launch();\n try {\n return await fn(await browser.newPage({ deviceScaleFactor: o.deviceScaleFactor }));\n } finally {\n await browser.close();\n }\n}\n\n/**\n * gt_save_crop for any HTML: rendered at `deviceScaleFactor`, trimmed to its content, `whitespace` image pixels of\n * `background` put back around it, then scaled to `width` when given. An image past Chromium's canvas limit (65,535\n * px a side, 268,435,456 px in all; near 1,000 plain rows at zoom 2) throws `SdvplotError` naming its size, before\n * anything is drawn or written.\n */\nexport async function htmlToPNG(html: string, options: PNGOptions = {}): Promise<Uint8Array> {\n const o = resolve(options);\n return withPage(o, async (p) => finish(p, await snap(p, html, o), o));\n}\n\n/** gt_save_crop: {@link htmlToPNG} of the rendered table. */\nexport function tableToPNG<Row>(\n spec: TableSpec<Row>,\n rows: readonly Row[],\n options: PNGOptions = {},\n): Promise<Uint8Array> {\n return htmlToPNG(renderHTML(spec, rows), options);\n}\n\n/** gt_social_crop: the trimmed, padded table on a fixed-ratio canvas placed by `gravity`; the table is never cropped. */\nexport async function socialCrop<Row>(\n spec: TableSpec<Row>,\n rows: readonly Row[],\n { aspect = \"1:1\", gravity = \"center\", ...rest }: SocialCropOptions = {},\n): Promise<Uint8Array> {\n const ratio = parseAspect(aspect);\n const place = parseGravity(gravity);\n const o = resolve({ whitespace: 60, ...rest });\n const html = renderHTML(spec, rows);\n return withPage(o, async (p) => finish(p, await snap(p, html, o), o, { ratio, gravity: place }));\n}\n\n/**\n * gt_save_batch: one image per group value, `{group}` in the file name replaced by `slug(value)`. A group whose table\n * fails to build (`build` or the spec throws) is skipped and named in one warning at the end; when none builds, the\n * error lists every failure. Python's `quiet` progress lines are not ported.\n */\nexport async function batchToPNG<Row, G extends keyof Row>(\n rows: readonly Row[],\n group: G,\n build: (groupRows: Row[], value: Row[G]) => TableItem<Row>,\n filePattern: string,\n { dir, matchWidth = true, ...rest }: BatchOptions,\n): Promise<string[]> {\n if (!filePattern.includes(\"{group}\"))\n throw new InputError(`filePattern must contain \"{group}\", got ${filePattern}`);\n checkPng(filePattern);\n const o = resolve(rest);\n const groups = new Map<Row[G], Row[]>();\n for (const r of rows) {\n const v = r[group];\n if (v === null || v === undefined) continue;\n const bucket = groups.get(v);\n if (bucket) bucket.push(r);\n else groups.set(v, [r]);\n }\n if (groups.size === 0) throw new InputError(`no non-missing values in column ${String(group)}`);\n const names = [...groups.keys()].map((v) => filePattern.replace(\"{group}\", slug(v)));\n const shared = [...new Set(names.filter((n, i) => names.indexOf(n) !== i))].sort();\n if (shared.length > 0)\n // Python gt_save_batch: two values must never write one file\n throw new InputError(\n `group values write to the same file name: ${shared.join(\", \")}; rename the values first`,\n );\n const items: { file: string; html: string }[] = [];\n const failed: string[] = [];\n [...groups].forEach(([v, rs], i) => {\n try {\n const built = build(rs, v);\n items.push({\n file: join(dir, names[i] as string),\n html: typeof built === \"string\" ? built : renderHTML(built.spec, built.rows),\n });\n } catch (e) {\n failed.push(`${String(v)}: ${e instanceof Error ? e.message : String(e)}`);\n }\n });\n if (items.length === 0) throw new SdvplotError(`No group built successfully:\\n${failed.join(\"\\n\")}`);\n await mkdir(dir, { recursive: true });\n const out = await withPage(o, async (p) => {\n const shots: Shot[] = [];\n for (const it of items) shots.push(await snap(p, it.html, o));\n // Python: every trimmed image extended (centred) to the widest one's width, then padded\n const minWidth = matchWidth ? Math.max(...shots.map((s) => s.trim[2])) : 0;\n for (const [i, it] of items.entries())\n await finish(p, shots[i] as Shot, { ...o, file: it.file }, { minWidth });\n return items.map((it) => it.file);\n });\n if (failed.length > 0)\n warn(\n `sdvtables:batch:${filePattern}:${failed.join(\"|\")}`,\n `${failed.length} group(s) failed and were skipped:\\n${failed.join(\"\\n\")}`,\n );\n return out;\n}\n"]}
|
package/dist/html.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { League } from '@sportsdataverse/sdvplot';
|
|
2
|
-
import { S as Sort, T as TableCursor, a as Table } from './engine-
|
|
3
|
-
export { c as createTable } from './engine-
|
|
4
|
-
import { C as ColumnSpec, T as TableSpec, a as ThemeRef, D as Density } from './spec-
|
|
5
|
-
import { T as Theme, G as GoogleFont } from './tokens-
|
|
2
|
+
import { S as Sort, T as TableCursor, a as Table } from './engine-BFIPJUlC.js';
|
|
3
|
+
export { c as createTable } from './engine-BFIPJUlC.js';
|
|
4
|
+
import { C as ColumnSpec, T as TableSpec, a as ThemeRef, I as IconName, D as Density } from './spec-A88vJX5A.js';
|
|
5
|
+
import { T as Theme, G as GoogleFont } from './tokens-CHwBt9gs.js';
|
|
6
6
|
|
|
7
7
|
interface RenderOptions {
|
|
8
8
|
readonly css?: "inline" | "none";
|
|
@@ -124,6 +124,9 @@ interface RenderContext<Row> {
|
|
|
124
124
|
/** The Google Fonts css2 `<link>` for these faces (weights merged per family, families sorted); "" when there are none. */
|
|
125
125
|
declare function fontsLink(fonts: readonly GoogleFont[]): string;
|
|
126
126
|
|
|
127
|
+
/** Every icon name iconSet / iconAssign accept. */
|
|
128
|
+
declare const ICON_NAMES: readonly IconName[];
|
|
129
|
+
|
|
127
130
|
/** J31: the engine id of the body row an event landed in; null for a header, toolbar, pager, group row, or outside the table. */
|
|
128
131
|
declare function rowIdAt<Row>(table: Table<Row>, target: EventTarget | null): string | null;
|
|
129
132
|
/** A click anywhere inside the table wrapper — shared by hydrate and `<SdvTable/>`. */
|
|
@@ -327,4 +330,4 @@ declare function themePreview<Row>(spec: TableSpec<Row>, rows: readonly Row[], t
|
|
|
327
330
|
density?: Density;
|
|
328
331
|
}): Record<string, string>;
|
|
329
332
|
|
|
330
|
-
export { type ColumnScale, GoogleFont, type KeyAction, type KeyContext, type RenderContext, type RenderOptions, type RenderedParts, assemble, attrsText, labelOf as columnLabel, fontsLink, fontsLinkFor, handleClick, handleHover, handleInput, handleKeydown, hydrate, keyAction, labelOf, leaguesOf, pagerLabel, prepare, renderHTML, renderHTMLAsync, renderInteractive, renderPager, renderParts, renderToolbar, rowIdAt, sortAria, styleSheet, tableHTML, tableRenderOptions, themeKey, themePreview, toElement };
|
|
333
|
+
export { type ColumnScale, GoogleFont, ICON_NAMES, IconName, type KeyAction, type KeyContext, type RenderContext, type RenderOptions, type RenderedParts, assemble, attrsText, labelOf as columnLabel, fontsLink, fontsLinkFor, handleClick, handleHover, handleInput, handleKeydown, hydrate, keyAction, labelOf, leaguesOf, pagerLabel, prepare, renderHTML, renderHTMLAsync, renderInteractive, renderPager, renderParts, renderToolbar, rowIdAt, sortAria, styleSheet, tableHTML, tableRenderOptions, themeKey, themePreview, toElement };
|
package/dist/html.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { hydrate, leaguesOf, prepare, renderHTML, renderHTMLAsync, themePreview, toElement } from './chunk-
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
1
|
+
export { hydrate, leaguesOf, prepare, renderHTML, renderHTMLAsync, themePreview, toElement } from './chunk-VLF2S6VW.js';
|
|
2
|
+
export { handleClick, handleHover, handleInput, handleKeydown, keyAction, pagerLabel, renderInteractive, renderPager, renderToolbar, rowIdAt, tableRenderOptions } from './chunk-3TJR7TKS.js';
|
|
3
|
+
export { ICON_NAMES, assemble, attrsText, labelOf as columnLabel, fontsLink, fontsLinkFor, labelOf, renderParts, sortAria, styleSheet, tableHTML, themeKey } from './chunk-R375P3MN.js';
|
|
4
|
+
export { createTable } from './chunk-UTOVFIFT.js';
|
|
4
5
|
//# sourceMappingURL=html.js.map
|
|
5
6
|
//# sourceMappingURL=html.js.map
|