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.
Files changed (109) hide show
  1. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +3 -1
  2. package/android/src/fabric/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +2 -1
  3. package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardAnimationCallback.kt +128 -18
  4. package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardAnimationController.kt +2 -0
  5. package/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt +11 -2
  6. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +7 -0
  7. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +2 -1
  8. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardGestureAreaViewManagerImpl.kt +1 -0
  9. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +6 -1
  10. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/StatusBarManagerCompatModuleImpl.kt +9 -3
  11. package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +44 -27
  12. package/android/src/main/java/com/reactnativekeyboardcontroller/views/KeyboardGestureAreaReactViewGroup.kt +119 -96
  13. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +6 -4
  14. package/android/src/paper/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +2 -1
  15. package/ios/Extensions.swift +31 -0
  16. package/ios/KeyboardController-Bridging-Header.h +1 -0
  17. package/ios/KeyboardControllerView.mm +25 -6
  18. package/ios/KeyboardControllerViewManager.swift +4 -2
  19. package/ios/KeyboardMoveEvent.h +3 -1
  20. package/ios/KeyboardMoveEvent.m +8 -0
  21. package/ios/KeyboardMovementObserver.swift +58 -11
  22. package/lib/commonjs/animated.js +4 -3
  23. package/lib/commonjs/animated.js.map +1 -1
  24. package/lib/commonjs/bindings.js +26 -0
  25. package/lib/commonjs/bindings.js.map +1 -0
  26. package/lib/commonjs/bindings.native.js +33 -0
  27. package/lib/commonjs/bindings.native.js.map +1 -0
  28. package/lib/commonjs/{native.js → constants.js} +2 -29
  29. package/lib/commonjs/constants.js.map +1 -0
  30. package/lib/commonjs/context.js +9 -1
  31. package/lib/commonjs/context.js.map +1 -1
  32. package/lib/commonjs/hooks.js +7 -6
  33. package/lib/commonjs/hooks.js.map +1 -1
  34. package/lib/commonjs/index.js +15 -4
  35. package/lib/commonjs/index.js.map +1 -1
  36. package/lib/commonjs/internal.js +0 -30
  37. package/lib/commonjs/internal.js.map +1 -1
  38. package/lib/commonjs/monkey-patch.android.js +34 -0
  39. package/lib/commonjs/monkey-patch.android.js.map +1 -0
  40. package/lib/commonjs/monkey-patch.js +1 -35
  41. package/lib/commonjs/monkey-patch.js.map +1 -1
  42. package/lib/commonjs/reanimated.js +9 -0
  43. package/lib/commonjs/reanimated.js.map +1 -0
  44. package/lib/commonjs/reanimated.native.js +37 -0
  45. package/lib/commonjs/reanimated.native.js.map +1 -0
  46. package/lib/commonjs/replicas.js +4 -3
  47. package/lib/commonjs/replicas.js.map +1 -1
  48. package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
  49. package/lib/commonjs/types.js.map +1 -1
  50. package/lib/module/animated.js +3 -2
  51. package/lib/module/animated.js.map +1 -1
  52. package/lib/module/bindings.js +16 -0
  53. package/lib/module/bindings.js.map +1 -0
  54. package/lib/module/bindings.native.js +23 -0
  55. package/lib/module/bindings.native.js.map +1 -0
  56. package/lib/module/{native.js → constants.js} +1 -24
  57. package/lib/module/constants.js.map +1 -0
  58. package/lib/module/context.js +8 -1
  59. package/lib/module/context.js.map +1 -1
  60. package/lib/module/hooks.js +7 -6
  61. package/lib/module/hooks.js.map +1 -1
  62. package/lib/module/index.js +2 -1
  63. package/lib/module/index.js.map +1 -1
  64. package/lib/module/internal.js +1 -30
  65. package/lib/module/internal.js.map +1 -1
  66. package/lib/module/monkey-patch.android.js +29 -0
  67. package/lib/module/monkey-patch.android.js.map +1 -0
  68. package/lib/module/monkey-patch.js +1 -31
  69. package/lib/module/monkey-patch.js.map +1 -1
  70. package/lib/module/reanimated.js +2 -0
  71. package/lib/module/reanimated.js.map +1 -0
  72. package/lib/module/reanimated.native.js +30 -0
  73. package/lib/module/reanimated.native.js.map +1 -0
  74. package/lib/module/replicas.js +2 -1
  75. package/lib/module/replicas.js.map +1 -1
  76. package/lib/module/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
  77. package/lib/module/types.js.map +1 -1
  78. package/lib/typescript/bindings.d.ts +6 -0
  79. package/lib/typescript/bindings.native.d.ts +6 -0
  80. package/lib/typescript/constants.d.ts +16 -0
  81. package/lib/typescript/context.d.ts +1 -0
  82. package/lib/typescript/index.d.ts +2 -1
  83. package/lib/typescript/internal.d.ts +1 -7
  84. package/lib/typescript/monkey-patch.android.d.ts +1 -0
  85. package/lib/typescript/monkey-patch.d.ts +0 -1
  86. package/lib/typescript/reanimated.d.ts +2 -0
  87. package/lib/typescript/reanimated.native.d.ts +2 -0
  88. package/lib/typescript/specs/KeyboardControllerViewNativeComponent.d.ts +3 -1
  89. package/lib/typescript/types.d.ts +15 -1
  90. package/package.json +1 -1
  91. package/react-native-keyboard-controller.podspec +9 -1
  92. package/src/animated.tsx +3 -6
  93. package/src/{native.ts → bindings.native.ts} +4 -25
  94. package/src/bindings.ts +22 -0
  95. package/src/constants.ts +17 -0
  96. package/src/context.ts +12 -1
  97. package/src/hooks.ts +12 -7
  98. package/src/index.ts +2 -1
  99. package/src/internal.ts +2 -57
  100. package/src/monkey-patch.android.ts +33 -0
  101. package/src/monkey-patch.ts +1 -37
  102. package/src/reanimated.native.ts +51 -0
  103. package/src/reanimated.ts +6 -0
  104. package/src/replicas.ts +2 -1
  105. package/src/specs/KeyboardControllerViewNativeComponent.ts +3 -0
  106. package/src/types.ts +27 -5
  107. package/lib/commonjs/native.js.map +0 -1
  108. package/lib/module/native.js.map +0 -1
  109. 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
- velocityTracker?.addMovement(event)
57
-
58
- lastTouchX = event.x
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
  }
@@ -10,23 +10,25 @@ class KeyboardControllerModule(mReactContext: ReactApplicationContext) : ReactCo
10
10
 
11
11
  override fun getName(): String = KeyboardControllerModuleImpl.NAME
12
12
 
13
- @ReactMethod(isBlockingSynchronousMethod = true)
13
+ @ReactMethod
14
14
  fun setInputMode(mode: Int) {
15
15
  module.setInputMode(mode)
16
16
  }
17
17
 
18
- @ReactMethod(isBlockingSynchronousMethod = true)
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
- /* Required for RN built-in Event Emitter Calls. */
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
- /* Required for RN built-in Event Emitter Calls. */
32
+ // Required for RN built-in Event Emitter Calls
31
33
  }
32
34
  }
@@ -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(private val mReactContext: ReactApplicationContext) : ReactContextBaseJavaModule(mReactContext) {
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
@@ -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
+ }
@@ -1 +1,2 @@
1
+ #import <React/RCTUITextField.h>
1
2
  #import <React/RCTViewManager.h>
@@ -44,7 +44,12 @@ using namespace facebook::react;
44
44
  _props = defaultProps;
45
45
 
46
46
  observer = [[KeyboardMovementObserver alloc]
47
- initWithHandler:^(NSString *event, NSNumber *height, NSNumber *progress) {
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], .progress = [progress 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], .progress = [progress 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], .progress = [progress 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], .progress = [progress 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
  }
@@ -14,6 +14,8 @@
14
14
  - (instancetype)initWithReactTag:(NSNumber *)reactTag
15
15
  event:(NSString *)event
16
16
  height:(NSNumber *)height
17
- progress:(NSNumber *)progress;
17
+ progress:(NSNumber *)progress
18
+ duration:(NSNumber *)duration
19
+ target:(NSNumber *)target;
18
20
 
19
21
  @end
@@ -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("onKeyboardMoveInteractive", position as NSNumber, position / CGFloat(keyboardHeight) as NSNumber)
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("onKeyboardMove", keyboardPosition as NSNumber, keyboardPosition / CGFloat(keyboardHeight) as NSNumber)
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
  }
@@ -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 _native = require("./native");
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(_native.KeyboardControllerView));
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, _internal.useAnimatedKeyboardHandler)({
78
+ const handler = (0, _reanimated.useAnimatedKeyboardHandler)({
78
79
  onKeyboardMoveStart: event => {
79
80
  'worklet';
80
81