@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
@@ -0,0 +1,97 @@
1
+ // Fast Refresh runs an edited module again, and every component it exports is a new function. React Refresh keeps
2
+ // the versions of one component in a family: Metro registers each export under its module and name, and React renders
3
+ // the newest version of the family in place of the old one. What the library keeps of a screen (its options, its key,
4
+ // its pair) is read through the same family, so an edit reaches it without a reload.
5
+
6
+ /** The versions of one component React Refresh keeps, `current` the newest. */
7
+ interface Family {
8
+ current: unknown;
9
+ }
10
+
11
+ interface ReactRefresh {
12
+ getFamilyByType?: (type: unknown) => Family | undefined;
13
+ register?: (type: unknown, id: string) => void;
14
+ createSignatureFunctionForTransform?: () => (
15
+ type: unknown,
16
+ key: string,
17
+ forceReset?: boolean
18
+ ) => unknown;
19
+ }
20
+
21
+ // React Native puts React Refresh where Metro's module system finds it, in development only.
22
+ function reactRefresh(): ReactRefresh | undefined {
23
+ if (!__DEV__) return undefined;
24
+ const global = globalThis as { __METRO_GLOBAL_PREFIX__?: string } & Record<string, unknown>;
25
+ return global[`${global.__METRO_GLOBAL_PREFIX__ ?? ''}__ReactRefresh`] as
26
+ ReactRefresh | undefined;
27
+ }
28
+
29
+ /** Whether React Refresh runs: development with Metro. */
30
+ export function refreshRuns(): boolean {
31
+ return reactRefresh()?.getFamilyByType !== undefined;
32
+ }
33
+
34
+ /** The family a component's versions are kept in; undefined in a release build or for a value Metro didn't export. */
35
+ export function familyOf(type: unknown): Family | undefined {
36
+ return reactRefresh()?.getFamilyByType?.(type);
37
+ }
38
+
39
+ /**
40
+ * The newest version of a component after Fast Refresh ran its module again; the component itself when there is none
41
+ * newer, and in a release build.
42
+ */
43
+ export function newestType<T extends object>(type: T): T {
44
+ return (familyOf(type)?.current as T | undefined) ?? type;
45
+ }
46
+
47
+ let redefinedListener: ((screen: object) => void) | undefined;
48
+
49
+ /** Sets what runs when a module makes a screen again: Fast Refresh ran it after an edit. */
50
+ export function onRedefined(listener: (screen: object) => void): void {
51
+ redefinedListener = listener;
52
+ }
53
+
54
+ /**
55
+ * Tells about a screen a module made, once the module ran: Metro registers its exports after it, so the family of the
56
+ * screen is known by then, and a new version of a screen that shows is found by it. Development only.
57
+ */
58
+ export function redefined(screen: object): void {
59
+ if (!__DEV__ || !redefinedListener) return;
60
+ const listener = redefinedListener;
61
+ Promise.resolve().then(() => listener(screen));
62
+ }
63
+
64
+ // The id each family of screens gives the components they render, so their versions join one family too.
65
+ const innerIds = new WeakMap<Family, string>();
66
+ let innerCount = 0;
67
+
68
+ /**
69
+ * Puts a component a screen or the app renders (`owner`: the screen, or the app made by createNavigation; `role`: which
70
+ * of its components) in a family of its own, once the owner's module ran and Metro has given the owner its family: the
71
+ * next version of the owner registers its component under the same id, so React Refresh keeps the component's state
72
+ * when the module runs again, also when the module doesn't export it. A component React Refresh knows already (its
73
+ * module exports it, or Babel registered it) keeps its own family. Development only.
74
+ */
75
+ export function keepStateOf(owner: object, component: unknown, role = 'component'): void {
76
+ const refresh = reactRefresh();
77
+ if (!refresh?.register || component === undefined) return;
78
+ const register = refresh.register;
79
+ Promise.resolve().then(() => {
80
+ const family = familyOf(owner);
81
+ if (!family) return;
82
+ let id = innerIds.get(family);
83
+ if (id === undefined) {
84
+ id = `apexnative.navigation#${++innerCount}`;
85
+ innerIds.set(family, id);
86
+ }
87
+ register(component, `${id}.${role}`);
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Makes React Refresh mount a component of the library again, not keep its state, when a new version of it comes: its
93
+ * hooks are the app's, which may have changed, and the library's code has no signature of them. Development only.
94
+ */
95
+ export function resetOnEdit(type: object): void {
96
+ reactRefresh()?.createSignatureFunctionForTransform?.()(type, 'apexnative.navigation', true);
97
+ }
@@ -29,9 +29,77 @@ export function rememberMergedOptions(id: string, options: Options): void {
29
29
  remembered.set(id, before ? mergeOptions(before, kept) : kept);
30
30
  }
31
31
 
32
+ /** Takes the options at the paths, such as `['topBar', 'subtitle']`, out of those remembered for the node with the id. */
33
+ export function forgetMergedPaths(id: string, paths: readonly string[][]): void {
34
+ const own = ownOptions.get(id);
35
+ if (own) own.options = withoutPaths(own.options, paths);
36
+ const options = remembered.get(id);
37
+ if (options) remembered.set(id, withoutPaths(options, paths));
38
+ }
39
+
40
+ // A copy of the options without the values at the paths.
41
+ function withoutPaths(options: Options, paths: readonly string[][]): Options {
42
+ const kept = structuredCopy(options);
43
+ for (const path of paths) {
44
+ const parent = path
45
+ .slice(0, -1)
46
+ .reduce<Record<string, unknown> | undefined>(
47
+ (group, key) => group?.[key] as Record<string, unknown> | undefined,
48
+ kept
49
+ );
50
+ if (parent) delete parent[path[path.length - 1]!];
51
+ }
52
+ return kept as Options;
53
+ }
54
+
55
+ // A copy of the plain objects of options, the other values shared.
56
+ function structuredCopy(options: Options): Record<string, unknown> {
57
+ return Object.fromEntries(
58
+ Object.entries(options).map(([key, value]) => [
59
+ key,
60
+ isGroup(value) ? structuredCopy(value as Options) : value,
61
+ ])
62
+ );
63
+ }
64
+
65
+ function isGroup(value: unknown): boolean {
66
+ return (
67
+ typeof value === 'object' && value !== null && Object.getPrototypeOf(value) === Object.prototype
68
+ );
69
+ }
70
+
32
71
  /** Forgets the merged options of a node that went; a node of the last root keeps them until another root goes out. */
33
72
  export function forgetMergedOptions(id: string): void {
34
73
  if (!lastRoot?.ids.has(id)) remembered.delete(id);
74
+ ownOptions.delete(id);
75
+ }
76
+
77
+ // The own options of each screen as JavaScript sent them, in development: those it showed with, and every merge and
78
+ // clear since. An edit of a screen's options (Fast Refresh) changes only what still has the old definition's value.
79
+ const ownOptions = new Map<string, { name: string; options: Options }>();
80
+
81
+ /** Notes the own options a screen of the name shows with (development). */
82
+ export function noteOwnOptions(id: string, name: string, options: Options): void {
83
+ if (__DEV__) ownOptions.set(id, { name, options });
84
+ }
85
+
86
+ /** Notes a merge into a screen's own options (development). */
87
+ export function noteMergedOwnOptions(id: string, options: Options): void {
88
+ const noted = __DEV__ ? ownOptions.get(id) : undefined;
89
+ if (noted) noted.options = mergeOptions(noted.options, options);
90
+ }
91
+
92
+ /** The own options of a screen as JavaScript sent them (development); undefined for a screen it didn't note. */
93
+ export function ownOptionsOf(id: string): Options | undefined {
94
+ return ownOptions.get(id)?.options;
95
+ }
96
+
97
+ /**
98
+ * The ids of the screens of the name the native side has, mounted in React or not yet (a tab not selected yet), as
99
+ * development notes them.
100
+ */
101
+ export function notedScreens(name: string): string[] {
102
+ return [...ownOptions].filter(([, noted]) => noted.name === name).map(([id]) => id);
35
103
  }
36
104
 
37
105
  /** The options merged into the node with the id, as remembered; undefined without any. */
@@ -55,6 +123,8 @@ export function rootGoesOut(nodes: LayoutNode[], again: SentRoot | undefined): v
55
123
  const before = lastRoot;
56
124
  lastRoot = { nodes, ids: new Set(nodes.flatMap(idsOf)) };
57
125
  if (!before) return;
126
+ // The screens of the root before go with it, the tabs React never mounted too.
127
+ for (const id of before.ids) if (!lastRoot.ids.has(id)) ownOptions.delete(id);
58
128
  const moved = new Map<string, Options>();
59
129
  if (again === before) {
60
130
  pairNodes(before.nodes, nodes, (old, node) => {
@@ -52,8 +52,15 @@ export type EngineScreen =
52
52
  /** What the root of a React screen renders: the screen's id, and the props the command gave it. */
53
53
  export type ScreenRootProps = { componentId: string } & Record<string, unknown>;
54
54
 
55
+ /**
56
+ * What React Native gives the root of a React screen: the screen's id, its root tag (none in a slot of the app's one
57
+ * tree), and the props the native side gives that root, a tab cell's `tabIndex`, `selected`, `badge`, `dot` and
58
+ * `dotColor`.
59
+ */
60
+ export type NativeRootProps = { componentId: string; rootTag?: number } & Record<string, unknown>;
61
+
55
62
  const screens = new Map<string, EngineScreen>();
56
- const roots = new Map<string, ComponentType<{ componentId: string; rootTag?: number }>>();
63
+ const roots = new Map<string, ComponentType<NativeRootProps>>();
57
64
  let unmounted: ((screenId: string) => void) | undefined;
58
65
  // The React roots mounted under each id, by their root tags. One id can have several: after a JavaScript reload React
59
66
  // Native starts the roots of the old screens again in the new runtime, under the ids the new screens get too, and
@@ -61,6 +68,34 @@ let unmounted: ((screenId: string) => void) | undefined;
61
68
  // bar of every screen that shows it. The id lets its props go only with the last of them. A slot of the app's one tree
62
69
  // has no tag: while the one tree shows a screen with the id, no root of that id lets it go.
63
70
  const owners = new Map<string, Set<number>>();
71
+ // The screens that show by their ids: the name, and how many roots or slots of the id are mounted (a component of the
72
+ // options has a root in the bar of every screen that shows it).
73
+ const shown = new Map<string, { name: string; mounts: number }>();
74
+
75
+ /**
76
+ * Notes a screen as shown, a React root or slot of it or the controller of a native screen, until the function it
77
+ * gives is called.
78
+ */
79
+ export function noteShown(componentId: string, name: string): () => void {
80
+ const entry = shown.get(componentId);
81
+ if (entry?.name === name) entry.mounts++;
82
+ else shown.set(componentId, { name, mounts: 1 });
83
+ return () => {
84
+ const current = shown.get(componentId);
85
+ if (current?.name !== name) return;
86
+ if (--current.mounts === 0) shown.delete(componentId);
87
+ };
88
+ }
89
+
90
+ /** The ids of the screens with the name that show now. */
91
+ export function shownScreens(name: string): string[] {
92
+ return [...shown].filter(([, entry]) => entry.name === name).map(([id]) => id);
93
+ }
94
+
95
+ /** Whether a React root of the screen with the id is mounted now (a slot of the app's one tree doesn't count). */
96
+ export function hasRoot(componentId: string): boolean {
97
+ return owners.has(componentId);
98
+ }
64
99
 
65
100
  /** What runs when the root of a screen unmounts: the screen is gone. */
66
101
  export function onScreenUnmounted(listener: (screenId: string) => void): void {
@@ -193,20 +228,29 @@ export function isReactScreen(name: string): boolean {
193
228
  return screen !== undefined && 'component' in screen;
194
229
  }
195
230
 
231
+ /**
232
+ * The props a screen's component renders with: those a command left under its id ([stored]), the props the native side
233
+ * gave its root over them ([root]: a tab cell's state, which the docs promise arrives on top of `passProps`), and its
234
+ * id. The root tag stays with the root.
235
+ */
236
+ export function screenProps(stored: object, root: NativeRootProps): ScreenRootProps {
237
+ const props: ScreenRootProps = { ...stored, ...root, componentId: root.componentId };
238
+ delete props.rootTag;
239
+ return props;
240
+ }
241
+
196
242
  /**
197
243
  * The root a React screen runs in: the screen with the props a command left for it under its id, and the new ones when
198
- * options give its id new props. React Native gives a root only its id (and its own root props, which stay here); the
199
- * slot of the app's one tree renders the same root.
244
+ * options give its id new props, under the props the native side gives the root ([screenProps]). The slot of the app's
245
+ * one tree renders the same root.
200
246
  */
201
- export function screenRoot(
202
- name: string
203
- ): ComponentType<{ componentId: string; rootTag?: number }> | undefined {
247
+ export function screenRoot(name: string): ComponentType<NativeRootProps> | undefined {
204
248
  const screen = screens.get(name);
205
249
  if (!screen || !('component' in screen)) return undefined;
206
250
  let root = roots.get(name);
207
251
  if (!root) {
208
252
  const Screen = screen.component();
209
- root = function ScreenRoot(rootProps: { componentId: string; rootTag?: number }) {
253
+ root = function ScreenRoot(rootProps: NativeRootProps) {
210
254
  // In the app's one tree the slot renders the root, without a tag of its own.
211
255
  const { componentId, rootTag } = rootProps;
212
256
  const watch = useCallback(
@@ -214,6 +258,7 @@ export function screenRoot(
214
258
  [componentId]
215
259
  );
216
260
  const props = useSyncExternalStore(watch, () => propsOf(componentId));
261
+ useEffect(() => noteShown(componentId, name), [componentId]);
217
262
  useEffect(() => {
218
263
  if (rootTag !== undefined) {
219
264
  const tags = owners.get(componentId) ?? new Set<number>();
@@ -240,7 +285,7 @@ export function screenRoot(
240
285
  unmounted?.(componentId);
241
286
  };
242
287
  }, [componentId, rootTag]);
243
- return <Screen {...props} componentId={componentId} />;
288
+ return <Screen {...screenProps(props, rootProps)} />;
244
289
  };
245
290
  root.displayName = `ScreenRoot(${name})`;
246
291
  roots.set(name, root);
@@ -95,8 +95,8 @@ export interface SheetDetentChangedEvent {
95
95
  /** The id of the visible screen of the modal. */
96
96
  componentId: string;
97
97
  /**
98
- * The detent the sheet stopped at: `'medium'`, `'large'`, or a height (points on iOS, dp on Android). `'content'`
99
- * is sent on iOS for a detent that fits the content; Android names such a detent `'medium'`.
98
+ * The detent the sheet stopped at: `'medium'`, `'large'`, `'content'` (the detent that fits the content), or a
99
+ * height (points on iOS, dp on Android).
100
100
  */
101
101
  detent: 'medium' | 'large' | 'content' | number;
102
102
  }
@@ -258,10 +258,12 @@ export type ElementAnimation = AppearingElementAnimation | DisappearingElementAn
258
258
  * the way (an error in its animation) is skipped too, with a warning in the log: it shows and hides with its screen,
259
259
  * and the transition and its command go on. Without an animation of the screens of its own, the screen that comes fades
260
260
  * in (or the one that goes fades out) in 300 ms around the elements. When the transition ends or is cancelled, the
261
- * element gets its own values back. `x` and `y` are the position in the parent of the element on both platforms. As on
262
- * a side of a screen, don't set `x` with `translationX` or `y` with `translationY` on one element: the translation
263
- * plays and the position doesn't. (Android) A view whose parent is a React view with `removeClippedSubviews` turned on,
264
- * such as a child of a clipping list, is not moved: it counts as not found.
261
+ * element gets its own values back. The element's own `transform` counts: a property that doesn't animate keeps the
262
+ * element's value, and a missing end is that value (a turn read from −180° to 180°, as React Native draws it). `x`
263
+ * and `y` are the position in the parent of the element on both platforms. As on a side of a screen, don't set `x`
264
+ * with `translationX` or `y` with `translationY` on one element: the translation plays and the position doesn't.
265
+ * The element plays where it is, in its screen: it moves with its screen, its parents clip it, and the views drawn
266
+ * after it in its screen stay over it.
265
267
  */
266
268
  export interface ElementTransition {
267
269
  /** The `nativeID` of the view to animate. */
@@ -292,12 +294,20 @@ export interface ElementTransition {
292
294
  * A view that flies from the screen that goes to the screen that comes, changing its place, size, corners and look on
293
295
  * the way. A pair whose view is not found on either screen is skipped while the rest plays, and the transition waits
294
296
  * until the views are found. A pair that fails on the way (an error in its start or its flight) is skipped too, with a
295
- * warning in the log: its views show and hide with their screens, and the transition and its command go on. (iOS) So is
296
- * a pair with a view without a size, or one whose picture can't be taken. Without an animation of the screens of its
297
- * own, the screen that comes fades in (or the one that goes fades out) in 300 ms around the pair. Text keeps its
298
- * proportions and changes its picture within a quarter of the flight. (Android) A picture that doesn't load within a
299
- * second drops out of the transition, and so does a pair with a view whose parent is a React view with
300
- * `removeClippedSubviews` turned on: it counts as not found.
297
+ * warning in the log: its views show and hide with their screens, and the transition and its command go on. So is a
298
+ * pair with a view without a size, or one whose picture can't be taken. Without an animation of the screens of its
299
+ * own, the screen that comes fades in (or the one that goes fades out) in 300 ms around the pair. What flies is a copy:
300
+ * pictures of the two views taken as the transition starts, while the views stay hidden in their places; what changes
301
+ * in them meanwhile shows when they are back. A view its parents cut (`overflow: hidden`, a scroll view, the edge of
302
+ * its screen) shows only the part they leave visible, and that part goes to the one of the other view on the way.
303
+ * Text keeps its proportions, flies whole and changes its picture within a quarter of the flight. A view turned or
304
+ * scaled by its own `transform` or by a parent's flies from where it shows, and its angle goes to the other view's
305
+ * along the shorter arc. The angle is the one React Native draws, from −180° to 180°:
306
+ * `rotate: '270deg'` turns back a quarter, and no pair spins a full turn. (Android) An image without a background or a
307
+ * border flies as the image of the view that comes, from the part and place the fit (`resizeMode`) shows in one view
308
+ * to those in the other, and an animated image plays on; images of other proportions fly as pictures. An image that
309
+ * doesn't load within a second drops out of the transition. A skew and a tilt (`skewX`, `rotateX`, …) don't fly: the
310
+ * view flies without them and has them again at the end.
301
311
  */
302
312
  export interface SharedElementTransition {
303
313
  /** The `nativeID` of the view on the screen that goes. */
@@ -406,14 +416,27 @@ export interface StackPushAnimationOptions extends StackAnimationOptions {
406
416
  }
407
417
 
408
418
  /**
409
- * The animation of a pop, read from the screen that goes. As {@link StackAnimationOptions}, with the preview of the
410
- * back gesture on Android.
419
+ * The animation of a pop, read from the screen that goes. As {@link StackAnimationOptions}, with the swipe back of iOS
420
+ * and the preview of the back gesture on Android.
411
421
  */
412
422
  export interface StackPopAnimationOptions extends StackAnimationOptions {
423
+ /**
424
+ * (iOS) Whether the swipe back plays this pop under the finger. Without it, the swipe back plays UIKit's slide, and
425
+ * the pop's own animation plays only for the back button and `pop`. With `true`, the swipe from the edge plays the
426
+ * pop's own animation as far as the finger has gone across the screen: the user can stop halfway, and letting go
427
+ * finishes the pop or puts the screen back, the rest playing faster after a quick flick. It applies only when the pop
428
+ * has an animation of its own (`content`, `elementTransitions`, `sharedElementTransitions`). The top bar shows the
429
+ * screen under from the start of the swipe, as UIKit does in a transition of its own, and comes back when the swipe
430
+ * is given up. On iOS 26 the swipe from the content stays off for a screen with an own pop.
431
+ * Android ignores it: a pop with an animation of its own plays when the back gesture ends.
432
+ * @default false
433
+ */
434
+ interactive?: boolean;
413
435
  /**
414
436
  * (Android 14+) Whether the back gesture on a pushed screen previews its pop as the user swipes: the screen shrinks
415
437
  * and follows the finger, the screen under it shows behind. `false` pops the screen when the gesture is released,
416
- * as on Android 13 and older; back still pops it. iOS ignores it: its swipe back is UIKit's.
438
+ * as on Android 13 and older; back still pops it. iOS ignores it: its swipe back is UIKit's, or the pop's own
439
+ * animation with `interactive`.
417
440
  * @default true
418
441
  */
419
442
  preview?: boolean;
@@ -469,6 +492,12 @@ export interface ModalAnimationOptions extends EnterExitAnimationOptions {
469
492
  * options of the command. A change by `mergeOptions` is for the next command and animates nothing itself. A theme
470
493
  * switch or a rotation does not change an animation. Without a key, the transition is the platform's own.
471
494
  *
495
+ * The system's setting to reduce motion is respected, with no option to turn that off. (iOS) With Reduce Motion on, a
496
+ * transition of these keys is a fade of the same length: the screen that comes fades in (a push, `setStackRoot`, a
497
+ * modal's show, `setRoot`), the screen that goes fades out (a pop, a dismissal); no element moves. (Android) With
498
+ * Remove animations on (or the animator duration scale at 0), the transitions end at once, no element moves and none
499
+ * is waited for, a side menu opens and closes without its slide, and a sheet shows and closes without it.
500
+ *
472
501
  * ```ts
473
502
  * const options: Options = {
474
503
  * animations: {
@@ -484,7 +513,8 @@ export interface AnimationOptions {
484
513
  push?: StackPushAnimationOptions;
485
514
  /**
486
515
  * The pop of a screen, also `popTo` and `popToRoot`, read from the screen that goes. The swipe back of iOS is
487
- * UIKit's; the back gesture of Android previews the pop (`preview`).
516
+ * UIKit's, or the pop's own animation under the finger (`interactive`); the back gesture of Android previews the pop
517
+ * (`preview`).
488
518
  */
489
519
  pop?: StackPopAnimationOptions;
490
520
  /** The replacing of the screens of a stack, read from the new top screen. */
@@ -73,8 +73,10 @@ export interface OptionsBottomTabsAccessoryComponent {
73
73
  /**
74
74
  * (iOS 26+) The accessory above the tab bar, like the mini player of a music app. The system sizes it (48 points high)
75
75
  * and the component fills that size. It is placed above the bar, or beside the bar when the bar is minimized; on an
76
- * iPad with the bar at the top it sits at the bottom over the full width, always above the bar. The accessory stops
77
- * together with the tabs. Android has no accessory.
76
+ * iPad with the bar at the top it sits at the bottom over the full width, always above the bar. A hidden accessory
77
+ * (`visible: false`, or no name) keeps its React root and its state, and shows again with them. When another accessory
78
+ * (another `componentId`) shows, the replaced one stops once it has left the window and its state is gone. The last
79
+ * accessory stops together with the tabs. Android has no accessory.
78
80
  */
79
81
  export interface OptionsBottomTabsAccessory {
80
82
  /**
@@ -155,6 +157,16 @@ export interface OptionsBottomTabs {
155
157
  * @default true
156
158
  */
157
159
  animate?: boolean;
160
+ /**
161
+ * How the content changes when another tab is selected on screen, by a press, by back or from code. `'platform'`
162
+ * does what the platform does: on Android the tab that goes fades out and the new one fades in and grows (Material's
163
+ * fade through, 300 ms), on iOS the content changes at once. `'none'` changes it at once on both platforms. The
164
+ * appear events of a switch and `ANN.BottomTabSelected` come at its end, so with `'none'` right away. The tab that is
165
+ * selected decides: the value of the screen it shows, over its own, the tabs' and the default options. A
166
+ * `mergeOptions` applies from the next switch. Off screen the content always changes at once.
167
+ * @default 'platform'
168
+ */
169
+ switchAnimation?: 'platform' | 'none';
158
170
  /**
159
171
  * The tab to select, as an index from 0. It selects once, when the options arrive, at creation (the first tab to
160
172
  * show) or in `mergeOptions`; it is not kept. An index out of range selects nothing (or the first tab at creation)
@@ -187,9 +199,10 @@ export interface OptionsBottomTabs {
187
199
  */
188
200
  drawBehind?: boolean;
189
201
  /**
190
- * The background color of the bar; `null` gives the default one. On Android the default is the surface color of the
191
- * Material 3 theme. Before iOS 26 (and with `UIDesignRequiresCompatibility`) the bar is opaque with the color, or
192
- * system opaque without it, and a transparent color makes it transparent. On iOS 26 with glass the color is ignored
202
+ * The background color of the bar; `null` gives the default one. On Android the default is the surface container color
203
+ * (`colorSurfaceContainer`) of the Material 3 theme. Before iOS 26, and in the compatibility mode (`UIDesignRequiresCompatibility` in Info.plist: iOS 26, and iOS 27
204
+ * only for an app built with an SDK before iOS 27), the bar is opaque with the color, or system opaque without it,
205
+ * and a transparent color makes it transparent. On iOS 26 and later with glass the color is ignored
193
206
  * with one warning, since the system has no color for glass, while a transparent color gives a transparent bar. The
194
207
  * theme color `bar` is its default option.
195
208
  */
@@ -396,7 +409,7 @@ export interface OptionsBottomTab {
396
409
  selectedFontSize?: number;
397
410
  /**
398
411
  * (iOS) The insets of the icon. Each side is independent. They apply before iOS 26 only; iOS 26 and the compatibility
399
- * mode ignore them, and so does a system role before iOS 18.
412
+ * mode (see `backgroundColor`) ignore them, and so does a system role before iOS 18.
400
413
  */
401
414
  iconInsets?: IconInsets;
402
415
  /**
@@ -60,7 +60,7 @@ export interface IconInsets {
60
60
  * a color the other fields are ignored.
61
61
  *
62
62
  * On iOS the icon is drawn onto the shape and the button becomes a button of its own (the back button keeps its
63
- * history menu), without the glass capsule on iOS 26 by default. On Android the icon sits on the shape, mirrored in a
63
+ * history menu), without the glass capsule (glass design, iOS 26+) by default. On Android the icon sits on the shape, mirrored in a
64
64
  * right-to-left layout. Layers merge differently: on iOS an object merges field by field like any other, on Android
65
65
  * an `iconBackground` that has a `color` replaces the earlier one as a whole.
66
66
  */
@@ -159,7 +159,7 @@ export interface OptionsTopBarButtonMenu {
159
159
  * A button of the top bar (`leftButtons`, `rightButtons`, `centerButtons`) or of the toolbar (`toolbar.buttons`). The
160
160
  * kinds are picked by the fields that are set. On iOS, the first that fits wins: a `component`, then an icon with text
161
161
  * and `showText`, then an icon with `iconBackground` or `iconInsets`, then an SF Symbol, an image `icon`, a `text`,
162
- * a `systemItem`; a button with none of them is not shown. On Android a button is a menu item of the bar: text, icon
162
+ * a `systemItem`; a button with none of them is not shown. On Android a button is a button of the bar's row: text, icon
163
163
  * or a component. Lists of buttons are replaced as a whole and take nothing from the lists of lower layers.
164
164
  *
165
165
  * Text, icon, color and similar fields are the same in all four places; where a place or a side differs, the field
@@ -189,7 +189,7 @@ export interface OptionsTopBarButton {
189
189
  */
190
190
  icon?: ImageResource;
191
191
  /**
192
- * (iOS) The space around the image of `icon`; the button becomes a button of its own, without a capsule on iOS 26.
192
+ * (iOS) The space around the image of `icon`; the button becomes a button of its own, without a capsule with the glass design (iOS 26+, see `UIDesignRequiresCompatibility`).
193
193
  */
194
194
  iconInsets?: IconInsets;
195
195
  /**
@@ -204,7 +204,7 @@ export interface OptionsTopBarButton {
204
204
  systemItem?: SystemItemIcon;
205
205
  /**
206
206
  * The text of the button. A button with an icon keeps it as a label for the overflow menu, the customization panel
207
- * and VoiceOver (iOS) or as the text of the menu item (Android, where it also shows when there is room).
207
+ * and VoiceOver (iOS), or for TalkBack, the tooltip of a long press and the overflow menu (Android).
208
208
  */
209
209
  text?: string;
210
210
  /** The text a screen reader says for the button. */
@@ -238,14 +238,14 @@ export interface OptionsTopBarButton {
238
238
  disabledColor?: Color;
239
239
  /**
240
240
  * A shape behind the icon. It works with an `icon` image and a `color` of the shape; on iOS the button becomes a
241
- * button of its own without menu, toggle or style, and without a capsule on iOS 26 by default. The color of a whole
241
+ * button of its own without menu, toggle or style, and without a capsule with the glass design by default. The color of a whole
242
242
  * side is `leftButtonBackgroundColor` / `rightButtonBackgroundColor`.
243
243
  */
244
244
  iconBackground?: IconBackgroundOptions;
245
245
  /**
246
- * (iOS) `'plain'` or `'prominent'`, the main action of the bar (Done, Send). On iOS 26 a prominent button is a
246
+ * (iOS) `'plain'` or `'prominent'`, the main action of the bar (Done, Send). With the glass design (iOS 26+, see `UIDesignRequiresCompatibility`) a prominent button is a
247
247
  * capsule in the button's tint with bold text or icon on it (the tint is `color`, the bar's tint without it);
248
- * before iOS 26 it is the system's done style with bold text. Not for component buttons, buttons with
248
+ * without it, it is the system's done style with bold text. Not for component buttons, buttons with
249
249
  * `iconBackground`, and `showText` buttons.
250
250
  * @default 'plain'
251
251
  */
@@ -257,7 +257,7 @@ export interface OptionsTopBarButton {
257
257
  */
258
258
  testID?: string;
259
259
  /**
260
- * (iOS 26+) Whether the button has no glass capsule under it; neighbors keep theirs. By default `true` for
260
+ * (iOS 26+, glass design) Whether the button has no glass capsule under it; neighbors keep theirs. By default `true` for
261
261
  * component buttons and for icon buttons with a background or insets, `false` for the rest. In a `group` of several
262
262
  * buttons the capsule is shared and this has no effect. A change by `mergeOptions` builds the buttons anew.
263
263
  */
@@ -270,8 +270,8 @@ export interface OptionsTopBarButton {
270
270
  */
271
271
  showText?: boolean;
272
272
  /**
273
- * (iOS 26+) Buttons next to each other with the same group share one capsule; a button without a group has its
274
- * own. Before iOS 26 it does nothing, on iOS 15 there are no groups. A side that has a group of several buttons
273
+ * (iOS 26+, glass design) Buttons next to each other with the same group share one capsule; a button without a group has its
274
+ * own. Without the glass design it does nothing, on iOS 15 there are no groups. A side that has a group of several buttons
275
275
  * doesn't animate button changes. The toolbar has no groups: only icons share a capsule there. For center buttons
276
276
  * the group is a section of the overflow menu, and its name is the id the customization is saved under.
277
277
  */
@@ -51,8 +51,7 @@ export type OptionsModalTransitionStyle =
51
51
  *
52
52
  * On iOS `'medium'` and `'large'` are the system stops; a number and `'content'` need iOS 16 and are left out below it
53
53
  * with a warning in the log. On Android `'medium'` is half of the window, `'large'` the full height under the status
54
- * bar, a number is capped at `'large'`, and `'content'` stands in as `'medium'` because the Material sheet has no
55
- * height by content.
54
+ * bar, a number is capped at `'large'`, and `'content'` is the height of the content, measured as on iOS.
56
55
  */
57
56
  export type OptionsModalSheetDetent = 'medium' | 'large' | 'content' | number;
58
57
 
@@ -67,11 +66,11 @@ export type OptionsModalSheetDetent = 'medium' | 'large' | 'content' | number;
67
66
  export interface OptionsModalSheet {
68
67
  /**
69
68
  * The stops the sheet can rest on, and the user drags it between, in any order: on both platforms they are sorted by
70
- * height and repeats dropped. On iOS `'medium'` counts as half of `'large'` there, and `'content'` takes its place by
71
- * the height of the content, again whenever that changes; a list left empty by unsupported values gives
72
- * `['large']`. On Android the Material sheet has three resting places at most: the lowest stop of the list, the
73
- * highest, and `'medium'` between them when the list has both `'medium'` and `'large'`. A stop without a place is
74
- * dropped.
69
+ * height and repeats dropped. On iOS `'medium'` counts as half of `'large'` there, and on both `'content'` takes its
70
+ * place by the height of the content, again whenever that changes; on iOS a list left empty by unsupported values
71
+ * gives `['large']`. On Android the Material sheet has three resting places at most: the lowest stop of the list, the
72
+ * highest, and one between them, `selectedDetent` when it stands there, else the lowest of the others; the rest are
73
+ * dropped. Stops of the same height there are one.
75
74
  * @default ['large']
76
75
  */
77
76
  detents?: OptionsModalSheetDetent[];
@@ -39,13 +39,18 @@ export interface HardwareBackButtonOptions {
39
39
 
40
40
  /**
41
41
  * (Android) A floating action button over the screen, or an entry of its menu. The button shows while its screen is
42
- * on top of the stack; without the option there is none. `mergeOptions` and the default options of the app apply, and
43
- * the colors of the theme are read again when the theme changes.
42
+ * on top of the stack; without the option there is none. It changes as a push or a pop starts and moves with its
43
+ * screen, as the tab bar does: the new screen's button comes in with it, the old one goes out with the screen it
44
+ * belongs to, and an open menu closes. With an animation of the screens' own the old button shrinks away and the new
45
+ * one grows as it starts; during the back gesture the button of the screen under the top shows with that screen.
46
+ * `mergeOptions` and the default options of the app apply, and the colors of the theme are read again when the theme
47
+ * changes.
44
48
  */
45
49
  export interface OptionsFab {
46
50
  /**
47
51
  * The identifier of the button. A press reports it, and the app finds the `onPress` of the button by it. A button
48
- * without an `id` doesn't show. The same `id` on a later show or merge updates the button; another `id` replaces it.
52
+ * without an `id` doesn't show. The same `id` on a later show or merge updates the button, which stays in place
53
+ * while the screens move; another `id` replaces it.
49
54
  * For an entry of `actions` it is the id of that entry's own press.
50
55
  */
51
56
  id: string;
@@ -34,12 +34,15 @@ export interface OptionsLayout {
34
34
  * indicator or navigation bar, the camera cutout. `[]` lets the content reach every edge. The array replaces the
35
35
  * one from a lower layer. The screen adds the padding itself; `useSafeAreaInsets()` returns what is still covered,
36
36
  * which is 0 on an edge the screen keeps and the whole covered size on an edge where the content goes under a bar.
37
- * Edges listed here always win, also for a screen with a large title.
37
+ * Edges listed here win, but the top under a large title: there the list keeps its first rows clear of the bar
38
+ * itself and scrolls under it, which collapses the title, so a `'top'` listed here counts for nothing.
38
39
  *
39
- * Without it, on iOS: every edge except those where the content goes under a bar that shows (the top bar, a tab
40
- * bar or toolbar at the bottom, the side of a vertical bar); a screen with a large title keeps the top free so its
41
- * list can scroll under the bar. On Android: all four edges, except the top under a top bar that hides on scroll
42
- * or under a large title, and the bottom under tabs that hide on scroll.
40
+ * Without it the screen keeps the top and the sides, and its content goes down to the bottom: under the home
41
+ * indicator or the navigation bar, and under a tab bar or toolbar it goes under. A list keeps its last rows clear
42
+ * with `useSafeAreaInsets().bottom`. The top is free where the content goes under a top bar: on iOS under a bar
43
+ * that shows, on Android under a bar that hides on scroll; on both under a large title, so the list scrolls under
44
+ * the bar. On iOS the side of a vertical bar is free too. To have the screen keep the bottom, list every edge it
45
+ * keeps: listed edges replace the default.
43
46
  */
44
47
  safeAreaEdges?: SafeAreaEdge[];
45
48
  /**
@@ -4,7 +4,7 @@ import type { OptionsTopBarButton } from './buttons';
4
4
  export interface OptionsToolbarSpace {
5
5
  /**
6
6
  * `'flexibleSpace'` takes the free width and pushes the next buttons to the far edge; `'fixedSpace'` is a gap of zero
7
- * width that starts a new capsule on iOS 26.
7
+ * width that starts a new capsule with the glass design (iOS 26+).
8
8
  */
9
9
  systemItem: 'flexibleSpace' | 'fixedSpace';
10
10
  }
@@ -36,7 +36,7 @@ export interface OptionsToolbar {
36
36
  * The buttons from the leading edge, in the format of the bar buttons: text, icon, SF Symbol, `systemItem`, a
37
37
  * component (drawn before the screen shows), `enabled`, `color`, `style`, `menu` (it opens upward), `toggle`,
38
38
  * `showText`, `group`; also spaces and the search field. A press sends a button press. The color of a side doesn't
39
- * apply to them, and badges are not drawn. On iOS 26 each button has its own capsule, neighbor icons with the same
39
+ * apply to them, and badges are not drawn. With the glass design (iOS 26+, see `UIDesignRequiresCompatibility`) each button has its own capsule, neighbor icons with the same
40
40
  * `group` share one (the toolbar has no real groups, so text buttons never share), and a zero-width space is put
41
41
  * between neighbors of different groups unless an explicit space is there. The list is replaced as a whole;
42
42
  * `mergeOptions` animates it.