virtual-scroller 1.15.0 → 1.15.2

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 (92) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +98 -8
  3. package/bundle/index-dom-bypass.html +3 -4
  4. package/bundle/index-dom-grid.html +3 -4
  5. package/bundle/index-dom-scrollableContainer.html +3 -4
  6. package/bundle/index-dom.html +3 -4
  7. package/bundle/index-react-bypass.html +56 -62
  8. package/bundle/index-react-grid.html +55 -61
  9. package/bundle/index-react-hook.html +209 -0
  10. package/bundle/index-react-scrollableContainer.html +56 -62
  11. package/bundle/index-react-strictMode.html +55 -61
  12. package/bundle/index-react-tbody-scrollableContainer.html +13 -15
  13. package/bundle/index-react-tbody.html +10 -12
  14. package/bundle/virtual-scroller-dom.js +1 -1
  15. package/bundle/virtual-scroller-dom.js.map +1 -1
  16. package/bundle/virtual-scroller-react.js +1 -1
  17. package/bundle/virtual-scroller-react.js.map +1 -1
  18. package/bundle/virtual-scroller.js +1 -1
  19. package/bundle/virtual-scroller.js.map +1 -1
  20. package/commonjs/VirtualScroller.constructor.js +3 -1
  21. package/commonjs/VirtualScroller.constructor.js.map +1 -1
  22. package/commonjs/VirtualScroller.items.js +19 -0
  23. package/commonjs/VirtualScroller.items.js.map +1 -1
  24. package/commonjs/react/VirtualScroller.js +69 -127
  25. package/commonjs/react/VirtualScroller.js.map +1 -1
  26. package/commonjs/react/useCreateVirtualScroller.js +64 -0
  27. package/commonjs/react/useCreateVirtualScroller.js.map +1 -0
  28. package/commonjs/react/useInstanceMethods.js +2 -2
  29. package/commonjs/react/useInstanceMethods.js.map +1 -1
  30. package/commonjs/react/useMergeRefs.js +52 -0
  31. package/commonjs/react/useMergeRefs.js.map +1 -0
  32. package/commonjs/react/{useVirtualScrollerStartStop.js → useStartStopVirtualScroller.js} +1 -1
  33. package/commonjs/react/{useVirtualScrollerStartStop.js.map → useStartStopVirtualScroller.js.map} +1 -1
  34. package/commonjs/react/useStyle.js +18 -0
  35. package/commonjs/react/useStyle.js.map +1 -1
  36. package/commonjs/react/useVirtualScroller.js +142 -43
  37. package/commonjs/react/useVirtualScroller.js.map +1 -1
  38. package/commonjs/test/VirtualScroller.js +17 -1
  39. package/commonjs/test/VirtualScroller.js.map +1 -1
  40. package/modules/VirtualScroller.constructor.js +3 -1
  41. package/modules/VirtualScroller.constructor.js.map +1 -1
  42. package/modules/VirtualScroller.items.js +19 -0
  43. package/modules/VirtualScroller.items.js.map +1 -1
  44. package/modules/react/VirtualScroller.js +68 -119
  45. package/modules/react/VirtualScroller.js.map +1 -1
  46. package/modules/react/useCreateVirtualScroller.js +53 -0
  47. package/modules/react/useCreateVirtualScroller.js.map +1 -0
  48. package/modules/react/useInstanceMethods.js +2 -2
  49. package/modules/react/useInstanceMethods.js.map +1 -1
  50. package/modules/react/useMergeRefs.js +44 -0
  51. package/modules/react/useMergeRefs.js.map +1 -0
  52. package/modules/react/{useVirtualScrollerStartStop.js → useStartStopVirtualScroller.js} +1 -1
  53. package/modules/react/{useVirtualScrollerStartStop.js.map → useStartStopVirtualScroller.js.map} +1 -1
  54. package/modules/react/useStyle.js +17 -0
  55. package/modules/react/useStyle.js.map +1 -1
  56. package/modules/react/useVirtualScroller.js +136 -43
  57. package/modules/react/useVirtualScroller.js.map +1 -1
  58. package/modules/test/VirtualScroller.js +17 -1
  59. package/modules/test/VirtualScroller.js.map +1 -1
  60. package/package.json +4 -1
  61. package/react/index.cjs +2 -1
  62. package/react/index.d.ts +51 -7
  63. package/react/index.js +1 -0
  64. package/rollup.config.mjs +15 -1
  65. package/source/VirtualScroller.constructor.js +3 -0
  66. package/source/VirtualScroller.items.js +14 -0
  67. package/source/react/VirtualScroller.js +66 -127
  68. package/source/react/useCreateVirtualScroller.js +65 -0
  69. package/source/react/useInstanceMethods.js +2 -2
  70. package/source/react/useMergeRefs.js +45 -0
  71. package/source/react/useStyle.js +15 -0
  72. package/source/react/useVirtualScroller.js +155 -48
  73. package/source/test/VirtualScroller.js +11 -1
  74. package/website/index-dom-bypass.html +3 -4
  75. package/website/index-dom-grid.html +3 -4
  76. package/website/index-dom-scrollableContainer.html +3 -4
  77. package/website/index-dom.html +3 -4
  78. package/website/index-react-bypass.html +56 -62
  79. package/website/index-react-grid.html +55 -61
  80. package/website/index-react-hook.html +209 -0
  81. package/website/index-react-scrollableContainer.html +56 -62
  82. package/website/index-react-strictMode.html +55 -61
  83. package/website/index-react-tbody-scrollableContainer.html +13 -15
  84. package/website/index-react-tbody.html +10 -12
  85. package/website/index-react.html +55 -61
  86. package/website/index.html +55 -61
  87. package/commonjs/react/useForwardedRef.js +0 -50
  88. package/commonjs/react/useForwardedRef.js.map +0 -1
  89. package/modules/react/useForwardedRef.js +0 -42
  90. package/modules/react/useForwardedRef.js.map +0 -1
  91. package/source/react/useForwardedRef.js +0 -39
  92. /package/source/react/{useVirtualScrollerStartStop.js → useStartStopVirtualScroller.js} +0 -0
@@ -10,8 +10,17 @@ export default class TestVirtualScroller {
10
10
  columnsCount,
11
11
  verticalSpacing,
12
12
  items,
13
- state: initialState
13
+ getItemId,
14
+ state: initialState,
15
+ ...rest
14
16
  }) {
17
+ // Validate that no unknown properties are passed.
18
+ // This prevents the cases when a developer confuses
19
+ // `TestVirtualScroller` options for `VirtualScroller` options.
20
+ if (Object.keys(rest).length > 0) {
21
+ throw new Error(`Unknown options: ${Object.keys(rest).join(', ')}`)
22
+ }
23
+
15
24
  this.expectedStateUpdates = []
16
25
 
17
26
  const scrollableContainerElement = {
@@ -81,6 +90,7 @@ export default class TestVirtualScroller {
81
90
  scrollableContainer: scrollableContainerElement,
82
91
  engine: Engine,
83
92
  _waitForScrollingToStop: false,
93
+ getItemId,
84
94
  getColumnsCount,
85
95
  state: initialState,
86
96
  onStateChange(state) {
@@ -72,6 +72,7 @@
72
72
  const { toIndex } = getState()
73
73
  fromIndex = Math.max(fromIndex - BATCH_SIZE, 0)
74
74
  setState({
75
+ ...getState(),
75
76
  fromIndex,
76
77
  items: items.slice(fromIndex, toIndex + 1)
77
78
  })
@@ -82,6 +83,7 @@
82
83
  let { toIndex } = getState()
83
84
  toIndex = Math.min(toIndex + BATCH_SIZE, items.length - 1)
84
85
  setState({
86
+ ...getState(),
85
87
  toIndex,
86
88
  items: items.slice(fromIndex, toIndex + 1)
87
89
  })
@@ -159,10 +161,7 @@
159
161
 
160
162
  setState = (newState) => {
161
163
  const prevState = this.state
162
- this.state = {
163
- ...this.state,
164
- ...newState
165
- }
164
+ this.state = newState
166
165
  this.onStateChange(this.state, prevState)
167
166
  }
168
167
 
@@ -78,6 +78,7 @@
78
78
  const { toIndex } = getState()
79
79
  fromIndex = Math.max(fromIndex - BATCH_SIZE, 0)
80
80
  setState({
81
+ ...getState(),
81
82
  fromIndex,
82
83
  items: items.slice(fromIndex, toIndex + 1)
83
84
  })
@@ -88,6 +89,7 @@
88
89
  let { toIndex } = getState()
89
90
  toIndex = Math.min(toIndex + BATCH_SIZE, items.length - 1)
90
91
  setState({
92
+ ...getState(),
91
93
  toIndex,
92
94
  items: items.slice(fromIndex, toIndex + 1)
93
95
  })
@@ -165,10 +167,7 @@
165
167
 
166
168
  setState = (newState) => {
167
169
  const prevState = this.state
168
- this.state = {
169
- ...this.state,
170
- ...newState
171
- }
170
+ this.state = newState
172
171
  this.onStateChange(this.state, prevState)
173
172
  }
174
173
 
@@ -85,6 +85,7 @@
85
85
  const { toIndex } = getState()
86
86
  fromIndex = Math.max(fromIndex - BATCH_SIZE, 0)
87
87
  setState({
88
+ ...getState(),
88
89
  fromIndex,
89
90
  items: items.slice(fromIndex, toIndex + 1)
90
91
  })
@@ -95,6 +96,7 @@
95
96
  let { toIndex } = getState()
96
97
  toIndex = Math.min(toIndex + BATCH_SIZE, items.length - 1)
97
98
  setState({
99
+ ...getState(),
98
100
  toIndex,
99
101
  items: items.slice(fromIndex, toIndex + 1)
100
102
  })
@@ -176,10 +178,7 @@
176
178
 
177
179
  setState = (newState) => {
178
180
  const prevState = this.state
179
- this.state = {
180
- ...this.state,
181
- ...newState
182
- }
181
+ this.state = newState
183
182
  this.onStateChange(this.state, prevState)
184
183
  }
185
184
 
@@ -72,6 +72,7 @@
72
72
  const { toIndex } = getState()
73
73
  fromIndex = Math.max(fromIndex - BATCH_SIZE, 0)
74
74
  setState({
75
+ ...getState(),
75
76
  fromIndex,
76
77
  items: items.slice(fromIndex, toIndex + 1)
77
78
  })
@@ -82,6 +83,7 @@
82
83
  let { toIndex } = getState()
83
84
  toIndex = Math.min(toIndex + BATCH_SIZE, items.length - 1)
84
85
  setState({
86
+ ...getState(),
85
87
  toIndex,
86
88
  items: items.slice(fromIndex, toIndex + 1)
87
89
  })
@@ -159,10 +161,7 @@
159
161
 
160
162
  setState = (newState) => {
161
163
  const prevState = this.state
162
- this.state = {
163
- ...this.state,
164
- ...newState
165
- }
164
+ this.state = newState
166
165
  this.onStateChange(this.state, prevState)
167
166
  }
168
167
 
@@ -64,6 +64,7 @@
64
64
  const { toIndex } = getState()
65
65
  fromIndex = Math.max(fromIndex - BATCH_SIZE, 0)
66
66
  setState({
67
+ ...getState(),
67
68
  fromIndex,
68
69
  items: items.slice(fromIndex, toIndex + 1)
69
70
  })
@@ -74,64 +75,59 @@
74
75
  let { toIndex } = getState()
75
76
  toIndex = Math.min(toIndex + BATCH_SIZE, items.length - 1)
76
77
  setState({
78
+ ...getState(),
77
79
  toIndex,
78
80
  items: items.slice(fromIndex, toIndex + 1)
79
81
  })
80
82
  }
81
83
 
82
- class VirtualScrollerDemo extends React.Component {
83
- constructor(props) {
84
- super(props)
84
+ function VirtualScrollerDemo() {
85
+ const [state, setState] = React.useState(getInitialState(ITEMS))
85
86
 
86
- this.state = getInitialState(ITEMS)
87
- }
88
-
89
- getState = () => this.state
87
+ const getState = () => state
90
88
 
91
- onShowPrevious = () => {
92
- onShowPrevious(ITEMS, this.getState, this.setState.bind(this))
89
+ const onShowPrevious_ = () => {
90
+ onShowPrevious(ITEMS, getState, setState)
93
91
  }
94
92
 
95
- onShowNext = () => {
96
- onShowNext(ITEMS, this.getState, this.setState.bind(this))
93
+ const onShowNext_ = () => {
94
+ onShowNext(ITEMS, getState, setState)
97
95
  }
98
96
 
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
- bypass
118
- id="list"
119
- items={items}
120
- itemComponent={Item}
121
- preserveScrollPositionOnPrependItems
122
- getColumnsCount={getColumnsCount}
123
- />
124
- {window.PAGINATION && toIndex < ITEMS.length - 1 &&
125
- <button
126
- type="button"
127
- onClick={this.onShowNext}
128
- className="load-items-button">
129
- Show next
130
- </button>
131
- }
132
- </React.Fragment>
133
- )
134
- }
97
+ const {
98
+ fromIndex,
99
+ toIndex,
100
+ items
101
+ } = state
102
+
103
+ return (
104
+ <React.Fragment>
105
+ {window.PAGINATION && fromIndex > 0 &&
106
+ <button
107
+ type="button"
108
+ onClick={onShowPrevious_}
109
+ className="load-items-button">
110
+ Show previous
111
+ </button>
112
+ }
113
+ <VirtualScroller
114
+ bypass
115
+ id="list"
116
+ items={items}
117
+ itemComponent={Item}
118
+ preserveScrollPositionOnPrependItems
119
+ getColumnsCount={getColumnsCount}
120
+ />
121
+ {window.PAGINATION && toIndex < ITEMS.length - 1 &&
122
+ <button
123
+ type="button"
124
+ onClick={onShowNext_}
125
+ className="load-items-button">
126
+ Show next
127
+ </button>
128
+ }
129
+ </React.Fragment>
130
+ )
135
131
  }
136
132
 
137
133
  const item = PropTypes.shape({
@@ -168,21 +164,19 @@
168
164
  children: item.isRequired
169
165
  }
170
166
 
171
- class Demo extends React.Component {
172
- render() {
173
- return (
174
- <section className="container">
175
- <h1>
176
- <TwitterLogo/>
177
- Latest Tweets on #politics
178
- </h1>
179
- <VirtualScrollerDemo/>
180
- <footer>
181
- © Twitter Inc., 2019
182
- </footer>
183
- </section>
184
- )
185
- }
167
+ function Demo() {
168
+ return (
169
+ <section className="container">
170
+ <h1>
171
+ <TwitterLogo/>
172
+ Latest Tweets on #politics
173
+ </h1>
174
+ <VirtualScrollerDemo/>
175
+ <footer>
176
+ © Twitter Inc., 2019
177
+ </footer>
178
+ </section>
179
+ )
186
180
  }
187
181
 
188
182
  function TwitterLogo() {
@@ -68,6 +68,7 @@
68
68
  const { toIndex } = getState()
69
69
  fromIndex = Math.max(fromIndex - BATCH_SIZE, 0)
70
70
  setState({
71
+ ...getState(),
71
72
  fromIndex,
72
73
  items: items.slice(fromIndex, toIndex + 1)
73
74
  })
@@ -78,63 +79,58 @@
78
79
  let { toIndex } = getState()
79
80
  toIndex = Math.min(toIndex + BATCH_SIZE, items.length - 1)
80
81
  setState({
82
+ ...getState(),
81
83
  toIndex,
82
84
  items: items.slice(fromIndex, toIndex + 1)
83
85
  })
84
86
  }
85
87
 
86
- class VirtualScrollerDemo extends React.Component {
87
- constructor(props) {
88
- super(props)
88
+ function VirtualScrollerDemo() {
89
+ const [state, setState] = React.useState(getInitialState(ITEMS))
89
90
 
90
- this.state = getInitialState(ITEMS)
91
- }
92
-
93
- getState = () => this.state
91
+ const getState = () => state
94
92
 
95
- onShowPrevious = () => {
96
- onShowPrevious(ITEMS, this.getState, this.setState.bind(this))
93
+ const onShowPrevious_ = () => {
94
+ onShowPrevious(ITEMS, getState, setState)
97
95
  }
98
96
 
99
- onShowNext = () => {
100
- onShowNext(ITEMS, this.getState, this.setState.bind(this))
97
+ const onShowNext_ = () => {
98
+ onShowNext(ITEMS, getState, setState)
101
99
  }
102
100
 
103
- render() {
104
- const {
105
- fromIndex,
106
- toIndex,
107
- items
108
- } = this.state
109
-
110
- return (
111
- <React.Fragment>
112
- {window.PAGINATION && fromIndex > 0 &&
113
- <button
114
- type="button"
115
- onClick={this.onShowPrevious}
116
- className="load-items-button">
117
- Show previous
118
- </button>
119
- }
120
- <VirtualScroller
121
- id="list"
122
- items={items}
123
- itemComponent={Item}
124
- preserveScrollPositionOnPrependItems
125
- getColumnsCount={getColumnsCount}
126
- />
127
- {window.PAGINATION && toIndex < ITEMS.length - 1 &&
128
- <button
129
- type="button"
130
- onClick={this.onShowNext}
131
- className="load-items-button">
132
- Show next
133
- </button>
134
- }
135
- </React.Fragment>
136
- )
137
- }
101
+ const {
102
+ fromIndex,
103
+ toIndex,
104
+ items
105
+ } = state
106
+
107
+ return (
108
+ <React.Fragment>
109
+ {window.PAGINATION && fromIndex > 0 &&
110
+ <button
111
+ type="button"
112
+ onClick={onShowPrevious_}
113
+ className="load-items-button">
114
+ Show previous
115
+ </button>
116
+ }
117
+ <VirtualScroller
118
+ id="list"
119
+ items={items}
120
+ itemComponent={Item}
121
+ preserveScrollPositionOnPrependItems
122
+ getColumnsCount={getColumnsCount}
123
+ />
124
+ {window.PAGINATION && toIndex < ITEMS.length - 1 &&
125
+ <button
126
+ type="button"
127
+ onClick={onShowNext_}
128
+ className="load-items-button">
129
+ Show next
130
+ </button>
131
+ }
132
+ </React.Fragment>
133
+ )
138
134
  }
139
135
 
140
136
  const item = PropTypes.shape({
@@ -171,21 +167,19 @@
171
167
  children: item.isRequired
172
168
  }
173
169
 
174
- class Demo extends React.Component {
175
- render() {
176
- return (
177
- <section className="container">
178
- <h1>
179
- <TwitterLogo/>
180
- Latest Tweets on #politics
181
- </h1>
182
- <VirtualScrollerDemo/>
183
- <footer>
184
- © Twitter Inc., 2019
185
- </footer>
186
- </section>
187
- )
188
- }
170
+ function Demo() {
171
+ return (
172
+ <section className="container">
173
+ <h1>
174
+ <TwitterLogo/>
175
+ Latest Tweets on #politics
176
+ </h1>
177
+ <VirtualScrollerDemo/>
178
+ <footer>
179
+ © Twitter Inc., 2019
180
+ </footer>
181
+ </section>
182
+ )
189
183
  }
190
184
 
191
185
  function TwitterLogo() {
@@ -0,0 +1,209 @@
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) (hook)</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
+ ...getState(),
68
+ fromIndex,
69
+ items: items.slice(fromIndex, toIndex + 1)
70
+ })
71
+ }
72
+
73
+ function onShowNext(items, getState, setState) {
74
+ const { fromIndex } = getState()
75
+ let { toIndex } = getState()
76
+ toIndex = Math.min(toIndex + BATCH_SIZE, items.length - 1)
77
+ setState({
78
+ ...getState(),
79
+ toIndex,
80
+ items: items.slice(fromIndex, toIndex + 1)
81
+ })
82
+ }
83
+
84
+ function VirtualScrollerDemo() {
85
+ const [state, setState] = React.useState(getInitialState(ITEMS))
86
+
87
+ const getState = () => state
88
+
89
+ const onShowPrevious_ = () => {
90
+ onShowPrevious(ITEMS, getState, setState)
91
+ }
92
+
93
+ const onShowNext_ = () => {
94
+ onShowNext(ITEMS, getState, setState)
95
+ }
96
+
97
+ const {
98
+ fromIndex,
99
+ toIndex,
100
+ items
101
+ } = state
102
+
103
+ const {
104
+ state: {
105
+ items: itemsToRender,
106
+ firstShownItemIndex,
107
+ lastShownItemIndex
108
+ },
109
+ style,
110
+ className,
111
+ itemsContainerRef
112
+ } = VirtualScroller.useVirtualScroller({
113
+ items,
114
+ preserveScrollPositionOnPrependItems: true,
115
+ getColumnsCount
116
+ })
117
+
118
+ return (
119
+ <React.Fragment>
120
+ {window.PAGINATION && fromIndex > 0 &&
121
+ <button
122
+ type="button"
123
+ onClick={onShowPrevious_}
124
+ className="load-items-button">
125
+ Show previous
126
+ </button>
127
+ }
128
+ <div id="list" ref={itemsContainerRef} style={style} className={className}>
129
+ {itemsToRender.map((item, i) => {
130
+ if (i >= firstShownItemIndex && i <= lastShownItemIndex) {
131
+ return (
132
+ <Item key={i} item={item}/>
133
+ )
134
+ }
135
+ return null
136
+ })}
137
+ </div>
138
+ {window.PAGINATION && toIndex < ITEMS.length - 1 &&
139
+ <button
140
+ type="button"
141
+ onClick={onShowNext_}
142
+ className="load-items-button">
143
+ Show next
144
+ </button>
145
+ }
146
+ </React.Fragment>
147
+ )
148
+ }
149
+
150
+ const item = PropTypes.shape({
151
+ username: PropTypes.string.isRequired,
152
+ date: PropTypes.instanceOf(Date).isRequired,
153
+ text: PropTypes.string.isRequired
154
+ })
155
+
156
+ function Item({ item }) {
157
+ const {
158
+ username,
159
+ date,
160
+ text
161
+ } = item
162
+
163
+ return (
164
+ <article className="list-item">
165
+ <a target="_blank" href={`https://twitter.com/${username}`}>
166
+ @{username}
167
+ </a>
168
+ <time dateTime={date.toISOString()}>
169
+ {date.getMonth() + 1}/{date.getDate()}/{date.getFullYear()}
170
+ </time>
171
+ <p>
172
+ {text}
173
+ </p>
174
+ </article>
175
+ )
176
+ }
177
+
178
+ Item.propTypes = {
179
+ children: item.isRequired
180
+ }
181
+
182
+ function Demo() {
183
+ return (
184
+ <section className="container">
185
+ <h1>
186
+ <TwitterLogo/>
187
+ Latest Tweets on #politics
188
+ </h1>
189
+ <VirtualScrollerDemo/>
190
+ <footer>
191
+ © Twitter Inc., 2019
192
+ </footer>
193
+ </section>
194
+ )
195
+ }
196
+
197
+ function TwitterLogo() {
198
+ return (
199
+ <svg className="twitter-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
200
+ <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"/>
201
+ </svg>
202
+ )
203
+ }
204
+
205
+ const root = ReactDOM.createRoot(document.getElementById('root'))
206
+ root.render(<Demo/>)
207
+ </script>
208
+ </body>
209
+ </html>