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,199 @@
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 (React) (<StrictMode/>)</title>
10
+
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
+
16
+ <script src="./virtual-scroller-react.js"></script>
17
+ <script src="./items.js"></script>
18
+
19
+ <link rel="stylesheet" href="./style.base.css"/>
20
+ <link rel="stylesheet" href="./style.list.css"/>
21
+ </head>
22
+
23
+ <body>
24
+ <!-- http://tholman.com/github-corners/ -->
25
+ <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>
26
+
27
+ <div id="root"></div>
28
+
29
+ <script>
30
+ // Enable debug output to console.
31
+ window.VirtualScrollerDebug = true
32
+ // Pass `?pagination=✓` URL query parameter to enable pagination.
33
+ window.PAGINATION = Boolean(new URL(window.location).searchParams.get('pagination'))
34
+ </script>
35
+
36
+ <script type="text/babel">
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
+ }
61
+
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 {
83
+ constructor(props) {
84
+ super(props)
85
+
86
+ this.state = getInitialState(ITEMS)
87
+ }
88
+
89
+ getState = () => this.state
90
+
91
+ onShowPrevious = () => {
92
+ onShowPrevious(ITEMS, this.getState, this.setState.bind(this))
93
+ }
94
+
95
+ onShowNext = () => {
96
+ onShowNext(ITEMS, this.getState, this.setState.bind(this))
97
+ }
98
+
99
+ render() {
100
+ const {
101
+ fromIndex,
102
+ toIndex,
103
+ items
104
+ } = this.state
105
+
106
+ return (
107
+ <React.Fragment>
108
+ {window.PAGINATION && fromIndex > 0 &&
109
+ <button
110
+ type="button"
111
+ onClick={this.onShowPrevious}
112
+ className="load-items-button">
113
+ Show previous
114
+ </button>
115
+ }
116
+ <VirtualScroller
117
+ id="list"
118
+ items={items}
119
+ itemComponent={Item}
120
+ preserveScrollPositionOnPrependItems
121
+ getColumnsCount={getColumnsCount}
122
+ />
123
+ {window.PAGINATION && toIndex < ITEMS.length - 1 &&
124
+ <button
125
+ type="button"
126
+ onClick={this.onShowNext}
127
+ className="load-items-button">
128
+ Show next
129
+ </button>
130
+ }
131
+ </React.Fragment>
132
+ )
133
+ }
134
+ }
135
+
136
+ const item = PropTypes.shape({
137
+ username: PropTypes.string.isRequired,
138
+ date: PropTypes.instanceOf(Date).isRequired,
139
+ text: PropTypes.string.isRequired
140
+ })
141
+
142
+ function Item(props) {
143
+ const { children: item } = props
144
+
145
+ const {
146
+ username,
147
+ date,
148
+ text
149
+ } = item
150
+
151
+ return (
152
+ <article className="list-item">
153
+ <a target="_blank" href={`https://twitter.com/${username}`}>
154
+ @{username}
155
+ </a>
156
+ <time dateTime={date.toISOString()}>
157
+ {date.getMonth() + 1}/{date.getDate()}/{date.getFullYear()}
158
+ </time>
159
+ <p>
160
+ {text}
161
+ </p>
162
+ </article>
163
+ )
164
+ }
165
+
166
+ Item.propTypes = {
167
+ children: item.isRequired
168
+ }
169
+
170
+ class Demo extends React.Component {
171
+ render() {
172
+ return (
173
+ <section className="container">
174
+ <h1>
175
+ <TwitterLogo/>
176
+ Latest Tweets on #politics
177
+ </h1>
178
+ <VirtualScrollerDemo/>
179
+ <footer>
180
+ © Twitter Inc., 2019
181
+ </footer>
182
+ </section>
183
+ )
184
+ }
185
+ }
186
+
187
+ function TwitterLogo() {
188
+ return (
189
+ <svg className="twitter-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
190
+ <path fill="#00AAEC" fillRule="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" clipRule="evenodd"/>
191
+ </svg>
192
+ )
193
+ }
194
+
195
+ const root = ReactDOM.createRoot(document.getElementById('root'))
196
+ root.render(<React.StrictMode><Demo/></React.StrictMode>)
197
+ </script>
198
+ </body>
199
+ </html>
@@ -0,0 +1,96 @@
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 (React) (Scrollable Container) (<tbody/>)</title>
10
+
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
+
16
+ <script src="./virtual-scroller-react.js"></script>
17
+
18
+ <link rel="stylesheet" href="./style.base.css"/>
19
+
20
+ <style>
21
+ #scrollable-container {
22
+ margin-top: 20vh;
23
+ margin-bottom: 20vh;
24
+ max-height: 60vh;
25
+ overflow: auto;
26
+ }
27
+ </style>
28
+
29
+ <style>
30
+ table {
31
+ width: 100%;
32
+ }
33
+
34
+ tr {
35
+ background: #fff
36
+ }
37
+
38
+ th, td {
39
+ padding-left: 1em;
40
+ padding-right: 1em;
41
+ padding-top: 0.2em;
42
+ padding-bottom: 0.2em;
43
+ }
44
+ </style>
45
+ </head>
46
+
47
+ <body>
48
+ <div id="root"></div>
49
+
50
+ <script>
51
+ // Enable debug output to console.
52
+ window.VirtualScrollerDebug = true
53
+ </script>
54
+
55
+ <script type="text/babel">
56
+ const rows = []
57
+ for (var i = 1; i <= 10000; i++) {
58
+ rows.push(i)
59
+ }
60
+
61
+ function Item({ item }) {
62
+ return (
63
+ <tr>
64
+ <td>
65
+ {item}
66
+ </td>
67
+ </tr>
68
+ )
69
+ }
70
+
71
+ function getScrollableContainer() {
72
+ return document.getElementById('scrollable-container')
73
+ }
74
+
75
+ class Demo extends React.Component {
76
+ render() {
77
+ return (
78
+ <div id="scrollable-container">
79
+ <table>
80
+ <VirtualScroller
81
+ items={rows}
82
+ itemComponent={Item}
83
+ as="tbody"
84
+ getScrollableContainer={getScrollableContainer}
85
+ />
86
+ </table>
87
+ </div>
88
+ )
89
+ }
90
+ }
91
+
92
+ const root = ReactDOM.createRoot(document.getElementById('root'))
93
+ root.render(<Demo/>)
94
+ </script>
95
+ </body>
96
+ </html>
@@ -0,0 +1,80 @@
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 (React) (<tbody/>)</title>
10
+
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
+
16
+ <script src="./virtual-scroller-react.js"></script>
17
+
18
+ <link rel="stylesheet" href="./style.base.css"/>
19
+
20
+ <style>
21
+ table {
22
+ width: 100%;
23
+ }
24
+
25
+ tr {
26
+ background: #fff
27
+ }
28
+
29
+ th, td {
30
+ padding-left: 1em;
31
+ padding-right: 1em;
32
+ padding-top: 0.2em;
33
+ padding-bottom: 0.2em;
34
+ }
35
+ </style>
36
+ </head>
37
+
38
+ <body>
39
+ <div id="root"></div>
40
+
41
+ <script>
42
+ // Enable debug output to console.
43
+ window.VirtualScrollerDebug = true
44
+ </script>
45
+
46
+ <script type="text/babel">
47
+ const rows = []
48
+ for (var i = 1; i <= 10000; i++) {
49
+ rows.push(i)
50
+ }
51
+
52
+ function Item({ item }) {
53
+ return (
54
+ <tr>
55
+ <td>
56
+ {item}
57
+ </td>
58
+ </tr>
59
+ )
60
+ }
61
+
62
+ class Demo extends React.Component {
63
+ render() {
64
+ return (
65
+ <table>
66
+ <VirtualScroller
67
+ items={rows}
68
+ itemComponent={Item}
69
+ as="tbody"
70
+ />
71
+ </table>
72
+ )
73
+ }
74
+ }
75
+
76
+ const root = ReactDOM.createRoot(document.getElementById('root'))
77
+ root.render(<Demo/>)
78
+ </script>
79
+ </body>
80
+ </html>
@@ -6,88 +6,106 @@
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)</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="./lib/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"/>
19
+ <link rel="stylesheet" href="./style.base.css"/>
20
+ <link rel="stylesheet" href="./style.list.css"/>
21
21
  </head>
22
22
 
23
23
  <body>
24
24
  <!-- http://tholman.com/github-corners/ -->
25
- <a title="Go to GitLab repo" href="https://gitlab.com/catamphetamine/virtual-scroller" class="github-corner" aria-label="View source on GitLab"><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>
25
+ <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>
26
26
 
27
27
  <div id="root"></div>
28
28
 
29
29
  <script>
30
30
  // Enable debug output to console.
31
31
  window.VirtualScrollerDebug = true
32
- // Whether "dynamically loaded list" mode is enabled.
33
- 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'))
34
34
  </script>
35
35
 
36
36
  <script type="text/babel">
37
- const BATCH_SIZE = 6
37
+ const BATCH_SIZE = 6
38
+ const COLUMNS_COUNT = 1
38
39
 
39
- class FeedMessages extends React.Component {
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
+ }
61
+
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 {
40
83
  constructor(props) {
41
84
  super(props)
42
- const { messages } = this.props
43
- if (window.dynamic) {
44
- const fromIndex = Math.floor(messages.length / 2 - BATCH_SIZE / 2)
45
- const toIndex = fromIndex + BATCH_SIZE - 1
46
- this.state = {
47
- fromIndex,
48
- toIndex,
49
- messages: messages.slice(fromIndex, toIndex + 1)
50
- }
51
- } else {
52
- this.state = {
53
- fromIndex: 0,
54
- toIndex: messages.length,
55
- messages
56
- }
57
- }
85
+
86
+ this.state = getInitialState(ITEMS)
58
87
  }
59
88
 
89
+ getState = () => this.state
90
+
60
91
  onShowPrevious = () => {
61
- const { messages } = this.props
62
- let { fromIndex } = this.state
63
- const { toIndex } = this.state
64
- fromIndex = Math.max(fromIndex - BATCH_SIZE, 0)
65
- this.setState({
66
- fromIndex,
67
- messages: messages.slice(fromIndex, toIndex + 1)
68
- })
92
+ onShowPrevious(ITEMS, this.getState, this.setState.bind(this))
69
93
  }
70
94
 
71
95
  onShowNext = () => {
72
- const { messages } = this.props
73
- const { fromIndex } = this.state
74
- let { toIndex } = this.state
75
- toIndex = Math.min(toIndex + BATCH_SIZE, messages.length - 1)
76
- this.setState({
77
- toIndex,
78
- messages: messages.slice(fromIndex, toIndex + 1)
79
- })
96
+ onShowNext(ITEMS, this.getState, this.setState.bind(this))
80
97
  }
81
98
 
82
99
  render() {
83
100
  const {
84
101
  fromIndex,
85
102
  toIndex,
86
- messages
103
+ items
87
104
  } = this.state
105
+
88
106
  return (
89
107
  <React.Fragment>
90
- {window.dynamic && fromIndex > 0 &&
108
+ {window.PAGINATION && fromIndex > 0 &&
91
109
  <button
92
110
  type="button"
93
111
  onClick={this.onShowPrevious}
@@ -96,12 +114,13 @@
96
114
  </button>
97
115
  }
98
116
  <VirtualScroller
99
- bypass
100
- id="messages"
101
- items={messages}
102
- itemComponent={Message}
103
- preserveScrollPositionOnPrependItems/>
104
- {window.dynamic && toIndex < this.props.messages.length - 1 &&
117
+ id="list"
118
+ items={items}
119
+ itemComponent={Item}
120
+ preserveScrollPositionOnPrependItems
121
+ getColumnsCount={getColumnsCount}
122
+ />
123
+ {window.PAGINATION && toIndex < ITEMS.length - 1 &&
105
124
  <button
106
125
  type="button"
107
126
  onClick={this.onShowNext}
@@ -114,25 +133,23 @@
114
133
  }
115
134
  }
116
135
 
117
- const message = PropTypes.shape({
136
+ const item = PropTypes.shape({
118
137
  username: PropTypes.string.isRequired,
119
138
  date: PropTypes.instanceOf(Date).isRequired,
120
139
  text: PropTypes.string.isRequired
121
140
  })
122
141
 
123
- FeedMessages.propTypes = {
124
- messages: PropTypes.arrayOf(message).isRequired
125
- }
142
+ function Item(props) {
143
+ const { children: item } = props
126
144
 
127
- function Message(props) {
128
- const { item: message } = props
129
145
  const {
130
146
  username,
131
147
  date,
132
148
  text
133
- } = message
149
+ } = item
150
+
134
151
  return (
135
- <article className="feed-message">
152
+ <article className="list-item">
136
153
  <a target="_blank" href={`https://twitter.com/${username}`}>
137
154
  @{username}
138
155
  </a>
@@ -146,11 +163,11 @@
146
163
  )
147
164
  }
148
165
 
149
- Message.propTypes = {
150
- item: message.isRequired
166
+ Item.propTypes = {
167
+ children: item.isRequired
151
168
  }
152
169
 
153
- class Feed extends React.Component {
170
+ class Demo extends React.Component {
154
171
  render() {
155
172
  return (
156
173
  <section className="container">
@@ -158,8 +175,7 @@
158
175
  <TwitterLogo/>
159
176
  Latest Tweets on #politics
160
177
  </h1>
161
- <FeedMessages
162
- messages={messages}/>
178
+ <VirtualScrollerDemo/>
163
179
  <footer>
164
180
  © Twitter Inc., 2019
165
181
  </footer>
@@ -176,10 +192,8 @@
176
192
  )
177
193
  }
178
194
 
179
- ReactDOM.render(
180
- <Feed/>,
181
- document.getElementById('root')
182
- )
195
+ const root = ReactDOM.createRoot(document.getElementById('root'))
196
+ root.render(<Demo/>)
183
197
  </script>
184
198
  </body>
185
199
  </html>