@svgrid/grid 3.0.7 → 3.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/GridMenus.svelte +3 -2
- package/dist/SvGrid.controller.svelte.d.ts +60 -8
- package/dist/SvGrid.controller.svelte.js +490 -114
- package/dist/SvGrid.css +186 -3
- package/dist/SvGrid.svelte +285 -164
- package/dist/SvGrid.types.d.ts +24 -7
- package/dist/cdn/{GridMenus-CcwU8I4t.js → GridMenus-B1sipj6y.js} +165 -165
- package/dist/cdn/{GridMenus-BgUS-W3o.js → GridMenus-DlAzfHLk.js} +201 -201
- package/dist/cdn/server-block-cache-Dj-KPqEQ.js +1289 -0
- package/dist/cdn/{src-kE9B98r6.js → src-BYsa66I0.js} +9747 -10178
- package/dist/cdn/{src-25JmJ_7g.js → src-pA4RroLV.js} +10709 -11140
- package/dist/cdn/svgrid.js +27 -27
- package/dist/cdn/svgrid.svelte-external.js +27 -27
- package/dist/cdn/validate-XuMe4_KR.js +80 -0
- package/dist/cell-render.js +25 -3
- package/dist/columns.js +16 -1
- package/dist/conditional-formatting.js +9 -2
- package/dist/core.d.ts +93 -0
- package/dist/core.js +313 -47
- package/dist/filtering/excel-filters.js +41 -6
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/keyboard-handlers.js +5 -1
- package/dist/selection.d.ts +2 -2
- package/dist/selection.js +46 -12
- package/dist/server-block-cache.d.ts +7 -32
- package/dist/server-block-cache.js +21 -12
- package/dist/server-data-source.js +3 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.js +1 -0
- package/dist/spreadsheet.js +7 -0
- package/dist/{subscribe.js → subscribe.svelte.js} +5 -1
- package/dist/validate.js +13 -14
- package/dist/virtualization/column-virtualizer.d.ts +4 -0
- package/dist/virtualization/column-virtualizer.js +2 -0
- package/dist/virtualization/types.d.ts +20 -0
- package/dist/virtualization/virtualizer.js +90 -24
- package/dist/windowed-brand.d.ts +22 -0
- package/dist/windowed-brand.js +60 -0
- package/dist/windowed-data.d.ts +6 -0
- package/dist/windowed-data.js +34 -0
- package/dist/windowed-row-model.d.ts +1 -0
- package/dist/windowed-row-model.js +97 -0
- package/package.json +1 -1
- package/src/GridMenus.svelte +3 -2
- package/src/SvGrid.controller.svelte.ts +483 -122
- package/src/SvGrid.css +186 -3
- package/src/SvGrid.svelte +285 -164
- package/src/SvGrid.types.ts +24 -7
- package/src/cell-render.ts +24 -2
- package/src/columns.ts +16 -1
- package/src/conditional-formatting.ts +6 -4
- package/src/core.sort.test.ts +61 -0
- package/src/core.ts +309 -49
- package/src/counting-cell.test.svelte +11 -0
- package/src/filtering/excel-filters.ts +39 -6
- package/src/headless.reactivity.svelte.test.ts +89 -0
- package/src/headless.ts +1 -1
- package/src/index.ts +7 -1
- package/src/keyboard-handlers.ts +5 -1
- package/src/new-features.test.ts +24 -0
- package/src/selection.test.ts +51 -0
- package/src/selection.ts +47 -14
- package/src/server-block-cache.ts +27 -10
- package/src/server-data-source.ts +3 -1
- package/src/server.ts +6 -0
- package/src/spreadsheet.ts +6 -0
- package/src/{subscribe.test.ts → subscribe.svelte.test.ts} +1 -1
- package/src/{subscribe.ts → subscribe.svelte.ts} +5 -1
- package/src/svgrid.behavior.test.ts +58 -0
- package/src/svgrid.column-cell-recycling.test.ts +120 -0
- package/src/svgrid.interaction.test.ts +4 -1
- package/src/svgrid.new-features.wrapper.test.ts +5 -4
- package/src/svgrid.pinned-column-virtualization.test.ts +162 -0
- package/src/svgrid.row-model-prop.svelte.test.ts +15 -3
- package/src/validate.test.ts +20 -4
- package/src/validate.ts +13 -16
- package/src/virtualization/column-virtualizer.ts +6 -0
- package/src/virtualization/types.ts +20 -0
- package/src/virtualization/virtualizer.test.ts +152 -0
- package/src/virtualization/virtualizer.ts +93 -27
- package/src/windowed-brand.ts +67 -0
- package/src/windowed-data.test.ts +105 -0
- package/src/windowed-data.ts +36 -0
- package/src/windowed-row-model.ts +113 -0
- package/dist/cdn/server-block-cache-DIqb3VZn.js +0 -241
- package/dist/cdn/validate-AZoD8BoR.js +0 -80
- /package/dist/{subscribe.d.ts → subscribe.svelte.d.ts} +0 -0
package/src/SvGrid.svelte
CHANGED
|
@@ -466,6 +466,9 @@
|
|
|
466
466
|
const measuredRowHeightPx = $derived(ctrl.measuredRowHeightPx);
|
|
467
467
|
// Under auto height the row must be free to grow, so it gets `min-height`
|
|
468
468
|
// (the pre-measure estimate) instead of a fixed `height` that would clip it.
|
|
469
|
+
// Otherwise the row is exactly the height the virtualizer placed it at: a
|
|
470
|
+
// flex row's `height` is exact. (A table row's is a minimum, so under the
|
|
471
|
+
// table layout content taller than the row still grows it.)
|
|
469
472
|
const rowHeightStyle = (size: number): string =>
|
|
470
473
|
autoRowHeightOn ? `min-height: ${size}px;` : `height: ${size}px;`;
|
|
471
474
|
// DOM-space spacer heights + capped total: identical to the logical
|
|
@@ -476,34 +479,40 @@
|
|
|
476
479
|
const rowBottomSpacer = $derived(ctrl.rowBottomSpacer);
|
|
477
480
|
const rowDomTotalSize = $derived(ctrl.rowDomTotalSize);
|
|
478
481
|
const renderedColumns = $derived(ctrl.renderedColumns);
|
|
482
|
+
const columnRunOf = $derived(ctrl.columnRunOf);
|
|
479
483
|
const totalColumnWidth = $derived(ctrl.totalColumnWidth);
|
|
480
484
|
|
|
481
485
|
// ---- Built-in cell flash (ColumnDef `cellFlash`) ----------------------
|
|
482
486
|
// Flashes a cell when its value changes (edits, streaming feeds, server
|
|
483
|
-
// pushes). Keyed by rowId so virtualization recycling a <td>
|
|
484
|
-
// on scroll does NOT trigger a spurious flash - only
|
|
485
|
-
// does. Inert (active:false) for columns without
|
|
487
|
+
// pushes). Keyed by rowId and columnId so virtualization recycling a <td>
|
|
488
|
+
// into a new row or column on scroll does NOT trigger a spurious flash - only
|
|
489
|
+
// a same-cell value change does. Inert (active:false) for columns without
|
|
490
|
+
// `cellFlash`.
|
|
486
491
|
function cellFlashAction(
|
|
487
492
|
node: HTMLElement,
|
|
488
493
|
params: {
|
|
489
494
|
rowId: string;
|
|
495
|
+
columnId: string;
|
|
490
496
|
value: unknown;
|
|
491
497
|
active: boolean;
|
|
492
498
|
className: string;
|
|
493
499
|
},
|
|
494
500
|
) {
|
|
495
501
|
let prevRow = params.rowId;
|
|
502
|
+
let prevColumn = params.columnId;
|
|
496
503
|
let prev = params.value;
|
|
497
504
|
return {
|
|
498
505
|
update(next: {
|
|
499
506
|
rowId: string;
|
|
507
|
+
columnId: string;
|
|
500
508
|
value: unknown;
|
|
501
509
|
active: boolean;
|
|
502
510
|
className: string;
|
|
503
511
|
}) {
|
|
504
|
-
if (next.rowId !== prevRow) {
|
|
505
|
-
// A different
|
|
512
|
+
if (next.rowId !== prevRow || next.columnId !== prevColumn) {
|
|
513
|
+
// A different cell scrolled into this recycled slot - reset, no flash.
|
|
506
514
|
prevRow = next.rowId;
|
|
515
|
+
prevColumn = next.columnId;
|
|
507
516
|
prev = next.value;
|
|
508
517
|
return;
|
|
509
518
|
}
|
|
@@ -543,6 +552,15 @@
|
|
|
543
552
|
const hasHorizontalOverflow = $derived(ctrl.hasHorizontalOverflow);
|
|
544
553
|
const columnWindowStart = $derived(ctrl.columnWindowStart);
|
|
545
554
|
const columnWindowRightSpacer = $derived(ctrl.columnWindowRightSpacer);
|
|
555
|
+
// The trailing spacer cell after the column window. A flex row is as wide
|
|
556
|
+
// as the table without it (the table carries the full width), and the
|
|
557
|
+
// spacer's area is always past the overscan, off screen; table layout
|
|
558
|
+
// needs it to give every row the same cells. Read as one boolean, so a
|
|
559
|
+
// column crossing - which changes the spacer's width - no longer re-runs
|
|
560
|
+
// a block and restyles a cell in every row.
|
|
561
|
+
const showRightSpacer = $derived(
|
|
562
|
+
columnVirtualizationEnabled && columnWindowRightSpacer > 0 && !ctrl.flexLayoutActive,
|
|
563
|
+
);
|
|
546
564
|
const activeCell = $derived(ctrl.activeCell);
|
|
547
565
|
const activeDescendantId = $derived(ctrl.activeDescendantId);
|
|
548
566
|
const summaryByColumn = $derived(ctrl.summaryByColumn);
|
|
@@ -583,9 +601,36 @@
|
|
|
583
601
|
const setActiveCell = $derived(ctrl.setActiveCell);
|
|
584
602
|
const scrollActiveCellIntoView = $derived(ctrl.scrollActiveCellIntoView);
|
|
585
603
|
const cellSelectionState = $derived(ctrl.cellSelectionState);
|
|
586
|
-
const getColumnWidth = $derived(ctrl.getColumnWidth);
|
|
587
604
|
const isInFillPreview = $derived(ctrl.isInFillPreview);
|
|
588
605
|
const fillMarqueeEdges = $derived(ctrl.fillMarqueeEdges);
|
|
606
|
+
/**
|
|
607
|
+
* A body cell's editing, selection and fill-drag state, read by the cell
|
|
608
|
+
* through one `{@const}`. `null` - the same value every time - for a cell
|
|
609
|
+
* in none of those states, which is nearly every cell, so the cell's
|
|
610
|
+
* attribute effect does not re-run when, say, the active cell moves
|
|
611
|
+
* elsewhere. It used to be four `{@const}`s per cell (cell edit, row edit,
|
|
612
|
+
* selection, fill edges): four deriveds created for every cell on mount,
|
|
613
|
+
* and dropping three of them took ~3.5 ms off a 100k-row mount of ~32 ms.
|
|
614
|
+
*/
|
|
615
|
+
function cellUi(
|
|
616
|
+
rowIndex: number,
|
|
617
|
+
c: { cellRow: { id: string }; cellColumn: { id: string }; colIndex: number },
|
|
618
|
+
) {
|
|
619
|
+
const ed = ctrl.editingCell;
|
|
620
|
+
const editing = !!ed && ed.rowId === c.cellRow.id && ed.columnId === c.cellColumn.id;
|
|
621
|
+
const rowEdit = !!fullRowEdit && fullRowEdit.rowId === c.cellRow.id && c.cellColumn.id in fullRowEdit.draft;
|
|
622
|
+
const sel = cellSelectionState(rowIndex, c.colIndex);
|
|
623
|
+
const fill = fillMarqueeEdges(rowIndex, c.colIndex);
|
|
624
|
+
if (!editing && !rowEdit && !sel && !fill) return null;
|
|
625
|
+
return { editing, rowEdit, sel, fill };
|
|
626
|
+
}
|
|
627
|
+
/** Whether a `pointerover`/`pointerout` on a cell crossed the cell's own
|
|
628
|
+
* edge, as `pointerenter`/`pointerleave` would, rather than a move between
|
|
629
|
+
* its children. */
|
|
630
|
+
function crossedCell(event: PointerEvent & { currentTarget: EventTarget }): boolean {
|
|
631
|
+
const other = event.relatedTarget as Node | null;
|
|
632
|
+
return !other || !(event.currentTarget as Node).contains(other);
|
|
633
|
+
}
|
|
589
634
|
const startFillDrag = $derived(ctrl.startFillDrag);
|
|
590
635
|
const onCellPointerDown = $derived(ctrl.onCellPointerDown);
|
|
591
636
|
const onCellPointerEnter = $derived(ctrl.onCellPointerEnter);
|
|
@@ -595,6 +640,64 @@
|
|
|
595
640
|
const emitCellDoubleClick = $derived(ctrl.emitCellDoubleClick);
|
|
596
641
|
const openContextMenu = $derived(ctrl.openContextMenu);
|
|
597
642
|
|
|
643
|
+
/**
|
|
644
|
+
* Everything a body cell shows that depends on its data: the merge it
|
|
645
|
+
* belongs to, the row and column it draws (a merge continuation draws the
|
|
646
|
+
* origin's), the value, the user's class / tooltip / validity / note, and
|
|
647
|
+
* the width. One derived per cell for all of it - each `{@const}` is its own
|
|
648
|
+
* reactive node, and a cell that scrolls in used to create fifteen of them.
|
|
649
|
+
* Selection and editing stay separate: they change on every arrow key and
|
|
650
|
+
* must not re-run the value and the validators of every visible cell.
|
|
651
|
+
* Returns null for a cell a merge covers (nothing is drawn there).
|
|
652
|
+
*/
|
|
653
|
+
/**
|
|
654
|
+
* The text of a cell that is only text, or null when the cell needs the full
|
|
655
|
+
* body snippet (a checkbox, chips, a sparkline, a custom `cell` template, a
|
|
656
|
+
* tree / group affordance or indent, or a visible conditional format). The
|
|
657
|
+
* full path walks about ten nested `{#if}` blocks to reach the same text
|
|
658
|
+
* node, each a reactive block of its own; most cells of a wide grid are
|
|
659
|
+
* plain text, and this is the per-cell cost a horizontal scroll pays.
|
|
660
|
+
* Produces exactly what `cellBody`'s last branch renders.
|
|
661
|
+
*/
|
|
662
|
+
function plainCellText(row: Row<TData>, column: Column<TData>, value: unknown): string | null {
|
|
663
|
+
const def = column.columnDef;
|
|
664
|
+
if (def.cell != null || def.sparkline) return null;
|
|
665
|
+
const editor = def.editorType;
|
|
666
|
+
if (editor === "checkbox" || editor === "list" || editor === "chips" || typeof value === "boolean") return null;
|
|
667
|
+
if (groupColumnMode && isAutoGroupColumn(column.id)) return null;
|
|
668
|
+
if (treeData && column.id === treeColumnId) return null;
|
|
669
|
+
if (row.depth > 0 && column.id === allColumns[0]?.id && !treeData && !groupColumnMode) return null;
|
|
670
|
+
if (hasConditionalFormats) {
|
|
671
|
+
const cf = cellConditionalFormat(row, column, value);
|
|
672
|
+
if (cf && (cf.background || cf.dataBar || cf.icon || cf.color || cf.fontWeight != null)) return null;
|
|
673
|
+
}
|
|
674
|
+
return formatCellValue(column, value, row);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function bodyCellInfo(row: Row<TData>, rowIndex: number, rendered: (typeof renderedColumns)[number]) {
|
|
678
|
+
const colIndex = rendered.item.index;
|
|
679
|
+
const draw = mergeIndex
|
|
680
|
+
? mergeDrawAt(mergeIndex, rowIndex, colIndex, { ...(rowIndex < frozenRowCount ? mergeFrozenWindow : mergeBodyWindow), ...columnRunOf(colIndex) })
|
|
681
|
+
: null;
|
|
682
|
+
if (draw?.kind === "skip") return null;
|
|
683
|
+
const cellRow = draw?.kind === "continuation" ? (allRows[draw.merge.rowIndex] ?? row) : row;
|
|
684
|
+
const cellColumn = draw?.kind === "continuation" ? (allColumns[draw.merge.colIndex] ?? rendered.column) : rendered.column;
|
|
685
|
+
const cellValue = getCellDisplayValue(cellRow.id, cellColumn.id, getColumnBaseValue(cellRow, cellColumn));
|
|
686
|
+
return {
|
|
687
|
+
colIndex,
|
|
688
|
+
draw,
|
|
689
|
+
cellRow,
|
|
690
|
+
cellColumn,
|
|
691
|
+
cellValue,
|
|
692
|
+
text: plainCellText(cellRow, cellColumn, cellValue),
|
|
693
|
+
userCellClass: computeCellClass(cellRow, cellColumn),
|
|
694
|
+
cellTooltip: computeCellTooltip(cellRow, cellColumn),
|
|
695
|
+
cellValidity: computeCellValidity(cellRow, cellColumn),
|
|
696
|
+
cellNote: computeCellNote(cellRow, cellColumn),
|
|
697
|
+
tdWidth: draw ? mergedWidth(colIndex, draw.colSpan, rendered.item.size) : rendered.item.size,
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
|
|
598
701
|
const onHeaderSortClick = $derived(ctrl.onHeaderSortClick);
|
|
599
702
|
const onGridKeyDown = $derived(ctrl.onGridKeyDown);
|
|
600
703
|
const onGridPaste = $derived(ctrl.onGridPaste);
|
|
@@ -1212,7 +1315,10 @@
|
|
|
1212
1315
|
column: Column<TData>,
|
|
1213
1316
|
cellValue: unknown,
|
|
1214
1317
|
)}
|
|
1215
|
-
|
|
1318
|
+
<!-- A column's own `cell` renderer wins over every built-in display, the
|
|
1319
|
+
checkbox included: a boolean field drawn as a flag button or a status
|
|
1320
|
+
pill used to come out as a read-only checkbox, with `cell` never called. -->
|
|
1321
|
+
{#if (column.columnDef.editorType === "checkbox" || typeof cellValue === "boolean") && column.columnDef.cell == null}
|
|
1216
1322
|
<div
|
|
1217
1323
|
class="sv-grid-checkbox sv-grid-checkbox-readonly"
|
|
1218
1324
|
role="checkbox"
|
|
@@ -1331,18 +1437,22 @@
|
|
|
1331
1437
|
getValue: () => cellValue,
|
|
1332
1438
|
})}
|
|
1333
1439
|
{#if rendered instanceof RenderSnippetConfig}
|
|
1334
|
-
|
|
1440
|
+
<!-- A <td> is handed to another column when its column leaves the
|
|
1441
|
+
column window (the body {#each} key), so a renderer of the app's
|
|
1442
|
+
own is re-created per column rather than handed another
|
|
1443
|
+
column's props with its old state. -->
|
|
1444
|
+
{#key column.id}{@render rendered.snippet(rendered.params)}{/key}
|
|
1335
1445
|
{:else if rendered instanceof RenderComponentConfig}
|
|
1336
|
-
<rendered.component {...rendered.props ?? {}} />
|
|
1446
|
+
{#key column.id}<rendered.component {...rendered.props ?? {}} />{/key}
|
|
1337
1447
|
{:else if typeof rendered === "string" || typeof rendered === "number"}
|
|
1338
|
-
{rendered}
|
|
1448
|
+
<span class="sv-grid-cell-text">{rendered}</span>
|
|
1339
1449
|
{:else}
|
|
1340
|
-
{formatCellValue(column, cellValue, row)}
|
|
1450
|
+
<span class="sv-grid-cell-text">{formatCellValue(column, cellValue, row)}</span>
|
|
1341
1451
|
{/if}
|
|
1342
1452
|
{:else if typeof cellTemplate === "string"}
|
|
1343
|
-
{cellTemplate}
|
|
1453
|
+
<span class="sv-grid-cell-text">{cellTemplate}</span>
|
|
1344
1454
|
{:else}
|
|
1345
|
-
{formatCellValue(column, cellValue, row)}
|
|
1455
|
+
<span class="sv-grid-cell-text">{formatCellValue(column, cellValue, row)}</span>
|
|
1346
1456
|
{/if}
|
|
1347
1457
|
{/if}
|
|
1348
1458
|
{/snippet}
|
|
@@ -1602,11 +1712,16 @@
|
|
|
1602
1712
|
{#if showDetailToggleEffective}
|
|
1603
1713
|
<td class="sv-grid-cell sv-grid-detail-toggle-cell"></td>
|
|
1604
1714
|
{/if}
|
|
1605
|
-
|
|
1606
|
-
|
|
1715
|
+
<!-- Same slots as a data row (spacers + the rendered window), so the
|
|
1716
|
+
skeleton lines up and a wide grid does not draw every column. -->
|
|
1717
|
+
{#if columnVirtualizationEnabled && columnWindowStart > 0}{@render cellGap(columnWindowStart)}{/if}
|
|
1718
|
+
{#each renderedColumns as rendered (rendered.key)}
|
|
1719
|
+
{#if rendered.gap > 0}{@render cellGap(rendered.gap)}{/if}
|
|
1720
|
+
<td class="sv-grid-cell sv-grid-placeholder-cell" style={`width: ${rendered.item.size}px; ${cellPinStyle(rendered.column.id)}`}>
|
|
1607
1721
|
<span class="sv-grid-placeholder-skeleton" aria-hidden="true"></span>
|
|
1608
1722
|
</td>
|
|
1609
1723
|
{/each}
|
|
1724
|
+
{#if showRightSpacer}{@render cellGap(columnWindowRightSpacer)}{/if}
|
|
1610
1725
|
{/if}
|
|
1611
1726
|
</tr>
|
|
1612
1727
|
{/snippet}
|
|
@@ -1637,7 +1752,7 @@
|
|
|
1637
1752
|
{@const stickyTop = stickyTopOfRow(rowIndex)}
|
|
1638
1753
|
<tr
|
|
1639
1754
|
class="sv-grid-row sv-grid-group-row"
|
|
1640
|
-
class:sv-grid-row-selected={isRowSelected(row.id)}
|
|
1755
|
+
class:sv-grid-row-selected={isRowSelected(row.id, row.original)}
|
|
1641
1756
|
class:sv-grid-row-frozen={rowIndex < frozenRowCount}
|
|
1642
1757
|
class:sv-grid-row-frozen-last={frozenRowCount > 0 && rowIndex === frozenRowCount - 1}
|
|
1643
1758
|
class:sv-grid-row-sticky-group={stickyTop !== undefined}
|
|
@@ -1666,7 +1781,7 @@
|
|
|
1666
1781
|
{@const stickyTop = stickyTopOfRow(rowIndex)}
|
|
1667
1782
|
<tr
|
|
1668
1783
|
class={`sv-grid-row ${userRowClass} ${rowDropClass(rowIndex)}`}
|
|
1669
|
-
class:sv-grid-row-selected={isRowSelected(row.id)}
|
|
1784
|
+
class:sv-grid-row-selected={isRowSelected(row.id, row.original)}
|
|
1670
1785
|
class:sv-grid-row-alt={opt.zebraRows &&
|
|
1671
1786
|
rowIndex % 2 === 1}
|
|
1672
1787
|
class:sv-grid-row-draggable={rowDragManagedEffective}
|
|
@@ -1689,31 +1804,33 @@
|
|
|
1689
1804
|
<td
|
|
1690
1805
|
class="sv-grid-cell sv-grid-row-number-cell"
|
|
1691
1806
|
style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; inset-inline-start: 0;`}
|
|
1692
|
-
>{rowIndex + 1}</td
|
|
1807
|
+
><span class="sv-grid-cell-line">{rowIndex + 1}</span></td
|
|
1693
1808
|
>
|
|
1694
1809
|
{/if}
|
|
1695
1810
|
{#if showRowSelectionEffective}
|
|
1696
1811
|
<td
|
|
1697
1812
|
class="sv-grid-cell sv-grid-selection-cell"
|
|
1698
1813
|
style={`width: ${selectionColumnWidth}px; min-width: ${selectionColumnWidth}px; max-width: ${selectionColumnWidth}px; inset-inline-start: ${showRowNumbersEffective ? rowNumberColumnWidth : 0}px;`}
|
|
1699
|
-
onclick={() => toggleRowSelectionById(row.id)}
|
|
1814
|
+
onclick={() => toggleRowSelectionById(row.id, row.original)}
|
|
1700
1815
|
>
|
|
1701
|
-
<
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
event
|
|
1709
|
-
toggleRowSelectionById(row.id);
|
|
1710
|
-
}}
|
|
1711
|
-
onkeydown={(event) =>
|
|
1712
|
-
toggleCheckboxWithKeyboard(event, () => {
|
|
1816
|
+
<span class="sv-grid-cell-line"
|
|
1817
|
+
><button
|
|
1818
|
+
type="button"
|
|
1819
|
+
class="sv-grid-checkbox"
|
|
1820
|
+
role="checkbox"
|
|
1821
|
+
aria-checked={isRowSelected(row.id, row.original)}
|
|
1822
|
+
aria-label="Select row"
|
|
1823
|
+
onclick={(event) => {
|
|
1713
1824
|
event.stopPropagation();
|
|
1714
|
-
toggleRowSelectionById(row.id);
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1825
|
+
toggleRowSelectionById(row.id, row.original);
|
|
1826
|
+
}}
|
|
1827
|
+
onkeydown={(event) =>
|
|
1828
|
+
toggleCheckboxWithKeyboard(event, () => {
|
|
1829
|
+
event.stopPropagation();
|
|
1830
|
+
toggleRowSelectionById(row.id, row.original);
|
|
1831
|
+
})}
|
|
1832
|
+
></button></span
|
|
1833
|
+
>
|
|
1717
1834
|
</td>
|
|
1718
1835
|
{/if}
|
|
1719
1836
|
{#if showDetailToggleEffective}
|
|
@@ -1723,15 +1840,17 @@
|
|
|
1723
1840
|
style={`width: ${detailToggleColumnWidth}px; min-width: ${detailToggleColumnWidth}px; max-width: ${detailToggleColumnWidth}px; left: ${detailToggleColumnLeft}px;`}
|
|
1724
1841
|
>
|
|
1725
1842
|
{#if dt}
|
|
1726
|
-
<
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
event
|
|
1733
|
-
|
|
1734
|
-
|
|
1843
|
+
<span class="sv-grid-cell-line"
|
|
1844
|
+
><button
|
|
1845
|
+
type="button"
|
|
1846
|
+
class="sv-grid-detail-toggle"
|
|
1847
|
+
aria-expanded={dt.open ? "true" : "false"}
|
|
1848
|
+
aria-label={dt.open ? messages.closeDetail : messages.openDetail}
|
|
1849
|
+
onclick={(event) => {
|
|
1850
|
+
event.stopPropagation();
|
|
1851
|
+
dt.toggle();
|
|
1852
|
+
}}>{@render icon("chevron-right")}</button
|
|
1853
|
+
></span
|
|
1735
1854
|
>
|
|
1736
1855
|
{/if}
|
|
1737
1856
|
</td>
|
|
@@ -1743,151 +1862,121 @@
|
|
|
1743
1862
|
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
1744
1863
|
></td>
|
|
1745
1864
|
{/if}
|
|
1746
|
-
{#each renderedColumns as rendered (rendered.
|
|
1747
|
-
{@const
|
|
1748
|
-
{
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
{#if draw?.kind !== "skip"}
|
|
1752
|
-
{@const cellRow = draw?.kind === "continuation" ? (allRows[draw.merge.rowIndex] ?? row) : row}
|
|
1753
|
-
{@const cellColumn = draw?.kind === "continuation" ? (allColumns[draw.merge.colIndex] ?? rendered.column) : rendered.column}
|
|
1754
|
-
{@const baseValue = getColumnBaseValue(
|
|
1755
|
-
cellRow,
|
|
1756
|
-
cellColumn,
|
|
1757
|
-
)}
|
|
1758
|
-
{@const cellValue = getCellDisplayValue(
|
|
1759
|
-
cellRow.id,
|
|
1760
|
-
cellColumn.id,
|
|
1761
|
-
baseValue,
|
|
1762
|
-
)}
|
|
1763
|
-
{@const isEditing =
|
|
1764
|
-
ctrl.editingCell?.rowId === cellRow.id &&
|
|
1765
|
-
ctrl.editingCell?.columnId === cellColumn.id}
|
|
1766
|
-
{@const inRowEdit =
|
|
1767
|
-
!!fullRowEdit &&
|
|
1768
|
-
fullRowEdit.rowId === cellRow.id &&
|
|
1769
|
-
cellColumn.id in fullRowEdit.draft}
|
|
1770
|
-
{@const cellSel = cellSelectionState(rowIndex, colIndex)}
|
|
1771
|
-
{@const fillEdges = fillMarqueeEdges(rowIndex, colIndex)}
|
|
1772
|
-
{@const userCellClass = computeCellClass(
|
|
1773
|
-
cellRow,
|
|
1774
|
-
cellColumn,
|
|
1775
|
-
)}
|
|
1776
|
-
{@const cellTooltip = computeCellTooltip(
|
|
1777
|
-
cellRow,
|
|
1778
|
-
cellColumn,
|
|
1779
|
-
)}
|
|
1780
|
-
{@const cellValidity = computeCellValidity(
|
|
1781
|
-
cellRow,
|
|
1782
|
-
cellColumn,
|
|
1783
|
-
)}
|
|
1784
|
-
{@const cellNote = computeCellNote(
|
|
1785
|
-
cellRow,
|
|
1786
|
-
cellColumn,
|
|
1787
|
-
)}
|
|
1788
|
-
{@const tdWidth = draw ? mergedWidth(colIndex, draw.colSpan, rendered.item.size) : rendered.item.size}
|
|
1865
|
+
{#each renderedColumns as rendered (rendered.key)}
|
|
1866
|
+
{@const c = bodyCellInfo(row, rowIndex, rendered)}
|
|
1867
|
+
{#if rendered.gap > 0}{@render cellGap(rendered.gap)}{/if}
|
|
1868
|
+
{#if c}
|
|
1869
|
+
{@const ui = cellUi(rowIndex, c)}
|
|
1789
1870
|
<td
|
|
1790
|
-
rowspan={draw && draw.rowSpan > 1 ? draw.rowSpan : undefined}
|
|
1791
|
-
colspan={draw && draw.colSpan > 1 ? draw.colSpan : undefined}
|
|
1792
|
-
data-merge-origin={draw ? `${draw.merge.rowIndex}:${draw.merge.colIndex}` : undefined}
|
|
1793
|
-
class:sv-grid-cell-merged={!!draw}
|
|
1794
|
-
class={`sv-grid-cell ${userCellClass}`}
|
|
1795
|
-
class:sv-grid-cell-editing={
|
|
1871
|
+
rowspan={c.draw && c.draw.rowSpan > 1 ? c.draw.rowSpan : undefined}
|
|
1872
|
+
colspan={c.draw && c.draw.colSpan > 1 ? c.draw.colSpan : undefined}
|
|
1873
|
+
data-merge-origin={c.draw ? `${c.draw.merge.rowIndex}:${c.draw.merge.colIndex}` : undefined}
|
|
1874
|
+
class:sv-grid-cell-merged={!!c.draw}
|
|
1875
|
+
class={`sv-grid-cell ${c.userCellClass}`}
|
|
1876
|
+
class:sv-grid-cell-editing={!!ui && (ui.editing || ui.rowEdit)}
|
|
1796
1877
|
class:sv-grid-cell-collapsed={!!collapsedColumns[rendered.column.id]}
|
|
1797
|
-
class:sv-grid-cell-active={!!
|
|
1798
|
-
class:sv-grid-cell-has-fill-handle={!!
|
|
1878
|
+
class:sv-grid-cell-active={!!ui?.sel?.active}
|
|
1879
|
+
class:sv-grid-cell-has-fill-handle={!!ui?.sel?.fillHandle}
|
|
1799
1880
|
class:sv-grid-cell-cf={hasConditionalFormats}
|
|
1800
|
-
class:sv-grid-cell-invalid={cellValidity.invalid}
|
|
1801
|
-
class:sv-grid-cell-has-note={cellNote != null}
|
|
1802
|
-
aria-invalid={cellValidity.invalid ? "true" : undefined}
|
|
1881
|
+
class:sv-grid-cell-invalid={c.cellValidity.invalid}
|
|
1882
|
+
class:sv-grid-cell-has-note={c.cellNote != null}
|
|
1883
|
+
aria-invalid={c.cellValidity.invalid ? "true" : undefined}
|
|
1803
1884
|
data-svgrid-row={rowIndex}
|
|
1804
|
-
data-svgrid-col={colIndex}
|
|
1885
|
+
data-svgrid-col={c.colIndex}
|
|
1805
1886
|
data-col-id={rendered.column.id}
|
|
1806
1887
|
data-align={getColumnAlign(rendered.column)}
|
|
1807
|
-
data-pinned={
|
|
1808
|
-
|
|
1809
|
-
data-
|
|
1810
|
-
data-range-
|
|
1811
|
-
data-range-bottom={cellSel?.edges?.bottom
|
|
1888
|
+
data-pinned={rendered.pinned ?? undefined}
|
|
1889
|
+
data-selected-range={ui?.sel?.edges ? "true" : undefined}
|
|
1890
|
+
data-range-top={ui?.sel?.edges?.top ? "true" : undefined}
|
|
1891
|
+
data-range-bottom={ui?.sel?.edges?.bottom
|
|
1812
1892
|
? "true"
|
|
1813
1893
|
: undefined}
|
|
1814
|
-
data-range-left={
|
|
1894
|
+
data-range-left={ui?.sel?.edges?.left
|
|
1815
1895
|
? "true"
|
|
1816
1896
|
: undefined}
|
|
1817
|
-
data-range-right={
|
|
1897
|
+
data-range-right={ui?.sel?.edges?.right
|
|
1818
1898
|
? "true"
|
|
1819
1899
|
: undefined}
|
|
1820
|
-
data-fill-preview={isInFillPreview(rowIndex, colIndex)
|
|
1900
|
+
data-fill-preview={isInFillPreview(rowIndex, c.colIndex)
|
|
1821
1901
|
? "true"
|
|
1822
1902
|
: undefined}
|
|
1823
|
-
data-fill-top={
|
|
1824
|
-
data-fill-bottom={
|
|
1825
|
-
data-fill-left={
|
|
1826
|
-
data-fill-right={
|
|
1827
|
-
style={`width: ${tdWidth}px; min-width: ${tdWidth}px; max-width: ${tdWidth}px; ${cellPinStyle(rendered.column.id)}`}
|
|
1903
|
+
data-fill-top={ui?.fill?.top ? "true" : undefined}
|
|
1904
|
+
data-fill-bottom={ui?.fill?.bottom ? "true" : undefined}
|
|
1905
|
+
data-fill-left={ui?.fill?.left ? "true" : undefined}
|
|
1906
|
+
data-fill-right={ui?.fill?.right ? "true" : undefined}
|
|
1907
|
+
style={`width: ${c.tdWidth}px; min-width: ${c.tdWidth}px; max-width: ${c.tdWidth}px; ${rendered.pinned ? cellPinStyle(rendered.column.id) : ""}`}
|
|
1828
1908
|
onpointerdown={(event) =>
|
|
1829
|
-
onCellPointerDown(rowIndex, colIndex, event)}
|
|
1830
|
-
|
|
1831
|
-
|
|
1909
|
+
onCellPointerDown(rowIndex, c.colIndex, event)}
|
|
1910
|
+
onpointerover={(event) => {
|
|
1911
|
+
// `pointerover`/`pointerout` rather than enter/leave:
|
|
1912
|
+
// Svelte delegates them to one listener on the root,
|
|
1913
|
+
// where enter/leave added two listeners to every
|
|
1914
|
+
// cell on mount (~1.5 ms of a 100k-row mount). A
|
|
1915
|
+
// move between the cell's own children is not an
|
|
1916
|
+
// enter.
|
|
1917
|
+
if (!crossedCell(event)) return;
|
|
1918
|
+
onCellPointerEnter(rowIndex, c.colIndex);
|
|
1832
1919
|
// The column tooltip fires on whole-cell hover; a
|
|
1833
1920
|
// per-cell note waits for its corner below. A
|
|
1834
1921
|
// validation message wins over the column's tooltip.
|
|
1835
1922
|
const tip =
|
|
1836
|
-
cellValidity.invalid && cellValidity.message
|
|
1837
|
-
? cellValidity.message
|
|
1838
|
-
: cellTooltip;
|
|
1923
|
+
c.cellValidity.invalid && c.cellValidity.message
|
|
1924
|
+
? c.cellValidity.message
|
|
1925
|
+
: c.cellTooltip;
|
|
1839
1926
|
if (tip)
|
|
1840
1927
|
showTooltipFor(
|
|
1841
1928
|
event.currentTarget as HTMLElement,
|
|
1842
1929
|
tip,
|
|
1843
1930
|
);
|
|
1844
1931
|
}}
|
|
1845
|
-
|
|
1932
|
+
onpointerout={(event) => {
|
|
1933
|
+
if (crossedCell(event)) hideTooltip();
|
|
1934
|
+
}}
|
|
1846
1935
|
ondblclick={() =>
|
|
1847
|
-
emitCellDoubleClick(rowIndex, colIndex)}
|
|
1848
|
-
onclick={() => onCellClick(rowIndex, colIndex)}
|
|
1936
|
+
emitCellDoubleClick(rowIndex, c.colIndex)}
|
|
1937
|
+
onclick={() => onCellClick(rowIndex, c.colIndex)}
|
|
1849
1938
|
oncontextmenu={(event) =>
|
|
1850
1939
|
openContextMenu(
|
|
1851
1940
|
event,
|
|
1852
1941
|
rowIndex,
|
|
1853
|
-
colIndex,
|
|
1942
|
+
c.colIndex,
|
|
1854
1943
|
rendered.column.id,
|
|
1855
1944
|
)}
|
|
1856
1945
|
use:cellFlashAction={{
|
|
1857
|
-
rowId: cellRow.id,
|
|
1858
|
-
|
|
1859
|
-
|
|
1946
|
+
rowId: c.cellRow.id,
|
|
1947
|
+
columnId: c.cellColumn.id,
|
|
1948
|
+
value: c.cellValue,
|
|
1949
|
+
active: !!c.cellColumn.columnDef.cellFlash,
|
|
1860
1950
|
className: flashClassFor(
|
|
1861
|
-
cellColumn.columnDef.cellFlash,
|
|
1951
|
+
c.cellColumn.columnDef.cellFlash,
|
|
1862
1952
|
),
|
|
1863
1953
|
}}
|
|
1864
1954
|
role="gridcell"
|
|
1865
|
-
id={getGridCellDomId(ctrl.gridDomId, rowIndex, colIndex)}
|
|
1866
|
-
aria-colindex={colIndex + 1}
|
|
1955
|
+
id={getGridCellDomId(ctrl.gridDomId, rowIndex, c.colIndex)}
|
|
1956
|
+
aria-colindex={c.colIndex + 1}
|
|
1867
1957
|
aria-rowindex={rowIndex + 1}
|
|
1868
|
-
aria-selected={isRowSelected(row.id)}
|
|
1958
|
+
aria-selected={isRowSelected(row.id, row.original)}
|
|
1869
1959
|
>
|
|
1870
|
-
{#if
|
|
1960
|
+
{#if ui && (ui.editing || ui.rowEdit)}
|
|
1871
1961
|
<!-- The editing cell stays empty until the lazy
|
|
1872
1962
|
editor chunk lands, exactly as it already did
|
|
1873
1963
|
for the dropdown / date editors. Falling back
|
|
1874
1964
|
to the read-only value here would flash stale
|
|
1875
1965
|
content into a cell the user is editing. -->
|
|
1876
1966
|
{#if CellEditor}
|
|
1877
|
-
<CellEditor {ctrl} column={cellColumn} row={cellRow} fullRow={
|
|
1967
|
+
<CellEditor {ctrl} column={c.cellColumn} row={c.cellRow} fullRow={!!ui?.rowEdit} />
|
|
1878
1968
|
{/if}
|
|
1879
1969
|
{:else}
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
{/if}
|
|
1970
|
+
<!-- One line box for the cell's content, as a table
|
|
1971
|
+
cell has: centred in the cell, with inline content
|
|
1972
|
+
(pills, chips, checkboxes) aligned in it the same
|
|
1973
|
+
way under table or flex rows, and the ellipsis and
|
|
1974
|
+
text-align applied to it. -->
|
|
1975
|
+
<span class="sv-grid-cell-line"
|
|
1976
|
+
>{#if c.text !== null}{c.text}{:else if plainCellBody}{@render cellBody(c.cellRow, c.cellColumn, c.cellValue)}{:else}{@render cellBodyWithFormat(c.cellRow, c.cellColumn, c.cellValue)}{/if}</span
|
|
1977
|
+
>
|
|
1889
1978
|
{/if}
|
|
1890
|
-
{#if !
|
|
1979
|
+
{#if !ui?.editing && ui?.sel?.fillHandle}
|
|
1891
1980
|
<!-- Excel-style fill handle: drag down/right to
|
|
1892
1981
|
extend the selection and pattern-fill the new
|
|
1893
1982
|
cells on release. Rendered inside the bottom-
|
|
@@ -1900,7 +1989,7 @@
|
|
|
1900
1989
|
aria-label="Fill handle"
|
|
1901
1990
|
tabindex={-1}
|
|
1902
1991
|
onpointerdown={(event) =>
|
|
1903
|
-
startFillDrag(event, rowIndex, colIndex)}
|
|
1992
|
+
startFillDrag(event, rowIndex, c.colIndex)}
|
|
1904
1993
|
ondblclick={(event) => {
|
|
1905
1994
|
// Not the cell's double-click: this fills
|
|
1906
1995
|
// down as far as the neighbour column goes.
|
|
@@ -1910,16 +1999,16 @@
|
|
|
1910
1999
|
}}
|
|
1911
2000
|
></div>
|
|
1912
2001
|
{/if}
|
|
1913
|
-
{#if cellValidity.invalid && cellValidity.message}
|
|
2002
|
+
{#if c.cellValidity.invalid && c.cellValidity.message}
|
|
1914
2003
|
<!-- The validation message, for assistive tech: the
|
|
1915
2004
|
tooltip above is mouse-only, and unlike a title
|
|
1916
2005
|
a visually-hidden span adds no second native
|
|
1917
2006
|
tooltip competing with the custom one. -->
|
|
1918
2007
|
<span class="sv-grid-sr-only"
|
|
1919
|
-
>{cellValidity.message}</span
|
|
2008
|
+
>{c.cellValidity.message}</span
|
|
1920
2009
|
>
|
|
1921
2010
|
{/if}
|
|
1922
|
-
{#if cellNote != null && !
|
|
2011
|
+
{#if c.cellNote != null && !ui?.editing}
|
|
1923
2012
|
<span
|
|
1924
2013
|
class="sv-grid-cell-note-corner"
|
|
1925
2014
|
aria-label="Note"
|
|
@@ -1927,7 +2016,7 @@
|
|
|
1927
2016
|
event.stopPropagation();
|
|
1928
2017
|
showTooltipFor(
|
|
1929
2018
|
event.currentTarget as HTMLElement,
|
|
1930
|
-
cellNote,
|
|
2019
|
+
c.cellNote ?? "",
|
|
1931
2020
|
);
|
|
1932
2021
|
}}
|
|
1933
2022
|
onpointerleave={(event) => {
|
|
@@ -1939,7 +2028,7 @@
|
|
|
1939
2028
|
</td>
|
|
1940
2029
|
{/if}
|
|
1941
2030
|
{/each}
|
|
1942
|
-
{#if
|
|
2031
|
+
{#if showRightSpacer}
|
|
1943
2032
|
<td
|
|
1944
2033
|
class="sv-grid-cell sv-grid-cell-spacer"
|
|
1945
2034
|
aria-hidden="true"
|
|
@@ -1954,6 +2043,23 @@
|
|
|
1954
2043
|
outside the rendered window), so this one carries no cell ids, no
|
|
1955
2044
|
row index attributes and is hidden from assistive tech; the group
|
|
1956
2045
|
cell's own expander still works, since it is the same renderer. -->
|
|
2046
|
+
<!-- The spacer for the columns skipped between two rendered runs (pinned
|
|
2047
|
+
left, the virtual window, pinned right). Every row template draws it
|
|
2048
|
+
before the same column, so the fixed table layout keeps its slots. -->
|
|
2049
|
+
{#snippet cellGap(px: number)}
|
|
2050
|
+
<td
|
|
2051
|
+
class="sv-grid-cell sv-grid-cell-spacer"
|
|
2052
|
+
aria-hidden="true"
|
|
2053
|
+
style={`width: ${px}px; min-width: ${px}px; max-width: ${px}px;`}
|
|
2054
|
+
></td>
|
|
2055
|
+
{/snippet}
|
|
2056
|
+
{#snippet columnGap(px: number)}
|
|
2057
|
+
<th
|
|
2058
|
+
class="sv-grid-column sv-grid-column-spacer"
|
|
2059
|
+
aria-hidden="true"
|
|
2060
|
+
style={`width: ${px}px; min-width: ${px}px; max-width: ${px}px;`}
|
|
2061
|
+
></th>
|
|
2062
|
+
{/snippet}
|
|
1957
2063
|
{#snippet stickyGroupRow(row: Row<TData>, rowIndex: number, top: number, size: number, last: boolean)}
|
|
1958
2064
|
{#if isGroupRow(row) && !groupColumnMode}
|
|
1959
2065
|
<tr
|
|
@@ -1986,7 +2092,7 @@
|
|
|
1986
2092
|
<td
|
|
1987
2093
|
class="sv-grid-cell sv-grid-row-number-cell"
|
|
1988
2094
|
style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; left: 0;`}
|
|
1989
|
-
>{rowIndex + 1}</td
|
|
2095
|
+
><span class="sv-grid-cell-line">{rowIndex + 1}</span></td
|
|
1990
2096
|
>
|
|
1991
2097
|
{/if}
|
|
1992
2098
|
{#if showRowSelectionEffective}
|
|
@@ -2009,6 +2115,7 @@
|
|
|
2009
2115
|
{/if}
|
|
2010
2116
|
{#each renderedColumns as rendered (rendered.column.id)}
|
|
2011
2117
|
{@const cellValue = getCellDisplayValue(row.id, rendered.column.id, getColumnBaseValue(row, rendered.column))}
|
|
2118
|
+
{#if rendered.gap > 0}{@render cellGap(rendered.gap)}{/if}
|
|
2012
2119
|
<td
|
|
2013
2120
|
class={`sv-grid-cell ${computeCellClass(row, rendered.column)}`}
|
|
2014
2121
|
class:sv-grid-cell-cf={hasConditionalFormats}
|
|
@@ -2024,7 +2131,7 @@
|
|
|
2024
2131
|
{/if}
|
|
2025
2132
|
</td>
|
|
2026
2133
|
{/each}
|
|
2027
|
-
{#if
|
|
2134
|
+
{#if showRightSpacer}
|
|
2028
2135
|
<td
|
|
2029
2136
|
class="sv-grid-cell sv-grid-cell-spacer"
|
|
2030
2137
|
style={`width: ${columnWindowRightSpacer}px; min-width: ${columnWindowRightSpacer}px; max-width: ${columnWindowRightSpacer}px;`}
|
|
@@ -2047,7 +2154,9 @@
|
|
|
2047
2154
|
<td
|
|
2048
2155
|
class="sv-grid-cell sv-grid-row-number-cell"
|
|
2049
2156
|
style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; inset-inline-start: 0;`}
|
|
2050
|
-
|
|
2157
|
+
><span class="sv-grid-cell-line"
|
|
2158
|
+
>{#if where === "top"}{@render icon("pinned-row-top")}{:else}{@render icon("pinned-row-bottom")}{/if}</span
|
|
2159
|
+
></td
|
|
2051
2160
|
>
|
|
2052
2161
|
{/if}
|
|
2053
2162
|
{#if showRowSelectionEffective}
|
|
@@ -2075,16 +2184,17 @@
|
|
|
2075
2184
|
rowData,
|
|
2076
2185
|
rendered.column,
|
|
2077
2186
|
)}
|
|
2187
|
+
{#if rendered.gap > 0}{@render cellGap(rendered.gap)}{/if}
|
|
2078
2188
|
<td
|
|
2079
2189
|
class={`sv-grid-cell ${userCellClass}`}
|
|
2080
2190
|
data-col-id={rendered.column.id}
|
|
2081
2191
|
data-align={getColumnAlign(rendered.column)}
|
|
2082
2192
|
data-pinned={isColumnPinned(rendered.column.id) ?? undefined}
|
|
2083
2193
|
style={`width: ${rendered.item.size}px; min-width: ${rendered.item.size}px; max-width: ${rendered.item.size}px; ${cellPinStyle(rendered.column.id)}`}
|
|
2084
|
-
>{formatPinnedValue(rendered.column, value)}</td
|
|
2194
|
+
><span class="sv-grid-cell-text">{formatPinnedValue(rendered.column, value)}</span></td
|
|
2085
2195
|
>
|
|
2086
2196
|
{/each}
|
|
2087
|
-
{#if
|
|
2197
|
+
{#if showRightSpacer}
|
|
2088
2198
|
<td
|
|
2089
2199
|
class="sv-grid-cell sv-grid-cell-spacer"
|
|
2090
2200
|
aria-hidden="true"
|
|
@@ -2201,7 +2311,9 @@
|
|
|
2201
2311
|
<table
|
|
2202
2312
|
bind:this={ctrl.gridRootEl}
|
|
2203
2313
|
class="sv-grid-table"
|
|
2314
|
+
class:sv-grid-table-flex={ctrl.flexRows}
|
|
2204
2315
|
class:sv-grid-no-row-hover={opt.enableRowHover !== true}
|
|
2316
|
+
class:sv-grid-numbers-beside-toggle={showDetailToggleEffective && !showRowSelectionEffective}
|
|
2205
2317
|
{...getGridRootA11yProps({
|
|
2206
2318
|
activeDescendantId,
|
|
2207
2319
|
rowCount: allRows.length,
|
|
@@ -2210,7 +2322,7 @@
|
|
|
2210
2322
|
})}
|
|
2211
2323
|
onkeydown={onGridKeyDown}
|
|
2212
2324
|
onpaste={onGridPaste}
|
|
2213
|
-
style={`min-width: ${totalColumnWidth}px;`}
|
|
2325
|
+
style={`min-width: ${totalColumnWidth + ctrl.systemColumnsWidth}px;`}
|
|
2214
2326
|
>
|
|
2215
2327
|
<!-- svelte-ignore a11y_no_redundant_roles -->
|
|
2216
2328
|
<thead class="sv-grid-head" bind:this={ctrl.theadEl} role="rowgroup">
|
|
@@ -2255,6 +2367,7 @@
|
|
|
2255
2367
|
></th>
|
|
2256
2368
|
{/if}
|
|
2257
2369
|
{#each row.cells as cell (cell.key)}
|
|
2370
|
+
{#if cell.gapBefore}{@render columnGap(cell.gapBefore)}{/if}
|
|
2258
2371
|
<th
|
|
2259
2372
|
class="sv-grid-column sv-grid-group-header-cell"
|
|
2260
2373
|
class:sv-grid-group-header-placeholder={cell.isPlaceholder}
|
|
@@ -2291,7 +2404,7 @@
|
|
|
2291
2404
|
{/if}
|
|
2292
2405
|
</th>
|
|
2293
2406
|
{/each}
|
|
2294
|
-
{#if
|
|
2407
|
+
{#if showRightSpacer}
|
|
2295
2408
|
<th
|
|
2296
2409
|
class="sv-grid-column sv-grid-column-spacer"
|
|
2297
2410
|
aria-hidden="true"
|
|
@@ -2359,8 +2472,9 @@
|
|
|
2359
2472
|
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
2360
2473
|
></th>
|
|
2361
2474
|
{/if}
|
|
2362
|
-
{#each renderedColumns as rendered (rendered.
|
|
2475
|
+
{#each renderedColumns as rendered (rendered.key)}
|
|
2363
2476
|
{@const header = headerGroup.headers[rendered.item.index]}
|
|
2477
|
+
{#if rendered.gap > 0}{@render columnGap(rendered.gap)}{/if}
|
|
2364
2478
|
{#if header}
|
|
2365
2479
|
{@const sortDirection =
|
|
2366
2480
|
sortDirectionByColumn[header.column.id]}
|
|
@@ -2453,9 +2567,12 @@
|
|
|
2453
2567
|
}}
|
|
2454
2568
|
>
|
|
2455
2569
|
{#if rendered instanceof RenderSnippetConfig}
|
|
2456
|
-
|
|
2570
|
+
<!-- Header cells are handed between columns as the
|
|
2571
|
+
window moves (slot-keyed, like body cells), so an
|
|
2572
|
+
app's own header renderer is re-created per column. -->
|
|
2573
|
+
{#key header.column.id}{@render rendered.snippet(rendered.params)}{/key}
|
|
2457
2574
|
{:else if rendered instanceof RenderComponentConfig}
|
|
2458
|
-
<rendered.component {...rendered.props ?? {}} />
|
|
2575
|
+
{#key header.column.id}<rendered.component {...rendered.props ?? {}} />{/key}
|
|
2459
2576
|
{:else if typeof rendered === "string" || typeof rendered === "number"}
|
|
2460
2577
|
{rendered}
|
|
2461
2578
|
{:else}
|
|
@@ -2567,7 +2684,7 @@
|
|
|
2567
2684
|
</th>
|
|
2568
2685
|
{/if}
|
|
2569
2686
|
{/each}
|
|
2570
|
-
{#if
|
|
2687
|
+
{#if showRightSpacer}
|
|
2571
2688
|
<th
|
|
2572
2689
|
class="sv-grid-column sv-grid-column-spacer"
|
|
2573
2690
|
aria-hidden="true"
|
|
@@ -2608,10 +2725,11 @@
|
|
|
2608
2725
|
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
2609
2726
|
></th>
|
|
2610
2727
|
{/if}
|
|
2611
|
-
{#each renderedColumns as rendered (rendered.
|
|
2728
|
+
{#each renderedColumns as rendered (rendered.key)}
|
|
2612
2729
|
{@const activeOperator =
|
|
2613
2730
|
filterMenuValues[rendered.column.id]?.operator ??
|
|
2614
2731
|
defaultOperatorFor(rendered.column)}
|
|
2732
|
+
{#if rendered.gap > 0}{@render columnGap(rendered.gap)}{/if}
|
|
2615
2733
|
<th
|
|
2616
2734
|
class="sv-grid-column"
|
|
2617
2735
|
data-pinned={isColumnPinned(rendered.column.id) ??
|
|
@@ -2791,7 +2909,7 @@
|
|
|
2791
2909
|
</div>
|
|
2792
2910
|
</th>
|
|
2793
2911
|
{/each}
|
|
2794
|
-
{#if
|
|
2912
|
+
{#if showRightSpacer}
|
|
2795
2913
|
<th
|
|
2796
2914
|
class="sv-grid-column sv-grid-column-spacer"
|
|
2797
2915
|
aria-hidden="true"
|
|
@@ -2934,17 +3052,18 @@
|
|
|
2934
3052
|
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
2935
3053
|
></th>
|
|
2936
3054
|
{/if}
|
|
2937
|
-
{#each renderedColumns as rendered (rendered.
|
|
3055
|
+
{#each renderedColumns as rendered (rendered.key)}
|
|
3056
|
+
{#if rendered.gap > 0}{@render columnGap(rendered.gap)}{/if}
|
|
2938
3057
|
<th
|
|
2939
3058
|
class="sv-grid-column sv-grid-summary-column"
|
|
2940
3059
|
data-pinned={isColumnPinned(rendered.column.id) ??
|
|
2941
3060
|
undefined}
|
|
2942
3061
|
style={`width: ${rendered.item.size}px; min-width: ${rendered.item.size}px; max-width: ${rendered.item.size}px; ${cellPinStyle(rendered.column.id)}`}
|
|
2943
3062
|
>
|
|
2944
|
-
{summaryByColumn[rendered.column.id] ?? ""}
|
|
3063
|
+
<span class="sv-grid-cell-text">{summaryByColumn[rendered.column.id] ?? ""}</span>
|
|
2945
3064
|
</th>
|
|
2946
3065
|
{/each}
|
|
2947
|
-
{#if
|
|
3066
|
+
{#if showRightSpacer}
|
|
2948
3067
|
<th
|
|
2949
3068
|
class="sv-grid-column sv-grid-column-spacer"
|
|
2950
3069
|
aria-hidden="true"
|
|
@@ -3121,10 +3240,12 @@
|
|
|
3121
3240
|
<div class="sv-grid-skeleton" aria-hidden="true">
|
|
3122
3241
|
{#each Array(opt.loadingSkeletonRows ?? 8) as _, r (r)}
|
|
3123
3242
|
<div class="sv-grid-skeleton-row">
|
|
3124
|
-
|
|
3243
|
+
<!-- The rendered window, not every column: a 10,000-column grid
|
|
3244
|
+
drew 80,000 skeleton cells here while it loaded. -->
|
|
3245
|
+
{#each renderedColumns as rendered (rendered.key)}
|
|
3125
3246
|
<div
|
|
3126
3247
|
class="sv-grid-skeleton-cell"
|
|
3127
|
-
style={`width:${
|
|
3248
|
+
style={`width:${rendered.item.size}px`}
|
|
3128
3249
|
>
|
|
3129
3250
|
<span class="sv-grid-skeleton-bar"></span>
|
|
3130
3251
|
</div>
|