virtual-scroller 1.7.6 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -1
- package/README.md +139 -33
- package/babel.config.js +25 -0
- package/babel.js +5 -0
- package/bundle/index-bypass.html +1 -1
- package/bundle/index-dom.html +1 -1
- package/bundle/index-grid.html +1 -2
- package/bundle/index-scrollableContainer.html +1 -1
- package/bundle/index-tbody-scrollableContainer.html +2 -0
- package/bundle/index-tbody.html +2 -0
- package/bundle/virtual-scroller-dom.js +1 -1
- package/bundle/virtual-scroller-dom.js.map +1 -1
- package/bundle/virtual-scroller-react.js +1 -1
- package/bundle/virtual-scroller-react.js.map +1 -1
- package/bundle/virtual-scroller.js +1 -1
- package/bundle/virtual-scroller.js.map +1 -1
- package/commonjs/BeforeResize.js +319 -0
- package/commonjs/BeforeResize.js.map +1 -0
- package/commonjs/DOM/Engine.js +46 -0
- package/commonjs/DOM/Engine.js.map +1 -0
- package/commonjs/DOM/ItemsContainer.js +78 -0
- package/commonjs/DOM/ItemsContainer.js.map +1 -0
- package/commonjs/DOM/{WaitForStylesToLoad.js → ListTopOffsetWatcher.js} +56 -35
- package/commonjs/DOM/ListTopOffsetWatcher.js.map +1 -0
- package/commonjs/DOM/ScrollableContainer.js +56 -81
- package/commonjs/DOM/ScrollableContainer.js.map +1 -1
- package/commonjs/DOM/VirtualScroller.js +20 -15
- package/commonjs/DOM/VirtualScroller.js.map +1 -1
- package/commonjs/DOM/tbody.js +2 -2
- package/commonjs/ItemHeights.js +13 -20
- package/commonjs/ItemHeights.js.map +1 -1
- package/commonjs/Layout.js +588 -215
- package/commonjs/Layout.js.map +1 -1
- package/commonjs/Layout.test.js +191 -0
- package/commonjs/Layout.test.js.map +1 -0
- package/commonjs/ListHeightChangeWatcher.js +126 -0
- package/commonjs/ListHeightChangeWatcher.js.map +1 -0
- package/commonjs/Resize.js +22 -21
- package/commonjs/Resize.js.map +1 -1
- package/commonjs/Scroll.js +148 -88
- package/commonjs/Scroll.js.map +1 -1
- package/commonjs/VirtualScroller.js +1269 -390
- package/commonjs/VirtualScroller.js.map +1 -1
- package/commonjs/getItemCoordinates.js.map +1 -1
- package/commonjs/getItemsDiff.js.map +1 -1
- package/commonjs/getVerticalSpacing.js +8 -8
- package/commonjs/getVerticalSpacing.js.map +1 -1
- package/commonjs/react/VirtualScroller.js +31 -37
- package/commonjs/react/VirtualScroller.js.map +1 -1
- package/commonjs/utility/debounce.js +26 -4
- package/commonjs/utility/debounce.js.map +1 -1
- package/commonjs/utility/debug.js +51 -12
- package/commonjs/utility/debug.js.map +1 -1
- package/commonjs/utility/getStateSnapshot.js +50 -0
- package/commonjs/utility/getStateSnapshot.js.map +1 -0
- package/commonjs/utility/px.js +1 -1
- package/commonjs/utility/px.js.map +1 -1
- package/commonjs/utility/px.test.js +14 -0
- package/commonjs/utility/px.test.js.map +1 -0
- package/commonjs/utility/shallowEqual.js +1 -1
- package/commonjs/utility/shallowEqual.js.map +1 -1
- package/commonjs/utility/throttle.js.map +1 -1
- package/dom/index.d.ts +23 -0
- package/index.d.ts +84 -0
- package/modules/BeforeResize.js +310 -0
- package/modules/BeforeResize.js.map +1 -0
- package/modules/DOM/Engine.js +27 -0
- package/modules/DOM/Engine.js.map +1 -0
- package/modules/DOM/ItemsContainer.js +71 -0
- package/modules/DOM/ItemsContainer.js.map +1 -0
- package/modules/DOM/{WaitForStylesToLoad.js → ListTopOffsetWatcher.js} +57 -35
- package/modules/DOM/ListTopOffsetWatcher.js.map +1 -0
- package/modules/DOM/ScrollableContainer.js +55 -80
- package/modules/DOM/ScrollableContainer.js.map +1 -1
- package/modules/DOM/VirtualScroller.js +15 -14
- package/modules/DOM/VirtualScroller.js.map +1 -1
- package/modules/ItemHeights.js +8 -19
- package/modules/ItemHeights.js.map +1 -1
- package/modules/Layout.js +582 -213
- package/modules/Layout.js.map +1 -1
- package/modules/Layout.test.js +185 -0
- package/modules/Layout.test.js.map +1 -0
- package/modules/ListHeightChangeWatcher.js +119 -0
- package/modules/ListHeightChangeWatcher.js.map +1 -0
- package/modules/Resize.js +21 -20
- package/modules/Resize.js.map +1 -1
- package/modules/Scroll.js +148 -87
- package/modules/Scroll.js.map +1 -1
- package/modules/VirtualScroller.js +1263 -390
- package/modules/VirtualScroller.js.map +1 -1
- package/modules/getItemCoordinates.js.map +1 -1
- package/modules/getItemsDiff.js.map +1 -1
- package/modules/getVerticalSpacing.js +8 -8
- package/modules/getVerticalSpacing.js.map +1 -1
- package/modules/react/VirtualScroller.js +31 -37
- package/modules/react/VirtualScroller.js.map +1 -1
- package/modules/utility/debounce.js +26 -4
- package/modules/utility/debounce.js.map +1 -1
- package/modules/utility/debug.js +47 -10
- package/modules/utility/debug.js.map +1 -1
- package/modules/utility/getStateSnapshot.js +43 -0
- package/modules/utility/getStateSnapshot.js.map +1 -0
- package/modules/utility/px.js +1 -1
- package/modules/utility/px.js.map +1 -1
- package/modules/utility/px.test.js +9 -0
- package/modules/utility/px.test.js.map +1 -0
- package/modules/utility/shallowEqual.js +1 -1
- package/modules/utility/shallowEqual.js.map +1 -1
- package/modules/utility/throttle.js.map +1 -1
- package/package.json +24 -22
- package/react/index.d.ts +27 -0
- package/source/BeforeResize.js +317 -0
- package/source/DOM/Engine.js +32 -0
- package/source/DOM/ItemsContainer.js +48 -0
- package/source/DOM/{WaitForStylesToLoad.js → ListTopOffsetWatcher.js} +48 -22
- package/source/DOM/ScrollableContainer.js +39 -56
- package/source/DOM/VirtualScroller.js +6 -7
- package/source/ItemHeights.js +10 -15
- package/source/Layout.js +626 -252
- package/source/Layout.test.js +171 -0
- package/source/ListHeightChangeWatcher.js +94 -0
- package/source/Resize.js +23 -15
- package/source/Scroll.js +139 -78
- package/source/VirtualScroller.js +1240 -286
- package/source/getVerticalSpacing.js +7 -7
- package/source/react/VirtualScroller.js +2 -18
- package/source/utility/debounce.js +20 -3
- package/source/utility/debug.js +34 -3
- package/source/utility/getStateSnapshot.js +36 -0
- package/source/utility/px.js +1 -1
- package/source/utility/px.test.js +9 -0
- package/website/index-bypass.html +195 -0
- package/website/index-grid.html +0 -1
- package/website/index-scrollableContainer.html +208 -0
- package/website/index-tbody-scrollableContainer.html +68 -0
- package/website/index-tbody.html +55 -0
- package/commonjs/DOM/RenderingEngine.js +0 -33
- package/commonjs/DOM/RenderingEngine.js.map +0 -1
- package/commonjs/DOM/Screen.js +0 -87
- package/commonjs/DOM/Screen.js.map +0 -1
- package/commonjs/DOM/WaitForStylesToLoad.js.map +0 -1
- package/commonjs/RestoreScroll.js +0 -118
- package/commonjs/RestoreScroll.js.map +0 -1
- package/modules/DOM/RenderingEngine.js +0 -19
- package/modules/DOM/RenderingEngine.js.map +0 -1
- package/modules/DOM/Screen.js +0 -80
- package/modules/DOM/Screen.js.map +0 -1
- package/modules/DOM/WaitForStylesToLoad.js.map +0 -1
- package/modules/RestoreScroll.js +0 -111
- package/modules/RestoreScroll.js.map +0 -1
- package/source/DOM/RenderingEngine.js +0 -22
- package/source/DOM/Screen.js +0 -51
- package/source/RestoreScroll.js +0 -86
package/commonjs/utility/px.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../source/utility/px.js"],"names":["px","number","toFixed"],"mappings":";;;;;;;AAAA
|
|
1
|
+
{"version":3,"sources":["../../source/utility/px.js"],"names":["px","number","toFixed"],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAASA,EAAT,CAAYC,MAAZ,EAAoB;AAClC;AACC,SAAO,CAACA,MAAM,GAAG,CAAT,KAAe,CAAf,GAAmBA,MAAnB,GAA4BA,MAAM,CAACC,OAAP,CAAe,CAAf,CAA7B,IAAkD,IAAzD;AACD","sourcesContent":["/**\r\n * Rounds coordinates upto 4th decimal place (after dot) and appends \"px\".\r\n * Small numbers could be printed as `\"1.2345e-50\"` unless rounded:\r\n * that would be invalid \"px\" value in CSS.\r\n * @param {number}\r\n * @return {string}\r\n */\r\nexport default function px(number) {\r\n\t// Fractional pixels are used on \"retina\" screens.\r\n return (number % 1 === 0 ? number : number.toFixed(2)) + 'px'\r\n}"],"file":"px.js"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _px = _interopRequireDefault(require("./px"));
|
|
4
|
+
|
|
5
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
6
|
+
|
|
7
|
+
describe('utility/px', function () {
|
|
8
|
+
it('should truncate px values', function () {
|
|
9
|
+
(0, _px["default"])(0).should.equal('0px');
|
|
10
|
+
(0, _px["default"])(1).should.equal('1px');
|
|
11
|
+
(0, _px["default"])(1.2345).should.equal('1.23px');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=px.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../source/utility/px.test.js"],"names":["describe","it","should","equal"],"mappings":";;AAAA;;;;AAEAA,QAAQ,CAAC,YAAD,EAAe,YAAW;AACjCC,EAAAA,EAAE,CAAC,2BAAD,EAA8B,YAAW;AAC1C,wBAAG,CAAH,EAAMC,MAAN,CAAaC,KAAb,CAAmB,KAAnB;AACA,wBAAG,CAAH,EAAMD,MAAN,CAAaC,KAAb,CAAmB,KAAnB;AACA,wBAAG,MAAH,EAAWD,MAAX,CAAkBC,KAAlB,CAAwB,QAAxB;AACA,GAJC,CAAF;AAKA,CANO,CAAR","sourcesContent":["import px from './px'\r\n\r\ndescribe('utility/px', function() {\r\n\tit('should truncate px values', function() {\r\n\t\tpx(0).should.equal('0px')\r\n\t\tpx(1).should.equal('1px')\r\n\t\tpx(1.2345).should.equal('1.23px')\r\n\t})\r\n})"],"file":"px.test.js"}
|
|
@@ -20,7 +20,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
20
20
|
});
|
|
21
21
|
exports["default"] = shallowEqual;
|
|
22
22
|
|
|
23
|
-
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
|
|
23
|
+
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
|
|
24
24
|
|
|
25
25
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
26
26
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../source/utility/shallowEqual.js"],"names":["hasOwnProperty","Object","prototype","is","x","y","shallowEqual","objA","objB","keysA","keys","keysB","length","i","call"],"mappings":"AAAA;AACA;;AAEA
|
|
1
|
+
{"version":3,"sources":["../../source/utility/shallowEqual.js"],"names":["hasOwnProperty","Object","prototype","is","x","y","shallowEqual","objA","objB","keysA","keys","keysB","length","i","call"],"mappings":"AAAA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AAEA;;;;;;;;;AAEA,IAAMA,cAAc,GAAGC,MAAM,CAACC,SAAP,CAAiBF,cAAxC;AAEA;AACA;AACA;AACA;;AACA,SAASG,EAAT,CAAYC,CAAZ,EAAeC,CAAf,EAAkB;AAChB;AACA,MAAID,CAAC,KAAKC,CAAV,EAAa;AAAE;AACb;AACA;AACA,WAAOD,CAAC,KAAK,CAAN,IAAWC,CAAC,KAAK,CAAjB,IAAsB,IAAID,CAAJ,KAAU,IAAIC,CAA3C;AACD,GAJD,MAIO;AACL;AACA,WAAOD,CAAC,KAAKA,CAAN,IAAWC,CAAC,KAAKA,CAAxB;AACD;AACF;AAED;AACA;AACA;AACA;AACA;;;AACe,SAASC,YAAT,CAAsBC,IAAtB,EAA4BC,IAA5B,EAAkC;AAC/C,MAAIL,EAAE,CAACI,IAAD,EAAOC,IAAP,CAAN,EAAoB;AAClB,WAAO,IAAP;AACD;;AAED,MAAI,QAAOD,IAAP,MAAgB,QAAhB,IAA4BA,IAAI,KAAK,IAArC,IACA,QAAOC,IAAP,MAAgB,QADhB,IAC4BA,IAAI,KAAK,IADzC,EAC+C;AAC7C,WAAO,KAAP;AACD;;AAED,MAAMC,KAAK,GAAGR,MAAM,CAACS,IAAP,CAAYH,IAAZ,CAAd;AACA,MAAMI,KAAK,GAAGV,MAAM,CAACS,IAAP,CAAYF,IAAZ,CAAd;;AAEA,MAAIC,KAAK,CAACG,MAAN,KAAiBD,KAAK,CAACC,MAA3B,EAAmC;AACjC,WAAO,KAAP;AACD,GAf8C,CAiB/C;;;AACA,OAAK,IAAIC,CAAC,GAAG,CAAb,EAAgBA,CAAC,GAAGJ,KAAK,CAACG,MAA1B,EAAkCC,CAAC,EAAnC,EAAuC;AACrC,QACE,CAACb,cAAc,CAACc,IAAf,CAAoBN,IAApB,EAA0BC,KAAK,CAACI,CAAD,CAA/B,CAAD,IACA,CAACV,EAAE,CAACI,IAAI,CAACE,KAAK,CAACI,CAAD,CAAN,CAAL,EAAiBL,IAAI,CAACC,KAAK,CAACI,CAAD,CAAN,CAArB,CAFL,EAGE;AACA,aAAO,KAAP;AACD;AACF;;AAED,SAAO,IAAP;AACD","sourcesContent":["// https://github.com/lodash/lodash/issues/2340\r\n// https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js\r\n\r\n/**\r\n * Copyright (c) 2013-present, Facebook, Inc.\r\n *\r\n * This source code is licensed under the MIT license found in the\r\n * LICENSE file in the root directory of this source tree.\r\n *\r\n * @providesModule shallowEqual\r\n * @typechecks\r\n * @flow\r\n */\r\n\r\n/*eslint-disable no-self-compare */\r\n\r\n'use strict';\r\n\r\nconst hasOwnProperty = Object.prototype.hasOwnProperty;\r\n\r\n/**\r\n * inlined Object.is polyfill to avoid requiring consumers ship their own\r\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is\r\n */\r\nfunction is(x, y) {\r\n // SameValue algorithm\r\n if (x === y) { // Steps 1-5, 7-10\r\n // Steps 6.b-6.e: +0 != -0\r\n // Added the nonzero y check to make Flow happy, but it is redundant\r\n return x !== 0 || y !== 0 || 1 / x === 1 / y;\r\n } else {\r\n // Step 6.a: NaN == NaN\r\n return x !== x && y !== y;\r\n }\r\n}\r\n\r\n/**\r\n * Performs equality by iterating through keys on an object and returning false\r\n * when any key has values which are not strictly equal between the arguments.\r\n * Returns true when the values of all keys are strictly equal.\r\n */\r\nexport default function shallowEqual(objA, objB) {\r\n if (is(objA, objB)) {\r\n return true;\r\n }\r\n\r\n if (typeof objA !== 'object' || objA === null ||\r\n typeof objB !== 'object' || objB === null) {\r\n return false;\r\n }\r\n\r\n const keysA = Object.keys(objA);\r\n const keysB = Object.keys(objB);\r\n\r\n if (keysA.length !== keysB.length) {\r\n return false;\r\n }\r\n\r\n // Test for A's keys different from B.\r\n for (let i = 0; i < keysA.length; i++) {\r\n if (\r\n !hasOwnProperty.call(objB, keysA[i]) ||\r\n !is(objA[keysA[i]], objB[keysA[i]])\r\n ) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}"],"file":"shallowEqual.js"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../source/utility/throttle.js"],"names":["throttle","func","interval","timeout","executedAt","scheduled","undefined","Date","now","remaining"],"mappings":";;;;;;;AAIA;;AAJA;AACA;AACA;AACA;;AAGA
|
|
1
|
+
{"version":3,"sources":["../../source/utility/throttle.js"],"names":["throttle","func","interval","timeout","executedAt","scheduled","undefined","Date","now","remaining"],"mappings":";;;;;;;AAIA;;AAJA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACe,SAASA,QAAT,CAAkBC,IAAlB,EAAwBC,QAAxB,EAAkC;AAChD,MAAIC,OAAJ;AACA,MAAIC,UAAU,GAAG,CAAjB;;AACA,MAAIC,SAAS,GAAG,SAAZA,SAAY,GAAW;AAC1BF,IAAAA,OAAO,GAAGG,SAAV;AACAF,IAAAA,UAAU,GAAGG,IAAI,CAACC,GAAL,EAAb;AACAP,IAAAA,IAAI;AACJ,GAJD;;AAKA,SAAO,YAAW;AACjB,QAAMO,GAAG,GAAGD,IAAI,CAACC,GAAL,EAAZ;AACA,QAAMC,SAAS,GAAGP,QAAQ,IAAIM,GAAG,GAAGJ,UAAV,CAA1B;;AACA,QAAIK,SAAS,IAAI,CAAjB,EAAoB;AACnB,UAAIN,OAAJ,EAAa;AACZ,wDAAaA,OAAb;AACAA,QAAAA,OAAO,GAAGG,SAAV;AACA;;AACDF,MAAAA,UAAU,GAAGI,GAAb;AACAP,MAAAA,IAAI;AACJ,KAPD,MAOO,IAAI,CAACE,OAAL,EAAc;AACpBA,MAAAA,OAAO,GAAG,8CAAWE,SAAX,EAAsBI,SAAtB,CAAV;AACA;AACD,GAbD;AAcA","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\n/**\r\n * Same as `lodash`'s `throttle()` for functions with no arguments.\r\n * @param {function} func\r\n * @param {number} interval\r\n * @return {function}\r\n */\r\nexport default function throttle(func, interval) {\r\n\tlet timeout\r\n\tlet executedAt = 0\r\n\tlet scheduled = function() {\r\n\t\ttimeout = undefined\r\n\t\texecutedAt = Date.now()\r\n\t\tfunc()\r\n\t}\r\n\treturn function() {\r\n\t\tconst now = Date.now()\r\n\t\tconst remaining = interval - (now - executedAt)\r\n\t\tif (remaining <= 0) {\r\n\t\t\tif (timeout) {\r\n\t\t\t\tclearTimeout(timeout)\r\n\t\t\t\ttimeout = undefined\r\n\t\t\t}\r\n\t\t\texecutedAt = now\r\n\t\t\tfunc()\r\n\t\t} else if (!timeout) {\r\n\t\t\ttimeout = setTimeout(scheduled, remaining)\r\n\t\t}\r\n\t}\r\n}"],"file":"throttle.js"}
|
package/dom/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { State, VirtualScrollerCommonOptions, VirtualScrollerSetItemsOptions } from '../index.d.ts';
|
|
2
|
+
|
|
3
|
+
export { State, ItemState } from '../index.d.ts';
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
|
|
7
|
+
interface Options<Item> extends VirtualScrollerCommonOptions<HTMLElement, Item> {
|
|
8
|
+
onMount?: () => void;
|
|
9
|
+
onItemUnmount?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default class VirtualScroller<Item> {
|
|
13
|
+
constructor(
|
|
14
|
+
itemsContainerElement: HTMLElement,
|
|
15
|
+
items: Item[],
|
|
16
|
+
renderItem: (item: Item) => HTMLElement,
|
|
17
|
+
options?: Options<Item>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
stop(): void;
|
|
21
|
+
setItems(newItems: Item[], options?: VirtualScrollerSetItemsOptions): void;
|
|
22
|
+
onItemHeightChange(i: number): void;
|
|
23
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export type ItemHeight = number | undefined;
|
|
2
|
+
export type ItemState = any | undefined;
|
|
3
|
+
|
|
4
|
+
export interface State<Item> {
|
|
5
|
+
items: Item[];
|
|
6
|
+
firstShownItemIndex: number;
|
|
7
|
+
lastShownItemIndex: number;
|
|
8
|
+
beforeItemsHeight: number;
|
|
9
|
+
afterItemsHeight: number;
|
|
10
|
+
itemStates: ItemState[];
|
|
11
|
+
itemHeights: ItemHeight[];
|
|
12
|
+
verticalSpacing?: number;
|
|
13
|
+
columnsCount?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ItemsContainer<Element> {
|
|
17
|
+
constructor(getElement: () => Element);
|
|
18
|
+
getNthRenderedItemTopOffset(renderedElementIndex: number): number;
|
|
19
|
+
getNthRenderedItemHeight(renderedElementIndex: number): number;
|
|
20
|
+
getHeight(): number;
|
|
21
|
+
clear(): void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ScrollableContainer<Element> {
|
|
25
|
+
constructor(element: Element, getItemsContainerElement: () => Element);
|
|
26
|
+
getWidth(): number;
|
|
27
|
+
getHeight(): number;
|
|
28
|
+
getItemsContainerTopOffset(): number;
|
|
29
|
+
getScrollY(): number;
|
|
30
|
+
scrollToY(scrollY: number): void;
|
|
31
|
+
onScroll(onScroll: () => void): () => void;
|
|
32
|
+
onResize(onResize: () => void): () => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface Engine<Element> {
|
|
36
|
+
createItemsContainer(getItemsContainerElement: () => Element): ItemsContainer<Element>;
|
|
37
|
+
createScrollableContainer(scrollableContainer: Element, getItemsContainerElement: () => Element): ScrollableContainer<Element>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface VirtualScrollerCommonOptionsWithoutInitialState<Element, Item> {
|
|
41
|
+
bypass?: boolean;
|
|
42
|
+
onStateChange?(newState: State<Item>, prevState?: State<Item>);
|
|
43
|
+
measureItemsBatchSize?: number;
|
|
44
|
+
estimatedItemHeight?: number;
|
|
45
|
+
initialScrollPosition?: number;
|
|
46
|
+
onScrollPositionChange?(scrollY: number): void;
|
|
47
|
+
onItemInitialRender?(item: Item): void;
|
|
48
|
+
getItemId?(item: Item): any;
|
|
49
|
+
getColumnsCount?(scrollableContainer: Element): number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface VirtualScrollerCommonOptions<Element, Item> extends VirtualScrollerCommonOptionsWithoutInitialState<Element, Item> {
|
|
53
|
+
state?: State<Item>;
|
|
54
|
+
customState?: object;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface Options<Element, Item> extends VirtualScrollerCommonOptions<Element, Item> {
|
|
58
|
+
getState?(): State<Item>;
|
|
59
|
+
setState?(stateUpdate: Partial<State<Item>>): void;
|
|
60
|
+
engine?: Engine<Element>;
|
|
61
|
+
tbody?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface VirtualScrollerSetItemsOptions {
|
|
65
|
+
preserveScrollPositionOnPrependItems?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default class VirtualScroller<Element, Item> {
|
|
69
|
+
constructor(
|
|
70
|
+
getItemsContainerElement: () => Element,
|
|
71
|
+
items: Item[],
|
|
72
|
+
options?: Options<Element, Item>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
listen(): void;
|
|
76
|
+
stop(): void;
|
|
77
|
+
willUpdateState(newState: State<Item>, prevState: State<Item>): void;
|
|
78
|
+
didUpdateState(prevState: State<Item>): void;
|
|
79
|
+
updateLayout(): void;
|
|
80
|
+
setItems(newItems: Item[], options?: VirtualScrollerSetItemsOptions): void;
|
|
81
|
+
onItemHeightChange(i: number): void;
|
|
82
|
+
onItemStateChange(i: number, itemState?: object): void;
|
|
83
|
+
getItemScrollPosition(i: number): number | undefined;
|
|
84
|
+
}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
|
|
2
|
+
|
|
3
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
|
|
4
|
+
|
|
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
|
+
|
|
7
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
8
|
+
|
|
9
|
+
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
|
10
|
+
|
|
11
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
|
12
|
+
|
|
13
|
+
import log from './utility/debug';
|
|
14
|
+
|
|
15
|
+
var BeforeResize = /*#__PURE__*/function () {
|
|
16
|
+
function BeforeResize(_ref) {
|
|
17
|
+
var getState = _ref.getState,
|
|
18
|
+
getVerticalSpacing = _ref.getVerticalSpacing,
|
|
19
|
+
getColumnsCount = _ref.getColumnsCount;
|
|
20
|
+
|
|
21
|
+
_classCallCheck(this, BeforeResize);
|
|
22
|
+
|
|
23
|
+
this.getState = getState;
|
|
24
|
+
this.getVerticalSpacing = getVerticalSpacing;
|
|
25
|
+
this.getColumnsCount = getColumnsCount;
|
|
26
|
+
} // Possibly clean up "before resize" property in state.
|
|
27
|
+
// "Before resize" state property is cleaned up when all "before resize" item heights
|
|
28
|
+
// have been re-measured in an asynchronous `this.setState({ beforeResize: undefined })` call.
|
|
29
|
+
// If `VirtualScroller` state was snapshotted externally before that `this.setState()` call
|
|
30
|
+
// has been applied, then "before resize" property might have not been cleaned up properly.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
_createClass(BeforeResize, [{
|
|
34
|
+
key: "onInitialState",
|
|
35
|
+
value: function onInitialState(state) {
|
|
36
|
+
if (state) {
|
|
37
|
+
if (state.beforeResize) {
|
|
38
|
+
if (state.beforeResize.itemHeights.length === 0) {
|
|
39
|
+
state.beforeResize = undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (state.beforeResize) {
|
|
44
|
+
this._includesBeforeResizeInState = true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} // Cleans up "before resize" item heights and adjusts the scroll position accordingly.
|
|
48
|
+
//
|
|
49
|
+
// Hypothetically, it could also wait for the user to stop scrolling and only then
|
|
50
|
+
// adjust the scroll position. The rationale is that if `window.scrollTo()` is called
|
|
51
|
+
// while the user is scrolling, the user would occasionally experience "lost" mouse wheel
|
|
52
|
+
// events when scrolling with a mouse wheel.
|
|
53
|
+
//
|
|
54
|
+
// Seems like Twitter's website waits for the user to stop scrolling before applying
|
|
55
|
+
// the scroll position correction after a window resize. This library could do that too,
|
|
56
|
+
// but that would require rewriting "before items height" top padding calculation
|
|
57
|
+
// so that it doesn't re-calculate it on every re-render and instead does so incrementally,
|
|
58
|
+
// and then, when the user stops, it re-calculates it from scratch removing the error
|
|
59
|
+
// and adjusting the scroll position accordingly so that there's no "jump of content".
|
|
60
|
+
//
|
|
61
|
+
// But, seems like it works fine as it is and there's no need to rewrite anything.
|
|
62
|
+
//
|
|
63
|
+
|
|
64
|
+
}, {
|
|
65
|
+
key: "cleanUpBeforeResizeItemHeights",
|
|
66
|
+
value: function cleanUpBeforeResizeItemHeights(prevState) {
|
|
67
|
+
var _this$getState = this.getState(),
|
|
68
|
+
firstShownItemIndex = _this$getState.firstShownItemIndex,
|
|
69
|
+
lastShownItemIndex = _this$getState.lastShownItemIndex,
|
|
70
|
+
itemHeights = _this$getState.itemHeights,
|
|
71
|
+
beforeResize = _this$getState.beforeResize; // If there're "before resize" properties in `state`
|
|
72
|
+
// then it means that the corresponding items are waiting to be
|
|
73
|
+
// re-measured after container resize. Since the resize,
|
|
74
|
+
// some of those non-re-measured items might have just been measured,
|
|
75
|
+
// so see if that's true, and if it is, remove those now-obsolete
|
|
76
|
+
// "before resize" item heights and ajust the scroll position
|
|
77
|
+
// so that there's no "content jumping".
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if (beforeResize) {
|
|
81
|
+
// If the user has scrolled up to reveal a previously hidden item
|
|
82
|
+
// that has not yet been re-measured after a previous resize.
|
|
83
|
+
if (firstShownItemIndex < beforeResize.itemHeights.length) {
|
|
84
|
+
log('~ Clean up "before resize" item heights and correct scroll position ~'); // Some of the "before" items have been un-hidden and re-measured.
|
|
85
|
+
// Un-hiding those items would result in a "jump of content"
|
|
86
|
+
// because "before resize" heights of those un-hidden items
|
|
87
|
+
// could (and most likely will) be different from the current ones,
|
|
88
|
+
// or because "before resize" columns count is different from
|
|
89
|
+
// the current one.
|
|
90
|
+
// To prevent a "jump of content", calculate the scroll position
|
|
91
|
+
// difference and adjust the scroll position.
|
|
92
|
+
// The height of the item rows that have transitioned
|
|
93
|
+
// from hidden to shown.
|
|
94
|
+
|
|
95
|
+
var newlyShownItemRowsHeight = 0; // Some of the `itemHeights` between the current `firstShownItemIndex` and
|
|
96
|
+
// the previous `firstShownItemIndex` could stay `undefined` if the user
|
|
97
|
+
// scrolled "abruptly": for example, by using a `window.scrollTo()` call.
|
|
98
|
+
// In that case, the items below the visible ones won't be rendered and measured.
|
|
99
|
+
// In such case, limit the items being iterated over to the current `lastShownItemIndex`
|
|
100
|
+
// rather than the previous `firstShownItemIndex`.
|
|
101
|
+
|
|
102
|
+
var prevFirstReMeasuredItemsRowIndex = Math.floor(beforeResize.itemHeights.length / this.getColumnsCount());
|
|
103
|
+
var newlyShownItemsToIndex = Math.min(prevFirstReMeasuredItemsRowIndex * this.getColumnsCount() - 1, lastShownItemIndex);
|
|
104
|
+
var i = firstShownItemIndex;
|
|
105
|
+
|
|
106
|
+
while (i <= newlyShownItemsToIndex) {
|
|
107
|
+
// Calculate newly shown row height.
|
|
108
|
+
var rowHeight = 0;
|
|
109
|
+
var columnIndex = 0;
|
|
110
|
+
|
|
111
|
+
while (columnIndex < this.getColumnsCount() && i <= newlyShownItemsToIndex) {
|
|
112
|
+
var itemHeight = itemHeights[i];
|
|
113
|
+
|
|
114
|
+
if (itemHeight === undefined) {
|
|
115
|
+
// `itemHeight` can only be `undefined` when not `beforeResize`.
|
|
116
|
+
// Use the current "average item height" as a substitute.
|
|
117
|
+
itemHeight = this.getAverageItemHeight();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
rowHeight = Math.max(rowHeight, itemHeight);
|
|
121
|
+
i++;
|
|
122
|
+
columnIndex++;
|
|
123
|
+
} // Append to the total "newly shown item rows height".
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
newlyShownItemRowsHeight += rowHeight;
|
|
127
|
+
newlyShownItemRowsHeight += this.getVerticalSpacing();
|
|
128
|
+
} // The height of the "before resize" item rows
|
|
129
|
+
// that will be "cleaned up" in this function call.
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
var cleanedUpBeforeResizeItemRowsHeight = 0; // Some of the `beforeResize` item rows might have been skipped if the user
|
|
133
|
+
// scrolled up "abruptly": for example, by using a `window.scrollTo()` call.
|
|
134
|
+
// In that case, the "before resize" items below the bottom border of the screen
|
|
135
|
+
// shouldn't be accounted for when calculating the scrollbar adjustment shift
|
|
136
|
+
// because items after `lastShownItemIndex` aren't participating in the calculation
|
|
137
|
+
// of `newlyShownItemRowsHeight`.
|
|
138
|
+
|
|
139
|
+
var maxParticipatingBeforeResizeItemsCount = Math.min(beforeResize.itemHeights.length, lastShownItemIndex + 1);
|
|
140
|
+
var participatingBeforeResizeItemRowsCount = Math.ceil(maxParticipatingBeforeResizeItemsCount / beforeResize.columnsCount);
|
|
141
|
+
var firstCleanedUpBeforeResizeItemsRowIndex = firstShownItemIndex === 0 ? 0 : Math.floor((firstShownItemIndex - 1) / beforeResize.columnsCount) + 1;
|
|
142
|
+
var k = firstCleanedUpBeforeResizeItemsRowIndex;
|
|
143
|
+
|
|
144
|
+
while (k < participatingBeforeResizeItemRowsCount) {
|
|
145
|
+
var _rowHeight = beforeResize.itemHeights[k * beforeResize.columnsCount];
|
|
146
|
+
cleanedUpBeforeResizeItemRowsHeight += _rowHeight;
|
|
147
|
+
cleanedUpBeforeResizeItemRowsHeight += beforeResize.verticalSpacing;
|
|
148
|
+
k++;
|
|
149
|
+
} // Schedule an asynchronous `this.setState()` call that will update
|
|
150
|
+
// `beforeResize` property of `state`. Ideally, it should be updated
|
|
151
|
+
// immediately, but since `this.setState()` calls are asynchronous,
|
|
152
|
+
// the code updates just the underlying `beforeResize.itemHeights`
|
|
153
|
+
// array immediately instead, which is still a hack but still a lesser one.
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
if (firstShownItemIndex === 0) {
|
|
157
|
+
log('Drop all "before resize" item heights');
|
|
158
|
+
} else {
|
|
159
|
+
var firstDroppedBeforeResizeItemIndex = firstShownItemIndex;
|
|
160
|
+
var lastDroppedBeforeResizeItemIndex = beforeResize.itemHeights.length - 1;
|
|
161
|
+
|
|
162
|
+
if (firstDroppedBeforeResizeItemIndex === lastDroppedBeforeResizeItemIndex) {
|
|
163
|
+
log('For item index', firstDroppedBeforeResizeItemIndex, '— drop "before resize" height', beforeResize.itemHeights[firstDroppedBeforeResizeItemIndex]);
|
|
164
|
+
} else {
|
|
165
|
+
log('For item indexes from', firstDroppedBeforeResizeItemIndex, 'to', lastDroppedBeforeResizeItemIndex, '— drop "before resize" heights', beforeResize.itemHeights.slice(firstDroppedBeforeResizeItemIndex));
|
|
166
|
+
}
|
|
167
|
+
} // Immediately update `beforeResize.itemHeights`
|
|
168
|
+
// so that the component isn't left in an inconsistent state
|
|
169
|
+
// before a `this.setState()` call below is applied.
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
beforeResize.itemHeights.splice(firstShownItemIndex, beforeResize.itemHeights.length - firstShownItemIndex); // Return the "scroll by" amount that would correct the scroll position.
|
|
173
|
+
// Also return a state update.
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
scrollBy: newlyShownItemRowsHeight - cleanedUpBeforeResizeItemRowsHeight,
|
|
177
|
+
beforeResize: firstShownItemIndex === 0 ? undefined : _objectSpread({}, beforeResize)
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} // Snapshots "before resize" values in order to preserve the currently
|
|
182
|
+
// shown items' vertical position on screen so that there's no "content jumping".
|
|
183
|
+
//
|
|
184
|
+
// `newFirstShownItemIndex` is `> 0`.
|
|
185
|
+
//
|
|
186
|
+
|
|
187
|
+
}, {
|
|
188
|
+
key: "snapshotBeforeResizeItemHeights",
|
|
189
|
+
value: function snapshotBeforeResizeItemHeights(_ref2) {
|
|
190
|
+
var firstShownItemIndex = _ref2.firstShownItemIndex,
|
|
191
|
+
newFirstShownItemIndex = _ref2.newFirstShownItemIndex,
|
|
192
|
+
newColumnsCount = _ref2.newColumnsCount;
|
|
193
|
+
var columnsCount = this.getColumnsCount();
|
|
194
|
+
var verticalSpacing = this.getVerticalSpacing();
|
|
195
|
+
this._includesBeforeResizeInState = true;
|
|
196
|
+
|
|
197
|
+
var _this$getState2 = this.getState(),
|
|
198
|
+
prevBeforeResize = _this$getState2.beforeResize,
|
|
199
|
+
itemHeights = _this$getState2.itemHeights;
|
|
200
|
+
|
|
201
|
+
var prevBeforeResizeItemsCount = prevBeforeResize ? prevBeforeResize.itemHeights.length : 0; // If there already are "before resize" values in `state`
|
|
202
|
+
// then it means that those should be merged with the new ones.
|
|
203
|
+
//
|
|
204
|
+
// `beforeResize.itemHeights` could be empty in an edge case
|
|
205
|
+
// when there's a pending state update that sets `beforeResize`
|
|
206
|
+
// to `undefined`, and in that case empty `beforeResize.itemHeights`
|
|
207
|
+
// signals about that type of a situation.
|
|
208
|
+
//
|
|
209
|
+
|
|
210
|
+
if (prevBeforeResizeItemsCount > 0) {
|
|
211
|
+
// Because the "previous" before resize values might have been captured
|
|
212
|
+
// for a window width corresponding to a layout with a different columns count
|
|
213
|
+
// and different vertical spacing, re-calculate those item heights as if
|
|
214
|
+
// they corresponded to the current columns count and current vertical spacing,
|
|
215
|
+
// since "previous" and "new" before resize item heights are gonna be merged.
|
|
216
|
+
if (prevBeforeResize.columnsCount !== columnsCount || prevBeforeResize.verticalSpacing !== verticalSpacing) {
|
|
217
|
+
var prevBeforeResizeBeforeItemsHeight = 0;
|
|
218
|
+
var prevBeforeResizeItemRowsCount = Math.ceil(prevBeforeResizeItemsCount / prevBeforeResize.columnsCount);
|
|
219
|
+
var rowIndex = 0;
|
|
220
|
+
|
|
221
|
+
while (rowIndex < prevBeforeResizeItemRowsCount) {
|
|
222
|
+
// Since all "before resize" item heights are equal within a row,
|
|
223
|
+
// the height of the first "before resize" item in a row is that row's height.
|
|
224
|
+
var rowHeight = prevBeforeResize.itemHeights[rowIndex * prevBeforeResize.columnsCount];
|
|
225
|
+
prevBeforeResizeBeforeItemsHeight += rowHeight;
|
|
226
|
+
prevBeforeResizeBeforeItemsHeight += prevBeforeResize.verticalSpacing;
|
|
227
|
+
rowIndex++;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
var newBeforeResizeAdditionalBeforeItemsHeight = 0;
|
|
231
|
+
var i = firstShownItemIndex;
|
|
232
|
+
|
|
233
|
+
while (i < newFirstShownItemIndex) {
|
|
234
|
+
var _rowHeight2 = 0;
|
|
235
|
+
var k = 0;
|
|
236
|
+
|
|
237
|
+
while (k < columnsCount && i < newFirstShownItemIndex) {
|
|
238
|
+
_rowHeight2 = Math.max(_rowHeight2, itemHeights[i]);
|
|
239
|
+
k++;
|
|
240
|
+
i++;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
newBeforeResizeAdditionalBeforeItemsHeight += _rowHeight2;
|
|
244
|
+
newBeforeResizeAdditionalBeforeItemsHeight += verticalSpacing;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
var newBeforeResizeBeforeItemsHeight = prevBeforeResizeBeforeItemsHeight + newBeforeResizeAdditionalBeforeItemsHeight;
|
|
248
|
+
var newBeforeResizeBeforeItemRowsCount = Math.ceil(newFirstShownItemIndex / columnsCount);
|
|
249
|
+
return new Array(newFirstShownItemIndex).fill( // Re-calculate "before resize" item heights so that "previous" and "new" ones
|
|
250
|
+
// correspond to the same (new) columns count.
|
|
251
|
+
// Also don't occasionally set item heights to `< 0`.
|
|
252
|
+
Math.max(0, newBeforeResizeBeforeItemsHeight / newBeforeResizeBeforeItemRowsCount - verticalSpacing));
|
|
253
|
+
} else {
|
|
254
|
+
// Add new item heights to the previously snapshotted ones.
|
|
255
|
+
return prevBeforeResize.itemHeights.concat(equalizeItemHeights(itemHeights, newFirstShownItemIndex, columnsCount).slice(prevBeforeResize.itemHeights.length));
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
return equalizeItemHeights(itemHeights, newFirstShownItemIndex, columnsCount);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}, {
|
|
262
|
+
key: "shouldIncludeBeforeResizeValuesInState",
|
|
263
|
+
value: function shouldIncludeBeforeResizeValuesInState() {
|
|
264
|
+
return this._includesBeforeResizeInState;
|
|
265
|
+
}
|
|
266
|
+
}]);
|
|
267
|
+
|
|
268
|
+
return BeforeResize;
|
|
269
|
+
}(); // Equalizes all item heights within a given row, for each row.
|
|
270
|
+
//
|
|
271
|
+
// The reason is that `beforeResize.itemHeights` is not necessarily divisible by
|
|
272
|
+
// `beforeResize.columnsCount`, which would result in varying last row height
|
|
273
|
+
// as items get removed from `beforeResize.itemHeights` as the user scrolls up.
|
|
274
|
+
//
|
|
275
|
+
// By equalizing all item heights within a given row, for each row, such "jumping"
|
|
276
|
+
// last "before resize" row height is prevented when the user scrolls up.
|
|
277
|
+
//
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
export { BeforeResize as default };
|
|
281
|
+
|
|
282
|
+
function equalizeItemHeights(itemHeights, maxItemsCount, columnsCount) {
|
|
283
|
+
itemHeights = itemHeights.slice(0, Math.ceil(maxItemsCount / columnsCount) * columnsCount);
|
|
284
|
+
var rowIndex = 0;
|
|
285
|
+
|
|
286
|
+
while (rowIndex * columnsCount < maxItemsCount) {
|
|
287
|
+
// Calculate row height.
|
|
288
|
+
var rowHeight = 0;
|
|
289
|
+
var k = 0;
|
|
290
|
+
|
|
291
|
+
while (k < columnsCount) {
|
|
292
|
+
rowHeight = Math.max(rowHeight, itemHeights[rowIndex * columnsCount + k]);
|
|
293
|
+
k++;
|
|
294
|
+
} // Equalize all item heights within the row.
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
k = 0;
|
|
298
|
+
|
|
299
|
+
while (k < columnsCount) {
|
|
300
|
+
itemHeights[rowIndex * columnsCount + k] = rowHeight;
|
|
301
|
+
k++;
|
|
302
|
+
} // Proceed with the next row.
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
rowIndex++;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return itemHeights.slice(0, maxItemsCount);
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=BeforeResize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../source/BeforeResize.js"],"names":["log","BeforeResize","getState","getVerticalSpacing","getColumnsCount","state","beforeResize","itemHeights","length","undefined","_includesBeforeResizeInState","prevState","firstShownItemIndex","lastShownItemIndex","newlyShownItemRowsHeight","prevFirstReMeasuredItemsRowIndex","Math","floor","newlyShownItemsToIndex","min","i","rowHeight","columnIndex","itemHeight","getAverageItemHeight","max","cleanedUpBeforeResizeItemRowsHeight","maxParticipatingBeforeResizeItemsCount","participatingBeforeResizeItemRowsCount","ceil","columnsCount","firstCleanedUpBeforeResizeItemsRowIndex","k","verticalSpacing","firstDroppedBeforeResizeItemIndex","lastDroppedBeforeResizeItemIndex","slice","splice","scrollBy","newFirstShownItemIndex","newColumnsCount","prevBeforeResize","prevBeforeResizeItemsCount","prevBeforeResizeBeforeItemsHeight","prevBeforeResizeItemRowsCount","rowIndex","newBeforeResizeAdditionalBeforeItemsHeight","newBeforeResizeBeforeItemsHeight","newBeforeResizeBeforeItemRowsCount","Array","fill","concat","equalizeItemHeights","maxItemsCount"],"mappings":"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","sourcesContent":["import log from './utility/debug'\r\n\r\nexport default class BeforeResize {\r\n\tconstructor({\r\n\t\tgetState,\r\n\t\tgetVerticalSpacing,\r\n\t\tgetColumnsCount\r\n\t}) {\r\n\t\tthis.getState = getState\r\n\t\tthis.getVerticalSpacing = getVerticalSpacing\r\n\t\tthis.getColumnsCount = getColumnsCount\r\n\t}\r\n\r\n\t// Possibly clean up \"before resize\" property in state.\r\n\t// \"Before resize\" state property is cleaned up when all \"before resize\" item heights\r\n\t// have been re-measured in an asynchronous `this.setState({ beforeResize: undefined })` call.\r\n\t// If `VirtualScroller` state was snapshotted externally before that `this.setState()` call\r\n\t// has been applied, then \"before resize\" property might have not been cleaned up properly.\r\n\tonInitialState(state) {\r\n\t\tif (state) {\r\n\t\t\tif (state.beforeResize) {\r\n\t\t\t\tif (state.beforeResize.itemHeights.length === 0) {\r\n\t\t\t\t\tstate.beforeResize = undefined\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (state.beforeResize) {\r\n\t\t\t\tthis._includesBeforeResizeInState = true\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t// Cleans up \"before resize\" item heights and adjusts the scroll position accordingly.\r\n\t//\r\n\t// Hypothetically, it could also wait for the user to stop scrolling and only then\r\n\t// adjust the scroll position. The rationale is that if `window.scrollTo()` is called\r\n\t// while the user is scrolling, the user would occasionally experience \"lost\" mouse wheel\r\n\t// events when scrolling with a mouse wheel.\r\n\t//\r\n\t// Seems like Twitter's website waits for the user to stop scrolling before applying\r\n\t// the scroll position correction after a window resize. This library could do that too,\r\n\t// but that would require rewriting \"before items height\" top padding calculation\r\n\t// so that it doesn't re-calculate it on every re-render and instead does so incrementally,\r\n\t// and then, when the user stops, it re-calculates it from scratch removing the error\r\n\t// and adjusting the scroll position accordingly so that there's no \"jump of content\".\r\n\t//\r\n\t// But, seems like it works fine as it is and there's no need to rewrite anything.\r\n\t//\r\n\tcleanUpBeforeResizeItemHeights(prevState) {\r\n\t\tconst {\r\n\t\t\tfirstShownItemIndex,\r\n\t\t\tlastShownItemIndex,\r\n\t\t\titemHeights,\r\n\t\t\tbeforeResize\r\n\t\t} = this.getState()\r\n\r\n\t\t// If there're \"before resize\" properties in `state`\r\n\t\t// then it means that the corresponding items are waiting to be\r\n\t\t// re-measured after container resize. Since the resize,\r\n\t\t// some of those non-re-measured items might have just been measured,\r\n\t\t// so see if that's true, and if it is, remove those now-obsolete\r\n\t\t// \"before resize\" item heights and ajust the scroll position\r\n\t\t// so that there's no \"content jumping\".\r\n\r\n\t\tif (beforeResize) {\r\n\t\t\t// If the user has scrolled up to reveal a previously hidden item\r\n\t\t\t// that has not yet been re-measured after a previous resize.\r\n\t\t\tif (firstShownItemIndex < beforeResize.itemHeights.length) {\r\n\t\t\t\tlog('~ Clean up \"before resize\" item heights and correct scroll position ~')\r\n\r\n\t\t\t\t// Some of the \"before\" items have been un-hidden and re-measured.\r\n\t\t\t\t// Un-hiding those items would result in a \"jump of content\"\r\n\t\t\t\t// because \"before resize\" heights of those un-hidden items\r\n\t\t\t\t// could (and most likely will) be different from the current ones,\r\n\t\t\t\t// or because \"before resize\" columns count is different from\r\n\t\t\t\t// the current one.\r\n\t\t\t\t// To prevent a \"jump of content\", calculate the scroll position\r\n\t\t\t\t// difference and adjust the scroll position.\r\n\r\n\t\t\t\t// The height of the item rows that have transitioned\r\n\t\t\t\t// from hidden to shown.\r\n\t\t\t\tlet newlyShownItemRowsHeight = 0\r\n\r\n\t\t\t\t// Some of the `itemHeights` between the current `firstShownItemIndex` and\r\n\t\t\t\t// the previous `firstShownItemIndex` could stay `undefined` if the user\r\n\t\t\t\t// scrolled \"abruptly\": for example, by using a `window.scrollTo()` call.\r\n\t\t\t\t// In that case, the items below the visible ones won't be rendered and measured.\r\n\t\t\t\t// In such case, limit the items being iterated over to the current `lastShownItemIndex`\r\n\t\t\t\t// rather than the previous `firstShownItemIndex`.\r\n\t\t\t\tconst prevFirstReMeasuredItemsRowIndex = Math.floor(beforeResize.itemHeights.length / this.getColumnsCount())\r\n\t\t\t\tconst newlyShownItemsToIndex = Math.min(\r\n\t\t\t\t\tprevFirstReMeasuredItemsRowIndex * this.getColumnsCount() - 1,\r\n\t\t\t\t\tlastShownItemIndex\r\n\t\t\t\t)\r\n\r\n\t\t\t\tlet i = firstShownItemIndex\r\n\t\t\t\twhile (i <= newlyShownItemsToIndex) {\r\n\t\t\t\t\t// Calculate newly shown row height.\r\n\t\t\t\t\tlet rowHeight = 0\r\n\t\t\t\t\tlet columnIndex = 0\r\n\t\t\t\t\twhile (columnIndex < this.getColumnsCount() && i <= newlyShownItemsToIndex) {\r\n\t\t\t\t\t\tlet itemHeight = itemHeights[i]\r\n\t\t\t\t\t\tif (itemHeight === undefined) {\r\n\t\t\t\t\t\t\t// `itemHeight` can only be `undefined` when not `beforeResize`.\r\n\t\t\t\t\t\t\t// Use the current \"average item height\" as a substitute.\r\n\t\t\t\t\t\t\titemHeight = this.getAverageItemHeight()\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\trowHeight = Math.max(rowHeight, itemHeight)\r\n\t\t\t\t\t\ti++\r\n\t\t\t\t\t\tcolumnIndex++\r\n\t\t\t\t\t}\r\n\t\t\t\t\t// Append to the total \"newly shown item rows height\".\r\n\t\t\t\t\tnewlyShownItemRowsHeight += rowHeight\r\n\t\t\t\t\tnewlyShownItemRowsHeight += this.getVerticalSpacing()\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// The height of the \"before resize\" item rows\r\n\t\t\t\t// that will be \"cleaned up\" in this function call.\r\n\t\t\t\tlet cleanedUpBeforeResizeItemRowsHeight = 0\r\n\r\n\t\t\t\t// Some of the `beforeResize` item rows might have been skipped if the user\r\n\t\t\t\t// scrolled up \"abruptly\": for example, by using a `window.scrollTo()` call.\r\n\t\t\t\t// In that case, the \"before resize\" items below the bottom border of the screen\r\n\t\t\t\t// shouldn't be accounted for when calculating the scrollbar adjustment shift\r\n\t\t\t\t// because items after `lastShownItemIndex` aren't participating in the calculation\r\n\t\t\t\t// of `newlyShownItemRowsHeight`.\r\n\t\t\t\tconst maxParticipatingBeforeResizeItemsCount = Math.min(beforeResize.itemHeights.length, lastShownItemIndex + 1)\r\n\t\t\t\tconst participatingBeforeResizeItemRowsCount = Math.ceil(maxParticipatingBeforeResizeItemsCount / beforeResize.columnsCount)\r\n\r\n\t\t\t\tconst firstCleanedUpBeforeResizeItemsRowIndex = firstShownItemIndex === 0\r\n\t\t\t\t\t? 0\r\n\t\t\t\t\t: Math.floor((firstShownItemIndex - 1) / beforeResize.columnsCount) + 1\r\n\r\n\t\t\t\tlet k = firstCleanedUpBeforeResizeItemsRowIndex\r\n\t\t\t\twhile (k < participatingBeforeResizeItemRowsCount) {\r\n\t\t\t\t\tconst rowHeight = beforeResize.itemHeights[k * beforeResize.columnsCount]\r\n\t\t\t\t\tcleanedUpBeforeResizeItemRowsHeight += rowHeight\r\n\t\t\t\t\tcleanedUpBeforeResizeItemRowsHeight += beforeResize.verticalSpacing\r\n\t\t\t\t\tk++\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Schedule an asynchronous `this.setState()` call that will update\r\n\t\t\t\t// `beforeResize` property of `state`. Ideally, it should be updated\r\n\t\t\t\t// immediately, but since `this.setState()` calls are asynchronous,\r\n\t\t\t\t// the code updates just the underlying `beforeResize.itemHeights`\r\n\t\t\t\t// array immediately instead, which is still a hack but still a lesser one.\r\n\t\t\t\tif (firstShownItemIndex === 0) {\r\n\t\t\t\t\tlog('Drop all \"before resize\" item heights')\r\n\t\t\t\t} else {\r\n\t\t\t\t\tconst firstDroppedBeforeResizeItemIndex = firstShownItemIndex\r\n\t\t\t\t\tconst lastDroppedBeforeResizeItemIndex = beforeResize.itemHeights.length - 1\r\n\t\t\t\t\tif (firstDroppedBeforeResizeItemIndex === lastDroppedBeforeResizeItemIndex) {\r\n\t\t\t\t\t\tlog('For item index', firstDroppedBeforeResizeItemIndex, '— drop \"before resize\" height', beforeResize.itemHeights[firstDroppedBeforeResizeItemIndex], )\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tlog('For item indexes from', firstDroppedBeforeResizeItemIndex, 'to', lastDroppedBeforeResizeItemIndex, '— drop \"before resize\" heights', beforeResize.itemHeights.slice(firstDroppedBeforeResizeItemIndex))\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Immediately update `beforeResize.itemHeights`\r\n\t\t\t\t// so that the component isn't left in an inconsistent state\r\n\t\t\t\t// before a `this.setState()` call below is applied.\r\n\t\t\t\tbeforeResize.itemHeights.splice(\r\n\t\t\t\t\tfirstShownItemIndex,\r\n\t\t\t\t\tbeforeResize.itemHeights.length - firstShownItemIndex\r\n\t\t\t\t)\r\n\r\n\t\t\t\t// Return the \"scroll by\" amount that would correct the scroll position.\r\n\t\t\t\t// Also return a state update.\r\n\t\t\t\treturn {\r\n\t\t\t\t\tscrollBy: newlyShownItemRowsHeight - cleanedUpBeforeResizeItemRowsHeight,\r\n\t\t\t\t\tbeforeResize: firstShownItemIndex === 0 ? undefined : {\r\n\t\t\t\t\t\t// Simply change the \"reference\" to `beforeResize` while leaving\r\n\t\t\t\t\t\t// its contents unchanged. That simply indicates that it has been updated:\r\n\t\t\t\t\t\t// `beforeResize.itemHeights` array length has been changed \"directly\".\r\n\t\t\t\t\t\t...beforeResize\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t// Snapshots \"before resize\" values in order to preserve the currently\r\n\t// shown items' vertical position on screen so that there's no \"content jumping\".\r\n\t//\r\n\t// `newFirstShownItemIndex` is `> 0`.\r\n\t//\r\n\tsnapshotBeforeResizeItemHeights({\r\n\t\tfirstShownItemIndex,\r\n\t\tnewFirstShownItemIndex,\r\n\t\tnewColumnsCount\r\n\t}) {\r\n\t\tconst columnsCount = this.getColumnsCount()\r\n\t\tconst verticalSpacing = this.getVerticalSpacing()\r\n\r\n\t\tthis._includesBeforeResizeInState = true\r\n\r\n\t\tconst {\r\n\t\t\tbeforeResize: prevBeforeResize,\r\n\t\t\titemHeights\r\n\t\t} = this.getState()\r\n\r\n\t\tconst prevBeforeResizeItemsCount = prevBeforeResize\r\n\t\t\t? prevBeforeResize.itemHeights.length\r\n\t\t\t: 0\r\n\r\n\t\t// If there already are \"before resize\" values in `state`\r\n\t\t// then it means that those should be merged with the new ones.\r\n\t\t//\r\n\t\t// `beforeResize.itemHeights` could be empty in an edge case\r\n\t\t// when there's a pending state update that sets `beforeResize`\r\n\t\t// to `undefined`, and in that case empty `beforeResize.itemHeights`\r\n\t\t// signals about that type of a situation.\r\n\t\t//\r\n\t\tif (prevBeforeResizeItemsCount > 0) {\r\n\t\t\t// Because the \"previous\" before resize values might have been captured\r\n\t\t\t// for a window width corresponding to a layout with a different columns count\r\n\t\t\t// and different vertical spacing, re-calculate those item heights as if\r\n\t\t\t// they corresponded to the current columns count and current vertical spacing,\r\n\t\t\t// since \"previous\" and \"new\" before resize item heights are gonna be merged.\r\n\t\t\tif (\r\n\t\t\t\tprevBeforeResize.columnsCount !== columnsCount ||\r\n\t\t\t\tprevBeforeResize.verticalSpacing !== verticalSpacing\r\n\t\t\t) {\r\n\t\t\t\tlet prevBeforeResizeBeforeItemsHeight = 0\r\n\r\n\t\t\t\tconst prevBeforeResizeItemRowsCount = Math.ceil(prevBeforeResizeItemsCount / prevBeforeResize.columnsCount)\r\n\t\t\t\tlet rowIndex = 0\r\n\t\t\t\twhile (rowIndex < prevBeforeResizeItemRowsCount) {\r\n\t\t\t\t\t// Since all \"before resize\" item heights are equal within a row,\r\n\t\t\t\t\t// the height of the first \"before resize\" item in a row is that row's height.\r\n\t\t\t\t\tconst rowHeight = prevBeforeResize.itemHeights[rowIndex * prevBeforeResize.columnsCount]\r\n\t\t\t\t\tprevBeforeResizeBeforeItemsHeight += rowHeight\r\n\t\t\t\t\tprevBeforeResizeBeforeItemsHeight += prevBeforeResize.verticalSpacing\r\n\t\t\t\t\trowIndex++\r\n\t\t\t\t}\r\n\r\n\t\t\t\tlet newBeforeResizeAdditionalBeforeItemsHeight = 0\r\n\t\t\t\tlet i = firstShownItemIndex\r\n\t\t\t\twhile (i < newFirstShownItemIndex) {\r\n\t\t\t\t\tlet rowHeight = 0\r\n\t\t\t\t\tlet k = 0\r\n\t\t\t\t\twhile (k < columnsCount && i < newFirstShownItemIndex) {\r\n\t\t\t\t\t\trowHeight = Math.max(rowHeight, itemHeights[i])\r\n\t\t\t\t\t\tk++\r\n\t\t\t\t\t\ti++\r\n\t\t\t\t\t}\r\n\t\t\t\t\tnewBeforeResizeAdditionalBeforeItemsHeight += rowHeight\r\n\t\t\t\t\tnewBeforeResizeAdditionalBeforeItemsHeight += verticalSpacing\r\n\t\t\t\t}\r\n\r\n\t\t\t\tconst newBeforeResizeBeforeItemsHeight = prevBeforeResizeBeforeItemsHeight + newBeforeResizeAdditionalBeforeItemsHeight\r\n\t\t\t\tconst newBeforeResizeBeforeItemRowsCount = Math.ceil(newFirstShownItemIndex / columnsCount)\r\n\r\n\t\t\t\treturn new Array(newFirstShownItemIndex).fill(\r\n\t\t\t\t\t// Re-calculate \"before resize\" item heights so that \"previous\" and \"new\" ones\r\n\t\t\t\t\t// correspond to the same (new) columns count.\r\n\t\t\t\t\t// Also don't occasionally set item heights to `< 0`.\r\n\t\t\t\t\tMath.max(0, newBeforeResizeBeforeItemsHeight / newBeforeResizeBeforeItemRowsCount - verticalSpacing)\r\n\t\t\t\t)\r\n\t\t\t} else {\r\n\t\t\t\t// Add new item heights to the previously snapshotted ones.\r\n\t\t\t\treturn prevBeforeResize.itemHeights.concat(\r\n\t\t\t\t\tequalizeItemHeights(\r\n\t\t\t\t\t\titemHeights,\r\n\t\t\t\t\t\tnewFirstShownItemIndex,\r\n\t\t\t\t\t\tcolumnsCount\r\n\t\t\t\t\t).slice(prevBeforeResize.itemHeights.length)\r\n\t\t\t\t)\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\treturn equalizeItemHeights(\r\n\t\t\t\titemHeights,\r\n\t\t\t\tnewFirstShownItemIndex,\r\n\t\t\t\tcolumnsCount\r\n\t\t\t)\r\n\t\t}\r\n\t}\r\n\r\n\tshouldIncludeBeforeResizeValuesInState() {\r\n\t\treturn this._includesBeforeResizeInState\r\n\t}\r\n}\r\n\r\n// Equalizes all item heights within a given row, for each row.\r\n//\r\n// The reason is that `beforeResize.itemHeights` is not necessarily divisible by\r\n// `beforeResize.columnsCount`, which would result in varying last row height\r\n// as items get removed from `beforeResize.itemHeights` as the user scrolls up.\r\n//\r\n// By equalizing all item heights within a given row, for each row, such \"jumping\"\r\n// last \"before resize\" row height is prevented when the user scrolls up.\r\n//\r\nfunction equalizeItemHeights(itemHeights, maxItemsCount, columnsCount) {\r\n\titemHeights = itemHeights.slice(0, Math.ceil(maxItemsCount / columnsCount) * columnsCount)\r\n\r\n\tlet rowIndex = 0\r\n\twhile (rowIndex * columnsCount < maxItemsCount) {\r\n\t\t// Calculate row height.\r\n\t\tlet rowHeight = 0\r\n\t\tlet k = 0\r\n\t\twhile (k < columnsCount) {\r\n\t\t\trowHeight = Math.max(rowHeight, itemHeights[rowIndex * columnsCount + k])\r\n\t\t\tk++\r\n\t\t}\r\n\r\n\t\t// Equalize all item heights within the row.\r\n\t\tk = 0\r\n\t\twhile (k < columnsCount) {\r\n\t\t\titemHeights[rowIndex * columnsCount + k] = rowHeight\r\n\t\t\tk++\r\n\t\t}\r\n\r\n\t\t// Proceed with the next row.\r\n\t\trowIndex++\r\n\t}\r\n\r\n\treturn itemHeights.slice(0, maxItemsCount)\r\n}"],"file":"BeforeResize.js"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import ItemsContainer from './ItemsContainer';
|
|
2
|
+
import ScrollableContainer, { ScrollableWindowContainer } from './ScrollableContainer';
|
|
3
|
+
import ListTopOffsetWatcher from './ListTopOffsetWatcher';
|
|
4
|
+
export default {
|
|
5
|
+
createItemsContainer: function createItemsContainer(getItemsContainerElement) {
|
|
6
|
+
return new ItemsContainer(getItemsContainerElement);
|
|
7
|
+
},
|
|
8
|
+
// Creates a `scrollableContainer`.
|
|
9
|
+
// On client side, `scrollableContainer` is always created.
|
|
10
|
+
// On server side, `scrollableContainer` is not created (and not used).
|
|
11
|
+
createScrollableContainer: function createScrollableContainer(scrollableContainer, getItemsContainerElement) {
|
|
12
|
+
if (scrollableContainer) {
|
|
13
|
+
return new ScrollableContainer(scrollableContainer, getItemsContainerElement);
|
|
14
|
+
} else if (typeof window !== 'undefined') {
|
|
15
|
+
return new ScrollableWindowContainer(getItemsContainerElement);
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
watchListTopOffset: function watchListTopOffset(_ref) {
|
|
19
|
+
var getListTopOffset = _ref.getListTopOffset,
|
|
20
|
+
onListTopOffsetChange = _ref.onListTopOffsetChange;
|
|
21
|
+
return new ListTopOffsetWatcher({
|
|
22
|
+
getListTopOffset: getListTopOffset,
|
|
23
|
+
onListTopOffsetChange: onListTopOffsetChange
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=Engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../source/DOM/Engine.js"],"names":["ItemsContainer","ScrollableContainer","ScrollableWindowContainer","ListTopOffsetWatcher","createItemsContainer","getItemsContainerElement","createScrollableContainer","scrollableContainer","window","watchListTopOffset","getListTopOffset","onListTopOffsetChange"],"mappings":"AAAA,OAAOA,cAAP,MAA2B,kBAA3B;AAEA,OAAOC,mBAAP,IACCC,yBADD,QAEO,uBAFP;AAIA,OAAOC,oBAAP,MAAiC,wBAAjC;AAEA,eAAe;AACdC,EAAAA,oBADc,gCACOC,wBADP,EACiC;AAC9C,WAAO,IAAIL,cAAJ,CAAmBK,wBAAnB,CAAP;AACA,GAHa;AAId;AACA;AACA;AACAC,EAAAA,yBAPc,qCAOYC,mBAPZ,EAOiCF,wBAPjC,EAO2D;AACxE,QAAIE,mBAAJ,EAAyB;AACxB,aAAO,IAAIN,mBAAJ,CAAwBM,mBAAxB,EAA6CF,wBAA7C,CAAP;AACA,KAFD,MAEO,IAAI,OAAOG,MAAP,KAAkB,WAAtB,EAAmC;AACzC,aAAO,IAAIN,yBAAJ,CAA8BG,wBAA9B,CAAP;AACA;AACD,GAba;AAcdI,EAAAA,kBAdc,oCAiBX;AAAA,QAFFC,gBAEE,QAFFA,gBAEE;AAAA,QADFC,qBACE,QADFA,qBACE;AACF,WAAO,IAAIR,oBAAJ,CAAyB;AAC/BO,MAAAA,gBAAgB,EAAhBA,gBAD+B;AAE/BC,MAAAA,qBAAqB,EAArBA;AAF+B,KAAzB,CAAP;AAIA;AAtBa,CAAf","sourcesContent":["import ItemsContainer from './ItemsContainer'\r\n\r\nimport ScrollableContainer, {\r\n\tScrollableWindowContainer\r\n} from './ScrollableContainer'\r\n\r\nimport ListTopOffsetWatcher from './ListTopOffsetWatcher'\r\n\r\nexport default {\r\n\tcreateItemsContainer(getItemsContainerElement) {\r\n\t\treturn new ItemsContainer(getItemsContainerElement)\r\n\t},\r\n\t// Creates a `scrollableContainer`.\r\n\t// On client side, `scrollableContainer` is always created.\r\n\t// On server side, `scrollableContainer` is not created (and not used).\r\n\tcreateScrollableContainer(scrollableContainer, getItemsContainerElement) {\r\n\t\tif (scrollableContainer) {\r\n\t\t\treturn new ScrollableContainer(scrollableContainer, getItemsContainerElement)\r\n\t\t} else if (typeof window !== 'undefined') {\r\n\t\t\treturn new ScrollableWindowContainer(getItemsContainerElement)\r\n\t\t}\r\n\t},\r\n\twatchListTopOffset({\r\n\t\tgetListTopOffset,\r\n\t\tonListTopOffsetChange\r\n\t}) {\r\n\t\treturn new ListTopOffsetWatcher({\r\n\t\t\tgetListTopOffset,\r\n\t\t\tonListTopOffsetChange\r\n\t\t})\r\n\t}\r\n}"],"file":"Engine.js"}
|