react-native-keyboard-controller 1.10.3 → 1.10.5
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.
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js +25 -7
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/commonjs/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.js +13 -6
- package/lib/commonjs/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.js.map +1 -1
- package/lib/module/components/KeyboardAwareScrollView/index.js +26 -8
- package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/module/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.js +13 -6
- package/lib/module/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.js.map +1 -1
- package/lib/typescript/components/KeyboardAwareScrollView/index.d.ts +10 -7
- package/package.json +1 -1
- package/src/components/KeyboardAwareScrollView/index.tsx +226 -191
- package/src/components/KeyboardAwareScrollView/useSmoothKeyboardHandler.ts +15 -7
|
@@ -51,11 +51,12 @@ function _extends() { _extends = Object.assign ? Object.assign.bind() : function
|
|
|
51
51
|
* +============================+ +============================+ +=====================================+
|
|
52
52
|
*
|
|
53
53
|
*/
|
|
54
|
-
const KeyboardAwareScrollView = _ref => {
|
|
54
|
+
const KeyboardAwareScrollView = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
|
|
55
55
|
let {
|
|
56
56
|
children,
|
|
57
57
|
bottomOffset = 0,
|
|
58
58
|
disableScrollOnKeyboardHide = false,
|
|
59
|
+
enabled = true,
|
|
59
60
|
...rest
|
|
60
61
|
} = _ref;
|
|
61
62
|
const scrollViewAnimatedRef = (0, _reactNativeReanimated.useAnimatedRef)();
|
|
@@ -79,6 +80,14 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
79
80
|
position.value = e.contentOffset.y;
|
|
80
81
|
}
|
|
81
82
|
}, []);
|
|
83
|
+
const onRef = (0, _react.useCallback)(assignedRef => {
|
|
84
|
+
if (typeof ref === "function") {
|
|
85
|
+
ref(assignedRef);
|
|
86
|
+
} else if (ref) {
|
|
87
|
+
ref.current = assignedRef;
|
|
88
|
+
}
|
|
89
|
+
scrollViewAnimatedRef(assignedRef);
|
|
90
|
+
}, []);
|
|
82
91
|
|
|
83
92
|
/**
|
|
84
93
|
* Function that will scroll a ScrollView as keyboard gets moving
|
|
@@ -88,6 +97,9 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
88
97
|
|
|
89
98
|
var _layout$value, _layout$value2;
|
|
90
99
|
let animated = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
100
|
+
if (!enabled) {
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
91
103
|
const visibleRect = height - keyboardHeight.value;
|
|
92
104
|
const absoluteY = ((_layout$value = layout.value) === null || _layout$value === void 0 ? void 0 : _layout$value.layout.absoluteY) || 0;
|
|
93
105
|
const inputHeight = ((_layout$value2 = layout.value) === null || _layout$value2 === void 0 ? void 0 : _layout$value2.layout.height) || 0;
|
|
@@ -104,7 +116,7 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
104
116
|
(0, _reactNativeReanimated.scrollTo)(scrollViewAnimatedRef, 0, topOfScreen - positionOnScreen, animated);
|
|
105
117
|
}
|
|
106
118
|
return 0;
|
|
107
|
-
}, [bottomOffset]);
|
|
119
|
+
}, [bottomOffset, enabled]);
|
|
108
120
|
const onChangeText = (0, _react.useCallback)(() => {
|
|
109
121
|
"worklet";
|
|
110
122
|
|
|
@@ -126,7 +138,7 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
126
138
|
(0, _reactNativeKeyboardController.useFocusedInputHandler)({
|
|
127
139
|
onChangeText: onChangeTextHandler
|
|
128
140
|
}, [onChangeTextHandler]);
|
|
129
|
-
(0, _useSmoothKeyboardHandler.useSmoothKeyboardHandler)({
|
|
141
|
+
(0, _useSmoothKeyboardHandler.useSmoothKeyboardHandler)(enabled ? {
|
|
130
142
|
onStart: e => {
|
|
131
143
|
"worklet";
|
|
132
144
|
|
|
@@ -181,7 +193,7 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
181
193
|
keyboardHeight.value = e.height;
|
|
182
194
|
scrollPosition.value = position.value;
|
|
183
195
|
}
|
|
184
|
-
}, [height, maybeScroll, disableScrollOnKeyboardHide]);
|
|
196
|
+
} : {}, [height, maybeScroll, disableScrollOnKeyboardHide, enabled]);
|
|
185
197
|
(0, _reactNativeReanimated.useAnimatedReaction)(() => input.value, (current, previous) => {
|
|
186
198
|
if ((current === null || current === void 0 ? void 0 : current.target) === (previous === null || previous === void 0 ? void 0 : previous.target) && (current === null || current === void 0 ? void 0 : current.layout.height) !== (previous === null || previous === void 0 ? void 0 : previous.layout.height)) {
|
|
187
199
|
const prevLayout = layout.value;
|
|
@@ -191,10 +203,16 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
191
203
|
}
|
|
192
204
|
}, []);
|
|
193
205
|
const view = (0, _reactNativeReanimated.useAnimatedStyle)(() => ({
|
|
194
|
-
|
|
206
|
+
// animations become laggy when scrolling to the end of the `ScrollView` (when the last input is focused)
|
|
207
|
+
// this happens because the layout recalculates on every frame. To avoid this we slightly increase padding
|
|
208
|
+
// by `+1`. In this way we assure, that `scrollTo` will never scroll to the end, because it uses interpolation
|
|
209
|
+
// from 0 to `keyboardHeight`, and here our padding is `keyboardHeight + 1`. It allows us not to re-run layout
|
|
210
|
+
// re-calculation on every animation frame and it helps to achieve smooth animation.
|
|
211
|
+
// see: https://github.com/kirillzyusko/react-native-keyboard-controller/pull/342
|
|
212
|
+
paddingBottom: currentKeyboardFrameHeight.value + 1
|
|
195
213
|
}), []);
|
|
196
214
|
return /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.ScrollView, _extends({
|
|
197
|
-
ref:
|
|
215
|
+
ref: onRef
|
|
198
216
|
}, rest, {
|
|
199
217
|
// @ts-expect-error `onScrollReanimated` is a fake prop needed for reanimated to intercept scroll events
|
|
200
218
|
onScrollReanimated: onScroll,
|
|
@@ -202,7 +220,7 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
202
220
|
}), children, /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, {
|
|
203
221
|
style: view
|
|
204
222
|
}));
|
|
205
|
-
};
|
|
223
|
+
});
|
|
206
224
|
var _default = KeyboardAwareScrollView;
|
|
207
225
|
exports.default = _default;
|
|
208
226
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","_reactNativeKeyboardController","_useSmoothKeyboardHandler","_utils","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","_extends","assign","bind","target","i","arguments","length","source","apply","KeyboardAwareScrollView","_ref","children","bottomOffset","disableScrollOnKeyboardHide","rest","scrollViewAnimatedRef","useAnimatedRef","scrollPosition","useSharedValue","position","currentKeyboardFrameHeight","keyboardHeight","keyboardWillAppear","tag","initialKeyboardSize","scrollBeforeKeyboardMovement","input","useReanimatedFocusedInput","layout","height","useWindowDimensions","onScroll","useAnimatedScrollHandler","e","value","contentOffset","y","maybeScroll","useCallback","_layout$value","_layout$value2","animated","undefined","visibleRect","absoluteY","inputHeight","point","interpolatedScrollTo","interpolate","targetScrollY","Math","max","scrollTo","positionOnScreen","topOfScreen","onChangeText","_layout$value3","_input$value","prevScrollPosition","prevLayout","onChangeTextHandler","useMemo","debounce","useFocusedInputHandler","useSmoothKeyboardHandler","onStart","keyboardWillChangeSize","keyboardWillHide","focusWasChanged","onMove","onEnd","useAnimatedReaction","current","previous","view","useAnimatedStyle","paddingBottom","createElement","ScrollView","ref","onScrollReanimated","scrollEventThrottle","View","style","_default","exports"],"sources":["index.tsx"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport { useWindowDimensions } from \"react-native\";\nimport Reanimated, {\n interpolate,\n scrollTo,\n useAnimatedReaction,\n useAnimatedRef,\n useAnimatedScrollHandler,\n useAnimatedStyle,\n useSharedValue,\n} from \"react-native-reanimated\";\n\nimport {\n useFocusedInputHandler,\n useReanimatedFocusedInput,\n} from \"react-native-keyboard-controller\";\n\nimport { useSmoothKeyboardHandler } from \"./useSmoothKeyboardHandler\";\nimport { debounce } from \"./utils\";\n\nimport type { FC } from \"react\";\nimport type { ScrollViewProps } from \"react-native\";\nimport type { FocusedInputLayoutChangedEvent } from \"react-native-keyboard-controller\";\n\ntype KeyboardAwareScrollViewProps = {\n /** The distance between keyboard and focused `TextInput` when keyboard is shown. Default is `0`. */\n bottomOffset?: number;\n /** Prevents automatic scrolling of the `ScrollView` when the keyboard gets hidden, maintaining the current screen position. Default is `false`. */\n disableScrollOnKeyboardHide?: boolean;\n} & ScrollViewProps;\n\n/*\n * Everything begins from `onStart` handler. This handler is called every time,\n * when keyboard changes its size or when focused `TextInput` was changed. In\n * this handler we are calculating/memoizing values which later will be used\n * during layout movement. For that we calculate:\n * - layout of focused field (`layout`) - to understand whether there will be overlap\n * - initial keyboard size (`initialKeyboardSize`) - used in scroll interpolation\n * - future keyboard height (`keyboardHeight`) - used in scroll interpolation\n * - current scroll position (`scrollPosition`) - used to scroll from this point\n *\n * Once we've calculated all necessary variables - we can actually start to use them.\n * It happens in `onMove` handler - this function simply calls `maybeScroll` with\n * current keyboard frame height. This functions makes the smooth transition.\n *\n * When the transition has finished we go to `onEnd` handler. In this handler\n * we verify, that the current field is not overlapped within a keyboard frame.\n * For full `onStart`/`onMove`/`onEnd` flow it may look like a redundant thing,\n * however there could be some cases, when `onMove` is not called:\n * - on iOS when TextInput was changed - keyboard transition is instant\n * - on Android when TextInput was changed and keyboard size wasn't changed\n * So `onEnd` handler handle the case, when `onMove` wasn't triggered.\n *\n * ====================================================================================================================+\n * -----------------------------------------------------Flow chart-----------------------------------------------------+\n * ====================================================================================================================+\n *\n * +============================+ +============================+ +==================================+\n * + User Press on TextInput + => + Keyboard starts showing + => + As keyboard moves frame by frame + =>\n * + + + (run `onStart`) + + `onMove` is getting called +\n * +============================+ +============================+ +==================================+\n *\n *\n * +============================+ +============================+ +=====================================+\n * + Keyboard is shown and we + => + User moved focus to + => + Only `onStart`/`onEnd` maybe called +\n * + call `onEnd` handler + + another `TextInput` + + (without involving `onMove`) +\n * +============================+ +============================+ +=====================================+\n *\n */\nconst KeyboardAwareScrollView: FC<KeyboardAwareScrollViewProps> = ({\n children,\n bottomOffset = 0,\n disableScrollOnKeyboardHide = false,\n ...rest\n}) => {\n const scrollViewAnimatedRef = useAnimatedRef<Reanimated.ScrollView>();\n const scrollPosition = useSharedValue(0);\n const position = useSharedValue(0);\n const currentKeyboardFrameHeight = useSharedValue(0);\n const keyboardHeight = useSharedValue(0);\n const keyboardWillAppear = useSharedValue(false);\n const tag = useSharedValue(-1);\n const initialKeyboardSize = useSharedValue(0);\n const scrollBeforeKeyboardMovement = useSharedValue(0);\n const { input } = useReanimatedFocusedInput();\n const layout = useSharedValue<FocusedInputLayoutChangedEvent | null>(null);\n\n const { height } = useWindowDimensions();\n\n const onScroll = useAnimatedScrollHandler(\n {\n onScroll: (e) => {\n position.value = e.contentOffset.y;\n },\n },\n [],\n );\n\n /**\n * Function that will scroll a ScrollView as keyboard gets moving\n */\n const maybeScroll = useCallback(\n (e: number, animated: boolean = false) => {\n \"worklet\";\n\n const visibleRect = height - keyboardHeight.value;\n const absoluteY = layout.value?.layout.absoluteY || 0;\n const inputHeight = layout.value?.layout.height || 0;\n const point = absoluteY + inputHeight;\n\n if (visibleRect - point <= bottomOffset) {\n const interpolatedScrollTo = interpolate(\n e,\n [initialKeyboardSize.value, keyboardHeight.value],\n [0, keyboardHeight.value - (height - point) + bottomOffset],\n );\n const targetScrollY =\n Math.max(interpolatedScrollTo, 0) + scrollPosition.value;\n scrollTo(scrollViewAnimatedRef, 0, targetScrollY, animated);\n\n return interpolatedScrollTo;\n }\n\n if (absoluteY < 0) {\n const positionOnScreen = visibleRect - inputHeight - bottomOffset;\n const topOfScreen = scrollPosition.value + absoluteY;\n\n scrollTo(\n scrollViewAnimatedRef,\n 0,\n topOfScreen - positionOnScreen,\n animated,\n );\n }\n\n return 0;\n },\n [bottomOffset],\n );\n\n const onChangeText = useCallback(() => {\n \"worklet\";\n\n // if typing a text caused layout shift, then we need to ignore this handler\n // because this event will be handled in `useAnimatedReaction` below\n if (layout.value?.layout.height !== input.value?.layout.height) {\n return;\n }\n\n const prevScrollPosition = scrollPosition.value;\n const prevLayout = layout.value;\n\n scrollPosition.value = position.value;\n layout.value = input.value;\n maybeScroll(keyboardHeight.value, true);\n scrollPosition.value = prevScrollPosition;\n layout.value = prevLayout;\n }, [maybeScroll]);\n const onChangeTextHandler = useMemo(\n () => debounce(onChangeText, 200),\n [onChangeText],\n );\n\n useFocusedInputHandler(\n {\n onChangeText: onChangeTextHandler,\n },\n [onChangeTextHandler],\n );\n\n useSmoothKeyboardHandler(\n {\n onStart: (e) => {\n \"worklet\";\n\n const keyboardWillChangeSize =\n keyboardHeight.value !== e.height && e.height > 0;\n keyboardWillAppear.value = e.height > 0 && keyboardHeight.value === 0;\n const keyboardWillHide = e.height === 0;\n const focusWasChanged =\n (tag.value !== e.target && e.target !== -1) || keyboardWillChangeSize;\n\n if (keyboardWillChangeSize) {\n initialKeyboardSize.value = keyboardHeight.value;\n }\n\n if (keyboardWillHide) {\n // on back transition need to interpolate as [0, keyboardHeight]\n initialKeyboardSize.value = 0;\n scrollPosition.value = scrollBeforeKeyboardMovement.value;\n }\n\n if (\n keyboardWillAppear.value ||\n keyboardWillChangeSize ||\n focusWasChanged\n ) {\n // persist scroll value\n scrollPosition.value = position.value;\n // just persist height - later will be used in interpolation\n keyboardHeight.value = e.height;\n }\n\n // focus was changed\n if (focusWasChanged) {\n tag.value = e.target;\n\n // save position of focused text input when keyboard starts to move\n layout.value = input.value;\n // save current scroll position - when keyboard will hide we'll reuse\n // this value to achieve smooth hide effect\n scrollBeforeKeyboardMovement.value = position.value;\n }\n\n if (focusWasChanged && !keyboardWillAppear.value) {\n // update position on scroll value, so `onEnd` handler\n // will pick up correct values\n position.value += maybeScroll(e.height, true);\n }\n },\n onMove: (e) => {\n \"worklet\";\n\n currentKeyboardFrameHeight.value = e.height;\n\n // if the user has set disableScrollOnKeyboardHide, only auto-scroll when the keyboard opens\n if (!disableScrollOnKeyboardHide || keyboardWillAppear.value) {\n maybeScroll(e.height);\n }\n },\n onEnd: (e) => {\n \"worklet\";\n\n keyboardHeight.value = e.height;\n scrollPosition.value = position.value;\n },\n },\n [height, maybeScroll, disableScrollOnKeyboardHide],\n );\n\n useAnimatedReaction(\n () => input.value,\n (current, previous) => {\n if (\n current?.target === previous?.target &&\n current?.layout.height !== previous?.layout.height\n ) {\n const prevLayout = layout.value;\n\n layout.value = input.value;\n scrollPosition.value += maybeScroll(keyboardHeight.value, true);\n layout.value = prevLayout;\n }\n },\n [],\n );\n\n const view = useAnimatedStyle(\n () => ({\n paddingBottom: currentKeyboardFrameHeight.value,\n }),\n [],\n );\n\n return (\n <Reanimated.ScrollView\n ref={scrollViewAnimatedRef}\n {...rest}\n // @ts-expect-error `onScrollReanimated` is a fake prop needed for reanimated to intercept scroll events\n onScrollReanimated={onScroll}\n scrollEventThrottle={16}\n >\n {children}\n <Reanimated.View style={view} />\n </Reanimated.ScrollView>\n );\n};\n\nexport default KeyboardAwareScrollView;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","_reactNativeKeyboardController","_useSmoothKeyboardHandler","_utils","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","_extends","assign","bind","target","i","arguments","length","source","apply","KeyboardAwareScrollView","forwardRef","_ref","ref","children","bottomOffset","disableScrollOnKeyboardHide","enabled","rest","scrollViewAnimatedRef","useAnimatedRef","scrollPosition","useSharedValue","position","currentKeyboardFrameHeight","keyboardHeight","keyboardWillAppear","tag","initialKeyboardSize","scrollBeforeKeyboardMovement","input","useReanimatedFocusedInput","layout","height","useWindowDimensions","onScroll","useAnimatedScrollHandler","e","value","contentOffset","y","onRef","useCallback","assignedRef","current","maybeScroll","_layout$value","_layout$value2","animated","undefined","visibleRect","absoluteY","inputHeight","point","interpolatedScrollTo","interpolate","targetScrollY","Math","max","scrollTo","positionOnScreen","topOfScreen","onChangeText","_layout$value3","_input$value","prevScrollPosition","prevLayout","onChangeTextHandler","useMemo","debounce","useFocusedInputHandler","useSmoothKeyboardHandler","onStart","keyboardWillChangeSize","keyboardWillHide","focusWasChanged","onMove","onEnd","useAnimatedReaction","previous","view","useAnimatedStyle","paddingBottom","createElement","ScrollView","onScrollReanimated","scrollEventThrottle","View","style","_default","exports"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useMemo } from \"react\";\nimport { useWindowDimensions } from \"react-native\";\nimport Reanimated, {\n interpolate,\n scrollTo,\n useAnimatedReaction,\n useAnimatedRef,\n useAnimatedScrollHandler,\n useAnimatedStyle,\n useSharedValue,\n} from \"react-native-reanimated\";\n\nimport {\n useFocusedInputHandler,\n useReanimatedFocusedInput,\n} from \"react-native-keyboard-controller\";\n\nimport { useSmoothKeyboardHandler } from \"./useSmoothKeyboardHandler\";\nimport { debounce } from \"./utils\";\n\nimport type { ScrollView, ScrollViewProps } from \"react-native\";\nimport type { FocusedInputLayoutChangedEvent } from \"react-native-keyboard-controller\";\n\ntype KeyboardAwareScrollViewProps = {\n /** The distance between keyboard and focused `TextInput` when keyboard is shown. Default is `0`. */\n bottomOffset?: number;\n /** Prevents automatic scrolling of the `ScrollView` when the keyboard gets hidden, maintaining the current screen position. Default is `false`. */\n disableScrollOnKeyboardHide?: boolean;\n /** Controls whether this `KeyboardAwareScrollView` instance should take effect. Default is `true` */\n enabled?: boolean;\n} & ScrollViewProps;\n\n/*\n * Everything begins from `onStart` handler. This handler is called every time,\n * when keyboard changes its size or when focused `TextInput` was changed. In\n * this handler we are calculating/memoizing values which later will be used\n * during layout movement. For that we calculate:\n * - layout of focused field (`layout`) - to understand whether there will be overlap\n * - initial keyboard size (`initialKeyboardSize`) - used in scroll interpolation\n * - future keyboard height (`keyboardHeight`) - used in scroll interpolation\n * - current scroll position (`scrollPosition`) - used to scroll from this point\n *\n * Once we've calculated all necessary variables - we can actually start to use them.\n * It happens in `onMove` handler - this function simply calls `maybeScroll` with\n * current keyboard frame height. This functions makes the smooth transition.\n *\n * When the transition has finished we go to `onEnd` handler. In this handler\n * we verify, that the current field is not overlapped within a keyboard frame.\n * For full `onStart`/`onMove`/`onEnd` flow it may look like a redundant thing,\n * however there could be some cases, when `onMove` is not called:\n * - on iOS when TextInput was changed - keyboard transition is instant\n * - on Android when TextInput was changed and keyboard size wasn't changed\n * So `onEnd` handler handle the case, when `onMove` wasn't triggered.\n *\n * ====================================================================================================================+\n * -----------------------------------------------------Flow chart-----------------------------------------------------+\n * ====================================================================================================================+\n *\n * +============================+ +============================+ +==================================+\n * + User Press on TextInput + => + Keyboard starts showing + => + As keyboard moves frame by frame + =>\n * + + + (run `onStart`) + + `onMove` is getting called +\n * +============================+ +============================+ +==================================+\n *\n *\n * +============================+ +============================+ +=====================================+\n * + Keyboard is shown and we + => + User moved focus to + => + Only `onStart`/`onEnd` maybe called +\n * + call `onEnd` handler + + another `TextInput` + + (without involving `onMove`) +\n * +============================+ +============================+ +=====================================+\n *\n */\nconst KeyboardAwareScrollView = forwardRef<\n ScrollView,\n React.PropsWithChildren<KeyboardAwareScrollViewProps>\n>(\n (\n {\n children,\n bottomOffset = 0,\n disableScrollOnKeyboardHide = false,\n enabled = true,\n ...rest\n },\n ref,\n ) => {\n const scrollViewAnimatedRef = useAnimatedRef<Reanimated.ScrollView>();\n const scrollPosition = useSharedValue(0);\n const position = useSharedValue(0);\n const currentKeyboardFrameHeight = useSharedValue(0);\n const keyboardHeight = useSharedValue(0);\n const keyboardWillAppear = useSharedValue(false);\n const tag = useSharedValue(-1);\n const initialKeyboardSize = useSharedValue(0);\n const scrollBeforeKeyboardMovement = useSharedValue(0);\n const { input } = useReanimatedFocusedInput();\n const layout = useSharedValue<FocusedInputLayoutChangedEvent | null>(null);\n\n const { height } = useWindowDimensions();\n\n const onScroll = useAnimatedScrollHandler(\n {\n onScroll: (e) => {\n position.value = e.contentOffset.y;\n },\n },\n [],\n );\n\n const onRef = useCallback((assignedRef: Reanimated.ScrollView) => {\n if (typeof ref === \"function\") {\n ref(assignedRef);\n } else if (ref) {\n ref.current = assignedRef;\n }\n\n scrollViewAnimatedRef(assignedRef);\n }, []);\n\n /**\n * Function that will scroll a ScrollView as keyboard gets moving\n */\n const maybeScroll = useCallback(\n (e: number, animated: boolean = false) => {\n \"worklet\";\n\n if (!enabled) {\n return 0;\n }\n\n const visibleRect = height - keyboardHeight.value;\n const absoluteY = layout.value?.layout.absoluteY || 0;\n const inputHeight = layout.value?.layout.height || 0;\n const point = absoluteY + inputHeight;\n\n if (visibleRect - point <= bottomOffset) {\n const interpolatedScrollTo = interpolate(\n e,\n [initialKeyboardSize.value, keyboardHeight.value],\n [0, keyboardHeight.value - (height - point) + bottomOffset],\n );\n const targetScrollY =\n Math.max(interpolatedScrollTo, 0) + scrollPosition.value;\n scrollTo(scrollViewAnimatedRef, 0, targetScrollY, animated);\n\n return interpolatedScrollTo;\n }\n\n if (absoluteY < 0) {\n const positionOnScreen = visibleRect - inputHeight - bottomOffset;\n const topOfScreen = scrollPosition.value + absoluteY;\n\n scrollTo(\n scrollViewAnimatedRef,\n 0,\n topOfScreen - positionOnScreen,\n animated,\n );\n }\n\n return 0;\n },\n [bottomOffset, enabled],\n );\n\n const onChangeText = useCallback(() => {\n \"worklet\";\n\n // if typing a text caused layout shift, then we need to ignore this handler\n // because this event will be handled in `useAnimatedReaction` below\n if (layout.value?.layout.height !== input.value?.layout.height) {\n return;\n }\n\n const prevScrollPosition = scrollPosition.value;\n const prevLayout = layout.value;\n\n scrollPosition.value = position.value;\n layout.value = input.value;\n maybeScroll(keyboardHeight.value, true);\n scrollPosition.value = prevScrollPosition;\n layout.value = prevLayout;\n }, [maybeScroll]);\n\n const onChangeTextHandler = useMemo(\n () => debounce(onChangeText, 200),\n [onChangeText],\n );\n\n useFocusedInputHandler(\n {\n onChangeText: onChangeTextHandler,\n },\n [onChangeTextHandler],\n );\n\n useSmoothKeyboardHandler(\n enabled\n ? {\n onStart: (e) => {\n \"worklet\";\n\n const keyboardWillChangeSize =\n keyboardHeight.value !== e.height && e.height > 0;\n keyboardWillAppear.value =\n e.height > 0 && keyboardHeight.value === 0;\n const keyboardWillHide = e.height === 0;\n const focusWasChanged =\n (tag.value !== e.target && e.target !== -1) ||\n keyboardWillChangeSize;\n\n if (keyboardWillChangeSize) {\n initialKeyboardSize.value = keyboardHeight.value;\n }\n\n if (keyboardWillHide) {\n // on back transition need to interpolate as [0, keyboardHeight]\n initialKeyboardSize.value = 0;\n scrollPosition.value = scrollBeforeKeyboardMovement.value;\n }\n\n if (\n keyboardWillAppear.value ||\n keyboardWillChangeSize ||\n focusWasChanged\n ) {\n // persist scroll value\n scrollPosition.value = position.value;\n // just persist height - later will be used in interpolation\n keyboardHeight.value = e.height;\n }\n\n // focus was changed\n if (focusWasChanged) {\n tag.value = e.target;\n\n // save position of focused text input when keyboard starts to move\n layout.value = input.value;\n // save current scroll position - when keyboard will hide we'll reuse\n // this value to achieve smooth hide effect\n scrollBeforeKeyboardMovement.value = position.value;\n }\n\n if (focusWasChanged && !keyboardWillAppear.value) {\n // update position on scroll value, so `onEnd` handler\n // will pick up correct values\n position.value += maybeScroll(e.height, true);\n }\n },\n onMove: (e) => {\n \"worklet\";\n\n currentKeyboardFrameHeight.value = e.height;\n\n // if the user has set disableScrollOnKeyboardHide, only auto-scroll when the keyboard opens\n if (!disableScrollOnKeyboardHide || keyboardWillAppear.value) {\n maybeScroll(e.height);\n }\n },\n onEnd: (e) => {\n \"worklet\";\n\n keyboardHeight.value = e.height;\n scrollPosition.value = position.value;\n },\n }\n : {},\n [height, maybeScroll, disableScrollOnKeyboardHide, enabled],\n );\n\n useAnimatedReaction(\n () => input.value,\n (current, previous) => {\n if (\n current?.target === previous?.target &&\n current?.layout.height !== previous?.layout.height\n ) {\n const prevLayout = layout.value;\n\n layout.value = input.value;\n scrollPosition.value += maybeScroll(keyboardHeight.value, true);\n layout.value = prevLayout;\n }\n },\n [],\n );\n\n const view = useAnimatedStyle(\n () => ({\n // animations become laggy when scrolling to the end of the `ScrollView` (when the last input is focused)\n // this happens because the layout recalculates on every frame. To avoid this we slightly increase padding\n // by `+1`. In this way we assure, that `scrollTo` will never scroll to the end, because it uses interpolation\n // from 0 to `keyboardHeight`, and here our padding is `keyboardHeight + 1`. It allows us not to re-run layout\n // re-calculation on every animation frame and it helps to achieve smooth animation.\n // see: https://github.com/kirillzyusko/react-native-keyboard-controller/pull/342\n paddingBottom: currentKeyboardFrameHeight.value + 1,\n }),\n [],\n );\n\n return (\n <Reanimated.ScrollView\n ref={onRef}\n {...rest}\n // @ts-expect-error `onScrollReanimated` is a fake prop needed for reanimated to intercept scroll events\n onScrollReanimated={onScroll}\n scrollEventThrottle={16}\n >\n {children}\n <Reanimated.View style={view} />\n </Reanimated.ScrollView>\n );\n },\n);\n\nexport default KeyboardAwareScrollView;\n"],"mappings":"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"}
|
|
@@ -24,16 +24,19 @@ const TELEGRAM_ANDROID_TIMING_CONFIG = {
|
|
|
24
24
|
*/
|
|
25
25
|
const useSmoothKeyboardHandler = (handler, deps) => {
|
|
26
26
|
const target = (0, _reactNativeReanimated.useSharedValue)(-1);
|
|
27
|
+
const height = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
27
28
|
const persistedHeight = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
28
29
|
const animatedKeyboardHeight = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
29
30
|
(0, _reactNativeReanimated.useAnimatedReaction)(() => {
|
|
30
31
|
if (IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
34
|
+
if (persistedHeight.value === 0) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
33
37
|
const event = {
|
|
34
|
-
// it'll be always
|
|
35
|
-
|
|
36
|
-
duration: 250,
|
|
38
|
+
// it'll be always `TELEGRAM_ANDROID_TIMING_CONFIG.duration`, since we're running animation via `withTiming`
|
|
39
|
+
duration: TELEGRAM_ANDROID_TIMING_CONFIG.duration,
|
|
37
40
|
target: target.value,
|
|
38
41
|
height: animatedKeyboardHeight.value,
|
|
39
42
|
progress: animatedKeyboardHeight.value / persistedHeight.value
|
|
@@ -47,9 +50,10 @@ const useSmoothKeyboardHandler = (handler, deps) => {
|
|
|
47
50
|
(_handler$onMove = handler.onMove) === null || _handler$onMove === void 0 ? void 0 : _handler$onMove.call(handler, evt);
|
|
48
51
|
|
|
49
52
|
// dispatch `onEnd`
|
|
50
|
-
if (evt.height ===
|
|
53
|
+
if (evt.height === height.value) {
|
|
51
54
|
var _handler$onEnd;
|
|
52
55
|
(_handler$onEnd = handler.onEnd) === null || _handler$onEnd === void 0 ? void 0 : _handler$onEnd.call(handler, evt);
|
|
56
|
+
persistedHeight.value = height.value;
|
|
53
57
|
}
|
|
54
58
|
}, [handler]);
|
|
55
59
|
(0, _reactNativeKeyboardController.useKeyboardHandler)({
|
|
@@ -66,6 +70,7 @@ const useSmoothKeyboardHandler = (handler, deps) => {
|
|
|
66
70
|
return;
|
|
67
71
|
}
|
|
68
72
|
target.value = e.target;
|
|
73
|
+
height.value = e.height;
|
|
69
74
|
if (e.height > 0) {
|
|
70
75
|
persistedHeight.value = e.height;
|
|
71
76
|
}
|
|
@@ -75,7 +80,10 @@ const useSmoothKeyboardHandler = (handler, deps) => {
|
|
|
75
80
|
if (!IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {
|
|
76
81
|
animatedKeyboardHeight.value = (0, _reactNativeReanimated.withTiming)(e.height, TELEGRAM_ANDROID_TIMING_CONFIG);
|
|
77
82
|
}
|
|
78
|
-
(_handler$onStart2 = handler.onStart) === null || _handler$onStart2 === void 0 ? void 0 : _handler$onStart2.call(handler,
|
|
83
|
+
(_handler$onStart2 = handler.onStart) === null || _handler$onStart2 === void 0 ? void 0 : _handler$onStart2.call(handler, {
|
|
84
|
+
...e,
|
|
85
|
+
duration: IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS ? e.duration : TELEGRAM_ANDROID_TIMING_CONFIG.duration
|
|
86
|
+
});
|
|
79
87
|
},
|
|
80
88
|
onMove: e => {
|
|
81
89
|
"worklet";
|
|
@@ -92,7 +100,6 @@ const useSmoothKeyboardHandler = (handler, deps) => {
|
|
|
92
100
|
var _handler$onEnd3;
|
|
93
101
|
(_handler$onEnd3 = handler.onEnd) === null || _handler$onEnd3 === void 0 ? void 0 : _handler$onEnd3.call(handler, e);
|
|
94
102
|
}
|
|
95
|
-
persistedHeight.value = e.height;
|
|
96
103
|
}
|
|
97
104
|
}, deps);
|
|
98
105
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","_reactNativeReanimated","_reactNativeKeyboardController","IS_ANDROID_ELEVEN_OR_HIGHER","Platform","OS","Version","IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS","TELEGRAM_ANDROID_TIMING_CONFIG","duration","easing","Easing","bezier","useSmoothKeyboardHandler","handler","deps","target","useSharedValue","persistedHeight","animatedKeyboardHeight","useAnimatedReaction","
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_reactNativeReanimated","_reactNativeKeyboardController","IS_ANDROID_ELEVEN_OR_HIGHER","Platform","OS","Version","IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS","TELEGRAM_ANDROID_TIMING_CONFIG","duration","easing","Easing","bezier","useSmoothKeyboardHandler","handler","deps","target","useSharedValue","height","persistedHeight","animatedKeyboardHeight","useAnimatedReaction","value","event","progress","evt","_handler$onMove","onMove","call","_handler$onEnd","onEnd","useKeyboardHandler","onStart","e","_handler$onStart2","_handler$onStart","_handler$onEnd2","withTiming","_handler$onMove2","_handler$onEnd3","exports"],"sources":["useSmoothKeyboardHandler.ts"],"sourcesContent":["import { Platform } from \"react-native\";\nimport {\n Easing,\n useAnimatedReaction,\n useSharedValue,\n withTiming,\n} from \"react-native-reanimated\";\n\nimport { useKeyboardHandler } from \"react-native-keyboard-controller\";\n\nconst IS_ANDROID_ELEVEN_OR_HIGHER =\n Platform.OS === \"android\" && Platform.Version >= 30;\n// on these platforms keyboard transitions will be smooth\nconst IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS =\n IS_ANDROID_ELEVEN_OR_HIGHER || Platform.OS === \"ios\";\n// on Android Telegram is not using androidx.core values and uses custom interpolation\n// duration is taken from here: https://github.com/DrKLO/Telegram/blob/e9a35cea54c06277c69d41b8e25d94b5d7ede065/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AdjustPanLayoutHelper.java#L39\n// and bezier is taken from: https://github.com/DrKLO/Telegram/blob/e9a35cea54c06277c69d41b8e25d94b5d7ede065/TMessagesProj/src/main/java/androidx/recyclerview/widget/ChatListItemAnimator.java#L40\nconst TELEGRAM_ANDROID_TIMING_CONFIG = {\n duration: 250,\n easing: Easing.bezier(\n 0.19919472913616398,\n 0.010644531250000006,\n 0.27920937042459737,\n 0.91025390625,\n ),\n};\n\n/**\n * Hook that uses default transitions for iOS and Android > 11, and uses\n * custom interpolation on Android < 11 to achieve more smooth animation\n */\nexport const useSmoothKeyboardHandler: typeof useKeyboardHandler = (\n handler,\n deps,\n) => {\n const target = useSharedValue(-1);\n const height = useSharedValue(0);\n const persistedHeight = useSharedValue(0);\n const animatedKeyboardHeight = useSharedValue(0);\n\n useAnimatedReaction(\n () => {\n if (IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {\n return;\n }\n if (persistedHeight.value === 0) {\n return;\n }\n const event = {\n // it'll be always `TELEGRAM_ANDROID_TIMING_CONFIG.duration`, since we're running animation via `withTiming`\n duration: TELEGRAM_ANDROID_TIMING_CONFIG.duration,\n target: target.value,\n height: animatedKeyboardHeight.value,\n progress: animatedKeyboardHeight.value / persistedHeight.value,\n };\n return event;\n },\n (evt) => {\n if (!evt) {\n return;\n }\n handler.onMove?.(evt);\n\n // dispatch `onEnd`\n if (evt.height === height.value) {\n handler.onEnd?.(evt);\n persistedHeight.value = height.value;\n }\n },\n [handler],\n );\n\n useKeyboardHandler(\n {\n onStart: (e) => {\n \"worklet\";\n\n // immediately dispatch onStart/onEnd events if onStart dispatched with the same height\n // and don't wait for animation 250ms\n if (\n !IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS &&\n e.height === persistedHeight.value\n ) {\n handler.onStart?.(e);\n handler.onEnd?.(e);\n\n return;\n }\n\n target.value = e.target;\n height.value = e.height;\n\n if (e.height > 0) {\n persistedHeight.value = e.height;\n }\n // if we are running on Android < 9, then we are using custom interpolation\n // to achieve smoother animation and use `animatedKeyboardHeight` as animation\n // driver\n if (!IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {\n animatedKeyboardHeight.value = withTiming(\n e.height,\n TELEGRAM_ANDROID_TIMING_CONFIG,\n );\n }\n\n handler.onStart?.({\n ...e,\n duration: IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS\n ? e.duration\n : TELEGRAM_ANDROID_TIMING_CONFIG.duration,\n });\n },\n onMove: (e) => {\n \"worklet\";\n\n if (IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {\n handler.onMove?.(e);\n }\n },\n onEnd: (e) => {\n \"worklet\";\n\n if (IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {\n handler.onEnd?.(e);\n }\n },\n },\n deps,\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
2
|
-
import React, { useCallback, useMemo } from "react";
|
|
2
|
+
import React, { forwardRef, useCallback, useMemo } from "react";
|
|
3
3
|
import { useWindowDimensions } from "react-native";
|
|
4
4
|
import Reanimated, { interpolate, scrollTo, useAnimatedReaction, useAnimatedRef, useAnimatedScrollHandler, useAnimatedStyle, useSharedValue } from "react-native-reanimated";
|
|
5
5
|
import { useFocusedInputHandler, useReanimatedFocusedInput } from "react-native-keyboard-controller";
|
|
@@ -43,11 +43,12 @@ import { debounce } from "./utils";
|
|
|
43
43
|
* +============================+ +============================+ +=====================================+
|
|
44
44
|
*
|
|
45
45
|
*/
|
|
46
|
-
const KeyboardAwareScrollView = _ref => {
|
|
46
|
+
const KeyboardAwareScrollView = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
47
47
|
let {
|
|
48
48
|
children,
|
|
49
49
|
bottomOffset = 0,
|
|
50
50
|
disableScrollOnKeyboardHide = false,
|
|
51
|
+
enabled = true,
|
|
51
52
|
...rest
|
|
52
53
|
} = _ref;
|
|
53
54
|
const scrollViewAnimatedRef = useAnimatedRef();
|
|
@@ -71,6 +72,14 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
71
72
|
position.value = e.contentOffset.y;
|
|
72
73
|
}
|
|
73
74
|
}, []);
|
|
75
|
+
const onRef = useCallback(assignedRef => {
|
|
76
|
+
if (typeof ref === "function") {
|
|
77
|
+
ref(assignedRef);
|
|
78
|
+
} else if (ref) {
|
|
79
|
+
ref.current = assignedRef;
|
|
80
|
+
}
|
|
81
|
+
scrollViewAnimatedRef(assignedRef);
|
|
82
|
+
}, []);
|
|
74
83
|
|
|
75
84
|
/**
|
|
76
85
|
* Function that will scroll a ScrollView as keyboard gets moving
|
|
@@ -80,6 +89,9 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
80
89
|
|
|
81
90
|
var _layout$value, _layout$value2;
|
|
82
91
|
let animated = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
92
|
+
if (!enabled) {
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
83
95
|
const visibleRect = height - keyboardHeight.value;
|
|
84
96
|
const absoluteY = ((_layout$value = layout.value) === null || _layout$value === void 0 ? void 0 : _layout$value.layout.absoluteY) || 0;
|
|
85
97
|
const inputHeight = ((_layout$value2 = layout.value) === null || _layout$value2 === void 0 ? void 0 : _layout$value2.layout.height) || 0;
|
|
@@ -96,7 +108,7 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
96
108
|
scrollTo(scrollViewAnimatedRef, 0, topOfScreen - positionOnScreen, animated);
|
|
97
109
|
}
|
|
98
110
|
return 0;
|
|
99
|
-
}, [bottomOffset]);
|
|
111
|
+
}, [bottomOffset, enabled]);
|
|
100
112
|
const onChangeText = useCallback(() => {
|
|
101
113
|
"worklet";
|
|
102
114
|
|
|
@@ -118,7 +130,7 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
118
130
|
useFocusedInputHandler({
|
|
119
131
|
onChangeText: onChangeTextHandler
|
|
120
132
|
}, [onChangeTextHandler]);
|
|
121
|
-
useSmoothKeyboardHandler({
|
|
133
|
+
useSmoothKeyboardHandler(enabled ? {
|
|
122
134
|
onStart: e => {
|
|
123
135
|
"worklet";
|
|
124
136
|
|
|
@@ -173,7 +185,7 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
173
185
|
keyboardHeight.value = e.height;
|
|
174
186
|
scrollPosition.value = position.value;
|
|
175
187
|
}
|
|
176
|
-
}, [height, maybeScroll, disableScrollOnKeyboardHide]);
|
|
188
|
+
} : {}, [height, maybeScroll, disableScrollOnKeyboardHide, enabled]);
|
|
177
189
|
useAnimatedReaction(() => input.value, (current, previous) => {
|
|
178
190
|
if ((current === null || current === void 0 ? void 0 : current.target) === (previous === null || previous === void 0 ? void 0 : previous.target) && (current === null || current === void 0 ? void 0 : current.layout.height) !== (previous === null || previous === void 0 ? void 0 : previous.layout.height)) {
|
|
179
191
|
const prevLayout = layout.value;
|
|
@@ -183,10 +195,16 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
183
195
|
}
|
|
184
196
|
}, []);
|
|
185
197
|
const view = useAnimatedStyle(() => ({
|
|
186
|
-
|
|
198
|
+
// animations become laggy when scrolling to the end of the `ScrollView` (when the last input is focused)
|
|
199
|
+
// this happens because the layout recalculates on every frame. To avoid this we slightly increase padding
|
|
200
|
+
// by `+1`. In this way we assure, that `scrollTo` will never scroll to the end, because it uses interpolation
|
|
201
|
+
// from 0 to `keyboardHeight`, and here our padding is `keyboardHeight + 1`. It allows us not to re-run layout
|
|
202
|
+
// re-calculation on every animation frame and it helps to achieve smooth animation.
|
|
203
|
+
// see: https://github.com/kirillzyusko/react-native-keyboard-controller/pull/342
|
|
204
|
+
paddingBottom: currentKeyboardFrameHeight.value + 1
|
|
187
205
|
}), []);
|
|
188
206
|
return /*#__PURE__*/React.createElement(Reanimated.ScrollView, _extends({
|
|
189
|
-
ref:
|
|
207
|
+
ref: onRef
|
|
190
208
|
}, rest, {
|
|
191
209
|
// @ts-expect-error `onScrollReanimated` is a fake prop needed for reanimated to intercept scroll events
|
|
192
210
|
onScrollReanimated: onScroll,
|
|
@@ -194,6 +212,6 @@ const KeyboardAwareScrollView = _ref => {
|
|
|
194
212
|
}), children, /*#__PURE__*/React.createElement(Reanimated.View, {
|
|
195
213
|
style: view
|
|
196
214
|
}));
|
|
197
|
-
};
|
|
215
|
+
});
|
|
198
216
|
export default KeyboardAwareScrollView;
|
|
199
217
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","useCallback","useMemo","useWindowDimensions","Reanimated","interpolate","scrollTo","useAnimatedReaction","useAnimatedRef","useAnimatedScrollHandler","useAnimatedStyle","useSharedValue","useFocusedInputHandler","useReanimatedFocusedInput","useSmoothKeyboardHandler","debounce","KeyboardAwareScrollView","_ref","children","bottomOffset","disableScrollOnKeyboardHide","rest","scrollViewAnimatedRef","scrollPosition","position","currentKeyboardFrameHeight","keyboardHeight","keyboardWillAppear","tag","initialKeyboardSize","scrollBeforeKeyboardMovement","input","layout","height","onScroll","e","value","contentOffset","y","maybeScroll","_layout$value","_layout$value2","animated","arguments","length","undefined","visibleRect","absoluteY","inputHeight","point","interpolatedScrollTo","targetScrollY","Math","max","positionOnScreen","topOfScreen","onChangeText","_layout$value3","_input$value","prevScrollPosition","prevLayout","onChangeTextHandler","onStart","keyboardWillChangeSize","keyboardWillHide","focusWasChanged","target","onMove","onEnd","current","previous","view","paddingBottom","createElement","ScrollView","_extends","ref","onScrollReanimated","scrollEventThrottle","View","style"],"sources":["index.tsx"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport { useWindowDimensions } from \"react-native\";\nimport Reanimated, {\n interpolate,\n scrollTo,\n useAnimatedReaction,\n useAnimatedRef,\n useAnimatedScrollHandler,\n useAnimatedStyle,\n useSharedValue,\n} from \"react-native-reanimated\";\n\nimport {\n useFocusedInputHandler,\n useReanimatedFocusedInput,\n} from \"react-native-keyboard-controller\";\n\nimport { useSmoothKeyboardHandler } from \"./useSmoothKeyboardHandler\";\nimport { debounce } from \"./utils\";\n\nimport type { FC } from \"react\";\nimport type { ScrollViewProps } from \"react-native\";\nimport type { FocusedInputLayoutChangedEvent } from \"react-native-keyboard-controller\";\n\ntype KeyboardAwareScrollViewProps = {\n /** The distance between keyboard and focused `TextInput` when keyboard is shown. Default is `0`. */\n bottomOffset?: number;\n /** Prevents automatic scrolling of the `ScrollView` when the keyboard gets hidden, maintaining the current screen position. Default is `false`. */\n disableScrollOnKeyboardHide?: boolean;\n} & ScrollViewProps;\n\n/*\n * Everything begins from `onStart` handler. This handler is called every time,\n * when keyboard changes its size or when focused `TextInput` was changed. In\n * this handler we are calculating/memoizing values which later will be used\n * during layout movement. For that we calculate:\n * - layout of focused field (`layout`) - to understand whether there will be overlap\n * - initial keyboard size (`initialKeyboardSize`) - used in scroll interpolation\n * - future keyboard height (`keyboardHeight`) - used in scroll interpolation\n * - current scroll position (`scrollPosition`) - used to scroll from this point\n *\n * Once we've calculated all necessary variables - we can actually start to use them.\n * It happens in `onMove` handler - this function simply calls `maybeScroll` with\n * current keyboard frame height. This functions makes the smooth transition.\n *\n * When the transition has finished we go to `onEnd` handler. In this handler\n * we verify, that the current field is not overlapped within a keyboard frame.\n * For full `onStart`/`onMove`/`onEnd` flow it may look like a redundant thing,\n * however there could be some cases, when `onMove` is not called:\n * - on iOS when TextInput was changed - keyboard transition is instant\n * - on Android when TextInput was changed and keyboard size wasn't changed\n * So `onEnd` handler handle the case, when `onMove` wasn't triggered.\n *\n * ====================================================================================================================+\n * -----------------------------------------------------Flow chart-----------------------------------------------------+\n * ====================================================================================================================+\n *\n * +============================+ +============================+ +==================================+\n * + User Press on TextInput + => + Keyboard starts showing + => + As keyboard moves frame by frame + =>\n * + + + (run `onStart`) + + `onMove` is getting called +\n * +============================+ +============================+ +==================================+\n *\n *\n * +============================+ +============================+ +=====================================+\n * + Keyboard is shown and we + => + User moved focus to + => + Only `onStart`/`onEnd` maybe called +\n * + call `onEnd` handler + + another `TextInput` + + (without involving `onMove`) +\n * +============================+ +============================+ +=====================================+\n *\n */\nconst KeyboardAwareScrollView: FC<KeyboardAwareScrollViewProps> = ({\n children,\n bottomOffset = 0,\n disableScrollOnKeyboardHide = false,\n ...rest\n}) => {\n const scrollViewAnimatedRef = useAnimatedRef<Reanimated.ScrollView>();\n const scrollPosition = useSharedValue(0);\n const position = useSharedValue(0);\n const currentKeyboardFrameHeight = useSharedValue(0);\n const keyboardHeight = useSharedValue(0);\n const keyboardWillAppear = useSharedValue(false);\n const tag = useSharedValue(-1);\n const initialKeyboardSize = useSharedValue(0);\n const scrollBeforeKeyboardMovement = useSharedValue(0);\n const { input } = useReanimatedFocusedInput();\n const layout = useSharedValue<FocusedInputLayoutChangedEvent | null>(null);\n\n const { height } = useWindowDimensions();\n\n const onScroll = useAnimatedScrollHandler(\n {\n onScroll: (e) => {\n position.value = e.contentOffset.y;\n },\n },\n [],\n );\n\n /**\n * Function that will scroll a ScrollView as keyboard gets moving\n */\n const maybeScroll = useCallback(\n (e: number, animated: boolean = false) => {\n \"worklet\";\n\n const visibleRect = height - keyboardHeight.value;\n const absoluteY = layout.value?.layout.absoluteY || 0;\n const inputHeight = layout.value?.layout.height || 0;\n const point = absoluteY + inputHeight;\n\n if (visibleRect - point <= bottomOffset) {\n const interpolatedScrollTo = interpolate(\n e,\n [initialKeyboardSize.value, keyboardHeight.value],\n [0, keyboardHeight.value - (height - point) + bottomOffset],\n );\n const targetScrollY =\n Math.max(interpolatedScrollTo, 0) + scrollPosition.value;\n scrollTo(scrollViewAnimatedRef, 0, targetScrollY, animated);\n\n return interpolatedScrollTo;\n }\n\n if (absoluteY < 0) {\n const positionOnScreen = visibleRect - inputHeight - bottomOffset;\n const topOfScreen = scrollPosition.value + absoluteY;\n\n scrollTo(\n scrollViewAnimatedRef,\n 0,\n topOfScreen - positionOnScreen,\n animated,\n );\n }\n\n return 0;\n },\n [bottomOffset],\n );\n\n const onChangeText = useCallback(() => {\n \"worklet\";\n\n // if typing a text caused layout shift, then we need to ignore this handler\n // because this event will be handled in `useAnimatedReaction` below\n if (layout.value?.layout.height !== input.value?.layout.height) {\n return;\n }\n\n const prevScrollPosition = scrollPosition.value;\n const prevLayout = layout.value;\n\n scrollPosition.value = position.value;\n layout.value = input.value;\n maybeScroll(keyboardHeight.value, true);\n scrollPosition.value = prevScrollPosition;\n layout.value = prevLayout;\n }, [maybeScroll]);\n const onChangeTextHandler = useMemo(\n () => debounce(onChangeText, 200),\n [onChangeText],\n );\n\n useFocusedInputHandler(\n {\n onChangeText: onChangeTextHandler,\n },\n [onChangeTextHandler],\n );\n\n useSmoothKeyboardHandler(\n {\n onStart: (e) => {\n \"worklet\";\n\n const keyboardWillChangeSize =\n keyboardHeight.value !== e.height && e.height > 0;\n keyboardWillAppear.value = e.height > 0 && keyboardHeight.value === 0;\n const keyboardWillHide = e.height === 0;\n const focusWasChanged =\n (tag.value !== e.target && e.target !== -1) || keyboardWillChangeSize;\n\n if (keyboardWillChangeSize) {\n initialKeyboardSize.value = keyboardHeight.value;\n }\n\n if (keyboardWillHide) {\n // on back transition need to interpolate as [0, keyboardHeight]\n initialKeyboardSize.value = 0;\n scrollPosition.value = scrollBeforeKeyboardMovement.value;\n }\n\n if (\n keyboardWillAppear.value ||\n keyboardWillChangeSize ||\n focusWasChanged\n ) {\n // persist scroll value\n scrollPosition.value = position.value;\n // just persist height - later will be used in interpolation\n keyboardHeight.value = e.height;\n }\n\n // focus was changed\n if (focusWasChanged) {\n tag.value = e.target;\n\n // save position of focused text input when keyboard starts to move\n layout.value = input.value;\n // save current scroll position - when keyboard will hide we'll reuse\n // this value to achieve smooth hide effect\n scrollBeforeKeyboardMovement.value = position.value;\n }\n\n if (focusWasChanged && !keyboardWillAppear.value) {\n // update position on scroll value, so `onEnd` handler\n // will pick up correct values\n position.value += maybeScroll(e.height, true);\n }\n },\n onMove: (e) => {\n \"worklet\";\n\n currentKeyboardFrameHeight.value = e.height;\n\n // if the user has set disableScrollOnKeyboardHide, only auto-scroll when the keyboard opens\n if (!disableScrollOnKeyboardHide || keyboardWillAppear.value) {\n maybeScroll(e.height);\n }\n },\n onEnd: (e) => {\n \"worklet\";\n\n keyboardHeight.value = e.height;\n scrollPosition.value = position.value;\n },\n },\n [height, maybeScroll, disableScrollOnKeyboardHide],\n );\n\n useAnimatedReaction(\n () => input.value,\n (current, previous) => {\n if (\n current?.target === previous?.target &&\n current?.layout.height !== previous?.layout.height\n ) {\n const prevLayout = layout.value;\n\n layout.value = input.value;\n scrollPosition.value += maybeScroll(keyboardHeight.value, true);\n layout.value = prevLayout;\n }\n },\n [],\n );\n\n const view = useAnimatedStyle(\n () => ({\n paddingBottom: currentKeyboardFrameHeight.value,\n }),\n [],\n );\n\n return (\n <Reanimated.ScrollView\n ref={scrollViewAnimatedRef}\n {...rest}\n // @ts-expect-error `onScrollReanimated` is a fake prop needed for reanimated to intercept scroll events\n onScrollReanimated={onScroll}\n scrollEventThrottle={16}\n >\n {children}\n <Reanimated.View style={view} />\n </Reanimated.ScrollView>\n );\n};\n\nexport default KeyboardAwareScrollView;\n"],"mappings":";AAAA,OAAOA,KAAK,IAAIC,WAAW,EAAEC,OAAO,QAAQ,OAAO;AACnD,SAASC,mBAAmB,QAAQ,cAAc;AAClD,OAAOC,UAAU,IACfC,WAAW,EACXC,QAAQ,EACRC,mBAAmB,EACnBC,cAAc,EACdC,wBAAwB,EACxBC,gBAAgB,EAChBC,cAAc,QACT,yBAAyB;AAEhC,SACEC,sBAAsB,EACtBC,yBAAyB,QACpB,kCAAkC;AAEzC,SAASC,wBAAwB,QAAQ,4BAA4B;AACrE,SAASC,QAAQ,QAAQ,SAAS;AAalC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,uBAAyD,GAAGC,IAAA,IAK5D;EAAA,IAL6D;IACjEC,QAAQ;IACRC,YAAY,GAAG,CAAC;IAChBC,2BAA2B,GAAG,KAAK;IACnC,GAAGC;EACL,CAAC,GAAAJ,IAAA;EACC,MAAMK,qBAAqB,GAAGd,cAAc,CAAwB,CAAC;EACrE,MAAMe,cAAc,GAAGZ,cAAc,CAAC,CAAC,CAAC;EACxC,MAAMa,QAAQ,GAAGb,cAAc,CAAC,CAAC,CAAC;EAClC,MAAMc,0BAA0B,GAAGd,cAAc,CAAC,CAAC,CAAC;EACpD,MAAMe,cAAc,GAAGf,cAAc,CAAC,CAAC,CAAC;EACxC,MAAMgB,kBAAkB,GAAGhB,cAAc,CAAC,KAAK,CAAC;EAChD,MAAMiB,GAAG,GAAGjB,cAAc,CAAC,CAAC,CAAC,CAAC;EAC9B,MAAMkB,mBAAmB,GAAGlB,cAAc,CAAC,CAAC,CAAC;EAC7C,MAAMmB,4BAA4B,GAAGnB,cAAc,CAAC,CAAC,CAAC;EACtD,MAAM;IAAEoB;EAAM,CAAC,GAAGlB,yBAAyB,CAAC,CAAC;EAC7C,MAAMmB,MAAM,GAAGrB,cAAc,CAAwC,IAAI,CAAC;EAE1E,MAAM;IAAEsB;EAAO,CAAC,GAAG9B,mBAAmB,CAAC,CAAC;EAExC,MAAM+B,QAAQ,GAAGzB,wBAAwB,CACvC;IACEyB,QAAQ,EAAGC,CAAC,IAAK;MACfX,QAAQ,CAACY,KAAK,GAAGD,CAAC,CAACE,aAAa,CAACC,CAAC;IACpC;EACF,CAAC,EACD,EACF,CAAC;;EAED;AACF;AACA;EACE,MAAMC,WAAW,GAAGtC,WAAW,CAC7B,UAACkC,CAAS,EAAgC;IACxC,SAAS;;IAAC,IAAAK,aAAA,EAAAC,cAAA;IAAA,IADAC,QAAiB,GAAAC,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAG,KAAK;IAGnC,MAAMG,WAAW,GAAGb,MAAM,GAAGP,cAAc,CAACU,KAAK;IACjD,MAAMW,SAAS,GAAG,EAAAP,aAAA,GAAAR,MAAM,CAACI,KAAK,cAAAI,aAAA,uBAAZA,aAAA,CAAcR,MAAM,CAACe,SAAS,KAAI,CAAC;IACrD,MAAMC,WAAW,GAAG,EAAAP,cAAA,GAAAT,MAAM,CAACI,KAAK,cAAAK,cAAA,uBAAZA,cAAA,CAAcT,MAAM,CAACC,MAAM,KAAI,CAAC;IACpD,MAAMgB,KAAK,GAAGF,SAAS,GAAGC,WAAW;IAErC,IAAIF,WAAW,GAAGG,KAAK,IAAI9B,YAAY,EAAE;MACvC,MAAM+B,oBAAoB,GAAG7C,WAAW,CACtC8B,CAAC,EACD,CAACN,mBAAmB,CAACO,KAAK,EAAEV,cAAc,CAACU,KAAK,CAAC,EACjD,CAAC,CAAC,EAAEV,cAAc,CAACU,KAAK,IAAIH,MAAM,GAAGgB,KAAK,CAAC,GAAG9B,YAAY,CAC5D,CAAC;MACD,MAAMgC,aAAa,GACjBC,IAAI,CAACC,GAAG,CAACH,oBAAoB,EAAE,CAAC,CAAC,GAAG3B,cAAc,CAACa,KAAK;MAC1D9B,QAAQ,CAACgB,qBAAqB,EAAE,CAAC,EAAE6B,aAAa,EAAET,QAAQ,CAAC;MAE3D,OAAOQ,oBAAoB;IAC7B;IAEA,IAAIH,SAAS,GAAG,CAAC,EAAE;MACjB,MAAMO,gBAAgB,GAAGR,WAAW,GAAGE,WAAW,GAAG7B,YAAY;MACjE,MAAMoC,WAAW,GAAGhC,cAAc,CAACa,KAAK,GAAGW,SAAS;MAEpDzC,QAAQ,CACNgB,qBAAqB,EACrB,CAAC,EACDiC,WAAW,GAAGD,gBAAgB,EAC9BZ,QACF,CAAC;IACH;IAEA,OAAO,CAAC;EACV,CAAC,EACD,CAACvB,YAAY,CACf,CAAC;EAED,MAAMqC,YAAY,GAAGvD,WAAW,CAAC,MAAM;IACrC,SAAS;;IAET;IACA;IAAA,IAAAwD,cAAA,EAAAC,YAAA;IACA,IAAI,EAAAD,cAAA,GAAAzB,MAAM,CAACI,KAAK,cAAAqB,cAAA,uBAAZA,cAAA,CAAczB,MAAM,CAACC,MAAM,QAAAyB,YAAA,GAAK3B,KAAK,CAACK,KAAK,cAAAsB,YAAA,uBAAXA,YAAA,CAAa1B,MAAM,CAACC,MAAM,GAAE;MAC9D;IACF;IAEA,MAAM0B,kBAAkB,GAAGpC,cAAc,CAACa,KAAK;IAC/C,MAAMwB,UAAU,GAAG5B,MAAM,CAACI,KAAK;IAE/Bb,cAAc,CAACa,KAAK,GAAGZ,QAAQ,CAACY,KAAK;IACrCJ,MAAM,CAACI,KAAK,GAAGL,KAAK,CAACK,KAAK;IAC1BG,WAAW,CAACb,cAAc,CAACU,KAAK,EAAE,IAAI,CAAC;IACvCb,cAAc,CAACa,KAAK,GAAGuB,kBAAkB;IACzC3B,MAAM,CAACI,KAAK,GAAGwB,UAAU;EAC3B,CAAC,EAAE,CAACrB,WAAW,CAAC,CAAC;EACjB,MAAMsB,mBAAmB,GAAG3D,OAAO,CACjC,MAAMa,QAAQ,CAACyC,YAAY,EAAE,GAAG,CAAC,EACjC,CAACA,YAAY,CACf,CAAC;EAED5C,sBAAsB,CACpB;IACE4C,YAAY,EAAEK;EAChB,CAAC,EACD,CAACA,mBAAmB,CACtB,CAAC;EAED/C,wBAAwB,CACtB;IACEgD,OAAO,EAAG3B,CAAC,IAAK;MACd,SAAS;;MAET,MAAM4B,sBAAsB,GAC1BrC,cAAc,CAACU,KAAK,KAAKD,CAAC,CAACF,MAAM,IAAIE,CAAC,CAACF,MAAM,GAAG,CAAC;MACnDN,kBAAkB,CAACS,KAAK,GAAGD,CAAC,CAACF,MAAM,GAAG,CAAC,IAAIP,cAAc,CAACU,KAAK,KAAK,CAAC;MACrE,MAAM4B,gBAAgB,GAAG7B,CAAC,CAACF,MAAM,KAAK,CAAC;MACvC,MAAMgC,eAAe,GAClBrC,GAAG,CAACQ,KAAK,KAAKD,CAAC,CAAC+B,MAAM,IAAI/B,CAAC,CAAC+B,MAAM,KAAK,CAAC,CAAC,IAAKH,sBAAsB;MAEvE,IAAIA,sBAAsB,EAAE;QAC1BlC,mBAAmB,CAACO,KAAK,GAAGV,cAAc,CAACU,KAAK;MAClD;MAEA,IAAI4B,gBAAgB,EAAE;QACpB;QACAnC,mBAAmB,CAACO,KAAK,GAAG,CAAC;QAC7Bb,cAAc,CAACa,KAAK,GAAGN,4BAA4B,CAACM,KAAK;MAC3D;MAEA,IACET,kBAAkB,CAACS,KAAK,IACxB2B,sBAAsB,IACtBE,eAAe,EACf;QACA;QACA1C,cAAc,CAACa,KAAK,GAAGZ,QAAQ,CAACY,KAAK;QACrC;QACAV,cAAc,CAACU,KAAK,GAAGD,CAAC,CAACF,MAAM;MACjC;;MAEA;MACA,IAAIgC,eAAe,EAAE;QACnBrC,GAAG,CAACQ,KAAK,GAAGD,CAAC,CAAC+B,MAAM;;QAEpB;QACAlC,MAAM,CAACI,KAAK,GAAGL,KAAK,CAACK,KAAK;QAC1B;QACA;QACAN,4BAA4B,CAACM,KAAK,GAAGZ,QAAQ,CAACY,KAAK;MACrD;MAEA,IAAI6B,eAAe,IAAI,CAACtC,kBAAkB,CAACS,KAAK,EAAE;QAChD;QACA;QACAZ,QAAQ,CAACY,KAAK,IAAIG,WAAW,CAACJ,CAAC,CAACF,MAAM,EAAE,IAAI,CAAC;MAC/C;IACF,CAAC;IACDkC,MAAM,EAAGhC,CAAC,IAAK;MACb,SAAS;;MAETV,0BAA0B,CAACW,KAAK,GAAGD,CAAC,CAACF,MAAM;;MAE3C;MACA,IAAI,CAACb,2BAA2B,IAAIO,kBAAkB,CAACS,KAAK,EAAE;QAC5DG,WAAW,CAACJ,CAAC,CAACF,MAAM,CAAC;MACvB;IACF,CAAC;IACDmC,KAAK,EAAGjC,CAAC,IAAK;MACZ,SAAS;;MAETT,cAAc,CAACU,KAAK,GAAGD,CAAC,CAACF,MAAM;MAC/BV,cAAc,CAACa,KAAK,GAAGZ,QAAQ,CAACY,KAAK;IACvC;EACF,CAAC,EACD,CAACH,MAAM,EAAEM,WAAW,EAAEnB,2BAA2B,CACnD,CAAC;EAEDb,mBAAmB,CACjB,MAAMwB,KAAK,CAACK,KAAK,EACjB,CAACiC,OAAO,EAAEC,QAAQ,KAAK;IACrB,IACE,CAAAD,OAAO,aAAPA,OAAO,uBAAPA,OAAO,CAAEH,MAAM,OAAKI,QAAQ,aAARA,QAAQ,uBAARA,QAAQ,CAAEJ,MAAM,KACpC,CAAAG,OAAO,aAAPA,OAAO,uBAAPA,OAAO,CAAErC,MAAM,CAACC,MAAM,OAAKqC,QAAQ,aAARA,QAAQ,uBAARA,QAAQ,CAAEtC,MAAM,CAACC,MAAM,GAClD;MACA,MAAM2B,UAAU,GAAG5B,MAAM,CAACI,KAAK;MAE/BJ,MAAM,CAACI,KAAK,GAAGL,KAAK,CAACK,KAAK;MAC1Bb,cAAc,CAACa,KAAK,IAAIG,WAAW,CAACb,cAAc,CAACU,KAAK,EAAE,IAAI,CAAC;MAC/DJ,MAAM,CAACI,KAAK,GAAGwB,UAAU;IAC3B;EACF,CAAC,EACD,EACF,CAAC;EAED,MAAMW,IAAI,GAAG7D,gBAAgB,CAC3B,OAAO;IACL8D,aAAa,EAAE/C,0BAA0B,CAACW;EAC5C,CAAC,CAAC,EACF,EACF,CAAC;EAED,oBACEpC,KAAA,CAAAyE,aAAA,CAACrE,UAAU,CAACsE,UAAU,EAAAC,QAAA;IACpBC,GAAG,EAAEtD;EAAsB,GACvBD,IAAI;IACR;IACAwD,kBAAkB,EAAE3C,QAAS;IAC7B4C,mBAAmB,EAAE;EAAG,IAEvB5D,QAAQ,eACTlB,KAAA,CAAAyE,aAAA,CAACrE,UAAU,CAAC2E,IAAI;IAACC,KAAK,EAAET;EAAK,CAAE,CACV,CAAC;AAE5B,CAAC;AAED,eAAevD,uBAAuB"}
|
|
1
|
+
{"version":3,"names":["React","forwardRef","useCallback","useMemo","useWindowDimensions","Reanimated","interpolate","scrollTo","useAnimatedReaction","useAnimatedRef","useAnimatedScrollHandler","useAnimatedStyle","useSharedValue","useFocusedInputHandler","useReanimatedFocusedInput","useSmoothKeyboardHandler","debounce","KeyboardAwareScrollView","_ref","ref","children","bottomOffset","disableScrollOnKeyboardHide","enabled","rest","scrollViewAnimatedRef","scrollPosition","position","currentKeyboardFrameHeight","keyboardHeight","keyboardWillAppear","tag","initialKeyboardSize","scrollBeforeKeyboardMovement","input","layout","height","onScroll","e","value","contentOffset","y","onRef","assignedRef","current","maybeScroll","_layout$value","_layout$value2","animated","arguments","length","undefined","visibleRect","absoluteY","inputHeight","point","interpolatedScrollTo","targetScrollY","Math","max","positionOnScreen","topOfScreen","onChangeText","_layout$value3","_input$value","prevScrollPosition","prevLayout","onChangeTextHandler","onStart","keyboardWillChangeSize","keyboardWillHide","focusWasChanged","target","onMove","onEnd","previous","view","paddingBottom","createElement","ScrollView","_extends","onScrollReanimated","scrollEventThrottle","View","style"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useMemo } from \"react\";\nimport { useWindowDimensions } from \"react-native\";\nimport Reanimated, {\n interpolate,\n scrollTo,\n useAnimatedReaction,\n useAnimatedRef,\n useAnimatedScrollHandler,\n useAnimatedStyle,\n useSharedValue,\n} from \"react-native-reanimated\";\n\nimport {\n useFocusedInputHandler,\n useReanimatedFocusedInput,\n} from \"react-native-keyboard-controller\";\n\nimport { useSmoothKeyboardHandler } from \"./useSmoothKeyboardHandler\";\nimport { debounce } from \"./utils\";\n\nimport type { ScrollView, ScrollViewProps } from \"react-native\";\nimport type { FocusedInputLayoutChangedEvent } from \"react-native-keyboard-controller\";\n\ntype KeyboardAwareScrollViewProps = {\n /** The distance between keyboard and focused `TextInput` when keyboard is shown. Default is `0`. */\n bottomOffset?: number;\n /** Prevents automatic scrolling of the `ScrollView` when the keyboard gets hidden, maintaining the current screen position. Default is `false`. */\n disableScrollOnKeyboardHide?: boolean;\n /** Controls whether this `KeyboardAwareScrollView` instance should take effect. Default is `true` */\n enabled?: boolean;\n} & ScrollViewProps;\n\n/*\n * Everything begins from `onStart` handler. This handler is called every time,\n * when keyboard changes its size or when focused `TextInput` was changed. In\n * this handler we are calculating/memoizing values which later will be used\n * during layout movement. For that we calculate:\n * - layout of focused field (`layout`) - to understand whether there will be overlap\n * - initial keyboard size (`initialKeyboardSize`) - used in scroll interpolation\n * - future keyboard height (`keyboardHeight`) - used in scroll interpolation\n * - current scroll position (`scrollPosition`) - used to scroll from this point\n *\n * Once we've calculated all necessary variables - we can actually start to use them.\n * It happens in `onMove` handler - this function simply calls `maybeScroll` with\n * current keyboard frame height. This functions makes the smooth transition.\n *\n * When the transition has finished we go to `onEnd` handler. In this handler\n * we verify, that the current field is not overlapped within a keyboard frame.\n * For full `onStart`/`onMove`/`onEnd` flow it may look like a redundant thing,\n * however there could be some cases, when `onMove` is not called:\n * - on iOS when TextInput was changed - keyboard transition is instant\n * - on Android when TextInput was changed and keyboard size wasn't changed\n * So `onEnd` handler handle the case, when `onMove` wasn't triggered.\n *\n * ====================================================================================================================+\n * -----------------------------------------------------Flow chart-----------------------------------------------------+\n * ====================================================================================================================+\n *\n * +============================+ +============================+ +==================================+\n * + User Press on TextInput + => + Keyboard starts showing + => + As keyboard moves frame by frame + =>\n * + + + (run `onStart`) + + `onMove` is getting called +\n * +============================+ +============================+ +==================================+\n *\n *\n * +============================+ +============================+ +=====================================+\n * + Keyboard is shown and we + => + User moved focus to + => + Only `onStart`/`onEnd` maybe called +\n * + call `onEnd` handler + + another `TextInput` + + (without involving `onMove`) +\n * +============================+ +============================+ +=====================================+\n *\n */\nconst KeyboardAwareScrollView = forwardRef<\n ScrollView,\n React.PropsWithChildren<KeyboardAwareScrollViewProps>\n>(\n (\n {\n children,\n bottomOffset = 0,\n disableScrollOnKeyboardHide = false,\n enabled = true,\n ...rest\n },\n ref,\n ) => {\n const scrollViewAnimatedRef = useAnimatedRef<Reanimated.ScrollView>();\n const scrollPosition = useSharedValue(0);\n const position = useSharedValue(0);\n const currentKeyboardFrameHeight = useSharedValue(0);\n const keyboardHeight = useSharedValue(0);\n const keyboardWillAppear = useSharedValue(false);\n const tag = useSharedValue(-1);\n const initialKeyboardSize = useSharedValue(0);\n const scrollBeforeKeyboardMovement = useSharedValue(0);\n const { input } = useReanimatedFocusedInput();\n const layout = useSharedValue<FocusedInputLayoutChangedEvent | null>(null);\n\n const { height } = useWindowDimensions();\n\n const onScroll = useAnimatedScrollHandler(\n {\n onScroll: (e) => {\n position.value = e.contentOffset.y;\n },\n },\n [],\n );\n\n const onRef = useCallback((assignedRef: Reanimated.ScrollView) => {\n if (typeof ref === \"function\") {\n ref(assignedRef);\n } else if (ref) {\n ref.current = assignedRef;\n }\n\n scrollViewAnimatedRef(assignedRef);\n }, []);\n\n /**\n * Function that will scroll a ScrollView as keyboard gets moving\n */\n const maybeScroll = useCallback(\n (e: number, animated: boolean = false) => {\n \"worklet\";\n\n if (!enabled) {\n return 0;\n }\n\n const visibleRect = height - keyboardHeight.value;\n const absoluteY = layout.value?.layout.absoluteY || 0;\n const inputHeight = layout.value?.layout.height || 0;\n const point = absoluteY + inputHeight;\n\n if (visibleRect - point <= bottomOffset) {\n const interpolatedScrollTo = interpolate(\n e,\n [initialKeyboardSize.value, keyboardHeight.value],\n [0, keyboardHeight.value - (height - point) + bottomOffset],\n );\n const targetScrollY =\n Math.max(interpolatedScrollTo, 0) + scrollPosition.value;\n scrollTo(scrollViewAnimatedRef, 0, targetScrollY, animated);\n\n return interpolatedScrollTo;\n }\n\n if (absoluteY < 0) {\n const positionOnScreen = visibleRect - inputHeight - bottomOffset;\n const topOfScreen = scrollPosition.value + absoluteY;\n\n scrollTo(\n scrollViewAnimatedRef,\n 0,\n topOfScreen - positionOnScreen,\n animated,\n );\n }\n\n return 0;\n },\n [bottomOffset, enabled],\n );\n\n const onChangeText = useCallback(() => {\n \"worklet\";\n\n // if typing a text caused layout shift, then we need to ignore this handler\n // because this event will be handled in `useAnimatedReaction` below\n if (layout.value?.layout.height !== input.value?.layout.height) {\n return;\n }\n\n const prevScrollPosition = scrollPosition.value;\n const prevLayout = layout.value;\n\n scrollPosition.value = position.value;\n layout.value = input.value;\n maybeScroll(keyboardHeight.value, true);\n scrollPosition.value = prevScrollPosition;\n layout.value = prevLayout;\n }, [maybeScroll]);\n\n const onChangeTextHandler = useMemo(\n () => debounce(onChangeText, 200),\n [onChangeText],\n );\n\n useFocusedInputHandler(\n {\n onChangeText: onChangeTextHandler,\n },\n [onChangeTextHandler],\n );\n\n useSmoothKeyboardHandler(\n enabled\n ? {\n onStart: (e) => {\n \"worklet\";\n\n const keyboardWillChangeSize =\n keyboardHeight.value !== e.height && e.height > 0;\n keyboardWillAppear.value =\n e.height > 0 && keyboardHeight.value === 0;\n const keyboardWillHide = e.height === 0;\n const focusWasChanged =\n (tag.value !== e.target && e.target !== -1) ||\n keyboardWillChangeSize;\n\n if (keyboardWillChangeSize) {\n initialKeyboardSize.value = keyboardHeight.value;\n }\n\n if (keyboardWillHide) {\n // on back transition need to interpolate as [0, keyboardHeight]\n initialKeyboardSize.value = 0;\n scrollPosition.value = scrollBeforeKeyboardMovement.value;\n }\n\n if (\n keyboardWillAppear.value ||\n keyboardWillChangeSize ||\n focusWasChanged\n ) {\n // persist scroll value\n scrollPosition.value = position.value;\n // just persist height - later will be used in interpolation\n keyboardHeight.value = e.height;\n }\n\n // focus was changed\n if (focusWasChanged) {\n tag.value = e.target;\n\n // save position of focused text input when keyboard starts to move\n layout.value = input.value;\n // save current scroll position - when keyboard will hide we'll reuse\n // this value to achieve smooth hide effect\n scrollBeforeKeyboardMovement.value = position.value;\n }\n\n if (focusWasChanged && !keyboardWillAppear.value) {\n // update position on scroll value, so `onEnd` handler\n // will pick up correct values\n position.value += maybeScroll(e.height, true);\n }\n },\n onMove: (e) => {\n \"worklet\";\n\n currentKeyboardFrameHeight.value = e.height;\n\n // if the user has set disableScrollOnKeyboardHide, only auto-scroll when the keyboard opens\n if (!disableScrollOnKeyboardHide || keyboardWillAppear.value) {\n maybeScroll(e.height);\n }\n },\n onEnd: (e) => {\n \"worklet\";\n\n keyboardHeight.value = e.height;\n scrollPosition.value = position.value;\n },\n }\n : {},\n [height, maybeScroll, disableScrollOnKeyboardHide, enabled],\n );\n\n useAnimatedReaction(\n () => input.value,\n (current, previous) => {\n if (\n current?.target === previous?.target &&\n current?.layout.height !== previous?.layout.height\n ) {\n const prevLayout = layout.value;\n\n layout.value = input.value;\n scrollPosition.value += maybeScroll(keyboardHeight.value, true);\n layout.value = prevLayout;\n }\n },\n [],\n );\n\n const view = useAnimatedStyle(\n () => ({\n // animations become laggy when scrolling to the end of the `ScrollView` (when the last input is focused)\n // this happens because the layout recalculates on every frame. To avoid this we slightly increase padding\n // by `+1`. In this way we assure, that `scrollTo` will never scroll to the end, because it uses interpolation\n // from 0 to `keyboardHeight`, and here our padding is `keyboardHeight + 1`. It allows us not to re-run layout\n // re-calculation on every animation frame and it helps to achieve smooth animation.\n // see: https://github.com/kirillzyusko/react-native-keyboard-controller/pull/342\n paddingBottom: currentKeyboardFrameHeight.value + 1,\n }),\n [],\n );\n\n return (\n <Reanimated.ScrollView\n ref={onRef}\n {...rest}\n // @ts-expect-error `onScrollReanimated` is a fake prop needed for reanimated to intercept scroll events\n onScrollReanimated={onScroll}\n scrollEventThrottle={16}\n >\n {children}\n <Reanimated.View style={view} />\n </Reanimated.ScrollView>\n );\n },\n);\n\nexport default KeyboardAwareScrollView;\n"],"mappings":"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"}
|
|
@@ -18,16 +18,19 @@ const TELEGRAM_ANDROID_TIMING_CONFIG = {
|
|
|
18
18
|
*/
|
|
19
19
|
export const useSmoothKeyboardHandler = (handler, deps) => {
|
|
20
20
|
const target = useSharedValue(-1);
|
|
21
|
+
const height = useSharedValue(0);
|
|
21
22
|
const persistedHeight = useSharedValue(0);
|
|
22
23
|
const animatedKeyboardHeight = useSharedValue(0);
|
|
23
24
|
useAnimatedReaction(() => {
|
|
24
25
|
if (IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
28
|
+
if (persistedHeight.value === 0) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
27
31
|
const event = {
|
|
28
|
-
// it'll be always
|
|
29
|
-
|
|
30
|
-
duration: 250,
|
|
32
|
+
// it'll be always `TELEGRAM_ANDROID_TIMING_CONFIG.duration`, since we're running animation via `withTiming`
|
|
33
|
+
duration: TELEGRAM_ANDROID_TIMING_CONFIG.duration,
|
|
31
34
|
target: target.value,
|
|
32
35
|
height: animatedKeyboardHeight.value,
|
|
33
36
|
progress: animatedKeyboardHeight.value / persistedHeight.value
|
|
@@ -41,9 +44,10 @@ export const useSmoothKeyboardHandler = (handler, deps) => {
|
|
|
41
44
|
(_handler$onMove = handler.onMove) === null || _handler$onMove === void 0 ? void 0 : _handler$onMove.call(handler, evt);
|
|
42
45
|
|
|
43
46
|
// dispatch `onEnd`
|
|
44
|
-
if (evt.height ===
|
|
47
|
+
if (evt.height === height.value) {
|
|
45
48
|
var _handler$onEnd;
|
|
46
49
|
(_handler$onEnd = handler.onEnd) === null || _handler$onEnd === void 0 ? void 0 : _handler$onEnd.call(handler, evt);
|
|
50
|
+
persistedHeight.value = height.value;
|
|
47
51
|
}
|
|
48
52
|
}, [handler]);
|
|
49
53
|
useKeyboardHandler({
|
|
@@ -60,6 +64,7 @@ export const useSmoothKeyboardHandler = (handler, deps) => {
|
|
|
60
64
|
return;
|
|
61
65
|
}
|
|
62
66
|
target.value = e.target;
|
|
67
|
+
height.value = e.height;
|
|
63
68
|
if (e.height > 0) {
|
|
64
69
|
persistedHeight.value = e.height;
|
|
65
70
|
}
|
|
@@ -69,7 +74,10 @@ export const useSmoothKeyboardHandler = (handler, deps) => {
|
|
|
69
74
|
if (!IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {
|
|
70
75
|
animatedKeyboardHeight.value = withTiming(e.height, TELEGRAM_ANDROID_TIMING_CONFIG);
|
|
71
76
|
}
|
|
72
|
-
(_handler$onStart2 = handler.onStart) === null || _handler$onStart2 === void 0 ? void 0 : _handler$onStart2.call(handler,
|
|
77
|
+
(_handler$onStart2 = handler.onStart) === null || _handler$onStart2 === void 0 ? void 0 : _handler$onStart2.call(handler, {
|
|
78
|
+
...e,
|
|
79
|
+
duration: IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS ? e.duration : TELEGRAM_ANDROID_TIMING_CONFIG.duration
|
|
80
|
+
});
|
|
73
81
|
},
|
|
74
82
|
onMove: e => {
|
|
75
83
|
"worklet";
|
|
@@ -86,7 +94,6 @@ export const useSmoothKeyboardHandler = (handler, deps) => {
|
|
|
86
94
|
var _handler$onEnd3;
|
|
87
95
|
(_handler$onEnd3 = handler.onEnd) === null || _handler$onEnd3 === void 0 ? void 0 : _handler$onEnd3.call(handler, e);
|
|
88
96
|
}
|
|
89
|
-
persistedHeight.value = e.height;
|
|
90
97
|
}
|
|
91
98
|
}, deps);
|
|
92
99
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Platform","Easing","useAnimatedReaction","useSharedValue","withTiming","useKeyboardHandler","IS_ANDROID_ELEVEN_OR_HIGHER","OS","Version","IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS","TELEGRAM_ANDROID_TIMING_CONFIG","duration","easing","bezier","useSmoothKeyboardHandler","handler","deps","target","
|
|
1
|
+
{"version":3,"names":["Platform","Easing","useAnimatedReaction","useSharedValue","withTiming","useKeyboardHandler","IS_ANDROID_ELEVEN_OR_HIGHER","OS","Version","IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS","TELEGRAM_ANDROID_TIMING_CONFIG","duration","easing","bezier","useSmoothKeyboardHandler","handler","deps","target","height","persistedHeight","animatedKeyboardHeight","value","event","progress","evt","_handler$onMove","onMove","call","_handler$onEnd","onEnd","onStart","e","_handler$onStart2","_handler$onStart","_handler$onEnd2","_handler$onMove2","_handler$onEnd3"],"sources":["useSmoothKeyboardHandler.ts"],"sourcesContent":["import { Platform } from \"react-native\";\nimport {\n Easing,\n useAnimatedReaction,\n useSharedValue,\n withTiming,\n} from \"react-native-reanimated\";\n\nimport { useKeyboardHandler } from \"react-native-keyboard-controller\";\n\nconst IS_ANDROID_ELEVEN_OR_HIGHER =\n Platform.OS === \"android\" && Platform.Version >= 30;\n// on these platforms keyboard transitions will be smooth\nconst IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS =\n IS_ANDROID_ELEVEN_OR_HIGHER || Platform.OS === \"ios\";\n// on Android Telegram is not using androidx.core values and uses custom interpolation\n// duration is taken from here: https://github.com/DrKLO/Telegram/blob/e9a35cea54c06277c69d41b8e25d94b5d7ede065/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AdjustPanLayoutHelper.java#L39\n// and bezier is taken from: https://github.com/DrKLO/Telegram/blob/e9a35cea54c06277c69d41b8e25d94b5d7ede065/TMessagesProj/src/main/java/androidx/recyclerview/widget/ChatListItemAnimator.java#L40\nconst TELEGRAM_ANDROID_TIMING_CONFIG = {\n duration: 250,\n easing: Easing.bezier(\n 0.19919472913616398,\n 0.010644531250000006,\n 0.27920937042459737,\n 0.91025390625,\n ),\n};\n\n/**\n * Hook that uses default transitions for iOS and Android > 11, and uses\n * custom interpolation on Android < 11 to achieve more smooth animation\n */\nexport const useSmoothKeyboardHandler: typeof useKeyboardHandler = (\n handler,\n deps,\n) => {\n const target = useSharedValue(-1);\n const height = useSharedValue(0);\n const persistedHeight = useSharedValue(0);\n const animatedKeyboardHeight = useSharedValue(0);\n\n useAnimatedReaction(\n () => {\n if (IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {\n return;\n }\n if (persistedHeight.value === 0) {\n return;\n }\n const event = {\n // it'll be always `TELEGRAM_ANDROID_TIMING_CONFIG.duration`, since we're running animation via `withTiming`\n duration: TELEGRAM_ANDROID_TIMING_CONFIG.duration,\n target: target.value,\n height: animatedKeyboardHeight.value,\n progress: animatedKeyboardHeight.value / persistedHeight.value,\n };\n return event;\n },\n (evt) => {\n if (!evt) {\n return;\n }\n handler.onMove?.(evt);\n\n // dispatch `onEnd`\n if (evt.height === height.value) {\n handler.onEnd?.(evt);\n persistedHeight.value = height.value;\n }\n },\n [handler],\n );\n\n useKeyboardHandler(\n {\n onStart: (e) => {\n \"worklet\";\n\n // immediately dispatch onStart/onEnd events if onStart dispatched with the same height\n // and don't wait for animation 250ms\n if (\n !IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS &&\n e.height === persistedHeight.value\n ) {\n handler.onStart?.(e);\n handler.onEnd?.(e);\n\n return;\n }\n\n target.value = e.target;\n height.value = e.height;\n\n if (e.height > 0) {\n persistedHeight.value = e.height;\n }\n // if we are running on Android < 9, then we are using custom interpolation\n // to achieve smoother animation and use `animatedKeyboardHeight` as animation\n // driver\n if (!IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {\n animatedKeyboardHeight.value = withTiming(\n e.height,\n TELEGRAM_ANDROID_TIMING_CONFIG,\n );\n }\n\n handler.onStart?.({\n ...e,\n duration: IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS\n ? e.duration\n : TELEGRAM_ANDROID_TIMING_CONFIG.duration,\n });\n },\n onMove: (e) => {\n \"worklet\";\n\n if (IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {\n handler.onMove?.(e);\n }\n },\n onEnd: (e) => {\n \"worklet\";\n\n if (IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {\n handler.onEnd?.(e);\n }\n },\n },\n deps,\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { ScrollViewProps } from "react-native";
|
|
3
|
-
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { ScrollView, ScrollViewProps } from "react-native";
|
|
3
|
+
declare const KeyboardAwareScrollView: React.ForwardRefExoticComponent<{
|
|
4
4
|
/** The distance between keyboard and focused `TextInput` when keyboard is shown. Default is `0`. */
|
|
5
|
-
bottomOffset?: number;
|
|
5
|
+
bottomOffset?: number | undefined;
|
|
6
6
|
/** Prevents automatic scrolling of the `ScrollView` when the keyboard gets hidden, maintaining the current screen position. Default is `false`. */
|
|
7
|
-
disableScrollOnKeyboardHide?: boolean;
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
disableScrollOnKeyboardHide?: boolean | undefined;
|
|
8
|
+
/** Controls whether this `KeyboardAwareScrollView` instance should take effect. Default is `true` */
|
|
9
|
+
enabled?: boolean | undefined;
|
|
10
|
+
} & ScrollViewProps & {
|
|
11
|
+
children?: React.ReactNode;
|
|
12
|
+
} & React.RefAttributes<ScrollView>>;
|
|
10
13
|
export default KeyboardAwareScrollView;
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useMemo } from "react";
|
|
1
|
+
import React, { forwardRef, useCallback, useMemo } from "react";
|
|
2
2
|
import { useWindowDimensions } from "react-native";
|
|
3
3
|
import Reanimated, {
|
|
4
4
|
interpolate,
|
|
@@ -18,8 +18,7 @@ import {
|
|
|
18
18
|
import { useSmoothKeyboardHandler } from "./useSmoothKeyboardHandler";
|
|
19
19
|
import { debounce } from "./utils";
|
|
20
20
|
|
|
21
|
-
import type {
|
|
22
|
-
import type { ScrollViewProps } from "react-native";
|
|
21
|
+
import type { ScrollView, ScrollViewProps } from "react-native";
|
|
23
22
|
import type { FocusedInputLayoutChangedEvent } from "react-native-keyboard-controller";
|
|
24
23
|
|
|
25
24
|
type KeyboardAwareScrollViewProps = {
|
|
@@ -27,6 +26,8 @@ type KeyboardAwareScrollViewProps = {
|
|
|
27
26
|
bottomOffset?: number;
|
|
28
27
|
/** Prevents automatic scrolling of the `ScrollView` when the keyboard gets hidden, maintaining the current screen position. Default is `false`. */
|
|
29
28
|
disableScrollOnKeyboardHide?: boolean;
|
|
29
|
+
/** Controls whether this `KeyboardAwareScrollView` instance should take effect. Default is `true` */
|
|
30
|
+
enabled?: boolean;
|
|
30
31
|
} & ScrollViewProps;
|
|
31
32
|
|
|
32
33
|
/*
|
|
@@ -67,213 +68,247 @@ type KeyboardAwareScrollViewProps = {
|
|
|
67
68
|
* +============================+ +============================+ +=====================================+
|
|
68
69
|
*
|
|
69
70
|
*/
|
|
70
|
-
const KeyboardAwareScrollView
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}) => {
|
|
76
|
-
const scrollViewAnimatedRef = useAnimatedRef<Reanimated.ScrollView>();
|
|
77
|
-
const scrollPosition = useSharedValue(0);
|
|
78
|
-
const position = useSharedValue(0);
|
|
79
|
-
const currentKeyboardFrameHeight = useSharedValue(0);
|
|
80
|
-
const keyboardHeight = useSharedValue(0);
|
|
81
|
-
const keyboardWillAppear = useSharedValue(false);
|
|
82
|
-
const tag = useSharedValue(-1);
|
|
83
|
-
const initialKeyboardSize = useSharedValue(0);
|
|
84
|
-
const scrollBeforeKeyboardMovement = useSharedValue(0);
|
|
85
|
-
const { input } = useReanimatedFocusedInput();
|
|
86
|
-
const layout = useSharedValue<FocusedInputLayoutChangedEvent | null>(null);
|
|
87
|
-
|
|
88
|
-
const { height } = useWindowDimensions();
|
|
89
|
-
|
|
90
|
-
const onScroll = useAnimatedScrollHandler(
|
|
71
|
+
const KeyboardAwareScrollView = forwardRef<
|
|
72
|
+
ScrollView,
|
|
73
|
+
React.PropsWithChildren<KeyboardAwareScrollViewProps>
|
|
74
|
+
>(
|
|
75
|
+
(
|
|
91
76
|
{
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
77
|
+
children,
|
|
78
|
+
bottomOffset = 0,
|
|
79
|
+
disableScrollOnKeyboardHide = false,
|
|
80
|
+
enabled = true,
|
|
81
|
+
...rest
|
|
95
82
|
},
|
|
96
|
-
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
83
|
+
ref,
|
|
84
|
+
) => {
|
|
85
|
+
const scrollViewAnimatedRef = useAnimatedRef<Reanimated.ScrollView>();
|
|
86
|
+
const scrollPosition = useSharedValue(0);
|
|
87
|
+
const position = useSharedValue(0);
|
|
88
|
+
const currentKeyboardFrameHeight = useSharedValue(0);
|
|
89
|
+
const keyboardHeight = useSharedValue(0);
|
|
90
|
+
const keyboardWillAppear = useSharedValue(false);
|
|
91
|
+
const tag = useSharedValue(-1);
|
|
92
|
+
const initialKeyboardSize = useSharedValue(0);
|
|
93
|
+
const scrollBeforeKeyboardMovement = useSharedValue(0);
|
|
94
|
+
const { input } = useReanimatedFocusedInput();
|
|
95
|
+
const layout = useSharedValue<FocusedInputLayoutChangedEvent | null>(null);
|
|
96
|
+
|
|
97
|
+
const { height } = useWindowDimensions();
|
|
98
|
+
|
|
99
|
+
const onScroll = useAnimatedScrollHandler(
|
|
100
|
+
{
|
|
101
|
+
onScroll: (e) => {
|
|
102
|
+
position.value = e.contentOffset.y;
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
[],
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const onRef = useCallback((assignedRef: Reanimated.ScrollView) => {
|
|
109
|
+
if (typeof ref === "function") {
|
|
110
|
+
ref(assignedRef);
|
|
111
|
+
} else if (ref) {
|
|
112
|
+
ref.current = assignedRef;
|
|
122
113
|
}
|
|
123
114
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const topOfScreen = scrollPosition.value + absoluteY;
|
|
115
|
+
scrollViewAnimatedRef(assignedRef);
|
|
116
|
+
}, []);
|
|
127
117
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return 0;
|
|
137
|
-
},
|
|
138
|
-
[bottomOffset],
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
const onChangeText = useCallback(() => {
|
|
142
|
-
"worklet";
|
|
143
|
-
|
|
144
|
-
// if typing a text caused layout shift, then we need to ignore this handler
|
|
145
|
-
// because this event will be handled in `useAnimatedReaction` below
|
|
146
|
-
if (layout.value?.layout.height !== input.value?.layout.height) {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const prevScrollPosition = scrollPosition.value;
|
|
151
|
-
const prevLayout = layout.value;
|
|
152
|
-
|
|
153
|
-
scrollPosition.value = position.value;
|
|
154
|
-
layout.value = input.value;
|
|
155
|
-
maybeScroll(keyboardHeight.value, true);
|
|
156
|
-
scrollPosition.value = prevScrollPosition;
|
|
157
|
-
layout.value = prevLayout;
|
|
158
|
-
}, [maybeScroll]);
|
|
159
|
-
const onChangeTextHandler = useMemo(
|
|
160
|
-
() => debounce(onChangeText, 200),
|
|
161
|
-
[onChangeText],
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
useFocusedInputHandler(
|
|
165
|
-
{
|
|
166
|
-
onChangeText: onChangeTextHandler,
|
|
167
|
-
},
|
|
168
|
-
[onChangeTextHandler],
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
useSmoothKeyboardHandler(
|
|
172
|
-
{
|
|
173
|
-
onStart: (e) => {
|
|
118
|
+
/**
|
|
119
|
+
* Function that will scroll a ScrollView as keyboard gets moving
|
|
120
|
+
*/
|
|
121
|
+
const maybeScroll = useCallback(
|
|
122
|
+
(e: number, animated: boolean = false) => {
|
|
174
123
|
"worklet";
|
|
175
124
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
keyboardWillAppear.value = e.height > 0 && keyboardHeight.value === 0;
|
|
179
|
-
const keyboardWillHide = e.height === 0;
|
|
180
|
-
const focusWasChanged =
|
|
181
|
-
(tag.value !== e.target && e.target !== -1) || keyboardWillChangeSize;
|
|
182
|
-
|
|
183
|
-
if (keyboardWillChangeSize) {
|
|
184
|
-
initialKeyboardSize.value = keyboardHeight.value;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (keyboardWillHide) {
|
|
188
|
-
// on back transition need to interpolate as [0, keyboardHeight]
|
|
189
|
-
initialKeyboardSize.value = 0;
|
|
190
|
-
scrollPosition.value = scrollBeforeKeyboardMovement.value;
|
|
125
|
+
if (!enabled) {
|
|
126
|
+
return 0;
|
|
191
127
|
}
|
|
192
128
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
129
|
+
const visibleRect = height - keyboardHeight.value;
|
|
130
|
+
const absoluteY = layout.value?.layout.absoluteY || 0;
|
|
131
|
+
const inputHeight = layout.value?.layout.height || 0;
|
|
132
|
+
const point = absoluteY + inputHeight;
|
|
133
|
+
|
|
134
|
+
if (visibleRect - point <= bottomOffset) {
|
|
135
|
+
const interpolatedScrollTo = interpolate(
|
|
136
|
+
e,
|
|
137
|
+
[initialKeyboardSize.value, keyboardHeight.value],
|
|
138
|
+
[0, keyboardHeight.value - (height - point) + bottomOffset],
|
|
139
|
+
);
|
|
140
|
+
const targetScrollY =
|
|
141
|
+
Math.max(interpolatedScrollTo, 0) + scrollPosition.value;
|
|
142
|
+
scrollTo(scrollViewAnimatedRef, 0, targetScrollY, animated);
|
|
143
|
+
|
|
144
|
+
return interpolatedScrollTo;
|
|
202
145
|
}
|
|
203
146
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
147
|
+
if (absoluteY < 0) {
|
|
148
|
+
const positionOnScreen = visibleRect - inputHeight - bottomOffset;
|
|
149
|
+
const topOfScreen = scrollPosition.value + absoluteY;
|
|
207
150
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
151
|
+
scrollTo(
|
|
152
|
+
scrollViewAnimatedRef,
|
|
153
|
+
0,
|
|
154
|
+
topOfScreen - positionOnScreen,
|
|
155
|
+
animated,
|
|
156
|
+
);
|
|
213
157
|
}
|
|
214
158
|
|
|
215
|
-
|
|
216
|
-
// update position on scroll value, so `onEnd` handler
|
|
217
|
-
// will pick up correct values
|
|
218
|
-
position.value += maybeScroll(e.height, true);
|
|
219
|
-
}
|
|
159
|
+
return 0;
|
|
220
160
|
},
|
|
221
|
-
|
|
222
|
-
|
|
161
|
+
[bottomOffset, enabled],
|
|
162
|
+
);
|
|
223
163
|
|
|
224
|
-
|
|
164
|
+
const onChangeText = useCallback(() => {
|
|
165
|
+
"worklet";
|
|
225
166
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
167
|
+
// if typing a text caused layout shift, then we need to ignore this handler
|
|
168
|
+
// because this event will be handled in `useAnimatedReaction` below
|
|
169
|
+
if (layout.value?.layout.height !== input.value?.layout.height) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const prevScrollPosition = scrollPosition.value;
|
|
174
|
+
const prevLayout = layout.value;
|
|
175
|
+
|
|
176
|
+
scrollPosition.value = position.value;
|
|
177
|
+
layout.value = input.value;
|
|
178
|
+
maybeScroll(keyboardHeight.value, true);
|
|
179
|
+
scrollPosition.value = prevScrollPosition;
|
|
180
|
+
layout.value = prevLayout;
|
|
181
|
+
}, [maybeScroll]);
|
|
182
|
+
|
|
183
|
+
const onChangeTextHandler = useMemo(
|
|
184
|
+
() => debounce(onChangeText, 200),
|
|
185
|
+
[onChangeText],
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
useFocusedInputHandler(
|
|
189
|
+
{
|
|
190
|
+
onChangeText: onChangeTextHandler,
|
|
230
191
|
},
|
|
231
|
-
|
|
232
|
-
|
|
192
|
+
[onChangeTextHandler],
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
useSmoothKeyboardHandler(
|
|
196
|
+
enabled
|
|
197
|
+
? {
|
|
198
|
+
onStart: (e) => {
|
|
199
|
+
"worklet";
|
|
200
|
+
|
|
201
|
+
const keyboardWillChangeSize =
|
|
202
|
+
keyboardHeight.value !== e.height && e.height > 0;
|
|
203
|
+
keyboardWillAppear.value =
|
|
204
|
+
e.height > 0 && keyboardHeight.value === 0;
|
|
205
|
+
const keyboardWillHide = e.height === 0;
|
|
206
|
+
const focusWasChanged =
|
|
207
|
+
(tag.value !== e.target && e.target !== -1) ||
|
|
208
|
+
keyboardWillChangeSize;
|
|
209
|
+
|
|
210
|
+
if (keyboardWillChangeSize) {
|
|
211
|
+
initialKeyboardSize.value = keyboardHeight.value;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (keyboardWillHide) {
|
|
215
|
+
// on back transition need to interpolate as [0, keyboardHeight]
|
|
216
|
+
initialKeyboardSize.value = 0;
|
|
217
|
+
scrollPosition.value = scrollBeforeKeyboardMovement.value;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (
|
|
221
|
+
keyboardWillAppear.value ||
|
|
222
|
+
keyboardWillChangeSize ||
|
|
223
|
+
focusWasChanged
|
|
224
|
+
) {
|
|
225
|
+
// persist scroll value
|
|
226
|
+
scrollPosition.value = position.value;
|
|
227
|
+
// just persist height - later will be used in interpolation
|
|
228
|
+
keyboardHeight.value = e.height;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// focus was changed
|
|
232
|
+
if (focusWasChanged) {
|
|
233
|
+
tag.value = e.target;
|
|
234
|
+
|
|
235
|
+
// save position of focused text input when keyboard starts to move
|
|
236
|
+
layout.value = input.value;
|
|
237
|
+
// save current scroll position - when keyboard will hide we'll reuse
|
|
238
|
+
// this value to achieve smooth hide effect
|
|
239
|
+
scrollBeforeKeyboardMovement.value = position.value;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (focusWasChanged && !keyboardWillAppear.value) {
|
|
243
|
+
// update position on scroll value, so `onEnd` handler
|
|
244
|
+
// will pick up correct values
|
|
245
|
+
position.value += maybeScroll(e.height, true);
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
onMove: (e) => {
|
|
249
|
+
"worklet";
|
|
250
|
+
|
|
251
|
+
currentKeyboardFrameHeight.value = e.height;
|
|
252
|
+
|
|
253
|
+
// if the user has set disableScrollOnKeyboardHide, only auto-scroll when the keyboard opens
|
|
254
|
+
if (!disableScrollOnKeyboardHide || keyboardWillAppear.value) {
|
|
255
|
+
maybeScroll(e.height);
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
onEnd: (e) => {
|
|
259
|
+
"worklet";
|
|
260
|
+
|
|
261
|
+
keyboardHeight.value = e.height;
|
|
262
|
+
scrollPosition.value = position.value;
|
|
263
|
+
},
|
|
264
|
+
}
|
|
265
|
+
: {},
|
|
266
|
+
[height, maybeScroll, disableScrollOnKeyboardHide, enabled],
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
useAnimatedReaction(
|
|
270
|
+
() => input.value,
|
|
271
|
+
(current, previous) => {
|
|
272
|
+
if (
|
|
273
|
+
current?.target === previous?.target &&
|
|
274
|
+
current?.layout.height !== previous?.layout.height
|
|
275
|
+
) {
|
|
276
|
+
const prevLayout = layout.value;
|
|
233
277
|
|
|
234
|
-
|
|
235
|
-
|
|
278
|
+
layout.value = input.value;
|
|
279
|
+
scrollPosition.value += maybeScroll(keyboardHeight.value, true);
|
|
280
|
+
layout.value = prevLayout;
|
|
281
|
+
}
|
|
236
282
|
},
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
ref={scrollViewAnimatedRef}
|
|
268
|
-
{...rest}
|
|
269
|
-
// @ts-expect-error `onScrollReanimated` is a fake prop needed for reanimated to intercept scroll events
|
|
270
|
-
onScrollReanimated={onScroll}
|
|
271
|
-
scrollEventThrottle={16}
|
|
272
|
-
>
|
|
273
|
-
{children}
|
|
274
|
-
<Reanimated.View style={view} />
|
|
275
|
-
</Reanimated.ScrollView>
|
|
276
|
-
);
|
|
277
|
-
};
|
|
283
|
+
[],
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
const view = useAnimatedStyle(
|
|
287
|
+
() => ({
|
|
288
|
+
// animations become laggy when scrolling to the end of the `ScrollView` (when the last input is focused)
|
|
289
|
+
// this happens because the layout recalculates on every frame. To avoid this we slightly increase padding
|
|
290
|
+
// by `+1`. In this way we assure, that `scrollTo` will never scroll to the end, because it uses interpolation
|
|
291
|
+
// from 0 to `keyboardHeight`, and here our padding is `keyboardHeight + 1`. It allows us not to re-run layout
|
|
292
|
+
// re-calculation on every animation frame and it helps to achieve smooth animation.
|
|
293
|
+
// see: https://github.com/kirillzyusko/react-native-keyboard-controller/pull/342
|
|
294
|
+
paddingBottom: currentKeyboardFrameHeight.value + 1,
|
|
295
|
+
}),
|
|
296
|
+
[],
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
return (
|
|
300
|
+
<Reanimated.ScrollView
|
|
301
|
+
ref={onRef}
|
|
302
|
+
{...rest}
|
|
303
|
+
// @ts-expect-error `onScrollReanimated` is a fake prop needed for reanimated to intercept scroll events
|
|
304
|
+
onScrollReanimated={onScroll}
|
|
305
|
+
scrollEventThrottle={16}
|
|
306
|
+
>
|
|
307
|
+
{children}
|
|
308
|
+
<Reanimated.View style={view} />
|
|
309
|
+
</Reanimated.ScrollView>
|
|
310
|
+
);
|
|
311
|
+
},
|
|
312
|
+
);
|
|
278
313
|
|
|
279
314
|
export default KeyboardAwareScrollView;
|
|
@@ -35,6 +35,7 @@ export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
|
|
|
35
35
|
deps,
|
|
36
36
|
) => {
|
|
37
37
|
const target = useSharedValue(-1);
|
|
38
|
+
const height = useSharedValue(0);
|
|
38
39
|
const persistedHeight = useSharedValue(0);
|
|
39
40
|
const animatedKeyboardHeight = useSharedValue(0);
|
|
40
41
|
|
|
@@ -43,10 +44,12 @@ export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
|
|
|
43
44
|
if (IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
47
|
+
if (persistedHeight.value === 0) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
46
50
|
const event = {
|
|
47
|
-
// it'll be always
|
|
48
|
-
|
|
49
|
-
duration: 250,
|
|
51
|
+
// it'll be always `TELEGRAM_ANDROID_TIMING_CONFIG.duration`, since we're running animation via `withTiming`
|
|
52
|
+
duration: TELEGRAM_ANDROID_TIMING_CONFIG.duration,
|
|
50
53
|
target: target.value,
|
|
51
54
|
height: animatedKeyboardHeight.value,
|
|
52
55
|
progress: animatedKeyboardHeight.value / persistedHeight.value,
|
|
@@ -60,8 +63,9 @@ export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
|
|
|
60
63
|
handler.onMove?.(evt);
|
|
61
64
|
|
|
62
65
|
// dispatch `onEnd`
|
|
63
|
-
if (evt.height ===
|
|
66
|
+
if (evt.height === height.value) {
|
|
64
67
|
handler.onEnd?.(evt);
|
|
68
|
+
persistedHeight.value = height.value;
|
|
65
69
|
}
|
|
66
70
|
},
|
|
67
71
|
[handler],
|
|
@@ -85,6 +89,7 @@ export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
|
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
target.value = e.target;
|
|
92
|
+
height.value = e.height;
|
|
88
93
|
|
|
89
94
|
if (e.height > 0) {
|
|
90
95
|
persistedHeight.value = e.height;
|
|
@@ -99,7 +104,12 @@ export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
|
|
|
99
104
|
);
|
|
100
105
|
}
|
|
101
106
|
|
|
102
|
-
handler.onStart?.(
|
|
107
|
+
handler.onStart?.({
|
|
108
|
+
...e,
|
|
109
|
+
duration: IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS
|
|
110
|
+
? e.duration
|
|
111
|
+
: TELEGRAM_ANDROID_TIMING_CONFIG.duration,
|
|
112
|
+
});
|
|
103
113
|
},
|
|
104
114
|
onMove: (e) => {
|
|
105
115
|
"worklet";
|
|
@@ -114,8 +124,6 @@ export const useSmoothKeyboardHandler: typeof useKeyboardHandler = (
|
|
|
114
124
|
if (IS_ANDROID_ELEVEN_OR_HIGHER_OR_IOS) {
|
|
115
125
|
handler.onEnd?.(e);
|
|
116
126
|
}
|
|
117
|
-
|
|
118
|
-
persistedHeight.value = e.height;
|
|
119
127
|
},
|
|
120
128
|
},
|
|
121
129
|
deps,
|