@pretable/react 0.0.1

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/dist/index.cjs ADDED
@@ -0,0 +1,1421 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var core = require('@pretable/core');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ // src/pretable-surface.tsx
8
+
9
+ // src/row-height.ts
10
+ var MIN_ROW_HEIGHT = 44;
11
+ function parsePxLength(value) {
12
+ if (!value) {
13
+ return 0;
14
+ }
15
+ const parsed = parseFloat(value);
16
+ return Number.isFinite(parsed) ? parsed : 0;
17
+ }
18
+ function measureRenderedRowHeight(row) {
19
+ const style = getComputedStyle(row);
20
+ const verticalPadding = parsePxLength(style.paddingTop) + parsePxLength(style.paddingBottom);
21
+ const borderHeight = parsePxLength(style.borderBottomWidth);
22
+ const wrappedCells = [
23
+ ...row.querySelectorAll(
24
+ '[data-pretable-cell][data-pretable-wrap="true"]'
25
+ )
26
+ ];
27
+ const measuredCells = wrappedCells.length > 0 ? wrappedCells : [...row.querySelectorAll("[data-pretable-cell]")];
28
+ const contentHeight = Math.max(
29
+ 0,
30
+ ...measuredCells.map((cell) => cell.scrollHeight).filter(Number.isFinite)
31
+ );
32
+ return Math.max(
33
+ MIN_ROW_HEIGHT,
34
+ Math.ceil(contentHeight + verticalPadding + borderHeight)
35
+ );
36
+ }
37
+
38
+ // ../layout-core/dist/prefix-sums.js
39
+ function createRowMetricsIndex(estimatedHeights) {
40
+ return new PrefixSumsRowMetricsIndex(estimatedHeights);
41
+ }
42
+ var PrefixSumsRowMetricsIndex = class {
43
+ rowCount;
44
+ #heights;
45
+ #starts;
46
+ #totalHeight;
47
+ constructor(estimatedHeights) {
48
+ this.rowCount = estimatedHeights.length;
49
+ this.#heights = estimatedHeights.map((height) => normalizeHeight(height));
50
+ this.#starts = [0];
51
+ for (const height of this.#heights) {
52
+ this.#starts.push((this.#starts.at(-1) ?? 0) + height);
53
+ }
54
+ this.#totalHeight = this.#starts.at(-1) ?? 0;
55
+ }
56
+ getHeight(index) {
57
+ return this.#heights[index] ?? 0;
58
+ }
59
+ getOffsetForIndex(index) {
60
+ if (index <= 0) {
61
+ return 0;
62
+ }
63
+ if (index >= this.rowCount) {
64
+ return this.#totalHeight;
65
+ }
66
+ return this.#starts[index] ?? this.#totalHeight;
67
+ }
68
+ getIndexForOffset(offset) {
69
+ if (this.rowCount === 0 || offset <= 0) {
70
+ return 0;
71
+ }
72
+ if (offset >= this.#totalHeight) {
73
+ return this.rowCount;
74
+ }
75
+ let low = 0;
76
+ let high = this.rowCount - 1;
77
+ while (low <= high) {
78
+ const middle = Math.floor((low + high) / 2);
79
+ const start = this.#starts[middle] ?? 0;
80
+ const end = this.#starts[middle + 1] ?? this.#totalHeight;
81
+ if (offset < start) {
82
+ high = middle - 1;
83
+ continue;
84
+ }
85
+ if (offset >= end) {
86
+ low = middle + 1;
87
+ continue;
88
+ }
89
+ return middle;
90
+ }
91
+ return Math.min(this.rowCount, low);
92
+ }
93
+ getTotalHeight() {
94
+ return this.#totalHeight;
95
+ }
96
+ updateHeight(index, height) {
97
+ const nextHeight = normalizeHeight(height);
98
+ const previousHeight = this.#heights[index];
99
+ if (previousHeight === void 0) {
100
+ throw new RangeError(`Row index ${index} is out of bounds.`);
101
+ }
102
+ const delta = nextHeight - previousHeight;
103
+ if (delta === 0) {
104
+ return;
105
+ }
106
+ this.#heights[index] = nextHeight;
107
+ for (let startIndex = index + 1; startIndex < this.#starts.length; startIndex += 1) {
108
+ this.#starts[startIndex] = (this.#starts[startIndex] ?? 0) + delta;
109
+ }
110
+ this.#totalHeight += delta;
111
+ }
112
+ };
113
+ function normalizeHeight(height) {
114
+ return Math.max(1, Math.round(height));
115
+ }
116
+
117
+ // ../layout-core/dist/column-plan.js
118
+ function planColumns(input) {
119
+ const pinned = [];
120
+ const scrollable = [];
121
+ let pinnedLeftWidth = 0;
122
+ let scrollableLeft = 0;
123
+ for (let i = 0; i < input.columns.length; i++) {
124
+ const col = input.columns[i];
125
+ if (col.pinned === "left") {
126
+ pinned.push({
127
+ index: i,
128
+ id: col.id,
129
+ left: pinnedLeftWidth,
130
+ width: col.width,
131
+ pinned: "left"
132
+ });
133
+ pinnedLeftWidth += col.width;
134
+ } else {
135
+ scrollable.push({
136
+ index: i,
137
+ id: col.id,
138
+ width: col.width,
139
+ left: scrollableLeft
140
+ });
141
+ scrollableLeft += col.width;
142
+ }
143
+ }
144
+ const totalWidth = pinnedLeftWidth + scrollableLeft;
145
+ if (scrollable.length === 0) {
146
+ return { columns: pinned, totalWidth, pinnedLeftWidth };
147
+ }
148
+ let low = 0;
149
+ let high = scrollable.length - 1;
150
+ let visibleStart = scrollable.length;
151
+ while (low <= high) {
152
+ const mid = Math.floor((low + high) / 2);
153
+ const colRight = scrollable[mid].left + scrollable[mid].width + pinnedLeftWidth;
154
+ if (colRight <= input.scrollLeft) {
155
+ low = mid + 1;
156
+ } else {
157
+ visibleStart = mid;
158
+ high = mid - 1;
159
+ }
160
+ }
161
+ let visibleEnd = visibleStart;
162
+ const scrollRight = input.scrollLeft + input.viewportWidth;
163
+ while (visibleEnd < scrollable.length) {
164
+ const colLeft = scrollable[visibleEnd].left + pinnedLeftWidth;
165
+ if (colLeft >= scrollRight) {
166
+ break;
167
+ }
168
+ visibleEnd++;
169
+ }
170
+ const overscanStart = Math.max(0, visibleStart - input.overscan);
171
+ const overscanEnd = Math.min(scrollable.length, visibleEnd + input.overscan);
172
+ const visibleScrollable = [];
173
+ for (let i = overscanStart; i < overscanEnd; i++) {
174
+ const col = scrollable[i];
175
+ visibleScrollable.push({
176
+ index: col.index,
177
+ id: col.id,
178
+ left: col.left + pinnedLeftWidth,
179
+ width: col.width
180
+ });
181
+ }
182
+ return {
183
+ columns: [...pinned, ...visibleScrollable],
184
+ totalWidth,
185
+ pinnedLeftWidth
186
+ };
187
+ }
188
+
189
+ // ../layout-core/dist/viewport-plan.js
190
+ function planViewport(input) {
191
+ const totalHeight = input.rowMetrics.getTotalHeight();
192
+ const rowCount = input.rowMetrics.rowCount;
193
+ if (rowCount === 0) {
194
+ return {
195
+ range: { start: 0, end: 0 },
196
+ rows: [],
197
+ totalHeight,
198
+ pinned: {
199
+ left: planPinnedColumns(input.pinnedLeft ?? [], "left"),
200
+ right: planPinnedColumns(input.pinnedRight ?? [], "right")
201
+ }
202
+ };
203
+ }
204
+ const clampedScrollTop = Math.max(0, Math.min(input.scrollTop, Math.max(0, totalHeight - 1)));
205
+ const visibleStart = Math.min(rowCount - 1, input.rowMetrics.getIndexForOffset(clampedScrollTop));
206
+ const visibleEndExclusive = Math.min(rowCount, Math.max(visibleStart + 1, input.rowMetrics.getIndexForOffset(clampedScrollTop + Math.max(0, input.viewportHeight)) + 1));
207
+ const start = Math.max(0, visibleStart - Math.max(0, input.overscan));
208
+ const end = Math.min(rowCount, visibleEndExclusive + Math.max(0, input.overscan));
209
+ const rows = [];
210
+ for (let index = start; index < end; index += 1) {
211
+ rows.push({
212
+ index,
213
+ top: input.rowMetrics.getOffsetForIndex(index),
214
+ height: input.rowMetrics.getHeight(index)
215
+ });
216
+ }
217
+ return {
218
+ range: { start, end },
219
+ rows,
220
+ totalHeight,
221
+ pinned: {
222
+ left: planPinnedColumns(input.pinnedLeft ?? [], "left"),
223
+ right: planPinnedColumns(input.pinnedRight ?? [], "right")
224
+ }
225
+ };
226
+ }
227
+ function planPinnedColumns(columns, side) {
228
+ let offset = 0;
229
+ return columns.map((column) => {
230
+ const planned = {
231
+ ...column,
232
+ side,
233
+ start: offset,
234
+ end: offset + column.width
235
+ };
236
+ offset = planned.end;
237
+ return planned;
238
+ });
239
+ }
240
+
241
+ // ../text-core/dist/layout-text.js
242
+ var DEFAULT_LINE_HEIGHT_PX = 20;
243
+ function layoutPreparedText(prepared, width, options = {}) {
244
+ const wrapMode = options.wrapMode ?? "wrap";
245
+ const lineHeightPx = options.lineHeightPx ?? DEFAULT_LINE_HEIGHT_PX;
246
+ const paddingBlockPx = options.paddingBlockPx ?? 0;
247
+ const explicitLineCount = countExplicitLines(prepared.tokens);
248
+ if (wrapMode === "nowrap") {
249
+ return buildLayout({
250
+ lineCount: explicitLineCount,
251
+ lineHeightPx,
252
+ paddingBlockPx,
253
+ measuredWidth: prepared.graphemeCount * prepared.averageCharWidth,
254
+ overflowX: prepared.graphemeCount * prepared.averageCharWidth > width
255
+ });
256
+ }
257
+ const charsPerLine = Math.max(1, Math.floor(width / prepared.averageCharWidth));
258
+ const { lineCount, maxLineChars } = wrapTokens(prepared.tokens, charsPerLine);
259
+ return buildLayout({
260
+ lineCount,
261
+ lineHeightPx,
262
+ paddingBlockPx,
263
+ measuredWidth: Math.min(width, maxLineChars * prepared.averageCharWidth),
264
+ overflowX: false
265
+ });
266
+ }
267
+ function countExplicitLines(tokens) {
268
+ return tokens.reduce((count, token) => count + (token.kind === "newline" ? 1 : 0), 1);
269
+ }
270
+ function wrapTokens(tokens, charsPerLine) {
271
+ let lineCount = 1;
272
+ let currentLineChars = 0;
273
+ let maxLineChars = 0;
274
+ const pushLine = () => {
275
+ maxLineChars = Math.max(maxLineChars, currentLineChars);
276
+ lineCount += 1;
277
+ currentLineChars = 0;
278
+ };
279
+ for (const token of tokens) {
280
+ if (token.kind === "newline") {
281
+ maxLineChars = Math.max(maxLineChars, currentLineChars);
282
+ lineCount += 1;
283
+ currentLineChars = 0;
284
+ continue;
285
+ }
286
+ if (token.kind === "space") {
287
+ if (currentLineChars === 0) {
288
+ continue;
289
+ }
290
+ if (currentLineChars + token.length <= charsPerLine) {
291
+ currentLineChars += token.length;
292
+ } else {
293
+ pushLine();
294
+ }
295
+ continue;
296
+ }
297
+ placeWord(token.length);
298
+ }
299
+ maxLineChars = Math.max(maxLineChars, currentLineChars);
300
+ return { lineCount, maxLineChars };
301
+ function placeWord(wordLength) {
302
+ if (currentLineChars === 0) {
303
+ currentLineChars = placeAtLineStart(wordLength);
304
+ return;
305
+ }
306
+ if (currentLineChars + wordLength <= charsPerLine) {
307
+ currentLineChars += wordLength;
308
+ return;
309
+ }
310
+ pushLine();
311
+ currentLineChars = placeAtLineStart(wordLength);
312
+ }
313
+ function placeAtLineStart(wordLength) {
314
+ if (wordLength <= charsPerLine) {
315
+ return wordLength;
316
+ }
317
+ const wrappedLines = Math.ceil(wordLength / charsPerLine);
318
+ lineCount += wrappedLines - 1;
319
+ maxLineChars = Math.max(maxLineChars, charsPerLine);
320
+ return wordLength % charsPerLine || charsPerLine;
321
+ }
322
+ }
323
+ function buildLayout(input) {
324
+ return {
325
+ lineCount: input.lineCount,
326
+ height: input.lineCount * input.lineHeightPx + input.paddingBlockPx * 2,
327
+ measuredWidth: input.measuredWidth,
328
+ overflowX: input.overflowX
329
+ };
330
+ }
331
+ function prepareText(input) {
332
+ const text = input.text.replaceAll("\r\n", "\n");
333
+ const graphemes = Array.from(text);
334
+ return {
335
+ text,
336
+ fontKey: input.fontKey,
337
+ graphemeCount: graphemes.length,
338
+ breakpoints: collectBreakpoints(graphemes),
339
+ averageCharWidth: input.averageCharWidth,
340
+ tokens: tokenizeText(text)
341
+ };
342
+ }
343
+ function collectBreakpoints(graphemes) {
344
+ const breakpoints = [];
345
+ for (let index = 0; index < graphemes.length; index += 1) {
346
+ const value = graphemes[index];
347
+ if (value === void 0) {
348
+ continue;
349
+ }
350
+ if (/\s/u.test(value) || value === "-" || value === "/" || value === "_") {
351
+ breakpoints.push(index + 1);
352
+ }
353
+ }
354
+ return breakpoints;
355
+ }
356
+ function tokenizeText(text) {
357
+ const matches = text.match(/\n|[^\S\n]+|[^\s]+/gu) ?? [];
358
+ return matches.map((value) => {
359
+ if (value === "\n") {
360
+ return { kind: "newline", value, length: 0 };
361
+ }
362
+ if (/^[^\S\n]+$/u.test(value)) {
363
+ return { kind: "space", value, length: Array.from(value).length };
364
+ }
365
+ return { kind: "word", value, length: Array.from(value).length };
366
+ });
367
+ }
368
+
369
+ // ../renderer-dom/dist/create-renderer.js
370
+ var DEFAULT_ROW_HEIGHT = 44;
371
+ var WRAPPED_COLUMN_WIDTH = 220;
372
+ var FIXED_COLUMN_WIDTH = 140;
373
+ var ROW_LINE_HEIGHT = 24;
374
+ var ROW_CHROME_HEIGHT = 42;
375
+ var ESTIMATED_CHARACTER_WIDTH = 7;
376
+ var ESTIMATE_FONT_KEY = "Pretable Estimate 14";
377
+ var estimatedRowHeightCache = /* @__PURE__ */ new WeakMap();
378
+ function createDomRenderSnapshot(input) {
379
+ const rowHeights = input.snapshot.visibleRows.map((entry) => {
380
+ const measuredHeight = input.measuredHeights?.[entry.id];
381
+ return measuredHeight ?? estimateRowHeight(entry.row, input.columns);
382
+ });
383
+ const rowMetrics = createRowMetricsIndex(rowHeights);
384
+ const viewportPlan = planViewport({
385
+ scrollTop: input.scrollTop,
386
+ viewportHeight: input.viewportHeight,
387
+ overscan: input.overscan,
388
+ rowMetrics,
389
+ pinnedLeft: input.columns.filter((column) => column.pinned === "left").map((column) => ({
390
+ columnId: column.id,
391
+ width: getColumnWidth(column)
392
+ }))
393
+ });
394
+ const rows = viewportPlan.rows.flatMap((plannedRow) => {
395
+ const entry = input.snapshot.visibleRows[plannedRow.index];
396
+ if (!entry) {
397
+ return [];
398
+ }
399
+ return [
400
+ {
401
+ id: entry.id,
402
+ row: entry.row,
403
+ rowIndex: plannedRow.index,
404
+ top: plannedRow.top,
405
+ height: plannedRow.height
406
+ }
407
+ ];
408
+ });
409
+ const columnInputs = input.columns.map((col) => ({
410
+ id: col.id,
411
+ width: getColumnWidth(col),
412
+ pinned: col.pinned
413
+ }));
414
+ const columnPlan = input.viewportWidth !== void 0 ? planColumns({
415
+ columns: columnInputs,
416
+ scrollLeft: input.scrollLeft ?? 0,
417
+ viewportWidth: input.viewportWidth,
418
+ overscan: input.overscan
419
+ }) : {
420
+ columns: (() => {
421
+ let left = 0;
422
+ return columnInputs.map((col, index) => {
423
+ const entry = {
424
+ index,
425
+ id: col.id,
426
+ left,
427
+ width: col.width,
428
+ pinned: col.pinned
429
+ };
430
+ left += col.width;
431
+ return entry;
432
+ });
433
+ })(),
434
+ totalWidth: columnInputs.reduce((sum, col) => sum + col.width, 0),
435
+ pinnedLeftWidth: columnInputs.filter((col) => col.pinned === "left").reduce((sum, col) => sum + col.width, 0)
436
+ };
437
+ return {
438
+ frame: {
439
+ snapshot: input.snapshot
440
+ },
441
+ rows,
442
+ columns: columnPlan.columns,
443
+ nodeCount: rows.length * columnPlan.columns.length,
444
+ totalHeight: viewportPlan.totalHeight,
445
+ totalWidth: columnPlan.totalWidth
446
+ };
447
+ }
448
+ function estimateRowHeight(row, columns) {
449
+ const cached = estimatedRowHeightCache.get(row);
450
+ if (cached && cached.columnsRef === columns) {
451
+ return cached.height;
452
+ }
453
+ const signature = getEstimatedRowHeightSignature(row, columns);
454
+ if (cached?.signature === signature) {
455
+ cached.columnsRef = columns;
456
+ return cached.height;
457
+ }
458
+ let estimatedHeight = DEFAULT_ROW_HEIGHT;
459
+ for (const column of columns) {
460
+ if (!column.wrap) {
461
+ continue;
462
+ }
463
+ const prepared = prepareText({
464
+ text: String(readCellValue(row, column)),
465
+ fontKey: ESTIMATE_FONT_KEY,
466
+ averageCharWidth: ESTIMATED_CHARACTER_WIDTH
467
+ });
468
+ const layout = layoutPreparedText(prepared, getColumnWidth(column), {
469
+ lineHeightPx: ROW_LINE_HEIGHT,
470
+ wrapMode: "wrap"
471
+ });
472
+ estimatedHeight = Math.max(estimatedHeight, layout.height + ROW_CHROME_HEIGHT);
473
+ }
474
+ estimatedRowHeightCache.set(row, {
475
+ signature,
476
+ height: estimatedHeight,
477
+ columnsRef: columns
478
+ });
479
+ return estimatedHeight;
480
+ }
481
+ function getEstimatedRowHeightSignature(row, columns) {
482
+ return columns.filter((column) => column.wrap).map((column) => {
483
+ const value = String(readCellValue(row, column) ?? "");
484
+ return `${column.id}:${getColumnWidth(column)}:${value}`;
485
+ }).join("|");
486
+ }
487
+ function readCellValue(row, column) {
488
+ return column.getValue ? column.getValue(row) : row[column.id];
489
+ }
490
+ function getColumnWidth(column) {
491
+ return column.widthPx ?? (column.wrap ? WRAPPED_COLUMN_WIDTH : FIXED_COLUMN_WIDTH);
492
+ }
493
+ function usePretable({
494
+ autosize,
495
+ columns,
496
+ rows,
497
+ getRowId
498
+ }) {
499
+ return react.useMemo(
500
+ () => core.createGrid({ columns, rows, getRowId, autosize }),
501
+ [autosize, columns, getRowId, rows]
502
+ );
503
+ }
504
+ function usePretableModel({
505
+ autosize,
506
+ columns,
507
+ rows,
508
+ getRowId,
509
+ viewportHeight,
510
+ viewportWidth,
511
+ overscan = 6,
512
+ interactionOverrides,
513
+ measuredHeights
514
+ }) {
515
+ const grid = usePretable({ autosize, columns, rows, getRowId });
516
+ if (interactionOverrides) {
517
+ grid.setSort(
518
+ interactionOverrides.sort?.columnId ?? null,
519
+ interactionOverrides.sort?.direction ?? null
520
+ );
521
+ grid.replaceFilters(interactionOverrides.filters ?? {});
522
+ if (interactionOverrides.focusedRowId !== void 0) {
523
+ const firstColumnId = columns[0]?.id ?? null;
524
+ grid.setFocus(
525
+ interactionOverrides.focusedRowId,
526
+ interactionOverrides.focusedRowId ? firstColumnId : null
527
+ );
528
+ }
529
+ if (interactionOverrides.selectedRowId !== void 0) {
530
+ grid.selectRow(interactionOverrides.selectedRowId);
531
+ }
532
+ }
533
+ const snapshot = react.useSyncExternalStore(
534
+ grid.subscribe,
535
+ grid.getSnapshot,
536
+ grid.getSnapshot
537
+ );
538
+ react.useLayoutEffect(() => {
539
+ if (snapshot.viewport.height === viewportHeight && snapshot.viewport.width === (viewportWidth ?? 0)) {
540
+ return;
541
+ }
542
+ grid.setViewport({
543
+ scrollTop: snapshot.viewport.scrollTop,
544
+ scrollLeft: snapshot.viewport.scrollLeft,
545
+ height: viewportHeight,
546
+ width: viewportWidth ?? 0
547
+ });
548
+ }, [
549
+ grid,
550
+ snapshot.viewport.height,
551
+ snapshot.viewport.width,
552
+ snapshot.viewport.scrollTop,
553
+ snapshot.viewport.scrollLeft,
554
+ viewportHeight,
555
+ viewportWidth
556
+ ]);
557
+ const renderSnapshot = react.useMemo(
558
+ () => createDomRenderSnapshot({
559
+ columns: grid.options.columns,
560
+ snapshot,
561
+ scrollTop: snapshot.viewport.scrollTop,
562
+ scrollLeft: snapshot.viewport.scrollLeft,
563
+ viewportHeight,
564
+ viewportWidth,
565
+ overscan,
566
+ measuredHeights
567
+ }),
568
+ [
569
+ grid.options.columns,
570
+ measuredHeights,
571
+ overscan,
572
+ snapshot,
573
+ viewportHeight,
574
+ viewportWidth
575
+ ]
576
+ );
577
+ const telemetry = react.useMemo(() => {
578
+ const viewportBottom = snapshot.viewport.scrollTop + Math.max(snapshot.viewport.height, viewportHeight);
579
+ const viewportRows = renderSnapshot.rows.filter((row) => {
580
+ const rowBottom = row.top + row.height;
581
+ return row.top < viewportBottom && rowBottom > snapshot.viewport.scrollTop;
582
+ });
583
+ const firstVisibleRow = viewportRows[0];
584
+ const lastVisibleRow = viewportRows[viewportRows.length - 1];
585
+ return {
586
+ focusedRowId: snapshot.focus.rowId,
587
+ rowModelRowCount: snapshot.visibleRows.length,
588
+ renderedRowCount: renderSnapshot.rows.length,
589
+ selectedRowId: snapshot.selection.rowIds[0] ?? null,
590
+ totalRowCount: snapshot.totalRowCount,
591
+ totalHeight: renderSnapshot.totalHeight,
592
+ visibleRowCount: viewportRows.length,
593
+ visibleRowRange: firstVisibleRow && lastVisibleRow ? {
594
+ start: firstVisibleRow.rowIndex,
595
+ end: lastVisibleRow.rowIndex + 1
596
+ } : {
597
+ start: 0,
598
+ end: 0
599
+ }
600
+ };
601
+ }, [
602
+ renderSnapshot.rows,
603
+ renderSnapshot.totalHeight,
604
+ snapshot.focus.rowId,
605
+ snapshot.visibleRows.length,
606
+ snapshot.selection.rowIds,
607
+ snapshot.totalRowCount,
608
+ snapshot.viewport.height,
609
+ snapshot.viewport.scrollTop,
610
+ viewportHeight
611
+ ]);
612
+ return {
613
+ grid,
614
+ snapshot,
615
+ renderSnapshot,
616
+ telemetry
617
+ };
618
+ }
619
+
620
+ // src/rendering.ts
621
+ var DEFAULT_ROW_HEIGHT2 = 44;
622
+ var DEFAULT_WRAPPED_COLUMN_WIDTH = 220;
623
+ var DEFAULT_FIXED_COLUMN_WIDTH = 140;
624
+ var HEADER_HEIGHT = 52;
625
+ function getColumnWidth2(column) {
626
+ return column.widthPx ?? (column.wrap ? DEFAULT_WRAPPED_COLUMN_WIDTH : DEFAULT_FIXED_COLUMN_WIDTH);
627
+ }
628
+ function getPinnedLeftOffsets(columns) {
629
+ const offsets = {};
630
+ let left = 0;
631
+ for (const column of columns) {
632
+ if (column.pinned !== "left") {
633
+ continue;
634
+ }
635
+ offsets[column.id] = left;
636
+ left += getColumnWidth2(column);
637
+ }
638
+ return offsets;
639
+ }
640
+ function getNextSortDirection(current) {
641
+ if (current === null) {
642
+ return "desc";
643
+ }
644
+ if (current === "desc") {
645
+ return "asc";
646
+ }
647
+ return null;
648
+ }
649
+ function resolveCellValue(row, column) {
650
+ return column.getValue ? column.getValue(row) : row[column.id];
651
+ }
652
+ function formatCellValue(value) {
653
+ if (Array.isArray(value)) {
654
+ return value.join(", ");
655
+ }
656
+ return String(value ?? "");
657
+ }
658
+
659
+ // src/density.ts
660
+ var FALLBACK_ROW_HEIGHT = 32;
661
+ var FALLBACK_HEADER_HEIGHT = HEADER_HEIGHT;
662
+ function parsePx(value) {
663
+ const match = value.trim().match(/^([\d.]+)px$/);
664
+ return match ? parseFloat(match[1]) : null;
665
+ }
666
+ function getDensityHeights() {
667
+ if (typeof document === "undefined") {
668
+ return {
669
+ rowHeight: FALLBACK_ROW_HEIGHT,
670
+ headerHeight: FALLBACK_HEADER_HEIGHT
671
+ };
672
+ }
673
+ const styles = getComputedStyle(document.documentElement);
674
+ const read = (name) => {
675
+ if (typeof styles?.getPropertyValue !== "function") return "";
676
+ return styles.getPropertyValue(name);
677
+ };
678
+ return {
679
+ rowHeight: parsePx(read("--pretable-row-height")) ?? FALLBACK_ROW_HEIGHT,
680
+ headerHeight: parsePx(read("--pretable-header-height")) ?? FALLBACK_HEADER_HEIGHT
681
+ };
682
+ }
683
+ function subscribe(callback) {
684
+ if (typeof document === "undefined") return () => {
685
+ };
686
+ const observer = new MutationObserver(callback);
687
+ observer.observe(document.documentElement, {
688
+ attributes: true,
689
+ attributeFilter: ["data-density", "data-theme", "class", "style"]
690
+ });
691
+ return () => observer.disconnect();
692
+ }
693
+ function useResolvedHeights(rowHeightProp, headerHeightProp) {
694
+ const cachedClient = react.useRef(null);
695
+ const cachedServer = react.useRef(null);
696
+ const getSnapshot = react.useCallback(() => {
697
+ const css = getDensityHeights();
698
+ const rowHeight = rowHeightProp ?? css.rowHeight;
699
+ const headerHeight = headerHeightProp ?? css.headerHeight;
700
+ const prev = cachedClient.current;
701
+ if (prev !== null && prev.rowHeight === rowHeight && prev.headerHeight === headerHeight) {
702
+ return prev;
703
+ }
704
+ const next = { rowHeight, headerHeight };
705
+ cachedClient.current = next;
706
+ return next;
707
+ }, [rowHeightProp, headerHeightProp]);
708
+ const getServerSnapshot = react.useCallback(() => {
709
+ const rowHeight = rowHeightProp ?? FALLBACK_ROW_HEIGHT;
710
+ const headerHeight = headerHeightProp ?? FALLBACK_HEADER_HEIGHT;
711
+ const prev = cachedServer.current;
712
+ if (prev !== null && prev.rowHeight === rowHeight && prev.headerHeight === headerHeight) {
713
+ return prev;
714
+ }
715
+ const next = { rowHeight, headerHeight };
716
+ cachedServer.current = next;
717
+ return next;
718
+ }, [rowHeightProp, headerHeightProp]);
719
+ return react.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
720
+ }
721
+
722
+ // src/styles.ts
723
+ function getViewportStyle(height) {
724
+ return {
725
+ contain: "content",
726
+ containIntrinsicSize: `auto ${height}px`,
727
+ contentVisibility: "auto",
728
+ height,
729
+ overflow: "auto",
730
+ overflowAnchor: "none",
731
+ overscrollBehavior: "contain",
732
+ position: "relative"
733
+ };
734
+ }
735
+ function getHeaderRowStyle(totalWidth, headerHeight = HEADER_HEIGHT) {
736
+ return {
737
+ display: "flex",
738
+ height: headerHeight,
739
+ insetInline: 0,
740
+ minWidth: totalWidth,
741
+ position: "sticky",
742
+ top: 0,
743
+ zIndex: 3
744
+ };
745
+ }
746
+ function getScrollContentStyle(totalHeight, totalWidth) {
747
+ return {
748
+ height: Math.max(totalHeight, 0),
749
+ minWidth: totalWidth,
750
+ position: "relative"
751
+ };
752
+ }
753
+ function getRowStyle(top, height) {
754
+ return {
755
+ boxSizing: "border-box",
756
+ display: "flex",
757
+ height,
758
+ insetInline: 0,
759
+ position: "absolute",
760
+ top
761
+ };
762
+ }
763
+ function getCellStyle(left, width) {
764
+ return {
765
+ boxSizing: "border-box",
766
+ height: "100%",
767
+ left,
768
+ position: "absolute",
769
+ top: 0,
770
+ width
771
+ };
772
+ }
773
+ function getHeaderCellStyle(left, width) {
774
+ return {
775
+ boxSizing: "border-box",
776
+ height: "100%",
777
+ left,
778
+ position: "absolute",
779
+ top: 0,
780
+ width
781
+ };
782
+ }
783
+ function getPinnedCellStyle(left) {
784
+ return {
785
+ left,
786
+ position: "sticky",
787
+ top: 0,
788
+ zIndex: 1
789
+ };
790
+ }
791
+ function PretableSurface({
792
+ ariaLabel,
793
+ autosize,
794
+ columns,
795
+ getBodyCellClassName,
796
+ getBodyCellProps,
797
+ getHeaderCellClassName,
798
+ getHeaderCellProps,
799
+ getRowClassName,
800
+ getRowId,
801
+ getRowProps,
802
+ interactionState,
803
+ overscan = 6,
804
+ onGridReady,
805
+ onSelectedRowIdChange,
806
+ onSortChange,
807
+ onTelemetryChange,
808
+ renderBodyCell,
809
+ renderHeaderCell,
810
+ rows,
811
+ selectFocusedRowOnArrowKey = false,
812
+ viewportStyle,
813
+ viewportHeight
814
+ }) {
815
+ const [measuredHeights, setMeasuredHeights] = react.useState({});
816
+ const [viewportWidth, setViewportWidth] = react.useState(0);
817
+ const measuredHeightsRef = react.useRef({});
818
+ const measuredRowKeysRef = react.useRef({});
819
+ const rowNodesRef = react.useRef(/* @__PURE__ */ new Map());
820
+ const viewportRef = react.useRef(null);
821
+ const { headerHeight } = useResolvedHeights();
822
+ const bodyViewportHeight = Math.max(viewportHeight - headerHeight, 0);
823
+ const { grid, snapshot, renderSnapshot, telemetry } = usePretableModel({
824
+ autosize,
825
+ columns,
826
+ getRowId,
827
+ interactionOverrides: interactionState ?? void 0,
828
+ measuredHeights,
829
+ overscan,
830
+ rows,
831
+ viewportHeight: bodyViewportHeight,
832
+ viewportWidth: viewportWidth || void 0
833
+ });
834
+ const pinnedOffsets = react.useMemo(() => getPinnedLeftOffsets(columns), [columns]);
835
+ react.useLayoutEffect(() => {
836
+ const el = viewportRef.current;
837
+ if (el && viewportWidth === 0) {
838
+ setViewportWidth(el.clientWidth);
839
+ }
840
+ });
841
+ react.useLayoutEffect(() => {
842
+ onTelemetryChange?.(telemetry);
843
+ }, [onTelemetryChange, telemetry]);
844
+ react.useLayoutEffect(() => {
845
+ onGridReady?.(grid);
846
+ }, [grid, onGridReady]);
847
+ react.useLayoutEffect(() => {
848
+ if (!interactionState?.selectedRowId) {
849
+ return;
850
+ }
851
+ const currentSelectedRowId = snapshot.selection.rowIds[0] ?? null;
852
+ if (currentSelectedRowId !== interactionState.selectedRowId) {
853
+ onSelectedRowIdChange?.(interactionState.selectedRowId);
854
+ }
855
+ }, [interactionState, onSelectedRowIdChange, snapshot.selection.rowIds]);
856
+ react.useLayoutEffect(() => {
857
+ let nextHeights = measuredHeightsRef.current;
858
+ let nextKeys = measuredRowKeysRef.current;
859
+ let changed = false;
860
+ for (const [rowId, node] of rowNodesRef.current) {
861
+ const plannedHeight = Number(node.getAttribute("data-row-height"));
862
+ const cachedHeight = nextHeights[rowId];
863
+ const currentRowKey = getRowMeasurementKey(node);
864
+ const cachedRowKey = nextKeys[rowId];
865
+ if (Number.isFinite(plannedHeight) && cachedHeight !== void 0 && cachedHeight === plannedHeight && cachedRowKey === currentRowKey) {
866
+ continue;
867
+ }
868
+ const measuredHeight = measureRenderedRowHeight(node);
869
+ if (measuredHeight <= DEFAULT_ROW_HEIGHT2) {
870
+ if (cachedHeight !== void 0 && cachedRowKey !== currentRowKey) {
871
+ const restHeights = { ...nextHeights };
872
+ delete restHeights[rowId];
873
+ const restKeys = { ...nextKeys };
874
+ delete restKeys[rowId];
875
+ nextHeights = restHeights;
876
+ nextKeys = restKeys;
877
+ changed = true;
878
+ }
879
+ continue;
880
+ }
881
+ if (nextHeights[rowId] === measuredHeight) {
882
+ if (cachedRowKey !== currentRowKey) {
883
+ nextKeys = { ...nextKeys, [rowId]: currentRowKey };
884
+ }
885
+ continue;
886
+ }
887
+ nextHeights = { ...nextHeights, [rowId]: measuredHeight };
888
+ nextKeys = { ...nextKeys, [rowId]: currentRowKey };
889
+ changed = true;
890
+ }
891
+ measuredHeightsRef.current = nextHeights;
892
+ measuredRowKeysRef.current = nextKeys;
893
+ if (changed) {
894
+ setMeasuredHeights(nextHeights);
895
+ }
896
+ }, [snapshot.visibleRows, columns, viewportWidth]);
897
+ return /* @__PURE__ */ jsxRuntime.jsxs(
898
+ "div",
899
+ {
900
+ "aria-label": ariaLabel,
901
+ "data-pretable-scroll-viewport": "",
902
+ ref: viewportRef,
903
+ role: "grid",
904
+ tabIndex: 0,
905
+ onKeyDown: (event) => {
906
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
907
+ grid.moveFocus(event.key === "ArrowDown" ? 1 : -1);
908
+ const nextFocus = grid.getSnapshot().focus;
909
+ if (nextFocus.rowId && nextFocus.columnId === null && columns[0]) {
910
+ grid.setFocus(nextFocus.rowId, columns[0].id);
911
+ }
912
+ if (selectFocusedRowOnArrowKey && nextFocus.rowId) {
913
+ grid.selectRow(nextFocus.rowId);
914
+ onSelectedRowIdChange?.(nextFocus.rowId);
915
+ }
916
+ event.preventDefault();
917
+ return;
918
+ }
919
+ if (event.key === "Enter" || event.key === " " || event.key === "Space") {
920
+ const focusedRowId = grid.getSnapshot().focus.rowId;
921
+ if (focusedRowId) {
922
+ grid.selectRow(focusedRowId);
923
+ onSelectedRowIdChange?.(focusedRowId);
924
+ event.preventDefault();
925
+ }
926
+ }
927
+ },
928
+ onScroll: (event) => {
929
+ const el = event.currentTarget;
930
+ grid.setViewport({
931
+ scrollTop: el.scrollTop,
932
+ scrollLeft: el.scrollLeft,
933
+ height: bodyViewportHeight,
934
+ width: el.clientWidth
935
+ });
936
+ if (el.clientWidth !== viewportWidth) {
937
+ setViewportWidth(el.clientWidth);
938
+ }
939
+ },
940
+ style: {
941
+ ...getViewportStyle(viewportHeight),
942
+ ...viewportStyle
943
+ },
944
+ children: [
945
+ /* @__PURE__ */ jsxRuntime.jsx(
946
+ "div",
947
+ {
948
+ "data-pretable-header-row": "",
949
+ style: getHeaderRowStyle(renderSnapshot.totalWidth, headerHeight),
950
+ children: renderSnapshot.columns.map((plannedCol) => {
951
+ const column = columns[plannedCol.index];
952
+ if (!column) {
953
+ return null;
954
+ }
955
+ const label = column.header ?? column.id;
956
+ const sortDirection = snapshot.sort.columnId === column.id ? snapshot.sort.direction : null;
957
+ const headerProps = getHeaderCellProps?.({
958
+ column,
959
+ sortDirection
960
+ }) ?? {};
961
+ const pinnedOffset = pinnedOffsets[column.id];
962
+ const positionStyle = plannedCol.pinned === "left" && pinnedOffset !== void 0 ? {
963
+ ...getHeaderCellStyle(plannedCol.left, plannedCol.width),
964
+ ...getPinnedCellStyle(pinnedOffset)
965
+ } : getHeaderCellStyle(plannedCol.left, plannedCol.width);
966
+ return /* @__PURE__ */ react.createElement(
967
+ "button",
968
+ {
969
+ ...headerProps,
970
+ "aria-label": `Sort ${label}`,
971
+ className: getHeaderCellClassName?.({
972
+ column,
973
+ sortDirection
974
+ }),
975
+ "data-pretable-header-cell": "",
976
+ "data-pinned": plannedCol.pinned === "left" ? "left" : void 0,
977
+ key: column.id,
978
+ onClick: () => {
979
+ const nextDirection = getNextSortDirection(sortDirection);
980
+ grid.setSort(column.id, nextDirection);
981
+ if (nextDirection) {
982
+ onSortChange?.({
983
+ columnId: column.id,
984
+ direction: nextDirection
985
+ });
986
+ } else {
987
+ onSortChange?.(null);
988
+ }
989
+ },
990
+ style: {
991
+ alignItems: "start",
992
+ border: 0,
993
+ borderRight: "1px solid rgba(255, 255, 255, 0.06)",
994
+ color: "inherit",
995
+ display: "grid",
996
+ gap: 4,
997
+ textAlign: "left",
998
+ ...positionStyle
999
+ },
1000
+ type: "button"
1001
+ },
1002
+ renderHeaderCell ? renderHeaderCell({
1003
+ column,
1004
+ label,
1005
+ sortDirection
1006
+ }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1007
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
1008
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: sortDirection === "desc" ? "Newest" : sortDirection === "asc" ? "Oldest" : "Sort" })
1009
+ ] })
1010
+ );
1011
+ })
1012
+ }
1013
+ ),
1014
+ /* @__PURE__ */ jsxRuntime.jsx(
1015
+ "div",
1016
+ {
1017
+ "data-pretable-scroll-content": "",
1018
+ style: getScrollContentStyle(
1019
+ renderSnapshot.totalHeight,
1020
+ renderSnapshot.totalWidth
1021
+ ),
1022
+ children: renderSnapshot.rows.map(({ height, id, row, rowIndex, top }) => {
1023
+ const isFocused = snapshot.focus.rowId === id;
1024
+ const isSelected = snapshot.selection.rowIds.includes(id);
1025
+ const rowProps = getRowProps?.({
1026
+ isFocused,
1027
+ isSelected,
1028
+ row,
1029
+ rowId: id,
1030
+ rowIndex
1031
+ }) ?? {};
1032
+ return /* @__PURE__ */ react.createElement(
1033
+ "div",
1034
+ {
1035
+ ...rowProps,
1036
+ "aria-rowindex": rowIndex + 1,
1037
+ "aria-selected": isSelected,
1038
+ className: getRowClassName?.({
1039
+ isFocused,
1040
+ isSelected,
1041
+ row,
1042
+ rowId: id,
1043
+ rowIndex
1044
+ }),
1045
+ "data-focused": isFocused ? "true" : "false",
1046
+ "data-pretable-row": "",
1047
+ "data-row-height": height,
1048
+ "data-row-id": id,
1049
+ "data-row-index": rowIndex,
1050
+ "data-selected": isSelected ? "true" : "false",
1051
+ "data-testid": "pretable-row",
1052
+ key: id,
1053
+ onClick: () => {
1054
+ grid.setFocus(id, columns[0]?.id ?? null);
1055
+ grid.selectRow(id);
1056
+ onSelectedRowIdChange?.(id);
1057
+ },
1058
+ ref: (node) => {
1059
+ if (node) {
1060
+ rowNodesRef.current.set(id, node);
1061
+ } else {
1062
+ rowNodesRef.current.delete(id);
1063
+ }
1064
+ },
1065
+ style: getRowStyle(top, height)
1066
+ },
1067
+ renderSnapshot.columns.map((plannedCol) => {
1068
+ const column = columns[plannedCol.index];
1069
+ if (!column) {
1070
+ return null;
1071
+ }
1072
+ const value = resolveCellValue(row, column);
1073
+ const bodyInput = {
1074
+ column,
1075
+ isFocused,
1076
+ isSelected,
1077
+ row,
1078
+ rowId: id,
1079
+ rowIndex,
1080
+ value
1081
+ };
1082
+ const bodyProps = getBodyCellProps?.(bodyInput) ?? {};
1083
+ const pinnedOffset = pinnedOffsets[column.id];
1084
+ const positionStyle = plannedCol.pinned === "left" && pinnedOffset !== void 0 ? {
1085
+ ...getCellStyle(plannedCol.left, plannedCol.width),
1086
+ ...getPinnedCellStyle(pinnedOffset)
1087
+ } : getCellStyle(plannedCol.left, plannedCol.width);
1088
+ return /* @__PURE__ */ react.createElement(
1089
+ "div",
1090
+ {
1091
+ ...bodyProps,
1092
+ className: getBodyCellClassName?.(bodyInput),
1093
+ "data-column-id": column.id,
1094
+ "data-focused": isFocused ? "true" : "false",
1095
+ "data-pinned": column.pinned === "left" ? "left" : void 0,
1096
+ "data-pretable-cell": "",
1097
+ "data-pretable-wrap": column.wrap ? "true" : void 0,
1098
+ "data-selected": isSelected ? "true" : "false",
1099
+ key: `${id}:${column.id}`,
1100
+ style: {
1101
+ overflowWrap: column.wrap ? "anywhere" : "normal",
1102
+ whiteSpace: column.wrap ? "pre-wrap" : "nowrap",
1103
+ ...positionStyle
1104
+ }
1105
+ },
1106
+ renderBodyCell ? renderBodyCell(bodyInput) : formatCellValue(value)
1107
+ );
1108
+ })
1109
+ );
1110
+ })
1111
+ }
1112
+ )
1113
+ ]
1114
+ }
1115
+ );
1116
+ }
1117
+ function getRowMeasurementKey(rowNode) {
1118
+ const rowParts = [
1119
+ rowNode.getAttribute("class") ?? "",
1120
+ normalizeStyleSignature(rowNode.getAttribute("style") ?? ""),
1121
+ rowNode.getAttribute("aria-selected") ?? "",
1122
+ rowNode.getAttribute("data-focused") ?? "",
1123
+ rowNode.getAttribute("data-selected") ?? ""
1124
+ ];
1125
+ const cellParts = [
1126
+ ...rowNode.querySelectorAll("[data-pretable-cell]")
1127
+ ].map(
1128
+ (cell) => [
1129
+ cell.getAttribute("data-column-id") ?? "",
1130
+ cell.getAttribute("class") ?? "",
1131
+ cell.getAttribute("style") ?? "",
1132
+ cell.getAttribute("data-pretable-wrap") ?? "",
1133
+ cell.getAttribute("data-focused") ?? "",
1134
+ cell.getAttribute("data-selected") ?? "",
1135
+ cell.textContent ?? ""
1136
+ ].join(":")
1137
+ );
1138
+ return [...rowParts, ...cellParts].join("|");
1139
+ }
1140
+ function normalizeStyleSignature(styleValue) {
1141
+ return styleValue.split(";").map((declaration) => declaration.trim()).filter(Boolean).filter((declaration) => !/^top\s*:/i.test(declaration)).join(";");
1142
+ }
1143
+ var VIEWPORT_HEIGHT = 320;
1144
+ var BENCHMARK_VIEWPORT_STYLE = {
1145
+ contain: "none",
1146
+ containIntrinsicSize: "none",
1147
+ contentVisibility: "visible",
1148
+ overflowAnchor: "none",
1149
+ overscrollBehavior: "contain"
1150
+ };
1151
+ function Pretable({
1152
+ columns,
1153
+ getRowId,
1154
+ rows
1155
+ }) {
1156
+ const resolvedGetRowId = getRowId ?? ((row, index) => {
1157
+ const candidate = row.id;
1158
+ if (typeof candidate === "string" || typeof candidate === "number") {
1159
+ return String(candidate);
1160
+ }
1161
+ return String(index);
1162
+ });
1163
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1164
+ "section",
1165
+ {
1166
+ "aria-label": "Pretable React adapter",
1167
+ style: {
1168
+ display: "grid",
1169
+ gap: 12
1170
+ },
1171
+ children: [
1172
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { children: [
1173
+ /* @__PURE__ */ jsxRuntime.jsx(
1174
+ "p",
1175
+ {
1176
+ style: {
1177
+ margin: 0,
1178
+ fontWeight: 700
1179
+ },
1180
+ children: "Pretable React adapter"
1181
+ }
1182
+ ),
1183
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { margin: "4px 0 0", opacity: 0.8 }, children: [
1184
+ "Rows: ",
1185
+ rows.length
1186
+ ] }),
1187
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { margin: "4px 0 0", opacity: 0.8 }, children: [
1188
+ "Columns: ",
1189
+ columns.length
1190
+ ] })
1191
+ ] }),
1192
+ /* @__PURE__ */ jsxRuntime.jsx(
1193
+ PretableSurface,
1194
+ {
1195
+ ariaLabel: "Pretable React adapter",
1196
+ columns,
1197
+ getRowId: resolvedGetRowId,
1198
+ renderBodyCell: ({ column, value }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1199
+ /* @__PURE__ */ jsxRuntime.jsx(
1200
+ "strong",
1201
+ {
1202
+ style: {
1203
+ display: "block",
1204
+ fontSize: 12,
1205
+ lineHeight: "16px",
1206
+ marginBottom: 4,
1207
+ opacity: 0.7
1208
+ },
1209
+ children: column.header ?? column.id
1210
+ }
1211
+ ),
1212
+ /* @__PURE__ */ jsxRuntime.jsx(
1213
+ "span",
1214
+ {
1215
+ style: {
1216
+ display: "block",
1217
+ lineHeight: "22px"
1218
+ },
1219
+ children: String(value ?? "")
1220
+ }
1221
+ )
1222
+ ] }),
1223
+ renderHeaderCell: ({ label, sortDirection }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1224
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
1225
+ /* @__PURE__ */ jsxRuntime.jsx(
1226
+ "strong",
1227
+ {
1228
+ style: {
1229
+ fontSize: 12,
1230
+ lineHeight: "16px",
1231
+ opacity: 0.7
1232
+ },
1233
+ children: sortDirection === "desc" ? "Newest" : sortDirection === "asc" ? "Oldest" : "Sort"
1234
+ }
1235
+ )
1236
+ ] }),
1237
+ rows,
1238
+ viewportStyle: BENCHMARK_VIEWPORT_STYLE,
1239
+ viewportHeight: VIEWPORT_HEIGHT
1240
+ }
1241
+ )
1242
+ ]
1243
+ }
1244
+ );
1245
+ }
1246
+
1247
+ // ../scenario-data/dist/inspection-profile.js
1248
+ var inspectionColumns = [
1249
+ { id: "timestamp", header: "Timestamp", pinned: "left", widthPx: 188 },
1250
+ { id: "severity", header: "Severity", pinned: "left", widthPx: 112 },
1251
+ { id: "source", header: "Source", widthPx: 160 },
1252
+ { id: "owner", header: "Owner", widthPx: 144 },
1253
+ {
1254
+ id: "tags",
1255
+ header: "Tags",
1256
+ widthPx: 200,
1257
+ getValue: (row) => row.tags.join(", ")
1258
+ },
1259
+ { id: "message", header: "Message", wrap: true, widthPx: 480 }
1260
+ ];
1261
+ function LabeledGridSurface({
1262
+ ariaLabel,
1263
+ bodyCellClassName,
1264
+ columns,
1265
+ formatValue,
1266
+ getBodyCellProps,
1267
+ getHeaderCellProps,
1268
+ getRowId,
1269
+ headerCellClassName,
1270
+ interactionState,
1271
+ labelClassName,
1272
+ overscan,
1273
+ onSelectedRowIdChange,
1274
+ onSortChange,
1275
+ onTelemetryChange,
1276
+ pinnedClassName,
1277
+ rowClassName,
1278
+ rows,
1279
+ selectFocusedRowOnArrowKey,
1280
+ valueClassName,
1281
+ viewportHeight
1282
+ }) {
1283
+ const getPinnedClassName = (column) => column.pinned === "left" && pinnedClassName ? pinnedClassName : void 0;
1284
+ const activeFilterColumns = new Set(
1285
+ Object.entries(interactionState?.filters ?? {}).filter(([, value]) => value.trim() !== "").map(([columnId]) => columnId)
1286
+ );
1287
+ const getFormattedValue = ({
1288
+ column,
1289
+ row,
1290
+ value
1291
+ }) => formatValue ? formatValue({ column, row, value }) : formatDefaultValue(value);
1292
+ return /* @__PURE__ */ jsxRuntime.jsx(
1293
+ PretableSurface,
1294
+ {
1295
+ ariaLabel,
1296
+ columns,
1297
+ getBodyCellClassName: ({ column }) => joinClassNames(bodyCellClassName, getPinnedClassName(column)),
1298
+ getBodyCellProps: (input) => mergeProps(
1299
+ input.column.pinned === "left" ? {
1300
+ "data-pinned": "left"
1301
+ } : void 0,
1302
+ getBodyCellProps?.(input)
1303
+ ),
1304
+ getHeaderCellClassName: ({ column }) => joinClassNames(
1305
+ headerCellClassName,
1306
+ getPinnedClassName(column),
1307
+ activeFilterColumns.has(column.id) ? "is-filtered" : void 0
1308
+ ),
1309
+ getHeaderCellProps: (input) => mergeProps(
1310
+ input.column.pinned === "left" ? {
1311
+ "data-pinned": "left"
1312
+ } : void 0,
1313
+ getHeaderCellProps?.(input)
1314
+ ),
1315
+ getRowClassName: () => rowClassName,
1316
+ getRowId,
1317
+ interactionState,
1318
+ overscan,
1319
+ onSelectedRowIdChange,
1320
+ onSortChange,
1321
+ onTelemetryChange,
1322
+ renderBodyCell: ({ column, row, value }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1323
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: labelClassName, children: column.header ?? column.id }),
1324
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: valueClassName, children: getFormattedValue({
1325
+ column,
1326
+ row,
1327
+ value
1328
+ }) })
1329
+ ] }),
1330
+ renderHeaderCell: ({ label, sortDirection }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1331
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
1332
+ sortDirection ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sort-indicator", children: sortDirection === "desc" ? "\u25BC" : "\u25B2" }) : null
1333
+ ] }),
1334
+ rows,
1335
+ selectFocusedRowOnArrowKey,
1336
+ viewportHeight
1337
+ }
1338
+ );
1339
+ }
1340
+ function joinClassNames(...values) {
1341
+ return values.filter(Boolean).join(" ") || void 0;
1342
+ }
1343
+ function mergeProps(base, extra) {
1344
+ if (!base) {
1345
+ return extra;
1346
+ }
1347
+ if (!extra) {
1348
+ return base;
1349
+ }
1350
+ return {
1351
+ ...base,
1352
+ ...extra
1353
+ };
1354
+ }
1355
+ function formatDefaultValue(value) {
1356
+ if (Array.isArray(value)) {
1357
+ return value.join(", ");
1358
+ }
1359
+ return String(value ?? "");
1360
+ }
1361
+ var inspectionGridColumns = [...inspectionColumns];
1362
+ var getInspectionRowId = (row) => row.id;
1363
+ var filterableBodyProps = {
1364
+ "data-filterable": "true"
1365
+ };
1366
+ var filterableHeaderProps = {
1367
+ "data-filterable": "true"
1368
+ };
1369
+ function InspectionGrid({
1370
+ ariaLabel,
1371
+ filterableColumnIds,
1372
+ interactionState,
1373
+ onSelectedRowIdChange,
1374
+ onSortChange,
1375
+ onTelemetryChange,
1376
+ overscan,
1377
+ rows,
1378
+ viewportHeight
1379
+ }) {
1380
+ const filterableColumns = new Set(filterableColumnIds);
1381
+ return /* @__PURE__ */ jsxRuntime.jsx(
1382
+ LabeledGridSurface,
1383
+ {
1384
+ ariaLabel,
1385
+ bodyCellClassName: "inspection-cell",
1386
+ columns: inspectionGridColumns,
1387
+ formatValue: ({ value }) => formatInspectionValue(value),
1388
+ getBodyCellProps: ({ column }) => filterableColumns.has(column.id) ? filterableBodyProps : void 0,
1389
+ getHeaderCellProps: ({ column }) => filterableColumns.has(column.id) ? filterableHeaderProps : void 0,
1390
+ getRowId: getInspectionRowId,
1391
+ headerCellClassName: "inspection-header-cell",
1392
+ interactionState,
1393
+ labelClassName: "inspection-cell-label",
1394
+ overscan,
1395
+ onSelectedRowIdChange,
1396
+ onSortChange,
1397
+ onTelemetryChange,
1398
+ pinnedClassName: "is-pinned",
1399
+ rowClassName: "inspection-row",
1400
+ rows,
1401
+ selectFocusedRowOnArrowKey: true,
1402
+ valueClassName: "inspection-cell-value",
1403
+ viewportHeight
1404
+ }
1405
+ );
1406
+ }
1407
+ function formatInspectionValue(value) {
1408
+ if (Array.isArray(value)) {
1409
+ return value.join(", ");
1410
+ }
1411
+ return String(value ?? "");
1412
+ }
1413
+
1414
+ exports.InspectionGrid = InspectionGrid;
1415
+ exports.LabeledGridSurface = LabeledGridSurface;
1416
+ exports.Pretable = Pretable;
1417
+ exports.PretableSurface = PretableSurface;
1418
+ exports.measureRenderedRowHeight = measureRenderedRowHeight;
1419
+ exports.usePretable = usePretable;
1420
+ exports.usePretableModel = usePretableModel;
1421
+ exports.useResolvedHeights = useResolvedHeights;