UNPKG

@react-vant-next/campaign

Version:

React Mobile UI Components based on Vant UI - Next Generation

95 lines (92 loc) 4.12 kB
import { __rest } from 'tslib'; import { jsxs, jsx } from 'react/jsx-runtime'; import { useSpring, animated } from '@react-spring/web'; import { useTouch, useEventListener } from '@react-vant-next/hooks'; import { createNamespace, getScrollTop, preventDefault, bound, getVisibleHeight } from '@react-vant-next/utils'; import cls from 'clsx'; import { useMemo, useRef, useImperativeHandle } from 'react'; const [bem] = createNamespace("floating-panel"); /** Check if EL is scrolling reach its bottom */ function scrollReachBottom(el) { const scrollTop = getScrollTop(el); return scrollTop >= el.scrollHeight - getVisibleHeight(el); } function FloatingPanel(_a) { var { ref } = _a, props = __rest(_a, ["ref"]); const { className, style, onHeightChange, anchors = [100] } = props; const sortAnchors = useMemo(() => anchors.sort((a, b) => a - b), [anchors]); const [minAnchor, maxAnchor] = [ sortAnchors[0], sortAnchors[Math.max(0, sortAnchors.length - 1)], ]; const root = useRef(void 0); const header = useRef(void 0); const body = useRef(void 0); const dragging = useRef(false); const draggingStartAt = useRef(null); const touch = useTouch(); const [{ visibleH }, api] = useSpring(() => ({ visibleH: minAnchor, config: { tension: 300 }, onChange: () => onHeightChange === null || onHeightChange === void 0 ? void 0 : onHeightChange(visibleH.get()), }), [minAnchor]); 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 && 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; preventDefault(event, true); api.start({ visibleH: 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() }, }); } }; useEventListener("touchstart", onTouchStart, { target: root, passive: false, }); useEventListener("touchmove", onTouchMove, { target: root, passive: false }); useEventListener("touchend", onTouchEnd, { target: root, passive: false }); // 使用类型断言解决 animated.div 不接受 children 属性的问题 const AnimatedDiv = animated.div; return (jsxs(AnimatedDiv, { ref: root, className: cls(bem(), className), style: Object.assign({ height: maxAnchor, transform: visibleH.to(h => `translateY(calc(100% - ${h}px))`) }, style), children: [jsx("div", { ref: header, className: cls(bem("header")), children: jsx("div", { className: cls(bem("thumb")) }) }), 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; }); } export { FloatingPanel as default }; //# sourceMappingURL=FloatingPanel.js.map