@svgrid/grid 3.0.8 → 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/dist/GridMenus.svelte +3 -2
- package/dist/SvGrid.controller.svelte.d.ts +60 -8
- package/dist/SvGrid.controller.svelte.js +475 -110
- package/dist/SvGrid.css +186 -3
- package/dist/SvGrid.svelte +281 -163
- package/dist/SvGrid.types.d.ts +24 -7
- package/dist/cdn/{GridMenus-D15bhpX5.js → GridMenus-B1sipj6y.js} +165 -165
- package/dist/cdn/{GridMenus-B8-Gbfq2.js → GridMenus-DlAzfHLk.js} +201 -201
- package/dist/cdn/server-block-cache-Dj-KPqEQ.js +1289 -0
- package/dist/cdn/{src-Dz625Fhx.js → src-BYsa66I0.js} +9738 -10174
- package/dist/cdn/{src-CleI0g9l.js → src-pA4RroLV.js} +10702 -11138
- package/dist/cdn/svgrid.js +27 -27
- package/dist/cdn/svgrid.svelte-external.js +27 -27
- package/dist/cdn/{validate-GEs4ZPSW.js → validate-XuMe4_KR.js} +2 -2
- 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 -52
- package/dist/filtering/excel-filters.js +41 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/selection.d.ts +2 -2
- package/dist/selection.js +32 -8
- 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/validate.js +3 -11
- 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 +469 -114
- package/src/SvGrid.css +186 -3
- package/src/SvGrid.svelte +281 -163
- 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 -52
- package/src/counting-cell.test.svelte +11 -0
- package/src/filtering/excel-filters.ts +39 -6
- package/src/headless.reactivity.svelte.test.ts +1 -1
- package/src/index.ts +6 -0
- package/src/new-features.test.ts +24 -0
- package/src/selection.test.ts +14 -0
- package/src/selection.ts +31 -8
- 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/svgrid.behavior.test.ts +29 -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 +4 -4
- package/src/validate.ts +3 -14
- 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/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);
|
|
@@ -1334,18 +1437,22 @@
|
|
|
1334
1437
|
getValue: () => cellValue,
|
|
1335
1438
|
})}
|
|
1336
1439
|
{#if rendered instanceof RenderSnippetConfig}
|
|
1337
|
-
|
|
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}
|
|
1338
1445
|
{:else if rendered instanceof RenderComponentConfig}
|
|
1339
|
-
<rendered.component {...rendered.props ?? {}} />
|
|
1446
|
+
{#key column.id}<rendered.component {...rendered.props ?? {}} />{/key}
|
|
1340
1447
|
{:else if typeof rendered === "string" || typeof rendered === "number"}
|
|
1341
|
-
{rendered}
|
|
1448
|
+
<span class="sv-grid-cell-text">{rendered}</span>
|
|
1342
1449
|
{:else}
|
|
1343
|
-
{formatCellValue(column, cellValue, row)}
|
|
1450
|
+
<span class="sv-grid-cell-text">{formatCellValue(column, cellValue, row)}</span>
|
|
1344
1451
|
{/if}
|
|
1345
1452
|
{:else if typeof cellTemplate === "string"}
|
|
1346
|
-
{cellTemplate}
|
|
1453
|
+
<span class="sv-grid-cell-text">{cellTemplate}</span>
|
|
1347
1454
|
{:else}
|
|
1348
|
-
{formatCellValue(column, cellValue, row)}
|
|
1455
|
+
<span class="sv-grid-cell-text">{formatCellValue(column, cellValue, row)}</span>
|
|
1349
1456
|
{/if}
|
|
1350
1457
|
{/if}
|
|
1351
1458
|
{/snippet}
|
|
@@ -1605,11 +1712,16 @@
|
|
|
1605
1712
|
{#if showDetailToggleEffective}
|
|
1606
1713
|
<td class="sv-grid-cell sv-grid-detail-toggle-cell"></td>
|
|
1607
1714
|
{/if}
|
|
1608
|
-
|
|
1609
|
-
|
|
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)}`}>
|
|
1610
1721
|
<span class="sv-grid-placeholder-skeleton" aria-hidden="true"></span>
|
|
1611
1722
|
</td>
|
|
1612
1723
|
{/each}
|
|
1724
|
+
{#if showRightSpacer}{@render cellGap(columnWindowRightSpacer)}{/if}
|
|
1613
1725
|
{/if}
|
|
1614
1726
|
</tr>
|
|
1615
1727
|
{/snippet}
|
|
@@ -1640,7 +1752,7 @@
|
|
|
1640
1752
|
{@const stickyTop = stickyTopOfRow(rowIndex)}
|
|
1641
1753
|
<tr
|
|
1642
1754
|
class="sv-grid-row sv-grid-group-row"
|
|
1643
|
-
class:sv-grid-row-selected={isRowSelected(row.id)}
|
|
1755
|
+
class:sv-grid-row-selected={isRowSelected(row.id, row.original)}
|
|
1644
1756
|
class:sv-grid-row-frozen={rowIndex < frozenRowCount}
|
|
1645
1757
|
class:sv-grid-row-frozen-last={frozenRowCount > 0 && rowIndex === frozenRowCount - 1}
|
|
1646
1758
|
class:sv-grid-row-sticky-group={stickyTop !== undefined}
|
|
@@ -1669,7 +1781,7 @@
|
|
|
1669
1781
|
{@const stickyTop = stickyTopOfRow(rowIndex)}
|
|
1670
1782
|
<tr
|
|
1671
1783
|
class={`sv-grid-row ${userRowClass} ${rowDropClass(rowIndex)}`}
|
|
1672
|
-
class:sv-grid-row-selected={isRowSelected(row.id)}
|
|
1784
|
+
class:sv-grid-row-selected={isRowSelected(row.id, row.original)}
|
|
1673
1785
|
class:sv-grid-row-alt={opt.zebraRows &&
|
|
1674
1786
|
rowIndex % 2 === 1}
|
|
1675
1787
|
class:sv-grid-row-draggable={rowDragManagedEffective}
|
|
@@ -1692,31 +1804,33 @@
|
|
|
1692
1804
|
<td
|
|
1693
1805
|
class="sv-grid-cell sv-grid-row-number-cell"
|
|
1694
1806
|
style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; inset-inline-start: 0;`}
|
|
1695
|
-
>{rowIndex + 1}</td
|
|
1807
|
+
><span class="sv-grid-cell-line">{rowIndex + 1}</span></td
|
|
1696
1808
|
>
|
|
1697
1809
|
{/if}
|
|
1698
1810
|
{#if showRowSelectionEffective}
|
|
1699
1811
|
<td
|
|
1700
1812
|
class="sv-grid-cell sv-grid-selection-cell"
|
|
1701
1813
|
style={`width: ${selectionColumnWidth}px; min-width: ${selectionColumnWidth}px; max-width: ${selectionColumnWidth}px; inset-inline-start: ${showRowNumbersEffective ? rowNumberColumnWidth : 0}px;`}
|
|
1702
|
-
onclick={() => toggleRowSelectionById(row.id)}
|
|
1814
|
+
onclick={() => toggleRowSelectionById(row.id, row.original)}
|
|
1703
1815
|
>
|
|
1704
|
-
<
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
event
|
|
1712
|
-
toggleRowSelectionById(row.id);
|
|
1713
|
-
}}
|
|
1714
|
-
onkeydown={(event) =>
|
|
1715
|
-
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) => {
|
|
1716
1824
|
event.stopPropagation();
|
|
1717
|
-
toggleRowSelectionById(row.id);
|
|
1718
|
-
}
|
|
1719
|
-
|
|
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
|
+
>
|
|
1720
1834
|
</td>
|
|
1721
1835
|
{/if}
|
|
1722
1836
|
{#if showDetailToggleEffective}
|
|
@@ -1726,15 +1840,17 @@
|
|
|
1726
1840
|
style={`width: ${detailToggleColumnWidth}px; min-width: ${detailToggleColumnWidth}px; max-width: ${detailToggleColumnWidth}px; left: ${detailToggleColumnLeft}px;`}
|
|
1727
1841
|
>
|
|
1728
1842
|
{#if dt}
|
|
1729
|
-
<
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
event
|
|
1736
|
-
|
|
1737
|
-
|
|
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
|
|
1738
1854
|
>
|
|
1739
1855
|
{/if}
|
|
1740
1856
|
</td>
|
|
@@ -1746,151 +1862,121 @@
|
|
|
1746
1862
|
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
1747
1863
|
></td>
|
|
1748
1864
|
{/if}
|
|
1749
|
-
{#each renderedColumns as rendered (rendered.
|
|
1750
|
-
{@const
|
|
1751
|
-
{
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
{#if draw?.kind !== "skip"}
|
|
1755
|
-
{@const cellRow = draw?.kind === "continuation" ? (allRows[draw.merge.rowIndex] ?? row) : row}
|
|
1756
|
-
{@const cellColumn = draw?.kind === "continuation" ? (allColumns[draw.merge.colIndex] ?? rendered.column) : rendered.column}
|
|
1757
|
-
{@const baseValue = getColumnBaseValue(
|
|
1758
|
-
cellRow,
|
|
1759
|
-
cellColumn,
|
|
1760
|
-
)}
|
|
1761
|
-
{@const cellValue = getCellDisplayValue(
|
|
1762
|
-
cellRow.id,
|
|
1763
|
-
cellColumn.id,
|
|
1764
|
-
baseValue,
|
|
1765
|
-
)}
|
|
1766
|
-
{@const isEditing =
|
|
1767
|
-
ctrl.editingCell?.rowId === cellRow.id &&
|
|
1768
|
-
ctrl.editingCell?.columnId === cellColumn.id}
|
|
1769
|
-
{@const inRowEdit =
|
|
1770
|
-
!!fullRowEdit &&
|
|
1771
|
-
fullRowEdit.rowId === cellRow.id &&
|
|
1772
|
-
cellColumn.id in fullRowEdit.draft}
|
|
1773
|
-
{@const cellSel = cellSelectionState(rowIndex, colIndex)}
|
|
1774
|
-
{@const fillEdges = fillMarqueeEdges(rowIndex, colIndex)}
|
|
1775
|
-
{@const userCellClass = computeCellClass(
|
|
1776
|
-
cellRow,
|
|
1777
|
-
cellColumn,
|
|
1778
|
-
)}
|
|
1779
|
-
{@const cellTooltip = computeCellTooltip(
|
|
1780
|
-
cellRow,
|
|
1781
|
-
cellColumn,
|
|
1782
|
-
)}
|
|
1783
|
-
{@const cellValidity = computeCellValidity(
|
|
1784
|
-
cellRow,
|
|
1785
|
-
cellColumn,
|
|
1786
|
-
)}
|
|
1787
|
-
{@const cellNote = computeCellNote(
|
|
1788
|
-
cellRow,
|
|
1789
|
-
cellColumn,
|
|
1790
|
-
)}
|
|
1791
|
-
{@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)}
|
|
1792
1870
|
<td
|
|
1793
|
-
rowspan={draw && draw.rowSpan > 1 ? draw.rowSpan : undefined}
|
|
1794
|
-
colspan={draw && draw.colSpan > 1 ? draw.colSpan : undefined}
|
|
1795
|
-
data-merge-origin={draw ? `${draw.merge.rowIndex}:${draw.merge.colIndex}` : undefined}
|
|
1796
|
-
class:sv-grid-cell-merged={!!draw}
|
|
1797
|
-
class={`sv-grid-cell ${userCellClass}`}
|
|
1798
|
-
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)}
|
|
1799
1877
|
class:sv-grid-cell-collapsed={!!collapsedColumns[rendered.column.id]}
|
|
1800
|
-
class:sv-grid-cell-active={!!
|
|
1801
|
-
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}
|
|
1802
1880
|
class:sv-grid-cell-cf={hasConditionalFormats}
|
|
1803
|
-
class:sv-grid-cell-invalid={cellValidity.invalid}
|
|
1804
|
-
class:sv-grid-cell-has-note={cellNote != null}
|
|
1805
|
-
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}
|
|
1806
1884
|
data-svgrid-row={rowIndex}
|
|
1807
|
-
data-svgrid-col={colIndex}
|
|
1885
|
+
data-svgrid-col={c.colIndex}
|
|
1808
1886
|
data-col-id={rendered.column.id}
|
|
1809
1887
|
data-align={getColumnAlign(rendered.column)}
|
|
1810
|
-
data-pinned={
|
|
1811
|
-
|
|
1812
|
-
data-
|
|
1813
|
-
data-range-
|
|
1814
|
-
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
|
|
1815
1892
|
? "true"
|
|
1816
1893
|
: undefined}
|
|
1817
|
-
data-range-left={
|
|
1894
|
+
data-range-left={ui?.sel?.edges?.left
|
|
1818
1895
|
? "true"
|
|
1819
1896
|
: undefined}
|
|
1820
|
-
data-range-right={
|
|
1897
|
+
data-range-right={ui?.sel?.edges?.right
|
|
1821
1898
|
? "true"
|
|
1822
1899
|
: undefined}
|
|
1823
|
-
data-fill-preview={isInFillPreview(rowIndex, colIndex)
|
|
1900
|
+
data-fill-preview={isInFillPreview(rowIndex, c.colIndex)
|
|
1824
1901
|
? "true"
|
|
1825
1902
|
: undefined}
|
|
1826
|
-
data-fill-top={
|
|
1827
|
-
data-fill-bottom={
|
|
1828
|
-
data-fill-left={
|
|
1829
|
-
data-fill-right={
|
|
1830
|
-
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) : ""}`}
|
|
1831
1908
|
onpointerdown={(event) =>
|
|
1832
|
-
onCellPointerDown(rowIndex, colIndex, event)}
|
|
1833
|
-
|
|
1834
|
-
|
|
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);
|
|
1835
1919
|
// The column tooltip fires on whole-cell hover; a
|
|
1836
1920
|
// per-cell note waits for its corner below. A
|
|
1837
1921
|
// validation message wins over the column's tooltip.
|
|
1838
1922
|
const tip =
|
|
1839
|
-
cellValidity.invalid && cellValidity.message
|
|
1840
|
-
? cellValidity.message
|
|
1841
|
-
: cellTooltip;
|
|
1923
|
+
c.cellValidity.invalid && c.cellValidity.message
|
|
1924
|
+
? c.cellValidity.message
|
|
1925
|
+
: c.cellTooltip;
|
|
1842
1926
|
if (tip)
|
|
1843
1927
|
showTooltipFor(
|
|
1844
1928
|
event.currentTarget as HTMLElement,
|
|
1845
1929
|
tip,
|
|
1846
1930
|
);
|
|
1847
1931
|
}}
|
|
1848
|
-
|
|
1932
|
+
onpointerout={(event) => {
|
|
1933
|
+
if (crossedCell(event)) hideTooltip();
|
|
1934
|
+
}}
|
|
1849
1935
|
ondblclick={() =>
|
|
1850
|
-
emitCellDoubleClick(rowIndex, colIndex)}
|
|
1851
|
-
onclick={() => onCellClick(rowIndex, colIndex)}
|
|
1936
|
+
emitCellDoubleClick(rowIndex, c.colIndex)}
|
|
1937
|
+
onclick={() => onCellClick(rowIndex, c.colIndex)}
|
|
1852
1938
|
oncontextmenu={(event) =>
|
|
1853
1939
|
openContextMenu(
|
|
1854
1940
|
event,
|
|
1855
1941
|
rowIndex,
|
|
1856
|
-
colIndex,
|
|
1942
|
+
c.colIndex,
|
|
1857
1943
|
rendered.column.id,
|
|
1858
1944
|
)}
|
|
1859
1945
|
use:cellFlashAction={{
|
|
1860
|
-
rowId: cellRow.id,
|
|
1861
|
-
|
|
1862
|
-
|
|
1946
|
+
rowId: c.cellRow.id,
|
|
1947
|
+
columnId: c.cellColumn.id,
|
|
1948
|
+
value: c.cellValue,
|
|
1949
|
+
active: !!c.cellColumn.columnDef.cellFlash,
|
|
1863
1950
|
className: flashClassFor(
|
|
1864
|
-
cellColumn.columnDef.cellFlash,
|
|
1951
|
+
c.cellColumn.columnDef.cellFlash,
|
|
1865
1952
|
),
|
|
1866
1953
|
}}
|
|
1867
1954
|
role="gridcell"
|
|
1868
|
-
id={getGridCellDomId(ctrl.gridDomId, rowIndex, colIndex)}
|
|
1869
|
-
aria-colindex={colIndex + 1}
|
|
1955
|
+
id={getGridCellDomId(ctrl.gridDomId, rowIndex, c.colIndex)}
|
|
1956
|
+
aria-colindex={c.colIndex + 1}
|
|
1870
1957
|
aria-rowindex={rowIndex + 1}
|
|
1871
|
-
aria-selected={isRowSelected(row.id)}
|
|
1958
|
+
aria-selected={isRowSelected(row.id, row.original)}
|
|
1872
1959
|
>
|
|
1873
|
-
{#if
|
|
1960
|
+
{#if ui && (ui.editing || ui.rowEdit)}
|
|
1874
1961
|
<!-- The editing cell stays empty until the lazy
|
|
1875
1962
|
editor chunk lands, exactly as it already did
|
|
1876
1963
|
for the dropdown / date editors. Falling back
|
|
1877
1964
|
to the read-only value here would flash stale
|
|
1878
1965
|
content into a cell the user is editing. -->
|
|
1879
1966
|
{#if CellEditor}
|
|
1880
|
-
<CellEditor {ctrl} column={cellColumn} row={cellRow} fullRow={
|
|
1967
|
+
<CellEditor {ctrl} column={c.cellColumn} row={c.cellRow} fullRow={!!ui?.rowEdit} />
|
|
1881
1968
|
{/if}
|
|
1882
1969
|
{:else}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
{/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
|
+
>
|
|
1892
1978
|
{/if}
|
|
1893
|
-
{#if !
|
|
1979
|
+
{#if !ui?.editing && ui?.sel?.fillHandle}
|
|
1894
1980
|
<!-- Excel-style fill handle: drag down/right to
|
|
1895
1981
|
extend the selection and pattern-fill the new
|
|
1896
1982
|
cells on release. Rendered inside the bottom-
|
|
@@ -1903,7 +1989,7 @@
|
|
|
1903
1989
|
aria-label="Fill handle"
|
|
1904
1990
|
tabindex={-1}
|
|
1905
1991
|
onpointerdown={(event) =>
|
|
1906
|
-
startFillDrag(event, rowIndex, colIndex)}
|
|
1992
|
+
startFillDrag(event, rowIndex, c.colIndex)}
|
|
1907
1993
|
ondblclick={(event) => {
|
|
1908
1994
|
// Not the cell's double-click: this fills
|
|
1909
1995
|
// down as far as the neighbour column goes.
|
|
@@ -1913,16 +1999,16 @@
|
|
|
1913
1999
|
}}
|
|
1914
2000
|
></div>
|
|
1915
2001
|
{/if}
|
|
1916
|
-
{#if cellValidity.invalid && cellValidity.message}
|
|
2002
|
+
{#if c.cellValidity.invalid && c.cellValidity.message}
|
|
1917
2003
|
<!-- The validation message, for assistive tech: the
|
|
1918
2004
|
tooltip above is mouse-only, and unlike a title
|
|
1919
2005
|
a visually-hidden span adds no second native
|
|
1920
2006
|
tooltip competing with the custom one. -->
|
|
1921
2007
|
<span class="sv-grid-sr-only"
|
|
1922
|
-
>{cellValidity.message}</span
|
|
2008
|
+
>{c.cellValidity.message}</span
|
|
1923
2009
|
>
|
|
1924
2010
|
{/if}
|
|
1925
|
-
{#if cellNote != null && !
|
|
2011
|
+
{#if c.cellNote != null && !ui?.editing}
|
|
1926
2012
|
<span
|
|
1927
2013
|
class="sv-grid-cell-note-corner"
|
|
1928
2014
|
aria-label="Note"
|
|
@@ -1930,7 +2016,7 @@
|
|
|
1930
2016
|
event.stopPropagation();
|
|
1931
2017
|
showTooltipFor(
|
|
1932
2018
|
event.currentTarget as HTMLElement,
|
|
1933
|
-
cellNote,
|
|
2019
|
+
c.cellNote ?? "",
|
|
1934
2020
|
);
|
|
1935
2021
|
}}
|
|
1936
2022
|
onpointerleave={(event) => {
|
|
@@ -1942,7 +2028,7 @@
|
|
|
1942
2028
|
</td>
|
|
1943
2029
|
{/if}
|
|
1944
2030
|
{/each}
|
|
1945
|
-
{#if
|
|
2031
|
+
{#if showRightSpacer}
|
|
1946
2032
|
<td
|
|
1947
2033
|
class="sv-grid-cell sv-grid-cell-spacer"
|
|
1948
2034
|
aria-hidden="true"
|
|
@@ -1957,6 +2043,23 @@
|
|
|
1957
2043
|
outside the rendered window), so this one carries no cell ids, no
|
|
1958
2044
|
row index attributes and is hidden from assistive tech; the group
|
|
1959
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}
|
|
1960
2063
|
{#snippet stickyGroupRow(row: Row<TData>, rowIndex: number, top: number, size: number, last: boolean)}
|
|
1961
2064
|
{#if isGroupRow(row) && !groupColumnMode}
|
|
1962
2065
|
<tr
|
|
@@ -1989,7 +2092,7 @@
|
|
|
1989
2092
|
<td
|
|
1990
2093
|
class="sv-grid-cell sv-grid-row-number-cell"
|
|
1991
2094
|
style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; left: 0;`}
|
|
1992
|
-
>{rowIndex + 1}</td
|
|
2095
|
+
><span class="sv-grid-cell-line">{rowIndex + 1}</span></td
|
|
1993
2096
|
>
|
|
1994
2097
|
{/if}
|
|
1995
2098
|
{#if showRowSelectionEffective}
|
|
@@ -2012,6 +2115,7 @@
|
|
|
2012
2115
|
{/if}
|
|
2013
2116
|
{#each renderedColumns as rendered (rendered.column.id)}
|
|
2014
2117
|
{@const cellValue = getCellDisplayValue(row.id, rendered.column.id, getColumnBaseValue(row, rendered.column))}
|
|
2118
|
+
{#if rendered.gap > 0}{@render cellGap(rendered.gap)}{/if}
|
|
2015
2119
|
<td
|
|
2016
2120
|
class={`sv-grid-cell ${computeCellClass(row, rendered.column)}`}
|
|
2017
2121
|
class:sv-grid-cell-cf={hasConditionalFormats}
|
|
@@ -2027,7 +2131,7 @@
|
|
|
2027
2131
|
{/if}
|
|
2028
2132
|
</td>
|
|
2029
2133
|
{/each}
|
|
2030
|
-
{#if
|
|
2134
|
+
{#if showRightSpacer}
|
|
2031
2135
|
<td
|
|
2032
2136
|
class="sv-grid-cell sv-grid-cell-spacer"
|
|
2033
2137
|
style={`width: ${columnWindowRightSpacer}px; min-width: ${columnWindowRightSpacer}px; max-width: ${columnWindowRightSpacer}px;`}
|
|
@@ -2050,7 +2154,9 @@
|
|
|
2050
2154
|
<td
|
|
2051
2155
|
class="sv-grid-cell sv-grid-row-number-cell"
|
|
2052
2156
|
style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; inset-inline-start: 0;`}
|
|
2053
|
-
|
|
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
|
|
2054
2160
|
>
|
|
2055
2161
|
{/if}
|
|
2056
2162
|
{#if showRowSelectionEffective}
|
|
@@ -2078,16 +2184,17 @@
|
|
|
2078
2184
|
rowData,
|
|
2079
2185
|
rendered.column,
|
|
2080
2186
|
)}
|
|
2187
|
+
{#if rendered.gap > 0}{@render cellGap(rendered.gap)}{/if}
|
|
2081
2188
|
<td
|
|
2082
2189
|
class={`sv-grid-cell ${userCellClass}`}
|
|
2083
2190
|
data-col-id={rendered.column.id}
|
|
2084
2191
|
data-align={getColumnAlign(rendered.column)}
|
|
2085
2192
|
data-pinned={isColumnPinned(rendered.column.id) ?? undefined}
|
|
2086
2193
|
style={`width: ${rendered.item.size}px; min-width: ${rendered.item.size}px; max-width: ${rendered.item.size}px; ${cellPinStyle(rendered.column.id)}`}
|
|
2087
|
-
>{formatPinnedValue(rendered.column, value)}</td
|
|
2194
|
+
><span class="sv-grid-cell-text">{formatPinnedValue(rendered.column, value)}</span></td
|
|
2088
2195
|
>
|
|
2089
2196
|
{/each}
|
|
2090
|
-
{#if
|
|
2197
|
+
{#if showRightSpacer}
|
|
2091
2198
|
<td
|
|
2092
2199
|
class="sv-grid-cell sv-grid-cell-spacer"
|
|
2093
2200
|
aria-hidden="true"
|
|
@@ -2204,7 +2311,9 @@
|
|
|
2204
2311
|
<table
|
|
2205
2312
|
bind:this={ctrl.gridRootEl}
|
|
2206
2313
|
class="sv-grid-table"
|
|
2314
|
+
class:sv-grid-table-flex={ctrl.flexRows}
|
|
2207
2315
|
class:sv-grid-no-row-hover={opt.enableRowHover !== true}
|
|
2316
|
+
class:sv-grid-numbers-beside-toggle={showDetailToggleEffective && !showRowSelectionEffective}
|
|
2208
2317
|
{...getGridRootA11yProps({
|
|
2209
2318
|
activeDescendantId,
|
|
2210
2319
|
rowCount: allRows.length,
|
|
@@ -2213,7 +2322,7 @@
|
|
|
2213
2322
|
})}
|
|
2214
2323
|
onkeydown={onGridKeyDown}
|
|
2215
2324
|
onpaste={onGridPaste}
|
|
2216
|
-
style={`min-width: ${totalColumnWidth}px;`}
|
|
2325
|
+
style={`min-width: ${totalColumnWidth + ctrl.systemColumnsWidth}px;`}
|
|
2217
2326
|
>
|
|
2218
2327
|
<!-- svelte-ignore a11y_no_redundant_roles -->
|
|
2219
2328
|
<thead class="sv-grid-head" bind:this={ctrl.theadEl} role="rowgroup">
|
|
@@ -2258,6 +2367,7 @@
|
|
|
2258
2367
|
></th>
|
|
2259
2368
|
{/if}
|
|
2260
2369
|
{#each row.cells as cell (cell.key)}
|
|
2370
|
+
{#if cell.gapBefore}{@render columnGap(cell.gapBefore)}{/if}
|
|
2261
2371
|
<th
|
|
2262
2372
|
class="sv-grid-column sv-grid-group-header-cell"
|
|
2263
2373
|
class:sv-grid-group-header-placeholder={cell.isPlaceholder}
|
|
@@ -2294,7 +2404,7 @@
|
|
|
2294
2404
|
{/if}
|
|
2295
2405
|
</th>
|
|
2296
2406
|
{/each}
|
|
2297
|
-
{#if
|
|
2407
|
+
{#if showRightSpacer}
|
|
2298
2408
|
<th
|
|
2299
2409
|
class="sv-grid-column sv-grid-column-spacer"
|
|
2300
2410
|
aria-hidden="true"
|
|
@@ -2362,8 +2472,9 @@
|
|
|
2362
2472
|
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
2363
2473
|
></th>
|
|
2364
2474
|
{/if}
|
|
2365
|
-
{#each renderedColumns as rendered (rendered.
|
|
2475
|
+
{#each renderedColumns as rendered (rendered.key)}
|
|
2366
2476
|
{@const header = headerGroup.headers[rendered.item.index]}
|
|
2477
|
+
{#if rendered.gap > 0}{@render columnGap(rendered.gap)}{/if}
|
|
2367
2478
|
{#if header}
|
|
2368
2479
|
{@const sortDirection =
|
|
2369
2480
|
sortDirectionByColumn[header.column.id]}
|
|
@@ -2456,9 +2567,12 @@
|
|
|
2456
2567
|
}}
|
|
2457
2568
|
>
|
|
2458
2569
|
{#if rendered instanceof RenderSnippetConfig}
|
|
2459
|
-
|
|
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}
|
|
2460
2574
|
{:else if rendered instanceof RenderComponentConfig}
|
|
2461
|
-
<rendered.component {...rendered.props ?? {}} />
|
|
2575
|
+
{#key header.column.id}<rendered.component {...rendered.props ?? {}} />{/key}
|
|
2462
2576
|
{:else if typeof rendered === "string" || typeof rendered === "number"}
|
|
2463
2577
|
{rendered}
|
|
2464
2578
|
{:else}
|
|
@@ -2570,7 +2684,7 @@
|
|
|
2570
2684
|
</th>
|
|
2571
2685
|
{/if}
|
|
2572
2686
|
{/each}
|
|
2573
|
-
{#if
|
|
2687
|
+
{#if showRightSpacer}
|
|
2574
2688
|
<th
|
|
2575
2689
|
class="sv-grid-column sv-grid-column-spacer"
|
|
2576
2690
|
aria-hidden="true"
|
|
@@ -2611,10 +2725,11 @@
|
|
|
2611
2725
|
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
2612
2726
|
></th>
|
|
2613
2727
|
{/if}
|
|
2614
|
-
{#each renderedColumns as rendered (rendered.
|
|
2728
|
+
{#each renderedColumns as rendered (rendered.key)}
|
|
2615
2729
|
{@const activeOperator =
|
|
2616
2730
|
filterMenuValues[rendered.column.id]?.operator ??
|
|
2617
2731
|
defaultOperatorFor(rendered.column)}
|
|
2732
|
+
{#if rendered.gap > 0}{@render columnGap(rendered.gap)}{/if}
|
|
2618
2733
|
<th
|
|
2619
2734
|
class="sv-grid-column"
|
|
2620
2735
|
data-pinned={isColumnPinned(rendered.column.id) ??
|
|
@@ -2794,7 +2909,7 @@
|
|
|
2794
2909
|
</div>
|
|
2795
2910
|
</th>
|
|
2796
2911
|
{/each}
|
|
2797
|
-
{#if
|
|
2912
|
+
{#if showRightSpacer}
|
|
2798
2913
|
<th
|
|
2799
2914
|
class="sv-grid-column sv-grid-column-spacer"
|
|
2800
2915
|
aria-hidden="true"
|
|
@@ -2937,17 +3052,18 @@
|
|
|
2937
3052
|
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
2938
3053
|
></th>
|
|
2939
3054
|
{/if}
|
|
2940
|
-
{#each renderedColumns as rendered (rendered.
|
|
3055
|
+
{#each renderedColumns as rendered (rendered.key)}
|
|
3056
|
+
{#if rendered.gap > 0}{@render columnGap(rendered.gap)}{/if}
|
|
2941
3057
|
<th
|
|
2942
3058
|
class="sv-grid-column sv-grid-summary-column"
|
|
2943
3059
|
data-pinned={isColumnPinned(rendered.column.id) ??
|
|
2944
3060
|
undefined}
|
|
2945
3061
|
style={`width: ${rendered.item.size}px; min-width: ${rendered.item.size}px; max-width: ${rendered.item.size}px; ${cellPinStyle(rendered.column.id)}`}
|
|
2946
3062
|
>
|
|
2947
|
-
{summaryByColumn[rendered.column.id] ?? ""}
|
|
3063
|
+
<span class="sv-grid-cell-text">{summaryByColumn[rendered.column.id] ?? ""}</span>
|
|
2948
3064
|
</th>
|
|
2949
3065
|
{/each}
|
|
2950
|
-
{#if
|
|
3066
|
+
{#if showRightSpacer}
|
|
2951
3067
|
<th
|
|
2952
3068
|
class="sv-grid-column sv-grid-column-spacer"
|
|
2953
3069
|
aria-hidden="true"
|
|
@@ -3124,10 +3240,12 @@
|
|
|
3124
3240
|
<div class="sv-grid-skeleton" aria-hidden="true">
|
|
3125
3241
|
{#each Array(opt.loadingSkeletonRows ?? 8) as _, r (r)}
|
|
3126
3242
|
<div class="sv-grid-skeleton-row">
|
|
3127
|
-
|
|
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)}
|
|
3128
3246
|
<div
|
|
3129
3247
|
class="sv-grid-skeleton-cell"
|
|
3130
|
-
style={`width:${
|
|
3248
|
+
style={`width:${rendered.item.size}px`}
|
|
3131
3249
|
>
|
|
3132
3250
|
<span class="sv-grid-skeleton-bar"></span>
|
|
3133
3251
|
</div>
|