@svgrid/grid 3.0.3 → 3.0.5
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 +6 -5
- package/dist/GridMenus.svelte +44 -2
- package/dist/SvChartMenu.svelte +69 -0
- package/dist/SvChartMenu.svelte.d.ts +11 -0
- package/dist/SvChartPanes.svelte +113 -0
- package/dist/SvChartPanes.svelte.d.ts +20 -0
- package/dist/SvGrid.controller.svelte.d.ts +122 -41
- package/dist/SvGrid.controller.svelte.js +846 -207
- package/dist/SvGrid.css +178 -2
- package/dist/SvGrid.svelte +717 -720
- package/dist/SvGrid.types.d.ts +566 -12
- package/dist/SvGridCellEditor.svelte +17 -0
- package/dist/SvGridChart.svelte +2257 -329
- package/dist/SvGridChart.svelte.d.ts +6 -106
- package/dist/SvGridChart.types.d.ts +338 -0
- package/dist/SvGridChart.types.js +1 -0
- package/dist/SvGridChartBuilder.svelte +323 -0
- package/dist/SvGridChartBuilder.svelte.d.ts +34 -0
- package/dist/SvGridChartPanel.svelte +221 -275
- package/dist/SvGridChartPickers.svelte +417 -0
- package/dist/SvGridChartPickers.svelte.d.ts +70 -0
- package/dist/SvGridChartView.svelte +13 -14
- package/dist/SvModal.svelte +7 -1
- package/dist/SvModal.svelte.d.ts +5 -0
- package/dist/ai.d.ts +40 -8
- package/dist/ai.js +135 -24
- package/dist/aria.d.ts +11 -0
- package/dist/build-api.js +119 -28
- package/dist/cdn/GridMenus-CID62rlI.js +636 -0
- package/dist/cdn/GridMenus-DLL-qZeu.js +635 -0
- package/dist/cdn/SvChartMenu-Bl6PBbkT.js +58 -0
- package/dist/cdn/SvChartMenu-FBSMINA6.js +59 -0
- package/dist/cdn/{SvDateRangeInput-DMLKmGEc.js → SvDateRangeInput-BeU_TkbQ.js} +15 -14
- package/dist/cdn/{SvDateRangeInput-CaOuMs8O.js → SvDateRangeInput-yX8vzleW.js} +38 -37
- package/dist/cdn/{SvDateTimePicker-sonaH0oh.js → SvDateTimePicker-DQwt4UAs.js} +139 -180
- package/dist/cdn/SvDateTimePicker-vNU6bZ-q.js +775 -0
- package/dist/cdn/{SvGridCellEditor-CjEJMJKc.js → SvGridCellEditor-B1p-vCK5.js} +188 -178
- package/dist/cdn/{SvGridCellEditor-BWnTi2N7.js → SvGridCellEditor-D_0q4xAu.js} +98 -88
- package/dist/cdn/SvGridChart-C3EWAZaM.js +3574 -0
- package/dist/cdn/SvGridChart-DzLkSwCH.js +3572 -0
- package/dist/cdn/SvGridChartBuilder-BE2T1ykB.js +783 -0
- package/dist/cdn/SvGridChartBuilder-CfII62sZ.js +784 -0
- package/dist/cdn/SvGridChartPanel-CCX5_Wgd.js +675 -0
- package/dist/cdn/SvGridChartPanel-xf4sWVTo.js +699 -0
- package/dist/cdn/SvGridChartView-CfuXmY5I.js +48 -0
- package/dist/cdn/SvGridChartView-DYwabQWj.js +47 -0
- package/dist/cdn/SvMenuList-CLN8OuIK.js +452 -0
- package/dist/cdn/SvMenuList-DkGhNKjf.js +451 -0
- package/dist/cdn/SvModal-BAE-pjZX.js +396 -0
- package/dist/cdn/SvModal-CIZWCcad.js +397 -0
- package/dist/cdn/chart-Dz7SqMXH.js +4341 -0
- package/dist/cdn/chart-export-BXqueeWJ.js +135 -0
- package/dist/cdn/chart-export-pdf-DONFUnNY.js +110 -0
- package/dist/cdn/chart-panel-messages-CUbf2R4i.js +1052 -0
- package/dist/cdn/chart-panel-messages-CmNrMdsr.js +1053 -0
- package/dist/cdn/chart-summary-BJW_tg_X.js +138 -0
- package/dist/cdn/chart-trend-CaN9mDEV.js +50 -0
- package/dist/cdn/chart-validate-HcOVFAfJ.js +188 -0
- package/dist/cdn/{column-resize-DsfNXMom.js → column-resize-DpmOLfRp.js} +3 -3
- package/dist/cdn/{date-format-BnnHlqGw.js → date-format-CtquV-p3.js} +583 -603
- package/dist/cdn/{date-format-BNii4zeD.js → date-format-D6KFzU_W.js} +347 -367
- package/dist/cdn/dismissable-DAHetSNk.js +44 -0
- package/dist/cdn/editor-contract-LXgQJAgd.js +22 -0
- package/dist/cdn/focus-trap-BBOQYBma.js +65 -0
- package/dist/cdn/{row-resize-BRcimkUT.js → row-resize-niQCp040.js} +44 -36
- package/dist/cdn/server-block-cache-CGoWz-87.js +237 -0
- package/dist/cdn/{src-D6XvA1Ij.js → src-Bklofsad.js} +10615 -10155
- package/dist/cdn/{src-Bugf5XlI.js → src-jcFMkBnN.js} +7111 -6651
- package/dist/cdn/svgrid.js +27 -15
- package/dist/cdn/svgrid.svelte-external.js +27 -15
- package/dist/cdn/validate-C7Rn_Bns.js +76 -0
- package/dist/chart-axes.d.ts +53 -0
- package/dist/chart-axes.js +351 -0
- package/dist/chart-cartesian.d.ts +88 -0
- package/dist/chart-cartesian.js +1862 -0
- package/dist/chart-decimate.d.ts +51 -0
- package/dist/chart-decimate.js +199 -0
- package/dist/chart-export-pdf.d.ts +47 -0
- package/dist/chart-export-pdf.js +187 -0
- package/dist/chart-export.d.ts +2 -1
- package/dist/chart-export.js +18 -3
- package/dist/chart-financial.d.ts +91 -0
- package/dist/chart-financial.js +175 -0
- package/dist/chart-flow.d.ts +10 -0
- package/dist/chart-flow.js +298 -0
- package/dist/chart-format.d.ts +18 -0
- package/dist/chart-format.js +65 -0
- package/dist/chart-grid.d.ts +8 -0
- package/dist/chart-grid.js +190 -0
- package/dist/chart-hierarchy.d.ts +17 -0
- package/dist/chart-hierarchy.js +146 -0
- package/dist/chart-indicators.d.ts +115 -0
- package/dist/chart-indicators.js +389 -0
- package/dist/chart-messages.d.ts +95 -0
- package/dist/chart-messages.js +92 -0
- package/dist/chart-motion.d.ts +16 -0
- package/dist/chart-motion.js +141 -0
- package/dist/chart-panel-messages.d.ts +265 -0
- package/dist/chart-panel-messages.js +285 -0
- package/dist/chart-pivot.d.ts +87 -0
- package/dist/chart-pivot.js +156 -0
- package/dist/chart-polar.d.ts +25 -0
- package/dist/chart-polar.js +700 -0
- package/dist/chart-samples.d.ts +16 -0
- package/dist/chart-samples.js +153 -0
- package/dist/chart-scale.d.ts +113 -0
- package/dist/chart-scale.js +405 -0
- package/dist/chart-stats.d.ts +111 -0
- package/dist/chart-stats.js +419 -0
- package/dist/chart-stream.d.ts +45 -0
- package/dist/chart-stream.js +91 -0
- package/dist/chart-summary.d.ts +27 -0
- package/dist/chart-summary.js +205 -0
- package/dist/chart-sync.svelte.d.ts +32 -0
- package/dist/chart-sync.svelte.js +23 -0
- package/dist/chart-table.d.ts +35 -0
- package/dist/chart-table.js +165 -0
- package/dist/chart-trend.d.ts +19 -0
- package/dist/chart-trend.js +61 -0
- package/dist/chart-types.d.ts +1456 -0
- package/dist/chart-types.js +6 -0
- package/dist/chart-validate.d.ts +43 -0
- package/dist/chart-validate.js +272 -0
- package/dist/chart-zoom.d.ts +40 -0
- package/dist/chart-zoom.js +145 -0
- package/dist/chart.d.ts +186 -865
- package/dist/chart.js +460 -2204
- package/dist/clipboard.d.ts +1 -0
- package/dist/clipboard.js +137 -18
- package/dist/column-resize.d.ts +3 -0
- package/dist/column-resize.js +4 -1
- package/dist/columns.js +3 -0
- package/dist/command-context.d.ts +19 -0
- package/dist/command-context.js +116 -0
- package/dist/core.d.ts +7 -0
- package/dist/core.js +36 -13
- package/dist/createPopoverSelect.svelte.d.ts +2 -2
- package/dist/editing.d.ts +2 -8
- package/dist/editing.js +272 -33
- package/dist/fill-patterns.d.ts +0 -5
- package/dist/fill-patterns.js +56 -2
- package/dist/gantt-view.svelte.d.ts +24 -0
- package/dist/gantt-view.svelte.js +13 -0
- package/dist/grid-icons.d.ts +2 -2
- package/dist/grid-icons.js +2 -2
- package/dist/grid-messages.d.ts +16 -3
- package/dist/grid-messages.js +10 -0
- package/dist/history.d.ts +83 -0
- package/dist/history.js +133 -0
- package/dist/index.d.ts +29 -10
- package/dist/index.js +23 -5
- package/dist/keyboard-handlers.js +88 -31
- package/dist/keyboard.d.ts +55 -0
- package/dist/keyboard.js +132 -0
- package/dist/menus.d.ts +3 -1
- package/dist/menus.js +7 -2
- package/dist/merges.d.ts +97 -0
- package/dist/merges.js +147 -0
- package/dist/row-model.d.ts +133 -0
- package/dist/row-model.js +16 -0
- package/dist/row-resize.d.ts +3 -0
- package/dist/row-resize.js +14 -0
- package/dist/scroll-sync.d.ts +1 -0
- package/dist/scroll-sync.js +24 -0
- package/dist/selection.d.ts +18 -1
- package/dist/selection.js +138 -18
- package/dist/server-block-cache.d.ts +214 -0
- package/dist/server-block-cache.js +531 -0
- package/dist/server-data-source.d.ts +240 -6
- package/dist/server-data-source.js +220 -20
- package/dist/server.d.ts +13 -0
- package/dist/server.js +13 -0
- package/dist/shortcut-registry.d.ts +145 -0
- package/dist/shortcut-registry.js +44 -0
- package/dist/svgrid-wrapper.types.d.ts +213 -22
- package/dist/validate.d.ts +4 -0
- package/dist/validate.js +21 -3
- package/dist/virtualization/virtualizer.js +38 -17
- package/package.json +11 -1
- package/src/GridMenus.svelte +44 -2
- package/src/SvChartMenu.svelte +69 -0
- package/src/SvChartPanes.svelte +113 -0
- package/src/SvChartPanes.test.ts +66 -0
- package/src/SvGrid.controller.svelte.ts +819 -179
- package/src/SvGrid.css +178 -2
- package/src/SvGrid.svelte +717 -720
- package/src/SvGrid.types.ts +564 -11
- package/src/SvGridCellEditor.svelte +17 -0
- package/src/SvGridChart.svelte +2257 -329
- package/src/SvGridChart.test.ts +1419 -1
- package/src/SvGridChart.types.ts +336 -0
- package/src/SvGridChartBuilder.svelte +323 -0
- package/src/SvGridChartPanel.svelte +221 -275
- package/src/SvGridChartPickers.svelte +417 -0
- package/src/SvGridChartView.svelte +13 -14
- package/src/SvModal.svelte +7 -1
- package/src/SvModal.test.ts +9 -0
- package/src/ai.test.ts +57 -2
- package/src/ai.ts +166 -22
- package/src/aria.d.ts +11 -0
- package/src/build-api.coverage.test.ts +184 -0
- package/src/build-api.ts +118 -22
- package/src/chart-axes.test.ts +573 -0
- package/src/chart-axes.ts +342 -0
- package/src/chart-cartesian.ts +1863 -0
- package/src/chart-decimate.test.ts +249 -0
- package/src/chart-decimate.ts +186 -0
- package/src/chart-export-pdf.test.ts +91 -0
- package/src/chart-export-pdf.ts +207 -0
- package/src/chart-export.test.ts +14 -0
- package/src/chart-export.ts +13 -3
- package/src/chart-financial.ts +208 -0
- package/src/chart-flow.ts +286 -0
- package/src/chart-format.ts +67 -0
- package/src/chart-grid.ts +179 -0
- package/src/chart-hierarchy.ts +135 -0
- package/src/chart-indicators.test.ts +443 -0
- package/src/chart-indicators.ts +384 -0
- package/src/chart-messages.test.ts +21 -0
- package/src/chart-messages.ts +184 -0
- package/src/chart-motion.test.ts +71 -0
- package/src/chart-motion.ts +148 -0
- package/src/chart-panel-messages.test.ts +29 -0
- package/src/chart-panel-messages.ts +550 -0
- package/src/chart-pivot.test.ts +54 -0
- package/src/chart-pivot.ts +225 -0
- package/src/chart-polar.ts +674 -0
- package/src/chart-samples.ts +106 -0
- package/src/chart-scale.ts +394 -0
- package/src/chart-series-types.test.ts +714 -0
- package/src/chart-stats.ts +409 -0
- package/src/chart-stream.test.ts +75 -0
- package/src/chart-stream.ts +114 -0
- package/src/chart-summary.test.ts +90 -0
- package/src/chart-summary.ts +197 -0
- package/src/chart-sync.svelte.ts +41 -0
- package/src/chart-table.test.ts +76 -0
- package/src/chart-table.ts +178 -0
- package/src/chart-trend.ts +47 -0
- package/src/chart-types.ts +1309 -0
- package/src/chart-validate.test.ts +100 -0
- package/src/chart-validate.ts +247 -0
- package/src/chart-zoom.test.ts +137 -0
- package/src/chart-zoom.ts +124 -0
- package/src/chart.coverage.test.ts +81 -0
- package/src/chart.test.ts +56 -3
- package/src/chart.ts +557 -2784
- package/src/clipboard.test.ts +151 -0
- package/src/clipboard.ts +145 -16
- package/src/column-resize.ts +7 -1
- package/src/columns.ts +2 -0
- package/src/command-context.test.ts +200 -0
- package/src/command-context.ts +137 -0
- package/src/core.rowmodel-cache.test.ts +51 -0
- package/src/core.ts +40 -12
- package/src/editing.test.ts +281 -3
- package/src/editing.ts +264 -38
- package/src/fill-patterns.test.ts +35 -0
- package/src/fill-patterns.ts +56 -2
- package/src/gantt-stub.test.svelte +38 -0
- package/src/gantt-view.svelte.ts +35 -0
- package/src/grid-icons.ts +2 -2
- package/src/grid-messages.ts +28 -3
- package/src/history.test.ts +196 -0
- package/src/history.ts +162 -0
- package/src/icon-seam.test.ts +5 -6
- package/src/index.ts +186 -12
- package/src/keyboard-handlers.coverage.test.ts +126 -6
- package/src/keyboard-handlers.ts +91 -30
- package/src/keyboard-shortcuts.seam.test.ts +200 -0
- package/src/keyboard.test.ts +124 -1
- package/src/keyboard.ts +142 -0
- package/src/menus.test.ts +26 -0
- package/src/menus.ts +10 -3
- package/src/merges.test.ts +112 -0
- package/src/merges.ts +168 -0
- package/src/pivot.grid.test.ts +40 -5
- package/src/row-model.ts +146 -0
- package/src/row-resize.test.ts +44 -0
- package/src/row-resize.ts +17 -0
- package/src/scroll-sync.test.ts +42 -1
- package/src/scroll-sync.ts +25 -0
- package/src/selection.multi-range.test.ts +34 -0
- package/src/selection.test.ts +33 -2
- package/src/selection.ts +143 -21
- package/src/server-block-cache.test.ts +645 -0
- package/src/server-block-cache.ts +677 -0
- package/src/server-data-source.infinite.test.ts +343 -0
- package/src/server-data-source.ts +450 -27
- package/src/server.ts +47 -0
- package/src/shortcut-registry.test.ts +97 -0
- package/src/shortcut-registry.ts +181 -0
- package/src/svgrid-wrapper.types.ts +184 -13
- package/src/svgrid.behavior.test.ts +135 -0
- package/src/svgrid.charting.test.ts +513 -3
- package/src/svgrid.context-menu.test.ts +31 -0
- package/src/svgrid.gantt-seam.test.ts +199 -0
- package/src/svgrid.new-features.wrapper.test.ts +10 -3
- package/src/svgrid.row-model-prop.svelte.test.ts +352 -0
- package/src/svgrid.row-model-seam.svelte.test.ts +289 -0
- package/src/svgrid.upsell-license.test.ts +7 -4
- package/src/validate.test.ts +29 -0
- package/src/validate.ts +28 -3
- package/src/virtualization/virtualizer.ts +39 -17
- package/dist/SvGroupCell.svelte +0 -141
- package/dist/SvGroupCell.svelte.d.ts +0 -49
- package/dist/SvRowGroupPanel.svelte +0 -186
- package/dist/SvRowGroupPanel.svelte.d.ts +0 -25
- package/dist/cdn/GridMenus-CHOMnW-c.js +0 -602
- package/dist/cdn/GridMenus-DhdPF700.js +0 -603
- package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +0 -816
- package/dist/cdn/SvGridChart-1PMSp2LZ.js +0 -1481
- package/dist/cdn/SvGridChart-D02avhyr.js +0 -1480
- package/dist/cdn/SvGridChartPanel-DJnViOv4.js +0 -834
- package/dist/cdn/SvGridChartPanel-vPTNerFZ.js +0 -810
- package/dist/cdn/SvGridChartView-C--HhdUo.js +0 -56
- package/dist/cdn/SvGridChartView-KyyFG-wY.js +0 -55
- package/dist/cdn/chart-DLg3_zTQ.js +0 -1652
- package/dist/cdn/validate-_CDJzgIo.js +0 -75
- package/dist/server-group-model.d.ts +0 -98
- package/dist/server-group-model.js +0 -263
- package/src/SvGroupCell.svelte +0 -141
- package/src/SvRowGroupPanel.svelte +0 -186
- package/src/server-group-model.test.ts +0 -294
- package/src/server-group-model.ts +0 -370
package/dist/SvGrid.svelte
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
generics="TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData"
|
|
4
4
|
>
|
|
5
5
|
import {
|
|
6
|
-
getGridCellA11yProps,
|
|
7
6
|
getGridCellDomId,
|
|
8
7
|
getGridHeaderA11yProps,
|
|
9
8
|
getGridRootA11yProps,
|
|
@@ -22,6 +21,7 @@
|
|
|
22
21
|
RenderComponentConfig,
|
|
23
22
|
} from "./render-component";
|
|
24
23
|
import { buildSparkline, toSparklineValues } from "./sparkline";
|
|
24
|
+
import { mergeDrawAt } from "./merges";
|
|
25
25
|
import { localizeOperatorLabel } from "./filter-operators";
|
|
26
26
|
import { GRID_ICON_GLYPHS, type GridIconName } from "./grid-icons";
|
|
27
27
|
// The whole editor UI - including the lazy SvGridDropdown / SvDateTimePicker
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
import { getSchedulerView } from "./scheduler-view.svelte";
|
|
55
55
|
import { getSelectionBarView } from "./selection-bar-view.svelte";
|
|
56
56
|
import { getBoardView } from "./board-view.svelte";
|
|
57
|
+
import { getGanttView } from "./gantt-view.svelte";
|
|
57
58
|
import { getChartView } from "./chart-view.svelte";
|
|
58
59
|
let props: Props<TFeatures, TData> = $props();
|
|
59
60
|
// Per-instance base for every DOM id the grid mints. `$props.id()` is stable
|
|
@@ -78,6 +79,9 @@
|
|
|
78
79
|
const boardConfig = $derived(opt.board);
|
|
79
80
|
// Scheduler / calendar mode: same "view of the grid" seam as the board.
|
|
80
81
|
const schedulerConfig = $derived(opt.scheduler);
|
|
82
|
+
// Gantt mode: a task table beside a time chart. Same seam again; the renderer
|
|
83
|
+
// ships in @svgrid/enterprise (enableGanttView).
|
|
84
|
+
const ganttConfig = $derived(opt.gantt);
|
|
81
85
|
// Chart mode: another "view of the grid". Unlike board/scheduler, the renderer
|
|
82
86
|
// is free - a built-in default (SvGridChartView, lazy-loaded below) wraps the
|
|
83
87
|
// standalone SvChart, overridable via the `chart-view` seam (getChartView).
|
|
@@ -387,8 +391,9 @@
|
|
|
387
391
|
);
|
|
388
392
|
|
|
389
393
|
const rowSizePx = (i: number): number => {
|
|
390
|
-
// A height the user dragged
|
|
391
|
-
// where they put it across re-renders
|
|
394
|
+
// A height the user dragged, or set through the api, wins over the
|
|
395
|
+
// declared one, so the row stays where they put it across re-renders
|
|
396
|
+
// and virtualization recycling.
|
|
392
397
|
const dragged = ctrl.rowResizeHeightPx(i);
|
|
393
398
|
if (dragged != null) return dragged;
|
|
394
399
|
const rh = opt.rowHeight;
|
|
@@ -402,10 +407,52 @@
|
|
|
402
407
|
autoRowHeightOn
|
|
403
408
|
? `min-height: ${measuredRowHeightPx(i) ?? rowSizePx(i)}px;`
|
|
404
409
|
: `height: ${rowSizePx(i)}px;`;
|
|
410
|
+
const collapsedColumns = $derived(ctrl.collapsedColumns);
|
|
405
411
|
const columnVirtualizationEnabled = $derived(
|
|
406
412
|
ctrl.columnVirtualizationEnabled,
|
|
407
413
|
);
|
|
408
414
|
const virtualRows = $derived(ctrl.virtualRows);
|
|
415
|
+
// The first body row on screen: where a failed run that started above
|
|
416
|
+
// the window repeats its message.
|
|
417
|
+
const firstRenderedRowIndex = $derived(
|
|
418
|
+
rowVirtualizationEnabled ? (virtualRows[0]?.index ?? 0) : 0,
|
|
419
|
+
);
|
|
420
|
+
const frozenRowCount = $derived(ctrl.frozenRowCount);
|
|
421
|
+
const frozenRowList = $derived(ctrl.frozenRowList);
|
|
422
|
+
// Merged cells. A merge is drawn once per band (the frozen rows, the
|
|
423
|
+
// scrolling body), by the top-left cell of the part the band shows, with
|
|
424
|
+
// its spans clamped to the rendered window: a td never spans into rows
|
|
425
|
+
// the table has not drawn.
|
|
426
|
+
const mergeIndex = $derived(ctrl.mergeIndex);
|
|
427
|
+
const mergeColumnWindow = $derived.by(() => ({
|
|
428
|
+
firstCol: renderedColumns[0]?.item.index ?? 0,
|
|
429
|
+
lastCol: renderedColumns[renderedColumns.length - 1]?.item.index ?? -1,
|
|
430
|
+
}));
|
|
431
|
+
const mergeBodyWindow = $derived.by(() => {
|
|
432
|
+
const cols = mergeColumnWindow;
|
|
433
|
+
if (!rowVirtualizationEnabled) return { firstRow: frozenRowCount, lastRow: allRows.length - 1, ...cols };
|
|
434
|
+
let firstRow = Number.POSITIVE_INFINITY;
|
|
435
|
+
let lastRow = -1;
|
|
436
|
+
for (const item of virtualRows) {
|
|
437
|
+
if (item.index < frozenRowCount) continue;
|
|
438
|
+
if (item.index < firstRow) firstRow = item.index;
|
|
439
|
+
if (item.index > lastRow) lastRow = item.index;
|
|
440
|
+
}
|
|
441
|
+
return { firstRow: Number.isFinite(firstRow) ? firstRow : frozenRowCount, lastRow, ...cols };
|
|
442
|
+
});
|
|
443
|
+
const mergeFrozenWindow = $derived.by(() => ({ firstRow: 0, lastRow: frozenRowCount - 1, ...mergeColumnWindow }));
|
|
444
|
+
/** The width a td spanning `colSpan` rendered columns from `colIndex` takes. */
|
|
445
|
+
function mergedWidth(colIndex: number, colSpan: number, own: number): number {
|
|
446
|
+
if (colSpan <= 1) return own;
|
|
447
|
+
let total = 0;
|
|
448
|
+
for (const rendered of renderedColumns) {
|
|
449
|
+
const i = rendered.item.index;
|
|
450
|
+
if (i >= colIndex && i < colIndex + colSpan) total += rendered.item.size;
|
|
451
|
+
}
|
|
452
|
+
return total || own;
|
|
453
|
+
}
|
|
454
|
+
const frozenBandHeight = $derived(ctrl.frozenBandHeight);
|
|
455
|
+
const frozenRowTop = $derived(ctrl.frozenRowTop);
|
|
409
456
|
const autoRowHeightOn = $derived(ctrl.autoRowHeightOn);
|
|
410
457
|
const measureRowHeight = $derived(ctrl.measureRowHeight);
|
|
411
458
|
const measuredRowHeightPx = $derived(ctrl.measuredRowHeightPx);
|
|
@@ -522,9 +569,8 @@
|
|
|
522
569
|
const toggleSelectAllRows = $derived(ctrl.toggleSelectAllRows);
|
|
523
570
|
const setActiveCell = $derived(ctrl.setActiveCell);
|
|
524
571
|
const scrollActiveCellIntoView = $derived(ctrl.scrollActiveCellIntoView);
|
|
572
|
+
const cellSelectionState = $derived(ctrl.cellSelectionState);
|
|
525
573
|
const getColumnWidth = $derived(ctrl.getColumnWidth);
|
|
526
|
-
const getCellRangeEdges = $derived(ctrl.getCellRangeEdges);
|
|
527
|
-
const fillHandleCell = $derived(ctrl.fillHandleCell);
|
|
528
574
|
const isInFillPreview = $derived(ctrl.isInFillPreview);
|
|
529
575
|
const fillMarqueeEdges = $derived(ctrl.fillMarqueeEdges);
|
|
530
576
|
const startFillDrag = $derived(ctrl.startFillDrag);
|
|
@@ -755,6 +801,19 @@
|
|
|
755
801
|
rotate-on-open both hang off it (SvGrid.css), and an override that
|
|
756
802
|
dropped the class would lose both silently - which is the kind of bug
|
|
757
803
|
that only shows up in someone else's app. -->
|
|
804
|
+
{#snippet chartToggle()}
|
|
805
|
+
<button
|
|
806
|
+
type="button"
|
|
807
|
+
class="sv-grid-toolbar-btn sv-grid-chart-toggle"
|
|
808
|
+
class:is-active={ctrl.chartPanelOpen}
|
|
809
|
+
aria-label={ctrl.chartPanelOpen ? "Close chart" : "Open chart"}
|
|
810
|
+
aria-expanded={ctrl.chartPanelOpen}
|
|
811
|
+
onclick={() => (ctrl.chartPanelOpen = !ctrl.chartPanelOpen)}
|
|
812
|
+
>
|
|
813
|
+
{@render icon("chart")}
|
|
814
|
+
Chart
|
|
815
|
+
</button>
|
|
816
|
+
{/snippet}
|
|
758
817
|
{#snippet icon(name: GridIconName)}
|
|
759
818
|
{@const override = opt.icons?.[name]}
|
|
760
819
|
{#if override}
|
|
@@ -1000,6 +1059,48 @@
|
|
|
1000
1059
|
{/if}
|
|
1001
1060
|
</div>
|
|
1002
1061
|
</div>
|
|
1062
|
+
{:else if ganttConfig}
|
|
1063
|
+
<div
|
|
1064
|
+
class="sv-grid-root sv-grid-gantt-root"
|
|
1065
|
+
class:sv-grid-root-fill={opt.containerHeight === "100%"}
|
|
1066
|
+
style={`height: ${
|
|
1067
|
+
typeof opt.containerHeight === "string"
|
|
1068
|
+
? opt.containerHeight
|
|
1069
|
+
: `${opt.containerHeight ?? 520}px`
|
|
1070
|
+
}; display: flex; flex-direction: column;`}
|
|
1071
|
+
>
|
|
1072
|
+
{#if ganttConfig.searchable !== false}
|
|
1073
|
+
<label class="sv-grid-board-search">
|
|
1074
|
+
{@render icon("search")}
|
|
1075
|
+
<input
|
|
1076
|
+
type="search"
|
|
1077
|
+
placeholder={ganttConfig.searchPlaceholder ??
|
|
1078
|
+
messages.ganttSearchPlaceholder}
|
|
1079
|
+
bind:value={ctrl.globalFilter}
|
|
1080
|
+
aria-label="Search tasks"
|
|
1081
|
+
/>
|
|
1082
|
+
</label>
|
|
1083
|
+
{/if}
|
|
1084
|
+
<div style="flex: 1 1 auto; min-height: 0;">
|
|
1085
|
+
{#if getGanttView()}
|
|
1086
|
+
{@const GanttView = getGanttView()}
|
|
1087
|
+
<GanttView
|
|
1088
|
+
data={boardData}
|
|
1089
|
+
columns={opt.columns}
|
|
1090
|
+
gantt={ganttConfig}
|
|
1091
|
+
getRowId={opt.getRowId}
|
|
1092
|
+
/>
|
|
1093
|
+
{:else}
|
|
1094
|
+
<!-- The Gantt renderer ships in @svgrid/enterprise. Call
|
|
1095
|
+
`enableGanttView()` (or `installEnterprise(api)`) to register it. -->
|
|
1096
|
+
<div class="sv-grid-scheduler-upsell sv-grid-gantt-upsell" role="note">
|
|
1097
|
+
<strong>{messages.ganttUpsellTitle}</strong>
|
|
1098
|
+
<p>{messages.ganttUpsellBody}</p>
|
|
1099
|
+
{@render enterpriseLicenseNote()}
|
|
1100
|
+
</div>
|
|
1101
|
+
{/if}
|
|
1102
|
+
</div>
|
|
1103
|
+
</div>
|
|
1003
1104
|
{:else if chartViewConfig}
|
|
1004
1105
|
<div
|
|
1005
1106
|
class="sv-grid-root sv-grid-chart-root"
|
|
@@ -1046,6 +1147,9 @@
|
|
|
1046
1147
|
</div>
|
|
1047
1148
|
</div>
|
|
1048
1149
|
{:else if pivotViewOn}
|
|
1150
|
+
<!-- The Chart panel docks here the way it docks beside the flat grid,
|
|
1151
|
+
and charts the pivot on screen: the root reserves the panel's edge and
|
|
1152
|
+
the pivot bar carries the toggle the toolbar carries in flat mode. -->
|
|
1049
1153
|
<div
|
|
1050
1154
|
class="sv-grid-root sv-grid-pivot-root"
|
|
1051
1155
|
class:sv-grid-root-fill={opt.containerHeight === "100%"}
|
|
@@ -1053,7 +1157,7 @@
|
|
|
1053
1157
|
typeof opt.containerHeight === "string"
|
|
1054
1158
|
? opt.containerHeight
|
|
1055
1159
|
: `${opt.containerHeight ?? 520}px`
|
|
1056
|
-
}; display: flex; flex-direction: column
|
|
1160
|
+
}; display: flex; flex-direction: column; ${chartDockReserveStyle}`}
|
|
1057
1161
|
>
|
|
1058
1162
|
<div class="sv-grid-pivot-bar">
|
|
1059
1163
|
<span class="sv-grid-pivot-badge">{messages.group}</span>
|
|
@@ -1063,6 +1167,9 @@
|
|
|
1063
1167
|
aria-pressed="true"
|
|
1064
1168
|
onclick={() => ctrl.togglePivotMode()}
|
|
1065
1169
|
>{messages.pivotUpsellTitle}</button>
|
|
1170
|
+
{#if ctrl.chartingEnabled}
|
|
1171
|
+
{@render chartToggle()}
|
|
1172
|
+
{/if}
|
|
1066
1173
|
</div>
|
|
1067
1174
|
<div style="flex: 1 1 auto; min-height: 0;">
|
|
1068
1175
|
{#if pivotResult}
|
|
@@ -1081,6 +1188,9 @@
|
|
|
1081
1188
|
</div>
|
|
1082
1189
|
{/if}
|
|
1083
1190
|
</div>
|
|
1191
|
+
{#if ctrl.chartingEnabled && ctrl.chartPanelOpen && ChartPanelView}
|
|
1192
|
+
<ChartPanelView {ctrl} />
|
|
1193
|
+
{/if}
|
|
1084
1194
|
</div>
|
|
1085
1195
|
{:else}
|
|
1086
1196
|
|
|
@@ -1402,234 +1512,602 @@
|
|
|
1402
1512
|
</tr>
|
|
1403
1513
|
{/snippet}
|
|
1404
1514
|
|
|
1405
|
-
<!-- A
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
the
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1515
|
+
<!-- A row whose data has not arrived.
|
|
1516
|
+
|
|
1517
|
+
"loading" keeps the real columns and draws a shimmer bar in each,
|
|
1518
|
+
so the table keeps its shape while blocks stream in - a full-width
|
|
1519
|
+
bar would make the whole grid jump every time one landed. "failed"
|
|
1520
|
+
is the opposite case: there is nothing per-column to say, just one
|
|
1521
|
+
message and a way to try again, so it spans the row like a detail
|
|
1522
|
+
cell does. Neither is selectable or editable; there is nothing
|
|
1523
|
+
there to act on.
|
|
1524
|
+
|
|
1525
|
+
A failed block is a run of failed rows, one per row it claimed,
|
|
1526
|
+
and one message serves the run: the first failed row on screen
|
|
1527
|
+
carries it (the run's first row, or the top of the window when
|
|
1528
|
+
the run started above it) and the rows under it are a plain
|
|
1529
|
+
tinted band. A hundred "Could not load" lines for one block
|
|
1530
|
+
was a wall of red. -->
|
|
1531
|
+
{#snippet placeholderRowMarkup(
|
|
1532
|
+
phRow: Row<TData>,
|
|
1533
|
+
phRowIndex: number,
|
|
1534
|
+
phState: "loading" | "failed",
|
|
1535
|
+
phStyle: string,
|
|
1536
|
+
phContinues: boolean,
|
|
1413
1537
|
)}
|
|
1414
1538
|
<tr
|
|
1415
|
-
class=
|
|
1416
|
-
|
|
1417
|
-
|
|
1539
|
+
class="sv-grid-row sv-grid-placeholder-row"
|
|
1540
|
+
class:sv-grid-placeholder-failed={phState === "failed"}
|
|
1541
|
+
class:sv-grid-placeholder-failed-cont={phContinues}
|
|
1542
|
+
aria-busy={phState === "loading" ? "true" : undefined}
|
|
1543
|
+
{...getGridRowA11yProps(phRowIndex + 1)}
|
|
1544
|
+
style={phStyle}
|
|
1418
1545
|
>
|
|
1419
|
-
{#if
|
|
1420
|
-
<td
|
|
1421
|
-
class="sv-grid-cell sv-grid-row-number-cell"
|
|
1422
|
-
style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; left: 0;`}
|
|
1423
|
-
>{#if where === "top"}{@render icon("pinned-row-top")}{:else}{@render icon("pinned-row-bottom")}{/if}</td
|
|
1424
|
-
>
|
|
1425
|
-
{/if}
|
|
1426
|
-
{#if showRowSelectionEffective}
|
|
1427
|
-
<td
|
|
1428
|
-
class="sv-grid-cell sv-grid-selection-cell"
|
|
1429
|
-
style={`width: ${selectionColumnWidth}px; min-width: ${selectionColumnWidth}px; max-width: ${selectionColumnWidth}px; left: ${showRowNumbersEffective ? rowNumberColumnWidth : 0}px;`}
|
|
1430
|
-
></td>
|
|
1431
|
-
{/if}
|
|
1432
|
-
{#if columnVirtualizationEnabled && columnWindowStart > 0}
|
|
1433
|
-
<td
|
|
1434
|
-
class="sv-grid-cell sv-grid-cell-spacer"
|
|
1435
|
-
aria-hidden="true"
|
|
1436
|
-
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
1437
|
-
></td>
|
|
1438
|
-
{/if}
|
|
1439
|
-
{#each renderedColumns as rendered (rendered.column.id)}
|
|
1440
|
-
{@const value = getPinnedCellValue(rowData, rendered.column)}
|
|
1441
|
-
{@const userCellClass = computePinnedCellClass(
|
|
1442
|
-
rowData,
|
|
1443
|
-
rendered.column,
|
|
1444
|
-
)}
|
|
1546
|
+
{#if phState === "failed"}
|
|
1445
1547
|
<td
|
|
1446
|
-
class=
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
>{formatPinnedValue(rendered.column, value)}</td
|
|
1548
|
+
class="sv-grid-cell sv-grid-placeholder-cell"
|
|
1549
|
+
colspan={allColumns.length +
|
|
1550
|
+
(showRowNumbersEffective ? 1 : 0) +
|
|
1551
|
+
(showRowSelectionEffective ? 1 : 0)}
|
|
1451
1552
|
>
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1553
|
+
{#if !phContinues}
|
|
1554
|
+
<div class="sv-grid-placeholder-failed-body">
|
|
1555
|
+
<span class="sv-grid-placeholder-error">{messages.rowLoadFailed}</span>
|
|
1556
|
+
{#if opt.onRetryRow}
|
|
1557
|
+
<button
|
|
1558
|
+
type="button"
|
|
1559
|
+
class="sv-grid-placeholder-retry"
|
|
1560
|
+
onclick={() => ctrl.retryRow(phRow)}>{messages.rowLoadRetry}</button
|
|
1561
|
+
>
|
|
1562
|
+
{/if}
|
|
1563
|
+
</div>
|
|
1564
|
+
{/if}
|
|
1565
|
+
</td>
|
|
1566
|
+
{:else}
|
|
1567
|
+
{#if showRowNumbersEffective}
|
|
1568
|
+
<td class="sv-grid-cell sv-grid-rownum-cell"></td>
|
|
1569
|
+
{/if}
|
|
1570
|
+
{#if showRowSelectionEffective}
|
|
1571
|
+
<td class="sv-grid-cell sv-grid-select-cell"></td>
|
|
1572
|
+
{/if}
|
|
1573
|
+
{#each allColumns as col (col.id)}
|
|
1574
|
+
<td class="sv-grid-cell sv-grid-placeholder-cell">
|
|
1575
|
+
<span class="sv-grid-placeholder-skeleton" aria-hidden="true"></span>
|
|
1576
|
+
</td>
|
|
1577
|
+
{/each}
|
|
1459
1578
|
{/if}
|
|
1460
1579
|
</tr>
|
|
1461
1580
|
{/snippet}
|
|
1462
1581
|
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1582
|
+
<!-- A single pinned row (top or bottom). Read-only by design: no
|
|
1583
|
+
inline editing, no row-selection checkbox, no fill handle.
|
|
1584
|
+
Position-sticky CSS keeps it anchored to the top of the body or
|
|
1585
|
+
the bottom of the viewport while the rest scrolls. -->
|
|
1586
|
+
<!--
|
|
1587
|
+
One body row, whatever path renders it: the virtualized window, the
|
|
1588
|
+
plain full-list body, or the frozen rows pinned above the window. The
|
|
1589
|
+
two loops used to carry a copy each and had drifted apart (cell
|
|
1590
|
+
tooltips on one, the fill handle on the other), so a feature landed in
|
|
1591
|
+
one mode and not the other. `rowStyle` is the height the caller knows:
|
|
1592
|
+
the virtualizer's measured size, or the declared one.
|
|
1593
|
+
-->
|
|
1594
|
+
{#snippet bodyRow(row: Row<TData>, rowIndex: number, rowStyle: string)}
|
|
1595
|
+
{@const phState = ctrl.placeholderStateOf(row)}
|
|
1596
|
+
{#if phState}
|
|
1597
|
+
{@const phContinues =
|
|
1598
|
+
phState === "failed" &&
|
|
1599
|
+
rowIndex > firstRenderedRowIndex &&
|
|
1600
|
+
ctrl.placeholderStateOf(allRows[rowIndex - 1]!) === "failed"}
|
|
1601
|
+
{@render placeholderRowMarkup(row, rowIndex, phState, rowStyle, phContinues)}
|
|
1602
|
+
{:else if opt.isDetailRow?.(row.original as TData, rowIndex)}
|
|
1603
|
+
{@render detailRowMarkup(row, rowIndex)}
|
|
1604
|
+
{:else if isGroupRow(row) && !groupColumnMode}
|
|
1605
|
+
<tr
|
|
1606
|
+
class="sv-grid-row sv-grid-group-row"
|
|
1607
|
+
class:sv-grid-row-selected={isRowSelected(row.id)}
|
|
1608
|
+
class:sv-grid-row-frozen={rowIndex < frozenRowCount}
|
|
1609
|
+
class:sv-grid-row-frozen-last={frozenRowCount > 0 && rowIndex === frozenRowCount - 1}
|
|
1610
|
+
style:top={rowIndex < frozenRowCount ? `${frozenRowTop(rowIndex)}px` : undefined}
|
|
1611
|
+
aria-level={row.depth + 1}
|
|
1612
|
+
aria-expanded={row.getIsExpanded?.() ? "true" : "false"}
|
|
1613
|
+
{...getGridRowA11yProps(rowIndex + 1)}
|
|
1614
|
+
class:sv-grid-row-collapsed={ctrl.isRowCollapsed(rowIndex)}
|
|
1615
|
+
style={rowStyle}
|
|
1616
|
+
>
|
|
1617
|
+
<td
|
|
1618
|
+
class="sv-grid-cell sv-grid-group-cell"
|
|
1619
|
+
class:sv-grid-cell-active={activeCell.rowIndex ===
|
|
1620
|
+
rowIndex}
|
|
1621
|
+
colspan={allColumns.length +
|
|
1622
|
+
(showRowNumbersEffective ? 1 : 0) +
|
|
1623
|
+
(showRowSelectionEffective ? 1 : 0)}
|
|
1624
|
+
onclick={() => row.toggleExpanded?.()}
|
|
1625
|
+
>
|
|
1626
|
+
{@render groupRowContent(row)}
|
|
1627
|
+
</td>
|
|
1628
|
+
</tr>
|
|
1629
|
+
{:else}
|
|
1630
|
+
{@const userRowClass = computeRowClass(row, rowIndex)}
|
|
1631
|
+
<tr
|
|
1632
|
+
class={`sv-grid-row ${userRowClass} ${rowDropClass(rowIndex)}`}
|
|
1633
|
+
class:sv-grid-row-selected={isRowSelected(row.id)}
|
|
1634
|
+
class:sv-grid-row-alt={opt.zebraRows &&
|
|
1635
|
+
rowIndex % 2 === 1}
|
|
1636
|
+
class:sv-grid-row-draggable={rowDragManagedEffective}
|
|
1637
|
+
class:sv-grid-group-footer-row={isGroupFooterRow(row)}
|
|
1638
|
+
class:sv-grid-grand-total-row={isGrandTotalRow(row)}
|
|
1639
|
+
class:sv-grid-row-auto-height={autoRowHeightOn}
|
|
1640
|
+
class:sv-grid-row-frozen={rowIndex < frozenRowCount}
|
|
1641
|
+
class:sv-grid-row-frozen-last={frozenRowCount > 0 && rowIndex === frozenRowCount - 1}
|
|
1642
|
+
style:top={rowIndex < frozenRowCount ? `${frozenRowTop(rowIndex)}px` : undefined}
|
|
1643
|
+
{...getGridRowA11yProps(rowIndex + 1)}
|
|
1644
|
+
aria-level={sgAriaLevel(row)}
|
|
1645
|
+
aria-expanded={sgAriaExpanded(row)}
|
|
1646
|
+
{...rowDragAttrs(rowIndex)}
|
|
1647
|
+
class:sv-grid-row-collapsed={ctrl.isRowCollapsed(rowIndex)}
|
|
1648
|
+
style={rowStyle}
|
|
1649
|
+
use:measureRowHeight={rowIndex}
|
|
1650
|
+
>
|
|
1651
|
+
{#if showRowNumbersEffective}
|
|
1652
|
+
<td
|
|
1653
|
+
class="sv-grid-cell sv-grid-row-number-cell"
|
|
1654
|
+
style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; left: 0;`}
|
|
1655
|
+
>{rowIndex + 1}</td
|
|
1656
|
+
>
|
|
1657
|
+
{/if}
|
|
1658
|
+
{#if showRowSelectionEffective}
|
|
1659
|
+
<td
|
|
1660
|
+
class="sv-grid-cell sv-grid-selection-cell"
|
|
1661
|
+
style={`width: ${selectionColumnWidth}px; min-width: ${selectionColumnWidth}px; max-width: ${selectionColumnWidth}px; left: ${showRowNumbersEffective ? rowNumberColumnWidth : 0}px;`}
|
|
1662
|
+
onclick={() => toggleRowSelectionById(row.id)}
|
|
1663
|
+
>
|
|
1664
|
+
<button
|
|
1665
|
+
type="button"
|
|
1666
|
+
class="sv-grid-checkbox"
|
|
1667
|
+
role="checkbox"
|
|
1668
|
+
aria-checked={isRowSelected(row.id)}
|
|
1669
|
+
aria-label="Select row"
|
|
1670
|
+
onclick={(event) => {
|
|
1671
|
+
event.stopPropagation();
|
|
1672
|
+
toggleRowSelectionById(row.id);
|
|
1673
|
+
}}
|
|
1674
|
+
onkeydown={(event) =>
|
|
1675
|
+
toggleCheckboxWithKeyboard(event, () => {
|
|
1676
|
+
event.stopPropagation();
|
|
1677
|
+
toggleRowSelectionById(row.id);
|
|
1678
|
+
})}
|
|
1679
|
+
></button>
|
|
1680
|
+
</td>
|
|
1681
|
+
{/if}
|
|
1682
|
+
{#if columnVirtualizationEnabled && columnWindowStart > 0}
|
|
1683
|
+
<td
|
|
1684
|
+
class="sv-grid-cell sv-grid-cell-spacer"
|
|
1685
|
+
aria-hidden="true"
|
|
1686
|
+
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
1687
|
+
></td>
|
|
1688
|
+
{/if}
|
|
1689
|
+
{#each renderedColumns as rendered (rendered.column.id)}
|
|
1690
|
+
{@const colIndex = rendered.item.index}
|
|
1691
|
+
{@const draw = mergeIndex
|
|
1692
|
+
? mergeDrawAt(mergeIndex, rowIndex, colIndex, rowIndex < frozenRowCount ? mergeFrozenWindow : mergeBodyWindow)
|
|
1693
|
+
: null}
|
|
1694
|
+
{#if draw?.kind !== "skip"}
|
|
1695
|
+
{@const cellRow = draw?.kind === "continuation" ? (allRows[draw.merge.rowIndex] ?? row) : row}
|
|
1696
|
+
{@const cellColumn = draw?.kind === "continuation" ? (allColumns[draw.merge.colIndex] ?? rendered.column) : rendered.column}
|
|
1697
|
+
{@const baseValue = getColumnBaseValue(
|
|
1698
|
+
cellRow,
|
|
1699
|
+
cellColumn,
|
|
1700
|
+
)}
|
|
1701
|
+
{@const cellValue = getCellDisplayValue(
|
|
1702
|
+
cellRow.id,
|
|
1703
|
+
cellColumn.id,
|
|
1704
|
+
baseValue,
|
|
1705
|
+
)}
|
|
1706
|
+
{@const isEditing =
|
|
1707
|
+
ctrl.editingCell?.rowId === cellRow.id &&
|
|
1708
|
+
ctrl.editingCell?.columnId === cellColumn.id}
|
|
1709
|
+
{@const inRowEdit =
|
|
1710
|
+
!!fullRowEdit &&
|
|
1711
|
+
fullRowEdit.rowId === cellRow.id &&
|
|
1712
|
+
cellColumn.id in fullRowEdit.draft}
|
|
1713
|
+
{@const cellSel = cellSelectionState(rowIndex, colIndex)}
|
|
1714
|
+
{@const fillEdges = fillMarqueeEdges(rowIndex, colIndex)}
|
|
1715
|
+
{@const userCellClass = computeCellClass(
|
|
1716
|
+
cellRow,
|
|
1717
|
+
cellColumn,
|
|
1718
|
+
)}
|
|
1719
|
+
{@const cellTooltip = computeCellTooltip(
|
|
1720
|
+
cellRow,
|
|
1721
|
+
cellColumn,
|
|
1722
|
+
)}
|
|
1723
|
+
{@const cellValidity = computeCellValidity(
|
|
1724
|
+
cellRow,
|
|
1725
|
+
cellColumn,
|
|
1726
|
+
)}
|
|
1727
|
+
{@const cellNote = computeCellNote(
|
|
1728
|
+
cellRow,
|
|
1729
|
+
cellColumn,
|
|
1730
|
+
)}
|
|
1731
|
+
{@const tdWidth = draw ? mergedWidth(colIndex, draw.colSpan, rendered.item.size) : rendered.item.size}
|
|
1732
|
+
<td
|
|
1733
|
+
rowspan={draw && draw.rowSpan > 1 ? draw.rowSpan : undefined}
|
|
1734
|
+
colspan={draw && draw.colSpan > 1 ? draw.colSpan : undefined}
|
|
1735
|
+
data-merge-origin={draw ? `${draw.merge.rowIndex}:${draw.merge.colIndex}` : undefined}
|
|
1736
|
+
class:sv-grid-cell-merged={!!draw}
|
|
1737
|
+
class={`sv-grid-cell ${userCellClass}`}
|
|
1738
|
+
class:sv-grid-cell-editing={isEditing || inRowEdit}
|
|
1739
|
+
class:sv-grid-cell-collapsed={!!collapsedColumns[rendered.column.id]}
|
|
1740
|
+
class:sv-grid-cell-active={!!cellSel?.active}
|
|
1741
|
+
class:sv-grid-cell-has-fill-handle={!!cellSel?.fillHandle}
|
|
1742
|
+
class:sv-grid-cell-cf={hasConditionalFormats}
|
|
1743
|
+
class:sv-grid-cell-invalid={cellValidity.invalid}
|
|
1744
|
+
class:sv-grid-cell-has-note={cellNote != null}
|
|
1745
|
+
aria-invalid={cellValidity.invalid ? "true" : undefined}
|
|
1746
|
+
data-svgrid-row={rowIndex}
|
|
1747
|
+
data-svgrid-col={colIndex}
|
|
1748
|
+
data-col-id={rendered.column.id}
|
|
1749
|
+
data-align={getColumnAlign(rendered.column)}
|
|
1750
|
+
data-pinned={isColumnPinned(rendered.column.id) ??
|
|
1751
|
+
undefined}
|
|
1752
|
+
data-selected-range={cellSel?.edges ? "true" : undefined}
|
|
1753
|
+
data-range-top={cellSel?.edges?.top ? "true" : undefined}
|
|
1754
|
+
data-range-bottom={cellSel?.edges?.bottom
|
|
1755
|
+
? "true"
|
|
1756
|
+
: undefined}
|
|
1757
|
+
data-range-left={cellSel?.edges?.left
|
|
1758
|
+
? "true"
|
|
1759
|
+
: undefined}
|
|
1760
|
+
data-range-right={cellSel?.edges?.right
|
|
1761
|
+
? "true"
|
|
1762
|
+
: undefined}
|
|
1763
|
+
data-fill-preview={isInFillPreview(rowIndex, colIndex)
|
|
1764
|
+
? "true"
|
|
1765
|
+
: undefined}
|
|
1766
|
+
data-fill-top={fillEdges?.top ? "true" : undefined}
|
|
1767
|
+
data-fill-bottom={fillEdges?.bottom ? "true" : undefined}
|
|
1768
|
+
data-fill-left={fillEdges?.left ? "true" : undefined}
|
|
1769
|
+
data-fill-right={fillEdges?.right ? "true" : undefined}
|
|
1770
|
+
style={`width: ${tdWidth}px; min-width: ${tdWidth}px; max-width: ${tdWidth}px; ${cellPinStyle(rendered.column.id)}`}
|
|
1771
|
+
onpointerdown={(event) =>
|
|
1772
|
+
onCellPointerDown(rowIndex, colIndex, event)}
|
|
1773
|
+
onpointerenter={(event) => {
|
|
1774
|
+
onCellPointerEnter(rowIndex, colIndex);
|
|
1775
|
+
// The column tooltip fires on whole-cell hover; a
|
|
1776
|
+
// per-cell note waits for its corner below. A
|
|
1777
|
+
// validation message wins over the column's tooltip.
|
|
1778
|
+
const tip =
|
|
1779
|
+
cellValidity.invalid && cellValidity.message
|
|
1780
|
+
? cellValidity.message
|
|
1781
|
+
: cellTooltip;
|
|
1782
|
+
if (tip)
|
|
1783
|
+
showTooltipFor(
|
|
1784
|
+
event.currentTarget as HTMLElement,
|
|
1785
|
+
tip,
|
|
1786
|
+
);
|
|
1787
|
+
}}
|
|
1788
|
+
onpointerleave={hideTooltip}
|
|
1789
|
+
ondblclick={() =>
|
|
1790
|
+
emitCellDoubleClick(rowIndex, colIndex)}
|
|
1791
|
+
onclick={() => onCellClick(rowIndex, colIndex)}
|
|
1792
|
+
oncontextmenu={(event) =>
|
|
1793
|
+
openContextMenu(
|
|
1794
|
+
event,
|
|
1795
|
+
rowIndex,
|
|
1796
|
+
colIndex,
|
|
1797
|
+
rendered.column.id,
|
|
1798
|
+
)}
|
|
1799
|
+
use:cellFlashAction={{
|
|
1800
|
+
rowId: cellRow.id,
|
|
1801
|
+
value: cellValue,
|
|
1802
|
+
active: !!cellColumn.columnDef.cellFlash,
|
|
1803
|
+
className: flashClassFor(
|
|
1804
|
+
cellColumn.columnDef.cellFlash,
|
|
1805
|
+
),
|
|
1806
|
+
}}
|
|
1807
|
+
role="gridcell"
|
|
1808
|
+
id={getGridCellDomId(ctrl.gridDomId, rowIndex, colIndex)}
|
|
1809
|
+
aria-colindex={colIndex + 1}
|
|
1810
|
+
aria-rowindex={rowIndex + 1}
|
|
1811
|
+
aria-selected={isRowSelected(row.id)}
|
|
1812
|
+
>
|
|
1813
|
+
{#if inRowEdit || isEditing}
|
|
1814
|
+
<!-- The editing cell stays empty until the lazy
|
|
1815
|
+
editor chunk lands, exactly as it already did
|
|
1816
|
+
for the dropdown / date editors. Falling back
|
|
1817
|
+
to the read-only value here would flash stale
|
|
1818
|
+
content into a cell the user is editing. -->
|
|
1819
|
+
{#if CellEditor}
|
|
1820
|
+
<CellEditor {ctrl} column={cellColumn} row={cellRow} fullRow={inRowEdit} />
|
|
1821
|
+
{/if}
|
|
1822
|
+
{:else}
|
|
1823
|
+
{#if plainCellBody}
|
|
1824
|
+
{@render cellBody(cellRow, cellColumn, cellValue)}
|
|
1825
|
+
{:else}
|
|
1826
|
+
{@render cellBodyWithFormat(
|
|
1827
|
+
cellRow,
|
|
1828
|
+
cellColumn,
|
|
1829
|
+
cellValue,
|
|
1830
|
+
)}
|
|
1831
|
+
{/if}
|
|
1832
|
+
{/if}
|
|
1833
|
+
{#if !isEditing && cellSel?.fillHandle}
|
|
1834
|
+
<!-- Excel-style fill handle: drag down/right to
|
|
1835
|
+
extend the selection and pattern-fill the new
|
|
1836
|
+
cells on release. Rendered inside the bottom-
|
|
1837
|
+
right cell of the selection range (or active
|
|
1838
|
+
cell if there's no range). -->
|
|
1839
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
1840
|
+
<div
|
|
1841
|
+
class="sv-grid-fill-handle"
|
|
1842
|
+
role="button"
|
|
1843
|
+
aria-label="Fill handle"
|
|
1844
|
+
tabindex={-1}
|
|
1845
|
+
onpointerdown={(event) =>
|
|
1846
|
+
startFillDrag(event, rowIndex, colIndex)}
|
|
1847
|
+
ondblclick={(event) => {
|
|
1848
|
+
// Not the cell's double-click: this fills
|
|
1849
|
+
// down as far as the neighbour column goes.
|
|
1850
|
+
event.stopPropagation();
|
|
1851
|
+
event.preventDefault();
|
|
1852
|
+
ctrl.fillDownToNeighbour();
|
|
1853
|
+
}}
|
|
1854
|
+
></div>
|
|
1855
|
+
{/if}
|
|
1856
|
+
{#if cellValidity.invalid && cellValidity.message}
|
|
1857
|
+
<!-- The validation message, for assistive tech: the
|
|
1858
|
+
tooltip above is mouse-only, and unlike a title
|
|
1859
|
+
a visually-hidden span adds no second native
|
|
1860
|
+
tooltip competing with the custom one. -->
|
|
1861
|
+
<span class="sv-grid-sr-only"
|
|
1862
|
+
>{cellValidity.message}</span
|
|
1863
|
+
>
|
|
1864
|
+
{/if}
|
|
1865
|
+
{#if cellNote != null && !isEditing}
|
|
1866
|
+
<span
|
|
1867
|
+
class="sv-grid-cell-note-corner"
|
|
1868
|
+
aria-label="Note"
|
|
1869
|
+
onpointerenter={(event) => {
|
|
1870
|
+
event.stopPropagation();
|
|
1871
|
+
showTooltipFor(
|
|
1872
|
+
event.currentTarget as HTMLElement,
|
|
1873
|
+
cellNote,
|
|
1874
|
+
);
|
|
1875
|
+
}}
|
|
1876
|
+
onpointerleave={(event) => {
|
|
1877
|
+
event.stopPropagation();
|
|
1878
|
+
hideTooltip();
|
|
1879
|
+
}}
|
|
1880
|
+
></span>
|
|
1881
|
+
{/if}
|
|
1882
|
+
</td>
|
|
1883
|
+
{/if}
|
|
1884
|
+
{/each}
|
|
1885
|
+
{#if columnVirtualizationEnabled && columnWindowRightSpacer > 0}
|
|
1886
|
+
<td
|
|
1887
|
+
class="sv-grid-cell sv-grid-cell-spacer"
|
|
1888
|
+
aria-hidden="true"
|
|
1889
|
+
style={`width: ${columnWindowRightSpacer}px; min-width: ${columnWindowRightSpacer}px; max-width: ${columnWindowRightSpacer}px;`}
|
|
1890
|
+
></td>
|
|
1891
|
+
{/if}
|
|
1892
|
+
</tr>
|
|
1893
|
+
{/if}
|
|
1894
|
+
{/snippet}
|
|
1895
|
+
{#snippet pinnedRowBody(
|
|
1896
|
+
rowData: TData,
|
|
1897
|
+
where: "top" | "bottom",
|
|
1898
|
+
index: number,
|
|
1899
|
+
)}
|
|
1900
|
+
<tr
|
|
1901
|
+
class={`sv-grid-row sv-grid-pinned-row sv-grid-pinned-row-${where}`}
|
|
1902
|
+
data-pinned-row={where}
|
|
1903
|
+
data-pinned-index={index}
|
|
1904
|
+
>
|
|
1905
|
+
{#if showRowNumbersEffective}
|
|
1906
|
+
<td
|
|
1907
|
+
class="sv-grid-cell sv-grid-row-number-cell"
|
|
1908
|
+
style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; left: 0;`}
|
|
1909
|
+
>{#if where === "top"}{@render icon("pinned-row-top")}{:else}{@render icon("pinned-row-bottom")}{/if}</td
|
|
1910
|
+
>
|
|
1911
|
+
{/if}
|
|
1912
|
+
{#if showRowSelectionEffective}
|
|
1913
|
+
<td
|
|
1914
|
+
class="sv-grid-cell sv-grid-selection-cell"
|
|
1915
|
+
style={`width: ${selectionColumnWidth}px; min-width: ${selectionColumnWidth}px; max-width: ${selectionColumnWidth}px; left: ${showRowNumbersEffective ? rowNumberColumnWidth : 0}px;`}
|
|
1916
|
+
></td>
|
|
1917
|
+
{/if}
|
|
1918
|
+
{#if columnVirtualizationEnabled && columnWindowStart > 0}
|
|
1919
|
+
<td
|
|
1920
|
+
class="sv-grid-cell sv-grid-cell-spacer"
|
|
1921
|
+
aria-hidden="true"
|
|
1922
|
+
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
1923
|
+
></td>
|
|
1924
|
+
{/if}
|
|
1925
|
+
{#each renderedColumns as rendered (rendered.column.id)}
|
|
1926
|
+
{@const value = getPinnedCellValue(rowData, rendered.column)}
|
|
1927
|
+
{@const userCellClass = computePinnedCellClass(
|
|
1928
|
+
rowData,
|
|
1929
|
+
rendered.column,
|
|
1930
|
+
)}
|
|
1931
|
+
<td
|
|
1932
|
+
class={`sv-grid-cell ${userCellClass}`}
|
|
1933
|
+
data-col-id={rendered.column.id}
|
|
1934
|
+
data-pinned={isColumnPinned(rendered.column.id) ?? undefined}
|
|
1935
|
+
style={`width: ${rendered.item.size}px; min-width: ${rendered.item.size}px; max-width: ${rendered.item.size}px; ${cellPinStyle(rendered.column.id)}`}
|
|
1936
|
+
>{formatPinnedValue(rendered.column, value)}</td
|
|
1937
|
+
>
|
|
1938
|
+
{/each}
|
|
1939
|
+
{#if columnVirtualizationEnabled && columnWindowRightSpacer > 0}
|
|
1940
|
+
<td
|
|
1941
|
+
class="sv-grid-cell sv-grid-cell-spacer"
|
|
1942
|
+
aria-hidden="true"
|
|
1943
|
+
style={`width: ${columnWindowRightSpacer}px; min-width: ${columnWindowRightSpacer}px; max-width: ${columnWindowRightSpacer}px;`}
|
|
1944
|
+
></td>
|
|
1945
|
+
{/if}
|
|
1946
|
+
</tr>
|
|
1947
|
+
{/snippet}
|
|
1948
|
+
|
|
1949
|
+
<div
|
|
1950
|
+
class="sv-grid-root"
|
|
1951
|
+
class:sv-grid-root-fill={opt.containerHeight === "100%"}
|
|
1952
|
+
style={chartDockReserveStyle}
|
|
1953
|
+
data-move-grab={ctrl.moveGrabHover || ctrl.moveDrag ? "true" : undefined}
|
|
1954
|
+
data-selbar={
|
|
1955
|
+
ctrl.selectionBarVisible
|
|
1956
|
+
? (getSelectionBarView() ? ctrl.selectionBarPosition : `upsell-${ctrl.selectionBarPosition}`)
|
|
1957
|
+
: undefined
|
|
1958
|
+
}
|
|
1959
|
+
use:lazyRowResize={{
|
|
1960
|
+
disabled: !ctrl.rowResizeOn,
|
|
1961
|
+
// No gutter column on most grids, so fall back to the first body cell -
|
|
1962
|
+
// otherwise `rowResize` would be a prop that silently does nothing.
|
|
1963
|
+
anchor: "row",
|
|
1964
|
+
onResize: (index, height) => ctrl.resizeRowByUser(index, height),
|
|
1965
|
+
onAutosize: (index) => ctrl.resizeRowByUser(index, null),
|
|
1966
|
+
}}
|
|
1967
|
+
use:lazyColumnResize={{
|
|
1968
|
+
disabled: !columnResizeEnabled,
|
|
1969
|
+
getWidth: (columnId) => ctrl.getColumnWidth(columnId),
|
|
1970
|
+
onResize: (columnId, width) => {
|
|
1971
|
+
ctrl.columnWidths = { ...ctrl.columnWidths, [columnId]: width };
|
|
1972
|
+
},
|
|
1973
|
+
onResizeEnd: (columnId, width, startWidth) => ctrl.resizeColumnByUser(columnId, width, startWidth),
|
|
1974
|
+
label: (columnId) => {
|
|
1975
|
+
const col = ctrl.findColumnById(columnId);
|
|
1976
|
+
return col ? toolPanelHeaderLabel(col) : columnId;
|
|
1977
|
+
},
|
|
1978
|
+
canResize: (columnId) => ctrl.columnResizable(columnId),
|
|
1979
|
+
onAutosize: (columnId) => ctrl.autosizeColumnByUser(columnId),
|
|
1980
|
+
}}
|
|
1981
|
+
>
|
|
1982
|
+
{#if showGlobalFilterEffective}
|
|
1983
|
+
<label class="sv-grid-global-filter">
|
|
1984
|
+
Filter all rows
|
|
1985
|
+
<input bind:value={ctrl.globalFilter} placeholder="Type to filter..." />
|
|
1986
|
+
</label>
|
|
1987
|
+
{/if}
|
|
1988
|
+
|
|
1989
|
+
{#if toolPanelEnabled || ctrl.chartingEnabled || ctrl.advancedFilter}
|
|
1990
|
+
<div class="sv-grid-toolbar">
|
|
1991
|
+
{#if ctrl.advancedFilter}
|
|
1992
|
+
<!--
|
|
1993
|
+
An advanced filter is set from a panel the consumer mounts, which
|
|
1994
|
+
may be scrolled away or in a drawer. Without something here, rows
|
|
1995
|
+
are missing from the grid with nothing on screen to explain why or
|
|
1996
|
+
to undo it. This is that something: state plus a way out.
|
|
1997
|
+
-->
|
|
1998
|
+
<span class="sv-grid-advf-chip">
|
|
1999
|
+
{@render icon("advanced-filter")}
|
|
2000
|
+
{messages.advancedFilterActive}
|
|
2001
|
+
<button
|
|
2002
|
+
type="button"
|
|
2003
|
+
class="sv-grid-advf-clear"
|
|
2004
|
+
aria-label={messages.advancedFilterClear}
|
|
2005
|
+
title={messages.advancedFilterClear}
|
|
2006
|
+
onclick={() => (ctrl.advancedFilter = null)}
|
|
2007
|
+
>{@render icon("clear")}</button>
|
|
2008
|
+
</span>
|
|
2009
|
+
{/if}
|
|
2010
|
+
{#if toolPanelEnabled}
|
|
2011
|
+
<button
|
|
2012
|
+
type="button"
|
|
2013
|
+
class="sv-grid-toolbar-btn"
|
|
2014
|
+
class:is-active={ctrl.toolPanelOpen}
|
|
2015
|
+
aria-label={ctrl.toolPanelOpen
|
|
2016
|
+
? "Close tool panel"
|
|
2017
|
+
: "Open tool panel (columns & filters)"}
|
|
2018
|
+
aria-expanded={ctrl.toolPanelOpen}
|
|
2019
|
+
onclick={() => (ctrl.toolPanelOpen = !ctrl.toolPanelOpen)}
|
|
2020
|
+
>
|
|
2021
|
+
{@render icon("tool-panel")}
|
|
2022
|
+
Columns & Filters
|
|
2023
|
+
</button>
|
|
2024
|
+
{/if}
|
|
2025
|
+
{#if ctrl.chartingEnabled}
|
|
2026
|
+
{@render chartToggle()}
|
|
2027
|
+
{/if}
|
|
2028
|
+
</div>
|
|
2029
|
+
{/if}
|
|
2030
|
+
|
|
2031
|
+
{#if hasMeasured && (opt.paginationPosition === "top" || opt.paginationPosition === "both")}
|
|
2032
|
+
<GridFooter {ctrl} {icon} showStatus={false} top pager pageSizeOptions={opt.pageSizeOptions} />
|
|
2033
|
+
{/if}
|
|
2034
|
+
|
|
2035
|
+
<div
|
|
2036
|
+
class="sv-grid-shell"
|
|
2037
|
+
style={`height: ${
|
|
2038
|
+
typeof opt.containerHeight === "string"
|
|
2039
|
+
? opt.containerHeight
|
|
2040
|
+
: `${opt.containerHeight ?? 520}px`
|
|
2041
|
+
}; --sg-thead-h: ${headerHeight}px; --sg-frozen-h: ${frozenBandHeight}px; --sg-pinned-row-h: ${typeof opt.rowHeight === "number" ? opt.rowHeight : 30}px;`}
|
|
2042
|
+
>
|
|
2043
|
+
<div
|
|
2044
|
+
class="sv-grid-container sv-grid-container-custom-scrollbars"
|
|
2045
|
+
class:sv-grid-has-vscroll={hasMeasured && hasVerticalOverflow}
|
|
2046
|
+
class:sv-grid-has-hscroll={hasMeasured && hasHorizontalOverflow}
|
|
2047
|
+
class:sv-grid-narrow={ctrl.isNarrowResponsive}
|
|
2048
|
+
bind:this={ctrl.scrollContainer}
|
|
2049
|
+
onscroll={onBodyScroll}
|
|
2050
|
+
style={`overflow: auto; position: relative; height: calc(100% - ${hasMeasured && hasHorizontalOverflow ? 16 : 0}px);`}
|
|
2051
|
+
>
|
|
2052
|
+
<table
|
|
2053
|
+
bind:this={ctrl.gridRootEl}
|
|
2054
|
+
class="sv-grid-table"
|
|
2055
|
+
class:sv-grid-no-row-hover={opt.enableRowHover !== true}
|
|
2056
|
+
{...getGridRootA11yProps({
|
|
2057
|
+
activeDescendantId,
|
|
2058
|
+
rowCount: allRows.length,
|
|
2059
|
+
colCount: allColumns.length,
|
|
2060
|
+
treegrid: !!opt.serverGroup || !!opt.treeData,
|
|
2061
|
+
})}
|
|
2062
|
+
onkeydown={onGridKeyDown}
|
|
2063
|
+
onpaste={onGridPaste}
|
|
2064
|
+
style={`min-width: ${totalColumnWidth}px;`}
|
|
2065
|
+
>
|
|
2066
|
+
<!-- svelte-ignore a11y_no_redundant_roles -->
|
|
2067
|
+
<thead class="sv-grid-head" bind:this={ctrl.theadEl} role="rowgroup">
|
|
2068
|
+
<!-- Multi-level group header rows. Only present when the
|
|
2069
|
+
consumer's column tree has `columns: [...]` nesting.
|
|
2070
|
+
Each TH spans the leaf widths underneath via the
|
|
2071
|
+
precomputed `widthPx` + `colSpan` from groupHeaderRows. -->
|
|
2072
|
+
{#each groupHeaderRows as row (row.id)}
|
|
2073
|
+
<tr
|
|
2074
|
+
class="sv-grid-row sv-grid-header-row sv-grid-group-header-row"
|
|
2075
|
+
{...getGridRowA11yProps()}
|
|
2076
|
+
style={opt.headerHeight
|
|
2077
|
+
? `height: ${opt.headerHeight}px;`
|
|
2078
|
+
: undefined}
|
|
2079
|
+
>
|
|
2080
|
+
{#if showRowNumbersEffective}
|
|
2081
|
+
<th
|
|
2082
|
+
class="sv-grid-column sv-grid-row-number-column"
|
|
2083
|
+
style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; left: 0;`}
|
|
2084
|
+
aria-hidden="true"
|
|
2085
|
+
></th>
|
|
2086
|
+
{/if}
|
|
2087
|
+
{#if showRowSelectionEffective}
|
|
2088
|
+
<th
|
|
2089
|
+
class="sv-grid-column sv-grid-selection-column"
|
|
2090
|
+
style={`width: ${selectionColumnWidth}px; min-width: ${selectionColumnWidth}px; max-width: ${selectionColumnWidth}px; left: ${showRowNumbersEffective ? rowNumberColumnWidth : 0}px;`}
|
|
2091
|
+
aria-hidden="true"
|
|
2092
|
+
></th>
|
|
2093
|
+
{/if}
|
|
2094
|
+
{#if columnVirtualizationEnabled && columnWindowStart > 0}
|
|
2095
|
+
<th
|
|
2096
|
+
class="sv-grid-column sv-grid-column-spacer"
|
|
2097
|
+
aria-hidden="true"
|
|
2098
|
+
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
2099
|
+
></th>
|
|
2100
|
+
{/if}
|
|
2101
|
+
{#each row.cells as cell (cell.key)}
|
|
2102
|
+
<th
|
|
2103
|
+
class="sv-grid-column sv-grid-group-header-cell"
|
|
2104
|
+
class:sv-grid-group-header-placeholder={cell.isPlaceholder}
|
|
2105
|
+
colspan={cell.colSpan}
|
|
2106
|
+
style={`width: ${cell.widthPx}px; min-width: ${cell.widthPx}px; max-width: ${cell.widthPx}px;`}
|
|
2107
|
+
>
|
|
2108
|
+
{#if !cell.isPlaceholder}
|
|
2109
|
+
{#if cell.collapsible}
|
|
2110
|
+
<button
|
|
1633
2111
|
type="button"
|
|
1634
2112
|
class="sv-grid-group-toggle"
|
|
1635
2113
|
class:is-collapsed={cell.collapsed}
|
|
@@ -1723,6 +2201,7 @@
|
|
|
1723
2201
|
)}
|
|
1724
2202
|
<th
|
|
1725
2203
|
class="sv-grid-column"
|
|
2204
|
+
class:sv-grid-column-collapsed={!!collapsedColumns[header.column.id]}
|
|
1726
2205
|
class:is-drag-target-before={colDropOnId ===
|
|
1727
2206
|
header.column.id && colDropSide === "before"}
|
|
1728
2207
|
class:is-drag-target-after={colDropOnId ===
|
|
@@ -2180,6 +2659,9 @@
|
|
|
2180
2659
|
</td>
|
|
2181
2660
|
</tr>
|
|
2182
2661
|
{:else if rowVirtualizationEnabled}
|
|
2662
|
+
{#each frozenRowList as row, rowIndex (row.id)}
|
|
2663
|
+
{@render bodyRow(row, rowIndex, rowStyleFor(rowIndex))}
|
|
2664
|
+
{/each}
|
|
2183
2665
|
{#if rowTopSpacer > 0}
|
|
2184
2666
|
<tr class="sv-grid-row sv-grid-row-spacer" aria-hidden="true">
|
|
2185
2667
|
<td
|
|
@@ -2191,256 +2673,17 @@
|
|
|
2191
2673
|
></td>
|
|
2192
2674
|
</tr>
|
|
2193
2675
|
{/if}
|
|
2676
|
+
<!-- The virtualizer counts every row, frozen ones included, so its
|
|
2677
|
+
offsets and sizes stay in row-index space; the frozen rows are
|
|
2678
|
+
rendered in the band above and SKIPPED here. Shifting the
|
|
2679
|
+
index instead (item i drawn as row i + N) sized every row with
|
|
2680
|
+
the height of the row N above it, so a row dragged taller under
|
|
2681
|
+
a freeze showed its neighbour's height. -->
|
|
2194
2682
|
{#each virtualRows as rowItem (rowItem.key)}
|
|
2195
2683
|
{@const rowIndex = rowItem.index}
|
|
2196
2684
|
{@const row = allRows[rowIndex]}
|
|
2197
|
-
{#if row}
|
|
2198
|
-
{
|
|
2199
|
-
{@render detailRowMarkup(row, rowIndex)}
|
|
2200
|
-
{:else if isGroupRow(row) && !groupColumnMode}
|
|
2201
|
-
<tr
|
|
2202
|
-
class="sv-grid-row sv-grid-group-row"
|
|
2203
|
-
class:sv-grid-row-selected={isRowSelected(row.id)}
|
|
2204
|
-
aria-level={row.depth + 1}
|
|
2205
|
-
aria-expanded={row.getIsExpanded?.() ? "true" : "false"}
|
|
2206
|
-
{...getGridRowA11yProps(rowIndex + 1)}
|
|
2207
|
-
style={rowHeightStyle(rowItem.size)}
|
|
2208
|
-
>
|
|
2209
|
-
<td
|
|
2210
|
-
class="sv-grid-cell sv-grid-group-cell"
|
|
2211
|
-
class:sv-grid-cell-active={activeCell.rowIndex ===
|
|
2212
|
-
rowIndex}
|
|
2213
|
-
colspan={allColumns.length +
|
|
2214
|
-
(showRowNumbersEffective ? 1 : 0) +
|
|
2215
|
-
(showRowSelectionEffective ? 1 : 0)}
|
|
2216
|
-
onclick={() => row.toggleExpanded?.()}
|
|
2217
|
-
>
|
|
2218
|
-
{@render groupRowContent(row)}
|
|
2219
|
-
</td>
|
|
2220
|
-
</tr>
|
|
2221
|
-
{:else}
|
|
2222
|
-
{@const userRowClass = computeRowClass(row, rowIndex)}
|
|
2223
|
-
<tr
|
|
2224
|
-
class={`sv-grid-row ${userRowClass} ${rowDropClass(rowIndex)}`}
|
|
2225
|
-
class:sv-grid-row-selected={isRowSelected(row.id)}
|
|
2226
|
-
class:sv-grid-row-alt={opt.zebraRows &&
|
|
2227
|
-
rowIndex % 2 === 1}
|
|
2228
|
-
class:sv-grid-row-draggable={rowDragManagedEffective}
|
|
2229
|
-
class:sv-grid-group-footer-row={isGroupFooterRow(row)}
|
|
2230
|
-
class:sv-grid-grand-total-row={isGrandTotalRow(row)}
|
|
2231
|
-
class:sv-grid-row-auto-height={autoRowHeightOn}
|
|
2232
|
-
{...getGridRowA11yProps(rowIndex + 1)}
|
|
2233
|
-
aria-level={sgAriaLevel(row)}
|
|
2234
|
-
aria-expanded={sgAriaExpanded(row)}
|
|
2235
|
-
{...rowDragAttrs(rowIndex)}
|
|
2236
|
-
style={rowHeightStyle(rowItem.size)}
|
|
2237
|
-
use:measureRowHeight={rowIndex}
|
|
2238
|
-
>
|
|
2239
|
-
{#if showRowNumbersEffective}
|
|
2240
|
-
<td
|
|
2241
|
-
class="sv-grid-cell sv-grid-row-number-cell"
|
|
2242
|
-
style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; left: 0;`}
|
|
2243
|
-
>{rowIndex + 1}</td
|
|
2244
|
-
>
|
|
2245
|
-
{/if}
|
|
2246
|
-
{#if showRowSelectionEffective}
|
|
2247
|
-
<td
|
|
2248
|
-
class="sv-grid-cell sv-grid-selection-cell"
|
|
2249
|
-
style={`width: ${selectionColumnWidth}px; min-width: ${selectionColumnWidth}px; max-width: ${selectionColumnWidth}px; left: ${showRowNumbersEffective ? rowNumberColumnWidth : 0}px;`}
|
|
2250
|
-
onclick={() => toggleRowSelectionById(row.id)}
|
|
2251
|
-
>
|
|
2252
|
-
<button
|
|
2253
|
-
type="button"
|
|
2254
|
-
class="sv-grid-checkbox"
|
|
2255
|
-
role="checkbox"
|
|
2256
|
-
aria-checked={isRowSelected(row.id)}
|
|
2257
|
-
aria-label="Select row"
|
|
2258
|
-
onclick={(event) => {
|
|
2259
|
-
event.stopPropagation();
|
|
2260
|
-
toggleRowSelectionById(row.id);
|
|
2261
|
-
}}
|
|
2262
|
-
onkeydown={(event) =>
|
|
2263
|
-
toggleCheckboxWithKeyboard(event, () => {
|
|
2264
|
-
event.stopPropagation();
|
|
2265
|
-
toggleRowSelectionById(row.id);
|
|
2266
|
-
})}
|
|
2267
|
-
></button>
|
|
2268
|
-
</td>
|
|
2269
|
-
{/if}
|
|
2270
|
-
{#if columnVirtualizationEnabled && columnWindowStart > 0}
|
|
2271
|
-
<td
|
|
2272
|
-
class="sv-grid-cell sv-grid-cell-spacer"
|
|
2273
|
-
aria-hidden="true"
|
|
2274
|
-
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
2275
|
-
></td>
|
|
2276
|
-
{/if}
|
|
2277
|
-
{#each renderedColumns as rendered (rendered.column.id)}
|
|
2278
|
-
{@const colIndex = rendered.item.index}
|
|
2279
|
-
{@const baseValue = getColumnBaseValue(
|
|
2280
|
-
row,
|
|
2281
|
-
rendered.column,
|
|
2282
|
-
)}
|
|
2283
|
-
{@const cellValue = getCellDisplayValue(
|
|
2284
|
-
row.id,
|
|
2285
|
-
rendered.column.id,
|
|
2286
|
-
baseValue,
|
|
2287
|
-
)}
|
|
2288
|
-
{@const isEditing =
|
|
2289
|
-
ctrl.editingCell?.rowId === row.id &&
|
|
2290
|
-
ctrl.editingCell?.columnId === rendered.column.id}
|
|
2291
|
-
{@const inRowEdit =
|
|
2292
|
-
!!fullRowEdit &&
|
|
2293
|
-
fullRowEdit.rowId === row.id &&
|
|
2294
|
-
rendered.column.id in fullRowEdit.draft}
|
|
2295
|
-
{@const rangeEdges = getCellRangeEdges(
|
|
2296
|
-
rowIndex,
|
|
2297
|
-
colIndex,
|
|
2298
|
-
)}
|
|
2299
|
-
{@const fillEdges = fillMarqueeEdges(rowIndex, colIndex)}
|
|
2300
|
-
{@const hasFillHandle =
|
|
2301
|
-
fillHandleCell &&
|
|
2302
|
-
fillHandleCell.rowIndex === rowIndex &&
|
|
2303
|
-
fillHandleCell.colIndex === colIndex}
|
|
2304
|
-
{@const userCellClass = computeCellClass(
|
|
2305
|
-
row,
|
|
2306
|
-
rendered.column,
|
|
2307
|
-
)}
|
|
2308
|
-
{@const cellValidity = computeCellValidity(
|
|
2309
|
-
row,
|
|
2310
|
-
rendered.column,
|
|
2311
|
-
)}
|
|
2312
|
-
{@const cellNote = computeCellNote(
|
|
2313
|
-
row,
|
|
2314
|
-
rendered.column,
|
|
2315
|
-
)}
|
|
2316
|
-
<td
|
|
2317
|
-
class={`sv-grid-cell ${userCellClass}`}
|
|
2318
|
-
class:sv-grid-cell-editing={isEditing || inRowEdit}
|
|
2319
|
-
class:sv-grid-cell-active={activeCell.rowIndex ===
|
|
2320
|
-
rowIndex && activeCell.colIndex === colIndex}
|
|
2321
|
-
class:sv-grid-cell-has-fill-handle={hasFillHandle}
|
|
2322
|
-
class:sv-grid-cell-cf={hasConditionalFormats}
|
|
2323
|
-
class:sv-grid-cell-invalid={cellValidity.invalid}
|
|
2324
|
-
class:sv-grid-cell-has-note={cellNote != null}
|
|
2325
|
-
aria-invalid={cellValidity.invalid ? "true" : undefined}
|
|
2326
|
-
title={cellValidity.message ?? undefined}
|
|
2327
|
-
data-svgrid-row={rowIndex}
|
|
2328
|
-
data-svgrid-col={colIndex}
|
|
2329
|
-
data-col-id={rendered.column.id}
|
|
2330
|
-
data-align={getColumnAlign(rendered.column)}
|
|
2331
|
-
data-pinned={isColumnPinned(rendered.column.id) ??
|
|
2332
|
-
undefined}
|
|
2333
|
-
data-selected-range={rangeEdges ? "true" : undefined}
|
|
2334
|
-
data-range-top={rangeEdges?.top ? "true" : undefined}
|
|
2335
|
-
data-range-bottom={rangeEdges?.bottom
|
|
2336
|
-
? "true"
|
|
2337
|
-
: undefined}
|
|
2338
|
-
data-range-left={rangeEdges?.left
|
|
2339
|
-
? "true"
|
|
2340
|
-
: undefined}
|
|
2341
|
-
data-range-right={rangeEdges?.right
|
|
2342
|
-
? "true"
|
|
2343
|
-
: undefined}
|
|
2344
|
-
data-fill-preview={isInFillPreview(rowIndex, colIndex)
|
|
2345
|
-
? "true"
|
|
2346
|
-
: undefined}
|
|
2347
|
-
data-fill-top={fillEdges?.top ? "true" : undefined}
|
|
2348
|
-
data-fill-bottom={fillEdges?.bottom ? "true" : undefined}
|
|
2349
|
-
data-fill-left={fillEdges?.left ? "true" : undefined}
|
|
2350
|
-
data-fill-right={fillEdges?.right ? "true" : undefined}
|
|
2351
|
-
style={`width: ${rendered.item.size}px; min-width: ${rendered.item.size}px; max-width: ${rendered.item.size}px; ${cellPinStyle(rendered.column.id)}`}
|
|
2352
|
-
onpointerdown={(event) =>
|
|
2353
|
-
onCellPointerDown(rowIndex, colIndex, event)}
|
|
2354
|
-
onpointerenter={() =>
|
|
2355
|
-
onCellPointerEnter(rowIndex, colIndex)}
|
|
2356
|
-
ondblclick={() =>
|
|
2357
|
-
emitCellDoubleClick(rowIndex, colIndex)}
|
|
2358
|
-
onclick={() => onCellClick(rowIndex, colIndex)}
|
|
2359
|
-
oncontextmenu={(event) =>
|
|
2360
|
-
openContextMenu(
|
|
2361
|
-
event,
|
|
2362
|
-
rowIndex,
|
|
2363
|
-
colIndex,
|
|
2364
|
-
rendered.column.id,
|
|
2365
|
-
)}
|
|
2366
|
-
use:cellFlashAction={{
|
|
2367
|
-
rowId: row.id,
|
|
2368
|
-
value: cellValue,
|
|
2369
|
-
active: !!rendered.column.columnDef.cellFlash,
|
|
2370
|
-
className: flashClassFor(
|
|
2371
|
-
rendered.column.columnDef.cellFlash,
|
|
2372
|
-
),
|
|
2373
|
-
}}
|
|
2374
|
-
role="gridcell"
|
|
2375
|
-
id={getGridCellDomId(ctrl.gridDomId, rowIndex, colIndex)}
|
|
2376
|
-
aria-colindex={colIndex + 1}
|
|
2377
|
-
aria-rowindex={rowIndex + 1}
|
|
2378
|
-
aria-selected={isRowSelected(row.id)}
|
|
2379
|
-
>
|
|
2380
|
-
{#if inRowEdit || isEditing}
|
|
2381
|
-
<!-- The editing cell stays empty until the lazy
|
|
2382
|
-
editor chunk lands, exactly as it already did
|
|
2383
|
-
for the dropdown / date editors. Falling back
|
|
2384
|
-
to the read-only value here would flash stale
|
|
2385
|
-
content into a cell the user is editing. -->
|
|
2386
|
-
{#if CellEditor}
|
|
2387
|
-
<CellEditor {ctrl} column={rendered.column} {row} fullRow={inRowEdit} />
|
|
2388
|
-
{/if}
|
|
2389
|
-
{:else}
|
|
2390
|
-
{#if plainCellBody}
|
|
2391
|
-
{@render cellBody(row, rendered.column, cellValue)}
|
|
2392
|
-
{:else}
|
|
2393
|
-
{@render cellBodyWithFormat(
|
|
2394
|
-
row,
|
|
2395
|
-
rendered.column,
|
|
2396
|
-
cellValue,
|
|
2397
|
-
)}
|
|
2398
|
-
{/if}
|
|
2399
|
-
{/if}
|
|
2400
|
-
{#if !isEditing && fillHandleCell && fillHandleCell.rowIndex === rowIndex && fillHandleCell.colIndex === colIndex}
|
|
2401
|
-
<!-- Excel-style fill handle: drag down/right to
|
|
2402
|
-
extend the selection and pattern-fill the new
|
|
2403
|
-
cells on release. Rendered inside the bottom-
|
|
2404
|
-
right cell of the selection range (or active
|
|
2405
|
-
cell if there's no range). -->
|
|
2406
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
2407
|
-
<div
|
|
2408
|
-
class="sv-grid-fill-handle"
|
|
2409
|
-
role="button"
|
|
2410
|
-
aria-label="Fill handle"
|
|
2411
|
-
tabindex={-1}
|
|
2412
|
-
onpointerdown={(event) =>
|
|
2413
|
-
startFillDrag(event, rowIndex, colIndex)}
|
|
2414
|
-
></div>
|
|
2415
|
-
{/if}
|
|
2416
|
-
{#if cellNote != null && !isEditing}
|
|
2417
|
-
<span
|
|
2418
|
-
class="sv-grid-cell-note-corner"
|
|
2419
|
-
aria-label="Note"
|
|
2420
|
-
onpointerenter={(event) => {
|
|
2421
|
-
event.stopPropagation();
|
|
2422
|
-
showTooltipFor(
|
|
2423
|
-
event.currentTarget as HTMLElement,
|
|
2424
|
-
cellNote,
|
|
2425
|
-
);
|
|
2426
|
-
}}
|
|
2427
|
-
onpointerleave={(event) => {
|
|
2428
|
-
event.stopPropagation();
|
|
2429
|
-
hideTooltip();
|
|
2430
|
-
}}
|
|
2431
|
-
></span>
|
|
2432
|
-
{/if}
|
|
2433
|
-
</td>
|
|
2434
|
-
{/each}
|
|
2435
|
-
{#if columnVirtualizationEnabled && columnWindowRightSpacer > 0}
|
|
2436
|
-
<td
|
|
2437
|
-
class="sv-grid-cell sv-grid-cell-spacer"
|
|
2438
|
-
aria-hidden="true"
|
|
2439
|
-
style={`width: ${columnWindowRightSpacer}px; min-width: ${columnWindowRightSpacer}px; max-width: ${columnWindowRightSpacer}px;`}
|
|
2440
|
-
></td>
|
|
2441
|
-
{/if}
|
|
2442
|
-
</tr>
|
|
2443
|
-
{/if}
|
|
2685
|
+
{#if row && rowIndex >= frozenRowCount}
|
|
2686
|
+
{@render bodyRow(row, rowIndex, rowHeightStyle(rowItem.size))}
|
|
2444
2687
|
{/if}
|
|
2445
2688
|
{/each}
|
|
2446
2689
|
{#if rowBottomSpacer > 0}
|
|
@@ -2456,253 +2699,7 @@
|
|
|
2456
2699
|
{/if}
|
|
2457
2700
|
{:else}
|
|
2458
2701
|
{#each allRows as row, rowIndex (row.id)}
|
|
2459
|
-
{
|
|
2460
|
-
{@render detailRowMarkup(row, rowIndex)}
|
|
2461
|
-
{:else if isGroupRow(row) && !groupColumnMode}
|
|
2462
|
-
<tr
|
|
2463
|
-
class="sv-grid-row sv-grid-group-row"
|
|
2464
|
-
class:sv-grid-row-selected={isRowSelected(row.id)}
|
|
2465
|
-
aria-level={row.depth + 1}
|
|
2466
|
-
aria-expanded={row.getIsExpanded?.() ? "true" : "false"}
|
|
2467
|
-
{...getGridRowA11yProps(rowIndex + 1)}
|
|
2468
|
-
style={rowStyleFor(rowIndex)}
|
|
2469
|
-
>
|
|
2470
|
-
<td
|
|
2471
|
-
class="sv-grid-cell sv-grid-group-cell"
|
|
2472
|
-
class:sv-grid-cell-active={activeCell.rowIndex ===
|
|
2473
|
-
rowIndex}
|
|
2474
|
-
colspan={allColumns.length +
|
|
2475
|
-
(showRowNumbersEffective ? 1 : 0) +
|
|
2476
|
-
(showRowSelectionEffective ? 1 : 0)}
|
|
2477
|
-
onclick={() => row.toggleExpanded?.()}
|
|
2478
|
-
>
|
|
2479
|
-
{@render groupRowContent(row)}
|
|
2480
|
-
</td>
|
|
2481
|
-
</tr>
|
|
2482
|
-
{:else}
|
|
2483
|
-
{@const userRowClass = computeRowClass(row, rowIndex)}
|
|
2484
|
-
<tr
|
|
2485
|
-
class={`sv-grid-row ${userRowClass} ${rowDropClass(rowIndex)}`}
|
|
2486
|
-
class:sv-grid-row-selected={isRowSelected(row.id)}
|
|
2487
|
-
class:sv-grid-row-alt={opt.zebraRows &&
|
|
2488
|
-
rowIndex % 2 === 1}
|
|
2489
|
-
class:sv-grid-row-draggable={rowDragManagedEffective}
|
|
2490
|
-
class:sv-grid-group-footer-row={isGroupFooterRow(row)}
|
|
2491
|
-
class:sv-grid-grand-total-row={isGrandTotalRow(row)}
|
|
2492
|
-
class:sv-grid-row-auto-height={autoRowHeightOn}
|
|
2493
|
-
{...getGridRowA11yProps(rowIndex + 1)}
|
|
2494
|
-
aria-level={sgAriaLevel(row)}
|
|
2495
|
-
aria-expanded={sgAriaExpanded(row)}
|
|
2496
|
-
{...rowDragAttrs(rowIndex)}
|
|
2497
|
-
style={rowStyleFor(rowIndex)}
|
|
2498
|
-
use:measureRowHeight={rowIndex}
|
|
2499
|
-
>
|
|
2500
|
-
{#if showRowNumbersEffective}
|
|
2501
|
-
<td
|
|
2502
|
-
class="sv-grid-cell sv-grid-row-number-cell"
|
|
2503
|
-
style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; left: 0;`}
|
|
2504
|
-
>{rowIndex + 1}</td
|
|
2505
|
-
>
|
|
2506
|
-
{/if}
|
|
2507
|
-
{#if showRowSelectionEffective}
|
|
2508
|
-
<td
|
|
2509
|
-
class="sv-grid-cell sv-grid-selection-cell"
|
|
2510
|
-
style={`width: ${selectionColumnWidth}px; min-width: ${selectionColumnWidth}px; max-width: ${selectionColumnWidth}px; left: ${showRowNumbersEffective ? rowNumberColumnWidth : 0}px;`}
|
|
2511
|
-
onclick={() => toggleRowSelectionById(row.id)}
|
|
2512
|
-
>
|
|
2513
|
-
<button
|
|
2514
|
-
type="button"
|
|
2515
|
-
class="sv-grid-checkbox"
|
|
2516
|
-
role="checkbox"
|
|
2517
|
-
aria-checked={isRowSelected(row.id)}
|
|
2518
|
-
aria-label="Select row"
|
|
2519
|
-
onclick={(event) => {
|
|
2520
|
-
event.stopPropagation();
|
|
2521
|
-
toggleRowSelectionById(row.id);
|
|
2522
|
-
}}
|
|
2523
|
-
onkeydown={(event) =>
|
|
2524
|
-
toggleCheckboxWithKeyboard(event, () => {
|
|
2525
|
-
event.stopPropagation();
|
|
2526
|
-
toggleRowSelectionById(row.id);
|
|
2527
|
-
})}
|
|
2528
|
-
></button>
|
|
2529
|
-
</td>
|
|
2530
|
-
{/if}
|
|
2531
|
-
{#if columnVirtualizationEnabled && columnWindowStart > 0}
|
|
2532
|
-
<td
|
|
2533
|
-
class="sv-grid-cell sv-grid-cell-spacer"
|
|
2534
|
-
aria-hidden="true"
|
|
2535
|
-
style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
|
|
2536
|
-
></td>
|
|
2537
|
-
{/if}
|
|
2538
|
-
{#each renderedColumns as rendered (rendered.column.id)}
|
|
2539
|
-
{@const colIndex = rendered.item.index}
|
|
2540
|
-
{@const baseValue = getColumnBaseValue(
|
|
2541
|
-
row,
|
|
2542
|
-
rendered.column,
|
|
2543
|
-
)}
|
|
2544
|
-
{@const cellValue = getCellDisplayValue(
|
|
2545
|
-
row.id,
|
|
2546
|
-
rendered.column.id,
|
|
2547
|
-
baseValue,
|
|
2548
|
-
)}
|
|
2549
|
-
{@const isEditing =
|
|
2550
|
-
ctrl.editingCell?.rowId === row.id &&
|
|
2551
|
-
ctrl.editingCell?.columnId === rendered.column.id}
|
|
2552
|
-
{@const inRowEdit =
|
|
2553
|
-
!!fullRowEdit &&
|
|
2554
|
-
fullRowEdit.rowId === row.id &&
|
|
2555
|
-
rendered.column.id in fullRowEdit.draft}
|
|
2556
|
-
{@const rangeEdges = getCellRangeEdges(
|
|
2557
|
-
rowIndex,
|
|
2558
|
-
colIndex,
|
|
2559
|
-
)}
|
|
2560
|
-
{@const userCellClass = computeCellClass(
|
|
2561
|
-
row,
|
|
2562
|
-
rendered.column,
|
|
2563
|
-
)}
|
|
2564
|
-
{@const cellTooltip = computeCellTooltip(
|
|
2565
|
-
row,
|
|
2566
|
-
rendered.column,
|
|
2567
|
-
)}
|
|
2568
|
-
{@const cellValidity = computeCellValidity(
|
|
2569
|
-
row,
|
|
2570
|
-
rendered.column,
|
|
2571
|
-
)}
|
|
2572
|
-
{@const cellNote = computeCellNote(row, rendered.column)}
|
|
2573
|
-
<td
|
|
2574
|
-
class={`sv-grid-cell ${userCellClass}`}
|
|
2575
|
-
class:sv-grid-cell-editing={isEditing || inRowEdit}
|
|
2576
|
-
class:sv-grid-cell-active={activeCell.rowIndex ===
|
|
2577
|
-
rowIndex && activeCell.colIndex === colIndex}
|
|
2578
|
-
class:sv-grid-cell-cf={hasConditionalFormats}
|
|
2579
|
-
class:sv-grid-cell-invalid={cellValidity.invalid}
|
|
2580
|
-
class:sv-grid-cell-has-note={cellNote != null}
|
|
2581
|
-
aria-invalid={cellValidity.invalid ? "true" : undefined}
|
|
2582
|
-
data-svgrid-row={rowIndex}
|
|
2583
|
-
data-svgrid-col={colIndex}
|
|
2584
|
-
data-col-id={rendered.column.id}
|
|
2585
|
-
data-align={getColumnAlign(rendered.column)}
|
|
2586
|
-
data-pinned={isColumnPinned(rendered.column.id) ??
|
|
2587
|
-
undefined}
|
|
2588
|
-
data-selected-range={rangeEdges ? "true" : undefined}
|
|
2589
|
-
data-range-top={rangeEdges?.top ? "true" : undefined}
|
|
2590
|
-
data-range-bottom={rangeEdges?.bottom
|
|
2591
|
-
? "true"
|
|
2592
|
-
: undefined}
|
|
2593
|
-
data-range-left={rangeEdges?.left ? "true" : undefined}
|
|
2594
|
-
data-range-right={rangeEdges?.right
|
|
2595
|
-
? "true"
|
|
2596
|
-
: undefined}
|
|
2597
|
-
style={`width: ${rendered.item.size}px; min-width: ${rendered.item.size}px; max-width: ${rendered.item.size}px; ${cellPinStyle(rendered.column.id)}`}
|
|
2598
|
-
onpointerdown={(event) =>
|
|
2599
|
-
onCellPointerDown(rowIndex, colIndex, event)}
|
|
2600
|
-
onpointerenter={(event) => {
|
|
2601
|
-
onCellPointerEnter(rowIndex, colIndex);
|
|
2602
|
-
// Column tooltip fires on whole-cell hover.
|
|
2603
|
-
// Per-cell notes are gated on the corner hot-
|
|
2604
|
-
// zone below (Excel-style: hover the small
|
|
2605
|
-
// triangle to read the note). A validation
|
|
2606
|
-
// message (when the cell is invalid) wins over
|
|
2607
|
-
// the plain column tooltip.
|
|
2608
|
-
const tip =
|
|
2609
|
-
cellValidity.invalid && cellValidity.message
|
|
2610
|
-
? cellValidity.message
|
|
2611
|
-
: cellTooltip;
|
|
2612
|
-
if (tip)
|
|
2613
|
-
showTooltipFor(
|
|
2614
|
-
event.currentTarget as HTMLElement,
|
|
2615
|
-
tip,
|
|
2616
|
-
);
|
|
2617
|
-
}}
|
|
2618
|
-
onpointerleave={hideTooltip}
|
|
2619
|
-
ondblclick={() =>
|
|
2620
|
-
emitCellDoubleClick(rowIndex, colIndex)}
|
|
2621
|
-
onclick={() => onCellClick(rowIndex, colIndex)}
|
|
2622
|
-
oncontextmenu={(event) =>
|
|
2623
|
-
openContextMenu(
|
|
2624
|
-
event,
|
|
2625
|
-
rowIndex,
|
|
2626
|
-
colIndex,
|
|
2627
|
-
rendered.column.id,
|
|
2628
|
-
)}
|
|
2629
|
-
use:cellFlashAction={{
|
|
2630
|
-
rowId: row.id,
|
|
2631
|
-
value: cellValue,
|
|
2632
|
-
active: !!rendered.column.columnDef.cellFlash,
|
|
2633
|
-
className: flashClassFor(
|
|
2634
|
-
rendered.column.columnDef.cellFlash,
|
|
2635
|
-
),
|
|
2636
|
-
}}
|
|
2637
|
-
{...getGridCellA11yProps({
|
|
2638
|
-
id: getGridCellDomId(ctrl.gridDomId, rowIndex, colIndex),
|
|
2639
|
-
rowIndex: rowIndex + 1,
|
|
2640
|
-
colIndex: colIndex + 1,
|
|
2641
|
-
selected: isRowSelected(row.id),
|
|
2642
|
-
})}
|
|
2643
|
-
>
|
|
2644
|
-
{#if inRowEdit || isEditing}
|
|
2645
|
-
<!-- Empty until the lazy editor chunk lands - see the
|
|
2646
|
-
matching branch in the pinned-row body above. -->
|
|
2647
|
-
{#if CellEditor}
|
|
2648
|
-
<CellEditor {ctrl} column={rendered.column} {row} fullRow={inRowEdit} />
|
|
2649
|
-
{/if}
|
|
2650
|
-
{:else}
|
|
2651
|
-
{@render cellBodyWithFormat(
|
|
2652
|
-
row,
|
|
2653
|
-
rendered.column,
|
|
2654
|
-
cellValue,
|
|
2655
|
-
)}
|
|
2656
|
-
{/if}
|
|
2657
|
-
{#if cellValidity.invalid && cellValidity.message}
|
|
2658
|
-
<!-- The validation message, for assistive tech.
|
|
2659
|
-
On this path the message is shown visually by
|
|
2660
|
-
the pointerenter tooltip above, which is mouse-
|
|
2661
|
-
only - a keyboard or screen-reader user would
|
|
2662
|
-
get `aria-invalid` with no reason attached.
|
|
2663
|
-
A visually-hidden span inside the cell is read
|
|
2664
|
-
as part of the cell when focus lands on it, and
|
|
2665
|
-
unlike `title` it adds no second native tooltip
|
|
2666
|
-
competing with the custom one. -->
|
|
2667
|
-
<span class="sv-grid-sr-only"
|
|
2668
|
-
>{cellValidity.message}</span
|
|
2669
|
-
>
|
|
2670
|
-
{/if}
|
|
2671
|
-
{#if cellNote != null && !isEditing}
|
|
2672
|
-
<!-- Excel-style per-cell note indicator. The
|
|
2673
|
-
triangle itself is the hot-zone; hover
|
|
2674
|
-
just the corner to see the note (Excel
|
|
2675
|
-
red-dot behaviour). The cell-level
|
|
2676
|
-
tooltip handler only shows the column
|
|
2677
|
-
tooltip, so the two surfaces stay
|
|
2678
|
-
separate. -->
|
|
2679
|
-
<span
|
|
2680
|
-
class="sv-grid-cell-note-corner"
|
|
2681
|
-
aria-label="Note"
|
|
2682
|
-
onpointerenter={(event) => {
|
|
2683
|
-
event.stopPropagation();
|
|
2684
|
-
showTooltipFor(
|
|
2685
|
-
event.currentTarget as HTMLElement,
|
|
2686
|
-
cellNote,
|
|
2687
|
-
);
|
|
2688
|
-
}}
|
|
2689
|
-
onpointerleave={(event) => {
|
|
2690
|
-
event.stopPropagation();
|
|
2691
|
-
hideTooltip();
|
|
2692
|
-
}}
|
|
2693
|
-
></span>
|
|
2694
|
-
{/if}
|
|
2695
|
-
</td>
|
|
2696
|
-
{/each}
|
|
2697
|
-
{#if columnVirtualizationEnabled && columnWindowRightSpacer > 0}
|
|
2698
|
-
<td
|
|
2699
|
-
class="sv-grid-cell sv-grid-cell-spacer"
|
|
2700
|
-
aria-hidden="true"
|
|
2701
|
-
style={`width: ${columnWindowRightSpacer}px; min-width: ${columnWindowRightSpacer}px; max-width: ${columnWindowRightSpacer}px;`}
|
|
2702
|
-
></td>
|
|
2703
|
-
{/if}
|
|
2704
|
-
</tr>
|
|
2705
|
-
{/if}
|
|
2702
|
+
{@render bodyRow(row, rowIndex, rowStyleFor(rowIndex))}
|
|
2706
2703
|
{/each}
|
|
2707
2704
|
{/if}
|
|
2708
2705
|
</tbody>
|
|
@@ -3132,7 +3129,7 @@
|
|
|
3132
3129
|
`absolute` against it, so moving it outside would anchor it to the
|
|
3133
3130
|
page. The renderer is Enterprise and arrives through the
|
|
3134
3131
|
selection-bar-view seam; without it, a short note in its place. -->
|
|
3135
|
-
{#if ctrl.
|
|
3132
|
+
{#if ctrl.selectionBarVisible}
|
|
3136
3133
|
{#if getSelectionBarView()}
|
|
3137
3134
|
{@const SelectionBarView = getSelectionBarView()}
|
|
3138
3135
|
<SelectionBarView {ctrl} />
|