virtual-scroller 1.8.1 → 1.9.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 +57 -0
- package/README.md +337 -160
- 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 +23 -27
- package/commonjs/BeforeResize.js.map +1 -1
- package/commonjs/DOM/Engine.js +7 -7
- package/commonjs/DOM/Engine.js.map +1 -1
- package/commonjs/DOM/ItemsContainer.js +1 -1
- package/commonjs/DOM/ItemsContainer.js.map +1 -1
- package/commonjs/DOM/ListTopOffsetWatcher.js +15 -9
- package/commonjs/DOM/ListTopOffsetWatcher.js.map +1 -1
- package/commonjs/DOM/ScrollableContainer.js +28 -28
- package/commonjs/DOM/ScrollableContainer.js.map +1 -1
- package/commonjs/DOM/VirtualScroller.js +20 -17
- package/commonjs/DOM/VirtualScroller.js.map +1 -1
- package/commonjs/DOM/tbody.js +16 -10
- package/commonjs/DOM/tbody.js.map +1 -1
- package/commonjs/ItemHeights.js +13 -7
- package/commonjs/ItemHeights.js.map +1 -1
- package/commonjs/Layout.js +15 -13
- package/commonjs/Layout.js.map +1 -1
- package/commonjs/Layout.test.js +8 -3
- package/commonjs/Layout.test.js.map +1 -1
- package/commonjs/{ListHeightChangeWatcher.js → ListHeightMeasurement.js} +26 -28
- package/commonjs/ListHeightMeasurement.js.map +1 -0
- package/commonjs/Resize.js +38 -28
- package/commonjs/Resize.js.map +1 -1
- package/commonjs/Scroll.js +28 -44
- package/commonjs/Scroll.js.map +1 -1
- package/commonjs/VirtualScroller.columns.js +43 -0
- package/commonjs/VirtualScroller.columns.js.map +1 -0
- package/commonjs/VirtualScroller.constructor.js +408 -0
- package/commonjs/VirtualScroller.constructor.js.map +1 -0
- package/commonjs/VirtualScroller.items.js +305 -0
- package/commonjs/VirtualScroller.items.js.map +1 -0
- package/commonjs/VirtualScroller.js +132 -1872
- package/commonjs/VirtualScroller.js.map +1 -1
- package/commonjs/VirtualScroller.layout.js +562 -0
- package/commonjs/VirtualScroller.layout.js.map +1 -0
- package/commonjs/VirtualScroller.onRender.js +357 -0
- package/commonjs/VirtualScroller.onRender.js.map +1 -0
- package/commonjs/VirtualScroller.resize.js +186 -0
- package/commonjs/VirtualScroller.resize.js.map +1 -0
- package/commonjs/VirtualScroller.state.js +301 -0
- package/commonjs/VirtualScroller.state.js.map +1 -0
- package/commonjs/VirtualScroller.verticalSpacing.js +65 -0
- package/commonjs/VirtualScroller.verticalSpacing.js.map +1 -0
- package/commonjs/getItemCoordinates.js.map +1 -1
- package/commonjs/getItemsDiff.js.map +1 -1
- package/commonjs/getVerticalSpacing.js.map +1 -1
- package/commonjs/package.json +5 -0
- package/commonjs/react/VirtualScroller.js +180 -620
- package/commonjs/react/VirtualScroller.js.map +1 -1
- package/commonjs/react/useClassName.js +26 -0
- package/commonjs/react/useClassName.js.map +1 -0
- package/commonjs/react/useHandleItemsChange.js +116 -0
- package/commonjs/react/useHandleItemsChange.js.map +1 -0
- package/commonjs/react/useInstanceMethods.js +37 -0
- package/commonjs/react/useInstanceMethods.js.map +1 -0
- package/commonjs/react/useItemKeys.js +60 -0
- package/commonjs/react/useItemKeys.js.map +1 -0
- package/commonjs/react/useOnItemHeightChange.js +32 -0
- package/commonjs/react/useOnItemHeightChange.js.map +1 -0
- package/commonjs/react/useOnItemStateChange.js +32 -0
- package/commonjs/react/useOnItemStateChange.js.map +1 -0
- package/commonjs/react/useState.js +140 -0
- package/commonjs/react/useState.js.map +1 -0
- package/commonjs/react/useStyle.js +29 -0
- package/commonjs/react/useStyle.js.map +1 -0
- package/commonjs/react/useVirtualScroller.js +62 -0
- package/commonjs/react/useVirtualScroller.js.map +1 -0
- package/commonjs/react/useVirtualScrollerStartStop.js +20 -0
- package/commonjs/react/useVirtualScrollerStartStop.js.map +1 -0
- package/commonjs/test/Engine.js +23 -0
- package/commonjs/test/Engine.js.map +1 -0
- package/commonjs/test/ItemsContainer.js +127 -0
- package/commonjs/test/ItemsContainer.js.map +1 -0
- package/commonjs/test/ScrollableContainer.js +130 -0
- package/commonjs/test/ScrollableContainer.js.map +1 -0
- package/commonjs/test/VirtualScroller.js +281 -0
- package/commonjs/test/VirtualScroller.js.map +1 -0
- package/commonjs/utility/debounce.js +2 -2
- package/commonjs/utility/debounce.js.map +1 -1
- package/commonjs/utility/debug.js.map +1 -1
- package/commonjs/utility/getStateSnapshot.js +2 -2
- package/commonjs/utility/getStateSnapshot.js.map +1 -1
- package/commonjs/utility/px.js.map +1 -1
- package/commonjs/utility/px.test.js +1 -1
- package/commonjs/utility/px.test.js.map +1 -1
- package/commonjs/utility/shallowEqual.js +1 -1
- package/commonjs/utility/shallowEqual.js.map +1 -1
- package/commonjs/utility/throttle.js.map +1 -1
- package/dom/index.cjs +4 -0
- package/dom/index.cjs.js +9 -0
- package/dom/index.d.ts +6 -4
- package/dom/index.js +1 -1
- package/dom/package.json +10 -4
- package/index.cjs +4 -0
- package/index.cjs.js +9 -0
- package/index.d.ts +30 -15
- package/index.js +1 -1
- package/modules/BeforeResize.js +22 -27
- package/modules/BeforeResize.js.map +1 -1
- package/modules/DOM/Engine.js +6 -6
- package/modules/DOM/Engine.js.map +1 -1
- package/modules/DOM/ItemsContainer.js +1 -1
- package/modules/DOM/ItemsContainer.js.map +1 -1
- package/modules/DOM/ListTopOffsetWatcher.js +15 -9
- package/modules/DOM/ListTopOffsetWatcher.js.map +1 -1
- package/modules/DOM/ScrollableContainer.js +28 -28
- package/modules/DOM/ScrollableContainer.js.map +1 -1
- package/modules/DOM/VirtualScroller.js +19 -16
- package/modules/DOM/VirtualScroller.js.map +1 -1
- package/modules/DOM/tbody.js +11 -9
- package/modules/DOM/tbody.js.map +1 -1
- package/modules/ItemHeights.js +12 -6
- package/modules/ItemHeights.js.map +1 -1
- package/modules/Layout.js +14 -12
- package/modules/Layout.js.map +1 -1
- package/modules/Layout.test.js +8 -3
- package/modules/Layout.test.js.map +1 -1
- package/modules/{ListHeightChangeWatcher.js → ListHeightMeasurement.js} +25 -27
- package/modules/ListHeightMeasurement.js.map +1 -0
- package/modules/Resize.js +38 -28
- package/modules/Resize.js.map +1 -1
- package/modules/Scroll.js +28 -44
- package/modules/Scroll.js.map +1 -1
- package/modules/VirtualScroller.columns.js +36 -0
- package/modules/VirtualScroller.columns.js.map +1 -0
- package/modules/VirtualScroller.constructor.js +371 -0
- package/modules/VirtualScroller.constructor.js.map +1 -0
- package/modules/VirtualScroller.items.js +288 -0
- package/modules/VirtualScroller.items.js.map +1 -0
- package/modules/VirtualScroller.js +132 -1860
- package/modules/VirtualScroller.js.map +1 -1
- package/modules/VirtualScroller.layout.js +549 -0
- package/modules/VirtualScroller.layout.js.map +1 -0
- package/modules/VirtualScroller.onRender.js +337 -0
- package/modules/VirtualScroller.onRender.js.map +1 -0
- package/modules/VirtualScroller.resize.js +176 -0
- package/modules/VirtualScroller.resize.js.map +1 -0
- package/modules/VirtualScroller.state.js +283 -0
- package/modules/VirtualScroller.state.js.map +1 -0
- package/modules/VirtualScroller.verticalSpacing.js +54 -0
- package/modules/VirtualScroller.verticalSpacing.js.map +1 -0
- package/modules/getItemCoordinates.js.map +1 -1
- package/modules/getItemsDiff.js.map +1 -1
- package/modules/getVerticalSpacing.js.map +1 -1
- package/modules/react/VirtualScroller.js +176 -625
- package/modules/react/VirtualScroller.js.map +1 -1
- package/modules/react/useClassName.js +18 -0
- package/modules/react/useClassName.js.map +1 -0
- package/modules/react/useHandleItemsChange.js +108 -0
- package/modules/react/useHandleItemsChange.js.map +1 -0
- package/modules/react/useInstanceMethods.js +28 -0
- package/modules/react/useInstanceMethods.js.map +1 -0
- package/modules/react/useItemKeys.js +52 -0
- package/modules/react/useItemKeys.js.map +1 -0
- package/modules/react/useOnItemHeightChange.js +24 -0
- package/modules/react/useOnItemHeightChange.js.map +1 -0
- package/modules/react/useOnItemStateChange.js +24 -0
- package/modules/react/useOnItemStateChange.js.map +1 -0
- package/modules/react/useState.js +132 -0
- package/modules/react/useState.js.map +1 -0
- package/modules/react/useStyle.js +19 -0
- package/modules/react/useStyle.js.map +1 -0
- package/modules/react/useVirtualScroller.js +51 -0
- package/modules/react/useVirtualScroller.js.map +1 -0
- package/modules/react/useVirtualScrollerStartStop.js +12 -0
- package/modules/react/useVirtualScrollerStartStop.js.map +1 -0
- package/modules/test/Engine.js +11 -0
- package/modules/test/Engine.js.map +1 -0
- package/modules/test/ItemsContainer.js +120 -0
- package/modules/test/ItemsContainer.js.map +1 -0
- package/modules/test/ScrollableContainer.js +123 -0
- package/modules/test/ScrollableContainer.js.map +1 -0
- package/modules/test/VirtualScroller.js +270 -0
- package/modules/test/VirtualScroller.js.map +1 -0
- package/modules/utility/debounce.js +2 -2
- package/modules/utility/debounce.js.map +1 -1
- package/modules/utility/debug.js.map +1 -1
- package/modules/utility/getStateSnapshot.js +2 -2
- package/modules/utility/getStateSnapshot.js.map +1 -1
- package/modules/utility/px.js.map +1 -1
- package/modules/utility/px.test.js +1 -1
- package/modules/utility/px.test.js.map +1 -1
- package/modules/utility/shallowEqual.js +1 -1
- package/modules/utility/shallowEqual.js.map +1 -1
- package/modules/utility/throttle.js.map +1 -1
- package/package.json +46 -23
- package/react/index.cjs +4 -0
- package/react/index.cjs.js +9 -0
- package/react/index.d.ts +10 -9
- package/react/index.js +1 -1
- package/react/package.json +10 -4
- package/rollup.config.mjs +62 -0
- package/runnable/create-commonjs-package-json.js +11 -0
- package/source/BeforeResize.js +16 -21
- package/source/DOM/Engine.js +8 -10
- package/source/DOM/ListTopOffsetWatcher.js +13 -8
- package/source/DOM/ScrollableContainer.js +23 -21
- package/source/DOM/VirtualScroller.js +27 -11
- package/source/DOM/tbody.js +30 -21
- package/source/ItemHeights.js +9 -4
- package/source/Layout.js +12 -9
- package/source/Layout.test.js +8 -3
- package/source/{ListHeightChangeWatcher.js → ListHeightMeasurement.js} +21 -20
- package/source/Resize.js +41 -25
- package/source/Scroll.js +27 -35
- package/source/VirtualScroller.columns.js +26 -0
- package/source/VirtualScroller.constructor.js +336 -0
- package/source/VirtualScroller.items.js +302 -0
- package/source/VirtualScroller.js +144 -1875
- package/source/VirtualScroller.layout.js +539 -0
- package/source/VirtualScroller.onRender.js +345 -0
- package/source/VirtualScroller.resize.js +189 -0
- package/source/VirtualScroller.state.js +284 -0
- package/source/VirtualScroller.verticalSpacing.js +51 -0
- package/source/react/VirtualScroller.js +243 -587
- package/source/react/useClassName.js +14 -0
- package/source/react/useHandleItemsChange.js +115 -0
- package/source/react/useInstanceMethods.js +25 -0
- package/source/react/useItemKeys.js +59 -0
- package/source/react/useOnItemHeightChange.js +28 -0
- package/source/react/useOnItemStateChange.js +28 -0
- package/source/react/useState.js +114 -0
- package/source/react/useStyle.js +20 -0
- package/source/react/useVirtualScroller.js +59 -0
- package/source/react/useVirtualScrollerStartStop.js +12 -0
- package/source/test/Engine.js +11 -0
- package/source/test/ItemsContainer.js +87 -0
- package/source/test/ScrollableContainer.js +88 -0
- package/source/test/VirtualScroller.js +232 -0
- package/source/utility/debounce.js +2 -2
- package/source/utility/px.test.js +1 -1
- package/babel.config.js +0 -25
- package/babel.js +0 -5
- package/commonjs/ListHeightChangeWatcher.js.map +0 -1
- package/dom/index.commonjs.js +0 -4
- package/index.commonjs.js +0 -4
- package/modules/ListHeightChangeWatcher.js.map +0 -1
- package/react/index.commonjs.js +0 -4
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import log, { warn, isDebug, reportError } from './utility/debug.js'
|
|
2
|
+
import { cleanUpBeforeResizeState } from './BeforeResize.js'
|
|
3
|
+
import getStateSnapshot from './utility/getStateSnapshot.js'
|
|
4
|
+
|
|
5
|
+
// There're three main places where state is updated:
|
|
6
|
+
//
|
|
7
|
+
// * On scroll.
|
|
8
|
+
// * On window resize.
|
|
9
|
+
// * On set new items.
|
|
10
|
+
//
|
|
11
|
+
// State updates may be "asynchronous" (like in React), in which case the
|
|
12
|
+
// corresponding operation is "pending" until the state update is applied.
|
|
13
|
+
//
|
|
14
|
+
// If there's a "pending" window resize or a "pending" update of the set of items,
|
|
15
|
+
// then "on scroll" updates aren't dispatched.
|
|
16
|
+
//
|
|
17
|
+
// If there's a "pending" on scroll update and the window is resize or a new set
|
|
18
|
+
// of items is set, then that "pending" on scroll update gets overwritten.
|
|
19
|
+
//
|
|
20
|
+
// If there's a "pending" update of the set of items, then window resize handler
|
|
21
|
+
// sees that "pending" update and dispatches its own state update so that the
|
|
22
|
+
// "pending" state update originating from `setItems()` is not lost.
|
|
23
|
+
//
|
|
24
|
+
// If there's a "pending" window resize, and a new set of items is set,
|
|
25
|
+
// then the state update of the window resize handler gets overwritten.
|
|
26
|
+
|
|
27
|
+
export default function createStateHelpers({
|
|
28
|
+
state,
|
|
29
|
+
onStateChange,
|
|
30
|
+
render,
|
|
31
|
+
items: initialItems
|
|
32
|
+
}) {
|
|
33
|
+
this.onStateChange = onStateChange
|
|
34
|
+
this._render = render
|
|
35
|
+
|
|
36
|
+
this._onItemStateChange = (i, newItemState) => {
|
|
37
|
+
if (isDebug()) {
|
|
38
|
+
log('~ Item state changed ~')
|
|
39
|
+
log('Item', i)
|
|
40
|
+
// Uses `JSON.stringify()` here instead of just outputting the JSON objects as is
|
|
41
|
+
// because outputting JSON objects as is would show different results later when
|
|
42
|
+
// the developer inspects those in the web browser console if those state objects
|
|
43
|
+
// get modified in between they've been output to the console and the developer
|
|
44
|
+
// decided to inspect them.
|
|
45
|
+
log('Previous state' + '\n' + JSON.stringify(this.getState().itemStates[i], null, 2))
|
|
46
|
+
log('New state' + '\n' + JSON.stringify(newItemState, null, 2))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.getState().itemStates[i] = newItemState
|
|
50
|
+
|
|
51
|
+
// Schedule the item state update for after the new items have been rendered.
|
|
52
|
+
if (this.newItemsWillBeRendered) {
|
|
53
|
+
if (!this.itemStatesThatChangedWhileNewItemsWereBeingRendered) {
|
|
54
|
+
this.itemStatesThatChangedWhileNewItemsWereBeingRendered = {}
|
|
55
|
+
}
|
|
56
|
+
this.itemStatesThatChangedWhileNewItemsWereBeingRendered[String(i)] = newItemState
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.getState = () => this._getState()
|
|
61
|
+
|
|
62
|
+
this.updateState = (stateUpdate) => {
|
|
63
|
+
if (isDebug()) {
|
|
64
|
+
log('~ Set state ~')
|
|
65
|
+
log(getStateSnapshot(stateUpdate))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Ensure that a non-initial `stateUpdate` can only contain an `items`
|
|
69
|
+
// property when it comes from a `setItems()` call.
|
|
70
|
+
if (stateUpdate.items) {
|
|
71
|
+
if (!this._isSettingNewItems) {
|
|
72
|
+
reportError('A `stateUpdate` can only contain `items` property as a result of calling `.setItems()`')
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
this._isSettingNewItems = undefined
|
|
76
|
+
|
|
77
|
+
// Update `state`.
|
|
78
|
+
this.previousState = this.getState()
|
|
79
|
+
this._updateState(stateUpdate)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.getInitialState = () => {
|
|
83
|
+
if (state) {
|
|
84
|
+
return getRestoredState.call(this, state)
|
|
85
|
+
}
|
|
86
|
+
return getInitialStateFromScratch.call(this)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.useState = ({
|
|
90
|
+
getState,
|
|
91
|
+
updateState
|
|
92
|
+
}) => {
|
|
93
|
+
if (this._isActive) {
|
|
94
|
+
throw new Error('[virtual-scroller] `VirtualScroller` has already been started')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (this._getState) {
|
|
98
|
+
throw new Error('[virtual-scroller] Custom state storage has already been configured')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (render) {
|
|
102
|
+
throw new Error('[virtual-scroller] Creating a `VirtualScroller` class instance with a `render()` parameter means using the default (internal) state storage')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!getState || !updateState) {
|
|
106
|
+
throw new Error('[virtual-scroller] When using a custom state storage, one must supply both `getState()` and `updateState()` functions')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this._usesCustomStateStorage = true
|
|
110
|
+
|
|
111
|
+
this._getState = getState
|
|
112
|
+
this._updateState = updateState
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.useDefaultStateStorage = () => {
|
|
116
|
+
if (!render) {
|
|
117
|
+
throw new Error('[virtual-scroller] When using the default (internal) state management, one must supply a `render(state, prevState)` function parameter')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Create default `getState()`/`updateState()` functions.
|
|
121
|
+
this._getState = defaultGetState.bind(this)
|
|
122
|
+
this._updateState = defaultUpdateState.bind(this)
|
|
123
|
+
|
|
124
|
+
// When `state` is stored externally, a developer is responsible for
|
|
125
|
+
// initializing it with the initial value.
|
|
126
|
+
// Otherwise, if default state management is used, set the initial state now.
|
|
127
|
+
const setInitialState = defaultSetInitialState.bind(this)
|
|
128
|
+
setInitialState(this.getInitialState())
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function defaultGetState() {
|
|
132
|
+
return this.state
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function defaultSetInitialState(newState) {
|
|
136
|
+
this.state = newState
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function defaultUpdateState(stateUpdate) {
|
|
140
|
+
// Because this variant of `.updateState()` is "synchronous" (immediate),
|
|
141
|
+
// it can be written like `...prevState`, and no state updates would be lost.
|
|
142
|
+
// But if it was "asynchronous" (not immediate), then `...prevState`
|
|
143
|
+
// wouldn't work in all cases, because it could be stale in cases
|
|
144
|
+
// when more than a single `updateState()` call is made before
|
|
145
|
+
// the state actually updates, making `prevState` stale.
|
|
146
|
+
this.state = {
|
|
147
|
+
...this.state,
|
|
148
|
+
...stateUpdate
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
render(this.state, this.previousState)
|
|
152
|
+
|
|
153
|
+
this.onRender()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Returns the initial state of the `VirtualScroller` "from scratch".
|
|
158
|
+
* (i.e. not from a previously saved one).
|
|
159
|
+
* @return {object}
|
|
160
|
+
*/
|
|
161
|
+
function getInitialStateFromScratch() {
|
|
162
|
+
const items = initialItems
|
|
163
|
+
const state = {
|
|
164
|
+
...getInitialLayoutState.call(this, items),
|
|
165
|
+
items,
|
|
166
|
+
itemStates: new Array(items.length)
|
|
167
|
+
}
|
|
168
|
+
if (isDebug()) {
|
|
169
|
+
log('Initial state (autogenerated)', getStateSnapshot(state))
|
|
170
|
+
}
|
|
171
|
+
log('First shown item index', state.firstShownItemIndex)
|
|
172
|
+
log('Last shown item index', state.lastShownItemIndex)
|
|
173
|
+
return state
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function getRestoredState(state) {
|
|
177
|
+
if (isDebug()) {
|
|
178
|
+
log('Restore state', getStateSnapshot(state))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Possibly clean up "before resize" property in state.
|
|
182
|
+
// "Before resize" state property is cleaned up when all "before resize" item heights
|
|
183
|
+
// have been re-measured in an asynchronous `this.updateState({ beforeResize: undefined })` call.
|
|
184
|
+
// If `VirtualScroller` state was snapshotted externally before that `this.updateState()` call
|
|
185
|
+
// has been applied, then "before resize" property might have not been cleaned up properly.
|
|
186
|
+
state = cleanUpBeforeResizeState(state)
|
|
187
|
+
|
|
188
|
+
// Reset `verticalSpacing` so that it re-measures it after the list
|
|
189
|
+
// has been rendered initially. The rationale is that a previously captured
|
|
190
|
+
// inter-item vertical spacing can't be "trusted" in a sense that the user
|
|
191
|
+
// might have resized the window after the previous `state` has been snapshotted.
|
|
192
|
+
// If the user has resized the window, then changing window width might have
|
|
193
|
+
// activated different CSS `@media()` "queries" resulting in a potentially different
|
|
194
|
+
// vertical spacing when the `VirtualScroller` is re-created with such previously
|
|
195
|
+
// snapshotted state.
|
|
196
|
+
state = {
|
|
197
|
+
...state,
|
|
198
|
+
verticalSpacing: undefined
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// `this.verticalSpacing` acts as a "true" source for vertical spacing value.
|
|
202
|
+
// Vertical spacing is also stored in `state` but `state` updates could be
|
|
203
|
+
// "asynchronous" (not applied immediately) and `this.onUpdateShownItemIndexes()`
|
|
204
|
+
// requires vertical spacing to be correct at any time, without any delays.
|
|
205
|
+
// So, vertical spacing is also duplicated in `state`, but the "true" source
|
|
206
|
+
// is still `this.verticalSpacing`.
|
|
207
|
+
//
|
|
208
|
+
// `this.verticalSpacing` must be initialized before calling `this.getInitialStateFromScratch()`
|
|
209
|
+
// because `this.getInitialStateFromScratch()` uses `this.verticalSpacing` in its calculations.
|
|
210
|
+
//
|
|
211
|
+
// With the code above, `state.verticalSpacing` is always gonna be `undefined`,
|
|
212
|
+
// so commented out this code. It's safer to just re-measure vertical spacing
|
|
213
|
+
// from scratch when `VirtualScroller` is mounted.
|
|
214
|
+
//
|
|
215
|
+
// this.verticalSpacing = state ? state.verticalSpacing : undefined
|
|
216
|
+
|
|
217
|
+
// Check if the actual `columnsCount` on the screen matches the one from state.
|
|
218
|
+
if (isStateColumnsCountMismatch(state, {
|
|
219
|
+
columnsCount: this.getActualColumnsCount()
|
|
220
|
+
})) {
|
|
221
|
+
warn('Reset Layout')
|
|
222
|
+
state = {
|
|
223
|
+
...state,
|
|
224
|
+
...getInitialLayoutState.call(this, state.items)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return state
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function getInitialLayoutState(items) {
|
|
232
|
+
const itemsCount = items.length
|
|
233
|
+
const {
|
|
234
|
+
firstShownItemIndex,
|
|
235
|
+
lastShownItemIndex,
|
|
236
|
+
beforeItemsHeight,
|
|
237
|
+
afterItemsHeight
|
|
238
|
+
} = this.layout.getInitialLayoutValues({
|
|
239
|
+
itemsCount,
|
|
240
|
+
columnsCount: this.getActualColumnsCount()
|
|
241
|
+
})
|
|
242
|
+
const itemHeights = new Array(itemsCount)
|
|
243
|
+
// Optionally preload items to be rendered.
|
|
244
|
+
this.onBeforeShowItems(
|
|
245
|
+
items,
|
|
246
|
+
itemHeights,
|
|
247
|
+
firstShownItemIndex,
|
|
248
|
+
lastShownItemIndex
|
|
249
|
+
)
|
|
250
|
+
return {
|
|
251
|
+
itemHeights,
|
|
252
|
+
columnsCount: this.getActualColumnsCountForState(),
|
|
253
|
+
verticalSpacing: this.verticalSpacing,
|
|
254
|
+
firstShownItemIndex,
|
|
255
|
+
lastShownItemIndex,
|
|
256
|
+
beforeItemsHeight,
|
|
257
|
+
afterItemsHeight
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Checks if the actual `columnsCount` on the screen matches the one from state.
|
|
262
|
+
//
|
|
263
|
+
// For example, a developer might snapshot `VirtualScroller` state
|
|
264
|
+
// when the user navigates from the page containing the list
|
|
265
|
+
// in order to later restore the list's state when the user goes "Back".
|
|
266
|
+
// But, the user might have also resized the window while being on that
|
|
267
|
+
// "other" page, and when they come "Back", their snapshotted state
|
|
268
|
+
// no longer qualifies. Well, it does qualify, but only partially.
|
|
269
|
+
// For example, `itemStates` are still valid, but first and last shown
|
|
270
|
+
// item indexes aren't.
|
|
271
|
+
//
|
|
272
|
+
function isStateColumnsCountMismatch(state, { columnsCount }) {
|
|
273
|
+
const stateColumnsCount = state.columnsCount || 1
|
|
274
|
+
if (stateColumnsCount !== columnsCount) {
|
|
275
|
+
warn('~ Columns Count changed from', stateColumnsCount, 'to', columnsCount, '~')
|
|
276
|
+
return true
|
|
277
|
+
}
|
|
278
|
+
const firstShownItemIndex = Math.floor(state.firstShownItemIndex / columnsCount) * columnsCount
|
|
279
|
+
if (firstShownItemIndex !== state.firstShownItemIndex) {
|
|
280
|
+
warn('~ First Shown Item Index', state.firstShownItemIndex, 'is not divisible by Columns Count', columnsCount, '~')
|
|
281
|
+
return true
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import log from './utility/debug.js'
|
|
2
|
+
import getVerticalSpacing from './getVerticalSpacing.js'
|
|
3
|
+
|
|
4
|
+
export default function createVerticalSpacingHelpers() {
|
|
5
|
+
// Bind to `this` in order to prevent bugs when this function is passed by reference
|
|
6
|
+
// and then called with its `this` being unintentionally `window` resulting in
|
|
7
|
+
// the `if` condition being "falsy".
|
|
8
|
+
this.getVerticalSpacing = () => {
|
|
9
|
+
return this.verticalSpacing || 0
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
this.getVerticalSpacingBeforeResize = () => {
|
|
13
|
+
// `beforeResize.verticalSpacing` can be `undefined`.
|
|
14
|
+
// For example, if `this.updateState({ verticalSpacing })` call hasn't been applied
|
|
15
|
+
// before the resize happened (in case of an "asynchronous" state update).
|
|
16
|
+
const { beforeResize } = this.getState()
|
|
17
|
+
return beforeResize && beforeResize.verticalSpacing || 0
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Measures item vertical spacing, if not measured.
|
|
22
|
+
* @return {object} [stateUpdate]
|
|
23
|
+
*/
|
|
24
|
+
this.measureVerticalSpacingIfNotMeasured = () => {
|
|
25
|
+
if (this.verticalSpacing === undefined) {
|
|
26
|
+
this.verticalSpacing = measureVerticalSpacing.call(this)
|
|
27
|
+
return this.verticalSpacing
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function measureVerticalSpacing() {
|
|
32
|
+
const {
|
|
33
|
+
firstShownItemIndex,
|
|
34
|
+
lastShownItemIndex
|
|
35
|
+
} = this.getState()
|
|
36
|
+
|
|
37
|
+
log('~ Measure item vertical spacing ~')
|
|
38
|
+
|
|
39
|
+
const verticalSpacing = getVerticalSpacing({
|
|
40
|
+
itemsContainer: this.itemsContainer,
|
|
41
|
+
renderedItemsCount: lastShownItemIndex - firstShownItemIndex + 1
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
if (verticalSpacing === undefined) {
|
|
45
|
+
log('Not enough items rendered to measure vertical spacing')
|
|
46
|
+
} else {
|
|
47
|
+
log('Item vertical spacing', verticalSpacing)
|
|
48
|
+
return verticalSpacing
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|