react-native-keyboard-controller 1.13.4 → 1.14.1

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 (115) hide show
  1. package/README.md +1 -0
  2. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +5 -3
  3. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +19 -20
  4. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardGestureAreaViewManager.kt +22 -12
  5. package/android/src/fabric/java/com/reactnativekeyboardcontroller/OverKeyboardViewManager.kt +40 -0
  6. package/android/src/fabric/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +7 -3
  7. package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt +33 -27
  8. package/android/src/main/java/com/reactnativekeyboardcontroller/constants/Keyboard.kt +7 -0
  9. package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputLayoutChangedEvent.kt +16 -15
  10. package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputSelectionChangedEvent.kt +25 -24
  11. package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputTextChangedEvent.kt +4 -3
  12. package/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt +7 -6
  13. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/Context.kt +38 -0
  14. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt +78 -58
  15. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ReactContext.kt +9 -4
  16. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +10 -3
  17. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/View.kt +10 -8
  18. package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/KeyboardAnimationController.kt +65 -60
  19. package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/Interpolator.kt +6 -1
  20. package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/IosInterpolator.kt +4 -2
  21. package/android/src/main/java/com/reactnativekeyboardcontroller/interactive/interpolators/LinearInterpolator.kt +1 -3
  22. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt +62 -58
  23. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt +75 -50
  24. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/WindowDimensionListener.kt +13 -7
  25. package/android/src/main/java/com/reactnativekeyboardcontroller/log/Logger.kt +10 -2
  26. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +35 -24
  27. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardGestureAreaViewManagerImpl.kt +21 -8
  28. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/OverKeyboardViewManagerImpl.kt +27 -0
  29. package/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt +46 -16
  30. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +8 -8
  31. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/StatusBarManagerCompatModuleImpl.kt +14 -4
  32. package/android/src/main/java/com/reactnativekeyboardcontroller/traversal/FocusedInputHolder.kt +1 -3
  33. package/android/src/main/java/com/reactnativekeyboardcontroller/traversal/ViewHierarchyNavigator.kt +19 -13
  34. package/android/src/main/java/com/reactnativekeyboardcontroller/ui/FrameScheduler.kt +11 -8
  35. package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +27 -18
  36. package/android/src/main/java/com/reactnativekeyboardcontroller/views/KeyboardGestureAreaReactViewGroup.kt +23 -14
  37. package/android/src/main/java/com/reactnativekeyboardcontroller/views/overlay/OverKeyboardHostShadowNode.kt +30 -0
  38. package/android/src/main/java/com/reactnativekeyboardcontroller/views/overlay/OverKeyboardViewGroup.kt +185 -0
  39. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +3 -1
  40. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +19 -10
  41. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardGestureAreaViewManager.kt +21 -8
  42. package/android/src/paper/java/com/reactnativekeyboardcontroller/OverKeyboardViewManager.kt +33 -0
  43. package/android/src/paper/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +7 -3
  44. package/android/src/test/java/com/reactnativekeyboardcontroller/traversal/ViewHierarchyNavigatorTest.kt +30 -21
  45. package/ios/KeyboardController-Bridging-Header.h +1 -0
  46. package/ios/{core → animations}/KeyboardAnimation.swift +1 -0
  47. package/ios/{core → animations}/SpringAnimation.swift +1 -1
  48. package/ios/{core → animations}/TimingAnimation.swift +21 -20
  49. package/ios/extensions/UIApplication.swift +16 -0
  50. package/ios/extensions/UIResponder.swift +0 -3
  51. package/ios/extensions/UIWindow.swift +48 -0
  52. package/ios/views/OverKeyboardViewManager.h +29 -0
  53. package/ios/views/OverKeyboardViewManager.mm +220 -0
  54. package/jest/index.js +1 -0
  55. package/lib/commonjs/bindings.js +2 -1
  56. package/lib/commonjs/bindings.js.map +1 -1
  57. package/lib/commonjs/bindings.native.js +2 -1
  58. package/lib/commonjs/bindings.native.js.map +1 -1
  59. package/lib/commonjs/components/KeyboardAvoidingView/hooks.js +6 -0
  60. package/lib/commonjs/components/KeyboardAvoidingView/hooks.js.map +1 -1
  61. package/lib/commonjs/components/KeyboardAwareScrollView/index.js +2 -1
  62. package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
  63. package/lib/commonjs/components/KeyboardToolbar/index.js +5 -3
  64. package/lib/commonjs/components/KeyboardToolbar/index.js.map +1 -1
  65. package/lib/commonjs/index.js +9 -1
  66. package/lib/commonjs/index.js.map +1 -1
  67. package/lib/commonjs/specs/OverKeyboardViewNativeComponent.js +10 -0
  68. package/lib/commonjs/specs/OverKeyboardViewNativeComponent.js.map +1 -0
  69. package/lib/commonjs/types.js.map +1 -1
  70. package/lib/commonjs/views/OverKeyboardView/index.js +43 -0
  71. package/lib/commonjs/views/OverKeyboardView/index.js.map +1 -0
  72. package/lib/commonjs/views/index.js +14 -0
  73. package/lib/commonjs/views/index.js.map +1 -0
  74. package/lib/module/bindings.js +1 -0
  75. package/lib/module/bindings.js.map +1 -1
  76. package/lib/module/bindings.native.js +1 -0
  77. package/lib/module/bindings.native.js.map +1 -1
  78. package/lib/module/components/KeyboardAvoidingView/hooks.js +6 -0
  79. package/lib/module/components/KeyboardAvoidingView/hooks.js.map +1 -1
  80. package/lib/module/components/KeyboardAwareScrollView/index.js +2 -1
  81. package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
  82. package/lib/module/components/KeyboardToolbar/index.js +5 -3
  83. package/lib/module/components/KeyboardToolbar/index.js.map +1 -1
  84. package/lib/module/index.js +1 -0
  85. package/lib/module/index.js.map +1 -1
  86. package/lib/module/specs/OverKeyboardViewNativeComponent.js +3 -0
  87. package/lib/module/specs/OverKeyboardViewNativeComponent.js.map +1 -0
  88. package/lib/module/types.js.map +1 -1
  89. package/lib/module/views/OverKeyboardView/index.js +35 -0
  90. package/lib/module/views/OverKeyboardView/index.js.map +1 -0
  91. package/lib/module/views/index.js +2 -0
  92. package/lib/module/views/index.js.map +1 -0
  93. package/lib/typescript/bindings.d.ts +2 -1
  94. package/lib/typescript/bindings.native.d.ts +2 -1
  95. package/lib/typescript/components/KeyboardAwareScrollView/index.d.ts +4 -0
  96. package/lib/typescript/components/KeyboardToolbar/index.d.ts +2 -2
  97. package/lib/typescript/index.d.ts +1 -0
  98. package/lib/typescript/specs/OverKeyboardViewNativeComponent.d.ts +7 -0
  99. package/lib/typescript/types.d.ts +4 -1
  100. package/lib/typescript/views/OverKeyboardView/index.d.ts +5 -0
  101. package/lib/typescript/views/index.d.ts +1 -0
  102. package/package.json +8 -3
  103. package/src/bindings.native.ts +3 -0
  104. package/src/bindings.ts +3 -0
  105. package/src/components/KeyboardAvoidingView/hooks.ts +6 -0
  106. package/src/components/KeyboardAwareScrollView/index.tsx +5 -2
  107. package/src/components/KeyboardToolbar/index.tsx +7 -3
  108. package/src/index.ts +1 -0
  109. package/src/specs/OverKeyboardViewNativeComponent.ts +12 -0
  110. package/src/types.ts +4 -0
  111. package/src/views/OverKeyboardView/index.tsx +43 -0
  112. package/src/views/index.ts +1 -0
  113. package/ios/.clang-format +0 -90
  114. package/ios/.swiftformat +0 -5
  115. package/ios/.swiftlint.yml +0 -14
@@ -20,14 +20,17 @@ import com.reactnativekeyboardcontroller.interactive.interpolators.LinearInterpo
20
20
  import kotlin.math.absoluteValue
21
21
  import kotlin.math.roundToInt
22
22
 
23
- val interpolators = mapOf(
24
- "linear" to LinearInterpolator(),
25
- "ios" to IosInterpolator(),
26
- )
23
+ val interpolators =
24
+ mapOf(
25
+ "linear" to LinearInterpolator(),
26
+ "ios" to IosInterpolator(),
27
+ )
27
28
 
28
29
  @Suppress("detekt:TooManyFunctions")
29
30
  @SuppressLint("ViewConstructor")
30
- class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactContext) : ReactViewGroup(reactContext) {
31
+ class KeyboardGestureAreaReactViewGroup(
32
+ private val reactContext: ThemedReactContext,
33
+ ) : ReactViewGroup(reactContext) {
31
34
  // internal state management
32
35
  private var isHandling = false
33
36
  private var lastTouchX = 0f
@@ -127,12 +130,13 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
127
130
  }
128
131
  // If we currently have control, we can update the IME insets to 'scroll'
129
132
  // the IME in
130
- val moveBy = this.interpolator.interpolate(
131
- dy.roundToInt(),
132
- this.getWindowHeight() - event.rawY.toInt(),
133
- controller.getCurrentKeyboardHeight(),
134
- offset,
135
- )
133
+ val moveBy =
134
+ this.interpolator.interpolate(
135
+ dy.roundToInt(),
136
+ this.getWindowHeight() - event.rawY.toInt(),
137
+ controller.getCurrentKeyboardHeight(),
138
+ offset,
139
+ )
136
140
 
137
141
  if (moveBy != 0) {
138
142
  controller.insetBy(moveBy)
@@ -141,8 +145,10 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
141
145
  !controller.isInsetAnimationRequestPending() &&
142
146
  shouldStartRequest(
143
147
  dy = dy,
144
- imeVisible = ViewCompat.getRootWindowInsets(this)
145
- ?.isVisible(WindowInsetsCompat.Type.ime()) == true,
148
+ imeVisible =
149
+ ViewCompat
150
+ .getRootWindowInsets(this)
151
+ ?.isVisible(WindowInsetsCompat.Type.ime()) == true,
146
152
  )
147
153
  ) {
148
154
  // If we don't currently have control (and a request isn't pending),
@@ -210,7 +216,10 @@ class KeyboardGestureAreaReactViewGroup(private val reactContext: ThemedReactCon
210
216
  * Returns true if the given [dy], [IME visibility][imeVisible], and constructor options
211
217
  * support a IME animation request.
212
218
  */
213
- private fun shouldStartRequest(dy: Float, imeVisible: Boolean) = when {
219
+ private fun shouldStartRequest(
220
+ dy: Float,
221
+ imeVisible: Boolean,
222
+ ) = when {
214
223
  // If the user is scroll up, return true if scrollImeOnScreenWhenNotVisible is true, and
215
224
  // the IME is not currently visible
216
225
  dy < 0 -> !imeVisible && scrollKeyboardOnScreenWhenNotVisible
@@ -0,0 +1,30 @@
1
+ package com.reactnativekeyboardcontroller.views.overlay
2
+
3
+ import com.facebook.react.uimanager.LayoutShadowNode
4
+ import com.facebook.react.uimanager.ReactShadowNodeImpl
5
+ import com.reactnativekeyboardcontroller.extensions.getDisplaySize
6
+
7
+ /**
8
+ * We implement the OverKeyboardView by using an Android WindowManager. That will fill the entire window of the
9
+ * application. To get layout to work properly, we need to layout all the elements within the
10
+ * OverKeyboardView's inner content view as if they can fill the entire window. To do that, we need to
11
+ * explicitly set the styleWidth and styleHeight on the LayoutShadowNode of the child of this node
12
+ * to be the window size. This will then cause the children of the Modal to layout as if they can
13
+ * fill the window.
14
+ */
15
+ internal class OverKeyboardHostShadowNode : LayoutShadowNode() {
16
+ /**
17
+ * We need to set the styleWidth and styleHeight of the one child (represented by the
18
+ * <View></View> within the <RCTOverKeyboardView></RCTOverKeyboardView> in OverKeyboardView.tsx.
19
+ * This needs to fill the entire window.
20
+ */
21
+ override fun addChildAt(
22
+ child: ReactShadowNodeImpl,
23
+ i: Int,
24
+ ) {
25
+ super.addChildAt(child, i)
26
+ val displaySize = themedContext.getDisplaySize()
27
+ child.setStyleWidth(displaySize.x.toFloat())
28
+ child.setStyleHeight(displaySize.y.toFloat())
29
+ }
30
+ }
@@ -0,0 +1,185 @@
1
+ package com.reactnativekeyboardcontroller.views.overlay
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.content.Context
5
+ import android.graphics.PixelFormat
6
+ import android.view.MotionEvent
7
+ import android.view.View
8
+ import android.view.WindowManager
9
+ import com.facebook.react.bridge.UiThreadUtil
10
+ import com.facebook.react.config.ReactFeatureFlags
11
+ import com.facebook.react.uimanager.JSPointerDispatcher
12
+ import com.facebook.react.uimanager.JSTouchDispatcher
13
+ import com.facebook.react.uimanager.RootView
14
+ import com.facebook.react.uimanager.ThemedReactContext
15
+ import com.facebook.react.uimanager.UIManagerHelper
16
+ import com.facebook.react.uimanager.events.EventDispatcher
17
+ import com.facebook.react.views.view.ReactViewGroup
18
+
19
+ @SuppressLint("ViewConstructor")
20
+ class OverKeyboardHostView(
21
+ private val reactContext: ThemedReactContext,
22
+ ) : ReactViewGroup(reactContext) {
23
+ private val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, this.id)
24
+ private var windowManager: WindowManager = reactContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
25
+ private var hostView: OverKeyboardRootViewGroup = OverKeyboardRootViewGroup(reactContext)
26
+
27
+ init {
28
+ hostView.eventDispatcher = dispatcher
29
+ }
30
+
31
+ // region life cycles
32
+ override fun onDetachedFromWindow() {
33
+ super.onDetachedFromWindow()
34
+
35
+ hide()
36
+ }
37
+
38
+ override fun addView(
39
+ child: View?,
40
+ index: Int,
41
+ ) {
42
+ UiThreadUtil.assertOnUiThread()
43
+ hostView.addView(child, index)
44
+ }
45
+
46
+ override fun getChildCount(): Int = hostView.childCount
47
+
48
+ override fun getChildAt(index: Int): View? = hostView.getChildAt(index)
49
+
50
+ override fun removeView(child: View?) {
51
+ UiThreadUtil.assertOnUiThread()
52
+
53
+ if (child != null) {
54
+ hostView.removeView(child)
55
+ }
56
+ }
57
+
58
+ override fun removeViewAt(index: Int) {
59
+ UiThreadUtil.assertOnUiThread()
60
+ val child = getChildAt(index)
61
+ hostView.removeView(child)
62
+ }
63
+
64
+ override fun onLayout(
65
+ changed: Boolean,
66
+ l: Int,
67
+ t: Int,
68
+ r: Int,
69
+ b: Int,
70
+ ) {
71
+ // Do nothing as we are laid out by UIManager
72
+ }
73
+ // endregion
74
+
75
+ fun show() {
76
+ val layoutParams =
77
+ WindowManager.LayoutParams(
78
+ WindowManager.LayoutParams.MATCH_PARENT,
79
+ WindowManager.LayoutParams.MATCH_PARENT,
80
+ // This type ensures it floats over other application windows but under system windows
81
+ WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,
82
+ // Ensures touches outside the view pass through to other windows
83
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
84
+ PixelFormat.TRANSLUCENT,
85
+ )
86
+
87
+ windowManager.addView(hostView, layoutParams)
88
+ }
89
+
90
+ fun hide() {
91
+ if (hostView.isAttached) {
92
+ windowManager.removeView(hostView)
93
+ }
94
+ }
95
+ }
96
+
97
+ @SuppressLint("ViewConstructor")
98
+ class OverKeyboardRootViewGroup(
99
+ private val reactContext: ThemedReactContext,
100
+ ) : ReactViewGroup(reactContext),
101
+ RootView {
102
+ private val jsTouchDispatcher: JSTouchDispatcher = JSTouchDispatcher(this)
103
+ private var jsPointerDispatcher: JSPointerDispatcher? = null
104
+ internal var eventDispatcher: EventDispatcher? = null
105
+ internal var isAttached = false
106
+
107
+ init {
108
+ if (ReactFeatureFlags.dispatchPointerEvents) {
109
+ jsPointerDispatcher = JSPointerDispatcher(this)
110
+ }
111
+ }
112
+
113
+ // region life cycles
114
+ override fun onAttachedToWindow() {
115
+ super.onAttachedToWindow()
116
+ isAttached = true
117
+ }
118
+
119
+ override fun onDetachedFromWindow() {
120
+ super.onDetachedFromWindow()
121
+ isAttached = false
122
+ }
123
+ // endregion
124
+
125
+ // region Touch events handling
126
+ override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
127
+ eventDispatcher?.let { eventDispatcher ->
128
+ jsTouchDispatcher.handleTouchEvent(event, eventDispatcher)
129
+ jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher, true)
130
+ }
131
+ return super.onInterceptTouchEvent(event)
132
+ }
133
+
134
+ @SuppressLint("ClickableViewAccessibility")
135
+ override fun onTouchEvent(event: MotionEvent): Boolean {
136
+ eventDispatcher?.let { eventDispatcher ->
137
+ jsTouchDispatcher.handleTouchEvent(event, eventDispatcher)
138
+ jsPointerDispatcher?.handleMotionEvent(event, eventDispatcher, false)
139
+ }
140
+ super.onTouchEvent(event)
141
+ // In case when there is no children interested in handling touch event, we return true from
142
+ // the root view in order to receive subsequent events related to that gesture
143
+ return true
144
+ }
145
+
146
+ override fun onInterceptHoverEvent(event: MotionEvent): Boolean {
147
+ eventDispatcher?.let { jsPointerDispatcher?.handleMotionEvent(event, it, true) }
148
+ return super.onHoverEvent(event)
149
+ }
150
+
151
+ override fun onHoverEvent(event: MotionEvent): Boolean {
152
+ eventDispatcher?.let { jsPointerDispatcher?.handleMotionEvent(event, it, false) }
153
+ return super.onHoverEvent(event)
154
+ }
155
+
156
+ override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
157
+ // No-op - override in order to still receive events to onInterceptTouchEvent
158
+ // even when some other view disallow that
159
+ }
160
+ // endregion
161
+
162
+ // region RootView methods
163
+ override fun onChildStartedNativeGesture(
164
+ childView: View,
165
+ ev: MotionEvent,
166
+ ) {
167
+ eventDispatcher?.let { eventDispatcher ->
168
+ jsTouchDispatcher.onChildStartedNativeGesture(ev, eventDispatcher)
169
+ jsPointerDispatcher?.onChildStartedNativeGesture(childView, ev, eventDispatcher)
170
+ }
171
+ }
172
+
173
+ override fun onChildEndedNativeGesture(
174
+ childView: View,
175
+ ev: MotionEvent,
176
+ ) {
177
+ eventDispatcher?.let { jsTouchDispatcher.onChildEndedNativeGesture(ev, it) }
178
+ jsPointerDispatcher?.onChildEndedNativeGesture()
179
+ }
180
+
181
+ override fun handleException(t: Throwable) {
182
+ reactContext.reactApplicationContext.handleException(RuntimeException(t))
183
+ }
184
+ // endregion
185
+ }
@@ -5,7 +5,9 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule
5
5
  import com.facebook.react.bridge.ReactMethod
6
6
  import com.reactnativekeyboardcontroller.modules.KeyboardControllerModuleImpl
7
7
 
8
- class KeyboardControllerModule(mReactContext: ReactApplicationContext) : ReactContextBaseJavaModule(mReactContext) {
8
+ class KeyboardControllerModule(
9
+ mReactContext: ReactApplicationContext,
10
+ ) : ReactContextBaseJavaModule(mReactContext) {
9
11
  private val module = KeyboardControllerModuleImpl(mReactContext)
10
12
 
11
13
  override fun getName(): String = KeyboardControllerModuleImpl.NAME
@@ -7,31 +7,40 @@ import com.facebook.react.views.view.ReactViewManager
7
7
  import com.reactnativekeyboardcontroller.managers.KeyboardControllerViewManagerImpl
8
8
  import com.reactnativekeyboardcontroller.views.EdgeToEdgeReactViewGroup
9
9
 
10
- class KeyboardControllerViewManager(mReactContext: ReactApplicationContext) : ReactViewManager() {
10
+ class KeyboardControllerViewManager(
11
+ mReactContext: ReactApplicationContext,
12
+ ) : ReactViewManager() {
11
13
  private val manager = KeyboardControllerViewManagerImpl(mReactContext)
12
14
 
13
15
  override fun getName(): String = KeyboardControllerViewManagerImpl.NAME
14
16
 
15
- override fun createViewInstance(reactContext: ThemedReactContext): EdgeToEdgeReactViewGroup {
16
- return manager.createViewInstance(reactContext)
17
- }
17
+ override fun createViewInstance(reactContext: ThemedReactContext): EdgeToEdgeReactViewGroup =
18
+ manager.createViewInstance(reactContext)
18
19
 
19
20
  @ReactProp(name = "enabled")
20
- fun setEnabled(view: EdgeToEdgeReactViewGroup, enabled: Boolean) {
21
+ fun setEnabled(
22
+ view: EdgeToEdgeReactViewGroup,
23
+ enabled: Boolean,
24
+ ) {
21
25
  manager.setEnabled(view, enabled)
22
26
  }
23
27
 
24
28
  @ReactProp(name = "statusBarTranslucent")
25
- fun setStatusBarTranslucent(view: EdgeToEdgeReactViewGroup, isStatusBarTranslucent: Boolean) {
29
+ fun setStatusBarTranslucent(
30
+ view: EdgeToEdgeReactViewGroup,
31
+ isStatusBarTranslucent: Boolean,
32
+ ) {
26
33
  manager.setStatusBarTranslucent(view, isStatusBarTranslucent)
27
34
  }
28
35
 
29
36
  @ReactProp(name = "navigationBarTranslucent")
30
- fun setNavigationBarTranslucent(view: EdgeToEdgeReactViewGroup, isNavigationBarTranslucent: Boolean) {
37
+ fun setNavigationBarTranslucent(
38
+ view: EdgeToEdgeReactViewGroup,
39
+ isNavigationBarTranslucent: Boolean,
40
+ ) {
31
41
  manager.setNavigationBarTranslucent(view, isNavigationBarTranslucent)
32
42
  }
33
43
 
34
- override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
35
- return manager.getExportedCustomDirectEventTypeConstants()
36
- }
44
+ override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> =
45
+ manager.getExportedCustomDirectEventTypeConstants()
37
46
  }
@@ -7,32 +7,45 @@ import com.facebook.react.views.view.ReactViewManager
7
7
  import com.reactnativekeyboardcontroller.managers.KeyboardGestureAreaViewManagerImpl
8
8
  import com.reactnativekeyboardcontroller.views.KeyboardGestureAreaReactViewGroup
9
9
 
10
- class KeyboardGestureAreaViewManager(mReactContext: ReactApplicationContext) : ReactViewManager() {
10
+ class KeyboardGestureAreaViewManager(
11
+ mReactContext: ReactApplicationContext,
12
+ ) : ReactViewManager() {
11
13
  private val manager = KeyboardGestureAreaViewManagerImpl(mReactContext)
12
14
 
13
15
  override fun getName(): String = KeyboardGestureAreaViewManagerImpl.NAME
14
16
 
15
- override fun createViewInstance(reactContext: ThemedReactContext): KeyboardGestureAreaReactViewGroup {
16
- return manager.createViewInstance(reactContext)
17
- }
17
+ override fun createViewInstance(reactContext: ThemedReactContext): KeyboardGestureAreaReactViewGroup =
18
+ manager.createViewInstance(reactContext)
18
19
 
19
20
  @ReactProp(name = "offset")
20
- fun setInterpolator(view: KeyboardGestureAreaReactViewGroup, offset: Double) {
21
+ fun setInterpolator(
22
+ view: KeyboardGestureAreaReactViewGroup,
23
+ offset: Double,
24
+ ) {
21
25
  manager.setOffset(view, offset)
22
26
  }
23
27
 
24
28
  @ReactProp(name = "interpolator")
25
- fun setInterpolator(view: KeyboardGestureAreaReactViewGroup, interpolator: String) {
29
+ fun setInterpolator(
30
+ view: KeyboardGestureAreaReactViewGroup,
31
+ interpolator: String,
32
+ ) {
26
33
  manager.setInterpolator(view, interpolator)
27
34
  }
28
35
 
29
36
  @ReactProp(name = "showOnSwipeUp")
30
- fun setScrollKeyboardOnScreenWhenNotVisible(view: KeyboardGestureAreaReactViewGroup, value: Boolean) {
37
+ fun setScrollKeyboardOnScreenWhenNotVisible(
38
+ view: KeyboardGestureAreaReactViewGroup,
39
+ value: Boolean,
40
+ ) {
31
41
  manager.setScrollKeyboardOnScreenWhenNotVisible(view, value)
32
42
  }
33
43
 
34
44
  @ReactProp(name = "enableSwipeToDismiss")
35
- fun setScrollKeyboardOffScreenWhenVisible(view: KeyboardGestureAreaReactViewGroup, value: Boolean) {
45
+ fun setScrollKeyboardOffScreenWhenVisible(
46
+ view: KeyboardGestureAreaReactViewGroup,
47
+ value: Boolean,
48
+ ) {
36
49
  manager.setScrollKeyboardOffScreenWhenVisible(view, value)
37
50
  }
38
51
  }
@@ -0,0 +1,33 @@
1
+ package com.reactnativekeyboardcontroller
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext
4
+ import com.facebook.react.uimanager.LayoutShadowNode
5
+ import com.facebook.react.uimanager.ThemedReactContext
6
+ import com.facebook.react.uimanager.ViewGroupManager
7
+ import com.facebook.react.uimanager.annotations.ReactProp
8
+ import com.reactnativekeyboardcontroller.managers.OverKeyboardViewManagerImpl
9
+ import com.reactnativekeyboardcontroller.views.overlay.OverKeyboardHostShadowNode
10
+ import com.reactnativekeyboardcontroller.views.overlay.OverKeyboardHostView
11
+
12
+ class OverKeyboardViewManager(
13
+ mReactContext: ReactApplicationContext,
14
+ ) : ViewGroupManager<OverKeyboardHostView>() {
15
+ private val manager = OverKeyboardViewManagerImpl(mReactContext)
16
+
17
+ override fun getName(): String = OverKeyboardViewManagerImpl.NAME
18
+
19
+ override fun createViewInstance(reactContext: ThemedReactContext): OverKeyboardHostView =
20
+ manager.createViewInstance(reactContext)
21
+
22
+ override fun createShadowNodeInstance(): LayoutShadowNode = OverKeyboardHostShadowNode()
23
+
24
+ override fun getShadowNodeClass(): Class<out LayoutShadowNode> = OverKeyboardHostShadowNode::class.java
25
+
26
+ @ReactProp(name = "visible")
27
+ fun setVisible(
28
+ view: OverKeyboardHostView,
29
+ value: Boolean,
30
+ ) {
31
+ manager.setVisible(view, value)
32
+ }
33
+ }
@@ -7,8 +7,9 @@ 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(mReactContext: ReactApplicationContext) :
11
- ReactContextBaseJavaModule(mReactContext) {
10
+ class StatusBarManagerCompatModule(
11
+ mReactContext: ReactApplicationContext,
12
+ ) : ReactContextBaseJavaModule(mReactContext) {
12
13
  private val module = StatusBarManagerCompatModuleImpl(mReactContext)
13
14
 
14
15
  override fun getName(): String = StatusBarManagerCompatModuleImpl.NAME
@@ -20,7 +21,10 @@ class StatusBarManagerCompatModule(mReactContext: ReactApplicationContext) :
20
21
 
21
22
  @ReactMethod
22
23
  @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
23
- private fun setColor(color: Int, animated: Boolean) {
24
+ private fun setColor(
25
+ color: Int,
26
+ animated: Boolean,
27
+ ) {
24
28
  module.setColor(color, animated)
25
29
  }
26
30
 
@@ -36,8 +36,16 @@ class ViewHierarchyNavigatorTest {
36
36
 
37
37
  editText1 = EditText(context).apply { id = 1 }
38
38
  editText2 = EditText(context).apply { id = 2 }
39
- editText3 = EditText(context).apply { id = 3; isEnabled = false }
40
- editText4 = EditText(context).apply { id = 4; isEnabled = false }
39
+ editText3 =
40
+ EditText(context).apply {
41
+ id = 3
42
+ isEnabled = false
43
+ }
44
+ editText4 =
45
+ EditText(context).apply {
46
+ id = 4
47
+ isEnabled = false
48
+ }
41
49
  editText5 = EditText(context).apply { id = 5 }
42
50
  editText6 = EditText(context).apply { id = 6 }
43
51
  editText7 = EditText(context).apply { id = 7 }
@@ -48,25 +56,26 @@ class ViewHierarchyNavigatorTest {
48
56
  editText12 = EditText(context).apply { id = 12 }
49
57
  editText13 = EditText(context).apply { id = 13 }
50
58
 
51
- layout = LinearLayout(context).apply {
52
- addView(editText1)
53
- addView(editText2)
54
- addView(editText3)
55
- addView(editText4)
56
- addView(
57
- LinearLayout(context).apply {
58
- addView(editText5)
59
- addView(editText6)
60
- addView(editText7)
61
- },
62
- )
63
- addView(editText8)
64
- addView(editText9)
65
- addView(editText10)
66
- addView(editText11)
67
- addView(editText12)
68
- addView(editText13)
69
- }
59
+ layout =
60
+ LinearLayout(context).apply {
61
+ addView(editText1)
62
+ addView(editText2)
63
+ addView(editText3)
64
+ addView(editText4)
65
+ addView(
66
+ LinearLayout(context).apply {
67
+ addView(editText5)
68
+ addView(editText6)
69
+ addView(editText7)
70
+ },
71
+ )
72
+ addView(editText8)
73
+ addView(editText9)
74
+ addView(editText10)
75
+ addView(editText11)
76
+ addView(editText12)
77
+ addView(editText13)
78
+ }
70
79
  }
71
80
 
72
81
  @Test
@@ -1,3 +1,4 @@
1
+ #import <React/RCTTouchHandler.h>
1
2
  #import <React/RCTUIManager.h>
2
3
  #import <React/RCTUITextView.h>
3
4
  #import <React/RCTViewManager.h>
@@ -6,6 +6,7 @@
6
6
  //
7
7
 
8
8
  import Foundation
9
+ import QuartzCore
9
10
 
10
11
  protocol KeyboardAnimationProtocol {
11
12
  var startTime: CFTimeInterval { get }
@@ -10,7 +10,7 @@ import Foundation
10
10
  import QuartzCore
11
11
 
12
12
  // swiftlint:disable identifier_name
13
- public class SpringAnimation: KeyboardAnimation {
13
+ public final class SpringAnimation: KeyboardAnimation {
14
14
  // internal variables
15
15
  private let zeta: Double // Damping ratio
16
16
  private let omega0: Double // Undamped angular frequency of the oscillator
@@ -15,13 +15,13 @@ import QuartzCore
15
15
  * This class calculates the progress of animations based on Bézier curve control points.
16
16
  * For more details on the Bézier curves, see [Desmos Graph](https://www.desmos.com/calculator/eynenh1aga?lang=en).
17
17
  */
18
- public class TimingAnimation: KeyboardAnimation {
18
+ public final class TimingAnimation: KeyboardAnimation {
19
19
  private let p1: CGPoint
20
20
  private let p2: CGPoint
21
21
 
22
22
  init(animation: CABasicAnimation, fromValue: Double, toValue: Double) {
23
23
  let timingFunction = animation.timingFunction
24
- var controlPoints: [Float] = [0, 0, 0, 0]
24
+ var controlPoints: ContiguousArray<Float> = ContiguousArray([0, 0, 0, 0])
25
25
  timingFunction?.getControlPoint(at: 1, values: &controlPoints[0])
26
26
  timingFunction?.getControlPoint(at: 2, values: &controlPoints[2])
27
27
  let p1 = CGPoint(x: CGFloat(controlPoints[0]), y: CGFloat(controlPoints[1]))
@@ -33,7 +33,20 @@ public class TimingAnimation: KeyboardAnimation {
33
33
  super.init(fromValue: fromValue, toValue: toValue, animation: animation)
34
34
  }
35
35
 
36
- func bezier(t: CGFloat, valueForPoint: (CGPoint) -> CGFloat) -> CGFloat {
36
+ // public functions
37
+ override func valueAt(time: Double) -> Double {
38
+ let x = time * Double(speed)
39
+ let frames = (animation?.duration ?? 0.0) * Double(speed)
40
+ let fraction = min(x / frames, 1)
41
+ let t = findTForX(xTarget: fraction)
42
+
43
+ let progress = bezierY(t: t)
44
+
45
+ return fromValue + (toValue - fromValue) * CGFloat(progress)
46
+ }
47
+
48
+ // private functions
49
+ private func bezier(t: CGFloat, valueForPoint: (CGPoint) -> CGFloat) -> CGFloat {
37
50
  let u = 1 - t
38
51
  let tt = t * t
39
52
  let uu = u * u
@@ -49,27 +62,15 @@ public class TimingAnimation: KeyboardAnimation {
49
62
  return term1 + term2 + term3
50
63
  }
51
64
 
52
- func bezierY(t: CGFloat) -> CGFloat {
65
+ private func bezierY(t: CGFloat) -> CGFloat {
53
66
  return bezier(t: t) { $0.y }
54
67
  }
55
68
 
56
- func bezierX(t: CGFloat) -> CGFloat {
69
+ private func bezierX(t: CGFloat) -> CGFloat {
57
70
  return bezier(t: t) { $0.x }
58
71
  }
59
72
 
60
- // public functions
61
- override func valueAt(time: Double) -> Double {
62
- let x = time * Double(speed)
63
- let frames = (animation?.duration ?? 0.0) * Double(speed)
64
- let fraction = min(x / frames, 1)
65
- let t = findTForX(xTarget: fraction)
66
-
67
- let progress = bezierY(t: t)
68
-
69
- return fromValue + (toValue - fromValue) * CGFloat(progress)
70
- }
71
-
72
- func findTForX(xTarget: CGFloat, epsilon: CGFloat = 0.0001, maxIterations: Int = 100) -> CGFloat {
73
+ private func findTForX(xTarget: CGFloat, epsilon: CGFloat = 0.0001, maxIterations: Int = 100) -> CGFloat {
73
74
  var t: CGFloat = 0.5 // Start with an initial guess of t = 0.5
74
75
  for _ in 0 ..< maxIterations {
75
76
  let currentX = bezierX(t: t) // Compute the x-coordinate at t
@@ -84,7 +85,7 @@ public class TimingAnimation: KeyboardAnimation {
84
85
  return t // Return the approximation of t
85
86
  }
86
87
 
87
- func bezierDerivative(t: CGFloat, valueForPoint: (CGPoint) -> CGFloat) -> CGFloat {
88
+ private func bezierDerivative(t: CGFloat, valueForPoint: (CGPoint) -> CGFloat) -> CGFloat {
88
89
  let u = 1 - t
89
90
  let uu = u * u
90
91
  let tt = t * t
@@ -100,7 +101,7 @@ public class TimingAnimation: KeyboardAnimation {
100
101
  return term1 + term2 + term3
101
102
  }
102
103
 
103
- func bezierXDerivative(t: CGFloat) -> CGFloat {
104
+ private func bezierXDerivative(t: CGFloat) -> CGFloat {
104
105
  return bezierDerivative(t: t) { $0.x }
105
106
  }
106
107
  }
@@ -9,6 +9,22 @@ import Foundation
9
9
  import UIKit
10
10
 
11
11
  public extension UIApplication {
12
+ var activeWindow: UIWindow? {
13
+ if #available(iOS 13.0, *) {
14
+ for scene in connectedScenes {
15
+ if scene.activationState == .foregroundActive,
16
+ let windowScene = scene as? UIWindowScene,
17
+ let keyWindow = windowScene.windows.first(where: { $0.isKeyWindow })
18
+ {
19
+ return keyWindow
20
+ }
21
+ }
22
+ return nil
23
+ } else {
24
+ return windows.last { $0.isKeyWindow }
25
+ }
26
+ }
27
+
12
28
  static func topViewController(
13
29
  base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
14
30
  ) -> UIViewController? {
@@ -56,10 +56,7 @@ public extension Optional where Wrapped: UIResponder {
56
56
  !(currentView is UITextView),
57
57
  scrollView.frame.width >= scrollView.contentSize.width,
58
58
  scrollView.isScrollEnabled
59
- // it was fixed in swiftlint https://github.com/realm/SwiftLint/issues/3756 but a new release is not available yet
60
- // swiftlint:disable all
61
59
  {
62
- // swiftlint:enable all
63
60
  return scrollView.reactViewTag
64
61
  }
65
62