react-native-keyboard-controller 1.20.0-beta.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +18 -0
  2. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt +1 -1
  3. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +7 -0
  4. package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +2 -2
  5. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +16 -0
  6. package/ios/KeyboardControllerModule.mm +1 -0
  7. package/ios/observers/FocusedInputObserver.swift +1 -1
  8. package/ios/views/KeyboardControllerView.mm +11 -0
  9. package/ios/views/KeyboardControllerViewManager.mm +3 -0
  10. package/ios/views/KeyboardControllerViewManager.swift +13 -1
  11. package/lib/commonjs/animated.js +14 -4
  12. package/lib/commonjs/animated.js.map +1 -1
  13. package/lib/commonjs/bindings.js +4 -1
  14. package/lib/commonjs/bindings.js.map +1 -1
  15. package/lib/commonjs/bindings.native.js +4 -2
  16. package/lib/commonjs/bindings.native.js.map +1 -1
  17. package/lib/commonjs/compat.js +65 -0
  18. package/lib/commonjs/compat.js.map +1 -0
  19. package/lib/commonjs/components/KeyboardAwareScrollView/index.js +25 -7
  20. package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
  21. package/lib/commonjs/components/index.js.map +1 -1
  22. package/lib/commonjs/context.js +1 -0
  23. package/lib/commonjs/context.js.map +1 -1
  24. package/lib/commonjs/hooks/index.js +2 -1
  25. package/lib/commonjs/hooks/index.js.map +1 -1
  26. package/lib/commonjs/hooks/useKeyboardState/index.js.map +1 -1
  27. package/lib/commonjs/index.js +12 -0
  28. package/lib/commonjs/index.js.map +1 -1
  29. package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js +5 -1
  30. package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
  31. package/lib/commonjs/types/internal.js.map +1 -1
  32. package/lib/commonjs/types/module.js.map +1 -1
  33. package/lib/module/animated.js +16 -6
  34. package/lib/module/animated.js.map +1 -1
  35. package/lib/module/bindings.js +3 -0
  36. package/lib/module/bindings.js.map +1 -1
  37. package/lib/module/bindings.native.js +3 -1
  38. package/lib/module/bindings.native.js.map +1 -1
  39. package/lib/module/compat.js +58 -0
  40. package/lib/module/compat.js.map +1 -0
  41. package/lib/module/components/KeyboardAwareScrollView/index.js +26 -8
  42. package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
  43. package/lib/module/components/index.js.map +1 -1
  44. package/lib/module/context.js +1 -0
  45. package/lib/module/context.js.map +1 -1
  46. package/lib/module/hooks/index.js +2 -1
  47. package/lib/module/hooks/index.js.map +1 -1
  48. package/lib/module/hooks/useKeyboardState/index.js.map +1 -1
  49. package/lib/module/index.js +1 -0
  50. package/lib/module/index.js.map +1 -1
  51. package/lib/module/specs/KeyboardControllerViewNativeComponent.js +4 -0
  52. package/lib/module/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
  53. package/lib/module/types/internal.js.map +1 -1
  54. package/lib/module/types/module.js.map +1 -1
  55. package/lib/typescript/bindings.d.ts +3 -0
  56. package/lib/typescript/bindings.native.d.ts +1 -0
  57. package/lib/typescript/compat.d.ts +27 -0
  58. package/lib/typescript/components/KeyboardAwareScrollView/index.d.ts +4 -1
  59. package/lib/typescript/components/index.d.ts +1 -1
  60. package/lib/typescript/context.d.ts +2 -0
  61. package/lib/typescript/hooks/index.d.ts +1 -0
  62. package/lib/typescript/hooks/useKeyboardState/index.d.ts +3 -3
  63. package/lib/typescript/index.d.ts +2 -1
  64. package/lib/typescript/specs/KeyboardControllerViewNativeComponent.d.ts +4 -0
  65. package/lib/typescript/types/internal.d.ts +1 -1
  66. package/lib/typescript/types/module.d.ts +3 -2
  67. package/package.json +1 -1
  68. package/src/animated.tsx +32 -6
  69. package/src/bindings.native.ts +4 -1
  70. package/src/bindings.ts +5 -0
  71. package/src/compat.ts +61 -0
  72. package/src/components/KeyboardAwareScrollView/index.tsx +43 -11
  73. package/src/components/index.ts +4 -1
  74. package/src/context.ts +3 -0
  75. package/src/hooks/index.ts +4 -1
  76. package/src/hooks/useKeyboardState/index.ts +4 -4
  77. package/src/index.ts +2 -0
  78. package/src/specs/KeyboardControllerViewNativeComponent.ts +11 -0
  79. package/src/types/internal.ts +3 -1
  80. package/src/types/module.ts +3 -2
  81. package/android/.settings/org.eclipse.buildship.core.prefs +0 -2
  82. package/ios/KeyboardController.xcodeproj/xcuserdata/kirylziusko.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  83. package/ios/Tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  84. package/ios/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  85. package/ios/Tests/Tests.xcodeproj/project.xcworkspace/xcuserdata/kirylziusko.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  86. package/ios/Tests/Tests.xcodeproj/xcuserdata/kirylziusko.xcuserdatad/xcschemes/xcschememanagement.plist +0 -32
@@ -22,6 +22,9 @@ export declare const WindowDimensionsEvents: WindowDimensionsEventsModule;
22
22
  * @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-controller-view|Documentation} page for more details.
23
23
  */
24
24
  export declare const KeyboardControllerView: React.FC<KeyboardControllerProps>;
25
+ export declare const KeyboardControllerViewCommands: {
26
+ synchronizeFocusedInputLayout: (_ref: React.Component<KeyboardControllerProps> | null) => void;
27
+ };
25
28
  /**
26
29
  * A view that defines a region on the screen, where gestures will control the keyboard position.
27
30
  *
@@ -8,6 +8,7 @@ export declare const KeyboardEvents: KeyboardEventsModule;
8
8
  export declare const FocusedInputEvents: FocusedInputEventsModule;
9
9
  export declare const WindowDimensionsEvents: WindowDimensionsEventsModule;
10
10
  export declare const KeyboardControllerView: React.FC<KeyboardControllerProps>;
11
+ export declare const KeyboardControllerViewCommands: any;
11
12
  export declare const KeyboardGestureArea: React.FC<KeyboardGestureAreaProps>;
12
13
  export declare const RCTOverKeyboardView: React.FC<OverKeyboardViewProps>;
13
14
  export declare const KeyboardBackgroundView: React.FC<KeyboardBackgroundViewProps>;
@@ -0,0 +1,27 @@
1
+ export declare const KeyboardState: {
2
+ UNKNOWN: number;
3
+ OPENING: number;
4
+ OPEN: number;
5
+ CLOSING: number;
6
+ CLOSED: number;
7
+ };
8
+ /**
9
+ * A compatibility layer for migration from https://docs.swmansion.com/react-native-reanimated/docs/device/useAnimatedKeyboard.
10
+ *
11
+ * @returns An object containing `height` and `state` properties represented as `SharedValue<number>`.
12
+ * @example
13
+ * ```ts
14
+ * import { useAnimatedKeyboard, useAnimatedStyle } from 'react-native-keyboard-controller';
15
+ *
16
+ * export default function App() {
17
+ * const keyboard = useAnimatedKeyboard();
18
+ *
19
+ * const animatedStyles = useAnimatedStyle(() => ({
20
+ * transform: [{ translateY: -keyboard.height.value }],
21
+ * }));
22
+ * }
23
+ */
24
+ export declare const useAnimatedKeyboard: () => {
25
+ height: import("react-native-reanimated").SharedValue<number>;
26
+ state: import("react-native-reanimated").SharedValue<number>;
27
+ };
@@ -12,6 +12,9 @@ export type KeyboardAwareScrollViewProps = {
12
12
  /** Custom component for `ScrollView`. Default is `ScrollView`. */
13
13
  ScrollViewComponent?: React.ComponentType<ScrollViewProps>;
14
14
  } & ScrollViewProps;
15
+ export type KeyboardAwareScrollViewRef = {
16
+ assureFocusedInputVisible: () => void;
17
+ } & ScrollView;
15
18
  /**
16
19
  * A ScrollView component that automatically handles keyboard appearance and disappearance
17
20
  * by adjusting its content position to ensure the focused input remains visible.
@@ -42,5 +45,5 @@ declare const KeyboardAwareScrollView: React.ForwardRefExoticComponent<{
42
45
  ScrollViewComponent?: React.ComponentType<ScrollViewProps>;
43
46
  } & ScrollViewProps & {
44
47
  children?: React.ReactNode | undefined;
45
- } & React.RefAttributes<ScrollView>>;
48
+ } & React.RefAttributes<KeyboardAwareScrollViewRef>>;
46
49
  export default KeyboardAwareScrollView;
@@ -4,5 +4,5 @@ export { default as KeyboardAwareScrollView } from "./KeyboardAwareScrollView";
4
4
  export { default as KeyboardToolbar, DefaultKeyboardToolbarTheme, } from "./KeyboardToolbar";
5
5
  export type { KeyboardAvoidingViewProps } from "./KeyboardAvoidingView";
6
6
  export type { KeyboardStickyViewProps } from "./KeyboardStickyView";
7
- export type { KeyboardAwareScrollViewProps } from "./KeyboardAwareScrollView";
7
+ export type { KeyboardAwareScrollViewProps, KeyboardAwareScrollViewRef, } from "./KeyboardAwareScrollView";
8
8
  export type { KeyboardToolbarProps } from "./KeyboardToolbar";
@@ -29,6 +29,8 @@ export type KeyboardAnimationContext = {
29
29
  reanimated: ReanimatedContext;
30
30
  /** Layout of the focused `TextInput` represented as `SharedValue`. */
31
31
  layout: SharedValue<FocusedInputLayoutChangedEvent | null>;
32
+ /** Method for updating info about focused input layout. */
33
+ update: () => Promise<void>;
32
34
  /** Method for setting workletized keyboard handlers. */
33
35
  setKeyboardHandlers: (handlers: EventHandlerProcessed<never, never>) => () => void;
34
36
  /** Method for setting workletized handlers for tracking focused input events. */
@@ -151,6 +151,7 @@ export declare function useKeyboardController(): {
151
151
  */
152
152
  export declare function useReanimatedFocusedInput(): {
153
153
  input: import("react-native-reanimated").SharedValue<import("../types").FocusedInputLayoutChangedEvent | null>;
154
+ update: () => Promise<void>;
154
155
  };
155
156
  /**
156
157
  * Hook for handling focused input events, such as changes of selection, text etc.
@@ -1,5 +1,5 @@
1
- import type { KeyboardState } from "../../types";
2
- type KeyboardStateSelector<T> = (state: KeyboardState) => T;
1
+ import type { IKeyboardState } from "../../types";
2
+ type KeyboardStateSelector<T> = (state: IKeyboardState) => T;
3
3
  /**
4
4
  * React Hook that represents the current keyboard state on iOS and Android.
5
5
  * It tracks keyboard visibility, height, appearance, type and other properties.
@@ -24,5 +24,5 @@ type KeyboardStateSelector<T> = (state: KeyboardState) => T;
24
24
  * }
25
25
  * ```
26
26
  */
27
- declare function useKeyboardState<T = KeyboardState>(selector?: KeyboardStateSelector<T>): T;
27
+ declare function useKeyboardState<T = IKeyboardState>(selector?: KeyboardStateSelector<T>): T;
28
28
  export { useKeyboardState };
@@ -5,6 +5,7 @@ export * from "./hooks";
5
5
  export * from "./constants";
6
6
  export * from "./module";
7
7
  export * from "./types";
8
+ export * from "./compat";
8
9
  export { KeyboardAvoidingView, KeyboardStickyView, KeyboardAwareScrollView, KeyboardToolbar, DefaultKeyboardToolbarTheme, } from "./components";
9
- export type { KeyboardAvoidingViewProps, KeyboardStickyViewProps, KeyboardAwareScrollViewProps, KeyboardToolbarProps, } from "./components";
10
+ export type { KeyboardAvoidingViewProps, KeyboardStickyViewProps, KeyboardAwareScrollViewProps, KeyboardAwareScrollViewRef, KeyboardToolbarProps, } from "./components";
10
11
  export { OverKeyboardView, KeyboardExtender } from "./views";
@@ -50,5 +50,9 @@ export interface NativeProps extends ViewProps {
50
50
  onFocusedInputTextChanged?: DirectEventHandler<FocusedInputTextChangedEvent>;
51
51
  onFocusedInputSelectionChanged?: DirectEventHandler<FocusedInputSelectionChangedEvent>;
52
52
  }
53
+ interface NativeCommands {
54
+ synchronizeFocusedInputLayout: (viewRef: React.ElementRef<HostComponent<NativeProps>>) => void;
55
+ }
56
+ export declare const Commands: NativeCommands;
53
57
  declare const _default: HostComponent<NativeProps>;
54
58
  export default _default;
@@ -1,5 +1,5 @@
1
1
  import type { EmitterSubscription } from "react-native";
2
- export type FocusedInputAvailableEvents = "focusDidSet";
2
+ export type FocusedInputAvailableEvents = "focusDidSet" | "layoutDidSynchronize";
3
3
  export type FocusedInputEventData = {
4
4
  current: number;
5
5
  count: number;
@@ -17,7 +17,7 @@ export type KeyboardEventData = {
17
17
  /**
18
18
  * An object that represent current keyboard state.
19
19
  */
20
- export type KeyboardState = {
20
+ export type IKeyboardState = {
21
21
  /** Whether the keyboard is currently visible. */
22
22
  isVisible: boolean;
23
23
  } & KeyboardEventData;
@@ -73,10 +73,11 @@ export type KeyboardControllerModule = {
73
73
  preload: () => void;
74
74
  /**
75
75
  * Dismisses the active keyboard. Removes a focus by default, but allows to pass `{keepFocus: true}` to keep focus.
76
+ * If you want to close keyboard immediately pass `{animated: false}`.
76
77
  *
77
78
  * @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-controller#dismiss|docs} page for more details.
78
79
  */
79
- dismiss: (options?: DismissOptions) => Promise<void>;
80
+ dismiss: (options?: Partial<DismissOptions>) => Promise<void>;
80
81
  /**
81
82
  * Moves focus to the specified direction (`next`, `prev` or `current` to restore a focus).
82
83
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-keyboard-controller",
3
- "version": "1.20.0-beta.0",
3
+ "version": "1.20.0",
4
4
  "description": "Keyboard manager which works in identical way on both iOS and Android",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
package/src/animated.tsx CHANGED
@@ -1,5 +1,11 @@
1
1
  /* eslint react/jsx-sort-props: off */
2
- import React, { useEffect, useMemo, useRef, useState } from "react";
2
+ import React, {
3
+ useCallback,
4
+ useEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ } from "react";
3
9
  import { Animated, Platform, StyleSheet } from "react-native";
4
10
  import {
5
11
  controlEdgeToEdgeValues,
@@ -7,7 +13,11 @@ import {
7
13
  } from "react-native-is-edge-to-edge";
8
14
  import Reanimated, { useSharedValue } from "react-native-reanimated";
9
15
 
10
- import { KeyboardControllerView } from "./bindings";
16
+ import {
17
+ FocusedInputEvents,
18
+ KeyboardControllerView,
19
+ KeyboardControllerViewCommands,
20
+ } from "./bindings";
11
21
  import { KeyboardContext } from "./context";
12
22
  import { useAnimatedValue, useEventHandlerRegistration } from "./internal";
13
23
  import { KeyboardController } from "./module";
@@ -117,7 +127,7 @@ export const KeyboardProvider = (props: KeyboardProviderProps) => {
117
127
  preload = true,
118
128
  } = props;
119
129
  // ref
120
- const viewTagRef = useRef<React.Component<KeyboardControllerProps>>(null);
130
+ const viewRef = useRef<React.Component<KeyboardControllerProps>>(null);
121
131
  // state
122
132
  const [enabled, setEnabled] = useState(initiallyEnabled);
123
133
  // animated values
@@ -127,8 +137,23 @@ export const KeyboardProvider = (props: KeyboardProviderProps) => {
127
137
  const progressSV = useSharedValue(0);
128
138
  const heightSV = useSharedValue(0);
129
139
  const layout = useSharedValue<FocusedInputLayoutChangedEvent | null>(null);
130
- const setKeyboardHandlers = useEventHandlerRegistration(viewTagRef);
131
- const setInputHandlers = useEventHandlerRegistration(viewTagRef);
140
+ const setKeyboardHandlers = useEventHandlerRegistration(viewRef);
141
+ const setInputHandlers = useEventHandlerRegistration(viewRef);
142
+ const update = useCallback(async () => {
143
+ KeyboardControllerViewCommands.synchronizeFocusedInputLayout(
144
+ viewRef.current,
145
+ );
146
+
147
+ await new Promise((resolve) => {
148
+ const subscription = FocusedInputEvents.addListener(
149
+ "layoutDidSynchronize",
150
+ () => {
151
+ subscription.remove();
152
+ resolve(null);
153
+ },
154
+ );
155
+ });
156
+ }, []);
132
157
  // memo
133
158
  const context = useMemo<KeyboardAnimationContext>(
134
159
  () => ({
@@ -136,6 +161,7 @@ export const KeyboardProvider = (props: KeyboardProviderProps) => {
136
161
  animated: { progress: progress, height: Animated.multiply(height, -1) },
137
162
  reanimated: { progress: progressSV, height: heightSV },
138
163
  layout,
164
+ update,
139
165
  setKeyboardHandlers,
140
166
  setInputHandlers,
141
167
  setEnabled,
@@ -232,7 +258,7 @@ export const KeyboardProvider = (props: KeyboardProviderProps) => {
232
258
  return (
233
259
  <KeyboardContext.Provider value={context}>
234
260
  <KeyboardControllerViewAnimated
235
- ref={viewTagRef}
261
+ ref={viewRef}
236
262
  enabled={enabled}
237
263
  navigationBarTranslucent={IS_EDGE_TO_EDGE || navigationBarTranslucent}
238
264
  statusBarTranslucent={IS_EDGE_TO_EDGE || statusBarTranslucent}
@@ -18,6 +18,7 @@ const LINKING_ERROR =
18
18
  "- You rebuilt the app after installing the package\n" +
19
19
  "- You are not using Expo Go\n";
20
20
 
21
+ const KeyboardControllerViewNativeComponentModule = require("./specs/KeyboardControllerViewNativeComponent");
21
22
  const RCTKeyboardController =
22
23
  require("./specs/NativeKeyboardController").default;
23
24
 
@@ -55,7 +56,9 @@ export const WindowDimensionsEvents: WindowDimensionsEventsModule = {
55
56
  eventEmitter.addListener(KEYBOARD_CONTROLLER_NAMESPACE + name, cb),
56
57
  };
57
58
  export const KeyboardControllerView: React.FC<KeyboardControllerProps> =
58
- require("./specs/KeyboardControllerViewNativeComponent").default;
59
+ KeyboardControllerViewNativeComponentModule.default;
60
+ export const KeyboardControllerViewCommands =
61
+ KeyboardControllerViewNativeComponentModule.Commands;
59
62
  export const KeyboardGestureArea: React.FC<KeyboardGestureAreaProps> =
60
63
  (Platform.OS === "android" && Platform.Version >= 30) || Platform.OS === "ios"
61
64
  ? require("./specs/KeyboardGestureAreaNativeComponent").default
package/src/bindings.ts CHANGED
@@ -53,6 +53,11 @@ export const WindowDimensionsEvents: WindowDimensionsEventsModule = {
53
53
  */
54
54
  export const KeyboardControllerView =
55
55
  View as unknown as React.FC<KeyboardControllerProps>;
56
+ export const KeyboardControllerViewCommands = {
57
+ synchronizeFocusedInputLayout: (
58
+ _ref: React.Component<KeyboardControllerProps> | null,
59
+ ) => {},
60
+ };
56
61
  /**
57
62
  * A view that defines a region on the screen, where gestures will control the keyboard position.
58
63
  *
package/src/compat.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { useSharedValue } from "react-native-reanimated";
2
+
3
+ import { useKeyboardHandler } from "./hooks";
4
+
5
+ export const KeyboardState = {
6
+ UNKNOWN: 0,
7
+ OPENING: 1,
8
+ OPEN: 2,
9
+ CLOSING: 3,
10
+ CLOSED: 4,
11
+ };
12
+
13
+ /**
14
+ * A compatibility layer for migration from https://docs.swmansion.com/react-native-reanimated/docs/device/useAnimatedKeyboard.
15
+ *
16
+ * @returns An object containing `height` and `state` properties represented as `SharedValue<number>`.
17
+ * @example
18
+ * ```ts
19
+ * import { useAnimatedKeyboard, useAnimatedStyle } from 'react-native-keyboard-controller';
20
+ *
21
+ * export default function App() {
22
+ * const keyboard = useAnimatedKeyboard();
23
+ *
24
+ * const animatedStyles = useAnimatedStyle(() => ({
25
+ * transform: [{ translateY: -keyboard.height.value }],
26
+ * }));
27
+ * }
28
+ */
29
+ export const useAnimatedKeyboard = () => {
30
+ const height = useSharedValue(0);
31
+ const state = useSharedValue(KeyboardState.UNKNOWN);
32
+
33
+ useKeyboardHandler(
34
+ {
35
+ onStart: (e) => {
36
+ "worklet";
37
+
38
+ state.set(e.height > 0 ? KeyboardState.OPENING : KeyboardState.CLOSING);
39
+ },
40
+ onMove: (e) => {
41
+ "worklet";
42
+
43
+ height.set(e.height);
44
+ },
45
+ onInteractive: (e) => {
46
+ "worklet";
47
+
48
+ height.set(e.height);
49
+ },
50
+ onEnd: (e) => {
51
+ "worklet";
52
+
53
+ state.set(e.height > 0 ? KeyboardState.OPEN : KeyboardState.CLOSED);
54
+ height.set(e.height);
55
+ },
56
+ },
57
+ [],
58
+ );
59
+
60
+ return { height, state };
61
+ };
@@ -1,4 +1,10 @@
1
- import React, { forwardRef, useCallback, useEffect, useMemo } from "react";
1
+ import React, {
2
+ forwardRef,
3
+ useCallback,
4
+ useEffect,
5
+ useImperativeHandle,
6
+ useMemo,
7
+ } from "react";
2
8
  import Reanimated, {
3
9
  clamp,
4
10
  interpolate,
@@ -44,6 +50,9 @@ export type KeyboardAwareScrollViewProps = {
44
50
  /** Custom component for `ScrollView`. Default is `ScrollView`. */
45
51
  ScrollViewComponent?: React.ComponentType<ScrollViewProps>;
46
52
  } & ScrollViewProps;
53
+ export type KeyboardAwareScrollViewRef = {
54
+ assureFocusedInputVisible: () => void;
55
+ } & ScrollView;
47
56
 
48
57
  // Everything begins from `onStart` handler. This handler is called every time,
49
58
  // when keyboard changes its size or when focused `TextInput` was changed. In
@@ -99,7 +108,7 @@ export type KeyboardAwareScrollViewProps = {
99
108
  * ```
100
109
  */
101
110
  const KeyboardAwareScrollView = forwardRef<
102
- ScrollView,
111
+ KeyboardAwareScrollViewRef,
103
112
  React.PropsWithChildren<KeyboardAwareScrollViewProps>
104
113
  >(
105
114
  (
@@ -117,6 +126,7 @@ const KeyboardAwareScrollView = forwardRef<
117
126
  ref,
118
127
  ) => {
119
128
  const scrollViewAnimatedRef = useAnimatedRef<Reanimated.ScrollView>();
129
+ const scrollViewRef = React.useRef<ScrollView>(null);
120
130
  const scrollViewTarget = useSharedValue<number | null>(null);
121
131
  const scrollPosition = useSharedValue(0);
122
132
  const position = useScrollViewOffset(scrollViewAnimatedRef);
@@ -126,7 +136,7 @@ const KeyboardAwareScrollView = forwardRef<
126
136
  const tag = useSharedValue(-1);
127
137
  const initialKeyboardSize = useSharedValue(0);
128
138
  const scrollBeforeKeyboardMovement = useSharedValue(0);
129
- const { input } = useReanimatedFocusedInput();
139
+ const { input, update } = useReanimatedFocusedInput();
130
140
  const layout = useSharedValue<FocusedInputLayoutChangedEvent | null>(null);
131
141
  const lastSelection =
132
142
  useSharedValue<FocusedInputSelectionChangedEvent | null>(null);
@@ -134,11 +144,7 @@ const KeyboardAwareScrollView = forwardRef<
134
144
  const { height } = useWindowDimensions();
135
145
 
136
146
  const onRef = useCallback((assignedRef: Reanimated.ScrollView) => {
137
- if (typeof ref === "function") {
138
- ref(assignedRef);
139
- } else if (ref) {
140
- ref.current = assignedRef;
141
- }
147
+ scrollViewRef.current = assignedRef;
142
148
 
143
149
  scrollViewAnimatedRef(assignedRef);
144
150
  }, []);
@@ -392,10 +398,36 @@ const KeyboardAwareScrollView = forwardRef<
392
398
  [maybeScroll, disableScrollOnKeyboardHide, syncKeyboardFrame],
393
399
  );
394
400
 
401
+ const synchronize = useCallback(async () => {
402
+ await update();
403
+
404
+ runOnUI(() => {
405
+ "worklet";
406
+
407
+ scrollFromCurrentPosition();
408
+ })();
409
+ }, [update, scrollFromCurrentPosition]);
410
+
411
+ useImperativeHandle(
412
+ ref,
413
+ () => {
414
+ const scrollView = scrollViewRef.current;
415
+
416
+ const existingMethods = scrollView ? { ...scrollView } : {};
417
+
418
+ return {
419
+ ...existingMethods,
420
+
421
+ assureFocusedInputVisible: () => {
422
+ synchronize();
423
+ },
424
+ } as KeyboardAwareScrollViewRef;
425
+ },
426
+ [synchronize],
427
+ );
428
+
395
429
  useEffect(() => {
396
- runOnUI(performScrollWithPositionRestoration)(
397
- scrollBeforeKeyboardMovement.value,
398
- );
430
+ synchronize();
399
431
  }, [bottomOffset]);
400
432
 
401
433
  useAnimatedReaction(
@@ -7,5 +7,8 @@ export {
7
7
  } from "./KeyboardToolbar";
8
8
  export type { KeyboardAvoidingViewProps } from "./KeyboardAvoidingView";
9
9
  export type { KeyboardStickyViewProps } from "./KeyboardStickyView";
10
- export type { KeyboardAwareScrollViewProps } from "./KeyboardAwareScrollView";
10
+ export type {
11
+ KeyboardAwareScrollViewProps,
12
+ KeyboardAwareScrollViewRef,
13
+ } from "./KeyboardAwareScrollView";
11
14
  export type { KeyboardToolbarProps } from "./KeyboardToolbar";
package/src/context.ts CHANGED
@@ -35,6 +35,8 @@ export type KeyboardAnimationContext = {
35
35
  reanimated: ReanimatedContext;
36
36
  /** Layout of the focused `TextInput` represented as `SharedValue`. */
37
37
  layout: SharedValue<FocusedInputLayoutChangedEvent | null>;
38
+ /** Method for updating info about focused input layout. */
39
+ update: () => Promise<void>;
38
40
  /** Method for setting workletized keyboard handlers. */
39
41
  setKeyboardHandlers: (
40
42
  handlers: EventHandlerProcessed<never, never>,
@@ -71,6 +73,7 @@ const defaultContext: KeyboardAnimationContext = {
71
73
  height: DEFAULT_SHARED_VALUE,
72
74
  },
73
75
  layout: DEFAULT_LAYOUT,
76
+ update: Promise.resolve,
74
77
  setKeyboardHandlers: NESTED_NOOP,
75
78
  setInputHandlers: NESTED_NOOP,
76
79
  setEnabled: NOOP,
@@ -236,7 +236,10 @@ export function useKeyboardController() {
236
236
  export function useReanimatedFocusedInput() {
237
237
  const context = useKeyboardContext();
238
238
 
239
- return { input: context.layout };
239
+ return {
240
+ input: context.layout,
241
+ update: context.update,
242
+ };
240
243
  }
241
244
 
242
245
  /**
@@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
3
3
  import { KeyboardEvents } from "../../bindings";
4
4
  import { KeyboardController } from "../../module";
5
5
 
6
- import type { KeyboardState } from "../../types";
6
+ import type { IKeyboardState } from "../../types";
7
7
 
8
8
  const EVENTS = ["keyboardDidShow", "keyboardDidHide"] as const;
9
9
 
@@ -12,9 +12,9 @@ const getLatestState = () => ({
12
12
  isVisible: KeyboardController.isVisible(),
13
13
  });
14
14
 
15
- type KeyboardStateSelector<T> = (state: KeyboardState) => T;
15
+ type KeyboardStateSelector<T> = (state: IKeyboardState) => T;
16
16
 
17
- const defaultSelector: KeyboardStateSelector<KeyboardState> = (state) => state;
17
+ const defaultSelector: KeyboardStateSelector<IKeyboardState> = (state) => state;
18
18
 
19
19
  /**
20
20
  * React Hook that represents the current keyboard state on iOS and Android.
@@ -40,7 +40,7 @@ const defaultSelector: KeyboardStateSelector<KeyboardState> = (state) => state;
40
40
  * }
41
41
  * ```
42
42
  */
43
- function useKeyboardState<T = KeyboardState>(
43
+ function useKeyboardState<T = IKeyboardState>(
44
44
  selector: KeyboardStateSelector<T> = defaultSelector as KeyboardStateSelector<T>,
45
45
  ): T {
46
46
  const [state, setState] = useState<T>(() => selector(getLatestState()));
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export * from "./hooks";
5
5
  export * from "./constants";
6
6
  export * from "./module";
7
7
  export * from "./types";
8
+ export * from "./compat";
8
9
 
9
10
  export {
10
11
  KeyboardAvoidingView,
@@ -18,6 +19,7 @@ export type {
18
19
  KeyboardAvoidingViewProps,
19
20
  KeyboardStickyViewProps,
20
21
  KeyboardAwareScrollViewProps,
22
+ KeyboardAwareScrollViewRef,
21
23
  KeyboardToolbarProps,
22
24
  } from "./components";
23
25
  export { OverKeyboardView, KeyboardExtender } from "./views";
@@ -1,3 +1,4 @@
1
+ import codegenNativeCommands from "react-native/Libraries/Utilities/codegenNativeCommands";
1
2
  import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNativeComponent";
2
3
 
3
4
  import type { HostComponent } from "react-native";
@@ -66,6 +67,16 @@ export interface NativeProps extends ViewProps {
66
67
  onFocusedInputSelectionChanged?: DirectEventHandler<FocusedInputSelectionChangedEvent>;
67
68
  }
68
69
 
70
+ interface NativeCommands {
71
+ synchronizeFocusedInputLayout: (
72
+ viewRef: React.ElementRef<HostComponent<NativeProps>>,
73
+ ) => void;
74
+ }
75
+
76
+ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
77
+ supportedCommands: ["synchronizeFocusedInputLayout"],
78
+ });
79
+
69
80
  export default codegenNativeComponent<NativeProps>("KeyboardControllerView", {
70
81
  interfaceOnly: true,
71
82
  }) as HostComponent<NativeProps>;
@@ -1,6 +1,8 @@
1
1
  import type { EmitterSubscription } from "react-native";
2
2
 
3
- export type FocusedInputAvailableEvents = "focusDidSet";
3
+ export type FocusedInputAvailableEvents =
4
+ | "focusDidSet"
5
+ | "layoutDidSynchronize";
4
6
  export type FocusedInputEventData = {
5
7
  current: number;
6
8
  count: number;
@@ -23,7 +23,7 @@ export type KeyboardEventData = {
23
23
  /**
24
24
  * An object that represent current keyboard state.
25
25
  */
26
- export type KeyboardState = {
26
+ export type IKeyboardState = {
27
27
  /** Whether the keyboard is currently visible. */
28
28
  isVisible: boolean;
29
29
  } & KeyboardEventData;
@@ -85,10 +85,11 @@ export type KeyboardControllerModule = {
85
85
  // all platforms
86
86
  /**
87
87
  * Dismisses the active keyboard. Removes a focus by default, but allows to pass `{keepFocus: true}` to keep focus.
88
+ * If you want to close keyboard immediately pass `{animated: false}`.
88
89
  *
89
90
  * @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-controller#dismiss|docs} page for more details.
90
91
  */
91
- dismiss: (options?: DismissOptions) => Promise<void>;
92
+ dismiss: (options?: Partial<DismissOptions>) => Promise<void>;
92
93
  /**
93
94
  * Moves focus to the specified direction (`next`, `prev` or `current` to restore a focus).
94
95
  *
@@ -1,2 +0,0 @@
1
- connection.project.dir=../../../android
2
- eclipse.preferences.version=1
@@ -1,14 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>SchemeUserState</key>
6
- <dict>
7
- <key>KeyboardController.xcscheme_^#shared#^_</key>
8
- <dict>
9
- <key>orderHint</key>
10
- <integer>0</integer>
11
- </dict>
12
- </dict>
13
- </dict>
14
- </plist>
@@ -1,7 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <Workspace
3
- version = "1.0">
4
- <FileRef
5
- location = "self:">
6
- </FileRef>
7
- </Workspace>
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>IDEDidComputeMac32BitWarning</key>
6
- <true/>
7
- </dict>
8
- </plist>
@@ -1,32 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>SchemeUserState</key>
6
- <dict>
7
- <key>Tests.xcscheme_^#shared#^_</key>
8
- <dict>
9
- <key>orderHint</key>
10
- <integer>0</integer>
11
- </dict>
12
- </dict>
13
- <key>SuppressBuildableAutocreation</key>
14
- <dict>
15
- <key>083F2DF22B860D89004A1AD1</key>
16
- <dict>
17
- <key>primary</key>
18
- <true/>
19
- </dict>
20
- <key>083F2E022B860D8C004A1AD1</key>
21
- <dict>
22
- <key>primary</key>
23
- <true/>
24
- </dict>
25
- <key>083F2E0C2B860D8C004A1AD1</key>
26
- <dict>
27
- <key>primary</key>
28
- <true/>
29
- </dict>
30
- </dict>
31
- </dict>
32
- </plist>