react-native-screens 4.10.0-beta.1 → 4.10.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledHeaderConfigViewGroup.kt +3 -8
  2. package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +111 -15
  3. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +11 -2
  4. package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +1 -1
  5. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +174 -199
  6. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +31 -285
  7. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +89 -54
  8. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigShadowNode.kt +3 -0
  9. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackViewManager.kt +1 -1
  10. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/SheetDelegate.kt +257 -3
  11. package/android/src/main/java/com/swmansion/rnscreens/ext/ViewExt.kt +7 -0
  12. package/android/src/main/java/com/swmansion/rnscreens/utils/FragmentTransactionKt.kt +103 -0
  13. package/android/src/main/java/com/swmansion/rnscreens/utils/InsetsKt.kt +31 -0
  14. package/android/src/main/java/com/swmansion/rnscreens/utils/PaddingBundle.kt +1 -0
  15. package/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledHeaderConfigViewGroup.kt +14 -5
  16. package/ios/RNSFullWindowOverlay.mm +6 -6
  17. package/lib/commonjs/components/Screen.js +5 -3
  18. package/lib/commonjs/components/Screen.js.map +1 -1
  19. package/lib/commonjs/native-stack/views/NativeStackView.js +3 -5
  20. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  21. package/lib/module/components/Screen.js +5 -3
  22. package/lib/module/components/Screen.js.map +1 -1
  23. package/lib/module/native-stack/views/NativeStackView.js +3 -5
  24. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  25. package/lib/typescript/components/Screen.d.ts.map +1 -1
  26. package/lib/typescript/native-stack/views/NativeStackView.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/src/components/Screen.tsx +7 -5
  29. package/src/native-stack/views/NativeStackView.tsx +11 -12
  30. package/android/src/main/java/com/swmansion/rnscreens/NativeDismissalObserver.kt +0 -12
@@ -23,24 +23,19 @@ abstract class FabricEnabledHeaderConfigViewGroup(
23
23
  mStateWrapper = wrapper
24
24
  }
25
25
 
26
- fun updatePaddings(
27
- paddingStart: Int,
28
- paddingEnd: Int,
29
- ) {
30
- // Do nothing on Fabric. This method is used only on Paper.
31
- }
32
-
33
26
  fun updateHeaderConfigState(
34
27
  width: Int,
35
28
  height: Int,
36
29
  paddingStart: Int,
37
30
  paddingEnd: Int,
38
31
  ) {
32
+ // Implementation of this method differs between Fabric & Paper!
39
33
  updateState(width, height, paddingStart, paddingEnd)
40
34
  }
41
35
 
36
+ // Implementation of this method differs between Fabric & Paper!
42
37
  @UiThread
43
- fun updateState(
38
+ private fun updateState(
44
39
  width: Int,
45
40
  height: Int,
46
41
  paddingStart: Int,
@@ -3,18 +3,40 @@ package com.swmansion.rnscreens
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
5
  import android.os.Build
6
+ import android.view.WindowInsets
6
7
  import android.view.WindowManager
7
8
  import androidx.appcompat.widget.Toolbar
9
+ import androidx.core.view.WindowInsetsCompat
8
10
  import com.facebook.react.modules.core.ChoreographerCompat
9
11
  import com.facebook.react.modules.core.ReactChoreographer
10
12
  import com.facebook.react.uimanager.ThemedReactContext
13
+ import com.swmansion.rnscreens.utils.InsetsCompat
14
+ import com.swmansion.rnscreens.utils.resolveInsetsOrZero
15
+ import kotlin.math.max
11
16
 
12
- // This class is used to store config closer to search bar
17
+ /**
18
+ * Main toolbar class representing the native header.
19
+ *
20
+ * This class is used to store config closer to search bar.
21
+ * It also handles inset/padding related logic in coordination with header config.
22
+ */
13
23
  @SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated.
14
24
  open class CustomToolbar(
15
25
  context: Context,
16
26
  val config: ScreenStackHeaderConfig,
17
27
  ) : Toolbar(context) {
28
+ // Switch this flag to enable/disable display cutout avoidance.
29
+ // Currently this is controlled by isTopInsetEnabled prop.
30
+ private val shouldAvoidDisplayCutout
31
+ get() = config.isTopInsetEnabled
32
+
33
+ private val shouldApplyTopInset
34
+ get() = config.isTopInsetEnabled
35
+
36
+ private var lastInsets = InsetsCompat.NONE
37
+
38
+ private var isForceShadowStateUpdateOnLayoutRequested = false
39
+
18
40
  private var isLayoutEnqueued = false
19
41
  private val layoutCallback: ChoreographerCompat.FrameCallback =
20
42
  object : ChoreographerCompat.FrameCallback() {
@@ -59,27 +81,101 @@ open class CustomToolbar(
59
81
  }
60
82
  }
61
83
 
84
+ override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets? {
85
+ val unhandledInsets = super.onApplyWindowInsets(insets)
86
+
87
+ // There are few UI modes we could be running in
88
+ //
89
+ // 1. legacy non edge-to-edge mode,
90
+ // 2. edge-to-edge with gesture navigation,
91
+ // 3. edge-to-edge with translucent navigation buttons bar.
92
+ //
93
+ // Additionally we need to gracefully handle possible display cutouts.
94
+
95
+ // We use rootWindowInsets in lieu of insets or unhandledInsets here,
96
+ // because cutout sometimes (only in certain scenarios, e.g. with headerLeft view present)
97
+ // happen to be Insets.ZERO and is not reliable.
98
+ val rootWindowInsets = rootWindowInsets
99
+ val cutoutInsets =
100
+ resolveInsetsOrZero(WindowInsetsCompat.Type.displayCutout(), rootWindowInsets)
101
+ val systemBarInsets =
102
+ resolveInsetsOrZero(WindowInsetsCompat.Type.systemBars(), rootWindowInsets)
103
+ val statusBarInsetsStable =
104
+ resolveInsetsOrZero(
105
+ WindowInsetsCompat.Type.systemBars(),
106
+ rootWindowInsets,
107
+ ignoreVisibility = true,
108
+ )
109
+
110
+ // This seems to work fine in all tested configurations, because cutout & system bars overlap
111
+ // only in portrait mode & top inset is controlled separately, therefore we don't count
112
+ // any insets twice.
113
+ val horizontalInsets =
114
+ InsetsCompat.of(
115
+ cutoutInsets.left + systemBarInsets.left,
116
+ 0,
117
+ cutoutInsets.right + systemBarInsets.right,
118
+ 0,
119
+ )
120
+
121
+ // We want to handle display cutout always, no matter the HeaderConfig prop values.
122
+ // If there are no cutout displays, we want to apply the additional padding to
123
+ // respect the status bar.
124
+ val verticalInsets =
125
+ InsetsCompat.of(
126
+ 0,
127
+ max(cutoutInsets.top, if (shouldApplyTopInset) statusBarInsetsStable.top else 0),
128
+ 0,
129
+ max(cutoutInsets.bottom, 0),
130
+ )
131
+
132
+ val newInsets = InsetsCompat.add(horizontalInsets, verticalInsets)
133
+
134
+ if (lastInsets != newInsets) {
135
+ lastInsets = newInsets
136
+ applyExactPadding(
137
+ lastInsets.left,
138
+ lastInsets.top,
139
+ lastInsets.right,
140
+ lastInsets.bottom,
141
+ )
142
+ }
143
+
144
+ return unhandledInsets
145
+ }
146
+
62
147
  override fun onLayout(
63
- changed: Boolean,
148
+ hasSizeChanged: Boolean,
64
149
  l: Int,
65
150
  t: Int,
66
151
  r: Int,
67
152
  b: Int,
68
153
  ) {
69
- super.onLayout(changed, l, t, r, b)
154
+ super.onLayout(hasSizeChanged, l, t, r, b)
70
155
 
71
- if (!changed) {
72
- return
73
- }
156
+ config.onNativeToolbarLayout(
157
+ this,
158
+ hasSizeChanged || isForceShadowStateUpdateOnLayoutRequested,
159
+ )
160
+ isForceShadowStateUpdateOnLayoutRequested = false
161
+ }
74
162
 
75
- val contentInsetStart = if (navigationIcon != null) contentInsetStartWithNavigation else contentInsetStart
76
- if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
77
- val width = r - l
78
- val height = b - t
79
- config.updateHeaderConfigState(width, height, contentInsetStart, contentInsetEnd)
80
- } else {
81
- // our children are already laid out
82
- config.updatePaddings(contentInsetStart, contentInsetEnd)
83
- }
163
+ fun updateContentInsets() {
164
+ contentInsetStartWithNavigation = config.preferredContentInsetStartWithNavigation
165
+ setContentInsetsRelative(config.preferredContentInsetStart, config.preferredContentInsetEnd)
166
+ }
167
+
168
+ private fun applyExactPadding(
169
+ left: Int,
170
+ top: Int,
171
+ right: Int,
172
+ bottom: Int,
173
+ ) {
174
+ requestForceShadowStateUpdateOnLayout()
175
+ setPadding(left, top, right, bottom)
176
+ }
177
+
178
+ private fun requestForceShadowStateUpdateOnLayout() {
179
+ isForceShadowStateUpdateOnLayoutRequested = shouldAvoidDisplayCutout
84
180
  }
85
181
  }
@@ -275,7 +275,7 @@ class Screen(
275
275
  throw IllegalStateException("[RNScreens] activityState can only progress in NativeStack")
276
276
  }
277
277
  this.activityState = activityState
278
- container?.notifyChildUpdate()
278
+ container?.onChildUpdate()
279
279
  }
280
280
 
281
281
  fun setScreenOrientation(screenOrientation: String?) {
@@ -482,7 +482,14 @@ class Screen(
482
482
  ?.dispatchEvent(HeaderHeightChangeEvent(surfaceId, id, headerHeight))
483
483
  }
484
484
 
485
- internal fun notifySheetDetentChange(
485
+ internal fun onSheetDetentChanged(
486
+ detentIndex: Int,
487
+ isStable: Boolean,
488
+ ) {
489
+ dispatchSheetDetentChanged(detentIndex, isStable)
490
+ }
491
+
492
+ private fun dispatchSheetDetentChanged(
486
493
  detentIndex: Int,
487
494
  isStable: Boolean,
488
495
  ) {
@@ -571,3 +578,5 @@ class Screen(
571
578
  const val SHEET_FIT_TO_CONTENTS = -1.0
572
579
  }
573
580
  }
581
+
582
+ internal fun View.asScreen() = this as Screen
@@ -91,7 +91,7 @@ open class ScreenContainer(
91
91
  val isNested: Boolean
92
92
  get() = parentScreenWrapper != null
93
93
 
94
- fun notifyChildUpdate() {
94
+ fun onChildUpdate() {
95
95
  performUpdatesNow()
96
96
  }
97
97