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
|
@@ -37,7 +37,16 @@ export default function createStateHelpers({
|
|
|
37
37
|
|
|
38
38
|
this.getInitialItemState = getInitialItemState
|
|
39
39
|
|
|
40
|
-
this._setItemState = (
|
|
40
|
+
this._setItemState = (itemOrIndex, newItemState) => {
|
|
41
|
+
// Item index.
|
|
42
|
+
const i = this._getItemIndexByItemOrIndex(itemOrIndex)
|
|
43
|
+
|
|
44
|
+
// If the item wasn't found, the error was already reported,
|
|
45
|
+
// so just return some "sensible" default value.
|
|
46
|
+
if (i === undefined) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
41
50
|
if (isDebug()) {
|
|
42
51
|
log('~ Item state changed ~')
|
|
43
52
|
log('Item index', i)
|
|
@@ -284,16 +293,6 @@ export default function createStateHelpers({
|
|
|
284
293
|
function getInitialLayoutState(items, { beforeStart }) {
|
|
285
294
|
const itemsCount = items.length
|
|
286
295
|
|
|
287
|
-
const getColumnsCount = () => this.getActualColumnsCount()
|
|
288
|
-
|
|
289
|
-
const columnsCount = beforeStart
|
|
290
|
-
? this.layout.getInitialLayoutValueWithFallback(
|
|
291
|
-
'columnsCount',
|
|
292
|
-
getColumnsCount,
|
|
293
|
-
1
|
|
294
|
-
)
|
|
295
|
-
: getColumnsCount()
|
|
296
|
-
|
|
297
296
|
const {
|
|
298
297
|
firstShownItemIndex,
|
|
299
298
|
lastShownItemIndex,
|
|
@@ -1,20 +1,46 @@
|
|
|
1
1
|
import log from './utility/debug.js'
|
|
2
2
|
import getVerticalSpacing from './getVerticalSpacing.js'
|
|
3
|
+
import { DEFAULT_INTER_ITEM_VERTICAL_SPACING } from './Layout.defaults.js'
|
|
3
4
|
|
|
4
|
-
export default function createVerticalSpacingHelpers(
|
|
5
|
+
export default function createVerticalSpacingHelpers({
|
|
6
|
+
getEstimatedInterItemVerticalSpacing
|
|
7
|
+
}) {
|
|
5
8
|
// Bind to `this` in order to prevent bugs when this function is passed by reference
|
|
6
9
|
// and then called with its `this` being unintentionally `window` resulting in
|
|
7
10
|
// the `if` condition being "falsy".
|
|
8
11
|
this.getVerticalSpacing = () => {
|
|
9
|
-
|
|
12
|
+
const { verticalSpacing } = this
|
|
13
|
+
if (typeof verticalSpacing === 'number') {
|
|
14
|
+
return verticalSpacing
|
|
15
|
+
}
|
|
16
|
+
return this.getEstimatedInterItemVerticalSpacing()
|
|
10
17
|
}
|
|
11
18
|
|
|
12
19
|
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
20
|
const { beforeResize } = this.getState()
|
|
17
|
-
|
|
21
|
+
if (beforeResize) {
|
|
22
|
+
const { verticalSpacing } = beforeResize
|
|
23
|
+
// `beforeResize.verticalSpacing` can be `undefined`.
|
|
24
|
+
// For example, if `this.updateState({ verticalSpacing })` call hasn't been applied
|
|
25
|
+
// before the resize happened (in case of an "asynchronous" state update).
|
|
26
|
+
if (typeof verticalSpacing === 'number') {
|
|
27
|
+
return verticalSpacing
|
|
28
|
+
}
|
|
29
|
+
return this.getEstimatedInterItemVerticalSpacing()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.getEstimatedInterItemVerticalSpacing = () => {
|
|
34
|
+
if (getEstimatedInterItemVerticalSpacing) {
|
|
35
|
+
const estimatedVerticalSpacing = getEstimatedInterItemVerticalSpacing()
|
|
36
|
+
if (typeof estimatedVerticalSpacing === 'number') {
|
|
37
|
+
return estimatedVerticalSpacing
|
|
38
|
+
}
|
|
39
|
+
throw new Error('[virtual-scroller] `getEstimatedInterItemVerticalSpacing()` must return a number')
|
|
40
|
+
}
|
|
41
|
+
// `DEFAULT_INTER_ITEM_VERTICAL_SPACING` will be used in server-side render
|
|
42
|
+
// unless `getEstimatedInterItemVerticalSpacing()` parameter is specified.
|
|
43
|
+
return DEFAULT_INTER_ITEM_VERTICAL_SPACING
|
|
18
44
|
}
|
|
19
45
|
|
|
20
46
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useMemo, useLayoutEffect } from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
|
|
4
4
|
import useState from './useState.js'
|
|
@@ -10,6 +10,8 @@ import useSetItemState from './useSetItemState.js'
|
|
|
10
10
|
import useOnItemHeightDidChange from './useOnItemHeightDidChange.js'
|
|
11
11
|
import useSetNewItemsOnItemsPropertyChange from './useSetNewItemsOnItemsPropertyChange.js'
|
|
12
12
|
import useUpdateItemKeysOnItemsChange from './useUpdateItemKeysOnItemsChange.js'
|
|
13
|
+
import useValidateTableBodyItemsContainer from './useValidateTableBodyItemsContainer.js'
|
|
14
|
+
import useForwardedRef from './useForwardedRef.js'
|
|
13
15
|
import useClassName from './useClassName.js'
|
|
14
16
|
import useStyle from './useStyle.js'
|
|
15
17
|
|
|
@@ -27,19 +29,32 @@ import { warn } from '../utility/debug.js'
|
|
|
27
29
|
// * The React component re-renders itself the second time.
|
|
28
30
|
|
|
29
31
|
function VirtualScroller({
|
|
30
|
-
|
|
32
|
+
// The following are `<VirtualScroller/>` properties.
|
|
33
|
+
//
|
|
34
|
+
// `as` property is deprecated, use `itemsContainerComponent` property instead.
|
|
35
|
+
as,
|
|
31
36
|
items: itemsProperty,
|
|
32
|
-
itemComponent:
|
|
37
|
+
itemComponent: ItemComponent,
|
|
33
38
|
itemComponentProps,
|
|
39
|
+
itemsContainerComponent: ItemsContainerComponent,
|
|
40
|
+
itemsContainerComponentProps,
|
|
41
|
+
itemsContainerRef,
|
|
34
42
|
// `estimatedItemHeight` property name is deprecated,
|
|
35
43
|
// use `getEstimatedItemHeight` property instead.
|
|
36
44
|
estimatedItemHeight,
|
|
37
45
|
getEstimatedItemHeight,
|
|
38
46
|
getEstimatedVisibleItemRowsCount,
|
|
39
|
-
|
|
47
|
+
getEstimatedInterItemVerticalSpacing,
|
|
48
|
+
onMount,
|
|
40
49
|
// `tbody` property is deprecated.
|
|
41
50
|
// Pass `as: "tbody"` property instead.
|
|
42
51
|
tbody,
|
|
52
|
+
readyToStart,
|
|
53
|
+
className: classNameProperty,
|
|
54
|
+
|
|
55
|
+
// The following are the "core" component options.
|
|
56
|
+
//
|
|
57
|
+
bypass,
|
|
43
58
|
// `preserveScrollPosition` property name is deprecated,
|
|
44
59
|
// use `preserveScrollPositionOnPrependItems` property instead.
|
|
45
60
|
preserveScrollPosition,
|
|
@@ -51,19 +66,26 @@ function VirtualScroller({
|
|
|
51
66
|
getScrollableContainer,
|
|
52
67
|
getColumnsCount,
|
|
53
68
|
getItemId,
|
|
54
|
-
className,
|
|
55
|
-
readyToStart,
|
|
56
|
-
onMount,
|
|
57
69
|
// `onItemFirstRender(i)` is deprecated, use `onItemInitialRender(item)` instead.
|
|
58
70
|
onItemFirstRender,
|
|
59
71
|
onItemInitialRender,
|
|
60
72
|
initialScrollPosition,
|
|
61
73
|
onScrollPositionChange,
|
|
62
|
-
onStateChange,
|
|
63
74
|
initialState,
|
|
64
75
|
getInitialItemState,
|
|
76
|
+
onStateChange,
|
|
77
|
+
|
|
78
|
+
// "Rest" properties that will be passed through to the `itemsContainerComponent`.
|
|
65
79
|
...rest
|
|
66
80
|
}, ref) {
|
|
81
|
+
// Previously, `as` property was being used instead of `itemsContainerComponent`,
|
|
82
|
+
// and the default `as` property value was a generic `<div/>`.
|
|
83
|
+
// Starting from version `1.14.1`, it is recommended to explicitly specify the `itemsContainerComponent`.
|
|
84
|
+
// The default `"div"` fallback value is just a legacy compatibility relic, and so is the `as` property.
|
|
85
|
+
if (!ItemsContainerComponent) {
|
|
86
|
+
ItemsContainerComponent = as || 'div'
|
|
87
|
+
}
|
|
88
|
+
|
|
67
89
|
// It turns out that since May 2022, `useVirtualScroller()` hook completely ignored the `tbody` property.
|
|
68
90
|
// Instead, it always derived `tbody` property value from `as` property value by comparing it to `"tbody"` string.
|
|
69
91
|
// As a result, it seemed like the explicit passing of `tbody` property didn't really work as intended.
|
|
@@ -71,11 +93,22 @@ function VirtualScroller({
|
|
|
71
93
|
// without a developer having to manually specify it. So the `tbody` property was deprecated.
|
|
72
94
|
// It still exists though for backwards compatibility with the older versions of the package.
|
|
73
95
|
if (tbody === undefined) {
|
|
74
|
-
tbody
|
|
96
|
+
// `tbody` should be somehow detected before any DOM Elements have been mounted.
|
|
97
|
+
// This is because during Server-Side Render there's no DOM Elements tree at all.
|
|
98
|
+
// And server-sider render result is required to be exactly the same as client-side render result.
|
|
99
|
+
// This means that `tbody` detection for the purposes of getting the initial
|
|
100
|
+
// `className` or `style` property values must not rely on any DOM Elements at all,
|
|
101
|
+
// and should use some other means such as explicitly passing a `tbody: true` property
|
|
102
|
+
// (as it used to be in the past) or detecting `<tbody/>` tag usage from the
|
|
103
|
+
// `itemsContainerCompoent` property value.
|
|
104
|
+
tbody = ItemsContainerComponent === 'tbody'
|
|
75
105
|
}
|
|
76
106
|
|
|
77
107
|
// List items "container" DOM Element reference.
|
|
78
|
-
const
|
|
108
|
+
const {
|
|
109
|
+
setRef: setItemsContainerRef,
|
|
110
|
+
internalRef: itemsContainer
|
|
111
|
+
} = useForwardedRef(itemsContainerRef)
|
|
79
112
|
|
|
80
113
|
// Create a `VirtualScroller` instance.
|
|
81
114
|
const virtualScroller = useVirtualScroller({
|
|
@@ -85,6 +118,7 @@ function VirtualScroller({
|
|
|
85
118
|
estimatedItemHeight,
|
|
86
119
|
getEstimatedItemHeight,
|
|
87
120
|
getEstimatedVisibleItemRowsCount,
|
|
121
|
+
getEstimatedInterItemVerticalSpacing,
|
|
88
122
|
bypass,
|
|
89
123
|
// bypassBatchSize,
|
|
90
124
|
onItemInitialRender,
|
|
@@ -99,12 +133,11 @@ function VirtualScroller({
|
|
|
99
133
|
getScrollableContainer,
|
|
100
134
|
getColumnsCount,
|
|
101
135
|
getItemId,
|
|
102
|
-
AsComponent,
|
|
103
136
|
initialState,
|
|
104
137
|
getInitialItemState,
|
|
105
138
|
onStateChange
|
|
106
139
|
}, {
|
|
107
|
-
|
|
140
|
+
itemsContainer
|
|
108
141
|
})
|
|
109
142
|
|
|
110
143
|
// Only compute the initial state once.
|
|
@@ -139,6 +172,7 @@ function VirtualScroller({
|
|
|
139
172
|
// "reuse" `itemComponent`s in cases when `items` are changed.
|
|
140
173
|
const {
|
|
141
174
|
getItemKey,
|
|
175
|
+
onItemKeysReset,
|
|
142
176
|
usesAutogeneratedItemKeys,
|
|
143
177
|
updateItemKeysForNewItems
|
|
144
178
|
} = useItemKeys({
|
|
@@ -148,14 +182,16 @@ function VirtualScroller({
|
|
|
148
182
|
// Cache per-item `setItemState` functions' "references"
|
|
149
183
|
// so that item components don't get re-rendered needlessly.
|
|
150
184
|
const getSetItemState = useSetItemState({
|
|
151
|
-
|
|
185
|
+
getItemKey,
|
|
186
|
+
onItemKeysReset,
|
|
152
187
|
virtualScroller
|
|
153
188
|
})
|
|
154
189
|
|
|
155
190
|
// Cache per-item `onItemHeightDidChange` functions' "references"
|
|
156
191
|
// so that item components don't get re-rendered needlessly.
|
|
157
192
|
const getOnItemHeightDidChange = useOnItemHeightDidChange({
|
|
158
|
-
|
|
193
|
+
getItemKey,
|
|
194
|
+
onItemKeysReset,
|
|
159
195
|
virtualScroller
|
|
160
196
|
})
|
|
161
197
|
|
|
@@ -190,6 +226,14 @@ function VirtualScroller({
|
|
|
190
226
|
}
|
|
191
227
|
}, [])
|
|
192
228
|
|
|
229
|
+
// A developer might "forget" to pass `itemsContainerComponent="tbody"` property
|
|
230
|
+
// when using a `<tbody/>` as a container for list items.
|
|
231
|
+
// This hook validates that the developer didn't "forget" to do that in such case.
|
|
232
|
+
useValidateTableBodyItemsContainer({
|
|
233
|
+
virtualScroller,
|
|
234
|
+
tbody
|
|
235
|
+
})
|
|
236
|
+
|
|
193
237
|
// `willRender()` function is no longer used.
|
|
194
238
|
//
|
|
195
239
|
// // `getSnapshotBeforeUpdate()` is called right before `componentDidUpdate()`.
|
|
@@ -205,11 +249,15 @@ function VirtualScroller({
|
|
|
205
249
|
// return null
|
|
206
250
|
// }
|
|
207
251
|
|
|
208
|
-
|
|
252
|
+
const classNamePassThrough = classNameProperty || itemsContainerComponentProps && itemsContainerComponentProps.className
|
|
253
|
+
|
|
254
|
+
const className = useClassName(classNamePassThrough, {
|
|
209
255
|
tbody
|
|
210
256
|
})
|
|
211
257
|
|
|
212
|
-
const
|
|
258
|
+
const stylePassThrough = itemsContainerComponentProps && itemsContainerComponentProps.style
|
|
259
|
+
|
|
260
|
+
const style = useStyle(stylePassThrough, {
|
|
213
261
|
tbody,
|
|
214
262
|
state: stateToRender
|
|
215
263
|
})
|
|
@@ -222,34 +270,39 @@ function VirtualScroller({
|
|
|
222
270
|
} = stateToRender
|
|
223
271
|
|
|
224
272
|
return (
|
|
225
|
-
<
|
|
273
|
+
<ItemsContainerComponent
|
|
274
|
+
{...itemsContainerComponentProps}
|
|
226
275
|
{...rest}
|
|
227
|
-
ref={
|
|
276
|
+
ref={setItemsContainerRef}
|
|
228
277
|
className={className}
|
|
229
278
|
style={style}>
|
|
230
279
|
{currentItems.map((item, i) => {
|
|
231
280
|
if (i >= firstShownItemIndex && i <= lastShownItemIndex) {
|
|
232
|
-
// * Passing `item` as `children` property is legacy and is deprecated.
|
|
281
|
+
// * Passing the `item` as `children` property is legacy and is deprecated.
|
|
233
282
|
// If version `2.x` is published in some hypothetical future,
|
|
234
|
-
// the `item`
|
|
235
|
-
// `{...itemComponentProps}`.
|
|
283
|
+
// the `item` property should be moved below `{...itemComponentProps}`.
|
|
236
284
|
//
|
|
237
|
-
// * Passing `itemIndex` property is legacy and is deprecated
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
//
|
|
285
|
+
// * Passing `itemIndex` property is legacy and is deprecated
|
|
286
|
+
// and could be removed in some future.
|
|
287
|
+
// The rationale for deprecation is that the `items` property
|
|
288
|
+
// is not constant and could change, in which case the `itemIndex` value
|
|
289
|
+
// would be of no use because the application wouldn't know
|
|
290
|
+
// which exact `items` it corresponds to at any given moment in time.
|
|
291
|
+
// Having just the `itemIndex` and no actual `item` is therefore considered useless.
|
|
292
|
+
// Instead, a developer could simply use `getItemKey(item)` function.
|
|
242
293
|
//
|
|
243
|
-
// *
|
|
294
|
+
// * `onStateChange` property is passed here for legacy reasons.
|
|
244
295
|
// The new property name is `setState`.
|
|
245
|
-
// The old property name `onStateChange` is deprecated
|
|
296
|
+
// The old property name `onStateChange` is deprecated
|
|
297
|
+
// and could be removed in some future.
|
|
246
298
|
//
|
|
247
|
-
// *
|
|
299
|
+
// * `onHeightChange` property is passed here for legacy reasons.
|
|
248
300
|
// The new property name is `onHeightDidChange`.
|
|
249
|
-
// The old property name `onHeightChange` is deprecated
|
|
301
|
+
// The old property name `onHeightChange` is deprecated
|
|
302
|
+
// and could be removed in some future.
|
|
250
303
|
//
|
|
251
304
|
return (
|
|
252
|
-
<
|
|
305
|
+
<ItemComponent
|
|
253
306
|
item={item}
|
|
254
307
|
itemIndex={i}
|
|
255
308
|
{...itemComponentProps}
|
|
@@ -260,12 +313,12 @@ function VirtualScroller({
|
|
|
260
313
|
onHeightChange={getOnItemHeightDidChange(i)}
|
|
261
314
|
onHeightDidChange={getOnItemHeightDidChange(i)}>
|
|
262
315
|
{item}
|
|
263
|
-
</
|
|
316
|
+
</ItemComponent>
|
|
264
317
|
)
|
|
265
318
|
}
|
|
266
319
|
return null
|
|
267
320
|
})}
|
|
268
|
-
</
|
|
321
|
+
</ItemsContainerComponent>
|
|
269
322
|
)
|
|
270
323
|
}
|
|
271
324
|
|
|
@@ -282,15 +335,25 @@ const elementType = PropTypes.elementType || PropTypes.oneOfType([
|
|
|
282
335
|
])
|
|
283
336
|
|
|
284
337
|
VirtualScroller.propTypes = {
|
|
338
|
+
// `as` property is deprecated, use `itemsContainerComponent` property instead.
|
|
285
339
|
as: elementType,
|
|
286
340
|
items: PropTypes.arrayOf(PropTypes.any).isRequired,
|
|
287
341
|
itemComponent: elementType.isRequired,
|
|
288
342
|
itemComponentProps: PropTypes.object,
|
|
343
|
+
// `itemsContainerComponent` property is not required just for legacy compatibility reasons.
|
|
344
|
+
// Any new applications should explicitly specify it.
|
|
345
|
+
itemsContainerComponent: elementType,
|
|
346
|
+
itemsContainerComponentProps: PropTypes.object,
|
|
347
|
+
itemsContainerRef: PropTypes.oneOfType([
|
|
348
|
+
PropTypes.func,
|
|
349
|
+
PropTypes.shape({ current: PropTypes.object })
|
|
350
|
+
]),
|
|
289
351
|
// `estimatedItemHeight` property name is deprecated,
|
|
290
352
|
// use `getEstimatedItemHeight` property instead.
|
|
291
353
|
estimatedItemHeight: PropTypes.number,
|
|
292
354
|
getEstimatedItemHeight: PropTypes.func,
|
|
293
355
|
getEstimatedVisibleItemRowsCount: PropTypes.func,
|
|
356
|
+
getEstimatedInterItemVerticalSpacing: PropTypes.func,
|
|
294
357
|
bypass: PropTypes.bool,
|
|
295
358
|
// bypassBatchSize: PropTypes.number,
|
|
296
359
|
// `tbody` property is deprecated.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CLASS_NAME_FOR_TBODY_WORKAROUND } from '../DOM/tbody.js'
|
|
2
2
|
|
|
3
3
|
export default function useClassName(className, { tbody }) {
|
|
4
4
|
// For `<tbody/>`, a workaround is used which uses CSS variables
|
|
@@ -6,9 +6,9 @@ export default function useClassName(className, { tbody }) {
|
|
|
6
6
|
// See `addTbodyStyles()` function in `../DOM/tbody.js` for more details.
|
|
7
7
|
if (tbody) {
|
|
8
8
|
if (className) {
|
|
9
|
-
return className + ' ' +
|
|
9
|
+
return className + ' ' + CLASS_NAME_FOR_TBODY_WORKAROUND
|
|
10
10
|
}
|
|
11
|
-
return
|
|
11
|
+
return CLASS_NAME_FOR_TBODY_WORKAROUND
|
|
12
12
|
}
|
|
13
13
|
return className
|
|
14
14
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// import type { MutableRefObject } from 'react'
|
|
2
|
+
import { useCallback, useRef } from 'react'
|
|
3
|
+
|
|
4
|
+
// When a React component receives a `ref` which it is supposed to "forward"
|
|
5
|
+
// and when it would like to also read that `ref` in its internal implementation,
|
|
6
|
+
// this `useForwardedRef()` hook could be used to get read access to such "forwarded" ref
|
|
7
|
+
// inside the component's internal implementation.
|
|
8
|
+
//
|
|
9
|
+
// ```js
|
|
10
|
+
// const FormWithAutoFocus = forwardRef((props, ref) => {
|
|
11
|
+
// const { setRef, internalRef } = useForwardedRef<RefValueType>(ref)
|
|
12
|
+
//
|
|
13
|
+
// useEffect(() => {
|
|
14
|
+
// internalRef.current.focus()
|
|
15
|
+
// }, [])
|
|
16
|
+
//
|
|
17
|
+
// return (
|
|
18
|
+
// <Form ref={setRef} {...props}/>
|
|
19
|
+
// )
|
|
20
|
+
// })
|
|
21
|
+
// ```
|
|
22
|
+
//
|
|
23
|
+
// export default function useForwardedRef<T extends MutableRefObject<any>>(ref) {
|
|
24
|
+
export default function useForwardedRef(ref) {
|
|
25
|
+
const internalRef = useRef() // as T
|
|
26
|
+
|
|
27
|
+
const setRef = useCallback((instance) => {
|
|
28
|
+
internalRef.current = instance
|
|
29
|
+
if (ref) {
|
|
30
|
+
if (typeof ref === 'function') {
|
|
31
|
+
ref(instance)
|
|
32
|
+
} else {
|
|
33
|
+
ref.current = instance
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}, [ref])
|
|
37
|
+
|
|
38
|
+
return { setRef, internalRef }
|
|
39
|
+
}
|
|
@@ -8,17 +8,25 @@ export default function useInstanceMethods(ref, {
|
|
|
8
8
|
}) {
|
|
9
9
|
useImperativeHandle(ref, () => ({
|
|
10
10
|
// This is a proxy for `VirtualScroller`'s `.updateLayout` instance method.
|
|
11
|
-
updateLayout: () =>
|
|
11
|
+
updateLayout: () => {
|
|
12
|
+
virtualScroller.updateLayout()
|
|
13
|
+
},
|
|
12
14
|
|
|
13
15
|
// (deprecated)
|
|
14
16
|
// `.layout()` method name is deprecated, use `.updateLayout()` instead.
|
|
15
|
-
layout: () =>
|
|
17
|
+
layout: () => {
|
|
18
|
+
virtualScroller.updateLayout()
|
|
19
|
+
},
|
|
16
20
|
|
|
17
21
|
// (deprecated)
|
|
18
|
-
updateItem: (i) =>
|
|
22
|
+
updateItem: (i) => {
|
|
23
|
+
reportError(`[virtual-scroller] ".updateItem(i)" method of React <VirtualScroller/> has been removed`)
|
|
24
|
+
},
|
|
19
25
|
|
|
20
26
|
// (deprecated)
|
|
21
|
-
renderItem: (i) =>
|
|
27
|
+
renderItem: (i) => {
|
|
28
|
+
reportError(`[virtual-scroller] ".renderItem(i)" method of React <VirtualScroller/> has been removed`)
|
|
29
|
+
}
|
|
22
30
|
}), [
|
|
23
31
|
virtualScroller
|
|
24
32
|
])
|
|
@@ -3,15 +3,31 @@ import log from '../utility/debug.js'
|
|
|
3
3
|
import { useRef, useMemo, useCallback } from 'react'
|
|
4
4
|
|
|
5
5
|
export default function useItemKeys({ getItemId }) {
|
|
6
|
+
// These "listeners" will be called in case the auto-generated item keys are reset
|
|
7
|
+
// due to the prefix counter number reaching its maximum allowed value.
|
|
8
|
+
const itemKeysResetEventListeners = useMemo(() => {
|
|
9
|
+
return []
|
|
10
|
+
}, [])
|
|
11
|
+
|
|
12
|
+
// Adds an "on item keys reset" listener.
|
|
13
|
+
const onItemKeysReset = useCallback((listener) => {
|
|
14
|
+
itemKeysResetEventListeners.push(listener)
|
|
15
|
+
}, [])
|
|
16
|
+
|
|
6
17
|
// List items are rendered with `key`s so that React doesn't
|
|
7
18
|
// "reuse" `itemComponent`s in cases when `items` are changed.
|
|
8
19
|
const itemKeyPrefix = useRef()
|
|
9
20
|
|
|
10
|
-
//
|
|
21
|
+
// When no `getItemId()` function is specified, it creates an item key
|
|
22
|
+
// from an auto-generated unique prefix and the item's index in the `items` array.
|
|
23
|
+
// This way, when the `items` change, their keys are also changed.
|
|
11
24
|
const generateItemKeyPrefix = useMemo(() => {
|
|
12
25
|
let counter = 0
|
|
13
26
|
function getNextCounter() {
|
|
14
27
|
if (counter === Number.MAX_SAFE_INTEGER) {
|
|
28
|
+
for (const listener of itemKeysResetEventListeners) {
|
|
29
|
+
listener()
|
|
30
|
+
}
|
|
15
31
|
counter = 0
|
|
16
32
|
}
|
|
17
33
|
counter++
|
|
@@ -21,11 +37,12 @@ export default function useItemKeys({ getItemId }) {
|
|
|
21
37
|
itemKeyPrefix.current = String(getNextCounter())
|
|
22
38
|
}
|
|
23
39
|
}, [
|
|
24
|
-
itemKeyPrefix
|
|
40
|
+
itemKeyPrefix,
|
|
41
|
+
itemKeysResetEventListeners
|
|
25
42
|
])
|
|
26
43
|
|
|
27
44
|
useMemo(() => {
|
|
28
|
-
// Generate an initial unique `key` prefix for list
|
|
45
|
+
// Generate an initial unique `key` prefix for list items.
|
|
29
46
|
generateItemKeyPrefix()
|
|
30
47
|
}, [])
|
|
31
48
|
|
|
@@ -50,7 +67,7 @@ export default function useItemKeys({ getItemId }) {
|
|
|
50
67
|
*/
|
|
51
68
|
const getItemKey = useCallback((item, i) => {
|
|
52
69
|
if (getItemId) {
|
|
53
|
-
return getItemId(item)
|
|
70
|
+
return String(getItemId(item))
|
|
54
71
|
}
|
|
55
72
|
return `${itemKeyPrefix.current}:${i}`
|
|
56
73
|
}, [
|
|
@@ -60,6 +77,7 @@ export default function useItemKeys({ getItemId }) {
|
|
|
60
77
|
|
|
61
78
|
return {
|
|
62
79
|
getItemKey,
|
|
80
|
+
onItemKeysReset,
|
|
63
81
|
usesAutogeneratedItemKeys,
|
|
64
82
|
updateItemKeysForNewItems: generateItemKeyPrefixIfNotUsingItemIds
|
|
65
83
|
}
|
|
@@ -1,28 +1,47 @@
|
|
|
1
1
|
import { useMemo, useRef, useCallback } from 'react'
|
|
2
2
|
|
|
3
3
|
export default function useOnItemHeightDidChange({
|
|
4
|
-
|
|
4
|
+
getItemKey,
|
|
5
|
+
onItemKeysReset,
|
|
5
6
|
virtualScroller
|
|
6
7
|
}) {
|
|
7
|
-
// Only
|
|
8
|
-
const
|
|
9
|
-
return
|
|
8
|
+
// Only create the initial cache once.
|
|
9
|
+
const initialCache = useMemo(() => {
|
|
10
|
+
return createCache()
|
|
10
11
|
}, [])
|
|
11
12
|
|
|
12
|
-
//
|
|
13
|
-
const cache = useRef(
|
|
13
|
+
// A cache of `onItemHeightDidChange()` functions.
|
|
14
|
+
const cache = useRef(initialCache)
|
|
15
|
+
|
|
16
|
+
// Adds an "on item keys reset" listener that clears the cache when item keys are reset.
|
|
17
|
+
useMemo(() => {
|
|
18
|
+
onItemKeysReset(() => {
|
|
19
|
+
cache.current = createCache()
|
|
20
|
+
})
|
|
21
|
+
}, [])
|
|
14
22
|
|
|
15
23
|
// Caches per-item `onItemHeightDidChange` functions' "references"
|
|
16
24
|
// so that item components don't get re-rendered needlessly.
|
|
17
|
-
const getOnItemHeightDidChange = useCallback((
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
const getOnItemHeightDidChange = useCallback((item) => {
|
|
26
|
+
const itemKey = getItemKey(item)
|
|
27
|
+
if (!cache.current[itemKey]) {
|
|
28
|
+
cache.current[itemKey] = () => {
|
|
29
|
+
virtualScroller.onItemHeightDidChange(item)
|
|
30
|
+
}
|
|
20
31
|
}
|
|
21
|
-
return cache.current[
|
|
32
|
+
return cache.current[itemKey]
|
|
22
33
|
}, [
|
|
23
34
|
virtualScroller,
|
|
35
|
+
getItemKey,
|
|
24
36
|
cache
|
|
25
37
|
])
|
|
26
38
|
|
|
27
39
|
return getOnItemHeightDidChange
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function createCache() {
|
|
43
|
+
// It could also use a `new Map()` here and then use `item` as a key.
|
|
44
|
+
// Although, sometimes an `item` "reference" might change while it still being
|
|
45
|
+
// the same item, i.e. having the same `getItemId(item)` value.
|
|
46
|
+
return {}
|
|
28
47
|
}
|
|
@@ -1,28 +1,50 @@
|
|
|
1
1
|
import { useMemo, useRef, useCallback } from 'react'
|
|
2
2
|
|
|
3
3
|
export default function useSetItemState({
|
|
4
|
-
|
|
4
|
+
getItemKey,
|
|
5
|
+
onItemKeysReset,
|
|
5
6
|
virtualScroller
|
|
6
7
|
}) {
|
|
7
|
-
// Only
|
|
8
|
-
const
|
|
9
|
-
return
|
|
8
|
+
// Only create the initial cache once.
|
|
9
|
+
const initialCache = useMemo(() => {
|
|
10
|
+
return createCache()
|
|
10
11
|
}, [])
|
|
11
12
|
|
|
12
|
-
//
|
|
13
|
-
const cache = useRef(
|
|
13
|
+
// A cache of `setItemState()` functions.
|
|
14
|
+
const cache = useRef(initialCache)
|
|
15
|
+
|
|
16
|
+
// Adds an "on item keys reset" listener that clears the cache when item keys are reset.
|
|
17
|
+
useMemo(() => {
|
|
18
|
+
onItemKeysReset(() => {
|
|
19
|
+
cache.current = createCache()
|
|
20
|
+
})
|
|
21
|
+
}, [])
|
|
14
22
|
|
|
15
23
|
// Caches per-item `setItemState` functions' "references"
|
|
16
24
|
// so that item components don't get re-rendered needlessly.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
// I.e. it could just re-create this function every time
|
|
26
|
+
// but that would also make React re-render the item component every time
|
|
27
|
+
// which wouldn't be efficient.
|
|
28
|
+
const getSetItemState = useCallback((item) => {
|
|
29
|
+
const itemKey = getItemKey(item)
|
|
30
|
+
if (!cache.current[itemKey]) {
|
|
31
|
+
cache.current[itemKey] = (itemState) => {
|
|
32
|
+
virtualScroller.setItemState(item, itemState)
|
|
33
|
+
}
|
|
20
34
|
}
|
|
21
|
-
return cache.current[
|
|
35
|
+
return cache.current[itemKey]
|
|
22
36
|
}, [
|
|
23
37
|
virtualScroller,
|
|
38
|
+
getItemKey,
|
|
24
39
|
cache
|
|
25
40
|
])
|
|
26
41
|
|
|
27
42
|
return getSetItemState
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function createCache() {
|
|
46
|
+
// It could also use a `new Map()` here and then use `item` as a key.
|
|
47
|
+
// Although, sometimes an `item` "reference" might change while it still being
|
|
48
|
+
// the same item, i.e. having the same `getItemId(item)` value.
|
|
49
|
+
return {}
|
|
28
50
|
}
|