virtual-scroller 1.7.9 → 1.8.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 +14 -1
- package/README.md +139 -33
- package/babel.config.js +25 -0
- package/babel.js +5 -0
- package/bundle/index-bypass.html +1 -1
- package/bundle/index-dom.html +1 -1
- package/bundle/index-grid.html +1 -2
- package/bundle/index-scrollableContainer.html +1 -1
- package/bundle/index-tbody-scrollableContainer.html +2 -0
- package/bundle/index-tbody.html +2 -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 +319 -0
- package/commonjs/BeforeResize.js.map +1 -0
- package/commonjs/DOM/Engine.js +46 -0
- package/commonjs/DOM/Engine.js.map +1 -0
- package/commonjs/DOM/ItemsContainer.js +78 -0
- package/commonjs/DOM/ItemsContainer.js.map +1 -0
- package/commonjs/DOM/{WaitForStylesToLoad.js → ListTopOffsetWatcher.js} +56 -35
- package/commonjs/DOM/ListTopOffsetWatcher.js.map +1 -0
- package/commonjs/DOM/ScrollableContainer.js +48 -80
- package/commonjs/DOM/ScrollableContainer.js.map +1 -1
- package/commonjs/DOM/VirtualScroller.js +20 -15
- package/commonjs/DOM/VirtualScroller.js.map +1 -1
- package/commonjs/DOM/tbody.js +2 -2
- package/commonjs/ItemHeights.js +13 -20
- package/commonjs/ItemHeights.js.map +1 -1
- package/commonjs/Layout.js +588 -215
- package/commonjs/Layout.js.map +1 -1
- package/commonjs/Layout.test.js +191 -0
- package/commonjs/Layout.test.js.map +1 -0
- package/commonjs/ListHeightChangeWatcher.js +126 -0
- package/commonjs/ListHeightChangeWatcher.js.map +1 -0
- package/commonjs/Resize.js +22 -21
- package/commonjs/Resize.js.map +1 -1
- package/commonjs/Scroll.js +148 -88
- package/commonjs/Scroll.js.map +1 -1
- package/commonjs/VirtualScroller.js +1269 -390
- package/commonjs/VirtualScroller.js.map +1 -1
- package/commonjs/getItemCoordinates.js.map +1 -1
- package/commonjs/getItemsDiff.js.map +1 -1
- package/commonjs/getVerticalSpacing.js +8 -8
- package/commonjs/getVerticalSpacing.js.map +1 -1
- package/commonjs/react/VirtualScroller.js +31 -37
- package/commonjs/react/VirtualScroller.js.map +1 -1
- package/commonjs/utility/debounce.js +26 -4
- package/commonjs/utility/debounce.js.map +1 -1
- package/commonjs/utility/debug.js +51 -12
- package/commonjs/utility/debug.js.map +1 -1
- package/commonjs/utility/getStateSnapshot.js +50 -0
- package/commonjs/utility/getStateSnapshot.js.map +1 -0
- package/commonjs/utility/px.js +1 -1
- package/commonjs/utility/px.js.map +1 -1
- package/commonjs/utility/px.test.js +14 -0
- package/commonjs/utility/px.test.js.map +1 -0
- 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.d.ts +23 -0
- package/index.d.ts +84 -0
- package/modules/BeforeResize.js +310 -0
- package/modules/BeforeResize.js.map +1 -0
- package/modules/DOM/Engine.js +27 -0
- package/modules/DOM/Engine.js.map +1 -0
- package/modules/DOM/ItemsContainer.js +71 -0
- package/modules/DOM/ItemsContainer.js.map +1 -0
- package/modules/DOM/{WaitForStylesToLoad.js → ListTopOffsetWatcher.js} +57 -35
- package/modules/DOM/ListTopOffsetWatcher.js.map +1 -0
- package/modules/DOM/ScrollableContainer.js +47 -79
- package/modules/DOM/ScrollableContainer.js.map +1 -1
- package/modules/DOM/VirtualScroller.js +15 -14
- package/modules/DOM/VirtualScroller.js.map +1 -1
- package/modules/ItemHeights.js +8 -19
- package/modules/ItemHeights.js.map +1 -1
- package/modules/Layout.js +582 -213
- package/modules/Layout.js.map +1 -1
- package/modules/Layout.test.js +185 -0
- package/modules/Layout.test.js.map +1 -0
- package/modules/ListHeightChangeWatcher.js +119 -0
- package/modules/ListHeightChangeWatcher.js.map +1 -0
- package/modules/Resize.js +21 -20
- package/modules/Resize.js.map +1 -1
- package/modules/Scroll.js +148 -87
- package/modules/Scroll.js.map +1 -1
- package/modules/VirtualScroller.js +1263 -390
- package/modules/VirtualScroller.js.map +1 -1
- package/modules/getItemCoordinates.js.map +1 -1
- package/modules/getItemsDiff.js.map +1 -1
- package/modules/getVerticalSpacing.js +8 -8
- package/modules/getVerticalSpacing.js.map +1 -1
- package/modules/react/VirtualScroller.js +31 -37
- package/modules/react/VirtualScroller.js.map +1 -1
- package/modules/utility/debounce.js +26 -4
- package/modules/utility/debounce.js.map +1 -1
- package/modules/utility/debug.js +47 -10
- package/modules/utility/debug.js.map +1 -1
- package/modules/utility/getStateSnapshot.js +43 -0
- package/modules/utility/getStateSnapshot.js.map +1 -0
- package/modules/utility/px.js +1 -1
- package/modules/utility/px.js.map +1 -1
- package/modules/utility/px.test.js +9 -0
- package/modules/utility/px.test.js.map +1 -0
- 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 +24 -22
- package/react/index.d.ts +27 -0
- package/source/BeforeResize.js +317 -0
- package/source/DOM/Engine.js +32 -0
- package/source/DOM/ItemsContainer.js +48 -0
- package/source/DOM/{WaitForStylesToLoad.js → ListTopOffsetWatcher.js} +48 -22
- package/source/DOM/ScrollableContainer.js +31 -55
- package/source/DOM/VirtualScroller.js +6 -7
- package/source/ItemHeights.js +10 -15
- package/source/Layout.js +626 -252
- package/source/Layout.test.js +171 -0
- package/source/ListHeightChangeWatcher.js +94 -0
- package/source/Resize.js +23 -15
- package/source/Scroll.js +139 -78
- package/source/VirtualScroller.js +1240 -286
- package/source/getVerticalSpacing.js +7 -7
- package/source/react/VirtualScroller.js +2 -18
- package/source/utility/debounce.js +20 -3
- package/source/utility/debug.js +34 -3
- package/source/utility/getStateSnapshot.js +36 -0
- package/source/utility/px.js +1 -1
- package/source/utility/px.test.js +9 -0
- package/website/index-bypass.html +195 -0
- package/website/index-grid.html +0 -1
- package/website/index-scrollableContainer.html +208 -0
- package/website/index-tbody-scrollableContainer.html +68 -0
- package/website/index-tbody.html +55 -0
- package/commonjs/DOM/RenderingEngine.js +0 -33
- package/commonjs/DOM/RenderingEngine.js.map +0 -1
- package/commonjs/DOM/Screen.js +0 -87
- package/commonjs/DOM/Screen.js.map +0 -1
- package/commonjs/DOM/WaitForStylesToLoad.js.map +0 -1
- package/commonjs/RestoreScroll.js +0 -118
- package/commonjs/RestoreScroll.js.map +0 -1
- package/modules/DOM/RenderingEngine.js +0 -19
- package/modules/DOM/RenderingEngine.js.map +0 -1
- package/modules/DOM/Screen.js +0 -80
- package/modules/DOM/Screen.js.map +0 -1
- package/modules/DOM/WaitForStylesToLoad.js.map +0 -1
- package/modules/RestoreScroll.js +0 -111
- package/modules/RestoreScroll.js.map +0 -1
- package/source/DOM/RenderingEngine.js +0 -22
- package/source/DOM/Screen.js +0 -51
- package/source/RestoreScroll.js +0 -86
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
<!-- `virtual-scroller`: in `.updateItems()` handle a case when `items.length` is the same, in which case find different items and if those items are rendered then maybe update them on screen and update their height, if the items are past rendered then maybe just discard all item heights past rendered, if the items are before rendered then maybe ignore and it will jump on scroll up which is kinda acceptable. -->
|
|
2
2
|
|
|
3
|
+
1.8.0 / 26.11.2021
|
|
4
|
+
==================
|
|
5
|
+
|
|
6
|
+
* Refactored the code. Some parts got rewritten.
|
|
7
|
+
|
|
8
|
+
* Added tests.
|
|
9
|
+
|
|
10
|
+
* Added TypeScript "typings" (didn't check).
|
|
11
|
+
|
|
12
|
+
* Fixed the list [being reset](https://gitlab.com/catamphetamine/virtual-scroller/-/issues/15) on resize.
|
|
13
|
+
|
|
14
|
+
* Removed `preserveScrollPositionOfTheBottomOfTheListOnMount` option because it's not used.
|
|
15
|
+
|
|
3
16
|
1.7.9 / 30.04.2021
|
|
4
17
|
==================
|
|
5
18
|
|
|
@@ -22,7 +35,7 @@
|
|
|
22
35
|
|
|
23
36
|
* Removed `getItemCoordinates(i)` function.
|
|
24
37
|
|
|
25
|
-
* Added `
|
|
38
|
+
* Added `engine` option.
|
|
26
39
|
|
|
27
40
|
* Refactored.
|
|
28
41
|
|
package/README.md
CHANGED
|
@@ -87,8 +87,6 @@ The following `state` properties are only used for saving and restoring `Virtual
|
|
|
87
87
|
* `verticalSpacing: number?` — Vertical item spacing. Is `undefined` until it has been measured. Is only measured once, when at least two rows of items have been rendered.
|
|
88
88
|
|
|
89
89
|
* `columnsCount: number?` — The count of items in a row. Is `undefined` if no `getColumnsCount()` parameter has been passed to `VirtualScroller`.
|
|
90
|
-
|
|
91
|
-
* `scrollY: number?` — The current page scroll position (page vertical scroll offset). If initial `state` is passed to `VirtualScroller`, then the page will be scrolled to `state.scrollY` on `.render()`.
|
|
92
90
|
</details>
|
|
93
91
|
|
|
94
92
|
#### Example
|
|
@@ -101,7 +99,20 @@ The following `state` properties are only used for saving and restoring `Virtual
|
|
|
101
99
|
```js
|
|
102
100
|
import VirtualScroller from 'virtual-scroller'
|
|
103
101
|
|
|
104
|
-
const
|
|
102
|
+
const items = [...]
|
|
103
|
+
|
|
104
|
+
const getItemsContainerElement = () => ...
|
|
105
|
+
|
|
106
|
+
const virtualScroller = new VirtualScroller(getItemsContainerElement, items, {
|
|
107
|
+
onStateChange(state) {
|
|
108
|
+
// Re-render the list based on its state:
|
|
109
|
+
// * items
|
|
110
|
+
// * firstShownItemIndex
|
|
111
|
+
// * lastShownItemIndex
|
|
112
|
+
// * beforeItemsHeight
|
|
113
|
+
// * afterItemsHeight
|
|
114
|
+
}
|
|
115
|
+
})
|
|
105
116
|
|
|
106
117
|
// Start listening to scroll events.
|
|
107
118
|
virtualScroller.listen()
|
|
@@ -110,9 +121,9 @@ virtualScroller.listen()
|
|
|
110
121
|
virtualScroller.stop()
|
|
111
122
|
```
|
|
112
123
|
|
|
113
|
-
* `
|
|
124
|
+
* `getItemsContainerElement()` function returns the list "element" that is gonna contain all list item "elements".
|
|
114
125
|
* `items` is the list of items.
|
|
115
|
-
* `
|
|
126
|
+
* `onStateChange(state)` is one of the available list `options`.
|
|
116
127
|
</details>
|
|
117
128
|
|
|
118
129
|
#####
|
|
@@ -217,11 +228,9 @@ virtualScroller.listen()
|
|
|
217
228
|
|
|
218
229
|
#####
|
|
219
230
|
|
|
220
|
-
* `estimatedItemHeight: number` — Is used for the initial render of the list: determines how many list items are rendered initially to cover the screen height plus some extra vertical margin for future scrolling. If not set then the list first renders just the first item, measures it, and then assumes it to be the `estimatedItemHeight` from which it calculates how many items to show on the second render pass to fill the screen height plus
|
|
231
|
+
* `estimatedItemHeight: number` — Is used for the initial render of the list: determines how many list items are rendered initially to cover the screen height plus some extra vertical margin (called "prerender margin") for future scrolling. If not set then the list first renders just the first item, measures it, and then assumes it to be the `estimatedItemHeight` from which it calculates how many items to show on the second render pass to fill the screen height plus the "prerender margin". Therefore, this setting is only for the initial render minor optimization and is not required.
|
|
221
232
|
|
|
222
|
-
|
|
223
|
-
* `margin` — Renders items which are outside of the screen by the amount of this "margin". Is the screen height by default: seems to be the optimal value for "Page Up" / "Page Down" navigation and optimized mouse wheel scrolling.
|
|
224
|
-
-->
|
|
233
|
+
* `prerenderMargin` — The list component renders not only the items that're currently visible but also the items that lie within some extra vertical margin (called "prerender margin") on top and bottom for future scrolling: this way, there'll be significantly less layout recalculations as the user scrolls, because now it doesn't have to recalculate layout on each scroll event. By default, the "prerender margin" is equal to the screen height: this seems to be the optimal value for "Page Up" / "Page Down" navigation and optimized mouse wheel scrolling. This parameter is currently ignored because the default value seems to fit all possible use cases.
|
|
225
234
|
|
|
226
235
|
* `state: object` — The initial state for `VirtualScroller`. Can be used, for example, to quicky restore the list when it's re-rendered on "Back" navigation.
|
|
227
236
|
|
|
@@ -229,20 +238,22 @@ virtualScroller.listen()
|
|
|
229
238
|
|
|
230
239
|
* `getState(): object` — (advanced) By default, `VirtualScroller` manages `state` internally by storing it in an instance variable. For more control, the `state` could be managed externally, in which case a developer should supply `getState`/`setState` options, in which case `getState` should return the externally stored `VirtualScroller` `state`. React `VirtualScroller` component implementation uses this option.
|
|
231
240
|
|
|
232
|
-
* `setState(
|
|
241
|
+
* `setState(stateUpdate: object, { willUpdateState, didUpdateState })` — (advanced) By default, `VirtualScroller` manages `state` internally by storing it in an instance variable. For more control, the `state` could be managed externally, in which case a developer should supply `getState`/`setState` options, in which case `setState` should update the externally stored `VirtualScroller` `state` (including setting the initial `state`), and it should do that by merging the previous `state` with the `stateUpdate` argument. `setState` must also call two functions: `willUpdateState(newState, prevState)` right before the state is updated and `didUpdateState(prevState)` right after the state is updated. The list should be re-rendered as part of either `setState` or `onStateChange`. `setState` could be ["asynchronous"](https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous), that is when state updates aren't applied immediately and are instead queued and then applied in a single state update for performance. React `VirtualScroller` component implementation uses this option.
|
|
233
242
|
|
|
234
|
-
* `onStateChange(newState: object, prevState: object?)` — Is called whenever `VirtualScroller` `state` is updated (including setting the initial `state`).
|
|
243
|
+
* `onStateChange(newState: object, prevState: object?)` — Is called whenever `VirtualScroller` `state` is updated (including setting the initial `state`). Is not called when individual item heights (including "before resize" ones) or states are updated: instead, individual item heights or states are updated in-place — `state.itemHeights[i] = newItemHeight` or `state.itemStates[i] = newItemState`. That's because those `state` properties are the ones that don’t affect the presentation, so there's no need to re-render the list when those do change — updating those properties is just an effect of some change rather than cause for one. In order for a `VirtualScroller` implementation to work, a developer must either supply `getState`/`setState` options or `onStateChange` option (or both). The list should be re-rendered as part of either `setState` or `onStateChange`.
|
|
235
244
|
|
|
236
245
|
* `scrollableContainer: Element` — (advanced) If the list is being rendered in a "scrollable container" (for example, if one of the parent elements of the list is styled with `max-height` and `overflow: auto`), then passing the "scrollable container" DOM Element is required for correct operation. *This feature is considered [experimental](https://github.com/catamphetamine/virtual-scroller/issues/8).* The width and height of the `scrollableContainer` shouldn't change unless window is resized.
|
|
237
246
|
|
|
247
|
+
* `initialScrollPosition: number` — If passed, the page will be scrolled to this `scrollY` position.
|
|
248
|
+
|
|
249
|
+
* `onScrollPositionChange(scrollY: number)` — Is called whenever a user scrolls the page.
|
|
250
|
+
|
|
238
251
|
* `bypass: boolean` — Pass `true` to turn off `VirtualScroller` behavior and just render the full list of items.
|
|
239
252
|
|
|
240
253
|
* `getItemId(item)` — (advanced) When `items` are dynamically updated via `.setItems()`, `VirtualScroller` detects an "incremental" update by comparing "new" and "old" item ["references"](https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0): this way, `VirtualScroller` can understand that the "new" `items` are (mostly) the same as the "old" `items` when some items get prepended or appended to the list, in which case it doesn't re-render the whole list from scratch, but rather just renders the "new" items that got prepended or appended. Sometimes though, some of the "old" items might get updated: for example, if `items` is a list of comments, then some of those comments might get edited in-between the refreshes. In that case, the edited comment object reference should change in order to indicate that the comment's content has changed and that the comment should be re-rendered (at least that's how it has to be done in React world). At the same time, changing the edited comment object reference would break `VirtualScroller`'s "incremental" update detection, and it would re-render the whole list of comments from scratch, which is not what it should be doing in such cases. So, in cases like this, `VirtualScroller` should have some way to understand that the updated item, even if its object reference has changed, is still the same as the old one, so that it doesn't break "incremental" update detection. For that, `getItemId(item)` parameter could be passed, which `VirtualScroller` would use to compare "old" and "new" items (instead of the default "reference equality" check), and that would fix the "re-rendering the whole list from scratch" issue. It can also be used when `items` are fetched from an external API, in which case all item object references change on every such fetch.
|
|
241
254
|
|
|
242
255
|
* `onItemInitialRender(item)` — Is called for each `item` when it's about to be rendered for the first time. Is guaranteed to be called at least once for each item rendered, though, in "asynchronous" rendering systems like React, it could be called multiple times for a given item, because "an item is calculated to be rendered" doesn't necessarily mean that the actual rendering will take place before a later calculation supercedes the former one. This function can be used to somehow "initialize" items before they're rendered for the first time. For example, consider a list of items that must be somehow "preprocessed" (parsed, enhanced, etc) before being rendered, and such "preprocessing" puts some load on the CPU (and therefore takes some time). In such case, instead of "preprocessing" the whole list of items up front, a developer could "preprocess" the items as they're being rendered, thereby eliminating any associated lag or freezing that would be inevitable have all the items been "preprocessed" up front. If a user only wants to see a few of the items, "preprocessing" all the items up front would simply be a waste.
|
|
243
256
|
|
|
244
|
-
* `preserveScrollPositionOfTheBottomOfTheListOnMount: boolean` — (advanced) Set to `true` to preserve scroll position relative to the bottom of the list when it's first mounted. A possible use case: consider a forum thread only showing unread posts by default. If a user navigates to such thread, it could show "No new posts" message with a "Show previous" button above it. When the user clicks the "Show previous" button, a `<VirtualScroller/>` is mounted with a list of posts. If `preserveScrollPositionOfTheBottomOfTheListOnMount: true` is set, then, after the list of posts is shown, page scroll will be restored so that the bottom of the list remains on screen so that the user could start scrolling up to read the "previous" posts starting from the most recent ones to the oldest ones.
|
|
245
|
-
|
|
246
257
|
* `shouldUpdateLayoutOnScreenResize(event: Event): boolean` — By default, `VirtualScroller` always performs a re-layout on window `resize` event. The `resize` event is not only triggered when a user resizes the window itself: it's also [triggered](https://developer.mozilla.org/en-US/docs/Web/API/Window/fullScreen#Notes) when the user switches into (and out of) fullscreen mode. By default, `VirtualScroller` performs a re-layout on all window `resize` events, except for ones that don't result in actual window width or height change, and except for cases when, for example, a video somewhere in a list is maximized into fullscreen. There still can be other "custom" cases: for example, when an application uses a custom "slideshow" component (rendered outside of the list DOM element) that goes into fullscreen when a user clicks a picture or a video in the list. For such "custom" cases `shouldUpdateLayoutOnScreenResize(event)` option / property can be specified.
|
|
247
258
|
|
|
248
259
|
* `measureItemsBatchSize: number` — (advanced) (experimental) Imagine a situation when a user doesn't gradually scroll through a huge list but instead hits an End key to scroll right to the end of such huge list: this will result in the whole list rendering at once (because an item needs to know the height of all previous items in order to render at correct scroll position) which could be CPU-intensive in some cases (for example, when using React due to its slow performance when initially rendering components on a page). To prevent freezing the UI in the process, a `measureItemsBatchSize` could be configured, that would limit the maximum count of items that're being rendered in a single pass for measuring their height: if `measureItemsBatchSize` is configured, then such items will be rendered and measured in batches. By default it's set to `100`. This is an experimental feature and could be removed in future non-major versions of this library. For example, the future React 17 will come with [Fiber](https://www.youtube.com/watch?v=ZCuYPiUIONs) rendering engine that is said to resolve such freezing issues internally. In that case, introducing this option may be reconsidered.
|
|
@@ -268,7 +279,9 @@ virtualScroller.listen()
|
|
|
268
279
|
|
|
269
280
|
* `onItemHeightChange(i: number)` — Must be called whenever a list item's height changes (for example, when a user clicks an "Expand"/"Collapse" button of a list item): it re-measures the item's height and updates `VirtualScroller` layout. Every change in an item's height must come as a result of changing some kind of state, be it the item's state in `VirtualScroller` via `.onItemStateChange()`, or some other state managed by the application. Implementation-wise, calling `onItemHeightChange()` manually could be replaced with detecting item height changes automatically via [Resize Observer](https://caniuse.com/#search=Resize%20Observer).
|
|
270
281
|
|
|
271
|
-
* `onItemStateChange(i: number, itemState: object)` — Updates a list item's state inside `VirtualScroller` state. Must be called whenever an item's "state" changes: this way, the item's state is preserved when the item is unmounted due to going off screen, and then restored when the item is on screen again. Calling `onItemStateChange()` doesn't trigger a re-layout of `VirtualScroller` because changing a list item's state doesn't necessarily mean a change of its height, so a re-layout might not be required. If an item's height did change as a result of changing its state, then `VirtualScroller` layout must be updated, and to do that, call `onItemHeightChange(i)` after calling `onItemStateChange()`. For example, consider a social network feed, each post optionally having an attachment. Suppose there's a post in the feed having a YouTube video attachment. The attachment is initially shown as a small thumbnail that expands into a full-sized embedded YouTube video player when a user clicks on it. If the expanded/collapsed state of such attachment isn't been managed in `VirtualScroller`, then, when the user expands the video, then scrolls down so that the post with the video is no longer visible and is unmounted as a result, then scrolls back up so that the post with the video is visible again, the video's expanded state would be lost, and it would be rendered as a small thumbnail as if the user didn't click on it. And don't forget about calling `onItemHeightChange(i)` in such cases: if `onItemHeightChange(i)` isn't called after expanding the thumbnail into a video player, then the scroll position would "jump" when such item goes off screen, because `VirtualScroller` would have based its calculations on the initially measured item height, not the "expanded" one.
|
|
282
|
+
* `onItemStateChange(i: number, itemState: object?)` — Updates a list item's state inside `VirtualScroller` state. Must be called whenever an item's "state" changes: this way, the item's state is preserved when the item is unmounted due to going off screen, and then restored when the item is on screen again. Calling `onItemStateChange()` doesn't trigger a re-layout of `VirtualScroller` because changing a list item's state doesn't necessarily mean a change of its height, so a re-layout might not be required. If an item's height did change as a result of changing its state, then `VirtualScroller` layout must be updated, and to do that, call `onItemHeightChange(i)` after calling `onItemStateChange()`. For example, consider a social network feed, each post optionally having an attachment. Suppose there's a post in the feed having a YouTube video attachment. The attachment is initially shown as a small thumbnail that expands into a full-sized embedded YouTube video player when a user clicks on it. If the expanded/collapsed state of such attachment isn't been managed in `VirtualScroller`, then, when the user expands the video, then scrolls down so that the post with the video is no longer visible and is unmounted as a result, then scrolls back up so that the post with the video is visible again, the video's expanded state would be lost, and it would be rendered as a small thumbnail as if the user didn't click on it. And don't forget about calling `onItemHeightChange(i)` in such cases: if `onItemHeightChange(i)` isn't called after expanding the thumbnail into a video player, then the scroll position would "jump" when such item goes off screen, because `VirtualScroller` would have based its calculations on the initially measured item height, not the "expanded" one.
|
|
283
|
+
|
|
284
|
+
* `getItemScrollPosition(i: number): number?` — Returns an item's scroll position inside the scrollable container. Returns `undefined` if any of the items before this item haven't been rendered yet.
|
|
272
285
|
|
|
273
286
|
* `setItems(newItems: any[], options: object?)` — Updates `VirtualScroller` `items`. For example, it can be used to prepend or append new items to the list. See [Dynamically Loaded Lists](#dynamically-loaded-lists) section for more details. Available options: `preserveScrollPositionOnPrependItems: boolean` — Set to `true` to enable "restore scroll position after prepending new items" feature (should be used when implementing a "Show previous items" button).
|
|
274
287
|
|
|
@@ -424,7 +437,7 @@ Message.propTypes = {
|
|
|
424
437
|
|
|
425
438
|
* `items` — The items list.
|
|
426
439
|
|
|
427
|
-
* `itemComponent` — List item component.
|
|
440
|
+
* `itemComponent` — List item component. For best performance, make sure it's a `React.PureComponent` or a `React.memo()`, otherwise it'll be re-rendering as the user scrolls and new items get shown or older ones get hidden.
|
|
428
441
|
|
|
429
442
|
* `itemComponentProps: object` — (optional) The props passed to `itemComponent`.
|
|
430
443
|
|
|
@@ -436,8 +449,6 @@ Message.propTypes = {
|
|
|
436
449
|
|
|
437
450
|
* `preserveScrollPositionOnPrependItems: boolean` — (optional) The `preserveScrollPositionOnPrependItems` option of `VirtualScroller.setItems()` method.
|
|
438
451
|
|
|
439
|
-
* `preserveScrollPositionOfTheBottomOfTheListOnMount: boolean` — (optional) The `preserveScrollPositionOfTheBottomOfTheListOnMount` option of `VirtualScroller`.
|
|
440
|
-
|
|
441
452
|
* `measureItemsBatchSize: number` — (optional) The `measureItemsBatchSize` option of `VirtualScroller`.
|
|
442
453
|
|
|
443
454
|
* `getColumnsCount(): number` — (optional) The `getColumnsCount()` option of `VirtualScroller`.
|
|
@@ -486,13 +497,13 @@ class Example extends React.Component {
|
|
|
486
497
|
|
|
487
498
|
#####
|
|
488
499
|
|
|
489
|
-
* `children` —
|
|
500
|
+
* `children` — The list item (an element of the `items` array).
|
|
490
501
|
|
|
491
|
-
* `state` —
|
|
502
|
+
* `state` — (advanced) List item element state. If a list item element renders differently depending on some "state" then that state should be "managed" (stored and later restored) as the list item becomes hidden and later visible again. See `onStateChange` property description.
|
|
492
503
|
|
|
493
|
-
* `onStateChange(
|
|
504
|
+
* `onStateChange(newItemState)` — (advanced) Can be called to save the list item element state when it changes. The need for saving and restoring list item element state arises because item elements get unmounted as they go off screen. For example, consider a social network feed where feed items (posts) can be expanded or collapsed via a "Show more"/"Show less" button. Suppose a user clicks a "Show more" button on a post resulting in that post expanding in height. Then the user scrolls down and since the post is no longer visible it gets unmounted. Since no state is preserved by default, when the user scrolls back up and the post gets mounted again, its previous state will be lost and it will render as a collapsed post instead of an expanded one, resulting in a perceived "jump" of page content by the difference in height of the post being expanded and collapsed. To prevent that, define a "state" of an item element — for example, `{ expanded: true }` — and then call `onStateChange(newState)` every time the item element state changes, and read that state from the `state` property when rendering the item element. Calling `onStateChange()` simply updates the item element `state`, and doesn't re-render the item element: it's just a proxy for `VirtualScroller.onItemStateChange(i, itemState)`.
|
|
494
505
|
|
|
495
|
-
* `onHeightChange()` —
|
|
506
|
+
* `onHeightChange()` — (advanced) Call this function whenever the item element height changes. In the example above, `onHeightChange()` should be called when a user clicks a "Show more"/"Show less" button because that results in a change of the item element's height, so `VirtualScroller` should re-measure it in order for its internal calculations to stay correct. This is simply a proxy for `VirtualScroller.onItemHeightChange(i)`.
|
|
496
507
|
</details>
|
|
497
508
|
|
|
498
509
|
#####
|
|
@@ -511,30 +522,29 @@ class Example extends React.Component {
|
|
|
511
522
|
|
|
512
523
|
## Rendering Engine
|
|
513
524
|
|
|
514
|
-
`VirtualScroller` is written in such a way that it supports any type of a rendering engine, not just DOM. For example, it could support something like React Native or `<canvas/>`: for that, someone would have to write custom versions of [`Screen.js`](https://gitlab.com/catamphetamine/virtual-scroller/-/blob/master/source/DOM/Screen.js) and [`ScrollableContainer.js`](https://gitlab.com/catamphetamine/virtual-scroller/-/blob/master/source/DOM/ScrollableContainer.js), and then instruct `VirtualScroller` to use those instead of the default ones by passing custom `
|
|
525
|
+
`VirtualScroller` is written in such a way that it supports any type of a rendering engine, not just DOM. For example, it could support something like React Native or `<canvas/>`: for that, someone would have to write custom versions of [`Screen.js`](https://gitlab.com/catamphetamine/virtual-scroller/-/blob/master/source/DOM/Screen.js) and [`ScrollableContainer.js`](https://gitlab.com/catamphetamine/virtual-scroller/-/blob/master/source/DOM/ScrollableContainer.js), and then instruct `VirtualScroller` to use those instead of the default ones by passing custom `engine` object when constructing a `VirtualScroller` instance:
|
|
515
526
|
|
|
516
527
|
```js
|
|
517
528
|
import VirtualScroller from 'virtual-scroller'
|
|
518
529
|
|
|
519
|
-
import
|
|
530
|
+
import Container from './Container'
|
|
520
531
|
import ScrollableContainer from './ScrollableContainer'
|
|
521
532
|
|
|
522
|
-
new VirtualScroller(
|
|
533
|
+
new VirtualScroller(getItemsContainerElement, items, {
|
|
523
534
|
scrollableContainer,
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
return new Screen()
|
|
535
|
+
engine: {
|
|
536
|
+
createItemsContainer(getItemsContainerElement) {
|
|
537
|
+
return new Container(getItemsContainerElement)
|
|
528
538
|
},
|
|
529
|
-
createScrollableContainer(scrollableContainer) {
|
|
530
|
-
return new ScrollableContainer(scrollableContainer)
|
|
539
|
+
createScrollableContainer(scrollableContainer, getItemsContainerElement) {
|
|
540
|
+
return new ScrollableContainer(scrollableContainer, getItemsContainerElement)
|
|
531
541
|
}
|
|
532
542
|
},
|
|
533
543
|
...
|
|
534
544
|
})
|
|
535
545
|
```
|
|
536
546
|
|
|
537
|
-
`
|
|
547
|
+
`getItemsContainerElement()` function would simply return a list "element", whatever that could mean. The concept of an "element" is "something, that can be rendered", so it could be anything, not just a DOM Element. Any operations with "elements" are done either in `Container.js` or in `ScrollableContainer.js`: `Container.js` defines the operations that could be applied to the list "container", or its items, such as getting its height or getting an items' height, and `ScrollableContainer.js` defines the operations that could be applied to a "scrollable container", such as getting its dimensions, listening for "resize" and "scroll" events, controlling scroll position, etc.
|
|
538
548
|
|
|
539
549
|
## Dynamically Loaded Lists
|
|
540
550
|
|
|
@@ -608,7 +618,7 @@ function getColumnsCount(container) {
|
|
|
608
618
|
|
|
609
619
|
### Margin collapse
|
|
610
620
|
|
|
611
|
-
If any vertical `margin` is set on the list items, then this may lead to page content "jumping" by the value of that margin while scrolling. The reason is that when the top of the list is visible on screen, no `padding-top` gets applied to the list element, and the CSS spec states that having `padding` on an element disables its ["margin collapse"](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing), so, while there's no `padding-top` on the list element, its margins do "collapse" with outer margins, but when the first item is no longer visible (and no longer rendered), `padding-top` gets applied to the list element to compensate for the non-rendered items, and that `padding-top` prevents the list's margins from "collapsing" with outer margins. So that results in the page content "jumping" when the first item in the list becomes invisible or becomes visible again. To fix that, don't set any `margin-top` on the first item of the list, and don't set any `margin-bottom` on the last item of the list. An example of fixing `margin` for the first and the last items of the list:
|
|
621
|
+
If any vertical CSS `margin` is set on the list items, then this may lead to page content "jumping" by the value of that margin while scrolling. The reason is that when the top of the list is visible on screen, no `padding-top` gets applied to the list element, and the CSS spec states that having `padding` on an element disables its ["margin collapse"](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing), so, while there's no `padding-top` on the list element, its margins do "collapse" with outer margins, but when the first item is no longer visible (and no longer rendered), `padding-top` gets applied to the list element to compensate for the non-rendered items, and that `padding-top` prevents the list's margins from "collapsing" with outer margins. So that results in the page content "jumping" when the first item in the list becomes invisible or becomes visible again. To fix that, don't set any `margin-top` on the first item of the list, and don't set any `margin-bottom` on the last item of the list. An example of fixing `margin` for the first and the last items of the list:
|
|
612
622
|
|
|
613
623
|
```css
|
|
614
624
|
/* This margin is supposed to "collapse" with the outer ones
|
|
@@ -628,7 +638,7 @@ If any vertical `margin` is set on the list items, then this may lead to page co
|
|
|
628
638
|
|
|
629
639
|
### Styling `:first-child` and `:last-child`
|
|
630
640
|
|
|
631
|
-
When styling the first and the last items of the list via `:first-child` and `:last-child
|
|
641
|
+
When styling the first and the last items of the list via `:first-child` and `:last-child`, one should also check that such styles don't change the item's height, which means that one should not add any `border` or `padding` styles to `:first-child` and `:last-child`, otherwise the list items will jump by that extra height during scrolling.
|
|
632
642
|
|
|
633
643
|
An example of a `:first-child`/`:last-child` style that will not work correctly with `VirtualScroller`:
|
|
634
644
|
|
|
@@ -641,6 +651,72 @@ An example of a `:first-child`/`:last-child` style that will not work correctly
|
|
|
641
651
|
}
|
|
642
652
|
```
|
|
643
653
|
|
|
654
|
+
### Resize
|
|
655
|
+
|
|
656
|
+
When the container width changes, all items' heights must be recalculated because some CSS [`@media()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries) rules might have been added or removed. If the list currently shows items starting from the `N`-th one, then all `N - 1` previous items have to be remeasured. But they can't be remeasured until rendered again, so `VirtualScroller` snapshots those items' heights before the resize, and then uses those snapshotted heights until the items are re-measured when they become visible again as the user scrolls up.
|
|
657
|
+
|
|
658
|
+
Also, when such snapshotted upper items get re-rendered and re-measured, the scroll position has to be corrected to avoid ["content jumping"](https://css-tricks.com/content-jumping-avoid/).
|
|
659
|
+
|
|
660
|
+
<details>
|
|
661
|
+
<summary>Correcting scroll position though doesn't seem to work in a particular case when using Chrome web browser on a desktop</summary>
|
|
662
|
+
|
|
663
|
+
#####
|
|
664
|
+
|
|
665
|
+
When the user scrolls up past the "prerender margin", which equals to the screen height by default, the list content does "jump" because the web browser doesn't want to apply the scroll position correction while scrolling for some weird reason. Looks like a bug in the web browser.
|
|
666
|
+
|
|
667
|
+
```
|
|
668
|
+
[virtual-scroller] The user is scrolling: perform a re-layout when they stop scrolling
|
|
669
|
+
Current scroll position: 7989
|
|
670
|
+
[virtual-scroller] The user is scrolling: perform a re-layout when they stop scrolling
|
|
671
|
+
Current scroll position: 7972
|
|
672
|
+
[virtual-scroller] The user is scrolling: perform a re-layout when they stop scrolling
|
|
673
|
+
Current scroll position: 7957
|
|
674
|
+
[virtual-scroller] The user has scrolled far enough: perform a re-layout
|
|
675
|
+
[virtual-scroller] ~ Update Layout (on scroll) ~
|
|
676
|
+
...
|
|
677
|
+
[virtual-scroller] ~ Rendered ~
|
|
678
|
+
[virtual-scroller] State ...
|
|
679
|
+
[virtual-scroller] ~ Measure item heights ~
|
|
680
|
+
[virtual-scroller] Item index 27 height 232
|
|
681
|
+
[virtual-scroller] Item index 28 height 178
|
|
682
|
+
[virtual-scroller] ~ Clean up "before resize" item heights and correct scroll position ~
|
|
683
|
+
[virtual-scroller] For item indexes from 27 to 28 — drop "before resize" heights [340, 259]
|
|
684
|
+
[virtual-scroller] Correct scroll position by -189
|
|
685
|
+
Scroll to position: 7768
|
|
686
|
+
[virtual-scroller] Set state ...
|
|
687
|
+
[virtual-scroller] ~ Rendered ~
|
|
688
|
+
[virtual-scroller] State ...
|
|
689
|
+
Current scroll position: 7944
|
|
690
|
+
[virtual-scroller] The user is scrolling: perform a re-layout when they stop scrolling
|
|
691
|
+
Current scroll position: 7933
|
|
692
|
+
[virtual-scroller] The user is scrolling: perform a re-layout when they stop scrolling
|
|
693
|
+
Current scroll position: 7924
|
|
694
|
+
[virtual-scroller] The user is scrolling: perform a re-layout when they stop scrolling
|
|
695
|
+
...
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
```js
|
|
699
|
+
var listener = () => {
|
|
700
|
+
console.log('Current scroll position:', window.pageYOffset)
|
|
701
|
+
}
|
|
702
|
+
document.addEventListener('scroll', listener)
|
|
703
|
+
var unlisten = () => document.removeEventListener('scroll', listener)
|
|
704
|
+
|
|
705
|
+
// Also add `console.log('Scroll to position:', scrollY)` in
|
|
706
|
+
// `scrollToY()` method in `./source/DOM/ScrollableContainer.js`.
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
Also, pressing the "Home" key wouldn't scroll up past the "prerender margin", which is equal to the screen height by default. The reason is the same: applying scroll position correction while the "Home" key is pressed cancels the effect of the "Home" button.
|
|
710
|
+
|
|
711
|
+
A possible workaround for those bugs would be postponing scroll position correction until the user stops scrolling, and instead change `margin-bottom` of some "spacer" element at the top of the list (or maybe even before the list). When the user stops scrolling, the scroll position would get corrected by the value of `margin-bottom` of that "spacer" element, after which the `margin-bottom` value on that "spacer" element would be reset. But this type of a workaround would only work in a DOM environment because it requires the support of "negative" margin.
|
|
712
|
+
|
|
713
|
+
For now, I don't see it as a bug that would be worth fixing. The user could just refresh the page, or not scroll up at all because they've already seen that content.
|
|
714
|
+
</details>
|
|
715
|
+
|
|
716
|
+
#####
|
|
717
|
+
|
|
718
|
+
The "before resize" snapshot is stored in `VirtualScroller` state in `beforeResize` object: `itemHeights: number[]`, `verticalSpacing: number`, `columnsCount: number`.
|
|
719
|
+
|
|
644
720
|
### `<tbody/>`
|
|
645
721
|
|
|
646
722
|
Due to the [inherent limitations](https://gitlab.com/catamphetamine/virtual-scroller/-/issues/1) of the `<tbody/>` HTML tag, when used as a container for the list items, a workaround involving CSS variables has to be used, and CSS variables aren't supported in Internet Explorer, so using a `<tbody/>` as a list items container won't work in Internet Explorer: in such case, `VirtualScroller` renders in "bypass" mode (render all items).
|
|
@@ -708,6 +784,36 @@ One can use any npm CDN service, e.g. [unpkg.com](https://unpkg.com) or [jsdeliv
|
|
|
708
784
|
* Currently React `<VirtualScroller/>` passes `onHeightChange()` property and provides `.renderItem(i)` instance method. Both these features could be replaced with doing it internally in `VirtualScroller`'s `.setItems(newItems)` method: it could detect the items that have changed (`prevItems[i] !== newItems[i]`) and recalculate heights for such items, while the changed `item` properties would also cause the relevant React elements to be rerendered.
|
|
709
785
|
-->
|
|
710
786
|
|
|
787
|
+
## TypeScript
|
|
788
|
+
|
|
789
|
+
This library comes with TypeScript "typings". If you happen to find any bugs in those, create an issue.
|
|
790
|
+
|
|
791
|
+
## Possible enhancements
|
|
792
|
+
|
|
793
|
+
### Alternative approach in DOM rendering
|
|
794
|
+
|
|
795
|
+
This library's `DOM` and `React` component implementations use `padding-top` and `padding-bottom` on the items container to emulate the items that're not currently visible. In DOM environment, this approach comes with a slight drawback: the web browser has to perform a "reflow" every time shown item indexes change as a result of the user scrolling the page.
|
|
796
|
+
|
|
797
|
+
Twitter seems to use a slightly different approach: they set `position: relative` and `min-height: <all-items-height>` on the items container, and then `position: absolute`, `width: 100%` and `transform: translateY(<item-top-offset>)` on every items. Since `transform`s are only applied at the "compositing" stage of a web browser's rendering cycle, there's no need to recalculate anything, and so scrolling the page comes without any possible performance penalties at all.
|
|
798
|
+
|
|
799
|
+
<details>
|
|
800
|
+
<summary>My thoughts on moving from <code>padding</code>s to <code>transform</code>s</summary>
|
|
801
|
+
|
|
802
|
+
######
|
|
803
|
+
|
|
804
|
+
I've fantasised a bit about moving to `transforms` in this library's `DOM` and `React` component implementations, and it seems to involve a bit more than it initially seems:
|
|
805
|
+
|
|
806
|
+
* Item heights aren't known before the items have been rendered, so it'll have to re-render twice rather than once as the user scrolls: first time to measure the newly-shown items' heights and second time to apply the calculated Y positions of those items.
|
|
807
|
+
|
|
808
|
+
* A bit more complexity is added when one recalls that this library supports multi-column layout: now not only `y` positions but also `x` positions of every item would have to be calculated, and not only vertical spacing but also horizontal spacing between the items in a row.
|
|
809
|
+
|
|
810
|
+
* The `state` would have to include a new property — `itemPositions` — that would include an `x` and `y` position for every item.
|
|
811
|
+
|
|
812
|
+
* Using `x`/`y` positions for every item would mean that the `x`/`y` position of every item would no longer be dynamically calculated by a web browser (in `auto` mode) and instead would have to be pre-calculated by the library meaning that everything would have to be constantly re-calculated and re-rendered as the user resizes the window, not just on window resize end like it currently does. For example, if the user starts shrinking window width, the items' heights would start increasing due to content overflow, which, without constant re-calculation and re-rendering, would result in items being rendered on top of each other. So the fix for that would be re-calculating and re-rendering stuff immediately on every window `resize` event as the user drags the handle rather than waiting for the user to let go of that handle and stop resizing the window, which would obviously come with some performance penalties but maybe a modern device can handle such things without breaking a sweat.
|
|
813
|
+
|
|
814
|
+
The points listed above aren't something difficult to implement, it's just that I don't want to do it unless there're any real observed performance issues related to the "reflows" during scrolling. "If it works, no need to change it".
|
|
815
|
+
</details>
|
|
816
|
+
|
|
711
817
|
## GitHub
|
|
712
818
|
|
|
713
819
|
On March 9th, 2020, GitHub, Inc. silently [banned](https://medium.com/@catamphetamine/how-github-blocked-me-and-all-my-libraries-c32c61f061d3) my account (erasing all my repos, issues and comments) without any notice or explanation. Because of that, all source codes had to be promptly moved to GitLab. The [GitHub repo](https://github.com/catamphetamine/virtual-scroller) is now only used as a backup (you can star the repo there too), and the primary repo is now the [GitLab one](https://gitlab.com/catamphetamine/virtual-scroller). Issues can be reported in any repo.
|
package/babel.config.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
"presets": [
|
|
3
|
+
"@babel/preset-env",
|
|
4
|
+
"@babel/preset-react"
|
|
5
|
+
],
|
|
6
|
+
|
|
7
|
+
"plugins": [
|
|
8
|
+
["@babel/plugin-transform-for-of", { loose: true }],
|
|
9
|
+
"@babel/plugin-proposal-object-rest-spread",
|
|
10
|
+
"@babel/plugin-proposal-class-properties"
|
|
11
|
+
],
|
|
12
|
+
|
|
13
|
+
"env": {
|
|
14
|
+
"es6": {
|
|
15
|
+
"presets": [
|
|
16
|
+
["@babel/preset-env", { modules: false }]
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"test": {
|
|
20
|
+
"plugins": [
|
|
21
|
+
"babel-plugin-istanbul"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
package/babel.js
ADDED
package/bundle/index-bypass.html
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
<body>
|
|
34
34
|
<!-- http://tholman.com/github-corners/ -->
|
|
35
|
-
<a title="Go to GitHub repo" href="https://
|
|
35
|
+
<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>
|
|
36
36
|
|
|
37
37
|
<div id="root"></div>
|
|
38
38
|
|
package/bundle/index-dom.html
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
<body>
|
|
21
21
|
<!-- http://tholman.com/github-corners/ -->
|
|
22
|
-
<a title="Go to GitHub repo" href="https://
|
|
22
|
+
<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>
|
|
23
23
|
|
|
24
24
|
<section class="container">
|
|
25
25
|
<h1>
|
package/bundle/index-grid.html
CHANGED
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
|
|
46
46
|
<body>
|
|
47
47
|
<!-- http://tholman.com/github-corners/ -->
|
|
48
|
-
<a title="Go to GitHub repo" href="https://
|
|
48
|
+
<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>
|
|
49
49
|
|
|
50
50
|
<div id="root"></div>
|
|
51
51
|
|
|
@@ -61,7 +61,6 @@
|
|
|
61
61
|
const COLUMNS_COUNT = 3
|
|
62
62
|
|
|
63
63
|
function getColumnsCount(container) {
|
|
64
|
-
console.log('Container width', container.getWidth())
|
|
65
64
|
if (container.getWidth() > 1280) {
|
|
66
65
|
return COLUMNS_COUNT
|
|
67
66
|
}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
<body>
|
|
24
24
|
<!-- http://tholman.com/github-corners/ -->
|
|
25
|
-
<a title="Go to GitHub repo" href="https://
|
|
25
|
+
<a title="Go to GitHub repo" href="https://gitlab.com/catamphetamine/virtual-scroller" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
|
|
26
26
|
|
|
27
27
|
<div id="root"></div>
|
|
28
28
|
|
package/bundle/index-tbody.html
CHANGED