@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
148 lines (147 loc) • 6.48 kB
JavaScript
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
};