UNPKG

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

Version:
117 lines (96 loc) 4.32 kB
import {type SharedValue, useDerivedValue} from "react-native-reanimated" import {useBottomSheetToolboxInternalContext, useKeyboardHeightSharedValue} from "../context" import {useFooterFreezeWhenExpanding} from "./use-footer-freeze-when-expanding" import {useLastSharedValueBeforeLock} from "./use-last-shared-value-before-lock" export interface UseFooterBottomPositionSharedValueArgs { /** * A shared value that tells the footer how it should be moved with the * sheet. By default, this is the internal children height of the sheet. */ bufferSharedValue?: SharedValue<number | null> | undefined /** * Whether the footer should be adjusted for the keyboard. */ disableKeyboardAdjust?: boolean | undefined /** * Whether the footer should freeze its position when the sheet is expanding. * This is useful when adding or removing items and is the default behavior. */ freezeWhenExpanding?: boolean | undefined } /** * A shared value used as the bottom position for a footer. */ export function useFooterBottomPositionSharedValue({ bufferSharedValue: outerBufferSharedValue, disableKeyboardAdjust = false, freezeWhenExpanding = true, }: UseFooterBottomPositionSharedValueArgs) { const { isReadySharedValue, currentPositionSharedValue, childrenHeightSharedValue, wrapperHeightSharedValue, footerHeightSharedValue, } = useBottomSheetToolboxInternalContext() const keyboardHeightSharedValue = useKeyboardHeightSharedValue() const bufferSharedValue = useDerivedValue(() => { if (outerBufferSharedValue) return outerBufferSharedValue.get() const childrenHeight = childrenHeightSharedValue.get() const wrapperHeight = wrapperHeightSharedValue.get() const footerHeight = footerHeightSharedValue.get() if (childrenHeight === null || wrapperHeight === null || footerHeight === null) { return null } return Math.min(childrenHeight, wrapperHeight - footerHeight) }, [ outerBufferSharedValue, childrenHeightSharedValue, wrapperHeightSharedValue, footerHeightSharedValue, ]) const rawBottomSharedValue = useDerivedValue(() => { const footerHeight = footerHeightSharedValue.get() const buffer = bufferSharedValue.get() if (!isReadySharedValue.get() || buffer === null || footerHeight === null) { return null } const keyboardAdjust = disableKeyboardAdjust ? 0 : keyboardHeightSharedValue.get() const unclampedClosingOffset = currentPositionSharedValue.get() - footerHeight - buffer - keyboardAdjust const closingOffset = Math.min(0, unclampedClosingOffset) const bottom = keyboardAdjust + closingOffset return bottom }, [ isReadySharedValue, currentPositionSharedValue, disableKeyboardAdjust, footerHeightSharedValue, keyboardHeightSharedValue, ]) const bottomAtTimeOfLockSharedValue = useLastSharedValueBeforeLock(rawBottomSharedValue) const freezeWhenExpandingSharedValue = useFooterFreezeWhenExpanding({ bufferSharedValue, bottomAtTimeOfLockSharedValue, }) /** * If the content-size of the sheet increases, then we actually want the * footer to stay where it is (not shift down as if it's animating back in). * * * To determine this, we snapshot the sheet position at the time of the lock * and see if we're expanding up or down. We also keep a snapshot of the * bottom right before the lock was engaged. */ const frozenBottomSharedValue = useDerivedValue(() => { const bottom = rawBottomSharedValue.get() const bottomAtTimeOfLock = bottomAtTimeOfLockSharedValue.get() const shouldFreezeBottom = freezeWhenExpanding && freezeWhenExpandingSharedValue.get() return shouldFreezeBottom ? (bottomAtTimeOfLock ?? bottom) : bottom }, [bottomAtTimeOfLockSharedValue, freezeWhenExpanding, freezeWhenExpandingSharedValue]) /** * Note: returning different SharedValue references doesn't play well with * useAnimatedStyle. Hence we always return the derived value. */ return frozenBottomSharedValue }