@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 +18 -3
- package/build/react/drawer/index.js +11 -2
- package/build/react/tabs/index.js +5 -1
- package/build/svelte/drawer/index.svelte +3 -0
- package/build/svelte/stack/index.svelte +3 -0
- package/build/svelte/tabs/index.svelte +3 -0
- package/package.json +13 -13
- package/src/react/drawer/index.ts +7 -0
- package/src/react/tabs/index.ts +3 -0
- package/src/svelte/drawer/index.svelte +3 -0
- package/src/svelte/stack/index.svelte +3 -0
- package/src/svelte/tabs/index.svelte +3 -0
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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/
|
|
174
|
+
"@symbiote-native/angular": "^3.1.1",
|
|
175
175
|
"@symbiote-native/engine": "^1.3.0",
|
|
176
|
-
"@symbiote-native/
|
|
177
|
-
"@symbiote-native/
|
|
178
|
-
"@symbiote-native/
|
|
179
|
-
"@symbiote-native/
|
|
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.
|
|
231
|
+
"@symbiote-native/angular": "3.1.1",
|
|
232
232
|
"@symbiote-native/engine": "1.3.0",
|
|
233
|
-
"@symbiote-native/react": "3.0.
|
|
234
|
-
"@symbiote-native/solid": "3.0.
|
|
235
|
-
"@symbiote-native/svelte": "3.0.
|
|
236
|
-
"@symbiote-native/test-utils": "0.4.
|
|
237
|
-
"@symbiote-native/vue": "3.0.
|
|
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
|
|
package/src/react/tabs/index.ts
CHANGED
|
@@ -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);
|