react-native-screens 4.0.0-beta.1 → 4.0.0-beta.10

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 (107) hide show
  1. package/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledHeaderConfigViewGroup.kt +61 -0
  2. package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +14 -0
  3. package/android/src/main/java/com/swmansion/rnscreens/InsetsObserverProxy.kt +2 -4
  4. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +46 -7
  5. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +24 -9
  6. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +37 -17
  7. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +1 -2
  8. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigShadowNode.kt +25 -0
  9. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt +18 -0
  10. package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +6 -1
  11. package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +1 -0
  12. package/android/src/main/java/com/swmansion/rnscreens/ext/ViewExt.kt +17 -0
  13. package/android/src/main/java/com/swmansion/rnscreens/utils/PaddingBundle.kt +8 -0
  14. package/android/src/main/jni/rnscreens.h +2 -0
  15. package/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledHeaderConfigViewGroup.kt +39 -0
  16. package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderConfigComponentDescriptor.h +44 -0
  17. package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderConfigShadowNode.cpp +8 -0
  18. package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderConfigShadowNode.h +32 -0
  19. package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderConfigState.cpp +23 -0
  20. package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderConfigState.h +50 -0
  21. package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderSubviewComponentDescriptor.h +27 -0
  22. package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderSubviewShadowNode.cpp +8 -0
  23. package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderSubviewShadowNode.h +32 -0
  24. package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderSubviewState.cpp +15 -0
  25. package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderSubviewState.h +40 -0
  26. package/ios/RNSConvert.mm +0 -1
  27. package/ios/RNSScreen.h +9 -0
  28. package/ios/RNSScreen.mm +37 -8
  29. package/ios/RNSScreenStack.mm +79 -1
  30. package/ios/RNSScreenStackHeaderConfig.h +63 -0
  31. package/ios/RNSScreenStackHeaderConfig.mm +126 -9
  32. package/ios/RNSScreenStackHeaderSubview.mm +26 -0
  33. package/ios/utils/UINavigationBar+RNSUtility.h +37 -0
  34. package/ios/utils/UINavigationBar+RNSUtility.mm +44 -0
  35. package/ios/utils/UIView+RNSUtility.mm +0 -1
  36. package/lib/commonjs/components/Screen.js +58 -7
  37. package/lib/commonjs/components/Screen.js.map +1 -1
  38. package/lib/commonjs/components/ScreenContentWrapper.web.js +11 -0
  39. package/lib/commonjs/components/ScreenContentWrapper.web.js.map +1 -0
  40. package/lib/commonjs/components/ScreenFooter.web.js +11 -0
  41. package/lib/commonjs/components/ScreenFooter.web.js.map +1 -0
  42. package/lib/commonjs/components/ScreenStackHeaderConfig.js +53 -17
  43. package/lib/commonjs/components/ScreenStackHeaderConfig.js.map +1 -1
  44. package/lib/commonjs/components/helpers/usePrevious.js +15 -0
  45. package/lib/commonjs/components/helpers/usePrevious.js.map +1 -0
  46. package/lib/commonjs/fabric/ModalScreenNativeComponent.js.map +1 -1
  47. package/lib/commonjs/fabric/ScreenNativeComponent.js.map +1 -1
  48. package/lib/commonjs/fabric/ScreenStackHeaderConfigNativeComponent.js +3 -1
  49. package/lib/commonjs/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  50. package/lib/commonjs/fabric/ScreenStackHeaderSubviewNativeComponent.js +3 -1
  51. package/lib/commonjs/fabric/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
  52. package/lib/commonjs/native-stack/views/NativeStackView.js +4 -10
  53. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  54. package/lib/module/components/Screen.js +58 -7
  55. package/lib/module/components/Screen.js.map +1 -1
  56. package/lib/module/components/ScreenContentWrapper.web.js +5 -0
  57. package/lib/module/components/ScreenContentWrapper.web.js.map +1 -0
  58. package/lib/module/components/ScreenFooter.web.js +5 -0
  59. package/lib/module/components/ScreenFooter.web.js.map +1 -0
  60. package/lib/module/components/ScreenStackHeaderConfig.js +50 -16
  61. package/lib/module/components/ScreenStackHeaderConfig.js.map +1 -1
  62. package/lib/module/components/helpers/usePrevious.js +9 -0
  63. package/lib/module/components/helpers/usePrevious.js.map +1 -0
  64. package/lib/module/fabric/ModalScreenNativeComponent.js.map +1 -1
  65. package/lib/module/fabric/ScreenNativeComponent.js.map +1 -1
  66. package/lib/module/fabric/ScreenStackHeaderConfigNativeComponent.js +3 -1
  67. package/lib/module/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  68. package/lib/module/fabric/ScreenStackHeaderSubviewNativeComponent.js +3 -1
  69. package/lib/module/fabric/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
  70. package/lib/module/native-stack/views/NativeStackView.js +4 -10
  71. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  72. package/lib/typescript/components/Screen.d.ts +5 -1
  73. package/lib/typescript/components/Screen.d.ts.map +1 -1
  74. package/lib/typescript/components/ScreenContentWrapper.web.d.ts +5 -0
  75. package/lib/typescript/components/ScreenContentWrapper.web.d.ts.map +1 -0
  76. package/lib/typescript/components/ScreenFooter.web.d.ts +5 -0
  77. package/lib/typescript/components/ScreenFooter.web.d.ts.map +1 -0
  78. package/lib/typescript/components/ScreenStackHeaderConfig.d.ts +1 -1
  79. package/lib/typescript/components/ScreenStackHeaderConfig.d.ts.map +1 -1
  80. package/lib/typescript/components/helpers/usePrevious.d.ts +2 -0
  81. package/lib/typescript/components/helpers/usePrevious.d.ts.map +1 -0
  82. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts +1 -0
  83. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts.map +1 -1
  84. package/lib/typescript/fabric/ScreenNativeComponent.d.ts +1 -1
  85. package/lib/typescript/fabric/ScreenNativeComponent.d.ts.map +1 -1
  86. package/lib/typescript/fabric/ScreenStackHeaderConfigNativeComponent.d.ts.map +1 -1
  87. package/lib/typescript/fabric/ScreenStackHeaderSubviewNativeComponent.d.ts.map +1 -1
  88. package/lib/typescript/native-stack/types.d.ts +2 -3
  89. package/lib/typescript/native-stack/types.d.ts.map +1 -1
  90. package/lib/typescript/native-stack/views/NativeStackView.d.ts.map +1 -1
  91. package/lib/typescript/types.d.ts +26 -9
  92. package/lib/typescript/types.d.ts.map +1 -1
  93. package/native-stack/README.md +3 -2
  94. package/package.json +1 -1
  95. package/src/components/Screen.tsx +97 -12
  96. package/src/components/ScreenContentWrapper.web.tsx +6 -0
  97. package/src/components/ScreenFooter.web.tsx +6 -0
  98. package/src/components/ScreenStackHeaderConfig.tsx +59 -26
  99. package/src/components/helpers/usePrevious.tsx +11 -0
  100. package/src/fabric/ModalScreenNativeComponent.ts +1 -0
  101. package/src/fabric/ScreenNativeComponent.ts +0 -1
  102. package/src/fabric/ScreenStackHeaderConfigNativeComponent.ts +3 -1
  103. package/src/fabric/ScreenStackHeaderSubviewNativeComponent.ts +3 -1
  104. package/src/native-stack/types.tsx +2 -3
  105. package/src/native-stack/views/NativeStackView.tsx +4 -10
  106. package/src/types.tsx +29 -13
  107. package/windows/RNScreens/Screen.h +0 -1
@@ -0,0 +1,61 @@
1
+ package com.swmansion.rnscreens
2
+
3
+ import android.content.Context
4
+ import android.view.ViewGroup
5
+ import androidx.annotation.UiThread
6
+ import com.facebook.react.bridge.WritableMap
7
+ import com.facebook.react.bridge.WritableNativeMap
8
+ import com.facebook.react.uimanager.PixelUtil
9
+ import com.facebook.react.uimanager.StateWrapper
10
+ import kotlin.math.abs
11
+
12
+ abstract class FabricEnabledHeaderConfigViewGroup(
13
+ context: Context?,
14
+ ) : ViewGroup(context) {
15
+ private var mStateWrapper: StateWrapper? = null
16
+
17
+ private var lastPaddingStart = 0f
18
+ private var lastPaddingEnd = 0f
19
+
20
+ fun setStateWrapper(wrapper: StateWrapper?) {
21
+ mStateWrapper = wrapper
22
+ }
23
+
24
+ fun updatePaddingsFabric(
25
+ paddingStart: Int,
26
+ paddingEnd: Int,
27
+ ) {
28
+ updateState(paddingStart, paddingEnd)
29
+ }
30
+
31
+ @UiThread
32
+ fun updateState(
33
+ paddingStart: Int,
34
+ paddingEnd: Int,
35
+ ) {
36
+ val paddingStartDip: Float = PixelUtil.toDIPFromPixel(paddingStart.toFloat())
37
+ val paddingEndDip: Float = PixelUtil.toDIPFromPixel(paddingEnd.toFloat())
38
+
39
+ // Check incoming state values. If they're already the correct value, return early to prevent
40
+ // infinite UpdateState/SetState loop.
41
+ if (abs(lastPaddingStart - paddingStart) < DELTA &&
42
+ abs(lastPaddingEnd - paddingEnd) < DELTA
43
+ ) {
44
+ return
45
+ }
46
+
47
+ lastPaddingStart = paddingStartDip
48
+ lastPaddingEnd = paddingEndDip
49
+
50
+ val map: WritableMap =
51
+ WritableNativeMap().apply {
52
+ putDouble("paddingStart", paddingStartDip.toDouble())
53
+ putDouble("paddingEnd", paddingEndDip.toDouble())
54
+ }
55
+ mStateWrapper?.updateState(map)
56
+ }
57
+
58
+ companion object {
59
+ private const val DELTA = 0.9f
60
+ }
61
+ }
@@ -48,4 +48,18 @@ open class CustomToolbar(
48
48
  }
49
49
  }
50
50
  }
51
+
52
+ override fun onLayout(
53
+ changed: Boolean,
54
+ l: Int,
55
+ t: Int,
56
+ r: Int,
57
+ b: Int,
58
+ ) {
59
+ super.onLayout(changed, l, t, r, b)
60
+
61
+ // our children are already laid out
62
+ val contentInsetStart = if (navigationIcon != null) contentInsetStartWithNavigation else contentInsetStart
63
+ config.updatePaddingsFabric(contentInsetStart, contentInsetEnd)
64
+ }
51
65
  }
@@ -57,10 +57,8 @@ object InsetsObserverProxy : OnApplyWindowInsetsListener {
57
57
  }
58
58
  }
59
59
 
60
- fun unregister() {
61
- eventSourceView.get()?.takeIf { hasBeenRegistered }?.let {
62
- ViewCompat.setOnApplyWindowInsetsListener(it, null)
63
- }
60
+ fun unregisterOnView(view: View) {
61
+ ViewCompat.setOnApplyWindowInsetsListener(view, null)
64
62
  }
65
63
 
66
64
  private fun getObservedView(): View? = eventSourceView.get()
@@ -16,13 +16,18 @@ import androidx.fragment.app.Fragment
16
16
  import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
17
17
  import com.facebook.react.bridge.GuardedRunnable
18
18
  import com.facebook.react.bridge.ReactContext
19
+ import com.facebook.react.uimanager.PixelUtil
19
20
  import com.facebook.react.uimanager.UIManagerHelper
20
21
  import com.facebook.react.uimanager.UIManagerModule
21
22
  import com.facebook.react.uimanager.events.EventDispatcher
22
- import com.facebook.react.views.scroll.ReactScrollView
23
23
  import com.google.android.material.bottomsheet.BottomSheetBehavior
24
+ import com.google.android.material.shape.CornerFamily
25
+ import com.google.android.material.shape.MaterialShapeDrawable
26
+ import com.google.android.material.shape.ShapeAppearanceModel
24
27
  import com.swmansion.rnscreens.events.HeaderHeightChangeEvent
25
28
  import com.swmansion.rnscreens.events.SheetDetentChangedEvent
29
+ import com.swmansion.rnscreens.ext.isInsideScrollViewWithRemoveClippedSubviews
30
+ import java.lang.ref.WeakReference
26
31
 
27
32
  @SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated.
28
33
  class Screen(
@@ -32,6 +37,8 @@ class Screen(
32
37
  val fragment: Fragment?
33
38
  get() = fragmentWrapper?.fragment
34
39
 
40
+ var contentWrapper = WeakReference<ScreenContentWrapper>(null)
41
+
35
42
  val sheetBehavior: BottomSheetBehavior<Screen>?
36
43
  get() = (layoutParams as? CoordinatorLayout.LayoutParams)?.behavior as? BottomSheetBehavior<Screen>
37
44
 
@@ -54,10 +61,16 @@ class Screen(
54
61
 
55
62
  // Props for controlling modal presentation
56
63
  var isSheetGrabberVisible: Boolean = false
64
+
65
+ // corner radius must be updated after all props prop updates from a single transaction
66
+ // have been applied, because it depends on the presentation type.
67
+ private var shouldUpdateSheetCornerRadius = false
57
68
  var sheetCornerRadius: Float = 0F
58
69
  set(value) {
59
- field = value
60
- (fragment as? ScreenStackFragment)?.onSheetCornerRadiusChange()
70
+ if (field != value) {
71
+ field = value
72
+ shouldUpdateSheetCornerRadius = true
73
+ }
61
74
  }
62
75
  var sheetExpandsWhenScrolledToEdge: Boolean = true
63
76
 
@@ -118,6 +131,7 @@ class Screen(
118
131
 
119
132
  fun registerLayoutCallbackForWrapper(wrapper: ScreenContentWrapper) {
120
133
  wrapper.delegate = this
134
+ this.contentWrapper = WeakReference(wrapper)
121
135
  }
122
136
 
123
137
  override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
@@ -224,6 +238,9 @@ class Screen(
224
238
  if (activityState == this.activityState) {
225
239
  return
226
240
  }
241
+ if (container is ScreenStack && this.activityState != null && activityState < this.activityState!!) {
242
+ throw IllegalStateException("[RNScreens] activityState can only progress in NativeStack")
243
+ }
227
244
  this.activityState = activityState
228
245
  container?.notifyChildUpdate()
229
246
  }
@@ -383,10 +400,10 @@ class Screen(
383
400
  }
384
401
  if (child is ViewGroup) {
385
402
  // The children are miscounted when there's a FlatList with
386
- // removeCLippedSubviews set to true (default).
403
+ // removeClippedSubviews set to true (default).
387
404
  // We add a simple view for each item in the list to make it work as expected.
388
- // See https://github.com/software-mansion/react-native-screens/issues/2282
389
- if (it is ReactScrollView && it.removeClippedSubviews) {
405
+ // See https://github.com/software-mansion/react-native-screens/pull/2383
406
+ if (child.isInsideScrollViewWithRemoveClippedSubviews()) {
390
407
  for (j in 0 until child.childCount) {
391
408
  child.addView(View(context))
392
409
  }
@@ -420,6 +437,29 @@ class Screen(
420
437
  )
421
438
  }
422
439
 
440
+ internal fun onFinalizePropsUpdate() {
441
+ if (shouldUpdateSheetCornerRadius) {
442
+ shouldUpdateSheetCornerRadius = false
443
+ onSheetCornerRadiusChange()
444
+ }
445
+ }
446
+
447
+ internal fun onSheetCornerRadiusChange() {
448
+ if (stackPresentation !== StackPresentation.FORM_SHEET || background == null) {
449
+ return
450
+ }
451
+ (background as? MaterialShapeDrawable?)?.let {
452
+ val resolvedCornerRadius = PixelUtil.toDIPFromPixel(sheetCornerRadius)
453
+ it.shapeAppearanceModel =
454
+ ShapeAppearanceModel
455
+ .Builder()
456
+ .apply {
457
+ setTopLeftCorner(CornerFamily.ROUNDED, resolvedCornerRadius)
458
+ setTopRightCorner(CornerFamily.ROUNDED, resolvedCornerRadius)
459
+ }.build()
460
+ }
461
+ }
462
+
423
463
  enum class StackPresentation {
424
464
  PUSH,
425
465
  MODAL,
@@ -435,7 +475,6 @@ class Screen(
435
475
  SLIDE_FROM_RIGHT,
436
476
  SLIDE_FROM_LEFT,
437
477
  FADE_FROM_BOTTOM,
438
- IOS,
439
478
  IOS_FROM_RIGHT,
440
479
  IOS_FROM_LEFT,
441
480
  }
@@ -104,7 +104,7 @@ class ScreenStack(
104
104
 
105
105
  for (i in screenWrappers.indices.reversed()) {
106
106
  val screenWrapper = getScreenFragmentWrapperAt(i)
107
- if (!dismissedWrappers.contains(screenWrapper)) {
107
+ if (!dismissedWrappers.contains(screenWrapper) && screenWrapper.screen.activityState !== Screen.ActivityState.INACTIVE) {
108
108
  if (newTop == null) {
109
109
  newTop = screenWrapper
110
110
  } else {
@@ -182,9 +182,16 @@ class ScreenStack(
182
182
  R.anim.rns_no_animation_medium,
183
183
  )
184
184
  StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_fade_from_bottom, R.anim.rns_no_animation_350)
185
- StackAnimation.IOS -> it.setCustomAnimations(R.anim.rns_ios_from_right_foreground_open, R.anim.rns_ios_from_right_background_open)
186
- StackAnimation.IOS_FROM_RIGHT -> it.setCustomAnimations(R.anim.rns_ios_from_right_foreground_open, R.anim.rns_ios_from_right_background_open)
187
- StackAnimation.IOS_FROM_LEFT -> it.setCustomAnimations(R.anim.rns_ios_from_left_foreground_open, R.anim.rns_ios_from_left_background_open)
185
+ StackAnimation.IOS_FROM_RIGHT ->
186
+ it.setCustomAnimations(
187
+ R.anim.rns_ios_from_right_foreground_open,
188
+ R.anim.rns_ios_from_right_background_open,
189
+ )
190
+ StackAnimation.IOS_FROM_LEFT ->
191
+ it.setCustomAnimations(
192
+ R.anim.rns_ios_from_left_foreground_open,
193
+ R.anim.rns_ios_from_left_background_open,
194
+ )
188
195
  }
189
196
  } else {
190
197
  when (stackAnimation) {
@@ -222,9 +229,16 @@ class ScreenStack(
222
229
  R.anim.rns_slide_out_to_bottom,
223
230
  )
224
231
  StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_no_animation_250, R.anim.rns_fade_to_bottom)
225
- StackAnimation.IOS -> it.setCustomAnimations(R.anim.rns_ios_from_right_foreground_close, R.anim.rns_ios_from_right_background_close)
226
- StackAnimation.IOS_FROM_RIGHT -> it.setCustomAnimations(R.anim.rns_ios_from_right_background_close, R.anim.rns_ios_from_right_foreground_close)
227
- StackAnimation.IOS_FROM_LEFT -> it.setCustomAnimations(R.anim.rns_ios_from_left_background_close, R.anim.rns_ios_from_left_foreground_close)
232
+ StackAnimation.IOS_FROM_RIGHT ->
233
+ it.setCustomAnimations(
234
+ R.anim.rns_ios_from_right_background_close,
235
+ R.anim.rns_ios_from_right_foreground_close,
236
+ )
237
+ StackAnimation.IOS_FROM_LEFT ->
238
+ it.setCustomAnimations(
239
+ R.anim.rns_ios_from_left_background_close,
240
+ R.anim.rns_ios_from_left_foreground_close,
241
+ )
228
242
  }
229
243
  }
230
244
  }
@@ -260,7 +274,9 @@ class ScreenStack(
260
274
  break
261
275
  }
262
276
  // detach all screens that should not be visible
263
- if (fragmentWrapper !== newTop && !dismissedWrappers.contains(fragmentWrapper)) {
277
+ if ((fragmentWrapper !== newTop && !dismissedWrappers.contains(fragmentWrapper)) ||
278
+ fragmentWrapper.screen.activityState === Screen.ActivityState.INACTIVE
279
+ ) {
264
280
  it.remove(fragmentWrapper.fragment)
265
281
  }
266
282
  }
@@ -417,7 +433,6 @@ class ScreenStack(
417
433
  Build.VERSION.SDK_INT >= 33 ||
418
434
  fragmentWrapper.screen.stackAnimation === StackAnimation.SLIDE_FROM_BOTTOM ||
419
435
  fragmentWrapper.screen.stackAnimation === StackAnimation.FADE_FROM_BOTTOM ||
420
- fragmentWrapper.screen.stackAnimation === StackAnimation.IOS ||
421
436
  fragmentWrapper.screen.stackAnimation === StackAnimation.IOS_FROM_RIGHT ||
422
437
  fragmentWrapper.screen.stackAnimation === StackAnimation.IOS_FROM_LEFT
423
438
  }
@@ -142,7 +142,11 @@ class ScreenStackFragment :
142
142
  // once it is hidden by user gesture.
143
143
  private val bottomSheetStateCallback =
144
144
  object : BottomSheetCallback() {
145
- private var lastStableState: Int = SheetUtils.sheetStateFromDetentIndex(screen.sheetInitialDetentIndex, screen.sheetDetents.count())
145
+ private var lastStableState: Int =
146
+ SheetUtils.sheetStateFromDetentIndex(
147
+ screen.sheetInitialDetentIndex,
148
+ screen.sheetDetents.count(),
149
+ )
146
150
 
147
151
  override fun onStateChanged(
148
152
  bottomSheet: View,
@@ -150,9 +154,20 @@ class ScreenStackFragment :
150
154
  ) {
151
155
  if (SheetUtils.isStateStable(newState)) {
152
156
  lastStableState = newState
153
- screen.notifySheetDetentChange(SheetUtils.detentIndexFromSheetState(lastStableState, screen.sheetDetents.count()), true)
157
+ screen.notifySheetDetentChange(
158
+ SheetUtils.detentIndexFromSheetState(
159
+ lastStableState,
160
+ screen.sheetDetents.count()
161
+ ), true
162
+ )
154
163
  } else if (newState == BottomSheetBehavior.STATE_DRAGGING) {
155
- screen.notifySheetDetentChange(SheetUtils.detentIndexFromSheetState(lastStableState, screen.sheetDetents.count()), false)
164
+ screen.notifySheetDetentChange(
165
+ SheetUtils.detentIndexFromSheetState(
166
+ lastStableState,
167
+ screen.sheetDetents.count()
168
+ ),
169
+ false,
170
+ )
156
171
  }
157
172
 
158
173
  if (newState == BottomSheetBehavior.STATE_HIDDEN) {
@@ -191,13 +206,7 @@ class ScreenStackFragment :
191
206
  }
192
207
 
193
208
  internal fun onSheetCornerRadiusChange() {
194
- (screen.background as MaterialShapeDrawable).shapeAppearanceModel =
195
- ShapeAppearanceModel
196
- .Builder()
197
- .apply {
198
- setTopLeftCorner(CornerFamily.ROUNDED, screen.sheetCornerRadius)
199
- setTopRightCorner(CornerFamily.ROUNDED, screen.sheetCornerRadius)
200
- }.build()
209
+ screen.onSheetCornerRadiusChange()
201
210
  }
202
211
 
203
212
  override fun onCreateView(
@@ -232,9 +241,7 @@ class ScreenStackFragment :
232
241
 
233
242
  coordinatorLayout.addView(screen.recycle())
234
243
 
235
- if (screen.stackPresentation != Screen.StackPresentation.MODAL &&
236
- screen.stackPresentation != Screen.StackPresentation.FORM_SHEET
237
- ) {
244
+ if (screen.stackPresentation != Screen.StackPresentation.FORM_SHEET) {
238
245
  appBarLayout =
239
246
  context?.let { AppBarLayout(it) }?.apply {
240
247
  // By default AppBarLayout will have a background color set but since we cover the whole layout
@@ -341,13 +348,23 @@ class ScreenStackFragment :
341
348
  return when (keyboardState) {
342
349
  is KeyboardNotVisible -> {
343
350
  when (screen.sheetDetents.count()) {
344
- 1 ->
351
+ 1 -> if (screen.sheetDetents.first() == Screen.SHEET_FIT_TO_CONTENTS) {
352
+ behavior.apply {
353
+ state = BottomSheetBehavior.STATE_EXPANDED
354
+ screen.contentWrapper.get()?.let {
355
+ maxHeight = it.height
356
+ }
357
+ skipCollapsed = true
358
+ isFitToContents = true
359
+ }
360
+ } else {
345
361
  behavior.apply {
346
362
  state = BottomSheetBehavior.STATE_EXPANDED
347
363
  skipCollapsed = true
348
364
  isFitToContents = true
349
365
  maxHeight = (screen.sheetDetents.first() * containerHeight).toInt()
350
366
  }
367
+ }
351
368
 
352
369
  2 ->
353
370
  behavior.apply {
@@ -372,7 +389,8 @@ class ScreenStackFragment :
372
389
  skipCollapsed = false
373
390
  isFitToContents = false
374
391
  peekHeight = (screen.sheetDetents[0] * containerHeight).toInt()
375
- expandedOffset = ((1 - screen.sheetDetents[2]) * containerHeight).toInt()
392
+ expandedOffset =
393
+ ((1 - screen.sheetDetents[2]) * containerHeight).toInt()
376
394
  halfExpandedRatio =
377
395
  (screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat()
378
396
  }
@@ -451,7 +469,8 @@ class ScreenStackFragment :
451
469
  skipCollapsed = false
452
470
  isFitToContents = false
453
471
  peekHeight = (screen.sheetDetents[0] * containerHeight).toInt()
454
- expandedOffset = ((1 - screen.sheetDetents[2]) * containerHeight).toInt()
472
+ expandedOffset =
473
+ ((1 - screen.sheetDetents[2]) * containerHeight).toInt()
455
474
  halfExpandedRatio =
456
475
  (screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat()
457
476
  }
@@ -576,7 +595,8 @@ class ScreenStackFragment :
576
595
  // ) : CoordinatorLayout(context), ReactCompoundViewGroup, ReactHitSlopView {
577
596
  ) : CoordinatorLayout(context),
578
597
  ReactPointerEventsView {
579
- override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets = super.onApplyWindowInsets(insets)
598
+ override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets =
599
+ super.onApplyWindowInsets(insets)
580
600
 
581
601
  private val animationListener: Animation.AnimationListener =
582
602
  object : Animation.AnimationListener {
@@ -8,7 +8,6 @@ import android.text.TextUtils
8
8
  import android.util.TypedValue
9
9
  import android.view.Gravity
10
10
  import android.view.View.OnClickListener
11
- import android.view.ViewGroup
12
11
  import android.view.WindowInsets
13
12
  import android.widget.ImageView
14
13
  import android.widget.TextView
@@ -25,7 +24,7 @@ import com.swmansion.rnscreens.events.HeaderDetachedEvent
25
24
 
26
25
  class ScreenStackHeaderConfig(
27
26
  context: Context,
28
- ) : ViewGroup(context) {
27
+ ) : FabricEnabledHeaderConfigViewGroup(context) {
29
28
  private val configSubviews = ArrayList<ScreenStackHeaderSubview>(3)
30
29
  val toolbar: CustomToolbar
31
30
  var isHeaderHidden = false // named this way to avoid conflict with platform's isHidden
@@ -0,0 +1,25 @@
1
+ package com.swmansion.rnscreens
2
+
3
+ import com.facebook.react.bridge.ReactContext
4
+ import com.facebook.react.uimanager.LayoutShadowNode
5
+ import com.facebook.react.uimanager.Spacing
6
+ import com.swmansion.rnscreens.utils.PaddingBundle
7
+
8
+ internal class ScreenStackHeaderConfigShadowNode(
9
+ private var context: ReactContext,
10
+ ) : LayoutShadowNode() {
11
+ var paddingStart: Float = 0f
12
+ var paddingEnd: Float = 0f
13
+
14
+ override fun setLocalData(data: Any?) {
15
+ if (data is PaddingBundle) {
16
+ paddingStart = data.paddingStart
17
+ paddingEnd = data.paddingEnd
18
+
19
+ setPadding(Spacing.START, paddingStart)
20
+ setPadding(Spacing.END, paddingEnd)
21
+ } else {
22
+ super.setLocalData(data)
23
+ }
24
+ }
25
+ }
@@ -3,8 +3,12 @@ package com.swmansion.rnscreens
3
3
  import android.util.Log
4
4
  import android.view.View
5
5
  import com.facebook.react.bridge.JSApplicationCausedNativeException
6
+ import com.facebook.react.bridge.ReactApplicationContext
6
7
  import com.facebook.react.common.MapBuilder
7
8
  import com.facebook.react.module.annotations.ReactModule
9
+ import com.facebook.react.uimanager.LayoutShadowNode
10
+ import com.facebook.react.uimanager.ReactStylesDiffMap
11
+ import com.facebook.react.uimanager.StateWrapper
8
12
  import com.facebook.react.uimanager.ThemedReactContext
9
13
  import com.facebook.react.uimanager.ViewGroupManager
10
14
  import com.facebook.react.uimanager.ViewManagerDelegate
@@ -29,6 +33,9 @@ class ScreenStackHeaderConfigViewManager :
29
33
 
30
34
  override fun createViewInstance(reactContext: ThemedReactContext) = ScreenStackHeaderConfig(reactContext)
31
35
 
36
+ // This works only on Paper. On Fabric the shadow node is implemented in C++ layer.
37
+ override fun createShadowNodeInstance(context: ReactApplicationContext): LayoutShadowNode = ScreenStackHeaderConfigShadowNode(context)
38
+
32
39
  override fun addView(
33
40
  parent: ScreenStackHeaderConfig,
34
41
  child: View,
@@ -42,6 +49,17 @@ class ScreenStackHeaderConfigViewManager :
42
49
  parent.addConfigSubview(child, index)
43
50
  }
44
51
 
52
+ override fun updateState(
53
+ view: ScreenStackHeaderConfig,
54
+ props: ReactStylesDiffMap?,
55
+ stateWrapper: StateWrapper?,
56
+ ): Any? {
57
+ if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
58
+ view.setStateWrapper(stateWrapper)
59
+ }
60
+ return super.updateState(view, props, stateWrapper)
61
+ }
62
+
45
63
  override fun onDropViewInstance(
46
64
  @Nonnull view: ScreenStackHeaderConfig,
47
65
  ) {
@@ -92,6 +92,12 @@ open class ScreenViewManager :
92
92
  return super.updateState(view, props, stateWrapper)
93
93
  }
94
94
 
95
+ // Called after all props are updated for given view
96
+ override fun onAfterUpdateTransaction(view: Screen) {
97
+ super.onAfterUpdateTransaction(view)
98
+ view.onFinalizePropsUpdate()
99
+ }
100
+
95
101
  @ReactProp(name = "activityState")
96
102
  fun setActivityState(
97
103
  view: Screen,
@@ -142,7 +148,6 @@ open class ScreenViewManager :
142
148
  "slide_from_left" -> Screen.StackAnimation.SLIDE_FROM_LEFT
143
149
  "slide_from_bottom" -> Screen.StackAnimation.SLIDE_FROM_BOTTOM
144
150
  "fade_from_bottom" -> Screen.StackAnimation.FADE_FROM_BOTTOM
145
- "ios" -> Screen.StackAnimation.IOS
146
151
  "ios_from_right" -> Screen.StackAnimation.IOS_FROM_RIGHT
147
152
  "ios_from_left" -> Screen.StackAnimation.IOS_FROM_LEFT
148
153
  else -> throw JSApplicationIllegalArgumentException("Unknown animation type $animation")
@@ -163,6 +163,7 @@ object ScreenWindowTraits {
163
163
  InsetsObserverProxy.registerOnView(decorView)
164
164
  InsetsObserverProxy.addOnApplyWindowInsetsListener(windowInsetsListener)
165
165
  } else {
166
+ InsetsObserverProxy.unregisterOnView(decorView)
166
167
  InsetsObserverProxy.removeOnApplyWindowInsetsListener(windowInsetsListener)
167
168
  }
168
169
  ViewCompat.requestApplyInsets(decorView)
@@ -3,6 +3,9 @@ package com.swmansion.rnscreens.ext
3
3
  import android.graphics.drawable.ColorDrawable
4
4
  import android.view.View
5
5
  import android.view.ViewGroup
6
+ import com.facebook.react.views.scroll.ReactHorizontalScrollView
7
+ import com.facebook.react.views.scroll.ReactScrollView
8
+ import com.swmansion.rnscreens.ScreenStack
6
9
 
7
10
  internal fun View.parentAsView() = this.parent as? View
8
11
 
@@ -30,3 +33,17 @@ internal fun View.maybeBgColor(): Int? {
30
33
  }
31
34
  return null
32
35
  }
36
+
37
+ internal fun View.isInsideScrollViewWithRemoveClippedSubviews(): Boolean {
38
+ if (this is ReactHorizontalScrollView || this is ReactScrollView) {
39
+ return false
40
+ }
41
+ var parentView = this.parent
42
+ while (parentView is ViewGroup && parentView !is ScreenStack) {
43
+ if (parentView is ReactScrollView) {
44
+ return parentView.removeClippedSubviews
45
+ }
46
+ parentView = parentView.parent
47
+ }
48
+ return false
49
+ }
@@ -0,0 +1,8 @@
1
+ package com.swmansion.rnscreens.utils
2
+
3
+ // Used only on Paper together with `setLocalData` mechanism to pass
4
+ // the information on header paddings to shadow node.
5
+ data class PaddingBundle(
6
+ val paddingStart: Float,
7
+ val paddingEnd: Float,
8
+ )
@@ -18,6 +18,8 @@
18
18
  */
19
19
  #include <react/renderer/components/rnscreens/RNSScreenComponentDescriptor.h>
20
20
  #include <react/renderer/components/rnscreens/RNSModalScreenComponentDescriptor.h>
21
+ #include <react/renderer/components/rnscreens/RNSScreenStackHeaderSubviewComponentDescriptor.h>
22
+ #include <react/renderer/components/rnscreens/RNSScreenStackHeaderConfigComponentDescriptor.h>
21
23
 
22
24
  namespace facebook {
23
25
  namespace react {
@@ -0,0 +1,39 @@
1
+ package com.swmansion.rnscreens
2
+
3
+ import android.content.Context
4
+ import android.view.ViewGroup
5
+ import com.facebook.react.bridge.ReactContext
6
+ import com.facebook.react.uimanager.StateWrapper
7
+ import com.facebook.react.uimanager.UIManagerModule
8
+ import com.swmansion.rnscreens.utils.PaddingBundle
9
+ import kotlin.math.abs
10
+
11
+ abstract class FabricEnabledHeaderConfigViewGroup(
12
+ context: Context,
13
+ ) : ViewGroup(context) {
14
+ private var lastPaddingStart = 0
15
+ private var lastPaddingEnd = 0
16
+
17
+ fun setStateWrapper(wrapper: StateWrapper?) = Unit
18
+
19
+ fun updatePaddingsFabric(
20
+ paddingStart: Int,
21
+ paddingEnd: Int,
22
+ ) {
23
+ // Note that on Paper we do not convert these props from px to dip. This is done internally by RN.
24
+ if (abs(lastPaddingStart - paddingStart) < DELTA && abs(lastPaddingEnd - paddingEnd) < DELTA) {
25
+ return
26
+ }
27
+
28
+ lastPaddingStart = paddingStart
29
+ lastPaddingEnd = paddingEnd
30
+
31
+ val reactContext = context as? ReactContext
32
+ val uiManagerModule = reactContext?.getNativeModule(UIManagerModule::class.java)
33
+ uiManagerModule?.setViewLocalData(this.id, PaddingBundle(paddingStart.toFloat(), paddingEnd.toFloat()))
34
+ }
35
+
36
+ companion object {
37
+ private const val DELTA = 0.9
38
+ }
39
+ }
@@ -0,0 +1,44 @@
1
+ #pragma once
2
+
3
+ #ifdef ANDROID
4
+ #include <fbjni/fbjni.h>
5
+ #endif
6
+ #include <react/debug/react_native_assert.h>
7
+ #include <react/renderer/components/rnscreens/Props.h>
8
+ #include <react/renderer/components/rnscreens/utils/RectUtil.h>
9
+ #include <react/renderer/core/ConcreteComponentDescriptor.h>
10
+ #include "RNSScreenStackHeaderConfigShadowNode.h"
11
+
12
+ namespace facebook::react {
13
+
14
+ using namespace rnscreens;
15
+
16
+ class RNSScreenStackHeaderConfigComponentDescriptor final
17
+ : public ConcreteComponentDescriptor<RNSScreenStackHeaderConfigShadowNode> {
18
+ public:
19
+ using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
20
+
21
+ void adopt(ShadowNode &shadowNode) const override {
22
+ react_native_assert(
23
+ dynamic_cast<RNSScreenStackHeaderConfigShadowNode *>(&shadowNode));
24
+ auto &configShadowNode =
25
+ static_cast<RNSScreenStackHeaderConfigShadowNode &>(shadowNode);
26
+
27
+ react_native_assert(
28
+ dynamic_cast<YogaLayoutableShadowNode *>(&configShadowNode));
29
+ auto &layoutableShadowNode =
30
+ dynamic_cast<YogaLayoutableShadowNode &>(configShadowNode);
31
+
32
+ auto state = std::static_pointer_cast<
33
+ const RNSScreenStackHeaderConfigShadowNode::ConcreteState>(
34
+ shadowNode.getState());
35
+ auto stateData = state->getData();
36
+
37
+ layoutableShadowNode.setPadding(
38
+ {stateData.getPaddingStart(), 0, stateData.getPaddingEnd(), 0});
39
+
40
+ ConcreteComponentDescriptor::adopt(shadowNode);
41
+ }
42
+ };
43
+
44
+ } // namespace facebook::react
@@ -0,0 +1,8 @@
1
+ #include "RNSScreenStackHeaderConfigShadowNode.h"
2
+
3
+ namespace facebook::react {
4
+
5
+ extern const char RNSScreenStackHeaderConfigComponentName[] =
6
+ "RNSScreenStackHeaderConfig";
7
+
8
+ }