virtual-scroller 1.14.0 → 1.15.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 (216) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +309 -250
  3. package/bundle/index-dom-bypass.html +198 -0
  4. package/bundle/index-dom-grid.html +204 -0
  5. package/bundle/index-dom-scrollableContainer.html +215 -0
  6. package/bundle/index-dom-tbody-scrollableContainer.html +81 -0
  7. package/bundle/index-dom-tbody.html +65 -0
  8. package/bundle/index-dom.html +115 -84
  9. package/bundle/{index-bypass.html → index-react-bypass.html} +83 -78
  10. package/bundle/{index-grid.html → index-react-grid.html} +78 -91
  11. package/bundle/{index-scrollableContainer.html → index-react-scrollableContainer.html} +96 -91
  12. package/bundle/index-react-strictMode.html +199 -0
  13. package/bundle/index-react-tbody-scrollableContainer.html +96 -0
  14. package/bundle/index-react-tbody.html +80 -0
  15. package/bundle/{messages.js → items.js} +1 -1
  16. package/bundle/lib/babel.min.js +25 -0
  17. package/bundle/lib/prop-types.min.js +1 -0
  18. package/bundle/lib/react-dom.development.js +29924 -0
  19. package/bundle/lib/react-dom.production.min.js +267 -0
  20. package/bundle/lib/react.development.js +3343 -0
  21. package/bundle/lib/react.production.min.js +31 -0
  22. package/bundle/style.base.css +33 -0
  23. package/{website/style.css → bundle/style.list.css} +10 -43
  24. package/bundle/style.list.grid.css +23 -0
  25. package/bundle/virtual-scroller-dom.js +1 -1
  26. package/bundle/virtual-scroller-dom.js.map +1 -1
  27. package/bundle/virtual-scroller-react.js +1 -1
  28. package/bundle/virtual-scroller-react.js.map +1 -1
  29. package/bundle/virtual-scroller.js +1 -1
  30. package/bundle/virtual-scroller.js.map +1 -1
  31. package/commonjs/BeforeResize.js +1 -2
  32. package/commonjs/BeforeResize.js.map +1 -1
  33. package/commonjs/DOM/VirtualScroller.js +7 -13
  34. package/commonjs/DOM/VirtualScroller.js.map +1 -1
  35. package/commonjs/DOM/tbody.js +6 -6
  36. package/commonjs/DOM/tbody.js.map +1 -1
  37. package/commonjs/ItemHeights.js +10 -13
  38. package/commonjs/ItemHeights.js.map +1 -1
  39. package/commonjs/Layout.defaults.js +21 -0
  40. package/commonjs/Layout.defaults.js.map +1 -0
  41. package/commonjs/Layout.js +75 -64
  42. package/commonjs/Layout.js.map +1 -1
  43. package/commonjs/Layout.test.js +8 -4
  44. package/commonjs/Layout.test.js.map +1 -1
  45. package/commonjs/VirtualScroller.constructor.js +38 -4
  46. package/commonjs/VirtualScroller.constructor.js.map +1 -1
  47. package/commonjs/VirtualScroller.items.js +50 -4
  48. package/commonjs/VirtualScroller.items.js.map +1 -1
  49. package/commonjs/VirtualScroller.js +23 -14
  50. package/commonjs/VirtualScroller.js.map +1 -1
  51. package/commonjs/VirtualScroller.layout.js +40 -29
  52. package/commonjs/VirtualScroller.layout.js.map +1 -1
  53. package/commonjs/VirtualScroller.onContainerResize.js +1 -2
  54. package/commonjs/VirtualScroller.onContainerResize.js.map +1 -1
  55. package/commonjs/VirtualScroller.state.js +10 -9
  56. package/commonjs/VirtualScroller.state.js.map +1 -1
  57. package/commonjs/VirtualScroller.verticalSpacing.js +39 -6
  58. package/commonjs/VirtualScroller.verticalSpacing.js.map +1 -1
  59. package/commonjs/react/VirtualScroller.js +85 -34
  60. package/commonjs/react/VirtualScroller.js.map +1 -1
  61. package/commonjs/react/useClassName.js +2 -2
  62. package/commonjs/react/useClassName.js.map +1 -1
  63. package/commonjs/react/useForwardedRef.js +50 -0
  64. package/commonjs/react/useForwardedRef.js.map +1 -0
  65. package/commonjs/react/useInstanceMethods.js +4 -4
  66. package/commonjs/react/useInstanceMethods.js.map +1 -1
  67. package/commonjs/react/useItemKeys.js +28 -5
  68. package/commonjs/react/useItemKeys.js.map +1 -1
  69. package/commonjs/react/useOnItemHeightDidChange.js +28 -12
  70. package/commonjs/react/useOnItemHeightDidChange.js.map +1 -1
  71. package/commonjs/react/useSetItemState.js +31 -12
  72. package/commonjs/react/useSetItemState.js.map +1 -1
  73. package/commonjs/react/useState.js +9 -9
  74. package/commonjs/react/useState.js.map +1 -1
  75. package/commonjs/react/{useStateNoStaleBug.js → useStateWithRepeatableRead.js} +3 -3
  76. package/commonjs/react/useStateWithRepeatableRead.js.map +1 -0
  77. package/commonjs/react/useStyle.js +10 -4
  78. package/commonjs/react/useStyle.js.map +1 -1
  79. package/commonjs/react/useValidateTableBodyItemsContainer.js +24 -0
  80. package/commonjs/react/useValidateTableBodyItemsContainer.js.map +1 -0
  81. package/commonjs/react/useVirtualScroller.js +4 -3
  82. package/commonjs/react/useVirtualScroller.js.map +1 -1
  83. package/commonjs/test/ItemsContainer.js +10 -10
  84. package/commonjs/test/ItemsContainer.js.map +1 -1
  85. package/commonjs/test/VirtualScroller.js +25 -10
  86. package/commonjs/test/VirtualScroller.js.map +1 -1
  87. package/dom/index.d.ts +6 -5
  88. package/index.d.ts +19 -8
  89. package/modules/BeforeResize.js +1 -2
  90. package/modules/BeforeResize.js.map +1 -1
  91. package/modules/DOM/VirtualScroller.js +7 -13
  92. package/modules/DOM/VirtualScroller.js.map +1 -1
  93. package/modules/DOM/tbody.js +4 -4
  94. package/modules/DOM/tbody.js.map +1 -1
  95. package/modules/ItemHeights.js +11 -14
  96. package/modules/ItemHeights.js.map +1 -1
  97. package/modules/Layout.defaults.js +11 -0
  98. package/modules/Layout.defaults.js.map +1 -0
  99. package/modules/Layout.js +74 -64
  100. package/modules/Layout.js.map +1 -1
  101. package/modules/Layout.test.js +8 -4
  102. package/modules/Layout.test.js.map +1 -1
  103. package/modules/VirtualScroller.constructor.js +37 -4
  104. package/modules/VirtualScroller.constructor.js.map +1 -1
  105. package/modules/VirtualScroller.items.js +51 -5
  106. package/modules/VirtualScroller.items.js.map +1 -1
  107. package/modules/VirtualScroller.js +23 -14
  108. package/modules/VirtualScroller.js.map +1 -1
  109. package/modules/VirtualScroller.layout.js +40 -29
  110. package/modules/VirtualScroller.layout.js.map +1 -1
  111. package/modules/VirtualScroller.onContainerResize.js +1 -2
  112. package/modules/VirtualScroller.onContainerResize.js.map +1 -1
  113. package/modules/VirtualScroller.state.js +10 -9
  114. package/modules/VirtualScroller.state.js.map +1 -1
  115. package/modules/VirtualScroller.verticalSpacing.js +38 -6
  116. package/modules/VirtualScroller.verticalSpacing.js.map +1 -1
  117. package/modules/react/VirtualScroller.js +84 -35
  118. package/modules/react/VirtualScroller.js.map +1 -1
  119. package/modules/react/useClassName.js +3 -3
  120. package/modules/react/useClassName.js.map +1 -1
  121. package/modules/react/useForwardedRef.js +42 -0
  122. package/modules/react/useForwardedRef.js.map +1 -0
  123. package/modules/react/useInstanceMethods.js +4 -4
  124. package/modules/react/useInstanceMethods.js.map +1 -1
  125. package/modules/react/useItemKeys.js +28 -5
  126. package/modules/react/useItemKeys.js.map +1 -1
  127. package/modules/react/useOnItemHeightDidChange.js +28 -12
  128. package/modules/react/useOnItemHeightDidChange.js.map +1 -1
  129. package/modules/react/useSetItemState.js +31 -12
  130. package/modules/react/useSetItemState.js.map +1 -1
  131. package/modules/react/useState.js +9 -9
  132. package/modules/react/useState.js.map +1 -1
  133. package/modules/react/{useStateNoStaleBug.js → useStateWithRepeatableRead.js} +2 -2
  134. package/modules/react/useStateWithRepeatableRead.js.map +1 -0
  135. package/modules/react/useStyle.js +10 -4
  136. package/modules/react/useStyle.js.map +1 -1
  137. package/modules/react/useValidateTableBodyItemsContainer.js +16 -0
  138. package/modules/react/useValidateTableBodyItemsContainer.js.map +1 -0
  139. package/modules/react/useVirtualScroller.js +4 -3
  140. package/modules/react/useVirtualScroller.js.map +1 -1
  141. package/modules/test/ItemsContainer.js +10 -10
  142. package/modules/test/ItemsContainer.js.map +1 -1
  143. package/modules/test/VirtualScroller.js +25 -10
  144. package/modules/test/VirtualScroller.js.map +1 -1
  145. package/package.json +1 -1
  146. package/react/as.d.ts +42 -0
  147. package/react/index.d.ts +204 -63
  148. package/source/BeforeResize.js +1 -2
  149. package/source/DOM/VirtualScroller.js +7 -13
  150. package/source/DOM/tbody.js +5 -5
  151. package/source/ItemHeights.js +11 -12
  152. package/source/Layout.defaults.js +8 -0
  153. package/source/Layout.js +66 -53
  154. package/source/Layout.test.js +7 -2
  155. package/source/VirtualScroller.constructor.js +27 -4
  156. package/source/VirtualScroller.items.js +47 -2
  157. package/source/VirtualScroller.js +23 -14
  158. package/source/VirtualScroller.layout.js +41 -28
  159. package/source/VirtualScroller.onContainerResize.js +1 -2
  160. package/source/VirtualScroller.state.js +10 -11
  161. package/source/VirtualScroller.verticalSpacing.js +32 -6
  162. package/source/react/VirtualScroller.js +96 -33
  163. package/source/react/useClassName.js +3 -3
  164. package/source/react/useForwardedRef.js +39 -0
  165. package/source/react/useInstanceMethods.js +12 -4
  166. package/source/react/useItemKeys.js +22 -4
  167. package/source/react/useOnItemHeightDidChange.js +29 -10
  168. package/source/react/useSetItemState.js +32 -10
  169. package/source/react/useState.js +6 -6
  170. package/source/react/{useStateNoStaleBug.js → useStateWithRepeatableRead.js} +1 -1
  171. package/source/react/useStyle.js +3 -2
  172. package/source/react/useValidateTableBodyItemsContainer.js +16 -0
  173. package/source/react/useVirtualScroller.js +4 -3
  174. package/source/test/ItemsContainer.js +10 -10
  175. package/source/test/VirtualScroller.js +16 -10
  176. package/website/index-dom-bypass.html +198 -0
  177. package/website/index-dom-grid.html +204 -0
  178. package/website/index-dom-scrollableContainer.html +215 -0
  179. package/website/index-dom-tbody-scrollableContainer.html +81 -0
  180. package/website/index-dom-tbody.html +65 -0
  181. package/website/index-dom.html +117 -84
  182. package/website/index-react-bypass.html +200 -0
  183. package/website/{index-grid.html → index-react-grid.html} +79 -92
  184. package/website/index-react-scrollableContainer.html +213 -0
  185. package/website/index-react-strictMode.html +199 -0
  186. package/website/index-react-tbody-scrollableContainer.html +96 -0
  187. package/website/index-react-tbody.html +80 -0
  188. package/website/{index-bypass.html → index-react.html} +84 -70
  189. package/website/index.html +84 -69
  190. package/website/{messages.js → items.js} +1 -1
  191. package/website/lib/babel.min.js +25 -0
  192. package/website/lib/prop-types.min.js +1 -0
  193. package/website/lib/react-dom.development.js +29924 -0
  194. package/website/lib/react-dom.production.min.js +267 -0
  195. package/website/lib/react.development.js +3343 -0
  196. package/website/lib/react.production.min.js +31 -0
  197. package/website/style.base.css +33 -0
  198. package/website/style.list.css +92 -0
  199. package/website/style.list.grid.css +23 -0
  200. package/bundle/index-tbody-scrollableContainer.html +0 -70
  201. package/bundle/index-tbody.html +0 -57
  202. package/bundle/on-scroll-to-dom.js +0 -2
  203. package/bundle/on-scroll-to-dom.js.map +0 -1
  204. package/bundle/on-scroll-to-react.js +0 -2
  205. package/bundle/on-scroll-to-react.js.map +0 -1
  206. package/bundle/on-scroll-to.js +0 -2
  207. package/bundle/on-scroll-to.js.map +0 -1
  208. package/commonjs/react/useStateNoStaleBug.js.map +0 -1
  209. package/modules/react/useStateNoStaleBug.js.map +0 -1
  210. package/website/index-scrollableContainer.html +0 -208
  211. package/website/index-tbody-scrollableContainer.html +0 -70
  212. package/website/index-tbody.html +0 -57
  213. package/website/lib/on-scroll-to-dom.js +0 -2
  214. package/website/lib/on-scroll-to-dom.js.map +0 -1
  215. package/website/lib/on-scroll-to-react.js +0 -2
  216. package/website/lib/on-scroll-to-react.js.map +0 -1
@@ -4,7 +4,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
4
4
 
5
5
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
6
 
7
- import log, { isDebug } from './utility/debug.js';
7
+ import log, { reportError } from './utility/debug.js';
8
8
  import getItemsDiff from './getItemsDiff.js';
9
9
  import fillArray from './utility/fillArray.js';
10
10
  export default function () {
@@ -13,6 +13,52 @@ export default function () {
13
13
  this.getItemsCount = function () {
14
14
  return _this.getState().items.length;
15
15
  };
16
+
17
+ this._getItemIndexByItemOrIndex = function (itemOrIndex) {
18
+ var item = itemOrIndex;
19
+
20
+ var _this$getState = _this.getState(),
21
+ items = _this$getState.items; // Find the `item`'s index in the `items` array.
22
+ //
23
+ // Performance notes:
24
+ // Doing an `.indexOf()` operation on a very large array could be inefficient
25
+ // because it would have to check every element of the array.
26
+ // In case of any hypothetical performance-related issues,
27
+ // the code could use a `new Map()` as an "index" for finding individual items.
28
+
29
+
30
+ var i = items.indexOf(item); // validate that the `item` exists in the `items` array.
31
+
32
+ if (i >= 0) {
33
+ return i;
34
+ } // Legacy behavior: some functions used to accept an `i: number` item index as an argument.
35
+ // Later the argument was changed to `item: Item`.
36
+ //
37
+ // The old way of passing an `i: number` argument would only result in weird application behavior
38
+ // when all of the below circumstances are true, which is assumed extremely unlikely:
39
+ //
40
+ // BOTH use a previous version of `virtual-scroller` (ones before December 2025, with downloads count that is not very high)
41
+ // AND to be used with an `items` array of type `number[]`
42
+ // AND to use any of the "advanced" functions:
43
+ // * `setItemState(i, newState)`
44
+ // * `onItemHeightDidChange(i)`
45
+ // * `getItemScrollPosition(i)`
46
+ //
47
+ // The code below handles the legacy compatibility aspect of the old type of argument
48
+ // except maybe for those "extremely unlikely" cases in which the app is still unlikely to crash.
49
+ //
50
+
51
+
52
+ if (typeof itemOrIndex === 'number') {
53
+ var _i = itemOrIndex; // Validate the item index argument.
54
+
55
+ if (_i >= 0 && _i < items.length) {
56
+ return _i;
57
+ }
58
+ }
59
+
60
+ reportError("Item not found: ".concat(JSON.stringify(item)));
61
+ };
16
62
  /**
17
63
  * Updates `items`. For example, can prepend or append new items to the list.
18
64
  * @param {any[]} newItems
@@ -23,8 +69,8 @@ export default function () {
23
69
  this._setItems = function (newItems) {
24
70
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
25
71
 
26
- var _this$getState = _this.getState(),
27
- previousItems = _this$getState.items; // Even if `newItems` are equal to `this.state.items`,
72
+ var _this$getState2 = _this.getState(),
73
+ previousItems = _this$getState2.items; // Even if `newItems` are equal to `this.state.items`,
28
74
  // still perform a `updateState()` call, because, if `updateState()` calls
29
75
  // were "asynchronous", there could be a situation when a developer
30
76
  // first calls `setItems(newItems)` and then `setItems(oldItems)`:
@@ -35,8 +81,8 @@ export default function () {
35
81
  // and that wouldn't be what the developer intended.
36
82
 
37
83
 
38
- var _this$getState2 = _this.getState(),
39
- itemStates = _this$getState2.itemStates;
84
+ var _this$getState3 = _this.getState(),
85
+ itemStates = _this$getState3.itemStates;
40
86
 
41
87
  var _ref = _this.widthHasChanged ? _this.widthHasChanged.stateUpdate : _this.getState(),
42
88
  itemHeights = _ref.itemHeights;
@@ -1 +1 @@
1
- {"version":3,"file":"VirtualScroller.items.js","names":["log","isDebug","getItemsDiff","fillArray","getItemsCount","getState","items","length","_setItems","newItems","options","previousItems","itemStates","widthHasChanged","stateUpdate","itemHeights","layoutUpdate","itemsUpdateInfo","itemsDiff","firstShownItemIndex","lastShownItemIndex","beforeItemsHeight","afterItemsHeight","shouldRestoreScrollPosition","preserveScrollPositionOnPrependItems","preserveScrollPosition","prependedItemsCount","appendedItemsCount","shouldResetGridLayout","layout","getLayoutUpdateForItemsDiff","itemsCount","columnsCount","getActualColumnsCount","onResetGridLayout","Array","concat","i","getInitialItemState","listHeightMeasurement","snapshotListHeightBeforeAddingNewItems","firstNonMeasuredItemIndex","undefined","getInitialLayoutValues","prepend","append","reset","replace","onBeforeShowItems","newItemsWillBeRendered","count","newState","beforeResize","shouldIncludeBeforeResizeValuesInState","shouldDiscardBeforeResizeItemHeights","_isSettingNewItems","updateState","isItemEqual"],"sources":["../source/VirtualScroller.items.js"],"sourcesContent":["import log, { isDebug } from './utility/debug.js'\r\nimport getItemsDiff from './getItemsDiff.js'\r\nimport fillArray from './utility/fillArray.js'\r\n\r\nexport default function() {\r\n\tthis.getItemsCount = () => {\r\n\t\treturn this.getState().items.length\r\n\t}\r\n\r\n\t/**\r\n\t * Updates `items`. For example, can prepend or append new items to the list.\r\n\t * @param {any[]} newItems\r\n\t * @param {boolean} [options.preserveScrollPositionOnPrependItems] — Set to `true` to enable \"restore scroll position after prepending items\" feature (could be useful when implementing \"Show previous items\" button).\r\n\t */\r\n\tthis._setItems = (newItems, options = {}) => {\r\n\t\tconst {\r\n\t\t\titems: previousItems\r\n\t\t} = this.getState()\r\n\r\n\t\t// Even if `newItems` are equal to `this.state.items`,\r\n\t\t// still perform a `updateState()` call, because, if `updateState()` calls\r\n\t\t// were \"asynchronous\", there could be a situation when a developer\r\n\t\t// first calls `setItems(newItems)` and then `setItems(oldItems)`:\r\n\t\t// if this function did `return` `if (newItems === this.state.items)`\r\n\t\t// then `updateState({ items: newItems })` would be scheduled as part of\r\n\t\t// `setItems(newItems)` call, but the subsequent `setItems(oldItems)` call\r\n\t\t// wouldn't do anything resulting in `newItems` being set as a result,\r\n\t\t// and that wouldn't be what the developer intended.\r\n\r\n\t\tlet { itemStates } = this.getState()\r\n\t\tlet { itemHeights } = this.widthHasChanged\r\n\t\t\t? this.widthHasChanged.stateUpdate\r\n\t\t\t: this.getState()\r\n\r\n\t\tlog('~ Update items ~')\r\n\r\n\t\tlet layoutUpdate\r\n\t\tlet itemsUpdateInfo\r\n\r\n\t\t// Compare the new items to the current items.\r\n\t\tconst itemsDiff = this.getItemsDiff(previousItems, newItems)\r\n\r\n\t\t// See if it's an \"incremental\" items update.\r\n\t\tif (itemsDiff) {\r\n\t\t\tconst {\r\n\t\t\t\tfirstShownItemIndex,\r\n\t\t\t\tlastShownItemIndex,\r\n\t\t\t\tbeforeItemsHeight,\r\n\t\t\t\tafterItemsHeight\r\n\t\t\t} = this.widthHasChanged\r\n\t\t\t\t? this.widthHasChanged.stateUpdate\r\n\t\t\t\t: this.getState()\r\n\r\n\t\t\tconst shouldRestoreScrollPosition = firstShownItemIndex === 0 &&\r\n\t\t\t\t// `preserveScrollPosition` option name is deprecated,\r\n\t\t\t\t// use `preserveScrollPositionOnPrependItems` instead.\r\n\t\t\t\t(options.preserveScrollPositionOnPrependItems || options.preserveScrollPosition)\r\n\r\n\t\t\tconst {\r\n\t\t\t\tprependedItemsCount,\r\n\t\t\t\tappendedItemsCount\r\n\t\t\t} = itemsDiff\r\n\r\n\t\t\tlet shouldResetGridLayout\r\n\r\n\t\t\tlayoutUpdate = this.layout.getLayoutUpdateForItemsDiff({\r\n\t\t\t\tfirstShownItemIndex,\r\n\t\t\t\tlastShownItemIndex,\r\n\t\t\t\tbeforeItemsHeight,\r\n\t\t\t\tafterItemsHeight\r\n\t\t\t}, {\r\n\t\t\t\tprependedItemsCount,\r\n\t\t\t\tappendedItemsCount\r\n\t\t\t}, {\r\n\t\t\t\titemsCount: newItems.length,\r\n\t\t\t\tcolumnsCount: this.getActualColumnsCount(),\r\n\t\t\t\tshouldRestoreScrollPosition,\r\n\t\t\t\tonResetGridLayout: () => shouldResetGridLayout = true\r\n\t\t\t})\r\n\r\n\t\t\tif (prependedItemsCount > 0) {\r\n\t\t\t\tlog('Prepend', prependedItemsCount, 'items')\r\n\r\n\t\t\t\titemHeights = new Array(prependedItemsCount).concat(itemHeights)\r\n\t\t\t\titemStates = fillArray(\r\n\t\t\t\t\tnew Array(prependedItemsCount),\r\n\t\t\t\t\t(i) => this.getInitialItemState(newItems[i])\r\n\t\t\t\t)\r\n\t\t\t\t\t.concat(itemStates)\r\n\r\n\t\t\t\t// Restore scroll position after prepending items (if requested).\r\n\t\t\t\tif (shouldRestoreScrollPosition) {\r\n\t\t\t\t\tlog('Will restore scroll position')\r\n\t\t\t\t\tthis.listHeightMeasurement.snapshotListHeightBeforeAddingNewItems({\r\n\t\t\t\t\t\tpreviousItems,\r\n\t\t\t\t\t\tnewItems,\r\n\t\t\t\t\t\tprependedItemsCount\r\n\t\t\t\t\t})\r\n\t\t\t\t\t// \"Seamless prepend\" scenario doesn't result in a re-layout,\r\n\t\t\t\t\t// so if any \"non measured item\" is currently pending,\r\n\t\t\t\t\t// it doesn't get reset and will be handled after `state` is updated.\r\n\t\t\t\t\tif (this.firstNonMeasuredItemIndex !== undefined) {\r\n\t\t\t\t\t\tthis.firstNonMeasuredItemIndex += prependedItemsCount\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlog('Reset layout')\r\n\t\t\t\t\tif (shouldResetGridLayout) {\r\n\t\t\t\t\t\tlog('Reason: Prepended items count', prependedItemsCount, 'is not divisible by Columns Count', this.getActualColumnsCount())\r\n\t\t\t\t\t\t// Reset item heights because the whole grid is going to be rebalanced\r\n\t\t\t\t\t\t// and re-rendered in a different configuration.\r\n\t\t\t\t\t\titemHeights = new Array(newItems.length)\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\t// Reset layout because none of the prepended items have been measured.\r\n\t\t\t\t\t\tlog('Reason: Prepended items\\' heights are unknown')\r\n\t\t\t\t\t}\r\n\t\t\t\t\tlayoutUpdate = this.layout.getInitialLayoutValues({\r\n\t\t\t\t\t\titemsCount: newItems.length,\r\n\t\t\t\t\t\tcolumnsCount: this.getActualColumnsCount()\r\n\t\t\t\t\t})\r\n\t\t\t\t\t// Unschedule a potentially scheduled layout update\r\n\t\t\t\t\t// after measuring a previously non-measured item\r\n\t\t\t\t\t// because the list will be re-layout anyway\r\n\t\t\t\t\t// due to the new items being set.\r\n\t\t\t\t\tthis.firstNonMeasuredItemIndex = undefined\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (appendedItemsCount > 0) {\r\n\t\t\t\tlog('Append', appendedItemsCount, 'items')\r\n\t\t\t\titemHeights = itemHeights.concat(new Array(appendedItemsCount))\r\n\t\t\t\titemStates = itemStates.concat(\r\n\t\t\t\t\tfillArray(\r\n\t\t\t\t\t\tnew Array(appendedItemsCount),\r\n\t\t\t\t\t\t(i) => this.getInitialItemState(newItems[prependedItemsCount + previousItems.length + i])\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t}\r\n\r\n\t\t\titemsUpdateInfo = {\r\n\t\t\t\tprepend: prependedItemsCount > 0,\r\n\t\t\t\tappend: appendedItemsCount > 0\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tlog('Items have changed, and', (itemsDiff ? 'a re-layout from scratch has been requested.' : 'it\\'s not a simple append and/or prepend.'), 'Rerender the entire list from scratch.')\r\n\t\t\tlog('Previous items', previousItems)\r\n\t\t\tlog('New items', newItems)\r\n\r\n\t\t\t// Reset item heights and item states.\r\n\t\t\titemHeights = new Array(newItems.length)\r\n\t\t\titemStates = fillArray(\r\n\t\t\t\tnew Array(newItems.length),\r\n\t\t\t\t(i) => this.getInitialItemState(newItems[i])\r\n\t\t\t)\r\n\r\n\t\t\tlayoutUpdate = this.layout.getInitialLayoutValues({\r\n\t\t\t\titemsCount: newItems.length,\r\n\t\t\t\tcolumnsCount: this.getActualColumnsCount()\r\n\t\t\t})\r\n\r\n\t\t\t// Unschedule a potentially scheduled layout update\r\n\t\t\t// after measuring a previously non-measured item\r\n\t\t\t// because the list will be re-layout from scratch\r\n\t\t\t// due to the new items being set.\r\n\t\t\tthis.firstNonMeasuredItemIndex = undefined\r\n\r\n\t\t\t// Also reset any potential pending scroll position restoration.\r\n\t\t\t// For example, imagine a developer first called `.setItems(incrementalItemsUpdate)`\r\n\t\t\t// and then called `.setItems(differentItems)` and there was no state update\r\n\t\t\t// in between those two calls. This could happen because state updates aren't\r\n\t\t\t// required to be \"synchronous\". On other words, calling `this.updateState()`\r\n\t\t\t// doesn't necessarily mean that the state is applied immediately.\r\n\t\t\t// Imagine also that such \"delayed\" state updates could be batched,\r\n\t\t\t// like they do in React inside event handlers (though that doesn't apply to this case):\r\n\t\t\t// https://github.com/facebook/react/issues/10231#issuecomment-316644950\r\n\t\t\t// If `this.listHeightMeasurement` wasn't reset on `.setItems(differentItems)`\r\n\t\t\t// and if the second `this.updateState()` call overwrites the first one\r\n\t\t\t// then it would attempt to restore scroll position in a situation when\r\n\t\t\t// it should no longer do that. Hence the reset here.\r\n\t\t\tthis.listHeightMeasurement.reset()\r\n\r\n\t\t\titemsUpdateInfo = {\r\n\t\t\t\treplace: true\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tlog('~ Update state ~')\r\n\r\n\t\t// const layoutValuesAfterUpdate = {\r\n\t\t// \t...this.getState(),\r\n\t\t// \t...layoutUpdate\r\n\t\t// }\r\n\r\n\t\t// `layoutUpdate` is equivalent to `layoutValuesAfterUpdate` because\r\n\t\t// `layoutUpdate` contains all the relevant properties.\r\n\t\tlog('First shown item index', layoutUpdate.firstShownItemIndex)\r\n\t\tlog('Last shown item index', layoutUpdate.lastShownItemIndex)\r\n\t\tlog('Before items height', layoutUpdate.beforeItemsHeight)\r\n\t\tlog('After items height (actual or estimated)', layoutUpdate.afterItemsHeight)\r\n\r\n\t\t// Optionally preload items to be rendered.\r\n\t\t//\r\n\t\t// `layoutUpdate` is equivalent to `layoutValuesAfterUpdate` because\r\n\t\t// `layoutUpdate` contains all the relevant properties.\r\n\t\t//\r\n\t\tthis.onBeforeShowItems(\r\n\t\t\tnewItems,\r\n\t\t\titemHeights,\r\n\t\t\tlayoutUpdate.firstShownItemIndex,\r\n\t\t\tlayoutUpdate.lastShownItemIndex\r\n\t\t)\r\n\r\n\t\t// `this.newItemsWillBeRendered` signals that new `items` are being rendered,\r\n\t\t// and that `VirtualScroller` should temporarily stop all other updates.\r\n\t\t//\r\n\t\t// `this.newItemsWillBeRendered` is cleared in `onRender()`.\r\n\t\t//\r\n\t\t// The values in `this.newItemsWillBeRendered` are used, for example,\r\n\t\t// in `.onContainerResize()` handler in order to not break state consistency when\r\n\t\t// state updates are \"asynchronous\" (delayed) and there's a window resize event\r\n\t\t// in between calling `updateState()` below and that call actually being applied.\r\n\t\t//\r\n\t\tthis.newItemsWillBeRendered = {\r\n\t\t\t...itemsUpdateInfo,\r\n\t\t\tcount: newItems.length,\r\n\t\t\t// `layoutUpdate` now contains all layout-related properties, even if those that\r\n\t\t\t// didn't change. So `firstShownItemIndex` is always in `this.newItemsWillBeRendered`.\r\n\t\t\tlayout: layoutUpdate\r\n\t\t}\r\n\r\n\t\t// `layoutUpdate` now contains all layout-related properties, even if those that\r\n\t\t// didn't change. So this part is no longer relevant.\r\n\t\t//\r\n\t\t// // If `firstShownItemIndex` is gonna be modified as a result of setting new items\r\n\t\t// // then keep that \"new\" `firstShownItemIndex` in order for it to be used by\r\n\t\t// // `onResize()` handler when it calculates \"new\" `firstShownItemIndex`\r\n\t\t// // based on the new columns count (corresponding to the new window width).\r\n\t\t// if (layoutUpdate.firstShownItemIndex !== undefined) {\r\n\t\t// \tthis.newItemsWillBeRendered = {\r\n\t\t// \t\t...this.newItemsWillBeRendered,\r\n\t\t// \t\tfirstShownItemIndex: layoutUpdate.firstShownItemIndex\r\n\t\t// \t}\r\n\t\t// }\r\n\r\n\t\t// Update `VirtualScroller` state.\r\n\t\t//\r\n\t\t// This state update should overwrite all the `state` properties\r\n\t\t// that are also updated in the \"on scroll\" handler (`getShownItemIndexes()`):\r\n\t\t//\r\n\t\t// * `firstShownItemIndex`\r\n\t\t// * `lastShownItemIndex`\r\n\t\t// * `beforeItemsHeight`\r\n\t\t// * `afterItemsHeight`\r\n\t\t//\r\n\t\t// That's because this `updateState()` update has a higher priority\r\n\t\t// than that of the \"on scroll\" handler, so it should overwrite\r\n\t\t// any potential state changes dispatched by the \"on scroll\" handler.\r\n\t\t//\r\n\t\tconst newState = {\r\n\t\t\t...layoutUpdate,\r\n\t\t\titems: newItems,\r\n\t\t\titemStates,\r\n\t\t\titemHeights\r\n\t\t}\r\n\r\n\t\t// Introduced `shouldIncludeBeforeResizeValuesInState()` getter just to prevent\r\n\t\t// cluttering `state` with `beforeResize: undefined` property if `beforeResize`\r\n\t\t// hasn't ever been set in `state` previously.\r\n\t\tif (this.beforeResize.shouldIncludeBeforeResizeValuesInState()) {\r\n\t\t\tif (this.shouldDiscardBeforeResizeItemHeights()) {\r\n\t\t\t\t// Reset \"before resize\" item heights because now there're new items prepended\r\n\t\t\t\t// with unknown heights, or completely new items with unknown heights, so\r\n\t\t\t\t// `beforeItemsHeight` value won't be preserved anyway.\r\n\t\t\t\tnewState.beforeResize = undefined\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\t// Overwrite `beforeResize` property in `state` even if it wasn't modified\r\n\t\t\t\t// because state updates could be \"asynchronous\" and in that case there could be\r\n\t\t\t\t// some previous `updateState()` call from some previous `setItems()` call that\r\n\t\t\t\t// hasn't yet been applied, and that previous call might have scheduled setting\r\n\t\t\t\t// `state.beforeResize` property to `undefined` in order to reset it, but this\r\n\t\t\t\t// next `updateState()` call might not require resetting `state.beforeResize` property\r\n\t\t\t\t// so it should undo resetting it by simply overwriting it with its normal value.\r\n\t\t\t\tnewState.beforeResize = this.widthHasChanged\r\n\t\t\t\t\t? this.widthHasChanged.stateUpdate.beforeResize\r\n\t\t\t\t\t: this.getState().beforeResize\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// `newState` should also overwrite all `state` properties that're updated in `onResize()`\r\n\t\t// because `setItems()`'s state updates always overwrite `onResize()`'s state updates.\r\n\t\t// (The least-priority ones are `onScroll()` state updates, but those're simply skipped\r\n\t\t// if there's a pending `setItems()` or `onResize()` update).\r\n\t\t//\r\n\t\t// `state` property exceptions:\r\n\t\t//\r\n\t\t// `verticalSpacing` property is not updated here because it's fine setting it to\r\n\t\t// `undefined` in `onResize()` — it will simply be re-measured after the component re-renders.\r\n\t\t//\r\n\t\t// `columnsCount` property is also not updated here because by definition it's only\r\n\t\t// updated in `onResize()`.\r\n\r\n\t\t// Render.\r\n\t\tthis._isSettingNewItems = true\r\n\t\tthis.updateState(newState)\r\n\t}\r\n\r\n\tthis.getItemsDiff = (previousItems, newItems) => {\r\n\t\treturn getItemsDiff(previousItems, newItems, this.isItemEqual)\r\n\t}\r\n}"],"mappings":";;;;;;AAAA,OAAOA,GAAP,IAAcC,OAAd,QAA6B,oBAA7B;AACA,OAAOC,YAAP,MAAyB,mBAAzB;AACA,OAAOC,SAAP,MAAsB,wBAAtB;AAEA,eAAe,YAAW;EAAA;;EACzB,KAAKC,aAAL,GAAqB,YAAM;IAC1B,OAAO,KAAI,CAACC,QAAL,GAAgBC,KAAhB,CAAsBC,MAA7B;EACA,CAFD;EAIA;AACD;AACA;AACA;AACA;;;EACC,KAAKC,SAAL,GAAiB,UAACC,QAAD,EAA4B;IAAA,IAAjBC,OAAiB,uEAAP,EAAO;;IAC5C,qBAEI,KAAI,CAACL,QAAL,EAFJ;IAAA,IACQM,aADR,kBACCL,KADD,CAD4C,CAK5C;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;IAEA,sBAAqB,KAAI,CAACD,QAAL,EAArB;IAAA,IAAMO,UAAN,mBAAMA,UAAN;;IACA,WAAsB,KAAI,CAACC,eAAL,GACnB,KAAI,CAACA,eAAL,CAAqBC,WADF,GAEnB,KAAI,CAACT,QAAL,EAFH;IAAA,IAAMU,WAAN,QAAMA,WAAN;;IAIAf,GAAG,CAAC,kBAAD,CAAH;IAEA,IAAIgB,YAAJ;IACA,IAAIC,eAAJ,CAvB4C,CAyB5C;;IACA,IAAMC,SAAS,GAAG,KAAI,CAAChB,YAAL,CAAkBS,aAAlB,EAAiCF,QAAjC,CAAlB,CA1B4C,CA4B5C;;;IACA,IAAIS,SAAJ,EAAe;MACd,YAKI,KAAI,CAACL,eAAL,GACD,KAAI,CAACA,eAAL,CAAqBC,WADpB,GAED,KAAI,CAACT,QAAL,EAPH;MAAA,IACCc,mBADD,SACCA,mBADD;MAAA,IAECC,kBAFD,SAECA,kBAFD;MAAA,IAGCC,iBAHD,SAGCA,iBAHD;MAAA,IAICC,gBAJD,SAICA,gBAJD;;MASA,IAAMC,2BAA2B,GAAGJ,mBAAmB,KAAK,CAAxB,MACnC;MACA;MACCT,OAAO,CAACc,oCAAR,IAAgDd,OAAO,CAACe,sBAHtB,CAApC;MAKA,IACCC,mBADD,GAGIR,SAHJ,CACCQ,mBADD;MAAA,IAECC,kBAFD,GAGIT,SAHJ,CAECS,kBAFD;MAKA,IAAIC,qBAAJ;MAEAZ,YAAY,GAAG,KAAI,CAACa,MAAL,CAAYC,2BAAZ,CAAwC;QACtDX,mBAAmB,EAAnBA,mBADsD;QAEtDC,kBAAkB,EAAlBA,kBAFsD;QAGtDC,iBAAiB,EAAjBA,iBAHsD;QAItDC,gBAAgB,EAAhBA;MAJsD,CAAxC,EAKZ;QACFI,mBAAmB,EAAnBA,mBADE;QAEFC,kBAAkB,EAAlBA;MAFE,CALY,EAQZ;QACFI,UAAU,EAAEtB,QAAQ,CAACF,MADnB;QAEFyB,YAAY,EAAE,KAAI,CAACC,qBAAL,EAFZ;QAGFV,2BAA2B,EAA3BA,2BAHE;QAIFW,iBAAiB,EAAE;UAAA,OAAMN,qBAAqB,GAAG,IAA9B;QAAA;MAJjB,CARY,CAAf;;MAeA,IAAIF,mBAAmB,GAAG,CAA1B,EAA6B;QAC5B1B,GAAG,CAAC,SAAD,EAAY0B,mBAAZ,EAAiC,OAAjC,CAAH;QAEAX,WAAW,GAAG,IAAIoB,KAAJ,CAAUT,mBAAV,EAA+BU,MAA/B,CAAsCrB,WAAtC,CAAd;QACAH,UAAU,GAAGT,SAAS,CACrB,IAAIgC,KAAJ,CAAUT,mBAAV,CADqB,EAErB,UAACW,CAAD;UAAA,OAAO,KAAI,CAACC,mBAAL,CAAyB7B,QAAQ,CAAC4B,CAAD,CAAjC,CAAP;QAAA,CAFqB,CAAT,CAIXD,MAJW,CAIJxB,UAJI,CAAb,CAJ4B,CAU5B;;QACA,IAAIW,2BAAJ,EAAiC;UAChCvB,GAAG,CAAC,8BAAD,CAAH;;UACA,KAAI,CAACuC,qBAAL,CAA2BC,sCAA3B,CAAkE;YACjE7B,aAAa,EAAbA,aADiE;YAEjEF,QAAQ,EAARA,QAFiE;YAGjEiB,mBAAmB,EAAnBA;UAHiE,CAAlE,EAFgC,CAOhC;UACA;UACA;;;UACA,IAAI,KAAI,CAACe,yBAAL,KAAmCC,SAAvC,EAAkD;YACjD,KAAI,CAACD,yBAAL,IAAkCf,mBAAlC;UACA;QACD,CAbD,MAaO;UACN1B,GAAG,CAAC,cAAD,CAAH;;UACA,IAAI4B,qBAAJ,EAA2B;YAC1B5B,GAAG,CAAC,+BAAD,EAAkC0B,mBAAlC,EAAuD,mCAAvD,EAA4F,KAAI,CAACO,qBAAL,EAA5F,CAAH,CAD0B,CAE1B;YACA;;YACAlB,WAAW,GAAG,IAAIoB,KAAJ,CAAU1B,QAAQ,CAACF,MAAnB,CAAd;UACA,CALD,MAKO;YACN;YACAP,GAAG,CAAC,+CAAD,CAAH;UACA;;UACDgB,YAAY,GAAG,KAAI,CAACa,MAAL,CAAYc,sBAAZ,CAAmC;YACjDZ,UAAU,EAAEtB,QAAQ,CAACF,MAD4B;YAEjDyB,YAAY,EAAE,KAAI,CAACC,qBAAL;UAFmC,CAAnC,CAAf,CAXM,CAeN;UACA;UACA;UACA;;UACA,KAAI,CAACQ,yBAAL,GAAiCC,SAAjC;QACA;MACD;;MAED,IAAIf,kBAAkB,GAAG,CAAzB,EAA4B;QAC3B3B,GAAG,CAAC,QAAD,EAAW2B,kBAAX,EAA+B,OAA/B,CAAH;QACAZ,WAAW,GAAGA,WAAW,CAACqB,MAAZ,CAAmB,IAAID,KAAJ,CAAUR,kBAAV,CAAnB,CAAd;QACAf,UAAU,GAAGA,UAAU,CAACwB,MAAX,CACZjC,SAAS,CACR,IAAIgC,KAAJ,CAAUR,kBAAV,CADQ,EAER,UAACU,CAAD;UAAA,OAAO,KAAI,CAACC,mBAAL,CAAyB7B,QAAQ,CAACiB,mBAAmB,GAAGf,aAAa,CAACJ,MAApC,GAA6C8B,CAA9C,CAAjC,CAAP;QAAA,CAFQ,CADG,CAAb;MAMA;;MAEDpB,eAAe,GAAG;QACjB2B,OAAO,EAAElB,mBAAmB,GAAG,CADd;QAEjBmB,MAAM,EAAElB,kBAAkB,GAAG;MAFZ,CAAlB;IAIA,CAnGD,MAmGO;MACN3B,GAAG,CAAC,yBAAD,EAA6BkB,SAAS,GAAG,8CAAH,GAAoD,2CAA1F,EAAwI,wCAAxI,CAAH;MACAlB,GAAG,CAAC,gBAAD,EAAmBW,aAAnB,CAAH;MACAX,GAAG,CAAC,WAAD,EAAcS,QAAd,CAAH,CAHM,CAKN;;MACAM,WAAW,GAAG,IAAIoB,KAAJ,CAAU1B,QAAQ,CAACF,MAAnB,CAAd;MACAK,UAAU,GAAGT,SAAS,CACrB,IAAIgC,KAAJ,CAAU1B,QAAQ,CAACF,MAAnB,CADqB,EAErB,UAAC8B,CAAD;QAAA,OAAO,KAAI,CAACC,mBAAL,CAAyB7B,QAAQ,CAAC4B,CAAD,CAAjC,CAAP;MAAA,CAFqB,CAAtB;MAKArB,YAAY,GAAG,KAAI,CAACa,MAAL,CAAYc,sBAAZ,CAAmC;QACjDZ,UAAU,EAAEtB,QAAQ,CAACF,MAD4B;QAEjDyB,YAAY,EAAE,KAAI,CAACC,qBAAL;MAFmC,CAAnC,CAAf,CAZM,CAiBN;MACA;MACA;MACA;;MACA,KAAI,CAACQ,yBAAL,GAAiCC,SAAjC,CArBM,CAuBN;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;;MACA,KAAI,CAACH,qBAAL,CAA2BO,KAA3B;;MAEA7B,eAAe,GAAG;QACjB8B,OAAO,EAAE;MADQ,CAAlB;IAGA;;IAED/C,GAAG,CAAC,kBAAD,CAAH,CA3K4C,CA6K5C;IACA;IACA;IACA;IAEA;IACA;;IACAA,GAAG,CAAC,wBAAD,EAA2BgB,YAAY,CAACG,mBAAxC,CAAH;IACAnB,GAAG,CAAC,uBAAD,EAA0BgB,YAAY,CAACI,kBAAvC,CAAH;IACApB,GAAG,CAAC,qBAAD,EAAwBgB,YAAY,CAACK,iBAArC,CAAH;IACArB,GAAG,CAAC,0CAAD,EAA6CgB,YAAY,CAACM,gBAA1D,CAAH,CAvL4C,CAyL5C;IACA;IACA;IACA;IACA;;IACA,KAAI,CAAC0B,iBAAL,CACCvC,QADD,EAECM,WAFD,EAGCC,YAAY,CAACG,mBAHd,EAICH,YAAY,CAACI,kBAJd,EA9L4C,CAqM5C;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;IACA,KAAI,CAAC6B,sBAAL,mCACIhC,eADJ;MAECiC,KAAK,EAAEzC,QAAQ,CAACF,MAFjB;MAGC;MACA;MACAsB,MAAM,EAAEb;IALT,GA/M4C,CAuN5C;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IAEA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;IACA,IAAMmC,QAAQ,mCACVnC,YADU;MAEbV,KAAK,EAAEG,QAFM;MAGbG,UAAU,EAAVA,UAHa;MAIbG,WAAW,EAAXA;IAJa,EAAd,CAnP4C,CA0P5C;IACA;IACA;;;IACA,IAAI,KAAI,CAACqC,YAAL,CAAkBC,sCAAlB,EAAJ,EAAgE;MAC/D,IAAI,KAAI,CAACC,oCAAL,EAAJ,EAAiD;QAChD;QACA;QACA;QACAH,QAAQ,CAACC,YAAT,GAAwBV,SAAxB;MACA,CALD,MAMK;QACJ;QACA;QACA;QACA;QACA;QACA;QACA;QACAS,QAAQ,CAACC,YAAT,GAAwB,KAAI,CAACvC,eAAL,GACrB,KAAI,CAACA,eAAL,CAAqBC,WAArB,CAAiCsC,YADZ,GAErB,KAAI,CAAC/C,QAAL,GAAgB+C,YAFnB;MAGA;IACD,CAhR2C,CAkR5C;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IAEA;;;IACA,KAAI,CAACG,kBAAL,GAA0B,IAA1B;;IACA,KAAI,CAACC,WAAL,CAAiBL,QAAjB;EACA,CAlSD;;EAoSA,KAAKjD,YAAL,GAAoB,UAACS,aAAD,EAAgBF,QAAhB,EAA6B;IAChD,OAAOP,YAAY,CAACS,aAAD,EAAgBF,QAAhB,EAA0B,KAAI,CAACgD,WAA/B,CAAnB;EACA,CAFD;AAGA"}
1
+ {"version":3,"file":"VirtualScroller.items.js","names":["log","reportError","getItemsDiff","fillArray","getItemsCount","getState","items","length","_getItemIndexByItemOrIndex","itemOrIndex","item","i","indexOf","JSON","stringify","_setItems","newItems","options","previousItems","itemStates","widthHasChanged","stateUpdate","itemHeights","layoutUpdate","itemsUpdateInfo","itemsDiff","firstShownItemIndex","lastShownItemIndex","beforeItemsHeight","afterItemsHeight","shouldRestoreScrollPosition","preserveScrollPositionOnPrependItems","preserveScrollPosition","prependedItemsCount","appendedItemsCount","shouldResetGridLayout","layout","getLayoutUpdateForItemsDiff","itemsCount","columnsCount","getActualColumnsCount","onResetGridLayout","Array","concat","getInitialItemState","listHeightMeasurement","snapshotListHeightBeforeAddingNewItems","firstNonMeasuredItemIndex","undefined","getInitialLayoutValues","prepend","append","reset","replace","onBeforeShowItems","newItemsWillBeRendered","count","newState","beforeResize","shouldIncludeBeforeResizeValuesInState","shouldDiscardBeforeResizeItemHeights","_isSettingNewItems","updateState","isItemEqual"],"sources":["../source/VirtualScroller.items.js"],"sourcesContent":["import log, { reportError } from './utility/debug.js'\r\nimport getItemsDiff from './getItemsDiff.js'\r\nimport fillArray from './utility/fillArray.js'\r\n\r\nexport default function() {\r\n\tthis.getItemsCount = () => {\r\n\t\treturn this.getState().items.length\r\n\t}\r\n\r\n\tthis._getItemIndexByItemOrIndex = (itemOrIndex) => {\r\n\t\tconst item = itemOrIndex\r\n\t\tconst { items } = this.getState()\r\n\t\t// Find the `item`'s index in the `items` array.\r\n\t\t//\r\n\t\t// Performance notes:\r\n\t\t// Doing an `.indexOf()` operation on a very large array could be inefficient\r\n\t\t// because it would have to check every element of the array.\r\n\t\t// In case of any hypothetical performance-related issues,\r\n\t\t// the code could use a `new Map()` as an \"index\" for finding individual items.\r\n\t\tconst i = items.indexOf(item)\r\n\t\t// validate that the `item` exists in the `items` array.\r\n\t\tif (i >= 0) {\r\n\t\t\treturn i\r\n\t\t}\r\n\r\n\t\t// Legacy behavior: some functions used to accept an `i: number` item index as an argument.\r\n\t\t// Later the argument was changed to `item: Item`.\r\n\t\t//\r\n\t\t// The old way of passing an `i: number` argument would only result in weird application behavior\r\n\t\t// when all of the below circumstances are true, which is assumed extremely unlikely:\r\n\t\t//\r\n\t\t// BOTH use a previous version of `virtual-scroller` (ones before December 2025, with downloads count that is not very high)\r\n\t\t// AND to be used with an `items` array of type `number[]`\r\n\t\t// AND to use any of the \"advanced\" functions:\r\n // * `setItemState(i, newState)`\r\n // * `onItemHeightDidChange(i)`\r\n // * `getItemScrollPosition(i)`\r\n\t\t//\r\n\t\t// The code below handles the legacy compatibility aspect of the old type of argument\r\n\t\t// except maybe for those \"extremely unlikely\" cases in which the app is still unlikely to crash.\r\n\t\t//\r\n\t\tif (typeof itemOrIndex === 'number') {\r\n\t\t\tconst i = itemOrIndex\r\n\t\t\t// Validate the item index argument.\r\n\t\t\tif (i >= 0 && i < items.length) {\r\n\t\t\t\treturn i\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treportError(`Item not found: ${JSON.stringify(item)}`)\r\n\t}\r\n\r\n\t/**\r\n\t * Updates `items`. For example, can prepend or append new items to the list.\r\n\t * @param {any[]} newItems\r\n\t * @param {boolean} [options.preserveScrollPositionOnPrependItems] — Set to `true` to enable \"restore scroll position after prepending items\" feature (could be useful when implementing \"Show previous items\" button).\r\n\t */\r\n\tthis._setItems = (newItems, options = {}) => {\r\n\t\tconst {\r\n\t\t\titems: previousItems\r\n\t\t} = this.getState()\r\n\r\n\t\t// Even if `newItems` are equal to `this.state.items`,\r\n\t\t// still perform a `updateState()` call, because, if `updateState()` calls\r\n\t\t// were \"asynchronous\", there could be a situation when a developer\r\n\t\t// first calls `setItems(newItems)` and then `setItems(oldItems)`:\r\n\t\t// if this function did `return` `if (newItems === this.state.items)`\r\n\t\t// then `updateState({ items: newItems })` would be scheduled as part of\r\n\t\t// `setItems(newItems)` call, but the subsequent `setItems(oldItems)` call\r\n\t\t// wouldn't do anything resulting in `newItems` being set as a result,\r\n\t\t// and that wouldn't be what the developer intended.\r\n\r\n\t\tlet { itemStates } = this.getState()\r\n\t\tlet { itemHeights } = this.widthHasChanged\r\n\t\t\t? this.widthHasChanged.stateUpdate\r\n\t\t\t: this.getState()\r\n\r\n\t\tlog('~ Update items ~')\r\n\r\n\t\tlet layoutUpdate\r\n\t\tlet itemsUpdateInfo\r\n\r\n\t\t// Compare the new items to the current items.\r\n\t\tconst itemsDiff = this.getItemsDiff(previousItems, newItems)\r\n\r\n\t\t// See if it's an \"incremental\" items update.\r\n\t\tif (itemsDiff) {\r\n\t\t\tconst {\r\n\t\t\t\tfirstShownItemIndex,\r\n\t\t\t\tlastShownItemIndex,\r\n\t\t\t\tbeforeItemsHeight,\r\n\t\t\t\tafterItemsHeight\r\n\t\t\t} = this.widthHasChanged\r\n\t\t\t\t? this.widthHasChanged.stateUpdate\r\n\t\t\t\t: this.getState()\r\n\r\n\t\t\tconst shouldRestoreScrollPosition = firstShownItemIndex === 0 &&\r\n\t\t\t\t// `preserveScrollPosition` option name is deprecated,\r\n\t\t\t\t// use `preserveScrollPositionOnPrependItems` instead.\r\n\t\t\t\t(options.preserveScrollPositionOnPrependItems || options.preserveScrollPosition)\r\n\r\n\t\t\tconst {\r\n\t\t\t\tprependedItemsCount,\r\n\t\t\t\tappendedItemsCount\r\n\t\t\t} = itemsDiff\r\n\r\n\t\t\tlet shouldResetGridLayout\r\n\r\n\t\t\tlayoutUpdate = this.layout.getLayoutUpdateForItemsDiff({\r\n\t\t\t\tfirstShownItemIndex,\r\n\t\t\t\tlastShownItemIndex,\r\n\t\t\t\tbeforeItemsHeight,\r\n\t\t\t\tafterItemsHeight\r\n\t\t\t}, {\r\n\t\t\t\tprependedItemsCount,\r\n\t\t\t\tappendedItemsCount\r\n\t\t\t}, {\r\n\t\t\t\titemsCount: newItems.length,\r\n\t\t\t\tcolumnsCount: this.getActualColumnsCount(),\r\n\t\t\t\tshouldRestoreScrollPosition,\r\n\t\t\t\tonResetGridLayout: () => shouldResetGridLayout = true\r\n\t\t\t})\r\n\r\n\t\t\tif (prependedItemsCount > 0) {\r\n\t\t\t\tlog('Prepend', prependedItemsCount, 'items')\r\n\r\n\t\t\t\titemHeights = new Array(prependedItemsCount)\r\n\t\t\t\t\t.concat(itemHeights)\r\n\r\n\t\t\t\titemStates = fillArray(\r\n\t\t\t\t\tnew Array(prependedItemsCount),\r\n\t\t\t\t\t(i) => this.getInitialItemState(newItems[i])\r\n\t\t\t\t)\r\n\t\t\t\t\t.concat(itemStates)\r\n\r\n\t\t\t\t// Restore scroll position after prepending items (if requested).\r\n\t\t\t\tif (shouldRestoreScrollPosition) {\r\n\t\t\t\t\tlog('Will restore scroll position')\r\n\t\t\t\t\tthis.listHeightMeasurement.snapshotListHeightBeforeAddingNewItems({\r\n\t\t\t\t\t\tpreviousItems,\r\n\t\t\t\t\t\tnewItems,\r\n\t\t\t\t\t\tprependedItemsCount\r\n\t\t\t\t\t})\r\n\t\t\t\t\t// \"Seamless prepend\" scenario doesn't result in a re-layout,\r\n\t\t\t\t\t// so if any \"non measured item\" is currently pending,\r\n\t\t\t\t\t// it doesn't get reset and will be handled after `state` is updated.\r\n\t\t\t\t\tif (this.firstNonMeasuredItemIndex !== undefined) {\r\n\t\t\t\t\t\tthis.firstNonMeasuredItemIndex += prependedItemsCount\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlog('Reset layout')\r\n\t\t\t\t\tif (shouldResetGridLayout) {\r\n\t\t\t\t\t\tlog('Reason: Prepended items count', prependedItemsCount, 'is not divisible by Columns Count', this.getActualColumnsCount())\r\n\t\t\t\t\t\t// Reset item heights because the whole grid is going to be rebalanced\r\n\t\t\t\t\t\t// and re-rendered in a different configuration.\r\n\t\t\t\t\t\titemHeights = new Array(newItems.length)\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\t// Reset layout because none of the prepended items have been measured.\r\n\t\t\t\t\t\tlog('Reason: Prepended items\\' heights are unknown')\r\n\t\t\t\t\t}\r\n\t\t\t\t\tlayoutUpdate = this.layout.getInitialLayoutValues({\r\n\t\t\t\t\t\titemsCount: newItems.length,\r\n\t\t\t\t\t\tcolumnsCount: this.getActualColumnsCount()\r\n\t\t\t\t\t})\r\n\t\t\t\t\t// Unschedule a potentially scheduled layout update\r\n\t\t\t\t\t// after measuring a previously non-measured item\r\n\t\t\t\t\t// because the list will be re-layout anyway\r\n\t\t\t\t\t// due to the new items being set.\r\n\t\t\t\t\tthis.firstNonMeasuredItemIndex = undefined\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (appendedItemsCount > 0) {\r\n\t\t\t\tlog('Append', appendedItemsCount, 'items')\r\n\t\t\t\titemHeights = itemHeights.concat(new Array(appendedItemsCount))\r\n\t\t\t\titemStates = itemStates.concat(\r\n\t\t\t\t\tfillArray(\r\n\t\t\t\t\t\tnew Array(appendedItemsCount),\r\n\t\t\t\t\t\t(i) => this.getInitialItemState(newItems[prependedItemsCount + previousItems.length + i])\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t}\r\n\r\n\t\t\titemsUpdateInfo = {\r\n\t\t\t\tprepend: prependedItemsCount > 0,\r\n\t\t\t\tappend: appendedItemsCount > 0\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tlog('Items have changed, and', (itemsDiff ? 'a re-layout from scratch has been requested.' : 'it\\'s not a simple append and/or prepend.'), 'Rerender the entire list from scratch.')\r\n\t\t\tlog('Previous items', previousItems)\r\n\t\t\tlog('New items', newItems)\r\n\r\n\t\t\t// Reset item heights and item states.\r\n\t\t\titemHeights = new Array(newItems.length)\r\n\t\t\titemStates = fillArray(\r\n\t\t\t\tnew Array(newItems.length),\r\n\t\t\t\t(i) => this.getInitialItemState(newItems[i])\r\n\t\t\t)\r\n\r\n\t\t\tlayoutUpdate = this.layout.getInitialLayoutValues({\r\n\t\t\t\titemsCount: newItems.length,\r\n\t\t\t\tcolumnsCount: this.getActualColumnsCount()\r\n\t\t\t})\r\n\r\n\t\t\t// Unschedule a potentially scheduled layout update\r\n\t\t\t// after measuring a previously non-measured item\r\n\t\t\t// because the list will be re-layout from scratch\r\n\t\t\t// due to the new items being set.\r\n\t\t\tthis.firstNonMeasuredItemIndex = undefined\r\n\r\n\t\t\t// Also reset any potential pending scroll position restoration.\r\n\t\t\t// For example, imagine a developer first called `.setItems(incrementalItemsUpdate)`\r\n\t\t\t// and then called `.setItems(differentItems)` and there was no state update\r\n\t\t\t// in between those two calls. This could happen because state updates aren't\r\n\t\t\t// required to be \"synchronous\". On other words, calling `this.updateState()`\r\n\t\t\t// doesn't necessarily mean that the state is applied immediately.\r\n\t\t\t// Imagine also that such \"delayed\" state updates could be batched,\r\n\t\t\t// like they do in React inside event handlers (though that doesn't apply to this case):\r\n\t\t\t// https://github.com/facebook/react/issues/10231#issuecomment-316644950\r\n\t\t\t// If `this.listHeightMeasurement` wasn't reset on `.setItems(differentItems)`\r\n\t\t\t// and if the second `this.updateState()` call overwrites the first one\r\n\t\t\t// then it would attempt to restore scroll position in a situation when\r\n\t\t\t// it should no longer do that. Hence the reset here.\r\n\t\t\tthis.listHeightMeasurement.reset()\r\n\r\n\t\t\titemsUpdateInfo = {\r\n\t\t\t\treplace: true\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tlog('~ Update state ~')\r\n\r\n\t\t// const layoutValuesAfterUpdate = {\r\n\t\t// \t...this.getState(),\r\n\t\t// \t...layoutUpdate\r\n\t\t// }\r\n\r\n\t\t// `layoutUpdate` is equivalent to `layoutValuesAfterUpdate` because\r\n\t\t// `layoutUpdate` contains all the relevant properties.\r\n\t\tlog('First shown item index', layoutUpdate.firstShownItemIndex)\r\n\t\tlog('Last shown item index', layoutUpdate.lastShownItemIndex)\r\n\t\tlog('Before items height', layoutUpdate.beforeItemsHeight)\r\n\t\tlog('After items height (actual or estimated)', layoutUpdate.afterItemsHeight)\r\n\r\n\t\t// Optionally preload items to be rendered.\r\n\t\t//\r\n\t\t// `layoutUpdate` is equivalent to `layoutValuesAfterUpdate` because\r\n\t\t// `layoutUpdate` contains all the relevant properties.\r\n\t\t//\r\n\t\tthis.onBeforeShowItems(\r\n\t\t\tnewItems,\r\n\t\t\titemHeights,\r\n\t\t\tlayoutUpdate.firstShownItemIndex,\r\n\t\t\tlayoutUpdate.lastShownItemIndex\r\n\t\t)\r\n\r\n\t\t// `this.newItemsWillBeRendered` signals that new `items` are being rendered,\r\n\t\t// and that `VirtualScroller` should temporarily stop all other updates.\r\n\t\t//\r\n\t\t// `this.newItemsWillBeRendered` is cleared in `onRender()`.\r\n\t\t//\r\n\t\t// The values in `this.newItemsWillBeRendered` are used, for example,\r\n\t\t// in `.onContainerResize()` handler in order to not break state consistency when\r\n\t\t// state updates are \"asynchronous\" (delayed) and there's a window resize event\r\n\t\t// in between calling `updateState()` below and that call actually being applied.\r\n\t\t//\r\n\t\tthis.newItemsWillBeRendered = {\r\n\t\t\t...itemsUpdateInfo,\r\n\t\t\tcount: newItems.length,\r\n\t\t\t// `layoutUpdate` now contains all layout-related properties, even if those that\r\n\t\t\t// didn't change. So `firstShownItemIndex` is always in `this.newItemsWillBeRendered`.\r\n\t\t\tlayout: layoutUpdate\r\n\t\t}\r\n\r\n\t\t// `layoutUpdate` now contains all layout-related properties, even if those that\r\n\t\t// didn't change. So this part is no longer relevant.\r\n\t\t//\r\n\t\t// // If `firstShownItemIndex` is gonna be modified as a result of setting new items\r\n\t\t// // then keep that \"new\" `firstShownItemIndex` in order for it to be used by\r\n\t\t// // `onResize()` handler when it calculates \"new\" `firstShownItemIndex`\r\n\t\t// // based on the new columns count (corresponding to the new window width).\r\n\t\t// if (layoutUpdate.firstShownItemIndex !== undefined) {\r\n\t\t// \tthis.newItemsWillBeRendered = {\r\n\t\t// \t\t...this.newItemsWillBeRendered,\r\n\t\t// \t\tfirstShownItemIndex: layoutUpdate.firstShownItemIndex\r\n\t\t// \t}\r\n\t\t// }\r\n\r\n\t\t// Update `VirtualScroller` state.\r\n\t\t//\r\n\t\t// This state update should overwrite all the `state` properties\r\n\t\t// that are also updated in the \"on scroll\" handler (`getShownItemIndexes()`):\r\n\t\t//\r\n\t\t// * `firstShownItemIndex`\r\n\t\t// * `lastShownItemIndex`\r\n\t\t// * `beforeItemsHeight`\r\n\t\t// * `afterItemsHeight`\r\n\t\t//\r\n\t\t// That's because this `updateState()` update has a higher priority\r\n\t\t// than that of the \"on scroll\" handler, so it should overwrite\r\n\t\t// any potential state changes dispatched by the \"on scroll\" handler.\r\n\t\t//\r\n\t\tconst newState = {\r\n\t\t\t...layoutUpdate,\r\n\t\t\titems: newItems,\r\n\t\t\titemStates,\r\n\t\t\titemHeights\r\n\t\t}\r\n\r\n\t\t// Introduced `shouldIncludeBeforeResizeValuesInState()` getter just to prevent\r\n\t\t// cluttering `state` with `beforeResize: undefined` property if `beforeResize`\r\n\t\t// hasn't ever been set in `state` previously.\r\n\t\tif (this.beforeResize.shouldIncludeBeforeResizeValuesInState()) {\r\n\t\t\tif (this.shouldDiscardBeforeResizeItemHeights()) {\r\n\t\t\t\t// Reset \"before resize\" item heights because now there're new items prepended\r\n\t\t\t\t// with unknown heights, or completely new items with unknown heights, so\r\n\t\t\t\t// `beforeItemsHeight` value won't be preserved anyway.\r\n\t\t\t\tnewState.beforeResize = undefined\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\t// Overwrite `beforeResize` property in `state` even if it wasn't modified\r\n\t\t\t\t// because state updates could be \"asynchronous\" and in that case there could be\r\n\t\t\t\t// some previous `updateState()` call from some previous `setItems()` call that\r\n\t\t\t\t// hasn't yet been applied, and that previous call might have scheduled setting\r\n\t\t\t\t// `state.beforeResize` property to `undefined` in order to reset it, but this\r\n\t\t\t\t// next `updateState()` call might not require resetting `state.beforeResize` property\r\n\t\t\t\t// so it should undo resetting it by simply overwriting it with its normal value.\r\n\t\t\t\tnewState.beforeResize = this.widthHasChanged\r\n\t\t\t\t\t? this.widthHasChanged.stateUpdate.beforeResize\r\n\t\t\t\t\t: this.getState().beforeResize\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// `newState` should also overwrite all `state` properties that're updated in `onResize()`\r\n\t\t// because `setItems()`'s state updates always overwrite `onResize()`'s state updates.\r\n\t\t// (The least-priority ones are `onScroll()` state updates, but those're simply skipped\r\n\t\t// if there's a pending `setItems()` or `onResize()` update).\r\n\t\t//\r\n\t\t// `state` property exceptions:\r\n\t\t//\r\n\t\t// `verticalSpacing` property is not updated here because it's fine setting it to\r\n\t\t// `undefined` in `onResize()` — it will simply be re-measured after the component re-renders.\r\n\t\t//\r\n\t\t// `columnsCount` property is also not updated here because by definition it's only\r\n\t\t// updated in `onResize()`.\r\n\r\n\t\t// Render.\r\n\t\tthis._isSettingNewItems = true\r\n\t\tthis.updateState(newState)\r\n\t}\r\n\r\n\tthis.getItemsDiff = (previousItems, newItems) => {\r\n\t\treturn getItemsDiff(previousItems, newItems, this.isItemEqual)\r\n\t}\r\n}"],"mappings":"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"}
@@ -211,10 +211,19 @@ var VirtualScroller = /*#__PURE__*/function () {
211
211
  value:
212
212
  /**
213
213
  * Returns the items's top offset relative to the scrollable container's top edge.
214
- * @param {number} i — Item index
214
+ * @param {any} item — Item
215
215
  * @return {[number]} Returns the item's scroll Y position. Returns `undefined` if any of the previous items haven't been rendered yet.
216
216
  */
217
- function getItemScrollPosition(i) {
217
+ function getItemScrollPosition(itemOrIndex) {
218
+ // Item index.
219
+ var i = this._getItemIndexByItemOrIndex(itemOrIndex); // If the item wasn't found, the error was already reported,
220
+ // so just return some "sensible" default value.
221
+
222
+
223
+ if (i === undefined) {
224
+ return;
225
+ }
226
+
218
227
  var itemTopOffsetInList = this.layout.getItemTopOffset(i);
219
228
 
220
229
  if (itemTopOffsetInList === undefined) {
@@ -230,32 +239,32 @@ var VirtualScroller = /*#__PURE__*/function () {
230
239
 
231
240
  }, {
232
241
  key: "onItemHeightChange",
233
- value: function onItemHeightChange(i) {
234
- warn('`.onItemHeightChange(i)` method was renamed to `.onItemHeightDidChange(i)`');
235
- this.onItemHeightDidChange(i);
242
+ value: function onItemHeightChange(item) {
243
+ warn('`.onItemHeightChange(item)` method was renamed to `.onItemHeightDidChange(item)`');
244
+ this.onItemHeightDidChange(item);
236
245
  }
237
246
  /**
238
247
  * Forces a re-measure of an item's height.
239
- * @param {number} i — Item index
248
+ * @param {any} item — Item. Legacy argument variant: Item index.
240
249
  */
241
250
 
242
251
  }, {
243
252
  key: "onItemHeightDidChange",
244
- value: function onItemHeightDidChange(i) {
253
+ value: function onItemHeightDidChange(itemOrIndex) {
245
254
  // See the comments in the `setItemState()` function below for the rationale
246
255
  // on why the `hasToBeStarted()` check was commented out.
247
256
  // this.hasToBeStarted()
248
- this._onItemHeightDidChange(i);
257
+ this._onItemHeightDidChange(itemOrIndex);
249
258
  }
250
259
  /**
251
260
  * Updates an item's state in `state.itemStates[]`.
252
- * @param {number} i — Item index
253
- * @param {any} i — Item's new state
261
+ * @param {any} item — Item. Legacy argument variant: Item index.
262
+ * @param {any} newItemState — Item's new state
254
263
  */
255
264
 
256
265
  }, {
257
266
  key: "setItemState",
258
- value: function setItemState(i, newItemState) {
267
+ value: function setItemState(itemOrIndex, newItemState) {
259
268
  // There is an issue in React 18.2.0 when `useInsertionEffect()` doesn't run twice
260
269
  // on mount unlike `useLayoutEffect()` in "strict" mode. That causes a bug in a React
261
270
  // implementation of the `virtual-scroller`.
@@ -298,14 +307,14 @@ var VirtualScroller = /*#__PURE__*/function () {
298
307
  // Commenting it out wouldn't result in any potential bugs because the code would work correctly
299
308
  // in both cases.
300
309
  // this.hasToBeStarted()
301
- this._setItemState(i, newItemState);
310
+ this._setItemState(itemOrIndex, newItemState);
302
311
  } // (deprecated)
303
312
  // Use `.setItemState()` method name instead.
304
313
 
305
314
  }, {
306
315
  key: "onItemStateChange",
307
- value: function onItemStateChange(i, newItemState) {
308
- this.setItemState(i, newItemState);
316
+ value: function onItemStateChange(item, newItemState) {
317
+ this.setItemState(item, newItemState);
309
318
  }
310
319
  /**
311
320
  * Updates `items`. For example, can prepend or append new items to the list.
@@ -1 +1 @@
1
- {"version":3,"file":"VirtualScroller.js","names":["VirtualScrollerConstructor","LAYOUT_REASON","log","warn","reportError","supportsTbody","hasTbodyStyles","addTbodyStyles","BROWSER_NOT_SUPPORTED_ERROR","VirtualScroller","getItemsContainerElement","items","options","_isActive","Error","scrollableContainerResizeHandler","stop","scroll","listTopOffsetWatcher","isStarted","cancelLayoutTimer","hasToBeStarted","onUpdateShownItemIndexes","reason","MANUAL","_onRender","getState","previousState","call","isRestart","setUpState","waitingForRender","_render","listHeightMeasurement","reset","_isResizing","undefined","_isSettingNewItems","isInBypassMode","isItemsContainerElementTableBody","_bypass","stateUpdate","_afterRenderStateUpdateThatWasStopped","verticalSpacing","verticalSpacingStateUpdate","measureItemHeightsAndSpacing","start","scrollableContainerWidth","scrollableContainer","getWidth","newWidth","prevWidth","onContainerResize","_usesCustomStateStorage","columnsCount","getActualColumnsCount","columnsCountFromState","STARTED","i","itemTopOffsetInList","layout","getItemTopOffset","getListTopOffsetInsideScrollableContainer","onItemHeightDidChange","_onItemHeightDidChange","newItemState","_setItemState","setItemState","newItems","_setItems"],"sources":["../source/VirtualScroller.js"],"sourcesContent":["import VirtualScrollerConstructor from './VirtualScroller.constructor.js'\r\nimport { LAYOUT_REASON } from './Layout.js'\r\nimport log, { warn, reportError } from './utility/debug.js'\r\n\r\nimport {\r\n\tsupportsTbody,\r\n\thasTbodyStyles,\r\n\taddTbodyStyles,\r\n\tBROWSER_NOT_SUPPORTED_ERROR\r\n} from './DOM/tbody.js'\r\n\r\nexport default class VirtualScroller {\r\n\t/**\r\n\t * @param {function} getItemsContainerElement — Returns the items container DOM `Element`.\r\n\t * @param {any[]} items — The list of items.\r\n\t * @param {Object} [options] — See README.md.\r\n\t * @return {VirtualScroller}\r\n\t */\r\n\tconstructor(\r\n\t\tgetItemsContainerElement,\r\n\t\titems,\r\n\t\toptions = {}\r\n\t) {\r\n\t\tVirtualScrollerConstructor.call(\r\n\t\t\tthis,\r\n\t\t\tgetItemsContainerElement,\r\n\t\t\titems,\r\n\t\t\toptions\r\n\t\t)\r\n\t}\r\n\r\n\t/**\r\n\t * Should be invoked after a \"container\" DOM Element is mounted (inserted into the DOM tree).\r\n\t */\r\n\tstart() {\r\n\t\tif (this._isActive) {\r\n\t\t\tthrow new Error('[virtual-scroller] `VirtualScroller` has already been started')\r\n\t\t}\r\n\r\n\t\t// If has been stopped previously.\r\n\t\tconst isRestart = this._isActive === false\r\n\r\n\t\tif (!isRestart) {\r\n\t\t\tthis.setUpState()\r\n\t\t\tthis.waitingForRender = true\r\n\t\t\t// If `render()` function parameter was passed,\r\n\t\t\t// perform an initial render.\r\n\t\t\tif (this._render) {\r\n\t\t\t\tthis._render(this.getState())\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (isRestart) {\r\n\t\t\tlog('~ Start (restart) ~')\r\n\t\t} else {\r\n\t\t\tlog('~ Start ~')\r\n\t\t}\r\n\r\n\t\t// `this._isActive = true` should be placed somewhere at the start of this function.\r\n\t\tthis._isActive = true\r\n\r\n\t\t// Reset `ListHeightMeasurement` just in case it has some \"leftover\" state.\r\n\t\tthis.listHeightMeasurement.reset()\r\n\r\n\t\t// Reset `_isResizing` flag just in case it has some \"leftover\" value.\r\n\t\tthis._isResizing = undefined\r\n\r\n\t\t// Reset `_isSettingNewItems` flag just in case it has some \"leftover\" value.\r\n\t\tthis._isSettingNewItems = undefined\r\n\r\n\t\t// When `<tbody/>` is used as an items container element,\r\n\t\t// `virtual-scroller` has to work around the HTML bug of\r\n\t\t// `padding` not working on a `<tbody/>` element.\r\n\t\t// https://gitlab.com/catamphetamine/virtual-scroller/-/issues/1\r\n\t\tif (!this.isInBypassMode()) {\r\n\t\t\tif (this.isItemsContainerElementTableBody()) {\r\n\t\t\t\tif (supportsTbody()) {\r\n\t\t\t\t\tif (!hasTbodyStyles(this.getItemsContainerElement())) {\r\n\t\t\t\t\t\tlog('~ <tbody/> container ~')\r\n\t\t\t\t\t\taddTbodyStyles(this.getItemsContainerElement())\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlog('~ <tbody/> container not supported ~')\r\n\t\t\t\t\treportError(BROWSER_NOT_SUPPORTED_ERROR)\r\n\t\t\t\t\tlog('~ enter \"bypass\" mode ~')\r\n\t\t\t\t\tthis._bypass = true\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// If there was a pending \"after render\" state update that didn't get applied\r\n\t\t// because the `VirtualScroller` got stopped, then apply that pending \"after render\"\r\n\t\t// state update now. Such state update could include properties like:\r\n\t\t// * A `verticalSpacing` that has been measured in `onRender()`.\r\n\t\t// * A cleaned-up `beforeResize` object that was cleaned-up in `onRender()`.\r\n\t\tlet stateUpdate = this._afterRenderStateUpdateThatWasStopped\r\n\t\tthis._afterRenderStateUpdateThatWasStopped = undefined\r\n\r\n\t\t// Reset `this.verticalSpacing` so that it re-measures it in cases when\r\n\t\t// the `VirtualScroller` was previously stopped and is now being restarted.\r\n\t\t// The rationale is that a previously captured inter-item vertical spacing\r\n\t\t// can't be \"trusted\" in a sense that the user might have resized the window\r\n\t\t// after the previous `state` has been snapshotted.\r\n\t\t// If the user has resized the window, then changing window width might have\r\n\t\t// activated different CSS `@media()` \"queries\" resulting in a potentially different\r\n\t\t// vertical spacing after the restart.\r\n\t\t// If it's not a restart then `this.verticalSpacing` is `undefined` anyway.\r\n\t\tthis.verticalSpacing = undefined\r\n\r\n\t\tconst verticalSpacingStateUpdate = this.measureItemHeightsAndSpacing()\r\n\t\tif (verticalSpacingStateUpdate) {\r\n\t\t\tstateUpdate = {\r\n\t\t\t\t...stateUpdate,\r\n\t\t\t\t...verticalSpacingStateUpdate\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis.scrollableContainerResizeHandler.start()\r\n\t\tthis.scroll.start()\r\n\r\n\t\t// If `scrollableContainerWidth` hasn't been measured yet,\r\n\t\t// measure it and write it to state.\r\n\t\tif (this.getState().scrollableContainerWidth === undefined) {\r\n\t\t\tconst scrollableContainerWidth = this.scrollableContainer.getWidth()\r\n\t\t\tstateUpdate = {\r\n\t\t\t\t...stateUpdate,\r\n\t\t\t\tscrollableContainerWidth\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\t// Reset layout:\r\n\t\t\t// * If the scrollable container width has changed while stopped.\r\n\t\t\t// * If the restored state was calculated for another scrollable container width.\r\n\t\t\tconst newWidth = this.scrollableContainer.getWidth()\r\n\t\t\tconst prevWidth = this.getState().scrollableContainerWidth\r\n\t\t\tif (newWidth !== prevWidth) {\r\n\t\t\t\tlog('~ Scrollable container width changed from', prevWidth, 'to', newWidth, '~')\r\n\t\t\t\t// The pending state update (if present) won't be applied in this case.\r\n\t\t\t\t// That's ok because such state update could currently only originate in\r\n\t\t\t\t// `this.onContainerResize()` function. Therefore, alling `this.onContainerResize()` again\r\n\t\t\t\t// would rewrite all those `stateUpdate` properties anyway, so they're not passed.\r\n\t\t\t\treturn this.onContainerResize()\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// If the `VirtualScroller` uses custom (external) state storage, then\r\n\t\t// check if the columns count has changed between calling `.getInitialState()`\r\n\t\t// and `.start()`. If it has, perform a re-layout \"from scratch\".\r\n\t\tif (this._usesCustomStateStorage) {\r\n\t\t\tconst columnsCount = this.getActualColumnsCount()\r\n\t\t\tconst columnsCountFromState = this.getState().columnsCount || 1\r\n\t\t\tif (columnsCount !== columnsCountFromState) {\r\n\t\t\t\treturn this.onContainerResize()\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Re-calculate layout and re-render the list.\r\n\t\t// Do that even if when an initial `state` parameter, containing layout values,\r\n\t\t// has been passed. The reason is that the `state` parameter can't be \"trusted\"\r\n\t\t// in a way that it could have been snapshotted for another window width and\r\n\t\t// the user might have resized their window since then.\r\n\t\tthis.onUpdateShownItemIndexes({ reason: LAYOUT_REASON.STARTED, stateUpdate })\r\n\t}\r\n\r\n\t// Could be passed as a \"callback\" parameter, so bind it to `this`.\r\n\tstop = () => {\r\n\t\tif (!this._isActive) {\r\n\t\t\tthrow new Error('[virtual-scroller] Can\\'t stop a `VirtualScroller` that hasn\\'t been started')\r\n\t\t}\r\n\r\n\t\tthis._isActive = false\r\n\r\n\t\tlog('~ Stop ~')\r\n\r\n\t\tthis.scrollableContainerResizeHandler.stop()\r\n\t\tthis.scroll.stop()\r\n\r\n\t\t// Stop `ListTopOffsetWatcher` if it has been started.\r\n\t\t// There seems to be no need to restart `ListTopOffsetWatcher`.\r\n\t\t// It's mainly a hacky workaround for development mode anyway.\r\n\t\tif (this.listTopOffsetWatcher && this.listTopOffsetWatcher.isStarted()) {\r\n\t\t\tthis.listTopOffsetWatcher.stop()\r\n\t\t}\r\n\r\n\t\t// Cancel any scheduled layout.\r\n\t\tthis.cancelLayoutTimer({})\r\n\t}\r\n\r\n\thasToBeStarted() {\r\n\t\tif (!this._isActive) {\r\n\t\t\tthrow new Error('[virtual-scroller] `VirtualScroller` hasn\\'t been started')\r\n\t\t}\r\n\t}\r\n\r\n\t// Bind it to `this` because this function could hypothetically be passed\r\n\t// as a \"callback\" parameter.\r\n\tupdateLayout = () => {\r\n\t\tthis.hasToBeStarted()\r\n\t\tthis.onUpdateShownItemIndexes({ reason: LAYOUT_REASON.MANUAL })\r\n\t}\r\n\r\n\t// Bind the function to `this` so that it could be passed as a callback\r\n\t// in a random application's code.\r\n\tonRender = () => {\r\n\t\tthis._onRender(this.getState(), this.previousState)\r\n\t}\r\n\r\n\t/**\r\n\t * Returns the items's top offset relative to the scrollable container's top edge.\r\n\t * @param {number} i — Item index\r\n\t * @return {[number]} Returns the item's scroll Y position. Returns `undefined` if any of the previous items haven't been rendered yet.\r\n\t */\r\n\tgetItemScrollPosition(i) {\r\n\t\tconst itemTopOffsetInList = this.layout.getItemTopOffset(i)\r\n\t\tif (itemTopOffsetInList === undefined) {\r\n\t\t\treturn\r\n\t\t}\r\n\t\treturn this.getListTopOffsetInsideScrollableContainer() + itemTopOffsetInList\r\n\t}\r\n\r\n\t/**\r\n\t * @deprecated\r\n\t * `.onItemHeightChange()` has been renamed to `.onItemHeightDidChange()`.\r\n\t */\r\n\tonItemHeightChange(i) {\r\n\t\twarn('`.onItemHeightChange(i)` method was renamed to `.onItemHeightDidChange(i)`')\r\n\t\tthis.onItemHeightDidChange(i)\r\n\t}\r\n\r\n\t/**\r\n\t * Forces a re-measure of an item's height.\r\n\t * @param {number} i — Item index\r\n\t */\r\n\tonItemHeightDidChange(i) {\r\n\t\t// See the comments in the `setItemState()` function below for the rationale\r\n\t\t// on why the `hasToBeStarted()` check was commented out.\r\n\t\t// this.hasToBeStarted()\r\n\t\tthis._onItemHeightDidChange(i)\r\n\t}\r\n\r\n\t/**\r\n\t * Updates an item's state in `state.itemStates[]`.\r\n\t * @param {number} i — Item index\r\n\t * @param {any} i — Item's new state\r\n\t */\r\n\tsetItemState(i, newItemState) {\r\n\t\t// There is an issue in React 18.2.0 when `useInsertionEffect()` doesn't run twice\r\n\t\t// on mount unlike `useLayoutEffect()` in \"strict\" mode. That causes a bug in a React\r\n\t\t// implementation of the `virtual-scroller`.\r\n\t\t// https://gitlab.com/catamphetamine/virtual-scroller/-/issues/33\r\n\t\t// https://github.com/facebook/react/issues/26320\r\n\t\t// A workaround for that bug is ignoring the second-initial run of the effects at mount.\r\n\t\t//\r\n\t\t// But in that case, if an `ItemComponent` calls `setItemState()` in `useLayoutEffect()`,\r\n\t\t// it could result in a bug.\r\n\t\t//\r\n\t\t// Consider a type of `useLayoutEffect()` that skips the initial mount:\r\n\t\t// `useLayoutEffectSkipInitialMount()`.\r\n\t\t// Suppose that effect is written in such a way that it only skips the first call of itself.\r\n\t\t// In that case, if React is run in \"strict\" mode, the effect will no longer work as expected\r\n\t\t// and it won't actually skip the initial mount and will be executed during the second initial run.\r\n\t\t// But the `VirtualScroller` itself has already implemented a workaround that prevents\r\n\t\t// its hooks from running twice on mount. This means that `useVirtualScrollerStartStop()`\r\n\t\t// of the React component would have already stopped the `VirtualScroller` by the time\r\n\t\t// `ItemComponent`'s incorrectly-behaving `useLayoutEffectSkipInitialMount()` effect is run,\r\n\t\t// resulting in an error: \"`VirtualScroller` hasn't been started\".\r\n\t\t//\r\n\t\t// The log when not in \"strict\" mode would be:\r\n\t\t//\r\n\t\t// * `useLayoutEffect()` is run in `ItemComponent` — skips the initial run.\r\n\t\t// * `useLayoutEffect()` is run in `useVirtualScrollerStartStop()`. It starts the `VirtualScroller`.\r\n\t\t// * Some dependency property gets updated inside `ItemComponent`.\r\n\t\t// * `useLayoutEffect()` is run in `ItemComponent` — no longer skips. Calls `setItemState()`.\r\n\t\t// * The `VirtualScroller` is started so it handles `setState()` correctly.\r\n\t\t//\r\n\t\t// The log when in \"strict\" mode would be:\r\n\t\t//\r\n\t\t// * `useLayoutEffect()` is run in `ItemComponent` — skips the initial run.\r\n\t\t// * `useLayoutEffect()` is run in `useVirtualScrollerStartStop()`. It starts the `VirtualScroller`.\r\n\t\t// * `useLayoutEffect()` is unmounted in `useVirtualScrollerStartStop()`. It stops the `VirtualScroller`.\r\n\t\t// * `useLayoutEffect()` is unmounted in `ItemComponent` — does nothing.\r\n\t\t// * `useLayoutEffect()` is run the second time in `ItemComponent` — no longer skips. Calls `setItemState()`.\r\n\t\t// * The `VirtualScroller` is stopped so it throws an error: \"`VirtualScroller` hasn't been started\".\r\n\t\t//\r\n\t\t// For that reason, the requirement of the `VirtualScroller` to be started was commented out.\r\n\t\t// Commenting it out wouldn't result in any potential bugs because the code would work correctly\r\n\t\t// in both cases.\r\n\t\t// this.hasToBeStarted()\r\n\t\tthis._setItemState(i, newItemState)\r\n\t}\r\n\r\n\t// (deprecated)\r\n\t// Use `.setItemState()` method name instead.\r\n\tonItemStateChange(i, newItemState) {\r\n\t\tthis.setItemState(i, newItemState)\r\n\t}\r\n\r\n\t/**\r\n\t * Updates `items`. For example, can prepend or append new items to the list.\r\n\t * @param {any[]} newItems\r\n\t * @param {boolean} [options.preserveScrollPositionOnPrependItems] — Set to `true` to enable \"restore scroll position after prepending items\" feature (could be useful when implementing \"Show previous items\" button).\r\n\t */\r\n\tsetItems(newItems, options = {}) {\r\n\t\tthis.hasToBeStarted()\r\n\t\treturn this._setItems(newItems, options)\r\n\t}\r\n}"],"mappings":"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AAC,4EAAD,CAAJ;MACA,KAAK4D,qBAAL,CAA2BL,CAA3B;IACA;IAED;AACD;AACA;AACA;;;;WACC,+BAAsBA,CAAtB,EAAyB;MACxB;MACA;MACA;MACA,KAAKM,sBAAL,CAA4BN,CAA5B;IACA;IAED;AACD;AACA;AACA;AACA;;;;WACC,sBAAaA,CAAb,EAAgBO,YAAhB,EAA8B;MAC7B;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA,KAAKC,aAAL,CAAmBR,CAAnB,EAAsBO,YAAtB;IACA,C,CAED;IACA;;;;WACA,2BAAkBP,CAAlB,EAAqBO,YAArB,EAAmC;MAClC,KAAKE,YAAL,CAAkBT,CAAlB,EAAqBO,YAArB;IACA;IAED;AACD;AACA;AACA;AACA;;;;WACC,kBAASG,QAAT,EAAiC;MAAA,IAAdxD,OAAc,uEAAJ,EAAI;MAChC,KAAKS,cAAL;MACA,OAAO,KAAKgD,SAAL,CAAeD,QAAf,EAAyBxD,OAAzB,CAAP;IACA;;;;;;SArSmBH,e"}
1
+ {"version":3,"file":"VirtualScroller.js","names":["VirtualScrollerConstructor","LAYOUT_REASON","log","warn","reportError","supportsTbody","hasTbodyStyles","addTbodyStyles","BROWSER_NOT_SUPPORTED_ERROR","VirtualScroller","getItemsContainerElement","items","options","_isActive","Error","scrollableContainerResizeHandler","stop","scroll","listTopOffsetWatcher","isStarted","cancelLayoutTimer","hasToBeStarted","onUpdateShownItemIndexes","reason","MANUAL","_onRender","getState","previousState","call","isRestart","setUpState","waitingForRender","_render","listHeightMeasurement","reset","_isResizing","undefined","_isSettingNewItems","isInBypassMode","isItemsContainerElementTableBody","_bypass","stateUpdate","_afterRenderStateUpdateThatWasStopped","verticalSpacing","verticalSpacingStateUpdate","measureItemHeightsAndSpacing","start","scrollableContainerWidth","scrollableContainer","getWidth","newWidth","prevWidth","onContainerResize","_usesCustomStateStorage","columnsCount","getActualColumnsCount","columnsCountFromState","STARTED","itemOrIndex","i","_getItemIndexByItemOrIndex","itemTopOffsetInList","layout","getItemTopOffset","getListTopOffsetInsideScrollableContainer","item","onItemHeightDidChange","_onItemHeightDidChange","newItemState","_setItemState","setItemState","newItems","_setItems"],"sources":["../source/VirtualScroller.js"],"sourcesContent":["import VirtualScrollerConstructor from './VirtualScroller.constructor.js'\r\nimport { LAYOUT_REASON } from './Layout.js'\r\nimport log, { warn, reportError } from './utility/debug.js'\r\n\r\nimport {\r\n\tsupportsTbody,\r\n\thasTbodyStyles,\r\n\taddTbodyStyles,\r\n\tBROWSER_NOT_SUPPORTED_ERROR\r\n} from './DOM/tbody.js'\r\n\r\nexport default class VirtualScroller {\r\n\t/**\r\n\t * @param {function} getItemsContainerElement — Returns the items container DOM `Element`.\r\n\t * @param {any[]} items — The list of items.\r\n\t * @param {Object} [options] — See README.md.\r\n\t * @return {VirtualScroller}\r\n\t */\r\n\tconstructor(\r\n\t\tgetItemsContainerElement,\r\n\t\titems,\r\n\t\toptions = {}\r\n\t) {\r\n\t\tVirtualScrollerConstructor.call(\r\n\t\t\tthis,\r\n\t\t\tgetItemsContainerElement,\r\n\t\t\titems,\r\n\t\t\toptions\r\n\t\t)\r\n\t}\r\n\r\n\t/**\r\n\t * Should be invoked after a \"container\" DOM Element is mounted (inserted into the DOM tree).\r\n\t */\r\n\tstart() {\r\n\t\tif (this._isActive) {\r\n\t\t\tthrow new Error('[virtual-scroller] `VirtualScroller` has already been started')\r\n\t\t}\r\n\r\n\t\t// If has been stopped previously.\r\n\t\tconst isRestart = this._isActive === false\r\n\r\n\t\tif (!isRestart) {\r\n\t\t\tthis.setUpState()\r\n\t\t\tthis.waitingForRender = true\r\n\t\t\t// If `render()` function parameter was passed,\r\n\t\t\t// perform an initial render.\r\n\t\t\tif (this._render) {\r\n\t\t\t\tthis._render(this.getState())\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (isRestart) {\r\n\t\t\tlog('~ Start (restart) ~')\r\n\t\t} else {\r\n\t\t\tlog('~ Start ~')\r\n\t\t}\r\n\r\n\t\t// `this._isActive = true` should be placed somewhere at the start of this function.\r\n\t\tthis._isActive = true\r\n\r\n\t\t// Reset `ListHeightMeasurement` just in case it has some \"leftover\" state.\r\n\t\tthis.listHeightMeasurement.reset()\r\n\r\n\t\t// Reset `_isResizing` flag just in case it has some \"leftover\" value.\r\n\t\tthis._isResizing = undefined\r\n\r\n\t\t// Reset `_isSettingNewItems` flag just in case it has some \"leftover\" value.\r\n\t\tthis._isSettingNewItems = undefined\r\n\r\n\t\t// When `<tbody/>` is used as an items container element,\r\n\t\t// `virtual-scroller` has to work around the HTML bug of\r\n\t\t// `padding` not working on a `<tbody/>` element.\r\n\t\t// https://gitlab.com/catamphetamine/virtual-scroller/-/issues/1\r\n\t\tif (!this.isInBypassMode()) {\r\n\t\t\tif (this.isItemsContainerElementTableBody()) {\r\n\t\t\t\tif (supportsTbody()) {\r\n\t\t\t\t\tif (!hasTbodyStyles(this.getItemsContainerElement())) {\r\n\t\t\t\t\t\tlog('~ <tbody/> container ~')\r\n\t\t\t\t\t\taddTbodyStyles(this.getItemsContainerElement())\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlog('~ <tbody/> container not supported ~')\r\n\t\t\t\t\treportError(BROWSER_NOT_SUPPORTED_ERROR)\r\n\t\t\t\t\tlog('~ enter \"bypass\" mode ~')\r\n\t\t\t\t\tthis._bypass = true\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// If there was a pending \"after render\" state update that didn't get applied\r\n\t\t// because the `VirtualScroller` got stopped, then apply that pending \"after render\"\r\n\t\t// state update now. Such state update could include properties like:\r\n\t\t// * A `verticalSpacing` that has been measured in `onRender()`.\r\n\t\t// * A cleaned-up `beforeResize` object that was cleaned-up in `onRender()`.\r\n\t\tlet stateUpdate = this._afterRenderStateUpdateThatWasStopped\r\n\t\tthis._afterRenderStateUpdateThatWasStopped = undefined\r\n\r\n\t\t// Reset `this.verticalSpacing` so that it re-measures it in cases when\r\n\t\t// the `VirtualScroller` was previously stopped and is now being restarted.\r\n\t\t// The rationale is that a previously captured inter-item vertical spacing\r\n\t\t// can't be \"trusted\" in a sense that the user might have resized the window\r\n\t\t// after the previous `state` has been snapshotted.\r\n\t\t// If the user has resized the window, then changing window width might have\r\n\t\t// activated different CSS `@media()` \"queries\" resulting in a potentially different\r\n\t\t// vertical spacing after the restart.\r\n\t\t// If it's not a restart then `this.verticalSpacing` is `undefined` anyway.\r\n\t\tthis.verticalSpacing = undefined\r\n\r\n\t\tconst verticalSpacingStateUpdate = this.measureItemHeightsAndSpacing()\r\n\t\tif (verticalSpacingStateUpdate) {\r\n\t\t\tstateUpdate = {\r\n\t\t\t\t...stateUpdate,\r\n\t\t\t\t...verticalSpacingStateUpdate\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis.scrollableContainerResizeHandler.start()\r\n\t\tthis.scroll.start()\r\n\r\n\t\t// If `scrollableContainerWidth` hasn't been measured yet,\r\n\t\t// measure it and write it to state.\r\n\t\tif (this.getState().scrollableContainerWidth === undefined) {\r\n\t\t\tconst scrollableContainerWidth = this.scrollableContainer.getWidth()\r\n\t\t\tstateUpdate = {\r\n\t\t\t\t...stateUpdate,\r\n\t\t\t\tscrollableContainerWidth\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\t// Reset layout:\r\n\t\t\t// * If the scrollable container width has changed while stopped.\r\n\t\t\t// * If the restored state was calculated for another scrollable container width.\r\n\t\t\tconst newWidth = this.scrollableContainer.getWidth()\r\n\t\t\tconst prevWidth = this.getState().scrollableContainerWidth\r\n\t\t\tif (newWidth !== prevWidth) {\r\n\t\t\t\tlog('~ Scrollable container width changed from', prevWidth, 'to', newWidth, '~')\r\n\t\t\t\t// The pending state update (if present) won't be applied in this case.\r\n\t\t\t\t// That's ok because such state update could currently only originate in\r\n\t\t\t\t// `this.onContainerResize()` function. Therefore, alling `this.onContainerResize()` again\r\n\t\t\t\t// would rewrite all those `stateUpdate` properties anyway, so they're not passed.\r\n\t\t\t\treturn this.onContainerResize()\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// If the `VirtualScroller` uses custom (external) state storage, then\r\n\t\t// check if the columns count has changed between calling `.getInitialState()`\r\n\t\t// and `.start()`. If it has, perform a re-layout \"from scratch\".\r\n\t\tif (this._usesCustomStateStorage) {\r\n\t\t\tconst columnsCount = this.getActualColumnsCount()\r\n\t\t\tconst columnsCountFromState = this.getState().columnsCount || 1\r\n\t\t\tif (columnsCount !== columnsCountFromState) {\r\n\t\t\t\treturn this.onContainerResize()\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Re-calculate layout and re-render the list.\r\n\t\t// Do that even if when an initial `state` parameter, containing layout values,\r\n\t\t// has been passed. The reason is that the `state` parameter can't be \"trusted\"\r\n\t\t// in a way that it could have been snapshotted for another window width and\r\n\t\t// the user might have resized their window since then.\r\n\t\tthis.onUpdateShownItemIndexes({ reason: LAYOUT_REASON.STARTED, stateUpdate })\r\n\t}\r\n\r\n\t// Could be passed as a \"callback\" parameter, so bind it to `this`.\r\n\tstop = () => {\r\n\t\tif (!this._isActive) {\r\n\t\t\tthrow new Error('[virtual-scroller] Can\\'t stop a `VirtualScroller` that hasn\\'t been started')\r\n\t\t}\r\n\r\n\t\tthis._isActive = false\r\n\r\n\t\tlog('~ Stop ~')\r\n\r\n\t\tthis.scrollableContainerResizeHandler.stop()\r\n\t\tthis.scroll.stop()\r\n\r\n\t\t// Stop `ListTopOffsetWatcher` if it has been started.\r\n\t\t// There seems to be no need to restart `ListTopOffsetWatcher`.\r\n\t\t// It's mainly a hacky workaround for development mode anyway.\r\n\t\tif (this.listTopOffsetWatcher && this.listTopOffsetWatcher.isStarted()) {\r\n\t\t\tthis.listTopOffsetWatcher.stop()\r\n\t\t}\r\n\r\n\t\t// Cancel any scheduled layout.\r\n\t\tthis.cancelLayoutTimer({})\r\n\t}\r\n\r\n\thasToBeStarted() {\r\n\t\tif (!this._isActive) {\r\n\t\t\tthrow new Error('[virtual-scroller] `VirtualScroller` hasn\\'t been started')\r\n\t\t}\r\n\t}\r\n\r\n\t// Bind it to `this` because this function could hypothetically be passed\r\n\t// as a \"callback\" parameter.\r\n\tupdateLayout = () => {\r\n\t\tthis.hasToBeStarted()\r\n\t\tthis.onUpdateShownItemIndexes({ reason: LAYOUT_REASON.MANUAL })\r\n\t}\r\n\r\n\t// Bind the function to `this` so that it could be passed as a callback\r\n\t// in a random application's code.\r\n\tonRender = () => {\r\n\t\tthis._onRender(this.getState(), this.previousState)\r\n\t}\r\n\r\n\t/**\r\n\t * Returns the items's top offset relative to the scrollable container's top edge.\r\n\t * @param {any} item — Item\r\n\t * @return {[number]} Returns the item's scroll Y position. Returns `undefined` if any of the previous items haven't been rendered yet.\r\n\t */\r\n\tgetItemScrollPosition(itemOrIndex) {\r\n\t\t// Item index.\r\n\t\tconst i = this._getItemIndexByItemOrIndex(itemOrIndex)\r\n\r\n\t\t// If the item wasn't found, the error was already reported,\r\n\t\t// so just return some \"sensible\" default value.\r\n\t\tif (i === undefined) {\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tconst itemTopOffsetInList = this.layout.getItemTopOffset(i)\r\n\t\tif (itemTopOffsetInList === undefined) {\r\n\t\t\treturn\r\n\t\t}\r\n\t\treturn this.getListTopOffsetInsideScrollableContainer() + itemTopOffsetInList\r\n\t}\r\n\r\n\t/**\r\n\t * @deprecated\r\n\t * `.onItemHeightChange()` has been renamed to `.onItemHeightDidChange()`.\r\n\t */\r\n\tonItemHeightChange(item) {\r\n\t\twarn('`.onItemHeightChange(item)` method was renamed to `.onItemHeightDidChange(item)`')\r\n\t\tthis.onItemHeightDidChange(item)\r\n\t}\r\n\r\n\t/**\r\n\t * Forces a re-measure of an item's height.\r\n\t * @param {any} item — Item. Legacy argument variant: Item index.\r\n\t */\r\n\tonItemHeightDidChange(itemOrIndex) {\r\n\t\t// See the comments in the `setItemState()` function below for the rationale\r\n\t\t// on why the `hasToBeStarted()` check was commented out.\r\n\t\t// this.hasToBeStarted()\r\n\t\tthis._onItemHeightDidChange(itemOrIndex)\r\n\t}\r\n\r\n\t/**\r\n\t * Updates an item's state in `state.itemStates[]`.\r\n\t * @param {any} item — Item. Legacy argument variant: Item index.\r\n\t * @param {any} newItemState — Item's new state\r\n\t */\r\n\tsetItemState(itemOrIndex, newItemState) {\r\n\t\t// There is an issue in React 18.2.0 when `useInsertionEffect()` doesn't run twice\r\n\t\t// on mount unlike `useLayoutEffect()` in \"strict\" mode. That causes a bug in a React\r\n\t\t// implementation of the `virtual-scroller`.\r\n\t\t// https://gitlab.com/catamphetamine/virtual-scroller/-/issues/33\r\n\t\t// https://github.com/facebook/react/issues/26320\r\n\t\t// A workaround for that bug is ignoring the second-initial run of the effects at mount.\r\n\t\t//\r\n\t\t// But in that case, if an `ItemComponent` calls `setItemState()` in `useLayoutEffect()`,\r\n\t\t// it could result in a bug.\r\n\t\t//\r\n\t\t// Consider a type of `useLayoutEffect()` that skips the initial mount:\r\n\t\t// `useLayoutEffectSkipInitialMount()`.\r\n\t\t// Suppose that effect is written in such a way that it only skips the first call of itself.\r\n\t\t// In that case, if React is run in \"strict\" mode, the effect will no longer work as expected\r\n\t\t// and it won't actually skip the initial mount and will be executed during the second initial run.\r\n\t\t// But the `VirtualScroller` itself has already implemented a workaround that prevents\r\n\t\t// its hooks from running twice on mount. This means that `useVirtualScrollerStartStop()`\r\n\t\t// of the React component would have already stopped the `VirtualScroller` by the time\r\n\t\t// `ItemComponent`'s incorrectly-behaving `useLayoutEffectSkipInitialMount()` effect is run,\r\n\t\t// resulting in an error: \"`VirtualScroller` hasn't been started\".\r\n\t\t//\r\n\t\t// The log when not in \"strict\" mode would be:\r\n\t\t//\r\n\t\t// * `useLayoutEffect()` is run in `ItemComponent` — skips the initial run.\r\n\t\t// * `useLayoutEffect()` is run in `useVirtualScrollerStartStop()`. It starts the `VirtualScroller`.\r\n\t\t// * Some dependency property gets updated inside `ItemComponent`.\r\n\t\t// * `useLayoutEffect()` is run in `ItemComponent` — no longer skips. Calls `setItemState()`.\r\n\t\t// * The `VirtualScroller` is started so it handles `setState()` correctly.\r\n\t\t//\r\n\t\t// The log when in \"strict\" mode would be:\r\n\t\t//\r\n\t\t// * `useLayoutEffect()` is run in `ItemComponent` — skips the initial run.\r\n\t\t// * `useLayoutEffect()` is run in `useVirtualScrollerStartStop()`. It starts the `VirtualScroller`.\r\n\t\t// * `useLayoutEffect()` is unmounted in `useVirtualScrollerStartStop()`. It stops the `VirtualScroller`.\r\n\t\t// * `useLayoutEffect()` is unmounted in `ItemComponent` — does nothing.\r\n\t\t// * `useLayoutEffect()` is run the second time in `ItemComponent` — no longer skips. Calls `setItemState()`.\r\n\t\t// * The `VirtualScroller` is stopped so it throws an error: \"`VirtualScroller` hasn't been started\".\r\n\t\t//\r\n\t\t// For that reason, the requirement of the `VirtualScroller` to be started was commented out.\r\n\t\t// Commenting it out wouldn't result in any potential bugs because the code would work correctly\r\n\t\t// in both cases.\r\n\t\t// this.hasToBeStarted()\r\n\t\tthis._setItemState(itemOrIndex, newItemState)\r\n\t}\r\n\r\n\t// (deprecated)\r\n\t// Use `.setItemState()` method name instead.\r\n\tonItemStateChange(item, newItemState) {\r\n\t\tthis.setItemState(item, newItemState)\r\n\t}\r\n\r\n\t/**\r\n\t * Updates `items`. For example, can prepend or append new items to the list.\r\n\t * @param {any[]} newItems\r\n\t * @param {boolean} [options.preserveScrollPositionOnPrependItems] — Set to `true` to enable \"restore scroll position after prepending items\" feature (could be useful when implementing \"Show previous items\" button).\r\n\t */\r\n\tsetItems(newItems, options = {}) {\r\n\t\tthis.hasToBeStarted()\r\n\t\treturn this._setItems(newItems, options)\r\n\t}\r\n}"],"mappings":"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EAAuC;QACtC;MACA;;MACD,OAAO,KAAK4B,yCAAL,KAAmDH,mBAA1D;IACA;IAED;AACD;AACA;AACA;;;;WACC,4BAAmBI,IAAnB,EAAyB;MACxB9D,IAAI,CAAC,kFAAD,CAAJ;MACA,KAAK+D,qBAAL,CAA2BD,IAA3B;IACA;IAED;AACD;AACA;AACA;;;;WACC,+BAAsBP,WAAtB,EAAmC;MAClC;MACA;MACA;MACA,KAAKS,sBAAL,CAA4BT,WAA5B;IACA;IAED;AACD;AACA;AACA;AACA;;;;WACC,sBAAaA,WAAb,EAA0BU,YAA1B,EAAwC;MACvC;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA,KAAKC,aAAL,CAAmBX,WAAnB,EAAgCU,YAAhC;IACA,C,CAED;IACA;;;;WACA,2BAAkBH,IAAlB,EAAwBG,YAAxB,EAAsC;MACrC,KAAKE,YAAL,CAAkBL,IAAlB,EAAwBG,YAAxB;IACA;IAED;AACD;AACA;AACA;AACA;;;;WACC,kBAASG,QAAT,EAAiC;MAAA,IAAd3D,OAAc,uEAAJ,EAAI;MAChC,KAAKS,cAAL;MACA,OAAO,KAAKmD,SAAL,CAAeD,QAAf,EAAyB3D,OAAzB,CAAP;IACA;;;;;;SA9SmBH,e"}
@@ -111,7 +111,7 @@ export default function () {
111
111
  // or an "Expand YouTube video" button, which would result
112
112
  // in the actual height of the list item being different
113
113
  // from what has been initially measured in `this.itemHeights[i]`,
114
- // if the developer didn't call `.setItemState(i, newState)` and `.onItemHeightDidChange(i)`.
114
+ // if the developer didn't call `.setItemState(item, newState)` and `.onItemHeightDidChange(item)`.
115
115
 
116
116
 
117
117
  if (!validateWillBeHiddenItemHeightsAreAccurate.call(this, firstShownItemIndex, lastShownItemIndex)) {
@@ -143,7 +143,7 @@ export default function () {
143
143
  log('Last shown item index', lastShownItemIndex);
144
144
  log('Before items height', beforeItemsHeight);
145
145
  log('After items height (actual or estimated)', afterItemsHeight);
146
- log('Average item height (used for estimated after items height calculation)', this.itemHeights.getAverage());
146
+ log('Average item height (used for estimated after items height calculation)', this.getAverageItemHeight());
147
147
 
148
148
  if (isDebug()) {
149
149
  log('Item heights', this.getState().itemHeights.slice());
@@ -179,9 +179,9 @@ export default function () {
179
179
  // Instead of using a `this.previouslyCalculatedLayout` instance variable,
180
180
  // this code could use `this.getState()` because it reflects what's currently on screen,
181
181
  // but there's a single edge case when it could go out of sync —
182
- // updating item heights externally via `.onItemHeightDidChange(i)`.
182
+ // updating item heights externally via `.onItemHeightDidChange(item)`.
183
183
  //
184
- // If, for example, an item height was updated externally via `.onItemHeightDidChange(i)`
184
+ // If, for example, an item height was updated externally via `.onItemHeightDidChange(item)`
185
185
  // then `this.getState().itemHeights` would get updated immediately but
186
186
  // `this.getState().beforeItemsHeight` or `this.getState().afterItemsHeight`
187
187
  // would still correspond to the previous item height, so those would be "stale".
@@ -269,7 +269,7 @@ export default function () {
269
269
  * or an "Expand YouTube video" button, which would result
270
270
  * in the actual height of the list item being different
271
271
  * from what has been initially measured in `this.itemHeights[i]`,
272
- * if the developer didn't call `.setItemState(i, newState)` and `.onItemHeightDidChange(i)`.
272
+ * if the developer didn't call `.setItemState(item, newState)` and `.onItemHeightDidChange(item)`.
273
273
  */
274
274
 
275
275
 
@@ -283,26 +283,26 @@ export default function () {
283
283
  // The item will be hidden. Re-measure its height.
284
284
  // The rationale is that there could be a situation when an item's
285
285
  // height has changed, and the developer has properly added an
286
- // `.onItemHeightDidChange(i)` call to notify `VirtualScroller`
286
+ // `.onItemHeightDidChange(item)` call to notify `VirtualScroller`
287
287
  // about that change, but at the same time that wouldn't work.
288
288
  // For example, suppose there's a list of several items on a page,
289
289
  // and those items are in "minimized" state (having height 100px).
290
290
  // Then, a user clicks an "Expand all items" button, and all items
291
291
  // in the list are expanded (expanded item height is gonna be 700px).
292
- // `VirtualScroller` demands that `.onItemHeightDidChange(i)` is called
292
+ // `VirtualScroller` demands that `.onItemHeightDidChange(item)` is called
293
293
  // in such cases, and the developer has properly added the code to do that.
294
294
  // So, if there were 10 "minimized" items visible on a page, then there
295
- // will be 10 individual `.onItemHeightDidChange(i)` calls. No issues so far.
296
- // But, as the first `.onItemHeightDidChange(i)` call executes, it immediately
295
+ // will be 10 individual `.onItemHeightDidChange(item)` calls. No issues so far.
296
+ // But, as the first `.onItemHeightDidChange(item)` call executes, it immediately
297
297
  // ("synchronously") triggers a re-layout, and that re-layout finds out
298
298
  // that now, because the first item is big, it occupies most of the screen
299
299
  // space, and only the first 3 items are visible on screen instead of 10,
300
300
  // and so it leaves the first 3 items mounted and unmounts the rest 7.
301
301
  // Then, after `VirtualScroller` has rerendered, the code returns to
302
- // where it was executing, and calls `.onItemHeightDidChange(i)` for the
302
+ // where it was executing, and calls `.onItemHeightDidChange(item)` for the
303
303
  // second item. It also triggers an immediate re-layout that finds out
304
304
  // that only the first 2 items are visible on screen, and it unmounts
305
- // the third one too. After that, it calls `.onItemHeightDidChange(i)`
305
+ // the third one too. After that, it calls `.onItemHeightDidChange(item)`
306
306
  // for the third item, but that item is no longer rendered, so its height
307
307
  // can't be measured, and the same's for all the rest of the original 10 items.
308
308
  // So, even though the developer has written their code properly, the
@@ -322,7 +322,7 @@ export default function () {
322
322
  }
323
323
 
324
324
  isValid = false;
325
- warn('Item index', i, 'is no longer visible and will be unmounted. Its height has changed from', previouslyMeasuredItemHeight, 'to', actualItemHeight, 'since it was last measured. This is not necessarily a bug, and could happen, for example, on screen width change, or when there\'re several `onItemHeightDidChange(i)` calls issued at the same time, and the first one triggers a re-layout before the rest of them have had a chance to be executed.');
325
+ warn('Item index', i, 'is no longer visible and will be unmounted. Its height has changed from', previouslyMeasuredItemHeight, 'to', actualItemHeight, 'since it was last measured. This is not necessarily a bug, and could happen, for example, on screen width change, or when there\'re several `onItemHeightDidChange(item)` calls issued at the same time, and the first one triggers a re-layout before the rest of them have had a chance to be executed.');
326
326
  }
327
327
  }
328
328
 
@@ -384,49 +384,57 @@ export default function () {
384
384
  return listTopOffset;
385
385
  };
386
386
 
387
- this._onItemHeightDidChange = function (i) {
388
- log('~ On Item Height Did Change was called ~');
389
- log('Item index', i);
387
+ this._onItemHeightDidChange = function (itemOrIndex) {
388
+ // Item index.
389
+ var i = _this._getItemIndexByItemOrIndex(itemOrIndex); // If the item wasn't found, the error was already reported,
390
+ // so just don't do anything else.
391
+
392
+
393
+ if (i === undefined) {
394
+ return;
395
+ }
390
396
 
391
397
  var _this$getState2 = _this.getState(),
392
398
  itemHeights = _this$getState2.itemHeights,
393
399
  firstShownItemIndex = _this$getState2.firstShownItemIndex,
394
- lastShownItemIndex = _this$getState2.lastShownItemIndex; // Check if the item is still rendered.
400
+ lastShownItemIndex = _this$getState2.lastShownItemIndex;
395
401
 
402
+ log('~ On Item Height Did Change was called ~');
403
+ log('Item index', i); // Check if the item is still rendered.
396
404
 
397
405
  if (!(i >= firstShownItemIndex && i <= lastShownItemIndex)) {
398
406
  // There could be valid cases when an item is no longer rendered
399
- // by the time `.onItemHeightDidChange(i)` gets called.
407
+ // by the time `.onItemHeightDidChange(item)` gets called.
400
408
  // For example, suppose there's a list of several items on a page,
401
409
  // and those items are in "minimized" state (having height 100px).
402
410
  // Then, a user clicks an "Expand all items" button, and all items
403
411
  // in the list are expanded (expanded item height is gonna be 700px).
404
- // `VirtualScroller` demands that `.onItemHeightDidChange(i)` is called
412
+ // `VirtualScroller` demands that `.onItemHeightDidChange(item)` is called
405
413
  // in such cases, and the developer has properly added the code to do that.
406
414
  // So, if there were 10 "minimized" items visible on a page, then there
407
- // will be 10 individual `.onItemHeightDidChange(i)` calls. No issues so far.
408
- // But, as the first `.onItemHeightDidChange(i)` call executes, it immediately
415
+ // will be 10 individual `.onItemHeightDidChange(item)` calls. No issues so far.
416
+ // But, as the first `.onItemHeightDidChange(item)` call executes, it immediately
409
417
  // ("synchronously") triggers a re-layout, and that re-layout finds out
410
418
  // that now, because the first item is big, it occupies most of the screen
411
419
  // space, and only the first 3 items are visible on screen instead of 10,
412
420
  // and so it leaves the first 3 items mounted and unmounts the rest 7.
413
421
  // Then, after `VirtualScroller` has rerendered, the code returns to
414
- // where it was executing, and calls `.onItemHeightDidChange(i)` for the
422
+ // where it was executing, and calls `.onItemHeightDidChange(item)` for the
415
423
  // second item. It also triggers an immediate re-layout that finds out
416
424
  // that only the first 2 items are visible on screen, and it unmounts
417
- // the third one too. After that, it calls `.onItemHeightDidChange(i)`
425
+ // the third one too. After that, it calls `.onItemHeightDidChange(item)`
418
426
  // for the third item, but that item is no longer rendered, so its height
419
427
  // can't be measured, and the same's for all the rest of the original 10 items.
420
428
  // So, even though the developer has written their code properly, there're
421
429
  // still situations when the item could be no longer rendered by the time
422
- // `.onItemHeightDidChange(i)` gets called.
423
- return warn('The item is no longer rendered. This is not necessarily a bug, and could happen, for example, when when a developer calls `onItemHeightDidChange(i)` while looping through a batch of items.');
430
+ // `.onItemHeightDidChange(item)` gets called.
431
+ return warn('The item is no longer rendered. This is not necessarily a bug, and could happen, for example, when when a developer calls `onItemHeightDidChange(item)` while looping through a batch of items.');
424
432
  }
425
433
 
426
434
  var previousHeight = itemHeights[i];
427
435
 
428
436
  if (previousHeight === undefined) {
429
- // There're valid cases when the item still hasn't been measured and `onItemHeightDidChange()`
437
+ // There're valid cases when the item still hasn't been measured and `onItemHeightDidChange(item)`
430
438
  // function was called for it. That's because measuring items is only done after the `VirtualScroller`
431
439
  // has `start()`ed. But it's not neccessarily `start()`ed by the time it has been rendered (mounted).
432
440
  // For example, the React component `<VirtualScroller/>` provides an `readyToStart={false}` property
@@ -446,7 +454,7 @@ export default function () {
446
454
  try {
447
455
  newHeight = remeasureItemHeight.call(_this, i);
448
456
  } catch (error) {
449
- // Successfully finishing an `onItemHeightDidChange(i)` call is not considered
457
+ // Successfully finishing an `onItemHeightDidChange(item)` call is not considered
450
458
  // critical for `VirtualScroller`'s operation, so such errors could be ignored.
451
459
  if (error instanceof ItemNotRenderedError) {
452
460
  return reportError("\"onItemHeightDidChange()\" has been called for item index ".concat(i, " but the item is not currently rendered and can't be measured. The exact error was: ").concat(error.message));
@@ -468,7 +476,7 @@ export default function () {
468
476
  updatePreviouslyCalculatedLayoutOnItemHeightChange.call(_this, i, previousHeight, newHeight); // Recalculate layout.
469
477
  //
470
478
  // If the `VirtualScroller` is already waiting for a state update to be rendered,
471
- // delay `onItemHeightDidChange(i)`'s re-layout until that state update is rendered.
479
+ // delay `onItemHeightDidChange(item)`'s re-layout until that state update is rendered.
472
480
  // The reason is that React `<VirtualScroller/>`'s `onHeightDidChange()` is meant to
473
481
  // be called inside `useLayoutEffect()` hook. Due to how React is implemented internally,
474
482
  // that might happen in the middle of the currently pending `setState()` operation
@@ -515,9 +523,12 @@ export default function () {
515
523
  // scroll past the screen height, because they'd stop to read through
516
524
  // the newly visible items first, and when they do stop scrolling, that's
517
525
  // when layout gets recalculated).
518
- var renderAheadMarginRatio = 1; // in scrollable container heights.
526
+ return _this.scrollableContainer.getHeight() * _this.getPrerenderMarginRatio();
527
+ };
519
528
 
520
- return _this.scrollableContainer.getHeight() * renderAheadMarginRatio;
529
+ this.getPrerenderMarginRatio = function () {
530
+ // See the readme for the description of `prerenderMarginRatio` option.
531
+ return 1; // in scrollable container heights.
521
532
  };
522
533
  /**
523
534
  * Calls `onItemFirstRender()` for items that haven't been