virtual-scroller 1.14.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +309 -250
  3. package/bundle/index-dom-bypass.html +198 -0
  4. package/bundle/index-dom-grid.html +204 -0
  5. package/bundle/index-dom-scrollableContainer.html +215 -0
  6. package/bundle/index-dom-tbody-scrollableContainer.html +81 -0
  7. package/bundle/index-dom-tbody.html +65 -0
  8. package/bundle/index-dom.html +115 -84
  9. package/bundle/{index-bypass.html → index-react-bypass.html} +83 -78
  10. package/bundle/{index-grid.html → index-react-grid.html} +78 -91
  11. package/bundle/{index-scrollableContainer.html → index-react-scrollableContainer.html} +96 -91
  12. package/bundle/index-react-strictMode.html +199 -0
  13. package/bundle/index-react-tbody-scrollableContainer.html +96 -0
  14. package/bundle/index-react-tbody.html +80 -0
  15. package/bundle/{messages.js → items.js} +1 -1
  16. package/bundle/lib/babel.min.js +25 -0
  17. package/bundle/lib/prop-types.min.js +1 -0
  18. package/bundle/lib/react-dom.development.js +29924 -0
  19. package/bundle/lib/react-dom.production.min.js +267 -0
  20. package/bundle/lib/react.development.js +3343 -0
  21. package/bundle/lib/react.production.min.js +31 -0
  22. package/bundle/style.base.css +33 -0
  23. package/{website/style.css → bundle/style.list.css} +10 -43
  24. package/bundle/style.list.grid.css +23 -0
  25. package/bundle/virtual-scroller-dom.js +1 -1
  26. package/bundle/virtual-scroller-dom.js.map +1 -1
  27. package/bundle/virtual-scroller-react.js +1 -1
  28. package/bundle/virtual-scroller-react.js.map +1 -1
  29. package/bundle/virtual-scroller.js +1 -1
  30. package/bundle/virtual-scroller.js.map +1 -1
  31. package/commonjs/BeforeResize.js +1 -2
  32. package/commonjs/BeforeResize.js.map +1 -1
  33. package/commonjs/DOM/VirtualScroller.js +7 -13
  34. package/commonjs/DOM/VirtualScroller.js.map +1 -1
  35. package/commonjs/DOM/tbody.js +6 -6
  36. package/commonjs/DOM/tbody.js.map +1 -1
  37. package/commonjs/ItemHeights.js +10 -13
  38. package/commonjs/ItemHeights.js.map +1 -1
  39. package/commonjs/Layout.defaults.js +21 -0
  40. package/commonjs/Layout.defaults.js.map +1 -0
  41. package/commonjs/Layout.js +75 -64
  42. package/commonjs/Layout.js.map +1 -1
  43. package/commonjs/Layout.test.js +8 -4
  44. package/commonjs/Layout.test.js.map +1 -1
  45. package/commonjs/VirtualScroller.constructor.js +38 -4
  46. package/commonjs/VirtualScroller.constructor.js.map +1 -1
  47. package/commonjs/VirtualScroller.items.js +50 -4
  48. package/commonjs/VirtualScroller.items.js.map +1 -1
  49. package/commonjs/VirtualScroller.js +23 -14
  50. package/commonjs/VirtualScroller.js.map +1 -1
  51. package/commonjs/VirtualScroller.layout.js +40 -29
  52. package/commonjs/VirtualScroller.layout.js.map +1 -1
  53. package/commonjs/VirtualScroller.onContainerResize.js +1 -2
  54. package/commonjs/VirtualScroller.onContainerResize.js.map +1 -1
  55. package/commonjs/VirtualScroller.state.js +10 -9
  56. package/commonjs/VirtualScroller.state.js.map +1 -1
  57. package/commonjs/VirtualScroller.verticalSpacing.js +39 -6
  58. package/commonjs/VirtualScroller.verticalSpacing.js.map +1 -1
  59. package/commonjs/react/VirtualScroller.js +85 -34
  60. package/commonjs/react/VirtualScroller.js.map +1 -1
  61. package/commonjs/react/useClassName.js +2 -2
  62. package/commonjs/react/useClassName.js.map +1 -1
  63. package/commonjs/react/useForwardedRef.js +50 -0
  64. package/commonjs/react/useForwardedRef.js.map +1 -0
  65. package/commonjs/react/useInstanceMethods.js +4 -4
  66. package/commonjs/react/useInstanceMethods.js.map +1 -1
  67. package/commonjs/react/useItemKeys.js +28 -5
  68. package/commonjs/react/useItemKeys.js.map +1 -1
  69. package/commonjs/react/useOnItemHeightDidChange.js +28 -12
  70. package/commonjs/react/useOnItemHeightDidChange.js.map +1 -1
  71. package/commonjs/react/useSetItemState.js +31 -12
  72. package/commonjs/react/useSetItemState.js.map +1 -1
  73. package/commonjs/react/useState.js +9 -9
  74. package/commonjs/react/useState.js.map +1 -1
  75. package/commonjs/react/{useStateNoStaleBug.js → useStateWithRepeatableRead.js} +3 -3
  76. package/commonjs/react/useStateWithRepeatableRead.js.map +1 -0
  77. package/commonjs/react/useStyle.js +10 -4
  78. package/commonjs/react/useStyle.js.map +1 -1
  79. package/commonjs/react/useValidateTableBodyItemsContainer.js +24 -0
  80. package/commonjs/react/useValidateTableBodyItemsContainer.js.map +1 -0
  81. package/commonjs/react/useVirtualScroller.js +4 -3
  82. package/commonjs/react/useVirtualScroller.js.map +1 -1
  83. package/commonjs/test/ItemsContainer.js +10 -10
  84. package/commonjs/test/ItemsContainer.js.map +1 -1
  85. package/commonjs/test/VirtualScroller.js +25 -10
  86. package/commonjs/test/VirtualScroller.js.map +1 -1
  87. package/dom/index.d.ts +6 -5
  88. package/index.d.ts +19 -8
  89. package/modules/BeforeResize.js +1 -2
  90. package/modules/BeforeResize.js.map +1 -1
  91. package/modules/DOM/VirtualScroller.js +7 -13
  92. package/modules/DOM/VirtualScroller.js.map +1 -1
  93. package/modules/DOM/tbody.js +4 -4
  94. package/modules/DOM/tbody.js.map +1 -1
  95. package/modules/ItemHeights.js +11 -14
  96. package/modules/ItemHeights.js.map +1 -1
  97. package/modules/Layout.defaults.js +11 -0
  98. package/modules/Layout.defaults.js.map +1 -0
  99. package/modules/Layout.js +74 -64
  100. package/modules/Layout.js.map +1 -1
  101. package/modules/Layout.test.js +8 -4
  102. package/modules/Layout.test.js.map +1 -1
  103. package/modules/VirtualScroller.constructor.js +37 -4
  104. package/modules/VirtualScroller.constructor.js.map +1 -1
  105. package/modules/VirtualScroller.items.js +51 -5
  106. package/modules/VirtualScroller.items.js.map +1 -1
  107. package/modules/VirtualScroller.js +23 -14
  108. package/modules/VirtualScroller.js.map +1 -1
  109. package/modules/VirtualScroller.layout.js +40 -29
  110. package/modules/VirtualScroller.layout.js.map +1 -1
  111. package/modules/VirtualScroller.onContainerResize.js +1 -2
  112. package/modules/VirtualScroller.onContainerResize.js.map +1 -1
  113. package/modules/VirtualScroller.state.js +10 -9
  114. package/modules/VirtualScroller.state.js.map +1 -1
  115. package/modules/VirtualScroller.verticalSpacing.js +38 -6
  116. package/modules/VirtualScroller.verticalSpacing.js.map +1 -1
  117. package/modules/react/VirtualScroller.js +84 -35
  118. package/modules/react/VirtualScroller.js.map +1 -1
  119. package/modules/react/useClassName.js +3 -3
  120. package/modules/react/useClassName.js.map +1 -1
  121. package/modules/react/useForwardedRef.js +42 -0
  122. package/modules/react/useForwardedRef.js.map +1 -0
  123. package/modules/react/useInstanceMethods.js +4 -4
  124. package/modules/react/useInstanceMethods.js.map +1 -1
  125. package/modules/react/useItemKeys.js +28 -5
  126. package/modules/react/useItemKeys.js.map +1 -1
  127. package/modules/react/useOnItemHeightDidChange.js +28 -12
  128. package/modules/react/useOnItemHeightDidChange.js.map +1 -1
  129. package/modules/react/useSetItemState.js +31 -12
  130. package/modules/react/useSetItemState.js.map +1 -1
  131. package/modules/react/useState.js +9 -9
  132. package/modules/react/useState.js.map +1 -1
  133. package/modules/react/{useStateNoStaleBug.js → useStateWithRepeatableRead.js} +2 -2
  134. package/modules/react/useStateWithRepeatableRead.js.map +1 -0
  135. package/modules/react/useStyle.js +10 -4
  136. package/modules/react/useStyle.js.map +1 -1
  137. package/modules/react/useValidateTableBodyItemsContainer.js +16 -0
  138. package/modules/react/useValidateTableBodyItemsContainer.js.map +1 -0
  139. package/modules/react/useVirtualScroller.js +4 -3
  140. package/modules/react/useVirtualScroller.js.map +1 -1
  141. package/modules/test/ItemsContainer.js +10 -10
  142. package/modules/test/ItemsContainer.js.map +1 -1
  143. package/modules/test/VirtualScroller.js +25 -10
  144. package/modules/test/VirtualScroller.js.map +1 -1
  145. package/package.json +1 -1
  146. package/react/as.d.ts +42 -0
  147. package/react/index.d.ts +204 -63
  148. package/source/BeforeResize.js +1 -2
  149. package/source/DOM/VirtualScroller.js +7 -13
  150. package/source/DOM/tbody.js +5 -5
  151. package/source/ItemHeights.js +11 -12
  152. package/source/Layout.defaults.js +8 -0
  153. package/source/Layout.js +66 -53
  154. package/source/Layout.test.js +7 -2
  155. package/source/VirtualScroller.constructor.js +27 -4
  156. package/source/VirtualScroller.items.js +47 -2
  157. package/source/VirtualScroller.js +23 -14
  158. package/source/VirtualScroller.layout.js +41 -28
  159. package/source/VirtualScroller.onContainerResize.js +1 -2
  160. package/source/VirtualScroller.state.js +10 -11
  161. package/source/VirtualScroller.verticalSpacing.js +32 -6
  162. package/source/react/VirtualScroller.js +96 -33
  163. package/source/react/useClassName.js +3 -3
  164. package/source/react/useForwardedRef.js +39 -0
  165. package/source/react/useInstanceMethods.js +12 -4
  166. package/source/react/useItemKeys.js +22 -4
  167. package/source/react/useOnItemHeightDidChange.js +29 -10
  168. package/source/react/useSetItemState.js +32 -10
  169. package/source/react/useState.js +6 -6
  170. package/source/react/{useStateNoStaleBug.js → useStateWithRepeatableRead.js} +1 -1
  171. package/source/react/useStyle.js +3 -2
  172. package/source/react/useValidateTableBodyItemsContainer.js +16 -0
  173. package/source/react/useVirtualScroller.js +4 -3
  174. package/source/test/ItemsContainer.js +10 -10
  175. package/source/test/VirtualScroller.js +16 -10
  176. package/website/index-dom-bypass.html +198 -0
  177. package/website/index-dom-grid.html +204 -0
  178. package/website/index-dom-scrollableContainer.html +215 -0
  179. package/website/index-dom-tbody-scrollableContainer.html +81 -0
  180. package/website/index-dom-tbody.html +65 -0
  181. package/website/index-dom.html +117 -84
  182. package/website/index-react-bypass.html +200 -0
  183. package/website/{index-grid.html → index-react-grid.html} +79 -92
  184. package/website/index-react-scrollableContainer.html +213 -0
  185. package/website/index-react-strictMode.html +199 -0
  186. package/website/index-react-tbody-scrollableContainer.html +96 -0
  187. package/website/index-react-tbody.html +80 -0
  188. package/website/{index-bypass.html → index-react.html} +84 -70
  189. package/website/index.html +84 -69
  190. package/website/{messages.js → items.js} +1 -1
  191. package/website/lib/babel.min.js +25 -0
  192. package/website/lib/prop-types.min.js +1 -0
  193. package/website/lib/react-dom.development.js +29924 -0
  194. package/website/lib/react-dom.production.min.js +267 -0
  195. package/website/lib/react.development.js +3343 -0
  196. package/website/lib/react.production.min.js +31 -0
  197. package/website/style.base.css +33 -0
  198. package/website/style.list.css +92 -0
  199. package/website/style.list.grid.css +23 -0
  200. package/bundle/index-tbody-scrollableContainer.html +0 -70
  201. package/bundle/index-tbody.html +0 -57
  202. package/bundle/on-scroll-to-dom.js +0 -2
  203. package/bundle/on-scroll-to-dom.js.map +0 -1
  204. package/bundle/on-scroll-to-react.js +0 -2
  205. package/bundle/on-scroll-to-react.js.map +0 -1
  206. package/bundle/on-scroll-to.js +0 -2
  207. package/bundle/on-scroll-to.js.map +0 -1
  208. package/commonjs/react/useStateNoStaleBug.js.map +0 -1
  209. package/modules/react/useStateNoStaleBug.js.map +0 -1
  210. package/website/index-scrollableContainer.html +0 -208
  211. package/website/index-tbody-scrollableContainer.html +0 -70
  212. package/website/index-tbody.html +0 -57
  213. package/website/lib/on-scroll-to-dom.js +0 -2
  214. package/website/lib/on-scroll-to-dom.js.map +0 -1
  215. package/website/lib/on-scroll-to-react.js +0 -2
  216. package/website/lib/on-scroll-to-react.js.map +0 -1
@@ -2,7 +2,7 @@ import log, { isDebug } from '../utility/debug.js'
2
2
  import getStateSnapshot from '../utility/getStateSnapshot.js'
3
3
 
4
4
  import { useRef, useCallback } from 'react'
5
- import useStateNoStaleBug from './useStateNoStaleBug.js'
5
+ import useStateWithRepeatableRead from './useStateWithRepeatableRead.js'
6
6
  import useInsertionEffectDontMountTwiceInStrictMode from './useInsertionEffectDontMountTwiceInStrictMode.js'
7
7
  import useLayoutEffectDontMountTwiceInStrictMode from './useLayoutEffectDontMountTwiceInStrictMode.js'
8
8
 
@@ -16,7 +16,7 @@ export default function _useState({
16
16
  // `VirtualScroller` state gets updated from this variable.
17
17
  // The reason for that is that `VirtualScroller` state must always
18
18
  // correspond exactly to what's currently rendered on the screen.
19
- const [_newState, _setNewState] = useStateNoStaleBug(initialState)
19
+ const [_newState, _setNewState] = useStateWithRepeatableRead(initialState)
20
20
 
21
21
  // This `state` reference is what `VirtualScroller` uses internally.
22
22
  // It's the "source of truth" on the actual `VirtualScroller` state.
@@ -38,11 +38,11 @@ export default function _useState({
38
38
  // called `onHeightDidChange()` from its own `useLayoutEffect()`.
39
39
  // In those cases, the `itemCompoent`'s effect would run before
40
40
  // the `<VirtualScroller/>`'s effect, resulting in
41
- // `VirtualScroller.onItemHeightDidChange(i)` being run at a moment in time
41
+ // `VirtualScroller.onItemHeightDidChange(item)` being run at a moment in time
42
42
  // when the DOM has already been updated for the next `VirtualScroller` state
43
43
  // but the actual `VirtualScroller` state is still a previous ("stale") one
44
44
  // containing "stale" first/last shown item indexes, which would result in an
45
- // "index out of bounds" error when `onItemHeightDidChange(i)` tries to access
45
+ // "index out of bounds" error when `onItemHeightDidChange(item)` tries to access
46
46
  // and measure the DOM element from item index `i` which doesn't already/yet exist.
47
47
  //
48
48
  // An example of such situation could be seen from a `VirtualScroller` debug log
@@ -98,11 +98,11 @@ export default function _useState({
98
98
  // ~ Rendered ~
99
99
 
100
100
  // "~ Rendered ~" is what gets output when `onRender()` function gets called.
101
- // It means that `useLayoutEffect()` was triggered after `onItemHeightDidChange(i)`
101
+ // It means that `useLayoutEffect()` was triggered after `onItemHeightDidChange(item)`
102
102
  // was called and after the "ERROR" happened.
103
103
  //
104
104
  // The "ERROR" happened because new item indexes 4…5 were actually rendered instead of
105
- // item indexes 2…5 by the time the application called `onItemHeightDidChange(i)` function
105
+ // item indexes 2…5 by the time the application called `onItemHeightDidChange(item)` function
106
106
  // inside `itemComponent`'s `useLayoutEffect()`.
107
107
  // Item indexes 4…5 is what was requested in a `setState()` call, which called `_setNewState()`.
108
108
  // This means that `_newState` changes have been applied to the DOM
@@ -2,7 +2,7 @@ import { useRef, useState, useCallback } from 'react'
2
2
 
3
3
  // This hook fixes any weird intermediate inconsistent/invalid/stale state values.
4
4
  // https://github.com/facebook/react/issues/25023#issuecomment-1480463544
5
- export default function useStateNoStaleBug(initialState) {
5
+ export default function useStateWithRepeatableRead(initialState) {
6
6
  // const latestValidState = useRef(initialState)
7
7
  const latestWrittenState = useRef(initialState)
8
8
  const [_state, _setState] = useState(initialState)
@@ -1,11 +1,11 @@
1
1
  import px from '../utility/px.js'
2
2
 
3
- export default function useStyle({
3
+ export default function useStyle(style, {
4
4
  tbody,
5
5
  state
6
6
  }) {
7
7
  if (tbody) {
8
- return
8
+ return style
9
9
  }
10
10
 
11
11
  const {
@@ -14,6 +14,7 @@ export default function useStyle({
14
14
  } = state
15
15
 
16
16
  return {
17
+ ...style,
17
18
  paddingTop: px(beforeItemsHeight),
18
19
  paddingBottom: px(afterItemsHeight)
19
20
  }
@@ -0,0 +1,16 @@
1
+ import { useEffect } from 'react'
2
+
3
+ // A developer might "forget" to pass `itemsContainerComponent="tbody"` property
4
+ // when using a `<tbody/>` as a container for list items.
5
+ // This hook validates that the developer didn't "forget" to do that in such case.
6
+ export default function useValidateTableBodyItemsContainer({
7
+ virtualScroller,
8
+ tbody
9
+ }) {
10
+ useEffect(() => {
11
+ const isTableBodyItemsContainer = virtualScroller.isItemsContainerElementTableBody()
12
+ if (isTableBodyItemsContainer && !tbody) {
13
+ console.error('[virtual-scroller] When using `<tbody/>` as a container for list items, `itemsContainerComponent` property must be `"tbody"`')
14
+ }
15
+ }, [])
16
+ }
@@ -9,6 +9,7 @@ export default function useVirtualScroller({
9
9
  estimatedItemHeight,
10
10
  getEstimatedItemHeight,
11
11
  getEstimatedVisibleItemRowsCount,
12
+ getEstimatedInterItemVerticalSpacing,
12
13
  bypass,
13
14
  // bypassBatchSize,
14
15
  onItemInitialRender,
@@ -23,17 +24,16 @@ export default function useVirtualScroller({
23
24
  getScrollableContainer,
24
25
  getColumnsCount,
25
26
  getItemId,
26
- AsComponent,
27
27
  initialState,
28
28
  getInitialItemState,
29
29
  onStateChange
30
30
  }, {
31
- container
31
+ itemsContainer
32
32
  }) {
33
33
  return useMemo(() => {
34
34
  // Create `virtual-scroller` instance.
35
35
  return new VirtualScroller(
36
- () => container.current,
36
+ () => itemsContainer.current,
37
37
  items,
38
38
  {
39
39
  _useTimeoutInRenderLoop: true,
@@ -41,6 +41,7 @@ export default function useVirtualScroller({
41
41
  estimatedItemHeight,
42
42
  getEstimatedItemHeight,
43
43
  getEstimatedVisibleItemRowsCount,
44
+ getEstimatedInterItemVerticalSpacing,
44
45
  bypass,
45
46
  // bypassBatchSize,
46
47
  onItemInitialRender,
@@ -32,21 +32,21 @@ export default class ItemsContainer {
32
32
 
33
33
  let i = 0
34
34
  while (i <= renderedElementIndex) {
35
- if (startNewRow || rowWidth + children[i].width > maxWidth) {
35
+ if (startNewRow || rowWidth + children[i].getWidth() > maxWidth) {
36
36
  if (i > 0) {
37
37
  topOffset += rowHeight
38
- topOffset += children[i].marginTop
38
+ topOffset += children[i].getMarginTop()
39
39
  }
40
- rowWidth = children[i].width
41
- rowHeight = children[i].height
40
+ rowWidth = children[i].getWidth()
41
+ rowHeight = children[i].getHeight()
42
42
  if (rowWidth > maxWidth) {
43
43
  startNewRow = true
44
44
  } else {
45
45
  startNewRow = false
46
46
  }
47
47
  } else {
48
- rowWidth += children[i].width
49
- rowHeight = Math.max(rowHeight, children[i].height)
48
+ rowWidth += children[i].getWidth()
49
+ rowHeight = Math.max(rowHeight, children[i].getHeight())
50
50
  }
51
51
  i++
52
52
  }
@@ -69,7 +69,7 @@ export default class ItemsContainer {
69
69
  })
70
70
  }
71
71
 
72
- return children[renderedElementIndex].height
72
+ return children[renderedElementIndex].getHeight()
73
73
  }
74
74
 
75
75
  /**
@@ -86,11 +86,11 @@ export default class ItemsContainer {
86
86
  let rowHeight = 0
87
87
  while (rowWidth <= maxWidth && i < children.length) {
88
88
  if (rowWidth === 0 && i > 0) {
89
- const verticalSpacing = children[i].marginTop
89
+ const verticalSpacing = children[i].getMarginTop()
90
90
  contentHeight += verticalSpacing
91
91
  }
92
- rowWidth += children[i].width
93
- rowHeight = Math.max(rowHeight, children[i].height)
92
+ rowWidth += children[i].getWidth()
93
+ rowHeight = Math.max(rowHeight, children[i].getHeight())
94
94
  i++
95
95
  }
96
96
  contentHeight += rowHeight
@@ -65,17 +65,15 @@ export default class TestVirtualScroller {
65
65
  lastShownItemIndex
66
66
  } = this.virtualScroller.getState()
67
67
 
68
- console.log('~ Render List ~')
69
-
70
68
  containerElement.paddingTop = beforeItemsHeight
71
69
  containerElement.paddingBottom = afterItemsHeight
72
70
 
73
71
  containerElement.children = items
74
72
  .slice(firstShownItemIndex, lastShownItemIndex + 1)
75
73
  .map((item) => ({
76
- width: getItemWidth(),
77
- height: item.area / getItemWidth(),
78
- marginTop: verticalSpacing
74
+ getWidth: () => getItemWidth(),
75
+ getHeight: () => (item.getArea ? item.getArea() : item.area) / getItemWidth(),
76
+ getMarginTop: () => verticalSpacing
79
77
  }))
80
78
  }
81
79
 
@@ -165,7 +163,7 @@ export default class TestVirtualScroller {
165
163
  this.virtualScroller.scrollableContainer.scrollToY(scrollPosition)
166
164
  }
167
165
 
168
- resize({
166
+ updateScreenDimensions({
169
167
  screenWidth: scrollableContainerWidth,
170
168
  screenHeight: scrollableContainerHeight,
171
169
  columnsCount
@@ -185,7 +183,7 @@ export default class TestVirtualScroller {
185
183
  columnsCount,
186
184
  verticalSpacing
187
185
  }) {
188
- this.resize({
186
+ this.updateScreenDimensions({
189
187
  screenWidth,
190
188
  screenHeight,
191
189
  columnsCount,
@@ -218,12 +216,20 @@ export default class TestVirtualScroller {
218
216
  this.virtualScroller.updateLayout()
219
217
  }
220
218
 
221
- getItemScrollPosition(i) {
222
- return this.virtualScroller.getItemScrollPosition(i)
219
+ getItemScrollPosition(itemOrIndex) {
220
+ return this.virtualScroller.getItemScrollPosition(itemOrIndex)
221
+ }
222
+
223
+ onItemHeightDidChange(itemOrIndex) {
224
+ this.virtualScroller.onItemHeightDidChange(itemOrIndex)
225
+ }
226
+
227
+ setItemState(itemOrIndex, newState) {
228
+ this.virtualScroller.setItemState(itemOrIndex, newState)
223
229
  }
224
230
 
225
231
  getAverageItemHeight() {
226
- return this.virtualScroller.itemHeights.getAverage()
232
+ return this.virtualScroller.getAverageItemHeight()
227
233
  }
228
234
 
229
235
  setItems(newItems, options) {
@@ -0,0 +1,198 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <!-- Fix encoding. -->
5
+ <meta charset="utf-8">
6
+ <!-- Fix document width for mobile devices. -->
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
+
9
+ <title>VirtualScroller (DOM) (bypass)</title>
10
+
11
+ <script src="./virtual-scroller-dom.js"></script>
12
+ <script src="./items.js"></script>
13
+
14
+ <link rel="stylesheet" href="./style.base.css"/>
15
+ <link rel="stylesheet" href="./style.list.css"/>
16
+ </head>
17
+
18
+ <body>
19
+ <!-- http://tholman.com/github-corners/ -->
20
+ <a title="Go to GitHub repo" href="https://gitlab.com/catamphetamine/virtual-scroller" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
21
+
22
+ <section class="container">
23
+ <h1>
24
+ <svg class="twitter-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
25
+ <path fill="#00AAEC" fill-rule="evenodd" d="M128 23.294a51.28 51.28 0 0 1-15.079 4.237c5.424-3.328 9.587-8.606 11.548-14.892a51.718 51.718 0 0 1-16.687 6.526c-4.778-5.231-11.608-8.498-19.166-8.498-14.493 0-26.251 12.057-26.251 26.927 0 2.111.225 4.16.676 6.133-21.824-1.126-41.17-11.835-54.131-28.145a27.422 27.422 0 0 0-3.554 13.552c0 9.338 4.636 17.581 11.683 22.412-4.297-.131-8.355-1.356-11.901-3.359v.331c0 13.051 9.053 23.937 21.074 26.403-2.201.632-4.523.948-6.92.948-1.69 0-3.343-.162-4.944-.478 3.343 10.694 13.035 18.483 24.53 18.691-8.986 7.227-20.315 11.533-32.614 11.533-2.119 0-4.215-.123-6.266-.37 11.623 7.627 25.432 12.088 40.255 12.088 48.309 0 74.717-41.026 74.717-76.612a89.39 89.39 0 0 0-.068-3.49A53.862 53.862 0 0 0 128 23.294" clip-rule="evenodd"></path>
26
+ </svg>
27
+ Latest Tweets on #politics
28
+ </h1>
29
+ <div id="show-previous"></div>
30
+ <div id="list"></div>
31
+ <div id="show-next"></div>
32
+ <footer>
33
+ © Twitter Inc., 2019
34
+ </footer>
35
+ </section>
36
+
37
+ <script>
38
+ // Enable debug output to console.
39
+ window.VirtualScrollerDebug = true
40
+ // Pass `?pagination=✓` URL query parameter to enable pagination.
41
+ window.PAGINATION = Boolean(new URL(window.location).searchParams.get('pagination'))
42
+ </script>
43
+
44
+ <script>
45
+ const BATCH_SIZE = 6
46
+ const COLUMNS_COUNT = 1
47
+
48
+ function getColumnsCount(container) {
49
+ return 1
50
+ }
51
+
52
+ function getInitialState(items) {
53
+ if (window.PAGINATION) {
54
+ const fromIndex = Math.floor((items.length - BATCH_SIZE) / 2 / COLUMNS_COUNT) * COLUMNS_COUNT
55
+ const toIndex = fromIndex + BATCH_SIZE - 1
56
+ return {
57
+ fromIndex,
58
+ toIndex,
59
+ items: items.slice(fromIndex, toIndex + 1)
60
+ }
61
+ } else {
62
+ return {
63
+ fromIndex: 0,
64
+ toIndex: items.length,
65
+ items: items
66
+ }
67
+ }
68
+ }
69
+
70
+ function onShowPrevious(items, getState, setState) {
71
+ let { fromIndex } = getState()
72
+ const { toIndex } = getState()
73
+ fromIndex = Math.max(fromIndex - BATCH_SIZE, 0)
74
+ setState({
75
+ fromIndex,
76
+ items: items.slice(fromIndex, toIndex + 1)
77
+ })
78
+ }
79
+
80
+ function onShowNext(items, getState, setState) {
81
+ const { fromIndex } = getState()
82
+ let { toIndex } = getState()
83
+ toIndex = Math.min(toIndex + BATCH_SIZE, items.length - 1)
84
+ setState({
85
+ toIndex,
86
+ items: items.slice(fromIndex, toIndex + 1)
87
+ })
88
+ }
89
+
90
+ function renderItem(item) {
91
+ const { username, date, text } = item
92
+
93
+ // Comment element.
94
+ const itemElement = document.createElement('article')
95
+ itemElement.classList.add('list-item')
96
+
97
+ // Comment author.
98
+ const authorElement = document.createElement('a')
99
+ authorElement.setAttribute('target', '_blank')
100
+ authorElement.setAttribute('href', `https://twitter.com/${username}`)
101
+ authorElement.textContent = `@${username}`
102
+ itemElement.appendChild(authorElement)
103
+
104
+ // Comment date.
105
+ const timeElement = document.createElement('time')
106
+ timeElement.setAttribute('datetime', date.toISOString())
107
+ timeElement.textContent = `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`
108
+ itemElement.appendChild(timeElement)
109
+
110
+ // Comment text.
111
+ const textElement = document.createElement('p')
112
+ textElement.textContent = text
113
+ itemElement.appendChild(textElement)
114
+
115
+ // Return comment element.
116
+ return itemElement
117
+ }
118
+
119
+ function renderShowPrevious(items, getState, setState) {
120
+ const { fromIndex } = getState()
121
+ const container = document.getElementById('show-previous')
122
+ while (container.firstChild) {
123
+ container.removeChild(container.firstChild)
124
+ }
125
+ if (window.PAGINATION && fromIndex > 0) {
126
+ const button = document.createElement('button')
127
+ button.setAttribute('type', 'button')
128
+ button.classList.add('load-items-button')
129
+ button.addEventListener('click', () => onShowPrevious(items, getState, setState))
130
+ button.textContent = 'Show previous'
131
+ container.appendChild(button)
132
+ }
133
+ }
134
+
135
+ function renderShowNext(items, getState, setState) {
136
+ const { toIndex } = getState()
137
+ const container = document.getElementById('show-next')
138
+ while (container.firstChild) {
139
+ container.removeChild(container.firstChild)
140
+ }
141
+ if (window.PAGINATION && toIndex < items.length - 1) {
142
+ const button = document.createElement('button')
143
+ button.setAttribute('type', 'button')
144
+ button.classList.add('load-items-button')
145
+ button.addEventListener('click', () => onShowNext(items, getState, setState))
146
+ button.textContent = 'Show next'
147
+ container.appendChild(button)
148
+ }
149
+ }
150
+
151
+ class VirtualScrollerDemo {
152
+ constructor() {
153
+ this.state = getInitialState(ITEMS)
154
+ }
155
+
156
+ getState = () => {
157
+ return this.state
158
+ }
159
+
160
+ setState = (newState) => {
161
+ const prevState = this.state
162
+ this.state = {
163
+ ...this.state,
164
+ ...newState
165
+ }
166
+ this.onStateChange(this.state, prevState)
167
+ }
168
+
169
+ onStateChange(state, prevState) {
170
+ if (state.items !== prevState.items) {
171
+ this.virtualScroller.setItems(state.items, {
172
+ preserveScrollPositionOnPrependItems: true
173
+ })
174
+ this.renderPrevNextButtons()
175
+ }
176
+ }
177
+
178
+ renderPrevNextButtons() {
179
+ renderShowPrevious(ITEMS, this.getState, this.setState)
180
+ renderShowNext(ITEMS, this.getState, this.setState)
181
+ }
182
+
183
+ render() {
184
+ this.virtualScroller = new VirtualScroller(
185
+ document.getElementById('list'),
186
+ this.getState().items,
187
+ renderItem,
188
+ { getColumnsCount, bypass: true }
189
+ )
190
+ this.renderPrevNextButtons()
191
+ }
192
+ }
193
+
194
+ const virtualScrollerDemo = new VirtualScrollerDemo()
195
+ virtualScrollerDemo.render()
196
+ </script>
197
+ </body>
198
+ </html>
@@ -0,0 +1,204 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <!-- Fix encoding. -->
5
+ <meta charset="utf-8">
6
+ <!-- Fix document width for mobile devices. -->
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
+
9
+ <title>VirtualScroller (DOM) (Grid)</title>
10
+
11
+ <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
12
+
13
+ <script src="./virtual-scroller-dom.js"></script>
14
+ <script src="./items.js"></script>
15
+
16
+ <link rel="stylesheet" href="./style.base.css"/>
17
+ <link rel="stylesheet" href="./style.list.css"/>
18
+ <link rel="stylesheet" href="./style.list.grid.css"/>
19
+ </head>
20
+
21
+ <body>
22
+ <!-- http://tholman.com/github-corners/ -->
23
+ <a title="Go to GitHub repo" href="https://gitlab.com/catamphetamine/virtual-scroller" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
24
+
25
+ <section class="container">
26
+ <h1>
27
+ <svg class="twitter-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
28
+ <path fill="#00AAEC" fill-rule="evenodd" d="M128 23.294a51.28 51.28 0 0 1-15.079 4.237c5.424-3.328 9.587-8.606 11.548-14.892a51.718 51.718 0 0 1-16.687 6.526c-4.778-5.231-11.608-8.498-19.166-8.498-14.493 0-26.251 12.057-26.251 26.927 0 2.111.225 4.16.676 6.133-21.824-1.126-41.17-11.835-54.131-28.145a27.422 27.422 0 0 0-3.554 13.552c0 9.338 4.636 17.581 11.683 22.412-4.297-.131-8.355-1.356-11.901-3.359v.331c0 13.051 9.053 23.937 21.074 26.403-2.201.632-4.523.948-6.92.948-1.69 0-3.343-.162-4.944-.478 3.343 10.694 13.035 18.483 24.53 18.691-8.986 7.227-20.315 11.533-32.614 11.533-2.119 0-4.215-.123-6.266-.37 11.623 7.627 25.432 12.088 40.255 12.088 48.309 0 74.717-41.026 74.717-76.612a89.39 89.39 0 0 0-.068-3.49A53.862 53.862 0 0 0 128 23.294" clip-rule="evenodd"></path>
29
+ </svg>
30
+ Latest Tweets on #politics
31
+ </h1>
32
+ <div id="show-previous"></div>
33
+ <div id="list"></div>
34
+ <div id="show-next"></div>
35
+ <footer>
36
+ © Twitter Inc., 2019
37
+ </footer>
38
+ </section>
39
+
40
+ <script>
41
+ // Enable debug output to console.
42
+ window.VirtualScrollerDebug = true
43
+ // Pass `?pagination=✓` URL query parameter to enable pagination.
44
+ window.PAGINATION = Boolean(new URL(window.location).searchParams.get('pagination'))
45
+ </script>
46
+
47
+ <script type="text/babel">
48
+ const BATCH_SIZE = 6
49
+ const COLUMNS_COUNT = 3
50
+
51
+ function getColumnsCount(container) {
52
+ if (container.getWidth() > 1280) {
53
+ return COLUMNS_COUNT
54
+ }
55
+ return 1
56
+ }
57
+
58
+ function getInitialState(items) {
59
+ if (window.PAGINATION) {
60
+ const fromIndex = Math.floor((items.length - BATCH_SIZE) / 2 / COLUMNS_COUNT) * COLUMNS_COUNT
61
+ const toIndex = fromIndex + BATCH_SIZE - 1
62
+ return {
63
+ fromIndex,
64
+ toIndex,
65
+ items: items.slice(fromIndex, toIndex + 1)
66
+ }
67
+ } else {
68
+ return {
69
+ fromIndex: 0,
70
+ toIndex: items.length,
71
+ items: items
72
+ }
73
+ }
74
+ }
75
+
76
+ function onShowPrevious(items, getState, setState) {
77
+ let { fromIndex } = getState()
78
+ const { toIndex } = getState()
79
+ fromIndex = Math.max(fromIndex - BATCH_SIZE, 0)
80
+ setState({
81
+ fromIndex,
82
+ items: items.slice(fromIndex, toIndex + 1)
83
+ })
84
+ }
85
+
86
+ function onShowNext(items, getState, setState) {
87
+ const { fromIndex } = getState()
88
+ let { toIndex } = getState()
89
+ toIndex = Math.min(toIndex + BATCH_SIZE, items.length - 1)
90
+ setState({
91
+ toIndex,
92
+ items: items.slice(fromIndex, toIndex + 1)
93
+ })
94
+ }
95
+
96
+ function renderItem(item) {
97
+ const { username, date, text } = item
98
+
99
+ // Comment element.
100
+ const itemElement = document.createElement('article')
101
+ itemElement.classList.add('list-item')
102
+
103
+ // Comment author.
104
+ const authorElement = document.createElement('a')
105
+ authorElement.setAttribute('target', '_blank')
106
+ authorElement.setAttribute('href', `https://twitter.com/${username}`)
107
+ authorElement.textContent = `@${username}`
108
+ itemElement.appendChild(authorElement)
109
+
110
+ // Comment date.
111
+ const timeElement = document.createElement('time')
112
+ timeElement.setAttribute('datetime', date.toISOString())
113
+ timeElement.textContent = `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`
114
+ itemElement.appendChild(timeElement)
115
+
116
+ // Comment text.
117
+ const textElement = document.createElement('p')
118
+ textElement.textContent = text
119
+ itemElement.appendChild(textElement)
120
+
121
+ // Return comment element.
122
+ return itemElement
123
+ }
124
+
125
+ function renderShowPrevious(items, getState, setState) {
126
+ const { fromIndex } = getState()
127
+ const container = document.getElementById('show-previous')
128
+ while (container.firstChild) {
129
+ container.removeChild(container.firstChild)
130
+ }
131
+ if (window.PAGINATION && fromIndex > 0) {
132
+ const button = document.createElement('button')
133
+ button.setAttribute('type', 'button')
134
+ button.classList.add('load-items-button')
135
+ button.addEventListener('click', () => onShowPrevious(items, getState, setState))
136
+ button.textContent = 'Show previous'
137
+ container.appendChild(button)
138
+ }
139
+ }
140
+
141
+ function renderShowNext(items, getState, setState) {
142
+ const { toIndex } = getState()
143
+ const container = document.getElementById('show-next')
144
+ while (container.firstChild) {
145
+ container.removeChild(container.firstChild)
146
+ }
147
+ if (window.PAGINATION && toIndex < items.length - 1) {
148
+ const button = document.createElement('button')
149
+ button.setAttribute('type', 'button')
150
+ button.classList.add('load-items-button')
151
+ button.addEventListener('click', () => onShowNext(items, getState, setState))
152
+ button.textContent = 'Show next'
153
+ container.appendChild(button)
154
+ }
155
+ }
156
+
157
+ class VirtualScrollerDemo {
158
+ constructor() {
159
+ this.state = getInitialState(ITEMS)
160
+ }
161
+
162
+ getState = () => {
163
+ return this.state
164
+ }
165
+
166
+ setState = (newState) => {
167
+ const prevState = this.state
168
+ this.state = {
169
+ ...this.state,
170
+ ...newState
171
+ }
172
+ this.onStateChange(this.state, prevState)
173
+ }
174
+
175
+ onStateChange(state, prevState) {
176
+ if (state.items !== prevState.items) {
177
+ this.virtualScroller.setItems(state.items, {
178
+ preserveScrollPositionOnPrependItems: true
179
+ })
180
+ this.renderPrevNextButtons()
181
+ }
182
+ }
183
+
184
+ renderPrevNextButtons() {
185
+ renderShowPrevious(ITEMS, this.getState, this.setState)
186
+ renderShowNext(ITEMS, this.getState, this.setState)
187
+ }
188
+
189
+ render() {
190
+ this.virtualScroller = new VirtualScroller(
191
+ document.getElementById('list'),
192
+ this.getState().items,
193
+ renderItem,
194
+ { getColumnsCount }
195
+ )
196
+ this.renderPrevNextButtons()
197
+ }
198
+ }
199
+
200
+ const virtualScrollerDemo = new VirtualScrollerDemo()
201
+ virtualScrollerDemo.render()
202
+ </script>
203
+ </body>
204
+ </html>