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

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 (456) hide show
  1. package/Package.swift +87 -0
  2. package/README.md +0 -11
  3. package/android/build.gradle +1 -0
  4. package/android/consumer-rules.pro +3 -0
  5. package/android/src/fabric/java/com/swmansion/rnscreens/legacy/FabricEnabledHeaderConfigViewGroup.kt +2 -0
  6. package/android/src/fabric/java/com/swmansion/rnscreens/legacy/FabricEnabledHeaderSubviewViewGroup.kt +2 -0
  7. package/android/src/fabric/java/com/swmansion/rnscreens/legacy/FabricEnabledViewGroup.kt +18 -3
  8. package/android/src/main/cpp/NativeProxy.cpp +30 -6
  9. package/android/src/main/cpp/NativeProxy.h +7 -1
  10. package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +2 -2
  11. package/android/src/main/java/com/swmansion/rnscreens/ScreensModule.kt +7 -3
  12. package/android/src/main/java/com/swmansion/rnscreens/common/colorscheme/ColorSchemeCoordinator.kt +11 -1
  13. package/android/src/main/java/com/swmansion/rnscreens/common/container/Container.kt +17 -0
  14. package/android/src/main/java/com/swmansion/rnscreens/common/container/ContainerItem.kt +13 -1
  15. package/android/src/main/java/com/swmansion/rnscreens/common/text/ReactTextAppearance.kt +62 -0
  16. package/android/src/main/java/com/swmansion/rnscreens/common/text/TextAppearance.kt +20 -0
  17. package/android/src/main/java/com/swmansion/rnscreens/common/text/TextAppearanceDefaults.kt +65 -0
  18. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetAppearanceCoordinator.kt +2 -2
  19. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetBehaviorController.kt +51 -20
  20. package/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/coordinator/FormSheetDimensionsCoordinator.kt +159 -0
  21. package/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/coordinator/FormSheetKeyboardCoordinator.kt +101 -0
  22. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/coordinator/FormSheetNativeDismissCoordinator.kt +2 -8
  23. package/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/dialog/FormSheetAvailableHeightProvider.kt +44 -0
  24. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native/core → core/modals/formsheet/dialog}/FormSheetContainer.kt +1 -1
  25. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native/core → core/modals/formsheet/dialog}/FormSheetDialog.kt +36 -4
  26. package/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/dialog/FormSheetDialogManager.kt +107 -0
  27. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/interfaces/FormSheetContentSizeChangeDelegate.kt +1 -1
  28. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/interfaces/FormSheetContentSizeChangeProvider.kt +1 -1
  29. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/interfaces/FormSheetDialogEventEmitter.kt +3 -1
  30. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/model/FormSheetConfig.kt +1 -1
  31. package/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/model/FormSheetDetents.kt +152 -0
  32. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetAnimatorFactory.kt +9 -10
  33. package/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/presentation/FormSheetDimmingManager.kt +103 -0
  34. package/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/presentation/FormSheetDismissalOrigin.kt +22 -0
  35. package/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/presentation/FormSheetPresentation.kt +173 -0
  36. package/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/presentation/FormSheetPresentationManager.kt +290 -0
  37. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/native → core/modals/formsheet}/presentation/FormSheetPresentationState.kt +2 -2
  38. package/android/src/main/java/com/swmansion/rnscreens/core/modals/formsheet/presentation/FormSheetStackRegistry.kt +28 -0
  39. package/android/src/main/java/com/swmansion/rnscreens/ext/FragmentManagerExt.kt +6 -0
  40. package/android/src/main/java/com/swmansion/rnscreens/helpers/FragmentManagerHelper.kt +0 -3
  41. package/android/src/main/java/com/swmansion/rnscreens/helpers/NoTintDrawable.kt +31 -0
  42. package/android/src/main/java/com/swmansion/rnscreens/helpers/PropIconResolver.kt +17 -23
  43. package/android/src/main/java/com/swmansion/rnscreens/helpers/ViewFinder.kt +0 -17
  44. package/android/src/main/java/com/swmansion/rnscreens/legacy/Screen.kt +32 -3
  45. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenContainer.kt +35 -3
  46. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStack.kt +15 -0
  47. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStackHeaderConfig.kt +7 -1
  48. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStackHeaderSubviewManager.kt +7 -0
  49. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStackViewManager.kt +6 -0
  50. package/android/src/main/java/com/swmansion/rnscreens/legacy/bottomsheet/DimmingView.kt +2 -0
  51. package/android/src/main/java/com/swmansion/rnscreens/legacy/stack/views/ScreensCoordinatorLayout.kt +2 -0
  52. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/contentwrapper/FormSheetContentWrapper.kt +3 -3
  53. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/contentwrapper/FormSheetContentWrapperViewManager.kt +1 -1
  54. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetDetentChangedEvent.kt +1 -1
  55. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetDidAppearEvent.kt +1 -1
  56. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetDidDisappearEvent.kt +1 -1
  57. package/android/src/main/java/com/swmansion/rnscreens/react/modals/formsheet/event/FormSheetDismissEvent.kt +29 -0
  58. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetNativeDismissEvent.kt +1 -1
  59. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetNativeDismissPreventedEvent.kt +1 -1
  60. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetSyncFlushEvent.kt +1 -1
  61. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetWillAppearEvent.kt +1 -1
  62. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/event/FormSheetWillDisappearEvent.kt +1 -1
  63. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/host/FormSheetContentView.kt +3 -3
  64. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/host/FormSheetHost.kt +5 -3
  65. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/host/FormSheetHostEventEmitter.kt +17 -10
  66. package/android/src/main/java/com/swmansion/rnscreens/{modals/formsheet/react → react/modals/formsheet}/host/FormSheetHostViewManager.kt +11 -9
  67. package/android/src/main/java/com/swmansion/rnscreens/safearea/OnInterfaceInsetsChangeListener.kt +15 -0
  68. package/android/src/main/java/com/swmansion/rnscreens/safearea/SafeAreaProvider.kt +8 -8
  69. package/android/src/main/java/com/swmansion/rnscreens/safearea/SafeAreaView.kt +3 -2
  70. package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderApplicator.kt +360 -101
  71. package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderBackPressHandler.kt +11 -0
  72. package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderCoordinatorLayout.kt +211 -113
  73. package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderAppBarLayout.kt +156 -24
  74. package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderContentScrimDrawable.kt +36 -0
  75. package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderToolbar.kt +74 -0
  76. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderCollapsedTitleGravityMode.kt +6 -0
  77. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfig.kt +162 -228
  78. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigViewManager.kt +385 -12
  79. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationObserver.kt +5 -6
  80. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationProviding.kt +29 -6
  81. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderDelegate.kt +0 -7
  82. package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderInvalidationFlags.kt +8 -7
  83. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuApplicator.kt +27 -280
  84. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuController.kt +448 -0
  85. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuDelegate.kt +10 -0
  86. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuMapper.kt +28 -20
  87. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuConfig.kt +3 -55
  88. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuGroupMetadata.kt +1 -10
  89. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuItemConfig.kt +1 -3
  90. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuModel.kt +180 -0
  91. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementOptions.kt +57 -6
  92. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementRawUpdate.kt +3 -1
  93. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementUpdate.kt +1 -0
  94. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuIconResolver.kt +4 -3
  95. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuUpdateQueue.kt +23 -9
  96. package/android/src/main/java/com/swmansion/rnscreens/stack/host/FragmentOperation.kt +1 -0
  97. package/android/src/main/java/com/swmansion/rnscreens/stack/host/FragmentOperationExecutor.kt +2 -1
  98. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainer.kt +159 -21
  99. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainerDelegate.kt +7 -0
  100. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainerUpdateCoordinator.kt +5 -3
  101. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackHost.kt +27 -5
  102. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackHostViewManager.kt +14 -0
  103. package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackUpdateBatchStateProviding.kt +10 -0
  104. package/android/src/main/java/com/swmansion/rnscreens/stack/screen/PreventNativeDismissCallback.kt +2 -2
  105. package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreen.kt +4 -0
  106. package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragment.kt +50 -10
  107. package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragmentDelegate.kt +12 -0
  108. package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceApplicator.kt +106 -10
  109. package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceCoordinator.kt +46 -7
  110. package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceModel.kt +3 -0
  111. package/android/src/main/java/com/swmansion/rnscreens/tabs/container/CustomBottomNavigationView.kt +21 -1
  112. package/android/src/main/java/com/swmansion/rnscreens/tabs/container/MenuHelpers.kt +5 -23
  113. package/android/src/main/java/com/swmansion/rnscreens/tabs/container/TabsContainer.kt +72 -36
  114. package/android/src/main/java/com/swmansion/rnscreens/tabs/container/TabsContainerDelegate.kt +11 -0
  115. package/android/src/main/java/com/swmansion/rnscreens/tabs/host/TabsHost.kt +22 -2
  116. package/android/src/main/java/com/swmansion/rnscreens/tabs/host/TabsHostA11yCoordinator.kt +23 -2
  117. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreen.kt +58 -17
  118. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenDelegate.kt +6 -0
  119. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenFragment.kt +3 -0
  120. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenIcon.kt +82 -0
  121. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenIconTinting.kt +17 -0
  122. package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenViewManager.kt +26 -19
  123. package/android/src/main/java/com/swmansion/rnscreens/utils/DimensionUtils.kt +20 -6
  124. package/android/src/main/java/com/swmansion/rnscreens/utils/RNSLog.kt +22 -6
  125. package/android/src/main/java/com/swmansion/rnscreens/utils/StyleUtils.kt +22 -0
  126. package/android/src/main/res/base/layout/rns_collapsing_toolbar_large_available_space.xml +8 -0
  127. package/android/src/main/res/base/layout/rns_collapsing_toolbar_large_entire_space.xml +8 -0
  128. package/android/src/main/res/base/layout/rns_collapsing_toolbar_medium_available_space.xml +8 -0
  129. package/android/src/main/res/base/layout/rns_collapsing_toolbar_medium_entire_space.xml +8 -0
  130. package/cpp/legacy/RNSScreenRemovalListener.cpp +31 -1
  131. package/cpp/legacy/RNSScreenRemovalListener.h +18 -3
  132. package/ios/RNSEnums.h +16 -1
  133. package/ios/conversion/RNSConversions.h +7 -108
  134. package/ios/helpers/image/RNSImageLoadingHelper.h +7 -0
  135. package/ios/helpers/image/RNSImageLoadingHelper.mm +9 -0
  136. package/ios/legacy/RNSBarButtonItem.mm +15 -0
  137. package/ios/legacy/RNSConvert.h +6 -0
  138. package/ios/legacy/RNSConvert.mm +17 -0
  139. package/ios/legacy/RNSFullWindowOverlay.mm +1 -1
  140. package/ios/legacy/RNSModalScreen.mm +1 -1
  141. package/ios/legacy/RNSNavigationBar.h +31 -0
  142. package/ios/legacy/RNSNavigationBar.mm +15 -0
  143. package/ios/legacy/RNSScreen.mm +130 -31
  144. package/ios/legacy/RNSScreenContentWrapper.mm +4 -1
  145. package/ios/legacy/RNSScreenStack.mm +202 -15
  146. package/ios/legacy/RNSScreenStackHeaderConfig.h +30 -0
  147. package/ios/legacy/RNSScreenStackHeaderConfig.mm +72 -17
  148. package/ios/legacy/RNSScreenStackHeaderSubview.mm +34 -1
  149. package/ios/legacy/RNSScreenWindowTraits.mm +5 -13
  150. package/ios/modals/form-sheet/RNSFormSheetConfigurationApplicator.h +1 -3
  151. package/ios/modals/form-sheet/RNSFormSheetConfigurationApplicator.mm +11 -22
  152. package/ios/modals/form-sheet/RNSFormSheetContentController.h +1 -1
  153. package/ios/modals/form-sheet/RNSFormSheetContentController.mm +7 -18
  154. package/ios/modals/form-sheet/RNSFormSheetHostComponentView.mm +4 -4
  155. package/ios/modals/form-sheet/RNSFormSheetHostShadowStateProxy.mm +2 -2
  156. package/ios/modals/form-sheet/RNSFormSheetUpdateFlags.h +3 -0
  157. package/ios/safe-area/RNSSafeAreaViewComponentView.mm +3 -14
  158. package/ios/{conversion → scroll-view-marker/conversion}/RNSConversions-ScrollViewMarker.mm +2 -3
  159. package/ios/split/RNSSplitAppearanceApplicator.h +4 -4
  160. package/ios/split/RNSSplitAppearanceApplicator.mm +53 -56
  161. package/ios/split/RNSSplitHostComponentEventEmitter.h +1 -0
  162. package/ios/split/RNSSplitHostComponentEventEmitter.mm +1 -1
  163. package/ios/split/RNSSplitHostComponentView.h +5 -15
  164. package/ios/split/RNSSplitHostComponentView.mm +55 -6
  165. package/ios/split/RNSSplitHostController.h +25 -6
  166. package/ios/split/RNSSplitHostController.mm +24 -92
  167. package/ios/split/RNSSplitHostProviders.h +67 -0
  168. package/ios/split/RNSSplitScreenComponentView.h +0 -39
  169. package/ios/split/RNSSplitScreenComponentView.mm +27 -10
  170. package/ios/split/RNSSplitScreenController.h +26 -3
  171. package/ios/split/RNSSplitScreenController.mm +13 -30
  172. package/ios/split/RNSSplitScreenShadowStateProxy.h +1 -40
  173. package/ios/split/RNSSplitScreenShadowStateProxy.mm +1 -26
  174. package/ios/split/conversion/RNSConversions-SplitView.h +56 -0
  175. package/ios/{conversion → split/conversion}/RNSConversions-SplitView.mm +4 -1
  176. package/ios/stack/conversion/RNSConversions-Stack.h +62 -0
  177. package/ios/stack/conversion/RNSConversions-Stack.mm +134 -0
  178. package/ios/stack/header/RNSHeaderItemVisibilityPriority.h +9 -0
  179. package/ios/stack/header/RNSStackHeaderAppearanceMapper.h +26 -0
  180. package/ios/stack/header/RNSStackHeaderAppearanceMapper.mm +88 -0
  181. package/ios/stack/header/RNSStackHeaderConfigComponentView.h +14 -1
  182. package/ios/stack/header/RNSStackHeaderConfigComponentView.mm +103 -37
  183. package/ios/stack/header/RNSStackHeaderConfigDataProviding.h +11 -0
  184. package/ios/stack/header/RNSStackHeaderConfigShadowStateProxy.mm +1 -1
  185. package/ios/stack/header/RNSStackHeaderContentFactory.mm +3 -0
  186. package/ios/stack/header/RNSStackHeaderIconResolver.mm +7 -0
  187. package/ios/stack/header/RNSStackHeaderItemComponentView.h +12 -3
  188. package/ios/stack/header/RNSStackHeaderItemComponentView.mm +72 -5
  189. package/ios/stack/header/RNSStackHeaderItemDataProviding.h +7 -0
  190. package/ios/stack/header/RNSStackHeaderItemShadowStateProxy.mm +1 -1
  191. package/ios/stack/header/RNSStackHeaderItemSpacerComponentView.mm +2 -1
  192. package/ios/stack/header/RNSStackHeaderMenuCoordinator.h +13 -0
  193. package/ios/stack/header/RNSStackHeaderMenuCoordinator.mm +24 -0
  194. package/ios/stack/header/RNSStackHeaderMenuFinder.h +8 -1
  195. package/ios/stack/header/RNSStackHeaderMenuFinder.mm +26 -9
  196. package/ios/stack/header/RNSStackHeaderMenuMapper.mm +4 -0
  197. package/ios/stack/host/RNSStackNavigationBar.h +25 -0
  198. package/ios/stack/host/RNSStackNavigationBar.mm +80 -0
  199. package/ios/stack/host/RNSStackNavigationBarCoordinator.h +8 -0
  200. package/ios/stack/host/RNSStackNavigationBarCoordinator.mm +11 -0
  201. package/ios/stack/host/RNSStackNavigationController.mm +13 -5
  202. package/ios/stack/host/RNSStackOperation.h +0 -2
  203. package/ios/stack/screen/RNSStackScreenComponentView.h +2 -1
  204. package/ios/stack/screen/RNSStackScreenComponentView.mm +32 -2
  205. package/ios/stack/screen/RNSStackScreenController.mm +12 -0
  206. package/ios/stack/screen/RNSStackScreenHeaderCoordinator.h +8 -0
  207. package/ios/stack/screen/RNSStackScreenHeaderCoordinator.mm +179 -24
  208. package/ios/tabs/RNSTabBarAppearanceCoordinator.mm +110 -16
  209. package/ios/tabs/RNSTabBarItemCoordinator.h +16 -0
  210. package/ios/tabs/RNSTabBarItemCoordinator.mm +143 -0
  211. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryComponentView.h +1 -1
  212. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryComponentView.mm +1 -1
  213. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryContentComponentView.mm +1 -1
  214. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryEventEmitter.mm +1 -1
  215. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryShadowStateProxy.mm +1 -1
  216. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryWrapperView.h +24 -0
  217. package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryWrapperView.mm +22 -0
  218. package/ios/tabs/conversion/RNSConversions-Tabs.h +100 -0
  219. package/ios/{conversion → tabs/conversion}/RNSConversions-Tabs.mm +105 -74
  220. package/ios/tabs/extensions/RNSTabsHostComponentView+RNSImageLoader.h +1 -1
  221. package/ios/tabs/host/RNSTabBarController.h +16 -0
  222. package/ios/tabs/host/RNSTabBarController.mm +516 -107
  223. package/ios/tabs/host/RNSTabsHostComponentView.h +4 -0
  224. package/ios/tabs/host/RNSTabsHostComponentView.mm +41 -4
  225. package/ios/tabs/host/RNSTabsHostEventEmitter.mm +1 -1
  226. package/ios/tabs/screen/RNSTabsScreenComponentView.h +21 -0
  227. package/ios/tabs/screen/RNSTabsScreenComponentView.mm +56 -56
  228. package/ios/tabs/screen/RNSTabsScreenViewController.mm +1 -0
  229. package/ios/utils/RNSDefines.h +15 -7
  230. package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js +37 -21
  231. package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js.map +1 -1
  232. package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js +41 -4
  233. package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
  234. package/lib/commonjs/components/stack/header/StackHeaderConfig.js +17 -0
  235. package/lib/commonjs/components/stack/header/StackHeaderConfig.js.map +1 -0
  236. package/lib/commonjs/components/stack/header/ios/StackHeaderItem.ios.js +4 -1
  237. package/lib/commonjs/components/stack/header/ios/StackHeaderItem.ios.js.map +1 -1
  238. package/lib/commonjs/components/stack/host/StackHost.js +17 -7
  239. package/lib/commonjs/components/stack/host/StackHost.js.map +1 -1
  240. package/lib/commonjs/components/stack/host/StackHost.web.js +0 -2
  241. package/lib/commonjs/components/stack/host/StackHost.web.js.map +1 -1
  242. package/lib/commonjs/components/stack/index.js.map +1 -1
  243. package/lib/commonjs/components/tabs/host/TabsHost.ios.js +3 -0
  244. package/lib/commonjs/components/tabs/host/TabsHost.ios.js.map +1 -1
  245. package/lib/commonjs/components/tabs/host/{TabsHost.web.js → TabsHost.js} +1 -1
  246. package/lib/commonjs/components/tabs/host/TabsHost.js.map +1 -0
  247. package/lib/commonjs/components/tabs/index.js.map +1 -1
  248. package/lib/commonjs/components/tabs/screen/TabsScreen.android.js +3 -1
  249. package/lib/commonjs/components/tabs/screen/TabsScreen.android.js.map +1 -1
  250. package/lib/commonjs/components/tabs/screen/TabsScreen.ios.js +10 -1
  251. package/lib/commonjs/components/tabs/screen/TabsScreen.ios.js.map +1 -1
  252. package/lib/commonjs/components/tabs/screen/{TabsScreen.web.js → TabsScreen.js} +1 -1
  253. package/lib/commonjs/components/tabs/screen/TabsScreen.js.map +1 -0
  254. package/lib/commonjs/components/{stack/header/StackHeaderConfig.d.js → tabs/screen/TabsScreenIcon.types.js} +1 -1
  255. package/lib/commonjs/components/{stack/header/StackHeaderConfig.d.js.map → tabs/screen/TabsScreenIcon.types.js.map} +1 -1
  256. package/lib/commonjs/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
  257. package/lib/commonjs/fabric/legacy/ScreenStackNativeComponent.js.map +1 -1
  258. package/lib/commonjs/fabric/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
  259. package/lib/commonjs/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
  260. package/lib/commonjs/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
  261. package/lib/commonjs/fabric/stack/StackHostNativeComponent.js.map +1 -1
  262. package/lib/commonjs/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
  263. package/lib/commonjs/fabric/tabs/TabsScreenAndroidNativeComponent.js.map +1 -1
  264. package/lib/commonjs/fabric/tabs/TabsScreenIOSNativeComponent.js +1 -1
  265. package/lib/commonjs/fabric/tabs/TabsScreenIOSNativeComponent.js.map +1 -1
  266. package/lib/commonjs/legacy/components/ScreenStackItem.js +31 -11
  267. package/lib/commonjs/legacy/components/ScreenStackItem.js.map +1 -1
  268. package/lib/module/components/stack/header/StackHeaderConfig.android.js +39 -23
  269. package/lib/module/components/stack/header/StackHeaderConfig.android.js.map +1 -1
  270. package/lib/module/components/stack/header/StackHeaderConfig.ios.js +43 -6
  271. package/lib/module/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
  272. package/lib/module/components/stack/header/StackHeaderConfig.js +10 -0
  273. package/lib/module/components/stack/header/StackHeaderConfig.js.map +1 -0
  274. package/lib/module/components/stack/header/ios/StackHeaderItem.ios.js +4 -1
  275. package/lib/module/components/stack/header/ios/StackHeaderItem.ios.js.map +1 -1
  276. package/lib/module/components/stack/host/StackHost.js +18 -8
  277. package/lib/module/components/stack/host/StackHost.js.map +1 -1
  278. package/lib/module/components/stack/host/StackHost.web.js +0 -3
  279. package/lib/module/components/stack/host/StackHost.web.js.map +1 -1
  280. package/lib/module/components/stack/index.js.map +1 -1
  281. package/lib/module/components/tabs/host/TabsHost.ios.js +3 -0
  282. package/lib/module/components/tabs/host/TabsHost.ios.js.map +1 -1
  283. package/{src/components/tabs/host/TabsHost.web.tsx → lib/module/components/tabs/host/TabsHost.js} +1 -2
  284. package/lib/module/components/tabs/host/TabsHost.js.map +1 -0
  285. package/lib/module/components/tabs/index.js.map +1 -1
  286. package/lib/module/components/tabs/screen/TabsScreen.android.js +3 -1
  287. package/lib/module/components/tabs/screen/TabsScreen.android.js.map +1 -1
  288. package/lib/module/components/tabs/screen/TabsScreen.ios.js +10 -1
  289. package/lib/module/components/tabs/screen/TabsScreen.ios.js.map +1 -1
  290. package/{src/components/tabs/screen/TabsScreen.web.tsx → lib/module/components/tabs/screen/TabsScreen.js} +1 -2
  291. package/lib/module/components/tabs/screen/TabsScreen.js.map +1 -0
  292. package/lib/module/components/tabs/screen/TabsScreenIcon.types.js +2 -0
  293. package/lib/module/components/{stack/header/StackHeaderConfig.d.js.map → tabs/screen/TabsScreenIcon.types.js.map} +1 -1
  294. package/lib/module/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
  295. package/lib/module/fabric/legacy/ScreenStackNativeComponent.js.map +1 -1
  296. package/lib/module/fabric/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
  297. package/lib/module/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
  298. package/lib/module/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
  299. package/lib/module/fabric/stack/StackHostNativeComponent.js.map +1 -1
  300. package/lib/module/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
  301. package/lib/module/fabric/tabs/TabsScreenAndroidNativeComponent.js.map +1 -1
  302. package/lib/module/fabric/tabs/TabsScreenIOSNativeComponent.js +1 -1
  303. package/lib/module/fabric/tabs/TabsScreenIOSNativeComponent.js.map +1 -1
  304. package/lib/module/legacy/components/ScreenStackItem.js +31 -11
  305. package/lib/module/legacy/components/ScreenStackItem.js.map +1 -1
  306. package/lib/typescript/components/modals/form-sheet/FormSheet.types.d.ts +1 -1
  307. package/lib/typescript/components/stack/header/StackHeaderConfig.android.d.ts +6 -3
  308. package/lib/typescript/components/stack/header/StackHeaderConfig.android.d.ts.map +1 -1
  309. package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts +620 -24
  310. package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts.map +1 -1
  311. package/lib/typescript/components/stack/header/StackHeaderConfig.d.ts +8 -0
  312. package/lib/typescript/components/stack/header/StackHeaderConfig.d.ts.map +1 -0
  313. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.d.ts +6 -3
  314. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.d.ts.map +1 -1
  315. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts +361 -2
  316. package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts.map +1 -1
  317. package/lib/typescript/components/stack/header/StackHeaderConfig.types.d.ts +4 -2
  318. package/lib/typescript/components/stack/header/StackHeaderConfig.types.d.ts.map +1 -1
  319. package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.d.ts.map +1 -1
  320. package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts +7 -0
  321. package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts.map +1 -1
  322. package/lib/typescript/components/stack/host/StackHost.d.ts +1 -1
  323. package/lib/typescript/components/stack/host/StackHost.d.ts.map +1 -1
  324. package/lib/typescript/components/stack/host/StackHost.types.d.ts +47 -0
  325. package/lib/typescript/components/stack/host/StackHost.types.d.ts.map +1 -1
  326. package/lib/typescript/components/stack/host/StackHost.web.d.ts +1 -3
  327. package/lib/typescript/components/stack/host/StackHost.web.d.ts.map +1 -1
  328. package/lib/typescript/components/stack/index.d.ts +4 -3
  329. package/lib/typescript/components/stack/index.d.ts.map +1 -1
  330. package/lib/typescript/components/tabs/host/TabsHost.d.ts +5 -0
  331. package/lib/typescript/components/tabs/host/TabsHost.d.ts.map +1 -0
  332. package/lib/typescript/components/tabs/host/TabsHost.ios.d.ts.map +1 -1
  333. package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts +72 -0
  334. package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts.map +1 -1
  335. package/lib/typescript/components/tabs/host/TabsHost.types.d.ts +3 -0
  336. package/lib/typescript/components/tabs/host/TabsHost.types.d.ts.map +1 -1
  337. package/lib/typescript/components/tabs/index.d.ts +4 -6
  338. package/lib/typescript/components/tabs/index.d.ts.map +1 -1
  339. package/lib/typescript/components/tabs/screen/TabsScreen.android.d.ts.map +1 -1
  340. package/lib/typescript/components/tabs/screen/TabsScreen.android.types.d.ts +42 -7
  341. package/lib/typescript/components/tabs/screen/TabsScreen.android.types.d.ts.map +1 -1
  342. package/lib/typescript/components/tabs/screen/TabsScreen.d.ts +5 -0
  343. package/lib/typescript/components/tabs/screen/TabsScreen.d.ts.map +1 -0
  344. package/lib/typescript/components/tabs/screen/TabsScreen.ios.d.ts.map +1 -1
  345. package/lib/typescript/components/tabs/screen/TabsScreen.ios.types.d.ts +78 -13
  346. package/lib/typescript/components/tabs/screen/TabsScreen.ios.types.d.ts.map +1 -1
  347. package/lib/typescript/components/tabs/screen/TabsScreenIcon.types.d.ts +91 -0
  348. package/lib/typescript/components/tabs/screen/TabsScreenIcon.types.d.ts.map +1 -0
  349. package/lib/typescript/components/tabs/screen/index.d.ts +1 -0
  350. package/lib/typescript/components/tabs/screen/index.d.ts.map +1 -1
  351. package/lib/typescript/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.d.ts +2 -0
  352. package/lib/typescript/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.d.ts.map +1 -1
  353. package/lib/typescript/fabric/legacy/ScreenStackNativeComponent.d.ts +1 -0
  354. package/lib/typescript/fabric/legacy/ScreenStackNativeComponent.d.ts.map +1 -1
  355. package/lib/typescript/fabric/stack/StackHeaderConfigAndroidNativeComponent.d.ts +52 -0
  356. package/lib/typescript/fabric/stack/StackHeaderConfigAndroidNativeComponent.d.ts.map +1 -1
  357. package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts +29 -1
  358. package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts.map +1 -1
  359. package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts +8 -0
  360. package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts.map +1 -1
  361. package/lib/typescript/fabric/stack/StackHostNativeComponent.d.ts +3 -1
  362. package/lib/typescript/fabric/stack/StackHostNativeComponent.d.ts.map +1 -1
  363. package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts +4 -0
  364. package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts.map +1 -1
  365. package/lib/typescript/fabric/tabs/TabsScreenAndroidNativeComponent.d.ts +6 -0
  366. package/lib/typescript/fabric/tabs/TabsScreenAndroidNativeComponent.d.ts.map +1 -1
  367. package/lib/typescript/fabric/tabs/TabsScreenIOSNativeComponent.d.ts +10 -1
  368. package/lib/typescript/fabric/tabs/TabsScreenIOSNativeComponent.d.ts.map +1 -1
  369. package/lib/typescript/legacy/components/ScreenStackItem.d.ts +25 -0
  370. package/lib/typescript/legacy/components/ScreenStackItem.d.ts.map +1 -1
  371. package/lib/typescript/legacy/components/helpers/prepareHeaderBarButtonItems.d.ts +4 -0
  372. package/lib/typescript/legacy/components/helpers/prepareHeaderBarButtonItems.d.ts.map +1 -1
  373. package/lib/typescript/legacy/types.d.ts +30 -0
  374. package/lib/typescript/legacy/types.d.ts.map +1 -1
  375. package/package.json +11 -8
  376. package/react-native.config.js +3 -0
  377. package/src/components/modals/form-sheet/FormSheet.types.ts +1 -1
  378. package/src/components/stack/header/StackHeaderConfig.android.tsx +54 -43
  379. package/src/components/stack/header/StackHeaderConfig.android.types.ts +641 -24
  380. package/src/components/stack/header/StackHeaderConfig.ios.tsx +55 -17
  381. package/src/components/stack/header/StackHeaderConfig.ios.types.ts +384 -2
  382. package/src/components/stack/header/StackHeaderConfig.tsx +12 -0
  383. package/src/components/stack/header/StackHeaderConfig.types.ts +4 -2
  384. package/src/components/stack/header/ios/StackHeaderItem.ios.tsx +9 -1
  385. package/src/components/stack/header/ios/StackHeaderItem.ios.types.ts +7 -0
  386. package/src/components/stack/host/StackHost.tsx +12 -3
  387. package/src/components/stack/host/StackHost.types.ts +50 -0
  388. package/src/components/stack/host/StackHost.web.tsx +1 -4
  389. package/src/components/stack/index.ts +14 -1
  390. package/src/components/tabs/host/TabsHost.ios.tsx +3 -0
  391. package/src/components/tabs/host/TabsHost.ios.types.ts +76 -0
  392. package/src/components/tabs/host/TabsHost.tsx +7 -0
  393. package/src/components/tabs/host/TabsHost.types.ts +3 -0
  394. package/src/components/tabs/index.ts +9 -0
  395. package/src/components/tabs/screen/TabsScreen.android.tsx +8 -3
  396. package/src/components/tabs/screen/TabsScreen.android.types.ts +42 -7
  397. package/src/components/tabs/screen/TabsScreen.ios.tsx +22 -5
  398. package/src/components/tabs/screen/TabsScreen.ios.types.ts +79 -17
  399. package/src/components/tabs/screen/TabsScreen.tsx +7 -0
  400. package/src/components/tabs/screen/TabsScreenIcon.types.ts +117 -0
  401. package/src/components/tabs/screen/index.ts +1 -0
  402. package/src/fabric/legacy/ScreenStackHeaderSubviewNativeComponent.ts +6 -0
  403. package/src/fabric/legacy/ScreenStackNativeComponent.ts +2 -0
  404. package/src/fabric/stack/StackHeaderConfigAndroidNativeComponent.ts +78 -0
  405. package/src/fabric/stack/StackHeaderConfigIOSNativeComponent.ts +43 -0
  406. package/src/fabric/stack/StackHeaderItemIOSNativeComponent.ts +13 -0
  407. package/src/fabric/stack/StackHostNativeComponent.ts +11 -2
  408. package/src/fabric/tabs/TabsHostIOSNativeComponent.ts +8 -0
  409. package/src/fabric/tabs/TabsScreenAndroidNativeComponent.ts +9 -0
  410. package/src/fabric/tabs/TabsScreenIOSNativeComponent.ts +20 -2
  411. package/src/legacy/components/ScreenStackItem.tsx +59 -12
  412. package/src/legacy/types.ts +31 -0
  413. package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenModalFragment.kt +0 -281
  414. package/android/src/main/java/com/swmansion/rnscreens/legacy/bottomsheet/BottomSheetDialogRootView.kt +0 -104
  415. package/android/src/main/java/com/swmansion/rnscreens/legacy/bottomsheet/BottomSheetDialogScreen.kt +0 -26
  416. package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingView.kt +0 -83
  417. package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingViewManager.kt +0 -89
  418. package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingViewPointerEvents.kt +0 -18
  419. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetDimensionsCoordinator.kt +0 -134
  420. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetDialogManager.kt +0 -162
  421. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/model/FormSheetDetents.kt +0 -106
  422. package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentationManager.kt +0 -198
  423. package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuSelectionController.kt +0 -83
  424. package/ios/conversion/RNSConversions-Stack.h +0 -32
  425. package/ios/conversion/RNSConversions-Stack.mm +0 -41
  426. package/ios/conversion/always_false.h +0 -31
  427. package/lib/commonjs/components/stack/header/StackHeaderConfig.web.js +0 -10
  428. package/lib/commonjs/components/stack/header/StackHeaderConfig.web.js.map +0 -1
  429. package/lib/commonjs/components/tabs/host/TabsHost.d.js +0 -2
  430. package/lib/commonjs/components/tabs/host/TabsHost.d.js.map +0 -1
  431. package/lib/commonjs/components/tabs/host/TabsHost.web.js.map +0 -1
  432. package/lib/commonjs/components/tabs/screen/TabsScreen.d.js +0 -2
  433. package/lib/commonjs/components/tabs/screen/TabsScreen.d.js.map +0 -1
  434. package/lib/commonjs/components/tabs/screen/TabsScreen.web.js.map +0 -1
  435. package/lib/module/components/stack/header/StackHeaderConfig.d.js +0 -2
  436. package/lib/module/components/stack/header/StackHeaderConfig.web.js +0 -4
  437. package/lib/module/components/stack/header/StackHeaderConfig.web.js.map +0 -1
  438. package/lib/module/components/tabs/host/TabsHost.d.js +0 -2
  439. package/lib/module/components/tabs/host/TabsHost.d.js.map +0 -1
  440. package/lib/module/components/tabs/host/TabsHost.web.js +0 -4
  441. package/lib/module/components/tabs/host/TabsHost.web.js.map +0 -1
  442. package/lib/module/components/tabs/screen/TabsScreen.d.js +0 -2
  443. package/lib/module/components/tabs/screen/TabsScreen.d.js.map +0 -1
  444. package/lib/module/components/tabs/screen/TabsScreen.web.js +0 -4
  445. package/lib/module/components/tabs/screen/TabsScreen.web.js.map +0 -1
  446. package/lib/typescript/components/stack/header/StackHeaderConfig.web.d.ts +0 -7
  447. package/lib/typescript/components/stack/header/StackHeaderConfig.web.d.ts.map +0 -1
  448. package/lib/typescript/components/tabs/host/TabsHost.web.d.ts +0 -7
  449. package/lib/typescript/components/tabs/host/TabsHost.web.d.ts.map +0 -1
  450. package/lib/typescript/components/tabs/screen/TabsScreen.web.d.ts +0 -7
  451. package/lib/typescript/components/tabs/screen/TabsScreen.web.d.ts.map +0 -1
  452. package/src/components/stack/header/StackHeaderConfig.d.ts +0 -22
  453. package/src/components/stack/header/StackHeaderConfig.web.tsx +0 -5
  454. package/src/components/tabs/host/TabsHost.d.ts +0 -12
  455. package/src/components/tabs/screen/TabsScreen.d.ts +0 -12
  456. /package/ios/{conversion → scroll-view-marker/conversion}/RNSConversions-ScrollViewMarker.h +0 -0
@@ -3,6 +3,8 @@ package com.swmansion.rnscreens.tabs.appearance
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
5
  import android.content.res.ColorStateList
6
+ import android.graphics.drawable.Drawable
7
+ import android.graphics.drawable.InsetDrawable
6
8
  import android.graphics.drawable.StateListDrawable
7
9
  import android.util.TypedValue
8
10
  import android.view.MenuItem
@@ -14,14 +16,99 @@ import com.facebook.react.common.assets.ReactFontManager
14
16
  import com.facebook.react.uimanager.PixelUtil
15
17
  import com.google.android.material.R
16
18
  import com.google.android.material.bottomnavigation.BottomNavigationView
19
+ import com.google.android.material.navigation.NavigationBarItemView
17
20
  import com.google.android.material.navigation.NavigationBarView
18
21
  import com.swmansion.rnscreens.tabs.screen.TabsScreen
22
+ import com.swmansion.rnscreens.utils.dpToPx
23
+ import com.swmansion.rnscreens.utils.pxToDp
19
24
  import com.swmansion.rnscreens.utils.resolveColorAttr
20
25
 
21
26
  @SuppressLint("PrivateResource") // We want to use variables from material design for default values
22
27
  internal class TabsAppearanceApplicator(
23
28
  private val bottomNavigationView: BottomNavigationView,
24
29
  ) {
30
+ // Resolved on each access: tracks the display density of the current configuration.
31
+ internal val defaultIconSizeDp: Float
32
+ get() =
33
+ bottomNavigationView.pxToDp(
34
+ bottomNavigationView.resources.getDimension(R.dimen.mtrl_navigation_bar_item_default_icon_size),
35
+ )
36
+
37
+ internal fun effectiveIconSizeDp(tabsScreen: TabsScreen): Float =
38
+ tabsScreen.appearance?.tabBarItemIconSize?.takeIf { it > 0f } ?: defaultIconSizeDp
39
+
40
+ // Material allows only one icon size for all items; iconBoxDp is the largest effective per-tab size.
41
+ fun applyIconBox(iconBoxDp: Float) {
42
+ val iconBoxPx = bottomNavigationView.dpToPx(iconBoxDp).toInt()
43
+ if (bottomNavigationView.itemIconSize != iconBoxPx) {
44
+ bottomNavigationView.itemIconSize = iconBoxPx
45
+ }
46
+ }
47
+
48
+ // Resolved on each access: tracks the material library version and the display density.
49
+ private val defaultIndicatorWidthPx: Int
50
+ get() = bottomNavigationView.resources.getDimensionPixelSize(R.dimen.m3_bottom_nav_item_active_indicator_width)
51
+
52
+ private val defaultIndicatorHeightPx: Int
53
+ get() = bottomNavigationView.resources.getDimensionPixelSize(R.dimen.m3_bottom_nav_item_active_indicator_height)
54
+
55
+ // Auto-scale preserves the themed default padding around the icon: default indicator minus default icon size.
56
+ private val autoIndicatorHorizontalPaddingDp: Float
57
+ get() = bottomNavigationView.pxToDp(defaultIndicatorWidthPx.toFloat()) - defaultIconSizeDp
58
+
59
+ private val autoIndicatorVerticalPaddingDp: Float
60
+ get() = bottomNavigationView.pxToDp(defaultIndicatorHeightPx.toFloat()) - defaultIconSizeDp
61
+
62
+ internal fun effectiveIndicatorWidthDp(tabsScreen: TabsScreen): Float =
63
+ tabsScreen.appearance?.tabBarItemActiveIndicatorWidth?.takeIf { it > 0f }
64
+ ?: (effectiveIconSizeDp(tabsScreen) + autoIndicatorHorizontalPaddingDp)
65
+
66
+ internal fun effectiveIndicatorHeightDp(tabsScreen: TabsScreen): Float =
67
+ tabsScreen.appearance?.tabBarItemActiveIndicatorHeight?.takeIf { it > 0f }
68
+ ?: (effectiveIconSizeDp(tabsScreen) + autoIndicatorVerticalPaddingDp)
69
+
70
+ @SuppressLint("RestrictedApi")
71
+ private fun applyActiveIndicatorSize(
72
+ menuItem: MenuItem,
73
+ tabsScreen: TabsScreen,
74
+ ): Boolean {
75
+ val widthPx = bottomNavigationView.dpToPx(effectiveIndicatorWidthDp(tabsScreen)).toInt()
76
+ val heightPx = bottomNavigationView.dpToPx(effectiveIndicatorHeightDp(tabsScreen)).toInt()
77
+ val isAlreadyApplied =
78
+ widthPx == (tabsScreen.appliedActiveIndicatorWidthPx ?: defaultIndicatorWidthPx) &&
79
+ heightPx == (tabsScreen.appliedActiveIndicatorHeightPx ?: defaultIndicatorHeightPx)
80
+ if (isAlreadyApplied) {
81
+ return true
82
+ }
83
+ val itemView =
84
+ bottomNavigationView.menuViewGroup.children.firstOrNull { it.id == menuItem.itemId } as? NavigationBarItemView
85
+ ?: return false
86
+ // NavigationBarItemView is restricted Material API. Without it, Material only offers one active
87
+ // indicator size for the whole tab bar (BottomNavigationView.itemActiveIndicatorWidth/Height).
88
+ itemView.setActiveIndicatorWidth(widthPx)
89
+ itemView.setActiveIndicatorHeight(heightPx)
90
+
91
+ tabsScreen.appliedActiveIndicatorWidthPx = widthPx
92
+ tabsScreen.appliedActiveIndicatorHeightPx = heightPx
93
+
94
+ return true
95
+ }
96
+
97
+ // Inset the icon so it renders at effectiveDp, centered within iconBoxDp.
98
+ // Intrinsic-relative on purpose: Material FIT_CENTER-scales the drawable to the icon box,
99
+ // so the intrinsic factor cancels and the glyph lands at effectiveDp/iconBoxDp of the box.
100
+ private fun sizeIcon(
101
+ icon: Drawable?,
102
+ effectiveDp: Float,
103
+ iconBoxDp: Float,
104
+ ): Drawable? {
105
+ if (icon == null || effectiveDp >= iconBoxDp) return icon
106
+ val larger = maxOf(icon.intrinsicWidth, icon.intrinsicHeight)
107
+ if (larger <= 0) return icon
108
+ val insetPx = (larger * (iconBoxDp - effectiveDp) / (2f * effectiveDp)).toInt()
109
+ return if (insetPx > 0) InsetDrawable(icon, insetPx) else icon
110
+ }
111
+
25
112
  private val states =
26
113
  arrayOf(
27
114
  intArrayOf(-android.R.attr.state_enabled), // disabled
@@ -174,23 +261,32 @@ internal class TabsAppearanceApplicator(
174
261
  fun updateMenuItemAppearance(
175
262
  menuItem: MenuItem,
176
263
  tabsScreen: TabsScreen,
264
+ iconBoxDp: Float,
177
265
  ) {
178
266
  if (menuItem.title != tabsScreen.tabTitle) {
179
267
  menuItem.title = tabsScreen.tabTitle
180
268
  }
181
269
 
182
- val targetIcon =
183
- if (tabsScreen.selectedIcon != null && tabsScreen.icon != null) {
184
- StateListDrawable().apply {
185
- addState(intArrayOf(android.R.attr.state_checked), tabsScreen.selectedIcon?.mutate())
186
- addState(intArrayOf(), tabsScreen.icon?.mutate())
270
+ if (tabsScreen.isMenuItemIconInvalidated) {
271
+ tabsScreen.isMenuItemIconInvalidated = false
272
+ // Sized per slot: a StateListDrawable's intrinsic size follows its current state, so a
273
+ // single inset computed from one child would mis-size the other when their resolutions differ.
274
+ val effectiveDp = effectiveIconSizeDp(tabsScreen)
275
+ val iconDrawable = sizeIcon(tabsScreen.icon.drawable, effectiveDp, iconBoxDp)
276
+ val selectedIconDrawable = sizeIcon(tabsScreen.selectedIcon.drawable, effectiveDp, iconBoxDp)
277
+ menuItem.icon =
278
+ if (selectedIconDrawable != null && iconDrawable != null) {
279
+ StateListDrawable().apply {
280
+ addState(intArrayOf(android.R.attr.state_checked), selectedIconDrawable.mutate())
281
+ addState(intArrayOf(), iconDrawable.mutate())
282
+ }
283
+ } else {
284
+ iconDrawable
187
285
  }
188
- } else {
189
- tabsScreen.icon
190
- }
286
+ }
191
287
 
192
- if (menuItem.icon != targetIcon) {
193
- menuItem.icon = targetIcon
288
+ if (tabsScreen.isMenuItemActiveIndicatorInvalidated && applyActiveIndicatorSize(menuItem, tabsScreen)) {
289
+ tabsScreen.isMenuItemActiveIndicatorInvalidated = false
194
290
  }
195
291
  }
196
292
 
@@ -4,7 +4,6 @@ import android.content.Context
4
4
  import android.view.MenuItem
5
5
  import com.google.android.material.bottomnavigation.BottomNavigationView
6
6
  import com.swmansion.rnscreens.tabs.container.TabsContainer
7
- import com.swmansion.rnscreens.tabs.container.menuItemIdForFragmentAtIndex
8
7
  import com.swmansion.rnscreens.tabs.screen.TabsScreen
9
8
  import com.swmansion.rnscreens.tabs.screen.TabsScreenFragment
10
9
 
@@ -14,28 +13,67 @@ internal class TabsAppearanceCoordinator(
14
13
  ) {
15
14
  private val appearanceApplicator = TabsAppearanceApplicator(bottomNavigationView)
16
15
 
16
+ private var appliedIconBoxDp: Float? = null
17
+
18
+ // Icon box is bar-wide: the largest effective size across tabs.
19
+ internal fun resolveIconBoxDp(): Float {
20
+ if (tabsScreenFragments.isEmpty()) {
21
+ return appearanceApplicator.defaultIconSizeDp
22
+ }
23
+ var largestIconDp = 0f
24
+ var tallestIndicatorDp = 0f
25
+ var shortestIndicatorDp = Float.MAX_VALUE
26
+ tabsScreenFragments.forEach {
27
+ largestIconDp = maxOf(largestIconDp, appearanceApplicator.effectiveIconSizeDp(it.tabsScreen))
28
+ val indicatorHeightDp = appearanceApplicator.effectiveIndicatorHeightDp(it.tabsScreen)
29
+ tallestIndicatorDp = maxOf(tallestIndicatorDp, indicatorHeightDp)
30
+ shortestIndicatorDp = minOf(shortestIndicatorDp, indicatorHeightDp)
31
+ }
32
+ val unevenIndicatorsOutgrowIcons = tallestIndicatorDp > largestIconDp && tallestIndicatorDp != shortestIndicatorDp
33
+ return if (unevenIndicatorsOutgrowIcons) tallestIndicatorDp else largestIconDp
34
+ }
35
+
36
+ internal fun invalidateMenuItemIcons() {
37
+ tabsScreenFragments.forEach { it.tabsScreen.isMenuItemIconInvalidated = true }
38
+ }
39
+
40
+ internal fun invalidateRebuiltMenuItems() {
41
+ invalidateMenuItemIcons()
42
+ tabsScreenFragments.forEach {
43
+ it.tabsScreen.isMenuItemActiveIndicatorInvalidated = true
44
+ it.tabsScreen.appliedActiveIndicatorWidthPx = null
45
+ it.tabsScreen.appliedActiveIndicatorHeightPx = null
46
+ }
47
+ }
48
+
17
49
  fun updateTabAppearance(
18
50
  context: Context,
19
51
  tabsContainer: TabsContainer,
20
52
  ) {
21
53
  val selectedTabAppearance = tabsContainer.selectedTab.tabsScreen.appearance
54
+ val iconBoxDp = resolveIconBoxDp()
55
+ if (iconBoxDp != appliedIconBoxDp) {
56
+ appliedIconBoxDp = iconBoxDp
57
+ invalidateMenuItemIcons()
58
+ }
59
+ appearanceApplicator.applyIconBox(iconBoxDp)
22
60
  appearanceApplicator.updateSharedAppearance(context, selectedTabAppearance, tabsContainer.tabBarHidden)
23
- updateMenuItems(context, selectedTabAppearance)
61
+ updateMenuItems(context, selectedTabAppearance, iconBoxDp)
24
62
  appearanceApplicator.updateFontStyles(context, selectedTabAppearance) // It needs to be updated after updateMenuItems
25
63
  }
26
64
 
27
65
  private fun updateMenuItems(
28
66
  context: Context,
29
67
  tabsAppearance: TabsAppearance?,
68
+ iconBoxDp: Float,
30
69
  ) {
31
- tabsScreenFragments.forEachIndexed { index, fragment ->
32
- val menuItemId = menuItemIdForFragmentAtIndex(index)
70
+ tabsScreenFragments.forEach { fragment ->
71
+ val menuItemId = fragment.menuItemId
33
72
  val menuItem =
34
73
  checkNotNull(bottomNavigationView.menu.findItem(menuItemId)) {
35
74
  "[RNScreens] Missing MenuItem for id: $menuItemId"
36
75
  }
37
- check(menuItem.itemId == menuItemId) { "[RNScreens] Illegal state: menu items are shuffled" }
38
- updateMenuItemAppearance(context, menuItem, fragment.tabsScreen, tabsAppearance)
76
+ updateMenuItemAppearance(context, menuItem, fragment.tabsScreen, tabsAppearance, iconBoxDp)
39
77
  }
40
78
  }
41
79
 
@@ -44,8 +82,9 @@ internal class TabsAppearanceCoordinator(
44
82
  menuItem: MenuItem,
45
83
  tabsScreen: TabsScreen,
46
84
  appearance: TabsAppearance?,
85
+ iconBoxDp: Float,
47
86
  ) {
48
- appearanceApplicator.updateMenuItemAppearance(menuItem, tabsScreen)
87
+ appearanceApplicator.updateMenuItemAppearance(menuItem, tabsScreen, iconBoxDp)
49
88
  appearanceApplicator.updateBadgeAppearance(context, menuItem, tabsScreen, appearance)
50
89
  }
51
90
  }
@@ -8,8 +8,11 @@ internal data class TabsAppearance(
8
8
  val selected: ItemStateAppearance? = null,
9
9
  val focused: ItemStateAppearance? = null,
10
10
  val disabled: ItemStateAppearance? = null,
11
+ val tabBarItemIconSize: Float? = null,
11
12
  val tabBarItemActiveIndicatorColor: Int? = null,
12
13
  val tabBarItemActiveIndicatorEnabled: Boolean? = null,
14
+ val tabBarItemActiveIndicatorWidth: Float? = null,
15
+ val tabBarItemActiveIndicatorHeight: Float? = null,
13
16
  val tabBarItemTitleFontFamily: String? = null,
14
17
  val tabBarItemTitleSmallLabelFontSize: Float? = null,
15
18
  val tabBarItemTitleLargeLabelFontSize: Float? = null,
@@ -2,15 +2,35 @@ package com.swmansion.rnscreens.tabs.container
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
+ import androidx.core.view.isVisible
6
+ import com.facebook.react.uimanager.PointerEvents
7
+ import com.facebook.react.uimanager.ReactPointerEventsView
5
8
  import com.google.android.material.bottomnavigation.BottomNavigationView
6
9
 
7
10
  @SuppressLint("ViewConstructor") // Should not be restored & should only be constructed by us.
8
11
  class CustomBottomNavigationView(
9
12
  context: Context,
10
13
  val container: TabsContainer,
11
- ) : BottomNavigationView(context) {
14
+ ) : BottomNavigationView(context),
15
+ ReactPointerEventsView {
12
16
  private var actionOrigin: TabsActionOrigin? = null
13
17
 
18
+ /**
19
+ * The tab bar is hidden by setting its visibility to `GONE`. A `GONE` view is skipped by its
20
+ * parent's layout pass, so it keeps the bounds it was last laid out with, and Android's own
21
+ * touch dispatch ignores it (`ViewGroup.canViewReceivePointerEvents` requires `VISIBLE`).
22
+ *
23
+ * React Native does not go through Android's dispatch to pick a touch target - it runs its own
24
+ * hit-test over the native view tree in `TouchTargetHelper`, which only checks bounds. The
25
+ * retained bounds of the hidden bar therefore keep capturing every touch aimed at the content
26
+ * laid out underneath it. Declaring [PointerEvents.NONE] while hidden is how a native view opts
27
+ * out of that hit-test, which brings it back in line with Android's dispatch.
28
+ *
29
+ * See https://github.com/software-mansion/react-native-screens/issues/4132.
30
+ */
31
+ override val pointerEvents: PointerEvents
32
+ get() = if (isVisible) PointerEvents.AUTO else PointerEvents.NONE
33
+
14
34
  internal fun setSelectedItemIdWithActionOrigin(
15
35
  itemId: Int,
16
36
  actionOrigin: TabsActionOrigin,
@@ -2,30 +2,12 @@ package com.swmansion.rnscreens.tabs.container
2
2
 
3
3
  import android.view.Menu
4
4
  import android.view.MenuItem
5
- import com.swmansion.rnscreens.tabs.screen.TabsScreen
5
+ import com.swmansion.rnscreens.tabs.screen.TabsScreenFragment
6
6
 
7
- // MenuItem ids are offset by one, because 0 has special meaning in the BottomNavigationMenuView API.
8
-
9
- /**
10
- * Compute MenuItem id for a fragment at given index in "tabsModel" of TabsContainer
11
- */
12
- internal fun menuItemIdForFragmentAtIndex(fragmentIndex: Int): Int = fragmentIndex + 1
13
-
14
- /**
15
- * Compute fragment index in "tabsModel" of TabsContainer for a MenuItem with given id.
16
- */
17
- internal fun fragmentIndexForMenuItemId(menuItemId: Int): Int {
18
- check(menuItemId >= 1) { "[RNScreens] MenuItem id must not be less than 1" }
19
- return menuItemId - 1
20
- }
21
-
22
- internal fun Menu.getOrCreateMenuItemForFragmentAt(
23
- index: Int,
24
- tabsScreen: TabsScreen,
25
- ): MenuItem =
26
- this.findItem(menuItemIdForFragmentAtIndex(index)) ?: this.add(
7
+ internal fun Menu.getOrCreateMenuItemForFragment(fragment: TabsScreenFragment): MenuItem =
8
+ this.findItem(fragment.menuItemId) ?: this.add(
27
9
  Menu.NONE,
28
- menuItemIdForFragmentAtIndex(index),
10
+ fragment.menuItemId,
29
11
  Menu.NONE,
30
- tabsScreen.tabTitle,
12
+ fragment.tabsScreen.tabTitle,
31
13
  )
@@ -24,14 +24,13 @@ import com.swmansion.rnscreens.common.colorscheme.ColorSchemeCoordinator
24
24
  import com.swmansion.rnscreens.common.colorscheme.ColorSchemeListener
25
25
  import com.swmansion.rnscreens.common.colorscheme.ColorSchemeProviding
26
26
  import com.swmansion.rnscreens.common.container.Container
27
+ import com.swmansion.rnscreens.common.container.ContainerItem
27
28
  import com.swmansion.rnscreens.common.container.ParentContainerItemRegistry
28
- import com.swmansion.rnscreens.helpers.FragmentManagerHelper
29
- import com.swmansion.rnscreens.helpers.ViewFinder
29
+ import com.swmansion.rnscreens.ext.createTransactionWithReordering
30
30
  import com.swmansion.rnscreens.helpers.ViewIdGenerator
31
- import com.swmansion.rnscreens.helpers.createTransactionWithReordering
32
31
  import com.swmansion.rnscreens.safearea.EdgeInsets
32
+ import com.swmansion.rnscreens.safearea.OnInterfaceInsetsChangeListener
33
33
  import com.swmansion.rnscreens.safearea.SafeAreaProvider
34
- import com.swmansion.rnscreens.safearea.SafeAreaView
35
34
  import com.swmansion.rnscreens.tabs.appearance.TabsAppearanceCoordinator
36
35
  import com.swmansion.rnscreens.tabs.host.TabsHost
37
36
  import com.swmansion.rnscreens.tabs.host.TabsHostA11yCoordinator
@@ -39,6 +38,7 @@ import com.swmansion.rnscreens.tabs.screen.TabsScreen
39
38
  import com.swmansion.rnscreens.tabs.screen.TabsScreenDelegate
40
39
  import com.swmansion.rnscreens.tabs.screen.TabsScreenFragment
41
40
  import com.swmansion.rnscreens.utils.RNSLog
41
+ import java.lang.ref.WeakReference
42
42
  import kotlin.properties.Delegates
43
43
 
44
44
  /**
@@ -52,6 +52,7 @@ import kotlin.properties.Delegates
52
52
  @SuppressLint("ViewConstructor") // Created only by us. Should never be restored.
53
53
  class TabsContainer internal constructor(
54
54
  private val context: Context,
55
+ private val delegate: WeakReference<TabsContainerDelegate>,
55
56
  ) : FrameLayout(context),
56
57
  Container,
57
58
  ColorSchemeProviding,
@@ -67,8 +68,7 @@ class TabsContainer internal constructor(
67
68
  val selectedTabScreen = this@TabsContainer.selectedTab.tabsScreen
68
69
 
69
70
  if (selectedTabScreen.shouldUseRepeatedTabSelectionPopToRootSpecialEffect) {
70
- val screenStack = ViewFinder.findScreenStackInFirstDescendantChain(selectedTabScreen)
71
- if (screenStack != null && screenStack.popToRoot()) {
71
+ if (selectedTabScreen.resolveNestedContainer()?.requestPopToRoot() == true) {
72
72
  return true
73
73
  }
74
74
  }
@@ -160,7 +160,7 @@ class TabsContainer internal constructor(
160
160
 
161
161
  private val a11yCoordinator = TabsHostA11yCoordinator(bottomNavigationView, tabsModel)
162
162
 
163
- private var interfaceInsetsChangeListener: SafeAreaView? = null
163
+ private var interfaceInsetsChangeListener: OnInterfaceInsetsChangeListener? = null
164
164
 
165
165
  internal var tabBarHidden: Boolean by Delegates.observable(false) { _, oldValue, newValue ->
166
166
  if (newValue != oldValue) {
@@ -255,9 +255,9 @@ class TabsContainer internal constructor(
255
255
 
256
256
  internal fun setupFragmentManager() {
257
257
  fragmentManager =
258
- checkNotNull(FragmentManagerHelper.findFragmentManagerForView(this)) {
259
- "[RNScreens] Nullish fragment manager - can't run container operations"
260
- }
258
+ checkNotNull(delegate.get()) {
259
+ "[RNScreens] TabsContainer has no delegate to resolve the fragment manager from"
260
+ }.resolveFragmentManager()
261
261
  }
262
262
 
263
263
  internal fun teardownFragmentManager() {
@@ -372,14 +372,14 @@ class TabsContainer internal constructor(
372
372
  }
373
373
  }
374
374
 
375
- override fun setOnInterfaceInsetsChangeListener(listener: SafeAreaView) {
375
+ override fun setOnInterfaceInsetsChangeListener(listener: OnInterfaceInsetsChangeListener) {
376
376
  if (interfaceInsetsChangeListener == null) {
377
377
  bottomNavigationView.addOnLayoutChangeListener(this)
378
378
  }
379
379
  interfaceInsetsChangeListener = listener
380
380
  }
381
381
 
382
- override fun removeOnInterfaceInsetsChangeListener(listener: SafeAreaView) {
382
+ override fun removeOnInterfaceInsetsChangeListener(listener: OnInterfaceInsetsChangeListener) {
383
383
  if (interfaceInsetsChangeListener == listener) {
384
384
  interfaceInsetsChangeListener = null
385
385
  bottomNavigationView.removeOnLayoutChangeListener(this)
@@ -415,11 +415,23 @@ class TabsContainer internal constructor(
415
415
  menuItem,
416
416
  tabsScreen,
417
417
  appearance,
418
+ appearanceCoordinator.resolveIconBoxDp(),
418
419
  )
419
420
  a11yCoordinator.setA11yPropertiesToTabItem(menuItem, tabsScreen)
420
421
  }
421
422
  }
422
423
 
424
+ override fun onItemSizeChange(tabsScreen: TabsScreen) {
425
+ // Icon box is bar-wide, so this must run even when the changed tab is not selected.
426
+ // Skip when detached; reattachment reapplies the full appearance anyway.
427
+ if (isAttachedToWindow) {
428
+ invalidationFlags.isNavigationMenuAppearanceInvalidated = true
429
+ post {
430
+ this.flushPendingUpdates()
431
+ }
432
+ }
433
+ }
434
+
423
435
  override fun getFragmentForTabsScreen(tabsScreen: TabsScreen): TabsScreenFragment? =
424
436
  tabsModel.find {
425
437
  it.tabsScreen ===
@@ -492,7 +504,10 @@ class TabsContainer internal constructor(
492
504
 
493
505
  private fun performSelectedTabUpdate() {
494
506
  if (pendingStateUpdateRequest == null) {
495
- RNSLog.w(TAG, "TabsContainer::performSelectedTabUpdate called w/o pending operation; skipping update")
507
+ RNSLog.w(
508
+ TAG,
509
+ "TabsContainer::performSelectedTabUpdate called w/o pending operation; skipping update",
510
+ )
496
511
  return
497
512
  }
498
513
 
@@ -516,7 +531,10 @@ class TabsContainer internal constructor(
516
531
  if (bottomNavigationView.selectedItemId != nextSelectedMenuItemId || navState.isEmpty()) {
517
532
  isInExternalOperationContext = true
518
533
  // This triggers on OnMenuItemClicked callback, where we perform actual update from
519
- bottomNavigationView.setSelectedItemIdWithActionOrigin(nextSelectedMenuItemId, stateUpdateRequest.actionOrigin)
534
+ bottomNavigationView.setSelectedItemIdWithActionOrigin(
535
+ nextSelectedMenuItemId,
536
+ stateUpdateRequest.actionOrigin,
537
+ )
520
538
  isInExternalOperationContext = false
521
539
  } else {
522
540
  observerRegistry.emitOnNavigationStateUpdateRejected(
@@ -530,18 +548,21 @@ class TabsContainer internal constructor(
530
548
  }
531
549
 
532
550
  private fun updateNavigationMenuStructure() {
533
- if (bottomNavigationView.menu.size != tabsModel.size) {
534
- // Most likely first render or some tab has been removed. Let's nuke the menu (easiest option).
535
- bottomNavigationView.menu.clear()
536
- }
537
- tabsModel.forEachIndexed { index, fragment ->
538
- val menuItem =
539
- bottomNavigationView.menu.getOrCreateMenuItemForFragmentAt(
540
- index,
541
- fragment.tabsScreen,
542
- )
543
- check(fragmentIndexForMenuItemId(menuItem.itemId) == index) { "[RNScreens] Illegal state: menu items are shuffled" }
551
+ val menu = bottomNavigationView.menu
552
+ val isMenuInSync =
553
+ menu.size == tabsModel.size &&
554
+ tabsModel.withIndex().all { (index, fragment) ->
555
+ menu.getItem(index).itemId == fragment.menuItemId
556
+ }
557
+
558
+ if (isMenuInSync) {
559
+ return
560
+ }
561
+ menu.clear()
562
+ tabsModel.forEach { fragment ->
563
+ menu.getOrCreateMenuItemForFragment(fragment)
544
564
  }
565
+ appearanceCoordinator.invalidateRebuiltMenuItems()
545
566
  }
546
567
 
547
568
  private fun updateBottomNavigationViewAppearance() {
@@ -573,7 +594,10 @@ class TabsContainer internal constructor(
573
594
  }
574
595
 
575
596
  progressNavigationState(nextSelectedFragment.requireScreenKey, actionOrigin)
576
- applyNextSelectedFragmentToFragmentManagerSync(currentSelectedFragment, nextSelectedFragment)
597
+ applyNextSelectedFragmentToFragmentManagerSync(
598
+ currentSelectedFragment,
599
+ nextSelectedFragment,
600
+ )
577
601
  return true
578
602
  }
579
603
 
@@ -643,7 +667,8 @@ class TabsContainer internal constructor(
643
667
  return false
644
668
  }
645
669
 
646
- val stateChanged = updateNavigationStateAndSelectedFragment(nextSelectedFragment, actionOrigin)
670
+ val stateChanged =
671
+ updateNavigationStateAndSelectedFragment(nextSelectedFragment, actionOrigin)
647
672
 
648
673
  val hasTriggeredSpecialEffect =
649
674
  if (isRepeated) specialEffectsHandler.handleRepeatedTabSelection() else false
@@ -688,7 +713,8 @@ class TabsContainer internal constructor(
688
713
 
689
714
  // We expect at most a single fragment added (detached fragments are not returned from
690
715
  // FragmentManager.getFragments call) and it being the currently selected tab.
691
- val isInUpToDateState = currentFragments.size == 1 && currentFragments.first() === selectedTab
716
+ val isInUpToDateState =
717
+ currentFragments.size == 1 && currentFragments.first() === selectedTab
692
718
  if (isInUpToDateState) {
693
719
  return
694
720
  } else if (currentFragments.isEmpty()) {
@@ -717,12 +743,10 @@ class TabsContainer internal constructor(
717
743
  appearanceCoordinator.updateTabAppearance(themedContext, this)
718
744
  }
719
745
 
720
- private fun getFragmentForMenuItemId(itemId: Int): TabsScreenFragment? = tabsModel.getOrNull(fragmentIndexForMenuItemId(itemId))
746
+ private fun getFragmentForMenuItemId(itemId: Int): TabsScreenFragment? = tabsModel.find { it.menuItemId == itemId }
721
747
 
722
748
  private fun getMenuItemIdForFragment(tabsScreenFragment: TabsScreenFragment): Int? =
723
- tabsModel.indexOfFirst { it === tabsScreenFragment }.takeIf { it != -1 }?.let {
724
- menuItemIdForFragmentAtIndex(it)
725
- }
749
+ tabsScreenFragment.menuItemId.takeIf { tabsModel.any { it === tabsScreenFragment } }
726
750
 
727
751
  private fun getSelectedTabsScreenFragmentId(): Int? =
728
752
  tabsModel
@@ -731,10 +755,9 @@ class TabsContainer internal constructor(
731
755
 
732
756
  private fun getMenuItemForTabsScreen(tabsScreen: TabsScreen): MenuItem? =
733
757
  tabsModel
734
- .indexOfFirst { it.tabsScreen === tabsScreen }
735
- .takeIf { it != -1 }
736
- ?.let { index ->
737
- bottomNavigationView.menu.findItem(menuItemIdForFragmentAtIndex(index))
758
+ .find { it.tabsScreen === tabsScreen }
759
+ ?.let { fragment ->
760
+ bottomNavigationView.menu.findItem(fragment.menuItemId)
738
761
  }
739
762
 
740
763
  private fun getFragmentForScreenKey(screenKey: String): TabsScreenFragment? = tabsModel.find { it.requireScreenKey == screenKey }
@@ -780,6 +803,19 @@ class TabsContainer internal constructor(
780
803
  return selectedTab.tabsScreen.findContentScrollView()
781
804
  }
782
805
 
806
+ // Only the active item is consulted - a preventing screen inside an inactive tab
807
+ // does not veto the dismissal.
808
+ override fun wantsToPreventStackNativeDismiss(): ContainerItem? =
809
+ if (navState.isNotEmpty()) {
810
+ selectedTab.tabsScreen.wantsToPreventStackNativeDismiss()
811
+ } else {
812
+ null
813
+ }
814
+
815
+ // Tabs have no stack of their own - the request goes to the selected tab's nested container.
816
+ override fun requestPopToRoot(): Boolean =
817
+ navState.isNotEmpty() && selectedTab.tabsScreen.resolveNestedContainer()?.requestPopToRoot() == true
818
+
783
819
  // endregion
784
820
 
785
821
  companion object {
@@ -0,0 +1,11 @@
1
+ package com.swmansion.rnscreens.tabs.container
2
+
3
+ import androidx.fragment.app.FragmentManager
4
+
5
+ internal interface TabsContainerDelegate {
6
+ /**
7
+ * Resolves the fragment manager the container should run its operations on. Called every time the container
8
+ * is attached to a window, as the result may differ between attachments.
9
+ */
10
+ fun resolveFragmentManager(): FragmentManager
11
+ }
@@ -4,21 +4,25 @@ import android.annotation.SuppressLint
4
4
  import android.view.ViewTreeObserver
5
5
  import android.widget.FrameLayout
6
6
  import androidx.core.graphics.drawable.toDrawable
7
+ import androidx.fragment.app.FragmentManager
7
8
  import com.facebook.react.bridge.UIManager
8
9
  import com.facebook.react.bridge.UIManagerListener
9
10
  import com.facebook.react.common.annotations.UnstableReactNativeAPI
10
11
  import com.facebook.react.uimanager.ThemedReactContext
11
12
  import com.facebook.react.uimanager.UIManagerHelper
12
13
  import com.swmansion.rnscreens.common.colorscheme.ColorScheme
14
+ import com.swmansion.rnscreens.helpers.FragmentManagerHelper
13
15
  import com.swmansion.rnscreens.helpers.getFabricUIManagerNotNull
14
16
  import com.swmansion.rnscreens.tabs.container.TabsActionOrigin
15
17
  import com.swmansion.rnscreens.tabs.container.TabsContainer
18
+ import com.swmansion.rnscreens.tabs.container.TabsContainerDelegate
16
19
  import com.swmansion.rnscreens.tabs.container.TabsNavigationState
17
20
  import com.swmansion.rnscreens.tabs.container.TabsNavigationStateObserver
18
21
  import com.swmansion.rnscreens.tabs.container.TabsNavigationStateRejectionReason
19
22
  import com.swmansion.rnscreens.tabs.container.TabsNavigationStateUpdateRequest
20
23
  import com.swmansion.rnscreens.tabs.screen.TabsScreen
21
24
  import com.swmansion.rnscreens.utils.RNSLog
25
+ import java.lang.ref.WeakReference
22
26
  import kotlin.properties.Delegates
23
27
 
24
28
  @SuppressLint("ViewConstructor") // Only created by us. Should never be restored
@@ -28,7 +32,8 @@ class TabsHost(
28
32
  ) : FrameLayout(reactContext),
29
33
  TabsNavigationStateObserver,
30
34
  ViewTreeObserver.OnPreDrawListener,
31
- UIManagerListener {
35
+ UIManagerListener,
36
+ TabsContainerDelegate {
32
37
  private val renderedScreens: ArrayList<TabsScreen> = arrayListOf()
33
38
  private var jsNavStateRequest: TabsNavigationStateUpdateRequest? = null
34
39
  private val layoutCoordinator: TabsHostLayoutCoordinator =
@@ -37,7 +42,7 @@ class TabsHost(
37
42
  private var hasFirstLayoutWithInsets: Boolean = false
38
43
 
39
44
  private val container: TabsContainer =
40
- TabsContainer(reactContext).apply {
45
+ TabsContainer(reactContext, WeakReference(this)).apply {
41
46
  layoutParams =
42
47
  LayoutParams(
43
48
  LayoutParams.MATCH_PARENT,
@@ -82,6 +87,11 @@ class TabsHost(
82
87
  super.onDetachedFromWindow()
83
88
  }
84
89
 
90
+ override fun resolveFragmentManager(): FragmentManager =
91
+ checkNotNull(FragmentManagerHelper.findFragmentManagerForView(this)) {
92
+ "[RNScreens] Nullish fragment manager - can't run container operations"
93
+ }
94
+
85
95
  internal fun mountReactSubviewAt(
86
96
  tabsScreen: TabsScreen,
87
97
  index: Int,
@@ -161,6 +171,16 @@ class TabsHost(
161
171
  refreshLayout()
162
172
  }
163
173
 
174
+ // On window configuration changes ViewRootImpl calls forceLayout() recursively on every view
175
+ // in the hierarchy, bypassing requestLayout(). React views never lay out their children during
176
+ // framework traversals, so nothing would clear the force-layout flags in our subtree — and once
177
+ // they are set, any subsequent requestLayout() from a descendant short-circuits before reaching
178
+ // the override above, permanently disabling the manual pass. Schedule it from here as well.
179
+ override fun forceLayout() {
180
+ super.forceLayout()
181
+ refreshLayout()
182
+ }
183
+
164
184
  private fun forceSubtreeMeasureAndLayoutPass() {
165
185
  measure(
166
186
  MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),