@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,414 @@
1
+ // Navigator: framework-agnostic render half. A screen's content is an arbitrary framework
2
+ // subtree the adapter owns (unlike the slider's one native leaf), so these resolvers hand back
3
+ // plain props objects for the RNSScreen/RNSScreenStack leaves the adapter builds itself with real
4
+ // children, and one full Descriptor for the header config. The header config Descriptor never
5
+ // takes framework children (headerLeftBarButtonItems/headerRightBarButtonItems are native config
6
+ // arrays from screen-options.ts, not elements), but can nest one RNSSearchBar leaf wrapped in an
7
+ // RNSScreenStackHeaderSubview(type: 'searchBar'), matching how react-native-screens' own
8
+ // ScreenStackHeaderSearchBarView mounts it. RNSScreenStackHeaderConfig.mm's mountChildComponentView
9
+ // rejects a bare RNSSearchBar child natively ("only accepts children of type
10
+ // ScreenStackHeaderSubview") and reads the search bar back out via `subview.subviews[0]`.
11
+
12
+ import { el } from '@symbiote-native/components';
13
+ import type { IDescriptor } from '@symbiote-native/components';
14
+ import type { ISymbioteEvent } from '@symbiote-native/engine';
15
+ import {
16
+ HEADER_ON_PRESS_BAR_BUTTON_ITEM,
17
+ HEADER_ON_PRESS_BAR_BUTTON_MENU_ITEM,
18
+ RNS_MODAL_SCREEN_VIEW_NAME,
19
+ RNS_SCREEN_STACK_HEADER_CONFIG_VIEW_NAME,
20
+ RNS_SCREEN_STACK_HEADER_SUBVIEW_VIEW_NAME,
21
+ RNS_SCREEN_VIEW_NAME,
22
+ RNS_SEARCH_BAR_VIEW_NAME,
23
+ SEARCH_BAR_ON_BLUR,
24
+ SEARCH_BAR_ON_CANCEL_BUTTON_PRESS,
25
+ SEARCH_BAR_ON_CHANGE_TEXT,
26
+ SEARCH_BAR_ON_CLOSE,
27
+ SEARCH_BAR_ON_FOCUS,
28
+ SEARCH_BAR_ON_OPEN,
29
+ SEARCH_BAR_ON_SEARCH_BUTTON_PRESS,
30
+ STACK_DEFAULT_ANIMATION,
31
+ STACK_DEFAULT_PRESENTATION,
32
+ } from './constants';
33
+ import { computeActivityState } from './navigator-state';
34
+ import type {
35
+ IHeaderConfigViewProps,
36
+ INavigatorPlatform,
37
+ IScreenOptions,
38
+ IScreenViewProps,
39
+ ISearchBarOptions,
40
+ ISearchBarViewProps,
41
+ IStackPresentation,
42
+ IStackViewProps,
43
+ } from './navigator-props';
44
+ import { resolveHeaderConfigView, resolveScreenView, resolveSearchBarView } from './screen-options';
45
+
46
+ // Mirrors react-native-screens' ScreenStackItem.tsx (getPositioningStyle): a formSheet's content
47
+ // wrapper does not get `flex: 1`. RNSScreen.mm's updateBounds pushes the sheet's live native size
48
+ // into Fabric's shadow state on every frame of a detent drag, and a `bottom: 0`-pinned wrapper
49
+ // forces React to set a strict frame on every update, causing the flicker PR #1870 fixed by
50
+ // switching to `absoluteWithNoBottom` (RNSScreen.mm: "we do not set bottom: 0 in JS for wrapper of
51
+ // the screen content, causing React to not set strict frame every time the sheet size is updated").
52
+ // Tradeoff: content shorter than the sheet's current detent leaves a gap below it.
53
+ // RNSScreenContentWrapper.mm's coerceChildScrollViewComponentSizeToSize fixes that natively, but
54
+ // only for a ScrollView child, which it resizes directly to the sheet's real frame bypassing
55
+ // Yoga/flex, not something reachable from a style choice here.
56
+ const SCREEN_CONTENT_WRAPPER_FLEX_STYLE = { flex: 1 };
57
+ const SHEET_CONTENT_WRAPPER_STYLE = {
58
+ position: 'absolute',
59
+ top: 0,
60
+ start: 0,
61
+ end: 0,
62
+ bottom: undefined,
63
+ };
64
+
65
+ // Android's native header (react-native-screens' CustomToolbar in a CoordinatorLayout alongside
66
+ // an AppBarLayout) auto-offsets screen content below the header via a Material
67
+ // `AppBarLayout.ScrollingViewBehavior` (ScreenStackFragment.kt's setToolbarTranslucent: `behavior
68
+ // = if (translucent) null else ScrollingViewBehavior()`). A plain (non-translucent) header gets
69
+ // this offset for free and needs no JS compensation. `headerTranslucent: true` removes that
70
+ // behavior: react-native-screens' contract for translucent is "background goes away AND content
71
+ // is no longer auto-pushed below it", meant for a screen painting under a see-through header. An
72
+ // app that sets headerTranslucent just for a custom (still opaque) header look, while relying on
73
+ // its own SafeAreaView for safe-area content, loses the free offset and gets clipped:
74
+ // SafeAreaView's Android inset only reflects WindowInsetsCompat's systemBars() (status
75
+ // bar/cutout), with no awareness of the sibling Toolbar, so content paints its first 56dp under
76
+ // the header. Verified empirically (2026-07-10) via `adb shell uiautomator dump`: a
77
+ // translucent-header screen's content measured starting at y=0 (see the
78
+ // navigation-header-content-offset skill for the full incident). iOS's UINavigationController
79
+ // already lays out below its real nav bar regardless of translucency, so this compensation is
80
+ // Android-only and applies only when translucent (a non-translucent header already gets the
81
+ // native offset; padding there would double it).
82
+ const ANDROID_HEADER_TOOLBAR_HEIGHT = 56;
83
+
84
+ // react-native-screens' own Screen.tsx picks a different Fabric component for a modally-presented
85
+ // screen ('RNSModalScreen' instead of plain 'RNSScreen') because "there is a need for different
86
+ // shadow nodes" (its own comment). RNSScreen.mm's updateLayoutMetrics only skips applying Yoga's
87
+ // computed frame for `[self isKindOfClass:RNSModalScreen.class]` ("the available space is most
88
+ // likely restricted & differs from what Yoga resolves during first layout - we want to rely on
89
+ // native layout here"). Emitting plain 'RNSScreen' for a formSheet/modal means that check never
90
+ // matches, so Yoga's frame (computed as an ordinary push-stack sibling) applies uncorrected,
91
+ // shifting the modal down by one full screen height (see register.ts's incident note). Mirrors
92
+ // Screen.tsx's `shouldUseModalScreenComponent` exactly, including the iOS-only scope (Android
93
+ // sizes modals natively regardless of Fabric view name).
94
+ export function resolveScreenViewName(
95
+ stackPresentation: IStackPresentation | undefined,
96
+ isAndroid: boolean,
97
+ ): string {
98
+ if (isAndroid) return RNS_SCREEN_VIEW_NAME;
99
+ const isPlainPresentation =
100
+ stackPresentation === undefined ||
101
+ stackPresentation === 'push' ||
102
+ stackPresentation === 'containedModal' ||
103
+ stackPresentation === 'containedTransparentModal';
104
+ return isPlainPresentation ? RNS_SCREEN_VIEW_NAME : RNS_MODAL_SCREEN_VIEW_NAME;
105
+ }
106
+
107
+ export function resolveScreenContentWrapperStyle(
108
+ stackPresentation: IStackPresentation | undefined,
109
+ headerHidden: boolean,
110
+ headerTranslucent: boolean | undefined,
111
+ isAndroid: boolean,
112
+ ): Record<string, unknown> {
113
+ let baseStyles;
114
+
115
+ if (stackPresentation !== 'formSheet') {
116
+ baseStyles = SCREEN_CONTENT_WRAPPER_FLEX_STYLE;
117
+ } else if (isAndroid) {
118
+ baseStyles = {
119
+ ...SHEET_CONTENT_WRAPPER_STYLE,
120
+ bottom: 0,
121
+ };
122
+ } else {
123
+ baseStyles = SHEET_CONTENT_WRAPPER_STYLE;
124
+ }
125
+
126
+ // paddingTop compensates the Android-only ScrollingViewBehavior removal above; iOS never needs
127
+ // it, so the `!isAndroid` check must stay.
128
+ if (!isAndroid || headerHidden || !headerTranslucent) return baseStyles;
129
+ return { ...baseStyles, paddingTop: ANDROID_HEADER_TOOLBAR_HEIGHT };
130
+ }
131
+
132
+ // A modally-presented screen (anything but 'push') has no UINavigationController on iOS, so
133
+ // RNSScreenStackHeaderConfig has nothing to attach a header bar to. react-native-screens' own
134
+ // ScreenStackItem.tsx (isHeaderInModal) works around this by nesting header + content inside a
135
+ // second, inner RNSScreenStack/RNSScreen pair purely to host the native nav bar. This is a UIKit
136
+ // constraint, not a React-renderer one, so it applies identically here. Android has no such
137
+ // requirement (its header draws without a nav-controller host).
138
+ export function isHeaderInModal(
139
+ stackPresentation: IStackPresentation | undefined,
140
+ headerHidden: boolean,
141
+ isAndroid: boolean,
142
+ ): boolean {
143
+ if (isAndroid) return false;
144
+ return (stackPresentation ?? STACK_DEFAULT_PRESENTATION) !== 'push' && !headerHidden;
145
+ }
146
+
147
+ // Inner-modal wrapper styles for isHeaderInModal: the inner RNSScreenStack fills its outer
148
+ // RNSScreen, and the inner RNSScreen fills the stack absolutely on all four edges. This mirrors
149
+ // react-native-screens' own `styles.container` / `StyleSheet.absoluteFill` for this exact pair.
150
+ const HEADER_IN_MODAL_STACK_STYLE = { flex: 1 };
151
+ const HEADER_IN_MODAL_SCREEN_STYLE = { position: 'absolute', top: 0, start: 0, end: 0, bottom: 0 };
152
+
153
+ export function resolveHeaderInModalStackStyle(): Record<string, unknown> {
154
+ return HEADER_IN_MODAL_STACK_STYLE;
155
+ }
156
+
157
+ export function resolveHeaderInModalScreenStyle(): Record<string, unknown> {
158
+ return HEADER_IN_MODAL_SCREEN_STYLE;
159
+ }
160
+
161
+ // The RNSScreen leaf's own props; the adapter spreads this record directly onto the element it
162
+ // builds and appends the header config child and real screen content itself, not here.
163
+ export function resolveScreenProps(view: IScreenViewProps): Record<string, unknown> {
164
+ return {
165
+ ...view.passthrough,
166
+ screenId: view.screenId,
167
+ activityState: view.activityState,
168
+ gestureEnabled: view.gestureEnabled,
169
+ stackAnimation: view.stackAnimation ?? STACK_DEFAULT_ANIMATION,
170
+ stackPresentation: view.stackPresentation ?? STACK_DEFAULT_PRESENTATION,
171
+ // Off by default upstream; a formSheet's updateBounds pushes the sheet's live native size
172
+ // into Fabric's shadow state on every drag frame, and this switches that push from
173
+ // Asynchronous to EventQueue::UpdateMode::unstable_Immediate. The ScrollView-frame correction
174
+ // that fills a taller detent (RNSScreenContentWrapper.mm's
175
+ // coerceChildScrollViewComponentSizeToSize) is driven by that same state, so an async push
176
+ // likely lags one step behind a fast drag. Scoped to formSheet: push/plain-modal screens have
177
+ // no comparable live-resize need.
178
+ synchronousShadowStateUpdatesEnabled: view.stackPresentation === 'formSheet',
179
+ transitionDuration: view.transitionDuration,
180
+ sheetAllowedDetents: view.sheetAllowedDetents,
181
+ // Native RNSScreen's real prop names carry an "Index" suffix (react-native-screens' own
182
+ // types.tsx); without it the value never reaches the native view and silently falls back to
183
+ // RNS's default.
184
+ sheetLargestUndimmedDetentIndex: view.sheetLargestUndimmedDetent,
185
+ sheetInitialDetentIndex: view.sheetInitialDetent,
186
+ sheetGrabberVisible: view.sheetGrabberVisible,
187
+ sheetCornerRadius: view.sheetCornerRadius,
188
+ sheetExpandsWhenScrolledToEdge: view.sheetExpandsWhenScrolledToEdge,
189
+ sheetElevation: view.sheetElevation,
190
+ sheetShouldOverflowTopInset: view.sheetShouldOverflowTopInset,
191
+ sheetDefaultResizeAnimationEnabled: view.sheetDefaultResizeAnimationEnabled,
192
+ statusBarStyle: view.statusBarStyle,
193
+ statusBarHidden: view.statusBarHidden,
194
+ statusBarAnimation: view.statusBarAnimation,
195
+ screenOrientation: view.screenOrientation,
196
+ };
197
+ }
198
+
199
+ // `style: {flex: 1}` is ours, not react-native-screens': RNSScreenStackView sizes its nav
200
+ // controller to `self.bounds`, which Yoga would otherwise collapse to zero, and (unlike RN's
201
+ // own ScreenStack.tsx) we drive the native view directly (<third_party_rn_packages_are_react_only>).
202
+ export function resolveStackProps(view: IStackViewProps): Record<string, unknown> {
203
+ return { style: { flex: 1 }, ...view.passthrough };
204
+ }
205
+
206
+ // The RNSScreenStackHeaderConfig leaf is a full Descriptor since it never hosts framework children,
207
+ // so the adapter bridges it verbatim (descriptorToReact/descriptorToVue) as the screen's first
208
+ // child, ahead of the real content. `searchBarProps` nests as an RNSSearchBar wrapped in an
209
+ // RNSScreenStackHeaderSubview(type: 'searchBar'), matching how react-native-screens' own JS
210
+ // component mounts it (see resolveSearchBarView's comment in screen-options.ts); omitted when the
211
+ // screen has no search bar.
212
+ export function renderHeaderConfig(
213
+ view: IHeaderConfigViewProps,
214
+ searchBarProps?: Record<string, unknown>,
215
+ ): IDescriptor {
216
+ const props: Record<string, unknown> = {
217
+ ...view.passthrough,
218
+ title: view.title,
219
+ hidden: view.hidden,
220
+ // Android-only (CustomToolbar.kt gates its WindowInsets padding on these; iOS's
221
+ // UINavigationController lays out under the safe area regardless). Upstream's JS
222
+ // ScreenStackHeaderConfig computes these via an EdgeInsetApplicationContext that coordinates a
223
+ // "consume once" top inset across nested headers; we drive RNSScreenStackHeaderConfig directly
224
+ // and skip that layer, so without this the native default (false) leaves the header glued
225
+ // under the status bar/cutout on Android. Every adapter renders one active header at a time,
226
+ // so consuming all four edges matches upstream's default for the common case; a genuine
227
+ // nested-header edge case would need upstream's same coordination.
228
+ consumeTopInset: !view.hidden,
229
+ consumeLeftInset: !view.hidden,
230
+ consumeRightInset: !view.hidden,
231
+ consumeBottomInset: !view.hidden,
232
+ backTitle: view.backTitle,
233
+ backTitleVisible: view.backTitleVisible,
234
+ backButtonDisplayMode: view.backButtonDisplayMode,
235
+ largeTitle: view.largeTitle,
236
+ translucent: view.translucent,
237
+ color: view.color,
238
+ titleColor: view.titleColor,
239
+ backgroundColor: view.backgroundColor,
240
+ largeTitleBackgroundColor: view.largeTitleBackgroundColor,
241
+ userInterfaceStyle: view.userInterfaceStyle,
242
+ headerLeftBarButtonItems: view.headerLeftBarButtonItems,
243
+ headerRightBarButtonItems: view.headerRightBarButtonItems,
244
+ [HEADER_ON_PRESS_BAR_BUTTON_ITEM]: view.onPressHeaderBarButtonItem,
245
+ [HEADER_ON_PRESS_BAR_BUTTON_MENU_ITEM]: view.onPressHeaderBarButtonMenuItem,
246
+ };
247
+ const children = searchBarProps
248
+ ? [
249
+ el(RNS_SCREEN_STACK_HEADER_SUBVIEW_VIEW_NAME, { type: 'searchBar' }, [
250
+ el(RNS_SEARCH_BAR_VIEW_NAME, searchBarProps),
251
+ ]),
252
+ ]
253
+ : [];
254
+ return el(RNS_SCREEN_STACK_HEADER_CONFIG_VIEW_NAME, props, children);
255
+ }
256
+
257
+ // The RNSSearchBar leaf's static config props; event handlers ride in `passthrough`.
258
+ // renderHeaderConfig wraps this into the RNSSearchBar Descriptor and nests it as the header
259
+ // config's child.
260
+ export function resolveSearchBarProps(view: ISearchBarViewProps): Record<string, unknown> {
261
+ return {
262
+ ...view.passthrough,
263
+ placeholder: view.placeholder,
264
+ autoCapitalize: view.autoCapitalize,
265
+ placement: view.placement,
266
+ hideWhenScrolling: view.hideWhenScrolling,
267
+ allowToolbarIntegration: view.allowToolbarIntegration,
268
+ obscureBackground: view.obscureBackground,
269
+ hideNavigationBar: view.hideNavigationBar,
270
+ cancelButtonText: view.cancelButtonText,
271
+ barTintColor: view.barTintColor,
272
+ tintColor: view.tintColor,
273
+ textColor: view.textColor,
274
+ autoFocus: view.autoFocus,
275
+ disableBackButtonOverride: view.disableBackButtonOverride,
276
+ inputType: view.inputType,
277
+ hintTextColor: view.hintTextColor,
278
+ headerIconColor: view.headerIconColor,
279
+ shouldShowHintSearchIcon: view.shouldShowHintSearchIcon,
280
+ };
281
+ }
282
+
283
+ // The 7 search-bar event keys -> option-callback passthrough: zero lifecycle dependency, so every
284
+ // adapter built the identical map by hand. `log` is an optional per-event hook so an adapter can
285
+ // still attach its own route-scoped dlog line (React/Vue do; Angular doesn't) without forking the
286
+ // map itself.
287
+ export function buildSearchBarPassthrough(
288
+ options: ISearchBarOptions,
289
+ log?: (message: string) => void,
290
+ ): Record<string, (event: ISymbioteEvent) => void> {
291
+ return {
292
+ [SEARCH_BAR_ON_FOCUS]: () => {
293
+ log?.('search bar focused');
294
+ options.onFocus?.();
295
+ },
296
+ [SEARCH_BAR_ON_BLUR]: () => {
297
+ log?.('search bar blurred');
298
+ options.onBlur?.();
299
+ },
300
+ [SEARCH_BAR_ON_CHANGE_TEXT]: (event: ISymbioteEvent) => {
301
+ const { text } = event.nativeEvent;
302
+ const changedText = typeof text === 'string' ? text : '';
303
+ log?.(`search text changed: ${changedText}`);
304
+ options.onChangeText?.(changedText);
305
+ },
306
+ [SEARCH_BAR_ON_SEARCH_BUTTON_PRESS]: (event: ISymbioteEvent) => {
307
+ const { text } = event.nativeEvent;
308
+ const pressedText = typeof text === 'string' ? text : '';
309
+ log?.(`search button pressed: ${pressedText}`);
310
+ options.onSearchButtonPress?.(pressedText);
311
+ },
312
+ [SEARCH_BAR_ON_CANCEL_BUTTON_PRESS]: () => {
313
+ log?.('search bar cancel pressed');
314
+ options.onCancelButtonPress?.();
315
+ },
316
+ [SEARCH_BAR_ON_CLOSE]: () => {
317
+ log?.('search bar closed');
318
+ options.onClose?.();
319
+ },
320
+ [SEARCH_BAR_ON_OPEN]: () => {
321
+ log?.('search bar opened');
322
+ options.onOpen?.();
323
+ },
324
+ };
325
+ }
326
+
327
+ // A screen's resolved render plan: the framework-agnostic half of one RNSScreen entry, threading
328
+ // a route's merged options through the same ~14-call resolver sequence every adapter's render loop
329
+ // used to repeat by hand (see the architecture review this facade answers). Lifecycle-bound event
330
+ // wiring (SCREEN_ON_* -> dispatch/emit, the search bar's imperative ref) stays adapter-owned and
331
+ // arrives here already built, via `screenPassthrough`/`searchBarPassthrough`; likewise the actual
332
+ // element/component creation (createElement/h/the Angular template) stays adapter-owned and reads
333
+ // off this plan's fields instead of re-deriving them.
334
+ export type IScreenRenderPlanInput = {
335
+ screenId: string;
336
+ index: number;
337
+ routeCount: number;
338
+ options: IScreenOptions | undefined;
339
+ platform: INavigatorPlatform;
340
+ isAndroid: boolean;
341
+ screenPassthrough: Record<string, unknown>;
342
+ // Already includes the imperative ref key where an adapter wires one (React/Vue); Angular wires
343
+ // its ref via a directive instead, so its map never carries one. Omitted entirely when the
344
+ // screen has no search bar.
345
+ searchBarPassthrough?: Record<string, unknown>;
346
+ };
347
+
348
+ export type IScreenRenderPlan = {
349
+ activityState: number;
350
+ // The outer RNSScreen/RNSModalScreen Fabric component name (resolveScreenViewName).
351
+ screenViewName: string;
352
+ screenProps: Record<string, unknown>;
353
+ // Full Descriptor (react-native-screens' own react-native-renderer/src/ReactFiberConfigFabric.js
354
+ // style props + a nested RNSSearchBar child when the screen has one) for the adapters that
355
+ // bridge it verbatim via descriptorToReact/descriptorToVue; Angular reads only `.props` and
356
+ // renders the search bar itself through its own template (see isHeaderInModal's rationale below
357
+ // for why Angular's element assembly differs here).
358
+ headerConfig: IDescriptor;
359
+ // The RNSSearchBar leaf's own props, standalone from `headerConfig` for the adapters (Angular)
360
+ // that mount it as a separate host element rather than through `headerConfig`'s children.
361
+ // undefined when the screen has no search bar.
362
+ searchBarProps: Record<string, unknown> | undefined;
363
+ // Always includes `collapsable: false` (see RNS_SCREEN_CONTENT_WRAPPER_VIEW_NAME's rationale in
364
+ // core/constants.ts) - every adapter passed this exact pair together, never the style alone.
365
+ contentWrapperProps: Record<string, unknown>;
366
+ inModal: boolean;
367
+ innerStackStyle: Record<string, unknown>;
368
+ innerScreenStyle: Record<string, unknown>;
369
+ };
370
+
371
+ export function resolveScreenRenderPlan(input: IScreenRenderPlanInput): IScreenRenderPlan {
372
+ const { options, isAndroid } = input;
373
+ const headerHidden = options?.headerShown === false;
374
+ const activityState = computeActivityState(input.index, input.routeCount);
375
+
376
+ const screenProps = resolveScreenProps(
377
+ resolveScreenView(input.screenId, activityState, options, input.screenPassthrough),
378
+ );
379
+
380
+ const searchBarOptions = options?.headerSearchBarOptions;
381
+ const searchBarProps = searchBarOptions
382
+ ? resolveSearchBarProps(
383
+ resolveSearchBarView(searchBarOptions, input.searchBarPassthrough ?? {}),
384
+ )
385
+ : undefined;
386
+
387
+ // See renderHeaderConfig's own header comment for why `props` never reflects `searchBarProps` --
388
+ // only `children` does -- so passing it here unconditionally is safe even for Angular, which
389
+ // never reads `children` off the result.
390
+ const headerConfig = renderHeaderConfig(
391
+ resolveHeaderConfigView(options, input.platform),
392
+ searchBarProps,
393
+ );
394
+
395
+ return {
396
+ activityState,
397
+ screenViewName: resolveScreenViewName(options?.stackPresentation, isAndroid),
398
+ screenProps,
399
+ headerConfig,
400
+ searchBarProps,
401
+ contentWrapperProps: {
402
+ style: resolveScreenContentWrapperStyle(
403
+ options?.stackPresentation,
404
+ headerHidden,
405
+ options?.headerTranslucent,
406
+ isAndroid,
407
+ ),
408
+ collapsable: false,
409
+ },
410
+ inModal: isHeaderInModal(options?.stackPresentation, headerHidden, isAndroid),
411
+ innerStackStyle: resolveHeaderInModalStackStyle(),
412
+ innerScreenStyle: resolveHeaderInModalScreenStyle(),
413
+ };
414
+ }
@@ -0,0 +1,138 @@
1
+ // TabBar: the render half (framework-agnostic). A bottom-tabs bar is a PURE-JS UI - unlike
2
+ // Stack, which drives real native RNSScreen/RNSScreenStack views, this paints ordinary
3
+ // `symbiote-view`/`symbiote-text` primitives, so it needs no react-native-screens ViewConfig
4
+ // work at all (see the task brief in packages/navigation's skill notes). Mirrors renderSwitch's
5
+ // shape: pure prop-driven functions building a `Descriptor` tree from pre-resolved view props;
6
+ // the adapter supplies focus state, tint-color resolution inputs, and the press wiring via
7
+ // `passthrough` (exactly like Stack wires onDismissed/onHeaderBackButtonClicked into
8
+ // resolveScreenView's passthrough) - this layer never invents an onPress/responder pair itself.
9
+
10
+ import { el, txt } from '@symbiote-native/components';
11
+ import type { IDescriptor } from '@symbiote-native/components';
12
+ import type { IColorValue, IViewStyle, ITextStyle, IStyleProp } from '@symbiote-native/engine';
13
+ import type { ITabBarIcon } from './tab-options';
14
+
15
+ // react-navigation's own bottom-tabs default to the surrounding theme's primary/text colors;
16
+ // symbiote's navigation package has no app-level theme yet, so these are placeholder defaults -
17
+ // the same iOS-system-blue/neutral-grey pair render-button.ts already uses for its default
18
+ // tint/disabled colors, reused here rather than inventing a third pair (no shared semantic color
19
+ // tokens exist yet in @symbiote-native/engine as of this writing).
20
+ const DEFAULT_ACTIVE_TINT_COLOR = '#007AFF';
21
+ const DEFAULT_INACTIVE_TINT_COLOR = '#8e8e93';
22
+ const BADGE_BACKGROUND_COLOR = '#FF3B30';
23
+ const BADGE_TEXT_COLOR = '#ffffff';
24
+
25
+ const TAB_BAR_HEIGHT = 49;
26
+ const TAB_ICON_FONT_SIZE = 20;
27
+ const TAB_LABEL_FONT_SIZE = 10;
28
+ const BADGE_SIZE = 16;
29
+ const BADGE_BORDER_RADIUS = BADGE_SIZE / 2;
30
+ const BADGE_OFFSET_TOP = -4;
31
+ const BADGE_OFFSET_RIGHT = -8;
32
+
33
+ // One tab item's pre-resolved paint inputs. `label`/`icon`/`badge`/tint colors are already
34
+ // folded from the route's ITabOptions by the adapter (same split as IScreenViewProps); `focused`
35
+ // decides which tint applies. `passthrough` carries the tap wiring (a plain `onPress`, which the
36
+ // engine synthesizes from a touchStart/touchEnd pair on the node - core/engine/src/events/index.ts
37
+ // - no responder-negotiation props needed) plus accessibility/testID.
38
+ export type ITabBarItemView = {
39
+ key: string;
40
+ focused: boolean;
41
+ label: string;
42
+ icon?: ITabBarIcon;
43
+ badge?: string | number;
44
+ activeTintColor?: IColorValue;
45
+ inactiveTintColor?: IColorValue;
46
+ passthrough: Record<string, unknown>;
47
+ };
48
+
49
+ export type ITabBarViewProps = {
50
+ items: readonly ITabBarItemView[];
51
+ style?: IStyleProp<IViewStyle>;
52
+ passthrough: Record<string, unknown>;
53
+ };
54
+
55
+ const TAB_BAR_STYLE: IViewStyle = {
56
+ flexDirection: 'row',
57
+ height: TAB_BAR_HEIGHT,
58
+ };
59
+
60
+ const TAB_ITEM_STYLE: IViewStyle = {
61
+ flex: 1,
62
+ alignItems: 'center',
63
+ justifyContent: 'center',
64
+ };
65
+
66
+ const ICON_WRAPPER_STYLE: IViewStyle = {
67
+ alignItems: 'center',
68
+ justifyContent: 'center',
69
+ };
70
+
71
+ const BADGE_STYLE: IViewStyle = {
72
+ position: 'absolute',
73
+ top: BADGE_OFFSET_TOP,
74
+ right: BADGE_OFFSET_RIGHT,
75
+ minWidth: BADGE_SIZE,
76
+ height: BADGE_SIZE,
77
+ borderRadius: BADGE_BORDER_RADIUS,
78
+ backgroundColor: BADGE_BACKGROUND_COLOR,
79
+ alignItems: 'center',
80
+ justifyContent: 'center',
81
+ paddingHorizontal: 3,
82
+ };
83
+
84
+ const BADGE_TEXT_STYLE: ITextStyle = {
85
+ color: BADGE_TEXT_COLOR,
86
+ fontSize: 8,
87
+ fontWeight: '600',
88
+ };
89
+
90
+ function resolveTintColor(item: ITabBarItemView): IColorValue {
91
+ if (item.focused) return item.activeTintColor ?? DEFAULT_ACTIVE_TINT_COLOR;
92
+ return item.inactiveTintColor ?? DEFAULT_INACTIVE_TINT_COLOR;
93
+ }
94
+
95
+ // A string icon paints as a glyph (an emoji/ligature font name - same convention a caller uses
96
+ // for a vector-icon-font label); an IDescriptor icon (an <Image>/vector-icon element the adapter
97
+ // already resolved) is spliced in verbatim, exactly like renderHeaderConfig treats a pre-built
98
+ // Descriptor child.
99
+ function renderIcon(icon: ITabBarIcon | undefined, color: IColorValue): IDescriptor | undefined {
100
+ if (icon === undefined) return undefined;
101
+ if (typeof icon === 'string')
102
+ return txt({ style: { color, fontSize: TAB_ICON_FONT_SIZE } }, [icon]);
103
+ return icon;
104
+ }
105
+
106
+ function renderBadge(badge: string | number | undefined): IDescriptor | undefined {
107
+ if (badge === undefined) return undefined;
108
+ return el('symbiote-view', { style: BADGE_STYLE }, [
109
+ txt({ style: BADGE_TEXT_STYLE }, [String(badge)]),
110
+ ]);
111
+ }
112
+
113
+ export function renderTabBarItem(item: ITabBarItemView): IDescriptor {
114
+ const color = resolveTintColor(item);
115
+ const icon = renderIcon(item.icon, color);
116
+ const badge = renderBadge(item.badge);
117
+
118
+ const iconChildren: IDescriptor[] = [];
119
+ if (icon) iconChildren.push(icon);
120
+ if (badge) iconChildren.push(badge);
121
+
122
+ const children: IDescriptor[] = [];
123
+ if (iconChildren.length > 0)
124
+ children.push(el('symbiote-view', { style: ICON_WRAPPER_STYLE }, iconChildren));
125
+ children.push(txt({ style: { color, fontSize: TAB_LABEL_FONT_SIZE } }, [item.label]));
126
+
127
+ return el(
128
+ 'symbiote-view',
129
+ { ...item.passthrough, style: TAB_ITEM_STYLE, accessibilityRole: 'tab' },
130
+ children,
131
+ item.key,
132
+ );
133
+ }
134
+
135
+ export function renderTabBar(view: ITabBarViewProps): IDescriptor {
136
+ const items = view.items.map(renderTabBarItem);
137
+ return el('symbiote-view', { ...view.passthrough, style: [TAB_BAR_STYLE, view.style] }, items);
138
+ }