virtual-scroller 1.8.0 → 1.9.1

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 (248) hide show
  1. package/.gitlab-ci.yml +1 -1
  2. package/CHANGELOG.md +57 -0
  3. package/README.md +337 -160
  4. package/bundle/virtual-scroller-dom.js +1 -1
  5. package/bundle/virtual-scroller-dom.js.map +1 -1
  6. package/bundle/virtual-scroller-react.js +1 -1
  7. package/bundle/virtual-scroller-react.js.map +1 -1
  8. package/bundle/virtual-scroller.js +1 -1
  9. package/bundle/virtual-scroller.js.map +1 -1
  10. package/commonjs/BeforeResize.js +23 -27
  11. package/commonjs/BeforeResize.js.map +1 -1
  12. package/commonjs/DOM/Engine.js +7 -7
  13. package/commonjs/DOM/Engine.js.map +1 -1
  14. package/commonjs/DOM/ItemsContainer.js +1 -1
  15. package/commonjs/DOM/ItemsContainer.js.map +1 -1
  16. package/commonjs/DOM/ListTopOffsetWatcher.js +15 -9
  17. package/commonjs/DOM/ListTopOffsetWatcher.js.map +1 -1
  18. package/commonjs/DOM/ScrollableContainer.js +28 -28
  19. package/commonjs/DOM/ScrollableContainer.js.map +1 -1
  20. package/commonjs/DOM/VirtualScroller.js +20 -17
  21. package/commonjs/DOM/VirtualScroller.js.map +1 -1
  22. package/commonjs/DOM/tbody.js +16 -10
  23. package/commonjs/DOM/tbody.js.map +1 -1
  24. package/commonjs/ItemHeights.js +23 -17
  25. package/commonjs/ItemHeights.js.map +1 -1
  26. package/commonjs/Layout.js +15 -13
  27. package/commonjs/Layout.js.map +1 -1
  28. package/commonjs/Layout.test.js +8 -3
  29. package/commonjs/Layout.test.js.map +1 -1
  30. package/commonjs/{ListHeightChangeWatcher.js → ListHeightMeasurement.js} +26 -28
  31. package/commonjs/ListHeightMeasurement.js.map +1 -0
  32. package/commonjs/Resize.js +38 -28
  33. package/commonjs/Resize.js.map +1 -1
  34. package/commonjs/Scroll.js +28 -44
  35. package/commonjs/Scroll.js.map +1 -1
  36. package/commonjs/VirtualScroller.columns.js +43 -0
  37. package/commonjs/VirtualScroller.columns.js.map +1 -0
  38. package/commonjs/VirtualScroller.constructor.js +408 -0
  39. package/commonjs/VirtualScroller.constructor.js.map +1 -0
  40. package/commonjs/VirtualScroller.items.js +305 -0
  41. package/commonjs/VirtualScroller.items.js.map +1 -0
  42. package/commonjs/VirtualScroller.js +132 -1872
  43. package/commonjs/VirtualScroller.js.map +1 -1
  44. package/commonjs/VirtualScroller.layout.js +562 -0
  45. package/commonjs/VirtualScroller.layout.js.map +1 -0
  46. package/commonjs/VirtualScroller.onRender.js +357 -0
  47. package/commonjs/VirtualScroller.onRender.js.map +1 -0
  48. package/commonjs/VirtualScroller.resize.js +186 -0
  49. package/commonjs/VirtualScroller.resize.js.map +1 -0
  50. package/commonjs/VirtualScroller.state.js +301 -0
  51. package/commonjs/VirtualScroller.state.js.map +1 -0
  52. package/commonjs/VirtualScroller.verticalSpacing.js +65 -0
  53. package/commonjs/VirtualScroller.verticalSpacing.js.map +1 -0
  54. package/commonjs/getItemCoordinates.js.map +1 -1
  55. package/commonjs/getItemsDiff.js.map +1 -1
  56. package/commonjs/getVerticalSpacing.js.map +1 -1
  57. package/commonjs/package.json +5 -0
  58. package/commonjs/react/VirtualScroller.js +184 -618
  59. package/commonjs/react/VirtualScroller.js.map +1 -1
  60. package/commonjs/react/useClassName.js +26 -0
  61. package/commonjs/react/useClassName.js.map +1 -0
  62. package/commonjs/react/useHandleItemsChange.js +116 -0
  63. package/commonjs/react/useHandleItemsChange.js.map +1 -0
  64. package/commonjs/react/useInstanceMethods.js +37 -0
  65. package/commonjs/react/useInstanceMethods.js.map +1 -0
  66. package/commonjs/react/useItemKeys.js +60 -0
  67. package/commonjs/react/useItemKeys.js.map +1 -0
  68. package/commonjs/react/useOnItemHeightChange.js +32 -0
  69. package/commonjs/react/useOnItemHeightChange.js.map +1 -0
  70. package/commonjs/react/useOnItemStateChange.js +32 -0
  71. package/commonjs/react/useOnItemStateChange.js.map +1 -0
  72. package/commonjs/react/useState.js +140 -0
  73. package/commonjs/react/useState.js.map +1 -0
  74. package/commonjs/react/useStyle.js +29 -0
  75. package/commonjs/react/useStyle.js.map +1 -0
  76. package/commonjs/react/useVirtualScroller.js +62 -0
  77. package/commonjs/react/useVirtualScroller.js.map +1 -0
  78. package/commonjs/react/useVirtualScrollerStartStop.js +20 -0
  79. package/commonjs/react/useVirtualScrollerStartStop.js.map +1 -0
  80. package/commonjs/test/Engine.js +23 -0
  81. package/commonjs/test/Engine.js.map +1 -0
  82. package/commonjs/test/ItemsContainer.js +127 -0
  83. package/commonjs/test/ItemsContainer.js.map +1 -0
  84. package/commonjs/test/ScrollableContainer.js +130 -0
  85. package/commonjs/test/ScrollableContainer.js.map +1 -0
  86. package/commonjs/test/VirtualScroller.js +281 -0
  87. package/commonjs/test/VirtualScroller.js.map +1 -0
  88. package/commonjs/utility/debounce.js +2 -2
  89. package/commonjs/utility/debounce.js.map +1 -1
  90. package/commonjs/utility/debug.js.map +1 -1
  91. package/commonjs/utility/getStateSnapshot.js +2 -2
  92. package/commonjs/utility/getStateSnapshot.js.map +1 -1
  93. package/commonjs/utility/px.js.map +1 -1
  94. package/commonjs/utility/px.test.js +1 -1
  95. package/commonjs/utility/px.test.js.map +1 -1
  96. package/commonjs/utility/shallowEqual.js +1 -1
  97. package/commonjs/utility/shallowEqual.js.map +1 -1
  98. package/commonjs/utility/throttle.js.map +1 -1
  99. package/dom/index.cjs +4 -0
  100. package/dom/index.cjs.js +9 -0
  101. package/dom/index.d.ts +6 -4
  102. package/dom/index.js +1 -1
  103. package/dom/package.json +10 -4
  104. package/index.cjs +4 -0
  105. package/index.cjs.js +9 -0
  106. package/index.d.ts +30 -15
  107. package/index.js +1 -1
  108. package/modules/BeforeResize.js +22 -27
  109. package/modules/BeforeResize.js.map +1 -1
  110. package/modules/DOM/Engine.js +6 -6
  111. package/modules/DOM/Engine.js.map +1 -1
  112. package/modules/DOM/ItemsContainer.js +1 -1
  113. package/modules/DOM/ItemsContainer.js.map +1 -1
  114. package/modules/DOM/ListTopOffsetWatcher.js +15 -9
  115. package/modules/DOM/ListTopOffsetWatcher.js.map +1 -1
  116. package/modules/DOM/ScrollableContainer.js +28 -28
  117. package/modules/DOM/ScrollableContainer.js.map +1 -1
  118. package/modules/DOM/VirtualScroller.js +19 -16
  119. package/modules/DOM/VirtualScroller.js.map +1 -1
  120. package/modules/DOM/tbody.js +11 -9
  121. package/modules/DOM/tbody.js.map +1 -1
  122. package/modules/ItemHeights.js +22 -16
  123. package/modules/ItemHeights.js.map +1 -1
  124. package/modules/Layout.js +14 -12
  125. package/modules/Layout.js.map +1 -1
  126. package/modules/Layout.test.js +8 -3
  127. package/modules/Layout.test.js.map +1 -1
  128. package/modules/{ListHeightChangeWatcher.js → ListHeightMeasurement.js} +25 -27
  129. package/modules/ListHeightMeasurement.js.map +1 -0
  130. package/modules/Resize.js +38 -28
  131. package/modules/Resize.js.map +1 -1
  132. package/modules/Scroll.js +28 -44
  133. package/modules/Scroll.js.map +1 -1
  134. package/modules/VirtualScroller.columns.js +36 -0
  135. package/modules/VirtualScroller.columns.js.map +1 -0
  136. package/modules/VirtualScroller.constructor.js +371 -0
  137. package/modules/VirtualScroller.constructor.js.map +1 -0
  138. package/modules/VirtualScroller.items.js +288 -0
  139. package/modules/VirtualScroller.items.js.map +1 -0
  140. package/modules/VirtualScroller.js +132 -1860
  141. package/modules/VirtualScroller.js.map +1 -1
  142. package/modules/VirtualScroller.layout.js +549 -0
  143. package/modules/VirtualScroller.layout.js.map +1 -0
  144. package/modules/VirtualScroller.onRender.js +337 -0
  145. package/modules/VirtualScroller.onRender.js.map +1 -0
  146. package/modules/VirtualScroller.resize.js +176 -0
  147. package/modules/VirtualScroller.resize.js.map +1 -0
  148. package/modules/VirtualScroller.state.js +283 -0
  149. package/modules/VirtualScroller.state.js.map +1 -0
  150. package/modules/VirtualScroller.verticalSpacing.js +54 -0
  151. package/modules/VirtualScroller.verticalSpacing.js.map +1 -0
  152. package/modules/getItemCoordinates.js.map +1 -1
  153. package/modules/getItemsDiff.js.map +1 -1
  154. package/modules/getVerticalSpacing.js.map +1 -1
  155. package/modules/react/VirtualScroller.js +176 -625
  156. package/modules/react/VirtualScroller.js.map +1 -1
  157. package/modules/react/useClassName.js +18 -0
  158. package/modules/react/useClassName.js.map +1 -0
  159. package/modules/react/useHandleItemsChange.js +108 -0
  160. package/modules/react/useHandleItemsChange.js.map +1 -0
  161. package/modules/react/useInstanceMethods.js +28 -0
  162. package/modules/react/useInstanceMethods.js.map +1 -0
  163. package/modules/react/useItemKeys.js +52 -0
  164. package/modules/react/useItemKeys.js.map +1 -0
  165. package/modules/react/useOnItemHeightChange.js +24 -0
  166. package/modules/react/useOnItemHeightChange.js.map +1 -0
  167. package/modules/react/useOnItemStateChange.js +24 -0
  168. package/modules/react/useOnItemStateChange.js.map +1 -0
  169. package/modules/react/useState.js +132 -0
  170. package/modules/react/useState.js.map +1 -0
  171. package/modules/react/useStyle.js +19 -0
  172. package/modules/react/useStyle.js.map +1 -0
  173. package/modules/react/useVirtualScroller.js +51 -0
  174. package/modules/react/useVirtualScroller.js.map +1 -0
  175. package/modules/react/useVirtualScrollerStartStop.js +12 -0
  176. package/modules/react/useVirtualScrollerStartStop.js.map +1 -0
  177. package/modules/test/Engine.js +11 -0
  178. package/modules/test/Engine.js.map +1 -0
  179. package/modules/test/ItemsContainer.js +120 -0
  180. package/modules/test/ItemsContainer.js.map +1 -0
  181. package/modules/test/ScrollableContainer.js +123 -0
  182. package/modules/test/ScrollableContainer.js.map +1 -0
  183. package/modules/test/VirtualScroller.js +270 -0
  184. package/modules/test/VirtualScroller.js.map +1 -0
  185. package/modules/utility/debounce.js +2 -2
  186. package/modules/utility/debounce.js.map +1 -1
  187. package/modules/utility/debug.js.map +1 -1
  188. package/modules/utility/getStateSnapshot.js +2 -2
  189. package/modules/utility/getStateSnapshot.js.map +1 -1
  190. package/modules/utility/px.js.map +1 -1
  191. package/modules/utility/px.test.js +1 -1
  192. package/modules/utility/px.test.js.map +1 -1
  193. package/modules/utility/shallowEqual.js +1 -1
  194. package/modules/utility/shallowEqual.js.map +1 -1
  195. package/modules/utility/throttle.js.map +1 -1
  196. package/package.json +46 -23
  197. package/react/index.cjs +4 -0
  198. package/react/index.cjs.js +9 -0
  199. package/react/index.d.ts +10 -9
  200. package/react/index.js +1 -1
  201. package/react/package.json +10 -4
  202. package/rollup.config.mjs +62 -0
  203. package/runnable/create-commonjs-package-json.js +11 -0
  204. package/source/BeforeResize.js +16 -21
  205. package/source/DOM/Engine.js +8 -10
  206. package/source/DOM/ListTopOffsetWatcher.js +13 -8
  207. package/source/DOM/ScrollableContainer.js +23 -21
  208. package/source/DOM/VirtualScroller.js +27 -11
  209. package/source/DOM/tbody.js +30 -21
  210. package/source/ItemHeights.js +19 -14
  211. package/source/Layout.js +12 -9
  212. package/source/Layout.test.js +8 -3
  213. package/source/{ListHeightChangeWatcher.js → ListHeightMeasurement.js} +21 -20
  214. package/source/Resize.js +41 -25
  215. package/source/Scroll.js +27 -35
  216. package/source/VirtualScroller.columns.js +26 -0
  217. package/source/VirtualScroller.constructor.js +336 -0
  218. package/source/VirtualScroller.items.js +302 -0
  219. package/source/VirtualScroller.js +144 -1872
  220. package/source/VirtualScroller.layout.js +539 -0
  221. package/source/VirtualScroller.onRender.js +345 -0
  222. package/source/VirtualScroller.resize.js +189 -0
  223. package/source/VirtualScroller.state.js +284 -0
  224. package/source/VirtualScroller.verticalSpacing.js +51 -0
  225. package/source/react/VirtualScroller.js +243 -587
  226. package/source/react/useClassName.js +14 -0
  227. package/source/react/useHandleItemsChange.js +115 -0
  228. package/source/react/useInstanceMethods.js +25 -0
  229. package/source/react/useItemKeys.js +59 -0
  230. package/source/react/useOnItemHeightChange.js +28 -0
  231. package/source/react/useOnItemStateChange.js +28 -0
  232. package/source/react/useState.js +114 -0
  233. package/source/react/useStyle.js +20 -0
  234. package/source/react/useVirtualScroller.js +59 -0
  235. package/source/react/useVirtualScrollerStartStop.js +12 -0
  236. package/source/test/Engine.js +11 -0
  237. package/source/test/ItemsContainer.js +87 -0
  238. package/source/test/ScrollableContainer.js +88 -0
  239. package/source/test/VirtualScroller.js +232 -0
  240. package/source/utility/debounce.js +2 -2
  241. package/source/utility/px.test.js +1 -1
  242. package/babel.config.js +0 -25
  243. package/babel.js +0 -5
  244. package/commonjs/ListHeightChangeWatcher.js.map +0 -1
  245. package/dom/index.commonjs.js +0 -4
  246. package/index.commonjs.js +0 -4
  247. package/modules/ListHeightChangeWatcher.js.map +0 -1
  248. package/react/index.commonjs.js +0 -4
@@ -0,0 +1,284 @@
1
+ import log, { warn, isDebug, reportError } from './utility/debug.js'
2
+ import { cleanUpBeforeResizeState } from './BeforeResize.js'
3
+ import getStateSnapshot from './utility/getStateSnapshot.js'
4
+
5
+ // There're three main places where state is updated:
6
+ //
7
+ // * On scroll.
8
+ // * On window resize.
9
+ // * On set new items.
10
+ //
11
+ // State updates may be "asynchronous" (like in React), in which case the
12
+ // corresponding operation is "pending" until the state update is applied.
13
+ //
14
+ // If there's a "pending" window resize or a "pending" update of the set of items,
15
+ // then "on scroll" updates aren't dispatched.
16
+ //
17
+ // If there's a "pending" on scroll update and the window is resize or a new set
18
+ // of items is set, then that "pending" on scroll update gets overwritten.
19
+ //
20
+ // If there's a "pending" update of the set of items, then window resize handler
21
+ // sees that "pending" update and dispatches its own state update so that the
22
+ // "pending" state update originating from `setItems()` is not lost.
23
+ //
24
+ // If there's a "pending" window resize, and a new set of items is set,
25
+ // then the state update of the window resize handler gets overwritten.
26
+
27
+ export default function createStateHelpers({
28
+ state,
29
+ onStateChange,
30
+ render,
31
+ items: initialItems
32
+ }) {
33
+ this.onStateChange = onStateChange
34
+ this._render = render
35
+
36
+ this._onItemStateChange = (i, newItemState) => {
37
+ if (isDebug()) {
38
+ log('~ Item state changed ~')
39
+ log('Item', i)
40
+ // Uses `JSON.stringify()` here instead of just outputting the JSON objects as is
41
+ // because outputting JSON objects as is would show different results later when
42
+ // the developer inspects those in the web browser console if those state objects
43
+ // get modified in between they've been output to the console and the developer
44
+ // decided to inspect them.
45
+ log('Previous state' + '\n' + JSON.stringify(this.getState().itemStates[i], null, 2))
46
+ log('New state' + '\n' + JSON.stringify(newItemState, null, 2))
47
+ }
48
+
49
+ this.getState().itemStates[i] = newItemState
50
+
51
+ // Schedule the item state update for after the new items have been rendered.
52
+ if (this.newItemsWillBeRendered) {
53
+ if (!this.itemStatesThatChangedWhileNewItemsWereBeingRendered) {
54
+ this.itemStatesThatChangedWhileNewItemsWereBeingRendered = {}
55
+ }
56
+ this.itemStatesThatChangedWhileNewItemsWereBeingRendered[String(i)] = newItemState
57
+ }
58
+ }
59
+
60
+ this.getState = () => this._getState()
61
+
62
+ this.updateState = (stateUpdate) => {
63
+ if (isDebug()) {
64
+ log('~ Set state ~')
65
+ log(getStateSnapshot(stateUpdate))
66
+ }
67
+
68
+ // Ensure that a non-initial `stateUpdate` can only contain an `items`
69
+ // property when it comes from a `setItems()` call.
70
+ if (stateUpdate.items) {
71
+ if (!this._isSettingNewItems) {
72
+ reportError('A `stateUpdate` can only contain `items` property as a result of calling `.setItems()`')
73
+ }
74
+ }
75
+ this._isSettingNewItems = undefined
76
+
77
+ // Update `state`.
78
+ this.previousState = this.getState()
79
+ this._updateState(stateUpdate)
80
+ }
81
+
82
+ this.getInitialState = () => {
83
+ if (state) {
84
+ return getRestoredState.call(this, state)
85
+ }
86
+ return getInitialStateFromScratch.call(this)
87
+ }
88
+
89
+ this.useState = ({
90
+ getState,
91
+ updateState
92
+ }) => {
93
+ if (this._isActive) {
94
+ throw new Error('[virtual-scroller] `VirtualScroller` has already been started')
95
+ }
96
+
97
+ if (this._getState) {
98
+ throw new Error('[virtual-scroller] Custom state storage has already been configured')
99
+ }
100
+
101
+ if (render) {
102
+ throw new Error('[virtual-scroller] Creating a `VirtualScroller` class instance with a `render()` parameter means using the default (internal) state storage')
103
+ }
104
+
105
+ if (!getState || !updateState) {
106
+ throw new Error('[virtual-scroller] When using a custom state storage, one must supply both `getState()` and `updateState()` functions')
107
+ }
108
+
109
+ this._usesCustomStateStorage = true
110
+
111
+ this._getState = getState
112
+ this._updateState = updateState
113
+ }
114
+
115
+ this.useDefaultStateStorage = () => {
116
+ if (!render) {
117
+ throw new Error('[virtual-scroller] When using the default (internal) state management, one must supply a `render(state, prevState)` function parameter')
118
+ }
119
+
120
+ // Create default `getState()`/`updateState()` functions.
121
+ this._getState = defaultGetState.bind(this)
122
+ this._updateState = defaultUpdateState.bind(this)
123
+
124
+ // When `state` is stored externally, a developer is responsible for
125
+ // initializing it with the initial value.
126
+ // Otherwise, if default state management is used, set the initial state now.
127
+ const setInitialState = defaultSetInitialState.bind(this)
128
+ setInitialState(this.getInitialState())
129
+ }
130
+
131
+ function defaultGetState() {
132
+ return this.state
133
+ }
134
+
135
+ function defaultSetInitialState(newState) {
136
+ this.state = newState
137
+ }
138
+
139
+ function defaultUpdateState(stateUpdate) {
140
+ // Because this variant of `.updateState()` is "synchronous" (immediate),
141
+ // it can be written like `...prevState`, and no state updates would be lost.
142
+ // But if it was "asynchronous" (not immediate), then `...prevState`
143
+ // wouldn't work in all cases, because it could be stale in cases
144
+ // when more than a single `updateState()` call is made before
145
+ // the state actually updates, making `prevState` stale.
146
+ this.state = {
147
+ ...this.state,
148
+ ...stateUpdate
149
+ }
150
+
151
+ render(this.state, this.previousState)
152
+
153
+ this.onRender()
154
+ }
155
+
156
+ /**
157
+ * Returns the initial state of the `VirtualScroller` "from scratch".
158
+ * (i.e. not from a previously saved one).
159
+ * @return {object}
160
+ */
161
+ function getInitialStateFromScratch() {
162
+ const items = initialItems
163
+ const state = {
164
+ ...getInitialLayoutState.call(this, items),
165
+ items,
166
+ itemStates: new Array(items.length)
167
+ }
168
+ if (isDebug()) {
169
+ log('Initial state (autogenerated)', getStateSnapshot(state))
170
+ }
171
+ log('First shown item index', state.firstShownItemIndex)
172
+ log('Last shown item index', state.lastShownItemIndex)
173
+ return state
174
+ }
175
+
176
+ function getRestoredState(state) {
177
+ if (isDebug()) {
178
+ log('Restore state', getStateSnapshot(state))
179
+ }
180
+
181
+ // Possibly clean up "before resize" property in state.
182
+ // "Before resize" state property is cleaned up when all "before resize" item heights
183
+ // have been re-measured in an asynchronous `this.updateState({ beforeResize: undefined })` call.
184
+ // If `VirtualScroller` state was snapshotted externally before that `this.updateState()` call
185
+ // has been applied, then "before resize" property might have not been cleaned up properly.
186
+ state = cleanUpBeforeResizeState(state)
187
+
188
+ // Reset `verticalSpacing` so that it re-measures it after the list
189
+ // has been rendered initially. The rationale is that a previously captured
190
+ // inter-item vertical spacing can't be "trusted" in a sense that the user
191
+ // might have resized the window after the previous `state` has been snapshotted.
192
+ // If the user has resized the window, then changing window width might have
193
+ // activated different CSS `@media()` "queries" resulting in a potentially different
194
+ // vertical spacing when the `VirtualScroller` is re-created with such previously
195
+ // snapshotted state.
196
+ state = {
197
+ ...state,
198
+ verticalSpacing: undefined
199
+ }
200
+
201
+ // `this.verticalSpacing` acts as a "true" source for vertical spacing value.
202
+ // Vertical spacing is also stored in `state` but `state` updates could be
203
+ // "asynchronous" (not applied immediately) and `this.onUpdateShownItemIndexes()`
204
+ // requires vertical spacing to be correct at any time, without any delays.
205
+ // So, vertical spacing is also duplicated in `state`, but the "true" source
206
+ // is still `this.verticalSpacing`.
207
+ //
208
+ // `this.verticalSpacing` must be initialized before calling `this.getInitialStateFromScratch()`
209
+ // because `this.getInitialStateFromScratch()` uses `this.verticalSpacing` in its calculations.
210
+ //
211
+ // With the code above, `state.verticalSpacing` is always gonna be `undefined`,
212
+ // so commented out this code. It's safer to just re-measure vertical spacing
213
+ // from scratch when `VirtualScroller` is mounted.
214
+ //
215
+ // this.verticalSpacing = state ? state.verticalSpacing : undefined
216
+
217
+ // Check if the actual `columnsCount` on the screen matches the one from state.
218
+ if (isStateColumnsCountMismatch(state, {
219
+ columnsCount: this.getActualColumnsCount()
220
+ })) {
221
+ warn('Reset Layout')
222
+ state = {
223
+ ...state,
224
+ ...getInitialLayoutState.call(this, state.items)
225
+ }
226
+ }
227
+
228
+ return state
229
+ }
230
+
231
+ function getInitialLayoutState(items) {
232
+ const itemsCount = items.length
233
+ const {
234
+ firstShownItemIndex,
235
+ lastShownItemIndex,
236
+ beforeItemsHeight,
237
+ afterItemsHeight
238
+ } = this.layout.getInitialLayoutValues({
239
+ itemsCount,
240
+ columnsCount: this.getActualColumnsCount()
241
+ })
242
+ const itemHeights = new Array(itemsCount)
243
+ // Optionally preload items to be rendered.
244
+ this.onBeforeShowItems(
245
+ items,
246
+ itemHeights,
247
+ firstShownItemIndex,
248
+ lastShownItemIndex
249
+ )
250
+ return {
251
+ itemHeights,
252
+ columnsCount: this.getActualColumnsCountForState(),
253
+ verticalSpacing: this.verticalSpacing,
254
+ firstShownItemIndex,
255
+ lastShownItemIndex,
256
+ beforeItemsHeight,
257
+ afterItemsHeight
258
+ }
259
+ }
260
+
261
+ // Checks if the actual `columnsCount` on the screen matches the one from state.
262
+ //
263
+ // For example, a developer might snapshot `VirtualScroller` state
264
+ // when the user navigates from the page containing the list
265
+ // in order to later restore the list's state when the user goes "Back".
266
+ // But, the user might have also resized the window while being on that
267
+ // "other" page, and when they come "Back", their snapshotted state
268
+ // no longer qualifies. Well, it does qualify, but only partially.
269
+ // For example, `itemStates` are still valid, but first and last shown
270
+ // item indexes aren't.
271
+ //
272
+ function isStateColumnsCountMismatch(state, { columnsCount }) {
273
+ const stateColumnsCount = state.columnsCount || 1
274
+ if (stateColumnsCount !== columnsCount) {
275
+ warn('~ Columns Count changed from', stateColumnsCount, 'to', columnsCount, '~')
276
+ return true
277
+ }
278
+ const firstShownItemIndex = Math.floor(state.firstShownItemIndex / columnsCount) * columnsCount
279
+ if (firstShownItemIndex !== state.firstShownItemIndex) {
280
+ warn('~ First Shown Item Index', state.firstShownItemIndex, 'is not divisible by Columns Count', columnsCount, '~')
281
+ return true
282
+ }
283
+ }
284
+ }
@@ -0,0 +1,51 @@
1
+ import log from './utility/debug.js'
2
+ import getVerticalSpacing from './getVerticalSpacing.js'
3
+
4
+ export default function createVerticalSpacingHelpers() {
5
+ // Bind to `this` in order to prevent bugs when this function is passed by reference
6
+ // and then called with its `this` being unintentionally `window` resulting in
7
+ // the `if` condition being "falsy".
8
+ this.getVerticalSpacing = () => {
9
+ return this.verticalSpacing || 0
10
+ }
11
+
12
+ this.getVerticalSpacingBeforeResize = () => {
13
+ // `beforeResize.verticalSpacing` can be `undefined`.
14
+ // For example, if `this.updateState({ verticalSpacing })` call hasn't been applied
15
+ // before the resize happened (in case of an "asynchronous" state update).
16
+ const { beforeResize } = this.getState()
17
+ return beforeResize && beforeResize.verticalSpacing || 0
18
+ }
19
+
20
+ /**
21
+ * Measures item vertical spacing, if not measured.
22
+ * @return {object} [stateUpdate]
23
+ */
24
+ this.measureVerticalSpacingIfNotMeasured = () => {
25
+ if (this.verticalSpacing === undefined) {
26
+ this.verticalSpacing = measureVerticalSpacing.call(this)
27
+ return this.verticalSpacing
28
+ }
29
+ }
30
+
31
+ function measureVerticalSpacing() {
32
+ const {
33
+ firstShownItemIndex,
34
+ lastShownItemIndex
35
+ } = this.getState()
36
+
37
+ log('~ Measure item vertical spacing ~')
38
+
39
+ const verticalSpacing = getVerticalSpacing({
40
+ itemsContainer: this.itemsContainer,
41
+ renderedItemsCount: lastShownItemIndex - firstShownItemIndex + 1
42
+ })
43
+
44
+ if (verticalSpacing === undefined) {
45
+ log('Not enough items rendered to measure vertical spacing')
46
+ } else {
47
+ log('Item vertical spacing', verticalSpacing)
48
+ return verticalSpacing
49
+ }
50
+ }
51
+ }