@highcharts/grid-pro 2.2.0 → 2.3.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 (266) hide show
  1. package/LICENSE.txt +6 -0
  2. package/README.md +15 -9
  3. package/css/grid-pro.css +1262 -1122
  4. package/css/modules/grid-base-variables.css +131 -0
  5. package/css/modules/grid-button-variables.css +140 -0
  6. package/css/modules/grid-caption-variables.css +11 -0
  7. package/css/modules/grid-description-variables.css +11 -0
  8. package/css/modules/grid-input-variables.css +114 -0
  9. package/css/modules/grid-link-variables.css +18 -0
  10. package/css/modules/grid-menu-variables.css +50 -0
  11. package/css/modules/grid-pagination-variables.css +12 -0
  12. package/css/modules/grid-popup-variables.css +24 -0
  13. package/css/modules/grid-pro.css +234 -0
  14. package/css/modules/grid-table-variables.css +385 -0
  15. package/css/modules/grid-theme-default.css +55 -0
  16. package/es-modules/Accessibility/A11yI18n.js +1 -2
  17. package/es-modules/Accessibility/Accessibility.js +1 -2
  18. package/es-modules/Accessibility/AccessibilityComponent.d.ts +2 -2
  19. package/es-modules/Accessibility/Components/InfoRegionsComponent.js +5 -6
  20. package/es-modules/Accessibility/Components/LegendComponent.js +1 -2
  21. package/es-modules/Accessibility/Components/MenuComponent.js +1 -2
  22. package/es-modules/Accessibility/Components/NavigatorComponent.js +3 -4
  23. package/es-modules/Accessibility/Components/RangeSelectorComponent.js +1 -2
  24. package/es-modules/Accessibility/Components/SeriesComponent/ForcedMarkers.js +1 -2
  25. package/es-modules/Accessibility/Components/SeriesComponent/NewDataAnnouncer.js +3 -4
  26. package/es-modules/Accessibility/Components/SeriesComponent/SeriesDescriber.js +3 -4
  27. package/es-modules/Accessibility/Components/SeriesComponent/SeriesKeyboardNavigation.js +1 -2
  28. package/es-modules/Accessibility/Components/ZoomComponent.js +1 -2
  29. package/es-modules/Accessibility/FocusBorder.js +1 -2
  30. package/es-modules/Accessibility/KeyboardNavigation.js +1 -2
  31. package/es-modules/Accessibility/KeyboardNavigationHandler.js +1 -2
  32. package/es-modules/Accessibility/Options/A11yDefaults.d.ts +11 -0
  33. package/es-modules/Accessibility/Options/A11yDefaults.js +11 -0
  34. package/es-modules/Accessibility/Options/DeprecatedOptions.d.ts +58 -0
  35. package/es-modules/Accessibility/Options/DeprecatedOptions.js +7 -2
  36. package/es-modules/Accessibility/Options/LangDefaults.js +9 -0
  37. package/es-modules/Accessibility/ProxyElement.js +2 -5
  38. package/es-modules/Accessibility/ProxyProvider.js +1 -2
  39. package/es-modules/Accessibility/Utils/Announcer.js +2 -3
  40. package/es-modules/Accessibility/Utils/ChartUtilities.js +1 -2
  41. package/es-modules/Accessibility/Utils/EventProvider.d.ts +2 -2
  42. package/es-modules/Accessibility/Utils/EventProvider.js +6 -2
  43. package/es-modules/Accessibility/Utils/HTMLUtilities.js +1 -2
  44. package/es-modules/Core/Animation/AnimationUtilities.js +1 -2
  45. package/es-modules/Core/Animation/Fx.js +1 -2
  46. package/es-modules/Core/Callback.d.ts +9 -2
  47. package/es-modules/Core/Chart/Chart.js +48 -41
  48. package/es-modules/Core/Chart/Chart3D.js +1 -2
  49. package/es-modules/Core/Chart/ChartDefaults.js +11 -0
  50. package/es-modules/Core/Chart/ChartOptions.d.ts +8 -0
  51. package/es-modules/Core/Chart/GanttChart.js +1 -2
  52. package/es-modules/Core/Chart/MapChart.js +1 -2
  53. package/es-modules/Core/Chart/StockChart.js +2 -3
  54. package/es-modules/Core/Color/Color.js +1 -2
  55. package/es-modules/Core/Defaults.js +36 -9
  56. package/es-modules/Core/Delaunay.d.ts +52 -0
  57. package/es-modules/Core/Delaunay.js +310 -0
  58. package/es-modules/Core/Foundation.js +1 -2
  59. package/es-modules/Core/Geometry/CircleUtilities.js +1 -2
  60. package/es-modules/Core/Globals.js +1 -1
  61. package/es-modules/Core/HttpUtilities.js +1 -2
  62. package/es-modules/Core/MSPointer.js +1 -2
  63. package/es-modules/Core/Math3D.js +1 -2
  64. package/es-modules/Core/Options.d.ts +6 -2
  65. package/es-modules/Core/Pointer.js +23 -4
  66. package/es-modules/Core/PointerEvent.d.ts +2 -0
  67. package/es-modules/Core/Renderer/HTML/AST.js +2 -2
  68. package/es-modules/Core/Renderer/HTML/HTMLElement.js +1 -2
  69. package/es-modules/Core/Renderer/RendererUtilities.js +34 -19
  70. package/es-modules/Core/Responsive.d.ts +4 -2
  71. package/es-modules/Core/Responsive.js +10 -4
  72. package/es-modules/Core/Templating.d.ts +1 -1
  73. package/es-modules/Core/Templating.js +3 -4
  74. package/es-modules/Core/Time.d.ts +8 -0
  75. package/es-modules/Core/Time.js +2 -2
  76. package/es-modules/Core/Tooltip.d.ts +4 -8
  77. package/es-modules/Core/Tooltip.js +100 -81
  78. package/es-modules/Core/TooltipOptions.d.ts +44 -1
  79. package/es-modules/Core/Utilities.d.ts +25 -665
  80. package/es-modules/Core/Utilities.js +17 -1398
  81. package/es-modules/Data/ColumnUtils.d.ts +83 -80
  82. package/es-modules/Data/ColumnUtils.js +103 -113
  83. package/es-modules/Data/Connectors/CSVConnector.d.ts +9 -14
  84. package/es-modules/Data/Connectors/CSVConnector.js +3 -4
  85. package/es-modules/Data/Connectors/DataConnector.d.ts +52 -54
  86. package/es-modules/Data/Connectors/DataConnector.js +33 -52
  87. package/es-modules/Data/Connectors/GoogleSheetsConnector.d.ts +40 -42
  88. package/es-modules/Data/Connectors/GoogleSheetsConnector.js +73 -88
  89. package/es-modules/Data/Connectors/GoogleSheetsConnectorOptions.d.ts +4 -2
  90. package/es-modules/Data/Connectors/HTMLTableConnector.d.ts +23 -28
  91. package/es-modules/Data/Connectors/HTMLTableConnector.js +3 -4
  92. package/es-modules/Data/Connectors/JSONConnector.d.ts +9 -14
  93. package/es-modules/Data/Connectors/JSONConnector.js +3 -4
  94. package/es-modules/Data/Converters/CSVConverter.d.ts +5 -5
  95. package/es-modules/Data/Converters/CSVConverter.js +3 -4
  96. package/es-modules/Data/Converters/CSVConverterOptions.d.ts +2 -2
  97. package/es-modules/Data/Converters/DataConverter.d.ts +76 -78
  98. package/es-modules/Data/Converters/DataConverter.js +26 -53
  99. package/es-modules/Data/Converters/DataConverterUtils.d.ts +88 -81
  100. package/es-modules/Data/Converters/DataConverterUtils.js +169 -181
  101. package/es-modules/Data/Converters/GoogleSheetsConverter.d.ts +4 -4
  102. package/es-modules/Data/Converters/GoogleSheetsConverter.js +3 -3
  103. package/es-modules/Data/Converters/GoogleSheetsConverterOptions.d.ts +2 -2
  104. package/es-modules/Data/Converters/HTMLTableConverter.d.ts +4 -4
  105. package/es-modules/Data/Converters/HTMLTableConverter.js +2 -3
  106. package/es-modules/Data/Converters/HTMLTableConverterOptions.d.ts +2 -2
  107. package/es-modules/Data/Converters/JSONConverter.d.ts +7 -7
  108. package/es-modules/Data/Converters/JSONConverter.js +6 -6
  109. package/es-modules/Data/Converters/JSONConverterOptions.d.ts +2 -2
  110. package/es-modules/Data/DataCursor.d.ts +65 -70
  111. package/es-modules/Data/DataCursor.js +119 -139
  112. package/es-modules/Data/DataEvent.d.ts +55 -57
  113. package/es-modules/Data/DataPool.d.ts +10 -17
  114. package/es-modules/Data/DataPool.js +20 -9
  115. package/es-modules/Data/DataTable.d.ts +111 -113
  116. package/es-modules/Data/DataTable.js +3 -3
  117. package/es-modules/Data/DataTableCore.d.ts +9 -9
  118. package/es-modules/Data/DataTableCore.js +2 -2
  119. package/es-modules/Data/Formula/FormulaParser.js +1 -2
  120. package/es-modules/Data/Formula/FormulaProcessor.js +1 -2
  121. package/es-modules/Data/Modifiers/ChainModifier.d.ts +29 -34
  122. package/es-modules/Data/Modifiers/ChainModifier.js +7 -8
  123. package/es-modules/Data/Modifiers/DataModifier.d.ts +36 -41
  124. package/es-modules/Data/Modifiers/DataModifier.js +31 -53
  125. package/es-modules/Data/Modifiers/FilterModifier.d.ts +4 -4
  126. package/es-modules/Data/Modifiers/FilterModifier.js +3 -4
  127. package/es-modules/Data/Modifiers/FilterModifierOptions.d.ts +3 -2
  128. package/es-modules/Data/Modifiers/InvertModifier.d.ts +4 -4
  129. package/es-modules/Data/Modifiers/InvertModifier.js +3 -4
  130. package/es-modules/Data/Modifiers/MathModifier.d.ts +5 -4
  131. package/es-modules/Data/Modifiers/RangeModifier.d.ts +4 -4
  132. package/es-modules/Data/Modifiers/RangeModifier.js +3 -4
  133. package/es-modules/Data/Modifiers/SortModifier.d.ts +6 -11
  134. package/es-modules/Data/Modifiers/SortModifier.js +3 -4
  135. package/es-modules/Data/Modifiers/SortModifierOptions.d.ts +3 -3
  136. package/es-modules/Grid/Core/Accessibility/Accessibility.d.ts +1 -1
  137. package/es-modules/Grid/Core/Accessibility/Accessibility.js +5 -6
  138. package/es-modules/Grid/Core/Data/DataProvider.d.ts +97 -0
  139. package/es-modules/Grid/Core/Data/DataProvider.js +89 -0
  140. package/es-modules/Grid/Core/Data/DataProviderRegistry.d.ts +20 -0
  141. package/es-modules/Grid/Core/Data/DataProviderRegistry.js +52 -0
  142. package/es-modules/Grid/Core/Data/DataProviderType.d.ts +19 -0
  143. package/es-modules/Grid/Core/Data/DataProviderType.js +15 -0
  144. package/es-modules/Grid/Core/Data/LocalDataProvider.d.ts +165 -0
  145. package/es-modules/Grid/Core/Data/LocalDataProvider.js +341 -0
  146. package/es-modules/Grid/Core/Defaults.js +4 -2
  147. package/es-modules/Grid/Core/Globals.d.ts +20 -16
  148. package/es-modules/Grid/Core/Globals.js +10 -8
  149. package/es-modules/Grid/Core/Grid.d.ts +31 -22
  150. package/es-modules/Grid/Core/Grid.js +214 -136
  151. package/es-modules/Grid/Core/GridUtils.d.ts +33 -0
  152. package/es-modules/Grid/Core/GridUtils.js +50 -3
  153. package/es-modules/Grid/Core/Options.d.ts +145 -4
  154. package/es-modules/Grid/Core/Pagination/Pagination.d.ts +3 -0
  155. package/es-modules/Grid/Core/Pagination/Pagination.js +63 -23
  156. package/es-modules/Grid/Core/Pagination/PaginationOptions.d.ts +4 -0
  157. package/es-modules/Grid/Core/Querying/FilteringController.js +1 -2
  158. package/es-modules/Grid/Core/Querying/PaginationController.d.ts +2 -2
  159. package/es-modules/Grid/Core/Querying/PaginationController.js +3 -3
  160. package/es-modules/Grid/Core/Querying/QueryingController.d.ts +1 -1
  161. package/es-modules/Grid/Core/Querying/QueryingController.js +2 -26
  162. package/es-modules/Grid/Core/Responsive/ResponsiveComposition.d.ts +53 -0
  163. package/es-modules/Grid/Core/Responsive/ResponsiveComposition.js +229 -0
  164. package/es-modules/Grid/Core/Responsive/ResponsiveOptions.d.ts +58 -0
  165. package/es-modules/Grid/Core/Responsive/ResponsiveOptions.js +15 -0
  166. package/es-modules/Grid/Core/Table/Actions/ColumnFiltering/ColumnFiltering.js +2 -3
  167. package/es-modules/Grid/Core/Table/Actions/ColumnFiltering/FilterCell.d.ts +1 -1
  168. package/es-modules/Grid/Core/Table/Actions/ColumnFiltering/FilterCell.js +3 -4
  169. package/es-modules/Grid/Core/Table/Actions/ColumnFiltering/FilterRow.d.ts +1 -1
  170. package/es-modules/Grid/Core/Table/Actions/ColumnFiltering/FilterRow.js +2 -2
  171. package/es-modules/Grid/Core/Table/Actions/ColumnSorting.d.ts +17 -1
  172. package/es-modules/Grid/Core/Table/Actions/ColumnSorting.js +58 -15
  173. package/es-modules/Grid/Core/Table/Actions/ColumnsResizer.js +1 -3
  174. package/es-modules/Grid/Core/Table/Actions/RowsVirtualizer.d.ts +57 -3
  175. package/es-modules/Grid/Core/Table/Actions/RowsVirtualizer.js +397 -118
  176. package/es-modules/Grid/Core/Table/Body/CellContextMenu.d.ts +11 -0
  177. package/es-modules/Grid/Core/Table/Body/CellContextMenu.js +84 -0
  178. package/es-modules/Grid/Core/Table/Body/TableCell.d.ts +27 -13
  179. package/es-modules/Grid/Core/Table/Body/TableCell.js +74 -25
  180. package/es-modules/Grid/Core/Table/Body/TableRow.d.ts +7 -15
  181. package/es-modules/Grid/Core/Table/Body/TableRow.js +24 -19
  182. package/es-modules/Grid/Core/Table/Cell.d.ts +16 -3
  183. package/es-modules/Grid/Core/Table/Cell.js +36 -3
  184. package/es-modules/Grid/Core/Table/CellContent/TextContent.js +3 -8
  185. package/es-modules/Grid/Core/Table/Column.d.ts +10 -4
  186. package/es-modules/Grid/Core/Table/Column.js +23 -34
  187. package/es-modules/Grid/Core/Table/ColumnResizing/IndependentResizingMode.js +9 -8
  188. package/es-modules/Grid/Core/Table/ColumnResizing/ResizingMode.js +4 -3
  189. package/es-modules/Grid/Core/Table/Header/ColumnToolbar/ColumnToolbar.d.ts +5 -0
  190. package/es-modules/Grid/Core/Table/Header/ColumnToolbar/ColumnToolbar.js +7 -2
  191. package/es-modules/Grid/Core/Table/Header/ColumnToolbar/FilterPopup.js +1 -2
  192. package/es-modules/Grid/Core/Table/Header/ColumnToolbar/MenuButtons/FilterMenuButton.js +1 -2
  193. package/es-modules/Grid/Core/Table/Header/ColumnToolbar/MenuButtons/SortMenuButton.js +2 -3
  194. package/es-modules/Grid/Core/Table/Header/ColumnToolbar/ToolbarButtons/FilterToolbarButton.js +1 -2
  195. package/es-modules/Grid/Core/Table/Header/ColumnToolbar/ToolbarButtons/MenuToolbarButton.js +1 -2
  196. package/es-modules/Grid/Core/Table/Header/ColumnToolbar/ToolbarButtons/SortToolbarButton.d.ts +0 -1
  197. package/es-modules/Grid/Core/Table/Header/ColumnToolbar/ToolbarButtons/SortToolbarButton.js +4 -14
  198. package/es-modules/Grid/Core/Table/Header/HeaderCell.d.ts +6 -1
  199. package/es-modules/Grid/Core/Table/Header/HeaderCell.js +33 -13
  200. package/es-modules/Grid/Core/Table/Header/HeaderRow.js +4 -5
  201. package/es-modules/Grid/Core/Table/Header/TableHeader.d.ts +1 -1
  202. package/es-modules/Grid/Core/Table/Header/TableHeader.js +3 -3
  203. package/es-modules/Grid/Core/Table/Row.d.ts +1 -1
  204. package/es-modules/Grid/Core/Table/Row.js +2 -2
  205. package/es-modules/Grid/Core/Table/Table.d.ts +38 -12
  206. package/es-modules/Grid/Core/Table/Table.js +184 -91
  207. package/es-modules/Grid/Core/UI/ContextMenuButton.d.ts +6 -7
  208. package/es-modules/Grid/Core/UI/ContextMenuButton.js +5 -3
  209. package/es-modules/Grid/Core/UI/Popup.js +7 -9
  210. package/es-modules/Grid/Core/UI/SvgIcons.d.ts +42 -7
  211. package/es-modules/Grid/Core/UI/SvgIcons.js +206 -33
  212. package/es-modules/Grid/Core/UI/Toolbar.d.ts +5 -0
  213. package/es-modules/Grid/Core/UI/ToolbarButton.d.ts +4 -12
  214. package/es-modules/Grid/Core/UI/ToolbarButton.js +8 -26
  215. package/es-modules/Grid/Pro/CellEditing/CellEditMode.d.ts +2 -2
  216. package/es-modules/Grid/Pro/CellEditing/CellEditing.js +10 -10
  217. package/es-modules/Grid/Pro/CellEditing/CellEditingComposition.js +1 -2
  218. package/es-modules/Grid/Pro/CellRendering/CellRenderersComposition.d.ts +1 -1
  219. package/es-modules/Grid/Pro/CellRendering/CellRenderersComposition.js +4 -2
  220. package/es-modules/Grid/Pro/CellRendering/ContentTypes/CheckboxContent.d.ts +2 -2
  221. package/es-modules/Grid/Pro/CellRendering/ContentTypes/NumberInputContent.js +1 -2
  222. package/es-modules/Grid/Pro/CellRendering/ContentTypes/SelectContent.d.ts +2 -2
  223. package/es-modules/Grid/Pro/CellRendering/ContentTypes/SparklineContent.js +1 -2
  224. package/es-modules/Grid/Pro/CellRendering/ContentTypes/TextInputContent.d.ts +2 -2
  225. package/es-modules/Grid/Pro/CellRendering/ContentTypes/TextInputContent.js +1 -2
  226. package/es-modules/Grid/Pro/CellRendering/Renderers/CheckboxRenderer.js +1 -2
  227. package/es-modules/Grid/Pro/CellRendering/Renderers/DateInputRenderer.js +1 -2
  228. package/es-modules/Grid/Pro/CellRendering/Renderers/DateTimeInputRenderer.js +1 -2
  229. package/es-modules/Grid/Pro/CellRendering/Renderers/NumberInputRenderer.js +1 -2
  230. package/es-modules/Grid/Pro/CellRendering/Renderers/SelectRenderer.js +1 -2
  231. package/es-modules/Grid/Pro/CellRendering/Renderers/SparklineRenderer.d.ts +2 -2
  232. package/es-modules/Grid/Pro/CellRendering/Renderers/SparklineRenderer.js +1 -2
  233. package/es-modules/Grid/Pro/CellRendering/Renderers/TextInputRenderer.js +1 -2
  234. package/es-modules/Grid/Pro/CellRendering/Renderers/TextRenderer.js +1 -2
  235. package/es-modules/Grid/Pro/CellRendering/Renderers/TimeInputRenderer.js +1 -2
  236. package/es-modules/Grid/Pro/ColumnTypes/Validator.js +34 -32
  237. package/es-modules/Grid/Pro/ColumnTypes/ValidatorComposition.d.ts +1 -1
  238. package/es-modules/Grid/Pro/ColumnTypes/ValidatorComposition.js +3 -4
  239. package/es-modules/Grid/Pro/Credits/CreditsProComposition.js +1 -3
  240. package/es-modules/Grid/Pro/Data/DataSourceHelper.d.ts +74 -0
  241. package/es-modules/Grid/Pro/Data/DataSourceHelper.js +246 -0
  242. package/es-modules/Grid/Pro/Data/QuerySerializer.d.ts +46 -0
  243. package/es-modules/Grid/Pro/Data/QuerySerializer.js +169 -0
  244. package/es-modules/Grid/Pro/Data/RemoteDataProvider.d.ts +187 -0
  245. package/es-modules/Grid/Pro/Data/RemoteDataProvider.js +500 -0
  246. package/es-modules/Grid/Pro/Export/Exporting.js +1 -2
  247. package/es-modules/Grid/Pro/Export/ExportingComposition.js +1 -2
  248. package/es-modules/Grid/Pro/GridEvents.js +1 -2
  249. package/es-modules/Grid/Pro/Pagination/PaginationComposition.d.ts +0 -1
  250. package/es-modules/Grid/Pro/Pagination/PaginationComposition.js +1 -2
  251. package/es-modules/Shared/BaseForm.js +1 -2
  252. package/es-modules/Shared/DownloadURL.js +1 -2
  253. package/es-modules/Shared/TimeBase.js +3 -3
  254. package/es-modules/Shared/Types.d.ts +7 -0
  255. package/es-modules/Shared/Utilities.d.ts +576 -0
  256. package/es-modules/Shared/Utilities.js +1368 -0
  257. package/es-modules/masters/grid-pro.src.d.ts +15 -11
  258. package/es-modules/masters/grid-pro.src.js +12 -5
  259. package/grid-pro.d.ts +15 -11
  260. package/grid-pro.js +3 -3
  261. package/grid-pro.js.map +1 -1
  262. package/grid-pro.src.d.ts +15 -11
  263. package/grid-pro.src.js +10408 -8009
  264. package/package.json +1 -1
  265. package/es-modules/Grid/Core/Pagination/Icons.d.ts +0 -7
  266. package/es-modules/Grid/Core/Pagination/Icons.js +0 -7
@@ -15,6 +15,7 @@
15
15
  'use strict';
16
16
  import TableRow from '../Body/TableRow.js';
17
17
  import Globals from '../../Globals.js';
18
+ import { defined } from '../../../../Shared/Utilities.js';
18
19
  /* *
19
20
  *
20
21
  * Class
@@ -24,6 +25,40 @@ import Globals from '../../Globals.js';
24
25
  * Represents a virtualized rows renderer for the data grid.
25
26
  */
26
27
  class RowsVirtualizer {
28
+ /**
29
+ * The maximum height of a HTML element in most browsers.
30
+ * Firefox has a lower limit than other browsers.
31
+ */
32
+ static getMaxElementHeight() {
33
+ if (RowsVirtualizer.maxElementHeight !== void 0) {
34
+ return RowsVirtualizer.maxElementHeight;
35
+ }
36
+ const isFirefox = Globals.userAgent.indexOf('Firefox') > -1;
37
+ const fallbackMax = ((isFirefox ? 6000000 : 31000000) /
38
+ (window.devicePixelRatio || 1));
39
+ if (!document.body) {
40
+ RowsVirtualizer.maxElementHeight = fallbackMax;
41
+ return RowsVirtualizer.maxElementHeight;
42
+ }
43
+ let res = 1000000;
44
+ const testUpTo = isFirefox ? 6000000 : 1000000000;
45
+ const div = document.createElement('div');
46
+ document.body.appendChild(div);
47
+ let done = false;
48
+ while (!done) {
49
+ const test = res * 2;
50
+ div.style.height = test + 'px';
51
+ if (test > testUpTo || div.clientHeight !== test) {
52
+ done = true;
53
+ continue;
54
+ }
55
+ res = test;
56
+ }
57
+ div.remove();
58
+ const safeMax = 16000000;
59
+ RowsVirtualizer.maxElementHeight = Math.min(res || fallbackMax, safeMax);
60
+ return RowsVirtualizer.maxElementHeight;
61
+ }
27
62
  /* *
28
63
  *
29
64
  * Constructor
@@ -36,6 +71,15 @@ class RowsVirtualizer {
36
71
  * The viewport of the data grid to render rows in.
37
72
  */
38
73
  constructor(viewport) {
74
+ /* *
75
+ *
76
+ * Properties
77
+ *
78
+ * */
79
+ /**
80
+ * The default height of a row.
81
+ */
82
+ this.defaultRowHeight = 49;
39
83
  /**
40
84
  * The index of the first visible row.
41
85
  */
@@ -45,6 +89,21 @@ class RowsVirtualizer {
45
89
  * flickering loops when scrolling to the last row.
46
90
  */
47
91
  this.preventScroll = false;
92
+ /**
93
+ * The total height of the grid, used when the Grid height
94
+ * exceeds the max element height.
95
+ */
96
+ this.totalGridHeight = 0;
97
+ /**
98
+ * The overflow height of the grid, used when the Grid height
99
+ * exceeds the max element height.
100
+ */
101
+ this.gridHeightOverflow = 0;
102
+ /**
103
+ * The scroll offset in pixels used to adjust the row positions when
104
+ * the Grid height exceeds the max element height.
105
+ */
106
+ this.scrollOffset = 0;
48
107
  /**
49
108
  * Reuse pool for rows that are currently out of viewport.
50
109
  */
@@ -54,12 +113,23 @@ class RowsVirtualizer {
54
113
  * frame.
55
114
  */
56
115
  this.scrollQueued = false;
116
+ /**
117
+ * Flag indicating if rows are currently being rendered to prevent
118
+ * concurrent render operations.
119
+ */
120
+ this.isRendering = false;
121
+ /**
122
+ * Pending row cursor to render after current render completes.
123
+ * Used to ensure the final scroll position is rendered.
124
+ */
125
+ this.pendingRowCursor = null;
57
126
  this.rowSettings =
58
127
  viewport.grid.options?.rendering?.rows;
59
128
  this.viewport = viewport;
129
+ this.rowCount = 0;
60
130
  this.strictRowHeights = this.rowSettings.strictHeights;
61
131
  this.buffer = Math.max(this.rowSettings.bufferSize, 0);
62
- this.defaultRowHeight = this.getDefaultRowHeight();
132
+ this.maxElementHeight = RowsVirtualizer.getMaxElementHeight();
63
133
  if (this.strictRowHeights) {
64
134
  viewport.tbodyElement.classList.add(Globals.getClassName('rowsContentNowrap'));
65
135
  }
@@ -72,20 +142,26 @@ class RowsVirtualizer {
72
142
  /**
73
143
  * Renders the rows in the viewport for the first time.
74
144
  */
75
- initialRender() {
145
+ async initialRender() {
146
+ this.defaultRowHeight = await this.getDefaultRowHeight();
76
147
  // Initial reflow to set the viewport height
77
148
  if (this.viewport.virtualRows) {
78
149
  this.viewport.reflow();
79
150
  }
151
+ await this.updateGridMetrics();
80
152
  // Load & render rows
81
- this.renderRows(this.rowCursor);
153
+ await this.renderRows(this.rowCursor);
82
154
  this.adjustRowHeights();
155
+ if (this.viewport.virtualRows) {
156
+ this.adjustRowOffsets();
157
+ }
83
158
  }
84
159
  /**
85
160
  * Renders the rows in the viewport. It is called when the rows need to be
86
161
  * re-rendered, e.g., after a sort or filter operation.
87
162
  */
88
- rerender() {
163
+ async rerender() {
164
+ await this.updateGridMetrics();
89
165
  const tbody = this.viewport.tbodyElement;
90
166
  let rows = this.viewport.rows;
91
167
  const oldScrollLeft = tbody.scrollLeft;
@@ -103,12 +179,11 @@ class RowsVirtualizer {
103
179
  }
104
180
  rows.length = 0;
105
181
  }
106
- this.renderRows(this.rowCursor);
182
+ await this.renderRows(this.rowCursor);
107
183
  if (this.viewport.virtualRows) {
108
- if (oldScrollTop !== void 0) {
184
+ if (defined(oldScrollTop)) {
109
185
  tbody.scrollTop = oldScrollTop;
110
186
  }
111
- this.scroll();
112
187
  }
113
188
  rows = this.viewport.rows;
114
189
  // Reflow the rendered row cells widths (check redundancy)
@@ -117,6 +192,33 @@ class RowsVirtualizer {
117
192
  }
118
193
  tbody.scrollLeft = oldScrollLeft;
119
194
  }
195
+ /**
196
+ * Refreshes the rendered rows without a full teardown.
197
+ * It updates the row range and reuses existing rows when possible.
198
+ */
199
+ async refreshRows() {
200
+ await this.updateGridMetrics();
201
+ const tbody = this.viewport.tbodyElement;
202
+ const oldScrollLeft = tbody.scrollLeft;
203
+ const oldScrollTop = this.viewport.virtualRows ?
204
+ tbody.scrollTop :
205
+ void 0;
206
+ const maxRowCursor = Math.max(0, this.rowCount - 1);
207
+ if (this.rowCursor > maxRowCursor) {
208
+ this.rowCursor = maxRowCursor;
209
+ }
210
+ // Render missing rows, drop out-of-range ones, and ensure last row.
211
+ await this.renderRows(this.rowCursor);
212
+ const rows = this.viewport.rows;
213
+ for (let i = 0, iEnd = rows.length; i < iEnd; ++i) {
214
+ // Update row data so indices map to fresh provider values.
215
+ await rows[i].update();
216
+ }
217
+ if (this.viewport.virtualRows && defined(oldScrollTop)) {
218
+ tbody.scrollTop = oldScrollTop;
219
+ }
220
+ tbody.scrollLeft = oldScrollLeft;
221
+ }
120
222
  /**
121
223
  * Method called on the viewport scroll event, only when the virtualization
122
224
  * is enabled.
@@ -128,16 +230,22 @@ class RowsVirtualizer {
128
230
  this.scrollQueued = true;
129
231
  requestAnimationFrame(() => {
130
232
  this.scrollQueued = false;
131
- this.applyScroll();
233
+ void this.applyScroll();
132
234
  });
133
235
  }
134
236
  /**
135
237
  * Applies the scroll logic for virtualized rows.
136
238
  */
137
- applyScroll() {
239
+ async applyScroll() {
138
240
  const target = this.viewport.tbodyElement;
139
241
  const { defaultRowHeight: rowHeight } = this;
140
242
  const lastScrollTop = target.scrollTop;
243
+ const scrollDenominator = this.maxElementHeight -
244
+ target.clientHeight;
245
+ const scrollPercentage = scrollDenominator > 0 ?
246
+ lastScrollTop / scrollDenominator :
247
+ 0;
248
+ this.scrollOffset = Math.floor(scrollPercentage * this.gridHeightOverflow);
141
249
  if (this.preventScroll) {
142
250
  if (lastScrollTop <= target.scrollTop) {
143
251
  this.preventScroll = false;
@@ -146,12 +254,16 @@ class RowsVirtualizer {
146
254
  return;
147
255
  }
148
256
  // Do vertical virtual scrolling
149
- const rowCursor = Math.floor(target.scrollTop / rowHeight);
257
+ let rowCursor = Math.floor((target.scrollTop / rowHeight) +
258
+ (this.scrollOffset / rowHeight));
259
+ const maxRowCursor = Math.max(0, this.rowCount - 1);
260
+ rowCursor = Math.min(rowCursor, maxRowCursor);
150
261
  if (this.rowCursor !== rowCursor) {
151
- this.renderRows(rowCursor);
262
+ await this.renderRows(rowCursor);
152
263
  }
153
264
  this.rowCursor = rowCursor;
154
265
  this.adjustRowHeights();
266
+ this.adjustRowOffsets();
155
267
  if (!this.strictRowHeights &&
156
268
  lastScrollTop > target.scrollTop &&
157
269
  !this.preventScroll) {
@@ -165,7 +277,16 @@ class RowsVirtualizer {
165
277
  adjustBottomRowHeights() {
166
278
  const rows = this.viewport.rows;
167
279
  const rowsLn = rows.length;
280
+ if (rowsLn < 1) {
281
+ return;
282
+ }
168
283
  const lastRow = rows[rowsLn - 1];
284
+ // Skip if row is not fully rendered or has no cells
285
+ if (!lastRow.rendered ||
286
+ !lastRow.cells.length ||
287
+ !lastRow.cells[0]?.htmlElement) {
288
+ return;
289
+ }
169
290
  let rowTop = lastRow.translateY;
170
291
  const rowBottom = rowTop + lastRow.htmlElement.offsetHeight;
171
292
  let newHeight = lastRow.cells[0].htmlElement.offsetHeight;
@@ -173,16 +294,28 @@ class RowsVirtualizer {
173
294
  lastRow.htmlElement.style.height = newHeight + 'px';
174
295
  lastRow.setTranslateY(rowTop);
175
296
  for (let j = 0, jEnd = lastRow.cells.length; j < jEnd; ++j) {
176
- lastRow.cells[j].htmlElement.style.transform = '';
297
+ const cell = lastRow.cells[j];
298
+ if (cell?.htmlElement) {
299
+ cell.htmlElement.style.transform = '';
300
+ }
177
301
  }
178
302
  for (let i = rowsLn - 2; i >= 0; i--) {
179
303
  const row = rows[i];
304
+ // Skip if row is not fully rendered or has no cells
305
+ if (!row.rendered ||
306
+ !row.cells.length ||
307
+ !row.cells[0]?.htmlElement) {
308
+ continue;
309
+ }
180
310
  newHeight = row.cells[0].htmlElement.offsetHeight;
181
311
  rowTop -= newHeight;
182
312
  row.htmlElement.style.height = newHeight + 'px';
183
313
  row.setTranslateY(rowTop);
184
314
  for (let j = 0, jEnd = row.cells.length; j < jEnd; ++j) {
185
- row.cells[j].htmlElement.style.transform = '';
315
+ const cell = row.cells[j];
316
+ if (cell?.htmlElement) {
317
+ cell.htmlElement.style.transform = '';
318
+ }
186
319
  }
187
320
  }
188
321
  }
@@ -193,121 +326,197 @@ class RowsVirtualizer {
193
326
  * @param rowCursor
194
327
  * The index of the first visible row.
195
328
  */
196
- renderRows(rowCursor) {
197
- const { viewport: vp, buffer } = this;
198
- const rowCount = vp.dataTable.getRowCount();
199
- // Stop rendering if there are no rows to render.
200
- if (rowCount < 1) {
329
+ async renderRows(rowCursor) {
330
+ // Prevent concurrent render operations - queue the latest cursor
331
+ if (this.isRendering) {
332
+ this.pendingRowCursor = rowCursor;
201
333
  return;
202
334
  }
203
- const isVirtualization = this.viewport.virtualRows;
204
- const rowsPerPage = isVirtualization ? Math.ceil((vp.grid.tableElement?.clientHeight || 0) /
205
- this.defaultRowHeight) : Infinity; // Need to be refactored when add pagination
206
- let rows = vp.rows;
207
- if (!isVirtualization && rows.length > 50) {
208
- // eslint-disable-next-line no-console
209
- console.warn('Grid: a large dataset can cause performance issues when ' +
210
- 'virtualization is disabled. Consider enabling ' +
211
- 'virtualization in the rows settings.');
212
- }
213
- if (!rows.length) {
214
- const last = new TableRow(vp, rowCount - 1);
215
- vp.tbodyElement.appendChild(last.htmlElement);
216
- last.render();
217
- rows.push(last);
218
- if (isVirtualization) {
219
- last.setTranslateY(last.getDefaultTopOffset());
335
+ this.isRendering = true;
336
+ try {
337
+ const { viewport: vp, buffer } = this;
338
+ await this.updateGridMetrics();
339
+ const rowCount = this.rowCount;
340
+ if (!defined(rowCount)) {
341
+ return;
220
342
  }
221
- }
222
- const from = Math.max(0, Math.min(rowCursor - buffer, rowCount - rowsPerPage));
223
- const to = Math.min(rowCursor + rowsPerPage + buffer, rows[rows.length - 1].index - 1);
224
- const alwaysLastRow = rows.pop();
225
- const tempRows = [];
226
- const currentFrom = rows[0]?.index;
227
- const currentTo = rows[rows.length - 1]?.index;
228
- const hasOverlap = (rows.length > 0 &&
229
- currentFrom !== void 0 &&
230
- currentTo !== void 0 &&
231
- !(to < currentFrom || from > currentTo));
232
- if (!hasOverlap) {
233
- // Remove rows that are out of the range except the last row.
234
- for (let i = 0, iEnd = rows.length; i < iEnd; ++i) {
235
- const row = rows[i];
236
- const rowIndex = row.index;
237
- if (rowIndex < from || rowIndex > to) {
238
- this.poolRow(row);
239
- }
240
- else {
241
- tempRows.push(row);
343
+ if (rowCount === 0) {
344
+ if (vp.rows.length) {
345
+ for (let i = 0, iEnd = vp.rows.length; i < iEnd; ++i) {
346
+ vp.rows[i].destroy();
347
+ }
348
+ vp.rows.length = 0;
242
349
  }
350
+ vp.tbodyElement.innerHTML = '';
351
+ this.rowCursor = 0;
352
+ return;
243
353
  }
244
- rows = tempRows;
245
- vp.rows = rows;
246
- for (let i = from; i <= to; ++i) {
247
- const row = rows[i - (rows[0]?.index || 0)];
248
- // Recreate row when it is destroyed and it is in the range.
249
- if (!row) {
250
- rows.push(this.getOrCreateRow(i));
251
- }
354
+ // Stop rendering if there are no rows to render.
355
+ if (rowCount < 1) {
356
+ return;
252
357
  }
253
- rows.sort((a, b) => a.index - b.index);
254
- }
255
- else {
256
- // Remove rows outside the range from the start.
257
- while (rows.length && rows[0].index < from) {
258
- this.poolRow(rows.shift());
358
+ const isVirtualization = this.viewport.virtualRows;
359
+ const rowsPerPage = isVirtualization ? Math.ceil((vp.grid.tableElement?.clientHeight || 0) /
360
+ this.defaultRowHeight) : Infinity; // Need to be refactored when add pagination
361
+ let rows = vp.rows;
362
+ if (!isVirtualization && rows.length > 50) {
363
+ // eslint-disable-next-line no-console
364
+ console.warn('Grid: a large dataset can cause performance issues when ' +
365
+ 'virtualization is disabled. Consider enabling ' +
366
+ 'virtualization in the rows settings.');
259
367
  }
260
- // Remove rows outside the range from the end.
261
- while (rows.length && rows[rows.length - 1].index > to) {
262
- this.poolRow(rows.pop());
368
+ if (!rows.length && rowCount > 0) {
369
+ const last = new TableRow(vp, rowCount - 1);
370
+ await last.init();
371
+ vp.tbodyElement.appendChild(last.htmlElement);
372
+ await last.render();
373
+ rows.push(last);
374
+ if (isVirtualization) {
375
+ const topOffset = Math.min(last.getDefaultTopOffset(), this.maxElementHeight -
376
+ last.htmlElement.offsetHeight);
377
+ last.setTranslateY(topOffset);
378
+ }
379
+ }
380
+ // The last row is always kept rendered for bottom alignment
381
+ let alwaysLastRow = rows.length > 0 ? rows.pop() : void 0;
382
+ if (alwaysLastRow && alwaysLastRow.index !== rowCount - 1) {
383
+ this.poolRow(alwaysLastRow);
384
+ alwaysLastRow = void 0;
263
385
  }
264
- if (!rows.length) {
386
+ const from = Math.max(0, Math.min(rowCursor - buffer, rowCount - rowsPerPage));
387
+ // `to` should not include the alwaysLastRow index (rowCount - 1)
388
+ const to = Math.min(rowCursor + rowsPerPage + buffer, rowCount - 2 // -2 because alwaysLastRow is at rowCount - 1
389
+ );
390
+ const tempRows = [];
391
+ const currentFrom = rows[0]?.index;
392
+ const currentTo = rows[rows.length - 1]?.index;
393
+ const hasOverlap = (rows.length > 0 &&
394
+ defined(currentFrom) &&
395
+ defined(currentTo) &&
396
+ !(to < currentFrom || from > currentTo));
397
+ if (!hasOverlap) {
398
+ // Remove rows that are out of the range except the last row.
399
+ for (let i = 0, iEnd = rows.length; i < iEnd; ++i) {
400
+ const row = rows[i];
401
+ const rowIndex = row.index;
402
+ if (rowIndex < from || rowIndex > to) {
403
+ this.poolRow(row);
404
+ }
405
+ else {
406
+ tempRows.push(row);
407
+ }
408
+ }
409
+ rows = tempRows;
410
+ vp.rows = rows;
265
411
  for (let i = from; i <= to; ++i) {
266
- rows.push(this.getOrCreateRow(i));
412
+ const firstRowIndex = rows.length > 0 ? rows[0].index : from;
413
+ const row = rows[i - firstRowIndex];
414
+ // Recreate row when it is destroyed and it is in the range.
415
+ if (!row) {
416
+ const newRow = await this.getOrCreateRow(i);
417
+ rows.push(newRow);
418
+ }
267
419
  }
420
+ rows.sort((a, b) => a.index - b.index);
268
421
  }
269
422
  else {
270
- // Add rows before the current range.
271
- for (let i = rows[0].index - 1; i >= from; --i) {
272
- rows.unshift(this.getOrCreateRow(i));
423
+ // Remove rows outside the range from the start.
424
+ while (rows.length && rows[0].index < from) {
425
+ this.poolRow(rows.shift());
273
426
  }
274
- // Add rows after the current range.
275
- for (let i = rows[rows.length - 1].index + 1; i <= to; ++i) {
276
- rows.push(this.getOrCreateRow(i));
427
+ // Remove rows outside the range from the end.
428
+ while (rows.length && rows[rows.length - 1].index > to) {
429
+ this.poolRow(rows.pop());
277
430
  }
431
+ if (!rows.length) {
432
+ for (let i = from; i <= to; ++i) {
433
+ rows.push(await this.getOrCreateRow(i));
434
+ }
435
+ }
436
+ else {
437
+ // Add rows before the current range.
438
+ for (let i = rows[0].index - 1; i >= from; --i) {
439
+ rows.unshift(await this.getOrCreateRow(i));
440
+ }
441
+ // Add rows after the current range.
442
+ const lastRowIndex = rows[rows.length - 1].index + 1;
443
+ for (let i = lastRowIndex; i <= to; ++i) {
444
+ rows.push(await this.getOrCreateRow(i));
445
+ }
446
+ }
447
+ vp.rows = rows;
278
448
  }
279
- vp.rows = rows;
280
- }
281
- for (let i = 0, iEnd = rows.length; i < iEnd; ++i) {
282
- const row = rows[i];
283
- if (!row.rendered) {
284
- vp.tbodyElement.insertBefore(row.htmlElement, vp.tbodyElement.lastChild);
285
- row.render();
286
- continue;
449
+ for (let i = 0, iEnd = rows.length; i < iEnd; ++i) {
450
+ const row = rows[i];
451
+ if (!row.rendered) {
452
+ // Ensure row is initialized before rendering
453
+ if (!row.htmlElement.hasAttribute('data-row-index')) {
454
+ await row.init();
455
+ }
456
+ vp.tbodyElement.insertBefore(row.htmlElement, vp.tbodyElement.lastChild);
457
+ await row.render();
458
+ if (isVirtualization) {
459
+ const topOffset = Math.min(row.getDefaultTopOffset(), this.maxElementHeight -
460
+ row.htmlElement.offsetHeight);
461
+ row.setTranslateY(topOffset);
462
+ }
463
+ continue;
464
+ }
465
+ if (!row.htmlElement.isConnected) {
466
+ vp.tbodyElement.insertBefore(row.htmlElement, vp.tbodyElement.lastChild);
467
+ }
287
468
  }
288
- if (!row.htmlElement.isConnected) {
289
- vp.tbodyElement.insertBefore(row.htmlElement, vp.tbodyElement.lastChild);
469
+ if (!alwaysLastRow && rowCount > 0) {
470
+ alwaysLastRow = await this.getOrCreateRow(rowCount - 1);
290
471
  }
291
- }
292
- if (alwaysLastRow) {
293
- rows.push(alwaysLastRow);
294
- }
295
- // Focus the cell if the focus cursor is set
296
- if (vp.focusCursor) {
297
- const [rowIndex, columnIndex] = vp.focusCursor;
298
- const row = rows.find((row) => row.index === rowIndex);
299
- if (row) {
300
- row.cells[columnIndex]?.htmlElement.focus({
301
- preventScroll: true
302
- });
472
+ if (alwaysLastRow) {
473
+ if (!alwaysLastRow.rendered) {
474
+ if (!alwaysLastRow.htmlElement
475
+ .hasAttribute('data-row-index')) {
476
+ await alwaysLastRow.init();
477
+ }
478
+ vp.tbodyElement.appendChild(alwaysLastRow.htmlElement);
479
+ await alwaysLastRow.render();
480
+ if (isVirtualization) {
481
+ const topOffset = Math.min(alwaysLastRow.getDefaultTopOffset(), this.maxElementHeight -
482
+ alwaysLastRow.htmlElement.offsetHeight);
483
+ alwaysLastRow.setTranslateY(topOffset);
484
+ }
485
+ }
486
+ else if (!alwaysLastRow.htmlElement.isConnected) {
487
+ vp.tbodyElement.appendChild(alwaysLastRow.htmlElement);
488
+ }
489
+ rows.push(alwaysLastRow);
490
+ }
491
+ // Focus the cell if the focus cursor is set
492
+ if (vp.focusCursor) {
493
+ const [rowIndex, columnIndex] = vp.focusCursor;
494
+ const row = rows.find((row) => row.index === rowIndex);
495
+ if (row) {
496
+ row.cells[columnIndex]?.htmlElement.focus({
497
+ preventScroll: true
498
+ });
499
+ }
500
+ }
501
+ // Set the focus anchor cell
502
+ if ((!vp.focusCursor || !vp.focusAnchorCell?.row.rendered) &&
503
+ rows.length > 0) {
504
+ const rowIndex = rowCursor - rows[0].index;
505
+ const targetRow = rows[rowIndex];
506
+ if (targetRow &&
507
+ targetRow.cells.length > 0 &&
508
+ targetRow.cells[0]) {
509
+ vp.setFocusAnchorCell(targetRow.cells[0]);
510
+ }
303
511
  }
304
512
  }
305
- // Set the focus anchor cell
306
- if ((!vp.focusCursor || !vp.focusAnchorCell?.row.rendered) &&
307
- rows.length > 0) {
308
- const rowIndex = rowCursor - rows[0].index;
309
- if (rows[rowIndex]) {
310
- vp.setFocusAnchorCell(rows[rowIndex].cells[0]);
513
+ finally {
514
+ this.isRendering = false;
515
+ // If there's a pending render request, process it
516
+ if (this.pendingRowCursor !== null) {
517
+ const pendingCursor = this.pendingRowCursor;
518
+ this.pendingRowCursor = null;
519
+ await this.renderRows(pendingCursor);
311
520
  }
312
521
  }
313
522
  }
@@ -324,18 +533,27 @@ class RowsVirtualizer {
324
533
  const { rowCursor: cursor, defaultRowHeight: defaultH } = this;
325
534
  const { rows, tbodyElement } = this.viewport;
326
535
  const rowsLn = rows.length;
327
- if (rowsLn < 1) {
536
+ if (rowsLn < 1 || !defaultH) {
328
537
  return;
329
538
  }
330
539
  let translateBuffer = rows[0].getDefaultTopOffset();
331
540
  for (let i = 0; i < rowsLn; ++i) {
332
541
  const row = rows[i];
542
+ // Skip if row is not fully rendered or has no cells
543
+ if (!row.rendered ||
544
+ !row.cells.length ||
545
+ !row.cells[0]?.htmlElement) {
546
+ row.htmlElement.style.height = defaultH + 'px';
547
+ continue;
548
+ }
333
549
  // Reset row height and cell transforms
334
550
  row.htmlElement.style.height = '';
335
551
  if (row.cells[0].htmlElement.style.transform) {
336
552
  for (let j = 0, jEnd = row.cells.length; j < jEnd; ++j) {
337
553
  const cell = row.cells[j];
338
- cell.htmlElement.style.transform = '';
554
+ if (cell?.htmlElement) {
555
+ cell.htmlElement.style.transform = '';
556
+ }
339
557
  }
340
558
  }
341
559
  // Rows above the first visible row
@@ -351,11 +569,13 @@ class RowsVirtualizer {
351
569
  }
352
570
  // First visible row
353
571
  if (row.htmlElement.offsetHeight > defaultH) {
354
- const newHeight = Math.floor(cellHeight - (cellHeight - defaultH) * (tbodyElement.scrollTop / defaultH - cursor));
572
+ const newHeight = Math.floor(cellHeight - (cellHeight - defaultH) * (tbodyElement.scrollTop / defaultH - Math.floor(cursor - this.scrollOffset / defaultH)));
355
573
  row.htmlElement.style.height = newHeight + 'px';
356
574
  for (let j = 0, jEnd = row.cells.length; j < jEnd; ++j) {
357
575
  const cell = row.cells[j];
358
- cell.htmlElement.style.transform = `translateY(${newHeight - cellHeight}px)`;
576
+ if (cell?.htmlElement) {
577
+ cell.htmlElement.style.transform = `translateY(${newHeight - cellHeight}px)`;
578
+ }
359
579
  }
360
580
  }
361
581
  }
@@ -383,6 +603,9 @@ class RowsVirtualizer {
383
603
  rows[i].reflow();
384
604
  }
385
605
  this.adjustRowHeights();
606
+ if (this.viewport.virtualRows) {
607
+ this.adjustRowOffsets();
608
+ }
386
609
  }
387
610
  /**
388
611
  * Gets a row from the pool or creates a new one for the given index.
@@ -393,12 +616,12 @@ class RowsVirtualizer {
393
616
  * @returns
394
617
  * A TableRow instance ready for use.
395
618
  */
396
- getOrCreateRow(index) {
619
+ async getOrCreateRow(index) {
397
620
  const vp = this.viewport;
398
621
  const isVirtualization = vp.virtualRows;
399
622
  const pooledRow = this.rowPool.pop();
400
623
  if (pooledRow) {
401
- pooledRow.reuse(index, false);
624
+ await pooledRow.reuse(index);
402
625
  if (isVirtualization) {
403
626
  pooledRow.setTranslateY(pooledRow.getDefaultTopOffset());
404
627
  }
@@ -406,6 +629,7 @@ class RowsVirtualizer {
406
629
  }
407
630
  const newRow = new TableRow(vp, index);
408
631
  newRow.rendered = false;
632
+ await newRow.init();
409
633
  if (isVirtualization) {
410
634
  newRow.setTranslateY(newRow.getDefaultTopOffset());
411
635
  }
@@ -433,17 +657,72 @@ class RowsVirtualizer {
433
657
  * @returns
434
658
  * The default height of a row.
435
659
  */
436
- getDefaultRowHeight() {
660
+ async getDefaultRowHeight() {
437
661
  const vp = this.viewport;
438
662
  const mockRow = new TableRow(vp, 0);
663
+ await mockRow.init();
439
664
  mockRow.htmlElement.style.position = 'absolute';
440
665
  mockRow.htmlElement.classList.add(Globals.getClassName('mockedRow'));
441
- this.viewport.tbodyElement.appendChild(mockRow.htmlElement);
442
- mockRow.render();
666
+ vp.tbodyElement.appendChild(mockRow.htmlElement);
667
+ await mockRow.render();
443
668
  const defaultRowHeight = mockRow.htmlElement.offsetHeight;
444
669
  mockRow.destroy();
445
670
  return defaultRowHeight;
446
671
  }
672
+ /**
673
+ * Updates cached row count and derived grid height metrics used for
674
+ * overflow-aware scrolling.
675
+ */
676
+ async updateGridMetrics() {
677
+ const rowCount = await this.viewport.grid.dataProvider?.getRowCount();
678
+ if (!defined(rowCount)) {
679
+ return;
680
+ }
681
+ this.rowCount = rowCount;
682
+ this.totalGridHeight = this.rowCount * this.defaultRowHeight;
683
+ this.gridHeightOverflow = Math.max(this.totalGridHeight - this.maxElementHeight, 0);
684
+ }
685
+ /**
686
+ * Updates row translate offsets based on scroll scaling. When the grid
687
+ * exceeds the max element height, it keeps the bottom rows aligned to the
688
+ * maximum scrollable height.
689
+ */
690
+ adjustRowOffsets() {
691
+ const { rows } = this.viewport;
692
+ const rowsLn = rows.length;
693
+ if (rowsLn < 2) {
694
+ return;
695
+ }
696
+ const lastRow = rows[rowsLn - 1];
697
+ const preLastRow = rows[rowsLn - 2];
698
+ const isSecondToLastRowVisible = preLastRow &&
699
+ preLastRow.index === lastRow.index - 1;
700
+ let translateBuffer = rows[0].getDefaultTopOffset();
701
+ translateBuffer = Math.floor(translateBuffer - this.scrollOffset);
702
+ if (isSecondToLastRowVisible && this.gridHeightOverflow > 0) {
703
+ lastRow.setTranslateY(this.maxElementHeight -
704
+ lastRow.htmlElement.offsetHeight);
705
+ let bottomOffset = this.maxElementHeight -
706
+ lastRow.htmlElement.offsetHeight;
707
+ for (let i = rowsLn - 2; i >= 0; i--) {
708
+ bottomOffset -= rows[i].htmlElement.offsetHeight;
709
+ rows[i].setTranslateY(bottomOffset);
710
+ }
711
+ return;
712
+ }
713
+ rows[0].setTranslateY(translateBuffer);
714
+ for (let i = 1, iEnd = rowsLn - 1; i < iEnd; ++i) {
715
+ translateBuffer += rows[i - 1].htmlElement.offsetHeight;
716
+ rows[i].setTranslateY(translateBuffer);
717
+ }
718
+ if (this.gridHeightOverflow > 0) {
719
+ lastRow.setTranslateY(this.maxElementHeight);
720
+ return;
721
+ }
722
+ if (preLastRow && preLastRow.index === lastRow.index - 1) {
723
+ lastRow.setTranslateY(preLastRow.htmlElement.offsetHeight + translateBuffer);
724
+ }
725
+ }
447
726
  }
448
727
  /**
449
728
  * Maximum number of rows to keep in the reuse pool.