@symbiote-native/navigation 2.1.0 → 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 (234) hide show
  1. package/README.md +42 -12
  2. package/build/angular/drawer/index.js +23 -9
  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.js +20 -6
  8. package/build/angular/tabs/index.js +21 -7
  9. package/build/core/drawer-options/index.js +19 -5
  10. package/build/core/drawer-router-state/index.js +8 -2
  11. package/build/core/guards.js +3 -1
  12. package/build/core/header-bar-buttons.js +14 -4
  13. package/build/core/index.d.ts +4 -4
  14. package/build/core/index.js +2 -2
  15. package/build/core/navigator-state/index.js +12 -4
  16. package/build/core/render-drawer.js +4 -1
  17. package/build/core/render-stack.js +13 -4
  18. package/build/core/screen-options.js +1 -1
  19. package/build/core/search-bar-commands.js +1 -1
  20. package/build/core/state-persistence/index.js +6 -2
  21. package/build/core/tab-router-state/index.js +8 -2
  22. package/build/react/drawer/index.js +36 -13
  23. package/build/react/index.d.ts +7 -7
  24. package/build/react/index.js +2 -2
  25. package/build/react/stack/index.js +34 -8
  26. package/build/react/tabs/index.js +14 -4
  27. package/build/solid/drawer/index.d.ts +22 -0
  28. package/build/solid/drawer/index.js +394 -0
  29. package/build/solid/drawer-screen-props.d.ts +13 -0
  30. package/build/solid/drawer-screen-props.js +2 -0
  31. package/build/solid/host.d.ts +2 -0
  32. package/build/solid/host.js +23 -0
  33. package/build/solid/index.d.ts +19 -0
  34. package/build/solid/index.js +24 -0
  35. package/build/solid/linking.d.ts +3 -0
  36. package/build/solid/linking.js +55 -0
  37. package/build/solid/navigation-context.d.ts +13 -0
  38. package/build/solid/navigation-context.js +37 -0
  39. package/build/solid/primitives/create-focus-effect.d.ts +1 -0
  40. package/build/solid/primitives/create-focus-effect.js +30 -0
  41. package/build/solid/primitives/create-is-focused.d.ts +2 -0
  42. package/build/solid/primitives/create-is-focused.js +31 -0
  43. package/build/solid/primitives/create-navigation-state.d.ts +3 -0
  44. package/build/solid/primitives/create-navigation-state.js +30 -0
  45. package/build/solid/primitives/index.d.ts +12 -0
  46. package/build/solid/primitives/index.js +8 -0
  47. package/build/solid/primitives/use-drawer-navigation.d.ts +5 -0
  48. package/build/solid/primitives/use-drawer-navigation.js +15 -0
  49. package/build/solid/primitives/use-navigation.d.ts +8 -0
  50. package/build/solid/primitives/use-navigation.js +23 -0
  51. package/build/solid/primitives/use-route.d.ts +3 -0
  52. package/build/solid/primitives/use-route.js +14 -0
  53. package/build/solid/primitives/use-stack-navigation.d.ts +5 -0
  54. package/build/solid/primitives/use-stack-navigation.js +19 -0
  55. package/build/solid/primitives/use-tab-navigation.d.ts +5 -0
  56. package/build/solid/primitives/use-tab-navigation.js +15 -0
  57. package/build/solid/screen-props.d.ts +19 -0
  58. package/build/solid/screen-props.js +9 -0
  59. package/build/solid/screen-registry.d.ts +31 -0
  60. package/build/solid/screen-registry.js +86 -0
  61. package/build/solid/screen.d.ts +6 -0
  62. package/build/solid/screen.js +69 -0
  63. package/build/solid/stack/index.d.ts +15 -0
  64. package/build/solid/stack/index.js +348 -0
  65. package/build/solid/tab-screen-props.d.ts +13 -0
  66. package/build/solid/tab-screen-props.js +3 -0
  67. package/build/solid/tabs/index.d.ts +14 -0
  68. package/build/solid/tabs/index.js +228 -0
  69. package/build/svelte/descriptor-subtree.js +3 -1
  70. package/build/svelte/drawer/index.svelte +163 -25
  71. package/build/svelte/drawer-screen.svelte +2 -1
  72. package/build/svelte/index.d.ts +3 -3
  73. package/build/svelte/navigation-scope.svelte +4 -1
  74. package/build/svelte/runes/use-navigation.svelte.js +5 -1
  75. package/build/svelte/stack/index.svelte +43 -6
  76. package/build/svelte/stack/stack-screen.svelte +66 -9
  77. package/build/svelte/tabs/index.svelte +42 -20
  78. package/build/vue/composables/use-navigation.js +5 -1
  79. package/build/vue/drawer/index.js +30 -8
  80. package/build/vue/index.d.ts +3 -3
  81. package/build/vue/navigation-context.js +1 -1
  82. package/build/vue/stack/index.js +17 -6
  83. package/build/vue/tabs/index.js +18 -5
  84. package/build-ngc/angular/drawer/index.js +31 -11
  85. package/build-ngc/angular/drawer/index.js.map +1 -1
  86. package/build-ngc/angular/index.d.ts +4 -4
  87. package/build-ngc/angular/index.js.map +1 -1
  88. package/build-ngc/angular/injectors/inject-focus-effect.js.map +1 -1
  89. package/build-ngc/angular/injectors/inject-is-focused.js.map +1 -1
  90. package/build-ngc/angular/injectors/inject-navigation-state.js.map +1 -1
  91. package/build-ngc/angular/injectors/inject-navigation.js +1 -1
  92. package/build-ngc/angular/injectors/inject-navigation.js.map +1 -1
  93. package/build-ngc/angular/linking.js.map +1 -1
  94. package/build-ngc/angular/navigation-context.service.js.map +1 -1
  95. package/build-ngc/angular/navigation-scope.directive.js +1 -1
  96. package/build-ngc/angular/navigation-scope.directive.js.map +1 -1
  97. package/build-ngc/angular/screen.directive.js.map +1 -1
  98. package/build-ngc/angular/search-bar-ref.directive.js +1 -1
  99. package/build-ngc/angular/search-bar-ref.directive.js.map +1 -1
  100. package/build-ngc/angular/stack/index.js +26 -8
  101. package/build-ngc/angular/stack/index.js.map +1 -1
  102. package/build-ngc/angular/tab-screen.directive.js.map +1 -1
  103. package/build-ngc/angular/tabs/index.js +21 -7
  104. package/build-ngc/angular/tabs/index.js.map +1 -1
  105. package/build-ngc/core/constants.js.map +1 -1
  106. package/build-ngc/core/drawer-options/index.js +19 -5
  107. package/build-ngc/core/drawer-options/index.js.map +1 -1
  108. package/build-ngc/core/drawer-router-state/index.js +8 -2
  109. package/build-ngc/core/drawer-router-state/index.js.map +1 -1
  110. package/build-ngc/core/guards.js +3 -1
  111. package/build-ngc/core/guards.js.map +1 -1
  112. package/build-ngc/core/header-bar-buttons.js +14 -4
  113. package/build-ngc/core/header-bar-buttons.js.map +1 -1
  114. package/build-ngc/core/index.d.ts +4 -4
  115. package/build-ngc/core/index.js +2 -2
  116. package/build-ngc/core/index.js.map +1 -1
  117. package/build-ngc/core/linking-config/index.js.map +1 -1
  118. package/build-ngc/core/navigation-events/index.js.map +1 -1
  119. package/build-ngc/core/navigator-handles/index.js.map +1 -1
  120. package/build-ngc/core/navigator-state/index.js +12 -4
  121. package/build-ngc/core/navigator-state/index.js.map +1 -1
  122. package/build-ngc/core/render-drawer.js +4 -1
  123. package/build-ngc/core/render-drawer.js.map +1 -1
  124. package/build-ngc/core/render-stack.js +13 -4
  125. package/build-ngc/core/render-stack.js.map +1 -1
  126. package/build-ngc/core/render-tabs.js.map +1 -1
  127. package/build-ngc/core/screen-options.js +1 -1
  128. package/build-ngc/core/screen-options.js.map +1 -1
  129. package/build-ngc/core/search-bar-commands.js +1 -1
  130. package/build-ngc/core/search-bar-commands.js.map +1 -1
  131. package/build-ngc/core/stack-router-state/index.js.map +1 -1
  132. package/build-ngc/core/state-persistence/index.js +6 -2
  133. package/build-ngc/core/state-persistence/index.js.map +1 -1
  134. package/build-ngc/core/tab-router-state/index.js +8 -2
  135. package/build-ngc/core/tab-router-state/index.js.map +1 -1
  136. package/package.json +23 -9
  137. package/src/angular/drawer/index.ts +69 -25
  138. package/src/angular/index.ts +20 -4
  139. package/src/angular/injectors/inject-focus-effect.ts +8 -2
  140. package/src/angular/injectors/inject-is-focused.ts +6 -4
  141. package/src/angular/injectors/inject-navigation-state.ts +7 -4
  142. package/src/angular/injectors/inject-navigation.ts +12 -3
  143. package/src/angular/linking.ts +4 -1
  144. package/src/angular/navigation-context.service.ts +5 -2
  145. package/src/angular/navigation-scope.directive.ts +6 -2
  146. package/src/angular/screen.directive.ts +13 -3
  147. package/src/angular/search-bar-ref.directive.ts +10 -2
  148. package/src/angular/stack/index.ts +68 -22
  149. package/src/angular/tab-screen.directive.ts +3 -1
  150. package/src/angular/tabs/index.ts +36 -12
  151. package/src/core/constants.ts +6 -3
  152. package/src/core/drawer-options/index.ts +40 -13
  153. package/src/core/drawer-router-state/index.ts +11 -3
  154. package/src/core/guards.ts +3 -1
  155. package/src/core/header-bar-buttons.ts +41 -12
  156. package/src/core/index.ts +18 -4
  157. package/src/core/linking-config/index.ts +31 -10
  158. package/src/core/navigation-events/index.ts +8 -2
  159. package/src/core/navigator-handles/index.ts +8 -3
  160. package/src/core/navigator-props.ts +13 -4
  161. package/src/core/navigator-state/index.ts +24 -7
  162. package/src/core/render-drawer.ts +24 -9
  163. package/src/core/render-stack.ts +52 -13
  164. package/src/core/render-tabs.ts +24 -6
  165. package/src/core/screen-options.ts +15 -5
  166. package/src/core/search-bar-commands.ts +12 -3
  167. package/src/core/stack-router-state/index.ts +5 -2
  168. package/src/core/state-persistence/index.ts +13 -3
  169. package/src/core/tab-router-state/index.ts +10 -3
  170. package/src/react/drawer/index.ts +408 -344
  171. package/src/react/drawer-screen.ts +8 -2
  172. package/src/react/hooks/use-focus-effect.ts +8 -2
  173. package/src/react/hooks/use-is-focused.ts +6 -2
  174. package/src/react/hooks/use-navigation.ts +8 -2
  175. package/src/react/index.ts +37 -7
  176. package/src/react/linking.ts +4 -1
  177. package/src/react/navigation-context.ts +6 -2
  178. package/src/react/screen.ts +12 -3
  179. package/src/react/stack/index.ts +294 -231
  180. package/src/react/tab-screen.ts +3 -1
  181. package/src/react/tabs/index.ts +188 -158
  182. package/src/solid/drawer/index.ts +570 -0
  183. package/src/solid/drawer-screen-props.ts +24 -0
  184. package/src/solid/host.ts +29 -0
  185. package/src/solid/index.ts +87 -0
  186. package/src/solid/linking.ts +74 -0
  187. package/src/solid/navigation-context.ts +56 -0
  188. package/src/solid/primitives/create-focus-effect.ts +41 -0
  189. package/src/solid/primitives/create-is-focused.ts +41 -0
  190. package/src/solid/primitives/create-navigation-state.ts +44 -0
  191. package/src/solid/primitives/index.ts +12 -0
  192. package/src/solid/primitives/use-drawer-navigation.ts +25 -0
  193. package/src/solid/primitives/use-navigation.ts +44 -0
  194. package/src/solid/primitives/use-route.ts +18 -0
  195. package/src/solid/primitives/use-stack-navigation.ts +29 -0
  196. package/src/solid/primitives/use-tab-navigation.ts +25 -0
  197. package/src/solid/screen-props.ts +51 -0
  198. package/src/solid/screen-registry.ts +159 -0
  199. package/src/solid/screen.ts +80 -0
  200. package/src/solid/stack/index.ts +528 -0
  201. package/src/solid/tab-screen-props.ts +21 -0
  202. package/src/solid/tabs/index.ts +327 -0
  203. package/src/svelte/attachments.ts +3 -1
  204. package/src/svelte/descriptor-subtree.ts +3 -1
  205. package/src/svelte/drawer/index.svelte +163 -25
  206. package/src/svelte/drawer-screen-props.ts +8 -2
  207. package/src/svelte/drawer-screen.svelte +2 -1
  208. package/src/svelte/fabric-tree.test-helper.ts +37 -3
  209. package/src/svelte/index.ts +15 -3
  210. package/src/svelte/linking.svelte.ts +11 -5
  211. package/src/svelte/navigation-scope.svelte +4 -1
  212. package/src/svelte/runes/use-drawer-navigation.svelte.ts +3 -1
  213. package/src/svelte/runes/use-focus-effect.svelte.ts +8 -2
  214. package/src/svelte/runes/use-navigation-state.svelte.ts +3 -1
  215. package/src/svelte/runes/use-navigation.svelte.ts +13 -3
  216. package/src/svelte/runes/use-stack-navigation.svelte.ts +3 -1
  217. package/src/svelte/screen-props.ts +7 -2
  218. package/src/svelte/screen-registry.ts +9 -3
  219. package/src/svelte/stack/index.svelte +43 -6
  220. package/src/svelte/stack/stack-screen.svelte +66 -9
  221. package/src/svelte/svelte-compile.test-helper.ts +16 -12
  222. package/src/svelte/tab-screen-props.ts +3 -1
  223. package/src/svelte/tabs/index.svelte +42 -20
  224. package/src/vue/composables/use-navigation-state.ts +7 -4
  225. package/src/vue/composables/use-navigation.ts +13 -3
  226. package/src/vue/drawer/index.ts +122 -36
  227. package/src/vue/drawer-screen.ts +12 -5
  228. package/src/vue/index.ts +15 -3
  229. package/src/vue/linking.ts +7 -2
  230. package/src/vue/navigation-context.ts +17 -5
  231. package/src/vue/screen.ts +7 -2
  232. package/src/vue/stack/index.ts +82 -34
  233. package/src/vue/tab-screen.ts +3 -1
  234. package/src/vue/tabs/index.ts +51 -13
@@ -15,7 +15,11 @@ export type {
15
15
  IScreenOptionsArgs,
16
16
  IScreenOptionsResolver,
17
17
  } from './screen.directive';
18
- export type { IScreenOptions, IStackAnimation, IStackPresentation } from '../core';
18
+ export type {
19
+ IScreenOptions,
20
+ IStackAnimation,
21
+ IStackPresentation,
22
+ } from '../core';
19
23
 
20
24
  export {
21
25
  injectNavigation,
@@ -46,7 +50,10 @@ export type { ILinkingConfig, IScreenLinkingConfig } from '../core';
46
50
  export { Tab } from './tabs';
47
51
  export type { ITabNavigatorHandle } from './tabs';
48
52
  export { TabScreenDirective } from './tab-screen.directive';
49
- export type { ITabScreenOptionsArgs, ITabScreenOptionsResolver } from './tab-screen.directive';
53
+ export type {
54
+ ITabScreenOptionsArgs,
55
+ ITabScreenOptionsResolver,
56
+ } from './tab-screen.directive';
50
57
  export type { ITabOptions, ITabBarIcon } from '../core';
51
58
 
52
59
  // Drawer: the swipeable drawer navigator, a PURE-JS UI (PanResponder + Animated, no
@@ -54,10 +61,19 @@ export type { ITabOptions, ITabBarIcon } from '../core';
54
61
  // ../register import above). See drawer.ts's header for the feasibility note re:
55
62
  // react-native-gesture-handler / react-native-reanimated parity gaps.
56
63
  export { Drawer } from './drawer';
57
- export type { IDrawerNavigatorHandle, IDrawerDescriptorMap, IDrawerContentContext } from './drawer';
64
+ export type {
65
+ IDrawerNavigatorHandle,
66
+ IDrawerDescriptorMap,
67
+ IDrawerContentContext,
68
+ } from './drawer';
58
69
  export { DrawerScreenDirective } from './drawer-screen.directive';
59
70
  export type {
60
71
  IDrawerScreenOptionsArgs,
61
72
  IDrawerScreenOptionsResolver,
62
73
  } from './drawer-screen.directive';
63
- export type { IDrawerOptions, IDrawerScreenOptions, IDrawerType, IDrawerPosition } from '../core';
74
+ export type {
75
+ IDrawerOptions,
76
+ IDrawerScreenOptions,
77
+ IDrawerType,
78
+ IDrawerPosition,
79
+ } from '../core';
@@ -26,8 +26,14 @@ export function injectFocusEffect(effect: IFocusEffectCallback): void {
26
26
  cleanup = undefined;
27
27
  };
28
28
 
29
- const unsubscribeFocus = context.emitter.addListener(NAVIGATION_EVENT_FOCUS, runEffect);
30
- const unsubscribeBlur = context.emitter.addListener(NAVIGATION_EVENT_BLUR, runCleanup);
29
+ const unsubscribeFocus = context.emitter.addListener(
30
+ NAVIGATION_EVENT_FOCUS,
31
+ runEffect,
32
+ );
33
+ const unsubscribeBlur = context.emitter.addListener(
34
+ NAVIGATION_EVENT_BLUR,
35
+ runCleanup,
36
+ );
31
37
 
32
38
  destroyRef.onDestroy(() => {
33
39
  unsubscribeFocus();
@@ -13,11 +13,13 @@ export function injectIsFocused(): Signal<boolean> {
13
13
  const destroyRef = inject(DestroyRef);
14
14
  const isFocused = signal(false);
15
15
 
16
- const unsubscribeFocus = context.emitter.addListener(NAVIGATION_EVENT_FOCUS, () =>
17
- isFocused.set(true),
16
+ const unsubscribeFocus = context.emitter.addListener(
17
+ NAVIGATION_EVENT_FOCUS,
18
+ () => isFocused.set(true),
18
19
  );
19
- const unsubscribeBlur = context.emitter.addListener(NAVIGATION_EVENT_BLUR, () =>
20
- isFocused.set(false),
20
+ const unsubscribeBlur = context.emitter.addListener(
21
+ NAVIGATION_EVENT_BLUR,
22
+ () => isFocused.set(false),
21
23
  );
22
24
  destroyRef.onDestroy(() => {
23
25
  unsubscribeFocus();
@@ -36,10 +36,13 @@ export function injectNavigationState<TResult>(
36
36
 
37
37
  const result = signal(selector({ routes: [route] }));
38
38
 
39
- const unsubscribe = context.emitter.addListener(NAVIGATION_EVENT_STATE, (state: unknown) => {
40
- if (!isNavigatorState(state)) return;
41
- result.set(selector(state));
42
- });
39
+ const unsubscribe = context.emitter.addListener(
40
+ NAVIGATION_EVENT_STATE,
41
+ (state: unknown) => {
42
+ if (!isNavigatorState(state)) return;
43
+ result.set(selector(state));
44
+ },
45
+ );
43
46
  destroyRef.onDestroy(unsubscribe);
44
47
 
45
48
  return result.asReadonly();
@@ -6,11 +6,20 @@
6
6
  // surface. All pub/sub logic lives in ../../core/navigation-events; this function only reads
7
7
  // NavigationContextService and binds identity.
8
8
 
9
- import type { INavigationEventListener, INavigationEventName } from '../../core';
10
- import { requireNavigationContext, type IAnyNavigatorHandle } from '../navigation-context.service';
9
+ import type {
10
+ INavigationEventListener,
11
+ INavigationEventName,
12
+ } from '../../core';
13
+ import {
14
+ requireNavigationContext,
15
+ type IAnyNavigatorHandle,
16
+ } from '../navigation-context.service';
11
17
 
12
18
  export type INavigationHandle = IAnyNavigatorHandle & {
13
- addListener: (event: INavigationEventName, listener: INavigationEventListener) => () => void;
19
+ addListener: (
20
+ event: INavigationEventName,
21
+ listener: INavigationEventListener,
22
+ ) => () => void;
14
23
  // Walks exactly ONE hop up NavigationContextService's `parent` chain to the enclosing
15
24
  // navigator's handle (plain immediate-parent walking, no named/targeted ancestor lookup).
16
25
  getParent: () => IAnyNavigatorHandle | undefined;
@@ -22,7 +22,10 @@ export function injectLinkingIntegration(
22
22
  ): void {
23
23
  const destroyRef = inject(DestroyRef);
24
24
 
25
- function applyRoute(url: string, dispatch: (name: string, params?: unknown) => void): void {
25
+ function applyRoute(
26
+ url: string,
27
+ dispatch: (name: string, params?: unknown) => void,
28
+ ): void {
26
29
  const route = resolveRouteFromUrl(config, url);
27
30
  if (route === null) {
28
31
  dlog(`injectLinkingIntegration: no route resolved for "${url}"`);
@@ -38,7 +38,8 @@ export class NavigationContextService {
38
38
  // package's zoneless/signals-first convention. `navigation`/`emitter` are stable for a screen's
39
39
  // whole lifetime, so they stay plain fields - no reactivity needed there.
40
40
  private readonly routeSignal = signal<IRoute<unknown> | undefined>(undefined);
41
- readonly route: Signal<IRoute<unknown> | undefined> = this.routeSignal.asReadonly();
41
+ readonly route: Signal<IRoute<unknown> | undefined> =
42
+ this.routeSignal.asReadonly();
42
43
 
43
44
  navigation!: IAnyNavigatorHandle;
44
45
  emitter!: INavigationEmitter;
@@ -57,7 +58,9 @@ export class NavigationContextService {
57
58
  // identical everywhere instead of six independently-typed copies. Must be called from an
58
59
  // injection context, same requirement `inject()` itself has - every call site is the first line
59
60
  // of an injectX function, which callers already only invoke from their own injection context.
60
- export function requireNavigationContext(injectorName: string): NavigationContextService {
61
+ export function requireNavigationContext(
62
+ injectorName: string,
63
+ ): NavigationContextService {
61
64
  const context = inject(NavigationContextService, { optional: true });
62
65
  if (!context) {
63
66
  throw new Error(
@@ -9,7 +9,10 @@
9
9
 
10
10
  import { Directive, Input, inject, type OnChanges } from '@angular/core';
11
11
  import type { INavigationEmitter, IRoute } from '../core';
12
- import { NavigationContextService, type IAnyNavigatorHandle } from './navigation-context.service';
12
+ import {
13
+ NavigationContextService,
14
+ type IAnyNavigatorHandle,
15
+ } from './navigation-context.service';
13
16
 
14
17
  @Directive({
15
18
  selector: '[symbioteNavigationScope]',
@@ -17,7 +20,8 @@ import { NavigationContextService, type IAnyNavigatorHandle } from './navigation
17
20
  providers: [NavigationContextService],
18
21
  })
19
22
  export class NavigationScopeDirective implements OnChanges {
20
- @Input({ required: true, alias: 'symbioteNavigationScope' }) route!: IRoute<unknown>;
23
+ @Input({ required: true, alias: 'symbioteNavigationScope' })
24
+ route!: IRoute<unknown>;
21
25
  @Input({ required: true }) navigation!: IAnyNavigatorHandle;
22
26
  @Input({ required: true }) emitter!: INavigationEmitter;
23
27
 
@@ -7,7 +7,12 @@
7
7
  // independent of whether the template is ever instantiated).
8
8
 
9
9
  import { Directive, Input, type Type } from '@angular/core';
10
- import type { ISearchBarCommands, ISearchBarOptions, IScreenOptions, IRoute } from '../core';
10
+ import type {
11
+ ISearchBarCommands,
12
+ ISearchBarOptions,
13
+ IScreenOptions,
14
+ IRoute,
15
+ } from '../core';
11
16
  import type { INavigatorHandle } from '../core';
12
17
 
13
18
  // The imperative ref (focus/blur/clearText/setText/cancelSearch/toggleCancelButton) carries a
@@ -21,7 +26,10 @@ export type IAngularSearchBarOptions = ISearchBarOptions & {
21
26
  ref?: { current: ISearchBarCommands | null };
22
27
  };
23
28
 
24
- export type IAngularScreenOptions = Omit<IScreenOptions, 'headerSearchBarOptions'> & {
29
+ export type IAngularScreenOptions = Omit<
30
+ IScreenOptions,
31
+ 'headerSearchBarOptions'
32
+ > & {
25
33
  headerSearchBarOptions?: IAngularSearchBarOptions;
26
34
  };
27
35
 
@@ -34,7 +42,9 @@ export type IScreenOptionsArgs = {
34
42
  navigation: INavigatorHandle;
35
43
  };
36
44
 
37
- export type IScreenOptionsResolver = (args: IScreenOptionsArgs) => IAngularScreenOptions;
45
+ export type IScreenOptionsResolver = (
46
+ args: IScreenOptionsArgs,
47
+ ) => IAngularScreenOptions;
38
48
 
39
49
  @Directive({
40
50
  selector: 'ng-template[symbioteScreen]',
@@ -7,7 +7,14 @@
7
7
  // needed here: buildSearchBarHandle's own methods are already a LAZY getter over the node,
8
8
  // tolerant of the node existing-but-not-yet-committed, same contract every adapter shares).
9
9
 
10
- import { Directive, Input, inject, type OnDestroy, type OnInit, ElementRef } from '@angular/core';
10
+ import {
11
+ Directive,
12
+ Input,
13
+ inject,
14
+ type OnDestroy,
15
+ type OnInit,
16
+ ElementRef,
17
+ } from '@angular/core';
11
18
  import { isSymbioteNode, dlog, debugNodeId } from '@symbiote-native/engine';
12
19
  import { buildSearchBarHandle } from '../core';
13
20
  import type { ISearchBarCommands } from '../core';
@@ -17,7 +24,8 @@ import type { ISearchBarCommands } from '../core';
17
24
  standalone: true,
18
25
  })
19
26
  export class SearchBarRefDirective implements OnInit, OnDestroy {
20
- @Input('symbioteSearchBarRef') ref: { current: ISearchBarCommands | null } | undefined;
27
+ @Input('symbioteSearchBarRef') ref:
28
+ { current: ISearchBarCommands | null } | undefined;
21
29
 
22
30
  private readonly elementRef = inject<ElementRef<unknown>>(ElementRef);
23
31
 
@@ -91,13 +91,18 @@ import type {
91
91
  import { NavigationScopeDirective } from '../navigation-scope.directive';
92
92
  import { SearchBarRefDirective } from '../search-bar-ref.directive';
93
93
  import { ScreenDirective } from '../screen.directive';
94
- import type { IAngularScreenOptions, IScreenOptionsArgs } from '../screen.directive';
94
+ import type {
95
+ IAngularScreenOptions,
96
+ IScreenOptionsArgs,
97
+ } from '../screen.directive';
95
98
 
96
99
  export type { INavigatorHandle } from '../../core';
97
100
 
98
101
  // backTitleVisible defaults to `true` on both platforms per the codegen spec's own default - no
99
102
  // ios/android divergence in v1 scope (mirrors react/stack.ts's own constant exactly).
100
- const NAVIGATOR_PLATFORM: INavigatorPlatform = { defaultHeaderBackTitleVisible: true };
103
+ const NAVIGATOR_PLATFORM: INavigatorPlatform = {
104
+ defaultHeaderBackTitleVisible: true,
105
+ };
101
106
 
102
107
  let stackInstanceCounter = 0;
103
108
 
@@ -162,7 +167,9 @@ let stackInstanceCounter = 0;
162
167
  </ng-template>
163
168
 
164
169
  <ng-template #headerAndContentTpl let-route let-index="index">
165
- <RNSScreenStackHeaderConfig [symbioteHostProps]="headerConfigProps(route, index)">
170
+ <RNSScreenStackHeaderConfig
171
+ [symbioteHostProps]="headerConfigProps(route, index)"
172
+ >
166
173
  @if (hasSearchBar(route)) {
167
174
  <RNSScreenStackHeaderSubview [symbioteHostProps]="headerSubviewProps">
168
175
  <RNSSearchBar
@@ -172,7 +179,9 @@ let stackInstanceCounter = 0;
172
179
  </RNSScreenStackHeaderSubview>
173
180
  }
174
181
  </RNSScreenStackHeaderConfig>
175
- <RNSScreenContentWrapper [symbioteHostProps]="contentWrapperProps(route, index)">
182
+ <RNSScreenContentWrapper
183
+ [symbioteHostProps]="contentWrapperProps(route, index)"
184
+ >
176
185
  <ng-container
177
186
  [symbioteNavigationScope]="route"
178
187
  [navigation]="this"
@@ -185,7 +194,8 @@ let stackInstanceCounter = 0;
185
194
  `,
186
195
  })
187
196
  export class Stack implements AfterContentInit, OnDestroy, INavigatorHandle {
188
- @ContentChildren(ScreenDirective) private readonly screenChildren!: QueryList<ScreenDirective>;
197
+ @ContentChildren(ScreenDirective)
198
+ private readonly screenChildren!: QueryList<ScreenDirective>;
189
199
 
190
200
  @Input() initialRouteName?: string;
191
201
  @Input() screenOptions?: IAngularScreenOptions;
@@ -215,7 +225,8 @@ export class Stack implements AfterContentInit, OnDestroy, INavigatorHandle {
215
225
 
216
226
  readonly push = (name: string, params?: unknown): void =>
217
227
  this.dispatch({ type: 'push', route: this.createRoute(name, params) });
218
- readonly pop = (count?: number): void => this.dispatch({ type: 'pop', count });
228
+ readonly pop = (count?: number): void =>
229
+ this.dispatch({ type: 'pop', count });
219
230
  readonly popToTop = (): void => this.dispatch({ type: 'popToTop' });
220
231
  readonly popTo = (key: string): void => this.dispatch({ type: 'popTo', key });
221
232
  readonly replace = (name: string, params?: unknown): void =>
@@ -224,14 +235,17 @@ export class Stack implements AfterContentInit, OnDestroy, INavigatorHandle {
224
235
  this.dispatch({ type: 'setParams', key, params });
225
236
  readonly reset = (nextState: INavigatorState): void =>
226
237
  this.dispatch({ type: 'reset', state: nextState });
227
- readonly canGoBack = (): boolean => (this.stateSignal()?.routes.length ?? 0) > 1;
238
+ readonly canGoBack = (): boolean =>
239
+ (this.stateSignal()?.routes.length ?? 0) > 1;
228
240
 
229
241
  ngAfterContentInit(): void {
230
242
  this.rebuildRegistry();
231
243
  this.initializeState();
232
- this.screenChildrenSubscription = this.screenChildren.changes.subscribe(() => {
233
- this.rebuildRegistry();
234
- });
244
+ this.screenChildrenSubscription = this.screenChildren.changes.subscribe(
245
+ () => {
246
+ this.rebuildRegistry();
247
+ },
248
+ );
235
249
  }
236
250
 
237
251
  ngOnDestroy(): void {
@@ -261,24 +275,36 @@ export class Stack implements AfterContentInit, OnDestroy, INavigatorHandle {
261
275
 
262
276
  private initializeState(): void {
263
277
  if (this.stateSignal() !== undefined) return;
264
- const initialRouteName = this.initialRouteName ?? this.registry.keys().next().value;
278
+ const initialRouteName =
279
+ this.initialRouteName ?? this.registry.keys().next().value;
265
280
  if (initialRouteName === undefined) {
266
281
  dlog('Stack: no <ng-template symbioteScreen> children registered');
267
282
  this.stateSignal.set(
268
- createInitialNavigatorState({ key: this.routeIdPrefix, name: '', params: undefined }),
283
+ createInitialNavigatorState({
284
+ key: this.routeIdPrefix,
285
+ name: '',
286
+ params: undefined,
287
+ }),
269
288
  );
270
289
  return;
271
290
  }
272
291
  this.stateSignal.set(
273
292
  createInitialNavigatorState(
274
- this.createRoute(initialRouteName, this.registry.get(initialRouteName)?.initialParams),
293
+ this.createRoute(
294
+ initialRouteName,
295
+ this.registry.get(initialRouteName)?.initialParams,
296
+ ),
275
297
  ),
276
298
  );
277
299
  }
278
300
 
279
301
  private createRoute(name: string, params: unknown): IRoute<unknown> {
280
302
  this.routeSequence += 1;
281
- return { key: `${this.routeIdPrefix}-${name}-${this.routeSequence}`, name, params };
303
+ return {
304
+ key: `${this.routeIdPrefix}-${name}-${this.routeSequence}`,
305
+ name,
306
+ params,
307
+ };
282
308
  }
283
309
 
284
310
  private dispatch(action: INavigatorAction): void {
@@ -361,7 +387,8 @@ export class Stack implements AfterContentInit, OnDestroy, INavigatorHandle {
361
387
  isAndroid: Platform.OS === 'android',
362
388
  screenPassthrough: {
363
389
  [SCREEN_ON_DISMISSED]: () => this.dispatch({ type: 'pop', count: 1 }),
364
- [SCREEN_ON_HEADER_BACK_BUTTON_CLICKED]: () => this.dispatch({ type: 'pop', count: 1 }),
390
+ [SCREEN_ON_HEADER_BACK_BUTTON_CLICKED]: () =>
391
+ this.dispatch({ type: 'pop', count: 1 }),
365
392
  [SCREEN_ON_APPEAR]: () => {
366
393
  dlog(`Stack: route "${route.name}" appeared (focus)`);
367
394
  this.emitterFor(route.key).emit(NAVIGATION_EVENT_FOCUS);
@@ -380,14 +407,19 @@ export class Stack implements AfterContentInit, OnDestroy, INavigatorHandle {
380
407
  }
381
408
 
382
409
  outerScreenIsModal(route: IRoute<unknown>, index: number): boolean {
383
- return this.planFor(route, index).screenViewName === RNS_MODAL_SCREEN_VIEW_NAME;
410
+ return (
411
+ this.planFor(route, index).screenViewName === RNS_MODAL_SCREEN_VIEW_NAME
412
+ );
384
413
  }
385
414
 
386
415
  isInModal(route: IRoute<unknown>, index: number): boolean {
387
416
  return this.planFor(route, index).inModal;
388
417
  }
389
418
 
390
- screenHostProps(route: IRoute<unknown>, index: number): Record<string, unknown> {
419
+ screenHostProps(
420
+ route: IRoute<unknown>,
421
+ index: number,
422
+ ): Record<string, unknown> {
391
423
  return this.planFor(route, index).screenProps;
392
424
  }
393
425
 
@@ -395,16 +427,25 @@ export class Stack implements AfterContentInit, OnDestroy, INavigatorHandle {
395
427
  return { style: resolveHeaderInModalStackStyle() };
396
428
  }
397
429
 
398
- innerScreenProps(route: IRoute<unknown>, index: number): Record<string, unknown> {
430
+ innerScreenProps(
431
+ route: IRoute<unknown>,
432
+ index: number,
433
+ ): Record<string, unknown> {
399
434
  const plan = this.planFor(route, index);
400
435
  return { style: plan.innerScreenStyle, activityState: plan.activityState };
401
436
  }
402
437
 
403
- contentWrapperProps(route: IRoute<unknown>, index: number): Record<string, unknown> {
438
+ contentWrapperProps(
439
+ route: IRoute<unknown>,
440
+ index: number,
441
+ ): Record<string, unknown> {
404
442
  return this.planFor(route, index).contentWrapperProps;
405
443
  }
406
444
 
407
- headerConfigProps(route: IRoute<unknown>, index: number): Record<string, unknown> {
445
+ headerConfigProps(
446
+ route: IRoute<unknown>,
447
+ index: number,
448
+ ): Record<string, unknown> {
408
449
  return this.planFor(route, index).headerConfig.props;
409
450
  }
410
451
 
@@ -412,11 +453,16 @@ export class Stack implements AfterContentInit, OnDestroy, INavigatorHandle {
412
453
  return this.mergedOptionsFor(route).headerSearchBarOptions !== undefined;
413
454
  }
414
455
 
415
- searchBarRef(route: IRoute<unknown>): { current: ISearchBarCommands | null } | undefined {
456
+ searchBarRef(
457
+ route: IRoute<unknown>,
458
+ ): { current: ISearchBarCommands | null } | undefined {
416
459
  return this.mergedOptionsFor(route).headerSearchBarOptions?.ref;
417
460
  }
418
461
 
419
- searchBarProps(route: IRoute<unknown>, index: number): Record<string, unknown> {
462
+ searchBarProps(
463
+ route: IRoute<unknown>,
464
+ index: number,
465
+ ): Record<string, unknown> {
420
466
  return this.planFor(route, index).searchBarProps ?? {};
421
467
  }
422
468
  }
@@ -12,7 +12,9 @@ export type ITabScreenOptionsArgs = {
12
12
  navigation: ITabNavigatorHandle;
13
13
  };
14
14
 
15
- export type ITabScreenOptionsResolver = (props: ITabScreenOptionsArgs) => ITabOptions;
15
+ export type ITabScreenOptionsResolver = (
16
+ props: ITabScreenOptionsArgs,
17
+ ) => ITabOptions;
16
18
 
17
19
  @Directive({
18
20
  selector: 'ng-template[symbioteTabScreen]',
@@ -66,7 +66,12 @@ let tabInstanceCounter = 0;
66
66
  @Component({
67
67
  selector: 'Tab',
68
68
  standalone: true,
69
- imports: [NgComponentOutlet, NavigationScopeDirective, DescriptorOutlet, View],
69
+ imports: [
70
+ NgComponentOutlet,
71
+ NavigationScopeDirective,
72
+ DescriptorOutlet,
73
+ View,
74
+ ],
70
75
  changeDetection: ChangeDetectionStrategy.OnPush,
71
76
  template: `
72
77
  <View [style]="rootStyle">
@@ -101,7 +106,8 @@ export class Tab implements AfterContentInit, OnDestroy, ITabNavigatorHandle {
101
106
  // Keyed by name -> the LIVE TabScreenDirective instance - see stack.ts's matching comment for
102
107
  // why a snapshot copy would go stale on an in-place `[options]`/`[component]` change.
103
108
  private readonly registry = new Map<string, TabScreenDirective>();
104
- private tabScreenChildrenSubscription: { unsubscribe: () => void } | undefined;
109
+ private tabScreenChildrenSubscription:
110
+ { unsubscribe: () => void } | undefined;
105
111
 
106
112
  private readonly stateSignal = signal<ITabRouterState | undefined>(undefined);
107
113
  readonly state = this.stateSignal.asReadonly();
@@ -116,9 +122,10 @@ export class Tab implements AfterContentInit, OnDestroy, ITabNavigatorHandle {
116
122
 
117
123
  ngAfterContentInit(): void {
118
124
  this.syncRegistry();
119
- this.tabScreenChildrenSubscription = this.tabScreenChildren.changes.subscribe(() => {
120
- this.syncRegistry();
121
- });
125
+ this.tabScreenChildrenSubscription =
126
+ this.tabScreenChildren.changes.subscribe(() => {
127
+ this.syncRegistry();
128
+ });
122
129
  }
123
130
 
124
131
  ngOnDestroy(): void {
@@ -150,7 +157,8 @@ export class Tab implements AfterContentInit, OnDestroy, ITabNavigatorHandle {
150
157
  private syncRegistry(): void {
151
158
  this.rebuildRegistry();
152
159
  const routes = this.routesFromRegistry();
153
- if (routes.length === 0) dlog('Tab: no <ng-template symbioteTabScreen> children registered');
160
+ if (routes.length === 0)
161
+ dlog('Tab: no <ng-template symbioteTabScreen> children registered');
154
162
  const current = this.stateSignal();
155
163
  if (current !== undefined) {
156
164
  this.stateSignal.set(reconcileTabRoutes(current, routes));
@@ -168,9 +176,15 @@ export class Tab implements AfterContentInit, OnDestroy, ITabNavigatorHandle {
168
176
  this.stateSignal.set(tabRouterReducer(current, action));
169
177
  }
170
178
 
171
- private resolveTabOptions(entry: TabScreenDirective, route: IRoute<unknown>): ITabOptions {
179
+ private resolveTabOptions(
180
+ entry: TabScreenDirective,
181
+ route: IRoute<unknown>,
182
+ ): ITabOptions {
172
183
  const props: ITabScreenOptionsArgs = { route, navigation: this };
173
- const own = typeof entry.options === 'function' ? entry.options(props) : entry.options;
184
+ const own =
185
+ typeof entry.options === 'function'
186
+ ? entry.options(props)
187
+ : entry.options;
174
188
  return { ...this.screenOptions, ...own };
175
189
  }
176
190
 
@@ -207,8 +221,12 @@ export class Tab implements AfterContentInit, OnDestroy, ITabNavigatorHandle {
207
221
  focusedRouteEmitter(): INavigationEmitter {
208
222
  const key = this.focusedRoute()?.key;
209
223
  return untracked(() => {
210
- if (key === this.currentEmitterKey && this.currentEmitter) return this.currentEmitter;
211
- const { blurKey, focusKey } = diffFocusedRoute(this.currentEmitterKey, key);
224
+ if (key === this.currentEmitterKey && this.currentEmitter)
225
+ return this.currentEmitter;
226
+ const { blurKey, focusKey } = diffFocusedRoute(
227
+ this.currentEmitterKey,
228
+ key,
229
+ );
212
230
  if (blurKey !== undefined && this.currentEmitter) {
213
231
  dlog('Tab: previous route blurred');
214
232
  this.currentEmitter.emit(NAVIGATION_EVENT_BLUR);
@@ -257,12 +275,18 @@ export class Tab implements AfterContentInit, OnDestroy, ITabNavigatorHandle {
257
275
  });
258
276
 
259
277
  const focusedRoute = this.focusedRoute();
260
- const focusedEntry = focusedRoute ? this.registry.get(focusedRoute.name) : undefined;
278
+ const focusedEntry = focusedRoute
279
+ ? this.registry.get(focusedRoute.name)
280
+ : undefined;
261
281
  const focusedOptions =
262
282
  focusedEntry && focusedRoute
263
283
  ? this.resolveTabOptions(focusedEntry, focusedRoute)
264
284
  : this.screenOptions;
265
285
 
266
- return renderTabBar({ items, style: focusedOptions?.tabBarStyle, passthrough: {} });
286
+ return renderTabBar({
287
+ items,
288
+ style: focusedOptions?.tabBarStyle,
289
+ passthrough: {},
290
+ });
267
291
  }
268
292
  }
@@ -11,12 +11,14 @@ export const RNS_SCREEN_VIEW_NAME = 'RNSScreen';
11
11
  // render-stack.ts for why a formSheet/modal screen MUST use this different Fabric name.
12
12
  export const RNS_MODAL_SCREEN_VIEW_NAME = 'RNSModalScreen';
13
13
  export const RNS_SCREEN_STACK_VIEW_NAME = 'RNSScreenStack';
14
- export const RNS_SCREEN_STACK_HEADER_CONFIG_VIEW_NAME = 'RNSScreenStackHeaderConfig';
14
+ export const RNS_SCREEN_STACK_HEADER_CONFIG_VIEW_NAME =
15
+ 'RNSScreenStackHeaderConfig';
15
16
  // The header-subview leaf react-native-screens itself uses to host arbitrary framework children
16
17
  // (left/right/back/title/center/searchBar) inside the header. Registered here for ground-truth
17
18
  // parity, but unconsumed by any fold in v1 scope: renderHeaderConfig's Descriptor never takes
18
19
  // framework children (see render-stack.ts), so mounting a subview child is left to the adapter.
19
- export const RNS_SCREEN_STACK_HEADER_SUBVIEW_VIEW_NAME = 'RNSScreenStackHeaderSubview';
20
+ export const RNS_SCREEN_STACK_HEADER_SUBVIEW_VIEW_NAME =
21
+ 'RNSScreenStackHeaderSubview';
20
22
  export const RNS_SEARCH_BAR_VIEW_NAME = 'RNSSearchBar';
21
23
  // react-native-screens' own JS layer (ScreenStackItem -> DebugContainer -> ScreenContentWrapper)
22
24
  // ALWAYS wraps a screen's content in this native view, `collapsable: false` so Fabric can never
@@ -43,7 +45,8 @@ export const STACK_ON_FINISH_TRANSITIONING = 'onFinishTransitioning';
43
45
  // plain bar-button-item press, and a press on a (possibly nested) menu action of a 'menu'-type
44
46
  // bar-button item.
45
47
  export const HEADER_ON_PRESS_BAR_BUTTON_ITEM = 'onPressHeaderBarButtonItem';
46
- export const HEADER_ON_PRESS_BAR_BUTTON_MENU_ITEM = 'onPressHeaderBarButtonMenuItem';
48
+ export const HEADER_ON_PRESS_BAR_BUTTON_MENU_ITEM =
49
+ 'onPressHeaderBarButtonMenuItem';
47
50
 
48
51
  // RNSSearchBar event prop keys (all DirectEventHandler in the codegen spec). onClose/onOpen are
49
52
  // Android-only per the codegen spec's own comment.