@svgrid/grid 3.0.3 → 4.0.0

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.
Files changed (318) hide show
  1. package/README.md +5 -4
  2. package/dist/GridMenus.svelte +44 -2
  3. package/dist/SvChartMenu.svelte +69 -0
  4. package/dist/SvChartMenu.svelte.d.ts +11 -0
  5. package/dist/SvChartPanes.svelte +113 -0
  6. package/dist/SvChartPanes.svelte.d.ts +20 -0
  7. package/dist/SvGrid.controller.svelte.d.ts +122 -41
  8. package/dist/SvGrid.controller.svelte.js +845 -207
  9. package/dist/SvGrid.css +178 -2
  10. package/dist/SvGrid.svelte +467 -516
  11. package/dist/SvGrid.types.d.ts +315 -12
  12. package/dist/SvGridCellEditor.svelte +17 -0
  13. package/dist/SvGridChart.svelte +2257 -329
  14. package/dist/SvGridChart.svelte.d.ts +6 -106
  15. package/dist/SvGridChart.types.d.ts +338 -0
  16. package/dist/SvGridChart.types.js +1 -0
  17. package/dist/SvGridChartBuilder.svelte +323 -0
  18. package/dist/SvGridChartBuilder.svelte.d.ts +34 -0
  19. package/dist/SvGridChartPanel.svelte +221 -275
  20. package/dist/SvGridChartPickers.svelte +417 -0
  21. package/dist/SvGridChartPickers.svelte.d.ts +70 -0
  22. package/dist/SvGridChartView.svelte +13 -14
  23. package/dist/SvModal.svelte +7 -1
  24. package/dist/SvModal.svelte.d.ts +5 -0
  25. package/dist/ai.d.ts +40 -8
  26. package/dist/ai.js +135 -24
  27. package/dist/aria.d.ts +11 -0
  28. package/dist/build-api.js +119 -28
  29. package/dist/cdn/GridMenus-CLI93jke.js +635 -0
  30. package/dist/cdn/{GridMenus-DhdPF700.js → GridMenus-CwooL3QA.js} +240 -207
  31. package/dist/cdn/SvChartMenu-Bl6PBbkT.js +58 -0
  32. package/dist/cdn/SvChartMenu-FBSMINA6.js +59 -0
  33. package/dist/cdn/{SvDateRangeInput-DMLKmGEc.js → SvDateRangeInput-BeU_TkbQ.js} +15 -14
  34. package/dist/cdn/{SvDateRangeInput-CaOuMs8O.js → SvDateRangeInput-yX8vzleW.js} +38 -37
  35. package/dist/cdn/{SvDateTimePicker-sonaH0oh.js → SvDateTimePicker-DQwt4UAs.js} +139 -180
  36. package/dist/cdn/SvDateTimePicker-vNU6bZ-q.js +775 -0
  37. package/dist/cdn/{SvGridCellEditor-CjEJMJKc.js → SvGridCellEditor-B1p-vCK5.js} +188 -178
  38. package/dist/cdn/{SvGridCellEditor-BWnTi2N7.js → SvGridCellEditor-D_0q4xAu.js} +98 -88
  39. package/dist/cdn/SvGridChart-C3EWAZaM.js +3574 -0
  40. package/dist/cdn/SvGridChart-DzLkSwCH.js +3572 -0
  41. package/dist/cdn/SvGridChartBuilder-BE2T1ykB.js +783 -0
  42. package/dist/cdn/SvGridChartBuilder-CfII62sZ.js +784 -0
  43. package/dist/cdn/SvGridChartPanel-CCX5_Wgd.js +675 -0
  44. package/dist/cdn/SvGridChartPanel-xf4sWVTo.js +699 -0
  45. package/dist/cdn/SvGridChartView-CfuXmY5I.js +48 -0
  46. package/dist/cdn/SvGridChartView-DYwabQWj.js +47 -0
  47. package/dist/cdn/SvMenuList-CLN8OuIK.js +452 -0
  48. package/dist/cdn/SvMenuList-DkGhNKjf.js +451 -0
  49. package/dist/cdn/SvModal-BAE-pjZX.js +396 -0
  50. package/dist/cdn/SvModal-CIZWCcad.js +397 -0
  51. package/dist/cdn/chart-Dz7SqMXH.js +4341 -0
  52. package/dist/cdn/chart-export-BXqueeWJ.js +135 -0
  53. package/dist/cdn/chart-export-pdf-DONFUnNY.js +110 -0
  54. package/dist/cdn/chart-panel-messages-CUbf2R4i.js +1052 -0
  55. package/dist/cdn/chart-panel-messages-CmNrMdsr.js +1053 -0
  56. package/dist/cdn/chart-summary-BJW_tg_X.js +138 -0
  57. package/dist/cdn/chart-trend-CaN9mDEV.js +50 -0
  58. package/dist/cdn/chart-validate-HcOVFAfJ.js +188 -0
  59. package/dist/cdn/{column-resize-DsfNXMom.js → column-resize-DpmOLfRp.js} +3 -3
  60. package/dist/cdn/{date-format-BnnHlqGw.js → date-format-CtquV-p3.js} +583 -603
  61. package/dist/cdn/{date-format-BNii4zeD.js → date-format-D6KFzU_W.js} +347 -367
  62. package/dist/cdn/dismissable-DAHetSNk.js +44 -0
  63. package/dist/cdn/editor-contract-LXgQJAgd.js +22 -0
  64. package/dist/cdn/focus-trap-BBOQYBma.js +65 -0
  65. package/dist/cdn/{row-resize-BRcimkUT.js → row-resize-niQCp040.js} +44 -36
  66. package/dist/cdn/server-block-cache-CGoWz-87.js +237 -0
  67. package/dist/cdn/{src-D6XvA1Ij.js → src-B6HXdfHY.js} +10448 -10049
  68. package/dist/cdn/{src-Bugf5XlI.js → src-C6Iowdvs.js} +6908 -6509
  69. package/dist/cdn/svgrid.js +27 -15
  70. package/dist/cdn/svgrid.svelte-external.js +27 -15
  71. package/dist/cdn/validate-CchXzyrX.js +76 -0
  72. package/dist/chart-axes.d.ts +53 -0
  73. package/dist/chart-axes.js +351 -0
  74. package/dist/chart-cartesian.d.ts +88 -0
  75. package/dist/chart-cartesian.js +1862 -0
  76. package/dist/chart-decimate.d.ts +51 -0
  77. package/dist/chart-decimate.js +199 -0
  78. package/dist/chart-export-pdf.d.ts +47 -0
  79. package/dist/chart-export-pdf.js +187 -0
  80. package/dist/chart-export.d.ts +2 -1
  81. package/dist/chart-export.js +18 -3
  82. package/dist/chart-financial.d.ts +91 -0
  83. package/dist/chart-financial.js +175 -0
  84. package/dist/chart-flow.d.ts +10 -0
  85. package/dist/chart-flow.js +298 -0
  86. package/dist/chart-format.d.ts +18 -0
  87. package/dist/chart-format.js +65 -0
  88. package/dist/chart-grid.d.ts +8 -0
  89. package/dist/chart-grid.js +190 -0
  90. package/dist/chart-hierarchy.d.ts +17 -0
  91. package/dist/chart-hierarchy.js +146 -0
  92. package/dist/chart-indicators.d.ts +115 -0
  93. package/dist/chart-indicators.js +389 -0
  94. package/dist/chart-messages.d.ts +95 -0
  95. package/dist/chart-messages.js +92 -0
  96. package/dist/chart-motion.d.ts +16 -0
  97. package/dist/chart-motion.js +141 -0
  98. package/dist/chart-panel-messages.d.ts +265 -0
  99. package/dist/chart-panel-messages.js +285 -0
  100. package/dist/chart-pivot.d.ts +87 -0
  101. package/dist/chart-pivot.js +156 -0
  102. package/dist/chart-polar.d.ts +25 -0
  103. package/dist/chart-polar.js +700 -0
  104. package/dist/chart-samples.d.ts +16 -0
  105. package/dist/chart-samples.js +153 -0
  106. package/dist/chart-scale.d.ts +113 -0
  107. package/dist/chart-scale.js +405 -0
  108. package/dist/chart-stats.d.ts +111 -0
  109. package/dist/chart-stats.js +419 -0
  110. package/dist/chart-stream.d.ts +45 -0
  111. package/dist/chart-stream.js +91 -0
  112. package/dist/chart-summary.d.ts +27 -0
  113. package/dist/chart-summary.js +205 -0
  114. package/dist/chart-sync.svelte.d.ts +32 -0
  115. package/dist/chart-sync.svelte.js +23 -0
  116. package/dist/chart-table.d.ts +35 -0
  117. package/dist/chart-table.js +165 -0
  118. package/dist/chart-trend.d.ts +19 -0
  119. package/dist/chart-trend.js +61 -0
  120. package/dist/chart-types.d.ts +1456 -0
  121. package/dist/chart-types.js +6 -0
  122. package/dist/chart-validate.d.ts +43 -0
  123. package/dist/chart-validate.js +272 -0
  124. package/dist/chart-zoom.d.ts +40 -0
  125. package/dist/chart-zoom.js +145 -0
  126. package/dist/chart.d.ts +186 -865
  127. package/dist/chart.js +460 -2204
  128. package/dist/clipboard.d.ts +1 -0
  129. package/dist/clipboard.js +137 -18
  130. package/dist/column-resize.d.ts +3 -0
  131. package/dist/column-resize.js +4 -1
  132. package/dist/columns.js +3 -0
  133. package/dist/command-context.d.ts +19 -0
  134. package/dist/command-context.js +116 -0
  135. package/dist/core.d.ts +7 -0
  136. package/dist/core.js +36 -13
  137. package/dist/createPopoverSelect.svelte.d.ts +2 -2
  138. package/dist/editing.d.ts +2 -8
  139. package/dist/editing.js +272 -33
  140. package/dist/fill-patterns.d.ts +0 -5
  141. package/dist/fill-patterns.js +56 -2
  142. package/dist/grid-icons.d.ts +2 -2
  143. package/dist/grid-icons.js +2 -2
  144. package/dist/grid-messages.d.ts +12 -3
  145. package/dist/grid-messages.js +7 -0
  146. package/dist/history.d.ts +83 -0
  147. package/dist/history.js +133 -0
  148. package/dist/index.d.ts +27 -10
  149. package/dist/index.js +22 -5
  150. package/dist/keyboard-handlers.js +88 -31
  151. package/dist/keyboard.d.ts +55 -0
  152. package/dist/keyboard.js +132 -0
  153. package/dist/menus.d.ts +3 -1
  154. package/dist/menus.js +7 -2
  155. package/dist/merges.d.ts +97 -0
  156. package/dist/merges.js +147 -0
  157. package/dist/row-model.d.ts +133 -0
  158. package/dist/row-model.js +16 -0
  159. package/dist/row-resize.d.ts +3 -0
  160. package/dist/row-resize.js +14 -0
  161. package/dist/scroll-sync.d.ts +1 -0
  162. package/dist/scroll-sync.js +24 -0
  163. package/dist/selection.d.ts +18 -1
  164. package/dist/selection.js +138 -18
  165. package/dist/server-block-cache.d.ts +214 -0
  166. package/dist/server-block-cache.js +531 -0
  167. package/dist/server-data-source.d.ts +240 -6
  168. package/dist/server-data-source.js +220 -20
  169. package/dist/server.d.ts +13 -0
  170. package/dist/server.js +13 -0
  171. package/dist/shortcut-registry.d.ts +145 -0
  172. package/dist/shortcut-registry.js +44 -0
  173. package/dist/svgrid-wrapper.types.d.ts +213 -22
  174. package/dist/validate.d.ts +2 -0
  175. package/dist/validate.js +8 -3
  176. package/dist/virtualization/virtualizer.js +38 -17
  177. package/package.json +11 -1
  178. package/src/GridMenus.svelte +44 -2
  179. package/src/SvChartMenu.svelte +69 -0
  180. package/src/SvChartPanes.svelte +113 -0
  181. package/src/SvChartPanes.test.ts +66 -0
  182. package/src/SvGrid.controller.svelte.ts +818 -179
  183. package/src/SvGrid.css +178 -2
  184. package/src/SvGrid.svelte +467 -516
  185. package/src/SvGrid.types.ts +293 -11
  186. package/src/SvGridCellEditor.svelte +17 -0
  187. package/src/SvGridChart.svelte +2257 -329
  188. package/src/SvGridChart.test.ts +1419 -1
  189. package/src/SvGridChart.types.ts +336 -0
  190. package/src/SvGridChartBuilder.svelte +323 -0
  191. package/src/SvGridChartPanel.svelte +221 -275
  192. package/src/SvGridChartPickers.svelte +417 -0
  193. package/src/SvGridChartView.svelte +13 -14
  194. package/src/SvModal.svelte +7 -1
  195. package/src/SvModal.test.ts +9 -0
  196. package/src/ai.test.ts +57 -2
  197. package/src/ai.ts +166 -22
  198. package/src/aria.d.ts +11 -0
  199. package/src/build-api.coverage.test.ts +184 -0
  200. package/src/build-api.ts +118 -22
  201. package/src/chart-axes.test.ts +573 -0
  202. package/src/chart-axes.ts +342 -0
  203. package/src/chart-cartesian.ts +1863 -0
  204. package/src/chart-decimate.test.ts +249 -0
  205. package/src/chart-decimate.ts +186 -0
  206. package/src/chart-export-pdf.test.ts +91 -0
  207. package/src/chart-export-pdf.ts +207 -0
  208. package/src/chart-export.test.ts +14 -0
  209. package/src/chart-export.ts +13 -3
  210. package/src/chart-financial.ts +208 -0
  211. package/src/chart-flow.ts +286 -0
  212. package/src/chart-format.ts +67 -0
  213. package/src/chart-grid.ts +179 -0
  214. package/src/chart-hierarchy.ts +135 -0
  215. package/src/chart-indicators.test.ts +443 -0
  216. package/src/chart-indicators.ts +384 -0
  217. package/src/chart-messages.test.ts +21 -0
  218. package/src/chart-messages.ts +184 -0
  219. package/src/chart-motion.test.ts +71 -0
  220. package/src/chart-motion.ts +148 -0
  221. package/src/chart-panel-messages.test.ts +29 -0
  222. package/src/chart-panel-messages.ts +550 -0
  223. package/src/chart-pivot.test.ts +54 -0
  224. package/src/chart-pivot.ts +225 -0
  225. package/src/chart-polar.ts +674 -0
  226. package/src/chart-samples.ts +106 -0
  227. package/src/chart-scale.ts +394 -0
  228. package/src/chart-series-types.test.ts +714 -0
  229. package/src/chart-stats.ts +409 -0
  230. package/src/chart-stream.test.ts +75 -0
  231. package/src/chart-stream.ts +114 -0
  232. package/src/chart-summary.test.ts +90 -0
  233. package/src/chart-summary.ts +197 -0
  234. package/src/chart-sync.svelte.ts +41 -0
  235. package/src/chart-table.test.ts +76 -0
  236. package/src/chart-table.ts +178 -0
  237. package/src/chart-trend.ts +47 -0
  238. package/src/chart-types.ts +1309 -0
  239. package/src/chart-validate.test.ts +100 -0
  240. package/src/chart-validate.ts +247 -0
  241. package/src/chart-zoom.test.ts +137 -0
  242. package/src/chart-zoom.ts +124 -0
  243. package/src/chart.coverage.test.ts +81 -0
  244. package/src/chart.test.ts +56 -3
  245. package/src/chart.ts +557 -2784
  246. package/src/clipboard.test.ts +151 -0
  247. package/src/clipboard.ts +145 -16
  248. package/src/column-resize.ts +7 -1
  249. package/src/columns.ts +2 -0
  250. package/src/command-context.test.ts +200 -0
  251. package/src/command-context.ts +137 -0
  252. package/src/core.rowmodel-cache.test.ts +51 -0
  253. package/src/core.ts +40 -12
  254. package/src/editing.test.ts +281 -3
  255. package/src/editing.ts +264 -38
  256. package/src/fill-patterns.test.ts +35 -0
  257. package/src/fill-patterns.ts +56 -2
  258. package/src/grid-icons.ts +2 -2
  259. package/src/grid-messages.ts +21 -3
  260. package/src/history.test.ts +196 -0
  261. package/src/history.ts +162 -0
  262. package/src/icon-seam.test.ts +5 -6
  263. package/src/index.ts +169 -12
  264. package/src/keyboard-handlers.coverage.test.ts +126 -6
  265. package/src/keyboard-handlers.ts +91 -30
  266. package/src/keyboard-shortcuts.seam.test.ts +200 -0
  267. package/src/keyboard.test.ts +124 -1
  268. package/src/keyboard.ts +142 -0
  269. package/src/menus.test.ts +26 -0
  270. package/src/menus.ts +10 -3
  271. package/src/merges.test.ts +112 -0
  272. package/src/merges.ts +168 -0
  273. package/src/pivot.grid.test.ts +40 -5
  274. package/src/row-model.ts +146 -0
  275. package/src/row-resize.test.ts +44 -0
  276. package/src/row-resize.ts +17 -0
  277. package/src/scroll-sync.test.ts +42 -1
  278. package/src/scroll-sync.ts +25 -0
  279. package/src/selection.multi-range.test.ts +34 -0
  280. package/src/selection.test.ts +33 -2
  281. package/src/selection.ts +143 -21
  282. package/src/server-block-cache.test.ts +645 -0
  283. package/src/server-block-cache.ts +677 -0
  284. package/src/server-data-source.infinite.test.ts +343 -0
  285. package/src/server-data-source.ts +450 -27
  286. package/src/server.ts +47 -0
  287. package/src/shortcut-registry.test.ts +97 -0
  288. package/src/shortcut-registry.ts +181 -0
  289. package/src/svgrid-wrapper.types.ts +184 -13
  290. package/src/svgrid.behavior.test.ts +135 -0
  291. package/src/svgrid.charting.test.ts +513 -3
  292. package/src/svgrid.context-menu.test.ts +31 -0
  293. package/src/svgrid.new-features.wrapper.test.ts +10 -3
  294. package/src/svgrid.row-model-prop.svelte.test.ts +352 -0
  295. package/src/svgrid.row-model-seam.svelte.test.ts +289 -0
  296. package/src/validate.test.ts +10 -0
  297. package/src/validate.ts +10 -3
  298. package/src/virtualization/virtualizer.ts +39 -17
  299. package/dist/SvGroupCell.svelte +0 -141
  300. package/dist/SvGroupCell.svelte.d.ts +0 -49
  301. package/dist/SvRowGroupPanel.svelte +0 -186
  302. package/dist/SvRowGroupPanel.svelte.d.ts +0 -25
  303. package/dist/cdn/GridMenus-CHOMnW-c.js +0 -602
  304. package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +0 -816
  305. package/dist/cdn/SvGridChart-1PMSp2LZ.js +0 -1481
  306. package/dist/cdn/SvGridChart-D02avhyr.js +0 -1480
  307. package/dist/cdn/SvGridChartPanel-DJnViOv4.js +0 -834
  308. package/dist/cdn/SvGridChartPanel-vPTNerFZ.js +0 -810
  309. package/dist/cdn/SvGridChartView-C--HhdUo.js +0 -56
  310. package/dist/cdn/SvGridChartView-KyyFG-wY.js +0 -55
  311. package/dist/cdn/chart-DLg3_zTQ.js +0 -1652
  312. package/dist/cdn/validate-_CDJzgIo.js +0 -75
  313. package/dist/server-group-model.d.ts +0 -98
  314. package/dist/server-group-model.js +0 -263
  315. package/src/SvGroupCell.svelte +0 -141
  316. package/src/SvRowGroupPanel.svelte +0 -186
  317. package/src/server-group-model.test.ts +0 -294
  318. package/src/server-group-model.ts +0 -370
@@ -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
@@ -387,8 +387,9 @@
387
387
  );
388
388
 
389
389
  const rowSizePx = (i: number): number => {
390
- // A height the user dragged wins over the declared one, so the row stays
391
- // where they put it across re-renders and virtualization recycling.
390
+ // A height the user dragged, or set through the api, wins over the
391
+ // declared one, so the row stays where they put it across re-renders
392
+ // and virtualization recycling.
392
393
  const dragged = ctrl.rowResizeHeightPx(i);
393
394
  if (dragged != null) return dragged;
394
395
  const rh = opt.rowHeight;
@@ -402,10 +403,52 @@
402
403
  autoRowHeightOn
403
404
  ? `min-height: ${measuredRowHeightPx(i) ?? rowSizePx(i)}px;`
404
405
  : `height: ${rowSizePx(i)}px;`;
406
+ const collapsedColumns = $derived(ctrl.collapsedColumns);
405
407
  const columnVirtualizationEnabled = $derived(
406
408
  ctrl.columnVirtualizationEnabled,
407
409
  );
408
410
  const virtualRows = $derived(ctrl.virtualRows);
411
+ // The first body row on screen: where a failed run that started above
412
+ // the window repeats its message.
413
+ const firstRenderedRowIndex = $derived(
414
+ rowVirtualizationEnabled ? (virtualRows[0]?.index ?? 0) : 0,
415
+ );
416
+ const frozenRowCount = $derived(ctrl.frozenRowCount);
417
+ const frozenRowList = $derived(ctrl.frozenRowList);
418
+ // Merged cells. A merge is drawn once per band (the frozen rows, the
419
+ // scrolling body), by the top-left cell of the part the band shows, with
420
+ // its spans clamped to the rendered window: a td never spans into rows
421
+ // the table has not drawn.
422
+ const mergeIndex = $derived(ctrl.mergeIndex);
423
+ const mergeColumnWindow = $derived.by(() => ({
424
+ firstCol: renderedColumns[0]?.item.index ?? 0,
425
+ lastCol: renderedColumns[renderedColumns.length - 1]?.item.index ?? -1,
426
+ }));
427
+ const mergeBodyWindow = $derived.by(() => {
428
+ const cols = mergeColumnWindow;
429
+ if (!rowVirtualizationEnabled) return { firstRow: frozenRowCount, lastRow: allRows.length - 1, ...cols };
430
+ let firstRow = Number.POSITIVE_INFINITY;
431
+ let lastRow = -1;
432
+ for (const item of virtualRows) {
433
+ if (item.index < frozenRowCount) continue;
434
+ if (item.index < firstRow) firstRow = item.index;
435
+ if (item.index > lastRow) lastRow = item.index;
436
+ }
437
+ return { firstRow: Number.isFinite(firstRow) ? firstRow : frozenRowCount, lastRow, ...cols };
438
+ });
439
+ const mergeFrozenWindow = $derived.by(() => ({ firstRow: 0, lastRow: frozenRowCount - 1, ...mergeColumnWindow }));
440
+ /** The width a td spanning `colSpan` rendered columns from `colIndex` takes. */
441
+ function mergedWidth(colIndex: number, colSpan: number, own: number): number {
442
+ if (colSpan <= 1) return own;
443
+ let total = 0;
444
+ for (const rendered of renderedColumns) {
445
+ const i = rendered.item.index;
446
+ if (i >= colIndex && i < colIndex + colSpan) total += rendered.item.size;
447
+ }
448
+ return total || own;
449
+ }
450
+ const frozenBandHeight = $derived(ctrl.frozenBandHeight);
451
+ const frozenRowTop = $derived(ctrl.frozenRowTop);
409
452
  const autoRowHeightOn = $derived(ctrl.autoRowHeightOn);
410
453
  const measureRowHeight = $derived(ctrl.measureRowHeight);
411
454
  const measuredRowHeightPx = $derived(ctrl.measuredRowHeightPx);
@@ -522,9 +565,8 @@
522
565
  const toggleSelectAllRows = $derived(ctrl.toggleSelectAllRows);
523
566
  const setActiveCell = $derived(ctrl.setActiveCell);
524
567
  const scrollActiveCellIntoView = $derived(ctrl.scrollActiveCellIntoView);
568
+ const cellSelectionState = $derived(ctrl.cellSelectionState);
525
569
  const getColumnWidth = $derived(ctrl.getColumnWidth);
526
- const getCellRangeEdges = $derived(ctrl.getCellRangeEdges);
527
- const fillHandleCell = $derived(ctrl.fillHandleCell);
528
570
  const isInFillPreview = $derived(ctrl.isInFillPreview);
529
571
  const fillMarqueeEdges = $derived(ctrl.fillMarqueeEdges);
530
572
  const startFillDrag = $derived(ctrl.startFillDrag);
@@ -755,6 +797,19 @@
755
797
  rotate-on-open both hang off it (SvGrid.css), and an override that
756
798
  dropped the class would lose both silently - which is the kind of bug
757
799
  that only shows up in someone else's app. -->
800
+ {#snippet chartToggle()}
801
+ <button
802
+ type="button"
803
+ class="sv-grid-toolbar-btn sv-grid-chart-toggle"
804
+ class:is-active={ctrl.chartPanelOpen}
805
+ aria-label={ctrl.chartPanelOpen ? "Close chart" : "Open chart"}
806
+ aria-expanded={ctrl.chartPanelOpen}
807
+ onclick={() => (ctrl.chartPanelOpen = !ctrl.chartPanelOpen)}
808
+ >
809
+ {@render icon("chart")}
810
+ Chart
811
+ </button>
812
+ {/snippet}
758
813
  {#snippet icon(name: GridIconName)}
759
814
  {@const override = opt.icons?.[name]}
760
815
  {#if override}
@@ -1046,6 +1101,9 @@
1046
1101
  </div>
1047
1102
  </div>
1048
1103
  {:else if pivotViewOn}
1104
+ <!-- The Chart panel docks here the way it docks beside the flat grid,
1105
+ and charts the pivot on screen: the root reserves the panel's edge and
1106
+ the pivot bar carries the toggle the toolbar carries in flat mode. -->
1049
1107
  <div
1050
1108
  class="sv-grid-root sv-grid-pivot-root"
1051
1109
  class:sv-grid-root-fill={opt.containerHeight === "100%"}
@@ -1053,7 +1111,7 @@
1053
1111
  typeof opt.containerHeight === "string"
1054
1112
  ? opt.containerHeight
1055
1113
  : `${opt.containerHeight ?? 520}px`
1056
- }; display: flex; flex-direction: column;`}
1114
+ }; display: flex; flex-direction: column; ${chartDockReserveStyle}`}
1057
1115
  >
1058
1116
  <div class="sv-grid-pivot-bar">
1059
1117
  <span class="sv-grid-pivot-badge">{messages.group}</span>
@@ -1063,6 +1121,9 @@
1063
1121
  aria-pressed="true"
1064
1122
  onclick={() => ctrl.togglePivotMode()}
1065
1123
  >{messages.pivotUpsellTitle}</button>
1124
+ {#if ctrl.chartingEnabled}
1125
+ {@render chartToggle()}
1126
+ {/if}
1066
1127
  </div>
1067
1128
  <div style="flex: 1 1 auto; min-height: 0;">
1068
1129
  {#if pivotResult}
@@ -1081,6 +1142,9 @@
1081
1142
  </div>
1082
1143
  {/if}
1083
1144
  </div>
1145
+ {#if ctrl.chartingEnabled && ctrl.chartPanelOpen && ChartPanelView}
1146
+ <ChartPanelView {ctrl} />
1147
+ {/if}
1084
1148
  </div>
1085
1149
  {:else}
1086
1150
 
@@ -1402,10 +1466,386 @@
1402
1466
  </tr>
1403
1467
  {/snippet}
1404
1468
 
1469
+ <!-- A row whose data has not arrived.
1470
+
1471
+ "loading" keeps the real columns and draws a shimmer bar in each,
1472
+ so the table keeps its shape while blocks stream in - a full-width
1473
+ bar would make the whole grid jump every time one landed. "failed"
1474
+ is the opposite case: there is nothing per-column to say, just one
1475
+ message and a way to try again, so it spans the row like a detail
1476
+ cell does. Neither is selectable or editable; there is nothing
1477
+ there to act on.
1478
+
1479
+ A failed block is a run of failed rows, one per row it claimed,
1480
+ and one message serves the run: the first failed row on screen
1481
+ carries it (the run's first row, or the top of the window when
1482
+ the run started above it) and the rows under it are a plain
1483
+ tinted band. A hundred "Could not load" lines for one block
1484
+ was a wall of red. -->
1485
+ {#snippet placeholderRowMarkup(
1486
+ phRow: Row<TData>,
1487
+ phRowIndex: number,
1488
+ phState: "loading" | "failed",
1489
+ phStyle: string,
1490
+ phContinues: boolean,
1491
+ )}
1492
+ <tr
1493
+ class="sv-grid-row sv-grid-placeholder-row"
1494
+ class:sv-grid-placeholder-failed={phState === "failed"}
1495
+ class:sv-grid-placeholder-failed-cont={phContinues}
1496
+ aria-busy={phState === "loading" ? "true" : undefined}
1497
+ {...getGridRowA11yProps(phRowIndex + 1)}
1498
+ style={phStyle}
1499
+ >
1500
+ {#if phState === "failed"}
1501
+ <td
1502
+ class="sv-grid-cell sv-grid-placeholder-cell"
1503
+ colspan={allColumns.length +
1504
+ (showRowNumbersEffective ? 1 : 0) +
1505
+ (showRowSelectionEffective ? 1 : 0)}
1506
+ >
1507
+ {#if !phContinues}
1508
+ <div class="sv-grid-placeholder-failed-body">
1509
+ <span class="sv-grid-placeholder-error">{messages.rowLoadFailed}</span>
1510
+ {#if opt.onRetryRow}
1511
+ <button
1512
+ type="button"
1513
+ class="sv-grid-placeholder-retry"
1514
+ onclick={() => ctrl.retryRow(phRow)}>{messages.rowLoadRetry}</button
1515
+ >
1516
+ {/if}
1517
+ </div>
1518
+ {/if}
1519
+ </td>
1520
+ {:else}
1521
+ {#if showRowNumbersEffective}
1522
+ <td class="sv-grid-cell sv-grid-rownum-cell"></td>
1523
+ {/if}
1524
+ {#if showRowSelectionEffective}
1525
+ <td class="sv-grid-cell sv-grid-select-cell"></td>
1526
+ {/if}
1527
+ {#each allColumns as col (col.id)}
1528
+ <td class="sv-grid-cell sv-grid-placeholder-cell">
1529
+ <span class="sv-grid-placeholder-skeleton" aria-hidden="true"></span>
1530
+ </td>
1531
+ {/each}
1532
+ {/if}
1533
+ </tr>
1534
+ {/snippet}
1535
+
1405
1536
  <!-- A single pinned row (top or bottom). Read-only by design: no
1406
1537
  inline editing, no row-selection checkbox, no fill handle.
1407
1538
  Position-sticky CSS keeps it anchored to the top of the body or
1408
1539
  the bottom of the viewport while the rest scrolls. -->
1540
+ <!--
1541
+ One body row, whatever path renders it: the virtualized window, the
1542
+ plain full-list body, or the frozen rows pinned above the window. The
1543
+ two loops used to carry a copy each and had drifted apart (cell
1544
+ tooltips on one, the fill handle on the other), so a feature landed in
1545
+ one mode and not the other. `rowStyle` is the height the caller knows:
1546
+ the virtualizer's measured size, or the declared one.
1547
+ -->
1548
+ {#snippet bodyRow(row: Row<TData>, rowIndex: number, rowStyle: string)}
1549
+ {@const phState = ctrl.placeholderStateOf(row)}
1550
+ {#if phState}
1551
+ {@const phContinues =
1552
+ phState === "failed" &&
1553
+ rowIndex > firstRenderedRowIndex &&
1554
+ ctrl.placeholderStateOf(allRows[rowIndex - 1]!) === "failed"}
1555
+ {@render placeholderRowMarkup(row, rowIndex, phState, rowStyle, phContinues)}
1556
+ {:else if opt.isDetailRow?.(row.original as TData, rowIndex)}
1557
+ {@render detailRowMarkup(row, rowIndex)}
1558
+ {:else if isGroupRow(row) && !groupColumnMode}
1559
+ <tr
1560
+ class="sv-grid-row sv-grid-group-row"
1561
+ class:sv-grid-row-selected={isRowSelected(row.id)}
1562
+ class:sv-grid-row-frozen={rowIndex < frozenRowCount}
1563
+ class:sv-grid-row-frozen-last={frozenRowCount > 0 && rowIndex === frozenRowCount - 1}
1564
+ style:top={rowIndex < frozenRowCount ? `${frozenRowTop(rowIndex)}px` : undefined}
1565
+ aria-level={row.depth + 1}
1566
+ aria-expanded={row.getIsExpanded?.() ? "true" : "false"}
1567
+ {...getGridRowA11yProps(rowIndex + 1)}
1568
+ class:sv-grid-row-collapsed={ctrl.isRowCollapsed(rowIndex)}
1569
+ style={rowStyle}
1570
+ >
1571
+ <td
1572
+ class="sv-grid-cell sv-grid-group-cell"
1573
+ class:sv-grid-cell-active={activeCell.rowIndex ===
1574
+ rowIndex}
1575
+ colspan={allColumns.length +
1576
+ (showRowNumbersEffective ? 1 : 0) +
1577
+ (showRowSelectionEffective ? 1 : 0)}
1578
+ onclick={() => row.toggleExpanded?.()}
1579
+ >
1580
+ {@render groupRowContent(row)}
1581
+ </td>
1582
+ </tr>
1583
+ {:else}
1584
+ {@const userRowClass = computeRowClass(row, rowIndex)}
1585
+ <tr
1586
+ class={`sv-grid-row ${userRowClass} ${rowDropClass(rowIndex)}`}
1587
+ class:sv-grid-row-selected={isRowSelected(row.id)}
1588
+ class:sv-grid-row-alt={opt.zebraRows &&
1589
+ rowIndex % 2 === 1}
1590
+ class:sv-grid-row-draggable={rowDragManagedEffective}
1591
+ class:sv-grid-group-footer-row={isGroupFooterRow(row)}
1592
+ class:sv-grid-grand-total-row={isGrandTotalRow(row)}
1593
+ class:sv-grid-row-auto-height={autoRowHeightOn}
1594
+ class:sv-grid-row-frozen={rowIndex < frozenRowCount}
1595
+ class:sv-grid-row-frozen-last={frozenRowCount > 0 && rowIndex === frozenRowCount - 1}
1596
+ style:top={rowIndex < frozenRowCount ? `${frozenRowTop(rowIndex)}px` : undefined}
1597
+ {...getGridRowA11yProps(rowIndex + 1)}
1598
+ aria-level={sgAriaLevel(row)}
1599
+ aria-expanded={sgAriaExpanded(row)}
1600
+ {...rowDragAttrs(rowIndex)}
1601
+ class:sv-grid-row-collapsed={ctrl.isRowCollapsed(rowIndex)}
1602
+ style={rowStyle}
1603
+ use:measureRowHeight={rowIndex}
1604
+ >
1605
+ {#if showRowNumbersEffective}
1606
+ <td
1607
+ class="sv-grid-cell sv-grid-row-number-cell"
1608
+ style={`width: ${rowNumberColumnWidth}px; min-width: ${rowNumberColumnWidth}px; max-width: ${rowNumberColumnWidth}px; left: 0;`}
1609
+ >{rowIndex + 1}</td
1610
+ >
1611
+ {/if}
1612
+ {#if showRowSelectionEffective}
1613
+ <td
1614
+ class="sv-grid-cell sv-grid-selection-cell"
1615
+ style={`width: ${selectionColumnWidth}px; min-width: ${selectionColumnWidth}px; max-width: ${selectionColumnWidth}px; left: ${showRowNumbersEffective ? rowNumberColumnWidth : 0}px;`}
1616
+ onclick={() => toggleRowSelectionById(row.id)}
1617
+ >
1618
+ <button
1619
+ type="button"
1620
+ class="sv-grid-checkbox"
1621
+ role="checkbox"
1622
+ aria-checked={isRowSelected(row.id)}
1623
+ aria-label="Select row"
1624
+ onclick={(event) => {
1625
+ event.stopPropagation();
1626
+ toggleRowSelectionById(row.id);
1627
+ }}
1628
+ onkeydown={(event) =>
1629
+ toggleCheckboxWithKeyboard(event, () => {
1630
+ event.stopPropagation();
1631
+ toggleRowSelectionById(row.id);
1632
+ })}
1633
+ ></button>
1634
+ </td>
1635
+ {/if}
1636
+ {#if columnVirtualizationEnabled && columnWindowStart > 0}
1637
+ <td
1638
+ class="sv-grid-cell sv-grid-cell-spacer"
1639
+ aria-hidden="true"
1640
+ style={`width: ${columnWindowStart}px; min-width: ${columnWindowStart}px; max-width: ${columnWindowStart}px;`}
1641
+ ></td>
1642
+ {/if}
1643
+ {#each renderedColumns as rendered (rendered.column.id)}
1644
+ {@const colIndex = rendered.item.index}
1645
+ {@const draw = mergeIndex
1646
+ ? mergeDrawAt(mergeIndex, rowIndex, colIndex, rowIndex < frozenRowCount ? mergeFrozenWindow : mergeBodyWindow)
1647
+ : null}
1648
+ {#if draw?.kind !== "skip"}
1649
+ {@const cellRow = draw?.kind === "continuation" ? (allRows[draw.merge.rowIndex] ?? row) : row}
1650
+ {@const cellColumn = draw?.kind === "continuation" ? (allColumns[draw.merge.colIndex] ?? rendered.column) : rendered.column}
1651
+ {@const baseValue = getColumnBaseValue(
1652
+ cellRow,
1653
+ cellColumn,
1654
+ )}
1655
+ {@const cellValue = getCellDisplayValue(
1656
+ cellRow.id,
1657
+ cellColumn.id,
1658
+ baseValue,
1659
+ )}
1660
+ {@const isEditing =
1661
+ ctrl.editingCell?.rowId === cellRow.id &&
1662
+ ctrl.editingCell?.columnId === cellColumn.id}
1663
+ {@const inRowEdit =
1664
+ !!fullRowEdit &&
1665
+ fullRowEdit.rowId === cellRow.id &&
1666
+ cellColumn.id in fullRowEdit.draft}
1667
+ {@const cellSel = cellSelectionState(rowIndex, colIndex)}
1668
+ {@const fillEdges = fillMarqueeEdges(rowIndex, colIndex)}
1669
+ {@const userCellClass = computeCellClass(
1670
+ cellRow,
1671
+ cellColumn,
1672
+ )}
1673
+ {@const cellTooltip = computeCellTooltip(
1674
+ cellRow,
1675
+ cellColumn,
1676
+ )}
1677
+ {@const cellValidity = computeCellValidity(
1678
+ cellRow,
1679
+ cellColumn,
1680
+ )}
1681
+ {@const cellNote = computeCellNote(
1682
+ cellRow,
1683
+ cellColumn,
1684
+ )}
1685
+ {@const tdWidth = draw ? mergedWidth(colIndex, draw.colSpan, rendered.item.size) : rendered.item.size}
1686
+ <td
1687
+ rowspan={draw && draw.rowSpan > 1 ? draw.rowSpan : undefined}
1688
+ colspan={draw && draw.colSpan > 1 ? draw.colSpan : undefined}
1689
+ data-merge-origin={draw ? `${draw.merge.rowIndex}:${draw.merge.colIndex}` : undefined}
1690
+ class:sv-grid-cell-merged={!!draw}
1691
+ class={`sv-grid-cell ${userCellClass}`}
1692
+ class:sv-grid-cell-editing={isEditing || inRowEdit}
1693
+ class:sv-grid-cell-collapsed={!!collapsedColumns[rendered.column.id]}
1694
+ class:sv-grid-cell-active={!!cellSel?.active}
1695
+ class:sv-grid-cell-has-fill-handle={!!cellSel?.fillHandle}
1696
+ class:sv-grid-cell-cf={hasConditionalFormats}
1697
+ class:sv-grid-cell-invalid={cellValidity.invalid}
1698
+ class:sv-grid-cell-has-note={cellNote != null}
1699
+ aria-invalid={cellValidity.invalid ? "true" : undefined}
1700
+ data-svgrid-row={rowIndex}
1701
+ data-svgrid-col={colIndex}
1702
+ data-col-id={rendered.column.id}
1703
+ data-align={getColumnAlign(rendered.column)}
1704
+ data-pinned={isColumnPinned(rendered.column.id) ??
1705
+ undefined}
1706
+ data-selected-range={cellSel?.edges ? "true" : undefined}
1707
+ data-range-top={cellSel?.edges?.top ? "true" : undefined}
1708
+ data-range-bottom={cellSel?.edges?.bottom
1709
+ ? "true"
1710
+ : undefined}
1711
+ data-range-left={cellSel?.edges?.left
1712
+ ? "true"
1713
+ : undefined}
1714
+ data-range-right={cellSel?.edges?.right
1715
+ ? "true"
1716
+ : undefined}
1717
+ data-fill-preview={isInFillPreview(rowIndex, colIndex)
1718
+ ? "true"
1719
+ : undefined}
1720
+ data-fill-top={fillEdges?.top ? "true" : undefined}
1721
+ data-fill-bottom={fillEdges?.bottom ? "true" : undefined}
1722
+ data-fill-left={fillEdges?.left ? "true" : undefined}
1723
+ data-fill-right={fillEdges?.right ? "true" : undefined}
1724
+ style={`width: ${tdWidth}px; min-width: ${tdWidth}px; max-width: ${tdWidth}px; ${cellPinStyle(rendered.column.id)}`}
1725
+ onpointerdown={(event) =>
1726
+ onCellPointerDown(rowIndex, colIndex, event)}
1727
+ onpointerenter={(event) => {
1728
+ onCellPointerEnter(rowIndex, colIndex);
1729
+ // The column tooltip fires on whole-cell hover; a
1730
+ // per-cell note waits for its corner below. A
1731
+ // validation message wins over the column's tooltip.
1732
+ const tip =
1733
+ cellValidity.invalid && cellValidity.message
1734
+ ? cellValidity.message
1735
+ : cellTooltip;
1736
+ if (tip)
1737
+ showTooltipFor(
1738
+ event.currentTarget as HTMLElement,
1739
+ tip,
1740
+ );
1741
+ }}
1742
+ onpointerleave={hideTooltip}
1743
+ ondblclick={() =>
1744
+ emitCellDoubleClick(rowIndex, colIndex)}
1745
+ onclick={() => onCellClick(rowIndex, colIndex)}
1746
+ oncontextmenu={(event) =>
1747
+ openContextMenu(
1748
+ event,
1749
+ rowIndex,
1750
+ colIndex,
1751
+ rendered.column.id,
1752
+ )}
1753
+ use:cellFlashAction={{
1754
+ rowId: cellRow.id,
1755
+ value: cellValue,
1756
+ active: !!cellColumn.columnDef.cellFlash,
1757
+ className: flashClassFor(
1758
+ cellColumn.columnDef.cellFlash,
1759
+ ),
1760
+ }}
1761
+ role="gridcell"
1762
+ id={getGridCellDomId(ctrl.gridDomId, rowIndex, colIndex)}
1763
+ aria-colindex={colIndex + 1}
1764
+ aria-rowindex={rowIndex + 1}
1765
+ aria-selected={isRowSelected(row.id)}
1766
+ >
1767
+ {#if inRowEdit || isEditing}
1768
+ <!-- The editing cell stays empty until the lazy
1769
+ editor chunk lands, exactly as it already did
1770
+ for the dropdown / date editors. Falling back
1771
+ to the read-only value here would flash stale
1772
+ content into a cell the user is editing. -->
1773
+ {#if CellEditor}
1774
+ <CellEditor {ctrl} column={cellColumn} row={cellRow} fullRow={inRowEdit} />
1775
+ {/if}
1776
+ {:else}
1777
+ {#if plainCellBody}
1778
+ {@render cellBody(cellRow, cellColumn, cellValue)}
1779
+ {:else}
1780
+ {@render cellBodyWithFormat(
1781
+ cellRow,
1782
+ cellColumn,
1783
+ cellValue,
1784
+ )}
1785
+ {/if}
1786
+ {/if}
1787
+ {#if !isEditing && cellSel?.fillHandle}
1788
+ <!-- Excel-style fill handle: drag down/right to
1789
+ extend the selection and pattern-fill the new
1790
+ cells on release. Rendered inside the bottom-
1791
+ right cell of the selection range (or active
1792
+ cell if there's no range). -->
1793
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
1794
+ <div
1795
+ class="sv-grid-fill-handle"
1796
+ role="button"
1797
+ aria-label="Fill handle"
1798
+ tabindex={-1}
1799
+ onpointerdown={(event) =>
1800
+ startFillDrag(event, rowIndex, colIndex)}
1801
+ ondblclick={(event) => {
1802
+ // Not the cell's double-click: this fills
1803
+ // down as far as the neighbour column goes.
1804
+ event.stopPropagation();
1805
+ event.preventDefault();
1806
+ ctrl.fillDownToNeighbour();
1807
+ }}
1808
+ ></div>
1809
+ {/if}
1810
+ {#if cellValidity.invalid && cellValidity.message}
1811
+ <!-- The validation message, for assistive tech: the
1812
+ tooltip above is mouse-only, and unlike a title
1813
+ a visually-hidden span adds no second native
1814
+ tooltip competing with the custom one. -->
1815
+ <span class="sv-grid-sr-only"
1816
+ >{cellValidity.message}</span
1817
+ >
1818
+ {/if}
1819
+ {#if cellNote != null && !isEditing}
1820
+ <span
1821
+ class="sv-grid-cell-note-corner"
1822
+ aria-label="Note"
1823
+ onpointerenter={(event) => {
1824
+ event.stopPropagation();
1825
+ showTooltipFor(
1826
+ event.currentTarget as HTMLElement,
1827
+ cellNote,
1828
+ );
1829
+ }}
1830
+ onpointerleave={(event) => {
1831
+ event.stopPropagation();
1832
+ hideTooltip();
1833
+ }}
1834
+ ></span>
1835
+ {/if}
1836
+ </td>
1837
+ {/if}
1838
+ {/each}
1839
+ {#if columnVirtualizationEnabled && columnWindowRightSpacer > 0}
1840
+ <td
1841
+ class="sv-grid-cell sv-grid-cell-spacer"
1842
+ aria-hidden="true"
1843
+ style={`width: ${columnWindowRightSpacer}px; min-width: ${columnWindowRightSpacer}px; max-width: ${columnWindowRightSpacer}px;`}
1844
+ ></td>
1845
+ {/if}
1846
+ </tr>
1847
+ {/if}
1848
+ {/snippet}
1409
1849
  {#snippet pinnedRowBody(
1410
1850
  rowData: TData,
1411
1851
  where: "top" | "bottom",
@@ -1466,7 +1906,7 @@
1466
1906
  style={chartDockReserveStyle}
1467
1907
  data-move-grab={ctrl.moveGrabHover || ctrl.moveDrag ? "true" : undefined}
1468
1908
  data-selbar={
1469
- ctrl.selectionBarOn && ctrl.selectionBarTarget.ids.length > 0
1909
+ ctrl.selectionBarVisible
1470
1910
  ? (getSelectionBarView() ? ctrl.selectionBarPosition : `upsell-${ctrl.selectionBarPosition}`)
1471
1911
  : undefined
1472
1912
  }
@@ -1475,7 +1915,8 @@
1475
1915
  // No gutter column on most grids, so fall back to the first body cell -
1476
1916
  // otherwise `rowResize` would be a prop that silently does nothing.
1477
1917
  anchor: "row",
1478
- onResize: (index, height) => ctrl.setRowResizeHeight(index, height),
1918
+ onResize: (index, height) => ctrl.resizeRowByUser(index, height),
1919
+ onAutosize: (index) => ctrl.resizeRowByUser(index, null),
1479
1920
  }}
1480
1921
  use:lazyColumnResize={{
1481
1922
  disabled: !columnResizeEnabled,
@@ -1483,12 +1924,13 @@
1483
1924
  onResize: (columnId, width) => {
1484
1925
  ctrl.columnWidths = { ...ctrl.columnWidths, [columnId]: width };
1485
1926
  },
1927
+ onResizeEnd: (columnId, width, startWidth) => ctrl.resizeColumnByUser(columnId, width, startWidth),
1486
1928
  label: (columnId) => {
1487
1929
  const col = ctrl.findColumnById(columnId);
1488
1930
  return col ? toolPanelHeaderLabel(col) : columnId;
1489
1931
  },
1490
1932
  canResize: (columnId) => ctrl.columnResizable(columnId),
1491
- onAutosize: (columnId) => ctrl.autosizeColumn(columnId),
1933
+ onAutosize: (columnId) => ctrl.autosizeColumnByUser(columnId),
1492
1934
  }}
1493
1935
  >
1494
1936
  {#if showGlobalFilterEffective}
@@ -1535,17 +1977,7 @@
1535
1977
  </button>
1536
1978
  {/if}
1537
1979
  {#if ctrl.chartingEnabled}
1538
- <button
1539
- type="button"
1540
- class="sv-grid-toolbar-btn sv-grid-chart-toggle"
1541
- class:is-active={ctrl.chartPanelOpen}
1542
- aria-label={ctrl.chartPanelOpen ? "Close chart" : "Open chart"}
1543
- aria-expanded={ctrl.chartPanelOpen}
1544
- onclick={() => (ctrl.chartPanelOpen = !ctrl.chartPanelOpen)}
1545
- >
1546
- {@render icon("chart")}
1547
- Chart
1548
- </button>
1980
+ {@render chartToggle()}
1549
1981
  {/if}
1550
1982
  </div>
1551
1983
  {/if}
@@ -1560,7 +1992,7 @@
1560
1992
  typeof opt.containerHeight === "string"
1561
1993
  ? opt.containerHeight
1562
1994
  : `${opt.containerHeight ?? 520}px`
1563
- }; --sg-thead-h: ${headerHeight}px; --sg-pinned-row-h: ${typeof opt.rowHeight === "number" ? opt.rowHeight : 30}px;`}
1995
+ }; --sg-thead-h: ${headerHeight}px; --sg-frozen-h: ${frozenBandHeight}px; --sg-pinned-row-h: ${typeof opt.rowHeight === "number" ? opt.rowHeight : 30}px;`}
1564
1996
  >
1565
1997
  <div
1566
1998
  class="sv-grid-container sv-grid-container-custom-scrollbars"
@@ -1723,6 +2155,7 @@
1723
2155
  )}
1724
2156
  <th
1725
2157
  class="sv-grid-column"
2158
+ class:sv-grid-column-collapsed={!!collapsedColumns[header.column.id]}
1726
2159
  class:is-drag-target-before={colDropOnId ===
1727
2160
  header.column.id && colDropSide === "before"}
1728
2161
  class:is-drag-target-after={colDropOnId ===
@@ -2180,6 +2613,9 @@
2180
2613
  </td>
2181
2614
  </tr>
2182
2615
  {:else if rowVirtualizationEnabled}
2616
+ {#each frozenRowList as row, rowIndex (row.id)}
2617
+ {@render bodyRow(row, rowIndex, rowStyleFor(rowIndex))}
2618
+ {/each}
2183
2619
  {#if rowTopSpacer > 0}
2184
2620
  <tr class="sv-grid-row sv-grid-row-spacer" aria-hidden="true">
2185
2621
  <td
@@ -2191,256 +2627,17 @@
2191
2627
  ></td>
2192
2628
  </tr>
2193
2629
  {/if}
2630
+ <!-- The virtualizer counts every row, frozen ones included, so its
2631
+ offsets and sizes stay in row-index space; the frozen rows are
2632
+ rendered in the band above and SKIPPED here. Shifting the
2633
+ index instead (item i drawn as row i + N) sized every row with
2634
+ the height of the row N above it, so a row dragged taller under
2635
+ a freeze showed its neighbour's height. -->
2194
2636
  {#each virtualRows as rowItem (rowItem.key)}
2195
2637
  {@const rowIndex = rowItem.index}
2196
2638
  {@const row = allRows[rowIndex]}
2197
- {#if row}
2198
- {#if opt.isDetailRow?.(row.original as TData, rowIndex)}
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}
2639
+ {#if row && rowIndex >= frozenRowCount}
2640
+ {@render bodyRow(row, rowIndex, rowHeightStyle(rowItem.size))}
2444
2641
  {/if}
2445
2642
  {/each}
2446
2643
  {#if rowBottomSpacer > 0}
@@ -2456,253 +2653,7 @@
2456
2653
  {/if}
2457
2654
  {:else}
2458
2655
  {#each allRows as row, rowIndex (row.id)}
2459
- {#if opt.isDetailRow?.(row.original as TData, rowIndex)}
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}
2656
+ {@render bodyRow(row, rowIndex, rowStyleFor(rowIndex))}
2706
2657
  {/each}
2707
2658
  {/if}
2708
2659
  </tbody>
@@ -3132,7 +3083,7 @@
3132
3083
  `absolute` against it, so moving it outside would anchor it to the
3133
3084
  page. The renderer is Enterprise and arrives through the
3134
3085
  selection-bar-view seam; without it, a short note in its place. -->
3135
- {#if ctrl.selectionBarOn && ctrl.selectionBarTarget.ids.length > 0}
3086
+ {#if ctrl.selectionBarVisible}
3136
3087
  {#if getSelectionBarView()}
3137
3088
  {@const SelectionBarView = getSelectionBarView()}
3138
3089
  <SelectionBarView {ctrl} />