@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
@@ -14,11 +14,15 @@ import android.widget.FrameLayout
14
14
  import androidx.coordinatorlayout.widget.CoordinatorLayout
15
15
  import androidx.core.view.ViewCompat
16
16
  import androidx.core.view.WindowInsetsCompat
17
+ import dev.apexnative.navigation.options.DefaultOptions
17
18
  import dev.apexnative.navigation.options.ModalAnimationOptions
18
19
  import dev.apexnative.navigation.options.Options
19
20
  import dev.apexnative.navigation.utils.TestIds
20
21
  import dev.apexnative.navigation.utils.dp
22
+ import dev.apexnative.navigation.viewcontrollers.component.ComponentViewController
23
+ import dev.apexnative.navigation.viewcontrollers.parent.ParentController
21
24
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
25
+ import dev.apexnative.navigation.views.animations.AnimationLayer
22
26
  import dev.apexnative.navigation.views.animations.AnimationRules
23
27
  import dev.apexnative.navigation.views.animations.SideAnimator
24
28
  import dev.apexnative.navigation.views.element.ElementTransition
@@ -95,7 +99,7 @@ internal class ModalViews(
95
99
  val container = parts.container
96
100
  container.isWaiting = false
97
101
  container.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
98
- if (record.isSheet) return presentSheet(record, parts, options, seen, events, onEnd)
102
+ if (record.isSheet) return presentSheet(record, parts, options, animated, seen, events, onEnd)
99
103
  applyTestIds(record, parts, options)
100
104
  if (!animated || !seen) {
101
105
  SideAnimator.rest(container)
@@ -117,11 +121,13 @@ internal class ModalViews(
117
121
  }
118
122
 
119
123
  // The root's view leaves the container and goes into the sheet's, in one turn; the sheet slides to its first
120
- // detent. Nobody sees the activity: the show counts as done now, the sheet's later end does nothing.
124
+ // detent, or stands there at once when not [animated]. Nobody sees the activity: the show counts as done now, the
125
+ // sheet's later end does nothing.
121
126
  private fun presentSheet(
122
127
  record: ModalRecord,
123
128
  parts: Parts,
124
129
  options: Options,
130
+ animated: Boolean,
125
131
  seen: Boolean,
126
132
  events: ModalSheet.Events,
127
133
  onEnd: () -> Unit,
@@ -129,7 +135,16 @@ internal class ModalViews(
129
135
  val container = parts.container
130
136
  val view = record.root.view
131
137
  container.removeView(view)
132
- val sheet = ModalSheet(container, view, events)
138
+ val sheet = ModalSheet(
139
+ container,
140
+ view,
141
+ events,
142
+ shownContent = { (shownScreen(record.root) as? ComponentViewController)?.content },
143
+ adjustsForKeyboard = {
144
+ val screen = shownScreen(record.root)
145
+ screen == null || !screen.resolveCurrentOptions(DefaultOptions.current).layout.adjustResize.isFalse
146
+ },
147
+ )
133
148
  parts.sheet = sheet
134
149
  container.alpha = 1f
135
150
  applyTestIds(record, parts, options)
@@ -140,11 +155,18 @@ internal class ModalViews(
140
155
  onEnd()
141
156
  }
142
157
  }
143
- sheet.show(options.modal.sheet, swipeToDismissOf(options)) { shown() }
158
+ sheet.show(options.modal.sheet, swipeToDismissOf(options), animated) { shown() }
144
159
  if (!seen) shown()
145
160
  return { told = true }
146
161
  }
147
162
 
163
+ // The screen a layout shows now: down through the child each parent shows.
164
+ private fun shownScreen(root: ViewController<*>): ViewController<*>? {
165
+ var node = root
166
+ while (node is ParentController<*>) node = node.currentChild ?: return null
167
+ return node
168
+ }
169
+
148
170
  override fun dismiss(
149
171
  record: ModalRecord,
150
172
  animated: Boolean,
@@ -158,7 +180,7 @@ internal class ModalViews(
158
180
  if (sheet != null) {
159
181
  if (!seen) return none(onEnd)
160
182
  var told = false
161
- sheet.close {
183
+ sheet.close(animated) {
162
184
  if (!told) {
163
185
  told = true
164
186
  onEnd()
@@ -263,6 +285,8 @@ internal class ModalViews(
263
285
  parts[record]?.container?.requestLayout()
264
286
  }
265
287
 
288
+ override fun sheetBack(record: ModalRecord): SheetBack? = parts[record]?.sheet
289
+
266
290
  // `modal.testID` on the modal's view; a sheet's on the sheet's container, `modal.sheet.testID` first, and its
267
291
  // grabber and scrim after it.
268
292
  private fun applyTestIds(record: ModalRecord, parts: Parts, options: Options) {
@@ -293,7 +317,11 @@ internal class ModalViews(
293
317
  val under = motion.underSide?.let { side -> underView?.let { SideAnimator.build(it, side, underBase) } }
294
318
  if (under != null && underView != null) {
295
319
  if (showing) moved[underView] = record else moved.remove(underView)
320
+ AnimationLayer.during(underView, under)
296
321
  }
322
+ // A fade of the modal or of the layer under it draws it through a hardware layer, given back at its end and at
323
+ // any stop (the set's cancel cancels it).
324
+ for (animator in listOfNotNull(side, fade)) AnimationLayer.during(container, animator)
297
325
  val fadeLength = motion.side?.takeIf(AnimationRules::isSingleAlpha)?.let(AnimationRules::lengthOf)
298
326
  val elements = motion.elements?.let { animation ->
299
327
  val content = modalsLayer.parent as? ViewGroup ?: return@let null
@@ -0,0 +1,42 @@
1
+ package dev.apexnative.navigation.viewcontrollers.modal
2
+
3
+ import androidx.activity.BackEventCompat
4
+
5
+ /**
6
+ * A modal's sheet as back sees it ([ModalSheet]): whether a release of back would hide it, and the preview of that
7
+ * hide during the back gesture (Android 14+). [ModalCoordinator.backTargetSheet] gives the one back would dismiss now.
8
+ * Main thread only.
9
+ */
10
+ internal interface SheetBack {
11
+ /**
12
+ * Back's release would hide the sheet, as a drag down does: it stands at a detent from which it goes down (its
13
+ * smallest, while it lets the user close it), laid out, and it isn't closing already. False at a larger detent:
14
+ * the Material behavior would only collapse it there.
15
+ */
16
+ val hidesOnBack: Boolean
17
+
18
+ /**
19
+ * Starts the preview of back on the sheet with the gesture's first [event]: the sheet shrinks a little toward the
20
+ * bottom with the progress, the scrim following it. None when [hidesOnBack] doesn't hold or a preview runs.
21
+ */
22
+ fun startBackPreview(event: BackEventCompat): Gesture?
23
+
24
+ /** A back gesture over the sheet ([startBackPreview]). */
25
+ interface Gesture {
26
+ /** The sheet the gesture previews. */
27
+ val sheet: SheetBack
28
+
29
+ /** The gesture moved. */
30
+ fun progress(event: BackEventCompat)
31
+
32
+ /** Cancelled, or back went elsewhere: the sheet grows back to where it stood, the scrim with it. */
33
+ fun cancel()
34
+
35
+ /**
36
+ * Released: the sheet slides down from where the preview left it and goes as one the user dragged down (its
37
+ * events, its dismissal). False when the preview stopped since it started (a command closed or moved the
38
+ * sheet): back goes the usual way.
39
+ */
40
+ fun commit(): Boolean
41
+ }
42
+ }
@@ -3,10 +3,68 @@ package dev.apexnative.navigation.viewcontrollers.modal
3
3
  import dev.apexnative.navigation.options.SheetDetent
4
4
 
5
5
  /**
6
- * Where a shown sheet goes at new options, as on iOS: the sheet keeps the stop it stands at (the user's last drag
7
- * included) until a `selectedDetent` arrives in a `mergeOptions`, or until its stop is no longer among the detents.
6
+ * Where a sheet rests: the stops it keeps of its detents, how the Material behavior stands at them, and where a shown
7
+ * sheet goes at new options, as on iOS: the sheet keeps the stop it stands at (the user's last drag included) until a
8
+ * `selectedDetent` arrives in a `mergeOptions`, or until its stop is no longer among the detents.
8
9
  */
9
10
  internal object SheetStops {
11
+ /** The Material behavior has three resting states: collapsed, half expanded and expanded. */
12
+ const val MAX_STOPS = 3
13
+
14
+ /**
15
+ * The stops of the sheet, from the lowest: [detents] by their height ([heightOf]), one of each height (the first
16
+ * the options give), and at most [MAX_STOPS] of them: the lowest, the highest, and between them [selected] when
17
+ * it stands there, else the lowest of the rest.
18
+ */
19
+ fun stops(detents: List<SheetDetent>, heightOf: (SheetDetent) -> Int, selected: SheetDetent?): List<SheetDetent> {
20
+ val sorted = detents.sortedBy(heightOf).distinctBy(heightOf)
21
+ if (sorted.size <= MAX_STOPS) return sorted
22
+ val between = sorted.subList(1, sorted.size - 1)
23
+ val middle = selected?.takeIf { it in between } ?: between.first()
24
+ return listOf(sorted.first(), middle, sorted.last())
25
+ }
26
+
27
+ /**
28
+ * How the Material behavior stands at the stops: [fitToContents] with one or two (collapsed and expanded, the
29
+ * sheet as tall as the highest, [maxHeight]), else half expanded between them at [halfExpandedRatio] of the
30
+ * layout, and expanded [expandedOffset] below its top. [skipCollapsed] with one stop: the sheet has only that.
31
+ * The heights are in pixels, as is the rest.
32
+ */
33
+ data class Material(
34
+ val fitToContents: Boolean,
35
+ val skipCollapsed: Boolean,
36
+ val peekHeight: Int,
37
+ val halfExpandedRatio: Float?,
38
+ val expandedOffset: Int,
39
+ val maxHeight: Int,
40
+ )
41
+
42
+ /**
43
+ * The behavior's settings for stops [heights] tall (from the lowest, each higher than the one before) in a layout
44
+ * [parentHeight] tall.
45
+ */
46
+ fun material(heights: List<Int>, parentHeight: Int): Material {
47
+ val lowest = heights.first()
48
+ val highest = heights.last()
49
+ val three = heights.size == MAX_STOPS && parentHeight > 0
50
+ return Material(
51
+ fitToContents = !three,
52
+ skipCollapsed = heights.size == 1,
53
+ peekHeight = lowest,
54
+ halfExpandedRatio = if (three) heights[1].toFloat() / parentHeight else null,
55
+ expandedOffset = (parentHeight - highest).coerceAtLeast(0),
56
+ maxHeight = highest,
57
+ )
58
+ }
59
+
60
+ /**
61
+ * The heights of stops [heights] tall (from the lowest, each higher) lifted by [lift] pixels, the part of the
62
+ * keyboard over the navigation bar: the sheet stands on the keyboard as it stood on the bottom. At most [full]
63
+ * each, and each still higher than the one below, so the behavior keeps all of its states.
64
+ */
65
+ fun lifted(heights: List<Int>, lift: Int, full: Int): List<Int> =
66
+ heights.mapIndexed { i, height -> (height + lift).coerceAtMost(full - (heights.lastIndex - i)) }
67
+
10
68
  /**
11
69
  * The stop to move to, or null to stay: [arrived] (the `selectedDetent` the merge names) when it is among
12
70
  * [detents]; else, when [settled] (the stop the sheet rests at) is no longer one of them, [selected] (the resolved
@@ -33,6 +33,7 @@ import dev.apexnative.navigation.viewcontrollers.externalcomponent.ExternalCompo
33
33
  import dev.apexnative.navigation.viewcontrollers.modal.ModalCoordinator
34
34
  import dev.apexnative.navigation.viewcontrollers.modal.ModalRecord
35
35
  import dev.apexnative.navigation.viewcontrollers.modal.ModalViews
36
+ import dev.apexnative.navigation.viewcontrollers.modal.SheetBack
36
37
  import dev.apexnative.navigation.viewcontrollers.overlay.OverlayCoordinator
37
38
  import dev.apexnative.navigation.viewcontrollers.overlay.OverlayRecord
38
39
  import dev.apexnative.navigation.viewcontrollers.overlay.OverlayViews
@@ -177,6 +178,9 @@ class Navigator(
177
178
  return if (modalRoot != null) modalRoot.stackPoppedByBack() else root?.stackPoppedByBack()
178
179
  }
179
180
 
181
+ /** The sheet [handleBack] would dismiss, as a drag down of the user would; none when back would do anything else. */
182
+ internal fun sheetDismissedByBack(): SheetBack? = modals.backTargetSheet()
183
+
180
184
  /**
181
185
  * [layer] may wait to show from now on ([WaitingLayer]): the nodes in it ask it through [isInWaitingLayer] and
182
186
  * [shownScreensChanged]. The root's layer is there from the start.
@@ -129,12 +129,9 @@ abstract class ParentController<V : ViewGroup>(
129
129
  open fun leavesTopEdgeFree(child: ViewController<*>): Boolean =
130
130
  parentController?.leavesTopEdgeFree(this) ?: false
131
131
 
132
- /**
133
- * Whether the safe area of [child] leaves its bottom edge free by default: the tabs show with
134
- * `bottomTabs.hideOnScroll` of [screen], the screen that asks.
135
- */
136
- open fun leavesBottomEdgeFree(child: ViewController<*>, screen: ViewController<*> = child): Boolean =
137
- parentController?.leavesBottomEdgeFree(this, screen) ?: false
132
+ /** Whether a large title shows over [child]: its stack shows the bar with one. No by default. */
133
+ open fun showsLargeTitleOver(child: ViewController<*>): Boolean =
134
+ parentController?.showsLargeTitleOver(this) ?: false
138
135
 
139
136
  /**
140
137
  * The tab bar at rest under [child]: where a stack's FAB keeps clear of it, by the options of [screen], the stack's
@@ -1,6 +1,7 @@
1
1
  package dev.apexnative.navigation.viewcontrollers.sidemenu
2
2
 
3
3
  import dev.apexnative.navigation.react.events.AppearanceDispatcher
4
+ import dev.apexnative.navigation.utils.SystemAnimations
4
5
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
5
6
 
6
7
  /**
@@ -26,6 +27,8 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
26
27
  internal class SideMenuMotion(
27
28
  private val drawer: () -> Drawer,
28
29
  private val host: Host,
30
+ /** The system's animations are off ([SystemAnimations]): a command or back moves the drawer at once. */
31
+ private val animationsOff: () -> Boolean = { SystemAnimations.areOff() },
29
32
  ) {
30
33
  /** A side of the side menu: physical, `left` stays on the left in RTL too. */
31
34
  enum class Side(
@@ -242,11 +245,14 @@ internal class SideMenuMotion(
242
245
  drawer.lock(side, locked = !isOnScreen(side) && !host.isEnabled(side))
243
246
  }
244
247
 
245
- // The drawer opens or closes [side]; one that doesn't call back has its result recorded here.
248
+ // The drawer opens or closes [side]; one that doesn't call back has its result recorded here. With the system's
249
+ // animations off it jumps, unless it moves: the drag and the settle aren't animators, and it can't jump while it
250
+ // settles.
246
251
  private fun move(side: Side, open: Boolean, animate: Boolean) {
247
252
  val drawer = drawer()
253
+ val animated = animate && (moving || !animationsOff())
248
254
  val silent = !drawer.callsBack
249
- if (open) drawer.open(side, animate) else drawer.close(side, animate)
255
+ if (open) drawer.open(side, animated) else drawer.close(side, animated)
250
256
  if (silent) record(side, open)
251
257
  }
252
258
 
@@ -2,6 +2,7 @@ package dev.apexnative.navigation.viewcontrollers.stack
2
2
 
3
3
  import android.animation.Animator
4
4
  import android.animation.AnimatorListenerAdapter
5
+ import android.animation.StateListAnimator
5
6
  import android.animation.ValueAnimator
6
7
  import android.content.res.Configuration
7
8
  import android.graphics.Bitmap
@@ -29,7 +30,7 @@ import dev.apexnative.navigation.views.stack.StackLayout
29
30
  * The preview of a pop while the user's back gesture runs (predictive back), as the system shows the activity under
30
31
  * the one that closes: the screen on top with its bar, a picture taken as the gesture starts, shrinks toward the
31
32
  * gesture's side with rounded corners and follows the finger up and down; the screen under it shows behind it with
32
- * its own bar, smaller and dimmed. Released, the pop plays its end ([commit]); cancelled, everything goes back
33
+ * its own bar and FAB, smaller and dimmed. Released, the pop plays its end ([commit]); cancelled, everything goes back
33
34
  * ([cancel]). The values are those of the system's cross-activity back animation (WMShell
34
35
  * `DefaultCrossActivityBackAnimation` of Android 15 and 16, the smallest scale as Android 17 shows it), also on Android
35
36
  * 14, whose own preview differs. Main thread only.
@@ -46,8 +47,13 @@ internal class BackPreview(
46
47
  * corners, while the tab bar stays, as Gmail shows it.
47
48
  */
48
49
  private val bottomRoom: Int,
49
- /** The picture is there: the stack shows the screen under the top, with its bar, before the preview draws. */
50
+ /**
51
+ * The picture is there: the stack shows the screen under the top, with its bar and its FAB, before the preview
52
+ * draws.
53
+ */
50
54
  private val onReveal: () -> Unit,
55
+ /** The view of the FAB the stack shows now, which goes with the screen under the top; none. */
56
+ private val fab: () -> View? = { null },
51
57
  ) {
52
58
  private val density = layout.resources.displayMetrics.density
53
59
  private val bar = layout.topBar
@@ -58,6 +64,11 @@ internal class BackPreview(
58
64
  private var animator: Animator? = null
59
65
  private var ended = false
60
66
  private var topAlpha = 1f
67
+ // The FAB that shows with the screen under the top, drawn flat under the dim and the picture, its own Z and the
68
+ // animator of its Z by its state.
69
+ private var flatFab: View? = null
70
+ private var fabZ = 0f
71
+ private var fabZByState: StateListAnimator? = null
61
72
  // What puts the stack as it was after a cancel: once, as the preview ends.
62
73
  private var onCancelled: (() -> Unit)? = null
63
74
  // Forward is to the end of the line: right to left, the screens move the other way, as the stack's pop does.
@@ -175,10 +186,17 @@ internal class BackPreview(
175
186
  card = null
176
187
  (scrim.parent as? ViewGroup)?.removeView(scrim)
177
188
  top.alpha = topAlpha
189
+ flatFab?.let {
190
+ flatFab = null
191
+ it.translationZ = fabZ
192
+ it.stateListAnimator = fabZByState
193
+ fabZByState = null
194
+ }
178
195
  entering(1f, 0f)
179
196
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
180
197
  target.resetPivot()
181
198
  bar.resetPivot()
199
+ fab()?.resetPivot()
182
200
  }
183
201
  onCancelled?.let {
184
202
  onCancelled = null
@@ -190,6 +208,16 @@ internal class BackPreview(
190
208
  // the top's place, cut to rounded corners.
191
209
  private fun show(picture: Bitmap) {
192
210
  onReveal()
211
+ // A child with a Z draws over those without, whatever their order: the FAB, raised by its elevation, would lie
212
+ // over the dim and the picture. Its state's animator, which Material's FAB sets on the frame after it joins the
213
+ // window (and on a press, focus or hover), would raise it again: it stays off while the preview draws.
214
+ fab()?.takeIf { it.z > 0f }?.let {
215
+ flatFab = it
216
+ fabZ = it.translationZ
217
+ fabZByState = it.stateListAnimator
218
+ it.stateListAnimator = null
219
+ it.translationZ = -it.elevation
220
+ }
193
221
  val dark = layout.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
194
222
  Configuration.UI_MODE_NIGHT_YES
195
223
  scrim.setBackgroundColor(Color.BLACK)
@@ -235,9 +263,9 @@ internal class BackPreview(
235
263
  entering(scale, -SystemTransition.DISTANCE_DP * density * forward)
236
264
  }
237
265
 
238
- // The screen under the top and its bar at [scale] around the stack's center, [x] along the line.
266
+ // The screen under the top, its bar and its FAB at [scale] around the stack's center, [x] along the line.
239
267
  private fun entering(scale: Float, x: Float) {
240
- for (view in listOf(target, bar)) {
268
+ for (view in listOfNotNull(target, bar, fab())) {
241
269
  view.pivotX = layout.width / 2f - view.left
242
270
  view.pivotY = areaHeight / 2f - view.top
243
271
  view.scaleX = scale
@@ -2,18 +2,18 @@ package dev.apexnative.navigation.viewcontrollers.stack
2
2
 
3
3
  import android.view.View
4
4
  import com.facebook.react.uimanager.PixelUtil.pxToDp
5
- import dev.apexnative.navigation.react.ScreenInsets
5
+ import dev.apexnative.navigation.react.ScreenEnvironment
6
6
 
7
7
  /**
8
8
  * Where a screen prepared for a push (`preparePush`) shows, in dp: the size of the container its slot fills and the
9
9
  * padding of its safe area view, as the push lays it out. JavaScript renders the screen with them before the push, and
10
10
  * the slot and the safe area view start their states with them.
11
11
  */
12
- internal data class PreparedFrame(val width: Float, val height: Float, val padding: ScreenInsets.Insets) {
12
+ internal data class PreparedFrame(val width: Float, val height: Float, val padding: ScreenEnvironment.Insets) {
13
13
  companion object {
14
14
  /** The size of [container] as its slot measures it (`SlotView`), so the slot finds it equal. */
15
15
  @JvmStatic
16
- fun of(container: View, padding: ScreenInsets.Insets) =
16
+ fun of(container: View, padding: ScreenEnvironment.Insets) =
17
17
  PreparedFrame(container.width.toFloat().pxToDp(), container.height.toFloat().pxToDp(), padding)
18
18
  }
19
19
  }
@@ -11,7 +11,7 @@ import dev.apexnative.navigation.options.DefaultOptions
11
11
  import dev.apexnative.navigation.react.CommandAnswer
12
12
  import dev.apexnative.navigation.react.CommandListener
13
13
  import dev.apexnative.navigation.react.ErrorTexts
14
- import dev.apexnative.navigation.react.ScreenInsets
14
+ import dev.apexnative.navigation.react.ScreenEnvironment
15
15
  import dev.apexnative.navigation.slots.SlotScreenView
16
16
  import dev.apexnative.navigation.utils.NavigationLog
17
17
  import dev.apexnative.navigation.viewcontrollers.child.ChildControllersRegistry
@@ -318,7 +318,7 @@ internal class StackCommands(
318
318
  layoutNow(stackView, screenView)
319
319
  node.updateSafeArea()
320
320
  val content = node.content?.asView()
321
- val padding = ScreenInsets.padding(node.id)
321
+ val padding = ScreenEnvironment.padding(node.id)
322
322
  val noInsets = content == null || ViewCompat.getRootWindowInsets(content) == null
323
323
  if (noInsets || padding == null || content.width <= 0 || content.height <= 0) {
324
324
  prepared.discard(node.id)
@@ -31,6 +31,7 @@ import dev.apexnative.navigation.viewcontrollers.parent.OptionsScreen
31
31
  import dev.apexnative.navigation.viewcontrollers.parent.ParentController
32
32
  import dev.apexnative.navigation.viewcontrollers.parent.ReturnsScreenFromColumn
33
33
  import dev.apexnative.navigation.viewcontrollers.parent.nodeWith
34
+ import dev.apexnative.navigation.viewcontrollers.stack.fab.FabMotion
34
35
  import dev.apexnative.navigation.viewcontrollers.stack.fab.FabPresenter
35
36
  import dev.apexnative.navigation.viewcontrollers.stack.topbar.LargeTitleCollapse
36
37
  import dev.apexnative.navigation.viewcontrollers.stack.topbar.TopBarPresenter
@@ -207,6 +208,17 @@ class StackController(
207
208
  screen.changeOptions { it.topBar.buttons.back.setVisible() }
208
209
  }
209
210
 
211
+ /**
212
+ * `clearOptions` took options out of the own options of [node], an entry of this stack or the options screen of
213
+ * one: the back arrow's rule ([join]) writes its part of them again, so the first screen keeps its arrow hidden
214
+ * and another shows it unless its options say otherwise.
215
+ */
216
+ internal fun ownOptionsCleared(node: ViewController<*>) {
217
+ val entries = queue.entries
218
+ val entry = entries.firstOrNull { it === node || optionsScreen(it) === node } ?: return
219
+ backRule(entry, first = entry === entries.first())
220
+ }
221
+
210
222
  /** The key of [entry]; none without one. */
211
223
  internal fun keyOf(entry: ViewController<*>): String? = keys[entry]
212
224
 
@@ -307,6 +319,9 @@ class StackController(
307
319
  // The back gesture over this stack whose preview hasn't ended yet; none.
308
320
  private var backGesture: BackGesture? = null
309
321
 
322
+ // A merge of `fab` into the top waits for the end of a back gesture's preview.
323
+ private var fabMergeWaits = false
324
+
310
325
  /**
311
326
  * A back gesture over the top of this stack ([startBackGesture]): its progress, its cancel, and its release, which
312
327
  * pops the top through the queue with the preview's end. A command accepted meanwhile, or the stack's destroy,
@@ -326,6 +341,10 @@ class StackController(
326
341
  private var revealed = false
327
342
  private var aborted = false
328
343
 
344
+ /** The preview shows the screen under the top, with its bar and its FAB. */
345
+ val isRevealed: Boolean
346
+ get() = revealed && !aborted
347
+
329
348
  /** The gesture moved: [progress] 0 … 1, the finger at [touchY]. */
330
349
  fun progress(progress: Float, touchY: Float) {
331
350
  if (!aborted) preview.progress(progress, touchY)
@@ -348,6 +367,9 @@ class StackController(
348
367
  return true
349
368
  }
350
369
  if (backGesture === this) backGesture = null
370
+ // The top's FAB, set aside, leaves with it; a merge into it no longer matters.
371
+ fab.dropAside()
372
+ fabMergeWaits = false
351
373
  queue.cut(target, null, id, "go back in the stack '$id'", null, gesture = true)
352
374
  return true
353
375
  }
@@ -360,7 +382,8 @@ class StackController(
360
382
  restore()
361
383
  }
362
384
 
363
- // The preview's picture is there: the screen under the top shows under it, and the bar shows that screen.
385
+ // The preview's picture is there: the screen under the top shows under it, and the bar and the FAB show that
386
+ // screen, at once.
364
387
  internal fun reveal() {
365
388
  val view = target.peekView() ?: return
366
389
  targetWasPlaced = view.parent === peekView() && view.visibility == View.VISIBLE
@@ -370,6 +393,8 @@ class StackController(
370
393
  views.reveal(target, top)
371
394
  view.alpha = 1f
372
395
  topBar.apply(target)
396
+ fab.setAside()
397
+ showFab(FabMotion.Move.AtOnce, target)
373
398
  }
374
399
 
375
400
  private fun restore() {
@@ -386,9 +411,14 @@ class StackController(
386
411
  views.detach(target)
387
412
  }
388
413
  }
389
- // The bar shows the top again, its components appeared again.
414
+ // The bar and the FAB show the top again, the bar's components appeared again.
390
415
  val shown = queue.currentChild
391
416
  topBar.apply(shown)
417
+ fab.bringBack()
418
+ if (fabMergeWaits) {
419
+ fabMergeWaits = false
420
+ mergeFab()
421
+ }
392
422
  shown?.let {
393
423
  topBar.screenWillAppear(optionsScreen(it))
394
424
  topBar.screenDidAppear(optionsScreen(it))
@@ -462,7 +492,8 @@ class StackController(
462
492
  ContainerBackground.apply(this, options)
463
493
  updateBar()
464
494
  takeKeptMerge(child)?.let(::applyTopMerge)
465
- if (queue.isAtRest) showFab()
495
+ // A back gesture's preview shows the FAB of the screen under the top.
496
+ if (queue.isAtRest && backGesture?.isRevealed != true) showFab()
466
497
  performOnParentController { it.applyChildOptions(withoutStackGroups(options), this) }
467
498
  }
468
499
 
@@ -594,6 +625,10 @@ class StackController(
594
625
  override fun leavesTopEdgeFree(child: ViewController<*>): Boolean =
595
626
  TopBarRoom.leavesTopEdgeFree(resolvedOf(child), largeTitleShown(child)) || super.leavesTopEdgeFree(child)
596
627
 
628
+ /** A large title shows over [child] in this stack's bar. */
629
+ override fun showsLargeTitleOver(child: ViewController<*>): Boolean =
630
+ largeTitleShown(child) || super.showsLargeTitleOver(child)
631
+
597
632
  /** A screen under another doesn't compute its safe area again; the prepared and waiting do. */
598
633
  override fun isTopOfStack(child: ViewController<*>): Boolean =
599
634
  child === queue.currentChild || child === queue.waitingScreen || isPrepared(child)
@@ -666,15 +701,20 @@ class StackController(
666
701
  topBar.apply(queue.currentChild)
667
702
  }
668
703
 
669
- // The FAB follows the top at the end of a transition.
670
- private fun showFab() {
704
+ // The FAB follows [top] (the current child) from the start of a transition by [move], with the screens; at rest, a
705
+ // FAB that comes by Material's swap.
706
+ private fun showFab(move: FabMotion.Move = FabMotion.Move.Swap, top: ViewController<*>? = queue.currentChild) {
671
707
  val layout = peekView() ?: return
672
- val top = queue.currentChild
673
708
  val screen = top?.let(::optionsScreen)
674
- fab.showFor(layout, screen, top?.let(::resolvedOf)?.fab ?: FabOptions(), scrollOf(screen))
709
+ fab.showFor(layout, screen, top?.let(::resolvedOf)?.fab ?: FabOptions(), scrollOf(screen), move)
675
710
  }
676
711
 
677
712
  private fun mergeFab() {
713
+ // A back gesture's preview shows the FAB of the screen under the top: the top's merge waits for a cancel.
714
+ if (backGesture?.isRevealed == true) {
715
+ fabMergeWaits = true
716
+ return
717
+ }
678
718
  val layout = peekView() ?: return
679
719
  val top = queue.currentChild ?: return
680
720
  val screen = optionsScreen(top)
@@ -723,6 +763,9 @@ class StackController(
723
763
  parentController?.tabBarTransition(this@StackController, move, side)
724
764
  }
725
765
 
766
+ // The FAB of the new top arrives with its screen, the old one leaves with its own.
767
+ override fun fabTransition(move: FabMotion.Move) = showFab(move)
768
+
726
769
  override fun operationEnded(topSettled: Boolean) {
727
770
  BackCallbackState.update()
728
771
  if (topSettled) parentController?.tabBarSettled(this@StackController)
@@ -21,6 +21,7 @@ import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.Custom
21
21
  import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.Elements
22
22
  import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.Fade
23
23
  import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.Motion
24
+ import dev.apexnative.navigation.viewcontrollers.stack.fab.FabMotion
24
25
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
25
26
  import dev.apexnative.navigation.views.animations.AnimationRules
26
27
  import java.util.WeakHashMap
@@ -58,7 +59,7 @@ private typealias Node = ViewController<*>
58
59
  * made in the commit of that mount (its `mergeOptions`), so the transition starts with the options of that commit —
59
60
  * content mounted already goes that way too. A push or a `setStackRoot` with elements waits for its targets after
60
61
  * that, a wait like the others, within what is left of its limit without `waitForRender`: a cut or a replace during it
61
- * interrupts the push, which moves no view into a layer.
62
+ * interrupts the push, which starts no element.
62
63
  *
63
64
  * The transition is the screens' own when the options say so (`AnimationRules.stackIsOwn`): the display plays each
64
65
  * screen's side ([StackDisplay.animateCustom]) with the same owners as the default transitions; the bar's row then
@@ -103,6 +104,12 @@ internal class StackQueue(
103
104
  */
104
105
  fun tabBarTransition(move: TabBarMotion.Move, side: BottomTabsEnterExitAnimationOptions?) = Unit
105
106
 
107
+ /**
108
+ * The current child is about to change, right after [barTransition]: [move] is how the FAB goes, the new top's
109
+ * arriving with its screen and the old one's leaving with its own from the start of the transition.
110
+ */
111
+ fun fabTransition(move: FabMotion.Move) = Unit
112
+
106
113
  /** [node] was accepted to show as the top (a push, the new top of a replace). */
107
114
  fun willShow(node: Node) = Unit
108
115
 
@@ -546,6 +553,7 @@ internal class StackQueue(
546
553
  val ended = takeOver(listOfNotNull(covered, node), op)
547
554
  host.tabBarTransition(TabBarMotion.Move.AtOnce, null)
548
555
  host.barTransition(null, BarVisibility.AT_ONCE)
556
+ host.fabTransition(FabMotion.Move.AtOnce)
549
557
  host.currentChildChanged()
550
558
  finishTakenOver(ended)
551
559
  if (display.hasView && covered != null) display.detach(covered)
@@ -785,6 +793,7 @@ internal class StackQueue(
785
793
  val ended = takeOver(old + top, op)
786
794
  host.tabBarTransition(TabBarMotion.Move.AtOnce, null)
787
795
  host.barTransition(null, BarVisibility.AT_ONCE)
796
+ host.fabTransition(FabMotion.Move.AtOnce)
788
797
  host.currentChildChanged()
789
798
  finishTakenOver(ended)
790
799
  op.done = true
@@ -1124,6 +1133,7 @@ internal class StackQueue(
1124
1133
  reject(op, text)
1125
1134
  host.tabBarTransition(TabBarMotion.Move.AtOnce, null)
1126
1135
  host.barTransition(null, BarVisibility.AT_ONCE)
1136
+ host.fabTransition(FabMotion.Move.AtOnce)
1127
1137
  host.currentChildChanged()
1128
1138
  settled()
1129
1139
  drain()
@@ -1174,7 +1184,7 @@ internal class StackQueue(
1174
1184
  // The bars with a change of the current child. The row goes with the default transition; with an animation of its
1175
1185
  // own the top bar goes with the screens ([travel]). The tab bar rides with the system's [system] move, slides with an
1176
1186
  // animation of its own or a side of its own, and is where the new top wants it at once without animation; with
1177
- // elements every bar changes at once.
1187
+ // elements every bar changes at once. The FAB goes with its screen, by Material's swap with an animation of its own.
1178
1188
  private fun tellBars(
1179
1189
  animated: Boolean,
1180
1190
  own: Boolean,
@@ -1183,7 +1193,8 @@ internal class StackQueue(
1183
1193
  system: TabBarMotion.Move,
1184
1194
  row: BarMotion,
1185
1195
  travel: BarTravel,
1186
- // The top bar shows the new top already (a back gesture's preview): it changes at once, the tab bar still moves.
1196
+ // The top bar and the FAB show the new top already (a back gesture's preview): they change at once, the tab bar
1197
+ // still moves.
1187
1198
  barAtOnce: Boolean = false,
1188
1199
  ) {
1189
1200
  val tabs = animation.bottomTabs
@@ -1200,6 +1211,7 @@ internal class StackQueue(
1200
1211
  barVisibility(barMoves && !elements, animation.topBar),
1201
1212
  travel.takeIf { barMoves && own && !elements },
1202
1213
  )
1214
+ host.fabTransition(FabMotion.moveOf(animated, own, row, shown = barAtOnce))
1203
1215
  }
1204
1216
 
1205
1217
  private fun barVisibility(animated: Boolean, side: TopBarEnterExitAnimationOptions): BarVisibility =