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
@@ -0,0 +1,81 @@
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) (Scrollable Container) (<tbody/>)</title>
10
+
11
+ <script src="./virtual-scroller-dom.js"></script>
12
+
13
+ <link rel="stylesheet" href="./style.base.css"/>
14
+
15
+ <style>
16
+ #scrollable-container {
17
+ margin-top: 20vh;
18
+ margin-bottom: 20vh;
19
+ max-height: 60vh;
20
+ overflow: auto;
21
+ }
22
+ </style>
23
+
24
+ <style>
25
+ table {
26
+ width: 100%;
27
+ }
28
+
29
+ tr {
30
+ background: #fff
31
+ }
32
+
33
+ th, td {
34
+ padding-left: 1em;
35
+ padding-right: 1em;
36
+ padding-top: 0.2em;
37
+ padding-bottom: 0.2em;
38
+ }
39
+ </style>
40
+ </head>
41
+
42
+ <body>
43
+ <div id="scrollable-container">
44
+ <table>
45
+ <tbody id="table-rows-container">
46
+ </tbody>
47
+ </table>
48
+ </div>
49
+
50
+ <script>
51
+ // Enable debug output to console.
52
+ window.VirtualScrollerDebug = true
53
+ </script>
54
+
55
+ <script>
56
+ const rows = []
57
+ for (var i = 1; i <= 10000; i++) {
58
+ rows.push(i)
59
+ }
60
+
61
+ function renderRow(i) {
62
+ var tr = document.createElement('tr')
63
+ var td = document.createElement('td')
64
+ td.innerText = i
65
+ tr.appendChild(td)
66
+ return tr
67
+ }
68
+
69
+ function getScrollableContainer() {
70
+ return document.getElementById('scrollable-container')
71
+ }
72
+
73
+ new VirtualScroller(
74
+ document.getElementById('table-rows-container'),
75
+ rows,
76
+ renderRow,
77
+ { getScrollableContainer }
78
+ )
79
+ </script>
80
+ </body>
81
+ </html>
@@ -0,0 +1,65 @@
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) (<tbody/>)</title>
10
+
11
+ <script src="./virtual-scroller-dom.js"></script>
12
+
13
+ <link rel="stylesheet" href="./style.base.css"/>
14
+
15
+ <style>
16
+ table {
17
+ width: 100%;
18
+ }
19
+
20
+ tr {
21
+ background: #fff
22
+ }
23
+
24
+ th, td {
25
+ padding-left: 1em;
26
+ padding-right: 1em;
27
+ padding-top: 0.2em;
28
+ padding-bottom: 0.2em;
29
+ }
30
+ </style>
31
+ </head>
32
+
33
+ <body>
34
+ <table>
35
+ <tbody id="table-rows-container">
36
+ </tbody>
37
+ </table>
38
+
39
+ <script>
40
+ // Enable debug output to console.
41
+ window.VirtualScrollerDebug = true
42
+ </script>
43
+
44
+ <script>
45
+ const rows = []
46
+ for (var i = 1; i <= 10000; i++) {
47
+ rows.push(i)
48
+ }
49
+
50
+ function renderRow(i) {
51
+ var tr = document.createElement('tr')
52
+ var td = document.createElement('td')
53
+ td.innerText = i
54
+ tr.appendChild(td)
55
+ return tr
56
+ }
57
+
58
+ new VirtualScroller(
59
+ document.getElementById('table-rows-container'),
60
+ rows,
61
+ renderRow
62
+ )
63
+ </script>
64
+ </body>
65
+ </html>
@@ -6,15 +6,13 @@
6
6
  <!-- Fix document width for mobile devices. -->
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
8
 
9
- <title>DOM VirtualScroller Demo</title>
10
-
11
- <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
9
+ <title>VirtualScroller (DOM)</title>
12
10
 
13
11
  <script src="./virtual-scroller-dom.js"></script>
14
- <script src="./on-scroll-to-dom.js"></script>
15
- <script src="./messages.js"></script>
12
+ <script src="./items.js"></script>
16
13
 
17
- <link rel="stylesheet" href="./style.css"/>
14
+ <link rel="stylesheet" href="./style.base.css"/>
15
+ <link rel="stylesheet" href="./style.list.css"/>
18
16
  </head>
19
17
 
20
18
  <body>
@@ -29,7 +27,7 @@
29
27
  Latest Tweets on #politics
30
28
  </h1>
31
29
  <div id="show-previous"></div>
32
- <div id="messages"></div>
30
+ <div id="list"></div>
33
31
  <div id="show-next"></div>
34
32
  <footer>
35
33
  © Twitter Inc., 2019
@@ -39,129 +37,162 @@
39
37
  <script>
40
38
  // Enable debug output to console.
41
39
  window.VirtualScrollerDebug = true
42
- // Whether "dynamically loaded list" mode is enabled.
43
- window.dynamic = new URL(window.location).searchParams.get('dynamic')
40
+ // Pass `?pagination=✓` URL query parameter to enable pagination.
41
+ window.PAGINATION = Boolean(new URL(window.location).searchParams.get('pagination'))
44
42
  </script>
45
43
 
46
- <script type="text/babel">
44
+ <script>
47
45
  const BATCH_SIZE = 6
46
+ const COLUMNS_COUNT = 1
48
47
 
49
- function renderMessage(message) {
50
- // Message element.
51
- const root = document.createElement('article')
52
- root.classList.add('feed-message')
53
- // Message author.
54
- const author = document.createElement('a')
55
- author.setAttribute('target', '_blank')
56
- author.setAttribute('href', `https://twitter.com/${message.username}`)
57
- author.textContent = `@${message.username}`
58
- root.appendChild(author)
59
- // Message date.
60
- const time = document.createElement('time')
61
- time.setAttribute('datetime', message.date.toISOString())
62
- time.textContent = `${message.date.getMonth() + 1}/${message.date.getDate()}/${message.date.getFullYear()}`
63
- root.appendChild(time)
64
- // Message text.
65
- const text = document.createElement('p')
66
- text.textContent = message.text
67
- root.appendChild(text)
68
- // Return message element.
69
- return root
48
+ function getColumnsCount(container) {
49
+ return 1
70
50
  }
71
51
 
72
- let state
73
- if (window.dynamic) {
74
- const fromIndex = Math.floor(messages.length / 2 - BATCH_SIZE / 2)
75
- const toIndex = fromIndex + BATCH_SIZE - 1
76
- state = {
77
- fromIndex,
78
- toIndex,
79
- messages: messages.slice(fromIndex, toIndex + 1)
80
- }
81
- } else {
82
- state = {
83
- fromIndex: 0,
84
- toIndex: messages.length,
85
- messages
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
+ }
86
67
  }
87
68
  }
88
69
 
89
- function setState(newState) {
90
- state = {
91
- ...state,
92
- ...newState
93
- }
94
- if (newState.messages) {
95
- virtualScroller.setItems(newState.messages, {
96
- preserveScrollPositionOnPrependItems: true
97
- })
98
- renderShowPrevious()
99
- renderShowNext()
100
- }
101
- }
102
-
103
- const onShowPrevious = () => {
104
- let { fromIndex } = state
105
- const { toIndex } = state
70
+ function onShowPrevious(items, getState, setState) {
71
+ let { fromIndex } = getState()
72
+ const { toIndex } = getState()
106
73
  fromIndex = Math.max(fromIndex - BATCH_SIZE, 0)
107
74
  setState({
108
75
  fromIndex,
109
- messages: messages.slice(fromIndex, toIndex + 1)
76
+ items: items.slice(fromIndex, toIndex + 1)
110
77
  })
111
78
  }
112
79
 
113
- const onShowNext = () => {
114
- const { fromIndex } = state
115
- let { toIndex } = state
116
- toIndex = Math.min(toIndex + BATCH_SIZE, messages.length - 1)
80
+ function onShowNext(items, getState, setState) {
81
+ const { fromIndex } = getState()
82
+ let { toIndex } = getState()
83
+ toIndex = Math.min(toIndex + BATCH_SIZE, items.length - 1)
117
84
  setState({
118
85
  toIndex,
119
- messages: messages.slice(fromIndex, toIndex + 1)
86
+ items: items.slice(fromIndex, toIndex + 1)
120
87
  })
121
88
  }
122
89
 
123
- function renderShowPrevious() {
124
- const { fromIndex } = state
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()
125
121
  const container = document.getElementById('show-previous')
126
122
  while (container.firstChild) {
127
123
  container.removeChild(container.firstChild)
128
124
  }
129
- if (window.dynamic && fromIndex > 0) {
125
+ if (window.PAGINATION && fromIndex > 0) {
130
126
  const button = document.createElement('button')
131
127
  button.setAttribute('type', 'button')
132
128
  button.classList.add('load-items-button')
133
- button.addEventListener('click', onShowPrevious)
129
+ button.addEventListener('click', () => onShowPrevious(items, getState, setState))
134
130
  button.textContent = 'Show previous'
135
131
  container.appendChild(button)
136
132
  }
137
133
  }
138
134
 
139
- function renderShowNext() {
140
- const { toIndex } = state
135
+ function renderShowNext(items, getState, setState) {
136
+ const { toIndex } = getState()
141
137
  const container = document.getElementById('show-next')
142
138
  while (container.firstChild) {
143
139
  container.removeChild(container.firstChild)
144
140
  }
145
- if (window.dynamic && toIndex < messages.length - 1) {
141
+ if (window.PAGINATION && toIndex < items.length - 1) {
146
142
  const button = document.createElement('button')
147
143
  button.setAttribute('type', 'button')
148
144
  button.classList.add('load-items-button')
149
- button.addEventListener('click', onShowNext)
145
+ button.addEventListener('click', () => onShowNext(items, getState, setState))
150
146
  button.textContent = 'Show next'
151
147
  container.appendChild(button)
152
148
  }
153
149
  }
154
150
 
155
- const virtualScroller = new VirtualScroller(
156
- document.getElementById('messages'),
157
- state.messages,
158
- renderMessage
159
- )
151
+ class VirtualScrollerDemo {
152
+ constructor() {
153
+ this.state = getInitialState(ITEMS)
154
+ }
160
155
 
161
- renderShowPrevious()
162
- renderShowNext()
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 }
189
+ )
190
+ this.renderPrevNextButtons()
191
+ }
192
+ }
163
193
 
164
- setTimeout(() => virtualScroller.start(), 5000)
194
+ const virtualScrollerDemo = new VirtualScrollerDemo()
195
+ virtualScrollerDemo.render()
165
196
  </script>
166
197
  </body>
167
198
  </html>
@@ -6,28 +6,18 @@
6
6
  <!-- Fix document width for mobile devices. -->
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
8
 
9
- <title>React VirtualScroller Demo</title>
9
+ <title>VirtualScroller (React) (bypass)</title>
10
10
 
11
- <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
12
- <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
13
- <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.7.2/prop-types.min.js"></script>
14
- <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
11
+ <script src="./lib/react.development.js"></script>
12
+ <script src="./lib/react-dom.development.js"></script>
13
+ <script src="./lib/prop-types.min.js"></script>
14
+ <script src="./lib/babel.min.js"></script>
15
15
 
16
16
  <script src="./virtual-scroller-react.js"></script>
17
- <script src="./on-scroll-to-react.js"></script>
18
- <script src="./messages.js"></script>
17
+ <script src="./items.js"></script>
19
18
 
20
- <link rel="stylesheet" href="./style.css"/>
21
-
22
- <!--
23
- <style>
24
- #messages {
25
- border-top: 2px solid rgb(230, 236, 240);
26
- display: grid;
27
- grid-gap: 2em;
28
- }
29
- </style>
30
- -->
19
+ <link rel="stylesheet" href="./style.base.css"/>
20
+ <link rel="stylesheet" href="./style.list.css"/>
31
21
  </head>
32
22
 
33
23
  <body>
@@ -39,65 +29,83 @@
39
29
  <script>
40
30
  // Enable debug output to console.
41
31
  window.VirtualScrollerDebug = true
42
- // Whether "dynamically loaded list" mode is enabled.
43
- window.dynamic = new URL(window.location).searchParams.get('dynamic')
32
+ // Pass `?pagination=✓` URL query parameter to enable pagination.
33
+ window.PAGINATION = Boolean(new URL(window.location).searchParams.get('pagination'))
44
34
  </script>
45
35
 
46
36
  <script type="text/babel">
47
- const BATCH_SIZE = 6
37
+ const BATCH_SIZE = 6
38
+ const COLUMNS_COUNT = 1
39
+
40
+ function getColumnsCount(container) {
41
+ return 1
42
+ }
43
+
44
+ function getInitialState(items) {
45
+ if (window.PAGINATION) {
46
+ const fromIndex = Math.floor((items.length - BATCH_SIZE) / 2 / COLUMNS_COUNT) * COLUMNS_COUNT
47
+ const toIndex = fromIndex + BATCH_SIZE - 1
48
+ return {
49
+ fromIndex,
50
+ toIndex,
51
+ items: items.slice(fromIndex, toIndex + 1)
52
+ }
53
+ } else {
54
+ return {
55
+ fromIndex: 0,
56
+ toIndex: items.length,
57
+ items: items
58
+ }
59
+ }
60
+ }
48
61
 
49
- class FeedMessages extends React.Component {
62
+ function onShowPrevious(items, getState, setState) {
63
+ let { fromIndex } = getState()
64
+ const { toIndex } = getState()
65
+ fromIndex = Math.max(fromIndex - BATCH_SIZE, 0)
66
+ setState({
67
+ fromIndex,
68
+ items: items.slice(fromIndex, toIndex + 1)
69
+ })
70
+ }
71
+
72
+ function onShowNext(items, getState, setState) {
73
+ const { fromIndex } = getState()
74
+ let { toIndex } = getState()
75
+ toIndex = Math.min(toIndex + BATCH_SIZE, items.length - 1)
76
+ setState({
77
+ toIndex,
78
+ items: items.slice(fromIndex, toIndex + 1)
79
+ })
80
+ }
81
+
82
+ class VirtualScrollerDemo extends React.Component {
50
83
  constructor(props) {
51
84
  super(props)
52
- const { messages } = this.props
53
- if (window.dynamic) {
54
- const fromIndex = Math.floor(messages.length / 2 - BATCH_SIZE / 2)
55
- const toIndex = fromIndex + BATCH_SIZE - 1
56
- this.state = {
57
- fromIndex,
58
- toIndex,
59
- messages: messages.slice(fromIndex, toIndex + 1)
60
- }
61
- } else {
62
- this.state = {
63
- fromIndex: 0,
64
- toIndex: messages.length,
65
- messages
66
- }
67
- }
85
+
86
+ this.state = getInitialState(ITEMS)
68
87
  }
69
88
 
89
+ getState = () => this.state
90
+
70
91
  onShowPrevious = () => {
71
- const { messages } = this.props
72
- let { fromIndex } = this.state
73
- const { toIndex } = this.state
74
- fromIndex = Math.max(fromIndex - BATCH_SIZE, 0)
75
- this.setState({
76
- fromIndex,
77
- messages: messages.slice(fromIndex, toIndex + 1)
78
- })
92
+ onShowPrevious(ITEMS, this.getState, this.setState.bind(this))
79
93
  }
80
94
 
81
95
  onShowNext = () => {
82
- const { messages } = this.props
83
- const { fromIndex } = this.state
84
- let { toIndex } = this.state
85
- toIndex = Math.min(toIndex + BATCH_SIZE, messages.length - 1)
86
- this.setState({
87
- toIndex,
88
- messages: messages.slice(fromIndex, toIndex + 1)
89
- })
96
+ onShowNext(ITEMS, this.getState, this.setState.bind(this))
90
97
  }
91
98
 
92
99
  render() {
93
100
  const {
94
101
  fromIndex,
95
102
  toIndex,
96
- messages
103
+ items
97
104
  } = this.state
105
+
98
106
  return (
99
107
  <React.Fragment>
100
- {window.dynamic && fromIndex > 0 &&
108
+ {window.PAGINATION && fromIndex > 0 &&
101
109
  <button
102
110
  type="button"
103
111
  onClick={this.onShowPrevious}
@@ -107,11 +115,13 @@
107
115
  }
108
116
  <VirtualScroller
109
117
  bypass
110
- id="messages"
111
- items={messages}
112
- itemComponent={Message}
113
- preserveScrollPositionOnPrependItems/>
114
- {window.dynamic && toIndex < this.props.messages.length - 1 &&
118
+ id="list"
119
+ items={items}
120
+ itemComponent={Item}
121
+ preserveScrollPositionOnPrependItems
122
+ getColumnsCount={getColumnsCount}
123
+ />
124
+ {window.PAGINATION && toIndex < ITEMS.length - 1 &&
115
125
  <button
116
126
  type="button"
117
127
  onClick={this.onShowNext}
@@ -124,25 +134,23 @@
124
134
  }
125
135
  }
126
136
 
127
- const message = PropTypes.shape({
137
+ const item = PropTypes.shape({
128
138
  username: PropTypes.string.isRequired,
129
139
  date: PropTypes.instanceOf(Date).isRequired,
130
140
  text: PropTypes.string.isRequired
131
141
  })
132
142
 
133
- FeedMessages.propTypes = {
134
- messages: PropTypes.arrayOf(message).isRequired
135
- }
143
+ function Item(props) {
144
+ const { children: item } = props
136
145
 
137
- function Message(props) {
138
- const { children: message } = props
139
146
  const {
140
147
  username,
141
148
  date,
142
149
  text
143
- } = message
150
+ } = item
151
+
144
152
  return (
145
- <article className="feed-message">
153
+ <article className="list-item">
146
154
  <a target="_blank" href={`https://twitter.com/${username}`}>
147
155
  @{username}
148
156
  </a>
@@ -156,11 +164,11 @@
156
164
  )
157
165
  }
158
166
 
159
- Message.propTypes = {
160
- children: message.isRequired
167
+ Item.propTypes = {
168
+ children: item.isRequired
161
169
  }
162
170
 
163
- class Feed extends React.Component {
171
+ class Demo extends React.Component {
164
172
  render() {
165
173
  return (
166
174
  <section className="container">
@@ -168,8 +176,7 @@
168
176
  <TwitterLogo/>
169
177
  Latest Tweets on #politics
170
178
  </h1>
171
- <FeedMessages
172
- messages={messages}/>
179
+ <VirtualScrollerDemo/>
173
180
  <footer>
174
181
  © Twitter Inc., 2019
175
182
  </footer>
@@ -186,10 +193,8 @@
186
193
  )
187
194
  }
188
195
 
189
- ReactDOM.render(
190
- <Feed/>,
191
- document.getElementById('root')
192
- )
196
+ const root = ReactDOM.createRoot(document.getElementById('root'))
197
+ root.render(<Demo/>)
193
198
  </script>
194
199
  </body>
195
200
  </html>