@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
@@ -4,7 +4,10 @@
4
4
  // (`key`) is generated by the CALLER (the adapter, via React's useId or an equivalent) - the
5
5
  // reducer never invents identity itself, so it stays a pure (state, action) => state function.
6
6
 
7
- import { SCREEN_ACTIVITY_STATE_FOCUSED, SCREEN_ACTIVITY_STATE_INACTIVE } from '../constants';
7
+ import {
8
+ SCREEN_ACTIVITY_STATE_FOCUSED,
9
+ SCREEN_ACTIVITY_STATE_INACTIVE,
10
+ } from '../constants';
8
11
  import { mergeParams } from '../guards';
9
12
 
10
13
  export type IRoute<TParams = undefined> = Readonly<{
@@ -28,7 +31,9 @@ export type INavigatorAction =
28
31
  | { type: 'setParams'; key?: string; params: unknown }
29
32
  | { type: 'reset'; state: INavigatorState };
30
33
 
31
- export function createInitialNavigatorState(initialRoute: IRouteEntry): INavigatorState {
34
+ export function createInitialNavigatorState(
35
+ initialRoute: IRouteEntry,
36
+ ): INavigatorState {
32
37
  return { routes: [initialRoute] };
33
38
  }
34
39
 
@@ -50,7 +55,9 @@ export function navigatorReducer(
50
55
  }
51
56
 
52
57
  case 'popToTop':
53
- return state.routes.length <= 1 ? state : { routes: state.routes.slice(0, 1) };
58
+ return state.routes.length <= 1
59
+ ? state
60
+ : { routes: state.routes.slice(0, 1) };
54
61
 
55
62
  case 'popTo': {
56
63
  const index = state.routes.findIndex(route => route.key === action.key);
@@ -68,7 +75,8 @@ export function navigatorReducer(
68
75
  // `source`-defaults-to-focused-route behavior, minus the cross-navigator target routing this
69
76
  // single-navigator reducer has no need for.
70
77
  case 'setParams': {
71
- const targetKey = action.key ?? state.routes[state.routes.length - 1]?.key;
78
+ const targetKey =
79
+ action.key ?? state.routes[state.routes.length - 1]?.key;
72
80
  if (targetKey === undefined) return state;
73
81
  const index = state.routes.findIndex(route => route.key === targetKey);
74
82
  if (index === -1) return state;
@@ -78,7 +86,11 @@ export function navigatorReducer(
78
86
  params: mergeParams(target.params, action.params),
79
87
  };
80
88
  return {
81
- routes: [...state.routes.slice(0, index), nextRoute, ...state.routes.slice(index + 1)],
89
+ routes: [
90
+ ...state.routes.slice(0, index),
91
+ nextRoute,
92
+ ...state.routes.slice(index + 1),
93
+ ],
82
94
  };
83
95
  }
84
96
 
@@ -102,9 +114,14 @@ export function navigatorReducer(
102
114
  // an already-mounted NativeStack screen's activityState can never decrease via a plain prop
103
115
  // update ("activityState can only progress in NativeStack") - since no mounted route's index ever
104
116
  // exceeds routesLength - 1, this formula never produces a decrease across a push or pop.
105
- export function computeActivityState(index: number, routesLength: number): number {
117
+ export function computeActivityState(
118
+ index: number,
119
+ routesLength: number,
120
+ ): number {
106
121
  const focusedIndex = routesLength - 1;
107
- return index > focusedIndex ? SCREEN_ACTIVITY_STATE_INACTIVE : SCREEN_ACTIVITY_STATE_FOCUSED;
122
+ return index > focusedIndex
123
+ ? SCREEN_ACTIVITY_STATE_INACTIVE
124
+ : SCREEN_ACTIVITY_STATE_FOCUSED;
108
125
  }
109
126
 
110
127
  export function isTopRoute(index: number, routesLength: number): boolean {
@@ -32,7 +32,9 @@ export type IDrawerSlot = 'content' | 'overlay' | 'panel';
32
32
  // slide: panel, overlay, content (both panel and content animate; content stays visually on top).
33
33
  // permanent: NOT absolutely positioned (an ordinary flexDirection:'row' sidebar), so sibling
34
34
  // order IS the left-to-right screen position - it must follow drawerPosition.
35
- export function drawerChildOrder(options: IDrawerOptions): readonly IDrawerSlot[] {
35
+ export function drawerChildOrder(
36
+ options: IDrawerOptions,
37
+ ): readonly IDrawerSlot[] {
36
38
  const type = resolveDrawerType(options);
37
39
  switch (type) {
38
40
  case 'back':
@@ -89,12 +91,22 @@ function panelBaseStyle(options: IDrawerOptions): IViewStyle {
89
91
  };
90
92
  }
91
93
 
92
- export function renderDrawer(view: IDrawerViewProps, options: IDrawerOptions): IDescriptor {
94
+ export function renderDrawer(
95
+ view: IDrawerViewProps,
96
+ options: IDrawerOptions,
97
+ ): IDescriptor {
93
98
  const type = resolveDrawerType(options);
94
- const content = el('symbiote-view', { ...view.contentPassthrough, style: CONTENT_STYLE }, []);
99
+ const content = el(
100
+ 'symbiote-view',
101
+ { ...view.contentPassthrough, style: CONTENT_STYLE },
102
+ [],
103
+ );
95
104
  const panel = el(
96
105
  'symbiote-view',
97
- { ...view.panelPassthrough, style: [panelBaseStyle(options), view.drawerStyle] },
106
+ {
107
+ ...view.panelPassthrough,
108
+ style: [panelBaseStyle(options), view.drawerStyle],
109
+ },
98
110
  [],
99
111
  );
100
112
 
@@ -110,11 +122,14 @@ export function renderDrawer(view: IDrawerViewProps, options: IDrawerOptions): I
110
122
  );
111
123
  }
112
124
 
113
- const children = drawerChildOrder(options).reduce<IDescriptor[]>((acc, slot) => {
114
- const descriptor = slots[slot];
115
- if (descriptor !== undefined) acc.push(descriptor);
116
- return acc;
117
- }, []);
125
+ const children = drawerChildOrder(options).reduce<IDescriptor[]>(
126
+ (acc, slot) => {
127
+ const descriptor = slots[slot];
128
+ if (descriptor !== undefined) acc.push(descriptor);
129
+ return acc;
130
+ },
131
+ [],
132
+ );
118
133
 
119
134
  const rootStyle: IViewStyle =
120
135
  type === 'permanent' ? { ...ROOT_STYLE, flexDirection: 'row' } : ROOT_STYLE;
@@ -41,7 +41,11 @@ import type {
41
41
  IStackPresentation,
42
42
  IStackViewProps,
43
43
  } from './navigator-props';
44
- import { resolveHeaderConfigView, resolveScreenView, resolveSearchBarView } from './screen-options';
44
+ import {
45
+ resolveHeaderConfigView,
46
+ resolveScreenView,
47
+ resolveSearchBarView,
48
+ } from './screen-options';
45
49
 
46
50
  // Mirrors react-native-screens' ScreenStackItem.tsx (getPositioningStyle): a formSheet's content
47
51
  // wrapper does not get `flex: 1`. RNSScreen.mm's updateBounds pushes the sheet's live native size
@@ -101,7 +105,9 @@ export function resolveScreenViewName(
101
105
  stackPresentation === 'push' ||
102
106
  stackPresentation === 'containedModal' ||
103
107
  stackPresentation === 'containedTransparentModal';
104
- return isPlainPresentation ? RNS_SCREEN_VIEW_NAME : RNS_MODAL_SCREEN_VIEW_NAME;
108
+ return isPlainPresentation
109
+ ? RNS_SCREEN_VIEW_NAME
110
+ : RNS_MODAL_SCREEN_VIEW_NAME;
105
111
  }
106
112
 
107
113
  export function resolveScreenContentWrapperStyle(
@@ -141,14 +147,23 @@ export function isHeaderInModal(
141
147
  isAndroid: boolean,
142
148
  ): boolean {
143
149
  if (isAndroid) return false;
144
- return (stackPresentation ?? STACK_DEFAULT_PRESENTATION) !== 'push' && !headerHidden;
150
+ return (
151
+ (stackPresentation ?? STACK_DEFAULT_PRESENTATION) !== 'push' &&
152
+ !headerHidden
153
+ );
145
154
  }
146
155
 
147
156
  // Inner-modal wrapper styles for isHeaderInModal: the inner RNSScreenStack fills its outer
148
157
  // RNSScreen, and the inner RNSScreen fills the stack absolutely on all four edges. This mirrors
149
158
  // react-native-screens' own `styles.container` / `StyleSheet.absoluteFill` for this exact pair.
150
159
  const HEADER_IN_MODAL_STACK_STYLE = { flex: 1 };
151
- const HEADER_IN_MODAL_SCREEN_STYLE = { position: 'absolute', top: 0, start: 0, end: 0, bottom: 0 };
160
+ const HEADER_IN_MODAL_SCREEN_STYLE = {
161
+ position: 'absolute',
162
+ top: 0,
163
+ start: 0,
164
+ end: 0,
165
+ bottom: 0,
166
+ };
152
167
 
153
168
  export function resolveHeaderInModalStackStyle(): Record<string, unknown> {
154
169
  return HEADER_IN_MODAL_STACK_STYLE;
@@ -160,7 +175,9 @@ export function resolveHeaderInModalScreenStyle(): Record<string, unknown> {
160
175
 
161
176
  // The RNSScreen leaf's own props; the adapter spreads this record directly onto the element it
162
177
  // builds and appends the header config child and real screen content itself, not here.
163
- export function resolveScreenProps(view: IScreenViewProps): Record<string, unknown> {
178
+ export function resolveScreenProps(
179
+ view: IScreenViewProps,
180
+ ): Record<string, unknown> {
164
181
  return {
165
182
  ...view.passthrough,
166
183
  screenId: view.screenId,
@@ -175,7 +192,8 @@ export function resolveScreenProps(view: IScreenViewProps): Record<string, unkno
175
192
  // coerceChildScrollViewComponentSizeToSize) is driven by that same state, so an async push
176
193
  // likely lags one step behind a fast drag. Scoped to formSheet: push/plain-modal screens have
177
194
  // no comparable live-resize need.
178
- synchronousShadowStateUpdatesEnabled: view.stackPresentation === 'formSheet',
195
+ synchronousShadowStateUpdatesEnabled:
196
+ view.stackPresentation === 'formSheet',
179
197
  transitionDuration: view.transitionDuration,
180
198
  sheetAllowedDetents: view.sheetAllowedDetents,
181
199
  // Native RNSScreen's real prop names carry an "Index" suffix (react-native-screens' own
@@ -199,7 +217,9 @@ export function resolveScreenProps(view: IScreenViewProps): Record<string, unkno
199
217
  // `style: {flex: 1}` is ours, not react-native-screens': RNSScreenStackView sizes its nav
200
218
  // controller to `self.bounds`, which Yoga would otherwise collapse to zero, and (unlike RN's
201
219
  // own ScreenStack.tsx) we drive the native view directly (<third_party_rn_packages_are_react_only>).
202
- export function resolveStackProps(view: IStackViewProps): Record<string, unknown> {
220
+ export function resolveStackProps(
221
+ view: IStackViewProps,
222
+ ): Record<string, unknown> {
203
223
  return { style: { flex: 1 }, ...view.passthrough };
204
224
  }
205
225
 
@@ -257,7 +277,9 @@ export function renderHeaderConfig(
257
277
  // The RNSSearchBar leaf's static config props; event handlers ride in `passthrough`.
258
278
  // renderHeaderConfig wraps this into the RNSSearchBar Descriptor and nests it as the header
259
279
  // config's child.
260
- export function resolveSearchBarProps(view: ISearchBarViewProps): Record<string, unknown> {
280
+ export function resolveSearchBarProps(
281
+ view: ISearchBarViewProps,
282
+ ): Record<string, unknown> {
261
283
  return {
262
284
  ...view.passthrough,
263
285
  placeholder: view.placeholder,
@@ -368,19 +390,29 @@ export type IScreenRenderPlan = {
368
390
  innerScreenStyle: Record<string, unknown>;
369
391
  };
370
392
 
371
- export function resolveScreenRenderPlan(input: IScreenRenderPlanInput): IScreenRenderPlan {
393
+ export function resolveScreenRenderPlan(
394
+ input: IScreenRenderPlanInput,
395
+ ): IScreenRenderPlan {
372
396
  const { options, isAndroid } = input;
373
397
  const headerHidden = options?.headerShown === false;
374
398
  const activityState = computeActivityState(input.index, input.routeCount);
375
399
 
376
400
  const screenProps = resolveScreenProps(
377
- resolveScreenView(input.screenId, activityState, options, input.screenPassthrough),
401
+ resolveScreenView(
402
+ input.screenId,
403
+ activityState,
404
+ options,
405
+ input.screenPassthrough,
406
+ ),
378
407
  );
379
408
 
380
409
  const searchBarOptions = options?.headerSearchBarOptions;
381
410
  const searchBarProps = searchBarOptions
382
411
  ? resolveSearchBarProps(
383
- resolveSearchBarView(searchBarOptions, input.searchBarPassthrough ?? {}),
412
+ resolveSearchBarView(
413
+ searchBarOptions,
414
+ input.searchBarPassthrough ?? {},
415
+ ),
384
416
  )
385
417
  : undefined;
386
418
 
@@ -394,7 +426,10 @@ export function resolveScreenRenderPlan(input: IScreenRenderPlanInput): IScreenR
394
426
 
395
427
  return {
396
428
  activityState,
397
- screenViewName: resolveScreenViewName(options?.stackPresentation, isAndroid),
429
+ screenViewName: resolveScreenViewName(
430
+ options?.stackPresentation,
431
+ isAndroid,
432
+ ),
398
433
  screenProps,
399
434
  headerConfig,
400
435
  searchBarProps,
@@ -407,7 +442,11 @@ export function resolveScreenRenderPlan(input: IScreenRenderPlanInput): IScreenR
407
442
  ),
408
443
  collapsable: false,
409
444
  },
410
- inModal: isHeaderInModal(options?.stackPresentation, headerHidden, isAndroid),
445
+ inModal: isHeaderInModal(
446
+ options?.stackPresentation,
447
+ headerHidden,
448
+ isAndroid,
449
+ ),
411
450
  innerStackStyle: resolveHeaderInModalStackStyle(),
412
451
  innerScreenStyle: resolveHeaderInModalScreenStyle(),
413
452
  };
@@ -9,7 +9,12 @@
9
9
 
10
10
  import { el, txt } from '@symbiote-native/components';
11
11
  import type { IDescriptor } from '@symbiote-native/components';
12
- import type { IColorValue, IViewStyle, ITextStyle, IStyleProp } from '@symbiote-native/engine';
12
+ import type {
13
+ IColorValue,
14
+ IViewStyle,
15
+ ITextStyle,
16
+ IStyleProp,
17
+ } from '@symbiote-native/engine';
13
18
  import type { ITabBarIcon } from './tab-options';
14
19
 
15
20
  // react-navigation's own bottom-tabs default to the surrounding theme's primary/text colors;
@@ -96,14 +101,19 @@ function resolveTintColor(item: ITabBarItemView): IColorValue {
96
101
  // for a vector-icon-font label); an IDescriptor icon (an <Image>/vector-icon element the adapter
97
102
  // already resolved) is spliced in verbatim, exactly like renderHeaderConfig treats a pre-built
98
103
  // Descriptor child.
99
- function renderIcon(icon: ITabBarIcon | undefined, color: IColorValue): IDescriptor | undefined {
104
+ function renderIcon(
105
+ icon: ITabBarIcon | undefined,
106
+ color: IColorValue,
107
+ ): IDescriptor | undefined {
100
108
  if (icon === undefined) return undefined;
101
109
  if (typeof icon === 'string')
102
110
  return txt({ style: { color, fontSize: TAB_ICON_FONT_SIZE } }, [icon]);
103
111
  return icon;
104
112
  }
105
113
 
106
- function renderBadge(badge: string | number | undefined): IDescriptor | undefined {
114
+ function renderBadge(
115
+ badge: string | number | undefined,
116
+ ): IDescriptor | undefined {
107
117
  if (badge === undefined) return undefined;
108
118
  return el('symbiote-view', { style: BADGE_STYLE }, [
109
119
  txt({ style: BADGE_TEXT_STYLE }, [String(badge)]),
@@ -121,8 +131,12 @@ export function renderTabBarItem(item: ITabBarItemView): IDescriptor {
121
131
 
122
132
  const children: IDescriptor[] = [];
123
133
  if (iconChildren.length > 0)
124
- children.push(el('symbiote-view', { style: ICON_WRAPPER_STYLE }, iconChildren));
125
- children.push(txt({ style: { color, fontSize: TAB_LABEL_FONT_SIZE } }, [item.label]));
134
+ children.push(
135
+ el('symbiote-view', { style: ICON_WRAPPER_STYLE }, iconChildren),
136
+ );
137
+ children.push(
138
+ txt({ style: { color, fontSize: TAB_LABEL_FONT_SIZE } }, [item.label]),
139
+ );
126
140
 
127
141
  return el(
128
142
  'symbiote-view',
@@ -134,5 +148,9 @@ export function renderTabBarItem(item: ITabBarItemView): IDescriptor {
134
148
 
135
149
  export function renderTabBar(view: ITabBarViewProps): IDescriptor {
136
150
  const items = view.items.map(renderTabBarItem);
137
- return el('symbiote-view', { ...view.passthrough, style: [TAB_BAR_STYLE, view.style] }, items);
151
+ return el(
152
+ 'symbiote-view',
153
+ { ...view.passthrough, style: [TAB_BAR_STYLE, view.style] },
154
+ items,
155
+ );
138
156
  }
@@ -20,7 +20,10 @@ import type {
20
20
  ISheetInitialDetent,
21
21
  ISheetLargestUndimmedDetent,
22
22
  } from './navigator-props';
23
- import { buildHeaderBarButtonDispatch, prepareHeaderBarButtonItems } from './header-bar-buttons';
23
+ import {
24
+ buildHeaderBarButtonDispatch,
25
+ prepareHeaderBarButtonItems,
26
+ } from './header-bar-buttons';
24
27
 
25
28
  // These sentinels must be kept in sync with react-native-screens' native side - they are RNS's
26
29
  // own compat values for the 'fitToContents'/'large'/'medium'/'all' legacy presets, replicated
@@ -31,7 +34,9 @@ const SHEET_COMPAT_MEDIUM: number[] = [0.5];
31
34
  const SHEET_COMPAT_ALL: number[] = [0.5, 1.0];
32
35
  const SHEET_DIMMED_ALWAYS = -1;
33
36
 
34
- function resolveSheetAllowedDetents(compat: ISheetAllowedDetents | undefined): number[] {
37
+ function resolveSheetAllowedDetents(
38
+ compat: ISheetAllowedDetents | undefined,
39
+ ): number[] {
35
40
  if (compat === undefined) return SHEET_COMPAT_LARGE;
36
41
  if (Array.isArray(compat)) return compat;
37
42
  switch (compat) {
@@ -74,7 +79,9 @@ function resolveSheetInitialDetent(
74
79
  return compat;
75
80
  }
76
81
 
77
- function toOptionalBooleanNativeProp(value: boolean | undefined): IOptionalBooleanNativeProp {
82
+ function toOptionalBooleanNativeProp(
83
+ value: boolean | undefined,
84
+ ): IOptionalBooleanNativeProp {
78
85
  if (value === undefined) return 'undefined';
79
86
  return value ? 'true' : 'false';
80
87
  }
@@ -119,7 +126,9 @@ export function resolveScreenView(
119
126
  options: IScreenOptions | undefined,
120
127
  passthrough: Record<string, unknown> = {},
121
128
  ): IScreenViewProps {
122
- const sheetAllowedDetents = resolveSheetAllowedDetents(options?.sheetAllowedDetents);
129
+ const sheetAllowedDetents = resolveSheetAllowedDetents(
130
+ options?.sheetAllowedDetents,
131
+ );
123
132
  const lastDetentIndex = sheetAllowedDetents.length - 1;
124
133
  return {
125
134
  screenId,
@@ -142,7 +151,8 @@ export function resolveScreenView(
142
151
  sheetExpandsWhenScrolledToEdge: options?.sheetExpandsWhenScrolledToEdge,
143
152
  sheetElevation: options?.sheetElevation,
144
153
  sheetShouldOverflowTopInset: options?.sheetShouldOverflowTopInset,
145
- sheetDefaultResizeAnimationEnabled: options?.sheetDefaultResizeAnimationEnabled,
154
+ sheetDefaultResizeAnimationEnabled:
155
+ options?.sheetDefaultResizeAnimationEnabled,
146
156
  statusBarStyle: options?.statusBarStyle,
147
157
  statusBarHidden: options?.statusBarHidden,
148
158
  statusBarAnimation: options?.statusBarAnimation,
@@ -5,7 +5,11 @@
5
5
  // buildScrollViewHandle exactly (same lazy-getter shape); the adapter supplies the node getter
6
6
  // and its own framework-specific ref field (see react/screen.ts's IReactSearchBarOptions - the
7
7
  // ref itself is per-adapter, same split as IPressableProps).
8
- import { dispatchViewCommand, dlog, type ISymbioteNode } from '@symbiote-native/engine';
8
+ import {
9
+ dispatchViewCommand,
10
+ dlog,
11
+ type ISymbioteNode,
12
+ } from '@symbiote-native/engine';
9
13
 
10
14
  export interface ISearchBarCommands {
11
15
  focus(): void;
@@ -21,7 +25,10 @@ export interface ISearchBarCommands {
21
25
  // stale handle outlived its screen) was the one unlogged gap, silently indistinguishable from
22
26
  // "the command fired but did nothing". Investigation instrumentation (HeaderOptionsScreen
23
27
  // unresponsive-buttons bug); kept behind DEBUG per <keep_logs_gate_behind_DEBUG>, never removed.
24
- function warnIfDetached(command: string, node: ISymbioteNode | null): node is ISymbioteNode {
28
+ function warnIfDetached(
29
+ command: string,
30
+ node: ISymbioteNode | null,
31
+ ): node is ISymbioteNode {
25
32
  if (node !== null) return true;
26
33
  dlog(
27
34
  `SearchBarCommands.${command}: skipped, node is null (ref never attached) at t=${Date.now()}`,
@@ -45,7 +52,9 @@ function makeCommand(
45
52
 
46
53
  // `getNode` is a LAZY getter, read on every call - see buildScrollViewHandle's comment for why
47
54
  // an eager capture would freeze `null`.
48
- export function buildSearchBarHandle(getNode: () => ISymbioteNode | null): ISearchBarCommands {
55
+ export function buildSearchBarHandle(
56
+ getNode: () => ISymbioteNode | null,
57
+ ): ISearchBarCommands {
49
58
  return {
50
59
  focus: makeCommand(getNode, 'focus'),
51
60
  blur: makeCommand(getNode, 'blur'),
@@ -42,8 +42,11 @@ export function reconcileStackRoutes(
42
42
  state: INavigatorState,
43
43
  registeredNames: readonly string[],
44
44
  ): INavigatorState {
45
- const routes = state.routes.filter(route => registeredNames.includes(route.name));
45
+ const routes = state.routes.filter(route =>
46
+ registeredNames.includes(route.name),
47
+ );
46
48
  // filter preserves order, so an unchanged length means nothing was dropped.
47
- if (routes.length === state.routes.length || routes.length === 0) return state;
49
+ if (routes.length === state.routes.length || routes.length === 0)
50
+ return state;
48
51
  return { routes };
49
52
  }
@@ -11,11 +11,19 @@ import { isRecord } from '../guards';
11
11
  // params: undefined}`, and JSON.stringify drops undefined-valued keys - a JSON round trip of a
12
12
  // perfectly valid route legitimately arrives here without a `params` key at all.
13
13
  function isRoute(value: unknown): value is IRoute<unknown> {
14
- return isRecord(value) && typeof value.key === 'string' && typeof value.name === 'string';
14
+ return (
15
+ isRecord(value) &&
16
+ typeof value.key === 'string' &&
17
+ typeof value.name === 'string'
18
+ );
15
19
  }
16
20
 
17
21
  function isNavigatorState(value: unknown): value is INavigatorState {
18
- return isRecord(value) && Array.isArray(value.routes) && value.routes.every(isRoute);
22
+ return (
23
+ isRecord(value) &&
24
+ Array.isArray(value.routes) &&
25
+ value.routes.every(isRoute)
26
+ );
19
27
  }
20
28
 
21
29
  export function serializeNavigatorState(state: INavigatorState): unknown {
@@ -24,7 +32,9 @@ export function serializeNavigatorState(state: INavigatorState): unknown {
24
32
 
25
33
  export function deserializeNavigatorState(raw: unknown): INavigatorState {
26
34
  if (!isNavigatorState(raw)) {
27
- throw new Error('deserializeNavigatorState: persisted value is not a valid navigator state');
35
+ throw new Error(
36
+ 'deserializeNavigatorState: persisted value is not a valid navigator state',
37
+ );
28
38
  }
29
39
  return raw;
30
40
  }
@@ -29,7 +29,8 @@ export function createInitialTabState(
29
29
  routes: readonly IRouteEntry[],
30
30
  initialRouteName?: string,
31
31
  ): ITabRouterState {
32
- if (initialRouteName === undefined) return { routes, index: INITIAL_FOCUSED_INDEX };
32
+ if (initialRouteName === undefined)
33
+ return { routes, index: INITIAL_FOCUSED_INDEX };
33
34
  const index = routes.findIndex(route => route.name === initialRouteName);
34
35
  return { routes, index: index === -1 ? INITIAL_FOCUSED_INDEX : index };
35
36
  }
@@ -77,7 +78,10 @@ export function tabRouterReducer(
77
78
  }
78
79
  const target = state.routes[index];
79
80
  const routes = state.routes.slice();
80
- routes[index] = { ...target, params: mergeParams(target.params, action.params) };
81
+ routes[index] = {
82
+ ...target,
83
+ params: mergeParams(target.params, action.params),
84
+ };
81
85
  return { ...state, index, routes };
82
86
  }
83
87
 
@@ -86,7 +90,10 @@ export function tabRouterReducer(
86
90
  if (index === -1) return state;
87
91
  const target = state.routes[index];
88
92
  const routes = state.routes.slice();
89
- routes[index] = { ...target, params: mergeParams(target.params, action.params) };
93
+ routes[index] = {
94
+ ...target,
95
+ params: mergeParams(target.params, action.params),
96
+ };
90
97
  return { ...state, routes };
91
98
  }
92
99