react-native-keyboard-controller 1.21.6 → 1.21.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +11 -1
  2. package/lib/commonjs/components/KeyboardChatScrollView/index.js +9 -0
  3. package/lib/commonjs/components/KeyboardChatScrollView/index.js.map +1 -1
  4. package/lib/commonjs/components/KeyboardChatScrollView/types.js.map +1 -1
  5. package/lib/commonjs/components/KeyboardChatScrollView/useEndVisible.js +40 -0
  6. package/lib/commonjs/components/KeyboardChatScrollView/useEndVisible.js.map +1 -0
  7. package/lib/commonjs/components/KeyboardStickyView/index.js +10 -14
  8. package/lib/commonjs/components/KeyboardStickyView/index.js.map +1 -1
  9. package/lib/commonjs/components/ScrollViewWithBottomPadding/index.js +34 -13
  10. package/lib/commonjs/components/ScrollViewWithBottomPadding/index.js.map +1 -1
  11. package/lib/module/components/KeyboardChatScrollView/index.js +9 -0
  12. package/lib/module/components/KeyboardChatScrollView/index.js.map +1 -1
  13. package/lib/module/components/KeyboardChatScrollView/types.js.map +1 -1
  14. package/lib/module/components/KeyboardChatScrollView/useEndVisible.js +33 -0
  15. package/lib/module/components/KeyboardChatScrollView/useEndVisible.js.map +1 -0
  16. package/lib/module/components/KeyboardStickyView/index.js +11 -15
  17. package/lib/module/components/KeyboardStickyView/index.js.map +1 -1
  18. package/lib/module/components/ScrollViewWithBottomPadding/index.js +35 -14
  19. package/lib/module/components/ScrollViewWithBottomPadding/index.js.map +1 -1
  20. package/lib/typescript/components/KeyboardChatScrollView/index.d.ts +2 -0
  21. package/lib/typescript/components/KeyboardChatScrollView/types.d.ts +25 -1
  22. package/lib/typescript/components/KeyboardChatScrollView/useEndVisible.d.ts +17 -0
  23. package/lib/typescript/components/ScrollViewWithBottomPadding/index.d.ts +12 -0
  24. package/package.json +1 -1
  25. package/src/components/KeyboardChatScrollView/index.tsx +10 -0
  26. package/src/components/KeyboardChatScrollView/types.ts +28 -1
  27. package/src/components/KeyboardChatScrollView/useEndVisible.ts +66 -0
  28. package/src/components/KeyboardStickyView/index.tsx +18 -19
  29. package/src/components/ScrollViewWithBottomPadding/index.tsx +65 -16
@@ -8,14 +8,18 @@ import com.facebook.react.bridge.Arguments
8
8
  import com.facebook.react.bridge.Promise
9
9
  import com.facebook.react.bridge.ReactApplicationContext
10
10
  import com.facebook.react.bridge.UiThreadUtil
11
+ import com.facebook.react.uimanager.IllegalViewOperationException
11
12
  import com.reactnativekeyboardcontroller.extensions.dp
12
13
  import com.reactnativekeyboardcontroller.extensions.screenLocation
13
14
  import com.reactnativekeyboardcontroller.extensions.uiManager
14
15
  import com.reactnativekeyboardcontroller.extensions.windowSoftInputMode
15
16
  import com.reactnativekeyboardcontroller.interactive.KeyboardAnimationController
17
+ import com.reactnativekeyboardcontroller.log.Logger
16
18
  import com.reactnativekeyboardcontroller.traversal.FocusedInputHolder
17
19
  import com.reactnativekeyboardcontroller.traversal.ViewHierarchyNavigator
18
20
 
21
+ private val TAG = KeyboardControllerModuleImpl::class.qualifiedName
22
+
19
23
  class KeyboardControllerModuleImpl(
20
24
  private val mReactContext: ReactApplicationContext,
21
25
  ) {
@@ -89,7 +93,13 @@ class KeyboardControllerModuleImpl(
89
93
  promise: Promise,
90
94
  ) {
91
95
  UiThreadUtil.runOnUiThread {
92
- val view = uiManager?.resolveView(viewTag.toInt())
96
+ val view =
97
+ try {
98
+ uiManager?.resolveView(viewTag.toInt())
99
+ } catch (e: IllegalViewOperationException) {
100
+ Logger.w(TAG, "Could not resolve view for tag ${viewTag.toInt()}", e)
101
+ null
102
+ }
93
103
  if (view == null) {
94
104
  promise.reject("E_VIEW_NOT_FOUND", "Could not find view for tag")
95
105
  return@runOnUiThread
@@ -10,6 +10,7 @@ var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reani
10
10
  var _useCombinedRef = _interopRequireDefault(require("../hooks/useCombinedRef"));
11
11
  var _ScrollViewWithBottomPadding = _interopRequireDefault(require("../ScrollViewWithBottomPadding"));
12
12
  var _useChatKeyboard = require("./useChatKeyboard");
13
+ var _useEndVisible = require("./useEndVisible");
13
14
  var _useExtraContentPadding = require("./useExtraContentPadding");
14
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
16
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
@@ -28,6 +29,7 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({
28
29
  applyWorkaroundForContentInsetHitTestBug = false,
29
30
  onLayout: onLayoutProp,
30
31
  onContentSizeChange: onContentSizeChangeProp,
32
+ onEndVisible,
31
33
  ...rest
32
34
  }, ref) => {
33
35
  const scrollViewRef = (0, _reactNativeReanimated.useAnimatedRef)();
@@ -63,6 +65,13 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({
63
65
  keyboardLiftBehavior,
64
66
  freeze: freezeSV
65
67
  });
68
+ (0, _useEndVisible.useEndVisible)({
69
+ scroll,
70
+ layout,
71
+ size,
72
+ inverted,
73
+ onEndVisible
74
+ });
66
75
  const totalPadding = (0, _reactNativeReanimated.useDerivedValue)(() => Math.max(blankSpace.value, padding.value + extraContentPadding.value));
67
76
 
68
77
  // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace).
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","_useCombinedRef","_interopRequireDefault","_ScrollViewWithBottomPadding","_useChatKeyboard","_useExtraContentPadding","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","ZERO_CONTENT_PADDING","makeMutable","ZERO_BLANK_SPACE","KeyboardChatScrollView","forwardRef","children","ScrollViewComponent","Reanimated","ScrollView","inverted","keyboardLiftBehavior","freeze","offset","extraContentPadding","blankSpace","applyWorkaroundForContentInsetHitTestBug","onLayout","onLayoutProp","onContentSizeChange","onContentSizeChangeProp","rest","ref","scrollViewRef","useAnimatedRef","onRef","useCombinedRef","freezeSV","useDerivedValue","value","padding","currentHeight","contentOffsetY","scroll","layout","size","onLayoutInternal","onContentSizeChangeInternal","useChatKeyboard","useExtraContentPadding","keyboardPadding","totalPadding","Math","max","indicatorPadding","useCallback","w","h","commitStyle","useAnimatedStyle","transform","translateY","commit","useMemo","styles","commitView","createElement","Fragment","bottomPadding","scrollIndicatorPadding","View","style","StyleSheet","create","display","position","_default","exports"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useMemo } from \"react\";\nimport { StyleSheet } from \"react-native\";\nimport {\n makeMutable,\n useAnimatedRef,\n useAnimatedStyle,\n useDerivedValue,\n} from \"react-native-reanimated\";\nimport Reanimated from \"react-native-reanimated\";\n\nimport useCombinedRef from \"../hooks/useCombinedRef\";\nimport ScrollViewWithBottomPadding from \"../ScrollViewWithBottomPadding\";\n\nimport { useChatKeyboard } from \"./useChatKeyboard\";\nimport { useExtraContentPadding } from \"./useExtraContentPadding\";\n\nimport type { KeyboardChatScrollViewProps } from \"./types\";\nimport type { LayoutChangeEvent } from \"react-native\";\n\nconst ZERO_CONTENT_PADDING = makeMutable(0);\nconst ZERO_BLANK_SPACE = makeMutable(0);\n\nconst KeyboardChatScrollView = forwardRef<\n Reanimated.ScrollView,\n React.PropsWithChildren<KeyboardChatScrollViewProps>\n>(\n (\n {\n children,\n ScrollViewComponent = Reanimated.ScrollView,\n inverted = false,\n keyboardLiftBehavior = \"always\",\n freeze = false,\n offset = 0,\n extraContentPadding = ZERO_CONTENT_PADDING,\n blankSpace = ZERO_BLANK_SPACE,\n applyWorkaroundForContentInsetHitTestBug = false,\n onLayout: onLayoutProp,\n onContentSizeChange: onContentSizeChangeProp,\n ...rest\n },\n ref,\n ) => {\n const scrollViewRef = useAnimatedRef<Reanimated.ScrollView>();\n const onRef = useCombinedRef(ref, scrollViewRef);\n const freezeSV = useDerivedValue(() =>\n typeof freeze === \"boolean\" ? freeze : freeze.value,\n );\n const {\n padding,\n currentHeight,\n contentOffsetY,\n scroll,\n layout,\n size,\n onLayout: onLayoutInternal,\n onContentSizeChange: onContentSizeChangeInternal,\n } = useChatKeyboard(scrollViewRef, {\n inverted,\n keyboardLiftBehavior,\n freeze: freezeSV,\n offset,\n blankSpace,\n extraContentPadding,\n });\n\n useExtraContentPadding({\n scrollViewRef,\n extraContentPadding,\n keyboardPadding: padding,\n blankSpace,\n scroll,\n layout,\n size,\n contentOffsetY,\n inverted,\n keyboardLiftBehavior,\n freeze: freezeSV,\n });\n\n const totalPadding = useDerivedValue(() =>\n Math.max(blankSpace.value, padding.value + extraContentPadding.value),\n );\n\n // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace).\n // Apps that render into the unsafe area can supply a negative\n // scrollIndicatorInsets adjustment at the application layer.\n const indicatorPadding = useDerivedValue(\n () => padding.value + extraContentPadding.value,\n );\n\n const onLayout = useCallback(\n (e: LayoutChangeEvent) => {\n onLayoutInternal(e);\n onLayoutProp?.(e);\n },\n [onLayoutInternal, onLayoutProp],\n );\n\n const onContentSizeChange = useCallback(\n (w: number, h: number) => {\n onContentSizeChangeInternal(w, h);\n onContentSizeChangeProp?.(w, h);\n },\n [onContentSizeChangeInternal, onContentSizeChangeProp],\n );\n\n // Invisible view whose animated style changes every frame during keyboard\n // animation. On Fabric, this forces Reanimated to schedule a commit,\n // which flushes the scrollTo call in the same frame (fixing de-synchronization).\n // see https://github.com/software-mansion/react-native-reanimated/issues/9000\n const commitStyle = useAnimatedStyle(\n () => ({\n transform: [{ translateY: -currentHeight.value }],\n }),\n [],\n );\n const commit = useMemo(\n () => [styles.commitView, commitStyle],\n [commitStyle],\n );\n\n return (\n <>\n <ScrollViewWithBottomPadding\n ref={onRef}\n {...rest}\n applyWorkaroundForContentInsetHitTestBug={\n applyWorkaroundForContentInsetHitTestBug\n }\n bottomPadding={totalPadding}\n contentOffsetY={contentOffsetY}\n inverted={inverted}\n scrollIndicatorPadding={indicatorPadding}\n ScrollViewComponent={ScrollViewComponent}\n onContentSizeChange={onContentSizeChange}\n onLayout={onLayout}\n >\n {children}\n </ScrollViewWithBottomPadding>\n <Reanimated.View style={commit} />\n </>\n );\n },\n);\n\nconst styles = StyleSheet.create({\n commitView: {\n display: \"none\",\n position: \"absolute\",\n },\n});\n\nexport default KeyboardChatScrollView;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","_useCombinedRef","_interopRequireDefault","_ScrollViewWithBottomPadding","_useChatKeyboard","_useEndVisible","_useExtraContentPadding","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","ZERO_CONTENT_PADDING","makeMutable","ZERO_BLANK_SPACE","KeyboardChatScrollView","forwardRef","children","ScrollViewComponent","Reanimated","ScrollView","inverted","keyboardLiftBehavior","freeze","offset","extraContentPadding","blankSpace","applyWorkaroundForContentInsetHitTestBug","onLayout","onLayoutProp","onContentSizeChange","onContentSizeChangeProp","onEndVisible","rest","ref","scrollViewRef","useAnimatedRef","onRef","useCombinedRef","freezeSV","useDerivedValue","value","padding","currentHeight","contentOffsetY","scroll","layout","size","onLayoutInternal","onContentSizeChangeInternal","useChatKeyboard","useExtraContentPadding","keyboardPadding","useEndVisible","totalPadding","Math","max","indicatorPadding","useCallback","w","h","commitStyle","useAnimatedStyle","transform","translateY","commit","useMemo","styles","commitView","createElement","Fragment","bottomPadding","scrollIndicatorPadding","View","style","StyleSheet","create","display","position","_default","exports"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useMemo } from \"react\";\nimport { StyleSheet } from \"react-native\";\nimport {\n makeMutable,\n useAnimatedRef,\n useAnimatedStyle,\n useDerivedValue,\n} from \"react-native-reanimated\";\nimport Reanimated from \"react-native-reanimated\";\n\nimport useCombinedRef from \"../hooks/useCombinedRef\";\nimport ScrollViewWithBottomPadding from \"../ScrollViewWithBottomPadding\";\n\nimport { useChatKeyboard } from \"./useChatKeyboard\";\nimport { useEndVisible } from \"./useEndVisible\";\nimport { useExtraContentPadding } from \"./useExtraContentPadding\";\n\nimport type { KeyboardChatScrollViewProps } from \"./types\";\nimport type { LayoutChangeEvent } from \"react-native\";\n\nconst ZERO_CONTENT_PADDING = makeMutable(0);\nconst ZERO_BLANK_SPACE = makeMutable(0);\n\nconst KeyboardChatScrollView = forwardRef<\n Reanimated.ScrollView,\n React.PropsWithChildren<KeyboardChatScrollViewProps>\n>(\n (\n {\n children,\n ScrollViewComponent = Reanimated.ScrollView,\n inverted = false,\n keyboardLiftBehavior = \"always\",\n freeze = false,\n offset = 0,\n extraContentPadding = ZERO_CONTENT_PADDING,\n blankSpace = ZERO_BLANK_SPACE,\n applyWorkaroundForContentInsetHitTestBug = false,\n onLayout: onLayoutProp,\n onContentSizeChange: onContentSizeChangeProp,\n onEndVisible,\n ...rest\n },\n ref,\n ) => {\n const scrollViewRef = useAnimatedRef<Reanimated.ScrollView>();\n const onRef = useCombinedRef(ref, scrollViewRef);\n const freezeSV = useDerivedValue(() =>\n typeof freeze === \"boolean\" ? freeze : freeze.value,\n );\n const {\n padding,\n currentHeight,\n contentOffsetY,\n scroll,\n layout,\n size,\n onLayout: onLayoutInternal,\n onContentSizeChange: onContentSizeChangeInternal,\n } = useChatKeyboard(scrollViewRef, {\n inverted,\n keyboardLiftBehavior,\n freeze: freezeSV,\n offset,\n blankSpace,\n extraContentPadding,\n });\n\n useExtraContentPadding({\n scrollViewRef,\n extraContentPadding,\n keyboardPadding: padding,\n blankSpace,\n scroll,\n layout,\n size,\n contentOffsetY,\n inverted,\n keyboardLiftBehavior,\n freeze: freezeSV,\n });\n\n useEndVisible({\n scroll,\n layout,\n size,\n inverted,\n onEndVisible,\n });\n\n const totalPadding = useDerivedValue(() =>\n Math.max(blankSpace.value, padding.value + extraContentPadding.value),\n );\n\n // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace).\n // Apps that render into the unsafe area can supply a negative\n // scrollIndicatorInsets adjustment at the application layer.\n const indicatorPadding = useDerivedValue(\n () => padding.value + extraContentPadding.value,\n );\n\n const onLayout = useCallback(\n (e: LayoutChangeEvent) => {\n onLayoutInternal(e);\n onLayoutProp?.(e);\n },\n [onLayoutInternal, onLayoutProp],\n );\n\n const onContentSizeChange = useCallback(\n (w: number, h: number) => {\n onContentSizeChangeInternal(w, h);\n onContentSizeChangeProp?.(w, h);\n },\n [onContentSizeChangeInternal, onContentSizeChangeProp],\n );\n\n // Invisible view whose animated style changes every frame during keyboard\n // animation. On Fabric, this forces Reanimated to schedule a commit,\n // which flushes the scrollTo call in the same frame (fixing de-synchronization).\n // see https://github.com/software-mansion/react-native-reanimated/issues/9000\n const commitStyle = useAnimatedStyle(\n () => ({\n transform: [{ translateY: -currentHeight.value }],\n }),\n [],\n );\n const commit = useMemo(\n () => [styles.commitView, commitStyle],\n [commitStyle],\n );\n\n return (\n <>\n <ScrollViewWithBottomPadding\n ref={onRef}\n {...rest}\n applyWorkaroundForContentInsetHitTestBug={\n applyWorkaroundForContentInsetHitTestBug\n }\n bottomPadding={totalPadding}\n contentOffsetY={contentOffsetY}\n inverted={inverted}\n scrollIndicatorPadding={indicatorPadding}\n ScrollViewComponent={ScrollViewComponent}\n onContentSizeChange={onContentSizeChange}\n onLayout={onLayout}\n >\n {children}\n </ScrollViewWithBottomPadding>\n <Reanimated.View style={commit} />\n </>\n );\n },\n);\n\nconst styles = StyleSheet.create({\n commitView: {\n display: \"none\",\n position: \"absolute\",\n },\n});\n\nexport default KeyboardChatScrollView;\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import type { AnimatedScrollViewComponent } from \"../ScrollViewWithBottomPadding\";\nimport type { KeyboardLiftBehavior } from \"./useChatKeyboard/types\";\nimport type { ScrollViewProps } from \"react-native\";\nimport type { SharedValue } from \"react-native-reanimated\";\n\nexport type KeyboardChatScrollViewProps = {\n /** Custom component for `ScrollView`. Default is `ScrollView`. */\n ScrollViewComponent?: AnimatedScrollViewComponent;\n /** Whether list are using `inverted` prop. Default is `false`. */\n inverted?: boolean;\n /**\n * The distance between the bottom of the screen and the `ScrollView`.\n * When the keyboard appears, the `ScrollView` will only push content by the effective\n * distance (`keyboardHeight - offset`) instead of the full keyboard height.\n *\n * Useful when the input is not at the very bottom of the screen (e.g., above safe area, above\n * bottom tabs, etc. - in this case offset should be equal to the height of the elements between\n * `ScrollView` and bottom of the screen).\n *\n * Default is `0`.\n */\n offset?: number;\n /**\n * Determines how the chat content should behave when the keyboard appears, specifically whether\n * the scroll view should automatically lift its content to keep it visible above the keyboard.\n *\n * Possible values:\n * - `'always'`: The content always lifts along with the keyboard, ensuring the messages from the bottom part of screen\n * remain visible regardless of the current scroll position. This is the default behavior for most chat applications (used in Telegram).\n * - `'whenAtEnd'`: The content lifts only if the scroll view is at the end (i.e., the last message\n * is visible or near the bottom). This prevents unnecessary adjustments when the user is scrolling\n * through older messages (ChatGPT mobile app behavior).\n * - `'persistent'`: The content always lifts when the keyboard appears (similar to `'always'`), but\n * does not reset (lower) when the keyboard hides. This mimics behaviors where the view remains adjusted\n * after keyboard dismissal to maintain focus on the latest content without shifting back (Claude mobile app behavior).\n * - `'never'`: The content does not lift at all when the keyboard appears. Use this for scenarios\n * when you don't want to disturb user attention with animations (Perplexity mobile app behavior).\n *\n * Default is `'always'`.\n */\n keyboardLiftBehavior?: KeyboardLiftBehavior;\n /**\n * When `true`, freezes all keyboard-driven layout changes (padding, content offset, scroll position).\n * Useful when dismissing the keyboard to open a bottom sheet — prevents visual disruption\n * while the sheet is visible.\n *\n * Default is `false`.\n */\n freeze?: boolean | SharedValue<boolean>;\n /**\n * A shared value representing additional padding from external elements\n * (e.g., a growing multiline `TextInput` in a `KeyboardStickyView`).\n *\n * When this value changes:\n * - The scrollable range is always extended/contracted (via `contentInset`).\n * - The scroll position is conditionally adjusted based on `keyboardLiftBehavior`.\n *\n * Default is `undefined` (no extra padding).\n */\n extraContentPadding?: SharedValue<number>;\n /**\n * When `true`, applies a runtime workaround for a React Native 0.81+ bug\n * where the ScrollView's `contentInset` area does not respond to touch/scroll\n * gestures (facebook/react-native#54123).\n *\n * This uses Objective-C runtime method swizzling on the ScrollView's container\n * view, which is inherently fragile. Only enable if you are affected by the\n * upstream bug and understand the risks.\n *\n * iOS only. Default is `false`.\n */\n applyWorkaroundForContentInsetHitTestBug?: boolean;\n /**\n * A shared value representing a minimum inset floor (in pixels).\n *\n * When set, the total bottom padding is computed as:\n * `max(blankSpace, keyboardPadding + extraContentPadding)`\n *\n * This means the keyboard \"absorbs\" into the minimum padding rather than adding to it:\n * - When `blankSpace >= keyboard + extraContentPadding`: content does NOT move on keyboard open/close.\n * - When `blankSpace < keyboard + extraContentPadding`: content moves, but only by the excess amount.\n *\n * Useful in AI chat apps where a sent message needs space below it (to push it to the top\n * of the viewport) while the AI response streams in, without that space causing extra movement\n * when the keyboard opens.\n *\n * Default is `undefined` (equivalent to `0` — no minimum floor).\n */\n blankSpace?: SharedValue<number>;\n} & ScrollViewProps;\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import type {\n AnimatedScrollViewComponent,\n ScrollViewContentInsets,\n} from \"../ScrollViewWithBottomPadding\";\nimport type { KeyboardLiftBehavior } from \"./useChatKeyboard/types\";\nimport type { ScrollViewProps } from \"react-native\";\nimport type { SharedValue } from \"react-native-reanimated\";\n\nexport type KeyboardChatScrollViewProps = {\n /** Custom component for `ScrollView`. Default is `ScrollView`. */\n ScrollViewComponent?: AnimatedScrollViewComponent;\n /** Whether list are using `inverted` prop. Default is `false`. */\n inverted?: boolean;\n /**\n * The distance between the bottom of the screen and the `ScrollView`.\n * When the keyboard appears, the `ScrollView` will only push content by the effective\n * distance (`keyboardHeight - offset`) instead of the full keyboard height.\n *\n * Useful when the input is not at the very bottom of the screen (e.g., above safe area, above\n * bottom tabs, etc. - in this case offset should be equal to the height of the elements between\n * `ScrollView` and bottom of the screen).\n *\n * Default is `0`.\n */\n offset?: number;\n /**\n * Determines how the chat content should behave when the keyboard appears, specifically whether\n * the scroll view should automatically lift its content to keep it visible above the keyboard.\n *\n * Possible values:\n * - `'always'`: The content always lifts along with the keyboard, ensuring the messages from the bottom part of screen\n * remain visible regardless of the current scroll position. This is the default behavior for most chat applications (used in Telegram).\n * - `'whenAtEnd'`: The content lifts only if the scroll view is at the end (i.e., the last message\n * is visible or near the bottom). This prevents unnecessary adjustments when the user is scrolling\n * through older messages (ChatGPT mobile app behavior).\n * - `'persistent'`: The content always lifts when the keyboard appears (similar to `'always'`), but\n * does not reset (lower) when the keyboard hides. This mimics behaviors where the view remains adjusted\n * after keyboard dismissal to maintain focus on the latest content without shifting back (Claude mobile app behavior).\n * - `'never'`: The content does not lift at all when the keyboard appears. Use this for scenarios\n * when you don't want to disturb user attention with animations (Perplexity mobile app behavior).\n *\n * Default is `'always'`.\n */\n keyboardLiftBehavior?: KeyboardLiftBehavior;\n /**\n * When `true`, freezes all keyboard-driven layout changes (padding, content offset, scroll position).\n * Useful when dismissing the keyboard to open a bottom sheet — prevents visual disruption\n * while the sheet is visible.\n *\n * Default is `false`.\n */\n freeze?: boolean | SharedValue<boolean>;\n /**\n * A shared value representing additional padding from external elements\n * (e.g., a growing multiline `TextInput` in a `KeyboardStickyView`).\n *\n * When this value changes:\n * - The scrollable range is always extended/contracted (via `contentInset`).\n * - The scroll position is conditionally adjusted based on `keyboardLiftBehavior`.\n *\n * Default is `undefined` (no extra padding).\n */\n extraContentPadding?: SharedValue<number>;\n /**\n * When `true`, applies a runtime workaround for a React Native 0.81+ bug\n * where the ScrollView's `contentInset` area does not respond to touch/scroll\n * gestures (facebook/react-native#54123).\n *\n * This uses Objective-C runtime method swizzling on the ScrollView's container\n * view, which is inherently fragile. Only enable if you are affected by the\n * upstream bug and understand the risks.\n *\n * iOS only. Default is `false`.\n */\n applyWorkaroundForContentInsetHitTestBug?: boolean;\n /**\n * A shared value representing a minimum inset floor (in pixels).\n *\n * When set, the total bottom padding is computed as:\n * `max(blankSpace, keyboardPadding + extraContentPadding)`\n *\n * This means the keyboard \"absorbs\" into the minimum padding rather than adding to it:\n * - When `blankSpace >= keyboard + extraContentPadding`: content does NOT move on keyboard open/close.\n * - When `blankSpace < keyboard + extraContentPadding`: content moves, but only by the excess amount.\n *\n * Useful in AI chat apps where a sent message needs space below it (to push it to the top\n * of the viewport) while the AI response streams in, without that space causing extra movement\n * when the keyboard opens.\n *\n * Default is `undefined` (equivalent to `0` — no minimum floor).\n */\n blankSpace?: SharedValue<number>;\n /**\n * Fires whenever the effective content inset changes — the static `contentInset`\n * prop combined with the dynamic keyboard-driven padding.\n *\n * Useful on Android, where the synthetic content inset is not reflected in the native\n * `onScroll` event payload. Consumers such as virtualized lists computing their own\n * `scrollToEnd` target can use this to track the current inset alongside scroll offsets.\n */\n onContentInsetChange?: (insets: ScrollViewContentInsets) => void;\n /**\n * Fires whenever the visibility of the content \"end\" changes — both when the user\n * arrives at the end and when they leave it. The boolean parameter reflects the new state.\n *\n * For non-inverted lists, the \"end\" is the bottom of the content. For inverted lists\n * (`inverted={true}`), the \"end\" is the top of the scroll view, where the latest messages\n * are rendered. The same internal detection drives `keyboardLiftBehavior=\"whenAtEnd\"`.\n *\n * The callback can be either a plain JS function or a Reanimated worklet — the type is\n * detected automatically. Worklets run on the UI thread; plain functions are dispatched\n * via `runOnJS`.\n *\n * Fires once on mount with the initial state (after the scroll view has been measured).\n */\n onEndVisible?: (visible: boolean) => void;\n} & ScrollViewProps;\n"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useEndVisible = void 0;
7
+ var _react = require("react");
8
+ var _reactNativeReanimated = require("react-native-reanimated");
9
+ var _helpers = require("./useChatKeyboard/helpers");
10
+ const hasWorkletHash = value => typeof value === "function" && !!value.__workletHash;
11
+ const useEndVisible = ({
12
+ scroll,
13
+ layout,
14
+ size,
15
+ inverted,
16
+ onEndVisible
17
+ }) => {
18
+ const isWorklet = (0, _react.useMemo)(() => hasWorkletHash(onEndVisible), [onEndVisible]);
19
+ const isAtEnd = (0, _reactNativeReanimated.useDerivedValue)(() => {
20
+ // Wait until the scroll view has been measured to avoid a spurious initial
21
+ // `true` on a (0,0,0) layout (the helper would otherwise treat unmeasured
22
+ // state as "at end" because 0 + 0 >= 0 - threshold).
23
+ if (layout.value.height === 0 || size.value.height === 0) {
24
+ return null;
25
+ }
26
+ return (0, _helpers.isScrollAtEnd)(scroll.value, layout.value.height, size.value.height, inverted);
27
+ });
28
+ (0, _reactNativeReanimated.useAnimatedReaction)(() => isAtEnd.value, (current, previous) => {
29
+ if (current === null || current === previous || !onEndVisible) {
30
+ return;
31
+ }
32
+ if (isWorklet) {
33
+ onEndVisible(current);
34
+ } else {
35
+ (0, _reactNativeReanimated.runOnJS)(onEndVisible)(current);
36
+ }
37
+ }, [onEndVisible, isWorklet, inverted]);
38
+ };
39
+ exports.useEndVisible = useEndVisible;
40
+ //# sourceMappingURL=useEndVisible.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","require","_reactNativeReanimated","_helpers","hasWorkletHash","value","__workletHash","useEndVisible","scroll","layout","size","inverted","onEndVisible","isWorklet","useMemo","isAtEnd","useDerivedValue","height","isScrollAtEnd","useAnimatedReaction","current","previous","runOnJS","exports"],"sources":["useEndVisible.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport {\n runOnJS,\n useAnimatedReaction,\n useDerivedValue,\n} from \"react-native-reanimated\";\n\nimport { isScrollAtEnd } from \"./useChatKeyboard/helpers\";\n\nimport type { SharedValue } from \"react-native-reanimated\";\n\ntype EndVisibleCallback = (visible: boolean) => void;\n\ntype Options = {\n scroll: SharedValue<number>;\n layout: SharedValue<{ width: number; height: number }>;\n size: SharedValue<{ width: number; height: number }>;\n inverted: boolean;\n onEndVisible?: EndVisibleCallback;\n};\n\nconst hasWorkletHash = (value: unknown): boolean =>\n typeof value === \"function\" &&\n !!(value as unknown as Record<string, unknown>).__workletHash;\n\nexport const useEndVisible = ({\n scroll,\n layout,\n size,\n inverted,\n onEndVisible,\n}: Options) => {\n const isWorklet = useMemo(() => hasWorkletHash(onEndVisible), [onEndVisible]);\n\n const isAtEnd = useDerivedValue(() => {\n // Wait until the scroll view has been measured to avoid a spurious initial\n // `true` on a (0,0,0) layout (the helper would otherwise treat unmeasured\n // state as \"at end\" because 0 + 0 >= 0 - threshold).\n if (layout.value.height === 0 || size.value.height === 0) {\n return null;\n }\n\n return isScrollAtEnd(\n scroll.value,\n layout.value.height,\n size.value.height,\n inverted,\n );\n });\n\n useAnimatedReaction(\n () => isAtEnd.value,\n (current, previous) => {\n if (current === null || current === previous || !onEndVisible) {\n return;\n }\n\n if (isWorklet) {\n onEndVisible(current);\n } else {\n runOnJS(onEndVisible)(current);\n }\n },\n [onEndVisible, isWorklet, inverted],\n );\n};\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,sBAAA,GAAAD,OAAA;AAMA,IAAAE,QAAA,GAAAF,OAAA;AAcA,MAAMG,cAAc,GAAIC,KAAc,IACpC,OAAOA,KAAK,KAAK,UAAU,IAC3B,CAAC,CAAEA,KAAK,CAAwCC,aAAa;AAExD,MAAMC,aAAa,GAAGA,CAAC;EAC5BC,MAAM;EACNC,MAAM;EACNC,IAAI;EACJC,QAAQ;EACRC;AACO,CAAC,KAAK;EACb,MAAMC,SAAS,GAAG,IAAAC,cAAO,EAAC,MAAMV,cAAc,CAACQ,YAAY,CAAC,EAAE,CAACA,YAAY,CAAC,CAAC;EAE7E,MAAMG,OAAO,GAAG,IAAAC,sCAAe,EAAC,MAAM;IACpC;IACA;IACA;IACA,IAAIP,MAAM,CAACJ,KAAK,CAACY,MAAM,KAAK,CAAC,IAAIP,IAAI,CAACL,KAAK,CAACY,MAAM,KAAK,CAAC,EAAE;MACxD,OAAO,IAAI;IACb;IAEA,OAAO,IAAAC,sBAAa,EAClBV,MAAM,CAACH,KAAK,EACZI,MAAM,CAACJ,KAAK,CAACY,MAAM,EACnBP,IAAI,CAACL,KAAK,CAACY,MAAM,EACjBN,QACF,CAAC;EACH,CAAC,CAAC;EAEF,IAAAQ,0CAAmB,EACjB,MAAMJ,OAAO,CAACV,KAAK,EACnB,CAACe,OAAO,EAAEC,QAAQ,KAAK;IACrB,IAAID,OAAO,KAAK,IAAI,IAAIA,OAAO,KAAKC,QAAQ,IAAI,CAACT,YAAY,EAAE;MAC7D;IACF;IAEA,IAAIC,SAAS,EAAE;MACbD,YAAY,CAACQ,OAAO,CAAC;IACvB,CAAC,MAAM;MACL,IAAAE,8BAAO,EAACV,YAAY,CAAC,CAACQ,OAAO,CAAC;IAChC;EACF,CAAC,EACD,CAACR,YAAY,EAAEC,SAAS,EAAEF,QAAQ,CACpC,CAAC;AACH,CAAC;AAACY,OAAA,CAAAhB,aAAA,GAAAA,aAAA","ignoreList":[]}
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
- var _reactNative = require("react-native");
8
+ var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
9
9
  var _hooks = require("../../hooks");
10
10
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
11
11
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
@@ -35,21 +35,17 @@ const KeyboardStickyView = /*#__PURE__*/(0, _react.forwardRef)(({
35
35
  const {
36
36
  height,
37
37
  progress
38
- } = (0, _hooks.useKeyboardAnimation)();
39
- const offset = progress.interpolate({
40
- inputRange: [0, 1],
41
- outputRange: [closed, opened]
42
- });
43
- const styles = (0, _react.useMemo)(() => {
44
- const disabled = _reactNative.Animated.add(_reactNative.Animated.multiply(height, 0), closed);
45
- const active = _reactNative.Animated.add(height, offset);
46
- return [{
38
+ } = (0, _hooks.useReanimatedKeyboardAnimation)();
39
+ const stickyViewStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
40
+ const offset = (0, _reactNativeReanimated.interpolate)(progress.value, [0, 1], [closed, opened]);
41
+ return {
47
42
  transform: [{
48
- translateY: enabled ? active : disabled
43
+ translateY: enabled ? height.value + offset : closed
49
44
  }]
50
- }, style];
51
- }, [closed, enabled, height, offset, style]);
52
- return /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, _extends({
45
+ };
46
+ }, [closed, opened, enabled]);
47
+ const styles = (0, _react.useMemo)(() => [style, stickyViewStyle], [style, stickyViewStyle]);
48
+ return /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, _extends({
53
49
  ref: ref,
54
50
  style: styles
55
51
  }, props), children);
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_hooks","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","KeyboardStickyView","forwardRef","children","offset","closed","opened","style","enabled","props","ref","height","progress","useKeyboardAnimation","interpolate","inputRange","outputRange","styles","useMemo","disabled","Animated","add","multiply","active","transform","translateY","createElement","View","_default","exports"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useMemo } from \"react\";\nimport { Animated } from \"react-native\";\n\nimport { useKeyboardAnimation } from \"../../hooks\";\n\nimport type { View, ViewProps } from \"react-native\";\n\nexport type KeyboardStickyViewProps = {\n /**\n * Specify additional offset to the view for given keyboard state.\n */\n offset?: {\n /**\n * Adds additional `translateY` when keyboard is close. By default `0`.\n */\n closed?: number;\n /**\n * Adds additional `translateY` when keyboard is open. By default `0`.\n */\n opened?: number;\n };\n\n /** Controls whether this `KeyboardStickyView` instance should take effect. Default is `true`. */\n enabled?: boolean;\n} & ViewProps;\n\n/**\n * A View component that sticks to the keyboard and moves with it when it appears or disappears.\n * The view can be configured with custom offsets for both closed and open keyboard states.\n *\n * @returns An animated View component that sticks to the keyboard.\n * @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/components/keyboard-sticky-view|Documentation} page for more details.\n * @example\n * ```tsx\n * <KeyboardStickyView offset={{ closed: 0, opened: 20 }}>\n * <Button title=\"Submit\" />\n * </KeyboardStickyView>\n * ```\n */\nconst KeyboardStickyView = forwardRef<\n View,\n React.PropsWithChildren<KeyboardStickyViewProps>\n>(\n (\n {\n children,\n offset: { closed = 0, opened = 0 } = {},\n style,\n enabled = true,\n ...props\n },\n ref,\n ) => {\n const { height, progress } = useKeyboardAnimation();\n\n const offset = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [closed, opened],\n });\n\n const styles = useMemo(() => {\n const disabled = Animated.add(Animated.multiply(height, 0), closed);\n const active = Animated.add(height, offset);\n\n return [\n {\n transform: [{ translateY: enabled ? active : disabled }],\n },\n style,\n ];\n }, [closed, enabled, height, offset, style]);\n\n return (\n <Animated.View ref={ref} style={styles} {...props}>\n {children}\n </Animated.View>\n );\n },\n);\n\nexport default KeyboardStickyView;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNativeReanimated","_hooks","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","KeyboardStickyView","forwardRef","children","offset","closed","opened","style","enabled","props","ref","height","progress","useReanimatedKeyboardAnimation","stickyViewStyle","useAnimatedStyle","interpolate","value","transform","translateY","styles","useMemo","createElement","View","_default","exports"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useMemo } from \"react\";\nimport Reanimated, {\n interpolate,\n useAnimatedStyle,\n} from \"react-native-reanimated\";\n\nimport { useReanimatedKeyboardAnimation } from \"../../hooks\";\n\nimport type { View, ViewProps } from \"react-native\";\n\nexport type KeyboardStickyViewProps = {\n /**\n * Specify additional offset to the view for given keyboard state.\n */\n offset?: {\n /**\n * Adds additional `translateY` when keyboard is close. By default `0`.\n */\n closed?: number;\n /**\n * Adds additional `translateY` when keyboard is open. By default `0`.\n */\n opened?: number;\n };\n\n /** Controls whether this `KeyboardStickyView` instance should take effect. Default is `true`. */\n enabled?: boolean;\n} & ViewProps;\n\n/**\n * A View component that sticks to the keyboard and moves with it when it appears or disappears.\n * The view can be configured with custom offsets for both closed and open keyboard states.\n *\n * @returns An animated View component that sticks to the keyboard.\n * @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/components/keyboard-sticky-view|Documentation} page for more details.\n * @example\n * ```tsx\n * <KeyboardStickyView offset={{ closed: 0, opened: 20 }}>\n * <Button title=\"Submit\" />\n * </KeyboardStickyView>\n * ```\n */\nconst KeyboardStickyView = forwardRef<\n View,\n React.PropsWithChildren<KeyboardStickyViewProps>\n>(\n (\n {\n children,\n offset: { closed = 0, opened = 0 } = {},\n style,\n enabled = true,\n ...props\n },\n ref,\n ) => {\n const { height, progress } = useReanimatedKeyboardAnimation();\n\n const stickyViewStyle = useAnimatedStyle(() => {\n const offset = interpolate(progress.value, [0, 1], [closed, opened]);\n\n return {\n transform: [{ translateY: enabled ? height.value + offset : closed }],\n };\n }, [closed, opened, enabled]);\n\n const styles = useMemo(\n () => [style, stickyViewStyle],\n [style, stickyViewStyle],\n );\n\n return (\n <Reanimated.View ref={ref} style={styles} {...props}>\n {children}\n </Reanimated.View>\n );\n },\n);\n\nexport default KeyboardStickyView;\n"],"mappings":"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","ignoreList":[]}
@@ -23,26 +23,47 @@ const ScrollViewWithBottomPadding = /*#__PURE__*/(0, _react.forwardRef)(({
23
23
  inverted,
24
24
  contentOffsetY,
25
25
  applyWorkaroundForContentInsetHitTestBug,
26
+ onContentInsetChange,
26
27
  children,
27
28
  ...rest
28
29
  }, ref) => {
29
30
  const prevContentOffsetY = (0, _reactNativeReanimated.useSharedValue)(null);
31
+ const insets = (0, _reactNativeReanimated.useDerivedValue)(() => {
32
+ const dynamicTop = inverted ? bottomPadding.value : 0;
33
+ const dynamicBottom = !inverted ? bottomPadding.value : 0;
34
+ return {
35
+ dynamic: {
36
+ top: dynamicTop,
37
+ bottom: dynamicBottom
38
+ },
39
+ effective: {
40
+ top: dynamicTop + ((contentInset === null || contentInset === void 0 ? void 0 : contentInset.top) || 0),
41
+ bottom: dynamicBottom + ((contentInset === null || contentInset === void 0 ? void 0 : contentInset.bottom) || 0),
42
+ left: (contentInset === null || contentInset === void 0 ? void 0 : contentInset.left) || 0,
43
+ right: (contentInset === null || contentInset === void 0 ? void 0 : contentInset.right) || 0
44
+ }
45
+ };
46
+ }, [inverted, contentInset === null || contentInset === void 0 ? void 0 : contentInset.top, contentInset === null || contentInset === void 0 ? void 0 : contentInset.bottom, contentInset === null || contentInset === void 0 ? void 0 : contentInset.left, contentInset === null || contentInset === void 0 ? void 0 : contentInset.right]);
47
+ (0, _reactNativeReanimated.useAnimatedReaction)(() => insets.value.effective, (current, previous) => {
48
+ if (!onContentInsetChange) {
49
+ return;
50
+ }
51
+ if (previous && current.top === previous.top && current.bottom === previous.bottom && current.left === previous.left && current.right === previous.right) {
52
+ return;
53
+ }
54
+ (0, _reactNativeReanimated.runOnJS)(onContentInsetChange)(current);
55
+ }, [onContentInsetChange]);
30
56
  const animatedProps = (0, _reactNativeReanimated.useAnimatedProps)(() => {
31
- const insetTop = inverted ? bottomPadding.value : 0;
32
- const insetBottom = !inverted ? bottomPadding.value : 0;
33
- const bottom = insetBottom + ((contentInset === null || contentInset === void 0 ? void 0 : contentInset.bottom) || 0);
34
- const top = insetTop + ((contentInset === null || contentInset === void 0 ? void 0 : contentInset.top) || 0);
57
+ const {
58
+ dynamic,
59
+ effective
60
+ } = insets.value;
35
61
  const indicatorPadding = scrollIndicatorPadding ?? bottomPadding;
36
62
  const indicatorTop = (inverted ? indicatorPadding.value : 0) + ((scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.top) || 0);
37
63
  const indicatorBottom = (!inverted ? indicatorPadding.value : 0) + ((scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.bottom) || 0);
38
64
  const result = {
39
65
  // iOS prop
40
- contentInset: {
41
- bottom: bottom,
42
- top: top,
43
- right: contentInset === null || contentInset === void 0 ? void 0 : contentInset.right,
44
- left: contentInset === null || contentInset === void 0 ? void 0 : contentInset.left
45
- },
66
+ contentInset: effective,
46
67
  scrollIndicatorInsets: {
47
68
  bottom: indicatorBottom,
48
69
  top: indicatorTop,
@@ -50,8 +71,8 @@ const ScrollViewWithBottomPadding = /*#__PURE__*/(0, _react.forwardRef)(({
50
71
  left: scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.left
51
72
  },
52
73
  // Android prop
53
- contentInsetBottom: insetBottom,
54
- contentInsetTop: insetTop
74
+ contentInsetBottom: dynamic.bottom,
75
+ contentInsetTop: dynamic.top
55
76
  };
56
77
  if (contentOffsetY) {
57
78
  const curr = contentOffsetY.value;
@@ -65,7 +86,7 @@ const ScrollViewWithBottomPadding = /*#__PURE__*/(0, _react.forwardRef)(({
65
86
  }
66
87
  }
67
88
  return result;
68
- }, [contentInset === null || contentInset === void 0 ? void 0 : contentInset.bottom, contentInset === null || contentInset === void 0 ? void 0 : contentInset.top, contentInset === null || contentInset === void 0 ? void 0 : contentInset.right, contentInset === null || contentInset === void 0 ? void 0 : contentInset.left, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.bottom, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.top, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.right, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.left, inverted, contentOffsetY]);
89
+ }, [scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.bottom, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.top, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.right, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.left, inverted, contentOffsetY]);
69
90
  return /*#__PURE__*/_react.default.createElement(ReanimatedClippingScrollView, {
70
91
  animatedProps: animatedProps,
71
92
  applyWorkaroundForContentInsetHitTestBug: applyWorkaroundForContentInsetHitTestBug,
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","_bindings","_styles","_interopRequireDefault","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","OS","Platform","ReanimatedClippingScrollView","Reanimated","createAnimatedComponent","ClippingScrollView","ScrollViewWithBottomPadding","forwardRef","ScrollViewComponent","bottomPadding","scrollIndicatorPadding","contentInset","scrollIndicatorInsets","inverted","contentOffsetY","applyWorkaroundForContentInsetHitTestBug","children","rest","ref","prevContentOffsetY","useSharedValue","animatedProps","useAnimatedProps","insetTop","value","insetBottom","bottom","top","indicatorPadding","indicatorTop","indicatorBottom","result","right","left","contentInsetBottom","contentInsetTop","curr","contentOffset","x","y","createElement","style","styles","container","_default","exports"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef } from \"react\";\nimport { Platform } from \"react-native\";\nimport Reanimated, {\n useAnimatedProps,\n useSharedValue,\n} from \"react-native-reanimated\";\n\nimport { ClippingScrollView } from \"../../bindings\";\n\nimport styles from \"./styles\";\n\nimport type { ScrollViewProps } from \"react-native\";\nimport type { SharedValue } from \"react-native-reanimated\";\n\nconst OS = Platform.OS;\nconst ReanimatedClippingScrollView =\n OS === \"android\"\n ? Reanimated.createAnimatedComponent(ClippingScrollView)\n : ClippingScrollView;\n\ntype AnimatedScrollViewProps = React.ComponentProps<\n typeof Reanimated.ScrollView\n>;\n\nexport type AnimatedScrollViewComponent = React.ForwardRefExoticComponent<\n AnimatedScrollViewProps & React.RefAttributes<Reanimated.ScrollView>\n>;\n\ntype ScrollViewWithBottomPaddingProps = {\n ScrollViewComponent: AnimatedScrollViewComponent;\n children?: React.ReactNode;\n inverted?: boolean;\n bottomPadding: SharedValue<number>;\n /** Padding for scroll indicator insets (excludes blankSpace). Falls back to bottomPadding when not provided. */\n scrollIndicatorPadding?: SharedValue<number>;\n /** Absolute Y content offset (iOS only, for KeyboardChatScrollView). */\n contentOffsetY?: SharedValue<number>;\n applyWorkaroundForContentInsetHitTestBug?: boolean;\n} & ScrollViewProps;\n\nconst ScrollViewWithBottomPadding = forwardRef<\n Reanimated.ScrollView,\n ScrollViewWithBottomPaddingProps\n>(\n (\n {\n ScrollViewComponent,\n bottomPadding,\n scrollIndicatorPadding,\n contentInset,\n scrollIndicatorInsets,\n inverted,\n contentOffsetY,\n applyWorkaroundForContentInsetHitTestBug,\n children,\n ...rest\n },\n ref,\n ) => {\n const prevContentOffsetY = useSharedValue<number | null>(null);\n\n const animatedProps = useAnimatedProps(() => {\n const insetTop = inverted ? bottomPadding.value : 0;\n const insetBottom = !inverted ? bottomPadding.value : 0;\n const bottom = insetBottom + (contentInset?.bottom || 0);\n const top = insetTop + (contentInset?.top || 0);\n\n const indicatorPadding = scrollIndicatorPadding ?? bottomPadding;\n const indicatorTop =\n (inverted ? indicatorPadding.value : 0) +\n (scrollIndicatorInsets?.top || 0);\n const indicatorBottom =\n (!inverted ? indicatorPadding.value : 0) +\n (scrollIndicatorInsets?.bottom || 0);\n\n const result: Record<string, unknown> = {\n // iOS prop\n contentInset: {\n bottom: bottom,\n top: top,\n right: contentInset?.right,\n left: contentInset?.left,\n },\n scrollIndicatorInsets: {\n bottom: indicatorBottom,\n top: indicatorTop,\n right: scrollIndicatorInsets?.right,\n left: scrollIndicatorInsets?.left,\n },\n // Android prop\n contentInsetBottom: insetBottom,\n contentInsetTop: insetTop,\n };\n\n if (contentOffsetY) {\n const curr = contentOffsetY.value;\n\n if (curr !== prevContentOffsetY.value) {\n // eslint-disable-next-line react-compiler/react-compiler\n prevContentOffsetY.value = curr;\n result.contentOffset = { x: 0, y: curr };\n }\n }\n\n return result;\n }, [\n contentInset?.bottom,\n contentInset?.top,\n contentInset?.right,\n contentInset?.left,\n scrollIndicatorInsets?.bottom,\n scrollIndicatorInsets?.top,\n scrollIndicatorInsets?.right,\n scrollIndicatorInsets?.left,\n inverted,\n contentOffsetY,\n ]);\n\n return (\n <ReanimatedClippingScrollView\n animatedProps={animatedProps}\n applyWorkaroundForContentInsetHitTestBug={\n applyWorkaroundForContentInsetHitTestBug\n }\n style={styles.container}\n >\n <ScrollViewComponent ref={ref} animatedProps={animatedProps} {...rest}>\n {children}\n </ScrollViewComponent>\n </ReanimatedClippingScrollView>\n );\n },\n);\n\nexport default ScrollViewWithBottomPadding;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","_bindings","_styles","_interopRequireDefault","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","OS","Platform","ReanimatedClippingScrollView","Reanimated","createAnimatedComponent","ClippingScrollView","ScrollViewWithBottomPadding","forwardRef","ScrollViewComponent","bottomPadding","scrollIndicatorPadding","contentInset","scrollIndicatorInsets","inverted","contentOffsetY","applyWorkaroundForContentInsetHitTestBug","onContentInsetChange","children","rest","ref","prevContentOffsetY","useSharedValue","insets","useDerivedValue","dynamicTop","value","dynamicBottom","dynamic","top","bottom","effective","left","right","useAnimatedReaction","current","previous","runOnJS","animatedProps","useAnimatedProps","indicatorPadding","indicatorTop","indicatorBottom","result","contentInsetBottom","contentInsetTop","curr","contentOffset","x","y","createElement","style","styles","container","_default","exports"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef } from \"react\";\nimport { Platform } from \"react-native\";\nimport Reanimated, {\n runOnJS,\n useAnimatedProps,\n useAnimatedReaction,\n useDerivedValue,\n useSharedValue,\n} from \"react-native-reanimated\";\n\nimport { ClippingScrollView } from \"../../bindings\";\n\nimport styles from \"./styles\";\n\nimport type { ScrollViewProps } from \"react-native\";\nimport type { SharedValue } from \"react-native-reanimated\";\n\nconst OS = Platform.OS;\nconst ReanimatedClippingScrollView =\n OS === \"android\"\n ? Reanimated.createAnimatedComponent(ClippingScrollView)\n : ClippingScrollView;\n\ntype AnimatedScrollViewProps = React.ComponentProps<\n typeof Reanimated.ScrollView\n>;\n\nexport type AnimatedScrollViewComponent = React.ForwardRefExoticComponent<\n AnimatedScrollViewProps & React.RefAttributes<Reanimated.ScrollView>\n>;\n\nexport type ScrollViewContentInsets = {\n top: number;\n bottom: number;\n left: number;\n right: number;\n};\n\ntype ScrollViewWithBottomPaddingProps = {\n ScrollViewComponent: AnimatedScrollViewComponent;\n children?: React.ReactNode;\n inverted?: boolean;\n bottomPadding: SharedValue<number>;\n /** Padding for scroll indicator insets (excludes blankSpace). Falls back to bottomPadding when not provided. */\n scrollIndicatorPadding?: SharedValue<number>;\n /** Absolute Y content offset (iOS only, for KeyboardChatScrollView). */\n contentOffsetY?: SharedValue<number>;\n applyWorkaroundForContentInsetHitTestBug?: boolean;\n /**\n * Fires whenever the effective content inset changes (combines the static `contentInset`\n * prop with the dynamic keyboard-driven padding). Useful on Android where the synthetic\n * inset is not reflected in `onScroll` events.\n */\n onContentInsetChange?: (insets: ScrollViewContentInsets) => void;\n} & ScrollViewProps;\n\nconst ScrollViewWithBottomPadding = forwardRef<\n Reanimated.ScrollView,\n ScrollViewWithBottomPaddingProps\n>(\n (\n {\n ScrollViewComponent,\n bottomPadding,\n scrollIndicatorPadding,\n contentInset,\n scrollIndicatorInsets,\n inverted,\n contentOffsetY,\n applyWorkaroundForContentInsetHitTestBug,\n onContentInsetChange,\n children,\n ...rest\n },\n ref,\n ) => {\n const prevContentOffsetY = useSharedValue<number | null>(null);\n\n const insets = useDerivedValue(() => {\n const dynamicTop = inverted ? bottomPadding.value : 0;\n const dynamicBottom = !inverted ? bottomPadding.value : 0;\n\n return {\n dynamic: {\n top: dynamicTop,\n bottom: dynamicBottom,\n },\n effective: {\n top: dynamicTop + (contentInset?.top || 0),\n bottom: dynamicBottom + (contentInset?.bottom || 0),\n left: contentInset?.left || 0,\n right: contentInset?.right || 0,\n } as ScrollViewContentInsets,\n };\n }, [\n inverted,\n contentInset?.top,\n contentInset?.bottom,\n contentInset?.left,\n contentInset?.right,\n ]);\n\n useAnimatedReaction(\n () => insets.value.effective,\n (current, previous) => {\n if (!onContentInsetChange) {\n return;\n }\n if (\n previous &&\n current.top === previous.top &&\n current.bottom === previous.bottom &&\n current.left === previous.left &&\n current.right === previous.right\n ) {\n return;\n }\n runOnJS(onContentInsetChange)(current);\n },\n [onContentInsetChange],\n );\n\n const animatedProps = useAnimatedProps(() => {\n const { dynamic, effective } = insets.value;\n\n const indicatorPadding = scrollIndicatorPadding ?? bottomPadding;\n const indicatorTop =\n (inverted ? indicatorPadding.value : 0) +\n (scrollIndicatorInsets?.top || 0);\n const indicatorBottom =\n (!inverted ? indicatorPadding.value : 0) +\n (scrollIndicatorInsets?.bottom || 0);\n\n const result: Record<string, unknown> = {\n // iOS prop\n contentInset: effective,\n scrollIndicatorInsets: {\n bottom: indicatorBottom,\n top: indicatorTop,\n right: scrollIndicatorInsets?.right,\n left: scrollIndicatorInsets?.left,\n },\n // Android prop\n contentInsetBottom: dynamic.bottom,\n contentInsetTop: dynamic.top,\n };\n\n if (contentOffsetY) {\n const curr = contentOffsetY.value;\n\n if (curr !== prevContentOffsetY.value) {\n // eslint-disable-next-line react-compiler/react-compiler\n prevContentOffsetY.value = curr;\n result.contentOffset = { x: 0, y: curr };\n }\n }\n\n return result;\n }, [\n scrollIndicatorInsets?.bottom,\n scrollIndicatorInsets?.top,\n scrollIndicatorInsets?.right,\n scrollIndicatorInsets?.left,\n inverted,\n contentOffsetY,\n ]);\n\n return (\n <ReanimatedClippingScrollView\n animatedProps={animatedProps}\n applyWorkaroundForContentInsetHitTestBug={\n applyWorkaroundForContentInsetHitTestBug\n }\n style={styles.container}\n >\n <ScrollViewComponent ref={ref} animatedProps={animatedProps} {...rest}>\n {children}\n </ScrollViewComponent>\n </ReanimatedClippingScrollView>\n );\n },\n);\n\nexport default ScrollViewWithBottomPadding;\n"],"mappings":"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","ignoreList":[]}
@@ -6,6 +6,7 @@ import Reanimated from "react-native-reanimated";
6
6
  import useCombinedRef from "../hooks/useCombinedRef";
7
7
  import ScrollViewWithBottomPadding from "../ScrollViewWithBottomPadding";
8
8
  import { useChatKeyboard } from "./useChatKeyboard";
9
+ import { useEndVisible } from "./useEndVisible";
9
10
  import { useExtraContentPadding } from "./useExtraContentPadding";
10
11
  const ZERO_CONTENT_PADDING = makeMutable(0);
11
12
  const ZERO_BLANK_SPACE = makeMutable(0);
@@ -21,6 +22,7 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({
21
22
  applyWorkaroundForContentInsetHitTestBug = false,
22
23
  onLayout: onLayoutProp,
23
24
  onContentSizeChange: onContentSizeChangeProp,
25
+ onEndVisible,
24
26
  ...rest
25
27
  }, ref) => {
26
28
  const scrollViewRef = useAnimatedRef();
@@ -56,6 +58,13 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({
56
58
  keyboardLiftBehavior,
57
59
  freeze: freezeSV
58
60
  });
61
+ useEndVisible({
62
+ scroll,
63
+ layout,
64
+ size,
65
+ inverted,
66
+ onEndVisible
67
+ });
59
68
  const totalPadding = useDerivedValue(() => Math.max(blankSpace.value, padding.value + extraContentPadding.value));
60
69
 
61
70
  // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace).
@@ -1 +1 @@
1
- {"version":3,"names":["React","forwardRef","useCallback","useMemo","StyleSheet","makeMutable","useAnimatedRef","useAnimatedStyle","useDerivedValue","Reanimated","useCombinedRef","ScrollViewWithBottomPadding","useChatKeyboard","useExtraContentPadding","ZERO_CONTENT_PADDING","ZERO_BLANK_SPACE","KeyboardChatScrollView","children","ScrollViewComponent","ScrollView","inverted","keyboardLiftBehavior","freeze","offset","extraContentPadding","blankSpace","applyWorkaroundForContentInsetHitTestBug","onLayout","onLayoutProp","onContentSizeChange","onContentSizeChangeProp","rest","ref","scrollViewRef","onRef","freezeSV","value","padding","currentHeight","contentOffsetY","scroll","layout","size","onLayoutInternal","onContentSizeChangeInternal","keyboardPadding","totalPadding","Math","max","indicatorPadding","e","w","h","commitStyle","transform","translateY","commit","styles","commitView","createElement","Fragment","_extends","bottomPadding","scrollIndicatorPadding","View","style","create","display","position"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useMemo } from \"react\";\nimport { StyleSheet } from \"react-native\";\nimport {\n makeMutable,\n useAnimatedRef,\n useAnimatedStyle,\n useDerivedValue,\n} from \"react-native-reanimated\";\nimport Reanimated from \"react-native-reanimated\";\n\nimport useCombinedRef from \"../hooks/useCombinedRef\";\nimport ScrollViewWithBottomPadding from \"../ScrollViewWithBottomPadding\";\n\nimport { useChatKeyboard } from \"./useChatKeyboard\";\nimport { useExtraContentPadding } from \"./useExtraContentPadding\";\n\nimport type { KeyboardChatScrollViewProps } from \"./types\";\nimport type { LayoutChangeEvent } from \"react-native\";\n\nconst ZERO_CONTENT_PADDING = makeMutable(0);\nconst ZERO_BLANK_SPACE = makeMutable(0);\n\nconst KeyboardChatScrollView = forwardRef<\n Reanimated.ScrollView,\n React.PropsWithChildren<KeyboardChatScrollViewProps>\n>(\n (\n {\n children,\n ScrollViewComponent = Reanimated.ScrollView,\n inverted = false,\n keyboardLiftBehavior = \"always\",\n freeze = false,\n offset = 0,\n extraContentPadding = ZERO_CONTENT_PADDING,\n blankSpace = ZERO_BLANK_SPACE,\n applyWorkaroundForContentInsetHitTestBug = false,\n onLayout: onLayoutProp,\n onContentSizeChange: onContentSizeChangeProp,\n ...rest\n },\n ref,\n ) => {\n const scrollViewRef = useAnimatedRef<Reanimated.ScrollView>();\n const onRef = useCombinedRef(ref, scrollViewRef);\n const freezeSV = useDerivedValue(() =>\n typeof freeze === \"boolean\" ? freeze : freeze.value,\n );\n const {\n padding,\n currentHeight,\n contentOffsetY,\n scroll,\n layout,\n size,\n onLayout: onLayoutInternal,\n onContentSizeChange: onContentSizeChangeInternal,\n } = useChatKeyboard(scrollViewRef, {\n inverted,\n keyboardLiftBehavior,\n freeze: freezeSV,\n offset,\n blankSpace,\n extraContentPadding,\n });\n\n useExtraContentPadding({\n scrollViewRef,\n extraContentPadding,\n keyboardPadding: padding,\n blankSpace,\n scroll,\n layout,\n size,\n contentOffsetY,\n inverted,\n keyboardLiftBehavior,\n freeze: freezeSV,\n });\n\n const totalPadding = useDerivedValue(() =>\n Math.max(blankSpace.value, padding.value + extraContentPadding.value),\n );\n\n // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace).\n // Apps that render into the unsafe area can supply a negative\n // scrollIndicatorInsets adjustment at the application layer.\n const indicatorPadding = useDerivedValue(\n () => padding.value + extraContentPadding.value,\n );\n\n const onLayout = useCallback(\n (e: LayoutChangeEvent) => {\n onLayoutInternal(e);\n onLayoutProp?.(e);\n },\n [onLayoutInternal, onLayoutProp],\n );\n\n const onContentSizeChange = useCallback(\n (w: number, h: number) => {\n onContentSizeChangeInternal(w, h);\n onContentSizeChangeProp?.(w, h);\n },\n [onContentSizeChangeInternal, onContentSizeChangeProp],\n );\n\n // Invisible view whose animated style changes every frame during keyboard\n // animation. On Fabric, this forces Reanimated to schedule a commit,\n // which flushes the scrollTo call in the same frame (fixing de-synchronization).\n // see https://github.com/software-mansion/react-native-reanimated/issues/9000\n const commitStyle = useAnimatedStyle(\n () => ({\n transform: [{ translateY: -currentHeight.value }],\n }),\n [],\n );\n const commit = useMemo(\n () => [styles.commitView, commitStyle],\n [commitStyle],\n );\n\n return (\n <>\n <ScrollViewWithBottomPadding\n ref={onRef}\n {...rest}\n applyWorkaroundForContentInsetHitTestBug={\n applyWorkaroundForContentInsetHitTestBug\n }\n bottomPadding={totalPadding}\n contentOffsetY={contentOffsetY}\n inverted={inverted}\n scrollIndicatorPadding={indicatorPadding}\n ScrollViewComponent={ScrollViewComponent}\n onContentSizeChange={onContentSizeChange}\n onLayout={onLayout}\n >\n {children}\n </ScrollViewWithBottomPadding>\n <Reanimated.View style={commit} />\n </>\n );\n },\n);\n\nconst styles = StyleSheet.create({\n commitView: {\n display: \"none\",\n position: \"absolute\",\n },\n});\n\nexport default KeyboardChatScrollView;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["React","forwardRef","useCallback","useMemo","StyleSheet","makeMutable","useAnimatedRef","useAnimatedStyle","useDerivedValue","Reanimated","useCombinedRef","ScrollViewWithBottomPadding","useChatKeyboard","useEndVisible","useExtraContentPadding","ZERO_CONTENT_PADDING","ZERO_BLANK_SPACE","KeyboardChatScrollView","children","ScrollViewComponent","ScrollView","inverted","keyboardLiftBehavior","freeze","offset","extraContentPadding","blankSpace","applyWorkaroundForContentInsetHitTestBug","onLayout","onLayoutProp","onContentSizeChange","onContentSizeChangeProp","onEndVisible","rest","ref","scrollViewRef","onRef","freezeSV","value","padding","currentHeight","contentOffsetY","scroll","layout","size","onLayoutInternal","onContentSizeChangeInternal","keyboardPadding","totalPadding","Math","max","indicatorPadding","e","w","h","commitStyle","transform","translateY","commit","styles","commitView","createElement","Fragment","_extends","bottomPadding","scrollIndicatorPadding","View","style","create","display","position"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useMemo } from \"react\";\nimport { StyleSheet } from \"react-native\";\nimport {\n makeMutable,\n useAnimatedRef,\n useAnimatedStyle,\n useDerivedValue,\n} from \"react-native-reanimated\";\nimport Reanimated from \"react-native-reanimated\";\n\nimport useCombinedRef from \"../hooks/useCombinedRef\";\nimport ScrollViewWithBottomPadding from \"../ScrollViewWithBottomPadding\";\n\nimport { useChatKeyboard } from \"./useChatKeyboard\";\nimport { useEndVisible } from \"./useEndVisible\";\nimport { useExtraContentPadding } from \"./useExtraContentPadding\";\n\nimport type { KeyboardChatScrollViewProps } from \"./types\";\nimport type { LayoutChangeEvent } from \"react-native\";\n\nconst ZERO_CONTENT_PADDING = makeMutable(0);\nconst ZERO_BLANK_SPACE = makeMutable(0);\n\nconst KeyboardChatScrollView = forwardRef<\n Reanimated.ScrollView,\n React.PropsWithChildren<KeyboardChatScrollViewProps>\n>(\n (\n {\n children,\n ScrollViewComponent = Reanimated.ScrollView,\n inverted = false,\n keyboardLiftBehavior = \"always\",\n freeze = false,\n offset = 0,\n extraContentPadding = ZERO_CONTENT_PADDING,\n blankSpace = ZERO_BLANK_SPACE,\n applyWorkaroundForContentInsetHitTestBug = false,\n onLayout: onLayoutProp,\n onContentSizeChange: onContentSizeChangeProp,\n onEndVisible,\n ...rest\n },\n ref,\n ) => {\n const scrollViewRef = useAnimatedRef<Reanimated.ScrollView>();\n const onRef = useCombinedRef(ref, scrollViewRef);\n const freezeSV = useDerivedValue(() =>\n typeof freeze === \"boolean\" ? freeze : freeze.value,\n );\n const {\n padding,\n currentHeight,\n contentOffsetY,\n scroll,\n layout,\n size,\n onLayout: onLayoutInternal,\n onContentSizeChange: onContentSizeChangeInternal,\n } = useChatKeyboard(scrollViewRef, {\n inverted,\n keyboardLiftBehavior,\n freeze: freezeSV,\n offset,\n blankSpace,\n extraContentPadding,\n });\n\n useExtraContentPadding({\n scrollViewRef,\n extraContentPadding,\n keyboardPadding: padding,\n blankSpace,\n scroll,\n layout,\n size,\n contentOffsetY,\n inverted,\n keyboardLiftBehavior,\n freeze: freezeSV,\n });\n\n useEndVisible({\n scroll,\n layout,\n size,\n inverted,\n onEndVisible,\n });\n\n const totalPadding = useDerivedValue(() =>\n Math.max(blankSpace.value, padding.value + extraContentPadding.value),\n );\n\n // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace).\n // Apps that render into the unsafe area can supply a negative\n // scrollIndicatorInsets adjustment at the application layer.\n const indicatorPadding = useDerivedValue(\n () => padding.value + extraContentPadding.value,\n );\n\n const onLayout = useCallback(\n (e: LayoutChangeEvent) => {\n onLayoutInternal(e);\n onLayoutProp?.(e);\n },\n [onLayoutInternal, onLayoutProp],\n );\n\n const onContentSizeChange = useCallback(\n (w: number, h: number) => {\n onContentSizeChangeInternal(w, h);\n onContentSizeChangeProp?.(w, h);\n },\n [onContentSizeChangeInternal, onContentSizeChangeProp],\n );\n\n // Invisible view whose animated style changes every frame during keyboard\n // animation. On Fabric, this forces Reanimated to schedule a commit,\n // which flushes the scrollTo call in the same frame (fixing de-synchronization).\n // see https://github.com/software-mansion/react-native-reanimated/issues/9000\n const commitStyle = useAnimatedStyle(\n () => ({\n transform: [{ translateY: -currentHeight.value }],\n }),\n [],\n );\n const commit = useMemo(\n () => [styles.commitView, commitStyle],\n [commitStyle],\n );\n\n return (\n <>\n <ScrollViewWithBottomPadding\n ref={onRef}\n {...rest}\n applyWorkaroundForContentInsetHitTestBug={\n applyWorkaroundForContentInsetHitTestBug\n }\n bottomPadding={totalPadding}\n contentOffsetY={contentOffsetY}\n inverted={inverted}\n scrollIndicatorPadding={indicatorPadding}\n ScrollViewComponent={ScrollViewComponent}\n onContentSizeChange={onContentSizeChange}\n onLayout={onLayout}\n >\n {children}\n </ScrollViewWithBottomPadding>\n <Reanimated.View style={commit} />\n </>\n );\n },\n);\n\nconst styles = StyleSheet.create({\n commitView: {\n display: \"none\",\n position: \"absolute\",\n },\n});\n\nexport default KeyboardChatScrollView;\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import type { AnimatedScrollViewComponent } from \"../ScrollViewWithBottomPadding\";\nimport type { KeyboardLiftBehavior } from \"./useChatKeyboard/types\";\nimport type { ScrollViewProps } from \"react-native\";\nimport type { SharedValue } from \"react-native-reanimated\";\n\nexport type KeyboardChatScrollViewProps = {\n /** Custom component for `ScrollView`. Default is `ScrollView`. */\n ScrollViewComponent?: AnimatedScrollViewComponent;\n /** Whether list are using `inverted` prop. Default is `false`. */\n inverted?: boolean;\n /**\n * The distance between the bottom of the screen and the `ScrollView`.\n * When the keyboard appears, the `ScrollView` will only push content by the effective\n * distance (`keyboardHeight - offset`) instead of the full keyboard height.\n *\n * Useful when the input is not at the very bottom of the screen (e.g., above safe area, above\n * bottom tabs, etc. - in this case offset should be equal to the height of the elements between\n * `ScrollView` and bottom of the screen).\n *\n * Default is `0`.\n */\n offset?: number;\n /**\n * Determines how the chat content should behave when the keyboard appears, specifically whether\n * the scroll view should automatically lift its content to keep it visible above the keyboard.\n *\n * Possible values:\n * - `'always'`: The content always lifts along with the keyboard, ensuring the messages from the bottom part of screen\n * remain visible regardless of the current scroll position. This is the default behavior for most chat applications (used in Telegram).\n * - `'whenAtEnd'`: The content lifts only if the scroll view is at the end (i.e., the last message\n * is visible or near the bottom). This prevents unnecessary adjustments when the user is scrolling\n * through older messages (ChatGPT mobile app behavior).\n * - `'persistent'`: The content always lifts when the keyboard appears (similar to `'always'`), but\n * does not reset (lower) when the keyboard hides. This mimics behaviors where the view remains adjusted\n * after keyboard dismissal to maintain focus on the latest content without shifting back (Claude mobile app behavior).\n * - `'never'`: The content does not lift at all when the keyboard appears. Use this for scenarios\n * when you don't want to disturb user attention with animations (Perplexity mobile app behavior).\n *\n * Default is `'always'`.\n */\n keyboardLiftBehavior?: KeyboardLiftBehavior;\n /**\n * When `true`, freezes all keyboard-driven layout changes (padding, content offset, scroll position).\n * Useful when dismissing the keyboard to open a bottom sheet — prevents visual disruption\n * while the sheet is visible.\n *\n * Default is `false`.\n */\n freeze?: boolean | SharedValue<boolean>;\n /**\n * A shared value representing additional padding from external elements\n * (e.g., a growing multiline `TextInput` in a `KeyboardStickyView`).\n *\n * When this value changes:\n * - The scrollable range is always extended/contracted (via `contentInset`).\n * - The scroll position is conditionally adjusted based on `keyboardLiftBehavior`.\n *\n * Default is `undefined` (no extra padding).\n */\n extraContentPadding?: SharedValue<number>;\n /**\n * When `true`, applies a runtime workaround for a React Native 0.81+ bug\n * where the ScrollView's `contentInset` area does not respond to touch/scroll\n * gestures (facebook/react-native#54123).\n *\n * This uses Objective-C runtime method swizzling on the ScrollView's container\n * view, which is inherently fragile. Only enable if you are affected by the\n * upstream bug and understand the risks.\n *\n * iOS only. Default is `false`.\n */\n applyWorkaroundForContentInsetHitTestBug?: boolean;\n /**\n * A shared value representing a minimum inset floor (in pixels).\n *\n * When set, the total bottom padding is computed as:\n * `max(blankSpace, keyboardPadding + extraContentPadding)`\n *\n * This means the keyboard \"absorbs\" into the minimum padding rather than adding to it:\n * - When `blankSpace >= keyboard + extraContentPadding`: content does NOT move on keyboard open/close.\n * - When `blankSpace < keyboard + extraContentPadding`: content moves, but only by the excess amount.\n *\n * Useful in AI chat apps where a sent message needs space below it (to push it to the top\n * of the viewport) while the AI response streams in, without that space causing extra movement\n * when the keyboard opens.\n *\n * Default is `undefined` (equivalent to `0` — no minimum floor).\n */\n blankSpace?: SharedValue<number>;\n} & ScrollViewProps;\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import type {\n AnimatedScrollViewComponent,\n ScrollViewContentInsets,\n} from \"../ScrollViewWithBottomPadding\";\nimport type { KeyboardLiftBehavior } from \"./useChatKeyboard/types\";\nimport type { ScrollViewProps } from \"react-native\";\nimport type { SharedValue } from \"react-native-reanimated\";\n\nexport type KeyboardChatScrollViewProps = {\n /** Custom component for `ScrollView`. Default is `ScrollView`. */\n ScrollViewComponent?: AnimatedScrollViewComponent;\n /** Whether list are using `inverted` prop. Default is `false`. */\n inverted?: boolean;\n /**\n * The distance between the bottom of the screen and the `ScrollView`.\n * When the keyboard appears, the `ScrollView` will only push content by the effective\n * distance (`keyboardHeight - offset`) instead of the full keyboard height.\n *\n * Useful when the input is not at the very bottom of the screen (e.g., above safe area, above\n * bottom tabs, etc. - in this case offset should be equal to the height of the elements between\n * `ScrollView` and bottom of the screen).\n *\n * Default is `0`.\n */\n offset?: number;\n /**\n * Determines how the chat content should behave when the keyboard appears, specifically whether\n * the scroll view should automatically lift its content to keep it visible above the keyboard.\n *\n * Possible values:\n * - `'always'`: The content always lifts along with the keyboard, ensuring the messages from the bottom part of screen\n * remain visible regardless of the current scroll position. This is the default behavior for most chat applications (used in Telegram).\n * - `'whenAtEnd'`: The content lifts only if the scroll view is at the end (i.e., the last message\n * is visible or near the bottom). This prevents unnecessary adjustments when the user is scrolling\n * through older messages (ChatGPT mobile app behavior).\n * - `'persistent'`: The content always lifts when the keyboard appears (similar to `'always'`), but\n * does not reset (lower) when the keyboard hides. This mimics behaviors where the view remains adjusted\n * after keyboard dismissal to maintain focus on the latest content without shifting back (Claude mobile app behavior).\n * - `'never'`: The content does not lift at all when the keyboard appears. Use this for scenarios\n * when you don't want to disturb user attention with animations (Perplexity mobile app behavior).\n *\n * Default is `'always'`.\n */\n keyboardLiftBehavior?: KeyboardLiftBehavior;\n /**\n * When `true`, freezes all keyboard-driven layout changes (padding, content offset, scroll position).\n * Useful when dismissing the keyboard to open a bottom sheet — prevents visual disruption\n * while the sheet is visible.\n *\n * Default is `false`.\n */\n freeze?: boolean | SharedValue<boolean>;\n /**\n * A shared value representing additional padding from external elements\n * (e.g., a growing multiline `TextInput` in a `KeyboardStickyView`).\n *\n * When this value changes:\n * - The scrollable range is always extended/contracted (via `contentInset`).\n * - The scroll position is conditionally adjusted based on `keyboardLiftBehavior`.\n *\n * Default is `undefined` (no extra padding).\n */\n extraContentPadding?: SharedValue<number>;\n /**\n * When `true`, applies a runtime workaround for a React Native 0.81+ bug\n * where the ScrollView's `contentInset` area does not respond to touch/scroll\n * gestures (facebook/react-native#54123).\n *\n * This uses Objective-C runtime method swizzling on the ScrollView's container\n * view, which is inherently fragile. Only enable if you are affected by the\n * upstream bug and understand the risks.\n *\n * iOS only. Default is `false`.\n */\n applyWorkaroundForContentInsetHitTestBug?: boolean;\n /**\n * A shared value representing a minimum inset floor (in pixels).\n *\n * When set, the total bottom padding is computed as:\n * `max(blankSpace, keyboardPadding + extraContentPadding)`\n *\n * This means the keyboard \"absorbs\" into the minimum padding rather than adding to it:\n * - When `blankSpace >= keyboard + extraContentPadding`: content does NOT move on keyboard open/close.\n * - When `blankSpace < keyboard + extraContentPadding`: content moves, but only by the excess amount.\n *\n * Useful in AI chat apps where a sent message needs space below it (to push it to the top\n * of the viewport) while the AI response streams in, without that space causing extra movement\n * when the keyboard opens.\n *\n * Default is `undefined` (equivalent to `0` — no minimum floor).\n */\n blankSpace?: SharedValue<number>;\n /**\n * Fires whenever the effective content inset changes — the static `contentInset`\n * prop combined with the dynamic keyboard-driven padding.\n *\n * Useful on Android, where the synthetic content inset is not reflected in the native\n * `onScroll` event payload. Consumers such as virtualized lists computing their own\n * `scrollToEnd` target can use this to track the current inset alongside scroll offsets.\n */\n onContentInsetChange?: (insets: ScrollViewContentInsets) => void;\n /**\n * Fires whenever the visibility of the content \"end\" changes — both when the user\n * arrives at the end and when they leave it. The boolean parameter reflects the new state.\n *\n * For non-inverted lists, the \"end\" is the bottom of the content. For inverted lists\n * (`inverted={true}`), the \"end\" is the top of the scroll view, where the latest messages\n * are rendered. The same internal detection drives `keyboardLiftBehavior=\"whenAtEnd\"`.\n *\n * The callback can be either a plain JS function or a Reanimated worklet — the type is\n * detected automatically. Worklets run on the UI thread; plain functions are dispatched\n * via `runOnJS`.\n *\n * Fires once on mount with the initial state (after the scroll view has been measured).\n */\n onEndVisible?: (visible: boolean) => void;\n} & ScrollViewProps;\n"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,33 @@
1
+ import { useMemo } from "react";
2
+ import { runOnJS, useAnimatedReaction, useDerivedValue } from "react-native-reanimated";
3
+ import { isScrollAtEnd } from "./useChatKeyboard/helpers";
4
+ const hasWorkletHash = value => typeof value === "function" && !!value.__workletHash;
5
+ export const useEndVisible = ({
6
+ scroll,
7
+ layout,
8
+ size,
9
+ inverted,
10
+ onEndVisible
11
+ }) => {
12
+ const isWorklet = useMemo(() => hasWorkletHash(onEndVisible), [onEndVisible]);
13
+ const isAtEnd = useDerivedValue(() => {
14
+ // Wait until the scroll view has been measured to avoid a spurious initial
15
+ // `true` on a (0,0,0) layout (the helper would otherwise treat unmeasured
16
+ // state as "at end" because 0 + 0 >= 0 - threshold).
17
+ if (layout.value.height === 0 || size.value.height === 0) {
18
+ return null;
19
+ }
20
+ return isScrollAtEnd(scroll.value, layout.value.height, size.value.height, inverted);
21
+ });
22
+ useAnimatedReaction(() => isAtEnd.value, (current, previous) => {
23
+ if (current === null || current === previous || !onEndVisible) {
24
+ return;
25
+ }
26
+ if (isWorklet) {
27
+ onEndVisible(current);
28
+ } else {
29
+ runOnJS(onEndVisible)(current);
30
+ }
31
+ }, [onEndVisible, isWorklet, inverted]);
32
+ };
33
+ //# sourceMappingURL=useEndVisible.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useMemo","runOnJS","useAnimatedReaction","useDerivedValue","isScrollAtEnd","hasWorkletHash","value","__workletHash","useEndVisible","scroll","layout","size","inverted","onEndVisible","isWorklet","isAtEnd","height","current","previous"],"sources":["useEndVisible.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport {\n runOnJS,\n useAnimatedReaction,\n useDerivedValue,\n} from \"react-native-reanimated\";\n\nimport { isScrollAtEnd } from \"./useChatKeyboard/helpers\";\n\nimport type { SharedValue } from \"react-native-reanimated\";\n\ntype EndVisibleCallback = (visible: boolean) => void;\n\ntype Options = {\n scroll: SharedValue<number>;\n layout: SharedValue<{ width: number; height: number }>;\n size: SharedValue<{ width: number; height: number }>;\n inverted: boolean;\n onEndVisible?: EndVisibleCallback;\n};\n\nconst hasWorkletHash = (value: unknown): boolean =>\n typeof value === \"function\" &&\n !!(value as unknown as Record<string, unknown>).__workletHash;\n\nexport const useEndVisible = ({\n scroll,\n layout,\n size,\n inverted,\n onEndVisible,\n}: Options) => {\n const isWorklet = useMemo(() => hasWorkletHash(onEndVisible), [onEndVisible]);\n\n const isAtEnd = useDerivedValue(() => {\n // Wait until the scroll view has been measured to avoid a spurious initial\n // `true` on a (0,0,0) layout (the helper would otherwise treat unmeasured\n // state as \"at end\" because 0 + 0 >= 0 - threshold).\n if (layout.value.height === 0 || size.value.height === 0) {\n return null;\n }\n\n return isScrollAtEnd(\n scroll.value,\n layout.value.height,\n size.value.height,\n inverted,\n );\n });\n\n useAnimatedReaction(\n () => isAtEnd.value,\n (current, previous) => {\n if (current === null || current === previous || !onEndVisible) {\n return;\n }\n\n if (isWorklet) {\n onEndVisible(current);\n } else {\n runOnJS(onEndVisible)(current);\n }\n },\n [onEndVisible, isWorklet, inverted],\n );\n};\n"],"mappings":"AAAA,SAASA,OAAO,QAAQ,OAAO;AAC/B,SACEC,OAAO,EACPC,mBAAmB,EACnBC,eAAe,QACV,yBAAyB;AAEhC,SAASC,aAAa,QAAQ,2BAA2B;AAczD,MAAMC,cAAc,GAAIC,KAAc,IACpC,OAAOA,KAAK,KAAK,UAAU,IAC3B,CAAC,CAAEA,KAAK,CAAwCC,aAAa;AAE/D,OAAO,MAAMC,aAAa,GAAGA,CAAC;EAC5BC,MAAM;EACNC,MAAM;EACNC,IAAI;EACJC,QAAQ;EACRC;AACO,CAAC,KAAK;EACb,MAAMC,SAAS,GAAGd,OAAO,CAAC,MAAMK,cAAc,CAACQ,YAAY,CAAC,EAAE,CAACA,YAAY,CAAC,CAAC;EAE7E,MAAME,OAAO,GAAGZ,eAAe,CAAC,MAAM;IACpC;IACA;IACA;IACA,IAAIO,MAAM,CAACJ,KAAK,CAACU,MAAM,KAAK,CAAC,IAAIL,IAAI,CAACL,KAAK,CAACU,MAAM,KAAK,CAAC,EAAE;MACxD,OAAO,IAAI;IACb;IAEA,OAAOZ,aAAa,CAClBK,MAAM,CAACH,KAAK,EACZI,MAAM,CAACJ,KAAK,CAACU,MAAM,EACnBL,IAAI,CAACL,KAAK,CAACU,MAAM,EACjBJ,QACF,CAAC;EACH,CAAC,CAAC;EAEFV,mBAAmB,CACjB,MAAMa,OAAO,CAACT,KAAK,EACnB,CAACW,OAAO,EAAEC,QAAQ,KAAK;IACrB,IAAID,OAAO,KAAK,IAAI,IAAIA,OAAO,KAAKC,QAAQ,IAAI,CAACL,YAAY,EAAE;MAC7D;IACF;IAEA,IAAIC,SAAS,EAAE;MACbD,YAAY,CAACI,OAAO,CAAC;IACvB,CAAC,MAAM;MACLhB,OAAO,CAACY,YAAY,CAAC,CAACI,OAAO,CAAC;IAChC;EACF,CAAC,EACD,CAACJ,YAAY,EAAEC,SAAS,EAAEF,QAAQ,CACpC,CAAC;AACH,CAAC","ignoreList":[]}
@@ -1,7 +1,7 @@
1
1
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
2
  import React, { forwardRef, useMemo } from "react";
3
- import { Animated } from "react-native";
4
- import { useKeyboardAnimation } from "../../hooks";
3
+ import Reanimated, { interpolate, useAnimatedStyle } from "react-native-reanimated";
4
+ import { useReanimatedKeyboardAnimation } from "../../hooks";
5
5
  /**
6
6
  * A View component that sticks to the keyboard and moves with it when it appears or disappears.
7
7
  * The view can be configured with custom offsets for both closed and open keyboard states.
@@ -28,21 +28,17 @@ const KeyboardStickyView = /*#__PURE__*/forwardRef(({
28
28
  const {
29
29
  height,
30
30
  progress
31
- } = useKeyboardAnimation();
32
- const offset = progress.interpolate({
33
- inputRange: [0, 1],
34
- outputRange: [closed, opened]
35
- });
36
- const styles = useMemo(() => {
37
- const disabled = Animated.add(Animated.multiply(height, 0), closed);
38
- const active = Animated.add(height, offset);
39
- return [{
31
+ } = useReanimatedKeyboardAnimation();
32
+ const stickyViewStyle = useAnimatedStyle(() => {
33
+ const offset = interpolate(progress.value, [0, 1], [closed, opened]);
34
+ return {
40
35
  transform: [{
41
- translateY: enabled ? active : disabled
36
+ translateY: enabled ? height.value + offset : closed
42
37
  }]
43
- }, style];
44
- }, [closed, enabled, height, offset, style]);
45
- return /*#__PURE__*/React.createElement(Animated.View, _extends({
38
+ };
39
+ }, [closed, opened, enabled]);
40
+ const styles = useMemo(() => [style, stickyViewStyle], [style, stickyViewStyle]);
41
+ return /*#__PURE__*/React.createElement(Reanimated.View, _extends({
46
42
  ref: ref,
47
43
  style: styles
48
44
  }, props), children);
@@ -1 +1 @@
1
- {"version":3,"names":["React","forwardRef","useMemo","Animated","useKeyboardAnimation","KeyboardStickyView","children","offset","closed","opened","style","enabled","props","ref","height","progress","interpolate","inputRange","outputRange","styles","disabled","add","multiply","active","transform","translateY","createElement","View","_extends"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useMemo } from \"react\";\nimport { Animated } from \"react-native\";\n\nimport { useKeyboardAnimation } from \"../../hooks\";\n\nimport type { View, ViewProps } from \"react-native\";\n\nexport type KeyboardStickyViewProps = {\n /**\n * Specify additional offset to the view for given keyboard state.\n */\n offset?: {\n /**\n * Adds additional `translateY` when keyboard is close. By default `0`.\n */\n closed?: number;\n /**\n * Adds additional `translateY` when keyboard is open. By default `0`.\n */\n opened?: number;\n };\n\n /** Controls whether this `KeyboardStickyView` instance should take effect. Default is `true`. */\n enabled?: boolean;\n} & ViewProps;\n\n/**\n * A View component that sticks to the keyboard and moves with it when it appears or disappears.\n * The view can be configured with custom offsets for both closed and open keyboard states.\n *\n * @returns An animated View component that sticks to the keyboard.\n * @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/components/keyboard-sticky-view|Documentation} page for more details.\n * @example\n * ```tsx\n * <KeyboardStickyView offset={{ closed: 0, opened: 20 }}>\n * <Button title=\"Submit\" />\n * </KeyboardStickyView>\n * ```\n */\nconst KeyboardStickyView = forwardRef<\n View,\n React.PropsWithChildren<KeyboardStickyViewProps>\n>(\n (\n {\n children,\n offset: { closed = 0, opened = 0 } = {},\n style,\n enabled = true,\n ...props\n },\n ref,\n ) => {\n const { height, progress } = useKeyboardAnimation();\n\n const offset = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [closed, opened],\n });\n\n const styles = useMemo(() => {\n const disabled = Animated.add(Animated.multiply(height, 0), closed);\n const active = Animated.add(height, offset);\n\n return [\n {\n transform: [{ translateY: enabled ? active : disabled }],\n },\n style,\n ];\n }, [closed, enabled, height, offset, style]);\n\n return (\n <Animated.View ref={ref} style={styles} {...props}>\n {children}\n </Animated.View>\n );\n },\n);\n\nexport default KeyboardStickyView;\n"],"mappings":";AAAA,OAAOA,KAAK,IAAIC,UAAU,EAAEC,OAAO,QAAQ,OAAO;AAClD,SAASC,QAAQ,QAAQ,cAAc;AAEvC,SAASC,oBAAoB,QAAQ,aAAa;AAuBlD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,kBAAkB,gBAAGJ,UAAU,CAInC,CACE;EACEK,QAAQ;EACRC,MAAM,EAAE;IAAEC,MAAM,GAAG,CAAC;IAAEC,MAAM,GAAG;EAAE,CAAC,GAAG,CAAC,CAAC;EACvCC,KAAK;EACLC,OAAO,GAAG,IAAI;EACd,GAAGC;AACL,CAAC,EACDC,GAAG,KACA;EACH,MAAM;IAAEC,MAAM;IAAEC;EAAS,CAAC,GAAGX,oBAAoB,CAAC,CAAC;EAEnD,MAAMG,MAAM,GAAGQ,QAAQ,CAACC,WAAW,CAAC;IAClCC,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAClBC,WAAW,EAAE,CAACV,MAAM,EAAEC,MAAM;EAC9B,CAAC,CAAC;EAEF,MAAMU,MAAM,GAAGjB,OAAO,CAAC,MAAM;IAC3B,MAAMkB,QAAQ,GAAGjB,QAAQ,CAACkB,GAAG,CAAClB,QAAQ,CAACmB,QAAQ,CAACR,MAAM,EAAE,CAAC,CAAC,EAAEN,MAAM,CAAC;IACnE,MAAMe,MAAM,GAAGpB,QAAQ,CAACkB,GAAG,CAACP,MAAM,EAAEP,MAAM,CAAC;IAE3C,OAAO,CACL;MACEiB,SAAS,EAAE,CAAC;QAAEC,UAAU,EAAEd,OAAO,GAAGY,MAAM,GAAGH;MAAS,CAAC;IACzD,CAAC,EACDV,KAAK,CACN;EACH,CAAC,EAAE,CAACF,MAAM,EAAEG,OAAO,EAAEG,MAAM,EAAEP,MAAM,EAAEG,KAAK,CAAC,CAAC;EAE5C,oBACEV,KAAA,CAAA0B,aAAA,CAACvB,QAAQ,CAACwB,IAAI,EAAAC,QAAA;IAACf,GAAG,EAAEA,GAAI;IAACH,KAAK,EAAES;EAAO,GAAKP,KAAK,GAC9CN,QACY,CAAC;AAEpB,CACF,CAAC;AAED,eAAeD,kBAAkB","ignoreList":[]}
1
+ {"version":3,"names":["React","forwardRef","useMemo","Reanimated","interpolate","useAnimatedStyle","useReanimatedKeyboardAnimation","KeyboardStickyView","children","offset","closed","opened","style","enabled","props","ref","height","progress","stickyViewStyle","value","transform","translateY","styles","createElement","View","_extends"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useMemo } from \"react\";\nimport Reanimated, {\n interpolate,\n useAnimatedStyle,\n} from \"react-native-reanimated\";\n\nimport { useReanimatedKeyboardAnimation } from \"../../hooks\";\n\nimport type { View, ViewProps } from \"react-native\";\n\nexport type KeyboardStickyViewProps = {\n /**\n * Specify additional offset to the view for given keyboard state.\n */\n offset?: {\n /**\n * Adds additional `translateY` when keyboard is close. By default `0`.\n */\n closed?: number;\n /**\n * Adds additional `translateY` when keyboard is open. By default `0`.\n */\n opened?: number;\n };\n\n /** Controls whether this `KeyboardStickyView` instance should take effect. Default is `true`. */\n enabled?: boolean;\n} & ViewProps;\n\n/**\n * A View component that sticks to the keyboard and moves with it when it appears or disappears.\n * The view can be configured with custom offsets for both closed and open keyboard states.\n *\n * @returns An animated View component that sticks to the keyboard.\n * @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/components/keyboard-sticky-view|Documentation} page for more details.\n * @example\n * ```tsx\n * <KeyboardStickyView offset={{ closed: 0, opened: 20 }}>\n * <Button title=\"Submit\" />\n * </KeyboardStickyView>\n * ```\n */\nconst KeyboardStickyView = forwardRef<\n View,\n React.PropsWithChildren<KeyboardStickyViewProps>\n>(\n (\n {\n children,\n offset: { closed = 0, opened = 0 } = {},\n style,\n enabled = true,\n ...props\n },\n ref,\n ) => {\n const { height, progress } = useReanimatedKeyboardAnimation();\n\n const stickyViewStyle = useAnimatedStyle(() => {\n const offset = interpolate(progress.value, [0, 1], [closed, opened]);\n\n return {\n transform: [{ translateY: enabled ? height.value + offset : closed }],\n };\n }, [closed, opened, enabled]);\n\n const styles = useMemo(\n () => [style, stickyViewStyle],\n [style, stickyViewStyle],\n );\n\n return (\n <Reanimated.View ref={ref} style={styles} {...props}>\n {children}\n </Reanimated.View>\n );\n },\n);\n\nexport default KeyboardStickyView;\n"],"mappings":";AAAA,OAAOA,KAAK,IAAIC,UAAU,EAAEC,OAAO,QAAQ,OAAO;AAClD,OAAOC,UAAU,IACfC,WAAW,EACXC,gBAAgB,QACX,yBAAyB;AAEhC,SAASC,8BAA8B,QAAQ,aAAa;AAuB5D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,kBAAkB,gBAAGN,UAAU,CAInC,CACE;EACEO,QAAQ;EACRC,MAAM,EAAE;IAAEC,MAAM,GAAG,CAAC;IAAEC,MAAM,GAAG;EAAE,CAAC,GAAG,CAAC,CAAC;EACvCC,KAAK;EACLC,OAAO,GAAG,IAAI;EACd,GAAGC;AACL,CAAC,EACDC,GAAG,KACA;EACH,MAAM;IAAEC,MAAM;IAAEC;EAAS,CAAC,GAAGX,8BAA8B,CAAC,CAAC;EAE7D,MAAMY,eAAe,GAAGb,gBAAgB,CAAC,MAAM;IAC7C,MAAMI,MAAM,GAAGL,WAAW,CAACa,QAAQ,CAACE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAACT,MAAM,EAAEC,MAAM,CAAC,CAAC;IAEpE,OAAO;MACLS,SAAS,EAAE,CAAC;QAAEC,UAAU,EAAER,OAAO,GAAGG,MAAM,CAACG,KAAK,GAAGV,MAAM,GAAGC;MAAO,CAAC;IACtE,CAAC;EACH,CAAC,EAAE,CAACA,MAAM,EAAEC,MAAM,EAAEE,OAAO,CAAC,CAAC;EAE7B,MAAMS,MAAM,GAAGpB,OAAO,CACpB,MAAM,CAACU,KAAK,EAAEM,eAAe,CAAC,EAC9B,CAACN,KAAK,EAAEM,eAAe,CACzB,CAAC;EAED,oBACElB,KAAA,CAAAuB,aAAA,CAACpB,UAAU,CAACqB,IAAI,EAAAC,QAAA;IAACV,GAAG,EAAEA,GAAI;IAACH,KAAK,EAAEU;EAAO,GAAKR,KAAK,GAChDN,QACc,CAAC;AAEtB,CACF,CAAC;AAED,eAAeD,kBAAkB","ignoreList":[]}
@@ -1,7 +1,7 @@
1
1
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
2
  import React, { forwardRef } from "react";
3
3
  import { Platform } from "react-native";
4
- import Reanimated, { useAnimatedProps, useSharedValue } from "react-native-reanimated";
4
+ import Reanimated, { runOnJS, useAnimatedProps, useAnimatedReaction, useDerivedValue, useSharedValue } from "react-native-reanimated";
5
5
  import { ClippingScrollView } from "../../bindings";
6
6
  import styles from "./styles";
7
7
  const OS = Platform.OS;
@@ -15,26 +15,47 @@ const ScrollViewWithBottomPadding = /*#__PURE__*/forwardRef(({
15
15
  inverted,
16
16
  contentOffsetY,
17
17
  applyWorkaroundForContentInsetHitTestBug,
18
+ onContentInsetChange,
18
19
  children,
19
20
  ...rest
20
21
  }, ref) => {
21
22
  const prevContentOffsetY = useSharedValue(null);
23
+ const insets = useDerivedValue(() => {
24
+ const dynamicTop = inverted ? bottomPadding.value : 0;
25
+ const dynamicBottom = !inverted ? bottomPadding.value : 0;
26
+ return {
27
+ dynamic: {
28
+ top: dynamicTop,
29
+ bottom: dynamicBottom
30
+ },
31
+ effective: {
32
+ top: dynamicTop + ((contentInset === null || contentInset === void 0 ? void 0 : contentInset.top) || 0),
33
+ bottom: dynamicBottom + ((contentInset === null || contentInset === void 0 ? void 0 : contentInset.bottom) || 0),
34
+ left: (contentInset === null || contentInset === void 0 ? void 0 : contentInset.left) || 0,
35
+ right: (contentInset === null || contentInset === void 0 ? void 0 : contentInset.right) || 0
36
+ }
37
+ };
38
+ }, [inverted, contentInset === null || contentInset === void 0 ? void 0 : contentInset.top, contentInset === null || contentInset === void 0 ? void 0 : contentInset.bottom, contentInset === null || contentInset === void 0 ? void 0 : contentInset.left, contentInset === null || contentInset === void 0 ? void 0 : contentInset.right]);
39
+ useAnimatedReaction(() => insets.value.effective, (current, previous) => {
40
+ if (!onContentInsetChange) {
41
+ return;
42
+ }
43
+ if (previous && current.top === previous.top && current.bottom === previous.bottom && current.left === previous.left && current.right === previous.right) {
44
+ return;
45
+ }
46
+ runOnJS(onContentInsetChange)(current);
47
+ }, [onContentInsetChange]);
22
48
  const animatedProps = useAnimatedProps(() => {
23
- const insetTop = inverted ? bottomPadding.value : 0;
24
- const insetBottom = !inverted ? bottomPadding.value : 0;
25
- const bottom = insetBottom + ((contentInset === null || contentInset === void 0 ? void 0 : contentInset.bottom) || 0);
26
- const top = insetTop + ((contentInset === null || contentInset === void 0 ? void 0 : contentInset.top) || 0);
49
+ const {
50
+ dynamic,
51
+ effective
52
+ } = insets.value;
27
53
  const indicatorPadding = scrollIndicatorPadding ?? bottomPadding;
28
54
  const indicatorTop = (inverted ? indicatorPadding.value : 0) + ((scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.top) || 0);
29
55
  const indicatorBottom = (!inverted ? indicatorPadding.value : 0) + ((scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.bottom) || 0);
30
56
  const result = {
31
57
  // iOS prop
32
- contentInset: {
33
- bottom: bottom,
34
- top: top,
35
- right: contentInset === null || contentInset === void 0 ? void 0 : contentInset.right,
36
- left: contentInset === null || contentInset === void 0 ? void 0 : contentInset.left
37
- },
58
+ contentInset: effective,
38
59
  scrollIndicatorInsets: {
39
60
  bottom: indicatorBottom,
40
61
  top: indicatorTop,
@@ -42,8 +63,8 @@ const ScrollViewWithBottomPadding = /*#__PURE__*/forwardRef(({
42
63
  left: scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.left
43
64
  },
44
65
  // Android prop
45
- contentInsetBottom: insetBottom,
46
- contentInsetTop: insetTop
66
+ contentInsetBottom: dynamic.bottom,
67
+ contentInsetTop: dynamic.top
47
68
  };
48
69
  if (contentOffsetY) {
49
70
  const curr = contentOffsetY.value;
@@ -57,7 +78,7 @@ const ScrollViewWithBottomPadding = /*#__PURE__*/forwardRef(({
57
78
  }
58
79
  }
59
80
  return result;
60
- }, [contentInset === null || contentInset === void 0 ? void 0 : contentInset.bottom, contentInset === null || contentInset === void 0 ? void 0 : contentInset.top, contentInset === null || contentInset === void 0 ? void 0 : contentInset.right, contentInset === null || contentInset === void 0 ? void 0 : contentInset.left, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.bottom, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.top, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.right, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.left, inverted, contentOffsetY]);
81
+ }, [scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.bottom, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.top, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.right, scrollIndicatorInsets === null || scrollIndicatorInsets === void 0 ? void 0 : scrollIndicatorInsets.left, inverted, contentOffsetY]);
61
82
  return /*#__PURE__*/React.createElement(ReanimatedClippingScrollView, {
62
83
  animatedProps: animatedProps,
63
84
  applyWorkaroundForContentInsetHitTestBug: applyWorkaroundForContentInsetHitTestBug,
@@ -1 +1 @@
1
- {"version":3,"names":["React","forwardRef","Platform","Reanimated","useAnimatedProps","useSharedValue","ClippingScrollView","styles","OS","ReanimatedClippingScrollView","createAnimatedComponent","ScrollViewWithBottomPadding","ScrollViewComponent","bottomPadding","scrollIndicatorPadding","contentInset","scrollIndicatorInsets","inverted","contentOffsetY","applyWorkaroundForContentInsetHitTestBug","children","rest","ref","prevContentOffsetY","animatedProps","insetTop","value","insetBottom","bottom","top","indicatorPadding","indicatorTop","indicatorBottom","result","right","left","contentInsetBottom","contentInsetTop","curr","contentOffset","x","y","createElement","style","container","_extends"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef } from \"react\";\nimport { Platform } from \"react-native\";\nimport Reanimated, {\n useAnimatedProps,\n useSharedValue,\n} from \"react-native-reanimated\";\n\nimport { ClippingScrollView } from \"../../bindings\";\n\nimport styles from \"./styles\";\n\nimport type { ScrollViewProps } from \"react-native\";\nimport type { SharedValue } from \"react-native-reanimated\";\n\nconst OS = Platform.OS;\nconst ReanimatedClippingScrollView =\n OS === \"android\"\n ? Reanimated.createAnimatedComponent(ClippingScrollView)\n : ClippingScrollView;\n\ntype AnimatedScrollViewProps = React.ComponentProps<\n typeof Reanimated.ScrollView\n>;\n\nexport type AnimatedScrollViewComponent = React.ForwardRefExoticComponent<\n AnimatedScrollViewProps & React.RefAttributes<Reanimated.ScrollView>\n>;\n\ntype ScrollViewWithBottomPaddingProps = {\n ScrollViewComponent: AnimatedScrollViewComponent;\n children?: React.ReactNode;\n inverted?: boolean;\n bottomPadding: SharedValue<number>;\n /** Padding for scroll indicator insets (excludes blankSpace). Falls back to bottomPadding when not provided. */\n scrollIndicatorPadding?: SharedValue<number>;\n /** Absolute Y content offset (iOS only, for KeyboardChatScrollView). */\n contentOffsetY?: SharedValue<number>;\n applyWorkaroundForContentInsetHitTestBug?: boolean;\n} & ScrollViewProps;\n\nconst ScrollViewWithBottomPadding = forwardRef<\n Reanimated.ScrollView,\n ScrollViewWithBottomPaddingProps\n>(\n (\n {\n ScrollViewComponent,\n bottomPadding,\n scrollIndicatorPadding,\n contentInset,\n scrollIndicatorInsets,\n inverted,\n contentOffsetY,\n applyWorkaroundForContentInsetHitTestBug,\n children,\n ...rest\n },\n ref,\n ) => {\n const prevContentOffsetY = useSharedValue<number | null>(null);\n\n const animatedProps = useAnimatedProps(() => {\n const insetTop = inverted ? bottomPadding.value : 0;\n const insetBottom = !inverted ? bottomPadding.value : 0;\n const bottom = insetBottom + (contentInset?.bottom || 0);\n const top = insetTop + (contentInset?.top || 0);\n\n const indicatorPadding = scrollIndicatorPadding ?? bottomPadding;\n const indicatorTop =\n (inverted ? indicatorPadding.value : 0) +\n (scrollIndicatorInsets?.top || 0);\n const indicatorBottom =\n (!inverted ? indicatorPadding.value : 0) +\n (scrollIndicatorInsets?.bottom || 0);\n\n const result: Record<string, unknown> = {\n // iOS prop\n contentInset: {\n bottom: bottom,\n top: top,\n right: contentInset?.right,\n left: contentInset?.left,\n },\n scrollIndicatorInsets: {\n bottom: indicatorBottom,\n top: indicatorTop,\n right: scrollIndicatorInsets?.right,\n left: scrollIndicatorInsets?.left,\n },\n // Android prop\n contentInsetBottom: insetBottom,\n contentInsetTop: insetTop,\n };\n\n if (contentOffsetY) {\n const curr = contentOffsetY.value;\n\n if (curr !== prevContentOffsetY.value) {\n // eslint-disable-next-line react-compiler/react-compiler\n prevContentOffsetY.value = curr;\n result.contentOffset = { x: 0, y: curr };\n }\n }\n\n return result;\n }, [\n contentInset?.bottom,\n contentInset?.top,\n contentInset?.right,\n contentInset?.left,\n scrollIndicatorInsets?.bottom,\n scrollIndicatorInsets?.top,\n scrollIndicatorInsets?.right,\n scrollIndicatorInsets?.left,\n inverted,\n contentOffsetY,\n ]);\n\n return (\n <ReanimatedClippingScrollView\n animatedProps={animatedProps}\n applyWorkaroundForContentInsetHitTestBug={\n applyWorkaroundForContentInsetHitTestBug\n }\n style={styles.container}\n >\n <ScrollViewComponent ref={ref} animatedProps={animatedProps} {...rest}>\n {children}\n </ScrollViewComponent>\n </ReanimatedClippingScrollView>\n );\n },\n);\n\nexport default ScrollViewWithBottomPadding;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["React","forwardRef","Platform","Reanimated","runOnJS","useAnimatedProps","useAnimatedReaction","useDerivedValue","useSharedValue","ClippingScrollView","styles","OS","ReanimatedClippingScrollView","createAnimatedComponent","ScrollViewWithBottomPadding","ScrollViewComponent","bottomPadding","scrollIndicatorPadding","contentInset","scrollIndicatorInsets","inverted","contentOffsetY","applyWorkaroundForContentInsetHitTestBug","onContentInsetChange","children","rest","ref","prevContentOffsetY","insets","dynamicTop","value","dynamicBottom","dynamic","top","bottom","effective","left","right","current","previous","animatedProps","indicatorPadding","indicatorTop","indicatorBottom","result","contentInsetBottom","contentInsetTop","curr","contentOffset","x","y","createElement","style","container","_extends"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef } from \"react\";\nimport { Platform } from \"react-native\";\nimport Reanimated, {\n runOnJS,\n useAnimatedProps,\n useAnimatedReaction,\n useDerivedValue,\n useSharedValue,\n} from \"react-native-reanimated\";\n\nimport { ClippingScrollView } from \"../../bindings\";\n\nimport styles from \"./styles\";\n\nimport type { ScrollViewProps } from \"react-native\";\nimport type { SharedValue } from \"react-native-reanimated\";\n\nconst OS = Platform.OS;\nconst ReanimatedClippingScrollView =\n OS === \"android\"\n ? Reanimated.createAnimatedComponent(ClippingScrollView)\n : ClippingScrollView;\n\ntype AnimatedScrollViewProps = React.ComponentProps<\n typeof Reanimated.ScrollView\n>;\n\nexport type AnimatedScrollViewComponent = React.ForwardRefExoticComponent<\n AnimatedScrollViewProps & React.RefAttributes<Reanimated.ScrollView>\n>;\n\nexport type ScrollViewContentInsets = {\n top: number;\n bottom: number;\n left: number;\n right: number;\n};\n\ntype ScrollViewWithBottomPaddingProps = {\n ScrollViewComponent: AnimatedScrollViewComponent;\n children?: React.ReactNode;\n inverted?: boolean;\n bottomPadding: SharedValue<number>;\n /** Padding for scroll indicator insets (excludes blankSpace). Falls back to bottomPadding when not provided. */\n scrollIndicatorPadding?: SharedValue<number>;\n /** Absolute Y content offset (iOS only, for KeyboardChatScrollView). */\n contentOffsetY?: SharedValue<number>;\n applyWorkaroundForContentInsetHitTestBug?: boolean;\n /**\n * Fires whenever the effective content inset changes (combines the static `contentInset`\n * prop with the dynamic keyboard-driven padding). Useful on Android where the synthetic\n * inset is not reflected in `onScroll` events.\n */\n onContentInsetChange?: (insets: ScrollViewContentInsets) => void;\n} & ScrollViewProps;\n\nconst ScrollViewWithBottomPadding = forwardRef<\n Reanimated.ScrollView,\n ScrollViewWithBottomPaddingProps\n>(\n (\n {\n ScrollViewComponent,\n bottomPadding,\n scrollIndicatorPadding,\n contentInset,\n scrollIndicatorInsets,\n inverted,\n contentOffsetY,\n applyWorkaroundForContentInsetHitTestBug,\n onContentInsetChange,\n children,\n ...rest\n },\n ref,\n ) => {\n const prevContentOffsetY = useSharedValue<number | null>(null);\n\n const insets = useDerivedValue(() => {\n const dynamicTop = inverted ? bottomPadding.value : 0;\n const dynamicBottom = !inverted ? bottomPadding.value : 0;\n\n return {\n dynamic: {\n top: dynamicTop,\n bottom: dynamicBottom,\n },\n effective: {\n top: dynamicTop + (contentInset?.top || 0),\n bottom: dynamicBottom + (contentInset?.bottom || 0),\n left: contentInset?.left || 0,\n right: contentInset?.right || 0,\n } as ScrollViewContentInsets,\n };\n }, [\n inverted,\n contentInset?.top,\n contentInset?.bottom,\n contentInset?.left,\n contentInset?.right,\n ]);\n\n useAnimatedReaction(\n () => insets.value.effective,\n (current, previous) => {\n if (!onContentInsetChange) {\n return;\n }\n if (\n previous &&\n current.top === previous.top &&\n current.bottom === previous.bottom &&\n current.left === previous.left &&\n current.right === previous.right\n ) {\n return;\n }\n runOnJS(onContentInsetChange)(current);\n },\n [onContentInsetChange],\n );\n\n const animatedProps = useAnimatedProps(() => {\n const { dynamic, effective } = insets.value;\n\n const indicatorPadding = scrollIndicatorPadding ?? bottomPadding;\n const indicatorTop =\n (inverted ? indicatorPadding.value : 0) +\n (scrollIndicatorInsets?.top || 0);\n const indicatorBottom =\n (!inverted ? indicatorPadding.value : 0) +\n (scrollIndicatorInsets?.bottom || 0);\n\n const result: Record<string, unknown> = {\n // iOS prop\n contentInset: effective,\n scrollIndicatorInsets: {\n bottom: indicatorBottom,\n top: indicatorTop,\n right: scrollIndicatorInsets?.right,\n left: scrollIndicatorInsets?.left,\n },\n // Android prop\n contentInsetBottom: dynamic.bottom,\n contentInsetTop: dynamic.top,\n };\n\n if (contentOffsetY) {\n const curr = contentOffsetY.value;\n\n if (curr !== prevContentOffsetY.value) {\n // eslint-disable-next-line react-compiler/react-compiler\n prevContentOffsetY.value = curr;\n result.contentOffset = { x: 0, y: curr };\n }\n }\n\n return result;\n }, [\n scrollIndicatorInsets?.bottom,\n scrollIndicatorInsets?.top,\n scrollIndicatorInsets?.right,\n scrollIndicatorInsets?.left,\n inverted,\n contentOffsetY,\n ]);\n\n return (\n <ReanimatedClippingScrollView\n animatedProps={animatedProps}\n applyWorkaroundForContentInsetHitTestBug={\n applyWorkaroundForContentInsetHitTestBug\n }\n style={styles.container}\n >\n <ScrollViewComponent ref={ref} animatedProps={animatedProps} {...rest}>\n {children}\n </ScrollViewComponent>\n </ReanimatedClippingScrollView>\n );\n },\n);\n\nexport default ScrollViewWithBottomPadding;\n"],"mappings":"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","ignoreList":[]}
@@ -9,6 +9,8 @@ declare const KeyboardChatScrollView: React.ForwardRefExoticComponent<{
9
9
  extraContentPadding?: import("react-native-reanimated").SharedValue<number>;
10
10
  applyWorkaroundForContentInsetHitTestBug?: boolean;
11
11
  blankSpace?: import("react-native-reanimated").SharedValue<number>;
12
+ onContentInsetChange?: (insets: import("../ScrollViewWithBottomPadding").ScrollViewContentInsets) => void;
13
+ onEndVisible?: (visible: boolean) => void;
12
14
  } & import("react-native").ScrollViewProps & {
13
15
  children?: React.ReactNode | undefined;
14
16
  } & React.RefAttributes<Reanimated.ScrollView>>;
@@ -1,4 +1,4 @@
1
- import type { AnimatedScrollViewComponent } from "../ScrollViewWithBottomPadding";
1
+ import type { AnimatedScrollViewComponent, ScrollViewContentInsets } from "../ScrollViewWithBottomPadding";
2
2
  import type { KeyboardLiftBehavior } from "./useChatKeyboard/types";
3
3
  import type { ScrollViewProps } from "react-native";
4
4
  import type { SharedValue } from "react-native-reanimated";
@@ -86,4 +86,28 @@ export type KeyboardChatScrollViewProps = {
86
86
  * Default is `undefined` (equivalent to `0` — no minimum floor).
87
87
  */
88
88
  blankSpace?: SharedValue<number>;
89
+ /**
90
+ * Fires whenever the effective content inset changes — the static `contentInset`
91
+ * prop combined with the dynamic keyboard-driven padding.
92
+ *
93
+ * Useful on Android, where the synthetic content inset is not reflected in the native
94
+ * `onScroll` event payload. Consumers such as virtualized lists computing their own
95
+ * `scrollToEnd` target can use this to track the current inset alongside scroll offsets.
96
+ */
97
+ onContentInsetChange?: (insets: ScrollViewContentInsets) => void;
98
+ /**
99
+ * Fires whenever the visibility of the content "end" changes — both when the user
100
+ * arrives at the end and when they leave it. The boolean parameter reflects the new state.
101
+ *
102
+ * For non-inverted lists, the "end" is the bottom of the content. For inverted lists
103
+ * (`inverted={true}`), the "end" is the top of the scroll view, where the latest messages
104
+ * are rendered. The same internal detection drives `keyboardLiftBehavior="whenAtEnd"`.
105
+ *
106
+ * The callback can be either a plain JS function or a Reanimated worklet — the type is
107
+ * detected automatically. Worklets run on the UI thread; plain functions are dispatched
108
+ * via `runOnJS`.
109
+ *
110
+ * Fires once on mount with the initial state (after the scroll view has been measured).
111
+ */
112
+ onEndVisible?: (visible: boolean) => void;
89
113
  } & ScrollViewProps;
@@ -0,0 +1,17 @@
1
+ import type { SharedValue } from "react-native-reanimated";
2
+ type EndVisibleCallback = (visible: boolean) => void;
3
+ type Options = {
4
+ scroll: SharedValue<number>;
5
+ layout: SharedValue<{
6
+ width: number;
7
+ height: number;
8
+ }>;
9
+ size: SharedValue<{
10
+ width: number;
11
+ height: number;
12
+ }>;
13
+ inverted: boolean;
14
+ onEndVisible?: EndVisibleCallback;
15
+ };
16
+ export declare const useEndVisible: ({ scroll, layout, size, inverted, onEndVisible, }: Options) => void;
17
+ export {};
@@ -4,6 +4,12 @@ import type { ScrollViewProps } from "react-native";
4
4
  import type { SharedValue } from "react-native-reanimated";
5
5
  type AnimatedScrollViewProps = React.ComponentProps<typeof Reanimated.ScrollView>;
6
6
  export type AnimatedScrollViewComponent = React.ForwardRefExoticComponent<AnimatedScrollViewProps & React.RefAttributes<Reanimated.ScrollView>>;
7
+ export type ScrollViewContentInsets = {
8
+ top: number;
9
+ bottom: number;
10
+ left: number;
11
+ right: number;
12
+ };
7
13
  declare const ScrollViewWithBottomPadding: React.ForwardRefExoticComponent<{
8
14
  ScrollViewComponent: AnimatedScrollViewComponent;
9
15
  children?: React.ReactNode;
@@ -14,5 +20,11 @@ declare const ScrollViewWithBottomPadding: React.ForwardRefExoticComponent<{
14
20
  /** Absolute Y content offset (iOS only, for KeyboardChatScrollView). */
15
21
  contentOffsetY?: SharedValue<number>;
16
22
  applyWorkaroundForContentInsetHitTestBug?: boolean;
23
+ /**
24
+ * Fires whenever the effective content inset changes (combines the static `contentInset`
25
+ * prop with the dynamic keyboard-driven padding). Useful on Android where the synthetic
26
+ * inset is not reflected in `onScroll` events.
27
+ */
28
+ onContentInsetChange?: (insets: ScrollViewContentInsets) => void;
17
29
  } & ScrollViewProps & React.RefAttributes<Reanimated.ScrollView>>;
18
30
  export default ScrollViewWithBottomPadding;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-keyboard-controller",
3
- "version": "1.21.6",
3
+ "version": "1.21.7",
4
4
  "description": "Keyboard manager which works in identical way on both iOS and Android",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -12,6 +12,7 @@ import useCombinedRef from "../hooks/useCombinedRef";
12
12
  import ScrollViewWithBottomPadding from "../ScrollViewWithBottomPadding";
13
13
 
14
14
  import { useChatKeyboard } from "./useChatKeyboard";
15
+ import { useEndVisible } from "./useEndVisible";
15
16
  import { useExtraContentPadding } from "./useExtraContentPadding";
16
17
 
17
18
  import type { KeyboardChatScrollViewProps } from "./types";
@@ -37,6 +38,7 @@ const KeyboardChatScrollView = forwardRef<
37
38
  applyWorkaroundForContentInsetHitTestBug = false,
38
39
  onLayout: onLayoutProp,
39
40
  onContentSizeChange: onContentSizeChangeProp,
41
+ onEndVisible,
40
42
  ...rest
41
43
  },
42
44
  ref,
@@ -78,6 +80,14 @@ const KeyboardChatScrollView = forwardRef<
78
80
  freeze: freezeSV,
79
81
  });
80
82
 
83
+ useEndVisible({
84
+ scroll,
85
+ layout,
86
+ size,
87
+ inverted,
88
+ onEndVisible,
89
+ });
90
+
81
91
  const totalPadding = useDerivedValue(() =>
82
92
  Math.max(blankSpace.value, padding.value + extraContentPadding.value),
83
93
  );
@@ -1,4 +1,7 @@
1
- import type { AnimatedScrollViewComponent } from "../ScrollViewWithBottomPadding";
1
+ import type {
2
+ AnimatedScrollViewComponent,
3
+ ScrollViewContentInsets,
4
+ } from "../ScrollViewWithBottomPadding";
2
5
  import type { KeyboardLiftBehavior } from "./useChatKeyboard/types";
3
6
  import type { ScrollViewProps } from "react-native";
4
7
  import type { SharedValue } from "react-native-reanimated";
@@ -87,4 +90,28 @@ export type KeyboardChatScrollViewProps = {
87
90
  * Default is `undefined` (equivalent to `0` — no minimum floor).
88
91
  */
89
92
  blankSpace?: SharedValue<number>;
93
+ /**
94
+ * Fires whenever the effective content inset changes — the static `contentInset`
95
+ * prop combined with the dynamic keyboard-driven padding.
96
+ *
97
+ * Useful on Android, where the synthetic content inset is not reflected in the native
98
+ * `onScroll` event payload. Consumers such as virtualized lists computing their own
99
+ * `scrollToEnd` target can use this to track the current inset alongside scroll offsets.
100
+ */
101
+ onContentInsetChange?: (insets: ScrollViewContentInsets) => void;
102
+ /**
103
+ * Fires whenever the visibility of the content "end" changes — both when the user
104
+ * arrives at the end and when they leave it. The boolean parameter reflects the new state.
105
+ *
106
+ * For non-inverted lists, the "end" is the bottom of the content. For inverted lists
107
+ * (`inverted={true}`), the "end" is the top of the scroll view, where the latest messages
108
+ * are rendered. The same internal detection drives `keyboardLiftBehavior="whenAtEnd"`.
109
+ *
110
+ * The callback can be either a plain JS function or a Reanimated worklet — the type is
111
+ * detected automatically. Worklets run on the UI thread; plain functions are dispatched
112
+ * via `runOnJS`.
113
+ *
114
+ * Fires once on mount with the initial state (after the scroll view has been measured).
115
+ */
116
+ onEndVisible?: (visible: boolean) => void;
90
117
  } & ScrollViewProps;
@@ -0,0 +1,66 @@
1
+ import { useMemo } from "react";
2
+ import {
3
+ runOnJS,
4
+ useAnimatedReaction,
5
+ useDerivedValue,
6
+ } from "react-native-reanimated";
7
+
8
+ import { isScrollAtEnd } from "./useChatKeyboard/helpers";
9
+
10
+ import type { SharedValue } from "react-native-reanimated";
11
+
12
+ type EndVisibleCallback = (visible: boolean) => void;
13
+
14
+ type Options = {
15
+ scroll: SharedValue<number>;
16
+ layout: SharedValue<{ width: number; height: number }>;
17
+ size: SharedValue<{ width: number; height: number }>;
18
+ inverted: boolean;
19
+ onEndVisible?: EndVisibleCallback;
20
+ };
21
+
22
+ const hasWorkletHash = (value: unknown): boolean =>
23
+ typeof value === "function" &&
24
+ !!(value as unknown as Record<string, unknown>).__workletHash;
25
+
26
+ export const useEndVisible = ({
27
+ scroll,
28
+ layout,
29
+ size,
30
+ inverted,
31
+ onEndVisible,
32
+ }: Options) => {
33
+ const isWorklet = useMemo(() => hasWorkletHash(onEndVisible), [onEndVisible]);
34
+
35
+ const isAtEnd = useDerivedValue(() => {
36
+ // Wait until the scroll view has been measured to avoid a spurious initial
37
+ // `true` on a (0,0,0) layout (the helper would otherwise treat unmeasured
38
+ // state as "at end" because 0 + 0 >= 0 - threshold).
39
+ if (layout.value.height === 0 || size.value.height === 0) {
40
+ return null;
41
+ }
42
+
43
+ return isScrollAtEnd(
44
+ scroll.value,
45
+ layout.value.height,
46
+ size.value.height,
47
+ inverted,
48
+ );
49
+ });
50
+
51
+ useAnimatedReaction(
52
+ () => isAtEnd.value,
53
+ (current, previous) => {
54
+ if (current === null || current === previous || !onEndVisible) {
55
+ return;
56
+ }
57
+
58
+ if (isWorklet) {
59
+ onEndVisible(current);
60
+ } else {
61
+ runOnJS(onEndVisible)(current);
62
+ }
63
+ },
64
+ [onEndVisible, isWorklet, inverted],
65
+ );
66
+ };
@@ -1,7 +1,10 @@
1
1
  import React, { forwardRef, useMemo } from "react";
2
- import { Animated } from "react-native";
2
+ import Reanimated, {
3
+ interpolate,
4
+ useAnimatedStyle,
5
+ } from "react-native-reanimated";
3
6
 
4
- import { useKeyboardAnimation } from "../../hooks";
7
+ import { useReanimatedKeyboardAnimation } from "../../hooks";
5
8
 
6
9
  import type { View, ViewProps } from "react-native";
7
10
 
@@ -51,29 +54,25 @@ const KeyboardStickyView = forwardRef<
51
54
  },
52
55
  ref,
53
56
  ) => {
54
- const { height, progress } = useKeyboardAnimation();
57
+ const { height, progress } = useReanimatedKeyboardAnimation();
55
58
 
56
- const offset = progress.interpolate({
57
- inputRange: [0, 1],
58
- outputRange: [closed, opened],
59
- });
59
+ const stickyViewStyle = useAnimatedStyle(() => {
60
+ const offset = interpolate(progress.value, [0, 1], [closed, opened]);
60
61
 
61
- const styles = useMemo(() => {
62
- const disabled = Animated.add(Animated.multiply(height, 0), closed);
63
- const active = Animated.add(height, offset);
62
+ return {
63
+ transform: [{ translateY: enabled ? height.value + offset : closed }],
64
+ };
65
+ }, [closed, opened, enabled]);
64
66
 
65
- return [
66
- {
67
- transform: [{ translateY: enabled ? active : disabled }],
68
- },
69
- style,
70
- ];
71
- }, [closed, enabled, height, offset, style]);
67
+ const styles = useMemo(
68
+ () => [style, stickyViewStyle],
69
+ [style, stickyViewStyle],
70
+ );
72
71
 
73
72
  return (
74
- <Animated.View ref={ref} style={styles} {...props}>
73
+ <Reanimated.View ref={ref} style={styles} {...props}>
75
74
  {children}
76
- </Animated.View>
75
+ </Reanimated.View>
77
76
  );
78
77
  },
79
78
  );
@@ -1,7 +1,10 @@
1
1
  import React, { forwardRef } from "react";
2
2
  import { Platform } from "react-native";
3
3
  import Reanimated, {
4
+ runOnJS,
4
5
  useAnimatedProps,
6
+ useAnimatedReaction,
7
+ useDerivedValue,
5
8
  useSharedValue,
6
9
  } from "react-native-reanimated";
7
10
 
@@ -26,6 +29,13 @@ export type AnimatedScrollViewComponent = React.ForwardRefExoticComponent<
26
29
  AnimatedScrollViewProps & React.RefAttributes<Reanimated.ScrollView>
27
30
  >;
28
31
 
32
+ export type ScrollViewContentInsets = {
33
+ top: number;
34
+ bottom: number;
35
+ left: number;
36
+ right: number;
37
+ };
38
+
29
39
  type ScrollViewWithBottomPaddingProps = {
30
40
  ScrollViewComponent: AnimatedScrollViewComponent;
31
41
  children?: React.ReactNode;
@@ -36,6 +46,12 @@ type ScrollViewWithBottomPaddingProps = {
36
46
  /** Absolute Y content offset (iOS only, for KeyboardChatScrollView). */
37
47
  contentOffsetY?: SharedValue<number>;
38
48
  applyWorkaroundForContentInsetHitTestBug?: boolean;
49
+ /**
50
+ * Fires whenever the effective content inset changes (combines the static `contentInset`
51
+ * prop with the dynamic keyboard-driven padding). Useful on Android where the synthetic
52
+ * inset is not reflected in `onScroll` events.
53
+ */
54
+ onContentInsetChange?: (insets: ScrollViewContentInsets) => void;
39
55
  } & ScrollViewProps;
40
56
 
41
57
  const ScrollViewWithBottomPadding = forwardRef<
@@ -52,6 +68,7 @@ const ScrollViewWithBottomPadding = forwardRef<
52
68
  inverted,
53
69
  contentOffsetY,
54
70
  applyWorkaroundForContentInsetHitTestBug,
71
+ onContentInsetChange,
55
72
  children,
56
73
  ...rest
57
74
  },
@@ -59,11 +76,52 @@ const ScrollViewWithBottomPadding = forwardRef<
59
76
  ) => {
60
77
  const prevContentOffsetY = useSharedValue<number | null>(null);
61
78
 
79
+ const insets = useDerivedValue(() => {
80
+ const dynamicTop = inverted ? bottomPadding.value : 0;
81
+ const dynamicBottom = !inverted ? bottomPadding.value : 0;
82
+
83
+ return {
84
+ dynamic: {
85
+ top: dynamicTop,
86
+ bottom: dynamicBottom,
87
+ },
88
+ effective: {
89
+ top: dynamicTop + (contentInset?.top || 0),
90
+ bottom: dynamicBottom + (contentInset?.bottom || 0),
91
+ left: contentInset?.left || 0,
92
+ right: contentInset?.right || 0,
93
+ } as ScrollViewContentInsets,
94
+ };
95
+ }, [
96
+ inverted,
97
+ contentInset?.top,
98
+ contentInset?.bottom,
99
+ contentInset?.left,
100
+ contentInset?.right,
101
+ ]);
102
+
103
+ useAnimatedReaction(
104
+ () => insets.value.effective,
105
+ (current, previous) => {
106
+ if (!onContentInsetChange) {
107
+ return;
108
+ }
109
+ if (
110
+ previous &&
111
+ current.top === previous.top &&
112
+ current.bottom === previous.bottom &&
113
+ current.left === previous.left &&
114
+ current.right === previous.right
115
+ ) {
116
+ return;
117
+ }
118
+ runOnJS(onContentInsetChange)(current);
119
+ },
120
+ [onContentInsetChange],
121
+ );
122
+
62
123
  const animatedProps = useAnimatedProps(() => {
63
- const insetTop = inverted ? bottomPadding.value : 0;
64
- const insetBottom = !inverted ? bottomPadding.value : 0;
65
- const bottom = insetBottom + (contentInset?.bottom || 0);
66
- const top = insetTop + (contentInset?.top || 0);
124
+ const { dynamic, effective } = insets.value;
67
125
 
68
126
  const indicatorPadding = scrollIndicatorPadding ?? bottomPadding;
69
127
  const indicatorTop =
@@ -75,12 +133,7 @@ const ScrollViewWithBottomPadding = forwardRef<
75
133
 
76
134
  const result: Record<string, unknown> = {
77
135
  // iOS prop
78
- contentInset: {
79
- bottom: bottom,
80
- top: top,
81
- right: contentInset?.right,
82
- left: contentInset?.left,
83
- },
136
+ contentInset: effective,
84
137
  scrollIndicatorInsets: {
85
138
  bottom: indicatorBottom,
86
139
  top: indicatorTop,
@@ -88,8 +141,8 @@ const ScrollViewWithBottomPadding = forwardRef<
88
141
  left: scrollIndicatorInsets?.left,
89
142
  },
90
143
  // Android prop
91
- contentInsetBottom: insetBottom,
92
- contentInsetTop: insetTop,
144
+ contentInsetBottom: dynamic.bottom,
145
+ contentInsetTop: dynamic.top,
93
146
  };
94
147
 
95
148
  if (contentOffsetY) {
@@ -104,10 +157,6 @@ const ScrollViewWithBottomPadding = forwardRef<
104
157
 
105
158
  return result;
106
159
  }, [
107
- contentInset?.bottom,
108
- contentInset?.top,
109
- contentInset?.right,
110
- contentInset?.left,
111
160
  scrollIndicatorInsets?.bottom,
112
161
  scrollIndicatorInsets?.top,
113
162
  scrollIndicatorInsets?.right,