UNPKG

@gorhom/bottom-sheet

Version:

A performant interactive bottom sheet with fully configurable options 🚀

110 lines (99 loc) • 3.46 kB
import { useMemo, useState } from 'react'; import { type SharedValue, makeMutable, useAnimatedReaction, } from 'react-native-reanimated'; import { INITIAL_CONTAINER_LAYOUT, INITIAL_LAYOUT_VALUE } from '../constants'; import type { ContainerLayoutState, LayoutState } from '../types'; const INITIAL_STATE: LayoutState = { rawContainerHeight: INITIAL_LAYOUT_VALUE, containerHeight: INITIAL_LAYOUT_VALUE, containerOffset: INITIAL_CONTAINER_LAYOUT.offset, handleHeight: INITIAL_LAYOUT_VALUE, footerHeight: INITIAL_LAYOUT_VALUE, contentHeight: INITIAL_LAYOUT_VALUE, }; /** * A custom hook that manages and animates the layout state of a container, * typically used in bottom sheet components. It calculates the effective * container height by considering top and bottom insets, and updates the * animated state in response to layout changes. The hook supports both modal * and non-modal modes, and ensures the container's animated layout state * remains in sync with the actual layout measurements. * * @param containerLayoutState - A shared value representing the current container layout state. * @param topInset - The top inset value to be subtracted from the container height. * @param bottomInset - The bottom inset value to be subtracted from the container height. * @param modal - Optional flag indicating if the layout is in modal mode. * @param shouldOverrideHandleHeight - Optional flag to override the handle height in the layout state, only when handle is set to null. * @returns An object containing the animated layout state. */ export function useAnimatedLayout( containerLayoutState: SharedValue<ContainerLayoutState> | undefined, topInset: number, bottomInset: number, modal?: boolean, shouldOverrideHandleHeight?: boolean ) { //#region variables const verticalInset = useMemo( () => topInset + bottomInset, [topInset, bottomInset] ); const initialState = useMemo(() => { const _state = { ...INITIAL_STATE }; if (containerLayoutState) { const containerLayout = containerLayoutState.get(); _state.containerHeight = modal ? containerLayout.height - verticalInset : containerLayout.height; _state.containerOffset = containerLayout.offset; } if (shouldOverrideHandleHeight) { _state.handleHeight = 0; } return _state; }, [containerLayoutState, modal, shouldOverrideHandleHeight, verticalInset]); //#endregion //#region state const [state] = useState(() => makeMutable(initialState)); //#endregion //#region effects useAnimatedReaction( () => state.value.rawContainerHeight, (result, previous) => { if (result === previous) { return; } if (result === INITIAL_LAYOUT_VALUE) { return; } state.modify(_state => { 'worklet'; _state.containerHeight = modal ? result - verticalInset : result; return _state; }); }, [state, verticalInset, modal] ); useAnimatedReaction( () => containerLayoutState?.get().height, (result, previous) => { if (!result || result === previous) { return; } if (result === INITIAL_LAYOUT_VALUE) { return; } state.modify(_state => { 'worklet'; _state.containerHeight = modal ? result - verticalInset : result; return _state; }); }, [state, verticalInset, modal] ); //#endregion return state; }