UNPKG

@nutui/nutui-react

Version:

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

197 lines (196 loc) 6.72 kB
import { a as __awaiter } from "./tslib.es6.js"; import React__default, { useState, useRef, useEffect } from "react"; import classNames from "classnames"; import { useDrag } from "@use-gesture/react"; import { useSpring, animated } from "@react-spring/web"; import { Loading, More } from "@nutui/icons-react"; import { useConfig } from "./ConfigProvider.js"; import { g as getScrollParent } from "./get-scroll-parent.js"; import { p as passiveSupported } from "./supports-passive.js"; import { C as ComponentDefaults } from "./typings.js"; function bound(position, min, max) { let ret = position; { ret = Math.max(position, min); } { ret = Math.min(ret, max); } return ret; } function rubberband(distance, dimension, constant) { return distance * dimension * constant / (dimension + constant * distance); } function rubberbandIfOutOfBounds(position, min, max, dimension, constant = 0.15) { if (constant === 0) return bound(position, min, max); if (position < min) return -rubberband(min - position, dimension, constant) + min; if (position > max) return +rubberband(position - max, dimension, constant) + max; return position; } const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time)); const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { type: "default", pullingText: "", canReleaseText: "", refreshingText: "", completeText: "", completeDelay: 500, disabled: false, headHeight: 50, threshold: 60, onRefresh: () => { } }); const PullToRefresh = (p) => { const classPrefix = "nut-pulltorefresh"; const { locale } = useConfig(); const props = Object.assign(Object.assign(Object.assign({}, defaultProps), p), { pullingText: p.pullingText || locale.pullToRefresh.pullingText, canReleaseText: p.canReleaseText || locale.pullToRefresh.canReleaseText, refreshingText: p.refreshingText || locale.pullToRefresh.refreshingText, completeText: p.completeText || locale.pullToRefresh.completeText }); const classes = classNames(classPrefix, props.className, `${classPrefix}-${props.type}`); const headHeight = props.headHeight; const threshold = props.threshold; const [status, setStatus] = useState("pulling"); const [springStyles, api] = useSpring(() => ({ from: { height: 0 }, config: { tension: 300, friction: 30, clamp: true } })); const elementRef = useRef(null); const pullingRef = useRef(false); useEffect(() => { var _a; (_a = elementRef.current) === null || _a === void 0 ? void 0 : _a.addEventListener("touchmove", () => { }); }, []); function doRefresh() { return __awaiter(this, void 0, void 0, function* () { api.start({ height: headHeight }); setStatus("refreshing"); try { yield props.onRefresh(); setStatus("complete"); } catch (e) { api.start({ to: (next) => __awaiter(this, void 0, void 0, function* () { yield next({ height: 0 }); setStatus("pulling"); }) }); throw e; } if (props.completeDelay > 0) { yield sleep(props.completeDelay); } api.start({ to: (next) => __awaiter(this, void 0, void 0, function* () { yield next({ height: 0 }); setStatus("pulling"); }) }); }); } useDrag((state) => { if (status === "refreshing" || status === "complete") return; const { event } = state; if (state.last) { pullingRef.current = false; if (status === "canRelease") { doRefresh(); } else { api.start({ height: 0 }); } return; } function getScrollTop(element) { return "scrollTop" in element ? element.scrollTop : element.scrollY; } const [, y] = state.movement; if (state.first && y > 0) { const target = state.event.target; if (!target || !(target instanceof Element)) return; let scrollParent = getScrollParent(target); while (true) { if (!scrollParent) return; const scrollTop = getScrollTop(scrollParent); if (scrollTop > 0) { return; } if (scrollParent instanceof Window) { break; } scrollParent = getScrollParent(scrollParent.parentNode); } pullingRef.current = true; } if (!pullingRef.current) return; if (event.cancelable) { event.preventDefault(); } event.stopPropagation(); const height = Math.max(rubberbandIfOutOfBounds(y, 0, 0, headHeight * 5, 0.5), 0); api.start({ height }); setStatus(height > threshold ? "canRelease" : "pulling"); }, { pointer: { touch: true }, axis: "y", target: elementRef, enabled: !props.disabled, eventOptions: passiveSupported ? { passive: false } : false }); const renderIcons = (status2) => { return React__default.createElement( React__default.Fragment, null, React__default.createElement( "i", { className: `${classPrefix}-head-content-icons` }, (status2 === "pulling" || status2 === "complete") && React__default.createElement(Loading, null), (status2 === "canRelease" || status2 === "refreshing") && React__default.createElement(More, null) ) ); }; const renderStatusIcon = () => { var _a; if (props.renderIcon) { return (_a = props.renderIcon) === null || _a === void 0 ? void 0 : _a.call(props, status); } return renderIcons(status); }; const renderStatusText = () => { var _a; if (props.renderText) { return (_a = props.renderText) === null || _a === void 0 ? void 0 : _a.call(props, status); } if (status === "pulling") return props.pullingText; if (status === "canRelease") return props.canReleaseText; if (status === "refreshing") return props.refreshingText; if (status === "complete") return props.completeText; return ""; }; return React__default.createElement( animated.div, { ref: elementRef, className: classes, style: props.style }, React__default.createElement( animated.div, { style: springStyles, className: `${classPrefix}-head` }, React__default.createElement( "div", { className: `${classPrefix}-head-content`, style: { height: headHeight } }, React__default.createElement("div", null, renderStatusIcon()), React__default.createElement("div", null, renderStatusText()) ) ), React__default.createElement("div", { className: `${classPrefix}-content` }, props.children) ); }; PullToRefresh.displayName = "NutPullToRefresh"; export { PullToRefresh as default };