@apexnative/navigation 1.0.0-alpha.0 → 1.0.0-alpha.10

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 (574) hide show
  1. package/ApexNavigation.podspec +2 -1
  2. package/CHANGELOG.md +128 -0
  3. package/README.md +147 -51
  4. package/android/CLAUDE.md +78 -25
  5. package/android/CORE.md +49 -25
  6. package/android/RECIPES.md +13 -6
  7. package/android/consumer-rules.pro +6 -0
  8. package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +85 -10
  9. package/android/src/main/java/dev/apexnative/navigation/NavigationApplication.kt +34 -13
  10. package/android/src/main/java/dev/apexnative/navigation/layout/LayoutChecks.kt +5 -1
  11. package/android/src/main/java/dev/apexnative/navigation/layout/LayoutNode.kt +6 -2
  12. package/android/src/main/java/dev/apexnative/navigation/nativescreen/NativeScreenContext.kt +2 -2
  13. package/android/src/main/java/dev/apexnative/navigation/options/AnimationsOptions.kt +2 -2
  14. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
  15. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
  16. package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
  17. package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +3 -3
  18. package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt +9 -1
  19. package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
  20. package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
  21. package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
  22. package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
  23. package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
  24. package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
  25. package/android/src/main/java/dev/apexnative/navigation/options/SideMenuOptions.kt +6 -1
  26. package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
  27. package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
  28. package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
  29. package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
  30. package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
  31. package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
  32. package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +29 -36
  33. package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
  34. package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
  35. package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
  36. package/android/src/main/java/dev/apexnative/navigation/react/ErrorTexts.kt +75 -60
  37. package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +35 -23
  38. package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +12 -7
  39. package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +42 -4
  40. package/android/src/main/java/dev/apexnative/navigation/react/PendingCalls.kt +48 -0
  41. package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +54 -20
  42. package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +7 -6
  43. package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
  44. package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +19 -50
  45. package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
  46. package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +50 -81
  47. package/android/src/main/java/dev/apexnative/navigation/react/events/EventContract.kt +226 -0
  48. package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +135 -94
  49. package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationState.kt +154 -0
  50. package/android/src/main/java/dev/apexnative/navigation/react/events/ScreenAppearance.kt +0 -6
  51. package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +12 -10
  52. package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
  53. package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +39 -19
  54. package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +16 -4
  55. package/android/src/main/java/dev/apexnative/navigation/slots/SlotViews.kt +40 -0
  56. package/android/src/main/java/dev/apexnative/navigation/utils/ImageKey.kt +66 -0
  57. package/android/src/main/java/dev/apexnative/navigation/utils/ImageLoader.kt +7 -89
  58. package/android/src/main/java/dev/apexnative/navigation/utils/ImageRequests.kt +76 -0
  59. package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
  60. package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
  61. package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
  62. package/android/src/main/java/dev/apexnative/navigation/utils/UiUtils.kt +3 -3
  63. package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
  64. package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
  65. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +10 -7
  66. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
  67. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
  68. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
  69. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +108 -34
  70. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
  71. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
  72. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt +3 -3
  73. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
  74. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
  75. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +12 -9
  76. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +2 -2
  77. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
  78. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
  79. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
  80. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
  81. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
  82. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalAnimations.kt +22 -24
  83. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +109 -21
  84. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalRecord.kt +21 -6
  85. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +476 -73
  86. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +57 -25
  87. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
  88. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +83 -0
  89. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackCheck.kt +50 -0
  90. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +49 -14
  91. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +71 -12
  92. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +37 -11
  93. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +10 -8
  94. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
  95. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ContainerBackground.kt +3 -3
  96. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +6 -6
  97. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +42 -9
  98. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +19 -8
  99. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuPresenter.kt +16 -7
  100. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuRules.kt +43 -4
  101. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
  102. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +77 -17
  103. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +315 -0
  104. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
  105. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +30 -20
  106. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +216 -18
  107. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +26 -1
  108. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +149 -41
  109. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt +26 -8
  110. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +115 -6
  111. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
  112. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +222 -48
  113. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt +3 -5
  114. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +30 -34
  115. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarComponents.kt +5 -51
  116. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
  117. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
  118. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
  119. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +219 -54
  120. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarVisibility.kt +9 -2
  121. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
  122. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +75 -21
  123. package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
  124. package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
  125. package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
  126. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +9 -7
  127. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
  128. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
  129. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
  130. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStep.kt +31 -0
  131. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +143 -135
  132. package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +13 -5
  133. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
  134. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionTargets.kt +9 -6
  135. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionViews.kt +100 -0
  136. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +112 -4
  137. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +346 -36
  138. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
  139. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +56 -41
  140. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextMorph.kt +176 -0
  141. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
  142. package/android/src/main/java/dev/apexnative/navigation/views/sidemenu/SideMenuView.kt +12 -3
  143. package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +45 -0
  144. package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +140 -1
  145. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
  146. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
  147. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
  148. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -17
  149. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +73 -34
  150. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
  151. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
  152. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
  153. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +125 -178
  154. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
  155. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
  156. package/android/src/main/jni/ANNContentHeights.cpp +82 -0
  157. package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
  158. package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
  159. package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
  160. package/android/src/main/res/values/styles.xml +1 -1
  161. package/android/src/main/res/values-night/styles.xml +1 -1
  162. package/cli/app.js +1 -1
  163. package/cli/doctor/checks/app.js +77 -14
  164. package/cli/doctor/checks/splash.js +4 -22
  165. package/cli/migrate/native.js +18 -0
  166. package/cli/project.js +10 -2
  167. package/cli/setup/index.js +18 -0
  168. package/cli/setup/source.js +2 -1
  169. package/cli/setup/steps/appDelegate.js +66 -11
  170. package/cli/setup/steps/mainApplication.js +134 -19
  171. package/cli/setup/steps/metro.js +46 -0
  172. package/cli/setup/steps/sceneDelegate.js +108 -0
  173. package/cli/splash/index.js +3 -1
  174. package/cli/upgrade/changelog.js +2 -2
  175. package/docs-md/architecture/native-screens.md +4 -0
  176. package/docs-md/architecture/theme-and-context.md +131 -12
  177. package/docs-md/architecture/transitions.md +58 -12
  178. package/docs-md/containers/modals.md +8 -4
  179. package/docs-md/containers/overlays.md +3 -1
  180. package/docs-md/containers/side-menu.md +19 -2
  181. package/docs-md/containers/tabs.md +5 -1
  182. package/docs-md/flows/bottom-sheets.md +4 -2
  183. package/docs-md/flows/forms-in-a-modal.md +1 -1
  184. package/docs-md/fundamentals/screens-and-props.md +9 -1
  185. package/docs-md/fundamentals/starting-the-app.md +1 -1
  186. package/docs-md/guides/cli.md +4 -3
  187. package/docs-md/guides/deep-links.md +15 -33
  188. package/docs-md/help/glossary.md +4 -4
  189. package/docs-md/help/troubleshooting.md +44 -6
  190. package/docs-md/index.json +57 -1
  191. package/docs-md/migration/from-expo-router.md +1 -1
  192. package/docs-md/migration/from-react-native-navigation.md +9 -3
  193. package/docs-md/migration/from-react-navigation.md +2 -2
  194. package/docs-md/screen-ui/back-and-gestures.md +46 -2
  195. package/docs-md/screen-ui/bar-buttons.md +5 -5
  196. package/docs-md/screen-ui/search.md +1 -1
  197. package/docs-md/screen-ui/system-ui.md +17 -12
  198. package/docs-md/screen-ui/toolbar-and-fab.md +4 -2
  199. package/docs-md/screen-ui/top-bar.md +8 -4
  200. package/docs-md/start/installation.md +21 -3
  201. package/ios/Animations/ANNFrameDriver.h +55 -15
  202. package/ios/Animations/ANNFrameDriver.mm +216 -60
  203. package/ios/Animations/ANNTransitionAnimator.h +26 -5
  204. package/ios/Animations/ANNTransitionAnimator.mm +61 -13
  205. package/ios/Animations/ANNTransitionPlan.h +2 -2
  206. package/ios/Animations/ANNTransitionPlan.mm +10 -0
  207. package/ios/Animations/ANNTransitionWait.mm +6 -7
  208. package/ios/Animations/Elements/ANNElementFinder.h +57 -0
  209. package/ios/Animations/Elements/ANNElementFinder.mm +174 -2
  210. package/ios/Animations/Elements/ANNElementTransition.h +10 -8
  211. package/ios/Animations/Elements/ANNElementTransition.mm +19 -21
  212. package/ios/Animations/Elements/ANNSharedElementFlight.h +16 -11
  213. package/ios/Animations/Elements/ANNSharedElementFlight.mm +308 -69
  214. package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
  215. package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
  216. package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
  217. package/ios/Animations/Interpolators/ANNViewTracks.h +9 -6
  218. package/ios/Animations/Interpolators/ANNViewTracks.mm +100 -30
  219. package/ios/CLAUDE.md +60 -24
  220. package/ios/CORE.md +44 -34
  221. package/ios/Contract/ANNCommandAnswer.h +13 -5
  222. package/ios/Contract/ANNCommandAnswer.mm +40 -32
  223. package/ios/Contract/ANNCommandContract.h +15 -0
  224. package/ios/Contract/ANNCommandContract.mm +78 -0
  225. package/ios/Contract/ANNCommandRunner.h +10 -9
  226. package/ios/Contract/ANNCommandRunner.mm +22 -28
  227. package/ios/Contract/ANNErrorTexts.h +6 -2
  228. package/ios/Contract/ANNErrorTexts.mm +18 -10
  229. package/ios/Contract/ANNLayoutNode.h +3 -3
  230. package/ios/Contract/ANNLayoutReader.h +3 -3
  231. package/ios/Core/ANNAppDelegate.mm +0 -2
  232. package/ios/Core/ANNAppearanceDispatcher.h +5 -4
  233. package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
  234. package/ios/Core/ANNDeepLinkQueue.h +22 -2
  235. package/ios/Core/ANNDeepLinkQueue.mm +121 -14
  236. package/ios/Core/ANNErrorHandler.h +9 -4
  237. package/ios/Core/ANNErrorHandler.mm +20 -2
  238. package/ios/Core/ANNEventContract.h +246 -0
  239. package/ios/Core/ANNEventContract.mm +225 -0
  240. package/ios/Core/ANNEventEmitter.h +6 -2
  241. package/ios/Core/ANNEventEmitter.mm +117 -80
  242. package/ios/Core/ANNHinge.h +6 -0
  243. package/ios/Core/ANNHinge.mm +15 -4
  244. package/ios/Core/ANNJavaScriptCalls.h +33 -0
  245. package/ios/Core/ANNJavaScriptCalls.mm +52 -0
  246. package/ios/Core/ANNLayoutProtocol.h +5 -5
  247. package/ios/Core/ANNNavigationState.h +7 -5
  248. package/ios/Core/ANNNavigationState.mm +8 -7
  249. package/ios/Core/ANNNodeHelper.h +32 -12
  250. package/ios/Core/ANNNodeHelper.mm +100 -3
  251. package/ios/Core/ANNParking.h +5 -4
  252. package/ios/Core/ANNReloadCoordinator.mm +5 -12
  253. package/ios/Core/ANNRenderWait.h +20 -6
  254. package/ios/Core/ANNRenderWait.mm +143 -56
  255. package/ios/Core/ANNSceneDelegate.mm +25 -15
  256. package/ios/Core/ANNSystemUI.h +3 -2
  257. package/ios/Core/ANNSystemUI.mm +7 -0
  258. package/ios/Core/ANNUtils.h +7 -0
  259. package/ios/Core/ANNUtils.mm +10 -0
  260. package/ios/Core/ApexNavigation+Internal.h +1 -1
  261. package/ios/Core/ApexNavigation.mm +7 -6
  262. package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
  263. package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
  264. package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
  265. package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
  266. package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +10 -8
  267. package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
  268. package/ios/Layouts/Modal/ANNModalCoordinator.mm +57 -16
  269. package/ios/Layouts/Modal/ANNModalPresentation.h +8 -5
  270. package/ios/Layouts/Modal/ANNModalPresentation.mm +12 -12
  271. package/ios/Layouts/Modal/ANNSheetPresenter.mm +83 -1
  272. package/ios/Layouts/Overlay/ANNOverlayCoordinator.mm +2 -8
  273. package/ios/Layouts/Root/ANNRootCoordinator.mm +58 -19
  274. package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
  275. package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
  276. package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
  277. package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
  278. package/ios/Layouts/Screen/ANNComponentViewController.h +15 -13
  279. package/ios/Layouts/Screen/ANNComponentViewController.mm +130 -75
  280. package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
  281. package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
  282. package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
  283. package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
  284. package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
  285. package/ios/Layouts/Screen/ANNScreenPlace.h +53 -0
  286. package/ios/Layouts/Screen/ANNScreenPlace.mm +205 -0
  287. package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
  288. package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -44
  289. package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
  290. package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
  291. package/ios/Layouts/Screen/ANNScreenSettling.h +32 -0
  292. package/ios/Layouts/Screen/ANNScreenSettling.mm +137 -0
  293. package/ios/Layouts/SideMenu/ANNSideMenuTypes.h +8 -0
  294. package/ios/Layouts/SideMenu/ANNSideMenuViewController.mm +83 -14
  295. package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
  296. package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
  297. package/ios/Layouts/SplitView/ANNSplitViewController.mm +7 -8
  298. package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
  299. package/ios/Layouts/Stack/ANNPreviewPush.mm +142 -27
  300. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
  301. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
  302. package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
  303. package/ios/Layouts/Stack/ANNStackController.h +8 -0
  304. package/ios/Layouts/Stack/ANNStackController.mm +184 -16
  305. package/ios/Layouts/Stack/ANNStackQueue.h +5 -2
  306. package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
  307. package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
  308. package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
  309. package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
  310. package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
  311. package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
  312. package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
  313. package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
  314. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
  315. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
  316. package/ios/Layouts/Tabs/ANNBottomTabsController.h +3 -0
  317. package/ios/Layouts/Tabs/ANNBottomTabsController.mm +237 -57
  318. package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
  319. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +6 -2
  320. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +232 -43
  321. package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
  322. package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
  323. package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
  324. package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
  325. package/ios/Options/ANNAnimationOptions.h +2 -1
  326. package/ios/Options/ANNAnimationOptions.mm +1 -1
  327. package/ios/Options/ANNBottomTabsOptions.h +2 -0
  328. package/ios/Options/ANNBottomTabsOptions.mm +1 -0
  329. package/ios/Options/ANNOptions.h +25 -0
  330. package/ios/Options/ANNOptions.mm +157 -1
  331. package/ios/Options/ANNOptionsCache.h +7 -6
  332. package/ios/Options/ANNOptionsReader.mm +108 -0
  333. package/ios/Options/ANNSearchBarOptions.h +4 -3
  334. package/ios/Options/ANNSheetOptions.h +4 -1
  335. package/ios/Options/ANNSideMenuOptions.h +2 -0
  336. package/ios/Options/ANNSideMenuOptions.mm +2 -0
  337. package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
  338. package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
  339. package/ios/Options/ANNUserChoice.h +57 -0
  340. package/ios/Options/ANNUserChoice.mm +44 -0
  341. package/ios/Options/Params/ANNImageSource.h +61 -0
  342. package/ios/Options/Params/ANNImageSource.mm +101 -0
  343. package/ios/Options/Params/ANNOptionImages.h +37 -0
  344. package/ios/Options/Params/ANNOptionImages.mm +284 -0
  345. package/ios/Options/Params/ANNParam.h +15 -0
  346. package/ios/Options/Params/ANNParam.mm +61 -0
  347. package/ios/Options/Params/Color.h +3 -0
  348. package/ios/Options/Params/Color.mm +21 -0
  349. package/ios/Options/Params/ColorParser.h +4 -3
  350. package/ios/Options/Params/ColorParser.mm +22 -4
  351. package/ios/Options/Params/Image.h +15 -2
  352. package/ios/Options/Params/Image.mm +35 -2
  353. package/ios/Options/Params/ImageParser.h +5 -5
  354. package/ios/Options/Params/ImageParser.mm +28 -46
  355. package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
  356. package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
  357. package/ios/RECIPES.md +13 -6
  358. package/ios/React/ANNReactView.mm +6 -25
  359. package/ios/React/ANNSafeAreaViewComponentView.mm +28 -17
  360. package/ios/React/ANNSurfaceHostingView.h +14 -0
  361. package/ios/React/ANNSurfaceHostingView.mm +144 -1
  362. package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
  363. package/ios/React/Slots/ANNSlotContainers.h +14 -6
  364. package/ios/React/Slots/ANNSlotContainers.mm +75 -2
  365. package/ios/React/Slots/ANNSlotHost.mm +1 -3
  366. package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
  367. package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
  368. package/ios/TopBar/ANNBarAppearance.h +2 -1
  369. package/ios/TopBar/ANNBarAppearance.mm +111 -30
  370. package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
  371. package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
  372. package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
  373. package/ios/TopBar/ANNBarButtons.h +14 -8
  374. package/ios/TopBar/ANNBarButtons.mm +397 -80
  375. package/ios/TopBar/ANNIconButtonImage.h +1 -0
  376. package/ios/TopBar/ANNIconButtonImage.mm +34 -1
  377. package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
  378. package/ios/TopBar/ANNLargeTitlePresenter.mm +50 -7
  379. package/ios/TopBar/ANNMainList.h +3 -0
  380. package/ios/TopBar/ANNMainList.mm +4 -0
  381. package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
  382. package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
  383. package/ios/TopBar/ANNSearchPresenter.mm +172 -21
  384. package/ios/TopBar/ANNSearchResultsViewController.mm +29 -6
  385. package/ios/TopBar/ANNTopBarPresenter.h +8 -2
  386. package/ios/TopBar/ANNTopBarPresenter.mm +155 -33
  387. package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
  388. package/ios/TurboModules/ANNTurboModule.mm +104 -33
  389. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
  390. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
  391. package/lib/module/ARCHITECTURE.md +40 -27
  392. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  393. package/lib/module/api/app/AppRegistry.js +14 -2
  394. package/lib/module/api/app/AppRegistry.js.map +1 -1
  395. package/lib/module/api/app/createNavigation.js +190 -66
  396. package/lib/module/api/app/createNavigation.js.map +1 -1
  397. package/lib/module/api/commands/commands.js +54 -6
  398. package/lib/module/api/commands/commands.js.map +1 -1
  399. package/lib/module/api/events/navigationEvents.js +6 -2
  400. package/lib/module/api/events/navigationEvents.js.map +1 -1
  401. package/lib/module/api/hooks/useNavigation.js +8 -3
  402. package/lib/module/api/hooks/useNavigation.js.map +1 -1
  403. package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
  404. package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
  405. package/lib/module/api/linking/dispatchedLinks.js +23 -0
  406. package/lib/module/api/linking/dispatchedLinks.js.map +1 -0
  407. package/lib/module/api/linking/linking.js +11 -0
  408. package/lib/module/api/linking/linking.js.map +1 -1
  409. package/lib/module/api/navigation.js.map +1 -1
  410. package/lib/module/api/options/OptionsChain.js +2 -2
  411. package/lib/module/api/screens/defineScreen.js +45 -3
  412. package/lib/module/api/screens/defineScreen.js.map +1 -1
  413. package/lib/module/api/screens/nativeScreens.js +31 -6
  414. package/lib/module/api/screens/nativeScreens.js.map +1 -1
  415. package/lib/module/api/screens/registerScreens.js +193 -35
  416. package/lib/module/api/screens/registerScreens.js.map +1 -1
  417. package/lib/module/api/theme/theme.js +5 -0
  418. package/lib/module/api/theme/theme.js.map +1 -1
  419. package/lib/module/engine/commands.js +51 -9
  420. package/lib/module/engine/commands.js.map +1 -1
  421. package/lib/module/engine/layout.js +20 -11
  422. package/lib/module/engine/layout.js.map +1 -1
  423. package/lib/module/engine/nativeScreens.js +9 -0
  424. package/lib/module/engine/nativeScreens.js.map +1 -1
  425. package/lib/module/engine/options.js +49 -15
  426. package/lib/module/engine/options.js.map +1 -1
  427. package/lib/module/engine/optionsChange.js +102 -0
  428. package/lib/module/engine/optionsChange.js.map +1 -0
  429. package/lib/module/engine/places.js +52 -7
  430. package/lib/module/engine/places.js.map +1 -1
  431. package/lib/module/engine/props.js +30 -1
  432. package/lib/module/engine/props.js.map +1 -1
  433. package/lib/module/engine/refresh.js +85 -0
  434. package/lib/module/engine/refresh.js.map +1 -0
  435. package/lib/module/engine/rememberedOptions.js +61 -1
  436. package/lib/module/engine/rememberedOptions.js.map +1 -1
  437. package/lib/module/engine/screens.js +77 -16
  438. package/lib/module/engine/screens.js.map +1 -1
  439. package/lib/module/interfaces/contract/commands.json +101 -0
  440. package/lib/module/interfaces/contract/errors.json +275 -0
  441. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +12 -2
  442. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  443. package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
  444. package/lib/typescript/api/app/createNavigation.d.ts +8 -3
  445. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  446. package/lib/typescript/api/commands/commands.d.ts.map +1 -1
  447. package/lib/typescript/api/commands/presentation.d.ts +4 -3
  448. package/lib/typescript/api/commands/presentation.d.ts.map +1 -1
  449. package/lib/typescript/api/events/navigationEvents.d.ts +3 -3
  450. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  451. package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
  452. package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
  453. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
  454. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
  455. package/lib/typescript/api/index.d.ts +1 -1
  456. package/lib/typescript/api/index.d.ts.map +1 -1
  457. package/lib/typescript/api/linking/dispatchedLinks.d.ts +7 -0
  458. package/lib/typescript/api/linking/dispatchedLinks.d.ts.map +1 -0
  459. package/lib/typescript/api/linking/linking.d.ts +8 -0
  460. package/lib/typescript/api/linking/linking.d.ts.map +1 -1
  461. package/lib/typescript/api/navigation.d.ts +4 -1
  462. package/lib/typescript/api/navigation.d.ts.map +1 -1
  463. package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
  464. package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
  465. package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
  466. package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
  467. package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
  468. package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
  469. package/lib/typescript/api/theme/theme.d.ts +2 -0
  470. package/lib/typescript/api/theme/theme.d.ts.map +1 -1
  471. package/lib/typescript/engine/commands.d.ts +5 -0
  472. package/lib/typescript/engine/commands.d.ts.map +1 -1
  473. package/lib/typescript/engine/layout.d.ts.map +1 -1
  474. package/lib/typescript/engine/nativeScreens.d.ts +5 -0
  475. package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
  476. package/lib/typescript/engine/options.d.ts +5 -2
  477. package/lib/typescript/engine/options.d.ts.map +1 -1
  478. package/lib/typescript/engine/optionsChange.d.ts +28 -0
  479. package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
  480. package/lib/typescript/engine/places.d.ts +7 -2
  481. package/lib/typescript/engine/places.d.ts.map +1 -1
  482. package/lib/typescript/engine/props.d.ts +8 -1
  483. package/lib/typescript/engine/props.d.ts.map +1 -1
  484. package/lib/typescript/engine/refresh.d.ts +35 -0
  485. package/lib/typescript/engine/refresh.d.ts.map +1 -0
  486. package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
  487. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  488. package/lib/typescript/engine/screens.d.ts +27 -6
  489. package/lib/typescript/engine/screens.d.ts.map +1 -1
  490. package/lib/typescript/interfaces/ComponentEvents.d.ts +4 -4
  491. package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
  492. package/lib/typescript/interfaces/Events.d.ts +28 -10
  493. package/lib/typescript/interfaces/Events.d.ts.map +1 -1
  494. package/lib/typescript/interfaces/Options.d.ts +1 -1
  495. package/lib/typescript/interfaces/Options.d.ts.map +1 -1
  496. package/lib/typescript/interfaces/options/animations.d.ts +80 -22
  497. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  498. package/lib/typescript/interfaces/options/bottomTabs.d.ts +21 -7
  499. package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
  500. package/lib/typescript/interfaces/options/buttons.d.ts +23 -15
  501. package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
  502. package/lib/typescript/interfaces/options/modals.d.ts +34 -26
  503. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  504. package/lib/typescript/interfaces/options/sideMenu.d.ts +23 -6
  505. package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
  506. package/lib/typescript/interfaces/options/stack.d.ts +17 -3
  507. package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
  508. package/lib/typescript/interfaces/options/system.d.ts +18 -11
  509. package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
  510. package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
  511. package/lib/typescript/interfaces/options/topBar.d.ts +68 -38
  512. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  513. package/metro/acceptPlugin.js +57 -0
  514. package/metro/babelTransformer.js +45 -0
  515. package/metro/index.js +36 -0
  516. package/package.json +4 -25
  517. package/src/ARCHITECTURE.md +40 -27
  518. package/src/adapters/NativeANNTurboModule.ts +12 -2
  519. package/src/api/app/AppRegistry.ts +16 -2
  520. package/src/api/app/createNavigation.ts +203 -69
  521. package/src/api/commands/commands.ts +48 -7
  522. package/src/api/commands/presentation.ts +4 -3
  523. package/src/api/events/navigationEvents.ts +10 -4
  524. package/src/api/hooks/useNavigation.ts +9 -4
  525. package/src/api/hooks/useSafeAreaInsets.ts +4 -3
  526. package/src/api/index.ts +1 -0
  527. package/src/api/linking/dispatchedLinks.ts +18 -0
  528. package/src/api/linking/linking.ts +22 -1
  529. package/src/api/navigation.ts +4 -1
  530. package/src/api/options/OptionsChain.ts +2 -2
  531. package/src/api/screens/defineScreen.ts +49 -5
  532. package/src/api/screens/nativeScreens.ts +40 -4
  533. package/src/api/screens/registerScreens.tsx +185 -42
  534. package/src/api/theme/theme.ts +5 -0
  535. package/src/engine/commands.ts +70 -13
  536. package/src/engine/layout.ts +27 -11
  537. package/src/engine/nativeScreens.ts +10 -1
  538. package/src/engine/options.ts +45 -19
  539. package/src/engine/optionsChange.ts +107 -0
  540. package/src/engine/places.ts +65 -7
  541. package/src/engine/props.ts +30 -1
  542. package/src/engine/refresh.ts +97 -0
  543. package/src/engine/rememberedOptions.ts +74 -1
  544. package/src/engine/screens.tsx +76 -21
  545. package/src/interfaces/ComponentEvents.ts +4 -4
  546. package/src/interfaces/Events.ts +29 -10
  547. package/src/interfaces/Options.ts +1 -0
  548. package/src/interfaces/contract/commands.json +101 -0
  549. package/src/interfaces/contract/errors.json +275 -0
  550. package/src/interfaces/options/animations.ts +81 -22
  551. package/src/interfaces/options/bottomTabs.ts +21 -7
  552. package/src/interfaces/options/buttons.ts +23 -15
  553. package/src/interfaces/options/modals.ts +34 -26
  554. package/src/interfaces/options/sideMenu.ts +24 -6
  555. package/src/interfaces/options/stack.ts +17 -3
  556. package/src/interfaces/options/system.ts +18 -11
  557. package/src/interfaces/options/toolbar.ts +2 -2
  558. package/src/interfaces/options/topBar.ts +68 -38
  559. package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -41
  560. package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -69
  561. package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationStateReporter.kt +0 -62
  562. package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
  563. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
  564. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
  565. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -138
  566. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -88
  567. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
  568. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -66
  569. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +0 -179
  570. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -139
  571. package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
  572. package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -68
  573. package/ios/Layouts/Tabs/ANNTabsPreloadWindow.h +0 -27
  574. package/ios/Layouts/Tabs/ANNTabsPreloadWindow.mm +0 -55
@@ -15,20 +15,27 @@ class HardwareBackButtonOptions {
15
15
  @JvmField var popStackOnPress: Bool = NullBool()
16
16
  /** Internal. */
17
17
  @JvmField var dismissModalOnPress: Bool = NullBool()
18
+ /**
19
+ * `systemAnimation`: `false` keeps back in the app on the first screen too, so the system doesn't play its
20
+ * animation of going home; the press then does the activity's own back.
21
+ */
22
+ @JvmField var systemAnimation: Bool = NullBool()
18
23
 
19
24
  fun hasValue(): Boolean = bottomTabsOnPress.hasValue() || popStackOnPress.hasValue() ||
20
- dismissModalOnPress.hasValue()
25
+ dismissModalOnPress.hasValue() || systemAnimation.hasValue()
21
26
 
22
27
  fun mergeWith(other: HardwareBackButtonOptions) {
23
28
  if (other.bottomTabsOnPress.hasValue()) bottomTabsOnPress = other.bottomTabsOnPress
24
29
  if (other.popStackOnPress.hasValue()) popStackOnPress = other.popStackOnPress
25
30
  if (other.dismissModalOnPress.hasValue()) dismissModalOnPress = other.dismissModalOnPress
31
+ if (other.systemAnimation.hasValue()) systemAnimation = other.systemAnimation
26
32
  }
27
33
 
28
34
  fun mergeWithDefault(defaults: HardwareBackButtonOptions) {
29
35
  if (!bottomTabsOnPress.hasValue()) bottomTabsOnPress = defaults.bottomTabsOnPress
30
36
  if (!popStackOnPress.hasValue()) popStackOnPress = defaults.popStackOnPress
31
37
  if (!dismissModalOnPress.hasValue()) dismissModalOnPress = defaults.dismissModalOnPress
38
+ if (!systemAnimation.hasValue()) systemAnimation = defaults.systemAnimation
32
39
  }
33
40
 
34
41
  fun copy() = HardwareBackButtonOptions().also { it.mergeWith(this) }
@@ -43,6 +50,7 @@ class HardwareBackButtonOptions {
43
50
  options.bottomTabsOnPress = TextParser.parse(map, "bottomTabsOnPress", BOTTOM_TABS_ON_PRESS)
44
51
  options.popStackOnPress = BoolParser.parse(map, "popStackOnPress")
45
52
  options.dismissModalOnPress = BoolParser.parse(map, "dismissModalOnPress")
53
+ options.systemAnimation = BoolParser.parse(map, "systemAnimation")
46
54
  return options
47
55
  }
48
56
  }
@@ -22,7 +22,7 @@ class LargeTitleOptions {
22
22
  @JvmField var collapseOnScroll: Bool = NullBool()
23
23
  @JvmField var fontSize: Fraction = NullFraction()
24
24
  @JvmField var color: ThemeColour = NullThemeColour()
25
- @JvmField var font = FontOptions()
25
+ @ReadFromParent @JvmField var font = FontOptions()
26
26
 
27
27
  /**
28
28
  * When the large title shows: [ALWAYS], [NEVER] or [AUTOMATIC], as on the screen below. `displayMode` over
@@ -9,7 +9,7 @@ import com.facebook.react.bridge.ReadableMap
9
9
  * first).
10
10
  */
11
11
  open class LayoutAnimation(
12
- @JvmField var sharedElements: SharedElements = SharedElements(),
12
+ @OptionKey("sharedElementTransitions") @JvmField var sharedElements: SharedElements = SharedElements(),
13
13
  @JvmField var elementTransitions: ElementTransitions = ElementTransitions(),
14
14
  ) {
15
15
  open fun hasValue(): Boolean = sharedElements.hasValue() || elementTransitions.hasValue()
@@ -0,0 +1,18 @@
1
+ package dev.apexnative.navigation.options
2
+
3
+ /**
4
+ * The JSON key of an option field whose name is not that key (`topBar.backButton` is `TopBarButtons.back`), for the
5
+ * paths of [OptionPaths]; also the flag that tells a value is given, which goes with it. A field without it answers to
6
+ * its own name.
7
+ */
8
+ @Target(AnnotationTarget.FIELD)
9
+ @Retention(AnnotationRetention.RUNTIME)
10
+ annotation class OptionKey(val name: String)
11
+
12
+ /**
13
+ * A group the reader takes from the object of the group that holds it, not from a key of its own (the font of a text,
14
+ * the top bar's buttons): for the paths of [OptionPaths] its fields answer to the keys of that object.
15
+ */
16
+ @Target(AnnotationTarget.FIELD)
17
+ @Retention(AnnotationRetention.RUNTIME)
18
+ annotation class ReadFromParent
@@ -0,0 +1,91 @@
1
+ package dev.apexnative.navigation.options
2
+
3
+ import dev.apexnative.navigation.options.params.Param
4
+ import dev.apexnative.navigation.options.params.ThemeColour
5
+ import java.lang.reflect.Field
6
+ import java.lang.reflect.Modifier
7
+ import java.util.concurrent.ConcurrentHashMap
8
+
9
+ /**
10
+ * The options at a JSON key path, the keys of the options joined with dots (`topBar.subtitle`, `bottomTab.badge`), as
11
+ * the readers parse them: `clearOptions` takes them out of a node's own options. Generic over the whole tree, by
12
+ * reflection on the fields of the groups: a field answers to the key of its name, or of its [OptionKey]; a group the
13
+ * reader takes from the object of its parent ([ReadFromParent]: the font of a text, the top bar's buttons) answers with
14
+ * its fields to the keys of that object. A group is a class with a `mergeWith` of its own class; anything else (a
15
+ * value, a color, a list, a button menu) ends a path.
16
+ */
17
+ internal object OptionPaths {
18
+ /**
19
+ * What a path names: the fields of [group] that answer to its last key, [members] (one, or a value and the flag
20
+ * that tells it is given, as `modal.sheet.largestUndimmedDetent`).
21
+ */
22
+ class Place(val group: Any, val members: List<Field>) {
23
+ /** The value at the path, of the first field. */
24
+ val value: Any? get() = members.first().get(group)
25
+ }
26
+
27
+ // The fields of each class walked, its superclasses' first.
28
+ private val fieldsOfType = ConcurrentHashMap<Class<*>, List<Field>>()
29
+
30
+ /**
31
+ * Takes the value at [path] out of [options], which it changes in place: the field gets the value a freshly made
32
+ * group has for it (unset; a group, an empty one). Whether the path names a field; an empty or unknown path, or
33
+ * one that goes on inside a value (a side of a color), changes nothing.
34
+ */
35
+ fun clear(options: Options, path: String): Boolean {
36
+ val place = find(options, path) ?: return false
37
+ val fresh = newGroup(place.group.javaClass) ?: return false
38
+ place.members.forEach { it.set(place.group, it.get(fresh)) }
39
+ return true
40
+ }
41
+
42
+ /** The fields [path] names in [options]; none for an empty or unknown path or one inside a value. */
43
+ fun find(options: Options, path: String): Place? {
44
+ val keys = path.split('.')
45
+ if (keys.any(String::isEmpty)) return null
46
+ var group: Any = options
47
+ for (key in keys.dropLast(1)) {
48
+ group = fieldOf(group, key)?.value?.takeIf(::isGroup) ?: return null
49
+ }
50
+ return fieldOf(group, keys.last())?.takeIf { newGroup(it.group.javaClass) != null }
51
+ }
52
+
53
+ // The fields of [group] that answer to [key]: its own, else those of a group read from the same object.
54
+ private fun fieldOf(group: Any, key: String): Place? {
55
+ val fields = fieldsOf(group.javaClass)
56
+ fields.filter { !it.isAnnotationPresent(ReadFromParent::class.java) && keyOf(it) == key }
57
+ .takeIf { it.isNotEmpty() }
58
+ ?.let { return Place(group, it) }
59
+ for (field in fields) {
60
+ if (!field.isAnnotationPresent(ReadFromParent::class.java)) continue
61
+ val inner = field.get(group)?.takeIf(::isGroup) ?: continue
62
+ fieldOf(inner, key)?.let { return it }
63
+ }
64
+ return null
65
+ }
66
+
67
+ private fun keyOf(field: Field): String = field.getAnnotation(OptionKey::class.java)?.name ?: field.name
68
+
69
+ private fun fieldsOf(type: Class<*>): List<Field> = fieldsOfType.getOrPut(type) {
70
+ generateSequence(type) { it.superclass }
71
+ .takeWhile { it != Any::class.java }
72
+ .toList()
73
+ .asReversed()
74
+ .flatMap { it.declaredFields.asList() }
75
+ .filter { !Modifier.isStatic(it.modifiers) && !it.isSynthetic }
76
+ .onEach { it.isAccessible = true }
77
+ }
78
+
79
+ private fun isGroup(value: Any): Boolean {
80
+ if (value is Param<*> || value is ThemeColour) return false
81
+ val type = value.javaClass
82
+ return type.declaredMethods.any { it.name == "mergeWith" && it.parameterTypes.contentEquals(arrayOf(type)) }
83
+ }
84
+
85
+ // A group as its class makes it, every field unset; none for a class without a constructor of no arguments.
86
+ private fun newGroup(type: Class<*>): Any? = try {
87
+ type.getDeclaredConstructor().newInstance()
88
+ } catch (_: ReflectiveOperationException) {
89
+ null
90
+ }
91
+ }
@@ -3,9 +3,10 @@ package dev.apexnative.navigation.options
3
3
  /**
4
4
  * The one point where the options of the app change: a process-wide generation that grows at each change. A node's
5
5
  * resolved options are kept while the generation stays and the nodes they were built along are the same
6
- * (`ViewController.resolveCurrentOptions`). Exactly two places call [optionsChanged]: the write of a node's own options
7
- * (`ViewController.options`, and `changeOptions` through it) and the store of new default options
8
- * ([DefaultOptions.set]). Nothing else changes a node's own options or the defaults. Main thread only.
6
+ * (`ViewController.resolveCurrentOptions`). Three places call [optionsChanged]: the write of a node's own options
7
+ * (`ViewController.options`, and `changeOptions` and `mergeOwnOptions` through it), the write of a node's user layer
8
+ * (`ViewController.changeChoices`, and a merge that takes a choice away) and the store of new default options
9
+ * ([DefaultOptions.set]). Nothing else changes a node's own options, its user layer or the defaults. Main thread only.
9
10
  */
10
11
  object OptionsCache {
11
12
  /** The generation of the options; it only grows. */
@@ -13,7 +14,10 @@ object OptionsCache {
13
14
  var generation: Long = 0
14
15
  private set
15
16
 
16
- /** A node's own options or the default options changed: every resolved result built before is stale. */
17
+ /**
18
+ * A node's own options, its user layer or the default options changed: every resolved result built before is
19
+ * stale.
20
+ */
17
21
  @JvmStatic
18
22
  fun optionsChanged() {
19
23
  generation++
@@ -12,25 +12,28 @@ import dev.apexnative.navigation.options.parsers.FractionParser
12
12
  import dev.apexnative.navigation.options.parsers.TextParser
13
13
 
14
14
  /**
15
- * A height a sheet stops at: about half the screen, the full height, or a height in dp. `content` stops at half the
16
- * screen on Android: the Material sheet has no height of its content.
15
+ * A height a sheet stops at: about half the screen, the full height, the height of the screen's content, or a height in
16
+ * dp.
17
17
  */
18
18
  sealed class SheetDetent {
19
19
  object Medium : SheetDetent()
20
20
  object Large : SheetDetent()
21
+ object Content : SheetDetent()
21
22
  data class Height(val dp: Double) : SheetDetent()
22
23
 
23
24
  /** The detent as the app names it, in `sheetDetentChanged`. */
24
25
  fun toValue(): Any = when (this) {
25
26
  Medium -> "medium"
26
27
  Large -> "large"
28
+ Content -> "content"
27
29
  is Height -> dp
28
30
  }
29
31
 
30
32
  companion object {
31
33
  fun parse(value: Any?): SheetDetent? = when (value) {
32
- "medium", "content" -> Medium
34
+ "medium" -> Medium
33
35
  "large" -> Large
36
+ "content" -> Content
34
37
  // A number is a height only when it is finite and positive.
35
38
  is Number -> value.toDouble().takeIf { it.isFinite() && it > 0 }?.let { Height(it) }
36
39
  else -> null
@@ -47,7 +50,7 @@ class SheetOptions {
47
50
  @JvmField var selectedDetent: SheetDetent? = null
48
51
  @JvmField var largestUndimmedDetent: SheetDetent? = null
49
52
  /** The options give `largestUndimmedDetent`, `null` included. */
50
- @JvmField var hasLargestUndimmedDetent = false
53
+ @OptionKey("largestUndimmedDetent") @JvmField var hasLargestUndimmedDetent = false
51
54
  @JvmField var grabber: Bool = NullBool()
52
55
  @JvmField var expandsOnScroll: Bool = NullBool()
53
56
  @JvmField var cornerRadius: Fraction = NullFraction()
@@ -20,16 +20,18 @@ class SideMenuOptions {
20
20
  @JvmField var right = SideMenuSideOptions()
21
21
  @JvmField var openGestureMode: Text = NullText()
22
22
  @JvmField var animationType: Text = NullText()
23
+ @JvmField var menusAttachMode: Text = NullText()
23
24
  @JvmField var testID: Text = NullText()
24
25
 
25
26
  fun hasValue(): Boolean = left.hasValue() || right.hasValue() || openGestureMode.hasValue() ||
26
- animationType.hasValue() || testID.hasValue()
27
+ animationType.hasValue() || menusAttachMode.hasValue() || testID.hasValue()
27
28
 
28
29
  fun mergeWith(other: SideMenuOptions) {
29
30
  left.mergeWith(other.left)
30
31
  right.mergeWith(other.right)
31
32
  if (other.openGestureMode.hasValue()) openGestureMode = other.openGestureMode
32
33
  if (other.animationType.hasValue()) animationType = other.animationType
34
+ if (other.menusAttachMode.hasValue()) menusAttachMode = other.menusAttachMode
33
35
  if (other.testID.hasValue()) testID = other.testID
34
36
  }
35
37
 
@@ -38,6 +40,7 @@ class SideMenuOptions {
38
40
  right.mergeWithDefault(defaults.right)
39
41
  if (!openGestureMode.hasValue()) openGestureMode = defaults.openGestureMode
40
42
  if (!animationType.hasValue()) animationType = defaults.animationType
43
+ if (!menusAttachMode.hasValue()) menusAttachMode = defaults.menusAttachMode
41
44
  if (!testID.hasValue()) testID = defaults.testID
42
45
  }
43
46
 
@@ -46,6 +49,7 @@ class SideMenuOptions {
46
49
  companion object {
47
50
  private val OPEN_GESTURE_MODES = setOf("bezel", "entireScreen")
48
51
  private val ANIMATION_TYPES = setOf("parallax", "slide", "slide-and-scale", "door")
52
+ private val MENUS_ATTACH_MODES = setOf("together", "afterCenter", "onOpen")
49
53
 
50
54
  @JvmStatic
51
55
  fun parse(map: ReadableMap?): SideMenuOptions {
@@ -55,6 +59,7 @@ class SideMenuOptions {
55
59
  options.right = map.group("right") { SideMenuSideOptions.parse(it) }
56
60
  options.openGestureMode = TextParser.parse(map, "openGestureMode", OPEN_GESTURE_MODES)
57
61
  options.animationType = TextParser.parse(map, "animationType", ANIMATION_TYPES)
62
+ options.menusAttachMode = TextParser.parse(map, "menusAttachMode", MENUS_ATTACH_MODES)
58
63
  options.testID = TextParser.parse(map, "testID")
59
64
  return options
60
65
  }
@@ -99,8 +99,8 @@ class BottomTabsEnterExitAnimationOptions {
99
99
  }
100
100
 
101
101
  /**
102
- * `StackAnimationOptions`: the animation of `pop`, and the base of `push` and `setStackRoot`; over the shared elements
103
- * and element transitions of [LayoutAnimation].
102
+ * `StackAnimationOptions`: the base of the animations of `push`, `pop` and `setStackRoot`; over the shared elements and
103
+ * element transitions of [LayoutAnimation].
104
104
  */
105
105
  open class StackAnimationOptions : LayoutAnimation() {
106
106
  @JvmField var enabled: Bool = NullBool()
@@ -188,6 +188,44 @@ class StackPushAnimationOptions : StackAnimationOptions() {
188
188
  }
189
189
  }
190
190
 
191
+ /**
192
+ * `StackPopAnimationOptions`: the animation of `pop`, and `preview`: whether a back gesture over the stack previews the
193
+ * pop of its top (Android 14+); `false` pops it when the gesture is released, as on Android 13.
194
+ */
195
+ class StackPopAnimationOptions : StackAnimationOptions() {
196
+ @JvmField var preview: Bool = NullBool()
197
+
198
+ override fun hasValue(): Boolean = super.hasValue() || preview.hasValue()
199
+
200
+ fun mergeWith(other: StackPopAnimationOptions) {
201
+ super.mergeWith(other)
202
+ if (other.preview.hasValue()) preview = other.preview
203
+ }
204
+
205
+ fun mergeWithDefault(defaults: StackPopAnimationOptions) {
206
+ super.mergeWithDefault(defaults)
207
+ if (!preview.hasValue()) preview = defaults.preview
208
+ }
209
+
210
+ override fun copy(): StackPopAnimationOptions = StackPopAnimationOptions().also { it.mergeWith(this) }
211
+
212
+ // Reads the fields of this class from [map], after those of its parent.
213
+ private fun readStackPopAnimationOptions(map: ReadableMap) {
214
+ readStackAnimationOptions(map)
215
+ preview = BoolParser.parse(map, "preview")
216
+ }
217
+
218
+ companion object {
219
+ @JvmStatic
220
+ fun parse(map: ReadableMap?): StackPopAnimationOptions {
221
+ val options = StackPopAnimationOptions()
222
+ if (map == null) return options
223
+ options.readStackPopAnimationOptions(map)
224
+ return options
225
+ }
226
+ }
227
+ }
228
+
191
229
  /** `SetStackRootAnimationOptions`: the animation of `setStackRoot`. */
192
230
  class SetStackRootAnimationOptions : StackAnimationOptions() {
193
231
  @JvmField var asPush: Bool = NullBool()
@@ -22,9 +22,13 @@ class StatusBarOptions {
22
22
  @JvmField var hideWithTopBar: Bool = NullBool()
23
23
  @JvmField var blur: Bool = NullBool()
24
24
 
25
- /** The status bar is hidden, or the screen is drawn under it: the screen keeps no room for it. */
26
- val isHiddenOrDrawBehind: Boolean
27
- get() = visible.isFalse || drawBehind.isTrue
25
+ /**
26
+ * The status bar is hidden, or the screen is drawn under it: the screen keeps no room for it. Without `drawBehind`, a
27
+ * screen or a layout in an overlay ([inOverlay]) is drawn under it: an overlay covers the whole window, its dimmed
28
+ * backdrop the status bar too.
29
+ */
30
+ fun isHiddenOrDrawBehind(inOverlay: Boolean): Boolean =
31
+ visible.isFalse || if (drawBehind.hasValue()) drawBehind.isTrue else inOverlay
28
32
 
29
33
  fun hasValue(): Boolean = visible.hasValue() || style.hasValue() || backgroundColor.hasValue() ||
30
34
  drawBehind.hasValue() || translucent.hasValue() || animate.hasValue() || hideWithTopBar.hasValue() ||
@@ -22,7 +22,7 @@ class TitleOptions {
22
22
  @JvmField var text: Text = NullText()
23
23
  @JvmField var fontSize: Fraction = NullFraction()
24
24
  @JvmField var color: ThemeColour = NullThemeColour()
25
- @JvmField var font = FontOptions()
25
+ @ReadFromParent @JvmField var font = FontOptions()
26
26
  @JvmField var component = TitleComponentOptions()
27
27
  /** Shared between copies, as its module wants. */
28
28
  @JvmField var menu: ButtonMenuOptions? = null
@@ -98,7 +98,7 @@ class SubtitleOptions {
98
98
  @JvmField var text: Text = NullText()
99
99
  @JvmField var fontSize: Fraction = NullFraction()
100
100
  @JvmField var color: ThemeColour = NullThemeColour()
101
- @JvmField var font = FontOptions()
101
+ @ReadFromParent @JvmField var font = FontOptions()
102
102
  @JvmField var alignment: Text = NullText()
103
103
 
104
104
  fun hasValue(): Boolean =
@@ -109,7 +109,7 @@ class TopBarBackgroundOptions {
109
109
  */
110
110
  class ScrollEdgeAppearanceTitleOptions {
111
111
  @JvmField var color: ThemeColour = NullThemeColour()
112
- @JvmField var font = FontOptions()
112
+ @ReadFromParent @JvmField var font = FontOptions()
113
113
  @JvmField var fontSize: Fraction = NullFraction()
114
114
 
115
115
  fun hasValue(): Boolean = color.hasValue() || font.hasValue() || fontSize.hasValue()
@@ -34,12 +34,14 @@ class TopBarOptions {
34
34
  @JvmField var rightButtonDisabledColor: ThemeColour = NullThemeColour()
35
35
  @JvmField var centerButtonColor: ThemeColour = NullThemeColour()
36
36
  @JvmField var centerButtonDisabledColor: ThemeColour = NullThemeColour()
37
+ @JvmField var menuBackgroundColor: ThemeColour = NullThemeColour()
38
+ @JvmField var menuTextColor: ThemeColour = NullThemeColour()
37
39
  @JvmField var drawBehind: Bool = NullBool()
38
40
  @JvmField var testID: Text = NullText()
39
41
  @JvmField var title = TitleOptions()
40
42
  @JvmField var subtitle = SubtitleOptions()
41
43
  /** `backButton`, `leftButtons`, `rightButtons`, `centerButtons`. */
42
- @JvmField var buttons = TopBarButtons()
44
+ @ReadFromParent @JvmField var buttons = TopBarButtons()
43
45
  @JvmField var customizationId: Text = NullText()
44
46
  /** Shared between copies, as its module wants. */
45
47
  @JvmField var overflowMenu: ButtonMenuOptions? = null
@@ -63,7 +65,8 @@ class TopBarOptions {
63
65
  hideOnTap.hasValue() || hideOnKeyboard.hasValue() || leftButtonColor.hasValue() ||
64
66
  rightButtonColor.hasValue() || leftButtonBackgroundColor.hasValue() || rightButtonBackgroundColor.hasValue() ||
65
67
  leftButtonDisabledColor.hasValue() || rightButtonDisabledColor.hasValue() || centerButtonColor.hasValue() ||
66
- centerButtonDisabledColor.hasValue() || drawBehind.hasValue() || testID.hasValue() || title.hasValue() ||
68
+ centerButtonDisabledColor.hasValue() || menuBackgroundColor.hasValue() || menuTextColor.hasValue() ||
69
+ drawBehind.hasValue() || testID.hasValue() || title.hasValue() ||
67
70
  subtitle.hasValue() || buttons.hasValue() || customizationId.hasValue() || overflowMenu != null ||
68
71
  verticalBarKeeps.hasValue() || background.hasValue() || scrollEdgeAppearance.hasValue() ||
69
72
  barStyle.hasValue() || style.hasValue() || noBorder.hasValue() || searchBar.hasAnyValue() ||
@@ -85,6 +88,8 @@ class TopBarOptions {
85
88
  rightButtonDisabledColor = rightButtonDisabledColor.mergeWith(other.rightButtonDisabledColor)
86
89
  centerButtonColor = centerButtonColor.mergeWith(other.centerButtonColor)
87
90
  centerButtonDisabledColor = centerButtonDisabledColor.mergeWith(other.centerButtonDisabledColor)
91
+ menuBackgroundColor = menuBackgroundColor.mergeWith(other.menuBackgroundColor)
92
+ menuTextColor = menuTextColor.mergeWith(other.menuTextColor)
88
93
  if (other.drawBehind.hasValue()) drawBehind = other.drawBehind
89
94
  if (other.testID.hasValue()) testID = other.testID
90
95
  title.mergeWith(other.title)
@@ -127,6 +132,8 @@ class TopBarOptions {
127
132
  rightButtonDisabledColor = rightButtonDisabledColor.withDefault(defaults.rightButtonDisabledColor)
128
133
  centerButtonColor = centerButtonColor.withDefault(defaults.centerButtonColor)
129
134
  centerButtonDisabledColor = centerButtonDisabledColor.withDefault(defaults.centerButtonDisabledColor)
135
+ menuBackgroundColor = menuBackgroundColor.withDefault(defaults.menuBackgroundColor)
136
+ menuTextColor = menuTextColor.withDefault(defaults.menuTextColor)
130
137
  if (!drawBehind.hasValue()) drawBehind = defaults.drawBehind
131
138
  if (!testID.hasValue()) testID = defaults.testID
132
139
  title.mergeWithDefault(defaults.title)
@@ -178,6 +185,8 @@ class TopBarOptions {
178
185
  options.rightButtonDisabledColor = ThemeColour.parse(context, map, "rightButtonDisabledColor")
179
186
  options.centerButtonColor = ThemeColour.parse(context, map, "centerButtonColor")
180
187
  options.centerButtonDisabledColor = ThemeColour.parse(context, map, "centerButtonDisabledColor")
188
+ options.menuBackgroundColor = ThemeColour.parse(context, map, "menuBackgroundColor")
189
+ options.menuTextColor = ThemeColour.parse(context, map, "menuTextColor")
181
190
  options.drawBehind = BoolParser.parse(map, "drawBehind")
182
191
  options.testID = TextParser.parse(map, "testID")
183
192
  options.title = map.group("title") { TitleOptions.parse(context, it) }
@@ -212,13 +221,13 @@ class TopBarOptions {
212
221
  * don't give it; `[]` takes the buttons of the layers below away.
213
222
  */
214
223
  class TopBarButtons {
215
- @JvmField var back = BackButtonOptions()
224
+ @OptionKey("backButton") @JvmField var back = BackButtonOptions()
216
225
  /** `topBar.leftButtons`; null when the options don't give them. */
217
- @JvmField var left: List<ButtonOptions>? = null
226
+ @OptionKey("leftButtons") @JvmField var left: List<ButtonOptions>? = null
218
227
  /** `topBar.rightButtons`; null when the options don't give them. */
219
- @JvmField var right: List<ButtonOptions>? = null
228
+ @OptionKey("rightButtons") @JvmField var right: List<ButtonOptions>? = null
220
229
  /** `topBar.centerButtons`; null when the options don't give them. */
221
- @JvmField var center: List<ButtonOptions>? = null
230
+ @OptionKey("centerButtons") @JvmField var center: List<ButtonOptions>? = null
222
231
 
223
232
  fun hasValue(): Boolean = back.hasValue() || left != null || right != null || center != null
224
233
 
@@ -263,7 +272,7 @@ class BackButtonOptions {
263
272
  @JvmField var showTitle: Bool = NullBool()
264
273
  @JvmField var color: ThemeColour = NullThemeColour()
265
274
  @JvmField var fontSize: Fraction = NullFraction()
266
- @JvmField var font = FontOptions()
275
+ @ReadFromParent @JvmField var font = FontOptions()
267
276
  @JvmField var iconBackground = IconBackgroundOptions()
268
277
  @JvmField var testID: Text = NullText()
269
278
  @JvmField var enableMenu: Bool = NullBool()
@@ -0,0 +1,159 @@
1
+ package dev.apexnative.navigation.options
2
+
3
+ import dev.apexnative.navigation.options.params.Text
4
+
5
+ /**
6
+ * The user layer of a node: what the user chose in the native UI, kept apart from the node's own options so that the
7
+ * app's next `mergeOptions` or `setDefaultOptions` doesn't wipe it. The choices are the check of a single-selection
8
+ * menu (a bar button's `menu`, by the button's id; `topBar.overflowMenu`) and the search scope
9
+ * (`topBar.searchBar.selectedScope`).
10
+ *
11
+ * It goes over everything else as the node's options resolve (defaults < parents < own < user), by identity rather
12
+ * than as a tree merged over: a menu's check by the button's id and the item's id, the scope by its id. A choice whose
13
+ * button, item or scope is gone is not applied, and leaves at the node's next merge; a merge that gives a chosen field
14
+ * a value other than the one the app had there before takes the choice away ([yieldTo]). `setDefaultOptions` never
15
+ * clears it. The node owns it and writes it through `ViewController.changeChoices`, which moves [OptionsCache]. Main
16
+ * thread only.
17
+ */
18
+ internal class UserChoices {
19
+ // The menus' checks: per menu (a button's id, OVERFLOW for `overflowMenu`) the item picked in each of its levels,
20
+ // in the order of the picks.
21
+ private val checks = LinkedHashMap<String, MutableList<String>>()
22
+ private var scope: String? = null
23
+
24
+ /** The user chose nothing that stays. */
25
+ val isEmpty: Boolean
26
+ get() = checks.isEmpty() && scope == null
27
+
28
+ /**
29
+ * The user picked [itemId] in [menu], the menu of the button [buttonId] or `overflowMenu` (null), as it showed: it
30
+ * takes the place of an earlier pick in the same level.
31
+ */
32
+ fun check(buttonId: String?, menu: ButtonMenuOptions, itemId: String) {
33
+ val picks = checks.getOrPut(keyOf(buttonId)) { mutableListOf() }
34
+ val level = levelItems(menu.items, itemId)?.map { it.id }.orEmpty()
35
+ picks.removeAll { it == itemId || it in level }
36
+ picks += itemId
37
+ }
38
+
39
+ /** The user picked the search scope [scopeId]. */
40
+ fun pickScope(scopeId: String) {
41
+ scope = scopeId
42
+ }
43
+
44
+ /**
45
+ * Writes the choices into [resolved], a result of resolution nobody else holds yet; the menus it changes are copied
46
+ * first, as a resolved result shares its menus with the layers they come from. Returns [resolved].
47
+ */
48
+ fun applyTo(resolved: Options): Options {
49
+ if (isEmpty) return resolved
50
+ val topBar = resolved.topBar
51
+ for ((key, picks) in checks) {
52
+ if (key == OVERFLOW) {
53
+ val menu = topBar.overflowMenu ?: continue
54
+ topBar.overflowMenu = menu.copyMenu().also { copy -> picks.forEach { mark(copy, it) } }
55
+ continue
56
+ }
57
+ val button = buttonIn(topBar.buttons, key) ?: continue
58
+ val menu = button.menu ?: continue
59
+ button.menu = menu.copyMenu().also { copy -> picks.forEach { mark(copy, it) } }
60
+ }
61
+ scope?.let { picked ->
62
+ val searchBar = topBar.searchBar
63
+ if (searchBar.scopes?.any { it.id == picked } == true) searchBar.selectedScope = Text(picked)
64
+ }
65
+ return resolved
66
+ }
67
+
68
+ /**
69
+ * The app merged [delta] into the node. A choice leaves when what it was made in is gone from [after] (the node's
70
+ * options as they resolve after the merge, without this layer), or when [delta] gives its field a value other than
71
+ * the one the node's own options had before the merge ([own]; where they had none, the app's resolution before the
72
+ * merge, [before]). The same value sent again (a re-render) keeps the choice. Returns whether a choice left.
73
+ */
74
+ fun yieldTo(delta: Options, own: Options, before: Options, after: Options): Boolean {
75
+ var changed = false
76
+ val entries = checks.entries.iterator()
77
+ while (entries.hasNext()) {
78
+ val (key, picks) = entries.next()
79
+ val now = menuIn(after, key)
80
+ val given = menuIn(delta, key)
81
+ val was = menuIn(own, key) ?: menuIn(before, key)
82
+ val left = picks.removeAll { itemId ->
83
+ val gone = now == null || levelItems(now.items, itemId) == null
84
+ gone || (given != null && checkedInLevel(given, itemId) != was?.let { checkedInLevel(it, itemId) })
85
+ }
86
+ if (left) changed = true
87
+ if (picks.isEmpty()) entries.remove()
88
+ }
89
+ scope?.let { picked ->
90
+ val given = delta.topBar.searchBar.selectedScope
91
+ val ownScope = own.topBar.searchBar.selectedScope
92
+ val was = (if (ownScope.hasValue()) ownScope else before.topBar.searchBar.selectedScope).get(null)
93
+ val changedByApp = given.hasValue() && given.get(null) != was
94
+ val gone = after.topBar.searchBar.scopes?.any { it.id == picked } != true
95
+ if (changedByApp || gone) {
96
+ scope = null
97
+ changed = true
98
+ }
99
+ }
100
+ return changed
101
+ }
102
+
103
+ companion object {
104
+ // The key of `overflowMenu`, which has no button; no placed button has an empty id.
105
+ private const val OVERFLOW = ""
106
+
107
+ private fun keyOf(buttonId: String?): String = buttonId ?: OVERFLOW
108
+
109
+ /** Whether picking [itemId] in [menu] moves a check: its level has `singleSelection`. */
110
+ fun checks(menu: ButtonMenuOptions, itemId: String): Boolean =
111
+ levelOf(menu.items, menu.singleSelection, itemId) == true
112
+
113
+ /** Moves the check of [itemId]'s level to it, in [menu] itself; nothing in a level without single selection. */
114
+ fun mark(menu: ButtonMenuOptions, itemId: String) {
115
+ mark(menu.items, menu.singleSelection, itemId)
116
+ }
117
+
118
+ private fun buttonIn(buttons: TopBarButtons, id: String): ButtonOptions? =
119
+ buttons.left?.firstOrNull { it.id.get(null) == id } ?: buttons.right?.firstOrNull { it.id.get(null) == id }
120
+
121
+ // The menu the key names in [options]: `overflowMenu`, or the menu of the button with that id.
122
+ private fun menuIn(options: Options, key: String): ButtonMenuOptions? =
123
+ if (key == OVERFLOW) options.topBar.overflowMenu else buttonIn(options.topBar.buttons, key)?.menu
124
+
125
+ // The checked items of the level that holds [itemId] in [menu]: the field a merge gives that level.
126
+ private fun checkedInLevel(menu: ButtonMenuOptions, itemId: String): Set<String>? =
127
+ levelItems(menu.items, itemId)?.filter { it.checked }?.map { it.id }?.toSet()
128
+
129
+ // The items of the level that holds [itemId]; null when it isn't in [elements].
130
+ private fun levelItems(
131
+ elements: List<ButtonMenuOptions.Element>,
132
+ itemId: String,
133
+ ): List<ButtonMenuOptions.Item>? {
134
+ val items = elements.filterIsInstance<ButtonMenuOptions.Item>()
135
+ if (items.any { it.id == itemId }) return items
136
+ return elements.filterIsInstance<ButtonMenuOptions.Submenu>().firstNotNullOfOrNull {
137
+ levelItems(it.items, itemId)
138
+ }
139
+ }
140
+
141
+ // Whether the level holding [itemId] has single selection; null when [itemId] isn't in [elements].
142
+ private fun levelOf(elements: List<ButtonMenuOptions.Element>, single: Boolean, itemId: String): Boolean? {
143
+ if (elements.any { it is ButtonMenuOptions.Item && it.id == itemId }) return single
144
+ for (element in elements) {
145
+ if (element !is ButtonMenuOptions.Submenu) continue
146
+ levelOf(element.items, element.singleSelection, itemId)?.let { return it }
147
+ }
148
+ return null
149
+ }
150
+
151
+ private fun mark(elements: List<ButtonMenuOptions.Element>, single: Boolean, itemId: String): Boolean {
152
+ if (elements.any { it is ButtonMenuOptions.Item && it.id == itemId }) {
153
+ if (single) elements.forEach { if (it is ButtonMenuOptions.Item) it.checked = it.id == itemId }
154
+ return true
155
+ }
156
+ return elements.any { it is ButtonMenuOptions.Submenu && mark(it.items, it.singleSelection, itemId) }
157
+ }
158
+ }
159
+ }