react-native-keyboard-controller 1.5.8 → 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 +128 -18
- package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardAnimationController.kt +2 -0
- 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 +119 -96
- 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 +58 -11
- 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
|
|
@@ -52,107 +53,16 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
when (event?.action) {
|
|
55
|
-
MotionEvent.ACTION_DOWN ->
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
lastTouchY = event.y
|
|
60
|
-
|
|
61
|
-
this.copyBoundsInWindow(bounds)
|
|
62
|
-
lastWindowY = bounds.top
|
|
63
|
-
}
|
|
64
|
-
MotionEvent.ACTION_MOVE -> {
|
|
65
|
-
// Since the view is likely to be translated/moved as the WindowInsetsAnimation
|
|
66
|
-
// progresses, we need to make sure we account for that change in our touch
|
|
67
|
-
// handling. We do that by keeping track of the view's Y position in the window,
|
|
68
|
-
// and detecting the difference between the current bounds.
|
|
69
|
-
this.copyBoundsInWindow(bounds)
|
|
70
|
-
val windowOffsetY = bounds.top - lastWindowY
|
|
71
|
-
|
|
72
|
-
// We then make a copy of the MotionEvent, and offset it with the calculated
|
|
73
|
-
// windowOffsetY. We can then pass it to the VelocityTracker.
|
|
74
|
-
val velocityTrackerEvent = MotionEvent.obtain(event)
|
|
75
|
-
velocityTrackerEvent.offsetLocation(0f, windowOffsetY.toFloat())
|
|
76
|
-
velocityTracker?.addMovement(velocityTrackerEvent)
|
|
77
|
-
|
|
78
|
-
val dx = velocityTrackerEvent.x - lastTouchX
|
|
79
|
-
val dy = velocityTrackerEvent.y - lastTouchY
|
|
80
|
-
|
|
81
|
-
if (!isHandling) {
|
|
82
|
-
// If we're not currently handling the touch gesture, lets check if we should
|
|
83
|
-
// start handling, by seeing if the gesture is majorly vertical, and
|
|
84
|
-
// larger than the touch slop
|
|
85
|
-
isHandling = dy.absoluteValue > dx.absoluteValue &&
|
|
86
|
-
dy.absoluteValue >= ViewConfiguration.get(this.context).scaledTouchSlop
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (isHandling) {
|
|
90
|
-
if (controller.isInsetAnimationInProgress()) {
|
|
91
|
-
if (keyboardHeight == 0) {
|
|
92
|
-
this.keyboardHeight = controller.getCurrentKeyboardHeight()
|
|
93
|
-
}
|
|
94
|
-
// If we currently have control, we can update the IME insets to 'scroll'
|
|
95
|
-
// the IME in
|
|
96
|
-
val moveBy = this.interpolator.interpolate(dy.roundToInt(), this.getWindowHeight() - event.rawY.toInt(), controller.getCurrentKeyboardHeight())
|
|
97
|
-
|
|
98
|
-
if (moveBy != 0) {
|
|
99
|
-
controller.insetBy(moveBy)
|
|
100
|
-
}
|
|
101
|
-
} else if (
|
|
102
|
-
!controller.isInsetAnimationRequestPending() &&
|
|
103
|
-
shouldStartRequest(
|
|
104
|
-
dy = dy,
|
|
105
|
-
imeVisible = ViewCompat.getRootWindowInsets(this)
|
|
106
|
-
?.isVisible(WindowInsetsCompat.Type.ime()) == true,
|
|
107
|
-
)
|
|
108
|
-
) {
|
|
109
|
-
// If we don't currently have control (and a request isn't pending),
|
|
110
|
-
// the IME is not shown, the user is scrolling up, and the view can't
|
|
111
|
-
// scroll up any more (i.e. over-scrolling), we can start to control
|
|
112
|
-
// the IME insets
|
|
113
|
-
controller.startControlRequest(this)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Lastly we record the event X, Y, and view's Y window position, for the
|
|
117
|
-
// next touch event
|
|
118
|
-
lastTouchY = event.y
|
|
119
|
-
lastTouchX = event.x
|
|
120
|
-
lastWindowY = bounds.top
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
MotionEvent.ACTION_UP -> {
|
|
124
|
-
velocityTracker?.addMovement(event)
|
|
125
|
-
|
|
126
|
-
// Calculate the current velocityY, over 500 milliseconds
|
|
127
|
-
velocityTracker?.computeCurrentVelocity(500)
|
|
128
|
-
val velocityY = velocityTracker?.yVelocity
|
|
129
|
-
val isKeyboardPositionChanged =
|
|
130
|
-
// check `isInsetAnimationInProgress()` before, since direct usage of `getCurrentKeyboardHeight()`
|
|
131
|
-
// may throw exception
|
|
132
|
-
!controller.isInsetAnimationInProgress() ||
|
|
133
|
-
this.keyboardHeight != controller.getCurrentKeyboardHeight()
|
|
134
|
-
// if keyboard height was changed after finger movement -> we need to calculate final position
|
|
135
|
-
// and make an animated transition
|
|
136
|
-
val passedVelocityY = if (isKeyboardPositionChanged) velocityY else null
|
|
137
|
-
|
|
138
|
-
// If we received a ACTION_UP event, end any current WindowInsetsAnimation passing
|
|
139
|
-
// in the calculated Y velocity
|
|
140
|
-
controller.animateToFinish(passedVelocityY)
|
|
141
|
-
|
|
142
|
-
// Reset our touch handling state
|
|
143
|
-
reset()
|
|
144
|
-
}
|
|
145
|
-
MotionEvent.ACTION_CANCEL -> {
|
|
146
|
-
// If we received a ACTION_CANCEL event, cancel any current WindowInsetsAnimation
|
|
147
|
-
controller.cancel()
|
|
148
|
-
// Reset our touch handling state
|
|
149
|
-
reset()
|
|
150
|
-
}
|
|
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()
|
|
151
60
|
}
|
|
152
61
|
|
|
153
62
|
return super.dispatchTouchEvent(event)
|
|
154
63
|
}
|
|
155
64
|
|
|
65
|
+
// region Props setters
|
|
156
66
|
fun setInterpolator(interpolator: String) {
|
|
157
67
|
this.interpolator = interpolators[interpolator] ?: LinearInterpolator()
|
|
158
68
|
}
|
|
@@ -164,6 +74,114 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
164
74
|
fun setScrollKeyboardOffScreenWhenVisible(scrollImeOffScreenWhenVisible: Boolean) {
|
|
165
75
|
this.scrollKeyboardOffScreenWhenVisible = scrollImeOffScreenWhenVisible
|
|
166
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
|
|
167
185
|
|
|
168
186
|
/**
|
|
169
187
|
* Resets all of our internal state.
|
|
@@ -202,4 +220,9 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
|
|
|
202
220
|
|
|
203
221
|
return metrics?.bounds?.height() ?: 0
|
|
204
222
|
}
|
|
223
|
+
|
|
224
|
+
companion object {
|
|
225
|
+
// Calculate the current velocity over 500 milliseconds
|
|
226
|
+
private const val VELOCITY_UNITS = 500
|
|
227
|
+
}
|
|
205
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
|
|
@@ -123,7 +127,13 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
123
127
|
return
|
|
124
128
|
}
|
|
125
129
|
|
|
126
|
-
onEvent(
|
|
130
|
+
onEvent(
|
|
131
|
+
"onKeyboardMoveInteractive",
|
|
132
|
+
position as NSNumber,
|
|
133
|
+
position / CGFloat(keyboardHeight) as NSNumber,
|
|
134
|
+
-1,
|
|
135
|
+
tag
|
|
136
|
+
)
|
|
127
137
|
}
|
|
128
138
|
}
|
|
129
139
|
|
|
@@ -134,24 +144,41 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
134
144
|
|
|
135
145
|
@objc func keyboardWillAppear(_ notification: Notification) {
|
|
136
146
|
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
|
|
147
|
+
tag = UIResponder.current.reactViewTag
|
|
137
148
|
let keyboardHeight = keyboardFrame.cgRectValue.size.height
|
|
149
|
+
let duration = Int(
|
|
150
|
+
(notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0) * 1000
|
|
151
|
+
)
|
|
138
152
|
self.keyboardHeight = keyboardHeight
|
|
153
|
+
self.duration = duration
|
|
139
154
|
|
|
140
155
|
var data = [AnyHashable: Any]()
|
|
141
156
|
data["height"] = keyboardHeight
|
|
157
|
+
data["duration"] = duration
|
|
158
|
+
data["timestamp"] = Date.currentTimeStamp
|
|
159
|
+
data["target"] = tag
|
|
142
160
|
|
|
143
|
-
onEvent("onKeyboardMoveStart", Float(keyboardHeight) as NSNumber, 1)
|
|
161
|
+
onEvent("onKeyboardMoveStart", Float(keyboardHeight) as NSNumber, 1, duration as NSNumber, tag)
|
|
144
162
|
onNotify("KeyboardController::keyboardWillShow", data)
|
|
145
163
|
|
|
146
164
|
setupKeyboardWatcher()
|
|
147
165
|
}
|
|
148
166
|
}
|
|
149
167
|
|
|
150
|
-
@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
|
+
|
|
151
175
|
var data = [AnyHashable: Any]()
|
|
152
176
|
data["height"] = 0
|
|
177
|
+
data["duration"] = duration
|
|
178
|
+
data["timestamp"] = Date.currentTimeStamp
|
|
179
|
+
data["target"] = tag
|
|
153
180
|
|
|
154
|
-
onEvent("onKeyboardMoveStart", 0, 0)
|
|
181
|
+
onEvent("onKeyboardMoveStart", 0, 0, duration as NSNumber, tag)
|
|
155
182
|
onNotify("KeyboardController::keyboardWillHide", data)
|
|
156
183
|
|
|
157
184
|
setupKeyboardWatcher()
|
|
@@ -161,12 +188,19 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
161
188
|
@objc func keyboardDidAppear(_ notification: Notification) {
|
|
162
189
|
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
|
|
163
190
|
let keyboardHeight = keyboardFrame.cgRectValue.size.height
|
|
191
|
+
tag = UIResponder.current.reactViewTag
|
|
164
192
|
self.keyboardHeight = keyboardHeight
|
|
193
|
+
let duration = Int(
|
|
194
|
+
(notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0) * 1000
|
|
195
|
+
)
|
|
165
196
|
|
|
166
197
|
var data = [AnyHashable: Any]()
|
|
167
198
|
data["height"] = keyboardHeight
|
|
199
|
+
data["duration"] = duration
|
|
200
|
+
data["timestamp"] = Date.currentTimeStamp
|
|
201
|
+
data["target"] = tag
|
|
168
202
|
|
|
169
|
-
onEvent("onKeyboardMoveEnd", keyboardHeight as NSNumber, 1)
|
|
203
|
+
onEvent("onKeyboardMoveEnd", keyboardHeight as NSNumber, 1, duration as NSNumber, tag)
|
|
170
204
|
onNotify("KeyboardController::keyboardDidShow", data)
|
|
171
205
|
|
|
172
206
|
removeKeyboardWatcher()
|
|
@@ -174,11 +208,18 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
174
208
|
}
|
|
175
209
|
}
|
|
176
210
|
|
|
177
|
-
@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
|
|
178
216
|
var data = [AnyHashable: Any]()
|
|
179
217
|
data["height"] = 0
|
|
218
|
+
data["duration"] = duration
|
|
219
|
+
data["timestamp"] = Date.currentTimeStamp
|
|
220
|
+
data["target"] = tag
|
|
180
221
|
|
|
181
|
-
onEvent("onKeyboardMoveEnd", 0 as NSNumber, 0)
|
|
222
|
+
onEvent("onKeyboardMoveEnd", 0 as NSNumber, 0, duration as NSNumber, tag)
|
|
182
223
|
onNotify("KeyboardController::keyboardDidHide", data)
|
|
183
224
|
|
|
184
225
|
removeKeyboardWatcher()
|
|
@@ -240,6 +281,12 @@ public class KeyboardMovementObserver: NSObject {
|
|
|
240
281
|
}
|
|
241
282
|
|
|
242
283
|
prevKeyboardPosition = keyboardPosition
|
|
243
|
-
onEvent(
|
|
284
|
+
onEvent(
|
|
285
|
+
"onKeyboardMove",
|
|
286
|
+
keyboardPosition as NSNumber,
|
|
287
|
+
keyboardPosition / CGFloat(keyboardHeight) as NSNumber,
|
|
288
|
+
duration as NSNumber,
|
|
289
|
+
tag
|
|
290
|
+
)
|
|
244
291
|
}
|
|
245
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
|
|