@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
@@ -15,6 +15,7 @@ import dev.apexnative.navigation.viewcontrollers.component.ComponentViewControll
15
15
  import dev.apexnative.navigation.viewcontrollers.externalcomponent.ExternalComponentViewController
16
16
  import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.Motion
17
17
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
18
+ import dev.apexnative.navigation.views.animations.AnimationLayer
18
19
  import dev.apexnative.navigation.views.animations.AnimationRules
19
20
  import dev.apexnative.navigation.views.animations.SideAnimator
20
21
  import dev.apexnative.navigation.views.animations.SystemTransition
@@ -149,14 +150,17 @@ internal class StackViews(private val stack: StackController) : StackDisplay {
149
150
  }
150
151
  }
151
152
 
152
- // The elements go into a layer over the screens, and back as the screen's animator ends or stops. A shared element
153
- // without a duration takes the screen's fade: its own single `alpha`, else 300 ms.
153
+ // The elements play in their screens and the pairs' copies fly in a layer over them, all done as the screen's
154
+ // animator ends or stops. A shared element without a duration takes the screen's fade: its own single `alpha`,
155
+ // else 300 ms.
154
156
  override fun animateCustom(screen: ViewController<*>, custom: StackDisplay.Custom, done: () -> Unit) {
155
157
  stop(screen)
156
158
  val view = screen.peekView() ?: return done()
157
159
  custom.alpha?.let { view.alpha = it }
158
160
  val own = custom.side?.let { SideAnimator.build(view, it, SideAnimator.Base(alpha = custom.alpha)) }
159
161
  val fade = if (own == null) StackTransitions.fade(view, custom.fade) else null
162
+ // A fade of the whole screen draws it through a hardware layer, as the default transition does ([dress]).
163
+ for (animator in listOfNotNull(own, fade)) AnimationLayer.during(view, animator)
160
164
  val elements = custom.elements?.let { elements ->
161
165
  val layout = layout ?: return@let null
162
166
  val fadeLength = custom.side?.takeIf(AnimationRules::isSingleAlpha)?.let(AnimationRules::lengthOf)
@@ -196,7 +200,7 @@ internal class StackViews(private val stack: StackController) : StackDisplay {
196
200
 
197
201
  /**
198
202
  * Starts the preview of a back gesture over [top], [target] under it (`BackPreview`): none without the stack's view
199
- * or a window. [onReveal] shows [target] and its bar as the preview's picture is there.
203
+ * or a window. [onReveal] shows [target] with its bar and its FAB as the preview's picture is there.
200
204
  */
201
205
  fun startBackPreview(
202
206
  top: ViewController<*>,
@@ -211,7 +215,9 @@ internal class StackViews(private val stack: StackController) : StackDisplay {
211
215
  val window = stack.activity.window ?: return null
212
216
  val topView = top.peekView() ?: return null
213
217
  val targetView = target.peekView() ?: return null
214
- val started = BackPreview(layout, topView, targetView, fromLeft, touchY, bottomRoom, onReveal)
218
+ val started = BackPreview(layout, topView, targetView, fromLeft, touchY, bottomRoom, onReveal) {
219
+ stack.fab.shownView
220
+ }
215
221
  preview = started
216
222
  previewScreens = top to target
217
223
  started.start(window)
@@ -0,0 +1,142 @@
1
+ package dev.apexnative.navigation.viewcontrollers.stack.fab
2
+
3
+ import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.BarMotion
4
+ import dev.apexnative.navigation.views.animations.FadeThrough
5
+ import dev.apexnative.navigation.views.animations.SystemTransition
6
+
7
+ /**
8
+ * How a stack's FAB goes with a change of its screen on top, as the values alone: the new top's FAB arrives with its
9
+ * screen and a copy of the old one leaves with the screen it belonged to, from the start of the transition. A FAB with
10
+ * the same id stays where it is. The stack plays them on its views (`FabPresenter`).
11
+ */
12
+ internal object FabMotion {
13
+ /** How the stack's transition goes, as its queue tells it (`StackQueue.Host.fabTransition`). */
14
+ enum class Move {
15
+ /** The system's push: each FAB rides with its screen along the line, as the tab bar does. */
16
+ WithPush,
17
+
18
+ /** The system's pop: the same, the other way. */
19
+ WithPop,
20
+
21
+ /** The fade through of a `setStackRoot`: the old one fades out first, the new one fades in and grows. */
22
+ FadeThrough,
23
+
24
+ /**
25
+ * A transition of the screens' own (`content`, elements): Material's swap at its start, the old one shrinking
26
+ * away, the new one growing from 60 %.
27
+ */
28
+ Swap,
29
+
30
+ /** No animation: the new top's FAB is there at once. */
31
+ AtOnce,
32
+ }
33
+
34
+ /** The curve a ride's move and scale take; its alpha changes on a straight line. */
35
+ enum class Curve { System, FadeThrough, Material }
36
+
37
+ /**
38
+ * A FAB's ride: along the x axis in dp, mirrored right to left; its scale on [curve] over [duration]; its alpha after
39
+ * a hold of [alphaDelay] over [alphaDuration], in ms.
40
+ */
41
+ data class Ride(
42
+ val fromX: Float,
43
+ val toX: Float,
44
+ val fromAlpha: Float,
45
+ val toAlpha: Float,
46
+ val fromScale: Float,
47
+ val toScale: Float,
48
+ val alphaDelay: Long,
49
+ val alphaDuration: Long,
50
+ val duration: Long,
51
+ val curve: Curve,
52
+ )
53
+
54
+ /** How long Material's swap takes, as the FAB shrinks or grows. */
55
+ const val SWAP_MILLIS = 200L
56
+
57
+ /** Where Material's swap grows the new FAB from. */
58
+ const val GROW_FROM = 0.6f
59
+
60
+ private const val FADE_THROUGH_SCALE = 0.92f
61
+
62
+ /**
63
+ * The move of the FAB with a change of the current child: none when the change comes at once or the new top shows
64
+ * already ([shown]: a back gesture's preview showed it); Material's swap with an animation of the screens' own
65
+ * ([own]); else the default transition's [row], as the bar's row goes.
66
+ */
67
+ fun moveOf(animated: Boolean, own: Boolean, row: BarMotion, shown: Boolean): Move = when {
68
+ !animated || shown -> Move.AtOnce
69
+ own -> Move.Swap
70
+ row == BarMotion.Push -> Move.WithPush
71
+ row == BarMotion.Pop -> Move.WithPop
72
+ else -> Move.FadeThrough
73
+ }
74
+
75
+ /**
76
+ * The new top's FAB comes with its screen: a push brings it from +96 dp (−96 dp on a pop), transparent for the hold
77
+ * of the screen's fade, then fading in; the fade through fades it in after the old one went and grows it from 92 %;
78
+ * the swap grows it from 60 %. None at once.
79
+ */
80
+ fun arrival(move: Move, rtl: Boolean): Ride? {
81
+ val distance = SystemTransition.DISTANCE_DP * if (rtl) -1 else 1
82
+ return when (move) {
83
+ Move.WithPush, Move.WithPop -> Ride(
84
+ fromX = if (move == Move.WithPush) distance else -distance,
85
+ toX = 0f,
86
+ fromAlpha = 0f,
87
+ toAlpha = 1f,
88
+ fromScale = 1f,
89
+ toScale = 1f,
90
+ alphaDelay = fadeDelay(move),
91
+ alphaDuration = SystemTransition.FADE_DURATION,
92
+ duration = SystemTransition.DURATION,
93
+ curve = Curve.System,
94
+ )
95
+ Move.FadeThrough -> Ride(
96
+ fromX = 0f,
97
+ toX = 0f,
98
+ fromAlpha = 0f,
99
+ toAlpha = 1f,
100
+ fromScale = FADE_THROUGH_SCALE,
101
+ toScale = 1f,
102
+ alphaDelay = fadeThroughOut(),
103
+ alphaDuration = FadeThrough.DURATION - fadeThroughOut(),
104
+ duration = FadeThrough.DURATION,
105
+ curve = Curve.FadeThrough,
106
+ )
107
+ Move.Swap -> Ride(0f, 0f, 1f, 1f, GROW_FROM, 1f, 0L, 0L, SWAP_MILLIS, Curve.Material)
108
+ Move.AtOnce -> null
109
+ }
110
+ }
111
+
112
+ /**
113
+ * The old top's FAB leaves with its screen, from where it is: a push takes it to −96 dp with the covered screen and
114
+ * fades it out as the new screen fades in over it; a pop takes it to +96 dp, fading with the leaving screen; the
115
+ * fade through fades it out in the first 35 %; the swap shrinks it to nothing. None at once.
116
+ */
117
+ fun departure(move: Move, rtl: Boolean): Ride? {
118
+ val distance = SystemTransition.DISTANCE_DP * if (rtl) -1 else 1
119
+ return when (move) {
120
+ Move.WithPush, Move.WithPop -> Ride(
121
+ fromX = 0f,
122
+ toX = if (move == Move.WithPush) -distance else distance,
123
+ fromAlpha = 1f,
124
+ toAlpha = 0f,
125
+ fromScale = 1f,
126
+ toScale = 1f,
127
+ alphaDelay = fadeDelay(move),
128
+ alphaDuration = SystemTransition.FADE_DURATION,
129
+ duration = SystemTransition.DURATION,
130
+ curve = Curve.System,
131
+ )
132
+ Move.FadeThrough -> Ride(0f, 0f, 1f, 0f, 1f, 1f, 0L, fadeThroughOut(), fadeThroughOut(), Curve.FadeThrough)
133
+ Move.Swap -> Ride(0f, 0f, 1f, 1f, 1f, 0f, 0L, 0L, SWAP_MILLIS, Curve.Material)
134
+ Move.AtOnce -> null
135
+ }
136
+ }
137
+
138
+ private fun fadeDelay(move: Move): Long =
139
+ if (move == Move.WithPush) SystemTransition.PUSH_FADE_DELAY else SystemTransition.POP_FADE_DELAY
140
+
141
+ private fun fadeThroughOut(): Long = (FadeThrough.DURATION * FadeThrough.OUT).toLong()
142
+ }
@@ -1,9 +1,16 @@
1
1
  package dev.apexnative.navigation.viewcontrollers.stack.fab
2
2
 
3
+ import android.animation.Animator
4
+ import android.animation.AnimatorListenerAdapter
5
+ import android.animation.AnimatorSet
6
+ import android.animation.ObjectAnimator
7
+ import android.animation.PropertyValuesHolder
3
8
  import android.app.Activity
4
9
  import android.view.Gravity
5
10
  import android.view.View
6
11
  import android.view.ViewGroup
12
+ import android.view.animation.AccelerateDecelerateInterpolator
13
+ import android.view.animation.LinearInterpolator
7
14
  import androidx.coordinatorlayout.widget.CoordinatorLayout
8
15
  import androidx.core.view.WindowInsetsCompat
9
16
  import dev.apexnative.navigation.options.FabOptions
@@ -11,16 +18,20 @@ import dev.apexnative.navigation.react.events.AppearanceDispatcher
11
18
  import dev.apexnative.navigation.utils.TestIds
12
19
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.ScrollEventListener
13
20
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
21
+ import dev.apexnative.navigation.views.animations.FadeThrough
22
+ import dev.apexnative.navigation.views.animations.SystemTransition
14
23
  import dev.apexnative.navigation.views.stack.StackLayout
15
24
  import dev.apexnative.navigation.views.stack.fab.Fab
25
+ import dev.apexnative.navigation.views.stack.fab.FabCopy
16
26
  import dev.apexnative.navigation.views.stack.fab.FabMenu
17
27
 
18
28
  /**
19
29
  * The FAB of one stack (`fab.*`), over the module's `Fab` and `FabMenu`: one FAB or one menu at a time, from the
20
30
  * resolved `fab` of the screen on top (its options over the stack's, its parents' and the defaults). It follows the top
21
- * at the end of each transition and the merges of `fab` into it; a press sends `ANN.NavigationButtonPressed` with the
22
- * screen whose options showed it, an action with its own id. Its views are children of the stack's view, over the
23
- * screens and the bar. Main thread only.
31
+ * from the start of each transition, going with the screens by [FabMotion], and the merges of `fab` into it; a press
32
+ * sends `ANN.NavigationButtonPressed` with the screen whose options showed it, an action with its own id. Its views are
33
+ * children of the stack's view, over the screens and the bar; a FAB that leaves goes as a copy ([FabCopy]). Main
34
+ * thread only.
24
35
  */
25
36
  internal class FabPresenter(
26
37
  private val activity: Activity,
@@ -38,38 +49,75 @@ internal class FabPresenter(
38
49
  var source: ViewController<*>? = null
39
50
  private set
40
51
 
52
+ // The ride of the FAB that shows as it arrives; none at rest.
53
+ private var ride: Animator? = null
54
+ // The copies of the FABs that leave, each with its ride.
55
+ private val copies = LinkedHashMap<FabCopy, Animator>()
56
+
57
+ // The top's FAB while a back gesture's preview shows the screen under the top with its own: out of the stack, as
58
+ // it was (hidden by the scroll, say).
59
+ private class Aside(
60
+ val fab: Fab?,
61
+ val menu: FabMenu?,
62
+ val shownId: String?,
63
+ var source: ViewController<*>?,
64
+ val alignStart: Boolean,
65
+ )
66
+
67
+ private var aside: Aside? = null
68
+
41
69
  private val view: View?
42
70
  get() = menu ?: fab
43
71
 
72
+ /** The view of the FAB or of its menu that shows; none. */
73
+ val shownView: View?
74
+ get() = view
75
+
44
76
  /**
45
- * The screen on top became [source], with its resolved [options]: the same id updates the FAB that shows and grows
46
- * it again, another id replaces it, none takes it away; the scroll of [scroll] hides it with `hideOnScroll`.
47
- * Nothing when [source] already shows it.
77
+ * The screen on top became [source], with its resolved [options], in a transition that goes by [move]: the same id
78
+ * updates the FAB that shows, where it stays as it is, another id replaces it, none takes it away — the new FAB
79
+ * arrives and a copy of the old one leaves by [move]; the scroll of [scroll] hides it with `hideOnScroll`. An open
80
+ * menu closes. The copies of an older transition go, and with [FabMotion.Move.AtOnce] a FAB still riding comes to
81
+ * rest. Nothing when [source] already shows it.
48
82
  */
49
- fun showFor(layout: StackLayout, source: ViewController<*>?, options: FabOptions, scroll: ScrollEventListener?) {
83
+ fun showFor(
84
+ layout: StackLayout,
85
+ source: ViewController<*>?,
86
+ options: FabOptions,
87
+ scroll: ScrollEventListener?,
88
+ move: FabMotion.Move = FabMotion.Move.Swap,
89
+ ) {
90
+ // At once, what still moves comes to rest, also when the top stays (a failed push).
91
+ if (move == FabMotion.Move.AtOnce) {
92
+ dropCopies()
93
+ stopRide(rest = true)
94
+ }
50
95
  if (source === this.source && this.layout === layout) return
51
96
  this.layout = layout
52
- disableCollapse()
97
+ dropCopies()
53
98
  this.source = source
54
99
  when (FabPlan.step(shownId, menu != null, options)) {
55
100
  FabPlan.Step.None -> Unit
56
- FabPlan.Step.Remove -> remove()
57
- FabPlan.Step.Create -> create(layout, options, scroll)
101
+ FabPlan.Step.Remove -> leave(layout, move)
102
+ FabPlan.Step.Create -> {
103
+ create(layout, options, scroll)
104
+ arrive(move)
105
+ }
58
106
  FabPlan.Step.Replace -> {
59
- remove()
107
+ leave(layout, move)
60
108
  create(layout, options, scroll)
109
+ arrive(move)
61
110
  }
62
111
  FabPlan.Step.Update -> {
112
+ disableCollapse()
113
+ menu?.close()
63
114
  apply(options, scroll)
64
115
  // A new top whose options don't hide it shows it, even where the screen before hid it.
65
116
  if (!options.visible.isFalse) {
66
117
  fab?.show()
67
118
  menu?.show()
68
119
  }
69
- view?.let {
70
- it.bringToFront()
71
- grow(it)
72
- }
120
+ view?.bringToFront()
73
121
  }
74
122
  }
75
123
  }
@@ -83,11 +131,15 @@ internal class FabPresenter(
83
131
  this.source = source
84
132
  when (FabPlan.step(shownId, menu != null, options)) {
85
133
  FabPlan.Step.None -> Unit
86
- FabPlan.Step.Remove -> remove()
87
- FabPlan.Step.Create -> create(layout, options, scroll)
134
+ FabPlan.Step.Remove -> leave(layout, FabMotion.Move.Swap)
135
+ FabPlan.Step.Create -> {
136
+ create(layout, options, scroll)
137
+ arrive(FabMotion.Move.Swap)
138
+ }
88
139
  FabPlan.Step.Replace -> {
89
- remove()
140
+ leave(layout, FabMotion.Move.Swap)
90
141
  create(layout, options, scroll)
142
+ arrive(FabMotion.Move.Swap)
91
143
  }
92
144
  FabPlan.Step.Update -> apply(options, scroll)
93
145
  }
@@ -130,13 +182,57 @@ internal class FabPresenter(
130
182
  view.layoutParams = params
131
183
  }
132
184
 
185
+ /**
186
+ * A back gesture's preview shows the screen under the top: the FAB that shows goes aside, out of the stack but as it
187
+ * is, its menu closing, until [bringBack] or [dropAside]; the screen's own FAB shows next ([showFor]).
188
+ */
189
+ fun setAside() {
190
+ if (aside != null) return
191
+ dropCopies()
192
+ stopRide(rest = true)
193
+ menu?.close()
194
+ view?.let { (it.parent as? ViewGroup)?.removeView(it) }
195
+ aside = Aside(fab, menu, shownId, source, alignStart)
196
+ fab = null
197
+ menu = null
198
+ shownId = null
199
+ source = null
200
+ }
201
+
202
+ /** The gesture was cancelled: the FAB the preview showed goes, and the one set aside is back as it was. */
203
+ fun bringBack() {
204
+ val kept = aside ?: return
205
+ aside = null
206
+ val layout = layout ?: return discard(kept)
207
+ leave(layout, FabMotion.Move.AtOnce)
208
+ fab = kept.fab
209
+ menu = kept.menu
210
+ shownId = kept.shownId
211
+ source = kept.source
212
+ alignStart = kept.alignStart
213
+ view?.let { layout.addOverAll(it, it.layoutParams as? CoordinatorLayout.LayoutParams ?: params()) }
214
+ place()
215
+ }
216
+
217
+ /** The gesture was released: the FAB set aside goes with the top that leaves. */
218
+ fun dropAside() {
219
+ val kept = aside ?: return
220
+ aside = null
221
+ discard(kept)
222
+ }
223
+
133
224
  /** [node] was destroyed: a FAB it showed sends nothing more. */
134
225
  fun forget(node: ViewController<*>) {
135
226
  if (source === node) source = null
227
+ aside?.let { if (it.source === node) it.source = null }
136
228
  }
137
229
 
138
- /** The stack goes: the FAB and its menu go at once, the scrim first. */
230
+ /** The stack goes: the FAB and its menu go at once, the scrim first, and the copies that leave. */
139
231
  fun destroy() {
232
+ aside?.let(::discard)
233
+ aside = null
234
+ dropCopies()
235
+ stopRide(rest = false)
140
236
  disableCollapse()
141
237
  menu?.let {
142
238
  it.removeScrim()
@@ -153,7 +249,7 @@ internal class FabPresenter(
153
249
  private fun create(layout: StackLayout, options: FabOptions, scroll: ScrollEventListener?) {
154
250
  val id = FabPlan.idOf(options) ?: return
155
251
  shownId = id
156
- val params = CoordinatorLayout.LayoutParams(WRAP, WRAP)
252
+ val params = params()
157
253
  if (FabPlan.isMenu(options)) {
158
254
  val menu = FabMenu(activity, id)
159
255
  this.menu = menu
@@ -164,7 +260,6 @@ internal class FabPresenter(
164
260
  layout.addOverAll(fab, params)
165
261
  }
166
262
  apply(options, scroll)
167
- view?.let(::grow)
168
263
  }
169
264
 
170
265
  // Every option of the FAB that shows; the collapse before the visibility, which `disableCollapse` would undo.
@@ -235,43 +330,120 @@ internal class FabPresenter(
235
330
  menu?.disableCollapse()
236
331
  }
237
332
 
238
- // The FAB that shows goes: a button shrinks to 0 in 200 ms; a menu goes at once, its scrim first.
239
- private fun remove() {
333
+ // The FAB that shows goes with its screen: a copy of it leaves by [move] while the button, or the menu, goes from the
334
+ // stack at once; an open menu closes, its scrim fading out by itself. At once nothing leaves, the scrim neither.
335
+ private fun leave(layout: StackLayout, move: FabMotion.Move) {
336
+ val view = view ?: return
337
+ val departure = FabMotion.departure(move, isRtl(layout))
338
+ val copy = departure?.let { FabCopy.of((view as? FabMenu)?.main ?: view, view, layout) }
339
+ stopRide(rest = false)
340
+ disableCollapse()
240
341
  menu?.let {
241
- it.removeScrim()
342
+ if (departure != null) it.closeToLeave() else it.removeScrim()
242
343
  (it.parent as? ViewGroup)?.removeView(it)
243
344
  }
244
- fab?.let { fab ->
245
- fab.animate().cancel()
246
- if (!fab.isAttachedToWindow) {
247
- (fab.parent as? ViewGroup)?.removeView(fab)
248
- } else {
249
- fab.animate().scaleX(0f).scaleY(0f).setDuration(ANIMATION_MILLIS)
250
- .withEndAction { (fab.parent as? ViewGroup)?.removeView(fab) }.start()
251
- }
252
- }
345
+ fab?.let { (it.parent as? ViewGroup)?.removeView(it) }
253
346
  menu = null
254
347
  fab = null
255
348
  shownId = null
349
+ if (copy == null) return
350
+ copies[copy] = play(copy, departure, fromCurrent = true) { copies.remove(copy)?.let { removeCopy(copy) } }
351
+ }
352
+
353
+ // The new FAB arrives with its screen by [move]; one its options hide (`visible: false`) has nothing to show.
354
+ private fun arrive(move: FabMotion.Move) {
355
+ val view = view ?: return
356
+ if (((view as? FabMenu)?.main ?: view).visibility != View.VISIBLE) return
357
+ val arrival = FabMotion.arrival(move, layout?.let(::isRtl) ?: false) ?: return
358
+ ride = play(view, arrival, fromCurrent = false) { animator -> if (ride === animator) ride = null }
359
+ }
360
+
361
+ // A ride stops where it is; [rest]: the FAB is in its place again, opaque and whole.
362
+ private fun stopRide(rest: Boolean) {
363
+ val running = ride ?: return
364
+ ride = null
365
+ running.cancel()
366
+ if (!rest) return
367
+ view?.apply {
368
+ translationX = 0f
369
+ alpha = 1f
370
+ scaleX = 1f
371
+ scaleY = 1f
372
+ }
373
+ }
374
+
375
+ // The copies of an older transition go at once.
376
+ private fun dropCopies() {
377
+ if (copies.isEmpty()) return
378
+ val dropped = copies.toList()
379
+ copies.clear()
380
+ dropped.forEach { (copy, animator) ->
381
+ animator.cancel()
382
+ removeCopy(copy)
383
+ }
256
384
  }
257
385
 
258
- // It comes to the top and grows from 60 % in 200 ms.
259
- private fun grow(view: View) {
260
- val target = (view as? FabMenu)?.main ?: view
261
- target.animate().cancel()
262
- target.scaleX = GROW_FROM
263
- target.scaleY = GROW_FROM
264
- target.animate().scaleX(1f).scaleY(1f).setDuration(ANIMATION_MILLIS).start()
386
+ private fun removeCopy(copy: FabCopy) {
387
+ (copy.parent as? ViewGroup)?.removeView(copy)
265
388
  }
266
389
 
390
+ // Plays [ride] on [view], from its start values or [fromCurrent] from where the view is; [end] at its end or stop.
391
+ private fun play(view: View, ride: FabMotion.Ride, fromCurrent: Boolean, end: (Animator) -> Unit): Animator {
392
+ val density = activity.resources.displayMetrics.density
393
+ if (!fromCurrent) {
394
+ view.translationX = ride.fromX * density
395
+ view.alpha = ride.fromAlpha
396
+ view.scaleX = ride.fromScale
397
+ view.scaleY = ride.fromScale
398
+ }
399
+ val move = ObjectAnimator.ofPropertyValuesHolder(
400
+ view,
401
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_X, ride.toX * density),
402
+ PropertyValuesHolder.ofFloat(View.SCALE_X, ride.toScale),
403
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, ride.toScale),
404
+ ).apply {
405
+ duration = ride.duration
406
+ interpolator = when (ride.curve) {
407
+ FabMotion.Curve.System -> SystemTransition.CURVE
408
+ FabMotion.Curve.FadeThrough -> FadeThrough.CURVE
409
+ FabMotion.Curve.Material -> AccelerateDecelerateInterpolator()
410
+ }
411
+ }
412
+ val animator = AnimatorSet()
413
+ if (ride.alphaDuration > 0) {
414
+ val fade = ObjectAnimator.ofFloat(view, View.ALPHA, ride.toAlpha).apply {
415
+ startDelay = ride.alphaDelay
416
+ duration = ride.alphaDuration
417
+ interpolator = LinearInterpolator()
418
+ }
419
+ animator.playTogether(move, fade)
420
+ } else {
421
+ animator.play(move)
422
+ }
423
+ animator.addListener(object : AnimatorListenerAdapter() {
424
+ override fun onAnimationEnd(animation: Animator) = end(animation)
425
+ })
426
+ animator.start()
427
+ return animator
428
+ }
429
+
430
+ // A FAB set aside that won't come back: it follows no scroll, its scrim goes.
431
+ private fun discard(kept: Aside) {
432
+ kept.fab?.disableCollapse()
433
+ kept.menu?.disableCollapse()
434
+ kept.menu?.removeScrim()
435
+ }
436
+
437
+ private fun params() = CoordinatorLayout.LayoutParams(WRAP, WRAP)
438
+
439
+ private fun isRtl(layout: StackLayout): Boolean = layout.layoutDirection == View.LAYOUT_DIRECTION_RTL
440
+
267
441
  companion object {
268
442
  private const val WRAP = ViewGroup.LayoutParams.WRAP_CONTENT
269
- private const val ANIMATION_MILLIS = 200L
270
- private const val GROW_FROM = 0.6f
271
443
 
272
444
  /**
273
445
  * A press on the FAB, or on an action of its menu, of [source] (EVT-NavigationButtonPressed): nothing once
274
- * [source] is destroyed — its FAB still fades for 200 ms.
446
+ * [source] is destroyed.
275
447
  */
276
448
  fun press(source: ViewController<*>?, buttonId: String) {
277
449
  if (source == null || source.isDestroyed) return
@@ -3,8 +3,6 @@ package dev.apexnative.navigation.viewcontrollers.stack.search
3
3
  import android.app.Activity
4
4
  import android.content.Context
5
5
  import android.graphics.drawable.Drawable
6
- import android.view.Menu
7
- import android.view.MenuItem
8
6
  import android.view.View
9
7
  import android.view.ViewGroup
10
8
  import androidx.appcompat.R as AppCompatR
@@ -15,12 +13,14 @@ import androidx.core.view.WindowInsetsCompat
15
13
  import dev.apexnative.navigation.options.SearchBarOptions
16
14
  import dev.apexnative.navigation.options.params.ThemeColour
17
15
  import dev.apexnative.navigation.react.events.EventEmitter
16
+ import dev.apexnative.navigation.utils.TestIds
18
17
  import dev.apexnative.navigation.utils.dp
19
18
  import dev.apexnative.navigation.viewcontrollers.parent.OptionsScreen
20
19
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
21
20
  import dev.apexnative.navigation.views.stack.topbar.TopBar
22
21
  import dev.apexnative.navigation.views.stack.topbar.TopBarDefaults
23
22
  import dev.apexnative.navigation.views.stack.topbar.search.SearchStyle
23
+ import dev.apexnative.navigation.views.stack.topbar.titlebar.BarButtonView
24
24
 
25
25
  /**
26
26
  * The search of the screens of a stack (`topBar.searchBar`): each screen has its own while it is in the stack, and the
@@ -38,6 +38,7 @@ class StackSearchPresenter(
38
38
  private var suggestions: View? = null
39
39
  private var wasActive = false
40
40
  private var onActiveChanged: Runnable? = null
41
+ private var magnifier: BarButtonView? = null
41
42
 
42
43
  /** The search of the screen on top opened or closed: the bar's hideOnScroll stops or goes on. */
43
44
  fun setOnActiveChanged(listener: Runnable?) {
@@ -49,6 +50,7 @@ class StackSearchPresenter(
49
50
 
50
51
  fun bindTopBar(topBar: TopBar) {
51
52
  this.topBar = topBar
53
+ magnifier = null
52
54
  // The suggestions hang from the bar: they follow its height as the search opens and its scopes show.
53
55
  topBar.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> topBar.post { positionSuggestions(topBar) } }
54
56
  }
@@ -91,28 +93,29 @@ class StackSearchPresenter(
91
93
  return true
92
94
  }
93
95
 
94
- /** After the right buttons change: the magnifier of an integrated search goes back among them, last. */
96
+ /**
97
+ * After the right buttons change: the magnifier of an integrated search stands after them, before "⋮", as a button
98
+ * of the bar, with the id `<topBar.searchBar.testID>.button`.
99
+ */
95
100
  fun applyMagnifier() {
96
101
  val bar = topBar ?: return
97
- val menu = bar.rightButtonBar.menu
98
- val item = menu.findItem(MAGNIFIER_ID)
99
- if (shown?.showsMagnifier != true) {
100
- if (item != null) menu.removeItem(MAGNIFIER_ID)
102
+ val row = bar.rightButtonBar
103
+ val search = shown
104
+ if (search?.showsMagnifier != true) {
105
+ row.setTrailing(null)
101
106
  return
102
107
  }
103
- if (item != null) return
104
- val context = styleContext()
105
- val label = context.getString(AppCompatR.string.abc_searchview_description_search)
106
- menu.add(Menu.NONE, MAGNIFIER_ID, MAGNIFIER_ORDER, label).apply {
107
- icon = AppCompatResources.getDrawable(context, AppCompatR.drawable.abc_ic_search_api_material)?.mutate()?.also {
108
- it.setTint(TopBarDefaults.overflowIconColor(context))
109
- }
110
- setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
111
- setOnMenuItemClickListener {
112
- shown?.activate()
113
- true
114
- }
108
+ val button = magnifier ?: BarButtonView(row.context).also { button ->
109
+ val context = styleContext()
110
+ val icon = AppCompatResources.getDrawable(context, AppCompatR.drawable.abc_ic_search_api_material)?.mutate()
111
+ icon?.setTint(TopBarDefaults.overflowIconColor(context))
112
+ val label = context.getString(AppCompatR.string.abc_searchview_description_search)
113
+ button.show(label, icon, null, true)
114
+ button.setOnClickListener { shown?.activate() }
115
+ magnifier = button
115
116
  }
117
+ TestIds.apply(button, search.options.testID.get(null)?.let { "$it.button" })
118
+ row.setTrailing(button)
116
119
  }
117
120
 
118
121
  override fun refresh(search: ScreenSearch) {
@@ -132,7 +135,9 @@ class StackSearchPresenter(
132
135
  fun onConfigurationChanged(tint: ThemeColour) {
133
136
  shown?.takeTint(tint)
134
137
  searches.values.forEach { it.restyle() }
135
- topBar?.rightButtonBar?.menu?.removeItem(MAGNIFIER_ID)
138
+ // The magnifier is made again in the new mode's colors.
139
+ magnifier = null
140
+ topBar?.rightButtonBar?.setTrailing(null)
136
141
  refresh()
137
142
  }
138
143
 
@@ -231,10 +236,4 @@ class StackSearchPresenter(
231
236
  lp.topMargin = top
232
237
  card.layoutParams = lp
233
238
  }
234
-
235
- private companion object {
236
- val MAGNIFIER_ID = View.generateViewId()
237
- // The highest a menu order goes: after the screen's own buttons.
238
- const val MAGNIFIER_ORDER = 0xFFFF
239
- }
240
239
  }