@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,331 @@
1
+ // Drawer option types + the pure swipe/geometry math. Framework-agnostic (numbers and
2
+ // booleans only, per CLAUDE.md <prop_types_split_agnostic_vs_per_adapter>), shared verbatim by
3
+ // every adapter. Mirrors react-navigation's Drawer prop surface (confirmed against the current
4
+ // @react-navigation/drawer docs): drawerType 'front'/'back'/'slide'/'permanent', drawerPosition,
5
+ // drawerWidth, overlayColor, swipeEnabled + the three swipe-tuning knobs. What is NOT ported:
6
+ // `configureGestureHandler` - a react-native-gesture-handler-specific escape hatch with no
7
+ // PanResponder equivalent (see packages/navigation's README / the drawer feasibility note for the
8
+ // full gap list).
9
+
10
+ import type { IPanResponderGestureState, ISymbioteEvent } from '@symbiote-native/engine';
11
+ import { isRecord } from '../guards';
12
+ import type { IDrawerSlot } from '../render-drawer';
13
+
14
+ export type IDrawerType = 'front' | 'back' | 'slide' | 'permanent';
15
+ export type IDrawerPosition = 'left' | 'right';
16
+
17
+ export interface IDrawerOptions {
18
+ drawerType?: IDrawerType;
19
+ drawerPosition?: IDrawerPosition;
20
+ drawerWidth?: number;
21
+ overlayColor?: string;
22
+ swipeEnabled?: boolean;
23
+ // How far from the position edge a closed-drawer swipe must START to count (react-navigation
24
+ // default: 32).
25
+ swipeEdgeWidth?: number;
26
+ // Minimum accumulated drag distance to snap-open/close on release (react-navigation default: 60).
27
+ swipeMinDistance?: number;
28
+ // Minimum release velocity (px/ms, same unit as gestureState.vx) that snaps regardless of
29
+ // distance (react-navigation default: 500 px/s == 0.5 px/ms).
30
+ swipeMinVelocity?: number;
31
+ }
32
+
33
+ // Per-screen options a caller's own renderDrawerContent reads to label its menu entries - the
34
+ // drawer navigator has no built-in menu UI (matching react-native-drawer-layout's Drawer
35
+ // primitive, which the underlying @react-navigation/drawer itself builds its DrawerItemList on
36
+ // top of), so this stays deliberately small next to IScreenOptions's header surface.
37
+ export interface IDrawerScreenOptions {
38
+ title?: string;
39
+ drawerLabel?: string;
40
+ }
41
+
42
+ export const DRAWER_DEFAULT_TYPE: IDrawerType = 'front';
43
+ export const DRAWER_DEFAULT_POSITION: IDrawerPosition = 'left';
44
+ export const DRAWER_DEFAULT_WIDTH = 280;
45
+ export const DRAWER_DEFAULT_OVERLAY_COLOR = 'rgba(0, 0, 0, 0.5)';
46
+ export const DRAWER_DEFAULT_SWIPE_ENABLED = true;
47
+ export const DRAWER_DEFAULT_SWIPE_EDGE_WIDTH = 32;
48
+ export const DRAWER_DEFAULT_SWIPE_MIN_DISTANCE = 60;
49
+ // react-navigation's 500 is px/second; gestureState.vx is px/ms, so the default converts.
50
+ export const DRAWER_DEFAULT_SWIPE_MIN_VELOCITY = 0.5;
51
+
52
+ // Exported so render-drawer.ts and the adapter can resolve the same defaults without
53
+ // re-declaring the `??` fallback (a second copy would drift the instant a default changes).
54
+ export function resolveDrawerWidth(options: IDrawerOptions): number {
55
+ return options.drawerWidth ?? DRAWER_DEFAULT_WIDTH;
56
+ }
57
+
58
+ export function resolveDrawerType(options: IDrawerOptions): IDrawerType {
59
+ return options.drawerType ?? DRAWER_DEFAULT_TYPE;
60
+ }
61
+
62
+ export function resolveDrawerPosition(options: IDrawerOptions): IDrawerPosition {
63
+ return options.drawerPosition ?? DRAWER_DEFAULT_POSITION;
64
+ }
65
+
66
+ // The animated outputs a progress value of 0 (closed) -> 1 (open) drives, resolved once per
67
+ // drawerType/position/width combination. 'permanent' never animates (no gesture, no snap), so
68
+ // callers short-circuit on isDrawerAnimated before reading this.
69
+ export type IDrawerGeometry = {
70
+ panelTranslateXClosed: number;
71
+ panelTranslateXOpen: number;
72
+ contentTranslateXClosed: number;
73
+ contentTranslateXOpen: number;
74
+ overlayOpacityClosed: number;
75
+ overlayOpacityOpen: number;
76
+ };
77
+
78
+ export function isDrawerAnimated(options: IDrawerOptions): boolean {
79
+ return resolveDrawerType(options) !== 'permanent';
80
+ }
81
+
82
+ export function isDrawerOverlayVisible(options: IDrawerOptions): boolean {
83
+ const type = resolveDrawerType(options);
84
+ return type === 'front' || type === 'slide';
85
+ }
86
+
87
+ // front: only the panel moves (-width -> 0), content stays put, overlay fades in.
88
+ // back: only the content moves, away from the position edge, to reveal the stationary panel;
89
+ // no overlay (the panel sits fully behind the content, nothing to dim).
90
+ // slide: panel AND content move together by the same delta, overlay fades in (content still
91
+ // covers the panel partially as they slide in tandem, same as front visually at rest).
92
+ // permanent: static, all zero (isDrawerAnimated() gates callers off this path already).
93
+ export function resolveDrawerGeometry(options: IDrawerOptions): IDrawerGeometry {
94
+ const width = resolveDrawerWidth(options);
95
+ const type = resolveDrawerType(options);
96
+ const sign = resolveDrawerPosition(options) === 'left' ? 1 : -1;
97
+ const closedPanelX = -sign * width;
98
+ const openContentX = sign * width;
99
+
100
+ switch (type) {
101
+ case 'back':
102
+ return {
103
+ panelTranslateXClosed: 0,
104
+ panelTranslateXOpen: 0,
105
+ contentTranslateXClosed: 0,
106
+ contentTranslateXOpen: openContentX,
107
+ overlayOpacityClosed: 0,
108
+ overlayOpacityOpen: 0,
109
+ };
110
+ case 'slide':
111
+ return {
112
+ panelTranslateXClosed: closedPanelX,
113
+ panelTranslateXOpen: 0,
114
+ contentTranslateXClosed: 0,
115
+ contentTranslateXOpen: openContentX,
116
+ overlayOpacityClosed: 0,
117
+ overlayOpacityOpen: 1,
118
+ };
119
+ case 'front':
120
+ case 'permanent':
121
+ default:
122
+ return {
123
+ panelTranslateXClosed: closedPanelX,
124
+ panelTranslateXOpen: 0,
125
+ contentTranslateXClosed: 0,
126
+ contentTranslateXOpen: 0,
127
+ overlayOpacityClosed: 0,
128
+ overlayOpacityOpen: 1,
129
+ };
130
+ }
131
+ }
132
+
133
+ export type IDrawerInterpolationRange = {
134
+ inputRange: readonly [number, number];
135
+ outputRange: readonly [number, number];
136
+ };
137
+
138
+ export type IDrawerContentSlotInterpolation = {
139
+ translateX: IDrawerInterpolationRange;
140
+ };
141
+
142
+ export type IDrawerOverlaySlotInterpolation = {
143
+ opacity: IDrawerInterpolationRange;
144
+ translateX: IDrawerInterpolationRange;
145
+ };
146
+
147
+ export type IDrawerPanelSlotInterpolation = {
148
+ translateX: IDrawerInterpolationRange;
149
+ };
150
+
151
+ const DRAWER_PROGRESS_INPUT_RANGE: readonly [number, number] = [0, 1];
152
+
153
+ // The pure `{inputRange, outputRange}` half of each slot's `progress.interpolate(...)` call,
154
+ // factored out of react/vue/angular drawer.ts (the same literal config recomputed inline in all
155
+ // three, including three near-identical spots within angular/drawer.ts's own content/overlay/
156
+ // panel style getters) - the adapter still owns the actual `Animated.Value.interpolate()` call
157
+ // (the `Animated.Value` instance is adapter-lifecycle-held per CLAUDE.md
158
+ // <adapters_stay_thin>), this only computes what to feed it.
159
+ //
160
+ // The three slots are NOT symmetric: content and panel each drive only their own translateX, but
161
+ // overlay drives BOTH its own opacity AND a translateX that deliberately tracks CONTENT's delta,
162
+ // not its own - see render-drawer.ts's `drawerChildOrder` header: for `slide`, content itself
163
+ // slides away, and an overlay that didn't follow it would stay pinned full-screen instead of
164
+ // dimming just the content it's meant to cover. The overload set below reflects that asymmetry in
165
+ // the return type per slot, instead of forcing one shape that would leave 'content'/'panel'
166
+ // carrying an unused `opacity` field or 'overlay' missing one it needs.
167
+ export function resolveDrawerSlotInterpolation(
168
+ geometry: IDrawerGeometry,
169
+ slot: 'content',
170
+ ): IDrawerContentSlotInterpolation;
171
+ export function resolveDrawerSlotInterpolation(
172
+ geometry: IDrawerGeometry,
173
+ slot: 'overlay',
174
+ ): IDrawerOverlaySlotInterpolation;
175
+ export function resolveDrawerSlotInterpolation(
176
+ geometry: IDrawerGeometry,
177
+ slot: 'panel',
178
+ ): IDrawerPanelSlotInterpolation;
179
+ export function resolveDrawerSlotInterpolation(
180
+ geometry: IDrawerGeometry,
181
+ slot: IDrawerSlot,
182
+ ):
183
+ | IDrawerContentSlotInterpolation
184
+ | IDrawerOverlaySlotInterpolation
185
+ | IDrawerPanelSlotInterpolation {
186
+ switch (slot) {
187
+ case 'content':
188
+ return {
189
+ translateX: {
190
+ inputRange: DRAWER_PROGRESS_INPUT_RANGE,
191
+ outputRange: [geometry.contentTranslateXClosed, geometry.contentTranslateXOpen],
192
+ },
193
+ };
194
+ case 'overlay':
195
+ return {
196
+ opacity: {
197
+ inputRange: DRAWER_PROGRESS_INPUT_RANGE,
198
+ outputRange: [geometry.overlayOpacityClosed, geometry.overlayOpacityOpen],
199
+ },
200
+ translateX: {
201
+ inputRange: DRAWER_PROGRESS_INPUT_RANGE,
202
+ outputRange: [geometry.contentTranslateXClosed, geometry.contentTranslateXOpen],
203
+ },
204
+ };
205
+ case 'panel':
206
+ return {
207
+ translateX: {
208
+ inputRange: DRAWER_PROGRESS_INPUT_RANGE,
209
+ outputRange: [geometry.panelTranslateXClosed, geometry.panelTranslateXOpen],
210
+ },
211
+ };
212
+ }
213
+ }
214
+
215
+ // onStartShouldSetPanResponder gate: while closed, a swipe must START within swipeEdgeWidth of
216
+ // the drawer's position edge (mirrors react-navigation's swipeEdgeWidth). While open, the whole
217
+ // content + overlay area is fair game to drag-close, matching the intuitive "swipe anywhere to
218
+ // dismiss an open drawer" behavior every drawer implementation (including RN's dropped native
219
+ // DrawerLayoutAndroid) shares.
220
+ export function isSwipeStartInEdge(
221
+ startX: number,
222
+ screenWidth: number,
223
+ isOpen: boolean,
224
+ options: IDrawerOptions,
225
+ ): boolean {
226
+ if (isOpen) return true;
227
+ const edgeWidth = options.swipeEdgeWidth ?? DRAWER_DEFAULT_SWIPE_EDGE_WIDTH;
228
+ return resolveDrawerPosition(options) === 'left'
229
+ ? startX <= edgeWidth
230
+ : startX >= screenWidth - edgeWidth;
231
+ }
232
+
233
+ // onMoveShouldSetPanResponder gate: only claim the gesture once it reads as a horizontal drag
234
+ // (RN's own PanResponder examples use this exact dominant-axis check to stay out of a vertical
235
+ // ScrollView's way).
236
+ const MOVE_CLAIM_THRESHOLD = 5;
237
+
238
+ export function isHorizontalDrag(gestureState: IPanResponderGestureState): boolean {
239
+ return (
240
+ Math.abs(gestureState.dx) > MOVE_CLAIM_THRESHOLD &&
241
+ Math.abs(gestureState.dx) > Math.abs(gestureState.dy)
242
+ );
243
+ }
244
+
245
+ export type ISwipeIntent = 'open' | 'close';
246
+
247
+ // The release-time decision, structurally the same shape as switch.ts's shouldSnapBack: a pure
248
+ // predicate over the accumulated gesture plus the option thresholds, no framework. Distance OR
249
+ // velocity past its threshold, in the direction that would move the drawer toward the opposite
250
+ // of its current state, snaps it there; anything else snaps back to where it started.
251
+ export function resolveSwipeIntent(
252
+ gestureState: IPanResponderGestureState,
253
+ isOpen: boolean,
254
+ options: IDrawerOptions,
255
+ ): ISwipeIntent {
256
+ const sign = resolveDrawerPosition(options) === 'left' ? 1 : -1;
257
+ // Positive `signedDelta` means "dragging toward open" for a left drawer's rightward drag (and
258
+ // symmetrically for a right drawer's leftward drag).
259
+ const signedDx = sign * gestureState.dx;
260
+ const signedVx = sign * gestureState.vx;
261
+ const minDistance = options.swipeMinDistance ?? DRAWER_DEFAULT_SWIPE_MIN_DISTANCE;
262
+ const minVelocity = options.swipeMinVelocity ?? DRAWER_DEFAULT_SWIPE_MIN_VELOCITY;
263
+
264
+ const pastDistance = Math.abs(signedDx) >= minDistance;
265
+ const pastVelocity = Math.abs(signedVx) >= minVelocity;
266
+ if (!pastDistance && !pastVelocity) return isOpen ? 'open' : 'close';
267
+
268
+ // Velocity wins the direction call when it clears its own threshold (a fast flick can reverse
269
+ // a short drag); otherwise the drag distance's sign decides.
270
+ const towardOpen = pastVelocity ? signedVx > 0 : signedDx > 0;
271
+ return towardOpen ? 'open' : 'close';
272
+ }
273
+
274
+ export function clamp01(value: number): number {
275
+ return Math.min(1, Math.max(0, value));
276
+ }
277
+
278
+ function toFiniteNumber(value: unknown): number | undefined {
279
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
280
+ }
281
+
282
+ // The touch's real starting page-X, read off the raw event rather than gestureState.x0: the
283
+ // engine's capture phase (core/engine/src/pan-responder/index.ts's
284
+ // onStartShouldSetResponderCapture/onMoveShouldSetResponderCapture) resets gestureState before
285
+ // the bubble-phase onStartShouldSetPanResponder/onMoveShouldSetPanResponder gates run, so x0 is
286
+ // only populated later, inside onResponderGrant. Shaped like core/engine/src/events/index.ts's
287
+ // readTouchPoint but not the same function: that one requires both pageX AND pageY and returns
288
+ // an {x, y} pair for touch-history bookkeeping; this only needs pageX and is private to the
289
+ // SET-gate below, so it stays a separate, narrower helper rather than forcing a shared contract
290
+ // onto two callers with different needs.
291
+ export function startPageXOf(event: ISymbioteEvent): number | undefined {
292
+ const { nativeEvent } = event;
293
+ const direct = toFiniteNumber(nativeEvent.pageX);
294
+ if (direct !== undefined) return direct;
295
+ const touches = nativeEvent.touches;
296
+ if (Array.isArray(touches) && isRecord(touches[0])) return toFiniteNumber(touches[0].pageX);
297
+ return undefined;
298
+ }
299
+
300
+ // onPanResponderMove's drag-to-progress math: how far the touch has traveled, in progress units
301
+ // (0..1), added to wherever the drag started from. Mirrors the SAME sign convention as
302
+ // resolveSwipeIntent (left drawer: rightward drag is positive-toward-open).
303
+ export function resolveDragProgress(
304
+ gestureState: IPanResponderGestureState,
305
+ dragStartProgress: number,
306
+ options: IDrawerOptions,
307
+ ): number {
308
+ const width = resolveDrawerWidth(options);
309
+ const sign = resolveDrawerPosition(options) === 'right' ? -1 : 1;
310
+ const delta = (sign * gestureState.dx) / width;
311
+ return clamp01(dragStartProgress + delta);
312
+ }
313
+
314
+ // The composed onStartShouldSetPanResponder/onMoveShouldSetPanResponder gate: swipeEnabled ->
315
+ // isDrawerAnimated -> isSwipeStartInEdge, then (move only) isHorizontalDrag on top. The start
316
+ // gate omits the dominant-axis check on purpose (react-navigation's own edge-swipe idiom: claim
317
+ // on entering the edge, confirm direction only once movement exists to measure).
318
+ export function shouldClaimDrawerSwipe(
319
+ event: ISymbioteEvent,
320
+ gestureState: IPanResponderGestureState,
321
+ screenWidth: number,
322
+ isOpen: boolean,
323
+ options: IDrawerOptions,
324
+ phase: 'start' | 'move',
325
+ ): boolean {
326
+ if ((options.swipeEnabled ?? DRAWER_DEFAULT_SWIPE_ENABLED) === false) return false;
327
+ if (!isDrawerAnimated(options)) return false;
328
+ const startX = startPageXOf(event) ?? gestureState.x0;
329
+ if (!isSwipeStartInEdge(startX, screenWidth, isOpen, options)) return false;
330
+ return phase === 'start' ? true : isHorizontalDrag(gestureState);
331
+ }
@@ -0,0 +1,116 @@
1
+ // Co-located unit test (ADR 0025) for the pure drawer router reducer - sibling to
2
+ // navigator-state.test.ts (stack). Unlike the stack's push/pop array, the drawer's route list
3
+ // never changes shape (see drawer-router-state.ts's header): only which route is focused
4
+ // (`index`) and whether the panel is open (`isOpen`) can change, so every action here is a
5
+ // jumpTo/open/close/toggle over a FIXED route array.
6
+
7
+ import { describe, expect, it } from 'vitest';
8
+ import { createInitialDrawerRouterState, drawerRouterReducer, focusedDrawerRoute } from './index';
9
+ import type { IDrawerRouterState } from './index';
10
+
11
+ const HOME = { key: 'home-1', name: 'Home', params: undefined };
12
+ const PROFILE = { key: 'profile-1', name: 'Profile', params: { tab: 'feed' } };
13
+ const SETTINGS = { key: 'settings-1', name: 'Settings', params: undefined };
14
+
15
+ function closedState(index = 0): IDrawerRouterState {
16
+ return { routes: [HOME, PROFILE, SETTINGS], index, isOpen: false };
17
+ }
18
+
19
+ function openState(index = 0): IDrawerRouterState {
20
+ return { routes: [HOME, PROFILE, SETTINGS], index, isOpen: true };
21
+ }
22
+
23
+ describe('createInitialDrawerRouterState', () => {
24
+ it('focuses the first route and starts closed when no initialRouteName is given', () => {
25
+ expect(createInitialDrawerRouterState([HOME, PROFILE])).toEqual({
26
+ routes: [HOME, PROFILE],
27
+ index: 0,
28
+ isOpen: false,
29
+ });
30
+ });
31
+
32
+ it('focuses the route matching initialRouteName', () => {
33
+ const state = createInitialDrawerRouterState([HOME, PROFILE, SETTINGS], 'Settings');
34
+ expect(state.index).toBe(2);
35
+ expect(state.isOpen).toBe(false);
36
+ });
37
+
38
+ it('falls back to the first route when initialRouteName matches nothing', () => {
39
+ expect(createInitialDrawerRouterState([HOME, PROFILE], 'Nowhere').index).toBe(0);
40
+ });
41
+ });
42
+
43
+ describe('drawerRouterReducer — jumpTo', () => {
44
+ it('focuses the named route', () => {
45
+ const next = drawerRouterReducer(closedState(0), { type: 'jumpTo', name: 'Profile' });
46
+ expect(next.index).toBe(1);
47
+ expect(next.isOpen).toBe(false);
48
+ });
49
+
50
+ it('also closes an already-open drawer when jumping to a different route', () => {
51
+ const next = drawerRouterReducer(openState(0), { type: 'jumpTo', name: 'Profile' });
52
+ expect(next.index).toBe(1);
53
+ expect(next.isOpen).toBe(false);
54
+ });
55
+
56
+ it('closes an open drawer even when jumping to the already-focused route', () => {
57
+ // Selecting a destination is itself the dismissal gesture (the reducer's own comment) -
58
+ // that still holds when the destination is the one already on screen.
59
+ const state = openState(0);
60
+ const next = drawerRouterReducer(state, { type: 'jumpTo', name: 'Home' });
61
+ expect(next).not.toBe(state);
62
+ expect(next).toEqual({ ...state, isOpen: false });
63
+ });
64
+
65
+ it('is a no-op for an unknown route name, same reference returned', () => {
66
+ const state = closedState(0);
67
+ expect(drawerRouterReducer(state, { type: 'jumpTo', name: 'Nowhere' })).toBe(state);
68
+ });
69
+
70
+ it('is a no-op when already focused on that route while closed, same reference returned', () => {
71
+ const state = closedState(0);
72
+ expect(drawerRouterReducer(state, { type: 'jumpTo', name: 'Home' })).toBe(state);
73
+ });
74
+ });
75
+
76
+ describe('drawerRouterReducer — openDrawer', () => {
77
+ it('opens a closed drawer', () => {
78
+ expect(drawerRouterReducer(closedState(), { type: 'openDrawer' }).isOpen).toBe(true);
79
+ });
80
+
81
+ it('is a no-op on an already-open drawer, same reference returned', () => {
82
+ const state = openState();
83
+ expect(drawerRouterReducer(state, { type: 'openDrawer' })).toBe(state);
84
+ });
85
+ });
86
+
87
+ describe('drawerRouterReducer — closeDrawer', () => {
88
+ it('closes an open drawer', () => {
89
+ expect(drawerRouterReducer(openState(), { type: 'closeDrawer' }).isOpen).toBe(false);
90
+ });
91
+
92
+ it('is a no-op on an already-closed drawer, same reference returned', () => {
93
+ const state = closedState();
94
+ expect(drawerRouterReducer(state, { type: 'closeDrawer' })).toBe(state);
95
+ });
96
+ });
97
+
98
+ describe('drawerRouterReducer — toggleDrawer', () => {
99
+ it('opens a closed drawer', () => {
100
+ expect(drawerRouterReducer(closedState(), { type: 'toggleDrawer' }).isOpen).toBe(true);
101
+ });
102
+
103
+ it('closes an open drawer', () => {
104
+ expect(drawerRouterReducer(openState(), { type: 'toggleDrawer' }).isOpen).toBe(false);
105
+ });
106
+ });
107
+
108
+ describe('focusedDrawerRoute', () => {
109
+ it('returns the route at the current index', () => {
110
+ expect(focusedDrawerRoute(closedState(1))).toBe(PROFILE);
111
+ });
112
+
113
+ it('returns undefined when the route list is empty', () => {
114
+ expect(focusedDrawerRoute(createInitialDrawerRouterState([]))).toBeUndefined();
115
+ });
116
+ });
@@ -0,0 +1,71 @@
1
+ // DrawerRouter: the logic half (framework-agnostic, zero render), sibling to navigator-state.ts
2
+ // and tab-router-state.ts. Unlike the stack's push/pop route array, a drawer's route list is
3
+ // FIXED at mount (one entry per declared <Drawer.Screen>, collected by the adapter exactly like
4
+ // Stack's/Tab's collectRegistry) - the reducer only tracks which one is focused (`index`) and
5
+ // whether the drawer panel is open. Route identity/shape is `IRoute`, reused verbatim from
6
+ // navigator-state.ts (same convention tab-router-state.ts follows) so all three navigators share
7
+ // one route shape.
8
+
9
+ import type { IRoute } from '../navigator-state';
10
+
11
+ export type { IRoute } from '../navigator-state';
12
+
13
+ type IRouteEntry = IRoute<unknown>;
14
+
15
+ export type IDrawerRouterState = Readonly<{
16
+ routes: readonly IRouteEntry[];
17
+ index: number;
18
+ isOpen: boolean;
19
+ }>;
20
+
21
+ export type IDrawerRouterAction =
22
+ | { type: 'jumpTo'; name: string }
23
+ | { type: 'openDrawer' }
24
+ | { type: 'closeDrawer' }
25
+ | { type: 'toggleDrawer' };
26
+
27
+ const INITIAL_FOCUSED_INDEX = 0;
28
+
29
+ export function createInitialDrawerRouterState(
30
+ routes: readonly IRouteEntry[],
31
+ initialRouteName?: string,
32
+ ): IDrawerRouterState {
33
+ if (initialRouteName === undefined) {
34
+ return { routes, index: INITIAL_FOCUSED_INDEX, isOpen: false };
35
+ }
36
+ const index = routes.findIndex(route => route.name === initialRouteName);
37
+ return { routes, index: index === -1 ? INITIAL_FOCUSED_INDEX : index, isOpen: false };
38
+ }
39
+
40
+ // Mirrors @react-navigation/drawer's DrawerActions: jumpTo focuses a route by NAME (TabRouter's
41
+ // own convention, not the stack's by-key popTo) AND closes the drawer - selecting a destination
42
+ // is itself the dismissal gesture, so a plain focus without also closing would leave the panel
43
+ // covering the just-selected screen.
44
+ export function drawerRouterReducer(
45
+ state: IDrawerRouterState,
46
+ action: IDrawerRouterAction,
47
+ ): IDrawerRouterState {
48
+ switch (action.type) {
49
+ case 'jumpTo': {
50
+ const index = state.routes.findIndex(route => route.name === action.name);
51
+ if (index === -1) return state;
52
+ return index === state.index && !state.isOpen ? state : { ...state, index, isOpen: false };
53
+ }
54
+
55
+ case 'openDrawer':
56
+ return state.isOpen ? state : { ...state, isOpen: true };
57
+
58
+ case 'closeDrawer':
59
+ return state.isOpen ? { ...state, isOpen: false } : state;
60
+
61
+ case 'toggleDrawer':
62
+ return { ...state, isOpen: !state.isOpen };
63
+
64
+ default:
65
+ return state;
66
+ }
67
+ }
68
+
69
+ export function focusedDrawerRoute(state: IDrawerRouterState): IRouteEntry | undefined {
70
+ return state.routes[state.index];
71
+ }
@@ -0,0 +1,23 @@
1
+ // Runtime guards shared across the navigation package's core. Centralizes what used to be ~11
2
+ // independent copies of the same `typeof value === 'object' && value !== null` check scattered
3
+ // across core and every adapter (route-param merges, persisted-JSON validation, vnode-prop
4
+ // narrowing, gesture-event field reads) - several of which had silently drifted on array
5
+ // handling, one excluding arrays, another not, for the same kind of check.
6
+ //
7
+ // Excludes arrays: every current call site (route params, persisted navigator-state JSON,
8
+ // touch-event objects, vnode props) only ever receives a genuine plain object, never a bare list
9
+ // standing in for one - so `Array.isArray` narrows out a real footgun (a malformed persisted
10
+ // value shaped like `[]` would otherwise pass this guard and fail confusingly deeper in, on a
11
+ // missing `.key`/`.name` field, instead of here) at zero cost to the realistic inputs.
12
+ export function isRecord(value: unknown): value is Record<string, unknown> {
13
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
14
+ }
15
+
16
+ // Mirrors @react-navigation's CommonActions.setParams: a shallow merge onto the current params,
17
+ // but only when both sides are genuine plain objects - an array (or any other non-record) on
18
+ // either side means a clean replace instead, never a numeric-key-mangled merge. Shared by
19
+ // navigator-state.ts (Stack) and tab-router-state.ts (Tabs) so the two reducers can't drift on
20
+ // this rule independently.
21
+ export function mergeParams(current: unknown, incoming: unknown): unknown {
22
+ return isRecord(current) && isRecord(incoming) ? { ...current, ...incoming } : incoming;
23
+ }