@apexnative/navigation 1.0.0-alpha.5 → 1.0.0-alpha.7

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 (358) hide show
  1. package/ApexNavigation.podspec +2 -1
  2. package/CHANGELOG.md +32 -0
  3. package/README.md +1 -1
  4. package/android/CLAUDE.md +31 -20
  5. package/android/CORE.md +12 -8
  6. package/android/consumer-rules.pro +6 -0
  7. package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +36 -5
  8. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
  9. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
  10. package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +1 -1
  11. package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
  12. package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
  13. package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
  14. package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
  15. package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
  16. package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
  17. package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
  18. package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
  19. package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
  20. package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
  21. package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +7 -3
  22. package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +41 -3
  23. package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +16 -1
  24. package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +0 -2
  25. package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
  26. package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +12 -49
  27. package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
  28. package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +6 -6
  29. package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +27 -7
  30. package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +12 -1
  31. package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
  32. package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
  33. package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
  34. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +9 -7
  35. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
  36. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +13 -10
  37. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
  38. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
  39. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +7 -8
  40. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
  41. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
  42. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +20 -2
  43. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +443 -58
  44. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +33 -5
  45. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
  46. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +60 -2
  47. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +4 -0
  48. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -6
  49. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +8 -2
  50. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +32 -4
  51. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
  52. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +2 -2
  53. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +50 -7
  54. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +15 -3
  55. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +10 -4
  56. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
  57. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +215 -43
  58. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +25 -26
  59. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
  60. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
  61. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +36 -38
  62. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
  63. package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
  64. package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
  65. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
  66. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
  67. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +129 -179
  68. package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +8 -1
  69. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
  70. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +67 -3
  71. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +346 -36
  72. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
  73. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +55 -42
  74. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextMorph.kt +176 -0
  75. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
  76. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
  77. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
  78. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -24
  79. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +51 -34
  80. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
  81. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
  82. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
  83. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +121 -175
  84. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
  85. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
  86. package/android/src/main/jni/ANNContentHeights.cpp +82 -0
  87. package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
  88. package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
  89. package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
  90. package/android/src/main/res/values/styles.xml +1 -1
  91. package/android/src/main/res/values-night/styles.xml +1 -1
  92. package/cli/doctor/checks/app.js +16 -1
  93. package/cli/project.js +4 -0
  94. package/cli/setup/index.js +5 -0
  95. package/cli/setup/steps/metro.js +46 -0
  96. package/docs-md/architecture/native-screens.md +4 -0
  97. package/docs-md/architecture/theme-and-context.md +131 -12
  98. package/docs-md/architecture/transitions.md +46 -5
  99. package/docs-md/containers/modals.md +2 -0
  100. package/docs-md/containers/overlays.md +1 -1
  101. package/docs-md/containers/side-menu.md +1 -1
  102. package/docs-md/containers/tabs.md +2 -0
  103. package/docs-md/flows/bottom-sheets.md +4 -2
  104. package/docs-md/fundamentals/screens-and-props.md +9 -1
  105. package/docs-md/fundamentals/starting-the-app.md +1 -1
  106. package/docs-md/guides/cli.md +3 -1
  107. package/docs-md/help/glossary.md +2 -2
  108. package/docs-md/help/troubleshooting.md +22 -1
  109. package/docs-md/index.json +41 -1
  110. package/docs-md/migration/from-expo-router.md +1 -1
  111. package/docs-md/migration/from-react-native-navigation.md +1 -1
  112. package/docs-md/migration/from-react-navigation.md +2 -2
  113. package/docs-md/screen-ui/back-and-gestures.md +17 -1
  114. package/docs-md/screen-ui/bar-buttons.md +5 -5
  115. package/docs-md/screen-ui/search.md +1 -1
  116. package/docs-md/screen-ui/system-ui.md +16 -11
  117. package/docs-md/screen-ui/toolbar-and-fab.md +4 -2
  118. package/docs-md/screen-ui/top-bar.md +8 -4
  119. package/docs-md/start/installation.md +16 -1
  120. package/ios/Animations/ANNFrameDriver.h +55 -15
  121. package/ios/Animations/ANNFrameDriver.mm +216 -60
  122. package/ios/Animations/ANNTransitionAnimator.h +26 -5
  123. package/ios/Animations/ANNTransitionAnimator.mm +49 -3
  124. package/ios/Animations/ANNTransitionPlan.h +2 -2
  125. package/ios/Animations/ANNTransitionPlan.mm +10 -0
  126. package/ios/Animations/Elements/ANNElementFinder.h +2 -2
  127. package/ios/Animations/Elements/ANNElementFinder.mm +2 -2
  128. package/ios/Animations/Elements/ANNElementTransition.h +3 -3
  129. package/ios/Animations/Elements/ANNElementTransition.mm +2 -2
  130. package/ios/Animations/Elements/ANNSharedElementFlight.h +3 -3
  131. package/ios/Animations/Elements/ANNSharedElementFlight.mm +249 -45
  132. package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
  133. package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
  134. package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
  135. package/ios/Animations/Interpolators/ANNViewTracks.h +9 -6
  136. package/ios/Animations/Interpolators/ANNViewTracks.mm +100 -30
  137. package/ios/CLAUDE.md +32 -22
  138. package/ios/CORE.md +25 -21
  139. package/ios/Contract/ANNCommandRunner.h +8 -7
  140. package/ios/Contract/ANNCommandRunner.mm +21 -27
  141. package/ios/Contract/ANNLayoutNode.h +3 -3
  142. package/ios/Contract/ANNLayoutReader.h +3 -3
  143. package/ios/Core/ANNAppDelegate.mm +0 -2
  144. package/ios/Core/ANNDeepLinkQueue.h +6 -5
  145. package/ios/Core/ANNDeepLinkQueue.mm +22 -10
  146. package/ios/Core/ANNJavaScriptCalls.h +11 -12
  147. package/ios/Core/ANNJavaScriptCalls.mm +7 -16
  148. package/ios/Core/ANNReloadCoordinator.mm +4 -8
  149. package/ios/Core/ANNSceneDelegate.mm +2 -2
  150. package/ios/Core/ApexNavigation+Internal.h +1 -1
  151. package/ios/Core/ApexNavigation.mm +6 -2
  152. package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
  153. package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
  154. package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +4 -4
  155. package/ios/Layouts/Root/ANNRootCoordinator.mm +45 -15
  156. package/ios/Layouts/Screen/ANNComponentViewController.h +2 -2
  157. package/ios/Layouts/Screen/ANNComponentViewController.mm +13 -23
  158. package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
  159. package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
  160. package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
  161. package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
  162. package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
  163. package/ios/Layouts/Screen/ANNScreenRegions.mm +0 -38
  164. package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
  165. package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
  166. package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
  167. package/ios/Layouts/Stack/ANNPreviewPush.mm +57 -5
  168. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
  169. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
  170. package/ios/Layouts/Stack/ANNStackController.h +8 -0
  171. package/ios/Layouts/Stack/ANNStackController.mm +153 -11
  172. package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
  173. package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
  174. package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
  175. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
  176. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
  177. package/ios/Layouts/Tabs/ANNBottomTabsController.mm +2 -1
  178. package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
  179. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +4 -0
  180. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +123 -36
  181. package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
  182. package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
  183. package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
  184. package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
  185. package/ios/Options/ANNAnimationOptions.h +2 -1
  186. package/ios/Options/ANNAnimationOptions.mm +1 -1
  187. package/ios/Options/ANNBottomTabsOptions.h +2 -0
  188. package/ios/Options/ANNBottomTabsOptions.mm +1 -0
  189. package/ios/Options/ANNOptions.h +25 -0
  190. package/ios/Options/ANNOptions.mm +157 -1
  191. package/ios/Options/ANNOptionsReader.mm +108 -0
  192. package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
  193. package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
  194. package/ios/Options/Params/ANNParam.h +15 -0
  195. package/ios/Options/Params/ANNParam.mm +61 -0
  196. package/ios/Options/Params/Color.h +3 -0
  197. package/ios/Options/Params/Color.mm +21 -0
  198. package/ios/Options/Params/ColorParser.h +4 -3
  199. package/ios/Options/Params/ColorParser.mm +22 -4
  200. package/ios/Options/Params/Image.h +4 -0
  201. package/ios/Options/Params/Image.mm +20 -0
  202. package/ios/React/ANNReactView.mm +6 -25
  203. package/ios/React/ANNSafeAreaViewComponentView.mm +26 -15
  204. package/ios/React/ANNSurfaceHostingView.h +14 -0
  205. package/ios/React/ANNSurfaceHostingView.mm +144 -1
  206. package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
  207. package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
  208. package/ios/TopBar/ANNBarAppearance.h +2 -1
  209. package/ios/TopBar/ANNBarAppearance.mm +111 -30
  210. package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
  211. package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
  212. package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
  213. package/ios/TopBar/ANNBarButtons.h +5 -3
  214. package/ios/TopBar/ANNBarButtons.mm +186 -17
  215. package/ios/TopBar/ANNLargeTitlePresenter.mm +24 -4
  216. package/ios/TopBar/ANNMainList.h +3 -0
  217. package/ios/TopBar/ANNMainList.mm +4 -0
  218. package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
  219. package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
  220. package/ios/TopBar/ANNSearchPresenter.mm +2 -2
  221. package/ios/TopBar/ANNSearchResultsViewController.mm +5 -5
  222. package/ios/TopBar/ANNTopBarPresenter.h +3 -2
  223. package/ios/TopBar/ANNTopBarPresenter.mm +95 -22
  224. package/ios/TurboModules/ANNTurboModule.mm +66 -16
  225. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
  226. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
  227. package/lib/module/ARCHITECTURE.md +17 -7
  228. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  229. package/lib/module/api/app/AppRegistry.js +14 -2
  230. package/lib/module/api/app/AppRegistry.js.map +1 -1
  231. package/lib/module/api/app/createNavigation.js +188 -66
  232. package/lib/module/api/app/createNavigation.js.map +1 -1
  233. package/lib/module/api/events/navigationEvents.js +6 -2
  234. package/lib/module/api/events/navigationEvents.js.map +1 -1
  235. package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
  236. package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
  237. package/lib/module/api/linking/linking.js +11 -0
  238. package/lib/module/api/linking/linking.js.map +1 -1
  239. package/lib/module/api/options/OptionsChain.js +2 -2
  240. package/lib/module/api/screens/defineScreen.js +45 -3
  241. package/lib/module/api/screens/defineScreen.js.map +1 -1
  242. package/lib/module/api/screens/nativeScreens.js +31 -6
  243. package/lib/module/api/screens/nativeScreens.js.map +1 -1
  244. package/lib/module/api/screens/registerScreens.js +172 -35
  245. package/lib/module/api/screens/registerScreens.js.map +1 -1
  246. package/lib/module/api/theme/theme.js +5 -0
  247. package/lib/module/api/theme/theme.js.map +1 -1
  248. package/lib/module/engine/commands.js +42 -2
  249. package/lib/module/engine/commands.js.map +1 -1
  250. package/lib/module/engine/layout.js +7 -2
  251. package/lib/module/engine/layout.js.map +1 -1
  252. package/lib/module/engine/nativeScreens.js +9 -0
  253. package/lib/module/engine/nativeScreens.js.map +1 -1
  254. package/lib/module/engine/options.js +5 -4
  255. package/lib/module/engine/options.js.map +1 -1
  256. package/lib/module/engine/optionsChange.js +102 -0
  257. package/lib/module/engine/optionsChange.js.map +1 -0
  258. package/lib/module/engine/refresh.js +85 -0
  259. package/lib/module/engine/refresh.js.map +1 -0
  260. package/lib/module/engine/rememberedOptions.js +60 -0
  261. package/lib/module/engine/rememberedOptions.js.map +1 -1
  262. package/lib/module/engine/screens.js +55 -4
  263. package/lib/module/engine/screens.js.map +1 -1
  264. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +7 -0
  265. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  266. package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
  267. package/lib/typescript/api/app/createNavigation.d.ts +8 -3
  268. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  269. package/lib/typescript/api/events/navigationEvents.d.ts +2 -2
  270. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  271. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
  272. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
  273. package/lib/typescript/api/index.d.ts +1 -1
  274. package/lib/typescript/api/index.d.ts.map +1 -1
  275. package/lib/typescript/api/linking/linking.d.ts +8 -0
  276. package/lib/typescript/api/linking/linking.d.ts.map +1 -1
  277. package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
  278. package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
  279. package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
  280. package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
  281. package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
  282. package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
  283. package/lib/typescript/api/theme/theme.d.ts +2 -0
  284. package/lib/typescript/api/theme/theme.d.ts.map +1 -1
  285. package/lib/typescript/engine/commands.d.ts +5 -0
  286. package/lib/typescript/engine/commands.d.ts.map +1 -1
  287. package/lib/typescript/engine/layout.d.ts.map +1 -1
  288. package/lib/typescript/engine/nativeScreens.d.ts +5 -0
  289. package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
  290. package/lib/typescript/engine/optionsChange.d.ts +28 -0
  291. package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
  292. package/lib/typescript/engine/refresh.d.ts +35 -0
  293. package/lib/typescript/engine/refresh.d.ts.map +1 -0
  294. package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
  295. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  296. package/lib/typescript/engine/screens.d.ts +27 -6
  297. package/lib/typescript/engine/screens.d.ts.map +1 -1
  298. package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
  299. package/lib/typescript/interfaces/options/animations.d.ts +44 -14
  300. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  301. package/lib/typescript/interfaces/options/bottomTabs.d.ts +19 -6
  302. package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
  303. package/lib/typescript/interfaces/options/buttons.d.ts +10 -10
  304. package/lib/typescript/interfaces/options/modals.d.ts +6 -7
  305. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  306. package/lib/typescript/interfaces/options/stack.d.ts +8 -3
  307. package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
  308. package/lib/typescript/interfaces/options/system.d.ts +8 -5
  309. package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
  310. package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
  311. package/lib/typescript/interfaces/options/topBar.d.ts +46 -29
  312. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  313. package/metro/acceptPlugin.js +57 -0
  314. package/metro/babelTransformer.js +45 -0
  315. package/metro/index.js +36 -0
  316. package/package.json +3 -1
  317. package/src/ARCHITECTURE.md +17 -7
  318. package/src/adapters/NativeANNTurboModule.ts +7 -0
  319. package/src/api/app/AppRegistry.ts +16 -2
  320. package/src/api/app/createNavigation.ts +201 -69
  321. package/src/api/events/navigationEvents.ts +9 -3
  322. package/src/api/hooks/useSafeAreaInsets.ts +4 -3
  323. package/src/api/index.ts +1 -0
  324. package/src/api/linking/linking.ts +22 -1
  325. package/src/api/options/OptionsChain.ts +2 -2
  326. package/src/api/screens/defineScreen.ts +49 -5
  327. package/src/api/screens/nativeScreens.ts +40 -4
  328. package/src/api/screens/registerScreens.tsx +170 -42
  329. package/src/api/theme/theme.ts +5 -0
  330. package/src/engine/commands.ts +51 -6
  331. package/src/engine/layout.ts +11 -2
  332. package/src/engine/nativeScreens.ts +10 -1
  333. package/src/engine/options.ts +6 -8
  334. package/src/engine/optionsChange.ts +107 -0
  335. package/src/engine/refresh.ts +97 -0
  336. package/src/engine/rememberedOptions.ts +70 -0
  337. package/src/engine/screens.tsx +53 -8
  338. package/src/interfaces/ComponentEvents.ts +2 -2
  339. package/src/interfaces/options/animations.ts +44 -14
  340. package/src/interfaces/options/bottomTabs.ts +19 -6
  341. package/src/interfaces/options/buttons.ts +10 -10
  342. package/src/interfaces/options/modals.ts +6 -7
  343. package/src/interfaces/options/stack.ts +8 -3
  344. package/src/interfaces/options/system.ts +8 -5
  345. package/src/interfaces/options/toolbar.ts +2 -2
  346. package/src/interfaces/options/topBar.ts +46 -29
  347. package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -42
  348. package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -70
  349. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
  350. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
  351. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -136
  352. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -86
  353. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
  354. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -64
  355. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +0 -177
  356. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -137
  357. package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
  358. package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -70
@@ -40,6 +40,27 @@
40
40
  return [super get];
41
41
  }
42
42
 
43
+ - (BOOL)isSameValueAs:(ANNParam *)other {
44
+ if (other == self) {
45
+ return YES;
46
+ }
47
+ if (other.class != self.class) {
48
+ return NO;
49
+ }
50
+ Color *color = (Color *)other;
51
+ if (_light || _dark || color.light || color.dark) {
52
+ return ANNJSONValuesSame(_light, color.light) && ANNJSONValuesSame(_dark, color.dark);
53
+ }
54
+ return [super isSameValueAs:other];
55
+ }
56
+
57
+ - (NSUInteger)valueHash {
58
+ if (_light || _dark) {
59
+ return _light.hash * 31 + _dark.hash;
60
+ }
61
+ return [super valueHash];
62
+ }
63
+
43
64
  - (UIColor *)withDefault:(UIColor *)defaultValue {
44
65
  if (!self.isPartial) {
45
66
  return self.hasValue ? self.get : defaultValue;
@@ -2,9 +2,10 @@
2
2
 
3
3
  NS_ASSUME_NONNULL_BEGIN
4
4
 
5
- /// Reads `key` of the options' JSON as a color, in the forms JS sends on iOS: an ARGB number, `{ dynamic: { light, dark
6
- /// } }` with a number or a platform color on each side, `{ semantic: [...] }`, and `'NoColor'` (or `null`) for "no
7
- /// color". A value it can't read is no value and a warning in development.
5
+ /// Reads `key` of the options' JSON as a color, in the forms JS sends on iOS: an ARGB number, `{ light, dark }` (or an
6
+ /// app's `DynamicColorIOS`, `{ dynamic: { light, dark } }`) with a number or a platform color on each side,
7
+ /// `{ semantic: [...] }`, and `'NoColor'` (or `null`) for "no color". A value it can't read is no value and a warning
8
+ /// in development.
8
9
  @interface ColorParser : NSObject
9
10
 
10
11
  + (Color *)parse:(nullable NSDictionary *)json key:(NSString *)key;
@@ -49,6 +49,24 @@ static UIColor *ANNOneColor(id value) {
49
49
  return nil;
50
50
  }
51
51
 
52
+ // A pair comes as `{ light, dark }`, either side may be left out; an app's own `DynamicColorIOS` as
53
+ // `{ dynamic: { light, dark } }`. Anything else is not a pair.
54
+ static NSDictionary *ANNColorPair(id value) {
55
+ if (![value isKindOfClass:NSDictionary.class]) {
56
+ return nil;
57
+ }
58
+ id dynamic = value[@"dynamic"];
59
+ if (dynamic) {
60
+ return [dynamic isKindOfClass:NSDictionary.class] ? dynamic : nil;
61
+ }
62
+ for (id side in value) {
63
+ if (![side isEqual:@"light"] && ![side isEqual:@"dark"]) {
64
+ return nil;
65
+ }
66
+ }
67
+ return value;
68
+ }
69
+
52
70
  @implementation ColorParser
53
71
 
54
72
  + (Color *)parse:(NSDictionary *)json key:(NSString *)key {
@@ -64,8 +82,8 @@ static UIColor *ANNOneColor(id value) {
64
82
  return [[Color alloc] initWithColor:color];
65
83
  }
66
84
  NSString *path = [ANNOptionsReader pathOfKey:key];
67
- NSDictionary *pair = [value isKindOfClass:NSDictionary.class] ? value[@"dynamic"] : nil;
68
- if ([pair isKindOfClass:NSDictionary.class]) {
85
+ NSDictionary *pair = ANNColorPair(value);
86
+ if (pair) {
69
87
  UIColor *light = [self side:pair[@"light"] name:@"light" path:path];
70
88
  UIColor *dark = [self side:pair[@"dark"] name:@"dark" path:path];
71
89
  if (light || dark) {
@@ -77,8 +95,8 @@ static UIColor *ANNOneColor(id value) {
77
95
  return [[Color alloc] initWithValue:nil];
78
96
  }
79
97
 
80
- // A side of `{ dynamic: { light, dark } }`. A side that is not there is a partial pair and is silent (the sides are
81
- // optional); a side that is there but can't be read is missing too, with a warning.
98
+ // A side of `{ light, dark }`. A side that is not there is a partial pair and is silent (the sides are optional); a
99
+ // side that is there but can't be read is missing too, with a warning.
82
100
  + (UIColor *)side:(id)value name:(NSString *)name path:(NSString *)path {
83
101
  if (!value || value == NSNull.null) {
84
102
  return nil;
@@ -19,6 +19,10 @@ NS_ASSUME_NONNULL_BEGIN
19
19
  /// What the options name; nil without a value.
20
20
  @property(nonatomic, strong, readonly, nullable) ANNImageSource *source;
21
21
 
22
+ /// The same image the options name: a source by its `sourceKey` (what a file holds now isn't compared, so a file the
23
+ /// app writes again at the same path is the same value), an image that came already by identity.
24
+ - (BOOL)isSameValueAs:(nullable ANNParam *)other;
25
+
22
26
  @end
23
27
 
24
28
  NS_ASSUME_NONNULL_END
@@ -17,6 +17,26 @@
17
17
  return source ? [ANNOptionImages imageOf:source] : nil;
18
18
  }
19
19
 
20
+ - (BOOL)isSameValueAs:(ANNParam *)other {
21
+ if (other == self) {
22
+ return YES;
23
+ }
24
+ if (other.class != self.class) {
25
+ return NO;
26
+ }
27
+ ANNImageSource *source = self.source;
28
+ ANNImageSource *otherSource = ((Image *)other).source;
29
+ if (source || otherSource) {
30
+ return [source.sourceKey isEqualToString:otherSource.sourceKey];
31
+ }
32
+ return self.value == other.value;
33
+ }
34
+
35
+ - (NSUInteger)valueHash {
36
+ ANNImageSource *source = self.source;
37
+ return source ? source.sourceKey.hash : (NSUInteger)(__bridge void *)self.value;
38
+ }
39
+
20
40
  // A source that doesn't load is no image: the default then, as without a value.
21
41
  - (UIImage *)withDefault:(UIImage *)defaultValue {
22
42
  UIImage *image = self.hasValue ? self.get : nil;
@@ -8,29 +8,9 @@
8
8
  #import "ANNSurfaceHostingView.h"
9
9
  #import <React/RCTFabricSurface.h>
10
10
  #import <React/RCTSurfacePresenter.h>
11
- #import <React/RCTSurfaceStage.h>
12
11
  #import <React/RCTSurfaceView.h>
13
12
  #import <ReactCommon/RCTHost.h>
14
13
 
15
- // A surface that still starts can't be stopped yet: it is checked this often, at most this many times (5 s), and then
16
- // stopped anyway.
17
- static const NSTimeInterval ANNSurfaceStopCheckInterval = 0.05;
18
- static const NSUInteger ANNSurfaceStopCheckLimit = 100;
19
-
20
- // Stops `surface` once it runs. The block holds the surface and the view that hosts it until then: a hosting view
21
- // released earlier would stop the surface in its `dealloc`, maybe off the main queue.
22
- static void ANNStopSurfaceWhenRunning(RCTFabricSurface *surface, UIView *hostingView,
23
- NSUInteger checksLeft) {
24
- if (RCTSurfaceStageIsRunning(surface.stage) || checksLeft == 0) {
25
- [surface stop];
26
- return;
27
- }
28
- int64_t delay = (int64_t)(ANNSurfaceStopCheckInterval * NSEC_PER_SEC);
29
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay), dispatch_get_main_queue(), ^{
30
- ANNStopSurfaceWhenRunning(surface, hostingView, checksLeft - 1);
31
- });
32
- }
33
-
34
14
  @interface ANNReactView () <RCTSurfacePresenterObserver, ANNScreenReadinessListener>
35
15
  @end
36
16
 
@@ -359,14 +339,15 @@ static void ANNStopSurfaceWhenRunning(RCTFabricSurface *surface, UIView *hosting
359
339
  _reactViewReadyBlock = nil;
360
340
  _mountHandlers = nil;
361
341
  [_contentDidMountHandlers removeAllObjects];
362
- RCTFabricSurface *surface = _surface;
363
- if (surface) {
342
+ ANNSurfaceHostingView *hostingView = _hostingView;
343
+ if (_surface && hostingView) {
364
344
  // On the next turn of the main queue: a destroy that runs inside React Native's mounting (a wait that ends in
365
345
  // a mount, a root that replaces at once) would stop the surface there, React Native would drop its last
366
- // transaction and recycle its root view with the old content still in it, to show in a later screen.
367
- UIView *hostingView = _hostingView;
346
+ // transaction and recycle its root view with the old content still in it, to show in a later screen. The
347
+ // hosting view stops the surface then, or at its start when it doesn't run yet; the block keeps the hosting
348
+ // view, and with it the surface, until it asks.
368
349
  dispatch_async(dispatch_get_main_queue(), ^{
369
- ANNStopSurfaceWhenRunning(surface, hostingView, ANNSurfaceStopCheckLimit);
350
+ [hostingView stopSurfaceWhenRunning];
370
351
  });
371
352
  }
372
353
  }
@@ -1,11 +1,14 @@
1
1
  #import "ANNSafeAreaViewComponentView.h"
2
- #import "ANNScreenInsets.h"
2
+ #import "ANNScreenEnvironment.h"
3
3
  #import "ANNScreenReadiness.h"
4
4
  #import "../cpp/react/renderer/components/ApexNavigationSpec/ANNSafeAreaViewComponentDescriptor.h"
5
5
  #import <React/RCTConversions.h>
6
6
 
7
7
  using namespace facebook::react;
8
8
 
9
+ @interface ANNSafeAreaViewComponentView () <ANNScreenPaddingListener>
10
+ @end
11
+
9
12
  @implementation ANNSafeAreaViewComponentView {
10
13
  ANNSafeAreaViewShadowNode::ConcreteState::Shared _state;
11
14
  NSString *_screenId;
@@ -22,21 +25,13 @@ using namespace facebook::react;
22
25
  if (self = [super initWithFrame:frame]) {
23
26
  static const auto defaultProps = std::make_shared<const ANNSafeAreaViewProps>();
24
27
  _props = defaultProps;
25
- [NSNotificationCenter.defaultCenter addObserver:self
26
- selector:@selector(paddingDidChange:)
27
- name:ANNScreenSafeAreaPaddingDidChangeNotification
28
- object:nil];
29
28
  }
30
29
  return self;
31
30
  }
32
31
 
33
- - (void)dealloc {
34
- [NSNotificationCenter.defaultCenter removeObserver:self];
35
- }
36
-
37
32
  - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps {
38
33
  const auto &newProps = static_cast<const ANNSafeAreaViewProps &>(*props);
39
- _screenId = RCTNSStringFromStringNilIfEmpty(newProps.screenId);
34
+ [self listenToScreenId:RCTNSStringFromStringNilIfEmpty(newProps.screenId)];
40
35
  // The screen's readiness comes from this view, never from its root's children.
41
36
  if (_screenId != nil) {
42
37
  [ANNScreenReadiness screenHasSafeAreaView:_screenId];
@@ -70,18 +65,33 @@ using namespace facebook::react;
70
65
  [self applyPadding];
71
66
  }
72
67
 
73
- - (void)paddingDidChange:(NSNotification *)notification {
74
- if ([notification.object isEqual:_screenId]) {
75
- [self applyPadding];
68
+ // The view hears the padding changes of its screen only. It listens before it applies the padding it has, at each
69
+ // props, so a change between the two can't be missed.
70
+ - (void)listenToScreenId:(nullable NSString *)screenId {
71
+ if ((screenId == nil && _screenId == nil) || [screenId isEqualToString:_screenId]) {
72
+ return;
73
+ }
74
+ if (_screenId != nil) {
75
+ [ANNScreenEnvironment removePaddingListener:self forScreenId:_screenId];
76
+ }
77
+ _screenId = screenId;
78
+ if (screenId != nil) {
79
+ [ANNScreenEnvironment addPaddingListener:self forScreenId:screenId];
76
80
  }
77
81
  }
78
82
 
83
+ #pragma mark - ANNScreenPaddingListener
84
+
85
+ - (void)screenPaddingDidChange {
86
+ [self applyPadding];
87
+ }
88
+
79
89
  // The padding of the screen goes to the state, which lays the view out with it.
80
90
  - (void)applyPadding {
81
91
  if (_state == nullptr || _screenId == nil) {
82
92
  return;
83
93
  }
84
- UIEdgeInsets padding = [ANNScreenInsets paddingForScreenId:_screenId];
94
+ UIEdgeInsets padding = [ANNScreenEnvironment paddingForScreenId:_screenId];
85
95
  EdgeInsets insets{.left = (Float)padding.left,
86
96
  .top = (Float)padding.top,
87
97
  .right = (Float)padding.right,
@@ -144,7 +154,8 @@ using namespace facebook::react;
144
154
  - (void)prepareForRecycle {
145
155
  [super prepareForRecycle];
146
156
  _state.reset();
147
- _screenId = nil;
157
+ // A recycled view gets its next screen's id with its next props.
158
+ [self listenToScreenId:nil];
148
159
  _readySent = NO;
149
160
  _contentRenderedSent = NO;
150
161
  _contentRendered = NO;
@@ -8,10 +8,24 @@ NS_ASSUME_NONNULL_BEGIN
8
8
  /// every change on.
9
9
  @interface ANNSurfaceHostingView : RCTSurfaceHostingView
10
10
 
11
+ /// A size transition of the window (a rotation, a new window size) runs until its coordinator completes. Meanwhile React
12
+ /// content takes its new size without animation (`layOutContent:`). Each call ends with its own transition; main queue.
13
+ + (void)followSizeTransition:(id<UIViewControllerTransitionCoordinator>)coordinator;
14
+
15
+ /// Runs a layout that gives React content a new size: without animation while a size transition runs, as it is
16
+ /// otherwise. Main queue.
17
+ + (void)layOutContent:(void (^)(void))layout;
18
+
11
19
  /// Called at each mount of the hosted surface: after a change of the surface's size, the first one lays the surface
12
20
  /// out again, so a render that was on its way during the change doesn't keep the old size's layout.
13
21
  - (void)surfaceDidMount;
14
22
 
23
+ /// The screen went: the hosted surface stops as soon as it runs, on the main queue. A surface that runs already stops
24
+ /// at once; one that doesn't run yet stops at its change to running (`surface:didChangeStage:`). Until then this view
25
+ /// and its surface are kept, so the core gives them up on the main queue. A surface that hasn't started after 5 s is
26
+ /// let go, and stops if it ever starts later. Once; main queue.
27
+ - (void)stopSurfaceWhenRunning;
28
+
15
29
  @end
16
30
 
17
31
  NS_ASSUME_NONNULL_END
@@ -1,9 +1,52 @@
1
1
  #import "ANNSurfaceHostingView.h"
2
+ #import "ANNAssert.h"
2
3
  #import <React/RCTFabricSurface.h>
4
+ #import <objc/runtime.h>
5
+
6
+ // How long a gone screen's surface that doesn't run yet is kept for its stop. The host starts a surface through a weak
7
+ // reference once its JavaScript ran, so a surface let go before that, which nothing else holds, goes away unstarted.
8
+ static const NSTimeInterval ANNSurfaceStartWaitLimit = 5.0;
9
+ static const void *ANNSurfaceStopperKey = &ANNSurfaceStopperKey;
10
+
11
+ // The hosting views whose surface stops at its start; each keeps its surface. Main queue.
12
+ static NSMutableSet<ANNSurfaceHostingView *> *ANNHostingViewsStoppingAtStart;
13
+
14
+ // The size transitions of the window that run now. Main queue.
15
+ static NSInteger ANNSizeTransitionCount;
16
+
17
+ // The delegate of a surface its hosting view let go at the limit: the surface keeps it, and it keeps nothing, so
18
+ // neither holds the other alive. A surface that something else still starts (a start already on its way holds its
19
+ // surface; React Native restarts every surface it knows after a reload, holding it) stops at once, instead of running
20
+ // with no screen and going away running, which leaves its React tree registered.
21
+ @interface ANNSurfaceStopper : NSObject <RCTSurfaceDelegate>
22
+ @end
23
+
24
+ @implementation ANNSurfaceStopper
25
+
26
+ // React Native's delegate method names the deprecated `RCTSurface`; the surface is an `RCTFabricSurface`.
27
+ #pragma clang diagnostic push
28
+ #pragma clang diagnostic ignored "-Wdeprecated"
29
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
30
+ - (void)surface:(RCTSurface *)surface didChangeStage:(RCTSurfaceStage)stage {
31
+ if (!RCTSurfaceStageIsRunning(stage)) {
32
+ return;
33
+ }
34
+ // The start tells this off the main queue, before it ends: the stop goes on the main queue, the block keeping the
35
+ // surface until then.
36
+ id<RCTSurfaceProtocol> startedSurface = (id<RCTSurfaceProtocol>)surface;
37
+ dispatch_async(dispatch_get_main_queue(), ^{
38
+ [startedSurface stop];
39
+ });
40
+ }
41
+ #pragma clang diagnostic pop
42
+
43
+ @end
3
44
 
4
45
  @implementation ANNSurfaceHostingView {
5
46
  // The surface was given a new size since its last mount.
6
47
  BOOL _surfaceSizeChanged;
48
+ // The screen went: the surface stops each time it runs. Main queue.
49
+ BOOL _stopsWhenRunning;
7
50
  }
8
51
 
9
52
  // React Native 0.87 posts the hosting view's traits as the app's scheme from here (`RCTSurfaceHostingView.mm`). On
@@ -20,6 +63,28 @@
20
63
 
21
64
  #pragma mark - The surface's size
22
65
 
66
+ // UIKit lays the screens out inside the animation of a rotation or a new window size, and React Native commits and
67
+ // mounts the new layout synchronously inside the layout that gives it the size: every frame of its views animated
68
+ // from the old size to the new one, and what a view draws itself (an SVG, a glass view, text) was drawn once at the new
69
+ // size and stretched along that animation. During such a transition the content takes its new layout at once; the
70
+ // hosting view itself still follows the animation. Other animated size changes (a sheet's detent, a split view's
71
+ // column) keep the content's frames animated, so content pinned to the moving edge rides along with it.
72
+ + (void)followSizeTransition:(id<UIViewControllerTransitionCoordinator>)coordinator {
73
+ ANNSizeTransitionCount++;
74
+ [coordinator animateAlongsideTransition:nil
75
+ completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
76
+ ANNSizeTransitionCount--;
77
+ }];
78
+ }
79
+
80
+ + (void)layOutContent:(void (^)(void))layout {
81
+ if (ANNSizeTransitionCount > 0) {
82
+ [UIView performWithoutAnimation:layout];
83
+ } else {
84
+ layout();
85
+ }
86
+ }
87
+
23
88
  // React Native gives the surface its size here. A size change is noted for the next mount (see below). The note is
24
89
  // put aside during the call: the change's own commit mounts inside it and must not take an earlier note, or a size
25
90
  // that changes every frame (a sheet dragged between detents, a window resized) would lay out three times a frame. A
@@ -30,7 +95,9 @@
30
95
  CGSize maximumSize = surface.maximumSize;
31
96
  BOOL sizeChangedBefore = _surfaceSizeChanged;
32
97
  _surfaceSizeChanged = NO;
33
- [super layoutSubviews];
98
+ [ANNSurfaceHostingView layOutContent:^{
99
+ [super layoutSubviews];
100
+ }];
34
101
  BOOL sizeChanged = surface && (!CGSizeEqualToSize(minimumSize, surface.minimumSize) ||
35
102
  !CGSizeEqualToSize(maximumSize, surface.maximumSize));
36
103
  _surfaceSizeChanged = sizeChanged || sizeChangedBefore;
@@ -60,6 +127,82 @@
60
127
  [surface setMinimumSize:minimumSize maximumSize:maximumSize];
61
128
  }
62
129
 
130
+ #pragma mark - The stop
131
+
132
+ // `RCTFabricSurface stop` does nothing while the surface doesn't run, and the host starts a surface later, off the main
133
+ // queue: a screen can go before that. Its surface then stops at its change to running. This view is kept until then,
134
+ // with its surface: released earlier, it would stop the surface in its `dealloc`, maybe off the main queue.
135
+ - (void)stopSurfaceWhenRunning {
136
+ ANNAssertMainQueue();
137
+ if (_stopsWhenRunning) {
138
+ return;
139
+ }
140
+ _stopsWhenRunning = YES;
141
+ RCTFabricSurface *surface = [self fabricSurface];
142
+ if (!surface) {
143
+ return;
144
+ }
145
+ if (RCTSurfaceStageIsRunning(surface.stage)) {
146
+ [surface stop];
147
+ return;
148
+ }
149
+ if (!ANNHostingViewsStoppingAtStart) {
150
+ ANNHostingViewsStoppingAtStart = [NSMutableSet new];
151
+ }
152
+ [ANNHostingViewsStoppingAtStart addObject:self];
153
+ __weak ANNSurfaceHostingView *weakSelf = self;
154
+ int64_t delay = (int64_t)(ANNSurfaceStartWaitLimit * NSEC_PER_SEC);
155
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay), dispatch_get_main_queue(), ^{
156
+ [weakSelf letSurfaceGo];
157
+ });
158
+ }
159
+
160
+ // The surface tells its stage off the main queue, from its start and its stop. React Native's hosting view follows it
161
+ // as before; a gone screen's surface that starts stops on the next turn of the main queue, once its start ended. The
162
+ // block keeps this view, and with it the surface, until then, so their last release is on the main queue.
163
+ #pragma clang diagnostic push
164
+ #pragma clang diagnostic ignored "-Wdeprecated"
165
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
166
+ - (void)surface:(RCTSurface *)surface didChangeStage:(RCTSurfaceStage)stage {
167
+ [super surface:surface didChangeStage:stage];
168
+ if (!RCTSurfaceStageIsRunning(stage)) {
169
+ return;
170
+ }
171
+ dispatch_async(dispatch_get_main_queue(), ^{
172
+ [self stopSurfaceIfAsked];
173
+ });
174
+ }
175
+ #pragma clang diagnostic pop
176
+
177
+ - (void)stopSurfaceIfAsked {
178
+ if (!_stopsWhenRunning) {
179
+ return;
180
+ }
181
+ RCTFabricSurface *surface = [self fabricSurface];
182
+ if (surface && RCTSurfaceStageIsRunning(surface.stage)) {
183
+ [surface stop];
184
+ }
185
+ [ANNHostingViewsStoppingAtStart removeObject:self];
186
+ }
187
+
188
+ // The limit: the surface hasn't started. It gets a delegate of its own that stops it at any later start, and this
189
+ // view lets it go. The delegate changes before the check: a start told before the change reaches this view, which
190
+ // stops the surface (its block keeps the view); one told after it reaches the new delegate.
191
+ - (void)letSurfaceGo {
192
+ if (![ANNHostingViewsStoppingAtStart containsObject:self]) {
193
+ return;
194
+ }
195
+ RCTFabricSurface *surface = [self fabricSurface];
196
+ ANNSurfaceStopper *stopper = [ANNSurfaceStopper new];
197
+ objc_setAssociatedObject(surface, ANNSurfaceStopperKey, stopper,
198
+ OBJC_ASSOCIATION_RETAIN_NONATOMIC);
199
+ surface.delegate = stopper;
200
+ if (RCTSurfaceStageIsRunning(surface.stage)) {
201
+ [surface stop];
202
+ }
203
+ [ANNHostingViewsStoppingAtStart removeObject:self];
204
+ }
205
+
63
206
  - (nullable RCTFabricSurface *)fabricSurface {
64
207
  id surface = self.surface;
65
208
  return [surface isKindOfClass:RCTFabricSurface.class] ? surface : nil;
@@ -3,6 +3,7 @@
3
3
  #import "ANNScreenAppearance.h"
4
4
  #import "ANNScreenReadiness.h"
5
5
  #import "ANNSlotHost.h"
6
+ #import "ANNSurfaceHostingView.h"
6
7
  #import "../../cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h"
7
8
  #import <React/RCTConstants.h>
8
9
  #import <React/RCTSurfacePresenter.h>
@@ -62,12 +63,17 @@
62
63
  return nil;
63
64
  }
64
65
 
65
- // The slot lays its content out at this size: every change goes to it (rotation, a sheet detent, the bars).
66
+ // The slot lays its content out at this size: every change goes to it (rotation, a sheet detent, the bars). Its state
67
+ // update mounts synchronously, here: during a rotation the content takes its new layout at once, as a screen's surface
68
+ // does (`+[ANNSurfaceHostingView layOutContent:]`).
66
69
  - (void)layoutSubviews {
67
70
  [super layoutSubviews];
68
71
  if (!CGSizeEqualToSize(self.bounds.size, _lastSize)) {
69
72
  _lastSize = self.bounds.size;
70
- [ANNSlotContainers containerDidChange:_componentId];
73
+ NSString *componentId = _componentId;
74
+ [ANNSurfaceHostingView layOutContent:^{
75
+ [ANNSlotContainers containerDidChange:componentId];
76
+ }];
71
77
  }
72
78
  }
73
79
 
@@ -2,6 +2,7 @@
2
2
  #import "ANNExternalViewController.h"
3
3
  #import "ANNIconButtonImage.h"
4
4
  #import "ANNStackController.h"
5
+ #import "ANNTabsDesign.h"
5
6
  #import <objc/runtime.h>
6
7
 
7
8
  // The item below that the core configured: what it set may go again.
@@ -141,10 +142,8 @@ static const void *ANNBackButtonConfiguredKey = &ANNBackButtonConfiguredKey;
141
142
  ANNBackBarButtonItem *item = [ANNBackBarButtonItem new];
142
143
  item.allowsMenu = [backButton.enableMenu withDefault:YES];
143
144
  item.style = UIBarButtonItemStylePlain;
144
- BOOL before26 = YES;
145
- if (@available(iOS 26.0, *)) {
146
- before26 = NO;
147
- }
145
+ // The compatibility mode of iOS 26 shows the title below too, as the design before it.
146
+ BOOL before26 = !ANNTabsDesign.isGlass;
148
147
  item.title = title ?: (before26 ? belowTitle : nil);
149
148
  UIColor *background = [self iconBackgroundColorOf:backButton];
150
149
  if (background) {
@@ -165,7 +164,9 @@ static const void *ANNBackButtonConfiguredKey = &ANNBackButtonConfiguredKey;
165
164
  [iconBackground.height withDefault:0])
166
165
  insets:UIEdgeInsetsZero];
167
166
  if (@available(iOS 26.0, *)) {
168
- item.hidesSharedBackground = [backButton.hideSharedBackground withDefault:YES];
167
+ if (ANNTabsDesign.isGlass) {
168
+ item.hidesSharedBackground = [backButton.hideSharedBackground withDefault:YES];
169
+ }
169
170
  }
170
171
  }
171
172
  return item;
@@ -45,7 +45,8 @@ typedef struct {
45
45
  /// (a clear color is a clear bar). The one rule for the bar, its color in the list and the top edge effect.
46
46
  + (nullable UIColor *)solidColorOf:(nullable Color *)color traits:(UITraitCollection *)traits;
47
47
 
48
- /// A 1×1 image of `color` resolved for `traits`.
48
+ /// A 1×1 image of `color` resolved for `traits`, with a variant for each style: the same image for the same resolved
49
+ /// colors, so a look filled again equals the one before (as the back button's tinted arrow).
49
50
  + (UIImage *)imageOfColor:(UIColor *)color traits:(UITraitCollection *)traits;
50
51
 
51
52
  @end