@symbiote-native/navigation 2.0.3 → 2.2.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 (320) hide show
  1. package/README.md +42 -12
  2. package/build/angular/drawer/index.js +29 -13
  3. package/build/angular/index.d.ts +4 -4
  4. package/build/angular/injectors/inject-navigation.js +1 -1
  5. package/build/angular/navigation-scope.directive.js +1 -1
  6. package/build/angular/search-bar-ref.directive.js +1 -1
  7. package/build/angular/stack/index.d.ts +2 -0
  8. package/build/angular/stack/index.js +43 -9
  9. package/build/angular/tabs/index.d.ts +1 -1
  10. package/build/angular/tabs/index.js +40 -13
  11. package/build/core/drawer-options/index.js +19 -5
  12. package/build/core/drawer-router-state/index.js +8 -2
  13. package/build/core/guards.js +3 -1
  14. package/build/core/header-bar-buttons.js +14 -4
  15. package/build/core/index.d.ts +6 -5
  16. package/build/core/index.js +7 -3
  17. package/build/core/navigator-state/index.js +12 -4
  18. package/build/core/render-drawer.js +4 -1
  19. package/build/core/render-stack.js +13 -4
  20. package/build/core/screen-options.js +1 -1
  21. package/build/core/search-bar-commands.js +1 -1
  22. package/build/core/stack-router-state/index.d.ts +2 -0
  23. package/build/core/stack-router-state/index.js +44 -0
  24. package/build/core/state-persistence/index.js +6 -2
  25. package/build/core/tab-router-state/index.d.ts +1 -0
  26. package/build/core/tab-router-state/index.js +27 -2
  27. package/build/react/drawer/index.js +59 -26
  28. package/build/react/index.d.ts +7 -7
  29. package/build/react/index.js +2 -2
  30. package/build/react/stack/index.js +47 -10
  31. package/build/react/tabs/index.js +36 -12
  32. package/build/solid/drawer/index.d.ts +22 -0
  33. package/build/solid/drawer/index.js +394 -0
  34. package/build/solid/drawer-screen-props.d.ts +13 -0
  35. package/build/solid/drawer-screen-props.js +2 -0
  36. package/build/solid/host.d.ts +2 -0
  37. package/build/solid/host.js +23 -0
  38. package/build/solid/index.d.ts +19 -0
  39. package/build/solid/index.js +24 -0
  40. package/build/solid/linking.d.ts +3 -0
  41. package/build/solid/linking.js +55 -0
  42. package/build/solid/navigation-context.d.ts +13 -0
  43. package/build/solid/navigation-context.js +37 -0
  44. package/build/solid/primitives/create-focus-effect.d.ts +1 -0
  45. package/build/solid/primitives/create-focus-effect.js +30 -0
  46. package/build/solid/primitives/create-is-focused.d.ts +2 -0
  47. package/build/solid/primitives/create-is-focused.js +31 -0
  48. package/build/solid/primitives/create-navigation-state.d.ts +3 -0
  49. package/build/solid/primitives/create-navigation-state.js +30 -0
  50. package/build/solid/primitives/index.d.ts +12 -0
  51. package/build/solid/primitives/index.js +8 -0
  52. package/build/solid/primitives/use-drawer-navigation.d.ts +5 -0
  53. package/build/solid/primitives/use-drawer-navigation.js +15 -0
  54. package/build/solid/primitives/use-navigation.d.ts +8 -0
  55. package/build/solid/primitives/use-navigation.js +23 -0
  56. package/build/solid/primitives/use-route.d.ts +3 -0
  57. package/build/solid/primitives/use-route.js +14 -0
  58. package/build/solid/primitives/use-stack-navigation.d.ts +5 -0
  59. package/build/solid/primitives/use-stack-navigation.js +19 -0
  60. package/build/solid/primitives/use-tab-navigation.d.ts +5 -0
  61. package/build/solid/primitives/use-tab-navigation.js +15 -0
  62. package/build/solid/screen-props.d.ts +19 -0
  63. package/build/solid/screen-props.js +9 -0
  64. package/build/solid/screen-registry.d.ts +31 -0
  65. package/build/solid/screen-registry.js +86 -0
  66. package/build/solid/screen.d.ts +6 -0
  67. package/build/solid/screen.js +69 -0
  68. package/build/solid/stack/index.d.ts +15 -0
  69. package/build/solid/stack/index.js +348 -0
  70. package/build/solid/tab-screen-props.d.ts +13 -0
  71. package/build/solid/tab-screen-props.js +3 -0
  72. package/build/solid/tabs/index.d.ts +14 -0
  73. package/build/solid/tabs/index.js +228 -0
  74. package/build/svelte/attachments.d.ts +5 -0
  75. package/build/svelte/attachments.js +61 -0
  76. package/build/svelte/descriptor-subtree.d.ts +3 -0
  77. package/build/svelte/descriptor-subtree.js +39 -0
  78. package/build/svelte/drawer/drawer-props.d.ts +14 -0
  79. package/build/svelte/drawer/drawer-props.js +3 -0
  80. package/build/svelte/drawer/index.d.ts +10 -0
  81. package/build/svelte/drawer/index.js +6 -0
  82. package/build/svelte/drawer/index.svelte +559 -0
  83. package/build/svelte/drawer/index.svelte.d.ts +10 -0
  84. package/build/svelte/drawer-screen-props.d.ts +13 -0
  85. package/build/svelte/drawer-screen-props.js +4 -0
  86. package/build/svelte/drawer-screen.svelte +25 -0
  87. package/build/svelte/drawer-screen.svelte.d.ts +4 -0
  88. package/build/svelte/index.d.ts +21 -0
  89. package/build/svelte/index.js +20 -0
  90. package/build/svelte/linking.svelte.d.ts +2 -0
  91. package/build/svelte/linking.svelte.js +55 -0
  92. package/build/svelte/navigation-context.d.ts +14 -0
  93. package/build/svelte/navigation-context.js +44 -0
  94. package/build/svelte/navigation-scope.svelte +27 -0
  95. package/build/svelte/navigation-scope.svelte.d.ts +9 -0
  96. package/build/svelte/registry-host.d.ts +1 -0
  97. package/build/svelte/registry-host.js +26 -0
  98. package/build/svelte/runes/index.d.ts +12 -0
  99. package/build/svelte/runes/index.js +12 -0
  100. package/build/svelte/runes/use-drawer-navigation.svelte.d.ts +6 -0
  101. package/build/svelte/runes/use-drawer-navigation.svelte.js +19 -0
  102. package/build/svelte/runes/use-focus-effect.svelte.d.ts +1 -0
  103. package/build/svelte/runes/use-focus-effect.svelte.js +31 -0
  104. package/build/svelte/runes/use-is-focused.svelte.d.ts +3 -0
  105. package/build/svelte/runes/use-is-focused.svelte.js +34 -0
  106. package/build/svelte/runes/use-navigation-state.svelte.d.ts +4 -0
  107. package/build/svelte/runes/use-navigation-state.svelte.js +39 -0
  108. package/build/svelte/runes/use-navigation.svelte.d.ts +9 -0
  109. package/build/svelte/runes/use-navigation.svelte.js +26 -0
  110. package/build/svelte/runes/use-route.svelte.d.ts +4 -0
  111. package/build/svelte/runes/use-route.svelte.js +12 -0
  112. package/build/svelte/runes/use-stack-navigation.svelte.d.ts +6 -0
  113. package/build/svelte/runes/use-stack-navigation.svelte.js +22 -0
  114. package/build/svelte/runes/use-tab-navigation.svelte.d.ts +6 -0
  115. package/build/svelte/runes/use-tab-navigation.svelte.js +19 -0
  116. package/build/svelte/screen-props.d.ts +21 -0
  117. package/build/svelte/screen-props.js +6 -0
  118. package/build/svelte/screen-registry.d.ts +17 -0
  119. package/build/svelte/screen-registry.js +57 -0
  120. package/build/svelte/screen.svelte +28 -0
  121. package/build/svelte/screen.svelte.d.ts +4 -0
  122. package/build/svelte/stack/index.d.ts +14 -0
  123. package/build/svelte/stack/index.js +7 -0
  124. package/build/svelte/stack/index.svelte +261 -0
  125. package/build/svelte/stack/index.svelte.d.ts +15 -0
  126. package/build/svelte/stack/stack-props.d.ts +20 -0
  127. package/build/svelte/stack/stack-props.js +4 -0
  128. package/build/svelte/stack/stack-screen.svelte +205 -0
  129. package/build/svelte/stack/stack-screen.svelte.d.ts +4 -0
  130. package/build/svelte/tab-screen-props.d.ts +13 -0
  131. package/build/svelte/tab-screen-props.js +4 -0
  132. package/build/svelte/tab-screen.svelte +26 -0
  133. package/build/svelte/tab-screen.svelte.d.ts +4 -0
  134. package/build/svelte/tabs/index.d.ts +8 -0
  135. package/build/svelte/tabs/index.js +6 -0
  136. package/build/svelte/tabs/index.svelte +269 -0
  137. package/build/svelte/tabs/index.svelte.d.ts +8 -0
  138. package/build/svelte/tabs/tab-props.d.ts +7 -0
  139. package/build/svelte/tabs/tab-props.js +3 -0
  140. package/build/vue/composables/use-navigation.js +5 -1
  141. package/build/vue/drawer/index.js +36 -13
  142. package/build/vue/index.d.ts +3 -3
  143. package/build/vue/navigation-context.js +1 -1
  144. package/build/vue/stack/index.js +43 -18
  145. package/build/vue/tabs/index.js +65 -39
  146. package/build-ngc/angular/drawer/index.js +40 -18
  147. package/build-ngc/angular/drawer/index.js.map +1 -1
  148. package/build-ngc/angular/drawer-screen.directive.js +3 -3
  149. package/build-ngc/angular/index.d.ts +4 -4
  150. package/build-ngc/angular/index.js.map +1 -1
  151. package/build-ngc/angular/injectors/inject-focus-effect.js.map +1 -1
  152. package/build-ngc/angular/injectors/inject-is-focused.js.map +1 -1
  153. package/build-ngc/angular/injectors/inject-navigation-state.js.map +1 -1
  154. package/build-ngc/angular/injectors/inject-navigation.js +1 -1
  155. package/build-ngc/angular/injectors/inject-navigation.js.map +1 -1
  156. package/build-ngc/angular/linking.js.map +1 -1
  157. package/build-ngc/angular/navigation-context.service.js +3 -3
  158. package/build-ngc/angular/navigation-context.service.js.map +1 -1
  159. package/build-ngc/angular/navigation-scope.directive.js +4 -4
  160. package/build-ngc/angular/navigation-scope.directive.js.map +1 -1
  161. package/build-ngc/angular/screen.directive.js +3 -3
  162. package/build-ngc/angular/screen.directive.js.map +1 -1
  163. package/build-ngc/angular/search-bar-ref.directive.js +4 -4
  164. package/build-ngc/angular/search-bar-ref.directive.js.map +1 -1
  165. package/build-ngc/angular/stack/index.d.ts +2 -0
  166. package/build-ngc/angular/stack/index.js +52 -14
  167. package/build-ngc/angular/stack/index.js.map +1 -1
  168. package/build-ngc/angular/tab-screen.directive.js +3 -3
  169. package/build-ngc/angular/tab-screen.directive.js.map +1 -1
  170. package/build-ngc/angular/tabs/index.d.ts +1 -1
  171. package/build-ngc/angular/tabs/index.js +43 -16
  172. package/build-ngc/angular/tabs/index.js.map +1 -1
  173. package/build-ngc/core/constants.js.map +1 -1
  174. package/build-ngc/core/drawer-options/index.js +19 -5
  175. package/build-ngc/core/drawer-options/index.js.map +1 -1
  176. package/build-ngc/core/drawer-router-state/index.js +8 -2
  177. package/build-ngc/core/drawer-router-state/index.js.map +1 -1
  178. package/build-ngc/core/guards.js +3 -1
  179. package/build-ngc/core/guards.js.map +1 -1
  180. package/build-ngc/core/header-bar-buttons.js +14 -4
  181. package/build-ngc/core/header-bar-buttons.js.map +1 -1
  182. package/build-ngc/core/index.d.ts +6 -5
  183. package/build-ngc/core/index.js +7 -3
  184. package/build-ngc/core/index.js.map +1 -1
  185. package/build-ngc/core/linking-config/index.js.map +1 -1
  186. package/build-ngc/core/navigation-events/index.js.map +1 -1
  187. package/build-ngc/core/navigator-handles/index.js.map +1 -1
  188. package/build-ngc/core/navigator-state/index.js +12 -4
  189. package/build-ngc/core/navigator-state/index.js.map +1 -1
  190. package/build-ngc/core/render-drawer.js +4 -1
  191. package/build-ngc/core/render-drawer.js.map +1 -1
  192. package/build-ngc/core/render-stack.js +13 -4
  193. package/build-ngc/core/render-stack.js.map +1 -1
  194. package/build-ngc/core/render-tabs.js.map +1 -1
  195. package/build-ngc/core/screen-options.js +1 -1
  196. package/build-ngc/core/screen-options.js.map +1 -1
  197. package/build-ngc/core/search-bar-commands.js +1 -1
  198. package/build-ngc/core/search-bar-commands.js.map +1 -1
  199. package/build-ngc/core/stack-router-state/index.d.ts +2 -0
  200. package/build-ngc/core/stack-router-state/index.js +45 -0
  201. package/build-ngc/core/stack-router-state/index.js.map +1 -0
  202. package/build-ngc/core/state-persistence/index.js +6 -2
  203. package/build-ngc/core/state-persistence/index.js.map +1 -1
  204. package/build-ngc/core/tab-router-state/index.d.ts +1 -0
  205. package/build-ngc/core/tab-router-state/index.js +27 -2
  206. package/build-ngc/core/tab-router-state/index.js.map +1 -1
  207. package/package.json +44 -16
  208. package/src/angular/drawer/index.ts +75 -29
  209. package/src/angular/index.ts +20 -4
  210. package/src/angular/injectors/inject-focus-effect.ts +8 -2
  211. package/src/angular/injectors/inject-is-focused.ts +6 -4
  212. package/src/angular/injectors/inject-navigation-state.ts +7 -4
  213. package/src/angular/injectors/inject-navigation.ts +12 -3
  214. package/src/angular/linking.ts +4 -1
  215. package/src/angular/navigation-context.service.ts +5 -2
  216. package/src/angular/navigation-scope.directive.ts +6 -2
  217. package/src/angular/screen.directive.ts +13 -3
  218. package/src/angular/search-bar-ref.directive.ts +10 -2
  219. package/src/angular/stack/index.ts +91 -24
  220. package/src/angular/tab-screen.directive.ts +3 -1
  221. package/src/angular/tabs/index.ts +54 -16
  222. package/src/core/constants.ts +6 -3
  223. package/src/core/drawer-options/index.ts +40 -13
  224. package/src/core/drawer-router-state/index.ts +11 -3
  225. package/src/core/guards.ts +3 -1
  226. package/src/core/header-bar-buttons.ts +41 -12
  227. package/src/core/index.ts +29 -5
  228. package/src/core/linking-config/index.ts +31 -10
  229. package/src/core/navigation-events/index.ts +8 -2
  230. package/src/core/navigator-handles/index.ts +8 -3
  231. package/src/core/navigator-props.ts +13 -4
  232. package/src/core/navigator-state/index.ts +24 -7
  233. package/src/core/render-drawer.ts +24 -9
  234. package/src/core/render-stack.ts +52 -13
  235. package/src/core/render-tabs.ts +24 -6
  236. package/src/core/screen-options.ts +15 -5
  237. package/src/core/search-bar-commands.ts +12 -3
  238. package/src/core/stack-router-state/index.ts +52 -0
  239. package/src/core/state-persistence/index.ts +13 -3
  240. package/src/core/tab-router-state/index.ts +35 -3
  241. package/src/react/drawer/index.ts +409 -330
  242. package/src/react/drawer-screen.ts +8 -2
  243. package/src/react/hooks/use-focus-effect.ts +8 -2
  244. package/src/react/hooks/use-is-focused.ts +6 -2
  245. package/src/react/hooks/use-navigation.ts +8 -2
  246. package/src/react/index.ts +37 -7
  247. package/src/react/linking.ts +4 -1
  248. package/src/react/navigation-context.ts +6 -2
  249. package/src/react/screen.ts +12 -3
  250. package/src/react/stack/index.ts +297 -220
  251. package/src/react/tab-screen.ts +3 -1
  252. package/src/react/tabs/index.ts +202 -139
  253. package/src/solid/drawer/index.ts +570 -0
  254. package/src/solid/drawer-screen-props.ts +24 -0
  255. package/src/solid/host.ts +29 -0
  256. package/src/solid/index.ts +87 -0
  257. package/src/solid/linking.ts +74 -0
  258. package/src/solid/navigation-context.ts +56 -0
  259. package/src/solid/primitives/create-focus-effect.ts +41 -0
  260. package/src/solid/primitives/create-is-focused.ts +41 -0
  261. package/src/solid/primitives/create-navigation-state.ts +44 -0
  262. package/src/solid/primitives/index.ts +12 -0
  263. package/src/solid/primitives/use-drawer-navigation.ts +25 -0
  264. package/src/solid/primitives/use-navigation.ts +44 -0
  265. package/src/solid/primitives/use-route.ts +18 -0
  266. package/src/solid/primitives/use-stack-navigation.ts +29 -0
  267. package/src/solid/primitives/use-tab-navigation.ts +25 -0
  268. package/src/solid/screen-props.ts +51 -0
  269. package/src/solid/screen-registry.ts +159 -0
  270. package/src/solid/screen.ts +80 -0
  271. package/src/solid/stack/index.ts +528 -0
  272. package/src/solid/tab-screen-props.ts +21 -0
  273. package/src/solid/tabs/index.ts +327 -0
  274. package/src/svelte/attachments.ts +70 -0
  275. package/src/svelte/descriptor-subtree.ts +51 -0
  276. package/src/svelte/drawer/drawer-props.ts +28 -0
  277. package/src/svelte/drawer/index.svelte +559 -0
  278. package/src/svelte/drawer/index.ts +10 -0
  279. package/src/svelte/drawer-screen-props.ts +26 -0
  280. package/src/svelte/drawer-screen.svelte +25 -0
  281. package/src/svelte/fabric-tree.test-helper.ts +95 -0
  282. package/src/svelte/index.ts +83 -0
  283. package/src/svelte/linking.svelte.ts +72 -0
  284. package/src/svelte/navigation-context.ts +62 -0
  285. package/src/svelte/navigation-scope.svelte +27 -0
  286. package/src/svelte/registry-host.ts +26 -0
  287. package/src/svelte/runes/index.ts +17 -0
  288. package/src/svelte/runes/use-drawer-navigation.svelte.ts +31 -0
  289. package/src/svelte/runes/use-focus-effect.svelte.ts +41 -0
  290. package/src/svelte/runes/use-is-focused.svelte.ts +38 -0
  291. package/src/svelte/runes/use-navigation-state.svelte.ts +48 -0
  292. package/src/svelte/runes/use-navigation.svelte.ts +49 -0
  293. package/src/svelte/runes/use-route.svelte.ts +16 -0
  294. package/src/svelte/runes/use-stack-navigation.svelte.ts +34 -0
  295. package/src/svelte/runes/use-tab-navigation.svelte.ts +29 -0
  296. package/src/svelte/screen-props.ts +50 -0
  297. package/src/svelte/screen-registry.ts +98 -0
  298. package/src/svelte/screen.svelte +28 -0
  299. package/src/svelte/stack/index.svelte +261 -0
  300. package/src/svelte/stack/index.ts +11 -0
  301. package/src/svelte/stack/stack-props.ts +31 -0
  302. package/src/svelte/stack/stack-screen.svelte +205 -0
  303. package/src/svelte/svelte-ambient.d.ts +6 -0
  304. package/src/svelte/svelte-compile.test-helper.ts +147 -0
  305. package/src/svelte/tab-screen-props.ts +22 -0
  306. package/src/svelte/tab-screen.svelte +26 -0
  307. package/src/svelte/tabs/index.svelte +269 -0
  308. package/src/svelte/tabs/index.ts +10 -0
  309. package/src/svelte/tabs/tab-props.ts +13 -0
  310. package/src/vue/composables/use-navigation-state.ts +7 -4
  311. package/src/vue/composables/use-navigation.ts +13 -3
  312. package/src/vue/drawer/index.ts +128 -41
  313. package/src/vue/drawer-screen.ts +12 -5
  314. package/src/vue/index.ts +15 -3
  315. package/src/vue/linking.ts +7 -2
  316. package/src/vue/navigation-context.ts +17 -5
  317. package/src/vue/screen.ts +7 -2
  318. package/src/vue/stack/index.ts +108 -43
  319. package/src/vue/tab-screen.ts +3 -1
  320. package/src/vue/tabs/index.ts +109 -68
@@ -0,0 +1,23 @@
1
+ // Building a RAW Fabric host element (RNSScreenStack, RNSScreen, RNSScreenContentWrapper) from
2
+ // Solid, without JSX.
3
+ //
4
+ // The whole `src/solid/**` tree is plain `.ts`, matching what every other adapter's navigation
5
+ // entry does (React calls createElement, Vue calls h, Svelte writes .svelte, Angular writes
6
+ // templates) - and here it is also what keeps ONE tsconfig per package honest: this package's `jsx`
7
+ // setting belongs to its React entry, and a `.tsx` file compiled under it would be type-checked
8
+ // against React's JSX namespace, not Solid's (.claude/rules/solid-jsx-namespace.md).
9
+ //
10
+ // It routes through descriptorToSolid rather than calling createElement/spread directly ON PURPOSE:
11
+ // that bridge already wraps the prop accessor in `withStableKeys`, which is the fix for Solid's
12
+ // `spread` having no removal pass - a key that VANISHES between runs would otherwise keep its last
13
+ // value on the native view forever (.claude/rules/solid-descriptor-bridge.md §1). A screen's
14
+ // resolved prop bag genuinely changes key set between options folds, so this is live, not
15
+ // theoretical. Re-implementing the widening here would be a second copy of a helper the adapter
16
+ // keeps private for exactly that reason.
17
+ //
18
+ // `children: []` because every caller mounts real Solid children with `insert` afterwards, not
19
+ // Descriptor children. The empty list keeps the shape guard's child count stable across recomputes.
20
+ import { descriptorToSolid } from '@symbiote-native/solid';
21
+ export function hostElement(type, props) {
22
+ return descriptorToSolid(() => ({ type, props: props(), children: [] }));
23
+ }
@@ -0,0 +1,19 @@
1
+ import '../register';
2
+ export { Stack } from './stack';
3
+ export type { INavigatorHandle, IStackProps } from './stack';
4
+ export { Screen, TabScreen, DrawerScreen } from './screen';
5
+ export type { IScreenOptionsArgs, IScreenOptionsResolver, IScreenProps, ISolidScreenOptions, ISolidSearchBarOptions, } from './screen-props';
6
+ export type { IScreenOptions, IStackAnimation, IStackPresentation, } from '../core';
7
+ export { useNavigation, useStackNavigation, useTabNavigation, useDrawerNavigation, useRoute, createIsFocused, createFocusEffect, createNavigationState, } from './primitives';
8
+ export type { INavigationHandle, IStackNavigationHandle, ITabNavigationHandle, IDrawerNavigationHandle, } from './primitives';
9
+ export type { INavigationScopeValue, IAnyNavigatorHandle, } from './navigation-context';
10
+ export { createLinkingIntegration } from './linking';
11
+ export type { ILinkingConfig, IScreenLinkingConfig } from '../core';
12
+ export { Tab } from './tabs';
13
+ export type { ITabNavigatorHandle, ITabProps } from './tabs';
14
+ export type { ITabScreenOptionsArgs, ITabScreenOptionsResolver, ITabScreenProps, } from './tab-screen-props';
15
+ export type { ITabOptions, ITabBarIcon } from '../core';
16
+ export { Drawer } from './drawer';
17
+ export type { IDrawerContentSlotProps, IDrawerNavigatorHandle, IDrawerProps, IDrawerDescriptorMap, } from './drawer';
18
+ export type { IDrawerScreenOptionsArgs, IDrawerScreenOptionsResolver, IDrawerScreenProps, } from './drawer-screen-props';
19
+ export type { IDrawerOptions, IDrawerScreenOptions, IDrawerType, IDrawerPosition, } from '../core';
@@ -0,0 +1,24 @@
1
+ // @symbiote-native/navigation/solid: the Solid native-stack navigator over react-native-screens'
2
+ // native view primitives. Importing this barrel first registers the native views' ViewConfigs
3
+ // (../register, a side-effect import of the codegen specs - never react-native-screens' own React
4
+ // components), then exposes Stack (with Stack.Screen attached) and the navigator handle. Mirrors
5
+ // vue/index.ts's and svelte/index.ts's barrel shape.
6
+ //
7
+ // The lifecycle helpers split by what they DO, not by a blanket prefix: `use*` for the ones that
8
+ // only consume the navigation scope already on the owner chain (useNavigation / useRoute and the
9
+ // three narrowing variants - Solid's own `useContext` sense), `create*` for the ones that own a
10
+ // signal, an effect or a subscription (createIsFocused / createFocusEffect / createNavigationState
11
+ // / createLinkingIntegration). Same rule the adapter's `createColorScheme` follows; a blanket `use`
12
+ // would read as a foreign ecosystem's convention (<adapter_src_follows_framework_idioms>).
13
+ import '../register';
14
+ export { Stack } from './stack/index.js';
15
+ export { Screen, TabScreen, DrawerScreen } from './screen.js';
16
+ export { useNavigation, useStackNavigation, useTabNavigation, useDrawerNavigation, useRoute, createIsFocused, createFocusEffect, createNavigationState, } from './primitives/index.js';
17
+ export { createLinkingIntegration } from './linking.js';
18
+ // Tab: the bottom-tabs navigator, a PURE-JS UI (no react-native-screens views involved, so no extra
19
+ // ViewConfig registration is needed beyond the ../register import above).
20
+ export { Tab } from './tabs/index.js';
21
+ // Drawer: the swipeable drawer navigator, a PURE-JS UI (PanResponder + Animated). See
22
+ // drawer/index.ts's header for the feasibility note re: react-native-gesture-handler /
23
+ // react-native-reanimated parity gaps.
24
+ export { Drawer } from './drawer/index.js';
@@ -0,0 +1,3 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import type { ILinkingConfig, INavigatorHandle } from '../core';
3
+ export declare function createLinkingIntegration(config: ILinkingConfig, navigatorHandle: Accessor<INavigatorHandle | null>): void;
@@ -0,0 +1,55 @@
1
+ // createLinkingIntegration: wires the framework-agnostic linking config (../core's
2
+ // resolveRouteFromUrl) onto an existing Stack's INavigatorHandle. Deliberately NOT called from
3
+ // inside ./stack - app code owns the wiring, the same top-level-prop shape @react-navigation keeps
4
+ // `linking` in on NavigationContainer rather than baking it into the router itself.
5
+ //
6
+ // `create*`, not `use*`: it owns a Linking subscription and its teardown. (Vue and Svelte spell it
7
+ // `useLinkingIntegration` because their ecosystems put every lifecycle helper behind `use`; Solid
8
+ // reserves that prefix for consuming something that already exists -
9
+ // <adapter_src_follows_framework_idioms>.)
10
+ //
11
+ // Takes an ACCESSOR of the handle, not the handle: `<Stack ref={setHandle}>` resolves after the
12
+ // navigator has been created, so an eagerly-passed value would be `null` at the call site. Reading
13
+ // it lazily inside onMount gives the same "already populated" guarantee React's layout-effect
14
+ // ordering does - a Solid `ref` on a component is called during that component's own creation,
15
+ // which is strictly before any effect runs.
16
+ import { onCleanup, onMount } from 'solid-js';
17
+ import { Linking, dlog } from '@symbiote-native/engine';
18
+ import { resolveRouteFromUrl } from '../core/index.js';
19
+ export function createLinkingIntegration(config, navigatorHandle) {
20
+ let cancelled = false;
21
+ let subscription;
22
+ function applyRoute(url, dispatch) {
23
+ const route = resolveRouteFromUrl(config, url);
24
+ if (route === null) {
25
+ dlog(`createLinkingIntegration: no route resolved for "${url}"`);
26
+ return;
27
+ }
28
+ dispatch(route.name, route.params);
29
+ }
30
+ onMount(() => {
31
+ const handle = navigatorHandle();
32
+ if (handle === null) {
33
+ dlog('createLinkingIntegration: navigatorHandle is not yet available at mount time');
34
+ return;
35
+ }
36
+ Linking.getInitialURL()
37
+ .then(url => {
38
+ if (cancelled || url === null)
39
+ return;
40
+ dlog(`createLinkingIntegration: initial URL "${url}"`);
41
+ applyRoute(url, handle.replace);
42
+ })
43
+ .catch((error) => {
44
+ dlog(`createLinkingIntegration: getInitialURL failed: ${String(error)}`);
45
+ });
46
+ subscription = Linking.addEventListener('url', (event) => {
47
+ dlog(`createLinkingIntegration: url event "${event.url}"`);
48
+ applyRoute(event.url, handle.push);
49
+ });
50
+ });
51
+ onCleanup(() => {
52
+ cancelled = true;
53
+ subscription?.remove();
54
+ });
55
+ }
@@ -0,0 +1,13 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import type { IAnyNavigatorHandle, INavigationEmitter, IRoute } from '../core';
3
+ export type { IAnyNavigatorHandle };
4
+ export type INavigationScopeValue = {
5
+ route: IRoute<unknown>;
6
+ navigation: IAnyNavigatorHandle;
7
+ emitter: INavigationEmitter;
8
+ parent?: INavigationScopeValue;
9
+ };
10
+ export type INavigationScope = Accessor<INavigationScopeValue>;
11
+ export declare const NavigationScopeProvider: import("solid-js").ContextProviderComponent<INavigationScope | undefined>;
12
+ export declare function useNavigationScope(): INavigationScope | undefined;
13
+ export declare function requireNavigationScope(functionName: string): INavigationScope;
@@ -0,0 +1,37 @@
1
+ // The lifecycle half of the framework-agnostic emitter (../core/navigation-events). Solid's twin of
2
+ // React's Context / Vue's provide-inject / Svelte's setContext is `createContext` + a Provider over
3
+ // one shared key, resolved along the OWNER chain - so a screen's subtree reads its own route,
4
+ // navigator handle and per-route emitter without prop-drilling.
5
+ //
6
+ // THE VALUE IS AN ACCESSOR, never a route object. A Solid component body runs once and a context
7
+ // value is captured once, so handing over `{ route, ... }` directly would freeze every consumer at
8
+ // the route as it looked when the screen mounted - `setParams` produces a NEW route object under
9
+ // the same key and would never reach `useRoute()`. This is the same accessors-not-values rule
10
+ // descriptorToSolid is built on, one layer up (.claude/rules/solid-descriptor-bridge.md).
11
+ //
12
+ // Each navigator mounts ONE Provider per live route (keyed by route.key, exactly like React's
13
+ // per-route `<NavigationContext.Provider>`), so sibling screens never see each other's scope -
14
+ // an inherent property of any hierarchical dependency-injection scheme, not a React artifact.
15
+ //
16
+ // `parent` threads the ambient value a navigator read on ITS OWN mount (undefined at the root) into
17
+ // the value it provides to its own screens, forming a linked list - this is what lets a screen
18
+ // nested inside e.g. a Stack-screen-renders-a-Tab composition reach the enclosing Stack via
19
+ // useNavigation().getParent().
20
+ import { createContext, useContext } from 'solid-js';
21
+ const NavigationScopeContext = createContext();
22
+ export const NavigationScopeProvider = NavigationScopeContext.Provider;
23
+ // undefined simply means "no ancestor scope", which is a legitimate state (the nesting root a
24
+ // navigator itself reads on its own mount) rather than an error - callers that require one go
25
+ // through requireNavigationScope below.
26
+ export function useNavigationScope() {
27
+ return useContext(NavigationScopeContext);
28
+ }
29
+ // The "throw if missing" half every primitive in ./primitives needs - they all read the scope and
30
+ // threw the same shaped error, differing only in which function name the message names.
31
+ export function requireNavigationScope(functionName) {
32
+ const scope = useNavigationScope();
33
+ if (scope === undefined) {
34
+ throw new Error(`${functionName} must be used within a screen rendered by <Stack>, <Tab>, or <Drawer>`);
35
+ }
36
+ return scope;
37
+ }
@@ -0,0 +1 @@
1
+ export declare function createFocusEffect(effect: () => (() => void) | void): void;
@@ -0,0 +1,30 @@
1
+ // Mirrors @react-navigation's useFocusEffect: run `effect` when the screen gains focus, run its
2
+ // returned cleanup when it loses focus (and once more on teardown).
3
+ //
4
+ // `create*`: it owns two subscriptions and the effect's cleanup slot.
5
+ //
6
+ // React's version requires memoizing `effect` (useCallback), since a new identity re-subscribes it
7
+ // like any other useEffect dependency. That does not apply here: a Solid body runs once, so
8
+ // `effect` is read once and closed over directly - there is no dependency array to go stale.
9
+ import { onCleanup } from 'solid-js';
10
+ import { NAVIGATION_EVENT_BLUR, NAVIGATION_EVENT_FOCUS } from '../../core/index.js';
11
+ import { requireNavigationScope } from '../navigation-context.js';
12
+ export function createFocusEffect(effect) {
13
+ const scope = requireNavigationScope('createFocusEffect');
14
+ let cleanup;
15
+ const runEffect = () => {
16
+ cleanup = effect();
17
+ };
18
+ const runCleanup = () => {
19
+ cleanup?.();
20
+ cleanup = undefined;
21
+ };
22
+ const { emitter } = scope();
23
+ const unsubscribeFocus = emitter.addListener(NAVIGATION_EVENT_FOCUS, runEffect);
24
+ const unsubscribeBlur = emitter.addListener(NAVIGATION_EVENT_BLUR, runCleanup);
25
+ onCleanup(() => {
26
+ unsubscribeFocus();
27
+ unsubscribeBlur();
28
+ runCleanup();
29
+ });
30
+ }
@@ -0,0 +1,2 @@
1
+ import type { Accessor } from 'solid-js';
2
+ export declare function createIsFocused(): Accessor<boolean>;
@@ -0,0 +1,31 @@
1
+ // Mirrors @react-navigation's useIsFocused.
2
+ //
3
+ // `create*`, not `use*`: this OWNS a signal and two emitter subscriptions. Solid reserves `use*`
4
+ // for consuming something that already exists, so a primitive that creates its own state and
5
+ // lifecycle is `createX` (<adapter_src_follows_framework_idioms>; the same reason the adapter
6
+ // spells its colour-scheme primitive `createColorScheme`).
7
+ //
8
+ // Starts `false` rather than guessing from stack position: the route's emitter only fires 'focus'
9
+ // once RNSScreen's native onAppear lands (../stack) or the screen mounts focused (../tabs,
10
+ // ../drawer) - a screen genuinely isn't focused at the instant it mounts, the same async gap real
11
+ // native transitions have.
12
+ //
13
+ // Subscribing SYNCHRONOUSLY in the body rather than from onMount, unlike React/Vue/Svelte: those
14
+ // have to defer past render, and Solid does not. It also matters here - the navigators emit the
15
+ // first 'focus' from a queued microtask after the screen subtree is built, and an onMount-based
16
+ // subscription would still be in the same effect queue, i.e. a race this simply does not enter.
17
+ import { createSignal, onCleanup } from 'solid-js';
18
+ import { NAVIGATION_EVENT_BLUR, NAVIGATION_EVENT_FOCUS } from '../../core/index.js';
19
+ import { requireNavigationScope } from '../navigation-context.js';
20
+ export function createIsFocused() {
21
+ const scope = requireNavigationScope('createIsFocused');
22
+ const [isFocused, setIsFocused] = createSignal(false);
23
+ const { emitter } = scope();
24
+ const unsubscribeFocus = emitter.addListener(NAVIGATION_EVENT_FOCUS, () => setIsFocused(true));
25
+ const unsubscribeBlur = emitter.addListener(NAVIGATION_EVENT_BLUR, () => setIsFocused(false));
26
+ onCleanup(() => {
27
+ unsubscribeFocus();
28
+ unsubscribeBlur();
29
+ });
30
+ return isFocused;
31
+ }
@@ -0,0 +1,3 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import type { INavigatorState } from '../../core';
3
+ export declare function createNavigationState<TResult>(selector: (state: INavigatorState) => TResult): Accessor<TResult>;
@@ -0,0 +1,30 @@
1
+ // Mirrors @react-navigation's useNavigationState. The reducer/dispatch machinery lives in
2
+ // core/navigator-state.ts (tab-router-state.ts / drawer-router-state.ts for the other two
3
+ // navigators) - this only wires the subscription to the `state` broadcast each navigator re-emits
4
+ // after its router state changes.
5
+ //
6
+ // `create*`: it owns a signal and a subscription.
7
+ //
8
+ // Seeded from a single-route snapshot ({ routes: [route] }) rather than left undefined: the real
9
+ // broadcast lands after the screen is built, so a selector reading e.g. `state.routes.at(-1)?.name`
10
+ // still resolves correctly on first paint for the common single-route case - the same async gap
11
+ // createIsFocused documents.
12
+ import { createSignal, onCleanup } from 'solid-js';
13
+ import { NAVIGATION_EVENT_STATE, isRecord } from '../../core/index.js';
14
+ import { requireNavigationScope } from '../navigation-context.js';
15
+ function isNavigatorState(value) {
16
+ return isRecord(value) && Array.isArray(value.routes);
17
+ }
18
+ export function createNavigationState(selector) {
19
+ const scope = requireNavigationScope('createNavigationState');
20
+ const [result, setResult] = createSignal(selector({ routes: [scope().route] }));
21
+ const unsubscribe = scope().emitter.addListener(NAVIGATION_EVENT_STATE, (state) => {
22
+ if (!isNavigatorState(state))
23
+ return;
24
+ // The updater form, not a bare value: a selector legitimately returns a FUNCTION for a
25
+ // navigation-driven callback, and `setResult(fn)` would otherwise be read as an updater.
26
+ setResult(() => selector(state));
27
+ });
28
+ onCleanup(unsubscribe);
29
+ return result;
30
+ }
@@ -0,0 +1,12 @@
1
+ export { useNavigation } from './use-navigation';
2
+ export type { INavigationHandle } from './use-navigation';
3
+ export { useStackNavigation } from './use-stack-navigation';
4
+ export type { IStackNavigationHandle } from './use-stack-navigation';
5
+ export { useTabNavigation } from './use-tab-navigation';
6
+ export type { ITabNavigationHandle } from './use-tab-navigation';
7
+ export { useDrawerNavigation } from './use-drawer-navigation';
8
+ export type { IDrawerNavigationHandle } from './use-drawer-navigation';
9
+ export { useRoute } from './use-route';
10
+ export { createIsFocused } from './create-is-focused';
11
+ export { createFocusEffect } from './create-focus-effect';
12
+ export { createNavigationState } from './create-navigation-state';
@@ -0,0 +1,8 @@
1
+ export { useNavigation } from './use-navigation.js';
2
+ export { useStackNavigation } from './use-stack-navigation.js';
3
+ export { useTabNavigation } from './use-tab-navigation.js';
4
+ export { useDrawerNavigation } from './use-drawer-navigation.js';
5
+ export { useRoute } from './use-route.js';
6
+ export { createIsFocused } from './create-is-focused.js';
7
+ export { createFocusEffect } from './create-focus-effect.js';
8
+ export { createNavigationState } from './create-navigation-state.js';
@@ -0,0 +1,5 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import type { IDrawerNavigatorHandle } from '../../core';
3
+ import type { INavigationHandle } from './use-navigation';
4
+ export type IDrawerNavigationHandle = IDrawerNavigatorHandle & Pick<INavigationHandle, 'addListener' | 'getParent'>;
5
+ export declare function useDrawerNavigation(): Accessor<IDrawerNavigationHandle>;
@@ -0,0 +1,15 @@
1
+ // Twin of use-stack-navigation.ts, narrowing to a Drawer handle instead.
2
+ import { createMemo } from 'solid-js';
3
+ import { isDrawerNavigatorHandle } from '../../core/index.js';
4
+ import { useNavigation } from './use-navigation.js';
5
+ export function useDrawerNavigation() {
6
+ const navigation = useNavigation();
7
+ return createMemo(() => {
8
+ const value = navigation();
9
+ if (!isDrawerNavigatorHandle(value)) {
10
+ throw new Error('useDrawerNavigation() was called from a component whose nearest navigator is not a Drawer. ' +
11
+ 'Use useNavigation() instead if this component can render under more than one navigator kind.');
12
+ }
13
+ return value;
14
+ });
15
+ }
@@ -0,0 +1,8 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import type { INavigationEventListener, INavigationEventName } from '../../core';
3
+ import type { IAnyNavigatorHandle } from '../navigation-context';
4
+ export type INavigationHandle = IAnyNavigatorHandle & {
5
+ addListener: (event: INavigationEventName, listener: INavigationEventListener) => () => void;
6
+ getParent: () => IAnyNavigatorHandle | undefined;
7
+ };
8
+ export declare function useNavigation(): Accessor<INavigationHandle>;
@@ -0,0 +1,23 @@
1
+ // Mirrors @react-navigation's `navigation.addListener('focus', cb)` surface. All pub/sub logic
2
+ // lives in ../../core/navigation-events; this only reads the injected scope and binds identity.
3
+ //
4
+ // `use*`, not `create*`: it CONSUMES an existing context and owns no state or subscription of its
5
+ // own - the same distinction Solid itself draws between `useContext`/`useTransition` and
6
+ // `createSignal`/`createEffect` (adapters/solid/src/primitives/create-color-scheme.ts's header).
7
+ //
8
+ // Returns an ACCESSOR for the reason every value crossing a boundary in this adapter does: a Solid
9
+ // component body runs once, so a returned object would freeze at the scope the screen mounted with
10
+ // and `getParent()` would keep answering with a stale ancestor after a nested navigator re-scoped.
11
+ import { createMemo } from 'solid-js';
12
+ import { requireNavigationScope } from '../navigation-context.js';
13
+ export function useNavigation() {
14
+ const scope = requireNavigationScope('useNavigation');
15
+ return createMemo(() => {
16
+ const { navigation, emitter, parent } = scope();
17
+ return {
18
+ ...navigation,
19
+ addListener: emitter.addListener,
20
+ getParent: () => parent?.navigation,
21
+ };
22
+ });
23
+ }
@@ -0,0 +1,3 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import type { IRoute } from '../../core';
3
+ export declare function useRoute(): Accessor<IRoute<unknown>>;
@@ -0,0 +1,14 @@
1
+ // Mirrors @react-navigation's useRoute. Zero logic of its own - the route object is built by the
2
+ // navigators' route derivation from the shared core reducer's state.
3
+ //
4
+ // THE ACCESSOR IS THE WHOLE POINT HERE. React re-runs a screen's body on every navigation, so its
5
+ // useRoute() can hand back a plain object; Solid runs the body ONCE. A `useRoute()` returning
6
+ // `IRoute` would therefore paint the params the screen was pushed with and never update again -
7
+ // `navigation.setParams({...})` produces a new route object under the SAME key, and nothing would
8
+ // re-read it. Pinned by "setParams reaches a mounted screen" in ../solid.test.tsx.
9
+ import { createMemo } from 'solid-js';
10
+ import { requireNavigationScope } from '../navigation-context.js';
11
+ export function useRoute() {
12
+ const scope = requireNavigationScope('useRoute');
13
+ return createMemo(() => scope().route);
14
+ }
@@ -0,0 +1,5 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import type { INavigatorHandle } from '../../core';
3
+ import type { INavigationHandle } from './use-navigation';
4
+ export type IStackNavigationHandle = INavigatorHandle & Pick<INavigationHandle, 'addListener' | 'getParent'>;
5
+ export declare function useStackNavigation(): Accessor<IStackNavigationHandle>;
@@ -0,0 +1,19 @@
1
+ // useNavigation() returns a union (IAnyNavigatorHandle), since it does not know which navigator
2
+ // mounted the calling component. When a component genuinely only ever renders under a Stack, this
3
+ // narrows that union ONCE, here, so call sites never write their own `'push' in navigation` check.
4
+ //
5
+ // `use*`: a pure consumer of useNavigation()'s accessor, no state of its own.
6
+ import { createMemo } from 'solid-js';
7
+ import { isStackNavigatorHandle } from '../../core/index.js';
8
+ import { useNavigation } from './use-navigation.js';
9
+ export function useStackNavigation() {
10
+ const navigation = useNavigation();
11
+ return createMemo(() => {
12
+ const value = navigation();
13
+ if (!isStackNavigatorHandle(value)) {
14
+ throw new Error('useStackNavigation() was called from a component whose nearest navigator is not a Stack. ' +
15
+ 'Use useNavigation() instead if this component can render under more than one navigator kind.');
16
+ }
17
+ return value;
18
+ });
19
+ }
@@ -0,0 +1,5 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import type { ITabNavigatorHandle } from '../../core';
3
+ import type { INavigationHandle } from './use-navigation';
4
+ export type ITabNavigationHandle = ITabNavigatorHandle & Pick<INavigationHandle, 'addListener' | 'getParent'>;
5
+ export declare function useTabNavigation(): Accessor<ITabNavigationHandle>;
@@ -0,0 +1,15 @@
1
+ // Twin of use-stack-navigation.ts, narrowing to a Tab handle instead.
2
+ import { createMemo } from 'solid-js';
3
+ import { isTabNavigatorHandle } from '../../core/index.js';
4
+ import { useNavigation } from './use-navigation.js';
5
+ export function useTabNavigation() {
6
+ const navigation = useNavigation();
7
+ return createMemo(() => {
8
+ const value = navigation();
9
+ if (!isTabNavigatorHandle(value)) {
10
+ throw new Error('useTabNavigation() was called from a component whose nearest navigator is not a Tab. ' +
11
+ 'Use useNavigation() instead if this component can render under more than one navigator kind.');
12
+ }
13
+ return value;
14
+ });
15
+ }
@@ -0,0 +1,19 @@
1
+ import type { Component } from 'solid-js';
2
+ import type { INavigatorHandle, IRoute, IScreenOptions, ISearchBarCommands, ISearchBarOptions } from '../core';
3
+ export type ISolidSearchBarOptions = ISearchBarOptions & {
4
+ ref?: (commands: ISearchBarCommands | null) => void;
5
+ };
6
+ export type ISolidScreenOptions = Omit<IScreenOptions, 'headerSearchBarOptions'> & {
7
+ headerSearchBarOptions?: ISolidSearchBarOptions;
8
+ };
9
+ export type IScreenOptionsArgs = {
10
+ route: IRoute<unknown>;
11
+ navigation: INavigatorHandle;
12
+ };
13
+ export type IScreenOptionsResolver = (args: IScreenOptionsArgs) => ISolidScreenOptions;
14
+ export type IScreenProps = {
15
+ name: string;
16
+ component: Component;
17
+ options?: ISolidScreenOptions | IScreenOptionsResolver;
18
+ initialParams?: unknown;
19
+ };
@@ -0,0 +1,9 @@
1
+ // Stack.Screen's public prop types, split out of the marker component itself so screen-registry.ts
2
+ // can name `IScreenProps['options']` without importing a component (which would make the two
3
+ // modules circular).
4
+ //
5
+ // Declared here rather than re-exported from another adapter: `component` is a Solid `Component`
6
+ // and the search-bar `ref` is a Solid callback ref, both framework values, which is exactly the
7
+ // test <prop_types_split_agnostic_vs_per_adapter> applies. The agnostic FIELD BASE (IScreenOptions,
8
+ // ISearchBarOptions, IRoute, INavigatorHandle) is shared from ../core verbatim.
9
+ export {};
@@ -0,0 +1,31 @@
1
+ import type { Accessor, Component } from 'solid-js';
2
+ import type { IScreenProps } from './screen-props';
3
+ import type { ITabScreenProps } from './tab-screen-props';
4
+ import type { IDrawerScreenProps } from './drawer-screen-props';
5
+ export type INavigatorKind = 'stack' | 'tab' | 'drawer';
6
+ export type IRegisteredScreen<TOptions> = {
7
+ readonly name: string;
8
+ readonly component: Component;
9
+ readonly options: TOptions;
10
+ readonly initialParams: unknown;
11
+ };
12
+ type IScreenCollector<TKind extends INavigatorKind, TOptions> = {
13
+ readonly kind: TKind;
14
+ register(screen: IRegisteredScreen<TOptions>): void;
15
+ unregister(screen: IRegisteredScreen<TOptions>): void;
16
+ };
17
+ export type IStackScreenCollector = IScreenCollector<'stack', IScreenProps['options']>;
18
+ export type ITabScreenCollector = IScreenCollector<'tab', ITabScreenProps['options']>;
19
+ export type IDrawerScreenCollector = IScreenCollector<'drawer', IDrawerScreenProps['options']>;
20
+ export type IAnyScreenCollector = IStackScreenCollector | ITabScreenCollector | IDrawerScreenCollector;
21
+ export declare const ScreenCollectorProvider: import("solid-js").ContextProviderComponent<IAnyScreenCollector | undefined>;
22
+ export declare function collectStackScreen(screen: IRegisteredScreen<IScreenProps['options']>): void;
23
+ export declare function collectTabScreen(screen: IRegisteredScreen<ITabScreenProps['options']>): void;
24
+ export declare function collectDrawerScreen(screen: IRegisteredScreen<IDrawerScreenProps['options']>): void;
25
+ export declare function toRegistry<TOptions>(screens: readonly IRegisteredScreen<TOptions>[]): Map<string, IRegisteredScreen<TOptions>>;
26
+ export type IScreenSignal<TKind extends INavigatorKind, TOptions> = {
27
+ screens: Accessor<readonly IRegisteredScreen<TOptions>[]>;
28
+ collector: IScreenCollector<TKind, TOptions>;
29
+ };
30
+ export declare function createScreenSignal<TKind extends INavigatorKind, TOptions>(kind: TKind): IScreenSignal<TKind, TOptions>;
31
+ export {};
@@ -0,0 +1,86 @@
1
+ // How a navigator learns about its `<Stack.Screen>` / `<Tab.Screen>` / `<Drawer.Screen>` markers.
2
+ //
3
+ // React reads `children` as an inspectable array; Vue scans the default slot's vnodes
4
+ // (`vnode.type === Screen`); Angular queries `@ContentChildren`. Solid can do NONE of those:
5
+ // `props.children` is a getter that CREATES the children when read, and what comes back is
6
+ // already-built host nodes (or, for a marker that paints nothing, `undefined`) - there is no
7
+ // element description left to inspect. So registration is INVERTED here, exactly as on Svelte:
8
+ // the navigator publishes a collector on the context, each marker registers ITSELF from its own
9
+ // body, and the navigator's route list derives from what came back.
10
+ //
11
+ // The consequence that shapes every navigator in this folder: the registry is EMPTY while the
12
+ // navigator's own body runs. Solid's Provider evaluates its children inside a render effect, i.e.
13
+ // after the body returned, so nothing may be computed eagerly from the registry. Route lists,
14
+ // seeds and options folds are all memos over `screens()` instead.
15
+ //
16
+ // ONE context key, not one per navigator kind: nearest-navigator-wins shadowing falls out of
17
+ // Solid's own owner-chain context lookup, so a Stack nested inside a Tab screen captures its own
18
+ // markers. The `kind` tag then rejects a MISMATCHED marker (a `<Tab.Screen>` written inside a
19
+ // `<Stack>`) instead of letting it register with some far-away ancestor Tab - the failure mode a
20
+ // per-kind key would have.
21
+ //
22
+ // Narrowing back out of the context is done by the three `collect*Screen` functions rather than by
23
+ // a generic one taking `kind` as a parameter: a literal comparison is what lets TypeScript
24
+ // discriminate the union, and a generic parameter would force a cast the project bans.
25
+ import { createContext, createSignal, onCleanup, useContext } from 'solid-js';
26
+ import { dlog } from '@symbiote-native/engine';
27
+ const ScreenCollectorContext = createContext();
28
+ export const ScreenCollectorProvider = ScreenCollectorContext.Provider;
29
+ function collectorFor(kind, name) {
30
+ const collector = useContext(ScreenCollectorContext);
31
+ if (collector === undefined) {
32
+ dlog(`navigation: <${kind} screen "${name}"> rendered outside any navigator, ignored`);
33
+ return undefined;
34
+ }
35
+ if (collector.kind !== kind) {
36
+ dlog(`navigation: a ${kind} screen marker ("${name}") was rendered inside a ` +
37
+ `${collector.kind} navigator, ignored`);
38
+ return undefined;
39
+ }
40
+ return collector;
41
+ }
42
+ // Registration is undone on cleanup, so a marker behind a `<Show>` disappears from the registry
43
+ // exactly like a removed Vue slot vnode stops being scanned.
44
+ function bind(collector, screen) {
45
+ collector.register(screen);
46
+ onCleanup(() => collector.unregister(screen));
47
+ }
48
+ export function collectStackScreen(screen) {
49
+ const collector = collectorFor('stack', screen.name);
50
+ if (collector === undefined || collector.kind !== 'stack')
51
+ return;
52
+ bind(collector, screen);
53
+ }
54
+ export function collectTabScreen(screen) {
55
+ const collector = collectorFor('tab', screen.name);
56
+ if (collector === undefined || collector.kind !== 'tab')
57
+ return;
58
+ bind(collector, screen);
59
+ }
60
+ export function collectDrawerScreen(screen) {
61
+ const collector = collectorFor('drawer', screen.name);
62
+ if (collector === undefined || collector.kind !== 'drawer')
63
+ return;
64
+ bind(collector, screen);
65
+ }
66
+ // name -> entry, last registration wins, exactly like Vue's collectRegistry Map.
67
+ export function toRegistry(screens) {
68
+ const registry = new Map();
69
+ for (const screen of screens)
70
+ registry.set(screen.name, screen);
71
+ return registry;
72
+ }
73
+ // The navigator half: the signal every navigator derives its route list from, plus the collector
74
+ // it publishes on the context. `unregister` filters by IDENTITY, never by name - two markers can
75
+ // briefly share a name while one is being replaced, and dropping by name would evict the survivor.
76
+ export function createScreenSignal(kind) {
77
+ const [screens, setScreens] = createSignal([]);
78
+ return {
79
+ screens,
80
+ collector: {
81
+ kind,
82
+ register: screen => setScreens(list => [...list, screen]),
83
+ unregister: screen => setScreens(list => list.filter(candidate => candidate !== screen)),
84
+ },
85
+ };
86
+ }
@@ -0,0 +1,6 @@
1
+ import type { IScreenProps } from './screen-props';
2
+ import type { ITabScreenProps } from './tab-screen-props';
3
+ import type { IDrawerScreenProps } from './drawer-screen-props';
4
+ export declare function Screen(props: IScreenProps): undefined;
5
+ export declare function TabScreen(props: ITabScreenProps): undefined;
6
+ export declare function DrawerScreen(props: IDrawerScreenProps): undefined;