react-native-keyboard-controller 1.14.5 → 1.15.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 (87) hide show
  1. package/android/build.gradle +3 -2
  2. package/android/gradle.properties +1 -0
  3. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +2 -2
  4. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt +35 -0
  5. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +18 -0
  6. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt +5 -0
  7. package/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt +9 -6
  8. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +13 -4
  9. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +2 -2
  10. package/ios/KeyboardControllerModule.mm +3 -3
  11. package/ios/KeyboardControllerModuleImpl.swift +66 -0
  12. package/ios/events/KeyboardEventEmitterPayload.swift +23 -0
  13. package/ios/extensions/UIKeyboardAppearance.swift +21 -0
  14. package/ios/extensions/UIKeyboardType.swift +30 -0
  15. package/ios/observers/KeyboardMovementObserver.swift +4 -14
  16. package/ios/protocols/TextInput.swift +5 -1
  17. package/ios/traversal/ViewHierarchyNavigator.swift +4 -1
  18. package/jest/index.js +20 -15
  19. package/lib/commonjs/animated.js +6 -0
  20. package/lib/commonjs/animated.js.map +1 -1
  21. package/lib/commonjs/bindings.js +5 -3
  22. package/lib/commonjs/bindings.js.map +1 -1
  23. package/lib/commonjs/bindings.native.js +2 -8
  24. package/lib/commonjs/bindings.native.js.map +1 -1
  25. package/lib/commonjs/components/KeyboardAvoidingView/index.js.map +1 -1
  26. package/lib/commonjs/components/KeyboardStickyView/index.js +3 -2
  27. package/lib/commonjs/components/KeyboardStickyView/index.js.map +1 -1
  28. package/lib/commonjs/components/KeyboardToolbar/index.js +14 -6
  29. package/lib/commonjs/components/KeyboardToolbar/index.js.map +1 -1
  30. package/lib/commonjs/hooks/index.js +3 -3
  31. package/lib/commonjs/hooks/index.js.map +1 -1
  32. package/lib/commonjs/hooks/useWindowDimensions/index.android.js +5 -0
  33. package/lib/commonjs/hooks/useWindowDimensions/index.android.js.map +1 -1
  34. package/lib/commonjs/index.js +12 -0
  35. package/lib/commonjs/index.js.map +1 -1
  36. package/lib/commonjs/internal.js +2 -2
  37. package/lib/commonjs/internal.js.map +1 -1
  38. package/lib/commonjs/module.js +45 -0
  39. package/lib/commonjs/module.js.map +1 -0
  40. package/lib/commonjs/specs/NativeKeyboardController.js.map +1 -1
  41. package/lib/commonjs/types.js.map +1 -1
  42. package/lib/module/animated.js +6 -0
  43. package/lib/module/animated.js.map +1 -1
  44. package/lib/module/bindings.js +4 -2
  45. package/lib/module/bindings.js.map +1 -1
  46. package/lib/module/bindings.native.js +1 -7
  47. package/lib/module/bindings.native.js.map +1 -1
  48. package/lib/module/components/KeyboardAvoidingView/index.js.map +1 -1
  49. package/lib/module/components/KeyboardStickyView/index.js +3 -2
  50. package/lib/module/components/KeyboardStickyView/index.js.map +1 -1
  51. package/lib/module/components/KeyboardToolbar/index.js +12 -4
  52. package/lib/module/components/KeyboardToolbar/index.js.map +1 -1
  53. package/lib/module/hooks/index.js +1 -1
  54. package/lib/module/hooks/index.js.map +1 -1
  55. package/lib/module/hooks/useWindowDimensions/index.android.js +5 -0
  56. package/lib/module/hooks/useWindowDimensions/index.android.js.map +1 -1
  57. package/lib/module/index.js +1 -0
  58. package/lib/module/index.js.map +1 -1
  59. package/lib/module/internal.js +2 -2
  60. package/lib/module/internal.js.map +1 -1
  61. package/lib/module/module.js +39 -0
  62. package/lib/module/module.js.map +1 -0
  63. package/lib/module/specs/NativeKeyboardController.js.map +1 -1
  64. package/lib/module/types.js.map +1 -1
  65. package/lib/typescript/bindings.d.ts +2 -2
  66. package/lib/typescript/bindings.native.d.ts +1 -2
  67. package/lib/typescript/components/KeyboardAvoidingView/index.d.ts +14 -24
  68. package/lib/typescript/components/KeyboardStickyView/index.d.ts +4 -0
  69. package/lib/typescript/components/KeyboardToolbar/index.d.ts +2 -1
  70. package/lib/typescript/index.d.ts +1 -0
  71. package/lib/typescript/module.d.ts +2 -0
  72. package/lib/typescript/specs/NativeKeyboardController.d.ts +1 -1
  73. package/lib/typescript/types.d.ts +14 -3
  74. package/package.json +1 -1
  75. package/src/animated.tsx +6 -0
  76. package/src/bindings.native.ts +1 -8
  77. package/src/bindings.ts +4 -2
  78. package/src/components/KeyboardAvoidingView/index.tsx +27 -11
  79. package/src/components/KeyboardStickyView/index.tsx +12 -3
  80. package/src/components/KeyboardToolbar/index.tsx +11 -4
  81. package/src/hooks/index.ts +1 -1
  82. package/src/hooks/useWindowDimensions/index.android.ts +5 -0
  83. package/src/index.ts +1 -0
  84. package/src/internal.ts +2 -2
  85. package/src/module.ts +50 -0
  86. package/src/specs/NativeKeyboardController.ts +1 -1
  87. package/src/types.ts +16 -2
@@ -1,15 +1,7 @@
1
1
  import React from "react";
2
2
  import { View } from "react-native";
3
3
  import type { ViewProps } from "react-native";
4
- export type KeyboardAvoidingViewProps = {
5
- /**
6
- * Specify how to react to the presence of the keyboard.
7
- */
8
- behavior?: "height" | "position" | "padding";
9
- /**
10
- * Style of the content container when `behavior` is 'position'.
11
- */
12
- contentContainerStyle?: ViewProps["style"];
4
+ export type KeyboardAvoidingViewBaseProps = {
13
5
  /**
14
6
  * Controls whether this `KeyboardAvoidingView` instance should take effect.
15
7
  * This is useful when more than one is on the screen. Defaults to true.
@@ -21,30 +13,28 @@ export type KeyboardAvoidingViewProps = {
21
13
  */
22
14
  keyboardVerticalOffset?: number;
23
15
  } & ViewProps;
24
- /**
25
- * View that moves out of the way when the keyboard appears by automatically
26
- * adjusting its height, position, or bottom padding.
27
- */
28
- declare const KeyboardAvoidingView: React.ForwardRefExoticComponent<{
16
+ export type KeyboardAvoidingViewProps = KeyboardAvoidingViewBaseProps & ({
29
17
  /**
30
18
  * Specify how to react to the presence of the keyboard.
31
19
  */
32
- behavior?: "height" | "position" | "padding";
20
+ behavior?: "position";
33
21
  /**
34
22
  * Style of the content container when `behavior` is 'position'.
35
23
  */
36
24
  contentContainerStyle?: ViewProps["style"];
25
+ } | {
37
26
  /**
38
- * Controls whether this `KeyboardAvoidingView` instance should take effect.
39
- * This is useful when more than one is on the screen. Defaults to true.
27
+ * Specify how to react to the presence of the keyboard.
40
28
  */
41
- enabled?: boolean;
29
+ behavior?: "height" | "padding";
42
30
  /**
43
- * Distance between the top of the user screen and the React Native view. This
44
- * may be non-zero in some cases. Defaults to 0.
31
+ * `contentContainerStyle` is not allowed for these behaviors.
45
32
  */
46
- keyboardVerticalOffset?: number;
47
- } & ViewProps & {
48
- children?: React.ReactNode | undefined;
49
- } & React.RefAttributes<View>>;
33
+ contentContainerStyle?: never;
34
+ });
35
+ /**
36
+ * View that moves out of the way when the keyboard appears by automatically
37
+ * adjusting its height, position, or bottom padding.
38
+ */
39
+ declare const KeyboardAvoidingView: React.ForwardRefExoticComponent<React.PropsWithChildren<KeyboardAvoidingViewProps> & React.RefAttributes<View>>;
50
40
  export default KeyboardAvoidingView;
@@ -14,6 +14,8 @@ export type KeyboardStickyViewProps = {
14
14
  */
15
15
  opened?: number;
16
16
  };
17
+ /** Controls whether this `KeyboardStickyView` instance should take effect. Default is `true` */
18
+ enabled?: boolean;
17
19
  } & ViewProps;
18
20
  declare const KeyboardStickyView: React.ForwardRefExoticComponent<{
19
21
  /**
@@ -29,6 +31,8 @@ declare const KeyboardStickyView: React.ForwardRefExoticComponent<{
29
31
  */
30
32
  opened?: number;
31
33
  };
34
+ /** Controls whether this `KeyboardStickyView` instance should take effect. Default is `true` */
35
+ enabled?: boolean;
32
36
  } & ViewProps & {
33
37
  children?: React.ReactNode | undefined;
34
38
  } & React.RefAttributes<View>>;
@@ -3,6 +3,7 @@ import Arrow from "./Arrow";
3
3
  import Button from "./Button";
4
4
  import { colors } from "./colors";
5
5
  import type { HEX, KeyboardToolbarTheme } from "./types";
6
+ import type { KeyboardStickyViewProps } from "../KeyboardStickyView";
6
7
  import type { ReactNode } from "react";
7
8
  import type { GestureResponderEvent, ViewProps } from "react-native";
8
9
  export type KeyboardToolbarProps = Omit<ViewProps, "style" | "testID" | "children"> & {
@@ -41,7 +42,7 @@ export type KeyboardToolbarProps = Omit<ViewProps, "style" | "testID" | "childre
41
42
  * A value for container opacity in hexadecimal format (e.g. `ff`). Default value is `ff`.
42
43
  */
43
44
  opacity?: HEX;
44
- };
45
+ } & Pick<KeyboardStickyViewProps, "offset" | "enabled">;
45
46
  /**
46
47
  * `KeyboardToolbar` is a component that is shown above the keyboard with `Prev`/`Next` and
47
48
  * `Done` buttons.
@@ -3,6 +3,7 @@ export * from "./animated";
3
3
  export * from "./context";
4
4
  export * from "./hooks";
5
5
  export * from "./constants";
6
+ export * from "./module";
6
7
  export * from "./types";
7
8
  export { KeyboardAvoidingView, KeyboardStickyView, KeyboardAwareScrollView, KeyboardToolbar, DefaultKeyboardToolbarTheme, } from "./components";
8
9
  export type { KeyboardAvoidingViewProps, KeyboardStickyViewProps, KeyboardAwareScrollViewProps, KeyboardToolbarProps, } from "./components";
@@ -0,0 +1,2 @@
1
+ import type { KeyboardControllerModule } from "./types";
2
+ export declare const KeyboardController: KeyboardControllerModule;
@@ -3,7 +3,7 @@ export interface Spec extends TurboModule {
3
3
  readonly getConstants: () => {};
4
4
  setInputMode(mode: number): void;
5
5
  setDefaultMode(): void;
6
- dismiss(): void;
6
+ dismiss(keepFocus: boolean): void;
7
7
  setFocusTo(direction: string): void;
8
8
  addListener: (eventName: string) => void;
9
9
  removeListeners: (count: number) => void;
@@ -1,5 +1,5 @@
1
1
  import type { PropsWithChildren } from "react";
2
- import type { EmitterSubscription, NativeSyntheticEvent, ViewProps } from "react-native";
2
+ import type { EmitterSubscription, NativeSyntheticEvent, TextInputProps, ViewProps } from "react-native";
3
3
  export type NativeEvent = {
4
4
  progress: number;
5
5
  height: number;
@@ -78,22 +78,33 @@ export type OverKeyboardViewProps = PropsWithChildren<{
78
78
  visible: boolean;
79
79
  }>;
80
80
  export type Direction = "next" | "prev" | "current";
81
+ export type DismissOptions = {
82
+ keepFocus: boolean;
83
+ };
81
84
  export type KeyboardControllerModule = {
82
85
  setDefaultMode: () => void;
83
86
  setInputMode: (mode: number) => void;
84
- dismiss: () => void;
87
+ dismiss: (options?: DismissOptions) => Promise<void>;
85
88
  setFocusTo: (direction: Direction) => void;
89
+ isVisible: () => boolean;
90
+ state: () => KeyboardEventData | null;
86
91
  };
87
92
  export type KeyboardControllerNativeModule = {
93
+ setDefaultMode: () => void;
94
+ setInputMode: (mode: number) => void;
95
+ dismiss: (keepFocus: boolean) => void;
96
+ setFocusTo: (direction: Direction) => void;
88
97
  addListener: (eventName: string) => void;
89
98
  removeListeners: (count: number) => void;
90
- } & KeyboardControllerModule;
99
+ };
91
100
  export type KeyboardControllerEvents = "keyboardWillShow" | "keyboardDidShow" | "keyboardWillHide" | "keyboardDidHide";
92
101
  export type KeyboardEventData = {
93
102
  height: number;
94
103
  duration: number;
95
104
  timestamp: number;
96
105
  target: number;
106
+ type: NonNullable<TextInputProps["keyboardType"]>;
107
+ appearance: NonNullable<TextInputProps["keyboardAppearance"]>;
97
108
  };
98
109
  export type KeyboardEventsModule = {
99
110
  addListener: (name: KeyboardControllerEvents, cb: (e: KeyboardEventData) => void) => EmitterSubscription;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-keyboard-controller",
3
- "version": "1.14.5",
3
+ "version": "1.15.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
@@ -169,6 +169,11 @@ export const KeyboardProvider = ({
169
169
 
170
170
  updateSharedValues(event, ["android", "ios"]);
171
171
  },
172
+ onKeyboardMoveEnd: (event: NativeEvent) => {
173
+ "worklet";
174
+
175
+ updateSharedValues(event, ["android"]);
176
+ },
172
177
  },
173
178
  [],
174
179
  );
@@ -213,6 +218,7 @@ export const KeyboardProvider = ({
213
218
  onKeyboardMoveStart={OS === "ios" ? onKeyboardMove : undefined}
214
219
  onKeyboardMove={OS === "android" ? onKeyboardMove : undefined}
215
220
  onKeyboardMoveInteractive={onKeyboardMove}
221
+ onKeyboardMoveEnd={OS === "android" ? onKeyboardMove : undefined}
216
222
  onFocusedInputLayoutChangedReanimated={inputLayoutHandler}
217
223
  >
218
224
  {children}
@@ -2,7 +2,6 @@ import { NativeEventEmitter, Platform } from "react-native";
2
2
 
3
3
  import type {
4
4
  FocusedInputEventsModule,
5
- KeyboardControllerModule,
6
5
  KeyboardControllerNativeModule,
7
6
  KeyboardControllerProps,
8
7
  KeyboardEventsModule,
@@ -40,13 +39,7 @@ export const KeyboardEvents: KeyboardEventsModule = {
40
39
  addListener: (name, cb) =>
41
40
  eventEmitter.addListener(KEYBOARD_CONTROLLER_NAMESPACE + name, cb),
42
41
  };
43
- export const KeyboardController: KeyboardControllerModule = {
44
- setDefaultMode: KeyboardControllerNative.setDefaultMode,
45
- setInputMode: KeyboardControllerNative.setInputMode,
46
- setFocusTo: KeyboardControllerNative.setFocusTo,
47
- // additional function is needed because of this https://github.com/kirillzyusko/react-native-keyboard-controller/issues/684
48
- dismiss: () => KeyboardControllerNative.dismiss(),
49
- };
42
+
50
43
  /**
51
44
  * This API is not documented, it's for internal usage only (for now), and is a subject to potential breaking changes in future.
52
45
  * Use it with cautious.
package/src/bindings.ts CHANGED
@@ -2,7 +2,7 @@ import { View } from "react-native";
2
2
 
3
3
  import type {
4
4
  FocusedInputEventsModule,
5
- KeyboardControllerModule,
5
+ KeyboardControllerNativeModule,
6
6
  KeyboardControllerProps,
7
7
  KeyboardEventsModule,
8
8
  KeyboardGestureAreaProps,
@@ -13,11 +13,13 @@ import type { EmitterSubscription } from "react-native";
13
13
 
14
14
  const NOOP = () => {};
15
15
 
16
- export const KeyboardController: KeyboardControllerModule = {
16
+ export const KeyboardControllerNative: KeyboardControllerNativeModule = {
17
17
  setDefaultMode: NOOP,
18
18
  setInputMode: NOOP,
19
19
  dismiss: NOOP,
20
20
  setFocusTo: NOOP,
21
+ addListener: NOOP,
22
+ removeListeners: NOOP,
21
23
  };
22
24
  export const KeyboardEvents: KeyboardEventsModule = {
23
25
  addListener: () => ({ remove: NOOP } as EmitterSubscription),
@@ -14,17 +14,7 @@ import { useKeyboardAnimation } from "./hooks";
14
14
 
15
15
  import type { LayoutRectangle, ViewProps } from "react-native";
16
16
 
17
- export type KeyboardAvoidingViewProps = {
18
- /**
19
- * Specify how to react to the presence of the keyboard.
20
- */
21
- behavior?: "height" | "position" | "padding";
22
-
23
- /**
24
- * Style of the content container when `behavior` is 'position'.
25
- */
26
- contentContainerStyle?: ViewProps["style"];
27
-
17
+ export type KeyboardAvoidingViewBaseProps = {
28
18
  /**
29
19
  * Controls whether this `KeyboardAvoidingView` instance should take effect.
30
20
  * This is useful when more than one is on the screen. Defaults to true.
@@ -38,6 +28,32 @@ export type KeyboardAvoidingViewProps = {
38
28
  keyboardVerticalOffset?: number;
39
29
  } & ViewProps;
40
30
 
31
+ export type KeyboardAvoidingViewProps = KeyboardAvoidingViewBaseProps &
32
+ (
33
+ | {
34
+ /**
35
+ * Specify how to react to the presence of the keyboard.
36
+ */
37
+ behavior?: "position";
38
+
39
+ /**
40
+ * Style of the content container when `behavior` is 'position'.
41
+ */
42
+ contentContainerStyle?: ViewProps["style"];
43
+ }
44
+ | {
45
+ /**
46
+ * Specify how to react to the presence of the keyboard.
47
+ */
48
+ behavior?: "height" | "padding";
49
+
50
+ /**
51
+ * `contentContainerStyle` is not allowed for these behaviors.
52
+ */
53
+ contentContainerStyle?: never;
54
+ }
55
+ );
56
+
41
57
  const defaultLayout: LayoutRectangle = {
42
58
  x: 0,
43
59
  y: 0,
@@ -22,6 +22,9 @@ export type KeyboardStickyViewProps = {
22
22
  */
23
23
  opened?: number;
24
24
  };
25
+
26
+ /** Controls whether this `KeyboardStickyView` instance should take effect. Default is `true` */
27
+ enabled?: boolean;
25
28
  } & ViewProps;
26
29
 
27
30
  const KeyboardStickyView = forwardRef<
@@ -29,7 +32,13 @@ const KeyboardStickyView = forwardRef<
29
32
  React.PropsWithChildren<KeyboardStickyViewProps>
30
33
  >(
31
34
  (
32
- { children, offset: { closed = 0, opened = 0 } = {}, style, ...props },
35
+ {
36
+ children,
37
+ offset: { closed = 0, opened = 0 } = {},
38
+ style,
39
+ enabled = true,
40
+ ...props
41
+ },
33
42
  ref,
34
43
  ) => {
35
44
  const { height, progress } = useReanimatedKeyboardAnimation();
@@ -38,9 +47,9 @@ const KeyboardStickyView = forwardRef<
38
47
  const offset = interpolate(progress.value, [0, 1], [closed, opened]);
39
48
 
40
49
  return {
41
- transform: [{ translateY: height.value + offset }],
50
+ transform: [{ translateY: enabled ? height.value + offset : closed }],
42
51
  };
43
- }, [closed, opened]);
52
+ }, [closed, opened, enabled]);
44
53
 
45
54
  const styles = useMemo(
46
55
  () => [style, stickyViewStyle],
@@ -1,7 +1,8 @@
1
1
  import React, { useCallback, useEffect, useMemo, useState } from "react";
2
2
  import { StyleSheet, Text, View } from "react-native";
3
3
 
4
- import { FocusedInputEvents, KeyboardController } from "../../bindings";
4
+ import { FocusedInputEvents } from "../../bindings";
5
+ import { KeyboardController } from "../../module";
5
6
  import useColorScheme from "../hooks/useColorScheme";
6
7
  import KeyboardStickyView from "../KeyboardStickyView";
7
8
 
@@ -10,6 +11,7 @@ import Button from "./Button";
10
11
  import { colors } from "./colors";
11
12
 
12
13
  import type { HEX, KeyboardToolbarTheme } from "./types";
14
+ import type { KeyboardStickyViewProps } from "../KeyboardStickyView";
13
15
  import type { ReactNode } from "react";
14
16
  import type { GestureResponderEvent, ViewProps } from "react-native";
15
17
 
@@ -52,7 +54,7 @@ export type KeyboardToolbarProps = Omit<
52
54
  * A value for container opacity in hexadecimal format (e.g. `ff`). Default value is `ff`.
53
55
  */
54
56
  opacity?: HEX;
55
- };
57
+ } & Pick<KeyboardStickyViewProps, "offset" | "enabled">;
56
58
 
57
59
  const TEST_ID_KEYBOARD_TOOLBAR = "keyboard.toolbar";
58
60
  const TEST_ID_KEYBOARD_TOOLBAR_PREVIOUS = `${TEST_ID_KEYBOARD_TOOLBAR}.previous`;
@@ -62,7 +64,6 @@ const TEST_ID_KEYBOARD_TOOLBAR_DONE = `${TEST_ID_KEYBOARD_TOOLBAR}.done`;
62
64
 
63
65
  const KEYBOARD_TOOLBAR_HEIGHT = 42;
64
66
  const DEFAULT_OPACITY: HEX = "FF";
65
- const offset = { closed: KEYBOARD_TOOLBAR_HEIGHT };
66
67
 
67
68
  /**
68
69
  * `KeyboardToolbar` is a component that is shown above the keyboard with `Prev`/`Next` and
@@ -80,6 +81,8 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = ({
80
81
  onDoneCallback,
81
82
  blur = null,
82
83
  opacity = DEFAULT_OPACITY,
84
+ offset: { closed = 0, opened = 0 } = {},
85
+ enabled = true,
83
86
  ...rest
84
87
  }) => {
85
88
  const colorScheme = useColorScheme();
@@ -110,6 +113,10 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = ({
110
113
  ],
111
114
  [colorScheme, opacity, theme],
112
115
  );
116
+ const offset = useMemo(
117
+ () => ({ closed: closed + KEYBOARD_TOOLBAR_HEIGHT, opened }),
118
+ [closed, opened],
119
+ );
113
120
  const ButtonContainer = button || Button;
114
121
  const IconContainer = icon || Arrow;
115
122
 
@@ -145,7 +152,7 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = ({
145
152
  );
146
153
 
147
154
  return (
148
- <KeyboardStickyView offset={offset}>
155
+ <KeyboardStickyView enabled={enabled} offset={offset}>
149
156
  <View {...rest} style={toolbarStyle} testID={TEST_ID_KEYBOARD_TOOLBAR}>
150
157
  {blur}
151
158
  {showArrows && (
@@ -1,8 +1,8 @@
1
1
  import { useEffect, useLayoutEffect } from "react";
2
2
 
3
- import { KeyboardController } from "../bindings";
4
3
  import { AndroidSoftInputModes } from "../constants";
5
4
  import { useKeyboardContext } from "../context";
5
+ import { KeyboardController } from "../module";
6
6
 
7
7
  import type { AnimatedContext, ReanimatedContext } from "../context";
8
8
  import type { FocusedInputHandler, KeyboardHandler } from "../types";
@@ -24,6 +24,11 @@ export const useWindowDimensions = () => {
24
24
  },
25
25
  );
26
26
 
27
+ // we might have missed an update between reading a value in render and
28
+ // `addListener` in this handler, so we set it here. If there was
29
+ // no change, React will filter out this update as a no-op.
30
+ setDimensions(initialDimensions);
31
+
27
32
  return () => {
28
33
  subscription.remove();
29
34
  };
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ export * from "./animated";
3
3
  export * from "./context";
4
4
  export * from "./hooks";
5
5
  export * from "./constants";
6
+ export * from "./module";
6
7
  export * from "./types";
7
8
 
8
9
  export {
package/src/internal.ts CHANGED
@@ -48,8 +48,8 @@ export function useEventHandlerRegistration<
48
48
  if (viewTagRef.current) {
49
49
  attachWorkletHandlers();
50
50
  } else {
51
- // view may not be mounted yet - defer registration to next event loop
52
- setImmediate(attachWorkletHandlers);
51
+ // view may not be mounted yet - defer registration until call-stack becomes empty
52
+ queueMicrotask(attachWorkletHandlers);
53
53
  }
54
54
 
55
55
  return () => {
package/src/module.ts ADDED
@@ -0,0 +1,50 @@
1
+ import { KeyboardControllerNative, KeyboardEvents } from "./bindings";
2
+
3
+ import type {
4
+ DismissOptions,
5
+ KeyboardControllerModule,
6
+ KeyboardEventData,
7
+ } from "./types";
8
+
9
+ let isClosed = false;
10
+ let lastEvent: KeyboardEventData | null = null;
11
+
12
+ KeyboardEvents.addListener("keyboardDidHide", (e) => {
13
+ isClosed = true;
14
+ lastEvent = e;
15
+ });
16
+
17
+ KeyboardEvents.addListener("keyboardDidShow", (e) => {
18
+ isClosed = false;
19
+ lastEvent = e;
20
+ });
21
+
22
+ const dismiss = async (
23
+ { keepFocus }: DismissOptions = { keepFocus: false },
24
+ ): Promise<void> => {
25
+ return new Promise((resolve) => {
26
+ if (isClosed) {
27
+ resolve();
28
+
29
+ return;
30
+ }
31
+
32
+ const subscription = KeyboardEvents.addListener("keyboardDidHide", () => {
33
+ resolve(undefined);
34
+ subscription.remove();
35
+ });
36
+
37
+ KeyboardControllerNative.dismiss(keepFocus);
38
+ });
39
+ };
40
+ const isVisible = () => !isClosed;
41
+ const state = () => lastEvent;
42
+
43
+ export const KeyboardController: KeyboardControllerModule = {
44
+ setDefaultMode: KeyboardControllerNative.setDefaultMode,
45
+ setInputMode: KeyboardControllerNative.setInputMode,
46
+ setFocusTo: KeyboardControllerNative.setFocusTo,
47
+ dismiss,
48
+ isVisible,
49
+ state,
50
+ };
@@ -8,7 +8,7 @@ export interface Spec extends TurboModule {
8
8
  // methods
9
9
  setInputMode(mode: number): void;
10
10
  setDefaultMode(): void;
11
- dismiss(): void;
11
+ dismiss(keepFocus: boolean): void;
12
12
  setFocusTo(direction: string): void;
13
13
 
14
14
  // event emitter
package/src/types.ts CHANGED
@@ -2,6 +2,7 @@ import type { PropsWithChildren } from "react";
2
2
  import type {
3
3
  EmitterSubscription,
4
4
  NativeSyntheticEvent,
5
+ TextInputProps,
5
6
  ViewProps,
6
7
  } from "react-native";
7
8
 
@@ -114,19 +115,30 @@ export type OverKeyboardViewProps = PropsWithChildren<{
114
115
  }>;
115
116
 
116
117
  export type Direction = "next" | "prev" | "current";
118
+ export type DismissOptions = {
119
+ keepFocus: boolean;
120
+ };
117
121
  export type KeyboardControllerModule = {
118
122
  // android only
119
123
  setDefaultMode: () => void;
120
124
  setInputMode: (mode: number) => void;
121
125
  // all platforms
122
- dismiss: () => void;
126
+ dismiss: (options?: DismissOptions) => Promise<void>;
123
127
  setFocusTo: (direction: Direction) => void;
128
+ isVisible: () => boolean;
129
+ state: () => KeyboardEventData | null;
124
130
  };
125
131
  export type KeyboardControllerNativeModule = {
132
+ // android only
133
+ setDefaultMode: () => void;
134
+ setInputMode: (mode: number) => void;
135
+ // all platforms
136
+ dismiss: (keepFocus: boolean) => void;
137
+ setFocusTo: (direction: Direction) => void;
126
138
  // native event module stuff
127
139
  addListener: (eventName: string) => void;
128
140
  removeListeners: (count: number) => void;
129
- } & KeyboardControllerModule;
141
+ };
130
142
 
131
143
  // Event module declarations
132
144
  export type KeyboardControllerEvents =
@@ -139,6 +151,8 @@ export type KeyboardEventData = {
139
151
  duration: number;
140
152
  timestamp: number;
141
153
  target: number;
154
+ type: NonNullable<TextInputProps["keyboardType"]>;
155
+ appearance: NonNullable<TextInputProps["keyboardAppearance"]>;
142
156
  };
143
157
  export type KeyboardEventsModule = {
144
158
  addListener: (