react-native-keyboard-controller 1.21.11 → 1.21.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt +1 -2
- package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt +1 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt +37 -9
- package/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt +7 -2
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/ClippingScrollViewDecoratorView.kt +95 -0
- package/ios/core/KeyboardControllerConfiguration.swift +16 -0
- package/ios/delegates/KCTextInputCompositeDelegate.swift +37 -18
- package/ios/objc/{UITextView+DelegateManager.h → TextInput+DelegateManager.h} +7 -1
- package/ios/objc/TextInput+DelegateManager.m +55 -0
- package/ios/observers/FocusedInputObserver.swift +3 -5
- package/ios/observers/movement/KeyboardTrackingView.swift +6 -4
- package/lib/commonjs/components/KeyboardChatScrollView/index.js +9 -1
- package/lib/commonjs/components/KeyboardChatScrollView/index.js.map +1 -1
- package/lib/commonjs/components/ScrollViewWithBottomPadding/index.js +7 -1
- package/lib/commonjs/components/ScrollViewWithBottomPadding/index.js.map +1 -1
- package/lib/module/components/KeyboardChatScrollView/index.js +9 -1
- package/lib/module/components/KeyboardChatScrollView/index.js.map +1 -1
- package/lib/module/components/ScrollViewWithBottomPadding/index.js +7 -1
- package/lib/module/components/ScrollViewWithBottomPadding/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/KeyboardChatScrollView/index.tsx +11 -1
- package/src/components/ScrollViewWithBottomPadding/index.tsx +7 -1
- package/ios/objc/UITextView+DelegateManager.m +0 -26
package/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt
CHANGED
|
@@ -16,8 +16,7 @@ class KeyboardTransitionEvent(
|
|
|
16
16
|
) : Event<KeyboardTransitionEvent>(surfaceId, viewId) {
|
|
17
17
|
override fun getEventName() = event.value
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
override fun getCoalescingKey(): Short = 0
|
|
19
|
+
override fun canCoalesce() = false
|
|
21
20
|
|
|
22
21
|
override fun getEventData(): WritableMap? =
|
|
23
22
|
Arguments.createMap().apply {
|
package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt
CHANGED
|
@@ -106,7 +106,7 @@ class FocusedInputObserver(
|
|
|
106
106
|
// remove it asynchronously to avoid crash in stripe input
|
|
107
107
|
// see https://github.com/stripe/stripe-android/issues/10178
|
|
108
108
|
input.post {
|
|
109
|
-
input.removeTextChangedListener(
|
|
109
|
+
watcher?.let { input.removeTextChangedListener(it) }
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
selectionSubscription?.invoke()
|
|
@@ -64,8 +64,8 @@ class KeyboardAnimationCallback(
|
|
|
64
64
|
private val surfaceId = UIManagerHelper.getSurfaceId(eventPropagationView)
|
|
65
65
|
|
|
66
66
|
// state variables
|
|
67
|
-
private var persistentKeyboardHeight =
|
|
68
|
-
private var prevKeyboardHeight =
|
|
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
|
|
375
|
-
|
|
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
|
|
436
|
-
val
|
|
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
|
-
|
|
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
|
package/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// KeyboardControllerConfiguration.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Kiryl Ziusko on 25/06/2026.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
enum KeyboardControllerConfiguration {
|
|
9
|
+
static var usesKeyboardLayoutGuideTracking: Bool {
|
|
10
|
+
if #available(iOS 26.0, *) {
|
|
11
|
+
return true
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -65,7 +65,7 @@ class KCTextInputCompositeDelegate: NSObject, UITextViewDelegate, UITextFieldDel
|
|
|
65
65
|
// MARK: setters/getters
|
|
66
66
|
|
|
67
67
|
func setTextViewDelegate(delegate: UITextViewDelegate?) {
|
|
68
|
-
// remove KVO from any old
|
|
68
|
+
// remove KVO from any old textField
|
|
69
69
|
if let oldTextField = observedTextFieldForSelection {
|
|
70
70
|
removeSelectionRangeObserver(from: oldTextField)
|
|
71
71
|
}
|
|
@@ -96,21 +96,6 @@ class KCTextInputCompositeDelegate: NSObject, UITextViewDelegate, UITextFieldDel
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
func canSubstituteTextFieldDelegate(delegate: UITextFieldDelegate?) -> Bool {
|
|
100
|
-
let type = String(describing: delegate)
|
|
101
|
-
if type.range(of: "SQIPTextFieldInputModifier") != nil {
|
|
102
|
-
// SQIPTextFieldInputModifier is a private class used internally by Square.
|
|
103
|
-
// It forwards input events to the keyboard-controller delegate.
|
|
104
|
-
// To prevent an infinite loop, we avoid setting our delegate in this case.
|
|
105
|
-
// Since Square's SDK is used imperatively and doesn't allow adding custom components,
|
|
106
|
-
// keyboard-controller components cannot be used in this context,
|
|
107
|
-
// so it's safe to skip replacing the delegate.
|
|
108
|
-
return false
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return true
|
|
112
|
-
}
|
|
113
|
-
|
|
114
99
|
/// Getter for the active delegate
|
|
115
100
|
var activeDelegate: AnyObject? {
|
|
116
101
|
return textViewDelegate ?? textFieldDelegate
|
|
@@ -208,15 +193,49 @@ class KCTextInputCompositeDelegate: NSObject, UITextViewDelegate, UITextFieldDel
|
|
|
208
193
|
|
|
209
194
|
// MARK: call forwarding
|
|
210
195
|
|
|
196
|
+
/// Decides whether `aSelector` should be forwarded to `delegate`.
|
|
197
|
+
///
|
|
198
|
+
/// Usually we stay fully transparent and forward every selector the
|
|
199
|
+
/// underlying delegate responds to. The exception is a field that acts as its
|
|
200
|
+
/// own UIKit delegate and relays callbacks to an inner delegate through
|
|
201
|
+
/// `forwardInvocation:` (e.g. `BKForwardingTextField`). For such a field
|
|
202
|
+
/// `activeDelegate` is the text input *itself*, so it also "responds" to the
|
|
203
|
+
/// many `UITextField` / `UITextView` methods that UIKit relays to its delegate
|
|
204
|
+
/// (e.g. `keyboardInputChangedSelection:`). Forwarding one of those back to the
|
|
205
|
+
/// field makes the field relay it to us again — an infinite recursion.
|
|
206
|
+
///
|
|
207
|
+
/// To stay transparent without looping, when the delegate is the text input
|
|
208
|
+
/// itself we forward only the selectors it handles *dynamically* (i.e. the
|
|
209
|
+
/// ones it routes to its inner delegate via its own message forwarding), and
|
|
210
|
+
/// never the ones it implements as a UIKit object. `instancesRespond(to:)`
|
|
211
|
+
/// inspects the real method table only, so it is `false` exactly for the
|
|
212
|
+
/// selectors resolved through the field's own `forwardInvocation:`.
|
|
213
|
+
private func shouldForward(_ aSelector: Selector!, to delegate: AnyObject) -> Bool {
|
|
214
|
+
guard delegate.responds(to: aSelector) else {
|
|
215
|
+
return false
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if delegate is UITextField || delegate is UITextView,
|
|
219
|
+
let delegateClass = type(of: delegate) as? NSObject.Type
|
|
220
|
+
{
|
|
221
|
+
return !delegateClass.instancesRespond(to: aSelector)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return true
|
|
225
|
+
}
|
|
226
|
+
|
|
211
227
|
override func responds(to aSelector: Selector!) -> Bool {
|
|
212
228
|
if super.responds(to: aSelector) {
|
|
213
229
|
return true
|
|
214
230
|
}
|
|
215
|
-
|
|
231
|
+
guard let activeDelegate = activeDelegate else {
|
|
232
|
+
return false
|
|
233
|
+
}
|
|
234
|
+
return shouldForward(aSelector, to: activeDelegate)
|
|
216
235
|
}
|
|
217
236
|
|
|
218
237
|
override func forwardingTarget(for aSelector: Selector!) -> Any? {
|
|
219
|
-
if activeDelegate
|
|
238
|
+
if let activeDelegate = activeDelegate, shouldForward(aSelector, to: activeDelegate) {
|
|
220
239
|
return activeDelegate
|
|
221
240
|
}
|
|
222
241
|
return super.forwardingTarget(for: aSelector)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//
|
|
2
|
-
//
|
|
2
|
+
// TextInput+DelegateManager.h
|
|
3
3
|
// react-native-keyboard-controller
|
|
4
4
|
//
|
|
5
5
|
// Created by Kiryl Ziusko on 24/04/2024.
|
|
@@ -10,3 +10,9 @@
|
|
|
10
10
|
- (void)setForceDelegate:(id<UITextViewDelegate>)delegate;
|
|
11
11
|
|
|
12
12
|
@end
|
|
13
|
+
|
|
14
|
+
@interface UITextField (DelegateManager)
|
|
15
|
+
|
|
16
|
+
- (void)setForceDelegate:(id<UITextFieldDelegate>)delegate;
|
|
17
|
+
|
|
18
|
+
@end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//
|
|
2
|
+
// TextInput+DelegateManager.m
|
|
3
|
+
// react-native-keyboard-controller
|
|
4
|
+
//
|
|
5
|
+
// Created by Kiryl Ziusko on 24/04/2024.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import <objc/runtime.h>
|
|
9
|
+
#import "TextInput+DelegateManager.h"
|
|
10
|
+
|
|
11
|
+
@implementation UITextView (DelegateManager)
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* We are aware that delegate is set by RN on mount and we can not re-define it
|
|
15
|
+
* but we are using composite delegate to forward all calls to original delegate
|
|
16
|
+
* so we use this method to force set our delegate
|
|
17
|
+
*/
|
|
18
|
+
- (void)setForceDelegate:(id<UITextViewDelegate>)delegate
|
|
19
|
+
{
|
|
20
|
+
Ivar ivar = class_getInstanceVariable([UITextView class], "_delegate");
|
|
21
|
+
if (ivar) {
|
|
22
|
+
object_setIvar(self, ivar, delegate);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@end
|
|
27
|
+
|
|
28
|
+
@implementation UITextField (DelegateManager)
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Some `UITextField` subclasses (e.g. card-input fields from third-party SDKs, aka Square)
|
|
32
|
+
* make themselves their own UIKit delegate via `[super setDelegate:self]` and
|
|
33
|
+
* override `setDelegate:` so that the public setter only stores an *inner*
|
|
34
|
+
* delegate, forwarding UIKit callbacks to it via `forwardInvocation:`.
|
|
35
|
+
*
|
|
36
|
+
* For such fields the regular `textField.delegate = composite` does NOT replace
|
|
37
|
+
* the real UIKit delegate slot — it only points the field's inner delegate at us
|
|
38
|
+
* while the field itself stays the real delegate. Combined with the composite
|
|
39
|
+
* forwarding unhandled calls back to that same field, this creates an infinite
|
|
40
|
+
* recursion (it blows the stack already inside `respondsToSelector:`).
|
|
41
|
+
*
|
|
42
|
+
* Writing the `_delegate` ivar directly installs the composite as the genuine
|
|
43
|
+
* UIKit delegate without touching the field's inner delegate slot, so no
|
|
44
|
+
* back-pointer to the composite is ever created and the chain stays acyclic:
|
|
45
|
+
* `UIKit -> composite -> field -> field's original inner delegate`.
|
|
46
|
+
*/
|
|
47
|
+
- (void)setForceDelegate:(id<UITextFieldDelegate>)delegate
|
|
48
|
+
{
|
|
49
|
+
Ivar ivar = class_getInstanceVariable([UITextField class], "_delegate");
|
|
50
|
+
if (ivar) {
|
|
51
|
+
object_setIvar(self, ivar, delegate);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@end
|
|
@@ -251,11 +251,9 @@ public class FocusedInputObserver: NSObject {
|
|
|
251
251
|
|
|
252
252
|
private func substituteDelegate(_ input: UIResponder?) {
|
|
253
253
|
if let textField = input as? UITextField {
|
|
254
|
-
if !(textField.delegate is KCTextInputCompositeDelegate)
|
|
255
|
-
delegate.canSubstituteTextFieldDelegate(delegate: textField.delegate)
|
|
256
|
-
{
|
|
254
|
+
if !(textField.delegate is KCTextInputCompositeDelegate) {
|
|
257
255
|
delegate.setTextFieldDelegate(delegate: textField.delegate, textField: textField)
|
|
258
|
-
textField.delegate
|
|
256
|
+
textField.setForceDelegate(delegate)
|
|
259
257
|
}
|
|
260
258
|
} else if let textView = input as? UITextView {
|
|
261
259
|
if !(textView.delegate is KCTextInputCompositeDelegate) {
|
|
@@ -267,7 +265,7 @@ public class FocusedInputObserver: NSObject {
|
|
|
267
265
|
|
|
268
266
|
private func substituteDelegateBack(_ input: UIResponder?) {
|
|
269
267
|
if let textField = input as? UITextField, let oldDelegate = delegate.activeDelegate as? UITextFieldDelegate {
|
|
270
|
-
textField.
|
|
268
|
+
textField.setForceDelegate(oldDelegate)
|
|
271
269
|
} else if let textView = input as? UITextView, let oldDelegate = delegate.activeDelegate as? UITextViewDelegate {
|
|
272
270
|
textView.setForceDelegate(oldDelegate)
|
|
273
271
|
}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
import UIKit
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* A compatibility view that resolves to `KeyboardView`
|
|
12
|
-
* and uses `keyboardLayoutGuide`
|
|
11
|
+
* A compatibility view that resolves to `KeyboardView` for the legacy path
|
|
12
|
+
* and uses `keyboardLayoutGuide` for configured iOS 26+ behavior.
|
|
13
13
|
*/
|
|
14
14
|
public final class KeyboardTrackingView: UIView {
|
|
15
15
|
private var keyboardView: UIView? {
|
|
@@ -69,6 +69,8 @@ public final class KeyboardTrackingView: UIView {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
@objc public func attachToTopmostView(toWindow window: UIWindow? = nil) {
|
|
72
|
+
guard KeyboardControllerConfiguration.usesKeyboardLayoutGuideTracking else { return }
|
|
73
|
+
|
|
72
74
|
var topViewController = window?.rootViewController
|
|
73
75
|
if let rootVC = topViewController, let topView = rootVC.view, topView.window != nil {
|
|
74
76
|
// ok, attach
|
|
@@ -120,7 +122,7 @@ public final class KeyboardTrackingView: UIView {
|
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
@objc var view: UIView? {
|
|
123
|
-
if
|
|
125
|
+
if KeyboardControllerConfiguration.usesKeyboardLayoutGuideTracking {
|
|
124
126
|
return self
|
|
125
127
|
} else {
|
|
126
128
|
return keyboardView
|
|
@@ -135,7 +137,7 @@ public final class KeyboardTrackingView: UIView {
|
|
|
135
137
|
let keyboardPosition = keyboardWindowH - keyboardFrameY
|
|
136
138
|
|
|
137
139
|
// for `keyboardLayoutGuide` case we can just read keyboard position directly - no interpolation needed
|
|
138
|
-
if
|
|
140
|
+
if KeyboardControllerConfiguration.usesKeyboardLayoutGuideTracking {
|
|
139
141
|
if keyboardPosition > keyboardHeight {
|
|
140
142
|
return Self.invalidPosition
|
|
141
143
|
}
|
|
@@ -72,7 +72,15 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
|
72
72
|
inverted,
|
|
73
73
|
onEndVisible
|
|
74
74
|
});
|
|
75
|
-
|
|
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":"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","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 (
|
|
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
|
-
|
|
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":";AAAA,OAAOA,KAAK,IAAIC,UAAU,EAAEC,WAAW,EAAEC,OAAO,QAAQ,OAAO;AAC/D,SAASC,UAAU,QAAQ,cAAc;AACzC,SACEC,WAAW,EACXC,cAAc,EACdC,gBAAgB,EAChBC,eAAe,QACV,yBAAyB;AAChC,OAAOC,UAAU,MAAM,yBAAyB;AAEhD,OAAOC,cAAc,MAAM,yBAAyB;AACpD,OAAOC,2BAA2B,MAAM,gCAAgC;AAExE,SAASC,eAAe,QAAQ,mBAAmB;AACnD,SAASC,aAAa,QAAQ,iBAAiB;AAC/C,SAASC,sBAAsB,QAAQ,0BAA0B;AAKjE,MAAMC,oBAAoB,GAAGV,WAAW,CAAC,CAAC,CAAC;AAC3C,MAAMW,gBAAgB,GAAGX,WAAW,CAAC,CAAC,CAAC;AAEvC,MAAMY,sBAAsB,gBAAGhB,UAAU,CAIvC,CACE;EACEiB,QAAQ;EACRC,mBAAmB,GAAGV,UAAU,CAACW,UAAU;EAC3CC,QAAQ,GAAG,KAAK;EAChBC,oBAAoB,GAAG,QAAQ;EAC/BC,MAAM,GAAG,KAAK;EACdC,MAAM,GAAG,CAAC;EACVC,mBAAmB,GAAGV,oBAAoB;EAC1CW,UAAU,GAAGV,gBAAgB;EAC7BW,wCAAwC,GAAG,KAAK;EAChDC,QAAQ,EAAEC,YAAY;EACtBC,mBAAmB,EAAEC,uBAAuB;EAC5CC,YAAY;EACZ,GAAGC;AACL,CAAC,EACDC,GAAG,KACA;EACH,MAAMC,aAAa,GAAG7B,cAAc,CAAwB,CAAC;EAC7D,MAAM8B,KAAK,GAAG1B,cAAc,CAACwB,GAAG,EAAEC,aAAa,CAAC;EAChD,MAAME,QAAQ,GAAG7B,eAAe,CAAC,MAC/B,OAAOe,MAAM,KAAK,SAAS,GAAGA,MAAM,GAAGA,MAAM,CAACe,KAChD,CAAC;EACD,MAAM;IACJC,OAAO;IACPC,aAAa;IACbC,cAAc;IACdC,MAAM;IACNC,MAAM;IACNC,IAAI;IACJhB,QAAQ,EAAEiB,gBAAgB;IAC1Bf,mBAAmB,EAAEgB;EACvB,CAAC,GAAGlC,eAAe,CAACuB,aAAa,EAAE;IACjCd,QAAQ;IACRC,oBAAoB;IACpBC,MAAM,EAAEc,QAAQ;IAChBb,MAAM;IACNE,UAAU;IACVD;EACF,CAAC,CAAC;EAEFX,sBAAsB,CAAC;IACrBqB,aAAa;IACbV,mBAAmB;IACnBsB,eAAe,EAAER,OAAO;IACxBb,UAAU;IACVgB,MAAM;IACNC,MAAM;IACNC,IAAI;IACJH,cAAc;IACdpB,QAAQ;IACRC,oBAAoB;IACpBC,MAAM,EAAEc;EACV,CAAC,CAAC;EAEFxB,aAAa,CAAC;IACZ6B,MAAM;IACNC,MAAM;IACNC,IAAI;IACJvB,QAAQ;IACRW;EACF,CAAC,CAAC;
|
|
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 (
|
|
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
|
@@ -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.
|
|
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 (
|
|
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
|
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// UITextView+DelegateManager.m
|
|
3
|
-
// react-native-keyboard-controller
|
|
4
|
-
//
|
|
5
|
-
// Created by Kiryl Ziusko on 24/04/2024.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
#import <objc/runtime.h>
|
|
9
|
-
#import "UITextView+DelegateManager.h"
|
|
10
|
-
|
|
11
|
-
@implementation UITextView (DelegateManager)
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* We are aware that delegate is set by RN on mount and we can not re-define it
|
|
15
|
-
* but we are using composite delegate to forward all calls to original delegate
|
|
16
|
-
* so we use this method to force set our delegate
|
|
17
|
-
*/
|
|
18
|
-
- (void)setForceDelegate:(id<UITextViewDelegate>)delegate
|
|
19
|
-
{
|
|
20
|
-
Ivar ivar = class_getInstanceVariable([UITextView class], "_delegate");
|
|
21
|
-
if (ivar) {
|
|
22
|
-
object_setIvar(self, ivar, delegate);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@end
|