@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
package/src/selection.ts CHANGED
@@ -14,22 +14,52 @@ import {
14
14
  getColumnBaseValue,
15
15
  isGroupRow,
16
16
  } from "./cell-values";
17
+ import { expandRectToMerges, originOf, endOf } from "./merges";
17
18
 
18
19
  export function createSelection<
19
20
  TFeatures extends TableFeatures = TableFeatures,
20
21
  TData extends RowData = RowData,
21
22
  >(ctx: any) {
22
23
  function isRowSelected(rowId: string) {
24
+ const model = ctx.props.rowSelectionModel;
25
+ if (model) {
26
+ const row = ctx.allRows.find((r: any) => r.id === rowId);
27
+ return row ? model.isSelected(rowId, row.original) : false;
28
+ }
23
29
  return Boolean(ctx.rowSelectionState[rowId]);
24
30
  }
25
31
 
26
32
  function toggleRowSelectionById(rowId: string) {
33
+ const model = ctx.props.rowSelectionModel;
34
+ if (model) {
35
+ const row = ctx.allRows.find((r: any) => r.id === rowId);
36
+ if (!row) return;
37
+ model.toggle(rowId, row.original, !model.isSelected(rowId, row.original));
38
+ return;
39
+ }
27
40
  ctx.grid.setRowSelection((prev: any) => ({ ...prev, [rowId]: !prev[rowId] }));
28
41
  }
29
42
 
30
43
  function toggleSelectAllRows() {
31
- const selectable = ctx.allRows.filter((row: any) => !isGroupRow(row));
32
- const select = ctx.headerSelectionState !== "all";
44
+ setSelectAllRows(ctx.headerSelectionState !== "all");
45
+ }
46
+
47
+ /**
48
+ * Select or clear every row, for the header checkbox and for
49
+ * `api.selectAllRows()`.
50
+ */
51
+ function setSelectAllRows(select: boolean) {
52
+ // An external model decides what "all" means - possibly a million rows
53
+ // the grid has never seen - so it gets the intent, not a row list.
54
+ if (ctx.props.rowSelectionModel) {
55
+ ctx.props.rowSelectionModel.toggleAll(select);
56
+ return;
57
+ }
58
+ // Placeholder rows are excluded: there is no row there to select yet,
59
+ // and ticking one would leave a selected id that never resolves.
60
+ const selectable = ctx.allRows.filter(
61
+ (row: any) => !isGroupRow(row) && !ctx.placeholderStateOf(row),
62
+ );
33
63
  ctx.grid.setRowSelection((prev: any) => {
34
64
  const next = { ...prev };
35
65
  for (const row of selectable) {
@@ -41,7 +71,18 @@ export function createSelection<
41
71
  }
42
72
 
43
73
  function setActiveCell(rowIndex: number, colIndex: number) {
74
+ // The active cell inside a merge is its origin: the one that draws, the
75
+ // one the formula bar shows, the one an edit writes.
76
+ ({ rowIndex, colIndex } = originOf(ctx.mergeIndex, rowIndex, colIndex));
44
77
  ctx.userHasActivatedCell = true;
78
+ // A click, an arrow, a jump: the Tab run is over. The Tab and Enter
79
+ // paths put the origin back after this call when it should live on.
80
+ // Activating the cell the cursor is already on (typing into it, F2, a
81
+ // double-click) is not a move and keeps the run.
82
+ const prev = ctx.grid.getState().activeCell;
83
+ if (!prev || prev.rowIndex !== rowIndex || prev.colIndex !== colIndex) {
84
+ ctx.tabRunOrigin = null;
85
+ }
45
86
  ctx.grid.setActiveCell({
46
87
  rowIndex,
47
88
  colIndex,
@@ -64,8 +105,16 @@ export function createSelection<
64
105
  if (!ctx.scrollContainer) return;
65
106
  if (rowIndex < 0 || rowIndex >= ctx.allRows.length) return;
66
107
  if (colIndex < 0 || colIndex >= ctx.allColumns.length) return;
67
-
68
- if (ctx.rowVirtualizationEnabled) {
108
+ const container = ctx.scrollContainer as HTMLElement;
109
+ const topBefore = container.scrollTop;
110
+ const leftBefore = container.scrollLeft;
111
+ // A frozen row is always in view; only the column may need to move.
112
+ const frozen: number = ctx.frozenRowCount ?? 0;
113
+ const frozenBand: number = ctx.frozenBandHeight ?? 0;
114
+
115
+ if (rowIndex < frozen) {
116
+ // Always in view; only the column below may need to move.
117
+ } else if (ctx.rowVirtualizationEnabled) {
69
118
  // Prefer the browser's native `scrollIntoView({ block: 'nearest' })`
70
119
  // when the target row is already mounted. It does Excel-style
71
120
  // minimum-scroll, respects the sticky thead via the
@@ -116,8 +165,10 @@ export function createSelection<
116
165
  const rowHeight = ctx.virtualizer.getSizeForIndex(rowIndex)
117
166
  const rowBottom = rowTopScroll + rowHeight
118
167
  let nextTop = currentTop
119
- if (rowTopScroll < currentTop + headerHeight) {
120
- nextTop = rowTopScroll - headerHeight
168
+ // The frozen band sits under the header, so a row is hidden until
169
+ // it clears both.
170
+ if (rowTopScroll < currentTop + headerHeight + frozenBand) {
171
+ nextTop = rowTopScroll - headerHeight - frozenBand
121
172
  } else if (rowBottom > currentTop + clientHeight) {
122
173
  nextTop = rowBottom - clientHeight
123
174
  }
@@ -170,33 +221,66 @@ export function createSelection<
170
221
  } else if (cellEnd > viewEnd) {
171
222
  ctx.scrollContainer.scrollLeft = cellEnd - ctx.scrollContainer.clientWidth;
172
223
  }
173
- // No inline scrollVersion bump - the `scroll` event triggers
174
- // onBodyScroll which flushes via rAF, doing one batched update.
224
+ // The scroll position is known right here, so hand it to the
225
+ // virtualizers now rather than waiting for the `scroll` event + rAF:
226
+ // that put the new row window one painted frame behind the active-cell
227
+ // move (see `syncScrollNow`). Reading the position back is cheap - the
228
+ // scrollIntoView / scrollTop writes above already laid the tree out.
229
+ const topAfter = container.scrollTop;
230
+ const leftAfter = container.scrollLeft;
231
+ if (topAfter !== topBefore || leftAfter !== leftBefore) {
232
+ ctx.syncScrollNow(topAfter, leftAfter);
233
+ }
175
234
  }
176
235
 
177
- // Normalize a range to a rectangle, or null when incomplete.
236
+ // Normalize a range to a rectangle, or null when incomplete. A merge
237
+ // the rectangle touches is in it whole, as a spreadsheet's selection
238
+ // has it: a merged cell is one cell.
178
239
  function rangeRect(range: SelectionRange | null) {
179
240
  const a = range?.anchor;
180
241
  const f = range?.focus;
181
242
  if (!a || !f) return null;
182
- return {
243
+ return expandRectToMerges(ctx.mergeIndex, {
183
244
  minRow: Math.min(a.rowIndex, f.rowIndex),
184
245
  maxRow: Math.max(a.rowIndex, f.rowIndex),
185
246
  minCol: Math.min(a.colIndex, f.colIndex),
186
247
  maxCol: Math.max(a.colIndex, f.colIndex),
187
- };
248
+ });
249
+ }
250
+
251
+ type SelectionRect = { minRow: number; maxRow: number; minCol: number; maxCol: number };
252
+
253
+ /** The active range as a rectangle, or null while nothing is anchored. */
254
+ function activeRangeRect(): SelectionRect | null {
255
+ return rangeRect(ctx.selectionRange as SelectionRange);
188
256
  }
257
+ // Memo for `getSelectionRects`, keyed on the identity of its two inputs.
258
+ // Both are replaced wholesale on every change and never mutated in place,
259
+ // so identity is a complete change check. Without it, every rendered
260
+ // cell's outline derived rebuilt this array on every arrow key - ~300
261
+ // arrays plus their rect objects per key press, for a result that is the
262
+ // same until the selection moves.
263
+ let rectsForRange: SelectionRange | null = null;
264
+ let rectsForRanges: SelectionRange[] | null = null;
265
+ let rectsMemo: SelectionRect[] = [];
189
266
 
190
267
  // Every selected rectangle: the committed extra ranges plus the active one.
191
268
  // Order matters for copy (added-order); the active range comes last.
192
- function getSelectionRects() {
193
- const rects = [] as Array<{ minRow: number; maxRow: number; minCol: number; maxCol: number }>;
194
- for (const r of (ctx.selectionRanges as SelectionRange[]) ?? []) {
269
+ // Callers must treat the result as read-only.
270
+ function getSelectionRects(): SelectionRect[] {
271
+ const range = ctx.selectionRange as SelectionRange;
272
+ const ranges = (ctx.selectionRanges as SelectionRange[]) ?? [];
273
+ if (range === rectsForRange && ranges === rectsForRanges) return rectsMemo;
274
+ const rects: SelectionRect[] = [];
275
+ for (const r of ranges) {
195
276
  const rect = rangeRect(r);
196
277
  if (rect) rects.push(rect);
197
278
  }
198
- const active = rangeRect(ctx.selectionRange);
279
+ const active = rangeRect(range);
199
280
  if (active) rects.push(active);
281
+ rectsForRange = range;
282
+ rectsForRanges = ranges;
283
+ rectsMemo = rects;
200
284
  return rects;
201
285
  }
202
286
 
@@ -259,6 +343,9 @@ export function createSelection<
259
343
  // that work to return null every time.
260
344
  if (!hasAnySelectionRect()) return null;
261
345
 
346
+ // A td inside a merge draws to the merge's far corner, so its bottom and
347
+ // right edges are the merge's, not the cell's.
348
+ const end = endOf(ctx.mergeIndex, rowIndex, colIndex);
262
349
  for (const rect of getSelectionRects()) {
263
350
  if (
264
351
  rowIndex < rect.minRow ||
@@ -270,14 +357,40 @@ export function createSelection<
270
357
  }
271
358
  return {
272
359
  top: rowIndex === rect.minRow,
273
- bottom: rowIndex === rect.maxRow,
360
+ bottom: end.rowIndex === rect.maxRow,
274
361
  left: colIndex === rect.minCol,
275
- right: colIndex === rect.maxCol,
362
+ right: end.colIndex === rect.maxCol,
276
363
  };
277
364
  }
278
365
  return null;
279
366
  }
280
367
 
368
+ /**
369
+ * Everything a cell's outline needs from the selection model, in ONE value:
370
+ * whether it is the active cell, which range edges it sits on (`edges` is
371
+ * null when it is in no range) and whether it carries the fill handle.
372
+ * `null` for a cell that is none of those - nearly every cell.
373
+ *
374
+ * One value on purpose. The view reads it through a single `{@const}`, so
375
+ * an active-cell move re-marks one derived per rendered cell instead of
376
+ * three (active flag, range edges, fill handle) plus the fill handle's
377
+ * `{#if}`. With ~300 cells rendered, that marking was the largest single
378
+ * cost of an arrow key that does not scroll. A plain cell returns the same
379
+ * `null` it returned before the move, so its attribute effect stays put.
380
+ */
381
+ function cellSelectionState(rowIndex: number, colIndex: number) {
382
+ const a = ctx.activeCell;
383
+ const active = !!a && a.rowIndex === rowIndex && a.colIndex === colIndex;
384
+ const edges = getCellRangeEdges(rowIndex, colIndex);
385
+ const fh = ctx.fillHandleCell;
386
+ // The handle sits on the range's bottom-right cell; when that is inside
387
+ // a merge, the td that draws the merge carries it.
388
+ const end = fh ? endOf(ctx.mergeIndex, rowIndex, colIndex) : null;
389
+ const fillHandle = !!fh && !!end && fh.rowIndex === end.rowIndex && fh.colIndex === end.colIndex;
390
+ if (!active && !edges && !fillHandle) return null;
391
+ return { active, edges, fillHandle };
392
+ }
393
+
281
394
  /**
282
395
  * The rectangle a drag is currently promising, or null when no drag is in
283
396
  * flight. A fill grows the source rectangle out to the pointer; a move
@@ -395,8 +508,12 @@ export function createSelection<
395
508
  ? { rowIndex: active.rowIndex, colIndex: active.colIndex }
396
509
  : null;
397
510
  if (event.shiftKey) {
511
+ // The range grows to the clicked cell and the active cell stays where
512
+ // it is, as in Excel: it is the cell the formula bar shows and the
513
+ // one the next keystroke edits. Used to jump to the clicked corner,
514
+ // so a Shift+click to select B2:D5 then typing wrote into D5.
398
515
  extendSelection(rowIndex, colIndex);
399
- setActiveCell(rowIndex, colIndex);
516
+ if (!active) setActiveCell(rowIndex, colIndex);
400
517
  } else {
401
518
  setActiveCell(rowIndex, colIndex);
402
519
  // Ctrl/Cmd starts an ADDITIONAL range, keeping prior ranges highlighted.
@@ -414,8 +531,9 @@ export function createSelection<
414
531
  if (!ctx.isDraggingSelection) return;
415
532
  const row = ctx.allRows[rowIndex];
416
533
  if (!row || isGroupRow(row)) return;
534
+ // The active cell is where the drag began; only the range's far corner
535
+ // follows the pointer.
417
536
  extendSelection(rowIndex, colIndex);
418
- setActiveCell(rowIndex, colIndex);
419
537
  }
420
538
 
421
539
  /** `event` is the window `pointerup` when this comes from the real gesture,
@@ -550,12 +668,13 @@ export function createSelection<
550
668
  }
551
669
 
552
670
  // onCellPointerDown already set active+selection for data cells. Only
553
- // decide whether to enter edit mode (click on the previously-active cell).
671
+ // decide whether to enter edit mode (click on the previously-active cell,
672
+ // unless the grid was told a second click is just a click).
554
673
  const wasActive =
555
674
  ctx.activeAtPointerDown !== null &&
556
675
  ctx.activeAtPointerDown.rowIndex === rowIndex &&
557
676
  ctx.activeAtPointerDown.colIndex === colIndex;
558
- if (wasActive && ctx.editingEnabled) {
677
+ if (wasActive && ctx.editingEnabled && ctx.props.editOnSecondClick !== false) {
559
678
  ctx.onCellDoubleClick(rowIndex, colIndex);
560
679
  return;
561
680
  }
@@ -597,12 +716,15 @@ export function createSelection<
597
716
  isRowSelected,
598
717
  toggleRowSelectionById,
599
718
  toggleSelectAllRows,
719
+ setSelectAllRows,
600
720
  setActiveCell,
601
721
  scrollActiveCellIntoView,
602
722
  setSelection,
603
723
  extendSelection,
604
724
  isCellInSelectedRange,
605
725
  getCellRangeEdges,
726
+ activeRangeRect,
727
+ cellSelectionState,
606
728
  fillMarqueeEdges,
607
729
  getSelectionRects,
608
730
  isInFillPreview,