virtual-scroller 1.16.2 → 1.16.4
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 +7 -0
- package/README.md +75 -35
- 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/DOM/ListTopOffsetWatcher.js +2 -4
- package/commonjs/DOM/ListTopOffsetWatcher.js.map +1 -1
- package/commonjs/Layout.test.js +26 -24
- package/commonjs/Layout.test.js.map +1 -1
- package/commonjs/Scroll.js +8 -11
- package/commonjs/Scroll.js.map +1 -1
- package/commonjs/VirtualScroller.constructor.js +7 -8
- package/commonjs/VirtualScroller.constructor.js.map +1 -1
- package/commonjs/VirtualScroller.js +10 -1
- package/commonjs/VirtualScroller.js.map +1 -1
- package/commonjs/VirtualScroller.layout.js +62 -20
- package/commonjs/VirtualScroller.layout.js.map +1 -1
- package/commonjs/VirtualScroller.onRender.js +25 -2
- package/commonjs/VirtualScroller.onRender.js.map +1 -1
- package/commonjs/VirtualScroller.state.js +39 -0
- package/commonjs/VirtualScroller.state.js.map +1 -1
- package/commonjs/react/useCreateVirtualScroller.js +1 -1
- package/commonjs/react/useCreateVirtualScroller.js.map +1 -1
- package/commonjs/test/VirtualScroller.js +8 -3
- package/commonjs/test/VirtualScroller.js.map +1 -1
- package/commonjs/utility/px.test.js +9 -5
- package/commonjs/utility/px.test.js.map +1 -1
- package/commonjs/utility/scheduleOnNextTick.js +25 -0
- package/commonjs/utility/scheduleOnNextTick.js.map +1 -0
- package/index.d.ts +4 -0
- package/modules/DOM/ListTopOffsetWatcher.js +1 -6
- package/modules/DOM/ListTopOffsetWatcher.js.map +1 -1
- package/modules/Layout.test.js +20 -19
- package/modules/Layout.test.js.map +1 -1
- package/modules/Scroll.js +8 -11
- package/modules/Scroll.js.map +1 -1
- package/modules/VirtualScroller.constructor.js +7 -8
- package/modules/VirtualScroller.constructor.js.map +1 -1
- package/modules/VirtualScroller.js +9 -1
- package/modules/VirtualScroller.js.map +1 -1
- package/modules/VirtualScroller.layout.js +61 -23
- package/modules/VirtualScroller.layout.js.map +1 -1
- package/modules/VirtualScroller.onRender.js +24 -2
- package/modules/VirtualScroller.onRender.js.map +1 -1
- package/modules/VirtualScroller.state.js +39 -1
- package/modules/VirtualScroller.state.js.map +1 -1
- package/modules/react/useCreateVirtualScroller.js +1 -1
- package/modules/react/useCreateVirtualScroller.js.map +1 -1
- package/modules/test/VirtualScroller.js +5 -2
- package/modules/test/VirtualScroller.js.map +1 -1
- package/modules/utility/px.test.js +5 -3
- package/modules/utility/px.test.js.map +1 -1
- package/modules/utility/scheduleOnNextTick.js +15 -0
- package/modules/utility/scheduleOnNextTick.js.map +1 -0
- package/package.json +6 -5
- package/source/DOM/ListTopOffsetWatcher.js +0 -6
- package/source/Layout.test.js +21 -20
- package/source/Scroll.js +7 -9
- package/source/VirtualScroller.constructor.js +7 -7
- package/source/VirtualScroller.js +8 -0
- package/source/VirtualScroller.layout.js +61 -25
- package/source/VirtualScroller.onRender.js +29 -3
- package/source/VirtualScroller.state.js +38 -0
- package/source/react/useCreateVirtualScroller.js +1 -1
- package/source/test/VirtualScroller.js +6 -2
- package/source/utility/px.test.js +6 -3
- package/source/utility/scheduleOnNextTick.js +16 -0
|
@@ -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","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\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\t// Perform the initial render of the list.\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":";;;;;;;;;;;;AAAA,OAAOA,0BAAP,MAAuC,kCAAvC;AACA,SAASC,aAAT,QAA8B,aAA9B;AACA,OAAOC,GAAP,IAAcC,IAAd,EAAoBC,WAApB,QAAuC,oBAAvC;AAEA,SACCC,aADD,EAECC,cAFD,EAGCC,cAHD,EAICC,2BAJD,QAKO,gBALP;;IAOqBC,e;EACpB;AACD;AACA;AACA;AACA;AACA;EACC,yBACCC,wBADD,EAECC,KAFD,EAIE;IAAA;;IAAA,IADDC,OACC,uEADS,EACT;;IAAA;;IAAA,8BAgJK,YAAM;MACZ,IAAI,CAAC,KAAI,CAACC,SAAV,EAAqB;QACpB,MAAM,IAAIC,KAAJ,CAAU,8EAAV,CAAN;MACA;;MAED,KAAI,CAACD,SAAL,GAAiB,KAAjB;MAEAX,GAAG,CAAC,UAAD,CAAH;;MAEA,KAAI,CAACa,gCAAL,CAAsCC,IAAtC;;MACA,KAAI,CAACC,MAAL,CAAYD,IAAZ,GAVY,CAYZ;MACA;MACA;;;MACA,IAAI,KAAI,CAACE,oBAAL,IAA6B,KAAI,CAACA,oBAAL,CAA0BC,SAA1B,EAAjC,EAAwE;QACvE,KAAI,CAACD,oBAAL,CAA0BF,IAA1B;MACA,CAjBW,CAmBZ;;;MACA,KAAI,CAACI,iBAAL,CAAuB,EAAvB;IACA,CArKC;;IAAA,sCA+Ka,YAAM;MACpB,KAAI,CAACC,cAAL;;MACA,KAAI,CAACC,wBAAL,CAA8B;QAAEC,MAAM,EAAEtB,aAAa,CAACuB;MAAxB,CAA9B;IACA,CAlLC;;IAAA,kCAsLS,YAAM;MAChB,KAAI,CAACC,SAAL,CAAe,KAAI,CAACC,QAAL,EAAf,EAAgC,KAAI,CAACC,aAArC;IACA,CAxLC;;IACD3B,0BAA0B,CAAC4B,IAA3B,CACC,IADD,EAEClB,wBAFD,EAGCC,KAHD,EAICC,OAJD;EAMA;EAED;AACD;AACA;;;;;WACC,iBAAQ;MACP,IAAI,KAAKC,SAAT,EAAoB;QACnB,MAAM,IAAIC,KAAJ,CAAU,+DAAV,CAAN;MACA,CAHM,CAKP;;;MACA,IAAMe,SAAS,GAAG,KAAKhB,SAAL,KAAmB,KAArC;;MAEA,IAAI,CAACgB,SAAL,EAAgB;QACf,KAAKC,UAAL;QACA,KAAKC,gBAAL,GAAwB,IAAxB,CAFe,CAIf;QACA;;QACA,IAAI,KAAKC,OAAT,EAAkB;UACjB;UACA,KAAKA,OAAL,CAAa,KAAKN,QAAL,EAAb;QACA;MACD;;MAED,IAAIG,SAAJ,EAAe;QACd3B,GAAG,CAAC,qBAAD,CAAH;MACA,CAFD,MAEO;QACNA,GAAG,CAAC,WAAD,CAAH;MACA,CAxBM,CA0BP;;;MACA,KAAKW,SAAL,GAAiB,IAAjB,CA3BO,CA6BP;;MACA,KAAKoB,qBAAL,CAA2BC,KAA3B,GA9BO,CAgCP;;MACA,KAAKC,WAAL,GAAmBC,SAAnB,CAjCO,CAmCP;;MACA,KAAKC,kBAAL,GAA0BD,SAA1B,CApCO,CAsCP;MACA;MACA;MACA;;MACA,IAAI,CAAC,KAAKE,cAAL,EAAL,EAA4B;QAC3B,IAAI,KAAKC,gCAAL,EAAJ,EAA6C;UAC5C,IAAIlC,aAAa,EAAjB,EAAqB;YACpB,IAAI,CAACC,cAAc,CAAC,KAAKI,wBAAL,EAAD,CAAnB,EAAsD;cACrDR,GAAG,CAAC,wBAAD,CAAH;cACAK,cAAc,CAAC,KAAKG,wBAAL,EAAD,CAAd;YACA;UACD,CALD,MAKO;YACNR,GAAG,CAAC,sCAAD,CAAH;YACAE,WAAW,CAACI,2BAAD,CAAX;YACAN,GAAG,CAAC,yBAAD,CAAH;YACA,KAAKsC,OAAL,GAAe,IAAf;UACA;QACD;MACD,CAxDM,CA0DP;MACA;MACA;MACA;MACA;;;MACA,IAAIC,WAAW,GAAG,KAAKC,qCAAvB;MACA,KAAKA,qCAAL,GAA6CN,SAA7C,CAhEO,CAkEP;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;;MACA,KAAKO,eAAL,GAAuBP,SAAvB;MAEA,IAAMQ,0BAA0B,GAAG,KAAKC,4BAAL,EAAnC;;MACA,IAAID,0BAAJ,EAAgC;QAC/BH,WAAW,mCACPA,WADO,GAEPG,0BAFO,CAAX;MAIA;;MAED,KAAK7B,gCAAL,CAAsC+B,KAAtC;MACA,KAAK7B,MAAL,CAAY6B,KAAZ,GAtFO,CAwFP;MACA;;MACA,IAAI,KAAKpB,QAAL,GAAgBqB,wBAAhB,KAA6CX,SAAjD,EAA4D;QAC3D,IAAMW,wBAAwB,GAAG,KAAKC,mBAAL,CAAyBC,QAAzB,EAAjC;QACAR,WAAW,mCACPA,WADO;UAEVM,wBAAwB,EAAxBA;QAFU,EAAX;MAIA,CAND,MAMO;QACN;QACA;QACA;QACA,IAAMG,QAAQ,GAAG,KAAKF,mBAAL,CAAyBC,QAAzB,EAAjB;QACA,IAAME,SAAS,GAAG,KAAKzB,QAAL,GAAgBqB,wBAAlC;;QACA,IAAIG,QAAQ,KAAKC,SAAjB,EAA4B;UAC3BjD,GAAG,CAAC,2CAAD,EAA8CiD,SAA9C,EAAyD,IAAzD,EAA+DD,QAA/D,EAAyE,GAAzE,CAAH,CAD2B,CAE3B;UACA;UACA;UACA;;UACA,OAAO,KAAKE,iBAAL,EAAP;QACA;MACD,CA9GM,CAgHP;MACA;MACA;;;MACA,IAAI,KAAKC,uBAAT,EAAkC;QACjC,IAAMC,YAAY,GAAG,KAAKC,qBAAL,EAArB;QACA,IAAMC,qBAAqB,GAAG,KAAK9B,QAAL,GAAgB4B,YAAhB,IAAgC,CAA9D;;QACA,IAAIA,YAAY,KAAKE,qBAArB,EAA4C;UAC3C,OAAO,KAAKJ,iBAAL,EAAP;QACA;MACD,CAzHM,CA2HP;MACA;MACA;MACA;MACA;;;MACA,KAAK9B,wBAAL,CAA8B;QAAEC,MAAM,EAAEtB,aAAa,CAACwD,OAAxB;QAAiChB,WAAW,EAAXA;MAAjC,CAA9B;IACA,C,CAED;;;;WAwBA,0BAAiB;MAChB,IAAI,CAAC,KAAK5B,SAAV,EAAqB;QACpB,MAAM,IAAIC,KAAJ,CAAU,2DAAV,CAAN;MACA;IACD,C,CAED;IACA;;;;;IAYA;AACD;AACA;AACA;AACA;IACC,+BAAsB4C,WAAtB,EAAmC;MAClC;MACA,IAAMC,CAAC,GAAG,KAAKC,0BAAL,CAAgCF,WAAhC,CAAV,CAFkC,CAIlC;MACA;;;MACA,IAAIC,CAAC,KAAKvB,SAAV,EAAqB;QACpB;MACA;;MAED,IAAMyB,mBAAmB,GAAG,KAAKC,MAAL,CAAYC,gBAAZ,CAA6BJ,CAA7B,CAA5B;;MACA,IAAIE,mBAAmB,KAAKzB,SAA5B,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;;;;;;SAhTmBH,e"}
|
|
1
|
+
{"version":3,"file":"VirtualScroller.js","names":["VirtualScrollerConstructor","LAYOUT_REASON","unscheduleOnNextTick","log","warn","reportError","supportsTbody","hasTbodyStyles","addTbodyStyles","BROWSER_NOT_SUPPORTED_ERROR","VirtualScroller","getItemsContainerElement","items","options","_isActive","Error","scrollableContainerResizeHandler","stop","scroll","listTopOffsetWatcher","isStarted","cancelLayoutTimer","_stateUpdateSequenceStartedAtResetTimer","undefined","hasToBeStarted","onUpdateShownItemIndexes","reason","MANUAL","_onRender","getState","previousState","call","isRestart","setUpState","waitingForRender","_render","listHeightMeasurement","reset","_isResizing","_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 { unscheduleOnNextTick } from './utility/scheduleOnNextTick.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\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\t// Perform the initial render of the list.\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\t// Any pending state update is discarded here.\r\n\t\tthis.cancelLayoutTimer({})\r\n\r\n\t\t// Cancel any scheduled \"recursive re-render duration\" timer.\r\n\t\tif (this._stateUpdateSequenceStartedAtResetTimer !== undefined) {\r\n\t\t\tunscheduleOnNextTick(this._stateUpdateSequenceStartedAtResetTimer)\r\n\t\t\tthis._stateUpdateSequenceStartedAtResetTimer = undefined\r\n\t\t}\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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"}
|
|
@@ -4,12 +4,8 @@ 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
|
-
// For some weird reason, in Chrome, `setTimeout()` would lag up to a second (or more) behind.
|
|
8
|
-
// Turns out, Chrome developers have deprecated `setTimeout()` API entirely without asking anyone.
|
|
9
|
-
// Replacing `setTimeout()` with `requestAnimationFrame()` can work around that Chrome bug.
|
|
10
|
-
// https://github.com/bvaughn/react-virtualized/issues/722
|
|
11
|
-
import { setTimeout, clearTimeout } from 'request-animation-frame-timeout';
|
|
12
7
|
import log, { warn, isDebug, reportError } from './utility/debug.js';
|
|
8
|
+
import { scheduleOnNextTick, unscheduleOnNextTick } from './utility/scheduleOnNextTick.js';
|
|
13
9
|
import { LAYOUT_REASON } from './Layout.js';
|
|
14
10
|
import ItemNotRenderedError from './ItemNotRenderedError.js';
|
|
15
11
|
export default function () {
|
|
@@ -19,21 +15,39 @@ export default function () {
|
|
|
19
15
|
var reason = _ref.reason,
|
|
20
16
|
stateUpdate = _ref.stateUpdate;
|
|
21
17
|
|
|
22
|
-
// In case of "don't
|
|
18
|
+
// In case of "don't perform this particular re-layout
|
|
19
|
+
// because a more-priority one is already scheduled".
|
|
23
20
|
var skip = function skip() {
|
|
24
21
|
if (stateUpdate) {
|
|
22
|
+
// Don't update layout. Just update some state.
|
|
23
|
+
// `stateUpdate` isn't supposed to affect the layout in any way
|
|
24
|
+
// because it doesn't contain any properties that affect the rendering of the list.
|
|
25
|
+
// I.e. it doesn't contain properties like `beforeItemsHeight` or `afterItemsHeight`, etc.
|
|
25
26
|
_this.updateState(stateUpdate);
|
|
26
27
|
}
|
|
27
28
|
}; // If new `items` have been set and are waiting to be applied,
|
|
28
29
|
// or if the viewport width has changed requiring a re-layout,
|
|
29
30
|
// then temporarily stop all other updates like "on scroll" updates.
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
// `
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
31
|
+
//
|
|
32
|
+
// This prevents `state` from becoming inconsistent, because several
|
|
33
|
+
// "contenders" (layout reasons) could be attempting to update
|
|
34
|
+
// `VirtualScroller` state in parallel between two successive renders.
|
|
35
|
+
//
|
|
36
|
+
// For example, both `VirtualScroller.setItems()` and `VirtualScroller.scroll.onScroll()`
|
|
37
|
+
// "contenders" could be attempting to update `VirtualScroller` state in parallel,
|
|
38
|
+
// and the layout calculated by one of them would overwrite the layout calculated by another.
|
|
39
|
+
//
|
|
40
|
+
// Which one should have the priority in such case then?
|
|
41
|
+
// In this specific example, `VirtualScroller.setItems()` should be the one
|
|
42
|
+
// because only it knows the correct new `items` whereas other "contenders"
|
|
43
|
+
// have the old stale `items`.
|
|
44
|
+
//
|
|
45
|
+
// Another example of a "contender" that should have a priority is
|
|
46
|
+
// "on list width has changed" handler because only it has access
|
|
47
|
+
// to the correct new width of the list rather than the old stale width.
|
|
48
|
+
//
|
|
49
|
+
// So in these two "priority" cases any other re-layout should be ignored.
|
|
50
|
+
//
|
|
37
51
|
|
|
38
52
|
|
|
39
53
|
if (_this.newItemsWillBeRendered || _this.widthHasChanged || _this._isResizing) {
|
|
@@ -43,11 +57,12 @@ export default function () {
|
|
|
43
57
|
|
|
44
58
|
if (_this.getItemsCount() === 0) {
|
|
45
59
|
return skip();
|
|
46
|
-
} // Cancel a "re-layout when user stops scrolling" timer
|
|
60
|
+
} // Cancel a scheduled "re-layout when user stops scrolling" timer
|
|
61
|
+
// because a re-layout will be performed here now.
|
|
47
62
|
|
|
48
63
|
|
|
49
|
-
_this.scroll.cancelScheduledLayout(); // Cancel a re-layout that is scheduled to run at the next "frame",
|
|
50
|
-
// because a re-layout will be performed
|
|
64
|
+
_this.scroll.cancelScheduledLayout(); // Cancel a scheduled re-layout that is scheduled to run at the next "frame",
|
|
65
|
+
// because a re-layout will be performed here now.
|
|
51
66
|
|
|
52
67
|
|
|
53
68
|
stateUpdate = _this.cancelLayoutTimer({
|
|
@@ -132,7 +147,24 @@ export default function () {
|
|
|
132
147
|
|
|
133
148
|
if (layoutDuration < SLOW_LAYOUT_DURATION) {// log('Calculated in', layoutDuration, 'ms')
|
|
134
149
|
} else {
|
|
135
|
-
|
|
150
|
+
// Getting this warning would mean that for a given (large) count of items,
|
|
151
|
+
// calculating `beforeItemsHeight` or `afterItemsHeight` requires too much time
|
|
152
|
+
// in order to iterate over all not-currently-visible items to add up their heights.
|
|
153
|
+
//
|
|
154
|
+
// Because layout calculation is a blocking procedure, it "freezes" the application
|
|
155
|
+
// for a given amount of milliseconds. If the amount of those milliseconds is longer
|
|
156
|
+
// than 16ms, frames are being dropped in a 60fps scenario.
|
|
157
|
+
// Modern phones even use 120fps as a baseline, so the timeframe requirement
|
|
158
|
+
// gets even more strict.
|
|
159
|
+
//
|
|
160
|
+
// What could be a way of dealing with this?
|
|
161
|
+
//
|
|
162
|
+
// * The code could cache clustered item heights in order to perform less add-up operations.
|
|
163
|
+
// For example, the code could cache the total height of items 1..100, 101..200, etc.
|
|
164
|
+
// Then 1..1000, 1001..2000, etc. Then 1..10000, 10001..20000, etc. So the clusters
|
|
165
|
+
// would just get progressively larger as more `items` are added.
|
|
166
|
+
//
|
|
167
|
+
warn('Layout calculation time too high:', layoutDuration, 'ms');
|
|
136
168
|
}
|
|
137
169
|
|
|
138
170
|
if (this.getColumnsCount()) {
|
|
@@ -143,7 +175,7 @@ export default function () {
|
|
|
143
175
|
log('Last shown item index', lastShownItemIndex);
|
|
144
176
|
log('Before items height', beforeItemsHeight);
|
|
145
177
|
log('After items height (actual or estimated)', afterItemsHeight);
|
|
146
|
-
log('Average item height (used
|
|
178
|
+
log('Average item height (will be used to calculate estimated "after items height")', this.getAverageItemHeight());
|
|
147
179
|
|
|
148
180
|
if (isDebug()) {
|
|
149
181
|
log('Item heights', this.getState().itemHeights.slice());
|
|
@@ -436,7 +468,7 @@ export default function () {
|
|
|
436
468
|
if (previousHeight === undefined) {
|
|
437
469
|
// There're valid cases when the item still hasn't been measured and `onItemHeightDidChange(item)`
|
|
438
470
|
// function was called for it. That's because measuring items is only done after the `VirtualScroller`
|
|
439
|
-
// has `start()`ed. But it's not
|
|
471
|
+
// has `start()`ed. But it's not necessarily `start()`ed by the time it has been rendered (mounted).
|
|
440
472
|
// For example, the React component `<VirtualScroller/>` provides an `readyToStart={false}` property
|
|
441
473
|
// in order to let the application finish initializing itself before starting the `VirtualScroller`.
|
|
442
474
|
// So there're legitimate cases when a `VirtualScroller` is already rendered but is not `start()`ed yet.
|
|
@@ -580,7 +612,7 @@ export default function () {
|
|
|
580
612
|
var stateUpdate = _ref3.stateUpdate;
|
|
581
613
|
|
|
582
614
|
if (_this.layoutTimer) {
|
|
583
|
-
|
|
615
|
+
unscheduleOnNextTick(_this.layoutTimer);
|
|
584
616
|
_this.layoutTimer = undefined; // Merge state updates.
|
|
585
617
|
|
|
586
618
|
if (stateUpdate || _this.layoutTimerStateUpdate) {
|
|
@@ -597,7 +629,7 @@ export default function () {
|
|
|
597
629
|
var reason = _ref4.reason,
|
|
598
630
|
stateUpdate = _ref4.stateUpdate;
|
|
599
631
|
_this.layoutTimerStateUpdate = stateUpdate;
|
|
600
|
-
_this.layoutTimer =
|
|
632
|
+
_this.layoutTimer = scheduleOnNextTick(function () {
|
|
601
633
|
_this.layoutTimerStateUpdate = undefined;
|
|
602
634
|
_this.layoutTimer = undefined;
|
|
603
635
|
|
|
@@ -605,8 +637,14 @@ export default function () {
|
|
|
605
637
|
reason: reason,
|
|
606
638
|
stateUpdate: stateUpdate
|
|
607
639
|
});
|
|
608
|
-
}
|
|
640
|
+
});
|
|
609
641
|
};
|
|
610
642
|
}
|
|
611
|
-
var
|
|
643
|
+
var FPS = 60; // though 120 fps is common on smartphones these days.
|
|
644
|
+
|
|
645
|
+
var FRAME_DURATION = 1000 / FPS; // in milliseconds
|
|
646
|
+
// Layout is considered "too slow" when it "blocks" the "main thread"
|
|
647
|
+
// for so long that it starts causing "frame drops".
|
|
648
|
+
|
|
649
|
+
var SLOW_LAYOUT_DURATION = FRAME_DURATION;
|
|
612
650
|
//# sourceMappingURL=VirtualScroller.layout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualScroller.layout.js","names":["setTimeout","clearTimeout","log","warn","isDebug","reportError","LAYOUT_REASON","ItemNotRenderedError","onUpdateShownItemIndexes","reason","stateUpdate","skip","updateState","newItemsWillBeRendered","widthHasChanged","_isResizing","getItemsCount","scroll","cancelScheduledLayout","cancelLayoutTimer","updateShownItemIndexes","call","startedAt","Date","now","getShownItemIndexes","firstShownItemIndex","lastShownItemIndex","shownItemsHeight","firstNonMeasuredItemIndex","listHeightMeasurement","hasSnapshot","getAnchorItemIndex","undefined","validateWillBeHiddenItemHeightsAreAccurate","beforeItemsHeight","layout","getBeforeItemsHeight","afterItemsHeight","getAfterItemsHeight","layoutDuration","isInBypassMode","SLOW_LAYOUT_DURATION","getColumnsCount","getAverageItemHeight","getState","itemHeights","slice","itemStates","onBeforeShowItems","items","previouslyCalculatedLayout","getCoordinatesOfVisibleAreaInsideTheList","visibleAreaBounds","getVisibleAreaBounds","latestLayoutVisibleArea","listTopOffsetInsideScrollableContainer","getListTopOffsetInsideScrollableContainer","top","bottom","itemsCount","visibleAreaInsideTheList","isVisible","itemsContainer","getHeight","getPrerenderMargin","getNonVisibleListShownItemIndexes","isValid","i","previouslyMeasuredItemHeight","actualItemHeight","remeasureItemHeight","updatePreviouslyCalculatedLayoutOnItemHeightChange","previousHeight","newHeight","prevLayout","heightDifference","listTopOffset","scrollableContainer","getItemsContainerTopOffset","listTopOffsetWatcher","onListTopOffset","_onItemHeightDidChange","itemOrIndex","_getItemIndexByItemOrIndex","error","message","_isActive","waitingForRender","updateLayoutAfterRenderBecauseItemHeightChanged","ITEM_HEIGHT_CHANGED","itemHeightsThatChangedWhileNewItemsWereBeingRendered","String","getPrerenderMarginRatio","onItemInitialRender","measureItemHeightsAndSpacing","measureItemHeights","verticalSpacing","measureVerticalSpacingIfNotMeasured","layoutTimer","layoutTimerStateUpdate","scheduleLayoutTimer"],"sources":["../source/VirtualScroller.layout.js"],"sourcesContent":["// For some weird reason, in Chrome, `setTimeout()` would lag up to a second (or more) behind.\r\n// Turns out, Chrome developers have deprecated `setTimeout()` API entirely without asking anyone.\r\n// Replacing `setTimeout()` with `requestAnimationFrame()` can work around that Chrome bug.\r\n// https://github.com/bvaughn/react-virtualized/issues/722\r\nimport { setTimeout, clearTimeout } from 'request-animation-frame-timeout'\r\n\r\nimport log, { warn, isDebug, reportError } from './utility/debug.js'\r\nimport { LAYOUT_REASON } from './Layout.js'\r\n\r\nimport ItemNotRenderedError from './ItemNotRenderedError.js'\r\n\r\nexport default function() {\r\n\tthis.onUpdateShownItemIndexes = ({ reason, stateUpdate }) => {\r\n\t\t// In case of \"don't do anything\".\r\n\t\tconst skip = () => {\r\n\t\t\tif (stateUpdate) {\r\n\t\t\t\tthis.updateState(stateUpdate)\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// If new `items` have been set and are waiting to be applied,\r\n\t\t// or if the viewport width has changed requiring a re-layout,\r\n\t\t// then temporarily stop all other updates like \"on scroll\" updates.\r\n\t\t// This prevents `state` being inconsistent, because, for example,\r\n\t\t// both `setItems()` and this function could update `VirtualScroller` state\r\n\t\t// and having them operate in parallel could result in incorrectly calculated\r\n\t\t// `beforeItemsHeight` / `afterItemsHeight` / `firstShownItemIndex` /\r\n\t\t// `lastShownItemIndex`, because, when operating in parallel, this function\r\n\t\t// would have different `items` than the `setItems()` function, so their\r\n\t\t// results could diverge.\r\n\t\tif (this.newItemsWillBeRendered || this.widthHasChanged || this._isResizing) {\r\n\t\t\treturn skip()\r\n\t\t}\r\n\r\n\t\t// If there're no items then there's no need to re-layout anything.\r\n\t\tif (this.getItemsCount() === 0) {\r\n\t\t\treturn skip()\r\n\t\t}\r\n\r\n\t\t// Cancel a \"re-layout when user stops scrolling\" timer.\r\n\t\tthis.scroll.cancelScheduledLayout()\r\n\r\n\t\t// Cancel a re-layout that is scheduled to run at the next \"frame\",\r\n\t\t// because a re-layout will be performed right now.\r\n\t\tstateUpdate = this.cancelLayoutTimer({ stateUpdate })\r\n\r\n\t\t// Perform a re-layout.\r\n\t\tlog(`~ Update Layout (on ${reason}) ~`)\r\n\t\tupdateShownItemIndexes.call(this, { stateUpdate })\r\n\t}\r\n\r\n\t/**\r\n\t * Updates the \"from\" and \"to\" shown item indexes.\r\n\t * If the list is visible and some of the items being shown are new\r\n\t * and are required to be measured first, then\r\n\t * `firstNonMeasuredItemIndex` is defined.\r\n\t * If the list is visible and all items being shown have been encountered\r\n\t * (and measured) before, then `firstNonMeasuredItemIndex` is `undefined`.\r\n\t *\r\n\t * The `stateUpdate` parameter is just an optional \"additional\" state update.\r\n\t */\r\n\tfunction updateShownItemIndexes({ stateUpdate }) {\r\n\t\tconst startedAt = Date.now()\r\n\r\n\t\t// Get shown item indexes.\r\n\t\tlet {\r\n\t\t\tfirstShownItemIndex,\r\n\t\t\tlastShownItemIndex,\r\n\t\t\tshownItemsHeight,\r\n\t\t\tfirstNonMeasuredItemIndex\r\n\t\t} = getShownItemIndexes.call(this)\r\n\r\n\t\t// If scroll position is scheduled to be restored after render,\r\n\t\t// then the \"anchor\" item must be rendered, and all of the prepended\r\n\t\t// items before it, all in a single pass. This way, all of the\r\n\t\t// prepended items' heights could be measured right after the render\r\n\t\t// has finished, and the scroll position can then be immediately restored.\r\n\t\tif (this.listHeightMeasurement.hasSnapshot()) {\r\n\t\t\tif (lastShownItemIndex < this.listHeightMeasurement.getAnchorItemIndex()) {\r\n\t\t\t\tlastShownItemIndex = this.listHeightMeasurement.getAnchorItemIndex()\r\n\t\t\t}\r\n\t\t\t// `firstShownItemIndex` is always `0` when prepending items.\r\n\t\t\t// And `lastShownItemIndex` always covers all prepended items in this case.\r\n\t\t\t// None of the prepended items have been rendered before,\r\n\t\t\t// so their heights are unknown. The code at the start of this function\r\n\t\t\t// did therefore set `firstNonMeasuredItemIndex` to non-`undefined`\r\n\t\t\t// in order to render just the first prepended item in order to\r\n\t\t\t// measure it, and only then make a decision on how many other\r\n\t\t\t// prepended items to render. But since we've instructed the code\r\n\t\t\t// to show all of the prepended items at once, there's no need to\r\n\t\t\t// \"redo layout after render\". Additionally, if layout was re-done\r\n\t\t\t// after render, then there would be a short interval of visual\r\n\t\t\t// \"jitter\" due to the scroll position not being restored because it'd\r\n\t\t\t// wait for the second layout to finish instead of being restored\r\n\t\t\t// right after the first one.\r\n\t\t\tfirstNonMeasuredItemIndex = undefined\r\n\t\t}\r\n\r\n\t\t// Validate the heights of items to be hidden on next render.\r\n\t\t// For example, a user could click a \"Show more\" button,\r\n\t\t// or an \"Expand YouTube video\" button, which would result\r\n\t\t// in the actual height of the list item being different\r\n\t\t// from what has been initially measured in `this.itemHeights[i]`,\r\n\t\t// if the developer didn't call `.setItemState(item, newState)` and `.onItemHeightDidChange(item)`.\r\n\t\tif (!validateWillBeHiddenItemHeightsAreAccurate.call(this, firstShownItemIndex, lastShownItemIndex)) {\r\n\t\t\tlog('~ Because some of the will-be-hidden item heights (listed above) have changed since they\\'ve last been measured, redo layout. ~')\r\n\t\t\t// Redo layout, now with the correct item heights.\r\n\t\t\treturn updateShownItemIndexes.call(this, { stateUpdate });\r\n\t\t}\r\n\r\n\t\t// Measure \"before\" items height.\r\n\t\tconst beforeItemsHeight = this.layout.getBeforeItemsHeight(\r\n\t\t\tfirstShownItemIndex\r\n\t\t)\r\n\r\n\t\t// Measure \"after\" items height.\r\n\t\tconst afterItemsHeight = this.layout.getAfterItemsHeight(\r\n\t\t\tlastShownItemIndex,\r\n\t\t\tthis.getItemsCount()\r\n\t\t)\r\n\r\n\t\tconst layoutDuration = Date.now() - startedAt\r\n\r\n\t\t// Debugging.\r\n\t\tlog('~ Calculated Layout' + (this.isInBypassMode() ? ' (bypass)' : '') + ' ~')\r\n\t\tif (layoutDuration < SLOW_LAYOUT_DURATION) {\r\n\t\t\t// log('Calculated in', layoutDuration, 'ms')\r\n\t\t} else {\r\n\t\t\twarn('Layout calculated in', layoutDuration, 'ms')\r\n\t\t}\r\n\t\tif (this.getColumnsCount()) {\r\n\t\t\tlog('Columns count', this.getColumnsCount())\r\n\t\t}\r\n\t\tlog('First shown item index', firstShownItemIndex)\r\n\t\tlog('Last shown item index', lastShownItemIndex)\r\n\t\tlog('Before items height', beforeItemsHeight)\r\n\t\tlog('After items height (actual or estimated)', afterItemsHeight)\r\n\t\tlog('Average item height (used for estimated after items height calculation)', this.getAverageItemHeight())\r\n\t\tif (isDebug()) {\r\n\t\t\tlog('Item heights', this.getState().itemHeights.slice())\r\n\t\t\tlog('Item states', this.getState().itemStates.slice())\r\n\t\t}\r\n\r\n\t\t// Optionally preload items to be rendered.\r\n\t\tthis.onBeforeShowItems(\r\n\t\t\tthis.getState().items,\r\n\t\t\tthis.getState().itemHeights,\r\n\t\t\tfirstShownItemIndex,\r\n\t\t\tlastShownItemIndex\r\n\t\t)\r\n\r\n\t\t// Set `this.firstNonMeasuredItemIndex`.\r\n\t\tthis.firstNonMeasuredItemIndex = firstNonMeasuredItemIndex\r\n\t\t// if (firstNonMeasuredItemIndex !== undefined) {\r\n\t\t// \tlog('Non-measured item index that will be measured at next layout', firstNonMeasuredItemIndex)\r\n\t\t// }\r\n\r\n\t\t// Set \"previously calculated layout\".\r\n\t\t//\r\n\t\t// The \"previously calculated layout\" feature is not currently used.\r\n\t\t//\r\n\t\t// The current layout snapshot could be stored as a \"previously calculated layout\" variable\r\n\t\t// so that it could theoretically be used when calculating new layout incrementally\r\n\t\t// rather than from scratch, which would be an optimization.\r\n\t\t//\r\n\t\t// Currently, this feature is not used, and `shownItemsHeight` property\r\n\t\t// is not returned at all, so don't set any \"previously calculated layout\".\r\n\t\t//\r\n\t\tif (shownItemsHeight === undefined) {\r\n\t\t\tthis.previouslyCalculatedLayout = undefined\r\n\t\t} else {\r\n\t\t\t// If \"previously calculated layout\" feature would be implmeneted,\r\n\t\t\t// then this code would set \"previously calculate layout\" instance variable.\r\n\t\t\t//\r\n\t\t\t// What for would this instance variable be used?\r\n\t\t\t//\r\n\t\t\t// Instead of using a `this.previouslyCalculatedLayout` instance variable,\r\n\t\t\t// this code could use `this.getState()` because it reflects what's currently on screen,\r\n\t\t\t// but there's a single edge case when it could go out of sync —\r\n\t\t\t// updating item heights externally via `.onItemHeightDidChange(item)`.\r\n\t\t\t//\r\n\t\t\t// If, for example, an item height was updated externally via `.onItemHeightDidChange(item)`\r\n\t\t\t// then `this.getState().itemHeights` would get updated immediately but\r\n\t\t\t// `this.getState().beforeItemsHeight` or `this.getState().afterItemsHeight`\r\n\t\t\t// would still correspond to the previous item height, so those would be \"stale\".\r\n\t\t\t// On the other hand, same values in `this.previouslyCalculatedLayout` instance variable\r\n\t\t\t// can also be updated immediately, so they won't go out of sync with the updated item height.\r\n\t\t\t// That seems the only edge case when using a separate `this.previouslyCalculatedLayout`\r\n\t\t\t// instance variable instead of using `this.getState()` would theoretically be justified.\r\n\t\t\t//\r\n\t\t\tthis.previouslyCalculatedLayout = {\r\n\t\t\t\tfirstShownItemIndex,\r\n\t\t\t\tlastShownItemIndex,\r\n\t\t\t\tbeforeItemsHeight,\r\n\t\t\t\tshownItemsHeight\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Update `VirtualScroller` state.\r\n\t\t// `VirtualScroller` automatically re-renders on state updates.\r\n\t\t//\r\n\t\t// All `state` properties updated here should be overwritten in\r\n\t\t// the implementation of `setItems()` and `onResize()` methods\r\n\t\t// so that the `state` is not left in an inconsistent state\r\n\t\t// whenever there're concurrent `updateState()` updates that could\r\n\t\t// possibly conflict with one another — instead, those state updates\r\n\t\t// should overwrite each other in terms of priority.\r\n\t\t// These \"on scroll\" updates have the lowest priority compared to\r\n\t\t// the state updates originating from `setItems()` and `onResize()` methods.\r\n\t\t//\r\n\t\tthis.updateState({\r\n\t\t\tfirstShownItemIndex,\r\n\t\t\tlastShownItemIndex,\r\n\t\t\tbeforeItemsHeight,\r\n\t\t\tafterItemsHeight,\r\n\t\t\t...stateUpdate\r\n\t\t})\r\n\t}\r\n\r\n\tfunction getCoordinatesOfVisibleAreaInsideTheList() {\r\n\t\tconst visibleAreaBounds = this.scroll.getVisibleAreaBounds()\r\n\t\tthis.latestLayoutVisibleArea = visibleAreaBounds\r\n\r\n\t\t// Subtract the top offset of the list inside the scrollable container.\r\n\t\tconst listTopOffsetInsideScrollableContainer = this.getListTopOffsetInsideScrollableContainer()\r\n\t\treturn {\r\n\t\t\ttop: visibleAreaBounds.top - listTopOffsetInsideScrollableContainer,\r\n\t\t\tbottom: visibleAreaBounds.bottom - listTopOffsetInsideScrollableContainer\r\n\t\t}\r\n\t}\r\n\r\n\tfunction getShownItemIndexes() {\r\n\t\tconst itemsCount = this.getItemsCount()\r\n\r\n\t\tconst visibleAreaInsideTheList = getCoordinatesOfVisibleAreaInsideTheList.call(this)\r\n\r\n\t\tif (this.isInBypassMode()) {\r\n\t\t\treturn {\r\n\t\t\t\tfirstShownItemIndex: 0,\r\n\t\t\t\tlastShownItemIndex: itemsCount - 1,\r\n\t\t\t\t// shownItemsHeight: this.getState().itemHeights.reduce((sum, itemHeight) => sum + itemHeight, 0)\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Find the indexes of the items that are currently visible\r\n\t\t// (or close to being visible) in the scrollable container.\r\n\t\t// For scrollable containers other than the main screen, it could also\r\n\t\t// check the visibility of such scrollable container itself, because it\r\n\t\t// might be not visible.\r\n\t\t// If such kind of an optimization would hypothetically be implemented,\r\n\t\t// then it would also require listening for \"scroll\" events on the screen.\r\n\t\t// Overall, I suppose that such \"actual visibility\" feature would be\r\n\t\t// a very minor optimization and not something I'd deal with.\r\n\t\tconst isVisible = visibleAreaInsideTheList.top < this.itemsContainer.getHeight() + this.layout.getPrerenderMargin() && visibleAreaInsideTheList.bottom > 0 - this.layout.getPrerenderMargin()\r\n\t\tif (!isVisible) {\r\n\t\t\tlog('The entire list is off-screen. No items are visible.')\r\n\t\t\treturn this.layout.getNonVisibleListShownItemIndexes()\r\n\t\t}\r\n\r\n\t\t// Get shown item indexes.\r\n\t\treturn this.layout.getShownItemIndexes({\r\n\t\t\titemsCount: this.getItemsCount(),\r\n\t\t\tvisibleAreaInsideTheList\r\n\t\t})\r\n\t}\r\n\r\n\t/**\r\n\t * Validates the heights of items to be hidden on next render.\r\n\t * For example, a user could click a \"Show more\" button,\r\n\t * or an \"Expand YouTube video\" button, which would result\r\n\t * in the actual height of the list item being different\r\n\t * from what has been initially measured in `this.itemHeights[i]`,\r\n\t * if the developer didn't call `.setItemState(item, newState)` and `.onItemHeightDidChange(item)`.\r\n\t */\r\n\tfunction validateWillBeHiddenItemHeightsAreAccurate(firstShownItemIndex, lastShownItemIndex) {\r\n\t\tlet isValid = true\r\n\t\tlet i = this.getState().firstShownItemIndex\r\n\t\twhile (i <= this.getState().lastShownItemIndex) {\r\n\t\t\tif (i >= firstShownItemIndex && i <= lastShownItemIndex) {\r\n\t\t\t\t// The item's still visible.\r\n\t\t\t} else {\r\n\t\t\t\t// The item will be hidden. Re-measure its height.\r\n\t\t\t\t// The rationale is that there could be a situation when an item's\r\n\t\t\t\t// height has changed, and the developer has properly added an\r\n\t\t\t\t// `.onItemHeightDidChange(item)` call to notify `VirtualScroller`\r\n\t\t\t\t// about that change, but at the same time that wouldn't work.\r\n\t\t\t\t// For example, suppose there's a list of several items on a page,\r\n\t\t\t\t// and those items are in \"minimized\" state (having height 100px).\r\n\t\t\t\t// Then, a user clicks an \"Expand all items\" button, and all items\r\n\t\t\t\t// in the list are expanded (expanded item height is gonna be 700px).\r\n\t\t\t\t// `VirtualScroller` demands that `.onItemHeightDidChange(item)` is called\r\n\t\t\t\t// in such cases, and the developer has properly added the code to do that.\r\n\t\t\t\t// So, if there were 10 \"minimized\" items visible on a page, then there\r\n\t\t\t\t// will be 10 individual `.onItemHeightDidChange(item)` calls. No issues so far.\r\n\t\t\t\t// But, as the first `.onItemHeightDidChange(item)` call executes, it immediately\r\n\t\t\t\t// (\"synchronously\") triggers a re-layout, and that re-layout finds out\r\n\t\t\t\t// that now, because the first item is big, it occupies most of the screen\r\n\t\t\t\t// space, and only the first 3 items are visible on screen instead of 10,\r\n\t\t\t\t// and so it leaves the first 3 items mounted and unmounts the rest 7.\r\n\t\t\t\t// Then, after `VirtualScroller` has rerendered, the code returns to\r\n\t\t\t\t// where it was executing, and calls `.onItemHeightDidChange(item)` for the\r\n\t\t\t\t// second item. It also triggers an immediate re-layout that finds out\r\n\t\t\t\t// that only the first 2 items are visible on screen, and it unmounts\r\n\t\t\t\t// the third one too. After that, it calls `.onItemHeightDidChange(item)`\r\n\t\t\t\t// for the third item, but that item is no longer rendered, so its height\r\n\t\t\t\t// can't be measured, and the same's for all the rest of the original 10 items.\r\n\t\t\t\t// So, even though the developer has written their code properly, the\r\n\t\t\t\t// `VirtualScroller` still ends up having incorrect `itemHeights[]`:\r\n\t\t\t\t// `[700px, 700px, 100px, 100px, 100px, 100px, 100px, 100px, 100px, 100px]`\r\n\t\t\t\t// while it should have been `700px` for all of them.\r\n\t\t\t\t// To work around such issues, every item's height is re-measured before it\r\n\t\t\t\t// gets hidden.\r\n\t\t\t\tconst previouslyMeasuredItemHeight = this.getState().itemHeights[i]\r\n\t\t\t\tconst actualItemHeight = remeasureItemHeight.call(this, i)\r\n\t\t\t\tif (actualItemHeight !== previouslyMeasuredItemHeight) {\r\n\t\t\t\t\tif (isValid) {\r\n\t\t\t\t\t\tlog('~ Validate will-be-hidden item heights. ~')\r\n\t\t\t\t\t\t// Update or reset previously calculated layout.\r\n\t\t\t\t\t\tupdatePreviouslyCalculatedLayoutOnItemHeightChange.call(this, i, previouslyMeasuredItemHeight, actualItemHeight)\r\n\t\t\t\t\t}\r\n\t\t\t\t\tisValid = false\r\n\t\t\t\t\twarn('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.')\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\ti++\r\n\t\t}\r\n\t\treturn isValid\r\n\t}\r\n\r\n\tfunction remeasureItemHeight(i) {\r\n\t\tconst { firstShownItemIndex } = this.getState()\r\n\t\treturn this.itemHeights.remeasureItemHeight(i, firstShownItemIndex)\r\n\t}\r\n\r\n\t// Updates the snapshot of the current layout when an item's height changes.\r\n\t//\r\n\t// The \"previously calculated layout\" feature is not currently used.\r\n\t//\r\n\t// The current layout snapshot could be stored as a \"previously calculated layout\" variable\r\n\t// so that it could theoretically be used when calculating new layout incrementally\r\n\t// rather than from scratch, which would be an optimization.\r\n\t//\r\n\tfunction updatePreviouslyCalculatedLayoutOnItemHeightChange(i, previousHeight, newHeight) {\r\n\t\tconst prevLayout = this.previouslyCalculatedLayout\r\n\t\tif (prevLayout) {\r\n\t\t\tconst heightDifference = newHeight - previousHeight\r\n\t\t\tif (i < prevLayout.firstShownItemIndex) {\r\n\t\t\t\t// Patch `prevLayout`'s `.beforeItemsHeight`.\r\n\t\t\t\tprevLayout.beforeItemsHeight += heightDifference\r\n\t\t\t} else if (i > prevLayout.lastShownItemIndex) {\r\n\t\t\t\t// Could patch `.afterItemsHeight` of `prevLayout` here,\r\n\t\t\t\t// if `.afterItemsHeight` property existed in `prevLayout`.\r\n\t\t\t\tif (prevLayout.afterItemsHeight !== undefined) {\r\n\t\t\t\t\tprevLayout.afterItemsHeight += heightDifference\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\t// Patch `prevLayout`'s shown items height.\r\n\t\t\t\tprevLayout.shownItemsHeight += newHeight - previousHeight\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Returns the list's top offset relative to the scrollable container's top edge.\r\n\t * @return {number}\r\n\t */\r\n\tthis.getListTopOffsetInsideScrollableContainer = () => {\r\n\t\tconst listTopOffset = this.scrollableContainer.getItemsContainerTopOffset()\r\n\t\tif (this.listTopOffsetWatcher) {\r\n\t\t\tthis.listTopOffsetWatcher.onListTopOffset(listTopOffset)\r\n\t\t}\r\n\t\treturn listTopOffset\r\n\t}\r\n\r\n\tthis._onItemHeightDidChange = (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 don't do anything else.\r\n\t\tif (i === undefined) {\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tconst {\r\n\t\t\titemHeights,\r\n\t\t\tfirstShownItemIndex,\r\n\t\t\tlastShownItemIndex\r\n\t\t} = this.getState()\r\n\r\n\t\tlog('~ On Item Height Did Change was called ~')\r\n\t\tlog('Item index', i)\r\n\r\n\t\t// Check if the item is still rendered.\r\n\t\tif (!(i >= firstShownItemIndex && i <= lastShownItemIndex)) {\r\n\t\t\t// There could be valid cases when an item is no longer rendered\r\n\t\t\t// by the time `.onItemHeightDidChange(item)` gets called.\r\n\t\t\t// For example, suppose there's a list of several items on a page,\r\n\t\t\t// and those items are in \"minimized\" state (having height 100px).\r\n\t\t\t// Then, a user clicks an \"Expand all items\" button, and all items\r\n\t\t\t// in the list are expanded (expanded item height is gonna be 700px).\r\n\t\t\t// `VirtualScroller` demands that `.onItemHeightDidChange(item)` is called\r\n\t\t\t// in such cases, and the developer has properly added the code to do that.\r\n\t\t\t// So, if there were 10 \"minimized\" items visible on a page, then there\r\n\t\t\t// will be 10 individual `.onItemHeightDidChange(item)` calls. No issues so far.\r\n\t\t\t// But, as the first `.onItemHeightDidChange(item)` call executes, it immediately\r\n\t\t\t// (\"synchronously\") triggers a re-layout, and that re-layout finds out\r\n\t\t\t// that now, because the first item is big, it occupies most of the screen\r\n\t\t\t// space, and only the first 3 items are visible on screen instead of 10,\r\n\t\t\t// and so it leaves the first 3 items mounted and unmounts the rest 7.\r\n\t\t\t// Then, after `VirtualScroller` has rerendered, the code returns to\r\n\t\t\t// where it was executing, and calls `.onItemHeightDidChange(item)` for the\r\n\t\t\t// second item. It also triggers an immediate re-layout that finds out\r\n\t\t\t// that only the first 2 items are visible on screen, and it unmounts\r\n\t\t\t// the third one too. After that, it calls `.onItemHeightDidChange(item)`\r\n\t\t\t// for the third item, but that item is no longer rendered, so its height\r\n\t\t\t// can't be measured, and the same's for all the rest of the original 10 items.\r\n\t\t\t// So, even though the developer has written their code properly, there're\r\n\t\t\t// still situations when the item could be no longer rendered by the time\r\n\t\t\t// `.onItemHeightDidChange(item)` gets called.\r\n\t\t\treturn 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.')\r\n\t\t}\r\n\r\n\t\tconst previousHeight = itemHeights[i]\r\n\t\tif (previousHeight === undefined) {\r\n\t\t\t// There're valid cases when the item still hasn't been measured and `onItemHeightDidChange(item)`\r\n\t\t\t// function was called for it. That's because measuring items is only done after the `VirtualScroller`\r\n\t\t\t// has `start()`ed. But it's not neccessarily `start()`ed by the time it has been rendered (mounted).\r\n\t\t\t// For example, the React component `<VirtualScroller/>` provides an `readyToStart={false}` property\r\n\t\t\t// in order to let the application finish initializing itself before starting the `VirtualScroller`.\r\n\t\t\t// So there're legitimate cases when a `VirtualScroller` is already rendered but is not `start()`ed yet.\r\n\t\t\t// In those cases, not re-measuring the item's height when it changes would not result in any bug\r\n\t\t\t// because the item height will be re-measured anyway the first time it's rendered.\r\n\t\t\t// So in such situations, re-measuring the item's height can be skipped without any consequence.\r\n\t\t\t//\r\n\t\t\t// return reportError(`\"onItemHeightDidChange()\" has been called for item index ${i} but the item hasn't been rendered before.`)\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tlog('~ Re-measure item height ~')\r\n\r\n\t\tlet newHeight\r\n\r\n\t\ttry {\r\n\t\t\tnewHeight = remeasureItemHeight.call(this, i)\r\n\t\t} catch (error) {\r\n\t\t\t// Successfully finishing an `onItemHeightDidChange(item)` call is not considered\r\n\t\t\t// critical for `VirtualScroller`'s operation, so such errors could be ignored.\r\n\t\t\tif (error instanceof ItemNotRenderedError) {\r\n\t\t\t\treturn reportError(`\"onItemHeightDidChange()\" has been called for item index ${i} but the item is not currently rendered and can\\'t be measured. The exact error was: ${error.message}`)\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tlog('Previous height', previousHeight)\r\n\t\tlog('New height', newHeight)\r\n\r\n\t\tif (previousHeight !== newHeight) {\r\n\t\t\tlog('~ Item height has changed. Should update layout. ~')\r\n\r\n\t\t\t// Update or reset a previously calculated layout with the new item height\r\n\t\t\t// so that the potential future \"diff\"s based on that \"previously calculated\" layout\r\n\t\t\t// would be correct.\r\n\t\t\t//\r\n\t\t\t// The \"previously calculated layout\" feature is not currently used\r\n\t\t\t// so this function call doesn't really affect anything.\r\n\t\t\t//\r\n\t\t\tupdatePreviouslyCalculatedLayoutOnItemHeightChange.call(this, i, previousHeight, newHeight)\r\n\r\n\t\t\t// Recalculate layout.\r\n\t\t\t//\r\n\t\t\t// If the `VirtualScroller` is already waiting for a state update to be rendered,\r\n\t\t\t// delay `onItemHeightDidChange(item)`'s re-layout until that state update is rendered.\r\n\t\t\t// The reason is that React `<VirtualScroller/>`'s `onHeightDidChange()` is meant to\r\n\t\t\t// be called inside `useLayoutEffect()` hook. Due to how React is implemented internally,\r\n\t\t\t// that might happen in the middle of the currently pending `setState()` operation\r\n\t\t\t// being applied, resulting in weird \"race condition\" bugs.\r\n\t\t\t//\r\n\t\t\tif (this._isActive) {\r\n\t\t\t\tif (this.waitingForRender) {\r\n\t\t\t\t\tlog('~ Another state update is already waiting to be rendered. Delay the layout update until then. ~')\r\n\t\t\t\t\tthis.updateLayoutAfterRenderBecauseItemHeightChanged = true\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthis.onUpdateShownItemIndexes({ reason: LAYOUT_REASON.ITEM_HEIGHT_CHANGED })\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// If there was a request for `setState()` with new `items`, then the changes\r\n\t\t\t// to `currentState.itemHeights[]` made above in a `remeasureItemHeight()` call\r\n\t\t\t// would be overwritten when that pending `setState()` call gets applied.\r\n\t\t\t// To fix that, the updates to current `itemHeights[]` are noted in\r\n\t\t\t// `this.itemHeightsThatChangedWhileNewItemsWereBeingRendered` variable.\r\n\t\t\t// That variable is then checked when the `setState()` call with the new `items`\r\n\t\t\t// has been updated.\r\n\t\t\tif (this.newItemsWillBeRendered) {\r\n\t\t\t\tif (!this.itemHeightsThatChangedWhileNewItemsWereBeingRendered) {\r\n\t\t\t\t\tthis.itemHeightsThatChangedWhileNewItemsWereBeingRendered = {}\r\n\t\t\t\t}\r\n\t\t\t\tthis.itemHeightsThatChangedWhileNewItemsWereBeingRendered[String(i)] = newHeight\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tthis.getPrerenderMargin = () => {\r\n\t\t// The list component renders not only the items that're currently visible\r\n\t\t// but also the items that lie within some extra vertical margin (called\r\n\t\t// \"prerender margin\") on top and bottom for future scrolling: this way,\r\n\t\t// there'll be significantly less layout recalculations as the user scrolls,\r\n\t\t// because now it doesn't have to recalculate layout on each scroll event.\r\n\t\t// By default, the \"prerender margin\" is equal to the screen height:\r\n\t\t// this seems to be the optimal value for \"Page Up\" / \"Page Down\" navigation\r\n\t\t// and optimized mouse wheel scrolling (a user is unlikely to continuously\r\n\t\t// scroll past the screen height, because they'd stop to read through\r\n\t\t// the newly visible items first, and when they do stop scrolling, that's\r\n\t\t// when layout gets recalculated).\r\n\t\treturn this.scrollableContainer.getHeight() * this.getPrerenderMarginRatio()\r\n\t}\r\n\r\n\tthis.getPrerenderMarginRatio = () => {\r\n\t\t// See the readme for the description of `prerenderMarginRatio` option.\r\n\t\treturn 1 // in scrollable container heights.\r\n\t}\r\n\r\n\t/**\r\n\t * Calls `onItemFirstRender()` for items that haven't been\r\n\t * \"seen\" previously.\r\n\t * @param {any[]} items\r\n\t * @param {number[]} itemHeights\r\n\t * @param {number} firstShownItemIndex\r\n\t * @param {number} lastShownItemIndex\r\n\t */\r\n\tthis.onBeforeShowItems = (\r\n\t\titems,\r\n\t\titemHeights,\r\n\t\tfirstShownItemIndex,\r\n\t\tlastShownItemIndex\r\n\t) => {\r\n\t\tif (this.onItemInitialRender) {\r\n\t\t\tlet i = firstShownItemIndex\r\n\t\t\twhile (i <= lastShownItemIndex) {\r\n\t\t\t\tif (itemHeights[i] === undefined) {\r\n\t\t\t\t\tthis.onItemInitialRender(items[i])\r\n\t\t\t\t}\r\n\t\t\t\ti++\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tthis.measureItemHeightsAndSpacing = () => {\r\n\t\t// Measure \"newly shown\" item heights.\r\n\t\t// Also re-validate already measured items' heights.\r\n\t\tthis.itemHeights.measureItemHeights(\r\n\t\t\tthis.getState().firstShownItemIndex,\r\n\t\t\tthis.getState().lastShownItemIndex\r\n\t\t)\r\n\r\n\t\t// Measure item vertical spacing, if required.\r\n\t\tconst verticalSpacing = this.measureVerticalSpacingIfNotMeasured()\r\n\r\n\t\t// Return a state update if vertical spacing has been measured.\r\n\t\t// Doesn't set `verticalSpacing: 0` in `state` because it is effectively\r\n\t\t// same as `verticalSpacing: undefined` in terms code behavior and calculations.\r\n\t\t// Not having `verticalSpacing: 0` in `state` just makes the `state` object\r\n\t\t// a bit more cleaner and a bit less cluttered (easier for inspection).\r\n\t\tif (verticalSpacing && verticalSpacing !== 0) {\r\n\t\t\t// Return a state update.\r\n\t\t\t// Sets `verticalSpacing` property in `state`.\r\n\t\t\treturn {\r\n\t\t\t\tverticalSpacing\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tthis.cancelLayoutTimer = ({ stateUpdate }) => {\r\n\t\tif (this.layoutTimer) {\r\n\t\t\tclearTimeout(this.layoutTimer)\r\n\t\t\tthis.layoutTimer = undefined\r\n\t\t\t// Merge state updates.\r\n\t\t\tif (stateUpdate || this.layoutTimerStateUpdate) {\r\n\t\t\t\tstateUpdate = {\r\n\t\t\t\t\t...this.layoutTimerStateUpdate,\r\n\t\t\t\t\t...stateUpdate\r\n\t\t\t\t}\r\n\t\t\t\tthis.layoutTimerStateUpdate = undefined\r\n\t\t\t\treturn stateUpdate\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\treturn stateUpdate\r\n\t\t}\r\n\t}\r\n\r\n\tthis.scheduleLayoutTimer = ({ reason, stateUpdate }) => {\r\n\t\tthis.layoutTimerStateUpdate = stateUpdate\r\n\t\tthis.layoutTimer = setTimeout(() => {\r\n\t\t\tthis.layoutTimerStateUpdate = undefined\r\n\t\t\tthis.layoutTimer = undefined\r\n\t\t\tthis.onUpdateShownItemIndexes({\r\n\t\t\t\treason,\r\n\t\t\t\tstateUpdate\r\n\t\t\t})\r\n\t\t}, 0)\r\n\t}\r\n}\r\n\r\nconst SLOW_LAYOUT_DURATION = 15 // in milliseconds."],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VirtualScroller.layout.js","names":["log","warn","isDebug","reportError","scheduleOnNextTick","unscheduleOnNextTick","LAYOUT_REASON","ItemNotRenderedError","onUpdateShownItemIndexes","reason","stateUpdate","skip","updateState","newItemsWillBeRendered","widthHasChanged","_isResizing","getItemsCount","scroll","cancelScheduledLayout","cancelLayoutTimer","updateShownItemIndexes","call","startedAt","Date","now","getShownItemIndexes","firstShownItemIndex","lastShownItemIndex","shownItemsHeight","firstNonMeasuredItemIndex","listHeightMeasurement","hasSnapshot","getAnchorItemIndex","undefined","validateWillBeHiddenItemHeightsAreAccurate","beforeItemsHeight","layout","getBeforeItemsHeight","afterItemsHeight","getAfterItemsHeight","layoutDuration","isInBypassMode","SLOW_LAYOUT_DURATION","getColumnsCount","getAverageItemHeight","getState","itemHeights","slice","itemStates","onBeforeShowItems","items","previouslyCalculatedLayout","getCoordinatesOfVisibleAreaInsideTheList","visibleAreaBounds","getVisibleAreaBounds","latestLayoutVisibleArea","listTopOffsetInsideScrollableContainer","getListTopOffsetInsideScrollableContainer","top","bottom","itemsCount","visibleAreaInsideTheList","isVisible","itemsContainer","getHeight","getPrerenderMargin","getNonVisibleListShownItemIndexes","isValid","i","previouslyMeasuredItemHeight","actualItemHeight","remeasureItemHeight","updatePreviouslyCalculatedLayoutOnItemHeightChange","previousHeight","newHeight","prevLayout","heightDifference","listTopOffset","scrollableContainer","getItemsContainerTopOffset","listTopOffsetWatcher","onListTopOffset","_onItemHeightDidChange","itemOrIndex","_getItemIndexByItemOrIndex","error","message","_isActive","waitingForRender","updateLayoutAfterRenderBecauseItemHeightChanged","ITEM_HEIGHT_CHANGED","itemHeightsThatChangedWhileNewItemsWereBeingRendered","String","getPrerenderMarginRatio","onItemInitialRender","measureItemHeightsAndSpacing","measureItemHeights","verticalSpacing","measureVerticalSpacingIfNotMeasured","layoutTimer","layoutTimerStateUpdate","scheduleLayoutTimer","FPS","FRAME_DURATION"],"sources":["../source/VirtualScroller.layout.js"],"sourcesContent":["import log, { warn, isDebug, reportError } from './utility/debug.js'\r\nimport { scheduleOnNextTick, unscheduleOnNextTick } from './utility/scheduleOnNextTick.js'\r\n\r\nimport { LAYOUT_REASON } from './Layout.js'\r\nimport ItemNotRenderedError from './ItemNotRenderedError.js'\r\n\r\nexport default function() {\r\n\tthis.onUpdateShownItemIndexes = ({ reason, stateUpdate }) => {\r\n\t\t// In case of \"don't perform this particular re-layout\r\n\t\t// because a more-priority one is already scheduled\".\r\n\t\tconst skip = () => {\r\n\t\t\tif (stateUpdate) {\r\n\t\t\t\t// Don't update layout. Just update some state.\r\n\t\t\t\t// `stateUpdate` isn't supposed to affect the layout in any way\r\n\t\t\t\t// because it doesn't contain any properties that affect the rendering of the list.\r\n\t\t\t\t// I.e. it doesn't contain properties like `beforeItemsHeight` or `afterItemsHeight`, etc.\r\n\t\t\t\tthis.updateState(stateUpdate)\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// If new `items` have been set and are waiting to be applied,\r\n\t\t// or if the viewport width has changed requiring a re-layout,\r\n\t\t// then temporarily stop all other updates like \"on scroll\" updates.\r\n\t\t//\r\n\t\t// This prevents `state` from becoming inconsistent, because several\r\n\t\t// \"contenders\" (layout reasons) could be attempting to update\r\n\t\t// `VirtualScroller` state in parallel between two successive renders.\r\n\t\t//\r\n\t\t// For example, both `VirtualScroller.setItems()` and `VirtualScroller.scroll.onScroll()`\r\n\t\t// \"contenders\" could be attempting to update `VirtualScroller` state in parallel,\r\n\t\t// and the layout calculated by one of them would overwrite the layout calculated by another.\r\n\t\t//\r\n\t\t// Which one should have the priority in such case then?\r\n\t\t// In this specific example, `VirtualScroller.setItems()` should be the one\r\n\t\t// because only it knows the correct new `items` whereas other \"contenders\"\r\n\t\t// have the old stale `items`.\r\n\t\t//\r\n\t\t// Another example of a \"contender\" that should have a priority is\r\n\t\t// \"on list width has changed\" handler because only it has access\r\n\t\t// to the correct new width of the list rather than the old stale width.\r\n\t\t//\r\n\t\t// So in these two \"priority\" cases any other re-layout should be ignored.\r\n\t\t//\r\n\t\tif (this.newItemsWillBeRendered || this.widthHasChanged || this._isResizing) {\r\n\t\t\treturn skip()\r\n\t\t}\r\n\r\n\t\t// If there're no items then there's no need to re-layout anything.\r\n\t\tif (this.getItemsCount() === 0) {\r\n\t\t\treturn skip()\r\n\t\t}\r\n\r\n\t\t// Cancel a scheduled \"re-layout when user stops scrolling\" timer\r\n\t\t// because a re-layout will be performed here now.\r\n\t\tthis.scroll.cancelScheduledLayout()\r\n\r\n\t\t// Cancel a scheduled re-layout that is scheduled to run at the next \"frame\",\r\n\t\t// because a re-layout will be performed here now.\r\n\t\tstateUpdate = this.cancelLayoutTimer({ stateUpdate })\r\n\r\n\t\t// Perform a re-layout.\r\n\t\tlog(`~ Update Layout (on ${reason}) ~`)\r\n\t\tupdateShownItemIndexes.call(this, { stateUpdate })\r\n\t}\r\n\r\n\t/**\r\n\t * Updates the \"from\" and \"to\" shown item indexes.\r\n\t * If the list is visible and some of the items being shown are new\r\n\t * and are required to be measured first, then\r\n\t * `firstNonMeasuredItemIndex` is defined.\r\n\t * If the list is visible and all items being shown have been encountered\r\n\t * (and measured) before, then `firstNonMeasuredItemIndex` is `undefined`.\r\n\t *\r\n\t * The `stateUpdate` parameter is just an optional \"additional\" state update.\r\n\t */\r\n\tfunction updateShownItemIndexes({ stateUpdate }) {\r\n\t\tconst startedAt = Date.now()\r\n\r\n\t\t// Get shown item indexes.\r\n\t\tlet {\r\n\t\t\tfirstShownItemIndex,\r\n\t\t\tlastShownItemIndex,\r\n\t\t\tshownItemsHeight,\r\n\t\t\tfirstNonMeasuredItemIndex\r\n\t\t} = getShownItemIndexes.call(this)\r\n\r\n\t\t// If scroll position is scheduled to be restored after render,\r\n\t\t// then the \"anchor\" item must be rendered, and all of the prepended\r\n\t\t// items before it, all in a single pass. This way, all of the\r\n\t\t// prepended items' heights could be measured right after the render\r\n\t\t// has finished, and the scroll position can then be immediately restored.\r\n\t\tif (this.listHeightMeasurement.hasSnapshot()) {\r\n\t\t\tif (lastShownItemIndex < this.listHeightMeasurement.getAnchorItemIndex()) {\r\n\t\t\t\tlastShownItemIndex = this.listHeightMeasurement.getAnchorItemIndex()\r\n\t\t\t}\r\n\t\t\t// `firstShownItemIndex` is always `0` when prepending items.\r\n\t\t\t// And `lastShownItemIndex` always covers all prepended items in this case.\r\n\t\t\t// None of the prepended items have been rendered before,\r\n\t\t\t// so their heights are unknown. The code at the start of this function\r\n\t\t\t// did therefore set `firstNonMeasuredItemIndex` to non-`undefined`\r\n\t\t\t// in order to render just the first prepended item in order to\r\n\t\t\t// measure it, and only then make a decision on how many other\r\n\t\t\t// prepended items to render. But since we've instructed the code\r\n\t\t\t// to show all of the prepended items at once, there's no need to\r\n\t\t\t// \"redo layout after render\". Additionally, if layout was re-done\r\n\t\t\t// after render, then there would be a short interval of visual\r\n\t\t\t// \"jitter\" due to the scroll position not being restored because it'd\r\n\t\t\t// wait for the second layout to finish instead of being restored\r\n\t\t\t// right after the first one.\r\n\t\t\tfirstNonMeasuredItemIndex = undefined\r\n\t\t}\r\n\r\n\t\t// Validate the heights of items to be hidden on next render.\r\n\t\t// For example, a user could click a \"Show more\" button,\r\n\t\t// or an \"Expand YouTube video\" button, which would result\r\n\t\t// in the actual height of the list item being different\r\n\t\t// from what has been initially measured in `this.itemHeights[i]`,\r\n\t\t// if the developer didn't call `.setItemState(item, newState)` and `.onItemHeightDidChange(item)`.\r\n\t\tif (!validateWillBeHiddenItemHeightsAreAccurate.call(this, firstShownItemIndex, lastShownItemIndex)) {\r\n\t\t\tlog('~ Because some of the will-be-hidden item heights (listed above) have changed since they\\'ve last been measured, redo layout. ~')\r\n\t\t\t// Redo layout, now with the correct item heights.\r\n\t\t\treturn updateShownItemIndexes.call(this, { stateUpdate });\r\n\t\t}\r\n\r\n\t\t// Measure \"before\" items height.\r\n\t\tconst beforeItemsHeight = this.layout.getBeforeItemsHeight(\r\n\t\t\tfirstShownItemIndex\r\n\t\t)\r\n\r\n\t\t// Measure \"after\" items height.\r\n\t\tconst afterItemsHeight = this.layout.getAfterItemsHeight(\r\n\t\t\tlastShownItemIndex,\r\n\t\t\tthis.getItemsCount()\r\n\t\t)\r\n\r\n\t\tconst layoutDuration = Date.now() - startedAt\r\n\r\n\t\t// Debugging.\r\n\t\tlog('~ Calculated Layout' + (this.isInBypassMode() ? ' (bypass)' : '') + ' ~')\r\n\t\tif (layoutDuration < SLOW_LAYOUT_DURATION) {\r\n\t\t\t// log('Calculated in', layoutDuration, 'ms')\r\n\t\t} else {\r\n\t\t\t// Getting this warning would mean that for a given (large) count of items,\r\n\t\t\t// calculating `beforeItemsHeight` or `afterItemsHeight` requires too much time\r\n\t\t\t// in order to iterate over all not-currently-visible items to add up their heights.\r\n\t\t\t//\r\n\t\t\t// Because layout calculation is a blocking procedure, it \"freezes\" the application\r\n\t\t\t// for a given amount of milliseconds. If the amount of those milliseconds is longer\r\n\t\t\t// than 16ms, frames are being dropped in a 60fps scenario.\r\n\t\t\t// Modern phones even use 120fps as a baseline, so the timeframe requirement\r\n\t\t\t// gets even more strict.\r\n\t\t\t//\r\n\t\t\t// What could be a way of dealing with this?\r\n\t\t\t//\r\n\t\t\t// * The code could cache clustered item heights in order to perform less add-up operations.\r\n\t\t\t// For example, the code could cache the total height of items 1..100, 101..200, etc.\r\n\t\t\t// Then 1..1000, 1001..2000, etc. Then 1..10000, 10001..20000, etc. So the clusters\r\n\t\t\t// would just get progressively larger as more `items` are added.\r\n\t\t\t//\r\n\t\t\twarn('Layout calculation time too high:', layoutDuration, 'ms')\r\n\t\t}\r\n\t\tif (this.getColumnsCount()) {\r\n\t\t\tlog('Columns count', this.getColumnsCount())\r\n\t\t}\r\n\t\tlog('First shown item index', firstShownItemIndex)\r\n\t\tlog('Last shown item index', lastShownItemIndex)\r\n\t\tlog('Before items height', beforeItemsHeight)\r\n\t\tlog('After items height (actual or estimated)', afterItemsHeight)\r\n\t\tlog('Average item height (will be used to calculate estimated \"after items height\")', this.getAverageItemHeight())\r\n\t\tif (isDebug()) {\r\n\t\t\tlog('Item heights', this.getState().itemHeights.slice())\r\n\t\t\tlog('Item states', this.getState().itemStates.slice())\r\n\t\t}\r\n\r\n\t\t// Optionally preload items to be rendered.\r\n\t\tthis.onBeforeShowItems(\r\n\t\t\tthis.getState().items,\r\n\t\t\tthis.getState().itemHeights,\r\n\t\t\tfirstShownItemIndex,\r\n\t\t\tlastShownItemIndex\r\n\t\t)\r\n\r\n\t\t// Set `this.firstNonMeasuredItemIndex`.\r\n\t\tthis.firstNonMeasuredItemIndex = firstNonMeasuredItemIndex\r\n\t\t// if (firstNonMeasuredItemIndex !== undefined) {\r\n\t\t// \tlog('Non-measured item index that will be measured at next layout', firstNonMeasuredItemIndex)\r\n\t\t// }\r\n\r\n\t\t// Set \"previously calculated layout\".\r\n\t\t//\r\n\t\t// The \"previously calculated layout\" feature is not currently used.\r\n\t\t//\r\n\t\t// The current layout snapshot could be stored as a \"previously calculated layout\" variable\r\n\t\t// so that it could theoretically be used when calculating new layout incrementally\r\n\t\t// rather than from scratch, which would be an optimization.\r\n\t\t//\r\n\t\t// Currently, this feature is not used, and `shownItemsHeight` property\r\n\t\t// is not returned at all, so don't set any \"previously calculated layout\".\r\n\t\t//\r\n\t\tif (shownItemsHeight === undefined) {\r\n\t\t\tthis.previouslyCalculatedLayout = undefined\r\n\t\t} else {\r\n\t\t\t// If \"previously calculated layout\" feature would be implmeneted,\r\n\t\t\t// then this code would set \"previously calculate layout\" instance variable.\r\n\t\t\t//\r\n\t\t\t// What for would this instance variable be used?\r\n\t\t\t//\r\n\t\t\t// Instead of using a `this.previouslyCalculatedLayout` instance variable,\r\n\t\t\t// this code could use `this.getState()` because it reflects what's currently on screen,\r\n\t\t\t// but there's a single edge case when it could go out of sync —\r\n\t\t\t// updating item heights externally via `.onItemHeightDidChange(item)`.\r\n\t\t\t//\r\n\t\t\t// If, for example, an item height was updated externally via `.onItemHeightDidChange(item)`\r\n\t\t\t// then `this.getState().itemHeights` would get updated immediately but\r\n\t\t\t// `this.getState().beforeItemsHeight` or `this.getState().afterItemsHeight`\r\n\t\t\t// would still correspond to the previous item height, so those would be \"stale\".\r\n\t\t\t// On the other hand, same values in `this.previouslyCalculatedLayout` instance variable\r\n\t\t\t// can also be updated immediately, so they won't go out of sync with the updated item height.\r\n\t\t\t// That seems the only edge case when using a separate `this.previouslyCalculatedLayout`\r\n\t\t\t// instance variable instead of using `this.getState()` would theoretically be justified.\r\n\t\t\t//\r\n\t\t\tthis.previouslyCalculatedLayout = {\r\n\t\t\t\tfirstShownItemIndex,\r\n\t\t\t\tlastShownItemIndex,\r\n\t\t\t\tbeforeItemsHeight,\r\n\t\t\t\tshownItemsHeight\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Update `VirtualScroller` state.\r\n\t\t// `VirtualScroller` automatically re-renders on state updates.\r\n\t\t//\r\n\t\t// All `state` properties updated here should be overwritten in\r\n\t\t// the implementation of `setItems()` and `onResize()` methods\r\n\t\t// so that the `state` is not left in an inconsistent state\r\n\t\t// whenever there're concurrent `updateState()` updates that could\r\n\t\t// possibly conflict with one another — instead, those state updates\r\n\t\t// should overwrite each other in terms of priority.\r\n\t\t// These \"on scroll\" updates have the lowest priority compared to\r\n\t\t// the state updates originating from `setItems()` and `onResize()` methods.\r\n\t\t//\r\n\t\tthis.updateState({\r\n\t\t\tfirstShownItemIndex,\r\n\t\t\tlastShownItemIndex,\r\n\t\t\tbeforeItemsHeight,\r\n\t\t\tafterItemsHeight,\r\n\t\t\t...stateUpdate\r\n\t\t})\r\n\t}\r\n\r\n\tfunction getCoordinatesOfVisibleAreaInsideTheList() {\r\n\t\tconst visibleAreaBounds = this.scroll.getVisibleAreaBounds()\r\n\t\tthis.latestLayoutVisibleArea = visibleAreaBounds\r\n\r\n\t\t// Subtract the top offset of the list inside the scrollable container.\r\n\t\tconst listTopOffsetInsideScrollableContainer = this.getListTopOffsetInsideScrollableContainer()\r\n\t\treturn {\r\n\t\t\ttop: visibleAreaBounds.top - listTopOffsetInsideScrollableContainer,\r\n\t\t\tbottom: visibleAreaBounds.bottom - listTopOffsetInsideScrollableContainer\r\n\t\t}\r\n\t}\r\n\r\n\tfunction getShownItemIndexes() {\r\n\t\tconst itemsCount = this.getItemsCount()\r\n\r\n\t\tconst visibleAreaInsideTheList = getCoordinatesOfVisibleAreaInsideTheList.call(this)\r\n\r\n\t\tif (this.isInBypassMode()) {\r\n\t\t\treturn {\r\n\t\t\t\tfirstShownItemIndex: 0,\r\n\t\t\t\tlastShownItemIndex: itemsCount - 1,\r\n\t\t\t\t// shownItemsHeight: this.getState().itemHeights.reduce((sum, itemHeight) => sum + itemHeight, 0)\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Find the indexes of the items that are currently visible\r\n\t\t// (or close to being visible) in the scrollable container.\r\n\t\t// For scrollable containers other than the main screen, it could also\r\n\t\t// check the visibility of such scrollable container itself, because it\r\n\t\t// might be not visible.\r\n\t\t// If such kind of an optimization would hypothetically be implemented,\r\n\t\t// then it would also require listening for \"scroll\" events on the screen.\r\n\t\t// Overall, I suppose that such \"actual visibility\" feature would be\r\n\t\t// a very minor optimization and not something I'd deal with.\r\n\t\tconst isVisible = visibleAreaInsideTheList.top < this.itemsContainer.getHeight() + this.layout.getPrerenderMargin() && visibleAreaInsideTheList.bottom > 0 - this.layout.getPrerenderMargin()\r\n\t\tif (!isVisible) {\r\n\t\t\tlog('The entire list is off-screen. No items are visible.')\r\n\t\t\treturn this.layout.getNonVisibleListShownItemIndexes()\r\n\t\t}\r\n\r\n\t\t// Get shown item indexes.\r\n\t\treturn this.layout.getShownItemIndexes({\r\n\t\t\titemsCount: this.getItemsCount(),\r\n\t\t\tvisibleAreaInsideTheList\r\n\t\t})\r\n\t}\r\n\r\n\t/**\r\n\t * Validates the heights of items to be hidden on next render.\r\n\t * For example, a user could click a \"Show more\" button,\r\n\t * or an \"Expand YouTube video\" button, which would result\r\n\t * in the actual height of the list item being different\r\n\t * from what has been initially measured in `this.itemHeights[i]`,\r\n\t * if the developer didn't call `.setItemState(item, newState)` and `.onItemHeightDidChange(item)`.\r\n\t */\r\n\tfunction validateWillBeHiddenItemHeightsAreAccurate(firstShownItemIndex, lastShownItemIndex) {\r\n\t\tlet isValid = true\r\n\t\tlet i = this.getState().firstShownItemIndex\r\n\t\twhile (i <= this.getState().lastShownItemIndex) {\r\n\t\t\tif (i >= firstShownItemIndex && i <= lastShownItemIndex) {\r\n\t\t\t\t// The item's still visible.\r\n\t\t\t} else {\r\n\t\t\t\t// The item will be hidden. Re-measure its height.\r\n\t\t\t\t// The rationale is that there could be a situation when an item's\r\n\t\t\t\t// height has changed, and the developer has properly added an\r\n\t\t\t\t// `.onItemHeightDidChange(item)` call to notify `VirtualScroller`\r\n\t\t\t\t// about that change, but at the same time that wouldn't work.\r\n\t\t\t\t// For example, suppose there's a list of several items on a page,\r\n\t\t\t\t// and those items are in \"minimized\" state (having height 100px).\r\n\t\t\t\t// Then, a user clicks an \"Expand all items\" button, and all items\r\n\t\t\t\t// in the list are expanded (expanded item height is gonna be 700px).\r\n\t\t\t\t// `VirtualScroller` demands that `.onItemHeightDidChange(item)` is called\r\n\t\t\t\t// in such cases, and the developer has properly added the code to do that.\r\n\t\t\t\t// So, if there were 10 \"minimized\" items visible on a page, then there\r\n\t\t\t\t// will be 10 individual `.onItemHeightDidChange(item)` calls. No issues so far.\r\n\t\t\t\t// But, as the first `.onItemHeightDidChange(item)` call executes, it immediately\r\n\t\t\t\t// (\"synchronously\") triggers a re-layout, and that re-layout finds out\r\n\t\t\t\t// that now, because the first item is big, it occupies most of the screen\r\n\t\t\t\t// space, and only the first 3 items are visible on screen instead of 10,\r\n\t\t\t\t// and so it leaves the first 3 items mounted and unmounts the rest 7.\r\n\t\t\t\t// Then, after `VirtualScroller` has rerendered, the code returns to\r\n\t\t\t\t// where it was executing, and calls `.onItemHeightDidChange(item)` for the\r\n\t\t\t\t// second item. It also triggers an immediate re-layout that finds out\r\n\t\t\t\t// that only the first 2 items are visible on screen, and it unmounts\r\n\t\t\t\t// the third one too. After that, it calls `.onItemHeightDidChange(item)`\r\n\t\t\t\t// for the third item, but that item is no longer rendered, so its height\r\n\t\t\t\t// can't be measured, and the same's for all the rest of the original 10 items.\r\n\t\t\t\t// So, even though the developer has written their code properly, the\r\n\t\t\t\t// `VirtualScroller` still ends up having incorrect `itemHeights[]`:\r\n\t\t\t\t// `[700px, 700px, 100px, 100px, 100px, 100px, 100px, 100px, 100px, 100px]`\r\n\t\t\t\t// while it should have been `700px` for all of them.\r\n\t\t\t\t// To work around such issues, every item's height is re-measured before it\r\n\t\t\t\t// gets hidden.\r\n\t\t\t\tconst previouslyMeasuredItemHeight = this.getState().itemHeights[i]\r\n\t\t\t\tconst actualItemHeight = remeasureItemHeight.call(this, i)\r\n\t\t\t\tif (actualItemHeight !== previouslyMeasuredItemHeight) {\r\n\t\t\t\t\tif (isValid) {\r\n\t\t\t\t\t\tlog('~ Validate will-be-hidden item heights. ~')\r\n\t\t\t\t\t\t// Update or reset previously calculated layout.\r\n\t\t\t\t\t\tupdatePreviouslyCalculatedLayoutOnItemHeightChange.call(this, i, previouslyMeasuredItemHeight, actualItemHeight)\r\n\t\t\t\t\t}\r\n\t\t\t\t\tisValid = false\r\n\t\t\t\t\twarn('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.')\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\ti++\r\n\t\t}\r\n\t\treturn isValid\r\n\t}\r\n\r\n\tfunction remeasureItemHeight(i) {\r\n\t\tconst { firstShownItemIndex } = this.getState()\r\n\t\treturn this.itemHeights.remeasureItemHeight(i, firstShownItemIndex)\r\n\t}\r\n\r\n\t// Updates the snapshot of the current layout when an item's height changes.\r\n\t//\r\n\t// The \"previously calculated layout\" feature is not currently used.\r\n\t//\r\n\t// The current layout snapshot could be stored as a \"previously calculated layout\" variable\r\n\t// so that it could theoretically be used when calculating new layout incrementally\r\n\t// rather than from scratch, which would be an optimization.\r\n\t//\r\n\tfunction updatePreviouslyCalculatedLayoutOnItemHeightChange(i, previousHeight, newHeight) {\r\n\t\tconst prevLayout = this.previouslyCalculatedLayout\r\n\t\tif (prevLayout) {\r\n\t\t\tconst heightDifference = newHeight - previousHeight\r\n\t\t\tif (i < prevLayout.firstShownItemIndex) {\r\n\t\t\t\t// Patch `prevLayout`'s `.beforeItemsHeight`.\r\n\t\t\t\tprevLayout.beforeItemsHeight += heightDifference\r\n\t\t\t} else if (i > prevLayout.lastShownItemIndex) {\r\n\t\t\t\t// Could patch `.afterItemsHeight` of `prevLayout` here,\r\n\t\t\t\t// if `.afterItemsHeight` property existed in `prevLayout`.\r\n\t\t\t\tif (prevLayout.afterItemsHeight !== undefined) {\r\n\t\t\t\t\tprevLayout.afterItemsHeight += heightDifference\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\t// Patch `prevLayout`'s shown items height.\r\n\t\t\t\tprevLayout.shownItemsHeight += newHeight - previousHeight\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Returns the list's top offset relative to the scrollable container's top edge.\r\n\t * @return {number}\r\n\t */\r\n\tthis.getListTopOffsetInsideScrollableContainer = () => {\r\n\t\tconst listTopOffset = this.scrollableContainer.getItemsContainerTopOffset()\r\n\t\tif (this.listTopOffsetWatcher) {\r\n\t\t\tthis.listTopOffsetWatcher.onListTopOffset(listTopOffset)\r\n\t\t}\r\n\t\treturn listTopOffset\r\n\t}\r\n\r\n\tthis._onItemHeightDidChange = (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 don't do anything else.\r\n\t\tif (i === undefined) {\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tconst {\r\n\t\t\titemHeights,\r\n\t\t\tfirstShownItemIndex,\r\n\t\t\tlastShownItemIndex\r\n\t\t} = this.getState()\r\n\r\n\t\tlog('~ On Item Height Did Change was called ~')\r\n\t\tlog('Item index', i)\r\n\r\n\t\t// Check if the item is still rendered.\r\n\t\tif (!(i >= firstShownItemIndex && i <= lastShownItemIndex)) {\r\n\t\t\t// There could be valid cases when an item is no longer rendered\r\n\t\t\t// by the time `.onItemHeightDidChange(item)` gets called.\r\n\t\t\t// For example, suppose there's a list of several items on a page,\r\n\t\t\t// and those items are in \"minimized\" state (having height 100px).\r\n\t\t\t// Then, a user clicks an \"Expand all items\" button, and all items\r\n\t\t\t// in the list are expanded (expanded item height is gonna be 700px).\r\n\t\t\t// `VirtualScroller` demands that `.onItemHeightDidChange(item)` is called\r\n\t\t\t// in such cases, and the developer has properly added the code to do that.\r\n\t\t\t// So, if there were 10 \"minimized\" items visible on a page, then there\r\n\t\t\t// will be 10 individual `.onItemHeightDidChange(item)` calls. No issues so far.\r\n\t\t\t// But, as the first `.onItemHeightDidChange(item)` call executes, it immediately\r\n\t\t\t// (\"synchronously\") triggers a re-layout, and that re-layout finds out\r\n\t\t\t// that now, because the first item is big, it occupies most of the screen\r\n\t\t\t// space, and only the first 3 items are visible on screen instead of 10,\r\n\t\t\t// and so it leaves the first 3 items mounted and unmounts the rest 7.\r\n\t\t\t// Then, after `VirtualScroller` has rerendered, the code returns to\r\n\t\t\t// where it was executing, and calls `.onItemHeightDidChange(item)` for the\r\n\t\t\t// second item. It also triggers an immediate re-layout that finds out\r\n\t\t\t// that only the first 2 items are visible on screen, and it unmounts\r\n\t\t\t// the third one too. After that, it calls `.onItemHeightDidChange(item)`\r\n\t\t\t// for the third item, but that item is no longer rendered, so its height\r\n\t\t\t// can't be measured, and the same's for all the rest of the original 10 items.\r\n\t\t\t// So, even though the developer has written their code properly, there're\r\n\t\t\t// still situations when the item could be no longer rendered by the time\r\n\t\t\t// `.onItemHeightDidChange(item)` gets called.\r\n\t\t\treturn 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.')\r\n\t\t}\r\n\r\n\t\tconst previousHeight = itemHeights[i]\r\n\t\tif (previousHeight === undefined) {\r\n\t\t\t// There're valid cases when the item still hasn't been measured and `onItemHeightDidChange(item)`\r\n\t\t\t// function was called for it. That's because measuring items is only done after the `VirtualScroller`\r\n\t\t\t// has `start()`ed. But it's not necessarily `start()`ed by the time it has been rendered (mounted).\r\n\t\t\t// For example, the React component `<VirtualScroller/>` provides an `readyToStart={false}` property\r\n\t\t\t// in order to let the application finish initializing itself before starting the `VirtualScroller`.\r\n\t\t\t// So there're legitimate cases when a `VirtualScroller` is already rendered but is not `start()`ed yet.\r\n\t\t\t// In those cases, not re-measuring the item's height when it changes would not result in any bug\r\n\t\t\t// because the item height will be re-measured anyway the first time it's rendered.\r\n\t\t\t// So in such situations, re-measuring the item's height can be skipped without any consequence.\r\n\t\t\t//\r\n\t\t\t// return reportError(`\"onItemHeightDidChange()\" has been called for item index ${i} but the item hasn't been rendered before.`)\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tlog('~ Re-measure item height ~')\r\n\r\n\t\tlet newHeight\r\n\r\n\t\ttry {\r\n\t\t\tnewHeight = remeasureItemHeight.call(this, i)\r\n\t\t} catch (error) {\r\n\t\t\t// Successfully finishing an `onItemHeightDidChange(item)` call is not considered\r\n\t\t\t// critical for `VirtualScroller`'s operation, so such errors could be ignored.\r\n\t\t\tif (error instanceof ItemNotRenderedError) {\r\n\t\t\t\treturn reportError(`\"onItemHeightDidChange()\" has been called for item index ${i} but the item is not currently rendered and can\\'t be measured. The exact error was: ${error.message}`)\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tlog('Previous height', previousHeight)\r\n\t\tlog('New height', newHeight)\r\n\r\n\t\tif (previousHeight !== newHeight) {\r\n\t\t\tlog('~ Item height has changed. Should update layout. ~')\r\n\r\n\t\t\t// Update or reset a previously calculated layout with the new item height\r\n\t\t\t// so that the potential future \"diff\"s based on that \"previously calculated\" layout\r\n\t\t\t// would be correct.\r\n\t\t\t//\r\n\t\t\t// The \"previously calculated layout\" feature is not currently used\r\n\t\t\t// so this function call doesn't really affect anything.\r\n\t\t\t//\r\n\t\t\tupdatePreviouslyCalculatedLayoutOnItemHeightChange.call(this, i, previousHeight, newHeight)\r\n\r\n\t\t\t// Recalculate layout.\r\n\t\t\t//\r\n\t\t\t// If the `VirtualScroller` is already waiting for a state update to be rendered,\r\n\t\t\t// delay `onItemHeightDidChange(item)`'s re-layout until that state update is rendered.\r\n\t\t\t// The reason is that React `<VirtualScroller/>`'s `onHeightDidChange()` is meant to\r\n\t\t\t// be called inside `useLayoutEffect()` hook. Due to how React is implemented internally,\r\n\t\t\t// that might happen in the middle of the currently pending `setState()` operation\r\n\t\t\t// being applied, resulting in weird \"race condition\" bugs.\r\n\t\t\t//\r\n\t\t\tif (this._isActive) {\r\n\t\t\t\tif (this.waitingForRender) {\r\n\t\t\t\t\tlog('~ Another state update is already waiting to be rendered. Delay the layout update until then. ~')\r\n\t\t\t\t\tthis.updateLayoutAfterRenderBecauseItemHeightChanged = true\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthis.onUpdateShownItemIndexes({ reason: LAYOUT_REASON.ITEM_HEIGHT_CHANGED })\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// If there was a request for `setState()` with new `items`, then the changes\r\n\t\t\t// to `currentState.itemHeights[]` made above in a `remeasureItemHeight()` call\r\n\t\t\t// would be overwritten when that pending `setState()` call gets applied.\r\n\t\t\t// To fix that, the updates to current `itemHeights[]` are noted in\r\n\t\t\t// `this.itemHeightsThatChangedWhileNewItemsWereBeingRendered` variable.\r\n\t\t\t// That variable is then checked when the `setState()` call with the new `items`\r\n\t\t\t// has been updated.\r\n\t\t\tif (this.newItemsWillBeRendered) {\r\n\t\t\t\tif (!this.itemHeightsThatChangedWhileNewItemsWereBeingRendered) {\r\n\t\t\t\t\tthis.itemHeightsThatChangedWhileNewItemsWereBeingRendered = {}\r\n\t\t\t\t}\r\n\t\t\t\tthis.itemHeightsThatChangedWhileNewItemsWereBeingRendered[String(i)] = newHeight\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tthis.getPrerenderMargin = () => {\r\n\t\t// The list component renders not only the items that're currently visible\r\n\t\t// but also the items that lie within some extra vertical margin (called\r\n\t\t// \"prerender margin\") on top and bottom for future scrolling: this way,\r\n\t\t// there'll be significantly less layout recalculations as the user scrolls,\r\n\t\t// because now it doesn't have to recalculate layout on each scroll event.\r\n\t\t// By default, the \"prerender margin\" is equal to the screen height:\r\n\t\t// this seems to be the optimal value for \"Page Up\" / \"Page Down\" navigation\r\n\t\t// and optimized mouse wheel scrolling (a user is unlikely to continuously\r\n\t\t// scroll past the screen height, because they'd stop to read through\r\n\t\t// the newly visible items first, and when they do stop scrolling, that's\r\n\t\t// when layout gets recalculated).\r\n\t\treturn this.scrollableContainer.getHeight() * this.getPrerenderMarginRatio()\r\n\t}\r\n\r\n\tthis.getPrerenderMarginRatio = () => {\r\n\t\t// See the readme for the description of `prerenderMarginRatio` option.\r\n\t\treturn 1 // in scrollable container heights.\r\n\t}\r\n\r\n\t/**\r\n\t * Calls `onItemFirstRender()` for items that haven't been\r\n\t * \"seen\" previously.\r\n\t * @param {any[]} items\r\n\t * @param {number[]} itemHeights\r\n\t * @param {number} firstShownItemIndex\r\n\t * @param {number} lastShownItemIndex\r\n\t */\r\n\tthis.onBeforeShowItems = (\r\n\t\titems,\r\n\t\titemHeights,\r\n\t\tfirstShownItemIndex,\r\n\t\tlastShownItemIndex\r\n\t) => {\r\n\t\tif (this.onItemInitialRender) {\r\n\t\t\tlet i = firstShownItemIndex\r\n\t\t\twhile (i <= lastShownItemIndex) {\r\n\t\t\t\tif (itemHeights[i] === undefined) {\r\n\t\t\t\t\tthis.onItemInitialRender(items[i])\r\n\t\t\t\t}\r\n\t\t\t\ti++\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tthis.measureItemHeightsAndSpacing = () => {\r\n\t\t// Measure \"newly shown\" item heights.\r\n\t\t// Also re-validate already measured items' heights.\r\n\t\tthis.itemHeights.measureItemHeights(\r\n\t\t\tthis.getState().firstShownItemIndex,\r\n\t\t\tthis.getState().lastShownItemIndex\r\n\t\t)\r\n\r\n\t\t// Measure item vertical spacing, if required.\r\n\t\tconst verticalSpacing = this.measureVerticalSpacingIfNotMeasured()\r\n\r\n\t\t// Return a state update if vertical spacing has been measured.\r\n\t\t// Doesn't set `verticalSpacing: 0` in `state` because it is effectively\r\n\t\t// same as `verticalSpacing: undefined` in terms code behavior and calculations.\r\n\t\t// Not having `verticalSpacing: 0` in `state` just makes the `state` object\r\n\t\t// a bit more cleaner and a bit less cluttered (easier for inspection).\r\n\t\tif (verticalSpacing && verticalSpacing !== 0) {\r\n\t\t\t// Return a state update.\r\n\t\t\t// Sets `verticalSpacing` property in `state`.\r\n\t\t\treturn {\r\n\t\t\t\tverticalSpacing\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tthis.cancelLayoutTimer = ({ stateUpdate }) => {\r\n\t\tif (this.layoutTimer) {\r\n\t\t\tunscheduleOnNextTick(this.layoutTimer)\r\n\t\t\tthis.layoutTimer = undefined\r\n\t\t\t// Merge state updates.\r\n\t\t\tif (stateUpdate || this.layoutTimerStateUpdate) {\r\n\t\t\t\tstateUpdate = {\r\n\t\t\t\t\t...this.layoutTimerStateUpdate,\r\n\t\t\t\t\t...stateUpdate\r\n\t\t\t\t}\r\n\t\t\t\tthis.layoutTimerStateUpdate = undefined\r\n\t\t\t\treturn stateUpdate\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\treturn stateUpdate\r\n\t\t}\r\n\t}\r\n\r\n\tthis.scheduleLayoutTimer = ({ reason, stateUpdate }) => {\r\n\t\tthis.layoutTimerStateUpdate = stateUpdate\r\n\t\tthis.layoutTimer = scheduleOnNextTick(() => {\r\n\t\t\tthis.layoutTimerStateUpdate = undefined\r\n\t\t\tthis.layoutTimer = undefined\r\n\t\t\tthis.onUpdateShownItemIndexes({\r\n\t\t\t\treason,\r\n\t\t\t\tstateUpdate\r\n\t\t\t})\r\n\t\t})\r\n\t}\r\n}\r\n\r\nconst FPS = 60 // though 120 fps is common on smartphones these days.\r\nconst FRAME_DURATION = 1000 / FPS // in milliseconds\r\n\r\n// Layout is considered \"too slow\" when it \"blocks\" the \"main thread\"\r\n// for so long that it starts causing \"frame drops\".\r\nconst SLOW_LAYOUT_DURATION = FRAME_DURATION"],"mappings":"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"}
|
|
@@ -200,11 +200,16 @@ export default function () {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
if (layoutUpdateReason) {
|
|
203
|
+
// Update layout.
|
|
203
204
|
updateStateRightAfterRender.call(_this, {
|
|
204
205
|
stateUpdate: stateUpdate,
|
|
205
206
|
reason: layoutUpdateReason
|
|
206
207
|
});
|
|
207
208
|
} else if (stateUpdate) {
|
|
209
|
+
// Don't update layout. Just update some state.
|
|
210
|
+
// `stateUpdate` isn't supposed to affect the layout in any way
|
|
211
|
+
// because it doesn't contain any properties that affect the rendering of the list.
|
|
212
|
+
// I.e. it doesn't contain properties like `beforeItemsHeight` or `afterItemsHeight`, etc.
|
|
208
213
|
_this.updateState(stateUpdate);
|
|
209
214
|
} else {
|
|
210
215
|
log('~ Finished Layout ~');
|
|
@@ -319,12 +324,21 @@ export default function () {
|
|
|
319
324
|
var reason = _ref.reason,
|
|
320
325
|
stateUpdate = _ref.stateUpdate;
|
|
321
326
|
|
|
322
|
-
//
|
|
327
|
+
// React doesn't like it when `setState()` is called during render:
|
|
328
|
+
//
|
|
323
329
|
// "Maximum update depth exceeded.
|
|
324
330
|
// This can happen when a component repeatedly calls
|
|
325
331
|
// `.updateState()` inside `componentWillUpdate()` or `componentDidUpdate()`.
|
|
326
332
|
// React limits the number of nested updates to prevent infinite loops."
|
|
327
|
-
|
|
333
|
+
//
|
|
334
|
+
// To work around that, in React it always prefers a delayed state update
|
|
335
|
+
// over an immediate state update right after a prevous state update.
|
|
336
|
+
//
|
|
337
|
+
// Another case when it should introduce an artificial momentary delay
|
|
338
|
+
// in a chain of immediate "syncrhonous" re-renders is when such chain
|
|
339
|
+
// of re-renders has been freezing the UI for far too long.
|
|
340
|
+
//
|
|
341
|
+
if (this._delayRecursiveRenderUntilNextTick || this._delayRecursiveRenderUntilNextTick !== false && this._stateUpdateSequenceStartedAt !== undefined && Date.now() - this._stateUpdateSequenceStartedAt > UNINTERRUPTED_RECURSIVE_STATE_UPDATE_SEQUENCE_MAX_ALLOWED_DURATION) {
|
|
328
342
|
// Cancel a previously scheduled re-layout.
|
|
329
343
|
stateUpdate = this.cancelLayoutTimer({
|
|
330
344
|
stateUpdate: stateUpdate
|
|
@@ -386,4 +400,12 @@ export default function () {
|
|
|
386
400
|
};
|
|
387
401
|
}
|
|
388
402
|
}
|
|
403
|
+
var FPS = 60; // though 120 fps is common on smartphones these days.
|
|
404
|
+
|
|
405
|
+
var FRAME_DURATION = 1000 / FPS; // in milliseconds
|
|
406
|
+
// Allow recursive "synchronous" re-renders
|
|
407
|
+
// until they start "blocking" the "main thread"
|
|
408
|
+
// for so long that it starts causing "frame drops".
|
|
409
|
+
|
|
410
|
+
var UNINTERRUPTED_RECURSIVE_STATE_UPDATE_SEQUENCE_MAX_ALLOWED_DURATION = FRAME_DURATION;
|
|
389
411
|
//# sourceMappingURL=VirtualScroller.onRender.js.map
|