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.
Files changed (115) hide show
  1. package/android/build.gradle +2 -2
  2. package/android/src/main/java/com/swmansion/rnscreens/InsetsObserverProxy.kt +67 -0
  3. package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +2 -0
  4. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +101 -4
  5. package/android/src/main/java/com/swmansion/rnscreens/ScreenContentWrapper.kt +38 -0
  6. package/android/src/main/java/com/swmansion/rnscreens/ScreenContentWrapperManager.kt +25 -0
  7. package/android/src/main/java/com/swmansion/rnscreens/ScreenFooter.kt +287 -0
  8. package/android/src/main/java/com/swmansion/rnscreens/ScreenFooterManager.kt +25 -0
  9. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +11 -19
  10. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragmentWrapper.kt +4 -0
  11. package/android/src/main/java/com/swmansion/rnscreens/ScreenModalFragment.kt +281 -0
  12. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +62 -19
  13. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +403 -41
  14. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragmentWrapper.kt +4 -1
  15. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +2 -2
  16. package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +95 -11
  17. package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +39 -28
  18. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/BottomSheetDialogRootView.kt +104 -0
  19. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/BottomSheetDialogScreen.kt +26 -0
  20. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/DimmingFragment.kt +488 -0
  21. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/DimmingView.kt +66 -0
  22. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/GestureTransparentViewGroup.kt +24 -0
  23. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/SheetUtils.kt +127 -0
  24. package/android/src/main/java/com/swmansion/rnscreens/events/SheetDetentChangedEvent.kt +27 -0
  25. package/android/src/main/java/com/swmansion/rnscreens/ext/NumericExt.kt +12 -0
  26. package/android/src/main/java/com/swmansion/rnscreens/ext/ViewExt.kt +32 -0
  27. package/android/src/main/java/com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper.kt +45 -8
  28. package/android/src/main/res/base/drawable/rns_rounder_top_corners_shape.xml +8 -0
  29. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenContentWrapperManagerDelegate.java +25 -0
  30. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenContentWrapperManagerInterface.java +16 -0
  31. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenFooterManagerDelegate.java +25 -0
  32. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenFooterManagerInterface.java +16 -0
  33. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerDelegate.java +9 -2
  34. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerInterface.java +5 -2
  35. package/ios/RNSConvert.h +5 -3
  36. package/ios/RNSConvert.mm +14 -20
  37. package/ios/RNSScreen.h +3 -2
  38. package/ios/RNSScreen.mm +433 -49
  39. package/ios/RNSScreenContentWrapper.h +44 -0
  40. package/ios/RNSScreenContentWrapper.mm +61 -0
  41. package/ios/RNSScreenFooter.h +30 -0
  42. package/ios/RNSScreenFooter.mm +137 -0
  43. package/lib/commonjs/components/Screen.js +6 -2
  44. package/lib/commonjs/components/Screen.js.map +1 -1
  45. package/lib/commonjs/components/ScreenContentWrapper.js +19 -0
  46. package/lib/commonjs/components/ScreenContentWrapper.js.map +1 -0
  47. package/lib/commonjs/components/ScreenFooter.js +23 -0
  48. package/lib/commonjs/components/ScreenFooter.js.map +1 -0
  49. package/lib/commonjs/fabric/ModalScreenNativeComponent.js.map +1 -1
  50. package/lib/commonjs/fabric/ScreenContentWrapperNativeComponent.js +10 -0
  51. package/lib/commonjs/fabric/ScreenContentWrapperNativeComponent.js.map +1 -0
  52. package/lib/commonjs/fabric/ScreenFooterNativeComponent.js +10 -0
  53. package/lib/commonjs/fabric/ScreenFooterNativeComponent.js.map +1 -0
  54. package/lib/commonjs/fabric/ScreenNativeComponent.js.map +1 -1
  55. package/lib/commonjs/index.js +30 -0
  56. package/lib/commonjs/index.js.map +1 -1
  57. package/lib/commonjs/native-stack/views/FooterComponent.js +18 -0
  58. package/lib/commonjs/native-stack/views/FooterComponent.js.map +1 -0
  59. package/lib/commonjs/native-stack/views/NativeStackView.js +59 -14
  60. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  61. package/lib/module/components/Screen.js +6 -2
  62. package/lib/module/components/Screen.js.map +1 -1
  63. package/lib/module/components/ScreenContentWrapper.js +12 -0
  64. package/lib/module/components/ScreenContentWrapper.js.map +1 -0
  65. package/lib/module/components/ScreenFooter.js +17 -0
  66. package/lib/module/components/ScreenFooter.js.map +1 -0
  67. package/lib/module/fabric/ModalScreenNativeComponent.js.map +1 -1
  68. package/lib/module/fabric/ScreenContentWrapperNativeComponent.js +3 -0
  69. package/lib/module/fabric/ScreenContentWrapperNativeComponent.js.map +1 -0
  70. package/lib/module/fabric/ScreenFooterNativeComponent.js +3 -0
  71. package/lib/module/fabric/ScreenFooterNativeComponent.js.map +1 -0
  72. package/lib/module/fabric/ScreenNativeComponent.js.map +1 -1
  73. package/lib/module/index.js +2 -0
  74. package/lib/module/index.js.map +1 -1
  75. package/lib/module/native-stack/views/FooterComponent.js +11 -0
  76. package/lib/module/native-stack/views/FooterComponent.js.map +1 -0
  77. package/lib/module/native-stack/views/NativeStackView.js +61 -15
  78. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  79. package/lib/typescript/components/Screen.d.ts.map +1 -1
  80. package/lib/typescript/components/ScreenContentWrapper.d.ts +6 -0
  81. package/lib/typescript/components/ScreenContentWrapper.d.ts.map +1 -0
  82. package/lib/typescript/components/ScreenFooter.d.ts +12 -0
  83. package/lib/typescript/components/ScreenFooter.d.ts.map +1 -0
  84. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts +2 -3
  85. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts.map +1 -1
  86. package/lib/typescript/fabric/ScreenContentWrapperNativeComponent.d.ts +7 -0
  87. package/lib/typescript/fabric/ScreenContentWrapperNativeComponent.d.ts.map +1 -0
  88. package/lib/typescript/fabric/ScreenFooterNativeComponent.d.ts +7 -0
  89. package/lib/typescript/fabric/ScreenFooterNativeComponent.d.ts.map +1 -0
  90. package/lib/typescript/fabric/ScreenNativeComponent.d.ts +9 -3
  91. package/lib/typescript/fabric/ScreenNativeComponent.d.ts.map +1 -1
  92. package/lib/typescript/index.d.ts +2 -0
  93. package/lib/typescript/index.d.ts.map +1 -1
  94. package/lib/typescript/native-stack/types.d.ts +63 -23
  95. package/lib/typescript/native-stack/types.d.ts.map +1 -1
  96. package/lib/typescript/native-stack/views/FooterComponent.d.ts +7 -0
  97. package/lib/typescript/native-stack/views/FooterComponent.d.ts.map +1 -0
  98. package/lib/typescript/native-stack/views/NativeStackView.d.ts.map +1 -1
  99. package/lib/typescript/types.d.ts +42 -17
  100. package/lib/typescript/types.d.ts.map +1 -1
  101. package/native-stack/README.md +16 -14
  102. package/package.json +1 -1
  103. package/react-native.config.js +18 -16
  104. package/src/components/Screen.tsx +6 -2
  105. package/src/components/ScreenContentWrapper.tsx +12 -0
  106. package/src/components/ScreenFooter.tsx +18 -0
  107. package/src/fabric/ModalScreenNativeComponent.ts +2 -4
  108. package/src/fabric/ScreenContentWrapperNativeComponent.ts +9 -0
  109. package/src/fabric/ScreenFooterNativeComponent.ts +6 -0
  110. package/src/fabric/ScreenNativeComponent.ts +10 -4
  111. package/src/index.tsx +10 -0
  112. package/src/native-stack/types.tsx +57 -23
  113. package/src/native-stack/views/FooterComponent.tsx +10 -0
  114. package/src/native-stack/views/NativeStackView.tsx +74 -11
  115. 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(recycleView(screen))
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 = parentFragment
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
- @JvmStatic
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
- for (i in 0 until screenCount) {
43
- val screenWrapper = getScreenFragmentWrapperAt(i)
44
- if (!dismissedWrappers.contains(screenWrapper)) {
45
- return screenWrapper.screen
46
- }
47
- }
48
- throw IllegalStateException("Stack has no root screen set")
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? = null // this is only set if newTop has TRANSPARENT_MODAL presentation mode
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 -> it.setCustomAnimations(R.anim.rns_default_enter_in, R.anim.rns_default_enter_out)
145
- StackAnimation.NONE -> it.setCustomAnimations(R.anim.rns_no_animation_20, R.anim.rns_no_animation_20)
146
- StackAnimation.FADE -> it.setCustomAnimations(R.anim.rns_fade_in, R.anim.rns_fade_out)
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 -> it.setCustomAnimations(R.anim.rns_default_exit_in, R.anim.rns_default_exit_out)
168
- StackAnimation.NONE -> it.setCustomAnimations(R.anim.rns_no_animation_20, R.anim.rns_no_animation_20)
169
- StackAnimation.FADE -> it.setCustomAnimations(R.anim.rns_fade_in, R.anim.rns_fade_out)
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 { top?.screen?.bringToFront() }
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(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
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.