@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
210 lines (209 loc) • 8 kB
JavaScript
import { _ as _define_property } from "@swc/helpers/_/_define_property";
import { _ as _object_spread } from "@swc/helpers/_/_object_spread";
import { _ as _object_spread_props } from "@swc/helpers/_/_object_spread_props";
import { _ as _object_without_properties } from "@swc/helpers/_/_object_without_properties";
import { _ as _sliced_to_array } from "@swc/helpers/_/_sliced_to_array";
import React, { useCallback, useEffect, useRef, useState } from "react";
import classNames from "classnames";
import { binarySearch, getEndIndex, getListTotalSize, initPositinoCache, updateItemSize } from "./utils";
import { ComponentDefaults } from "../../utils/typings";
var defaultProps = _object_spread_props(_object_spread({}, ComponentDefaults), {
list: [],
itemHeight: 66,
itemEqual: true,
direction: 'vertical',
overscan: 2
});
export var VirtualList = function(props) {
var _ref = _object_spread({}, defaultProps, props), list = _ref.list, itemRender = _ref.itemRender, itemEqual = _ref.itemEqual, itemHeight = _ref.itemHeight, direction = _ref.direction, overscan = _ref.overscan, key = _ref.key, onScroll = _ref.onScroll, className = _ref.className, containerHeight = _ref.containerHeight, rest = _object_without_properties(_ref, [
"list",
"itemRender",
"itemEqual",
"itemHeight",
"direction",
"overscan",
"key",
"onScroll",
"className",
"containerHeight"
]);
var horizontal = direction === 'horizontal';
var sizeKey = horizontal ? 'width' : 'height';
var scrollKey = horizontal ? 'scrollLeft' : 'scrollTop';
var offsetKey = horizontal ? 'left' : 'top';
// 虚拟列表容器ref
var scrollRef = useRef(null);
// 虚拟列表显示区域ref
var itemsRef = useRef(null);
// 列表位置信息
var _useState = _sliced_to_array(useState([
{
index: 0,
left: 0,
top: 0,
bottom: 0,
width: 0,
height: 0,
right: 0
}
]), 2), positions = _useState[0], setPositions = _useState[1];
// 列表总大小
var _useState1 = _sliced_to_array(useState(99999999), 2), listTotalSize = _useState1[0], setListTotalSize = _useState1[1];
// 可视区域条数
var _useState2 = _sliced_to_array(useState(0), 2), visibleCount = _useState2[0], setVisibleCount = _useState2[1];
var _useState3 = _sliced_to_array(useState(containerHeight || 0), 2), offSetSize = _useState3[0], setOffSetSize = _useState3[1];
var _useState4 = _sliced_to_array(useState({
startOffset: 0,
startIndex: 0,
overStart: 0,
endIndex: 10
}), 2), options = _useState4[0], setOptions = _useState4[1];
// 列表位置信息
useEffect(function() {
var pos = initPositinoCache(itemHeight, list.length);
setPositions(pos);
var totalSize = getListTotalSize(pos, horizontal);
setListTotalSize(totalSize);
}, [
list,
itemHeight,
horizontal
]);
var getElement = useCallback(function() {
var _scrollRef_current;
return ((_scrollRef_current = scrollRef.current) === null || _scrollRef_current === void 0 ? void 0 : _scrollRef_current.parentElement) || document.body;
}, []);
useEffect(function() {
if (containerHeight) return;
var size = horizontal ? getElement().offsetWidth : getElement().offsetHeight;
setOffSetSize(size);
}, [
getElement,
horizontal,
containerHeight
]);
useEffect(function() {
// 初始-计算visibleCount
if (offSetSize === 0) return;
var count = Math.ceil(offSetSize / itemHeight) + overscan;
setVisibleCount(count);
setOptions(function(options) {
return _object_spread_props(_object_spread({}, options), {
endIndex: count
});
});
}, [
getElement,
horizontal,
itemHeight,
overscan,
offSetSize
]);
var updateTotalSize = useCallback(function() {
if (!itemsRef.current) return;
var items = itemsRef.current.children;
if (!items.length) return;
// 更新缓存
updateItemSize(positions, items, sizeKey);
var totalSize = getListTotalSize(positions, horizontal);
setListTotalSize(totalSize);
}, [
positions,
sizeKey,
horizontal
]);
var scroll = useCallback(function() {
requestAnimationFrame(function(e) {
var scrollSize = getElement()[scrollKey];
var startIndex = binarySearch(positions, horizontal, scrollSize);
var overStart = startIndex - overscan > -1 ? startIndex - overscan : 0;
// const offSetSize = horizontal ? getElement().offsetWidth : getElement().offsetHeight
if (!itemEqual) {
updateTotalSize();
}
var endIndex = getEndIndex({
list: list,
startIndex: startIndex,
visibleCount: visibleCount,
itemEqual: itemEqual,
positions: positions,
offSetSize: offSetSize,
sizeKey: sizeKey,
overscan: overscan
});
var startOffset = positions[startIndex][offsetKey];
setOptions({
startOffset: startOffset,
startIndex: startIndex,
overStart: overStart,
endIndex: endIndex
});
// 无限下滑
if (endIndex > list.length - 1) {
if (onScroll) {
onScroll();
}
}
});
}, [
positions,
getElement,
list,
visibleCount,
itemEqual,
updateTotalSize,
offsetKey,
sizeKey,
scrollKey,
horizontal,
overscan,
offSetSize
]);
useEffect(function() {
var element = getElement();
element.addEventListener('scroll', scroll, false);
return function() {
element.removeEventListener('scroll', scroll, false);
};
}, [
getElement,
scroll
]);
return /*#__PURE__*/ React.createElement("div", _object_spread_props(_object_spread({
className: classNames('nut-virtualList-box', className)
}, rest), {
style: _define_property({}, sizeKey, containerHeight ? "".concat(offSetSize, "px") : '')
}), /*#__PURE__*/ React.createElement("div", {
ref: scrollRef,
className: classNames({
'nut-horizontal-box': horizontal,
'nut-vertical-box': !horizontal
}),
style: _define_property({
position: 'relative'
}, sizeKey, "".concat(listTotalSize, "px"))
}, /*#__PURE__*/ React.createElement("ul", {
className: classNames('nut-virtuallist-items', {
'nut-horizontal-items': horizontal,
'nut-vertical-items': !horizontal
}),
ref: itemsRef,
style: {
transform: horizontal ? "translate3d(".concat(options.startOffset, "px,0,0)") : "translate3d(0,".concat(options.startOffset, "px,0)")
}
}, list.slice(options.overStart, options.endIndex).map(function(data, index) {
var startIndex = options.startIndex, overStart = options.overStart;
var dataIndex = overStart + index;
var styleVal = dataIndex < startIndex ? 'none' : 'block';
var keyVal = key && data[key] ? data[key] : dataIndex;
return /*#__PURE__*/ React.createElement("li", {
"data-index": "".concat(dataIndex),
className: "nut-virtuallist-item",
key: "".concat(keyVal),
style: {
display: styleVal
}
}, itemRender ? itemRender(data, dataIndex, index) : data);
}))));
};
VirtualList.displayName = 'NutVirtualList';