react-native-keyboard-controller 1.0.0-alpha.3 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -7
- package/android/build.gradle +1 -1
- package/android/gradle.properties +2 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/EdgeToEdgeReactViewGroup.kt +2 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt +6 -6
- package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +6 -2
- package/android/src/main/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +81 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/TranslateDeferringInsetsAnimationCallback.kt +41 -41
- package/ios/.swift-version +1 -0
- package/ios/.swiftformat +5 -0
- package/ios/.swiftlint.yml +5 -0
- package/ios/KeyboardControllerModule.swift +17 -17
- package/ios/KeyboardControllerViewManager.swift +87 -59
- package/ios/KeyboardMoveEvent.swift +31 -27
- package/lib/commonjs/animated.js +14 -11
- package/lib/commonjs/animated.js.map +1 -1
- package/lib/commonjs/index.js +2 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/monkey-patch.js +44 -0
- package/lib/commonjs/monkey-patch.js.map +1 -0
- package/lib/module/animated.js +13 -12
- package/lib/module/animated.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/monkey-patch.js +36 -0
- package/lib/module/monkey-patch.js.map +1 -0
- package/lib/typescript/animated.d.ts +5 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/monkey-patch.d.ts +1 -0
- package/package.json +6 -3
- package/src/animated.tsx +20 -16
- package/src/index.ts +2 -0
- package/src/monkey-patch.tsx +36 -0
- package/android/.gradle/6.8/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/6.8/fileChanges/last-build.bin +0 -0
- package/android/.gradle/6.8/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/6.8/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/checksums/checksums.lock +0 -0
- package/android/.gradle/configuration-cache/gc.properties +0 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/ios/KeyboardController.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/ios/KeyboardController.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/ios/KeyboardController.xcodeproj/project.xcworkspace/xcuserdata/kiryl.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/KeyboardController.xcodeproj/xcuserdata/kiryl.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
package/README.md
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Keyboard manager which works in identical way on both iOS and Android.
|
|
4
4
|
|
|
5
|
-
> **Note**: This library is still in development and in `
|
|
5
|
+
> **Note**: This library is still in development and in `beta` stage. So most likely it has bugs/issues - don't hesitate to report if you find them 🙂.
|
|
6
6
|
|
|
7
7
|
## Demonstration
|
|
8
8
|
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
</div>
|
|
9
|
+
<img src="./gifs/demo.gif?raw=true" width="60%">
|
|
10
|
+
|
|
11
|
+
## Key features
|
|
13
12
|
|
|
14
13
|
- mapping keyboard appearance to animated values 😎
|
|
15
14
|
- missing `keyboardWillShow` / `keyboardWillHide` events are available on Android 😍
|
|
@@ -18,8 +17,6 @@ Keyboard manager which works in identical way on both iOS and Android.
|
|
|
18
17
|
- interactive keyboard dismissing (planned) 👆📱
|
|
19
18
|
- and more is coming... Stay tuned! 😊
|
|
20
19
|
|
|
21
|
-
<br clear="right" />
|
|
22
|
-
|
|
23
20
|
## Installation
|
|
24
21
|
|
|
25
22
|
Install `react-native-keyboard-controller` package from npm:
|
package/android/build.gradle
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
package com.reactnativekeyboardcontroller
|
|
2
2
|
|
|
3
|
+
import android.annotation.SuppressLint
|
|
3
4
|
import androidx.core.view.WindowCompat
|
|
4
5
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
5
6
|
import com.facebook.react.views.view.ReactViewGroup
|
|
6
7
|
|
|
8
|
+
@SuppressLint("ViewConstructor")
|
|
7
9
|
class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : ReactViewGroup(reactContext) {
|
|
8
10
|
override fun onAttachedToWindow() {
|
|
9
11
|
super.onAttachedToWindow()
|
package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt
CHANGED
|
@@ -6,11 +6,11 @@ import com.facebook.react.bridge.ReactApplicationContext
|
|
|
6
6
|
import com.facebook.react.uimanager.ViewManager
|
|
7
7
|
|
|
8
8
|
class KeyboardControllerPackage : ReactPackage {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
10
|
+
return listOf(KeyboardControllerModule(reactContext), StatusBarManagerCompatModule(reactContext))
|
|
11
|
+
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
14
|
+
return listOf(KeyboardControllerViewManager(reactContext))
|
|
15
|
+
}
|
|
16
16
|
}
|
package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
package com.reactnativekeyboardcontroller
|
|
2
2
|
|
|
3
3
|
import androidx.appcompat.widget.FitWindowsLinearLayout
|
|
4
|
-
import androidx.core.view
|
|
4
|
+
import androidx.core.view.ViewCompat
|
|
5
|
+
import androidx.core.view.WindowInsetsAnimationCompat
|
|
6
|
+
import androidx.core.view.WindowInsetsCompat
|
|
5
7
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
8
|
import com.facebook.react.common.MapBuilder
|
|
7
9
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
@@ -18,6 +20,8 @@ class KeyboardControllerViewManager(reactContext: ReactApplicationContext) : Rea
|
|
|
18
20
|
|
|
19
21
|
override fun createViewInstance(reactContext: ThemedReactContext): ReactViewGroup {
|
|
20
22
|
val view = EdgeToEdgeReactViewGroup(reactContext)
|
|
23
|
+
val window = mReactContext.currentActivity!!.window
|
|
24
|
+
val decorView = window.decorView
|
|
21
25
|
|
|
22
26
|
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
|
|
23
27
|
val content =
|
|
@@ -33,7 +37,7 @@ class KeyboardControllerViewManager(reactContext: ReactApplicationContext) : Rea
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
ViewCompat.setWindowInsetsAnimationCallback(
|
|
36
|
-
|
|
40
|
+
decorView,
|
|
37
41
|
TranslateDeferringInsetsAnimationCallback(
|
|
38
42
|
view = view,
|
|
39
43
|
persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),
|
package/android/src/main/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
package com.reactnativekeyboardcontroller
|
|
2
|
+
|
|
3
|
+
import android.animation.ArgbEvaluator
|
|
4
|
+
import android.animation.ValueAnimator
|
|
5
|
+
import android.os.Build
|
|
6
|
+
import androidx.annotation.RequiresApi
|
|
7
|
+
import androidx.core.view.WindowInsetsCompat
|
|
8
|
+
import androidx.core.view.WindowInsetsControllerCompat
|
|
9
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
10
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
11
|
+
import com.facebook.react.bridge.ReactMethod
|
|
12
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
13
|
+
|
|
14
|
+
class StatusBarManagerCompatModule(private val mReactContext: ReactApplicationContext) : ReactContextBaseJavaModule(mReactContext) {
|
|
15
|
+
private var controller: WindowInsetsControllerCompat? = null
|
|
16
|
+
|
|
17
|
+
override fun getName(): String = "StatusBarManagerCompat"
|
|
18
|
+
|
|
19
|
+
@ReactMethod
|
|
20
|
+
private fun setHidden(hidden: Boolean) {
|
|
21
|
+
UiThreadUtil.runOnUiThread {
|
|
22
|
+
if (hidden) {
|
|
23
|
+
getController()?.hide(WindowInsetsCompat.Type.statusBars())
|
|
24
|
+
} else {
|
|
25
|
+
getController()?.show(WindowInsetsCompat.Type.statusBars())
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
|
31
|
+
@ReactMethod
|
|
32
|
+
private fun setColor(color: Int, animated: Boolean) {
|
|
33
|
+
UiThreadUtil.runOnUiThread {
|
|
34
|
+
val window = mReactContext.currentActivity!!.window
|
|
35
|
+
|
|
36
|
+
if (animated) {
|
|
37
|
+
val curColor: Int = window.statusBarColor
|
|
38
|
+
val colorAnimation = ValueAnimator.ofObject(ArgbEvaluator(), curColor, color)
|
|
39
|
+
colorAnimation.addUpdateListener { animator ->
|
|
40
|
+
window.statusBarColor = animator.animatedValue as Int
|
|
41
|
+
}
|
|
42
|
+
colorAnimation.setDuration(300).startDelay = 0
|
|
43
|
+
colorAnimation.start()
|
|
44
|
+
} else {
|
|
45
|
+
window.statusBarColor = color
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@ReactMethod
|
|
51
|
+
private fun setTranslucent(translucent: Boolean) {
|
|
52
|
+
// the status bar is translucent by default (once you wrapped App in Provider,
|
|
53
|
+
// and EdgeToEdgeReactViewGroup has been mounted and called
|
|
54
|
+
// `setDecorFitsSystemWindows(window, false)`. By default this library applies default padding
|
|
55
|
+
// which equal to StatusBar height, so it will have a default RN app behavior. Though once you
|
|
56
|
+
// need to set StatusBar as translucent, you will need to use `statusBarTranslucent` prop on
|
|
57
|
+
// `KeyboardProvider` (it will preventing of applying additional padding, and status bar will be
|
|
58
|
+
// translucent. Though it's important to note, that this value is not reactive (i. e. if you change
|
|
59
|
+
// `statusBarTranslucent` in runtime it will not have any effect. Just theoretically I could make
|
|
60
|
+
// it reactive, but I know, that most of apps or don't use StatusBar translucency at all or they are
|
|
61
|
+
// specifying it for entire app, so I don't see a lot of sense to make it reactive as of now. If your
|
|
62
|
+
// app requires to dynamically manage it - just shoot an issue and I will try to add a support fot that.
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@ReactMethod
|
|
66
|
+
private fun setStyle(style: String) {
|
|
67
|
+
UiThreadUtil.runOnUiThread {
|
|
68
|
+
getController()?.isAppearanceLightStatusBars = style == "dark-content"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private fun getController(): WindowInsetsControllerCompat? {
|
|
73
|
+
if (this.controller == null) {
|
|
74
|
+
val window = mReactContext.currentActivity!!.window
|
|
75
|
+
|
|
76
|
+
this.controller = WindowInsetsControllerCompat(window, window.decorView)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return this.controller
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -61,10 +61,10 @@ class TranslateDeferringInsetsAnimationCallback(
|
|
|
61
61
|
private var persistentKeyboardHeight = 0
|
|
62
62
|
|
|
63
63
|
init {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
require(persistentInsetTypes and deferredInsetTypes == 0) {
|
|
65
|
+
"persistentInsetTypes and deferredInsetTypes can not contain any of " +
|
|
66
|
+
" same WindowInsetsCompat.Type values"
|
|
67
|
+
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
override fun onStart(
|
|
@@ -81,9 +81,9 @@ class TranslateDeferringInsetsAnimationCallback(
|
|
|
81
81
|
|
|
82
82
|
val params: WritableMap = Arguments.createMap()
|
|
83
83
|
params.putInt("height", keyboardHeight)
|
|
84
|
-
context?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit("KeyboardController::" + if(!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow", params)
|
|
84
|
+
context?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit("KeyboardController::" + if (!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow", params)
|
|
85
85
|
|
|
86
|
-
Log.i(TAG, "KeyboardController::" + if(!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow")
|
|
86
|
+
Log.i(TAG, "KeyboardController::" + if (!isKeyboardVisible) "keyboardWillHide" else "keyboardWillShow")
|
|
87
87
|
Log.i(TAG, "HEIGHT:: $keyboardHeight")
|
|
88
88
|
|
|
89
89
|
return super.onStart(animation, bounds)
|
|
@@ -95,9 +95,9 @@ class TranslateDeferringInsetsAnimationCallback(
|
|
|
95
95
|
val isKeyboardVisible = isKeyboardVisible()
|
|
96
96
|
|
|
97
97
|
val params: WritableMap = Arguments.createMap()
|
|
98
|
-
context?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit("KeyboardController::" + if(!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow", params)
|
|
98
|
+
context?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit("KeyboardController::" + if (!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow", params)
|
|
99
99
|
|
|
100
|
-
Log.i(TAG, "KeyboardController::" + if(!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow")
|
|
100
|
+
Log.i(TAG, "KeyboardController::" + if (!isKeyboardVisible) "keyboardDidHide" else "keyboardDidShow")
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
private fun isKeyboardVisible(): Boolean {
|
|
@@ -115,38 +115,38 @@ class TranslateDeferringInsetsAnimationCallback(
|
|
|
115
115
|
return Math.max(toDp((keyboardHeight - navigationBar).toFloat(), context!!), 0)
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
Log.i(TAG, "DiffY: $diffY $height")
|
|
144
|
-
|
|
145
|
-
context
|
|
146
|
-
.getNativeModule(UIManagerModule::class.java)
|
|
147
|
-
?.eventDispatcher
|
|
148
|
-
?.dispatchEvent(KeyboardTransitionEvent(view.id, height, progress))
|
|
149
|
-
|
|
150
|
-
return insets
|
|
118
|
+
override fun onProgress(
|
|
119
|
+
insets: WindowInsetsCompat,
|
|
120
|
+
runningAnimations: List<WindowInsetsAnimationCompat>
|
|
121
|
+
): WindowInsetsCompat {
|
|
122
|
+
// onProgress() is called when any of the running animations progress...
|
|
123
|
+
|
|
124
|
+
// First we get the insets which are potentially deferred
|
|
125
|
+
val typesInset = insets.getInsets(deferredInsetTypes)
|
|
126
|
+
// Then we get the persistent inset types which are applied as padding during layout
|
|
127
|
+
val otherInset = insets.getInsets(persistentInsetTypes)
|
|
128
|
+
|
|
129
|
+
// Now that we subtract the two insets, to calculate the difference. We also coerce
|
|
130
|
+
// the insets to be >= 0, to make sure we don't use negative insets.
|
|
131
|
+
val diff = Insets.subtract(typesInset, otherInset).let {
|
|
132
|
+
Insets.max(it, Insets.NONE)
|
|
133
|
+
}
|
|
134
|
+
val diffY = (diff.top - diff.bottom).toFloat()
|
|
135
|
+
val height = toDp(diffY, context!!)
|
|
136
|
+
|
|
137
|
+
var progress = 0.0
|
|
138
|
+
try {
|
|
139
|
+
progress = Math.abs((height.toDouble() / persistentKeyboardHeight))
|
|
140
|
+
} catch (e: ArithmeticException) {
|
|
141
|
+
// do nothing, send progress as 0
|
|
151
142
|
}
|
|
143
|
+
Log.i(TAG, "DiffY: $diffY $height")
|
|
144
|
+
|
|
145
|
+
context
|
|
146
|
+
.getNativeModule(UIManagerModule::class.java)
|
|
147
|
+
?.eventDispatcher
|
|
148
|
+
?.dispatchEvent(KeyboardTransitionEvent(view.id, height, progress))
|
|
149
|
+
|
|
150
|
+
return insets
|
|
151
|
+
}
|
|
152
152
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
5.6
|
package/ios/.swiftformat
ADDED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
// Copyright © 2022 Facebook. All rights reserved.
|
|
7
7
|
//
|
|
8
8
|
|
|
9
|
+
import AVFoundation
|
|
9
10
|
import Foundation
|
|
10
11
|
import UIKit
|
|
11
|
-
import AVFoundation
|
|
12
12
|
|
|
13
13
|
@objc(KeyboardController)
|
|
14
14
|
class KeyboardController: RCTEventEmitter {
|
|
15
15
|
public static var shared: KeyboardController?
|
|
16
16
|
private var hasListeners = false
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
override class func requiresMainQueueSetup() -> Bool {
|
|
19
19
|
return false
|
|
20
20
|
}
|
|
@@ -25,29 +25,29 @@ class KeyboardController: RCTEventEmitter {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// Android stubs
|
|
28
|
-
@objc func setInputMode(_
|
|
28
|
+
@objc func setInputMode(_: NSNumber!) {}
|
|
29
29
|
@objc func setDefaultMode() {}
|
|
30
|
-
|
|
31
|
-
@objc open
|
|
30
|
+
|
|
31
|
+
@objc override open func supportedEvents() -> [String] {
|
|
32
32
|
return [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
"KeyboardController::keyboardWillShow",
|
|
34
|
+
"KeyboardController::keyboardDidShow",
|
|
35
|
+
"KeyboardController::keyboardWillHide",
|
|
36
|
+
"KeyboardController::keyboardDidHide",
|
|
37
37
|
]
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
@objc open
|
|
39
|
+
|
|
40
|
+
@objc override open func startObserving() {
|
|
41
41
|
hasListeners = true
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
@objc open
|
|
43
|
+
|
|
44
|
+
@objc override open func stopObserving() {
|
|
45
45
|
hasListeners = false
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
@objc open
|
|
49
|
-
if
|
|
50
|
-
|
|
47
|
+
|
|
48
|
+
@objc override open func sendEvent(withName name: String!, body: Any!) {
|
|
49
|
+
if hasListeners {
|
|
50
|
+
super.sendEvent(withName: name, body: body)
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -5,74 +5,102 @@ class KeyboardControllerViewManager: RCTViewManager {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
override func view() -> (KeyboardControllerView) {
|
|
8
|
-
|
|
8
|
+
return KeyboardControllerView(frame: CGRect.zero, eventDispatcher: bridge.eventDispatcher())
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
class KeyboardControllerView
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
init(frame: CGRect, eventDispatcher: RCTEventDispatcherProtocol) {
|
|
17
|
-
self.eventDispatcher = eventDispatcher
|
|
18
|
-
super.init(frame: frame)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
required init?(coder: NSCoder) {
|
|
22
|
-
fatalError("init(coder:) has not been implemented")
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
override func willMove(toWindow newWindow: UIWindow?) {
|
|
26
|
-
if (newWindow == nil) {
|
|
27
|
-
// Will be removed from a window
|
|
28
|
-
NotificationCenter.default.removeObserver(self)
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
override func didMoveToWindow() {
|
|
33
|
-
if (self.window != nil) {
|
|
34
|
-
// Added to a window
|
|
35
|
-
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear), name: UIResponder.keyboardWillHideNotification, object: nil)
|
|
36
|
-
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear), name: UIResponder.keyboardWillShowNotification, object: nil)
|
|
37
|
-
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidAppear), name: UIResponder.keyboardDidShowNotification, object: nil)
|
|
38
|
-
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidDisappear), name: UIResponder.keyboardDidHideNotification, object: nil)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
12
|
+
class KeyboardControllerView: UIView {
|
|
13
|
+
private var eventDispatcher: RCTEventDispatcherProtocol
|
|
14
|
+
@objc var onKeyboardMove: RCTDirectEventBlock?
|
|
41
15
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
16
|
+
init(frame: CGRect, eventDispatcher: RCTEventDispatcherProtocol) {
|
|
17
|
+
self.eventDispatcher = eventDispatcher
|
|
18
|
+
super.init(frame: frame)
|
|
19
|
+
}
|
|
45
20
|
|
|
46
|
-
|
|
21
|
+
@available(*, unavailable)
|
|
22
|
+
required init?(coder _: NSCoder) {
|
|
23
|
+
fatalError("init(coder:) has not been implemented")
|
|
24
|
+
}
|
|
47
25
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
26
|
+
override func willMove(toWindow newWindow: UIWindow?) {
|
|
27
|
+
if newWindow == nil {
|
|
28
|
+
// Will be removed from a window
|
|
29
|
+
// swiftlint:disable notification_center_detachment
|
|
30
|
+
NotificationCenter.default.removeObserver(self)
|
|
52
31
|
}
|
|
32
|
+
}
|
|
53
33
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
34
|
+
override func didMoveToWindow() {
|
|
35
|
+
if window != nil {
|
|
36
|
+
// Added to a window
|
|
37
|
+
NotificationCenter.default.addObserver(
|
|
38
|
+
self,
|
|
39
|
+
selector: #selector(keyboardWillDisappear),
|
|
40
|
+
name: UIResponder.keyboardWillHideNotification,
|
|
41
|
+
object: nil
|
|
42
|
+
)
|
|
43
|
+
NotificationCenter.default.addObserver(
|
|
44
|
+
self,
|
|
45
|
+
selector: #selector(keyboardWillAppear),
|
|
46
|
+
name: UIResponder.keyboardWillShowNotification,
|
|
47
|
+
object: nil
|
|
48
|
+
)
|
|
49
|
+
NotificationCenter.default.addObserver(
|
|
50
|
+
self,
|
|
51
|
+
selector: #selector(keyboardDidAppear),
|
|
52
|
+
name: UIResponder.keyboardDidShowNotification,
|
|
53
|
+
object: nil
|
|
54
|
+
)
|
|
55
|
+
NotificationCenter.default.addObserver(
|
|
56
|
+
self,
|
|
57
|
+
selector: #selector(keyboardDidDisappear),
|
|
58
|
+
name: UIResponder.keyboardDidHideNotification,
|
|
59
|
+
object: nil
|
|
60
|
+
)
|
|
60
61
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@objc func keyboardWillAppear(_ notification: Notification) {
|
|
65
|
+
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
|
|
66
|
+
let keyboardHeight = keyboardFrame.cgRectValue.size.height
|
|
67
|
+
|
|
68
|
+
eventDispatcher.send(
|
|
69
|
+
KeyboardMoveEvent(
|
|
70
|
+
viewTag: reactTag,
|
|
71
|
+
height: -keyboardHeight as NSNumber,
|
|
72
|
+
progress: 1
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
var data = [AnyHashable: Any]()
|
|
77
|
+
data["height"] = keyboardHeight
|
|
78
|
+
KeyboardController.shared?.sendEvent(withName: "KeyboardController::keyboardWillShow", body: data)
|
|
71
79
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@objc func keyboardWillDisappear() {
|
|
83
|
+
eventDispatcher.send(KeyboardMoveEvent(viewTag: reactTag, height: 0, progress: 0))
|
|
84
|
+
|
|
85
|
+
var data = [AnyHashable: Any]()
|
|
86
|
+
data["height"] = 0
|
|
87
|
+
KeyboardController.shared?.sendEvent(withName: "KeyboardController::keyboardWillHide", body: data)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@objc func keyboardDidAppear(_ notification: Notification) {
|
|
91
|
+
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
|
|
92
|
+
let keyboardHeight = keyboardFrame.cgRectValue.size.height
|
|
93
|
+
|
|
94
|
+
var data = [AnyHashable: Any]()
|
|
95
|
+
data["height"] = keyboardHeight
|
|
96
|
+
|
|
97
|
+
KeyboardController.shared?.sendEvent(withName: "KeyboardController::keyboardDidShow", body: data)
|
|
77
98
|
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@objc func keyboardDidDisappear() {
|
|
102
|
+
var data = [AnyHashable: Any]()
|
|
103
|
+
data["height"] = 0
|
|
104
|
+
KeyboardController.shared?.sendEvent(withName: "KeyboardController::keyboardDidHide", body: data)
|
|
105
|
+
}
|
|
78
106
|
}
|
|
@@ -7,31 +7,35 @@
|
|
|
7
7
|
//
|
|
8
8
|
|
|
9
9
|
class KeyboardMoveEvent: NSObject, RCTEvent {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
10
|
+
var viewTag: NSNumber!
|
|
11
|
+
var eventName: String = "onKeyboardMove"
|
|
12
|
+
var coalescingKey: UInt16 = 0
|
|
13
|
+
var height: NSNumber!
|
|
14
|
+
var progress: NSNumber!
|
|
15
|
+
|
|
16
|
+
func canCoalesce() -> Bool {
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
func coalesce(with newEvent: RCTEvent!) -> RCTEvent! {
|
|
21
|
+
return newEvent
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static func moduleDotMethod() -> String! {
|
|
25
|
+
return "RCTEventEmitter.receiveEvent"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func arguments() -> [Any]! {
|
|
29
|
+
return [
|
|
30
|
+
viewTag,
|
|
31
|
+
RCTNormalizeInputEventName(eventName),
|
|
32
|
+
["height": height, "progress": progress],
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
init(viewTag: NSNumber, height: NSNumber, progress: NSNumber) {
|
|
37
|
+
self.viewTag = viewTag
|
|
38
|
+
self.height = height
|
|
39
|
+
self.progress = progress
|
|
40
|
+
}
|
|
37
41
|
}
|
package/lib/commonjs/animated.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.useReanimatedKeyboardAnimation = exports.useKeyboardAnimation = exports.styles = exports.KeyboardProvider = void 0;
|
|
6
|
+
exports.useReanimatedKeyboardAnimation = exports.useKeyboardAnimation = exports.styles = exports.KeyboardProvider = exports.KeyboardContext = void 0;
|
|
7
7
|
|
|
8
8
|
var _react = _interopRequireWildcard(require("react"));
|
|
9
9
|
|
|
@@ -36,6 +36,8 @@ const defaultContext = {
|
|
|
36
36
|
|
|
37
37
|
const KeyboardContext = /*#__PURE__*/_react.default.createContext(defaultContext);
|
|
38
38
|
|
|
39
|
+
exports.KeyboardContext = KeyboardContext;
|
|
40
|
+
|
|
39
41
|
const useKeyboardAnimation = () => {
|
|
40
42
|
(0, _native.useResizeMode)();
|
|
41
43
|
const context = (0, _react.useContext)(KeyboardContext);
|
|
@@ -87,8 +89,8 @@ const KeyboardProvider = _ref => {
|
|
|
87
89
|
children,
|
|
88
90
|
statusBarTranslucent
|
|
89
91
|
} = _ref;
|
|
90
|
-
const progress = (0, _react.
|
|
91
|
-
const height = (0, _react.
|
|
92
|
+
const progress = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
93
|
+
const height = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
92
94
|
const progressSV = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
93
95
|
const heightSV = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
94
96
|
const context = (0, _react.useMemo)(() => ({
|
|
@@ -101,6 +103,13 @@ const KeyboardProvider = _ref => {
|
|
|
101
103
|
height: heightSV
|
|
102
104
|
}
|
|
103
105
|
}), []);
|
|
106
|
+
const style = (0, _react.useMemo)(() => [styles.hidden, {
|
|
107
|
+
transform: [{
|
|
108
|
+
translateX: height
|
|
109
|
+
}, {
|
|
110
|
+
translateY: progress
|
|
111
|
+
}]
|
|
112
|
+
}], []);
|
|
104
113
|
const onKeyboardMove = (0, _react.useMemo)(() => _reactNative.Animated.event([{
|
|
105
114
|
nativeEvent: {
|
|
106
115
|
progress,
|
|
@@ -125,7 +134,7 @@ const KeyboardProvider = _ref => {
|
|
|
125
134
|
statusBarTranslucent: statusBarTranslucent,
|
|
126
135
|
style: styles.container
|
|
127
136
|
}, /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, {
|
|
128
|
-
|
|
137
|
+
// we are using this small hack, because if the component (where
|
|
129
138
|
// animated value has been used) is unmounted, then animation will
|
|
130
139
|
// stop receiving events (seems like it's react-native optimization).
|
|
131
140
|
// So we need to keep a reference to the animated value, to keep it's
|
|
@@ -133,13 +142,7 @@ const KeyboardProvider = _ref => {
|
|
|
133
142
|
//
|
|
134
143
|
// To test why it's needed, try to open screen which consumes Animated.Value
|
|
135
144
|
// then close it and open it again (for example 'Animated transition').
|
|
136
|
-
|
|
137
|
-
transform: [{
|
|
138
|
-
translateX: height
|
|
139
|
-
}, {
|
|
140
|
-
translateY: progress
|
|
141
|
-
}]
|
|
142
|
-
}]
|
|
145
|
+
style: style
|
|
143
146
|
}), children)));
|
|
144
147
|
};
|
|
145
148
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["animated.tsx"],"names":["KeyboardControllerViewAnimated","Reanimated","createAnimatedComponent","Animated","KeyboardControllerView","defaultContext","animated","progress","Value","height","reanimated","value","KeyboardContext","React","createContext","useKeyboardAnimation","context","useReanimatedKeyboardAnimation","useAnimatedKeyboardHandler","handlers","dependencies","doDependenciesDiffer","event","onKeyboardMove","eventName","endsWith","styles","StyleSheet","create","container","flex","hidden","display","position","KeyboardProvider","children","statusBarTranslucent","progressSV","heightSV","
|
|
1
|
+
{"version":3,"sources":["animated.tsx"],"names":["KeyboardControllerViewAnimated","Reanimated","createAnimatedComponent","Animated","KeyboardControllerView","defaultContext","animated","progress","Value","height","reanimated","value","KeyboardContext","React","createContext","useKeyboardAnimation","context","useReanimatedKeyboardAnimation","useAnimatedKeyboardHandler","handlers","dependencies","doDependenciesDiffer","event","onKeyboardMove","eventName","endsWith","styles","StyleSheet","create","container","flex","hidden","display","position","KeyboardProvider","children","statusBarTranslucent","current","progressSV","heightSV","style","transform","translateX","translateY","nativeEvent","useNativeDriver","handler"],"mappings":"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","sourcesContent":["import React, { useContext, useMemo, useRef } from 'react';\nimport { Animated, StyleSheet, ViewStyle } from 'react-native';\nimport Reanimated, {\n useEvent,\n useHandler,\n useSharedValue,\n} from 'react-native-reanimated';\nimport {\n EventWithName,\n KeyboardControllerProps,\n KeyboardControllerView,\n NativeEvent,\n useResizeMode,\n} from './native';\n\nconst KeyboardControllerViewAnimated = Reanimated.createAnimatedComponent(\n Animated.createAnimatedComponent(\n KeyboardControllerView\n ) as React.FC<KeyboardControllerProps>\n);\n\ntype AnimatedContext = {\n progress: Animated.Value;\n height: Animated.Value;\n};\ntype ReanimatedContext = {\n progress: Reanimated.SharedValue<number>;\n height: Reanimated.SharedValue<number>;\n};\ntype KeyboardAnimationContext = {\n animated: AnimatedContext;\n reanimated: ReanimatedContext;\n};\nconst defaultContext: KeyboardAnimationContext = {\n animated: {\n progress: new Animated.Value(0),\n height: new Animated.Value(0),\n },\n reanimated: {\n progress: { value: 0 },\n height: { value: 0 },\n },\n};\nexport const KeyboardContext = React.createContext(defaultContext);\n\nexport const useKeyboardAnimation = (): AnimatedContext => {\n useResizeMode();\n const context = useContext(KeyboardContext);\n\n return context.animated;\n};\n\nexport const useReanimatedKeyboardAnimation = (): ReanimatedContext => {\n useResizeMode();\n const context = useContext(KeyboardContext);\n\n return context.reanimated;\n};\n\nfunction useAnimatedKeyboardHandler<TContext extends Record<string, unknown>>(\n handlers: {\n onKeyboardMove?: (e: NativeEvent, context: TContext) => void;\n },\n dependencies?: ReadonlyArray<unknown>\n) {\n const { context, doDependenciesDiffer } = useHandler(handlers, dependencies);\n\n return useEvent(\n (event: EventWithName<NativeEvent>) => {\n 'worklet';\n const { onKeyboardMove } = handlers;\n\n if (onKeyboardMove && event.eventName.endsWith('onKeyboardMove')) {\n onKeyboardMove(event, context);\n }\n },\n ['onKeyboardMove'],\n doDependenciesDiffer\n );\n}\n\ntype Styles = {\n container: ViewStyle;\n hidden: ViewStyle;\n};\n\nexport const styles = StyleSheet.create<Styles>({\n container: {\n flex: 1,\n },\n hidden: {\n display: 'none',\n position: 'absolute',\n },\n});\n\ntype KeyboardProviderProps = {\n children: React.ReactNode;\n /**\n * Set the value to `true`, if you use translucent status bar on Android.\n * If you already control status bar translucency via `react-native-screens`\n * or `StatusBar` component from `react-native`, you can ignore it.\n * Defaults to `false`.\n *\n * @see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/14\n * @platform android\n */\n statusBarTranslucent?: boolean;\n};\n\nexport const KeyboardProvider = ({\n children,\n statusBarTranslucent,\n}: KeyboardProviderProps) => {\n const progress = useRef(new Animated.Value(0)).current;\n const height = useRef(new Animated.Value(0)).current;\n const progressSV = useSharedValue(0);\n const heightSV = useSharedValue(0);\n const context = useMemo(\n () => ({\n animated: { progress: progress, height: height },\n reanimated: { progress: progressSV, height: heightSV },\n }),\n []\n );\n const style = useMemo(\n () => [\n styles.hidden,\n { transform: [{ translateX: height }, { translateY: progress }] },\n ],\n []\n );\n\n const onKeyboardMove = useMemo(\n () =>\n Animated.event(\n [\n {\n nativeEvent: {\n progress,\n height,\n },\n },\n ],\n { useNativeDriver: true }\n ),\n []\n );\n\n const handler = useAnimatedKeyboardHandler(\n {\n onKeyboardMove: (event: NativeEvent) => {\n 'worklet';\n progressSV.value = event.progress;\n heightSV.value = event.height;\n },\n },\n []\n );\n\n return (\n <KeyboardContext.Provider value={context}>\n <KeyboardControllerViewAnimated\n onKeyboardMoveReanimated={handler}\n onKeyboardMove={onKeyboardMove}\n statusBarTranslucent={statusBarTranslucent}\n style={styles.container}\n >\n <>\n <Animated.View\n // we are using this small hack, because if the component (where\n // animated value has been used) is unmounted, then animation will\n // stop receiving events (seems like it's react-native optimization).\n // So we need to keep a reference to the animated value, to keep it's\n // always mounted (keep a reference to an animated value).\n //\n // To test why it's needed, try to open screen which consumes Animated.Value\n // then close it and open it again (for example 'Animated transition').\n style={style}\n />\n {children}\n </>\n </KeyboardControllerViewAnimated>\n </KeyboardContext.Provider>\n );\n};\n"]}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["index.ts"],"names":[],"mappings":";;;;;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AACA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AACA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA","sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["index.ts"],"names":[],"mappings":";;;;;;AAAA;;AAEA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AACA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AACA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA","sourcesContent":["import './monkey-patch';\n\nexport * from './native';\nexport * from './animated';\nexport * from './replicas';\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _reactNative = require("react-native");
|
|
4
|
+
|
|
5
|
+
var NativeAndroidManager = _interopRequireWildcard(require("react-native/Libraries/Components/StatusBar/NativeStatusBarManagerAndroid"));
|
|
6
|
+
|
|
7
|
+
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); }
|
|
8
|
+
|
|
9
|
+
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; }
|
|
10
|
+
|
|
11
|
+
// @ts-expect-error because there is no corresponding type definition
|
|
12
|
+
const getConstants = NativeAndroidManager.default.getConstants;
|
|
13
|
+
const RCTStatusBarManagerCompat = _reactNative.NativeModules.StatusBarManagerCompat; // On Android < 11 RN uses legacy API which breaks EdgeToEdge mode in RN, so
|
|
14
|
+
// in order to use library on all available platforms we have to monkey patch
|
|
15
|
+
// default RN implementation and use modern `WindowInsetsControllerCompat`.
|
|
16
|
+
|
|
17
|
+
if (_reactNative.Platform.OS === 'android') {
|
|
18
|
+
NativeAndroidManager.default = {
|
|
19
|
+
getConstants,
|
|
20
|
+
|
|
21
|
+
setColor(color, animated) {
|
|
22
|
+
RCTStatusBarManagerCompat.setColor(color, animated);
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
setTranslucent(translucent) {
|
|
26
|
+
RCTStatusBarManagerCompat.setTranslucent(translucent);
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* - statusBarStyles can be:
|
|
31
|
+
* - 'default'
|
|
32
|
+
* - 'dark-content'
|
|
33
|
+
*/
|
|
34
|
+
setStyle(statusBarStyle) {
|
|
35
|
+
RCTStatusBarManagerCompat.setStyle(statusBarStyle);
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
setHidden(hidden) {
|
|
39
|
+
RCTStatusBarManagerCompat.setHidden(hidden);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=monkey-patch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["monkey-patch.tsx"],"names":["getConstants","NativeAndroidManager","default","RCTStatusBarManagerCompat","NativeModules","StatusBarManagerCompat","Platform","OS","setColor","color","animated","setTranslucent","translucent","setStyle","statusBarStyle","setHidden","hidden"],"mappings":";;AAAA;;AAEA;;;;;;AADA;AAGA,MAAMA,YAAY,GAAGC,oBAAoB,CAACC,OAArB,CAA6BF,YAAlD;AAEA,MAAMG,yBAAyB,GAAGC,2BAAcC,sBAAhD,C,CAEA;AACA;AACA;;AACA,IAAIC,sBAASC,EAAT,KAAgB,SAApB,EAA+B;AAC7BN,EAAAA,oBAAoB,CAACC,OAArB,GAA+B;AAC7BF,IAAAA,YAD6B;;AAE7BQ,IAAAA,QAAQ,CAACC,KAAD,EAAgBC,QAAhB,EAAyC;AAC/CP,MAAAA,yBAAyB,CAACK,QAA1B,CAAmCC,KAAnC,EAA0CC,QAA1C;AACD,KAJ4B;;AAM7BC,IAAAA,cAAc,CAACC,WAAD,EAA6B;AACzCT,MAAAA,yBAAyB,CAACQ,cAA1B,CAAyCC,WAAzC;AACD,KAR4B;;AAU7B;AACJ;AACA;AACA;AACA;AACIC,IAAAA,QAAQ,CAACC,cAAD,EAAgC;AACtCX,MAAAA,yBAAyB,CAACU,QAA1B,CAAmCC,cAAnC;AACD,KAjB4B;;AAmB7BC,IAAAA,SAAS,CAACC,MAAD,EAAwB;AAC/Bb,MAAAA,yBAAyB,CAACY,SAA1B,CAAoCC,MAApC;AACD;;AArB4B,GAA/B;AAuBD","sourcesContent":["import { NativeModules, Platform } from 'react-native';\n// @ts-expect-error because there is no corresponding type definition\nimport * as NativeAndroidManager from 'react-native/Libraries/Components/StatusBar/NativeStatusBarManagerAndroid';\n\nconst getConstants = NativeAndroidManager.default.getConstants;\n\nconst RCTStatusBarManagerCompat = NativeModules.StatusBarManagerCompat;\n\n// On Android < 11 RN uses legacy API which breaks EdgeToEdge mode in RN, so\n// in order to use library on all available platforms we have to monkey patch\n// default RN implementation and use modern `WindowInsetsControllerCompat`.\nif (Platform.OS === 'android') {\n NativeAndroidManager.default = {\n getConstants,\n setColor(color: number, animated: boolean): void {\n RCTStatusBarManagerCompat.setColor(color, animated);\n },\n\n setTranslucent(translucent: boolean): void {\n RCTStatusBarManagerCompat.setTranslucent(translucent);\n },\n\n /**\n * - statusBarStyles can be:\n * - 'default'\n * - 'dark-content'\n */\n setStyle(statusBarStyle?: string): void {\n RCTStatusBarManagerCompat.setStyle(statusBarStyle);\n },\n\n setHidden(hidden: boolean): void {\n RCTStatusBarManagerCompat.setHidden(hidden);\n },\n };\n}\n"]}
|
package/lib/module/animated.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useContext, useMemo } from 'react';
|
|
1
|
+
import React, { useContext, useMemo, useRef } from 'react';
|
|
2
2
|
import { Animated, StyleSheet } from 'react-native';
|
|
3
3
|
import Reanimated, { useEvent, useHandler, useSharedValue } from 'react-native-reanimated';
|
|
4
4
|
import { KeyboardControllerView, useResizeMode } from './native';
|
|
@@ -17,7 +17,7 @@ const defaultContext = {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
|
-
const KeyboardContext = /*#__PURE__*/React.createContext(defaultContext);
|
|
20
|
+
export const KeyboardContext = /*#__PURE__*/React.createContext(defaultContext);
|
|
21
21
|
export const useKeyboardAnimation = () => {
|
|
22
22
|
useResizeMode();
|
|
23
23
|
const context = useContext(KeyboardContext);
|
|
@@ -61,8 +61,8 @@ export const KeyboardProvider = _ref => {
|
|
|
61
61
|
children,
|
|
62
62
|
statusBarTranslucent
|
|
63
63
|
} = _ref;
|
|
64
|
-
const progress =
|
|
65
|
-
const height =
|
|
64
|
+
const progress = useRef(new Animated.Value(0)).current;
|
|
65
|
+
const height = useRef(new Animated.Value(0)).current;
|
|
66
66
|
const progressSV = useSharedValue(0);
|
|
67
67
|
const heightSV = useSharedValue(0);
|
|
68
68
|
const context = useMemo(() => ({
|
|
@@ -75,6 +75,13 @@ export const KeyboardProvider = _ref => {
|
|
|
75
75
|
height: heightSV
|
|
76
76
|
}
|
|
77
77
|
}), []);
|
|
78
|
+
const style = useMemo(() => [styles.hidden, {
|
|
79
|
+
transform: [{
|
|
80
|
+
translateX: height
|
|
81
|
+
}, {
|
|
82
|
+
translateY: progress
|
|
83
|
+
}]
|
|
84
|
+
}], []);
|
|
78
85
|
const onKeyboardMove = useMemo(() => Animated.event([{
|
|
79
86
|
nativeEvent: {
|
|
80
87
|
progress,
|
|
@@ -99,7 +106,7 @@ export const KeyboardProvider = _ref => {
|
|
|
99
106
|
statusBarTranslucent: statusBarTranslucent,
|
|
100
107
|
style: styles.container
|
|
101
108
|
}, /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Animated.View, {
|
|
102
|
-
|
|
109
|
+
// we are using this small hack, because if the component (where
|
|
103
110
|
// animated value has been used) is unmounted, then animation will
|
|
104
111
|
// stop receiving events (seems like it's react-native optimization).
|
|
105
112
|
// So we need to keep a reference to the animated value, to keep it's
|
|
@@ -107,13 +114,7 @@ export const KeyboardProvider = _ref => {
|
|
|
107
114
|
//
|
|
108
115
|
// To test why it's needed, try to open screen which consumes Animated.Value
|
|
109
116
|
// then close it and open it again (for example 'Animated transition').
|
|
110
|
-
|
|
111
|
-
transform: [{
|
|
112
|
-
translateX: height
|
|
113
|
-
}, {
|
|
114
|
-
translateY: progress
|
|
115
|
-
}]
|
|
116
|
-
}]
|
|
117
|
+
style: style
|
|
117
118
|
}), children)));
|
|
118
119
|
};
|
|
119
120
|
//# sourceMappingURL=animated.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["animated.tsx"],"names":["React","useContext","useMemo","Animated","StyleSheet","Reanimated","useEvent","useHandler","useSharedValue","KeyboardControllerView","useResizeMode","KeyboardControllerViewAnimated","createAnimatedComponent","defaultContext","animated","progress","Value","height","reanimated","value","KeyboardContext","createContext","useKeyboardAnimation","context","useReanimatedKeyboardAnimation","useAnimatedKeyboardHandler","handlers","dependencies","doDependenciesDiffer","event","onKeyboardMove","eventName","endsWith","styles","create","container","flex","hidden","display","position","KeyboardProvider","children","statusBarTranslucent","progressSV","heightSV","
|
|
1
|
+
{"version":3,"sources":["animated.tsx"],"names":["React","useContext","useMemo","useRef","Animated","StyleSheet","Reanimated","useEvent","useHandler","useSharedValue","KeyboardControllerView","useResizeMode","KeyboardControllerViewAnimated","createAnimatedComponent","defaultContext","animated","progress","Value","height","reanimated","value","KeyboardContext","createContext","useKeyboardAnimation","context","useReanimatedKeyboardAnimation","useAnimatedKeyboardHandler","handlers","dependencies","doDependenciesDiffer","event","onKeyboardMove","eventName","endsWith","styles","create","container","flex","hidden","display","position","KeyboardProvider","children","statusBarTranslucent","current","progressSV","heightSV","style","transform","translateX","translateY","nativeEvent","useNativeDriver","handler"],"mappings":"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","sourcesContent":["import React, { useContext, useMemo, useRef } from 'react';\nimport { Animated, StyleSheet, ViewStyle } from 'react-native';\nimport Reanimated, {\n useEvent,\n useHandler,\n useSharedValue,\n} from 'react-native-reanimated';\nimport {\n EventWithName,\n KeyboardControllerProps,\n KeyboardControllerView,\n NativeEvent,\n useResizeMode,\n} from './native';\n\nconst KeyboardControllerViewAnimated = Reanimated.createAnimatedComponent(\n Animated.createAnimatedComponent(\n KeyboardControllerView\n ) as React.FC<KeyboardControllerProps>\n);\n\ntype AnimatedContext = {\n progress: Animated.Value;\n height: Animated.Value;\n};\ntype ReanimatedContext = {\n progress: Reanimated.SharedValue<number>;\n height: Reanimated.SharedValue<number>;\n};\ntype KeyboardAnimationContext = {\n animated: AnimatedContext;\n reanimated: ReanimatedContext;\n};\nconst defaultContext: KeyboardAnimationContext = {\n animated: {\n progress: new Animated.Value(0),\n height: new Animated.Value(0),\n },\n reanimated: {\n progress: { value: 0 },\n height: { value: 0 },\n },\n};\nexport const KeyboardContext = React.createContext(defaultContext);\n\nexport const useKeyboardAnimation = (): AnimatedContext => {\n useResizeMode();\n const context = useContext(KeyboardContext);\n\n return context.animated;\n};\n\nexport const useReanimatedKeyboardAnimation = (): ReanimatedContext => {\n useResizeMode();\n const context = useContext(KeyboardContext);\n\n return context.reanimated;\n};\n\nfunction useAnimatedKeyboardHandler<TContext extends Record<string, unknown>>(\n handlers: {\n onKeyboardMove?: (e: NativeEvent, context: TContext) => void;\n },\n dependencies?: ReadonlyArray<unknown>\n) {\n const { context, doDependenciesDiffer } = useHandler(handlers, dependencies);\n\n return useEvent(\n (event: EventWithName<NativeEvent>) => {\n 'worklet';\n const { onKeyboardMove } = handlers;\n\n if (onKeyboardMove && event.eventName.endsWith('onKeyboardMove')) {\n onKeyboardMove(event, context);\n }\n },\n ['onKeyboardMove'],\n doDependenciesDiffer\n );\n}\n\ntype Styles = {\n container: ViewStyle;\n hidden: ViewStyle;\n};\n\nexport const styles = StyleSheet.create<Styles>({\n container: {\n flex: 1,\n },\n hidden: {\n display: 'none',\n position: 'absolute',\n },\n});\n\ntype KeyboardProviderProps = {\n children: React.ReactNode;\n /**\n * Set the value to `true`, if you use translucent status bar on Android.\n * If you already control status bar translucency via `react-native-screens`\n * or `StatusBar` component from `react-native`, you can ignore it.\n * Defaults to `false`.\n *\n * @see https://github.com/kirillzyusko/react-native-keyboard-controller/issues/14\n * @platform android\n */\n statusBarTranslucent?: boolean;\n};\n\nexport const KeyboardProvider = ({\n children,\n statusBarTranslucent,\n}: KeyboardProviderProps) => {\n const progress = useRef(new Animated.Value(0)).current;\n const height = useRef(new Animated.Value(0)).current;\n const progressSV = useSharedValue(0);\n const heightSV = useSharedValue(0);\n const context = useMemo(\n () => ({\n animated: { progress: progress, height: height },\n reanimated: { progress: progressSV, height: heightSV },\n }),\n []\n );\n const style = useMemo(\n () => [\n styles.hidden,\n { transform: [{ translateX: height }, { translateY: progress }] },\n ],\n []\n );\n\n const onKeyboardMove = useMemo(\n () =>\n Animated.event(\n [\n {\n nativeEvent: {\n progress,\n height,\n },\n },\n ],\n { useNativeDriver: true }\n ),\n []\n );\n\n const handler = useAnimatedKeyboardHandler(\n {\n onKeyboardMove: (event: NativeEvent) => {\n 'worklet';\n progressSV.value = event.progress;\n heightSV.value = event.height;\n },\n },\n []\n );\n\n return (\n <KeyboardContext.Provider value={context}>\n <KeyboardControllerViewAnimated\n onKeyboardMoveReanimated={handler}\n onKeyboardMove={onKeyboardMove}\n statusBarTranslucent={statusBarTranslucent}\n style={styles.container}\n >\n <>\n <Animated.View\n // we are using this small hack, because if the component (where\n // animated value has been used) is unmounted, then animation will\n // stop receiving events (seems like it's react-native optimization).\n // So we need to keep a reference to the animated value, to keep it's\n // always mounted (keep a reference to an animated value).\n //\n // To test why it's needed, try to open screen which consumes Animated.Value\n // then close it and open it again (for example 'Animated transition').\n style={style}\n />\n {children}\n </>\n </KeyboardControllerViewAnimated>\n </KeyboardContext.Provider>\n );\n};\n"]}
|
package/lib/module/index.js
CHANGED
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAd;AACA,cAAc,YAAd;AACA,cAAc,YAAd","sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAP;AAEA,cAAc,UAAd;AACA,cAAc,YAAd;AACA,cAAc,YAAd","sourcesContent":["import './monkey-patch';\n\nexport * from './native';\nexport * from './animated';\nexport * from './replicas';\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { NativeModules, Platform } from 'react-native'; // @ts-expect-error because there is no corresponding type definition
|
|
2
|
+
|
|
3
|
+
import * as NativeAndroidManager from 'react-native/Libraries/Components/StatusBar/NativeStatusBarManagerAndroid';
|
|
4
|
+
const getConstants = NativeAndroidManager.default.getConstants;
|
|
5
|
+
const RCTStatusBarManagerCompat = NativeModules.StatusBarManagerCompat; // On Android < 11 RN uses legacy API which breaks EdgeToEdge mode in RN, so
|
|
6
|
+
// in order to use library on all available platforms we have to monkey patch
|
|
7
|
+
// default RN implementation and use modern `WindowInsetsControllerCompat`.
|
|
8
|
+
|
|
9
|
+
if (Platform.OS === 'android') {
|
|
10
|
+
NativeAndroidManager.default = {
|
|
11
|
+
getConstants,
|
|
12
|
+
|
|
13
|
+
setColor(color, animated) {
|
|
14
|
+
RCTStatusBarManagerCompat.setColor(color, animated);
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
setTranslucent(translucent) {
|
|
18
|
+
RCTStatusBarManagerCompat.setTranslucent(translucent);
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* - statusBarStyles can be:
|
|
23
|
+
* - 'default'
|
|
24
|
+
* - 'dark-content'
|
|
25
|
+
*/
|
|
26
|
+
setStyle(statusBarStyle) {
|
|
27
|
+
RCTStatusBarManagerCompat.setStyle(statusBarStyle);
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
setHidden(hidden) {
|
|
31
|
+
RCTStatusBarManagerCompat.setHidden(hidden);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=monkey-patch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["monkey-patch.tsx"],"names":["NativeModules","Platform","NativeAndroidManager","getConstants","default","RCTStatusBarManagerCompat","StatusBarManagerCompat","OS","setColor","color","animated","setTranslucent","translucent","setStyle","statusBarStyle","setHidden","hidden"],"mappings":"AAAA,SAASA,aAAT,EAAwBC,QAAxB,QAAwC,cAAxC,C,CACA;;AACA,OAAO,KAAKC,oBAAZ,MAAsC,2EAAtC;AAEA,MAAMC,YAAY,GAAGD,oBAAoB,CAACE,OAArB,CAA6BD,YAAlD;AAEA,MAAME,yBAAyB,GAAGL,aAAa,CAACM,sBAAhD,C,CAEA;AACA;AACA;;AACA,IAAIL,QAAQ,CAACM,EAAT,KAAgB,SAApB,EAA+B;AAC7BL,EAAAA,oBAAoB,CAACE,OAArB,GAA+B;AAC7BD,IAAAA,YAD6B;;AAE7BK,IAAAA,QAAQ,CAACC,KAAD,EAAgBC,QAAhB,EAAyC;AAC/CL,MAAAA,yBAAyB,CAACG,QAA1B,CAAmCC,KAAnC,EAA0CC,QAA1C;AACD,KAJ4B;;AAM7BC,IAAAA,cAAc,CAACC,WAAD,EAA6B;AACzCP,MAAAA,yBAAyB,CAACM,cAA1B,CAAyCC,WAAzC;AACD,KAR4B;;AAU7B;AACJ;AACA;AACA;AACA;AACIC,IAAAA,QAAQ,CAACC,cAAD,EAAgC;AACtCT,MAAAA,yBAAyB,CAACQ,QAA1B,CAAmCC,cAAnC;AACD,KAjB4B;;AAmB7BC,IAAAA,SAAS,CAACC,MAAD,EAAwB;AAC/BX,MAAAA,yBAAyB,CAACU,SAA1B,CAAoCC,MAApC;AACD;;AArB4B,GAA/B;AAuBD","sourcesContent":["import { NativeModules, Platform } from 'react-native';\n// @ts-expect-error because there is no corresponding type definition\nimport * as NativeAndroidManager from 'react-native/Libraries/Components/StatusBar/NativeStatusBarManagerAndroid';\n\nconst getConstants = NativeAndroidManager.default.getConstants;\n\nconst RCTStatusBarManagerCompat = NativeModules.StatusBarManagerCompat;\n\n// On Android < 11 RN uses legacy API which breaks EdgeToEdge mode in RN, so\n// in order to use library on all available platforms we have to monkey patch\n// default RN implementation and use modern `WindowInsetsControllerCompat`.\nif (Platform.OS === 'android') {\n NativeAndroidManager.default = {\n getConstants,\n setColor(color: number, animated: boolean): void {\n RCTStatusBarManagerCompat.setColor(color, animated);\n },\n\n setTranslucent(translucent: boolean): void {\n RCTStatusBarManagerCompat.setTranslucent(translucent);\n },\n\n /**\n * - statusBarStyles can be:\n * - 'default'\n * - 'dark-content'\n */\n setStyle(statusBarStyle?: string): void {\n RCTStatusBarManagerCompat.setStyle(statusBarStyle);\n },\n\n setHidden(hidden: boolean): void {\n RCTStatusBarManagerCompat.setHidden(hidden);\n },\n };\n}\n"]}
|
|
@@ -9,6 +9,11 @@ declare type ReanimatedContext = {
|
|
|
9
9
|
progress: Reanimated.SharedValue<number>;
|
|
10
10
|
height: Reanimated.SharedValue<number>;
|
|
11
11
|
};
|
|
12
|
+
declare type KeyboardAnimationContext = {
|
|
13
|
+
animated: AnimatedContext;
|
|
14
|
+
reanimated: ReanimatedContext;
|
|
15
|
+
};
|
|
16
|
+
export declare const KeyboardContext: React.Context<KeyboardAnimationContext>;
|
|
12
17
|
export declare const useKeyboardAnimation: () => AnimatedContext;
|
|
13
18
|
export declare const useReanimatedKeyboardAnimation: () => ReanimatedContext;
|
|
14
19
|
declare type Styles = {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-keyboard-controller",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-beta.0",
|
|
4
4
|
"description": "Platform agnostic keyboard manager",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"scripts": {
|
|
25
25
|
"test": "jest",
|
|
26
26
|
"typescript": "tsc --noEmit",
|
|
27
|
-
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
27
|
+
"lint": "eslint --quiet \"**/*.{js,ts,tsx}\"",
|
|
28
28
|
"prepare": "bob build",
|
|
29
29
|
"release": "release-it",
|
|
30
30
|
"example": "yarn --cwd example",
|
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
},
|
|
34
34
|
"keywords": [
|
|
35
35
|
"react-native",
|
|
36
|
+
"keyboard",
|
|
37
|
+
"animation",
|
|
36
38
|
"ios",
|
|
37
39
|
"android"
|
|
38
40
|
],
|
|
@@ -118,7 +120,8 @@
|
|
|
118
120
|
"trailingComma": "es5",
|
|
119
121
|
"useTabs": false
|
|
120
122
|
}
|
|
121
|
-
]
|
|
123
|
+
],
|
|
124
|
+
"react-hooks/exhaustive-deps": "warn"
|
|
122
125
|
}
|
|
123
126
|
},
|
|
124
127
|
"eslintIgnore": [
|
package/src/animated.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useContext, useMemo } from 'react';
|
|
1
|
+
import React, { useContext, useMemo, useRef } from 'react';
|
|
2
2
|
import { Animated, StyleSheet, ViewStyle } from 'react-native';
|
|
3
3
|
import Reanimated, {
|
|
4
4
|
useEvent,
|
|
@@ -41,7 +41,7 @@ const defaultContext: KeyboardAnimationContext = {
|
|
|
41
41
|
height: { value: 0 },
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
|
-
const KeyboardContext = React.createContext(defaultContext);
|
|
44
|
+
export const KeyboardContext = React.createContext(defaultContext);
|
|
45
45
|
|
|
46
46
|
export const useKeyboardAnimation = (): AnimatedContext => {
|
|
47
47
|
useResizeMode();
|
|
@@ -112,8 +112,8 @@ export const KeyboardProvider = ({
|
|
|
112
112
|
children,
|
|
113
113
|
statusBarTranslucent,
|
|
114
114
|
}: KeyboardProviderProps) => {
|
|
115
|
-
const progress =
|
|
116
|
-
const height =
|
|
115
|
+
const progress = useRef(new Animated.Value(0)).current;
|
|
116
|
+
const height = useRef(new Animated.Value(0)).current;
|
|
117
117
|
const progressSV = useSharedValue(0);
|
|
118
118
|
const heightSV = useSharedValue(0);
|
|
119
119
|
const context = useMemo(
|
|
@@ -123,6 +123,13 @@ export const KeyboardProvider = ({
|
|
|
123
123
|
}),
|
|
124
124
|
[]
|
|
125
125
|
);
|
|
126
|
+
const style = useMemo(
|
|
127
|
+
() => [
|
|
128
|
+
styles.hidden,
|
|
129
|
+
{ transform: [{ translateX: height }, { translateY: progress }] },
|
|
130
|
+
],
|
|
131
|
+
[]
|
|
132
|
+
);
|
|
126
133
|
|
|
127
134
|
const onKeyboardMove = useMemo(
|
|
128
135
|
() =>
|
|
@@ -161,18 +168,15 @@ export const KeyboardProvider = ({
|
|
|
161
168
|
>
|
|
162
169
|
<>
|
|
163
170
|
<Animated.View
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
styles.hidden,
|
|
174
|
-
{ transform: [{ translateX: height }, { translateY: progress }] },
|
|
175
|
-
]}
|
|
171
|
+
// we are using this small hack, because if the component (where
|
|
172
|
+
// animated value has been used) is unmounted, then animation will
|
|
173
|
+
// stop receiving events (seems like it's react-native optimization).
|
|
174
|
+
// So we need to keep a reference to the animated value, to keep it's
|
|
175
|
+
// always mounted (keep a reference to an animated value).
|
|
176
|
+
//
|
|
177
|
+
// To test why it's needed, try to open screen which consumes Animated.Value
|
|
178
|
+
// then close it and open it again (for example 'Animated transition').
|
|
179
|
+
style={style}
|
|
176
180
|
/>
|
|
177
181
|
{children}
|
|
178
182
|
</>
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { NativeModules, Platform } from 'react-native';
|
|
2
|
+
// @ts-expect-error because there is no corresponding type definition
|
|
3
|
+
import * as NativeAndroidManager from 'react-native/Libraries/Components/StatusBar/NativeStatusBarManagerAndroid';
|
|
4
|
+
|
|
5
|
+
const getConstants = NativeAndroidManager.default.getConstants;
|
|
6
|
+
|
|
7
|
+
const RCTStatusBarManagerCompat = NativeModules.StatusBarManagerCompat;
|
|
8
|
+
|
|
9
|
+
// On Android < 11 RN uses legacy API which breaks EdgeToEdge mode in RN, so
|
|
10
|
+
// in order to use library on all available platforms we have to monkey patch
|
|
11
|
+
// default RN implementation and use modern `WindowInsetsControllerCompat`.
|
|
12
|
+
if (Platform.OS === 'android') {
|
|
13
|
+
NativeAndroidManager.default = {
|
|
14
|
+
getConstants,
|
|
15
|
+
setColor(color: number, animated: boolean): void {
|
|
16
|
+
RCTStatusBarManagerCompat.setColor(color, animated);
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
setTranslucent(translucent: boolean): void {
|
|
20
|
+
RCTStatusBarManagerCompat.setTranslucent(translucent);
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* - statusBarStyles can be:
|
|
25
|
+
* - 'default'
|
|
26
|
+
* - 'dark-content'
|
|
27
|
+
*/
|
|
28
|
+
setStyle(statusBarStyle?: string): void {
|
|
29
|
+
RCTStatusBarManagerCompat.setStyle(statusBarStyle);
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
setHidden(hidden: boolean): void {
|
|
33
|
+
RCTStatusBarManagerCompat.setHidden(hidden);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
Binary file
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
-
<plist version="1.0">
|
|
4
|
-
<dict>
|
|
5
|
-
<key>SchemeUserState</key>
|
|
6
|
-
<dict>
|
|
7
|
-
<key>KeyboardController.xcscheme_^#shared#^_</key>
|
|
8
|
-
<dict>
|
|
9
|
-
<key>orderHint</key>
|
|
10
|
-
<integer>0</integer>
|
|
11
|
-
</dict>
|
|
12
|
-
</dict>
|
|
13
|
-
</dict>
|
|
14
|
-
</plist>
|