react-native-screens 4.0.0-beta.0 → 4.0.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 (174) hide show
  1. package/README.md +1 -1
  2. package/android/build.gradle +26 -6
  3. package/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +4 -3
  4. package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +40 -1
  5. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +18 -34
  6. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +9 -3
  7. package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +2 -1
  8. package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +1 -1
  9. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderHeightChangeEvent.kt +3 -3
  10. package/android/src/main/res/base/anim/rns_ios_from_left_background_close.xml +5 -0
  11. package/android/src/main/res/base/anim/{rns_slide_out_to_left_ios.xml → rns_ios_from_left_background_open.xml} +1 -1
  12. package/android/src/main/res/base/anim/rns_ios_from_left_foreground_close.xml +6 -0
  13. package/android/src/main/res/base/anim/rns_ios_from_left_foreground_open.xml +6 -0
  14. package/android/src/main/res/base/anim/rns_ios_from_right_background_open.xml +5 -0
  15. package/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +1 -1
  16. package/common/cpp/react/renderer/components/rnscreens/RNSScreenComponentDescriptor.h +5 -99
  17. package/common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.cpp +101 -0
  18. package/common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.h +2 -0
  19. package/ios/RNSConvert.mm +4 -1
  20. package/ios/RNSFullWindowOverlay.mm +6 -0
  21. package/ios/RNSModalScreen.mm +7 -0
  22. package/ios/RNSScreen.mm +9 -0
  23. package/ios/RNSScreenContainer.mm +6 -0
  24. package/ios/RNSScreenContentWrapper.mm +6 -0
  25. package/ios/RNSScreenFooter.mm +6 -0
  26. package/ios/RNSScreenNavigationContainer.mm +7 -0
  27. package/ios/RNSScreenStack.mm +7 -7
  28. package/ios/RNSScreenStackHeaderConfig.mm +10 -0
  29. package/ios/RNSScreenStackHeaderSubview.mm +6 -3
  30. package/ios/RNSSearchBar.mm +7 -0
  31. package/lib/commonjs/TransitionProgressContext.js +1 -0
  32. package/lib/commonjs/TransitionProgressContext.js.map +1 -1
  33. package/lib/commonjs/components/Screen.js +58 -3
  34. package/lib/commonjs/components/Screen.js.map +1 -1
  35. package/lib/commonjs/components/Screen.web.js +2 -0
  36. package/lib/commonjs/components/Screen.web.js.map +1 -1
  37. package/lib/commonjs/components/ScreenContainer.js +1 -0
  38. package/lib/commonjs/components/ScreenContainer.js.map +1 -1
  39. package/lib/commonjs/components/ScreenContentWrapper.web.js +10 -0
  40. package/lib/commonjs/components/ScreenContentWrapper.web.js.map +1 -0
  41. package/lib/commonjs/components/ScreenStack.js +1 -0
  42. package/lib/commonjs/components/ScreenStack.js.map +1 -1
  43. package/lib/commonjs/components/ScreenStackHeaderConfig.js +1 -0
  44. package/lib/commonjs/components/ScreenStackHeaderConfig.js.map +1 -1
  45. package/lib/commonjs/components/SearchBar.js +1 -0
  46. package/lib/commonjs/components/SearchBar.js.map +1 -1
  47. package/lib/commonjs/core.js +1 -0
  48. package/lib/commonjs/core.js.map +1 -1
  49. package/lib/commonjs/fabric/FullWindowOverlayNativeComponent.js +1 -0
  50. package/lib/commonjs/fabric/FullWindowOverlayNativeComponent.js.map +1 -1
  51. package/lib/commonjs/fabric/ModalScreenNativeComponent.js +1 -0
  52. package/lib/commonjs/fabric/ModalScreenNativeComponent.js.map +1 -1
  53. package/lib/commonjs/fabric/NativeScreensModule.js +2 -1
  54. package/lib/commonjs/fabric/NativeScreensModule.js.map +1 -1
  55. package/lib/commonjs/fabric/ScreenContainerNativeComponent.js +1 -0
  56. package/lib/commonjs/fabric/ScreenContainerNativeComponent.js.map +1 -1
  57. package/lib/commonjs/fabric/ScreenNativeComponent.js +1 -0
  58. package/lib/commonjs/fabric/ScreenNativeComponent.js.map +1 -1
  59. package/lib/commonjs/fabric/ScreenNavigationContainerNativeComponent.js +1 -0
  60. package/lib/commonjs/fabric/ScreenNavigationContainerNativeComponent.js.map +1 -1
  61. package/lib/commonjs/fabric/ScreenStackHeaderConfigNativeComponent.js +1 -0
  62. package/lib/commonjs/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  63. package/lib/commonjs/fabric/ScreenStackHeaderSubviewNativeComponent.js +1 -0
  64. package/lib/commonjs/fabric/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
  65. package/lib/commonjs/fabric/ScreenStackNativeComponent.js +1 -0
  66. package/lib/commonjs/fabric/ScreenStackNativeComponent.js.map +1 -1
  67. package/lib/commonjs/fabric/SearchBarNativeComponent.js +2 -2
  68. package/lib/commonjs/fabric/SearchBarNativeComponent.js.map +1 -1
  69. package/lib/commonjs/native-stack/contexts/GHContext.js +1 -0
  70. package/lib/commonjs/native-stack/contexts/GHContext.js.map +1 -1
  71. package/lib/commonjs/native-stack/views/NativeStackView.js +4 -4
  72. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  73. package/lib/module/TransitionProgressContext.js +2 -0
  74. package/lib/module/TransitionProgressContext.js.map +1 -1
  75. package/lib/module/components/Screen.js +59 -3
  76. package/lib/module/components/Screen.js.map +1 -1
  77. package/lib/module/components/Screen.web.js +3 -0
  78. package/lib/module/components/Screen.web.js.map +1 -1
  79. package/lib/module/components/ScreenContainer.js +2 -0
  80. package/lib/module/components/ScreenContainer.js.map +1 -1
  81. package/lib/module/components/ScreenContentWrapper.web.js +4 -0
  82. package/lib/module/components/ScreenContentWrapper.web.js.map +1 -0
  83. package/lib/module/components/ScreenStack.js +2 -0
  84. package/lib/module/components/ScreenStack.js.map +1 -1
  85. package/lib/module/components/ScreenStackHeaderConfig.js +2 -0
  86. package/lib/module/components/ScreenStackHeaderConfig.js.map +1 -1
  87. package/lib/module/components/SearchBar.js +2 -0
  88. package/lib/module/components/SearchBar.js.map +1 -1
  89. package/lib/module/core.js +2 -0
  90. package/lib/module/core.js.map +1 -1
  91. package/lib/module/fabric/FullWindowOverlayNativeComponent.js +2 -0
  92. package/lib/module/fabric/FullWindowOverlayNativeComponent.js.map +1 -1
  93. package/lib/module/fabric/ModalScreenNativeComponent.js +2 -0
  94. package/lib/module/fabric/ModalScreenNativeComponent.js.map +1 -1
  95. package/lib/module/fabric/NativeScreensModule.js +2 -1
  96. package/lib/module/fabric/NativeScreensModule.js.map +1 -1
  97. package/lib/module/fabric/ScreenContainerNativeComponent.js +2 -0
  98. package/lib/module/fabric/ScreenContainerNativeComponent.js.map +1 -1
  99. package/lib/module/fabric/ScreenNativeComponent.js +2 -0
  100. package/lib/module/fabric/ScreenNativeComponent.js.map +1 -1
  101. package/lib/module/fabric/ScreenNavigationContainerNativeComponent.js +2 -0
  102. package/lib/module/fabric/ScreenNavigationContainerNativeComponent.js.map +1 -1
  103. package/lib/module/fabric/ScreenStackHeaderConfigNativeComponent.js +2 -0
  104. package/lib/module/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  105. package/lib/module/fabric/ScreenStackHeaderSubviewNativeComponent.js +2 -0
  106. package/lib/module/fabric/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
  107. package/lib/module/fabric/ScreenStackNativeComponent.js +2 -0
  108. package/lib/module/fabric/ScreenStackNativeComponent.js.map +1 -1
  109. package/lib/module/fabric/SearchBarNativeComponent.js +2 -0
  110. package/lib/module/fabric/SearchBarNativeComponent.js.map +1 -1
  111. package/lib/module/index.js +6 -6
  112. package/lib/module/native-stack/contexts/GHContext.js +2 -0
  113. package/lib/module/native-stack/contexts/GHContext.js.map +1 -1
  114. package/lib/module/native-stack/views/NativeStackView.js +4 -4
  115. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  116. package/lib/typescript/TransitionProgressContext.d.ts.map +1 -1
  117. package/lib/typescript/components/Screen.d.ts.map +1 -1
  118. package/lib/typescript/components/Screen.web.d.ts.map +1 -1
  119. package/lib/typescript/components/ScreenContainer.d.ts.map +1 -1
  120. package/lib/typescript/components/ScreenContentWrapper.web.d.ts +4 -0
  121. package/lib/typescript/components/ScreenContentWrapper.web.d.ts.map +1 -0
  122. package/lib/typescript/components/ScreenStack.d.ts.map +1 -1
  123. package/lib/typescript/components/ScreenStackHeaderConfig.d.ts.map +1 -1
  124. package/lib/typescript/components/SearchBar.d.ts.map +1 -1
  125. package/lib/typescript/core.d.ts.map +1 -1
  126. package/lib/typescript/fabric/FullWindowOverlayNativeComponent.d.ts.map +1 -1
  127. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts.map +1 -1
  128. package/lib/typescript/fabric/NativeScreensModule.d.ts.map +1 -1
  129. package/lib/typescript/fabric/ScreenContainerNativeComponent.d.ts.map +1 -1
  130. package/lib/typescript/fabric/ScreenNativeComponent.d.ts +1 -1
  131. package/lib/typescript/fabric/ScreenNativeComponent.d.ts.map +1 -1
  132. package/lib/typescript/fabric/ScreenNavigationContainerNativeComponent.d.ts.map +1 -1
  133. package/lib/typescript/fabric/ScreenStackHeaderConfigNativeComponent.d.ts.map +1 -1
  134. package/lib/typescript/fabric/ScreenStackHeaderSubviewNativeComponent.d.ts.map +1 -1
  135. package/lib/typescript/fabric/ScreenStackNativeComponent.d.ts.map +1 -1
  136. package/lib/typescript/fabric/SearchBarNativeComponent.d.ts.map +1 -1
  137. package/lib/typescript/index.d.ts +18 -0
  138. package/lib/typescript/index.d.ts.map +1 -1
  139. package/lib/typescript/native-stack/contexts/GHContext.d.ts.map +1 -1
  140. package/lib/typescript/native-stack/types.d.ts +13 -3
  141. package/lib/typescript/native-stack/types.d.ts.map +1 -1
  142. package/lib/typescript/native-stack/views/NativeStackView.d.ts.map +1 -1
  143. package/lib/typescript/types.d.ts +34 -6
  144. package/lib/typescript/types.d.ts.map +1 -1
  145. package/native-stack/README.md +28 -4
  146. package/package.json +1 -1
  147. package/src/TransitionProgressContext.tsx +2 -0
  148. package/src/components/Screen.tsx +71 -3
  149. package/src/components/Screen.web.tsx +3 -0
  150. package/src/components/ScreenContainer.tsx +2 -0
  151. package/src/components/ScreenContentWrapper.web.tsx +4 -0
  152. package/src/components/ScreenStack.tsx +2 -0
  153. package/src/components/ScreenStackHeaderConfig.tsx +2 -0
  154. package/src/components/SearchBar.tsx +2 -0
  155. package/src/core.ts +2 -0
  156. package/src/fabric/FullWindowOverlayNativeComponent.ts +2 -0
  157. package/src/fabric/ModalScreenNativeComponent.ts +2 -0
  158. package/src/fabric/NativeScreensModule.ts +2 -0
  159. package/src/fabric/ScreenContainerNativeComponent.ts +2 -0
  160. package/src/fabric/ScreenNativeComponent.ts +5 -1
  161. package/src/fabric/ScreenNavigationContainerNativeComponent.ts +2 -0
  162. package/src/fabric/ScreenStackHeaderConfigNativeComponent.ts +2 -0
  163. package/src/fabric/ScreenStackHeaderSubviewNativeComponent.ts +2 -0
  164. package/src/fabric/ScreenStackNativeComponent.ts +2 -0
  165. package/src/fabric/SearchBarNativeComponent.ts +2 -0
  166. package/src/index.tsx +6 -6
  167. package/src/native-stack/contexts/GHContext.tsx +2 -0
  168. package/src/native-stack/types.tsx +13 -3
  169. package/src/native-stack/views/NativeStackView.tsx +8 -7
  170. package/src/types.tsx +42 -6
  171. package/windows/RNScreens/Screen.h +3 -1
  172. /package/android/src/main/res/base/anim/{rns_slide_in_from_left_ios.xml → rns_ios_from_right_background_close.xml} +0 -0
  173. /package/android/src/main/res/base/anim/{rns_slide_out_to_right_ios.xml → rns_ios_from_right_foreground_close.xml} +0 -0
  174. /package/android/src/main/res/base/anim/{rns_slide_in_from_right_ios.xml → rns_ios_from_right_foreground_open.xml} +0 -0
package/README.md CHANGED
@@ -188,7 +188,7 @@ To take advantage of the native stack navigator primitive for React Navigation t
188
188
 
189
189
  ## `FullWindowOverlay`
190
190
 
191
- Native `iOS` component for rendering views straight under the `Window`. Based on `RCTPerfMonitor`. You should treat it as a wrapper, providing full-screen, transparent view which receives no props and should ideally render one child `View`, being the root of its view hierarchy. For the example usage, see https://github.com/software-mansion/react-native-screens/blob/main/TestsExample/src/Test1096.tsx
191
+ Native `iOS` component for rendering views straight under the `Window`. Based on `RCTPerfMonitor`. You should treat it as a wrapper, providing full-screen, transparent view which receives no props and should ideally render one child `View`, being the root of its view hierarchy. For the example usage, see https://github.com/software-mansion/react-native-screens/blob/main/apps/src/tests/Test1096.tsx
192
192
 
193
193
  ## Interop with [react-native-navigation](https://github.com/wix/react-native-navigation)
194
194
 
@@ -9,7 +9,11 @@ buildscript {
9
9
  rnsDefaultKotlinVersion = '1.8.0'
10
10
  }
11
11
  ext.safeExtGet = {prop, fallback ->
12
- rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
12
+ def props = (prop instanceof String) ? [prop] : prop
13
+ def result = props.find { key ->
14
+ return rootProject.ext.has(key)
15
+ }
16
+ return result ? rootProject.ext.get(result) : fallback
13
17
  }
14
18
  repositories {
15
19
  google()
@@ -77,8 +81,8 @@ android {
77
81
  }
78
82
 
79
83
  defaultConfig {
80
- minSdkVersion safeExtGet('minSdkVersion', rnsDefaultMinSdkVersion)
81
- targetSdkVersion safeExtGet('targetSdkVersion', rnsDefaultTargetSdkVersion)
84
+ minSdkVersion safeExtGet(['minSdkVersion', 'minSdk'], rnsDefaultMinSdkVersion)
85
+ targetSdkVersion safeExtGet(['targetSdkVersion', 'targetSdk'], rnsDefaultTargetSdkVersion)
82
86
  versionCode 1
83
87
  versionName "1.0"
84
88
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", IS_NEW_ARCHITECTURE_ENABLED.toString()
@@ -136,7 +140,7 @@ android {
136
140
  }
137
141
  }
138
142
  res {
139
- if (safeExtGet('compileSdkVersion', rnsDefaultCompileSdkVersion) >= 33) {
143
+ if (safeExtGet(['compileSdkVersion', 'compileSdk'], rnsDefaultCompileSdkVersion) >= 33) {
140
144
  srcDirs = ["${androidResDir}/base", "${androidResDir}/v33"]
141
145
  } else {
142
146
  srcDirs = ["${androidResDir}/base"]
@@ -148,10 +152,26 @@ android {
148
152
  repositories {
149
153
  maven {
150
154
  // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
151
- // Matches the RN Hello World template
155
+
156
+ // First look for the standard location of react-native, as in RN Hello World template
152
157
  // https://github.com/facebook/react-native/blob/1e8f3b11027fe0a7514b4fc97d0798d3c64bc895/local-cli/templates/HelloWorld/android/build.gradle#L21
153
- url "$projectDir/../node_modules/react-native/android"
158
+ // TODO(kkafar): Note, that in latest template app https://github.com/react-native-community/template/blob/0f4745b7a9d84232aeedec2def8d75ab9b050d11/template/android/build.gradle
159
+ // this is not specified at all.
160
+ File standardRnAndroidDirLocation = file("$rootDir/../node_modules/react-native/android")
161
+ if (standardRnAndroidDirLocation.exists()) {
162
+ url standardRnAndroidDirLocation
163
+ } else {
164
+ // We're in non standard setup - try to use node resolver to locate the react-native package.
165
+ File reactNativePackage = file(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim())
166
+ def rnAndroidDirLocation = "$reactNativePackage.parentFile/android"
167
+ if (reactNativePackage.exists()) {
168
+ url rnAndroidDirLocation
169
+ } else {
170
+ println "[RNScreens] Failed to resolve react-native directory. Attempted locations: ${standardRnAndroidDirLocation}, ${rnAndroidDirLocation}"
171
+ }
172
+ }
154
173
  }
174
+
155
175
  mavenCentral()
156
176
  mavenLocal()
157
177
  google()
@@ -24,7 +24,7 @@ abstract class FabricEnabledViewGroup(
24
24
  protected fun updateScreenSizeFabric(
25
25
  width: Int,
26
26
  height: Int,
27
- headerHeight: Double,
27
+ headerHeight: Int,
28
28
  ) {
29
29
  updateState(width, height, headerHeight)
30
30
  }
@@ -33,10 +33,11 @@ abstract class FabricEnabledViewGroup(
33
33
  fun updateState(
34
34
  width: Int,
35
35
  height: Int,
36
- headerHeight: Double,
36
+ headerHeight: Int,
37
37
  ) {
38
38
  val realWidth: Float = PixelUtil.toDIPFromPixel(width.toFloat())
39
39
  val realHeight: Float = PixelUtil.toDIPFromPixel(height.toFloat())
40
+ val realHeaderHeight: Float = PixelUtil.toDIPFromPixel(headerHeight.toFloat())
40
41
 
41
42
  // Check incoming state values. If they're already the correct value, return early to prevent
42
43
  // infinite UpdateState/SetState loop.
@@ -54,7 +55,7 @@ abstract class FabricEnabledViewGroup(
54
55
  putDouble("frameWidth", realWidth.toDouble())
55
56
  putDouble("frameHeight", realHeight.toDouble())
56
57
  putDouble("contentOffsetX", 0.0)
57
- putDouble("contentOffsetY", headerHeight)
58
+ putDouble("contentOffsetY", realHeaderHeight.toDouble())
58
59
  }
59
60
  mStateWrapper?.updateState(map)
60
61
  }
@@ -2,11 +2,50 @@ package com.swmansion.rnscreens
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
+ import android.os.Build
5
6
  import androidx.appcompat.widget.Toolbar
7
+ import com.facebook.react.modules.core.ChoreographerCompat
8
+ import com.facebook.react.modules.core.ReactChoreographer
6
9
 
7
10
  // This class is used to store config closer to search bar
8
11
  @SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated.
9
12
  open class CustomToolbar(
10
13
  context: Context,
11
14
  val config: ScreenStackHeaderConfig,
12
- ) : Toolbar(context)
15
+ ) : Toolbar(context) {
16
+ private var isLayoutEnqueued = false
17
+ private val layoutCallback: ChoreographerCompat.FrameCallback =
18
+ object : ChoreographerCompat.FrameCallback() {
19
+ override fun doFrame(frameTimeNanos: Long) {
20
+ isLayoutEnqueued = false
21
+ measure(
22
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
23
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
24
+ )
25
+ layout(left, top, right, bottom)
26
+ }
27
+ }
28
+
29
+ override fun requestLayout() {
30
+ super.requestLayout()
31
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
32
+ // Below Android API 29, layout is not being requested when subviews are being added to the layout,
33
+ // leading to having their subviews in position 0,0 of the toolbar (as Android don't calculate
34
+ // the position of each subview, even if Yoga has correctly set their width and height).
35
+ // This is mostly the issue, when windowSoftInputMode is set to adjustPan in AndroidManifest.
36
+ // Thus, we're manually calling the layout **after** the current layout.
37
+ @Suppress("SENSELESS_COMPARISON") // mLayoutCallback can be null here since this method can be called in init
38
+ if (!isLayoutEnqueued && layoutCallback != null) {
39
+ isLayoutEnqueued = true
40
+ // we use NATIVE_ANIMATED_MODULE choreographer queue because it allows us to catch the current
41
+ // looper loop instead of enqueueing the update in the next loop causing a one frame delay.
42
+ ReactChoreographer
43
+ .getInstance()
44
+ .postFrameCallback(
45
+ ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE,
46
+ layoutCallback,
47
+ )
48
+ }
49
+ }
50
+ }
51
+ }
@@ -5,20 +5,21 @@ import android.content.pm.ActivityInfo
5
5
  import android.graphics.Paint
6
6
  import android.os.Parcelable
7
7
  import android.util.SparseArray
8
- import android.util.TypedValue
9
8
  import android.view.View
10
9
  import android.view.ViewGroup
11
10
  import android.view.WindowManager
12
11
  import android.webkit.WebView
12
+ import android.widget.ImageView
13
13
  import androidx.coordinatorlayout.widget.CoordinatorLayout
14
14
  import androidx.core.view.children
15
15
  import androidx.fragment.app.Fragment
16
+ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
16
17
  import com.facebook.react.bridge.GuardedRunnable
17
18
  import com.facebook.react.bridge.ReactContext
18
- import com.facebook.react.uimanager.PixelUtil
19
19
  import com.facebook.react.uimanager.UIManagerHelper
20
20
  import com.facebook.react.uimanager.UIManagerModule
21
21
  import com.facebook.react.uimanager.events.EventDispatcher
22
+ import com.facebook.react.views.scroll.ReactScrollView
22
23
  import com.google.android.material.bottomsheet.BottomSheetBehavior
23
24
  import com.swmansion.rnscreens.events.HeaderHeightChangeEvent
24
25
  import com.swmansion.rnscreens.events.SheetDetentChangedEvent
@@ -140,17 +141,14 @@ class Screen(
140
141
  val width = r - l
141
142
  val height = b - t
142
143
 
143
- val headerHeight = calculateHeaderHeight()
144
- val totalHeight =
145
- headerHeight.first + headerHeight.second // action bar height + status bar height
146
144
  if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
147
- updateScreenSizeFabric(width, height, totalHeight)
145
+ updateScreenSizeFabric(width, height, t)
148
146
  } else {
149
147
  updateScreenSizePaper(width, height)
150
148
  }
151
149
 
152
150
  footer?.onParentLayout(changed, l, t, r, b, container!!.height)
153
- notifyHeaderHeightChange(totalHeight)
151
+ notifyHeaderHeightChange(t)
154
152
  }
155
153
  }
156
154
 
@@ -367,7 +365,7 @@ class Screen(
367
365
  parent?.let {
368
366
  for (i in 0 until it.childCount) {
369
367
  val child = it.getChildAt(i)
370
- if (child.javaClass.simpleName.equals("CircleImageView")) {
368
+ if (parent is SwipeRefreshLayout && child is ImageView) {
371
369
  // SwipeRefreshLayout class which has CircleImageView as a child,
372
370
  // does not handle `startViewTransition` properly.
373
371
  // It has a custom `getChildDrawingOrder` method which returns
@@ -384,38 +382,22 @@ class Screen(
384
382
  startTransitionRecursive(child.toolbar)
385
383
  }
386
384
  if (child is ViewGroup) {
385
+ // The children are miscounted when there's a FlatList with
386
+ // removeCLippedSubviews set to true (default).
387
+ // 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) {
390
+ for (j in 0 until child.childCount) {
391
+ child.addView(View(context))
392
+ }
393
+ }
387
394
  startTransitionRecursive(child)
388
395
  }
389
396
  }
390
397
  }
391
398
  }
392
399
 
393
- private fun calculateHeaderHeight(): Pair<Double, Double> {
394
- val actionBarTv = TypedValue()
395
- val resolvedActionBarSize =
396
- context.theme.resolveAttribute(android.R.attr.actionBarSize, actionBarTv, true)
397
-
398
- // Check if it's possible to get an attribute from theme context and assign a value from it.
399
- // Otherwise, the default value will be returned.
400
- val actionBarHeight =
401
- TypedValue
402
- .complexToDimensionPixelSize(actionBarTv.data, resources.displayMetrics)
403
- .takeIf { resolvedActionBarSize && headerConfig?.isHeaderHidden != true && headerConfig?.isHeaderTranslucent != true }
404
- ?.let { PixelUtil.toDIPFromPixel(it.toFloat()).toDouble() } ?: 0.0
405
-
406
- val statusBarHeight =
407
- context.resources
408
- .getIdentifier("status_bar_height", "dimen", "android")
409
- // Count only status bar when action bar is visible and status bar is not hidden
410
- .takeIf { it > 0 && isStatusBarHidden != true && actionBarHeight > 0 }
411
- ?.let { (context.resources::getDimensionPixelSize)(it) }
412
- ?.let { PixelUtil.toDIPFromPixel(it.toFloat()).toDouble() }
413
- ?: 0.0
414
-
415
- return actionBarHeight to statusBarHeight
416
- }
417
-
418
- private fun notifyHeaderHeightChange(headerHeight: Double) {
400
+ private fun notifyHeaderHeightChange(headerHeight: Int) {
419
401
  val screenContext = context as ReactContext
420
402
  val surfaceId = UIManagerHelper.getSurfaceId(screenContext)
421
403
  UIManagerHelper
@@ -454,6 +436,8 @@ class Screen(
454
436
  SLIDE_FROM_LEFT,
455
437
  FADE_FROM_BOTTOM,
456
438
  IOS,
439
+ IOS_FROM_RIGHT,
440
+ IOS_FROM_LEFT,
457
441
  }
458
442
 
459
443
  enum class ReplaceAnimation {
@@ -182,7 +182,9 @@ 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_slide_in_from_right_ios, R.anim.rns_slide_out_to_left_ios)
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)
186
188
  }
187
189
  } else {
188
190
  when (stackAnimation) {
@@ -220,7 +222,9 @@ class ScreenStack(
220
222
  R.anim.rns_slide_out_to_bottom,
221
223
  )
222
224
  StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_no_animation_250, R.anim.rns_fade_to_bottom)
223
- StackAnimation.IOS -> it.setCustomAnimations(R.anim.rns_slide_in_from_left_ios, R.anim.rns_slide_out_to_right_ios)
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)
224
228
  }
225
229
  }
226
230
  }
@@ -413,6 +417,8 @@ class ScreenStack(
413
417
  Build.VERSION.SDK_INT >= 33 ||
414
418
  fragmentWrapper.screen.stackAnimation === StackAnimation.SLIDE_FROM_BOTTOM ||
415
419
  fragmentWrapper.screen.stackAnimation === StackAnimation.FADE_FROM_BOTTOM ||
416
- fragmentWrapper.screen.stackAnimation === StackAnimation.IOS
420
+ fragmentWrapper.screen.stackAnimation === StackAnimation.IOS ||
421
+ fragmentWrapper.screen.stackAnimation === StackAnimation.IOS_FROM_RIGHT ||
422
+ fragmentWrapper.screen.stackAnimation === StackAnimation.IOS_FROM_LEFT
417
423
  }
418
424
  }
@@ -87,7 +87,6 @@ open class ScreenViewManager :
87
87
  stateWrapper: StateWrapper?,
88
88
  ): Any? {
89
89
  if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
90
- // fabricViewStateManager should never be null in Fabric. The null check is only for Paper's empty impl.
91
90
  view.setStateWrapper(stateWrapper)
92
91
  }
93
92
  return super.updateState(view, props, stateWrapper)
@@ -144,6 +143,8 @@ open class ScreenViewManager :
144
143
  "slide_from_bottom" -> Screen.StackAnimation.SLIDE_FROM_BOTTOM
145
144
  "fade_from_bottom" -> Screen.StackAnimation.FADE_FROM_BOTTOM
146
145
  "ios" -> Screen.StackAnimation.IOS
146
+ "ios_from_right" -> Screen.StackAnimation.IOS_FROM_RIGHT
147
+ "ios_from_left" -> Screen.StackAnimation.IOS_FROM_LEFT
147
148
  else -> throw JSApplicationIllegalArgumentException("Unknown animation type $animation")
148
149
  }
149
150
  }
@@ -225,7 +225,7 @@ object ScreenWindowTraits {
225
225
  val window = activity.window
226
226
 
227
227
  val screenForNavBarTranslucent = findScreenForTrait(screen, WindowTraits.NAVIGATION_BAR_TRANSLUCENT)
228
- val translucent = screenForNavBarTranslucent?.isNavigationBarTranslucent ?: false
228
+ val translucent = screenForNavBarTranslucent?.isNavigationBarTranslucent ?: return
229
229
 
230
230
  // Following method controls whether to display edge-to-edge content that draws behind the navigation bar
231
231
  WindowCompat.setDecorFitsSystemWindows(window, !translucent)
@@ -7,16 +7,16 @@ import com.facebook.react.uimanager.events.Event
7
7
  class HeaderHeightChangeEvent(
8
8
  surfaceId: Int,
9
9
  viewId: Int,
10
- private val headerHeight: Double,
10
+ private val headerHeight: Int,
11
11
  ) : Event<HeaderHeightChangeEvent>(surfaceId, viewId) {
12
12
  override fun getEventName() = EVENT_NAME
13
13
 
14
14
  // As the same header height could appear twice, use header height as a coalescing key.
15
- override fun getCoalescingKey(): Short = headerHeight.toInt().toShort()
15
+ override fun getCoalescingKey(): Short = headerHeight.toShort()
16
16
 
17
17
  override fun getEventData(): WritableMap? =
18
18
  Arguments.createMap().apply {
19
- putDouble("headerHeight", headerHeight)
19
+ putDouble("headerHeight", headerHeight.toDouble())
20
20
  }
21
21
 
22
22
  companion object {
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:duration="@android:integer/config_shortAnimTime"
4
+ android:fromXDelta="30%"
5
+ android:toXDelta="0%" />
@@ -2,4 +2,4 @@
2
2
  <translate xmlns:android="http://schemas.android.com/apk/res/android"
3
3
  android:duration="@android:integer/config_shortAnimTime"
4
4
  android:fromXDelta="0%"
5
- android:toXDelta="-30%"/>
5
+ android:toXDelta="30%" />
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:duration="@android:integer/config_shortAnimTime"
4
+ android:interpolator="@android:interpolator/accelerate_decelerate"
5
+ android:fromXDelta="0%"
6
+ android:toXDelta="-100%" />
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:duration="@android:integer/config_shortAnimTime"
4
+ android:interpolator="@android:interpolator/accelerate_decelerate"
5
+ android:fromXDelta="-100%"
6
+ android:toXDelta="0%" />
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:duration="@android:integer/config_shortAnimTime"
4
+ android:fromXDelta="0%"
5
+ android:toXDelta="-30%" />
@@ -12,7 +12,7 @@ abstract class FabricEnabledViewGroup(
12
12
  protected fun updateScreenSizeFabric(
13
13
  width: Int,
14
14
  height: Int,
15
- headerHeight: Double,
15
+ headerHeight: Int,
16
16
  ) {
17
17
  // do nothing
18
18
  }
@@ -5,9 +5,9 @@
5
5
  #endif
6
6
  #include <react/debug/react_native_assert.h>
7
7
  #include <react/renderer/components/rnscreens/Props.h>
8
+ #include <react/renderer/components/rnscreens/utils/RectUtil.h>
8
9
  #include <react/renderer/core/ConcreteComponentDescriptor.h>
9
10
  #include "RNSScreenShadowNode.h"
10
- #include "utils/RectUtil.h"
11
11
 
12
12
  namespace facebook {
13
13
  namespace react {
@@ -19,9 +19,6 @@ class RNSScreenComponentDescriptor final
19
19
  public:
20
20
  using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
21
21
 
22
- static constexpr const char *kScreenDummyLayoutHelperClass =
23
- "com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper";
24
-
25
22
  void adopt(ShadowNode &shadowNode) const override {
26
23
  react_native_assert(dynamic_cast<RNSScreenShadowNode *>(&shadowNode));
27
24
  auto &screenShadowNode = static_cast<RNSScreenShadowNode &>(shadowNode);
@@ -39,8 +36,8 @@ class RNSScreenComponentDescriptor final
39
36
  #ifdef ANDROID
40
37
  if (stateData.frameSize.width != 0 && stateData.frameSize.height != 0) {
41
38
  // When we receive dimensions from JVM side we can remove padding used for
42
- // correction, and we can stop applying height correction for the frame.
43
- // We want to leave top offset correction though intact.
39
+ // correction, and we can stop applying height and offset corrections for
40
+ // the frame.
44
41
  // TODO: In future, when we have dynamic header height we might want to
45
42
  // update Y offset correction here.
46
43
 
@@ -67,36 +64,11 @@ class RNSScreenComponentDescriptor final
67
64
  screenShadowNode.setPadding({0, 0, 0, 0});
68
65
  screenShadowNode.getFrameCorrectionModes().unset(
69
66
  FrameCorrectionModes::Mode::FrameHeightCorrection);
67
+ screenShadowNode.getFrameCorrectionModes().unset(
68
+ FrameCorrectionModes::Mode::FrameOriginCorrection);
70
69
 
71
70
  layoutableShadowNode.setSize(
72
71
  Size{stateData.frameSize.width, stateData.frameSize.height});
73
- } else {
74
- // This code path should be executed only on the very first (few)
75
- // layout(s), when we haven't received state update from JVM side yet.
76
-
77
- auto headerConfigChildOpt = findHeaderConfigChild(layoutableShadowNode);
78
-
79
- // During creation of the shadow node children are not attached yet.
80
- // We also do not want to set any padding in case.
81
- if (headerConfigChildOpt) {
82
- const auto &headerConfigChild = headerConfigChildOpt->get();
83
- const auto &headerProps =
84
- *std::static_pointer_cast<const RNSScreenStackHeaderConfigProps>(
85
- headerConfigChild->getProps());
86
-
87
- const auto headerHeight = headerProps.hidden
88
- ? 0.f
89
- : findHeaderHeight(
90
- headerProps.titleFontSize, headerProps.title.empty())
91
- .value_or(0.f);
92
-
93
- screenShadowNode.setPadding({0, 0, 0, headerHeight});
94
- screenShadowNode.setHeaderHeight(headerHeight);
95
- screenShadowNode.getFrameCorrectionModes().set(
96
- FrameCorrectionModes::Mode(
97
- FrameCorrectionModes::Mode::FrameHeightCorrection |
98
- FrameCorrectionModes::Mode::FrameOriginCorrection));
99
- }
100
72
  }
101
73
  #else
102
74
  if (stateData.frameSize.width != 0 && stateData.frameSize.height != 0) {
@@ -106,72 +78,6 @@ class RNSScreenComponentDescriptor final
106
78
  #endif // ANDROID
107
79
  ConcreteComponentDescriptor::adopt(shadowNode);
108
80
  }
109
-
110
- std::optional<std::reference_wrapper<const ShadowNode::Shared>>
111
- findHeaderConfigChild(
112
- const YogaLayoutableShadowNode &screenShadowNode) const {
113
- for (const ShadowNode::Shared &child : screenShadowNode.getChildren()) {
114
- if (std::strcmp(
115
- child->getComponentName(), "RNSScreenStackHeaderConfig") == 0) {
116
- return {std::cref(child)};
117
- }
118
- }
119
- return {};
120
- }
121
-
122
- #ifdef ANDROID
123
- std::optional<float> findHeaderHeight(
124
- const int fontSize,
125
- const bool isTitleEmpty) const {
126
- JNIEnv *env = facebook::jni::Environment::current();
127
-
128
- if (env == nullptr) {
129
- LOG(ERROR) << "[RNScreens] Failed to retrieve env\n";
130
- return {};
131
- }
132
-
133
- jclass layoutHelperClass = env->FindClass(kScreenDummyLayoutHelperClass);
134
-
135
- if (layoutHelperClass == nullptr) {
136
- LOG(ERROR) << "[RNScreens] Failed to find class with id "
137
- << kScreenDummyLayoutHelperClass;
138
- return {};
139
- }
140
-
141
- jmethodID computeDummyLayoutID =
142
- env->GetMethodID(layoutHelperClass, "computeDummyLayout", "(IZ)F");
143
-
144
- if (computeDummyLayoutID == nullptr) {
145
- LOG(ERROR)
146
- << "[RNScreens] Failed to retrieve computeDummyLayout method ID";
147
- return {};
148
- }
149
-
150
- jmethodID getInstanceMethodID = env->GetStaticMethodID(
151
- layoutHelperClass,
152
- "getInstance",
153
- "()Lcom/swmansion/rnscreens/utils/ScreenDummyLayoutHelper;");
154
-
155
- if (getInstanceMethodID == nullptr) {
156
- LOG(ERROR) << "[RNScreens] Failed to retrieve getInstanceMethodID";
157
- return {};
158
- }
159
-
160
- jobject packageInstance =
161
- env->CallStaticObjectMethod(layoutHelperClass, getInstanceMethodID);
162
-
163
- if (packageInstance == nullptr) {
164
- LOG(ERROR)
165
- << "[RNScreens] Failed to retrieve packageInstance or the package instance was null on JVM side";
166
- return {};
167
- }
168
-
169
- jfloat headerHeight = env->CallFloatMethod(
170
- packageInstance, computeDummyLayoutID, fontSize, isTitleEmpty);
171
-
172
- return {headerHeight};
173
- }
174
- #endif // ANDROID
175
81
  };
176
82
 
177
83
  } // namespace react
@@ -15,6 +15,107 @@ Point RNSScreenShadowNode::getContentOriginOffset(
15
15
  return {contentOffset.x, contentOffset.y};
16
16
  }
17
17
 
18
+ std::optional<std::reference_wrapper<const ShadowNode::Shared>>
19
+ findHeaderConfigChild(const YogaLayoutableShadowNode &screenShadowNode) {
20
+ for (const ShadowNode::Shared &child : screenShadowNode.getChildren()) {
21
+ if (std::strcmp(child->getComponentName(), "RNSScreenStackHeaderConfig") ==
22
+ 0) {
23
+ return {std::cref(child)};
24
+ }
25
+ }
26
+ return {};
27
+ }
28
+
29
+ static constexpr const char *kScreenDummyLayoutHelperClass =
30
+ "com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper";
31
+
32
+ #ifdef ANDROID
33
+ std::optional<float> findHeaderHeight(
34
+ const int fontSize,
35
+ const bool isTitleEmpty) {
36
+ JNIEnv *env = facebook::jni::Environment::current();
37
+
38
+ if (env == nullptr) {
39
+ LOG(ERROR) << "[RNScreens] Failed to retrieve env\n";
40
+ return {};
41
+ }
42
+
43
+ jclass layoutHelperClass = env->FindClass(kScreenDummyLayoutHelperClass);
44
+
45
+ if (layoutHelperClass == nullptr) {
46
+ LOG(ERROR) << "[RNScreens] Failed to find class with id "
47
+ << kScreenDummyLayoutHelperClass;
48
+ return {};
49
+ }
50
+
51
+ jmethodID computeDummyLayoutID =
52
+ env->GetMethodID(layoutHelperClass, "computeDummyLayout", "(IZ)F");
53
+
54
+ if (computeDummyLayoutID == nullptr) {
55
+ LOG(ERROR) << "[RNScreens] Failed to retrieve computeDummyLayout method ID";
56
+ return {};
57
+ }
58
+
59
+ jmethodID getInstanceMethodID = env->GetStaticMethodID(
60
+ layoutHelperClass,
61
+ "getInstance",
62
+ "()Lcom/swmansion/rnscreens/utils/ScreenDummyLayoutHelper;");
63
+
64
+ if (getInstanceMethodID == nullptr) {
65
+ LOG(ERROR) << "[RNScreens] Failed to retrieve getInstanceMethodID";
66
+ return {};
67
+ }
68
+
69
+ jobject packageInstance =
70
+ env->CallStaticObjectMethod(layoutHelperClass, getInstanceMethodID);
71
+
72
+ if (packageInstance == nullptr) {
73
+ LOG(ERROR)
74
+ << "[RNScreens] Failed to retrieve packageInstance or the package instance was null on JVM side";
75
+ return {};
76
+ }
77
+
78
+ jfloat headerHeight = env->CallFloatMethod(
79
+ packageInstance, computeDummyLayoutID, fontSize, isTitleEmpty);
80
+
81
+ return {headerHeight};
82
+ }
83
+ #endif // ANDROID
84
+
85
+ void RNSScreenShadowNode::appendChild(const ShadowNode::Shared &child) {
86
+ YogaLayoutableShadowNode::appendChild(child);
87
+ #ifdef ANDROID
88
+ const auto &stateData = getStateData();
89
+ if (stateData.frameSize.width == 0 || stateData.frameSize.height == 0) {
90
+ // This code path should be executed only on the very first (few)
91
+ // layout(s), when we haven't received state update from JVM side yet.
92
+ auto headerConfigChildOpt = findHeaderConfigChild(*this);
93
+ auto &screenShadowNode = static_cast<RNSScreenShadowNode &>(*this);
94
+
95
+ // During creation of the shadow node children are not attached yet.
96
+ // We also do not want to set any padding in case.
97
+ if (headerConfigChildOpt) {
98
+ const auto &headerConfigChild = headerConfigChildOpt->get();
99
+ const auto &headerProps =
100
+ *std::static_pointer_cast<const RNSScreenStackHeaderConfigProps>(
101
+ headerConfigChild->getProps());
102
+
103
+ const auto headerHeight = headerProps.hidden
104
+ ? 0.f
105
+ : findHeaderHeight(
106
+ headerProps.titleFontSize, headerProps.title.empty())
107
+ .value_or(0.f);
108
+
109
+ screenShadowNode.setPadding({0, 0, 0, headerHeight});
110
+ screenShadowNode.setHeaderHeight(headerHeight);
111
+ screenShadowNode.getFrameCorrectionModes().set(FrameCorrectionModes::Mode(
112
+ FrameCorrectionModes::Mode::FrameHeightCorrection |
113
+ FrameCorrectionModes::Mode::FrameOriginCorrection));
114
+ }
115
+ }
116
+ #endif // ANDROID
117
+ }
118
+
18
119
  void RNSScreenShadowNode::layout(facebook::react::LayoutContext layoutContext) {
19
120
  YogaLayoutableShadowNode::layout(layoutContext);
20
121
 
@@ -28,6 +28,8 @@ class JSI_EXPORT RNSScreenShadowNode final : public ConcreteViewShadowNode<
28
28
 
29
29
  Point getContentOriginOffset(bool includeTransform) const override;
30
30
 
31
+ void appendChild(const ShadowNode::Shared &child) override;
32
+
31
33
  void layout(LayoutContext layoutContext) override;
32
34
 
33
35
  #pragma mark - Custom interface