@sigx/lynx-navigation 0.4.0 → 0.4.2

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 (187) hide show
  1. package/dist/components/Drawer.js +74 -0
  2. package/dist/components/Drawer.js.map +1 -0
  3. package/dist/components/EdgeBackHandle.js +144 -0
  4. package/dist/components/EdgeBackHandle.js.map +1 -0
  5. package/dist/components/EntryScope.d.ts +1 -1
  6. package/dist/components/EntryScope.d.ts.map +1 -1
  7. package/dist/components/EntryScope.js +39 -0
  8. package/dist/components/EntryScope.js.map +1 -0
  9. package/dist/components/Header.js +103 -0
  10. package/dist/components/Header.js.map +1 -0
  11. package/dist/components/Layer.d.ts +2 -2
  12. package/dist/components/Layer.d.ts.map +1 -1
  13. package/dist/components/Layer.js +66 -0
  14. package/dist/components/Layer.js.map +1 -0
  15. package/dist/components/Link.d.ts +2 -2
  16. package/dist/components/Link.d.ts.map +1 -1
  17. package/dist/components/Link.js +51 -0
  18. package/dist/components/Link.js.map +1 -0
  19. package/dist/components/NavigationRoot.d.ts +2 -2
  20. package/dist/components/NavigationRoot.d.ts.map +1 -1
  21. package/dist/components/NavigationRoot.js +67 -0
  22. package/dist/components/NavigationRoot.js.map +1 -0
  23. package/dist/components/Screen.js +98 -0
  24. package/dist/components/Screen.js.map +1 -0
  25. package/dist/components/Stack.js +257 -0
  26. package/dist/components/Stack.js.map +1 -0
  27. package/dist/components/TabBar.d.ts +1 -1
  28. package/dist/components/TabBar.d.ts.map +1 -1
  29. package/dist/components/TabBar.js +63 -0
  30. package/dist/components/TabBar.js.map +1 -0
  31. package/dist/components/Tabs.d.ts +8 -3
  32. package/dist/components/Tabs.d.ts.map +1 -1
  33. package/dist/components/Tabs.js +168 -0
  34. package/dist/components/Tabs.js.map +1 -0
  35. package/dist/define-routes.d.ts +1 -1
  36. package/dist/define-routes.d.ts.map +1 -1
  37. package/{src/define-routes.d.ts → dist/define-routes.js} +4 -2
  38. package/dist/define-routes.js.map +1 -0
  39. package/dist/hooks/use-focus.js +87 -0
  40. package/dist/hooks/use-focus.js.map +1 -0
  41. package/dist/hooks/use-hardware-back.js +84 -0
  42. package/dist/hooks/use-hardware-back.js.map +1 -0
  43. package/dist/hooks/use-linking-nav.d.ts +3 -3
  44. package/dist/hooks/use-linking-nav.d.ts.map +1 -1
  45. package/dist/hooks/use-linking-nav.js +109 -0
  46. package/dist/hooks/use-linking-nav.js.map +1 -0
  47. package/dist/hooks/use-nav-internal.d.ts +2 -2
  48. package/dist/hooks/use-nav-internal.d.ts.map +1 -1
  49. package/dist/hooks/use-nav-internal.js +55 -0
  50. package/dist/hooks/use-nav-internal.js.map +1 -0
  51. package/dist/hooks/use-nav-serializer.d.ts +1 -1
  52. package/dist/hooks/use-nav-serializer.d.ts.map +1 -1
  53. package/dist/hooks/use-nav-serializer.js +181 -0
  54. package/dist/hooks/use-nav-serializer.js.map +1 -0
  55. package/dist/hooks/use-nav.d.ts +2 -2
  56. package/dist/hooks/use-nav.d.ts.map +1 -1
  57. package/dist/hooks/use-nav.js +11 -0
  58. package/dist/hooks/use-nav.js.map +1 -0
  59. package/dist/hooks/use-params.d.ts +1 -1
  60. package/dist/hooks/use-params.d.ts.map +1 -1
  61. package/{src/hooks/use-params.d.ts → dist/hooks/use-params.js} +6 -2
  62. package/dist/hooks/use-params.js.map +1 -0
  63. package/dist/hooks/use-screen-chrome.d.ts +1 -1
  64. package/dist/hooks/use-screen-chrome.d.ts.map +1 -1
  65. package/dist/hooks/use-screen-chrome.js +102 -0
  66. package/dist/hooks/use-screen-chrome.js.map +1 -0
  67. package/dist/hooks/use-screen-options.d.ts +1 -1
  68. package/dist/hooks/use-screen-options.d.ts.map +1 -1
  69. package/dist/hooks/use-screen-options.js +43 -0
  70. package/dist/hooks/use-screen-options.js.map +1 -0
  71. package/dist/hooks/use-search.d.ts +1 -1
  72. package/dist/hooks/use-search.d.ts.map +1 -1
  73. package/{src/hooks/use-search.d.ts → dist/hooks/use-search.js} +6 -2
  74. package/dist/hooks/use-search.js.map +1 -0
  75. package/dist/href.d.ts +2 -2
  76. package/dist/href.d.ts.map +1 -1
  77. package/dist/href.js +57 -0
  78. package/dist/href.js.map +1 -0
  79. package/dist/index.d.ts +33 -33
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +30 -1160
  82. package/dist/index.js.map +1 -1
  83. package/dist/internal/layer-plan.d.ts +1 -1
  84. package/dist/internal/layer-plan.d.ts.map +1 -1
  85. package/dist/internal/layer-plan.js +102 -0
  86. package/dist/internal/layer-plan.js.map +1 -0
  87. package/dist/internal/screen-registry.d.ts +1 -1
  88. package/dist/internal/screen-registry.d.ts.map +1 -1
  89. package/{src/internal/screen-registry.d.ts → dist/internal/screen-registry.js} +32 -21
  90. package/dist/internal/screen-registry.js.map +1 -0
  91. package/{src/internal/screen-width.d.ts → dist/internal/screen-width.js} +17 -2
  92. package/dist/internal/screen-width.js.map +1 -0
  93. package/dist/navigator/core.d.ts +3 -3
  94. package/dist/navigator/core.d.ts.map +1 -1
  95. package/dist/navigator/core.js +394 -0
  96. package/dist/navigator/core.js.map +1 -0
  97. package/dist/register.d.ts +1 -1
  98. package/dist/register.d.ts.map +1 -1
  99. package/dist/register.js +2 -0
  100. package/dist/register.js.map +1 -0
  101. package/dist/types.js +9 -0
  102. package/dist/types.js.map +1 -0
  103. package/dist/url/build.js +30 -0
  104. package/dist/url/build.js.map +1 -0
  105. package/dist/url/compile.js +83 -0
  106. package/dist/url/compile.js.map +1 -0
  107. package/dist/url/format.js +102 -0
  108. package/dist/url/format.js.map +1 -0
  109. package/dist/url/index.d.ts +6 -6
  110. package/dist/url/index.d.ts.map +1 -1
  111. package/dist/url/index.js +13 -0
  112. package/dist/url/index.js.map +1 -0
  113. package/dist/url/parse.d.ts +1 -1
  114. package/dist/url/parse.d.ts.map +1 -1
  115. package/dist/url/parse.js +94 -0
  116. package/dist/url/parse.js.map +1 -0
  117. package/dist/url/registry.d.ts +2 -2
  118. package/dist/url/registry.d.ts.map +1 -1
  119. package/{src/url/registry.d.ts → dist/url/registry.js} +28 -12
  120. package/dist/url/registry.js.map +1 -0
  121. package/dist/url/validate.d.ts +1 -1
  122. package/dist/url/validate.d.ts.map +1 -1
  123. package/dist/url/validate.js +37 -0
  124. package/dist/url/validate.js.map +1 -0
  125. package/package.json +16 -13
  126. package/src/components/EdgeBackHandle.tsx +2 -2
  127. package/src/components/EntryScope.tsx +3 -3
  128. package/src/components/Header.tsx +3 -3
  129. package/src/components/Layer.tsx +3 -3
  130. package/src/components/Link.tsx +4 -4
  131. package/src/components/NavigationRoot.tsx +6 -6
  132. package/src/components/Screen.tsx +3 -3
  133. package/src/components/Stack.tsx +8 -8
  134. package/src/components/TabBar.tsx +1 -1
  135. package/src/components/Tabs.tsx +8 -3
  136. package/src/define-routes.ts +1 -1
  137. package/src/hooks/use-focus.ts +2 -2
  138. package/src/hooks/use-hardware-back.ts +1 -1
  139. package/src/hooks/use-linking-nav.ts +4 -4
  140. package/src/hooks/use-nav-internal.ts +2 -2
  141. package/src/hooks/use-nav-serializer.ts +3 -3
  142. package/src/hooks/use-nav.ts +2 -2
  143. package/src/hooks/use-params.ts +2 -2
  144. package/src/hooks/use-screen-chrome.ts +3 -3
  145. package/src/hooks/use-screen-options.ts +3 -3
  146. package/src/hooks/use-search.ts +2 -2
  147. package/src/href.ts +6 -6
  148. package/src/index.ts +33 -33
  149. package/src/internal/layer-plan.ts +2 -2
  150. package/src/internal/screen-registry.ts +1 -1
  151. package/src/navigator/core.ts +3 -3
  152. package/src/register.ts +1 -1
  153. package/src/url/build.ts +2 -2
  154. package/src/url/index.ts +6 -6
  155. package/src/url/parse.ts +6 -6
  156. package/src/url/registry.ts +3 -3
  157. package/src/url/validate.ts +1 -1
  158. package/src/components/Drawer.d.ts +0 -55
  159. package/src/components/EdgeBackHandle.d.ts +0 -1
  160. package/src/components/EntryScope.d.ts +0 -25
  161. package/src/components/Header.d.ts +0 -6
  162. package/src/components/Layer.d.ts +0 -33
  163. package/src/components/Link.d.ts +0 -60
  164. package/src/components/NavigationRoot.d.ts +0 -36
  165. package/src/components/Screen.d.ts +0 -97
  166. package/src/components/Stack.d.ts +0 -90
  167. package/src/components/TabBar.d.ts +0 -38
  168. package/src/components/Tabs.d.ts +0 -109
  169. package/src/hooks/use-focus.d.ts +0 -45
  170. package/src/hooks/use-hardware-back.d.ts +0 -37
  171. package/src/hooks/use-linking-nav.d.ts +0 -91
  172. package/src/hooks/use-nav-internal.d.ts +0 -91
  173. package/src/hooks/use-nav-serializer.d.ts +0 -82
  174. package/src/hooks/use-nav.d.ts +0 -111
  175. package/src/hooks/use-screen-chrome.d.ts +0 -18
  176. package/src/hooks/use-screen-options.d.ts +0 -2
  177. package/src/href.d.ts +0 -54
  178. package/src/index.d.ts +0 -39
  179. package/src/internal/layer-plan.d.ts +0 -68
  180. package/src/navigator/core.d.ts +0 -96
  181. package/src/register.d.ts +0 -37
  182. package/src/types.d.ts +0 -217
  183. package/src/url/build.d.ts +0 -15
  184. package/src/url/compile.d.ts +0 -34
  185. package/src/url/format.d.ts +0 -28
  186. package/src/url/parse.d.ts +0 -20
  187. package/src/url/validate.d.ts +0 -23
@@ -1,91 +0,0 @@
1
- import { type SharedValue } from '@sigx/lynx';
2
- import type { ScreenRegistry } from '../internal/screen-registry';
3
- import type { RouteMap, StackEntry } from '../types';
4
- /**
5
- * Internal injectable: the `StackEntry` the calling screen was rendered for.
6
- *
7
- * Provided by `<EntryScope>` which `<Stack>` and `<ScreenContainer>` wrap
8
- * around each screen component mount. Screens use this to derive their own
9
- * focus state (`useIsFocused`, `useFocusEffect`) without having to track
10
- * `entry.key` themselves.
11
- *
12
- * Default throws so calling `useIsFocused()` outside a screen mounted by a
13
- * navigator surfaces a clear error rather than silently returning `false`.
14
- */
15
- export declare const useCurrentEntry: import("@sigx/runtime-core").InjectableFunction<StackEntry<string, unknown, unknown>>;
16
- /**
17
- * Soft companion to {@link useCurrentEntry} — returns the current scope's
18
- * entry if any, `null` when called outside an `<EntryScope>` instead of
19
- * throwing. Provided alongside the strict version by `<EntryScope>`.
20
- *
21
- * Used by chrome consumers (`useScreenChrome`) where "no scoped entry"
22
- * is a legitimate state (a Stack chrome slot lives outside the screen's
23
- * EntryScope) and the caller wants to soft-fallback to the navigator's
24
- * destination entry rather than crash.
25
- */
26
- export declare const useCurrentEntryOptional: import("@sigx/runtime-core").InjectableFunction<StackEntry<string, unknown, unknown> | null>;
27
- /**
28
- * Internal injectable: the route registry passed into `<NavigationRoot>`.
29
- * Components (Stack, Screen) read this to look up route definitions by name.
30
- *
31
- * Not exported from the package barrel — use `useNav()` for navigation, and
32
- * the registry is implicit from `<NavigationRoot routes={...}>`.
33
- */
34
- export declare const useNavRoutes: import("@sigx/runtime-core").InjectableFunction<RouteMap>;
35
- /**
36
- * Internal injectable: low-level navigator handles used by the edge-back
37
- * gesture. Holds the progress SharedValue (so gesture worklets can write it
38
- * directly on MT) plus BG-side begin/commit/cancel functions invoked via
39
- * `runOnBackground` from gesture worklets.
40
- *
41
- * `progress` is `null` when the navigator was created with `animated={false}`
42
- * (e.g. tests). `beginBackGesture` is also a no-op in that case.
43
- */
44
- export interface NavInternals {
45
- /** MT-driven transition progress; null when animations are disabled. */
46
- readonly progress: SharedValue<number> | null;
47
- /**
48
- * Set transition state for a gesture-driven pop. Does not start any
49
- * automatic animation — the gesture worklet writes `progress` directly
50
- * per frame, then animates to the commit/cancel endpoint on release.
51
- */
52
- beginBackGesture(): void;
53
- /** Commit the back gesture: pop top entry + clear transition. */
54
- commitBackGesture(): void;
55
- /** Cancel the back gesture: clear transition without popping. */
56
- cancelBackGesture(): void;
57
- /** Whether the user opted into the edge-swipe-back gesture. */
58
- readonly edgeSwipeEnabled: boolean;
59
- /**
60
- * Cross-entry screen registry controller. `<EntryScope>` calls
61
- * `register` on mount and `unregister` on unmount. Persistent chrome
62
- * (HeaderBar / TabBar — later slices) calls `get(entryKey)` to read
63
- * the focused screen's options + slot fills without remounting itself.
64
- */
65
- readonly screens: {
66
- register(registry: ScreenRegistry): void;
67
- /**
68
- * Identity-checked: only removes the entry if `registry` is the
69
- * one currently registered under its `entry.key`. A no-op when
70
- * a newer registry has already taken that slot (which happens
71
- * at the transition→idle handoff, where a fresh `<EntryScope>`
72
- * for the same entry mounts before the old one's unmount fires).
73
- */
74
- unregister(registry: ScreenRegistry): void;
75
- get(entryKey: string): ScreenRegistry | undefined;
76
- };
77
- }
78
- export declare const useNavInternals: import("@sigx/runtime-core").InjectableFunction<NavInternals>;
79
- /**
80
- * Internal injectable: the calling screen's `ScreenRegistry`.
81
- *
82
- * Provided by `<EntryScope>` alongside `useCurrentEntry`. The `<Screen>`
83
- * component and its slot-filling sub-components write options and slot
84
- * fills here; the navigator's persistent chrome (HeaderBar, TabBar — later
85
- * slices) reads from this registry via `getScreenRegistry(key)` on the
86
- * navigator state, which keys into a cross-entry map.
87
- *
88
- * Throws when used outside an EntryScope so calling `<Screen>` at the app
89
- * root surfaces a clear error rather than silently no-op'ing.
90
- */
91
- export declare const useScreenRegistry: import("@sigx/runtime-core").InjectableFunction<ScreenRegistry>;
@@ -1,82 +0,0 @@
1
- import type { StackEntry } from '../types';
2
- /**
3
- * Plain JSON snapshot of a navigator. The whole point of holding navigation
4
- * state in signals is that this is a one-liner — `JSON.stringify(nav.stack)`.
5
- *
6
- * Shape is deliberately minimal:
7
- *
8
- * {
9
- * version: 1,
10
- * stack: [ { key, route, params, search, state, presentation }, ... ],
11
- * }
12
- *
13
- * `version` lets future schema migrations (or hard breakage) reject old
14
- * snapshots cleanly rather than restoring incompatible state.
15
- *
16
- * Per spec resolved-decisions: only the root navigator is persisted in v1.
17
- * Per-tab / nested-navigator stacks are deferred until the nested-navigators
18
- * follow-up slice lands.
19
- */
20
- export interface NavSnapshot {
21
- version: number;
22
- stack: StackEntry[];
23
- }
24
- export declare const NAV_SNAPSHOT_VERSION = 1;
25
- /**
26
- * Adapter contract for `useNavSerializer`. Implementations bridge to whatever
27
- * storage backend the host app uses — `@sigx/lynx-storage`, `localStorage`,
28
- * an MMKV bridge, etc. Both methods may be async; the hook awaits load before
29
- * applying anything to the stack and fires save in a debounced manner.
30
- *
31
- * - `load()` returns `null` (or rejects) when no snapshot exists, when the
32
- * stored payload is malformed, or when the host opts not to restore on
33
- * this launch.
34
- * - `save(snapshot)` persists the latest stack. The hook drops save errors
35
- * on the floor — losing a write is preferable to crashing the navigator.
36
- */
37
- export interface NavStorageAdapter {
38
- load(): Promise<NavSnapshot | null> | NavSnapshot | null;
39
- save(snapshot: NavSnapshot): Promise<void> | void;
40
- }
41
- export interface UseNavSerializerOptions {
42
- storage: NavStorageAdapter;
43
- /**
44
- * Trailing-edge debounce in ms before pushing a stack change to storage.
45
- * Defaults to 250ms — quick enough that a force-quit one tick after a
46
- * push is recoverable, slow enough that rapid `pop/push` flurries
47
- * coalesce into one write.
48
- */
49
- debounceMs?: number;
50
- /**
51
- * Optional callback after a successful restore — lets the host run
52
- * post-restore wiring (analytics, focus shifts, etc.) only when we
53
- * actually applied state, not on every mount.
54
- */
55
- onRestored?: (snapshot: NavSnapshot) => void;
56
- /**
57
- * Optional callback when a snapshot is rejected (validation failed or
58
- * load threw). Defaults to silent. Useful for logging during migration.
59
- */
60
- onRestoreError?: (reason: 'version' | 'shape' | 'unknown-route' | 'load-threw', err?: unknown) => void;
61
- }
62
- /**
63
- * Wire a navigator's stack to a storage adapter.
64
- *
65
- * On mount:
66
- * 1. Call `storage.load()`.
67
- * 2. Validate the snapshot (version match, every entry's route still
68
- * registered).
69
- * 3. On success, `nav.reset({ stack })` to apply.
70
- * 4. On any failure, leave the stack alone (initial route remains).
71
- *
72
- * Then subscribe to `nav.stack` and call `storage.save(snapshot)` debounced.
73
- *
74
- * Why we don't validate `params` / `search` against schemas here: schemas
75
- * are part of the route definition, and re-running them across all entries
76
- * on every launch costs more than it's worth. The contract is "entries were
77
- * validated when they were pushed; if the schema has since changed in a
78
- * breaking way, bump `version` to reject old snapshots wholesale." Callers
79
- * who want a stricter check can run their own validation in
80
- * `storage.load()` and return `null` on mismatch.
81
- */
82
- export declare function useNavSerializer(options: UseNavSerializerOptions): void;
@@ -1,111 +0,0 @@
1
- import type { RegisteredRoutes, RouteId, RouteParams, RouteSearch } from '../register';
2
- import type { PopOptions, PushOptions, RouteRequiresParams, StackEntry, TransitionState } from '../types';
3
- /**
4
- * Subset of registered route names that declare a `params` schema (and so
5
- * require a `params` argument when navigating).
6
- *
7
- * Computed via mapped-type filtering rather than a conditional inside the
8
- * method signature: when a conditional like `RouteRequiresParams<R[K]>` is
9
- * embedded inside a generic method parameter, TS evaluates it at definition
10
- * time with K bound to the *whole* union of route ids — which distributes
11
- * `RouteRequiresParams` over every route and collapses the result to
12
- * `boolean`, breaking the conditional. Filtering once at the type level avoids
13
- * the issue and produces clean overload candidates.
14
- */
15
- export type RoutesWithParams = {
16
- [K in RouteId]: RouteRequiresParams<RegisteredRoutes[K]> extends true ? K : never;
17
- }[RouteId];
18
- /** Routes that don't declare a `params` schema. */
19
- export type RoutesWithoutParams = Exclude<RouteId, RoutesWithParams>;
20
- /**
21
- * The navigator handle returned by `useNav()`.
22
- *
23
- * Read access (`current`, `stack`, `canGoBack`) is reactive — these properties
24
- * are getters that read from the underlying stack signal, so accessing them
25
- * inside a component's render function (or inside `effect` / `computed`) takes
26
- * a reactive dependency. Mutating methods (`push`, `pop`, etc.) trigger the
27
- * dependents to update.
28
- *
29
- * `push` and `replace` are split into two overloads — one for routes without a
30
- * params schema (no params arg) and one for routes with a params schema
31
- * (params required). See `RoutesWithParams` above for why this isn't a single
32
- * conditional return type.
33
- */
34
- export interface Nav {
35
- /** Push a route that has no params schema. */
36
- push<K extends RoutesWithoutParams>(name: K, search?: RouteSearch<K>, options?: PushOptions): void;
37
- /** Push a route that requires params. */
38
- push<K extends RoutesWithParams>(name: K, params: RouteParams<K>, search?: RouteSearch<K>, options?: PushOptions): void;
39
- /** Replace the top entry — same overload pattern as push. */
40
- replace<K extends RoutesWithoutParams>(name: K, search?: RouteSearch<K>, options?: PushOptions): void;
41
- replace<K extends RoutesWithParams>(name: K, params: RouteParams<K>, search?: RouteSearch<K>, options?: PushOptions): void;
42
- /** Pop one or more entries off the top of the stack. */
43
- pop(count?: number, options?: PopOptions): void;
44
- /** Pop entries until the named route is at the top. */
45
- popTo<K extends RouteId>(name: K): void;
46
- /** Pop all the way to the root entry. */
47
- popToRoot(): void;
48
- /** Wholesale-replace the stack. */
49
- reset(state: {
50
- stack: ReadonlyArray<StackEntry>;
51
- }): void;
52
- /** Dismiss the topmost modal stack (no-op if none active). */
53
- dismiss(): void;
54
- /** Currently-focused entry. Reactive via property access. */
55
- readonly current: StackEntry;
56
- /** Full stack, top last. Reactive. */
57
- readonly stack: ReadonlyArray<StackEntry>;
58
- /** Whether the user can go back from the current entry. Reactive. */
59
- readonly canGoBack: boolean;
60
- /**
61
- * Parent navigator (e.g. the root nav above a per-tab `<Stack>`), or null
62
- * at the root. Set when a `<Stack>` mints its own navigator via
63
- * `<Stack initialRoute="…">` — that stack's `useNav()` returns a nav
64
- * whose `parent` is the enclosing nav.
65
- *
66
- * `push` calls for routes whose resolved presentation is non-`card`
67
- * (`modal` / `fullScreen` / `transparent-modal`) escalate up the
68
- * `parent` chain automatically — you don't normally need to reach
69
- * through `parent` to present modals. `parent` is exposed as an escape
70
- * hatch for power users (e.g. imperative `parent.pop()` from a child
71
- * stack). Avoid pushing card routes onto `parent` directly — that
72
- * defeats per-tab stack isolation.
73
- */
74
- readonly parent: Nav | null;
75
- /**
76
- * Whether this navigator is part of the currently-focused chain. True
77
- * for the root nav at all times; for a nested nav (e.g. a per-tab
78
- * stack), true only when its host entry is the top of `parent`, the
79
- * parent itself is locally focused, and any extra gate (e.g. the
80
- * enclosing tab is active) reports active.
81
- *
82
- * Reactive. `useIsFocused()` ANDs `nav.current.key === myKey` with
83
- * `nav.isLocallyFocused`.
84
- */
85
- readonly isLocallyFocused: boolean;
86
- /**
87
- * @internal
88
- * Set of child navigators (per-tab `<Stack>` instances) that have
89
- * registered themselves under this nav. Used by `useHardwareBack` to
90
- * find the deepest currently-focused nav and route the back press
91
- * there before falling back up the chain.
92
- *
93
- * Not part of the public API — leading-underscore marks it as
94
- * implementation detail.
95
- */
96
- readonly _children: Set<Nav>;
97
- /**
98
- * In-flight transition, or null when navigation is at rest. Reactive —
99
- * `<Stack>` reads this to decide whether to render one screen or two
100
- * (during a slide transition both the outgoing and incoming screens
101
- * stay mounted with `useAnimatedStyle`-driven transforms).
102
- */
103
- readonly transition: TransitionState | null;
104
- }
105
- /**
106
- * Access the innermost navigator. Provided by `<NavigationRoot>` via
107
- * `defineProvide`. Throws when called outside a NavigationRoot subtree.
108
- *
109
- * Mirrors `@sigx/router`'s `useRouter` pattern (`packages/router/src/router.ts:30`).
110
- */
111
- export declare const useNav: import("@sigx/runtime-core").InjectableFunction<Nav>;
@@ -1,18 +0,0 @@
1
- import type { ScreenSlotFills } from '../types';
2
- export interface ScreenChrome {
3
- /** Resolved screen title — `options.title` (string or getter) or the route name as fallback. Reactive. */
4
- readonly title: string;
5
- /** Whether the header should render. Defaults to true unless the screen set `headerShown: false`. Reactive. */
6
- readonly headerShown: boolean;
7
- /** True when the current stack has more than one entry — i.e. there's something to pop back to. Reactive. */
8
- readonly canGoBack: boolean;
9
- /** Pop the top entry. No-op when `!canGoBack`. */
10
- pop(): void;
11
- /** Full header override slot, if `<Screen.Header>` was set. Render its return value in place of the default layout. */
12
- readonly header: ScreenSlotFills['header'] | undefined;
13
- /** Left-aligned slot (typically a back button). Reactive. */
14
- readonly headerLeft: ScreenSlotFills['headerLeft'] | undefined;
15
- /** Right-aligned slot (typically actions). Reactive. */
16
- readonly headerRight: ScreenSlotFills['headerRight'] | undefined;
17
- }
18
- export declare function useScreenChrome(): ScreenChrome;
@@ -1,2 +0,0 @@
1
- import type { ScreenOptions } from '../types';
2
- export declare function useScreenOptions(optionsOrFn: ScreenOptions | (() => ScreenOptions)): void;
package/src/href.d.ts DELETED
@@ -1,54 +0,0 @@
1
- import type { RouteId, RouteParams, RouteSearch } from './register';
2
- import type { RoutesWithoutParams, RoutesWithParams } from './hooks/use-nav';
3
- /**
4
- * A typed reference to a navigation target — what `<Link to={...}>` consumes
5
- * and what `hrefFor()` produces.
6
- *
7
- * Holds both the typed pieces (route name, params, search) and the serialized
8
- * URL form (when the route declares a `path` template). Either side can drive
9
- * navigation — typed for in-app links, URL for deep links / sharing.
10
- */
11
- export interface Href<K extends RouteId = RouteId> {
12
- readonly route: K;
13
- readonly params: RouteParams<K>;
14
- readonly search: RouteSearch<K>;
15
- /** URL form. `null` when the route declares no `path` template. */
16
- readonly url: string | null;
17
- }
18
- /**
19
- * Build a typed Href for a given route. Validates params against the route's
20
- * schema at runtime; type-checks them at compile time.
21
- *
22
- * Overloaded the same way as `nav.push` — one signature for routes without a
23
- * params schema, one for routes that require params. See `RoutesWithParams`
24
- * in `./hooks/use-nav.js` for why this isn't expressed as a single conditional.
25
- *
26
- * Requires a `<NavigationRoot>` (or an explicit `_setRouteRegistry` call) to
27
- * have run — the URL form is built against the active route registry. The
28
- * typed pieces (`route`, `params`, `search`) are returned regardless; `url`
29
- * is `null` when the route declares no `path` template.
30
- *
31
- * Schema validation errors throw — pass already-validated values from
32
- * `useParams` / `useSearch` to round-trip safely, or wrap in try/catch when
33
- * building hrefs from external input.
34
- *
35
- * @example
36
- * ```ts
37
- * hrefFor('profile', { id: '42' }); // → { route, params, search: {}, url: '/users/42' }
38
- * hrefFor('profile', { id: '42' }, { tab: 'about' });
39
- * hrefFor('home'); // params arg omitted (no schema)
40
- * ```
41
- */
42
- export declare function hrefFor<K extends RoutesWithoutParams>(name: K, search?: RouteSearch<K>): Href<K>;
43
- export declare function hrefFor<K extends RoutesWithParams>(name: K, params: RouteParams<K>, search?: RouteSearch<K>): Href<K>;
44
- /**
45
- * Parse a URL string into a typed Href against the registered routes.
46
- * Returns `null` if no route's `path` template matches the URL or if the
47
- * extracted params/search fail the route's schema validation.
48
- *
49
- * Accepts both absolute (`myapp://host/path?q`) and pathname-only
50
- * (`/path?q`) forms — the pathname is matched against each route's
51
- * compiled template. Iteration order is the registration order; first match
52
- * wins.
53
- */
54
- export declare function parseHref(url: string): Href | null;
package/src/index.d.ts DELETED
@@ -1,39 +0,0 @@
1
- /**
2
- * @sigx/lynx-navigation — type-first native stack router.
3
- *
4
- * Phase 0.1 (current): typed registry, stack runtime, NavigationRoot + Stack.
5
- * Coming next: Screen with slot-based header API, MTS transitions, Tabs.
6
- */
7
- export { defineRoutes } from './define-routes';
8
- export type { Register, RegisteredRoutes, RouteId, RouteParams, RouteSearch } from './register';
9
- export { useNav } from './hooks/use-nav';
10
- export type { Nav, RoutesWithoutParams, RoutesWithParams } from './hooks/use-nav';
11
- export { useParams } from './hooks/use-params';
12
- export { useSearch } from './hooks/use-search';
13
- export { useHardwareBack } from './hooks/use-hardware-back';
14
- export { useLinkingNav } from './hooks/use-linking-nav';
15
- export type { UseLinkingNavOptions } from './hooks/use-linking-nav';
16
- export { useIsFocused, useFocusEffect } from './hooks/use-focus';
17
- export { useScreenOptions } from './hooks/use-screen-options';
18
- export { useScreenChrome } from './hooks/use-screen-chrome';
19
- export type { ScreenChrome } from './hooks/use-screen-chrome';
20
- export { useNavSerializer, NAV_SNAPSHOT_VERSION, } from './hooks/use-nav-serializer';
21
- export type { NavSnapshot, NavStorageAdapter, UseNavSerializerOptions, } from './hooks/use-nav-serializer';
22
- export { hrefFor, parseHref } from './href';
23
- export type { Href } from './href';
24
- export { _setRouteRegistry, _clearRouteRegistry } from './url/registry';
25
- export { compilePath } from './url/compile';
26
- export type { CompiledPath } from './url/compile';
27
- export { NavigationRoot } from './components/NavigationRoot';
28
- export { Stack } from './components/Stack';
29
- export { Screen } from './components/Screen';
30
- export { Header } from './components/Header';
31
- export { Tabs, useTabs } from './components/Tabs';
32
- export type { TabInfo, TabsNav } from './components/Tabs';
33
- export { TabBar } from './components/TabBar';
34
- export type { TabRenderContext } from './components/TabBar';
35
- export { Drawer, useDrawer } from './components/Drawer';
36
- export type { DrawerNav } from './components/Drawer';
37
- export { Link } from './components/Link';
38
- export type { LinkProps } from './components/Link';
39
- export type { ComponentLike, EmptyParams, InferOutput, ParamsOf, PopOptions, Presentation, PushOptions, RouteDefinition, RouteMap, RouteRequiresParams, ScreenOptions, ScreenSlotFills, SearchOf, StackEntry, StandardSchemaV1, TransitionKind, TransitionRole, TransitionState, } from './types';
@@ -1,68 +0,0 @@
1
- /**
2
- * Pure layer-plan computation for `<Stack>`'s render.
3
- *
4
- * Given (stack, transition, progress), produces an ordered list of
5
- * `Layer`s — each is an entry to render plus an optional transform
6
- * spec for animation. The Stack render emits one absolutely-positioned
7
- * `<view>` per layer, stacked bottom-to-top in document order.
8
- *
9
- * Why this is its own module: the layer-selection logic is the only
10
- * non-obvious part of the navigator's render path, and the rules are
11
- * easier to read (and unit-test) as a pure function over the
12
- * navigator's state than as inline render branches.
13
- *
14
- * Rules:
15
- *
16
- * - **Idle (no transition).** Render the topmost non-overlay entry
17
- * as the base, plus every overlay entry above it. Overlays
18
- * (`modal` / `fullScreen` / `transparent-modal`) keep their
19
- * underneath mounted; cards replace their underneath in the base
20
- * layer.
21
- *
22
- * - **Card transition.** Two layers: the underneath entry (animated
23
- * with the parallax-card-underneath spec) and the top entry
24
- * (animated with the slide-in-from-right spec). After the
25
- * transition completes, the idle rule kicks in — the underneath
26
- * unmounts because the new top becomes the sole base.
27
- *
28
- * - **Overlay transition.** The full idle layer stack up through the
29
- * underneath entry stays static (no transform). The animated top
30
- * is the only layer with a transform. After the transition, the
31
- * overlay either joins the static idle stack (push) or unmounts
32
- * (pop).
33
- *
34
- * The Layer.key for the Stack render is
35
- * `layer-${entry.key}-${animVariant(layer.animation)}`. The variant
36
- * suffix forces a remount when an entry transitions from animated to
37
- * static (or vice versa) — `useAnimatedStyle` can't re-bind mid-life,
38
- * so we get a fresh `useAnimatedStyle` call per animation state.
39
- * Modal underneath layers never animate, so they stay statically
40
- * keyed across the modal lifecycle and their state (per-tab Stack,
41
- * scroll, in-flight inputs) survives.
42
- */
43
- import type { SharedValue } from '@sigx/lynx';
44
- import type { Presentation, StackEntry, TransitionState } from '../types';
45
- export type LayerAnimation = {
46
- axis: 'translateX' | 'translateY';
47
- inputRange: readonly [number, number];
48
- outputRange: readonly [number, number];
49
- progress: SharedValue<number>;
50
- };
51
- export interface Layer {
52
- /** The entry whose component renders inside this layer. */
53
- readonly entry: StackEntry;
54
- /** When non-null, the layer's host view binds a `useAnimatedStyle` mapper. */
55
- readonly animation: LayerAnimation | null;
56
- }
57
- export declare function isOverlayPresentation(p: Presentation): boolean;
58
- /**
59
- * Suffix used in a layer's render key. Stable for the layer's
60
- * lifetime (same entry, same animation kind) and changes when the
61
- * animation transitions on/off so the Layer remounts and rebinds.
62
- */
63
- export declare function animationVariant(animation: LayerAnimation | null): string;
64
- /**
65
- * Compute the visible-layer list for one render of `<Stack>`. Pure —
66
- * unit-testable independently of the renderer.
67
- */
68
- export declare function computeLayers(stack: readonly StackEntry[], transition: TransitionState | null, progress: SharedValue<number> | null): Layer[];
@@ -1,96 +0,0 @@
1
- import { type SharedValue } from '@sigx/lynx';
2
- import type { Nav } from '../hooks/use-nav';
3
- import type { ScreenRegistry } from '../internal/screen-registry';
4
- import type { RouteMap, StackEntry } from '../types';
5
- /**
6
- * The reactive backing state for one navigator instance.
7
- *
8
- * Two reactive signals drive the public surface:
9
- * - `stack` is the entry array (read via `nav.stack` / `nav.current`).
10
- * - `transition` is non-null only while a push/pop animation is in flight;
11
- * `<Stack>` reads it to decide whether to render one screen or two.
12
- *
13
- * Pop is committed *after* its slide animation completes — `nav.canGoBack`
14
- * stays true during the slide, then flips when the entry actually leaves the
15
- * stack. Push commits its stack mutation immediately and animates the new
16
- * entry in.
17
- */
18
- export interface NavigatorState {
19
- readonly nav: Nav;
20
- readonly routes: RouteMap;
21
- /**
22
- * Internal: BG-side gesture-back controller used by `<EdgeBackHandle>`.
23
- * The `progress` SharedValue is wired here so a gesture worklet can write
24
- * it directly on MT; the begin/commit/cancel methods set the transition
25
- * state appropriately without driving their own auto-animation (the
26
- * gesture worklet is in charge of that).
27
- */
28
- readonly _gesture: {
29
- beginBackGesture(): void;
30
- commitBackGesture(): void;
31
- cancelBackGesture(): void;
32
- };
33
- /**
34
- * Internal: cross-entry `<Screen>` registry lookup.
35
- *
36
- * Each `<EntryScope>` registers its `ScreenRegistry` here on mount and
37
- * removes it on unmount. The navigator's persistent chrome (HeaderBar /
38
- * TabBar, shipped in later slices) calls `getScreenRegistry(entry.key)`
39
- * to read the currently-focused screen's options/slot fills without
40
- * being itself remounted on each navigation.
41
- *
42
- * Returns `undefined` when no screen for that key has mounted yet (or
43
- * after it has unmounted) — consumers must tolerate this and render
44
- * defaults.
45
- */
46
- readonly _screens: {
47
- register(registry: ScreenRegistry): void;
48
- /** Identity-checked: no-op when a newer registry has taken the slot. */
49
- unregister(registry: ScreenRegistry): void;
50
- get(entryKey: string): ScreenRegistry | undefined;
51
- };
52
- /**
53
- * Internal: set `nav.isLocallyFocused` from outside.
54
- *
55
- * `<Stack>` calls this when its host entry's locally-focused state
56
- * changes (top of parent + parent focused + enclosing tab active). For
57
- * the root nav this stays `true` for the lifetime of the navigator.
58
- */
59
- readonly _setLocallyFocused: (focused: boolean) => void;
60
- }
61
- export interface CreateNavigatorOptions {
62
- routes: RouteMap;
63
- initial: StackEntry;
64
- /**
65
- * SharedValue driving push/pop transition progress. Created in
66
- * `<NavigationRoot>` setup via `useSharedValue(0)` so the bridge
67
- * plumbing is wired (SharedValue is an MT-bridged ref). When undefined,
68
- * navigations are instant — used by tests against `@sigx/lynx-testing`
69
- * that don't have an MT runtime.
70
- */
71
- progress?: SharedValue<number>;
72
- /**
73
- * Parent navigator. Set when this navigator is nested under another
74
- * (e.g. a per-tab `<Stack initialRoute>` under root). Drives the
75
- * `nav.parent` getter and the modal-escalation behaviour of `push`:
76
- * a push of a route whose resolved presentation is not `'card'`
77
- * recurses via `parent.push(...)`, walking up the chain until it
78
- * lands on a navigator with no parent (the root).
79
- *
80
- * Leave undefined for the root navigator.
81
- */
82
- parent?: Nav | null;
83
- /**
84
- * Whether this navigator is considered "locally focused" at creation
85
- * time. Defaults to true for the root nav; nested stacks pass `false`
86
- * here and then flip the flag via `_setLocallyFocused` once their
87
- * host-entry/tab-active state is computed.
88
- */
89
- initialLocallyFocused?: boolean;
90
- }
91
- /**
92
- * Create a navigator. Returns the public `nav` handle plus the routes map.
93
- * The transition signal lives on `nav` (via `nav.transition`) so `<Stack>`
94
- * can subscribe to it.
95
- */
96
- export declare function createNavigatorState(opts: CreateNavigatorOptions): NavigatorState;
package/src/register.d.ts DELETED
@@ -1,37 +0,0 @@
1
- import type { ParamsOf, RouteMap, SearchOf } from './types';
2
- /**
3
- * Module-augmentation surface for the user's typed route map.
4
- *
5
- * Apps register their routes by augmenting this interface — the rest of the
6
- * library's typed APIs (useNav, useParams, useSearch, <Link>) read from
7
- * `RegisteredRoutes` so all inference is global.
8
- *
9
- * @example
10
- * ```ts
11
- * // In your app's main.ts (or a routes.ts that's imported early):
12
- * import type { routes } from './routes';
13
- *
14
- * declare module '@sigx/lynx-navigation' {
15
- * interface Register { routes: typeof routes }
16
- * }
17
- * ```
18
- *
19
- * If `Register.routes` is not augmented the library falls back to a permissive
20
- * `RouteMap` so non-augmented usage still type-checks (just without precise
21
- * inference). The recommended pattern is always to augment.
22
- */
23
- export interface Register {
24
- }
25
- /**
26
- * The user's registered route map, or a permissive fallback when not
27
- * augmented. All higher-level types derive from this.
28
- */
29
- export type RegisteredRoutes = Register extends {
30
- routes: infer R;
31
- } ? R : RouteMap;
32
- /** Union of registered route names (string literal union when registered). */
33
- export type RouteId = keyof RegisteredRoutes & string;
34
- /** Params type for a registered route name. */
35
- export type RouteParams<K extends RouteId> = ParamsOf<RegisteredRoutes[K]>;
36
- /** Search type for a registered route name. */
37
- export type RouteSearch<K extends RouteId> = SearchOf<RegisteredRoutes[K]>;