react-native-screens 5.0.0-alpha.2 → 5.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/Package.swift +87 -0
  2. package/README.md +0 -7
  3. package/android/src/main/cpp/NativeProxy.cpp +30 -6
  4. package/android/src/main/cpp/NativeProxy.h +7 -1
  5. package/android/src/main/java/com/swmansion/rnscreens/common/container/Container.kt +8 -0
  6. package/android/src/main/java/com/swmansion/rnscreens/common/container/ContainerItem.kt +13 -1
  7. package/android/src/main/java/com/swmansion/rnscreens/common/text/ReactTextAppearance.kt +62 -0
  8. package/android/src/main/java/com/swmansion/rnscreens/common/text/TextAppearance.kt +20 -0
  9. package/android/src/main/java/com/swmansion/rnscreens/common/text/TextAppearanceDefaults.kt +65 -0
  10. package/android/src/main/java/com/swmansion/rnscreens/helpers/PropIconResolver.kt +17 -23
  11. package/android/src/main/java/com/swmansion/rnscreens/legacy/Screen.kt +3 -1
  12. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStack.kt +2 -0
  13. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetBehaviorController.kt +49 -18
  14. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetDimensionsCoordinator.kt +65 -40
  15. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetKeyboardCoordinator.kt +101 -0
  16. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetNativeDismissCoordinator.kt +0 -6
  17. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetAvailableHeightProvider.kt +44 -0
  18. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetDialog.kt +35 -3
  19. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetDialogManager.kt +33 -106
  20. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/interfaces/FormSheetDialogEventEmitter.kt +2 -0
  21. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/model/FormSheetDetents.kt +61 -15
  22. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetAnimatorFactory.kt +8 -9
  23. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetDimmingManager.kt +115 -0
  24. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetDismissalOrigin.kt +22 -0
  25. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentation.kt +173 -0
  26. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentationManager.kt +141 -49
  27. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentationState.kt +1 -1
  28. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetStackRegistry.kt +28 -0
  29. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/react/event/FormSheetDismissEvent.kt +29 -0
  30. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/react/host/FormSheetHostEventEmitter.kt +7 -0
  31. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/react/host/FormSheetHostViewManager.kt +2 -0
  32. package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderApplicator.kt +369 -58
  33. package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderBackPressHandler.kt +11 -0
  34. package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderCoordinatorLayout.kt +143 -98
  35. package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderAppBarLayout.kt +155 -23
  36. package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderContentScrimDrawable.kt +36 -0
  37. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderCollapsedTitleGravityMode.kt +6 -0
  38. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfig.kt +170 -167
  39. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigViewManager.kt +385 -12
  40. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationObserver.kt +0 -4
  41. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationProviding.kt +32 -3
  42. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderDelegate.kt +0 -7
  43. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderInvalidationFlags.kt +7 -2
  44. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuApplicator.kt +27 -280
  45. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuController.kt +448 -0
  46. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuDelegate.kt +10 -0
  47. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuMapper.kt +28 -20
  48. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuConfig.kt +3 -55
  49. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuGroupMetadata.kt +1 -10
  50. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuItemConfig.kt +1 -3
  51. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuModel.kt +180 -0
  52. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementOptions.kt +57 -6
  53. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementRawUpdate.kt +3 -1
  54. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementUpdate.kt +1 -0
  55. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuIconResolver.kt +4 -3
  56. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuUpdateQueue.kt +23 -9
  57. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainer.kt +103 -3
  58. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackHost.kt +13 -0
  59. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackHostViewManager.kt +14 -0
  60. package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreen.kt +4 -0
  61. package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragment.kt +20 -1
  62. package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragmentDelegate.kt +12 -0
  63. package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceCoordinator.kt +2 -4
  64. package/android/src/main/java/com/swmansion/rnscreens/tabs/container/MenuHelpers.kt +5 -23
  65. package/android/src/main/java/com/swmansion/rnscreens/tabs/container/TabsContainer.kt +46 -25
  66. package/android/src/main/java/com/swmansion/rnscreens/tabs/host/TabsHost.kt +10 -0
  67. package/android/src/main/java/com/swmansion/rnscreens/tabs/host/TabsHostA11yCoordinator.kt +23 -2
  68. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreen.kt +17 -0
  69. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenFragment.kt +3 -0
  70. package/android/src/main/java/com/swmansion/rnscreens/utils/DimensionUtils.kt +7 -0
  71. package/android/src/main/java/com/swmansion/rnscreens/utils/StyleUtils.kt +22 -0
  72. package/android/src/main/res/base/layout/rns_collapsing_toolbar_large_available_space.xml +8 -0
  73. package/android/src/main/res/base/layout/rns_collapsing_toolbar_large_entire_space.xml +8 -0
  74. package/android/src/main/res/base/layout/rns_collapsing_toolbar_medium_available_space.xml +8 -0
  75. package/android/src/main/res/base/layout/rns_collapsing_toolbar_medium_entire_space.xml +8 -0
  76. package/cpp/legacy/RNSScreenRemovalListener.cpp +31 -1
  77. package/cpp/legacy/RNSScreenRemovalListener.h +18 -3
  78. package/ios/RNSEnums.h +2 -0
  79. package/ios/conversion/RNSConversions.h +7 -108
  80. package/ios/legacy/RNSFullWindowOverlay.mm +1 -1
  81. package/ios/legacy/RNSModalScreen.mm +1 -1
  82. package/ios/legacy/RNSScreen.mm +8 -19
  83. package/ios/legacy/RNSScreenStackHeaderConfig.mm +3 -1
  84. package/ios/legacy/RNSScreenStackHeaderSubview.mm +1 -1
  85. package/ios/legacy/RNSScreenWindowTraits.mm +5 -13
  86. package/ios/modals/form-sheet/RNSFormSheetConfigurationApplicator.h +1 -3
  87. package/ios/modals/form-sheet/RNSFormSheetConfigurationApplicator.mm +11 -22
  88. package/ios/modals/form-sheet/RNSFormSheetContentController.h +1 -1
  89. package/ios/modals/form-sheet/RNSFormSheetContentController.mm +7 -18
  90. package/ios/modals/form-sheet/RNSFormSheetHostComponentView.mm +4 -4
  91. package/ios/modals/form-sheet/RNSFormSheetHostShadowStateProxy.mm +2 -2
  92. package/ios/modals/form-sheet/RNSFormSheetUpdateFlags.h +3 -0
  93. package/ios/safe-area/RNSSafeAreaViewComponentView.mm +2 -2
  94. package/ios/{conversion → scroll-view-marker/conversion}/RNSConversions-ScrollViewMarker.mm +2 -3
  95. package/ios/split/RNSSplitAppearanceApplicator.h +4 -4
  96. package/ios/split/RNSSplitAppearanceApplicator.mm +53 -56
  97. package/ios/split/RNSSplitHostComponentEventEmitter.h +1 -0
  98. package/ios/split/RNSSplitHostComponentEventEmitter.mm +1 -1
  99. package/ios/split/RNSSplitHostComponentView.h +5 -15
  100. package/ios/split/RNSSplitHostComponentView.mm +55 -6
  101. package/ios/split/RNSSplitHostController.h +25 -6
  102. package/ios/split/RNSSplitHostController.mm +24 -92
  103. package/ios/split/RNSSplitHostProviders.h +67 -0
  104. package/ios/split/RNSSplitScreenComponentView.h +0 -39
  105. package/ios/split/RNSSplitScreenComponentView.mm +27 -10
  106. package/ios/split/RNSSplitScreenController.h +26 -3
  107. package/ios/split/RNSSplitScreenController.mm +13 -30
  108. package/ios/split/RNSSplitScreenShadowStateProxy.h +1 -40
  109. package/ios/split/RNSSplitScreenShadowStateProxy.mm +1 -26
  110. package/ios/split/conversion/RNSConversions-SplitView.h +56 -0
  111. package/ios/{conversion → split/conversion}/RNSConversions-SplitView.mm +4 -1
  112. package/ios/stack/conversion/RNSConversions-Stack.h +32 -0
  113. package/ios/stack/conversion/RNSConversions-Stack.mm +55 -0
  114. package/ios/stack/header/RNSStackHeaderAppearanceMapper.h +26 -0
  115. package/ios/stack/header/RNSStackHeaderAppearanceMapper.mm +88 -0
  116. package/ios/stack/header/RNSStackHeaderConfigComponentView.h +10 -1
  117. package/ios/stack/header/RNSStackHeaderConfigComponentView.mm +67 -35
  118. package/ios/stack/header/RNSStackHeaderConfigDataProviding.h +6 -0
  119. package/ios/stack/header/RNSStackHeaderConfigShadowStateProxy.mm +1 -1
  120. package/ios/stack/header/RNSStackHeaderIconResolver.mm +7 -0
  121. package/ios/stack/header/RNSStackHeaderItemComponentView.h +3 -1
  122. package/ios/stack/header/RNSStackHeaderItemComponentView.mm +15 -2
  123. package/ios/stack/header/RNSStackHeaderItemDataProviding.h +2 -0
  124. package/ios/stack/header/RNSStackHeaderItemShadowStateProxy.mm +1 -1
  125. package/ios/stack/header/RNSStackHeaderItemSpacerComponentView.mm +2 -1
  126. package/ios/stack/header/RNSStackHeaderMenuMapper.mm +4 -0
  127. package/ios/stack/host/RNSStackNavigationBar.h +25 -0
  128. package/ios/stack/host/RNSStackNavigationBar.mm +80 -0
  129. package/ios/stack/host/RNSStackNavigationBarCoordinator.h +8 -0
  130. package/ios/stack/host/RNSStackNavigationBarCoordinator.mm +11 -0
  131. package/ios/stack/host/RNSStackNavigationController.mm +13 -5
  132. package/ios/stack/host/RNSStackOperation.h +0 -2
  133. package/ios/stack/screen/RNSStackScreenComponentView.mm +9 -2
  134. package/ios/stack/screen/RNSStackScreenController.mm +12 -0
  135. package/ios/stack/screen/RNSStackScreenHeaderCoordinator.h +8 -0
  136. package/ios/stack/screen/RNSStackScreenHeaderCoordinator.mm +98 -16
  137. package/ios/tabs/RNSTabBarAppearanceCoordinator.mm +40 -9
  138. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryComponentView.h +1 -1
  139. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryComponentView.mm +1 -1
  140. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryContentComponentView.mm +1 -1
  141. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryEventEmitter.mm +1 -1
  142. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryShadowStateProxy.mm +1 -1
  143. package/ios/tabs/conversion/RNSConversions-Tabs.h +78 -0
  144. package/ios/{conversion → tabs/conversion}/RNSConversions-Tabs.mm +8 -71
  145. package/ios/tabs/extensions/RNSTabsHostComponentView+RNSImageLoader.h +1 -1
  146. package/ios/tabs/host/RNSTabsHostComponentView.h +2 -0
  147. package/ios/tabs/host/RNSTabsHostComponentView.mm +10 -3
  148. package/ios/tabs/host/RNSTabsHostEventEmitter.mm +1 -1
  149. package/ios/tabs/screen/RNSTabsScreenComponentView.mm +20 -5
  150. package/ios/utils/RNSDefines.h +3 -0
  151. package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js +34 -19
  152. package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js.map +1 -1
  153. package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js +33 -1
  154. package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
  155. package/lib/commonjs/components/stack/host/StackHost.js +9 -6
  156. package/lib/commonjs/components/stack/host/StackHost.js.map +1 -1
  157. package/lib/commonjs/components/stack/index.js.map +1 -1
  158. package/lib/commonjs/components/tabs/host/TabsHost.ios.js +1 -0
  159. package/lib/commonjs/components/tabs/host/TabsHost.ios.js.map +1 -1
  160. package/lib/commonjs/fabric/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
  161. package/lib/commonjs/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
  162. package/lib/commonjs/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
  163. package/lib/commonjs/fabric/stack/StackHostNativeComponent.js.map +1 -1
  164. package/lib/commonjs/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
  165. package/lib/module/components/stack/header/StackHeaderConfig.android.js +35 -20
  166. package/lib/module/components/stack/header/StackHeaderConfig.android.js.map +1 -1
  167. package/lib/module/components/stack/header/StackHeaderConfig.ios.js +34 -2
  168. package/lib/module/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
  169. package/lib/module/components/stack/host/StackHost.js +9 -6
  170. package/lib/module/components/stack/host/StackHost.js.map +1 -1
  171. package/lib/module/components/stack/index.js.map +1 -1
  172. package/lib/module/components/tabs/host/TabsHost.ios.js +1 -0
  173. package/lib/module/components/tabs/host/TabsHost.ios.js.map +1 -1
  174. package/lib/module/fabric/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
  175. package/lib/module/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
  176. package/lib/module/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
  177. package/lib/module/fabric/stack/StackHostNativeComponent.js.map +1 -1
  178. package/lib/module/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
  179. package/lib/typescript/components/modals/form-sheet/FormSheet.types.d.ts +1 -1
  180. package/lib/typescript/components/stack/header/StackHeaderConfig.android.d.ts.map +1 -1
  181. package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts +616 -21
  182. package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts.map +1 -1
  183. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.d.ts.map +1 -1
  184. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts +226 -2
  185. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts.map +1 -1
  186. package/lib/typescript/components/stack/header/StackHeaderConfig.types.d.ts +2 -2
  187. package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts +2 -0
  188. package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts.map +1 -1
  189. package/lib/typescript/components/stack/host/StackHost.d.ts +1 -1
  190. package/lib/typescript/components/stack/host/StackHost.d.ts.map +1 -1
  191. package/lib/typescript/components/stack/host/StackHost.types.d.ts +24 -0
  192. package/lib/typescript/components/stack/host/StackHost.types.d.ts.map +1 -1
  193. package/lib/typescript/components/stack/index.d.ts +2 -2
  194. package/lib/typescript/components/stack/index.d.ts.map +1 -1
  195. package/lib/typescript/components/tabs/host/TabsHost.ios.d.ts.map +1 -1
  196. package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts +16 -0
  197. package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts.map +1 -1
  198. package/lib/typescript/components/tabs/host/TabsHost.types.d.ts +3 -0
  199. package/lib/typescript/components/tabs/host/TabsHost.types.d.ts.map +1 -1
  200. package/lib/typescript/fabric/stack/StackHeaderConfigAndroidNativeComponent.d.ts +52 -0
  201. package/lib/typescript/fabric/stack/StackHeaderConfigAndroidNativeComponent.d.ts.map +1 -1
  202. package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts +25 -1
  203. package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts.map +1 -1
  204. package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts +2 -0
  205. package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts.map +1 -1
  206. package/lib/typescript/fabric/stack/StackHostNativeComponent.d.ts +3 -1
  207. package/lib/typescript/fabric/stack/StackHostNativeComponent.d.ts.map +1 -1
  208. package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts +1 -0
  209. package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts.map +1 -1
  210. package/package.json +8 -7
  211. package/react-native.config.js +3 -0
  212. package/src/components/modals/form-sheet/FormSheet.types.ts +1 -1
  213. package/src/components/stack/header/StackHeaderConfig.android.tsx +48 -23
  214. package/src/components/stack/header/StackHeaderConfig.android.types.ts +637 -21
  215. package/src/components/stack/header/StackHeaderConfig.ios.tsx +45 -1
  216. package/src/components/stack/header/StackHeaderConfig.ios.types.ts +238 -2
  217. package/src/components/stack/header/StackHeaderConfig.types.ts +2 -2
  218. package/src/components/stack/header/ios/StackHeaderItem.ios.types.ts +2 -0
  219. package/src/components/stack/host/StackHost.tsx +4 -2
  220. package/src/components/stack/host/StackHost.types.ts +26 -0
  221. package/src/components/stack/index.ts +6 -1
  222. package/src/components/tabs/host/TabsHost.ios.tsx +1 -0
  223. package/src/components/tabs/host/TabsHost.ios.types.ts +16 -0
  224. package/src/components/tabs/host/TabsHost.types.ts +3 -0
  225. package/src/fabric/stack/StackHeaderConfigAndroidNativeComponent.ts +78 -0
  226. package/src/fabric/stack/StackHeaderConfigIOSNativeComponent.ts +32 -0
  227. package/src/fabric/stack/StackHeaderItemIOSNativeComponent.ts +2 -0
  228. package/src/fabric/stack/StackHostNativeComponent.ts +11 -2
  229. package/src/fabric/tabs/TabsHostIOSNativeComponent.ts +1 -0
  230. package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingView.kt +0 -83
  231. package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingViewManager.kt +0 -89
  232. package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingViewPointerEvents.kt +0 -18
  233. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuSelectionController.kt +0 -83
  234. package/ios/conversion/RNSConversions-Stack.h +0 -32
  235. package/ios/conversion/RNSConversions-Stack.mm +0 -41
  236. package/ios/conversion/always_false.h +0 -31
  237. /package/ios/{conversion → scroll-view-marker/conversion}/RNSConversions-ScrollViewMarker.h +0 -0
@@ -1,5 +1,7 @@
1
1
  package com.swmansion.rnscreens.modals.formsheet.native.model
2
2
 
3
+ import kotlin.math.roundToInt
4
+
3
5
  internal class FormSheetDetents(
4
6
  rawDetents: List<Double>,
5
7
  ) {
@@ -31,47 +33,83 @@ internal class FormSheetDetents(
31
33
 
32
34
  private fun heightFractionAt(index: Int): Double = detents[index]
33
35
 
36
+ // Height of the sheet resting at the given detent, measured down to the bottom edge of the container.
34
37
  private fun heightAt(
35
38
  index: Int,
36
39
  containerHeight: Int,
37
- ): Int = (heightFractionAt(index) * containerHeight).toInt()
40
+ keyboardLift: Int,
41
+ ): Int = ((heightFractionAt(index) * containerHeight).roundToInt() + keyboardLift).coerceAtMost(containerHeight)
38
42
 
39
- internal fun firstHeight(containerHeight: Int): Int = heightAt(0, containerHeight)
43
+ /**
44
+ * Height handed to Material as `peekHeight`. Material treats the peek height as the content height above
45
+ * the bottom inset and adds that inset back (`BottomSheetBehavior.calculatePeekHeight`), while every
46
+ * other metric (`maxHeight`, `halfExpandedRatio`) describes the sheet down to the screen edge. The inset is
47
+ * subtracted here so the lowest detent is resolved against the same reference as the other ones.
48
+ * Material's inset covers the keyboard as well, so the lift is subtracted along with the system inset.
49
+ */
50
+ internal fun peekHeight(
51
+ containerHeight: Int,
52
+ bottomInset: Int,
53
+ keyboardLift: Int = 0,
54
+ ): Int = (heightAt(0, containerHeight, keyboardLift) - bottomInset - keyboardLift).coerceAtLeast(0)
40
55
 
41
- internal fun maxAllowedHeight(containerHeight: Int): Int = heightAt(count - 1, containerHeight)
56
+ internal fun maxAllowedHeight(
57
+ containerHeight: Int,
58
+ keyboardLift: Int = 0,
59
+ ): Int = heightAt(count - 1, containerHeight, keyboardLift)
42
60
 
43
61
  internal fun maxAllowedHeightForFitToContents(
44
62
  containerHeight: Int,
45
63
  contentHeight: Int,
46
64
  bottomInset: Int,
65
+ keyboardLift: Int = 0,
47
66
  ): Int {
48
67
  /*
49
- * We add the `bottomInset` to the `contentHeight` so that the Material BottomSheet
50
- * is laid out behind the system navigation bar. The sheet's container covers the insets,
68
+ * We add the `bottomInset` and the `keyboardLift` to the `contentHeight` so that the Material BottomSheet
69
+ * is laid out behind the system navigation bar or the keyboard. The sheet's container covers the insets,
51
70
  * while the RN content is strictly constrained to `contentHeight`.
52
71
  */
53
72
  if (contentHeight <= 0) {
54
73
  // Avoid collapsing the sheet before the React content has been laid out and measured.
55
74
  return containerHeight
56
75
  }
57
- return (contentHeight + bottomInset).coerceAtMost(containerHeight)
76
+ return (contentHeight + bottomInset + keyboardLift).coerceAtMost(containerHeight)
58
77
  }
59
78
 
60
- internal fun halfExpandedRatio(): Float {
79
+ /**
80
+ * Ratio of the middle detent's sheet height to the container height, handed to Material as `halfExpandedRatio`.
81
+ * Material resolves the half-expanded position from it as `(int) (parentHeight * (1 - ratio))`.
82
+ *
83
+ * The keyboard lift can push both the middle and the largest detent to the top of the container. Material needs
84
+ * the half-expanded position strictly below the expanded one so the middle detent is kept at least
85
+ * [MIDDLE_DETENT_MIN_GAP] px shorter than the largest one.
86
+ */
87
+ internal fun halfExpandedRatio(
88
+ containerHeight: Int,
89
+ keyboardLift: Int = 0,
90
+ ): Float {
61
91
  check(count == MAX_DETENTS) { "[RNScreens] Exactly $MAX_DETENTS detents are required for halfExpandedRatio." }
62
- return (heightFractionAt(1) / heightFractionAt(2)).toFloat()
92
+ val middleDetentHeight =
93
+ heightAt(1, containerHeight, keyboardLift)
94
+ .coerceAtMost(maxAllowedHeight(containerHeight, keyboardLift) - MIDDLE_DETENT_MIN_GAP)
95
+ .coerceAtLeast(1)
96
+ return middleDetentHeight.toFloat() / containerHeight
63
97
  }
64
98
 
65
99
  internal fun expandedOffsetFromTop(
66
100
  containerHeight: Int,
67
101
  topInset: Int = 0,
102
+ keyboardLift: Int = 0,
68
103
  ): Int {
69
104
  check(count == MAX_DETENTS) { "[RNScreens] Exactly $MAX_DETENTS detents are required for expandedOffsetFromTop." }
70
- return ((1 - heightFractionAt(2)) * containerHeight).toInt() + topInset
105
+ return largestDetentTopOffset(containerHeight, keyboardLift) + topInset
71
106
  }
72
107
 
73
108
  // Distance from the top of the window to the top of the largest detent's surface.
74
- private fun largestDetentTopOffset(containerHeight: Int): Int = containerHeight - maxAllowedHeight(containerHeight)
109
+ private fun largestDetentTopOffset(
110
+ containerHeight: Int,
111
+ keyboardLift: Int,
112
+ ): Int = containerHeight - maxAllowedHeight(containerHeight, keyboardLift)
75
113
 
76
114
  /**
77
115
  * Material's BottomSheetDialog dynamically applies padding when its content overlaps
@@ -83,24 +121,32 @@ internal class FormSheetDetents(
83
121
  topInset: Int,
84
122
  bottomInset: Int,
85
123
  contentHeight: Int = 0,
124
+ keyboardLift: Int = 0,
86
125
  ): Int {
126
+ val bottomPadding = bottomInset + keyboardLift
127
+
87
128
  if (isFitToContents) {
88
129
  // Until we have a measured content height, fall back to a safe-area height so Yoga can lay out.
89
130
  if (contentHeight <= 0) {
90
- return (containerHeight - topInset - bottomInset).coerceAtLeast(0)
131
+ return (containerHeight - topInset - bottomPadding).coerceAtLeast(0)
91
132
  }
92
- return contentHeight.coerceAtMost(containerHeight)
133
+ // Content taller than the safe area is aligned with it.
134
+ return contentHeight.coerceAtMost((containerHeight - topInset - bottomPadding).coerceAtLeast(0))
93
135
  }
94
136
 
95
- // Bottom inset is always fully subtracted because the sheet is in its dedicated window and it's
137
+ // Bottom padding is always fully subtracted because the sheet is in its dedicated window and it's
96
138
  // anchored to the bottom.
97
139
  // Top inset is subtracted only by the amount the sheet actually overlaps it.
98
- val topOverlap = (topInset - largestDetentTopOffset(containerHeight)).coerceAtLeast(0)
99
- return (maxAllowedHeight(containerHeight) - topOverlap - bottomInset).coerceAtLeast(0)
140
+ val topOverlap = (topInset - largestDetentTopOffset(containerHeight, keyboardLift)).coerceAtLeast(0)
141
+ return (maxAllowedHeight(containerHeight, keyboardLift) - topOverlap - bottomPadding).coerceAtLeast(0)
100
142
  }
101
143
 
102
144
  companion object {
103
145
  const val MAX_DETENTS = 3
104
146
  const val FIT_TO_CONTENTS_DETENT_VALUE = -1.0
147
+
148
+ // Material resolves the half-expanded position in float precision and the truncation to int can eat 1 px
149
+ // which would put the middle detent back on the expanded position.
150
+ private const val MIDDLE_DETENT_MIN_GAP = 2
105
151
  }
106
152
  }
@@ -4,11 +4,9 @@ import android.animation.Animator
4
4
  import android.animation.AnimatorSet
5
5
  import android.animation.ValueAnimator
6
6
  import android.view.View
7
- import androidx.core.animation.doOnStart
8
- import com.swmansion.rnscreens.modals.dimmingview.DimmingViewManager
9
7
 
10
8
  internal class FormSheetAnimatorFactory(
11
- private val dimmingManager: DimmingViewManager,
9
+ private val dimmingManager: FormSheetDimmingManager,
12
10
  ) {
13
11
  // TODO: @t0maboro - consider exposing as a prop
14
12
  val animationDuration = 250L
@@ -18,7 +16,7 @@ internal class FormSheetAnimatorFactory(
18
16
  isInterrupting: Boolean = false,
19
17
  ): Animator {
20
18
  val startY = if (isInterrupting) view.translationY else view.height.toFloat()
21
- val startAlpha = if (isInterrupting) dimmingManager.dimmingViewAlpha else 0f
19
+ val startAlpha = if (isInterrupting) dimmingManager.dimmingAlpha else 0f
22
20
 
23
21
  val slideAnimator =
24
22
  ValueAnimator.ofFloat(startY, 0f).apply {
@@ -30,14 +28,13 @@ internal class FormSheetAnimatorFactory(
30
28
  val alphaAnimator =
31
29
  ValueAnimator.ofFloat(startAlpha, dimmingManager.maxAlpha).apply {
32
30
  addUpdateListener { animation ->
33
- dimmingManager.dimmingViewAlpha = animation.animatedValue as Float
31
+ dimmingManager.dimmingAlpha = animation.animatedValue as Float
34
32
  }
35
33
  }
36
34
 
37
35
  return AnimatorSet().apply {
38
36
  playTogether(slideAnimator, alphaAnimator)
39
37
  duration = animationDuration
40
- doOnStart { view.translationY = startY }
41
38
  }
42
39
  }
43
40
 
@@ -45,8 +42,10 @@ internal class FormSheetAnimatorFactory(
45
42
  view: View,
46
43
  isInterrupting: Boolean = false,
47
44
  ): Animator {
48
- val startY = if (isInterrupting) view.translationY else 0f
49
- val startAlpha = if (isInterrupting) dimmingManager.dimmingViewAlpha else dimmingManager.maxAlpha
45
+ // Always leave from the current translation: besides an interrupted enter animation, the sheet
46
+ // may be mid-way through tracking the keyboard animation when the dismissal starts.
47
+ val startY = view.translationY
48
+ val startAlpha = if (isInterrupting) dimmingManager.dimmingAlpha else dimmingManager.maxAlpha
50
49
 
51
50
  val slideAnimator =
52
51
  ValueAnimator.ofFloat(startY, view.height.toFloat()).apply {
@@ -58,7 +57,7 @@ internal class FormSheetAnimatorFactory(
58
57
  val alphaAnimator =
59
58
  ValueAnimator.ofFloat(startAlpha, 0f).apply {
60
59
  addUpdateListener { animation ->
61
- dimmingManager.dimmingViewAlpha = animation.animatedValue as Float
60
+ dimmingManager.dimmingAlpha = animation.animatedValue as Float
62
61
  }
63
62
  }
64
63
 
@@ -0,0 +1,115 @@
1
+ package com.swmansion.rnscreens.modals.formsheet.native.presentation
2
+
3
+ import android.app.Activity
4
+ import android.content.Context
5
+ import android.content.ContextWrapper
6
+ import android.graphics.Color
7
+ import android.graphics.drawable.ColorDrawable
8
+ import android.util.Log
9
+ import android.view.View
10
+ import com.facebook.react.bridge.ReactContext
11
+ import com.google.android.material.bottomsheet.BottomSheetBehavior
12
+ import kotlin.math.roundToInt
13
+
14
+ internal class FormSheetDimmingManager(
15
+ private val context: Context,
16
+ ) {
17
+ // TODO: @t0maboro - consider exposing as a prop
18
+ internal val maxAlpha: Float = MAX_DIMMING_ALPHA_FRACTION
19
+
20
+ internal var isTransitionAnimationRunning: Boolean = false
21
+
22
+ // The drawable rendering this sheet's dimming, added to the overlay of dimmingHost which is
23
+ // Material's `design_bottom_sheet` component or the DecorView of the main application content.
24
+ private var dimmingDrawable: ColorDrawable? = null
25
+ private var dimmingHost: View? = null
26
+
27
+ private val dimmingHostLayoutListener =
28
+ View.OnLayoutChangeListener { view, _, _, _, _, _, _, _, _ ->
29
+ dimmingDrawable?.setBounds(0, 0, view.width, view.height)
30
+ }
31
+
32
+ internal var dimmingAlpha: Float = 0f
33
+ set(value) {
34
+ field = value
35
+ // We're transforming the value, because View.alpha is within [0.0, 1.0] range,
36
+ // while Drawable.alpha is an Integer in [0, 255] range.
37
+ dimmingDrawable?.alpha = (value * MAX_DRAWABLE_OVERLAY_ALPHA).roundToInt().coerceIn(0, MAX_DRAWABLE_OVERLAY_ALPHA)
38
+ }
39
+
40
+ /**
41
+ * Called when the sheet is presented. [belowSheetView] is the `design_bottom_sheet` of the
42
+ * sheet directly below in the stack, or null when this sheet is the bottom-most one - the
43
+ * dimming then goes onto the decor of the activity hosting the sheets.
44
+ */
45
+ internal fun attachDimming(belowSheetView: View?) {
46
+ val host = belowSheetView ?: resolveActivityDecorView()
47
+ if (host == null) {
48
+ Log.e(TAG, "[RNScreens] Neither a sheet below nor an activity decor found; the sheet will present undimmed.")
49
+ return
50
+ }
51
+
52
+ dimmingHost = host
53
+ dimmingDrawable =
54
+ ColorDrawable(Color.BLACK).apply {
55
+ alpha = 0
56
+ setBounds(0, 0, host.width, host.height)
57
+ }
58
+ host.overlay.add(dimmingDrawable!!)
59
+ host.addOnLayoutChangeListener(dimmingHostLayoutListener)
60
+ }
61
+
62
+ internal fun detachDimming() {
63
+ dimmingHost?.removeOnLayoutChangeListener(dimmingHostLayoutListener)
64
+ dimmingDrawable?.let { dimmingHost?.overlay?.remove(it) }
65
+ dimmingHost = null
66
+ dimmingDrawable = null
67
+ }
68
+
69
+ internal fun attachToBehavior(behavior: BottomSheetBehavior<*>) {
70
+ behavior.addBottomSheetCallback(
71
+ object : BottomSheetBehavior.BottomSheetCallback() {
72
+ override fun onStateChanged(
73
+ bottomSheet: View,
74
+ newState: Int,
75
+ ) = Unit
76
+
77
+ override fun onSlide(
78
+ bottomSheet: View,
79
+ slideOffset: Float,
80
+ ) {
81
+ // Prevent system updates from overriding alpha while running custom enter/exit animation.
82
+ // When initialDetentIndex is snapping to a high detent, BottomSheetBehavior fires onSlide
83
+ // events that conflict with our manual alpha animator, causing the backdrop to flash.
84
+ if (isTransitionAnimationRunning) {
85
+ return
86
+ }
87
+
88
+ val fraction = if (slideOffset >= 0) 1f else 1f + slideOffset
89
+ dimmingAlpha = fraction * maxAlpha
90
+ }
91
+ },
92
+ )
93
+ }
94
+
95
+ private fun resolveActivityDecorView(): View? {
96
+ var current: Context? = context
97
+ while (current is ContextWrapper) {
98
+ if (current is Activity) {
99
+ return current.window?.decorView
100
+ }
101
+ if (current is ReactContext) {
102
+ return current.currentActivity?.window?.decorView
103
+ }
104
+ current = current.baseContext
105
+ }
106
+ return null
107
+ }
108
+
109
+ companion object {
110
+ const val TAG = "FormSheetDimmingManager"
111
+
112
+ private const val MAX_DIMMING_ALPHA_FRACTION = 0.3f
113
+ private const val MAX_DRAWABLE_OVERLAY_ALPHA = 255
114
+ }
115
+ }
@@ -0,0 +1,22 @@
1
+ package com.swmansion.rnscreens.modals.formsheet.native.presentation
2
+
3
+ internal enum class FormSheetDismissalOrigin {
4
+ /**
5
+ * No dismissal request is pending. Completing a dismissal with this
6
+ * origin should report an error.
7
+ */
8
+ UNSPECIFIED,
9
+
10
+ /**
11
+ * The dismissal was requested from JS by setting `isOpen = false`.
12
+ * Emits `onDismiss`.
13
+ */
14
+ PROGRAMMATIC_JS,
15
+
16
+ /**
17
+ * The dismissal was initiated by the user (swiping the sheet down,
18
+ * triggering the native back button, or tapping the backdrop).
19
+ * Emits `onNativeDismiss`.
20
+ */
21
+ USER,
22
+ }
@@ -0,0 +1,173 @@
1
+ package com.swmansion.rnscreens.modals.formsheet.native.presentation
2
+
3
+ import android.content.Context
4
+ import android.util.Log
5
+ import android.view.ViewGroup
6
+ import android.widget.FrameLayout
7
+ import com.google.android.material.bottomsheet.BottomSheetBehavior
8
+ import com.swmansion.rnscreens.modals.formsheet.native.coordinator.FormSheetAppearanceCoordinator
9
+ import com.swmansion.rnscreens.modals.formsheet.native.coordinator.FormSheetBehaviorController
10
+ import com.swmansion.rnscreens.modals.formsheet.native.coordinator.FormSheetDimensionsCoordinator
11
+ import com.swmansion.rnscreens.modals.formsheet.native.coordinator.FormSheetKeyboardCoordinator
12
+ import com.swmansion.rnscreens.modals.formsheet.native.coordinator.FormSheetNativeDismissCoordinator
13
+ import com.swmansion.rnscreens.modals.formsheet.native.core.FormSheetContainer
14
+ import com.swmansion.rnscreens.modals.formsheet.native.core.FormSheetDialog
15
+ import com.swmansion.rnscreens.modals.formsheet.native.model.FormSheetConfig
16
+ import com.swmansion.rnscreens.modals.formsheet.native.model.FormSheetDetents
17
+
18
+ internal class FormSheetPresentation(
19
+ themedContext: Context,
20
+ private val container: FormSheetContainer,
21
+ private val callbacks: Callbacks,
22
+ ) {
23
+ internal interface Callbacks {
24
+ fun onDetentChanged(index: Int)
25
+
26
+ fun onNativeDismissAllowed()
27
+
28
+ fun onNativeDismissPrevented()
29
+ }
30
+
31
+ internal val dialog =
32
+ FormSheetDialog(themedContext).apply {
33
+ setContentView(container)
34
+ // Backdrop taps are handled by Material's `touch_outside` view, which calls `cancel()`,
35
+ // intercepted by FormSheetDialog.cancelRequestInterceptor, that's responsible for
36
+ // preventNativeDismiss handling.
37
+ setCanceledOnTouchOutside(true)
38
+ }
39
+
40
+ internal val bottomSheetView: FrameLayout? = dialog.findViewById(com.google.android.material.R.id.design_bottom_sheet)
41
+
42
+ internal val sheetBehavior: BottomSheetBehavior<FrameLayout>?
43
+ get() = bottomSheetView?.let { BottomSheetBehavior.from(it) }
44
+
45
+ private val behaviorController =
46
+ bottomSheetView?.let {
47
+ FormSheetBehaviorController(it) { index -> callbacks.onDetentChanged(index) }
48
+ }
49
+
50
+ private val appearanceCoordinator =
51
+ FormSheetAppearanceCoordinator(
52
+ bottomSheetView = bottomSheetView,
53
+ )
54
+
55
+ private val dimensionsCoordinator =
56
+ FormSheetDimensionsCoordinator(
57
+ dialog = dialog,
58
+ container = container,
59
+ behaviorController = behaviorController,
60
+ )
61
+
62
+ private val keyboardCoordinator =
63
+ bottomSheetView?.let {
64
+ FormSheetKeyboardCoordinator(bottomSheetView = it)
65
+ }
66
+
67
+ private val nativeDismissCoordinator =
68
+ FormSheetNativeDismissCoordinator(
69
+ dialog = dialog,
70
+ behaviorController = behaviorController,
71
+ onDismissAllowed = callbacks::onNativeDismissAllowed,
72
+ onDismissPrevented = callbacks::onNativeDismissPrevented,
73
+ )
74
+
75
+ init {
76
+ nativeDismissCoordinator.setup()
77
+ appearanceCoordinator.setup()
78
+ dimensionsCoordinator.setup()
79
+ keyboardCoordinator?.setup()
80
+ behaviorController?.setup()
81
+ }
82
+
83
+ /**
84
+ * Enables tracking the keyboard animation. Expected to be on only while the sheet rests in the
85
+ * PRESENTED state - the enter / exit animators own `translationY` otherwise.
86
+ */
87
+ internal fun setKeyboardTrackingEnabled(enabled: Boolean) {
88
+ keyboardCoordinator?.isTrackingEnabled = enabled
89
+ }
90
+
91
+ internal fun onContentHeightChanged(height: Int) {
92
+ dimensionsCoordinator.onContentHeightChanged(height)
93
+ }
94
+
95
+ internal fun applyInitialConfig(
96
+ config: FormSheetConfig,
97
+ contentHeight: Int,
98
+ ) {
99
+ onContentHeightChanged(contentHeight)
100
+ dimensionsCoordinator.updateFormSheetDimensions(
101
+ resolveDetents(config.detents),
102
+ config.initialDetentIndex,
103
+ applyInitialDetent = true,
104
+ )
105
+ container.setGrabberVisible(config.prefersGrabberVisible)
106
+ appearanceCoordinator.updateCornerRadius(config.preferredCornerRadius)
107
+ appearanceCoordinator.updateBackgroundColor(config.nativeContainerBackgroundColor)
108
+ nativeDismissCoordinator.shouldPreventDismiss = config.shouldPreventNativeDismiss
109
+ }
110
+
111
+ internal fun applyConfigUpdate(
112
+ oldConfig: FormSheetConfig,
113
+ newConfig: FormSheetConfig,
114
+ ) {
115
+ if (oldConfig.detents != newConfig.detents) {
116
+ dimensionsCoordinator.updateFormSheetDimensions(
117
+ resolveDetents(newConfig.detents),
118
+ newConfig.initialDetentIndex,
119
+ )
120
+ }
121
+
122
+ if (oldConfig.prefersGrabberVisible != newConfig.prefersGrabberVisible) {
123
+ container.setGrabberVisible(newConfig.prefersGrabberVisible)
124
+ }
125
+
126
+ if (oldConfig.preferredCornerRadius != newConfig.preferredCornerRadius) {
127
+ appearanceCoordinator.updateCornerRadius(newConfig.preferredCornerRadius)
128
+ }
129
+
130
+ if (oldConfig.nativeContainerBackgroundColor != newConfig.nativeContainerBackgroundColor) {
131
+ appearanceCoordinator.updateBackgroundColor(newConfig.nativeContainerBackgroundColor)
132
+ }
133
+
134
+ if (oldConfig.shouldPreventNativeDismiss != newConfig.shouldPreventNativeDismiss) {
135
+ nativeDismissCoordinator.shouldPreventDismiss = newConfig.shouldPreventNativeDismiss
136
+ }
137
+ }
138
+
139
+ internal fun destroy() {
140
+ behaviorController?.destroy()
141
+ nativeDismissCoordinator.destroy()
142
+ dimensionsCoordinator.destroy()
143
+ keyboardCoordinator?.destroy()
144
+
145
+ dialog.setOnShowListener(null)
146
+ dialog.dismiss()
147
+
148
+ // The FormSheetContainer outlives the presentation - its lifecycle is managed by FormSheetDialogManager
149
+ // which is tied with the React's Host lifecycle.
150
+ (container.parent as? ViewGroup)?.removeView(container)
151
+ }
152
+
153
+ private fun resolveDetents(rawDetents: List<Double>): FormSheetDetents {
154
+ if (rawDetents.isEmpty()) {
155
+ return FormSheetDetents(listOf(LARGE_DETENT_FRACTION))
156
+ }
157
+
158
+ return try {
159
+ FormSheetDetents(rawDetents)
160
+ } catch (e: IllegalArgumentException) {
161
+ Log.e(
162
+ "[RNScreens]",
163
+ "Invalid FormSheet detents: $rawDetents. Falling back to large detent.",
164
+ e,
165
+ )
166
+ FormSheetDetents(listOf(LARGE_DETENT_FRACTION))
167
+ }
168
+ }
169
+
170
+ companion object {
171
+ private const val LARGE_DETENT_FRACTION = 1.0
172
+ }
173
+ }