@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
@@ -1,9 +1,10 @@
1
1
  import { type RowData, type TableFeatures } from "./index.js";
2
2
  import "./sv-grid-scrollbar.js";
3
3
  export declare function createSelection<TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData>(ctx: any): {
4
- isRowSelected: (rowId: string) => boolean;
4
+ isRowSelected: (rowId: string) => any;
5
5
  toggleRowSelectionById: (rowId: string) => void;
6
6
  toggleSelectAllRows: () => void;
7
+ setSelectAllRows: (select: boolean) => void;
7
8
  setActiveCell: (rowIndex: number, colIndex: number) => void;
8
9
  scrollActiveCellIntoView: (rowIndex: number, colIndex: number) => void;
9
10
  setSelection: (rowIndex: number, colIndex: number, additive?: boolean) => void;
@@ -15,6 +16,22 @@ export declare function createSelection<TFeatures extends TableFeatures = TableF
15
16
  left: boolean;
16
17
  right: boolean;
17
18
  } | null;
19
+ activeRangeRect: () => {
20
+ minRow: number;
21
+ maxRow: number;
22
+ minCol: number;
23
+ maxCol: number;
24
+ } | null;
25
+ cellSelectionState: (rowIndex: number, colIndex: number) => {
26
+ active: boolean;
27
+ edges: {
28
+ top: boolean;
29
+ bottom: boolean;
30
+ left: boolean;
31
+ right: boolean;
32
+ } | null;
33
+ fillHandle: boolean;
34
+ } | null;
18
35
  fillMarqueeEdges: (rowIndex: number, colIndex: number) => {
19
36
  top: boolean;
20
37
  bottom: boolean;
package/dist/selection.js CHANGED
@@ -4,16 +4,44 @@
4
4
  import { getGridCellDomId, } from "./index.js";
5
5
  import "./sv-grid-scrollbar.js";
6
6
  import { getColumnBaseValue, isGroupRow, } from "./cell-values.js";
7
+ import { expandRectToMerges, originOf, endOf } from "./merges.js";
7
8
  export function createSelection(ctx) {
8
9
  function isRowSelected(rowId) {
10
+ const model = ctx.props.rowSelectionModel;
11
+ if (model) {
12
+ const row = ctx.allRows.find((r) => r.id === rowId);
13
+ return row ? model.isSelected(rowId, row.original) : false;
14
+ }
9
15
  return Boolean(ctx.rowSelectionState[rowId]);
10
16
  }
11
17
  function toggleRowSelectionById(rowId) {
18
+ const model = ctx.props.rowSelectionModel;
19
+ if (model) {
20
+ const row = ctx.allRows.find((r) => r.id === rowId);
21
+ if (!row)
22
+ return;
23
+ model.toggle(rowId, row.original, !model.isSelected(rowId, row.original));
24
+ return;
25
+ }
12
26
  ctx.grid.setRowSelection((prev) => ({ ...prev, [rowId]: !prev[rowId] }));
13
27
  }
14
28
  function toggleSelectAllRows() {
15
- const selectable = ctx.allRows.filter((row) => !isGroupRow(row));
16
- const select = ctx.headerSelectionState !== "all";
29
+ setSelectAllRows(ctx.headerSelectionState !== "all");
30
+ }
31
+ /**
32
+ * Select or clear every row, for the header checkbox and for
33
+ * `api.selectAllRows()`.
34
+ */
35
+ function setSelectAllRows(select) {
36
+ // An external model decides what "all" means - possibly a million rows
37
+ // the grid has never seen - so it gets the intent, not a row list.
38
+ if (ctx.props.rowSelectionModel) {
39
+ ctx.props.rowSelectionModel.toggleAll(select);
40
+ return;
41
+ }
42
+ // Placeholder rows are excluded: there is no row there to select yet,
43
+ // and ticking one would leave a selected id that never resolves.
44
+ const selectable = ctx.allRows.filter((row) => !isGroupRow(row) && !ctx.placeholderStateOf(row));
17
45
  ctx.grid.setRowSelection((prev) => {
18
46
  const next = { ...prev };
19
47
  for (const row of selectable) {
@@ -26,7 +54,18 @@ export function createSelection(ctx) {
26
54
  });
27
55
  }
28
56
  function setActiveCell(rowIndex, colIndex) {
57
+ // The active cell inside a merge is its origin: the one that draws, the
58
+ // one the formula bar shows, the one an edit writes.
59
+ ({ rowIndex, colIndex } = originOf(ctx.mergeIndex, rowIndex, colIndex));
29
60
  ctx.userHasActivatedCell = true;
61
+ // A click, an arrow, a jump: the Tab run is over. The Tab and Enter
62
+ // paths put the origin back after this call when it should live on.
63
+ // Activating the cell the cursor is already on (typing into it, F2, a
64
+ // double-click) is not a move and keeps the run.
65
+ const prev = ctx.grid.getState().activeCell;
66
+ if (!prev || prev.rowIndex !== rowIndex || prev.colIndex !== colIndex) {
67
+ ctx.tabRunOrigin = null;
68
+ }
30
69
  ctx.grid.setActiveCell({
31
70
  rowIndex,
32
71
  colIndex,
@@ -51,7 +90,16 @@ export function createSelection(ctx) {
51
90
  return;
52
91
  if (colIndex < 0 || colIndex >= ctx.allColumns.length)
53
92
  return;
54
- if (ctx.rowVirtualizationEnabled) {
93
+ const container = ctx.scrollContainer;
94
+ const topBefore = container.scrollTop;
95
+ const leftBefore = container.scrollLeft;
96
+ // A frozen row is always in view; only the column may need to move.
97
+ const frozen = ctx.frozenRowCount ?? 0;
98
+ const frozenBand = ctx.frozenBandHeight ?? 0;
99
+ if (rowIndex < frozen) {
100
+ // Always in view; only the column below may need to move.
101
+ }
102
+ else if (ctx.rowVirtualizationEnabled) {
55
103
  // Prefer the browser's native `scrollIntoView({ block: 'nearest' })`
56
104
  // when the target row is already mounted. It does Excel-style
57
105
  // minimum-scroll, respects the sticky thead via the
@@ -99,8 +147,10 @@ export function createSelection(ctx) {
99
147
  const rowHeight = ctx.virtualizer.getSizeForIndex(rowIndex);
100
148
  const rowBottom = rowTopScroll + rowHeight;
101
149
  let nextTop = currentTop;
102
- if (rowTopScroll < currentTop + headerHeight) {
103
- nextTop = rowTopScroll - headerHeight;
150
+ // The frozen band sits under the header, so a row is hidden until
151
+ // it clears both.
152
+ if (rowTopScroll < currentTop + headerHeight + frozenBand) {
153
+ nextTop = rowTopScroll - headerHeight - frozenBand;
104
154
  }
105
155
  else if (rowBottom > currentTop + clientHeight) {
106
156
  nextTop = rowBottom - clientHeight;
@@ -154,34 +204,65 @@ export function createSelection(ctx) {
154
204
  else if (cellEnd > viewEnd) {
155
205
  ctx.scrollContainer.scrollLeft = cellEnd - ctx.scrollContainer.clientWidth;
156
206
  }
157
- // No inline scrollVersion bump - the `scroll` event triggers
158
- // onBodyScroll which flushes via rAF, doing one batched update.
207
+ // The scroll position is known right here, so hand it to the
208
+ // virtualizers now rather than waiting for the `scroll` event + rAF:
209
+ // that put the new row window one painted frame behind the active-cell
210
+ // move (see `syncScrollNow`). Reading the position back is cheap - the
211
+ // scrollIntoView / scrollTop writes above already laid the tree out.
212
+ const topAfter = container.scrollTop;
213
+ const leftAfter = container.scrollLeft;
214
+ if (topAfter !== topBefore || leftAfter !== leftBefore) {
215
+ ctx.syncScrollNow(topAfter, leftAfter);
216
+ }
159
217
  }
160
- // Normalize a range to a rectangle, or null when incomplete.
218
+ // Normalize a range to a rectangle, or null when incomplete. A merge
219
+ // the rectangle touches is in it whole, as a spreadsheet's selection
220
+ // has it: a merged cell is one cell.
161
221
  function rangeRect(range) {
162
222
  const a = range?.anchor;
163
223
  const f = range?.focus;
164
224
  if (!a || !f)
165
225
  return null;
166
- return {
226
+ return expandRectToMerges(ctx.mergeIndex, {
167
227
  minRow: Math.min(a.rowIndex, f.rowIndex),
168
228
  maxRow: Math.max(a.rowIndex, f.rowIndex),
169
229
  minCol: Math.min(a.colIndex, f.colIndex),
170
230
  maxCol: Math.max(a.colIndex, f.colIndex),
171
- };
231
+ });
172
232
  }
233
+ /** The active range as a rectangle, or null while nothing is anchored. */
234
+ function activeRangeRect() {
235
+ return rangeRect(ctx.selectionRange);
236
+ }
237
+ // Memo for `getSelectionRects`, keyed on the identity of its two inputs.
238
+ // Both are replaced wholesale on every change and never mutated in place,
239
+ // so identity is a complete change check. Without it, every rendered
240
+ // cell's outline derived rebuilt this array on every arrow key - ~300
241
+ // arrays plus their rect objects per key press, for a result that is the
242
+ // same until the selection moves.
243
+ let rectsForRange = null;
244
+ let rectsForRanges = null;
245
+ let rectsMemo = [];
173
246
  // Every selected rectangle: the committed extra ranges plus the active one.
174
247
  // Order matters for copy (added-order); the active range comes last.
248
+ // Callers must treat the result as read-only.
175
249
  function getSelectionRects() {
250
+ const range = ctx.selectionRange;
251
+ const ranges = ctx.selectionRanges ?? [];
252
+ if (range === rectsForRange && ranges === rectsForRanges)
253
+ return rectsMemo;
176
254
  const rects = [];
177
- for (const r of ctx.selectionRanges ?? []) {
255
+ for (const r of ranges) {
178
256
  const rect = rangeRect(r);
179
257
  if (rect)
180
258
  rects.push(rect);
181
259
  }
182
- const active = rangeRect(ctx.selectionRange);
260
+ const active = rangeRect(range);
183
261
  if (active)
184
262
  rects.push(active);
263
+ rectsForRange = range;
264
+ rectsForRanges = ranges;
265
+ rectsMemo = rects;
185
266
  return rects;
186
267
  }
187
268
  function setSelection(rowIndex, colIndex, additive = false) {
@@ -240,6 +321,9 @@ export function createSelection(ctx) {
240
321
  // that work to return null every time.
241
322
  if (!hasAnySelectionRect())
242
323
  return null;
324
+ // A td inside a merge draws to the merge's far corner, so its bottom and
325
+ // right edges are the merge's, not the cell's.
326
+ const end = endOf(ctx.mergeIndex, rowIndex, colIndex);
243
327
  for (const rect of getSelectionRects()) {
244
328
  if (rowIndex < rect.minRow ||
245
329
  rowIndex > rect.maxRow ||
@@ -249,13 +333,39 @@ export function createSelection(ctx) {
249
333
  }
250
334
  return {
251
335
  top: rowIndex === rect.minRow,
252
- bottom: rowIndex === rect.maxRow,
336
+ bottom: end.rowIndex === rect.maxRow,
253
337
  left: colIndex === rect.minCol,
254
- right: colIndex === rect.maxCol,
338
+ right: end.colIndex === rect.maxCol,
255
339
  };
256
340
  }
257
341
  return null;
258
342
  }
343
+ /**
344
+ * Everything a cell's outline needs from the selection model, in ONE value:
345
+ * whether it is the active cell, which range edges it sits on (`edges` is
346
+ * null when it is in no range) and whether it carries the fill handle.
347
+ * `null` for a cell that is none of those - nearly every cell.
348
+ *
349
+ * One value on purpose. The view reads it through a single `{@const}`, so
350
+ * an active-cell move re-marks one derived per rendered cell instead of
351
+ * three (active flag, range edges, fill handle) plus the fill handle's
352
+ * `{#if}`. With ~300 cells rendered, that marking was the largest single
353
+ * cost of an arrow key that does not scroll. A plain cell returns the same
354
+ * `null` it returned before the move, so its attribute effect stays put.
355
+ */
356
+ function cellSelectionState(rowIndex, colIndex) {
357
+ const a = ctx.activeCell;
358
+ const active = !!a && a.rowIndex === rowIndex && a.colIndex === colIndex;
359
+ const edges = getCellRangeEdges(rowIndex, colIndex);
360
+ const fh = ctx.fillHandleCell;
361
+ // The handle sits on the range's bottom-right cell; when that is inside
362
+ // a merge, the td that draws the merge carries it.
363
+ const end = fh ? endOf(ctx.mergeIndex, rowIndex, colIndex) : null;
364
+ const fillHandle = !!fh && !!end && fh.rowIndex === end.rowIndex && fh.colIndex === end.colIndex;
365
+ if (!active && !edges && !fillHandle)
366
+ return null;
367
+ return { active, edges, fillHandle };
368
+ }
259
369
  /**
260
370
  * The rectangle a drag is currently promising, or null when no drag is in
261
371
  * flight. A fill grows the source rectangle out to the pointer; a move
@@ -368,8 +478,13 @@ export function createSelection(ctx) {
368
478
  ? { rowIndex: active.rowIndex, colIndex: active.colIndex }
369
479
  : null;
370
480
  if (event.shiftKey) {
481
+ // The range grows to the clicked cell and the active cell stays where
482
+ // it is, as in Excel: it is the cell the formula bar shows and the
483
+ // one the next keystroke edits. Used to jump to the clicked corner,
484
+ // so a Shift+click to select B2:D5 then typing wrote into D5.
371
485
  extendSelection(rowIndex, colIndex);
372
- setActiveCell(rowIndex, colIndex);
486
+ if (!active)
487
+ setActiveCell(rowIndex, colIndex);
373
488
  }
374
489
  else {
375
490
  setActiveCell(rowIndex, colIndex);
@@ -390,8 +505,9 @@ export function createSelection(ctx) {
390
505
  const row = ctx.allRows[rowIndex];
391
506
  if (!row || isGroupRow(row))
392
507
  return;
508
+ // The active cell is where the drag began; only the range's far corner
509
+ // follows the pointer.
393
510
  extendSelection(rowIndex, colIndex);
394
- setActiveCell(rowIndex, colIndex);
395
511
  }
396
512
  /** `event` is the window `pointerup` when this comes from the real gesture,
397
513
  * and absent when `onWindowPointerMove` calls it to recover from a release
@@ -516,11 +632,12 @@ export function createSelection(ctx) {
516
632
  return;
517
633
  }
518
634
  // onCellPointerDown already set active+selection for data cells. Only
519
- // decide whether to enter edit mode (click on the previously-active cell).
635
+ // decide whether to enter edit mode (click on the previously-active cell,
636
+ // unless the grid was told a second click is just a click).
520
637
  const wasActive = ctx.activeAtPointerDown !== null &&
521
638
  ctx.activeAtPointerDown.rowIndex === rowIndex &&
522
639
  ctx.activeAtPointerDown.colIndex === colIndex;
523
- if (wasActive && ctx.editingEnabled) {
640
+ if (wasActive && ctx.editingEnabled && ctx.props.editOnSecondClick !== false) {
524
641
  ctx.onCellDoubleClick(rowIndex, colIndex);
525
642
  return;
526
643
  }
@@ -556,12 +673,15 @@ export function createSelection(ctx) {
556
673
  isRowSelected,
557
674
  toggleRowSelectionById,
558
675
  toggleSelectAllRows,
676
+ setSelectAllRows,
559
677
  setActiveCell,
560
678
  scrollActiveCellIntoView,
561
679
  setSelection,
562
680
  extendSelection,
563
681
  isCellInSelectedRange,
564
682
  getCellRangeEdges,
683
+ activeRangeRect,
684
+ cellSelectionState,
565
685
  fillMarqueeEdges,
566
686
  getSelectionRects,
567
687
  isInFillPreview,
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Block cache for server-backed rows - the engine behind infinite scrolling.
3
+ *
4
+ * Paging asks for "page 3". Infinite scrolling asks a different question: the
5
+ * user is looking at rows 4,000 to 4,020 of a table with a million rows, so
6
+ * fetch the block that covers them, keep a bounded number of recently-seen
7
+ * blocks, and render something sensible for every row nobody has fetched yet.
8
+ * That is all this module does, in plain TypeScript with no Svelte and no DOM.
9
+ *
10
+ * The shape of the problem (and most of the vocabulary) is the same one every
11
+ * grid with a server row model solves, so the options here are deliberately
12
+ * recognisable: `blockSize`, `maxBlocksInCache`, `maxConcurrentRequests`,
13
+ * `blockLoadDebounceMs`, `initialRowCount`, `overflowRows`.
14
+ *
15
+ * Three things it takes seriously:
16
+ *
17
+ * - **Unloaded rows still have to render.** `rows()` always returns a dense
18
+ * array, with a shared sentinel object in every slot that has no data. The
19
+ * grid recognises those (see {@link rowPlaceholderState}) and draws a
20
+ * skeleton or a retry affordance instead of an empty row, so the scrollbar
21
+ * never lies and scrolling never leaves a hole.
22
+ * - **The row count may be unknown.** A backend that cannot cheaply count
23
+ * says so (omit `rowCount`, or send `-1`) and the list grows by a block at
24
+ * a time until a short block proves where the end is.
25
+ * - **Requests are not free.** In-flight blocks are deduplicated, at most
26
+ * `maxConcurrentRequests` are open at once, a fast scroll debounces rather
27
+ * than firing a request per frame, and blocks abandoned by eviction or
28
+ * purge are aborted.
29
+ *
30
+ * Enterprise's server row model runs one of these per group level; the free
31
+ * flat controller runs exactly one.
32
+ */
33
+ /**
34
+ * The mark that says "this row is not data yet".
35
+ *
36
+ * A symbol, for two reasons. It cannot collide with a field in anyone's row
37
+ * data, which a string key like `__svPlaceholder` eventually would. And
38
+ * `Symbol.for` puts it in the global registry, so a placeholder still reads as
39
+ * one after it has crossed a module boundary - two copies of this file loaded
40
+ * through different specifiers would otherwise mint different symbols.
41
+ */
42
+ declare const PLACEHOLDER: unique symbol;
43
+ /** Rows the cache has not loaded. One shared object per state, not per row. */
44
+ declare const LOADING_ROW: Readonly<{
45
+ [PLACEHOLDER]: "loading";
46
+ }>;
47
+ declare const FAILED_ROW: Readonly<{
48
+ [PLACEHOLDER]: "failed";
49
+ }>;
50
+ /**
51
+ * Why a row is not real data, or `null` when it is.
52
+ *
53
+ * Deliberately NOT an identity check. The sentinels are shared objects, so
54
+ * `row === LOADING_ROW` looks like the obvious test - but rows routinely
55
+ * arrive here through something that wrapped them. Svelte is the everyday
56
+ * case: assigning the controller state into `$state` makes the row array
57
+ * deeply reactive, and every row read back out is a proxy of the original.
58
+ * Identity fails, every placeholder reads as real data, and the grid renders
59
+ * a screen of blank rows instead of skeletons. Reading the mark works through
60
+ * any wrapper that forwards property access, which is all of them.
61
+ */
62
+ export declare function rowPlaceholderState(row: unknown): 'loading' | 'failed' | null;
63
+ /**
64
+ * Mint a frozen placeholder the grid will recognise, carrying whatever
65
+ * other fields the caller wants on it. The row model uses this to give its
66
+ * placeholders a `kind`, so code reading display rows and code reading grid
67
+ * rows agree on what they are looking at.
68
+ */
69
+ export declare function createRowPlaceholder<T extends object>(state: 'loading' | 'failed', fields: T): Readonly<T & {
70
+ readonly [PLACEHOLDER]: 'loading' | 'failed';
71
+ }>;
72
+ /** What a block fetch has to answer with. `rowCount` is optional: see {@link BlockCacheOptions}. */
73
+ export type BlockFetchResult<TData> = {
74
+ rows: ReadonlyArray<TData>;
75
+ /**
76
+ * Total rows after filtering. Omit it (or send `-1`) when the backend cannot
77
+ * count cheaply - the cache then discovers the end from the first short
78
+ * block, and `lastRowKnown()` stays false until it does.
79
+ */
80
+ rowCount?: number;
81
+ };
82
+ /** What a block cache is built from: block size, caps, debounce and the fetch. */
83
+ export type BlockCacheOptions<TData> = {
84
+ /**
85
+ * Rows per request. Bigger blocks mean fewer round trips and more wasted
86
+ * rows when the user scrolls past; 100 is the usual compromise.
87
+ */
88
+ blockSize?: number;
89
+ /**
90
+ * Keep at most this many loaded blocks. Blocks outside the viewport are
91
+ * evicted least-recently-seen first, and scrolling back re-fetches them.
92
+ * Unlimited by default, which is right until the dataset is big enough that
93
+ * holding every visited block matters.
94
+ */
95
+ maxBlocksInCache?: number;
96
+ /** Requests open at once. Default 2. */
97
+ maxConcurrentRequests?: number;
98
+ /**
99
+ * Wait this long after the viewport settles before fetching. Non-zero values
100
+ * stop a drag of the scrollbar from requesting every block it flies past.
101
+ */
102
+ blockLoadDebounceMs?: number;
103
+ /**
104
+ * Rows to claim before anything is loaded, so the grid has a scrollbar (and
105
+ * `scrollToRow` has somewhere to land) on the first paint. Default 1.
106
+ */
107
+ initialRowCount?: number;
108
+ /**
109
+ * While the total is unknown, how many unloaded rows to keep past the last
110
+ * loaded one. Scrolling into them is what asks for the next block. Default 1.
111
+ */
112
+ overflowRows?: number;
113
+ /** Fetch one block. Reject, or throw, to mark it failed. */
114
+ fetch: (startRow: number, endRow: number, signal: AbortSignal) => Promise<BlockFetchResult<TData>>;
115
+ /** Called after any change to the rows, the count, or a block's state. */
116
+ onChange: (cache: BlockCacheState) => void;
117
+ /**
118
+ * How to coalesce `onChange`. Defaults to a microtask, so a burst of block
119
+ * arrivals rebuilds the row array once. Pass `(fn) => fn()` in tests to make
120
+ * every change synchronous.
121
+ */
122
+ schedule?: (flush: () => void) => void;
123
+ };
124
+ /** One block's place in the world, for diagnostics and for tests. */
125
+ export type BlockState = {
126
+ blockIndex: number;
127
+ startRow: number;
128
+ endRow: number;
129
+ status: 'loading' | 'loaded' | 'failed';
130
+ /** Monotonic counter of when the viewport last covered this block. */
131
+ lastTouched: number;
132
+ };
133
+ /** One block as `getCacheState()` reports it. */
134
+ export type BlockCacheState = {
135
+ /** Rows after filtering, or `null` while the backend has not said. */
136
+ rowCount: number | null;
137
+ /** False while the end of the data is still being discovered. */
138
+ lastRowKnown: boolean;
139
+ /** True while at least one block is in flight. */
140
+ loading: boolean;
141
+ /** Blocks whose fetch rejected. Empty unless something went wrong. */
142
+ failedBlocks: number[];
143
+ };
144
+ /**
145
+ * A block cache: rows arrive in blocks as the viewport reaches them,
146
+ * placeholders stand in until then, and blocks far from the viewport are
147
+ * evicted past the cap.
148
+ */
149
+ export type BlockCache<TData> = {
150
+ /**
151
+ * Tell the cache which rows are on screen. Fetches what is missing (after
152
+ * `blockLoadDebounceMs`) and marks those blocks as recently seen so eviction
153
+ * spares them. Safe to call on every scroll frame.
154
+ */
155
+ setViewport(startIndex: number, endIndex: number): void;
156
+ /** The dense row array to hand the grid. Placeholders fill unloaded slots. */
157
+ rows(): ReadonlyArray<TData>;
158
+ /** One row, without building the array. Returns a placeholder when unloaded. */
159
+ getRow(index: number): TData | typeof LOADING_ROW | typeof FAILED_ROW;
160
+ rowCount(): number | null;
161
+ lastRowKnown(): boolean;
162
+ /** Re-fetch the blocks that failed. */
163
+ retryFailed(): void;
164
+ /**
165
+ * Re-fetch the loaded blocks in place, keeping the row count and scroll
166
+ * position. What you want after a mutation lands on the server.
167
+ */
168
+ refresh(): void;
169
+ /** Drop everything and start over from the current viewport. */
170
+ purge(): void;
171
+ /** Override the total, e.g. from a count endpoint that answered separately. */
172
+ setRowCount(count: number | null, known?: boolean): void;
173
+ /**
174
+ * Write rows straight into the cache from `startRow`, as if a fetch had
175
+ * returned them, bypassing the datasource, the debounce and the
176
+ * concurrency cap. `startRow` must sit on a block boundary. An explicit
177
+ * `rowCount` settles the total; without one a short final slice does.
178
+ */
179
+ applyRows(startRow: number, rows: ReadonlyArray<TData>, rowCount?: number): void;
180
+ /** Replace one loaded row. No-op when its block is not loaded. */
181
+ patch(index: number, row: TData): boolean;
182
+ /**
183
+ * The index of the first LOADED row matching `predicate`, or -1. Rows
184
+ * that are not loaded are not visited - a transaction addressed by id
185
+ * can only touch what is in the cache.
186
+ */
187
+ findIndex(predicate: (row: TData, index: number) => boolean): number;
188
+ /**
189
+ * Insert rows at an index and grow the count. Works within whichever
190
+ * run of loaded blocks contains `index`; loaded blocks AFTER that run
191
+ * are dropped, because their rows have shifted and will be re-fetched at
192
+ * their new offsets. An index in an unloaded region grows the count
193
+ * only (the rows exist; they arrive when scrolled to). Returns whether
194
+ * the rows were placed in the cache.
195
+ */
196
+ insert(index: number, rows: ReadonlyArray<TData>): boolean;
197
+ /**
198
+ * Remove `count` rows at an index and shrink the count, with the same
199
+ * run semantics as `insert`. Returns how many rows were actually
200
+ * removed from the cache.
201
+ */
202
+ remove(index: number, count?: number): number;
203
+ /** Every block the cache is holding, for `debug` output and tests. */
204
+ getCacheState(): BlockState[];
205
+ /** Abort what is in flight and stop emitting. Call on unmount. */
206
+ dispose(): void;
207
+ };
208
+ /**
209
+ * Build a block cache over a `fetch(startRow, endRow, signal)`. The free
210
+ * infinite row model and the Enterprise server-side row model both run on
211
+ * it, one per level.
212
+ */
213
+ export declare function createBlockCache<TData>(options: BlockCacheOptions<TData>): BlockCache<TData>;
214
+ export {};