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
@@ -0,0 +1,176 @@
1
+ import Layout from './Layout.js'
2
+
3
+ import Engine from './test/Engine.js'
4
+
5
+ describe('Layout', function() {
6
+ it('should work', function() {
7
+ const SCREEN_HEIGHT = 400
8
+
9
+ const scrollableContainer = {
10
+ width: 800,
11
+ height: SCREEN_HEIGHT
12
+ }
13
+
14
+ const ITEM_WIDTH = scrollableContainer.width
15
+ const ITEM_HEIGHT = 200
16
+
17
+ const VERTICAL_SPACING = 100
18
+
19
+ const items = new Array(9).fill(ITEM_WIDTH * ITEM_HEIGHT)
20
+
21
+ const layout = new Layout({
22
+ getPrerenderMargin: () => SCREEN_HEIGHT,
23
+ getVerticalSpacing: () => VERTICAL_SPACING,
24
+ getColumnsCount: () => 1,
25
+ getItemHeight: (i) => items[i] / scrollableContainer.width,
26
+ getBeforeResizeItemsCount: () => 0,
27
+ getAverageItemHeight: () => ITEM_HEIGHT,
28
+ getScrollableContainerHeight: () => scrollableContainer.height
29
+ })
30
+
31
+ // Initial render.
32
+ layout.getShownItemIndexes({
33
+ itemsCount: items.length,
34
+ visibleAreaTop: 0,
35
+ visibleAreaBottom: SCREEN_HEIGHT
36
+ }).should.deep.equal({
37
+ firstShownItemIndex: 0,
38
+ lastShownItemIndex: 2
39
+ })
40
+
41
+ // The first item is almost hidden.
42
+ layout.getShownItemIndexes({
43
+ itemsCount: items.length,
44
+ visibleAreaTop: SCREEN_HEIGHT + ITEM_HEIGHT - 1,
45
+ visibleAreaBottom: (SCREEN_HEIGHT + ITEM_HEIGHT - 1) + SCREEN_HEIGHT
46
+ }).should.deep.equal({
47
+ firstShownItemIndex: 0,
48
+ lastShownItemIndex: 4
49
+ })
50
+
51
+ // The first item is hidden.
52
+ layout.getShownItemIndexes({
53
+ itemsCount: items.length,
54
+ visibleAreaTop: SCREEN_HEIGHT + ITEM_HEIGHT,
55
+ visibleAreaBottom: (SCREEN_HEIGHT + ITEM_HEIGHT) + SCREEN_HEIGHT
56
+ }).should.deep.equal({
57
+ firstShownItemIndex: 1,
58
+ lastShownItemIndex: 4
59
+ })
60
+
61
+ // A new item at the bottom is almost visible.
62
+ layout.getShownItemIndexes({
63
+ itemsCount: items.length,
64
+ visibleAreaTop: (ITEM_HEIGHT + VERTICAL_SPACING) * 5 - SCREEN_HEIGHT * 2,
65
+ visibleAreaBottom: (ITEM_HEIGHT + VERTICAL_SPACING) * 5 - SCREEN_HEIGHT
66
+ }).should.deep.equal({
67
+ firstShownItemIndex: 1,
68
+ lastShownItemIndex: 4
69
+ })
70
+
71
+ // A new item at the bottom is visible.
72
+ layout.getShownItemIndexes({
73
+ itemsCount: items.length,
74
+ visibleAreaTop: (ITEM_HEIGHT + VERTICAL_SPACING) * 5 + 1 - SCREEN_HEIGHT * 2,
75
+ visibleAreaBottom: (ITEM_HEIGHT + VERTICAL_SPACING) * 5 + 1 - SCREEN_HEIGHT
76
+ }).should.deep.equal({
77
+ firstShownItemIndex: 1,
78
+ lastShownItemIndex: 5
79
+ })
80
+ })
81
+
82
+ it('should update layout for items incremental change', function() {
83
+ const scrollableContainer = {
84
+ width: 800,
85
+ height: 400
86
+ }
87
+
88
+ const ITEM_WIDTH = scrollableContainer.width
89
+ const ITEM_HEIGHT = 200
90
+
91
+ const items = new Array(9).fill(ITEM_WIDTH * ITEM_HEIGHT)
92
+
93
+ const VERTICAL_SPACING = 100
94
+
95
+ const layout = new Layout({
96
+ getPrerenderMargin: () => scrollableContainer.height,
97
+ getVerticalSpacing: () => VERTICAL_SPACING,
98
+ getColumnsCount: () => 1,
99
+ getItemHeight: (i) => ITEM_HEIGHT,
100
+ getBeforeResizeItemsCount: () => 0,
101
+ getAverageItemHeight: () => ITEM_HEIGHT,
102
+ getScrollableContainerHeight: () => scrollableContainer.height
103
+ })
104
+
105
+ layout.getLayoutUpdateForItemsDiff(
106
+ {
107
+ firstShownItemIndex: 3,
108
+ lastShownItemIndex: 5,
109
+ beforeItemsHeight: 3 * (ITEM_HEIGHT + VERTICAL_SPACING),
110
+ afterItemsHeight: 3 * (ITEM_HEIGHT + VERTICAL_SPACING)
111
+ },
112
+ {
113
+ prependedItemsCount: 5,
114
+ appendedItemsCount: 5
115
+ }, {
116
+ itemsCount: 5 + 5 + items.length,
117
+ columnsCount: 1
118
+ }
119
+ ).should.deep.equal({
120
+ firstShownItemIndex: 5 + 3,
121
+ lastShownItemIndex: 5 + 5,
122
+ beforeItemsHeight: (5 + 3) * (ITEM_HEIGHT + VERTICAL_SPACING),
123
+ afterItemsHeight: (3 + 5) * (ITEM_HEIGHT + VERTICAL_SPACING)
124
+ })
125
+ })
126
+
127
+ it('should update layout for items incremental change (rows get rebalanced)', function() {
128
+ const scrollableContainer = {
129
+ width: 800,
130
+ height: 400
131
+ }
132
+ const ITEM_WIDTH = scrollableContainer.width
133
+ const ITEM_HEIGHT = 400
134
+
135
+ const items = new Array(9).fill(ITEM_WIDTH * ITEM_HEIGHT)
136
+
137
+ const VERTICAL_SPACING = 100
138
+
139
+ const layout = new Layout({
140
+ getPrerenderMargin: () => scrollableContainer.height,
141
+ getVerticalSpacing: () => VERTICAL_SPACING,
142
+ getColumnsCount: () => 4,
143
+ getItemHeight: () => ITEM_HEIGHT,
144
+ getBeforeResizeItemsCount: () => 0,
145
+ getAverageItemHeight: () => ITEM_HEIGHT,
146
+ getScrollableContainerHeight: () => scrollableContainer.height
147
+ })
148
+
149
+ let shouldResetGridLayout
150
+
151
+ layout.getLayoutUpdateForItemsDiff(
152
+ {
153
+ firstShownItemIndex: 3,
154
+ lastShownItemIndex: 5,
155
+ beforeItemsHeight: 3 * (ITEM_HEIGHT + VERTICAL_SPACING),
156
+ afterItemsHeight: 3 * (ITEM_HEIGHT + VERTICAL_SPACING)
157
+ },
158
+ {
159
+ prependedItemsCount: 5,
160
+ appendedItemsCount: 5
161
+ }, {
162
+ itemsCount: 5 + 5 + items.length,
163
+ columnsCount: 4,
164
+ shouldRestoreScrollPosition: true,
165
+ onResetGridLayout: () => shouldResetGridLayout = true
166
+ }
167
+ ).should.deep.equal({
168
+ firstShownItemIndex: 0,
169
+ lastShownItemIndex: 5 + 5,
170
+ beforeItemsHeight: 0,
171
+ afterItemsHeight: 5 * (ITEM_HEIGHT + VERTICAL_SPACING)
172
+ })
173
+
174
+ shouldResetGridLayout.should.equal(true)
175
+ })
176
+ })
@@ -0,0 +1,95 @@
1
+ export default class ListHeightMeasurement {
2
+ constructor({
3
+ itemsContainer,
4
+ getListTopOffset
5
+ }) {
6
+ this.itemsContainer = itemsContainer
7
+ this.getListTopOffset = getListTopOffset
8
+ }
9
+
10
+ /**
11
+ * Snapshots the list height while `previousItems` are still rendered,
12
+ * before rendering `newItems`. The list height will be re-measured
13
+ * after the new items have been rendered, yielding the list height difference
14
+ * which is gonna be the amount to scroll vertically in order to restore
15
+ * the previous scroll position. Is only used when prepending items.
16
+ * @param {any[]} previousItems
17
+ * @param {any[]} newItems
18
+ * @param {number} prependedItemsCount
19
+ */
20
+ snapshotListHeightBeforeAddingNewItems({
21
+ previousItems,
22
+ newItems,
23
+ prependedItemsCount
24
+ }) {
25
+ // If there were no items in the list
26
+ // then there's no point in restoring scroll position.
27
+ if (previousItems.length === 0) {
28
+ return
29
+ }
30
+
31
+ // If no items were prepended then no need to restore scroll position.
32
+ if (prependedItemsCount === 0) {
33
+ return
34
+ }
35
+
36
+ // The first item is supposed to be shown when the user clicks
37
+ // "Show previous items" button. If it isn't shown though,
38
+ // could still calculate the first item's top position using
39
+ // the values from `itemHeights` and `verticalSpacing`.
40
+ // But that would be a weird non-realistic scenario.
41
+ // if (firstShownItemIndex > 0) {
42
+ // let i = firstShownItemIndex - 1
43
+ // while (i >= 0) {
44
+ // firstItemTopOffset += itemHeights[i] + verticalSpacing
45
+ // i--
46
+ // }
47
+ // }
48
+
49
+ // This part is longer relevant: <ReactVirtualScroller/> no longer calls
50
+ // this function two times consequtively.
51
+ //
52
+ // // If the scroll position has already been captured for restoration,
53
+ // // then don't capture it the second time.
54
+ // if (this._snapshot &&
55
+ // this._snapshot.previousItems === previousItems &&
56
+ // this._snapshot.newItems === newItems) {
57
+ // return
58
+ // }
59
+
60
+ this._snapshot = {
61
+ previousItems,
62
+ newItems,
63
+ itemIndex: prependedItemsCount,
64
+ itemTopOffset: this.itemsContainer.getNthRenderedItemTopOffset(0),
65
+ // Snapshot list top offset inside the scrollable container too
66
+ // because it's common to hide the "Show previous items" button
67
+ // when the user has browsed to the top of the list, which causes
68
+ // the list's top position to shift upwards due to the button
69
+ // no longer being rendered. Tracking list top offset doesn't
70
+ // fit here that well, but it makes sense in real-world applications.
71
+ listTopOffset: this.getListTopOffset()
72
+ }
73
+ }
74
+
75
+ getAnchorItemIndex() {
76
+ return this._snapshot.itemIndex
77
+ }
78
+
79
+ hasSnapshot() {
80
+ return this._snapshot !== undefined
81
+ }
82
+
83
+ getListBottomOffsetChange() {
84
+ const { itemIndex, itemTopOffset, listTopOffset } = this._snapshot
85
+ // `firstShownItemIndex` is supposed to be `0` at this point,
86
+ // so `renderedElementIndex` would be the same as the `itemIndex`.
87
+ const itemTopOffsetNew = this.itemsContainer.getNthRenderedItemTopOffset(itemIndex)
88
+ const listTopOffsetNew = this.getListTopOffset()
89
+ return (itemTopOffsetNew - itemTopOffset) + (listTopOffsetNew - listTopOffset)
90
+ }
91
+
92
+ reset() {
93
+ this._snapshot = undefined
94
+ }
95
+ }
package/source/Resize.js CHANGED
@@ -1,72 +1,96 @@
1
- import { LAYOUT_REASON } from './Layout'
2
- import debounce from './utility/debounce'
1
+ import debounce from './utility/debounce.js'
2
+ import log from './utility/debug.js'
3
3
 
4
4
  export default class Resize {
5
5
  constructor({
6
6
  bypass,
7
- scrollableContainer,
8
- getContainerElement,
9
- updateLayout,
10
- resetStateAndLayout
7
+ getWidth,
8
+ getHeight,
9
+ listenForResize,
10
+ onResizeStart,
11
+ onResizeStop,
12
+ onHeightChange,
13
+ onWidthChange,
14
+ onNoChange
11
15
  }) {
12
16
  this.bypass = bypass
13
- this.scrollableContainer = scrollableContainer
14
- this.getContainerElement = getContainerElement
15
- this.updateLayout = updateLayout
16
- this.resetStateAndLayout = resetStateAndLayout
17
+
18
+ this.onHeightChange = onHeightChange
19
+ this.onWidthChange = onWidthChange
20
+ this.onNoChange = onNoChange
21
+
22
+ this.getWidth = getWidth
23
+ this.getHeight = getHeight
24
+ this.listenForResize = listenForResize
25
+
26
+ this.onResize = debounce(
27
+ this._onResize,
28
+ SCROLLABLE_CONTAINER_RESIZE_DEBOUNCE_INTERVAL,
29
+ {
30
+ onStart: onResizeStart,
31
+ onStop: onResizeStop
32
+ }
33
+ )
17
34
  }
18
35
 
19
- listen() {
36
+ start() {
37
+ this.isActive = true
20
38
  if (this.bypass) {
21
39
  return
22
40
  }
23
- this.isRendered = true
24
- this.scrollableContainerWidth = this.scrollableContainer.getWidth()
25
- this.scrollableContainerHeight = this.scrollableContainer.getHeight()
26
- this.scrollableContainerUnlistenResize = this.scrollableContainer.onResize(this.onResize, {
27
- container: this.getContainerElement()
28
- })
41
+ this.width = this.getWidth()
42
+ this.height = this.getHeight()
43
+ this.unlistenResize = this.listenForResize(this.onResize)
29
44
  }
30
45
 
31
46
  stop() {
32
- this.isRendered = false
33
- if (this.scrollableContainerUnlistenResize) {
34
- this.scrollableContainerUnlistenResize()
47
+ this.isActive = false
48
+ this.width = undefined
49
+ this.height = undefined
50
+ if (this.unlistenResize) {
51
+ this.unlistenResize()
52
+ this.unlistenResize = undefined
35
53
  }
36
54
  }
37
55
 
38
56
  /**
39
57
  * On scrollable container resize.
40
58
  */
41
- onResize = debounce(() => {
59
+ _onResize = () => {
42
60
  // If `VirtualScroller` has been unmounted
43
61
  // while `debounce()`'s `setTimeout()` was waiting, then exit.
44
- if (!this.isRendered) {
62
+ // If the `VirtualScroller` gets restarted later, it will detect
63
+ // that `state.scrollableContainerWidth` doesn't match the actual
64
+ // scrollable container width, and will call `this.onResize()`.
65
+ if (!this.isActive) {
45
66
  return
46
67
  }
47
- const prevScrollableContainerWidth = this.scrollableContainerWidth
48
- const prevScrollableContainerHeight = this.scrollableContainerHeight
49
- this.scrollableContainerWidth = this.scrollableContainer.getWidth()
50
- this.scrollableContainerHeight = this.scrollableContainer.getHeight()
51
- if (this.scrollableContainerWidth === prevScrollableContainerWidth) {
52
- if (this.scrollableContainerHeight === prevScrollableContainerHeight) {
68
+
69
+ const prevScrollableContainerWidth = this.width
70
+ const prevScrollableContainerHeight = this.height
71
+
72
+ this.width = this.getWidth()
73
+ this.height = this.getHeight()
74
+
75
+ if (this.width === prevScrollableContainerWidth) {
76
+ if (this.height === prevScrollableContainerHeight) {
53
77
  // The dimensions of the container didn't change,
54
78
  // so there's no need to re-layout anything.
55
- return
79
+ this.onNoChange()
56
80
  } else {
57
81
  // Scrollable container height has changed,
58
82
  // so just recalculate shown item indexes.
59
83
  // No need to perform a re-layout from scratch.
60
- this.updateLayout({ reason: LAYOUT_REASON.RESIZE })
84
+ this.onHeightChange(prevScrollableContainerHeight, this.height)
61
85
  }
62
86
  } else {
63
87
  // Reset item heights, because if scrollable container's width (or height)
64
88
  // has changed, then the list width (or height) most likely also has changed,
65
89
  // and also some CSS `@media()` rules might have been added or removed.
66
90
  // So re-render the list entirely.
67
- this.resetStateAndLayout()
91
+ this.onWidthChange(prevScrollableContainerWidth, this.width)
68
92
  }
69
- }, SCROLLABLE_CONTAINER_RESIZE_DEBOUNCE_INTERVAL)
93
+ }
70
94
  }
71
95
 
72
96
  const SCROLLABLE_CONTAINER_RESIZE_DEBOUNCE_INTERVAL = 250