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