UNPKG

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

Version:
155 lines (129 loc) 5.73 kB
// import { nanoid } from "nanoid/non-secure"; import * as React from "react" import {useAnimatedReaction, useSharedValue} from "react-native-reanimated" import {useBottomSheetToolboxInternalContext} from "../context" import {DEFAULT_CLOSE_SHEET_SNAP_POINT_SPRING_CONFIG} from "../utils" import {BottomSheetPositionBuiltInLockPriority, CLOSED_SNAP_POINT} from "../utils/constants" import {useBottomSheetOnSnapDetection} from "./use-bottom-sheet-on-snap-detection" import {useBottomSheetPositionLock} from "./use-bottom-sheet-position-lock" import {useWorkletUiCallback} from "./use-worklet-ui-callback" export interface BottomSheetToolboxActions { snapToIndex: (snapPointIndex: number) => void expand: () => void close: () => void getCurrentSnapIndex: () => number | null } /** * Creates a set of actions that can be used to imperatively control the bottom sheet. */ export function useBottomSheetActions(id: string) { const {currentPositionSharedValue, normalizedSnapPointsSharedValue} = useBottomSheetToolboxInternalContext() const { lock: snapLock, release: setSnapRelease, setPositionAnimate: setSnapPositionAnimate, setVelocity: setSnapVelocity, hasLockSharedValue: hasSnapLockSharedValue, } = useBottomSheetPositionLock( `useBottomSheetActions:${id}:snap`, BottomSheetPositionBuiltInLockPriority.ACTIONS_SNAP ) // We apply imperative snaps reactively, like this, so we can adjust for changes in the snap points etc. const snapDestinationIndexSharedValue = useSharedValue<number | null>(null) const lastSnapPointsSharedValue = useSharedValue<number[] | null>(null) useAnimatedReaction( () => ({ currentPosition: currentPositionSharedValue.get(), snapDestinationIndex: snapDestinationIndexSharedValue.get(), snapPoints: normalizedSnapPointsSharedValue.get(), hasSnapLock: hasSnapLockSharedValue.get(), }), ({currentPosition, snapPoints, snapDestinationIndex, hasSnapLock}, lastResult) => { if (!hasSnapLock && lastResult?.hasSnapLock === true) { snapDestinationIndexSharedValue.set(null) return } if (snapPoints === null || snapDestinationIndex === null) return const lastSnapPoints = lastSnapPointsSharedValue.get() const didSnapChange = lastSnapPoints === null || lastSnapPoints[snapDestinationIndex] !== snapPoints[snapDestinationIndex] || lastResult?.snapDestinationIndex !== snapDestinationIndex const snapPoint = snapPoints[snapDestinationIndex] ?? null if (snapPoint === null || currentPosition === snapPoint) { snapDestinationIndexSharedValue.set(null) setSnapRelease() return } if (!didSnapChange) return lastSnapPointsSharedValue.set(snapPoints) setSnapVelocity(0) setSnapPositionAnimate(snapPoint) }, [hasSnapLockSharedValue, currentPositionSharedValue, normalizedSnapPointsSharedValue] ) const snapToIndex = useWorkletUiCallback( (snapPointIndex: number) => { "worklet" const normalizedSnapPoints = normalizedSnapPointsSharedValue.get() if (!normalizedSnapPoints || normalizedSnapPoints.length === 0) { throw new Error("No snap-points to snap to.") } if (!(snapPointIndex in normalizedSnapPoints)) { throw new Error(`Invalid snap point index: ${snapPointIndex}`) } snapLock() snapDestinationIndexSharedValue.set(snapPointIndex) }, [snapLock, normalizedSnapPointsSharedValue] ) const { lock: closeLock, release: closeRelease, setPositionAnimate: setClosePositionAnimate, setVelocity: setCloseVelocity, hasLockSharedValue: hasCloseLockSharedValue, } = useBottomSheetPositionLock( `useBottomSheetActions:${id}:close`, BottomSheetPositionBuiltInLockPriority.ACTIONS_CLOSE ) const expand = useWorkletUiCallback(() => { "worklet" const normalizedSnapPoints = normalizedSnapPointsSharedValue.get() if (!normalizedSnapPoints || normalizedSnapPoints.length === 0) { throw new Error("No snap-points to snap to.") } else if (normalizedSnapPoints.length === 1 && normalizedSnapPoints[0] === 0) { throw new Error("Sheet cannot expand as only available snap-point is '0'.") } snapToIndex(normalizedSnapPoints.length - 1) }, [normalizedSnapPointsSharedValue, snapToIndex]) const close = useWorkletUiCallback(() => { "worklet" if (hasCloseLockSharedValue.get()) return closeLock() setCloseVelocity(0) setClosePositionAnimate( CLOSED_SNAP_POINT, DEFAULT_CLOSE_SHEET_SNAP_POINT_SPRING_CONFIG, closeRelease ) }, [ hasCloseLockSharedValue, closeLock, setCloseVelocity, setClosePositionAnimate, closeRelease, ]) const currentSnapRef = React.useRef<number | null>(null) useBottomSheetOnSnapDetection( React.useCallback((snap: number) => { currentSnapRef.current = snap }, []) ) const getCurrentSnapIndex = React.useCallback(() => currentSnapRef.current, []) return React.useMemo<BottomSheetToolboxActions>( () => ({snapToIndex, expand, close, getCurrentSnapIndex}), [snapToIndex, expand, close, getCurrentSnapIndex] ) }