@gorhom/bottom-sheet
Version:
A performant interactive bottom sheet with fully configurable options 🚀
132 lines (122 loc) • 3.64 kB
text/typescript
import { useEffect } from 'react';
import {
Keyboard,
type KeyboardEvent,
type KeyboardEventEasing,
type KeyboardEventName,
Platform,
} from 'react-native';
import {
runOnUI,
useAnimatedReaction,
useSharedValue,
useWorkletCallback,
} from 'react-native-reanimated';
import { KEYBOARD_STATE } from '../constants';
const KEYBOARD_EVENT_MAPPER = {
KEYBOARD_SHOW: Platform.select({
ios: 'keyboardWillShow',
android: 'keyboardDidShow',
default: '',
}) as KeyboardEventName,
KEYBOARD_HIDE: Platform.select({
ios: 'keyboardWillHide',
android: 'keyboardDidHide',
default: '',
}) as KeyboardEventName,
};
export const useKeyboard = () => {
//#region variables
const shouldHandleKeyboardEvents = useSharedValue(false);
const keyboardState = useSharedValue<KEYBOARD_STATE>(
KEYBOARD_STATE.UNDETERMINED
);
const keyboardHeight = useSharedValue(0);
const keyboardAnimationEasing =
useSharedValue<KeyboardEventEasing>('keyboard');
const keyboardAnimationDuration = useSharedValue(500);
// biome-ignore lint: to be addressed!
const temporaryCachedKeyboardEvent = useSharedValue<any[]>([]);
//#endregion
//#region worklets
const handleKeyboardEvent = useWorkletCallback(
(
state: KEYBOARD_STATE,
height: number,
duration: number,
easing: KeyboardEventEasing
) => {
if (state === KEYBOARD_STATE.SHOWN && !shouldHandleKeyboardEvents.value) {
/**
* if the keyboard event was fired before the `onFocus` on TextInput,
* then we cache the input, and wait till the `shouldHandleKeyboardEvents`
* to be updated then fire this function again.
*/
temporaryCachedKeyboardEvent.value = [state, height, duration, easing];
return;
}
keyboardHeight.value =
state === KEYBOARD_STATE.SHOWN ? height : keyboardHeight.value;
keyboardAnimationDuration.value = duration;
keyboardAnimationEasing.value = easing;
keyboardState.value = state;
temporaryCachedKeyboardEvent.value = [];
},
[]
);
//#endregion
//#region effects
useEffect(() => {
const handleOnKeyboardShow = (event: KeyboardEvent) => {
runOnUI(handleKeyboardEvent)(
KEYBOARD_STATE.SHOWN,
event.endCoordinates.height,
event.duration,
event.easing
);
};
const handleOnKeyboardHide = (event: KeyboardEvent) => {
runOnUI(handleKeyboardEvent)(
KEYBOARD_STATE.HIDDEN,
event.endCoordinates.height,
event.duration,
event.easing
);
};
const showSubscription = Keyboard.addListener(
KEYBOARD_EVENT_MAPPER.KEYBOARD_SHOW,
handleOnKeyboardShow
);
const hideSubscription = Keyboard.addListener(
KEYBOARD_EVENT_MAPPER.KEYBOARD_HIDE,
handleOnKeyboardHide
);
return () => {
showSubscription.remove();
hideSubscription.remove();
};
}, [handleKeyboardEvent]);
/**
* This reaction is needed to handle the issue with multiline text input.
*
* @link https://github.com/gorhom/react-native-bottom-sheet/issues/411
*/
useAnimatedReaction(
() => shouldHandleKeyboardEvents.value,
result => {
const params = temporaryCachedKeyboardEvent.value;
if (result && params.length > 0) {
handleKeyboardEvent(params[0], params[1], params[2], params[3]);
}
},
[]
);
//#endregion
return {
state: keyboardState,
height: keyboardHeight,
animationEasing: keyboardAnimationEasing,
animationDuration: keyboardAnimationDuration,
shouldHandleKeyboardEvents,
};
};