@sigx/lynx-navigation 0.1.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/README.md +189 -7
  2. package/dist/components/EntryScope.d.ts +1 -1
  3. package/dist/components/EntryScope.d.ts.map +1 -1
  4. package/dist/components/Layer.d.ts +34 -0
  5. package/dist/components/Layer.d.ts.map +1 -0
  6. package/dist/components/Link.d.ts +2 -2
  7. package/dist/components/Link.d.ts.map +1 -1
  8. package/dist/components/NavigationRoot.d.ts +2 -2
  9. package/dist/components/NavigationRoot.d.ts.map +1 -1
  10. package/dist/components/Screen.d.ts +6 -6
  11. package/dist/components/Screen.d.ts.map +1 -1
  12. package/dist/components/Stack.d.ts +83 -13
  13. package/dist/components/Stack.d.ts.map +1 -1
  14. package/dist/components/TabBar.d.ts +19 -20
  15. package/dist/components/TabBar.d.ts.map +1 -1
  16. package/dist/components/Tabs.d.ts +30 -21
  17. package/dist/components/Tabs.d.ts.map +1 -1
  18. package/dist/define-routes.d.ts +1 -1
  19. package/dist/define-routes.d.ts.map +1 -1
  20. package/dist/hooks/use-focus.d.ts.map +1 -1
  21. package/dist/hooks/use-hardware-back.d.ts +9 -2
  22. package/dist/hooks/use-hardware-back.d.ts.map +1 -1
  23. package/dist/hooks/use-linking-nav.d.ts +3 -3
  24. package/dist/hooks/use-linking-nav.d.ts.map +1 -1
  25. package/dist/hooks/use-nav-internal.d.ts +21 -3
  26. package/dist/hooks/use-nav-internal.d.ts.map +1 -1
  27. package/dist/hooks/use-nav-serializer.d.ts +1 -1
  28. package/dist/hooks/use-nav-serializer.d.ts.map +1 -1
  29. package/dist/hooks/use-nav.d.ts +38 -3
  30. package/dist/hooks/use-nav.d.ts.map +1 -1
  31. package/dist/hooks/use-params.d.ts +1 -1
  32. package/dist/hooks/use-params.d.ts.map +1 -1
  33. package/dist/hooks/use-screen-chrome.d.ts +19 -0
  34. package/dist/hooks/use-screen-chrome.d.ts.map +1 -0
  35. package/dist/hooks/use-screen-options.d.ts +1 -1
  36. package/dist/hooks/use-screen-options.d.ts.map +1 -1
  37. package/dist/hooks/use-search.d.ts +1 -1
  38. package/dist/hooks/use-search.d.ts.map +1 -1
  39. package/dist/href.d.ts +2 -2
  40. package/dist/href.d.ts.map +1 -1
  41. package/dist/index.d.ts +33 -31
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +1160 -29
  44. package/dist/index.js.map +1 -1
  45. package/dist/internal/layer-plan.d.ts +69 -0
  46. package/dist/internal/layer-plan.d.ts.map +1 -0
  47. package/dist/internal/screen-registry.d.ts +1 -1
  48. package/dist/internal/screen-registry.d.ts.map +1 -1
  49. package/dist/internal/screen-width.d.ts +9 -7
  50. package/dist/internal/screen-width.d.ts.map +1 -1
  51. package/dist/navigator/core.d.ts +31 -4
  52. package/dist/navigator/core.d.ts.map +1 -1
  53. package/dist/register.d.ts +1 -1
  54. package/dist/register.d.ts.map +1 -1
  55. package/dist/url/index.d.ts +6 -6
  56. package/dist/url/index.d.ts.map +1 -1
  57. package/dist/url/parse.d.ts +1 -1
  58. package/dist/url/parse.d.ts.map +1 -1
  59. package/dist/url/registry.d.ts +2 -2
  60. package/dist/url/registry.d.ts.map +1 -1
  61. package/dist/url/validate.d.ts +1 -1
  62. package/dist/url/validate.d.ts.map +1 -1
  63. package/package.json +11 -10
  64. package/src/components/Drawer.d.ts +55 -0
  65. package/src/components/EdgeBackHandle.d.ts +1 -0
  66. package/src/components/EdgeBackHandle.tsx +2 -2
  67. package/{dist/components/EntryScope.js → src/components/EntryScope.d.ts} +7 -15
  68. package/src/components/EntryScope.tsx +15 -4
  69. package/src/components/Header.d.ts +6 -0
  70. package/src/components/Header.tsx +3 -3
  71. package/src/components/Layer.d.ts +33 -0
  72. package/src/components/Layer.tsx +96 -0
  73. package/src/components/Link.d.ts +60 -0
  74. package/src/components/Link.tsx +4 -4
  75. package/src/components/NavigationRoot.d.ts +36 -0
  76. package/src/components/NavigationRoot.tsx +6 -6
  77. package/src/components/Screen.d.ts +97 -0
  78. package/src/components/Screen.tsx +13 -11
  79. package/src/components/Stack.d.ts +90 -0
  80. package/src/components/Stack.tsx +333 -92
  81. package/src/components/TabBar.d.ts +38 -0
  82. package/src/components/TabBar.tsx +22 -22
  83. package/src/components/Tabs.d.ts +109 -0
  84. package/src/components/Tabs.tsx +54 -22
  85. package/{dist/define-routes.js → src/define-routes.d.ts} +2 -4
  86. package/src/define-routes.ts +1 -1
  87. package/{dist/hooks/use-focus.js → src/hooks/use-focus.d.ts} +3 -39
  88. package/src/hooks/use-focus.ts +9 -3
  89. package/src/hooks/use-hardware-back.d.ts +37 -0
  90. package/src/hooks/use-hardware-back.ts +43 -9
  91. package/src/hooks/use-linking-nav.d.ts +91 -0
  92. package/src/hooks/use-linking-nav.ts +4 -4
  93. package/src/hooks/use-nav-internal.d.ts +91 -0
  94. package/src/hooks/use-nav-internal.ts +24 -3
  95. package/src/hooks/use-nav-serializer.d.ts +82 -0
  96. package/src/hooks/use-nav-serializer.ts +3 -3
  97. package/src/hooks/use-nav.d.ts +111 -0
  98. package/src/hooks/use-nav.ts +40 -3
  99. package/{dist/hooks/use-params.js → src/hooks/use-params.d.ts} +2 -6
  100. package/src/hooks/use-params.ts +2 -2
  101. package/src/hooks/use-screen-chrome.d.ts +18 -0
  102. package/src/hooks/use-screen-chrome.ts +122 -0
  103. package/src/hooks/use-screen-options.d.ts +2 -0
  104. package/src/hooks/use-screen-options.ts +3 -3
  105. package/{dist/hooks/use-search.js → src/hooks/use-search.d.ts} +2 -6
  106. package/src/hooks/use-search.ts +2 -2
  107. package/src/href.d.ts +54 -0
  108. package/src/href.ts +6 -6
  109. package/src/index.d.ts +39 -0
  110. package/src/index.ts +33 -31
  111. package/src/internal/layer-plan.d.ts +68 -0
  112. package/src/internal/layer-plan.ts +187 -0
  113. package/{dist/internal/screen-registry.js → src/internal/screen-registry.d.ts} +21 -32
  114. package/src/internal/screen-registry.ts +1 -1
  115. package/src/internal/screen-width.d.ts +17 -0
  116. package/src/internal/screen-width.ts +22 -14
  117. package/src/navigator/core.d.ts +96 -0
  118. package/src/navigator/core.ts +90 -10
  119. package/src/register.d.ts +37 -0
  120. package/src/register.ts +1 -1
  121. package/src/types.d.ts +217 -0
  122. package/src/url/build.d.ts +15 -0
  123. package/src/url/build.ts +2 -2
  124. package/src/url/compile.d.ts +34 -0
  125. package/src/url/format.d.ts +28 -0
  126. package/src/url/index.ts +6 -6
  127. package/src/url/parse.d.ts +20 -0
  128. package/src/url/parse.ts +6 -6
  129. package/{dist/url/registry.js → src/url/registry.d.ts} +12 -28
  130. package/src/url/registry.ts +3 -3
  131. package/src/url/validate.d.ts +23 -0
  132. package/src/url/validate.ts +1 -1
  133. package/dist/components/Drawer.js +0 -74
  134. package/dist/components/Drawer.js.map +0 -1
  135. package/dist/components/EdgeBackHandle.js +0 -144
  136. package/dist/components/EdgeBackHandle.js.map +0 -1
  137. package/dist/components/EntryScope.js.map +0 -1
  138. package/dist/components/Header.js +0 -103
  139. package/dist/components/Header.js.map +0 -1
  140. package/dist/components/Link.js +0 -51
  141. package/dist/components/Link.js.map +0 -1
  142. package/dist/components/NavigationRoot.js +0 -67
  143. package/dist/components/NavigationRoot.js.map +0 -1
  144. package/dist/components/Screen.js +0 -94
  145. package/dist/components/Screen.js.map +0 -1
  146. package/dist/components/ScreenContainer.d.ts +0 -18
  147. package/dist/components/ScreenContainer.d.ts.map +0 -1
  148. package/dist/components/ScreenContainer.js +0 -77
  149. package/dist/components/ScreenContainer.js.map +0 -1
  150. package/dist/components/Stack.js +0 -75
  151. package/dist/components/Stack.js.map +0 -1
  152. package/dist/components/TabBar.js +0 -63
  153. package/dist/components/TabBar.js.map +0 -1
  154. package/dist/components/Tabs.js +0 -140
  155. package/dist/components/Tabs.js.map +0 -1
  156. package/dist/define-routes.js.map +0 -1
  157. package/dist/hooks/use-focus.js.map +0 -1
  158. package/dist/hooks/use-hardware-back.js +0 -50
  159. package/dist/hooks/use-hardware-back.js.map +0 -1
  160. package/dist/hooks/use-linking-nav.js +0 -109
  161. package/dist/hooks/use-linking-nav.js.map +0 -1
  162. package/dist/hooks/use-nav-internal.js +0 -44
  163. package/dist/hooks/use-nav-internal.js.map +0 -1
  164. package/dist/hooks/use-nav-serializer.js +0 -181
  165. package/dist/hooks/use-nav-serializer.js.map +0 -1
  166. package/dist/hooks/use-nav.js +0 -11
  167. package/dist/hooks/use-nav.js.map +0 -1
  168. package/dist/hooks/use-params.js.map +0 -1
  169. package/dist/hooks/use-screen-options.js +0 -43
  170. package/dist/hooks/use-screen-options.js.map +0 -1
  171. package/dist/hooks/use-search.js.map +0 -1
  172. package/dist/href.js +0 -57
  173. package/dist/href.js.map +0 -1
  174. package/dist/internal/screen-registry.js.map +0 -1
  175. package/dist/internal/screen-width.js +0 -30
  176. package/dist/internal/screen-width.js.map +0 -1
  177. package/dist/navigator/core.js +0 -344
  178. package/dist/navigator/core.js.map +0 -1
  179. package/dist/register.js +0 -2
  180. package/dist/register.js.map +0 -1
  181. package/dist/types.js +0 -9
  182. package/dist/types.js.map +0 -1
  183. package/dist/url/build.js +0 -30
  184. package/dist/url/build.js.map +0 -1
  185. package/dist/url/compile.js +0 -83
  186. package/dist/url/compile.js.map +0 -1
  187. package/dist/url/format.js +0 -102
  188. package/dist/url/format.js.map +0 -1
  189. package/dist/url/index.js +0 -13
  190. package/dist/url/index.js.map +0 -1
  191. package/dist/url/parse.js +0 -94
  192. package/dist/url/parse.js.map +0 -1
  193. package/dist/url/registry.js.map +0 -1
  194. package/dist/url/validate.js +0 -37
  195. package/dist/url/validate.js.map +0 -1
  196. package/src/components/ScreenContainer.tsx +0 -114
@@ -1,16 +1,18 @@
1
1
  /**
2
- * Logical screen width (in dp) read from `lynx.SystemInfo` at module load.
3
- * Falls back to 400 (typical phone) if SystemInfo isn't available — module
4
- * load happens BG-side after the bundle initializes, by which time
5
- * `lynx.SystemInfo` is populated, so the fallback only fires in tests / SSR /
6
- * non-Lynx hosts.
2
+ * Logical screen dimensions (in dp) read from `lynx.SystemInfo` at module
3
+ * load. Falls back to typical phone values if SystemInfo isn't available —
4
+ * module load happens BG-side after the bundle initializes, by which time
5
+ * `lynx.SystemInfo` is populated, so the fallback only fires in tests /
6
+ * SSR / non-Lynx hosts.
7
7
  *
8
8
  * Used by:
9
- * - `<ScreenContainer>` for the slide-from-right transform output range.
9
+ * - `<ScreenContainer>` for the slide-from-right (translateX) and
10
+ * slide-from-bottom (translateY, modal) transform output ranges.
10
11
  * - `<EdgeBackHandle>` for the gesture commit threshold (`dx / width`).
11
12
  *
12
13
  * Both must agree, otherwise the commit threshold and the animation
13
- * geometry won't line up. Single shared constant avoids drift.
14
+ * geometry won't line up. Single shared module avoids drift.
14
15
  */
15
16
  export declare const SCREEN_WIDTH: number;
17
+ export declare const SCREEN_HEIGHT: number;
16
18
  //# sourceMappingURL=screen-width.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"screen-width.d.ts","sourceRoot":"","sources":["../../src/internal/screen-width.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAoBH,eAAO,MAAM,YAAY,QAAoB,CAAC"}
1
+ {"version":3,"file":"screen-width.d.ts","sourceRoot":"","sources":["../../src/internal/screen-width.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA0BH,eAAO,MAAM,YAAY,QAA4B,CAAC;AACtD,eAAO,MAAM,aAAa,QAA6B,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { type SharedValue } from '@sigx/lynx';
2
- import type { Nav } from '../hooks/use-nav.js';
3
- import type { ScreenRegistry } from '../internal/screen-registry.js';
4
- import type { RouteMap, StackEntry } from '../types.js';
2
+ import type { Nav } from '../hooks/use-nav';
3
+ import type { ScreenRegistry } from '../internal/screen-registry';
4
+ import type { RouteMap, StackEntry } from '../types';
5
5
  /**
6
6
  * The reactive backing state for one navigator instance.
7
7
  *
@@ -45,9 +45,18 @@ export interface NavigatorState {
45
45
  */
46
46
  readonly _screens: {
47
47
  register(registry: ScreenRegistry): void;
48
- unregister(entryKey: string): void;
48
+ /** Identity-checked: no-op when a newer registry has taken the slot. */
49
+ unregister(registry: ScreenRegistry): void;
49
50
  get(entryKey: string): ScreenRegistry | undefined;
50
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;
51
60
  }
52
61
  export interface CreateNavigatorOptions {
53
62
  routes: RouteMap;
@@ -60,6 +69,24 @@ export interface CreateNavigatorOptions {
60
69
  * that don't have an MT runtime.
61
70
  */
62
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;
63
90
  }
64
91
  /**
65
92
  * Create a navigator. Returns the public `nav` handle plus the routes map.
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/navigator/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,KAAK,WAAW,EACnB,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAIR,QAAQ,EACR,UAAU,EAEb,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE;QACf,gBAAgB,IAAI,IAAI,CAAC;QACzB,iBAAiB,IAAI,IAAI,CAAC;QAC1B,iBAAiB,IAAI,IAAI,CAAC;KAC7B,CAAC;IACF;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,QAAQ,EAAE;QACf,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;QACzC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;KACrD,CAAC;CACL;AAuED,MAAM,WAAW,sBAAsB;IACnC,MAAM,EAAE,QAAQ,CAAC;IACjB,OAAO,EAAE,UAAU,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAClC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,sBAAsB,GAAG,cAAc,CA+QjF"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/navigator/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,KAAK,WAAW,EACnB,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAIR,QAAQ,EACR,UAAU,EAEb,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE;QACf,gBAAgB,IAAI,IAAI,CAAC;QACzB,iBAAiB,IAAI,IAAI,CAAC;QAC1B,iBAAiB,IAAI,IAAI,CAAC;KAC7B,CAAC;IACF;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,QAAQ,EAAE;QACf,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;QACzC,wEAAwE;QACxE,UAAU,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;QAC3C,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;KACrD,CAAC;IACF;;;;;;OAMG;IACH,QAAQ,CAAC,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3D;AAuED,MAAM,WAAW,sBAAsB;IACnC,MAAM,EAAE,QAAQ,CAAC;IACjB,OAAO,EAAE,UAAU,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IACpB;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,sBAAsB,GAAG,cAAc,CA0TjF"}
@@ -1,4 +1,4 @@
1
- import type { ParamsOf, RouteMap, SearchOf } from './types.js';
1
+ import type { ParamsOf, RouteMap, SearchOf } from './types';
2
2
  /**
3
3
  * Module-augmentation surface for the user's typed route map.
4
4
  *
@@ -1 +1 @@
1
- {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,QAAQ;CAExB;AAED;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,QAAQ,CAAC;AAEnF,8EAA8E;AAC9E,MAAM,MAAM,OAAO,GAAG,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEtD,+CAA+C;AAC/C,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3E,+CAA+C;AAC/C,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,QAAQ;CAExB;AAED;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,QAAQ,CAAC;AAEnF,8EAA8E;AAC9E,MAAM,MAAM,OAAO,GAAG,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEtD,+CAA+C;AAC/C,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3E,+CAA+C;AAC/C,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -4,10 +4,10 @@
4
4
  * Not re-exported from the package root. Public surface is `hrefFor` /
5
5
  * `parseHref` in ../href.ts plus `_setRouteRegistry` for tests/bootstrap.
6
6
  */
7
- export { compilePath, type CompiledPath } from './compile.js';
8
- export { buildUrl } from './build.js';
9
- export { parseHrefImpl } from './parse.js';
10
- export { formatSearch, parseSearch } from './format.js';
11
- export { _setRouteRegistry, _clearRouteRegistry, getRouteRegistry, getCompiledPath, } from './registry.js';
12
- export { validateSync, type ValidateOutcome } from './validate.js';
7
+ export { compilePath, type CompiledPath } from './compile';
8
+ export { buildUrl } from './build';
9
+ export { parseHrefImpl } from './parse';
10
+ export { formatSearch, parseSearch } from './format';
11
+ export { _setRouteRegistry, _clearRouteRegistry, getRouteRegistry, getCompiledPath, } from './registry';
12
+ export { validateSync, type ValidateOutcome } from './validate';
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/url/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EACH,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/url/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EACH,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC"}
@@ -9,7 +9,7 @@
9
9
  * Validation failures return `null` rather than throwing — deep-link handlers
10
10
  * fall back to the initial route on a bad URL instead of crashing the app.
11
11
  */
12
- import type { Href } from '../href.js';
12
+ import type { Href } from '../href';
13
13
  /**
14
14
  * Parse a URL string against the active route registry.
15
15
  *
@@ -1 +1 @@
1
- {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/url/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAQvC;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAkCtD"}
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/url/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAQpC;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAkCtD"}
@@ -11,8 +11,8 @@
11
11
  * directly. The leading underscore is a convention: not part of the supported
12
12
  * public API (test/integration use only).
13
13
  */
14
- import type { CompiledPath } from './compile.js';
15
- import type { RouteMap } from '../types.js';
14
+ import type { CompiledPath } from './compile';
15
+ import type { RouteMap } from '../types';
16
16
  interface RegistryState {
17
17
  readonly routes: RouteMap;
18
18
  /** Lazy-compiled paths keyed by route name. */
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/url/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,UAAU,aAAa;IACnB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAChD;AAID;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAExD;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,IAAI,aAAa,CAOhD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAU1F"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/url/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,UAAU,aAAa;IACnB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAChD;AAID;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAExD;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,IAAI,aAAa,CAOhD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAU1F"}
@@ -5,7 +5,7 @@
5
5
  * resolution) so we restrict to sync validators. Zod/Valibot/ArkType are all
6
6
  * sync, which covers the common case. Async validators throw a clear error.
7
7
  */
8
- import type { StandardSchemaV1 } from '../types.js';
8
+ import type { StandardSchemaV1 } from '../types';
9
9
  /** Outcome of a sync validation call — discriminated for explicit handling. */
10
10
  export type ValidateOutcome = {
11
11
  readonly ok: true;
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/url/validate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAmBpD,+EAA+E;AAC/E,MAAM,MAAM,eAAe,GACrB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC9C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC;AAErE;;;;;GAKG;AACH,wBAAgB,YAAY,CACxB,MAAM,EAAE,gBAAgB,GAAG,SAAS,EACpC,KAAK,EAAE,OAAO,GACf,eAAe,CAiBjB"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/url/validate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAmBjD,+EAA+E;AAC/E,MAAM,MAAM,eAAe,GACrB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC9C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC;AAErE;;;;;GAKG;AACH,wBAAgB,YAAY,CACxB,MAAM,EAAE,gBAAgB,GAAG,SAAS,EACpC,KAAK,EAAE,OAAO,GACf,eAAe,CAiBjB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigx/lynx-navigation",
3
- "version": "0.1.3",
3
+ "version": "0.4.0",
4
4
  "description": "Type-first native navigator for sigx-lynx — Stack, Tabs, Drawer, modals, lazy routes, deep links",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,9 +16,9 @@
16
16
  "dist"
17
17
  ],
18
18
  "peerDependencies": {
19
- "@sigx/lynx-linking": "^0.1.2",
20
- "@sigx/lynx-motion": "^0.1.2",
21
- "@sigx/lynx": "^0.1.4"
19
+ "@sigx/lynx-linking": "^0.4.0",
20
+ "@sigx/lynx": "^0.4.0",
21
+ "@sigx/lynx-motion": "^0.4.0"
22
22
  },
23
23
  "peerDependenciesMeta": {
24
24
  "@sigx/lynx-linking": {
@@ -28,10 +28,11 @@
28
28
  "devDependencies": {
29
29
  "typescript": "^6.0.3",
30
30
  "vitest": "^4.1.6",
31
- "@sigx/lynx-linking": "^0.1.2",
32
- "@sigx/lynx-testing": "^0.2.6",
33
- "@sigx/lynx": "^0.1.4",
34
- "@sigx/lynx-motion": "^0.1.2"
31
+ "@sigx/vite": "^0.4.3",
32
+ "@sigx/lynx": "^0.4.0",
33
+ "@sigx/lynx-linking": "^0.4.0",
34
+ "@sigx/lynx-testing": "^0.4.0",
35
+ "@sigx/lynx-motion": "^0.4.0"
35
36
  },
36
37
  "keywords": [
37
38
  "sigx",
@@ -59,8 +60,8 @@
59
60
  "access": "public"
60
61
  },
61
62
  "scripts": {
62
- "build": "tsc",
63
- "dev": "tsc --watch",
63
+ "build": "vite build && tsgo --emitDeclarationOnly",
64
+ "dev": "vite build --watch",
64
65
  "test": "vitest run",
65
66
  "test:types": "tsc --noEmit",
66
67
  "bench": "vitest bench --run"
@@ -0,0 +1,55 @@
1
+ /**
2
+ * `<Drawer>` — minimal off-canvas drawer navigator.
3
+ *
4
+ * Usage:
5
+ *
6
+ * ```tsx
7
+ * <NavigationRoot routes={routes}>
8
+ * <Drawer slots={{ sidebar: () => <view><text>Menu</text></view> }}>
9
+ * <Stack />
10
+ * </Drawer>
11
+ * </NavigationRoot>
12
+ * ```
13
+ *
14
+ * `useDrawer()` from inside any descendant gives `{ isOpen, open(), close(),
15
+ * toggle() }`. The sidebar is laid out absolutely on the left and is
16
+ * visible whenever `isOpen` is true.
17
+ *
18
+ * Scope: this slice ships the state primitive + the bare-bones layout.
19
+ * Gesture-driven open (edge swipe from the left) and MTS slide-in are out
20
+ * of scope — the app shell can wrap its sidebar JSX in its own transition.
21
+ *
22
+ * Design note: the sidebar lives in a named slot (`sidebar`) rather than
23
+ * a render-prop or a `<Drawer.Sidebar>` child. Mixing
24
+ * "register-yourself-as-a-fill" children with the parent's own visible
25
+ * layout creates a feedback loop in sigx's reactive scope (the parent's
26
+ * render reads the fill, child's setup writes it, parent re-renders,
27
+ * child re-mounts, …). A scoped slot avoids that entirely and keeps the
28
+ * call site declarative.
29
+ *
30
+ * `default` slot is the main content (almost always a `<Stack>`).
31
+ */
32
+ import { type Define } from '@sigx/lynx';
33
+ /** Reactive controller returned by `useDrawer()`. */
34
+ export interface DrawerNav {
35
+ /** True when the drawer is currently visible. Reactive. */
36
+ readonly isOpen: boolean;
37
+ /** Opens the drawer. */
38
+ open(): void;
39
+ /** Closes the drawer. */
40
+ close(): void;
41
+ /** Toggles between open and closed. */
42
+ toggle(): void;
43
+ }
44
+ /**
45
+ * Access the enclosing Drawer navigator. Throws when called outside
46
+ * `<Drawer>`.
47
+ */
48
+ export declare const useDrawer: import("@sigx/runtime-core").InjectableFunction<DrawerNav>;
49
+ type DrawerProps = Define.Prop<'initialOpen', boolean> & Define.Slot<'sidebar'> & Define.Slot<'default'>;
50
+ export declare const Drawer: import("@sigx/runtime-core").ComponentFactory<DrawerProps, void, {
51
+ sidebar: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
52
+ } & {
53
+ default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
54
+ }>;
55
+ export {};
@@ -0,0 +1 @@
1
+ export declare const EdgeBackHandle: import("@sigx/runtime-core").ComponentFactory<{}, void, unknown>;
@@ -7,8 +7,8 @@ import {
7
7
  type MainThread,
8
8
  } from '@sigx/lynx';
9
9
  import { withTiming } from '@sigx/lynx-motion';
10
- import { useNavInternals } from '../hooks/use-nav-internal.js';
11
- import { SCREEN_WIDTH } from '../internal/screen-width.js';
10
+ import { useNavInternals } from '../hooks/use-nav-internal';
11
+ import { SCREEN_WIDTH } from '../internal/screen-width';
12
12
 
13
13
  /**
14
14
  * Edge-pan recognizer for iOS-style swipe-back. Mounts as an absolutely-
@@ -1,6 +1,6 @@
1
- import { component, defineProvide, onUnmounted } from '@sigx/lynx';
2
- import { useCurrentEntry, useNavInternals, useScreenRegistry } from '../hooks/use-nav-internal.js';
3
- import { createScreenRegistry } from '../internal/screen-registry.js';
1
+ import { type Define } from '@sigx/lynx';
2
+ import type { StackEntry } from '../types';
3
+ type EntryScopeProps = Define.Prop<'entry', StackEntry, true> & Define.Slot<'default'>;
4
4
  /**
5
5
  * Provider wrapper for a single screen mount.
6
6
  *
@@ -19,15 +19,7 @@ import { createScreenRegistry } from '../internal/screen-registry.js';
19
19
  * Renders the default slot directly; no extra layout element is inserted,
20
20
  * so this is layout-neutral for the screen it wraps.
21
21
  */
22
- export const EntryScope = component(({ props, slots }) => {
23
- const internals = useNavInternals();
24
- const registry = createScreenRegistry(props.entry);
25
- internals.screens.register(registry);
26
- onUnmounted(() => {
27
- internals.screens.unregister(props.entry.key);
28
- });
29
- defineProvide(useCurrentEntry, () => props.entry);
30
- defineProvide(useScreenRegistry, () => registry);
31
- return () => slots.default?.();
32
- });
33
- //# sourceMappingURL=EntryScope.js.map
22
+ export declare const EntryScope: import("@sigx/runtime-core").ComponentFactory<EntryScopeProps, void, {
23
+ default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
24
+ }>;
25
+ export {};
@@ -1,7 +1,12 @@
1
1
  import { component, defineProvide, onUnmounted, type Define } from '@sigx/lynx';
2
- import { useCurrentEntry, useNavInternals, useScreenRegistry } from '../hooks/use-nav-internal.js';
3
- import { createScreenRegistry } from '../internal/screen-registry.js';
4
- import type { StackEntry } from '../types.js';
2
+ import {
3
+ useCurrentEntry,
4
+ useCurrentEntryOptional,
5
+ useNavInternals,
6
+ useScreenRegistry,
7
+ } from '../hooks/use-nav-internal';
8
+ import { createScreenRegistry } from '../internal/screen-registry';
9
+ import type { StackEntry } from '../types';
5
10
 
6
11
  type EntryScopeProps =
7
12
  & Define.Prop<'entry', StackEntry, true>
@@ -30,9 +35,15 @@ export const EntryScope = component<EntryScopeProps>(({ props, slots }) => {
30
35
  const registry = createScreenRegistry(props.entry);
31
36
  internals.screens.register(registry);
32
37
  onUnmounted(() => {
33
- internals.screens.unregister(props.entry.key);
38
+ // Pass the registry instance — `unregister` is identity-checked,
39
+ // so this is a no-op when a newer EntryScope has already taken
40
+ // over the same entry key (e.g. at the transition→idle handoff
41
+ // where the reconciler mounts the new EntryScope before
42
+ // unmounting the old).
43
+ internals.screens.unregister(registry);
34
44
  });
35
45
  defineProvide(useCurrentEntry, () => props.entry);
46
+ defineProvide(useCurrentEntryOptional, () => props.entry);
36
47
  defineProvide(useScreenRegistry, () => registry);
37
48
  return () => slots.default?.();
38
49
  });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Persistent header chrome. Mount once above `<Stack>`; reactively follows
3
+ * the focused entry. No props in v1 — styling is a host-app concern,
4
+ * arrived at through the slot fills.
5
+ */
6
+ export declare const Header: import("@sigx/runtime-core").ComponentFactory<{}, void, unknown>;
@@ -20,9 +20,9 @@
20
20
  * - making it opt-in keeps `<Stack>`'s contract narrow.
21
21
  */
22
22
  import { component, computed } from '@sigx/lynx';
23
- import { useNav } from '../hooks/use-nav.js';
24
- import { useNavInternals } from '../hooks/use-nav-internal.js';
25
- import type { ScreenOptions, ScreenSlotFills, StackEntry } from '../types.js';
23
+ import { useNav } from '../hooks/use-nav';
24
+ import { useNavInternals } from '../hooks/use-nav-internal';
25
+ import type { ScreenOptions, ScreenSlotFills, StackEntry } from '../types';
26
26
 
27
27
  /**
28
28
  * Resolve a title (string or getter) to a plain string.
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `<Layer>` — one row in `<Stack>`'s layered render. Absolutely-
3
+ * positioned host view that fills the Stack's relative wrapper, with
4
+ * an optional MT-bound `translateX` / `translateY` animation driven
5
+ * by a `SharedValue<number>` from the navigator's transition state.
6
+ *
7
+ * `<Stack>` emits one `<Layer>` per entry returned by
8
+ * `computeLayers(...)`. Layer.key in the parent is
9
+ * `layer-${entry.key}-${animationVariant(animation)}` so that:
10
+ *
11
+ * - The same entry under the same animation state is preserved across
12
+ * renders (modal underneath stays mounted through the modal
13
+ * lifecycle; per-tab Stack state survives).
14
+ * - An entry transitioning between animated and static (e.g. a card
15
+ * top after its push transition completes) remounts so the
16
+ * `useAnimatedStyle` binding can be rebound — the underlying
17
+ * `useAnimatedStyle` is set-once at setup and can't switch its
18
+ * mapper at runtime.
19
+ *
20
+ * Layouts:
21
+ * - Host view is `position: absolute; top/right/bottom/left: 0;
22
+ * display: flex; flexDirection: column` so descendants that
23
+ * flex-fill (SafeAreaView, daisyui screens) get a sized parent.
24
+ * - No background. Screens own their own surface colour (typically
25
+ * via a daisy `bg-base-*` class on the screen body).
26
+ */
27
+ import { type ComponentFactory, type Define } from '@sigx/lynx';
28
+ import type { LayerAnimation } from '../internal/layer-plan';
29
+ import type { RouteMap, StackEntry } from '../types';
30
+ export type LayerProps = Define.Prop<'entry', StackEntry, true> & Define.Prop<'routes', RouteMap, true>
31
+ /** When set, the host view animates per the transform spec. */
32
+ & Define.Prop<'animation', LayerAnimation | null, false>;
33
+ export declare const Layer: ComponentFactory<LayerProps, void, {}>;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * `<Layer>` — one row in `<Stack>`'s layered render. Absolutely-
3
+ * positioned host view that fills the Stack's relative wrapper, with
4
+ * an optional MT-bound `translateX` / `translateY` animation driven
5
+ * by a `SharedValue<number>` from the navigator's transition state.
6
+ *
7
+ * `<Stack>` emits one `<Layer>` per entry returned by
8
+ * `computeLayers(...)`. Layer.key in the parent is
9
+ * `layer-${entry.key}-${animationVariant(animation)}` so that:
10
+ *
11
+ * - The same entry under the same animation state is preserved across
12
+ * renders (modal underneath stays mounted through the modal
13
+ * lifecycle; per-tab Stack state survives).
14
+ * - An entry transitioning between animated and static (e.g. a card
15
+ * top after its push transition completes) remounts so the
16
+ * `useAnimatedStyle` binding can be rebound — the underlying
17
+ * `useAnimatedStyle` is set-once at setup and can't switch its
18
+ * mapper at runtime.
19
+ *
20
+ * Layouts:
21
+ * - Host view is `position: absolute; top/right/bottom/left: 0;
22
+ * display: flex; flexDirection: column` so descendants that
23
+ * flex-fill (SafeAreaView, daisyui screens) get a sized parent.
24
+ * - No background. Screens own their own surface colour (typically
25
+ * via a daisy `bg-base-*` class on the screen body).
26
+ */
27
+ import {
28
+ component,
29
+ useMainThreadRef,
30
+ useAnimatedStyle,
31
+ type ComponentFactory,
32
+ type Define,
33
+ type MainThread,
34
+ } from '@sigx/lynx';
35
+ import { Suspense, isLazyComponent } from '@sigx/lynx';
36
+ import type { LayerAnimation } from '../internal/layer-plan';
37
+ import type { RouteMap, StackEntry } from '../types';
38
+ import { EntryScope } from './EntryScope';
39
+
40
+ export type LayerProps =
41
+ & Define.Prop<'entry', StackEntry, true>
42
+ & Define.Prop<'routes', RouteMap, true>
43
+ /** When set, the host view animates per the transform spec. */
44
+ & Define.Prop<'animation', LayerAnimation | null, false>;
45
+
46
+ export const Layer = component<LayerProps>(({ props }) => {
47
+ const ref = useMainThreadRef<MainThread.Element | null>(null);
48
+ // `useAnimatedStyle` binds once at setup. Calling it conditionally
49
+ // is safe because setup runs once per mount and props.animation
50
+ // never changes for a given Layer instance — animation changes
51
+ // re-key the Layer at the parent, forcing a fresh mount.
52
+ if (props.animation) {
53
+ const a = props.animation;
54
+ useAnimatedStyle(ref, a.progress, a.axis, {
55
+ inputRange: [a.inputRange[0], a.inputRange[1]],
56
+ outputRange: [a.outputRange[0], a.outputRange[1]],
57
+ });
58
+ }
59
+
60
+ return () => {
61
+ const route = props.routes[props.entry.route];
62
+ if (!route) return null;
63
+ const Comp = route.component as unknown as ComponentFactory<
64
+ Record<string, unknown>,
65
+ unknown,
66
+ unknown
67
+ >;
68
+ if (typeof Comp !== 'function') return null;
69
+ const entryParams = props.entry.params as Record<string, unknown>;
70
+ const body = isLazyComponent(Comp) && route.fallback
71
+ ? (
72
+ <Suspense fallback={route.fallback as never}>
73
+ <Comp {...entryParams} />
74
+ </Suspense>
75
+ )
76
+ : <Comp {...entryParams} />;
77
+ return (
78
+ <view
79
+ main-thread:ref={ref}
80
+ style={{
81
+ position: 'absolute',
82
+ top: '0',
83
+ left: '0',
84
+ right: '0',
85
+ bottom: '0',
86
+ display: 'flex',
87
+ flexDirection: 'column',
88
+ }}
89
+ >
90
+ <EntryScope key={props.entry.key} entry={props.entry}>
91
+ {body}
92
+ </EntryScope>
93
+ </view>
94
+ );
95
+ };
96
+ });
@@ -0,0 +1,60 @@
1
+ import type { RouteId, RouteParams, RouteSearch } from '../register';
2
+ import type { RoutesWithParams } from '../hooks/use-nav';
3
+ /**
4
+ * Per-route conditional props for `<Link>`.
5
+ *
6
+ * Mapped over `RouteId`, then indexed by `RouteId` to flatten into a union.
7
+ * Each branch enforces the `params`-required-iff-route-has-schema rule:
8
+ *
9
+ * - `<Link to="profile" />` → TS error (profile requires params)
10
+ * - `<Link to="profile" params={...} />` → ok
11
+ * - `<Link to="home" />` → ok (home has no params)
12
+ * - `<Link to="home" params={...} />` → TS error (home accepts no params)
13
+ *
14
+ * Same per-route discrimination as `nav.push`, expressed as a JSX-friendly
15
+ * union rather than overloads.
16
+ */
17
+ type LinkPropsByRoute = {
18
+ [K in RouteId]: K extends RoutesWithParams ? {
19
+ to: K;
20
+ params: RouteParams<K>;
21
+ search?: RouteSearch<K>;
22
+ } : {
23
+ to: K;
24
+ params?: undefined;
25
+ search?: RouteSearch<K>;
26
+ };
27
+ }[RouteId];
28
+ /**
29
+ * Public type for `<Link>`'s props. The conditional `LinkPropsByRoute` carries
30
+ * the typed `to`/`params`/`search` triple; the rest are simple optionals.
31
+ *
32
+ * `children` is declared explicitly because the public type is exposed via a
33
+ * type-cast (so JSX sees a function-shaped signature). The cast strips sigx's
34
+ * built-in `Define.Slot<'default'>` → JSX-children wiring, so we add a
35
+ * permissive `children?` slot ourselves.
36
+ */
37
+ export type LinkProps = LinkPropsByRoute & {
38
+ /** Use `replace` instead of `push` (no new history entry). */
39
+ replace?: boolean;
40
+ /** Link content rendered inside the tappable container. */
41
+ children?: unknown;
42
+ };
43
+ /**
44
+ * Declarative navigation. Same typing as `nav.push` — pass `params` only when
45
+ * the route declares a schema. Wraps a `<view>` that fires `nav.push` (or
46
+ * `nav.replace` if `replace` is set) on tap.
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * <Link to="home">Home</Link>
51
+ * <Link to="profile" params={{ id: '42' }}>View profile</Link>
52
+ * <Link to="profile" params={{ id: '42' }} search={{ tab: 'about' }}>About</Link>
53
+ * <Link to="settings" replace>Settings (no back)</Link>
54
+ * ```
55
+ *
56
+ * The cast widens the inferred prop type from the loose impl to the strict
57
+ * `LinkProps` so JSX usage gets per-route discrimination. Runtime is identical.
58
+ */
59
+ export declare const Link: (props: LinkProps) => unknown;
60
+ export {};
@@ -1,8 +1,8 @@
1
1
  import { component, type Define } from '@sigx/lynx';
2
- import { useNav } from '../hooks/use-nav.js';
3
- import { useNavRoutes } from '../hooks/use-nav-internal.js';
4
- import type { RouteId, RouteParams, RouteSearch } from '../register.js';
5
- import type { RoutesWithParams } from '../hooks/use-nav.js';
2
+ import { useNav } from '../hooks/use-nav';
3
+ import { useNavRoutes } from '../hooks/use-nav-internal';
4
+ import type { RouteId, RouteParams, RouteSearch } from '../register';
5
+ import type { RoutesWithParams } from '../hooks/use-nav';
6
6
 
7
7
  /**
8
8
  * Per-route conditional props for `<Link>`.
@@ -0,0 +1,36 @@
1
+ import { type Define } from '@sigx/lynx';
2
+ import type { RouteId } from '../register';
3
+ import type { RouteMap } from '../types';
4
+ type NavigationRootProps = Define.Prop<'routes', RouteMap, true> & Define.Prop<'initialRoute', RouteId> & Define.Prop<'initialParams', Record<string, unknown>> & Define.Prop<'initialSearch', Record<string, unknown>>
5
+ /**
6
+ * Enable slide-from-right transitions on push/pop. Defaults to true.
7
+ * Tests against `@sigx/lynx-testing` (which doesn't have an MT runtime)
8
+ * should pass `animated={false}` so navigations commit synchronously.
9
+ */
10
+ & Define.Prop<'animated', boolean>
11
+ /**
12
+ * Enable the iOS-style edge-swipe-back gesture. Defaults to true. Set
13
+ * to false if it conflicts with screen content on the leftmost 20px,
14
+ * or while debugging gesture issues.
15
+ */
16
+ & Define.Prop<'edgeSwipeEnabled', boolean> & Define.Slot<'default'>;
17
+ /**
18
+ * Root of a navigator subtree.
19
+ *
20
+ * Creates a fresh `NavigatorState` from `routes` and provides it via
21
+ * `defineProvide`, so descendant `<Stack>` / `<Screen>` components and any
22
+ * `useNav()` / `useParams()` calls resolve through this instance.
23
+ *
24
+ * The bottom-of-stack entry is built from `initialRoute` (defaults to the
25
+ * first key in `routes`). For routes that declare a params schema, you must
26
+ * pass `initialParams` matching that schema.
27
+ *
28
+ * Mirrors the install pattern of `@sigx/router` (see
29
+ * `packages/router/src/router.ts:519-528`), but at component scope rather than
30
+ * `app.use(router)` — no app-wide singleton, so multi-navigator apps and
31
+ * tests get isolated state for free.
32
+ */
33
+ export declare const NavigationRoot: import("@sigx/runtime-core").ComponentFactory<NavigationRootProps, void, {
34
+ default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
35
+ }>;
36
+ export {};