diff --git a/src/compat.ts b/src/compat.ts new file mode 100644 index 0000000000..36e638052e --- /dev/null +++ b/src/compat.ts @@ -0,0 +1,61 @@ +import { useSharedValue } from "react-native-reanimated"; + +import { useKeyboardHandler } from "./hooks"; + +export const KeyboardState = { + UNKNOWN: 0, + OPENING: 1, + OPEN: 2, + CLOSING: 3, + CLOSED: 4, +}; + +/** + * A compatibility layer for migration from https://docs.swmansion.com/react-native-reanimated/docs/device/useAnimatedKeyboard. + * + * @returns An object containing `height` and `state` properties represented as `SharedValue`. + * @example + * ```ts + * import { useAnimatedKeyboard, useAnimatedStyle } from 'react-native-keyboard-controller'; + * + * export default function App() { + * const keyboard = useAnimatedKeyboard(); + * + * const animatedStyles = useAnimatedStyle(() => ({ + * transform: [{ translateY: -keyboard.height.value }], + * })); + * } + */ +export const useAnimatedKeyboard = () => { + const height = useSharedValue(0); + const state = useSharedValue(KeyboardState.UNKNOWN); + + useKeyboardHandler( + { + onStart: (e) => { + "worklet"; + + state.set(e.height > 0 ? KeyboardState.OPENING : KeyboardState.CLOSING); + }, + onMove: (e) => { + "worklet"; + + height.set(e.height); + }, + onInteractive: (e) => { + "worklet"; + + height.set(e.height); + }, + onEnd: (e) => { + "worklet"; + + state.set(e.height > 0 ? KeyboardState.OPEN : KeyboardState.CLOSED); + height.set(e.height); + }, + }, + [], + ); + + return { height, state }; +}; diff --git a/src/hooks/useKeyboardState/index.ts b/src/hooks/useKeyboardState/index.ts index f0b39bcf2e..c9d6107629 100644 --- a/src/hooks/useKeyboardState/index.ts +++ b/src/hooks/useKeyboardState/index.ts @@ -3,7 +3,7 @@ import { useEffect, useState } from "react"; import { KeyboardEvents } from "../../bindings"; import { KeyboardController } from "../../module"; -import type { KeyboardState } from "../../types"; +import type { IKeyboardState } from "../../types"; const EVENTS = ["keyboardDidShow", "keyboardDidHide"] as const; @@ -12,9 +12,9 @@ const getLatestState = () => ({ isVisible: KeyboardController.isVisible(), }); -type KeyboardStateSelector = (state: KeyboardState) => T; +type KeyboardStateSelector = (state: IKeyboardState) => T; -const defaultSelector: KeyboardStateSelector = (state) => state; +const defaultSelector: KeyboardStateSelector = (state) => state; /** * React Hook that represents the current keyboard state on iOS and Android. @@ -40,7 +40,7 @@ const defaultSelector: KeyboardStateSelector = (state) => state; * } * ``` */ -function useKeyboardState( +function useKeyboardState( selector: KeyboardStateSelector = defaultSelector as KeyboardStateSelector, ): T { const [state, setState] = useState(() => selector(getLatestState())); diff --git a/src/index.ts b/src/index.ts index 9093708b5e..427d4fdbaa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ export * from "./hooks"; export * from "./constants"; export * from "./module"; export * from "./types"; +export * from "./compat"; export { KeyboardAvoidingView, diff --git a/src/types/module.ts b/src/types/module.ts index 09a5840c8a..e9991a6b56 100644 --- a/src/types/module.ts +++ b/src/types/module.ts @@ -23,7 +23,7 @@ export type KeyboardEventData = { /** * An object that represent current keyboard state. */ -export type KeyboardState = { +export type IKeyboardState = { /** Whether the keyboard is currently visible. */ isVisible: boolean; } & KeyboardEventData;