@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
@@ -1,2081 +0,0 @@
1
- import { SdvplotError, mix, contrast, resolveSync, UnresolvedTeamError, teamColorsSync, warn, onColor, hex6 } from '@sportsdataverse/sdvplot';
2
-
3
- // src/errors.ts
4
- var TableSpecError = class extends SdvplotError {
5
- };
6
-
7
- // src/define.ts
8
- var RANK_PALETTE = ["#3D8B6E", "#9DC5A7", "#EDE0CC", "#DB9070", "#BE4D3A"];
9
- function columnFactory() {
10
- const f = {
11
- text: (key, o = {}) => ({ kind: "text", key, ...o }),
12
- num: (key, o = {}) => ({ kind: "num", key, ...o }),
13
- int: (key, o = {}) => ({ kind: "int", key, ...o }),
14
- pct: (key, o = {}) => ({ kind: "pct", key, digits: 1, scale: true, ...o }),
15
- rank: (key, o = {}) => ({ kind: "rank", key, superscript: true, suffixSize: "0.7em", ...o }),
16
- delta: (from, to, o = {}) => ({
17
- kind: "delta",
18
- key: from,
19
- to,
20
- label: "Change",
21
- percent: false,
22
- decimals: 1,
23
- arrows: false,
24
- color: true,
25
- colorPositive: "#1B7837",
26
- colorNegative: "#B2182B",
27
- forceSign: true,
28
- ...o
29
- }),
30
- tally: (keys, o = {}) => ({
31
- kind: "tally",
32
- key: keys[0],
33
- keys,
34
- separator: "-",
35
- share: false,
36
- shareOf: 0,
37
- shareDecimals: 1,
38
- shareLabel: "%",
39
- sharePrefix: " (",
40
- shareSuffix: ")",
41
- ...o
42
- }),
43
- logo: (key, o) => ({ kind: "logo", key, height: 30, includeName: false, ...o }),
44
- wordmark: (key, o) => ({ kind: "wordmark", key, height: 30, ...o }),
45
- headshot: (key, o) => ({ kind: "headshot", key, idSystem: "espn", height: 30, ...o }),
46
- colorPills: (key, o = {}) => ({
47
- kind: "colorPills",
48
- key,
49
- palette: ["#C84630", "#5DA271"],
50
- fillType: "continuous",
51
- rankOrder: "desc",
52
- formatType: "number",
53
- scalePercent: true,
54
- suffix: "",
55
- reverse: false,
56
- outlineWidth: 0.25,
57
- pillHeight: 25,
58
- ...o
59
- }),
60
- colorRanks: (key, o = {}) => ({ kind: "colorRanks", key, palette: RANK_PALETTE, reverse: false, ...o }),
61
- // gt_color_results fills the whole row (_cells.py:155-222); spec §6.1 lists it among the c.* kinds
62
- colorResults: (key, o = {}) => ({
63
- kind: "colorResults",
64
- key,
65
- winColor: "#5DA271",
66
- lossColor: "#C84630",
67
- winTextColor: "white",
68
- lossTextColor: "white",
69
- tieTextColor: "white",
70
- tieValue: "T",
71
- resultType: "wl",
72
- ...o
73
- }),
74
- // width: the <th> width, Python gt_percentile_bar width=220 px (_layout.py:662-684)
75
- percentileBar: (key, o = {}) => ({
76
- kind: "percentileBar",
77
- key,
78
- domain: [0, 100],
79
- scale: "auto",
80
- palette: ["#3661AD", "#C9C9C9", "#D22D49"],
81
- reverse: false,
82
- trackColor: "#E9E9E9",
83
- trackHeight: 6,
84
- markerSize: 22,
85
- textColor: "#FFFFFF",
86
- ringWidth: 2,
87
- fullTrack: true,
88
- naLabel: "\u2014",
89
- naTextColor: "#9A9A9A",
90
- decimals: 0,
91
- width: "220px",
92
- ...o
93
- }),
94
- // gt_indicator_boxes: color_yes #FCCF10, color_no #EEEEEE, 20x20 box, yes = value 1 (_cells.py:1337-1347, :1414-1416)
95
- indicatorBox: (key, o = {}) => ({
96
- kind: "indicatorBox",
97
- key,
98
- truthy: [1, true, "1"],
99
- fill: "#FCCF10",
100
- neutral: "#EEEEEE",
101
- size: 20,
102
- ...o
103
- }),
104
- highlight: (key, when, o = {}) => ({ kind: "highlight", key, when, fill: "#FFF3B0", bold: false, ...o }),
105
- highlightNa: (key, o = {}) => ({
106
- kind: "highlightNa",
107
- key,
108
- fill: "#F0F0F0",
109
- bold: false,
110
- italic: false,
111
- naStrings: ["NA"],
112
- ignoreCase: false,
113
- ...o
114
- }),
115
- mergeStackTeamColor: (key, stack, team, o) => ({
116
- kind: "mergeStackTeamColor",
117
- key,
118
- stack,
119
- team,
120
- fontSizeTop: 14,
121
- fontSizeBottom: 12,
122
- color: "black",
123
- ...o
124
- }),
125
- teamColorBar: (key, o) => ({
126
- kind: "teamColorBar",
127
- key,
128
- which: "primary",
129
- naColor: "#b3b3b3",
130
- barWidth: 4,
131
- ...o
132
- }),
133
- teamColorBg: (key, o) => ({
134
- kind: "teamColorBg",
135
- key,
136
- which: "primary",
137
- alpha: 0.4,
138
- naColor: "#b3b3b3",
139
- ...o
140
- }),
141
- image: (key, o = {}) => ({ kind: "image", key, height: "55px", ...o })
142
- };
143
- return { ...f, fmtRank: f.rank, fmtTally: f.tally };
144
- }
145
- var THEME_DENSITY = {
146
- almanac: "compact",
147
- scoreboard: "compact",
148
- terminal: "compact"
149
- };
150
- var TableBuilder = class _TableBuilder {
151
- constructor(s) {
152
- this.s = s;
153
- }
154
- s;
155
- add(d) {
156
- return new _TableBuilder({ ...this.s, decorations: [...this.s.decorations, d] });
157
- }
158
- id(id) {
159
- return new _TableBuilder({ ...this.s, id });
160
- }
161
- /** J31: the link id column (see `TableSpec.rowKey`). */
162
- rowKey(key) {
163
- return new _TableBuilder({ ...this.s, rowKey: key });
164
- }
165
- columns(fn) {
166
- return new _TableBuilder({ ...this.s, columns: fn(columnFactory()) });
167
- }
168
- theme(name, o = {}) {
169
- return new _TableBuilder({
170
- ...this.s,
171
- theme: {
172
- name,
173
- density: o.density ?? THEME_DENSITY[name] ?? "comfortable",
174
- ...o.options ? { options: o.options } : {}
175
- }
176
- });
177
- }
178
- title(text) {
179
- return this.add({ type: "title", text });
180
- }
181
- subtitle(text) {
182
- return this.add({ type: "subtitle", text });
183
- }
184
- titleHeader(title, o = {}) {
185
- return this.add({ type: "titleHeader", title, ...o });
186
- }
187
- sourceNote(html, o = {}) {
188
- return this.add({ type: "sourceNote", html, ...o });
189
- }
190
- caption538(o = {}) {
191
- return this.add({ type: "caption538", ruleWidth: 1, size: 12, align: "right", ...o });
192
- }
193
- groupBy(key) {
194
- return this.add({ type: "groupBy", key });
195
- }
196
- groupStripes(o = {}) {
197
- return this.add({ type: "groupStripes", color: "#F5F5F5", start: 2, ...o });
198
- }
199
- rowAccent(key, o = {}) {
200
- return this.add({
201
- type: "rowAccent",
202
- key,
203
- width: 4,
204
- side: "left",
205
- hide: true,
206
- naColor: "transparent",
207
- ...o
208
- });
209
- }
210
- boldRows(rows, o = {}) {
211
- return this.add({ type: "boldRows", rows, textColor: "black", ...o });
212
- }
213
- spotlight(rows, o = {}) {
214
- return this.add({ type: "spotlight", rows, bold: true, accentWidth: 4, dimColor: "auto", ...o });
215
- }
216
- cutline(after, o = {}) {
217
- return this.add({
218
- type: "cutline",
219
- after: typeof after === "number" ? [after] : after,
220
- color: "#A6081A",
221
- weight: 2,
222
- style: "dashed",
223
- labelSize: 9,
224
- labelPosition: "below",
225
- gap: [0],
226
- ...o
227
- });
228
- }
229
- borderGrid(o = {}) {
230
- return this.add({ type: "borderGrid", color: "black", weight: 1, includeLabels: false, ...o });
231
- }
232
- borderBars(side, colors, o = {}) {
233
- return this.add({
234
- type: "borderBars",
235
- side,
236
- colors,
237
- barHeight: 10,
238
- barWidth: "100%",
239
- barAlign: "center",
240
- imgWidth: 30,
241
- imgHeight: 30,
242
- imgPadding: 10,
243
- imgAlign: "right",
244
- textWeight: "bold",
245
- textColor: "#FFFFFF",
246
- textSize: 18,
247
- textAlign: "left",
248
- textPadding: 10,
249
- ...o
250
- });
251
- }
252
- // location "bottom": Python gt_legend_continuous default (_layout.py:357)
253
- legendContinuous(o = {}) {
254
- return this.add({
255
- type: "legendContinuous",
256
- nBins: 5,
257
- digits: 0,
258
- titlePosition: "top",
259
- location: "bottom",
260
- swatchWidth: 40,
261
- swatchHeight: 12,
262
- ...o
263
- });
264
- }
265
- legendDiscrete(key, o = {}) {
266
- return this.add({
267
- type: "legendDiscrete",
268
- key,
269
- location: "top",
270
- shape: "square",
271
- swatchSize: 14,
272
- border: true,
273
- gap: 14,
274
- direction: "horizontal",
275
- align: "center",
276
- ...o
277
- });
278
- }
279
- // hideP: Python hide_p=True (_layout.py:1303)
280
- significance(pairs, o = {}) {
281
- return this.add({
282
- type: "significance",
283
- pairs,
284
- levels: [0.01, 0.05, 0.1],
285
- symbols: ["***", "**", "*"],
286
- superscript: true,
287
- note: true,
288
- hideP: true,
289
- ...o
290
- });
291
- }
292
- outliers(columns, o = {}) {
293
- return this.add({ type: "outliers", columns, method: "iqr", side: "both", bold: true, ...o });
294
- }
295
- scaleNote(columns, o = {}) {
296
- return this.add({ type: "scaleNote", columns, divisor: 1e3, where: "sourceNote", decimals: 0, ...o });
297
- }
298
- socialTag(accounts, o = {}) {
299
- return this.add({
300
- type: "socialTag",
301
- accounts,
302
- stack: false,
303
- separator: " | ",
304
- align: "right",
305
- iconHeight: "0.9em",
306
- ...o
307
- });
308
- }
309
- watermark(o = {}) {
310
- return this.add({
311
- type: "watermark",
312
- opacity: 0.06,
313
- size: "60%",
314
- position: "center",
315
- color: "#000000",
316
- angle: 0,
317
- font: "Helvetica, Arial, sans-serif",
318
- ...o
319
- });
320
- }
321
- wrapLabels(o = {}) {
322
- return this.add({ type: "wrapLabels", width: 12, balance: true, ...o });
323
- }
324
- marginalia(columns, o = {}) {
325
- return this.add({
326
- type: "marginalia",
327
- columns,
328
- width: 220,
329
- label: "",
330
- italic: true,
331
- size: "0.92em",
332
- rule: true,
333
- align: "left",
334
- ...o
335
- });
336
- }
337
- snake(o = {}) {
338
- return this.add({ type: "snake", nCols: 2, gap: 20, fill: "", cleanGaps: true, ...o });
339
- }
340
- tiers(levels, tierKey, imageColumns, o = {}) {
341
- return this.add({ type: "tiers", levels, tierKey, imageColumns, imgHeight: "55px", style: "dark", ...o });
342
- }
343
- font(family, o = {}) {
344
- return this.add({ type: "font", family, google: true, ...o });
345
- }
346
- build() {
347
- if (this.s.columns.length === 0)
348
- throw new TableSpecError("a table needs at least one column: call .columns(c => [...])");
349
- const seen = /* @__PURE__ */ new Set();
350
- for (const col of this.s.columns) {
351
- if (seen.has(col.key)) throw new TableSpecError(`column key "${col.key}" appears twice`);
352
- seen.add(col.key);
353
- }
354
- if (this.s.decorations.filter((d) => d.type === "title").length > 1)
355
- throw new TableSpecError("only one .title()");
356
- return this.s;
357
- }
358
- };
359
- function defineTable() {
360
- return new TableBuilder({
361
- columns: [],
362
- decorations: [],
363
- theme: { name: "sdv", density: "comfortable" }
364
- });
365
- }
366
-
367
- // src/predicate.ts
368
- var isNull = (v) => v === null || v === void 0 || typeof v === "number" && Number.isNaN(v);
369
- function matches(p, row) {
370
- const v = row[p.key];
371
- switch (p.op) {
372
- case "isNull":
373
- return isNull(v);
374
- case "notNull":
375
- return !isNull(v);
376
- case "==":
377
- return v === p.value;
378
- case "!=":
379
- return v !== p.value;
380
- case ">":
381
- return typeof v === "number" && v > p.value;
382
- case ">=":
383
- return typeof v === "number" && v >= p.value;
384
- case "<":
385
- return typeof v === "number" && v < p.value;
386
- case "<=":
387
- return typeof v === "number" && v <= p.value;
388
- case "in":
389
- return Array.isArray(p.value) && p.value.includes(v);
390
- case "notIn":
391
- return Array.isArray(p.value) && !p.value.includes(v);
392
- case "matches":
393
- return typeof v === "string" && new RegExp(String(p.value)).test(v);
394
- default: {
395
- const bad = p.op;
396
- throw new TableSpecError(`unknown predicate op ${String(bad)}`);
397
- }
398
- }
399
- }
400
- function selectRows(sel, rows, source = rows) {
401
- if (Array.isArray(sel)) {
402
- for (const i of sel)
403
- if (!Number.isInteger(i) || i < 0 || i >= source.length)
404
- throw new TableSpecError(`row ${i} is outside 0..${source.length - 1}`);
405
- if (source === rows) return [...sel];
406
- const want = new Set(sel.map((i) => source[i]));
407
- return rows.flatMap((r, i) => want.has(r) ? [i] : []);
408
- }
409
- const out = [];
410
- rows.forEach((r, i) => {
411
- if (matches(sel, r)) out.push(i);
412
- });
413
- return out;
414
- }
415
-
416
- // src/snake.ts
417
- function snakeAlign(rows, o = {}) {
418
- let blocks;
419
- let per;
420
- if (o.rowsPerCol !== void 0) {
421
- per = Math.trunc(o.rowsPerCol);
422
- if (!(per >= 1)) throw new TableSpecError("rowsPerCol must be at least 1");
423
- blocks = Math.ceil(rows.length / per);
424
- } else {
425
- blocks = Math.trunc(o.nCols ?? 2);
426
- if (!(blocks >= 1)) throw new TableSpecError("nCols must be at least 1");
427
- per = Math.ceil(rows.length / blocks);
428
- }
429
- const fill = o.fill ?? null;
430
- return Array.from(
431
- { length: blocks },
432
- (_, j) => Array.from({ length: per }, (_2, k) => rows[j * per + k] ?? fill)
433
- );
434
- }
435
-
436
- // src/format.ts
437
- var isBlank = (v) => v === null || v === void 0 || typeof v === "number" && Number.isNaN(v) || typeof v === "string" && v.trim() === "";
438
- function toNumber(v) {
439
- if (isBlank(v)) return null;
440
- if (typeof v === "string" && /^\s*[+-]?0[xXoObB]/.test(v)) return null;
441
- const n = typeof v === "number" ? v : typeof v === "boolean" ? Number(v) : typeof v === "string" ? Number(v) : Number.NaN;
442
- return Number.isNaN(n) ? null : n;
443
- }
444
- var group = (s) => s.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
445
- function scaledDigits(a, digits) {
446
- const dv = new DataView(new ArrayBuffer(8));
447
- dv.setFloat64(0, a);
448
- const bits = dv.getBigUint64(0);
449
- const biased = Number(bits >> 52n & 0x7ffn);
450
- const frac = bits & (1n << 52n) - 1n;
451
- const m = biased === 0 ? frac : frac | 1n << 52n;
452
- const e = (biased === 0 ? 1 : biased) - 1075;
453
- const num = m * 10n ** BigInt(digits);
454
- if (e >= 0) return (num << BigInt(e)).toString();
455
- const k = BigInt(-e);
456
- let q = num >> k;
457
- const r = num & (1n << k) - 1n;
458
- const half = 1n << k - 1n;
459
- if (r > half || r === half && (q & 1n) === 1n) q += 1n;
460
- return q.toString();
461
- }
462
- function fixed(v, digits, big) {
463
- if (!Number.isFinite(v)) return Number.isNaN(v) ? "nan" : v > 0 ? "inf" : "-inf";
464
- const s = scaledDigits(Math.abs(v), digits).padStart(digits + 1, "0");
465
- const i = s.slice(0, s.length - digits);
466
- const f = s.slice(s.length - digits);
467
- return (v < 0 || Object.is(v, -0) ? "-" : "") + (big ? group(i) : i) + (digits > 0 ? `.${f}` : "");
468
- }
469
- function naturalDigits(v, big = false) {
470
- if (!Number.isFinite(v)) return v > 0 ? "Inf" : "-Inf";
471
- const exp = Number(Math.abs(v).toExponential(6).split("e")[1]);
472
- const seven = fixed(Math.abs(v), Math.max(0, 6 - exp), false);
473
- const frac = seven.split(".")[1]?.replace(/0+$/, "") ?? "";
474
- return fixed(v, frac.length, big);
475
- }
476
- var minus = (s) => s.replace(/-/g, "\u2212");
477
- function formatValue(v, digits, formatType, suffix) {
478
- const big = formatType === "comma" || formatType === "currency";
479
- let core = v === null ? "NA" : digits === null || digits === void 0 ? naturalDigits(v, big) : fixed(v, digits, big);
480
- if (formatType === "currency") core = `$${core}`;
481
- else if (formatType === "percent") core += "%";
482
- return core + suffix;
483
- }
484
- function formatNumber(v, o = {}) {
485
- const body = o.digits === void 0 ? naturalDigits(v, o.big ?? false) : fixed(v, o.digits, o.big ?? false);
486
- const sign = o.forceSign && !body.startsWith("-") && Number(body.replace(/,/g, "")) !== 0 ? "+" : "";
487
- return `${o.prefix ?? ""}${sign}${minus(body)}${o.suffix ?? ""}`;
488
- }
489
- function ordinal(n) {
490
- const t = Math.abs(Math.trunc(n));
491
- const m100 = t % 100;
492
- const suf = m100 >= 11 && m100 <= 13 ? "th" : ["th", "st", "nd", "rd"][t % 10 <= 3 ? t % 10 : 0];
493
- return `${Math.trunc(n)}${suf}`;
494
- }
495
- var pxOf = (n) => `${Number(fixed(n, 1, false))}px`;
496
-
497
- // src/themes/tokens.ts
498
- var DENSITY = {
499
- // sdvplotR utils-theme.R .theme_density() = _marks.py DENSITY
500
- comfortable: { body: 14, pad: 6, title: 26, subtitle: 15, label: 10, group: 11, source: 11 },
501
- compact: { body: 12, pad: 3, title: 22, subtitle: 13, label: 9, group: 10, source: 10 },
502
- social: { body: 17, pad: 9, title: 34, subtitle: 19, label: 12, group: 13, source: 13 }
503
- };
504
- function densitySizes(d) {
505
- const s = DENSITY[d];
506
- if (!s) throw new TableSpecError(`density must be comfortable, compact or social, not ${String(d)}`);
507
- return s;
508
- }
509
- function scaleFor(d) {
510
- const b = DENSITY.comfortable;
511
- const s = densitySizes(d);
512
- return {
513
- body: s.body / b.body,
514
- pad: s.pad / b.pad,
515
- title: s.title / b.title,
516
- subtitle: s.subtitle / b.subtitle,
517
- label: s.label / b.label,
518
- group: s.group / b.group,
519
- source: s.source / b.source
520
- };
521
- }
522
- var px = pxOf;
523
- function hexOption(value, arg) {
524
- try {
525
- return hex6(value);
526
- } catch {
527
- throw new TableSpecError(
528
- `${arg} must be a hex color with no transparency, such as '#8C2F1E', not "${value}"`
529
- );
530
- }
531
- }
532
- var TOKEN_KEYS = [
533
- "fontBody",
534
- "fontLabel",
535
- "fontTitle",
536
- "bg",
537
- "text",
538
- "muted",
539
- "accent",
540
- "rule",
541
- "hline",
542
- "headingBg",
543
- "headingAlign",
544
- "titleColor",
545
- "titleWeight",
546
- "titleSize",
547
- "titleTransform",
548
- "titleTracking",
549
- "subtitleColor",
550
- "subtitleSize",
551
- "subtitleStyle",
552
- "subtitleWeight",
553
- "labelBg",
554
- "labelColor",
555
- "labelWeight",
556
- "labelSize",
557
- "labelTransform",
558
- "labelTracking",
559
- "labelStyle",
560
- "labelBorderTop",
561
- "labelBorderBottom",
562
- "groupBg",
563
- "groupColor",
564
- "groupWeight",
565
- "groupSize",
566
- "groupTransform",
567
- "groupBorderTop",
568
- "groupBorderBottom",
569
- "bodySize",
570
- "bodyWeight",
571
- "pad",
572
- "headingPad",
573
- "labelPad",
574
- "groupPad",
575
- "sourcePad",
576
- "lineHeight",
577
- "bodyBorderBottom",
578
- "tableBorderTop",
579
- "tableBorderBottom",
580
- "tableBorderX",
581
- "stripe",
582
- "sourceColor",
583
- "sourceSize",
584
- "sourceStyle",
585
- "horizon"
586
- ];
587
- function baseTokens(d, font) {
588
- return {
589
- fontBody: font,
590
- fontLabel: font,
591
- fontTitle: font,
592
- bg: "#FFFFFF",
593
- text: "#333333",
594
- muted: "#5A5A5A",
595
- accent: "#333333",
596
- rule: "#D3D3D3",
597
- hline: "none",
598
- headingBg: "transparent",
599
- headingAlign: "left",
600
- titleColor: "inherit",
601
- titleWeight: "700",
602
- titleSize: px(d.title),
603
- titleTransform: "none",
604
- titleTracking: "normal",
605
- subtitleColor: "inherit",
606
- subtitleSize: px(d.subtitle),
607
- subtitleStyle: "normal",
608
- subtitleWeight: "400",
609
- labelBg: "transparent",
610
- labelColor: "inherit",
611
- labelWeight: "700",
612
- labelSize: px(d.label),
613
- labelTransform: "none",
614
- labelTracking: "normal",
615
- labelStyle: "normal",
616
- labelBorderTop: "none",
617
- labelBorderBottom: "none",
618
- groupBg: "transparent",
619
- groupColor: "inherit",
620
- groupWeight: "700",
621
- groupSize: px(d.group),
622
- groupTransform: "none",
623
- groupBorderTop: "none",
624
- groupBorderBottom: "none",
625
- bodySize: px(d.body),
626
- bodyWeight: "400",
627
- pad: px(d.pad),
628
- headingPad: px(d.pad),
629
- labelPad: px(d.pad),
630
- groupPad: px(d.pad),
631
- sourcePad: px(d.pad),
632
- lineHeight: "normal",
633
- bodyBorderBottom: "none",
634
- tableBorderTop: "none",
635
- tableBorderBottom: "none",
636
- tableBorderX: "none",
637
- stripe: "transparent",
638
- sourceColor: "inherit",
639
- sourceSize: px(d.source),
640
- sourceStyle: "normal",
641
- horizon: "none"
642
- };
643
- }
644
- var SYSTEM_FONTS = "system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'";
645
- var fontStack = (family) => `'${family}', ${SYSTEM_FONTS}`;
646
- var border = (w, color, style = "solid") => `${px(w)} ${style} ${color}`;
647
- var SDV_NAVY = "#0B1A33";
648
- var SDV_CYAN = "#7FE6DC";
649
- var SDV_HORIZON = "linear-gradient(90deg, #3346F0, #7FE6DC)";
650
- var light = (horizon) => ({
651
- bg: "#FFFFFF",
652
- headingBg: "#FFFFFF",
653
- title: SDV_NAVY,
654
- muted: "#4A5A75",
655
- label: "#16305C",
656
- text: SDV_NAVY,
657
- rule: "#E3E8F1",
658
- groupBg: "#EEF3FA",
659
- horizon
660
- });
661
- var dark = () => ({
662
- bg: SDV_NAVY,
663
- headingBg: SDV_NAVY,
664
- title: "#FFFFFF",
665
- muted: "#A9B8D0",
666
- label: "#9CCBFF",
667
- text: "#EAEBEC",
668
- rule: "#1D3A66",
669
- groupBg: "#16305C",
670
- horizon: SDV_HORIZON
671
- });
672
- function secondaryOn(bg, fg, target = 4.5) {
673
- for (let i = 0; i < 12; i++) {
674
- const c = mix(bg, fg, 0.45 + i * 0.05);
675
- if (contrast(c, bg) >= target) return c;
676
- }
677
- return fg;
678
- }
679
- function build(name, ref, pal) {
680
- const d = densitySizes(ref.density);
681
- const k = scaleFor(ref.density);
682
- const sz = (n, role) => px(n * k[role]);
683
- const t = {
684
- ...baseTokens(d, fontStack("Lato")),
685
- fontLabel: fontStack("Chivo"),
686
- fontTitle: fontStack("Chivo"),
687
- bg: pal.bg,
688
- text: pal.text,
689
- muted: pal.muted,
690
- accent: pal.label,
691
- rule: pal.rule,
692
- hline: border(1, pal.rule),
693
- headingBg: pal.headingBg,
694
- titleColor: pal.title,
695
- titleWeight: "800",
696
- titleSize: sz(22, "title"),
697
- subtitleColor: pal.subtitle ?? pal.muted,
698
- subtitleSize: sz(14, "subtitle"),
699
- labelBg: pal.bg,
700
- labelColor: pal.label,
701
- labelWeight: "500",
702
- labelSize: sz(13, "label"),
703
- groupBg: pal.groupBg,
704
- groupColor: pal.label,
705
- groupWeight: "500",
706
- groupSize: sz(13, "group"),
707
- bodySize: sz(15, "body"),
708
- pad: sz(7, "pad"),
709
- headingPad: sz(4, "pad"),
710
- labelPad: sz(6, "pad"),
711
- groupPad: sz(8, "pad"),
712
- sourcePad: sz(4, "pad"),
713
- sourceColor: pal.muted,
714
- sourceSize: sz(12, "source"),
715
- horizon: pal.horizon
716
- };
717
- return {
718
- name,
719
- tokens: t,
720
- fonts: [
721
- { family: "Chivo", weights: [500, 800] },
722
- { family: "Lato", weights: [400, 700] }
723
- ],
724
- rules: (s) => [
725
- `${s} th.sdvt-label{padding-bottom:10px}`,
726
- `${s} caption,${s} tfoot td,${s} th.sdvt-label:first-child,${s} td.sdvt-cell:first-child{padding-left:14px}`,
727
- `${s} caption,${s} tfoot td,${s} th.sdvt-label:last-child,${s} td.sdvt-cell:last-child{padding-right:14px}`,
728
- `${s} .sdvt-title{padding-top:12px;display:block}${s} .sdvt-subtitle{padding-bottom:12px;display:block}`,
729
- `${s} tbody tr:last-child td{border-bottom:2px solid ${pal.bg}}`
730
- ].join("\n")
731
- };
732
- }
733
- var sdv = (ref) => {
734
- const style = ref.options?.style ?? "light";
735
- if (style !== "light" && style !== "dark")
736
- throw new TableSpecError(`sdv theme style must be "light" or "dark", not "${style}"`);
737
- return build("sdv", ref, style === "dark" ? dark() : light(SDV_HORIZON));
738
- };
739
- var sdvTeam = (ref) => {
740
- const league = ref.options?.league;
741
- if (!league) throw new TableSpecError('sdvTeam needs options.league (e.g. { league: "nfl", team: "KC" })');
742
- let primary = SDV_NAVY;
743
- let secondary = SDV_CYAN;
744
- const team = ref.options?.team;
745
- if (team) {
746
- const id = resolveSync(team, league, { strict: true });
747
- if (!id) throw new UnresolvedTeamError(`${team} is not a ${league} team`);
748
- const p = teamColorsSync(league, id);
749
- const s = teamColorsSync(league, id, { which: "secondary" });
750
- if (p) {
751
- primary = p;
752
- secondary = s ?? p;
753
- } else
754
- warn(
755
- `sdvtables:sdvTeam:${league}:${team}`,
756
- `no colors on file for ${league} team "${team}"; using the SportsDataverse colors`
757
- );
758
- }
759
- const title = onColor(primary);
760
- const pal = light(contrast(secondary, "#ffffff") >= 1.5 ? secondary : primary);
761
- return build("sdvTeam", ref, {
762
- ...pal,
763
- headingBg: primary,
764
- title,
765
- subtitle: secondaryOn(primary, title),
766
- label: contrast(primary, "#ffffff") >= 3 ? primary : SDV_NAVY
767
- });
768
- };
769
-
770
- // src/themes/almanac.ts
771
- var almanac = (ref) => {
772
- const d = densitySizes(ref.density);
773
- const accent = hexOption(ref.options?.accent ?? "#8C2F1E", "accent");
774
- const stripeOpt = ref.options?.stripe ?? "#F1F1EF";
775
- const stripe = stripeOpt === "none" ? "transparent" : hexOption(stripeOpt, "stripe");
776
- const ink = "#1A1A1A";
777
- const secondary = "#6B6B68";
778
- const rule = "#D8D8D4";
779
- const bg = "#FFFFFF";
780
- const narrow = fontStack("Archivo Narrow");
781
- return {
782
- name: "almanac",
783
- tokens: {
784
- ...baseTokens(d, fontStack("Zilla Slab")),
785
- fontLabel: narrow,
786
- bg,
787
- text: ink,
788
- muted: secondary,
789
- accent,
790
- rule,
791
- stripe,
792
- headingBg: bg,
793
- titleColor: ink,
794
- titleSize: px(d.title),
795
- subtitleColor: secondary,
796
- subtitleWeight: "400",
797
- subtitleSize: px(d.subtitle),
798
- labelBg: bg,
799
- labelColor: ink,
800
- labelWeight: "700",
801
- labelSize: px(d.label + 1),
802
- labelTransform: "uppercase",
803
- labelTracking: "0.05em",
804
- labelBorderBottom: border(1.5, ink),
805
- labelPad: px(d.pad + 1),
806
- groupBg: bg,
807
- groupColor: accent,
808
- groupWeight: "700",
809
- groupSize: px(d.group + 1),
810
- groupTransform: "uppercase",
811
- groupBorderTop: border(1, rule),
812
- groupPad: px(Math.max(d.pad, 3)),
813
- bodySize: px(d.body),
814
- pad: px(d.pad),
815
- headingPad: px(d.pad + 2),
816
- sourcePad: px(d.pad + 2),
817
- bodyBorderBottom: border(1, ink),
818
- tableBorderTop: border(2, accent),
819
- sourceColor: secondary,
820
- sourceSize: px(d.source + 1)
821
- },
822
- fonts: [
823
- { family: "Zilla Slab", weights: [400, 700] },
824
- { family: "Archivo Narrow", weights: [700] }
825
- ],
826
- rules: (s) => [
827
- `${s} th.sdvt-group{letter-spacing:0.05em}`,
828
- `${s} tfoot td{font-family:${narrow}}`,
829
- `${s} .sdvt-subtitle{display:block;padding-bottom:${px(d.pad + 6)}}${s} .sdvt-title{display:block;padding-bottom:${px(Math.ceil(d.pad / 2))}}`,
830
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
831
- ].join("\n")
832
- };
833
- };
834
-
835
- // src/themes/athletic.ts
836
- var athletic = (ref) => {
837
- const d = densitySizes(ref.density);
838
- const k = scaleFor(ref.density);
839
- const sz = (n, role) => px(n * k[role]);
840
- const bg = "#FFFFFF";
841
- const work = fontStack("Work Sans");
842
- const hline = border(1.5, "black", "dotted");
843
- return {
844
- name: "athletic",
845
- tokens: {
846
- ...baseTokens(d, fontStack("Spline Sans Mono")),
847
- fontLabel: work,
848
- fontTitle: work,
849
- bg,
850
- hline,
851
- titleWeight: "650",
852
- titleSize: sz(22, "title"),
853
- subtitleWeight: "500",
854
- subtitleSize: sz(14, "subtitle"),
855
- labelBg: bg,
856
- labelWeight: "650",
857
- labelSize: sz(12, "label"),
858
- labelTransform: "uppercase",
859
- labelBorderBottom: border(1, "black"),
860
- labelPad: sz(5, "pad"),
861
- groupBg: "black",
862
- groupColor: "white",
863
- groupWeight: "650",
864
- groupSize: sz(12, "group"),
865
- groupBorderBottom: border(1, "black"),
866
- groupPad: sz(1.5, "pad"),
867
- bodySize: sz(12, "body"),
868
- bodyWeight: "500",
869
- pad: sz(8, "pad"),
870
- headingPad: sz(4, "pad"),
871
- sourcePad: sz(4, "pad"),
872
- bodyBorderBottom: border(2, "#D3D3D3"),
873
- sourceSize: sz(10, "source")
874
- },
875
- fonts: [
876
- { family: "Spline Sans Mono", weights: [500] },
877
- { family: "Work Sans", weights: [500, 650] }
878
- ],
879
- rules: (s) => [
880
- `${s} td.sdvt-cell:not(:first-child){border-left:0.5px solid black}`,
881
- `${s} tbody tr:first-child td.sdvt-cell{border-top:${hline}}`,
882
- // _themes.py:1371: the dotted top rule is on EVERY body cell, the first row too
883
- `${s} td.sdvt-cell,${s} th.sdvt-label{text-align:center}`,
884
- `${s} caption{padding-top:6px;padding-bottom:0}`,
885
- `${s} .sdvt-subtitle{display:block;padding:0 0 4px}`,
886
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
887
- ].join("\n")
888
- };
889
- };
890
-
891
- // src/themes/booktabs.ts
892
- var booktabs = (ref) => {
893
- const d = densitySizes(ref.density);
894
- const accent = hexOption(ref.options?.accent ?? "#111111", "accent");
895
- const ink = "#111111";
896
- const secondary = "#5A5A5A";
897
- const bg = "#FFFFFF";
898
- return {
899
- name: "booktabs",
900
- tokens: {
901
- ...baseTokens(d, fontStack("Tinos")),
902
- bg,
903
- text: ink,
904
- muted: secondary,
905
- accent,
906
- titleColor: ink,
907
- titleSize: px(d.title),
908
- subtitleColor: secondary,
909
- subtitleStyle: "italic",
910
- subtitleSize: px(d.subtitle),
911
- labelBg: bg,
912
- labelColor: ink,
913
- labelWeight: "700",
914
- labelSize: px(d.label + 1),
915
- labelBorderTop: border(2, accent),
916
- labelBorderBottom: border(1, accent),
917
- labelPad: px(Math.max(d.pad - 1, 2)),
918
- groupBg: bg,
919
- groupColor: accent,
920
- groupWeight: "700",
921
- groupSize: px(d.group + 1),
922
- groupBorderTop: border(1, accent),
923
- groupPad: px(Math.max(d.pad - 2, 2)),
924
- bodySize: px(d.body),
925
- pad: px(d.pad),
926
- bodyBorderBottom: border(2, accent),
927
- sourceColor: secondary,
928
- sourceSize: px(d.source)
929
- },
930
- fonts: [{ family: "Tinos", weights: [400, 700], italic: true }],
931
- rules: (s) => [
932
- `${s} th.sdvt-group{font-style:italic}`,
933
- `${s} tfoot td{padding-top:${px(d.pad + 4)}}`,
934
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
935
- ].join("\n")
936
- };
937
- };
938
-
939
- // src/themes/broadsheet.ts
940
- var PAPER = {
941
- white: ["#FBFAF7", "#DEDAD2"],
942
- salmon: ["#FFF1E5", "#EAD9C7"]
943
- };
944
- var broadsheet = (ref) => {
945
- const d = densitySizes(ref.density);
946
- const accent = hexOption(ref.options?.accent ?? "#A6081A", "accent");
947
- const paper = ref.options?.paper ?? "white";
948
- const [bg, rule] = PAPER[paper] ?? [hexOption(paper, "paper"), "#DEDAD2"];
949
- const ink = "#16130F";
950
- const secondary = "#5C574F";
951
- const sans = fontStack("Public Sans");
952
- return {
953
- name: "broadsheet",
954
- tokens: {
955
- ...baseTokens(d, fontStack("Source Serif 4")),
956
- fontTitle: fontStack("Newsreader"),
957
- fontLabel: sans,
958
- bg,
959
- text: ink,
960
- muted: secondary,
961
- accent,
962
- rule,
963
- hline: border(1, rule),
964
- headingBg: bg,
965
- titleColor: ink,
966
- titleWeight: "600",
967
- titleSize: px(d.title),
968
- subtitleColor: secondary,
969
- subtitleSize: px(d.subtitle),
970
- subtitleStyle: "italic",
971
- labelBg: bg,
972
- labelColor: secondary,
973
- labelWeight: "600",
974
- labelSize: px(d.label),
975
- labelTransform: "uppercase",
976
- labelTracking: "0.09em",
977
- labelBorderBottom: border(1.5, ink),
978
- labelPad: px(Math.max(d.pad - 2, 2)),
979
- groupBg: bg,
980
- groupColor: accent,
981
- groupWeight: "700",
982
- groupSize: px(d.group),
983
- groupTransform: "uppercase",
984
- groupBorderTop: border(1, ink),
985
- groupPad: px(Math.max(d.pad - 3, 2)),
986
- bodySize: px(d.body),
987
- pad: px(d.pad),
988
- bodyBorderBottom: border(1, ink),
989
- tableBorderTop: border(2, accent),
990
- sourceColor: secondary,
991
- sourceSize: px(d.source)
992
- },
993
- fonts: [
994
- { family: "Source Serif 4", weights: [400, 600] },
995
- { family: "Newsreader", weights: [400, 600], italic: true },
996
- { family: "Public Sans", weights: [600, 700] }
997
- ],
998
- rules: (s) => [
999
- `${s} th.sdvt-group{letter-spacing:0.08em}`,
1000
- `${s} tr.sdvt-group-row + tr td.sdvt-cell{padding-top:${px(Math.max(d.pad - 2, 2))}}`,
1001
- `${s} .sdvt-subtitle{display:block;font-family:var(--sdvt-font-title);padding-bottom:${px(d.pad + 8)}}${s} .sdvt-title{display:block;padding-bottom:${px(Math.ceil(d.pad / 2))}}`,
1002
- `${s} tfoot td{font-family:${sans};padding-top:${px(d.pad + 4)}}`,
1003
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
1004
- ].join("\n")
1005
- };
1006
- };
1007
-
1008
- // src/themes/brutalist.ts
1009
- var brutalist = (ref) => {
1010
- const d = densitySizes(ref.density);
1011
- const accent = hexOption(ref.options?.accent ?? "#FF3B00", "accent");
1012
- const ink = "#000000";
1013
- const bg = "#FFFFFF";
1014
- const frame = border(3, ink);
1015
- return {
1016
- name: "brutalist",
1017
- tokens: {
1018
- ...baseTokens(d, fontStack("Archivo")),
1019
- fontTitle: fontStack("Archivo Black"),
1020
- bg,
1021
- text: ink,
1022
- muted: ink,
1023
- accent,
1024
- rule: ink,
1025
- hline: border(1, ink),
1026
- headingBg: bg,
1027
- titleColor: ink,
1028
- titleWeight: "400",
1029
- // Archivo Black carries its own weight; Python sets none
1030
- titleSize: px(d.title + 6),
1031
- titleTransform: "uppercase",
1032
- titleTracking: "-0.02em",
1033
- subtitleColor: ink,
1034
- subtitleWeight: "600",
1035
- subtitleSize: px(d.subtitle),
1036
- labelBg: ink,
1037
- labelColor: "#FFFFFF",
1038
- labelWeight: "700",
1039
- labelSize: px(d.label + 1),
1040
- labelTransform: "uppercase",
1041
- labelTracking: "0.04em",
1042
- labelPad: px(d.pad),
1043
- groupBg: bg,
1044
- groupColor: accent,
1045
- groupWeight: "700",
1046
- groupSize: px(d.group + 1),
1047
- groupTransform: "uppercase",
1048
- groupBorderTop: border(2, ink),
1049
- groupPad: px(Math.max(d.pad - 1, 3)),
1050
- bodySize: px(d.body),
1051
- bodyWeight: "500",
1052
- pad: px(d.pad),
1053
- headingPad: px(d.pad + 2),
1054
- sourcePad: px(d.pad + 2),
1055
- tableBorderTop: frame,
1056
- tableBorderBottom: frame,
1057
- tableBorderX: frame,
1058
- sourceColor: ink,
1059
- sourceSize: px(d.source)
1060
- },
1061
- fonts: [
1062
- { family: "Archivo", weights: [500, 600, 700] },
1063
- { family: "Archivo Black", weights: [400] }
1064
- ],
1065
- rules: (s) => [
1066
- `${s} caption{border-bottom:3px solid ${ink}}`,
1067
- `${s} th.sdvt-group{letter-spacing:0.06em}`,
1068
- `${s} tfoot td{font-weight:500}`,
1069
- `${s} .sdvt-subtitle{display:block;padding-bottom:${px(d.pad + 2)}}${s} .sdvt-title{display:block;line-height:1.05;padding-bottom:${px(Math.ceil(d.pad / 2))}}`,
1070
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
1071
- ].join("\n")
1072
- };
1073
- };
1074
- var c2to3 = (x, y) => [x / y, 1, (1 - x - y) / y];
1075
- var P = [c2to3(0.64, 0.33), c2to3(0.3, 0.6), c2to3(0.15, 0.06)];
1076
- var WHITE = c2to3(0.3137, 0.3291);
1077
- var at = (m, i, j) => m[i]?.[j] ?? 0;
1078
- var DET = at(P, 0, 0) * (at(P, 1, 1) * at(P, 2, 2) - at(P, 1, 2) * at(P, 2, 1)) - at(P, 0, 1) * (at(P, 1, 0) * at(P, 2, 2) - at(P, 1, 2) * at(P, 2, 0)) + at(P, 0, 2) * (at(P, 1, 0) * at(P, 2, 1) - at(P, 1, 1) * at(P, 2, 0));
1079
- var INV = [0, 1, 2].map(
1080
- (i) => [0, 1, 2].map(
1081
- (j) => (at(P, (j + 1) % 3, (i + 1) % 3) * at(P, (j + 2) % 3, (i + 2) % 3) - at(P, (j + 1) % 3, (i + 2) % 3) * at(P, (j + 2) % 3, (i + 1) % 3)) / DET
1082
- )
1083
- );
1084
- var S = [0, 1, 2].map((j) => [0, 1, 2].reduce((acc, k) => acc + (WHITE[k] ?? 0) * at(INV, k, j), 0));
1085
- var M = [0, 1, 2].map((r) => [0, 1, 2].map((j) => (S[r] ?? 0) * at(P, r, j)));
1086
- function adjustLuminance(color, steps) {
1087
- const c = hex6(color);
1088
- const lin = [1, 3, 5].map((i) => Number.parseInt(c.slice(i, i + 2), 16) / 255).map((v2) => v2 < 0.04045 ? v2 / 12.92 : ((v2 + 0.055) / 1.055) ** 2.4);
1089
- const [x, y, z] = [0, 1, 2].map(
1090
- (j) => [0, 1, 2].reduce((acc, k) => acc + (lin[k] ?? 0) * at(M, k, j), 0)
1091
- );
1092
- const [wx, wy, wz] = WHITE;
1093
- const denom = x + 15 * y + 3 * z;
1094
- const wdenom = wx + 15 * wy + 3 * wz;
1095
- const [u1, v1] = denom ? [4 * x / denom, 9 * y / denom] : [1, 1];
1096
- const yr = y / wy;
1097
- let lum = yr <= 216 / 24389 ? 24389 / 27 * yr : 116 * yr ** (1 / 3) - 16;
1098
- const u = 13 * lum * (u1 - 4 * wx / wdenom);
1099
- const v = 13 * lum * (v1 - 9 * wy / wdenom);
1100
- const hue = Math.atan2(v, u);
1101
- const chroma = Math.hypot(u, v);
1102
- const frac = lum / 100;
1103
- if (frac <= 0) return "#000000";
1104
- lum = frac >= 1 ? 100 : 100 / (1 + Math.exp(-(Math.log(frac / (1 - frac)) + steps)));
1105
- const yy = 100 * (lum > 7.999592 ? ((lum + 16) / 116) ** 3 : lum / 903.3);
1106
- const uu = chroma * Math.cos(hue) / (13 * lum) + 0.1978398;
1107
- const vv = chroma * Math.sin(hue) / (13 * lum) + 0.4683363;
1108
- const xx = 9 * yy * uu / (4 * vv);
1109
- const zz = -xx / 3 - 5 * yy + 3 * yy / vv;
1110
- const gamma = (t) => t > 304e-5 ? 1.055 * t ** (1 / 2.4) - 0.055 : 12.92 * t;
1111
- const out = [
1112
- gamma((3.240479 * xx - 1.53715 * yy - 0.498535 * zz) / 100),
1113
- gamma((-0.969256 * xx + 1.875992 * yy + 0.041556 * zz) / 100),
1114
- gamma((0.055648 * xx - 0.204043 * yy + 1.057311 * zz) / 100)
1115
- ];
1116
- return `#${out.map(
1117
- (t) => Math.min(255, Math.max(0, Math.trunc(255 * t + 0.5))).toString(16).padStart(2, "0")
1118
- ).join("")}`;
1119
- }
1120
-
1121
- // src/themes/drench.ts
1122
- var drench = (ref) => {
1123
- const d = densitySizes(ref.density);
1124
- const color = hexOption(ref.options?.color ?? "#123F5E", "color");
1125
- const ink = onColor(color);
1126
- const darkType = ink === "#000000";
1127
- const rule = adjustLuminance(color, darkType ? -0.6 : 0.9);
1128
- const surface = adjustLuminance(color, darkType ? 0.5 : -0.7);
1129
- const secondary = secondaryOn(color, ink, 4.5);
1130
- return {
1131
- name: "drench",
1132
- tokens: {
1133
- ...baseTokens(d, fontStack("Gabarito")),
1134
- bg: color,
1135
- text: ink,
1136
- muted: secondary,
1137
- accent: ink,
1138
- rule,
1139
- hline: border(1, rule),
1140
- headingBg: color,
1141
- titleColor: ink,
1142
- titleSize: px(d.title + 2),
1143
- subtitleColor: secondary,
1144
- subtitleWeight: "400",
1145
- subtitleSize: px(d.subtitle),
1146
- labelBg: color,
1147
- labelColor: secondary,
1148
- labelWeight: "700",
1149
- labelSize: px(d.label),
1150
- labelTransform: "uppercase",
1151
- labelTracking: "0.08em",
1152
- labelBorderBottom: border(1, rule),
1153
- labelPad: px(d.pad + 1),
1154
- groupBg: surface,
1155
- groupColor: ink,
1156
- groupWeight: "700",
1157
- groupSize: px(d.group),
1158
- groupTransform: "uppercase",
1159
- groupPad: px(Math.max(d.pad - 1, 3)),
1160
- bodySize: px(d.body),
1161
- bodyWeight: "500",
1162
- pad: px(d.pad + 1),
1163
- headingPad: px(d.pad + 2),
1164
- sourcePad: px(d.pad + 2),
1165
- lineHeight: darkType ? "normal" : "1.55",
1166
- // light type on a saturated ground reads lighter than it is
1167
- sourceColor: secondary,
1168
- sourceSize: px(d.source)
1169
- },
1170
- fonts: [{ family: "Gabarito", weights: [400, 500, 700] }],
1171
- rules: (s) => [
1172
- `${s} th.sdvt-group{letter-spacing:0.06em}`,
1173
- `${s} .sdvt-subtitle{display:block;padding-bottom:${px(d.pad + 8)}}${s} .sdvt-title{display:block;padding-bottom:${px(Math.ceil(d.pad / 2))}}`,
1174
- `${s} tbody tr:last-child td{border-bottom:2px solid ${color}}`
1175
- ].join("\n")
1176
- };
1177
- };
1178
-
1179
- // src/themes/gtutils.ts
1180
- var gtutils = (ref) => {
1181
- const d = densitySizes(ref.density);
1182
- const k = scaleFor(ref.density);
1183
- const sz = (n, role) => px(n * k[role]);
1184
- const bg = "#FFFDF5";
1185
- const rule = "#8A817C";
1186
- const signika = fontStack("Signika Negative");
1187
- return {
1188
- name: "gtutils",
1189
- tokens: {
1190
- ...baseTokens(d, fontStack("Almarai")),
1191
- fontLabel: signika,
1192
- fontTitle: signika,
1193
- bg,
1194
- rule,
1195
- hline: border(1, rule),
1196
- titleWeight: "650",
1197
- titleSize: sz(20, "body"),
1198
- subtitleWeight: "500",
1199
- subtitleSize: sz(13.6, "body"),
1200
- labelBg: bg,
1201
- labelWeight: "650",
1202
- labelSize: sz(14, "label"),
1203
- labelBorderBottom: border(1, "black"),
1204
- labelPad: "2px",
1205
- groupBg: rule,
1206
- groupColor: bg,
1207
- groupWeight: "650",
1208
- groupSize: sz(14, "group"),
1209
- groupBorderBottom: border(1, "black"),
1210
- groupPad: sz(1.5, "pad"),
1211
- bodySize: sz(16, "body"),
1212
- bodyWeight: "500",
1213
- pad: sz(1, "pad"),
1214
- headingPad: sz(4, "pad"),
1215
- sourcePad: sz(4, "pad"),
1216
- bodyBorderBottom: border(2, "#D3D3D3"),
1217
- sourceSize: sz(12, "source")
1218
- },
1219
- fonts: [
1220
- { family: "Almarai", weights: [400, 500, 700] },
1221
- { family: "Signika Negative", weights: [500, 650] }
1222
- ],
1223
- rules: (s) => [
1224
- `${s} td.sdvt-cell,${s} th.sdvt-label{text-align:center}`,
1225
- `${s} caption{padding-top:6px;padding-bottom:0}`,
1226
- `${s} .sdvt-subtitle{display:block;padding:0 0 4px}`,
1227
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
1228
- ].join("\n")
1229
- };
1230
- };
1231
-
1232
- // src/themes/kenpom.ts
1233
- var kenpom = (ref) => {
1234
- const d = densitySizes(ref.density);
1235
- const k = scaleFor(ref.density);
1236
- const sz = (n, role) => px(n * k[role]);
1237
- const bg = "#FFFFFF";
1238
- const band = "#c3d9ff";
1239
- const bandInk = "#02b";
1240
- return {
1241
- name: "kenpom",
1242
- tokens: {
1243
- ...baseTokens(d, fontStack("Helvetica Neue")),
1244
- bg,
1245
- hline: border(1, "#000000"),
1246
- headingAlign: "center",
1247
- titleWeight: "650",
1248
- titleSize: sz(18, "title"),
1249
- subtitleWeight: "500",
1250
- subtitleSize: sz(14, "subtitle"),
1251
- labelBg: band,
1252
- labelColor: bandInk,
1253
- labelWeight: "650",
1254
- labelSize: sz(14, "label"),
1255
- labelPad: "2px",
1256
- groupBg: band,
1257
- groupColor: bandInk,
1258
- groupWeight: "bold",
1259
- groupSize: sz(14, "group"),
1260
- groupBorderBottom: border(1, "black"),
1261
- groupPad: sz(1.5, "pad"),
1262
- bodySize: sz(16, "body"),
1263
- bodyWeight: "500",
1264
- pad: sz(2, "pad"),
1265
- headingPad: sz(4, "pad"),
1266
- sourcePad: sz(4, "pad"),
1267
- bodyBorderBottom: border(2, "white"),
1268
- sourceSize: sz(12, "source")
1269
- },
1270
- fonts: [],
1271
- rules: (s) => [
1272
- // R's odd and even rows (1-based), counted over data rows only (group rows excluded)
1273
- `${s} tbody tr.sdvt-row:nth-child(odd of :not(.sdvt-group-row)) td.sdvt-cell{background-color:#F2FAFD}`,
1274
- `${s} tbody tr.sdvt-row:nth-child(even of :not(.sdvt-group-row)) td.sdvt-cell{background-color:#e5ecf9}`,
1275
- `${s} caption{padding-top:6px;padding-bottom:0}`,
1276
- `${s} .sdvt-title,${s} .sdvt-subtitle{display:block;text-align:left}`,
1277
- // the title and subtitle styles align left inside a centered heading
1278
- `${s} .sdvt-subtitle{padding:0 0 4px}`,
1279
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
1280
- ].join("\n")
1281
- };
1282
- };
1283
-
1284
- // src/themes/midnight.ts
1285
- var midnight = (ref) => {
1286
- const d = densitySizes(ref.density);
1287
- const accent = hexOption(ref.options?.accent ?? "#5B8DEF", "accent");
1288
- const ground = "#0C0D10";
1289
- const surface = "#16181D";
1290
- const primary = "#E8E9ED";
1291
- const secondary = "#9498A3";
1292
- const rule = "#24272E";
1293
- return {
1294
- name: "midnight",
1295
- tokens: {
1296
- ...baseTokens(d, fontStack("Chivo")),
1297
- bg: ground,
1298
- text: primary,
1299
- muted: secondary,
1300
- accent,
1301
- rule,
1302
- hline: border(1, rule),
1303
- headingBg: ground,
1304
- titleColor: primary,
1305
- titleWeight: "700",
1306
- titleSize: px(d.title),
1307
- subtitleColor: secondary,
1308
- subtitleWeight: "400",
1309
- subtitleSize: px(d.subtitle),
1310
- labelBg: surface,
1311
- labelColor: secondary,
1312
- labelWeight: "600",
1313
- labelSize: px(d.label + 1),
1314
- labelBorderBottom: border(1, rule),
1315
- labelPad: px(Math.max(d.pad - 1, 3)),
1316
- groupBg: ground,
1317
- groupColor: accent,
1318
- groupWeight: "700",
1319
- groupSize: px(d.group),
1320
- groupBorderTop: border(1, rule),
1321
- groupPad: px(Math.max(d.pad - 2, 2)),
1322
- bodySize: px(d.body),
1323
- pad: px(d.pad),
1324
- lineHeight: "1.55",
1325
- // light on dark reads lighter than it is
1326
- bodyBorderBottom: border(1, rule),
1327
- tableBorderTop: border(2, accent),
1328
- sourceColor: secondary,
1329
- sourceSize: px(d.source)
1330
- },
1331
- fonts: [{ family: "Chivo", weights: [400, 600, 700] }],
1332
- rules: (s) => [
1333
- `${s} .sdvt-subtitle{display:block;padding-bottom:${px(d.pad + 8)}}${s} .sdvt-title{display:block;padding-bottom:${px(Math.ceil(d.pad / 2))}}`,
1334
- `${s} tbody tr:last-child td{border-bottom:2px solid ${ground}}`
1335
- ].join("\n")
1336
- };
1337
- };
1338
-
1339
- // src/themes/ncaa.ts
1340
- var ncaa = (ref) => {
1341
- const d = densitySizes(ref.density);
1342
- const k = scaleFor(ref.density);
1343
- const sz = (n, role) => px(n * k[role]);
1344
- const bg = "#FFFFFF";
1345
- const almarai = fontStack("Almarai");
1346
- return {
1347
- name: "ncaa",
1348
- tokens: {
1349
- ...baseTokens(d, fontStack("Open Sans")),
1350
- bg,
1351
- stripe: "#F4F4F4",
1352
- // opt_row_striping() default in great_tables
1353
- hline: border(1, "transparent"),
1354
- titleSize: sz(18, "title"),
1355
- subtitleWeight: "400",
1356
- subtitleSize: sz(14, "subtitle"),
1357
- labelBg: "#000000",
1358
- labelColor: "white",
1359
- labelWeight: "400",
1360
- // great_tables' column-label default; Python sets none
1361
- labelSize: sz(14, "label"),
1362
- labelTransform: "uppercase",
1363
- labelBorderTop: border(1, "black"),
1364
- labelPad: sz(5, "pad"),
1365
- groupBg: "#3C3A40",
1366
- groupColor: "#ffffff",
1367
- groupWeight: "650",
1368
- groupSize: sz(14, "group"),
1369
- groupBorderBottom: border(1, "black"),
1370
- groupPad: sz(1.5, "pad"),
1371
- bodySize: sz(14, "body"),
1372
- pad: sz(2, "pad"),
1373
- headingPad: sz(4, "pad"),
1374
- sourcePad: sz(4, "pad"),
1375
- bodyBorderBottom: border(2, "white"),
1376
- sourceSize: sz(12, "source")
1377
- },
1378
- fonts: [
1379
- { family: "Open Sans", weights: [400, 650, 700] },
1380
- { family: "Almarai", weights: [400] }
1381
- ],
1382
- rules: (s) => [
1383
- `${s} th.sdvt-label,${s} td.sdvt-cell{padding:5px 5px 5px 25px;text-align:left}`,
1384
- `${s} tfoot td{font-family:${almarai}}`,
1385
- `${s} caption{padding-top:6px;padding-bottom:0}`,
1386
- `${s} .sdvt-subtitle{display:block;padding:0 0 4px}`,
1387
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
1388
- ].join("\n")
1389
- };
1390
- };
1391
-
1392
- // src/themes/pl.ts
1393
- var pl = (ref) => {
1394
- const d = densitySizes(ref.density);
1395
- const k = scaleFor(ref.density);
1396
- const sz = (n, role) => px(n * k[role]);
1397
- const bg = "#FFFFFF";
1398
- const purple = "#37003c";
1399
- const dim = "#87668a";
1400
- return {
1401
- name: "pl",
1402
- tokens: {
1403
- ...baseTokens(d, fontStack("DM Sans")),
1404
- bg,
1405
- text: purple,
1406
- muted: dim,
1407
- rule: purple,
1408
- hline: border(1, purple),
1409
- titleColor: "#333333",
1410
- titleWeight: "650",
1411
- titleSize: sz(20, "body"),
1412
- subtitleColor: "#333333",
1413
- subtitleWeight: "500",
1414
- subtitleSize: sz(13.6, "body"),
1415
- labelBg: bg,
1416
- labelColor: dim,
1417
- labelWeight: "650",
1418
- labelSize: sz(13, "label"),
1419
- labelBorderBottom: border(1, purple),
1420
- labelPad: sz(5, "pad"),
1421
- groupBg: "#C0BACA",
1422
- groupColor: "#ffffff",
1423
- groupWeight: "650",
1424
- groupSize: sz(12, "group"),
1425
- groupBorderBottom: border(1, purple),
1426
- groupPad: sz(1.5, "pad"),
1427
- bodySize: sz(14, "body"),
1428
- pad: sz(2, "pad"),
1429
- headingPad: sz(4, "pad"),
1430
- sourcePad: sz(4, "pad"),
1431
- bodyBorderBottom: border(2, "white"),
1432
- sourceColor: "#333333",
1433
- sourceSize: sz(12, "source")
1434
- },
1435
- fonts: [{ family: "DM Sans", weights: [500, 650] }],
1436
- rules: (s) => [
1437
- `${s} tbody{border-top:1px solid ${purple}}`,
1438
- `${s} th.sdvt-label{padding-bottom:3px}`,
1439
- `${s} caption{padding-top:6px;padding-bottom:0}`,
1440
- `${s} .sdvt-subtitle{display:block;padding:2px 0 6px}`,
1441
- `${s} tfoot td{line-height:1.2}`,
1442
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
1443
- ].join("\n")
1444
- };
1445
- };
1446
-
1447
- // src/themes/savant.ts
1448
- var savant = (ref) => {
1449
- const d = densitySizes(ref.density);
1450
- const k = scaleFor(ref.density);
1451
- const sz = (n, role) => px(n * k[role]);
1452
- const bg = "#FFFFFF";
1453
- return {
1454
- name: "savant",
1455
- tokens: {
1456
- ...baseTokens(d, fontStack("Roboto Condensed")),
1457
- bg,
1458
- stripe: "#F4F4F4",
1459
- // opt_row_striping() default in great_tables
1460
- hline: border(1, "transparent"),
1461
- headingAlign: "center",
1462
- titleSize: sz(18, "title"),
1463
- subtitleWeight: "400",
1464
- subtitleSize: sz(14, "subtitle"),
1465
- labelBg: bg,
1466
- labelWeight: "700",
1467
- labelSize: sz(14, "label"),
1468
- labelBorderTop: border(1, "black"),
1469
- labelPad: "2px",
1470
- groupBg: "#000000",
1471
- groupColor: "#FFFDF5",
1472
- groupWeight: "650",
1473
- groupSize: sz(14, "group"),
1474
- groupBorderBottom: border(1, "black"),
1475
- groupPad: sz(1.5, "pad"),
1476
- bodySize: sz(14, "body"),
1477
- pad: sz(1, "pad"),
1478
- headingPad: sz(4, "pad"),
1479
- sourcePad: sz(4, "pad"),
1480
- bodyBorderBottom: border(2, "white"),
1481
- sourceSize: sz(12, "source")
1482
- },
1483
- fonts: [{ family: "Roboto Condensed", weights: [400, 650, 700] }],
1484
- rules: (s) => [
1485
- `${s} caption{padding-top:6px;padding-bottom:0}`,
1486
- `${s} .sdvt-subtitle{display:block;padding:0 0 4px}`,
1487
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
1488
- ].join("\n")
1489
- };
1490
- };
1491
- var scoreboard = (ref) => {
1492
- const d = densitySizes(ref.density);
1493
- const accent = hexOption(ref.options?.accent ?? "#0E1621", "accent");
1494
- const ink = "#141719";
1495
- const rule = "#E6E9ED";
1496
- const muted = "#5A6069";
1497
- const bg = "#FFFFFF";
1498
- const condensed = fontStack("Barlow Condensed");
1499
- return {
1500
- name: "scoreboard",
1501
- tokens: {
1502
- ...baseTokens(d, fontStack("Barlow")),
1503
- fontLabel: condensed,
1504
- fontTitle: condensed,
1505
- bg,
1506
- text: ink,
1507
- muted,
1508
- accent,
1509
- rule,
1510
- hline: border(1, rule),
1511
- headingBg: bg,
1512
- titleColor: ink,
1513
- titleSize: px(d.title + 4),
1514
- titleTransform: "uppercase",
1515
- titleTracking: "0.01em",
1516
- subtitleColor: muted,
1517
- subtitleWeight: "500",
1518
- subtitleSize: px(d.subtitle + 1),
1519
- labelBg: accent,
1520
- labelColor: onColor(accent),
1521
- // a pale accent needs dark labels, so measure it
1522
- labelWeight: "700",
1523
- labelSize: px(d.label + 2),
1524
- labelTransform: "uppercase",
1525
- labelTracking: "0.06em",
1526
- labelPad: px(d.pad + 2),
1527
- groupBg: "#F2F4F6",
1528
- groupColor: accent,
1529
- groupWeight: "700",
1530
- groupSize: px(d.group + 1),
1531
- groupTransform: "uppercase",
1532
- groupPad: px(Math.max(d.pad, 3)),
1533
- bodySize: px(d.body),
1534
- bodyWeight: "500",
1535
- pad: px(d.pad),
1536
- headingPad: px(d.pad + 1),
1537
- sourcePad: px(d.pad + 2),
1538
- bodyBorderBottom: border(2, accent),
1539
- sourceColor: muted,
1540
- sourceSize: px(d.source)
1541
- },
1542
- fonts: [
1543
- { family: "Barlow", weights: [500] },
1544
- { family: "Barlow Condensed", weights: [500, 700] }
1545
- ],
1546
- rules: (s) => [
1547
- `${s} caption{letter-spacing:0.01em}`,
1548
- `${s} th.sdvt-group{letter-spacing:0.06em}`,
1549
- `${s} .sdvt-subtitle{display:block;font-family:var(--sdvt-font-title);padding-bottom:${px(d.pad + 6)}}`,
1550
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
1551
- ].join("\n")
1552
- };
1553
- };
1554
-
1555
- // src/themes/sofa.ts
1556
- var sofa = (ref) => {
1557
- const d = densitySizes(ref.density);
1558
- const k = scaleFor(ref.density);
1559
- const sz = (n, role) => px(n * k[role]);
1560
- const style = ref.options?.style ?? "light";
1561
- if (style !== "light" && style !== "dark")
1562
- throw new TableSpecError(`sofa theme style must be "light" or "dark", not "${style}"`);
1563
- const bg = style === "light" ? "#F0EAD6" : "#1c2632";
1564
- return {
1565
- name: "sofa",
1566
- tokens: {
1567
- ...baseTokens(d, fontStack("Sofia Sans Condensed")),
1568
- bg,
1569
- text: style === "light" ? "#333333" : "#FFFFFF",
1570
- hline: border(1, "transparent"),
1571
- titleSize: sz(22, "title"),
1572
- subtitleWeight: "400",
1573
- subtitleSize: sz(14, "subtitle"),
1574
- labelBg: bg,
1575
- labelWeight: "bold",
1576
- labelSize: sz(14, "label"),
1577
- labelPad: "2px",
1578
- groupBg: bg,
1579
- groupWeight: "bold",
1580
- groupSize: sz(14, "group"),
1581
- groupBorderBottom: border(1, "black"),
1582
- groupPad: sz(1.5, "pad"),
1583
- bodySize: sz(14, "body"),
1584
- pad: sz(1, "pad"),
1585
- headingPad: sz(4, "pad"),
1586
- sourcePad: sz(4, "pad"),
1587
- bodyBorderBottom: border(2, "#D3D3D3"),
1588
- sourceSize: sz(10, "source")
1589
- },
1590
- fonts: [{ family: "Sofia Sans Condensed", weights: [400, 650, 700] }],
1591
- rules: (s) => [
1592
- `${s} caption{padding-top:6px;padding-bottom:0}`,
1593
- `${s} .sdvt-subtitle{display:block;padding:0 0 4px}`,
1594
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
1595
- ].join("\n")
1596
- };
1597
- };
1598
-
1599
- // src/themes/swiss.ts
1600
- var swiss = (ref) => {
1601
- const d = densitySizes(ref.density);
1602
- const accent = hexOption(ref.options?.accent ?? "#111111", "accent");
1603
- const ink = "#111111";
1604
- const secondary = "#6B6B6B";
1605
- const bg = "#FFFFFF";
1606
- return {
1607
- name: "swiss",
1608
- tokens: {
1609
- ...baseTokens(d, fontStack("Archivo")),
1610
- bg,
1611
- text: ink,
1612
- muted: secondary,
1613
- accent,
1614
- headingBg: bg,
1615
- titleColor: ink,
1616
- titleSize: px(d.title + 4),
1617
- titleTracking: "-0.02em",
1618
- subtitleColor: secondary,
1619
- subtitleWeight: "400",
1620
- subtitleSize: px(d.subtitle),
1621
- labelBg: bg,
1622
- labelColor: ink,
1623
- labelWeight: "500",
1624
- labelSize: px(d.label),
1625
- labelTransform: "uppercase",
1626
- labelTracking: "0.12em",
1627
- labelBorderBottom: border(1, accent),
1628
- labelPad: px(d.pad + 2),
1629
- groupBg: bg,
1630
- groupColor: secondary,
1631
- groupWeight: "500",
1632
- groupSize: px(d.group),
1633
- groupTransform: "uppercase",
1634
- groupPad: px(d.pad + 6),
1635
- bodySize: px(d.body),
1636
- pad: px(d.pad + 5),
1637
- headingPad: px(d.pad + 4),
1638
- sourcePad: px(d.pad + 4),
1639
- bodyBorderBottom: border(1, ink),
1640
- sourceColor: secondary,
1641
- sourceSize: px(d.source)
1642
- },
1643
- fonts: [{ family: "Archivo", weights: [400, 500, 700] }],
1644
- rules: (s) => [
1645
- `${s} th.sdvt-group{letter-spacing:0.12em}`,
1646
- `${s} .sdvt-subtitle{display:block;padding-bottom:${px(d.pad + 14)}}${s} .sdvt-title{display:block;padding-bottom:${px(Math.ceil(d.pad / 2))}}`,
1647
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
1648
- ].join("\n")
1649
- };
1650
- };
1651
-
1652
- // src/themes/terminal.ts
1653
- var terminal = (ref) => {
1654
- const d = densitySizes(ref.density);
1655
- const accent = hexOption(ref.options?.accent ?? "#FFB86C", "accent");
1656
- const ground = "#0F1115";
1657
- const primary = "#C9D1D9";
1658
- const secondary = "#7D8590";
1659
- const rule = "#262B33";
1660
- return {
1661
- name: "terminal",
1662
- tokens: {
1663
- ...baseTokens(d, fontStack("JetBrains Mono")),
1664
- bg: ground,
1665
- text: primary,
1666
- muted: secondary,
1667
- accent,
1668
- rule,
1669
- hline: border(1, rule),
1670
- headingBg: ground,
1671
- titleColor: primary,
1672
- titleSize: px(d.title - 2),
1673
- titleTransform: "uppercase",
1674
- titleTracking: "0.04em",
1675
- subtitleColor: secondary,
1676
- subtitleWeight: "400",
1677
- subtitleSize: px(d.subtitle - 1),
1678
- labelBg: ground,
1679
- labelColor: accent,
1680
- labelWeight: "700",
1681
- labelSize: px(d.label),
1682
- labelTransform: "uppercase",
1683
- labelTracking: "0.08em",
1684
- labelBorderBottom: border(1, accent),
1685
- labelPad: px(d.pad + 1),
1686
- groupBg: ground,
1687
- groupColor: accent,
1688
- groupWeight: "700",
1689
- groupSize: px(d.group),
1690
- groupTransform: "uppercase",
1691
- groupBorderTop: border(1, rule),
1692
- groupPad: px(Math.max(d.pad, 3)),
1693
- bodySize: px(d.body),
1694
- pad: px(d.pad),
1695
- headingPad: px(d.pad + 2),
1696
- sourcePad: px(d.pad + 2),
1697
- lineHeight: "1.5",
1698
- tableBorderTop: border(1, accent),
1699
- tableBorderBottom: border(1, rule),
1700
- sourceColor: secondary,
1701
- sourceSize: px(d.source)
1702
- },
1703
- fonts: [{ family: "JetBrains Mono", weights: [400, 700] }],
1704
- rules: (s) => [
1705
- `${s} .sdvt-subtitle{display:block;padding-bottom:${px(d.pad + 6)}}${s} .sdvt-title{display:block;padding-bottom:2px}`,
1706
- `${s} tbody tr:last-child td{border-bottom:2px solid ${ground}}`
1707
- ].join("\n")
1708
- };
1709
- };
1710
-
1711
- // src/themes/tier.ts
1712
- var tier = (ref) => {
1713
- const d = densitySizes(ref.density);
1714
- const k = scaleFor(ref.density);
1715
- const sz = (n, role) => px(n * k[role]);
1716
- const style = ref.options?.style ?? "dark";
1717
- if (style !== "light" && style !== "dark")
1718
- throw new TableSpecError(`tier theme style must be "light" or "dark", not "${style}"`);
1719
- const bg = style === "dark" ? "#1a1a17" : "#ffffff";
1720
- return {
1721
- name: "tier",
1722
- tokens: {
1723
- ...baseTokens(d, fontStack("Oswald")),
1724
- bg,
1725
- text: style === "dark" ? "#FFFFFF" : "#333333",
1726
- hline: border(1, "black"),
1727
- titleWeight: "650",
1728
- titleSize: sz(20, "body"),
1729
- subtitleWeight: "500",
1730
- subtitleSize: sz(13.6, "body"),
1731
- labelBg: bg,
1732
- labelWeight: "400",
1733
- labelSize: sz(16, "body"),
1734
- labelPad: "2px",
1735
- groupBg: bg,
1736
- groupWeight: "400",
1737
- groupSize: sz(16, "body"),
1738
- groupBorderBottom: border(1, "black"),
1739
- groupPad: sz(1.5, "pad"),
1740
- bodySize: sz(16, "body"),
1741
- bodyWeight: "500",
1742
- pad: sz(1, "pad"),
1743
- headingPad: sz(4, "pad"),
1744
- sourcePad: sz(4, "pad"),
1745
- bodyBorderBottom: border(2, "#D3D3D3"),
1746
- sourceSize: sz(14.4, "body")
1747
- },
1748
- fonts: [{ family: "Oswald", weights: [500, 650] }],
1749
- rules: (s) => [
1750
- `${s} td.sdvt-cell,${s} th.sdvt-label{text-align:center}`,
1751
- `${s} caption{padding-top:6px;padding-bottom:0}`,
1752
- `${s} .sdvt-subtitle{display:block;padding:0 0 4px}`,
1753
- `${s} tbody tr:last-child td{border-bottom:2px solid ${bg}}`
1754
- ].join("\n")
1755
- };
1756
- };
1757
-
1758
- // src/themes/tufte.ts
1759
- var tufte = (ref) => {
1760
- const d = densitySizes(ref.density);
1761
- const accent = hexOption(ref.options?.accent ?? "#111111", "accent");
1762
- const ink = "#111111";
1763
- const secondary = "#6F6A60";
1764
- const hair = "#C9C4B8";
1765
- const bg = "#FFFFF8";
1766
- return {
1767
- name: "tufte",
1768
- tokens: {
1769
- ...baseTokens(d, fontStack("EB Garamond")),
1770
- bg,
1771
- text: ink,
1772
- muted: secondary,
1773
- accent,
1774
- rule: hair,
1775
- headingBg: bg,
1776
- titleColor: ink,
1777
- titleWeight: "500",
1778
- titleSize: px(d.title),
1779
- subtitleColor: secondary,
1780
- subtitleStyle: "italic",
1781
- subtitleSize: px(d.subtitle),
1782
- labelBg: bg,
1783
- labelColor: secondary,
1784
- labelWeight: "400",
1785
- labelStyle: "italic",
1786
- labelSize: px(d.label + 2),
1787
- labelBorderBottom: border(1, accent),
1788
- labelPad: px(Math.max(d.pad - 1, 2)),
1789
- groupBg: bg,
1790
- groupColor: accent,
1791
- groupWeight: "600",
1792
- groupSize: px(d.group + 2),
1793
- groupPad: px(Math.max(d.pad - 1, 2)),
1794
- bodySize: px(d.body + 1),
1795
- pad: px(d.pad),
1796
- bodyBorderBottom: border(1, hair),
1797
- sourceColor: secondary,
1798
- sourceSize: px(d.source + 1),
1799
- sourceStyle: "italic"
1800
- },
1801
- fonts: [{ family: "EB Garamond", weights: [400, 500, 600], italic: true }],
1802
- rules: (s) => [`${s} th.sdvt-group{font-style:italic}`, `${s} tfoot td{padding-top:${px(d.pad + 4)}}`].join("\n")
1803
- };
1804
- };
1805
-
1806
- // src/themes/index.ts
1807
- var THEMES = {
1808
- sdv,
1809
- sdvTeam,
1810
- almanac,
1811
- athletic,
1812
- booktabs,
1813
- broadsheet,
1814
- brutalist,
1815
- drench,
1816
- gtutils,
1817
- kenpom,
1818
- midnight,
1819
- ncaa,
1820
- pl,
1821
- savant,
1822
- scoreboard,
1823
- sofa,
1824
- swiss,
1825
- terminal,
1826
- tier,
1827
- tufte
1828
- };
1829
- var THEME_NAMES = Object.keys(THEMES);
1830
- function resolveTheme(ref) {
1831
- const def = THEMES[ref.name];
1832
- if (!def) throw new TableSpecError(`unknown theme "${ref.name}"; one of ${THEME_NAMES.join(", ")}`);
1833
- return def(ref);
1834
- }
1835
-
1836
- // src/engine.ts
1837
- var NUMERIC_KINDS = /* @__PURE__ */ new Set([
1838
- "num",
1839
- "int",
1840
- "pct",
1841
- "rank",
1842
- "delta",
1843
- "tally",
1844
- "colorPills",
1845
- "colorRanks",
1846
- "percentileBar"
1847
- ]);
1848
- function isMissing(v) {
1849
- return v === null || v === void 0 || v === "" || typeof v === "number" && Number.isNaN(v) || v instanceof Date && Number.isNaN(v.getTime());
1850
- }
1851
- var collator = new Intl.Collator("en", { numeric: true, sensitivity: "base" });
1852
- var compareNum = (a, b) => Number(a) - Number(b);
1853
- var compareText = (a, b) => collator.compare(String(a), String(b));
1854
- var compareDate = (a, b) => {
1855
- const t = (v) => v instanceof Date ? v.getTime() : new Date(String(v)).getTime();
1856
- const x = t(a);
1857
- const y = t(b);
1858
- return Number.isNaN(x) || Number.isNaN(y) ? 0 : x - y;
1859
- };
1860
- var fieldOf = (row, key) => row[key];
1861
- function comparatorFor(col, rows) {
1862
- if (col.compare) return col.compare;
1863
- if (NUMERIC_KINDS.has(col.kind)) return compareNum;
1864
- const sample = rows.map((r) => fieldOf(r, col.key)).find((v) => !isMissing(v));
1865
- return typeof sample === "number" ? compareNum : sample instanceof Date ? compareDate : compareText;
1866
- }
1867
- function withMissingLast(cmp, dir) {
1868
- return (a, b) => {
1869
- const ma = isMissing(a);
1870
- const mb = isMissing(b);
1871
- if (ma || mb) return ma === mb ? 0 : ma ? 1 : -1;
1872
- const c = cmp(a, b);
1873
- if (Number.isNaN(c)) return 0;
1874
- return dir === "asc" ? c : -c;
1875
- };
1876
- }
1877
- function findColumn(spec, key) {
1878
- const col = spec.columns.find((c) => c.key === key);
1879
- if (!col)
1880
- throw new TableSpecError(
1881
- `unknown column "${key}"; columns are ${spec.columns.map((c) => c.key).join(", ")}`
1882
- );
1883
- return col;
1884
- }
1885
- function applySort(spec, rows, sort) {
1886
- if (!sort) return [...rows];
1887
- const col = findColumn(spec, sort.col);
1888
- const cmp = withMissingLast(comparatorFor(col, rows), sort.dir);
1889
- return [...rows].sort((a, b) => cmp(fieldOf(a, col.key), fieldOf(b, col.key)));
1890
- }
1891
- function matches2(filter, value, row) {
1892
- if (typeof filter === "function") return filter(value, row);
1893
- if (typeof filter === "string")
1894
- return String(value ?? "").toLowerCase().includes(filter.toLowerCase());
1895
- return value === filter;
1896
- }
1897
- function applyFilters(spec, rows, state) {
1898
- const active = Object.entries(state.filters).map(([key, f]) => [findColumn(spec, key), f]);
1899
- const q = state.globalFilter.trim().toLowerCase();
1900
- const searchable = spec.columns.filter((c) => !state.hidden.includes(c.key));
1901
- const external = state.externalFilter;
1902
- return rows.filter(
1903
- (row) => (external === null || external(row)) && active.every(([col, f]) => matches2(f, fieldOf(row, col.key), row)) && (q === "" || searchable.some(
1904
- (c) => String(fieldOf(row, c.key) ?? "").toLowerCase().includes(q)
1905
- ))
1906
- );
1907
- }
1908
- function paginate(rows, page, pageSize) {
1909
- const pageCount = Math.max(1, Math.ceil(rows.length / pageSize));
1910
- const p = Math.min(Math.max(0, page), pageCount - 1);
1911
- const slice = pageSize === Number.POSITIVE_INFINITY ? [...rows] : rows.slice(p * pageSize, (p + 1) * pageSize);
1912
- return { rows: slice, pageCount };
1913
- }
1914
- function nextSortDir(current, col) {
1915
- if (!current || current.col !== col) return "asc";
1916
- return current.dir === "asc" ? "desc" : null;
1917
- }
1918
- function checkPageSize(n) {
1919
- if (n === Number.POSITIVE_INFINITY) return n;
1920
- if (!Number.isInteger(n) || n < 1)
1921
- throw new TableSpecError(`pageSize must be a positive integer or Infinity, got ${String(n)}`);
1922
- return n;
1923
- }
1924
- var sameSet = (a, b) => a.size === b.size && [...a].every((id) => b.has(id));
1925
- var CHANGE = { type: "change" };
1926
- function createTable(spec, rows, options = {}) {
1927
- if (options.sort) findColumn(spec, options.sort.col);
1928
- let state = {
1929
- sort: options.sort ?? null,
1930
- filters: {},
1931
- globalFilter: "",
1932
- page: 0,
1933
- pageSize: checkPageSize(options.pageSize ?? spec.interactive?.pageSize ?? Number.POSITIVE_INFINITY),
1934
- hidden: [],
1935
- externalFilter: null,
1936
- selection: /* @__PURE__ */ new Set(),
1937
- cursor: { row: 0, col: null }
1938
- };
1939
- const listeners = /* @__PURE__ */ new Set();
1940
- const emit = (event) => {
1941
- for (const fn of [...listeners]) fn(event);
1942
- };
1943
- const key = spec.rowKey;
1944
- let source = rows;
1945
- let indexOf = null;
1946
- let warned = false;
1947
- const rowId = (row) => {
1948
- if (key === void 0) {
1949
- const i = indexOf?.get(row);
1950
- return i === void 0 ? "" : String(i);
1951
- }
1952
- const v = fieldOf(row, key);
1953
- return isMissing(v) ? "" : String(v);
1954
- };
1955
- const setSource = (next) => {
1956
- source = next;
1957
- if (key === void 0) {
1958
- indexOf = new Map(next.map((r, i) => [r, i]));
1959
- return;
1960
- }
1961
- if (warned) return;
1962
- const ids = next.map(rowId).filter((id) => id !== "");
1963
- const dupes = ids.length - new Set(ids).size;
1964
- if (dupes > 0) {
1965
- warned = true;
1966
- console.warn(
1967
- `sdvtables: rowKey "${key}" is not unique \u2014 ${dupes} duplicate id(s); linked selection will merge them`
1968
- );
1969
- }
1970
- };
1971
- setSource(rows);
1972
- let hovered = null;
1973
- const compute = () => {
1974
- const filtered = applyFilters(spec, source, state);
1975
- const sorted = applySort(spec, filtered, state.sort);
1976
- const { rows: pageRows, pageCount } = paginate(sorted, state.page, state.pageSize);
1977
- return { state, rows: pageRows, filteredCount: filtered.length, pageCount };
1978
- };
1979
- let snapshot = compute();
1980
- const update = (patch, event = CHANGE) => {
1981
- state = { ...state, ...patch };
1982
- snapshot = compute();
1983
- if (state.page > snapshot.pageCount - 1) {
1984
- state = { ...state, page: snapshot.pageCount - 1 };
1985
- snapshot = compute();
1986
- }
1987
- const last = Math.max(0, snapshot.rows.length - 1);
1988
- if (state.cursor.row > last) {
1989
- state = { ...state, cursor: { ...state.cursor, row: last } };
1990
- snapshot = { ...snapshot, state };
1991
- }
1992
- emit(event);
1993
- };
1994
- return {
1995
- spec,
1996
- get allRows() {
1997
- return source;
1998
- },
1999
- get state() {
2000
- return snapshot.state;
2001
- },
2002
- get rows() {
2003
- return snapshot.rows;
2004
- },
2005
- get filteredCount() {
2006
- return snapshot.filteredCount;
2007
- },
2008
- get pageCount() {
2009
- return snapshot.pageCount;
2010
- },
2011
- get columns() {
2012
- return spec.columns.filter((c) => !snapshot.state.hidden.includes(c.key));
2013
- },
2014
- setSort(col, dir) {
2015
- if (dir !== null) findColumn(spec, col);
2016
- update({ sort: dir === null ? null : { col, dir }, page: 0 });
2017
- },
2018
- setFilter(col, filter) {
2019
- findColumn(spec, col);
2020
- const { [col]: _dropped, ...rest } = state.filters;
2021
- const filters = filter === null || filter === void 0 || filter === "" ? rest : { ...rest, [col]: filter };
2022
- update({ filters, page: 0 });
2023
- },
2024
- setGlobalFilter(text) {
2025
- update({ globalFilter: text, page: 0 });
2026
- },
2027
- setPage(n) {
2028
- if (!Number.isFinite(n)) return;
2029
- update({ page: Math.max(0, Math.min(Math.trunc(n), snapshot.pageCount - 1)) });
2030
- },
2031
- setPageSize(n) {
2032
- update({ pageSize: checkPageSize(n), page: 0 });
2033
- },
2034
- toggleColumn(col, visible) {
2035
- findColumn(spec, col);
2036
- const hiddenNow = state.hidden.includes(col);
2037
- const hide = visible === void 0 ? !hiddenNow : !visible;
2038
- const hidden = hide ? hiddenNow ? state.hidden : [...state.hidden, col] : state.hidden.filter((c) => c !== col);
2039
- update({ hidden, sort: hide && state.sort?.col === col ? null : state.sort });
2040
- },
2041
- setRows(next) {
2042
- setSource(next);
2043
- update({});
2044
- },
2045
- rowId,
2046
- setExternalFilter(filter) {
2047
- if (filter === state.externalFilter) return;
2048
- update({ externalFilter: filter, page: 0 });
2049
- },
2050
- setSelection(ids) {
2051
- if (sameSet(ids, state.selection)) return;
2052
- const selection = new Set(ids);
2053
- update({ selection }, { type: "select", ids: selection });
2054
- },
2055
- getSelection: () => state.selection,
2056
- setCursor(row, col) {
2057
- if (!Number.isFinite(row)) return;
2058
- if (col !== null) findColumn(spec, col);
2059
- const r = Math.max(0, Math.min(Math.trunc(row), snapshot.rows.length - 1));
2060
- if (r === state.cursor.row && col === state.cursor.col) return;
2061
- update({ cursor: { row: r, col } });
2062
- },
2063
- setHover(id) {
2064
- if (id === hovered) return;
2065
- hovered = id;
2066
- emit({ type: "hover", id });
2067
- },
2068
- getHover: () => hovered,
2069
- subscribe(fn) {
2070
- listeners.add(fn);
2071
- return () => {
2072
- listeners.delete(fn);
2073
- };
2074
- },
2075
- getSnapshot: () => snapshot
2076
- };
2077
- }
2078
-
2079
- export { DENSITY, NUMERIC_KINDS, RANK_PALETTE, THEMES, THEME_NAMES, TOKEN_KEYS, TableBuilder, TableSpecError, applyFilters, applySort, columnFactory, comparatorFor, createTable, defineTable, findColumn, fontStack, formatNumber, formatValue, isBlank, isMissing, matches, naturalDigits, nextSortDir, ordinal, paginate, resolveTheme, secondaryOn, selectRows, snakeAlign, toNumber, withMissingLast };
2080
- //# sourceMappingURL=chunk-4VKYHOHL.js.map
2081
- //# sourceMappingURL=chunk-4VKYHOHL.js.map