@symbiote-native/navigation 5.0.2 → 5.0.3

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.
package/README.md CHANGED
@@ -14,13 +14,28 @@ logic/view/lifecycle split every other SymbioteNative component follows.
14
14
 
15
15
  ## Install
16
16
 
17
+ **New app:**
18
+
19
+ ```bash
20
+ npx @symbiote-native/cli new my-app --navigation
21
+ ```
22
+
23
+ **Existing SymbioteNative app:**
24
+
25
+ ```bash
26
+ npx @symbiote-native/cli add --navigation
27
+ ```
28
+
29
+ Either way: installs and wires this for you — see [`@symbiote-native/cli`](../cli).
30
+
31
+ <details>
32
+ <summary>Manual install</summary>
33
+
17
34
  ```bash
18
35
  npm install @symbiote-native/navigation
19
36
  ```
20
37
 
21
- Scaffolding or extending a SymbioteNative app? `npx @symbiote-native/cli new --navigation` (or
22
- `add --navigation` in an existing app) installs and wires this for you — see
23
- [`@symbiote-native/cli`](../cli).
38
+ </details>
24
39
 
25
40
  Only this package — never `react-native-screens` directly. `@symbiote-native/navigation` depends
26
41
  on it and ships as the sole autolinked native proxy (`react-native.config.cjs` +
@@ -149,7 +149,12 @@ const DrawerImpl = forwardRef((props, forwardedRef) => {
149
149
  },
150
150
  })).current;
151
151
  const animated = isDrawerAnimated(options);
152
- const geometry = useMemo(() => resolveDrawerGeometry(options), [options.drawerType, options.drawerPosition, options.drawerWidth]);
152
+ // Narrow on purpose: resolveDrawerGeometry only ever reads these 3 fields off options (see
153
+ // drawer-options/index.ts's resolveDrawerWidth/Type/Position) — depending on the whole
154
+ // `options` object would recompute on every unrelated options field, defeating the memo.
155
+ const geometry = useMemo(() => resolveDrawerGeometry(options),
156
+ // eslint-disable-next-line react-hooks/exhaustive-deps
157
+ [options.drawerType, options.drawerPosition, options.drawerWidth]);
153
158
  const panelSlot = animated
154
159
  ? resolveDrawerSlotInterpolation(geometry, 'panel')
155
160
  : undefined;
@@ -191,7 +196,11 @@ const DrawerImpl = forwardRef((props, forwardedRef) => {
191
196
  // route object, so a jumpTo-with-no-params re-focus of the ALREADY-focused route (a no-op in
192
197
  // drawerRouterReducer) and any future params merge don't spuriously re-fire focus/blur.
193
198
  const focusedRouteKey = focusedRoute?.key;
194
- const routeEmitter = useMemo(() => createNavigationEmitter(), [focusedRouteKey]);
199
+ // focusedRouteKey is a pure invalidation signal — createNavigationEmitter() takes no
200
+ // arguments, the key only forces a fresh emitter per focus change (see comment above).
201
+ const routeEmitter = useMemo(() => createNavigationEmitter(),
202
+ // eslint-disable-next-line react-hooks/exhaustive-deps
203
+ [focusedRouteKey]);
195
204
  // Drawer paints its own panel in pure JS - there is no native onAppear/onDisappear to hook
196
205
  // (unlike Stack's RNSScreen), so focus/blur is synthesized here: mount = focus, cleanup = blur,
197
206
  // exactly what an effect keyed on focusedRouteKey already encodes - no diffFocusedRoute
@@ -106,7 +106,11 @@ const TabImpl = forwardRef((props, forwardedRef) => {
106
106
  // object so a setParams-only change (new route object, same key) doesn't spuriously re-fire
107
107
  // focus/blur.
108
108
  const focusedRouteKey = focusedRoute?.key;
109
- const routeEmitter = useMemo(() => createNavigationEmitter(), [focusedRouteKey]);
109
+ // focusedRouteKey is a pure invalidation signal — createNavigationEmitter() takes no
110
+ // arguments, the key only forces a fresh emitter per focus change (see comment above).
111
+ const routeEmitter = useMemo(() => createNavigationEmitter(),
112
+ // eslint-disable-next-line react-hooks/exhaustive-deps
113
+ [focusedRouteKey]);
110
114
  // Tab paints its own bar in pure JS - there is no native onAppear/onDisappear to hook (unlike
111
115
  // Stack's RNSScreen), so focus/blur is synthesized here: mount = focus, cleanup = blur, exactly
112
116
  // what an effect keyed on focusedRouteKey already encodes - no diffFocusedRoute indirection
@@ -279,6 +279,9 @@
279
279
  // Only the focused route's screen is ever mounted (like Tab, unlike Stack which keeps every
280
280
  // pushed route alive). One emitter per route.key, created lazily and cached for the navigator's
281
281
  // whole lifetime.
282
+ // Plain Map: a get/set/delete identity cache never read by the reactive graph, not rendered
283
+ // state — SvelteMap's tracking would be pure overhead here.
284
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity
282
285
  const emitters = new Map<string, INavigationEmitter>();
283
286
  function emitterFor(routeKey: string): INavigationEmitter {
284
287
  let emitter = emitters.get(routeKey);
@@ -132,6 +132,9 @@
132
132
 
133
133
  // One emitter per route.key, created lazily the first time a route is rendered and pruned once
134
134
  // it is popped off the stack (the broadcast effect below).
135
+ // Plain Map: a get/set/delete identity cache never read by the reactive graph, not rendered
136
+ // state — SvelteMap's tracking would be pure overhead here.
137
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity
135
138
  const emitters = new Map<string, INavigationEmitter>();
136
139
  function emitterFor(routeKey: string): INavigationEmitter {
137
140
  let emitter = emitters.get(routeKey);
@@ -123,6 +123,9 @@
123
123
  // mirrors Stack's own `emitters` map. This decouples emitter IDENTITY (stable, looked up by
124
124
  // key, read below when a route's NavigationScope is built) from emit TIMING (must wait until
125
125
  // the focused screen has actually mounted and subscribed).
126
+ // Plain Map: a get/set/delete identity cache never read by the reactive graph, not rendered
127
+ // state — SvelteMap's tracking would be pure overhead here.
128
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity
126
129
  const emitters = new Map<string, INavigationEmitter>();
127
130
  function emitterFor(routeKey: string): INavigationEmitter {
128
131
  let emitter = emitters.get(routeKey);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/navigation",
3
- "version": "5.0.2",
3
+ "version": "5.0.3",
4
4
  "description": "Native stack navigator for SymbioteNative, built on react-native-screens' native view primitives (RNSScreen/RNSScreenStack) — one framework-agnostic core, reachable from the React, Vue, Svelte, Solid, and Angular adapters.",
5
5
  "keywords": [
6
6
  "react-native",
@@ -160,7 +160,7 @@
160
160
  },
161
161
  "dependencies": {
162
162
  "react-native-screens": "4.26.0",
163
- "@symbiote-native/components": "3.1.0"
163
+ "@symbiote-native/components": "3.1.1"
164
164
  },
165
165
  "peerDependencies": {
166
166
  "@angular/common": ">=20",
@@ -171,12 +171,12 @@
171
171
  "solid-js": ">=1.9.0",
172
172
  "svelte": ">=5.56.0",
173
173
  "vue": ">=3.5.0",
174
- "@symbiote-native/react": "^3.0.2",
174
+ "@symbiote-native/angular": "^3.1.1",
175
175
  "@symbiote-native/engine": "^1.3.0",
176
- "@symbiote-native/svelte": "^3.0.2",
177
- "@symbiote-native/angular": "^3.1.0",
178
- "@symbiote-native/vue": "^3.0.2",
179
- "@symbiote-native/solid": "^3.0.2"
176
+ "@symbiote-native/react": "^3.0.3",
177
+ "@symbiote-native/solid": "^3.0.3",
178
+ "@symbiote-native/svelte": "^3.0.3",
179
+ "@symbiote-native/vue": "^3.0.3"
180
180
  },
181
181
  "peerDependenciesMeta": {
182
182
  "@angular/common": {
@@ -228,13 +228,13 @@
228
228
  "solid-js": "^1.9.14",
229
229
  "svelte": "^5.56.0",
230
230
  "typescript": "~6.0.0",
231
- "@symbiote-native/angular": "3.1.0",
231
+ "@symbiote-native/angular": "3.1.1",
232
232
  "@symbiote-native/engine": "1.3.0",
233
- "@symbiote-native/react": "3.0.2",
234
- "@symbiote-native/solid": "3.0.2",
235
- "@symbiote-native/svelte": "3.0.2",
236
- "@symbiote-native/test-utils": "0.4.2",
237
- "@symbiote-native/vue": "3.0.2"
233
+ "@symbiote-native/react": "3.0.3",
234
+ "@symbiote-native/solid": "3.0.3",
235
+ "@symbiote-native/svelte": "3.0.3",
236
+ "@symbiote-native/test-utils": "0.4.3",
237
+ "@symbiote-native/vue": "3.0.3"
238
238
  },
239
239
  "scripts": {
240
240
  "clean": "rm -rf build-ngc",
@@ -320,8 +320,12 @@ const DrawerImpl = forwardRef<IDrawerNavigatorHandle, IDrawerProps>(
320
320
  ).current;
321
321
 
322
322
  const animated = isDrawerAnimated(options);
323
+ // Narrow on purpose: resolveDrawerGeometry only ever reads these 3 fields off options (see
324
+ // drawer-options/index.ts's resolveDrawerWidth/Type/Position) — depending on the whole
325
+ // `options` object would recompute on every unrelated options field, defeating the memo.
323
326
  const geometry = useMemo(
324
327
  () => resolveDrawerGeometry(options),
328
+ // eslint-disable-next-line react-hooks/exhaustive-deps
325
329
  [options.drawerType, options.drawerPosition, options.drawerWidth],
326
330
  );
327
331
 
@@ -371,8 +375,11 @@ const DrawerImpl = forwardRef<IDrawerNavigatorHandle, IDrawerProps>(
371
375
  // route object, so a jumpTo-with-no-params re-focus of the ALREADY-focused route (a no-op in
372
376
  // drawerRouterReducer) and any future params merge don't spuriously re-fire focus/blur.
373
377
  const focusedRouteKey = focusedRoute?.key;
378
+ // focusedRouteKey is a pure invalidation signal — createNavigationEmitter() takes no
379
+ // arguments, the key only forces a fresh emitter per focus change (see comment above).
374
380
  const routeEmitter = useMemo(
375
381
  () => createNavigationEmitter(),
382
+ // eslint-disable-next-line react-hooks/exhaustive-deps
376
383
  [focusedRouteKey],
377
384
  );
378
385
 
@@ -216,8 +216,11 @@ const TabImpl = forwardRef<ITabNavigatorHandle, ITabProps>(
216
216
  // object so a setParams-only change (new route object, same key) doesn't spuriously re-fire
217
217
  // focus/blur.
218
218
  const focusedRouteKey = focusedRoute?.key;
219
+ // focusedRouteKey is a pure invalidation signal — createNavigationEmitter() takes no
220
+ // arguments, the key only forces a fresh emitter per focus change (see comment above).
219
221
  const routeEmitter = useMemo(
220
222
  () => createNavigationEmitter(),
223
+ // eslint-disable-next-line react-hooks/exhaustive-deps
221
224
  [focusedRouteKey],
222
225
  );
223
226
 
@@ -279,6 +279,9 @@
279
279
  // Only the focused route's screen is ever mounted (like Tab, unlike Stack which keeps every
280
280
  // pushed route alive). One emitter per route.key, created lazily and cached for the navigator's
281
281
  // whole lifetime.
282
+ // Plain Map: a get/set/delete identity cache never read by the reactive graph, not rendered
283
+ // state — SvelteMap's tracking would be pure overhead here.
284
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity
282
285
  const emitters = new Map<string, INavigationEmitter>();
283
286
  function emitterFor(routeKey: string): INavigationEmitter {
284
287
  let emitter = emitters.get(routeKey);
@@ -132,6 +132,9 @@
132
132
 
133
133
  // One emitter per route.key, created lazily the first time a route is rendered and pruned once
134
134
  // it is popped off the stack (the broadcast effect below).
135
+ // Plain Map: a get/set/delete identity cache never read by the reactive graph, not rendered
136
+ // state — SvelteMap's tracking would be pure overhead here.
137
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity
135
138
  const emitters = new Map<string, INavigationEmitter>();
136
139
  function emitterFor(routeKey: string): INavigationEmitter {
137
140
  let emitter = emitters.get(routeKey);
@@ -123,6 +123,9 @@
123
123
  // mirrors Stack's own `emitters` map. This decouples emitter IDENTITY (stable, looked up by
124
124
  // key, read below when a route's NavigationScope is built) from emit TIMING (must wait until
125
125
  // the focused screen has actually mounted and subscribed).
126
+ // Plain Map: a get/set/delete identity cache never read by the reactive graph, not rendered
127
+ // state — SvelteMap's tracking would be pure overhead here.
128
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity
126
129
  const emitters = new Map<string, INavigationEmitter>();
127
130
  function emitterFor(routeKey: string): INavigationEmitter {
128
131
  let emitter = emitters.get(routeKey);