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
|
@@ -4,7 +4,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
|
|
|
4
4
|
|
|
5
5
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
6
|
|
|
7
|
-
import log, {
|
|
7
|
+
import log, { reportError } from './utility/debug.js';
|
|
8
8
|
import getItemsDiff from './getItemsDiff.js';
|
|
9
9
|
import fillArray from './utility/fillArray.js';
|
|
10
10
|
export default function () {
|
|
@@ -13,6 +13,52 @@ export default function () {
|
|
|
13
13
|
this.getItemsCount = function () {
|
|
14
14
|
return _this.getState().items.length;
|
|
15
15
|
};
|
|
16
|
+
|
|
17
|
+
this._getItemIndexByItemOrIndex = function (itemOrIndex) {
|
|
18
|
+
var item = itemOrIndex;
|
|
19
|
+
|
|
20
|
+
var _this$getState = _this.getState(),
|
|
21
|
+
items = _this$getState.items; // Find the `item`'s index in the `items` array.
|
|
22
|
+
//
|
|
23
|
+
// Performance notes:
|
|
24
|
+
// Doing an `.indexOf()` operation on a very large array could be inefficient
|
|
25
|
+
// because it would have to check every element of the array.
|
|
26
|
+
// In case of any hypothetical performance-related issues,
|
|
27
|
+
// the code could use a `new Map()` as an "index" for finding individual items.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
var i = items.indexOf(item); // validate that the `item` exists in the `items` array.
|
|
31
|
+
|
|
32
|
+
if (i >= 0) {
|
|
33
|
+
return i;
|
|
34
|
+
} // Legacy behavior: some functions used to accept an `i: number` item index as an argument.
|
|
35
|
+
// Later the argument was changed to `item: Item`.
|
|
36
|
+
//
|
|
37
|
+
// The old way of passing an `i: number` argument would only result in weird application behavior
|
|
38
|
+
// when all of the below circumstances are true, which is assumed extremely unlikely:
|
|
39
|
+
//
|
|
40
|
+
// BOTH use a previous version of `virtual-scroller` (ones before December 2025, with downloads count that is not very high)
|
|
41
|
+
// AND to be used with an `items` array of type `number[]`
|
|
42
|
+
// AND to use any of the "advanced" functions:
|
|
43
|
+
// * `setItemState(i, newState)`
|
|
44
|
+
// * `onItemHeightDidChange(i)`
|
|
45
|
+
// * `getItemScrollPosition(i)`
|
|
46
|
+
//
|
|
47
|
+
// The code below handles the legacy compatibility aspect of the old type of argument
|
|
48
|
+
// except maybe for those "extremely unlikely" cases in which the app is still unlikely to crash.
|
|
49
|
+
//
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if (typeof itemOrIndex === 'number') {
|
|
53
|
+
var _i = itemOrIndex; // Validate the item index argument.
|
|
54
|
+
|
|
55
|
+
if (_i >= 0 && _i < items.length) {
|
|
56
|
+
return _i;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
reportError("Item not found: ".concat(JSON.stringify(item)));
|
|
61
|
+
};
|
|
16
62
|
/**
|
|
17
63
|
* Updates `items`. For example, can prepend or append new items to the list.
|
|
18
64
|
* @param {any[]} newItems
|
|
@@ -23,8 +69,8 @@ export default function () {
|
|
|
23
69
|
this._setItems = function (newItems) {
|
|
24
70
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
25
71
|
|
|
26
|
-
var _this$
|
|
27
|
-
previousItems = _this$
|
|
72
|
+
var _this$getState2 = _this.getState(),
|
|
73
|
+
previousItems = _this$getState2.items; // Even if `newItems` are equal to `this.state.items`,
|
|
28
74
|
// still perform a `updateState()` call, because, if `updateState()` calls
|
|
29
75
|
// were "asynchronous", there could be a situation when a developer
|
|
30
76
|
// first calls `setItems(newItems)` and then `setItems(oldItems)`:
|
|
@@ -35,8 +81,8 @@ export default function () {
|
|
|
35
81
|
// and that wouldn't be what the developer intended.
|
|
36
82
|
|
|
37
83
|
|
|
38
|
-
var _this$
|
|
39
|
-
itemStates = _this$
|
|
84
|
+
var _this$getState3 = _this.getState(),
|
|
85
|
+
itemStates = _this$getState3.itemStates;
|
|
40
86
|
|
|
41
87
|
var _ref = _this.widthHasChanged ? _this.widthHasChanged.stateUpdate : _this.getState(),
|
|
42
88
|
itemHeights = _ref.itemHeights;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualScroller.items.js","names":["log","isDebug","getItemsDiff","fillArray","getItemsCount","getState","items","length","_setItems","newItems","options","previousItems","itemStates","widthHasChanged","stateUpdate","itemHeights","layoutUpdate","itemsUpdateInfo","itemsDiff","firstShownItemIndex","lastShownItemIndex","beforeItemsHeight","afterItemsHeight","shouldRestoreScrollPosition","preserveScrollPositionOnPrependItems","preserveScrollPosition","prependedItemsCount","appendedItemsCount","shouldResetGridLayout","layout","getLayoutUpdateForItemsDiff","itemsCount","columnsCount","getActualColumnsCount","onResetGridLayout","Array","concat","i","getInitialItemState","listHeightMeasurement","snapshotListHeightBeforeAddingNewItems","firstNonMeasuredItemIndex","undefined","getInitialLayoutValues","prepend","append","reset","replace","onBeforeShowItems","newItemsWillBeRendered","count","newState","beforeResize","shouldIncludeBeforeResizeValuesInState","shouldDiscardBeforeResizeItemHeights","_isSettingNewItems","updateState","isItemEqual"],"sources":["../source/VirtualScroller.items.js"],"sourcesContent":["import log, { isDebug } from './utility/debug.js'\r\nimport getItemsDiff from './getItemsDiff.js'\r\nimport fillArray from './utility/fillArray.js'\r\n\r\nexport default function() {\r\n\tthis.getItemsCount = () => {\r\n\t\treturn this.getState().items.length\r\n\t}\r\n\r\n\t/**\r\n\t * Updates `items`. For example, can prepend or append new items to the list.\r\n\t * @param {any[]} newItems\r\n\t * @param {boolean} [options.preserveScrollPositionOnPrependItems] — Set to `true` to enable \"restore scroll position after prepending items\" feature (could be useful when implementing \"Show previous items\" button).\r\n\t */\r\n\tthis._setItems = (newItems, options = {}) => {\r\n\t\tconst {\r\n\t\t\titems: previousItems\r\n\t\t} = this.getState()\r\n\r\n\t\t// Even if `newItems` are equal to `this.state.items`,\r\n\t\t// still perform a `updateState()` call, because, if `updateState()` calls\r\n\t\t// were \"asynchronous\", there could be a situation when a developer\r\n\t\t// first calls `setItems(newItems)` and then `setItems(oldItems)`:\r\n\t\t// if this function did `return` `if (newItems === this.state.items)`\r\n\t\t// then `updateState({ items: newItems })` would be scheduled as part of\r\n\t\t// `setItems(newItems)` call, but the subsequent `setItems(oldItems)` call\r\n\t\t// wouldn't do anything resulting in `newItems` being set as a result,\r\n\t\t// and that wouldn't be what the developer intended.\r\n\r\n\t\tlet { itemStates } = this.getState()\r\n\t\tlet { itemHeights } = this.widthHasChanged\r\n\t\t\t? this.widthHasChanged.stateUpdate\r\n\t\t\t: this.getState()\r\n\r\n\t\tlog('~ Update items ~')\r\n\r\n\t\tlet layoutUpdate\r\n\t\tlet itemsUpdateInfo\r\n\r\n\t\t// Compare the new items to the current items.\r\n\t\tconst itemsDiff = this.getItemsDiff(previousItems, newItems)\r\n\r\n\t\t// See if it's an \"incremental\" items update.\r\n\t\tif (itemsDiff) {\r\n\t\t\tconst {\r\n\t\t\t\tfirstShownItemIndex,\r\n\t\t\t\tlastShownItemIndex,\r\n\t\t\t\tbeforeItemsHeight,\r\n\t\t\t\tafterItemsHeight\r\n\t\t\t} = this.widthHasChanged\r\n\t\t\t\t? this.widthHasChanged.stateUpdate\r\n\t\t\t\t: this.getState()\r\n\r\n\t\t\tconst shouldRestoreScrollPosition = firstShownItemIndex === 0 &&\r\n\t\t\t\t// `preserveScrollPosition` option name is deprecated,\r\n\t\t\t\t// use `preserveScrollPositionOnPrependItems` instead.\r\n\t\t\t\t(options.preserveScrollPositionOnPrependItems || options.preserveScrollPosition)\r\n\r\n\t\t\tconst {\r\n\t\t\t\tprependedItemsCount,\r\n\t\t\t\tappendedItemsCount\r\n\t\t\t} = itemsDiff\r\n\r\n\t\t\tlet shouldResetGridLayout\r\n\r\n\t\t\tlayoutUpdate = this.layout.getLayoutUpdateForItemsDiff({\r\n\t\t\t\tfirstShownItemIndex,\r\n\t\t\t\tlastShownItemIndex,\r\n\t\t\t\tbeforeItemsHeight,\r\n\t\t\t\tafterItemsHeight\r\n\t\t\t}, {\r\n\t\t\t\tprependedItemsCount,\r\n\t\t\t\tappendedItemsCount\r\n\t\t\t}, {\r\n\t\t\t\titemsCount: newItems.length,\r\n\t\t\t\tcolumnsCount: this.getActualColumnsCount(),\r\n\t\t\t\tshouldRestoreScrollPosition,\r\n\t\t\t\tonResetGridLayout: () => shouldResetGridLayout = true\r\n\t\t\t})\r\n\r\n\t\t\tif (prependedItemsCount > 0) {\r\n\t\t\t\tlog('Prepend', prependedItemsCount, 'items')\r\n\r\n\t\t\t\titemHeights = new Array(prependedItemsCount).concat(itemHeights)\r\n\t\t\t\titemStates = fillArray(\r\n\t\t\t\t\tnew Array(prependedItemsCount),\r\n\t\t\t\t\t(i) => this.getInitialItemState(newItems[i])\r\n\t\t\t\t)\r\n\t\t\t\t\t.concat(itemStates)\r\n\r\n\t\t\t\t// Restore scroll position after prepending items (if requested).\r\n\t\t\t\tif (shouldRestoreScrollPosition) {\r\n\t\t\t\t\tlog('Will restore scroll position')\r\n\t\t\t\t\tthis.listHeightMeasurement.snapshotListHeightBeforeAddingNewItems({\r\n\t\t\t\t\t\tpreviousItems,\r\n\t\t\t\t\t\tnewItems,\r\n\t\t\t\t\t\tprependedItemsCount\r\n\t\t\t\t\t})\r\n\t\t\t\t\t// \"Seamless prepend\" scenario doesn't result in a re-layout,\r\n\t\t\t\t\t// so if any \"non measured item\" is currently pending,\r\n\t\t\t\t\t// it doesn't get reset and will be handled after `state` is updated.\r\n\t\t\t\t\tif (this.firstNonMeasuredItemIndex !== undefined) {\r\n\t\t\t\t\t\tthis.firstNonMeasuredItemIndex += prependedItemsCount\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlog('Reset layout')\r\n\t\t\t\t\tif (shouldResetGridLayout) {\r\n\t\t\t\t\t\tlog('Reason: Prepended items count', prependedItemsCount, 'is not divisible by Columns Count', this.getActualColumnsCount())\r\n\t\t\t\t\t\t// Reset item heights because the whole grid is going to be rebalanced\r\n\t\t\t\t\t\t// and re-rendered in a different configuration.\r\n\t\t\t\t\t\titemHeights = new Array(newItems.length)\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\t// Reset layout because none of the prepended items have been measured.\r\n\t\t\t\t\t\tlog('Reason: Prepended items\\' heights are unknown')\r\n\t\t\t\t\t}\r\n\t\t\t\t\tlayoutUpdate = this.layout.getInitialLayoutValues({\r\n\t\t\t\t\t\titemsCount: newItems.length,\r\n\t\t\t\t\t\tcolumnsCount: this.getActualColumnsCount()\r\n\t\t\t\t\t})\r\n\t\t\t\t\t// Unschedule a potentially scheduled layout update\r\n\t\t\t\t\t// after measuring a previously non-measured item\r\n\t\t\t\t\t// because the list will be re-layout anyway\r\n\t\t\t\t\t// due to the new items being set.\r\n\t\t\t\t\tthis.firstNonMeasuredItemIndex = undefined\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (appendedItemsCount > 0) {\r\n\t\t\t\tlog('Append', appendedItemsCount, 'items')\r\n\t\t\t\titemHeights = itemHeights.concat(new Array(appendedItemsCount))\r\n\t\t\t\titemStates = itemStates.concat(\r\n\t\t\t\t\tfillArray(\r\n\t\t\t\t\t\tnew Array(appendedItemsCount),\r\n\t\t\t\t\t\t(i) => this.getInitialItemState(newItems[prependedItemsCount + previousItems.length + i])\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t}\r\n\r\n\t\t\titemsUpdateInfo = {\r\n\t\t\t\tprepend: prependedItemsCount > 0,\r\n\t\t\t\tappend: appendedItemsCount > 0\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tlog('Items have changed, and', (itemsDiff ? 'a re-layout from scratch has been requested.' : 'it\\'s not a simple append and/or prepend.'), 'Rerender the entire list from scratch.')\r\n\t\t\tlog('Previous items', previousItems)\r\n\t\t\tlog('New items', newItems)\r\n\r\n\t\t\t// Reset item heights and item states.\r\n\t\t\titemHeights = new Array(newItems.length)\r\n\t\t\titemStates = fillArray(\r\n\t\t\t\tnew Array(newItems.length),\r\n\t\t\t\t(i) => this.getInitialItemState(newItems[i])\r\n\t\t\t)\r\n\r\n\t\t\tlayoutUpdate = this.layout.getInitialLayoutValues({\r\n\t\t\t\titemsCount: newItems.length,\r\n\t\t\t\tcolumnsCount: this.getActualColumnsCount()\r\n\t\t\t})\r\n\r\n\t\t\t// Unschedule a potentially scheduled layout update\r\n\t\t\t// after measuring a previously non-measured item\r\n\t\t\t// because the list will be re-layout from scratch\r\n\t\t\t// due to the new items being set.\r\n\t\t\tthis.firstNonMeasuredItemIndex = undefined\r\n\r\n\t\t\t// Also reset any potential pending scroll position restoration.\r\n\t\t\t// For example, imagine a developer first called `.setItems(incrementalItemsUpdate)`\r\n\t\t\t// and then called `.setItems(differentItems)` and there was no state update\r\n\t\t\t// in between those two calls. This could happen because state updates aren't\r\n\t\t\t// required to be \"synchronous\". On other words, calling `this.updateState()`\r\n\t\t\t// doesn't necessarily mean that the state is applied immediately.\r\n\t\t\t// Imagine also that such \"delayed\" state updates could be batched,\r\n\t\t\t// like they do in React inside event handlers (though that doesn't apply to this case):\r\n\t\t\t// https://github.com/facebook/react/issues/10231#issuecomment-316644950\r\n\t\t\t// If `this.listHeightMeasurement` wasn't reset on `.setItems(differentItems)`\r\n\t\t\t// and if the second `this.updateState()` call overwrites the first one\r\n\t\t\t// then it would attempt to restore scroll position in a situation when\r\n\t\t\t// it should no longer do that. Hence the reset here.\r\n\t\t\tthis.listHeightMeasurement.reset()\r\n\r\n\t\t\titemsUpdateInfo = {\r\n\t\t\t\treplace: true\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tlog('~ Update state ~')\r\n\r\n\t\t// const layoutValuesAfterUpdate = {\r\n\t\t// \t...this.getState(),\r\n\t\t// \t...layoutUpdate\r\n\t\t// }\r\n\r\n\t\t// `layoutUpdate` is equivalent to `layoutValuesAfterUpdate` because\r\n\t\t// `layoutUpdate` contains all the relevant properties.\r\n\t\tlog('First shown item index', layoutUpdate.firstShownItemIndex)\r\n\t\tlog('Last shown item index', layoutUpdate.lastShownItemIndex)\r\n\t\tlog('Before items height', layoutUpdate.beforeItemsHeight)\r\n\t\tlog('After items height (actual or estimated)', layoutUpdate.afterItemsHeight)\r\n\r\n\t\t// Optionally preload items to be rendered.\r\n\t\t//\r\n\t\t// `layoutUpdate` is equivalent to `layoutValuesAfterUpdate` because\r\n\t\t// `layoutUpdate` contains all the relevant properties.\r\n\t\t//\r\n\t\tthis.onBeforeShowItems(\r\n\t\t\tnewItems,\r\n\t\t\titemHeights,\r\n\t\t\tlayoutUpdate.firstShownItemIndex,\r\n\t\t\tlayoutUpdate.lastShownItemIndex\r\n\t\t)\r\n\r\n\t\t// `this.newItemsWillBeRendered` signals that new `items` are being rendered,\r\n\t\t// and that `VirtualScroller` should temporarily stop all other updates.\r\n\t\t//\r\n\t\t// `this.newItemsWillBeRendered` is cleared in `onRender()`.\r\n\t\t//\r\n\t\t// The values in `this.newItemsWillBeRendered` are used, for example,\r\n\t\t// in `.onContainerResize()` handler in order to not break state consistency when\r\n\t\t// state updates are \"asynchronous\" (delayed) and there's a window resize event\r\n\t\t// in between calling `updateState()` below and that call actually being applied.\r\n\t\t//\r\n\t\tthis.newItemsWillBeRendered = {\r\n\t\t\t...itemsUpdateInfo,\r\n\t\t\tcount: newItems.length,\r\n\t\t\t// `layoutUpdate` now contains all layout-related properties, even if those that\r\n\t\t\t// didn't change. So `firstShownItemIndex` is always in `this.newItemsWillBeRendered`.\r\n\t\t\tlayout: layoutUpdate\r\n\t\t}\r\n\r\n\t\t// `layoutUpdate` now contains all layout-related properties, even if those that\r\n\t\t// didn't change. So this part is no longer relevant.\r\n\t\t//\r\n\t\t// // If `firstShownItemIndex` is gonna be modified as a result of setting new items\r\n\t\t// // then keep that \"new\" `firstShownItemIndex` in order for it to be used by\r\n\t\t// // `onResize()` handler when it calculates \"new\" `firstShownItemIndex`\r\n\t\t// // based on the new columns count (corresponding to the new window width).\r\n\t\t// if (layoutUpdate.firstShownItemIndex !== undefined) {\r\n\t\t// \tthis.newItemsWillBeRendered = {\r\n\t\t// \t\t...this.newItemsWillBeRendered,\r\n\t\t// \t\tfirstShownItemIndex: layoutUpdate.firstShownItemIndex\r\n\t\t// \t}\r\n\t\t// }\r\n\r\n\t\t// Update `VirtualScroller` state.\r\n\t\t//\r\n\t\t// This state update should overwrite all the `state` properties\r\n\t\t// that are also updated in the \"on scroll\" handler (`getShownItemIndexes()`):\r\n\t\t//\r\n\t\t// * `firstShownItemIndex`\r\n\t\t// * `lastShownItemIndex`\r\n\t\t// * `beforeItemsHeight`\r\n\t\t// * `afterItemsHeight`\r\n\t\t//\r\n\t\t// That's because this `updateState()` update has a higher priority\r\n\t\t// than that of the \"on scroll\" handler, so it should overwrite\r\n\t\t// any potential state changes dispatched by the \"on scroll\" handler.\r\n\t\t//\r\n\t\tconst newState = {\r\n\t\t\t...layoutUpdate,\r\n\t\t\titems: newItems,\r\n\t\t\titemStates,\r\n\t\t\titemHeights\r\n\t\t}\r\n\r\n\t\t// Introduced `shouldIncludeBeforeResizeValuesInState()` getter just to prevent\r\n\t\t// cluttering `state` with `beforeResize: undefined` property if `beforeResize`\r\n\t\t// hasn't ever been set in `state` previously.\r\n\t\tif (this.beforeResize.shouldIncludeBeforeResizeValuesInState()) {\r\n\t\t\tif (this.shouldDiscardBeforeResizeItemHeights()) {\r\n\t\t\t\t// Reset \"before resize\" item heights because now there're new items prepended\r\n\t\t\t\t// with unknown heights, or completely new items with unknown heights, so\r\n\t\t\t\t// `beforeItemsHeight` value won't be preserved anyway.\r\n\t\t\t\tnewState.beforeResize = undefined\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\t// Overwrite `beforeResize` property in `state` even if it wasn't modified\r\n\t\t\t\t// because state updates could be \"asynchronous\" and in that case there could be\r\n\t\t\t\t// some previous `updateState()` call from some previous `setItems()` call that\r\n\t\t\t\t// hasn't yet been applied, and that previous call might have scheduled setting\r\n\t\t\t\t// `state.beforeResize` property to `undefined` in order to reset it, but this\r\n\t\t\t\t// next `updateState()` call might not require resetting `state.beforeResize` property\r\n\t\t\t\t// so it should undo resetting it by simply overwriting it with its normal value.\r\n\t\t\t\tnewState.beforeResize = this.widthHasChanged\r\n\t\t\t\t\t? this.widthHasChanged.stateUpdate.beforeResize\r\n\t\t\t\t\t: this.getState().beforeResize\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// `newState` should also overwrite all `state` properties that're updated in `onResize()`\r\n\t\t// because `setItems()`'s state updates always overwrite `onResize()`'s state updates.\r\n\t\t// (The least-priority ones are `onScroll()` state updates, but those're simply skipped\r\n\t\t// if there's a pending `setItems()` or `onResize()` update).\r\n\t\t//\r\n\t\t// `state` property exceptions:\r\n\t\t//\r\n\t\t// `verticalSpacing` property is not updated here because it's fine setting it to\r\n\t\t// `undefined` in `onResize()` — it will simply be re-measured after the component re-renders.\r\n\t\t//\r\n\t\t// `columnsCount` property is also not updated here because by definition it's only\r\n\t\t// updated in `onResize()`.\r\n\r\n\t\t// Render.\r\n\t\tthis._isSettingNewItems = true\r\n\t\tthis.updateState(newState)\r\n\t}\r\n\r\n\tthis.getItemsDiff = (previousItems, newItems) => {\r\n\t\treturn getItemsDiff(previousItems, newItems, this.isItemEqual)\r\n\t}\r\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VirtualScroller.items.js","names":["log","reportError","getItemsDiff","fillArray","getItemsCount","getState","items","length","_getItemIndexByItemOrIndex","itemOrIndex","item","i","indexOf","JSON","stringify","_setItems","newItems","options","previousItems","itemStates","widthHasChanged","stateUpdate","itemHeights","layoutUpdate","itemsUpdateInfo","itemsDiff","firstShownItemIndex","lastShownItemIndex","beforeItemsHeight","afterItemsHeight","shouldRestoreScrollPosition","preserveScrollPositionOnPrependItems","preserveScrollPosition","prependedItemsCount","appendedItemsCount","shouldResetGridLayout","layout","getLayoutUpdateForItemsDiff","itemsCount","columnsCount","getActualColumnsCount","onResetGridLayout","Array","concat","getInitialItemState","listHeightMeasurement","snapshotListHeightBeforeAddingNewItems","firstNonMeasuredItemIndex","undefined","getInitialLayoutValues","prepend","append","reset","replace","onBeforeShowItems","newItemsWillBeRendered","count","newState","beforeResize","shouldIncludeBeforeResizeValuesInState","shouldDiscardBeforeResizeItemHeights","_isSettingNewItems","updateState","isItemEqual"],"sources":["../source/VirtualScroller.items.js"],"sourcesContent":["import log, { reportError } from './utility/debug.js'\r\nimport getItemsDiff from './getItemsDiff.js'\r\nimport fillArray from './utility/fillArray.js'\r\n\r\nexport default function() {\r\n\tthis.getItemsCount = () => {\r\n\t\treturn this.getState().items.length\r\n\t}\r\n\r\n\tthis._getItemIndexByItemOrIndex = (itemOrIndex) => {\r\n\t\tconst item = itemOrIndex\r\n\t\tconst { items } = this.getState()\r\n\t\t// Find the `item`'s index in the `items` array.\r\n\t\t//\r\n\t\t// Performance notes:\r\n\t\t// Doing an `.indexOf()` operation on a very large array could be inefficient\r\n\t\t// because it would have to check every element of the array.\r\n\t\t// In case of any hypothetical performance-related issues,\r\n\t\t// the code could use a `new Map()` as an \"index\" for finding individual items.\r\n\t\tconst i = items.indexOf(item)\r\n\t\t// validate that the `item` exists in the `items` array.\r\n\t\tif (i >= 0) {\r\n\t\t\treturn i\r\n\t\t}\r\n\r\n\t\t// Legacy behavior: some functions used to accept an `i: number` item index as an argument.\r\n\t\t// Later the argument was changed to `item: Item`.\r\n\t\t//\r\n\t\t// The old way of passing an `i: number` argument would only result in weird application behavior\r\n\t\t// when all of the below circumstances are true, which is assumed extremely unlikely:\r\n\t\t//\r\n\t\t// BOTH use a previous version of `virtual-scroller` (ones before December 2025, with downloads count that is not very high)\r\n\t\t// AND to be used with an `items` array of type `number[]`\r\n\t\t// AND to use any of the \"advanced\" functions:\r\n // * `setItemState(i, newState)`\r\n // * `onItemHeightDidChange(i)`\r\n // * `getItemScrollPosition(i)`\r\n\t\t//\r\n\t\t// The code below handles the legacy compatibility aspect of the old type of argument\r\n\t\t// except maybe for those \"extremely unlikely\" cases in which the app is still unlikely to crash.\r\n\t\t//\r\n\t\tif (typeof itemOrIndex === 'number') {\r\n\t\t\tconst i = itemOrIndex\r\n\t\t\t// Validate the item index argument.\r\n\t\t\tif (i >= 0 && i < items.length) {\r\n\t\t\t\treturn i\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treportError(`Item not found: ${JSON.stringify(item)}`)\r\n\t}\r\n\r\n\t/**\r\n\t * Updates `items`. For example, can prepend or append new items to the list.\r\n\t * @param {any[]} newItems\r\n\t * @param {boolean} [options.preserveScrollPositionOnPrependItems] — Set to `true` to enable \"restore scroll position after prepending items\" feature (could be useful when implementing \"Show previous items\" button).\r\n\t */\r\n\tthis._setItems = (newItems, options = {}) => {\r\n\t\tconst {\r\n\t\t\titems: previousItems\r\n\t\t} = this.getState()\r\n\r\n\t\t// Even if `newItems` are equal to `this.state.items`,\r\n\t\t// still perform a `updateState()` call, because, if `updateState()` calls\r\n\t\t// were \"asynchronous\", there could be a situation when a developer\r\n\t\t// first calls `setItems(newItems)` and then `setItems(oldItems)`:\r\n\t\t// if this function did `return` `if (newItems === this.state.items)`\r\n\t\t// then `updateState({ items: newItems })` would be scheduled as part of\r\n\t\t// `setItems(newItems)` call, but the subsequent `setItems(oldItems)` call\r\n\t\t// wouldn't do anything resulting in `newItems` being set as a result,\r\n\t\t// and that wouldn't be what the developer intended.\r\n\r\n\t\tlet { itemStates } = this.getState()\r\n\t\tlet { itemHeights } = this.widthHasChanged\r\n\t\t\t? this.widthHasChanged.stateUpdate\r\n\t\t\t: this.getState()\r\n\r\n\t\tlog('~ Update items ~')\r\n\r\n\t\tlet layoutUpdate\r\n\t\tlet itemsUpdateInfo\r\n\r\n\t\t// Compare the new items to the current items.\r\n\t\tconst itemsDiff = this.getItemsDiff(previousItems, newItems)\r\n\r\n\t\t// See if it's an \"incremental\" items update.\r\n\t\tif (itemsDiff) {\r\n\t\t\tconst {\r\n\t\t\t\tfirstShownItemIndex,\r\n\t\t\t\tlastShownItemIndex,\r\n\t\t\t\tbeforeItemsHeight,\r\n\t\t\t\tafterItemsHeight\r\n\t\t\t} = this.widthHasChanged\r\n\t\t\t\t? this.widthHasChanged.stateUpdate\r\n\t\t\t\t: this.getState()\r\n\r\n\t\t\tconst shouldRestoreScrollPosition = firstShownItemIndex === 0 &&\r\n\t\t\t\t// `preserveScrollPosition` option name is deprecated,\r\n\t\t\t\t// use `preserveScrollPositionOnPrependItems` instead.\r\n\t\t\t\t(options.preserveScrollPositionOnPrependItems || options.preserveScrollPosition)\r\n\r\n\t\t\tconst {\r\n\t\t\t\tprependedItemsCount,\r\n\t\t\t\tappendedItemsCount\r\n\t\t\t} = itemsDiff\r\n\r\n\t\t\tlet shouldResetGridLayout\r\n\r\n\t\t\tlayoutUpdate = this.layout.getLayoutUpdateForItemsDiff({\r\n\t\t\t\tfirstShownItemIndex,\r\n\t\t\t\tlastShownItemIndex,\r\n\t\t\t\tbeforeItemsHeight,\r\n\t\t\t\tafterItemsHeight\r\n\t\t\t}, {\r\n\t\t\t\tprependedItemsCount,\r\n\t\t\t\tappendedItemsCount\r\n\t\t\t}, {\r\n\t\t\t\titemsCount: newItems.length,\r\n\t\t\t\tcolumnsCount: this.getActualColumnsCount(),\r\n\t\t\t\tshouldRestoreScrollPosition,\r\n\t\t\t\tonResetGridLayout: () => shouldResetGridLayout = true\r\n\t\t\t})\r\n\r\n\t\t\tif (prependedItemsCount > 0) {\r\n\t\t\t\tlog('Prepend', prependedItemsCount, 'items')\r\n\r\n\t\t\t\titemHeights = new Array(prependedItemsCount)\r\n\t\t\t\t\t.concat(itemHeights)\r\n\r\n\t\t\t\titemStates = fillArray(\r\n\t\t\t\t\tnew Array(prependedItemsCount),\r\n\t\t\t\t\t(i) => this.getInitialItemState(newItems[i])\r\n\t\t\t\t)\r\n\t\t\t\t\t.concat(itemStates)\r\n\r\n\t\t\t\t// Restore scroll position after prepending items (if requested).\r\n\t\t\t\tif (shouldRestoreScrollPosition) {\r\n\t\t\t\t\tlog('Will restore scroll position')\r\n\t\t\t\t\tthis.listHeightMeasurement.snapshotListHeightBeforeAddingNewItems({\r\n\t\t\t\t\t\tpreviousItems,\r\n\t\t\t\t\t\tnewItems,\r\n\t\t\t\t\t\tprependedItemsCount\r\n\t\t\t\t\t})\r\n\t\t\t\t\t// \"Seamless prepend\" scenario doesn't result in a re-layout,\r\n\t\t\t\t\t// so if any \"non measured item\" is currently pending,\r\n\t\t\t\t\t// it doesn't get reset and will be handled after `state` is updated.\r\n\t\t\t\t\tif (this.firstNonMeasuredItemIndex !== undefined) {\r\n\t\t\t\t\t\tthis.firstNonMeasuredItemIndex += prependedItemsCount\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlog('Reset layout')\r\n\t\t\t\t\tif (shouldResetGridLayout) {\r\n\t\t\t\t\t\tlog('Reason: Prepended items count', prependedItemsCount, 'is not divisible by Columns Count', this.getActualColumnsCount())\r\n\t\t\t\t\t\t// Reset item heights because the whole grid is going to be rebalanced\r\n\t\t\t\t\t\t// and re-rendered in a different configuration.\r\n\t\t\t\t\t\titemHeights = new Array(newItems.length)\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\t// Reset layout because none of the prepended items have been measured.\r\n\t\t\t\t\t\tlog('Reason: Prepended items\\' heights are unknown')\r\n\t\t\t\t\t}\r\n\t\t\t\t\tlayoutUpdate = this.layout.getInitialLayoutValues({\r\n\t\t\t\t\t\titemsCount: newItems.length,\r\n\t\t\t\t\t\tcolumnsCount: this.getActualColumnsCount()\r\n\t\t\t\t\t})\r\n\t\t\t\t\t// Unschedule a potentially scheduled layout update\r\n\t\t\t\t\t// after measuring a previously non-measured item\r\n\t\t\t\t\t// because the list will be re-layout anyway\r\n\t\t\t\t\t// due to the new items being set.\r\n\t\t\t\t\tthis.firstNonMeasuredItemIndex = undefined\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (appendedItemsCount > 0) {\r\n\t\t\t\tlog('Append', appendedItemsCount, 'items')\r\n\t\t\t\titemHeights = itemHeights.concat(new Array(appendedItemsCount))\r\n\t\t\t\titemStates = itemStates.concat(\r\n\t\t\t\t\tfillArray(\r\n\t\t\t\t\t\tnew Array(appendedItemsCount),\r\n\t\t\t\t\t\t(i) => this.getInitialItemState(newItems[prependedItemsCount + previousItems.length + i])\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t}\r\n\r\n\t\t\titemsUpdateInfo = {\r\n\t\t\t\tprepend: prependedItemsCount > 0,\r\n\t\t\t\tappend: appendedItemsCount > 0\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tlog('Items have changed, and', (itemsDiff ? 'a re-layout from scratch has been requested.' : 'it\\'s not a simple append and/or prepend.'), 'Rerender the entire list from scratch.')\r\n\t\t\tlog('Previous items', previousItems)\r\n\t\t\tlog('New items', newItems)\r\n\r\n\t\t\t// Reset item heights and item states.\r\n\t\t\titemHeights = new Array(newItems.length)\r\n\t\t\titemStates = fillArray(\r\n\t\t\t\tnew Array(newItems.length),\r\n\t\t\t\t(i) => this.getInitialItemState(newItems[i])\r\n\t\t\t)\r\n\r\n\t\t\tlayoutUpdate = this.layout.getInitialLayoutValues({\r\n\t\t\t\titemsCount: newItems.length,\r\n\t\t\t\tcolumnsCount: this.getActualColumnsCount()\r\n\t\t\t})\r\n\r\n\t\t\t// Unschedule a potentially scheduled layout update\r\n\t\t\t// after measuring a previously non-measured item\r\n\t\t\t// because the list will be re-layout from scratch\r\n\t\t\t// due to the new items being set.\r\n\t\t\tthis.firstNonMeasuredItemIndex = undefined\r\n\r\n\t\t\t// Also reset any potential pending scroll position restoration.\r\n\t\t\t// For example, imagine a developer first called `.setItems(incrementalItemsUpdate)`\r\n\t\t\t// and then called `.setItems(differentItems)` and there was no state update\r\n\t\t\t// in between those two calls. This could happen because state updates aren't\r\n\t\t\t// required to be \"synchronous\". On other words, calling `this.updateState()`\r\n\t\t\t// doesn't necessarily mean that the state is applied immediately.\r\n\t\t\t// Imagine also that such \"delayed\" state updates could be batched,\r\n\t\t\t// like they do in React inside event handlers (though that doesn't apply to this case):\r\n\t\t\t// https://github.com/facebook/react/issues/10231#issuecomment-316644950\r\n\t\t\t// If `this.listHeightMeasurement` wasn't reset on `.setItems(differentItems)`\r\n\t\t\t// and if the second `this.updateState()` call overwrites the first one\r\n\t\t\t// then it would attempt to restore scroll position in a situation when\r\n\t\t\t// it should no longer do that. Hence the reset here.\r\n\t\t\tthis.listHeightMeasurement.reset()\r\n\r\n\t\t\titemsUpdateInfo = {\r\n\t\t\t\treplace: true\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tlog('~ Update state ~')\r\n\r\n\t\t// const layoutValuesAfterUpdate = {\r\n\t\t// \t...this.getState(),\r\n\t\t// \t...layoutUpdate\r\n\t\t// }\r\n\r\n\t\t// `layoutUpdate` is equivalent to `layoutValuesAfterUpdate` because\r\n\t\t// `layoutUpdate` contains all the relevant properties.\r\n\t\tlog('First shown item index', layoutUpdate.firstShownItemIndex)\r\n\t\tlog('Last shown item index', layoutUpdate.lastShownItemIndex)\r\n\t\tlog('Before items height', layoutUpdate.beforeItemsHeight)\r\n\t\tlog('After items height (actual or estimated)', layoutUpdate.afterItemsHeight)\r\n\r\n\t\t// Optionally preload items to be rendered.\r\n\t\t//\r\n\t\t// `layoutUpdate` is equivalent to `layoutValuesAfterUpdate` because\r\n\t\t// `layoutUpdate` contains all the relevant properties.\r\n\t\t//\r\n\t\tthis.onBeforeShowItems(\r\n\t\t\tnewItems,\r\n\t\t\titemHeights,\r\n\t\t\tlayoutUpdate.firstShownItemIndex,\r\n\t\t\tlayoutUpdate.lastShownItemIndex\r\n\t\t)\r\n\r\n\t\t// `this.newItemsWillBeRendered` signals that new `items` are being rendered,\r\n\t\t// and that `VirtualScroller` should temporarily stop all other updates.\r\n\t\t//\r\n\t\t// `this.newItemsWillBeRendered` is cleared in `onRender()`.\r\n\t\t//\r\n\t\t// The values in `this.newItemsWillBeRendered` are used, for example,\r\n\t\t// in `.onContainerResize()` handler in order to not break state consistency when\r\n\t\t// state updates are \"asynchronous\" (delayed) and there's a window resize event\r\n\t\t// in between calling `updateState()` below and that call actually being applied.\r\n\t\t//\r\n\t\tthis.newItemsWillBeRendered = {\r\n\t\t\t...itemsUpdateInfo,\r\n\t\t\tcount: newItems.length,\r\n\t\t\t// `layoutUpdate` now contains all layout-related properties, even if those that\r\n\t\t\t// didn't change. So `firstShownItemIndex` is always in `this.newItemsWillBeRendered`.\r\n\t\t\tlayout: layoutUpdate\r\n\t\t}\r\n\r\n\t\t// `layoutUpdate` now contains all layout-related properties, even if those that\r\n\t\t// didn't change. So this part is no longer relevant.\r\n\t\t//\r\n\t\t// // If `firstShownItemIndex` is gonna be modified as a result of setting new items\r\n\t\t// // then keep that \"new\" `firstShownItemIndex` in order for it to be used by\r\n\t\t// // `onResize()` handler when it calculates \"new\" `firstShownItemIndex`\r\n\t\t// // based on the new columns count (corresponding to the new window width).\r\n\t\t// if (layoutUpdate.firstShownItemIndex !== undefined) {\r\n\t\t// \tthis.newItemsWillBeRendered = {\r\n\t\t// \t\t...this.newItemsWillBeRendered,\r\n\t\t// \t\tfirstShownItemIndex: layoutUpdate.firstShownItemIndex\r\n\t\t// \t}\r\n\t\t// }\r\n\r\n\t\t// Update `VirtualScroller` state.\r\n\t\t//\r\n\t\t// This state update should overwrite all the `state` properties\r\n\t\t// that are also updated in the \"on scroll\" handler (`getShownItemIndexes()`):\r\n\t\t//\r\n\t\t// * `firstShownItemIndex`\r\n\t\t// * `lastShownItemIndex`\r\n\t\t// * `beforeItemsHeight`\r\n\t\t// * `afterItemsHeight`\r\n\t\t//\r\n\t\t// That's because this `updateState()` update has a higher priority\r\n\t\t// than that of the \"on scroll\" handler, so it should overwrite\r\n\t\t// any potential state changes dispatched by the \"on scroll\" handler.\r\n\t\t//\r\n\t\tconst newState = {\r\n\t\t\t...layoutUpdate,\r\n\t\t\titems: newItems,\r\n\t\t\titemStates,\r\n\t\t\titemHeights\r\n\t\t}\r\n\r\n\t\t// Introduced `shouldIncludeBeforeResizeValuesInState()` getter just to prevent\r\n\t\t// cluttering `state` with `beforeResize: undefined` property if `beforeResize`\r\n\t\t// hasn't ever been set in `state` previously.\r\n\t\tif (this.beforeResize.shouldIncludeBeforeResizeValuesInState()) {\r\n\t\t\tif (this.shouldDiscardBeforeResizeItemHeights()) {\r\n\t\t\t\t// Reset \"before resize\" item heights because now there're new items prepended\r\n\t\t\t\t// with unknown heights, or completely new items with unknown heights, so\r\n\t\t\t\t// `beforeItemsHeight` value won't be preserved anyway.\r\n\t\t\t\tnewState.beforeResize = undefined\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\t// Overwrite `beforeResize` property in `state` even if it wasn't modified\r\n\t\t\t\t// because state updates could be \"asynchronous\" and in that case there could be\r\n\t\t\t\t// some previous `updateState()` call from some previous `setItems()` call that\r\n\t\t\t\t// hasn't yet been applied, and that previous call might have scheduled setting\r\n\t\t\t\t// `state.beforeResize` property to `undefined` in order to reset it, but this\r\n\t\t\t\t// next `updateState()` call might not require resetting `state.beforeResize` property\r\n\t\t\t\t// so it should undo resetting it by simply overwriting it with its normal value.\r\n\t\t\t\tnewState.beforeResize = this.widthHasChanged\r\n\t\t\t\t\t? this.widthHasChanged.stateUpdate.beforeResize\r\n\t\t\t\t\t: this.getState().beforeResize\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// `newState` should also overwrite all `state` properties that're updated in `onResize()`\r\n\t\t// because `setItems()`'s state updates always overwrite `onResize()`'s state updates.\r\n\t\t// (The least-priority ones are `onScroll()` state updates, but those're simply skipped\r\n\t\t// if there's a pending `setItems()` or `onResize()` update).\r\n\t\t//\r\n\t\t// `state` property exceptions:\r\n\t\t//\r\n\t\t// `verticalSpacing` property is not updated here because it's fine setting it to\r\n\t\t// `undefined` in `onResize()` — it will simply be re-measured after the component re-renders.\r\n\t\t//\r\n\t\t// `columnsCount` property is also not updated here because by definition it's only\r\n\t\t// updated in `onResize()`.\r\n\r\n\t\t// Render.\r\n\t\tthis._isSettingNewItems = true\r\n\t\tthis.updateState(newState)\r\n\t}\r\n\r\n\tthis.getItemsDiff = (previousItems, newItems) => {\r\n\t\treturn getItemsDiff(previousItems, newItems, this.isItemEqual)\r\n\t}\r\n}"],"mappings":"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"}
|
|
@@ -211,10 +211,19 @@ var VirtualScroller = /*#__PURE__*/function () {
|
|
|
211
211
|
value:
|
|
212
212
|
/**
|
|
213
213
|
* Returns the items's top offset relative to the scrollable container's top edge.
|
|
214
|
-
* @param {
|
|
214
|
+
* @param {any} item — Item
|
|
215
215
|
* @return {[number]} Returns the item's scroll Y position. Returns `undefined` if any of the previous items haven't been rendered yet.
|
|
216
216
|
*/
|
|
217
|
-
function getItemScrollPosition(
|
|
217
|
+
function getItemScrollPosition(itemOrIndex) {
|
|
218
|
+
// Item index.
|
|
219
|
+
var i = this._getItemIndexByItemOrIndex(itemOrIndex); // If the item wasn't found, the error was already reported,
|
|
220
|
+
// so just return some "sensible" default value.
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
if (i === undefined) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
218
227
|
var itemTopOffsetInList = this.layout.getItemTopOffset(i);
|
|
219
228
|
|
|
220
229
|
if (itemTopOffsetInList === undefined) {
|
|
@@ -230,32 +239,32 @@ var VirtualScroller = /*#__PURE__*/function () {
|
|
|
230
239
|
|
|
231
240
|
}, {
|
|
232
241
|
key: "onItemHeightChange",
|
|
233
|
-
value: function onItemHeightChange(
|
|
234
|
-
warn('`.onItemHeightChange(
|
|
235
|
-
this.onItemHeightDidChange(
|
|
242
|
+
value: function onItemHeightChange(item) {
|
|
243
|
+
warn('`.onItemHeightChange(item)` method was renamed to `.onItemHeightDidChange(item)`');
|
|
244
|
+
this.onItemHeightDidChange(item);
|
|
236
245
|
}
|
|
237
246
|
/**
|
|
238
247
|
* Forces a re-measure of an item's height.
|
|
239
|
-
* @param
|
|
248
|
+
* @param {any} item — Item. Legacy argument variant: Item index.
|
|
240
249
|
*/
|
|
241
250
|
|
|
242
251
|
}, {
|
|
243
252
|
key: "onItemHeightDidChange",
|
|
244
|
-
value: function onItemHeightDidChange(
|
|
253
|
+
value: function onItemHeightDidChange(itemOrIndex) {
|
|
245
254
|
// See the comments in the `setItemState()` function below for the rationale
|
|
246
255
|
// on why the `hasToBeStarted()` check was commented out.
|
|
247
256
|
// this.hasToBeStarted()
|
|
248
|
-
this._onItemHeightDidChange(
|
|
257
|
+
this._onItemHeightDidChange(itemOrIndex);
|
|
249
258
|
}
|
|
250
259
|
/**
|
|
251
260
|
* Updates an item's state in `state.itemStates[]`.
|
|
252
|
-
* @param {
|
|
253
|
-
* @param {any}
|
|
261
|
+
* @param {any} item — Item. Legacy argument variant: Item index.
|
|
262
|
+
* @param {any} newItemState — Item's new state
|
|
254
263
|
*/
|
|
255
264
|
|
|
256
265
|
}, {
|
|
257
266
|
key: "setItemState",
|
|
258
|
-
value: function setItemState(
|
|
267
|
+
value: function setItemState(itemOrIndex, newItemState) {
|
|
259
268
|
// There is an issue in React 18.2.0 when `useInsertionEffect()` doesn't run twice
|
|
260
269
|
// on mount unlike `useLayoutEffect()` in "strict" mode. That causes a bug in a React
|
|
261
270
|
// implementation of the `virtual-scroller`.
|
|
@@ -298,14 +307,14 @@ var VirtualScroller = /*#__PURE__*/function () {
|
|
|
298
307
|
// Commenting it out wouldn't result in any potential bugs because the code would work correctly
|
|
299
308
|
// in both cases.
|
|
300
309
|
// this.hasToBeStarted()
|
|
301
|
-
this._setItemState(
|
|
310
|
+
this._setItemState(itemOrIndex, newItemState);
|
|
302
311
|
} // (deprecated)
|
|
303
312
|
// Use `.setItemState()` method name instead.
|
|
304
313
|
|
|
305
314
|
}, {
|
|
306
315
|
key: "onItemStateChange",
|
|
307
|
-
value: function onItemStateChange(
|
|
308
|
-
this.setItemState(
|
|
316
|
+
value: function onItemStateChange(item, newItemState) {
|
|
317
|
+
this.setItemState(item, newItemState);
|
|
309
318
|
}
|
|
310
319
|
/**
|
|
311
320
|
* Updates `items`. For example, can prepend or append new items to the list.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualScroller.js","names":["VirtualScrollerConstructor","LAYOUT_REASON","log","warn","reportError","supportsTbody","hasTbodyStyles","addTbodyStyles","BROWSER_NOT_SUPPORTED_ERROR","VirtualScroller","getItemsContainerElement","items","options","_isActive","Error","scrollableContainerResizeHandler","stop","scroll","listTopOffsetWatcher","isStarted","cancelLayoutTimer","hasToBeStarted","onUpdateShownItemIndexes","reason","MANUAL","_onRender","getState","previousState","call","isRestart","setUpState","waitingForRender","_render","listHeightMeasurement","reset","_isResizing","undefined","_isSettingNewItems","isInBypassMode","isItemsContainerElementTableBody","_bypass","stateUpdate","_afterRenderStateUpdateThatWasStopped","verticalSpacing","verticalSpacingStateUpdate","measureItemHeightsAndSpacing","start","scrollableContainerWidth","scrollableContainer","getWidth","newWidth","prevWidth","onContainerResize","_usesCustomStateStorage","columnsCount","getActualColumnsCount","columnsCountFromState","STARTED","i","itemTopOffsetInList","layout","getItemTopOffset","getListTopOffsetInsideScrollableContainer","onItemHeightDidChange","_onItemHeightDidChange","newItemState","_setItemState","setItemState","newItems","_setItems"],"sources":["../source/VirtualScroller.js"],"sourcesContent":["import VirtualScrollerConstructor from './VirtualScroller.constructor.js'\r\nimport { LAYOUT_REASON } from './Layout.js'\r\nimport log, { warn, reportError } from './utility/debug.js'\r\n\r\nimport {\r\n\tsupportsTbody,\r\n\thasTbodyStyles,\r\n\taddTbodyStyles,\r\n\tBROWSER_NOT_SUPPORTED_ERROR\r\n} from './DOM/tbody.js'\r\n\r\nexport default class VirtualScroller {\r\n\t/**\r\n\t * @param {function} getItemsContainerElement — Returns the items container DOM `Element`.\r\n\t * @param {any[]} items — The list of items.\r\n\t * @param {Object} [options] — See README.md.\r\n\t * @return {VirtualScroller}\r\n\t */\r\n\tconstructor(\r\n\t\tgetItemsContainerElement,\r\n\t\titems,\r\n\t\toptions = {}\r\n\t) {\r\n\t\tVirtualScrollerConstructor.call(\r\n\t\t\tthis,\r\n\t\t\tgetItemsContainerElement,\r\n\t\t\titems,\r\n\t\t\toptions\r\n\t\t)\r\n\t}\r\n\r\n\t/**\r\n\t * Should be invoked after a \"container\" DOM Element is mounted (inserted into the DOM tree).\r\n\t */\r\n\tstart() {\r\n\t\tif (this._isActive) {\r\n\t\t\tthrow new Error('[virtual-scroller] `VirtualScroller` has already been started')\r\n\t\t}\r\n\r\n\t\t// If has been stopped previously.\r\n\t\tconst isRestart = this._isActive === false\r\n\r\n\t\tif (!isRestart) {\r\n\t\t\tthis.setUpState()\r\n\t\t\tthis.waitingForRender = true\r\n\t\t\t// If `render()` function parameter was passed,\r\n\t\t\t// perform an initial render.\r\n\t\t\tif (this._render) {\r\n\t\t\t\tthis._render(this.getState())\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (isRestart) {\r\n\t\t\tlog('~ Start (restart) ~')\r\n\t\t} else {\r\n\t\t\tlog('~ Start ~')\r\n\t\t}\r\n\r\n\t\t// `this._isActive = true` should be placed somewhere at the start of this function.\r\n\t\tthis._isActive = true\r\n\r\n\t\t// Reset `ListHeightMeasurement` just in case it has some \"leftover\" state.\r\n\t\tthis.listHeightMeasurement.reset()\r\n\r\n\t\t// Reset `_isResizing` flag just in case it has some \"leftover\" value.\r\n\t\tthis._isResizing = undefined\r\n\r\n\t\t// Reset `_isSettingNewItems` flag just in case it has some \"leftover\" value.\r\n\t\tthis._isSettingNewItems = undefined\r\n\r\n\t\t// When `<tbody/>` is used as an items container element,\r\n\t\t// `virtual-scroller` has to work around the HTML bug of\r\n\t\t// `padding` not working on a `<tbody/>` element.\r\n\t\t// https://gitlab.com/catamphetamine/virtual-scroller/-/issues/1\r\n\t\tif (!this.isInBypassMode()) {\r\n\t\t\tif (this.isItemsContainerElementTableBody()) {\r\n\t\t\t\tif (supportsTbody()) {\r\n\t\t\t\t\tif (!hasTbodyStyles(this.getItemsContainerElement())) {\r\n\t\t\t\t\t\tlog('~ <tbody/> container ~')\r\n\t\t\t\t\t\taddTbodyStyles(this.getItemsContainerElement())\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlog('~ <tbody/> container not supported ~')\r\n\t\t\t\t\treportError(BROWSER_NOT_SUPPORTED_ERROR)\r\n\t\t\t\t\tlog('~ enter \"bypass\" mode ~')\r\n\t\t\t\t\tthis._bypass = true\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// If there was a pending \"after render\" state update that didn't get applied\r\n\t\t// because the `VirtualScroller` got stopped, then apply that pending \"after render\"\r\n\t\t// state update now. Such state update could include properties like:\r\n\t\t// * A `verticalSpacing` that has been measured in `onRender()`.\r\n\t\t// * A cleaned-up `beforeResize` object that was cleaned-up in `onRender()`.\r\n\t\tlet stateUpdate = this._afterRenderStateUpdateThatWasStopped\r\n\t\tthis._afterRenderStateUpdateThatWasStopped = undefined\r\n\r\n\t\t// Reset `this.verticalSpacing` so that it re-measures it in cases when\r\n\t\t// the `VirtualScroller` was previously stopped and is now being restarted.\r\n\t\t// The rationale is that a previously captured inter-item vertical spacing\r\n\t\t// can't be \"trusted\" in a sense that the user might have resized the window\r\n\t\t// after the previous `state` has been snapshotted.\r\n\t\t// If the user has resized the window, then changing window width might have\r\n\t\t// activated different CSS `@media()` \"queries\" resulting in a potentially different\r\n\t\t// vertical spacing after the restart.\r\n\t\t// If it's not a restart then `this.verticalSpacing` is `undefined` anyway.\r\n\t\tthis.verticalSpacing = undefined\r\n\r\n\t\tconst verticalSpacingStateUpdate = this.measureItemHeightsAndSpacing()\r\n\t\tif (verticalSpacingStateUpdate) {\r\n\t\t\tstateUpdate = {\r\n\t\t\t\t...stateUpdate,\r\n\t\t\t\t...verticalSpacingStateUpdate\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis.scrollableContainerResizeHandler.start()\r\n\t\tthis.scroll.start()\r\n\r\n\t\t// If `scrollableContainerWidth` hasn't been measured yet,\r\n\t\t// measure it and write it to state.\r\n\t\tif (this.getState().scrollableContainerWidth === undefined) {\r\n\t\t\tconst scrollableContainerWidth = this.scrollableContainer.getWidth()\r\n\t\t\tstateUpdate = {\r\n\t\t\t\t...stateUpdate,\r\n\t\t\t\tscrollableContainerWidth\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\t// Reset layout:\r\n\t\t\t// * If the scrollable container width has changed while stopped.\r\n\t\t\t// * If the restored state was calculated for another scrollable container width.\r\n\t\t\tconst newWidth = this.scrollableContainer.getWidth()\r\n\t\t\tconst prevWidth = this.getState().scrollableContainerWidth\r\n\t\t\tif (newWidth !== prevWidth) {\r\n\t\t\t\tlog('~ Scrollable container width changed from', prevWidth, 'to', newWidth, '~')\r\n\t\t\t\t// The pending state update (if present) won't be applied in this case.\r\n\t\t\t\t// That's ok because such state update could currently only originate in\r\n\t\t\t\t// `this.onContainerResize()` function. Therefore, alling `this.onContainerResize()` again\r\n\t\t\t\t// would rewrite all those `stateUpdate` properties anyway, so they're not passed.\r\n\t\t\t\treturn this.onContainerResize()\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// If the `VirtualScroller` uses custom (external) state storage, then\r\n\t\t// check if the columns count has changed between calling `.getInitialState()`\r\n\t\t// and `.start()`. If it has, perform a re-layout \"from scratch\".\r\n\t\tif (this._usesCustomStateStorage) {\r\n\t\t\tconst columnsCount = this.getActualColumnsCount()\r\n\t\t\tconst columnsCountFromState = this.getState().columnsCount || 1\r\n\t\t\tif (columnsCount !== columnsCountFromState) {\r\n\t\t\t\treturn this.onContainerResize()\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Re-calculate layout and re-render the list.\r\n\t\t// Do that even if when an initial `state` parameter, containing layout values,\r\n\t\t// has been passed. The reason is that the `state` parameter can't be \"trusted\"\r\n\t\t// in a way that it could have been snapshotted for another window width and\r\n\t\t// the user might have resized their window since then.\r\n\t\tthis.onUpdateShownItemIndexes({ reason: LAYOUT_REASON.STARTED, stateUpdate })\r\n\t}\r\n\r\n\t// Could be passed as a \"callback\" parameter, so bind it to `this`.\r\n\tstop = () => {\r\n\t\tif (!this._isActive) {\r\n\t\t\tthrow new Error('[virtual-scroller] Can\\'t stop a `VirtualScroller` that hasn\\'t been started')\r\n\t\t}\r\n\r\n\t\tthis._isActive = false\r\n\r\n\t\tlog('~ Stop ~')\r\n\r\n\t\tthis.scrollableContainerResizeHandler.stop()\r\n\t\tthis.scroll.stop()\r\n\r\n\t\t// Stop `ListTopOffsetWatcher` if it has been started.\r\n\t\t// There seems to be no need to restart `ListTopOffsetWatcher`.\r\n\t\t// It's mainly a hacky workaround for development mode anyway.\r\n\t\tif (this.listTopOffsetWatcher && this.listTopOffsetWatcher.isStarted()) {\r\n\t\t\tthis.listTopOffsetWatcher.stop()\r\n\t\t}\r\n\r\n\t\t// Cancel any scheduled layout.\r\n\t\tthis.cancelLayoutTimer({})\r\n\t}\r\n\r\n\thasToBeStarted() {\r\n\t\tif (!this._isActive) {\r\n\t\t\tthrow new Error('[virtual-scroller] `VirtualScroller` hasn\\'t been started')\r\n\t\t}\r\n\t}\r\n\r\n\t// Bind it to `this` because this function could hypothetically be passed\r\n\t// as a \"callback\" parameter.\r\n\tupdateLayout = () => {\r\n\t\tthis.hasToBeStarted()\r\n\t\tthis.onUpdateShownItemIndexes({ reason: LAYOUT_REASON.MANUAL })\r\n\t}\r\n\r\n\t// Bind the function to `this` so that it could be passed as a callback\r\n\t// in a random application's code.\r\n\tonRender = () => {\r\n\t\tthis._onRender(this.getState(), this.previousState)\r\n\t}\r\n\r\n\t/**\r\n\t * Returns the items's top offset relative to the scrollable container's top edge.\r\n\t * @param {number} i — Item index\r\n\t * @return {[number]} Returns the item's scroll Y position. Returns `undefined` if any of the previous items haven't been rendered yet.\r\n\t */\r\n\tgetItemScrollPosition(i) {\r\n\t\tconst itemTopOffsetInList = this.layout.getItemTopOffset(i)\r\n\t\tif (itemTopOffsetInList === undefined) {\r\n\t\t\treturn\r\n\t\t}\r\n\t\treturn this.getListTopOffsetInsideScrollableContainer() + itemTopOffsetInList\r\n\t}\r\n\r\n\t/**\r\n\t * @deprecated\r\n\t * `.onItemHeightChange()` has been renamed to `.onItemHeightDidChange()`.\r\n\t */\r\n\tonItemHeightChange(i) {\r\n\t\twarn('`.onItemHeightChange(i)` method was renamed to `.onItemHeightDidChange(i)`')\r\n\t\tthis.onItemHeightDidChange(i)\r\n\t}\r\n\r\n\t/**\r\n\t * Forces a re-measure of an item's height.\r\n\t * @param {number} i — Item index\r\n\t */\r\n\tonItemHeightDidChange(i) {\r\n\t\t// See the comments in the `setItemState()` function below for the rationale\r\n\t\t// on why the `hasToBeStarted()` check was commented out.\r\n\t\t// this.hasToBeStarted()\r\n\t\tthis._onItemHeightDidChange(i)\r\n\t}\r\n\r\n\t/**\r\n\t * Updates an item's state in `state.itemStates[]`.\r\n\t * @param {number} i — Item index\r\n\t * @param {any} i — Item's new state\r\n\t */\r\n\tsetItemState(i, newItemState) {\r\n\t\t// There is an issue in React 18.2.0 when `useInsertionEffect()` doesn't run twice\r\n\t\t// on mount unlike `useLayoutEffect()` in \"strict\" mode. That causes a bug in a React\r\n\t\t// implementation of the `virtual-scroller`.\r\n\t\t// https://gitlab.com/catamphetamine/virtual-scroller/-/issues/33\r\n\t\t// https://github.com/facebook/react/issues/26320\r\n\t\t// A workaround for that bug is ignoring the second-initial run of the effects at mount.\r\n\t\t//\r\n\t\t// But in that case, if an `ItemComponent` calls `setItemState()` in `useLayoutEffect()`,\r\n\t\t// it could result in a bug.\r\n\t\t//\r\n\t\t// Consider a type of `useLayoutEffect()` that skips the initial mount:\r\n\t\t// `useLayoutEffectSkipInitialMount()`.\r\n\t\t// Suppose that effect is written in such a way that it only skips the first call of itself.\r\n\t\t// In that case, if React is run in \"strict\" mode, the effect will no longer work as expected\r\n\t\t// and it won't actually skip the initial mount and will be executed during the second initial run.\r\n\t\t// But the `VirtualScroller` itself has already implemented a workaround that prevents\r\n\t\t// its hooks from running twice on mount. This means that `useVirtualScrollerStartStop()`\r\n\t\t// of the React component would have already stopped the `VirtualScroller` by the time\r\n\t\t// `ItemComponent`'s incorrectly-behaving `useLayoutEffectSkipInitialMount()` effect is run,\r\n\t\t// resulting in an error: \"`VirtualScroller` hasn't been started\".\r\n\t\t//\r\n\t\t// The log when not in \"strict\" mode would be:\r\n\t\t//\r\n\t\t// * `useLayoutEffect()` is run in `ItemComponent` — skips the initial run.\r\n\t\t// * `useLayoutEffect()` is run in `useVirtualScrollerStartStop()`. It starts the `VirtualScroller`.\r\n\t\t// * Some dependency property gets updated inside `ItemComponent`.\r\n\t\t// * `useLayoutEffect()` is run in `ItemComponent` — no longer skips. Calls `setItemState()`.\r\n\t\t// * The `VirtualScroller` is started so it handles `setState()` correctly.\r\n\t\t//\r\n\t\t// The log when in \"strict\" mode would be:\r\n\t\t//\r\n\t\t// * `useLayoutEffect()` is run in `ItemComponent` — skips the initial run.\r\n\t\t// * `useLayoutEffect()` is run in `useVirtualScrollerStartStop()`. It starts the `VirtualScroller`.\r\n\t\t// * `useLayoutEffect()` is unmounted in `useVirtualScrollerStartStop()`. It stops the `VirtualScroller`.\r\n\t\t// * `useLayoutEffect()` is unmounted in `ItemComponent` — does nothing.\r\n\t\t// * `useLayoutEffect()` is run the second time in `ItemComponent` — no longer skips. Calls `setItemState()`.\r\n\t\t// * The `VirtualScroller` is stopped so it throws an error: \"`VirtualScroller` hasn't been started\".\r\n\t\t//\r\n\t\t// For that reason, the requirement of the `VirtualScroller` to be started was commented out.\r\n\t\t// Commenting it out wouldn't result in any potential bugs because the code would work correctly\r\n\t\t// in both cases.\r\n\t\t// this.hasToBeStarted()\r\n\t\tthis._setItemState(i, newItemState)\r\n\t}\r\n\r\n\t// (deprecated)\r\n\t// Use `.setItemState()` method name instead.\r\n\tonItemStateChange(i, newItemState) {\r\n\t\tthis.setItemState(i, newItemState)\r\n\t}\r\n\r\n\t/**\r\n\t * Updates `items`. For example, can prepend or append new items to the list.\r\n\t * @param {any[]} newItems\r\n\t * @param {boolean} [options.preserveScrollPositionOnPrependItems] — Set to `true` to enable \"restore scroll position after prepending items\" feature (could be useful when implementing \"Show previous items\" button).\r\n\t */\r\n\tsetItems(newItems, options = {}) {\r\n\t\tthis.hasToBeStarted()\r\n\t\treturn this._setItems(newItems, options)\r\n\t}\r\n}"],"mappings":"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AAC,4EAAD,CAAJ;MACA,KAAK4D,qBAAL,CAA2BL,CAA3B;IACA;IAED;AACD;AACA;AACA;;;;WACC,+BAAsBA,CAAtB,EAAyB;MACxB;MACA;MACA;MACA,KAAKM,sBAAL,CAA4BN,CAA5B;IACA;IAED;AACD;AACA;AACA;AACA;;;;WACC,sBAAaA,CAAb,EAAgBO,YAAhB,EAA8B;MAC7B;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA,KAAKC,aAAL,CAAmBR,CAAnB,EAAsBO,YAAtB;IACA,C,CAED;IACA;;;;WACA,2BAAkBP,CAAlB,EAAqBO,YAArB,EAAmC;MAClC,KAAKE,YAAL,CAAkBT,CAAlB,EAAqBO,YAArB;IACA;IAED;AACD;AACA;AACA;AACA;;;;WACC,kBAASG,QAAT,EAAiC;MAAA,IAAdxD,OAAc,uEAAJ,EAAI;MAChC,KAAKS,cAAL;MACA,OAAO,KAAKgD,SAAL,CAAeD,QAAf,EAAyBxD,OAAzB,CAAP;IACA;;;;;;SArSmBH,e"}
|
|
1
|
+
{"version":3,"file":"VirtualScroller.js","names":["VirtualScrollerConstructor","LAYOUT_REASON","log","warn","reportError","supportsTbody","hasTbodyStyles","addTbodyStyles","BROWSER_NOT_SUPPORTED_ERROR","VirtualScroller","getItemsContainerElement","items","options","_isActive","Error","scrollableContainerResizeHandler","stop","scroll","listTopOffsetWatcher","isStarted","cancelLayoutTimer","hasToBeStarted","onUpdateShownItemIndexes","reason","MANUAL","_onRender","getState","previousState","call","isRestart","setUpState","waitingForRender","_render","listHeightMeasurement","reset","_isResizing","undefined","_isSettingNewItems","isInBypassMode","isItemsContainerElementTableBody","_bypass","stateUpdate","_afterRenderStateUpdateThatWasStopped","verticalSpacing","verticalSpacingStateUpdate","measureItemHeightsAndSpacing","start","scrollableContainerWidth","scrollableContainer","getWidth","newWidth","prevWidth","onContainerResize","_usesCustomStateStorage","columnsCount","getActualColumnsCount","columnsCountFromState","STARTED","itemOrIndex","i","_getItemIndexByItemOrIndex","itemTopOffsetInList","layout","getItemTopOffset","getListTopOffsetInsideScrollableContainer","item","onItemHeightDidChange","_onItemHeightDidChange","newItemState","_setItemState","setItemState","newItems","_setItems"],"sources":["../source/VirtualScroller.js"],"sourcesContent":["import VirtualScrollerConstructor from './VirtualScroller.constructor.js'\r\nimport { LAYOUT_REASON } from './Layout.js'\r\nimport log, { warn, reportError } from './utility/debug.js'\r\n\r\nimport {\r\n\tsupportsTbody,\r\n\thasTbodyStyles,\r\n\taddTbodyStyles,\r\n\tBROWSER_NOT_SUPPORTED_ERROR\r\n} from './DOM/tbody.js'\r\n\r\nexport default class VirtualScroller {\r\n\t/**\r\n\t * @param {function} getItemsContainerElement — Returns the items container DOM `Element`.\r\n\t * @param {any[]} items — The list of items.\r\n\t * @param {Object} [options] — See README.md.\r\n\t * @return {VirtualScroller}\r\n\t */\r\n\tconstructor(\r\n\t\tgetItemsContainerElement,\r\n\t\titems,\r\n\t\toptions = {}\r\n\t) {\r\n\t\tVirtualScrollerConstructor.call(\r\n\t\t\tthis,\r\n\t\t\tgetItemsContainerElement,\r\n\t\t\titems,\r\n\t\t\toptions\r\n\t\t)\r\n\t}\r\n\r\n\t/**\r\n\t * Should be invoked after a \"container\" DOM Element is mounted (inserted into the DOM tree).\r\n\t */\r\n\tstart() {\r\n\t\tif (this._isActive) {\r\n\t\t\tthrow new Error('[virtual-scroller] `VirtualScroller` has already been started')\r\n\t\t}\r\n\r\n\t\t// If has been stopped previously.\r\n\t\tconst isRestart = this._isActive === false\r\n\r\n\t\tif (!isRestart) {\r\n\t\t\tthis.setUpState()\r\n\t\t\tthis.waitingForRender = true\r\n\t\t\t// If `render()` function parameter was passed,\r\n\t\t\t// perform an initial render.\r\n\t\t\tif (this._render) {\r\n\t\t\t\tthis._render(this.getState())\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (isRestart) {\r\n\t\t\tlog('~ Start (restart) ~')\r\n\t\t} else {\r\n\t\t\tlog('~ Start ~')\r\n\t\t}\r\n\r\n\t\t// `this._isActive = true` should be placed somewhere at the start of this function.\r\n\t\tthis._isActive = true\r\n\r\n\t\t// Reset `ListHeightMeasurement` just in case it has some \"leftover\" state.\r\n\t\tthis.listHeightMeasurement.reset()\r\n\r\n\t\t// Reset `_isResizing` flag just in case it has some \"leftover\" value.\r\n\t\tthis._isResizing = undefined\r\n\r\n\t\t// Reset `_isSettingNewItems` flag just in case it has some \"leftover\" value.\r\n\t\tthis._isSettingNewItems = undefined\r\n\r\n\t\t// When `<tbody/>` is used as an items container element,\r\n\t\t// `virtual-scroller` has to work around the HTML bug of\r\n\t\t// `padding` not working on a `<tbody/>` element.\r\n\t\t// https://gitlab.com/catamphetamine/virtual-scroller/-/issues/1\r\n\t\tif (!this.isInBypassMode()) {\r\n\t\t\tif (this.isItemsContainerElementTableBody()) {\r\n\t\t\t\tif (supportsTbody()) {\r\n\t\t\t\t\tif (!hasTbodyStyles(this.getItemsContainerElement())) {\r\n\t\t\t\t\t\tlog('~ <tbody/> container ~')\r\n\t\t\t\t\t\taddTbodyStyles(this.getItemsContainerElement())\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlog('~ <tbody/> container not supported ~')\r\n\t\t\t\t\treportError(BROWSER_NOT_SUPPORTED_ERROR)\r\n\t\t\t\t\tlog('~ enter \"bypass\" mode ~')\r\n\t\t\t\t\tthis._bypass = true\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// If there was a pending \"after render\" state update that didn't get applied\r\n\t\t// because the `VirtualScroller` got stopped, then apply that pending \"after render\"\r\n\t\t// state update now. Such state update could include properties like:\r\n\t\t// * A `verticalSpacing` that has been measured in `onRender()`.\r\n\t\t// * A cleaned-up `beforeResize` object that was cleaned-up in `onRender()`.\r\n\t\tlet stateUpdate = this._afterRenderStateUpdateThatWasStopped\r\n\t\tthis._afterRenderStateUpdateThatWasStopped = undefined\r\n\r\n\t\t// Reset `this.verticalSpacing` so that it re-measures it in cases when\r\n\t\t// the `VirtualScroller` was previously stopped and is now being restarted.\r\n\t\t// The rationale is that a previously captured inter-item vertical spacing\r\n\t\t// can't be \"trusted\" in a sense that the user might have resized the window\r\n\t\t// after the previous `state` has been snapshotted.\r\n\t\t// If the user has resized the window, then changing window width might have\r\n\t\t// activated different CSS `@media()` \"queries\" resulting in a potentially different\r\n\t\t// vertical spacing after the restart.\r\n\t\t// If it's not a restart then `this.verticalSpacing` is `undefined` anyway.\r\n\t\tthis.verticalSpacing = undefined\r\n\r\n\t\tconst verticalSpacingStateUpdate = this.measureItemHeightsAndSpacing()\r\n\t\tif (verticalSpacingStateUpdate) {\r\n\t\t\tstateUpdate = {\r\n\t\t\t\t...stateUpdate,\r\n\t\t\t\t...verticalSpacingStateUpdate\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis.scrollableContainerResizeHandler.start()\r\n\t\tthis.scroll.start()\r\n\r\n\t\t// If `scrollableContainerWidth` hasn't been measured yet,\r\n\t\t// measure it and write it to state.\r\n\t\tif (this.getState().scrollableContainerWidth === undefined) {\r\n\t\t\tconst scrollableContainerWidth = this.scrollableContainer.getWidth()\r\n\t\t\tstateUpdate = {\r\n\t\t\t\t...stateUpdate,\r\n\t\t\t\tscrollableContainerWidth\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\t// Reset layout:\r\n\t\t\t// * If the scrollable container width has changed while stopped.\r\n\t\t\t// * If the restored state was calculated for another scrollable container width.\r\n\t\t\tconst newWidth = this.scrollableContainer.getWidth()\r\n\t\t\tconst prevWidth = this.getState().scrollableContainerWidth\r\n\t\t\tif (newWidth !== prevWidth) {\r\n\t\t\t\tlog('~ Scrollable container width changed from', prevWidth, 'to', newWidth, '~')\r\n\t\t\t\t// The pending state update (if present) won't be applied in this case.\r\n\t\t\t\t// That's ok because such state update could currently only originate in\r\n\t\t\t\t// `this.onContainerResize()` function. Therefore, alling `this.onContainerResize()` again\r\n\t\t\t\t// would rewrite all those `stateUpdate` properties anyway, so they're not passed.\r\n\t\t\t\treturn this.onContainerResize()\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// If the `VirtualScroller` uses custom (external) state storage, then\r\n\t\t// check if the columns count has changed between calling `.getInitialState()`\r\n\t\t// and `.start()`. If it has, perform a re-layout \"from scratch\".\r\n\t\tif (this._usesCustomStateStorage) {\r\n\t\t\tconst columnsCount = this.getActualColumnsCount()\r\n\t\t\tconst columnsCountFromState = this.getState().columnsCount || 1\r\n\t\t\tif (columnsCount !== columnsCountFromState) {\r\n\t\t\t\treturn this.onContainerResize()\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Re-calculate layout and re-render the list.\r\n\t\t// Do that even if when an initial `state` parameter, containing layout values,\r\n\t\t// has been passed. The reason is that the `state` parameter can't be \"trusted\"\r\n\t\t// in a way that it could have been snapshotted for another window width and\r\n\t\t// the user might have resized their window since then.\r\n\t\tthis.onUpdateShownItemIndexes({ reason: LAYOUT_REASON.STARTED, stateUpdate })\r\n\t}\r\n\r\n\t// Could be passed as a \"callback\" parameter, so bind it to `this`.\r\n\tstop = () => {\r\n\t\tif (!this._isActive) {\r\n\t\t\tthrow new Error('[virtual-scroller] Can\\'t stop a `VirtualScroller` that hasn\\'t been started')\r\n\t\t}\r\n\r\n\t\tthis._isActive = false\r\n\r\n\t\tlog('~ Stop ~')\r\n\r\n\t\tthis.scrollableContainerResizeHandler.stop()\r\n\t\tthis.scroll.stop()\r\n\r\n\t\t// Stop `ListTopOffsetWatcher` if it has been started.\r\n\t\t// There seems to be no need to restart `ListTopOffsetWatcher`.\r\n\t\t// It's mainly a hacky workaround for development mode anyway.\r\n\t\tif (this.listTopOffsetWatcher && this.listTopOffsetWatcher.isStarted()) {\r\n\t\t\tthis.listTopOffsetWatcher.stop()\r\n\t\t}\r\n\r\n\t\t// Cancel any scheduled layout.\r\n\t\tthis.cancelLayoutTimer({})\r\n\t}\r\n\r\n\thasToBeStarted() {\r\n\t\tif (!this._isActive) {\r\n\t\t\tthrow new Error('[virtual-scroller] `VirtualScroller` hasn\\'t been started')\r\n\t\t}\r\n\t}\r\n\r\n\t// Bind it to `this` because this function could hypothetically be passed\r\n\t// as a \"callback\" parameter.\r\n\tupdateLayout = () => {\r\n\t\tthis.hasToBeStarted()\r\n\t\tthis.onUpdateShownItemIndexes({ reason: LAYOUT_REASON.MANUAL })\r\n\t}\r\n\r\n\t// Bind the function to `this` so that it could be passed as a callback\r\n\t// in a random application's code.\r\n\tonRender = () => {\r\n\t\tthis._onRender(this.getState(), this.previousState)\r\n\t}\r\n\r\n\t/**\r\n\t * Returns the items's top offset relative to the scrollable container's top edge.\r\n\t * @param {any} item — Item\r\n\t * @return {[number]} Returns the item's scroll Y position. Returns `undefined` if any of the previous items haven't been rendered yet.\r\n\t */\r\n\tgetItemScrollPosition(itemOrIndex) {\r\n\t\t// Item index.\r\n\t\tconst i = this._getItemIndexByItemOrIndex(itemOrIndex)\r\n\r\n\t\t// If the item wasn't found, the error was already reported,\r\n\t\t// so just return some \"sensible\" default value.\r\n\t\tif (i === undefined) {\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tconst itemTopOffsetInList = this.layout.getItemTopOffset(i)\r\n\t\tif (itemTopOffsetInList === undefined) {\r\n\t\t\treturn\r\n\t\t}\r\n\t\treturn this.getListTopOffsetInsideScrollableContainer() + itemTopOffsetInList\r\n\t}\r\n\r\n\t/**\r\n\t * @deprecated\r\n\t * `.onItemHeightChange()` has been renamed to `.onItemHeightDidChange()`.\r\n\t */\r\n\tonItemHeightChange(item) {\r\n\t\twarn('`.onItemHeightChange(item)` method was renamed to `.onItemHeightDidChange(item)`')\r\n\t\tthis.onItemHeightDidChange(item)\r\n\t}\r\n\r\n\t/**\r\n\t * Forces a re-measure of an item's height.\r\n\t * @param {any} item — Item. Legacy argument variant: Item index.\r\n\t */\r\n\tonItemHeightDidChange(itemOrIndex) {\r\n\t\t// See the comments in the `setItemState()` function below for the rationale\r\n\t\t// on why the `hasToBeStarted()` check was commented out.\r\n\t\t// this.hasToBeStarted()\r\n\t\tthis._onItemHeightDidChange(itemOrIndex)\r\n\t}\r\n\r\n\t/**\r\n\t * Updates an item's state in `state.itemStates[]`.\r\n\t * @param {any} item — Item. Legacy argument variant: Item index.\r\n\t * @param {any} newItemState — Item's new state\r\n\t */\r\n\tsetItemState(itemOrIndex, newItemState) {\r\n\t\t// There is an issue in React 18.2.0 when `useInsertionEffect()` doesn't run twice\r\n\t\t// on mount unlike `useLayoutEffect()` in \"strict\" mode. That causes a bug in a React\r\n\t\t// implementation of the `virtual-scroller`.\r\n\t\t// https://gitlab.com/catamphetamine/virtual-scroller/-/issues/33\r\n\t\t// https://github.com/facebook/react/issues/26320\r\n\t\t// A workaround for that bug is ignoring the second-initial run of the effects at mount.\r\n\t\t//\r\n\t\t// But in that case, if an `ItemComponent` calls `setItemState()` in `useLayoutEffect()`,\r\n\t\t// it could result in a bug.\r\n\t\t//\r\n\t\t// Consider a type of `useLayoutEffect()` that skips the initial mount:\r\n\t\t// `useLayoutEffectSkipInitialMount()`.\r\n\t\t// Suppose that effect is written in such a way that it only skips the first call of itself.\r\n\t\t// In that case, if React is run in \"strict\" mode, the effect will no longer work as expected\r\n\t\t// and it won't actually skip the initial mount and will be executed during the second initial run.\r\n\t\t// But the `VirtualScroller` itself has already implemented a workaround that prevents\r\n\t\t// its hooks from running twice on mount. This means that `useVirtualScrollerStartStop()`\r\n\t\t// of the React component would have already stopped the `VirtualScroller` by the time\r\n\t\t// `ItemComponent`'s incorrectly-behaving `useLayoutEffectSkipInitialMount()` effect is run,\r\n\t\t// resulting in an error: \"`VirtualScroller` hasn't been started\".\r\n\t\t//\r\n\t\t// The log when not in \"strict\" mode would be:\r\n\t\t//\r\n\t\t// * `useLayoutEffect()` is run in `ItemComponent` — skips the initial run.\r\n\t\t// * `useLayoutEffect()` is run in `useVirtualScrollerStartStop()`. It starts the `VirtualScroller`.\r\n\t\t// * Some dependency property gets updated inside `ItemComponent`.\r\n\t\t// * `useLayoutEffect()` is run in `ItemComponent` — no longer skips. Calls `setItemState()`.\r\n\t\t// * The `VirtualScroller` is started so it handles `setState()` correctly.\r\n\t\t//\r\n\t\t// The log when in \"strict\" mode would be:\r\n\t\t//\r\n\t\t// * `useLayoutEffect()` is run in `ItemComponent` — skips the initial run.\r\n\t\t// * `useLayoutEffect()` is run in `useVirtualScrollerStartStop()`. It starts the `VirtualScroller`.\r\n\t\t// * `useLayoutEffect()` is unmounted in `useVirtualScrollerStartStop()`. It stops the `VirtualScroller`.\r\n\t\t// * `useLayoutEffect()` is unmounted in `ItemComponent` — does nothing.\r\n\t\t// * `useLayoutEffect()` is run the second time in `ItemComponent` — no longer skips. Calls `setItemState()`.\r\n\t\t// * The `VirtualScroller` is stopped so it throws an error: \"`VirtualScroller` hasn't been started\".\r\n\t\t//\r\n\t\t// For that reason, the requirement of the `VirtualScroller` to be started was commented out.\r\n\t\t// Commenting it out wouldn't result in any potential bugs because the code would work correctly\r\n\t\t// in both cases.\r\n\t\t// this.hasToBeStarted()\r\n\t\tthis._setItemState(itemOrIndex, newItemState)\r\n\t}\r\n\r\n\t// (deprecated)\r\n\t// Use `.setItemState()` method name instead.\r\n\tonItemStateChange(item, newItemState) {\r\n\t\tthis.setItemState(item, newItemState)\r\n\t}\r\n\r\n\t/**\r\n\t * Updates `items`. For example, can prepend or append new items to the list.\r\n\t * @param {any[]} newItems\r\n\t * @param {boolean} [options.preserveScrollPositionOnPrependItems] — Set to `true` to enable \"restore scroll position after prepending items\" feature (could be useful when implementing \"Show previous items\" button).\r\n\t */\r\n\tsetItems(newItems, options = {}) {\r\n\t\tthis.hasToBeStarted()\r\n\t\treturn this._setItems(newItems, options)\r\n\t}\r\n}"],"mappings":"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EAAuC;QACtC;MACA;;MACD,OAAO,KAAK4B,yCAAL,KAAmDH,mBAA1D;IACA;IAED;AACD;AACA;AACA;;;;WACC,4BAAmBI,IAAnB,EAAyB;MACxB9D,IAAI,CAAC,kFAAD,CAAJ;MACA,KAAK+D,qBAAL,CAA2BD,IAA3B;IACA;IAED;AACD;AACA;AACA;;;;WACC,+BAAsBP,WAAtB,EAAmC;MAClC;MACA;MACA;MACA,KAAKS,sBAAL,CAA4BT,WAA5B;IACA;IAED;AACD;AACA;AACA;AACA;;;;WACC,sBAAaA,WAAb,EAA0BU,YAA1B,EAAwC;MACvC;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA,KAAKC,aAAL,CAAmBX,WAAnB,EAAgCU,YAAhC;IACA,C,CAED;IACA;;;;WACA,2BAAkBH,IAAlB,EAAwBG,YAAxB,EAAsC;MACrC,KAAKE,YAAL,CAAkBL,IAAlB,EAAwBG,YAAxB;IACA;IAED;AACD;AACA;AACA;AACA;;;;WACC,kBAASG,QAAT,EAAiC;MAAA,IAAd3D,OAAc,uEAAJ,EAAI;MAChC,KAAKS,cAAL;MACA,OAAO,KAAKmD,SAAL,CAAeD,QAAf,EAAyB3D,OAAzB,CAAP;IACA;;;;;;SA9SmBH,e"}
|
|
@@ -111,7 +111,7 @@ export default function () {
|
|
|
111
111
|
// or an "Expand YouTube video" button, which would result
|
|
112
112
|
// in the actual height of the list item being different
|
|
113
113
|
// from what has been initially measured in `this.itemHeights[i]`,
|
|
114
|
-
// if the developer didn't call `.setItemState(
|
|
114
|
+
// if the developer didn't call `.setItemState(item, newState)` and `.onItemHeightDidChange(item)`.
|
|
115
115
|
|
|
116
116
|
|
|
117
117
|
if (!validateWillBeHiddenItemHeightsAreAccurate.call(this, firstShownItemIndex, lastShownItemIndex)) {
|
|
@@ -143,7 +143,7 @@ export default function () {
|
|
|
143
143
|
log('Last shown item index', lastShownItemIndex);
|
|
144
144
|
log('Before items height', beforeItemsHeight);
|
|
145
145
|
log('After items height (actual or estimated)', afterItemsHeight);
|
|
146
|
-
log('Average item height (used for estimated after items height calculation)', this.
|
|
146
|
+
log('Average item height (used for estimated after items height calculation)', this.getAverageItemHeight());
|
|
147
147
|
|
|
148
148
|
if (isDebug()) {
|
|
149
149
|
log('Item heights', this.getState().itemHeights.slice());
|
|
@@ -179,9 +179,9 @@ export default function () {
|
|
|
179
179
|
// Instead of using a `this.previouslyCalculatedLayout` instance variable,
|
|
180
180
|
// this code could use `this.getState()` because it reflects what's currently on screen,
|
|
181
181
|
// but there's a single edge case when it could go out of sync —
|
|
182
|
-
// updating item heights externally via `.onItemHeightDidChange(
|
|
182
|
+
// updating item heights externally via `.onItemHeightDidChange(item)`.
|
|
183
183
|
//
|
|
184
|
-
// If, for example, an item height was updated externally via `.onItemHeightDidChange(
|
|
184
|
+
// If, for example, an item height was updated externally via `.onItemHeightDidChange(item)`
|
|
185
185
|
// then `this.getState().itemHeights` would get updated immediately but
|
|
186
186
|
// `this.getState().beforeItemsHeight` or `this.getState().afterItemsHeight`
|
|
187
187
|
// would still correspond to the previous item height, so those would be "stale".
|
|
@@ -269,7 +269,7 @@ export default function () {
|
|
|
269
269
|
* or an "Expand YouTube video" button, which would result
|
|
270
270
|
* in the actual height of the list item being different
|
|
271
271
|
* from what has been initially measured in `this.itemHeights[i]`,
|
|
272
|
-
* if the developer didn't call `.setItemState(
|
|
272
|
+
* if the developer didn't call `.setItemState(item, newState)` and `.onItemHeightDidChange(item)`.
|
|
273
273
|
*/
|
|
274
274
|
|
|
275
275
|
|
|
@@ -283,26 +283,26 @@ export default function () {
|
|
|
283
283
|
// The item will be hidden. Re-measure its height.
|
|
284
284
|
// The rationale is that there could be a situation when an item's
|
|
285
285
|
// height has changed, and the developer has properly added an
|
|
286
|
-
// `.onItemHeightDidChange(
|
|
286
|
+
// `.onItemHeightDidChange(item)` call to notify `VirtualScroller`
|
|
287
287
|
// about that change, but at the same time that wouldn't work.
|
|
288
288
|
// For example, suppose there's a list of several items on a page,
|
|
289
289
|
// and those items are in "minimized" state (having height 100px).
|
|
290
290
|
// Then, a user clicks an "Expand all items" button, and all items
|
|
291
291
|
// in the list are expanded (expanded item height is gonna be 700px).
|
|
292
|
-
// `VirtualScroller` demands that `.onItemHeightDidChange(
|
|
292
|
+
// `VirtualScroller` demands that `.onItemHeightDidChange(item)` is called
|
|
293
293
|
// in such cases, and the developer has properly added the code to do that.
|
|
294
294
|
// So, if there were 10 "minimized" items visible on a page, then there
|
|
295
|
-
// will be 10 individual `.onItemHeightDidChange(
|
|
296
|
-
// But, as the first `.onItemHeightDidChange(
|
|
295
|
+
// will be 10 individual `.onItemHeightDidChange(item)` calls. No issues so far.
|
|
296
|
+
// But, as the first `.onItemHeightDidChange(item)` call executes, it immediately
|
|
297
297
|
// ("synchronously") triggers a re-layout, and that re-layout finds out
|
|
298
298
|
// that now, because the first item is big, it occupies most of the screen
|
|
299
299
|
// space, and only the first 3 items are visible on screen instead of 10,
|
|
300
300
|
// and so it leaves the first 3 items mounted and unmounts the rest 7.
|
|
301
301
|
// Then, after `VirtualScroller` has rerendered, the code returns to
|
|
302
|
-
// where it was executing, and calls `.onItemHeightDidChange(
|
|
302
|
+
// where it was executing, and calls `.onItemHeightDidChange(item)` for the
|
|
303
303
|
// second item. It also triggers an immediate re-layout that finds out
|
|
304
304
|
// that only the first 2 items are visible on screen, and it unmounts
|
|
305
|
-
// the third one too. After that, it calls `.onItemHeightDidChange(
|
|
305
|
+
// the third one too. After that, it calls `.onItemHeightDidChange(item)`
|
|
306
306
|
// for the third item, but that item is no longer rendered, so its height
|
|
307
307
|
// can't be measured, and the same's for all the rest of the original 10 items.
|
|
308
308
|
// So, even though the developer has written their code properly, the
|
|
@@ -322,7 +322,7 @@ export default function () {
|
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
isValid = false;
|
|
325
|
-
warn('Item index', i, 'is no longer visible and will be unmounted. Its height has changed from', previouslyMeasuredItemHeight, 'to', actualItemHeight, 'since it was last measured. This is not necessarily a bug, and could happen, for example, on screen width change, or when there\'re several `onItemHeightDidChange(
|
|
325
|
+
warn('Item index', i, 'is no longer visible and will be unmounted. Its height has changed from', previouslyMeasuredItemHeight, 'to', actualItemHeight, 'since it was last measured. This is not necessarily a bug, and could happen, for example, on screen width change, or when there\'re several `onItemHeightDidChange(item)` calls issued at the same time, and the first one triggers a re-layout before the rest of them have had a chance to be executed.');
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
|
|
@@ -384,49 +384,57 @@ export default function () {
|
|
|
384
384
|
return listTopOffset;
|
|
385
385
|
};
|
|
386
386
|
|
|
387
|
-
this._onItemHeightDidChange = function (
|
|
388
|
-
|
|
389
|
-
|
|
387
|
+
this._onItemHeightDidChange = function (itemOrIndex) {
|
|
388
|
+
// Item index.
|
|
389
|
+
var i = _this._getItemIndexByItemOrIndex(itemOrIndex); // If the item wasn't found, the error was already reported,
|
|
390
|
+
// so just don't do anything else.
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
if (i === undefined) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
390
396
|
|
|
391
397
|
var _this$getState2 = _this.getState(),
|
|
392
398
|
itemHeights = _this$getState2.itemHeights,
|
|
393
399
|
firstShownItemIndex = _this$getState2.firstShownItemIndex,
|
|
394
|
-
lastShownItemIndex = _this$getState2.lastShownItemIndex;
|
|
400
|
+
lastShownItemIndex = _this$getState2.lastShownItemIndex;
|
|
395
401
|
|
|
402
|
+
log('~ On Item Height Did Change was called ~');
|
|
403
|
+
log('Item index', i); // Check if the item is still rendered.
|
|
396
404
|
|
|
397
405
|
if (!(i >= firstShownItemIndex && i <= lastShownItemIndex)) {
|
|
398
406
|
// There could be valid cases when an item is no longer rendered
|
|
399
|
-
// by the time `.onItemHeightDidChange(
|
|
407
|
+
// by the time `.onItemHeightDidChange(item)` gets called.
|
|
400
408
|
// For example, suppose there's a list of several items on a page,
|
|
401
409
|
// and those items are in "minimized" state (having height 100px).
|
|
402
410
|
// Then, a user clicks an "Expand all items" button, and all items
|
|
403
411
|
// in the list are expanded (expanded item height is gonna be 700px).
|
|
404
|
-
// `VirtualScroller` demands that `.onItemHeightDidChange(
|
|
412
|
+
// `VirtualScroller` demands that `.onItemHeightDidChange(item)` is called
|
|
405
413
|
// in such cases, and the developer has properly added the code to do that.
|
|
406
414
|
// So, if there were 10 "minimized" items visible on a page, then there
|
|
407
|
-
// will be 10 individual `.onItemHeightDidChange(
|
|
408
|
-
// But, as the first `.onItemHeightDidChange(
|
|
415
|
+
// will be 10 individual `.onItemHeightDidChange(item)` calls. No issues so far.
|
|
416
|
+
// But, as the first `.onItemHeightDidChange(item)` call executes, it immediately
|
|
409
417
|
// ("synchronously") triggers a re-layout, and that re-layout finds out
|
|
410
418
|
// that now, because the first item is big, it occupies most of the screen
|
|
411
419
|
// space, and only the first 3 items are visible on screen instead of 10,
|
|
412
420
|
// and so it leaves the first 3 items mounted and unmounts the rest 7.
|
|
413
421
|
// Then, after `VirtualScroller` has rerendered, the code returns to
|
|
414
|
-
// where it was executing, and calls `.onItemHeightDidChange(
|
|
422
|
+
// where it was executing, and calls `.onItemHeightDidChange(item)` for the
|
|
415
423
|
// second item. It also triggers an immediate re-layout that finds out
|
|
416
424
|
// that only the first 2 items are visible on screen, and it unmounts
|
|
417
|
-
// the third one too. After that, it calls `.onItemHeightDidChange(
|
|
425
|
+
// the third one too. After that, it calls `.onItemHeightDidChange(item)`
|
|
418
426
|
// for the third item, but that item is no longer rendered, so its height
|
|
419
427
|
// can't be measured, and the same's for all the rest of the original 10 items.
|
|
420
428
|
// So, even though the developer has written their code properly, there're
|
|
421
429
|
// still situations when the item could be no longer rendered by the time
|
|
422
|
-
// `.onItemHeightDidChange(
|
|
423
|
-
return warn('The item is no longer rendered. This is not necessarily a bug, and could happen, for example, when when a developer calls `onItemHeightDidChange(
|
|
430
|
+
// `.onItemHeightDidChange(item)` gets called.
|
|
431
|
+
return warn('The item is no longer rendered. This is not necessarily a bug, and could happen, for example, when when a developer calls `onItemHeightDidChange(item)` while looping through a batch of items.');
|
|
424
432
|
}
|
|
425
433
|
|
|
426
434
|
var previousHeight = itemHeights[i];
|
|
427
435
|
|
|
428
436
|
if (previousHeight === undefined) {
|
|
429
|
-
// There're valid cases when the item still hasn't been measured and `onItemHeightDidChange()`
|
|
437
|
+
// There're valid cases when the item still hasn't been measured and `onItemHeightDidChange(item)`
|
|
430
438
|
// function was called for it. That's because measuring items is only done after the `VirtualScroller`
|
|
431
439
|
// has `start()`ed. But it's not neccessarily `start()`ed by the time it has been rendered (mounted).
|
|
432
440
|
// For example, the React component `<VirtualScroller/>` provides an `readyToStart={false}` property
|
|
@@ -446,7 +454,7 @@ export default function () {
|
|
|
446
454
|
try {
|
|
447
455
|
newHeight = remeasureItemHeight.call(_this, i);
|
|
448
456
|
} catch (error) {
|
|
449
|
-
// Successfully finishing an `onItemHeightDidChange(
|
|
457
|
+
// Successfully finishing an `onItemHeightDidChange(item)` call is not considered
|
|
450
458
|
// critical for `VirtualScroller`'s operation, so such errors could be ignored.
|
|
451
459
|
if (error instanceof ItemNotRenderedError) {
|
|
452
460
|
return reportError("\"onItemHeightDidChange()\" has been called for item index ".concat(i, " but the item is not currently rendered and can't be measured. The exact error was: ").concat(error.message));
|
|
@@ -468,7 +476,7 @@ export default function () {
|
|
|
468
476
|
updatePreviouslyCalculatedLayoutOnItemHeightChange.call(_this, i, previousHeight, newHeight); // Recalculate layout.
|
|
469
477
|
//
|
|
470
478
|
// If the `VirtualScroller` is already waiting for a state update to be rendered,
|
|
471
|
-
// delay `onItemHeightDidChange(
|
|
479
|
+
// delay `onItemHeightDidChange(item)`'s re-layout until that state update is rendered.
|
|
472
480
|
// The reason is that React `<VirtualScroller/>`'s `onHeightDidChange()` is meant to
|
|
473
481
|
// be called inside `useLayoutEffect()` hook. Due to how React is implemented internally,
|
|
474
482
|
// that might happen in the middle of the currently pending `setState()` operation
|
|
@@ -515,9 +523,12 @@ export default function () {
|
|
|
515
523
|
// scroll past the screen height, because they'd stop to read through
|
|
516
524
|
// the newly visible items first, and when they do stop scrolling, that's
|
|
517
525
|
// when layout gets recalculated).
|
|
518
|
-
|
|
526
|
+
return _this.scrollableContainer.getHeight() * _this.getPrerenderMarginRatio();
|
|
527
|
+
};
|
|
519
528
|
|
|
520
|
-
|
|
529
|
+
this.getPrerenderMarginRatio = function () {
|
|
530
|
+
// See the readme for the description of `prerenderMarginRatio` option.
|
|
531
|
+
return 1; // in scrollable container heights.
|
|
521
532
|
};
|
|
522
533
|
/**
|
|
523
534
|
* Calls `onItemFirstRender()` for items that haven't been
|