UNPKG

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

Version:
101 lines (86 loc) 3.94 kB
import {useAnimatedReaction, useSharedValue} from "react-native-reanimated" import {useBottomSheetToolboxInternalContext} from "../context" import {BottomSheetPositionBuiltInLockPriority, snapPoint} from "../utils" import {useBottomSheetPositionLock} from "./use-bottom-sheet-position-lock" const LOCK_ID = "useBottomSheetSnapWhenIdle" /** * This hook snaps the bottom sheet to the nearest snap point when * nothing else is interacting with it. It takes into account the * last known velocity. * * A snap is triggered after either of the following two conditions: * - The position off any snap-point, and has been released. * - The underlying snap-points have changed. */ export function useBottomSheetSnapWhenIdle() { const { currentPositionSharedValue, currentPositionLastVelocitySharedValue, currentPositionDestinationSharedValue, normalizedSnapPointsSharedValue, isReadySharedValue, currentPositionLockSharedValue, } = useBottomSheetToolboxInternalContext() const {canLockSharedValue, hasLockSharedValue, lock, release, setPositionAnimate, setVelocity} = useBottomSheetPositionLock(LOCK_ID, BottomSheetPositionBuiltInLockPriority.SNAP_WHEN_IDLE) const lastSnapPointIndexSharedValue = useSharedValue<number>(-1) useAnimatedReaction( () => ({ canLock: isReadySharedValue.get() && canLockSharedValue.get(), hasLock: hasLockSharedValue.get(), snapPoints: normalizedSnapPointsSharedValue.get(), }), ({canLock, hasLock, snapPoints}, lastResults) => { const canLockOrHasLock = canLock || hasLock if (!snapPoints || !canLockOrHasLock) { lastSnapPointIndexSharedValue.set(-1) return } const lastCanLockOrHasLock = lastResults?.canLock || lastResults?.hasLock const didLockStatusChange = lastCanLockOrHasLock !== canLockOrHasLock const lastSnapPoints = lastResults?.snapPoints || [] const didSnapPointsChange = snapPoints === null ? lastSnapPoints === null : lastSnapPoints.length !== snapPoints.length || lastSnapPoints.some((v, index) => v !== snapPoints[index]) // Nothing to do if (!didLockStatusChange && !didSnapPointsChange) return /** * If our snap-points change, then try to follow the snap-point we * were last at. This also supports the case where the snap-points * are updated to be equal, but then separate again. That case can * happen when the keyboard opens. */ lastSnapPointIndexSharedValue.set( lastSnapPoints.indexOf(currentPositionDestinationSharedValue.get()) ) const currentPosition = currentPositionSharedValue.get() const point = lastSnapPointIndexSharedValue.get() === -1 ? snapPoint( currentPosition, currentPositionLastVelocitySharedValue.get(), snapPoints ) : (snapPoints[lastSnapPointIndexSharedValue.get()] ?? null) if (point === null || point === currentPosition) return if (!hasLock) { lock() } setPositionAnimate(point, undefined, (finished) => { const currentPositionLock = currentPositionLockSharedValue.get() if (finished || currentPositionLock?.id !== LOCK_ID) { release() } }) setVelocity(0) }, [ canLockSharedValue, hasLockSharedValue, normalizedSnapPointsSharedValue, isReadySharedValue.get, ] ) }