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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/ApexNavigation.podspec +2 -1
  2. package/CHANGELOG.md +32 -0
  3. package/README.md +1 -1
  4. package/android/CLAUDE.md +31 -20
  5. package/android/CORE.md +12 -8
  6. package/android/consumer-rules.pro +6 -0
  7. package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +36 -5
  8. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
  9. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
  10. package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +1 -1
  11. package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
  12. package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
  13. package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
  14. package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
  15. package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
  16. package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
  17. package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
  18. package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
  19. package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
  20. package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
  21. package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +7 -3
  22. package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +41 -3
  23. package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +16 -1
  24. package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +0 -2
  25. package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
  26. package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +12 -49
  27. package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
  28. package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +6 -6
  29. package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +27 -7
  30. package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +12 -1
  31. package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
  32. package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
  33. package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
  34. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +9 -7
  35. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
  36. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +13 -10
  37. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
  38. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
  39. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +7 -8
  40. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
  41. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
  42. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +20 -2
  43. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +443 -58
  44. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +33 -5
  45. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
  46. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +60 -2
  47. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +4 -0
  48. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -6
  49. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +8 -2
  50. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +32 -4
  51. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
  52. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +2 -2
  53. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +50 -7
  54. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +15 -3
  55. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +10 -4
  56. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
  57. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +215 -43
  58. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +25 -26
  59. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
  60. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
  61. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +36 -38
  62. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
  63. package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
  64. package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
  65. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
  66. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
  67. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +129 -179
  68. package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +8 -1
  69. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
  70. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +67 -3
  71. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +346 -36
  72. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
  73. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +55 -42
  74. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextMorph.kt +176 -0
  75. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
  76. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
  77. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
  78. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -24
  79. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +51 -34
  80. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
  81. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
  82. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
  83. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +121 -175
  84. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
  85. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
  86. package/android/src/main/jni/ANNContentHeights.cpp +82 -0
  87. package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
  88. package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
  89. package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
  90. package/android/src/main/res/values/styles.xml +1 -1
  91. package/android/src/main/res/values-night/styles.xml +1 -1
  92. package/cli/doctor/checks/app.js +16 -1
  93. package/cli/project.js +4 -0
  94. package/cli/setup/index.js +5 -0
  95. package/cli/setup/steps/metro.js +46 -0
  96. package/docs-md/architecture/native-screens.md +4 -0
  97. package/docs-md/architecture/theme-and-context.md +131 -12
  98. package/docs-md/architecture/transitions.md +46 -5
  99. package/docs-md/containers/modals.md +2 -0
  100. package/docs-md/containers/overlays.md +1 -1
  101. package/docs-md/containers/side-menu.md +1 -1
  102. package/docs-md/containers/tabs.md +2 -0
  103. package/docs-md/flows/bottom-sheets.md +4 -2
  104. package/docs-md/fundamentals/screens-and-props.md +9 -1
  105. package/docs-md/fundamentals/starting-the-app.md +1 -1
  106. package/docs-md/guides/cli.md +3 -1
  107. package/docs-md/help/glossary.md +2 -2
  108. package/docs-md/help/troubleshooting.md +22 -1
  109. package/docs-md/index.json +41 -1
  110. package/docs-md/migration/from-expo-router.md +1 -1
  111. package/docs-md/migration/from-react-native-navigation.md +1 -1
  112. package/docs-md/migration/from-react-navigation.md +2 -2
  113. package/docs-md/screen-ui/back-and-gestures.md +17 -1
  114. package/docs-md/screen-ui/bar-buttons.md +5 -5
  115. package/docs-md/screen-ui/search.md +1 -1
  116. package/docs-md/screen-ui/system-ui.md +16 -11
  117. package/docs-md/screen-ui/toolbar-and-fab.md +4 -2
  118. package/docs-md/screen-ui/top-bar.md +8 -4
  119. package/docs-md/start/installation.md +16 -1
  120. package/ios/Animations/ANNFrameDriver.h +55 -15
  121. package/ios/Animations/ANNFrameDriver.mm +216 -60
  122. package/ios/Animations/ANNTransitionAnimator.h +26 -5
  123. package/ios/Animations/ANNTransitionAnimator.mm +49 -3
  124. package/ios/Animations/ANNTransitionPlan.h +2 -2
  125. package/ios/Animations/ANNTransitionPlan.mm +10 -0
  126. package/ios/Animations/Elements/ANNElementFinder.h +2 -2
  127. package/ios/Animations/Elements/ANNElementFinder.mm +2 -2
  128. package/ios/Animations/Elements/ANNElementTransition.h +3 -3
  129. package/ios/Animations/Elements/ANNElementTransition.mm +2 -2
  130. package/ios/Animations/Elements/ANNSharedElementFlight.h +3 -3
  131. package/ios/Animations/Elements/ANNSharedElementFlight.mm +249 -45
  132. package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
  133. package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
  134. package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
  135. package/ios/Animations/Interpolators/ANNViewTracks.h +9 -6
  136. package/ios/Animations/Interpolators/ANNViewTracks.mm +100 -30
  137. package/ios/CLAUDE.md +32 -22
  138. package/ios/CORE.md +25 -21
  139. package/ios/Contract/ANNCommandRunner.h +8 -7
  140. package/ios/Contract/ANNCommandRunner.mm +21 -27
  141. package/ios/Contract/ANNLayoutNode.h +3 -3
  142. package/ios/Contract/ANNLayoutReader.h +3 -3
  143. package/ios/Core/ANNAppDelegate.mm +0 -2
  144. package/ios/Core/ANNDeepLinkQueue.h +6 -5
  145. package/ios/Core/ANNDeepLinkQueue.mm +22 -10
  146. package/ios/Core/ANNJavaScriptCalls.h +11 -12
  147. package/ios/Core/ANNJavaScriptCalls.mm +7 -16
  148. package/ios/Core/ANNReloadCoordinator.mm +4 -8
  149. package/ios/Core/ANNSceneDelegate.mm +2 -2
  150. package/ios/Core/ApexNavigation+Internal.h +1 -1
  151. package/ios/Core/ApexNavigation.mm +6 -2
  152. package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
  153. package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
  154. package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +4 -4
  155. package/ios/Layouts/Root/ANNRootCoordinator.mm +45 -15
  156. package/ios/Layouts/Screen/ANNComponentViewController.h +2 -2
  157. package/ios/Layouts/Screen/ANNComponentViewController.mm +13 -23
  158. package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
  159. package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
  160. package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
  161. package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
  162. package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
  163. package/ios/Layouts/Screen/ANNScreenRegions.mm +0 -38
  164. package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
  165. package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
  166. package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
  167. package/ios/Layouts/Stack/ANNPreviewPush.mm +57 -5
  168. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
  169. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
  170. package/ios/Layouts/Stack/ANNStackController.h +8 -0
  171. package/ios/Layouts/Stack/ANNStackController.mm +153 -11
  172. package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
  173. package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
  174. package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
  175. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
  176. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
  177. package/ios/Layouts/Tabs/ANNBottomTabsController.mm +2 -1
  178. package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
  179. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +4 -0
  180. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +123 -36
  181. package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
  182. package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
  183. package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
  184. package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
  185. package/ios/Options/ANNAnimationOptions.h +2 -1
  186. package/ios/Options/ANNAnimationOptions.mm +1 -1
  187. package/ios/Options/ANNBottomTabsOptions.h +2 -0
  188. package/ios/Options/ANNBottomTabsOptions.mm +1 -0
  189. package/ios/Options/ANNOptions.h +25 -0
  190. package/ios/Options/ANNOptions.mm +157 -1
  191. package/ios/Options/ANNOptionsReader.mm +108 -0
  192. package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
  193. package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
  194. package/ios/Options/Params/ANNParam.h +15 -0
  195. package/ios/Options/Params/ANNParam.mm +61 -0
  196. package/ios/Options/Params/Color.h +3 -0
  197. package/ios/Options/Params/Color.mm +21 -0
  198. package/ios/Options/Params/ColorParser.h +4 -3
  199. package/ios/Options/Params/ColorParser.mm +22 -4
  200. package/ios/Options/Params/Image.h +4 -0
  201. package/ios/Options/Params/Image.mm +20 -0
  202. package/ios/React/ANNReactView.mm +6 -25
  203. package/ios/React/ANNSafeAreaViewComponentView.mm +26 -15
  204. package/ios/React/ANNSurfaceHostingView.h +14 -0
  205. package/ios/React/ANNSurfaceHostingView.mm +144 -1
  206. package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
  207. package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
  208. package/ios/TopBar/ANNBarAppearance.h +2 -1
  209. package/ios/TopBar/ANNBarAppearance.mm +111 -30
  210. package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
  211. package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
  212. package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
  213. package/ios/TopBar/ANNBarButtons.h +5 -3
  214. package/ios/TopBar/ANNBarButtons.mm +186 -17
  215. package/ios/TopBar/ANNLargeTitlePresenter.mm +24 -4
  216. package/ios/TopBar/ANNMainList.h +3 -0
  217. package/ios/TopBar/ANNMainList.mm +4 -0
  218. package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
  219. package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
  220. package/ios/TopBar/ANNSearchPresenter.mm +2 -2
  221. package/ios/TopBar/ANNSearchResultsViewController.mm +5 -5
  222. package/ios/TopBar/ANNTopBarPresenter.h +3 -2
  223. package/ios/TopBar/ANNTopBarPresenter.mm +95 -22
  224. package/ios/TurboModules/ANNTurboModule.mm +66 -16
  225. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
  226. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
  227. package/lib/module/ARCHITECTURE.md +17 -7
  228. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  229. package/lib/module/api/app/AppRegistry.js +14 -2
  230. package/lib/module/api/app/AppRegistry.js.map +1 -1
  231. package/lib/module/api/app/createNavigation.js +188 -66
  232. package/lib/module/api/app/createNavigation.js.map +1 -1
  233. package/lib/module/api/events/navigationEvents.js +6 -2
  234. package/lib/module/api/events/navigationEvents.js.map +1 -1
  235. package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
  236. package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
  237. package/lib/module/api/linking/linking.js +11 -0
  238. package/lib/module/api/linking/linking.js.map +1 -1
  239. package/lib/module/api/options/OptionsChain.js +2 -2
  240. package/lib/module/api/screens/defineScreen.js +45 -3
  241. package/lib/module/api/screens/defineScreen.js.map +1 -1
  242. package/lib/module/api/screens/nativeScreens.js +31 -6
  243. package/lib/module/api/screens/nativeScreens.js.map +1 -1
  244. package/lib/module/api/screens/registerScreens.js +172 -35
  245. package/lib/module/api/screens/registerScreens.js.map +1 -1
  246. package/lib/module/api/theme/theme.js +5 -0
  247. package/lib/module/api/theme/theme.js.map +1 -1
  248. package/lib/module/engine/commands.js +42 -2
  249. package/lib/module/engine/commands.js.map +1 -1
  250. package/lib/module/engine/layout.js +7 -2
  251. package/lib/module/engine/layout.js.map +1 -1
  252. package/lib/module/engine/nativeScreens.js +9 -0
  253. package/lib/module/engine/nativeScreens.js.map +1 -1
  254. package/lib/module/engine/options.js +5 -4
  255. package/lib/module/engine/options.js.map +1 -1
  256. package/lib/module/engine/optionsChange.js +102 -0
  257. package/lib/module/engine/optionsChange.js.map +1 -0
  258. package/lib/module/engine/refresh.js +85 -0
  259. package/lib/module/engine/refresh.js.map +1 -0
  260. package/lib/module/engine/rememberedOptions.js +60 -0
  261. package/lib/module/engine/rememberedOptions.js.map +1 -1
  262. package/lib/module/engine/screens.js +55 -4
  263. package/lib/module/engine/screens.js.map +1 -1
  264. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +7 -0
  265. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  266. package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
  267. package/lib/typescript/api/app/createNavigation.d.ts +8 -3
  268. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  269. package/lib/typescript/api/events/navigationEvents.d.ts +2 -2
  270. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  271. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
  272. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
  273. package/lib/typescript/api/index.d.ts +1 -1
  274. package/lib/typescript/api/index.d.ts.map +1 -1
  275. package/lib/typescript/api/linking/linking.d.ts +8 -0
  276. package/lib/typescript/api/linking/linking.d.ts.map +1 -1
  277. package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
  278. package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
  279. package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
  280. package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
  281. package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
  282. package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
  283. package/lib/typescript/api/theme/theme.d.ts +2 -0
  284. package/lib/typescript/api/theme/theme.d.ts.map +1 -1
  285. package/lib/typescript/engine/commands.d.ts +5 -0
  286. package/lib/typescript/engine/commands.d.ts.map +1 -1
  287. package/lib/typescript/engine/layout.d.ts.map +1 -1
  288. package/lib/typescript/engine/nativeScreens.d.ts +5 -0
  289. package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
  290. package/lib/typescript/engine/optionsChange.d.ts +28 -0
  291. package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
  292. package/lib/typescript/engine/refresh.d.ts +35 -0
  293. package/lib/typescript/engine/refresh.d.ts.map +1 -0
  294. package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
  295. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  296. package/lib/typescript/engine/screens.d.ts +27 -6
  297. package/lib/typescript/engine/screens.d.ts.map +1 -1
  298. package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
  299. package/lib/typescript/interfaces/options/animations.d.ts +44 -14
  300. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  301. package/lib/typescript/interfaces/options/bottomTabs.d.ts +19 -6
  302. package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
  303. package/lib/typescript/interfaces/options/buttons.d.ts +10 -10
  304. package/lib/typescript/interfaces/options/modals.d.ts +6 -7
  305. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  306. package/lib/typescript/interfaces/options/stack.d.ts +8 -3
  307. package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
  308. package/lib/typescript/interfaces/options/system.d.ts +8 -5
  309. package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
  310. package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
  311. package/lib/typescript/interfaces/options/topBar.d.ts +46 -29
  312. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  313. package/metro/acceptPlugin.js +57 -0
  314. package/metro/babelTransformer.js +45 -0
  315. package/metro/index.js +36 -0
  316. package/package.json +3 -1
  317. package/src/ARCHITECTURE.md +17 -7
  318. package/src/adapters/NativeANNTurboModule.ts +7 -0
  319. package/src/api/app/AppRegistry.ts +16 -2
  320. package/src/api/app/createNavigation.ts +201 -69
  321. package/src/api/events/navigationEvents.ts +9 -3
  322. package/src/api/hooks/useSafeAreaInsets.ts +4 -3
  323. package/src/api/index.ts +1 -0
  324. package/src/api/linking/linking.ts +22 -1
  325. package/src/api/options/OptionsChain.ts +2 -2
  326. package/src/api/screens/defineScreen.ts +49 -5
  327. package/src/api/screens/nativeScreens.ts +40 -4
  328. package/src/api/screens/registerScreens.tsx +170 -42
  329. package/src/api/theme/theme.ts +5 -0
  330. package/src/engine/commands.ts +51 -6
  331. package/src/engine/layout.ts +11 -2
  332. package/src/engine/nativeScreens.ts +10 -1
  333. package/src/engine/options.ts +6 -8
  334. package/src/engine/optionsChange.ts +107 -0
  335. package/src/engine/refresh.ts +97 -0
  336. package/src/engine/rememberedOptions.ts +70 -0
  337. package/src/engine/screens.tsx +53 -8
  338. package/src/interfaces/ComponentEvents.ts +2 -2
  339. package/src/interfaces/options/animations.ts +44 -14
  340. package/src/interfaces/options/bottomTabs.ts +19 -6
  341. package/src/interfaces/options/buttons.ts +10 -10
  342. package/src/interfaces/options/modals.ts +6 -7
  343. package/src/interfaces/options/stack.ts +8 -3
  344. package/src/interfaces/options/system.ts +8 -5
  345. package/src/interfaces/options/toolbar.ts +2 -2
  346. package/src/interfaces/options/topBar.ts +46 -29
  347. package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -42
  348. package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -70
  349. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
  350. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
  351. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -136
  352. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -86
  353. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
  354. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -64
  355. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +0 -177
  356. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -137
  357. package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
  358. package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -70
@@ -34,6 +34,7 @@ import dev.apexnative.navigation.viewcontrollers.stack.StackController
34
34
  import dev.apexnative.navigation.viewcontrollers.stack.fab.FabPlacement
35
35
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.Presenter
36
36
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
37
+ import dev.apexnative.navigation.views.animations.AnimationLayer
37
38
  import dev.apexnative.navigation.views.animations.FadeThrough
38
39
  import dev.apexnative.navigation.views.bottomtabs.BottomTabItem
39
40
  import dev.apexnative.navigation.views.bottomtabs.BottomTabsLayout
@@ -199,7 +200,9 @@ class BottomTabsController(
199
200
  }
200
201
  })
201
202
  val set = AnimatorSet()
202
- set.playTogether(exit, FadeThrough.enter(coming))
203
+ // Each tab fades whole through a hardware layer; a switch ended early ([set]'s end) gives them back too.
204
+ val enter = FadeThrough.enter(coming)
205
+ set.playTogether(AnimationLayer.during(going, exit), AnimationLayer.during(coming, enter))
203
206
  set.addListener(object : AnimatorListenerAdapter() {
204
207
  override fun onAnimationEnd(animation: Animator) {
205
208
  set.removeAllListeners()
@@ -350,8 +353,8 @@ class BottomTabsController(
350
353
 
351
354
  /**
352
355
  * Selects the tab at [index]: the bar shows it at once; on screen the tab it left fades through to it (Material's
353
- * fade through), off screen one container becomes invisible and the other visible at once; then [switched]. The
354
- * history of back follows.
356
+ * fade through); off screen, or when the new tab's `bottomTabs.switchAnimation` is `'none'`, one container becomes
357
+ * invisible and the other visible at once; then [switched]. The history of back follows.
355
358
  */
356
359
  private fun select(index: Int, how: How) {
357
360
  if (index == selectedIndex || index !in tabs.indices || isDestroyed) return
@@ -381,7 +384,7 @@ class BottomTabsController(
381
384
  // it must act in this tab, not in the one still fading out.
382
385
  if (how != How.Press) AppearanceDispatcher.reportState()
383
386
  if (views == null) return switched(index, previous, how, onScreen, hasViews = false)
384
- endSwitch = views.switch(shown, left, animated = onScreen) {
387
+ endSwitch = views.switch(shown, left, animated = onScreen && fadesThrough(shown)) {
385
388
  endSwitch = null
386
389
  if (!isDestroyed) switched(index, previous, how, onScreen, hasViews = true)
387
390
  }
@@ -427,6 +430,11 @@ class BottomTabsController(
427
430
  if (screen == null) finish() else AppearanceDispatcher.afterDidAppear(screen, finish)
428
431
  }
429
432
 
433
+ // `bottomTabs.switchAnimation` of the tab that shows, its shown screen's over its own, the tabs' and the defaults:
434
+ // `'platform'`, the default, fades through; `'none'` switches at once.
435
+ private fun fadesThrough(tab: ViewController<*>): Boolean =
436
+ tab.resolveShownOptions(DefaultOptions.current).bottomTabs.switchAnimation.get(null) != "none"
437
+
430
438
  // A switch still running ends at once and a press's `ANN.BottomTabSelected` still waiting goes, before anything
431
439
  // newer on these tabs.
432
440
  private fun flushPendingSelected() {
@@ -548,9 +556,6 @@ class BottomTabsController(
548
556
  return TabsRoom.tabsTop(room(), screenOptions(screen), bottom)
549
557
  }
550
558
 
551
- override fun leavesBottomEdgeFree(child: ViewController<*>, screen: ViewController<*>): Boolean =
552
- TabsRoom.leavesBottomEdgeFree(screenOptions(screen)) || super.leavesBottomEdgeFree(child, screen)
553
-
554
559
  override fun tabsAtRestOf(child: ViewController<*>, screen: ViewController<*>): FabPlacement.Tabs? {
555
560
  if (peekView() == null) return super.tabsAtRestOf(child, screen)
556
561
  return TabsRoom.fabTabs(room(), screenOptions(screen))
@@ -631,7 +636,6 @@ class BottomTabsController(
631
636
  layout.row = row
632
637
  layout.bar.customRowOptionsListener = { applyRow(row, it) }
633
638
  layout.bar.customRowOptions = resolveShownOptions(DefaultOptions.current).bottomTabs.customRow.copy()
634
- CustomTabRowPlacer.track(activity)
635
639
  }
636
640
 
637
641
  private fun applyRow(row: CustomTabRow, options: BottomTabsCustomRowOptions) {
@@ -683,7 +687,7 @@ class BottomTabsController(
683
687
 
684
688
  /**
685
689
  * Destroys the tabs once: every tab and the registry first, then the cells (each stops its root once), the row out
686
- * of the window, the scroll it followed. A press's event still waiting goes with them.
690
+ * of the tabs' view, the scroll it followed. A press's event still waiting goes with them.
687
691
  */
688
692
  override fun destroy() {
689
693
  if (isDestroyed) return
@@ -694,7 +698,6 @@ class BottomTabsController(
694
698
  endSwitch?.invoke()
695
699
  peekView()?.let { layout ->
696
700
  layout.bar.destroyCustomItemViews()
697
- layout.row?.let(CustomTabRowPlacer::forget)
698
701
  layout.row = null
699
702
  }
700
703
  cells = emptyList()
@@ -49,6 +49,22 @@ internal object CustomTabRowGeometry {
49
49
  content = content,
50
50
  )
51
51
 
52
+ /**
53
+ * The row over the bar as the tabs' view lays it out: the bar's [barLeft], [barRight] and [barBottom] in that
54
+ * view, whose height is [tabsHeight] and whose bottom [navigationBar] pixels lie under the navigation bar — the
55
+ * row's bottom never goes over them —, with [margin], [content] and [bottomMargin] as [place] takes them.
56
+ */
57
+ fun inTabs(
58
+ barLeft: Int,
59
+ barRight: Int,
60
+ barBottom: Int,
61
+ tabsHeight: Int,
62
+ navigationBar: Int,
63
+ margin: Int,
64
+ content: Int,
65
+ bottomMargin: Int,
66
+ ): Place = place(barLeft, barRight, minOf(barBottom, tabsHeight - navigationBar), margin, content, bottomMargin)
67
+
52
68
  /**
53
69
  * The part of the row's frame at [place] below its visible part, in pixels: `bottomMargin`. The row draws and takes
54
70
  * touches only above it.
@@ -61,6 +77,12 @@ internal object CustomTabRowGeometry {
61
77
  */
62
78
  fun needsPlacing(last: Place?, next: Place): Boolean = last != next
63
79
 
80
+ /**
81
+ * The row's own lift over its [elevation] while the bar's container stands at [barZ]: as high as the container, so
82
+ * the row, after it in the tabs' view, draws over it and takes the touches first even under a raised bar.
83
+ */
84
+ fun lift(barZ: Float, elevation: Float): Float = (barZ - elevation).coerceAtLeast(0f)
85
+
64
86
  /**
65
87
  * The row's background: `customRow.backgroundColor` ([color]; `null` and `'NoColor'` are none); without it
66
88
  * `backgroundEffect: 'none'` ([effect]) is clear, any other effect or none the theme's `android:colorBackground`
@@ -58,11 +58,6 @@ internal object TabsRoom {
58
58
  fun tabsTop(bar: Bar, screen: Screen, tabsBottom: Int): Int? =
59
59
  if (screen.hidden) null else tabsBottom - bar.reach - bar.rowOvershoot
60
60
 
61
- /**
62
- * The safe area of the screen leaves its bottom edge free: the bar hides on scroll and shows for it.
63
- */
64
- fun leavesBottomEdgeFree(screen: Screen): Boolean = screen.hideOnScroll && !screen.hidden
65
-
66
61
  /**
67
62
  * The bar at rest under a stack's FAB: also when the screen is under it (`drawBehind`, `hideOnScroll`); none when
68
63
  * the screen hides it.
@@ -8,9 +8,8 @@ import androidx.core.view.ViewCompat
8
8
  import androidx.core.view.WindowInsetsCompat
9
9
  import dev.apexnative.navigation.options.DefaultOptions
10
10
  import dev.apexnative.navigation.options.Options
11
- import dev.apexnative.navigation.react.ArrangementStates
12
11
  import dev.apexnative.navigation.react.ReactView
13
- import dev.apexnative.navigation.react.ScreenInsets
12
+ import dev.apexnative.navigation.react.ScreenEnvironment
14
13
  import dev.apexnative.navigation.react.ScreenRegions
15
14
  import dev.apexnative.navigation.react.events.AppearanceDispatcher
16
15
  import dev.apexnative.navigation.react.events.ComponentType
@@ -157,7 +156,7 @@ class ComponentViewController(
157
156
  shown = layout.isShown || prepared,
158
157
  layoutPending = !prepared && isLayoutPending(content),
159
158
  topOfStack = parent?.isTopOfStack(this) ?: true,
160
- hasSafeArea = ScreenInsets.padding(id) != null,
159
+ hasSafeArea = ScreenEnvironment.padding(id) != null,
161
160
  )
162
161
  if (!measures) return
163
162
  val windowInsets = ViewCompat.getRootWindowInsets(content) ?: return
@@ -176,11 +175,13 @@ class ComponentViewController(
176
175
  val edges = ScreenSafeArea.paddedEdges(
177
176
  safeAreaEdges = resolveCurrentOptions(DefaultOptions.current).layout.safeAreaEdges,
178
177
  topFree = parent?.leavesTopEdgeFree(this) == true,
179
- bottomFree = parent?.leavesBottomEdgeFree(this) == true,
178
+ largeTitle = parent?.showsLargeTitleOver(this) == true,
180
179
  )
181
180
  // The events of the screen go to the JavaScript it was made for, and to no other.
182
181
  val emitter = EventEmitter.of(jsGeneration)
183
- ScreenInsets.update(id, insets, ScreenSafeArea.padding(insets, edges), emitter)
182
+ val padding = ScreenSafeArea.padding(insets, edges)
183
+ val uncovered = ScreenSafeArea.uncovered(insets, padding)
184
+ ScreenEnvironment.setInsets(id, uncovered, padding, ScreenEnvironment.changesOf(emitter))
184
185
  val area = Rect(place.left, place.top, place.right, place.bottom)
185
186
  ScreenRegions.update(activity, id, area, windowInsets, emitter)
186
187
  }
@@ -245,9 +246,7 @@ class ComponentViewController(
245
246
  }
246
247
  setParentController(null)
247
248
  if (isIdTakenByAnother) return
248
- ScreenInsets.remove(id)
249
- ScreenRegions.remove(id)
250
- ArrangementStates.remove(id)
249
+ ScreenEnvironment.remove(id)
251
250
  ScreenReadiness.remove(id)
252
251
  }
253
252
 
@@ -1,6 +1,6 @@
1
1
  package dev.apexnative.navigation.viewcontrollers.component
2
2
 
3
- import dev.apexnative.navigation.react.ScreenInsets
3
+ import dev.apexnative.navigation.react.ScreenEnvironment.Insets
4
4
 
5
5
  /**
6
6
  * The arithmetic of a screen's room, safe area and background on Android, without views: the screen nodes read the
@@ -19,6 +19,7 @@ internal object ScreenSafeArea {
19
19
  const val LEFT = "left"
20
20
 
21
21
  private val ALL_EDGES = setOf(TOP, RIGHT, BOTTOM, LEFT)
22
+ private val DEFAULT_EDGES = setOf(TOP, RIGHT, LEFT)
22
23
  private const val OPAQUE = 0xFF
23
24
 
24
25
  /**
@@ -57,10 +58,10 @@ internal object ScreenSafeArea {
57
58
  headerBottom: Int?,
58
59
  tabsTop: Int?,
59
60
  density: Float,
60
- ): ScreenInsets.Insets {
61
+ ): Insets {
61
62
  val coveredAbove = maxOf(system.top, headerBottom ?: 0)
62
63
  val coveredBelow = minOf(windowHeight - system.bottom, tabsTop ?: windowHeight)
63
- return ScreenInsets.Insets(
64
+ return Insets(
64
65
  top = dp((coveredAbove - root.top).coerceAtLeast(0), density),
65
66
  right = dp((root.right - (windowWidth - system.right)).coerceAtLeast(0), density),
66
67
  bottom = dp((root.bottom - coveredBelow).coerceAtLeast(0), density),
@@ -69,26 +70,43 @@ internal object ScreenSafeArea {
69
70
  }
70
71
 
71
72
  /**
72
- * The edges the screen's safe area view keeps its content off: exactly [safeAreaEdges] when the options give them;
73
- * else all four, but the top when the stack's header hides on scroll or a large title shows ([topFree]) and the
74
- * bottom when the tabs hide on scroll ([bottomFree]).
73
+ * The edges the screen's safe area view keeps its content off: exactly [safeAreaEdges] when the options give them,
74
+ * but the top under a large title ([largeTitle]); else the top and the sides, but the top when the stack's header
75
+ * hides on scroll or a large title shows ([topFree]). The bottom is free by default: the content goes down to the
76
+ * bottom, and a list keeps its last rows clear of the navigation bar with `useSafeAreaInsets()`.
75
77
  */
76
- fun paddedEdges(safeAreaEdges: List<String>?, topFree: Boolean, bottomFree: Boolean): Set<String> {
77
- if (safeAreaEdges != null) return safeAreaEdges.filterTo(LinkedHashSet()) { it in ALL_EDGES }
78
- val edges = LinkedHashSet(ALL_EDGES)
78
+ fun paddedEdges(safeAreaEdges: List<String>?, topFree: Boolean, largeTitle: Boolean): Set<String> {
79
+ if (safeAreaEdges != null) {
80
+ val edges = safeAreaEdges.filterTo(LinkedHashSet()) { it in ALL_EDGES }
81
+ // Under a large title the list keeps its first rows clear of the bar itself; a padded top would follow the
82
+ // header as it collapses and move the list with it.
83
+ if (largeTitle) edges -= TOP
84
+ return edges
85
+ }
86
+ val edges = LinkedHashSet(DEFAULT_EDGES)
79
87
  if (topFree) edges -= TOP
80
- if (bottomFree) edges -= BOTTOM
81
88
  return edges
82
89
  }
83
90
 
84
91
  /** The padding of the safe area view: the [insets] on the [edges] it keeps, 0 on the others. */
85
- fun padding(insets: ScreenInsets.Insets, edges: Set<String>): ScreenInsets.Insets = ScreenInsets.Insets(
92
+ fun padding(insets: Insets, edges: Set<String>): Insets = Insets(
86
93
  top = if (TOP in edges) insets.top else 0.0,
87
94
  right = if (RIGHT in edges) insets.right else 0.0,
88
95
  bottom = if (BOTTOM in edges) insets.bottom else 0.0,
89
96
  left = if (LEFT in edges) insets.left else 0.0,
90
97
  )
91
98
 
99
+ /**
100
+ * The insets JavaScript gets (`useSafeAreaInsets`): what of the [insets] still covers the content once the safe
101
+ * area view keeps it off its [padding], 0 on a padded edge.
102
+ */
103
+ fun uncovered(insets: Insets, padding: Insets) = Insets(
104
+ top = (insets.top - padding.top).coerceAtLeast(0.0),
105
+ right = (insets.right - padding.right).coerceAtLeast(0.0),
106
+ bottom = (insets.bottom - padding.bottom).coerceAtLeast(0.0),
107
+ left = (insets.left - padding.left).coerceAtLeast(0.0),
108
+ )
109
+
92
110
  /**
93
111
  * The inner padding of a screen's view: the side system bars ([systemBars]) at the sides; at the bottom the larger
94
112
  * of the navigation bar (none when the screen draws under it) and the keyboard ([imeBottom]; none with
@@ -10,11 +10,9 @@ import dev.apexnative.navigation.layout.LayoutException
10
10
  import dev.apexnative.navigation.nativescreen.NativeScreenContext
11
11
  import dev.apexnative.navigation.options.DefaultOptions
12
12
  import dev.apexnative.navigation.options.Options
13
- import dev.apexnative.navigation.react.ArrangementStates
14
13
  import dev.apexnative.navigation.react.CommandScope
15
14
  import dev.apexnative.navigation.react.ErrorTexts
16
- import dev.apexnative.navigation.react.ScreenInsets
17
- import dev.apexnative.navigation.react.ScreenRegions
15
+ import dev.apexnative.navigation.react.ScreenEnvironment
18
16
  import dev.apexnative.navigation.react.events.AppearanceDispatcher
19
17
  import dev.apexnative.navigation.react.events.ComponentType
20
18
  import dev.apexnative.navigation.react.events.ScreenAppearance
@@ -170,9 +168,7 @@ class ExternalComponentViewController internal constructor(
170
168
  peekView()?.let { (it.parent as? ViewGroup)?.removeView(it) }
171
169
  setParentController(null)
172
170
  if (isIdTakenByAnother) return
173
- ScreenInsets.remove(id)
174
- ScreenRegions.remove(id)
175
- ArrangementStates.remove(id)
171
+ ScreenEnvironment.remove(id)
176
172
  ScreenReadiness.remove(id)
177
173
  if (!notMade) AppearanceDispatcher.nativeScreenReleased(this)
178
174
  }
@@ -158,6 +158,9 @@ internal class ModalCoordinator(
158
158
 
159
159
  /** The record's views lay out again (the activity resumed). */
160
160
  fun requestLayout(record: ModalRecord) = Unit
161
+
162
+ /** The record's sheet as back sees it; none before its show and for a modal that isn't a sheet. */
163
+ fun sheetBack(record: ModalRecord): SheetBack? = null
161
164
  }
162
165
 
163
166
  /**
@@ -397,11 +400,26 @@ internal class ModalCoordinator(
397
400
  }
398
401
 
399
402
  /**
400
- * The root of the modal that back goes to first; none without one. A back gesture previews only a pop in it: its
401
- * dismissal has a preview of its own nowhere yet.
403
+ * The root of the modal that back goes to first; none without one. A back gesture previews a pop in it, or the
404
+ * modal's dismissal when it is a sheet ([backTargetSheet]).
402
405
  */
403
406
  fun backTargetRoot(): ViewController<*>? = backTarget()?.root
404
407
 
408
+ /**
409
+ * The sheet back would dismiss now, as [handleBack] would, and as a drag down of the user would: the top modal is
410
+ * a sheet that shows, its content can't go back (else back goes there, and a pop in it has its own preview),
411
+ * `modal.swipeToDismiss` isn't false, `hardwareBackButton.dismissModalOnPress` doesn't hold back, it isn't the only
412
+ * modal over no root, and its sheet would hide, not collapse, and isn't closing already ([SheetBack.hidesOnBack]).
413
+ * None otherwise. The back gesture previews its hide on Android 14+, and its release closes it as the user does.
414
+ */
415
+ fun backTargetSheet(): SheetBack? {
416
+ val record = backTarget()?.takeIf { it.isSheet && it.state == State.Shown } ?: return null
417
+ if (record.root.canHandleBack() || holdsBack(record) || isLastWithoutRoot(record)) return null
418
+ val options = record.root.resolveShownOptions(DefaultOptions.current)
419
+ if (options.modal.swipeToDismiss.isFalse) return null
420
+ return views.sheetBack(record)?.takeIf { it.hidesOnBack }
421
+ }
422
+
405
423
  /** The mirror of [handleBack], without acting; null without a modal to ask. */
406
424
  fun canHandleBack(): Boolean? {
407
425
  val record = backTarget() ?: return null