diff --git a/example/src/Dev.tsx b/example/src/Dev.tsx index a82ec7f..cd636f1 100644 --- a/example/src/Dev.tsx +++ b/example/src/Dev.tsx @@ -1,21 +1,11 @@ /* eslint-disable no-console */ /* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable react-native/no-inline-styles */ import React, { useCallback, useMemo, useRef, useState } from 'react'; -import { - View, - StyleSheet, - Dimensions, - StatusBar, - Button as RNButton, -} from 'react-native'; -import Animated, { - useAnimatedStyle, - useSharedValue, -} from 'react-native-reanimated'; -import { useSafeArea } from 'react-native-safe-area-context'; +import { View, StyleSheet, Dimensions, StatusBar } from 'react-native'; +import { useAnimatedStyle, useSharedValue } from 'react-native-reanimated'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; import BottomSheet from '@gorhom/bottom-sheet'; -import SearchHandle from './components/searchHandle' +import SearchHandle from './components/searchHandle'; import Button from './components/button'; import ContactList from './components/contactList'; @@ -28,11 +18,11 @@ const BasicExample = () => { //#region hooks const bottomSheetRef = useRef(null); - const { top: topSafeArea, bottom: bottomSafeArea } = useSafeArea(); + const { top: topSafeArea, bottom: bottomSafeArea } = useSafeAreaInsets(); //#endregion //#region variables - const snapPoints = useMemo(() => [200], [dynamicSnapPoint]); + const snapPoints = useMemo(() => [200, dynamicSnapPoint], [dynamicSnapPoint]); const animatedPosition = useSharedValue(0); //#endregion @@ -97,9 +87,6 @@ const BasicExample = () => { const handleIncreaseDynamicSnapPoint = useCallback(() => { setDynamicSnapPoint(state => state + 50); }, []); - const handleFocus = useCallback(() => { - bottomSheetRef.current.fullScreenExpand(); - }, []); //#endregion // renders diff --git a/example/src/components/searchHandle/SearchHandle.tsx b/example/src/components/searchHandle/SearchHandle.tsx index 708ad71..09580e6 100644 --- a/example/src/components/searchHandle/SearchHandle.tsx +++ b/example/src/components/searchHandle/SearchHandle.tsx @@ -6,7 +6,7 @@ import { NativeSyntheticEvent, TextInputChangeEventData, } from 'react-native'; -import { TextInput } from 'react-native-gesture-handler'; +import { BottomSheetTextInput } from '@gorhom/bottom-sheet'; import isEqual from 'lodash.isequal'; import { useAppearance } from '../../hooks'; @@ -48,7 +48,7 @@ const BottomSheetHandleComponent = () => { return ( - ( // scrollable variables const { scrollableContentOffsetY, - scrollableDecelerationRate, setScrollableRef, removeScrollableRef, flashScrollableIndicators, @@ -198,6 +196,7 @@ const BottomSheetComponent = forwardRef( height: keyboardHeight, animationDuration: keyboardAnimationDuration, animationEasing: keyboardAnimationEasing, + shouldHandleKeyboardEvents, } = useKeyboard(); // normalize snap points @@ -297,9 +296,22 @@ const BottomSheetComponent = forwardRef( keyboardBehavior === KEYBOARD_BEHAVIOR.interactive && isExtendedByKeyboard.value ) { - return keyboardState.value === KEYBOARD_STATE.SHOWN - ? sheetHeight - : sheetHeight + keyboardHeight.value; + const safeFullScreenSheetHeight = + safeContainerHeight - topInset - safeHandleHeight; + const sheetWithKeyboardHeight = sheetHeight + keyboardHeight.value; + + if (keyboardState.value === KEYBOARD_STATE.SHOWN) { + if (sheetHeight >= safeFullScreenSheetHeight) { + return safeFullScreenSheetHeight - keyboardHeight.value; + } + return sheetHeight; + } + + if (sheetWithKeyboardHeight > safeFullScreenSheetHeight) { + return safeFullScreenSheetHeight; + } + + return sheetWithKeyboardHeight; } return sheetHeight; @@ -534,6 +546,7 @@ const BottomSheetComponent = forwardRef( contentPanGestureHandler, scrollableState, scrollableContentOffsetY, + shouldHandleKeyboardEvents, simultaneousHandlers: _providedSimultaneousHandlers, waitFor: _providedWaitFor, activeOffsetX: _providedActiveOffsetX, @@ -552,6 +565,7 @@ const BottomSheetComponent = forwardRef( contentPanGestureHandler, handleSettingScrollableRef, removeScrollableRef, + shouldHandleKeyboardEvents, scrollableState, scrollableContentOffsetY, enableContentPanningGesture, @@ -683,7 +697,12 @@ const BottomSheetComponent = forwardRef( state === KEYBOARD_STATE.SHOWN ) { const newSnapPoint = snapPoints[snapPoints.length - 1]; - animateToPoint(newSnapPoint); + animateToPoint( + newSnapPoint, + 0, + keyboardAnimationDuration.value, + KEYBOARD_EASING_MAPPER[keyboardAnimationEasing.value] + ); return; } @@ -695,41 +714,32 @@ const BottomSheetComponent = forwardRef( state === KEYBOARD_STATE.SHOWN ) { isExtendedByKeyboard.value = true; - animateToPoint(topInset); + animateToPoint( + topInset, + 0, + keyboardAnimationDuration.value, + KEYBOARD_EASING_MAPPER[keyboardAnimationEasing.value] + ); return; } + /** + * handle interactive behavior + */ if ( keyboardBehavior === KEYBOARD_BEHAVIOR.interactive && state === KEYBOARD_STATE.SHOWN ) { isExtendedByKeyboard.value = true; const newSnapPoint = snapPoints[snapPoints.length - 1]; - animateToPoint(newSnapPoint - keyboardHeight.value); + animateToPoint( + Math.max(topInset, newSnapPoint - keyboardHeight.value), + 0, + keyboardAnimationDuration.value, + KEYBOARD_EASING_MAPPER[keyboardAnimationEasing.value] + ); } - }, - [snapPoints, keyboardBehavior, topInset, animateToPoint] - ); - - /** - * set scrollable deceleration rate based on sheet - * position. - */ - useAnimatedReaction( - () => scrollableState.value, - (_scrollableState, _prevScrollableState) => { - if (_prevScrollableState === _scrollableState) { - return; - } - const newDecelerationRate = - _scrollableState === SCROLLABLE_STATE.UNLOCKED - ? DECELERATION_RATE - : 0; - if (scrollableDecelerationRate.value !== newDecelerationRate) { - scrollableDecelerationRate.value = newDecelerationRate; - } - }, - [snapPoints.length] + } ); /** @@ -790,14 +800,14 @@ const BottomSheetComponent = forwardRef( //#endregion // render - console.log( - 'BottomSheet', - 'render', - snapPoints, - safeContainerHeight, - safeHandleHeight, - sheetHeight - ); + // console.log( + // 'BottomSheet', + // 'render', + // snapPoints, + // safeContainerHeight, + // safeHandleHeight, + // sheetHeight + // ); return ( { //#region variables + const shouldHandleKeyboardEvents = useSharedValue(false); const keyboardState = useSharedValue( KEYBOARD_STATE.UNDETERMINED ); @@ -34,54 +39,79 @@ export const useKeyboard = () => { const keyboardAnimationDuration = useSharedValue(0); //#endregion + //#region worklets + const handleKeyboardEvent = useWorkletCallback( + (state, height, duration, easing) => { + if (state === KEYBOARD_STATE.SHOWN && !shouldHandleKeyboardEvents.value) { + return; + } + keyboardState.value = state; + keyboardHeight.value = + state === KEYBOARD_STATE.SHOWN + ? height + : height === 0 + ? keyboardHeight.value + : height; + keyboardAnimationDuration.value = duration; + keyboardAnimationEasing.value = easing; + } + ); + //#endregion + + //#region callbacks + const handleOnKeyboardShow = useCallback( + (event: KeyboardEvent) => { + runOnUI(handleKeyboardEvent)( + KEYBOARD_STATE.SHOWN, + event.endCoordinates.height, + event.duration, + event.easing + ); + }, + [handleKeyboardEvent] + ); + + const handleOnKeyboardHide = useCallback( + (event: KeyboardEvent) => { + runOnUI(handleKeyboardEvent)( + KEYBOARD_STATE.HIDDEN, + event.endCoordinates.height, + event.duration, + event.easing + ); + }, + [handleKeyboardEvent] + ); + //#endregion + //#region effects useEffect(() => { - const handleKeyboardShow = (event: KeyboardEvent) => { - runOnUI((height, duration, easing) => { - keyboardState.value = KEYBOARD_STATE.SHOWN; - keyboardHeight.value = height; - keyboardAnimationDuration.value = duration; - keyboardAnimationEasing.value = easing; - })(event.endCoordinates.height, event.duration, event.easing); - }; - Keyboard.addListener( KEYBOARD_EVENT_MAPPER.KEYBOARD_SHOW, - handleKeyboardShow + handleOnKeyboardShow ); return () => { Keyboard.removeListener( KEYBOARD_EVENT_MAPPER.KEYBOARD_SHOW, - handleKeyboardShow + handleOnKeyboardShow ); }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [handleOnKeyboardShow]); useEffect(() => { - const handleKeyboardHide = (event: KeyboardEvent) => { - runOnUI((height, duration, easing) => { - keyboardState.value = KEYBOARD_STATE.HIDDEN; - // prevent zeroing the height - keyboardHeight.value = height === 0 ? keyboardHeight.value : height; - keyboardAnimationDuration.value = duration; - keyboardAnimationEasing.value = easing; - })(event.endCoordinates.height, event.duration, event.easing); - }; Keyboard.addListener( KEYBOARD_EVENT_MAPPER.KEYBOARD_HIDE, - handleKeyboardHide + handleOnKeyboardHide ); return () => { Keyboard.removeListener( KEYBOARD_EVENT_MAPPER.KEYBOARD_HIDE, - handleKeyboardHide + handleOnKeyboardHide ); }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [handleOnKeyboardHide]); //#endregion return { @@ -89,5 +119,6 @@ export const useKeyboard = () => { height: keyboardHeight, animationDuration: keyboardAnimationDuration, animationEasing: keyboardAnimationEasing, + shouldHandleKeyboardEvents, }; };