react-native-keyboard-controller 1.12.6 → 1.13.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 (152) hide show
  1. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardGestureAreaViewManager.kt +5 -0
  2. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ReactContext.kt +13 -0
  3. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +0 -10
  4. package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/Interpolator.kt +2 -1
  5. package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/IosInterpolator.kt +2 -1
  6. package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/LinearInterpolator.kt +1 -0
  7. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt +15 -8
  8. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt +71 -59
  9. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardGestureAreaViewManagerImpl.kt +4 -0
  10. package/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt +80 -0
  11. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/StatusBarManagerCompatModuleImpl.kt +6 -12
  12. package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +31 -4
  13. package/android/src/main/java/com/reactnativekeyboardcontroller/views/KeyboardGestureAreaReactViewGroup.kt +7 -0
  14. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardGestureAreaViewManager.kt +5 -0
  15. package/ios/core/KeyboardAnimation.swift +1 -1
  16. package/ios/core/TimingAnimation.swift +108 -0
  17. package/ios/extensions/UIApplication.swift +29 -0
  18. package/ios/extensions/UIView.swift +10 -0
  19. package/ios/observers/FocusedInputObserver.swift +2 -0
  20. package/ios/observers/KeyboardMovementObserver.swift +19 -10
  21. package/ios/traversal/ViewHierarchyNavigator.swift +1 -1
  22. package/ios/views/KeyboardControllerView.mm +30 -39
  23. package/lib/commonjs/animated.js +16 -30
  24. package/lib/commonjs/animated.js.map +1 -1
  25. package/lib/commonjs/bindings.js.map +1 -1
  26. package/lib/commonjs/bindings.native.js.map +1 -1
  27. package/lib/commonjs/components/KeyboardAvoidingView/hooks.js +3 -14
  28. package/lib/commonjs/components/KeyboardAvoidingView/hooks.js.map +1 -1
  29. package/lib/commonjs/components/KeyboardAvoidingView/index.js +5 -4
  30. package/lib/commonjs/components/KeyboardAvoidingView/index.js.map +1 -1
  31. package/lib/commonjs/components/KeyboardAwareScrollView/index.js +42 -16
  32. package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
  33. package/lib/commonjs/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.js +1 -0
  34. package/lib/commonjs/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.js.map +1 -1
  35. package/lib/commonjs/components/KeyboardAwareScrollView/utils.js.map +1 -1
  36. package/lib/commonjs/components/KeyboardStickyView/index.js +2 -2
  37. package/lib/commonjs/components/KeyboardStickyView/index.js.map +1 -1
  38. package/lib/commonjs/components/KeyboardToolbar/Arrow.js +2 -2
  39. package/lib/commonjs/components/KeyboardToolbar/Arrow.js.map +1 -1
  40. package/lib/commonjs/components/KeyboardToolbar/Button.js +12 -12
  41. package/lib/commonjs/components/KeyboardToolbar/Button.js.map +1 -1
  42. package/lib/commonjs/components/KeyboardToolbar/index.js +33 -27
  43. package/lib/commonjs/components/KeyboardToolbar/index.js.map +1 -1
  44. package/lib/commonjs/components/index.js +2 -2
  45. package/lib/commonjs/components/index.js.map +1 -1
  46. package/lib/commonjs/context.js +3 -2
  47. package/lib/commonjs/context.js.map +1 -1
  48. package/lib/commonjs/event-handler.js +19 -0
  49. package/lib/commonjs/event-handler.js.map +1 -0
  50. package/lib/commonjs/event-handler.web.js +10 -0
  51. package/lib/commonjs/event-handler.web.js.map +1 -0
  52. package/lib/commonjs/event-mappings.js +9 -0
  53. package/lib/commonjs/event-mappings.js.map +1 -0
  54. package/lib/commonjs/hooks/index.js +8 -21
  55. package/lib/commonjs/hooks/index.js.map +1 -1
  56. package/lib/commonjs/hooks/useSyncEffect/index.js +37 -0
  57. package/lib/commonjs/hooks/useSyncEffect/index.js.map +1 -0
  58. package/lib/commonjs/internal.js +20 -43
  59. package/lib/commonjs/internal.js.map +1 -1
  60. package/lib/commonjs/monkey-patch.android.js +1 -1
  61. package/lib/commonjs/monkey-patch.android.js.map +1 -1
  62. package/lib/commonjs/monkey-patch.js.map +1 -1
  63. package/lib/commonjs/reanimated.js +1 -3
  64. package/lib/commonjs/reanimated.js.map +1 -1
  65. package/lib/commonjs/reanimated.native.js +1 -35
  66. package/lib/commonjs/reanimated.native.js.map +1 -1
  67. package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js +1 -1
  68. package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
  69. package/lib/commonjs/specs/KeyboardGestureAreaNativeComponent.js +1 -1
  70. package/lib/commonjs/specs/KeyboardGestureAreaNativeComponent.js.map +1 -1
  71. package/lib/commonjs/types.js.map +1 -1
  72. package/lib/module/animated.js +17 -32
  73. package/lib/module/animated.js.map +1 -1
  74. package/lib/module/bindings.js.map +1 -1
  75. package/lib/module/bindings.native.js.map +1 -1
  76. package/lib/module/components/KeyboardAvoidingView/hooks.js +3 -14
  77. package/lib/module/components/KeyboardAvoidingView/hooks.js.map +1 -1
  78. package/lib/module/components/KeyboardAvoidingView/index.js +4 -3
  79. package/lib/module/components/KeyboardAvoidingView/index.js.map +1 -1
  80. package/lib/module/components/KeyboardAwareScrollView/index.js +41 -15
  81. package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
  82. package/lib/module/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.js +1 -0
  83. package/lib/module/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.js.map +1 -1
  84. package/lib/module/components/KeyboardAwareScrollView/utils.js.map +1 -1
  85. package/lib/module/components/KeyboardStickyView/index.js +1 -1
  86. package/lib/module/components/KeyboardToolbar/Button.js +10 -10
  87. package/lib/module/components/KeyboardToolbar/Button.js.map +1 -1
  88. package/lib/module/components/KeyboardToolbar/index.js +31 -25
  89. package/lib/module/components/KeyboardToolbar/index.js.map +1 -1
  90. package/lib/module/context.js +3 -2
  91. package/lib/module/context.js.map +1 -1
  92. package/lib/module/event-handler.js +13 -0
  93. package/lib/module/event-handler.js.map +1 -0
  94. package/lib/module/event-handler.web.js +5 -0
  95. package/lib/module/event-handler.web.js.map +1 -0
  96. package/lib/module/event-mappings.js +3 -0
  97. package/lib/module/event-mappings.js.map +1 -0
  98. package/lib/module/hooks/index.js +7 -21
  99. package/lib/module/hooks/index.js.map +1 -1
  100. package/lib/module/hooks/useSyncEffect/index.js +31 -0
  101. package/lib/module/hooks/useSyncEffect/index.js.map +1 -0
  102. package/lib/module/internal.js +21 -44
  103. package/lib/module/internal.js.map +1 -1
  104. package/lib/module/monkey-patch.js.map +1 -1
  105. package/lib/module/reanimated.js +0 -2
  106. package/lib/module/reanimated.js.map +1 -1
  107. package/lib/module/reanimated.native.js +0 -32
  108. package/lib/module/reanimated.native.js.map +1 -1
  109. package/lib/module/specs/KeyboardGestureAreaNativeComponent.js.map +1 -1
  110. package/lib/module/types.js.map +1 -1
  111. package/lib/typescript/components/KeyboardToolbar/Button.d.ts +2 -2
  112. package/lib/typescript/components/KeyboardToolbar/index.d.ts +4 -3
  113. package/lib/typescript/context.d.ts +3 -3
  114. package/lib/typescript/event-handler.d.ts +3 -0
  115. package/lib/typescript/event-mappings.d.ts +2 -0
  116. package/lib/typescript/hooks/index.d.ts +1 -1
  117. package/lib/typescript/hooks/useSyncEffect/index.d.ts +14 -0
  118. package/lib/typescript/internal.d.ts +5 -19
  119. package/lib/typescript/reanimated.d.ts +1 -3
  120. package/lib/typescript/reanimated.native.d.ts +1 -3
  121. package/lib/typescript/specs/KeyboardGestureAreaNativeComponent.d.ts +2 -1
  122. package/lib/typescript/types.d.ts +6 -0
  123. package/package.json +14 -7
  124. package/src/animated.tsx +21 -43
  125. package/src/bindings.native.ts +1 -0
  126. package/src/bindings.ts +1 -0
  127. package/src/components/KeyboardAvoidingView/hooks.ts +3 -13
  128. package/src/components/KeyboardAvoidingView/index.tsx +2 -1
  129. package/src/components/KeyboardAwareScrollView/index.tsx +51 -13
  130. package/src/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.ts +2 -0
  131. package/src/components/KeyboardAwareScrollView/utils.ts +1 -0
  132. package/src/components/KeyboardToolbar/Button.tsx +11 -11
  133. package/src/components/KeyboardToolbar/index.tsx +45 -25
  134. package/src/context.ts +8 -6
  135. package/src/event-handler.js +15 -0
  136. package/src/event-handler.ts +8 -0
  137. package/src/event-handler.web.js +5 -0
  138. package/src/event-mappings.ts +14 -0
  139. package/src/hooks/index.ts +9 -16
  140. package/src/hooks/useSyncEffect/index.ts +36 -0
  141. package/src/internal.ts +34 -53
  142. package/src/monkey-patch.ts +1 -0
  143. package/src/reanimated.native.ts +0 -50
  144. package/src/reanimated.ts +1 -12
  145. package/src/specs/KeyboardGestureAreaNativeComponent.ts +5 -1
  146. package/src/types.ts +6 -0
  147. package/lib/commonjs/utils.js +0 -9
  148. package/lib/commonjs/utils.js.map +0 -1
  149. package/lib/module/utils.js +0 -2
  150. package/lib/module/utils.js.map +0 -1
  151. package/lib/typescript/utils.d.ts +0 -1
  152. package/src/utils.ts +0 -1
package/src/animated.tsx CHANGED
@@ -1,16 +1,16 @@
1
- import React, { useEffect, useMemo, useState } from "react";
1
+ /* eslint react/jsx-sort-props: off */
2
+ import React, { useEffect, useMemo, useRef, useState } from "react";
2
3
  import { Animated, Platform, StyleSheet } from "react-native";
3
4
  import Reanimated, { useSharedValue } from "react-native-reanimated";
4
5
 
5
6
  import { KeyboardControllerView } from "./bindings";
6
7
  import { KeyboardContext } from "./context";
7
- import { useAnimatedValue, useSharedHandlers } from "./internal";
8
+ import { focusedInputEventsMap, keyboardEventsMap } from "./event-mappings";
9
+ import { useAnimatedValue, useEventHandlerRegistration } from "./internal";
8
10
  import { applyMonkeyPatch, revertMonkeyPatch } from "./monkey-patch";
9
11
  import {
10
12
  useAnimatedKeyboardHandler,
11
13
  useFocusedInputLayoutHandler,
12
- useFocusedInputSelectionHandler,
13
- useFocusedInputTextHandler,
14
14
  } from "./reanimated";
15
15
 
16
16
  import type { KeyboardAnimationContext } from "./context";
@@ -24,9 +24,7 @@ import type {
24
24
  import type { ViewStyle } from "react-native";
25
25
 
26
26
  const KeyboardControllerViewAnimated = Reanimated.createAnimatedComponent(
27
- Animated.createAnimatedComponent(
28
- KeyboardControllerView,
29
- ) as React.FC<KeyboardControllerProps>,
27
+ Animated.createAnimatedComponent(KeyboardControllerView),
30
28
  );
31
29
 
32
30
  type Styles = {
@@ -83,6 +81,8 @@ export const KeyboardProvider = ({
83
81
  navigationBarTranslucent,
84
82
  enabled: initiallyEnabled = true,
85
83
  }: KeyboardProviderProps) => {
84
+ // ref
85
+ const viewTagRef = useRef<React.Component<KeyboardControllerProps>>(null);
86
86
  // state
87
87
  const [enabled, setEnabled] = useState(initiallyEnabled);
88
88
  // animated values
@@ -92,10 +92,14 @@ export const KeyboardProvider = ({
92
92
  const progressSV = useSharedValue(0);
93
93
  const heightSV = useSharedValue(0);
94
94
  const layout = useSharedValue<FocusedInputLayoutChangedEvent | null>(null);
95
- const [setKeyboardHandlers, broadcastKeyboardEvents] =
96
- useSharedHandlers<KeyboardHandler>();
97
- const [setInputHandlers, broadcastInputEvents] =
98
- useSharedHandlers<FocusedInputHandler>();
95
+ const setKeyboardHandlers = useEventHandlerRegistration<KeyboardHandler>(
96
+ keyboardEventsMap,
97
+ viewTagRef,
98
+ );
99
+ const setInputHandlers = useEventHandlerRegistration<FocusedInputHandler>(
100
+ focusedInputEventsMap,
101
+ viewTagRef,
102
+ );
99
103
  // memo
100
104
  const context = useMemo<KeyboardAnimationContext>(
101
105
  () => ({
@@ -136,6 +140,7 @@ export const KeyboardProvider = ({
136
140
  "worklet";
137
141
 
138
142
  if (platforms.includes(OS)) {
143
+ // eslint-disable-next-line react-compiler/react-compiler
139
144
  progressSV.value = event.progress;
140
145
  heightSV.value = -event.height;
141
146
  }
@@ -145,25 +150,17 @@ export const KeyboardProvider = ({
145
150
  onKeyboardMoveStart: (event: NativeEvent) => {
146
151
  "worklet";
147
152
 
148
- broadcastKeyboardEvents("onStart", event);
149
153
  updateSharedValues(event, ["ios"]);
150
154
  },
151
155
  onKeyboardMove: (event: NativeEvent) => {
152
156
  "worklet";
153
157
 
154
- broadcastKeyboardEvents("onMove", event);
155
158
  updateSharedValues(event, ["android"]);
156
159
  },
157
- onKeyboardMoveEnd: (event: NativeEvent) => {
158
- "worklet";
159
-
160
- broadcastKeyboardEvents("onEnd", event);
161
- },
162
160
  onKeyboardMoveInteractive: (event: NativeEvent) => {
163
161
  "worklet";
164
162
 
165
163
  updateSharedValues(event, ["android", "ios"]);
166
- broadcastKeyboardEvents("onInteractive", event);
167
164
  },
168
165
  },
169
166
  [],
@@ -182,26 +179,7 @@ export const KeyboardProvider = ({
182
179
  },
183
180
  [],
184
181
  );
185
- const inputTextHandler = useFocusedInputTextHandler(
186
- {
187
- onFocusedInputTextChanged: (e) => {
188
- "worklet";
189
-
190
- broadcastInputEvents("onChangeText", e);
191
- },
192
- },
193
- [],
194
- );
195
- const inputSelectionHandler = useFocusedInputSelectionHandler(
196
- {
197
- onFocusedInputSelectionChanged: (e) => {
198
- "worklet";
199
182
 
200
- broadcastInputEvents("onSelectionChange", e);
201
- },
202
- },
203
- [],
204
- );
205
183
  // effects
206
184
  useEffect(() => {
207
185
  if (enabled) {
@@ -214,17 +192,17 @@ export const KeyboardProvider = ({
214
192
  return (
215
193
  <KeyboardContext.Provider value={context}>
216
194
  <KeyboardControllerViewAnimated
195
+ ref={viewTagRef}
217
196
  enabled={enabled}
197
+ navigationBarTranslucent={navigationBarTranslucent}
198
+ statusBarTranslucent={statusBarTranslucent}
199
+ style={styles.container}
200
+ // on*Reanimated prop must precede animated handlers to work correctly
218
201
  onKeyboardMoveReanimated={keyboardHandler}
219
202
  onKeyboardMoveStart={OS === "ios" ? onKeyboardMove : undefined}
220
203
  onKeyboardMove={OS === "android" ? onKeyboardMove : undefined}
221
204
  onKeyboardMoveInteractive={onKeyboardMove}
222
205
  onFocusedInputLayoutChangedReanimated={inputLayoutHandler}
223
- onFocusedInputTextChangedReanimated={inputTextHandler}
224
- onFocusedInputSelectionChangedReanimated={inputSelectionHandler}
225
- navigationBarTranslucent={navigationBarTranslucent}
226
- statusBarTranslucent={statusBarTranslucent}
227
- style={styles.container}
228
206
  >
229
207
  {children}
230
208
  </KeyboardControllerViewAnimated>
@@ -17,6 +17,7 @@ const LINKING_ERROR =
17
17
 
18
18
  const RCTKeyboardController =
19
19
  require("./specs/NativeKeyboardController").default;
20
+
20
21
  export const KeyboardController = (
21
22
  RCTKeyboardController
22
23
  ? RCTKeyboardController
package/src/bindings.ts CHANGED
@@ -11,6 +11,7 @@ import type {
11
11
  import type { EmitterSubscription } from "react-native";
12
12
 
13
13
  const NOOP = () => {};
14
+
14
15
  export const KeyboardController: KeyboardControllerModule = {
15
16
  setDefaultMode: NOOP,
16
17
  setInputMode: NOOP,
@@ -18,6 +18,7 @@ export const useKeyboardAnimation = () => {
18
18
  "worklet";
19
19
 
20
20
  if (e.height > 0) {
21
+ // eslint-disable-next-line react-compiler/react-compiler
21
22
  isClosed.value = false;
22
23
  heightWhenOpened.value = e.height;
23
24
  }
@@ -33,19 +34,8 @@ export const useKeyboardAnimation = () => {
33
34
 
34
35
  isClosed.value = e.height === 0;
35
36
 
36
- // `height` update happens in `onMove` handler
37
- // in `onEnd` we need to update only if `onMove`
38
- // wasn't called (i. e. duration === 0)
39
- //
40
- // we can not call this code without condition below
41
- // because in some corner cases (iOS with `secureTextEntry`)
42
- // `onEnd` can be emitted before `onMove` and in this case
43
- // it may lead to choppy/glitchy/jumpy UI
44
- // see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/327
45
- if (e.duration === 0) {
46
- progress.value = e.progress;
47
- height.value = e.height;
48
- }
37
+ height.value = e.height;
38
+ progress.value = e.progress;
49
39
  },
50
40
  },
51
41
  [],
@@ -85,6 +85,7 @@ const KeyboardAvoidingView = forwardRef<
85
85
  "worklet";
86
86
 
87
87
  if (keyboard.isClosed.value || initialFrame.value === null) {
88
+ // eslint-disable-next-line react-compiler/react-compiler
88
89
  initialFrame.value = layout;
89
90
  }
90
91
  }, []);
@@ -143,8 +144,8 @@ const KeyboardAvoidingView = forwardRef<
143
144
  return (
144
145
  <Reanimated.View
145
146
  ref={ref}
146
- onLayout={onLayout}
147
147
  style={combinedStyles}
148
+ onLayout={onLayout}
148
149
  {...props}
149
150
  >
150
151
  {children}
@@ -24,7 +24,10 @@ import type {
24
24
  ScrollView,
25
25
  ScrollViewProps,
26
26
  } from "react-native";
27
- import type { FocusedInputLayoutChangedEvent } from "react-native-keyboard-controller";
27
+ import type {
28
+ FocusedInputLayoutChangedEvent,
29
+ FocusedInputSelectionChangedEvent,
30
+ } from "react-native-keyboard-controller";
28
31
 
29
32
  export type KeyboardAwareScrollViewProps = {
30
33
  /** The distance between keyboard and focused `TextInput` when keyboard is shown. Default is `0`. */
@@ -87,6 +90,7 @@ const KeyboardAwareScrollView = forwardRef<
87
90
  disableScrollOnKeyboardHide = false,
88
91
  enabled = true,
89
92
  extraKeyboardSpace = 0,
93
+ snapToOffsets,
90
94
  ...rest
91
95
  },
92
96
  ref,
@@ -155,12 +159,13 @@ const KeyboardAwareScrollView = forwardRef<
155
159
  0,
156
160
  scrollDistanceWithRespectToSnapPoints(
157
161
  relativeScrollTo + scrollPosition.value,
158
- rest.snapToOffsets,
162
+ snapToOffsets,
159
163
  ) - scrollPosition.value,
160
164
  ],
161
165
  );
162
166
  const targetScrollY =
163
167
  Math.max(interpolatedScrollTo, 0) + scrollPosition.value;
168
+
164
169
  scrollTo(scrollViewAnimatedRef, 0, targetScrollY, animated);
165
170
 
166
171
  return interpolatedScrollTo;
@@ -180,9 +185,35 @@ const KeyboardAwareScrollView = forwardRef<
180
185
 
181
186
  return 0;
182
187
  },
183
- [bottomOffset, enabled, height, rest.snapToOffsets],
188
+ [bottomOffset, enabled, height, snapToOffsets],
184
189
  );
185
190
 
191
+ const scrollFromCurrentPosition = useCallback(
192
+ (customHeight?: number) => {
193
+ "worklet";
194
+
195
+ const prevScrollPosition = scrollPosition.value;
196
+ const prevLayout = layout.value;
197
+
198
+ if (!input.value?.layout) {
199
+ return;
200
+ }
201
+
202
+ // eslint-disable-next-line react-compiler/react-compiler
203
+ layout.value = {
204
+ ...input.value,
205
+ layout: {
206
+ ...input.value.layout,
207
+ height: customHeight ?? input.value.layout.height,
208
+ },
209
+ };
210
+ scrollPosition.value = position.value;
211
+ maybeScroll(keyboardHeight.value, true);
212
+ scrollPosition.value = prevScrollPosition;
213
+ layout.value = prevLayout;
214
+ },
215
+ [maybeScroll],
216
+ );
186
217
  const onChangeText = useCallback(() => {
187
218
  "worklet";
188
219
 
@@ -192,15 +223,18 @@ const KeyboardAwareScrollView = forwardRef<
192
223
  return;
193
224
  }
194
225
 
195
- const prevScrollPosition = scrollPosition.value;
196
- const prevLayout = layout.value;
226
+ scrollFromCurrentPosition();
227
+ }, [scrollFromCurrentPosition]);
228
+ const onSelectionChange = useCallback(
229
+ (e: FocusedInputSelectionChangedEvent) => {
230
+ "worklet";
197
231
 
198
- scrollPosition.value = position.value;
199
- layout.value = input.value;
200
- maybeScroll(keyboardHeight.value, true);
201
- scrollPosition.value = prevScrollPosition;
202
- layout.value = prevLayout;
203
- }, [maybeScroll]);
232
+ if (e.selection.start.position !== e.selection.end.position) {
233
+ scrollFromCurrentPosition(e.selection.end.y);
234
+ }
235
+ },
236
+ [scrollFromCurrentPosition],
237
+ );
204
238
 
205
239
  const onChangeTextHandler = useMemo(
206
240
  () => debounce(onChangeText, 200),
@@ -210,8 +244,9 @@ const KeyboardAwareScrollView = forwardRef<
210
244
  useFocusedInputHandler(
211
245
  {
212
246
  onChangeText: onChangeTextHandler,
247
+ onSelectionChange: onSelectionChange,
213
248
  },
214
- [onChangeTextHandler],
249
+ [onChangeTextHandler, onSelectionChange],
215
250
  );
216
251
 
217
252
  useSmoothKeyboardHandler(
@@ -221,7 +256,9 @@ const KeyboardAwareScrollView = forwardRef<
221
256
 
222
257
  const keyboardWillChangeSize =
223
258
  keyboardHeight.value !== e.height && e.height > 0;
259
+
224
260
  keyboardWillAppear.value = e.height > 0 && keyboardHeight.value === 0;
261
+
225
262
  const keyboardWillHide = e.height === 0;
226
263
  const focusWasChanged =
227
264
  (tag.value !== e.target && e.target !== -1) ||
@@ -273,6 +310,7 @@ const KeyboardAwareScrollView = forwardRef<
273
310
  [0, keyboardHeight.value],
274
311
  [0, keyboardHeight.value + extraKeyboardSpace],
275
312
  );
313
+
276
314
  currentKeyboardFrameHeight.value = keyboardFrame;
277
315
 
278
316
  // if the user has set disableScrollOnKeyboardHide, only auto-scroll when the keyboard opens
@@ -327,8 +365,8 @@ const KeyboardAwareScrollView = forwardRef<
327
365
  <Reanimated.ScrollView
328
366
  ref={onRef}
329
367
  {...rest}
330
- onLayout={onScrollViewLayout}
331
368
  scrollEventThrottle={16}
369
+ onLayout={onScrollViewLayout}
332
370
  >
333
371
  {children}
334
372
  <Reanimated.View style={view} />
@@ -54,6 +54,7 @@ export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
54
54
  height: animatedKeyboardHeight.value,
55
55
  progress: animatedKeyboardHeight.value / persistedHeight.value,
56
56
  };
57
+
57
58
  return event;
58
59
  },
59
60
  (evt) => {
@@ -65,6 +66,7 @@ export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
65
66
  // dispatch `onEnd`
66
67
  if (evt.height === height.value) {
67
68
  handler.onEnd?.(evt);
69
+ // eslint-disable-next-line react-compiler/react-compiler
68
70
  persistedHeight.value = height.value;
69
71
  }
70
72
  },
@@ -16,6 +16,7 @@ export const debounce = <F extends (...args: Parameters<F>) => ReturnType<F>>(
16
16
 
17
17
  if (now < wait) {
18
18
  value.time = t;
19
+
19
20
  return;
20
21
  }
21
22
 
@@ -10,11 +10,11 @@ import useColorScheme from "../hooks/useColorScheme";
10
10
 
11
11
  import type { KeyboardToolbarTheme } from "./types";
12
12
  import type { PropsWithChildren } from "react";
13
- import type { ViewStyle } from "react-native";
13
+ import type { GestureResponderEvent, ViewStyle } from "react-native";
14
14
 
15
15
  type ButtonProps = {
16
16
  disabled?: boolean;
17
- onPress: () => void;
17
+ onPress: (event: GestureResponderEvent) => void;
18
18
  accessibilityLabel: string;
19
19
  accessibilityHint: string;
20
20
  testID: string;
@@ -41,13 +41,13 @@ const ButtonIOS = ({
41
41
 
42
42
  return (
43
43
  <Container
44
- accessibilityState={accessibilityState}
45
- accessibilityRole="button"
46
- accessibilityLabel={accessibilityLabel}
47
44
  accessibilityHint={accessibilityHint}
48
- onPress={onPress}
45
+ accessibilityLabel={accessibilityLabel}
46
+ accessibilityRole="button"
47
+ accessibilityState={accessibilityState}
49
48
  style={style}
50
49
  testID={testID}
50
+ onPress={onPress}
51
51
  >
52
52
  {children}
53
53
  </Container>
@@ -78,14 +78,14 @@ const ButtonAndroid = ({
78
78
 
79
79
  return (
80
80
  <TouchableNativeFeedback
81
- accessibilityState={accessibilityState}
82
- accessibilityRole="button"
83
- accessibilityLabel={accessibilityLabel}
84
81
  accessibilityHint={accessibilityHint}
85
- onPress={onPress}
82
+ accessibilityLabel={accessibilityLabel}
83
+ accessibilityRole="button"
84
+ accessibilityState={accessibilityState}
86
85
  background={ripple}
87
- testID={testID}
88
86
  style={style}
87
+ testID={testID}
88
+ onPress={onPress}
89
89
  >
90
90
  <View style={style}>{children}</View>
91
91
  </TouchableNativeFeedback>
@@ -15,6 +15,7 @@ import { colors } from "./colors";
15
15
 
16
16
  import type { HEX, KeyboardToolbarTheme } from "./types";
17
17
  import type { ReactNode } from "react";
18
+ import type { GestureResponderEvent } from "react-native";
18
19
 
19
20
  export type KeyboardToolbarProps = {
20
21
  /** An element that is shown in the middle of the toolbar. */
@@ -35,15 +36,15 @@ export type KeyboardToolbarProps = {
35
36
  /**
36
37
  * A callback that is called when the user presses the next button along with the default action.
37
38
  */
38
- onNextCallback?: () => void;
39
+ onNextCallback?: (event: GestureResponderEvent) => void;
39
40
  /**
40
41
  * A callback that is called when the user presses the previous button along with the default action.
41
42
  */
42
- onPrevCallback?: () => void;
43
+ onPrevCallback?: (event: GestureResponderEvent) => void;
43
44
  /**
44
45
  * A callback that is called when the user presses the done button along with the default action.
45
46
  */
46
- onDoneCallback?: () => void;
47
+ onDoneCallback?: (event: GestureResponderEvent) => void;
47
48
  /**
48
49
  * A component that applies blur effect to the toolbar.
49
50
  */
@@ -53,6 +54,7 @@ export type KeyboardToolbarProps = {
53
54
  */
54
55
  opacity?: HEX;
55
56
  };
57
+
56
58
  const TEST_ID_KEYBOARD_TOOLBAR = "keyboard.toolbar";
57
59
  const TEST_ID_KEYBOARD_TOOLBAR_PREVIOUS = `${TEST_ID_KEYBOARD_TOOLBAR}.previous`;
58
60
  const TEST_ID_KEYBOARD_TOOLBAR_NEXT = `${TEST_ID_KEYBOARD_TOOLBAR}.next`;
@@ -115,18 +117,36 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = ({
115
117
  const ButtonContainer = button || Button;
116
118
  const IconContainer = icon || Arrow;
117
119
 
118
- const onPressNext = useCallback(() => {
119
- goToNextField();
120
- onNextCallback?.();
121
- }, [onNextCallback]);
122
- const onPressPrev = useCallback(() => {
123
- goToPrevField();
124
- onPrevCallback?.();
125
- }, [onPrevCallback]);
126
- const onPressDone = useCallback(() => {
127
- dismissKeyboard();
128
- onDoneCallback?.();
129
- }, [onDoneCallback]);
120
+ const onPressNext = useCallback(
121
+ (event: GestureResponderEvent) => {
122
+ onNextCallback?.(event);
123
+
124
+ if (!event.isDefaultPrevented()) {
125
+ goToNextField();
126
+ }
127
+ },
128
+ [onNextCallback],
129
+ );
130
+ const onPressPrev = useCallback(
131
+ (event: GestureResponderEvent) => {
132
+ onPrevCallback?.(event);
133
+
134
+ if (!event.isDefaultPrevented()) {
135
+ goToPrevField();
136
+ }
137
+ },
138
+ [onPrevCallback],
139
+ );
140
+ const onPressDone = useCallback(
141
+ (event: GestureResponderEvent) => {
142
+ onDoneCallback?.(event);
143
+
144
+ if (!event.isDefaultPrevented()) {
145
+ dismissKeyboard();
146
+ }
147
+ },
148
+ [onDoneCallback],
149
+ );
130
150
 
131
151
  return (
132
152
  <KeyboardStickyView offset={offset}>
@@ -135,31 +155,31 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = ({
135
155
  {showArrows && (
136
156
  <>
137
157
  <ButtonContainer
158
+ accessibilityHint="Moves focus to the previous field"
138
159
  accessibilityLabel="Previous"
139
- accessibilityHint="Will move focus to previous field"
140
160
  disabled={isPrevDisabled}
141
- onPress={onPressPrev}
142
161
  testID={TEST_ID_KEYBOARD_TOOLBAR_PREVIOUS}
143
162
  theme={theme}
163
+ onPress={onPressPrev}
144
164
  >
145
165
  <IconContainer
146
166
  disabled={isPrevDisabled}
147
- type="prev"
148
167
  theme={theme}
168
+ type="prev"
149
169
  />
150
170
  </ButtonContainer>
151
171
  <ButtonContainer
172
+ accessibilityHint="Moves focus to the next field"
152
173
  accessibilityLabel="Next"
153
- accessibilityHint="Will move focus to next field"
154
174
  disabled={isNextDisabled}
155
- onPress={onPressNext}
156
175
  testID={TEST_ID_KEYBOARD_TOOLBAR_NEXT}
157
176
  theme={theme}
177
+ onPress={onPressNext}
158
178
  >
159
179
  <IconContainer
160
180
  disabled={isNextDisabled}
161
- type="next"
162
181
  theme={theme}
182
+ type="next"
163
183
  />
164
184
  </ButtonContainer>
165
185
  </>
@@ -169,15 +189,15 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = ({
169
189
  {content}
170
190
  </View>
171
191
  <ButtonContainer
192
+ accessibilityHint="Closes the keyboard"
172
193
  accessibilityLabel="Done"
173
- accessibilityHint="Will close the keyboard"
174
- onPress={onPressDone}
175
- testID={TEST_ID_KEYBOARD_TOOLBAR_DONE}
176
194
  rippleRadius={28}
177
195
  style={styles.doneButtonContainer}
196
+ testID={TEST_ID_KEYBOARD_TOOLBAR_DONE}
178
197
  theme={theme}
198
+ onPress={onPressDone}
179
199
  >
180
- <Text style={doneStyle} maxFontSizeMultiplier={1.3}>
200
+ <Text maxFontSizeMultiplier={1.3} style={doneStyle}>
181
201
  {doneText || "Done"}
182
202
  </Text>
183
203
  </ButtonContainer>
package/src/context.ts CHANGED
@@ -2,9 +2,9 @@ import { createContext, useContext } from "react";
2
2
  import { Animated } from "react-native";
3
3
 
4
4
  import type {
5
- FocusedInputHandlers,
5
+ FocusedInputHandler,
6
6
  FocusedInputLayoutChangedEvent,
7
- KeyboardHandlers,
7
+ KeyboardHandler,
8
8
  } from "./types";
9
9
  import type React from "react";
10
10
  import type { SharedValue } from "react-native-reanimated";
@@ -22,11 +22,12 @@ export type KeyboardAnimationContext = {
22
22
  animated: AnimatedContext;
23
23
  reanimated: ReanimatedContext;
24
24
  layout: SharedValue<FocusedInputLayoutChangedEvent | null>;
25
- setKeyboardHandlers: (handlers: KeyboardHandlers) => void;
26
- setInputHandlers: (handlers: FocusedInputHandlers) => void;
25
+ setKeyboardHandlers: (handlers: KeyboardHandler) => () => void;
26
+ setInputHandlers: (handlers: FocusedInputHandler) => () => void;
27
27
  setEnabled: React.Dispatch<React.SetStateAction<boolean>>;
28
28
  };
29
29
  const NOOP = () => {};
30
+ const NESTED_NOOP = () => NOOP;
30
31
  const withSharedValue = <T>(value: T): SharedValue<T> => ({
31
32
  value,
32
33
  addListener: NOOP,
@@ -48,10 +49,11 @@ const defaultContext: KeyboardAnimationContext = {
48
49
  height: DEFAULT_SHARED_VALUE,
49
50
  },
50
51
  layout: DEFAULT_LAYOUT,
51
- setKeyboardHandlers: NOOP,
52
- setInputHandlers: NOOP,
52
+ setKeyboardHandlers: NESTED_NOOP,
53
+ setInputHandlers: NESTED_NOOP,
53
54
  setEnabled: NOOP,
54
55
  };
56
+
55
57
  export const KeyboardContext = createContext(defaultContext);
56
58
  export const useKeyboardContext = () => {
57
59
  const context = useContext(KeyboardContext);
@@ -0,0 +1,15 @@
1
+ let REACore = null;
2
+
3
+ try {
4
+ REACore = require("react-native-reanimated/src/core");
5
+ } catch (e1) {
6
+ try {
7
+ REACore = require("react-native-reanimated/src/reanimated2/core");
8
+ } catch (e2) {
9
+ console.warn("Failed to load REACore from both paths");
10
+ }
11
+ }
12
+ const registerEventHandler = REACore.registerEventHandler;
13
+ const unregisterEventHandler = REACore.unregisterEventHandler;
14
+
15
+ export { registerEventHandler, unregisterEventHandler };
@@ -0,0 +1,8 @@
1
+ declare function registerEventHandler(
2
+ handler: (event: never) => void,
3
+ eventName: string,
4
+ viewTag: number,
5
+ ): number;
6
+ declare function unregisterEventHandler(id: number): void;
7
+
8
+ export { registerEventHandler, unregisterEventHandler };
@@ -0,0 +1,5 @@
1
+ const NOOP = () => {};
2
+ const registerEventHandler = NOOP;
3
+ const unregisterEventHandler = NOOP;
4
+
5
+ export { registerEventHandler, unregisterEventHandler };
@@ -0,0 +1,14 @@
1
+ import type { FocusedInputHandler, KeyboardHandler } from "./types";
2
+
3
+ export const keyboardEventsMap = new Map<keyof KeyboardHandler, string>([
4
+ ["onStart", "onKeyboardMoveStart"],
5
+ ["onMove", "onKeyboardMove"],
6
+ ["onEnd", "onKeyboardMoveEnd"],
7
+ ["onInteractive", "onKeyboardMoveInteractive"],
8
+ ]);
9
+ export const focusedInputEventsMap = new Map<keyof FocusedInputHandler, string>(
10
+ [
11
+ ["onChangeText", "onFocusedInputTextChanged"],
12
+ ["onSelectionChange", "onFocusedInputSelectionChanged"],
13
+ ],
14
+ );