@sigx/lynx-navigation 0.1.0 → 0.1.1
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/LICENSE +1 -1
- package/README.md +355 -0
- package/dist/components/Drawer.d.ts +58 -0
- package/dist/components/Drawer.d.ts.map +1 -0
- package/dist/components/Drawer.js +76 -0
- package/dist/components/Drawer.js.map +1 -0
- package/dist/components/EdgeBackHandle.js +144 -0
- package/dist/components/EdgeBackHandle.js.map +1 -0
- package/dist/components/EntryScope.d.ts +26 -0
- package/dist/components/EntryScope.d.ts.map +1 -0
- package/dist/components/EntryScope.js +33 -0
- package/dist/components/EntryScope.js.map +1 -0
- package/dist/components/Header.d.ts +7 -0
- package/dist/components/Header.d.ts.map +1 -0
- package/dist/components/Header.js +103 -0
- package/dist/components/Header.js.map +1 -0
- package/dist/components/Link.js +1 -4
- package/dist/components/Link.js.map +1 -1
- package/dist/components/NavigationRoot.d.ts +1 -1
- package/dist/components/NavigationRoot.d.ts.map +1 -1
- package/dist/components/NavigationRoot.js +29 -3
- package/dist/components/NavigationRoot.js.map +1 -1
- package/dist/components/Screen.d.ts +98 -0
- package/dist/components/Screen.d.ts.map +1 -0
- package/dist/components/Screen.js +94 -0
- package/dist/components/Screen.js.map +1 -0
- package/dist/components/ScreenContainer.d.ts.map +1 -1
- package/dist/components/ScreenContainer.js +77 -0
- package/dist/components/ScreenContainer.js.map +1 -0
- package/dist/components/Stack.d.ts.map +1 -1
- package/dist/components/Stack.js +60 -24
- package/dist/components/Stack.js.map +1 -1
- package/dist/components/TabBar.d.ts +40 -0
- package/dist/components/TabBar.d.ts.map +1 -0
- package/dist/components/TabBar.js +63 -0
- package/dist/components/TabBar.js.map +1 -0
- package/dist/components/Tabs.d.ts +101 -0
- package/dist/components/Tabs.d.ts.map +1 -0
- package/dist/components/Tabs.js +135 -0
- package/dist/components/Tabs.js.map +1 -0
- package/dist/hooks/use-focus.d.ts +46 -0
- package/dist/hooks/use-focus.d.ts.map +1 -0
- package/dist/hooks/use-focus.js +77 -0
- package/dist/hooks/use-focus.js.map +1 -0
- package/dist/hooks/use-hardware-back.js +50 -0
- package/dist/hooks/use-hardware-back.js.map +1 -0
- package/dist/hooks/use-linking-nav.d.ts +92 -0
- package/dist/hooks/use-linking-nav.d.ts.map +1 -0
- package/dist/hooks/use-linking-nav.js +109 -0
- package/dist/hooks/use-linking-nav.js.map +1 -0
- package/dist/hooks/use-nav-internal.d.ts +38 -1
- package/dist/hooks/use-nav-internal.d.ts.map +1 -1
- package/dist/hooks/use-nav-internal.js +32 -0
- package/dist/hooks/use-nav-internal.js.map +1 -1
- package/dist/hooks/use-nav-serializer.d.ts +83 -0
- package/dist/hooks/use-nav-serializer.d.ts.map +1 -0
- package/dist/hooks/use-nav-serializer.js +181 -0
- package/dist/hooks/use-nav-serializer.js.map +1 -0
- package/dist/hooks/use-nav.js.map +1 -1
- package/dist/hooks/use-screen-options.d.ts +3 -0
- package/dist/hooks/use-screen-options.d.ts.map +1 -0
- package/dist/hooks/use-screen-options.js +43 -0
- package/dist/hooks/use-screen-options.js.map +1 -0
- package/dist/href.d.ts +16 -1
- package/dist/href.d.ts.map +1 -1
- package/dist/href.js +50 -7
- package/dist/href.js.map +1 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/screen-registry.d.ts +49 -0
- package/dist/internal/screen-registry.d.ts.map +1 -0
- package/dist/internal/screen-registry.js +59 -0
- package/dist/internal/screen-registry.js.map +1 -0
- package/dist/internal/screen-width.js +30 -0
- package/dist/internal/screen-width.js.map +1 -0
- package/dist/navigator/core.d.ts +20 -1
- package/dist/navigator/core.d.ts.map +1 -1
- package/dist/navigator/core.js +231 -36
- package/dist/navigator/core.js.map +1 -1
- package/dist/types.d.ts +56 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/url/build.d.ts +16 -0
- package/dist/url/build.d.ts.map +1 -0
- package/dist/url/build.js +30 -0
- package/dist/url/build.js.map +1 -0
- package/dist/url/compile.d.ts +35 -0
- package/dist/url/compile.d.ts.map +1 -0
- package/dist/url/compile.js +83 -0
- package/dist/url/compile.js.map +1 -0
- package/dist/url/format.d.ts +26 -0
- package/dist/url/format.d.ts.map +1 -0
- package/dist/url/format.js +99 -0
- package/dist/url/format.js.map +1 -0
- package/dist/url/index.d.ts +13 -0
- package/dist/url/index.d.ts.map +1 -0
- package/dist/url/index.js +13 -0
- package/dist/url/index.js.map +1 -0
- package/dist/url/parse.d.ts +21 -0
- package/dist/url/parse.d.ts.map +1 -0
- package/dist/url/parse.js +90 -0
- package/dist/url/parse.js.map +1 -0
- package/dist/url/registry.d.ts +41 -0
- package/dist/url/registry.d.ts.map +1 -0
- package/dist/url/registry.js +56 -0
- package/dist/url/registry.js.map +1 -0
- package/dist/url/validate.d.ts +24 -0
- package/dist/url/validate.d.ts.map +1 -0
- package/dist/url/validate.js +37 -0
- package/dist/url/validate.js.map +1 -0
- package/package.json +44 -15
- package/src/components/Drawer.tsx +121 -0
- package/src/components/EdgeBackHandle.tsx +1 -1
- package/src/components/EntryScope.tsx +38 -0
- package/src/components/Header.tsx +124 -0
- package/src/components/NavigationRoot.tsx +9 -1
- package/src/components/Screen.tsx +116 -0
- package/src/components/ScreenContainer.tsx +14 -1
- package/src/components/Stack.tsx +21 -2
- package/src/components/TabBar.tsx +103 -0
- package/src/components/Tabs.tsx +212 -0
- package/src/hooks/use-focus.ts +77 -0
- package/src/hooks/use-linking-nav.ts +159 -0
- package/src/hooks/use-nav-internal.ts +48 -1
- package/src/hooks/use-nav-serializer.ts +239 -0
- package/src/hooks/use-screen-options.ts +48 -0
- package/src/href.ts +68 -11
- package/src/index.ts +29 -0
- package/src/internal/screen-registry.ts +89 -0
- package/src/navigator/core.ts +86 -4
- package/src/types.ts +56 -0
- package/src/url/build.ts +35 -0
- package/src/url/compile.ts +109 -0
- package/src/url/format.ts +92 -0
- package/src/url/index.ts +18 -0
- package/src/url/parse.ts +97 -0
- package/src/url/registry.ts +69 -0
- package/src/url/validate.ts +67 -0
package/dist/navigator/core.js
CHANGED
|
@@ -1,15 +1,33 @@
|
|
|
1
|
-
import { signal } from '@sigx/lynx';
|
|
1
|
+
import { runOnMainThread, signal, untrack, } from '@sigx/lynx';
|
|
2
|
+
import { isLazyComponent } from '@sigx/lynx';
|
|
3
|
+
import { withTiming } from '@sigx/lynx-motion';
|
|
4
|
+
/**
|
|
5
|
+
* Slide-from-right transition timing. Kept as constants so screen options
|
|
6
|
+
* can override per-screen later (Phase 0.5). Duration is in seconds — that's
|
|
7
|
+
* what `@sigx/lynx-motion`'s `withTiming` expects (per `with-timing.ts`).
|
|
8
|
+
*/
|
|
9
|
+
const TRANSITION_DURATION_SEC = 0.28;
|
|
10
|
+
/**
|
|
11
|
+
* Kick off a lazy component's chunk fetch when its route is navigated to.
|
|
12
|
+
*
|
|
13
|
+
* Lazy routes (`component: lazy(() => import('./Heavy.js'))`) start loading
|
|
14
|
+
* the moment `push`/`replace` is called rather than waiting until render
|
|
15
|
+
* tries to instantiate them — by the time `<Stack>` swaps screens the chunk
|
|
16
|
+
* is usually already resolved, so the user sees the screen instead of the
|
|
17
|
+
* `<Suspense fallback>`. Fire-and-forget: errors here surface through
|
|
18
|
+
* `<Suspense>` at render time.
|
|
19
|
+
*/
|
|
20
|
+
function preloadRouteComponent(component) {
|
|
21
|
+
if (isLazyComponent(component)) {
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
23
|
+
component.preload().catch(() => { });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
2
26
|
let entryKeyCounter = 0;
|
|
3
27
|
function nextEntryKey() {
|
|
4
28
|
entryKeyCounter += 1;
|
|
5
29
|
return `entry-${entryKeyCounter}-${Math.random().toString(36).slice(2, 8)}`;
|
|
6
30
|
}
|
|
7
|
-
/**
|
|
8
|
-
* Build a StackEntry from a navigation call.
|
|
9
|
-
*
|
|
10
|
-
* Defaults applied here (rather than at call site) keep the shape consistent
|
|
11
|
-
* regardless of which overload was used.
|
|
12
|
-
*/
|
|
13
31
|
function makeEntry(name, params, search, options, routes) {
|
|
14
32
|
const route = routes[name];
|
|
15
33
|
const presentation = options?.presentation ?? route?.presentation ?? 'card';
|
|
@@ -22,15 +40,6 @@ function makeEntry(name, params, search, options, routes) {
|
|
|
22
40
|
presentation,
|
|
23
41
|
};
|
|
24
42
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Disambiguate `nav.push` / `replace` arguments at runtime.
|
|
27
|
-
*
|
|
28
|
-
* The two overloads differ in arity:
|
|
29
|
-
* - `push(name, search?, options?)` — for routes with no params schema
|
|
30
|
-
* - `push(name, params, search?, options?)` — for routes with a params schema
|
|
31
|
-
*
|
|
32
|
-
* At runtime we read the route definition to decide which shape was used.
|
|
33
|
-
*/
|
|
34
43
|
function unpackArgs(name, args, routes) {
|
|
35
44
|
const route = routes[name];
|
|
36
45
|
const requiresParams = !!route?.params;
|
|
@@ -42,52 +51,157 @@ function unpackArgs(name, args, routes) {
|
|
|
42
51
|
return { params: undefined, search, options };
|
|
43
52
|
}
|
|
44
53
|
/**
|
|
45
|
-
* Create a navigator
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* navigation. Apps usually pass the `initialRoute` of the app (e.g. 'home').
|
|
54
|
+
* Create a navigator. Returns the public `nav` handle plus the routes map.
|
|
55
|
+
* The transition signal lives on `nav` (via `nav.transition`) so `<Stack>`
|
|
56
|
+
* can subscribe to it.
|
|
49
57
|
*/
|
|
50
|
-
export function createNavigatorState(
|
|
51
|
-
|
|
52
|
-
// for atomic full-replacement (`packages/reactivity/src/types.ts:31`). The
|
|
53
|
-
// proxy *is* the array — read indices/length directly; use `$set` to
|
|
54
|
-
// replace.
|
|
58
|
+
export function createNavigatorState(opts) {
|
|
59
|
+
const { routes, initial, progress } = opts;
|
|
55
60
|
const stackSignal = signal([initial]);
|
|
61
|
+
// `signal(null)` would wrap as a primitive (no `$set`), so wrap in an
|
|
62
|
+
// object to get the standard `{ value }`-style API. Reading `.value`
|
|
63
|
+
// tracks; writing triggers re-render of `<Stack>`.
|
|
64
|
+
const transitionBox = signal({ value: null });
|
|
56
65
|
function getStack() {
|
|
57
66
|
return stackSignal;
|
|
58
67
|
}
|
|
59
68
|
function setStack(next) {
|
|
60
69
|
stackSignal.$set(next);
|
|
61
70
|
}
|
|
71
|
+
function setTransition(next) {
|
|
72
|
+
transitionBox.value = next;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Whether a transition is currently in flight. Used to no-op concurrent
|
|
76
|
+
* navigation calls — keeps the state machine simple. A queued/aborted
|
|
77
|
+
* model is a v0.3 polish item.
|
|
78
|
+
*/
|
|
79
|
+
function isTransitioning() {
|
|
80
|
+
return transitionBox.value !== null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Run the slide animation by hopping a worklet onto the main thread that
|
|
84
|
+
* resets `progress` to 0 and starts a `withTiming` to the target. Then
|
|
85
|
+
* wait the animation duration on BG so we can fire the completion
|
|
86
|
+
* callback (clear transition / commit the popped entry) when the visual
|
|
87
|
+
* animation is done.
|
|
88
|
+
*
|
|
89
|
+
* Why the SV reset lives *inside* the worklet (not on BG before the call):
|
|
90
|
+
* the BG-side render ops (Stack re-render mounting the two
|
|
91
|
+
* `ScreenContainer`s with their `useAnimatedStyle` bindings) and a BG-side
|
|
92
|
+
* SV write (`progress.value = 0`) travel different bridge channels. On
|
|
93
|
+
* subsequent navigations, MT can register the new bindings before the
|
|
94
|
+
* BG-side reset arrives — the bindings snapshot sv at its previous
|
|
95
|
+
* end-state (`1`), and `withTiming(sv, 1, ...)` then animates from 1→1
|
|
96
|
+
* (no visible motion). Resetting inside the worklet guarantees the order
|
|
97
|
+
* `bindings register → sv resets → withTiming starts` happens atomically
|
|
98
|
+
* on MT.
|
|
99
|
+
*
|
|
100
|
+
* Why we don't `await` the worklet's Promise: `withTiming` returns a
|
|
101
|
+
* Promise on MT, but Promises don't serialize across the BG/MT bridge —
|
|
102
|
+
* `runOnMainThread`'s callback fires the moment the worklet *returns*
|
|
103
|
+
* (synchronously, with `undefined` since the Promise can't cross), not
|
|
104
|
+
* when the underlying animation finishes. We time the BG-side wait
|
|
105
|
+
* against the duration we passed to MT instead.
|
|
106
|
+
*/
|
|
107
|
+
async function animateProgress(target, durationSec) {
|
|
108
|
+
if (!progress)
|
|
109
|
+
return;
|
|
110
|
+
const sv = progress;
|
|
111
|
+
const runner = runOnMainThread((t, d) => {
|
|
112
|
+
'main thread';
|
|
113
|
+
// MT-side direct write — `sv.value` is a BG-side getter/setter
|
|
114
|
+
// that emits a "read-only on BG" warning when set; the actual
|
|
115
|
+
// MT field (which `withTiming`'s animate() reads as the start
|
|
116
|
+
// value) is `sv.current.value`. See `packages/lynx-runtime/src/
|
|
117
|
+
// animated/shared-value.ts:14-44`.
|
|
118
|
+
sv.current.value = 0;
|
|
119
|
+
withTiming(sv, t, { duration: d });
|
|
120
|
+
});
|
|
121
|
+
runner(target, durationSec);
|
|
122
|
+
await new Promise((resolve) => {
|
|
123
|
+
setTimeout(resolve, Math.round(durationSec * 1000));
|
|
124
|
+
});
|
|
125
|
+
}
|
|
62
126
|
const push = ((name, ...args) => {
|
|
127
|
+
if (isTransitioning())
|
|
128
|
+
return;
|
|
63
129
|
const { params, search, options } = unpackArgs(name, args, routes);
|
|
64
130
|
if (!routes[name]) {
|
|
65
131
|
throw new Error(`[lynx-navigation] push('${name}'): route is not registered. ` +
|
|
66
132
|
`Known routes: ${Object.keys(routes).join(', ') || '(none)'}`);
|
|
67
133
|
}
|
|
68
|
-
|
|
69
|
-
|
|
134
|
+
preloadRouteComponent(routes[name].component);
|
|
135
|
+
const newEntry = makeEntry(name, params, search, options, routes);
|
|
136
|
+
const cur = getStack();
|
|
137
|
+
const prevTop = cur[cur.length - 1];
|
|
138
|
+
// Append eagerly — UX-wise the user just initiated a forward nav, so
|
|
139
|
+
// the new entry should be queryable immediately (`nav.current` =
|
|
140
|
+
// newEntry). The slide animation overlays the visual transition.
|
|
141
|
+
setStack([...cur, newEntry]);
|
|
142
|
+
const animated = options?.animated !== false && !!progress;
|
|
143
|
+
if (!animated)
|
|
144
|
+
return;
|
|
145
|
+
setTransition({
|
|
146
|
+
kind: 'push',
|
|
147
|
+
topEntry: newEntry,
|
|
148
|
+
underneathEntry: prevTop,
|
|
149
|
+
progress,
|
|
150
|
+
});
|
|
151
|
+
animateProgress(1, TRANSITION_DURATION_SEC).then(() => setTransition(null), () => setTransition(null));
|
|
70
152
|
});
|
|
71
153
|
const replace = ((name, ...args) => {
|
|
154
|
+
if (isTransitioning())
|
|
155
|
+
return;
|
|
72
156
|
const { params, search, options } = unpackArgs(name, args, routes);
|
|
73
157
|
if (!routes[name]) {
|
|
74
158
|
throw new Error(`[lynx-navigation] replace('${name}'): route is not registered.`);
|
|
75
159
|
}
|
|
160
|
+
preloadRouteComponent(routes[name].component);
|
|
76
161
|
const entry = makeEntry(name, params, search, options, routes);
|
|
77
162
|
const cur = getStack();
|
|
163
|
+
// Replace doesn't animate in v1 — it's a swap, not a forward/back nav.
|
|
164
|
+
// Adding a fade-or-slide variant is a screen-option in Phase 0.5.
|
|
78
165
|
setStack([...cur.slice(0, cur.length - 1), entry]);
|
|
79
166
|
});
|
|
80
|
-
function pop(count = 1) {
|
|
167
|
+
function pop(count = 1, options) {
|
|
168
|
+
if (isTransitioning())
|
|
169
|
+
return;
|
|
81
170
|
const cur = getStack();
|
|
82
|
-
// Always preserve at least the root entry — popping past the root is a no-op.
|
|
83
171
|
const target = Math.max(1, cur.length - Math.max(1, count));
|
|
84
172
|
if (target === cur.length)
|
|
85
173
|
return;
|
|
86
|
-
|
|
174
|
+
const animated = options?.animated !== false && !!progress && count === 1 && cur.length >= 2;
|
|
175
|
+
if (!animated) {
|
|
176
|
+
setStack(cur.slice(0, target));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
// Single-step animated pop: keep the popped entry on the stack until
|
|
180
|
+
// the slide finishes, so `<Stack>` can render both screens during the
|
|
181
|
+
// animation. The stack mutation happens on completion.
|
|
182
|
+
const popping = cur[cur.length - 1];
|
|
183
|
+
const next = cur[cur.length - 2];
|
|
184
|
+
setTransition({
|
|
185
|
+
kind: 'pop',
|
|
186
|
+
topEntry: popping,
|
|
187
|
+
underneathEntry: next,
|
|
188
|
+
progress,
|
|
189
|
+
});
|
|
190
|
+
animateProgress(1, TRANSITION_DURATION_SEC).then(() => {
|
|
191
|
+
setStack(cur.slice(0, cur.length - 1));
|
|
192
|
+
setTransition(null);
|
|
193
|
+
}, () => {
|
|
194
|
+
// On animation failure, snap to the destination state anyway —
|
|
195
|
+
// leaving the popped entry rendered would be more confusing
|
|
196
|
+
// than skipping the animation.
|
|
197
|
+
setStack(cur.slice(0, cur.length - 1));
|
|
198
|
+
setTransition(null);
|
|
199
|
+
});
|
|
87
200
|
}
|
|
88
201
|
function popTo(name) {
|
|
202
|
+
if (isTransitioning())
|
|
203
|
+
return;
|
|
89
204
|
const cur = getStack();
|
|
90
|
-
// Find the topmost matching route (search from the top down).
|
|
91
205
|
for (let i = cur.length - 1; i >= 0; i--) {
|
|
92
206
|
if (cur[i].route === name) {
|
|
93
207
|
if (i === cur.length - 1)
|
|
@@ -96,9 +210,10 @@ export function createNavigatorState(routes, initial) {
|
|
|
96
210
|
return;
|
|
97
211
|
}
|
|
98
212
|
}
|
|
99
|
-
// No matching entry — leave the stack alone.
|
|
100
213
|
}
|
|
101
214
|
function popToRoot() {
|
|
215
|
+
if (isTransitioning())
|
|
216
|
+
return;
|
|
102
217
|
const cur = getStack();
|
|
103
218
|
if (cur.length <= 1)
|
|
104
219
|
return;
|
|
@@ -109,11 +224,11 @@ export function createNavigatorState(routes, initial) {
|
|
|
109
224
|
throw new Error('[lynx-navigation] reset() called with empty stack.');
|
|
110
225
|
}
|
|
111
226
|
setStack([...state.stack]);
|
|
227
|
+
setTransition(null);
|
|
112
228
|
}
|
|
113
229
|
function dismiss() {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
// once, mirroring iOS UIViewController dismiss behavior.
|
|
230
|
+
if (isTransitioning())
|
|
231
|
+
return;
|
|
117
232
|
const cur = getStack();
|
|
118
233
|
let i = cur.length - 1;
|
|
119
234
|
while (i > 0 && cur[i].presentation !== 'card') {
|
|
@@ -123,6 +238,38 @@ export function createNavigatorState(routes, initial) {
|
|
|
123
238
|
setStack(cur.slice(0, i + 1));
|
|
124
239
|
}
|
|
125
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Set up a gesture-driven pop transition. Same shape as `pop()` sets but
|
|
243
|
+
* does NOT call `animateProgress` — the gesture worklet writes the
|
|
244
|
+
* progress SV directly per frame, then animates to commit/cancel
|
|
245
|
+
* endpoints on release before invoking `commitBackGesture` or
|
|
246
|
+
* `cancelBackGesture` via `runOnBackground`.
|
|
247
|
+
*/
|
|
248
|
+
function beginBackGesture() {
|
|
249
|
+
if (isTransitioning())
|
|
250
|
+
return;
|
|
251
|
+
const cur = getStack();
|
|
252
|
+
if (cur.length < 2)
|
|
253
|
+
return;
|
|
254
|
+
const popping = cur[cur.length - 1];
|
|
255
|
+
const next = cur[cur.length - 2];
|
|
256
|
+
setTransition({
|
|
257
|
+
kind: 'pop',
|
|
258
|
+
topEntry: popping,
|
|
259
|
+
underneathEntry: next,
|
|
260
|
+
progress: progress,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
function commitBackGesture() {
|
|
264
|
+
const cur = getStack();
|
|
265
|
+
if (cur.length >= 2) {
|
|
266
|
+
setStack(cur.slice(0, cur.length - 1));
|
|
267
|
+
}
|
|
268
|
+
setTransition(null);
|
|
269
|
+
}
|
|
270
|
+
function cancelBackGesture() {
|
|
271
|
+
setTransition(null);
|
|
272
|
+
}
|
|
126
273
|
const nav = {
|
|
127
274
|
push,
|
|
128
275
|
replace,
|
|
@@ -143,7 +290,55 @@ export function createNavigatorState(routes, initial) {
|
|
|
143
290
|
get parent() {
|
|
144
291
|
return null;
|
|
145
292
|
},
|
|
293
|
+
get transition() {
|
|
294
|
+
return transitionBox.value;
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
return {
|
|
298
|
+
nav,
|
|
299
|
+
routes,
|
|
300
|
+
_gesture: { beginBackGesture, commitBackGesture, cancelBackGesture },
|
|
301
|
+
_screens: createScreenRegistries(),
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Map-backed `_screens` controller. Pulled out as a tiny factory so test
|
|
306
|
+
* tooling can call it directly when asserting registry behaviour without
|
|
307
|
+
* standing up an entire navigator.
|
|
308
|
+
*
|
|
309
|
+
* Not reactive — `<EntryScope>` registers once at setup and unregisters at
|
|
310
|
+
* unmount, so reads from the navigator's chrome are point-in-time lookups,
|
|
311
|
+
* and the registry's own internal signals carry the reactive payload.
|
|
312
|
+
*/
|
|
313
|
+
function createScreenRegistries() {
|
|
314
|
+
const byKey = new Map();
|
|
315
|
+
// Reactive version tick — bumped on every register/unregister so consumers
|
|
316
|
+
// (HeaderBar's computeds) re-evaluate their lookups when entries come and
|
|
317
|
+
// go. `Map.get` itself isn't tracked, so without this a chrome component
|
|
318
|
+
// that renders before its target entry mounts would never see the late
|
|
319
|
+
// arrival of the registry.
|
|
320
|
+
const version = signal({ v: 0 });
|
|
321
|
+
return {
|
|
322
|
+
register(reg) {
|
|
323
|
+
byKey.set(reg.entry.key, reg);
|
|
324
|
+
// `register` is called from `<EntryScope>` setup, which itself
|
|
325
|
+
// runs inside a tracked scope. Read-then-write on `version`
|
|
326
|
+
// would self-loop, so we untrack the bump.
|
|
327
|
+
untrack(() => { version.v = version.v + 1; });
|
|
328
|
+
},
|
|
329
|
+
unregister(key) {
|
|
330
|
+
byKey.delete(key);
|
|
331
|
+
untrack(() => { version.v = version.v + 1; });
|
|
332
|
+
},
|
|
333
|
+
get(key) {
|
|
334
|
+
// Touch the version signal so the caller's reactive scope
|
|
335
|
+
// re-runs on the next register/unregister. The actual returned
|
|
336
|
+
// value still comes from the plain Map — registries themselves
|
|
337
|
+
// are signal-backed, so once a caller has one in hand they
|
|
338
|
+
// track the bits they care about (options/slots) directly.
|
|
339
|
+
void version.v;
|
|
340
|
+
return byKey.get(key);
|
|
341
|
+
},
|
|
146
342
|
};
|
|
147
|
-
return { nav, routes };
|
|
148
343
|
}
|
|
149
344
|
//# sourceMappingURL=core.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/navigator/core.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/navigator/core.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -46,6 +46,20 @@ export type Presentation = 'card' | 'modal' | 'fullScreen' | 'transparent-modal'
|
|
|
46
46
|
export interface RouteDefinition<Params extends StandardSchemaV1 | undefined = StandardSchemaV1 | undefined, Search extends StandardSchemaV1 | undefined = StandardSchemaV1 | undefined> {
|
|
47
47
|
/** Component factory or lazy importer. */
|
|
48
48
|
component: ComponentLike;
|
|
49
|
+
/**
|
|
50
|
+
* Fallback shown while a lazy `component` is loading.
|
|
51
|
+
*
|
|
52
|
+
* Set this only on routes whose `component` was created with `lazy(...)`.
|
|
53
|
+
* The fallback is rendered inside a `<Suspense>` boundary wrapping the
|
|
54
|
+
* screen mount, so the user sees this UI while the screen's chunk is
|
|
55
|
+
* being fetched. When omitted, lazy routes still work — the caller is
|
|
56
|
+
* responsible for placing its own `<Suspense>` boundary (e.g. above the
|
|
57
|
+
* `<NavigationRoot>` or inside the screen component).
|
|
58
|
+
*
|
|
59
|
+
* Accepts a component factory (`MyLoadingScreen`) or a function returning
|
|
60
|
+
* JSX (`() => <Spinner />`). Eager routes ignore this field.
|
|
61
|
+
*/
|
|
62
|
+
fallback?: ComponentLike | (() => unknown);
|
|
49
63
|
/** Standard-Schema validator for path params. Optional. */
|
|
50
64
|
params?: Params;
|
|
51
65
|
/** Standard-Schema validator for query/search params. Optional. */
|
|
@@ -159,4 +173,46 @@ export interface TransitionState {
|
|
|
159
173
|
/** Animation progress signal — typed loosely; cast at the runtime boundary. */
|
|
160
174
|
readonly progress: unknown;
|
|
161
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Per-screen display options written by `<Screen>` into its entry's registry.
|
|
178
|
+
*
|
|
179
|
+
* Read by persistent navigator chrome (the `<HeaderBar>` shipped in the
|
|
180
|
+
* `header` slice; `<TabBar>` later). All fields are optional — consumers
|
|
181
|
+
* apply sensible defaults (headerShown defaults to true, gestureEnabled to
|
|
182
|
+
* true, title falls back to the route name).
|
|
183
|
+
*
|
|
184
|
+
* `title` accepts a function so the header can be derived from reactive
|
|
185
|
+
* state (e.g. a user's display name signal). Plain strings are wrapped in
|
|
186
|
+
* a thunk by consumers when read.
|
|
187
|
+
*/
|
|
188
|
+
export interface ScreenOptions {
|
|
189
|
+
/** Header title. Either a static string or a getter (re-tracked each render). */
|
|
190
|
+
title?: string | (() => string);
|
|
191
|
+
/** When false, the navigator's header is hidden for this screen. Default true. */
|
|
192
|
+
headerShown?: boolean;
|
|
193
|
+
/** When false, the iOS edge-swipe-back gesture is disabled for this screen. Default true. */
|
|
194
|
+
gestureEnabled?: boolean;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Slot fills written by `<Screen.Header>` / `<Screen.HeaderLeft>` /
|
|
198
|
+
* `<Screen.HeaderRight>` / `<Screen.TabBarItem>`.
|
|
199
|
+
*
|
|
200
|
+
* Each fill is the rendered output of that sub-component's `default` slot,
|
|
201
|
+
* captured as a thunk so the navigator's persistent chrome can call it at
|
|
202
|
+
* render time. `tabBarItem` is a scoped slot — the consumer passes
|
|
203
|
+
* `{ active }` so the same screen's tab-bar item can style itself
|
|
204
|
+
* differently when focused vs. not.
|
|
205
|
+
*/
|
|
206
|
+
export interface ScreenSlotFills {
|
|
207
|
+
/** Full header replacement. When set, takes precedence over title + headerLeft/Right. */
|
|
208
|
+
header?: () => unknown;
|
|
209
|
+
/** Left-side header content (typically back arrow override). */
|
|
210
|
+
headerLeft?: () => unknown;
|
|
211
|
+
/** Right-side header content (typically action buttons). */
|
|
212
|
+
headerRight?: () => unknown;
|
|
213
|
+
/** Tab-bar item — scoped slot receives `{ active }` indicating focus. */
|
|
214
|
+
tabBarItem?: (ctx: {
|
|
215
|
+
active: boolean;
|
|
216
|
+
}) => unknown;
|
|
217
|
+
}
|
|
162
218
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed → URL: build the `url` field of an Href from a route's params/search.
|
|
3
|
+
*
|
|
4
|
+
* Mirror of parse.ts. Used by `hrefFor()` after schema validation succeeds.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Build the URL form of a route + params + search, or `null` if the route
|
|
8
|
+
* declares no `path` template (typed navigation still works — only deep-link
|
|
9
|
+
* serialization is unavailable).
|
|
10
|
+
*
|
|
11
|
+
* Params must already be valid for the route's schema (callers run this after
|
|
12
|
+
* `validateSync`). Search values are stringified as-is — schema-validated
|
|
13
|
+
* inputs survive round-tripping because `parseHref` re-runs the same schema.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildUrl(routeName: string, params: Record<string, unknown> | undefined, search: Record<string, unknown> | undefined): string | null;
|
|
16
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/url/build.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CACpB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC3C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC5C,MAAM,GAAG,IAAI,CAYf"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed → URL: build the `url` field of an Href from a route's params/search.
|
|
3
|
+
*
|
|
4
|
+
* Mirror of parse.ts. Used by `hrefFor()` after schema validation succeeds.
|
|
5
|
+
*/
|
|
6
|
+
import { formatSearch } from './format.js';
|
|
7
|
+
import { getCompiledPath, getRouteRegistry } from './registry.js';
|
|
8
|
+
/**
|
|
9
|
+
* Build the URL form of a route + params + search, or `null` if the route
|
|
10
|
+
* declares no `path` template (typed navigation still works — only deep-link
|
|
11
|
+
* serialization is unavailable).
|
|
12
|
+
*
|
|
13
|
+
* Params must already be valid for the route's schema (callers run this after
|
|
14
|
+
* `validateSync`). Search values are stringified as-is — schema-validated
|
|
15
|
+
* inputs survive round-tripping because `parseHref` re-runs the same schema.
|
|
16
|
+
*/
|
|
17
|
+
export function buildUrl(routeName, params, search) {
|
|
18
|
+
const registry = getRouteRegistry();
|
|
19
|
+
const compiled = getCompiledPath(registry, routeName);
|
|
20
|
+
if (!compiled)
|
|
21
|
+
return null;
|
|
22
|
+
const formatted = compiled.format(
|
|
23
|
+
// The compiler accepts string|number; we widen to unknown and let it
|
|
24
|
+
// String()-coerce. Booleans/null get filtered by the missing-param
|
|
25
|
+
// check, which is what we want — boolean path params don't exist.
|
|
26
|
+
(params ?? {}));
|
|
27
|
+
const querystring = formatSearch(search);
|
|
28
|
+
return querystring.length > 0 ? `${formatted}?${querystring}` : formatted;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/url/build.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAElE;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CACpB,SAAiB,EACjB,MAA2C,EAC3C,MAA2C;IAE3C,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACtD,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM;IAC7B,qEAAqE;IACrE,mEAAmE;IACnE,kEAAkE;IAClE,CAAC,MAAM,IAAI,EAAE,CAAoC,CACpD,CAAC;IACF,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACzC,OAAO,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path template compiler.
|
|
3
|
+
*
|
|
4
|
+
* Turns a route's `path` (e.g. `/users/:id/posts/:postId`) into a compiled
|
|
5
|
+
* object that can both match a URL pathname against it and format a typed
|
|
6
|
+
* params object back into a URL.
|
|
7
|
+
*
|
|
8
|
+
* Supported syntax (intentionally minimal for v1):
|
|
9
|
+
* - Literal segments: `/users`, `/users/me`
|
|
10
|
+
* - Named params: `:id` (matches `[^/]+`)
|
|
11
|
+
* - Trailing slashes tolerated on match
|
|
12
|
+
*
|
|
13
|
+
* Out of scope for v1 (future-compatible — additions won't break v1 paths):
|
|
14
|
+
* - Wildcards `*`
|
|
15
|
+
* - Optional params `:id?`
|
|
16
|
+
* - Typed/constrained params `:id<number>` or `:id(\\d+)`
|
|
17
|
+
*/
|
|
18
|
+
/** Result of compiling a path template — used by parse + format. */
|
|
19
|
+
export interface CompiledPath {
|
|
20
|
+
readonly source: string;
|
|
21
|
+
readonly paramNames: readonly string[];
|
|
22
|
+
/** Regex that matches a URL pathname. Captures are param values in order. */
|
|
23
|
+
readonly regex: RegExp;
|
|
24
|
+
/**
|
|
25
|
+
* Render a URL pathname for this template given param values. Each value
|
|
26
|
+
* is `encodeURIComponent`-encoded. Throws if a required `:name` is missing.
|
|
27
|
+
*/
|
|
28
|
+
format(params: Record<string, string | number>): string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Compile a path template. Throws on malformed input (duplicate param names,
|
|
32
|
+
* unexpected `:` syntax). Pure — safe to memoize.
|
|
33
|
+
*/
|
|
34
|
+
export declare function compilePath(template: string): CompiledPath;
|
|
35
|
+
//# sourceMappingURL=compile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/url/compile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,oEAAoE;AACpE,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC;CAC3D;AAID;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAkE1D"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path template compiler.
|
|
3
|
+
*
|
|
4
|
+
* Turns a route's `path` (e.g. `/users/:id/posts/:postId`) into a compiled
|
|
5
|
+
* object that can both match a URL pathname against it and format a typed
|
|
6
|
+
* params object back into a URL.
|
|
7
|
+
*
|
|
8
|
+
* Supported syntax (intentionally minimal for v1):
|
|
9
|
+
* - Literal segments: `/users`, `/users/me`
|
|
10
|
+
* - Named params: `:id` (matches `[^/]+`)
|
|
11
|
+
* - Trailing slashes tolerated on match
|
|
12
|
+
*
|
|
13
|
+
* Out of scope for v1 (future-compatible — additions won't break v1 paths):
|
|
14
|
+
* - Wildcards `*`
|
|
15
|
+
* - Optional params `:id?`
|
|
16
|
+
* - Typed/constrained params `:id<number>` or `:id(\\d+)`
|
|
17
|
+
*/
|
|
18
|
+
const PARAM_RE = /:([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
19
|
+
/**
|
|
20
|
+
* Compile a path template. Throws on malformed input (duplicate param names,
|
|
21
|
+
* unexpected `:` syntax). Pure — safe to memoize.
|
|
22
|
+
*/
|
|
23
|
+
export function compilePath(template) {
|
|
24
|
+
if (typeof template !== 'string') {
|
|
25
|
+
throw new TypeError(`compilePath: expected string, got ${typeof template}`);
|
|
26
|
+
}
|
|
27
|
+
if (template.length === 0) {
|
|
28
|
+
throw new Error('compilePath: path template must not be empty');
|
|
29
|
+
}
|
|
30
|
+
// Normalize: ensure leading `/`. Trailing slashes are tolerated on match,
|
|
31
|
+
// but the canonical formatted output preserves the template's trailing
|
|
32
|
+
// slash policy.
|
|
33
|
+
const normalized = template.startsWith('/') ? template : `/${template}`;
|
|
34
|
+
const paramNames = [];
|
|
35
|
+
// Build the regex by replacing :name with a capture group. We escape the
|
|
36
|
+
// surrounding literal text so paths with regex-special chars (`.`, `+`)
|
|
37
|
+
// match literally — only `:name` is treated as a placeholder.
|
|
38
|
+
let lastIndex = 0;
|
|
39
|
+
let pattern = '';
|
|
40
|
+
PARAM_RE.lastIndex = 0;
|
|
41
|
+
for (let m = PARAM_RE.exec(normalized); m !== null; m = PARAM_RE.exec(normalized)) {
|
|
42
|
+
const name = m[1];
|
|
43
|
+
if (paramNames.includes(name)) {
|
|
44
|
+
throw new Error(`compilePath: duplicate param name ':${name}' in '${template}'`);
|
|
45
|
+
}
|
|
46
|
+
paramNames.push(name);
|
|
47
|
+
pattern += escapeRegex(normalized.slice(lastIndex, m.index));
|
|
48
|
+
pattern += '([^/]+)';
|
|
49
|
+
lastIndex = m.index + m[0].length;
|
|
50
|
+
}
|
|
51
|
+
pattern += escapeRegex(normalized.slice(lastIndex));
|
|
52
|
+
// Trim a trailing slash from the pattern so `/users/` and `/users` both
|
|
53
|
+
// match `/users/:?`. We keep the formatter's output as-templated.
|
|
54
|
+
const matchPattern = pattern.endsWith('/') ? pattern.slice(0, -1) : pattern;
|
|
55
|
+
const regex = new RegExp(`^${matchPattern}/?$`);
|
|
56
|
+
return {
|
|
57
|
+
source: template,
|
|
58
|
+
paramNames,
|
|
59
|
+
regex,
|
|
60
|
+
format(params) {
|
|
61
|
+
let out = '';
|
|
62
|
+
let i = 0;
|
|
63
|
+
PARAM_RE.lastIndex = 0;
|
|
64
|
+
for (let m = PARAM_RE.exec(normalized); m !== null; m = PARAM_RE.exec(normalized)) {
|
|
65
|
+
const name = m[1];
|
|
66
|
+
const value = params[name];
|
|
67
|
+
if (value === undefined || value === null) {
|
|
68
|
+
throw new Error(`compilePath.format: missing required param ':${name}' for '${template}'`);
|
|
69
|
+
}
|
|
70
|
+
out += normalized.slice(i, m.index);
|
|
71
|
+
out += encodeURIComponent(String(value));
|
|
72
|
+
i = m.index + m[0].length;
|
|
73
|
+
}
|
|
74
|
+
out += normalized.slice(i);
|
|
75
|
+
return out;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const REGEX_SPECIALS = /[.*+?^${}()|[\]\\]/g;
|
|
80
|
+
function escapeRegex(s) {
|
|
81
|
+
return s.replace(REGEX_SPECIALS, '\\$&');
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=compile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/url/compile.ts"],"names":[],"mappings":"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"}
|