react-native-screens 4.12.0 → 4.13.0

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 (235) hide show
  1. package/RNScreens.podspec +29 -2
  2. package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +4 -0
  3. package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +9 -1
  4. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragmentWrapper.kt +0 -1
  5. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +4 -4
  6. package/android/src/main/java/com/swmansion/rnscreens/ScreensModule.kt +1 -1
  7. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/DimmingView.kt +4 -4
  8. package/android/src/main/java/com/swmansion/rnscreens/bottomsheet/DimmingViewManager.kt +8 -2
  9. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenAnimationDelegate.kt +1 -2
  10. package/android/src/main/java/com/swmansion/rnscreens/events/ScreenEventEmitter.kt +16 -13
  11. package/android/src/main/java/com/swmansion/rnscreens/gamma/common/BaseEventEmitter.kt +22 -0
  12. package/android/src/main/java/com/swmansion/rnscreens/gamma/common/FragmentProviding.kt +11 -0
  13. package/android/src/main/java/com/swmansion/rnscreens/gamma/common/NamingAwareEventType.kt +13 -0
  14. package/android/src/main/java/com/swmansion/rnscreens/gamma/helpers/EventHelpers.kt +6 -0
  15. package/android/src/main/java/com/swmansion/rnscreens/gamma/helpers/FragmentManagerHelper.kt +76 -0
  16. package/android/src/main/java/com/swmansion/rnscreens/gamma/helpers/SystemDrawable.kt +33 -0
  17. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/TabScreen.kt +101 -0
  18. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/TabScreenDelegate.kt +17 -0
  19. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/TabScreenEventEmitter.kt +46 -0
  20. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/TabScreenFragment.kt +37 -0
  21. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/TabScreenViewManager.kt +178 -0
  22. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/TabsHost.kt +433 -0
  23. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/TabsHostEventEmitter.kt +14 -0
  24. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/TabsHostViewManager.kt +177 -0
  25. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/event/TabScreenDidAppearEvent.kt +30 -0
  26. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/event/TabScreenDidDisappearEvent.kt +30 -0
  27. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/event/TabScreenWillAppearEvent.kt +30 -0
  28. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/event/TabScreenWillDisappearEvent.kt +30 -0
  29. package/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/event/TabsHostNativeFocusChangeEvent.kt +36 -0
  30. package/android/src/main/java/com/swmansion/rnscreens/stack/views/ChildDrawingOrderStrategyImpl.kt +0 -1
  31. package/android/src/main/java/com/swmansion/rnscreens/transition/ExternalBoundaryValuesEvaluator.kt +9 -2
  32. package/android/src/main/java/com/swmansion/rnscreens/utils/FragmentTransactionKt.kt +4 -1
  33. package/android/src/main/jni/rnscreens.h +1 -0
  34. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSBottomTabsManagerDelegate.java +76 -0
  35. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSBottomTabsManagerInterface.java +33 -0
  36. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSBottomTabsScreenManagerDelegate.java +97 -0
  37. package/android/src/paper/java/com/facebook/react/viewmanagers/RNSBottomTabsScreenManagerInterface.java +40 -0
  38. package/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledHeaderConfigViewGroup.kt +2 -2
  39. package/android/src/versioned/backgroundcolor/76/ViewBackgroundUtils.kt +0 -1
  40. package/android/src/versioned/backgroundcolor/latest/ViewBackgroundUtils.kt +0 -3
  41. package/android/src/versioned/pointerevents/77/com/swmansion/rnscreens/PointerEventsBoxNoneImpl.kt +1 -1
  42. package/android/src/versioned/pointerevents/latest/com/swmansion/rnscreens/PointerEventsBoxNoneImpl.kt +1 -1
  43. package/common/cpp/react/renderer/components/rnscreens/RNSBottomTabsComponentDescriptor.h +31 -0
  44. package/common/cpp/react/renderer/components/rnscreens/RNSBottomTabsShadowNode.cpp +20 -0
  45. package/common/cpp/react/renderer/components/rnscreens/RNSBottomTabsShadowNode.h +32 -0
  46. package/common/cpp/react/renderer/components/rnscreens/RNSBottomTabsState.cpp +22 -0
  47. package/common/cpp/react/renderer/components/rnscreens/RNSBottomTabsState.h +44 -0
  48. package/common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.cpp +8 -0
  49. package/common/cpp/react/renderer/components/rnscreens/RNSSplitViewScreenComponentDescriptor.h +40 -0
  50. package/common/cpp/react/renderer/components/rnscreens/RNSSplitViewScreenShadowNode.cpp +13 -0
  51. package/common/cpp/react/renderer/components/rnscreens/RNSSplitViewScreenShadowNode.h +36 -0
  52. package/common/cpp/react/renderer/components/rnscreens/RNSSplitViewScreenState.h +32 -0
  53. package/cpp/RNScreensTurboModule.h +5 -0
  54. package/ios/RNSEnums.h +6 -0
  55. package/ios/RNSScreen.h +2 -1
  56. package/ios/RNSScreen.mm +48 -1
  57. package/ios/RNSScreenContainer.mm +6 -0
  58. package/ios/RNSScreenStack.h +3 -1
  59. package/ios/RNSScreenStack.mm +39 -2
  60. package/ios/RNSScreenStackHeaderConfig.mm +1 -1
  61. package/ios/RNSScrollViewBehaviorOverriding.h +24 -0
  62. package/ios/RNSScrollViewFinder.h +13 -0
  63. package/ios/RNSScrollViewFinder.mm +22 -0
  64. package/ios/RNSScrollViewHelper.h +10 -0
  65. package/ios/RNSScrollViewHelper.mm +15 -0
  66. package/ios/RNScreens-Bridging-Header.h +4 -0
  67. package/ios/UIScrollView+RNScreens.h +14 -0
  68. package/ios/UIScrollView+RNScreens.mm +15 -0
  69. package/ios/bottom-tabs/RCTConvert+RNSBottomTabs.h +18 -0
  70. package/ios/bottom-tabs/RCTConvert+RNSBottomTabs.mm +25 -0
  71. package/ios/bottom-tabs/RNSBottomTabsHostComponentView.h +80 -0
  72. package/ios/bottom-tabs/RNSBottomTabsHostComponentView.mm +486 -0
  73. package/ios/bottom-tabs/RNSBottomTabsHostComponentViewManager.h +11 -0
  74. package/ios/bottom-tabs/RNSBottomTabsHostComponentViewManager.mm +48 -0
  75. package/ios/bottom-tabs/RNSBottomTabsHostEventEmitter.h +53 -0
  76. package/ios/bottom-tabs/RNSBottomTabsHostEventEmitter.mm +57 -0
  77. package/ios/bottom-tabs/RNSBottomTabsScreenComponentView.h +95 -0
  78. package/ios/bottom-tabs/RNSBottomTabsScreenComponentView.mm +492 -0
  79. package/ios/bottom-tabs/RNSBottomTabsScreenComponentViewManager.h +9 -0
  80. package/ios/bottom-tabs/RNSBottomTabsScreenComponentViewManager.mm +55 -0
  81. package/ios/bottom-tabs/RNSBottomTabsScreenEventEmitter.h +53 -0
  82. package/ios/bottom-tabs/RNSBottomTabsScreenEventEmitter.mm +106 -0
  83. package/ios/bottom-tabs/RNSBottomTabsSpecialEffectsSupporting.h +17 -0
  84. package/ios/bottom-tabs/RNSTabBarAppearanceCoordinator.h +34 -0
  85. package/ios/bottom-tabs/RNSTabBarAppearanceCoordinator.mm +243 -0
  86. package/ios/bottom-tabs/RNSTabBarAppearanceProvider.h +23 -0
  87. package/ios/bottom-tabs/RNSTabBarController.h +132 -0
  88. package/ios/bottom-tabs/RNSTabBarController.mm +206 -0
  89. package/ios/bottom-tabs/RNSTabBarControllerDelegate.h +9 -0
  90. package/ios/bottom-tabs/RNSTabBarControllerDelegate.mm +63 -0
  91. package/ios/bottom-tabs/RNSTabsScreenViewController.h +42 -0
  92. package/ios/bottom-tabs/RNSTabsScreenViewController.mm +105 -0
  93. package/ios/bottom-tabs/extensions/RNSBottomTabsHostComponentView+RNSImageLoader.h +21 -0
  94. package/ios/bottom-tabs/extensions/RNSBottomTabsHostComponentView+RNSImageLoader.mm +22 -0
  95. package/ios/bridging/RNSReactBaseView.h +31 -0
  96. package/ios/bridging/RNSReactBaseView.mm +5 -0
  97. package/ios/bridging/Swift-Bridging.h +7 -0
  98. package/ios/conversion/RNSConversions-BottomTabs.mm +216 -0
  99. package/ios/conversion/RNSConversions-SplitView.mm +63 -0
  100. package/ios/conversion/RNSConversions.h +53 -0
  101. package/ios/gamma/ReactMountingTransactionObserving.swift +7 -0
  102. package/ios/gamma/split-view/RNSSplitViewHostComponentView.h +29 -0
  103. package/ios/gamma/split-view/RNSSplitViewHostComponentView.mm +209 -0
  104. package/ios/gamma/split-view/RNSSplitViewHostComponentViewManager.h +11 -0
  105. package/ios/gamma/split-view/RNSSplitViewHostComponentViewManager.mm +7 -0
  106. package/ios/gamma/split-view/RNSSplitViewHostController.swift +98 -0
  107. package/ios/gamma/split-view/RNSSplitViewNavigationController.swift +31 -0
  108. package/ios/gamma/split-view/RNSSplitViewScreenComponentView.h +24 -0
  109. package/ios/gamma/split-view/RNSSplitViewScreenComponentView.mm +106 -0
  110. package/ios/gamma/split-view/RNSSplitViewScreenComponentViewManager.h +11 -0
  111. package/ios/gamma/split-view/RNSSplitViewScreenComponentViewManager.mm +7 -0
  112. package/ios/gamma/split-view/RNSSplitViewScreenController.swift +86 -0
  113. package/ios/gamma/split-view/RNSSplitViewScreenShadowStateProxy.h +35 -0
  114. package/ios/gamma/split-view/RNSSplitViewScreenShadowStateProxy.mm +56 -0
  115. package/ios/gamma/stack/RNSScreenStackHostComponentView.h +16 -0
  116. package/ios/gamma/stack/RNSScreenStackHostComponentView.mm +143 -0
  117. package/ios/gamma/stack/RNSScreenStackHostComponentViewManager.h +11 -0
  118. package/ios/gamma/stack/RNSScreenStackHostComponentViewManager.mm +7 -0
  119. package/ios/gamma/stack/RNSStackController.swift +65 -0
  120. package/ios/gamma/stack/RNSStackScreenComponentEventEmitter.h +37 -0
  121. package/ios/gamma/stack/RNSStackScreenComponentEventEmitter.mm +60 -0
  122. package/ios/gamma/stack/RNSStackScreenComponentView.h +36 -0
  123. package/ios/gamma/stack/RNSStackScreenComponentView.mm +124 -0
  124. package/ios/gamma/stack/RNSStackScreenComponentViewManager.h +11 -0
  125. package/ios/gamma/stack/RNSStackScreenComponentViewManager.mm +7 -0
  126. package/ios/gamma/stack/RNSStackScreenController.swift +56 -0
  127. package/ios/utils/NSString+RNSUtility.h +17 -0
  128. package/ios/utils/NSString+RNSUtility.mm +37 -0
  129. package/lib/commonjs/components/BottomTabs.js +54 -0
  130. package/lib/commonjs/components/BottomTabs.js.map +1 -0
  131. package/lib/commonjs/components/BottomTabsScreen.js +149 -0
  132. package/lib/commonjs/components/BottomTabsScreen.js.map +1 -0
  133. package/lib/commonjs/components/gamma/ScreenStackHost.js +27 -0
  134. package/lib/commonjs/components/gamma/ScreenStackHost.js.map +1 -0
  135. package/lib/commonjs/components/gamma/SplitViewHost.js +57 -0
  136. package/lib/commonjs/components/gamma/SplitViewHost.js.map +1 -0
  137. package/lib/commonjs/components/gamma/SplitViewScreen.js +22 -0
  138. package/lib/commonjs/components/gamma/SplitViewScreen.js.map +1 -0
  139. package/lib/commonjs/components/gamma/StackScreen.js +51 -0
  140. package/lib/commonjs/components/gamma/StackScreen.js.map +1 -0
  141. package/lib/commonjs/fabric/BottomTabsNativeComponent.js +19 -0
  142. package/lib/commonjs/fabric/BottomTabsNativeComponent.js.map +1 -0
  143. package/lib/commonjs/fabric/BottomTabsScreenNativeComponent.js +14 -0
  144. package/lib/commonjs/fabric/BottomTabsScreenNativeComponent.js.map +1 -0
  145. package/lib/commonjs/fabric/gamma/ScreenStackHostNativeComponent.js +11 -0
  146. package/lib/commonjs/fabric/gamma/ScreenStackHostNativeComponent.js.map +1 -0
  147. package/lib/commonjs/fabric/gamma/SplitViewHostNativeComponent.js +11 -0
  148. package/lib/commonjs/fabric/gamma/SplitViewHostNativeComponent.js.map +1 -0
  149. package/lib/commonjs/fabric/gamma/SplitViewScreenNativeComponent.js +13 -0
  150. package/lib/commonjs/fabric/gamma/SplitViewScreenNativeComponent.js.map +1 -0
  151. package/lib/commonjs/fabric/gamma/StackScreenNativeComponent.js +12 -0
  152. package/lib/commonjs/fabric/gamma/StackScreenNativeComponent.js.map +1 -0
  153. package/lib/commonjs/flags.js +70 -0
  154. package/lib/commonjs/flags.js.map +1 -0
  155. package/lib/commonjs/index.js +66 -3
  156. package/lib/commonjs/index.js.map +1 -1
  157. package/lib/commonjs/utils.js +0 -28
  158. package/lib/commonjs/utils.js.map +1 -1
  159. package/lib/module/components/BottomTabs.js +48 -0
  160. package/lib/module/components/BottomTabs.js.map +1 -0
  161. package/lib/module/components/BottomTabsScreen.js +144 -0
  162. package/lib/module/components/BottomTabsScreen.js.map +1 -0
  163. package/lib/module/components/gamma/ScreenStackHost.js +20 -0
  164. package/lib/module/components/gamma/ScreenStackHost.js.map +1 -0
  165. package/lib/module/components/gamma/SplitViewHost.js +50 -0
  166. package/lib/module/components/gamma/SplitViewHost.js.map +1 -0
  167. package/lib/module/components/gamma/SplitViewScreen.js +15 -0
  168. package/lib/module/components/gamma/SplitViewScreen.js.map +1 -0
  169. package/lib/module/components/gamma/StackScreen.js +44 -0
  170. package/lib/module/components/gamma/StackScreen.js.map +1 -0
  171. package/lib/module/fabric/BottomTabsNativeComponent.js +15 -0
  172. package/lib/module/fabric/BottomTabsNativeComponent.js.map +1 -0
  173. package/lib/module/fabric/BottomTabsScreenNativeComponent.js +12 -0
  174. package/lib/module/fabric/BottomTabsScreenNativeComponent.js.map +1 -0
  175. package/lib/module/fabric/gamma/ScreenStackHostNativeComponent.js +5 -0
  176. package/lib/module/fabric/gamma/ScreenStackHostNativeComponent.js.map +1 -0
  177. package/lib/module/fabric/gamma/SplitViewHostNativeComponent.js +5 -0
  178. package/lib/module/fabric/gamma/SplitViewHostNativeComponent.js.map +1 -0
  179. package/lib/module/fabric/gamma/SplitViewScreenNativeComponent.js +7 -0
  180. package/lib/module/fabric/gamma/SplitViewScreenNativeComponent.js.map +1 -0
  181. package/lib/module/fabric/gamma/StackScreenNativeComponent.js +8 -0
  182. package/lib/module/fabric/gamma/StackScreenNativeComponent.js.map +1 -0
  183. package/lib/module/flags.js +64 -0
  184. package/lib/module/flags.js.map +1 -0
  185. package/lib/module/index.js +16 -1
  186. package/lib/module/index.js.map +1 -1
  187. package/lib/module/utils.js +0 -27
  188. package/lib/module/utils.js.map +1 -1
  189. package/lib/typescript/components/BottomTabs.d.ts +30 -0
  190. package/lib/typescript/components/BottomTabs.d.ts.map +1 -0
  191. package/lib/typescript/components/BottomTabsScreen.d.ts +56 -0
  192. package/lib/typescript/components/BottomTabsScreen.d.ts.map +1 -0
  193. package/lib/typescript/components/gamma/ScreenStackHost.d.ts +13 -0
  194. package/lib/typescript/components/gamma/ScreenStackHost.d.ts.map +1 -0
  195. package/lib/typescript/components/gamma/SplitViewHost.d.ts +13 -0
  196. package/lib/typescript/components/gamma/SplitViewHost.d.ts.map +1 -0
  197. package/lib/typescript/components/gamma/SplitViewScreen.d.ts +13 -0
  198. package/lib/typescript/components/gamma/SplitViewScreen.d.ts.map +1 -0
  199. package/lib/typescript/components/gamma/StackScreen.d.ts +21 -0
  200. package/lib/typescript/components/gamma/StackScreen.d.ts.map +1 -0
  201. package/lib/typescript/fabric/BottomTabsNativeComponent.d.ts +31 -0
  202. package/lib/typescript/fabric/BottomTabsNativeComponent.d.ts.map +1 -0
  203. package/lib/typescript/fabric/BottomTabsScreenNativeComponent.d.ts +51 -0
  204. package/lib/typescript/fabric/BottomTabsScreenNativeComponent.d.ts.map +1 -0
  205. package/lib/typescript/fabric/gamma/ScreenStackHostNativeComponent.d.ts +7 -0
  206. package/lib/typescript/fabric/gamma/ScreenStackHostNativeComponent.d.ts.map +1 -0
  207. package/lib/typescript/fabric/gamma/SplitViewHostNativeComponent.d.ts +16 -0
  208. package/lib/typescript/fabric/gamma/SplitViewHostNativeComponent.d.ts.map +1 -0
  209. package/lib/typescript/fabric/gamma/SplitViewScreenNativeComponent.d.ts +7 -0
  210. package/lib/typescript/fabric/gamma/SplitViewScreenNativeComponent.d.ts.map +1 -0
  211. package/lib/typescript/fabric/gamma/StackScreenNativeComponent.d.ts +15 -0
  212. package/lib/typescript/fabric/gamma/StackScreenNativeComponent.d.ts.map +1 -0
  213. package/lib/typescript/flags.d.ts +45 -0
  214. package/lib/typescript/flags.d.ts.map +1 -0
  215. package/lib/typescript/index.d.ts +14 -1
  216. package/lib/typescript/index.d.ts.map +1 -1
  217. package/lib/typescript/utils.d.ts +0 -26
  218. package/lib/typescript/utils.d.ts.map +1 -1
  219. package/package.json +34 -6
  220. package/react-native.config.js +2 -1
  221. package/src/components/BottomTabs.tsx +115 -0
  222. package/src/components/BottomTabsScreen.tsx +291 -0
  223. package/src/components/gamma/ScreenStackHost.tsx +32 -0
  224. package/src/components/gamma/SplitViewHost.tsx +84 -0
  225. package/src/components/gamma/SplitViewScreen.tsx +26 -0
  226. package/src/components/gamma/StackScreen.tsx +64 -0
  227. package/src/fabric/BottomTabsNativeComponent.ts +82 -0
  228. package/src/fabric/BottomTabsScreenNativeComponent.ts +107 -0
  229. package/src/fabric/gamma/ScreenStackHostNativeComponent.ts +8 -0
  230. package/src/fabric/gamma/SplitViewHostNativeComponent.ts +39 -0
  231. package/src/fabric/gamma/SplitViewScreenNativeComponent.ts +10 -0
  232. package/src/fabric/gamma/StackScreenNativeComponent.ts +25 -0
  233. package/src/flags.ts +72 -0
  234. package/src/index.tsx +18 -1
  235. package/src/utils.ts +0 -28
@@ -0,0 +1,178 @@
1
+ package com.swmansion.rnscreens.gamma.tabs
2
+
3
+ import android.util.Log
4
+ import com.facebook.react.bridge.ReadableMap
5
+ import com.facebook.react.module.annotations.ReactModule
6
+ import com.facebook.react.uimanager.ThemedReactContext
7
+ import com.facebook.react.uimanager.ViewGroupManager
8
+ import com.facebook.react.uimanager.ViewManagerDelegate
9
+ import com.facebook.react.uimanager.annotations.ReactProp
10
+ import com.facebook.react.viewmanagers.RNSBottomTabsScreenManagerDelegate
11
+ import com.facebook.react.viewmanagers.RNSBottomTabsScreenManagerInterface
12
+ import com.swmansion.rnscreens.gamma.helpers.makeEventRegistrationInfo
13
+ import com.swmansion.rnscreens.gamma.tabs.event.TabScreenDidAppearEvent
14
+ import com.swmansion.rnscreens.gamma.tabs.event.TabScreenDidDisappearEvent
15
+ import com.swmansion.rnscreens.gamma.tabs.event.TabScreenWillAppearEvent
16
+ import com.swmansion.rnscreens.gamma.tabs.event.TabScreenWillDisappearEvent
17
+
18
+ @ReactModule(name = TabScreenViewManager.REACT_CLASS)
19
+ class TabScreenViewManager :
20
+ ViewGroupManager<TabScreen>(),
21
+ RNSBottomTabsScreenManagerInterface<TabScreen> {
22
+ private val delegate: ViewManagerDelegate<TabScreen> = RNSBottomTabsScreenManagerDelegate<TabScreen, TabScreenViewManager>(this)
23
+
24
+ override fun getName() = REACT_CLASS
25
+
26
+ override fun createViewInstance(reactContext: ThemedReactContext): TabScreen {
27
+ Log.d(REACT_CLASS, "createViewInstance")
28
+ return TabScreen(reactContext)
29
+ }
30
+
31
+ override fun getDelegate() = delegate
32
+
33
+ override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> =
34
+ mutableMapOf(
35
+ makeEventRegistrationInfo(TabScreenWillAppearEvent),
36
+ makeEventRegistrationInfo(TabScreenDidAppearEvent),
37
+ makeEventRegistrationInfo(TabScreenWillDisappearEvent),
38
+ makeEventRegistrationInfo(TabScreenDidDisappearEvent),
39
+ )
40
+
41
+ override fun addEventEmitters(
42
+ reactContext: ThemedReactContext,
43
+ view: TabScreen,
44
+ ) {
45
+ super.addEventEmitters(reactContext, view)
46
+ view.onViewManagerAddEventEmitters()
47
+ }
48
+
49
+ // These should be ignored or another component, dedicated for Android should be used
50
+
51
+ override fun setTabBarBackgroundColor(
52
+ view: TabScreen,
53
+ value: Int?,
54
+ ) = Unit
55
+
56
+ override fun setTabBarBlurEffect(
57
+ view: TabScreen,
58
+ value: String?,
59
+ ) = Unit
60
+
61
+ override fun setTabBarItemTitleFontFamily(
62
+ view: TabScreen,
63
+ value: String?,
64
+ ) = Unit
65
+
66
+ override fun setTabBarItemTitleFontSize(
67
+ view: TabScreen,
68
+ value: Float,
69
+ ) = Unit
70
+
71
+ override fun setTabBarItemTitleFontWeight(
72
+ view: TabScreen,
73
+ value: String?,
74
+ ) = Unit
75
+
76
+ override fun setTabBarItemTitleFontStyle(
77
+ view: TabScreen,
78
+ value: String?,
79
+ ) = Unit
80
+
81
+ override fun setTabBarItemTitleFontColor(
82
+ view: TabScreen,
83
+ value: Int?,
84
+ ) = Unit
85
+
86
+ override fun setTabBarItemBadgeBackgroundColor(
87
+ view: TabScreen,
88
+ value: Int?,
89
+ ) = Unit
90
+
91
+ override fun setTabBarItemTitlePositionAdjustment(
92
+ view: TabScreen?,
93
+ value: ReadableMap?,
94
+ ) = Unit
95
+
96
+ override fun setTabBarItemIconColor(
97
+ view: TabScreen?,
98
+ value: Int?,
99
+ ) = Unit
100
+
101
+ override fun setIconType(
102
+ view: TabScreen?,
103
+ value: String?,
104
+ ) = Unit
105
+
106
+ override fun setIconImageSource(
107
+ view: TabScreen?,
108
+ value: ReadableMap?,
109
+ ) = Unit
110
+
111
+ override fun setIconSfSymbolName(
112
+ view: TabScreen?,
113
+ value: String?,
114
+ ) = Unit
115
+
116
+ override fun setSelectedIconImageSource(
117
+ view: TabScreen?,
118
+ value: ReadableMap?
119
+ ) = Unit
120
+
121
+ override fun setSelectedIconSfSymbolName(
122
+ view: TabScreen?,
123
+ value: String?
124
+ ) = Unit
125
+
126
+ // Annotation is Paper only
127
+ @ReactProp(name = "isFocused")
128
+ override fun setIsFocused(
129
+ view: TabScreen,
130
+ value: Boolean,
131
+ ) {
132
+ Log.d(REACT_CLASS, "TabScreen [${view.id}] setIsFocused $value")
133
+ view.isFocusedTab = value
134
+ }
135
+
136
+ @ReactProp(name = "tabKey")
137
+ override fun setTabKey(
138
+ view: TabScreen,
139
+ value: String?,
140
+ ) {
141
+ view.tabKey = value
142
+ }
143
+
144
+ override fun setBadgeValue(
145
+ view: TabScreen?,
146
+ value: String?,
147
+ ) = Unit
148
+
149
+ @ReactProp(name = "title")
150
+ override fun setTitle(
151
+ view: TabScreen,
152
+ value: String?,
153
+ ) {
154
+ view.tabTitle = value
155
+ }
156
+
157
+ @ReactProp(name = "iconResourceName")
158
+ override fun setIconResourceName(
159
+ view: TabScreen,
160
+ value: String?,
161
+ ) {
162
+ view.iconResourceName = value
163
+ }
164
+
165
+ override fun setSpecialEffects(
166
+ view: TabScreen,
167
+ value: ReadableMap?,
168
+ ) = Unit
169
+
170
+ override fun setOverrideScrollViewContentInsetAdjustmentBehavior(
171
+ view: TabScreen,
172
+ value: Boolean
173
+ ) = Unit
174
+
175
+ companion object {
176
+ const val REACT_CLASS = "RNSBottomTabsScreen"
177
+ }
178
+ }
@@ -0,0 +1,433 @@
1
+ package com.swmansion.rnscreens.gamma.tabs
2
+
3
+ import android.R
4
+ import android.content.res.ColorStateList
5
+ import android.util.Log
6
+ import android.view.Menu
7
+ import android.view.MenuItem
8
+ import android.view.ViewGroup
9
+ import android.widget.FrameLayout
10
+ import android.widget.LinearLayout
11
+ import android.widget.TextView
12
+ import androidx.core.view.children
13
+ import androidx.core.view.isVisible
14
+ import androidx.fragment.app.FragmentManager
15
+ import com.facebook.react.common.assets.ReactFontManager
16
+ import com.facebook.react.uimanager.ThemedReactContext
17
+ import com.google.android.material.bottomnavigation.BottomNavigationView
18
+ import com.google.android.material.navigation.NavigationBarView
19
+ import com.swmansion.rnscreens.BuildConfig
20
+ import com.swmansion.rnscreens.gamma.helpers.FragmentManagerHelper
21
+ import kotlin.properties.Delegates
22
+
23
+ class TabsHost(
24
+ val reactContext: ThemedReactContext,
25
+ ) : LinearLayout(reactContext),
26
+ TabScreenDelegate {
27
+ /**
28
+ * All container updates should go through instance of this class.
29
+ * The semantics are as follows:
30
+ *
31
+ * * `invalidateXXX` methods do mark that some update is required, however **they do not schedule the update**!
32
+ * * `postXXX` methods schedule an update
33
+ * * `runXXX` methods execute update synchronously
34
+ *
35
+ * If there is a posted update & before it is executed updates are flushed synchronously, then
36
+ * the posted update becomes a noop.
37
+ */
38
+ private inner class ContainerUpdateCoordinator {
39
+ private var isUpdatePending: Boolean = false
40
+
41
+ private var isSelectedTabInvalidated: Boolean = false
42
+ private var isBottomNavigationMenuInvalidated: Boolean = false
43
+
44
+ fun invalidateSelectedTab() {
45
+ isSelectedTabInvalidated = true
46
+ }
47
+
48
+ fun invalidateNavigationMenu() {
49
+ isBottomNavigationMenuInvalidated = true
50
+ }
51
+
52
+ fun invalidateAll() {
53
+ invalidateSelectedTab()
54
+ invalidateNavigationMenu()
55
+ }
56
+
57
+ fun postContainerUpdateIfNeeded() {
58
+ if (isUpdatePending) {
59
+ return
60
+ }
61
+ postContainerUpdate()
62
+ }
63
+
64
+ fun postContainerUpdate() {
65
+ isUpdatePending = true
66
+ post {
67
+ runContainerUpdateIfNeeded()
68
+ }
69
+ }
70
+
71
+ private fun runContainerUpdateIfNeeded() {
72
+ if (isUpdatePending) {
73
+ runContainerUpdate()
74
+ }
75
+ }
76
+
77
+ fun runContainerUpdate() {
78
+ isUpdatePending = false
79
+ if (isSelectedTabInvalidated) {
80
+ isSelectedTabInvalidated = false
81
+ this@TabsHost.updateSelectedTab()
82
+ }
83
+ if (isBottomNavigationMenuInvalidated) {
84
+ isBottomNavigationMenuInvalidated = false
85
+ this@TabsHost.updateBottomNavigationViewAppearance()
86
+ }
87
+ }
88
+ }
89
+
90
+ private val containerUpdateCoordinator = ContainerUpdateCoordinator()
91
+
92
+ private val bottomNavigationView: BottomNavigationView =
93
+ BottomNavigationView(reactContext).apply {
94
+ layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
95
+ }
96
+
97
+ private val contentView: FrameLayout =
98
+ FrameLayout(reactContext).apply {
99
+ layoutParams =
100
+ LinearLayout
101
+ .LayoutParams(
102
+ LayoutParams.MATCH_PARENT,
103
+ LayoutParams.WRAP_CONTENT,
104
+ ).apply {
105
+ weight = 1f
106
+ }
107
+ id = generateViewId()
108
+ }
109
+
110
+ internal lateinit var eventEmitter: TabsHostEventEmitter
111
+
112
+ private var fragmentManager: FragmentManager? = null
113
+ private val requireFragmentManager
114
+ get() = checkNotNull(fragmentManager) { "[RNScreens] Nullish fragment manager" }
115
+
116
+ private val tabScreenFragments: MutableList<TabScreenFragment> = arrayListOf()
117
+
118
+ private var isLayoutInvalidated: Boolean = false
119
+
120
+ var tabBarBackgroundColor: Int? by Delegates.observable<Int?>(null) { _, oldValue, newValue ->
121
+ updateNavigationMenuIfNeeded(oldValue, newValue)
122
+ }
123
+
124
+ var tabBarItemIconColor: Int? by Delegates.observable<Int?>(null) { _, oldValue, newValue ->
125
+ updateNavigationMenuIfNeeded(oldValue, newValue)
126
+ }
127
+
128
+ var tabBarItemTitleFontFamily: String? by Delegates.observable<String?>(null) { _, oldValue, newValue ->
129
+ updateNavigationMenuIfNeeded(oldValue, newValue)
130
+ }
131
+
132
+ var tabBarItemIconColorActive: Int? by Delegates.observable<Int?>(null) { _, oldValue, newValue ->
133
+ updateNavigationMenuIfNeeded(oldValue, newValue)
134
+ }
135
+
136
+ var tabBarItemTitleFontColor: Int? by Delegates.observable<Int?>(null) { _, oldValue, newValue ->
137
+ updateNavigationMenuIfNeeded(oldValue, newValue)
138
+ }
139
+
140
+ var tabBarItemTitleFontColorActive: Int? by Delegates.observable<Int?>(null) { _, oldValue, newValue ->
141
+ updateNavigationMenuIfNeeded(oldValue, newValue)
142
+ }
143
+
144
+ var tabBarItemTitleFontSize: Float? by Delegates.observable(null) { _, oldValue, newValue ->
145
+ updateNavigationMenuIfNeeded(oldValue, newValue)
146
+ }
147
+
148
+ var tabBarItemTitleFontSizeActive: Float? by Delegates.observable(null) { _, oldValue, newValue ->
149
+ updateNavigationMenuIfNeeded(oldValue, newValue)
150
+ }
151
+
152
+ var tabBarItemTitleFontWeight: String? by Delegates.observable(null) { _, oldValue, newValue ->
153
+ updateNavigationMenuIfNeeded(oldValue, newValue)
154
+ }
155
+
156
+ var tabBarItemTitleFontStyle: String? by Delegates.observable(null) { _, oldValue, newValue ->
157
+ updateNavigationMenuIfNeeded(oldValue, newValue)
158
+ }
159
+
160
+ private fun <T> updateNavigationMenuIfNeeded(
161
+ oldValue: T,
162
+ newValue: T,
163
+ ) {
164
+ if (newValue != oldValue) {
165
+ containerUpdateCoordinator.let {
166
+ it.invalidateNavigationMenu()
167
+ it.postContainerUpdateIfNeeded()
168
+ }
169
+ }
170
+ }
171
+
172
+ init {
173
+ orientation = VERTICAL
174
+ bottomNavigationView.labelVisibilityMode = NavigationBarView.LABEL_VISIBILITY_LABELED
175
+ addView(contentView)
176
+ addView(bottomNavigationView)
177
+
178
+ bottomNavigationView.addOnLayoutChangeListener { view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
179
+ Log.d(
180
+ TAG,
181
+ "BottomNavigationView layout changed {$left, $top} {${right - left}, ${bottom - top}}",
182
+ )
183
+ }
184
+
185
+ bottomNavigationView.setOnItemSelectedListener { item ->
186
+ Log.d(TAG, "Item selected $item")
187
+ val fragment = getFragmentForMenuItemId(item.itemId)
188
+ val tabKey = fragment?.tabScreen?.tabKey ?: "undefined"
189
+ eventEmitter.emitOnNativeFocusChange(tabKey)
190
+ true
191
+ }
192
+ }
193
+
194
+ override fun onAttachedToWindow() {
195
+ Log.d(TAG, "TabsHost [$id] attached to window")
196
+ super.onAttachedToWindow()
197
+ fragmentManager =
198
+ checkNotNull(FragmentManagerHelper.findFragmentManagerForView(this)) {
199
+ "[RNScreens] Nullish fragment manager - can't run container operations"
200
+ }
201
+ if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
202
+ // On Paper the children are not yet attached here.
203
+ containerUpdateCoordinator.let {
204
+ it.invalidateAll()
205
+ it.runContainerUpdate()
206
+ }
207
+ }
208
+ }
209
+
210
+ internal fun mountReactSubviewAt(
211
+ tabScreen: TabScreen,
212
+ index: Int,
213
+ ) {
214
+ require(index < bottomNavigationView.maxItemCount) {
215
+ "[RNScreens] Attempt to insert TabScreen at index $index; BottomNavigationView supports at most ${bottomNavigationView.maxItemCount} items"
216
+ }
217
+
218
+ val tabScreenFragment = TabScreenFragment(tabScreen)
219
+ tabScreenFragments.add(index, tabScreenFragment)
220
+ tabScreen.setTabScreenDelegate(this)
221
+ containerUpdateCoordinator.let {
222
+ it.invalidateAll()
223
+ it.postContainerUpdateIfNeeded()
224
+ }
225
+ }
226
+
227
+ internal fun unmountReactSubviewAt(index: Int) {
228
+ tabScreenFragments.removeAt(index).also {
229
+ it.tabScreen.setTabScreenDelegate(null)
230
+ containerUpdateCoordinator.let {
231
+ it.invalidateAll()
232
+ it.postContainerUpdateIfNeeded()
233
+ }
234
+ }
235
+ }
236
+
237
+ internal fun unmountReactSubview(reactSubview: TabScreen) {
238
+ tabScreenFragments.removeIf { it.tabScreen === reactSubview }.takeIf { it }?.let {
239
+ reactSubview.setTabScreenDelegate(null)
240
+ containerUpdateCoordinator.let {
241
+ it.invalidateAll()
242
+ it.postContainerUpdateIfNeeded()
243
+ }
244
+ }
245
+ }
246
+
247
+ internal fun unmountAllReactSubviews() {
248
+ tabScreenFragments.forEach { it.tabScreen.setTabScreenDelegate(null) }
249
+ tabScreenFragments.clear()
250
+ containerUpdateCoordinator.let {
251
+ it.invalidateAll()
252
+ it.postContainerUpdateIfNeeded()
253
+ }
254
+ }
255
+
256
+ override fun onTabFocusChangedFromJS(
257
+ tabScreen: TabScreen,
258
+ isFocused: Boolean,
259
+ ) {
260
+ containerUpdateCoordinator.let {
261
+ it.invalidateSelectedTab()
262
+ it.postContainerUpdateIfNeeded()
263
+ }
264
+ }
265
+
266
+ override fun onMenuItemAttributesChange(tabScreen: TabScreen) {
267
+ getMenuItemForTabScreen(tabScreen)?.let { menuItem ->
268
+ updateMenuItemOfTabScreen(menuItem, tabScreen)
269
+ }
270
+ }
271
+
272
+ override fun getFragmentForTabScreen(tabScreen: TabScreen): TabScreenFragment? = tabScreenFragments.find { it.tabScreen === tabScreen }
273
+
274
+ private fun updateBottomNavigationViewAppearance() {
275
+ Log.w(TAG, "updateBottomNavigationViewAppearance")
276
+
277
+ bottomNavigationView.isVisible = true
278
+ bottomNavigationView.setBackgroundColor(
279
+ tabBarBackgroundColor ?: com.google.android.material.R.color.m3_sys_color_light_surface_container,
280
+ )
281
+
282
+ val states = arrayOf(intArrayOf(-R.attr.state_checked), intArrayOf(R.attr.state_checked))
283
+
284
+ // Font color
285
+ val fontInactiveColor = tabBarItemTitleFontColor ?: com.google.android.material.R.color.m3_tabs_text_color_secondary
286
+ val fontActiveColor =
287
+ tabBarItemTitleFontColorActive ?: tabBarItemTitleFontColor ?: com.google.android.material.R.color.m3_tabs_text_color
288
+ val fontColors = intArrayOf(fontInactiveColor, fontActiveColor)
289
+ bottomNavigationView.itemTextColor = ColorStateList(states, fontColors)
290
+
291
+ // Icon color
292
+ val iconInactiveColor = tabBarItemIconColor ?: com.google.android.material.R.color.m3_tabs_icon_color_secondary
293
+ val iconActiveColor = tabBarItemIconColorActive ?: tabBarItemIconColor ?: com.google.android.material.R.color.m3_tabs_icon_color
294
+ val iconColors = intArrayOf(iconInactiveColor, iconActiveColor)
295
+ bottomNavigationView.itemIconTintList = ColorStateList(states, iconColors)
296
+
297
+ // First clean the menu, then populate it
298
+ bottomNavigationView.menu.clear()
299
+
300
+ tabScreenFragments.forEachIndexed { index, fragment ->
301
+ Log.d(TAG, "Add menu item: $index")
302
+ val item =
303
+ bottomNavigationView.menu.add(
304
+ Menu.NONE,
305
+ index,
306
+ Menu.NONE,
307
+ fragment.tabScreen.tabTitle,
308
+ )
309
+
310
+ item.icon = fragment.tabScreen.icon
311
+ }
312
+
313
+ // Update font styles
314
+ updateFontStyles()
315
+
316
+ bottomNavigationView.selectedItemId =
317
+ checkNotNull(getSelectedTabScreenFragmentId()) { "[RNScreens] A single selected tab must be present" }
318
+
319
+ post {
320
+ forceSubtreeMeasureAndLayoutPass()
321
+ Log.d(TAG, "BottomNavigationView request layout")
322
+ }
323
+ }
324
+
325
+ private fun updateFontStyles() {
326
+ val bottomNavigationMenuView = bottomNavigationView.getChildAt(0) as ViewGroup
327
+
328
+ for (menuItem in bottomNavigationMenuView.children) {
329
+ val largeLabel =
330
+ menuItem.findViewById<TextView>(com.google.android.material.R.id.navigation_bar_item_large_label_view)
331
+ val smallLabel =
332
+ menuItem.findViewById<TextView>(com.google.android.material.R.id.navigation_bar_item_small_label_view)
333
+
334
+ val isFontStyleItalic = tabBarItemTitleFontStyle == "italic"
335
+
336
+ // Bold is 700, normal is 400 -> https://github.com/facebook/react-native/blob/e0efd3eb5b637bd00fb7528ab4d129f6b3e13d03/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/assets/ReactFontManager.kt#L150
337
+ // It can be any other int -> https://reactnative.dev/docs/text-style-props#fontweight
338
+ // Default is 400 -> https://github.com/facebook/react-native/blob/e0efd3eb5b637bd00fb7528ab4d129f6b3e13d03/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/assets/ReactFontManager.kt#L117
339
+ val fontWeight = if (tabBarItemTitleFontWeight == "bold") 700 else tabBarItemTitleFontWeight?.toIntOrNull() ?: 400
340
+
341
+ val fontFamily =
342
+ ReactFontManager.getInstance().getTypeface(
343
+ tabBarItemTitleFontFamily ?: "",
344
+ fontWeight,
345
+ isFontStyleItalic,
346
+ reactContext.assets,
347
+ )
348
+
349
+ val smallFontSize = tabBarItemTitleFontSize?.takeIf { it > 0 } ?: 12f
350
+ val largeFontSize = tabBarItemTitleFontSizeActive?.takeIf { it > 0 } ?: 14f
351
+
352
+ // Inactive
353
+ smallLabel.textSize = smallFontSize
354
+ smallLabel.typeface = fontFamily
355
+
356
+ // Active
357
+ largeLabel.textSize = largeFontSize
358
+ largeLabel.typeface = fontFamily
359
+ }
360
+ }
361
+
362
+ private fun updateSelectedTab() {
363
+ val newFocusedTab =
364
+ checkNotNull(tabScreenFragments.find { it.tabScreen.isFocusedTab }) { "[RNScreens] No focused tab present" }
365
+
366
+ check(requireFragmentManager.fragments.size <= 1) { "[RNScreens] There can be only a single focused tab" }
367
+ val oldFocusedTab = requireFragmentManager.fragments.firstOrNull()
368
+
369
+ if (newFocusedTab === oldFocusedTab) {
370
+ return
371
+ }
372
+
373
+ if (oldFocusedTab == null) {
374
+ requireFragmentManager
375
+ .beginTransaction()
376
+ .setReorderingAllowed(true)
377
+ .apply {
378
+ this.add(contentView.id, newFocusedTab)
379
+ }.commitNowAllowingStateLoss()
380
+ } else {
381
+ requireFragmentManager
382
+ .beginTransaction()
383
+ .setReorderingAllowed(true)
384
+ .apply {
385
+ this.remove(oldFocusedTab)
386
+ this.add(contentView.id, newFocusedTab)
387
+ }.commitNowAllowingStateLoss()
388
+ }
389
+ }
390
+
391
+ private fun forceSubtreeMeasureAndLayoutPass() {
392
+ isLayoutInvalidated = false
393
+
394
+ measure(
395
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
396
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
397
+ )
398
+
399
+ layout(left, top, right, bottom)
400
+ }
401
+
402
+ private fun getFragmentForMenuItemId(itemId: Int): TabScreenFragment? = tabScreenFragments.getOrNull(itemId)
403
+
404
+ private fun getSelectedTabScreenFragmentId(): Int? {
405
+ if (tabScreenFragments.isEmpty()) {
406
+ return null
407
+ }
408
+ return checkNotNull(tabScreenFragments.indexOfFirst { it.tabScreen.isFocusedTab }) { "[RNScreens] There must be a focused tab" }
409
+ }
410
+
411
+ private fun getMenuItemForTabScreen(tabScreen: TabScreen): MenuItem? =
412
+ tabScreenFragments.indexOfFirst { it.tabScreen === tabScreen }.takeIf { it != -1 }?.let { index ->
413
+ bottomNavigationView.menu.findItem(index)
414
+ }
415
+
416
+ private fun updateMenuItemOfTabScreen(
417
+ menuItem: MenuItem,
418
+ tabScreen: TabScreen,
419
+ ) {
420
+ menuItem.title = tabScreen.tabTitle
421
+ menuItem.icon = tabScreen.icon
422
+ }
423
+
424
+ internal fun onViewManagerAddEventEmitters() {
425
+ // When this is called from View Manager the view tag is already set
426
+ check(id != NO_ID) { "[RNScreens] TabsHost must have its tag set when registering event emitters" }
427
+ eventEmitter = TabsHostEventEmitter(reactContext, id)
428
+ }
429
+
430
+ companion object {
431
+ const val TAG = "TabsHost"
432
+ }
433
+ }
@@ -0,0 +1,14 @@
1
+ package com.swmansion.rnscreens.gamma.tabs
2
+
3
+ import com.facebook.react.bridge.ReactContext
4
+ import com.swmansion.rnscreens.gamma.common.BaseEventEmitter
5
+ import com.swmansion.rnscreens.gamma.tabs.event.TabsHostNativeFocusChangeEvent
6
+
7
+ internal class TabsHostEventEmitter(
8
+ reactContext: ReactContext,
9
+ viewTag: Int,
10
+ ) : BaseEventEmitter(reactContext, viewTag) {
11
+ fun emitOnNativeFocusChange(tabKey: String) {
12
+ reactEventDispatcher.dispatchEvent(TabsHostNativeFocusChangeEvent(surfaceId, viewTag, tabKey))
13
+ }
14
+ }