@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,472 @@
1
+ // Co-located React-driven test (ADR 0025) for the @symbiote-native/navigation React Drawer.
2
+ // Unlike Stack (real native RNSScreen views) and Tab (a pure-JS tab bar), Drawer additionally
3
+ // drives a real PanResponder + Animated.timing gesture, so this file borrows the fake-touch-event
4
+ // technique from pan-responder-multitouch.test.tsx (topTouchStart/topTouchMove/topTouchEnd with
5
+ // pageX/identifier/target) to prove an edge-swipe actually opens/closes the drawer, plus the rAF
6
+ // polyfill from animated-integration.test.tsx (every openDrawer/closeDrawer/toggleDrawer call and
7
+ // a gesture release schedules a real requestAnimationFrame via Animated.timing, which is not a
8
+ // Node global). Drawer is imported from './drawer' (NOT the package barrel) so the third-party
9
+ // native-spec side-effect (../register) never loads headless - matching stack.test.tsx/
10
+ // tabs.test.tsx, even though Drawer itself needs no injected ViewConfig (PanResponder + Animated
11
+ // are pure JS, like Tab's bar).
12
+
13
+ import { act, createElement, createRef, useCallback } from 'react';
14
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
15
+ import { mount, unmount, Dimensions } from '@symbiote-native/react';
16
+ import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
17
+ import { Drawer } from './index';
18
+ import type { IDrawerNavigatorHandle } from './index';
19
+ import { useFocusEffect, useIsFocused, useNavigation, useRoute } from '../hooks';
20
+
21
+ const ROOT_TAG = 704;
22
+ const TOUCH_START = 'topTouchStart';
23
+ const TOUCH_MOVE = 'topTouchMove';
24
+ const TOUCH_END = 'topTouchEnd';
25
+ const TOUCH_ID = 1;
26
+
27
+ // Drawer reads the screen width off useWindowDimensions() to resolve the swipe edge zone
28
+ // (isSwipeStartInEdge) - headless has no DeviceInfo native module, so seed a concrete width once;
29
+ // every mount in this file reads this same cached value (Dimensions is a module-level singleton).
30
+ Dimensions.set({ window: { width: 375, height: 812, scale: 1, fontScale: 1 } });
31
+
32
+ // rAF is not a Node global; Animated.timing (driven by every openDrawer/closeDrawer/toggleDrawer
33
+ // call and by a gesture release) reads it at .start() time. Ported verbatim from
34
+ // animated-integration.test.tsx's setTimeout-based polyfill - no frame is ever awaited here since
35
+ // these tests assert on state.isOpen-derived props, not the animated frame values.
36
+ let frameClock = 0;
37
+ const pendingFrames = new Map<number, (time: number) => void>();
38
+ let nextFrameId = 1;
39
+
40
+ function installRequestAnimationFrame(): void {
41
+ Object.assign(globalThis, {
42
+ requestAnimationFrame(callback: (time: number) => void): number {
43
+ const id = nextFrameId++;
44
+ pendingFrames.set(id, callback);
45
+ setTimeout(() => {
46
+ const cb = pendingFrames.get(id);
47
+ if (cb !== undefined) {
48
+ pendingFrames.delete(id);
49
+ frameClock += 16;
50
+ cb(frameClock);
51
+ }
52
+ }, 0);
53
+ return id;
54
+ },
55
+ cancelAnimationFrame(id: number): void {
56
+ pendingFrames.delete(id);
57
+ },
58
+ });
59
+ }
60
+
61
+ const fabric = installFabric();
62
+
63
+ beforeEach(() => {
64
+ fabric.reset();
65
+ frameClock = 0;
66
+ pendingFrames.clear();
67
+ nextFrameId = 1;
68
+ installRequestAnimationFrame();
69
+ });
70
+ afterEach(() => {
71
+ unmount(ROOT_TAG);
72
+ Reflect.deleteProperty(globalThis, 'requestAnimationFrame');
73
+ Reflect.deleteProperty(globalThis, 'cancelAnimationFrame');
74
+ });
75
+
76
+ function HomeScreen(): ReturnType<typeof createElement> {
77
+ return createElement('symbiote-text', {}, 'home');
78
+ }
79
+ function ProfileScreen(): ReturnType<typeof createElement> {
80
+ return createElement('symbiote-text', {}, 'profile');
81
+ }
82
+
83
+ function findAllText(nodes: readonly IFakeNode[]): string[] {
84
+ const found: string[] = [];
85
+ const collect = (list: readonly IFakeNode[]): void => {
86
+ for (const node of list) {
87
+ if (node.viewName === 'RCTRawText' && typeof node.props.text === 'string') {
88
+ found.push(node.props.text);
89
+ }
90
+ collect(node.children);
91
+ }
92
+ };
93
+ collect(nodes);
94
+ return found;
95
+ }
96
+
97
+ // Drawer's own root view (holds panResponder.panHandlers) - first child under the AppContainer,
98
+ // mirroring pan-responder-multitouch.test.tsx's `viewNode`.
99
+ function drawerRoot(): IFakeNode {
100
+ return fabric.appRoot().children[0];
101
+ }
102
+
103
+ // Default drawerType ('front') paints [content, overlay, panel] in that sibling order
104
+ // (render-drawer.ts's drawerChildOrder) - the overlay's pointerEvents prop ('auto' while open,
105
+ // 'none' while closed) is the one stable, non-animated signal of state.isOpen this file reads,
106
+ // since the slide/opacity transforms themselves are driven by a real (unawaited) Animated.timing.
107
+ function overlayNode(): IFakeNode {
108
+ const overlay = drawerRoot().children[1];
109
+ if (!overlay) throw new Error('no overlay child committed');
110
+ return overlay;
111
+ }
112
+
113
+ function isOpenByOverlay(): boolean {
114
+ return overlayNode().props.pointerEvents === 'auto';
115
+ }
116
+
117
+ type ITouchFrame = { x: number; y: number; t: number };
118
+
119
+ // Fires a start -> N moves -> end sequence at the drawer root, mirroring the fake-Fabric touch
120
+ // technique from pan-responder-multitouch.test.tsx (touches carry their own `target`, matching
121
+ // how the engine resolves touch ancestry - plain {identifier,pageX,pageY} like tabs.test.tsx's
122
+ // simple tap is not enough for a multi-frame drag). Wrapped in one `act()` (tabs.test.tsx's
123
+ // tapItem convention) since a release can dispatch a router action and re-render.
124
+ function swipe(path: readonly ITouchFrame[]): void {
125
+ const node = drawerRoot();
126
+ const handle = node.instanceHandle;
127
+ const tag = node.tag;
128
+ const point = (frame: ITouchFrame) => ({
129
+ identifier: TOUCH_ID,
130
+ pageX: frame.x,
131
+ pageY: frame.y,
132
+ timestamp: frame.t,
133
+ target: handle,
134
+ });
135
+ const fire = (type: string, frame: ITouchFrame): void => {
136
+ const touch = point(frame);
137
+ fabric.fireEvent(handle, type, {
138
+ touches: type === TOUCH_END ? [] : [touch],
139
+ changedTouches: [touch],
140
+ target: tag,
141
+ timestamp: frame.t,
142
+ });
143
+ };
144
+ act(() => {
145
+ const [start, ...rest] = path;
146
+ fire(TOUCH_START, start);
147
+ rest.forEach((frame, i) => fire(i === rest.length - 1 ? TOUCH_END : TOUCH_MOVE, frame));
148
+ });
149
+ }
150
+
151
+ // Clears the default swipeEdgeWidth (32) and swipeMinDistance (60) at position 'left' - a start
152
+ // near x=10 followed by a large horizontal move.
153
+ const OPEN_SWIPE: readonly ITouchFrame[] = [
154
+ { x: 10, y: 400, t: 1_000 },
155
+ { x: 130, y: 400, t: 1_050 }, // dx=120 (>=60), dt=50 -> vx=2.4 (>=0.5): both thresholds clear.
156
+ { x: 130, y: 400, t: 1_060 },
157
+ ];
158
+
159
+ // Same edge start, but the move never clears either threshold: dx=20 (<60), dt=100 -> vx=0.2
160
+ // (<0.5).
161
+ const UNDER_THRESHOLD_SWIPE: readonly ITouchFrame[] = [
162
+ { x: 10, y: 400, t: 1_000 },
163
+ { x: 30, y: 400, t: 1_100 },
164
+ { x: 30, y: 400, t: 1_110 },
165
+ ];
166
+
167
+ // Same dx/dt as OPEN_SWIPE (clears both thresholds), but starting well past swipeEdgeWidth (32)
168
+ // on a 375-wide screen: regression for the bug where onStartShouldSetPanResponder read the
169
+ // always-zero gestureState.x0 instead of the touch's real start, which made isSwipeStartInEdge
170
+ // pass no matter where a left drawer's swipe began.
171
+ const MID_SCREEN_SWIPE: readonly ITouchFrame[] = [
172
+ { x: 180, y: 400, t: 1_000 },
173
+ { x: 300, y: 400, t: 1_050 },
174
+ { x: 300, y: 400, t: 1_060 },
175
+ ];
176
+
177
+ // Starts inside the RIGHT edge zone (screenWidth 375 - swipeEdgeWidth 32 = 343) and drags
178
+ // leftward -- the 'open' direction for drawerPosition: 'right' (resolveSwipeIntent flips sign).
179
+ // Same |dx|=120/dt=50 magnitude as OPEN_SWIPE, mirrored: regression for the same bug's inverse
180
+ // symptom, where a right drawer's edge-swipe-to-open was entirely non-functional.
181
+ const RIGHT_EDGE_OPEN_SWIPE: readonly ITouchFrame[] = [
182
+ { x: 360, y: 400, t: 1_000 },
183
+ { x: 240, y: 400, t: 1_050 },
184
+ { x: 240, y: 400, t: 1_060 },
185
+ ];
186
+
187
+ describe('React Drawer navigator', () => {
188
+ it("mounts only the focused route's content, drawer closed", () => {
189
+ mount(
190
+ ROOT_TAG,
191
+ createElement(
192
+ Drawer,
193
+ { initialRouteName: 'Home' },
194
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
195
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
196
+ ),
197
+ );
198
+ expect(findAllText(fabric.committed)).toContain('home');
199
+ expect(findAllText(fabric.committed)).not.toContain('profile');
200
+ expect(isOpenByOverlay()).toBe(false);
201
+ });
202
+
203
+ it('openDrawer() opens the drawer', () => {
204
+ const ref = createRef<IDrawerNavigatorHandle>();
205
+ mount(
206
+ ROOT_TAG,
207
+ createElement(
208
+ Drawer,
209
+ { ref, initialRouteName: 'Home' },
210
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
211
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
212
+ ),
213
+ );
214
+ act(() => ref.current?.openDrawer());
215
+ expect(isOpenByOverlay()).toBe(true);
216
+ });
217
+
218
+ it('closeDrawer() closes an open drawer', () => {
219
+ const ref = createRef<IDrawerNavigatorHandle>();
220
+ mount(
221
+ ROOT_TAG,
222
+ createElement(
223
+ Drawer,
224
+ { ref, initialRouteName: 'Home' },
225
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
226
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
227
+ ),
228
+ );
229
+ act(() => ref.current?.openDrawer());
230
+ act(() => ref.current?.closeDrawer());
231
+ expect(isOpenByOverlay()).toBe(false);
232
+ });
233
+
234
+ it('toggleDrawer() flips open/closed across two calls', () => {
235
+ const ref = createRef<IDrawerNavigatorHandle>();
236
+ mount(
237
+ ROOT_TAG,
238
+ createElement(
239
+ Drawer,
240
+ { ref, initialRouteName: 'Home' },
241
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
242
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
243
+ ),
244
+ );
245
+ act(() => ref.current?.toggleDrawer());
246
+ expect(isOpenByOverlay()).toBe(true);
247
+ act(() => ref.current?.toggleDrawer());
248
+ expect(isOpenByOverlay()).toBe(false);
249
+ });
250
+
251
+ it('jumpTo() switches the focused screen content while closed', () => {
252
+ const ref = createRef<IDrawerNavigatorHandle>();
253
+ mount(
254
+ ROOT_TAG,
255
+ createElement(
256
+ Drawer,
257
+ { ref, initialRouteName: 'Home' },
258
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
259
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
260
+ ),
261
+ );
262
+ act(() => ref.current?.jumpTo('Profile'));
263
+ expect(findAllText(fabric.committed)).toContain('profile');
264
+ expect(findAllText(fabric.committed)).not.toContain('home');
265
+ });
266
+
267
+ it('jumpTo() to an unknown route name is a no-op', () => {
268
+ const ref = createRef<IDrawerNavigatorHandle>();
269
+ mount(
270
+ ROOT_TAG,
271
+ createElement(
272
+ Drawer,
273
+ { ref, initialRouteName: 'Home' },
274
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
275
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
276
+ ),
277
+ );
278
+ act(() => ref.current?.jumpTo('Nowhere'));
279
+ expect(findAllText(fabric.committed)).toContain('home');
280
+ });
281
+
282
+ it('jumpTo() closes an already-open drawer', () => {
283
+ const ref = createRef<IDrawerNavigatorHandle>();
284
+ mount(
285
+ ROOT_TAG,
286
+ createElement(
287
+ Drawer,
288
+ { ref, initialRouteName: 'Home' },
289
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
290
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
291
+ ),
292
+ );
293
+ act(() => ref.current?.openDrawer());
294
+ expect(isOpenByOverlay()).toBe(true);
295
+ act(() => ref.current?.jumpTo('Profile'));
296
+ expect(isOpenByOverlay()).toBe(false);
297
+ expect(findAllText(fabric.committed)).toContain('profile');
298
+ });
299
+
300
+ it('a valid edge-swipe opens the drawer', () => {
301
+ mount(
302
+ ROOT_TAG,
303
+ createElement(
304
+ Drawer,
305
+ { initialRouteName: 'Home' },
306
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
307
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
308
+ ),
309
+ );
310
+ expect(isOpenByOverlay()).toBe(false);
311
+ swipe(OPEN_SWIPE);
312
+ expect(isOpenByOverlay()).toBe(true);
313
+ });
314
+
315
+ it('a swipe that clears neither the distance nor velocity threshold snaps back closed', () => {
316
+ mount(
317
+ ROOT_TAG,
318
+ createElement(
319
+ Drawer,
320
+ { initialRouteName: 'Home' },
321
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
322
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
323
+ ),
324
+ );
325
+ swipe(UNDER_THRESHOLD_SWIPE);
326
+ expect(isOpenByOverlay()).toBe(false);
327
+ });
328
+
329
+ it('swipeEnabled: false suppresses the gesture entirely', () => {
330
+ mount(
331
+ ROOT_TAG,
332
+ createElement(
333
+ Drawer,
334
+ { initialRouteName: 'Home', swipeEnabled: false },
335
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
336
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
337
+ ),
338
+ );
339
+ // Same geometry as the passing OPEN_SWIPE case above - the only variable is swipeEnabled.
340
+ swipe(OPEN_SWIPE);
341
+ expect(isOpenByOverlay()).toBe(false);
342
+ });
343
+
344
+ it('a swipe starting outside the edge zone does not open a left-positioned drawer', () => {
345
+ mount(
346
+ ROOT_TAG,
347
+ createElement(
348
+ Drawer,
349
+ { initialRouteName: 'Home' },
350
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
351
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
352
+ ),
353
+ );
354
+ swipe(MID_SCREEN_SWIPE);
355
+ expect(isOpenByOverlay()).toBe(false);
356
+ });
357
+
358
+ it('a valid edge-swipe opens a right-positioned drawer', () => {
359
+ mount(
360
+ ROOT_TAG,
361
+ createElement(
362
+ Drawer,
363
+ { initialRouteName: 'Home', drawerPosition: 'right' },
364
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
365
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
366
+ ),
367
+ );
368
+ expect(isOpenByOverlay()).toBe(false);
369
+ swipe(RIGHT_EDGE_OPEN_SWIPE);
370
+ expect(isOpenByOverlay()).toBe(true);
371
+ });
372
+
373
+ // Before this fix, Drawer never wrapped its focused screen in NavigationContext.Provider at
374
+ // all, so every one of these hooks threw "must be used within a screen rendered by <Stack>" the
375
+ // moment a Drawer screen called them. These cases prove the context is now provided and the
376
+ // focus semantics (focused when it's the current index, blurred otherwise) are wired.
377
+ it('useNavigation()/useRoute() are usable inside a Drawer screen, and useIsFocused() reflects the focused route', () => {
378
+ let homeIsFocused: boolean | undefined;
379
+ let homeRouteName: string | undefined;
380
+ let profileIsFocused: boolean | undefined;
381
+
382
+ function TrackedHomeScreen(): ReturnType<typeof createElement> {
383
+ const navigation = useNavigation();
384
+ homeIsFocused = useIsFocused();
385
+ homeRouteName = useRoute().name;
386
+ // Merely proving the handle is a real IDrawerNavigatorHandle (openDrawer/jumpTo/...), not
387
+ // the Stack-only shape this Context value was hard-typed to before the widened union.
388
+ expect(typeof navigation.jumpTo).toBe('function');
389
+ return createElement('symbiote-text', {}, 'home');
390
+ }
391
+ function TrackedProfileScreen(): ReturnType<typeof createElement> {
392
+ profileIsFocused = useIsFocused();
393
+ return createElement('symbiote-text', {}, 'profile');
394
+ }
395
+
396
+ const ref = createRef<IDrawerNavigatorHandle>();
397
+ // Drawer's own focus-emitting effect runs in the same commit as the initial mount, but the
398
+ // setIsFocused(true) it triggers inside useIsFocused's listener lands in a follow-up render -
399
+ // act() is what drains that cascade synchronously (mirrors every other state-changing call in
400
+ // this file already being act()-wrapped).
401
+ act(() => {
402
+ mount(
403
+ ROOT_TAG,
404
+ createElement(
405
+ Drawer,
406
+ { ref, initialRouteName: 'Home' },
407
+ createElement(Drawer.Screen, { name: 'Home', component: TrackedHomeScreen }),
408
+ createElement(Drawer.Screen, { name: 'Profile', component: TrackedProfileScreen }),
409
+ ),
410
+ );
411
+ });
412
+ expect(homeIsFocused).toBe(true);
413
+ expect(homeRouteName).toBe('Home');
414
+
415
+ act(() => ref.current?.jumpTo('Profile'));
416
+ expect(profileIsFocused).toBe(true);
417
+ });
418
+
419
+ it('merges navigator-level screenOptions under a screen that does not override them', () => {
420
+ let capturedDrawerLabel: string | undefined;
421
+ mount(
422
+ ROOT_TAG,
423
+ createElement(
424
+ Drawer,
425
+ {
426
+ initialRouteName: 'Home',
427
+ screenOptions: { drawerLabel: 'Shared Label' },
428
+ renderDrawerContent: ({ state, descriptors }) => {
429
+ const homeRoute = state.routes.find(route => route.name === 'Home');
430
+ capturedDrawerLabel = homeRoute
431
+ ? descriptors[homeRoute.key]?.options.drawerLabel
432
+ : undefined;
433
+ return null;
434
+ },
435
+ },
436
+ createElement(Drawer.Screen, { name: 'Home', component: HomeScreen }),
437
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
438
+ ),
439
+ );
440
+ expect(capturedDrawerLabel).toBe('Shared Label');
441
+ });
442
+
443
+ it('useFocusEffect runs on Drawer focus and its cleanup once jumpTo moves focus away', () => {
444
+ const events: string[] = [];
445
+ function TrackedHomeScreen(): ReturnType<typeof createElement> {
446
+ useFocusEffect(
447
+ useCallback(() => {
448
+ events.push('effect');
449
+ return () => events.push('cleanup');
450
+ }, []),
451
+ );
452
+ return createElement('symbiote-text', {}, 'home');
453
+ }
454
+
455
+ const ref = createRef<IDrawerNavigatorHandle>();
456
+ mount(
457
+ ROOT_TAG,
458
+ createElement(
459
+ Drawer,
460
+ { ref, initialRouteName: 'Home' },
461
+ createElement(Drawer.Screen, { name: 'Home', component: TrackedHomeScreen }),
462
+ createElement(Drawer.Screen, { name: 'Profile', component: ProfileScreen }),
463
+ ),
464
+ );
465
+ // Drawer paints no native RNSScreen (unlike Stack), so there is no onAppear to wait for - the
466
+ // focused screen's useFocusEffect runs as soon as it mounts.
467
+ expect(events).toEqual(['effect']);
468
+
469
+ act(() => ref.current?.jumpTo('Profile'));
470
+ expect(events).toEqual(['effect', 'cleanup']);
471
+ });
472
+ });