@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
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
|
-
-
|
|
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
|
|
233
|
-
`
|
|
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
|