react-native-keyboard-controller 1.11.1 → 1.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt +8 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/KeyboardAnimationCallback.kt +52 -60
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +1 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/traversal/FocusedInputHolder.kt +10 -5
- package/android/src/main/java/com/reactnativekeyboardcontroller/traversal/ViewHierarchyNavigator.kt +2 -6
- package/android/src/test/java/com/reactnativekeyboardcontroller/traversal/FocusedInputHolderTest.kt +45 -0
- package/android/src/test/java/com/reactnativekeyboardcontroller/traversal/ViewHierarchyNavigatorTest.kt +11 -10
- package/ios/traversal/FocusedInputHolder.swift +5 -5
- package/ios/traversal/TextInput.swift +3 -3
- package/ios/traversal/ViewHierarchyNavigator.swift +2 -2
- package/lib/commonjs/components/KeyboardAvoidingView/index.js +1 -6
- package/lib/commonjs/components/KeyboardAvoidingView/index.js.map +1 -1
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js +4 -3
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/commonjs/components/KeyboardStickyView/index.js +3 -7
- package/lib/commonjs/components/KeyboardStickyView/index.js.map +1 -1
- package/lib/commonjs/components/KeyboardToolbar/index.js +4 -3
- package/lib/commonjs/components/KeyboardToolbar/index.js.map +1 -1
- package/lib/commonjs/components/index.js +0 -6
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/index.js +1 -8
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/monkey-patch.android.js +36 -24
- package/lib/commonjs/monkey-patch.android.js.map +1 -1
- package/lib/module/components/KeyboardAvoidingView/index.js +2 -6
- package/lib/module/components/KeyboardAvoidingView/index.js.map +1 -1
- package/lib/module/components/KeyboardAwareScrollView/index.js +5 -4
- package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/module/components/KeyboardStickyView/index.js +4 -7
- package/lib/module/components/KeyboardStickyView/index.js.map +1 -1
- package/lib/module/components/KeyboardToolbar/index.js +4 -3
- package/lib/module/components/KeyboardToolbar/index.js.map +1 -1
- package/lib/module/components/index.js +1 -1
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/monkey-patch.android.js +36 -24
- package/lib/module/monkey-patch.android.js.map +1 -1
- package/lib/typescript/components/KeyboardToolbar/index.d.ts +5 -0
- package/lib/typescript/components/index.d.ts +2 -1
- package/lib/typescript/index.d.ts +2 -1
- package/package.json +2 -2
- package/react-native-keyboard-controller.podspec +27 -14
- package/src/components/KeyboardAvoidingView/index.tsx +6 -7
- package/src/components/KeyboardAwareScrollView/index.tsx +4 -4
- package/src/components/KeyboardStickyView/index.tsx +6 -6
- package/src/components/KeyboardToolbar/index.tsx +38 -20
- package/src/components/index.ts +1 -1
- package/src/index.ts +1 -1
- package/src/monkey-patch.android.ts +31 -26
- package/lib/commonjs/components/hooks/useKeyboardInterpolation.js +0 -89
- package/lib/commonjs/components/hooks/useKeyboardInterpolation.js.map +0 -1
- package/lib/module/components/hooks/useKeyboardInterpolation.js +0 -82
- package/lib/module/components/hooks/useKeyboardInterpolation.js.map +0 -1
- package/lib/typescript/components/hooks/useKeyboardInterpolation.d.ts +0 -20
- package/src/components/hooks/useKeyboardInterpolation.ts +0 -109
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
package com.reactnativekeyboardcontroller.listeners
|
|
2
2
|
|
|
3
|
-
import android.animation.ValueAnimator
|
|
4
3
|
import android.os.Build
|
|
5
4
|
import android.util.Log
|
|
6
5
|
import android.view.View
|
|
7
6
|
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener
|
|
8
|
-
import androidx.core.animation.doOnEnd
|
|
9
7
|
import androidx.core.graphics.Insets
|
|
10
8
|
import androidx.core.view.OnApplyWindowInsetsListener
|
|
11
9
|
import androidx.core.view.ViewCompat
|
|
@@ -26,6 +24,7 @@ import com.reactnativekeyboardcontroller.interactive.InteractiveKeyboardProvider
|
|
|
26
24
|
import kotlin.math.abs
|
|
27
25
|
|
|
28
26
|
private val TAG = KeyboardAnimationCallback::class.qualifiedName
|
|
27
|
+
private val isResizeHandledInCallbackMethods = Build.VERSION.SDK_INT < Build.VERSION_CODES.R
|
|
29
28
|
|
|
30
29
|
class KeyboardAnimationCallback(
|
|
31
30
|
val view: ReactViewGroup,
|
|
@@ -38,11 +37,12 @@ class KeyboardAnimationCallback(
|
|
|
38
37
|
|
|
39
38
|
// state variables
|
|
40
39
|
private var persistentKeyboardHeight = 0.0
|
|
40
|
+
private var prevKeyboardHeight = 0.0
|
|
41
41
|
private var isKeyboardVisible = false
|
|
42
42
|
private var isTransitioning = false
|
|
43
43
|
private var duration = 0
|
|
44
44
|
private var viewTagFocused = -1
|
|
45
|
-
private var
|
|
45
|
+
private var animationsToSkip = hashSetOf<WindowInsetsAnimationCompat>()
|
|
46
46
|
|
|
47
47
|
// listeners
|
|
48
48
|
private val focusListener = OnGlobalFocusChangeListener { oldFocus, newFocus ->
|
|
@@ -130,7 +130,7 @@ class KeyboardAnimationCallback(
|
|
|
130
130
|
// in this method
|
|
131
131
|
val isKeyboardSizeEqual = this.persistentKeyboardHeight == keyboardHeight
|
|
132
132
|
|
|
133
|
-
if (isKeyboardFullyVisible && !isKeyboardSizeEqual &&
|
|
133
|
+
if (isKeyboardFullyVisible && !isKeyboardSizeEqual && !isResizeHandledInCallbackMethods) {
|
|
134
134
|
Log.i(TAG, "onApplyWindowInsets: ${this.persistentKeyboardHeight} -> $keyboardHeight")
|
|
135
135
|
layoutObserver?.syncUpLayout()
|
|
136
136
|
this.onKeyboardResized(keyboardHeight)
|
|
@@ -139,6 +139,7 @@ class KeyboardAnimationCallback(
|
|
|
139
139
|
return insets
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
@Suppress("detekt:ReturnCount")
|
|
142
143
|
override fun onStart(
|
|
143
144
|
animation: WindowInsetsAnimationCompat,
|
|
144
145
|
bounds: WindowInsetsAnimationCompat.BoundsCompat,
|
|
@@ -158,6 +159,18 @@ class KeyboardAnimationCallback(
|
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
layoutObserver?.syncUpLayout()
|
|
162
|
+
|
|
163
|
+
// keyboard gets resized - we do not want to have a default animated transition
|
|
164
|
+
// so we skip these animations
|
|
165
|
+
val isKeyboardResized = keyboardHeight != 0.0 && prevKeyboardHeight != keyboardHeight
|
|
166
|
+
val isKeyboardShown = isKeyboardVisible && prevKeyboardHeight != 0.0
|
|
167
|
+
if (isKeyboardResized && isKeyboardShown && isResizeHandledInCallbackMethods) {
|
|
168
|
+
onKeyboardResized(keyboardHeight)
|
|
169
|
+
animationsToSkip.add(animation)
|
|
170
|
+
|
|
171
|
+
return bounds
|
|
172
|
+
}
|
|
173
|
+
|
|
161
174
|
context.emitEvent(
|
|
162
175
|
"KeyboardController::" + if (!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow",
|
|
163
176
|
getEventParams(keyboardHeight),
|
|
@@ -186,8 +199,8 @@ class KeyboardAnimationCallback(
|
|
|
186
199
|
): WindowInsetsCompat {
|
|
187
200
|
// onProgress() is called when any of the running animations progress...
|
|
188
201
|
|
|
189
|
-
// ignore non-keyboard animation
|
|
190
|
-
runningAnimations.find { it.isKeyboardAnimation } ?: return insets
|
|
202
|
+
// ignore non-keyboard animation or animation that we intentionally want to skip
|
|
203
|
+
runningAnimations.find { it.isKeyboardAnimation && !animationsToSkip.contains(it) } ?: return insets
|
|
191
204
|
|
|
192
205
|
// First we get the insets which are potentially deferred
|
|
193
206
|
val typesInset = insets.getInsets(deferredInsetTypes)
|
|
@@ -254,6 +267,13 @@ class KeyboardAnimationCallback(
|
|
|
254
267
|
InteractiveKeyboardProvider.shown = false
|
|
255
268
|
}
|
|
256
269
|
isKeyboardVisible = isKeyboardVisible || isKeyboardShown
|
|
270
|
+
prevKeyboardHeight = keyboardHeight
|
|
271
|
+
|
|
272
|
+
if (animation in animationsToSkip) {
|
|
273
|
+
duration = 0
|
|
274
|
+
animationsToSkip.remove(animation)
|
|
275
|
+
return
|
|
276
|
+
}
|
|
257
277
|
|
|
258
278
|
context.emitEvent(
|
|
259
279
|
"KeyboardController::" + if (!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow",
|
|
@@ -282,22 +302,10 @@ class KeyboardAnimationCallback(
|
|
|
282
302
|
}
|
|
283
303
|
|
|
284
304
|
/*
|
|
285
|
-
*
|
|
286
|
-
* - we dispatch `keyboardWillShow` (onStart);
|
|
287
|
-
* - we dispatch change height/progress as animated values (onProgress);
|
|
288
|
-
* - we dispatch `keyboardDidShow` (onEnd).
|
|
305
|
+
* Method that dispatches necessary events when keyboard gets resized
|
|
289
306
|
*/
|
|
290
307
|
private fun onKeyboardResized(keyboardHeight: Double) {
|
|
291
|
-
|
|
292
|
-
Log.i(TAG, "onKeyboardResized -> cancelling animation that is in progress")
|
|
293
|
-
// if animation is in progress, then we are:
|
|
294
|
-
// - removing listeners (update, onEnd)
|
|
295
|
-
// - updating `persistentKeyboardHeight` to latest animated value
|
|
296
|
-
// - cancelling animation to free up CPU resources
|
|
297
|
-
this.animation?.removeAllListeners()
|
|
298
|
-
this.persistentKeyboardHeight = (this.animation?.animatedValue as Float).toDouble()
|
|
299
|
-
this.animation?.cancel()
|
|
300
|
-
}
|
|
308
|
+
duration = 0
|
|
301
309
|
|
|
302
310
|
context.emitEvent("KeyboardController::keyboardWillShow", getEventParams(keyboardHeight))
|
|
303
311
|
context.dispatchEvent(
|
|
@@ -308,48 +316,36 @@ class KeyboardAnimationCallback(
|
|
|
308
316
|
"topKeyboardMoveStart",
|
|
309
317
|
keyboardHeight,
|
|
310
318
|
1.0,
|
|
311
|
-
|
|
319
|
+
0,
|
|
312
320
|
viewTagFocused,
|
|
313
321
|
),
|
|
314
322
|
)
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
val toValue = animator.animatedValue as Float
|
|
320
|
-
context.dispatchEvent(
|
|
323
|
+
context.dispatchEvent(
|
|
324
|
+
view.id,
|
|
325
|
+
KeyboardTransitionEvent(
|
|
326
|
+
surfaceId,
|
|
321
327
|
view.id,
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
animation.doOnEnd {
|
|
334
|
-
context.emitEvent("KeyboardController::keyboardDidShow", getEventParams(keyboardHeight))
|
|
335
|
-
context.dispatchEvent(
|
|
328
|
+
"topKeyboardMove",
|
|
329
|
+
keyboardHeight,
|
|
330
|
+
1.0,
|
|
331
|
+
0,
|
|
332
|
+
viewTagFocused,
|
|
333
|
+
),
|
|
334
|
+
)
|
|
335
|
+
context.dispatchEvent(
|
|
336
|
+
view.id,
|
|
337
|
+
KeyboardTransitionEvent(
|
|
338
|
+
surfaceId,
|
|
336
339
|
view.id,
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
),
|
|
346
|
-
)
|
|
347
|
-
this.animation = null
|
|
348
|
-
}
|
|
349
|
-
animation.setDuration(DEFAULT_ANIMATION_TIME.toLong()).startDelay = 0
|
|
350
|
-
animation.start()
|
|
340
|
+
"topKeyboardMoveEnd",
|
|
341
|
+
keyboardHeight,
|
|
342
|
+
1.0,
|
|
343
|
+
0,
|
|
344
|
+
viewTagFocused,
|
|
345
|
+
),
|
|
346
|
+
)
|
|
347
|
+
context.emitEvent("KeyboardController::keyboardDidShow", getEventParams(keyboardHeight))
|
|
351
348
|
|
|
352
|
-
this.animation = animation
|
|
353
349
|
this.persistentKeyboardHeight = keyboardHeight
|
|
354
350
|
}
|
|
355
351
|
|
|
@@ -377,8 +373,4 @@ class KeyboardAnimationCallback(
|
|
|
377
373
|
|
|
378
374
|
return params
|
|
379
375
|
}
|
|
380
|
-
|
|
381
|
-
companion object {
|
|
382
|
-
private const val DEFAULT_ANIMATION_TIME = 250
|
|
383
|
-
}
|
|
384
376
|
}
|
|
@@ -32,7 +32,7 @@ class KeyboardControllerModuleImpl(private val mReactContext: ReactApplicationCo
|
|
|
32
32
|
|
|
33
33
|
fun setFocusTo(direction: String) {
|
|
34
34
|
if (direction == "current") {
|
|
35
|
-
return FocusedInputHolder.
|
|
35
|
+
return FocusedInputHolder.focus()
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
val activity = mReactContext.currentActivity
|
package/android/src/main/java/com/reactnativekeyboardcontroller/traversal/FocusedInputHolder.kt
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
package com.reactnativekeyboardcontroller.traversal
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import android.widget.EditText
|
|
4
|
+
import com.reactnativekeyboardcontroller.extensions.focus
|
|
4
5
|
import java.lang.ref.WeakReference
|
|
5
6
|
|
|
6
7
|
object FocusedInputHolder {
|
|
7
|
-
private var input: WeakReference<
|
|
8
|
+
private var input: WeakReference<EditText?>? = null
|
|
8
9
|
|
|
9
|
-
fun set(textInput:
|
|
10
|
+
fun set(textInput: EditText) {
|
|
10
11
|
input = WeakReference(textInput)
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
fun
|
|
14
|
-
input?.get()
|
|
14
|
+
fun get(): EditText? {
|
|
15
|
+
return input?.get()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
fun focus() {
|
|
19
|
+
input?.get()?.focus()
|
|
15
20
|
}
|
|
16
21
|
}
|
package/android/src/main/java/com/reactnativekeyboardcontroller/traversal/ViewHierarchyNavigator.kt
CHANGED
|
@@ -4,18 +4,14 @@ import android.view.View
|
|
|
4
4
|
import android.view.ViewGroup
|
|
5
5
|
import android.widget.EditText
|
|
6
6
|
import com.facebook.react.bridge.UiThreadUtil
|
|
7
|
-
import com.
|
|
7
|
+
import com.reactnativekeyboardcontroller.extensions.focus
|
|
8
8
|
|
|
9
9
|
object ViewHierarchyNavigator {
|
|
10
10
|
fun setFocusTo(direction: String, view: View) {
|
|
11
11
|
val input = if (direction == "next") findNextEditText(view) else findPreviousEditText(view)
|
|
12
12
|
|
|
13
13
|
UiThreadUtil.runOnUiThread {
|
|
14
|
-
|
|
15
|
-
input.requestFocusFromJS()
|
|
16
|
-
} else {
|
|
17
|
-
input?.requestFocus()
|
|
18
|
-
}
|
|
14
|
+
input.focus()
|
|
19
15
|
}
|
|
20
16
|
}
|
|
21
17
|
|
package/android/src/test/java/com/reactnativekeyboardcontroller/traversal/FocusedInputHolderTest.kt
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
package com.reactnativekeyboardcontroller.traversal
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.widget.EditText
|
|
5
|
+
import androidx.test.core.app.ApplicationProvider
|
|
6
|
+
import org.junit.Assert.assertEquals
|
|
7
|
+
import org.junit.Assert.assertFalse
|
|
8
|
+
import org.junit.Assert.assertNull
|
|
9
|
+
import org.junit.Assert.assertTrue
|
|
10
|
+
import org.junit.Test
|
|
11
|
+
import org.junit.runner.RunWith
|
|
12
|
+
import org.robolectric.RobolectricTestRunner
|
|
13
|
+
|
|
14
|
+
@RunWith(RobolectricTestRunner::class)
|
|
15
|
+
class FocusedInputHolderTest {
|
|
16
|
+
@Test
|
|
17
|
+
fun `FocusedInputHolder should hold a weak reference`() {
|
|
18
|
+
val context = ApplicationProvider.getApplicationContext<Context>()
|
|
19
|
+
var input: EditText? = EditText(context)
|
|
20
|
+
|
|
21
|
+
FocusedInputHolder.set(input as EditText)
|
|
22
|
+
|
|
23
|
+
assertEquals(FocusedInputHolder.get(), input)
|
|
24
|
+
|
|
25
|
+
input = null
|
|
26
|
+
|
|
27
|
+
@Suppress("detekt:ExplicitGarbageCollectionCall")
|
|
28
|
+
System.gc()
|
|
29
|
+
|
|
30
|
+
assertNull(FocusedInputHolder.get())
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Test
|
|
34
|
+
fun `focus() should request focus on expected field`() {
|
|
35
|
+
val context = ApplicationProvider.getApplicationContext<Context>()
|
|
36
|
+
val input = EditText(context)
|
|
37
|
+
|
|
38
|
+
assertFalse(input.hasFocus())
|
|
39
|
+
|
|
40
|
+
FocusedInputHolder.set(input)
|
|
41
|
+
FocusedInputHolder.focus()
|
|
42
|
+
|
|
43
|
+
assertTrue(input.hasFocus())
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -4,6 +4,7 @@ import android.content.Context
|
|
|
4
4
|
import android.widget.EditText
|
|
5
5
|
import android.widget.LinearLayout
|
|
6
6
|
import androidx.test.core.app.ApplicationProvider
|
|
7
|
+
import com.reactnativekeyboardcontroller.extensions.focus
|
|
7
8
|
import org.junit.Assert.assertTrue
|
|
8
9
|
import org.junit.Before
|
|
9
10
|
import org.junit.Test
|
|
@@ -78,7 +79,7 @@ class ViewHierarchyNavigatorTest {
|
|
|
78
79
|
|
|
79
80
|
@Test
|
|
80
81
|
fun `setFocusTo to 'next' should set focus to next field`() {
|
|
81
|
-
editText1.
|
|
82
|
+
editText1.focus()
|
|
82
83
|
|
|
83
84
|
ViewHierarchyNavigator.setFocusTo("next", editText1)
|
|
84
85
|
|
|
@@ -89,7 +90,7 @@ class ViewHierarchyNavigatorTest {
|
|
|
89
90
|
|
|
90
91
|
@Test
|
|
91
92
|
fun `setFocusTo to 'prev' should set focus to previous field`() {
|
|
92
|
-
editText2.
|
|
93
|
+
editText2.focus()
|
|
93
94
|
|
|
94
95
|
ViewHierarchyNavigator.setFocusTo("prev", editText2)
|
|
95
96
|
|
|
@@ -100,7 +101,7 @@ class ViewHierarchyNavigatorTest {
|
|
|
100
101
|
|
|
101
102
|
@Test
|
|
102
103
|
fun `setFocusTo to 'next' should skip non-editable fields`() {
|
|
103
|
-
editText2.
|
|
104
|
+
editText2.focus()
|
|
104
105
|
|
|
105
106
|
ViewHierarchyNavigator.setFocusTo("next", editText2)
|
|
106
107
|
|
|
@@ -111,7 +112,7 @@ class ViewHierarchyNavigatorTest {
|
|
|
111
112
|
|
|
112
113
|
@Test
|
|
113
114
|
fun `setFocusTo to 'prev' should skip non-editable fields`() {
|
|
114
|
-
editText5.
|
|
115
|
+
editText5.focus()
|
|
115
116
|
|
|
116
117
|
ViewHierarchyNavigator.setFocusTo("prev", editText5)
|
|
117
118
|
|
|
@@ -122,7 +123,7 @@ class ViewHierarchyNavigatorTest {
|
|
|
122
123
|
|
|
123
124
|
@Test
|
|
124
125
|
fun `setFocusTo to 'next' should set focus relatively to current group`() {
|
|
125
|
-
editText5.
|
|
126
|
+
editText5.focus()
|
|
126
127
|
|
|
127
128
|
ViewHierarchyNavigator.setFocusTo("next", editText5)
|
|
128
129
|
|
|
@@ -133,7 +134,7 @@ class ViewHierarchyNavigatorTest {
|
|
|
133
134
|
|
|
134
135
|
@Test
|
|
135
136
|
fun `setFocusTo to 'prev' should set focus relatively to current group`() {
|
|
136
|
-
editText7.
|
|
137
|
+
editText7.focus()
|
|
137
138
|
|
|
138
139
|
ViewHierarchyNavigator.setFocusTo("prev", editText7)
|
|
139
140
|
|
|
@@ -144,7 +145,7 @@ class ViewHierarchyNavigatorTest {
|
|
|
144
145
|
|
|
145
146
|
@Test
|
|
146
147
|
fun `setFocusTo to 'next' should correctly exit from current group`() {
|
|
147
|
-
editText7.
|
|
148
|
+
editText7.focus()
|
|
148
149
|
|
|
149
150
|
ViewHierarchyNavigator.setFocusTo("next", editText7)
|
|
150
151
|
|
|
@@ -155,7 +156,7 @@ class ViewHierarchyNavigatorTest {
|
|
|
155
156
|
|
|
156
157
|
@Test
|
|
157
158
|
fun `setFocusTo to 'prev' should set focus to last element in group`() {
|
|
158
|
-
editText8.
|
|
159
|
+
editText8.focus()
|
|
159
160
|
|
|
160
161
|
ViewHierarchyNavigator.setFocusTo("prev", editText8)
|
|
161
162
|
|
|
@@ -166,7 +167,7 @@ class ViewHierarchyNavigatorTest {
|
|
|
166
167
|
|
|
167
168
|
@Test
|
|
168
169
|
fun `setFocusTo to 'next' should do nothing if it's last element`() {
|
|
169
|
-
editText13.
|
|
170
|
+
editText13.focus()
|
|
170
171
|
|
|
171
172
|
ViewHierarchyNavigator.setFocusTo("next", editText13)
|
|
172
173
|
|
|
@@ -177,7 +178,7 @@ class ViewHierarchyNavigatorTest {
|
|
|
177
178
|
|
|
178
179
|
@Test
|
|
179
180
|
fun `setFocusTo to 'prev' should do nothing if it's first element`() {
|
|
180
|
-
editText1.
|
|
181
|
+
editText1.focus()
|
|
181
182
|
|
|
182
183
|
ViewHierarchyNavigator.setFocusTo("prev", editText1)
|
|
183
184
|
|
|
@@ -19,14 +19,14 @@ class FocusedInputHolder {
|
|
|
19
19
|
currentFocusedInput = input
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
// Requests focus for the currentFocusedInput if it's set
|
|
23
|
-
func requestFocus() {
|
|
24
|
-
currentFocusedInput?.requestFocus()
|
|
25
|
-
}
|
|
26
|
-
|
|
27
22
|
func get() -> TextInput? {
|
|
28
23
|
return currentFocusedInput
|
|
29
24
|
}
|
|
30
25
|
|
|
26
|
+
// Requests focus for the currentFocusedInput if it's set
|
|
27
|
+
func focus() {
|
|
28
|
+
currentFocusedInput?.focus()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
31
|
private init() {}
|
|
32
32
|
}
|
|
@@ -10,17 +10,17 @@ import Foundation
|
|
|
10
10
|
import UIKit
|
|
11
11
|
|
|
12
12
|
public protocol TextInput: AnyObject {
|
|
13
|
-
func
|
|
13
|
+
func focus()
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
extension UITextField: TextInput {
|
|
17
|
-
public func
|
|
17
|
+
public func focus() {
|
|
18
18
|
becomeFirstResponder()
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
extension UITextView: TextInput {
|
|
23
|
-
public func
|
|
23
|
+
public func focus() {
|
|
24
24
|
becomeFirstResponder()
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -14,7 +14,7 @@ public class ViewHierarchyNavigator: NSObject {
|
|
|
14
14
|
@objc public static func setFocusTo(direction: String) {
|
|
15
15
|
DispatchQueue.main.async {
|
|
16
16
|
if direction == "current" {
|
|
17
|
-
FocusedInputHolder.shared.
|
|
17
|
+
FocusedInputHolder.shared.focus()
|
|
18
18
|
return
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -22,7 +22,7 @@ public class ViewHierarchyNavigator: NSObject {
|
|
|
22
22
|
guard let view = input else { return }
|
|
23
23
|
|
|
24
24
|
let textField = findTextInputInDirection(currentFocus: view, direction: direction)
|
|
25
|
-
textField?.
|
|
25
|
+
textField?.focus()
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -7,9 +7,7 @@ exports.default = void 0;
|
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
|
|
10
|
-
var _useKeyboardInterpolation = _interopRequireDefault(require("../hooks/useKeyboardInterpolation"));
|
|
11
10
|
var _hooks = require("./hooks");
|
|
12
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
11
|
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
12
|
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
13
|
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
@@ -47,9 +45,6 @@ const KeyboardAvoidingView = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) =>
|
|
|
47
45
|
const keyboardY = screenHeight - keyboard.heightWhenOpened.value - keyboardVerticalOffset;
|
|
48
46
|
return Math.max(frame.value.y + frame.value.height - keyboardY, 0);
|
|
49
47
|
}, [screenHeight, keyboardVerticalOffset]);
|
|
50
|
-
const {
|
|
51
|
-
interpolate
|
|
52
|
-
} = (0, _useKeyboardInterpolation.default)();
|
|
53
48
|
const onLayoutWorklet = (0, _react.useCallback)(layout => {
|
|
54
49
|
"worklet";
|
|
55
50
|
|
|
@@ -62,7 +57,7 @@ const KeyboardAvoidingView = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) =>
|
|
|
62
57
|
onLayoutProps === null || onLayoutProps === void 0 ? void 0 : onLayoutProps(e);
|
|
63
58
|
}, [onLayoutProps]);
|
|
64
59
|
const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
|
|
65
|
-
const bottom = interpolate(keyboard.
|
|
60
|
+
const bottom = (0, _reactNativeReanimated.interpolate)(keyboard.progress.value, [0, 1], [0, relativeKeyboardHeight()]);
|
|
66
61
|
const bottomHeight = enabled ? bottom : 0;
|
|
67
62
|
switch (behavior) {
|
|
68
63
|
case "height":
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","_hooks","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","_extends","assign","bind","target","i","arguments","length","source","apply","defaultLayout","x","y","width","height","KeyboardAvoidingView","forwardRef","_ref","ref","behavior","children","contentContainerStyle","enabled","keyboardVerticalOffset","style","onLayout","onLayoutProps","props","initialFrame","useSharedValue","frame","useDerivedValue","value","keyboard","useKeyboardAnimation","screenHeight","useWindowDimensions","relativeKeyboardHeight","useCallback","keyboardY","heightWhenOpened","Math","max","onLayoutWorklet","layout","isClosed","e","runOnUI","nativeEvent","animatedStyle","useAnimatedStyle","bottom","interpolate","progress","bottomHeight","flex","paddingBottom","isPositionBehavior","containerStyle","combinedStyles","useMemo","createElement","View","_default","exports"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useMemo } from \"react\";\nimport { View, useWindowDimensions } from \"react-native\";\nimport Reanimated, {\n interpolate,\n runOnUI,\n useAnimatedStyle,\n useDerivedValue,\n useSharedValue,\n} from \"react-native-reanimated\";\n\nimport { useKeyboardAnimation } from \"./hooks\";\n\nimport type { LayoutRectangle, ViewProps } from \"react-native\";\n\ntype Props = {\n /**\n * Specify how to react to the presence of the keyboard.\n */\n behavior?: \"height\" | \"position\" | \"padding\";\n\n /**\n * Style of the content container when `behavior` is 'position'.\n */\n contentContainerStyle?: ViewProps[\"style\"];\n\n /**\n * Controls whether this `KeyboardAvoidingView` instance should take effect.\n * This is useful when more than one is on the screen. Defaults to true.\n */\n enabled?: boolean;\n\n /**\n * Distance between the top of the user screen and the React Native view. This\n * may be non-zero in some cases. Defaults to 0.\n */\n keyboardVerticalOffset?: number;\n} & ViewProps;\n\nconst defaultLayout: LayoutRectangle = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n};\n\n/**\n * View that moves out of the way when the keyboard appears by automatically\n * adjusting its height, position, or bottom padding.\n */\nconst KeyboardAvoidingView = forwardRef<View, React.PropsWithChildren<Props>>(\n (\n {\n behavior,\n children,\n contentContainerStyle,\n enabled = true,\n keyboardVerticalOffset = 0,\n style,\n onLayout: onLayoutProps,\n ...props\n },\n ref,\n ) => {\n const initialFrame = useSharedValue<LayoutRectangle | null>(null);\n const frame = useDerivedValue(() => initialFrame.value || defaultLayout);\n\n const keyboard = useKeyboardAnimation();\n const { height: screenHeight } = useWindowDimensions();\n\n const relativeKeyboardHeight = useCallback(() => {\n \"worklet\";\n\n const keyboardY =\n screenHeight - keyboard.heightWhenOpened.value - keyboardVerticalOffset;\n\n return Math.max(frame.value.y + frame.value.height - keyboardY, 0);\n }, [screenHeight, keyboardVerticalOffset]);\n\n const onLayoutWorklet = useCallback((layout: LayoutRectangle) => {\n \"worklet\";\n\n if (keyboard.isClosed.value) {\n initialFrame.value = layout;\n }\n }, []);\n const onLayout = useCallback<NonNullable<ViewProps[\"onLayout\"]>>(\n (e) => {\n runOnUI(onLayoutWorklet)(e.nativeEvent.layout);\n onLayoutProps?.(e);\n },\n [onLayoutProps],\n );\n\n const animatedStyle = useAnimatedStyle(() => {\n const bottom = interpolate(\n keyboard.progress.value,\n [0, 1],\n [0, relativeKeyboardHeight()],\n );\n const bottomHeight = enabled ? bottom : 0;\n\n switch (behavior) {\n case \"height\":\n if (!keyboard.isClosed.value) {\n return {\n height: frame.value.height - bottomHeight,\n flex: 0,\n };\n }\n\n return {};\n\n case \"position\":\n return { bottom: bottomHeight };\n\n case \"padding\":\n return { paddingBottom: bottomHeight };\n\n default:\n return {};\n }\n }, [behavior, enabled, relativeKeyboardHeight]);\n const isPositionBehavior = behavior === \"position\";\n const containerStyle = isPositionBehavior ? contentContainerStyle : style;\n const combinedStyles = useMemo(\n () => [containerStyle, animatedStyle],\n [containerStyle, animatedStyle],\n );\n\n if (isPositionBehavior) {\n return (\n <View ref={ref} style={style} onLayout={onLayout} {...props}>\n <Reanimated.View style={combinedStyles}>{children}</Reanimated.View>\n </View>\n );\n }\n\n return (\n <Reanimated.View\n ref={ref}\n onLayout={onLayout}\n style={combinedStyles}\n {...props}\n >\n {children}\n </Reanimated.View>\n );\n },\n);\n\nexport default KeyboardAvoidingView;\n"],"mappings":"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"}
|
|
@@ -91,7 +91,7 @@ const KeyboardAwareScrollView = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref)
|
|
|
91
91
|
scrollViewAnimatedRef(assignedRef);
|
|
92
92
|
}, []);
|
|
93
93
|
const onScrollViewLayout = (0, _react.useCallback)(e => {
|
|
94
|
-
scrollViewTarget.value =
|
|
94
|
+
scrollViewTarget.value = (0, _reactNative.findNodeHandle)(scrollViewAnimatedRef.current);
|
|
95
95
|
onLayout === null || onLayout === void 0 ? void 0 : onLayout(e);
|
|
96
96
|
}, [onLayout]);
|
|
97
97
|
|
|
@@ -225,8 +225,9 @@ const KeyboardAwareScrollView = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref)
|
|
|
225
225
|
return /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.ScrollView, _extends({
|
|
226
226
|
ref: onRef
|
|
227
227
|
}, rest, {
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
onLayout: onScrollViewLayout
|
|
229
|
+
// @ts-expect-error `onScrollReanimated` is a fake prop needed for reanimated to intercept scroll events
|
|
230
|
+
,
|
|
230
231
|
onScrollReanimated: onScroll,
|
|
231
232
|
scrollEventThrottle: 16
|
|
232
233
|
}), children, /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, {
|