react-native-screens 3.35.0-rc.0 → 4.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/android/build.gradle +2 -2
- package/android/src/main/java/com/swmansion/rnscreens/InsetsObserverProxy.kt +67 -0
- package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +2 -0
- package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +101 -4
- package/android/src/main/java/com/swmansion/rnscreens/ScreenContentWrapper.kt +38 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenContentWrapperManager.kt +25 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenFooter.kt +287 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenFooterManager.kt +25 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +11 -19
- package/android/src/main/java/com/swmansion/rnscreens/ScreenFragmentWrapper.kt +4 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenModalFragment.kt +281 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +62 -19
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +403 -41
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragmentWrapper.kt +4 -1
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +2 -2
- package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +95 -11
- package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +39 -28
- package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/BottomSheetDialogRootView.kt +104 -0
- package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/BottomSheetDialogScreen.kt +26 -0
- package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/DimmingFragment.kt +488 -0
- package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/DimmingView.kt +66 -0
- package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/GestureTransparentViewGroup.kt +24 -0
- package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/SheetUtils.kt +127 -0
- package/android/src/main/java/com/swmansion/rnscreens/events/SheetDetentChangedEvent.kt +27 -0
- package/android/src/main/java/com/swmansion/rnscreens/ext/NumericExt.kt +12 -0
- package/android/src/main/java/com/swmansion/rnscreens/ext/ViewExt.kt +32 -0
- package/android/src/main/java/com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper.kt +45 -8
- package/android/src/main/res/base/drawable/rns_rounder_top_corners_shape.xml +8 -0
- package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenContentWrapperManagerDelegate.java +25 -0
- package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenContentWrapperManagerInterface.java +16 -0
- package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenFooterManagerDelegate.java +25 -0
- package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenFooterManagerInterface.java +16 -0
- package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerDelegate.java +9 -2
- package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerInterface.java +5 -2
- package/ios/RNSConvert.h +5 -3
- package/ios/RNSConvert.mm +14 -20
- package/ios/RNSScreen.h +3 -2
- package/ios/RNSScreen.mm +433 -49
- package/ios/RNSScreenContentWrapper.h +44 -0
- package/ios/RNSScreenContentWrapper.mm +61 -0
- package/ios/RNSScreenFooter.h +30 -0
- package/ios/RNSScreenFooter.mm +137 -0
- package/lib/commonjs/components/Screen.js +6 -2
- package/lib/commonjs/components/Screen.js.map +1 -1
- package/lib/commonjs/components/ScreenContentWrapper.js +19 -0
- package/lib/commonjs/components/ScreenContentWrapper.js.map +1 -0
- package/lib/commonjs/components/ScreenFooter.js +23 -0
- package/lib/commonjs/components/ScreenFooter.js.map +1 -0
- package/lib/commonjs/fabric/ModalScreenNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/ScreenContentWrapperNativeComponent.js +10 -0
- package/lib/commonjs/fabric/ScreenContentWrapperNativeComponent.js.map +1 -0
- package/lib/commonjs/fabric/ScreenFooterNativeComponent.js +10 -0
- package/lib/commonjs/fabric/ScreenFooterNativeComponent.js.map +1 -0
- package/lib/commonjs/fabric/ScreenNativeComponent.js.map +1 -1
- package/lib/commonjs/index.js +30 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native-stack/views/FooterComponent.js +18 -0
- package/lib/commonjs/native-stack/views/FooterComponent.js.map +1 -0
- package/lib/commonjs/native-stack/views/NativeStackView.js +59 -14
- package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
- package/lib/module/components/Screen.js +6 -2
- package/lib/module/components/Screen.js.map +1 -1
- package/lib/module/components/ScreenContentWrapper.js +12 -0
- package/lib/module/components/ScreenContentWrapper.js.map +1 -0
- package/lib/module/components/ScreenFooter.js +17 -0
- package/lib/module/components/ScreenFooter.js.map +1 -0
- package/lib/module/fabric/ModalScreenNativeComponent.js.map +1 -1
- package/lib/module/fabric/ScreenContentWrapperNativeComponent.js +3 -0
- package/lib/module/fabric/ScreenContentWrapperNativeComponent.js.map +1 -0
- package/lib/module/fabric/ScreenFooterNativeComponent.js +3 -0
- package/lib/module/fabric/ScreenFooterNativeComponent.js.map +1 -0
- package/lib/module/fabric/ScreenNativeComponent.js.map +1 -1
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/native-stack/views/FooterComponent.js +11 -0
- package/lib/module/native-stack/views/FooterComponent.js.map +1 -0
- package/lib/module/native-stack/views/NativeStackView.js +61 -15
- package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
- package/lib/typescript/components/Screen.d.ts.map +1 -1
- package/lib/typescript/components/ScreenContentWrapper.d.ts +6 -0
- package/lib/typescript/components/ScreenContentWrapper.d.ts.map +1 -0
- package/lib/typescript/components/ScreenFooter.d.ts +12 -0
- package/lib/typescript/components/ScreenFooter.d.ts.map +1 -0
- package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts +2 -3
- package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/ScreenContentWrapperNativeComponent.d.ts +7 -0
- package/lib/typescript/fabric/ScreenContentWrapperNativeComponent.d.ts.map +1 -0
- package/lib/typescript/fabric/ScreenFooterNativeComponent.d.ts +7 -0
- package/lib/typescript/fabric/ScreenFooterNativeComponent.d.ts.map +1 -0
- package/lib/typescript/fabric/ScreenNativeComponent.d.ts +9 -3
- package/lib/typescript/fabric/ScreenNativeComponent.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/native-stack/types.d.ts +63 -23
- package/lib/typescript/native-stack/types.d.ts.map +1 -1
- package/lib/typescript/native-stack/views/FooterComponent.d.ts +7 -0
- package/lib/typescript/native-stack/views/FooterComponent.d.ts.map +1 -0
- package/lib/typescript/native-stack/views/NativeStackView.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +42 -17
- package/lib/typescript/types.d.ts.map +1 -1
- package/native-stack/README.md +16 -14
- package/package.json +1 -1
- package/react-native.config.js +18 -16
- package/src/components/Screen.tsx +6 -2
- package/src/components/ScreenContentWrapper.tsx +12 -0
- package/src/components/ScreenFooter.tsx +18 -0
- package/src/fabric/ModalScreenNativeComponent.ts +2 -4
- package/src/fabric/ScreenContentWrapperNativeComponent.ts +9 -0
- package/src/fabric/ScreenFooterNativeComponent.ts +6 -0
- package/src/fabric/ScreenNativeComponent.ts +10 -4
- package/src/index.tsx +10 -0
- package/src/native-stack/types.tsx +57 -23
- package/src/native-stack/views/FooterComponent.tsx +10 -0
- package/src/native-stack/views/NativeStackView.tsx +74 -11
- package/src/types.tsx +41 -16
|
@@ -15,6 +15,7 @@ import com.facebook.react.bridge.UiThreadUtil
|
|
|
15
15
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
16
16
|
import com.facebook.react.uimanager.events.Event
|
|
17
17
|
import com.facebook.react.uimanager.events.EventDispatcher
|
|
18
|
+
import com.swmansion.rnscreens.bottomsheet.DimmingFragment
|
|
18
19
|
import com.swmansion.rnscreens.events.HeaderBackButtonClickedEvent
|
|
19
20
|
import com.swmansion.rnscreens.events.ScreenAppearEvent
|
|
20
21
|
import com.swmansion.rnscreens.events.ScreenDisappearEvent
|
|
@@ -22,6 +23,7 @@ import com.swmansion.rnscreens.events.ScreenDismissedEvent
|
|
|
22
23
|
import com.swmansion.rnscreens.events.ScreenTransitionProgressEvent
|
|
23
24
|
import com.swmansion.rnscreens.events.ScreenWillAppearEvent
|
|
24
25
|
import com.swmansion.rnscreens.events.ScreenWillDisappearEvent
|
|
26
|
+
import com.swmansion.rnscreens.ext.recycle
|
|
25
27
|
import kotlin.math.max
|
|
26
28
|
import kotlin.math.min
|
|
27
29
|
|
|
@@ -92,7 +94,7 @@ open class ScreenFragment :
|
|
|
92
94
|
)
|
|
93
95
|
val wrapper =
|
|
94
96
|
context?.let { ScreensFrameLayout(it) }?.apply {
|
|
95
|
-
addView(
|
|
97
|
+
addView(screen.recycle())
|
|
96
98
|
}
|
|
97
99
|
return wrapper
|
|
98
100
|
}
|
|
@@ -288,7 +290,12 @@ open class ScreenFragment :
|
|
|
288
290
|
// since we subscribe to parent's animation start/end and dispatch events in child from there
|
|
289
291
|
// check for `isTransitioning` should be enough since the child's animation should take only
|
|
290
292
|
// 20ms due to always being `StackAnimation.NONE` when nested stack being pushed
|
|
291
|
-
val parent =
|
|
293
|
+
val parent =
|
|
294
|
+
if (parentFragment is DimmingFragment) {
|
|
295
|
+
parentFragment?.parentFragment
|
|
296
|
+
} else {
|
|
297
|
+
parentFragment
|
|
298
|
+
}
|
|
292
299
|
if (parent == null || (parent is ScreenFragment && !parent.isTransitioning)) {
|
|
293
300
|
// onViewAnimationStart/End is triggered from View#onAnimationStart/End method of the fragment's root
|
|
294
301
|
// view. We override an appropriate method of the StackFragment's
|
|
@@ -312,7 +319,7 @@ open class ScreenFragment :
|
|
|
312
319
|
override fun onDestroy() {
|
|
313
320
|
super.onDestroy()
|
|
314
321
|
val container = screen.container
|
|
315
|
-
if (container == null || !container.hasScreen(this)) {
|
|
322
|
+
if (container == null || !container.hasScreen(this.screen.fragmentWrapper)) {
|
|
316
323
|
// we only send dismissed even when the screen has been removed from its container
|
|
317
324
|
val screenContext = screen.context
|
|
318
325
|
if (screenContext is ReactContext) {
|
|
@@ -326,22 +333,7 @@ open class ScreenFragment :
|
|
|
326
333
|
}
|
|
327
334
|
|
|
328
335
|
companion object {
|
|
329
|
-
|
|
330
|
-
protected fun recycleView(view: View): View {
|
|
331
|
-
// screen fragments reuse view instances instead of creating new ones. In order to reuse a given
|
|
332
|
-
// view it needs to be detached from the view hierarchy to allow the fragment to attach it back.
|
|
333
|
-
val parent = view.parent
|
|
334
|
-
if (parent != null) {
|
|
335
|
-
(parent as ViewGroup).endViewTransition(view)
|
|
336
|
-
parent.removeView(view)
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// view detached from fragment manager get their visibility changed to GONE after their state is
|
|
340
|
-
// dumped. Since we don't restore the state but want to reuse the view we need to change
|
|
341
|
-
// visibility back to VISIBLE in order for the fragment manager to animate in the view.
|
|
342
|
-
view.visibility = View.VISIBLE
|
|
343
|
-
return view
|
|
344
|
-
}
|
|
336
|
+
const val TAG = "ScreenFragment"
|
|
345
337
|
|
|
346
338
|
fun getCoalescingKey(progress: Float): Short {
|
|
347
339
|
/* We want value of 0 and 1 to be always dispatched so we base coalescing key on the progress:
|
|
@@ -15,6 +15,10 @@ interface ScreenFragmentWrapper :
|
|
|
15
15
|
|
|
16
16
|
fun removeChildScreenContainer(container: ScreenContainer)
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Container that this fragment belongs to calls it to notify the fragment,
|
|
20
|
+
* that the container has updated.
|
|
21
|
+
*/
|
|
18
22
|
fun onContainerUpdate()
|
|
19
23
|
|
|
20
24
|
// Animation phase callbacks
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
package com.swmansion.rnscreens
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.app.Dialog
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.os.Build
|
|
7
|
+
import android.os.Bundle
|
|
8
|
+
import android.view.LayoutInflater
|
|
9
|
+
import android.view.View
|
|
10
|
+
import android.view.ViewGroup
|
|
11
|
+
import android.view.ViewParent
|
|
12
|
+
import android.view.WindowManager
|
|
13
|
+
import androidx.appcompat.widget.Toolbar
|
|
14
|
+
import androidx.fragment.app.Fragment
|
|
15
|
+
import com.facebook.react.bridge.ReactContext
|
|
16
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
17
|
+
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
18
|
+
import com.google.android.material.bottomsheet.BottomSheetDialog
|
|
19
|
+
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
|
20
|
+
import com.swmansion.rnscreens.bottomsheet.BottomSheetDialogRootView
|
|
21
|
+
import com.swmansion.rnscreens.bottomsheet.BottomSheetDialogScreen
|
|
22
|
+
import com.swmansion.rnscreens.bottomsheet.SheetUtils
|
|
23
|
+
import com.swmansion.rnscreens.events.ScreenDismissedEvent
|
|
24
|
+
import com.swmansion.rnscreens.ext.parentAsView
|
|
25
|
+
import com.swmansion.rnscreens.ext.recycle
|
|
26
|
+
|
|
27
|
+
class ScreenModalFragment :
|
|
28
|
+
BottomSheetDialogFragment,
|
|
29
|
+
ScreenStackFragmentWrapper {
|
|
30
|
+
override lateinit var screen: Screen
|
|
31
|
+
|
|
32
|
+
// Nested containers
|
|
33
|
+
override val childScreenContainers = ArrayList<ScreenContainer>()
|
|
34
|
+
|
|
35
|
+
private val container: ScreenStack?
|
|
36
|
+
get() = screen.container as? ScreenStack
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Dialog instance. Note that we are responsible for creating the dialog.
|
|
40
|
+
* This member is valid after `onCreateDialog` method runs.
|
|
41
|
+
*/
|
|
42
|
+
private lateinit var sheetDialog: BottomSheetDialog
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Behaviour attached to bottom sheet dialog.
|
|
46
|
+
* This member is valid after `onCreateDialog` method runs.
|
|
47
|
+
*/
|
|
48
|
+
private val behavior
|
|
49
|
+
get() = sheetDialog.behavior
|
|
50
|
+
|
|
51
|
+
override val fragment: Fragment
|
|
52
|
+
get() = this
|
|
53
|
+
|
|
54
|
+
constructor() {
|
|
55
|
+
throw IllegalStateException(
|
|
56
|
+
"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.",
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
constructor(screen: Screen) : super() {
|
|
61
|
+
this.screen = screen
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
65
|
+
super.onCreate(savedInstanceState)
|
|
66
|
+
// Right now whole purpose of this Fragment is to be displayed as a dialog.
|
|
67
|
+
// I've experimented with setting false here, but could not get it to work.
|
|
68
|
+
showsDialog = true
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// We override this method to provide our custom dialog type instead of the default Dialog.
|
|
72
|
+
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
73
|
+
configureDialogAndBehaviour()
|
|
74
|
+
|
|
75
|
+
val reactEventDispatcher = checkNotNull(screen.reactEventDispatcher) { "[RNScreens] No ReactEventDispatcher attached to screen while creating modal fragment" }
|
|
76
|
+
val rootView = BottomSheetDialogRootView(screen.reactContext, reactEventDispatcher)
|
|
77
|
+
|
|
78
|
+
rootView.addView(screen.recycle())
|
|
79
|
+
sheetDialog.setContentView(rootView)
|
|
80
|
+
|
|
81
|
+
rootView.parentAsView()?.clipToOutline = true
|
|
82
|
+
|
|
83
|
+
return sheetDialog
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
override fun onCreateView(
|
|
87
|
+
inflater: LayoutInflater,
|
|
88
|
+
container: ViewGroup?,
|
|
89
|
+
savedInstanceState: Bundle?,
|
|
90
|
+
): View? = null
|
|
91
|
+
|
|
92
|
+
override fun dismissFromContainer() {
|
|
93
|
+
check(container is ScreenStack)
|
|
94
|
+
val container = container as ScreenStack
|
|
95
|
+
container.dismiss(this)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Modal can never be first on the stack
|
|
99
|
+
override fun canNavigateBack(): Boolean = true
|
|
100
|
+
|
|
101
|
+
override fun addChildScreenContainer(container: ScreenContainer) {
|
|
102
|
+
childScreenContainers.add(container)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
override fun removeChildScreenContainer(container: ScreenContainer) {
|
|
106
|
+
childScreenContainers.remove(container)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
override fun onContainerUpdate() {
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
override fun onViewAnimationStart() {
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
override fun onViewAnimationEnd() {
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
override fun tryGetActivity(): Activity? = requireActivity()
|
|
119
|
+
|
|
120
|
+
override fun tryGetContext(): ReactContext? {
|
|
121
|
+
if (context is ReactContext) {
|
|
122
|
+
return context as ReactContext
|
|
123
|
+
}
|
|
124
|
+
if (screen.context is ReactContext) {
|
|
125
|
+
return screen.context as ReactContext
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
var parent: ViewParent? = screen.container
|
|
129
|
+
while (parent != null) {
|
|
130
|
+
if (parent is Screen && parent.context is ReactContext) {
|
|
131
|
+
return parent.context as ReactContext
|
|
132
|
+
}
|
|
133
|
+
parent = parent.parent
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return null
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
override fun canDispatchLifecycleEvent(event: ScreenFragment.ScreenLifecycleEvent): Boolean {
|
|
140
|
+
TODO("Not yet implemented")
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
override fun updateLastEventDispatched(event: ScreenFragment.ScreenLifecycleEvent) {
|
|
144
|
+
TODO("Not yet implemented")
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
override fun dispatchLifecycleEvent(
|
|
148
|
+
event: ScreenFragment.ScreenLifecycleEvent,
|
|
149
|
+
fragmentWrapper: ScreenFragmentWrapper,
|
|
150
|
+
) {
|
|
151
|
+
TODO("Not yet implemented")
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
override fun dispatchLifecycleEventInChildContainers(event: ScreenFragment.ScreenLifecycleEvent) {
|
|
155
|
+
TODO("Not yet implemented")
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
override fun dispatchHeaderBackButtonClickedEvent() {
|
|
159
|
+
TODO("Not yet implemented")
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
override fun dispatchTransitionProgressEvent(
|
|
163
|
+
alpha: Float,
|
|
164
|
+
closing: Boolean,
|
|
165
|
+
) {
|
|
166
|
+
TODO("Not yet implemented")
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
override fun onDestroy() {
|
|
170
|
+
super.onDestroy()
|
|
171
|
+
val container = container
|
|
172
|
+
if (container == null || !container.hasScreen(this)) {
|
|
173
|
+
val screenContext = screen.context
|
|
174
|
+
if (screenContext is ReactContext) {
|
|
175
|
+
val surfaceId = UIManagerHelper.getSurfaceId(screenContext)
|
|
176
|
+
UIManagerHelper
|
|
177
|
+
.getEventDispatcherForReactTag(screenContext, screen.id)
|
|
178
|
+
?.dispatchEvent(ScreenDismissedEvent(surfaceId, screen.id))
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
childScreenContainers.clear()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
override fun removeToolbar(): Unit = throw IllegalStateException("[RNScreens] Modal screens on Android do not support header right now")
|
|
185
|
+
|
|
186
|
+
override fun setToolbar(toolbar: Toolbar): Unit =
|
|
187
|
+
throw IllegalStateException("[RNScreens] Modal screens on Android do not support header right now")
|
|
188
|
+
|
|
189
|
+
override fun setToolbarShadowHidden(hidden: Boolean): Unit =
|
|
190
|
+
throw IllegalStateException("[RNScreens] Modal screens on Android do not support header right now")
|
|
191
|
+
|
|
192
|
+
override fun setToolbarTranslucent(translucent: Boolean): Unit =
|
|
193
|
+
throw IllegalStateException("[RNScreens] Modal screens on Android do not support header right now")
|
|
194
|
+
|
|
195
|
+
private fun configureDialogAndBehaviour(): BottomSheetDialog {
|
|
196
|
+
sheetDialog = BottomSheetDialogScreen(requireContext(), this)
|
|
197
|
+
sheetDialog.dismissWithAnimation = true
|
|
198
|
+
sheetDialog.setCanceledOnTouchOutside(screen.sheetClosesOnTouchOutside)
|
|
199
|
+
|
|
200
|
+
configureBehaviour()
|
|
201
|
+
|
|
202
|
+
return sheetDialog
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* This method might return slightly different values depending on code path,
|
|
207
|
+
* but during testing I've found this effect negligible. For practical purposes
|
|
208
|
+
* this is acceptable.
|
|
209
|
+
*/
|
|
210
|
+
private fun tryResolveContainerHeight(): Int? {
|
|
211
|
+
screen.container?.height?.let { return it }
|
|
212
|
+
context
|
|
213
|
+
?.resources
|
|
214
|
+
?.displayMetrics
|
|
215
|
+
?.heightPixels
|
|
216
|
+
?.let { return it }
|
|
217
|
+
|
|
218
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
219
|
+
(context?.getSystemService(Context.WINDOW_SERVICE) as? WindowManager)
|
|
220
|
+
?.currentWindowMetrics
|
|
221
|
+
?.bounds
|
|
222
|
+
?.height()
|
|
223
|
+
?.let { return it }
|
|
224
|
+
}
|
|
225
|
+
return null
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private fun configureBehaviour() {
|
|
229
|
+
val containerHeight = tryResolveContainerHeight()
|
|
230
|
+
check(containerHeight != null) { "[RNScreens] Failed to find window height during bottom sheet behaviour configuration" }
|
|
231
|
+
|
|
232
|
+
behavior.apply {
|
|
233
|
+
isHideable = true
|
|
234
|
+
isDraggable = true
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
when (screen.sheetDetents.count()) {
|
|
238
|
+
1 ->
|
|
239
|
+
behavior.apply {
|
|
240
|
+
state = BottomSheetBehavior.STATE_EXPANDED
|
|
241
|
+
skipCollapsed = true
|
|
242
|
+
isFitToContents = true
|
|
243
|
+
maxHeight = (screen.sheetDetents.first() * containerHeight).toInt()
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
2 ->
|
|
247
|
+
behavior.apply {
|
|
248
|
+
state =
|
|
249
|
+
SheetUtils.sheetStateFromDetentIndex(
|
|
250
|
+
screen.sheetInitialDetentIndex,
|
|
251
|
+
screen.sheetDetents.count(),
|
|
252
|
+
)
|
|
253
|
+
skipCollapsed = false
|
|
254
|
+
isFitToContents = true
|
|
255
|
+
peekHeight = (screen.sheetDetents[0] * containerHeight).toInt()
|
|
256
|
+
maxHeight = (screen.sheetDetents[1] * containerHeight).toInt()
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
3 ->
|
|
260
|
+
behavior.apply {
|
|
261
|
+
state =
|
|
262
|
+
SheetUtils.sheetStateFromDetentIndex(
|
|
263
|
+
screen.sheetInitialDetentIndex,
|
|
264
|
+
screen.sheetDetents.count(),
|
|
265
|
+
)
|
|
266
|
+
skipCollapsed = false
|
|
267
|
+
isFitToContents = false
|
|
268
|
+
peekHeight = (screen.sheetDetents[0] * containerHeight).toInt()
|
|
269
|
+
expandedOffset = ((1 - screen.sheetDetents[2]) * containerHeight).toInt()
|
|
270
|
+
halfExpandedRatio =
|
|
271
|
+
(screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat()
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
else -> throw IllegalStateException("[RNScreens] Invalid detent count ${screen.sheetDetents.count()}. Expected at most 3.")
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
companion object {
|
|
279
|
+
const val TAG = "ScreenModalFragment"
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -7,6 +7,7 @@ import android.view.View
|
|
|
7
7
|
import com.facebook.react.bridge.ReactContext
|
|
8
8
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
9
9
|
import com.swmansion.rnscreens.Screen.StackAnimation
|
|
10
|
+
import com.swmansion.rnscreens.bottomsheet.DimmingFragment
|
|
10
11
|
import com.swmansion.rnscreens.events.StackFinishTransitioningEvent
|
|
11
12
|
import java.util.Collections
|
|
12
13
|
import kotlin.collections.ArrayList
|
|
@@ -26,6 +27,11 @@ class ScreenStack(
|
|
|
26
27
|
private var previousChildrenCount = 0
|
|
27
28
|
var goingForward = false
|
|
28
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Marks given fragment as to-be-dismissed and performs updates on container
|
|
32
|
+
*
|
|
33
|
+
* @param fragmentWrapper to-be-dismissed wrapper
|
|
34
|
+
*/
|
|
29
35
|
fun dismiss(screenFragment: ScreenStackFragmentWrapper) {
|
|
30
36
|
dismissedWrappers.add(screenFragment)
|
|
31
37
|
performUpdatesNow()
|
|
@@ -38,18 +44,16 @@ class ScreenStack(
|
|
|
38
44
|
get() = stack
|
|
39
45
|
|
|
40
46
|
val rootScreen: Screen
|
|
41
|
-
get()
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
get() =
|
|
48
|
+
screenWrappers.firstOrNull { !dismissedWrappers.contains(it) }?.screen
|
|
49
|
+
?: throw IllegalStateException("[RNScreens] Stack has no root screen set")
|
|
50
|
+
|
|
51
|
+
override fun adapt(screen: Screen): ScreenStackFragmentWrapper =
|
|
52
|
+
when (screen.stackPresentation) {
|
|
53
|
+
Screen.StackPresentation.FORM_SHEET -> DimmingFragment(ScreenStackFragment(screen))
|
|
54
|
+
else -> ScreenStackFragment(screen)
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
override fun adapt(screen: Screen) = ScreenStackFragment(screen)
|
|
52
|
-
|
|
53
57
|
override fun startViewTransition(view: View) {
|
|
54
58
|
super.startViewTransition(view)
|
|
55
59
|
removalTransitionStarted = true
|
|
@@ -94,8 +98,10 @@ class ScreenStack(
|
|
|
94
98
|
// when all screens are dismissed and no screen is to be displayed on top. We need to gracefully
|
|
95
99
|
// handle the case of newTop being NULL, which happens in several places below
|
|
96
100
|
var newTop: ScreenFragmentWrapper? = null // newTop is nullable, see the above comment ^
|
|
97
|
-
var visibleBottom: ScreenFragmentWrapper? =
|
|
101
|
+
var visibleBottom: ScreenFragmentWrapper? =
|
|
102
|
+
null // this is only set if newTop has one of transparent presentation modes
|
|
98
103
|
isDetachingCurrentScreen = false // we reset it so the previous value is not used by mistake
|
|
104
|
+
|
|
99
105
|
for (i in screenWrappers.indices.reversed()) {
|
|
100
106
|
val screenWrapper = getScreenFragmentWrapperAt(i)
|
|
101
107
|
if (!dismissedWrappers.contains(screenWrapper)) {
|
|
@@ -109,6 +115,7 @@ class ScreenStack(
|
|
|
109
115
|
}
|
|
110
116
|
}
|
|
111
117
|
}
|
|
118
|
+
|
|
112
119
|
var shouldUseOpenAnimation = true
|
|
113
120
|
var stackAnimation: StackAnimation? = null
|
|
114
121
|
if (!stack.contains(newTop)) {
|
|
@@ -141,9 +148,24 @@ class ScreenStack(
|
|
|
141
148
|
if (stackAnimation != null) {
|
|
142
149
|
if (shouldUseOpenAnimation) {
|
|
143
150
|
when (stackAnimation) {
|
|
144
|
-
StackAnimation.DEFAULT ->
|
|
145
|
-
|
|
146
|
-
|
|
151
|
+
StackAnimation.DEFAULT ->
|
|
152
|
+
it.setCustomAnimations(
|
|
153
|
+
R.anim.rns_default_enter_in,
|
|
154
|
+
R.anim.rns_default_enter_out,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
StackAnimation.NONE ->
|
|
158
|
+
it.setCustomAnimations(
|
|
159
|
+
R.anim.rns_no_animation_20,
|
|
160
|
+
R.anim.rns_no_animation_20,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
StackAnimation.FADE ->
|
|
164
|
+
it.setCustomAnimations(
|
|
165
|
+
R.anim.rns_fade_in,
|
|
166
|
+
R.anim.rns_fade_out,
|
|
167
|
+
)
|
|
168
|
+
|
|
147
169
|
StackAnimation.SLIDE_FROM_RIGHT ->
|
|
148
170
|
it.setCustomAnimations(
|
|
149
171
|
R.anim.rns_slide_in_from_right,
|
|
@@ -164,9 +186,24 @@ class ScreenStack(
|
|
|
164
186
|
}
|
|
165
187
|
} else {
|
|
166
188
|
when (stackAnimation) {
|
|
167
|
-
StackAnimation.DEFAULT ->
|
|
168
|
-
|
|
169
|
-
|
|
189
|
+
StackAnimation.DEFAULT ->
|
|
190
|
+
it.setCustomAnimations(
|
|
191
|
+
R.anim.rns_default_exit_in,
|
|
192
|
+
R.anim.rns_default_exit_out,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
StackAnimation.NONE ->
|
|
196
|
+
it.setCustomAnimations(
|
|
197
|
+
R.anim.rns_no_animation_20,
|
|
198
|
+
R.anim.rns_no_animation_20,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
StackAnimation.FADE ->
|
|
202
|
+
it.setCustomAnimations(
|
|
203
|
+
R.anim.rns_fade_in,
|
|
204
|
+
R.anim.rns_fade_out,
|
|
205
|
+
)
|
|
206
|
+
|
|
170
207
|
StackAnimation.SLIDE_FROM_RIGHT ->
|
|
171
208
|
it.setCustomAnimations(
|
|
172
209
|
R.anim.rns_slide_in_from_left,
|
|
@@ -239,7 +276,9 @@ class ScreenStack(
|
|
|
239
276
|
}
|
|
240
277
|
}
|
|
241
278
|
// when first visible screen found, make all screens after that visible
|
|
242
|
-
it.add(id, fragmentWrapper.fragment).runOnCommit {
|
|
279
|
+
it.add(id, fragmentWrapper.fragment).runOnCommit {
|
|
280
|
+
top?.screen?.bringToFront()
|
|
281
|
+
}
|
|
243
282
|
}
|
|
244
283
|
} else if (newTop != null && !newTop.fragment.isAdded) {
|
|
245
284
|
it.add(id, newTop.fragment)
|
|
@@ -262,7 +301,9 @@ class ScreenStack(
|
|
|
262
301
|
val screenFragmentsBeneathTop = screenWrappers.slice(0 until screenWrappers.size - 1).asReversed()
|
|
263
302
|
// go from the top of the stack excluding the top screen
|
|
264
303
|
for (fragmentWrapper in screenFragmentsBeneathTop) {
|
|
265
|
-
fragmentWrapper.screen.changeAccessibilityMode(
|
|
304
|
+
fragmentWrapper.screen.changeAccessibilityMode(
|
|
305
|
+
IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS,
|
|
306
|
+
)
|
|
266
307
|
|
|
267
308
|
// don't change a11y below non-transparent screens
|
|
268
309
|
if (fragmentWrapper == visibleBottom) {
|
|
@@ -363,6 +404,8 @@ class ScreenStack(
|
|
|
363
404
|
}
|
|
364
405
|
|
|
365
406
|
companion object {
|
|
407
|
+
const val TAG = "ScreenStack"
|
|
408
|
+
|
|
366
409
|
private fun needsDrawReordering(fragmentWrapper: ScreenFragmentWrapper): Boolean =
|
|
367
410
|
// On Android sdk 33 and above the animation is different and requires draw reordering.
|
|
368
411
|
// For React Native 0.70 and lower versions, `Build.VERSION_CODES.TIRAMISU` is not defined yet.
|