virtual-scroller 1.7.9 → 1.9.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 (283) hide show
  1. package/.gitlab-ci.yml +1 -1
  2. package/CHANGELOG.md +71 -1
  3. package/README.md +434 -151
  4. package/bundle/index-bypass.html +1 -1
  5. package/bundle/index-dom.html +1 -1
  6. package/bundle/index-grid.html +1 -2
  7. package/bundle/index-scrollableContainer.html +1 -1
  8. package/bundle/index-tbody-scrollableContainer.html +2 -0
  9. package/bundle/index-tbody.html +2 -0
  10. package/bundle/virtual-scroller-dom.js +1 -1
  11. package/bundle/virtual-scroller-dom.js.map +1 -1
  12. package/bundle/virtual-scroller-react.js +1 -1
  13. package/bundle/virtual-scroller-react.js.map +1 -1
  14. package/bundle/virtual-scroller.js +1 -1
  15. package/bundle/virtual-scroller.js.map +1 -1
  16. package/commonjs/BeforeResize.js +315 -0
  17. package/commonjs/BeforeResize.js.map +1 -0
  18. package/commonjs/DOM/Engine.js +46 -0
  19. package/commonjs/DOM/Engine.js.map +1 -0
  20. package/commonjs/DOM/ItemsContainer.js +78 -0
  21. package/commonjs/DOM/ItemsContainer.js.map +1 -0
  22. package/commonjs/DOM/{WaitForStylesToLoad.js → ListTopOffsetWatcher.js} +71 -44
  23. package/commonjs/DOM/ListTopOffsetWatcher.js.map +1 -0
  24. package/commonjs/DOM/ScrollableContainer.js +69 -101
  25. package/commonjs/DOM/ScrollableContainer.js.map +1 -1
  26. package/commonjs/DOM/VirtualScroller.js +37 -29
  27. package/commonjs/DOM/VirtualScroller.js.map +1 -1
  28. package/commonjs/DOM/tbody.js +17 -11
  29. package/commonjs/DOM/tbody.js.map +1 -1
  30. package/commonjs/ItemHeights.js +33 -34
  31. package/commonjs/ItemHeights.js.map +1 -1
  32. package/commonjs/Layout.js +591 -216
  33. package/commonjs/Layout.js.map +1 -1
  34. package/commonjs/Layout.test.js +196 -0
  35. package/commonjs/Layout.test.js.map +1 -0
  36. package/commonjs/ListHeightMeasurement.js +124 -0
  37. package/commonjs/ListHeightMeasurement.js.map +1 -0
  38. package/commonjs/Resize.js +50 -39
  39. package/commonjs/Resize.js.map +1 -1
  40. package/commonjs/Scroll.js +139 -95
  41. package/commonjs/Scroll.js.map +1 -1
  42. package/commonjs/VirtualScroller.columns.js +43 -0
  43. package/commonjs/VirtualScroller.columns.js.map +1 -0
  44. package/commonjs/VirtualScroller.constructor.js +408 -0
  45. package/commonjs/VirtualScroller.constructor.js.map +1 -0
  46. package/commonjs/VirtualScroller.items.js +305 -0
  47. package/commonjs/VirtualScroller.items.js.map +1 -0
  48. package/commonjs/VirtualScroller.js +160 -1021
  49. package/commonjs/VirtualScroller.js.map +1 -1
  50. package/commonjs/VirtualScroller.layout.js +562 -0
  51. package/commonjs/VirtualScroller.layout.js.map +1 -0
  52. package/commonjs/VirtualScroller.onRender.js +357 -0
  53. package/commonjs/VirtualScroller.onRender.js.map +1 -0
  54. package/commonjs/VirtualScroller.resize.js +186 -0
  55. package/commonjs/VirtualScroller.resize.js.map +1 -0
  56. package/commonjs/VirtualScroller.state.js +301 -0
  57. package/commonjs/VirtualScroller.state.js.map +1 -0
  58. package/commonjs/VirtualScroller.verticalSpacing.js +65 -0
  59. package/commonjs/VirtualScroller.verticalSpacing.js.map +1 -0
  60. package/commonjs/getItemCoordinates.js.map +1 -1
  61. package/commonjs/getItemsDiff.js.map +1 -1
  62. package/commonjs/getVerticalSpacing.js +8 -8
  63. package/commonjs/getVerticalSpacing.js.map +1 -1
  64. package/commonjs/package.json +5 -0
  65. package/commonjs/react/VirtualScroller.js +182 -628
  66. package/commonjs/react/VirtualScroller.js.map +1 -1
  67. package/commonjs/react/useClassName.js +26 -0
  68. package/commonjs/react/useClassName.js.map +1 -0
  69. package/commonjs/react/useHandleItemsChange.js +116 -0
  70. package/commonjs/react/useHandleItemsChange.js.map +1 -0
  71. package/commonjs/react/useInstanceMethods.js +37 -0
  72. package/commonjs/react/useInstanceMethods.js.map +1 -0
  73. package/commonjs/react/useItemKeys.js +60 -0
  74. package/commonjs/react/useItemKeys.js.map +1 -0
  75. package/commonjs/react/useOnItemHeightChange.js +32 -0
  76. package/commonjs/react/useOnItemHeightChange.js.map +1 -0
  77. package/commonjs/react/useOnItemStateChange.js +32 -0
  78. package/commonjs/react/useOnItemStateChange.js.map +1 -0
  79. package/commonjs/react/useState.js +140 -0
  80. package/commonjs/react/useState.js.map +1 -0
  81. package/commonjs/react/useStyle.js +29 -0
  82. package/commonjs/react/useStyle.js.map +1 -0
  83. package/commonjs/react/useVirtualScroller.js +62 -0
  84. package/commonjs/react/useVirtualScroller.js.map +1 -0
  85. package/commonjs/react/useVirtualScrollerStartStop.js +20 -0
  86. package/commonjs/react/useVirtualScrollerStartStop.js.map +1 -0
  87. package/commonjs/test/Engine.js +23 -0
  88. package/commonjs/test/Engine.js.map +1 -0
  89. package/commonjs/test/ItemsContainer.js +127 -0
  90. package/commonjs/test/ItemsContainer.js.map +1 -0
  91. package/commonjs/test/ScrollableContainer.js +130 -0
  92. package/commonjs/test/ScrollableContainer.js.map +1 -0
  93. package/commonjs/test/VirtualScroller.js +281 -0
  94. package/commonjs/test/VirtualScroller.js.map +1 -0
  95. package/commonjs/utility/debounce.js +28 -6
  96. package/commonjs/utility/debounce.js.map +1 -1
  97. package/commonjs/utility/debug.js +51 -12
  98. package/commonjs/utility/debug.js.map +1 -1
  99. package/commonjs/utility/getStateSnapshot.js +50 -0
  100. package/commonjs/utility/getStateSnapshot.js.map +1 -0
  101. package/commonjs/utility/px.js +1 -1
  102. package/commonjs/utility/px.js.map +1 -1
  103. package/commonjs/utility/px.test.js +14 -0
  104. package/commonjs/utility/px.test.js.map +1 -0
  105. package/commonjs/utility/shallowEqual.js +1 -1
  106. package/commonjs/utility/shallowEqual.js.map +1 -1
  107. package/commonjs/utility/throttle.js.map +1 -1
  108. package/dom/index.cjs +4 -0
  109. package/dom/index.cjs.js +9 -0
  110. package/dom/index.d.ts +25 -0
  111. package/dom/index.js +1 -1
  112. package/dom/package.json +10 -4
  113. package/index.cjs +4 -0
  114. package/index.cjs.js +9 -0
  115. package/index.d.ts +99 -0
  116. package/index.js +1 -1
  117. package/modules/BeforeResize.js +305 -0
  118. package/modules/BeforeResize.js.map +1 -0
  119. package/modules/DOM/Engine.js +27 -0
  120. package/modules/DOM/Engine.js.map +1 -0
  121. package/modules/DOM/ItemsContainer.js +71 -0
  122. package/modules/DOM/ItemsContainer.js.map +1 -0
  123. package/modules/DOM/{WaitForStylesToLoad.js → ListTopOffsetWatcher.js} +72 -44
  124. package/modules/DOM/ListTopOffsetWatcher.js.map +1 -0
  125. package/modules/DOM/ScrollableContainer.js +68 -100
  126. package/modules/DOM/ScrollableContainer.js.map +1 -1
  127. package/modules/DOM/VirtualScroller.js +32 -28
  128. package/modules/DOM/VirtualScroller.js.map +1 -1
  129. package/modules/DOM/tbody.js +11 -9
  130. package/modules/DOM/tbody.js.map +1 -1
  131. package/modules/ItemHeights.js +28 -33
  132. package/modules/ItemHeights.js.map +1 -1
  133. package/modules/Layout.js +585 -214
  134. package/modules/Layout.js.map +1 -1
  135. package/modules/Layout.test.js +190 -0
  136. package/modules/Layout.test.js.map +1 -0
  137. package/modules/ListHeightMeasurement.js +117 -0
  138. package/modules/ListHeightMeasurement.js.map +1 -0
  139. package/modules/Resize.js +50 -39
  140. package/modules/Resize.js.map +1 -1
  141. package/modules/Scroll.js +139 -94
  142. package/modules/Scroll.js.map +1 -1
  143. package/modules/VirtualScroller.columns.js +36 -0
  144. package/modules/VirtualScroller.columns.js.map +1 -0
  145. package/modules/VirtualScroller.constructor.js +371 -0
  146. package/modules/VirtualScroller.constructor.js.map +1 -0
  147. package/modules/VirtualScroller.items.js +288 -0
  148. package/modules/VirtualScroller.items.js.map +1 -0
  149. package/modules/VirtualScroller.js +159 -1014
  150. package/modules/VirtualScroller.js.map +1 -1
  151. package/modules/VirtualScroller.layout.js +549 -0
  152. package/modules/VirtualScroller.layout.js.map +1 -0
  153. package/modules/VirtualScroller.onRender.js +337 -0
  154. package/modules/VirtualScroller.onRender.js.map +1 -0
  155. package/modules/VirtualScroller.resize.js +176 -0
  156. package/modules/VirtualScroller.resize.js.map +1 -0
  157. package/modules/VirtualScroller.state.js +283 -0
  158. package/modules/VirtualScroller.state.js.map +1 -0
  159. package/modules/VirtualScroller.verticalSpacing.js +54 -0
  160. package/modules/VirtualScroller.verticalSpacing.js.map +1 -0
  161. package/modules/getItemCoordinates.js.map +1 -1
  162. package/modules/getItemsDiff.js.map +1 -1
  163. package/modules/getVerticalSpacing.js +8 -8
  164. package/modules/getVerticalSpacing.js.map +1 -1
  165. package/modules/react/VirtualScroller.js +179 -634
  166. package/modules/react/VirtualScroller.js.map +1 -1
  167. package/modules/react/useClassName.js +18 -0
  168. package/modules/react/useClassName.js.map +1 -0
  169. package/modules/react/useHandleItemsChange.js +108 -0
  170. package/modules/react/useHandleItemsChange.js.map +1 -0
  171. package/modules/react/useInstanceMethods.js +28 -0
  172. package/modules/react/useInstanceMethods.js.map +1 -0
  173. package/modules/react/useItemKeys.js +52 -0
  174. package/modules/react/useItemKeys.js.map +1 -0
  175. package/modules/react/useOnItemHeightChange.js +24 -0
  176. package/modules/react/useOnItemHeightChange.js.map +1 -0
  177. package/modules/react/useOnItemStateChange.js +24 -0
  178. package/modules/react/useOnItemStateChange.js.map +1 -0
  179. package/modules/react/useState.js +132 -0
  180. package/modules/react/useState.js.map +1 -0
  181. package/modules/react/useStyle.js +19 -0
  182. package/modules/react/useStyle.js.map +1 -0
  183. package/modules/react/useVirtualScroller.js +51 -0
  184. package/modules/react/useVirtualScroller.js.map +1 -0
  185. package/modules/react/useVirtualScrollerStartStop.js +12 -0
  186. package/modules/react/useVirtualScrollerStartStop.js.map +1 -0
  187. package/modules/test/Engine.js +11 -0
  188. package/modules/test/Engine.js.map +1 -0
  189. package/modules/test/ItemsContainer.js +120 -0
  190. package/modules/test/ItemsContainer.js.map +1 -0
  191. package/modules/test/ScrollableContainer.js +123 -0
  192. package/modules/test/ScrollableContainer.js.map +1 -0
  193. package/modules/test/VirtualScroller.js +270 -0
  194. package/modules/test/VirtualScroller.js.map +1 -0
  195. package/modules/utility/debounce.js +28 -6
  196. package/modules/utility/debounce.js.map +1 -1
  197. package/modules/utility/debug.js +47 -10
  198. package/modules/utility/debug.js.map +1 -1
  199. package/modules/utility/getStateSnapshot.js +43 -0
  200. package/modules/utility/getStateSnapshot.js.map +1 -0
  201. package/modules/utility/px.js +1 -1
  202. package/modules/utility/px.js.map +1 -1
  203. package/modules/utility/px.test.js +9 -0
  204. package/modules/utility/px.test.js.map +1 -0
  205. package/modules/utility/shallowEqual.js +1 -1
  206. package/modules/utility/shallowEqual.js.map +1 -1
  207. package/modules/utility/throttle.js.map +1 -1
  208. package/package.json +54 -29
  209. package/react/index.cjs +4 -0
  210. package/react/index.cjs.js +9 -0
  211. package/react/index.d.ts +28 -0
  212. package/react/index.js +1 -1
  213. package/react/package.json +10 -4
  214. package/rollup.config.mjs +62 -0
  215. package/runnable/create-commonjs-package-json.js +11 -0
  216. package/source/BeforeResize.js +312 -0
  217. package/source/DOM/Engine.js +30 -0
  218. package/source/DOM/ItemsContainer.js +48 -0
  219. package/source/DOM/{WaitForStylesToLoad.js → ListTopOffsetWatcher.js} +61 -30
  220. package/source/DOM/ScrollableContainer.js +51 -73
  221. package/source/DOM/VirtualScroller.js +33 -18
  222. package/source/DOM/tbody.js +30 -21
  223. package/source/ItemHeights.js +27 -27
  224. package/source/Layout.js +629 -252
  225. package/source/Layout.test.js +176 -0
  226. package/source/ListHeightMeasurement.js +95 -0
  227. package/source/Resize.js +56 -32
  228. package/source/Scroll.js +135 -82
  229. package/source/VirtualScroller.columns.js +26 -0
  230. package/source/VirtualScroller.constructor.js +336 -0
  231. package/source/VirtualScroller.items.js +302 -0
  232. package/source/VirtualScroller.js +162 -936
  233. package/source/VirtualScroller.layout.js +539 -0
  234. package/source/VirtualScroller.onRender.js +345 -0
  235. package/source/VirtualScroller.resize.js +189 -0
  236. package/source/VirtualScroller.state.js +284 -0
  237. package/source/VirtualScroller.verticalSpacing.js +51 -0
  238. package/source/getVerticalSpacing.js +7 -7
  239. package/source/react/VirtualScroller.js +243 -603
  240. package/source/react/useClassName.js +14 -0
  241. package/source/react/useHandleItemsChange.js +115 -0
  242. package/source/react/useInstanceMethods.js +25 -0
  243. package/source/react/useItemKeys.js +59 -0
  244. package/source/react/useOnItemHeightChange.js +28 -0
  245. package/source/react/useOnItemStateChange.js +28 -0
  246. package/source/react/useState.js +114 -0
  247. package/source/react/useStyle.js +20 -0
  248. package/source/react/useVirtualScroller.js +59 -0
  249. package/source/react/useVirtualScrollerStartStop.js +12 -0
  250. package/source/test/Engine.js +11 -0
  251. package/source/test/ItemsContainer.js +87 -0
  252. package/source/test/ScrollableContainer.js +88 -0
  253. package/source/test/VirtualScroller.js +232 -0
  254. package/source/utility/debounce.js +22 -5
  255. package/source/utility/debug.js +34 -3
  256. package/source/utility/getStateSnapshot.js +36 -0
  257. package/source/utility/px.js +1 -1
  258. package/source/utility/px.test.js +9 -0
  259. package/website/index-bypass.html +195 -0
  260. package/website/index-grid.html +0 -1
  261. package/website/index-scrollableContainer.html +208 -0
  262. package/website/index-tbody-scrollableContainer.html +68 -0
  263. package/website/index-tbody.html +55 -0
  264. package/commonjs/DOM/RenderingEngine.js +0 -33
  265. package/commonjs/DOM/RenderingEngine.js.map +0 -1
  266. package/commonjs/DOM/Screen.js +0 -87
  267. package/commonjs/DOM/Screen.js.map +0 -1
  268. package/commonjs/DOM/WaitForStylesToLoad.js.map +0 -1
  269. package/commonjs/RestoreScroll.js +0 -118
  270. package/commonjs/RestoreScroll.js.map +0 -1
  271. package/dom/index.commonjs.js +0 -4
  272. package/index.commonjs.js +0 -4
  273. package/modules/DOM/RenderingEngine.js +0 -19
  274. package/modules/DOM/RenderingEngine.js.map +0 -1
  275. package/modules/DOM/Screen.js +0 -80
  276. package/modules/DOM/Screen.js.map +0 -1
  277. package/modules/DOM/WaitForStylesToLoad.js.map +0 -1
  278. package/modules/RestoreScroll.js +0 -111
  279. package/modules/RestoreScroll.js.map +0 -1
  280. package/react/index.commonjs.js +0 -4
  281. package/source/DOM/RenderingEngine.js +0 -22
  282. package/source/DOM/Screen.js +0 -51
  283. package/source/RestoreScroll.js +0 -86
@@ -1,1007 +1,246 @@
1
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
1
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
2
+
3
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
2
4
 
3
5
  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
4
6
 
5
7
  function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
6
8
 
7
- function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
9
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
8
10
 
9
11
  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; }
10
12
 
11
- // For some weird reason, in Chrome, `setTimeout()` would lag up to a second (or more) behind.
12
- // Turns out, Chrome developers have deprecated `setTimeout()` API entirely without asking anyone.
13
- // Replacing `setTimeout()` with `requestAnimationFrame()` can work around that Chrome bug.
14
- // https://github.com/bvaughn/react-virtualized/issues/722
15
- import { setTimeout, clearTimeout } from 'request-animation-frame-timeout';
16
- import { supportsTbody, BROWSER_NOT_SUPPORTED_ERROR, addTbodyStyles, setTbodyPadding } from './DOM/tbody';
17
- import DOMRenderingEngine from './DOM/RenderingEngine';
18
- import WaitForStylesToLoad from './DOM/WaitForStylesToLoad';
19
- import Layout, { LAYOUT_REASON } from './Layout';
20
- import Resize from './Resize';
21
- import Scroll from './Scroll';
22
- import RestoreScroll from './RestoreScroll';
23
- import ItemHeights from './ItemHeights';
24
- import _getItemsDiff from './getItemsDiff';
25
- import getVerticalSpacing from './getVerticalSpacing'; // import getItemCoordinates from './getItemCoordinates'
26
-
27
- import log, { warn, isDebug, reportError } from './utility/debug';
28
- import shallowEqual from './utility/shallowEqual';
13
+ import VirtualScrollerConstructor from './VirtualScroller.constructor.js';
14
+ import { hasTbodyStyles, addTbodyStyles } from './DOM/tbody.js';
15
+ import { LAYOUT_REASON } from './Layout.js';
16
+ import log from './utility/debug.js';
29
17
 
30
- var VirtualScroller =
31
- /*#__PURE__*/
32
- function () {
18
+ var VirtualScroller = /*#__PURE__*/function () {
33
19
  /**
34
- * @param {function} getContainerElement — Returns the container DOM `Element`.
20
+ * @param {function} getItemsContainerElement — Returns the container DOM `Element`.
35
21
  * @param {any[]} items — The list of items.
36
22
  * @param {Object} [options] — See README.md.
37
23
  * @return {VirtualScroller}
38
24
  */
39
- function VirtualScroller(getContainerElement, items) {
25
+ function VirtualScroller(getItemsContainerElement, items) {
40
26
  var _this = this;
41
27
 
42
28
  var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
43
29
 
44
30
  _classCallCheck(this, VirtualScroller);
45
31
 
46
- _defineProperty(this, "getListTopOffsetInsideScrollableContainer", function () {
47
- var listTopOffset = _this.scrollableContainer.getTopOffset(_this.getContainerElement());
48
-
49
- _this.waitForStylesToLoad.onGotListTopOffset(listTopOffset);
50
-
51
- return listTopOffset;
52
- });
53
-
54
- _defineProperty(this, "willUpdateState", function (newState, prevState) {
55
- // Ignore setting initial state.
56
- if (!prevState) {
57
- return;
58
- } // This function isn't currently used.
59
- // Was previously used to capture scroll position in order to
60
- // restore it later after the new state is rendered.
61
-
62
- });
63
-
64
- _defineProperty(this, "didUpdateState", function (prevState) {
65
- var newState = _this.getState();
66
-
67
- if (_this.onStateChange) {
68
- if (!shallowEqual(newState, prevState)) {
69
- _this.onStateChange(newState, prevState);
70
- }
71
- } // Ignore setting initial state.
72
-
73
-
74
- if (!prevState) {
75
- return;
76
- }
77
-
78
- if (!_this.isRendered) {
79
- return;
80
- }
81
-
82
- log('~ Rendered ~');
83
- _this.newItemsPending = undefined;
84
- _this.layoutResetPending = undefined;
85
- var redoLayoutReason = _this.redoLayoutReason;
86
- _this.redoLayoutReason = undefined;
87
- var previousItems = prevState.items;
88
- var newItems = newState.items;
89
-
90
- if (newItems !== previousItems) {
91
- var layoutNeedsReCalculating = true;
92
-
93
- var itemsDiff = _this.getItemsDiff(previousItems, newItems); // If it's an "incremental" update.
94
-
95
-
96
- if (itemsDiff) {
97
- var prependedItemsCount = itemsDiff.prependedItemsCount,
98
- appendedItemsCount = itemsDiff.appendedItemsCount;
99
-
100
- if (prependedItemsCount > 0) {
101
- // The call to `.onPrepend()` must precede
102
- // the call to `.measureItemHeights()`
103
- // which is called in `.onRendered()`.
104
- _this.itemHeights.onPrepend(prependedItemsCount);
105
-
106
- if (_this.restoreScroll.shouldRestoreScrollAfterRender()) {
107
- layoutNeedsReCalculating = false;
108
- log('~ Restore Scroll Position ~');
109
-
110
- var scrollByY = _this.restoreScroll.getScrollDifference();
111
-
112
- if (scrollByY) {
113
- log('Scroll down by', scrollByY);
114
-
115
- _this.scroll.scrollByY(scrollByY);
116
- } else {
117
- log('Scroll position hasn\'t changed');
118
- }
119
- }
120
- }
121
- } else {
122
- _this.itemHeights.reset();
123
-
124
- _this.itemHeights.initialize(_this.getState().itemHeights);
125
- }
126
-
127
- if (layoutNeedsReCalculating) {
128
- redoLayoutReason = LAYOUT_REASON.ITEMS_CHANGED;
129
- }
130
- } // Call `.onRendered()` if shown items configuration changed.
131
-
132
-
133
- if (newState.firstShownItemIndex !== prevState.firstShownItemIndex || newState.lastShownItemIndex !== prevState.lastShownItemIndex || newState.items !== prevState.items) {
134
- _this.onRenderedNewLayout();
135
- }
136
-
137
- if (redoLayoutReason) {
138
- return _this.redoLayoutRightAfterRender({
139
- reason: redoLayoutReason
140
- });
32
+ _defineProperty(this, "stop", function () {
33
+ if (!_this._isActive) {
34
+ throw new Error('[virtual-scroller] Can\'t stop a `VirtualScroller` that hasn\'t been started');
141
35
  }
142
- });
143
-
144
- _defineProperty(this, "updateShownItemIndexes", function () {
145
- log('~ Layout results ' + (_this.bypass ? '(bypass) ' : '') + '~');
146
-
147
- var visibleAreaIncludingMargins = _this.getVisibleAreaBoundsIncludingMargins();
148
-
149
- _this.latestLayoutVisibleAreaIncludingMargins = visibleAreaIncludingMargins;
150
-
151
- var listTopOffsetInsideScrollableContainer = _this.getListTopOffsetInsideScrollableContainer(); // Get shown item indexes.
152
-
153
-
154
- var _this$layout$getShown = _this.layout.getShownItemIndexes({
155
- listHeight: _this.screen.getElementHeight(_this.getContainerElement()),
156
- itemsCount: _this.getItemsCount(),
157
- visibleAreaIncludingMargins: visibleAreaIncludingMargins,
158
- listTopOffsetInsideScrollableContainer: listTopOffsetInsideScrollableContainer
159
- }),
160
- firstShownItemIndex = _this$layout$getShown.firstShownItemIndex,
161
- lastShownItemIndex = _this$layout$getShown.lastShownItemIndex,
162
- redoLayoutAfterMeasuringItemHeights = _this$layout$getShown.redoLayoutAfterMeasuringItemHeights; // If scroll position is scheduled to be restored after render,
163
- // then the "anchor" item must be rendered, and all of the prepended
164
- // items before it, all in a single pass. This way, all of the
165
- // prepended items' heights could be measured right after the render
166
- // has finished, and the scroll position can then be immediately restored.
167
-
168
-
169
- if (_this.restoreScroll.shouldRestoreScrollAfterRender()) {
170
- if (lastShownItemIndex < _this.restoreScroll.getAnchorItemIndex()) {
171
- lastShownItemIndex = _this.restoreScroll.getAnchorItemIndex();
172
- } // `firstShownItemIndex` is always `0` when prepending items.
173
- // And `lastShownItemIndex` always covers all prepended items in this case.
174
- // None of the prepended items have been rendered before,
175
- // so their heights are unknown. The code at the start of this function
176
- // did therefore set `redoLayoutAfterMeasuringItemHeights` to `true`
177
- // in order to render just the first prepended item in order to
178
- // measure it, and only then make a decision on how many other
179
- // prepended items to render. But since we've instructed the code
180
- // to show all of the prepended items at once, there's no need to
181
- // "redo layout after render". Additionally, if layout was re-done
182
- // after render, then there would be a short interval of visual
183
- // "jitter" due to the scroll position not being restored because it'd
184
- // wait for the second layout to finish instead of being restored
185
- // right after the first one.
186
-
187
-
188
- redoLayoutAfterMeasuringItemHeights = false;
189
- } // Validate the heights of items to be hidden on next render.
190
- // For example, a user could click a "Show more" button,
191
- // or an "Expand YouTube video" button, which would result
192
- // in the actual height of the list item being different
193
- // from what has been initially measured in `this.itemHeights[i]`,
194
- // if the developer didn't call `.onItemStateChange()` and `.onItemHeightChange(i)`.
195
-
196
36
 
197
- if (!_this.validateWillBeHiddenItemHeightsAreAccurate(firstShownItemIndex, lastShownItemIndex)) {
198
- // Redo layout, now with the correct item heights.
199
- log('~ Some of the will-be-hidden item heights have changed since they\'ve last been measured. Redo layout. ~');
200
- return _this.updateShownItemIndexes();
201
- } // Measure "before" items height.
37
+ _this._isActive = false;
38
+ log('~ Stop ~');
202
39
 
40
+ _this.resize.stop();
203
41
 
204
- var beforeItemsHeight = _this.layout.getBeforeItemsHeight(firstShownItemIndex, lastShownItemIndex); // Measure "after" items height.
42
+ _this.scroll.stop(); // Stop `ListTopOffsetWatcher` if it has been started.
43
+ // There seems to be no need to restart `ListTopOffsetWatcher`.
44
+ // It's mainly a hacky workaround for development mode anyway.
205
45
 
206
46
 
207
- var afterItemsHeight = _this.layout.getAfterItemsHeight(firstShownItemIndex, lastShownItemIndex, _this.getItemsCount()); // Debugging.
47
+ if (_this.listTopOffsetWatcher && _this.listTopOffsetWatcher.isStarted()) {
48
+ _this.listTopOffsetWatcher.stop();
49
+ } // Cancel any scheduled layout.
208
50
 
209
51
 
210
- if (_this._getColumnsCount) {
211
- log('Columns count', _this.getColumnsCount());
212
- }
213
-
214
- log('First shown item index', firstShownItemIndex);
215
- log('Last shown item index', lastShownItemIndex);
216
- log('Before items height', beforeItemsHeight);
217
- log('After items height (actual or estimated)', afterItemsHeight);
218
- log('Average item height (calculated on previous render)', _this.itemHeights.getAverage());
219
-
220
- if (isDebug()) {
221
- log('Item heights', _this.getState().itemHeights.slice());
222
- log('Item states', _this.getState().itemStates.slice());
223
- }
224
-
225
- if (redoLayoutAfterMeasuringItemHeights) {
226
- // `this.redoLayoutReason` will be detected in `didUpdateState()`.
227
- // `didUpdateState()` is triggered by `this.setState()` below.
228
- _this.redoLayoutReason = LAYOUT_REASON.ITEM_HEIGHT_NOT_MEASURED;
229
- } // Optionally preload items to be rendered.
230
-
231
-
232
- _this.onBeforeShowItems(_this.getState().items, _this.getState().itemHeights, firstShownItemIndex, lastShownItemIndex); // Render.
233
-
234
-
235
- _this.setState({
236
- firstShownItemIndex: firstShownItemIndex,
237
- lastShownItemIndex: lastShownItemIndex,
238
- beforeItemsHeight: beforeItemsHeight,
239
- afterItemsHeight: afterItemsHeight // // Average item height is stored in state to differentiate between
240
- // // the initial state and "anything has been measured already" state.
241
- // averageItemHeight: this.itemHeights.getAverage()
242
-
243
- });
244
- });
245
-
246
- _defineProperty(this, "onUpdateShownItemIndexes", function (_ref) {
247
- var reason = _ref.reason;
248
-
249
- // If there're no items then there's no need to re-layout anything.
250
- if (_this.getItemsCount() === 0) {
251
- return;
252
- } // Cancel a "re-layout when user stops scrolling" timer.
253
-
254
-
255
- _this.scroll.onLayout(); // Cancel a re-layout that is scheduled to run at the next "frame",
256
- // because a re-layout will be performed right now.
257
-
258
-
259
- if (_this.layoutTimer) {
260
- clearTimeout(_this.layoutTimer);
261
- _this.layoutTimer = undefined;
262
- } // Perform a re-layout.
263
-
264
-
265
- log("~ Calculate Layout (on ".concat(reason, ") ~"));
266
-
267
- _this.updateShownItemIndexes();
52
+ _this.cancelLayoutTimer({});
268
53
  });
269
54
 
270
55
  _defineProperty(this, "updateLayout", function () {
271
- return _this.onUpdateShownItemIndexes({
56
+ _this.hasToBeStarted();
57
+
58
+ _this.onUpdateShownItemIndexes({
272
59
  reason: LAYOUT_REASON.MANUAL
273
60
  });
274
61
  });
275
62
 
276
- _defineProperty(this, "layout", function () {
277
- return _this.updateLayout();
63
+ _defineProperty(this, "onRender", function () {
64
+ _this._onRender(_this.getState(), _this.previousState);
278
65
  });
279
66
 
280
- var getState = options.getState,
281
- setState = options.setState,
282
- onStateChange = options.onStateChange,
283
- customState = options.customState,
284
- preserveScrollPositionAtBottomOnMount = options.preserveScrollPositionAtBottomOnMount,
285
- preserveScrollPositionOfTheBottomOfTheListOnMount = options.preserveScrollPositionOfTheBottomOfTheListOnMount,
286
- initialScrollPosition = options.initialScrollPosition,
287
- onScrollPositionChange = options.onScrollPositionChange,
288
- measureItemsBatchSize = options.measureItemsBatchSize,
289
- getScrollableContainer = options.getScrollableContainer,
290
- getColumnsCount = options.getColumnsCount,
291
- getItemId = options.getItemId,
292
- tbody = options.tbody,
293
- _useTimeoutInRenderLoop = options._useTimeoutInRenderLoop;
294
- var bypass = options.bypass,
295
- estimatedItemHeight = options.estimatedItemHeight,
296
- onItemInitialRender = options.onItemInitialRender,
297
- onItemFirstRender = options.onItemFirstRender,
298
- scrollableContainer = options.scrollableContainer,
299
- state = options.state,
300
- renderingEngine = options.renderingEngine;
301
- log('~ Initialize ~'); // If `state` is passed then use `items` from `state`
302
- // instead of the `items` argument.
303
-
304
- if (state) {
305
- items = state.items;
306
- } // `getScrollableContainer` option is deprecated.
307
- // Use `scrollableContainer` instead.
308
-
309
-
310
- if (!scrollableContainer && getScrollableContainer) {
311
- scrollableContainer = getScrollableContainer();
312
- } // Could support non-DOM rendering engines.
313
- // For example, React Native, `<canvas/>`, etc.
314
-
315
-
316
- if (!renderingEngine) {
317
- renderingEngine = DOMRenderingEngine;
318
- }
319
-
320
- this.screen = renderingEngine.createScreen();
321
- this.scrollableContainer = renderingEngine.createScrollableContainer(scrollableContainer); // if (margin === undefined) {
322
- // // Renders items which are outside of the screen by this "margin".
323
- // // Is the screen height by default: seems to be the optimal value
324
- // // for "Page Up" / "Page Down" navigation and optimized mouse wheel scrolling.
325
- // margin = this.scrollableContainer ? this.scrollableContainer.getHeight() : 0
326
- // }
327
- // Work around `<tbody/>` not being able to have `padding`.
328
- // https://gitlab.com/catamphetamine/virtual-scroller/-/issues/1
329
-
330
- if (tbody) {
331
- if (renderingEngine.name !== 'DOM') {
332
- throw new Error('`tbody` option is only supported for DOM rendering engine');
333
- }
334
-
335
- log('~ <tbody/> detected ~');
336
- this.tbody = true;
337
-
338
- if (!supportsTbody()) {
339
- log('~ <tbody/> not supported ~');
340
- reportError(BROWSER_NOT_SUPPORTED_ERROR);
341
- bypass = true;
342
- }
343
- }
344
-
345
- if (bypass) {
346
- log('~ "bypass" mode ~');
347
- } // In `bypass` mode, `VirtualScroller` doesn't wait
348
- // for the user to scroll down to render all items:
349
- // instead, it renders all items right away, as if
350
- // the list is rendered without using `VirtualScroller`.
351
- // It was added just to measure how much is the
352
- // performance difference between using a `VirtualScroller`
353
- // and not using a `VirtualScroller`.
354
- // It turned out that unmounting large React component trees
355
- // is a very long process, so `VirtualScroller` does seem to
356
- // make sense when used in a React application.
357
-
358
-
359
- this.bypass = bypass; // this.bypassBatchSize = bypassBatchSize || 10
360
- // Using `setTimeout()` in render loop is a workaround
361
- // for avoiding a React error message:
362
- // "Maximum update depth exceeded.
363
- // This can happen when a component repeatedly calls
364
- // `.setState()` inside `componentWillUpdate()` or `componentDidUpdate()`.
365
- // React limits the number of nested updates to prevent infinite loops."
366
-
367
- this._useTimeoutInRenderLoop = _useTimeoutInRenderLoop;
368
-
369
- if (getItemId) {
370
- this.isItemEqual = function (a, b) {
371
- return getItemId(a) === getItemId(b);
372
- };
373
- } else {
374
- this.isItemEqual = function (a, b) {
375
- return a === b;
376
- };
377
- }
378
-
379
- this.initialItems = items; // this.margin = margin
380
-
381
- this.onStateChange = onStateChange;
382
- this._getColumnsCount = getColumnsCount;
383
-
384
- if (onItemInitialRender) {
385
- this.onItemInitialRender = onItemInitialRender;
386
- } // `onItemFirstRender(i)` is deprecated, use `onItemInitialRender(item)` instead.
387
- else if (onItemFirstRender) {
388
- this.onItemInitialRender = function (item) {
389
- warn('`onItemFirstRender(i)` is deprecated, use `onItemInitialRender(item)` instead.');
390
-
391
- var _this$getState = _this.getState(),
392
- items = _this$getState.items;
393
-
394
- var i = items.indexOf(item); // The `item` could also be non-found due to the inconsistency bug:
395
- // The reason is that `i` can be non-consistent with the `items`
396
- // passed to `<VirtualScroller/>` in React due to `setState()` not being
397
- // instanteneous: when new `items` are passed to `<VirtualScroller/>`,
398
- // `VirtualScroller.setState({ items })` is called, and if `onItemFirstRender(i)`
399
- // is called after the aforementioned `setState()` is called but before it finishes,
400
- // `i` would point to an index in "previous" `items` while the application
401
- // would assume that `i` points to an index in the "new" `items`,
402
- // resulting in an incorrect item being assumed by the application
403
- // or even in an "array index out of bounds" error.
404
-
405
- if (i >= 0) {
406
- onItemFirstRender(i);
407
- }
408
- };
409
- }
410
-
411
- log('Items count', items.length);
412
-
413
- if (estimatedItemHeight) {
414
- log('Estimated item height', estimatedItemHeight);
415
- }
416
-
417
- if (setState) {
418
- this.getState = getState;
419
-
420
- this.setState = function (state) {
421
- log('Set state', state);
422
- setState(state, {
423
- willUpdateState: _this.willUpdateState,
424
- didUpdateState: _this.didUpdateState
425
- });
426
- };
427
- } else {
428
- this.getState = function () {
429
- return _this.state;
430
- };
431
-
432
- this.setState = function (state) {
433
- log('Set state', state);
434
-
435
- var prevState = _this.getState(); // Because this variant of `.setState()` is "synchronous" (immediate),
436
- // it can be written like `...prevState`, and no state updates would be lost.
437
- // But if it was "asynchronous" (not immediate), then `...prevState`
438
- // wouldn't work in all cases, because it could be stale in cases
439
- // when more than a single `setState()` call is made before
440
- // the state actually updates, making `prevState` stale.
441
-
442
-
443
- var newState = _objectSpread({}, prevState, state);
444
-
445
- _this.willUpdateState(newState, prevState);
446
-
447
- _this.state = newState;
448
-
449
- _this.didUpdateState(prevState);
450
- };
451
- }
452
-
453
- if (state) {
454
- log('Initial state (passed)', state);
455
- } // Sometimes, when `new VirtualScroller()` instance is created,
456
- // `getContainerElement()` might not be ready to return the "container" DOM Element yet
457
- // (for example, because it's not rendered yet). That's the reason why it's a getter function.
458
- // For example, in React `<VirtualScroller/>` component, a `VirtualScroller`
459
- // instance is created in the React component's `constructor()`, and at that time
460
- // the container Element is not yet available. The container Element is available
461
- // in `componentDidMount()`, but `componentDidMount()` is not executed on server,
462
- // which would mean that React `<VirtualScroller/>` wouldn't render at all
463
- // on server side, while with the `getContainerElement()` approach, on server side,
464
- // it still "renders" a list with a predefined amount of items in it by default.
465
- // (`initiallyRenderedItemsCount`, or `1`).
466
-
467
-
468
- this.getContainerElement = getContainerElement; // Remove any accidental text nodes from container (like whitespace).
469
- // Also guards against cases when someone accidentally tries
470
- // using `VirtualScroller` on a non-empty element.
471
-
472
- if (getContainerElement()) {
473
- this.screen.clearElement(getContainerElement());
474
- }
475
-
476
- this.itemHeights = new ItemHeights(this.screen, this.getContainerElement, function (i) {
477
- return _this.getState().itemHeights[i];
478
- }, function (i, height) {
479
- return _this.getState().itemHeights[i] = height;
480
- }); // Initialize `ItemHeights` from the initially passed `state`.
481
-
482
- if (state) {
483
- this.itemHeights.initialize(state.itemHeights);
484
- }
485
-
486
- this.layout = new Layout({
487
- bypass: bypass,
488
- estimatedItemHeight: estimatedItemHeight,
489
- measureItemsBatchSize: measureItemsBatchSize === undefined ? 50 : measureItemsBatchSize,
490
- getVerticalSpacing: function getVerticalSpacing() {
491
- return _this.getVerticalSpacing();
492
- },
493
- getColumnsCount: function getColumnsCount() {
494
- return _this.getColumnsCount();
495
- },
496
- getItemHeight: function getItemHeight(i) {
497
- return _this.getState().itemHeights[i];
498
- },
499
- getAverageItemHeight: function getAverageItemHeight() {
500
- return _this.itemHeights.getAverage();
501
- }
502
- });
503
- this.resize = new Resize({
504
- bypass: bypass,
505
- scrollableContainer: this.scrollableContainer,
506
- getContainerElement: this.getContainerElement,
507
- updateLayout: function updateLayout(_ref2) {
508
- var reason = _ref2.reason;
509
- return _this.onUpdateShownItemIndexes({
510
- reason: reason
511
- });
512
- },
513
- resetStateAndLayout: function resetStateAndLayout() {
514
- // Reset item heights, because if scrollable container's width (or height)
515
- // has changed, then the list width (or height) most likely also has changed,
516
- // and also some CSS `@media()` rules might have been added or removed.
517
- // So re-render the list entirely.
518
- log('~ Scrollable container size changed, re-measure item heights. ~');
519
- _this.redoLayoutReason = LAYOUT_REASON.RESIZE; // `this.layoutResetPending` flag will be cleared in `didUpdateState()`.
520
-
521
- _this.layoutResetPending = true;
522
- log('Reset state'); // Calling `this.setState(state)` will trigger `didUpdateState()`.
523
- // `didUpdateState()` will detect `this.redoLayoutReason`.
524
-
525
- _this.setState(_this.getInitialLayoutState(_this.newItemsPending || _this.getState().items));
526
- }
527
- });
528
-
529
- if (preserveScrollPositionAtBottomOnMount) {
530
- warn('`preserveScrollPositionAtBottomOnMount` option/property has been renamed to `preserveScrollPositionOfTheBottomOfTheListOnMount`');
531
- }
532
-
533
- this.preserveScrollPositionOfTheBottomOfTheListOnMount = preserveScrollPositionOfTheBottomOfTheListOnMount || preserveScrollPositionAtBottomOnMount;
534
- this.scroll = new Scroll({
535
- bypass: this.bypass,
536
- scrollableContainer: this.scrollableContainer,
537
- updateLayout: function updateLayout(_ref3) {
538
- var reason = _ref3.reason;
539
- return _this.onUpdateShownItemIndexes({
540
- reason: reason
541
- });
542
- },
543
- initialScrollPosition: initialScrollPosition,
544
- onScrollPositionChange: onScrollPositionChange,
545
- isImmediateLayoutScheduled: function isImmediateLayoutScheduled() {
546
- return _this.layoutTimer;
547
- },
548
- hasNonRenderedItemsAtTheTop: function hasNonRenderedItemsAtTheTop() {
549
- return _this.getState().firstShownItemIndex > 0;
550
- },
551
- hasNonRenderedItemsAtTheBottom: function hasNonRenderedItemsAtTheBottom() {
552
- return _this.getState().lastShownItemIndex < _this.getItemsCount() - 1;
553
- },
554
- getLatestLayoutVisibleAreaIncludingMargins: function getLatestLayoutVisibleAreaIncludingMargins() {
555
- return _this.latestLayoutVisibleAreaIncludingMargins;
556
- },
557
- preserveScrollPositionOfTheBottomOfTheListOnMount: this.preserveScrollPositionOfTheBottomOfTheListOnMount
558
- });
559
- this.restoreScroll = new RestoreScroll({
560
- screen: this.screen,
561
- getContainerElement: this.getContainerElement
562
- });
563
- this.waitForStylesToLoad = new WaitForStylesToLoad({
564
- updateLayout: function updateLayout(_ref4) {
565
- var reason = _ref4.reason;
566
- return _this.onUpdateShownItemIndexes({
567
- reason: reason
568
- });
569
- },
570
- getListTopOffsetInsideScrollableContainer: this.getListTopOffsetInsideScrollableContainer
571
- });
572
- this.setState(state || this.getInitialState(customState));
67
+ VirtualScrollerConstructor.call(this, getItemsContainerElement, items, options);
573
68
  }
574
69
  /**
575
- * Returns the initial state of the `VirtualScroller`.
576
- * @param {object} [customState] — Any additional "custom" state may be stored in `VirtualScroller`'s state. For example, React implementation stores item "refs" as "custom" state.
577
- * @return {object}
70
+ * Should be invoked after a "container" DOM Element is mounted (inserted into the DOM tree).
578
71
  */
579
72
 
580
73
 
581
74
  _createClass(VirtualScroller, [{
582
- key: "getInitialState",
583
- value: function getInitialState(customState) {
584
- var items = this.initialItems;
75
+ key: "start",
76
+ value: function start() {
77
+ if (this._isActive) {
78
+ throw new Error('[virtual-scroller] `VirtualScroller` has already been started');
79
+ } // If has been stopped previously.
585
80
 
586
- var state = _objectSpread({}, customState, this.getInitialLayoutState(items), {
587
- items: items,
588
- itemStates: new Array(items.length)
589
- });
590
81
 
591
- log('Initial state (autogenerated)', state);
592
- log('First shown item index', state.firstShownItemIndex);
593
- log('Last shown item index', state.lastShownItemIndex);
594
- return state;
595
- }
596
- }, {
597
- key: "getInitialLayoutValues",
598
- value: function getInitialLayoutValues(_ref5) {
599
- var itemsCount = _ref5.itemsCount,
600
- bypass = _ref5.bypass;
601
- return this.layout.getInitialLayoutValues({
602
- bypass: bypass,
603
- itemsCount: itemsCount,
604
- visibleAreaHeightIncludingMargins: this.scrollableContainer && 2 * this.getMargin() + this.scrollableContainer.getHeight()
605
- });
606
- }
607
- }, {
608
- key: "getInitialLayoutState",
609
- value: function getInitialLayoutState(items) {
610
- var itemsCount = items.length;
611
-
612
- var _this$getInitialLayou = this.getInitialLayoutValues({
613
- itemsCount: itemsCount,
614
- bypass: this.preserveScrollPositionOfTheBottomOfTheListOnMount
615
- }),
616
- firstShownItemIndex = _this$getInitialLayou.firstShownItemIndex,
617
- lastShownItemIndex = _this$getInitialLayou.lastShownItemIndex,
618
- beforeItemsHeight = _this$getInitialLayou.beforeItemsHeight,
619
- afterItemsHeight = _this$getInitialLayou.afterItemsHeight;
82
+ var isRestart = this._isActive === false;
620
83
 
621
- var itemHeights = new Array(itemsCount); // Optionally preload items to be rendered.
84
+ if (!isRestart) {
85
+ // If no custom one has been configured, uses the default one.
86
+ // Also sets the initial state.
87
+ if (!this._usesCustomStateStorage) {
88
+ this.useDefaultStateStorage();
89
+ } // If `render()` function parameter was passed,
90
+ // perform an initial render.
622
91
 
623
- this.onBeforeShowItems(items, itemHeights, firstShownItemIndex, lastShownItemIndex); // This "initial" state object must include all possible state properties
624
- // because `this.setState()` gets called with this state on window resize,
625
- // when `VirtualScroller` gets reset.
626
- // Item states aren't included here because the state of all items should be
627
- // preserved on window resize.
628
92
 
629
- return {
630
- itemHeights: itemHeights,
631
- columnsCount: this._getColumnsCount ? this._getColumnsCount(this.scrollableContainer) : undefined,
632
- verticalSpacing: undefined,
633
- firstShownItemIndex: firstShownItemIndex,
634
- lastShownItemIndex: lastShownItemIndex,
635
- beforeItemsHeight: beforeItemsHeight,
636
- afterItemsHeight: afterItemsHeight
637
- };
638
- }
639
- }, {
640
- key: "getVerticalSpacing",
641
- value: function getVerticalSpacing() {
642
- return this.getState() && this.getState().verticalSpacing || 0;
643
- }
644
- }, {
645
- key: "getColumnsCount",
646
- value: function getColumnsCount() {
647
- return this.getState() && this.getState().columnsCount || 1;
648
- }
649
- }, {
650
- key: "getItemsCount",
651
- value: function getItemsCount() {
652
- return this.getState().items.length;
653
- }
654
- }, {
655
- key: "getMargin",
656
- value: function getMargin() {
657
- // `VirtualScroller` also items that are outside of the screen
658
- // by the amount of this "render ahead margin" (both on top and bottom).
659
- // The default "render ahead margin" is equal to the screen height:
660
- // this seems to be the optimal value for "Page Up" / "Page Down" navigation
661
- // and optimized mouse wheel scrolling (a user is unlikely to continuously
662
- // scroll past the height of a screen, and when they stop scrolling,
663
- // the list is re-rendered).
664
- var renderAheadMarginRatio = 1; // in scrollable container heights.
665
-
666
- return this.scrollableContainer.getHeight() * renderAheadMarginRatio;
667
- }
668
- /**
669
- * Calls `onItemFirstRender()` for items that haven't been
670
- * "seen" previously.
671
- * @param {any[]} items
672
- * @param {number[]} itemHeights
673
- * @param {number} firstShownItemIndex
674
- * @param {number} lastShownItemIndex
675
- */
676
-
677
- }, {
678
- key: "onBeforeShowItems",
679
- value: function onBeforeShowItems(items, itemHeights, firstShownItemIndex, lastShownItemIndex) {
680
- if (this.onItemInitialRender) {
681
- var i = firstShownItemIndex;
682
-
683
- while (i <= lastShownItemIndex) {
684
- if (itemHeights[i] === undefined) {
685
- this.onItemInitialRender(items[i]);
686
- }
687
-
688
- i++;
93
+ if (this._render) {
94
+ this._render(this.getState());
689
95
  }
690
96
  }
691
- }
692
- }, {
693
- key: "onMount",
694
- value: function onMount() {
695
- warn('`.onMount()` instance method name is deprecated, use `.listen()` instance method name instead.');
696
- this.listen();
697
- }
698
- }, {
699
- key: "render",
700
- value: function render() {
701
- warn('`.render()` instance method name is deprecated, use `.listen()` instance method name instead.');
702
- this.listen();
703
- }
704
- /**
705
- * Should be invoked after a "container" DOM Element is mounted (inserted into the DOM tree).
706
- */
707
-
708
- }, {
709
- key: "listen",
710
- value: function listen() {
711
- if (this.isRendered === false) {
712
- throw new Error('[virtual-scroller] Can\'t restart a `VirtualScroller` after it has been stopped');
713
- }
714
97
 
715
- log('~ Rendered (initial) ~'); // `this.isRendered = true` should be the first statement in this function,
716
- // otherwise `DOMVirtualScroller` would enter an infinite re-render loop.
98
+ log('~ Start ~'); // `this._isActive = true` should be placed somewhere at the start of this function.
717
99
 
718
- this.isRendered = true;
719
- this.onRenderedNewLayout();
720
- this.resize.listen();
721
- this.scroll.listen(); // Work around `<tbody/>` not being able to have `padding`.
722
- // https://gitlab.com/catamphetamine/virtual-scroller/-/issues/1
100
+ this._isActive = true; // Reset `ListHeightMeasurement` just in case it has some "leftover" state.
723
101
 
724
- if (this.tbody) {
725
- addTbodyStyles(this.getContainerElement());
726
- }
102
+ this.listHeightMeasurement.reset(); // Reset `_isResizing` flag just in case it has some "leftover" value.
727
103
 
728
- if (this.preserveScrollPositionOfTheBottomOfTheListOnMount) {// In this case, all items are shown, so there's no need to call
729
- // `this.onUpdateShownItemIndexes()` after the initial render.
730
- } else {
731
- this.onUpdateShownItemIndexes({
732
- reason: LAYOUT_REASON.MOUNT
733
- });
734
- }
735
- }
736
- }, {
737
- key: "onRenderedNewLayout",
738
- value: function onRenderedNewLayout() {
739
- // Update item vertical spacing.
740
- this.measureVerticalSpacing(); // Measure "newly shown" item heights.
741
- // Also re-validate already measured items' heights.
104
+ this._isResizing = undefined; // Reset `_isSettingNewItems` flag just in case it has some "leftover" value.
742
105
 
743
- this.itemHeights.measureItemHeights(this.getState().firstShownItemIndex, this.getState().lastShownItemIndex); // Update `<tbody/>` `padding`.
744
- // (`<tbody/>` is different in a way that it can't have `margin`, only `padding`).
106
+ this._isSettingNewItems = undefined; // Work around `<tbody/>` not being able to have `padding`.
745
107
  // https://gitlab.com/catamphetamine/virtual-scroller/-/issues/1
746
108
 
747
109
  if (this.tbody) {
748
- setTbodyPadding(this.getContainerElement(), this.getState().beforeItemsHeight, this.getState().afterItemsHeight);
749
- }
750
- }
751
- }, {
752
- key: "getVisibleAreaBoundsIncludingMargins",
753
- value: function getVisibleAreaBoundsIncludingMargins() {
754
- var visibleArea = this.scroll.getVisibleAreaBounds();
755
- visibleArea.top -= this.getMargin();
756
- visibleArea.bottom += this.getMargin();
757
- return visibleArea;
758
- }
759
- /**
760
- * Returns the list's top offset relative to the scrollable container's top edge.
761
- * @return {number}
762
- */
763
-
764
- }, {
765
- key: "onUnmount",
766
- value: function onUnmount() {
767
- warn('`.onUnmount()` instance method name is deprecated, use `.stop()` instance method name instead.');
768
- this.stop();
769
- }
770
- }, {
771
- key: "destroy",
772
- value: function destroy() {
773
- warn('`.destroy()` instance method name is deprecated, use `.stop()` instance method name instead.');
774
- this.stop();
775
- }
776
- }, {
777
- key: "stop",
778
- value: function stop() {
779
- this.isRendered = false;
780
- this.resize.stop();
781
- this.scroll.stop();
782
- this.waitForStylesToLoad.stop();
783
-
784
- if (this.layoutTimer) {
785
- clearTimeout(this.layoutTimer);
786
- this.layoutTimer = undefined;
110
+ if (!hasTbodyStyles(this.getItemsContainerElement())) {
111
+ addTbodyStyles(this.getItemsContainerElement());
112
+ }
113
+ } // If there was a pending state update that didn't get applied
114
+ // because of stopping the `VirtualScroller`, apply that state update now.
115
+ //
116
+ // The pending state update won't get applied if the scrollable container width
117
+ // has changed but that's ok because that state update currently could only contain:
118
+ // * `scrollableContainerWidth`
119
+ // * `verticalSpacing`
120
+ // * `beforeResize`
121
+ // All of those get rewritten in `onResize()` anyway.
122
+ //
123
+
124
+
125
+ var stateUpdate = this._stoppedStateUpdate;
126
+ this._stoppedStateUpdate = undefined; // Reset `this.verticalSpacing` so that it re-measures it in cases when
127
+ // the `VirtualScroller` was previously stopped and is now being restarted.
128
+ // The rationale is that a previously captured inter-item vertical spacing
129
+ // can't be "trusted" in a sense that the user might have resized the window
130
+ // after the previous `state` has been snapshotted.
131
+ // If the user has resized the window, then changing window width might have
132
+ // activated different CSS `@media()` "queries" resulting in a potentially different
133
+ // vertical spacing after the restart.
134
+ // If it's not a restart then `this.verticalSpacing` is `undefined` anyway.
135
+
136
+ this.verticalSpacing = undefined;
137
+ var verticalSpacingStateUpdate = this.measureItemHeightsAndSpacing();
138
+
139
+ if (verticalSpacingStateUpdate) {
140
+ stateUpdate = _objectSpread(_objectSpread({}, stateUpdate), verticalSpacingStateUpdate);
787
141
  }
788
- }
789
- /**
790
- * Should be called right before `state` is updated.
791
- * @param {object} prevState
792
- * @param {object} newState
793
- */
794
142
 
795
- }, {
796
- key: "redoLayoutRightAfterRender",
797
- value: function redoLayoutRightAfterRender(_ref6) {
798
- var _this2 = this;
143
+ this.resize.start();
144
+ this.scroll.start(); // If `scrollableContainerWidth` hasn't been measured yet,
145
+ // measure it and write it to state.
799
146
 
800
- var reason = _ref6.reason;
147
+ if (this.getState().scrollableContainerWidth === undefined) {
148
+ var scrollableContainerWidth = this.scrollableContainer.getWidth();
149
+ stateUpdate = _objectSpread(_objectSpread({}, stateUpdate), {}, {
150
+ scrollableContainerWidth: scrollableContainerWidth
151
+ });
152
+ } else {
153
+ // Reset layout:
154
+ // * If the scrollable container width has changed while stopped.
155
+ // * If the restored state was calculated for another scrollable container width.
156
+ var newWidth = this.scrollableContainer.getWidth();
157
+ var prevWidth = this.getState().scrollableContainerWidth;
158
+
159
+ if (newWidth !== prevWidth) {
160
+ log('~ Scrollable container width changed from', prevWidth, 'to', newWidth, '~'); // `stateUpdate` doesn't get passed to `this.onResize()`, and, therefore,
161
+ // won't be applied. But that's ok because currently it could only contain:
162
+ // * `scrollableContainerWidth`
163
+ // * `verticalSpacing`
164
+ // * `beforeResize`
165
+ // All of those get rewritten in `onResize()` anyway.
166
+
167
+ return this.onResize();
168
+ }
169
+ } // If the `VirtualScroller` uses custom (external) state storage, then
170
+ // check if the columns count has changed between calling `.getInitialState()`
171
+ // and `.start()`. If it has, perform a re-layout "from scratch".
801
172
 
802
- // In React, `setTimeout()` is used to prevent a React error:
803
- // "Maximum update depth exceeded.
804
- // This can happen when a component repeatedly calls
805
- // `.setState()` inside `componentWillUpdate()` or `componentDidUpdate()`.
806
- // React limits the number of nested updates to prevent infinite loops."
807
- if (this._useTimeoutInRenderLoop) {
808
- // Cancel a previously scheduled re-layout.
809
- if (this.layoutTimer) {
810
- clearTimeout(this.layoutTimer);
811
- } // Schedule a new re-layout.
812
173
 
174
+ if (this._usesCustomStateStorage) {
175
+ var columnsCount = this.getActualColumnsCount();
176
+ var columnsCountFromState = this.getState().columnsCount || 1;
813
177
 
814
- this.layoutTimer = setTimeout(function () {
815
- _this2.layoutTimer = undefined;
178
+ if (columnsCount !== columnsCountFromState) {
179
+ return this.onResize();
180
+ }
181
+ } // Re-calculate layout and re-render the list.
182
+ // Do that even if when an initial `state` parameter, containing layout values,
183
+ // has been passed. The reason is that the `state` parameter can't be "trusted"
184
+ // in a way that it could have been snapshotted for another window width and
185
+ // the user might have resized their window since then.
816
186
 
817
- _this2.onUpdateShownItemIndexes({
818
- reason: reason
819
- });
820
- }, 0);
821
- } else {
822
- this.onUpdateShownItemIndexes({
823
- reason: reason
824
- });
825
- }
826
- }
827
- }, {
828
- key: "measureVerticalSpacing",
829
- value: function measureVerticalSpacing() {
830
- if (this.getState().verticalSpacing === undefined) {
831
- log('~ Measure item vertical spacing ~');
832
- var verticalSpacing = getVerticalSpacing({
833
- container: this.getContainerElement(),
834
- screen: this.screen
835
- });
836
187
 
837
- if (verticalSpacing === undefined) {
838
- log('Not enough items rendered to measure vertical spacing');
839
- } else {
840
- log('Item vertical spacing', verticalSpacing);
841
- this.setState({
842
- verticalSpacing: verticalSpacing
843
- });
844
- }
845
- }
846
- }
847
- }, {
848
- key: "remeasureItemHeight",
849
- value: function remeasureItemHeight(i) {
850
- var _this$getState2 = this.getState(),
851
- firstShownItemIndex = _this$getState2.firstShownItemIndex;
188
+ this.onUpdateShownItemIndexes({
189
+ reason: LAYOUT_REASON.STARTED,
190
+ stateUpdate: stateUpdate
191
+ });
192
+ } // Could be passed as a "callback" parameter, so bind it to `this`.
852
193
 
853
- return this.itemHeights.remeasureItemHeight(i, firstShownItemIndex);
854
- }
855
194
  }, {
856
- key: "onItemStateChange",
857
- value: function onItemStateChange(i, itemState) {
858
- if (isDebug()) {
859
- log('~ Item state changed ~');
860
- log('Item', i);
861
- log('Previous state' + '\n' + JSON.stringify(this.getState().itemStates[i], null, 2));
862
- log('New state' + '\n' + JSON.stringify(itemState, null, 2));
195
+ key: "hasToBeStarted",
196
+ value: function hasToBeStarted() {
197
+ if (!this._isActive) {
198
+ throw new Error('[virtual-scroller] `VirtualScroller` hasn\'t been started');
863
199
  }
200
+ } // Bind it to `this` because this function could hypothetically be passed
201
+ // as a "callback" parameter.
864
202
 
865
- this.getState().itemStates[i] = itemState;
866
- }
867
203
  }, {
868
- key: "onItemHeightChange",
869
- value: function onItemHeightChange(i) {
870
- log('~ Re-measure item height ~');
871
- log('Item', i);
872
-
873
- var _this$getState3 = this.getState(),
874
- itemHeights = _this$getState3.itemHeights;
875
-
876
- var previousHeight = itemHeights[i];
877
-
878
- if (previousHeight === undefined) {
879
- return reportError("\"onItemHeightChange()\" has been called for item ".concat(i, ", but that item hasn't been rendered before."));
880
- }
881
-
882
- var newHeight = this.remeasureItemHeight(i); // Check if the item is still rendered.
204
+ key: "getItemScrollPosition",
205
+ value:
206
+ /**
207
+ * Returns the items's top offset relative to the scrollable container's top edge.
208
+ * @param {number} i — Item index
209
+ * @return {[number]} Returns the item's scroll Y position. Returns `undefined` if any of the previous items haven't been rendered yet.
210
+ */
211
+ function getItemScrollPosition(i) {
212
+ var itemTopOffsetInList = this.layout.getItemTopOffset(i);
883
213
 
884
- if (newHeight === undefined) {
885
- // There could be valid cases when an item is no longer rendered
886
- // by the time `.onItemHeightChange(i)` gets called.
887
- // For example, suppose there's a list of several items on a page,
888
- // and those items are in "minimized" state (having height 100px).
889
- // Then, a user clicks an "Expand all items" button, and all items
890
- // in the list are expanded (expanded item height is gonna be 700px).
891
- // `VirtualScroller` demands that `.onItemHeightChange(i)` is called
892
- // in such cases, and the developer has properly added the code to do that.
893
- // So, if there were 10 "minimized" items visible on a page, then there
894
- // will be 10 individual `.onItemHeightChange(i)` calls. No issues so far.
895
- // But, as the first `.onItemHeightChange(i)` call executes, it immediately
896
- // ("synchronously") triggers a re-layout, and that re-layout finds out
897
- // that now, because the first item is big, it occupies most of the screen
898
- // space, and only the first 3 items are visible on screen instead of 10,
899
- // and so it leaves the first 3 items mounted and unmounts the rest 7.
900
- // Then, after `VirtualScroller` has rerendered, the code returns to
901
- // where it was executing, and calls `.onItemHeightChange(i)` for the
902
- // second item. It also triggers an immediate re-layout that finds out
903
- // that only the first 2 items are visible on screen, and it unmounts
904
- // the third one too. After that, it calls `.onItemHeightChange(i)`
905
- // for the third item, but that item is no longer rendered, so its height
906
- // can't be measured, and the same's for all the rest of the original 10 items.
907
- // So, even though the developer has written their code properly, there're
908
- // still situations when the item could be no longer rendered by the time
909
- // `.onItemHeightChange(i)` gets called.
910
- return warn('The item is no longer rendered. This is not necessarily a bug, and could happen, for example, when there\'re several `onItemHeightChange(i)` calls issued at the same time.');
214
+ if (itemTopOffsetInList === undefined) {
215
+ return;
911
216
  }
912
217
 
913
- log('Previous height', previousHeight);
914
- log('New height', newHeight);
915
-
916
- if (previousHeight !== newHeight) {
917
- log('~ Item height has changed ~'); // log('Item', i)
918
-
919
- this.onUpdateShownItemIndexes({
920
- reason: LAYOUT_REASON.ITEM_HEIGHT_CHANGED
921
- });
922
- }
218
+ return this.getListTopOffsetInsideScrollableContainer() + itemTopOffsetInList;
923
219
  }
924
220
  /**
925
- * Validates the heights of items to be hidden on next render.
926
- * For example, a user could click a "Show more" button,
927
- * or an "Expand YouTube video" button, which would result
928
- * in the actual height of the list item being different
929
- * from what has been initially measured in `this.itemHeights[i]`,
930
- * if the developer didn't call `.onItemStateChange()` and `.onItemHeightChange(i)`.
221
+ * Forces a re-measure of an item's height.
222
+ * @param {number} i Item index
931
223
  */
932
224
 
933
225
  }, {
934
- key: "validateWillBeHiddenItemHeightsAreAccurate",
935
- value: function validateWillBeHiddenItemHeightsAreAccurate(firstShownItemIndex, lastShownItemIndex) {
936
- var isValid = true;
937
- var i = this.getState().firstShownItemIndex;
938
-
939
- while (i <= this.getState().lastShownItemIndex) {
940
- if (i >= firstShownItemIndex && i <= lastShownItemIndex) {// The item's still visible.
941
- } else {
942
- // The item will be hidden. Re-measure its height.
943
- // The rationale is that there could be a situation when an item's
944
- // height has changed, and the developer has properly added an
945
- // `.onItemHeightChange(i)` call to notify `VirtualScroller`
946
- // about that change, but at the same time that wouldn't work.
947
- // For example, suppose there's a list of several items on a page,
948
- // and those items are in "minimized" state (having height 100px).
949
- // Then, a user clicks an "Expand all items" button, and all items
950
- // in the list are expanded (expanded item height is gonna be 700px).
951
- // `VirtualScroller` demands that `.onItemHeightChange(i)` is called
952
- // in such cases, and the developer has properly added the code to do that.
953
- // So, if there were 10 "minimized" items visible on a page, then there
954
- // will be 10 individual `.onItemHeightChange(i)` calls. No issues so far.
955
- // But, as the first `.onItemHeightChange(i)` call executes, it immediately
956
- // ("synchronously") triggers a re-layout, and that re-layout finds out
957
- // that now, because the first item is big, it occupies most of the screen
958
- // space, and only the first 3 items are visible on screen instead of 10,
959
- // and so it leaves the first 3 items mounted and unmounts the rest 7.
960
- // Then, after `VirtualScroller` has rerendered, the code returns to
961
- // where it was executing, and calls `.onItemHeightChange(i)` for the
962
- // second item. It also triggers an immediate re-layout that finds out
963
- // that only the first 2 items are visible on screen, and it unmounts
964
- // the third one too. After that, it calls `.onItemHeightChange(i)`
965
- // for the third item, but that item is no longer rendered, so its height
966
- // can't be measured, and the same's for all the rest of the original 10 items.
967
- // So, even though the developer has written their code properly, the
968
- // `VirtualScroller` still ends up having incorrect `itemHeights[]`:
969
- // `[700px, 700px, 100px, 100px, 100px, 100px, 100px, 100px, 100px, 100px]`
970
- // while it should have been `700px` for all of them.
971
- // To work around such issues, every item's height is re-measured before it
972
- // gets hidden.
973
- var previouslyMeasuredItemHeight = this.getState().itemHeights[i];
974
- var actualItemHeight = this.remeasureItemHeight(i);
975
-
976
- if (actualItemHeight !== previouslyMeasuredItemHeight) {
977
- isValid = false;
978
- warn('Item', i, 'will be unmounted at next render because it\'s no longer visible. Its height has changed from', previouslyMeasuredItemHeight, 'to', actualItemHeight, 'since it was last measured. This is not necessarily a bug, and could happen, for example, when there\'re several `onItemHeightChange(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.');
979
- }
980
- }
981
-
982
- i++;
983
- }
226
+ key: "onItemHeightChange",
227
+ value: function onItemHeightChange(i) {
228
+ this.hasToBeStarted();
984
229
 
985
- return isValid;
230
+ this._onItemHeightChange(i);
986
231
  }
987
232
  /**
988
- * Updates the "from" and "to" shown item indexes.
989
- * If the list is visible and some of the items being shown are new
990
- * and are required to be measured first, then
991
- * `redoLayoutAfterMeasuringItemHeights` is `true`.
992
- * If the list is visible and all items being shown have been encountered
993
- * (and measured) before, then `redoLayoutAfterMeasuringItemHeights` is `false`.
233
+ * Updates an item's state in `state.itemStates[]`.
234
+ * @param {number} i Item index
235
+ * @param {any} i Item's new state
994
236
  */
995
237
 
996
238
  }, {
997
- key: "updateItems",
239
+ key: "onItemStateChange",
240
+ value: function onItemStateChange(i, newItemState) {
241
+ this.hasToBeStarted();
998
242
 
999
- /**
1000
- * @deprecated
1001
- * `.updateItems()` has been renamed to `.setItems()`.
1002
- */
1003
- value: function updateItems(newItems, options) {
1004
- return this.setItems(newItems, options);
243
+ this._onItemStateChange(i, newItemState);
1005
244
  }
1006
245
  /**
1007
246
  * Updates `items`. For example, can prepend or append new items to the list.
@@ -1013,102 +252,8 @@ function () {
1013
252
  key: "setItems",
1014
253
  value: function setItems(newItems) {
1015
254
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1016
-
1017
- // * @param {object} [newCustomState] — If `customState` was passed to `getInitialState()`, this `newCustomState` updates it.
1018
- var _this$getState4 = this.getState(),
1019
- previousItems = _this$getState4.items;
1020
-
1021
- var _this$getState5 = this.getState(),
1022
- itemStates = _this$getState5.itemStates,
1023
- itemHeights = _this$getState5.itemHeights;
1024
-
1025
- log('~ Update items ~');
1026
- var layout;
1027
- var itemsDiff = this.getItemsDiff(previousItems, newItems); // If it's an "incremental" update.
1028
-
1029
- if (itemsDiff && !this.layoutResetPending) {
1030
- var _this$getState6 = this.getState(),
1031
- firstShownItemIndex = _this$getState6.firstShownItemIndex,
1032
- lastShownItemIndex = _this$getState6.lastShownItemIndex,
1033
- beforeItemsHeight = _this$getState6.beforeItemsHeight,
1034
- afterItemsHeight = _this$getState6.afterItemsHeight;
1035
-
1036
- layout = {
1037
- firstShownItemIndex: firstShownItemIndex,
1038
- lastShownItemIndex: lastShownItemIndex,
1039
- beforeItemsHeight: beforeItemsHeight,
1040
- afterItemsHeight: afterItemsHeight
1041
- };
1042
- var prependedItemsCount = itemsDiff.prependedItemsCount,
1043
- appendedItemsCount = itemsDiff.appendedItemsCount;
1044
-
1045
- if (prependedItemsCount > 0) {
1046
- log('Prepend', prependedItemsCount, 'items');
1047
- itemHeights = new Array(prependedItemsCount).concat(itemHeights);
1048
-
1049
- if (itemStates) {
1050
- itemStates = new Array(prependedItemsCount).concat(itemStates);
1051
- }
1052
- }
1053
-
1054
- if (appendedItemsCount > 0) {
1055
- log('Append', appendedItemsCount, 'items');
1056
- itemHeights = itemHeights.concat(new Array(appendedItemsCount));
1057
-
1058
- if (itemStates) {
1059
- itemStates = itemStates.concat(new Array(appendedItemsCount));
1060
- }
1061
- }
1062
-
1063
- this.layout.updateLayoutForItemsDiff(layout, itemsDiff, {
1064
- itemsCount: newItems.length
1065
- });
1066
-
1067
- if (prependedItemsCount > 0) {
1068
- // `preserveScrollPosition` option name is deprecated,
1069
- // use `preserveScrollPositionOnPrependItems` instead.
1070
- if (options.preserveScrollPositionOnPrependItems || options.preserveScrollPosition) {
1071
- if (this.getState().firstShownItemIndex === 0) {
1072
- this.restoreScroll.captureScroll({
1073
- previousItems: previousItems,
1074
- newItems: newItems,
1075
- prependedItemsCount: prependedItemsCount
1076
- });
1077
- this.layout.showItemsFromTheStart(layout);
1078
- }
1079
- }
1080
- }
1081
- } else {
1082
- log('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.');
1083
- log('Previous items', previousItems);
1084
- log('New items', newItems);
1085
- itemHeights = new Array(newItems.length);
1086
- itemStates = new Array(newItems.length);
1087
- layout = this.getInitialLayoutValues({
1088
- itemsCount: newItems.length
1089
- });
1090
- }
1091
-
1092
- log('~ Update state ~');
1093
- log('First shown item index', layout.firstShownItemIndex);
1094
- log('Last shown item index', layout.lastShownItemIndex);
1095
- log('Before items height', layout.beforeItemsHeight);
1096
- log('After items height (actual or estimated)', layout.afterItemsHeight); // Optionally preload items to be rendered.
1097
-
1098
- this.onBeforeShowItems(newItems, itemHeights, layout.firstShownItemIndex, layout.lastShownItemIndex); // `this.newItemsPending` will be cleared in `didUpdateState()`.
1099
-
1100
- this.newItemsPending = newItems; // Update state.
1101
-
1102
- this.setState(_objectSpread({}, layout, {
1103
- items: newItems,
1104
- itemStates: itemStates,
1105
- itemHeights: itemHeights
1106
- }));
1107
- }
1108
- }, {
1109
- key: "getItemsDiff",
1110
- value: function getItemsDiff(previousItems, newItems) {
1111
- return _getItemsDiff(previousItems, newItems, this.isItemEqual);
255
+ this.hasToBeStarted();
256
+ return this._setItems(newItems, options);
1112
257
  }
1113
258
  }]);
1114
259