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