UNPKG

@attio/react-native-bottom-sheet-toolbox

Version:
108 lines (105 loc) 4.42 kB
"use strict"; import * as React from "react"; import { Dimensions } from "react-native"; import { Gesture } from "react-native-gesture-handler"; import { runOnJS, useAnimatedReaction, useSharedValue } from "react-native-reanimated"; import { useBottomSheetToolboxInternalContext } from "../context"; import { BottomSheetPositionBuiltInLockPriority, friction } from "../utils"; import { useBottomSheetPositionLock } from "./use-bottom-sheet-position-lock"; const LOCK_ID_NAMESPACE = "useBottomSheetDragGesture"; // Gesture velocity is calculated between gesture updates. // If the user does not move their finger, the gesture doesn't see update // and so the velocity is miss-leading (i.e. not 0) // We detect this by checking the time between the last gesture update. const SUPPRESS_VELOCITY_TIME_MS = 150; export function useBottomSheetDragGesture({ id, disabled = false, clamped = false, frozenSharedValue }) { const { currentPositionSharedValue, currentPositionLastVelocitySharedValue, wrapperHeightSharedValue, normalizedSnapPointsSharedValue } = useBottomSheetToolboxInternalContext(); const { lock, release, setPosition, setVelocity, canLockSharedValue, hasLockSharedValue } = useBottomSheetPositionLock(`${LOCK_ID_NAMESPACE}:${id}`, BottomSheetPositionBuiltInLockPriority.GESTURE); const lastLockRef = React.useRef(id); React.useEffect(() => { if (id !== lastLockRef.current) throw new Error("Lock id can't be changed."); }, [id]); const startPositionSharedValue = useSharedValue(0); const lastUpdateTime = useSharedValue(0); const [enabled, setEnabled] = React.useState(false); useAnimatedReaction(() => canLockSharedValue.get() || hasLockSharedValue.get(), (lockable, lastLockable) => { if (lockable === lastLockable) return; runOnJS(setEnabled)(lockable); }, [canLockSharedValue, hasLockSharedValue.get]); const [windowHeight, setWindowHeight] = React.useState(Dimensions.get("window").height); React.useEffect(() => { const handle = Dimensions.addEventListener("change", ({ window }) => { setWindowHeight(window.height); }); return () => handle.remove(); }, []); const frozenOriginOffsetYSharedValue = useSharedValue(0); return Gesture.Pan().onBegin(() => { if (!canLockSharedValue.get()) return; startPositionSharedValue.set(currentPositionSharedValue.get()); frozenOriginOffsetYSharedValue.set(0); }).onUpdate(evt => { const normalizedSnapPoints = normalizedSnapPointsSharedValue.get(); const wrapperHeight = wrapperHeightSharedValue.get(); const lockable = canLockSharedValue.get() || hasLockSharedValue.get(); if (!lockable || !normalizedSnapPoints || !wrapperHeight) return; // We want to lock onUpdate, rather than onBegin, so nested // touches do no unnecessarily lock the position. if (!hasLockSharedValue.get()) { lock(); } if (frozenSharedValue?.get()) { frozenOriginOffsetYSharedValue.set(evt.translationY); return; } setVelocity(evt.velocityY); const rawPosition = startPositionSharedValue.get() - evt.translationY + frozenOriginOffsetYSharedValue.get(); const maxSnapPoint = Math.max(...normalizedSnapPoints); const overshoot = Math.max(0, rawPosition - maxSnapPoint); if (clamped) { const clampedPosition = Math.max(Math.min(rawPosition, maxSnapPoint), 0); setPosition(clampedPosition); } else if (overshoot > 0) { const topInset = Math.max(1, windowHeight - wrapperHeight); const frictionRatio = friction(overshoot / topInset); const frictionPosition = maxSnapPoint + topInset * frictionRatio; const finalPosition = Math.min(frictionPosition, wrapperHeight + topInset); setPosition(finalPosition); } else { setPosition(rawPosition); } lastUpdateTime.set(Date.now()); }).onFinalize(evt => { if (!hasLockSharedValue.get()) return; if (frozenSharedValue?.get()) { setVelocity(0); } else { const currentTime = Date.now(); const velocity = -evt.velocityY; const timeSinceLastUpdate = currentTime - lastUpdateTime.get(); currentPositionLastVelocitySharedValue.set(timeSinceLastUpdate > SUPPRESS_VELOCITY_TIME_MS ? 0 : velocity); setVelocity(velocity); } release(); }).enabled(enabled && !disabled); } //# sourceMappingURL=use-bottom-sheet-drag-gesture.js.map