@apexnative/navigation 1.0.0-alpha.1 → 1.0.0-alpha.11

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 (581) hide show
  1. package/ApexNavigation.podspec +2 -1
  2. package/CHANGELOG.md +132 -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/ArrangementDefinition.kt +9 -3
  67. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementPlanner.kt +20 -15
  68. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
  69. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
  70. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
  71. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +108 -34
  72. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
  73. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
  74. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt +3 -3
  75. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
  76. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
  77. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +12 -9
  78. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +2 -2
  79. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
  80. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
  81. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
  82. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
  83. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
  84. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalAnimations.kt +22 -24
  85. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +109 -21
  86. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalRecord.kt +21 -6
  87. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +476 -73
  88. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +57 -25
  89. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
  90. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +83 -0
  91. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackCheck.kt +50 -0
  92. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +49 -14
  93. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +71 -12
  94. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +37 -11
  95. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +10 -8
  96. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
  97. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ContainerBackground.kt +3 -3
  98. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +6 -6
  99. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +42 -9
  100. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +19 -8
  101. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuPresenter.kt +16 -7
  102. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuRules.kt +43 -4
  103. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
  104. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +77 -17
  105. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +315 -0
  106. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
  107. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +30 -20
  108. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +216 -18
  109. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +26 -1
  110. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +149 -41
  111. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt +26 -8
  112. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +115 -6
  113. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
  114. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +222 -48
  115. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt +3 -5
  116. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +30 -34
  117. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarComponents.kt +5 -51
  118. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
  119. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
  120. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
  121. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +219 -54
  122. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarVisibility.kt +9 -2
  123. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
  124. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +75 -21
  125. package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
  126. package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
  127. package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
  128. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +9 -7
  129. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
  130. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
  131. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
  132. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStep.kt +31 -0
  133. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +143 -135
  134. package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +13 -5
  135. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
  136. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionTargets.kt +9 -6
  137. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionViews.kt +100 -0
  138. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +112 -4
  139. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +346 -36
  140. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
  141. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +56 -41
  142. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextMorph.kt +176 -0
  143. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
  144. package/android/src/main/java/dev/apexnative/navigation/views/sidemenu/SideMenuView.kt +12 -3
  145. package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +45 -0
  146. package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +140 -1
  147. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
  148. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
  149. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
  150. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -17
  151. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +73 -34
  152. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
  153. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
  154. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
  155. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +125 -178
  156. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
  157. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
  158. package/android/src/main/jni/ANNContentHeights.cpp +82 -0
  159. package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
  160. package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
  161. package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
  162. package/android/src/main/res/values/styles.xml +1 -1
  163. package/android/src/main/res/values-night/styles.xml +1 -1
  164. package/cli/app.js +1 -1
  165. package/cli/doctor/checks/app.js +77 -14
  166. package/cli/doctor/checks/splash.js +4 -22
  167. package/cli/migrate/native.js +18 -0
  168. package/cli/project.js +10 -2
  169. package/cli/setup/index.js +18 -0
  170. package/cli/setup/source.js +2 -1
  171. package/cli/setup/steps/appDelegate.js +66 -11
  172. package/cli/setup/steps/mainApplication.js +134 -19
  173. package/cli/setup/steps/metro.js +46 -0
  174. package/cli/setup/steps/sceneDelegate.js +108 -0
  175. package/cli/splash/index.js +3 -1
  176. package/docs-md/architecture/native-screens.md +4 -0
  177. package/docs-md/architecture/theme-and-context.md +131 -12
  178. package/docs-md/architecture/transitions.md +58 -12
  179. package/docs-md/containers/arrangement.md +17 -1
  180. package/docs-md/containers/modals.md +8 -4
  181. package/docs-md/containers/overlays.md +3 -1
  182. package/docs-md/containers/side-menu.md +19 -2
  183. package/docs-md/containers/tabs.md +5 -1
  184. package/docs-md/flows/bottom-sheets.md +4 -2
  185. package/docs-md/flows/forms-in-a-modal.md +1 -1
  186. package/docs-md/fundamentals/screens-and-props.md +9 -1
  187. package/docs-md/fundamentals/starting-the-app.md +1 -1
  188. package/docs-md/guides/cli.md +4 -3
  189. package/docs-md/guides/deep-links.md +15 -33
  190. package/docs-md/help/glossary.md +4 -4
  191. package/docs-md/help/troubleshooting.md +44 -6
  192. package/docs-md/index.json +61 -1
  193. package/docs-md/migration/from-expo-router.md +1 -1
  194. package/docs-md/migration/from-react-native-navigation.md +9 -3
  195. package/docs-md/migration/from-react-navigation.md +2 -2
  196. package/docs-md/screen-ui/back-and-gestures.md +46 -2
  197. package/docs-md/screen-ui/bar-buttons.md +5 -5
  198. package/docs-md/screen-ui/search.md +1 -1
  199. package/docs-md/screen-ui/system-ui.md +17 -12
  200. package/docs-md/screen-ui/toolbar-and-fab.md +4 -2
  201. package/docs-md/screen-ui/top-bar.md +8 -4
  202. package/docs-md/start/installation.md +21 -3
  203. package/ios/Animations/ANNFrameDriver.h +55 -15
  204. package/ios/Animations/ANNFrameDriver.mm +216 -60
  205. package/ios/Animations/ANNTransitionAnimator.h +26 -5
  206. package/ios/Animations/ANNTransitionAnimator.mm +61 -13
  207. package/ios/Animations/ANNTransitionPlan.h +2 -2
  208. package/ios/Animations/ANNTransitionPlan.mm +10 -0
  209. package/ios/Animations/ANNTransitionWait.mm +6 -7
  210. package/ios/Animations/Elements/ANNElementFinder.h +57 -0
  211. package/ios/Animations/Elements/ANNElementFinder.mm +174 -2
  212. package/ios/Animations/Elements/ANNElementTransition.h +10 -8
  213. package/ios/Animations/Elements/ANNElementTransition.mm +19 -21
  214. package/ios/Animations/Elements/ANNSharedElementFlight.h +16 -11
  215. package/ios/Animations/Elements/ANNSharedElementFlight.mm +308 -69
  216. package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
  217. package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
  218. package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
  219. package/ios/Animations/Interpolators/ANNViewTracks.h +9 -6
  220. package/ios/Animations/Interpolators/ANNViewTracks.mm +100 -30
  221. package/ios/CLAUDE.md +60 -24
  222. package/ios/CORE.md +44 -34
  223. package/ios/Contract/ANNCommandAnswer.h +13 -5
  224. package/ios/Contract/ANNCommandAnswer.mm +40 -32
  225. package/ios/Contract/ANNCommandContract.h +15 -0
  226. package/ios/Contract/ANNCommandContract.mm +78 -0
  227. package/ios/Contract/ANNCommandRunner.h +10 -9
  228. package/ios/Contract/ANNCommandRunner.mm +22 -28
  229. package/ios/Contract/ANNErrorTexts.h +6 -2
  230. package/ios/Contract/ANNErrorTexts.mm +18 -10
  231. package/ios/Contract/ANNLayoutNode.h +3 -3
  232. package/ios/Contract/ANNLayoutReader.h +3 -3
  233. package/ios/Core/ANNAppDelegate.mm +0 -2
  234. package/ios/Core/ANNAppearanceDispatcher.h +5 -4
  235. package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
  236. package/ios/Core/ANNDeepLinkQueue.h +22 -2
  237. package/ios/Core/ANNDeepLinkQueue.mm +121 -14
  238. package/ios/Core/ANNErrorHandler.h +9 -4
  239. package/ios/Core/ANNErrorHandler.mm +20 -2
  240. package/ios/Core/ANNEventContract.h +246 -0
  241. package/ios/Core/ANNEventContract.mm +225 -0
  242. package/ios/Core/ANNEventEmitter.h +6 -2
  243. package/ios/Core/ANNEventEmitter.mm +117 -80
  244. package/ios/Core/ANNHinge.h +6 -0
  245. package/ios/Core/ANNHinge.mm +15 -4
  246. package/ios/Core/ANNJavaScriptCalls.h +33 -0
  247. package/ios/Core/ANNJavaScriptCalls.mm +52 -0
  248. package/ios/Core/ANNLayoutProtocol.h +5 -5
  249. package/ios/Core/ANNNavigationState.h +7 -5
  250. package/ios/Core/ANNNavigationState.mm +8 -7
  251. package/ios/Core/ANNNodeHelper.h +32 -12
  252. package/ios/Core/ANNNodeHelper.mm +100 -3
  253. package/ios/Core/ANNParking.h +5 -4
  254. package/ios/Core/ANNReloadCoordinator.mm +5 -12
  255. package/ios/Core/ANNRenderWait.h +20 -6
  256. package/ios/Core/ANNRenderWait.mm +143 -56
  257. package/ios/Core/ANNSceneDelegate.mm +25 -15
  258. package/ios/Core/ANNSystemUI.h +3 -2
  259. package/ios/Core/ANNSystemUI.mm +7 -0
  260. package/ios/Core/ANNUtils.h +7 -0
  261. package/ios/Core/ANNUtils.mm +10 -0
  262. package/ios/Core/ApexNavigation+Internal.h +1 -1
  263. package/ios/Core/ApexNavigation.mm +7 -6
  264. package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
  265. package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
  266. package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
  267. package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
  268. package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +195 -21
  269. package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
  270. package/ios/Layouts/Modal/ANNModalCoordinator.mm +57 -16
  271. package/ios/Layouts/Modal/ANNModalPresentation.h +8 -5
  272. package/ios/Layouts/Modal/ANNModalPresentation.mm +12 -12
  273. package/ios/Layouts/Modal/ANNSheetPresenter.mm +83 -1
  274. package/ios/Layouts/Overlay/ANNOverlayCoordinator.mm +2 -8
  275. package/ios/Layouts/Root/ANNRootCoordinator.mm +58 -19
  276. package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
  277. package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
  278. package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
  279. package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
  280. package/ios/Layouts/Screen/ANNComponentViewController.h +15 -13
  281. package/ios/Layouts/Screen/ANNComponentViewController.mm +130 -75
  282. package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
  283. package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
  284. package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
  285. package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
  286. package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
  287. package/ios/Layouts/Screen/ANNScreenPlace.h +53 -0
  288. package/ios/Layouts/Screen/ANNScreenPlace.mm +205 -0
  289. package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
  290. package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -44
  291. package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
  292. package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
  293. package/ios/Layouts/Screen/ANNScreenSettling.h +34 -0
  294. package/ios/Layouts/Screen/ANNScreenSettling.mm +171 -0
  295. package/ios/Layouts/SideMenu/ANNSideMenuTypes.h +8 -0
  296. package/ios/Layouts/SideMenu/ANNSideMenuViewController.mm +83 -14
  297. package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
  298. package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
  299. package/ios/Layouts/SplitView/ANNSplitViewController.mm +7 -8
  300. package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
  301. package/ios/Layouts/Stack/ANNPreviewPush.mm +142 -27
  302. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
  303. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
  304. package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
  305. package/ios/Layouts/Stack/ANNStackController.h +8 -0
  306. package/ios/Layouts/Stack/ANNStackController.mm +184 -16
  307. package/ios/Layouts/Stack/ANNStackQueue.h +5 -2
  308. package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
  309. package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
  310. package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
  311. package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
  312. package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
  313. package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
  314. package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
  315. package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
  316. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
  317. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
  318. package/ios/Layouts/Tabs/ANNBottomTabsController.h +3 -0
  319. package/ios/Layouts/Tabs/ANNBottomTabsController.mm +237 -57
  320. package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
  321. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +6 -2
  322. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +232 -43
  323. package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
  324. package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
  325. package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
  326. package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
  327. package/ios/Options/ANNAnimationOptions.h +2 -1
  328. package/ios/Options/ANNAnimationOptions.mm +1 -1
  329. package/ios/Options/ANNBottomTabsOptions.h +2 -0
  330. package/ios/Options/ANNBottomTabsOptions.mm +1 -0
  331. package/ios/Options/ANNOptions.h +25 -0
  332. package/ios/Options/ANNOptions.mm +157 -1
  333. package/ios/Options/ANNOptionsCache.h +7 -6
  334. package/ios/Options/ANNOptionsReader.mm +108 -0
  335. package/ios/Options/ANNSearchBarOptions.h +4 -3
  336. package/ios/Options/ANNSheetOptions.h +4 -1
  337. package/ios/Options/ANNSideMenuOptions.h +2 -0
  338. package/ios/Options/ANNSideMenuOptions.mm +2 -0
  339. package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
  340. package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
  341. package/ios/Options/ANNUserChoice.h +57 -0
  342. package/ios/Options/ANNUserChoice.mm +44 -0
  343. package/ios/Options/Params/ANNImageSource.h +61 -0
  344. package/ios/Options/Params/ANNImageSource.mm +101 -0
  345. package/ios/Options/Params/ANNOptionImages.h +37 -0
  346. package/ios/Options/Params/ANNOptionImages.mm +284 -0
  347. package/ios/Options/Params/ANNParam.h +15 -0
  348. package/ios/Options/Params/ANNParam.mm +61 -0
  349. package/ios/Options/Params/Color.h +3 -0
  350. package/ios/Options/Params/Color.mm +21 -0
  351. package/ios/Options/Params/ColorParser.h +4 -3
  352. package/ios/Options/Params/ColorParser.mm +22 -4
  353. package/ios/Options/Params/Image.h +15 -2
  354. package/ios/Options/Params/Image.mm +35 -2
  355. package/ios/Options/Params/ImageParser.h +5 -5
  356. package/ios/Options/Params/ImageParser.mm +28 -46
  357. package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
  358. package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
  359. package/ios/RECIPES.md +13 -6
  360. package/ios/React/ANNReactView.mm +6 -25
  361. package/ios/React/ANNSafeAreaViewComponentView.mm +28 -17
  362. package/ios/React/ANNSurfaceHostingView.h +14 -0
  363. package/ios/React/ANNSurfaceHostingView.mm +144 -1
  364. package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
  365. package/ios/React/Slots/ANNSlotContainers.h +14 -6
  366. package/ios/React/Slots/ANNSlotContainers.mm +75 -2
  367. package/ios/React/Slots/ANNSlotHost.mm +1 -3
  368. package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
  369. package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
  370. package/ios/TopBar/ANNBarAppearance.h +2 -1
  371. package/ios/TopBar/ANNBarAppearance.mm +111 -30
  372. package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
  373. package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
  374. package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
  375. package/ios/TopBar/ANNBarButtons.h +14 -8
  376. package/ios/TopBar/ANNBarButtons.mm +397 -80
  377. package/ios/TopBar/ANNIconButtonImage.h +1 -0
  378. package/ios/TopBar/ANNIconButtonImage.mm +34 -1
  379. package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
  380. package/ios/TopBar/ANNLargeTitlePresenter.mm +50 -7
  381. package/ios/TopBar/ANNMainList.h +3 -0
  382. package/ios/TopBar/ANNMainList.mm +4 -0
  383. package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
  384. package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
  385. package/ios/TopBar/ANNSearchPresenter.mm +172 -21
  386. package/ios/TopBar/ANNSearchResultsViewController.mm +29 -6
  387. package/ios/TopBar/ANNTopBarPresenter.h +8 -2
  388. package/ios/TopBar/ANNTopBarPresenter.mm +155 -33
  389. package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
  390. package/ios/TurboModules/ANNTurboModule.mm +104 -33
  391. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
  392. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
  393. package/lib/module/ARCHITECTURE.md +40 -27
  394. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  395. package/lib/module/api/app/AppRegistry.js +14 -2
  396. package/lib/module/api/app/AppRegistry.js.map +1 -1
  397. package/lib/module/api/app/createNavigation.js +190 -66
  398. package/lib/module/api/app/createNavigation.js.map +1 -1
  399. package/lib/module/api/commands/commands.js +54 -6
  400. package/lib/module/api/commands/commands.js.map +1 -1
  401. package/lib/module/api/events/navigationEvents.js +6 -2
  402. package/lib/module/api/events/navigationEvents.js.map +1 -1
  403. package/lib/module/api/hooks/useNavigation.js +8 -3
  404. package/lib/module/api/hooks/useNavigation.js.map +1 -1
  405. package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
  406. package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
  407. package/lib/module/api/linking/dispatchedLinks.js +23 -0
  408. package/lib/module/api/linking/dispatchedLinks.js.map +1 -0
  409. package/lib/module/api/linking/linking.js +11 -0
  410. package/lib/module/api/linking/linking.js.map +1 -1
  411. package/lib/module/api/navigation.js.map +1 -1
  412. package/lib/module/api/options/OptionsChain.js +2 -2
  413. package/lib/module/api/screens/arrangement.js +5 -2
  414. package/lib/module/api/screens/arrangement.js.map +1 -1
  415. package/lib/module/api/screens/defineScreen.js +45 -3
  416. package/lib/module/api/screens/defineScreen.js.map +1 -1
  417. package/lib/module/api/screens/nativeScreens.js +31 -6
  418. package/lib/module/api/screens/nativeScreens.js.map +1 -1
  419. package/lib/module/api/screens/registerScreens.js +193 -35
  420. package/lib/module/api/screens/registerScreens.js.map +1 -1
  421. package/lib/module/api/theme/theme.js +5 -0
  422. package/lib/module/api/theme/theme.js.map +1 -1
  423. package/lib/module/engine/commands.js +51 -9
  424. package/lib/module/engine/commands.js.map +1 -1
  425. package/lib/module/engine/layout.js +20 -11
  426. package/lib/module/engine/layout.js.map +1 -1
  427. package/lib/module/engine/nativeScreens.js +9 -0
  428. package/lib/module/engine/nativeScreens.js.map +1 -1
  429. package/lib/module/engine/options.js +49 -15
  430. package/lib/module/engine/options.js.map +1 -1
  431. package/lib/module/engine/optionsChange.js +102 -0
  432. package/lib/module/engine/optionsChange.js.map +1 -0
  433. package/lib/module/engine/places.js +52 -7
  434. package/lib/module/engine/places.js.map +1 -1
  435. package/lib/module/engine/props.js +30 -1
  436. package/lib/module/engine/props.js.map +1 -1
  437. package/lib/module/engine/refresh.js +85 -0
  438. package/lib/module/engine/refresh.js.map +1 -0
  439. package/lib/module/engine/rememberedOptions.js +61 -1
  440. package/lib/module/engine/rememberedOptions.js.map +1 -1
  441. package/lib/module/engine/screens.js +77 -16
  442. package/lib/module/engine/screens.js.map +1 -1
  443. package/lib/module/interfaces/contract/commands.json +101 -0
  444. package/lib/module/interfaces/contract/errors.json +275 -0
  445. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +12 -2
  446. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  447. package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
  448. package/lib/typescript/api/app/createNavigation.d.ts +8 -3
  449. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  450. package/lib/typescript/api/commands/commands.d.ts.map +1 -1
  451. package/lib/typescript/api/commands/presentation.d.ts +4 -3
  452. package/lib/typescript/api/commands/presentation.d.ts.map +1 -1
  453. package/lib/typescript/api/events/navigationEvents.d.ts +3 -3
  454. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  455. package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
  456. package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
  457. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
  458. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
  459. package/lib/typescript/api/index.d.ts +1 -1
  460. package/lib/typescript/api/index.d.ts.map +1 -1
  461. package/lib/typescript/api/linking/dispatchedLinks.d.ts +7 -0
  462. package/lib/typescript/api/linking/dispatchedLinks.d.ts.map +1 -0
  463. package/lib/typescript/api/linking/linking.d.ts +8 -0
  464. package/lib/typescript/api/linking/linking.d.ts.map +1 -1
  465. package/lib/typescript/api/navigation.d.ts +4 -1
  466. package/lib/typescript/api/navigation.d.ts.map +1 -1
  467. package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
  468. package/lib/typescript/api/screens/arrangement.d.ts +12 -2
  469. package/lib/typescript/api/screens/arrangement.d.ts.map +1 -1
  470. package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
  471. package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
  472. package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
  473. package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
  474. package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
  475. package/lib/typescript/api/theme/theme.d.ts +2 -0
  476. package/lib/typescript/api/theme/theme.d.ts.map +1 -1
  477. package/lib/typescript/engine/commands.d.ts +5 -0
  478. package/lib/typescript/engine/commands.d.ts.map +1 -1
  479. package/lib/typescript/engine/layout.d.ts.map +1 -1
  480. package/lib/typescript/engine/nativeScreens.d.ts +5 -0
  481. package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
  482. package/lib/typescript/engine/options.d.ts +5 -2
  483. package/lib/typescript/engine/options.d.ts.map +1 -1
  484. package/lib/typescript/engine/optionsChange.d.ts +28 -0
  485. package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
  486. package/lib/typescript/engine/places.d.ts +7 -2
  487. package/lib/typescript/engine/places.d.ts.map +1 -1
  488. package/lib/typescript/engine/props.d.ts +8 -1
  489. package/lib/typescript/engine/props.d.ts.map +1 -1
  490. package/lib/typescript/engine/refresh.d.ts +35 -0
  491. package/lib/typescript/engine/refresh.d.ts.map +1 -0
  492. package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
  493. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  494. package/lib/typescript/engine/screens.d.ts +27 -6
  495. package/lib/typescript/engine/screens.d.ts.map +1 -1
  496. package/lib/typescript/interfaces/ComponentEvents.d.ts +4 -4
  497. package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
  498. package/lib/typescript/interfaces/Events.d.ts +28 -10
  499. package/lib/typescript/interfaces/Events.d.ts.map +1 -1
  500. package/lib/typescript/interfaces/Options.d.ts +1 -1
  501. package/lib/typescript/interfaces/Options.d.ts.map +1 -1
  502. package/lib/typescript/interfaces/options/animations.d.ts +80 -22
  503. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  504. package/lib/typescript/interfaces/options/bottomTabs.d.ts +21 -7
  505. package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
  506. package/lib/typescript/interfaces/options/buttons.d.ts +23 -15
  507. package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
  508. package/lib/typescript/interfaces/options/modals.d.ts +34 -26
  509. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  510. package/lib/typescript/interfaces/options/sideMenu.d.ts +23 -6
  511. package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
  512. package/lib/typescript/interfaces/options/stack.d.ts +17 -3
  513. package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
  514. package/lib/typescript/interfaces/options/system.d.ts +18 -11
  515. package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
  516. package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
  517. package/lib/typescript/interfaces/options/topBar.d.ts +68 -38
  518. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  519. package/metro/acceptPlugin.js +57 -0
  520. package/metro/babelTransformer.js +45 -0
  521. package/metro/index.js +36 -0
  522. package/package.json +3 -1
  523. package/src/ARCHITECTURE.md +40 -27
  524. package/src/adapters/NativeANNTurboModule.ts +12 -2
  525. package/src/api/app/AppRegistry.ts +16 -2
  526. package/src/api/app/createNavigation.ts +203 -69
  527. package/src/api/commands/commands.ts +48 -7
  528. package/src/api/commands/presentation.ts +4 -3
  529. package/src/api/events/navigationEvents.ts +10 -4
  530. package/src/api/hooks/useNavigation.ts +9 -4
  531. package/src/api/hooks/useSafeAreaInsets.ts +4 -3
  532. package/src/api/index.ts +1 -0
  533. package/src/api/linking/dispatchedLinks.ts +18 -0
  534. package/src/api/linking/linking.ts +22 -1
  535. package/src/api/navigation.ts +4 -1
  536. package/src/api/options/OptionsChain.ts +2 -2
  537. package/src/api/screens/arrangement.ts +17 -4
  538. package/src/api/screens/defineScreen.ts +49 -5
  539. package/src/api/screens/nativeScreens.ts +40 -4
  540. package/src/api/screens/registerScreens.tsx +185 -42
  541. package/src/api/theme/theme.ts +5 -0
  542. package/src/engine/commands.ts +70 -13
  543. package/src/engine/layout.ts +27 -11
  544. package/src/engine/nativeScreens.ts +10 -1
  545. package/src/engine/options.ts +45 -19
  546. package/src/engine/optionsChange.ts +107 -0
  547. package/src/engine/places.ts +65 -7
  548. package/src/engine/props.ts +30 -1
  549. package/src/engine/refresh.ts +97 -0
  550. package/src/engine/rememberedOptions.ts +74 -1
  551. package/src/engine/screens.tsx +76 -21
  552. package/src/interfaces/ComponentEvents.ts +4 -4
  553. package/src/interfaces/Events.ts +29 -10
  554. package/src/interfaces/Options.ts +1 -0
  555. package/src/interfaces/contract/commands.json +101 -0
  556. package/src/interfaces/contract/errors.json +275 -0
  557. package/src/interfaces/options/animations.ts +81 -22
  558. package/src/interfaces/options/bottomTabs.ts +21 -7
  559. package/src/interfaces/options/buttons.ts +23 -15
  560. package/src/interfaces/options/modals.ts +34 -26
  561. package/src/interfaces/options/sideMenu.ts +24 -6
  562. package/src/interfaces/options/stack.ts +17 -3
  563. package/src/interfaces/options/system.ts +18 -11
  564. package/src/interfaces/options/toolbar.ts +2 -2
  565. package/src/interfaces/options/topBar.ts +68 -38
  566. package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -41
  567. package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -69
  568. package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationStateReporter.kt +0 -62
  569. package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
  570. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
  571. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
  572. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -138
  573. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -88
  574. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
  575. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -66
  576. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +0 -179
  577. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -139
  578. package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
  579. package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -68
  580. package/ios/Layouts/Tabs/ANNTabsPreloadWindow.h +0 -27
  581. package/ios/Layouts/Tabs/ANNTabsPreloadWindow.mm +0 -55
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
4
4
  import android.content.Context
5
5
  import android.graphics.Color
6
6
  import android.graphics.Outline
7
+ import android.os.SystemClock
7
8
  import android.view.GestureDetector
8
9
  import android.view.Gravity
9
10
  import android.view.MotionEvent
@@ -15,8 +16,10 @@ import android.view.ViewOutlineProvider
15
16
  import android.view.ViewTreeObserver
16
17
  import android.widget.FrameLayout
17
18
  import android.widget.ScrollView
19
+ import androidx.activity.BackEventCompat
18
20
  import androidx.coordinatorlayout.widget.CoordinatorLayout
19
21
  import androidx.core.graphics.Insets
22
+ import androidx.core.view.OneShotPreDrawListener
20
23
  import androidx.core.view.ViewCompat
21
24
  import androidx.core.view.WindowInsetsCompat
22
25
  import com.google.android.material.bottomsheet.BottomSheetBehavior
@@ -24,22 +27,34 @@ import com.google.android.material.bottomsheet.BottomSheetDragHandleView
24
27
  import dev.apexnative.navigation.options.SheetDetent
25
28
  import dev.apexnative.navigation.options.SheetOptions
26
29
  import dev.apexnative.navigation.utils.MaterialTheme
30
+ import dev.apexnative.navigation.utils.SystemAnimations
27
31
  import dev.apexnative.navigation.utils.TestIds
28
32
  import dev.apexnative.navigation.utils.dp
33
+ import dev.apexnative.navigation.viewcontrollers.viewcontroller.IReactView
29
34
  import kotlin.math.roundToInt
30
35
 
31
36
  /**
32
- * A modal shown as a Material bottom sheet (`modal.sheet`): the modal's view in a container with a
33
- * [BottomSheetBehavior], in the modals' CoordinatorLayout, over a scrim. It stops at the detents of its options —
34
- * `medium` at half the height, `large` at the full height under the status bar, a number at that height in dp — and the
35
- * user drags it between them. A drag down past the smallest one closes it, unless `swipeToDismiss` is false: then the
36
- * sheet stays, and the try is told. The screen under it stays in place.
37
+ * A modal shown as a Material bottom sheet (`pageSheet`, `formSheet`, `popover`, or `modal.sheet` without a style): the
38
+ * modal's view in a container with a [BottomSheetBehavior], in the modals' CoordinatorLayout, over a scrim. It stops at
39
+ * the detents of its options — `medium` at half the height, `large` at the full height under the status bar, a number
40
+ * at that height in dp, `content` at the height of the shown screen's content — and the user drags it between them; at
41
+ * three of them at most, the Material behavior's resting states ([SheetStops]). The content is measured without a
42
+ * height limit, as on iOS ([IReactView.contentSize]), again after each of its mounts, and the stops follow it. In a
43
+ * window wider than [MAX_WIDTH_DP] it stands in the middle at that width. While the keyboard shows, the sheet stands
44
+ * on it at every stop, as on iOS, unless its screen keeps its size for it. A drag down past the smallest one closes
45
+ * it, unless `swipeToDismiss` is false: then the sheet stays, and the try is told. The screen under it stays in place.
46
+ * On Android 14+ the back gesture previews its hide ([SheetBack]): the Material behavior shrinks it with the progress
47
+ * and slides it down at the release.
37
48
  */
38
49
  internal class ModalSheet(
39
50
  private val layout: CoordinatorLayout,
40
51
  private val content: View,
41
52
  private val events: Events,
42
- ) {
53
+ /** The content of the screen the modal shows now, which the `content` detent measures; none without one. */
54
+ private val shownContent: () -> IReactView?,
55
+ /** The screen the modal shows now moves its content out of the keyboard's way (`layout.adjustResize`). */
56
+ private val adjustsForKeyboard: () -> Boolean,
57
+ ) : SheetBack {
43
58
  interface Events {
44
59
  /** The user dragged the sheet to another detent. */
45
60
  fun onDetentChanged(detent: SheetDetent)
@@ -60,16 +75,75 @@ internal class ModalSheet(
60
75
  private var options = SheetOptions()
61
76
  private var swipeToDismiss = true
62
77
  private var detents: List<SheetDetent> = listOf(SheetDetent.Large)
78
+ // The detents the sheet rests at, from the lowest (`SheetStops.stops`): the behavior's states.
79
+ private var stops: List<SheetDetent> = listOf(SheetDetent.Large)
80
+ // The heights the behavior has for the stops, in pixels: lifted by the keyboard (`SheetStops.lifted`).
81
+ private var stopHeights: List<Int> = emptyList()
82
+ // The part of the keyboard over the navigation bar, in pixels; 0 without the keyboard.
83
+ private var keyboard = 0
84
+ // The stops' heights changed while the sheet moved (a drag, a slide): it takes them once it rests, and goes to the
85
+ // `selectedDetent` a merge named meanwhile ([pendingArrived]).
86
+ private var restopPending = false
87
+ // The sheet showed while its `content` detent had no height yet: once it has one, the sheet goes where the show
88
+ // would have taken it ([showTarget]), unless the user moved it first.
89
+ private var awaiting = false
90
+ private var pendingArrived: SheetDetent? = null
63
91
  private var selected: SheetDetent? = null
64
92
  private var topGap = 0
65
93
  private var dragged = false
66
94
  private var shown: (() -> Unit)? = null
67
95
  private var closing: (() -> Unit)? = null
96
+ // The back gesture whose preview runs now; none.
97
+ private var backPreview: BackPreview? = null
98
+ // Released: the behavior slides the sheet down from where the preview left it, and hides it at the end.
99
+ private var backFinishing = false
100
+ // The window's observer the scrim follows the preview's frames through, from its start until the sheet is back
101
+ // in place or gone; none otherwise.
102
+ private var backFramesObserver: ViewTreeObserver? = null
103
+
104
+ // The behavior tells no slide while back's preview scales the sheet and its release slides it down (they move it by
105
+ // its transform, not its offset): the scrim follows what is drawn, frame by frame, until the sheet stands again.
106
+ private val backFrames = ViewTreeObserver.OnPreDrawListener {
107
+ updateScrim()
108
+ if (backPreview == null && !backFinishing && container.scaleY == 1f && container.translationY == 0f) {
109
+ stopBackFrames()
110
+ }
111
+ true
112
+ }
113
+
114
+ private val sheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
115
+ override fun onStateChanged(sheet: View, newState: Int) = onState(newState)
116
+ override fun onSlide(sheet: View, slideOffset: Float) = updateScrim()
117
+ }
68
118
 
69
119
  // A list inside the sheet scrolls with it, as the Material behavior expects: up at half height the sheet goes up
70
120
  // first, down at the top of the list the sheet comes down. The behavior follows views that scroll nested.
71
121
  private val scrollViews = ViewTreeObserver.OnGlobalLayoutListener {
72
- if (behavior.dragAnywhere) enableNestedScrolling(container)
122
+ turnScrollViews()
123
+ // The bars above the content may have taken their height.
124
+ if (followed != null) contentChanged()
125
+ }
126
+
127
+ // The shown screen's content that the `content` detent follows, its height without a height limit (pixels, none
128
+ // before it is measured), and the height of the detent the behavior has now.
129
+ private var followed: IReactView? = null
130
+ // The shown screen's content whose mounts turn the lists in it to scroll nested ([hearContent]).
131
+ private var heard: IReactView? = null
132
+ private var contentBody: Int? = null
133
+ private var appliedContentHeight: Int? = null
134
+ // When the content was measured last after a mount, and whether a measure waits ([contentMounted]).
135
+ private var lastMeasure = 0L
136
+ private var measureWaits = false
137
+ private val measureLater = Runnable {
138
+ measureWaits = false
139
+ lastMeasure = SystemClock.uptimeMillis()
140
+ measureContent()
141
+ contentChanged()
142
+ }
143
+
144
+ // The stops stand in pixels: they follow the layout's height (a rotation, a resize).
145
+ private val layoutHeight = View.OnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom ->
146
+ if (bottom - top != oldBottom - oldTop) container.post { restop() }
73
147
  }
74
148
 
75
149
  /** The sheet is down, out of sight: the modal can go at once. */
@@ -88,6 +162,26 @@ internal class ModalSheet(
88
162
  }
89
163
  })
90
164
  scrim.setOnTouchListener { _, event -> taps.onTouchEvent(event) }
165
+ // The keyboard covers the bottom of the window: the sheet stands on it, by the keyboard's part over the
166
+ // navigation bar (the detents count that bar already), unless the screen in it keeps its size for the keyboard
167
+ // (`layout.adjustResize: false`). The scrim, under the sheet, hears the window's insets.
168
+ ViewCompat.setOnApplyWindowInsetsListener(scrim) { _, insets ->
169
+ val ime = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
170
+ val bar = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
171
+ val lift = (ime - bar).coerceAtLeast(0)
172
+ if (lift != keyboard) {
173
+ keyboard = lift
174
+ restop()
175
+ }
176
+ insets
177
+ }
178
+ // The sheet keeps a touch that lands on it and no view in it takes (its bar, an empty part), as Material's
179
+ // BottomSheetDialog does: else the touch falls to the scrim under it, and a tap there closes a dimmed sheet;
180
+ // past an undimmed scrim it reaches the behavior, which takes the sheet into its drag state at the touch's
181
+ // down, before any move. The back gesture starts with such a touch, and a sheet in the drag state has no
182
+ // preview. A drag that starts there still moves the sheet: the behavior takes it past the touch slop, as from
183
+ // the views in it.
184
+ container.setOnTouchListener { _, _ -> true }
91
185
  container.clipToOutline = true
92
186
  container.outlineProvider = object : ViewOutlineProvider() {
93
187
  // The bottom corners are under the screen's bottom: only the top ones round.
@@ -96,74 +190,109 @@ internal class ModalSheet(
96
190
  outline.setRoundRect(0, 0, view.width, view.height + radius.roundToInt(), radius)
97
191
  }
98
192
  }
99
- // The sheet is under the status bar: its screens leave no room for it at their top.
193
+ // The sheet is under the status bar: its screens leave no room for it at their top. In the middle of a wide
194
+ // window it reaches no side, and keeps room only for the part of a side bar that is over it.
100
195
  ViewCompat.setOnApplyWindowInsetsListener(container) { _, insets ->
101
196
  val types = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()
102
197
  val bars = insets.getInsets(types)
103
- WindowInsetsCompat.Builder(insets).setInsets(types, Insets.of(bars.left, 0, bars.right, bars.bottom)).build()
198
+ val laidOut = container.width > 0
199
+ val left = if (laidOut) (bars.left - container.left).coerceAtLeast(0) else bars.left
200
+ val right = if (laidOut) (bars.right - (layout.width - container.right)).coerceAtLeast(0) else bars.right
201
+ WindowInsetsCompat.Builder(insets).setInsets(types, Insets.of(left, 0, right, bars.bottom)).build()
202
+ }
203
+ // Its sides move with the window's width (a rotation, a resize): the room for the side bars follows.
204
+ container.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ ->
205
+ if (left != oldLeft || right != oldRight) container.post { ViewCompat.requestApplyInsets(container) }
104
206
  }
105
207
  container.addView(content, FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
106
208
  container.addView(grabber, FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.TOP or Gravity.CENTER_HORIZONTAL))
107
209
  behavior.isHideable = true
108
210
  behavior.state = BottomSheetBehavior.STATE_HIDDEN
109
211
  behavior.halfExpandedRatio = 0.5f
110
- behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
111
- override fun onStateChanged(sheet: View, newState: Int) = onState(newState)
112
- override fun onSlide(sheet: View, slideOffset: Float) = updateScrim()
113
- })
212
+ behavior.addBottomSheetCallback(sheetCallback)
114
213
  container.viewTreeObserver.addOnGlobalLayoutListener(scrollViews)
115
214
  }
116
215
 
117
- /** Slides the sheet up to its selected detent; [onShown] once it is there. */
118
- fun show(sheetOptions: SheetOptions, swipeToDismiss: Boolean, onShown: () -> Unit) {
216
+ /**
217
+ * Slides the sheet up to its selected detent; [onShown] once it is there. Not [animated], or with the system's
218
+ * animations off ([SystemAnimations]), it stands there at once.
219
+ */
220
+ fun show(sheetOptions: SheetOptions, swipeToDismiss: Boolean, animated: Boolean, onShown: () -> Unit) {
119
221
  topGap = ViewCompat.getRootWindowInsets(layout)?.getInsets(WindowInsetsCompat.Type.systemBars())?.top ?: 0
120
222
  layout.addView(scrim, CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
121
223
  // As tall as its largest detent (maxHeight), not moved down by a margin: the behavior counts its states from
122
224
  // where the sheet is laid out.
123
- layout.addView(container, CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT).also { it.behavior = behavior })
225
+ // In a wide window it stands in the middle, as wide as a Material modal sheet at most.
226
+ behavior.maxWidth = MAX_WIDTH_DP.dp
227
+ val params = CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
228
+ params.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
229
+ params.behavior = behavior
230
+ layout.addView(container, params)
231
+ layout.addOnLayoutChangeListener(layoutHeight)
124
232
  shown = onShown
125
- update(sheetOptions, swipeToDismiss, move = false)
233
+ update(sheetOptions, swipeToDismiss, merge = null)
234
+ selected = sheetOptions.selectedDetent?.takeIf { it in detents }
235
+ awaiting = SheetDetent.Content in detents && contentHeight() == null
236
+ if (!animated || SystemAnimations.areOff(context)) {
237
+ // The behavior lays the sheet out at the state it has before its first layout, with no slide, and tells
238
+ // no state then: the sheet stands at its detent once laid out.
239
+ behavior.state = stateOf(showTarget())
240
+ container.post { if (closing == null) onState(behavior.state) }
241
+ return
242
+ }
126
243
  // Laid out hidden first, so it slides up from the bottom.
127
- container.post { if (closing == null) behavior.state = stateOf(selected ?: detents.first()) }
244
+ container.post { if (closing == null) behavior.state = stateOf(showTarget()) }
128
245
  }
129
246
 
130
- /** New options from mergeOptions: the detents, the look, and the selected detent, which the sheet moves to. */
131
- fun update(sheetOptions: SheetOptions, swipeToDismiss: Boolean, move: Boolean = true) {
132
- val previous = selected
247
+ // The stop the show goes to: the selected one when it is a stop already, else the lowest.
248
+ private fun showTarget(): SheetDetent = selected?.takeIf { it in stops } ?: stops.first()
249
+
250
+ /**
251
+ * New resolved options: the detents and the look. The sheet moves only as [SheetStops] says, after a `mergeOptions`
252
+ * of [merge]; without one (the shown screen changed, the show) it stays where it is.
253
+ */
254
+ fun update(sheetOptions: SheetOptions, swipeToDismiss: Boolean, merge: SheetOptions?) {
255
+ // The stop it rests at, by the stops it has now: the new ones may give its state another detent.
256
+ val settled = detentOf(behavior.state)
133
257
  options = sheetOptions
134
258
  this.swipeToDismiss = swipeToDismiss
135
- detents = sheetOptions.detents?.takeIf { it.isNotEmpty() }?.sortedBy { heightOf(it) } ?: listOf(SheetDetent.Large)
136
- selected = sheetOptions.selectedDetent?.takeIf { it in detents }
259
+ detents = sheetOptions.detents?.takeIf { it.isNotEmpty() } ?: listOf(SheetDetent.Large)
137
260
  grabber.visibility = if (sheetOptions.grabber.isTrue) View.VISIBLE else View.GONE
138
261
  behavior.dragAnywhere = sheetOptions.expandsOnScroll.get(true)
139
262
  container.invalidateOutline()
140
- configure()
141
- if (!move || closing != null) return
142
- val settled = detentOf(behavior.state)
143
- if (settled != null) behavior.isHideable = swipeToDismiss && settled == detents.first()
144
- val target = when {
145
- selected != null && selected != previous -> selected
146
- settled != null && settled !in detents -> selected ?: detents.first()
147
- else -> null
148
- }
149
- if (target != null) behavior.state = stateOf(target)
150
- updateScrim()
263
+ hearContent()
264
+ followContent()
265
+ restop(settled, merge?.selectedDetent)
151
266
  }
152
267
 
153
- /** Slides the sheet down; [onClosed] once it is out of sight. */
154
- fun close(onClosed: () -> Unit) {
268
+ /** Slides the sheet down; [onClosed] once it is out of sight. Not [animated], or with animations off, at once. */
269
+ fun close(animated: Boolean, onClosed: () -> Unit) {
155
270
  closing = onClosed
156
- if (isClosed) {
271
+ if (isClosed || !animated || SystemAnimations.areOff(context)) {
272
+ stopBackPreview()
157
273
  finishClosing()
158
274
  return
159
275
  }
276
+ // Released by back, it goes down already: it is closed at the end of that slide.
277
+ if (backFinishing) return
278
+ stopBackPreview()
160
279
  behavior.isHideable = true
161
280
  behavior.state = BottomSheetBehavior.STATE_HIDDEN
162
281
  }
163
282
 
164
283
  /** Takes the sheet's views away; the modal's view is the caller's. */
165
284
  fun remove() {
285
+ // A slide down of back's release that ends after this tells nothing: the modal went already.
286
+ behavior.removeBottomSheetCallback(sheetCallback)
287
+ stopBackPreview()
288
+ stopBackFrames()
166
289
  container.viewTreeObserver.removeOnGlobalLayoutListener(scrollViews)
290
+ layout.removeOnLayoutChangeListener(layoutHeight)
291
+ followed?.setOnContentMounted(CONTENT_KEY, null)
292
+ followed = null
293
+ container.removeCallbacks(measureLater)
294
+ heard?.setOnContentMounted(SCROLL_KEY, null)
295
+ heard = null
167
296
  container.removeView(content)
168
297
  layout.removeView(container)
169
298
  layout.removeView(scrim)
@@ -176,58 +305,278 @@ internal class ModalSheet(
176
305
  TestIds.apply(scrim, testId?.let { "$it.scrim" })
177
306
  }
178
307
 
308
+ override val hidesOnBack: Boolean
309
+ get() = closing == null && !backFinishing && behavior.isHideable && container.isLaidOut &&
310
+ detentOf(behavior.state) != null
311
+
312
+ override fun startBackPreview(event: BackEventCompat): SheetBack.Gesture? {
313
+ if (!hidesOnBack || backPreview != null) return null
314
+ val preview = BackPreview()
315
+ backPreview = preview
316
+ startBackFrames()
317
+ behavior.startBackProgress(event)
318
+ return preview
319
+ }
320
+
321
+ // A command closed or moved the sheet, or it goes, while back's preview runs: the sheet grows back where it stands
322
+ // (the behavior's cancel), and the gesture's release does the usual back.
323
+ private fun stopBackPreview() {
324
+ if (backPreview == null) return
325
+ backPreview = null
326
+ behavior.cancelBackProgress()
327
+ }
328
+
329
+ private fun startBackFrames() {
330
+ if (backFramesObserver != null) return
331
+ backFramesObserver = container.viewTreeObserver.also { it.addOnPreDrawListener(backFrames) }
332
+ }
333
+
334
+ // The listener leaves the observer it joined: a container out of the window has another one.
335
+ private fun stopBackFrames() {
336
+ val observer = backFramesObserver ?: return
337
+ backFramesObserver = null
338
+ if (observer.isAlive) observer.removeOnPreDrawListener(backFrames)
339
+ }
340
+
341
+ /** The back gesture over this sheet: the Material behavior previews its hide (`MaterialBackHandler`). */
342
+ private inner class BackPreview : SheetBack.Gesture {
343
+ override val sheet: SheetBack
344
+ get() = this@ModalSheet
345
+
346
+ override fun progress(event: BackEventCompat) {
347
+ if (backPreview !== this) return
348
+ behavior.updateBackProgress(event)
349
+ updateScrim()
350
+ }
351
+
352
+ override fun cancel() {
353
+ if (backPreview === this) stopBackPreview()
354
+ }
355
+
356
+ // The behavior slides the sheet down from the preview and sets its hidden state at the end: `onState` closes
357
+ // it there as a drag down does (`onClosedByUser`), so the modal goes through the user's path once.
358
+ override fun commit(): Boolean {
359
+ if (backPreview !== this) return false
360
+ backPreview = null
361
+ if (!hidesOnBack) {
362
+ behavior.cancelBackProgress()
363
+ return false
364
+ }
365
+ backFinishing = true
366
+ behavior.handleBackInvoked()
367
+ return true
368
+ }
369
+ }
370
+
179
371
  private fun closeByUser() {
180
372
  if (!swipeToDismiss) {
181
373
  events.onCloseAttempted()
182
374
  return
183
375
  }
376
+ // With animations off the modal goes at once, and its close takes the sheet away with no slide.
377
+ if (SystemAnimations.areOff(context)) {
378
+ if (closing != null) return
379
+ closing = {}
380
+ return events.onClosedByUser()
381
+ }
184
382
  behavior.isHideable = true
185
383
  behavior.state = BottomSheetBehavior.STATE_HIDDEN
186
384
  }
187
385
 
188
- // How the Material behavior stops at the detents: with half and full height both, at its three states (the
189
- // smallest at the collapsed one); otherwise at the collapsed state (the smaller detent) and the expanded one, which
190
- // is only as tall as the largest detent.
191
- private fun configure() {
192
- val height = detents.filterIsInstance<SheetDetent.Height>().firstOrNull()
193
- val halfAndFull = SheetDetent.Medium in detents && SheetDetent.Large in detents
194
- behavior.isFitToContents = !halfAndFull
195
- behavior.expandedOffset = topGap
196
- behavior.maxHeight = heightOf(detents.last())
197
- val collapsed = if (halfAndFull) height else detents.takeIf { it.size > 1 }?.first()
198
- behavior.skipCollapsed = collapsed == null
199
- // Without a detent of its own the collapsed state is the smallest detent: a sheet that isn't hideable settles
200
- // there, not at Material's peek height.
201
- behavior.peekHeight = heightOf(collapsed ?: detents.first())
386
+ // The stops of the detents and how the Material behavior rests at them (`SheetStops.material`): at one or two it
387
+ // fits the sheet to its highest stop, at three it stops half expanded between them. The detent the options select
388
+ // stays the middle one when there are more.
389
+ // A new height of the sheet (`maxHeight`), of its collapsed state, or a change of the mode is laid out: the
390
+ // behavior measures the sheet and counts a fitted sheet's top in its layout. It says whether a layout comes.
391
+ private fun configure(): Boolean {
392
+ appliedContentHeight = contentHeight()
393
+ // A `content` detent not measured yet has no height: it stays out of the stops until it has one, or it would
394
+ // count as the full height and take the place of `large`.
395
+ val measured = detents.filter { it != SheetDetent.Content || contentHeight() != null }.ifEmpty { detents }
396
+ stops = SheetStops.stops(measured, ::heightOf, options.selectedDetent)
397
+ val lift = if (adjustsForKeyboard()) keyboard else 0
398
+ stopHeights = SheetStops.lifted(stops.map(::heightOf), lift, fullHeight())
399
+ val material = SheetStops.material(stopHeights, parentHeight())
400
+ val relayout = behavior.maxHeight != material.maxHeight || behavior.peekHeight != material.peekHeight ||
401
+ behavior.isFitToContents != material.fitToContents
402
+ behavior.isFitToContents = material.fitToContents
403
+ material.halfExpandedRatio?.takeIf { it > 0f && it < 1f }?.let { behavior.halfExpandedRatio = it }
404
+ behavior.expandedOffset = material.expandedOffset
405
+ behavior.maxHeight = material.maxHeight
406
+ behavior.skipCollapsed = material.skipCollapsed
407
+ behavior.peekHeight = material.peekHeight
408
+ if (relayout) container.requestLayout()
409
+ return relayout
202
410
  }
203
411
 
204
- private fun stateOf(detent: SheetDetent): Int = when {
205
- detent == detents.last() -> BottomSheetBehavior.STATE_EXPANDED
206
- detent == SheetDetent.Medium && !behavior.isFitToContents -> BottomSheetBehavior.STATE_HALF_EXPANDED
207
- else -> BottomSheetBehavior.STATE_COLLAPSED
412
+ /**
413
+ * The stops or their heights changed (the options, the layout, the content, the keyboard): the behavior takes them,
414
+ * and a sheet at rest goes to [arrived] (a `selectedDetent` a merge names) when it is a stop, else stays at the
415
+ * stop it rested at ([settled], by the stops before the change) where it stands now, else goes to the selected one
416
+ * or the lowest (`SheetStops.target`). A sheet on the move takes them once it rests.
417
+ */
418
+ private fun restop(settled: SheetDetent? = detentOf(behavior.state), arrived: SheetDetent? = null) {
419
+ if (container.parent == null) return
420
+ val relayout = configure()
421
+ updateScrim()
422
+ if (closing != null || backFinishing) return
423
+ if (isMoving()) {
424
+ restopPending = true
425
+ if (arrived != null) pendingArrived = arrived
426
+ return
427
+ }
428
+ // Not shown yet: the show slides it to the stop a merge names.
429
+ if (settled == null) {
430
+ if (arrived != null && arrived in stops) selected = arrived
431
+ return
432
+ }
433
+ val shows = awaiting && contentHeight() != null
434
+ if (shows) awaiting = false
435
+ val wanted = arrived ?: if (shows) showTarget() else null
436
+ val target = SheetStops.target(settled, stops, wanted, options.selectedDetent) ?: settled
437
+ behavior.isHideable = swipeToDismiss && target == stops.first()
438
+ // A move, or a sheet that can no longer go down, ends a preview of back on it.
439
+ if (target != settled || !behavior.isHideable) stopBackPreview()
440
+ if (backPreview != null) return
441
+ if (target != settled) selected = target
442
+ if (!relayout) {
443
+ behavior.state = stateOf(target)
444
+ return
445
+ }
446
+ // The new height comes with the sheet's next layout, where the behavior puts a sheet at rest at its state's
447
+ // offset at once; a move to another stop follows it.
448
+ afterNextLayout {
449
+ val gone = closing != null || backFinishing || container.parent == null
450
+ if (gone || backPreview != null) return@afterNextLayout
451
+ if (isMoving()) {
452
+ restopPending = true
453
+ if (target != settled) pendingArrived = target
454
+ return@afterNextLayout
455
+ }
456
+ behavior.state = stateOf(target)
457
+ }
458
+ }
459
+
460
+ private fun isMoving(): Boolean =
461
+ behavior.state == BottomSheetBehavior.STATE_DRAGGING || behavior.state == BottomSheetBehavior.STATE_SETTLING
462
+
463
+ // Runs [step] on the pre-draw of the frame of the sheet's next layout: the behavior has placed it by then.
464
+ private fun afterNextLayout(step: () -> Unit) {
465
+ container.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
466
+ override fun onLayoutChange(
467
+ view: View, left: Int, top: Int, right: Int, bottom: Int,
468
+ oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int,
469
+ ) {
470
+ view.removeOnLayoutChangeListener(this)
471
+ OneShotPreDrawListener.add(view, step)
472
+ }
473
+ })
474
+ }
475
+
476
+ private fun stateOf(detent: SheetDetent): Int = when (detent) {
477
+ stops.last() -> BottomSheetBehavior.STATE_EXPANDED
478
+ stops.first() -> BottomSheetBehavior.STATE_COLLAPSED
479
+ else -> BottomSheetBehavior.STATE_HALF_EXPANDED
208
480
  }
209
481
 
210
482
  private fun detentOf(state: Int): SheetDetent? = when (state) {
211
- BottomSheetBehavior.STATE_EXPANDED -> detents.last()
212
- BottomSheetBehavior.STATE_HALF_EXPANDED -> SheetDetent.Medium
213
- BottomSheetBehavior.STATE_COLLAPSED -> detents.first()
483
+ BottomSheetBehavior.STATE_EXPANDED -> stops.last()
484
+ BottomSheetBehavior.STATE_HALF_EXPANDED -> stops.getOrNull(1)?.takeIf { stops.size == SheetStops.MAX_STOPS }
485
+ BottomSheetBehavior.STATE_COLLAPSED -> stops.first()
214
486
  else -> null
215
487
  }
216
488
 
489
+ private fun parentHeight(): Int = if (layout.height > 0) layout.height else layout.rootView.height
490
+
491
+ // The tallest a sheet is: the layout under the status bar.
492
+ private fun fullHeight(): Int = parentHeight() - topGap
493
+
217
494
  /** The height of the sheet at the detent, in px. */
218
495
  private fun heightOf(detent: SheetDetent): Int {
219
- val full = (if (layout.height > 0) layout.height else layout.rootView.height) - topGap
496
+ val full = fullHeight()
220
497
  return when (detent) {
221
498
  SheetDetent.Large -> full
222
499
  SheetDetent.Medium -> (full + topGap) / 2
500
+ SheetDetent.Content -> contentHeight()?.coerceAtMost(full) ?: full
223
501
  is SheetDetent.Height -> detent.dp.toFloat().dp.coerceAtMost(full)
224
502
  }
225
503
  }
226
504
 
505
+ // The `content` detent follows the content of the screen shown now, and measures it after each of its mounts; a
506
+ // sheet without the detent follows nothing.
507
+ private fun followContent() {
508
+ val content = if (SheetDetent.Content in detents) shownContent() else null
509
+ if (content !== followed) {
510
+ followed?.setOnContentMounted(CONTENT_KEY, null)
511
+ followed = content
512
+ // The last height stays until the new content has one: a push in the sheet doesn't jump it to the top.
513
+ content?.setOnContentMounted(CONTENT_KEY) { contentMounted() }
514
+ }
515
+ measureContent()
516
+ }
517
+
518
+ // Fabric tells every mount of the app: the content is measured at most once per [MEASURE_INTERVAL_MS], the last
519
+ // mount of a burst included, so a screen that renders each frame elsewhere doesn't lay the sheet's tree out each
520
+ // frame too.
521
+ private fun contentMounted() {
522
+ val wait = lastMeasure + MEASURE_INTERVAL_MS - SystemClock.uptimeMillis()
523
+ if (wait > 0) {
524
+ if (!measureWaits) {
525
+ measureWaits = true
526
+ container.postDelayed(measureLater, wait)
527
+ }
528
+ return
529
+ }
530
+ measureLater.run()
531
+ }
532
+
533
+ // The content's own height at the width it has (the sheet's, before it is laid out); kept until a measure gives
534
+ // one again.
535
+ private fun measureContent() {
536
+ val content = followed ?: return
537
+ val width = content.asView().width.takeIf { it > 0 } ?: container.width.takeIf { it > 0 } ?: sheetWidth()
538
+ content.contentSize(width)?.let { contentBody = it.height }
539
+ }
540
+
541
+ // The height of the `content` detent: the content's own, the bars above it in the sheet (a stack's top bar), and
542
+ // the navigation bar below it, which a detent leaves room for as UIKit adds the bottom safe area; none before the
543
+ // content is measured.
544
+ private fun contentHeight(): Int? {
545
+ val body = contentBody ?: return null
546
+ val insets = ViewCompat.getRootWindowInsets(layout)
547
+ val bottom = insets?.getInsets(WindowInsetsCompat.Type.navigationBars())?.bottom ?: 0
548
+ return offsetAbove() + body + bottom
549
+ }
550
+
551
+ // How far below the sheet's top the content stands, by layout: the views between them, without their transforms.
552
+ private fun offsetAbove(): Int {
553
+ var view: View = followed?.asView() ?: return 0
554
+ var top = 0
555
+ while (view !== container) {
556
+ top += view.top
557
+ view = view.parent as? View ?: return 0
558
+ }
559
+ return top
560
+ }
561
+
562
+ // The content may have changed its height: the stops follow it.
563
+ private fun contentChanged() {
564
+ if (contentHeight() != appliedContentHeight) restop()
565
+ }
566
+
567
+ private fun sheetWidth(): Int {
568
+ val width = if (layout.width > 0) layout.width else layout.rootView.width
569
+ return width.coerceAtMost(MAX_WIDTH_DP.dp)
570
+ }
571
+
227
572
  private fun onState(state: Int) {
228
573
  when (state) {
229
- BottomSheetBehavior.STATE_DRAGGING -> dragged = true
574
+ BottomSheetBehavior.STATE_DRAGGING -> {
575
+ dragged = true
576
+ awaiting = false
577
+ }
230
578
  BottomSheetBehavior.STATE_HIDDEN -> {
579
+ backFinishing = false
231
580
  when {
232
581
  closing != null -> finishClosing()
233
582
  else -> {
@@ -241,7 +590,7 @@ internal class ModalSheet(
241
590
  if (closing != null) return
242
591
  val detent = detentOf(state)
243
592
  // A drag down closes the sheet from its smallest detent only; from a larger one it stops at the next.
244
- behavior.isHideable = swipeToDismiss && detent == detents.first()
593
+ behavior.isHideable = swipeToDismiss && detent == stops.first()
245
594
  shown?.let {
246
595
  shown = null
247
596
  it()
@@ -251,6 +600,12 @@ internal class ModalSheet(
251
600
  events.onDetentChanged(detent)
252
601
  }
253
602
  dragged = false
603
+ if (restopPending) {
604
+ val arrived = pendingArrived
605
+ restopPending = false
606
+ pendingArrived = null
607
+ container.post { restop(arrived = arrived) }
608
+ }
254
609
  }
255
610
  }
256
611
  updateScrim()
@@ -265,13 +620,23 @@ internal class ModalSheet(
265
620
  // The screen under the sheet dims as the sheet goes up past `largestUndimmedDetent` (from the bottom without
266
621
  // one), to the dim of a Material modal sheet at the next detent; while it isn't dimmed, it takes touches.
267
622
  private fun updateScrim() {
268
- val height = layout.height - container.top
269
- val undimmed = options.largestUndimmedDetent?.takeIf { it in detents }
270
- val from = undimmed?.let { heightOf(it) } ?: 0
271
- val to = detents.firstOrNull { heightOf(it) > from }?.let { heightOf(it) } ?: return run { setDim(0f) }
623
+ // Hidden at the end of back's slide, it still stands at its detent until the next layout.
624
+ if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) return setDim(0f)
625
+ val height = layout.height - sheetTop()
626
+ val undimmed = stops.indexOf(options.largestUndimmedDetent).takeIf { it >= 0 }
627
+ val from = undimmed?.let { stopHeights.getOrNull(it) } ?: 0
628
+ val to = stopHeights.firstOrNull { it > from } ?: return run { setDim(0f) }
272
629
  setDim(((height - from).toFloat() / (to - from)).coerceIn(0f, 1f))
273
630
  }
274
631
 
632
+ // The top of the sheet as drawn: its offset, and while back's preview moves it, its slide and its scale toward
633
+ // the bottom (the behavior's pivot).
634
+ private fun sheetTop(): Int {
635
+ if (backFramesObserver == null) return container.top
636
+ val scaled = container.pivotY * (1f - container.scaleY)
637
+ return (container.top + container.translationY + scaled).roundToInt()
638
+ }
639
+
275
640
  private fun setDim(fraction: Float) {
276
641
  scrim.alpha = fraction * SCRIM_ALPHA
277
642
  }
@@ -280,9 +645,35 @@ internal class ModalSheet(
280
645
 
281
646
  private fun cornerRadius(): Int = if (options.cornerRadius.hasValue()) options.cornerRadius.get().toFloat().dp else 28.dp
282
647
 
283
- private fun enableNestedScrolling(view: View) {
284
- if (view is ScrollView && !view.isNestedScrollingEnabled) view.isNestedScrollingEnabled = true
285
- if (view is ViewGroup) for (i in 0 until view.childCount) enableNestedScrolling(view.getChildAt(i))
648
+ // React Native mounts the lists of the shown screen after the sheet's layout, and its views take no layout of their
649
+ // own: the sheet hears each mount of the content and turns a new list to scroll nested there.
650
+ private fun hearContent() {
651
+ val content = if (behavior.dragAnywhere) shownContent() else null
652
+ if (content !== heard) {
653
+ heard?.setOnContentMounted(SCROLL_KEY, null)
654
+ heard = content
655
+ content?.setOnContentMounted(SCROLL_KEY) { turnScrollViews() }
656
+ }
657
+ turnScrollViews()
658
+ }
659
+
660
+ // The behavior finds the lists that scroll nested only in its layout of the sheet: a list turned so gets the sheet
661
+ // one more, else the behavior knows no list and takes a drag down on a scrolled one for a drag of the sheet.
662
+ private fun turnScrollViews() {
663
+ if (behavior.dragAnywhere && enableNestedScrolling(container)) container.requestLayout()
664
+ }
665
+
666
+ // Whether a list was turned to scroll nested now.
667
+ private fun enableNestedScrolling(view: View): Boolean {
668
+ var turned = false
669
+ if (view is ScrollView && !view.isNestedScrollingEnabled) {
670
+ view.isNestedScrollingEnabled = true
671
+ turned = true
672
+ }
673
+ if (view is ViewGroup) {
674
+ for (i in 0 until view.childCount) turned = enableNestedScrolling(view.getChildAt(i)) || turned
675
+ }
676
+ return turned
286
677
  }
287
678
 
288
679
  /** With `expandsOnScroll: false` only the top of the sheet, around the grabber, drags it; the rest scrolls. */
@@ -298,7 +689,7 @@ internal class ModalSheet(
298
689
  when (event.actionMasked) {
299
690
  MotionEvent.ACTION_DOWN -> {
300
691
  downY = event.rawY
301
- downAtSmallest = detentOf(state) == detents.first()
692
+ downAtSmallest = detentOf(state) == stops.first()
302
693
  }
303
694
  MotionEvent.ACTION_UP -> {
304
695
  if (!swipeToDismiss && closing == null && downAtSmallest && event.rawY - downY > ATTEMPT_DISTANCE_DP.dp) {
@@ -333,10 +724,22 @@ internal class ModalSheet(
333
724
  }
334
725
  }
335
726
 
336
- private companion object {
727
+ companion object {
728
+ /** The widest a sheet is, in dp: the Material modal bottom sheet's `maxWidth`. */
729
+ const val MAX_WIDTH_DP = 640
730
+
337
731
  /** The dim of a Material modal bottom sheet's scrim. */
338
- const val SCRIM_ALPHA = 0.32f
339
- const val GRAB_AREA_DP = 48
340
- const val ATTEMPT_DISTANCE_DP = 48
732
+ private const val SCRIM_ALPHA = 0.32f
733
+ private const val GRAB_AREA_DP = 48
734
+ private const val ATTEMPT_DISTANCE_DP = 48
735
+
736
+ // The key of the sheet's handler of its content's mounts.
737
+ private const val CONTENT_KEY = "sheet.content"
738
+
739
+ // The least time between two measures of the content after its mounts.
740
+ private const val MEASURE_INTERVAL_MS = 100L
741
+
742
+ // The key of the sheet's handler that turns its content's lists to scroll nested.
743
+ private const val SCROLL_KEY = "sheet.scroll"
341
744
  }
342
745
  }