@symbiote-native/navigation 1.0.0

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 (455) hide show
  1. package/LICENSE +21 -0
  2. package/build/angular/drawer/index.d.ts +76 -0
  3. package/build/angular/drawer/index.js +508 -0
  4. package/build/angular/drawer-screen.directive.d.ts +14 -0
  5. package/build/angular/drawer-screen.directive.js +87 -0
  6. package/build/angular/index.d.ts +23 -0
  7. package/build/angular/index.js +22 -0
  8. package/build/angular/injectors/index.d.ts +13 -0
  9. package/build/angular/injectors/index.js +8 -0
  10. package/build/angular/injectors/inject-drawer-navigation.d.ts +4 -0
  11. package/build/angular/injectors/inject-drawer-navigation.js +12 -0
  12. package/build/angular/injectors/inject-focus-effect.d.ts +2 -0
  13. package/build/angular/injectors/inject-focus-effect.js +29 -0
  14. package/build/angular/injectors/inject-is-focused.d.ts +2 -0
  15. package/build/angular/injectors/inject-is-focused.js +20 -0
  16. package/build/angular/injectors/inject-navigation-state.d.ts +3 -0
  17. package/build/angular/injectors/inject-navigation-state.js +35 -0
  18. package/build/angular/injectors/inject-navigation.d.ts +7 -0
  19. package/build/angular/injectors/inject-navigation.js +16 -0
  20. package/build/angular/injectors/inject-route.d.ts +3 -0
  21. package/build/angular/injectors/inject-route.js +20 -0
  22. package/build/angular/injectors/inject-stack-navigation.d.ts +4 -0
  23. package/build/angular/injectors/inject-stack-navigation.js +15 -0
  24. package/build/angular/injectors/inject-tab-navigation.d.ts +4 -0
  25. package/build/angular/injectors/inject-tab-navigation.js +11 -0
  26. package/build/angular/linking.d.ts +3 -0
  27. package/build/angular/linking.js +43 -0
  28. package/build/angular/navigation-context.service.d.ts +13 -0
  29. package/build/angular/navigation-context.service.js +105 -0
  30. package/build/angular/navigation-scope.directive.d.ts +10 -0
  31. package/build/angular/navigation-scope.directive.js +90 -0
  32. package/build/angular/screen.directive.d.ts +22 -0
  33. package/build/angular/screen.directive.js +90 -0
  34. package/build/angular/search-bar-ref.directive.d.ts +10 -0
  35. package/build/angular/search-bar-ref.directive.js +88 -0
  36. package/build/angular/stack/index.d.ts +55 -0
  37. package/build/angular/stack/index.js +386 -0
  38. package/build/angular/tab-screen.directive.d.ts +14 -0
  39. package/build/angular/tab-screen.directive.js +87 -0
  40. package/build/angular/tabs/index.d.ts +42 -0
  41. package/build/angular/tabs/index.js +263 -0
  42. package/build/core/constants.d.ts +27 -0
  43. package/build/core/constants.js +61 -0
  44. package/build/core/drawer-options/index.d.ts +64 -0
  45. package/build/core/drawer-options/index.js +200 -0
  46. package/build/core/drawer-router-state/index.d.ts +21 -0
  47. package/build/core/drawer-router-state/index.js +40 -0
  48. package/build/core/guards.d.ts +2 -0
  49. package/build/core/guards.js +22 -0
  50. package/build/core/header-bar-buttons.d.ts +9 -0
  51. package/build/core/header-bar-buttons.js +163 -0
  52. package/build/core/index.d.ts +29 -0
  53. package/build/core/index.js +32 -0
  54. package/build/core/linking-config/index.d.ts +13 -0
  55. package/build/core/linking-config/index.js +171 -0
  56. package/build/core/navigation-events/index.d.ts +16 -0
  57. package/build/core/navigation-events/index.js +57 -0
  58. package/build/core/navigator-handles/index.d.ts +30 -0
  59. package/build/core/navigator-handles/index.js +15 -0
  60. package/build/core/navigator-props.d.ts +216 -0
  61. package/build/core/navigator-props.js +7 -0
  62. package/build/core/navigator-state/index.d.ts +36 -0
  63. package/build/core/navigator-state/index.js +81 -0
  64. package/build/core/render-drawer.d.ts +13 -0
  65. package/build/core/render-drawer.js +88 -0
  66. package/build/core/render-stack.d.ts +35 -0
  67. package/build/core/render-stack.js +294 -0
  68. package/build/core/render-tabs.d.ts +20 -0
  69. package/build/core/render-tabs.js +97 -0
  70. package/build/core/screen-options.d.ts +4 -0
  71. package/build/core/screen-options.js +133 -0
  72. package/build/core/search-bar-commands.d.ts +10 -0
  73. package/build/core/search-bar-commands.js +42 -0
  74. package/build/core/state-persistence/index.d.ts +3 -0
  75. package/build/core/state-persistence/index.js +23 -0
  76. package/build/core/tab-options.d.ts +13 -0
  77. package/build/core/tab-options.js +10 -0
  78. package/build/core/tab-router-state/index.d.ts +19 -0
  79. package/build/core/tab-router-state/index.js +51 -0
  80. package/build/react/collect-registry.d.ts +4 -0
  81. package/build/react/collect-registry.js +15 -0
  82. package/build/react/drawer/index.d.ts +29 -0
  83. package/build/react/drawer/index.js +277 -0
  84. package/build/react/drawer-screen.d.ts +14 -0
  85. package/build/react/drawer-screen.js +8 -0
  86. package/build/react/hooks/index.d.ts +12 -0
  87. package/build/react/hooks/index.js +8 -0
  88. package/build/react/hooks/use-drawer-navigation.d.ts +4 -0
  89. package/build/react/hooks/use-drawer-navigation.js +12 -0
  90. package/build/react/hooks/use-focus-effect.d.ts +2 -0
  91. package/build/react/hooks/use-focus-effect.js +29 -0
  92. package/build/react/hooks/use-is-focused.d.ts +1 -0
  93. package/build/react/hooks/use-is-focused.js +22 -0
  94. package/build/react/hooks/use-navigation-state/index.d.ts +2 -0
  95. package/build/react/hooks/use-navigation-state/index.js +39 -0
  96. package/build/react/hooks/use-navigation.d.ts +7 -0
  97. package/build/react/hooks/use-navigation.js +16 -0
  98. package/build/react/hooks/use-route.d.ts +2 -0
  99. package/build/react/hooks/use-route.js +8 -0
  100. package/build/react/hooks/use-stack-navigation.d.ts +4 -0
  101. package/build/react/hooks/use-stack-navigation.js +14 -0
  102. package/build/react/hooks/use-tab-navigation.d.ts +4 -0
  103. package/build/react/hooks/use-tab-navigation.js +12 -0
  104. package/build/react/index.d.ts +24 -0
  105. package/build/react/index.js +21 -0
  106. package/build/react/linking.d.ts +3 -0
  107. package/build/react/linking.js +47 -0
  108. package/build/react/navigation-context.d.ts +10 -0
  109. package/build/react/navigation-context.js +22 -0
  110. package/build/react/screen.d.ts +22 -0
  111. package/build/react/screen.js +7 -0
  112. package/build/react/stack/index.d.ts +13 -0
  113. package/build/react/stack/index.js +202 -0
  114. package/build/react/tab-screen.d.ts +15 -0
  115. package/build/react/tab-screen.js +8 -0
  116. package/build/react/tabs/index.d.ts +12 -0
  117. package/build/react/tabs/index.js +116 -0
  118. package/build/register.d.ts +7 -0
  119. package/build/register.js +58 -0
  120. package/build/vue/composables/index.d.ts +12 -0
  121. package/build/vue/composables/index.js +8 -0
  122. package/build/vue/composables/use-drawer-navigation.d.ts +5 -0
  123. package/build/vue/composables/use-drawer-navigation.js +15 -0
  124. package/build/vue/composables/use-focus-effect.d.ts +1 -0
  125. package/build/vue/composables/use-focus-effect.js +31 -0
  126. package/build/vue/composables/use-is-focused.d.ts +2 -0
  127. package/build/vue/composables/use-is-focused.js +28 -0
  128. package/build/vue/composables/use-navigation-state.d.ts +3 -0
  129. package/build/vue/composables/use-navigation-state.js +29 -0
  130. package/build/vue/composables/use-navigation.d.ts +8 -0
  131. package/build/vue/composables/use-navigation.js +13 -0
  132. package/build/vue/composables/use-route.d.ts +3 -0
  133. package/build/vue/composables/use-route.js +9 -0
  134. package/build/vue/composables/use-stack-navigation.d.ts +5 -0
  135. package/build/vue/composables/use-stack-navigation.js +18 -0
  136. package/build/vue/composables/use-tab-navigation.d.ts +5 -0
  137. package/build/vue/composables/use-tab-navigation.js +15 -0
  138. package/build/vue/drawer/index.d.ts +19 -0
  139. package/build/vue/drawer/index.js +390 -0
  140. package/build/vue/drawer-screen.d.ts +14 -0
  141. package/build/vue/drawer-screen.js +12 -0
  142. package/build/vue/index.d.ts +21 -0
  143. package/build/vue/index.js +19 -0
  144. package/build/vue/linking.d.ts +3 -0
  145. package/build/vue/linking.js +57 -0
  146. package/build/vue/navigation-context.d.ts +16 -0
  147. package/build/vue/navigation-context.js +65 -0
  148. package/build/vue/screen.d.ts +21 -0
  149. package/build/vue/screen.js +12 -0
  150. package/build/vue/stack/index.d.ts +9 -0
  151. package/build/vue/stack/index.js +279 -0
  152. package/build/vue/tab-screen.d.ts +14 -0
  153. package/build/vue/tab-screen.js +11 -0
  154. package/build/vue/tabs/index.d.ts +10 -0
  155. package/build/vue/tabs/index.js +209 -0
  156. package/build-ngc/angular/drawer/index.d.ts +79 -0
  157. package/build-ngc/angular/drawer/index.js +479 -0
  158. package/build-ngc/angular/drawer/index.js.map +1 -0
  159. package/build-ngc/angular/drawer-screen.directive.d.ts +17 -0
  160. package/build-ngc/angular/drawer-screen.directive.js +32 -0
  161. package/build-ngc/angular/drawer-screen.directive.js.map +1 -0
  162. package/build-ngc/angular/drawer.d.ts +79 -0
  163. package/build-ngc/angular/drawer.js +475 -0
  164. package/build-ngc/angular/drawer.js.map +1 -0
  165. package/build-ngc/angular/index.d.ts +23 -0
  166. package/build-ngc/angular/index.js +23 -0
  167. package/build-ngc/angular/index.js.map +1 -0
  168. package/build-ngc/angular/injectors/index.d.ts +13 -0
  169. package/build-ngc/angular/injectors/index.js +9 -0
  170. package/build-ngc/angular/injectors/index.js.map +1 -0
  171. package/build-ngc/angular/injectors/inject-drawer-navigation.d.ts +4 -0
  172. package/build-ngc/angular/injectors/inject-drawer-navigation.js +13 -0
  173. package/build-ngc/angular/injectors/inject-drawer-navigation.js.map +1 -0
  174. package/build-ngc/angular/injectors/inject-focus-effect.d.ts +2 -0
  175. package/build-ngc/angular/injectors/inject-focus-effect.js +30 -0
  176. package/build-ngc/angular/injectors/inject-focus-effect.js.map +1 -0
  177. package/build-ngc/angular/injectors/inject-is-focused.d.ts +2 -0
  178. package/build-ngc/angular/injectors/inject-is-focused.js +22 -0
  179. package/build-ngc/angular/injectors/inject-is-focused.js.map +1 -0
  180. package/build-ngc/angular/injectors/inject-navigation-state.d.ts +3 -0
  181. package/build-ngc/angular/injectors/inject-navigation-state.js +37 -0
  182. package/build-ngc/angular/injectors/inject-navigation-state.js.map +1 -0
  183. package/build-ngc/angular/injectors/inject-navigation.d.ts +7 -0
  184. package/build-ngc/angular/injectors/inject-navigation.js +17 -0
  185. package/build-ngc/angular/injectors/inject-navigation.js.map +1 -0
  186. package/build-ngc/angular/injectors/inject-route.d.ts +3 -0
  187. package/build-ngc/angular/injectors/inject-route.js +21 -0
  188. package/build-ngc/angular/injectors/inject-route.js.map +1 -0
  189. package/build-ngc/angular/injectors/inject-stack-navigation.d.ts +4 -0
  190. package/build-ngc/angular/injectors/inject-stack-navigation.js +16 -0
  191. package/build-ngc/angular/injectors/inject-stack-navigation.js.map +1 -0
  192. package/build-ngc/angular/injectors/inject-tab-navigation.d.ts +4 -0
  193. package/build-ngc/angular/injectors/inject-tab-navigation.js +12 -0
  194. package/build-ngc/angular/injectors/inject-tab-navigation.js.map +1 -0
  195. package/build-ngc/angular/linking.d.ts +3 -0
  196. package/build-ngc/angular/linking.js +44 -0
  197. package/build-ngc/angular/linking.js.map +1 -0
  198. package/build-ngc/angular/navigation-context.service.d.ts +16 -0
  199. package/build-ngc/angular/navigation-context.service.js +63 -0
  200. package/build-ngc/angular/navigation-context.service.js.map +1 -0
  201. package/build-ngc/angular/navigation-scope.directive.d.ts +13 -0
  202. package/build-ngc/angular/navigation-scope.directive.js +42 -0
  203. package/build-ngc/angular/navigation-scope.directive.js.map +1 -0
  204. package/build-ngc/angular/screen.directive.d.ts +25 -0
  205. package/build-ngc/angular/screen.directive.js +35 -0
  206. package/build-ngc/angular/screen.directive.js.map +1 -0
  207. package/build-ngc/angular/search-bar-ref.directive.d.ts +13 -0
  208. package/build-ngc/angular/search-bar-ref.directive.js +44 -0
  209. package/build-ngc/angular/search-bar-ref.directive.js.map +1 -0
  210. package/build-ngc/angular/stack/index.d.ts +58 -0
  211. package/build-ngc/angular/stack/index.js +406 -0
  212. package/build-ngc/angular/stack/index.js.map +1 -0
  213. package/build-ngc/angular/stack.d.ts +58 -0
  214. package/build-ngc/angular/stack.js +402 -0
  215. package/build-ngc/angular/stack.js.map +1 -0
  216. package/build-ngc/angular/tab-screen.directive.d.ts +17 -0
  217. package/build-ngc/angular/tab-screen.directive.js +32 -0
  218. package/build-ngc/angular/tab-screen.directive.js.map +1 -0
  219. package/build-ngc/angular/tabs/index.d.ts +45 -0
  220. package/build-ngc/angular/tabs/index.js +231 -0
  221. package/build-ngc/angular/tabs/index.js.map +1 -0
  222. package/build-ngc/angular/tabs.d.ts +45 -0
  223. package/build-ngc/angular/tabs.js +231 -0
  224. package/build-ngc/angular/tabs.js.map +1 -0
  225. package/build-ngc/core/constants.d.ts +27 -0
  226. package/build-ngc/core/constants.js +62 -0
  227. package/build-ngc/core/constants.js.map +1 -0
  228. package/build-ngc/core/drawer-options/index.d.ts +64 -0
  229. package/build-ngc/core/drawer-options/index.js +201 -0
  230. package/build-ngc/core/drawer-options/index.js.map +1 -0
  231. package/build-ngc/core/drawer-options.d.ts +64 -0
  232. package/build-ngc/core/drawer-options.js +201 -0
  233. package/build-ngc/core/drawer-options.js.map +1 -0
  234. package/build-ngc/core/drawer-router-state/index.d.ts +21 -0
  235. package/build-ngc/core/drawer-router-state/index.js +41 -0
  236. package/build-ngc/core/drawer-router-state/index.js.map +1 -0
  237. package/build-ngc/core/drawer-router-state.d.ts +21 -0
  238. package/build-ngc/core/drawer-router-state.js +41 -0
  239. package/build-ngc/core/drawer-router-state.js.map +1 -0
  240. package/build-ngc/core/guards.d.ts +2 -0
  241. package/build-ngc/core/guards.js +23 -0
  242. package/build-ngc/core/guards.js.map +1 -0
  243. package/build-ngc/core/header-bar-buttons.d.ts +9 -0
  244. package/build-ngc/core/header-bar-buttons.js +164 -0
  245. package/build-ngc/core/header-bar-buttons.js.map +1 -0
  246. package/build-ngc/core/href.d.ts +16 -0
  247. package/build-ngc/core/href.js +55 -0
  248. package/build-ngc/core/href.js.map +1 -0
  249. package/build-ngc/core/index.d.ts +29 -0
  250. package/build-ngc/core/index.js +33 -0
  251. package/build-ngc/core/index.js.map +1 -0
  252. package/build-ngc/core/linking-config/index.d.ts +13 -0
  253. package/build-ngc/core/linking-config/index.js +172 -0
  254. package/build-ngc/core/linking-config/index.js.map +1 -0
  255. package/build-ngc/core/linking-config.d.ts +13 -0
  256. package/build-ngc/core/linking-config.js +172 -0
  257. package/build-ngc/core/linking-config.js.map +1 -0
  258. package/build-ngc/core/navigation-events/index.d.ts +16 -0
  259. package/build-ngc/core/navigation-events/index.js +58 -0
  260. package/build-ngc/core/navigation-events/index.js.map +1 -0
  261. package/build-ngc/core/navigation-events.d.ts +16 -0
  262. package/build-ngc/core/navigation-events.js +58 -0
  263. package/build-ngc/core/navigation-events.js.map +1 -0
  264. package/build-ngc/core/navigator-handles/index.d.ts +30 -0
  265. package/build-ngc/core/navigator-handles/index.js +16 -0
  266. package/build-ngc/core/navigator-handles/index.js.map +1 -0
  267. package/build-ngc/core/navigator-handles.d.ts +30 -0
  268. package/build-ngc/core/navigator-handles.js +16 -0
  269. package/build-ngc/core/navigator-handles.js.map +1 -0
  270. package/build-ngc/core/navigator-props.d.ts +216 -0
  271. package/build-ngc/core/navigator-props.js +8 -0
  272. package/build-ngc/core/navigator-props.js.map +1 -0
  273. package/build-ngc/core/navigator-state/index.d.ts +36 -0
  274. package/build-ngc/core/navigator-state/index.js +82 -0
  275. package/build-ngc/core/navigator-state/index.js.map +1 -0
  276. package/build-ngc/core/navigator-state.d.ts +36 -0
  277. package/build-ngc/core/navigator-state.js +82 -0
  278. package/build-ngc/core/navigator-state.js.map +1 -0
  279. package/build-ngc/core/render-drawer.d.ts +13 -0
  280. package/build-ngc/core/render-drawer.js +89 -0
  281. package/build-ngc/core/render-drawer.js.map +1 -0
  282. package/build-ngc/core/render-stack.d.ts +35 -0
  283. package/build-ngc/core/render-stack.js +295 -0
  284. package/build-ngc/core/render-stack.js.map +1 -0
  285. package/build-ngc/core/render-tabs.d.ts +20 -0
  286. package/build-ngc/core/render-tabs.js +98 -0
  287. package/build-ngc/core/render-tabs.js.map +1 -0
  288. package/build-ngc/core/screen-options.d.ts +4 -0
  289. package/build-ngc/core/screen-options.js +134 -0
  290. package/build-ngc/core/screen-options.js.map +1 -0
  291. package/build-ngc/core/search-bar-commands.d.ts +10 -0
  292. package/build-ngc/core/search-bar-commands.js +43 -0
  293. package/build-ngc/core/search-bar-commands.js.map +1 -0
  294. package/build-ngc/core/search-params.d.ts +9 -0
  295. package/build-ngc/core/search-params.js +60 -0
  296. package/build-ngc/core/search-params.js.map +1 -0
  297. package/build-ngc/core/state-persistence/index.d.ts +3 -0
  298. package/build-ngc/core/state-persistence/index.js +24 -0
  299. package/build-ngc/core/state-persistence/index.js.map +1 -0
  300. package/build-ngc/core/state-persistence.d.ts +3 -0
  301. package/build-ngc/core/state-persistence.js +24 -0
  302. package/build-ngc/core/state-persistence.js.map +1 -0
  303. package/build-ngc/core/tab-options.d.ts +13 -0
  304. package/build-ngc/core/tab-options.js +11 -0
  305. package/build-ngc/core/tab-options.js.map +1 -0
  306. package/build-ngc/core/tab-router-state/index.d.ts +19 -0
  307. package/build-ngc/core/tab-router-state/index.js +52 -0
  308. package/build-ngc/core/tab-router-state/index.js.map +1 -0
  309. package/build-ngc/core/tab-router-state.d.ts +19 -0
  310. package/build-ngc/core/tab-router-state.js +52 -0
  311. package/build-ngc/core/tab-router-state.js.map +1 -0
  312. package/build-ngc/register.d.ts +7 -0
  313. package/build-ngc/register.js +59 -0
  314. package/build-ngc/register.js.map +1 -0
  315. package/codegen-specs/FullWindowOverlayNativeComponent.ts +13 -0
  316. package/codegen-specs/ModalScreenNativeComponent.ts +129 -0
  317. package/codegen-specs/NativeScreensModule.ts +8 -0
  318. package/codegen-specs/NativeScreensModule.web.ts +1 -0
  319. package/codegen-specs/ScreenContainerNativeComponent.ts +8 -0
  320. package/codegen-specs/ScreenContentWrapperNativeComponent.ts +9 -0
  321. package/codegen-specs/ScreenFooterNativeComponent.ts +6 -0
  322. package/codegen-specs/ScreenNativeComponent.ts +133 -0
  323. package/codegen-specs/ScreenNavigationContainerNativeComponent.ts +11 -0
  324. package/codegen-specs/ScreenStackHeaderConfigNativeComponent.ts +99 -0
  325. package/codegen-specs/ScreenStackHeaderSubviewNativeComponent.ts +25 -0
  326. package/codegen-specs/ScreenStackNativeComponent.ts +17 -0
  327. package/codegen-specs/SearchBarNativeComponent.ts +101 -0
  328. package/codegen-specs/codegenUtils.ts +6 -0
  329. package/codegen-specs/gamma/ScrollViewMarkerNativeComponent.ts +15 -0
  330. package/codegen-specs/gamma/modals/form-sheet/FormSheetContentWrapperNativeComponent.ts +10 -0
  331. package/codegen-specs/gamma/modals/form-sheet/FormSheetHostNativeComponent.ts +47 -0
  332. package/codegen-specs/gamma/split/SplitHostNativeComponent.ts +124 -0
  333. package/codegen-specs/gamma/split/SplitScreenNativeComponent.ts +24 -0
  334. package/codegen-specs/gamma/stack/StackHeaderConfigAndroidNativeComponent.ts +144 -0
  335. package/codegen-specs/gamma/stack/StackHeaderConfigIOSNativeComponent.ts +34 -0
  336. package/codegen-specs/gamma/stack/StackHeaderItemIOSNativeComponent.ts +47 -0
  337. package/codegen-specs/gamma/stack/StackHeaderItemSpacerIOSNativeComponent.ts +20 -0
  338. package/codegen-specs/gamma/stack/StackHeaderSubviewAndroidNativeComponent.ts +27 -0
  339. package/codegen-specs/gamma/stack/StackHostNativeComponent.ts +8 -0
  340. package/codegen-specs/gamma/stack/StackScreenNativeComponent.ts +42 -0
  341. package/codegen-specs/safe-area/SafeAreaViewNativeComponent.ts +24 -0
  342. package/codegen-specs/tabs/TabsBottomAccessoryContentNativeComponent.ts +15 -0
  343. package/codegen-specs/tabs/TabsBottomAccessoryNativeComponent.ts +19 -0
  344. package/codegen-specs/tabs/TabsHostAndroidNativeComponent.ts +69 -0
  345. package/codegen-specs/tabs/TabsHostIOSNativeComponent.ts +92 -0
  346. package/codegen-specs/tabs/TabsScreenAndroidNativeComponent.ts +112 -0
  347. package/codegen-specs/tabs/TabsScreenIOSNativeComponent.ts +171 -0
  348. package/package.json +201 -0
  349. package/react-native.config.cjs +58 -0
  350. package/src/angular/drawer/drawer.test.ts +299 -0
  351. package/src/angular/drawer/index.ts +546 -0
  352. package/src/angular/drawer-screen.directive.ts +28 -0
  353. package/src/angular/index.ts +63 -0
  354. package/src/angular/injectors/index.ts +13 -0
  355. package/src/angular/injectors/inject-drawer-navigation.ts +21 -0
  356. package/src/angular/injectors/inject-focus-effect.ts +37 -0
  357. package/src/angular/injectors/inject-is-focused.ts +28 -0
  358. package/src/angular/injectors/inject-navigation-state.ts +46 -0
  359. package/src/angular/injectors/inject-navigation.ts +26 -0
  360. package/src/angular/injectors/inject-route.ts +25 -0
  361. package/src/angular/injectors/inject-stack-navigation.ts +24 -0
  362. package/src/angular/injectors/inject-tab-navigation.ts +20 -0
  363. package/src/angular/injectors/injectors.test.ts +247 -0
  364. package/src/angular/injectors/use-typed-navigation.test.ts +229 -0
  365. package/src/angular/linking.ts +55 -0
  366. package/src/angular/navigation-context.service.ts +68 -0
  367. package/src/angular/navigation-scope.directive.ts +31 -0
  368. package/src/angular/nested-navigation.test.ts +189 -0
  369. package/src/angular/screen.directive.ts +48 -0
  370. package/src/angular/search-bar-ref.directive.ts +38 -0
  371. package/src/angular/stack/index.ts +401 -0
  372. package/src/angular/stack/stack.test.ts +419 -0
  373. package/src/angular/tab-screen.directive.ts +26 -0
  374. package/src/angular/tabs/index.ts +254 -0
  375. package/src/angular/tabs/tabs.test.ts +249 -0
  376. package/src/core/constants.ts +68 -0
  377. package/src/core/drawer-options/drawer-options.test.ts +415 -0
  378. package/src/core/drawer-options/index.ts +331 -0
  379. package/src/core/drawer-router-state/drawer-router-state.test.ts +116 -0
  380. package/src/core/drawer-router-state/index.ts +71 -0
  381. package/src/core/guards.ts +23 -0
  382. package/src/core/header-bar-buttons.ts +212 -0
  383. package/src/core/index.ts +170 -0
  384. package/src/core/linking-config/index.ts +209 -0
  385. package/src/core/linking-config/linking-config.test.ts +109 -0
  386. package/src/core/navigation-events/index.ts +94 -0
  387. package/src/core/navigation-events/navigation-events.test.ts +56 -0
  388. package/src/core/navigator-handles/index.ts +63 -0
  389. package/src/core/navigator-handles/navigator-handles.test.ts +46 -0
  390. package/src/core/navigator-props.ts +323 -0
  391. package/src/core/navigator-state/index.ts +112 -0
  392. package/src/core/navigator-state/navigator-state.test.ts +80 -0
  393. package/src/core/render-drawer.ts +123 -0
  394. package/src/core/render-stack.ts +414 -0
  395. package/src/core/render-tabs.ts +138 -0
  396. package/src/core/screen-options.ts +181 -0
  397. package/src/core/search-bar-commands.ts +57 -0
  398. package/src/core/state-persistence/index.ts +30 -0
  399. package/src/core/state-persistence/state-persistence.test.ts +42 -0
  400. package/src/core/tab-options.ts +25 -0
  401. package/src/core/tab-router-state/index.ts +75 -0
  402. package/src/core/tab-router-state/tab-router-state.test.ts +34 -0
  403. package/src/globals.d.ts +4 -0
  404. package/src/react/collect-registry.ts +19 -0
  405. package/src/react/drawer/drawer.test.tsx +472 -0
  406. package/src/react/drawer/index.ts +451 -0
  407. package/src/react/drawer-screen.ts +29 -0
  408. package/src/react/hooks/hooks.test.tsx +199 -0
  409. package/src/react/hooks/index.ts +12 -0
  410. package/src/react/hooks/use-drawer-navigation.ts +21 -0
  411. package/src/react/hooks/use-focus-effect.ts +36 -0
  412. package/src/react/hooks/use-is-focused.ts +26 -0
  413. package/src/react/hooks/use-navigation-state/index.ts +47 -0
  414. package/src/react/hooks/use-navigation-state/use-navigation-state.test.ts +85 -0
  415. package/src/react/hooks/use-navigation.ts +36 -0
  416. package/src/react/hooks/use-route.ts +11 -0
  417. package/src/react/hooks/use-stack-navigation.ts +23 -0
  418. package/src/react/hooks/use-tab-navigation.ts +21 -0
  419. package/src/react/hooks/use-typed-navigation.test.tsx +187 -0
  420. package/src/react/index.ts +52 -0
  421. package/src/react/linking.ts +59 -0
  422. package/src/react/navigation-context.ts +40 -0
  423. package/src/react/nested-navigation.test.tsx +145 -0
  424. package/src/react/screen.ts +44 -0
  425. package/src/react/stack/index.ts +323 -0
  426. package/src/react/stack/stack.test.tsx +514 -0
  427. package/src/react/tab-screen.ts +30 -0
  428. package/src/react/tabs/index.ts +204 -0
  429. package/src/react/tabs/tabs.test.tsx +256 -0
  430. package/src/register.ts +59 -0
  431. package/src/vue/composables/composables.test.ts +221 -0
  432. package/src/vue/composables/index.ts +12 -0
  433. package/src/vue/composables/use-drawer-navigation.ts +25 -0
  434. package/src/vue/composables/use-focus-effect.ts +37 -0
  435. package/src/vue/composables/use-is-focused.ts +34 -0
  436. package/src/vue/composables/use-navigation-state.ts +38 -0
  437. package/src/vue/composables/use-navigation.ts +30 -0
  438. package/src/vue/composables/use-route.ts +13 -0
  439. package/src/vue/composables/use-stack-navigation.ts +28 -0
  440. package/src/vue/composables/use-tab-navigation.ts +25 -0
  441. package/src/vue/composables/use-typed-navigation.test.ts +205 -0
  442. package/src/vue/drawer/drawer.test.ts +451 -0
  443. package/src/vue/drawer/index.ts +550 -0
  444. package/src/vue/drawer-screen.ts +32 -0
  445. package/src/vue/index.ts +70 -0
  446. package/src/vue/linking.ts +69 -0
  447. package/src/vue/navigation-context.ts +87 -0
  448. package/src/vue/nested-navigation.test.ts +155 -0
  449. package/src/vue/screen.ts +52 -0
  450. package/src/vue/stack/index.ts +376 -0
  451. package/src/vue/stack/stack.test.ts +501 -0
  452. package/src/vue/tab-screen.ts +29 -0
  453. package/src/vue/tabs/index.ts +302 -0
  454. package/src/vue/tabs/tabs.test.ts +278 -0
  455. package/symbiote-navigation.podspec +84 -0
@@ -0,0 +1,21 @@
1
+ // Twin of use-stack-navigation.ts, narrowing to a Drawer handle instead. See that file's header
2
+ // for why this exists: it hides the union-narrowing guard inside the library instead of every call site.
3
+
4
+ import type { IDrawerNavigatorHandle } from '../../core';
5
+ import { isDrawerNavigatorHandle } from '../../core';
6
+ import { useNavigation } from './use-navigation';
7
+ import type { INavigationHandle } from './use-navigation';
8
+
9
+ export type IDrawerNavigationHandle = IDrawerNavigatorHandle &
10
+ Pick<INavigationHandle, 'addListener' | 'getParent'>;
11
+
12
+ export function useDrawerNavigation(): IDrawerNavigationHandle {
13
+ const navigation = useNavigation();
14
+ if (!isDrawerNavigatorHandle(navigation)) {
15
+ throw new Error(
16
+ 'useDrawerNavigation() was called from a component whose nearest navigator is not a Drawer. ' +
17
+ 'Use useNavigation() instead if this component can render under more than one navigator kind.',
18
+ );
19
+ }
20
+ return navigation;
21
+ }
@@ -0,0 +1,36 @@
1
+ // Thin useEffect wrapper: runs `effect` while the route is focused and runs its own returned
2
+ // cleanup on blur - exactly React.useEffect's contract, just re-armed on every focus/blur pair
3
+ // instead of once on mount - mirrors @react-navigation's useFocusEffect. Callers should memoize
4
+ // `effect` (React.useCallback), same requirement the upstream hook documents, since a new
5
+ // `effect` identity re-subscribes here like any other useEffect dependency change.
6
+
7
+ import { useEffect } from 'react';
8
+ import type { EffectCallback } from 'react';
9
+ import { NAVIGATION_EVENT_BLUR, NAVIGATION_EVENT_FOCUS } from '../../core';
10
+ import { useRequiredNavigationContext } from '../navigation-context';
11
+
12
+ export function useFocusEffect(effect: EffectCallback): void {
13
+ const context = useRequiredNavigationContext('useFocusEffect');
14
+ const { emitter } = context;
15
+
16
+ useEffect(() => {
17
+ let cleanup: ReturnType<EffectCallback> | undefined;
18
+
19
+ const runEffect = (): void => {
20
+ cleanup = effect();
21
+ };
22
+ const runCleanup = (): void => {
23
+ cleanup?.();
24
+ cleanup = undefined;
25
+ };
26
+
27
+ const unsubscribeFocus = emitter.addListener(NAVIGATION_EVENT_FOCUS, runEffect);
28
+ const unsubscribeBlur = emitter.addListener(NAVIGATION_EVENT_BLUR, runCleanup);
29
+
30
+ return () => {
31
+ unsubscribeFocus();
32
+ unsubscribeBlur();
33
+ runCleanup();
34
+ };
35
+ }, [emitter, effect]);
36
+ }
@@ -0,0 +1,26 @@
1
+ // Thin useEffect wrapper: subscribes to the route's focus/blur pair and returns whether it's
2
+ // currently focused, re-rendering on change - mirrors @react-navigation's useIsFocused. Starts
3
+ // `false` rather than guessing from stack position: the route's emitter only fires 'focus' once
4
+ // RNSScreen's native onAppear actually lands (stack.ts), so a screen genuinely isn't focused yet
5
+ // at the instant it mounts - same async gap real native transitions have.
6
+
7
+ import { useEffect, useState } from 'react';
8
+ import { NAVIGATION_EVENT_BLUR, NAVIGATION_EVENT_FOCUS } from '../../core';
9
+ import { useRequiredNavigationContext } from '../navigation-context';
10
+
11
+ export function useIsFocused(): boolean {
12
+ const context = useRequiredNavigationContext('useIsFocused');
13
+ const { emitter } = context;
14
+ const [isFocused, setIsFocused] = useState(false);
15
+
16
+ useEffect(() => {
17
+ const unsubscribeFocus = emitter.addListener(NAVIGATION_EVENT_FOCUS, () => setIsFocused(true));
18
+ const unsubscribeBlur = emitter.addListener(NAVIGATION_EVENT_BLUR, () => setIsFocused(false));
19
+ return () => {
20
+ unsubscribeFocus();
21
+ unsubscribeBlur();
22
+ };
23
+ }, [emitter]);
24
+
25
+ return isFocused;
26
+ }
@@ -0,0 +1,47 @@
1
+ // Thin selector wrapper: subscribes to the router's `state` broadcast (stack.ts's render loop
2
+ // re-emits the full INavigatorState to every route's emitter after each commit) and returns
3
+ // `selector(state)`, re-rendering only when the emitted state changes - mirrors
4
+ // @react-navigation's useNavigationState. The reducer/dispatch machinery it selects over lives in
5
+ // core/navigator-state.ts; this hook only wires the subscription + local re-render.
6
+ //
7
+ // Seeded from a single-route snapshot ({ routes: [route] }) rather than left undefined: the real
8
+ // broadcast lands a tick later (post-commit useEffect), so a selector reading e.g.
9
+ // `state.routes.at(-1)?.name` still resolves correctly on first paint for the common single-route
10
+ // case, closing the same async gap useIsFocused documents.
11
+ //
12
+ // Live updates currently only arrive under <Stack> - it's the only navigator that emits
13
+ // NAVIGATION_EVENT_STATE (its post-commit useEffect in stack.ts); <Tab>/<Drawer> don't broadcast
14
+ // it, so under those this hook silently stays on its initial snapshot.
15
+
16
+ import { useEffect, useRef, useState } from 'react';
17
+ import type { INavigatorState } from '../../../core';
18
+ import { NAVIGATION_EVENT_STATE, isRecord } from '../../../core';
19
+ import { useRequiredNavigationContext } from '../../navigation-context';
20
+
21
+ export function useNavigationState<TResult>(
22
+ selector: (state: INavigatorState) => TResult,
23
+ ): TResult {
24
+ const context = useRequiredNavigationContext('useNavigationState');
25
+ const { route, emitter } = context;
26
+ const [result, setResult] = useState(() => selector({ routes: [route] }));
27
+
28
+ // selector is typically an inline arrow at the call site (`useNavigationState(s => ...)`), a
29
+ // fresh identity every render - read through a ref so the effect below doesn't need `selector`
30
+ // in its deps, and doesn't unsubscribe/resubscribe on every render because of it (the same trap
31
+ // useFocusEffect's own comment documents for its `effect` argument).
32
+ const selectorRef = useRef(selector);
33
+ selectorRef.current = selector;
34
+
35
+ useEffect(() => {
36
+ return emitter.addListener(NAVIGATION_EVENT_STATE, (state: unknown) => {
37
+ if (!isNavigatorState(state)) return;
38
+ setResult(selectorRef.current(state));
39
+ });
40
+ }, [emitter]);
41
+
42
+ return result;
43
+ }
44
+
45
+ function isNavigatorState(value: unknown): value is INavigatorState {
46
+ return isRecord(value) && Array.isArray(value.routes);
47
+ }
@@ -0,0 +1,85 @@
1
+ // Co-located React-driven test (ADR 0025) for useNavigationState's subscription lifecycle.
2
+ // hooks.test.tsx already exercises the hook's OBSERVABLE behavior (state.routes.length tracking a
3
+ // real Stack push/pop) - this file targets a narrower, easy-to-miss regression: the call site's
4
+ // selector is typically an inline arrow (`useNavigationState(s => s.routes.length)`), a fresh
5
+ // function identity every render, so an effect keyed on `selector` unsubscribes/resubscribes on
6
+ // every parent re-render instead of once. A fake emitter (rather than a real Stack) isolates
7
+ // exactly that churn, independent of any router/native-screen machinery.
8
+
9
+ import { act, createElement, useReducer } from 'react';
10
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
11
+ import { mount, unmount } from '@symbiote-native/react';
12
+ import { installFabric } from '@symbiote-native/test-utils';
13
+ import type { INavigationEmitter, INavigationEventListener, INavigatorState } from '../../../core';
14
+ import { NavigationContext } from '../../navigation-context';
15
+ import type { INavigationContextValue } from '../../navigation-context';
16
+ import { useNavigationState } from './index';
17
+
18
+ const ROOT_TAG = 833;
19
+
20
+ const fabric = installFabric();
21
+
22
+ beforeEach(() => fabric.reset());
23
+ afterEach(() => unmount(ROOT_TAG));
24
+
25
+ describe('useNavigationState subscription lifecycle', () => {
26
+ it('subscribes to the emitter once, not once per render, across a fresh inline selector', () => {
27
+ let subscribeCount = 0;
28
+ let unsubscribeCount = 0;
29
+ const listeners = new Set<INavigationEventListener>();
30
+ const fakeEmitter: INavigationEmitter = {
31
+ emit: () => {},
32
+ addListener: (_event, listener) => {
33
+ subscribeCount += 1;
34
+ listeners.add(listener);
35
+ return () => {
36
+ unsubscribeCount += 1;
37
+ listeners.delete(listener);
38
+ };
39
+ },
40
+ };
41
+ const contextValue: INavigationContextValue = {
42
+ route: { key: 'home-1', name: 'Home', params: undefined },
43
+ navigation: {
44
+ push: () => {},
45
+ pop: () => {},
46
+ popToTop: () => {},
47
+ popTo: () => {},
48
+ replace: () => {},
49
+ setParams: () => {},
50
+ reset: (_state: INavigatorState) => {},
51
+ canGoBack: () => false,
52
+ },
53
+ emitter: fakeEmitter,
54
+ };
55
+
56
+ let triggerRerender: (() => void) | undefined;
57
+
58
+ function Child(): null {
59
+ // Deliberately a fresh inline selector on every render - the exact call-site shape
60
+ // (`useNavigationState(s => ...)`) that causes the churn this test guards against.
61
+ useNavigationState(state => state.routes.length);
62
+ return null;
63
+ }
64
+
65
+ function Harness(): ReturnType<typeof createElement> {
66
+ const [, dispatch] = useReducer((n: number) => n + 1, 0);
67
+ triggerRerender = () => dispatch();
68
+ return createElement(
69
+ NavigationContext.Provider,
70
+ { value: contextValue },
71
+ createElement(Child),
72
+ );
73
+ }
74
+
75
+ act(() => mount(ROOT_TAG, createElement(Harness)));
76
+ expect(subscribeCount).toBe(1);
77
+
78
+ act(() => triggerRerender?.());
79
+ act(() => triggerRerender?.());
80
+ act(() => triggerRerender?.());
81
+
82
+ expect(subscribeCount).toBe(1);
83
+ expect(unsubscribeCount).toBe(0);
84
+ });
85
+ });
@@ -0,0 +1,36 @@
1
+ // Thin lifecycle wrapper: returns the current screen's navigator handle (push/pop/replace/... for a
2
+ // Stack screen, jumpTo/setParams for a Tab screen, openDrawer/... for a Drawer screen) plus
3
+ // addListener bound to that route's own emitter - mirrors @react-navigation's
4
+ // `navigation.addListener('focus', cb)` surface. All pub/sub logic lives in
5
+ // ../../core/navigation-events; this hook only reads NavigationContext and binds identity.
6
+
7
+ import { useMemo } from 'react';
8
+ import type { INavigationEventListener, INavigationEventName } from '../../core';
9
+ import { useRequiredNavigationContext } from '../navigation-context';
10
+ import type { IAnyNavigatorHandle } from '../navigation-context';
11
+
12
+ export type INavigationHandle = IAnyNavigatorHandle & {
13
+ addListener: (event: INavigationEventName, listener: INavigationEventListener) => () => void;
14
+ // Walks exactly ONE hop up navigation-context.ts's `parent` chain to the enclosing navigator's
15
+ // handle - e.g. a Tab screen nested inside a Stack screen calling getParent() to push a new
16
+ // Stack route. Callers narrow the union themselves ('push' in parent, etc.). Deliberately NOT
17
+ // react-navigation's getParent(id) (named/targeted ancestor lookup) or a target-based dispatch
18
+ // to a specific nested navigator by name - plain immediate-parent walking is v1 scope; multi-hop
19
+ // ancestry would need each returned handle to carry its own getParent, which the plain
20
+ // per-navigator handle types (INavigatorHandle/ITabNavigatorHandle/IDrawerNavigatorHandle) don't.
21
+ getParent: () => IAnyNavigatorHandle | undefined;
22
+ };
23
+
24
+ export function useNavigation(): INavigationHandle {
25
+ const context = useRequiredNavigationContext('useNavigation');
26
+ const { navigation, emitter, parent } = context;
27
+
28
+ return useMemo(
29
+ () => ({
30
+ ...navigation,
31
+ addListener: emitter.addListener,
32
+ getParent: () => parent?.navigation,
33
+ }),
34
+ [navigation, emitter, parent],
35
+ );
36
+ }
@@ -0,0 +1,11 @@
1
+ // Thin lifecycle wrapper: returns the current screen's IRoute from NavigationContext - mirrors
2
+ // @react-navigation's useRoute. Zero logic of its own; the route object itself is built by
3
+ // stack.ts's render loop from the core reducer's state.
4
+
5
+ import type { IRoute } from '../../core';
6
+ import { useRequiredNavigationContext } from '../navigation-context';
7
+
8
+ export function useRoute(): IRoute<unknown> {
9
+ const context = useRequiredNavigationContext('useRoute');
10
+ return context.route;
11
+ }
@@ -0,0 +1,23 @@
1
+ // useNavigation() returns a union (IAnyNavigatorHandle) since it doesn't know which navigator
2
+ // mounted the calling component. When a component genuinely knows it only ever renders under a
3
+ // Stack (the common case for anything nested a few levels below a Stack screen), this hook
4
+ // narrows that union ONCE, here, so call sites never write their own 'push' in navigation check.
5
+
6
+ import type { INavigatorHandle } from '../../core';
7
+ import { isStackNavigatorHandle } from '../../core';
8
+ import { useNavigation } from './use-navigation';
9
+ import type { INavigationHandle } from './use-navigation';
10
+
11
+ export type IStackNavigationHandle = INavigatorHandle &
12
+ Pick<INavigationHandle, 'addListener' | 'getParent'>;
13
+
14
+ export function useStackNavigation(): IStackNavigationHandle {
15
+ const navigation = useNavigation();
16
+ if (!isStackNavigatorHandle(navigation)) {
17
+ throw new Error(
18
+ 'useStackNavigation() was called from a component whose nearest navigator is not a Stack. ' +
19
+ 'Use useNavigation() instead if this component can render under more than one navigator kind.',
20
+ );
21
+ }
22
+ return navigation;
23
+ }
@@ -0,0 +1,21 @@
1
+ // Twin of use-stack-navigation.ts, narrowing to a Tab handle instead. See that file's header for
2
+ // why this exists: it hides the union-narrowing guard inside the library instead of every call site.
3
+
4
+ import type { ITabNavigatorHandle } from '../../core';
5
+ import { isTabNavigatorHandle } from '../../core';
6
+ import { useNavigation } from './use-navigation';
7
+ import type { INavigationHandle } from './use-navigation';
8
+
9
+ export type ITabNavigationHandle = ITabNavigatorHandle &
10
+ Pick<INavigationHandle, 'addListener' | 'getParent'>;
11
+
12
+ export function useTabNavigation(): ITabNavigationHandle {
13
+ const navigation = useNavigation();
14
+ if (!isTabNavigatorHandle(navigation)) {
15
+ throw new Error(
16
+ 'useTabNavigation() was called from a component whose nearest navigator is not a Tab. ' +
17
+ 'Use useNavigation() instead if this component can render under more than one navigator kind.',
18
+ );
19
+ }
20
+ return navigation;
21
+ }
@@ -0,0 +1,187 @@
1
+ // Co-located React-driven test (ADR 0025) for useStackNavigation/useTabNavigation/
2
+ // useDrawerNavigation - the narrowed twins of useNavigation() that hide the union guard.
3
+
4
+ import { act, createElement, createRef } from 'react';
5
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
+ import { mount, unmount, setNativeViewConfigSource, Dimensions } from '@symbiote-native/react';
7
+ import type { INativeViewConfig } from '@symbiote-native/engine';
8
+ import { installFabric } from '@symbiote-native/test-utils';
9
+ import { Stack } from '../stack';
10
+ import type { INavigatorHandle } from '../stack';
11
+ import { Tab } from '../tabs';
12
+ import { Drawer } from '../drawer';
13
+ import { useDrawerNavigation, useStackNavigation, useTabNavigation } from './index';
14
+
15
+ const ROOT_TAG = 613;
16
+ const SCREEN_VIEW = 'RNSScreen';
17
+ const STACK_VIEW = 'RNSScreenStack';
18
+
19
+ const VIEW_CONFIGS: Record<string, INativeViewConfig> = {
20
+ [SCREEN_VIEW]: {
21
+ directEventTypes: {
22
+ topAppear: { registrationName: 'onAppear' },
23
+ topDisappear: { registrationName: 'onDisappear' },
24
+ topWillAppear: { registrationName: 'onWillAppear' },
25
+ topWillDisappear: { registrationName: 'onWillDisappear' },
26
+ topDismissed: { registrationName: 'onDismissed' },
27
+ topHeaderBackButtonClicked: { registrationName: 'onHeaderBackButtonClicked' },
28
+ },
29
+ validAttributes: {
30
+ screenId: true,
31
+ activityState: true,
32
+ gestureEnabled: true,
33
+ stackAnimation: true,
34
+ stackPresentation: true,
35
+ transitionDuration: true,
36
+ },
37
+ },
38
+ [STACK_VIEW]: {
39
+ directEventTypes: { topFinishTransitioning: { registrationName: 'onFinishTransitioning' } },
40
+ validAttributes: {},
41
+ },
42
+ };
43
+
44
+ // Drawer reads the screen width off useWindowDimensions() to resolve its swipe edge zone;
45
+ // headless has no DeviceInfo native module, so seed a concrete width once (Dimensions is a
46
+ // module-level singleton) - same fixture as drawer.test.tsx.
47
+ Dimensions.set({ window: { width: 375, height: 812, scale: 1, fontScale: 1 } });
48
+
49
+ const fabric = installFabric();
50
+ setNativeViewConfigSource(name => VIEW_CONFIGS[name]);
51
+
52
+ beforeEach(() => fabric.reset());
53
+ afterEach(() => unmount(ROOT_TAG));
54
+
55
+ function textScreen(label: string) {
56
+ return () => createElement('symbiote-text', {}, label);
57
+ }
58
+
59
+ describe('useStackNavigation', () => {
60
+ it('returns a concretely-typed Stack handle with push, no narrowing needed', () => {
61
+ let canPush = false;
62
+ function TrackedHomeScreen(): ReturnType<typeof createElement> {
63
+ const navigation = useStackNavigation();
64
+ canPush = typeof navigation.push === 'function';
65
+ return createElement('symbiote-text', {}, 'home');
66
+ }
67
+
68
+ const ref = createRef<INavigatorHandle>();
69
+ mount(
70
+ ROOT_TAG,
71
+ createElement(
72
+ Stack,
73
+ { ref, initialRouteName: 'Home' },
74
+ createElement(Stack.Screen, { name: 'Home', component: TrackedHomeScreen }),
75
+ createElement(Stack.Screen, { name: 'Details', component: textScreen('details') }),
76
+ ),
77
+ );
78
+ expect(canPush).toBe(true);
79
+ });
80
+
81
+ it('throws when the nearest navigator is a Tab, not a Stack', () => {
82
+ function TrackedHomeTab(): ReturnType<typeof createElement> {
83
+ useStackNavigation();
84
+ return createElement('symbiote-text', {}, 'home');
85
+ }
86
+
87
+ expect(() => {
88
+ act(() => {
89
+ mount(
90
+ ROOT_TAG,
91
+ createElement(
92
+ Tab,
93
+ { initialRouteName: 'Home' },
94
+ createElement(Tab.Screen, { name: 'Home', component: TrackedHomeTab }),
95
+ createElement(Tab.Screen, { name: 'Search', component: textScreen('search') }),
96
+ ),
97
+ );
98
+ });
99
+ }).toThrow(/nearest navigator is not a Stack/);
100
+ });
101
+ });
102
+
103
+ describe('useTabNavigation', () => {
104
+ it('returns a concretely-typed Tab handle with jumpTo, no narrowing needed', () => {
105
+ let canJumpTo = false;
106
+ function TrackedHomeTab(): ReturnType<typeof createElement> {
107
+ const navigation = useTabNavigation();
108
+ canJumpTo = typeof navigation.jumpTo === 'function';
109
+ return createElement('symbiote-text', {}, 'home');
110
+ }
111
+
112
+ mount(
113
+ ROOT_TAG,
114
+ createElement(
115
+ Tab,
116
+ { initialRouteName: 'Home' },
117
+ createElement(Tab.Screen, { name: 'Home', component: TrackedHomeTab }),
118
+ createElement(Tab.Screen, { name: 'Search', component: textScreen('search') }),
119
+ ),
120
+ );
121
+ expect(canJumpTo).toBe(true);
122
+ });
123
+
124
+ it('throws when the nearest navigator is a Stack, not a Tab', () => {
125
+ function TrackedHomeScreen(): ReturnType<typeof createElement> {
126
+ useTabNavigation();
127
+ return createElement('symbiote-text', {}, 'home');
128
+ }
129
+
130
+ expect(() => {
131
+ act(() => {
132
+ mount(
133
+ ROOT_TAG,
134
+ createElement(
135
+ Stack,
136
+ { initialRouteName: 'Home' },
137
+ createElement(Stack.Screen, { name: 'Home', component: TrackedHomeScreen }),
138
+ createElement(Stack.Screen, { name: 'Details', component: textScreen('details') }),
139
+ ),
140
+ );
141
+ });
142
+ }).toThrow(/nearest navigator is not a Tab/);
143
+ });
144
+ });
145
+
146
+ describe('useDrawerNavigation', () => {
147
+ it('returns a concretely-typed Drawer handle with openDrawer, no narrowing needed', () => {
148
+ let canOpenDrawer = false;
149
+ function TrackedHomeScreen(): ReturnType<typeof createElement> {
150
+ const navigation = useDrawerNavigation();
151
+ canOpenDrawer = typeof navigation.openDrawer === 'function';
152
+ return createElement('symbiote-text', {}, 'home');
153
+ }
154
+
155
+ mount(
156
+ ROOT_TAG,
157
+ createElement(
158
+ Drawer,
159
+ { initialRouteName: 'Home' },
160
+ createElement(Drawer.Screen, { name: 'Home', component: TrackedHomeScreen }),
161
+ createElement(Drawer.Screen, { name: 'Profile', component: textScreen('profile') }),
162
+ ),
163
+ );
164
+ expect(canOpenDrawer).toBe(true);
165
+ });
166
+
167
+ it('throws when the nearest navigator is a Stack, not a Drawer', () => {
168
+ function TrackedHomeScreen(): ReturnType<typeof createElement> {
169
+ useDrawerNavigation();
170
+ return createElement('symbiote-text', {}, 'home');
171
+ }
172
+
173
+ expect(() => {
174
+ act(() => {
175
+ mount(
176
+ ROOT_TAG,
177
+ createElement(
178
+ Stack,
179
+ { initialRouteName: 'Home' },
180
+ createElement(Stack.Screen, { name: 'Home', component: TrackedHomeScreen }),
181
+ createElement(Stack.Screen, { name: 'Details', component: textScreen('details') }),
182
+ ),
183
+ );
184
+ });
185
+ }).toThrow(/nearest navigator is not a Drawer/);
186
+ });
187
+ });
@@ -0,0 +1,52 @@
1
+ // @symbiote-native/navigation/react: the React native-stack navigator over react-native-screens'
2
+ // native view primitives. Importing this barrel first registers the native views' ViewConfigs
3
+ // (../register, a side-effect import of the three codegen specs - never react-native-screens' own
4
+ // React components), then exposes Stack (with Stack.Screen attached) and the navigator handle.
5
+
6
+ import '../register';
7
+
8
+ export { Stack } from './stack';
9
+ export type { INavigatorHandle, IStackProps } from './stack';
10
+ export { Screen } from './screen';
11
+ export type { IScreenOptionsArgs, IScreenOptionsResolver, IScreenProps } from './screen';
12
+ export type { IScreenOptions, IStackAnimation, IStackPresentation } from '../core';
13
+
14
+ export { useNavigation, useRoute, useIsFocused, useFocusEffect, useNavigationState } from './hooks';
15
+ export type { INavigationHandle } from './hooks';
16
+ export { useStackNavigation, useTabNavigation, useDrawerNavigation } from './hooks';
17
+ export type {
18
+ IStackNavigationHandle,
19
+ ITabNavigationHandle,
20
+ IDrawerNavigationHandle,
21
+ } from './hooks';
22
+ export { NavigationContext } from './navigation-context';
23
+ export type { INavigationContextValue, IAnyNavigatorHandle } from './navigation-context';
24
+
25
+ export { useLinkingIntegration } from './linking';
26
+ export type { ILinkingConfig, IScreenLinkingConfig } from '../core';
27
+
28
+ // Tab: the bottom-tabs navigator, a PURE-JS UI (no react-native-screens views involved, so no
29
+ // extra ViewConfig registration is needed beyond the ../register import above).
30
+ export { Tab } from './tabs';
31
+ export type { ITabNavigatorHandle, ITabProps } from './tabs';
32
+ export { TabScreen } from './tab-screen';
33
+ export type {
34
+ ITabScreenOptionsArgs,
35
+ ITabScreenOptionsResolver,
36
+ ITabScreenProps,
37
+ } from './tab-screen';
38
+ export type { ITabOptions, ITabBarIcon } from '../core';
39
+
40
+ // Drawer: the swipeable drawer navigator, a PURE-JS UI (PanResponder + Animated, no
41
+ // react-native-screens views involved, so no extra ViewConfig registration is needed beyond the
42
+ // ../register import above). See react/drawer.ts's header for the feasibility note re:
43
+ // react-native-gesture-handler / react-native-reanimated parity gaps.
44
+ export { Drawer } from './drawer';
45
+ export type { IDrawerNavigatorHandle, IDrawerProps, IDrawerDescriptorMap } from './drawer';
46
+ export { DrawerScreen } from './drawer-screen';
47
+ export type {
48
+ IDrawerScreenOptionsArgs,
49
+ IDrawerScreenOptionsResolver,
50
+ IDrawerScreenProps,
51
+ } from './drawer-screen';
52
+ export type { IDrawerOptions, IDrawerScreenOptions, IDrawerType, IDrawerPosition } from '../core';
@@ -0,0 +1,59 @@
1
+ // useLinkingIntegration: wires the framework-agnostic linking config (../core's
2
+ // resolveRouteFromUrl) onto an existing Stack ref's INavigatorHandle. Deliberately NOT called
3
+ // from inside stack.ts - app code owns the wiring (a <Stack ref={...}> plus this hook alongside
4
+ // it), the same top-level-prop shape @react-navigation keeps `linking` in on NavigationContainer
5
+ // rather than baking it into the router itself.
6
+
7
+ import { useEffect, useRef } from 'react';
8
+ import { Linking, dlog } from '@symbiote-native/engine';
9
+ import type { IUrlEvent } from '@symbiote-native/engine';
10
+ import { resolveRouteFromUrl } from '../core';
11
+ import type { ILinkingConfig } from '../core';
12
+ import type { INavigatorHandle } from '../core';
13
+
14
+ export function useLinkingIntegration(
15
+ config: ILinkingConfig,
16
+ navigatorHandle: INavigatorHandle,
17
+ ): void {
18
+ // Refs, not effect deps: `navigatorHandle` is re-created by Stack on every push/pop
19
+ // (see stack.ts's useMemo deps including `state.routes.length`), so depending on it directly
20
+ // would resubscribe on every navigation instead of once on mount.
21
+ const configRef = useRef(config);
22
+ configRef.current = config;
23
+ const handleRef = useRef(navigatorHandle);
24
+ handleRef.current = navigatorHandle;
25
+
26
+ useEffect(() => {
27
+ function applyRoute(url: string, dispatch: (name: string, params?: unknown) => void): void {
28
+ const route = resolveRouteFromUrl(configRef.current, url);
29
+ if (route === null) {
30
+ dlog(`useLinkingIntegration: no route resolved for "${url}"`);
31
+ return;
32
+ }
33
+ dispatch(route.name, route.params);
34
+ }
35
+
36
+ let cancelled = false;
37
+
38
+ Linking.getInitialURL()
39
+ .then(url => {
40
+ if (cancelled || url === null) return;
41
+ dlog(`useLinkingIntegration: initial URL "${url}"`);
42
+ applyRoute(url, handleRef.current.replace);
43
+ })
44
+ .catch((error: unknown) => {
45
+ dlog(`useLinkingIntegration: getInitialURL failed: ${String(error)}`);
46
+ });
47
+
48
+ const subscription = Linking.addEventListener('url', (event: IUrlEvent) => {
49
+ dlog(`useLinkingIntegration: url event "${event.url}"`);
50
+ applyRoute(event.url, handleRef.current.push);
51
+ });
52
+
53
+ return () => {
54
+ cancelled = true;
55
+ subscription.remove();
56
+ };
57
+ // Mount-once: freshness of config/navigatorHandle is read through the refs above.
58
+ }, []);
59
+ }
@@ -0,0 +1,40 @@
1
+ // The lifecycle half of the framework-agnostic emitter (../core/navigation-events): a plain
2
+ // React Context so a screen's subtree can read its own route, navigator handle, and per-route
3
+ // emitter without prop-drilling - the same role @react-navigation's NavigationContext /
4
+ // NavigationRouteContext play, collapsed into one context since a symbiote screen only ever needs
5
+ // its OWN route (no per-navigator param-list generics in v1 scope - see screen.ts). Provided
6
+ // per-screen by each of stack.ts/tabs.ts/drawer.ts's render loop, consumed by the hooks in
7
+ // ./hooks. `parent` threads the ambient context a navigator read on ITS OWN mount (undefined at
8
+ // the root) into the value it provides to its own screens, forming a linked list - this is what
9
+ // lets a screen nested inside e.g. a Stack-screen-renders-a-Tab composition reach the enclosing
10
+ // Stack via useNavigation().getParent().
11
+
12
+ import { createContext, useContext } from 'react';
13
+ import type { INavigationEmitter, IRoute, IAnyNavigatorHandle } from '../core';
14
+ export type { IAnyNavigatorHandle } from '../core';
15
+
16
+ // A nested navigator (e.g. a Tab rendered as a Stack screen's content) means a screen's OWN
17
+ // navigation prop and its PARENT's handle can be different navigator kinds, so the Context
18
+ // value's `navigation` field must stay a union rather than Stack-specific. Consumers narrow it
19
+ // themselves (e.g. `'push' in handle` picks out a Stack handle) - no `as` casts.
20
+ export type INavigationContextValue = {
21
+ route: IRoute<unknown>;
22
+ navigation: IAnyNavigatorHandle;
23
+ emitter: INavigationEmitter;
24
+ parent?: INavigationContextValue;
25
+ };
26
+
27
+ export const NavigationContext = createContext<INavigationContextValue | undefined>(undefined);
28
+
29
+ // Every hook in ./hooks needs the same `useContext(NavigationContext)` + missing-provider throw,
30
+ // so it's co-located here instead of repeated per hook. `hookName` keeps each hook's own name in
31
+ // the thrown message.
32
+ export function useRequiredNavigationContext(hookName: string): INavigationContextValue {
33
+ const context = useContext(NavigationContext);
34
+ if (!context) {
35
+ throw new Error(
36
+ `${hookName} must be used within a screen rendered by <Stack>, <Tab>, or <Drawer>`,
37
+ );
38
+ }
39
+ return context;
40
+ }