react-native-screens 3.35.0-rc.1 → 4.0.0-beta.1

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 (239) hide show
  1. package/README.md +1 -1
  2. package/android/build.gradle +28 -8
  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/InsetsObserverProxy.kt +67 -0
  6. package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +2 -0
  7. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +119 -38
  8. package/android/src/main/java/com/swmansion/rnscreens/ScreenContentWrapper.kt +38 -0
  9. package/android/src/main/java/com/swmansion/rnscreens/ScreenContentWrapperManager.kt +25 -0
  10. package/android/src/main/java/com/swmansion/rnscreens/ScreenFooter.kt +287 -0
  11. package/android/src/main/java/com/swmansion/rnscreens/ScreenFooterManager.kt +25 -0
  12. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +11 -19
  13. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragmentWrapper.kt +4 -0
  14. package/android/src/main/java/com/swmansion/rnscreens/ScreenModalFragment.kt +281 -0
  15. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +71 -22
  16. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +403 -41
  17. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragmentWrapper.kt +4 -1
  18. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +2 -2
  19. package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +97 -12
  20. package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +40 -29
  21. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/BottomSheetDialogRootView.kt +104 -0
  22. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/BottomSheetDialogScreen.kt +26 -0
  23. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/DimmingFragment.kt +488 -0
  24. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/DimmingView.kt +66 -0
  25. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/GestureTransparentViewGroup.kt +24 -0
  26. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/SheetUtils.kt +127 -0
  27. package/android/src/main/java/com/swmansion/rnscreens/events/HeaderHeightChangeEvent.kt +3 -3
  28. package/android/src/main/java/com/swmansion/rnscreens/events/SheetDetentChangedEvent.kt +27 -0
  29. package/android/src/main/java/com/swmansion/rnscreens/ext/NumericExt.kt +12 -0
  30. package/android/src/main/java/com/swmansion/rnscreens/ext/ViewExt.kt +32 -0
  31. package/android/src/main/res/base/anim/rns_ios_from_left_background_close.xml +5 -0
  32. package/android/src/main/res/base/anim/{rns_slide_out_to_left_ios.xml → rns_ios_from_left_background_open.xml} +1 -1
  33. package/android/src/main/res/base/anim/rns_ios_from_left_foreground_close.xml +6 -0
  34. package/android/src/main/res/base/anim/rns_ios_from_left_foreground_open.xml +6 -0
  35. package/android/src/main/res/base/anim/rns_ios_from_right_background_open.xml +5 -0
  36. package/android/src/main/res/base/drawable/rns_rounder_top_corners_shape.xml +8 -0
  37. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenContentWrapperManagerDelegate.java +25 -0
  38. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenContentWrapperManagerInterface.java +16 -0
  39. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenFooterManagerDelegate.java +25 -0
  40. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenFooterManagerInterface.java +16 -0
  41. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerDelegate.java +9 -2
  42. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenManagerInterface.java +5 -2
  43. package/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +1 -1
  44. package/common/cpp/react/renderer/components/rnscreens/RNSScreenComponentDescriptor.h +5 -99
  45. package/common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.cpp +101 -0
  46. package/common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.h +2 -0
  47. package/ios/RNSConvert.h +5 -3
  48. package/ios/RNSConvert.mm +18 -21
  49. package/ios/RNSFullWindowOverlay.mm +6 -0
  50. package/ios/RNSModalScreen.mm +7 -0
  51. package/ios/RNSScreen.h +3 -2
  52. package/ios/RNSScreen.mm +442 -49
  53. package/ios/RNSScreenContainer.mm +6 -0
  54. package/ios/RNSScreenContentWrapper.h +44 -0
  55. package/ios/RNSScreenContentWrapper.mm +67 -0
  56. package/ios/RNSScreenFooter.h +30 -0
  57. package/ios/RNSScreenFooter.mm +143 -0
  58. package/ios/RNSScreenNavigationContainer.mm +7 -0
  59. package/ios/RNSScreenStack.mm +7 -7
  60. package/ios/RNSScreenStackHeaderConfig.mm +10 -0
  61. package/ios/RNSScreenStackHeaderSubview.mm +6 -3
  62. package/ios/RNSSearchBar.mm +7 -0
  63. package/lib/commonjs/TransitionProgressContext.js +1 -0
  64. package/lib/commonjs/TransitionProgressContext.js.map +1 -1
  65. package/lib/commonjs/components/Screen.js +63 -4
  66. package/lib/commonjs/components/Screen.js.map +1 -1
  67. package/lib/commonjs/components/Screen.web.js +2 -0
  68. package/lib/commonjs/components/Screen.web.js.map +1 -1
  69. package/lib/commonjs/components/ScreenContainer.js +1 -0
  70. package/lib/commonjs/components/ScreenContainer.js.map +1 -1
  71. package/lib/commonjs/components/ScreenContentWrapper.js +19 -0
  72. package/lib/commonjs/components/ScreenContentWrapper.js.map +1 -0
  73. package/lib/commonjs/components/ScreenFooter.js +23 -0
  74. package/lib/commonjs/components/ScreenFooter.js.map +1 -0
  75. package/lib/commonjs/components/ScreenStack.js +1 -0
  76. package/lib/commonjs/components/ScreenStack.js.map +1 -1
  77. package/lib/commonjs/components/ScreenStackHeaderConfig.js +1 -0
  78. package/lib/commonjs/components/ScreenStackHeaderConfig.js.map +1 -1
  79. package/lib/commonjs/components/SearchBar.js +1 -0
  80. package/lib/commonjs/components/SearchBar.js.map +1 -1
  81. package/lib/commonjs/core.js +1 -0
  82. package/lib/commonjs/core.js.map +1 -1
  83. package/lib/commonjs/fabric/FullWindowOverlayNativeComponent.js +1 -0
  84. package/lib/commonjs/fabric/FullWindowOverlayNativeComponent.js.map +1 -1
  85. package/lib/commonjs/fabric/ModalScreenNativeComponent.js +1 -0
  86. package/lib/commonjs/fabric/ModalScreenNativeComponent.js.map +1 -1
  87. package/lib/commonjs/fabric/NativeScreensModule.js +2 -1
  88. package/lib/commonjs/fabric/NativeScreensModule.js.map +1 -1
  89. package/lib/commonjs/fabric/ScreenContainerNativeComponent.js +1 -0
  90. package/lib/commonjs/fabric/ScreenContainerNativeComponent.js.map +1 -1
  91. package/lib/commonjs/fabric/ScreenContentWrapperNativeComponent.js +10 -0
  92. package/lib/commonjs/fabric/ScreenContentWrapperNativeComponent.js.map +1 -0
  93. package/lib/commonjs/fabric/ScreenFooterNativeComponent.js +10 -0
  94. package/lib/commonjs/fabric/ScreenFooterNativeComponent.js.map +1 -0
  95. package/lib/commonjs/fabric/ScreenNativeComponent.js +1 -0
  96. package/lib/commonjs/fabric/ScreenNativeComponent.js.map +1 -1
  97. package/lib/commonjs/fabric/ScreenNavigationContainerNativeComponent.js +1 -0
  98. package/lib/commonjs/fabric/ScreenNavigationContainerNativeComponent.js.map +1 -1
  99. package/lib/commonjs/fabric/ScreenStackHeaderConfigNativeComponent.js +1 -0
  100. package/lib/commonjs/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  101. package/lib/commonjs/fabric/ScreenStackHeaderSubviewNativeComponent.js +1 -0
  102. package/lib/commonjs/fabric/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
  103. package/lib/commonjs/fabric/ScreenStackNativeComponent.js +1 -0
  104. package/lib/commonjs/fabric/ScreenStackNativeComponent.js.map +1 -1
  105. package/lib/commonjs/fabric/SearchBarNativeComponent.js +2 -2
  106. package/lib/commonjs/fabric/SearchBarNativeComponent.js.map +1 -1
  107. package/lib/commonjs/index.js +30 -0
  108. package/lib/commonjs/index.js.map +1 -1
  109. package/lib/commonjs/native-stack/contexts/GHContext.js +1 -0
  110. package/lib/commonjs/native-stack/contexts/GHContext.js.map +1 -1
  111. package/lib/commonjs/native-stack/views/FooterComponent.js +18 -0
  112. package/lib/commonjs/native-stack/views/FooterComponent.js.map +1 -0
  113. package/lib/commonjs/native-stack/views/NativeStackView.js +59 -14
  114. package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
  115. package/lib/module/TransitionProgressContext.js +2 -0
  116. package/lib/module/TransitionProgressContext.js.map +1 -1
  117. package/lib/module/components/Screen.js +64 -4
  118. package/lib/module/components/Screen.js.map +1 -1
  119. package/lib/module/components/Screen.web.js +3 -0
  120. package/lib/module/components/Screen.web.js.map +1 -1
  121. package/lib/module/components/ScreenContainer.js +2 -0
  122. package/lib/module/components/ScreenContainer.js.map +1 -1
  123. package/lib/module/components/ScreenContentWrapper.js +12 -0
  124. package/lib/module/components/ScreenContentWrapper.js.map +1 -0
  125. package/lib/module/components/ScreenFooter.js +17 -0
  126. package/lib/module/components/ScreenFooter.js.map +1 -0
  127. package/lib/module/components/ScreenStack.js +2 -0
  128. package/lib/module/components/ScreenStack.js.map +1 -1
  129. package/lib/module/components/ScreenStackHeaderConfig.js +2 -0
  130. package/lib/module/components/ScreenStackHeaderConfig.js.map +1 -1
  131. package/lib/module/components/SearchBar.js +2 -0
  132. package/lib/module/components/SearchBar.js.map +1 -1
  133. package/lib/module/core.js +2 -0
  134. package/lib/module/core.js.map +1 -1
  135. package/lib/module/fabric/FullWindowOverlayNativeComponent.js +2 -0
  136. package/lib/module/fabric/FullWindowOverlayNativeComponent.js.map +1 -1
  137. package/lib/module/fabric/ModalScreenNativeComponent.js +2 -0
  138. package/lib/module/fabric/ModalScreenNativeComponent.js.map +1 -1
  139. package/lib/module/fabric/NativeScreensModule.js +2 -1
  140. package/lib/module/fabric/NativeScreensModule.js.map +1 -1
  141. package/lib/module/fabric/ScreenContainerNativeComponent.js +2 -0
  142. package/lib/module/fabric/ScreenContainerNativeComponent.js.map +1 -1
  143. package/lib/module/fabric/ScreenContentWrapperNativeComponent.js +3 -0
  144. package/lib/module/fabric/ScreenContentWrapperNativeComponent.js.map +1 -0
  145. package/lib/module/fabric/ScreenFooterNativeComponent.js +3 -0
  146. package/lib/module/fabric/ScreenFooterNativeComponent.js.map +1 -0
  147. package/lib/module/fabric/ScreenNativeComponent.js +2 -0
  148. package/lib/module/fabric/ScreenNativeComponent.js.map +1 -1
  149. package/lib/module/fabric/ScreenNavigationContainerNativeComponent.js +2 -0
  150. package/lib/module/fabric/ScreenNavigationContainerNativeComponent.js.map +1 -1
  151. package/lib/module/fabric/ScreenStackHeaderConfigNativeComponent.js +2 -0
  152. package/lib/module/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
  153. package/lib/module/fabric/ScreenStackHeaderSubviewNativeComponent.js +2 -0
  154. package/lib/module/fabric/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
  155. package/lib/module/fabric/ScreenStackNativeComponent.js +2 -0
  156. package/lib/module/fabric/ScreenStackNativeComponent.js.map +1 -1
  157. package/lib/module/fabric/SearchBarNativeComponent.js +2 -0
  158. package/lib/module/fabric/SearchBarNativeComponent.js.map +1 -1
  159. package/lib/module/index.js +8 -6
  160. package/lib/module/index.js.map +1 -1
  161. package/lib/module/native-stack/contexts/GHContext.js +2 -0
  162. package/lib/module/native-stack/contexts/GHContext.js.map +1 -1
  163. package/lib/module/native-stack/views/FooterComponent.js +11 -0
  164. package/lib/module/native-stack/views/FooterComponent.js.map +1 -0
  165. package/lib/module/native-stack/views/NativeStackView.js +61 -15
  166. package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
  167. package/lib/typescript/TransitionProgressContext.d.ts.map +1 -1
  168. package/lib/typescript/components/Screen.d.ts.map +1 -1
  169. package/lib/typescript/components/Screen.web.d.ts.map +1 -1
  170. package/lib/typescript/components/ScreenContainer.d.ts.map +1 -1
  171. package/lib/typescript/components/ScreenContentWrapper.d.ts +6 -0
  172. package/lib/typescript/components/ScreenContentWrapper.d.ts.map +1 -0
  173. package/lib/typescript/components/ScreenFooter.d.ts +12 -0
  174. package/lib/typescript/components/ScreenFooter.d.ts.map +1 -0
  175. package/lib/typescript/components/ScreenStack.d.ts.map +1 -1
  176. package/lib/typescript/components/ScreenStackHeaderConfig.d.ts.map +1 -1
  177. package/lib/typescript/components/SearchBar.d.ts.map +1 -1
  178. package/lib/typescript/core.d.ts.map +1 -1
  179. package/lib/typescript/fabric/FullWindowOverlayNativeComponent.d.ts.map +1 -1
  180. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts +2 -3
  181. package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts.map +1 -1
  182. package/lib/typescript/fabric/NativeScreensModule.d.ts.map +1 -1
  183. package/lib/typescript/fabric/ScreenContainerNativeComponent.d.ts.map +1 -1
  184. package/lib/typescript/fabric/ScreenContentWrapperNativeComponent.d.ts +7 -0
  185. package/lib/typescript/fabric/ScreenContentWrapperNativeComponent.d.ts.map +1 -0
  186. package/lib/typescript/fabric/ScreenFooterNativeComponent.d.ts +7 -0
  187. package/lib/typescript/fabric/ScreenFooterNativeComponent.d.ts.map +1 -0
  188. package/lib/typescript/fabric/ScreenNativeComponent.d.ts +10 -4
  189. package/lib/typescript/fabric/ScreenNativeComponent.d.ts.map +1 -1
  190. package/lib/typescript/fabric/ScreenNavigationContainerNativeComponent.d.ts.map +1 -1
  191. package/lib/typescript/fabric/ScreenStackHeaderConfigNativeComponent.d.ts.map +1 -1
  192. package/lib/typescript/fabric/ScreenStackHeaderSubviewNativeComponent.d.ts.map +1 -1
  193. package/lib/typescript/fabric/ScreenStackNativeComponent.d.ts.map +1 -1
  194. package/lib/typescript/fabric/SearchBarNativeComponent.d.ts.map +1 -1
  195. package/lib/typescript/index.d.ts +20 -0
  196. package/lib/typescript/index.d.ts.map +1 -1
  197. package/lib/typescript/native-stack/contexts/GHContext.d.ts.map +1 -1
  198. package/lib/typescript/native-stack/types.d.ts +72 -22
  199. package/lib/typescript/native-stack/types.d.ts.map +1 -1
  200. package/lib/typescript/native-stack/views/FooterComponent.d.ts +7 -0
  201. package/lib/typescript/native-stack/views/FooterComponent.d.ts.map +1 -0
  202. package/lib/typescript/native-stack/views/NativeStackView.d.ts.map +1 -1
  203. package/lib/typescript/types.d.ts +71 -18
  204. package/lib/typescript/types.d.ts.map +1 -1
  205. package/native-stack/README.md +40 -14
  206. package/package.json +1 -1
  207. package/react-native.config.js +18 -16
  208. package/src/TransitionProgressContext.tsx +2 -0
  209. package/src/components/Screen.tsx +76 -4
  210. package/src/components/Screen.web.tsx +3 -0
  211. package/src/components/ScreenContainer.tsx +2 -0
  212. package/src/components/ScreenContentWrapper.tsx +12 -0
  213. package/src/components/ScreenFooter.tsx +18 -0
  214. package/src/components/ScreenStack.tsx +2 -0
  215. package/src/components/ScreenStackHeaderConfig.tsx +2 -0
  216. package/src/components/SearchBar.tsx +2 -0
  217. package/src/core.ts +2 -0
  218. package/src/fabric/FullWindowOverlayNativeComponent.ts +2 -0
  219. package/src/fabric/ModalScreenNativeComponent.ts +4 -4
  220. package/src/fabric/NativeScreensModule.ts +2 -0
  221. package/src/fabric/ScreenContainerNativeComponent.ts +2 -0
  222. package/src/fabric/ScreenContentWrapperNativeComponent.ts +9 -0
  223. package/src/fabric/ScreenFooterNativeComponent.ts +6 -0
  224. package/src/fabric/ScreenNativeComponent.ts +15 -5
  225. package/src/fabric/ScreenNavigationContainerNativeComponent.ts +2 -0
  226. package/src/fabric/ScreenStackHeaderConfigNativeComponent.ts +2 -0
  227. package/src/fabric/ScreenStackHeaderSubviewNativeComponent.ts +2 -0
  228. package/src/fabric/ScreenStackNativeComponent.ts +2 -0
  229. package/src/fabric/SearchBarNativeComponent.ts +2 -0
  230. package/src/index.tsx +16 -6
  231. package/src/native-stack/contexts/GHContext.tsx +2 -0
  232. package/src/native-stack/types.tsx +66 -22
  233. package/src/native-stack/views/FooterComponent.tsx +10 -0
  234. package/src/native-stack/views/NativeStackView.tsx +75 -11
  235. package/src/types.tsx +78 -17
  236. package/windows/RNScreens/Screen.h +3 -1
  237. /package/android/src/main/res/base/anim/{rns_slide_in_from_left_ios.xml → rns_ios_from_right_background_close.xml} +0 -0
  238. /package/android/src/main/res/base/anim/{rns_slide_out_to_right_ios.xml → rns_ios_from_right_foreground_close.xml} +0 -0
  239. /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()
@@ -159,8 +179,8 @@ repositories {
159
179
 
160
180
  dependencies {
161
181
  implementation 'com.facebook.react:react-native:+'
162
- implementation 'androidx.appcompat:appcompat:1.4.2'
163
- implementation 'androidx.fragment:fragment:1.3.6'
182
+ implementation 'androidx.appcompat:appcompat:1.6.1'
183
+ implementation 'androidx.fragment:fragment-ktx:1.6.1'
164
184
  implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
165
185
  implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
166
186
  implementation 'com.google.android.material:material:1.6.1'
@@ -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
+ }
@@ -0,0 +1,67 @@
1
+ package com.swmansion.rnscreens
2
+
3
+ import android.view.View
4
+ import androidx.core.view.OnApplyWindowInsetsListener
5
+ import androidx.core.view.ViewCompat
6
+ import androidx.core.view.WindowInsetsCompat
7
+ import java.lang.ref.WeakReference
8
+
9
+ object InsetsObserverProxy : OnApplyWindowInsetsListener {
10
+ private val listeners: ArrayList<OnApplyWindowInsetsListener> = arrayListOf()
11
+ private var eventSourceView: WeakReference<View> = WeakReference(null)
12
+
13
+ // Please note semantics of this property. This is not `isRegistered`, because somebody, could unregister
14
+ // us, without our knowledge, e.g. reanimated or different 3rd party library. This holds only information
15
+ // whether this observer has been initially registered.
16
+ private var hasBeenRegistered: Boolean = false
17
+
18
+ private var shouldForwardInsetsToView = true
19
+
20
+ override fun onApplyWindowInsets(
21
+ v: View,
22
+ insets: WindowInsetsCompat,
23
+ ): WindowInsetsCompat {
24
+ var rollingInsets =
25
+ if (shouldForwardInsetsToView) {
26
+ WindowInsetsCompat.toWindowInsetsCompat(
27
+ v.onApplyWindowInsets(insets.toWindowInsets()),
28
+ v,
29
+ )
30
+ } else {
31
+ insets
32
+ }
33
+
34
+ listeners.forEach {
35
+ rollingInsets = it.onApplyWindowInsets(v, insets)
36
+ }
37
+ return rollingInsets
38
+ }
39
+
40
+ fun addOnApplyWindowInsetsListener(listener: OnApplyWindowInsetsListener) {
41
+ listeners.add(listener)
42
+ }
43
+
44
+ fun removeOnApplyWindowInsetsListener(listener: OnApplyWindowInsetsListener) {
45
+ listeners.remove(listener)
46
+ }
47
+
48
+ fun registerOnView(view: View) {
49
+ if (!hasBeenRegistered) {
50
+ ViewCompat.setOnApplyWindowInsetsListener(view, this)
51
+ eventSourceView = WeakReference(view)
52
+ hasBeenRegistered = true
53
+ } else if (getObservedView() != view) {
54
+ throw IllegalStateException(
55
+ "[RNScreens] Attempt to register InsetsObserverProxy on $view while it has been already registered on ${getObservedView()}",
56
+ )
57
+ }
58
+ }
59
+
60
+ fun unregister() {
61
+ eventSourceView.get()?.takeIf { hasBeenRegistered }?.let {
62
+ ViewCompat.setOnApplyWindowInsetsListener(it, null)
63
+ }
64
+ }
65
+
66
+ private fun getObservedView(): View? = eventSourceView.get()
67
+ }
@@ -37,6 +37,8 @@ class RNScreensPackage : TurboReactPackage() {
37
37
  ScreenStackHeaderConfigViewManager(),
38
38
  ScreenStackHeaderSubviewManager(),
39
39
  SearchBarManager(),
40
+ ScreenFooterManager(),
41
+ ScreenContentWrapperManager(),
40
42
  )
41
43
  }
42
44
 
@@ -5,27 +5,39 @@ 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
+ import androidx.coordinatorlayout.widget.CoordinatorLayout
13
14
  import androidx.core.view.children
14
15
  import androidx.fragment.app.Fragment
16
+ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
15
17
  import com.facebook.react.bridge.GuardedRunnable
16
18
  import com.facebook.react.bridge.ReactContext
17
- import com.facebook.react.uimanager.PixelUtil
18
19
  import com.facebook.react.uimanager.UIManagerHelper
19
20
  import com.facebook.react.uimanager.UIManagerModule
21
+ import com.facebook.react.uimanager.events.EventDispatcher
22
+ import com.facebook.react.views.scroll.ReactScrollView
23
+ import com.google.android.material.bottomsheet.BottomSheetBehavior
20
24
  import com.swmansion.rnscreens.events.HeaderHeightChangeEvent
25
+ import com.swmansion.rnscreens.events.SheetDetentChangedEvent
21
26
 
22
27
  @SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated.
23
28
  class Screen(
24
- context: ReactContext?,
25
- ) : FabricEnabledViewGroup(context) {
29
+ val reactContext: ReactContext,
30
+ ) : FabricEnabledViewGroup(reactContext),
31
+ ScreenContentWrapper.OnLayoutCallback {
26
32
  val fragment: Fragment?
27
33
  get() = fragmentWrapper?.fragment
28
34
 
35
+ val sheetBehavior: BottomSheetBehavior<Screen>?
36
+ get() = (layoutParams as? CoordinatorLayout.LayoutParams)?.behavior as? BottomSheetBehavior<Screen>
37
+
38
+ val reactEventDispatcher: EventDispatcher?
39
+ get() = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
40
+
29
41
  var fragmentWrapper: ScreenFragmentWrapper? = null
30
42
  var container: ScreenContainer? = null
31
43
  var activityState: ActivityState? = null
@@ -40,6 +52,33 @@ class Screen(
40
52
  var isStatusBarAnimated: Boolean? = null
41
53
  var isBeingRemoved = false
42
54
 
55
+ // Props for controlling modal presentation
56
+ var isSheetGrabberVisible: Boolean = false
57
+ var sheetCornerRadius: Float = 0F
58
+ set(value) {
59
+ field = value
60
+ (fragment as? ScreenStackFragment)?.onSheetCornerRadiusChange()
61
+ }
62
+ var sheetExpandsWhenScrolledToEdge: Boolean = true
63
+
64
+ // We want to make sure here that at least one value is present in this array all the time.
65
+ // TODO: Model this with custom data structure to guarantee that this invariant is not violated.
66
+ var sheetDetents = mutableListOf(1.0)
67
+ var sheetLargestUndimmedDetentIndex: Int = -1
68
+ var sheetInitialDetentIndex: Int = 0
69
+ var sheetClosesOnTouchOutside = true
70
+ var sheetElevation: Float = 24F
71
+
72
+ var footer: ScreenFooter? = null
73
+ set(value) {
74
+ if (value == null && field != null) {
75
+ sheetBehavior?.let { field!!.unregisterWithSheetBehavior(it) }
76
+ } else if (value != null) {
77
+ sheetBehavior?.let { value.registerWithSheetBehavior(it) }
78
+ }
79
+ field = value
80
+ }
81
+
43
82
  init {
44
83
  // we set layout params as WindowManager.LayoutParams to workaround the issue with TextInputs
45
84
  // not displaying modal menus (e.g., copy/paste or selection). The missing menus are due to the
@@ -54,6 +93,33 @@ class Screen(
54
93
  layoutParams = WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_APPLICATION)
55
94
  }
56
95
 
96
+ /**
97
+ * ScreenContentWrapper notifies us here on it's layout. It is essential for implementing
98
+ * `fitToContents` for formSheets, as this is first entry point where we can acquire
99
+ * height of our content.
100
+ */
101
+ override fun onLayoutCallback(
102
+ changed: Boolean,
103
+ left: Int,
104
+ top: Int,
105
+ right: Int,
106
+ bottom: Int,
107
+ ) {
108
+ val height = bottom - top
109
+
110
+ if (sheetDetents.count() == 1 && sheetDetents.first() == SHEET_FIT_TO_CONTENTS) {
111
+ sheetBehavior?.let {
112
+ if (it.maxHeight != height) {
113
+ it.maxHeight = height
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ fun registerLayoutCallbackForWrapper(wrapper: ScreenContentWrapper) {
120
+ wrapper.delegate = this
121
+ }
122
+
57
123
  override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
58
124
  // do nothing, react native will keep the view hierarchy so no need to serialize/deserialize
59
125
  // view's states. The side effect of restoring is that TextInput components would trigger
@@ -75,16 +141,14 @@ class Screen(
75
141
  val width = r - l
76
142
  val height = b - t
77
143
 
78
- val headerHeight = calculateHeaderHeight()
79
- val totalHeight =
80
- headerHeight.first + headerHeight.second // action bar height + status bar height
81
144
  if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
82
- updateScreenSizeFabric(width, height, totalHeight)
145
+ updateScreenSizeFabric(width, height, t)
83
146
  } else {
84
147
  updateScreenSizePaper(width, height)
85
148
  }
86
149
 
87
- notifyHeaderHeightChange(totalHeight)
150
+ footer?.onParentLayout(changed, l, t, r, b, container!!.height)
151
+ notifyHeaderHeightChange(t)
88
152
  }
89
153
  }
90
154
 
@@ -92,7 +156,6 @@ class Screen(
92
156
  width: Int,
93
157
  height: Int,
94
158
  ) {
95
- val reactContext = context as ReactContext
96
159
  reactContext.runOnNativeModulesQueueThread(
97
160
  object : GuardedRunnable(reactContext.exceptionHandler) {
98
161
  override fun runGuarded() {
@@ -127,7 +190,14 @@ class Screen(
127
190
  )
128
191
  }
129
192
 
130
- fun isTransparent(): Boolean = stackPresentation === StackPresentation.TRANSPARENT_MODAL
193
+ fun isTransparent(): Boolean =
194
+ when (stackPresentation) {
195
+ StackPresentation.TRANSPARENT_MODAL,
196
+ StackPresentation.FORM_SHEET,
197
+ -> true
198
+
199
+ else -> false
200
+ }
131
201
 
132
202
  private fun hasWebView(viewGroup: ViewGroup): Boolean {
133
203
  for (i in 0 until viewGroup.childCount) {
@@ -295,7 +365,7 @@ class Screen(
295
365
  parent?.let {
296
366
  for (i in 0 until it.childCount) {
297
367
  val child = it.getChildAt(i)
298
- if (child.javaClass.simpleName.equals("CircleImageView")) {
368
+ if (parent is SwipeRefreshLayout && child is ImageView) {
299
369
  // SwipeRefreshLayout class which has CircleImageView as a child,
300
370
  // does not handle `startViewTransition` properly.
301
371
  // It has a custom `getChildDrawingOrder` method which returns
@@ -312,38 +382,22 @@ class Screen(
312
382
  startTransitionRecursive(child.toolbar)
313
383
  }
314
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
+ }
315
394
  startTransitionRecursive(child)
316
395
  }
317
396
  }
318
397
  }
319
398
  }
320
399
 
321
- private fun calculateHeaderHeight(): Pair<Double, Double> {
322
- val actionBarTv = TypedValue()
323
- val resolvedActionBarSize =
324
- context.theme.resolveAttribute(android.R.attr.actionBarSize, actionBarTv, true)
325
-
326
- // Check if it's possible to get an attribute from theme context and assign a value from it.
327
- // Otherwise, the default value will be returned.
328
- val actionBarHeight =
329
- TypedValue
330
- .complexToDimensionPixelSize(actionBarTv.data, resources.displayMetrics)
331
- .takeIf { resolvedActionBarSize && headerConfig?.isHeaderHidden != true && headerConfig?.isHeaderTranslucent != true }
332
- ?.let { PixelUtil.toDIPFromPixel(it.toFloat()).toDouble() } ?: 0.0
333
-
334
- val statusBarHeight =
335
- context.resources
336
- .getIdentifier("status_bar_height", "dimen", "android")
337
- // Count only status bar when action bar is visible and status bar is not hidden
338
- .takeIf { it > 0 && isStatusBarHidden != true && actionBarHeight > 0 }
339
- ?.let { (context.resources::getDimensionPixelSize)(it) }
340
- ?.let { PixelUtil.toDIPFromPixel(it.toFloat()).toDouble() }
341
- ?: 0.0
342
-
343
- return actionBarHeight to statusBarHeight
344
- }
345
-
346
- private fun notifyHeaderHeightChange(headerHeight: Double) {
400
+ private fun notifyHeaderHeightChange(headerHeight: Int) {
347
401
  val screenContext = context as ReactContext
348
402
  val surfaceId = UIManagerHelper.getSurfaceId(screenContext)
349
403
  UIManagerHelper
@@ -351,10 +405,26 @@ class Screen(
351
405
  ?.dispatchEvent(HeaderHeightChangeEvent(surfaceId, id, headerHeight))
352
406
  }
353
407
 
408
+ internal fun notifySheetDetentChange(
409
+ detentIndex: Int,
410
+ isStable: Boolean,
411
+ ) {
412
+ val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
413
+ reactEventDispatcher?.dispatchEvent(
414
+ SheetDetentChangedEvent(
415
+ surfaceId,
416
+ id,
417
+ detentIndex,
418
+ isStable,
419
+ ),
420
+ )
421
+ }
422
+
354
423
  enum class StackPresentation {
355
424
  PUSH,
356
425
  MODAL,
357
426
  TRANSPARENT_MODAL,
427
+ FORM_SHEET,
358
428
  }
359
429
 
360
430
  enum class StackAnimation {
@@ -366,6 +436,8 @@ class Screen(
366
436
  SLIDE_FROM_LEFT,
367
437
  FADE_FROM_BOTTOM,
368
438
  IOS,
439
+ IOS_FROM_RIGHT,
440
+ IOS_FROM_LEFT,
369
441
  }
370
442
 
371
443
  enum class ReplaceAnimation {
@@ -390,4 +462,13 @@ class Screen(
390
462
  NAVIGATION_BAR_TRANSLUCENT,
391
463
  NAVIGATION_BAR_HIDDEN,
392
464
  }
465
+
466
+ companion object {
467
+ const val TAG = "Screen"
468
+
469
+ /**
470
+ * This value describes value in sheet detents array that will be treated as `fitToContents` option.
471
+ */
472
+ const val SHEET_FIT_TO_CONTENTS = -1.0
473
+ }
393
474
  }
@@ -0,0 +1,38 @@
1
+ package com.swmansion.rnscreens
2
+
3
+ import android.annotation.SuppressLint
4
+ import com.facebook.react.bridge.ReactContext
5
+ import com.facebook.react.views.view.ReactViewGroup
6
+
7
+ /**
8
+ * When we wrap children of the Screen component inside this component in JS code,
9
+ * we can later use it to get the enclosing frame size of our content as it is rendered by RN.
10
+ *
11
+ * This is useful when adapting form sheet height to its contents height.
12
+ */
13
+ @SuppressLint("ViewConstructor")
14
+ class ScreenContentWrapper(
15
+ reactContext: ReactContext,
16
+ ) : ReactViewGroup(reactContext) {
17
+ internal var delegate: OnLayoutCallback? = null
18
+
19
+ interface OnLayoutCallback {
20
+ fun onLayoutCallback(
21
+ changed: Boolean,
22
+ left: Int,
23
+ top: Int,
24
+ right: Int,
25
+ bottom: Int,
26
+ )
27
+ }
28
+
29
+ override fun onLayout(
30
+ changed: Boolean,
31
+ left: Int,
32
+ top: Int,
33
+ right: Int,
34
+ bottom: Int,
35
+ ) {
36
+ delegate?.onLayoutCallback(changed, left, top, right, bottom)
37
+ }
38
+ }
@@ -0,0 +1,25 @@
1
+ package com.swmansion.rnscreens
2
+
3
+ import com.facebook.react.module.annotations.ReactModule
4
+ import com.facebook.react.uimanager.ThemedReactContext
5
+ import com.facebook.react.uimanager.ViewGroupManager
6
+ import com.facebook.react.uimanager.ViewManagerDelegate
7
+ import com.facebook.react.viewmanagers.RNSScreenContentWrapperManagerDelegate
8
+ import com.facebook.react.viewmanagers.RNSScreenContentWrapperManagerInterface
9
+
10
+ @ReactModule(name = ScreenContentWrapperManager.REACT_CLASS)
11
+ class ScreenContentWrapperManager :
12
+ ViewGroupManager<ScreenContentWrapper>(),
13
+ RNSScreenContentWrapperManagerInterface<ScreenContentWrapper> {
14
+ private val delegate: ViewManagerDelegate<ScreenContentWrapper> = RNSScreenContentWrapperManagerDelegate(this)
15
+
16
+ companion object {
17
+ const val REACT_CLASS = "RNSScreenContentWrapper"
18
+ }
19
+
20
+ override fun getName(): String = REACT_CLASS
21
+
22
+ override fun createViewInstance(reactContext: ThemedReactContext): ScreenContentWrapper = ScreenContentWrapper(reactContext)
23
+
24
+ override fun getDelegate(): ViewManagerDelegate<ScreenContentWrapper> = delegate
25
+ }