@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.
Files changed (42) hide show
  1. package/README.md +70 -112
  2. package/data/table_themes.json +12136 -0
  3. package/data/table_themes.json.sha256 +1 -0
  4. package/dist/aggrid.d.ts +104 -0
  5. package/dist/aggrid.js +230 -0
  6. package/dist/aggrid.js.map +1 -0
  7. package/dist/chunk-3TJR7TKS.js +223 -0
  8. package/dist/chunk-3TJR7TKS.js.map +1 -0
  9. package/dist/{chunk-UCRLJGDL.js → chunk-R375P3MN.js} +963 -276
  10. package/dist/chunk-R375P3MN.js.map +1 -0
  11. package/dist/chunk-UTOVFIFT.js +15176 -0
  12. package/dist/chunk-UTOVFIFT.js.map +1 -0
  13. package/dist/{chunk-U6W6MWKW.js → chunk-VLF2S6VW.js} +7 -5
  14. package/dist/chunk-VLF2S6VW.js.map +1 -0
  15. package/dist/chunk-WBTFIGYK.js +189 -0
  16. package/dist/chunk-WBTFIGYK.js.map +1 -0
  17. package/dist/{engine-CkfxYXAL.d.ts → engine-BFIPJUlC.d.ts} +1 -1
  18. package/dist/export.d.ts +1 -1
  19. package/dist/export.js +4 -3
  20. package/dist/export.js.map +1 -1
  21. package/dist/html.d.ts +8 -5
  22. package/dist/html.js +4 -3
  23. package/dist/index.d.ts +121 -8
  24. package/dist/index.js +51 -3
  25. package/dist/index.js.map +1 -1
  26. package/dist/mui.d.ts +134 -0
  27. package/dist/mui.js +193 -0
  28. package/dist/mui.js.map +1 -0
  29. package/dist/react.d.ts +2 -2
  30. package/dist/react.js +3 -2
  31. package/dist/react.js.map +1 -1
  32. package/dist/spec-A88vJX5A.d.ts +748 -0
  33. package/dist/tanstack-table.d.ts +113 -0
  34. package/dist/tanstack-table.js +98 -0
  35. package/dist/tanstack-table.js.map +1 -0
  36. package/dist/{tokens-CpYFsFtf.d.ts → tokens-CHwBt9gs.d.ts} +1 -1
  37. package/package.json +40 -3
  38. package/dist/chunk-4VKYHOHL.js +0 -2081
  39. package/dist/chunk-4VKYHOHL.js.map +0 -1
  40. package/dist/chunk-U6W6MWKW.js.map +0 -1
  41. package/dist/chunk-UCRLJGDL.js.map +0 -1
  42. package/dist/spec-BEhLDtj_.d.ts +0 -430
package/README.md CHANGED
@@ -90,115 +90,6 @@ fonts yourself (`fontsLink(fonts)` builds the same tag). `toElement` moves the l
90
90
  Decoration CSS is scoped to the table `#id`. Two renders of the same spec on one page therefore need distinct ids:
91
91
  call `.id("a")` / `.id("b")` on the builder.
92
92
 
93
- ## **TanStack Table interop (recipe)**
94
-
95
- sdvtables has its own headless engine and takes no TanStack dependency. If your app already renders tables with
96
- `@tanstack/react-table` 8, map a `TableSpec` to `ColumnDef`s: the header comes from the spec, and each
97
- cell reuses the markup `renderHTML` draws (logos, pills, bars), read back from `toElement()` by `data-row` / `data-col`.
98
- Sorting uses the spec's `sortable` and `compare`, the fields [the engine](#interactive) reads: a column without
99
- `compare` falls back to TanStack's default sort, and `compare` stays ascending because TanStack inverts it for
100
- descending itself.
101
-
102
- `toElement` needs a `document`, and a `"use client"` component is still server-rendered in Next, so build the columns
103
- only after mount: in a `useEffect` (below), or load the table component with `next/dynamic(..., { ssr: false })`.
104
-
105
- ```tsx
106
- import type { TableSpec } from "@sportsdataverse/sdvtables";
107
- import { columnLabel, toElement } from "@sportsdataverse/sdvtables/html";
108
- import type { ColumnDef } from "@tanstack/react-table";
109
-
110
- /** sdvtables columns -> TanStack column defs. Cells reuse renderHTML's own markup (logos, pills, bars); wrap the
111
- * table in `<div className={themeKey(spec.theme)}>` and emit `<style>{styleSheet(spec)}</style>` once. */
112
- export function toTanStackColumns<Row>(
113
- spec: TableSpec<Row>,
114
- rows: readonly Row[],
115
- ): ColumnDef<Row, unknown>[] {
116
- const cells = new Map<string, Element>(); // "row|col" -> sdvtables' <td>
117
- // needs a DOM (throws TableSpecError without a document): call this after mount, never during render
118
- for (const td of Array.from(toElement(spec, rows).querySelectorAll("tr[data-row] > td[data-col]")))
119
- cells.set(`${td.parentElement?.getAttribute("data-row")}|${td.getAttribute("data-col")}`, td);
120
- return spec.columns.map(
121
- (c): ColumnDef<Row, unknown> => ({
122
- id: c.key,
123
- accessorFn: (row) => row[c.key],
124
- header: columnLabel(c),
125
- enableSorting: c.sortable !== false,
126
- ...(c.compare ? { sortingFn: (a, b, id) => c.compare?.(a.getValue(id), b.getValue(id)) ?? 0 } : {}),
127
- cell: (ctx) => {
128
- const cell = cells.get(`${ctx.row.index}|${c.key}`); // row.index is the input order, as data-row is
129
- // pill / rank fills live on sdvtables' own <td style>, so carry that over too
130
- return (
131
- <div
132
- ref={(n) => n?.setAttribute("style", cell?.getAttribute("style") ?? "")}
133
- // biome-ignore lint/security/noDangerouslySetInnerHtml: sdvtables escapes every value it renders
134
- dangerouslySetInnerHTML={{ __html: cell?.innerHTML ?? "" }}
135
- />
136
- );
137
- },
138
- }),
139
- );
140
- }
141
- ```
142
-
143
- ```tsx
144
- "use client";
145
- import type { TableSpec } from "@sportsdataverse/sdvtables";
146
- import { prepare, styleSheet, themeKey } from "@sportsdataverse/sdvtables/html";
147
- import { type ColumnDef, flexRender, getCoreRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
148
- import { type ReactElement, useEffect, useState } from "react";
149
- import { toTanStackColumns } from "./to-tanstack-columns"; // the first fence
150
-
151
- export function StandingsTable<Row>({ spec, rows }: { spec: TableSpec<Row>; rows: Row[] }): ReactElement {
152
- // empty on the server and on the first client render; filled once the effect has run
153
- const [columns, setColumns] = useState<ColumnDef<Row, unknown>[]>([]);
154
- useEffect(() => {
155
- let live = true;
156
- void prepare(spec).then(() => {
157
- if (live) setColumns(toTanStackColumns(spec, rows));
158
- });
159
- return () => {
160
- live = false;
161
- };
162
- }, [spec, rows]);
163
-
164
- const table = useReactTable({
165
- data: rows, // keep this reference stable: a new array every render makes TanStack loop
166
- columns,
167
- getCoreRowModel: getCoreRowModel(),
168
- getSortedRowModel: getSortedRowModel(),
169
- });
170
- return (
171
- <div className={themeKey(spec.theme)}>
172
- <style>{styleSheet(spec)}</style>
173
- <table>
174
- <thead>
175
- {table.getHeaderGroups().map((g) => (
176
- <tr key={g.id}>
177
- {g.headers.map((h) => (
178
- <th key={h.id}>{flexRender(h.column.columnDef.header, h.getContext())}</th>
179
- ))}
180
- </tr>
181
- ))}
182
- </thead>
183
- <tbody>
184
- {table.getRowModel().rows.map((r) => (
185
- <tr key={r.id}>
186
- {r.getVisibleCells().map((c) => (
187
- <td key={c.id}>{flexRender(c.column.columnDef.cell, c.getContext())}</td>
188
- ))}
189
- </tr>
190
- ))}
191
- </tbody>
192
- </table>
193
- </div>
194
- );
195
- }
196
- ```
197
-
198
- Limits: `snake` and tier layouts change which `<tr>` holds which row, so the recipe covers plain specs; table-level
199
- decorations (title, caption, stripes) are not cells and are not carried over. Written for `@tanstack/react-table` 8
200
- (sdv-web pins `^8.21.3`); v9 is a breaking major and is not covered.
201
-
202
93
  ## **Themes**
203
94
 
204
95
  20 themes: the 18 gtUtils themes plus `sdv` (light/dark) and `sdvTeam`. Set one with
@@ -227,10 +118,12 @@ decorations (title, caption, stripes) are not cells and are not carried over. Wr
227
118
 
228
119
  ## **Surface**
229
120
 
230
- - 21 column kinds on the `c` factory (`text`, `num`, `int`, `pct`, `rank`, `delta`, `tally`, `logo`, `wordmark`,
121
+ - 29 column kinds on the `c` factory (`text`, `num`, `int`, `pct`, `rank`, `delta`, `tally`, `logo`, `wordmark`,
231
122
  `headshot`, `colorPills`, `colorRanks`, `colorResults`, `percentileBar`, `indicatorBox`, `highlight`,
232
- `highlightNa`, `mergeStackTeamColor`, `teamColorBar`, `teamColorBg`, `image`; `fmtRank`/`fmtTally` alias
233
- `rank`/`tally`).
123
+ `highlightNa`, `mergeStackTeamColor`, `teamColorBar`, `teamColorBg`, `image`, and the reactablefmtr formatters
124
+ `dataBar`, `iconSet`, `iconAssign`, `mergeStack`, `bubble`, and the in-cell charts `sparkline`, `sparkbar`, `gauge`
125
+ (inline SVG, array-valued columns for the first two); `fmtRank`/`fmtTally` alias `rank`/`tally`).
126
+ Any column takes `tooltip: true` (the value as a `title` attribute).
234
127
  - 25 table decorations as builder methods (titles, legends, cutlines, row accents, snake layout, tiers, watermark, ...).
235
128
  - Conditions are data predicates (`{ key, op, value }`), not callbacks, so specs stay serializable.
236
129
  - Number cells display negatives with U+2212 (a true minus); `formatValue`-style labels (legends, notes) stay ASCII.
@@ -348,6 +241,71 @@ function Standings() {
348
241
  sort, filters, page and selection; only a structural `spec` change builds a new engine. Without `interactive` the
349
242
  component renders the static table. The `react` entry is marked `"use client"`.
350
243
 
244
+ ### MUI X Data Grid
245
+
246
+ ```tsx
247
+ import { DataGrid } from "@mui/x-data-grid"; // peer: @mui/x-data-grid >= 9.15.0 (MIT; never -pro or -premium)
248
+ import { prepare } from "@sportsdataverse/sdvtables/html";
249
+ import { dataGridProps } from "@sportsdataverse/sdvtables/mui";
250
+
251
+ await prepare(spec);
252
+ <DataGrid rows={rows} {...dataGridProps(spec, rows, { teamRows: { key: "team", league: "nfl" } })} autoHeight />;
253
+ ```
254
+
255
+ `dataGridProps(spec, rows, o)` maps a spec onto MUI X Data Grid: one `GridColDef` per column (label, alignment, px
256
+ width, sdvtables' number formats through `valueFormatter`), logo, wordmark and headshot cells through `renderCell`,
257
+ `teamColorBg` / `teamColorBar` cells and `teamRows` in sdvplot's team palette (`@sportsdataverse/sdvplot/mui`'s
258
+ `teamColorPalette`) with black or white text by contrast, and the theme's fonts, colours, stripes and density as the
259
+ grid's `sx` and `density`. It never imports MUI. Kinds drawn in HTML or SVG (pills, bars, sparklines...) show their
260
+ plain value, with one warning per kind. Guide: [MUI X Data Grid](https://plot.sportsdataverse.org/guides/mui-x-data-grid).
261
+
262
+ ### AG Grid
263
+
264
+ ```ts
265
+ import { AllCommunityModule, ModuleRegistry, createGrid, themeQuartz } from "ag-grid-community"; // peer: >= 36.2.0 (MIT)
266
+ import { gridOptions } from "@sportsdataverse/sdvtables/aggrid";
267
+ import { prepare } from "@sportsdataverse/sdvtables/html";
268
+
269
+ ModuleRegistry.registerModules([AllCommunityModule]);
270
+ await prepare(spec);
271
+ const el = document.querySelector("#grid")!; // AG Grid fills its container: size it, or use domLayout: "autoHeight"
272
+ createGrid(el, {
273
+ ...gridOptions(spec, rows, { theme: themeQuartz, teamRows: { key: "team", league: "nfl" } }),
274
+ domLayout: "autoHeight",
275
+ });
276
+ ```
277
+
278
+ `gridOptions(spec, rows, o)` maps a spec onto AG Grid Community: one column definition per column (label, alignment,
279
+ px width, sdvtables' number formats through `valueFormatter`, sdvtables' sort with missing values last both ways),
280
+ logo, wordmark and headshot cells through cell renderer classes that work in plain JavaScript and in ag-grid-react,
281
+ `teamColorBg` / `teamColorBar` cells and `teamRows` in sdvplot's team palette with black or white text by contrast
282
+ (kept through AG Grid's hover and selection), unique row ids, and, given AG Grid's `themeQuartz` (or another base), the
283
+ theme's fonts and colours through AG Grid's Theming API. It never imports AG Grid at runtime. Community only: nothing
284
+ here uses or needs `ag-grid-enterprise`. Guide: [AG Grid](https://plot.sportsdataverse.org/guides/ag-grid).
285
+
286
+ ### TanStack Table v9
287
+
288
+ `@sportsdataverse/sdvtables/tanstack-table` turns a spec into TanStack Table v9 options:
289
+ `tanstackTable(spec, rows, { features, render })` returns `options` for React's `useTable` or Svelte's `createTable`
290
+ (`features` is your own `tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel() })`, passed through)
291
+ and `columnStyle`, each column's alignment and width. Columns keep the HTML table's labels and number text (a
292
+ `scaleNote` and ordinals included), sort with sdvtables' comparator with missing values last both ways, and take their
293
+ row ids from the spec's `rowKey` (a repeated or blank value throws). Logo, wordmark, headshot and superscript-rank cells
294
+ come to your `render` as plain data (`CellPart`), which you turn into a React element or a Svelte component; a team or
295
+ player with no image keeps its text and warns once. It imports table-core's types only and touches no DOM, so it runs on
296
+ a server. Peer: `@tanstack/table-core` >= 9.3.1, optional (its types are in the declarations, so TypeScript needs it
297
+ installed). Call `await prepare(spec)` first.
298
+
299
+ The guide, [TanStack Table](https://plot.sportsdataverse.org/guides/tanstack-table), shows a sortable React component
300
+ and a Svelte 5 one (`examples/snippets/tanstack-table/SdvTable.tsx` and `examples/snippets/svelte/TanstackStandings.svelte`
301
+ in this repository), both compiled and rendered by the tests.
302
+
303
+ **From the v8 recipe.** This README used to carry an untested recipe for `@tanstack/react-table` 8 (`toTanStackColumns`,
304
+ reading each cell's markup back from `toElement()`). TanStack Table v9 changed its API: `useTable` replaces
305
+ `useReactTable`, features and row models are registered in `tableFeatures` (`createSortedRowModel()` where v8 took
306
+ `getSortedRowModel()`), `sortingFn` is `sortFn`, and `ColumnDef` takes the features' type first. Use `tanstackTable`
307
+ instead; it needs no DOM, so the columns no longer wait for a mount.
308
+
351
309
  ## **Export (Node; peer: playwright)**
352
310
 
353
311
  `@sportsdataverse/sdvtables/export` renders tables to PNG in headless Chromium. Its functions port sdvplotR's