@react-vant-next/campaign
Version:
React Mobile UI Components based on Vant UI - Next Generation
99 lines (94 loc) • 4.22 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var tslib = require('tslib');
var jsxRuntime = require('react/jsx-runtime');
var web = require('@react-spring/web');
var hooks = require('@react-vant-next/hooks');
var utils = require('@react-vant-next/utils');
var cls = require('clsx');
var React = require('react');
const [bem] = utils.createNamespace("floating-panel");
/** Check if EL is scrolling reach its bottom */
function scrollReachBottom(el) {
const scrollTop = utils.getScrollTop(el);
return scrollTop >= el.scrollHeight - utils.getVisibleHeight(el);
}
function FloatingPanel(_a) {
var { ref } = _a, props = tslib.__rest(_a, ["ref"]);
const { className, style, onHeightChange, anchors = [100] } = props;
const sortAnchors = React.useMemo(() => anchors.sort((a, b) => a - b), [anchors]);
const [minAnchor, maxAnchor] = [
sortAnchors[0],
sortAnchors[Math.max(0, sortAnchors.length - 1)],
];
const root = React.useRef(void 0);
const header = React.useRef(void 0);
const body = React.useRef(void 0);
const dragging = React.useRef(false);
const draggingStartAt = React.useRef(null);
const touch = hooks.useTouch();
const [{ visibleH }, api] = web.useSpring(() => ({
visibleH: minAnchor,
config: { tension: 300 },
onChange: () => onHeightChange === null || onHeightChange === void 0 ? void 0 : onHeightChange(visibleH.get()),
}), [minAnchor]);
React.useImperativeHandle(ref, () => ({
moveTo: height => api.start({ visibleH: height }),
}));
const onTouchStart = (event) => {
touch.start(event);
draggingStartAt.current = visibleH.get();
dragging.current = true;
};
const onTouchMove = (event) => {
const [headerEL, bodyEL] = [header.current, body.current];
touch.move(event);
if (visibleH.goal >= maxAnchor && bodyEL) {
if (touch.firstMove.current
// try going up to body top
&& ((touch.deltaY.current > 0 && utils.getScrollTop(bodyEL) > 0)
// try going down to body bottom
|| (touch.deltaY.current < 0 && !scrollReachBottom(bodyEL)))) {
dragging.current = false;
}
}
if (headerEL && headerEL.contains(event.target)) {
dragging.current = true;
}
if (!dragging.current)
return;
utils.preventDefault(event, true);
api.start({
visibleH: utils.bound(draggingStartAt.current + -touch.deltaY.current, minAnchor, maxAnchor),
});
};
const onTouchEnd = () => {
const memoDraggingStartAt = draggingStartAt.current;
dragging.current = false;
draggingStartAt.current = null;
touch.reset();
if (memoDraggingStartAt) {
const nearestAnchor = findNearestAnchor(sortAnchors, visibleH.get());
api.start({
visibleH: nearestAnchor,
from: { visibleH: visibleH.get() },
});
}
};
hooks.useEventListener("touchstart", onTouchStart, {
target: root,
passive: false,
});
hooks.useEventListener("touchmove", onTouchMove, { target: root, passive: false });
hooks.useEventListener("touchend", onTouchEnd, { target: root, passive: false });
// 使用类型断言解决 animated.div 不接受 children 属性的问题
const AnimatedDiv = web.animated.div;
return (jsxRuntime.jsxs(AnimatedDiv, { ref: root, className: cls(bem(), className), style: Object.assign({ height: maxAnchor, transform: visibleH.to(h => `translateY(calc(100% - ${h}px))`) }, style), children: [jsxRuntime.jsx("div", { ref: header, className: cls(bem("header")), children: jsxRuntime.jsx("div", { className: cls(bem("thumb")) }) }), jsxRuntime.jsx(AnimatedDiv, { ref: body, className: cls(bem("body")), children: props.children })] }));
}
function findNearestAnchor(anchors, target) {
return anchors.reduce((pre, cur) => {
return Math.abs(target - pre) < Math.abs(target - cur) ? pre : cur;
});
}
exports.default = FloatingPanel;
//# sourceMappingURL=FloatingPanel.js.map