react-native-screens 3.10.0 → 3.11.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.
- package/LICENSE +1 -1
- package/README.md +9 -7
- package/android/build.gradle +1 -2
- package/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +28 -11
- package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +64 -33
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +9 -31
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +3 -32
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +12 -5
- package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +10 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +72 -11
- package/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt +18 -1
- package/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt +7 -2
- package/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt +29 -2
- package/android/src/main/res/anim/rns_default_enter_in.xml +18 -0
- package/android/src/main/res/anim/rns_default_enter_out.xml +19 -0
- package/android/src/main/res/anim/rns_default_exit_in.xml +17 -0
- package/android/src/main/res/anim/rns_default_exit_out.xml +18 -0
- package/android/src/main/res/anim/rns_fade_in.xml +7 -0
- package/android/src/main/res/anim/rns_fade_out.xml +7 -0
- package/android/src/main/res/anim/rns_no_animation_20.xml +6 -0
- package/createNativeStackNavigator/README.md +21 -33
- package/ios/RNSScreen.h +10 -0
- package/ios/RNSScreen.m +34 -0
- package/ios/RNSScreenContainer.m +5 -0
- package/ios/RNSScreenStack.m +22 -7
- package/ios/RNSScreenStackAnimator.m +45 -14
- package/ios/RNSScreenStackHeaderConfig.m +4 -1
- package/ios/RNSScreenWindowTraits.h +1 -0
- package/ios/RNSScreenWindowTraits.m +20 -0
- package/ios/UIViewController+RNScreens.m +10 -0
- package/lib/commonjs/native-stack/views/NativeStackView.js +33 -4
- package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
- package/lib/module/native-stack/views/NativeStackView.js +33 -4
- package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
- package/lib/typescript/native-stack/types.d.ts +34 -0
- package/lib/typescript/reanimated/ReanimatedNativeStackScreen.d.ts +1 -1
- package/lib/typescript/reanimated/ReanimatedScreen.d.ts +1 -1
- package/lib/typescript/types.d.ts +60 -5
- package/native-stack/README.md +39 -3
- package/package.json +1 -1
- package/reanimated/package.json +6 -0
- package/src/native-stack/types.tsx +34 -0
- package/src/native-stack/views/NativeStackView.tsx +33 -4
- package/src/types.tsx +60 -5
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2018
|
|
3
|
+
Copyright (c) 2018 Software Mansion <swmansion.com>
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -123,8 +123,8 @@ You can also disable the usage of native screens per navigator with [`detachInac
|
|
|
123
123
|
To take advantage of the native stack navigator primitive for React Navigation that leverages `UINavigationController` on iOS and `Fragment` on Android, please refer:
|
|
124
124
|
|
|
125
125
|
- for React Navigation >= v6 to the [Native Stack Navigator part of React Navigation documentation](https://reactnavigation.org/docs/native-stack-navigator)
|
|
126
|
-
- for React Navigation v5 to the [README in react-native-screens/native-stack](https://github.com/software-mansion/react-native-screens/tree/
|
|
127
|
-
- for older versions to the [README in react-native-screens/createNativeStackNavigator](https://github.com/software-mansion/react-native-screens/tree/
|
|
126
|
+
- for React Navigation v5 to the [README in react-native-screens/native-stack](https://github.com/software-mansion/react-native-screens/tree/main/native-stack)
|
|
127
|
+
- for older versions to the [README in react-native-screens/createNativeStackNavigator](https://github.com/software-mansion/react-native-screens/tree/main/createNativeStackNavigator)
|
|
128
128
|
|
|
129
129
|
## Interop with [react-native-navigation](https://github.com/wix/react-native-navigation)
|
|
130
130
|
|
|
@@ -132,12 +132,12 @@ React-native-navigation library already uses native containers for rendering nav
|
|
|
132
132
|
|
|
133
133
|
## Interop with other libraries
|
|
134
134
|
|
|
135
|
-
This library should work out of the box with all existing react-native libraries. If you experience problems with interoperability please [report an issue](https://github.com/
|
|
135
|
+
This library should work out of the box with all existing react-native libraries. If you experience problems with interoperability please [report an issue](https://github.com/software-mansion/react-native-screens/issues).
|
|
136
136
|
|
|
137
137
|
## Guide for navigation library authors
|
|
138
138
|
|
|
139
139
|
If you are building a navigation library you may want to use `react-native-screens` to have control over which parts of the React component tree are attached to the native view hierarchy.
|
|
140
|
-
To do that, `react-native-screens` provides you with the components documented [here](https://github.com/
|
|
140
|
+
To do that, `react-native-screens` provides you with the components documented [here](https://github.com/software-mansion/react-native-screens/tree/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md).
|
|
141
141
|
|
|
142
142
|
## Common problems
|
|
143
143
|
|
|
@@ -158,10 +158,11 @@ Use `ScrollView` with prop `contentInsetAdjustmentBehavior=“automatic”` as a
|
|
|
158
158
|
| [SVG component becomes transparent when goBack](https://github.com/software-mansion/react-native-screens/issues/773) | [related PRs](https://github.com/software-mansion/react-native-screens/issues/773#issuecomment-783469792) |
|
|
159
159
|
| [Memory leak while moving from one screen to another in the same stack](https://github.com/software-mansion/react-native-screens/issues/843) | [explanation](https://github.com/software-mansion/react-native-screens/issues/843#issuecomment-832034119) |
|
|
160
160
|
| [LargeHeader stays small after pop/goBack/swipe gesture on iOS 14+](https://github.com/software-mansion/react-native-screens/issues/649) | [potential fix](https://github.com/software-mansion/react-native-screens/issues/649#issuecomment-712199895) |
|
|
161
|
+
| [`onScroll` and `onMomentumScrollEnd` of previous screen triggered in bottom tabs](https://github.com/software-mansion/react-native-screens/issues/1183) | [explanation](https://github.com/software-mansion/react-native-screens/issues/1183#issuecomment-949313111) |
|
|
161
162
|
|
|
162
163
|
## Contributing
|
|
163
164
|
|
|
164
|
-
There are many ways to contribute to this project. See [CONTRIBUTING](https://github.com/
|
|
165
|
+
There are many ways to contribute to this project. See [CONTRIBUTING](https://github.com/software-mansion/react-native-screens/tree/main/guides/CONTRIBUTING.md) guide for more information. Thank you for your interest in contributing!
|
|
165
166
|
|
|
166
167
|
## License
|
|
167
168
|
|
|
@@ -169,7 +170,8 @@ React native screens library is licensed under [The MIT License](LICENSE).
|
|
|
169
170
|
|
|
170
171
|
## Credits
|
|
171
172
|
|
|
172
|
-
This project is
|
|
173
|
+
This project has been build and is maintained thanks to the support from [Shopify](https://shopify.com), [Expo.io](https://expo.io) and [Software Mansion](https://swmansion.com)
|
|
173
174
|
|
|
175
|
+
[](https://shopify.com)
|
|
174
176
|
[](https://expo.io)
|
|
175
|
-
[](https://swmansion.com)
|
package/android/build.gradle
CHANGED
|
@@ -4,7 +4,6 @@ buildscript {
|
|
|
4
4
|
}
|
|
5
5
|
repositories {
|
|
6
6
|
google()
|
|
7
|
-
jcenter()
|
|
8
7
|
mavenCentral()
|
|
9
8
|
}
|
|
10
9
|
dependencies {
|
|
@@ -50,7 +49,6 @@ repositories {
|
|
|
50
49
|
mavenCentral()
|
|
51
50
|
mavenLocal()
|
|
52
51
|
google()
|
|
53
|
-
jcenter()
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
dependencies {
|
|
@@ -60,4 +58,5 @@ dependencies {
|
|
|
60
58
|
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
|
|
61
59
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
|
|
62
60
|
implementation 'com.google.android.material:material:1.1.0'
|
|
61
|
+
implementation "androidx.core:core-ktx:1.5.0"
|
|
63
62
|
}
|
|
@@ -5,7 +5,7 @@ import androidx.activity.OnBackPressedCallback
|
|
|
5
5
|
import androidx.appcompat.widget.SearchView
|
|
6
6
|
import androidx.fragment.app.Fragment
|
|
7
7
|
|
|
8
|
-
class CustomSearchView(context: Context
|
|
8
|
+
class CustomSearchView(context: Context, fragment: Fragment) : SearchView(context) {
|
|
9
9
|
/*
|
|
10
10
|
CustomSearchView uses some variables from SearchView. They are listed below with links to documentation
|
|
11
11
|
isIconified - https://developer.android.com/reference/android/widget/SearchView#setIconified(boolean)
|
|
@@ -29,6 +29,8 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
29
29
|
private var mStatusBarHidden: Boolean? = null
|
|
30
30
|
private var mStatusBarTranslucent: Boolean? = null
|
|
31
31
|
private var mStatusBarColor: Int? = null
|
|
32
|
+
private var mNavigationBarColor: Int? = null
|
|
33
|
+
private var mNavigationBarHidden: Boolean? = null
|
|
32
34
|
var isStatusBarAnimated: Boolean? = null
|
|
33
35
|
private var mNativeBackButtonDismissalEnabled = true
|
|
34
36
|
|
|
@@ -46,16 +48,6 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
46
48
|
layoutParams = WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_APPLICATION)
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
override fun onAnimationStart() {
|
|
50
|
-
super.onAnimationStart()
|
|
51
|
-
fragment?.onViewAnimationStart()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
override fun onAnimationEnd() {
|
|
55
|
-
super.onAnimationEnd()
|
|
56
|
-
fragment?.onViewAnimationEnd()
|
|
57
|
-
}
|
|
58
|
-
|
|
59
51
|
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
|
|
60
52
|
// do nothing, react native will keep the view hierarchy so no need to serialize/deserialize
|
|
61
53
|
// view's states. The side effect of restoring is that TextInput components would trigger
|
|
@@ -209,6 +201,31 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
209
201
|
fragment?.let { ScreenWindowTraits.setColor(this, it.tryGetActivity(), it.tryGetContext()) }
|
|
210
202
|
}
|
|
211
203
|
|
|
204
|
+
var navigationBarColor: Int?
|
|
205
|
+
get() = mNavigationBarColor
|
|
206
|
+
set(navigationBarColor) {
|
|
207
|
+
if (navigationBarColor != null) {
|
|
208
|
+
ScreenWindowTraits.applyDidSetNavigationBarAppearance()
|
|
209
|
+
}
|
|
210
|
+
mNavigationBarColor = navigationBarColor
|
|
211
|
+
fragment?.let { ScreenWindowTraits.setNavigationBarColor(this, it.tryGetActivity()) }
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
var isNavigationBarHidden: Boolean?
|
|
215
|
+
get() = mNavigationBarHidden
|
|
216
|
+
set(navigationBarHidden) {
|
|
217
|
+
if (navigationBarHidden != null) {
|
|
218
|
+
ScreenWindowTraits.applyDidSetNavigationBarAppearance()
|
|
219
|
+
}
|
|
220
|
+
mNavigationBarHidden = navigationBarHidden
|
|
221
|
+
fragment?.let {
|
|
222
|
+
ScreenWindowTraits.setNavigationBarHidden(
|
|
223
|
+
this,
|
|
224
|
+
it.tryGetActivity(),
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
212
229
|
var nativeBackButtonDismissalEnabled: Boolean
|
|
213
230
|
get() = mNativeBackButtonDismissalEnabled
|
|
214
231
|
set(enableNativeBackButtonDismissal) {
|
|
@@ -232,6 +249,6 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
232
249
|
}
|
|
233
250
|
|
|
234
251
|
enum class WindowTraits {
|
|
235
|
-
ORIENTATION, COLOR, STYLE, TRANSLUCENT, HIDDEN, ANIMATED
|
|
252
|
+
ORIENTATION, COLOR, STYLE, TRANSLUCENT, HIDDEN, ANIMATED, NAVIGATION_BAR_COLOR, NAVIGATION_BAR_HIDDEN
|
|
236
253
|
}
|
|
237
254
|
}
|
|
@@ -166,7 +166,7 @@ open class ScreenContainer<T : ScreenFragment>(context: Context?) : ViewGroup(co
|
|
|
166
166
|
while (context !is FragmentActivity && context is ContextWrapper) {
|
|
167
167
|
context = context.baseContext
|
|
168
168
|
}
|
|
169
|
-
check(context is FragmentActivity) { "In order to use RNScreens components your app's activity need to extend
|
|
169
|
+
check(context is FragmentActivity) { "In order to use RNScreens components your app's activity need to extend ReactActivity" }
|
|
170
170
|
setFragmentManager(context.supportFragmentManager)
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -38,6 +38,17 @@ open class ScreenFragment : Fragment {
|
|
|
38
38
|
// due to progress value being already 0.0f
|
|
39
39
|
private var mProgress = -1f
|
|
40
40
|
|
|
41
|
+
// those 2 vars are needed since sometimes the events would be dispatched twice in child containers
|
|
42
|
+
// (should only happen if parent has `NONE` animation) and we don't need too complicated logic.
|
|
43
|
+
// We just check if, after the event was dispatched, its "counter-event" has been also dispatched before sending the same event again.
|
|
44
|
+
// We do it for 'willAppear' -> 'willDisappear' and 'appear' -> 'disappear'
|
|
45
|
+
private var canDispatchWillAppear = true
|
|
46
|
+
private var canDispatchAppear = true
|
|
47
|
+
|
|
48
|
+
// we want to know if we are currently transitioning in order not to fire lifecycle events
|
|
49
|
+
// in nested fragments. See more explanation in dispatchViewAnimationEvent
|
|
50
|
+
private var isTransitioning = false
|
|
51
|
+
|
|
41
52
|
constructor() {
|
|
42
53
|
throw IllegalStateException(
|
|
43
54
|
"Screen fragments should never be restored. Follow instructions from https://github.com/software-mansion/react-native-screens/issues/17#issuecomment-424704067 to properly configure your main activity."
|
|
@@ -141,33 +152,52 @@ open class ScreenFragment : Fragment {
|
|
|
141
152
|
val childScreenContainers: List<ScreenContainer<*>>
|
|
142
153
|
get() = mChildScreenContainers
|
|
143
154
|
|
|
144
|
-
fun
|
|
155
|
+
private fun canDispatchEvent(event: ScreenLifecycleEvent): Boolean {
|
|
156
|
+
return when (event) {
|
|
157
|
+
ScreenLifecycleEvent.WillAppear -> canDispatchWillAppear
|
|
158
|
+
ScreenLifecycleEvent.Appear -> canDispatchAppear
|
|
159
|
+
ScreenLifecycleEvent.WillDisappear -> !canDispatchWillAppear
|
|
160
|
+
ScreenLifecycleEvent.Disappear -> !canDispatchAppear
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private fun setLastEventDispatched(event: ScreenLifecycleEvent) {
|
|
165
|
+
when (event) {
|
|
166
|
+
ScreenLifecycleEvent.WillAppear -> canDispatchWillAppear = false
|
|
167
|
+
ScreenLifecycleEvent.Appear -> canDispatchAppear = false
|
|
168
|
+
ScreenLifecycleEvent.WillDisappear -> canDispatchWillAppear = true
|
|
169
|
+
ScreenLifecycleEvent.Disappear -> canDispatchAppear = true
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private fun dispatchOnWillAppear() {
|
|
145
174
|
dispatchEvent(ScreenLifecycleEvent.WillAppear, this)
|
|
146
175
|
|
|
147
176
|
dispatchTransitionProgress(0.0f, false)
|
|
148
177
|
}
|
|
149
178
|
|
|
150
|
-
fun dispatchOnAppear() {
|
|
179
|
+
private fun dispatchOnAppear() {
|
|
151
180
|
dispatchEvent(ScreenLifecycleEvent.Appear, this)
|
|
152
181
|
|
|
153
182
|
dispatchTransitionProgress(1.0f, false)
|
|
154
183
|
}
|
|
155
184
|
|
|
156
|
-
|
|
185
|
+
private fun dispatchOnWillDisappear() {
|
|
157
186
|
dispatchEvent(ScreenLifecycleEvent.WillDisappear, this)
|
|
158
187
|
|
|
159
188
|
dispatchTransitionProgress(0.0f, true)
|
|
160
189
|
}
|
|
161
190
|
|
|
162
|
-
|
|
191
|
+
private fun dispatchOnDisappear() {
|
|
163
192
|
dispatchEvent(ScreenLifecycleEvent.Disappear, this)
|
|
164
193
|
|
|
165
194
|
dispatchTransitionProgress(1.0f, true)
|
|
166
195
|
}
|
|
167
196
|
|
|
168
197
|
private fun dispatchEvent(event: ScreenLifecycleEvent, fragment: ScreenFragment) {
|
|
169
|
-
if (fragment is ScreenStackFragment) {
|
|
198
|
+
if (fragment is ScreenStackFragment && fragment.canDispatchEvent(event)) {
|
|
170
199
|
fragment.screen.let {
|
|
200
|
+
fragment.setLastEventDispatched(event)
|
|
171
201
|
val lifecycleEvent: Event<*> = when (event) {
|
|
172
202
|
ScreenLifecycleEvent.WillAppear -> ScreenWillAppearEvent(it.id)
|
|
173
203
|
ScreenLifecycleEvent.Appear -> ScreenAppearEvent(it.id)
|
|
@@ -187,12 +217,7 @@ open class ScreenFragment : Fragment {
|
|
|
187
217
|
for (sc in mChildScreenContainers) {
|
|
188
218
|
if (sc.screenCount > 0) {
|
|
189
219
|
sc.topScreen?.let {
|
|
190
|
-
|
|
191
|
-
// we do not dispatch events in child when it has `none` animation
|
|
192
|
-
// and we are going forward since then they will be dispatched in child via
|
|
193
|
-
// `onCreateAnimation` of ScreenStackFragment
|
|
194
|
-
sc.topScreen?.fragment?.let { fragment -> dispatchEvent(event, fragment) }
|
|
195
|
-
}
|
|
220
|
+
sc.topScreen?.fragment?.let { fragment -> dispatchEvent(event, fragment) }
|
|
196
221
|
}
|
|
197
222
|
}
|
|
198
223
|
}
|
|
@@ -238,31 +263,37 @@ open class ScreenFragment : Fragment {
|
|
|
238
263
|
}
|
|
239
264
|
|
|
240
265
|
fun onViewAnimationStart() {
|
|
241
|
-
|
|
242
|
-
// view. We override Screen#onAnimationStart and an appropriate method of the StackFragment's
|
|
243
|
-
// root view in order to achieve this.
|
|
244
|
-
if (isResumed) {
|
|
245
|
-
// Android dispatches the animation start event for the fragment that is being added first
|
|
246
|
-
// however we want the one being dismissed first to match iOS. It also makes more sense from
|
|
247
|
-
// a navigation point of view to have the disappear event first.
|
|
248
|
-
// Since there are no explicit relationships between the fragment being added / removed the
|
|
249
|
-
// practical way to fix this is delaying dispatching the appear events at the end of the
|
|
250
|
-
// frame.
|
|
251
|
-
UiThreadUtil.runOnUiThread { dispatchOnWillAppear() }
|
|
252
|
-
} else {
|
|
253
|
-
dispatchOnWillDisappear()
|
|
254
|
-
}
|
|
266
|
+
dispatchViewAnimationEvent(false)
|
|
255
267
|
}
|
|
256
268
|
|
|
257
269
|
open fun onViewAnimationEnd() {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
270
|
+
dispatchViewAnimationEvent(true)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private fun dispatchViewAnimationEvent(animationEnd: Boolean) {
|
|
274
|
+
isTransitioning = !animationEnd
|
|
275
|
+
// if parent fragment is transitioning, we do not want the events dispatched from the child,
|
|
276
|
+
// since we subscribe to parent's animation start/end and dispatch events in child from there
|
|
277
|
+
// check for `isTransitioning` should be enough since the child's animation should take only
|
|
278
|
+
// 20ms due to always being `StackAnimation.NONE` when nested stack being pushed
|
|
279
|
+
val parent = parentFragment
|
|
280
|
+
if (parent == null || (parent is ScreenFragment && !parent.isTransitioning)) {
|
|
281
|
+
// onViewAnimationStart/End is triggered from View#onAnimationStart/End method of the fragment's root
|
|
282
|
+
// view. We override an appropriate method of the StackFragment's
|
|
283
|
+
// root view in order to achieve this.
|
|
284
|
+
if (isResumed) {
|
|
285
|
+
// Android dispatches the animation start event for the fragment that is being added first
|
|
286
|
+
// however we want the one being dismissed first to match iOS. It also makes more sense from
|
|
287
|
+
// a navigation point of view to have the disappear event first.
|
|
288
|
+
// Since there are no explicit relationships between the fragment being added / removed the
|
|
289
|
+
// practical way to fix this is delaying dispatching the appear events at the end of the
|
|
290
|
+
// frame.
|
|
291
|
+
UiThreadUtil.runOnUiThread {
|
|
292
|
+
if (animationEnd) dispatchOnAppear() else dispatchOnWillAppear()
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
if (animationEnd) dispatchOnDisappear() else dispatchOnWillDisappear()
|
|
296
|
+
}
|
|
266
297
|
}
|
|
267
298
|
}
|
|
268
299
|
|
|
@@ -3,7 +3,6 @@ package com.swmansion.rnscreens
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.graphics.Canvas
|
|
5
5
|
import android.view.View
|
|
6
|
-
import androidx.fragment.app.FragmentTransaction
|
|
7
6
|
import com.facebook.react.bridge.ReactContext
|
|
8
7
|
import com.facebook.react.uimanager.UIManagerModule
|
|
9
8
|
import com.swmansion.rnscreens.Screen.StackAnimation
|
|
@@ -110,7 +109,6 @@ class ScreenStack(context: Context?) : ScreenContainer<ScreenStackFragment>(cont
|
|
|
110
109
|
}
|
|
111
110
|
}
|
|
112
111
|
var shouldUseOpenAnimation = true
|
|
113
|
-
var transition = FragmentTransaction.TRANSIT_FRAGMENT_OPEN
|
|
114
112
|
var stackAnimation: StackAnimation? = null
|
|
115
113
|
if (!mStack.contains(newTop)) {
|
|
116
114
|
// if new top screen wasn't on stack we do "open animation" so long it is not the very first
|
|
@@ -127,17 +125,9 @@ class ScreenStack(context: Context?) : ScreenContainer<ScreenStackFragment>(cont
|
|
|
127
125
|
stackAnimation = if (shouldUseOpenAnimation) newTop.screen.stackAnimation else mTopScreen?.screen?.stackAnimation
|
|
128
126
|
} else if (mTopScreen == null && newTop != null) {
|
|
129
127
|
// mTopScreen was not present before so newTop is the first screen added to a stack
|
|
130
|
-
// and we don't want the animation when it is entering
|
|
131
|
-
// willAppear and Appear events to the user, which won't be sent by default if Screen's
|
|
132
|
-
// stack animation is not NONE (see check for stackAnimation in onCreateAnimation in
|
|
133
|
-
// ScreenStackFragment).
|
|
134
|
-
// We don't do it if the stack is nested since the parent will trigger these events in child
|
|
128
|
+
// and we don't want the animation when it is entering
|
|
135
129
|
stackAnimation = StackAnimation.NONE
|
|
136
|
-
|
|
137
|
-
goingForward = true
|
|
138
|
-
newTop.dispatchOnWillAppear()
|
|
139
|
-
newTop.dispatchOnAppear()
|
|
140
|
-
}
|
|
130
|
+
goingForward = true
|
|
141
131
|
}
|
|
142
132
|
} else if (mTopScreen != null && mTopScreen != newTop) {
|
|
143
133
|
// otherwise if we are performing top screen change we do "close animation"
|
|
@@ -149,40 +139,32 @@ class ScreenStack(context: Context?) : ScreenContainer<ScreenStackFragment>(cont
|
|
|
149
139
|
// animation logic start
|
|
150
140
|
if (stackAnimation != null) {
|
|
151
141
|
if (shouldUseOpenAnimation) {
|
|
152
|
-
transition = FragmentTransaction.TRANSIT_FRAGMENT_OPEN
|
|
153
142
|
when (stackAnimation) {
|
|
143
|
+
StackAnimation.DEFAULT -> it.setCustomAnimations(R.anim.rns_default_enter_in, R.anim.rns_default_enter_out)
|
|
144
|
+
StackAnimation.NONE -> it.setCustomAnimations(R.anim.rns_no_animation_20, R.anim.rns_no_animation_20)
|
|
145
|
+
StackAnimation.FADE -> it.setCustomAnimations(R.anim.rns_fade_in, R.anim.rns_fade_out)
|
|
154
146
|
StackAnimation.SLIDE_FROM_RIGHT -> it.setCustomAnimations(R.anim.rns_slide_in_from_right, R.anim.rns_slide_out_to_left)
|
|
155
147
|
StackAnimation.SLIDE_FROM_LEFT -> it.setCustomAnimations(R.anim.rns_slide_in_from_left, R.anim.rns_slide_out_to_right)
|
|
156
148
|
StackAnimation.SLIDE_FROM_BOTTOM -> it.setCustomAnimations(
|
|
157
149
|
R.anim.rns_slide_in_from_bottom, R.anim.rns_no_animation_medium
|
|
158
150
|
)
|
|
159
151
|
StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_fade_from_bottom, R.anim.rns_no_animation_350)
|
|
160
|
-
else -> {
|
|
161
|
-
}
|
|
162
152
|
}
|
|
163
153
|
} else {
|
|
164
|
-
transition = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE
|
|
165
154
|
when (stackAnimation) {
|
|
155
|
+
StackAnimation.DEFAULT -> it.setCustomAnimations(R.anim.rns_default_exit_in, R.anim.rns_default_exit_out)
|
|
156
|
+
StackAnimation.NONE -> it.setCustomAnimations(R.anim.rns_no_animation_20, R.anim.rns_no_animation_20)
|
|
157
|
+
StackAnimation.FADE -> it.setCustomAnimations(R.anim.rns_fade_in, R.anim.rns_fade_out)
|
|
166
158
|
StackAnimation.SLIDE_FROM_RIGHT -> it.setCustomAnimations(R.anim.rns_slide_in_from_left, R.anim.rns_slide_out_to_right)
|
|
167
159
|
StackAnimation.SLIDE_FROM_LEFT -> it.setCustomAnimations(R.anim.rns_slide_in_from_right, R.anim.rns_slide_out_to_left)
|
|
168
160
|
StackAnimation.SLIDE_FROM_BOTTOM -> it.setCustomAnimations(
|
|
169
161
|
R.anim.rns_no_animation_medium, R.anim.rns_slide_out_to_bottom
|
|
170
162
|
)
|
|
171
163
|
StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_no_animation_250, R.anim.rns_fade_to_bottom)
|
|
172
|
-
else -> {
|
|
173
|
-
}
|
|
174
164
|
}
|
|
175
165
|
}
|
|
176
166
|
}
|
|
177
|
-
|
|
178
|
-
transition = FragmentTransaction.TRANSIT_NONE
|
|
179
|
-
}
|
|
180
|
-
if (stackAnimation === StackAnimation.FADE) {
|
|
181
|
-
transition = FragmentTransaction.TRANSIT_FRAGMENT_FADE
|
|
182
|
-
}
|
|
183
|
-
if (stackAnimation != null && isSystemAnimation(stackAnimation)) {
|
|
184
|
-
it.setTransition(transition)
|
|
185
|
-
}
|
|
167
|
+
|
|
186
168
|
// animation logic end
|
|
187
169
|
goingForward = shouldUseOpenAnimation
|
|
188
170
|
|
|
@@ -348,10 +330,6 @@ class ScreenStack(context: Context?) : ScreenContainer<ScreenStackFragment>(cont
|
|
|
348
330
|
}
|
|
349
331
|
|
|
350
332
|
companion object {
|
|
351
|
-
private fun isSystemAnimation(stackAnimation: StackAnimation): Boolean {
|
|
352
|
-
return stackAnimation === StackAnimation.DEFAULT || stackAnimation === StackAnimation.FADE || stackAnimation === StackAnimation.NONE
|
|
353
|
-
}
|
|
354
|
-
|
|
355
333
|
private fun isTransparent(fragment: ScreenStackFragment): Boolean {
|
|
356
334
|
return (
|
|
357
335
|
fragment.screen.stackPresentation
|
|
@@ -16,7 +16,6 @@ import android.view.animation.Transformation
|
|
|
16
16
|
import android.widget.LinearLayout
|
|
17
17
|
import androidx.appcompat.widget.Toolbar
|
|
18
18
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
19
|
-
import com.facebook.react.bridge.UiThreadUtil
|
|
20
19
|
import com.facebook.react.uimanager.PixelUtil
|
|
21
20
|
import com.google.android.material.appbar.AppBarLayout
|
|
22
21
|
import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior
|
|
@@ -86,35 +85,6 @@ class ScreenStackFragment : ScreenFragment {
|
|
|
86
85
|
notifyViewAppearTransitionEnd()
|
|
87
86
|
}
|
|
88
87
|
|
|
89
|
-
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
|
|
90
|
-
// this means that the fragment will appear with a custom transition, in the case
|
|
91
|
-
// of animation: 'none', onViewAnimationStart and onViewAnimationEnd
|
|
92
|
-
// won't be called and we need to notify stack directly from here.
|
|
93
|
-
// When using the Toolbar back button this is called an extra time with transit = 0 but in
|
|
94
|
-
// this case we don't want to notify. The way I found to detect is case is check isHidden.
|
|
95
|
-
if (transit == 0 && !isHidden &&
|
|
96
|
-
screen.stackAnimation === Screen.StackAnimation.NONE
|
|
97
|
-
) {
|
|
98
|
-
if (enter) {
|
|
99
|
-
// Android dispatches the animation start event for the fragment that is being added first
|
|
100
|
-
// however we want the one being dismissed first to match iOS. It also makes more sense
|
|
101
|
-
// from a navigation point of view to have the disappear event first.
|
|
102
|
-
// Since there are no explicit relationships between the fragment being added / removed
|
|
103
|
-
// the practical way to fix this is delaying dispatching the appear events at the end of
|
|
104
|
-
// the frame.
|
|
105
|
-
UiThreadUtil.runOnUiThread {
|
|
106
|
-
dispatchOnWillAppear()
|
|
107
|
-
dispatchOnAppear()
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
dispatchOnWillDisappear()
|
|
111
|
-
dispatchOnDisappear()
|
|
112
|
-
notifyViewAppearTransitionEnd()
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return null
|
|
116
|
-
}
|
|
117
|
-
|
|
118
88
|
private fun notifyViewAppearTransitionEnd() {
|
|
119
89
|
val screenStack = view?.parent
|
|
120
90
|
if (screenStack is ScreenStack) {
|
|
@@ -181,8 +151,9 @@ class ScreenStackFragment : ScreenFragment {
|
|
|
181
151
|
private fun updateToolbarMenu(menu: Menu) {
|
|
182
152
|
menu.clear()
|
|
183
153
|
if (shouldShowSearchBar()) {
|
|
184
|
-
|
|
185
|
-
|
|
154
|
+
val currentContext = context
|
|
155
|
+
if (searchView == null && currentContext != null) {
|
|
156
|
+
val newSearchView = CustomSearchView(currentContext, this)
|
|
186
157
|
searchView = newSearchView
|
|
187
158
|
onSearchViewCreate?.invoke(newSearchView)
|
|
188
159
|
}
|
|
@@ -24,6 +24,7 @@ import com.facebook.react.views.text.ReactTypefaceUtils
|
|
|
24
24
|
class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) {
|
|
25
25
|
private val mConfigSubviews = ArrayList<ScreenStackHeaderSubview>(3)
|
|
26
26
|
val toolbar: CustomToolbar
|
|
27
|
+
private var headerTopInset: Int? = null
|
|
27
28
|
private var mTitle: String? = null
|
|
28
29
|
private var mTitleColor = 0
|
|
29
30
|
private var mTitleFontFamily: String? = null
|
|
@@ -81,6 +82,15 @@ class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) {
|
|
|
81
82
|
super.onAttachedToWindow()
|
|
82
83
|
mIsAttachedToWindow = true
|
|
83
84
|
sendEvent("onAttached", null)
|
|
85
|
+
// we want to save the top inset before the status bar can be hidden, which would resolve in
|
|
86
|
+
// inset being 0
|
|
87
|
+
if (headerTopInset == null) {
|
|
88
|
+
headerTopInset = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
|
89
|
+
rootWindowInsets.systemWindowInsetTop
|
|
90
|
+
else
|
|
91
|
+
// Hacky fallback for old android. Before Marshmallow, the status bar height was always 25
|
|
92
|
+
(25 * resources.displayMetrics.density).toInt()
|
|
93
|
+
}
|
|
84
94
|
onUpdate()
|
|
85
95
|
}
|
|
86
96
|
|
|
@@ -159,11 +169,8 @@ class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) {
|
|
|
159
169
|
screenFragment?.setToolbar(toolbar)
|
|
160
170
|
}
|
|
161
171
|
if (mIsTopInsetEnabled) {
|
|
162
|
-
|
|
163
|
-
toolbar.setPadding(0,
|
|
164
|
-
} else {
|
|
165
|
-
// Hacky fallback for old android. Before Marshmallow, the status bar height was always 25
|
|
166
|
-
toolbar.setPadding(0, (25 * resources.displayMetrics.density).toInt(), 0, 0)
|
|
172
|
+
headerTopInset.let {
|
|
173
|
+
toolbar.setPadding(0, it ?: 0, 0, 0)
|
|
167
174
|
}
|
|
168
175
|
} else {
|
|
169
176
|
if (toolbar.paddingTop > 0) {
|
|
@@ -112,6 +112,16 @@ class ScreenViewManager : ViewGroupManager<Screen>() {
|
|
|
112
112
|
view.isStatusBarHidden = statusBarHidden
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
@ReactProp(name = "navigationBarColor", customType = "Color")
|
|
116
|
+
fun setNavigationBarColor(view: Screen, navigationBarColor: Int) {
|
|
117
|
+
view.navigationBarColor = navigationBarColor
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@ReactProp(name = "navigationBarHidden")
|
|
121
|
+
fun setNavigationBarHidden(view: Screen, navigationBarHidden: Boolean?) {
|
|
122
|
+
view.isNavigationBarHidden = navigationBarHidden
|
|
123
|
+
}
|
|
124
|
+
|
|
115
125
|
@ReactProp(name = "nativeBackButtonDismissalEnabled")
|
|
116
126
|
fun setNativeBackButtonDismissalEnabled(
|
|
117
127
|
view: Screen,
|