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.
- package/CHANGELOG.md +23 -0
- package/README.md +309 -250
- package/bundle/index-dom-bypass.html +198 -0
- package/bundle/index-dom-grid.html +204 -0
- package/bundle/index-dom-scrollableContainer.html +215 -0
- package/bundle/index-dom-tbody-scrollableContainer.html +81 -0
- package/bundle/index-dom-tbody.html +65 -0
- package/bundle/index-dom.html +115 -84
- package/bundle/{index-bypass.html → index-react-bypass.html} +83 -78
- package/bundle/{index-grid.html → index-react-grid.html} +78 -91
- package/bundle/{index-scrollableContainer.html → index-react-scrollableContainer.html} +96 -91
- package/bundle/index-react-strictMode.html +199 -0
- package/bundle/index-react-tbody-scrollableContainer.html +96 -0
- package/bundle/index-react-tbody.html +80 -0
- package/bundle/{messages.js → items.js} +1 -1
- package/bundle/lib/babel.min.js +25 -0
- package/bundle/lib/prop-types.min.js +1 -0
- package/bundle/lib/react-dom.development.js +29924 -0
- package/bundle/lib/react-dom.production.min.js +267 -0
- package/bundle/lib/react.development.js +3343 -0
- package/bundle/lib/react.production.min.js +31 -0
- package/bundle/style.base.css +33 -0
- package/{website/style.css → bundle/style.list.css} +10 -43
- package/bundle/style.list.grid.css +23 -0
- package/bundle/virtual-scroller-dom.js +1 -1
- package/bundle/virtual-scroller-dom.js.map +1 -1
- package/bundle/virtual-scroller-react.js +1 -1
- package/bundle/virtual-scroller-react.js.map +1 -1
- package/bundle/virtual-scroller.js +1 -1
- package/bundle/virtual-scroller.js.map +1 -1
- package/commonjs/BeforeResize.js +1 -2
- package/commonjs/BeforeResize.js.map +1 -1
- package/commonjs/DOM/VirtualScroller.js +7 -13
- package/commonjs/DOM/VirtualScroller.js.map +1 -1
- package/commonjs/DOM/tbody.js +6 -6
- package/commonjs/DOM/tbody.js.map +1 -1
- package/commonjs/ItemHeights.js +10 -13
- package/commonjs/ItemHeights.js.map +1 -1
- package/commonjs/Layout.defaults.js +21 -0
- package/commonjs/Layout.defaults.js.map +1 -0
- package/commonjs/Layout.js +75 -64
- package/commonjs/Layout.js.map +1 -1
- package/commonjs/Layout.test.js +8 -4
- package/commonjs/Layout.test.js.map +1 -1
- package/commonjs/VirtualScroller.constructor.js +38 -4
- package/commonjs/VirtualScroller.constructor.js.map +1 -1
- package/commonjs/VirtualScroller.items.js +50 -4
- package/commonjs/VirtualScroller.items.js.map +1 -1
- package/commonjs/VirtualScroller.js +23 -14
- package/commonjs/VirtualScroller.js.map +1 -1
- package/commonjs/VirtualScroller.layout.js +40 -29
- package/commonjs/VirtualScroller.layout.js.map +1 -1
- package/commonjs/VirtualScroller.onContainerResize.js +1 -2
- package/commonjs/VirtualScroller.onContainerResize.js.map +1 -1
- package/commonjs/VirtualScroller.state.js +10 -9
- package/commonjs/VirtualScroller.state.js.map +1 -1
- package/commonjs/VirtualScroller.verticalSpacing.js +39 -6
- package/commonjs/VirtualScroller.verticalSpacing.js.map +1 -1
- package/commonjs/react/VirtualScroller.js +85 -34
- package/commonjs/react/VirtualScroller.js.map +1 -1
- package/commonjs/react/useClassName.js +2 -2
- package/commonjs/react/useClassName.js.map +1 -1
- package/commonjs/react/useForwardedRef.js +50 -0
- package/commonjs/react/useForwardedRef.js.map +1 -0
- package/commonjs/react/useInstanceMethods.js +4 -4
- package/commonjs/react/useInstanceMethods.js.map +1 -1
- package/commonjs/react/useItemKeys.js +28 -5
- package/commonjs/react/useItemKeys.js.map +1 -1
- package/commonjs/react/useOnItemHeightDidChange.js +28 -12
- package/commonjs/react/useOnItemHeightDidChange.js.map +1 -1
- package/commonjs/react/useSetItemState.js +31 -12
- package/commonjs/react/useSetItemState.js.map +1 -1
- package/commonjs/react/useState.js +9 -9
- package/commonjs/react/useState.js.map +1 -1
- package/commonjs/react/{useStateNoStaleBug.js → useStateWithRepeatableRead.js} +3 -3
- package/commonjs/react/useStateWithRepeatableRead.js.map +1 -0
- package/commonjs/react/useStyle.js +10 -4
- package/commonjs/react/useStyle.js.map +1 -1
- package/commonjs/react/useValidateTableBodyItemsContainer.js +24 -0
- package/commonjs/react/useValidateTableBodyItemsContainer.js.map +1 -0
- package/commonjs/react/useVirtualScroller.js +4 -3
- package/commonjs/react/useVirtualScroller.js.map +1 -1
- package/commonjs/test/ItemsContainer.js +10 -10
- package/commonjs/test/ItemsContainer.js.map +1 -1
- package/commonjs/test/VirtualScroller.js +25 -10
- package/commonjs/test/VirtualScroller.js.map +1 -1
- package/dom/index.d.ts +6 -5
- package/index.d.ts +19 -8
- package/modules/BeforeResize.js +1 -2
- package/modules/BeforeResize.js.map +1 -1
- package/modules/DOM/VirtualScroller.js +7 -13
- package/modules/DOM/VirtualScroller.js.map +1 -1
- package/modules/DOM/tbody.js +4 -4
- package/modules/DOM/tbody.js.map +1 -1
- package/modules/ItemHeights.js +11 -14
- package/modules/ItemHeights.js.map +1 -1
- package/modules/Layout.defaults.js +11 -0
- package/modules/Layout.defaults.js.map +1 -0
- package/modules/Layout.js +74 -64
- package/modules/Layout.js.map +1 -1
- package/modules/Layout.test.js +8 -4
- package/modules/Layout.test.js.map +1 -1
- package/modules/VirtualScroller.constructor.js +37 -4
- package/modules/VirtualScroller.constructor.js.map +1 -1
- package/modules/VirtualScroller.items.js +51 -5
- package/modules/VirtualScroller.items.js.map +1 -1
- package/modules/VirtualScroller.js +23 -14
- package/modules/VirtualScroller.js.map +1 -1
- package/modules/VirtualScroller.layout.js +40 -29
- package/modules/VirtualScroller.layout.js.map +1 -1
- package/modules/VirtualScroller.onContainerResize.js +1 -2
- package/modules/VirtualScroller.onContainerResize.js.map +1 -1
- package/modules/VirtualScroller.state.js +10 -9
- package/modules/VirtualScroller.state.js.map +1 -1
- package/modules/VirtualScroller.verticalSpacing.js +38 -6
- package/modules/VirtualScroller.verticalSpacing.js.map +1 -1
- package/modules/react/VirtualScroller.js +84 -35
- package/modules/react/VirtualScroller.js.map +1 -1
- package/modules/react/useClassName.js +3 -3
- package/modules/react/useClassName.js.map +1 -1
- package/modules/react/useForwardedRef.js +42 -0
- package/modules/react/useForwardedRef.js.map +1 -0
- package/modules/react/useInstanceMethods.js +4 -4
- package/modules/react/useInstanceMethods.js.map +1 -1
- package/modules/react/useItemKeys.js +28 -5
- package/modules/react/useItemKeys.js.map +1 -1
- package/modules/react/useOnItemHeightDidChange.js +28 -12
- package/modules/react/useOnItemHeightDidChange.js.map +1 -1
- package/modules/react/useSetItemState.js +31 -12
- package/modules/react/useSetItemState.js.map +1 -1
- package/modules/react/useState.js +9 -9
- package/modules/react/useState.js.map +1 -1
- package/modules/react/{useStateNoStaleBug.js → useStateWithRepeatableRead.js} +2 -2
- package/modules/react/useStateWithRepeatableRead.js.map +1 -0
- package/modules/react/useStyle.js +10 -4
- package/modules/react/useStyle.js.map +1 -1
- package/modules/react/useValidateTableBodyItemsContainer.js +16 -0
- package/modules/react/useValidateTableBodyItemsContainer.js.map +1 -0
- package/modules/react/useVirtualScroller.js +4 -3
- package/modules/react/useVirtualScroller.js.map +1 -1
- package/modules/test/ItemsContainer.js +10 -10
- package/modules/test/ItemsContainer.js.map +1 -1
- package/modules/test/VirtualScroller.js +25 -10
- package/modules/test/VirtualScroller.js.map +1 -1
- package/package.json +1 -1
- package/react/as.d.ts +42 -0
- package/react/index.d.ts +204 -63
- package/source/BeforeResize.js +1 -2
- package/source/DOM/VirtualScroller.js +7 -13
- package/source/DOM/tbody.js +5 -5
- package/source/ItemHeights.js +11 -12
- package/source/Layout.defaults.js +8 -0
- package/source/Layout.js +66 -53
- package/source/Layout.test.js +7 -2
- package/source/VirtualScroller.constructor.js +27 -4
- package/source/VirtualScroller.items.js +47 -2
- package/source/VirtualScroller.js +23 -14
- package/source/VirtualScroller.layout.js +41 -28
- package/source/VirtualScroller.onContainerResize.js +1 -2
- package/source/VirtualScroller.state.js +10 -11
- package/source/VirtualScroller.verticalSpacing.js +32 -6
- package/source/react/VirtualScroller.js +96 -33
- package/source/react/useClassName.js +3 -3
- package/source/react/useForwardedRef.js +39 -0
- package/source/react/useInstanceMethods.js +12 -4
- package/source/react/useItemKeys.js +22 -4
- package/source/react/useOnItemHeightDidChange.js +29 -10
- package/source/react/useSetItemState.js +32 -10
- package/source/react/useState.js +6 -6
- package/source/react/{useStateNoStaleBug.js → useStateWithRepeatableRead.js} +1 -1
- package/source/react/useStyle.js +3 -2
- package/source/react/useValidateTableBodyItemsContainer.js +16 -0
- package/source/react/useVirtualScroller.js +4 -3
- package/source/test/ItemsContainer.js +10 -10
- package/source/test/VirtualScroller.js +16 -10
- package/website/index-dom-bypass.html +198 -0
- package/website/index-dom-grid.html +204 -0
- package/website/index-dom-scrollableContainer.html +215 -0
- package/website/index-dom-tbody-scrollableContainer.html +81 -0
- package/website/index-dom-tbody.html +65 -0
- package/website/index-dom.html +117 -84
- package/website/index-react-bypass.html +200 -0
- package/website/{index-grid.html → index-react-grid.html} +79 -92
- package/website/index-react-scrollableContainer.html +213 -0
- package/website/index-react-strictMode.html +199 -0
- package/website/index-react-tbody-scrollableContainer.html +96 -0
- package/website/index-react-tbody.html +80 -0
- package/website/{index-bypass.html → index-react.html} +84 -70
- package/website/index.html +84 -69
- package/website/{messages.js → items.js} +1 -1
- package/website/lib/babel.min.js +25 -0
- package/website/lib/prop-types.min.js +1 -0
- package/website/lib/react-dom.development.js +29924 -0
- package/website/lib/react-dom.production.min.js +267 -0
- package/website/lib/react.development.js +3343 -0
- package/website/lib/react.production.min.js +31 -0
- package/website/style.base.css +33 -0
- package/website/style.list.css +92 -0
- package/website/style.list.grid.css +23 -0
- package/bundle/index-tbody-scrollableContainer.html +0 -70
- package/bundle/index-tbody.html +0 -57
- package/bundle/on-scroll-to-dom.js +0 -2
- package/bundle/on-scroll-to-dom.js.map +0 -1
- package/bundle/on-scroll-to-react.js +0 -2
- package/bundle/on-scroll-to-react.js.map +0 -1
- package/bundle/on-scroll-to.js +0 -2
- package/bundle/on-scroll-to.js.map +0 -1
- package/commonjs/react/useStateNoStaleBug.js.map +0 -1
- package/modules/react/useStateNoStaleBug.js.map +0 -1
- package/website/index-scrollableContainer.html +0 -208
- package/website/index-tbody-scrollableContainer.html +0 -70
- package/website/index-tbody.html +0 -57
- package/website/lib/on-scroll-to-dom.js +0 -2
- package/website/lib/on-scroll-to-dom.js.map +0 -1
- package/website/lib/on-scroll-to-react.js +0 -2
- 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>
|
package/bundle/index-dom.html
CHANGED
|
@@ -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>
|
|
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="./
|
|
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="
|
|
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
|
-
//
|
|
43
|
-
window.
|
|
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
|
|
44
|
+
<script>
|
|
47
45
|
const BATCH_SIZE = 6
|
|
46
|
+
const COLUMNS_COUNT = 1
|
|
48
47
|
|
|
49
|
-
function
|
|
50
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
76
|
+
items: items.slice(fromIndex, toIndex + 1)
|
|
110
77
|
})
|
|
111
78
|
}
|
|
112
79
|
|
|
113
|
-
|
|
114
|
-
const { fromIndex } =
|
|
115
|
-
let { toIndex } =
|
|
116
|
-
toIndex = Math.min(toIndex + BATCH_SIZE,
|
|
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
|
-
|
|
86
|
+
items: items.slice(fromIndex, toIndex + 1)
|
|
120
87
|
})
|
|
121
88
|
}
|
|
122
89
|
|
|
123
|
-
function
|
|
124
|
-
const {
|
|
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.
|
|
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 } =
|
|
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.
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
)
|
|
151
|
+
class VirtualScrollerDemo {
|
|
152
|
+
constructor() {
|
|
153
|
+
this.state = getInitialState(ITEMS)
|
|
154
|
+
}
|
|
160
155
|
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
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
|
|
9
|
+
<title>VirtualScroller (React) (bypass)</title>
|
|
10
10
|
|
|
11
|
-
<script
|
|
12
|
-
<script
|
|
13
|
-
<script
|
|
14
|
-
<script src="
|
|
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="./
|
|
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
|
-
//
|
|
43
|
-
window.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
+
items
|
|
97
104
|
} = this.state
|
|
105
|
+
|
|
98
106
|
return (
|
|
99
107
|
<React.Fragment>
|
|
100
|
-
{window.
|
|
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="
|
|
111
|
-
items={
|
|
112
|
-
itemComponent={
|
|
113
|
-
preserveScrollPositionOnPrependItems
|
|
114
|
-
|
|
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
|
|
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
|
-
|
|
134
|
-
|
|
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
|
-
} =
|
|
150
|
+
} = item
|
|
151
|
+
|
|
144
152
|
return (
|
|
145
|
-
<article className="
|
|
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
|
-
|
|
160
|
-
children:
|
|
167
|
+
Item.propTypes = {
|
|
168
|
+
children: item.isRequired
|
|
161
169
|
}
|
|
162
170
|
|
|
163
|
-
class
|
|
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
|
-
<
|
|
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.
|
|
190
|
-
|
|
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>
|