@symbiote-native/navigation 2.0.3 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. package/README.md +42 -12
  2. package/build/angular/drawer/index.js +29 -13
  3. package/build/angular/index.d.ts +4 -4
  4. package/build/angular/injectors/inject-navigation.js +1 -1
  5. package/build/angular/navigation-scope.directive.js +1 -1
  6. package/build/angular/search-bar-ref.directive.js +1 -1
  7. package/build/angular/stack/index.d.ts +2 -0
  8. package/build/angular/stack/index.js +43 -9
  9. package/build/angular/tabs/index.d.ts +1 -1
  10. package/build/angular/tabs/index.js +40 -13
  11. package/build/core/drawer-options/index.js +19 -5
  12. package/build/core/drawer-router-state/index.js +8 -2
  13. package/build/core/guards.js +3 -1
  14. package/build/core/header-bar-buttons.js +14 -4
  15. package/build/core/index.d.ts +6 -5
  16. package/build/core/index.js +7 -3
  17. package/build/core/navigator-state/index.js +12 -4
  18. package/build/core/render-drawer.js +4 -1
  19. package/build/core/render-stack.js +13 -4
  20. package/build/core/screen-options.js +1 -1
  21. package/build/core/search-bar-commands.js +1 -1
  22. package/build/core/stack-router-state/index.d.ts +2 -0
  23. package/build/core/stack-router-state/index.js +44 -0
  24. package/build/core/state-persistence/index.js +6 -2
  25. package/build/core/tab-router-state/index.d.ts +1 -0
  26. package/build/core/tab-router-state/index.js +27 -2
  27. package/build/react/drawer/index.js +59 -26
  28. package/build/react/index.d.ts +7 -7
  29. package/build/react/index.js +2 -2
  30. package/build/react/stack/index.js +47 -10
  31. package/build/react/tabs/index.js +36 -12
  32. package/build/solid/drawer/index.d.ts +22 -0
  33. package/build/solid/drawer/index.js +394 -0
  34. package/build/solid/drawer-screen-props.d.ts +13 -0
  35. package/build/solid/drawer-screen-props.js +2 -0
  36. package/build/solid/host.d.ts +2 -0
  37. package/build/solid/host.js +23 -0
  38. package/build/solid/index.d.ts +19 -0
  39. package/build/solid/index.js +24 -0
  40. package/build/solid/linking.d.ts +3 -0
  41. package/build/solid/linking.js +55 -0
  42. package/build/solid/navigation-context.d.ts +13 -0
  43. package/build/solid/navigation-context.js +37 -0
  44. package/build/solid/primitives/create-focus-effect.d.ts +1 -0
  45. package/build/solid/primitives/create-focus-effect.js +30 -0
  46. package/build/solid/primitives/create-is-focused.d.ts +2 -0
  47. package/build/solid/primitives/create-is-focused.js +31 -0
  48. package/build/solid/primitives/create-navigation-state.d.ts +3 -0
  49. package/build/solid/primitives/create-navigation-state.js +30 -0
  50. package/build/solid/primitives/index.d.ts +12 -0
  51. package/build/solid/primitives/index.js +8 -0
  52. package/build/solid/primitives/use-drawer-navigation.d.ts +5 -0
  53. package/build/solid/primitives/use-drawer-navigation.js +15 -0
  54. package/build/solid/primitives/use-navigation.d.ts +8 -0
  55. package/build/solid/primitives/use-navigation.js +23 -0
  56. package/build/solid/primitives/use-route.d.ts +3 -0
  57. package/build/solid/primitives/use-route.js +14 -0
  58. package/build/solid/primitives/use-stack-navigation.d.ts +5 -0
  59. package/build/solid/primitives/use-stack-navigation.js +19 -0
  60. package/build/solid/primitives/use-tab-navigation.d.ts +5 -0
  61. package/build/solid/primitives/use-tab-navigation.js +15 -0
  62. package/build/solid/screen-props.d.ts +19 -0
  63. package/build/solid/screen-props.js +9 -0
  64. package/build/solid/screen-registry.d.ts +31 -0
  65. package/build/solid/screen-registry.js +86 -0
  66. package/build/solid/screen.d.ts +6 -0
  67. package/build/solid/screen.js +69 -0
  68. package/build/solid/stack/index.d.ts +15 -0
  69. package/build/solid/stack/index.js +348 -0
  70. package/build/solid/tab-screen-props.d.ts +13 -0
  71. package/build/solid/tab-screen-props.js +3 -0
  72. package/build/solid/tabs/index.d.ts +14 -0
  73. package/build/solid/tabs/index.js +228 -0
  74. package/build/svelte/attachments.d.ts +5 -0
  75. package/build/svelte/attachments.js +61 -0
  76. package/build/svelte/descriptor-subtree.d.ts +3 -0
  77. package/build/svelte/descriptor-subtree.js +39 -0
  78. package/build/svelte/drawer/drawer-props.d.ts +14 -0
  79. package/build/svelte/drawer/drawer-props.js +3 -0
  80. package/build/svelte/drawer/index.d.ts +10 -0
  81. package/build/svelte/drawer/index.js +6 -0
  82. package/build/svelte/drawer/index.svelte +559 -0
  83. package/build/svelte/drawer/index.svelte.d.ts +10 -0
  84. package/build/svelte/drawer-screen-props.d.ts +13 -0
  85. package/build/svelte/drawer-screen-props.js +4 -0
  86. package/build/svelte/drawer-screen.svelte +25 -0
  87. package/build/svelte/drawer-screen.svelte.d.ts +4 -0
  88. package/build/svelte/index.d.ts +21 -0
  89. package/build/svelte/index.js +20 -0
  90. package/build/svelte/linking.svelte.d.ts +2 -0
  91. package/build/svelte/linking.svelte.js +55 -0
  92. package/build/svelte/navigation-context.d.ts +14 -0
  93. package/build/svelte/navigation-context.js +44 -0
  94. package/build/svelte/navigation-scope.svelte +27 -0
  95. package/build/svelte/navigation-scope.svelte.d.ts +9 -0
  96. package/build/svelte/registry-host.d.ts +1 -0
  97. package/build/svelte/registry-host.js +26 -0
  98. package/build/svelte/runes/index.d.ts +12 -0
  99. package/build/svelte/runes/index.js +12 -0
  100. package/build/svelte/runes/use-drawer-navigation.svelte.d.ts +6 -0
  101. package/build/svelte/runes/use-drawer-navigation.svelte.js +19 -0
  102. package/build/svelte/runes/use-focus-effect.svelte.d.ts +1 -0
  103. package/build/svelte/runes/use-focus-effect.svelte.js +31 -0
  104. package/build/svelte/runes/use-is-focused.svelte.d.ts +3 -0
  105. package/build/svelte/runes/use-is-focused.svelte.js +34 -0
  106. package/build/svelte/runes/use-navigation-state.svelte.d.ts +4 -0
  107. package/build/svelte/runes/use-navigation-state.svelte.js +39 -0
  108. package/build/svelte/runes/use-navigation.svelte.d.ts +9 -0
  109. package/build/svelte/runes/use-navigation.svelte.js +26 -0
  110. package/build/svelte/runes/use-route.svelte.d.ts +4 -0
  111. package/build/svelte/runes/use-route.svelte.js +12 -0
  112. package/build/svelte/runes/use-stack-navigation.svelte.d.ts +6 -0
  113. package/build/svelte/runes/use-stack-navigation.svelte.js +22 -0
  114. package/build/svelte/runes/use-tab-navigation.svelte.d.ts +6 -0
  115. package/build/svelte/runes/use-tab-navigation.svelte.js +19 -0
  116. package/build/svelte/screen-props.d.ts +21 -0
  117. package/build/svelte/screen-props.js +6 -0
  118. package/build/svelte/screen-registry.d.ts +17 -0
  119. package/build/svelte/screen-registry.js +57 -0
  120. package/build/svelte/screen.svelte +28 -0
  121. package/build/svelte/screen.svelte.d.ts +4 -0
  122. package/build/svelte/stack/index.d.ts +14 -0
  123. package/build/svelte/stack/index.js +7 -0
  124. package/build/svelte/stack/index.svelte +261 -0
  125. package/build/svelte/stack/index.svelte.d.ts +15 -0
  126. package/build/svelte/stack/stack-props.d.ts +20 -0
  127. package/build/svelte/stack/stack-props.js +4 -0
  128. package/build/svelte/stack/stack-screen.svelte +205 -0
  129. package/build/svelte/stack/stack-screen.svelte.d.ts +4 -0
  130. package/build/svelte/tab-screen-props.d.ts +13 -0
  131. package/build/svelte/tab-screen-props.js +4 -0
  132. package/build/svelte/tab-screen.svelte +26 -0
  133. package/build/svelte/tab-screen.svelte.d.ts +4 -0
  134. package/build/svelte/tabs/index.d.ts +8 -0
  135. package/build/svelte/tabs/index.js +6 -0
  136. package/build/svelte/tabs/index.svelte +269 -0
  137. package/build/svelte/tabs/index.svelte.d.ts +8 -0
  138. package/build/svelte/tabs/tab-props.d.ts +7 -0
  139. package/build/svelte/tabs/tab-props.js +3 -0
  140. package/build/vue/composables/use-navigation.js +5 -1
  141. package/build/vue/drawer/index.js +36 -13
  142. package/build/vue/index.d.ts +3 -3
  143. package/build/vue/navigation-context.js +1 -1
  144. package/build/vue/stack/index.js +43 -18
  145. package/build/vue/tabs/index.js +65 -39
  146. package/build-ngc/angular/drawer/index.js +40 -18
  147. package/build-ngc/angular/drawer/index.js.map +1 -1
  148. package/build-ngc/angular/drawer-screen.directive.js +3 -3
  149. package/build-ngc/angular/index.d.ts +4 -4
  150. package/build-ngc/angular/index.js.map +1 -1
  151. package/build-ngc/angular/injectors/inject-focus-effect.js.map +1 -1
  152. package/build-ngc/angular/injectors/inject-is-focused.js.map +1 -1
  153. package/build-ngc/angular/injectors/inject-navigation-state.js.map +1 -1
  154. package/build-ngc/angular/injectors/inject-navigation.js +1 -1
  155. package/build-ngc/angular/injectors/inject-navigation.js.map +1 -1
  156. package/build-ngc/angular/linking.js.map +1 -1
  157. package/build-ngc/angular/navigation-context.service.js +3 -3
  158. package/build-ngc/angular/navigation-context.service.js.map +1 -1
  159. package/build-ngc/angular/navigation-scope.directive.js +4 -4
  160. package/build-ngc/angular/navigation-scope.directive.js.map +1 -1
  161. package/build-ngc/angular/screen.directive.js +3 -3
  162. package/build-ngc/angular/screen.directive.js.map +1 -1
  163. package/build-ngc/angular/search-bar-ref.directive.js +4 -4
  164. package/build-ngc/angular/search-bar-ref.directive.js.map +1 -1
  165. package/build-ngc/angular/stack/index.d.ts +2 -0
  166. package/build-ngc/angular/stack/index.js +52 -14
  167. package/build-ngc/angular/stack/index.js.map +1 -1
  168. package/build-ngc/angular/tab-screen.directive.js +3 -3
  169. package/build-ngc/angular/tab-screen.directive.js.map +1 -1
  170. package/build-ngc/angular/tabs/index.d.ts +1 -1
  171. package/build-ngc/angular/tabs/index.js +43 -16
  172. package/build-ngc/angular/tabs/index.js.map +1 -1
  173. package/build-ngc/core/constants.js.map +1 -1
  174. package/build-ngc/core/drawer-options/index.js +19 -5
  175. package/build-ngc/core/drawer-options/index.js.map +1 -1
  176. package/build-ngc/core/drawer-router-state/index.js +8 -2
  177. package/build-ngc/core/drawer-router-state/index.js.map +1 -1
  178. package/build-ngc/core/guards.js +3 -1
  179. package/build-ngc/core/guards.js.map +1 -1
  180. package/build-ngc/core/header-bar-buttons.js +14 -4
  181. package/build-ngc/core/header-bar-buttons.js.map +1 -1
  182. package/build-ngc/core/index.d.ts +6 -5
  183. package/build-ngc/core/index.js +7 -3
  184. package/build-ngc/core/index.js.map +1 -1
  185. package/build-ngc/core/linking-config/index.js.map +1 -1
  186. package/build-ngc/core/navigation-events/index.js.map +1 -1
  187. package/build-ngc/core/navigator-handles/index.js.map +1 -1
  188. package/build-ngc/core/navigator-state/index.js +12 -4
  189. package/build-ngc/core/navigator-state/index.js.map +1 -1
  190. package/build-ngc/core/render-drawer.js +4 -1
  191. package/build-ngc/core/render-drawer.js.map +1 -1
  192. package/build-ngc/core/render-stack.js +13 -4
  193. package/build-ngc/core/render-stack.js.map +1 -1
  194. package/build-ngc/core/render-tabs.js.map +1 -1
  195. package/build-ngc/core/screen-options.js +1 -1
  196. package/build-ngc/core/screen-options.js.map +1 -1
  197. package/build-ngc/core/search-bar-commands.js +1 -1
  198. package/build-ngc/core/search-bar-commands.js.map +1 -1
  199. package/build-ngc/core/stack-router-state/index.d.ts +2 -0
  200. package/build-ngc/core/stack-router-state/index.js +45 -0
  201. package/build-ngc/core/stack-router-state/index.js.map +1 -0
  202. package/build-ngc/core/state-persistence/index.js +6 -2
  203. package/build-ngc/core/state-persistence/index.js.map +1 -1
  204. package/build-ngc/core/tab-router-state/index.d.ts +1 -0
  205. package/build-ngc/core/tab-router-state/index.js +27 -2
  206. package/build-ngc/core/tab-router-state/index.js.map +1 -1
  207. package/package.json +44 -16
  208. package/src/angular/drawer/index.ts +75 -29
  209. package/src/angular/index.ts +20 -4
  210. package/src/angular/injectors/inject-focus-effect.ts +8 -2
  211. package/src/angular/injectors/inject-is-focused.ts +6 -4
  212. package/src/angular/injectors/inject-navigation-state.ts +7 -4
  213. package/src/angular/injectors/inject-navigation.ts +12 -3
  214. package/src/angular/linking.ts +4 -1
  215. package/src/angular/navigation-context.service.ts +5 -2
  216. package/src/angular/navigation-scope.directive.ts +6 -2
  217. package/src/angular/screen.directive.ts +13 -3
  218. package/src/angular/search-bar-ref.directive.ts +10 -2
  219. package/src/angular/stack/index.ts +91 -24
  220. package/src/angular/tab-screen.directive.ts +3 -1
  221. package/src/angular/tabs/index.ts +54 -16
  222. package/src/core/constants.ts +6 -3
  223. package/src/core/drawer-options/index.ts +40 -13
  224. package/src/core/drawer-router-state/index.ts +11 -3
  225. package/src/core/guards.ts +3 -1
  226. package/src/core/header-bar-buttons.ts +41 -12
  227. package/src/core/index.ts +29 -5
  228. package/src/core/linking-config/index.ts +31 -10
  229. package/src/core/navigation-events/index.ts +8 -2
  230. package/src/core/navigator-handles/index.ts +8 -3
  231. package/src/core/navigator-props.ts +13 -4
  232. package/src/core/navigator-state/index.ts +24 -7
  233. package/src/core/render-drawer.ts +24 -9
  234. package/src/core/render-stack.ts +52 -13
  235. package/src/core/render-tabs.ts +24 -6
  236. package/src/core/screen-options.ts +15 -5
  237. package/src/core/search-bar-commands.ts +12 -3
  238. package/src/core/stack-router-state/index.ts +52 -0
  239. package/src/core/state-persistence/index.ts +13 -3
  240. package/src/core/tab-router-state/index.ts +35 -3
  241. package/src/react/drawer/index.ts +409 -330
  242. package/src/react/drawer-screen.ts +8 -2
  243. package/src/react/hooks/use-focus-effect.ts +8 -2
  244. package/src/react/hooks/use-is-focused.ts +6 -2
  245. package/src/react/hooks/use-navigation.ts +8 -2
  246. package/src/react/index.ts +37 -7
  247. package/src/react/linking.ts +4 -1
  248. package/src/react/navigation-context.ts +6 -2
  249. package/src/react/screen.ts +12 -3
  250. package/src/react/stack/index.ts +297 -220
  251. package/src/react/tab-screen.ts +3 -1
  252. package/src/react/tabs/index.ts +202 -139
  253. package/src/solid/drawer/index.ts +570 -0
  254. package/src/solid/drawer-screen-props.ts +24 -0
  255. package/src/solid/host.ts +29 -0
  256. package/src/solid/index.ts +87 -0
  257. package/src/solid/linking.ts +74 -0
  258. package/src/solid/navigation-context.ts +56 -0
  259. package/src/solid/primitives/create-focus-effect.ts +41 -0
  260. package/src/solid/primitives/create-is-focused.ts +41 -0
  261. package/src/solid/primitives/create-navigation-state.ts +44 -0
  262. package/src/solid/primitives/index.ts +12 -0
  263. package/src/solid/primitives/use-drawer-navigation.ts +25 -0
  264. package/src/solid/primitives/use-navigation.ts +44 -0
  265. package/src/solid/primitives/use-route.ts +18 -0
  266. package/src/solid/primitives/use-stack-navigation.ts +29 -0
  267. package/src/solid/primitives/use-tab-navigation.ts +25 -0
  268. package/src/solid/screen-props.ts +51 -0
  269. package/src/solid/screen-registry.ts +159 -0
  270. package/src/solid/screen.ts +80 -0
  271. package/src/solid/stack/index.ts +528 -0
  272. package/src/solid/tab-screen-props.ts +21 -0
  273. package/src/solid/tabs/index.ts +327 -0
  274. package/src/svelte/attachments.ts +70 -0
  275. package/src/svelte/descriptor-subtree.ts +51 -0
  276. package/src/svelte/drawer/drawer-props.ts +28 -0
  277. package/src/svelte/drawer/index.svelte +559 -0
  278. package/src/svelte/drawer/index.ts +10 -0
  279. package/src/svelte/drawer-screen-props.ts +26 -0
  280. package/src/svelte/drawer-screen.svelte +25 -0
  281. package/src/svelte/fabric-tree.test-helper.ts +95 -0
  282. package/src/svelte/index.ts +83 -0
  283. package/src/svelte/linking.svelte.ts +72 -0
  284. package/src/svelte/navigation-context.ts +62 -0
  285. package/src/svelte/navigation-scope.svelte +27 -0
  286. package/src/svelte/registry-host.ts +26 -0
  287. package/src/svelte/runes/index.ts +17 -0
  288. package/src/svelte/runes/use-drawer-navigation.svelte.ts +31 -0
  289. package/src/svelte/runes/use-focus-effect.svelte.ts +41 -0
  290. package/src/svelte/runes/use-is-focused.svelte.ts +38 -0
  291. package/src/svelte/runes/use-navigation-state.svelte.ts +48 -0
  292. package/src/svelte/runes/use-navigation.svelte.ts +49 -0
  293. package/src/svelte/runes/use-route.svelte.ts +16 -0
  294. package/src/svelte/runes/use-stack-navigation.svelte.ts +34 -0
  295. package/src/svelte/runes/use-tab-navigation.svelte.ts +29 -0
  296. package/src/svelte/screen-props.ts +50 -0
  297. package/src/svelte/screen-registry.ts +98 -0
  298. package/src/svelte/screen.svelte +28 -0
  299. package/src/svelte/stack/index.svelte +261 -0
  300. package/src/svelte/stack/index.ts +11 -0
  301. package/src/svelte/stack/stack-props.ts +31 -0
  302. package/src/svelte/stack/stack-screen.svelte +205 -0
  303. package/src/svelte/svelte-ambient.d.ts +6 -0
  304. package/src/svelte/svelte-compile.test-helper.ts +147 -0
  305. package/src/svelte/tab-screen-props.ts +22 -0
  306. package/src/svelte/tab-screen.svelte +26 -0
  307. package/src/svelte/tabs/index.svelte +269 -0
  308. package/src/svelte/tabs/index.ts +10 -0
  309. package/src/svelte/tabs/tab-props.ts +13 -0
  310. package/src/vue/composables/use-navigation-state.ts +7 -4
  311. package/src/vue/composables/use-navigation.ts +13 -3
  312. package/src/vue/drawer/index.ts +128 -41
  313. package/src/vue/drawer-screen.ts +12 -5
  314. package/src/vue/index.ts +15 -3
  315. package/src/vue/linking.ts +7 -2
  316. package/src/vue/navigation-context.ts +17 -5
  317. package/src/vue/screen.ts +7 -2
  318. package/src/vue/stack/index.ts +108 -43
  319. package/src/vue/tab-screen.ts +3 -1
  320. package/src/vue/tabs/index.ts +109 -68
@@ -13,7 +13,12 @@
13
13
  import { defineComponent, h, shallowRef, useId } from '@vue/runtime-core';
14
14
  import type { VNode } from '@vue/runtime-core';
15
15
  import { descriptorToVue, normalizeVueAttrs } from '@symbiote-native/vue';
16
- import { Platform, debugNodeId, dlog, isSymbioteNode } from '@symbiote-native/engine';
16
+ import {
17
+ Platform,
18
+ debugNodeId,
19
+ dlog,
20
+ isSymbioteNode,
21
+ } from '@symbiote-native/engine';
17
22
  import {
18
23
  NAVIGATION_EVENT_BLUR,
19
24
  NAVIGATION_EVENT_FOCUS,
@@ -35,6 +40,7 @@ import {
35
40
  createNavigationEmitter,
36
41
  isRecord,
37
42
  navigatorReducer,
43
+ reconcileStackRoutes,
38
44
  resolveScreenRenderPlan,
39
45
  resolveStackProps,
40
46
  } from '../../core';
@@ -47,7 +53,11 @@ import type {
47
53
  } from '../../core';
48
54
  import { NavigationScope, injectNavigationScope } from '../navigation-context';
49
55
  import { Screen } from '../screen';
50
- import type { IScreenOptionsArgs, IScreenProps, IVueScreenOptions } from '../screen';
56
+ import type {
57
+ IScreenOptionsArgs,
58
+ IScreenProps,
59
+ IVueScreenOptions,
60
+ } from '../screen';
51
61
 
52
62
  export type { INavigatorHandle } from '../../core';
53
63
 
@@ -61,7 +71,9 @@ export type IStackProps = {
61
71
  // backTitleVisible defaults to `true` on both platforms per the codegen spec's own default
62
72
  // (CT.WithDefault<boolean, 'true'>) - no ios/android divergence in v1 scope, so a single constant
63
73
  // stands in for the per-platform injection point ISliderPlatform-style adapters use elsewhere.
64
- const NAVIGATOR_PLATFORM: INavigatorPlatform = { defaultHeaderBackTitleVisible: true };
74
+ const NAVIGATOR_PLATFORM: INavigatorPlatform = {
75
+ defaultHeaderBackTitleVisible: true,
76
+ };
65
77
 
66
78
  type IScreenRegistryEntry = {
67
79
  component: IScreenProps['component'];
@@ -92,7 +104,9 @@ function asScreenOptionsOrResolver(value: unknown): IScreenProps['options'] {
92
104
  return asScreenOptions(value);
93
105
  }
94
106
 
95
- function collectRegistry(vnodes: readonly VNode[]): Map<string, IScreenRegistryEntry> {
107
+ function collectRegistry(
108
+ vnodes: readonly VNode[],
109
+ ): Map<string, IScreenRegistryEntry> {
96
110
  const registry = new Map<string, IScreenRegistryEntry>();
97
111
  for (const vnode of vnodes) {
98
112
  if (vnode.type !== Screen || !isRecord(vnode.props)) continue;
@@ -114,7 +128,9 @@ function resolveScreenOptions(
114
128
  screenOptions: IVueScreenOptions | undefined,
115
129
  ): IVueScreenOptions {
116
130
  const own =
117
- typeof entry.options === 'function' ? entry.options(screenOptionsArgs) : entry.options;
131
+ typeof entry.options === 'function'
132
+ ? entry.options(screenOptionsArgs)
133
+ : entry.options;
118
134
  return { ...screenOptions, ...own };
119
135
  }
120
136
 
@@ -167,25 +183,44 @@ const StackImpl = defineComponent<IStackProps>(
167
183
  });
168
184
  } else {
169
185
  initialState = createInitialNavigatorState(
170
- createRoute(initialRouteName, initialRegistry.get(initialRouteName)?.initialParams),
186
+ createRoute(
187
+ initialRouteName,
188
+ initialRegistry.get(initialRouteName)?.initialParams,
189
+ ),
171
190
  );
172
191
  }
173
192
 
174
193
  const state = shallowRef<INavigatorState>(initialState);
175
194
 
195
+ // The screen names the render pass below last saw in the slot. A PLAIN local, not a ref: it is
196
+ // a cache of what was just rendered, never a reactive source, so refreshing it from inside the
197
+ // render closure is not a state write during render (which would re-trigger the very render
198
+ // effect that produced it).
199
+ let registeredNames: readonly string[] = [];
200
+
201
+ // A <Stack.Screen> marker can vanish from the slot while its route is still in the pushed
202
+ // history, which would leave that entry with nothing to render (reconcileStackRoutes' header).
203
+ // Reconciling on READ rather than writing the ref keeps the repair out of the render pass; the
204
+ // dispatch below then persists it, so the next push builds on the pruned history.
205
+ function currentState(): INavigatorState {
206
+ return reconcileStackRoutes(state.value, registeredNames);
207
+ }
208
+
176
209
  function dispatch(action: Parameters<typeof navigatorReducer>[1]): void {
177
- state.value = navigatorReducer(state.value, action);
210
+ state.value = navigatorReducer(currentState(), action);
178
211
  }
179
212
 
180
213
  const handle: INavigatorHandle = {
181
- push: (name, params) => dispatch({ type: 'push', route: createRoute(name, params) }),
214
+ push: (name, params) =>
215
+ dispatch({ type: 'push', route: createRoute(name, params) }),
182
216
  pop: count => dispatch({ type: 'pop', count }),
183
217
  popToTop: () => dispatch({ type: 'popToTop' }),
184
218
  popTo: key => dispatch({ type: 'popTo', key }),
185
- replace: (name, params) => dispatch({ type: 'replace', route: createRoute(name, params) }),
219
+ replace: (name, params) =>
220
+ dispatch({ type: 'replace', route: createRoute(name, params) }),
186
221
  setParams: (params, key) => dispatch({ type: 'setParams', key, params }),
187
222
  reset: nextState => dispatch({ type: 'reset', state: nextState }),
188
- canGoBack: () => state.value.routes.length > 1,
223
+ canGoBack: () => currentState().routes.length > 1,
189
224
  };
190
225
  expose(handle);
191
226
 
@@ -195,18 +230,21 @@ const StackImpl = defineComponent<IStackProps>(
195
230
  // useEffect, which must be deferred past render to avoid a descendant setState-during-render):
196
231
  // Vue's provide/inject + shallowRef.value writes are safe to perform directly inside the
197
232
  // render closure below, so the broadcast happens right there, once per render.
198
- function broadcastState(): void {
199
- for (const route of state.value.routes) {
200
- emitterFor(route.key).emit(NAVIGATION_EVENT_STATE, state.value);
233
+ function broadcastState(current: INavigatorState): void {
234
+ for (const route of current.routes) {
235
+ emitterFor(route.key).emit(NAVIGATION_EVENT_STATE, current);
201
236
  }
202
237
  for (const routeKey of emitters.keys()) {
203
- if (!state.value.routes.some(route => route.key === routeKey)) emitters.delete(routeKey);
238
+ if (!current.routes.some(route => route.key === routeKey))
239
+ emitters.delete(routeKey);
204
240
  }
205
241
  }
206
242
 
207
243
  return () => {
208
- broadcastState();
209
244
  const registry = collectRegistry(slots.default?.() ?? []);
245
+ registeredNames = [...registry.keys()];
246
+ const current = currentState();
247
+ broadcastState(current);
210
248
  const screenOptions = asScreenOptions(attrs.screenOptions);
211
249
 
212
250
  // Investigation instrumentation (flicker-on-focus bug): STACK_ON_FINISH_TRANSITIONING is
@@ -221,21 +259,31 @@ const StackImpl = defineComponent<IStackProps>(
221
259
  },
222
260
  });
223
261
 
224
- const children = state.value.routes.map((route, index) => {
262
+ const children = current.routes.map((route, index) => {
225
263
  const entry = registry.get(route.name);
226
264
  if (entry === undefined) {
227
265
  dlog(`Stack: no screen registered for route name "${route.name}"`);
228
266
  return null;
229
267
  }
230
268
 
231
- const screenOptionsArgs: IScreenOptionsArgs = { route, navigation: handle };
232
- const mergedOptions = resolveScreenOptions(entry, screenOptionsArgs, screenOptions);
233
- const activityState = computeActivityState(index, state.value.routes.length);
269
+ const screenOptionsArgs: IScreenOptionsArgs = {
270
+ route,
271
+ navigation: handle,
272
+ };
273
+ const mergedOptions = resolveScreenOptions(
274
+ entry,
275
+ screenOptionsArgs,
276
+ screenOptions,
277
+ );
278
+ const activityState = computeActivityState(
279
+ index,
280
+ current.routes.length,
281
+ );
234
282
  // Investigation instrumentation (flicker-on-focus bug): fires on EVERY Stack render, not
235
283
  // just on transitions, so the log stream shows whether a route's activityState/index ever
236
284
  // changes outside of a push/pop dispatch. Kept behind DEBUG, never removed.
237
285
  dlog(
238
- `Stack: render route "${route.name}" index=${index}/${state.value.routes.length - 1} ` +
286
+ `Stack: render route "${route.name}" index=${index}/${current.routes.length - 1} ` +
239
287
  `activityState=${activityState} at t=${Date.now()}`,
240
288
  );
241
289
 
@@ -245,27 +293,36 @@ const StackImpl = defineComponent<IStackProps>(
245
293
  const plan = resolveScreenRenderPlan({
246
294
  screenId: route.key,
247
295
  index,
248
- routeCount: state.value.routes.length,
296
+ routeCount: current.routes.length,
249
297
  options: mergedOptions,
250
298
  platform: NAVIGATOR_PLATFORM,
251
299
  isAndroid: Platform.OS === 'android',
252
300
  screenPassthrough: {
253
301
  [SCREEN_ON_DISMISSED]: () => dispatch({ type: 'pop', count: 1 }),
254
- [SCREEN_ON_HEADER_BACK_BUTTON_CLICKED]: () => dispatch({ type: 'pop', count: 1 }),
302
+ [SCREEN_ON_HEADER_BACK_BUTTON_CLICKED]: () =>
303
+ dispatch({ type: 'pop', count: 1 }),
255
304
  // onAppear/onDisappear are the definitive visibility boundary (post-transition-
256
305
  // animation), so 'focus'/'blur' fire exactly once per transition; onWillAppear/
257
306
  // onWillDisappear fire BEFORE the animation runs, so wiring them to emit() too would
258
307
  // double-invoke useFocusEffect per transition - they only get a debug log here.
259
308
  [SCREEN_ON_WILL_APPEAR]: () =>
260
- dlog(`Stack: route "${route.name}" will appear at t=${Date.now()}`),
309
+ dlog(
310
+ `Stack: route "${route.name}" will appear at t=${Date.now()}`,
311
+ ),
261
312
  [SCREEN_ON_APPEAR]: () => {
262
- dlog(`Stack: route "${route.name}" appeared (focus) at t=${Date.now()}`);
313
+ dlog(
314
+ `Stack: route "${route.name}" appeared (focus) at t=${Date.now()}`,
315
+ );
263
316
  routeEmitter.emit(NAVIGATION_EVENT_FOCUS);
264
317
  },
265
318
  [SCREEN_ON_WILL_DISAPPEAR]: () =>
266
- dlog(`Stack: route "${route.name}" will disappear at t=${Date.now()}`),
319
+ dlog(
320
+ `Stack: route "${route.name}" will disappear at t=${Date.now()}`,
321
+ ),
267
322
  [SCREEN_ON_DISAPPEAR]: () => {
268
- dlog(`Stack: route "${route.name}" disappeared (blur) at t=${Date.now()}`);
323
+ dlog(
324
+ `Stack: route "${route.name}" disappeared (blur) at t=${Date.now()}`,
325
+ );
269
326
  routeEmitter.emit(NAVIGATION_EVENT_BLUR);
270
327
  },
271
328
  },
@@ -292,7 +349,8 @@ const StackImpl = defineComponent<IStackProps>(
292
349
  );
293
350
  const appRef = searchBarOptions.ref;
294
351
  if (appRef === undefined) return;
295
- appRef.value = node === null ? null : buildSearchBarHandle(() => node);
352
+ appRef.value =
353
+ node === null ? null : buildSearchBarHandle(() => node);
296
354
  },
297
355
  }
298
356
  : undefined,
@@ -317,22 +375,26 @@ const StackImpl = defineComponent<IStackProps>(
317
375
  // sizing (see RNS_SCREEN_CONTENT_WRAPPER_VIEW_NAME's comment in core/constants.ts). A
318
376
  // `push` screen doesn't need it, but a `formSheet` one is otherwise left with no content
319
377
  // ever attached natively.
320
- const content = h(RNS_SCREEN_CONTENT_WRAPPER_VIEW_NAME, plan.contentWrapperProps, [
321
- h(
322
- NavigationScope,
323
- {
324
- value: {
325
- route,
326
- navigation: handle,
327
- emitter: routeEmitter,
328
- parent: ambientScopeRef?.value,
378
+ const content = h(
379
+ RNS_SCREEN_CONTENT_WRAPPER_VIEW_NAME,
380
+ plan.contentWrapperProps,
381
+ [
382
+ h(
383
+ NavigationScope,
384
+ {
385
+ value: {
386
+ route,
387
+ navigation: handle,
388
+ emitter: routeEmitter,
389
+ parent: ambientScopeRef?.value,
390
+ },
329
391
  },
330
- },
331
- // No route/navigation props: the screen reads both through composables off the
332
- // NavigationScope provided just above (useRoute / useStackNavigation).
333
- () => h(entry.component),
334
- ),
335
- ]);
392
+ // No route/navigation props: the screen reads both through composables off the
393
+ // NavigationScope provided just above (useRoute / useStackNavigation).
394
+ () => h(entry.component),
395
+ ),
396
+ ],
397
+ );
336
398
 
337
399
  // react-native-screens' own Screen.tsx swaps in a DIFFERENT Fabric component
338
400
  // ('RNSModalScreen') for a modally-presented screen - its native updateLayoutMetrics:
@@ -356,7 +418,10 @@ const StackImpl = defineComponent<IStackProps>(
356
418
  // RNSScreen.mm treats an unset/inactive nested screen as not yet pushed,
357
419
  // leaving it parked at its pre-push transition position (off past the bottom
358
420
  // edge) instead of its real, presented frame.
359
- { style: plan.innerScreenStyle, activityState: plan.activityState },
421
+ {
422
+ style: plan.innerScreenStyle,
423
+ activityState: plan.activityState,
424
+ },
360
425
  [descriptorToVue(plan.headerConfig), content],
361
426
  ),
362
427
  ]),
@@ -14,7 +14,9 @@ export type ITabScreenOptionsArgs = {
14
14
  navigation: ITabNavigatorHandle;
15
15
  };
16
16
 
17
- export type ITabScreenOptionsResolver = (props: ITabScreenOptionsArgs) => ITabOptions;
17
+ export type ITabScreenOptionsResolver = (
18
+ props: ITabScreenOptionsArgs,
19
+ ) => ITabOptions;
18
20
 
19
21
  export type ITabScreenProps = {
20
22
  name: string;
@@ -1,8 +1,8 @@
1
1
  // Tab, the Vue lifecycle half. The focused-index router (tab-router-state) and the tab-bar
2
2
  // Descriptor builder (render-tabs) live in @symbiote-native/navigation core, shared verbatim with
3
- // the React/Angular adapters; here Vue supplies the lifecycle - a plain ref for the focused index
4
- // (Vue's twin of useReducer), useId for route-key generation, expose() for the jumpTo/setParams
5
- // handle - plus the descriptor bridge for the tab-bar leaf, exactly like Stack bridges its header
3
+ // the React/Angular adapters; here Vue supplies the lifecycle - a shallowRef for the dispatched
4
+ // router state (Vue's twin of useReducer), useId for route-key generation, expose() for the
5
+ // jumpTo/setParams handle - plus the descriptor bridge for the tab-bar leaf, like Stack's header
6
6
  // config (stack.ts). Unlike Stack, a bottom-tabs bar is a PURE-JS UI: it paints ordinary
7
7
  // `symbiote-view`/`symbiote-text` primitives via the shared render fn, so there is no
8
8
  // react-native-screens ViewConfig to register here - Tab needs no `../register` import.
@@ -11,11 +11,9 @@ import {
11
11
  defineComponent,
12
12
  h,
13
13
  nextTick,
14
- onMounted,
15
14
  onUnmounted,
16
15
  shallowRef,
17
16
  useId,
18
- watch,
19
17
  } from '@vue/runtime-core';
20
18
  import type { VNode } from '@vue/runtime-core';
21
19
  import { descriptorToVue, normalizeVueAttrs } from '@symbiote-native/vue';
@@ -28,6 +26,7 @@ import {
28
26
  diffFocusedRoute,
29
27
  isFocusedRoute,
30
28
  isRecord,
29
+ reconcileTabRoutes,
31
30
  renderTabBar,
32
31
  tabRouterReducer,
33
32
  } from '../../core';
@@ -37,6 +36,8 @@ import type {
37
36
  ITabBarItemView,
38
37
  ITabNavigatorHandle,
39
38
  ITabOptions,
39
+ ITabRouterAction,
40
+ ITabRouterState,
40
41
  } from '../../core';
41
42
  import { NavigationScope, injectNavigationScope } from '../navigation-context';
42
43
  import { TabScreen } from '../tab-screen';
@@ -78,7 +79,9 @@ function asTabOptionsOrResolver(value: unknown): ITabScreenProps['options'] {
78
79
  return asTabOptions(value);
79
80
  }
80
81
 
81
- function collectRegistry(vnodes: readonly VNode[]): Map<string, ITabRegistryEntry> {
82
+ function collectRegistry(
83
+ vnodes: readonly VNode[],
84
+ ): Map<string, ITabRegistryEntry> {
82
85
  const registry = new Map<string, ITabRegistryEntry>();
83
86
  for (const vnode of vnodes) {
84
87
  if (vnode.type !== TabScreen || !isRecord(vnode.props)) continue;
@@ -100,7 +103,9 @@ function resolveTabOptions(
100
103
  screenOptions: ITabOptions | undefined,
101
104
  ): ITabOptions {
102
105
  const own =
103
- typeof entry.options === 'function' ? entry.options(screenComponentProps) : entry.options;
106
+ typeof entry.options === 'function'
107
+ ? entry.options(screenComponentProps)
108
+ : entry.options;
104
109
  return { ...screenOptions, ...own };
105
110
  }
106
111
 
@@ -118,7 +123,9 @@ const TabImpl = defineComponent<ITabProps>(
118
123
  const ambientScopeRef = injectNavigationScope();
119
124
  const routeIdPrefix = useId();
120
125
 
121
- function buildRoutes(registry: Map<string, ITabRegistryEntry>): IRoute<unknown>[] {
126
+ function buildRoutes(
127
+ registry: Map<string, ITabRegistryEntry>,
128
+ ): IRoute<unknown>[] {
122
129
  return Array.from(registry.entries()).map(([name, entry]) => ({
123
130
  key: `${routeIdPrefix}-${name}`,
124
131
  name,
@@ -128,14 +135,36 @@ const TabImpl = defineComponent<ITabProps>(
128
135
 
129
136
  const initialRegistry = collectRegistry(slots.default?.() ?? []);
130
137
  const initialRoutes = buildRoutes(initialRegistry);
131
- if (initialRoutes.length === 0) dlog('Tab: no <Tab.Screen> children registered');
132
-
133
- const state = shallowRef(
134
- createInitialTabState(initialRoutes, asString(attrs.initialRouteName)),
135
- );
138
+ if (initialRoutes.length === 0)
139
+ dlog('Tab: no <Tab.Screen> children registered');
140
+
141
+ // A <Tab.Screen> can appear or disappear after mount (a marker behind a v-if, a data-driven
142
+ // screen list), so the route list is re-collected from the slot on every render and the
143
+ // DISPATCHED state is reconciled against it (reconcileTabRoutes, core - it preserves each
144
+ // surviving route's key and accumulated params and keeps focus on the same route NAME) rather
145
+ // than staying frozen at whatever setup() first saw. Slot vnodes are not reactive state, so
146
+ // the render fn is the only place that can observe the registry change: the reconciliation
147
+ // happens there, as a pure local, and never writes back - a state write inside a derivation is
148
+ // an error in Vue, not a style choice. `null` means nothing has dispatched yet, which is what
149
+ // keeps initialRouteName honored when the markers arrive after the first render.
150
+ const dispatchedState = shallowRef<ITabRouterState | null>(null);
151
+ // Non-reactive cache of the route list the last render derived: dispatch() reduces over the
152
+ // RECONCILED state, so it needs the same list that paint used. A ref here would be a reactive
153
+ // write from the render fn, exactly what the comment above rules out.
154
+ let latestRoutes: readonly IRoute<unknown>[] = initialRoutes;
155
+
156
+ function resolveState(routes: readonly IRoute<unknown>[]): ITabRouterState {
157
+ const dispatched = dispatchedState.value;
158
+ return dispatched === null
159
+ ? createInitialTabState(routes, asString(attrs.initialRouteName))
160
+ : reconcileTabRoutes(dispatched, routes);
161
+ }
136
162
 
137
- function dispatch(action: Parameters<typeof tabRouterReducer>[1]): void {
138
- state.value = tabRouterReducer(state.value, action);
163
+ function dispatch(action: ITabRouterAction): void {
164
+ dispatchedState.value = tabRouterReducer(
165
+ resolveState(latestRoutes),
166
+ action,
167
+ );
139
168
  }
140
169
 
141
170
  const jumpTo = (name: string, params?: unknown): void =>
@@ -146,16 +175,6 @@ const TabImpl = defineComponent<ITabProps>(
146
175
  const handle: ITabNavigatorHandle = { jumpTo, setParams };
147
176
  expose(handle);
148
177
 
149
- // Tab paints its own bar in pure JS - there is no native onAppear/onDisappear to hook (unlike
150
- // Stack's RNSScreen), so focus/blur is synthesized here: emit 'focus' once the newly-focused
151
- // route's content has mounted, 'blur' when it's about to be replaced or Tab itself unmounts.
152
- // Keyed on the route KEY (not the route object) so a setParams-only change (new route object,
153
- // same key) doesn't spuriously re-fire focus/blur - mirrors tabs.ts's React twin exactly,
154
- // just expressed as a `watch` over the focused key instead of a useEffect dependency array.
155
- function focusedKeyOf(current: typeof state.value): string | undefined {
156
- return current.routes[current.index]?.key;
157
- }
158
-
159
178
  // One emitter per route.key, created lazily and cached for the navigator's whole lifetime -
160
179
  // mirrors stack.ts's own `emitters` map. This decouples emitter IDENTITY (stable, looked up
161
180
  // by key, read by the render closure below when it builds a route's NavigationScope) from
@@ -179,45 +198,42 @@ const TabImpl = defineComponent<ITabProps>(
179
198
  // Stack's RNSScreen), so focus/blur is synthesized here: emit 'focus' once the newly-focused
180
199
  // route's content has mounted, 'blur' when it's about to be replaced or Tab itself unmounts.
181
200
  // Keyed on the route KEY (not the route object) so a setParams-only change (new route object,
182
- // same key) doesn't spuriously re-fire focus/blur - mirrors tabs.ts's React twin exactly,
183
- // just expressed as a `watch` over the focused key instead of a useEffect dependency array.
184
- let focusedRouteKey = focusedKeyOf(state.value);
185
-
186
- onMounted(() => {
187
- if (focusedRouteKey !== undefined) {
188
- dlog(`Tab: route "${focusedRouteKey}" focused at t=${Date.now()}`);
189
- emitterFor(focusedRouteKey).emit(NAVIGATION_EVENT_FOCUS);
190
- }
191
- });
192
-
201
+ // same key) doesn't spuriously re-fire focus/blur.
202
+ //
203
+ // Driven from the render pass rather than a `watch` because the focused key follows the
204
+ // SLOT-collected route list too - a marker disappearing can move focus, and slot vnodes are
205
+ // not reactive state, so no watcher source can see that; only a render can. The diff therefore
206
+ // lives where the state is derived, and stays a plain local, never a reactive write.
207
+ //
193
208
  // The bookkeeping (which key is focused) updates immediately so the render closure below
194
209
  // always reads the right route; the actual emit is deferred to nextTick(), which resolves
195
210
  // only after the CURRENT flush cycle fully drains - including the newly-focused screen's own
196
- // onMounted (subscribing its useIsFocused/useFocusEffect listeners). flush:'post' alone is
197
- // NOT enough here: it only guarantees this callback runs after THIS component's own render
198
- // effect, not after an arbitrary sibling post-flush job (a just-mounted child's onMounted)
199
- // that may be queued after it in the same batch - nextTick is the one API that waits for the
200
- // WHOLE cycle, not just this watcher's own slot in it.
201
- watch(
202
- () => focusedKeyOf(state.value),
203
- nextKey => {
204
- const { blurKey, focusKey } = diffFocusedRoute(focusedRouteKey, nextKey);
205
- if (blurKey === undefined && focusKey === undefined) return;
206
- focusedRouteKey = nextKey;
207
- nextTick(() => {
208
- if (blurKey !== undefined) {
209
- dlog(`Tab: route "${blurKey}" blurred at t=${Date.now()}`);
210
- emitterFor(blurKey).emit(NAVIGATION_EVENT_BLUR);
211
- }
212
- if (focusKey !== undefined) {
213
- dlog(`Tab: route "${focusKey}" focused at t=${Date.now()}`);
214
- emitterFor(focusKey).emit(NAVIGATION_EVENT_FOCUS);
215
- }
216
- });
217
- },
218
- );
211
+ // onMounted (subscribing its useIsFocused/useFocusEffect listeners). Emitting inline would
212
+ // reach zero subscribers; flush:'post' would not have been enough either, since it only
213
+ // orders a callback after THIS component's own render effect, not after an arbitrary sibling
214
+ // post-flush job (a just-mounted child's onMounted) queued after it in the same batch.
215
+ let focusedRouteKey: string | undefined;
216
+
217
+ function syncFocus(current: ITabRouterState): void {
218
+ const nextKey = current.routes[current.index]?.key;
219
+ const { blurKey, focusKey } = diffFocusedRoute(focusedRouteKey, nextKey);
220
+ if (blurKey === undefined && focusKey === undefined) return;
221
+ focusedRouteKey = nextKey;
222
+ nextTick(() => {
223
+ if (blurKey !== undefined) {
224
+ dlog(`Tab: route "${blurKey}" blurred at t=${Date.now()}`);
225
+ emitterFor(blurKey).emit(NAVIGATION_EVENT_BLUR);
226
+ }
227
+ if (focusKey !== undefined) {
228
+ dlog(`Tab: route "${focusKey}" focused at t=${Date.now()}`);
229
+ emitterFor(focusKey).emit(NAVIGATION_EVENT_FOCUS);
230
+ }
231
+ });
232
+ }
233
+
219
234
  onUnmounted(() => {
220
- if (focusedRouteKey !== undefined) emitterFor(focusedRouteKey).emit(NAVIGATION_EVENT_BLUR);
235
+ if (focusedRouteKey !== undefined)
236
+ emitterFor(focusedRouteKey).emit(NAVIGATION_EVENT_BLUR);
221
237
  });
222
238
 
223
239
  return () => {
@@ -226,17 +242,32 @@ const TabImpl = defineComponent<ITabProps>(
226
242
 
227
243
  if (registry.size === 0) dlog('Tab: no <Tab.Screen> children registered');
228
244
 
229
- const focusedRoute: IRoute<unknown> | undefined = state.value.routes[state.value.index];
245
+ const routes = buildRoutes(registry);
246
+ latestRoutes = routes;
247
+ const state = resolveState(routes);
248
+ syncFocus(state);
249
+
250
+ const focusedRoute: IRoute<unknown> | undefined =
251
+ state.routes[state.index];
230
252
 
231
- const items: ITabBarItemView[] = state.value.routes.map((route, index) => {
253
+ const items: ITabBarItemView[] = state.routes.map((route, index) => {
232
254
  const entry = registry.get(route.name);
233
- const focused = isFocusedRoute(index, state.value.index);
255
+ const focused = isFocusedRoute(index, state.index);
234
256
  if (entry === undefined) {
235
257
  dlog(`Tab: no screen registered for route name "${route.name}"`);
236
- return { key: route.key, focused, label: route.name, passthrough: {} };
258
+ return {
259
+ key: route.key,
260
+ focused,
261
+ label: route.name,
262
+ passthrough: {},
263
+ };
237
264
  }
238
265
 
239
- const options = resolveTabOptions(entry, { route, navigation: handle }, screenOptions);
266
+ const options = resolveTabOptions(
267
+ entry,
268
+ { route, navigation: handle },
269
+ screenOptions,
270
+ );
240
271
 
241
272
  return {
242
273
  key: route.key,
@@ -254,7 +285,9 @@ const TabImpl = defineComponent<ITabProps>(
254
285
  };
255
286
  });
256
287
 
257
- const focusedEntry = focusedRoute ? registry.get(focusedRoute.name) : undefined;
288
+ const focusedEntry = focusedRoute
289
+ ? registry.get(focusedRoute.name)
290
+ : undefined;
258
291
  const focusedOptions: ITabOptions | undefined =
259
292
  focusedEntry && focusedRoute
260
293
  ? resolveTabOptions(
@@ -265,7 +298,11 @@ const TabImpl = defineComponent<ITabProps>(
265
298
  : screenOptions;
266
299
 
267
300
  const tabBar = descriptorToVue(
268
- renderTabBar({ items, style: focusedOptions?.tabBarStyle, passthrough: {} }),
301
+ renderTabBar({
302
+ items,
303
+ style: focusedOptions?.tabBarStyle,
304
+ passthrough: {},
305
+ }),
269
306
  );
270
307
 
271
308
  // Only the focused route's screen is ever mounted (unlike Stack, which keeps every pushed
@@ -291,7 +328,11 @@ const TabImpl = defineComponent<ITabProps>(
291
328
  : null;
292
329
 
293
330
  return h('symbiote-view', { style: TAB_ROOT_STYLE }, [
294
- h('symbiote-view', { style: TAB_CONTENT_STYLE }, content === null ? [] : [content]),
331
+ h(
332
+ 'symbiote-view',
333
+ { style: TAB_CONTENT_STYLE },
334
+ content === null ? [] : [content],
335
+ ),
295
336
  tabBar,
296
337
  ]);
297
338
  };