antd-mobile
Version:
<img src="https://gw.alipayobjects.com/mdn/rms_ee68a8/afts/img/A*hjjDS5Yy-ooAAAAAAAAAAAAAARQnAQ" alt="logo" width="100%" />
169 lines (148 loc) • 4.5 kB
JavaScript
import { __awaiter } from "tslib";
import { mergeProps } from '../../utils/with-default-props';
import { animated, useSpring } from '@react-spring/web';
import { useDrag } from '@use-gesture/react';
import { getScrollParent } from '../../utils/get-scroll-parent';
import React, { useEffect, useRef, useState } from 'react';
import { supportsPassive } from '../../utils/supports-passive';
import { convertPx } from '../../utils/convert-px';
import { rubberbandIfOutOfBounds } from '../../utils/rubberband';
import { sleep } from '../../utils/sleep';
const classPrefix = `adm-pull-to-refresh`;
export const defaultProps = {
pullingText: '下拉刷新',
canReleaseText: '释放立即刷新',
refreshingText: '加载中……',
completeText: '刷新成功',
completeDelay: 500,
disabled: false,
onRefresh: () => {}
};
export const PullToRefresh = p => {
var _a, _b;
const props = mergeProps(defaultProps, p);
const headHeight = (_a = props.headHeight) !== null && _a !== void 0 ? _a : convertPx(40);
const threshold = (_b = props.threshold) !== null && _b !== void 0 ? _b : convertPx(60);
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;
}
const [, y] = state.movement;
if (state.first) {
const element = elementRef.current;
if (!element) return;
const scrollParent = getScrollParent(element);
if (!scrollParent) return;
const top = 'scrollTop' in scrollParent ? scrollParent.scrollTop : scrollParent.pageYOffset;
if (top <= 0 && y > 0) {
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: supportsPassive ? {
passive: false
} : false
});
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 React.createElement(animated.div, {
ref: elementRef,
className: classPrefix
}, React.createElement(animated.div, {
style: springStyles,
className: `${classPrefix}-head`
}, React.createElement("div", {
className: `${classPrefix}-head-content`,
style: {
height: headHeight
}
}, renderStatusText())), React.createElement("div", {
className: `${classPrefix}-content`
}, props.children));
};