@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
@@ -65,7 +65,10 @@ import {
65
65
  View,
66
66
  WindowDimensionsService,
67
67
  } from '@symbiote-native/angular';
68
- import type { IDescriptor, IDescriptorChild } from '@symbiote-native/components';
68
+ import type {
69
+ IDescriptor,
70
+ IDescriptorChild,
71
+ } from '@symbiote-native/components';
69
72
  import {
70
73
  DRAWER_DEFAULT_OVERLAY_COLOR,
71
74
  NAVIGATION_EVENT_BLUR,
@@ -132,7 +135,10 @@ let drawerInstanceCounter = 0;
132
135
  @for (slot of slotOrder(); track slot) {
133
136
  @switch (slot) {
134
137
  @case ('content') {
135
- <AnimatedView [style]="contentStyle()" [animatedProps]="slotAnimatedProps('content')">
138
+ <AnimatedView
139
+ [style]="contentStyle()"
140
+ [animatedProps]="slotAnimatedProps('content')"
141
+ >
136
142
  @if (focusedRoute(); as route) {
137
143
  @if (componentForRoute(route); as component) {
138
144
  <ng-container
@@ -153,7 +159,10 @@ let drawerInstanceCounter = 0;
153
159
  />
154
160
  }
155
161
  @case ('panel') {
156
- <AnimatedView [style]="panelStyle()" [animatedProps]="slotAnimatedProps('panel')">
162
+ <AnimatedView
163
+ [style]="panelStyle()"
164
+ [animatedProps]="slotAnimatedProps('panel')"
165
+ >
157
166
  @if (drawerContentTemplate) {
158
167
  <ng-container
159
168
  *ngTemplateOutlet="
@@ -170,7 +179,9 @@ let drawerInstanceCounter = 0;
170
179
  }
171
180
  `,
172
181
  })
173
- export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNavigatorHandle {
182
+ export class Drawer
183
+ implements AfterContentInit, OnChanges, OnDestroy, IDrawerNavigatorHandle
184
+ {
174
185
  @ContentChildren(DrawerScreenDirective)
175
186
  private readonly drawerScreenChildren!: QueryList<DrawerScreenDirective>;
176
187
  @ContentChild('drawerContent', { read: TemplateRef })
@@ -187,15 +198,19 @@ export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNa
187
198
  @Input() swipeMinDistance?: number;
188
199
  @Input() swipeMinVelocity?: number;
189
200
 
190
- private readonly windowDimensions = inject(WindowDimensionsService).dimensions;
201
+ private readonly windowDimensions = inject(WindowDimensionsService)
202
+ .dimensions;
191
203
 
192
204
  private readonly routeIdPrefix = `drawer-${(drawerInstanceCounter += 1)}`;
193
205
  // Keyed by name -> the LIVE DrawerScreenDirective instance - see stack.ts's matching comment
194
206
  // for why a snapshot copy would go stale on an in-place `[options]`/`[component]` change.
195
207
  private readonly registry = new Map<string, DrawerScreenDirective>();
196
- private drawerScreenChildrenSubscription: { unsubscribe: () => void } | undefined;
208
+ private drawerScreenChildrenSubscription:
209
+ { unsubscribe: () => void } | undefined;
197
210
 
198
- private readonly stateSignal = signal<IDrawerRouterState | undefined>(undefined);
211
+ private readonly stateSignal = signal<IDrawerRouterState | undefined>(
212
+ undefined,
213
+ );
199
214
  readonly state = this.stateSignal.asReadonly();
200
215
 
201
216
  private currentEmitterKey: string | undefined;
@@ -260,9 +275,16 @@ export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNa
260
275
  dlog('Drawer: gesture grant');
261
276
  this.dragStartProgress = (this.stateSignal()?.isOpen ?? false) ? 1 : 0;
262
277
  },
263
- onPanResponderMove: (_event: ISymbioteEvent, gestureState: IPanResponderGestureState): void => {
278
+ onPanResponderMove: (
279
+ _event: ISymbioteEvent,
280
+ gestureState: IPanResponderGestureState,
281
+ ): void => {
264
282
  this.progress.setValue(
265
- resolveDragProgress(gestureState, this.dragStartProgress, this.optionsSnapshot()),
283
+ resolveDragProgress(
284
+ gestureState,
285
+ this.dragStartProgress,
286
+ this.optionsSnapshot(),
287
+ ),
266
288
  );
267
289
  },
268
290
  onPanResponderRelease: (
@@ -288,9 +310,10 @@ export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNa
288
310
  ngAfterContentInit(): void {
289
311
  this.rebuildRegistry();
290
312
  this.initializeState();
291
- this.drawerScreenChildrenSubscription = this.drawerScreenChildren.changes.subscribe(() => {
292
- this.rebuildRegistry();
293
- });
313
+ this.drawerScreenChildrenSubscription =
314
+ this.drawerScreenChildren.changes.subscribe(() => {
315
+ this.rebuildRegistry();
316
+ });
294
317
  }
295
318
 
296
319
  ngOnDestroy(): void {
@@ -318,7 +341,9 @@ export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNa
318
341
  const routes = this.routesFromRegistry();
319
342
  if (routes.length === 0)
320
343
  dlog('Drawer: no <ng-template symbioteDrawerScreen> children registered');
321
- this.stateSignal.set(createInitialDrawerRouterState(routes, this.initialRouteName));
344
+ this.stateSignal.set(
345
+ createInitialDrawerRouterState(routes, this.initialRouteName),
346
+ );
322
347
  }
323
348
 
324
349
  private dispatch(action: IDrawerRouterAction): void {
@@ -343,10 +368,12 @@ export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNa
343
368
  // ngOnChanges (below) pushes every input change in here instead, which drawerRoot/slotsMap CAN
344
369
  // track correctly since reading a signal (unlike a plain field) IS visible to computed()'s
345
370
  // dependency collection.
346
- private readonly optionsSnapshot = signal<IDrawerOptions>(this.buildOptionsSnapshot());
347
- private readonly drawerStyleSnapshot = signal<IStyleProp<IViewStyle> | undefined>(
348
- this.drawerStyle,
371
+ private readonly optionsSnapshot = signal<IDrawerOptions>(
372
+ this.buildOptionsSnapshot(),
349
373
  );
374
+ private readonly drawerStyleSnapshot = signal<
375
+ IStyleProp<IViewStyle> | undefined
376
+ >(this.drawerStyle);
350
377
 
351
378
  ngOnChanges(): void {
352
379
  this.optionsSnapshot.set(this.buildOptionsSnapshot());
@@ -384,10 +411,13 @@ export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNa
384
411
  private readonly drawerRoot = computed<IDescriptor>(() =>
385
412
  renderDrawer(
386
413
  {
387
- overlayColor: this.optionsSnapshot().overlayColor ?? DRAWER_DEFAULT_OVERLAY_COLOR,
414
+ overlayColor:
415
+ this.optionsSnapshot().overlayColor ?? DRAWER_DEFAULT_OVERLAY_COLOR,
388
416
  drawerStyle: this.drawerStyleSnapshot(),
389
417
  contentPassthrough: {},
390
- overlayPassthrough: this.isAnimated() ? this.overlayResponderPassthrough() : {},
418
+ overlayPassthrough: this.isAnimated()
419
+ ? this.overlayResponderPassthrough()
420
+ : {},
391
421
  panelPassthrough: {},
392
422
  },
393
423
  this.optionsSnapshot(),
@@ -400,7 +430,8 @@ export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNa
400
430
  const map = new Map<IDrawerSlot, IDescriptor>();
401
431
  order.forEach((slot, index) => {
402
432
  const child: IDescriptorChild | undefined = root.children[index];
403
- if (child !== undefined && typeof child !== 'string') map.set(slot, child);
433
+ if (child !== undefined && typeof child !== 'string')
434
+ map.set(slot, child);
404
435
  });
405
436
  return map;
406
437
  });
@@ -433,7 +464,9 @@ export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNa
433
464
  animatedStyle: () => Record<string, unknown>,
434
465
  ): Record<string, unknown> {
435
466
  const base = this.slotsMap().get(slot)?.props.style;
436
- return this.isAnimated() ? flattenStyle([base, animatedStyle()]) : flattenStyle(base);
467
+ return this.isAnimated()
468
+ ? flattenStyle([base, animatedStyle()])
469
+ : flattenStyle(base);
437
470
  }
438
471
 
439
472
  contentStyle(): Record<string, unknown> {
@@ -452,7 +485,10 @@ export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNa
452
485
  overlayStyle(): Record<string, unknown> {
453
486
  return this.slotStyle('overlay', () => {
454
487
  const g = resolveDrawerGeometry(this.optionsSnapshot());
455
- const { opacity, translateX } = resolveDrawerSlotInterpolation(g, 'overlay');
488
+ const { opacity, translateX } = resolveDrawerSlotInterpolation(
489
+ g,
490
+ 'overlay',
491
+ );
456
492
  return {
457
493
  opacity: this.progress.interpolate(opacity),
458
494
  transform: [{ translateX: this.progress.interpolate(translateX) }],
@@ -513,8 +549,12 @@ export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNa
513
549
  focusedRouteEmitter(): INavigationEmitter {
514
550
  const key = this.focusedRoute()?.key;
515
551
  return untracked(() => {
516
- if (key === this.currentEmitterKey && this.currentEmitter) return this.currentEmitter;
517
- const { blurKey, focusKey } = diffFocusedRoute(this.currentEmitterKey, key);
552
+ if (key === this.currentEmitterKey && this.currentEmitter)
553
+ return this.currentEmitter;
554
+ const { blurKey, focusKey } = diffFocusedRoute(
555
+ this.currentEmitterKey,
556
+ key,
557
+ );
518
558
  if (blurKey !== undefined && this.currentEmitter) {
519
559
  dlog('Drawer: previous route blurred');
520
560
  this.currentEmitter.emit(NAVIGATION_EVENT_BLUR);
@@ -533,7 +573,9 @@ export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNa
533
573
  });
534
574
  }
535
575
 
536
- drawerContentContext(currentState: IDrawerRouterState): IDrawerContentContext {
576
+ drawerContentContext(
577
+ currentState: IDrawerRouterState,
578
+ ): IDrawerContentContext {
537
579
  const descriptors: IDrawerDescriptorMap = {};
538
580
  for (const route of currentState.routes) {
539
581
  const entry = this.registry.get(route.name);
@@ -543,6 +585,8 @@ export class Drawer implements AfterContentInit, OnChanges, OnDestroy, IDrawerNa
543
585
  navigation: this,
544
586
  };
545
587
  }
546
- return { $implicit: { state: currentState, descriptors, navigation: this } };
588
+ return {
589
+ $implicit: { state: currentState, descriptors, navigation: this },
590
+ };
547
591
  }
548
592
  }
@@ -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]',