From 84307fc2c5a97789240028b7557d0f757de052fc Mon Sep 17 00:00:00 2001 From: Or Antonio Orsatti Date: Wed, 14 Jun 2023 18:47:00 +0300 Subject: [PATCH 1/5] feat: add dynamic height scrollable hook controller --- .../useMaxHeightScrollableBottomSheet.ts | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/hooks/useMaxHeightScrollableBottomSheet.ts diff --git a/src/hooks/useMaxHeightScrollableBottomSheet.ts b/src/hooks/useMaxHeightScrollableBottomSheet.ts new file mode 100644 index 000000000..c068090b1 --- /dev/null +++ b/src/hooks/useMaxHeightScrollableBottomSheet.ts @@ -0,0 +1,53 @@ +import { useWindowDimensions } from 'react-native'; +import { useDerivedValue, useAnimatedStyle } from 'react-native-reanimated'; +import { useBottomSheetDynamicSnapPoints } from './useBottomSheetDynamicSnapPoints'; +type CSSHeight = `${number}%` | number; + +export const useMaxHeightScrollableBottomSheet = ( + maxHeight?: CSSHeight | false +) => { + const deviceHeight = useWindowDimensions().height; + const { + animatedHandleHeight, + animatedSnapPoints, + animatedContentHeight, + handleContentLayout, + } = useBottomSheetDynamicSnapPoints(['CONTENT_HEIGHT']); + + const derivedSnapPoints = useDerivedValue(() => { + // we get the dynamic size from the animatedSnapPoints + const dynamicHeight = animatedSnapPoints.value[0]; + // if no fillHeight is provided, we use the default height (50%) + const fixedHeight = maxHeight === false ? undefined : maxHeight ?? '50%'; + if (!fixedHeight) return [dynamicHeight]; + // we calculate the fillHeight based on the device height + const paredFixedHeight = + typeof fixedHeight === 'number' + ? fixedHeight + : (parseInt(fixedHeight.replace('%', ''), 10) / 100) * deviceHeight; + + return typeof dynamicHeight === 'number' && + dynamicHeight <= paredFixedHeight + ? [dynamicHeight] + : [paredFixedHeight]; + }, [deviceHeight, maxHeight]); + + // changing the height of the inner scroll view + // based on the snap point calculated above + const innerScrollViewAnimatedStyles = useAnimatedStyle(() => { + // we do not set maxHeight until the hook is ready + // due to performance issues + if (animatedHandleHeight.value === -999) return {}; + return { + maxHeight: derivedSnapPoints.value[0], + }; + }, []); + + return { + animatedHandleHeight, + animatedContentHeight, + handleContentLayout, + animatedSnapPoints: derivedSnapPoints, + innerScrollViewAnimatedStyles, + }; +}; From 71dad11aef398b64ebae3bd9c9b73d77c72753a0 Mon Sep 17 00:00:00 2001 From: Or Antonio Orsatti Date: Wed, 14 Jun 2023 18:47:30 +0300 Subject: [PATCH 2/5] feat: add hook to indexes --- src/hooks/index.ts | 1 + src/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 52e9ec7fd..19cc6b317 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -22,3 +22,4 @@ export { useNormalizedSnapPoints } from './useNormalizedSnapPoints'; export { useReactiveSharedValue } from './useReactiveSharedValue'; export { useBottomSheetDynamicSnapPoints } from './useBottomSheetDynamicSnapPoints'; export { useBottomSheetGestureHandlers } from './useBottomSheetGestureHandlers'; +export { useMaxHeightScrollableBottomSheet } from './useMaxHeightScrollableBottomSheet'; diff --git a/src/index.ts b/src/index.ts index 68bb3abf6..083431b32 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,7 @@ export { useBottomSheetTimingConfigs } from './hooks/useBottomSheetTimingConfigs export { useBottomSheetInternal } from './hooks/useBottomSheetInternal'; export { useBottomSheetModalInternal } from './hooks/useBottomSheetModalInternal'; export { useBottomSheetDynamicSnapPoints } from './hooks/useBottomSheetDynamicSnapPoints'; +export { useMaxHeightScrollableBottomSheet } from './hooks/useMaxHeightScrollableBottomSheet'; export { useScrollEventsHandlersDefault } from './hooks/useScrollEventsHandlersDefault'; export { useGestureEventsHandlersDefault } from './hooks/useGestureEventsHandlersDefault'; export { useBottomSheetGestureHandlers } from './hooks/useBottomSheetGestureHandlers'; From abe06981eda713f4a7d4431acad0623f5e80016d Mon Sep 17 00:00:00 2001 From: Or Antonio Orsatti Date: Wed, 14 Jun 2023 19:11:50 +0300 Subject: [PATCH 3/5] feat: add example --- .../ScrollableDynamicHeightExample.tsx | 135 ++++++++++++++++++ example/app/src/screens/index.ts | 6 + 2 files changed, 141 insertions(+) create mode 100644 example/app/src/screens/advanced/ScrollableDynamicHeightExample.tsx diff --git a/example/app/src/screens/advanced/ScrollableDynamicHeightExample.tsx b/example/app/src/screens/advanced/ScrollableDynamicHeightExample.tsx new file mode 100644 index 000000000..e13fde779 --- /dev/null +++ b/example/app/src/screens/advanced/ScrollableDynamicHeightExample.tsx @@ -0,0 +1,135 @@ +import React, { useCallback, useRef, useState } from 'react'; +import { View, StyleSheet, Text } from 'react-native'; +import BottomSheet, { + BottomSheetScrollView, + useMaxHeightScrollableBottomSheet, +} from '@gorhom/bottom-sheet'; +import { Button } from '../../components/button'; +type CSSHeight = `${number}%` | number; + +const DynamicSnapPointExample = () => { + // state + const [count, setCount] = useState(3); + const [maxHeight, setMaxHeight] = useState(); + // hooks + const bottomSheetRef = useRef(null); + const { + animatedHandleHeight, + animatedSnapPoints, + animatedContentHeight, + handleContentLayout, + innerScrollViewAnimatedStyles, + } = useMaxHeightScrollableBottomSheet(maxHeight); + // callbacks + + const handleExpandPress = useCallback(() => { + bottomSheetRef.current?.expand(); + }, []); + const handleClosePress = useCallback(() => { + bottomSheetRef.current?.close(); + }, []); + + const changeItemsCount = useCallback((direction: 1 | -1) => { + setCount(state => state + direction); + }, []); + + // styles + + // renders + return ( + +