UNPKG

@nutui/nutui-react-taro

Version:

京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序

148 lines (147 loc) 5.75 kB
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 };