UNPKG

@nutui/nutui-react

Version:

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

148 lines (147 loc) 6.48 kB
import { _ as __rest, a as __awaiter } from "./tslib.es6.js"; import React__default, { useState, useRef, useEffect } from "react"; import classNames from "classnames"; import { useConfig } from "./ConfigProvider.js"; import { C as ComponentDefaults } from "./typings.js"; const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { type: "default", hasMore: true, threshold: 200, target: "", capture: false, pullRefresh: false }); const classPrefix = `nut-infiniteloading`; const InfiniteLoading = (props) => { const { locale } = useConfig(); const _a = Object.assign(Object.assign({}, defaultProps), props), { children, type, hasMore, threshold, target, capture, pullRefresh, pullingText, loadingText, loadMoreText, className, onRefresh, onLoadMore, onScroll } = _a, restProps = __rest(_a, ["children", "type", "hasMore", "threshold", "target", "capture", "pullRefresh", "pullingText", "loadingText", "loadMoreText", "className", "onRefresh", "onLoadMore", "onScroll"]); const [isInfiniting, setIsInfiniting] = useState(false); const scroller = useRef(null); const refreshTop = useRef(null); const scrollEl = useRef(null); const isTouching = useRef(false); const beforeScrollTop = useRef(0); const refreshMaxH = useRef(0); const y = useRef(0); const distance = useRef(0); const classes = classNames(classPrefix, className, `${classPrefix}-${type}`); useEffect(() => { var _a2; if (target && document.getElementById(target)) { scrollEl.current = document.getElementById(target); } else { scrollEl.current = window; } (_a2 = scrollEl.current) === null || _a2 === void 0 ? void 0 : _a2.addEventListener("scroll", handleScroll, capture); return () => { var _a3; (_a3 = scrollEl.current) === null || _a3 === void 0 ? void 0 : _a3.removeEventListener("scroll", handleScroll, capture); }; }, [hasMore, isInfiniting, onLoadMore]); useEffect(() => { const element = scroller.current; element.addEventListener("touchmove", touchMove, { passive: false }); return () => { element.removeEventListener("touchmove", touchMove, { passive: false }); }; }, []); const getStyle = () => { return { height: distance.current < 0 ? `0px` : `${distance.current}px`, transition: isTouching.current ? `height 0s cubic-bezier(0.25,0.1,0.25,1)` : `height 0.2s cubic-bezier(0.25,0.1,0.25,1)` }; }; const handleScroll = () => __awaiter(void 0, void 0, void 0, function* () { if (!isScrollAtBottom() || !hasMore || isInfiniting) { return; } setIsInfiniting(true); yield onLoadMore === null || onLoadMore === void 0 ? void 0 : onLoadMore(); infiniteDone(); }); const infiniteDone = () => { setIsInfiniting(false); }; const getRefreshTop = () => { return refreshTop.current; }; const refreshDone = () => { distance.current = 0; getRefreshTop().style.height = `${distance.current}px`; isTouching.current = false; }; const touchStart = (event) => { if (beforeScrollTop.current === 0 && !isTouching.current && pullRefresh) { y.current = event.touches[0].pageY; isTouching.current = true; const childHeight = getRefreshTop().firstElementChild.offsetHeight; refreshMaxH.current = Math.floor(childHeight * 1 + 10); } }; const touchMove = (event) => { distance.current = event.touches[0].pageY - y.current; if (distance.current > 0 && isTouching.current) { event.preventDefault(); if (distance.current >= refreshMaxH.current) { distance.current = refreshMaxH.current; getRefreshTop().style.height = `${distance.current}px`; } else { getRefreshTop().style.height = `${distance.current}px`; } } else { distance.current = 0; getRefreshTop().style.height = `${distance.current}px`; isTouching.current = false; } }; const touchEnd = () => __awaiter(void 0, void 0, void 0, function* () { if (distance.current < refreshMaxH.current) { distance.current = 0; getRefreshTop().style.height = `${distance.current}px`; isTouching.current = false; } else { yield onRefresh === null || onRefresh === void 0 ? void 0 : onRefresh(); refreshDone(); } }); const getWindowScrollTop = () => { return window.scrollY !== void 0 ? window.scrollY : (document.documentElement || document.body.parentNode || document.body).scrollTop; }; const calculateTopPosition = (el) => { return !el ? 0 : el.offsetTop + calculateTopPosition(el.offsetParent); }; const isScrollAtBottom = () => { let offsetDistance = 0; let resScrollTop = 0; let direction = "down"; const windowScrollTop = getWindowScrollTop(); if (!target || !document.getElementById(target)) { if (scroller.current) { offsetDistance = calculateTopPosition(scroller.current) + scroller.current.offsetHeight - windowScrollTop - window.innerHeight; } resScrollTop = windowScrollTop; } else { const { scrollHeight, clientHeight, scrollTop } = scrollEl.current; offsetDistance = scrollHeight - clientHeight - scrollTop; resScrollTop = scrollTop; } if (beforeScrollTop.current > resScrollTop) { direction = "up"; } else { direction = "down"; } beforeScrollTop.current = resScrollTop; onScroll && onScroll(resScrollTop); return offsetDistance <= threshold && direction === "down"; }; return React__default.createElement( "div", Object.assign({ className: classes, ref: scroller, onTouchStart: touchStart, onTouchEnd: touchEnd }, restProps), React__default.createElement( "div", { className: "nut-infinite-top", ref: refreshTop, style: getStyle() }, React__default.createElement("div", { className: "nut-infinite-top-tips" }, pullingText || locale.infiniteloading.pullRefreshText) ), React__default.createElement("div", { className: "nut-infinite-container" }, children), React__default.createElement("div", { className: "nut-infinite-bottom" }, isInfiniting ? React__default.createElement("div", { className: "nut-infinite-bottom-tips" }, loadingText || locale.infiniteloading.loadText) : !hasMore && React__default.createElement("div", { className: "nut-infinite-bottom-tips" }, loadMoreText || locale.infiniteloading.loadMoreText)) ); }; InfiniteLoading.displayName = "NutInfiniteLoading"; export { InfiniteLoading as default };