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