You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

826 lines
34 KiB

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var React = _interopRequireWildcard(require("react"));
var _classnames = _interopRequireDefault(require("classnames"));
var _raf = _interopRequireDefault(require("raf"));
var _Filler = _interopRequireDefault(require("./Filler"));
var _itemUtil = require("./utils/itemUtil");
var _algorithmUtil = require("./utils/algorithmUtil");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
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; }
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; }
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; }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
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); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
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); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
var ScrollStyle = {
overflowY: 'auto',
overflowAnchor: 'none'
};
var ITEM_SCALE_RATE = 1;
/**
* We use class component here since typescript can not support generic in function component
*
* Virtual list display logic:
* 1. scroll / initialize trigger measure
* 2. Get location item of current `scrollTop`
* 3. [Render] Render visible items
* 4. Get all the visible items height
* 5. [Render] Update top item `margin-top` to fit the position
*
* Algorithm:
* We split scroll bar into equal slice. An item with whatever height occupy the same range slice.
* When `scrollTop` change,
* it will calculate the item percentage position and move item to the position.
* Then calculate other item position base on the located item.
*
* Concept:
*
* # located item
* The base position item which other items position calculate base on.
*/
var List =
/** @class */
function () {
var List = /*#__PURE__*/function (_React$Component) {
_inherits(List, _React$Component);
var _super = _createSuper(List);
function List(props) {
var _this;
_classCallCheck(this, List);
_this = _super.call(this, props);
_this.listRef = React.createRef();
_this.itemElements = {};
_this.itemElementHeights = {};
/**
* Lock scroll process with `onScroll` event.
* This is used for `data` length change and `scrollTop` restore
*/
_this.lockScroll = false;
/**
* Phase 2: Trigger render since we should re-calculate current position.
*/
_this.onScroll = function (event) {
var _this$props = _this.props,
data = _this$props.data,
height = _this$props.height,
itemHeight = _this$props.itemHeight,
disabled = _this$props.disabled;
var _this$listRef$current = _this.listRef.current,
originScrollTop = _this$listRef$current.scrollTop,
clientHeight = _this$listRef$current.clientHeight,
scrollHeight = _this$listRef$current.scrollHeight;
var scrollTop = (0, _itemUtil.alignScrollTop)(originScrollTop, scrollHeight - clientHeight); // Skip if `scrollTop` not change to avoid shake
if (scrollTop === _this.state.scrollTop || _this.lockScroll || disabled) {
return;
}
var scrollPtg = (0, _itemUtil.getElementScrollPercentage)(_this.listRef.current);
var visibleCount = Math.ceil(height / itemHeight);
var _getRangeIndex = (0, _itemUtil.getRangeIndex)(scrollPtg, data.length, visibleCount),
itemIndex = _getRangeIndex.itemIndex,
itemOffsetPtg = _getRangeIndex.itemOffsetPtg,
startIndex = _getRangeIndex.startIndex,
endIndex = _getRangeIndex.endIndex;
_this.setState({
status: 'MEASURE_START',
scrollTop: scrollTop,
itemIndex: itemIndex,
itemOffsetPtg: itemOffsetPtg,
startIndex: startIndex,
endIndex: endIndex
});
_this.triggerOnScroll(event);
};
_this.onRawScroll = function (event) {
var scrollTop = _this.listRef.current.scrollTop;
_this.setState({
scrollTop: scrollTop
});
_this.triggerOnScroll(event);
};
_this.triggerOnScroll = function (event) {
var onScroll = _this.props.onScroll;
if (onScroll && event) {
onScroll(event);
}
};
_this.getIndexKey = function (index, props) {
var mergedProps = props || _this.props;
var _mergedProps$data = mergedProps.data,
data = _mergedProps$data === void 0 ? [] : _mergedProps$data; // Return ghost key as latest index item
if (index === data.length) {
return _itemUtil.GHOST_ITEM_KEY;
}
var item = data[index];
/* istanbul ignore next */
if (item === undefined) {
console.error('Not find index item. Please report this since it is a bug.');
return null;
}
return _this.getItemKey(item, mergedProps);
};
_this.getItemKey = function (item, props) {
var _ref = props || _this.props,
itemKey = _ref.itemKey;
return typeof itemKey === 'function' ? itemKey(item) : item[itemKey];
};
/**
* Collect current rendered dom element item heights
*/
_this.collectItemHeights = function (range) {
var _ref2 = range || _this.state,
startIndex = _ref2.startIndex,
endIndex = _ref2.endIndex;
var data = _this.props.data; // Record here since measure item height will get warning in `render`
for (var index = startIndex; index <= endIndex; index += 1) {
var item = data[index]; // Only collect exist item height
if (item) {
var eleKey = _this.getItemKey(item);
_this.itemElementHeights[eleKey] = (0, _itemUtil.getNodeHeight)(_this.itemElements[eleKey]);
}
}
};
_this.scrollTo = function (arg0) {
_raf.default.cancel(_this.rafId);
_this.rafId = (0, _raf.default)(function () {
// Number top
if (_typeof(arg0) === 'object') {
var isVirtual = _this.state.isVirtual;
var _this$props2 = _this.props,
height = _this$props2.height,
itemHeight = _this$props2.itemHeight,
data = _this$props2.data;
var _arg0$align = arg0.align,
align = _arg0$align === void 0 ? 'auto' : _arg0$align;
var index = 0;
if ('index' in arg0) {
index = arg0.index;
} else if ('key' in arg0) {
var key = arg0.key;
index = data.findIndex(function (item) {
return _this.getItemKey(item) === key;
});
}
var visibleCount = Math.ceil(height / itemHeight);
var item = data[index];
if (item) {
var clientHeight = _this.listRef.current.clientHeight;
if (isVirtual) {
// Calculate related data
var _this$state = _this.state,
itemIndex = _this$state.itemIndex,
itemOffsetPtg = _this$state.itemOffsetPtg;
var scrollTop = _this.listRef.current.scrollTop;
var scrollPtg = (0, _itemUtil.getElementScrollPercentage)(_this.listRef.current);
var relativeLocatedItemTop = (0, _itemUtil.getItemRelativeTop)({
itemIndex: itemIndex,
itemOffsetPtg: itemOffsetPtg,
itemElementHeights: _this.itemElementHeights,
scrollPtg: scrollPtg,
clientHeight: clientHeight,
getItemKey: _this.getIndexKey
}); // We will force render related items to collect height for re-location
_this.setState({
startIndex: Math.max(0, index - visibleCount),
endIndex: Math.min(data.length - 1, index + visibleCount)
}, function () {
_this.collectItemHeights(); // Calculate related top
var relativeTop;
var mergedAlgin = align;
if (align === 'auto') {
var shouldChange = true; // Check if exist in the visible range
if (Math.abs(itemIndex - index) < visibleCount) {
var itemTop = relativeLocatedItemTop;
if (index < itemIndex) {
for (var i = index; i < itemIndex; i += 1) {
var eleKey = _this.getIndexKey(i);
itemTop -= _this.itemElementHeights[eleKey] || 0;
}
} else {
for (var _i = itemIndex; _i <= index; _i += 1) {
var _eleKey = _this.getIndexKey(_i);
itemTop += _this.itemElementHeights[_eleKey] || 0;
}
}
shouldChange = itemTop <= 0 || itemTop >= clientHeight;
}
if (shouldChange) {
// Out of range will fall back to position align
mergedAlgin = index < itemIndex ? 'top' : 'bottom';
} else {
var _getRangeIndex2 = (0, _itemUtil.getRangeIndex)(scrollPtg, data.length, visibleCount),
nextIndex = _getRangeIndex2.itemIndex,
newOffsetPtg = _getRangeIndex2.itemOffsetPtg,
startIndex = _getRangeIndex2.startIndex,
endIndex = _getRangeIndex2.endIndex;
_this.setState({
scrollTop: scrollTop,
itemIndex: nextIndex,
itemOffsetPtg: newOffsetPtg,
startIndex: startIndex,
endIndex: endIndex
});
return;
}
} // Align with position should make scroll happen
if (mergedAlgin === 'top') {
relativeTop = 0;
} else if (mergedAlgin === 'bottom') {
var _eleKey2 = _this.getItemKey(item);
relativeTop = clientHeight - _this.itemElementHeights[_eleKey2] || 0;
}
_this.internalScrollTo({
itemIndex: index,
relativeTop: relativeTop
});
});
} else {
// Raw list without virtual scroll set position directly
_this.collectItemHeights({
startIndex: 0,
endIndex: data.length - 1
});
var mergedAlgin = align; // Collection index item position
var indexItemHeight = _this.itemElementHeights[_this.getIndexKey(index)];
var itemTop = 0;
for (var i = 0; i < index; i += 1) {
var eleKey = _this.getIndexKey(i);
itemTop += _this.itemElementHeights[eleKey] || 0;
}
var itemBottom = itemTop + indexItemHeight;
if (mergedAlgin === 'auto') {
if (itemTop < _this.listRef.current.scrollTop) {
mergedAlgin = 'top';
} else if (itemBottom > _this.listRef.current.scrollTop + clientHeight) {
mergedAlgin = 'bottom';
}
}
if (mergedAlgin === 'top') {
_this.listRef.current.scrollTop = itemTop;
} else if (mergedAlgin === 'bottom') {
_this.listRef.current.scrollTop = itemTop - (clientHeight - indexItemHeight);
}
}
}
} else {
_this.listRef.current.scrollTop = arg0;
}
});
};
/**
* Phase 4: Render item and get all the visible items height
*/
_this.renderChildren = function (list, startIndex, renderFunc) {
var status = _this.state.status; // We should measure rendered item height
return list.map(function (item, index) {
var eleIndex = startIndex + index;
var node = renderFunc(item, eleIndex, {
style: status === 'MEASURE_START' ? {
visibility: 'hidden'
} : {}
});
var eleKey = _this.getIndexKey(eleIndex); // Pass `key` and `ref` for internal measure
return React.cloneElement(node, {
key: eleKey,
ref: function ref(ele) {
_this.itemElements[eleKey] = ele;
}
});
});
};
_this.cachedProps = props;
_this.state = {
status: 'NONE',
scrollTop: null,
itemIndex: 0,
itemOffsetPtg: 0,
startIndex: 0,
endIndex: 0,
startItemTop: 0,
isVirtual: (0, _itemUtil.requireVirtual)(props.height, props.itemHeight, props.data.length, props.virtual),
itemCount: props.data.length
};
return _this;
}
_createClass(List, [{
key: "componentDidMount",
/**
* Phase 1: Initial should sync with default scroll top
*/
value: function componentDidMount() {
if (this.listRef.current) {
this.listRef.current.scrollTop = 0;
this.onScroll(null);
}
}
/**
* Phase 4: Record used item height
* Phase 5: Trigger re-render to use correct position
*/
}, {
key: "componentDidUpdate",
value: function componentDidUpdate() {
var _this2 = this;
var status = this.state.status;
var _this$props3 = this.props,
data = _this$props3.data,
height = _this$props3.height,
itemHeight = _this$props3.itemHeight,
disabled = _this$props3.disabled,
onSkipRender = _this$props3.onSkipRender,
virtual = _this$props3.virtual;
var prevData = this.cachedProps.data || [];
var changedItemIndex = null;
if (prevData.length !== data.length) {
var diff = (0, _algorithmUtil.findListDiffIndex)(prevData, data, this.getItemKey);
changedItemIndex = diff ? diff.index : null;
}
if (disabled) {
// Should trigger `onSkipRender` to tell that diff component is not render in the list
if (data.length > prevData.length) {
var _this$state2 = this.state,
startIndex = _this$state2.startIndex,
endIndex = _this$state2.endIndex;
if (onSkipRender && (changedItemIndex === null || changedItemIndex < startIndex || endIndex < changedItemIndex)) {
onSkipRender();
}
}
return;
}
var isVirtual = (0, _itemUtil.requireVirtual)(height, itemHeight, data.length, virtual);
var nextStatus = status;
if (this.state.isVirtual !== isVirtual) {
nextStatus = isVirtual ? 'SWITCH_TO_VIRTUAL' : 'SWITCH_TO_RAW';
this.setState({
isVirtual: isVirtual,
status: nextStatus
});
/**
* We will wait a tick to let list turn to virtual list.
* And then use virtual list sync logic to adjust the scroll.
*/
if (nextStatus === 'SWITCH_TO_VIRTUAL') {
return;
}
}
if (status === 'MEASURE_START') {
var _this$state3 = this.state,
_startIndex = _this$state3.startIndex,
itemIndex = _this$state3.itemIndex,
itemOffsetPtg = _this$state3.itemOffsetPtg;
var scrollTop = this.listRef.current.scrollTop; // Record here since measure item height will get warning in `render`
this.collectItemHeights(); // Calculate top visible item top offset
var locatedItemTop = (0, _itemUtil.getItemAbsoluteTop)({
itemIndex: itemIndex,
itemOffsetPtg: itemOffsetPtg,
itemElementHeights: this.itemElementHeights,
scrollTop: scrollTop,
scrollPtg: (0, _itemUtil.getElementScrollPercentage)(this.listRef.current),
clientHeight: this.listRef.current.clientHeight,
getItemKey: this.getIndexKey
});
var startItemTop = locatedItemTop;
for (var index = itemIndex - 1; index >= _startIndex; index -= 1) {
startItemTop -= this.itemElementHeights[this.getIndexKey(index)] || 0;
}
this.setState({
status: 'MEASURE_DONE',
startItemTop: startItemTop
});
}
if (status === 'SWITCH_TO_RAW') {
/**
* After virtual list back to raw list,
* we update the `scrollTop` to real top instead of percentage top.
*/
var _this$state$cacheScro = this.state.cacheScroll,
_itemIndex = _this$state$cacheScro.itemIndex,
relativeTop = _this$state$cacheScro.relativeTop;
var rawTop = relativeTop;
for (var _index = 0; _index < _itemIndex; _index += 1) {
rawTop -= this.itemElementHeights[this.getIndexKey(_index)] || 0;
}
this.lockScroll = true;
this.listRef.current.scrollTop = -rawTop;
this.setState({
status: 'MEASURE_DONE',
itemIndex: 0
});
requestAnimationFrame(function () {
requestAnimationFrame(function () {
_this2.lockScroll = false;
});
});
} else if (prevData.length !== data.length && changedItemIndex !== null && height && virtual !== false) {
/**
* Re-calculate the item position since `data` length changed.
* [IMPORTANT] We use relative position calculate here.
*/
var originItemIndex = this.state.itemIndex;
var _this$state4 = this.state,
originItemOffsetPtg = _this$state4.itemOffsetPtg,
originStartIndex = _this$state4.startIndex,
originEndIndex = _this$state4.endIndex,
originScrollTop = _this$state4.scrollTop; // 1. Refresh item heights
this.collectItemHeights(); // 1. Get origin located item top
var originLocatedItemRelativeTop;
if (this.state.status === 'SWITCH_TO_VIRTUAL') {
originItemIndex = 0;
originLocatedItemRelativeTop = -this.state.scrollTop;
} else {
originLocatedItemRelativeTop = (0, _itemUtil.getItemRelativeTop)({
itemIndex: originItemIndex,
itemOffsetPtg: originItemOffsetPtg,
itemElementHeights: this.itemElementHeights,
scrollPtg: (0, _itemUtil.getScrollPercentage)({
scrollTop: originScrollTop,
scrollHeight: prevData.length * itemHeight,
clientHeight: this.listRef.current.clientHeight
}),
clientHeight: this.listRef.current.clientHeight,
getItemKey: function getItemKey(index) {
return _this2.getIndexKey(index, _this2.cachedProps);
}
});
} // 2. Find the compare item
var originCompareItemIndex = changedItemIndex - 1; // Use next one since there are not more item before removed
if (originCompareItemIndex < 0) {
originCompareItemIndex = 0;
} // 3. Find the compare item top
var originCompareItemTop = (0, _itemUtil.getCompareItemRelativeTop)({
locatedItemRelativeTop: originLocatedItemRelativeTop,
locatedItemIndex: originItemIndex,
compareItemIndex: originCompareItemIndex,
startIndex: originStartIndex,
endIndex: originEndIndex,
getItemKey: function getItemKey(index) {
return _this2.getIndexKey(index, _this2.cachedProps);
},
itemElementHeights: this.itemElementHeights
});
if (nextStatus === 'SWITCH_TO_RAW') {
/**
* We will record current measure relative item top and apply in raw list after list turned
*/
this.setState({
cacheScroll: {
itemIndex: originCompareItemIndex,
relativeTop: originCompareItemTop
}
});
} else {
this.internalScrollTo({
itemIndex: originCompareItemIndex,
relativeTop: originCompareItemTop
});
}
} else if (nextStatus === 'SWITCH_TO_RAW') {
// This is only trigger when height changes that all items can show in raw
// Let's reset back to top
this.setState({
cacheScroll: {
itemIndex: 0,
relativeTop: 0
}
});
}
this.cachedProps = this.props;
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
_raf.default.cancel(this.rafId);
}
}, {
key: "internalScrollTo",
value: function internalScrollTo(relativeScroll) {
var _this3 = this;
var compareItemIndex = relativeScroll.itemIndex,
compareItemRelativeTop = relativeScroll.relativeTop;
var originScrollTop = this.state.scrollTop;
var _this$props4 = this.props,
data = _this$props4.data,
itemHeight = _this$props4.itemHeight,
height = _this$props4.height; // 1. Find the best match compare item top
var bestSimilarity = Number.MAX_VALUE;
var bestScrollTop = null;
var bestItemIndex = null;
var bestItemOffsetPtg = null;
var bestStartIndex = null;
var bestEndIndex = null;
var missSimilarity = 0;
var scrollHeight = data.length * itemHeight;
var clientHeight = this.listRef.current.clientHeight;
var maxScrollTop = scrollHeight - clientHeight;
for (var i = 0; i < maxScrollTop; i += 1) {
var scrollTop = (0, _algorithmUtil.getIndexByStartLoc)(0, maxScrollTop, originScrollTop, i);
var scrollPtg = (0, _itemUtil.getScrollPercentage)({
scrollTop: scrollTop,
scrollHeight: scrollHeight,
clientHeight: clientHeight
});
var visibleCount = Math.ceil(height / itemHeight);
var _getRangeIndex3 = (0, _itemUtil.getRangeIndex)(scrollPtg, data.length, visibleCount),
itemIndex = _getRangeIndex3.itemIndex,
itemOffsetPtg = _getRangeIndex3.itemOffsetPtg,
startIndex = _getRangeIndex3.startIndex,
endIndex = _getRangeIndex3.endIndex; // No need to check if compare item out of the index to save performance
if (startIndex <= compareItemIndex && compareItemIndex <= endIndex) {
// 1.1 Get measure located item relative top
var locatedItemRelativeTop = (0, _itemUtil.getItemRelativeTop)({
itemIndex: itemIndex,
itemOffsetPtg: itemOffsetPtg,
itemElementHeights: this.itemElementHeights,
scrollPtg: scrollPtg,
clientHeight: clientHeight,
getItemKey: this.getIndexKey
});
var compareItemTop = (0, _itemUtil.getCompareItemRelativeTop)({
locatedItemRelativeTop: locatedItemRelativeTop,
locatedItemIndex: itemIndex,
compareItemIndex: compareItemIndex,
startIndex: startIndex,
endIndex: endIndex,
getItemKey: this.getIndexKey,
itemElementHeights: this.itemElementHeights
}); // 1.2 Find best match compare item top
var similarity = Math.abs(compareItemTop - compareItemRelativeTop);
if (similarity < bestSimilarity) {
bestSimilarity = similarity;
bestScrollTop = scrollTop;
bestItemIndex = itemIndex;
bestItemOffsetPtg = itemOffsetPtg;
bestStartIndex = startIndex;
bestEndIndex = endIndex;
missSimilarity = 0;
} else {
missSimilarity += 1;
}
} // If keeping 10 times not match similarity,
// check more scrollTop is meaningless.
// Here boundary is set to 10.
if (missSimilarity > 10) {
break;
}
} // 2. Re-scroll if has best scroll match
if (bestScrollTop !== null) {
this.lockScroll = true;
this.listRef.current.scrollTop = bestScrollTop;
this.setState({
status: 'MEASURE_START',
scrollTop: bestScrollTop,
itemIndex: bestItemIndex,
itemOffsetPtg: bestItemOffsetPtg,
startIndex: bestStartIndex,
endIndex: bestEndIndex
});
requestAnimationFrame(function () {
requestAnimationFrame(function () {
_this3.lockScroll = false;
});
});
}
}
}, {
key: "render",
value: function render() {
var _this$state5 = this.state,
isVirtual = _this$state5.isVirtual,
itemCount = _this$state5.itemCount;
var _this$props5 = this.props,
prefixCls = _this$props5.prefixCls,
style = _this$props5.style,
className = _this$props5.className,
_this$props5$componen = _this$props5.component,
Component = _this$props5$componen === void 0 ? 'div' : _this$props5$componen,
height = _this$props5.height,
itemHeight = _this$props5.itemHeight,
_this$props5$fullHeig = _this$props5.fullHeight,
fullHeight = _this$props5$fullHeig === void 0 ? true : _this$props5$fullHeig,
data = _this$props5.data,
children = _this$props5.children,
itemKey = _this$props5.itemKey,
onSkipRender = _this$props5.onSkipRender,
disabled = _this$props5.disabled,
virtual = _this$props5.virtual,
restProps = _objectWithoutProperties(_this$props5, ["prefixCls", "style", "className", "component", "height", "itemHeight", "fullHeight", "data", "children", "itemKey", "onSkipRender", "disabled", "virtual"]);
var mergedClassName = (0, _classnames.default)(prefixCls, className); // Render pure list if not set height or height is enough for all items
if (!isVirtual) {
/**
* Virtual list switch is works on component updated.
* We should double check here if need cut the content.
*/
var shouldVirtual = (0, _itemUtil.requireVirtual)(height, itemHeight, data.length, virtual);
return React.createElement(Component, Object.assign({
style: height ? _objectSpread(_objectSpread({}, style), {}, _defineProperty({}, fullHeight ? 'height' : 'maxHeight', height), ScrollStyle) : style,
className: mergedClassName
}, restProps, {
onScroll: this.onRawScroll,
ref: this.listRef
}), React.createElement(_Filler.default, {
prefixCls: prefixCls,
height: height
}, this.renderChildren(shouldVirtual ? data.slice(0, Math.ceil(height / itemHeight)) : data, 0, children)));
} // Use virtual list
var mergedStyle = _objectSpread(_objectSpread({}, style), {}, {
height: height
}, ScrollStyle);
var _this$state6 = this.state,
status = _this$state6.status,
startIndex = _this$state6.startIndex,
endIndex = _this$state6.endIndex,
startItemTop = _this$state6.startItemTop;
var contentHeight = itemCount * itemHeight * ITEM_SCALE_RATE;
return React.createElement(Component, Object.assign({
style: mergedStyle,
className: mergedClassName
}, restProps, {
onScroll: this.onScroll,
ref: this.listRef
}), React.createElement(_Filler.default, {
prefixCls: prefixCls,
height: contentHeight,
offset: status === 'MEASURE_DONE' ? startItemTop : 0
}, this.renderChildren(data.slice(startIndex, endIndex + 1), startIndex, children)));
}
}], [{
key: "getDerivedStateFromProps",
value: function getDerivedStateFromProps(nextProps) {
if (!nextProps.disabled) {
return {
itemCount: nextProps.data.length
};
}
return null;
}
}]);
return List;
}(React.Component);
List.defaultProps = {
itemHeight: 15,
data: []
};
return List;
}();
var _default = List;
exports.default = _default;