@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
@@ -34,12 +34,14 @@ class TopBarOptions {
34
34
  @JvmField var rightButtonDisabledColor: ThemeColour = NullThemeColour()
35
35
  @JvmField var centerButtonColor: ThemeColour = NullThemeColour()
36
36
  @JvmField var centerButtonDisabledColor: ThemeColour = NullThemeColour()
37
+ @JvmField var menuBackgroundColor: ThemeColour = NullThemeColour()
38
+ @JvmField var menuTextColor: ThemeColour = NullThemeColour()
37
39
  @JvmField var drawBehind: Bool = NullBool()
38
40
  @JvmField var testID: Text = NullText()
39
41
  @JvmField var title = TitleOptions()
40
42
  @JvmField var subtitle = SubtitleOptions()
41
43
  /** `backButton`, `leftButtons`, `rightButtons`, `centerButtons`. */
42
- @JvmField var buttons = TopBarButtons()
44
+ @ReadFromParent @JvmField var buttons = TopBarButtons()
43
45
  @JvmField var customizationId: Text = NullText()
44
46
  /** Shared between copies, as its module wants. */
45
47
  @JvmField var overflowMenu: ButtonMenuOptions? = null
@@ -63,7 +65,8 @@ class TopBarOptions {
63
65
  hideOnTap.hasValue() || hideOnKeyboard.hasValue() || leftButtonColor.hasValue() ||
64
66
  rightButtonColor.hasValue() || leftButtonBackgroundColor.hasValue() || rightButtonBackgroundColor.hasValue() ||
65
67
  leftButtonDisabledColor.hasValue() || rightButtonDisabledColor.hasValue() || centerButtonColor.hasValue() ||
66
- centerButtonDisabledColor.hasValue() || drawBehind.hasValue() || testID.hasValue() || title.hasValue() ||
68
+ centerButtonDisabledColor.hasValue() || menuBackgroundColor.hasValue() || menuTextColor.hasValue() ||
69
+ drawBehind.hasValue() || testID.hasValue() || title.hasValue() ||
67
70
  subtitle.hasValue() || buttons.hasValue() || customizationId.hasValue() || overflowMenu != null ||
68
71
  verticalBarKeeps.hasValue() || background.hasValue() || scrollEdgeAppearance.hasValue() ||
69
72
  barStyle.hasValue() || style.hasValue() || noBorder.hasValue() || searchBar.hasAnyValue() ||
@@ -85,6 +88,8 @@ class TopBarOptions {
85
88
  rightButtonDisabledColor = rightButtonDisabledColor.mergeWith(other.rightButtonDisabledColor)
86
89
  centerButtonColor = centerButtonColor.mergeWith(other.centerButtonColor)
87
90
  centerButtonDisabledColor = centerButtonDisabledColor.mergeWith(other.centerButtonDisabledColor)
91
+ menuBackgroundColor = menuBackgroundColor.mergeWith(other.menuBackgroundColor)
92
+ menuTextColor = menuTextColor.mergeWith(other.menuTextColor)
88
93
  if (other.drawBehind.hasValue()) drawBehind = other.drawBehind
89
94
  if (other.testID.hasValue()) testID = other.testID
90
95
  title.mergeWith(other.title)
@@ -127,6 +132,8 @@ class TopBarOptions {
127
132
  rightButtonDisabledColor = rightButtonDisabledColor.withDefault(defaults.rightButtonDisabledColor)
128
133
  centerButtonColor = centerButtonColor.withDefault(defaults.centerButtonColor)
129
134
  centerButtonDisabledColor = centerButtonDisabledColor.withDefault(defaults.centerButtonDisabledColor)
135
+ menuBackgroundColor = menuBackgroundColor.withDefault(defaults.menuBackgroundColor)
136
+ menuTextColor = menuTextColor.withDefault(defaults.menuTextColor)
130
137
  if (!drawBehind.hasValue()) drawBehind = defaults.drawBehind
131
138
  if (!testID.hasValue()) testID = defaults.testID
132
139
  title.mergeWithDefault(defaults.title)
@@ -178,6 +185,8 @@ class TopBarOptions {
178
185
  options.rightButtonDisabledColor = ThemeColour.parse(context, map, "rightButtonDisabledColor")
179
186
  options.centerButtonColor = ThemeColour.parse(context, map, "centerButtonColor")
180
187
  options.centerButtonDisabledColor = ThemeColour.parse(context, map, "centerButtonDisabledColor")
188
+ options.menuBackgroundColor = ThemeColour.parse(context, map, "menuBackgroundColor")
189
+ options.menuTextColor = ThemeColour.parse(context, map, "menuTextColor")
181
190
  options.drawBehind = BoolParser.parse(map, "drawBehind")
182
191
  options.testID = TextParser.parse(map, "testID")
183
192
  options.title = map.group("title") { TitleOptions.parse(context, it) }
@@ -212,13 +221,13 @@ class TopBarOptions {
212
221
  * don't give it; `[]` takes the buttons of the layers below away.
213
222
  */
214
223
  class TopBarButtons {
215
- @JvmField var back = BackButtonOptions()
224
+ @OptionKey("backButton") @JvmField var back = BackButtonOptions()
216
225
  /** `topBar.leftButtons`; null when the options don't give them. */
217
- @JvmField var left: List<ButtonOptions>? = null
226
+ @OptionKey("leftButtons") @JvmField var left: List<ButtonOptions>? = null
218
227
  /** `topBar.rightButtons`; null when the options don't give them. */
219
- @JvmField var right: List<ButtonOptions>? = null
228
+ @OptionKey("rightButtons") @JvmField var right: List<ButtonOptions>? = null
220
229
  /** `topBar.centerButtons`; null when the options don't give them. */
221
- @JvmField var center: List<ButtonOptions>? = null
230
+ @OptionKey("centerButtons") @JvmField var center: List<ButtonOptions>? = null
222
231
 
223
232
  fun hasValue(): Boolean = back.hasValue() || left != null || right != null || center != null
224
233
 
@@ -263,7 +272,7 @@ class BackButtonOptions {
263
272
  @JvmField var showTitle: Bool = NullBool()
264
273
  @JvmField var color: ThemeColour = NullThemeColour()
265
274
  @JvmField var fontSize: Fraction = NullFraction()
266
- @JvmField var font = FontOptions()
275
+ @ReadFromParent @JvmField var font = FontOptions()
267
276
  @JvmField var iconBackground = IconBackgroundOptions()
268
277
  @JvmField var testID: Text = NullText()
269
278
  @JvmField var enableMenu: Bool = NullBool()
@@ -0,0 +1,95 @@
1
+ package dev.apexnative.navigation.react
2
+
3
+ import android.content.Context
4
+ import android.util.Log
5
+ import com.facebook.react.interfaces.fabric.ReactSurface
6
+ import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
7
+ import com.facebook.react.modules.i18nmanager.I18nUtil
8
+ import com.facebook.react.runtime.ReactSurfaceImpl
9
+ import com.facebook.soloader.SoLoader
10
+ import dev.apexnative.navigation.utils.NavigationLog
11
+
12
+ /**
13
+ * The height of React content laid out at a width without a height limit, as iOS measures a sheet's `'content'`
14
+ * detent: Fabric lays out a copy of the tree, so the shown layout stays as it is. A React root's surface measures
15
+ * through `SurfaceHandler::measure`; a slot's content in the slot's own layout, once asked for ([requestSlot]), which
16
+ * keeps the height for [slotHeight] (`ANNSlotContentHeights`, the C++ iOS shares). The native half is the library's
17
+ * own C++ (`android/src/main/jni`), in the codegen library of its components; when that library doesn't load, nothing
18
+ * is measured. Main thread.
19
+ */
20
+ internal object ContentHeights {
21
+ private const val LIBRARY = "react_codegen_ApexNavigationSpec"
22
+
23
+ private val loaded: Boolean by lazy {
24
+ try {
25
+ SoLoader.loadLibrary(LIBRARY)
26
+ true
27
+ } catch (e: Throwable) {
28
+ Log.w(NavigationLog.TAG, "[ApexNavigation] $LIBRARY doesn't load: no content is measured", e)
29
+ false
30
+ }
31
+ }
32
+
33
+ /**
34
+ * The height of [surface]'s content at [width] pixels, in pixels, with the layout context the surface lays out
35
+ * with (`ReactSurfaceImpl.updateLayoutSpecs`); null before the surface runs or renders, or when it can't measure.
36
+ */
37
+ fun surfaceHeight(context: Context, surface: ReactSurface, width: Int): Int? {
38
+ if (width <= 0 || surface !is ReactSurfaceImpl || !loaded) return null
39
+ val density = context.resources.displayMetrics.density
40
+ val scalesFont = ReactNativeFeatureFlags.enableFontScaleChangesUpdatingLayout()
41
+ val fontScale = if (scalesFont) context.resources.configuration.fontScale else 1f
42
+ val height = try {
43
+ nativeSurfaceHeight(
44
+ surface,
45
+ width / density,
46
+ density,
47
+ fontScale,
48
+ I18nUtil.instance.isRTL(context),
49
+ I18nUtil.instance.doLeftAndRightSwapInRTL(context),
50
+ )
51
+ } catch (e: Throwable) {
52
+ Log.w(NavigationLog.TAG, "[ApexNavigation] the content of a surface can't be measured", e)
53
+ return null
54
+ }
55
+ return if (height > 0f) Math.round(height * density) else null
56
+ }
57
+
58
+ /** The content of the slot [slotId] is measured from its next layout on. */
59
+ fun requestSlot(slotId: String) {
60
+ if (loaded) nativeRequestSlot(slotId)
61
+ }
62
+
63
+ /** The content of the slot [slotId] is measured. */
64
+ fun isSlotRequested(slotId: String): Boolean = loaded && nativeIsSlotRequested(slotId)
65
+
66
+ /** The height of the content of the slot [slotId] at its last layout, in dp; null before it. */
67
+ fun slotHeight(slotId: String): Float? = if (loaded) nativeSlotHeight(slotId).takeIf { it >= 0f } else null
68
+
69
+ /** The screen of the slot [slotId] went: nothing more is measured for it. */
70
+ fun forgetSlot(slotId: String) {
71
+ if (loaded) nativeForgetSlot(slotId)
72
+ }
73
+
74
+ @JvmStatic
75
+ private external fun nativeRequestSlot(slotId: String)
76
+
77
+ @JvmStatic
78
+ private external fun nativeIsSlotRequested(slotId: String): Boolean
79
+
80
+ @JvmStatic
81
+ private external fun nativeSlotHeight(slotId: String): Float
82
+
83
+ @JvmStatic
84
+ private external fun nativeForgetSlot(slotId: String)
85
+
86
+ @JvmStatic
87
+ private external fun nativeSurfaceHeight(
88
+ surface: ReactSurfaceImpl,
89
+ width: Float,
90
+ pointScale: Float,
91
+ fontScale: Float,
92
+ rtl: Boolean,
93
+ swapLeftAndRight: Boolean,
94
+ ): Float
95
+ }
@@ -0,0 +1,66 @@
1
+ package dev.apexnative.navigation.react
2
+
3
+ import com.facebook.react.bridge.UIManager
4
+ import com.facebook.react.bridge.UIManagerListener
5
+ import com.facebook.react.common.annotations.UnstableReactNativeAPI
6
+ import com.facebook.react.uimanager.UIManagerHelper
7
+ import com.facebook.react.uimanager.common.UIManagerType
8
+ import dev.apexnative.navigation.NavigationApplication
9
+
10
+ /**
11
+ * The handlers of a screen's content that hear each mount of Fabric (`IReactView.setOnContentMounted`), by key, while
12
+ * [hears] holds (the content is in a window and not destroyed). Fabric tells every mount of the app, on the main
13
+ * thread, not those of one surface: the handlers measure and compare, so a mount elsewhere costs only that. It listens
14
+ * only while a handler waits. Main thread only.
15
+ */
16
+ internal class ContentMounts(private val hears: () -> Boolean) {
17
+ private val handlers = LinkedHashMap<String, Runnable>()
18
+ // The UI manager that tells the mounts; none while no handler waits.
19
+ private var uiManager: UIManager? = null
20
+
21
+ // React Native marks the listener unstable; it is the one place that tells a mount.
22
+ @OptIn(UnstableReactNativeAPI::class)
23
+ private val listener = object : UIManagerListener {
24
+ override fun willDispatchViewUpdates(uiManager: UIManager) = Unit
25
+
26
+ override fun willMountItems(uiManager: UIManager) = Unit
27
+
28
+ override fun didMountItems(uiManager: UIManager) {
29
+ if (hears()) run()
30
+ }
31
+
32
+ override fun didDispatchMountItems(uiManager: UIManager) = Unit
33
+
34
+ override fun didScheduleMountItems(uiManager: UIManager) = Unit
35
+ }
36
+
37
+ /** [handler] runs after each mount under [key]; null removes the handler of [key]. */
38
+ fun set(key: String, handler: Runnable?) {
39
+ if (handler == null) handlers.remove(key) else handlers[key] = handler
40
+ if (handlers.isEmpty()) stop() else start()
41
+ }
42
+
43
+ /** Runs the handlers now: a mount the content tells itself. */
44
+ fun run() = handlers.values.toList().forEach(Runnable::run)
45
+
46
+ /** The content goes: no handler runs again. */
47
+ fun clear() {
48
+ handlers.clear()
49
+ stop()
50
+ }
51
+
52
+ @OptIn(UnstableReactNativeAPI::class)
53
+ private fun start() {
54
+ if (uiManager != null) return
55
+ val reactContext = NavigationApplication.instanceOrNull?.reactGatewayOrNull?.currentReactContext ?: return
56
+ uiManager = UIManagerHelper.getUIManager(reactContext, UIManagerType.FABRIC)?.also {
57
+ it.addUIManagerEventListener(listener)
58
+ }
59
+ }
60
+
61
+ @OptIn(UnstableReactNativeAPI::class)
62
+ private fun stop() {
63
+ uiManager?.removeUIManagerEventListener(listener)
64
+ uiManager = null
65
+ }
66
+ }
@@ -55,6 +55,10 @@ class NavigationTurboModule(reactContext: ReactApplicationContext) : NativeANNTu
55
55
  override fun mergeOptions(componentId: String, options: ReadableMap) =
56
56
  optionsCommands.mergeOptions(componentId, options)
57
57
 
58
+ // Development (Fast Refresh): the options an edit took out of a screen that shows; as mergeOptions without one.
59
+ override fun clearOptions(componentId: String, paths: ReadableArray) =
60
+ optionsCommands.clearOptions(componentId, paths)
61
+
58
62
  override fun push(commandId: String, componentId: String, layout: ReadableMap, promise: Promise) {
59
63
  val what = "push the screen '${LayoutReader.idOf(layout)}'"
60
64
  runner.run(
@@ -236,11 +240,11 @@ class NavigationTurboModule(reactContext: ReactApplicationContext) : NativeANNTu
236
240
  check = {},
237
241
  ) { _, answer -> navigator().stackCommands.setColumnShown(componentId, column, shown, answer) }
238
242
 
239
- override fun getArrangementState(componentId: String): WritableMap = ArrangementStates.get(componentId)
243
+ override fun getArrangementState(componentId: String): WritableMap = ScreenEnvironment.getArrangementState(componentId)
240
244
 
241
- override fun getScreenInsets(componentId: String): WritableMap = ScreenInsets.get(componentId)
245
+ override fun getScreenInsets(componentId: String): WritableMap = ScreenEnvironment.getScreenInsets(componentId)
242
246
 
243
- override fun getScreenRegions(componentId: String): WritableMap = ScreenRegions.get(componentId)
247
+ override fun getScreenRegions(componentId: String): WritableMap = ScreenEnvironment.getScreenRegions(componentId)
244
248
 
245
249
  // With the activity the user sees, if there is one; the hinge remembers it and goes on with the next activity
246
250
  // (`Hinge.attach`).
@@ -1,8 +1,11 @@
1
1
  package dev.apexnative.navigation.react
2
2
 
3
3
  import android.content.Context
4
+ import com.facebook.react.bridge.ReadableArray
4
5
  import com.facebook.react.bridge.ReadableMap
6
+ import com.facebook.react.bridge.ReadableType
5
7
  import dev.apexnative.navigation.options.DefaultOptions
8
+ import dev.apexnative.navigation.options.OptionPaths
6
9
  import dev.apexnative.navigation.options.Options
7
10
  import dev.apexnative.navigation.options.OptionsReader
8
11
  import dev.apexnative.navigation.system.AppDirection
@@ -10,9 +13,9 @@ import dev.apexnative.navigation.viewcontrollers.navigator.Navigators
10
13
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
11
14
 
12
15
  /**
13
- * The commands of the options, `setDefaultOptions` and `mergeOptions`: both without a promise. Their options are read
14
- * on the module thread, in the runner's step before the main task; the main task stores and applies them. What can't be
15
- * read is logged (`[ApexNavigation] <method>: <text>`); the app goes on.
16
+ * The commands of the options, `setDefaultOptions`, `mergeOptions` and `clearOptions`: all without a promise. Their
17
+ * options and paths are read on the module thread, in the runner's step before the main task; the main task stores
18
+ * and applies them. What can't be read is logged (`[ApexNavigation] <method>: <text>`); the app goes on.
16
19
  */
17
20
  internal class OptionsCommands(
18
21
  private val runner: CommandRunner,
@@ -30,6 +33,11 @@ internal class OptionsCommands(
30
33
  * system UI follows (`Navigator.optionsMerged`, `mergeOptions` step 5). Main thread.
31
34
  */
32
35
  private val merged: (ViewController<*>, Options) -> Unit = { _, _ -> },
36
+ /**
37
+ * Repaints what shows in the current navigation activity, as [repaint] does without new default options
38
+ * (`Navigator.repaint`), after `clearOptions`. Main thread.
39
+ */
40
+ private val repaintCurrent: () -> Unit = {},
33
41
  ) {
34
42
  constructor(runner: CommandRunner, context: () -> Context?) : this(
35
43
  runner = runner,
@@ -40,6 +48,7 @@ internal class OptionsCommands(
40
48
  Navigators.all().forEach { it.navigator?.defaultOptionsChanged(direction) }
41
49
  },
42
50
  merged = { node, options -> Navigators.current()?.navigator?.optionsMerged(node, options) },
51
+ repaintCurrent = { Navigators.current()?.navigator?.repaint() },
43
52
  )
44
53
 
45
54
  /**
@@ -82,8 +91,37 @@ internal class OptionsCommands(
82
91
  merged(node, read.options)
83
92
  }
84
93
 
94
+ /**
95
+ * `clearOptions` (development: Fast Refresh, after an edit took options out of a screen that shows): the values at
96
+ * [paths] (`topBar.subtitle`; a group's path for the whole group) leave the own options of the node [componentId]
97
+ * names, through `ViewController.changeOptions` ([OptionPaths.clear]), so it resolves to what the layouts around it
98
+ * and the defaults give there; the user layer stays. The stack it is in writes its back arrow's visibility into
99
+ * them again (`StackController.ownOptionsCleared`), then what shows is repainted as after `setDefaultOptions`. No
100
+ * node with that id, or no path that names an option: nothing, not even a log line. Without an activity: one log
101
+ * line, as `mergeOptions`.
102
+ */
103
+ fun clearOptions(componentId: String, paths: ReadableArray) = runner.runVoid(
104
+ name = CLEAR_OPTIONS,
105
+ what = "clear the options of the screen '$componentId'",
106
+ prepare = {
107
+ (0 until paths.size()).filter { paths.getType(it) == ReadableType.String }.mapNotNull(paths::getString)
108
+ },
109
+ ) { read ->
110
+ if (!runner.hasActivity()) {
111
+ runner.logFailure(CLEAR_OPTIONS, ErrorTexts.nowhereToShow())
112
+ return@runVoid
113
+ }
114
+ val node = findNode(componentId) ?: return@runVoid
115
+ val known = read.filter { OptionPaths.find(node.options, it) != null }
116
+ if (known.isEmpty()) return@runVoid
117
+ node.changeOptions { own -> known.forEach { OptionPaths.clear(own, it) } }
118
+ node.findParentStack()?.ownOptionsCleared(node)
119
+ repaintCurrent()
120
+ }
121
+
85
122
  private companion object {
86
123
  const val SET_DEFAULT_OPTIONS = "setDefaultOptions"
87
124
  const val MERGE_OPTIONS = "mergeOptions"
125
+ const val CLEAR_OPTIONS = "clearOptions"
88
126
  }
89
127
  }
@@ -3,6 +3,7 @@ package dev.apexnative.navigation.react
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
5
  import android.os.Bundle
6
+ import android.util.Size
6
7
  import android.view.MotionEvent
7
8
  import android.view.ViewGroup
8
9
  import android.widget.FrameLayout
@@ -28,7 +29,9 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ScrollEventListe
28
29
  * never makes this one ready. A screen without a safe area view (no view ever told anything of its id) is ready once
29
30
  * the root view of its surface has children, read on this view's own layout passes. A root that renders at once
30
31
  * ([rendersAtOnce]: a component of a bar, a tab's cell) marks its id so from before its surface starts until it stops.
31
- * Ready once, ready for good; what waits for it ([whenReady]) runs then, once.
32
+ * Ready once, ready for good; what waits for it ([whenReady]) runs then, once. Its content measures without a height
33
+ * limit ([contentSize], `ContentHeights`), and the handlers of [setOnContentMounted] hear each mount of Fabric while
34
+ * the view is in a window.
32
35
  * Without a signal or children, the limits of what waits decide: the view has no timer of its own. It owns the
33
36
  * record of its appearance, which the dispatcher of the appearance events holds until the content is ready
34
37
  * (`AppearanceDispatcher`).
@@ -51,6 +54,7 @@ class ReactView @JvmOverloads constructor(
51
54
  private var readyTold = false
52
55
  private val readyListeners = mutableListOf<Runnable>()
53
56
  private var listening = false
57
+ private val mounts = ContentMounts { !destroyed && isAttachedToWindow }
54
58
 
55
59
  // What this root hears of its id: only the signals of its own surface.
56
60
  private val readiness = object : ScreenReadiness.Listener {
@@ -112,6 +116,16 @@ class ReactView @JvmOverloads constructor(
112
116
  override val touchRoot: ViewGroup?
113
117
  get() = surface?.view as? ViewGroup
114
118
 
119
+ override fun contentSize(maxWidth: Int): Size? {
120
+ val surface = surface ?: return null
121
+ val height = ContentHeights.surfaceHeight(context, surface, maxWidth) ?: return null
122
+ return Size(maxWidth, height)
123
+ }
124
+
125
+ override fun setOnContentMounted(key: String, handler: Runnable?) {
126
+ if (!destroyed) mounts.set(key, handler)
127
+ }
128
+
115
129
  override fun isReady(): Boolean {
116
130
  if (!ready && hasRendered()) ready = true
117
131
  return ready
@@ -167,6 +181,7 @@ class ReactView @JvmOverloads constructor(
167
181
  AppearanceDispatcher.didDisappear(appearance)
168
182
  destroyed = true
169
183
  readyListeners.clear()
184
+ mounts.clear()
170
185
  stopListening()
171
186
  if (rendersAtOnce) ScreenReadiness.setRendersAtOnce(componentId, this, false)
172
187
  val surface = surface ?: return
@@ -82,8 +82,6 @@ internal object ReloadCoordinator {
82
82
  UiThread.runOnMain {
83
83
  launches.contextReady(context)
84
84
  NativeScreenCalls.failAll { name, componentId -> ErrorTexts.nativeCallReloaded(name, componentId) }
85
- // Scroll listeners that found no event dispatcher listen now.
86
- ScrollEvents.dispatcherReady()
87
85
  Navigators.all().filter { it.isNavigationResumed }.forEach(::launchIfDue)
88
86
  }
89
87
  }
@@ -0,0 +1,177 @@
1
+ package dev.apexnative.navigation.react
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.WritableArray
5
+ import com.facebook.react.bridge.WritableMap
6
+ import dev.apexnative.navigation.react.events.EventContract
7
+ import dev.apexnative.navigation.react.events.EventEmitter
8
+ import java.util.concurrent.ConcurrentHashMap
9
+
10
+ /**
11
+ * What JavaScript knows of each screen's place, by the screen's id, in one record: its safe area (the insets JavaScript
12
+ * gets and the padding of its safe area view, `useSafeAreaInsets`), its reserved regions (`useReservedRegions`) and,
13
+ * in a pair, its arrangement (`useArrangement`). The core measures them (`ScreenSafeArea`, [ScreenRegions], the pair's
14
+ * plan) on the main thread and keeps them here; JavaScript reads them from its own thread for a screen's first render
15
+ * and gets every change as an event. The record is immutable and replaced at once, so a reader on another thread sees
16
+ * one whole record. When the screen goes, [remove] clears all of it. As on iOS (`ANNScreenEnvironment`).
17
+ */
18
+ internal object ScreenEnvironment {
19
+ /** How much covers each edge of a screen's content, in dp. */
20
+ data class Insets(val top: Double, val right: Double, val bottom: Double, val left: Double)
21
+
22
+ /** A reserved region, in dp from the top left of the screen's content. */
23
+ data class Region(val x: Double, val y: Double, val width: Double, val height: Double)
24
+
25
+ /** Where a screen divides (`division`) and where hardware covers it (`occlusion`). */
26
+ data class Regions(val division: List<Region>, val occlusion: List<Region>) {
27
+ val isEmpty: Boolean get() = division.isEmpty() && occlusion.isEmpty()
28
+ }
29
+
30
+ /** Which screen of its pair a screen is, how the pair lies, whether it lies over the other one and is hidden. */
31
+ data class Arrangement(val placement: String, val layout: String, val front: Boolean, val hidden: Boolean)
32
+
33
+ /** Where the changes JavaScript hears go: the events of the screen's JavaScript, or a test's record. */
34
+ interface Changes {
35
+ fun insetsChanged(screenId: String, insets: Insets)
36
+
37
+ fun regionsChanged(screenId: String, regions: Regions)
38
+
39
+ fun arrangementChanged(screenId: String, arrangement: Arrangement)
40
+ }
41
+
42
+ // One screen's record; null for a fact not known yet (not laid out, outside a pair).
43
+ private data class Record(
44
+ val insets: Insets? = null,
45
+ val padding: Insets? = null,
46
+ val regions: Regions? = null,
47
+ val arrangement: Arrangement? = null,
48
+ )
49
+
50
+ private val NO_REGIONS = Regions(emptyList(), emptyList())
51
+ private val records = ConcurrentHashMap<String, Record>()
52
+ // Main thread only.
53
+ private val paddingListeners = HashMap<String, MutableSet<() -> Unit>>()
54
+
55
+ /**
56
+ * Keeps the screen's [insets] for JavaScript (what still covers its content) and the [padding] of its safe area
57
+ * view. Calls the screen's padding listeners when the padding changed, then tells JavaScript when the insets did.
58
+ * Main thread only.
59
+ */
60
+ fun setInsets(screenId: String, insets: Insets, padding: Insets, changes: Changes) {
61
+ val before = update(screenId) { it.copy(insets = insets, padding = padding) }
62
+ if (before?.padding != padding) paddingListeners[screenId]?.toList()?.forEach { it() }
63
+ if (before?.insets != insets) changes.insetsChanged(screenId, insets)
64
+ }
65
+
66
+ /**
67
+ * Keeps the screen's [regions], and tells JavaScript when they changed; none at first is what JavaScript reads
68
+ * before the first layout, so it isn't told. Main thread only.
69
+ */
70
+ fun setRegions(screenId: String, regions: Regions, changes: Changes) {
71
+ val before = update(screenId) { it.copy(regions = regions) }?.regions
72
+ if (before == regions || (before == null && regions.isEmpty)) return
73
+ changes.regionsChanged(screenId, regions)
74
+ }
75
+
76
+ /** Keeps the screen's [arrangement], and tells JavaScript when it changed and [notify] holds. Main thread only. */
77
+ fun setArrangement(screenId: String, arrangement: Arrangement, notify: Boolean, changes: Changes) {
78
+ val before = update(screenId) { it.copy(arrangement = arrangement) }?.arrangement
79
+ if (before == arrangement || !notify) return
80
+ changes.arrangementChanged(screenId, arrangement)
81
+ }
82
+
83
+ /** The padding of the screen's safe area view; none before the screen was laid out. Any thread. */
84
+ fun padding(screenId: String): Insets? = records[screenId]?.padding
85
+
86
+ /** The insets JavaScript gets; none before the screen was laid out. Any thread. */
87
+ fun insets(screenId: String): Insets? = records[screenId]?.insets
88
+
89
+ /** The screen's reserved regions; none before its first layout. Any thread. */
90
+ fun regions(screenId: String): Regions? = records[screenId]?.regions
91
+
92
+ /** The screen's arrangement; none outside a pair. Any thread. */
93
+ fun arrangement(screenId: String): Arrangement? = records[screenId]?.arrangement
94
+
95
+ /**
96
+ * Calls `listener` when the padding of the screen changes; returns the function that stops it. Main thread only.
97
+ */
98
+ fun listenToPadding(screenId: String, listener: () -> Unit): () -> Unit {
99
+ paddingListeners.getOrPut(screenId) { LinkedHashSet() }.add(listener)
100
+ return {
101
+ paddingListeners[screenId]?.let {
102
+ it.remove(listener)
103
+ if (it.isEmpty()) paddingListeners.remove(screenId)
104
+ }
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Forgets everything kept for the screen. Its padding listeners stay: a live safe area view keeps listening while
110
+ * it keeps the id, and stops itself.
111
+ */
112
+ fun remove(screenId: String) {
113
+ records.remove(screenId)
114
+ }
115
+
116
+ /** `getScreenInsets`: `{ top, right, bottom, left }`, empty before the screen was laid out. Any thread. */
117
+ fun getScreenInsets(screenId: String): WritableMap = insets(screenId)?.let(::toMap) ?: Arguments.createMap()
118
+
119
+ /** `getScreenRegions`: `{ division, occlusion }`, empty lists before the first layout. Any thread. */
120
+ fun getScreenRegions(screenId: String): WritableMap = toMap(regions(screenId) ?: NO_REGIONS)
121
+
122
+ /** `getArrangementState`: `{ placement, layout, front, hidden }`, empty outside a pair. Any thread. */
123
+ fun getArrangementState(screenId: String): WritableMap =
124
+ arrangement(screenId)?.let(::toMap) ?: Arguments.createMap()
125
+
126
+ /** The changes as events through [emitter], the one of the JavaScript the screen was made for. */
127
+ fun changesOf(emitter: EventEmitter): Changes = object : Changes {
128
+ override fun insetsChanged(screenId: String, insets: Insets) =
129
+ emitter.emitScreenInsetsChanged(screenId, toMap(insets))
130
+
131
+ override fun regionsChanged(screenId: String, regions: Regions) =
132
+ emitter.emitScreenRegionsChanged(screenId, toMap(regions))
133
+
134
+ override fun arrangementChanged(screenId: String, arrangement: Arrangement) =
135
+ emitter.emitArrangementChanged(screenId, toMap(arrangement))
136
+ }
137
+
138
+ // Replaces the screen's record at once with [change] of it; returns the record before, null for a new screen.
139
+ private fun update(screenId: String, change: (Record) -> Record): Record? {
140
+ var before: Record? = null
141
+ records.compute(screenId) { _, old ->
142
+ before = old
143
+ change(old ?: Record())
144
+ }
145
+ return before
146
+ }
147
+
148
+ private fun toMap(value: Insets): WritableMap = Arguments.createMap().apply {
149
+ putDouble(EventContract.ScreenInsetsChanged.TOP, value.top)
150
+ putDouble(EventContract.ScreenInsetsChanged.RIGHT, value.right)
151
+ putDouble(EventContract.ScreenInsetsChanged.BOTTOM, value.bottom)
152
+ putDouble(EventContract.ScreenInsetsChanged.LEFT, value.left)
153
+ }
154
+
155
+ private fun toMap(value: Regions): WritableMap = Arguments.createMap().apply {
156
+ putArray(EventContract.ScreenRegionsChanged.DIVISION, toArray(value.division))
157
+ putArray(EventContract.ScreenRegionsChanged.OCCLUSION, toArray(value.occlusion))
158
+ }
159
+
160
+ private fun toArray(regions: List<Region>): WritableArray = Arguments.createArray().apply {
161
+ for (region in regions) {
162
+ pushMap(Arguments.createMap().apply {
163
+ putDouble(EventContract.ScreenRegionRect.X, region.x)
164
+ putDouble(EventContract.ScreenRegionRect.Y, region.y)
165
+ putDouble(EventContract.ScreenRegionRect.WIDTH, region.width)
166
+ putDouble(EventContract.ScreenRegionRect.HEIGHT, region.height)
167
+ })
168
+ }
169
+ }
170
+
171
+ private fun toMap(value: Arrangement): WritableMap = Arguments.createMap().apply {
172
+ putString(EventContract.ArrangementChanged.PLACEMENT, value.placement)
173
+ putString(EventContract.ArrangementChanged.LAYOUT, value.layout)
174
+ putBoolean(EventContract.ArrangementChanged.FRONT, value.front)
175
+ putBoolean(EventContract.ArrangementChanged.HIDDEN, value.hidden)
176
+ }
177
+ }