react-native-keyboard-controller 1.11.6 → 1.12.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/main/java/com/reactnativekeyboardcontroller/events/FocusedInputLayoutChangedEvent.kt +1 -1
  2. package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputSelectionChangedEvent.kt +51 -0
  3. package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputTextChangedEvent.kt +1 -1
  4. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt +79 -0
  5. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +1 -2
  6. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/View.kt +1 -1
  7. package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/IosInterpolator.kt +1 -1
  8. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt +32 -0
  9. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt +1 -1
  10. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +2 -0
  11. package/android/src/main/java/com/reactnativekeyboardcontroller/ui/FrameScheduler.kt +40 -0
  12. package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +1 -1
  13. package/ios/KeyboardController-Bridging-Header.h +1 -0
  14. package/ios/core/KeyboardAnimation.swift +77 -0
  15. package/ios/core/SpringAnimation.swift +60 -0
  16. package/ios/core/UIUtils.swift +11 -0
  17. package/ios/delegates/KCTextInputCompositeDelegate.swift +148 -0
  18. package/ios/events/FocusedInputSelectionChangedEvent.h +15 -0
  19. package/ios/events/FocusedInputSelectionChangedEvent.m +76 -0
  20. package/ios/objc/RCTUITextView+DelegateManager.h +14 -0
  21. package/ios/objc/RCTUITextView+DelegateManager.m +26 -0
  22. package/ios/observers/FocusedInputObserver.swift +74 -13
  23. package/ios/observers/KeyboardMovementObserver.swift +39 -2
  24. package/ios/views/KeyboardControllerView.mm +45 -8
  25. package/ios/views/KeyboardControllerViewManager.mm +1 -0
  26. package/ios/views/KeyboardControllerViewManager.swift +10 -2
  27. package/lib/commonjs/animated.js +8 -0
  28. package/lib/commonjs/animated.js.map +1 -1
  29. package/lib/commonjs/components/KeyboardAvoidingView/index.js.map +1 -1
  30. package/lib/commonjs/components/KeyboardAwareScrollView/index.js +5 -5
  31. package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
  32. package/lib/commonjs/components/KeyboardStickyView/index.js.map +1 -1
  33. package/lib/commonjs/components/KeyboardToolbar/index.js +25 -7
  34. package/lib/commonjs/components/KeyboardToolbar/index.js.map +1 -1
  35. package/lib/commonjs/components/KeyboardToolbar/types.js.map +1 -1
  36. package/lib/commonjs/components/index.js.map +1 -1
  37. package/lib/commonjs/index.js.map +1 -1
  38. package/lib/commonjs/reanimated.js +2 -1
  39. package/lib/commonjs/reanimated.js.map +1 -1
  40. package/lib/commonjs/reanimated.native.js +18 -1
  41. package/lib/commonjs/reanimated.native.js.map +1 -1
  42. package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
  43. package/lib/commonjs/types.js.map +1 -1
  44. package/lib/module/animated.js +9 -1
  45. package/lib/module/animated.js.map +1 -1
  46. package/lib/module/components/KeyboardAvoidingView/index.js.map +1 -1
  47. package/lib/module/components/KeyboardAwareScrollView/index.js +5 -5
  48. package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
  49. package/lib/module/components/KeyboardStickyView/index.js.map +1 -1
  50. package/lib/module/components/KeyboardToolbar/index.js +26 -8
  51. package/lib/module/components/KeyboardToolbar/index.js.map +1 -1
  52. package/lib/module/components/KeyboardToolbar/types.js.map +1 -1
  53. package/lib/module/components/index.js.map +1 -1
  54. package/lib/module/index.js.map +1 -1
  55. package/lib/module/reanimated.js +1 -0
  56. package/lib/module/reanimated.js.map +1 -1
  57. package/lib/module/reanimated.native.js +16 -0
  58. package/lib/module/reanimated.native.js.map +1 -1
  59. package/lib/module/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
  60. package/lib/module/types.js.map +1 -1
  61. package/lib/typescript/components/KeyboardAvoidingView/index.d.ts +20 -0
  62. package/lib/typescript/components/KeyboardAwareScrollView/index.d.ts +8 -0
  63. package/lib/typescript/components/KeyboardStickyView/index.d.ts +15 -0
  64. package/lib/typescript/components/KeyboardToolbar/index.d.ts +21 -1
  65. package/lib/typescript/components/KeyboardToolbar/types.d.ts +3 -1
  66. package/lib/typescript/components/index.d.ts +3 -0
  67. package/lib/typescript/index.d.ts +1 -1
  68. package/lib/typescript/reanimated.d.ts +2 -1
  69. package/lib/typescript/reanimated.native.d.ts +7 -4
  70. package/lib/typescript/specs/KeyboardControllerViewNativeComponent.d.ts +16 -0
  71. package/lib/typescript/types.d.ts +21 -0
  72. package/package.json +4 -3
  73. package/src/animated.tsx +12 -0
  74. package/src/components/KeyboardAvoidingView/index.tsx +5 -2
  75. package/src/components/KeyboardAwareScrollView/index.tsx +81 -81
  76. package/src/components/KeyboardStickyView/index.tsx +1 -1
  77. package/src/components/KeyboardToolbar/index.tsx +47 -7
  78. package/src/components/KeyboardToolbar/types.ts +25 -1
  79. package/src/components/index.ts +3 -0
  80. package/src/index.ts +6 -1
  81. package/src/reanimated.native.ts +30 -3
  82. package/src/reanimated.ts +6 -0
  83. package/src/specs/KeyboardControllerViewNativeComponent.ts +17 -0
  84. package/src/types.ts +31 -0
  85. package/ios/KeyboardController.xcodeproj/project.pbxproj +0 -387
  86. package/ios/observers/TextChangeObserver.swift +0 -41
@@ -1,5 +1,20 @@
1
1
  import React from "react";
2
2
  import type { View, ViewProps } from "react-native";
3
+ export type KeyboardStickyViewProps = {
4
+ /**
5
+ * Specify additional offset to the view for given keyboard state.
6
+ */
7
+ offset?: {
8
+ /**
9
+ * Adds additional `translateY` when keyboard is close. By default `0`.
10
+ */
11
+ closed?: number;
12
+ /**
13
+ * Adds additional `translateY` when keyboard is open. By default `0`.
14
+ */
15
+ opened?: number;
16
+ };
17
+ } & ViewProps;
3
18
  declare const KeyboardStickyView: React.ForwardRefExoticComponent<{
4
19
  /**
5
20
  * Specify additional offset to the view for given keyboard state.
@@ -2,7 +2,7 @@ import React from "react";
2
2
  import Arrow from "./Arrow";
3
3
  import Button from "./Button";
4
4
  import { colors } from "./colors";
5
- import type { KeyboardToolbarTheme } from "./types";
5
+ import type { HEX, KeyboardToolbarTheme } from "./types";
6
6
  import type { ReactNode } from "react";
7
7
  export type KeyboardToolbarProps = {
8
8
  /** An element that is shown in the middle of the toolbar. */
@@ -20,6 +20,26 @@ export type KeyboardToolbarProps = {
20
20
  * and want to show only `Done` button. Default to `true`.
21
21
  */
22
22
  showArrows?: boolean;
23
+ /**
24
+ * A callback that is called when the user presses the next button along with the default action.
25
+ */
26
+ onNextCallback?: () => void;
27
+ /**
28
+ * A callback that is called when the user presses the previous button along with the default action.
29
+ */
30
+ onPrevCallback?: () => void;
31
+ /**
32
+ * A callback that is called when the user presses the done button along with the default action.
33
+ */
34
+ onDoneCallback?: () => void;
35
+ /**
36
+ * A component that applies blur effect to the toolbar.
37
+ */
38
+ blur?: JSX.Element | null;
39
+ /**
40
+ * A value for container opacity in hexadecimal format (e.g. `ff`). Default value is `ff`.
41
+ */
42
+ opacity?: HEX;
23
43
  };
24
44
  /**
25
45
  * `KeyboardToolbar` is a component that is shown above the keyboard with `Prev`/`Next` and
@@ -5,7 +5,7 @@ type Theme = {
5
5
  /** Color for arrow when it's disabled */
6
6
  disabled: ColorValue;
7
7
  /** Keyboard toolbar background color */
8
- background: ColorValue;
8
+ background: string;
9
9
  /** Color for ripple effect (on button touch) on Android */
10
10
  ripple: ColorValue;
11
11
  };
@@ -13,4 +13,6 @@ export type KeyboardToolbarTheme = {
13
13
  light: Theme;
14
14
  dark: Theme;
15
15
  };
16
+ type HexSymbol = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "A" | "B" | "C" | "D" | "E" | "F" | "a" | "b" | "c" | "d" | "e" | "f";
17
+ export type HEX = `${HexSymbol}${HexSymbol}`;
16
18
  export {};
@@ -2,4 +2,7 @@ export { default as KeyboardAvoidingView } from "./KeyboardAvoidingView";
2
2
  export { default as KeyboardStickyView } from "./KeyboardStickyView";
3
3
  export { default as KeyboardAwareScrollView } from "./KeyboardAwareScrollView";
4
4
  export { default as KeyboardToolbar, DefaultKeyboardToolbarTheme, } from "./KeyboardToolbar";
5
+ export type { KeyboardAvoidingViewProps } from "./KeyboardAvoidingView";
6
+ export type { KeyboardStickyViewProps } from "./KeyboardStickyView";
7
+ export type { KeyboardAwareScrollViewProps } from "./KeyboardAwareScrollView";
5
8
  export type { KeyboardToolbarProps } from "./KeyboardToolbar";
@@ -5,4 +5,4 @@ export * from "./hooks";
5
5
  export * from "./constants";
6
6
  export * from "./types";
7
7
  export { KeyboardAvoidingView, KeyboardStickyView, KeyboardAwareScrollView, KeyboardToolbar, DefaultKeyboardToolbarTheme, } from "./components";
8
- export type { KeyboardToolbarProps } from "./components";
8
+ export type { KeyboardAvoidingViewProps, KeyboardStickyViewProps, KeyboardAwareScrollViewProps, KeyboardToolbarProps, } from "./components";
@@ -1,4 +1,5 @@
1
- import type { EventWithName, FocusedInputLayoutChangedEvent, FocusedInputLayoutHandlerHook, FocusedInputTextChangedEvent, FocusedInputTextHandlerHook, KeyboardHandlerHook, NativeEvent } from "./types";
1
+ import type { EventWithName, FocusedInputLayoutChangedEvent, FocusedInputLayoutHandlerHook, FocusedInputSelectionChangedEvent, FocusedInputSelectionHandlerHook, FocusedInputTextChangedEvent, FocusedInputTextHandlerHook, KeyboardHandlerHook, NativeEvent } from "./types";
2
2
  export declare const useAnimatedKeyboardHandler: KeyboardHandlerHook<Record<string, unknown>, EventWithName<NativeEvent>>;
3
3
  export declare const useFocusedInputLayoutHandler: FocusedInputLayoutHandlerHook<Record<string, unknown>, EventWithName<FocusedInputLayoutChangedEvent>>;
4
4
  export declare const useFocusedInputTextHandler: FocusedInputTextHandlerHook<Record<string, unknown>, EventWithName<FocusedInputTextChangedEvent>>;
5
+ export declare const useFocusedInputSelectionHandler: FocusedInputSelectionHandlerHook<Record<string, unknown>, EventWithName<FocusedInputSelectionChangedEvent>>;
@@ -1,4 +1,7 @@
1
- import type { EventWithName, FocusedInputLayoutChangedEvent, FocusedInputLayoutHandlerHook, FocusedInputTextChangedEvent, FocusedInputTextHandlerHook, KeyboardHandlerHook, NativeEvent } from "./types";
2
- export declare const useAnimatedKeyboardHandler: KeyboardHandlerHook<Record<string, unknown>, EventWithName<NativeEvent>>;
3
- export declare const useFocusedInputLayoutHandler: FocusedInputLayoutHandlerHook<Record<string, unknown>, EventWithName<FocusedInputLayoutChangedEvent>>;
4
- export declare const useFocusedInputTextHandler: FocusedInputTextHandlerHook<Record<string, unknown>, EventWithName<FocusedInputTextChangedEvent>>;
1
+ import type { EventWithName, FocusedInputLayoutChangedEvent, FocusedInputLayoutHandlerHook, FocusedInputSelectionChangedEvent, FocusedInputSelectionHandlerHook, FocusedInputTextChangedEvent, FocusedInputTextHandlerHook, KeyboardHandlerHook, NativeEvent } from "./types";
2
+ type EventContext = Record<string, unknown>;
3
+ export declare const useAnimatedKeyboardHandler: KeyboardHandlerHook<EventContext, EventWithName<NativeEvent>>;
4
+ export declare const useFocusedInputLayoutHandler: FocusedInputLayoutHandlerHook<EventContext, EventWithName<FocusedInputLayoutChangedEvent>>;
5
+ export declare const useFocusedInputTextHandler: FocusedInputTextHandlerHook<EventContext, EventWithName<FocusedInputTextChangedEvent>>;
6
+ export declare const useFocusedInputSelectionHandler: FocusedInputSelectionHandlerHook<EventContext, EventWithName<FocusedInputSelectionChangedEvent>>;
7
+ export {};
@@ -22,6 +22,21 @@ type FocusedInputLayoutChangedEvent = Readonly<{
22
22
  type FocusedInputTextChangedEvent = Readonly<{
23
23
  text: string;
24
24
  }>;
25
+ type FocusedInputSelectionChangedEvent = Readonly<{
26
+ target: Int32;
27
+ selection: {
28
+ start: {
29
+ x: Double;
30
+ y: Double;
31
+ position: Int32;
32
+ };
33
+ end: {
34
+ x: Double;
35
+ y: Double;
36
+ position: Int32;
37
+ };
38
+ };
39
+ }>;
25
40
  export interface NativeProps extends ViewProps {
26
41
  enabled?: boolean;
27
42
  statusBarTranslucent?: boolean;
@@ -32,6 +47,7 @@ export interface NativeProps extends ViewProps {
32
47
  onKeyboardMoveInteractive?: DirectEventHandler<KeyboardMoveEvent>;
33
48
  onFocusedInputLayoutChanged?: DirectEventHandler<FocusedInputLayoutChangedEvent>;
34
49
  onFocusedInputTextChanged?: DirectEventHandler<FocusedInputTextChangedEvent>;
50
+ onFocusedInputSelectionChanged?: DirectEventHandler<FocusedInputSelectionChangedEvent>;
35
51
  }
36
52
  declare const _default: HostComponent<NativeProps>;
37
53
  export default _default;
@@ -20,6 +20,21 @@ export type FocusedInputLayoutChangedEvent = {
20
20
  export type FocusedInputTextChangedEvent = {
21
21
  text: string;
22
22
  };
23
+ export type FocusedInputSelectionChangedEvent = {
24
+ target: number;
25
+ selection: {
26
+ start: {
27
+ x: number;
28
+ y: number;
29
+ position: number;
30
+ };
31
+ end: {
32
+ x: number;
33
+ y: number;
34
+ position: number;
35
+ };
36
+ };
37
+ };
23
38
  export type EventWithName<T> = {
24
39
  eventName: string;
25
40
  } & T;
@@ -30,9 +45,11 @@ export type KeyboardControllerProps = {
30
45
  onKeyboardMoveInteractive?: (e: NativeSyntheticEvent<EventWithName<NativeEvent>>) => void;
31
46
  onFocusedInputLayoutChanged?: (e: NativeSyntheticEvent<EventWithName<FocusedInputLayoutChangedEvent>>) => void;
32
47
  onFocusedInputTextChanged?: (e: NativeSyntheticEvent<EventWithName<FocusedInputTextChangedEvent>>) => void;
48
+ onFocusedInputSelectionChanged?: (e: NativeSyntheticEvent<EventWithName<FocusedInputSelectionChangedEvent>>) => void;
33
49
  onKeyboardMoveReanimated?: (e: NativeSyntheticEvent<EventWithName<NativeEvent>>) => void;
34
50
  onFocusedInputLayoutChangedReanimated?: (e: NativeSyntheticEvent<EventWithName<FocusedInputLayoutChangedEvent>>) => void;
35
51
  onFocusedInputTextChangedReanimated?: (e: NativeSyntheticEvent<EventWithName<FocusedInputTextChangedEvent>>) => void;
52
+ onFocusedInputSelectionChangedReanimated?: (e: NativeSyntheticEvent<EventWithName<FocusedInputSelectionChangedEvent>>) => void;
36
53
  statusBarTranslucent?: boolean;
37
54
  navigationBarTranslucent?: boolean;
38
55
  enabled?: boolean;
@@ -90,6 +107,9 @@ export type FocusedInputLayoutHandlerHook<TContext, Event> = (handlers: {
90
107
  export type FocusedInputTextHandlerHook<TContext, Event> = (handlers: {
91
108
  onFocusedInputTextChanged?: (e: FocusedInputTextChangedEvent, context: TContext) => void;
92
109
  }, dependencies?: unknown[]) => (e: NativeSyntheticEvent<Event>) => void;
110
+ export type FocusedInputSelectionHandlerHook<TContext, Event> = (handlers: {
111
+ onFocusedInputSelectionChanged?: (e: FocusedInputSelectionChangedEvent, context: TContext) => void;
112
+ }, dependencies?: unknown[]) => (e: NativeSyntheticEvent<Event>) => void;
93
113
  export type Handlers<T> = Record<string, T | undefined>;
94
114
  export type KeyboardHandler = Partial<{
95
115
  onStart: (e: NativeEvent) => void;
@@ -100,5 +120,6 @@ export type KeyboardHandler = Partial<{
100
120
  export type KeyboardHandlers = Handlers<KeyboardHandler>;
101
121
  export type FocusedInputHandler = Partial<{
102
122
  onChangeText: (e: FocusedInputTextChangedEvent) => void;
123
+ onSelectionChange: (e: FocusedInputSelectionChangedEvent) => void;
103
124
  }>;
104
125
  export type FocusedInputHandlers = Handlers<FocusedInputHandler>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-keyboard-controller",
3
- "version": "1.11.6",
3
+ "version": "1.12.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",
@@ -47,6 +47,7 @@
47
47
  "focused input",
48
48
  "text changed",
49
49
  "avoiding view",
50
+ "avoid keyboard",
50
51
  "sticky view",
51
52
  "keyboard aware scroll view",
52
53
  "keyboard toolbar",
@@ -87,9 +88,9 @@
87
88
  "pod-install": "^0.1.0",
88
89
  "prettier": "^2.8.8",
89
90
  "react": "18.2.0",
90
- "react-native": "0.73.6",
91
+ "react-native": "0.74.0",
91
92
  "react-native-builder-bob": "^0.18.0",
92
- "react-native-reanimated": "3.8.0",
93
+ "react-native-reanimated": "3.9.0",
93
94
  "release-it": "^14.2.2",
94
95
  "typescript": "5.0.4"
95
96
  },
package/src/animated.tsx CHANGED
@@ -9,6 +9,7 @@ import { applyMonkeyPatch, revertMonkeyPatch } from "./monkey-patch";
9
9
  import {
10
10
  useAnimatedKeyboardHandler,
11
11
  useFocusedInputLayoutHandler,
12
+ useFocusedInputSelectionHandler,
12
13
  useFocusedInputTextHandler,
13
14
  } from "./reanimated";
14
15
 
@@ -191,6 +192,16 @@ export const KeyboardProvider = ({
191
192
  },
192
193
  [],
193
194
  );
195
+ const inputSelectionHandler = useFocusedInputSelectionHandler(
196
+ {
197
+ onFocusedInputSelectionChanged: (e) => {
198
+ "worklet";
199
+
200
+ broadcastInputEvents("onSelectionChange", e);
201
+ },
202
+ },
203
+ [],
204
+ );
194
205
  // effects
195
206
  useEffect(() => {
196
207
  if (enabled) {
@@ -210,6 +221,7 @@ export const KeyboardProvider = ({
210
221
  onKeyboardMoveInteractive={onKeyboardMove}
211
222
  onFocusedInputLayoutChangedReanimated={inputLayoutHandler}
212
223
  onFocusedInputTextChangedReanimated={inputTextHandler}
224
+ onFocusedInputSelectionChangedReanimated={inputSelectionHandler}
213
225
  navigationBarTranslucent={navigationBarTranslucent}
214
226
  statusBarTranslucent={statusBarTranslucent}
215
227
  style={styles.container}
@@ -12,7 +12,7 @@ import { useKeyboardAnimation } from "./hooks";
12
12
 
13
13
  import type { LayoutRectangle, ViewProps } from "react-native";
14
14
 
15
- type Props = {
15
+ export type KeyboardAvoidingViewProps = {
16
16
  /**
17
17
  * Specify how to react to the presence of the keyboard.
18
18
  */
@@ -47,7 +47,10 @@ const defaultLayout: LayoutRectangle = {
47
47
  * View that moves out of the way when the keyboard appears by automatically
48
48
  * adjusting its height, position, or bottom padding.
49
49
  */
50
- const KeyboardAvoidingView = forwardRef<View, React.PropsWithChildren<Props>>(
50
+ const KeyboardAvoidingView = forwardRef<
51
+ View,
52
+ React.PropsWithChildren<KeyboardAvoidingViewProps>
53
+ >(
51
54
  (
52
55
  {
53
56
  behavior,
@@ -24,7 +24,7 @@ import type {
24
24
  } from "react-native";
25
25
  import type { FocusedInputLayoutChangedEvent } from "react-native-keyboard-controller";
26
26
 
27
- type KeyboardAwareScrollViewProps = {
27
+ export type KeyboardAwareScrollViewProps = {
28
28
  /** The distance between keyboard and focused `TextInput` when keyboard is shown. Default is `0`. */
29
29
  bottomOffset?: number;
30
30
  /** Prevents automatic scrolling of the `ScrollView` when the keyboard gets hidden, maintaining the current screen position. Default is `false`. */
@@ -212,77 +212,74 @@ const KeyboardAwareScrollView = forwardRef<
212
212
  );
213
213
 
214
214
  useSmoothKeyboardHandler(
215
- enabled
216
- ? {
217
- onStart: (e) => {
218
- "worklet";
219
-
220
- const keyboardWillChangeSize =
221
- keyboardHeight.value !== e.height && e.height > 0;
222
- keyboardWillAppear.value =
223
- e.height > 0 && keyboardHeight.value === 0;
224
- const keyboardWillHide = e.height === 0;
225
- const focusWasChanged =
226
- (tag.value !== e.target && e.target !== -1) ||
227
- keyboardWillChangeSize;
228
-
229
- if (keyboardWillChangeSize) {
230
- initialKeyboardSize.value = keyboardHeight.value;
231
- }
232
-
233
- if (keyboardWillHide) {
234
- // on back transition need to interpolate as [0, keyboardHeight]
235
- initialKeyboardSize.value = 0;
236
- scrollPosition.value = scrollBeforeKeyboardMovement.value;
237
- }
238
-
239
- if (
240
- keyboardWillAppear.value ||
241
- keyboardWillChangeSize ||
242
- focusWasChanged
243
- ) {
244
- // persist scroll value
245
- scrollPosition.value = position.value;
246
- // just persist height - later will be used in interpolation
247
- keyboardHeight.value = e.height;
248
- }
249
-
250
- // focus was changed
251
- if (focusWasChanged) {
252
- tag.value = e.target;
253
-
254
- // save position of focused text input when keyboard starts to move
255
- layout.value = input.value;
256
- // save current scroll position - when keyboard will hide we'll reuse
257
- // this value to achieve smooth hide effect
258
- scrollBeforeKeyboardMovement.value = position.value;
259
- }
260
-
261
- if (focusWasChanged && !keyboardWillAppear.value) {
262
- // update position on scroll value, so `onEnd` handler
263
- // will pick up correct values
264
- position.value += maybeScroll(e.height, true);
265
- }
266
- },
267
- onMove: (e) => {
268
- "worklet";
269
-
270
- currentKeyboardFrameHeight.value = e.height;
271
-
272
- // if the user has set disableScrollOnKeyboardHide, only auto-scroll when the keyboard opens
273
- if (!disableScrollOnKeyboardHide || keyboardWillAppear.value) {
274
- maybeScroll(e.height);
275
- }
276
- },
277
- onEnd: (e) => {
278
- "worklet";
279
-
280
- keyboardHeight.value = e.height;
281
- scrollPosition.value = position.value;
282
- },
215
+ {
216
+ onStart: (e) => {
217
+ "worklet";
218
+
219
+ const keyboardWillChangeSize =
220
+ keyboardHeight.value !== e.height && e.height > 0;
221
+ keyboardWillAppear.value = e.height > 0 && keyboardHeight.value === 0;
222
+ const keyboardWillHide = e.height === 0;
223
+ const focusWasChanged =
224
+ (tag.value !== e.target && e.target !== -1) ||
225
+ keyboardWillChangeSize;
226
+
227
+ if (keyboardWillChangeSize) {
228
+ initialKeyboardSize.value = keyboardHeight.value;
229
+ }
230
+
231
+ if (keyboardWillHide) {
232
+ // on back transition need to interpolate as [0, keyboardHeight]
233
+ initialKeyboardSize.value = 0;
234
+ scrollPosition.value = scrollBeforeKeyboardMovement.value;
235
+ }
236
+
237
+ if (
238
+ keyboardWillAppear.value ||
239
+ keyboardWillChangeSize ||
240
+ focusWasChanged
241
+ ) {
242
+ // persist scroll value
243
+ scrollPosition.value = position.value;
244
+ // just persist height - later will be used in interpolation
245
+ keyboardHeight.value = e.height;
246
+ }
247
+
248
+ // focus was changed
249
+ if (focusWasChanged) {
250
+ tag.value = e.target;
251
+
252
+ // save position of focused text input when keyboard starts to move
253
+ layout.value = input.value;
254
+ // save current scroll position - when keyboard will hide we'll reuse
255
+ // this value to achieve smooth hide effect
256
+ scrollBeforeKeyboardMovement.value = position.value;
283
257
  }
284
- : {},
285
- [height, maybeScroll, disableScrollOnKeyboardHide, enabled],
258
+
259
+ if (focusWasChanged && !keyboardWillAppear.value) {
260
+ // update position on scroll value, so `onEnd` handler
261
+ // will pick up correct values
262
+ position.value += maybeScroll(e.height, true);
263
+ }
264
+ },
265
+ onMove: (e) => {
266
+ "worklet";
267
+
268
+ currentKeyboardFrameHeight.value = e.height;
269
+
270
+ // if the user has set disableScrollOnKeyboardHide, only auto-scroll when the keyboard opens
271
+ if (!disableScrollOnKeyboardHide || keyboardWillAppear.value) {
272
+ maybeScroll(e.height);
273
+ }
274
+ },
275
+ onEnd: (e) => {
276
+ "worklet";
277
+
278
+ keyboardHeight.value = e.height;
279
+ scrollPosition.value = position.value;
280
+ },
281
+ },
282
+ [height, maybeScroll, disableScrollOnKeyboardHide],
286
283
  );
287
284
 
288
285
  useAnimatedReaction(
@@ -303,16 +300,19 @@ const KeyboardAwareScrollView = forwardRef<
303
300
  );
304
301
 
305
302
  const view = useAnimatedStyle(
306
- () => ({
307
- // animations become laggy when scrolling to the end of the `ScrollView` (when the last input is focused)
308
- // this happens because the layout recalculates on every frame. To avoid this we slightly increase padding
309
- // by `+1`. In this way we assure, that `scrollTo` will never scroll to the end, because it uses interpolation
310
- // from 0 to `keyboardHeight`, and here our padding is `keyboardHeight + 1`. It allows us not to re-run layout
311
- // re-calculation on every animation frame and it helps to achieve smooth animation.
312
- // see: https://github.com/kirillzyusko/react-native-keyboard-controller/pull/342
313
- paddingBottom: currentKeyboardFrameHeight.value + 1,
314
- }),
315
- [],
303
+ () =>
304
+ enabled
305
+ ? {
306
+ // animations become choppy when scrolling to the end of the `ScrollView` (when the last input is focused)
307
+ // this happens because the layout recalculates on every frame. To avoid this we slightly increase padding
308
+ // by `+1`. In this way we assure, that `scrollTo` will never scroll to the end, because it uses interpolation
309
+ // from 0 to `keyboardHeight`, and here our padding is `keyboardHeight + 1`. It allows us not to re-run layout
310
+ // re-calculation on every animation frame and it helps to achieve smooth animation.
311
+ // see: https://github.com/kirillzyusko/react-native-keyboard-controller/pull/342
312
+ paddingBottom: currentKeyboardFrameHeight.value + 1,
313
+ }
314
+ : {},
315
+ [enabled],
316
316
  );
317
317
 
318
318
  return (
@@ -8,7 +8,7 @@ import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller
8
8
 
9
9
  import type { View, ViewProps } from "react-native";
10
10
 
11
- type KeyboardStickyViewProps = {
11
+ export type KeyboardStickyViewProps = {
12
12
  /**
13
13
  * Specify additional offset to the view for given keyboard state.
14
14
  */
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useMemo, useState } from "react";
1
+ import React, { useCallback, useEffect, useMemo, useState } from "react";
2
2
  import { StyleSheet, Text, View } from "react-native";
3
3
 
4
4
  import {
@@ -13,7 +13,7 @@ import Arrow from "./Arrow";
13
13
  import Button from "./Button";
14
14
  import { colors } from "./colors";
15
15
 
16
- import type { KeyboardToolbarTheme } from "./types";
16
+ import type { HEX, KeyboardToolbarTheme } from "./types";
17
17
  import type { ReactNode } from "react";
18
18
 
19
19
  export type KeyboardToolbarProps = {
@@ -32,6 +32,26 @@ export type KeyboardToolbarProps = {
32
32
  * and want to show only `Done` button. Default to `true`.
33
33
  */
34
34
  showArrows?: boolean;
35
+ /**
36
+ * A callback that is called when the user presses the next button along with the default action.
37
+ */
38
+ onNextCallback?: () => void;
39
+ /**
40
+ * A callback that is called when the user presses the previous button along with the default action.
41
+ */
42
+ onPrevCallback?: () => void;
43
+ /**
44
+ * A callback that is called when the user presses the done button along with the default action.
45
+ */
46
+ onDoneCallback?: () => void;
47
+ /**
48
+ * A component that applies blur effect to the toolbar.
49
+ */
50
+ blur?: JSX.Element | null;
51
+ /**
52
+ * A value for container opacity in hexadecimal format (e.g. `ff`). Default value is `ff`.
53
+ */
54
+ opacity?: HEX;
35
55
  };
36
56
  const TEST_ID_KEYBOARD_TOOLBAR = "keyboard.toolbar";
37
57
  const TEST_ID_KEYBOARD_TOOLBAR_PREVIOUS = `${TEST_ID_KEYBOARD_TOOLBAR}.previous`;
@@ -40,6 +60,7 @@ const TEST_ID_KEYBOARD_TOOLBAR_CONTENT = `${TEST_ID_KEYBOARD_TOOLBAR}.content`;
40
60
  const TEST_ID_KEYBOARD_TOOLBAR_DONE = `${TEST_ID_KEYBOARD_TOOLBAR}.done`;
41
61
 
42
62
  const KEYBOARD_TOOLBAR_HEIGHT = 42;
63
+ const DEFAULT_OPACITY: HEX = "FF";
43
64
  const offset = { closed: KEYBOARD_TOOLBAR_HEIGHT };
44
65
 
45
66
  const dismissKeyboard = () => KeyboardController.dismiss();
@@ -57,6 +78,11 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = ({
57
78
  button,
58
79
  icon,
59
80
  showArrows = true,
81
+ onNextCallback,
82
+ onPrevCallback,
83
+ onDoneCallback,
84
+ blur = null,
85
+ opacity = DEFAULT_OPACITY,
60
86
  }) => {
61
87
  const colorScheme = useColorScheme();
62
88
  const [inputs, setInputs] = useState({
@@ -81,24 +107,38 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = ({
81
107
  () => [
82
108
  styles.toolbar,
83
109
  {
84
- backgroundColor: theme[colorScheme].background,
110
+ backgroundColor: `${theme[colorScheme].background}${opacity}`,
85
111
  },
86
112
  ],
87
- [colorScheme, theme],
113
+ [colorScheme, opacity, theme],
88
114
  );
89
115
  const ButtonContainer = button || Button;
90
116
  const IconContainer = icon || Arrow;
91
117
 
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]);
130
+
92
131
  return (
93
132
  <KeyboardStickyView offset={offset}>
94
133
  <View style={toolbarStyle} testID={TEST_ID_KEYBOARD_TOOLBAR}>
134
+ {blur}
95
135
  {showArrows && (
96
136
  <>
97
137
  <ButtonContainer
98
138
  accessibilityLabel="Previous"
99
139
  accessibilityHint="Will move focus to previous field"
100
140
  disabled={isPrevDisabled}
101
- onPress={goToPrevField}
141
+ onPress={onPressPrev}
102
142
  testID={TEST_ID_KEYBOARD_TOOLBAR_PREVIOUS}
103
143
  theme={theme}
104
144
  >
@@ -112,7 +152,7 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = ({
112
152
  accessibilityLabel="Next"
113
153
  accessibilityHint="Will move focus to next field"
114
154
  disabled={isNextDisabled}
115
- onPress={goToNextField}
155
+ onPress={onPressNext}
116
156
  testID={TEST_ID_KEYBOARD_TOOLBAR_NEXT}
117
157
  theme={theme}
118
158
  >
@@ -131,7 +171,7 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = ({
131
171
  <ButtonContainer
132
172
  accessibilityLabel="Done"
133
173
  accessibilityHint="Will close the keyboard"
134
- onPress={dismissKeyboard}
174
+ onPress={onPressDone}
135
175
  testID={TEST_ID_KEYBOARD_TOOLBAR_DONE}
136
176
  rippleRadius={28}
137
177
  style={styles.doneButtonContainer}
@@ -6,7 +6,7 @@ type Theme = {
6
6
  /** Color for arrow when it's disabled */
7
7
  disabled: ColorValue;
8
8
  /** Keyboard toolbar background color */
9
- background: ColorValue;
9
+ background: string;
10
10
  /** Color for ripple effect (on button touch) on Android */
11
11
  ripple: ColorValue;
12
12
  };
@@ -14,3 +14,27 @@ export type KeyboardToolbarTheme = {
14
14
  light: Theme;
15
15
  dark: Theme;
16
16
  };
17
+ type HexSymbol =
18
+ | "0"
19
+ | "1"
20
+ | "2"
21
+ | "3"
22
+ | "4"
23
+ | "5"
24
+ | "6"
25
+ | "7"
26
+ | "8"
27
+ | "9"
28
+ | "A"
29
+ | "B"
30
+ | "C"
31
+ | "D"
32
+ | "E"
33
+ | "F"
34
+ | "a"
35
+ | "b"
36
+ | "c"
37
+ | "d"
38
+ | "e"
39
+ | "f";
40
+ export type HEX = `${HexSymbol}${HexSymbol}`;
@@ -5,4 +5,7 @@ export {
5
5
  default as KeyboardToolbar,
6
6
  DefaultKeyboardToolbarTheme,
7
7
  } from "./KeyboardToolbar";
8
+ export type { KeyboardAvoidingViewProps } from "./KeyboardAvoidingView";
9
+ export type { KeyboardStickyViewProps } from "./KeyboardStickyView";
10
+ export type { KeyboardAwareScrollViewProps } from "./KeyboardAwareScrollView";
8
11
  export type { KeyboardToolbarProps } from "./KeyboardToolbar";
package/src/index.ts CHANGED
@@ -13,4 +13,9 @@ export {
13
13
  KeyboardToolbar,
14
14
  DefaultKeyboardToolbarTheme,
15
15
  } from "./components";
16
- export type { KeyboardToolbarProps } from "./components";
16
+ export type {
17
+ KeyboardAvoidingViewProps,
18
+ KeyboardStickyViewProps,
19
+ KeyboardAwareScrollViewProps,
20
+ KeyboardToolbarProps,
21
+ } from "./components";