rc-virtual-list
Advanced tools
Comparing version 1.1.2 to 1.1.3
@@ -30,3 +30,3 @@ 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; } | ||
}; | ||
innerStyle = _objectSpread({}, innerStyle, { | ||
innerStyle = _objectSpread(_objectSpread({}, innerStyle), {}, { | ||
transform: "translateY(".concat(offset, "px)"), | ||
@@ -33,0 +33,0 @@ position: 'absolute', |
import * as React from 'react'; | ||
import { Key } from './utils/itemUtil'; | ||
declare type ScrollAlign = 'top' | 'bottom' | 'auto'; | ||
declare type ScrollConfig = { | ||
index: number; | ||
align?: ScrollAlign; | ||
} | { | ||
key: Key; | ||
align?: ScrollAlign; | ||
}; | ||
export declare type RenderFunc<T> = (item: T, index: number, props: { | ||
@@ -125,3 +132,3 @@ style: React.CSSProperties; | ||
getIndexKey: (index: number, props?: Partial<ListProps<T>>) => string | number; | ||
getItemKey: (item: T, props?: Partial<ListProps<T>>) => string | number; | ||
getItemKey: (item: T, props?: Partial<ListProps<T>>) => Key; | ||
/** | ||
@@ -134,9 +141,3 @@ * Collect current rendered dom element item heights | ||
}) => void; | ||
scrollTo: (arg0: number | { | ||
index: number; | ||
align?: ScrollAlign; | ||
} | { | ||
key: string | number; | ||
align?: ScrollAlign; | ||
}) => void; | ||
scrollTo: (arg0: number | ScrollConfig) => void; | ||
internalScrollTo(relativeScroll: RelativeScroll): void; | ||
@@ -143,0 +144,0 @@ /** |
1187
es/List.js
@@ -19,2 +19,8 @@ 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 _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 () { 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); } | ||
@@ -24,8 +30,6 @@ | ||
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); } | ||
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); } | ||
import * as React from 'react'; | ||
@@ -64,726 +68,735 @@ import classNames from 'classnames'; | ||
var List = /*#__PURE__*/function (_React$Component) { | ||
_inherits(List, _React$Component); | ||
var List = | ||
/** @class */ | ||
function () { | ||
var List = /*#__PURE__*/function (_React$Component) { | ||
_inherits(List, _React$Component); | ||
function List(props) { | ||
var _this; | ||
var _super = _createSuper(List); | ||
_classCallCheck(this, List); | ||
function List(props) { | ||
var _this; | ||
_this = _possibleConstructorReturn(this, _getPrototypeOf(List).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 | ||
*/ | ||
_classCallCheck(this, List); | ||
_this.lockScroll = false; | ||
/** | ||
* Phase 2: Trigger render since we should re-calculate current position. | ||
*/ | ||
_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.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 = alignScrollTop(originScrollTop, scrollHeight - clientHeight); // Skip if `scrollTop` not change to avoid shake | ||
_this.lockScroll = false; | ||
/** | ||
* Phase 2: Trigger render since we should re-calculate current position. | ||
*/ | ||
if (scrollTop === _this.state.scrollTop || _this.lockScroll || disabled) { | ||
return; | ||
} | ||
_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 = alignScrollTop(originScrollTop, scrollHeight - clientHeight); // Skip if `scrollTop` not change to avoid shake | ||
var scrollPtg = getElementScrollPercentage(_this.listRef.current); | ||
var visibleCount = Math.ceil(height / itemHeight); | ||
if (scrollTop === _this.state.scrollTop || _this.lockScroll || disabled) { | ||
return; | ||
} | ||
var _getRangeIndex = getRangeIndex(scrollPtg, data.length, visibleCount), | ||
itemIndex = _getRangeIndex.itemIndex, | ||
itemOffsetPtg = _getRangeIndex.itemOffsetPtg, | ||
startIndex = _getRangeIndex.startIndex, | ||
endIndex = _getRangeIndex.endIndex; | ||
var scrollPtg = getElementScrollPercentage(_this.listRef.current); | ||
var visibleCount = Math.ceil(height / itemHeight); | ||
_this.setState({ | ||
status: 'MEASURE_START', | ||
scrollTop: scrollTop, | ||
itemIndex: itemIndex, | ||
itemOffsetPtg: itemOffsetPtg, | ||
startIndex: startIndex, | ||
endIndex: endIndex | ||
}); | ||
var _getRangeIndex = getRangeIndex(scrollPtg, data.length, visibleCount), | ||
itemIndex = _getRangeIndex.itemIndex, | ||
itemOffsetPtg = _getRangeIndex.itemOffsetPtg, | ||
startIndex = _getRangeIndex.startIndex, | ||
endIndex = _getRangeIndex.endIndex; | ||
_this.triggerOnScroll(event); | ||
}; | ||
_this.setState({ | ||
status: 'MEASURE_START', | ||
scrollTop: scrollTop, | ||
itemIndex: itemIndex, | ||
itemOffsetPtg: itemOffsetPtg, | ||
startIndex: startIndex, | ||
endIndex: endIndex | ||
}); | ||
_this.onRawScroll = function (event) { | ||
var scrollTop = _this.listRef.current.scrollTop; | ||
_this.triggerOnScroll(event); | ||
}; | ||
_this.setState({ | ||
scrollTop: scrollTop | ||
}); | ||
_this.onRawScroll = function (event) { | ||
var scrollTop = _this.listRef.current.scrollTop; | ||
_this.triggerOnScroll(event); | ||
}; | ||
_this.setState({ | ||
scrollTop: scrollTop | ||
}); | ||
_this.triggerOnScroll = function (event) { | ||
var onScroll = _this.props.onScroll; | ||
_this.triggerOnScroll(event); | ||
}; | ||
if (onScroll && event) { | ||
onScroll(event); | ||
} | ||
}; | ||
_this.triggerOnScroll = function (event) { | ||
var onScroll = _this.props.onScroll; | ||
_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 (onScroll && event) { | ||
onScroll(event); | ||
} | ||
}; | ||
if (index === data.length) { | ||
return GHOST_ITEM_KEY; | ||
} | ||
_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 | ||
var item = data[index]; | ||
if (index === data.length) { | ||
return GHOST_ITEM_KEY; | ||
} | ||
if (!item) { | ||
/* istanbul ignore next */ | ||
console.error('Not find index item. Please report this since it is a bug.'); | ||
} | ||
var item = data[index]; | ||
return _this.getItemKey(item, mergedProps); | ||
}; | ||
if (item === undefined) { | ||
/* istanbul ignore next */ | ||
console.error('Not find index item. Please report this since it is a bug.'); | ||
return null; | ||
} | ||
_this.getItemKey = function (item, props) { | ||
var _ref = props || _this.props, | ||
itemKey = _ref.itemKey; | ||
return _this.getItemKey(item, mergedProps); | ||
}; | ||
return typeof itemKey === 'function' ? itemKey(item) : item[itemKey]; | ||
}; | ||
/** | ||
* Collect current rendered dom element item heights | ||
*/ | ||
_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` | ||
_this.collectItemHeights = function (range) { | ||
var _ref2 = range || _this.state, | ||
startIndex = _ref2.startIndex, | ||
endIndex = _ref2.endIndex; | ||
for (var index = startIndex; index <= endIndex; index += 1) { | ||
var item = data[index]; // Only collect exist item height | ||
var data = _this.props.data; // Record here since measure item height will get warning in `render` | ||
if (item) { | ||
var eleKey = _this.getItemKey(item); | ||
for (var index = startIndex; index <= endIndex; index += 1) { | ||
var item = data[index]; // Only collect exist item height | ||
_this.itemElementHeights[eleKey] = getNodeHeight(_this.itemElements[eleKey]); | ||
if (item) { | ||
var eleKey = _this.getItemKey(item); | ||
_this.itemElementHeights[eleKey] = getNodeHeight(_this.itemElements[eleKey]); | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
_this.scrollTo = function (arg0) { | ||
raf(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; | ||
_this.scrollTo = function (arg0) { | ||
raf(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; | ||
}); | ||
} | ||
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]; | ||
var visibleCount = Math.ceil(height / itemHeight); | ||
var item = data[index]; | ||
if (item) { | ||
var clientHeight = _this.listRef.current.clientHeight; | ||
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 = getElementScrollPercentage(_this.listRef.current); | ||
var relativeLocatedItemTop = 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 | ||
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 = getElementScrollPercentage(_this.listRef.current); | ||
var relativeLocatedItemTop = 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 | ||
_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; | ||
var relativeTop; | ||
var mergedAlgin = align; | ||
if (align === 'auto') { | ||
var shouldChange = true; // Check if exist in the visible range | ||
if (align === 'auto') { | ||
var shouldChange = true; // Check if exist in the visible range | ||
if (Math.abs(itemIndex - index) < visibleCount) { | ||
var itemTop = relativeLocatedItemTop; | ||
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); | ||
if (index < itemIndex) { | ||
for (var i = index; i < itemIndex; i += 1) { | ||
var eleKey = _this.getIndexKey(i); | ||
itemTop -= _this.itemElementHeights[eleKey] || 0; | ||
itemTop -= _this.itemElementHeights[eleKey] || 0; | ||
} | ||
} else { | ||
for (var _i = itemIndex; _i <= index; _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; | ||
} | ||
shouldChange = itemTop <= 0 || itemTop >= clientHeight; | ||
} | ||
if (shouldChange) { | ||
// Out of range will fall back to position align | ||
mergedAlgin = index < itemIndex ? 'top' : 'bottom'; | ||
} else { | ||
var _getRangeIndex2 = getRangeIndex(scrollPtg, data.length, visibleCount), | ||
nextIndex = _getRangeIndex2.itemIndex, | ||
newOffsetPtg = _getRangeIndex2.itemOffsetPtg, | ||
startIndex = _getRangeIndex2.startIndex, | ||
endIndex = _getRangeIndex2.endIndex; | ||
if (shouldChange) { | ||
// Out of range will fall back to position align | ||
mergedAlgin = index < itemIndex ? 'top' : 'bottom'; | ||
} else { | ||
var _getRangeIndex2 = 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 | ||
}); | ||
_this.setState({ | ||
scrollTop: scrollTop, | ||
itemIndex: nextIndex, | ||
itemOffsetPtg: newOffsetPtg, | ||
startIndex: startIndex, | ||
endIndex: endIndex | ||
}); | ||
return; | ||
} | ||
} // Align with position should make scroll happen | ||
return; | ||
} | ||
} // Align with position should make scroll happen | ||
if (mergedAlgin === 'top') { | ||
relativeTop = 0; | ||
} else if (mergedAlgin === 'bottom') { | ||
var _eleKey2 = _this.getItemKey(item); | ||
if (mergedAlgin === 'top') { | ||
relativeTop = 0; | ||
} else if (mergedAlgin === 'bottom') { | ||
var _eleKey2 = _this.getItemKey(item); | ||
relativeTop = clientHeight - _this.itemElementHeights[_eleKey2] || 0; | ||
} | ||
relativeTop = clientHeight - _this.itemElementHeights[_eleKey2] || 0; | ||
} | ||
_this.internalScrollTo({ | ||
itemIndex: index, | ||
relativeTop: relativeTop | ||
_this.internalScrollTo({ | ||
itemIndex: index, | ||
relativeTop: relativeTop | ||
}); | ||
}); | ||
}); | ||
} else { | ||
// Raw list without virtual scroll set position directly | ||
_this.collectItemHeights({ | ||
startIndex: 0, | ||
endIndex: data.length - 1 | ||
}); | ||
} 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 mergedAlgin = align; // Collection index item position | ||
var indexItemHeight = _this.itemElementHeights[_this.getIndexKey(index)]; | ||
var indexItemHeight = _this.itemElementHeights[_this.getIndexKey(index)]; | ||
var itemTop = 0; | ||
var itemTop = 0; | ||
for (var i = 0; i < index; i += 1) { | ||
var eleKey = _this.getIndexKey(i); | ||
for (var i = 0; i < index; i += 1) { | ||
var eleKey = _this.getIndexKey(i); | ||
itemTop += _this.itemElementHeights[eleKey] || 0; | ||
} | ||
itemTop += _this.itemElementHeights[eleKey] || 0; | ||
} | ||
var itemBottom = itemTop + indexItemHeight; | ||
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 === '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); | ||
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; | ||
} | ||
} else { | ||
_this.listRef.current.scrollTop = arg0; | ||
} | ||
}); | ||
}; | ||
/** | ||
* Phase 4: Render item and get all the visible items height | ||
*/ | ||
}); | ||
}; | ||
/** | ||
* 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 | ||
_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' | ||
} : {} | ||
}); | ||
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 | ||
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; | ||
} | ||
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: requireVirtual(props.height, props.itemHeight, props.data.length, props.virtual), | ||
itemCount: props.data.length | ||
}; | ||
return _this; | ||
} | ||
_this.cachedProps = props; | ||
_this.state = { | ||
status: 'NONE', | ||
scrollTop: null, | ||
itemIndex: 0, | ||
itemOffsetPtg: 0, | ||
startIndex: 0, | ||
endIndex: 0, | ||
startItemTop: 0, | ||
isVirtual: requireVirtual(props.height, props.itemHeight, props.data.length, props.virtual), | ||
itemCount: props.data.length | ||
}; | ||
return _this; | ||
} | ||
_createClass(List, [{ | ||
key: "componentDidMount", | ||
_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 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 | ||
*/ | ||
/** | ||
* Phase 4: Record used item height | ||
* Phase 5: Trigger re-render to use correct position | ||
*/ | ||
}, { | ||
key: "componentDidUpdate", | ||
value: function componentDidUpdate() { | ||
var _this2 = this; | ||
}, { | ||
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; | ||
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 = findListDiffIndex(prevData, data, this.getItemKey); | ||
changedItemIndex = diff ? diff.index : null; | ||
} | ||
if (prevData.length !== data.length) { | ||
var diff = 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 (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(); | ||
if (onSkipRender && (changedItemIndex === null || changedItemIndex < startIndex || endIndex < changedItemIndex)) { | ||
onSkipRender(); | ||
} | ||
} | ||
return; | ||
} | ||
return; | ||
} | ||
var isVirtual = requireVirtual(height, itemHeight, data.length, virtual); | ||
var nextStatus = status; | ||
var isVirtual = 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 (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 (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` | ||
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 | ||
this.collectItemHeights(); // Calculate top visible item top offset | ||
var locatedItemTop = getItemAbsoluteTop({ | ||
itemIndex: itemIndex, | ||
itemOffsetPtg: itemOffsetPtg, | ||
itemElementHeights: this.itemElementHeights, | ||
scrollTop: scrollTop, | ||
scrollPtg: getElementScrollPercentage(this.listRef.current), | ||
clientHeight: this.listRef.current.clientHeight, | ||
getItemKey: this.getIndexKey | ||
}); | ||
var startItemTop = locatedItemTop; | ||
var locatedItemTop = getItemAbsoluteTop({ | ||
itemIndex: itemIndex, | ||
itemOffsetPtg: itemOffsetPtg, | ||
itemElementHeights: this.itemElementHeights, | ||
scrollTop: scrollTop, | ||
scrollPtg: 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; | ||
for (var index = itemIndex - 1; index >= _startIndex; index -= 1) { | ||
startItemTop -= this.itemElementHeights[this.getIndexKey(index)] || 0; | ||
} | ||
this.setState({ | ||
status: 'MEASURE_DONE', | ||
startItemTop: startItemTop | ||
}); | ||
} | ||
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; | ||
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; | ||
} | ||
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 () { | ||
this.lockScroll = true; | ||
this.listRef.current.scrollTop = -rawTop; | ||
this.setState({ | ||
status: 'MEASURE_DONE', | ||
itemIndex: 0 | ||
}); | ||
requestAnimationFrame(function () { | ||
_this2.lockScroll = false; | ||
requestAnimationFrame(function () { | ||
_this2.lockScroll = false; | ||
}); | ||
}); | ||
}); | ||
} else if (prevData.length !== data.length && changedItemIndex !== null && height) { | ||
/** | ||
* 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 | ||
} else if (prevData.length !== data.length && changedItemIndex !== null && height) { | ||
/** | ||
* 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 | ||
this.collectItemHeights(); // 1. Get origin located item top | ||
var originLocatedItemRelativeTop; | ||
var originLocatedItemRelativeTop; | ||
if (this.state.status === 'SWITCH_TO_VIRTUAL') { | ||
originItemIndex = 0; | ||
originLocatedItemRelativeTop = -this.state.scrollTop; | ||
} else { | ||
originLocatedItemRelativeTop = getItemRelativeTop({ | ||
itemIndex: originItemIndex, | ||
itemOffsetPtg: originItemOffsetPtg, | ||
itemElementHeights: this.itemElementHeights, | ||
scrollPtg: 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 | ||
if (this.state.status === 'SWITCH_TO_VIRTUAL') { | ||
originItemIndex = 0; | ||
originLocatedItemRelativeTop = -this.state.scrollTop; | ||
} else { | ||
originLocatedItemRelativeTop = getItemRelativeTop({ | ||
itemIndex: originItemIndex, | ||
itemOffsetPtg: originItemOffsetPtg, | ||
itemElementHeights: this.itemElementHeights, | ||
scrollPtg: 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 | ||
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 | ||
if (originCompareItemIndex < 0) { | ||
originCompareItemIndex = 0; | ||
} // 3. Find the compare item top | ||
var originCompareItemTop = getCompareItemRelativeTop({ | ||
locatedItemRelativeTop: originLocatedItemRelativeTop, | ||
locatedItemIndex: originItemIndex, | ||
compareItemIndex: originCompareItemIndex, | ||
startIndex: originStartIndex, | ||
endIndex: originEndIndex, | ||
getItemKey: function getItemKey(index) { | ||
return _this2.getIndexKey(index, _this2.cachedProps); | ||
}, | ||
itemElementHeights: this.itemElementHeights | ||
}); | ||
var originCompareItemTop = 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 | ||
*/ | ||
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: originCompareItemIndex, | ||
relativeTop: originCompareItemTop | ||
itemIndex: 0, | ||
relativeTop: 0 | ||
} | ||
}); | ||
} 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: "internalScrollTo", | ||
value: function internalScrollTo(relativeScroll) { | ||
var _this3 = this; | ||
this.cachedProps = this.props; | ||
} | ||
}, { | ||
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 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; | ||
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 = getIndexByStartLoc(0, maxScrollTop, originScrollTop, i); | ||
var scrollPtg = getScrollPercentage({ | ||
scrollTop: scrollTop, | ||
scrollHeight: scrollHeight, | ||
clientHeight: clientHeight | ||
}); | ||
var visibleCount = Math.ceil(height / itemHeight); | ||
for (var i = 0; i < maxScrollTop; i += 1) { | ||
var scrollTop = getIndexByStartLoc(0, maxScrollTop, originScrollTop, i); | ||
var scrollPtg = getScrollPercentage({ | ||
scrollTop: scrollTop, | ||
scrollHeight: scrollHeight, | ||
clientHeight: clientHeight | ||
}); | ||
var visibleCount = Math.ceil(height / itemHeight); | ||
var _getRangeIndex3 = 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 | ||
var _getRangeIndex3 = 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 = getItemRelativeTop({ | ||
itemIndex: itemIndex, | ||
itemOffsetPtg: itemOffsetPtg, | ||
itemElementHeights: this.itemElementHeights, | ||
scrollPtg: scrollPtg, | ||
clientHeight: clientHeight, | ||
getItemKey: this.getIndexKey | ||
}); | ||
var compareItemTop = 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 | ||
if (startIndex <= compareItemIndex && compareItemIndex <= endIndex) { | ||
// 1.1 Get measure located item relative top | ||
var locatedItemRelativeTop = getItemRelativeTop({ | ||
itemIndex: itemIndex, | ||
itemOffsetPtg: itemOffsetPtg, | ||
itemElementHeights: this.itemElementHeights, | ||
scrollPtg: scrollPtg, | ||
clientHeight: clientHeight, | ||
getItemKey: this.getIndexKey | ||
}); | ||
var compareItemTop = 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); | ||
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 (similarity < bestSimilarity) { | ||
bestSimilarity = similarity; | ||
bestScrollTop = scrollTop; | ||
bestItemIndex = itemIndex; | ||
bestItemOffsetPtg = itemOffsetPtg; | ||
bestStartIndex = startIndex; | ||
bestEndIndex = endIndex; | ||
missSimilarity = 0; | ||
} else { | ||
missSimilarity += 1; | ||
if (missSimilarity > 10) { | ||
break; | ||
} | ||
} // If keeping 10 times not match similarity, | ||
// check more scrollTop is meaningless. | ||
// Here boundary is set to 10. | ||
} // 2. Re-scroll if has best scroll match | ||
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 () { | ||
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 () { | ||
_this3.lockScroll = false; | ||
requestAnimationFrame(function () { | ||
_this3.lockScroll = false; | ||
}); | ||
}); | ||
}); | ||
} | ||
} | ||
} | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
var _this$state5 = this.state, | ||
isVirtual = _this$state5.isVirtual, | ||
itemCount = _this$state5.itemCount; | ||
}, { | ||
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 _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 = classNames(prefixCls, className); // Render pure list if not set height or height is enough for all items | ||
var mergedClassName = classNames(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 = requireVirtual(height, itemHeight, data.length, virtual); | ||
if (!isVirtual) { | ||
/** | ||
* Virtual list switch is works on component updated. | ||
* We should double check here if need cut the content. | ||
*/ | ||
var shouldVirtual = 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, { | ||
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: height ? _objectSpread({}, style, _defineProperty({}, fullHeight ? 'height' : 'maxHeight', height), ScrollStyle) : style, | ||
style: mergedStyle, | ||
className: mergedClassName | ||
}, restProps, { | ||
onScroll: this.onRawScroll, | ||
onScroll: this.onScroll, | ||
ref: this.listRef | ||
}), React.createElement(Filler, { | ||
prefixCls: prefixCls, | ||
height: height | ||
}, this.renderChildren(shouldVirtual ? data.slice(0, Math.ceil(height / itemHeight)) : data, 0, children))); | ||
} // Use virtual list | ||
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 | ||
}; | ||
} | ||
var mergedStyle = _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, { | ||
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 null; | ||
} | ||
}]); | ||
return List; | ||
}(React.Component); | ||
List.defaultProps = { | ||
itemHeight: 15, | ||
data: [] | ||
}; | ||
return List; | ||
}(React.Component); | ||
}(); | ||
List.defaultProps = { | ||
itemHeight: 15, | ||
data: [] | ||
}; | ||
export default List; |
@@ -19,2 +19,8 @@ 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 _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 () { 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); } | ||
@@ -24,8 +30,6 @@ | ||
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); } | ||
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); } | ||
import * as React from 'react'; | ||
@@ -39,2 +43,4 @@ import classNames from 'classnames'; | ||
var _super = _createSuper(List); | ||
function List() { | ||
@@ -45,3 +51,3 @@ var _this; | ||
_this = _possibleConstructorReturn(this, _getPrototypeOf(List).apply(this, arguments)); | ||
_this = _super.apply(this, arguments); | ||
@@ -79,3 +85,3 @@ _this.scrollTo = function () {// Do nothing sine this is a mock! | ||
return React.createElement(Component, Object.assign({ | ||
style: _objectSpread({}, style, { | ||
style: _objectSpread(_objectSpread({}, style), {}, { | ||
height: height | ||
@@ -82,0 +88,0 @@ }), |
@@ -46,3 +46,3 @@ "use strict"; | ||
}; | ||
innerStyle = _objectSpread({}, innerStyle, { | ||
innerStyle = _objectSpread(_objectSpread({}, innerStyle), {}, { | ||
transform: "translateY(".concat(offset, "px)"), | ||
@@ -49,0 +49,0 @@ position: 'absolute', |
import * as React from 'react'; | ||
import { Key } from './utils/itemUtil'; | ||
declare type ScrollAlign = 'top' | 'bottom' | 'auto'; | ||
declare type ScrollConfig = { | ||
index: number; | ||
align?: ScrollAlign; | ||
} | { | ||
key: Key; | ||
align?: ScrollAlign; | ||
}; | ||
export declare type RenderFunc<T> = (item: T, index: number, props: { | ||
@@ -125,3 +132,3 @@ style: React.CSSProperties; | ||
getIndexKey: (index: number, props?: Partial<ListProps<T>>) => string | number; | ||
getItemKey: (item: T, props?: Partial<ListProps<T>>) => string | number; | ||
getItemKey: (item: T, props?: Partial<ListProps<T>>) => Key; | ||
/** | ||
@@ -134,9 +141,3 @@ * Collect current rendered dom element item heights | ||
}) => void; | ||
scrollTo: (arg0: number | { | ||
index: number; | ||
align?: ScrollAlign; | ||
} | { | ||
key: string | number; | ||
align?: ScrollAlign; | ||
}) => void; | ||
scrollTo: (arg0: number | ScrollConfig) => void; | ||
internalScrollTo(relativeScroll: RelativeScroll): void; | ||
@@ -143,0 +144,0 @@ /** |
1187
lib/List.js
@@ -44,2 +44,8 @@ "use strict"; | ||
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 () { 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); } | ||
@@ -49,8 +55,6 @@ | ||
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); } | ||
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); } | ||
var ScrollStyle = { | ||
@@ -83,727 +87,736 @@ overflowY: 'auto', | ||
var List = /*#__PURE__*/function (_React$Component) { | ||
_inherits(List, _React$Component); | ||
var List = | ||
/** @class */ | ||
function () { | ||
var List = /*#__PURE__*/function (_React$Component) { | ||
_inherits(List, _React$Component); | ||
function List(props) { | ||
var _this; | ||
var _super = _createSuper(List); | ||
_classCallCheck(this, List); | ||
function List(props) { | ||
var _this; | ||
_this = _possibleConstructorReturn(this, _getPrototypeOf(List).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 | ||
*/ | ||
_classCallCheck(this, List); | ||
_this.lockScroll = false; | ||
/** | ||
* Phase 2: Trigger render since we should re-calculate current position. | ||
*/ | ||
_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.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 | ||
_this.lockScroll = false; | ||
/** | ||
* Phase 2: Trigger render since we should re-calculate current position. | ||
*/ | ||
if (scrollTop === _this.state.scrollTop || _this.lockScroll || disabled) { | ||
return; | ||
} | ||
_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 | ||
var scrollPtg = (0, _itemUtil.getElementScrollPercentage)(_this.listRef.current); | ||
var visibleCount = Math.ceil(height / itemHeight); | ||
if (scrollTop === _this.state.scrollTop || _this.lockScroll || disabled) { | ||
return; | ||
} | ||
var _getRangeIndex = (0, _itemUtil.getRangeIndex)(scrollPtg, data.length, visibleCount), | ||
itemIndex = _getRangeIndex.itemIndex, | ||
itemOffsetPtg = _getRangeIndex.itemOffsetPtg, | ||
startIndex = _getRangeIndex.startIndex, | ||
endIndex = _getRangeIndex.endIndex; | ||
var scrollPtg = (0, _itemUtil.getElementScrollPercentage)(_this.listRef.current); | ||
var visibleCount = Math.ceil(height / itemHeight); | ||
_this.setState({ | ||
status: 'MEASURE_START', | ||
scrollTop: scrollTop, | ||
itemIndex: itemIndex, | ||
itemOffsetPtg: itemOffsetPtg, | ||
startIndex: startIndex, | ||
endIndex: endIndex | ||
}); | ||
var _getRangeIndex = (0, _itemUtil.getRangeIndex)(scrollPtg, data.length, visibleCount), | ||
itemIndex = _getRangeIndex.itemIndex, | ||
itemOffsetPtg = _getRangeIndex.itemOffsetPtg, | ||
startIndex = _getRangeIndex.startIndex, | ||
endIndex = _getRangeIndex.endIndex; | ||
_this.triggerOnScroll(event); | ||
}; | ||
_this.setState({ | ||
status: 'MEASURE_START', | ||
scrollTop: scrollTop, | ||
itemIndex: itemIndex, | ||
itemOffsetPtg: itemOffsetPtg, | ||
startIndex: startIndex, | ||
endIndex: endIndex | ||
}); | ||
_this.onRawScroll = function (event) { | ||
var scrollTop = _this.listRef.current.scrollTop; | ||
_this.triggerOnScroll(event); | ||
}; | ||
_this.setState({ | ||
scrollTop: scrollTop | ||
}); | ||
_this.onRawScroll = function (event) { | ||
var scrollTop = _this.listRef.current.scrollTop; | ||
_this.triggerOnScroll(event); | ||
}; | ||
_this.setState({ | ||
scrollTop: scrollTop | ||
}); | ||
_this.triggerOnScroll = function (event) { | ||
var onScroll = _this.props.onScroll; | ||
_this.triggerOnScroll(event); | ||
}; | ||
if (onScroll && event) { | ||
onScroll(event); | ||
} | ||
}; | ||
_this.triggerOnScroll = function (event) { | ||
var onScroll = _this.props.onScroll; | ||
_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 (onScroll && event) { | ||
onScroll(event); | ||
} | ||
}; | ||
if (index === data.length) { | ||
return _itemUtil.GHOST_ITEM_KEY; | ||
} | ||
_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 | ||
var item = data[index]; | ||
if (index === data.length) { | ||
return _itemUtil.GHOST_ITEM_KEY; | ||
} | ||
if (!item) { | ||
/* istanbul ignore next */ | ||
console.error('Not find index item. Please report this since it is a bug.'); | ||
} | ||
var item = data[index]; | ||
return _this.getItemKey(item, mergedProps); | ||
}; | ||
if (item === undefined) { | ||
/* istanbul ignore next */ | ||
console.error('Not find index item. Please report this since it is a bug.'); | ||
return null; | ||
} | ||
_this.getItemKey = function (item, props) { | ||
var _ref = props || _this.props, | ||
itemKey = _ref.itemKey; | ||
return _this.getItemKey(item, mergedProps); | ||
}; | ||
return typeof itemKey === 'function' ? itemKey(item) : item[itemKey]; | ||
}; | ||
/** | ||
* Collect current rendered dom element item heights | ||
*/ | ||
_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` | ||
_this.collectItemHeights = function (range) { | ||
var _ref2 = range || _this.state, | ||
startIndex = _ref2.startIndex, | ||
endIndex = _ref2.endIndex; | ||
for (var index = startIndex; index <= endIndex; index += 1) { | ||
var item = data[index]; // Only collect exist item height | ||
var data = _this.props.data; // Record here since measure item height will get warning in `render` | ||
if (item) { | ||
var eleKey = _this.getItemKey(item); | ||
for (var index = startIndex; index <= endIndex; index += 1) { | ||
var item = data[index]; // Only collect exist item height | ||
_this.itemElementHeights[eleKey] = (0, _itemUtil.getNodeHeight)(_this.itemElements[eleKey]); | ||
if (item) { | ||
var eleKey = _this.getItemKey(item); | ||
_this.itemElementHeights[eleKey] = (0, _itemUtil.getNodeHeight)(_this.itemElements[eleKey]); | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
_this.scrollTo = function (arg0) { | ||
(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; | ||
_this.scrollTo = function (arg0) { | ||
(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; | ||
}); | ||
} | ||
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]; | ||
var visibleCount = Math.ceil(height / itemHeight); | ||
var item = data[index]; | ||
if (item) { | ||
var clientHeight = _this.listRef.current.clientHeight; | ||
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 | ||
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 | ||
_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; | ||
var relativeTop; | ||
var mergedAlgin = align; | ||
if (align === 'auto') { | ||
var shouldChange = true; // Check if exist in the visible range | ||
if (align === 'auto') { | ||
var shouldChange = true; // Check if exist in the visible range | ||
if (Math.abs(itemIndex - index) < visibleCount) { | ||
var itemTop = relativeLocatedItemTop; | ||
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); | ||
if (index < itemIndex) { | ||
for (var i = index; i < itemIndex; i += 1) { | ||
var eleKey = _this.getIndexKey(i); | ||
itemTop -= _this.itemElementHeights[eleKey] || 0; | ||
itemTop -= _this.itemElementHeights[eleKey] || 0; | ||
} | ||
} else { | ||
for (var _i = itemIndex; _i <= index; _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; | ||
} | ||
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; | ||
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 | ||
}); | ||
_this.setState({ | ||
scrollTop: scrollTop, | ||
itemIndex: nextIndex, | ||
itemOffsetPtg: newOffsetPtg, | ||
startIndex: startIndex, | ||
endIndex: endIndex | ||
}); | ||
return; | ||
} | ||
} // Align with position should make scroll happen | ||
return; | ||
} | ||
} // Align with position should make scroll happen | ||
if (mergedAlgin === 'top') { | ||
relativeTop = 0; | ||
} else if (mergedAlgin === 'bottom') { | ||
var _eleKey2 = _this.getItemKey(item); | ||
if (mergedAlgin === 'top') { | ||
relativeTop = 0; | ||
} else if (mergedAlgin === 'bottom') { | ||
var _eleKey2 = _this.getItemKey(item); | ||
relativeTop = clientHeight - _this.itemElementHeights[_eleKey2] || 0; | ||
} | ||
relativeTop = clientHeight - _this.itemElementHeights[_eleKey2] || 0; | ||
} | ||
_this.internalScrollTo({ | ||
itemIndex: index, | ||
relativeTop: relativeTop | ||
_this.internalScrollTo({ | ||
itemIndex: index, | ||
relativeTop: relativeTop | ||
}); | ||
}); | ||
}); | ||
} else { | ||
// Raw list without virtual scroll set position directly | ||
_this.collectItemHeights({ | ||
startIndex: 0, | ||
endIndex: data.length - 1 | ||
}); | ||
} 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 mergedAlgin = align; // Collection index item position | ||
var indexItemHeight = _this.itemElementHeights[_this.getIndexKey(index)]; | ||
var indexItemHeight = _this.itemElementHeights[_this.getIndexKey(index)]; | ||
var itemTop = 0; | ||
var itemTop = 0; | ||
for (var i = 0; i < index; i += 1) { | ||
var eleKey = _this.getIndexKey(i); | ||
for (var i = 0; i < index; i += 1) { | ||
var eleKey = _this.getIndexKey(i); | ||
itemTop += _this.itemElementHeights[eleKey] || 0; | ||
} | ||
itemTop += _this.itemElementHeights[eleKey] || 0; | ||
} | ||
var itemBottom = itemTop + indexItemHeight; | ||
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 === '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); | ||
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; | ||
} | ||
} else { | ||
_this.listRef.current.scrollTop = arg0; | ||
} | ||
}); | ||
}; | ||
/** | ||
* Phase 4: Render item and get all the visible items height | ||
*/ | ||
}); | ||
}; | ||
/** | ||
* 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 | ||
_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' | ||
} : {} | ||
}); | ||
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 | ||
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; | ||
} | ||
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; | ||
} | ||
_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", | ||
_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 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 | ||
*/ | ||
/** | ||
* Phase 4: Record used item height | ||
* Phase 5: Trigger re-render to use correct position | ||
*/ | ||
}, { | ||
key: "componentDidUpdate", | ||
value: function componentDidUpdate() { | ||
var _this2 = this; | ||
}, { | ||
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; | ||
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 (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 (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(); | ||
if (onSkipRender && (changedItemIndex === null || changedItemIndex < startIndex || endIndex < changedItemIndex)) { | ||
onSkipRender(); | ||
} | ||
} | ||
return; | ||
} | ||
return; | ||
} | ||
var isVirtual = (0, _itemUtil.requireVirtual)(height, itemHeight, data.length, virtual); | ||
var nextStatus = status; | ||
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 (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 (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` | ||
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 | ||
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; | ||
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; | ||
for (var index = itemIndex - 1; index >= _startIndex; index -= 1) { | ||
startItemTop -= this.itemElementHeights[this.getIndexKey(index)] || 0; | ||
} | ||
this.setState({ | ||
status: 'MEASURE_DONE', | ||
startItemTop: startItemTop | ||
}); | ||
} | ||
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; | ||
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; | ||
} | ||
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 () { | ||
this.lockScroll = true; | ||
this.listRef.current.scrollTop = -rawTop; | ||
this.setState({ | ||
status: 'MEASURE_DONE', | ||
itemIndex: 0 | ||
}); | ||
requestAnimationFrame(function () { | ||
_this2.lockScroll = false; | ||
requestAnimationFrame(function () { | ||
_this2.lockScroll = false; | ||
}); | ||
}); | ||
}); | ||
} else if (prevData.length !== data.length && changedItemIndex !== null && height) { | ||
/** | ||
* 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 | ||
} else if (prevData.length !== data.length && changedItemIndex !== null && height) { | ||
/** | ||
* 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 | ||
this.collectItemHeights(); // 1. Get origin located item top | ||
var originLocatedItemRelativeTop; | ||
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 | ||
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 | ||
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 | ||
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 | ||
}); | ||
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 | ||
*/ | ||
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: originCompareItemIndex, | ||
relativeTop: originCompareItemTop | ||
itemIndex: 0, | ||
relativeTop: 0 | ||
} | ||
}); | ||
} 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: "internalScrollTo", | ||
value: function internalScrollTo(relativeScroll) { | ||
var _this3 = this; | ||
this.cachedProps = this.props; | ||
} | ||
}, { | ||
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 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; | ||
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); | ||
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 | ||
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 | ||
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); | ||
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 (similarity < bestSimilarity) { | ||
bestSimilarity = similarity; | ||
bestScrollTop = scrollTop; | ||
bestItemIndex = itemIndex; | ||
bestItemOffsetPtg = itemOffsetPtg; | ||
bestStartIndex = startIndex; | ||
bestEndIndex = endIndex; | ||
missSimilarity = 0; | ||
} else { | ||
missSimilarity += 1; | ||
if (missSimilarity > 10) { | ||
break; | ||
} | ||
} // If keeping 10 times not match similarity, | ||
// check more scrollTop is meaningless. | ||
// Here boundary is set to 10. | ||
} // 2. Re-scroll if has best scroll match | ||
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 () { | ||
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 () { | ||
_this3.lockScroll = false; | ||
requestAnimationFrame(function () { | ||
_this3.lockScroll = false; | ||
}); | ||
}); | ||
}); | ||
} | ||
} | ||
} | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
var _this$state5 = this.state, | ||
isVirtual = _this$state5.isVirtual, | ||
itemCount = _this$state5.itemCount; | ||
}, { | ||
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 _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 | ||
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); | ||
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: height ? _objectSpread({}, style, _defineProperty({}, fullHeight ? 'height' : 'maxHeight', height), ScrollStyle) : style, | ||
style: mergedStyle, | ||
className: mergedClassName | ||
}, restProps, { | ||
onScroll: this.onRawScroll, | ||
onScroll: this.onScroll, | ||
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 | ||
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 | ||
}; | ||
} | ||
var mergedStyle = _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 null; | ||
} | ||
}]); | ||
return List; | ||
}(React.Component); | ||
List.defaultProps = { | ||
itemHeight: 15, | ||
data: [] | ||
}; | ||
return List; | ||
}(React.Component); | ||
}(); | ||
List.defaultProps = { | ||
itemHeight: 15, | ||
data: [] | ||
}; | ||
var _default = List; | ||
exports.default = _default; |
@@ -40,2 +40,8 @@ "use strict"; | ||
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 () { 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); } | ||
@@ -45,11 +51,11 @@ | ||
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); } | ||
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); } | ||
var List = /*#__PURE__*/function (_OriginList) { | ||
_inherits(List, _OriginList); | ||
var _super = _createSuper(List); | ||
function List() { | ||
@@ -60,3 +66,3 @@ var _this; | ||
_this = _possibleConstructorReturn(this, _getPrototypeOf(List).apply(this, arguments)); | ||
_this = _super.apply(this, arguments); | ||
@@ -94,3 +100,3 @@ _this.scrollTo = function () {// Do nothing sine this is a mock! | ||
return React.createElement(Component, Object.assign({ | ||
style: _objectSpread({}, style, { | ||
style: _objectSpread(_objectSpread({}, style), {}, { | ||
height: height | ||
@@ -97,0 +103,0 @@ }), |
{ | ||
"name": "rc-virtual-list", | ||
"version": "1.1.2", | ||
"version": "1.1.3", | ||
"description": "React Virtual List Component", | ||
@@ -5,0 +5,0 @@ "engines": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
126105
2584