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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/ApexNavigation.podspec +2 -1
  2. package/CHANGELOG.md +32 -0
  3. package/README.md +1 -1
  4. package/android/CLAUDE.md +31 -20
  5. package/android/CORE.md +12 -8
  6. package/android/consumer-rules.pro +6 -0
  7. package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +36 -5
  8. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
  9. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
  10. package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +1 -1
  11. package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
  12. package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
  13. package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
  14. package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
  15. package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
  16. package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
  17. package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
  18. package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
  19. package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
  20. package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
  21. package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +7 -3
  22. package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +41 -3
  23. package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +16 -1
  24. package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +0 -2
  25. package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
  26. package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +12 -49
  27. package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
  28. package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +6 -6
  29. package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +27 -7
  30. package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +12 -1
  31. package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
  32. package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
  33. package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
  34. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +9 -7
  35. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
  36. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +13 -10
  37. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
  38. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
  39. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +7 -8
  40. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
  41. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
  42. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +20 -2
  43. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +443 -58
  44. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +33 -5
  45. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
  46. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +60 -2
  47. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +4 -0
  48. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -6
  49. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +8 -2
  50. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +32 -4
  51. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
  52. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +2 -2
  53. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +50 -7
  54. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +15 -3
  55. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +10 -4
  56. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
  57. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +215 -43
  58. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +25 -26
  59. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
  60. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
  61. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +36 -38
  62. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
  63. package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
  64. package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
  65. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
  66. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
  67. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +129 -179
  68. package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +8 -1
  69. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
  70. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +67 -3
  71. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +346 -36
  72. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
  73. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +55 -42
  74. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextMorph.kt +176 -0
  75. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
  76. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
  77. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
  78. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -24
  79. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +51 -34
  80. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
  81. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
  82. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
  83. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +121 -175
  84. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
  85. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
  86. package/android/src/main/jni/ANNContentHeights.cpp +82 -0
  87. package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
  88. package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
  89. package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
  90. package/android/src/main/res/values/styles.xml +1 -1
  91. package/android/src/main/res/values-night/styles.xml +1 -1
  92. package/cli/doctor/checks/app.js +16 -1
  93. package/cli/project.js +4 -0
  94. package/cli/setup/index.js +5 -0
  95. package/cli/setup/steps/metro.js +46 -0
  96. package/docs-md/architecture/native-screens.md +4 -0
  97. package/docs-md/architecture/theme-and-context.md +131 -12
  98. package/docs-md/architecture/transitions.md +46 -5
  99. package/docs-md/containers/modals.md +2 -0
  100. package/docs-md/containers/overlays.md +1 -1
  101. package/docs-md/containers/side-menu.md +1 -1
  102. package/docs-md/containers/tabs.md +2 -0
  103. package/docs-md/flows/bottom-sheets.md +4 -2
  104. package/docs-md/fundamentals/screens-and-props.md +9 -1
  105. package/docs-md/fundamentals/starting-the-app.md +1 -1
  106. package/docs-md/guides/cli.md +3 -1
  107. package/docs-md/help/glossary.md +2 -2
  108. package/docs-md/help/troubleshooting.md +22 -1
  109. package/docs-md/index.json +41 -1
  110. package/docs-md/migration/from-expo-router.md +1 -1
  111. package/docs-md/migration/from-react-native-navigation.md +1 -1
  112. package/docs-md/migration/from-react-navigation.md +2 -2
  113. package/docs-md/screen-ui/back-and-gestures.md +17 -1
  114. package/docs-md/screen-ui/bar-buttons.md +5 -5
  115. package/docs-md/screen-ui/search.md +1 -1
  116. package/docs-md/screen-ui/system-ui.md +16 -11
  117. package/docs-md/screen-ui/toolbar-and-fab.md +4 -2
  118. package/docs-md/screen-ui/top-bar.md +8 -4
  119. package/docs-md/start/installation.md +16 -1
  120. package/ios/Animations/ANNFrameDriver.h +55 -15
  121. package/ios/Animations/ANNFrameDriver.mm +216 -60
  122. package/ios/Animations/ANNTransitionAnimator.h +26 -5
  123. package/ios/Animations/ANNTransitionAnimator.mm +49 -3
  124. package/ios/Animations/ANNTransitionPlan.h +2 -2
  125. package/ios/Animations/ANNTransitionPlan.mm +10 -0
  126. package/ios/Animations/Elements/ANNElementFinder.h +2 -2
  127. package/ios/Animations/Elements/ANNElementFinder.mm +2 -2
  128. package/ios/Animations/Elements/ANNElementTransition.h +3 -3
  129. package/ios/Animations/Elements/ANNElementTransition.mm +2 -2
  130. package/ios/Animations/Elements/ANNSharedElementFlight.h +3 -3
  131. package/ios/Animations/Elements/ANNSharedElementFlight.mm +249 -45
  132. package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
  133. package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
  134. package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
  135. package/ios/Animations/Interpolators/ANNViewTracks.h +9 -6
  136. package/ios/Animations/Interpolators/ANNViewTracks.mm +100 -30
  137. package/ios/CLAUDE.md +32 -22
  138. package/ios/CORE.md +25 -21
  139. package/ios/Contract/ANNCommandRunner.h +8 -7
  140. package/ios/Contract/ANNCommandRunner.mm +21 -27
  141. package/ios/Contract/ANNLayoutNode.h +3 -3
  142. package/ios/Contract/ANNLayoutReader.h +3 -3
  143. package/ios/Core/ANNAppDelegate.mm +0 -2
  144. package/ios/Core/ANNDeepLinkQueue.h +6 -5
  145. package/ios/Core/ANNDeepLinkQueue.mm +22 -10
  146. package/ios/Core/ANNJavaScriptCalls.h +11 -12
  147. package/ios/Core/ANNJavaScriptCalls.mm +7 -16
  148. package/ios/Core/ANNReloadCoordinator.mm +4 -8
  149. package/ios/Core/ANNSceneDelegate.mm +2 -2
  150. package/ios/Core/ApexNavigation+Internal.h +1 -1
  151. package/ios/Core/ApexNavigation.mm +6 -2
  152. package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
  153. package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
  154. package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +4 -4
  155. package/ios/Layouts/Root/ANNRootCoordinator.mm +45 -15
  156. package/ios/Layouts/Screen/ANNComponentViewController.h +2 -2
  157. package/ios/Layouts/Screen/ANNComponentViewController.mm +13 -23
  158. package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
  159. package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
  160. package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
  161. package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
  162. package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
  163. package/ios/Layouts/Screen/ANNScreenRegions.mm +0 -38
  164. package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
  165. package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
  166. package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
  167. package/ios/Layouts/Stack/ANNPreviewPush.mm +57 -5
  168. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
  169. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
  170. package/ios/Layouts/Stack/ANNStackController.h +8 -0
  171. package/ios/Layouts/Stack/ANNStackController.mm +153 -11
  172. package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
  173. package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
  174. package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
  175. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
  176. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
  177. package/ios/Layouts/Tabs/ANNBottomTabsController.mm +2 -1
  178. package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
  179. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +4 -0
  180. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +123 -36
  181. package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
  182. package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
  183. package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
  184. package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
  185. package/ios/Options/ANNAnimationOptions.h +2 -1
  186. package/ios/Options/ANNAnimationOptions.mm +1 -1
  187. package/ios/Options/ANNBottomTabsOptions.h +2 -0
  188. package/ios/Options/ANNBottomTabsOptions.mm +1 -0
  189. package/ios/Options/ANNOptions.h +25 -0
  190. package/ios/Options/ANNOptions.mm +157 -1
  191. package/ios/Options/ANNOptionsReader.mm +108 -0
  192. package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
  193. package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
  194. package/ios/Options/Params/ANNParam.h +15 -0
  195. package/ios/Options/Params/ANNParam.mm +61 -0
  196. package/ios/Options/Params/Color.h +3 -0
  197. package/ios/Options/Params/Color.mm +21 -0
  198. package/ios/Options/Params/ColorParser.h +4 -3
  199. package/ios/Options/Params/ColorParser.mm +22 -4
  200. package/ios/Options/Params/Image.h +4 -0
  201. package/ios/Options/Params/Image.mm +20 -0
  202. package/ios/React/ANNReactView.mm +6 -25
  203. package/ios/React/ANNSafeAreaViewComponentView.mm +26 -15
  204. package/ios/React/ANNSurfaceHostingView.h +14 -0
  205. package/ios/React/ANNSurfaceHostingView.mm +144 -1
  206. package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
  207. package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
  208. package/ios/TopBar/ANNBarAppearance.h +2 -1
  209. package/ios/TopBar/ANNBarAppearance.mm +111 -30
  210. package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
  211. package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
  212. package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
  213. package/ios/TopBar/ANNBarButtons.h +5 -3
  214. package/ios/TopBar/ANNBarButtons.mm +186 -17
  215. package/ios/TopBar/ANNLargeTitlePresenter.mm +24 -4
  216. package/ios/TopBar/ANNMainList.h +3 -0
  217. package/ios/TopBar/ANNMainList.mm +4 -0
  218. package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
  219. package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
  220. package/ios/TopBar/ANNSearchPresenter.mm +2 -2
  221. package/ios/TopBar/ANNSearchResultsViewController.mm +5 -5
  222. package/ios/TopBar/ANNTopBarPresenter.h +3 -2
  223. package/ios/TopBar/ANNTopBarPresenter.mm +95 -22
  224. package/ios/TurboModules/ANNTurboModule.mm +66 -16
  225. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
  226. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
  227. package/lib/module/ARCHITECTURE.md +17 -7
  228. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  229. package/lib/module/api/app/AppRegistry.js +14 -2
  230. package/lib/module/api/app/AppRegistry.js.map +1 -1
  231. package/lib/module/api/app/createNavigation.js +188 -66
  232. package/lib/module/api/app/createNavigation.js.map +1 -1
  233. package/lib/module/api/events/navigationEvents.js +6 -2
  234. package/lib/module/api/events/navigationEvents.js.map +1 -1
  235. package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
  236. package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
  237. package/lib/module/api/linking/linking.js +11 -0
  238. package/lib/module/api/linking/linking.js.map +1 -1
  239. package/lib/module/api/options/OptionsChain.js +2 -2
  240. package/lib/module/api/screens/defineScreen.js +45 -3
  241. package/lib/module/api/screens/defineScreen.js.map +1 -1
  242. package/lib/module/api/screens/nativeScreens.js +31 -6
  243. package/lib/module/api/screens/nativeScreens.js.map +1 -1
  244. package/lib/module/api/screens/registerScreens.js +172 -35
  245. package/lib/module/api/screens/registerScreens.js.map +1 -1
  246. package/lib/module/api/theme/theme.js +5 -0
  247. package/lib/module/api/theme/theme.js.map +1 -1
  248. package/lib/module/engine/commands.js +42 -2
  249. package/lib/module/engine/commands.js.map +1 -1
  250. package/lib/module/engine/layout.js +7 -2
  251. package/lib/module/engine/layout.js.map +1 -1
  252. package/lib/module/engine/nativeScreens.js +9 -0
  253. package/lib/module/engine/nativeScreens.js.map +1 -1
  254. package/lib/module/engine/options.js +5 -4
  255. package/lib/module/engine/options.js.map +1 -1
  256. package/lib/module/engine/optionsChange.js +102 -0
  257. package/lib/module/engine/optionsChange.js.map +1 -0
  258. package/lib/module/engine/refresh.js +85 -0
  259. package/lib/module/engine/refresh.js.map +1 -0
  260. package/lib/module/engine/rememberedOptions.js +60 -0
  261. package/lib/module/engine/rememberedOptions.js.map +1 -1
  262. package/lib/module/engine/screens.js +55 -4
  263. package/lib/module/engine/screens.js.map +1 -1
  264. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +7 -0
  265. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  266. package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
  267. package/lib/typescript/api/app/createNavigation.d.ts +8 -3
  268. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  269. package/lib/typescript/api/events/navigationEvents.d.ts +2 -2
  270. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  271. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
  272. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
  273. package/lib/typescript/api/index.d.ts +1 -1
  274. package/lib/typescript/api/index.d.ts.map +1 -1
  275. package/lib/typescript/api/linking/linking.d.ts +8 -0
  276. package/lib/typescript/api/linking/linking.d.ts.map +1 -1
  277. package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
  278. package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
  279. package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
  280. package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
  281. package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
  282. package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
  283. package/lib/typescript/api/theme/theme.d.ts +2 -0
  284. package/lib/typescript/api/theme/theme.d.ts.map +1 -1
  285. package/lib/typescript/engine/commands.d.ts +5 -0
  286. package/lib/typescript/engine/commands.d.ts.map +1 -1
  287. package/lib/typescript/engine/layout.d.ts.map +1 -1
  288. package/lib/typescript/engine/nativeScreens.d.ts +5 -0
  289. package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
  290. package/lib/typescript/engine/optionsChange.d.ts +28 -0
  291. package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
  292. package/lib/typescript/engine/refresh.d.ts +35 -0
  293. package/lib/typescript/engine/refresh.d.ts.map +1 -0
  294. package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
  295. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  296. package/lib/typescript/engine/screens.d.ts +27 -6
  297. package/lib/typescript/engine/screens.d.ts.map +1 -1
  298. package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
  299. package/lib/typescript/interfaces/options/animations.d.ts +44 -14
  300. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  301. package/lib/typescript/interfaces/options/bottomTabs.d.ts +19 -6
  302. package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
  303. package/lib/typescript/interfaces/options/buttons.d.ts +10 -10
  304. package/lib/typescript/interfaces/options/modals.d.ts +6 -7
  305. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  306. package/lib/typescript/interfaces/options/stack.d.ts +8 -3
  307. package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
  308. package/lib/typescript/interfaces/options/system.d.ts +8 -5
  309. package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
  310. package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
  311. package/lib/typescript/interfaces/options/topBar.d.ts +46 -29
  312. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  313. package/metro/acceptPlugin.js +57 -0
  314. package/metro/babelTransformer.js +45 -0
  315. package/metro/index.js +36 -0
  316. package/package.json +3 -1
  317. package/src/ARCHITECTURE.md +17 -7
  318. package/src/adapters/NativeANNTurboModule.ts +7 -0
  319. package/src/api/app/AppRegistry.ts +16 -2
  320. package/src/api/app/createNavigation.ts +201 -69
  321. package/src/api/events/navigationEvents.ts +9 -3
  322. package/src/api/hooks/useSafeAreaInsets.ts +4 -3
  323. package/src/api/index.ts +1 -0
  324. package/src/api/linking/linking.ts +22 -1
  325. package/src/api/options/OptionsChain.ts +2 -2
  326. package/src/api/screens/defineScreen.ts +49 -5
  327. package/src/api/screens/nativeScreens.ts +40 -4
  328. package/src/api/screens/registerScreens.tsx +170 -42
  329. package/src/api/theme/theme.ts +5 -0
  330. package/src/engine/commands.ts +51 -6
  331. package/src/engine/layout.ts +11 -2
  332. package/src/engine/nativeScreens.ts +10 -1
  333. package/src/engine/options.ts +6 -8
  334. package/src/engine/optionsChange.ts +107 -0
  335. package/src/engine/refresh.ts +97 -0
  336. package/src/engine/rememberedOptions.ts +70 -0
  337. package/src/engine/screens.tsx +53 -8
  338. package/src/interfaces/ComponentEvents.ts +2 -2
  339. package/src/interfaces/options/animations.ts +44 -14
  340. package/src/interfaces/options/bottomTabs.ts +19 -6
  341. package/src/interfaces/options/buttons.ts +10 -10
  342. package/src/interfaces/options/modals.ts +6 -7
  343. package/src/interfaces/options/stack.ts +8 -3
  344. package/src/interfaces/options/system.ts +8 -5
  345. package/src/interfaces/options/toolbar.ts +2 -2
  346. package/src/interfaces/options/topBar.ts +46 -29
  347. package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -42
  348. package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -70
  349. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
  350. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
  351. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -136
  352. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -86
  353. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
  354. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -64
  355. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +0 -177
  356. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -137
  357. package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
  358. package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -70
@@ -1,5 +1,12 @@
1
1
  #import "ANNBarAppearance.h"
2
2
  #import "ANNBackButtonPresenter.h"
3
+ #import "ANNTabsDesign.h"
4
+ #import <objc/runtime.h>
5
+
6
+ // The icon an indicator was tinted from: an entry found by the icon's address counts only for that icon. An indicator
7
+ // that is the icon itself (already an original image) carries nothing: the icon must not hold itself, and it may be
8
+ // the image the options' cache keeps.
9
+ static const void *ANNIndicatorIconKey = &ANNIndicatorIconKey;
3
10
 
4
11
  // UIKit's fonts of the places the options build over: the title of the row, the large title, the text of the back
5
12
  // button.
@@ -28,6 +35,27 @@ static BOOL ANNIsClear(UIColor *color, UITraitCollection *traits) {
28
35
  return CGColorGetAlpha([color resolvedColorWithTraitCollection:traits].CGColor) == 0;
29
36
  }
30
37
 
38
+ // The images the looks are made of, by what they are drawn from, so a look filled again from the same options is
39
+ // equal to the one before (UIKit compares appearances by value, images by identity) and nothing is set again. Main
40
+ // queue; a memory warning empties it.
41
+ static NSCache<id, UIImage *> *ANNLookImages(void) {
42
+ static NSCache<id, UIImage *> *images;
43
+ static dispatch_once_t once;
44
+ dispatch_once(&once, ^{
45
+ images = [NSCache new];
46
+ images.countLimit = 64;
47
+ });
48
+ return images;
49
+ }
50
+
51
+ // A color of the options as a key: a pair by its sides (its dynamic color is made anew), else its value.
52
+ static NSArray *ANNColorKeyOf(Color *color) {
53
+ if (color.light || color.dark) {
54
+ return @[ color.light ?: NSNull.null, color.dark ?: NSNull.null ];
55
+ }
56
+ return @[ color.value ?: NSNull.null ];
57
+ }
58
+
31
59
  static NSDictionary<NSAttributedStringKey, id> *ANNTextAttributes(ANNFontOptions *font,
32
60
  Color *color,
33
61
  UIFont *placeFont) {
@@ -104,16 +132,14 @@ static NSDictionary<NSAttributedStringKey, id> *ANNTextAttributes(ANNFontOptions
104
132
  return resolved && !ANNIsClear(resolved, traits) ? resolved : nil;
105
133
  }
106
134
 
135
+ // The glass bar is translucent by default; the design before iOS 26 (and its compatibility mode) opaque.
107
136
  + (BOOL)translucentByDefault {
108
- if (@available(iOS 26.0, *)) {
109
- return YES;
110
- }
111
- return NO;
137
+ return ANNTabsDesign.isGlass;
112
138
  }
113
139
 
114
- // `color` beats `blur` and `translucent`; a clear color is a clear bar. On iOS 26 a color is a solid bar: a clear base
115
- // with the color and a 1×1 image of it, without the system effect. `blur` is the system's chrome material on a clear
116
- // base, with no view of its own in the bar.
140
+ // `color` beats `blur` and `translucent`; a clear color is a clear bar. On the glass of iOS 26 a color is a solid
141
+ // bar: a clear base with the color and a 1×1 image of it, without the system effect. `blur` is the system's chrome
142
+ // material on a clear base, with no view of its own in the bar.
117
143
  + (void)configure:(UINavigationBarAppearance *)appearance
118
144
  color:(UIColor *)color
119
145
  blur:(BOOL)blur
@@ -124,7 +150,7 @@ static NSDictionary<NSAttributedStringKey, id> *ANNTextAttributes(ANNFontOptions
124
150
  [appearance configureWithTransparentBackground];
125
151
  return;
126
152
  }
127
- if (@available(iOS 26.0, *)) {
153
+ if (ANNTabsDesign.isGlass) {
128
154
  [appearance configureWithTransparentBackground];
129
155
  appearance.backgroundEffect = nil;
130
156
  appearance.backgroundColor = color;
@@ -219,23 +245,51 @@ static NSDictionary<NSAttributedStringKey, id> *ANNTextAttributes(ANNFontOptions
219
245
  if (!color) {
220
246
  return nil;
221
247
  }
222
- icon = [UIImage systemImageNamed:@"chevron.backward"];
248
+ symbol = @"chevron.backward";
249
+ icon = [UIImage systemImageNamed:symbol];
223
250
  isSymbol = YES;
224
251
  }
252
+ // A symbol by its name; an image by its object, which the entry keeps, so its address is never another's.
253
+ id iconKey = isSymbol ? symbol : [NSValue valueWithNonretainedObject:icon];
254
+ NSArray *key = @[
255
+ @"back", iconKey, color ? ANNColorKeyOf(backButton.color) : NSNull.null
256
+ ];
257
+ NSCache<id, UIImage *> *images = ANNLookImages();
258
+ UIImage *cached = [images objectForKey:key];
259
+ if (cached && (isSymbol || cached == icon ||
260
+ objc_getAssociatedObject(cached, ANNIndicatorIconKey) == icon)) {
261
+ return cached;
262
+ }
263
+ UIImage *indicator = nil;
225
264
  if (color) {
226
- return [icon imageWithTintColor:color renderingMode:UIImageRenderingModeAlwaysOriginal];
265
+ indicator = [icon imageWithTintColor:color
266
+ renderingMode:UIImageRenderingModeAlwaysOriginal];
267
+ } else {
268
+ indicator =
269
+ isSymbol ? icon : [icon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
270
+ }
271
+ if (!isSymbol && indicator != icon) {
272
+ objc_setAssociatedObject(indicator, ANNIndicatorIconKey, icon,
273
+ OBJC_ASSOCIATION_RETAIN_NONATOMIC);
227
274
  }
228
- return isSymbol ? icon : [icon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
275
+ [images setObject:indicator forKey:key];
276
+ return indicator;
229
277
  }
230
278
 
279
+ // One for the app: every look that hides the arrow holds the same image.
231
280
  + (UIImage *)emptyIndicator {
232
- UIGraphicsImageRendererFormat *format = [UIGraphicsImageRendererFormat preferredFormat];
233
- format.opaque = NO;
234
- UIGraphicsImageRenderer *renderer =
235
- [[UIGraphicsImageRenderer alloc] initWithSize:CGSizeMake(1, 1) format:format];
236
- UIImage *empty = [renderer imageWithActions:^(UIGraphicsImageRendererContext *context){
237
- }];
238
- return [empty imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
281
+ static UIImage *empty;
282
+ static dispatch_once_t once;
283
+ dispatch_once(&once, ^{
284
+ UIGraphicsImageRendererFormat *format = [UIGraphicsImageRendererFormat preferredFormat];
285
+ format.opaque = NO;
286
+ UIGraphicsImageRenderer *renderer =
287
+ [[UIGraphicsImageRenderer alloc] initWithSize:CGSizeMake(1, 1) format:format];
288
+ UIImage *drawn = [renderer imageWithActions:^(UIGraphicsImageRendererContext *context){
289
+ }];
290
+ empty = [drawn imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
291
+ });
292
+ return empty;
239
293
  }
240
294
 
241
295
  + (UIColor *)pinnedTitleColor:(Color *)color traits:(UITraitCollection *)traits {
@@ -244,8 +298,44 @@ static NSDictionary<NSAttributedStringKey, id> *ANNTextAttributes(ANNFontOptions
244
298
  }
245
299
 
246
300
  // The 1×1 picture of the color in both styles: the bar's image view picks the variant of its own style, so a `{ light,
247
- // dark }` color follows a switch of the style without a new appearance.
301
+ // dark }` color follows a switch of the style without a new appearance. Made once for the two resolved colors and the
302
+ // style picked.
248
303
  + (UIImage *)imageOfColor:(UIColor *)color traits:(UITraitCollection *)traits {
304
+ UITraitCollection *light =
305
+ [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight];
306
+ UITraitCollection *dark =
307
+ [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
308
+ // Each variant is drawn with the window's other traits (contrast, elevation) and registered by its style only.
309
+ NSArray<UITraitCollection *> *base = traits ? @[ traits ] : @[];
310
+ UITraitCollection *lightTraits = [UITraitCollection
311
+ traitCollectionWithTraitsFromCollections:[base arrayByAddingObject:light]];
312
+ UITraitCollection *darkTraits = [UITraitCollection
313
+ traitCollectionWithTraitsFromCollections:[base arrayByAddingObject:dark]];
314
+ UIColor *lightColor = [color resolvedColorWithTraitCollection:lightTraits];
315
+ UIColor *darkColor = [color resolvedColorWithTraitCollection:darkTraits];
316
+ BOOL isDark = traits.userInterfaceStyle == UIUserInterfaceStyleDark;
317
+ NSArray *key = @[ @"color", lightColor, darkColor, @(isDark) ];
318
+ NSCache<id, UIImage *> *images = ANNLookImages();
319
+ UIImage *cached = [images objectForKey:key];
320
+ if (cached) {
321
+ return cached;
322
+ }
323
+ UIImage *image = [self drawImageOf:color
324
+ light:light
325
+ dark:dark
326
+ lightTraits:lightTraits
327
+ darkTraits:darkTraits
328
+ isDark:isDark];
329
+ [images setObject:image forKey:key];
330
+ return image;
331
+ }
332
+
333
+ + (UIImage *)drawImageOf:(UIColor *)color
334
+ light:(UITraitCollection *)light
335
+ dark:(UITraitCollection *)dark
336
+ lightTraits:(UITraitCollection *)lightTraits
337
+ darkTraits:(UITraitCollection *)darkTraits
338
+ isDark:(BOOL)isDark {
249
339
  UIImage * (^draw)(UITraitCollection *) = ^UIImage *(UITraitCollection *variant) {
250
340
  UIColor *resolved = [color resolvedColorWithTraitCollection:variant];
251
341
  UIGraphicsImageRendererFormat *format = [UIGraphicsImageRendererFormat preferredFormat];
@@ -257,20 +347,11 @@ static NSDictionary<NSAttributedStringKey, id> *ANNTextAttributes(ANNFontOptions
257
347
  [context fillRect:CGRectMake(0, 0, 1, 1)];
258
348
  }];
259
349
  };
260
- UITraitCollection *light =
261
- [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight];
262
- UITraitCollection *dark =
263
- [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
264
- // Each variant is drawn with the window's other traits (contrast, elevation) and registered by its style only.
265
- NSArray<UITraitCollection *> *base = traits ? @[ traits ] : @[];
266
- UIImage *lightImage =
267
- draw([UITraitCollection traitCollectionWithTraitsFromCollections:[base arrayByAddingObject:light]]);
268
- UIImage *darkImage =
269
- draw([UITraitCollection traitCollectionWithTraitsFromCollections:[base arrayByAddingObject:dark]]);
350
+ UIImage *lightImage = draw(lightTraits);
351
+ UIImage *darkImage = draw(darkTraits);
270
352
  UIImageAsset *asset = [UIImageAsset new];
271
353
  [asset registerImage:lightImage withTraitCollection:light];
272
354
  [asset registerImage:darkImage withTraitCollection:dark];
273
- BOOL isDark = traits.userInterfaceStyle == UIUserInterfaceStyleDark;
274
355
  UIImage *picked = [asset imageWithTraitCollection:isDark ? dark : light];
275
356
  return picked ?: (isDark ? darkImage : lightImage);
276
357
  }
@@ -1,4 +1,5 @@
1
1
  #import "ANNBarBackgroundComponent.h"
2
+ #import <React/RCTLog.h>
2
3
  #import <objc/runtime.h>
3
4
 
4
5
  // The background a bar shows now.
@@ -13,6 +14,21 @@ static UIView *ANNBackgroundHostOf(UINavigationBar *bar) {
13
14
  return subview;
14
15
  }
15
16
  }
17
+ #if DEBUG
18
+ // The name is UIKit's private one: a system that renames the view puts the components on the bar itself. Told
19
+ // once, in development, and only for a bar that has its views already (one not laid out yet has none).
20
+ static BOOL told = NO;
21
+ if (!told && bar.subviews.count > 0) {
22
+ told = YES;
23
+ NSMutableArray<NSString *> *names = [NSMutableArray new];
24
+ for (UIView *subview in bar.subviews) {
25
+ [names addObject:NSStringFromClass(subview.class)];
26
+ }
27
+ RCTLogWarn(@"[ApexNavigation] the bar has no view named …BarBackground, so a background "
28
+ @"component goes on the bar itself at index 1; its views: %@",
29
+ [names componentsJoinedByString:@", "]);
30
+ }
31
+ #endif
16
32
  return bar;
17
33
  }
18
34
 
@@ -4,10 +4,10 @@
4
4
  NS_ASSUME_NONNULL_BEGIN
5
5
 
6
6
  /// The slot of a React button (`topBar.leftButtons[].component`) as the custom view of its bar item. It takes a content
7
- /// view (`UIView<ANNComponentProtocol>`), never a class of the roots. On iOS 26 without `UIDesignRequiresCompatibility`
8
- /// in the app's Info.plist the slot is 44 pt high and as wide as the content, at least 44 pt; the width follows the
9
- /// content both ways (measured after every mount; a change over 0.5 pt only), and the slot's constraints live with the
10
- /// view, so a new item built around it reuses them. With that key and before iOS 26: the content's size, no
7
+ /// view (`UIView<ANNComponentProtocol>`), never a class of the roots. In the glass design of iOS 26 (`ANNTabsDesign`)
8
+ /// the slot is 44 pt high and as wide as the content, at least 44 pt; the width follows the content both ways
9
+ /// (measured after every mount; a change over 0.5 pt only), and the slot's constraints live with the view, so a new
10
+ /// item built around it reuses them. In the compatibility mode and before iOS 26: the content's size, no
11
11
  /// constraints. A button rendered before the screen shows enters at its width. The core measures only the surface and
12
12
  /// never moves the views inside it: the JS wrapper centres them.
13
13
  @interface ANNBarButtonComponentView : UIView
@@ -7,7 +7,7 @@ static const CGFloat ANNBarButtonSlotHeight = 44;
7
7
  static const CGFloat ANNBarButtonSlotMinimumWidth = 44;
8
8
  static const CGFloat ANNBarButtonWidthTolerance = 0.5;
9
9
 
10
- // The slot of iOS 26's bar: unless the app keeps the earlier design (`UIDesignRequiresCompatibility`).
10
+ // The slot of iOS 26's bar: unless UIKit keeps the earlier design for the app (`ANNTabsDesign`).
11
11
  static BOOL ANNUsesSlotConstraints(void) {
12
12
  return ANNTabsDesign.isGlass;
13
13
  }
@@ -14,8 +14,9 @@ typedef NS_ENUM(NSInteger, ANNBarButtonsSide) {
14
14
 
15
15
  /// The buttons of one screen's bar and toolbar (`topBar.leftButtons[].*`, the side options, `overflowMenu`,
16
16
  /// `toolbar.buttons`): built from the resolved options, whole, at the screen's creation, at each appearance and at a
17
- /// merge of a side. Each button is an `ANNUIBarButtonItem` of the first kind its fields give (React view → icon and
18
- /// text → image button → SF Symbol → image → text → system item); a button without `id` is not placed. Side colors go
17
+ /// merge of a side, when anything the side's build reads changed or the item no longer holds what it put there.
18
+ /// Each button is an `ANNUIBarButtonItem` of the first kind its fields give (React view → icon and text → image
19
+ /// button → SF Symbol → image → text → system item); a button without `id` is not placed. Side colors go
19
20
  /// onto what is built, never into the options; what the user chose (a toggle, a menu's check) goes into the screen's
20
21
  /// user layer. The React buttons' slots are kept by component id, so a button built again reuses its slot. Main queue.
21
22
  @interface ANNBarButtons : NSObject
@@ -27,7 +28,8 @@ typedef NS_ENUM(NSInteger, ANNBarButtonsSide) {
27
28
  + (instancetype)new NS_UNAVAILABLE;
28
29
 
29
30
  /// Sets `side` of the screen's item (left, right, center) from `options`; `animated` fades new buttons in (a side set
30
- /// as groups changes at once).
31
+ /// as groups changes at once). Nothing when the side's buttons, colors, loaded images, React roots and traits are the
32
+ /// same as at its last build and the item still holds what that build set: iOS 26 animates even equal new items.
31
33
  - (void)applySide:(ANNBarButtonsSide)side
32
34
  options:(ANNNavigationOptions *)options
33
35
  animated:(BOOL)animated;
@@ -2,6 +2,7 @@
2
2
  #import "ANNBarButtonComponentView.h"
3
3
  #import "ANNIconButtonImage.h"
4
4
  #import "ANNShowTextButton.h"
5
+ #import "ANNTabsDesign.h"
5
6
  #import "ANNUIBarButtonItem.h"
6
7
  #import "ANNUserChoice.h"
7
8
  #import "UIViewController+LayoutProtocol.h"
@@ -301,6 +302,27 @@ static BOOL ANNHasComponent(ANNButtonOptions *button) {
301
302
  [button.component.componentId withDefault:nil].length > 0;
302
303
  }
303
304
 
305
+ // The image an icon loads, for telling builds apart (NSNull without one): a source of a file, data or Metro's server
306
+ // loads through the shared cache, which gives the same image until the file at its path changes or a failed load
307
+ // succeeds. An SF Symbol is the system's: its name in the options says it all.
308
+ static id ANNLoadedImageOf(Image *icon) {
309
+ ANNImageSource *source = icon.source;
310
+ if (!icon.hasValue || (source && source.kind == ANNImageSourceKindSymbol)) {
311
+ return NSNull.null;
312
+ }
313
+ return [icon withDefault:nil] ?: NSNull.null;
314
+ }
315
+
316
+ // The images the items of a menu load (an item with `sfSymbol` shows the symbol), at any depth.
317
+ static void ANNAddMenuImages(NSArray<ANNMenuItemOptions *> *items, NSMutableArray *loaded) {
318
+ for (ANNMenuItemOptions *item in items) {
319
+ if (!item.sfSymbol.hasValue) {
320
+ [loaded addObject:ANNLoadedImageOf(item.icon)];
321
+ }
322
+ ANNAddMenuImages(item.items, loaded);
323
+ }
324
+ }
325
+
304
326
  // The image button's background: its own `iconBackground.color`, else (only for an image icon) the side's.
305
327
  static UIColor *ANNIconBackgroundOf(ANNButtonOptions *button, Color *sideBackground) {
306
328
  UIColor *own = ANNColorOf(button.iconBackground.color);
@@ -394,6 +416,60 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
394
416
  @implementation ANNButtonPress
395
417
  @end
396
418
 
419
+ // The same objects, one by one: what a build read by identity (a loaded image, a React button's slot) and what it put
420
+ // on the navigation item. A string (the item's customization id) by its value.
421
+ static BOOL ANNSameObjects(NSArray *a, NSArray *b) {
422
+ if (a.count != b.count) {
423
+ return NO;
424
+ }
425
+ for (NSUInteger index = 0; index < a.count; index++) {
426
+ id mine = a[index];
427
+ id theirs = b[index];
428
+ if (mine != theirs && !([mine isKindOfClass:NSString.class] && [mine isEqual:theirs])) {
429
+ return NO;
430
+ }
431
+ }
432
+ return YES;
433
+ }
434
+
435
+ // Everything the build of one side reads: the side's buttons, its colors and the customization id of the middle by
436
+ // value; the images it loads and the slots of its React buttons by identity (a file written again at its path loads as
437
+ // another image, a reload gives other roots); the traits it builds under (a text button's font follows the text size,
438
+ // a drawn image the scale and the style).
439
+ @interface ANNSideInputs : NSObject
440
+ @property(nonatomic, strong, nullable) NSArray<ANNButtonOptions *> *buttons;
441
+ @property(nonatomic, strong, nullable) Color *color;
442
+ @property(nonatomic, strong, nullable) Color *disabledColor;
443
+ @property(nonatomic, strong, nullable) Color *background;
444
+ @property(nonatomic, strong, nullable) Text *customizationId;
445
+ @property(nonatomic, copy) NSArray *loaded;
446
+ @property(nonatomic, copy) NSArray *traits;
447
+ @end
448
+
449
+ @implementation ANNSideInputs
450
+
451
+ - (BOOL)isSameAs:(ANNSideInputs *)other {
452
+ return ANNOptionValuesSame(_buttons, other.buttons) &&
453
+ ANNOptionValuesSame(_color, other.color) &&
454
+ ANNOptionValuesSame(_disabledColor, other.disabledColor) &&
455
+ ANNOptionValuesSame(_background, other.background) &&
456
+ ANNOptionValuesSame(_customizationId, other.customizationId) &&
457
+ ANNSameObjects(_loaded, other.loaded) && [_traits isEqualToArray:other.traits];
458
+ }
459
+
460
+ @end
461
+
462
+ // The last build of one side: what it read and what the navigation item held right after it.
463
+ @interface ANNSideBuild : NSObject
464
+ @property(nonatomic, strong) ANNSideInputs *inputs;
465
+ // The side went in as groups (`leadingItemGroups`, …), not as a list of items.
466
+ @property(nonatomic) BOOL grouped;
467
+ @property(nonatomic, copy) NSArray *placed;
468
+ @end
469
+
470
+ @implementation ANNSideBuild
471
+ @end
472
+
397
473
  @implementation ANNBarButtons {
398
474
  __weak UIViewController *_screen;
399
475
  ANNReactComponentRegistry *_registry;
@@ -402,6 +478,8 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
402
478
  NSMutableDictionary<NSString *, NSNumber *> *_selected;
403
479
  // What a press of each sender (an item, a button of an item) means.
404
480
  NSMapTable<id, ANNButtonPress *> *_presses;
481
+ // The last build of each side, by the side.
482
+ NSMutableDictionary<NSNumber *, ANNSideBuild *> *_builds;
405
483
  }
406
484
 
407
485
  - (instancetype)initWithScreen:(UIViewController *)screen
@@ -412,12 +490,16 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
412
490
  _slots = [NSMutableDictionary new];
413
491
  _selected = [NSMutableDictionary new];
414
492
  _presses = [NSMapTable weakToStrongObjectsMapTable];
493
+ _builds = [NSMutableDictionary new];
415
494
  }
416
495
  return self;
417
496
  }
418
497
 
419
498
  #pragma mark - The sides
420
499
 
500
+ // From iOS 26 UIKit animates any change of the bar's items, also to equal ones and with `animated:NO`: new items on
501
+ // each appearance would morph, close an open menu and drop a pressed state. So a side is built again only when what
502
+ // its build reads changed, or when the item no longer holds what the last build put there.
421
503
  - (void)applySide:(ANNBarButtonsSide)side
422
504
  options:(ANNNavigationOptions *)options
423
505
  animated:(BOOL)animated {
@@ -426,12 +508,92 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
426
508
  return;
427
509
  }
428
510
  UINavigationItem *navigationItem = screen.navigationItem;
511
+ ANNSideInputs *inputs = [self inputsOfSide:side options:options];
512
+ ANNSideBuild *last = _builds[@(side)];
513
+ if (last && [last.inputs isSameAs:inputs]) {
514
+ NSArray *placed = [self placedOf:side grouped:last.grouped item:navigationItem];
515
+ if (ANNSameObjects(last.placed, placed)) {
516
+ return;
517
+ }
518
+ }
519
+ ANNSideBuild *build = [ANNSideBuild new];
520
+ build.inputs = inputs;
521
+ build.grouped = [self placeSide:side options:options item:navigationItem animated:animated];
522
+ build.placed = [self placedOf:side grouped:build.grouped item:navigationItem];
523
+ _builds[@(side)] = build;
524
+ }
525
+
526
+ - (ANNSideInputs *)inputsOfSide:(ANNBarButtonsSide)side options:(ANNNavigationOptions *)options {
527
+ ANNTopBarOptions *topBar = options.topBar;
528
+ ANNSideInputs *inputs = [ANNSideInputs new];
529
+ inputs.buttons = ANNButtonsOfSide(options, side);
530
+ inputs.color = ANNSideColor(topBar, side);
531
+ inputs.disabledColor = ANNSideDisabledColor(topBar, side);
532
+ inputs.background = ANNSideBackgroundColor(topBar, side);
533
+ inputs.customizationId = side == ANNBarButtonsSideCenter ? topBar.customizationId : nil;
534
+ NSMutableArray *loaded = [NSMutableArray new];
535
+ for (ANNButtonOptions *button in inputs.buttons ?: @[]) {
536
+ if (ANNHasComponent(button)) {
537
+ [loaded addObject:[self slotFor:button] ?: NSNull.null];
538
+ }
539
+ [loaded addObject:ANNLoadedImageOf(button.icon)];
540
+ ANNAddMenuImages(button.menu.items, loaded);
541
+ }
542
+ inputs.loaded = loaded;
543
+ UITraitCollection *traits = UITraitCollection.currentTraitCollection;
544
+ inputs.traits = @[
545
+ @(traits.userInterfaceStyle), @(traits.displayScale),
546
+ traits.preferredContentSizeCategory ?: @"", @(traits.legibilityWeight)
547
+ ];
548
+ return inputs;
549
+ }
550
+
551
+ // What the navigation item holds of `side` now, as the side's build put it there.
552
+ - (NSArray *)placedOf:(ANNBarButtonsSide)side
553
+ grouped:(BOOL)grouped
554
+ item:(UINavigationItem *)navigationItem {
555
+ NSMutableArray *placed = [NSMutableArray new];
556
+ BOOL left = side == ANNBarButtonsSideLeft;
557
+ switch (side) {
558
+ case ANNBarButtonsSideLeft:
559
+ case ANNBarButtonsSideRight: {
560
+ NSArray *shown =
561
+ left ? navigationItem.leftBarButtonItems : navigationItem.rightBarButtonItems;
562
+ if (@available(iOS 16.0, *)) {
563
+ if (grouped) {
564
+ shown = left ? navigationItem.leadingItemGroups
565
+ : navigationItem.trailingItemGroups;
566
+ }
567
+ if (!left) {
568
+ [placed addObject:navigationItem.pinnedTrailingGroup ?: NSNull.null];
569
+ }
570
+ }
571
+ [placed addObjectsFromArray:shown ?: @[]];
572
+ break;
573
+ }
574
+ case ANNBarButtonsSideCenter:
575
+ if (@available(iOS 16.0, *)) {
576
+ [placed addObjectsFromArray:navigationItem.centerItemGroups ?: @[]];
577
+ [placed addObject:navigationItem.customizationIdentifier ?: NSNull.null];
578
+ }
579
+ break;
580
+ case ANNBarButtonsSideToolbar:
581
+ break;
582
+ }
583
+ return placed;
584
+ }
585
+
586
+ // Builds `side` whole and puts it on the item; YES when it went in as groups.
587
+ - (BOOL)placeSide:(ANNBarButtonsSide)side
588
+ options:(ANNNavigationOptions *)options
589
+ item:(UINavigationItem *)navigationItem
590
+ animated:(BOOL)animated {
429
591
  NSArray<ANNBuiltButton *> *built = [self buildSide:side options:options];
430
592
  if (side == ANNBarButtonsSideCenter) {
431
593
  if (@available(iOS 16.0, *)) {
432
594
  [self setCenter:built options:options item:navigationItem];
433
595
  }
434
- return;
596
+ return YES;
435
597
  }
436
598
  NSMutableArray<ANNBuiltButton *> *placed = [built mutableCopy];
437
599
  if (side == ANNBarButtonsSideRight) {
@@ -440,26 +602,28 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
440
602
  }
441
603
  }
442
604
  if (@available(iOS 26.0, *)) {
443
- if ([self hasGroupOfSeveral:placed]) {
605
+ if (ANNTabsDesign.isGlass && [self hasGroupOfSeveral:placed]) {
444
606
  NSArray<UIBarButtonItemGroup *> *groups = [self groupsOf:placed];
445
607
  if (side == ANNBarButtonsSideLeft) {
446
608
  navigationItem.leadingItemGroups = groups;
447
609
  } else {
448
610
  navigationItem.trailingItemGroups = groups.reverseObjectEnumerator.allObjects;
449
611
  }
450
- return;
612
+ return YES;
451
613
  }
452
614
  }
453
615
  NSMutableArray<UIBarButtonItem *> *items = [NSMutableArray new];
454
616
  for (ANNBuiltButton *button in placed) {
455
617
  if (@available(iOS 26.0, *)) {
456
618
  // Each button its own capsule: a zero-width space between neighbours, and the right ones never share the
457
- // capsule of "•••".
458
- if (items.count > 0) {
459
- [items addObject:[UIBarButtonItem fixedSpaceItemOfWidth:0]];
460
- }
461
- if (side == ANNBarButtonsSideRight) {
462
- button.item.sharesBackground = NO;
619
+ // capsule of "•••". The compatibility mode has no capsules.
620
+ if (ANNTabsDesign.isGlass) {
621
+ if (items.count > 0) {
622
+ [items addObject:[UIBarButtonItem fixedSpaceItemOfWidth:0]];
623
+ }
624
+ if (side == ANNBarButtonsSideRight) {
625
+ button.item.sharesBackground = NO;
626
+ }
463
627
  }
464
628
  }
465
629
  [items addObject:button.item];
@@ -469,6 +633,7 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
469
633
  } else {
470
634
  [navigationItem setRightBarButtonItems:items animated:animated];
471
635
  }
636
+ return NO;
472
637
  }
473
638
 
474
639
  - (NSArray<ANNBuiltButton *> *)buildSide:(ANNBarButtonsSide)side
@@ -559,7 +724,7 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
559
724
  NSMutableArray<UIBarButtonItem *> *items = [NSMutableArray new];
560
725
  for (ANNBuiltButton *button in run) {
561
726
  if (@available(iOS 26.0, *)) {
562
- if (run.count == 1) {
727
+ if (run.count == 1 && ANNTabsDesign.isGlass) {
563
728
  button.item.sharesBackground = NO;
564
729
  }
565
730
  }
@@ -667,7 +832,7 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
667
832
  BOOL icon = kind == ANNBarButtonKindSymbol || kind == ANNBarButtonKindImage ||
668
833
  kind == ANNBarButtonKindImageButton || kind == ANNBarButtonKindShowText;
669
834
  NSString *group = icon ? [button.group withDefault:nil] : nil;
670
- if (@available(iOS 26.0, *)) {
835
+ if (ANNTabsDesign.isGlass) {
671
836
  // Only icons of one group share a capsule (the toolbar has no groups): any other neighbours get a
672
837
  // zero-width space, unless a space of the options is there already.
673
838
  BOOL sameGroup = group.length > 0 && [group isEqualToString:previousIconGroup];
@@ -835,10 +1000,12 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
835
1000
  }
836
1001
  BOOL prominent = [[button.style withDefault:nil] isEqualToString:@"prominent"];
837
1002
  if (prominent) {
1003
+ // The capsule of the glass; the design before it (and its compatibility mode) has the bold `done` style.
1004
+ item.style = UIBarButtonItemStyleDone;
838
1005
  if (@available(iOS 26.0, *)) {
839
- item.style = UIBarButtonItemStyleProminent;
840
- } else {
841
- item.style = UIBarButtonItemStyleDone;
1006
+ if (ANNTabsDesign.isGlass) {
1007
+ item.style = UIBarButtonItemStyleProminent;
1008
+ }
842
1009
  }
843
1010
  }
844
1011
  // The color: the tint, and for a plain button also the text's; `prominent` takes it only into the tint, and a font
@@ -1021,9 +1188,11 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
1021
1188
  kind:(ANNBarButtonKind)kind
1022
1189
  to:(UIBarButtonItem *)item {
1023
1190
  if (@available(iOS 26.0, *)) {
1024
- BOOL ownBackground =
1025
- kind == ANNBarButtonKindComponent || kind == ANNBarButtonKindImageButton;
1026
- item.hidesSharedBackground = [button.hideSharedBackground withDefault:ownBackground];
1191
+ if (ANNTabsDesign.isGlass) {
1192
+ BOOL ownBackground =
1193
+ kind == ANNBarButtonKindComponent || kind == ANNBarButtonKindImageButton;
1194
+ item.hidesSharedBackground = [button.hideSharedBackground withDefault:ownBackground];
1195
+ }
1027
1196
  }
1028
1197
  #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 270000
1029
1198
  if (@available(iOS 27.0, *)) {
@@ -3,6 +3,13 @@
3
3
  #import "ANNBarAppearance.h"
4
4
  #import "ANNMainList.h"
5
5
  #import "ANNScreenBars.h"
6
+ #import "ANNTabsDesign.h"
7
+
8
+ // The glass design of iOS 26 draws the large title inside the list the bar follows: the places, fills and bands of
9
+ // this file are for it. The compatibility mode keeps the title in the bar, as before iOS 26 (`ANNTabsDesign`).
10
+ static BOOL ANNTitleInList(void) {
11
+ return ANNTabsDesign.isGlass;
12
+ }
6
13
 
7
14
  // How far the color reaches above the list's content: as far as a pull can show.
8
15
  static const CGFloat ANNListFillHeight = 2000;
@@ -58,10 +65,8 @@ static UINavigationItemLargeTitleDisplayMode ANNItemModeOf(NSString *mode) {
58
65
  ![options.topBar.largeTitle.collapseOnScroll withDefault:YES]) {
59
66
  return NO;
60
67
  }
61
- if (@available(iOS 26.0, *)) {
62
- if (ANNAppDirection.reactNativeIsRightToLeft) {
63
- return NO;
64
- }
68
+ if (ANNTitleInList() && ANNAppDirection.reactNativeIsRightToLeft) {
69
+ return NO;
65
70
  }
66
71
  return YES;
67
72
  }
@@ -132,6 +137,9 @@ static UINavigationItemLargeTitleDisplayMode ANNItemModeOf(NSString *mode) {
132
137
  - (void)screenInPop:(ANNNavigationOptions *)options
133
138
  list:(UIScrollView *)list
134
139
  inlineInRow:(BOOL)inlineInRow {
140
+ if (!ANNTitleInList()) {
141
+ return;
142
+ }
135
143
  if (@available(iOS 26.0, *)) {
136
144
  UIViewController *screen = _screen;
137
145
  UIView *screenView = screen.viewIfLoaded;
@@ -168,6 +176,9 @@ static UINavigationItemLargeTitleDisplayMode ANNItemModeOf(NSString *mode) {
168
176
  inlineInRow:(BOOL)inlineInRow
169
177
  top:(CGFloat)top
170
178
  stackTop:(CGFloat)stackTop {
179
+ if (!ANNTitleInList()) {
180
+ return;
181
+ }
171
182
  if (@available(iOS 26.0, *)) {
172
183
  UIView *screenView = _screen.viewIfLoaded;
173
184
  if (!screenView || inlineInRow || !ANNLargeTitleCanShow(options) || top - stackTop <= 0) {
@@ -282,6 +293,9 @@ static UINavigationItemLargeTitleDisplayMode ANNItemModeOf(NSString *mode) {
282
293
  list:(UIScrollView *)list
283
294
  inlineInRow:(BOOL)inlineInRow
284
295
  backgroundComponent:(BOOL)backgroundComponent {
296
+ if (!ANNTitleInList()) {
297
+ return;
298
+ }
285
299
  if (@available(iOS 26.0, *)) {
286
300
  UIViewController *screen = _screen;
287
301
  UIColor *fill = [ANNBarAppearance solidColorOf:options.topBar.background.color
@@ -320,6 +334,9 @@ static UINavigationItemLargeTitleDisplayMode ANNItemModeOf(NSString *mode) {
320
334
  - (void)fillTitlePlaceFrom:(CGFloat)oldBarBottom
321
335
  list:(UIScrollView *)list
322
336
  options:(ANNNavigationOptions *)options {
337
+ if (!ANNTitleInList()) {
338
+ return;
339
+ }
323
340
  if (@available(iOS 26.0, *)) {
324
341
  [self removeTitlePlaceFill];
325
342
  if (!ANNLargeTitleCanShow(options) || [ANNLargeTitlePresenter collapsesIntoList:options]) {
@@ -334,6 +351,9 @@ static UINavigationItemLargeTitleDisplayMode ANNItemModeOf(NSString *mode) {
334
351
  // The pushed screen's content may render only during the push, so its list mounts later than the push starts: the
335
352
  // fill is placed again at each mount and layout until the screen appeared.
336
353
  - (void)updateTitlePlaceFill:(ANNNavigationOptions *)options list:(UIScrollView *)list {
354
+ if (!ANNTitleInList()) {
355
+ return;
356
+ }
337
357
  if (@available(iOS 26.0, *)) {
338
358
  UIViewController *screen = _screen;
339
359
  UIView *screenView = screen.viewIfLoaded;