react-native-keyboard-controller 1.21.11 → 1.21.12

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.
@@ -16,8 +16,7 @@ class KeyboardTransitionEvent(
16
16
  ) : Event<KeyboardTransitionEvent>(surfaceId, viewId) {
17
17
  override fun getEventName() = event.value
18
18
 
19
- // All events for a given view can be coalesced?
20
- override fun getCoalescingKey(): Short = 0
19
+ override fun canCoalesce() = false
21
20
 
22
21
  override fun getEventData(): WritableMap? =
23
22
  Arguments.createMap().apply {
@@ -64,8 +64,8 @@ class KeyboardAnimationCallback(
64
64
  private val surfaceId = UIManagerHelper.getSurfaceId(eventPropagationView)
65
65
 
66
66
  // state variables
67
- private var persistentKeyboardHeight = 0.0
68
- private var prevKeyboardHeight = 0.0
67
+ private var persistentKeyboardHeight = getCurrentKeyboardHeight()
68
+ private var prevKeyboardHeight = getCurrentKeyboardHeight()
69
69
  private var isKeyboardVisible = false
70
70
  private var isTransitioning = false
71
71
  private var duration = 0
@@ -170,11 +170,34 @@ class KeyboardAnimationCallback(
170
170
  Logger.i(TAG, "onApplyWindowInsets: ${this.persistentKeyboardHeight} -> $keyboardHeight")
171
171
  layoutObserver?.syncUpLayout()
172
172
  this.onKeyboardResized(keyboardHeight)
173
+
174
+ return insets
175
+ }
176
+
177
+ // always verify insets, because sometimes default lifecycle methods may not be invoked
178
+ // (when we press "Share" on Android 16, when Modal closes keyboard, when permission Alert presented etc.)
179
+ val newHeight = getCurrentKeyboardHeight(insets)
180
+ if (prevKeyboardHeight != newHeight && !isMoving && !isSuspended) {
181
+ Logger.w(
182
+ TAG,
183
+ "detected desynchronized state - force updating it: $newHeight",
184
+ )
185
+ this.syncKeyboardPosition(newHeight, newHeight > 0)
173
186
  }
174
187
 
175
188
  return insets
176
189
  }
177
190
 
191
+ override fun onPrepare(animation: WindowInsetsAnimationCompat) {
192
+ super.onPrepare(animation)
193
+
194
+ if (!animation.isKeyboardAnimation || isSuspended) {
195
+ return
196
+ }
197
+
198
+ isTransitioning = true
199
+ }
200
+
178
201
  @Suppress("detekt:ReturnCount")
179
202
  override fun onStart(
180
203
  animation: WindowInsetsAnimationCompat,
@@ -184,7 +207,6 @@ class KeyboardAnimationCallback(
184
207
  return bounds
185
208
  }
186
209
 
187
- isTransitioning = true
188
210
  pendingStartEvent = null
189
211
  isKeyboardVisible = isKeyboardVisible()
190
212
  duration = animation.durationMillis.toInt()
@@ -371,8 +393,13 @@ class KeyboardAnimationCallback(
371
393
  "KeyboardController::" + if (!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow",
372
394
  getEventParams(keyboardHeight),
373
395
  )
374
- // dispatch `onMove` to update RN animated value and `onEnd` to indicate that transition finished
375
- listOf(KeyboardTransitionEvent.Move, KeyboardTransitionEvent.End).forEach { eventName ->
396
+ // dispatch full lifecycle of onStart/onMove/onEnd events to update RN animated value and
397
+ // keep imperative hooks like `useKeyboardHandler` in sync
398
+ listOf(
399
+ KeyboardTransitionEvent.Start,
400
+ KeyboardTransitionEvent.Move,
401
+ KeyboardTransitionEvent.End,
402
+ ).forEach { eventName ->
376
403
  context.dispatchEvent(
377
404
  eventPropagationView.id,
378
405
  KeyboardTransitionEvent(
@@ -431,14 +458,15 @@ class KeyboardAnimationCallback(
431
458
  return insets?.isVisible(WindowInsetsCompat.Type.ime()) ?: false
432
459
  }
433
460
 
434
- private fun getCurrentKeyboardHeight(): Double {
435
- val insets = ViewCompat.getRootWindowInsets(view)
436
- val keyboardHeight = insets?.getInsets(WindowInsetsCompat.Type.ime())?.bottom ?: 0
461
+ private fun getCurrentKeyboardHeight(insets: WindowInsetsCompat? = null): Double {
462
+ val root = ViewCompat.getRootWindowInsets(view)
463
+ val final = insets ?: root
464
+ val keyboardHeight = final?.getInsets(WindowInsetsCompat.Type.ime())?.bottom ?: 0
437
465
  val navigationBar =
438
466
  if (config.hasTranslucentNavigationBar) {
439
467
  0
440
468
  } else {
441
- insets?.getInsets(WindowInsetsCompat.Type.navigationBars())?.bottom ?: 0
469
+ root?.getInsets(WindowInsetsCompat.Type.navigationBars())?.bottom ?: 0
442
470
  }
443
471
 
444
472
  // on hide it will be negative value, so we are using max function
@@ -82,14 +82,19 @@ class ModalAttachedWatcher(
82
82
  // position ourself, because callback can be attached after keyboard
83
83
  // auto-dismissal and we may miss some events and keyboard position
84
84
  // will be outdated
85
- callback.syncKeyboardPosition(0.0, false)
85
+ view.post {
86
+ callback.syncKeyboardPosition(0.0, false)
87
+ }
86
88
  }
87
89
 
88
90
  dialog?.setOnDismissListener {
89
91
  callback.syncKeyboardPosition()
90
92
  callback.destroy()
91
93
  eventView.removeSelf()
92
- this.callback()?.suspend(false)
94
+ // un-pause it in next frame because straight away `onApplyWindowInsets` will be called
95
+ view.post {
96
+ this.callback()?.suspend(false)
97
+ }
93
98
  }
94
99
 
95
100
  // imitating edge-to-edge mode behavior
@@ -1,12 +1,14 @@
1
1
  package com.reactnativekeyboardcontroller.views
2
2
 
3
3
  import android.annotation.SuppressLint
4
+ import android.view.MotionEvent
4
5
  import android.view.View
5
6
  import android.view.ViewGroup
6
7
  import android.widget.ScrollView
7
8
  import com.facebook.react.uimanager.ThemedReactContext
8
9
  import com.facebook.react.views.view.ReactViewGroup
9
10
  import com.reactnativekeyboardcontroller.extensions.px
11
+ import kotlin.math.max
10
12
 
11
13
  @SuppressLint("ViewConstructor")
12
14
  class ClippingScrollViewDecoratorView(
@@ -15,6 +17,7 @@ class ClippingScrollViewDecoratorView(
15
17
  private var insetBottom = 0.0
16
18
  private var insetTop = 0.0
17
19
  private var appliedTopInsetPx = 0
20
+ private var paddingScrollWorkaroundActive = false
18
21
 
19
22
  override fun onAttachedToWindow() {
20
23
  super.onAttachedToWindow()
@@ -22,6 +25,34 @@ class ClippingScrollViewDecoratorView(
22
25
  decorateScrollView()
23
26
  }
24
27
 
28
+ override fun dispatchTouchEvent(event: MotionEvent): Boolean {
29
+ val scrollView = findScrollView(this)
30
+
31
+ if (scrollView == null) {
32
+ return super.dispatchTouchEvent(event)
33
+ }
34
+
35
+ if (event.actionMasked == MotionEvent.ACTION_DOWN) {
36
+ paddingScrollWorkaroundActive = shouldUsePaddingScrollWorkaround(scrollView, event)
37
+ }
38
+
39
+ val handled =
40
+ if (paddingScrollWorkaroundActive) {
41
+ dispatchWithExpandedContentRange(scrollView, event)
42
+ } else {
43
+ super.dispatchTouchEvent(event)
44
+ }
45
+
46
+ if (
47
+ event.actionMasked == MotionEvent.ACTION_UP ||
48
+ event.actionMasked == MotionEvent.ACTION_CANCEL
49
+ ) {
50
+ paddingScrollWorkaroundActive = false
51
+ }
52
+
53
+ return handled
54
+ }
55
+
25
56
  fun setContentInsetBottom(value: Double) {
26
57
  insetBottom = value
27
58
  decorateScrollView()
@@ -68,6 +99,65 @@ class ClippingScrollViewDecoratorView(
68
99
  appliedTopInsetPx = newTopInsetPx
69
100
  }
70
101
 
102
+ private fun shouldUsePaddingScrollWorkaround(
103
+ scrollView: ScrollView,
104
+ event: MotionEvent,
105
+ ): Boolean {
106
+ val contentView = scrollView.getChildAt(0) ?: return false
107
+ val viewportHeight =
108
+ scrollView.height - scrollView.paddingTop - scrollView.paddingBottom
109
+ val paddingCreatesScrollRange = contentView.height > viewportHeight
110
+
111
+ return scrollView.scrollY == 0 &&
112
+ paddingCreatesScrollRange &&
113
+ !scrollView.canScrollVertically(1) &&
114
+ isTouchInScrollContent(scrollView, event)
115
+ }
116
+
117
+ private fun dispatchWithExpandedContentRange(
118
+ scrollView: ScrollView,
119
+ event: MotionEvent,
120
+ ): Boolean {
121
+ val contentView = scrollView.getChildAt(0)
122
+ val originalBottom = contentView?.bottom ?: 0
123
+ val expandedBottom =
124
+ max(originalBottom, scrollView.height + scrollView.scrollY + MIN_SCROLL_RANGE_PX)
125
+
126
+ if (contentView == null || expandedBottom == originalBottom) {
127
+ return super.dispatchTouchEvent(event)
128
+ }
129
+
130
+ return try {
131
+ contentView.bottom = expandedBottom
132
+ super.dispatchTouchEvent(event)
133
+ } finally {
134
+ contentView.bottom = originalBottom
135
+ }
136
+ }
137
+
138
+ private fun isTouchInScrollContent(
139
+ scrollView: ScrollView,
140
+ event: MotionEvent,
141
+ ): Boolean {
142
+ val contentView = scrollView.getChildAt(0) ?: return false
143
+ val thisLocation = IntArray(COORDINATES_SIZE)
144
+ val scrollViewLocation = IntArray(COORDINATES_SIZE)
145
+
146
+ getLocationOnScreen(thisLocation)
147
+ scrollView.getLocationOnScreen(scrollViewLocation)
148
+
149
+ val x = event.x + thisLocation[0] - scrollViewLocation[0]
150
+ val y = event.y + thisLocation[1] - scrollViewLocation[1]
151
+ val scrollY = scrollView.scrollY
152
+
153
+ return !(
154
+ y < contentView.top - scrollY ||
155
+ y >= contentView.bottom - scrollY ||
156
+ x < contentView.left ||
157
+ x >= contentView.right
158
+ )
159
+ }
160
+
71
161
  private fun findScrollView(view: View?): ScrollView? {
72
162
  var result: ScrollView? = null
73
163
 
@@ -83,4 +173,9 @@ class ClippingScrollViewDecoratorView(
83
173
 
84
174
  return result
85
175
  }
176
+
177
+ companion object {
178
+ private const val COORDINATES_SIZE = 2
179
+ private const val MIN_SCROLL_RANGE_PX = 2
180
+ }
86
181
  }
@@ -72,7 +72,15 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({
72
72
  inverted,
73
73
  onEndVisible
74
74
  });
75
- const totalPadding = (0, _reactNativeReanimated.useDerivedValue)(() => Math.max(blankSpace.value, padding.value + extraContentPadding.value));
75
+
76
+ // intentionally clamp `blankSpace` at one ScrollView viewport. The Android
77
+ // `ClippingScrollView` workaround temporarily substitutes padding/range
78
+ // during touch handling, and oversized blank ranges can de-sync during fast
79
+ // momentum gestures. One viewport is enough for the short-content case;
80
+ // larger values only allow scrolling to a fully blank screen which is not
81
+ // the use case for this library. If you found this comment and it causes
82
+ // a bug for you, please open an issue.
83
+ const totalPadding = (0, _reactNativeReanimated.useDerivedValue)(() => Math.min(layout.value.height, Math.max(blankSpace.value, padding.value + extraContentPadding.value)));
76
84
 
77
85
  // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace).
78
86
  // Apps that render into the unsafe area can supply a negative
@@ -1 +1 @@
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
+ {"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","min","height","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 // intentionally clamp `blankSpace` at one ScrollView viewport. The Android\n // `ClippingScrollView` workaround temporarily substitutes padding/range\n // during touch handling, and oversized blank ranges can de-sync during fast\n // momentum gestures. One viewport is enough for the short-content case;\n // larger values only allow scrolling to a fully blank screen which is not\n // the use case for this library. If you found this comment and it causes\n // a bug for you, please open an issue.\n const totalPadding = useDerivedValue(() =>\n Math.min(\n layout.value.height,\n Math.max(blankSpace.value, padding.value + extraContentPadding.value),\n ),\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":";;;;;;AAAA,IAAAA,MAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AACA,IAAAE,sBAAA,GAAAH,uBAAA,CAAAC,OAAA;AAQA,IAAAG,eAAA,GAAAC,sBAAA,CAAAJ,OAAA;AACA,IAAAK,4BAAA,GAAAD,sBAAA,CAAAJ,OAAA;AAEA,IAAAM,gBAAA,GAAAN,OAAA;AACA,IAAAO,cAAA,GAAAP,OAAA;AACA,IAAAQ,uBAAA,GAAAR,OAAA;AAAkE,SAAAI,uBAAAK,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAAA,SAAAV,wBAAAU,CAAA,EAAAG,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAd,uBAAA,YAAAA,CAAAU,CAAA,EAAAG,CAAA,SAAAA,CAAA,IAAAH,CAAA,IAAAA,CAAA,CAAAC,UAAA,SAAAD,CAAA,MAAAO,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAR,OAAA,EAAAF,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAS,CAAA,MAAAF,CAAA,GAAAJ,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAE,CAAA,CAAAI,GAAA,CAAAX,CAAA,UAAAO,CAAA,CAAAK,GAAA,CAAAZ,CAAA,GAAAO,CAAA,CAAAM,GAAA,CAAAb,CAAA,EAAAS,CAAA,gBAAAN,CAAA,IAAAH,CAAA,gBAAAG,CAAA,OAAAW,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAG,CAAA,OAAAK,CAAA,IAAAD,CAAA,GAAAS,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAG,CAAA,OAAAK,CAAA,CAAAI,GAAA,IAAAJ,CAAA,CAAAK,GAAA,IAAAN,CAAA,CAAAE,CAAA,EAAAN,CAAA,EAAAK,CAAA,IAAAC,CAAA,CAAAN,CAAA,IAAAH,CAAA,CAAAG,CAAA,WAAAM,CAAA,KAAAT,CAAA,EAAAG,CAAA;AAAA,SAAAgB,SAAA,WAAAA,QAAA,GAAAH,MAAA,CAAAI,MAAA,GAAAJ,MAAA,CAAAI,MAAA,CAAAC,IAAA,eAAAf,CAAA,aAAAN,CAAA,MAAAA,CAAA,GAAAsB,SAAA,CAAAC,MAAA,EAAAvB,CAAA,UAAAG,CAAA,GAAAmB,SAAA,CAAAtB,CAAA,YAAAK,CAAA,IAAAF,CAAA,OAAAW,cAAA,CAAAC,IAAA,CAAAZ,CAAA,EAAAE,CAAA,MAAAC,CAAA,CAAAD,CAAA,IAAAF,CAAA,CAAAE,CAAA,aAAAC,CAAA,KAAAa,QAAA,CAAAK,KAAA,OAAAF,SAAA;AAKlE,MAAMG,oBAAoB,GAAG,IAAAC,kCAAW,EAAC,CAAC,CAAC;AAC3C,MAAMC,gBAAgB,GAAG,IAAAD,kCAAW,EAAC,CAAC,CAAC;AAEvC,MAAME,sBAAsB,gBAAG,IAAAC,iBAAU,EAIvC,CACE;EACEC,QAAQ;EACRC,mBAAmB,GAAGC,8BAAU,CAACC,UAAU;EAC3CC,QAAQ,GAAG,KAAK;EAChBC,oBAAoB,GAAG,QAAQ;EAC/BC,MAAM,GAAG,KAAK;EACdC,MAAM,GAAG,CAAC;EACVC,mBAAmB,GAAGb,oBAAoB;EAC1Cc,UAAU,GAAGZ,gBAAgB;EAC7Ba,wCAAwC,GAAG,KAAK;EAChDC,QAAQ,EAAEC,YAAY;EACtBC,mBAAmB,EAAEC,uBAAuB;EAC5CC,YAAY;EACZ,GAAGC;AACL,CAAC,EACDC,GAAG,KACA;EACH,MAAMC,aAAa,GAAG,IAAAC,qCAAc,EAAwB,CAAC;EAC7D,MAAMC,KAAK,GAAG,IAAAC,uBAAc,EAACJ,GAAG,EAAEC,aAAa,CAAC;EAChD,MAAMI,QAAQ,GAAG,IAAAC,sCAAe,EAAC,MAC/B,OAAOjB,MAAM,KAAK,SAAS,GAAGA,MAAM,GAAGA,MAAM,CAACkB,KAChD,CAAC;EACD,MAAM;IACJC,OAAO;IACPC,aAAa;IACbC,cAAc;IACdC,MAAM;IACNC,MAAM;IACNC,IAAI;IACJnB,QAAQ,EAAEoB,gBAAgB;IAC1BlB,mBAAmB,EAAEmB;EACvB,CAAC,GAAG,IAAAC,gCAAe,EAACf,aAAa,EAAE;IACjCd,QAAQ;IACRC,oBAAoB;IACpBC,MAAM,EAAEgB,QAAQ;IAChBf,MAAM;IACNE,UAAU;IACVD;EACF,CAAC,CAAC;EAEF,IAAA0B,8CAAsB,EAAC;IACrBhB,aAAa;IACbV,mBAAmB;IACnB2B,eAAe,EAAEV,OAAO;IACxBhB,UAAU;IACVmB,MAAM;IACNC,MAAM;IACNC,IAAI;IACJH,cAAc;IACdvB,QAAQ;IACRC,oBAAoB;IACpBC,MAAM,EAAEgB;EACV,CAAC,CAAC;EAEF,IAAAc,4BAAa,EAAC;IACZR,MAAM;IACNC,MAAM;IACNC,IAAI;IACJ1B,QAAQ;IACRW;EACF,CAAC,CAAC;;EAEF;EACA;EACA;EACA;EACA;EACA;EACA;EACA,MAAMsB,YAAY,GAAG,IAAAd,sCAAe,EAAC,MACnCe,IAAI,CAACC,GAAG,CACNV,MAAM,CAACL,KAAK,CAACgB,MAAM,EACnBF,IAAI,CAACG,GAAG,CAAChC,UAAU,CAACe,KAAK,EAAEC,OAAO,CAACD,KAAK,GAAGhB,mBAAmB,CAACgB,KAAK,CACtE,CACF,CAAC;;EAED;EACA;EACA;EACA,MAAMkB,gBAAgB,GAAG,IAAAnB,sCAAe,EACtC,MAAME,OAAO,CAACD,KAAK,GAAGhB,mBAAmB,CAACgB,KAC5C,CAAC;EAED,MAAMb,QAAQ,GAAG,IAAAgC,kBAAW,EACzBzE,CAAoB,IAAK;IACxB6D,gBAAgB,CAAC7D,CAAC,CAAC;IACnB0C,YAAY,aAAZA,YAAY,eAAZA,YAAY,CAAG1C,CAAC,CAAC;EACnB,CAAC,EACD,CAAC6D,gBAAgB,EAAEnB,YAAY,CACjC,CAAC;EAED,MAAMC,mBAAmB,GAAG,IAAA8B,kBAAW,EACrC,CAACC,CAAS,EAAEC,CAAS,KAAK;IACxBb,2BAA2B,CAACY,CAAC,EAAEC,CAAC,CAAC;IACjC/B,uBAAuB,aAAvBA,uBAAuB,eAAvBA,uBAAuB,CAAG8B,CAAC,EAAEC,CAAC,CAAC;EACjC,CAAC,EACD,CAACb,2BAA2B,EAAElB,uBAAuB,CACvD,CAAC;;EAED;EACA;EACA;EACA;EACA,MAAMgC,WAAW,GAAG,IAAAC,uCAAgB,EAClC,OAAO;IACLC,SAAS,EAAE,CAAC;MAAEC,UAAU,EAAE,CAACvB,aAAa,CAACF;IAAM,CAAC;EAClD,CAAC,CAAC,EACF,EACF,CAAC;EACD,MAAM0B,MAAM,GAAG,IAAAC,cAAO,EACpB,MAAM,CAACC,MAAM,CAACC,UAAU,EAAEP,WAAW,CAAC,EACtC,CAACA,WAAW,CACd,CAAC;EAED,oBACEvF,MAAA,CAAAa,OAAA,CAAAkF,aAAA,CAAA/F,MAAA,CAAAa,OAAA,CAAAmF,QAAA,qBACEhG,MAAA,CAAAa,OAAA,CAAAkF,aAAA,CAACxF,4BAAA,CAAAM,OAA2B,EAAAiB,QAAA;IAC1B4B,GAAG,EAAEG;EAAM,GACPJ,IAAI;IACRN,wCAAwC,EACtCA,wCACD;IACD8C,aAAa,EAAEnB,YAAa;IAC5BV,cAAc,EAAEA,cAAe;IAC/BvB,QAAQ,EAAEA,QAAS;IACnBqD,sBAAsB,EAAEf,gBAAiB;IACzCzC,mBAAmB,EAAEA,mBAAoB;IACzCY,mBAAmB,EAAEA,mBAAoB;IACzCF,QAAQ,EAAEA;EAAS,IAElBX,QAC0B,CAAC,eAC9BzC,MAAA,CAAAa,OAAA,CAAAkF,aAAA,CAAC3F,sBAAA,CAAAS,OAAU,CAACsF,IAAI;IAACC,KAAK,EAAET;EAAO,CAAE,CACjC,CAAC;AAEP,CACF,CAAC;AAED,MAAME,MAAM,GAAGQ,uBAAU,CAACC,MAAM,CAAC;EAC/BR,UAAU,EAAE;IACVS,OAAO,EAAE,MAAM;IACfC,QAAQ,EAAE;EACZ;AACF,CAAC,CAAC;AAAC,IAAAC,QAAA,GAAAC,OAAA,CAAA7F,OAAA,GAEY0B,sBAAsB","ignoreList":[]}
@@ -76,9 +76,15 @@ const ScrollViewWithBottomPadding = /*#__PURE__*/(0, _react.forwardRef)(({
76
76
  };
77
77
  if (contentOffsetY) {
78
78
  const curr = contentOffsetY.value;
79
- if (curr !== prevContentOffsetY.value) {
79
+ if (prevContentOffsetY.value === null) {
80
+ // Swallow the initial evaluation: emitting `contentOffset {x:0,y:0}`
81
+ // in the first animatedProps run overrides the wrapped ScrollView's
82
+ // own `contentOffset` prop on Fabric (e.g. a list's initial scroll
83
+ // offset), making the list mount scrolled to the top natively.
80
84
  // eslint-disable-next-line react-compiler/react-compiler
81
85
  prevContentOffsetY.value = curr;
86
+ } else if (curr !== prevContentOffsetY.value) {
87
+ prevContentOffsetY.value = curr;
82
88
  result.contentOffset = {
83
89
  x: 0,
84
90
  y: curr
@@ -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","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":[]}
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 (prevContentOffsetY.value === null) {\n // Swallow the initial evaluation: emitting `contentOffset {x:0,y:0}`\n // in the first animatedProps run overrides the wrapped ScrollView's\n // own `contentOffset` prop on Fabric (e.g. a list's initial scroll\n // offset), making the list mount scrolled to the top natively.\n // eslint-disable-next-line react-compiler/react-compiler\n prevContentOffsetY.value = curr;\n } else if (curr !== prevContentOffsetY.value) {\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":[]}
@@ -65,7 +65,15 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({
65
65
  inverted,
66
66
  onEndVisible
67
67
  });
68
- const totalPadding = useDerivedValue(() => Math.max(blankSpace.value, padding.value + extraContentPadding.value));
68
+
69
+ // intentionally clamp `blankSpace` at one ScrollView viewport. The Android
70
+ // `ClippingScrollView` workaround temporarily substitutes padding/range
71
+ // during touch handling, and oversized blank ranges can de-sync during fast
72
+ // momentum gestures. One viewport is enough for the short-content case;
73
+ // larger values only allow scrolling to a fully blank screen which is not
74
+ // the use case for this library. If you found this comment and it causes
75
+ // a bug for you, please open an issue.
76
+ const totalPadding = useDerivedValue(() => Math.min(layout.value.height, Math.max(blankSpace.value, padding.value + extraContentPadding.value)));
69
77
 
70
78
  // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace).
71
79
  // Apps that render into the unsafe area can supply a negative
@@ -1 +1 @@
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
+ {"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","min","height","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 // intentionally clamp `blankSpace` at one ScrollView viewport. The Android\n // `ClippingScrollView` workaround temporarily substitutes padding/range\n // during touch handling, and oversized blank ranges can de-sync during fast\n // momentum gestures. One viewport is enough for the short-content case;\n // larger values only allow scrolling to a fully blank screen which is not\n // the use case for this library. If you found this comment and it causes\n // a bug for you, please open an issue.\n const totalPadding = useDerivedValue(() =>\n Math.min(\n layout.value.height,\n Math.max(blankSpace.value, padding.value + extraContentPadding.value),\n ),\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":[]}
@@ -68,9 +68,15 @@ const ScrollViewWithBottomPadding = /*#__PURE__*/forwardRef(({
68
68
  };
69
69
  if (contentOffsetY) {
70
70
  const curr = contentOffsetY.value;
71
- if (curr !== prevContentOffsetY.value) {
71
+ if (prevContentOffsetY.value === null) {
72
+ // Swallow the initial evaluation: emitting `contentOffset {x:0,y:0}`
73
+ // in the first animatedProps run overrides the wrapped ScrollView's
74
+ // own `contentOffset` prop on Fabric (e.g. a list's initial scroll
75
+ // offset), making the list mount scrolled to the top natively.
72
76
  // eslint-disable-next-line react-compiler/react-compiler
73
77
  prevContentOffsetY.value = curr;
78
+ } else if (curr !== prevContentOffsetY.value) {
79
+ prevContentOffsetY.value = curr;
74
80
  result.contentOffset = {
75
81
  x: 0,
76
82
  y: curr
@@ -1 +1 @@
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":[]}
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 (prevContentOffsetY.value === null) {\n // Swallow the initial evaluation: emitting `contentOffset {x:0,y:0}`\n // in the first animatedProps run overrides the wrapped ScrollView's\n // own `contentOffset` prop on Fabric (e.g. a list's initial scroll\n // offset), making the list mount scrolled to the top natively.\n // eslint-disable-next-line react-compiler/react-compiler\n prevContentOffsetY.value = curr;\n } else if (curr !== prevContentOffsetY.value) {\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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-keyboard-controller",
3
- "version": "1.21.11",
3
+ "version": "1.21.12",
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",
@@ -88,8 +88,18 @@ const KeyboardChatScrollView = forwardRef<
88
88
  onEndVisible,
89
89
  });
90
90
 
91
+ // intentionally clamp `blankSpace` at one ScrollView viewport. The Android
92
+ // `ClippingScrollView` workaround temporarily substitutes padding/range
93
+ // during touch handling, and oversized blank ranges can de-sync during fast
94
+ // momentum gestures. One viewport is enough for the short-content case;
95
+ // larger values only allow scrolling to a fully blank screen which is not
96
+ // the use case for this library. If you found this comment and it causes
97
+ // a bug for you, please open an issue.
91
98
  const totalPadding = useDerivedValue(() =>
92
- Math.max(blankSpace.value, padding.value + extraContentPadding.value),
99
+ Math.min(
100
+ layout.value.height,
101
+ Math.max(blankSpace.value, padding.value + extraContentPadding.value),
102
+ ),
93
103
  );
94
104
 
95
105
  // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace).
@@ -148,9 +148,15 @@ const ScrollViewWithBottomPadding = forwardRef<
148
148
  if (contentOffsetY) {
149
149
  const curr = contentOffsetY.value;
150
150
 
151
- if (curr !== prevContentOffsetY.value) {
151
+ if (prevContentOffsetY.value === null) {
152
+ // Swallow the initial evaluation: emitting `contentOffset {x:0,y:0}`
153
+ // in the first animatedProps run overrides the wrapped ScrollView's
154
+ // own `contentOffset` prop on Fabric (e.g. a list's initial scroll
155
+ // offset), making the list mount scrolled to the top natively.
152
156
  // eslint-disable-next-line react-compiler/react-compiler
153
157
  prevContentOffsetY.value = curr;
158
+ } else if (curr !== prevContentOffsetY.value) {
159
+ prevContentOffsetY.value = curr;
154
160
  result.contentOffset = { x: 0, y: curr };
155
161
  }
156
162
  }