diff --git a/README.md b/README.md index 98154095f..da91e28f6 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,12 @@ To start the sheet closed and snap to initial index when it's mounted. > `required:` NO | `type:` boolean | `default:` false +#### `handleHeight` + +Handle height to help adjust snap points. + +> `required:` NO | `type:` number | `default:` undefined + #### `animationDuration` Snapping animation duration. diff --git a/example/src/App.tsx b/example/src/App.tsx index c4ae9d976..16465daf1 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -115,6 +115,15 @@ const App = () => { require('./screens/advanced/MapExample').default } /> + + require('./screens/advanced/DynamicSnapPointExample').default + } + /> diff --git a/example/src/components/handle/Handle.tsx b/example/src/components/handle/Handle.tsx index 623f486c2..42432de01 100644 --- a/example/src/components/handle/Handle.tsx +++ b/example/src/components/handle/Handle.tsx @@ -10,21 +10,33 @@ interface HandleProps extends BottomSheetHandleProps { const Handle: React.FC = ({ style, animatedPositionIndex }) => { //#region animations - const borderTopRadius = interpolate(animatedPositionIndex, { - inputRange: [1, 2], - outputRange: [20, 0], - extrapolate: Extrapolate.CLAMP, - }); - const indicatorTransformOriginY = interpolate(animatedPositionIndex, { - inputRange: [0, 1, 2], - outputRange: [-1, 0, 1], - extrapolate: Extrapolate.CLAMP, - }); - const leftIndicatorRotate = interpolate(animatedPositionIndex, { - inputRange: [0, 1, 2], - outputRange: [toRad(-30), 0, toRad(30)], - extrapolate: Extrapolate.CLAMP, - }); + const borderTopRadius = useMemo( + () => + interpolate(animatedPositionIndex, { + inputRange: [1, 2], + outputRange: [20, 0], + extrapolate: Extrapolate.CLAMP, + }), + [animatedPositionIndex] + ); + const indicatorTransformOriginY = useMemo( + () => + interpolate(animatedPositionIndex, { + inputRange: [0, 1, 2], + outputRange: [-1, 0, 1], + extrapolate: Extrapolate.CLAMP, + }), + [animatedPositionIndex] + ); + const leftIndicatorRotate = useMemo( + () => + interpolate(animatedPositionIndex, { + inputRange: [0, 1, 2], + outputRange: [toRad(-30), 0, toRad(30)], + extrapolate: Extrapolate.CLAMP, + }), + [animatedPositionIndex] + ); const rightIndicatorRotate = interpolate(animatedPositionIndex, { inputRange: [0, 1, 2], outputRange: [toRad(30), 0, toRad(-30)], @@ -42,8 +54,7 @@ const Handle: React.FC = ({ style, animatedPositionIndex }) => { borderTopRightRadius: borderTopRadius, }, ], - // eslint-disable-next-line react-hooks/exhaustive-deps - [style] + [borderTopRadius, style] ); const leftIndicatorStyle = useMemo( () => ({ @@ -57,8 +68,7 @@ const Handle: React.FC = ({ style, animatedPositionIndex }) => { } ), }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [] + [indicatorTransformOriginY, leftIndicatorRotate] ); const rightIndicatorStyle = useMemo( () => ({ @@ -72,8 +82,7 @@ const Handle: React.FC = ({ style, animatedPositionIndex }) => { } ), }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [] + [indicatorTransformOriginY, rightIndicatorRotate] ); //#endregion diff --git a/example/src/components/weather/Weather.tsx b/example/src/components/weather/Weather.tsx index c94712edc..8138beae2 100644 --- a/example/src/components/weather/Weather.tsx +++ b/example/src/components/weather/Weather.tsx @@ -3,6 +3,7 @@ import { StyleSheet } from 'react-native'; import Animated, { Extrapolate, interpolate } from 'react-native-reanimated'; import { useAppearance } from '../../hooks'; import Text from '../text'; +import { SEARCH_HANDLE_HEIGHT } from '../../components/searchHandle'; interface WeatherProps { animatedPosition: Animated.Node; @@ -21,8 +22,14 @@ const Weather = ({ animatedPosition, snapPoints }: WeatherProps) => { transform: [ { translateY: interpolate(animatedPosition, { - inputRange: [snapPoints[0], snapPoints[1]], - outputRange: [-snapPoints[0], -snapPoints[1]], + inputRange: [ + snapPoints[0] + SEARCH_HANDLE_HEIGHT, + snapPoints[1] + SEARCH_HANDLE_HEIGHT, + ], + outputRange: [ + -(snapPoints[0] + SEARCH_HANDLE_HEIGHT), + -(snapPoints[1] + SEARCH_HANDLE_HEIGHT), + ], extrapolate: Extrapolate.CLAMP, }), }, diff --git a/example/src/screens/Root.tsx b/example/src/screens/Root.tsx index b55ed8989..7e40449f4 100644 --- a/example/src/screens/Root.tsx +++ b/example/src/screens/Root.tsx @@ -62,6 +62,10 @@ const data = [ name: 'Map', slug: 'Advanced/MapExample', }, + { + name: 'Dynamic Snap Point', + slug: 'Advanced/DynamicSnapPointExample', + }, ], }, ].reverse(); diff --git a/example/src/screens/advanced/CustomHandleExample.tsx b/example/src/screens/advanced/CustomHandleExample.tsx index 39f393852..8ae46cc4c 100644 --- a/example/src/screens/advanced/CustomHandleExample.tsx +++ b/example/src/screens/advanced/CustomHandleExample.tsx @@ -1,6 +1,5 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'; import { View, StyleSheet, Text } from 'react-native'; -import { useHeaderHeight } from '@react-navigation/stack'; import BottomSheet from '@gorhom/bottom-sheet'; import Handle from '../../components/handle'; import Button from '../../components/button'; @@ -12,7 +11,6 @@ const CustomHandleExample = () => { // hooks const bottomSheetRef = useRef(null); - const headerHeight = useHeaderHeight(); // variables const snapPoints = useMemo(() => [150, 300, 450], []); @@ -88,7 +86,6 @@ const CustomHandleExample = () => { enabled={enabled} snapPoints={snapPoints} initialSnapIndex={1} - topInset={headerHeight} handleComponent={Handle} > diff --git a/example/src/screens/advanced/DynamicSnapPointExample.tsx b/example/src/screens/advanced/DynamicSnapPointExample.tsx new file mode 100644 index 000000000..8379dc368 --- /dev/null +++ b/example/src/screens/advanced/DynamicSnapPointExample.tsx @@ -0,0 +1,147 @@ +import React, { useCallback, useMemo, useRef, useState } from 'react'; +import { View, StyleSheet, Text } from 'react-native'; +import BottomSheet, { BottomSheetView } from '@gorhom/bottom-sheet'; +import { useSafeArea } from 'react-native-safe-area-context'; +import { Easing } from 'react-native-reanimated'; +import Button from '../../components/button'; + +const DynamicSnapPointExample = () => { + // state + const [count, setCount] = useState(0); + const [contentHeight, setContentHeight] = useState(0); + + // hooks + const bottomSheetRef = useRef(null); + const { bottom: safeBottomArea } = useSafeArea(); + + // variables + const snapPoints = useMemo(() => [0, contentHeight], [contentHeight]); + + // callbacks + const handleIncreaseContentPress = useCallback(() => { + setCount(state => state + 1); + }, []); + const handleDecreaseContentPress = useCallback(() => { + setCount(state => Math.max(state - 1, 0)); + }, []); + const handleExpandPress = useCallback(() => { + bottomSheetRef.current?.expand(); + }, []); + const handleClosePress = useCallback(() => { + bottomSheetRef.current?.close(); + }, []); + const handleOnLayout = useCallback( + ({ + nativeEvent: { + layout: { height }, + }, + }) => { + // console.log('SCREEN \t\t', 'handleOnLayout', height); + setContentHeight(height); + }, + [] + ); + + // styles + const contentContainerStyle = useMemo( + () => ({ + ...styles.contentContainerStyle, + paddingBottom: safeBottomArea, + }), + [safeBottomArea] + ); + const emojiContainerStyle = useMemo( + () => ({ + ...styles.emojiContainer, + height: 50 * count, + }), + [count] + ); + + // renders + const renderBackground = useCallback( + () => , + [] + ); + + // console.log('SCREEN \t\t', 'render \t'); + return ( + +