@attio/react-native-bottom-sheet-toolbox
Version:
91 lines (76 loc) • 3.65 kB
text/typescript
import {PixelRatio} from "react-native"
import {type SharedValue, useAnimatedReaction, useDerivedValue} from "react-native-reanimated"
import {useBottomSheetToolboxInternalContext, useKeyboardProgressSharedValue} from "../context"
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>
/**
* A shared value of the bottom position when the sheet was locked.
*/
bottomAtTimeOfLockSharedValue: SharedValue<number | null>
}
/**
* Returns a shared value that tells the footer whether it should freeze its
* position as the content is expanding.
*/
export function useFooterFreezeWhenExpanding({
bufferSharedValue,
bottomAtTimeOfLockSharedValue,
}: UseFooterBottomPositionSharedValueArgs) {
const {currentPositionDestinationSharedValue} = useBottomSheetToolboxInternalContext()
const keyboardProgressSharedValue = useKeyboardProgressSharedValue()
const lastBufferSharedValue = useLastSharedValueBeforeLock(bufferSharedValue)
const pixelRatio = PixelRatio.get()
useAnimatedReaction(
() => currentPositionDestinationSharedValue.get(),
(destination, lastDestination) => {
const lastBuffer = lastBufferSharedValue.get()
if (lastBuffer !== null) {
const destinationDiff = destination - (lastDestination ?? 0)
const destinationDiffRounded = Math.round(destinationDiff * pixelRatio) / pixelRatio
if (destinationDiffRounded < -1) {
lastBufferSharedValue.set(bufferSharedValue.get())
}
}
},
[currentPositionDestinationSharedValue]
)
const isExpandingSharedValue = useDerivedValue(() => {
const buffer = bufferSharedValue.get()
const lastBuffer = lastBufferSharedValue.get()
const delta = buffer === null || lastBuffer === null ? null : buffer - lastBuffer
return delta !== null && delta > 0
}, [lastBufferSharedValue, bufferSharedValue.get])
// This happens when opening and quickly closing.
const wasMovingWithSheetSharedValue = useDerivedValue(() => {
const bottomAtTimeOfLock = bottomAtTimeOfLockSharedValue.get()
const roundedBottom =
bottomAtTimeOfLock !== null
? Math.round(bottomAtTimeOfLock * pixelRatio) / pixelRatio
: null
return roundedBottom !== null && roundedBottom < 0
}, [bottomAtTimeOfLockSharedValue, pixelRatio])
const isKeyboardMovingSharedValue = useDerivedValue(
() => keyboardProgressSharedValue.get() !== 0 && keyboardProgressSharedValue.get() !== 1,
[keyboardProgressSharedValue]
)
/**
* 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.
*/
return useDerivedValue(() => {
const wasMovingWithSheet = wasMovingWithSheetSharedValue.get()
const isSettled = !wasMovingWithSheet && !isKeyboardMovingSharedValue.get()
const isExpanding = isExpandingSharedValue.get()
return isExpanding && isSettled
}, [])
}