react-native-screens 4.25.0-beta.1 → 4.25.0-beta.2

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 (96) hide show
  1. package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/StackHeaderAppBarLayout.kt +6 -14
  2. package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/StackHeaderAppBarLayoutBehavior.kt +29 -0
  3. package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/StackHeaderCoordinator.kt +56 -0
  4. package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/config/StackHeaderConfig.kt +11 -0
  5. package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/config/StackHeaderConfigProviding.kt +5 -0
  6. package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/config/StackHeaderConfigViewManager.kt +35 -0
  7. package/android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/subview/StackHeaderSubview.kt +3 -7
  8. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsActionOrigin.kt +26 -0
  9. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsContainer.kt +227 -151
  10. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsNavigationState.kt +60 -0
  11. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/{TabsContainerDelegate.kt → TabsNavigationStateObserver.kt} +19 -14
  12. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsNavigationStateObserverRegistry.kt +88 -0
  13. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/TabsHost.kt +40 -24
  14. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/TabsHostEventEmitter.kt +11 -9
  15. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/TabsHostViewManager.kt +19 -7
  16. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/event/TabsHostTabSelectedEvent.kt +4 -3
  17. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/event/TabsHostTabSelectionPreventedEvent.kt +3 -3
  18. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/event/TabsHostTabSelectionRejectedEvent.kt +11 -10
  19. package/ios/conversion/RNSConversions-Tabs.mm +19 -0
  20. package/ios/conversion/RNSConversions.h +3 -0
  21. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryHelper.mm +34 -5
  22. package/ios/tabs/host/RNSTabBarController.h +152 -99
  23. package/ios/tabs/host/RNSTabBarController.mm +137 -113
  24. package/ios/tabs/host/RNSTabsHostComponentView.h +7 -8
  25. package/ios/tabs/host/RNSTabsHostComponentView.mm +37 -33
  26. package/ios/tabs/host/RNSTabsHostEventEmitter.h +4 -4
  27. package/ios/tabs/host/RNSTabsHostEventEmitter.mm +5 -3
  28. package/ios/tabs/host/RNSTabsNavigationState.h +142 -27
  29. package/ios/tabs/host/RNSTabsNavigationState.mm +35 -2
  30. package/ios/tabs/host/RNSTabsNavigationStateObserverRegistry.h +62 -0
  31. package/ios/tabs/host/RNSTabsNavigationStateObserverRegistry.mm +104 -0
  32. package/lib/commonjs/components/gamma/stack/header/StackHeaderConfig.android.js +46 -1
  33. package/lib/commonjs/components/gamma/stack/header/StackHeaderConfig.android.js.map +1 -1
  34. package/lib/commonjs/components/safe-area/SafeAreaView.web.js +2 -3
  35. package/lib/commonjs/components/safe-area/SafeAreaView.web.js.map +1 -1
  36. package/lib/commonjs/components/tabs/host/TabsHost.android.js +2 -2
  37. package/lib/commonjs/components/tabs/host/TabsHost.android.js.map +1 -1
  38. package/lib/commonjs/components/tabs/host/TabsHost.ios.js +2 -2
  39. package/lib/commonjs/components/tabs/host/TabsHost.ios.js.map +1 -1
  40. package/lib/commonjs/fabric/gamma/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
  41. package/lib/commonjs/flags.js +1 -0
  42. package/lib/commonjs/flags.js.map +1 -1
  43. package/lib/module/components/gamma/stack/header/StackHeaderConfig.android.js +46 -1
  44. package/lib/module/components/gamma/stack/header/StackHeaderConfig.android.js.map +1 -1
  45. package/lib/module/components/safe-area/SafeAreaView.web.js +1 -1
  46. package/lib/module/components/safe-area/SafeAreaView.web.js.map +1 -1
  47. package/lib/module/components/tabs/host/TabsHost.android.js +2 -2
  48. package/lib/module/components/tabs/host/TabsHost.android.js.map +1 -1
  49. package/lib/module/components/tabs/host/TabsHost.ios.js +2 -2
  50. package/lib/module/components/tabs/host/TabsHost.ios.js.map +1 -1
  51. package/lib/module/fabric/gamma/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
  52. package/lib/module/flags.js +1 -0
  53. package/lib/module/flags.js.map +1 -1
  54. package/lib/typescript/components/gamma/split/SplitHost.types.d.ts +1 -1
  55. package/lib/typescript/components/gamma/split/SplitHost.types.d.ts.map +1 -1
  56. package/lib/typescript/components/gamma/stack/header/StackHeaderConfig.android.d.ts.map +1 -1
  57. package/lib/typescript/components/gamma/stack/header/StackHeaderConfig.android.types.d.ts +183 -8
  58. package/lib/typescript/components/gamma/stack/header/StackHeaderConfig.android.types.d.ts.map +1 -1
  59. package/lib/typescript/components/gamma/stack/header/StackHeaderConfig.types.d.ts +37 -0
  60. package/lib/typescript/components/gamma/stack/header/StackHeaderConfig.types.d.ts.map +1 -1
  61. package/lib/typescript/components/gamma/stack/header/android/StackHeaderSubview.android.types.d.ts +1 -1
  62. package/lib/typescript/components/gamma/stack/header/android/StackHeaderSubview.android.types.d.ts.map +1 -1
  63. package/lib/typescript/components/gamma/stack/host/StackHost.types.d.ts +1 -1
  64. package/lib/typescript/components/gamma/stack/host/StackHost.types.d.ts.map +1 -1
  65. package/lib/typescript/components/safe-area/SafeAreaView.web.d.ts +1 -1
  66. package/lib/typescript/components/safe-area/SafeAreaView.web.d.ts.map +1 -1
  67. package/lib/typescript/components/tabs/host/TabsHost.types.d.ts +27 -17
  68. package/lib/typescript/components/tabs/host/TabsHost.types.d.ts.map +1 -1
  69. package/lib/typescript/components/tabs/index.d.ts +1 -1
  70. package/lib/typescript/components/tabs/index.d.ts.map +1 -1
  71. package/lib/typescript/fabric/gamma/stack/StackHeaderConfigAndroidNativeComponent.d.ts +5 -0
  72. package/lib/typescript/fabric/gamma/stack/StackHeaderConfigAndroidNativeComponent.d.ts.map +1 -1
  73. package/lib/typescript/fabric/tabs/TabsHostAndroidNativeComponent.d.ts +4 -4
  74. package/lib/typescript/fabric/tabs/TabsHostAndroidNativeComponent.d.ts.map +1 -1
  75. package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts +4 -4
  76. package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts.map +1 -1
  77. package/lib/typescript/flags.d.ts +1 -0
  78. package/lib/typescript/flags.d.ts.map +1 -1
  79. package/package.json +1 -1
  80. package/src/components/gamma/split/SplitHost.types.ts +1 -1
  81. package/src/components/gamma/stack/header/StackHeaderConfig.android.tsx +72 -2
  82. package/src/components/gamma/stack/header/StackHeaderConfig.android.types.ts +183 -8
  83. package/src/components/gamma/stack/header/StackHeaderConfig.types.ts +37 -0
  84. package/src/components/gamma/stack/header/android/StackHeaderSubview.android.types.ts +1 -1
  85. package/src/components/gamma/stack/host/StackHost.types.ts +1 -1
  86. package/src/components/safe-area/SafeAreaView.web.tsx +1 -1
  87. package/src/components/tabs/host/TabsHost.android.tsx +2 -2
  88. package/src/components/tabs/host/TabsHost.ios.tsx +2 -2
  89. package/src/components/tabs/host/TabsHost.types.ts +27 -17
  90. package/src/components/tabs/index.ts +1 -1
  91. package/src/fabric/gamma/stack/StackHeaderConfigAndroidNativeComponent.ts +6 -0
  92. package/src/fabric/tabs/TabsHostAndroidNativeComponent.ts +4 -4
  93. package/src/fabric/tabs/TabsHostIOSNativeComponent.ts +4 -4
  94. package/src/flags.ts +1 -0
  95. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsContainerOps.kt +0 -7
  96. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsNavState.kt +0 -43
@@ -7,9 +7,6 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
7
7
  import androidx.coordinatorlayout.widget.CoordinatorLayout
8
8
  import com.google.android.material.R
9
9
  import com.google.android.material.appbar.AppBarLayout
10
- import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
11
- import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL
12
- import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
13
10
  import com.google.android.material.appbar.CollapsingToolbarLayout
14
11
  import com.google.android.material.appbar.MaterialToolbar
15
12
  import com.swmansion.rnscreens.gamma.stack.header.config.StackHeaderType
@@ -21,7 +18,10 @@ internal sealed class StackHeaderAppBarLayout(
21
18
  abstract val toolbar: MaterialToolbar
22
19
 
23
20
  init {
24
- layoutParams = CoordinatorLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
21
+ layoutParams =
22
+ CoordinatorLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT).apply {
23
+ behavior = StackHeaderAppBarLayoutBehavior()
24
+ }
25
25
 
26
26
  // TODO: this should be exposed in the future via prop. Also, it might not work correctly
27
27
  // until we set liftOnScrollView manually. Also, we should disable it in transparent
@@ -39,11 +39,7 @@ internal sealed class StackHeaderAppBarLayout(
39
39
  override val toolbar =
40
40
  MaterialToolbar(context).apply {
41
41
  elevation = 0f
42
- layoutParams =
43
- LayoutParams(MATCH_PARENT, WRAP_CONTENT).apply {
44
- // TODO: debug only for small header, must be moved to config
45
- scrollFlags = SCROLL_FLAG_NO_SCROLL
46
- }
42
+ layoutParams = LayoutParams(MATCH_PARENT, WRAP_CONTENT)
47
43
  }
48
44
 
49
45
  init {
@@ -80,15 +76,11 @@ internal sealed class StackHeaderAppBarLayout(
80
76
  else -> error("[RNScreens] Invalid header mode.")
81
77
  }
82
78
  CollapsingToolbarLayout(context, null, styleAttr).apply {
83
- fitsSystemWindows = false
84
79
  layoutParams =
85
80
  LayoutParams(
86
81
  MATCH_PARENT,
87
82
  resolveDimensionAttr(context, sizeAttr),
88
- ).apply {
89
- // TODO: debug only for medium/large header, must be moved to config
90
- scrollFlags = SCROLL_FLAG_SCROLL or SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
91
- }
83
+ )
92
84
  addView(toolbar)
93
85
  }
94
86
  }
@@ -0,0 +1,29 @@
1
+ package com.swmansion.rnscreens.gamma.stack.header
2
+
3
+ import android.view.View
4
+ import androidx.coordinatorlayout.widget.CoordinatorLayout
5
+ import com.google.android.material.appbar.AppBarLayout
6
+
7
+ /**
8
+ * Pre-clamps nested down-scroll so the AppBar offset cannot exceed 0 (its natural rest).
9
+ *
10
+ * Without this, AppBarLayout has a bug where SCROLL | ENTER_ALWAYS | EXIT_UNTIL_COLLAPSED
11
+ * combined with non-zero top window insets sets max = +topInset in onNestedPreScroll, allowing
12
+ * pull-down overshoot that snap-back animates away on release. The downNestedPreScrollRange
13
+ * inset clamp in AppBarLayout only fires when topInset > minimumHeight, which is rarely true
14
+ * (typical actionBarSize ~56dp vs status bar ~24dp).
15
+ */
16
+ internal class StackHeaderAppBarLayoutBehavior : AppBarLayout.Behavior() {
17
+ override fun onNestedPreScroll(
18
+ coordinatorLayout: CoordinatorLayout,
19
+ child: AppBarLayout,
20
+ target: View,
21
+ dx: Int,
22
+ dy: Int,
23
+ consumed: IntArray,
24
+ type: Int,
25
+ ) {
26
+ val adjustedDy = if (dy < 0) maxOf(dy, topAndBottomOffset) else dy
27
+ super.onNestedPreScroll(coordinatorLayout, child, target, dx, adjustedDy, consumed, type)
28
+ }
29
+ }
@@ -16,6 +16,11 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
16
16
  import androidx.core.widget.TextViewCompat
17
17
  import com.google.android.material.R
18
18
  import com.google.android.material.appbar.AppBarLayout
19
+ import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
20
+ import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED
21
+ import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
22
+ import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
23
+ import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP
19
24
  import com.google.android.material.appbar.CollapsingToolbarLayout
20
25
  import com.google.android.material.appbar.MaterialToolbar
21
26
  import com.swmansion.rnscreens.ext.detachFromCurrentParent
@@ -56,6 +61,8 @@ internal class StackHeaderCoordinator(
56
61
  private var lastBackButtonTintColor: Int? = null
57
62
  private var lastBackButtonIcon: Drawable? = null
58
63
 
64
+ private var lastScrollFlags: Int? = null
65
+
59
66
  // For small header, we need to use custom title view in order to
60
67
  // render a subview to the leading side of the title.
61
68
  private var managedTitleView: AppCompatTextView? = null
@@ -154,6 +161,7 @@ internal class StackHeaderCoordinator(
154
161
  lastBackButtonVisible = null
155
162
  lastBackButtonTintColor = null
156
163
  lastBackButtonIcon = null
164
+ lastScrollFlags = null
157
165
  clearCachedRebuildTriggers()
158
166
  }
159
167
 
@@ -323,6 +331,7 @@ internal class StackHeaderCoordinator(
323
331
  }
324
332
  }
325
333
 
334
+ applyScrollFlags(appBar, config)
326
335
  applyBackButton(appBar.toolbar, config)
327
336
  }
328
337
 
@@ -336,6 +345,53 @@ internal class StackHeaderCoordinator(
336
345
  }
337
346
  }
338
347
 
348
+ private fun applyScrollFlags(
349
+ appBar: StackHeaderAppBarLayout,
350
+ config: StackHeaderConfigProviding,
351
+ ) {
352
+ val desired = computeScrollFlags(config)
353
+
354
+ if (desired == lastScrollFlags) return
355
+ lastScrollFlags = desired
356
+
357
+ warnInvalidScrollFlagCombinations(config)
358
+
359
+ val target: View =
360
+ when (appBar) {
361
+ is StackHeaderAppBarLayout.Small -> appBar.toolbar
362
+ is StackHeaderAppBarLayout.Collapsing -> appBar.collapsingToolbarLayout
363
+ }
364
+ val params = target.layoutParams as AppBarLayout.LayoutParams
365
+ params.scrollFlags = desired
366
+ target.layoutParams = params
367
+ // Snap back to expanded so the visible state matches the new flags.
368
+ appBar.setExpanded(true, false)
369
+ }
370
+
371
+ private fun computeScrollFlags(config: StackHeaderConfigProviding): Int {
372
+ var flags = 0
373
+ if (config.scrollFlagScroll) flags = flags or SCROLL_FLAG_SCROLL
374
+ if (config.scrollFlagEnterAlways) flags = flags or SCROLL_FLAG_ENTER_ALWAYS
375
+ if (config.scrollFlagEnterAlwaysCollapsed) flags = flags or SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED
376
+ if (config.scrollFlagExitUntilCollapsed) flags = flags or SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
377
+ if (config.scrollFlagSnap) flags = flags or SCROLL_FLAG_SNAP
378
+ return flags
379
+ }
380
+
381
+ private fun warnInvalidScrollFlagCombinations(config: StackHeaderConfigProviding) {
382
+ val anyDependentFlag =
383
+ config.scrollFlagEnterAlways ||
384
+ config.scrollFlagEnterAlwaysCollapsed ||
385
+ config.scrollFlagExitUntilCollapsed ||
386
+ config.scrollFlagSnap
387
+ if (anyDependentFlag && !config.scrollFlagScroll) {
388
+ Log.e(TAG, "[RNScreens] scrollFlag* requires scrollFlagScroll to take effect.")
389
+ }
390
+ if (config.scrollFlagEnterAlwaysCollapsed && !config.scrollFlagEnterAlways) {
391
+ Log.e(TAG, "[RNScreens] scrollFlagEnterAlwaysCollapsed requires scrollFlagEnterAlways to take effect.")
392
+ }
393
+ }
394
+
339
395
  // endregion
340
396
 
341
397
  // region Back button
@@ -34,6 +34,17 @@ class StackHeaderConfig(
34
34
  override var backButtonIcon: Drawable? = null
35
35
  internal set
36
36
 
37
+ override var scrollFlagScroll: Boolean = false
38
+ internal set
39
+ override var scrollFlagEnterAlways: Boolean = false
40
+ internal set
41
+ override var scrollFlagEnterAlwaysCollapsed: Boolean = false
42
+ internal set
43
+ override var scrollFlagExitUntilCollapsed: Boolean = false
44
+ internal set
45
+ override var scrollFlagSnap: Boolean = false
46
+ internal set
47
+
37
48
  // Staging fields for back button icon resolution.
38
49
  // Both props may arrive in any order within a single update batch.
39
50
  // Resolution happens in resolveBackButtonIconIfNeeded(), called from onAfterUpdateTransaction.
@@ -11,6 +11,11 @@ interface StackHeaderConfigProviding {
11
11
  val backButtonHidden: Boolean
12
12
  val backButtonTintColor: Int?
13
13
  val backButtonIcon: Drawable?
14
+ val scrollFlagScroll: Boolean
15
+ val scrollFlagEnterAlways: Boolean
16
+ val scrollFlagEnterAlwaysCollapsed: Boolean
17
+ val scrollFlagExitUntilCollapsed: Boolean
18
+ val scrollFlagSnap: Boolean
14
19
  val leadingSubview: StackHeaderSubviewProviding?
15
20
  val centerSubview: StackHeaderSubviewProviding?
16
21
  val trailingSubview: StackHeaderSubviewProviding?
@@ -151,6 +151,41 @@ open class StackHeaderConfigViewManager :
151
151
  view.backButtonImageIconUri = value?.getString("uri")
152
152
  }
153
153
 
154
+ override fun setScrollFlagScroll(
155
+ view: StackHeaderConfig,
156
+ value: Boolean,
157
+ ) {
158
+ view.scrollFlagScroll = value
159
+ }
160
+
161
+ override fun setScrollFlagEnterAlways(
162
+ view: StackHeaderConfig,
163
+ value: Boolean,
164
+ ) {
165
+ view.scrollFlagEnterAlways = value
166
+ }
167
+
168
+ override fun setScrollFlagEnterAlwaysCollapsed(
169
+ view: StackHeaderConfig,
170
+ value: Boolean,
171
+ ) {
172
+ view.scrollFlagEnterAlwaysCollapsed = value
173
+ }
174
+
175
+ override fun setScrollFlagExitUntilCollapsed(
176
+ view: StackHeaderConfig,
177
+ value: Boolean,
178
+ ) {
179
+ view.scrollFlagExitUntilCollapsed = value
180
+ }
181
+
182
+ override fun setScrollFlagSnap(
183
+ view: StackHeaderConfig,
184
+ value: Boolean,
185
+ ) {
186
+ view.scrollFlagSnap = value
187
+ }
188
+
154
189
  companion object {
155
190
  const val REACT_CLASS = "RNSStackHeaderConfigAndroid"
156
191
  }
@@ -68,13 +68,9 @@ class StackHeaderSubview(
68
68
  }
69
69
  }
70
70
 
71
- if (yogaWidth > 0 && yogaHeight > 0) {
72
- setMeasuredDimension(yogaWidth, yogaHeight)
73
- if (invalidated && !isInLayout) {
74
- requestLayout()
75
- }
76
- } else {
77
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
71
+ setMeasuredDimension(yogaWidth, yogaHeight)
72
+ if (invalidated && !isInLayout) {
73
+ requestLayout()
78
74
  }
79
75
  }
80
76
 
@@ -0,0 +1,26 @@
1
+ package com.swmansion.rnscreens.gamma.tabs.container
2
+
3
+ /**
4
+ * Origin (actor) that requested a tab transition. Mirrors the public `actionOrigin` event field.
5
+ *
6
+ * - [USER] — direct native UI interaction (tab bar tap).
7
+ * - [PROGRAMMATIC_JS] — JS-initiated request delivered via the `navStateRequest` prop.
8
+ * - [PROGRAMMATIC_NATIVE] — request initiated from the native side by a downstream library
9
+ * integrating directly against [TabsContainer] (not produced by this library itself).
10
+ *
11
+ * The `implicit` origin defined on the public TS API is iOS-only at the moment;
12
+ * Android does not currently produce it.
13
+ */
14
+ enum class TabsActionOrigin {
15
+ USER,
16
+ PROGRAMMATIC_JS,
17
+ PROGRAMMATIC_NATIVE,
18
+ ;
19
+
20
+ override fun toString(): String =
21
+ when (this) {
22
+ USER -> "user"
23
+ PROGRAMMATIC_JS -> "programmatic-js"
24
+ PROGRAMMATIC_NATIVE -> "programmatic-native"
25
+ }
26
+ }