diff --git a/example/src/screens/integrations/MapExample.tsx b/example/src/screens/integrations/MapExample.tsx index 9e7d32380..ae1a72030 100644 --- a/example/src/screens/integrations/MapExample.tsx +++ b/example/src/screens/integrations/MapExample.tsx @@ -55,7 +55,7 @@ const MapExample = () => { () => [ bottomSafeArea === 0 ? SEARCH_HANDLE_HEIGHT / 2 : bottomSafeArea, LOCATION_DETAILS_HEIGHT + bottomSafeArea, - SCREEN_HEIGHT, + '100%', ], [bottomSafeArea] ); @@ -180,7 +180,7 @@ const MapExample = () => { snapPoints={poiListSnapPoints} handleHeight={SEARCH_HANDLE_HEIGHT} topInset={headerHeight} - dismissOnPanDown={false} + enableDismissOnClose={false} keyboardBehavior="extend" animatedPosition={animatedPOIListPosition} animatedIndex={animatedPOIListIndex} diff --git a/src/components/bottomSheet/BottomSheet.tsx b/src/components/bottomSheet/BottomSheet.tsx index b5333ab74..dd114d034 100644 --- a/src/components/bottomSheet/BottomSheet.tsx +++ b/src/components/bottomSheet/BottomSheet.tsx @@ -127,6 +127,9 @@ const BottomSheetComponent = forwardRef( onChange: _providedOnChange, onAnimate: _providedOnAnimate, + // private + $modal = false, + // components handleComponent, backdropComponent, @@ -136,16 +139,33 @@ const BottomSheetComponent = forwardRef( //#endregion //#region layout variables - const animatedContainerHeight = useReactiveSharedValue( + /** + * This variable is consider an internal variable, + * that will be used conditionally in `animatedContainerHeight` + */ + const _animatedContainerHeight = useReactiveSharedValue( _providedContainerHeight ?? INITIAL_CONTAINER_HEIGHT ); + /** + * This is a conditional variable, where if the `BottomSheet` is used + * in a modal, then it will subset vertical insets (top+bottom) from + * provided container height. + */ + const animatedContainerHeight = useDerivedValue(() => { + const verticalInset = topInset + bottomInset; + return $modal + ? _animatedContainerHeight.value - verticalInset + : _animatedContainerHeight.value; + }); const animatedHandleHeight = useReactiveSharedValue( _providedHandleHeight ?? INITIAL_HANDLE_HEIGHT ); const animatedSnapPoints = useNormalizedSnapPoints( _providedSnapPoints, animatedContainerHeight, - topInset + topInset, + bottomInset, + $modal ); const animatedLastSnapPoint = useDerivedValue( () => animatedSnapPoints.value[animatedSnapPoints.value.length - 1] @@ -721,7 +741,7 @@ const BottomSheetComponent = forwardRef( const containerAnimatedStyle = useAnimatedStyle(() => ({ transform: [ { - translateY: Math.max(animatedPosition.value, topInset), + translateY: animatedPosition.value, }, ], })); @@ -911,7 +931,7 @@ const BottomSheetComponent = forwardRef( () => animatedPosition.value, _animatedPosition => { if (_providedAnimatedPosition) { - _providedAnimatedPosition.value = _animatedPosition; + _providedAnimatedPosition.value = _animatedPosition + topInset; } } ); @@ -990,7 +1010,8 @@ const BottomSheetComponent = forwardRef( /> diff --git a/src/components/bottomSheet/types.d.ts b/src/components/bottomSheet/types.d.ts index 3e2b86631..25979bcec 100644 --- a/src/components/bottomSheet/types.d.ts +++ b/src/components/bottomSheet/types.d.ts @@ -207,6 +207,14 @@ export interface BottomSheetProps */ children: (() => React.ReactNode) | React.ReactNode[] | React.ReactNode; //#endregion + + //#region private + /** + * An indicator whether if the sheet running in a modal. + * @type boolean + */ + $modal?: boolean; + //#endregion } export interface BottomSheetAnimationConfigs { diff --git a/src/components/bottomSheetContainer/BottomSheetContainer.tsx b/src/components/bottomSheetContainer/BottomSheetContainer.tsx index 1a55677dc..95daf1707 100644 --- a/src/components/bottomSheetContainer/BottomSheetContainer.tsx +++ b/src/components/bottomSheetContainer/BottomSheetContainer.tsx @@ -1,7 +1,6 @@ -import React, { memo, useMemo } from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import { LayoutChangeEvent, View } from 'react-native'; import { print } from '../../utilities'; -import { INITIAL_CONTAINER_HEIGHT } from '../bottomSheet/constants'; import { styles } from './styles'; import type { BottomSheetContainerProps } from './types'; @@ -9,6 +8,7 @@ function BottomSheetContainerComponent({ containerHeight, topInset = 0, bottomInset = 0, + shouldCalculateHeight = true, children, }: BottomSheetContainerProps) { //#region styles @@ -25,28 +25,24 @@ function BottomSheetContainerComponent({ //#endregion //#region callbacks - const getHandleContainerLayout = useMemo( - () => - containerHeight.value === INITIAL_CONTAINER_HEIGHT - ? function handleContainerLayout({ - nativeEvent: { - layout: { height }, - }, - }: LayoutChangeEvent) { - if (height === containerHeight.value) { - return; - } - containerHeight.value = height; - print({ - component: BottomSheetContainer.displayName, - method: 'handleContainerLayout', - params: { - height, - }, - }); - } - : undefined, - + const handleContainerLayout = useCallback( + function handleContainerLayout({ + nativeEvent: { + layout: { height }, + }, + }: LayoutChangeEvent) { + if (height === containerHeight.value) { + return; + } + containerHeight.value = height; + print({ + component: BottomSheetContainer.displayName, + method: 'handleContainerLayout', + params: { + height, + }, + }); + }, [containerHeight] ); //#endregion @@ -55,7 +51,7 @@ function BottomSheetContainerComponent({ return ( diff --git a/src/components/bottomSheetContainer/types.d.ts b/src/components/bottomSheetContainer/types.d.ts index 313f6fb42..403151d9d 100644 --- a/src/components/bottomSheetContainer/types.d.ts +++ b/src/components/bottomSheetContainer/types.d.ts @@ -5,5 +5,6 @@ export interface BottomSheetContainerProps { containerHeight: Animated.SharedValue; topInset?: number; bottomInset?: number; + shouldCalculateHeight?: boolean; children: ReactNode; } diff --git a/src/components/bottomSheetDebugView/BottomSheetDebugView.tsx b/src/components/bottomSheetDebugView/BottomSheetDebugView.tsx index 54bc366e6..8919606cb 100644 --- a/src/components/bottomSheetDebugView/BottomSheetDebugView.tsx +++ b/src/components/bottomSheetDebugView/BottomSheetDebugView.tsx @@ -5,7 +5,7 @@ import ReText from './ReText'; import { styles } from './styles'; interface BottomSheetDebugViewProps { - values: Record>; + values: Record | number>; } const BottomSheetDebugView = ({ values }: BottomSheetDebugViewProps) => { diff --git a/src/components/bottomSheetDebugView/ReText.tsx b/src/components/bottomSheetDebugView/ReText.tsx index a420657c2..9d7b1cdb7 100644 --- a/src/components/bottomSheetDebugView/ReText.tsx +++ b/src/components/bottomSheetDebugView/ReText.tsx @@ -1,10 +1,13 @@ import React from 'react'; import { TextProps as RNTextProps, TextInput } from 'react-native'; -import Animated, { useAnimatedProps } from 'react-native-reanimated'; +import Animated, { + useAnimatedProps, + useDerivedValue, +} from 'react-native-reanimated'; interface TextProps { text: string; - value: Animated.SharedValue; + value: Animated.SharedValue | number; style?: Animated.AnimateProps['style']; } @@ -12,24 +15,23 @@ const AnimatedTextInput = Animated.createAnimatedComponent(TextInput); const ReText = (props: TextProps) => { const { text, value: _providedValue, style } = { style: {}, ...props }; + const providedValue = useDerivedValue(() => + typeof _providedValue === 'number' + ? _providedValue + : typeof _providedValue.value === 'number' + ? _providedValue.value.toFixed(2) + : _providedValue.value + ); const animatedProps = useAnimatedProps(() => { return { - text: `${text}: ${ - typeof _providedValue.value === 'number' - ? _providedValue.value.toFixed(2) - : _providedValue.value - }`, + text: `${text}: ${providedValue.value}`, }; - }, [_providedValue.value]); + }, [providedValue]); return ( ) : null; diff --git a/src/hooks/useNormalizedSnapPoints.ts b/src/hooks/useNormalizedSnapPoints.ts index 7f480624c..8c7d4343a 100644 --- a/src/hooks/useNormalizedSnapPoints.ts +++ b/src/hooks/useNormalizedSnapPoints.ts @@ -12,12 +12,16 @@ import { * @param providedSnapPoints provided snap points. * @param containerHeight BottomSheetContainer height. * @param topInset top inset. + * @param bottomInset bottom inset. + * @param $modal is sheet in a modal. * @returns {Animated.SharedValue} */ export const useNormalizedSnapPoints = ( providedSnapPoints: BottomSheetProps['snapPoints'], containerHeight: Animated.SharedValue, - topInset: number + topInset: number, + bottomInset: number, + $modal: boolean ) => { const normalizedSnapPoints = useDerivedValue(() => providedSnapPoints.map(snapPoint => { @@ -25,7 +29,13 @@ export const useNormalizedSnapPoints = ( return INITIAL_SNAP_POINT; } - return normalizeSnapPoint(snapPoint, containerHeight.value, topInset); + return normalizeSnapPoint( + snapPoint, + containerHeight.value, + topInset, + bottomInset, + $modal + ); }) ); diff --git a/src/utilities/normalizeSnapPoint.ts b/src/utilities/normalizeSnapPoint.ts index f61ecf477..ead8c0398 100644 --- a/src/utilities/normalizeSnapPoint.ts +++ b/src/utilities/normalizeSnapPoint.ts @@ -4,10 +4,11 @@ export const normalizeSnapPoint = ( snapPoint: number | string, containerHeight: number, - topInset: number + topInset: number, + _bottomInset: number, + $modal: boolean ) => { 'worklet'; - let normalizedSnapPoint = snapPoint; // percentage snap point @@ -15,6 +16,5 @@ export const normalizeSnapPoint = ( normalizedSnapPoint = (Number(normalizedSnapPoint.split('%')[0]) * containerHeight) / 100; } - - return Math.max(topInset, containerHeight - normalizedSnapPoint); + return Math.max($modal ? 0 : topInset, containerHeight - normalizedSnapPoint); };