@nutui/nutui-react-taro
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
148 lines (147 loc) • 5.75 kB
JavaScript
import { _ as __rest } from "./tslib.es6-iWu3F_1J.js";
import React__default, { useMemo, useState, useRef, useEffect, useCallback } from "react";
import { View, ScrollView } from "@tarojs/components";
import classNames from "classnames";
import { g as getWindowInfo } from "./get-system-info-D27xNglo.js";
import { C as ComponentDefaults } from "./typings-DV9RBfhj.js";
const initPositinoCache = (reaItemSize, length = 0) => {
let index = 0;
const positions = Array(length);
while (index < length) {
positions[index] = {
index,
height: reaItemSize,
width: reaItemSize,
top: index * reaItemSize,
bottom: (index + 1) * reaItemSize,
left: index * reaItemSize,
right: (index + 1) * reaItemSize
};
index++;
}
return positions;
};
const updateItemSize = (positions, items, sizeKey, margin) => {
const newPos = positions.concat();
Array.from(items).forEach((item) => {
const index = Number(item.getAttribute("data-index"));
const styleVal = item.getAttribute("style");
if (styleVal && styleVal.includes("none"))
return;
let nowSize = item.getBoundingClientRect()[sizeKey];
if (margin)
nowSize += margin;
const oldSize = positions[index][sizeKey];
const dValue = oldSize - nowSize;
if (dValue) {
{
newPos[index].bottom -= dValue;
newPos[index][sizeKey] = nowSize;
for (let k = index + 1; k < positions.length; k++) {
newPos[k].top = positions[k - 1].bottom;
newPos[k].bottom -= dValue;
}
}
}
});
};
const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { list: [], itemHeight: 66, margin: 10, itemEqual: true, overscan: 2 });
const VirtualList = (props) => {
const _a = Object.assign(Object.assign({}, defaultProps), props), { list, itemRender, itemHeight, margin, itemEqual, overscan, key, onScroll, className, containerHeight: origContainerHeight } = _a, rest = __rest(_a, ["list", "itemRender", "itemHeight", "margin", "itemEqual", "overscan", "key", "onScroll", "className", "containerHeight"]);
const clientHeight = useMemo(() => getWindowInfo().windowHeight - 5 || 667, []);
const containerHeight = useMemo(() => origContainerHeight !== null && origContainerHeight !== void 0 ? origContainerHeight : clientHeight, [origContainerHeight, clientHeight]);
const [startOffset, setStartOffset] = useState(0);
const start = useRef(0);
const scrollRef = useRef(null);
const itemsRef = useRef(null);
const [positions, setPositions] = useState([
{
index: 0,
left: 0,
top: 0,
bottom: 0,
width: 0,
height: 0,
right: 0
}
]);
const [offSetSize, setOffSetSize] = useState(containerHeight || 0);
useEffect(() => {
setPositions((options) => {
return Object.assign(Object.assign({}, options), { endIndex: visibleCount() });
});
}, [itemHeight, overscan, offSetSize]);
useEffect(() => {
if (containerHeight)
return;
setOffSetSize(getContainerHeight());
}, [containerHeight]);
useEffect(() => {
const pos = initPositinoCache(itemHeight + margin, list.length);
setPositions(pos);
}, [itemHeight, list]);
const getContainerHeight = () => {
const initH = (itemHeight + margin) * list.length;
return initH < clientHeight ? initH + overscan * (itemHeight + margin) - 5 : Math.min(containerHeight, clientHeight);
};
const visibleCount = () => {
return Math.ceil(getContainerHeight() / (itemHeight + margin)) + overscan;
};
const end = () => {
return start.current + visibleCount();
};
const listHeight = () => {
return list.length * (itemHeight + margin);
};
const visibleData = () => {
return list.slice(start.current, Math.min(end(), list.length));
};
const updateTotalSize = useCallback(() => {
if (!itemsRef.current)
return;
const items = itemsRef.current.children;
if (!items.length)
return;
updateItemSize(positions, items, "height", margin);
}, [positions]);
const listScroll = (e) => {
const scrollTop = e.target.scrollTop;
if (scrollTop <= 0) {
e.target.scrollTop = 0;
return setStartOffset(0);
}
if (!itemEqual) {
updateTotalSize();
}
start.current = Math.floor(scrollTop / (itemHeight + margin));
setStartOffset(scrollTop - scrollTop % (itemHeight + margin));
const endIndex = end();
if (endIndex > list.length - 1) {
onScroll && onScroll();
}
};
return React__default.createElement(
View,
Object.assign({ className: classNames("nut-virtualList-box", className) }, rest, { style: {
height: containerHeight ? `${offSetSize}px` : ""
} }),
React__default.createElement(
ScrollView,
{ scrollY: true, bounces: false, type: "list", ref: scrollRef, className: "nut-virtuallist", style: {
height: `${getContainerHeight()}px`
}, onScroll: listScroll },
React__default.createElement(View, { className: "nut-virtuallist-phantom", style: { height: `${listHeight()}px` } }),
React__default.createElement(View, { className: "nut-virtuallist-container", ref: itemsRef, style: { transform: `translate3d(0, ${startOffset}px, 0)` } }, visibleData().map((data, index) => {
const dataIndex = start.current + index;
const keyVal = key && data[key] ? data[key] : dataIndex;
return React__default.createElement(View, { "data-index": `${dataIndex}`, className: "nut-virtuallist-item", key: `${keyVal}`, style: {
height: `${itemEqual ? `${itemHeight}px` : "auto"}`
} }, itemRender ? itemRender(data, dataIndex, index) : data);
}))
)
);
};
VirtualList.displayName = "NutVirtualList";
export {
VirtualList as V
};