@apexnative/navigation 1.0.0-alpha.1 → 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 (573) hide show
  1. package/ApexNavigation.podspec +2 -1
  2. package/CHANGELOG.md +120 -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/docs-md/architecture/native-screens.md +4 -0
  175. package/docs-md/architecture/theme-and-context.md +131 -12
  176. package/docs-md/architecture/transitions.md +58 -12
  177. package/docs-md/containers/modals.md +8 -4
  178. package/docs-md/containers/overlays.md +3 -1
  179. package/docs-md/containers/side-menu.md +19 -2
  180. package/docs-md/containers/tabs.md +5 -1
  181. package/docs-md/flows/bottom-sheets.md +4 -2
  182. package/docs-md/flows/forms-in-a-modal.md +1 -1
  183. package/docs-md/fundamentals/screens-and-props.md +9 -1
  184. package/docs-md/fundamentals/starting-the-app.md +1 -1
  185. package/docs-md/guides/cli.md +4 -3
  186. package/docs-md/guides/deep-links.md +15 -33
  187. package/docs-md/help/glossary.md +4 -4
  188. package/docs-md/help/troubleshooting.md +44 -6
  189. package/docs-md/index.json +57 -1
  190. package/docs-md/migration/from-expo-router.md +1 -1
  191. package/docs-md/migration/from-react-native-navigation.md +9 -3
  192. package/docs-md/migration/from-react-navigation.md +2 -2
  193. package/docs-md/screen-ui/back-and-gestures.md +46 -2
  194. package/docs-md/screen-ui/bar-buttons.md +5 -5
  195. package/docs-md/screen-ui/search.md +1 -1
  196. package/docs-md/screen-ui/system-ui.md +17 -12
  197. package/docs-md/screen-ui/toolbar-and-fab.md +4 -2
  198. package/docs-md/screen-ui/top-bar.md +8 -4
  199. package/docs-md/start/installation.md +21 -3
  200. package/ios/Animations/ANNFrameDriver.h +55 -15
  201. package/ios/Animations/ANNFrameDriver.mm +216 -60
  202. package/ios/Animations/ANNTransitionAnimator.h +26 -5
  203. package/ios/Animations/ANNTransitionAnimator.mm +61 -13
  204. package/ios/Animations/ANNTransitionPlan.h +2 -2
  205. package/ios/Animations/ANNTransitionPlan.mm +10 -0
  206. package/ios/Animations/ANNTransitionWait.mm +6 -7
  207. package/ios/Animations/Elements/ANNElementFinder.h +57 -0
  208. package/ios/Animations/Elements/ANNElementFinder.mm +174 -2
  209. package/ios/Animations/Elements/ANNElementTransition.h +10 -8
  210. package/ios/Animations/Elements/ANNElementTransition.mm +19 -21
  211. package/ios/Animations/Elements/ANNSharedElementFlight.h +16 -11
  212. package/ios/Animations/Elements/ANNSharedElementFlight.mm +308 -69
  213. package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
  214. package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
  215. package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
  216. package/ios/Animations/Interpolators/ANNViewTracks.h +9 -6
  217. package/ios/Animations/Interpolators/ANNViewTracks.mm +100 -30
  218. package/ios/CLAUDE.md +60 -24
  219. package/ios/CORE.md +44 -34
  220. package/ios/Contract/ANNCommandAnswer.h +13 -5
  221. package/ios/Contract/ANNCommandAnswer.mm +40 -32
  222. package/ios/Contract/ANNCommandContract.h +15 -0
  223. package/ios/Contract/ANNCommandContract.mm +78 -0
  224. package/ios/Contract/ANNCommandRunner.h +10 -9
  225. package/ios/Contract/ANNCommandRunner.mm +22 -28
  226. package/ios/Contract/ANNErrorTexts.h +6 -2
  227. package/ios/Contract/ANNErrorTexts.mm +18 -10
  228. package/ios/Contract/ANNLayoutNode.h +3 -3
  229. package/ios/Contract/ANNLayoutReader.h +3 -3
  230. package/ios/Core/ANNAppDelegate.mm +0 -2
  231. package/ios/Core/ANNAppearanceDispatcher.h +5 -4
  232. package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
  233. package/ios/Core/ANNDeepLinkQueue.h +22 -2
  234. package/ios/Core/ANNDeepLinkQueue.mm +121 -14
  235. package/ios/Core/ANNErrorHandler.h +9 -4
  236. package/ios/Core/ANNErrorHandler.mm +20 -2
  237. package/ios/Core/ANNEventContract.h +246 -0
  238. package/ios/Core/ANNEventContract.mm +225 -0
  239. package/ios/Core/ANNEventEmitter.h +6 -2
  240. package/ios/Core/ANNEventEmitter.mm +117 -80
  241. package/ios/Core/ANNHinge.h +6 -0
  242. package/ios/Core/ANNHinge.mm +15 -4
  243. package/ios/Core/ANNJavaScriptCalls.h +33 -0
  244. package/ios/Core/ANNJavaScriptCalls.mm +52 -0
  245. package/ios/Core/ANNLayoutProtocol.h +5 -5
  246. package/ios/Core/ANNNavigationState.h +7 -5
  247. package/ios/Core/ANNNavigationState.mm +8 -7
  248. package/ios/Core/ANNNodeHelper.h +32 -12
  249. package/ios/Core/ANNNodeHelper.mm +100 -3
  250. package/ios/Core/ANNParking.h +5 -4
  251. package/ios/Core/ANNReloadCoordinator.mm +5 -12
  252. package/ios/Core/ANNRenderWait.h +20 -6
  253. package/ios/Core/ANNRenderWait.mm +143 -56
  254. package/ios/Core/ANNSceneDelegate.mm +25 -15
  255. package/ios/Core/ANNSystemUI.h +3 -2
  256. package/ios/Core/ANNSystemUI.mm +7 -0
  257. package/ios/Core/ANNUtils.h +7 -0
  258. package/ios/Core/ANNUtils.mm +10 -0
  259. package/ios/Core/ApexNavigation+Internal.h +1 -1
  260. package/ios/Core/ApexNavigation.mm +7 -6
  261. package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
  262. package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
  263. package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
  264. package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
  265. package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +10 -8
  266. package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
  267. package/ios/Layouts/Modal/ANNModalCoordinator.mm +57 -16
  268. package/ios/Layouts/Modal/ANNModalPresentation.h +8 -5
  269. package/ios/Layouts/Modal/ANNModalPresentation.mm +12 -12
  270. package/ios/Layouts/Modal/ANNSheetPresenter.mm +83 -1
  271. package/ios/Layouts/Overlay/ANNOverlayCoordinator.mm +2 -8
  272. package/ios/Layouts/Root/ANNRootCoordinator.mm +58 -19
  273. package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
  274. package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
  275. package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
  276. package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
  277. package/ios/Layouts/Screen/ANNComponentViewController.h +15 -13
  278. package/ios/Layouts/Screen/ANNComponentViewController.mm +130 -75
  279. package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
  280. package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
  281. package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
  282. package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
  283. package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
  284. package/ios/Layouts/Screen/ANNScreenPlace.h +53 -0
  285. package/ios/Layouts/Screen/ANNScreenPlace.mm +205 -0
  286. package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
  287. package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -44
  288. package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
  289. package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
  290. package/ios/Layouts/Screen/ANNScreenSettling.h +32 -0
  291. package/ios/Layouts/Screen/ANNScreenSettling.mm +137 -0
  292. package/ios/Layouts/SideMenu/ANNSideMenuTypes.h +8 -0
  293. package/ios/Layouts/SideMenu/ANNSideMenuViewController.mm +83 -14
  294. package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
  295. package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
  296. package/ios/Layouts/SplitView/ANNSplitViewController.mm +7 -8
  297. package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
  298. package/ios/Layouts/Stack/ANNPreviewPush.mm +142 -27
  299. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
  300. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
  301. package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
  302. package/ios/Layouts/Stack/ANNStackController.h +8 -0
  303. package/ios/Layouts/Stack/ANNStackController.mm +184 -16
  304. package/ios/Layouts/Stack/ANNStackQueue.h +5 -2
  305. package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
  306. package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
  307. package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
  308. package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
  309. package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
  310. package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
  311. package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
  312. package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
  313. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
  314. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
  315. package/ios/Layouts/Tabs/ANNBottomTabsController.h +3 -0
  316. package/ios/Layouts/Tabs/ANNBottomTabsController.mm +237 -57
  317. package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
  318. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +6 -2
  319. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +232 -43
  320. package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
  321. package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
  322. package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
  323. package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
  324. package/ios/Options/ANNAnimationOptions.h +2 -1
  325. package/ios/Options/ANNAnimationOptions.mm +1 -1
  326. package/ios/Options/ANNBottomTabsOptions.h +2 -0
  327. package/ios/Options/ANNBottomTabsOptions.mm +1 -0
  328. package/ios/Options/ANNOptions.h +25 -0
  329. package/ios/Options/ANNOptions.mm +157 -1
  330. package/ios/Options/ANNOptionsCache.h +7 -6
  331. package/ios/Options/ANNOptionsReader.mm +108 -0
  332. package/ios/Options/ANNSearchBarOptions.h +4 -3
  333. package/ios/Options/ANNSheetOptions.h +4 -1
  334. package/ios/Options/ANNSideMenuOptions.h +2 -0
  335. package/ios/Options/ANNSideMenuOptions.mm +2 -0
  336. package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
  337. package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
  338. package/ios/Options/ANNUserChoice.h +57 -0
  339. package/ios/Options/ANNUserChoice.mm +44 -0
  340. package/ios/Options/Params/ANNImageSource.h +61 -0
  341. package/ios/Options/Params/ANNImageSource.mm +101 -0
  342. package/ios/Options/Params/ANNOptionImages.h +37 -0
  343. package/ios/Options/Params/ANNOptionImages.mm +284 -0
  344. package/ios/Options/Params/ANNParam.h +15 -0
  345. package/ios/Options/Params/ANNParam.mm +61 -0
  346. package/ios/Options/Params/Color.h +3 -0
  347. package/ios/Options/Params/Color.mm +21 -0
  348. package/ios/Options/Params/ColorParser.h +4 -3
  349. package/ios/Options/Params/ColorParser.mm +22 -4
  350. package/ios/Options/Params/Image.h +15 -2
  351. package/ios/Options/Params/Image.mm +35 -2
  352. package/ios/Options/Params/ImageParser.h +5 -5
  353. package/ios/Options/Params/ImageParser.mm +28 -46
  354. package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
  355. package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
  356. package/ios/RECIPES.md +13 -6
  357. package/ios/React/ANNReactView.mm +6 -25
  358. package/ios/React/ANNSafeAreaViewComponentView.mm +28 -17
  359. package/ios/React/ANNSurfaceHostingView.h +14 -0
  360. package/ios/React/ANNSurfaceHostingView.mm +144 -1
  361. package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
  362. package/ios/React/Slots/ANNSlotContainers.h +14 -6
  363. package/ios/React/Slots/ANNSlotContainers.mm +75 -2
  364. package/ios/React/Slots/ANNSlotHost.mm +1 -3
  365. package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
  366. package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
  367. package/ios/TopBar/ANNBarAppearance.h +2 -1
  368. package/ios/TopBar/ANNBarAppearance.mm +111 -30
  369. package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
  370. package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
  371. package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
  372. package/ios/TopBar/ANNBarButtons.h +14 -8
  373. package/ios/TopBar/ANNBarButtons.mm +397 -80
  374. package/ios/TopBar/ANNIconButtonImage.h +1 -0
  375. package/ios/TopBar/ANNIconButtonImage.mm +34 -1
  376. package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
  377. package/ios/TopBar/ANNLargeTitlePresenter.mm +50 -7
  378. package/ios/TopBar/ANNMainList.h +3 -0
  379. package/ios/TopBar/ANNMainList.mm +4 -0
  380. package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
  381. package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
  382. package/ios/TopBar/ANNSearchPresenter.mm +172 -21
  383. package/ios/TopBar/ANNSearchResultsViewController.mm +29 -6
  384. package/ios/TopBar/ANNTopBarPresenter.h +8 -2
  385. package/ios/TopBar/ANNTopBarPresenter.mm +155 -33
  386. package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
  387. package/ios/TurboModules/ANNTurboModule.mm +104 -33
  388. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
  389. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
  390. package/lib/module/ARCHITECTURE.md +40 -27
  391. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  392. package/lib/module/api/app/AppRegistry.js +14 -2
  393. package/lib/module/api/app/AppRegistry.js.map +1 -1
  394. package/lib/module/api/app/createNavigation.js +190 -66
  395. package/lib/module/api/app/createNavigation.js.map +1 -1
  396. package/lib/module/api/commands/commands.js +54 -6
  397. package/lib/module/api/commands/commands.js.map +1 -1
  398. package/lib/module/api/events/navigationEvents.js +6 -2
  399. package/lib/module/api/events/navigationEvents.js.map +1 -1
  400. package/lib/module/api/hooks/useNavigation.js +8 -3
  401. package/lib/module/api/hooks/useNavigation.js.map +1 -1
  402. package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
  403. package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
  404. package/lib/module/api/linking/dispatchedLinks.js +23 -0
  405. package/lib/module/api/linking/dispatchedLinks.js.map +1 -0
  406. package/lib/module/api/linking/linking.js +11 -0
  407. package/lib/module/api/linking/linking.js.map +1 -1
  408. package/lib/module/api/navigation.js.map +1 -1
  409. package/lib/module/api/options/OptionsChain.js +2 -2
  410. package/lib/module/api/screens/defineScreen.js +45 -3
  411. package/lib/module/api/screens/defineScreen.js.map +1 -1
  412. package/lib/module/api/screens/nativeScreens.js +31 -6
  413. package/lib/module/api/screens/nativeScreens.js.map +1 -1
  414. package/lib/module/api/screens/registerScreens.js +193 -35
  415. package/lib/module/api/screens/registerScreens.js.map +1 -1
  416. package/lib/module/api/theme/theme.js +5 -0
  417. package/lib/module/api/theme/theme.js.map +1 -1
  418. package/lib/module/engine/commands.js +51 -9
  419. package/lib/module/engine/commands.js.map +1 -1
  420. package/lib/module/engine/layout.js +20 -11
  421. package/lib/module/engine/layout.js.map +1 -1
  422. package/lib/module/engine/nativeScreens.js +9 -0
  423. package/lib/module/engine/nativeScreens.js.map +1 -1
  424. package/lib/module/engine/options.js +49 -15
  425. package/lib/module/engine/options.js.map +1 -1
  426. package/lib/module/engine/optionsChange.js +102 -0
  427. package/lib/module/engine/optionsChange.js.map +1 -0
  428. package/lib/module/engine/places.js +52 -7
  429. package/lib/module/engine/places.js.map +1 -1
  430. package/lib/module/engine/props.js +30 -1
  431. package/lib/module/engine/props.js.map +1 -1
  432. package/lib/module/engine/refresh.js +85 -0
  433. package/lib/module/engine/refresh.js.map +1 -0
  434. package/lib/module/engine/rememberedOptions.js +61 -1
  435. package/lib/module/engine/rememberedOptions.js.map +1 -1
  436. package/lib/module/engine/screens.js +77 -16
  437. package/lib/module/engine/screens.js.map +1 -1
  438. package/lib/module/interfaces/contract/commands.json +101 -0
  439. package/lib/module/interfaces/contract/errors.json +275 -0
  440. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +12 -2
  441. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  442. package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
  443. package/lib/typescript/api/app/createNavigation.d.ts +8 -3
  444. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  445. package/lib/typescript/api/commands/commands.d.ts.map +1 -1
  446. package/lib/typescript/api/commands/presentation.d.ts +4 -3
  447. package/lib/typescript/api/commands/presentation.d.ts.map +1 -1
  448. package/lib/typescript/api/events/navigationEvents.d.ts +3 -3
  449. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  450. package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
  451. package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
  452. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
  453. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
  454. package/lib/typescript/api/index.d.ts +1 -1
  455. package/lib/typescript/api/index.d.ts.map +1 -1
  456. package/lib/typescript/api/linking/dispatchedLinks.d.ts +7 -0
  457. package/lib/typescript/api/linking/dispatchedLinks.d.ts.map +1 -0
  458. package/lib/typescript/api/linking/linking.d.ts +8 -0
  459. package/lib/typescript/api/linking/linking.d.ts.map +1 -1
  460. package/lib/typescript/api/navigation.d.ts +4 -1
  461. package/lib/typescript/api/navigation.d.ts.map +1 -1
  462. package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
  463. package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
  464. package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
  465. package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
  466. package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
  467. package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
  468. package/lib/typescript/api/theme/theme.d.ts +2 -0
  469. package/lib/typescript/api/theme/theme.d.ts.map +1 -1
  470. package/lib/typescript/engine/commands.d.ts +5 -0
  471. package/lib/typescript/engine/commands.d.ts.map +1 -1
  472. package/lib/typescript/engine/layout.d.ts.map +1 -1
  473. package/lib/typescript/engine/nativeScreens.d.ts +5 -0
  474. package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
  475. package/lib/typescript/engine/options.d.ts +5 -2
  476. package/lib/typescript/engine/options.d.ts.map +1 -1
  477. package/lib/typescript/engine/optionsChange.d.ts +28 -0
  478. package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
  479. package/lib/typescript/engine/places.d.ts +7 -2
  480. package/lib/typescript/engine/places.d.ts.map +1 -1
  481. package/lib/typescript/engine/props.d.ts +8 -1
  482. package/lib/typescript/engine/props.d.ts.map +1 -1
  483. package/lib/typescript/engine/refresh.d.ts +35 -0
  484. package/lib/typescript/engine/refresh.d.ts.map +1 -0
  485. package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
  486. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  487. package/lib/typescript/engine/screens.d.ts +27 -6
  488. package/lib/typescript/engine/screens.d.ts.map +1 -1
  489. package/lib/typescript/interfaces/ComponentEvents.d.ts +4 -4
  490. package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
  491. package/lib/typescript/interfaces/Events.d.ts +28 -10
  492. package/lib/typescript/interfaces/Events.d.ts.map +1 -1
  493. package/lib/typescript/interfaces/Options.d.ts +1 -1
  494. package/lib/typescript/interfaces/Options.d.ts.map +1 -1
  495. package/lib/typescript/interfaces/options/animations.d.ts +80 -22
  496. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  497. package/lib/typescript/interfaces/options/bottomTabs.d.ts +21 -7
  498. package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
  499. package/lib/typescript/interfaces/options/buttons.d.ts +23 -15
  500. package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
  501. package/lib/typescript/interfaces/options/modals.d.ts +34 -26
  502. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  503. package/lib/typescript/interfaces/options/sideMenu.d.ts +23 -6
  504. package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
  505. package/lib/typescript/interfaces/options/stack.d.ts +17 -3
  506. package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
  507. package/lib/typescript/interfaces/options/system.d.ts +18 -11
  508. package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
  509. package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
  510. package/lib/typescript/interfaces/options/topBar.d.ts +68 -38
  511. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  512. package/metro/acceptPlugin.js +57 -0
  513. package/metro/babelTransformer.js +45 -0
  514. package/metro/index.js +36 -0
  515. package/package.json +3 -1
  516. package/src/ARCHITECTURE.md +40 -27
  517. package/src/adapters/NativeANNTurboModule.ts +12 -2
  518. package/src/api/app/AppRegistry.ts +16 -2
  519. package/src/api/app/createNavigation.ts +203 -69
  520. package/src/api/commands/commands.ts +48 -7
  521. package/src/api/commands/presentation.ts +4 -3
  522. package/src/api/events/navigationEvents.ts +10 -4
  523. package/src/api/hooks/useNavigation.ts +9 -4
  524. package/src/api/hooks/useSafeAreaInsets.ts +4 -3
  525. package/src/api/index.ts +1 -0
  526. package/src/api/linking/dispatchedLinks.ts +18 -0
  527. package/src/api/linking/linking.ts +22 -1
  528. package/src/api/navigation.ts +4 -1
  529. package/src/api/options/OptionsChain.ts +2 -2
  530. package/src/api/screens/defineScreen.ts +49 -5
  531. package/src/api/screens/nativeScreens.ts +40 -4
  532. package/src/api/screens/registerScreens.tsx +185 -42
  533. package/src/api/theme/theme.ts +5 -0
  534. package/src/engine/commands.ts +70 -13
  535. package/src/engine/layout.ts +27 -11
  536. package/src/engine/nativeScreens.ts +10 -1
  537. package/src/engine/options.ts +45 -19
  538. package/src/engine/optionsChange.ts +107 -0
  539. package/src/engine/places.ts +65 -7
  540. package/src/engine/props.ts +30 -1
  541. package/src/engine/refresh.ts +97 -0
  542. package/src/engine/rememberedOptions.ts +74 -1
  543. package/src/engine/screens.tsx +76 -21
  544. package/src/interfaces/ComponentEvents.ts +4 -4
  545. package/src/interfaces/Events.ts +29 -10
  546. package/src/interfaces/Options.ts +1 -0
  547. package/src/interfaces/contract/commands.json +101 -0
  548. package/src/interfaces/contract/errors.json +275 -0
  549. package/src/interfaces/options/animations.ts +81 -22
  550. package/src/interfaces/options/bottomTabs.ts +21 -7
  551. package/src/interfaces/options/buttons.ts +23 -15
  552. package/src/interfaces/options/modals.ts +34 -26
  553. package/src/interfaces/options/sideMenu.ts +24 -6
  554. package/src/interfaces/options/stack.ts +17 -3
  555. package/src/interfaces/options/system.ts +18 -11
  556. package/src/interfaces/options/toolbar.ts +2 -2
  557. package/src/interfaces/options/topBar.ts +68 -38
  558. package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -41
  559. package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -69
  560. package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationStateReporter.kt +0 -62
  561. package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
  562. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
  563. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
  564. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -138
  565. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -88
  566. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
  567. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -66
  568. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +0 -179
  569. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -139
  570. package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
  571. package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -68
  572. package/ios/Layouts/Tabs/ANNTabsPreloadWindow.h +0 -27
  573. package/ios/Layouts/Tabs/ANNTabsPreloadWindow.mm +0 -55
@@ -14,6 +14,7 @@ import { nextId } from './ids';
14
14
  import { showNativeScreen } from './nativeScreens';
15
15
  import { toNativeOptions } from './options';
16
16
  import { setProps } from './props';
17
+ import { noteOwnOptions } from './rememberedOptions';
17
18
  import { getStartInfo } from './native';
18
19
  import { noteAlone } from './places';
19
20
  import {
@@ -103,6 +104,13 @@ function screen(component: LayoutComponent, inPair = false, keyed = !inPair): La
103
104
  if (nativeName) {
104
105
  // A native screen: its props go to the native side as data, its own options over those of the show.
105
106
  const options = takeButtonHandlers(id, component.options ?? {});
107
+ if (__DEV__) {
108
+ noteOwnOptions(
109
+ id,
110
+ name,
111
+ mergeOptions(screenOptions(name, props ?? {}), component.options ?? {})
112
+ );
113
+ }
106
114
  const node = external({
107
115
  id,
108
116
  name: nativeName,
@@ -113,8 +121,9 @@ function screen(component: LayoutComponent, inPair = false, keyed = !inPair): La
113
121
  }
114
122
  // The screen's own options under those of the show (a command has them there already: merging again changes
115
123
  // nothing).
116
- const own = screenOptions(name, props ?? {});
117
- const options = takeButtonHandlers(id, mergeOptions(own, component.options ?? {}));
124
+ const own = mergeOptions(screenOptions(name, props ?? {}), component.options ?? {});
125
+ noteOwnOptions(id, name, own);
126
+ const options = takeButtonHandlers(id, own);
118
127
  const key = keyed ? screenKey(name, props ?? {}) : undefined;
119
128
  setProps(id, props);
120
129
  return {
@@ -122,7 +131,7 @@ function screen(component: LayoutComponent, inPair = false, keyed = !inPair): La
122
131
  type: 'Component',
123
132
  data: {
124
133
  name: rendersInSlot(name) ? slotName(id, name) : name,
125
- options: toNativeOptions(options),
134
+ options: toNativeOptions(options, id),
126
135
  ...(key !== undefined && { key }),
127
136
  },
128
137
  children: [],
@@ -167,12 +176,13 @@ function arrangement(
167
176
  }
168
177
 
169
178
  function external(component: ExternalComponent): LayoutNode {
179
+ const id = component.id || nextId('ExternalComponent');
170
180
  return {
171
- id: component.id || nextId('ExternalComponent'),
181
+ id,
172
182
  type: 'ExternalComponent',
173
183
  data: {
174
184
  name: String(component.name),
175
- options: toNativeOptions(component.options),
185
+ options: toNativeOptions(component.options, id),
176
186
  passProps: component.passProps,
177
187
  },
178
188
  children: [],
@@ -185,10 +195,11 @@ function parent(
185
195
  options: Options | undefined,
186
196
  children: Layout[] = []
187
197
  ): LayoutNode {
198
+ const nodeId = id || nextId(type);
188
199
  return {
189
- id: id || nextId(type),
200
+ id: nodeId,
190
201
  type,
191
- data: { options: toNativeOptions(options) },
202
+ data: { options: toNativeOptions(options, nodeId) },
192
203
  children: children.map(toNativeLayout),
193
204
  };
194
205
  }
@@ -213,10 +224,11 @@ function sideMenu(menu: LayoutSideMenu): LayoutNode {
213
224
  data: {},
214
225
  children: [toNativeLayout(content)],
215
226
  });
227
+ const id = menu.id || nextId('SideMenuRoot');
216
228
  return {
217
- id: menu.id || nextId('SideMenuRoot'),
229
+ id,
218
230
  type: 'SideMenuRoot',
219
- data: { options: toNativeOptions(menu.options) },
231
+ data: { options: toNativeOptions(menu.options, id) },
220
232
  children: [
221
233
  ...(menu.left ? [side('SideMenuLeft', menu.left)] : []),
222
234
  side('SideMenuCenter', menu.center),
@@ -238,10 +250,14 @@ function splitView(split: LayoutSplitView): LayoutNode {
238
250
  ] as [string, Layout | undefined][]
239
251
  ).filter((column): column is [string, Layout] => column[1] !== undefined);
240
252
  const present = split.master && split.detail ? columns : [];
253
+ const id = split.id || nextId('SplitView');
241
254
  return {
242
- id: split.id || nextId('SplitView'),
255
+ id,
243
256
  type: 'SplitView',
244
- data: { options: toNativeOptions(split.options), columns: present.map(([column]) => column) },
257
+ data: {
258
+ options: toNativeOptions(split.options, id),
259
+ columns: present.map(([column]) => column),
260
+ },
245
261
  children: present.map(([, content]) => toNativeLayout(content)),
246
262
  };
247
263
  }
@@ -38,6 +38,8 @@ interface NativeProps {
38
38
  interface OpenScreen {
39
39
  id: string;
40
40
  name: string;
41
+ /** The props the command gave, as they are. */
42
+ given: object;
41
43
  props: NativeProps;
42
44
  /** The data the native side has, and the names of the functions; none before the command sends the screen. */
43
45
  sent?: { data: Map<string, string>; functions: string };
@@ -78,7 +80,7 @@ export const nativeScreenHooks = {
78
80
  * screen with `useProps` starts its hook, and its props go in once the hook has returned (`whenNativeScreensReady`).
79
81
  */
80
82
  export function showNativeScreen(node: LayoutNode, name: string, props: object): void {
81
- const screen: OpenScreen = { id: node.id, name, props: toNativeProps(props) };
83
+ const screen: OpenScreen = { id: node.id, name, given: props, props: toNativeProps(props) };
82
84
  const Controller = nativeScreenController(name);
83
85
  if (Controller) {
84
86
  screen.first = deferred();
@@ -135,6 +137,13 @@ export function callNativeScreenFunction(id: string, name: string, args: unknown
135
137
  return fn(...args);
136
138
  }
137
139
 
140
+ /** The native screens with the name that are open, each with the props its command gave. */
141
+ export function openNativeScreens(name: string): { id: string; props: object }[] {
142
+ return [...screens.values()]
143
+ .filter((screen) => screen.name === name)
144
+ .map((screen) => ({ id: screen.id, props: screen.given }));
145
+ }
146
+
138
147
  /** The native screen with the id, for the navigation of its calls. */
139
148
  export function nativeScreenInfo(id: string): { id: string; name: string } | undefined {
140
149
  const screen = screens.get(id);
@@ -1,17 +1,27 @@
1
- import { DynamicColorIOS, Image, Platform, processColor } from 'react-native';
1
+ import { Image, Platform, processColor } from 'react-native';
2
2
  import type { ColorValue, ImageSourcePropType } from 'react-native';
3
3
  import { nextId } from './ids';
4
- import { rememberButtonRoot, setProps } from './props';
4
+ import { rememberButtonRoot, rememberOptionsComponent, setProps } from './props';
5
5
 
6
6
  type Fields = Record<string, unknown>;
7
7
 
8
8
  /**
9
9
  * The options as the native side reads them: colors in the format of the platform, images resolved, and every
10
10
  * component in them with an id of its own (its props stay in JavaScript). Options come in their own form only:
11
- * animations with `enter` and `exit`, `searchBar` and `interpolation` as objects.
11
+ * animations with `enter` and `exit`, `searchBar` and `interpolation` as objects. `owner` is the id of the node the
12
+ * options are for, or `'defaultOptions'`: a component without an id of its own gets one made of the owner, its place
13
+ * in the options and its name, the same for every `mergeOptions` of the node, so the native side keeps the mounted
14
+ * component and only its props change. Without an owner each conversion gives it a new one.
12
15
  */
13
- export function toNativeOptions<T>(options: T): T {
14
- return convert(options) as T;
16
+ export function toNativeOptions<T>(options: T, owner?: string): T {
17
+ return convert(options, undefined, { owner, path: '' }) as T;
18
+ }
19
+
20
+ // Where a value is in the options being converted: the node they are for, and the path to the value
21
+ // (`topBar.rightButtons[0].component`).
22
+ interface Place {
23
+ owner: string | undefined;
24
+ path: string;
15
25
  }
16
26
 
17
27
  /**
@@ -35,11 +45,16 @@ function isGroup(value: unknown): value is Fields {
35
45
  return isFields(value) && !Array.isArray(value);
36
46
  }
37
47
 
38
- function convert(value: unknown, key?: string): unknown {
48
+ function convert(value: unknown, key: string | undefined, place: Place): unknown {
39
49
  if (key !== undefined && isColor(key)) return nativeColor(value);
40
50
  if (key !== undefined && isImage(key)) return nativeImage(value);
41
51
  if (Array.isArray(value)) {
42
- const items = value.map((item) => convert(item));
52
+ // An item is known by its id where it has one (a button, a suggestion), so a component keeps its root when the
53
+ // list around it changes; by its place in the list otherwise.
54
+ const items = value.map((item, index) => {
55
+ const id = isFields(item) && typeof item.id === 'string' && item.id ? item.id : index;
56
+ return convert(item, undefined, { ...place, path: `${place.path}[${id}]` });
57
+ });
43
58
  if (key?.endsWith('Buttons') || key === 'buttons') items.forEach(noteButtonRoot);
44
59
  return key?.endsWith('Buttons') ? items.map(takeButtonProps) : items;
45
60
  }
@@ -47,9 +62,12 @@ function convert(value: unknown, key?: string): unknown {
47
62
  const result: Fields = {};
48
63
  for (const [name, item] of Object.entries(withSymbols(value))) {
49
64
  // The props of a component are its own, not options.
50
- result[name] = name === 'passProps' ? item : convert(item, name);
65
+ const path = place.path ? `${place.path}.${name}` : name;
66
+ result[name] = name === 'passProps' ? item : convert(item, name, { ...place, path });
51
67
  }
52
- return key === 'component' || key === 'resultsComponent' ? takeComponentProps(result) : result;
68
+ return key === 'component' || key === 'resultsComponent'
69
+ ? takeComponentProps(result, place)
70
+ : result;
53
71
  }
54
72
 
55
73
  function isColor(key: string): boolean {
@@ -105,13 +123,23 @@ function isFields(value: unknown): value is Fields {
105
123
  return typeof value === 'object' && value !== null;
106
124
  }
107
125
 
108
- // A component in the options gets an id; the native side creates it with that id, and its props wait here.
109
- function takeComponentProps({ passProps, ...component }: Fields): Fields {
110
- const componentId = (component.id as string | undefined) || nextId('CustomComponent');
126
+ // A component in the options gets an id; the native side creates it with that id, and its props wait here. Without
127
+ // an id of its own it is the owner's, the place of the component (`topBar.title`, `topBar.rightButtons[Save]` by the
128
+ // button's id, `topBar.searchBar.resultsComponent`) and its name: another name there is another component.
129
+ function takeComponentProps({ passProps, ...component }: Fields, place: Place): Fields {
130
+ const componentId = (component.id as string | undefined) || componentIdAt(place, component.name);
111
131
  if (passProps) setProps(componentId, passProps as object);
112
132
  return { ...component, componentId };
113
133
  }
114
134
 
135
+ function componentIdAt({ owner, path }: Place, name: unknown): string {
136
+ if (owner === undefined) return nextId('CustomComponent');
137
+ const at = path.endsWith('.component') ? path.slice(0, -'.component'.length) : path;
138
+ const id = `${owner}/${at}/${String(name)}`;
139
+ rememberOptionsComponent(owner, id);
140
+ return id;
141
+ }
142
+
115
143
  // The component of a bar button (the top bar's `*Buttons`, the toolbar's `buttons`) is a root the native side only
116
144
  // sizes: its wrapper centers the content in the button's slot.
117
145
  function noteButtonRoot(button: unknown): void {
@@ -134,19 +162,17 @@ function nativeColor(value: unknown): unknown {
134
162
  return Platform.OS === 'ios' ? iosColor(value) : androidColor(value);
135
163
  }
136
164
 
137
- // One color, a pair for the light and the dark appearance, or a semantic color; null takes the color away.
165
+ // One color, a pair for the light and the dark appearance, or a semantic color; null takes the color away. A pair goes
166
+ // as `{ light, dark }`, an app's `DynamicColorIOS` too; a side without a color is left out and shows the platform's.
138
167
  function iosColor(value: unknown): unknown {
139
168
  if (value === null) return 'NoColor';
140
169
  if (!isFields(value)) return processColor(value as ColorValue);
141
170
  if ('semantic' in value) return value;
142
171
  const pair = ('dynamic' in value ? value.dynamic : value) as {
143
- light: ColorValue;
144
- dark: ColorValue;
172
+ light?: ColorValue;
173
+ dark?: ColorValue;
145
174
  };
146
- return DynamicColorIOS({
147
- light: processColor(pair.light) as ColorValue,
148
- dark: processColor(pair.dark) as ColorValue,
149
- });
175
+ return { light: processColor(pair.light), dark: processColor(pair.dark) };
150
176
  }
151
177
 
152
178
  // Android always takes a pair; one color goes to both, and the side without a color has none.
@@ -0,0 +1,107 @@
1
+ import type { Options } from '../interfaces/Options';
2
+
3
+ /** What an edit of a screen's options changed: the options to merge, and the paths of the options it took out. */
4
+ export interface OptionsChange {
5
+ /** The options that are new or have another value; undefined when none. */
6
+ merge?: Options;
7
+ /** The paths of the options set before and not now, such as `['topBar', 'subtitle']`. */
8
+ removed: string[][];
9
+ }
10
+
11
+ /**
12
+ * What `next` changes of `previous`, both the options of one screen. Objects compare key by key; anything else, an
13
+ * array such as buttons too, changes as a whole, and a function by identity (`sameValue`).
14
+ */
15
+ export function optionsChange(previous: Options, next: Options): OptionsChange {
16
+ const removed: string[][] = [];
17
+ const merge = changed(previous, next, [], removed) as Options | undefined;
18
+ return merge ? { merge, removed } : { removed };
19
+ }
20
+
21
+ function changed(previous: unknown, next: unknown, path: string[], removed: string[][]): unknown {
22
+ if (isWholeValue(path) && isPlainObject(previous) && isPlainObject(next)) {
23
+ if (sameValue(previous, next)) return undefined;
24
+ // A part of it taken out: the native side may merge the parts, so the value goes first.
25
+ if (Object.keys(previous).some((key) => next[key] === undefined)) removed.push(path);
26
+ return next;
27
+ }
28
+ if (!isPlainObject(previous) || !isPlainObject(next)) {
29
+ return sameValue(previous, next) ? undefined : next;
30
+ }
31
+ const result: Record<string, unknown> = {};
32
+ for (const [key, value] of Object.entries(next)) {
33
+ if (value === undefined) continue;
34
+ const change = changed(previous[key], value, [...path, key], removed);
35
+ if (change !== undefined) result[key] = change;
36
+ }
37
+ for (const [key, value] of Object.entries(previous)) {
38
+ if (value !== undefined && next[key] === undefined) removed.push([...path, key]);
39
+ }
40
+ return Object.keys(result).length > 0 ? result : undefined;
41
+ }
42
+
43
+ // A color (`{ light, dark }`), an icon or an image, and a menu are one value each on the native side, not a group: a
44
+ // part of one taken out goes as the whole value again.
45
+ function isWholeValue(path: string[]): boolean {
46
+ const key = path[path.length - 1];
47
+ return key !== undefined && /^(color|icon|image|menu)$|(Color|Icon|Image)$/.test(key);
48
+ }
49
+
50
+ /**
51
+ * Whether two values are the same: plain objects key by key, arrays item by item, anything else, functions too, by
52
+ * identity. A module that ran again makes its functions anew, so they count as changed: Hermes keeps no source to tell
53
+ * an edited one, and sending a button's handler again costs little. With `functionsAlike` any two functions are alike.
54
+ */
55
+ export function sameValue(a: unknown, b: unknown, functionsAlike = false): boolean {
56
+ if (functionsAlike && typeof a === 'function' && typeof b === 'function') return true;
57
+ if (Array.isArray(a) && Array.isArray(b)) {
58
+ return (
59
+ a.length === b.length && a.every((item, index) => sameValue(item, b[index], functionsAlike))
60
+ );
61
+ }
62
+ if (isPlainObject(a) && isPlainObject(b)) {
63
+ const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
64
+ return [...keys].every((key) => sameValue(a[key], b[key], functionsAlike));
65
+ }
66
+ return Object.is(a, b);
67
+ }
68
+
69
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
70
+ if (typeof value !== 'object' || value === null) return false;
71
+ const prototype = Object.getPrototypeOf(value);
72
+ return prototype === Object.prototype || prototype === null;
73
+ }
74
+
75
+ /**
76
+ * The part of an edit's change that the screen shows the old definition's value of: where the command that showed it,
77
+ * or a merge since, set another value, that value stays. `before` is what the old definition gave, `own` the screen's
78
+ * own options now.
79
+ */
80
+ export function changeOverOwn(change: OptionsChange, before: Options, own: Options): OptionsChange {
81
+ // The functions of the definition are made anew each time it is read (a chain's button handlers): any function
82
+ // there stands for the definition's.
83
+ const fromDefinition = (path: string[]) =>
84
+ sameValue(valueAt(own, path), valueAt(before, path), true);
85
+ const removed = change.removed.filter(fromDefinition);
86
+ const merge =
87
+ change.merge && (keptWhere(change.merge, [], fromDefinition) as Options | undefined);
88
+ return merge ? { merge, removed } : { removed };
89
+ }
90
+
91
+ function keptWhere(value: unknown, path: string[], keep: (path: string[]) => boolean): unknown {
92
+ if (!isPlainObject(value) || isWholeValue(path)) return keep(path) ? value : undefined;
93
+ const result: Record<string, unknown> = {};
94
+ for (const [key, item] of Object.entries(value)) {
95
+ const kept = keptWhere(item, [...path, key], keep);
96
+ if (kept !== undefined) result[key] = kept;
97
+ }
98
+ return Object.keys(result).length > 0 ? result : undefined;
99
+ }
100
+
101
+ /** The value at a path of options, such as `['topBar', 'title', 'text']`; undefined where there is none. */
102
+ export function valueAt(options: unknown, path: readonly string[]): unknown {
103
+ return path.reduce<unknown>(
104
+ (value, key) => (isPlainObject(value) ? value[key] : undefined),
105
+ options
106
+ );
107
+ }
@@ -1,6 +1,7 @@
1
1
  import type { SplitViewColumnsStackedEvent } from '../interfaces/Events';
2
2
  import type { LayoutNode } from './layout';
3
3
  import { onNativeEvent } from './native';
4
+ import { forgetProps } from './props';
4
5
  import { screenNameOf } from './slots';
5
6
 
6
7
  // Where each screen is, noted as the engine builds the layouts of the commands: what it shows in and its place in its
@@ -51,6 +52,11 @@ const stackedSplits = new Set<string>();
51
52
  let listeningToSplitViews = false;
52
53
  // The primaries of pairs shown alone, noted as their node is built, until their place is noted.
53
54
  const alone = new Set<string>();
55
+ // The layouts each screen shows in (its stack, pair, tabs, menu, split view, and the root of its layer), and how many
56
+ // live screens each layout holds. A layout whose last screen went is gone, and with it the props of the components the
57
+ // library named in its options: they stay as long as any of its screens may show them again.
58
+ const layoutsOfScreen = new Map<string, readonly string[]>();
59
+ const screensInLayout = new Map<string, number>();
54
60
  // The last screens that went, so a command from one of them can say so; a screen the engine never saw is not one.
55
61
  const gone = new Set<string>();
56
62
  const GONE_KEPT = 200;
@@ -59,6 +65,15 @@ export function placeOf(screenId: string): ScreenPlace | undefined {
59
65
  return places.get(screenId);
60
66
  }
61
67
 
68
+ /** The first screen of a stack, as the engine noted it; with a pair first, its primary. */
69
+ export function firstScreenOf(stack: string): string | undefined {
70
+ for (const [id, place] of places) {
71
+ if (place.stack === stack && place.index === 0 && place.arrangement?.placement !== 'secondary')
72
+ return id;
73
+ }
74
+ return undefined;
75
+ }
76
+
62
77
  /** Notes that a screen is the primary of a pair shown alone, where the native side shows no pairs. */
63
78
  export function noteAlone(screenId: string): void {
64
79
  alone.add(screenId);
@@ -69,9 +84,15 @@ export function isGone(screenId: string): boolean {
69
84
  return gone.has(screenId);
70
85
  }
71
86
 
87
+ /** The layouts the screen shows in, outermost first: those whose screens come with it in a command go in it too. */
88
+ export function layoutsOf(screenId: string): readonly string[] {
89
+ return layoutsOfScreen.get(screenId) ?? [];
90
+ }
91
+
72
92
  /**
73
93
  * Notes the screens of a layout, the first of a stack at `index`. A modal or an overlay given as a whole is a layer of
74
- * its own; screens added to one, such as a push, take the layer of the screen they come from.
94
+ * its own; screens added to one, such as a push, take the layer of the screen they come from, and `within`, the
95
+ * layouts they show in (`layoutsOf` that screen).
75
96
  */
76
97
  export function notePlaces(
77
98
  node: LayoutNode,
@@ -79,12 +100,14 @@ export function notePlaces(
79
100
  stack: string | null = null,
80
101
  index = 0,
81
102
  menu?: ScreenPlace['menu'],
82
- layer: string = container === 'root' ? 'root' : node.id
103
+ layer: string = container === 'root' ? 'root' : node.id,
104
+ within: readonly string[] = []
83
105
  ): void {
84
106
  if (node.type === 'Component' || node.type === 'ExternalComponent') {
85
107
  const name = node.data.name ?? '';
86
108
  gone.delete(node.id);
87
109
  columnOfScreen.delete(node.id);
110
+ noteLayouts(node.id, within);
88
111
  places.set(node.id, {
89
112
  name: screenNameOf(name),
90
113
  container,
@@ -99,7 +122,7 @@ export function notePlaces(
99
122
  } else if (node.type === 'Arrangement') {
100
123
  // One entry of its stack: both screens have its place there, and each knows the pair.
101
124
  node.children.forEach((child, childIndex) => {
102
- notePlaces(child, container, stack, index, menu, layer);
125
+ notePlaces(child, container, stack, index, menu, layer, [...within, node.id]);
103
126
  const place = places.get(child.id);
104
127
  if (place) {
105
128
  const placement = childIndex === 0 ? 'primary' : 'secondary';
@@ -109,7 +132,7 @@ export function notePlaces(
109
132
  } else if (node.type === 'Stack') {
110
133
  columnOfStack.delete(node.id);
111
134
  node.children.forEach((child, childIndex) =>
112
- notePlaces(child, container, node.id, childIndex, menu, layer)
135
+ notePlaces(child, container, node.id, childIndex, menu, layer, [...within, node.id])
113
136
  );
114
137
  } else if (node.type === 'SplitView') {
115
138
  // A split view sent again with its id starts anew: its columns are noted again, and it isn't one stack until the
@@ -118,7 +141,7 @@ export function notePlaces(
118
141
  const columns = node.data.columns ?? [];
119
142
  const stacks: Partial<Record<string, string>> = {};
120
143
  node.children.forEach((child, childIndex) => {
121
- notePlaces(child, container, null, 0, menu, layer);
144
+ notePlaces(child, container, null, 0, menu, layer, [...within, node.id]);
122
145
  const column = columns[childIndex];
123
146
  if (!column) return;
124
147
  if (child.type === 'Stack') stacks[column] = child.id;
@@ -128,12 +151,46 @@ export function notePlaces(
128
151
  } else if (node.type === 'SideMenuRoot') {
129
152
  for (const part of node.children) {
130
153
  const side = MENU_PARTS[part.type];
154
+ const parts = [...within, node.id, part.id];
131
155
  part.children.forEach((child) =>
132
- notePlaces(child, container, null, 0, side ? { id: node.id, part: side } : menu, layer)
156
+ notePlaces(
157
+ child,
158
+ container,
159
+ null,
160
+ 0,
161
+ side ? { id: node.id, part: side } : menu,
162
+ layer,
163
+ parts
164
+ )
133
165
  );
134
166
  }
135
167
  } else {
136
- node.children.forEach((child) => notePlaces(child, container, null, 0, menu, layer));
168
+ node.children.forEach((child) =>
169
+ notePlaces(child, container, null, 0, menu, layer, [...within, node.id])
170
+ );
171
+ }
172
+ }
173
+
174
+ // A screen noted again under its id (a layout sent again) leaves the layouts it was in before.
175
+ function noteLayouts(screenId: string, within: readonly string[]): void {
176
+ leaveLayouts(screenId);
177
+ const layouts = [...new Set(within)];
178
+ layoutsOfScreen.set(screenId, layouts);
179
+ for (const layout of layouts) screensInLayout.set(layout, (screensInLayout.get(layout) ?? 0) + 1);
180
+ }
181
+
182
+ function leaveLayouts(screenId: string): void {
183
+ const layouts = layoutsOfScreen.get(screenId);
184
+ if (!layouts) return;
185
+ layoutsOfScreen.delete(screenId);
186
+ for (const layout of layouts) {
187
+ const count = (screensInLayout.get(layout) ?? 1) - 1;
188
+ if (count > 0) {
189
+ screensInLayout.set(layout, count);
190
+ continue;
191
+ }
192
+ screensInLayout.delete(layout);
193
+ forgetProps(layout);
137
194
  }
138
195
  }
139
196
 
@@ -198,6 +255,7 @@ export function listenToSplitViews(): void {
198
255
 
199
256
  /** The screen is gone. */
200
257
  export function forgetPlace(screenId: string): void {
258
+ leaveLayouts(screenId);
201
259
  places.delete(screenId);
202
260
  columnOfScreen.delete(screenId);
203
261
  gone.add(screenId);
@@ -7,6 +7,11 @@ const watchers = new Map<string, Set<() => void>>();
7
7
  const none = {};
8
8
  // The roots that are the components of bar buttons: their wrapper centers the content in the button's slot.
9
9
  const buttonRoots = new Set<string>();
10
+ // The ids the library gave components in options, by the node whose options they are in. Such an id stays the same
11
+ // for every show of the node and may have several roots at once (a component of the default or a stack's options):
12
+ // its props go with the node, not with a root.
13
+ const optionComponents = new Map<string, Set<string>>();
14
+ const optionComponentIds = new Set<string>();
10
15
 
11
16
  export function setProps(id: string, props: object | undefined): void {
12
17
  if (props === undefined) byId.delete(id);
@@ -38,8 +43,32 @@ export function isButtonRoot(id: string): boolean {
38
43
  return buttonRoots.has(id);
39
44
  }
40
45
 
41
- /** The view is gone: its props, and whether it is a button's root, are not needed again. */
46
+ /** Notes an id the library gave a component in the options of the node `owner`. */
47
+ export function rememberOptionsComponent(owner: string, id: string): void {
48
+ let ids = optionComponents.get(owner);
49
+ if (!ids) optionComponents.set(owner, (ids = new Set()));
50
+ ids.add(id);
51
+ optionComponentIds.add(id);
52
+ }
53
+
54
+ /** Whether the library gave the id to a component in options: its props go with the node, not with a root. */
55
+ export function isOptionsComponent(id: string): boolean {
56
+ return optionComponentIds.has(id);
57
+ }
58
+
59
+ /**
60
+ * The view is gone: its props, and whether it is a button's root, are not needed again; nor are those of the
61
+ * components the library named in its options.
62
+ */
42
63
  export function forgetProps(id: string): void {
43
64
  byId.delete(id);
44
65
  buttonRoots.delete(id);
66
+ const ids = optionComponents.get(id);
67
+ if (!ids) return;
68
+ optionComponents.delete(id);
69
+ for (const component of ids) {
70
+ byId.delete(component);
71
+ buttonRoots.delete(component);
72
+ optionComponentIds.delete(component);
73
+ }
45
74
  }
@@ -0,0 +1,97 @@
1
+ // Fast Refresh runs an edited module again, and every component it exports is a new function. React Refresh keeps
2
+ // the versions of one component in a family: Metro registers each export under its module and name, and React renders
3
+ // the newest version of the family in place of the old one. What the library keeps of a screen (its options, its key,
4
+ // its pair) is read through the same family, so an edit reaches it without a reload.
5
+
6
+ /** The versions of one component React Refresh keeps, `current` the newest. */
7
+ interface Family {
8
+ current: unknown;
9
+ }
10
+
11
+ interface ReactRefresh {
12
+ getFamilyByType?: (type: unknown) => Family | undefined;
13
+ register?: (type: unknown, id: string) => void;
14
+ createSignatureFunctionForTransform?: () => (
15
+ type: unknown,
16
+ key: string,
17
+ forceReset?: boolean
18
+ ) => unknown;
19
+ }
20
+
21
+ // React Native puts React Refresh where Metro's module system finds it, in development only.
22
+ function reactRefresh(): ReactRefresh | undefined {
23
+ if (!__DEV__) return undefined;
24
+ const global = globalThis as { __METRO_GLOBAL_PREFIX__?: string } & Record<string, unknown>;
25
+ return global[`${global.__METRO_GLOBAL_PREFIX__ ?? ''}__ReactRefresh`] as
26
+ ReactRefresh | undefined;
27
+ }
28
+
29
+ /** Whether React Refresh runs: development with Metro. */
30
+ export function refreshRuns(): boolean {
31
+ return reactRefresh()?.getFamilyByType !== undefined;
32
+ }
33
+
34
+ /** The family a component's versions are kept in; undefined in a release build or for a value Metro didn't export. */
35
+ export function familyOf(type: unknown): Family | undefined {
36
+ return reactRefresh()?.getFamilyByType?.(type);
37
+ }
38
+
39
+ /**
40
+ * The newest version of a component after Fast Refresh ran its module again; the component itself when there is none
41
+ * newer, and in a release build.
42
+ */
43
+ export function newestType<T extends object>(type: T): T {
44
+ return (familyOf(type)?.current as T | undefined) ?? type;
45
+ }
46
+
47
+ let redefinedListener: ((screen: object) => void) | undefined;
48
+
49
+ /** Sets what runs when a module makes a screen again: Fast Refresh ran it after an edit. */
50
+ export function onRedefined(listener: (screen: object) => void): void {
51
+ redefinedListener = listener;
52
+ }
53
+
54
+ /**
55
+ * Tells about a screen a module made, once the module ran: Metro registers its exports after it, so the family of the
56
+ * screen is known by then, and a new version of a screen that shows is found by it. Development only.
57
+ */
58
+ export function redefined(screen: object): void {
59
+ if (!__DEV__ || !redefinedListener) return;
60
+ const listener = redefinedListener;
61
+ Promise.resolve().then(() => listener(screen));
62
+ }
63
+
64
+ // The id each family of screens gives the components they render, so their versions join one family too.
65
+ const innerIds = new WeakMap<Family, string>();
66
+ let innerCount = 0;
67
+
68
+ /**
69
+ * Puts a component a screen or the app renders (`owner`: the screen, or the app made by createNavigation; `role`: which
70
+ * of its components) in a family of its own, once the owner's module ran and Metro has given the owner its family: the
71
+ * next version of the owner registers its component under the same id, so React Refresh keeps the component's state
72
+ * when the module runs again, also when the module doesn't export it. A component React Refresh knows already (its
73
+ * module exports it, or Babel registered it) keeps its own family. Development only.
74
+ */
75
+ export function keepStateOf(owner: object, component: unknown, role = 'component'): void {
76
+ const refresh = reactRefresh();
77
+ if (!refresh?.register || component === undefined) return;
78
+ const register = refresh.register;
79
+ Promise.resolve().then(() => {
80
+ const family = familyOf(owner);
81
+ if (!family) return;
82
+ let id = innerIds.get(family);
83
+ if (id === undefined) {
84
+ id = `apexnative.navigation#${++innerCount}`;
85
+ innerIds.set(family, id);
86
+ }
87
+ register(component, `${id}.${role}`);
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Makes React Refresh mount a component of the library again, not keep its state, when a new version of it comes: its
93
+ * hooks are the app's, which may have changed, and the library's code has no signature of them. Development only.
94
+ */
95
+ export function resetOnEdit(type: object): void {
96
+ reactRefresh()?.createSignatureFunctionForTransform?.()(type, 'apexnative.navigation', true);
97
+ }