@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,451 @@
1
+ // Drawer, the React lifecycle half. The open/closed + focused-route router (drawer-router-state)
2
+ // and the pure swipe/geometry math (drawer-options) live in @symbiote-native/navigation core,
3
+ // shared verbatim with the Vue/Angular adapters; here React supplies the lifecycle - useReducer
4
+ // for the router, a PanResponder for the swipe gesture (RN's own idiom: build it ONCE via
5
+ // useRef, let its callbacks read current values off refs rather than rebuilding it every render),
6
+ // an Animated.Value driving the slide/opacity transforms, and useImperativeHandle for the
7
+ // open/close/toggle/jumpTo handle - mirroring react/tabs.ts's shape (Tab is the closer sibling:
8
+ // both are fixed-route-list, no-react-native-screens navigators; Stack's push/pop + native-screen
9
+ // bridging don't apply here).
10
+ //
11
+ // FEASIBILITY NOTE (see the drawer skill / PR description for the full writeup): the REAL
12
+ // @react-navigation/drawer is built on react-native-gesture-handler + react-native-reanimated,
13
+ // neither of which this codebase depends on. What's built here reaches the same swipe-to-open/
14
+ // close + front/back/slide/permanent behavior using only PanResponder + Animated (both already in
15
+ // @symbiote-native/engine), which is sufficient for a solid drawer but NOT byte-for-byte parity -
16
+ // see the explicit gap list at the bottom of this file.
17
+
18
+ import {
19
+ createElement,
20
+ forwardRef,
21
+ isValidElement,
22
+ useCallback,
23
+ useContext,
24
+ useEffect,
25
+ useId,
26
+ useImperativeHandle,
27
+ useMemo,
28
+ useReducer,
29
+ useRef,
30
+ } from 'react';
31
+ import type { ReactElement, ReactNode } from 'react';
32
+ import { Animated, PanResponder, useWindowDimensions } from '@symbiote-native/react';
33
+ import type { IPanResponderGestureState, ISymbioteEvent } from '@symbiote-native/react';
34
+ import type { IDescriptor } from '@symbiote-native/components';
35
+ import { dlog, type IStyleProp, type IViewStyle } from '@symbiote-native/engine';
36
+ import {
37
+ DRAWER_DEFAULT_OVERLAY_COLOR,
38
+ NAVIGATION_EVENT_BLUR,
39
+ NAVIGATION_EVENT_FOCUS,
40
+ createInitialDrawerRouterState,
41
+ createNavigationEmitter,
42
+ drawerChildOrder,
43
+ drawerRouterReducer,
44
+ isDrawerAnimated,
45
+ renderDrawer,
46
+ resolveDragProgress,
47
+ resolveDrawerGeometry,
48
+ resolveDrawerSlotInterpolation,
49
+ resolveSwipeIntent,
50
+ shouldClaimDrawerSwipe,
51
+ } from '../../core';
52
+ import type {
53
+ IDrawerDescriptorMap,
54
+ IDrawerNavigatorHandle,
55
+ IDrawerOptions,
56
+ IDrawerRouterState,
57
+ IDrawerScreenOptions,
58
+ IDrawerSlot,
59
+ IRoute,
60
+ } from '../../core';
61
+ import { collectRegistry } from '../collect-registry';
62
+ import { NavigationContext } from '../navigation-context';
63
+ import { DrawerScreen } from '../drawer-screen';
64
+ import type { IDrawerScreenOptionsArgs, IDrawerScreenProps } from '../drawer-screen';
65
+
66
+ export type { IDrawerNavigatorHandle, IDrawerDescriptorMap } from '../../core';
67
+
68
+ export type IDrawerProps = IDrawerOptions & {
69
+ initialRouteName?: string;
70
+ screenOptions?: IDrawerScreenOptions;
71
+ drawerStyle?: IStyleProp<IViewStyle>;
72
+ renderDrawerContent?: (props: {
73
+ state: IDrawerRouterState;
74
+ descriptors: IDrawerDescriptorMap;
75
+ navigation: IDrawerNavigatorHandle;
76
+ }) => ReactNode;
77
+ children?: ReactNode;
78
+ };
79
+
80
+ type IDrawerRegistryEntry = Omit<IDrawerScreenProps, 'name'>;
81
+
82
+ function isDrawerScreenElement(child: ReactNode): child is ReactElement<IDrawerScreenProps> {
83
+ return isValidElement(child) && child.type === DrawerScreen;
84
+ }
85
+
86
+ function resolveDrawerScreenOptions(
87
+ entry: IDrawerRegistryEntry,
88
+ optionsArgs: IDrawerScreenOptionsArgs,
89
+ screenOptions: IDrawerScreenOptions | undefined,
90
+ ): IDrawerScreenOptions {
91
+ const own = typeof entry.options === 'function' ? entry.options(optionsArgs) : entry.options;
92
+ return { ...screenOptions, ...own };
93
+ }
94
+
95
+ const DRAWER_SNAP_DURATION = 250;
96
+
97
+ const DrawerImpl = forwardRef<IDrawerNavigatorHandle, IDrawerProps>((props, forwardedRef) => {
98
+ // Read BEFORE establishing this Drawer's own Context value below - becomes the `parent` link a
99
+ // nested screen's useNavigation().getParent() walks (e.g. this Drawer rendered as a Stack
100
+ // screen's content reaches that Stack via this value). undefined when this Drawer is the
101
+ // nesting root.
102
+ const ambientContext = useContext(NavigationContext);
103
+ const {
104
+ initialRouteName,
105
+ screenOptions,
106
+ renderDrawerContent,
107
+ children,
108
+ drawerStyle,
109
+ drawerType,
110
+ drawerPosition,
111
+ drawerWidth,
112
+ overlayColor,
113
+ swipeEnabled,
114
+ swipeEdgeWidth,
115
+ swipeMinDistance,
116
+ swipeMinVelocity,
117
+ } = props;
118
+
119
+ const options: IDrawerOptions = {
120
+ drawerType,
121
+ drawerPosition,
122
+ drawerWidth,
123
+ overlayColor,
124
+ swipeEnabled,
125
+ swipeEdgeWidth,
126
+ swipeMinDistance,
127
+ swipeMinVelocity,
128
+ };
129
+
130
+ const registry = useMemo(() => collectRegistry(children, isDrawerScreenElement), [children]);
131
+ const routeIdPrefix = useId();
132
+
133
+ const routes = useMemo<IRoute<unknown>[]>(
134
+ () =>
135
+ Array.from(registry.entries()).map(([name, entry]) => ({
136
+ key: `${routeIdPrefix}-${name}`,
137
+ name,
138
+ params: entry.initialParams,
139
+ })),
140
+ [registry, routeIdPrefix],
141
+ );
142
+
143
+ const [state, dispatch] = useReducer(drawerRouterReducer, undefined, () =>
144
+ createInitialDrawerRouterState(routes, initialRouteName),
145
+ );
146
+
147
+ if (routes.length === 0) dlog('Drawer: no <Drawer.Screen> children registered');
148
+
149
+ const { width: screenWidth } = useWindowDimensions();
150
+
151
+ // progress: 0 closed -> 1 open, the single Animated.Value every slide/opacity transform below
152
+ // interpolates from.
153
+ const progress = useRef(new Animated.Value(state.isOpen ? 1 : 0)).current;
154
+ // Where a drag STARTS from, in progress units. Always exactly 0 or 1: a gesture only ever
155
+ // begins at rest, since terminate/release always snap the value back to a resting state before
156
+ // another grant can fire.
157
+ const dragStartProgress = useRef(0);
158
+
159
+ // Refs so the PanResponder's callbacks - built ONCE below, RN's own idiom - always read the
160
+ // CURRENT render's values without forcing a new PanResponder identity on every state/prop
161
+ // change (recreating panHandlers mid-gesture would drop the in-flight touch).
162
+ const optionsRef = useRef(options);
163
+ optionsRef.current = options;
164
+ const screenWidthRef = useRef(screenWidth);
165
+ screenWidthRef.current = screenWidth;
166
+ const isOpenRef = useRef(state.isOpen);
167
+ isOpenRef.current = state.isOpen;
168
+
169
+ const animateProgressTo = useCallback(
170
+ (open: boolean): void => {
171
+ // Investigation instrumentation (Drawer openDrawer-no-op / toggleDrawer-no-animation bug):
172
+ // every imperative caller (openDrawer/closeDrawer/toggleDrawer/jumpTo) funnels through here,
173
+ // so this single seam proves whether Animated.timing is actually being started at all, and
174
+ // with what toValue - a live-only failure (e.g. a stale progress ref, or Animated.timing
175
+ // silently short-circuiting) would show up as this log firing with no visible motion. Kept
176
+ // behind DEBUG per <keep_logs_gate_behind_DEBUG>, never removed.
177
+ dlog(`Drawer: animateProgressTo(open=${open}) starting at t=${Date.now()}`);
178
+ Animated.timing(progress, {
179
+ toValue: open ? 1 : 0,
180
+ duration: DRAWER_SNAP_DURATION,
181
+ // Native-driver wiring (the AnimatedComponent passthrough opt-in ADR 0017 defines) is
182
+ // deferred for v1 - see this file's header feasibility note. The JS timing loop still
183
+ // drives every frame, same as any other non-native-driven Animated.timing in this codebase.
184
+ useNativeDriver: false,
185
+ }).start();
186
+ },
187
+ [progress],
188
+ );
189
+
190
+ const handle = useMemo<IDrawerNavigatorHandle>(
191
+ () => ({
192
+ openDrawer: () => {
193
+ dlog(`Drawer: openDrawer() called, isOpen=${isOpenRef.current} at t=${Date.now()}`);
194
+ animateProgressTo(true);
195
+ dispatch({ type: 'openDrawer' });
196
+ },
197
+ closeDrawer: () => {
198
+ dlog(`Drawer: closeDrawer() called, isOpen=${isOpenRef.current} at t=${Date.now()}`);
199
+ animateProgressTo(false);
200
+ dispatch({ type: 'closeDrawer' });
201
+ },
202
+ toggleDrawer: () => {
203
+ dlog(`Drawer: toggleDrawer() called, isOpen=${isOpenRef.current} at t=${Date.now()}`);
204
+ animateProgressTo(!isOpenRef.current);
205
+ dispatch({ type: 'toggleDrawer' });
206
+ },
207
+ jumpTo: (name: string) => {
208
+ dispatch({ type: 'jumpTo', name });
209
+ if (isOpenRef.current) animateProgressTo(false);
210
+ },
211
+ }),
212
+ [animateProgressTo],
213
+ );
214
+
215
+ useImperativeHandle(forwardedRef, () => handle, [handle]);
216
+
217
+ const panResponder = useRef(
218
+ PanResponder.create({
219
+ onStartShouldSetPanResponder: (
220
+ event: ISymbioteEvent,
221
+ gestureState: IPanResponderGestureState,
222
+ ): boolean =>
223
+ shouldClaimDrawerSwipe(
224
+ event,
225
+ gestureState,
226
+ screenWidthRef.current,
227
+ isOpenRef.current,
228
+ optionsRef.current,
229
+ 'start',
230
+ ),
231
+ onMoveShouldSetPanResponder: (
232
+ event: ISymbioteEvent,
233
+ gestureState: IPanResponderGestureState,
234
+ ): boolean =>
235
+ shouldClaimDrawerSwipe(
236
+ event,
237
+ gestureState,
238
+ screenWidthRef.current,
239
+ isOpenRef.current,
240
+ optionsRef.current,
241
+ 'move',
242
+ ),
243
+ onPanResponderGrant: (): void => {
244
+ dlog('Drawer: gesture grant');
245
+ dragStartProgress.current = isOpenRef.current ? 1 : 0;
246
+ },
247
+ onPanResponderMove: (
248
+ _event: ISymbioteEvent,
249
+ gestureState: IPanResponderGestureState,
250
+ ): void => {
251
+ const nextProgress = resolveDragProgress(
252
+ gestureState,
253
+ dragStartProgress.current,
254
+ optionsRef.current,
255
+ );
256
+ progress.setValue(nextProgress);
257
+ },
258
+ onPanResponderRelease: (
259
+ _event: ISymbioteEvent,
260
+ gestureState: IPanResponderGestureState,
261
+ ): void => {
262
+ const intent = resolveSwipeIntent(gestureState, isOpenRef.current, optionsRef.current);
263
+ const open = intent === 'open';
264
+ dlog(`Drawer: gesture release -> ${open ? 'open' : 'close'}`);
265
+ animateProgressTo(open);
266
+ dispatch(open ? { type: 'openDrawer' } : { type: 'closeDrawer' });
267
+ },
268
+ onPanResponderTerminate: (): void => {
269
+ dlog('Drawer: gesture terminated, snapping back');
270
+ animateProgressTo(isOpenRef.current);
271
+ },
272
+ }),
273
+ ).current;
274
+
275
+ const animated = isDrawerAnimated(options);
276
+ const geometry = useMemo(
277
+ () => resolveDrawerGeometry(options),
278
+ [options.drawerType, options.drawerPosition, options.drawerWidth],
279
+ );
280
+
281
+ const panelSlot = animated ? resolveDrawerSlotInterpolation(geometry, 'panel') : undefined;
282
+ const contentSlot = animated ? resolveDrawerSlotInterpolation(geometry, 'content') : undefined;
283
+ const overlaySlot = animated ? resolveDrawerSlotInterpolation(geometry, 'overlay') : undefined;
284
+
285
+ const panelTranslateX = panelSlot ? progress.interpolate(panelSlot.translateX) : undefined;
286
+ const contentTranslateX = contentSlot ? progress.interpolate(contentSlot.translateX) : undefined;
287
+ const overlayOpacity = overlaySlot ? progress.interpolate(overlaySlot.opacity) : undefined;
288
+ // The overlay is a full-screen absolutely-positioned sibling BELOW content in paint order
289
+ // (see render-drawer.ts's drawerChildOrder) - for 'front' that's fine since content never moves,
290
+ // but for 'slide' content itself translates away by contentTranslateX, and without following it
291
+ // the overlay stays pinned full-screen, dimming (and touch-capturing) the now-revealed panel
292
+ // underneath instead of just the content sliver it's meant to dim. Tying overlay to the SAME
293
+ // translateX as content keeps it registered exactly under content, wherever content actually is
294
+ // (resolveDrawerSlotInterpolation's 'overlay' overload returns that same content delta).
295
+ const overlayTranslateX = overlaySlot ? progress.interpolate(overlaySlot.translateX) : undefined;
296
+
297
+ const focusedRoute = state.routes[state.index];
298
+ const focusedEntry = focusedRoute ? registry.get(focusedRoute.name) : undefined;
299
+ if (focusedRoute && !focusedEntry) {
300
+ dlog(`Drawer: no screen registered for route name "${focusedRoute.name}"`);
301
+ }
302
+
303
+ // Only the focused route's screen is ever mounted (like Tab, unlike Stack which keeps every
304
+ // pushed route alive), so a fresh emitter per focus change is sufficient - see tabs.ts's
305
+ // matching comment for why no per-route emitter map is needed. Keyed on the route KEY, not the
306
+ // route object, so a jumpTo-with-no-params re-focus of the ALREADY-focused route (a no-op in
307
+ // drawerRouterReducer) and any future params merge don't spuriously re-fire focus/blur.
308
+ const focusedRouteKey = focusedRoute?.key;
309
+ const routeEmitter = useMemo(() => createNavigationEmitter(), [focusedRouteKey]);
310
+
311
+ // Drawer paints its own panel in pure JS - there is no native onAppear/onDisappear to hook
312
+ // (unlike Stack's RNSScreen), so focus/blur is synthesized here: mount = focus, cleanup = blur,
313
+ // exactly what an effect keyed on focusedRouteKey already encodes - no diffFocusedRoute
314
+ // indirection needed (unlike Vue/Angular, which diff real prev/next keys inside an imperative
315
+ // watch/CD callback that has no mount/cleanup pairing of its own).
316
+ useEffect(() => {
317
+ if (focusedRouteKey === undefined) return undefined;
318
+ dlog(`Drawer: route "${focusedRoute?.name}" focused`);
319
+ routeEmitter.emit(NAVIGATION_EVENT_FOCUS);
320
+ return () => {
321
+ dlog(`Drawer: route "${focusedRoute?.name}" blurred`);
322
+ routeEmitter.emit(NAVIGATION_EVENT_BLUR);
323
+ };
324
+ // focusedRoute omitted deliberately: only its .key (tracked via focusedRouteKey) should
325
+ // re-run this effect - see the comment above on focusedRouteKey.
326
+ // eslint-disable-next-line react-hooks/exhaustive-deps
327
+ }, [routeEmitter, focusedRouteKey]);
328
+
329
+ const content =
330
+ focusedEntry && focusedRoute
331
+ ? createElement(
332
+ NavigationContext.Provider,
333
+ {
334
+ value: {
335
+ route: focusedRoute,
336
+ navigation: handle,
337
+ emitter: routeEmitter,
338
+ parent: ambientContext,
339
+ },
340
+ },
341
+ createElement(focusedEntry.component),
342
+ )
343
+ : null;
344
+
345
+ const descriptors: IDrawerDescriptorMap = {};
346
+ for (const route of state.routes) {
347
+ const entry = registry.get(route.name);
348
+ if (entry === undefined) continue;
349
+ descriptors[route.key] = {
350
+ options: resolveDrawerScreenOptions(entry, { route, navigation: handle }, screenOptions),
351
+ navigation: handle,
352
+ };
353
+ }
354
+
355
+ const drawerContent = renderDrawerContent?.({ state, descriptors, navigation: handle }) ?? null;
356
+
357
+ const root = renderDrawer(
358
+ {
359
+ overlayColor: overlayColor ?? DRAWER_DEFAULT_OVERLAY_COLOR,
360
+ drawerStyle,
361
+ contentPassthrough: {},
362
+ overlayPassthrough: animated
363
+ ? {
364
+ pointerEvents: state.isOpen ? 'auto' : 'none',
365
+ onStartShouldSetResponder: () => true,
366
+ onResponderRelease: () => {
367
+ animateProgressTo(false);
368
+ dispatch({ type: 'closeDrawer' });
369
+ },
370
+ }
371
+ : {},
372
+ panelPassthrough: {},
373
+ },
374
+ options,
375
+ );
376
+
377
+ const order = drawerChildOrder(options);
378
+ const slots = new Map<IDrawerSlot, IDescriptor>();
379
+ order.forEach((slot, index) => {
380
+ const descriptor = root.children[index];
381
+ if (typeof descriptor !== 'string') slots.set(slot, descriptor);
382
+ });
383
+
384
+ const slotChildren: Record<IDrawerSlot, ReactNode> = {
385
+ content,
386
+ overlay: null,
387
+ panel: drawerContent,
388
+ };
389
+ // Each animated style holds an AnimatedInterpolation node, not a plain number/color - it feeds
390
+ // only Animated.View's deliberately permissive `style?: unknown` (see create-animated-component.tsx),
391
+ // never the plain-IViewStyle branch below, so this stays untyped rather than widening IViewStyle.
392
+ const slotAnimatedStyle: Record<IDrawerSlot, unknown> = {
393
+ content:
394
+ contentTranslateX === undefined
395
+ ? undefined
396
+ : { transform: [{ translateX: contentTranslateX }] },
397
+ overlay:
398
+ overlayOpacity === undefined
399
+ ? undefined
400
+ : { opacity: overlayOpacity, transform: [{ translateX: overlayTranslateX }] },
401
+ panel:
402
+ panelTranslateX === undefined ? undefined : { transform: [{ translateX: panelTranslateX }] },
403
+ };
404
+
405
+ function renderSlot(slot: IDrawerSlot): ReactElement | null {
406
+ const descriptor = slots.get(slot);
407
+ if (descriptor === undefined) return null;
408
+ const animatedStyle = slotAnimatedStyle[slot];
409
+ if (animatedStyle === undefined) {
410
+ return createElement(
411
+ descriptor.type,
412
+ { key: descriptor.key, ...descriptor.props },
413
+ slotChildren[slot],
414
+ );
415
+ }
416
+ const style = [descriptor.props.style, animatedStyle];
417
+ return createElement(
418
+ Animated.View,
419
+ { key: descriptor.key, ...descriptor.props, style },
420
+ slotChildren[slot],
421
+ );
422
+ }
423
+
424
+ const drawerChildren = order
425
+ .map(renderSlot)
426
+ .filter((element): element is ReactElement => element !== null);
427
+
428
+ return createElement(
429
+ 'symbiote-view',
430
+ { style: root.props.style, ...panResponder.panHandlers },
431
+ ...drawerChildren,
432
+ );
433
+ });
434
+
435
+ export const Drawer = Object.assign(DrawerImpl, { Screen: DrawerScreen });
436
+
437
+ // --- Explicit gap list vs the real react-native-gesture-handler + react-native-reanimated
438
+ // @react-navigation/drawer (confirmed against its current docs) ---
439
+ // 1. `configureGestureHandler` - a raw react-native-gesture-handler `Gesture` object escape
440
+ // hatch. No PanResponder equivalent exists; not ported.
441
+ // 2. Simultaneous/failure gesture RELATIONSHIPS (gesture-handler's declarative composition vs a
442
+ // nested ScrollView, another PanResponder, etc.) - PanResponder only offers negotiation via the
443
+ // should-set boolean gates used here (edge-start + dominant-axis), which is more prone to an
444
+ // accidental hijack of a nested horizontal ScrollView/Swiper than gesture-handler's system.
445
+ // 3. `useDrawerProgress` - a Reanimated SharedValue read on the UI thread. `progress` here is a
446
+ // JS-thread AnimatedValue; interpolating it for consumer-facing content animation works, but
447
+ // without native-driver wiring (gap noted above) it does not carry the same synchronous
448
+ // UI-thread guarantee under JS-thread load.
449
+ // 4. `hideStatusBarOnOpen` / `keyboardDismissMode` / `statusBarAnimation` / `overlayStyle` - not
450
+ // wired in this pass; straightforward additions once StatusBar/Keyboard module wiring is
451
+ // needed here (not a PanResponder/Animated limitation, just unscoped for v1).
@@ -0,0 +1,29 @@
1
+ // Drawer.Screen: a declarative marker, never rendered on its own - Drawer reads its props via
2
+ // React.Children to build the static name -> {component, options} registry, then mounts the
3
+ // FOCUSED route's component. Mirrors react/tab-screen.ts (Tab's twin - the closer sibling, both
4
+ // are fixed-route-list/no-push navigators) and react/screen.ts (Stack's twin); see CLAUDE.md
5
+ // <third_party_rn_packages_are_react_only> - this and Drawer import nothing from react-native.
6
+
7
+ import type { FC, ReactElement } from 'react';
8
+ import type { IDrawerNavigatorHandle, IDrawerScreenOptions, IRoute } from '../core';
9
+
10
+ // The options resolver runs INSIDE Drawer while it computes a screen's options, closing over the
11
+ // live navigator handle - not the mounted screen component (that reads navigation/route via the
12
+ // hooks). See react/screen.ts's IScreenOptionsArgs for the same distinction.
13
+ export type IDrawerScreenOptionsArgs = {
14
+ route: IRoute<unknown>;
15
+ navigation: IDrawerNavigatorHandle;
16
+ };
17
+
18
+ export type IDrawerScreenOptionsResolver = (args: IDrawerScreenOptionsArgs) => IDrawerScreenOptions;
19
+
20
+ export type IDrawerScreenProps = {
21
+ name: string;
22
+ component: FC;
23
+ options?: IDrawerScreenOptions | IDrawerScreenOptionsResolver;
24
+ initialParams?: unknown;
25
+ };
26
+
27
+ export function DrawerScreen(_props: IDrawerScreenProps): ReactElement | null {
28
+ return null;
29
+ }
@@ -0,0 +1,199 @@
1
+ // Co-located React-driven test (ADR 0025) for the focus/event hooks layer. Mirrors
2
+ // ../stack.test.tsx's fixture (an injected codegen-shaped RNSScreen ViewConfig exposing
3
+ // onAppear/onDisappear/onWillAppear/onWillDisappear) and drives the same native events stack.ts
4
+ // wires to emit 'focus'/'blur' - proving the hooks react to the real RNS lifecycle, not to a
5
+ // synthetic shortcut.
6
+
7
+ import { act, createElement, createRef, useCallback, useEffect } from 'react';
8
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
9
+ import { mount, unmount, setNativeViewConfigSource } from '@symbiote-native/react';
10
+ import type { INativeViewConfig } from '@symbiote-native/engine';
11
+ import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
12
+ import { Stack } from '../stack';
13
+ import type { INavigatorHandle } from '../stack';
14
+ import { useFocusEffect, useIsFocused, useNavigation, useNavigationState, useRoute } from './index';
15
+
16
+ const ROOT_TAG = 513;
17
+ const SCREEN_VIEW = 'RNSScreen';
18
+ const STACK_VIEW = 'RNSScreenStack';
19
+ const HEADER_CONFIG_VIEW = 'RNSScreenStackHeaderConfig';
20
+
21
+ function directEvent(registrationName: string) {
22
+ return { registrationName };
23
+ }
24
+
25
+ const VIEW_CONFIGS: Record<string, INativeViewConfig> = {
26
+ [SCREEN_VIEW]: {
27
+ directEventTypes: {
28
+ topAppear: directEvent('onAppear'),
29
+ topDisappear: directEvent('onDisappear'),
30
+ topWillAppear: directEvent('onWillAppear'),
31
+ topWillDisappear: directEvent('onWillDisappear'),
32
+ topDismissed: directEvent('onDismissed'),
33
+ topHeaderBackButtonClicked: directEvent('onHeaderBackButtonClicked'),
34
+ },
35
+ validAttributes: {
36
+ screenId: true,
37
+ activityState: true,
38
+ gestureEnabled: true,
39
+ stackAnimation: true,
40
+ stackPresentation: true,
41
+ transitionDuration: true,
42
+ },
43
+ },
44
+ [STACK_VIEW]: {
45
+ directEventTypes: { topFinishTransitioning: directEvent('onFinishTransitioning') },
46
+ validAttributes: {},
47
+ },
48
+ [HEADER_CONFIG_VIEW]: {
49
+ directEventTypes: { topPressHeaderBarButtonItem: directEvent('onPressHeaderBarButtonItem') },
50
+ validAttributes: { title: true, hidden: true, backTitle: true, backTitleVisible: true },
51
+ },
52
+ };
53
+
54
+ const fabric = installFabric();
55
+ setNativeViewConfigSource(name => VIEW_CONFIGS[name]);
56
+
57
+ beforeEach(() => fabric.reset());
58
+ afterEach(() => unmount(ROOT_TAG));
59
+
60
+ function screenNodes(): IFakeNode[] {
61
+ const found: IFakeNode[] = [];
62
+ const collect = (nodes: readonly IFakeNode[]): void => {
63
+ for (const node of nodes) {
64
+ if (node.viewName === SCREEN_VIEW) found.push(node);
65
+ collect(node.children);
66
+ }
67
+ };
68
+ collect(fabric.committed);
69
+ return found;
70
+ }
71
+
72
+ function HomeScreen(): ReturnType<typeof createElement> {
73
+ return createElement('symbiote-text', {}, 'home');
74
+ }
75
+
76
+ function DetailsScreen(): ReturnType<typeof createElement> {
77
+ return createElement('symbiote-text', {}, 'details');
78
+ }
79
+
80
+ describe('navigation hooks', () => {
81
+ it("useIsFocused reflects the route's native appear/disappear events", () => {
82
+ let latestIsFocused: boolean | undefined;
83
+ function TrackedHomeScreen(): ReturnType<typeof createElement> {
84
+ latestIsFocused = useIsFocused();
85
+ return createElement('symbiote-text', {}, 'home');
86
+ }
87
+
88
+ mount(
89
+ ROOT_TAG,
90
+ createElement(
91
+ Stack,
92
+ { initialRouteName: 'Home' },
93
+ createElement(Stack.Screen, { name: 'Home', component: TrackedHomeScreen }),
94
+ createElement(Stack.Screen, { name: 'Details', component: DetailsScreen }),
95
+ ),
96
+ );
97
+ expect(latestIsFocused).toBe(false);
98
+
99
+ const home = screenNodes()[0];
100
+ act(() => fabric.fireEvent(home.instanceHandle, 'topAppear', {}));
101
+ expect(latestIsFocused).toBe(true);
102
+
103
+ act(() => fabric.fireEvent(home.instanceHandle, 'topDisappear', {}));
104
+ expect(latestIsFocused).toBe(false);
105
+ });
106
+
107
+ it('useFocusEffect runs its effect on focus and its cleanup on blur', () => {
108
+ const events: string[] = [];
109
+ function TrackedHomeScreen(): ReturnType<typeof createElement> {
110
+ useFocusEffect(
111
+ useCallback(() => {
112
+ events.push('effect');
113
+ return () => events.push('cleanup');
114
+ }, []),
115
+ );
116
+ return createElement('symbiote-text', {}, 'home');
117
+ }
118
+
119
+ mount(
120
+ ROOT_TAG,
121
+ createElement(
122
+ Stack,
123
+ { initialRouteName: 'Home' },
124
+ createElement(Stack.Screen, { name: 'Home', component: TrackedHomeScreen }),
125
+ createElement(Stack.Screen, { name: 'Details', component: DetailsScreen }),
126
+ ),
127
+ );
128
+ expect(events).toEqual([]);
129
+
130
+ const home = screenNodes()[0];
131
+ act(() => fabric.fireEvent(home.instanceHandle, 'topAppear', {}));
132
+ expect(events).toEqual(['effect']);
133
+
134
+ act(() => fabric.fireEvent(home.instanceHandle, 'topDisappear', {}));
135
+ expect(events).toEqual(['effect', 'cleanup']);
136
+ });
137
+
138
+ it('useNavigation().addListener fires on focus and useRoute exposes name/params', () => {
139
+ let capturedName: string | undefined;
140
+ let capturedParams: unknown;
141
+ const focusEvents: string[] = [];
142
+
143
+ function TrackedDetailsScreen(): ReturnType<typeof createElement> {
144
+ const navigation = useNavigation();
145
+ const route = useRoute();
146
+ capturedName = route.name;
147
+ capturedParams = route.params;
148
+ useEffect(
149
+ () => navigation.addListener('focus', () => focusEvents.push('focus')),
150
+ [navigation],
151
+ );
152
+ return createElement('symbiote-text', {}, 'details');
153
+ }
154
+
155
+ const ref = createRef<INavigatorHandle>();
156
+ mount(
157
+ ROOT_TAG,
158
+ createElement(
159
+ Stack,
160
+ { ref, initialRouteName: 'Home' },
161
+ createElement(Stack.Screen, { name: 'Home', component: HomeScreen }),
162
+ createElement(Stack.Screen, { name: 'Details', component: TrackedDetailsScreen }),
163
+ ),
164
+ );
165
+ act(() => ref.current?.push('Details', { id: 7 }));
166
+ expect(capturedName).toBe('Details');
167
+ expect(capturedParams).toEqual({ id: 7 });
168
+
169
+ const details = screenNodes()[1];
170
+ act(() => fabric.fireEvent(details.instanceHandle, 'topAppear', {}));
171
+ expect(focusEvents).toEqual(['focus']);
172
+ });
173
+
174
+ it('useNavigationState reflects the route stack growing across a push', () => {
175
+ let routeCount: number | undefined;
176
+ function TrackedHomeScreen(): ReturnType<typeof createElement> {
177
+ routeCount = useNavigationState(state => state.routes.length);
178
+ return createElement('symbiote-text', {}, 'home');
179
+ }
180
+
181
+ const ref = createRef<INavigatorHandle>();
182
+ mount(
183
+ ROOT_TAG,
184
+ createElement(
185
+ Stack,
186
+ { ref, initialRouteName: 'Home' },
187
+ createElement(Stack.Screen, { name: 'Home', component: TrackedHomeScreen }),
188
+ createElement(Stack.Screen, { name: 'Details', component: DetailsScreen }),
189
+ ),
190
+ );
191
+ expect(routeCount).toBe(1);
192
+
193
+ act(() => ref.current?.push('Details'));
194
+ expect(routeCount).toBe(2);
195
+
196
+ act(() => ref.current?.pop());
197
+ expect(routeCount).toBe(1);
198
+ });
199
+ });
@@ -0,0 +1,12 @@
1
+ export { useNavigation } from './use-navigation';
2
+ export type { INavigationHandle } from './use-navigation';
3
+ export { useStackNavigation } from './use-stack-navigation';
4
+ export type { IStackNavigationHandle } from './use-stack-navigation';
5
+ export { useTabNavigation } from './use-tab-navigation';
6
+ export type { ITabNavigationHandle } from './use-tab-navigation';
7
+ export { useDrawerNavigation } from './use-drawer-navigation';
8
+ export type { IDrawerNavigationHandle } from './use-drawer-navigation';
9
+ export { useRoute } from './use-route';
10
+ export { useIsFocused } from './use-is-focused';
11
+ export { useFocusEffect } from './use-focus-effect';
12
+ export { useNavigationState } from './use-navigation-state';