@symbiote-native/navigation 2.1.0 → 3.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 (237) hide show
  1. package/README.md +89 -14
  2. package/build/angular/drawer/index.js +23 -9
  3. package/build/angular/index.d.ts +4 -4
  4. package/build/angular/index.js +1 -1
  5. package/build/angular/injectors/inject-navigation.js +1 -1
  6. package/build/angular/navigation-scope.directive.js +1 -1
  7. package/build/angular/search-bar-ref.directive.js +1 -1
  8. package/build/angular/stack/index.js +20 -6
  9. package/build/angular/tabs/index.js +21 -7
  10. package/build/core/drawer-options/index.js +19 -5
  11. package/build/core/drawer-router-state/index.js +8 -2
  12. package/build/core/guards.js +3 -1
  13. package/build/core/header-bar-buttons.js +14 -4
  14. package/build/core/index.d.ts +4 -4
  15. package/build/core/index.js +2 -2
  16. package/build/core/navigator-state/index.js +12 -4
  17. package/build/core/render-drawer.js +4 -1
  18. package/build/core/render-stack.js +13 -4
  19. package/build/core/screen-options.js +1 -1
  20. package/build/core/search-bar-commands.js +1 -1
  21. package/build/core/state-persistence/index.js +6 -2
  22. package/build/core/tab-router-state/index.js +8 -2
  23. package/build/react/drawer/index.js +36 -13
  24. package/build/react/index.d.ts +7 -7
  25. package/build/react/index.js +3 -3
  26. package/build/react/stack/index.js +34 -8
  27. package/build/react/tabs/index.js +14 -4
  28. package/build/solid/drawer/index.d.ts +22 -0
  29. package/build/solid/drawer/index.js +394 -0
  30. package/build/solid/drawer-screen-props.d.ts +13 -0
  31. package/build/solid/drawer-screen-props.js +2 -0
  32. package/build/solid/host.d.ts +2 -0
  33. package/build/solid/host.js +23 -0
  34. package/build/solid/index.d.ts +19 -0
  35. package/build/solid/index.js +24 -0
  36. package/build/solid/linking.d.ts +3 -0
  37. package/build/solid/linking.js +55 -0
  38. package/build/solid/navigation-context.d.ts +13 -0
  39. package/build/solid/navigation-context.js +37 -0
  40. package/build/solid/primitives/create-focus-effect.d.ts +1 -0
  41. package/build/solid/primitives/create-focus-effect.js +30 -0
  42. package/build/solid/primitives/create-is-focused.d.ts +2 -0
  43. package/build/solid/primitives/create-is-focused.js +31 -0
  44. package/build/solid/primitives/create-navigation-state.d.ts +3 -0
  45. package/build/solid/primitives/create-navigation-state.js +30 -0
  46. package/build/solid/primitives/index.d.ts +12 -0
  47. package/build/solid/primitives/index.js +8 -0
  48. package/build/solid/primitives/use-drawer-navigation.d.ts +5 -0
  49. package/build/solid/primitives/use-drawer-navigation.js +15 -0
  50. package/build/solid/primitives/use-navigation.d.ts +8 -0
  51. package/build/solid/primitives/use-navigation.js +23 -0
  52. package/build/solid/primitives/use-route.d.ts +3 -0
  53. package/build/solid/primitives/use-route.js +14 -0
  54. package/build/solid/primitives/use-stack-navigation.d.ts +5 -0
  55. package/build/solid/primitives/use-stack-navigation.js +19 -0
  56. package/build/solid/primitives/use-tab-navigation.d.ts +5 -0
  57. package/build/solid/primitives/use-tab-navigation.js +15 -0
  58. package/build/solid/screen-props.d.ts +19 -0
  59. package/build/solid/screen-props.js +9 -0
  60. package/build/solid/screen-registry.d.ts +31 -0
  61. package/build/solid/screen-registry.js +86 -0
  62. package/build/solid/screen.d.ts +6 -0
  63. package/build/solid/screen.js +69 -0
  64. package/build/solid/stack/index.d.ts +15 -0
  65. package/build/solid/stack/index.js +348 -0
  66. package/build/solid/tab-screen-props.d.ts +13 -0
  67. package/build/solid/tab-screen-props.js +3 -0
  68. package/build/solid/tabs/index.d.ts +14 -0
  69. package/build/solid/tabs/index.js +228 -0
  70. package/build/svelte/descriptor-subtree.js +3 -1
  71. package/build/svelte/drawer/index.svelte +163 -25
  72. package/build/svelte/drawer-screen.svelte +2 -1
  73. package/build/svelte/index.d.ts +3 -3
  74. package/build/svelte/index.js +1 -1
  75. package/build/svelte/navigation-scope.svelte +4 -1
  76. package/build/svelte/runes/use-navigation.svelte.js +5 -1
  77. package/build/svelte/stack/index.svelte +43 -6
  78. package/build/svelte/stack/stack-screen.svelte +66 -9
  79. package/build/svelte/tabs/index.svelte +42 -20
  80. package/build/vue/composables/use-navigation.js +5 -1
  81. package/build/vue/drawer/index.js +30 -8
  82. package/build/vue/index.d.ts +3 -3
  83. package/build/vue/index.js +1 -1
  84. package/build/vue/navigation-context.js +1 -1
  85. package/build/vue/stack/index.js +17 -6
  86. package/build/vue/tabs/index.js +18 -5
  87. package/build-ngc/angular/drawer/index.js +31 -11
  88. package/build-ngc/angular/drawer/index.js.map +1 -1
  89. package/build-ngc/angular/index.d.ts +4 -4
  90. package/build-ngc/angular/index.js.map +1 -1
  91. package/build-ngc/angular/injectors/inject-focus-effect.js.map +1 -1
  92. package/build-ngc/angular/injectors/inject-is-focused.js.map +1 -1
  93. package/build-ngc/angular/injectors/inject-navigation-state.js.map +1 -1
  94. package/build-ngc/angular/injectors/inject-navigation.js +1 -1
  95. package/build-ngc/angular/injectors/inject-navigation.js.map +1 -1
  96. package/build-ngc/angular/linking.js.map +1 -1
  97. package/build-ngc/angular/navigation-context.service.js.map +1 -1
  98. package/build-ngc/angular/navigation-scope.directive.js +1 -1
  99. package/build-ngc/angular/navigation-scope.directive.js.map +1 -1
  100. package/build-ngc/angular/screen.directive.js.map +1 -1
  101. package/build-ngc/angular/search-bar-ref.directive.js +1 -1
  102. package/build-ngc/angular/search-bar-ref.directive.js.map +1 -1
  103. package/build-ngc/angular/stack/index.js +26 -8
  104. package/build-ngc/angular/stack/index.js.map +1 -1
  105. package/build-ngc/angular/tab-screen.directive.js.map +1 -1
  106. package/build-ngc/angular/tabs/index.js +21 -7
  107. package/build-ngc/angular/tabs/index.js.map +1 -1
  108. package/build-ngc/core/constants.js.map +1 -1
  109. package/build-ngc/core/drawer-options/index.js +19 -5
  110. package/build-ngc/core/drawer-options/index.js.map +1 -1
  111. package/build-ngc/core/drawer-router-state/index.js +8 -2
  112. package/build-ngc/core/drawer-router-state/index.js.map +1 -1
  113. package/build-ngc/core/guards.js +3 -1
  114. package/build-ngc/core/guards.js.map +1 -1
  115. package/build-ngc/core/header-bar-buttons.js +14 -4
  116. package/build-ngc/core/header-bar-buttons.js.map +1 -1
  117. package/build-ngc/core/index.d.ts +4 -4
  118. package/build-ngc/core/index.js +2 -2
  119. package/build-ngc/core/index.js.map +1 -1
  120. package/build-ngc/core/linking-config/index.js.map +1 -1
  121. package/build-ngc/core/navigation-events/index.js.map +1 -1
  122. package/build-ngc/core/navigator-handles/index.js.map +1 -1
  123. package/build-ngc/core/navigator-state/index.js +12 -4
  124. package/build-ngc/core/navigator-state/index.js.map +1 -1
  125. package/build-ngc/core/render-drawer.js +4 -1
  126. package/build-ngc/core/render-drawer.js.map +1 -1
  127. package/build-ngc/core/render-stack.js +13 -4
  128. package/build-ngc/core/render-stack.js.map +1 -1
  129. package/build-ngc/core/render-tabs.js.map +1 -1
  130. package/build-ngc/core/screen-options.js +1 -1
  131. package/build-ngc/core/screen-options.js.map +1 -1
  132. package/build-ngc/core/search-bar-commands.js +1 -1
  133. package/build-ngc/core/search-bar-commands.js.map +1 -1
  134. package/build-ngc/core/stack-router-state/index.js.map +1 -1
  135. package/build-ngc/core/state-persistence/index.js +6 -2
  136. package/build-ngc/core/state-persistence/index.js.map +1 -1
  137. package/build-ngc/core/tab-router-state/index.js +8 -2
  138. package/build-ngc/core/tab-router-state/index.js.map +1 -1
  139. package/package.json +40 -15
  140. package/src/angular/drawer/index.ts +69 -25
  141. package/src/angular/index.ts +20 -4
  142. package/src/angular/injectors/inject-focus-effect.ts +8 -2
  143. package/src/angular/injectors/inject-is-focused.ts +6 -4
  144. package/src/angular/injectors/inject-navigation-state.ts +7 -4
  145. package/src/angular/injectors/inject-navigation.ts +12 -3
  146. package/src/angular/linking.ts +4 -1
  147. package/src/angular/navigation-context.service.ts +5 -2
  148. package/src/angular/navigation-scope.directive.ts +6 -2
  149. package/src/angular/screen.directive.ts +13 -3
  150. package/src/angular/search-bar-ref.directive.ts +10 -2
  151. package/src/angular/stack/index.ts +68 -22
  152. package/src/angular/tab-screen.directive.ts +3 -1
  153. package/src/angular/tabs/index.ts +36 -12
  154. package/src/core/constants.ts +6 -3
  155. package/src/core/drawer-options/index.ts +40 -13
  156. package/src/core/drawer-router-state/index.ts +11 -3
  157. package/src/core/guards.ts +3 -1
  158. package/src/core/header-bar-buttons.ts +41 -12
  159. package/src/core/index.ts +18 -4
  160. package/src/core/linking-config/index.ts +31 -10
  161. package/src/core/navigation-events/index.ts +8 -2
  162. package/src/core/navigator-handles/index.ts +8 -3
  163. package/src/core/navigator-props.ts +13 -4
  164. package/src/core/navigator-state/index.ts +24 -7
  165. package/src/core/render-drawer.ts +24 -9
  166. package/src/core/render-stack.ts +52 -13
  167. package/src/core/render-tabs.ts +24 -6
  168. package/src/core/screen-options.ts +15 -5
  169. package/src/core/search-bar-commands.ts +12 -3
  170. package/src/core/stack-router-state/index.ts +5 -2
  171. package/src/core/state-persistence/index.ts +13 -3
  172. package/src/core/tab-router-state/index.ts +10 -3
  173. package/src/react/drawer/index.ts +408 -344
  174. package/src/react/drawer-screen.ts +8 -2
  175. package/src/react/hooks/use-focus-effect.ts +8 -2
  176. package/src/react/hooks/use-is-focused.ts +6 -2
  177. package/src/react/hooks/use-navigation.ts +8 -2
  178. package/src/react/index.ts +37 -7
  179. package/src/react/linking.ts +4 -1
  180. package/src/react/navigation-context.ts +6 -2
  181. package/src/react/screen.ts +12 -3
  182. package/src/react/stack/index.ts +294 -231
  183. package/src/react/tab-screen.ts +3 -1
  184. package/src/react/tabs/index.ts +188 -158
  185. package/src/solid/drawer/index.ts +570 -0
  186. package/src/solid/drawer-screen-props.ts +24 -0
  187. package/src/solid/host.ts +29 -0
  188. package/src/solid/index.ts +87 -0
  189. package/src/solid/linking.ts +74 -0
  190. package/src/solid/navigation-context.ts +56 -0
  191. package/src/solid/primitives/create-focus-effect.ts +41 -0
  192. package/src/solid/primitives/create-is-focused.ts +41 -0
  193. package/src/solid/primitives/create-navigation-state.ts +44 -0
  194. package/src/solid/primitives/index.ts +12 -0
  195. package/src/solid/primitives/use-drawer-navigation.ts +25 -0
  196. package/src/solid/primitives/use-navigation.ts +44 -0
  197. package/src/solid/primitives/use-route.ts +18 -0
  198. package/src/solid/primitives/use-stack-navigation.ts +29 -0
  199. package/src/solid/primitives/use-tab-navigation.ts +25 -0
  200. package/src/solid/screen-props.ts +51 -0
  201. package/src/solid/screen-registry.ts +159 -0
  202. package/src/solid/screen.ts +80 -0
  203. package/src/solid/stack/index.ts +528 -0
  204. package/src/solid/tab-screen-props.ts +21 -0
  205. package/src/solid/tabs/index.ts +327 -0
  206. package/src/svelte/attachments.ts +3 -1
  207. package/src/svelte/descriptor-subtree.ts +3 -1
  208. package/src/svelte/drawer/index.svelte +163 -25
  209. package/src/svelte/drawer-screen-props.ts +8 -2
  210. package/src/svelte/drawer-screen.svelte +2 -1
  211. package/src/svelte/fabric-tree.test-helper.ts +37 -3
  212. package/src/svelte/index.ts +15 -3
  213. package/src/svelte/linking.svelte.ts +11 -5
  214. package/src/svelte/navigation-scope.svelte +4 -1
  215. package/src/svelte/runes/use-drawer-navigation.svelte.ts +3 -1
  216. package/src/svelte/runes/use-focus-effect.svelte.ts +8 -2
  217. package/src/svelte/runes/use-navigation-state.svelte.ts +3 -1
  218. package/src/svelte/runes/use-navigation.svelte.ts +13 -3
  219. package/src/svelte/runes/use-stack-navigation.svelte.ts +3 -1
  220. package/src/svelte/screen-props.ts +7 -2
  221. package/src/svelte/screen-registry.ts +9 -3
  222. package/src/svelte/stack/index.svelte +43 -6
  223. package/src/svelte/stack/stack-screen.svelte +66 -9
  224. package/src/svelte/svelte-compile.test-helper.ts +16 -12
  225. package/src/svelte/tab-screen-props.ts +3 -1
  226. package/src/svelte/tabs/index.svelte +42 -20
  227. package/src/vue/composables/use-navigation-state.ts +7 -4
  228. package/src/vue/composables/use-navigation.ts +13 -3
  229. package/src/vue/drawer/index.ts +122 -36
  230. package/src/vue/drawer-screen.ts +12 -5
  231. package/src/vue/index.ts +15 -3
  232. package/src/vue/linking.ts +7 -2
  233. package/src/vue/navigation-context.ts +17 -5
  234. package/src/vue/screen.ts +7 -2
  235. package/src/vue/stack/index.ts +82 -34
  236. package/src/vue/tab-screen.ts +3 -1
  237. package/src/vue/tabs/index.ts +51 -13
@@ -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;
@@ -0,0 +1,69 @@
1
+ // Stack.Screen / Tab.Screen / Drawer.Screen: declarative markers that paint nothing. The navigator
2
+ // reads them through the context collector (screen-registry.ts) rather than by scanning children
3
+ // the way React and Vue can, then mounts the registered component itself.
4
+ //
5
+ // Every field is handed over as a GETTER over this component's own props, never a snapshot: a Solid
6
+ // component body runs ONCE, so `{ name: props.name }` would freeze the marker at its mount-time
7
+ // values and an app that swaps `options` on a live marker would silently keep the old header.
8
+ //
9
+ // The three live in one file because they differ only in which collector they call - three
10
+ // near-identical files would drift the way the two descriptor-shape guards did before they were
11
+ // lifted into @symbiote-native/components.
12
+ //
13
+ // Returning `undefined` (not an empty host element) is what keeps a marker free: Solid inserts
14
+ // nothing for it, and where a following sibling forces a position placeholder the renderer emits an
15
+ // engine ANCHOR, which the commit walk skips. Svelte needed a hidden zero-size host for its markers
16
+ // because its compiler turns the whitespace BETWEEN them into real text nodes; Solid's JSX drops
17
+ // that whitespace, so the hazard does not exist here.
18
+ import { collectDrawerScreen, collectStackScreen, collectTabScreen, } from './screen-registry.js';
19
+ export function Screen(props) {
20
+ collectStackScreen({
21
+ get name() {
22
+ return props.name;
23
+ },
24
+ get component() {
25
+ return props.component;
26
+ },
27
+ get options() {
28
+ return props.options;
29
+ },
30
+ get initialParams() {
31
+ return props.initialParams;
32
+ },
33
+ });
34
+ return undefined;
35
+ }
36
+ export function TabScreen(props) {
37
+ collectTabScreen({
38
+ get name() {
39
+ return props.name;
40
+ },
41
+ get component() {
42
+ return props.component;
43
+ },
44
+ get options() {
45
+ return props.options;
46
+ },
47
+ get initialParams() {
48
+ return props.initialParams;
49
+ },
50
+ });
51
+ return undefined;
52
+ }
53
+ export function DrawerScreen(props) {
54
+ collectDrawerScreen({
55
+ get name() {
56
+ return props.name;
57
+ },
58
+ get component() {
59
+ return props.component;
60
+ },
61
+ get options() {
62
+ return props.options;
63
+ },
64
+ get initialParams() {
65
+ return props.initialParams;
66
+ },
67
+ });
68
+ return undefined;
69
+ }
@@ -0,0 +1,15 @@
1
+ import type { JSX } from '@symbiote-native/solid/jsx-runtime';
2
+ import type { INavigatorHandle } from '../../core';
3
+ import { Screen } from '../screen';
4
+ import type { ISolidScreenOptions } from '../screen-props';
5
+ export type { INavigatorHandle } from '../../core';
6
+ export type IStackProps = {
7
+ initialRouteName?: string;
8
+ screenOptions?: ISolidScreenOptions;
9
+ ref?: (handle: INavigatorHandle) => void;
10
+ children?: JSX.Element;
11
+ };
12
+ declare function StackImpl(props: IStackProps): JSX.Element;
13
+ export declare const Stack: typeof StackImpl & {
14
+ Screen: typeof Screen;
15
+ };