react-native-keyboard-controller 1.5.7 → 1.6.0
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/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +3 -1
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +2 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardAnimationCallback.kt +130 -18
- package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardAnimationController.kt +10 -2
- package/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt +11 -2
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +7 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +2 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardGestureAreaViewManagerImpl.kt +1 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +6 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/StatusBarManagerCompatModuleImpl.kt +9 -3
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +44 -27
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/KeyboardGestureAreaReactViewGroup.kt +121 -85
- package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +6 -4
- package/android/src/paper/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +2 -1
- package/ios/Extensions.swift +31 -0
- package/ios/KeyboardController-Bridging-Header.h +1 -0
- package/ios/KeyboardControllerView.mm +25 -6
- package/ios/KeyboardControllerViewManager.swift +4 -2
- package/ios/KeyboardMoveEvent.h +3 -1
- package/ios/KeyboardMoveEvent.m +8 -0
- package/ios/KeyboardMovementObserver.swift +64 -12
- package/lib/commonjs/animated.js +4 -3
- package/lib/commonjs/animated.js.map +1 -1
- package/lib/commonjs/bindings.js +26 -0
- package/lib/commonjs/bindings.js.map +1 -0
- package/lib/commonjs/bindings.native.js +33 -0
- package/lib/commonjs/bindings.native.js.map +1 -0
- package/lib/commonjs/{native.js → constants.js} +2 -29
- package/lib/commonjs/constants.js.map +1 -0
- package/lib/commonjs/context.js +9 -1
- package/lib/commonjs/context.js.map +1 -1
- package/lib/commonjs/hooks.js +7 -6
- package/lib/commonjs/hooks.js.map +1 -1
- package/lib/commonjs/index.js +15 -4
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/internal.js +0 -30
- package/lib/commonjs/internal.js.map +1 -1
- package/lib/commonjs/monkey-patch.android.js +34 -0
- package/lib/commonjs/monkey-patch.android.js.map +1 -0
- package/lib/commonjs/monkey-patch.js +1 -35
- package/lib/commonjs/monkey-patch.js.map +1 -1
- package/lib/commonjs/reanimated.js +9 -0
- package/lib/commonjs/reanimated.js.map +1 -0
- package/lib/commonjs/reanimated.native.js +37 -0
- package/lib/commonjs/reanimated.native.js.map +1 -0
- package/lib/commonjs/replicas.js +4 -3
- package/lib/commonjs/replicas.js.map +1 -1
- package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/module/animated.js +3 -2
- package/lib/module/animated.js.map +1 -1
- package/lib/module/bindings.js +16 -0
- package/lib/module/bindings.js.map +1 -0
- package/lib/module/bindings.native.js +23 -0
- package/lib/module/bindings.native.js.map +1 -0
- package/lib/module/{native.js → constants.js} +1 -24
- package/lib/module/constants.js.map +1 -0
- package/lib/module/context.js +8 -1
- package/lib/module/context.js.map +1 -1
- package/lib/module/hooks.js +7 -6
- package/lib/module/hooks.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal.js +1 -30
- package/lib/module/internal.js.map +1 -1
- package/lib/module/monkey-patch.android.js +29 -0
- package/lib/module/monkey-patch.android.js.map +1 -0
- package/lib/module/monkey-patch.js +1 -31
- package/lib/module/monkey-patch.js.map +1 -1
- package/lib/module/reanimated.js +2 -0
- package/lib/module/reanimated.js.map +1 -0
- package/lib/module/reanimated.native.js +30 -0
- package/lib/module/reanimated.native.js.map +1 -0
- package/lib/module/replicas.js +2 -1
- package/lib/module/replicas.js.map +1 -1
- package/lib/module/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/bindings.d.ts +6 -0
- package/lib/typescript/bindings.native.d.ts +6 -0
- package/lib/typescript/constants.d.ts +16 -0
- package/lib/typescript/context.d.ts +1 -0
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/internal.d.ts +1 -7
- package/lib/typescript/monkey-patch.android.d.ts +1 -0
- package/lib/typescript/monkey-patch.d.ts +0 -1
- package/lib/typescript/reanimated.d.ts +2 -0
- package/lib/typescript/reanimated.native.d.ts +2 -0
- package/lib/typescript/specs/KeyboardControllerViewNativeComponent.d.ts +3 -1
- package/lib/typescript/types.d.ts +15 -1
- package/package.json +1 -1
- package/react-native-keyboard-controller.podspec +9 -1
- package/src/animated.tsx +3 -6
- package/src/{native.ts → bindings.native.ts} +4 -25
- package/src/bindings.ts +22 -0
- package/src/constants.ts +17 -0
- package/src/context.ts +12 -1
- package/src/hooks.ts +12 -7
- package/src/index.ts +2 -1
- package/src/internal.ts +2 -57
- package/src/monkey-patch.android.ts +33 -0
- package/src/monkey-patch.ts +1 -37
- package/src/reanimated.native.ts +51 -0
- package/src/reanimated.ts +6 -0
- package/src/replicas.ts +2 -1
- package/src/specs/KeyboardControllerViewNativeComponent.ts +3 -0
- package/src/types.ts +27 -5
- package/lib/commonjs/native.js.map +0 -1
- package/lib/module/native.js.map +0 -1
- package/lib/typescript/native.d.ts +0 -24
|
@@ -24,6 +24,7 @@ val interpolators = mapOf(
|
|
|
24
24
|
"ios" to IosInterpolator(),
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
+
@Suppress("detekt:TooManyFunctions")
|
|
27
28
|
@SuppressLint("ViewConstructor")
|
|
28
29
|
class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactContext) : ReactViewGroup(reactContext) {
|
|
29
30
|
// internal state management
|
|
@@ -31,6 +32,7 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
31
32
|
private var lastTouchX = 0f
|
|
32
33
|
private var lastTouchY = 0f
|
|
33
34
|
private var lastWindowY = 0
|
|
35
|
+
private var keyboardHeight = 0
|
|
34
36
|
|
|
35
37
|
// react props
|
|
36
38
|
private var interpolator: Interpolator = LinearInterpolator()
|
|
@@ -51,96 +53,16 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
when (event?.action) {
|
|
54
|
-
MotionEvent.ACTION_DOWN ->
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
lastTouchY = event.y
|
|
59
|
-
|
|
60
|
-
this.copyBoundsInWindow(bounds)
|
|
61
|
-
lastWindowY = bounds.top
|
|
62
|
-
}
|
|
63
|
-
MotionEvent.ACTION_MOVE -> {
|
|
64
|
-
// Since the view is likely to be translated/moved as the WindowInsetsAnimation
|
|
65
|
-
// progresses, we need to make sure we account for that change in our touch
|
|
66
|
-
// handling. We do that by keeping track of the view's Y position in the window,
|
|
67
|
-
// and detecting the difference between the current bounds.
|
|
68
|
-
this.copyBoundsInWindow(bounds)
|
|
69
|
-
val windowOffsetY = bounds.top - lastWindowY
|
|
70
|
-
|
|
71
|
-
// We then make a copy of the MotionEvent, and offset it with the calculated
|
|
72
|
-
// windowOffsetY. We can then pass it to the VelocityTracker.
|
|
73
|
-
val velocityTrackerEvent = MotionEvent.obtain(event)
|
|
74
|
-
velocityTrackerEvent.offsetLocation(0f, windowOffsetY.toFloat())
|
|
75
|
-
velocityTracker?.addMovement(velocityTrackerEvent)
|
|
76
|
-
|
|
77
|
-
val dx = velocityTrackerEvent.x - lastTouchX
|
|
78
|
-
val dy = velocityTrackerEvent.y - lastTouchY
|
|
79
|
-
|
|
80
|
-
if (!isHandling) {
|
|
81
|
-
// If we're not currently handling the touch gesture, lets check if we should
|
|
82
|
-
// start handling, by seeing if the gesture is majorly vertical, and
|
|
83
|
-
// larger than the touch slop
|
|
84
|
-
isHandling = dy.absoluteValue > dx.absoluteValue &&
|
|
85
|
-
dy.absoluteValue >= ViewConfiguration.get(this.context).scaledTouchSlop
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (isHandling) {
|
|
89
|
-
if (controller.isInsetAnimationInProgress()) {
|
|
90
|
-
// If we currently have control, we can update the IME insets to 'scroll'
|
|
91
|
-
// the IME in
|
|
92
|
-
val moveBy = this.interpolator.interpolate(dy.roundToInt(), this.getWindowHeight() - event.rawY.toInt(), controller.getCurrentKeyboardHeight())
|
|
93
|
-
|
|
94
|
-
if (moveBy != 0) {
|
|
95
|
-
controller.insetBy(moveBy)
|
|
96
|
-
}
|
|
97
|
-
} else if (
|
|
98
|
-
!controller.isInsetAnimationRequestPending() &&
|
|
99
|
-
shouldStartRequest(
|
|
100
|
-
dy = dy,
|
|
101
|
-
imeVisible = ViewCompat.getRootWindowInsets(this)
|
|
102
|
-
?.isVisible(WindowInsetsCompat.Type.ime()) == true,
|
|
103
|
-
)
|
|
104
|
-
) {
|
|
105
|
-
// If we don't currently have control (and a request isn't pending),
|
|
106
|
-
// the IME is not shown, the user is scrolling up, and the view can't
|
|
107
|
-
// scroll up any more (i.e. over-scrolling), we can start to control
|
|
108
|
-
// the IME insets
|
|
109
|
-
controller.startControlRequest(this)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Lastly we record the event X, Y, and view's Y window position, for the
|
|
113
|
-
// next touch event
|
|
114
|
-
lastTouchY = event.y
|
|
115
|
-
lastTouchX = event.x
|
|
116
|
-
lastWindowY = bounds.top
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
MotionEvent.ACTION_UP -> {
|
|
120
|
-
velocityTracker?.addMovement(event)
|
|
121
|
-
|
|
122
|
-
// Calculate the current velocityY, over 500 milliseconds
|
|
123
|
-
velocityTracker?.computeCurrentVelocity(500)
|
|
124
|
-
val velocityY = velocityTracker?.yVelocity
|
|
125
|
-
|
|
126
|
-
// If we received a ACTION_UP event, end any current WindowInsetsAnimation passing
|
|
127
|
-
// in the calculated Y velocity
|
|
128
|
-
controller.animateToFinish(velocityY)
|
|
129
|
-
|
|
130
|
-
// Reset our touch handling state
|
|
131
|
-
reset()
|
|
132
|
-
}
|
|
133
|
-
MotionEvent.ACTION_CANCEL -> {
|
|
134
|
-
// If we received a ACTION_CANCEL event, cancel any current WindowInsetsAnimation
|
|
135
|
-
controller.cancel()
|
|
136
|
-
// Reset our touch handling state
|
|
137
|
-
reset()
|
|
138
|
-
}
|
|
56
|
+
MotionEvent.ACTION_DOWN -> this.onActionDown(event)
|
|
57
|
+
MotionEvent.ACTION_MOVE -> this.onActionMove(event)
|
|
58
|
+
MotionEvent.ACTION_UP -> this.onActionUp(event)
|
|
59
|
+
MotionEvent.ACTION_CANCEL -> this.onActionCancel()
|
|
139
60
|
}
|
|
140
61
|
|
|
141
62
|
return super.dispatchTouchEvent(event)
|
|
142
63
|
}
|
|
143
64
|
|
|
65
|
+
// region Props setters
|
|
144
66
|
fun setInterpolator(interpolator: String) {
|
|
145
67
|
this.interpolator = interpolators[interpolator] ?: LinearInterpolator()
|
|
146
68
|
}
|
|
@@ -152,6 +74,114 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
152
74
|
fun setScrollKeyboardOffScreenWhenVisible(scrollImeOffScreenWhenVisible: Boolean) {
|
|
153
75
|
this.scrollKeyboardOffScreenWhenVisible = scrollImeOffScreenWhenVisible
|
|
154
76
|
}
|
|
77
|
+
// endregion
|
|
78
|
+
|
|
79
|
+
// region Handlers
|
|
80
|
+
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
|
81
|
+
private fun onActionDown(event: MotionEvent) {
|
|
82
|
+
velocityTracker?.addMovement(event)
|
|
83
|
+
|
|
84
|
+
lastTouchX = event.x
|
|
85
|
+
lastTouchY = event.y
|
|
86
|
+
|
|
87
|
+
this.copyBoundsInWindow(bounds)
|
|
88
|
+
lastWindowY = bounds.top
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@RequiresApi(Build.VERSION_CODES.R)
|
|
92
|
+
private fun onActionMove(event: MotionEvent) {
|
|
93
|
+
// Since the view is likely to be translated/moved as the WindowInsetsAnimation
|
|
94
|
+
// progresses, we need to make sure we account for that change in our touch
|
|
95
|
+
// handling. We do that by keeping track of the view's Y position in the window,
|
|
96
|
+
// and detecting the difference between the current bounds.
|
|
97
|
+
this.copyBoundsInWindow(bounds)
|
|
98
|
+
val windowOffsetY = bounds.top - lastWindowY
|
|
99
|
+
|
|
100
|
+
// We then make a copy of the MotionEvent, and offset it with the calculated
|
|
101
|
+
// windowOffsetY. We can then pass it to the VelocityTracker.
|
|
102
|
+
val velocityTrackerEvent = MotionEvent.obtain(event)
|
|
103
|
+
velocityTrackerEvent.offsetLocation(0f, windowOffsetY.toFloat())
|
|
104
|
+
velocityTracker?.addMovement(velocityTrackerEvent)
|
|
105
|
+
|
|
106
|
+
val dx = velocityTrackerEvent.x - lastTouchX
|
|
107
|
+
val dy = velocityTrackerEvent.y - lastTouchY
|
|
108
|
+
|
|
109
|
+
if (!isHandling) {
|
|
110
|
+
// If we're not currently handling the touch gesture, lets check if we should
|
|
111
|
+
// start handling, by seeing if the gesture is majorly vertical, and
|
|
112
|
+
// larger than the touch slop
|
|
113
|
+
isHandling = dy.absoluteValue > dx.absoluteValue &&
|
|
114
|
+
dy.absoluteValue >= ViewConfiguration.get(this.context).scaledTouchSlop
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (isHandling) {
|
|
118
|
+
if (controller.isInsetAnimationInProgress()) {
|
|
119
|
+
if (keyboardHeight == 0) {
|
|
120
|
+
this.keyboardHeight = controller.getCurrentKeyboardHeight()
|
|
121
|
+
}
|
|
122
|
+
// If we currently have control, we can update the IME insets to 'scroll'
|
|
123
|
+
// the IME in
|
|
124
|
+
val moveBy = this.interpolator.interpolate(
|
|
125
|
+
dy.roundToInt(),
|
|
126
|
+
this.getWindowHeight() - event.rawY.toInt(),
|
|
127
|
+
controller.getCurrentKeyboardHeight(),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
if (moveBy != 0) {
|
|
131
|
+
controller.insetBy(moveBy)
|
|
132
|
+
}
|
|
133
|
+
} else if (
|
|
134
|
+
!controller.isInsetAnimationRequestPending() &&
|
|
135
|
+
shouldStartRequest(
|
|
136
|
+
dy = dy,
|
|
137
|
+
imeVisible = ViewCompat.getRootWindowInsets(this)
|
|
138
|
+
?.isVisible(WindowInsetsCompat.Type.ime()) == true,
|
|
139
|
+
)
|
|
140
|
+
) {
|
|
141
|
+
// If we don't currently have control (and a request isn't pending),
|
|
142
|
+
// the IME is not shown, the user is scrolling up, and the view can't
|
|
143
|
+
// scroll up any more (i.e. over-scrolling), we can start to control
|
|
144
|
+
// the IME insets
|
|
145
|
+
controller.startControlRequest(this)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Lastly we record the event X, Y, and view's Y window position, for the
|
|
149
|
+
// next touch event
|
|
150
|
+
lastTouchY = event.y
|
|
151
|
+
lastTouchX = event.x
|
|
152
|
+
lastWindowY = bounds.top
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private fun onActionUp(event: MotionEvent) {
|
|
157
|
+
velocityTracker?.addMovement(event)
|
|
158
|
+
velocityTracker?.computeCurrentVelocity(VELOCITY_UNITS)
|
|
159
|
+
|
|
160
|
+
val velocityY = velocityTracker?.yVelocity
|
|
161
|
+
val isKeyboardPositionChanged =
|
|
162
|
+
// check `isInsetAnimationInProgress()` before, since direct usage of `getCurrentKeyboardHeight()`
|
|
163
|
+
// may throw exception
|
|
164
|
+
!controller.isInsetAnimationInProgress() ||
|
|
165
|
+
this.keyboardHeight != controller.getCurrentKeyboardHeight()
|
|
166
|
+
// if keyboard height was changed after finger movement -> we need to calculate final position
|
|
167
|
+
// and make an animated transition
|
|
168
|
+
val passedVelocityY = if (isKeyboardPositionChanged) velocityY else null
|
|
169
|
+
|
|
170
|
+
// If we received a ACTION_UP event, end any current WindowInsetsAnimation passing
|
|
171
|
+
// in the calculated Y velocity
|
|
172
|
+
controller.animateToFinish(passedVelocityY)
|
|
173
|
+
|
|
174
|
+
// Reset our touch handling state
|
|
175
|
+
reset()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private fun onActionCancel() {
|
|
179
|
+
// If we received a ACTION_CANCEL event, cancel any current WindowInsetsAnimation
|
|
180
|
+
controller.cancel()
|
|
181
|
+
// Reset our touch handling state
|
|
182
|
+
reset()
|
|
183
|
+
}
|
|
184
|
+
// endregion
|
|
155
185
|
|
|
156
186
|
/**
|
|
157
187
|
* Resets all of our internal state.
|
|
@@ -162,6 +192,7 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
162
192
|
lastTouchX = 0f
|
|
163
193
|
lastTouchY = 0f
|
|
164
194
|
lastWindowY = 0
|
|
195
|
+
keyboardHeight = 0
|
|
165
196
|
bounds.setEmpty()
|
|
166
197
|
|
|
167
198
|
velocityTracker?.recycle()
|
|
@@ -189,4 +220,9 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
189
220
|
|
|
190
221
|
return metrics?.bounds?.height() ?: 0
|
|
191
222
|
}
|
|
223
|
+
|
|
224
|
+
companion object {
|
|
225
|
+
// Calculate the current velocity over 500 milliseconds
|
|
226
|
+
private const val VELOCITY_UNITS = 500
|
|
227
|
+
}
|
|
192
228
|
}
|
package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt
CHANGED
|
@@ -10,23 +10,25 @@ class KeyboardControllerModule(mReactContext: ReactApplicationContext) : ReactCo
|
|
|
10
10
|
|
|
11
11
|
override fun getName(): String = KeyboardControllerModuleImpl.NAME
|
|
12
12
|
|
|
13
|
-
@ReactMethod
|
|
13
|
+
@ReactMethod
|
|
14
14
|
fun setInputMode(mode: Int) {
|
|
15
15
|
module.setInputMode(mode)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
@ReactMethod
|
|
18
|
+
@ReactMethod
|
|
19
19
|
fun setDefaultMode() {
|
|
20
20
|
module.setDefaultMode()
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
@Suppress("detekt:UnusedParameter")
|
|
23
24
|
@ReactMethod
|
|
24
25
|
fun addListener(eventName: String?) {
|
|
25
|
-
|
|
26
|
+
// Required for RN built-in Event Emitter Calls
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
@Suppress("detekt:UnusedParameter")
|
|
28
30
|
@ReactMethod
|
|
29
31
|
fun removeListeners(count: Int?) {
|
|
30
|
-
|
|
32
|
+
// Required for RN built-in Event Emitter Calls
|
|
31
33
|
}
|
|
32
34
|
}
|
package/android/src/paper/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt
CHANGED
|
@@ -7,7 +7,8 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
|
7
7
|
import com.facebook.react.bridge.ReactMethod
|
|
8
8
|
import com.reactnativekeyboardcontroller.modules.StatusBarManagerCompatModuleImpl
|
|
9
9
|
|
|
10
|
-
class StatusBarManagerCompatModule(
|
|
10
|
+
class StatusBarManagerCompatModule(mReactContext: ReactApplicationContext) :
|
|
11
|
+
ReactContextBaseJavaModule(mReactContext) {
|
|
11
12
|
private val module = StatusBarManagerCompatModuleImpl(mReactContext)
|
|
12
13
|
|
|
13
14
|
override fun getName(): String = StatusBarManagerCompatModuleImpl.NAME
|
package/ios/Extensions.swift
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
|
|
9
9
|
import Foundation
|
|
10
|
+
import UIKit
|
|
10
11
|
|
|
11
12
|
public extension CGFloat {
|
|
12
13
|
static func interpolate(inputRange: [CGFloat], outputRange: [CGFloat], currentValue: CGFloat) -> CGFloat {
|
|
@@ -21,3 +22,33 @@ public extension CGFloat {
|
|
|
21
22
|
return interpolatedValue
|
|
22
23
|
}
|
|
23
24
|
}
|
|
25
|
+
|
|
26
|
+
public extension Date {
|
|
27
|
+
static var currentTimeStamp: Int64 {
|
|
28
|
+
return Int64(Date().timeIntervalSince1970 * 1000)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public extension UIResponder {
|
|
33
|
+
private weak static var _currentFirstResponder: UIResponder?
|
|
34
|
+
|
|
35
|
+
static var current: UIResponder? {
|
|
36
|
+
UIResponder._currentFirstResponder = nil
|
|
37
|
+
UIApplication.shared.sendAction(#selector(findFirstResponder(sender:)), to: nil, from: nil, for: nil)
|
|
38
|
+
return UIResponder._currentFirstResponder
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@objc internal func findFirstResponder(sender _: AnyObject) {
|
|
42
|
+
UIResponder._currentFirstResponder = self
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public extension Optional where Wrapped == UIResponder {
|
|
47
|
+
var reactViewTag: NSNumber {
|
|
48
|
+
#if KEYBOARD_CONTROLLER_NEW_ARCH_ENABLED
|
|
49
|
+
return ((self as? RCTUITextField)?.superview?.tag ?? -1) as NSNumber
|
|
50
|
+
#else
|
|
51
|
+
return (self as? RCTUITextField)?.superview?.reactTag ?? -1
|
|
52
|
+
#endif
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -44,7 +44,12 @@ using namespace facebook::react;
|
|
|
44
44
|
_props = defaultProps;
|
|
45
45
|
|
|
46
46
|
observer = [[KeyboardMovementObserver alloc]
|
|
47
|
-
initWithHandler:^(
|
|
47
|
+
initWithHandler:^(
|
|
48
|
+
NSString *event,
|
|
49
|
+
NSNumber *height,
|
|
50
|
+
NSNumber *progress,
|
|
51
|
+
NSNumber *duration,
|
|
52
|
+
NSNumber *target) {
|
|
48
53
|
if (self->_eventEmitter) {
|
|
49
54
|
// TODO: use reflection to reduce code duplication?
|
|
50
55
|
if ([event isEqualToString:@"onKeyboardMoveStart"]) {
|
|
@@ -52,21 +57,30 @@ using namespace facebook::react;
|
|
|
52
57
|
self->_eventEmitter)
|
|
53
58
|
->onKeyboardMoveStart(
|
|
54
59
|
facebook::react::KeyboardControllerViewEventEmitter::OnKeyboardMoveStart{
|
|
55
|
-
.height = [height doubleValue],
|
|
60
|
+
.height = [height doubleValue],
|
|
61
|
+
.progress = [progress doubleValue],
|
|
62
|
+
.duration = [duration intValue],
|
|
63
|
+
.target = [target intValue]});
|
|
56
64
|
}
|
|
57
65
|
if ([event isEqualToString:@"onKeyboardMove"]) {
|
|
58
66
|
std::dynamic_pointer_cast<const facebook::react::KeyboardControllerViewEventEmitter>(
|
|
59
67
|
self->_eventEmitter)
|
|
60
68
|
->onKeyboardMove(
|
|
61
69
|
facebook::react::KeyboardControllerViewEventEmitter::OnKeyboardMove{
|
|
62
|
-
.height = [height doubleValue],
|
|
70
|
+
.height = [height doubleValue],
|
|
71
|
+
.progress = [progress doubleValue],
|
|
72
|
+
.duration = [duration intValue],
|
|
73
|
+
.target = [target intValue]});
|
|
63
74
|
}
|
|
64
75
|
if ([event isEqualToString:@"onKeyboardMoveEnd"]) {
|
|
65
76
|
std::dynamic_pointer_cast<const facebook::react::KeyboardControllerViewEventEmitter>(
|
|
66
77
|
self->_eventEmitter)
|
|
67
78
|
->onKeyboardMoveEnd(
|
|
68
79
|
facebook::react::KeyboardControllerViewEventEmitter::OnKeyboardMoveEnd{
|
|
69
|
-
.height = [height doubleValue],
|
|
80
|
+
.height = [height doubleValue],
|
|
81
|
+
.progress = [progress doubleValue],
|
|
82
|
+
.duration = [duration intValue],
|
|
83
|
+
.target = [target intValue]});
|
|
70
84
|
}
|
|
71
85
|
}
|
|
72
86
|
if ([event isEqualToString:@"onKeyboardMoveInteractive"]) {
|
|
@@ -74,7 +88,10 @@ using namespace facebook::react;
|
|
|
74
88
|
self->_eventEmitter)
|
|
75
89
|
->onKeyboardMoveInteractive(
|
|
76
90
|
facebook::react::KeyboardControllerViewEventEmitter::OnKeyboardMoveInteractive{
|
|
77
|
-
.height = [height doubleValue],
|
|
91
|
+
.height = [height doubleValue],
|
|
92
|
+
.progress = [progress doubleValue],
|
|
93
|
+
.duration = [duration intValue],
|
|
94
|
+
.target = [target intValue]});
|
|
78
95
|
}
|
|
79
96
|
|
|
80
97
|
// TODO: use built-in _eventEmitter once NativeAnimated module will use ModernEventemitter
|
|
@@ -84,7 +101,9 @@ using namespace facebook::react;
|
|
|
84
101
|
[[KeyboardMoveEvent alloc] initWithReactTag:@(self.tag)
|
|
85
102
|
event:event
|
|
86
103
|
height:height
|
|
87
|
-
progress:progress
|
|
104
|
+
progress:progress
|
|
105
|
+
duration:duration
|
|
106
|
+
target:target];
|
|
88
107
|
[bridge.eventDispatcher sendEvent:keyboardMoveEvent];
|
|
89
108
|
}
|
|
90
109
|
}
|
|
@@ -46,7 +46,7 @@ class KeyboardControllerView: UIView {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
func onEvent(event: NSString, height: NSNumber, progress: NSNumber) {
|
|
49
|
+
func onEvent(event: NSString, height: NSNumber, progress: NSNumber, duration: NSNumber, target: NSNumber) {
|
|
50
50
|
// we don't want to send event to JS before the JS thread is ready
|
|
51
51
|
if bridge.value(forKey: "_jsThread") == nil {
|
|
52
52
|
return
|
|
@@ -56,7 +56,9 @@ class KeyboardControllerView: UIView {
|
|
|
56
56
|
reactTag: reactTag,
|
|
57
57
|
event: event as String,
|
|
58
58
|
height: height,
|
|
59
|
-
progress: progress
|
|
59
|
+
progress: progress,
|
|
60
|
+
duration: duration,
|
|
61
|
+
target: target
|
|
60
62
|
)
|
|
61
63
|
)
|
|
62
64
|
}
|
package/ios/KeyboardMoveEvent.h
CHANGED
package/ios/KeyboardMoveEvent.m
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
@implementation KeyboardMoveEvent {
|
|
13
13
|
NSNumber *_progress;
|
|
14
14
|
NSNumber *_height;
|
|
15
|
+
NSNumber *_duration;
|
|
16
|
+
NSNumber *_target;
|
|
15
17
|
uint16_t _coalescingKey;
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -22,6 +24,8 @@
|
|
|
22
24
|
event:(NSString *)event
|
|
23
25
|
height:(NSNumber *)height
|
|
24
26
|
progress:(NSNumber *)progress
|
|
27
|
+
duration:(NSNumber *)duration
|
|
28
|
+
target:(NSNumber *)target
|
|
25
29
|
{
|
|
26
30
|
RCTAssertParam(reactTag);
|
|
27
31
|
|
|
@@ -30,6 +34,8 @@
|
|
|
30
34
|
_viewTag = reactTag;
|
|
31
35
|
_progress = progress;
|
|
32
36
|
_height = height;
|
|
37
|
+
_duration = duration;
|
|
38
|
+
_target = target;
|
|
33
39
|
_coalescingKey = 0;
|
|
34
40
|
}
|
|
35
41
|
return self;
|
|
@@ -47,6 +53,8 @@ RCT_NOT_IMPLEMENTED(-(instancetype)init)
|
|
|
47
53
|
NSDictionary *body = @{
|
|
48
54
|
@"progress" : _progress,
|
|
49
55
|
@"height" : _height,
|
|
56
|
+
@"duration" : _duration,
|
|
57
|
+
@"target" : _target,
|
|
50
58
|
};
|
|
51
59
|
|
|
52
60
|
return body;
|
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
//
|
|
8
8
|
|
|
9
9
|
import Foundation
|
|
10
|
+
import UIKit
|
|
10
11
|
|
|
11
12
|
@objc(KeyboardMovementObserver)
|
|
12
13
|
public class KeyboardMovementObserver: NSObject {
|
|
13
14
|
// class members
|
|
14
|
-
var onEvent: (NSString, NSNumber, NSNumber) -> Void
|
|
15
|
+
var onEvent: (NSString, NSNumber, NSNumber, NSNumber, NSNumber) -> Void
|
|
15
16
|
var onNotify: (String, Any) -> Void
|
|
16
17
|
// progress tracker
|
|
17
18
|
private var _keyboardView: UIView?
|
|
@@ -29,11 +30,14 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
29
30
|
private var _windowsCount: Int = 0
|
|
30
31
|
private var prevKeyboardPosition = 0.0
|
|
31
32
|
private var displayLink: CADisplayLink?
|
|
32
|
-
private var keyboardHeight: CGFloat = 0.0
|
|
33
33
|
private var hasKVObserver = false
|
|
34
|
+
// state variables
|
|
35
|
+
private var keyboardHeight: CGFloat = 0.0
|
|
36
|
+
private var duration = 0
|
|
37
|
+
private var tag: NSNumber = -1
|
|
34
38
|
|
|
35
39
|
@objc public init(
|
|
36
|
-
handler: @escaping (NSString, NSNumber, NSNumber) -> Void,
|
|
40
|
+
handler: @escaping (NSString, NSNumber, NSNumber, NSNumber, NSNumber) -> Void,
|
|
37
41
|
onNotify: @escaping (String, Any) -> Void
|
|
38
42
|
) {
|
|
39
43
|
onEvent = handler
|
|
@@ -100,6 +104,11 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
100
104
|
if displayLink != nil {
|
|
101
105
|
return
|
|
102
106
|
}
|
|
107
|
+
// if keyboard height is not equal to its bounds - we can ignore
|
|
108
|
+
// values, since they'll be invalid and will cause UI jumps
|
|
109
|
+
if keyboardView?.bounds.size.height != keyboardHeight {
|
|
110
|
+
return
|
|
111
|
+
}
|
|
103
112
|
|
|
104
113
|
// swiftlint:disable:next force_cast
|
|
105
114
|
let keyboardFrameY = (change?[.newKey] as! NSValue).cgPointValue.y
|
|
@@ -118,7 +127,13 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
118
127
|
return
|
|
119
128
|
}
|
|
120
129
|
|
|
121
|
-
onEvent(
|
|
130
|
+
onEvent(
|
|
131
|
+
"onKeyboardMoveInteractive",
|
|
132
|
+
position as NSNumber,
|
|
133
|
+
position / CGFloat(keyboardHeight) as NSNumber,
|
|
134
|
+
-1,
|
|
135
|
+
tag
|
|
136
|
+
)
|
|
122
137
|
}
|
|
123
138
|
}
|
|
124
139
|
|
|
@@ -129,24 +144,41 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
129
144
|
|
|
130
145
|
@objc func keyboardWillAppear(_ notification: Notification) {
|
|
131
146
|
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
|
|
147
|
+
tag = UIResponder.current.reactViewTag
|
|
132
148
|
let keyboardHeight = keyboardFrame.cgRectValue.size.height
|
|
149
|
+
let duration = Int(
|
|
150
|
+
(notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0) * 1000
|
|
151
|
+
)
|
|
133
152
|
self.keyboardHeight = keyboardHeight
|
|
153
|
+
self.duration = duration
|
|
134
154
|
|
|
135
155
|
var data = [AnyHashable: Any]()
|
|
136
156
|
data["height"] = keyboardHeight
|
|
157
|
+
data["duration"] = duration
|
|
158
|
+
data["timestamp"] = Date.currentTimeStamp
|
|
159
|
+
data["target"] = tag
|
|
137
160
|
|
|
138
|
-
onEvent("onKeyboardMoveStart", Float(keyboardHeight) as NSNumber, 1)
|
|
161
|
+
onEvent("onKeyboardMoveStart", Float(keyboardHeight) as NSNumber, 1, duration as NSNumber, tag)
|
|
139
162
|
onNotify("KeyboardController::keyboardWillShow", data)
|
|
140
163
|
|
|
141
164
|
setupKeyboardWatcher()
|
|
142
165
|
}
|
|
143
166
|
}
|
|
144
167
|
|
|
145
|
-
@objc func keyboardWillDisappear() {
|
|
168
|
+
@objc func keyboardWillDisappear(_ notification: Notification) {
|
|
169
|
+
let duration = Int(
|
|
170
|
+
(notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0) * 1000
|
|
171
|
+
)
|
|
172
|
+
tag = UIResponder.current.reactViewTag
|
|
173
|
+
self.duration = duration
|
|
174
|
+
|
|
146
175
|
var data = [AnyHashable: Any]()
|
|
147
176
|
data["height"] = 0
|
|
177
|
+
data["duration"] = duration
|
|
178
|
+
data["timestamp"] = Date.currentTimeStamp
|
|
179
|
+
data["target"] = tag
|
|
148
180
|
|
|
149
|
-
onEvent("onKeyboardMoveStart", 0, 0)
|
|
181
|
+
onEvent("onKeyboardMoveStart", 0, 0, duration as NSNumber, tag)
|
|
150
182
|
onNotify("KeyboardController::keyboardWillHide", data)
|
|
151
183
|
|
|
152
184
|
setupKeyboardWatcher()
|
|
@@ -156,12 +188,19 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
156
188
|
@objc func keyboardDidAppear(_ notification: Notification) {
|
|
157
189
|
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
|
|
158
190
|
let keyboardHeight = keyboardFrame.cgRectValue.size.height
|
|
191
|
+
tag = UIResponder.current.reactViewTag
|
|
159
192
|
self.keyboardHeight = keyboardHeight
|
|
193
|
+
let duration = Int(
|
|
194
|
+
(notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0) * 1000
|
|
195
|
+
)
|
|
160
196
|
|
|
161
197
|
var data = [AnyHashable: Any]()
|
|
162
198
|
data["height"] = keyboardHeight
|
|
199
|
+
data["duration"] = duration
|
|
200
|
+
data["timestamp"] = Date.currentTimeStamp
|
|
201
|
+
data["target"] = tag
|
|
163
202
|
|
|
164
|
-
onEvent("onKeyboardMoveEnd", keyboardHeight as NSNumber, 1)
|
|
203
|
+
onEvent("onKeyboardMoveEnd", keyboardHeight as NSNumber, 1, duration as NSNumber, tag)
|
|
165
204
|
onNotify("KeyboardController::keyboardDidShow", data)
|
|
166
205
|
|
|
167
206
|
removeKeyboardWatcher()
|
|
@@ -169,11 +208,18 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
169
208
|
}
|
|
170
209
|
}
|
|
171
210
|
|
|
172
|
-
@objc func keyboardDidDisappear() {
|
|
211
|
+
@objc func keyboardDidDisappear(_ notification: Notification) {
|
|
212
|
+
let duration = Int(
|
|
213
|
+
(notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0) * 1000
|
|
214
|
+
)
|
|
215
|
+
tag = UIResponder.current.reactViewTag
|
|
173
216
|
var data = [AnyHashable: Any]()
|
|
174
217
|
data["height"] = 0
|
|
218
|
+
data["duration"] = duration
|
|
219
|
+
data["timestamp"] = Date.currentTimeStamp
|
|
220
|
+
data["target"] = tag
|
|
175
221
|
|
|
176
|
-
onEvent("onKeyboardMoveEnd", 0 as NSNumber, 0)
|
|
222
|
+
onEvent("onKeyboardMoveEnd", 0 as NSNumber, 0, duration as NSNumber, tag)
|
|
177
223
|
onNotify("KeyboardController::keyboardDidHide", data)
|
|
178
224
|
|
|
179
225
|
removeKeyboardWatcher()
|
|
@@ -208,7 +254,7 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
208
254
|
if subview.description.hasPrefix("<UIInputSetContainerView") {
|
|
209
255
|
for hostView in subview.subviews {
|
|
210
256
|
if hostView.description.hasPrefix("<UIInputSetHostView") {
|
|
211
|
-
result = hostView
|
|
257
|
+
result = hostView
|
|
212
258
|
break
|
|
213
259
|
}
|
|
214
260
|
}
|
|
@@ -235,6 +281,12 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
235
281
|
}
|
|
236
282
|
|
|
237
283
|
prevKeyboardPosition = keyboardPosition
|
|
238
|
-
onEvent(
|
|
284
|
+
onEvent(
|
|
285
|
+
"onKeyboardMove",
|
|
286
|
+
keyboardPosition as NSNumber,
|
|
287
|
+
keyboardPosition / CGFloat(keyboardHeight) as NSNumber,
|
|
288
|
+
duration as NSNumber,
|
|
289
|
+
tag
|
|
290
|
+
)
|
|
239
291
|
}
|
|
240
292
|
}
|
package/lib/commonjs/animated.js
CHANGED
|
@@ -9,10 +9,11 @@ var _reactNative = require("react-native");
|
|
|
9
9
|
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
|
|
10
10
|
var _context = require("./context");
|
|
11
11
|
var _internal = require("./internal");
|
|
12
|
-
var
|
|
12
|
+
var _bindings = require("./bindings");
|
|
13
|
+
var _reanimated = require("./reanimated");
|
|
13
14
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
14
15
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
15
|
-
const KeyboardControllerViewAnimated = _reactNativeReanimated.default.createAnimatedComponent(_reactNative.Animated.createAnimatedComponent(
|
|
16
|
+
const KeyboardControllerViewAnimated = _reactNativeReanimated.default.createAnimatedComponent(_reactNative.Animated.createAnimatedComponent(_bindings.KeyboardControllerView));
|
|
16
17
|
const styles = _reactNative.StyleSheet.create({
|
|
17
18
|
container: {
|
|
18
19
|
flex: 1
|
|
@@ -74,7 +75,7 @@ const KeyboardProvider = _ref => {
|
|
|
74
75
|
heightSV.value = -event.height;
|
|
75
76
|
}
|
|
76
77
|
};
|
|
77
|
-
const handler = (0,
|
|
78
|
+
const handler = (0, _reanimated.useAnimatedKeyboardHandler)({
|
|
78
79
|
onKeyboardMoveStart: event => {
|
|
79
80
|
'worklet';
|
|
80
81
|
|