@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
package/README.md CHANGED
@@ -98,7 +98,95 @@ and every other API gets typed.
98
98
  ### `<Stack>`
99
99
 
100
100
  Renders the topmost stack entry plus the entry beneath it during
101
- transitions. No props driven entirely by the navigator state.
101
+ transitions. By default it binds to the enclosing navigator (the root one
102
+ under `<NavigationRoot>`).
103
+
104
+ Pass `initialRoute` to make `<Stack>` create its **own** nested navigator —
105
+ this is how per-tab stacks work. See [Per-tab nested stacks](#per-tab-nested-stacks).
106
+
107
+ ```tsx
108
+ <Stack initialRoute="tripsHome" />
109
+ <Stack initialRoute="profile" initialParams={{ id: 'me' }} />
110
+ ```
111
+
112
+ ### Per-tab nested stacks
113
+
114
+ Drop a `<Stack initialRoute=…>` inside each `<Tabs.Screen>` to give every
115
+ tab its own back-stack. Card-presentation pushes stay inside the tab;
116
+ modal / fullScreen / transparent-modal pushes **escalate** to the root
117
+ navigator so they overlay the entire tabs UI (TabBar included).
118
+
119
+ ```tsx
120
+ <NavigationRoot routes={routes} initialRoute="root">
121
+ <Header />
122
+ <Stack /> {/* root navigator — renders the `root` entry below */}
123
+ </NavigationRoot>
124
+
125
+ // `root` route renders this:
126
+ <Tabs initialTab="trips">
127
+ <Tabs.Screen name="trips" label="Trips">
128
+ <Stack initialRoute="tripsHome" />
129
+ </Tabs.Screen>
130
+ <Tabs.Screen name="map" label="Map">
131
+ <Stack initialRoute="mapHome" />
132
+ </Tabs.Screen>
133
+ <TabBar />
134
+ </Tabs>
135
+ ```
136
+
137
+ Inside a tab body, `useNav()` resolves to the **innermost** navigator:
138
+
139
+ - `nav.push('tripDetail', { tripId })` — `tripDetail` is a card route →
140
+ pushed onto the trips tab's stack, TabBar stays visible.
141
+ - `nav.push('newTrip')` — `newTrip` is `presentation: 'modal'` → walks up
142
+ `nav.parent` to root and pushes there, overlays the whole tabs UI.
143
+ - `nav.replace(...)` is **strictly local** and never escalates (asymmetric
144
+ with `push` by design — keeps the root stack stable).
145
+ - Hardware/edge back pops the focused inner nav first; only falls through
146
+ to root once the inner stack is empty.
147
+ - `useIsFocused()` is `true` only when the screen is the top of its own
148
+ nav **and** every ancestor is focused (parent's current entry matches +
149
+ enclosing tab is active).
150
+
151
+ **Modal preservation.** When a `modal` / `fullScreen` /
152
+ `transparent-modal` push goes onto the root navigator (via escalation
153
+ from a per-tab stack), `<Stack>` keeps the underneath entry mounted
154
+ behind the overlay. Per-tab stack state, scroll positions, and in-flight
155
+ inputs all survive the modal lifecycle. Card pushes still replace the
156
+ underneath in the base layer — the user's mental model is "back
157
+ recreates the previous screen from history".
158
+
159
+ **Limitations (current slice)**:
160
+
161
+ - One global route registry — there's no per-tab whitelist yet. Deep-link
162
+ routing always pushes against the innermost nav of the caller site
163
+ (modal routes still escalate). A future slice may add `<Stack routes={…}>`.
164
+ - `useNavSerializer` snapshots one nav only — nested-tab stack state isn't
165
+ persisted across reload yet.
166
+
167
+ ### Per-stack header chrome
168
+
169
+ `<Stack>` accepts a default slot — its children render *inside* the
170
+ stack's nav scope, above the active screen. That's how to give a
171
+ nested per-tab stack its own header, since the root-level `<Header />`
172
+ only tracks the root navigator:
173
+
174
+ ```tsx
175
+ <Tabs.Screen name="trips" label="Trips">
176
+ <Stack initialRoute="tripsHome">
177
+ <Header /> {/* useNav() here resolves to the per-tab nav */}
178
+ </Stack>
179
+ </Tabs.Screen>
180
+ ```
181
+
182
+ Without this, a `<Header />` placed as a sibling of `<Stack>` would
183
+ resolve `useNav()` to the *enclosing* nav and never react to pushes
184
+ inside the nested stack — the back button + title wouldn't update on
185
+ `nav.push('tripDetail', ...)`.
186
+
187
+ For a daisy-themed bar (height, padding, centred title, separator),
188
+ use `<NavHeader />` from `@sigx/lynx-daisyui` — same slot position,
189
+ same `useScreenChrome()` data source.
102
190
 
103
191
  ### `<Screen>`
104
192
 
@@ -126,13 +214,61 @@ const Profile = component(() => () => (
126
214
  All sub-slots are optional. Anything not declared falls back to the
127
215
  navigator's default chrome.
128
216
 
217
+ **Placement — `<Screen>` is a child component, return it from
218
+ render.** `<Screen>` and its slot-filler siblings (`<Screen.Header>`,
219
+ `<Screen.HeaderRight>`, etc.) each call `useScreenRegistry()` from
220
+ their *own* setup, which means they have to actually mount inside
221
+ your screen's `<EntryScope>` to find the registry — and that only
222
+ happens when JSX returned from your render function is reconciled
223
+ into the tree. Constructing `<Screen ... />` JSX inside setup without
224
+ returning it doesn't mount anything:
225
+
226
+ ```tsx
227
+ // ✗ Won't take effect — the JSX is created but never rendered.
228
+ const Profile = component(() => {
229
+ <Screen title="Profile" />;
230
+ return () => <view>profile body</view>;
231
+ });
232
+
233
+ // ✓ Returned from render — gets mounted, registers its options.
234
+ const Profile = component(() => {
235
+ return () => (
236
+ <Screen title="Profile">
237
+ <view>profile body</view>
238
+ </Screen>
239
+ );
240
+ });
241
+ ```
242
+
243
+ When `<Screen>` *does* mount outside of any `<EntryScope>` — typically
244
+ because the consumer placed it at the app root instead of inside a
245
+ route component — it throws:
246
+
247
+ > `[lynx-navigation] No screen registry in scope. `<Screen>` (and
248
+ > `<Screen.Header>`, etc.) must be used inside a route component
249
+ > rendered by `<Stack>`.`
250
+
251
+ If you need to write options *imperatively* at setup time (e.g. to
252
+ react to a hook result before the first render), reach for
253
+ `useScreenOptions(...)` — it calls `useScreenRegistry()` directly
254
+ from your own setup, where the EntryScope's registry is already
255
+ visible.
256
+
129
257
  ### `<Header>`
130
258
 
131
- Default navigator header. Reads the focused entry's `<Screen.Header>`
132
- slot if set, otherwise renders `headerLeft | title | headerRight` with a
133
- back button as the default `headerLeft` when `nav.canGoBack` is true.
134
- Pulls `title` / `headerShown` / `gestureEnabled` from the focused
135
- screen's `useScreenOptions(...)` registration.
259
+ **Headless** default navigator header bare `<view>`/`<text>` nodes,
260
+ no flex direction, no padding, no theme. Reads the focused entry's
261
+ `<Screen.Header>` slot if set, otherwise renders
262
+ `headerLeft | title | headerRight` with a back button as the default
263
+ `headerLeft` when `nav.canGoBack` is true. Pulls `title` /
264
+ `headerShown` / `gestureEnabled` from the focused screen's
265
+ `useScreenOptions(...)` registration (or declarative `<Screen title=…>`).
266
+
267
+ For a daisy-themed bar with sensible defaults (surface colour,
268
+ separator, fixed ~48dp height, centred title), use `<NavHeader />`
269
+ from `@sigx/lynx-daisyui` — same data source via `useScreenChrome()`.
270
+ Custom designs can build their own component on top of
271
+ `useScreenChrome()` without touching internals.
136
272
 
137
273
  ```tsx
138
274
  <NavigationRoot routes={routes} initialRoute="home">
@@ -143,7 +279,10 @@ screen's `useScreenOptions(...)` registration.
143
279
 
144
280
  ### `<Tabs>` + `<Tabs.Screen>` + `<TabBar>`
145
281
 
146
- Persistent tab navigator. Each tab keeps its own stack across switches.
282
+ Persistent tab navigator. Each tab body stays mounted (hidden via
283
+ `display: none`) so switching tabs preserves state. Drop a `<Stack
284
+ initialRoute=…>` inside a `<Tabs.Screen>` to give that tab its own
285
+ back-stack — see [Per-tab nested stacks](#per-tab-nested-stacks).
147
286
 
148
287
  ```tsx
149
288
  <Tabs initialTab="home">
@@ -250,6 +389,49 @@ const Profile = component(() => {
250
389
  });
251
390
  ```
252
391
 
392
+ Equivalent declarative form via `<Screen>`:
393
+
394
+ ```tsx
395
+ <Screen title={() => `User ${id}`} />
396
+ ```
397
+
398
+ `<Screen>` only patches keys you actually pass — omitting `title`
399
+ won't clear a title set elsewhere on the same entry. Safe to use a
400
+ bare `<Screen>` purely to host `<Screen.HeaderRight>` slots without
401
+ worrying about wiping a sibling `useScreenOptions(...)` write.
402
+
403
+ ### `useScreenChrome()`
404
+
405
+ Reactive read of the focused screen's options + slot fills, plus
406
+ navigation helpers a header would need (`canGoBack`, `pop`). The
407
+ public foundation for building custom header components without
408
+ touching internal modules — `<NavHeader />` in `@sigx/lynx-daisyui` is
409
+ built on this.
410
+
411
+ ```tsx
412
+ import { useScreenChrome } from '@sigx/lynx-navigation';
413
+
414
+ const MyHeader = component(() => {
415
+ const chrome = useScreenChrome();
416
+ return () => {
417
+ if (!chrome.headerShown) return null;
418
+ return (
419
+ <view class="my-header">
420
+ {chrome.canGoBack
421
+ ? <view bindtap={chrome.pop}><text>‹ Back</text></view>
422
+ : null}
423
+ <text>{chrome.title}</text>
424
+ {chrome.headerRight?.()}
425
+ </view>
426
+ );
427
+ };
428
+ });
429
+ ```
430
+
431
+ Every property is a getter — reading it inside a render or `computed`
432
+ subscribes to the underlying signal, so the consumer re-renders when
433
+ title / slots change.
434
+
253
435
  ### `useLinkingNav(options?)`
254
436
 
255
437
  Bridges `@sigx/lynx-linking` URL events into the navigator. Call once
@@ -1,5 +1,5 @@
1
1
  import { type Define } from '@sigx/lynx';
2
- import type { StackEntry } from '../types.js';
2
+ import type { StackEntry } from '../types';
3
3
  type EntryScopeProps = Define.Prop<'entry', StackEntry, true> & Define.Slot<'default'>;
4
4
  /**
5
5
  * Provider wrapper for a single screen mount.
@@ -1 +1 @@
1
- {"version":3,"file":"EntryScope.d.ts","sourceRoot":"","sources":["../../src/components/EntryScope.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyC,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAGhF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,KAAK,eAAe,GACd,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,GACtC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAE7B;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU;;EAUrB,CAAC"}
1
+ {"version":3,"file":"EntryScope.d.ts","sourceRoot":"","sources":["../../src/components/EntryScope.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyC,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAQhF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,KAAK,eAAe,GACd,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,GACtC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAE7B;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU;;EAgBrB,CAAC"}
@@ -0,0 +1,34 @@
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, {}>;
34
+ //# sourceMappingURL=Layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Layer.d.ts","sourceRoot":"","sources":["../../src/components/Layer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAIH,KAAK,gBAAgB,EACrB,KAAK,MAAM,EAEd,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGrD,MAAM,MAAM,UAAU,GAChB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,GACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC;AACvC,+DAA+D;GAC7D,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC;AAE7D,eAAO,MAAM,KAAK,wCAkDhB,CAAC"}
@@ -1,5 +1,5 @@
1
- import type { RouteId, RouteParams, RouteSearch } from '../register.js';
2
- import type { RoutesWithParams } from '../hooks/use-nav.js';
1
+ import type { RouteId, RouteParams, RouteSearch } from '../register';
2
+ import type { RoutesWithParams } from '../hooks/use-nav';
3
3
  /**
4
4
  * Per-route conditional props for `<Link>`.
5
5
  *
@@ -1 +1 @@
1
- {"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../src/components/Link.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,KAAK,gBAAgB,GAAG;KACnB,CAAC,IAAI,OAAO,GAAG,CAAC,SAAS,gBAAgB,GACpC;QAAE,EAAE,EAAE,CAAC,CAAC;QAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;KAAE,GAC1D;QAAE,EAAE,EAAE,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,SAAS,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;KAAE;CAC/D,CAAC,OAAO,CAAC,CAAC;AAEX;;;;;;;;GAQG;AACH,MAAM,MAAM,SAAS,GAAG,gBAAgB,GAAG;IACvC,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAwDF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,IAAI,EAA0B,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC"}
1
+ {"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../src/components/Link.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD;;;;;;;;;;;;;GAaG;AACH,KAAK,gBAAgB,GAAG;KACnB,CAAC,IAAI,OAAO,GAAG,CAAC,SAAS,gBAAgB,GACpC;QAAE,EAAE,EAAE,CAAC,CAAC;QAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;KAAE,GAC1D;QAAE,EAAE,EAAE,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,SAAS,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;KAAE;CAC/D,CAAC,OAAO,CAAC,CAAC;AAEX;;;;;;;;GAQG;AACH,MAAM,MAAM,SAAS,GAAG,gBAAgB,GAAG;IACvC,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAwDF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,IAAI,EAA0B,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { type Define } from '@sigx/lynx';
2
- import type { RouteId } from '../register.js';
3
- import type { RouteMap } from '../types.js';
2
+ import type { RouteId } from '../register';
3
+ import type { RouteMap } from '../types';
4
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
5
  /**
6
6
  * Enable slide-from-right transitions on push/pop. Defaults to true.
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationRoot.d.ts","sourceRoot":"","sources":["../../src/components/NavigationRoot.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4C,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAInF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAgB,QAAQ,EAAc,MAAM,aAAa,CAAC;AAGtE,KAAK,mBAAmB,GAClB,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,GACrC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,GACpC,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACrD,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACvD;;;;GAIG;GACD,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC;AAClC;;;;GAIG;GACD,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAAC,GACxC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAE7B;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc;;EAiDzB,CAAC"}
1
+ {"version":3,"file":"NavigationRoot.d.ts","sourceRoot":"","sources":["../../src/components/NavigationRoot.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4C,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAInF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAgB,QAAQ,EAAc,MAAM,UAAU,CAAC;AAGnE,KAAK,mBAAmB,GAClB,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,GACrC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,GACpC,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACrD,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACvD;;;;GAIG;GACD,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC;AAClC;;;;GAIG;GACD,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAAC,GACxC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAE7B;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc;;EAiDzB,CAAC"}
@@ -50,9 +50,9 @@ type TabBarItemProps = Define.Slot<'default'>;
50
50
  * call site shown in the file header.
51
51
  */
52
52
  export declare const Screen: ((props: {
53
- title?: string | (() => string) | undefined;
54
- headerShown?: boolean | undefined;
55
53
  gestureEnabled?: boolean | undefined;
54
+ headerShown?: boolean | undefined;
55
+ title?: string | (() => string) | undefined;
56
56
  } & {} & {
57
57
  slots?: Partial<{
58
58
  default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
@@ -62,18 +62,18 @@ export declare const Screen: ((props: {
62
62
  children?: any;
63
63
  }) => import("@sigx/runtime-core").JSXElement) & {
64
64
  __setup: import("@sigx/runtime-core").SetupFn<{
65
- title?: string | (() => string) | undefined;
66
- headerShown?: boolean | undefined;
67
65
  gestureEnabled?: boolean | undefined;
66
+ headerShown?: boolean | undefined;
67
+ title?: string | (() => string) | undefined;
68
68
  }, ScreenProps, void, {
69
69
  default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
70
70
  }>;
71
71
  __name?: string;
72
72
  __islandId?: string;
73
73
  __props: {
74
- title?: string | (() => string) | undefined;
75
- headerShown?: boolean | undefined;
76
74
  gestureEnabled?: boolean | undefined;
75
+ headerShown?: boolean | undefined;
76
+ title?: string | (() => string) | undefined;
77
77
  };
78
78
  __events: ScreenProps;
79
79
  __ref: void;
@@ -1 +1 @@
1
- {"version":3,"file":"Screen.d.ts","sourceRoot":"","sources":["../../src/components/Screen.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAA0B,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAIjE,KAAK,WAAW,GACV,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC,GAC7C,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,GACnC,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,GACtC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAgB7B,KAAK,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAoB9C;;;;;;;;;GASG;AACH,KAAK,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAmB9C;;;;GAIG;AACH,eAAO,MAAM,MAAM;4BAzEwB,MAAM;;;;;;;;;;;;gCAAN,MAAM;;;;;;;;;gCAAN,MAAM;;;;;;;;;;;;;;;;;;;;;;CA8E/C,CAAC"}
1
+ {"version":3,"file":"Screen.d.ts","sourceRoot":"","sources":["../../src/components/Screen.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAA0B,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAKjE,KAAK,WAAW,GACV,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC,GAC7C,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,GACnC,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,GACtC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAiB7B,KAAK,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAoB9C;;;;;;;;;GASG;AACH,KAAK,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAmB9C;;;;GAIG;AACH,eAAO,MAAM,MAAM;;;4BA1EwB,MAAM;;;;;;;;;;;;gCAAN,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+E/C,CAAC"}
@@ -1,21 +1,91 @@
1
- import { type ComponentFactory } from '@sigx/lynx';
1
+ import { type Define } from '@sigx/lynx';
2
+ type StackProps =
3
+ /**
4
+ * Mint a nested navigator with this route at its base. When set, the
5
+ * `<Stack>` becomes the owner of a new `NavigatorState` and provides
6
+ * `useNav` / `useNavInternals` / `useNavRoutes` to its subtree, so
7
+ * `nav.push('card-route', …)` from inside the stack stays *inside* it
8
+ * (e.g. for per-tab stacks). Routes presented as `modal` / `fullScreen` /
9
+ * `transparent-modal` automatically escalate to the parent navigator
10
+ * via `nav.parent`, walking up until they reach the root — so modals
11
+ * still overlay the whole app.
12
+ *
13
+ * Omit to render the *enclosing* navigator's stack (the default — this
14
+ * is how `<NavigationRoot> → <Stack />` works).
15
+ */
16
+ Define.Prop<'initialRoute', string>
17
+ /** Initial params for the nested-stack base entry. */
18
+ & Define.Prop<'initialParams', Record<string, unknown>>
19
+ /** Initial search for the nested-stack base entry. */
20
+ & Define.Prop<'initialSearch', Record<string, unknown>>
21
+ /**
22
+ * Optional chrome rendered *above* the active screen, **inside this
23
+ * Stack's nav scope**. The intended use is `<Header />`, which needs
24
+ * to resolve `useNav()` to the per-stack nav (not the enclosing one)
25
+ * so it can react to pushes inside this stack — e.g. show a back
26
+ * button when a card is pushed onto a per-tab stack.
27
+ *
28
+ * Without this, a `<Header />` placed as a sibling of `<Stack>`
29
+ * would see the enclosing nav and never update when pushes happen
30
+ * inside the nested stack.
31
+ */
32
+ & Define.Slot<'default'>;
2
33
  /**
3
34
  * Stack navigator — renders the topmost stack entry's component at rest, or
4
35
  * the top + underneath entries during a transition.
5
36
  *
6
- * **Idle**: just the top entry, full-bleed, no transform. The screen
7
- * component mounts directly so it can use its own layout (no extra absolute
8
- * positioning that would break percentage heights).
37
+ * Two modes:
38
+ *
39
+ * **Bound** (no `initialRoute`): renders the enclosing navigator's stack.
40
+ * This is the shape used directly under `<NavigationRoot>` and is what
41
+ * single-stack apps want.
42
+ *
43
+ * **Nested-owner** (`initialRoute="…"`): mints a fresh `NavigatorState` with
44
+ * its own progress `SharedValue` and edge-back gesture, and provides
45
+ * `useNav` / `useNavInternals` / `useNavRoutes` to its subtree. `useNav()`
46
+ * inside this stack returns the nested nav; `nav.parent` points to the
47
+ * enclosing one. Per-tab stacks are the canonical use case:
48
+ *
49
+ * ```tsx
50
+ * <Tabs initialTab="trips">
51
+ * <Tabs.Screen name="trips"><Stack initialRoute="tripsHome" /></Tabs.Screen>
52
+ * <Tabs.Screen name="map"><Stack initialRoute="mapHome" /></Tabs.Screen>
53
+ * </Tabs>
54
+ * ```
55
+ *
56
+ * Modal/fullScreen pushes escalate up the parent chain automatically — so
57
+ * `nav.push('newTrip')` from inside Trips (where `newTrip` is `modal`)
58
+ * walks to root and overlays the whole UI. `replace` stays strictly local
59
+ * (asymmetric with `push`) so a modal `replace` never wipes the root stack.
60
+ *
61
+ * **Render strategy.** Stack always emits the same JSX shape — a
62
+ * relative wrapper containing one `<Layer>` per entry returned by
63
+ * `computeLayers(stack, transition, progress)`. Each Layer is an
64
+ * absolutely-positioned host view with optional MT-bound translate
65
+ * animation. The pure layer-plan function decides:
9
66
  *
10
- * **Transitioning**: two `<ScreenContainer>` instances stacked absolutely,
11
- * each with an MT-driven `translateX` that reads from the navigator's
12
- * progress `SharedValue`. The host's BG thread doesn't tick per frame —
13
- * `useAnimatedStyle` runs the interpolation entirely on MT.
67
+ * - **Idle.** Topmost non-overlay base + any overlays above it. All
68
+ * static (no transform). Overlays (`modal` / `fullScreen` /
69
+ * `transparent-modal`) keep their underneath mounted; cards
70
+ * replace their underneath in the base layer.
71
+ * - **Card transition.** Both top and underneath animate (slide-in
72
+ * + parallax). After settle, idle rules apply — the underneath
73
+ * unmounts because the new top is the sole base.
74
+ * - **Overlay transition.** The full idle layer stack up through
75
+ * the underneath stays static; only the animated top has a
76
+ * transform. After settle, the overlay either joins the static
77
+ * idle stack (push) or unmounts (pop).
14
78
  *
15
- * `key={top.key}` keeps the idle render's component instance stable across
16
- * unrelated re-renders. During transitions, composite keys
17
- * (`${entry.key}-${role}-${kind}`) ensure a fresh mount per role/kind pair so
18
- * the `useAnimatedStyle` binding is set with the right input/output ranges.
79
+ * Layer keys are `layer-${entry.key}-${animationVariant}`. The variant
80
+ * suffix forces a remount when an entry transitions from animated to
81
+ * static (or vice versa) `useAnimatedStyle` binds once at setup and
82
+ * can't switch its mapper at runtime. Modal underneath layers never
83
+ * animate, so their key is stable across the modal lifecycle and the
84
+ * subtree's state (per-tab Stack navigators, scroll positions,
85
+ * in-flight inputs) survives.
19
86
  */
20
- export declare const Stack: ComponentFactory<{}, void, unknown>;
87
+ export declare const Stack: import("@sigx/runtime-core").ComponentFactory<StackProps, void, {
88
+ default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
89
+ }>;
90
+ export {};
21
91
  //# sourceMappingURL=Stack.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Stack.d.ts","sourceRoot":"","sources":["../../src/components/Stack.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,gBAAgB,EAAoB,MAAM,YAAY,CAAC;AAQhF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,KAAK,qCA2FhB,CAAC"}
1
+ {"version":3,"file":"Stack.d.ts","sourceRoot":"","sources":["../../src/components/Stack.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOH,KAAK,MAAM,EACd,MAAM,YAAY,CAAC;AAepB,KAAK,UAAU;AACX;;;;;;;;;;;;GAYG;AACD,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC;AACrC,sDAAsD;GACpD,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACvD,sDAAsD;GACpD,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACvD;;;;;;;;;;GAUG;GACD,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAI7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,eAAO,MAAM,KAAK;;EAuPhB,CAAC"}
@@ -1,32 +1,31 @@
1
1
  /**
2
- * `<TabBar>` — default chrome for `<Tabs>`.
2
+ * `<TabBar>` — headless default chrome for `<Tabs>`.
3
3
  *
4
- * Renders a row of tab buttons reading from the enclosing `useTabs()`
5
- * navigator. Active tab is highlighted via the `active` prop on each
6
- * default button (consumers can re-style via `renderTab`).
4
+ * Renders the active-tab buttons reading from the enclosing `useTabs()`
5
+ * navigator. Intentionally **unstyled** this lives in the (theme-less)
6
+ * navigation package, so it ships pure structure + accessibility wiring.
7
+ * Themed chrome belongs in a UI-kit package: see `<NavTabBar />` in
8
+ * `@sigx/lynx-daisyui` for the daisy-themed equivalent.
7
9
  *
8
- * Customization knobs:
10
+ * Use this directly only if you want to handle styling yourself via the
11
+ * `renderTab` prop. For a "looks like a tab bar out of the box" component,
12
+ * pull `<NavTabBar />` from `@sigx/lynx-daisyui` (or your own UI kit).
13
+ *
14
+ * Customization:
9
15
  * - `renderTab`: a function `(info, ctx) => JSX` that fully replaces the
10
16
  * default button rendering for each tab. `ctx.active` tells the
11
17
  * consumer whether this tab is currently focused; `ctx.onPress`
12
- * activates the tab.
13
- *
14
- * Accessibility:
15
- * - Each default button gets `accessibility-label` from
16
- * `info.accessibilityLabel ?? info.label ?? info.name`.
17
- * - Each default button gets `accessibility-element="true"` so screen
18
- * readers see the whole pill, not just the inner `<text>`.
19
- * - Each default button gets `accessibility-trait="button"` and a
20
- * `selected` flag on the active one so VoiceOver/TalkBack announces
21
- * focus state on tab switch.
18
+ * activates the tab. **Recommended** for any visual treatment.
22
19
  *
23
- * Placement: mount inside `<Tabs>` alongside the `<Tabs.Screen>`s. Order
24
- * matters visually (place above or below the screen bodies depending on
25
- * the layout), and `<Tabs.Screen>` bodies all stack with `display:flex` so
26
- * the TabBar should be at a deterministic position in the JSX.
20
+ * Accessibility (baked into the default button — the one structural
21
+ * concern this component keeps):
22
+ * - `accessibility-label` from `info.accessibilityLabel ?? info.label ?? info.name`.
23
+ * - `accessibility-element="true"` so screen readers see the whole pill.
24
+ * - `accessibility-trait="button"` and a `selected` flag on the active
25
+ * one so VoiceOver/TalkBack announces focus state on tab switch.
27
26
  */
28
27
  import { type JSXElement } from '@sigx/lynx';
29
- import { type TabInfo } from './Tabs.js';
28
+ import { type TabInfo } from './Tabs';
30
29
  /** Rendering context passed to a `renderTab` consumer. */
31
30
  export interface TabRenderContext {
32
31
  /** True when this tab is currently active. Reactive — re-runs render on change. */
@@ -1 +1 @@
1
- {"version":3,"file":"TabBar.d.ts","sourceRoot":"","sources":["../../src/components/TabBar.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAGH,KAAK,UAAU,EAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAElD,0DAA0D;AAC1D,MAAM,WAAW,gBAAgB;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,2EAA2E;IAC3E,OAAO,IAAI,IAAI,CAAC;CACnB;AAmCD,eAAO,MAAM,MAAM;wBAhCmB,OAAO,OAAO,gBAAgB,KAAK,UAAU;YA4DjF,CAAC"}
1
+ {"version":3,"file":"TabBar.d.ts","sourceRoot":"","sources":["../../src/components/TabBar.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAGH,KAAK,UAAU,EAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAE/C,0DAA0D;AAC1D,MAAM,WAAW,gBAAgB;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,2EAA2E;IAC3E,OAAO,IAAI,IAAI,CAAC;CACnB;AAoCD,eAAO,MAAM,MAAM;wBAjCmB,OAAO,OAAO,gBAAgB,KAAK,UAAU;YA6DjF,CAAC"}
@@ -4,31 +4,32 @@
4
4
  * Usage:
5
5
  *
6
6
  * ```tsx
7
- * <NavigationRoot routes={routes}>
8
- * <Tabs initialTab="feed">
9
- * <Tabs.Screen name="feed" icon={<FeedIcon />} label="Feed">
10
- * <FeedView />
11
- * </Tabs.Screen>
12
- * <Tabs.Screen name="me" icon={<MeIcon />} label="Profile">
13
- * <ProfileView />
14
- * </Tabs.Screen>
15
- * </Tabs>
7
+ * <NavigationRoot routes={routes} initialRoute="root">
8
+ * <Stack />
16
9
  * </NavigationRoot>
17
- * ```
18
10
  *
19
- * Scope of this slice (v0.1): pure UI primitive. Each tab's body stays
20
- * mounted for state preservation (the inactive ones render with
21
- * `display: 'none'`). Active tab is reactive via `useTabs()`.
11
+ * // The route "root" component renders:
12
+ * <Tabs initialTab="feed">
13
+ * <Tabs.Screen name="feed" icon={<FeedIcon />} label="Feed">
14
+ * <Stack initialRoute="feedHome" />
15
+ * </Tabs.Screen>
16
+ * <Tabs.Screen name="me" icon={<MeIcon />} label="Profile">
17
+ * <Stack initialRoute="profileHome" />
18
+ * </Tabs.Screen>
19
+ * <TabBar />
20
+ * </Tabs>
21
+ * ```
22
22
  *
23
- * Out of scope (deferred to a nested-navigators slice):
24
- * - Per-tab `<Stack>` with its own navigator state machine
25
- * - `nav.parent` chain into the Tabs nav
26
- * - Named navigators (`useNav('root')`)
23
+ * Tab bodies stay mounted across switches (the inactive ones render with
24
+ * `display: 'none'`), so each tab's nested `<Stack>` keeps its history when
25
+ * the user flips back to it. The active tab is reactive via `useTabs()`.
27
26
  *
28
- * Those build on multi-navigator-state plumbing that isn't ready yet.
29
- * For now, the inner content of a `<Tabs.Screen>` shares the same nav as
30
- * its outer `<NavigationRoot>` usable for shallow tab apps, but full
31
- * nested routing comes later.
27
+ * Per-tab stacks: each `<Tabs.Screen>` can host a `<Stack initialRoute="…">`
28
+ * which mints its own navigator. `useNav()` inside that subtree resolves to
29
+ * the tab's stack, so `nav.push('card-route', …)` stays inside the tab.
30
+ * Routes presented as `modal` / `fullScreen` / `transparent-modal` escalate
31
+ * up `nav.parent` to the root navigator automatically — they overlay the
32
+ * tabs UI (TabBar included) and dismiss back into the originating tab.
32
33
  */
33
34
  import { type Define, type JSXElement } from '@sigx/lynx';
34
35
  /** Metadata about a registered `<Tabs.Screen>`. */
@@ -59,6 +60,14 @@ export interface TabsNav {
59
60
  * Access the enclosing Tabs navigator. Throws when called outside `<Tabs>`.
60
61
  */
61
62
  export declare const useTabs: import("@sigx/runtime-core").InjectableFunction<TabsNav>;
63
+ /**
64
+ * @internal
65
+ * Provided by each `<Tabs.Screen>` so a nested `<Stack initialRoute>` can
66
+ * discover *which* tab it's hosted by, and gate its focus state on that
67
+ * tab being active. Throws when called outside a `<Tabs.Screen>` body so
68
+ * the gate degrades to "always active" via the caller's try/catch.
69
+ */
70
+ export declare const useTabScreenName: import("@sigx/runtime-core").InjectableFunction<string>;
62
71
  type TabsProps = Define.Prop<'initialTab', string> & Define.Slot<'default'>;
63
72
  type TabsScreenProps = Define.Prop<'name', string, true> & Define.Prop<'icon', JSXElement> & Define.Prop<'label', string> & Define.Prop<'accessibilityLabel', string> & Define.Slot<'default'>;
64
73
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../src/components/Tabs.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAQH,KAAK,MAAM,EACX,KAAK,UAAU,EAElB,MAAM,YAAY,CAAC;AAEpB,mDAAmD;AACnD,MAAM,WAAW,OAAO;IACpB,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,yDAAyD;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACxC;AAED,kDAAkD;AAClD,MAAM,WAAW,OAAO;IACpB,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,0DAIlB,CAAC;AAsBH,KAAK,SAAS,GACR,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,GACjC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAgE7B,KAAK,eAAe,GACd,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,GACjC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,GAC/B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,GAC5B,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC,GACzC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAoC7B;;;;;GAKG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAEf,CAAC"}
1
+ {"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../src/components/Tabs.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAQH,KAAK,MAAM,EACX,KAAK,UAAU,EAElB,MAAM,YAAY,CAAC;AAEpB,mDAAmD;AACnD,MAAM,WAAW,OAAO;IACpB,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,yDAAyD;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACxC;AAED,kDAAkD;AAClD,MAAM,WAAW,OAAO;IACpB,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,0DAIlB,CAAC;AAsBH;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,yDAI3B,CAAC;AAEH,KAAK,SAAS,GACR,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,GACjC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAgE7B,KAAK,eAAe,GACd,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,GACjC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,GAC/B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,GAC5B,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC,GACzC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAsD7B;;;;;GAKG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAEf,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { RouteMap } from './types.js';
1
+ import type { RouteMap } from './types';
2
2
  /**
3
3
  * Define a typed route registry.
4
4
  *
@@ -1 +1 @@
1
- {"version":3,"file":"define-routes.d.ts","sourceRoot":"","sources":["../src/define-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAEnE"}
1
+ {"version":3,"file":"define-routes.d.ts","sourceRoot":"","sources":["../src/define-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAEnE"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-focus.d.ts","sourceRoot":"","sources":["../../src/hooks/use-focus.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,KAAK,QAAQ,EAChB,MAAM,YAAY,CAAC;AAIpB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,IAAI,QAAQ,CAAC,OAAO,CAAC,CAOhD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CA0BlE"}
1
+ {"version":3,"file":"use-focus.d.ts","sourceRoot":"","sources":["../../src/hooks/use-focus.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,KAAK,QAAQ,EAChB,MAAM,YAAY,CAAC;AAIpB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,IAAI,QAAQ,CAAC,OAAO,CAAC,CAahD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CA0BlE"}