react-native-keyboard-controller 1.10.0 → 1.10.2

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 (144) hide show
  1. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt +31 -1
  2. package/ios/.clang-format +30 -30
  3. package/ios/.swiftlint.yml +1 -1
  4. package/ios/observers/FocusedInputObserver.swift +1 -0
  5. package/ios/views/KeyboardControllerViewManager.swift +12 -2
  6. package/jest/index.js +14 -4
  7. package/lib/commonjs/animated.js +19 -19
  8. package/lib/commonjs/animated.js.map +1 -1
  9. package/lib/commonjs/bindings.js.map +1 -1
  10. package/lib/commonjs/bindings.native.js +6 -6
  11. package/lib/commonjs/bindings.native.js.map +1 -1
  12. package/lib/commonjs/components/KeyboardAvoidingView/hooks.js +19 -5
  13. package/lib/commonjs/components/KeyboardAvoidingView/hooks.js.map +1 -1
  14. package/lib/commonjs/components/KeyboardAvoidingView/index.js +12 -7
  15. package/lib/commonjs/components/KeyboardAvoidingView/index.js.map +1 -1
  16. package/lib/commonjs/components/KeyboardAwareScrollView/index.js +10 -8
  17. package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
  18. package/lib/commonjs/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.js +5 -5
  19. package/lib/commonjs/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.js.map +1 -1
  20. package/lib/commonjs/components/KeyboardAwareScrollView/utils.js +2 -2
  21. package/lib/commonjs/components/KeyboardAwareScrollView/utils.js.map +1 -1
  22. package/lib/commonjs/components/KeyboardStickyView/index.js +9 -5
  23. package/lib/commonjs/components/KeyboardStickyView/index.js.map +1 -1
  24. package/lib/commonjs/components/hooks/useKeyboardInterpolation.js +88 -0
  25. package/lib/commonjs/components/hooks/useKeyboardInterpolation.js.map +1 -0
  26. package/lib/commonjs/components/index.js.map +1 -1
  27. package/lib/commonjs/constants.js.map +1 -1
  28. package/lib/commonjs/context.js.map +1 -1
  29. package/lib/commonjs/hooks.js.map +1 -1
  30. package/lib/commonjs/index.js.map +1 -1
  31. package/lib/commonjs/internal.js +1 -1
  32. package/lib/commonjs/internal.js.map +1 -1
  33. package/lib/commonjs/monkey-patch.android.js +1 -1
  34. package/lib/commonjs/monkey-patch.android.js.map +1 -1
  35. package/lib/commonjs/monkey-patch.js.map +1 -1
  36. package/lib/commonjs/reanimated.js.map +1 -1
  37. package/lib/commonjs/reanimated.native.js +12 -12
  38. package/lib/commonjs/reanimated.native.js.map +1 -1
  39. package/lib/commonjs/replicas.js +5 -5
  40. package/lib/commonjs/replicas.js.map +1 -1
  41. package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js +1 -1
  42. package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
  43. package/lib/commonjs/specs/KeyboardGestureAreaNativeComponent.js +2 -2
  44. package/lib/commonjs/specs/KeyboardGestureAreaNativeComponent.js.map +1 -1
  45. package/lib/commonjs/specs/NativeKeyboardController.js +1 -1
  46. package/lib/commonjs/specs/NativeKeyboardController.js.map +1 -1
  47. package/lib/commonjs/specs/NativeStatusBarManagerCompat.js +1 -1
  48. package/lib/commonjs/specs/NativeStatusBarManagerCompat.js.map +1 -1
  49. package/lib/commonjs/types.js.map +1 -1
  50. package/lib/commonjs/utils.js.map +1 -1
  51. package/lib/module/animated.js +27 -27
  52. package/lib/module/animated.js.map +1 -1
  53. package/lib/module/bindings.js +1 -1
  54. package/lib/module/bindings.js.map +1 -1
  55. package/lib/module/bindings.native.js +7 -7
  56. package/lib/module/bindings.native.js.map +1 -1
  57. package/lib/module/components/KeyboardAvoidingView/hooks.js +19 -5
  58. package/lib/module/components/KeyboardAvoidingView/hooks.js.map +1 -1
  59. package/lib/module/components/KeyboardAvoidingView/index.js +15 -11
  60. package/lib/module/components/KeyboardAvoidingView/index.js.map +1 -1
  61. package/lib/module/components/KeyboardAwareScrollView/index.js +16 -14
  62. package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
  63. package/lib/module/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.js +8 -8
  64. package/lib/module/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.js.map +1 -1
  65. package/lib/module/components/KeyboardAwareScrollView/utils.js +2 -2
  66. package/lib/module/components/KeyboardAwareScrollView/utils.js.map +1 -1
  67. package/lib/module/components/KeyboardStickyView/index.js +9 -6
  68. package/lib/module/components/KeyboardStickyView/index.js.map +1 -1
  69. package/lib/module/components/hooks/useKeyboardInterpolation.js +81 -0
  70. package/lib/module/components/hooks/useKeyboardInterpolation.js.map +1 -0
  71. package/lib/module/components/index.js +3 -3
  72. package/lib/module/components/index.js.map +1 -1
  73. package/lib/module/context.js +2 -2
  74. package/lib/module/context.js.map +1 -1
  75. package/lib/module/hooks.js +5 -5
  76. package/lib/module/hooks.js.map +1 -1
  77. package/lib/module/index.js +8 -8
  78. package/lib/module/index.js.map +1 -1
  79. package/lib/module/internal.js +4 -4
  80. package/lib/module/internal.js.map +1 -1
  81. package/lib/module/monkey-patch.android.js +2 -2
  82. package/lib/module/monkey-patch.android.js.map +1 -1
  83. package/lib/module/monkey-patch.js.map +1 -1
  84. package/lib/module/reanimated.js.map +1 -1
  85. package/lib/module/reanimated.native.js +13 -13
  86. package/lib/module/reanimated.native.js.map +1 -1
  87. package/lib/module/replicas.js +11 -11
  88. package/lib/module/replicas.js.map +1 -1
  89. package/lib/module/specs/KeyboardControllerViewNativeComponent.js +2 -2
  90. package/lib/module/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
  91. package/lib/module/specs/KeyboardGestureAreaNativeComponent.js +3 -3
  92. package/lib/module/specs/KeyboardGestureAreaNativeComponent.js.map +1 -1
  93. package/lib/module/specs/NativeKeyboardController.js +2 -2
  94. package/lib/module/specs/NativeKeyboardController.js.map +1 -1
  95. package/lib/module/specs/NativeStatusBarManagerCompat.js +2 -2
  96. package/lib/module/specs/NativeStatusBarManagerCompat.js.map +1 -1
  97. package/lib/module/types.js.map +1 -1
  98. package/lib/module/utils.js.map +1 -1
  99. package/lib/typescript/animated.d.ts +1 -1
  100. package/lib/typescript/bindings.d.ts +1 -1
  101. package/lib/typescript/bindings.native.d.ts +1 -1
  102. package/lib/typescript/components/KeyboardAvoidingView/index.d.ts +4 -4
  103. package/lib/typescript/components/KeyboardAwareScrollView/index.d.ts +2 -2
  104. package/lib/typescript/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.d.ts +1 -1
  105. package/lib/typescript/components/KeyboardStickyView/index.d.ts +2 -2
  106. package/lib/typescript/components/hooks/useKeyboardInterpolation.d.ts +20 -0
  107. package/lib/typescript/components/index.d.ts +3 -3
  108. package/lib/typescript/context.d.ts +4 -4
  109. package/lib/typescript/hooks.d.ts +3 -3
  110. package/lib/typescript/index.d.ts +8 -8
  111. package/lib/typescript/internal.d.ts +2 -2
  112. package/lib/typescript/reanimated.d.ts +1 -1
  113. package/lib/typescript/reanimated.native.d.ts +1 -1
  114. package/lib/typescript/replicas.d.ts +1 -1
  115. package/lib/typescript/specs/KeyboardControllerViewNativeComponent.d.ts +3 -3
  116. package/lib/typescript/specs/KeyboardGestureAreaNativeComponent.d.ts +4 -4
  117. package/lib/typescript/specs/NativeKeyboardController.d.ts +1 -1
  118. package/lib/typescript/specs/NativeStatusBarManagerCompat.d.ts +1 -1
  119. package/lib/typescript/types.d.ts +3 -3
  120. package/package.json +8 -10
  121. package/src/animated.tsx +39 -39
  122. package/src/bindings.native.ts +11 -11
  123. package/src/bindings.ts +3 -3
  124. package/src/components/KeyboardAvoidingView/hooks.ts +20 -6
  125. package/src/components/KeyboardAvoidingView/index.tsx +25 -24
  126. package/src/components/KeyboardAwareScrollView/index.tsx +29 -25
  127. package/src/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.ts +13 -13
  128. package/src/components/KeyboardAwareScrollView/utils.ts +3 -3
  129. package/src/components/KeyboardStickyView/index.tsx +12 -12
  130. package/src/components/hooks/useKeyboardInterpolation.ts +108 -0
  131. package/src/components/index.ts +3 -3
  132. package/src/context.ts +7 -7
  133. package/src/hooks.ts +12 -12
  134. package/src/index.ts +8 -8
  135. package/src/internal.ts +9 -9
  136. package/src/monkey-patch.android.ts +2 -2
  137. package/src/reanimated.native.ts +20 -20
  138. package/src/reanimated.ts +1 -1
  139. package/src/replicas.ts +16 -16
  140. package/src/specs/KeyboardControllerViewNativeComponent.ts +5 -5
  141. package/src/specs/KeyboardGestureAreaNativeComponent.ts +7 -7
  142. package/src/specs/NativeKeyboardController.ts +3 -3
  143. package/src/specs/NativeStatusBarManagerCompat.ts +3 -3
  144. package/src/types.ts +21 -21
@@ -1,27 +1,28 @@
1
- import React, { forwardRef, useCallback, useMemo } from 'react';
2
- import { View, useWindowDimensions } from 'react-native';
1
+ import React, { forwardRef, useCallback, useMemo } from "react";
2
+ import { View, useWindowDimensions } from "react-native";
3
3
  import Reanimated, {
4
- interpolate,
5
4
  runOnUI,
6
5
  useAnimatedStyle,
7
6
  useDerivedValue,
8
7
  useSharedValue,
9
- } from 'react-native-reanimated';
8
+ } from "react-native-reanimated";
10
9
 
11
- import { useKeyboardAnimation } from './hooks';
10
+ import useKeyboardInterpolation from "../hooks/useKeyboardInterpolation";
12
11
 
13
- import type { LayoutRectangle, ViewProps } from 'react-native';
12
+ import { useKeyboardAnimation } from "./hooks";
13
+
14
+ import type { LayoutRectangle, ViewProps } from "react-native";
14
15
 
15
16
  type Props = {
16
17
  /**
17
18
  * Specify how to react to the presence of the keyboard.
18
19
  */
19
- behavior?: 'height' | 'position' | 'padding';
20
+ behavior?: "height" | "position" | "padding";
20
21
 
21
22
  /**
22
23
  * Style of the content container when `behavior` is 'position'.
23
24
  */
24
- contentContainerStyle?: ViewProps['style'];
25
+ contentContainerStyle?: ViewProps["style"];
25
26
 
26
27
  /**
27
28
  * Controls whether this `KeyboardAvoidingView` instance should take effect.
@@ -59,7 +60,7 @@ const KeyboardAvoidingView = forwardRef<View, React.PropsWithChildren<Props>>(
59
60
  onLayout: onLayoutProps,
60
61
  ...props
61
62
  },
62
- ref
63
+ ref,
63
64
  ) => {
64
65
  const initialFrame = useSharedValue<LayoutRectangle | null>(null);
65
66
  const frame = useDerivedValue(() => initialFrame.value || defaultLayout);
@@ -68,39 +69,39 @@ const KeyboardAvoidingView = forwardRef<View, React.PropsWithChildren<Props>>(
68
69
  const { height: screenHeight } = useWindowDimensions();
69
70
 
70
71
  const relativeKeyboardHeight = useCallback(() => {
71
- 'worklet';
72
+ "worklet";
72
73
 
73
74
  const keyboardY =
74
75
  screenHeight - keyboard.heightWhenOpened.value - keyboardVerticalOffset;
75
76
 
76
77
  return Math.max(frame.value.y + frame.value.height - keyboardY, 0);
77
78
  }, [screenHeight, keyboardVerticalOffset]);
79
+ const { interpolate } = useKeyboardInterpolation();
78
80
 
79
81
  const onLayoutWorklet = useCallback((layout: LayoutRectangle) => {
80
- 'worklet';
82
+ "worklet";
81
83
 
82
84
  if (keyboard.isClosed.value) {
83
85
  initialFrame.value = layout;
84
86
  }
85
87
  }, []);
86
- const onLayout = useCallback<NonNullable<ViewProps['onLayout']>>(
88
+ const onLayout = useCallback<NonNullable<ViewProps["onLayout"]>>(
87
89
  (e) => {
88
90
  runOnUI(onLayoutWorklet)(e.nativeEvent.layout);
89
91
  onLayoutProps?.(e);
90
92
  },
91
- [onLayoutProps]
93
+ [onLayoutProps],
92
94
  );
93
95
 
94
96
  const animatedStyle = useAnimatedStyle(() => {
95
- const bottom = interpolate(
96
- keyboard.progress.value,
97
- [0, 1],
98
- [0, relativeKeyboardHeight()]
99
- );
97
+ const bottom = interpolate(keyboard.height.value, [
98
+ 0,
99
+ relativeKeyboardHeight(),
100
+ ]);
100
101
  const bottomHeight = enabled ? bottom : 0;
101
102
 
102
103
  switch (behavior) {
103
- case 'height':
104
+ case "height":
104
105
  if (!keyboard.isClosed.value) {
105
106
  return {
106
107
  height: frame.value.height - bottomHeight,
@@ -110,21 +111,21 @@ const KeyboardAvoidingView = forwardRef<View, React.PropsWithChildren<Props>>(
110
111
 
111
112
  return {};
112
113
 
113
- case 'position':
114
+ case "position":
114
115
  return { bottom: bottomHeight };
115
116
 
116
- case 'padding':
117
+ case "padding":
117
118
  return { paddingBottom: bottomHeight };
118
119
 
119
120
  default:
120
121
  return {};
121
122
  }
122
123
  }, [behavior, enabled, relativeKeyboardHeight]);
123
- const isPositionBehavior = behavior === 'position';
124
+ const isPositionBehavior = behavior === "position";
124
125
  const containerStyle = isPositionBehavior ? contentContainerStyle : style;
125
126
  const combinedStyles = useMemo(
126
127
  () => [containerStyle, animatedStyle],
127
- [containerStyle, animatedStyle]
128
+ [containerStyle, animatedStyle],
128
129
  );
129
130
 
130
131
  if (isPositionBehavior) {
@@ -145,7 +146,7 @@ const KeyboardAvoidingView = forwardRef<View, React.PropsWithChildren<Props>>(
145
146
  {children}
146
147
  </Reanimated.View>
147
148
  );
148
- }
149
+ },
149
150
  );
150
151
 
151
152
  export default KeyboardAvoidingView;
@@ -1,5 +1,5 @@
1
- import React, { useCallback, useMemo } from 'react';
2
- import { useWindowDimensions } from 'react-native';
1
+ import React, { useCallback, useMemo } from "react";
2
+ import { useWindowDimensions } from "react-native";
3
3
  import Reanimated, {
4
4
  interpolate,
5
5
  scrollTo,
@@ -8,19 +8,19 @@ import Reanimated, {
8
8
  useAnimatedScrollHandler,
9
9
  useAnimatedStyle,
10
10
  useSharedValue,
11
- } from 'react-native-reanimated';
11
+ } from "react-native-reanimated";
12
12
 
13
13
  import {
14
14
  useFocusedInputHandler,
15
15
  useReanimatedFocusedInput,
16
- } from 'react-native-keyboard-controller';
16
+ } from "react-native-keyboard-controller";
17
17
 
18
- import { useSmoothKeyboardHandler } from './useSmoothKeyboardHandler';
19
- import { debounce } from './utils';
18
+ import { useSmoothKeyboardHandler } from "./useSmoothKeyboardHandler";
19
+ import { debounce } from "./utils";
20
20
 
21
- import type { FC } from 'react';
22
- import type { ScrollViewProps } from 'react-native';
23
- import type { FocusedInputLayoutChangedEvent } from 'react-native-keyboard-controller';
21
+ import type { FC } from "react";
22
+ import type { ScrollViewProps } from "react-native";
23
+ import type { FocusedInputLayoutChangedEvent } from "react-native-keyboard-controller";
24
24
 
25
25
  type KeyboardAwareScrollViewProps = {
26
26
  bottomOffset?: number;
@@ -72,6 +72,7 @@ const KeyboardAwareScrollView: FC<KeyboardAwareScrollViewProps> = ({
72
72
  const scrollViewAnimatedRef = useAnimatedRef<Reanimated.ScrollView>();
73
73
  const scrollPosition = useSharedValue(0);
74
74
  const position = useSharedValue(0);
75
+ const currentKeyboardFrameHeight = useSharedValue(0);
75
76
  const keyboardHeight = useSharedValue(0);
76
77
  const tag = useSharedValue(-1);
77
78
  const initialKeyboardSize = useSharedValue(0);
@@ -87,7 +88,7 @@ const KeyboardAwareScrollView: FC<KeyboardAwareScrollViewProps> = ({
87
88
  position.value = e.contentOffset.y;
88
89
  },
89
90
  },
90
- []
91
+ [],
91
92
  );
92
93
 
93
94
  /**
@@ -95,7 +96,7 @@ const KeyboardAwareScrollView: FC<KeyboardAwareScrollViewProps> = ({
95
96
  */
96
97
  const maybeScroll = useCallback(
97
98
  (e: number, animated: boolean = false) => {
98
- 'worklet';
99
+ "worklet";
99
100
 
100
101
  const visibleRect = height - keyboardHeight.value;
101
102
  const absoluteY = layout.value?.layout.absoluteY || 0;
@@ -106,7 +107,7 @@ const KeyboardAwareScrollView: FC<KeyboardAwareScrollViewProps> = ({
106
107
  const interpolatedScrollTo = interpolate(
107
108
  e,
108
109
  [initialKeyboardSize.value, keyboardHeight.value],
109
- [0, keyboardHeight.value - (height - point) + bottomOffset]
110
+ [0, keyboardHeight.value - (height - point) + bottomOffset],
110
111
  );
111
112
  const targetScrollY =
112
113
  Math.max(interpolatedScrollTo, 0) + scrollPosition.value;
@@ -123,17 +124,17 @@ const KeyboardAwareScrollView: FC<KeyboardAwareScrollViewProps> = ({
123
124
  scrollViewAnimatedRef,
124
125
  0,
125
126
  topOfScreen - positionOnScreen,
126
- animated
127
+ animated,
127
128
  );
128
129
  }
129
130
 
130
131
  return 0;
131
132
  },
132
- [bottomOffset]
133
+ [bottomOffset],
133
134
  );
134
135
 
135
136
  const onChangeText = useCallback(() => {
136
- 'worklet';
137
+ "worklet";
137
138
 
138
139
  // if typing a text caused layout shift, then we need to ignore this handler
139
140
  // because this event will be handled in `useAnimatedReaction` below
@@ -152,20 +153,20 @@ const KeyboardAwareScrollView: FC<KeyboardAwareScrollViewProps> = ({
152
153
  }, [maybeScroll]);
153
154
  const onChangeTextHandler = useMemo(
154
155
  () => debounce(onChangeText, 200),
155
- [onChangeText]
156
+ [onChangeText],
156
157
  );
157
158
 
158
159
  useFocusedInputHandler(
159
160
  {
160
161
  onChangeText: onChangeTextHandler,
161
162
  },
162
- [onChangeTextHandler]
163
+ [onChangeTextHandler],
163
164
  );
164
165
 
165
166
  useSmoothKeyboardHandler(
166
167
  {
167
168
  onStart: (e) => {
168
- 'worklet';
169
+ "worklet";
169
170
 
170
171
  const keyboardWillChangeSize =
171
172
  keyboardHeight.value !== e.height && e.height > 0;
@@ -209,18 +210,20 @@ const KeyboardAwareScrollView: FC<KeyboardAwareScrollViewProps> = ({
209
210
  }
210
211
  },
211
212
  onMove: (e) => {
212
- 'worklet';
213
+ "worklet";
214
+
215
+ currentKeyboardFrameHeight.value = e.height;
213
216
 
214
217
  maybeScroll(e.height);
215
218
  },
216
219
  onEnd: (e) => {
217
- 'worklet';
220
+ "worklet";
218
221
 
219
222
  keyboardHeight.value = e.height;
220
223
  scrollPosition.value = position.value;
221
224
  },
222
225
  },
223
- [height, maybeScroll]
226
+ [height, maybeScroll],
224
227
  );
225
228
 
226
229
  useAnimatedReaction(
@@ -237,14 +240,14 @@ const KeyboardAwareScrollView: FC<KeyboardAwareScrollViewProps> = ({
237
240
  layout.value = prevLayout;
238
241
  }
239
242
  },
240
- []
243
+ [],
241
244
  );
242
245
 
243
246
  const view = useAnimatedStyle(
244
247
  () => ({
245
- paddingBottom: keyboardHeight.value,
248
+ paddingBottom: currentKeyboardFrameHeight.value,
246
249
  }),
247
- []
250
+ [],
248
251
  );
249
252
 
250
253
  return (
@@ -254,7 +257,8 @@ const KeyboardAwareScrollView: FC<KeyboardAwareScrollViewProps> = ({
254
257
  onScroll={onScroll}
255
258
  scrollEventThrottle={16}
256
259
  >
257
- <Reanimated.View style={view}>{children}</Reanimated.View>
260
+ {children}
261
+ <Reanimated.View style={view} />
258
262
  </Reanimated.ScrollView>
259
263
  );
260
264
  };
@@ -1,18 +1,18 @@
1
- import { Platform } from 'react-native';
1
+ import { Platform } from "react-native";
2
2
  import {
3
3
  Easing,
4
4
  useAnimatedReaction,
5
5
  useSharedValue,
6
6
  withTiming,
7
- } from 'react-native-reanimated';
7
+ } from "react-native-reanimated";
8
8
 
9
- import { useKeyboardHandler } from 'react-native-keyboard-controller';
9
+ import { useKeyboardHandler } from "react-native-keyboard-controller";
10
10
 
11
11
  const IS_ANDROID_ELEVEN_OR_HIGHER =
12
- Platform.OS === 'android' && Platform.Version >= 30;
12
+ Platform.OS === "android" && Platform.Version >= 30;
13
13
  // on these platforms keyboard transitions will be smooth
14
14
  const IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS =
15
- IS_ANDROID_ELEVEN_OR_HIGHER || Platform.OS === 'ios';
15
+ IS_ANDROID_ELEVEN_OR_HIGHER || Platform.OS === "ios";
16
16
  // on Android Telegram is not using androidx.core values and uses custom interpolation
17
17
  // duration is taken from here: https://github.com/DrKLO/Telegram/blob/e9a35cea54c06277c69d41b8e25d94b5d7ede065/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AdjustPanLayoutHelper.java#L39
18
18
  // and bezier is taken from: https://github.com/DrKLO/Telegram/blob/e9a35cea54c06277c69d41b8e25d94b5d7ede065/TMessagesProj/src/main/java/androidx/recyclerview/widget/ChatListItemAnimator.java#L40
@@ -22,7 +22,7 @@ const TELEGRAM_ANDROID_TIMING_CONFIG = {
22
22
  0.19919472913616398,
23
23
  0.010644531250000006,
24
24
  0.27920937042459737,
25
- 0.91025390625
25
+ 0.91025390625,
26
26
  ),
27
27
  };
28
28
 
@@ -32,7 +32,7 @@ const TELEGRAM_ANDROID_TIMING_CONFIG = {
32
32
  */
33
33
  export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
34
34
  handler,
35
- deps
35
+ deps,
36
36
  ) => {
37
37
  const target = useSharedValue(-1);
38
38
  const persistedHeight = useSharedValue(0);
@@ -64,13 +64,13 @@ export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
64
64
  handler.onEnd?.(evt);
65
65
  }
66
66
  },
67
- [handler]
67
+ [handler],
68
68
  );
69
69
 
70
70
  useKeyboardHandler(
71
71
  {
72
72
  onStart: (e) => {
73
- 'worklet';
73
+ "worklet";
74
74
 
75
75
  // immediately dispatch onStart/onEnd events if onStart dispatched with the same height
76
76
  // and don't wait for animation 250ms
@@ -95,21 +95,21 @@ export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
95
95
  if (!IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {
96
96
  animatedKeyboardHeight.value = withTiming(
97
97
  e.height,
98
- TELEGRAM_ANDROID_TIMING_CONFIG
98
+ TELEGRAM_ANDROID_TIMING_CONFIG,
99
99
  );
100
100
  }
101
101
 
102
102
  handler.onStart?.(e);
103
103
  },
104
104
  onMove: (e) => {
105
- 'worklet';
105
+ "worklet";
106
106
 
107
107
  if (IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {
108
108
  handler.onMove?.(e);
109
109
  }
110
110
  },
111
111
  onEnd: (e) => {
112
- 'worklet';
112
+ "worklet";
113
113
 
114
114
  if (IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {
115
115
  handler.onEnd?.(e);
@@ -118,6 +118,6 @@ export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
118
118
  persistedHeight.value = e.height;
119
119
  },
120
120
  },
121
- deps
121
+ deps,
122
122
  );
123
123
  };
@@ -1,15 +1,15 @@
1
1
  export const debounce = <F extends (...args: Parameters<F>) => ReturnType<F>>(
2
2
  worklet: F,
3
- wait = 0
3
+ wait = 0,
4
4
  ) => {
5
- 'worklet';
5
+ "worklet";
6
6
 
7
7
  const value = {
8
8
  time: 0,
9
9
  };
10
10
 
11
11
  return (...args: Parameters<F>): ReturnType<F> | void => {
12
- 'worklet';
12
+ "worklet";
13
13
 
14
14
  const t = Date.now();
15
15
  const now = t - value.time;
@@ -1,12 +1,11 @@
1
- import React, { forwardRef, useMemo } from 'react';
2
- import Reanimated, {
3
- interpolate,
4
- useAnimatedStyle,
5
- } from 'react-native-reanimated';
1
+ import React, { forwardRef, useMemo } from "react";
2
+ import Reanimated, { useAnimatedStyle } from "react-native-reanimated";
6
3
 
7
- import { useReanimatedKeyboardAnimation } from '../../hooks';
4
+ import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller";
8
5
 
9
- import type { View, ViewProps } from 'react-native';
6
+ import useKeyboardInterpolation from "../hooks/useKeyboardInterpolation";
7
+
8
+ import type { View, ViewProps } from "react-native";
10
9
 
11
10
  type KeyboardStickyViewProps = {
12
11
  /**
@@ -30,12 +29,13 @@ const KeyboardStickyView = forwardRef<
30
29
  >(
31
30
  (
32
31
  { children, offset: { closed = 0, opened = 0 } = {}, style, ...props },
33
- ref
32
+ ref,
34
33
  ) => {
35
- const { height, progress } = useReanimatedKeyboardAnimation();
34
+ const { height } = useReanimatedKeyboardAnimation();
35
+ const { interpolate } = useKeyboardInterpolation();
36
36
 
37
37
  const stickyViewStyle = useAnimatedStyle(() => {
38
- const offset = interpolate(progress.value, [0, 1], [closed, opened]);
38
+ const offset = interpolate(-height.value, [closed, opened]);
39
39
 
40
40
  return {
41
41
  transform: [{ translateY: height.value + offset }],
@@ -44,7 +44,7 @@ const KeyboardStickyView = forwardRef<
44
44
 
45
45
  const styles = useMemo(
46
46
  () => [style, stickyViewStyle],
47
- [style, stickyViewStyle]
47
+ [style, stickyViewStyle],
48
48
  );
49
49
 
50
50
  return (
@@ -52,7 +52,7 @@ const KeyboardStickyView = forwardRef<
52
52
  {children}
53
53
  </Reanimated.View>
54
54
  );
55
- }
55
+ },
56
56
  );
57
57
 
58
58
  export default KeyboardStickyView;
@@ -0,0 +1,108 @@
1
+ import { Platform } from "react-native";
2
+ import {
3
+ interpolate as interpolateREA,
4
+ useSharedValue,
5
+ } from "react-native-reanimated";
6
+
7
+ import { useKeyboardHandler } from "react-native-keyboard-controller";
8
+
9
+ type KeyboardInterpolationOutput = [number, number];
10
+
11
+ /**
12
+ * Hook that can be used for interpolation keyboard movement. The main concern is the thing
13
+ * when keyboard is opened and gets resized on Android. Let's say we are interpolating from
14
+ * closed to open [0, 200] and we want to interpolate it to [0, 230] (to achieve nice parallax effect).
15
+ * Then let's say keyboard changes its height to 220 (and we want to interpolate the value to 250, +30
16
+ * to keyboard height). If we interpolate based on `progress` value, then we will have a jump on first frame:
17
+ * the last interpolated position was 230, now we will interpolate to 250, but first frame will be calculated
18
+ * as 200 / 220 * 250 = 227 (and last interpolated position was 230) so we will have a jump.
19
+ *
20
+ * This hook handles it, and when keyboard changes its size it does an interpolation as:
21
+ * [200, 220] -> [230, 250], i. e. we preserve last interpolated value and use it as initial value for interpolation
22
+ * and because of that we will not have a jump and animation will start from the last frame and will be smooth.
23
+ *
24
+ * @see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/315
25
+ */
26
+ const useKeyboardInterpolation = () => {
27
+ // keyboard heights
28
+ const nextKeyboardHeight = useSharedValue(0);
29
+ const prevKeyboardHeight = useSharedValue(0);
30
+ // save latest interpolated position
31
+ const lastInterpolation = useSharedValue(0);
32
+ // boolean flag indicating which output range should be used
33
+ const shouldUseInternalInterpolation = useSharedValue(false);
34
+
35
+ const interpolate = (
36
+ keyboardPosition: number,
37
+ output: KeyboardInterpolationOutput,
38
+ ) => {
39
+ "worklet";
40
+
41
+ // on iOS it's safe to interpolate between 0 and `fullKeyboardSize` because when
42
+ // keyboard resized we will not have intermediate values and transition will be instant
43
+ // see: https://github.com/kirillzyusko/react-native-keyboard-controller/issues/327
44
+ if (Platform.OS === "ios") {
45
+ return interpolateREA(
46
+ keyboardPosition,
47
+ [0, nextKeyboardHeight.value],
48
+ output,
49
+ );
50
+ }
51
+
52
+ lastInterpolation.value = interpolateREA(
53
+ keyboardPosition,
54
+ [prevKeyboardHeight.value, nextKeyboardHeight.value],
55
+ shouldUseInternalInterpolation.value
56
+ ? [lastInterpolation.value, output[1]]
57
+ : output,
58
+ );
59
+
60
+ return lastInterpolation.value;
61
+ };
62
+
63
+ useKeyboardHandler(
64
+ {
65
+ onStart: (e) => {
66
+ "worklet";
67
+
68
+ const keyboardWillBeHidden = e.height === 0;
69
+
70
+ // keyboard will be hidden
71
+ if (keyboardWillBeHidden) {
72
+ shouldUseInternalInterpolation.value = false;
73
+ prevKeyboardHeight.value = 0;
74
+ }
75
+
76
+ // keyboard will change its size
77
+ if (
78
+ // keyboard is shown on screen
79
+ nextKeyboardHeight.value !== 0 &&
80
+ // it really changes size (handles iOS case when after interactive keyboard gets shown again)
81
+ nextKeyboardHeight.value !== e.height &&
82
+ // keyboard is not hiding
83
+ !keyboardWillBeHidden
84
+ ) {
85
+ prevKeyboardHeight.value = nextKeyboardHeight.value;
86
+ shouldUseInternalInterpolation.value = true;
87
+ }
88
+
89
+ // keyboard will show or change size
90
+ if (!keyboardWillBeHidden) {
91
+ nextKeyboardHeight.value = e.height;
92
+ }
93
+ },
94
+ onEnd: (e) => {
95
+ "worklet";
96
+
97
+ // handles case show -> resize -> hide -> show
98
+ // here we reset value to 0 when keyboard is hidden
99
+ nextKeyboardHeight.value = e.height;
100
+ },
101
+ },
102
+ [],
103
+ );
104
+
105
+ return { interpolate };
106
+ };
107
+
108
+ export default useKeyboardInterpolation;
@@ -1,3 +1,3 @@
1
- export { default as KeyboardAvoidingView } from './KeyboardAvoidingView';
2
- export { default as KeyboardStickyView } from './KeyboardStickyView';
3
- export { default as KeyboardAwareScrollView } from './KeyboardAwareScrollView';
1
+ export { default as KeyboardAvoidingView } from "./KeyboardAvoidingView";
2
+ export { default as KeyboardStickyView } from "./KeyboardStickyView";
3
+ export { default as KeyboardAwareScrollView } from "./KeyboardAwareScrollView";
package/src/context.ts CHANGED
@@ -1,13 +1,13 @@
1
- import { createContext, useContext } from 'react';
2
- import { Animated } from 'react-native';
1
+ import { createContext, useContext } from "react";
2
+ import { Animated } from "react-native";
3
3
 
4
4
  import type {
5
5
  FocusedInputHandlers,
6
6
  FocusedInputLayoutChangedEvent,
7
7
  KeyboardHandlers,
8
- } from './types';
9
- import type React from 'react';
10
- import type { SharedValue } from 'react-native-reanimated';
8
+ } from "./types";
9
+ import type React from "react";
10
+ import type { SharedValue } from "react-native-reanimated";
11
11
 
12
12
  export type AnimatedContext = {
13
13
  progress: Animated.Value;
@@ -35,7 +35,7 @@ const withSharedValue = <T>(value: T): SharedValue<T> => ({
35
35
  });
36
36
  const DEFAULT_SHARED_VALUE = withSharedValue(0);
37
37
  const DEFAULT_LAYOUT = withSharedValue<FocusedInputLayoutChangedEvent | null>(
38
- null
38
+ null,
39
39
  );
40
40
  const defaultContext: KeyboardAnimationContext = {
41
41
  enabled: true,
@@ -58,7 +58,7 @@ export const useKeyboardContext = () => {
58
58
 
59
59
  if (__DEV__ && context === defaultContext) {
60
60
  console.warn(
61
- "Couldn't find real values for `KeyboardContext`. Please make sure you're inside of `KeyboardProvider` - otherwise functionality of `react-native-keyboard-controller` will not work."
61
+ "Couldn't find real values for `KeyboardContext`. Please make sure you're inside of `KeyboardProvider` - otherwise functionality of `react-native-keyboard-controller` will not work.",
62
62
  );
63
63
  }
64
64
 
package/src/hooks.ts CHANGED
@@ -1,18 +1,18 @@
1
- import { useEffect } from 'react';
1
+ import { useEffect } from "react";
2
2
 
3
- import { KeyboardController } from './bindings';
4
- import { AndroidSoftInputModes } from './constants';
5
- import { useKeyboardContext } from './context';
6
- import { uuid } from './utils';
3
+ import { KeyboardController } from "./bindings";
4
+ import { AndroidSoftInputModes } from "./constants";
5
+ import { useKeyboardContext } from "./context";
6
+ import { uuid } from "./utils";
7
7
 
8
- import type { AnimatedContext, ReanimatedContext } from './context';
9
- import type { FocusedInputHandler, KeyboardHandler } from './types';
10
- import type { DependencyList } from 'react';
8
+ import type { AnimatedContext, ReanimatedContext } from "./context";
9
+ import type { FocusedInputHandler, KeyboardHandler } from "./types";
10
+ import type { DependencyList } from "react";
11
11
 
12
12
  export const useResizeMode = () => {
13
13
  useEffect(() => {
14
14
  KeyboardController.setInputMode(
15
- AndroidSoftInputModes.SOFT_INPUT_ADJUST_RESIZE
15
+ AndroidSoftInputModes.SOFT_INPUT_ADJUST_RESIZE,
16
16
  );
17
17
 
18
18
  return () => KeyboardController.setDefaultMode();
@@ -35,7 +35,7 @@ export const useReanimatedKeyboardAnimation = (): ReanimatedContext => {
35
35
 
36
36
  export function useGenericKeyboardHandler(
37
37
  handler: KeyboardHandler,
38
- deps?: DependencyList
38
+ deps?: DependencyList,
39
39
  ) {
40
40
  const context = useKeyboardContext();
41
41
 
@@ -52,7 +52,7 @@ export function useGenericKeyboardHandler(
52
52
 
53
53
  export function useKeyboardHandler(
54
54
  handler: KeyboardHandler,
55
- deps?: DependencyList
55
+ deps?: DependencyList,
56
56
  ) {
57
57
  useResizeMode();
58
58
  useGenericKeyboardHandler(handler, deps);
@@ -72,7 +72,7 @@ export function useReanimatedFocusedInput() {
72
72
 
73
73
  export function useFocusedInputHandler(
74
74
  handler?: FocusedInputHandler,
75
- deps?: DependencyList
75
+ deps?: DependencyList,
76
76
  ) {
77
77
  const context = useKeyboardContext();
78
78