@idealyst/mcp-server 1.2.24 → 1.2.26

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 (193) hide show
  1. package/dist/index.cjs +22366 -0
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.d.cts +1 -0
  4. package/dist/index.d.ts +0 -2
  5. package/dist/index.js +22186 -1034
  6. package/dist/index.js.map +1 -1
  7. package/package.json +17 -7
  8. package/dist/data/cli-commands.d.ts +0 -2
  9. package/dist/data/cli-commands.d.ts.map +0 -1
  10. package/dist/data/cli-commands.js +0 -100
  11. package/dist/data/cli-commands.js.map +0 -1
  12. package/dist/data/components/Accordion.d.ts +0 -15
  13. package/dist/data/components/Accordion.d.ts.map +0 -1
  14. package/dist/data/components/Accordion.js +0 -113
  15. package/dist/data/components/Accordion.js.map +0 -1
  16. package/dist/data/components/ActivityIndicator.d.ts +0 -15
  17. package/dist/data/components/ActivityIndicator.d.ts.map +0 -1
  18. package/dist/data/components/ActivityIndicator.js +0 -80
  19. package/dist/data/components/ActivityIndicator.js.map +0 -1
  20. package/dist/data/components/Alert.d.ts +0 -15
  21. package/dist/data/components/Alert.d.ts.map +0 -1
  22. package/dist/data/components/Alert.js +0 -130
  23. package/dist/data/components/Alert.js.map +0 -1
  24. package/dist/data/components/Avatar.d.ts +0 -15
  25. package/dist/data/components/Avatar.d.ts.map +0 -1
  26. package/dist/data/components/Avatar.js +0 -91
  27. package/dist/data/components/Avatar.js.map +0 -1
  28. package/dist/data/components/Badge.d.ts +0 -15
  29. package/dist/data/components/Badge.d.ts.map +0 -1
  30. package/dist/data/components/Badge.js +0 -64
  31. package/dist/data/components/Badge.js.map +0 -1
  32. package/dist/data/components/Breadcrumb.d.ts +0 -15
  33. package/dist/data/components/Breadcrumb.d.ts.map +0 -1
  34. package/dist/data/components/Breadcrumb.js +0 -92
  35. package/dist/data/components/Breadcrumb.js.map +0 -1
  36. package/dist/data/components/Button.d.ts +0 -16
  37. package/dist/data/components/Button.d.ts.map +0 -1
  38. package/dist/data/components/Button.js +0 -118
  39. package/dist/data/components/Button.js.map +0 -1
  40. package/dist/data/components/Card.d.ts +0 -15
  41. package/dist/data/components/Card.d.ts.map +0 -1
  42. package/dist/data/components/Card.js +0 -75
  43. package/dist/data/components/Card.js.map +0 -1
  44. package/dist/data/components/Checkbox.d.ts +0 -15
  45. package/dist/data/components/Checkbox.d.ts.map +0 -1
  46. package/dist/data/components/Checkbox.js +0 -118
  47. package/dist/data/components/Checkbox.js.map +0 -1
  48. package/dist/data/components/Chip.d.ts +0 -15
  49. package/dist/data/components/Chip.d.ts.map +0 -1
  50. package/dist/data/components/Chip.js +0 -94
  51. package/dist/data/components/Chip.js.map +0 -1
  52. package/dist/data/components/Dialog.d.ts +0 -15
  53. package/dist/data/components/Dialog.d.ts.map +0 -1
  54. package/dist/data/components/Dialog.js +0 -137
  55. package/dist/data/components/Dialog.js.map +0 -1
  56. package/dist/data/components/Divider.d.ts +0 -15
  57. package/dist/data/components/Divider.d.ts.map +0 -1
  58. package/dist/data/components/Divider.js +0 -68
  59. package/dist/data/components/Divider.js.map +0 -1
  60. package/dist/data/components/Icon.d.ts +0 -15
  61. package/dist/data/components/Icon.d.ts.map +0 -1
  62. package/dist/data/components/Icon.js +0 -68
  63. package/dist/data/components/Icon.js.map +0 -1
  64. package/dist/data/components/Image.d.ts +0 -15
  65. package/dist/data/components/Image.d.ts.map +0 -1
  66. package/dist/data/components/Image.js +0 -119
  67. package/dist/data/components/Image.js.map +0 -1
  68. package/dist/data/components/Input.d.ts +0 -15
  69. package/dist/data/components/Input.d.ts.map +0 -1
  70. package/dist/data/components/Input.js +0 -155
  71. package/dist/data/components/Input.js.map +0 -1
  72. package/dist/data/components/Link.d.ts +0 -15
  73. package/dist/data/components/Link.d.ts.map +0 -1
  74. package/dist/data/components/Link.js +0 -142
  75. package/dist/data/components/Link.js.map +0 -1
  76. package/dist/data/components/List.d.ts +0 -15
  77. package/dist/data/components/List.d.ts.map +0 -1
  78. package/dist/data/components/List.js +0 -113
  79. package/dist/data/components/List.js.map +0 -1
  80. package/dist/data/components/Menu.d.ts +0 -15
  81. package/dist/data/components/Menu.d.ts.map +0 -1
  82. package/dist/data/components/Menu.js +0 -123
  83. package/dist/data/components/Menu.js.map +0 -1
  84. package/dist/data/components/Popover.d.ts +0 -15
  85. package/dist/data/components/Popover.d.ts.map +0 -1
  86. package/dist/data/components/Popover.js +0 -157
  87. package/dist/data/components/Popover.js.map +0 -1
  88. package/dist/data/components/Pressable.d.ts +0 -15
  89. package/dist/data/components/Pressable.d.ts.map +0 -1
  90. package/dist/data/components/Pressable.js +0 -125
  91. package/dist/data/components/Pressable.js.map +0 -1
  92. package/dist/data/components/Progress.d.ts +0 -15
  93. package/dist/data/components/Progress.d.ts.map +0 -1
  94. package/dist/data/components/Progress.js +0 -93
  95. package/dist/data/components/Progress.js.map +0 -1
  96. package/dist/data/components/RadioButton.d.ts +0 -15
  97. package/dist/data/components/RadioButton.d.ts.map +0 -1
  98. package/dist/data/components/RadioButton.js +0 -131
  99. package/dist/data/components/RadioButton.js.map +0 -1
  100. package/dist/data/components/SVGImage.d.ts +0 -15
  101. package/dist/data/components/SVGImage.d.ts.map +0 -1
  102. package/dist/data/components/SVGImage.js +0 -112
  103. package/dist/data/components/SVGImage.js.map +0 -1
  104. package/dist/data/components/Screen.d.ts +0 -15
  105. package/dist/data/components/Screen.d.ts.map +0 -1
  106. package/dist/data/components/Screen.js +0 -109
  107. package/dist/data/components/Screen.js.map +0 -1
  108. package/dist/data/components/Select.d.ts +0 -15
  109. package/dist/data/components/Select.d.ts.map +0 -1
  110. package/dist/data/components/Select.js +0 -141
  111. package/dist/data/components/Select.js.map +0 -1
  112. package/dist/data/components/Skeleton.d.ts +0 -15
  113. package/dist/data/components/Skeleton.d.ts.map +0 -1
  114. package/dist/data/components/Skeleton.js +0 -100
  115. package/dist/data/components/Skeleton.js.map +0 -1
  116. package/dist/data/components/Slider.d.ts +0 -15
  117. package/dist/data/components/Slider.d.ts.map +0 -1
  118. package/dist/data/components/Slider.js +0 -151
  119. package/dist/data/components/Slider.js.map +0 -1
  120. package/dist/data/components/Switch.d.ts +0 -15
  121. package/dist/data/components/Switch.d.ts.map +0 -1
  122. package/dist/data/components/Switch.js +0 -128
  123. package/dist/data/components/Switch.js.map +0 -1
  124. package/dist/data/components/TabBar.d.ts +0 -17
  125. package/dist/data/components/TabBar.d.ts.map +0 -1
  126. package/dist/data/components/TabBar.js +0 -244
  127. package/dist/data/components/TabBar.js.map +0 -1
  128. package/dist/data/components/Table.d.ts +0 -15
  129. package/dist/data/components/Table.d.ts.map +0 -1
  130. package/dist/data/components/Table.js +0 -159
  131. package/dist/data/components/Table.js.map +0 -1
  132. package/dist/data/components/Tabs.d.ts +0 -15
  133. package/dist/data/components/Tabs.d.ts.map +0 -1
  134. package/dist/data/components/Tabs.js +0 -150
  135. package/dist/data/components/Tabs.js.map +0 -1
  136. package/dist/data/components/Text.d.ts +0 -15
  137. package/dist/data/components/Text.d.ts.map +0 -1
  138. package/dist/data/components/Text.js +0 -97
  139. package/dist/data/components/Text.js.map +0 -1
  140. package/dist/data/components/TextArea.d.ts +0 -15
  141. package/dist/data/components/TextArea.d.ts.map +0 -1
  142. package/dist/data/components/TextArea.js +0 -156
  143. package/dist/data/components/TextArea.js.map +0 -1
  144. package/dist/data/components/Tooltip.d.ts +0 -15
  145. package/dist/data/components/Tooltip.d.ts.map +0 -1
  146. package/dist/data/components/Tooltip.js +0 -103
  147. package/dist/data/components/Tooltip.js.map +0 -1
  148. package/dist/data/components/Video.d.ts +0 -15
  149. package/dist/data/components/Video.d.ts.map +0 -1
  150. package/dist/data/components/Video.js +0 -166
  151. package/dist/data/components/Video.js.map +0 -1
  152. package/dist/data/components/View.d.ts +0 -15
  153. package/dist/data/components/View.d.ts.map +0 -1
  154. package/dist/data/components/View.js +0 -127
  155. package/dist/data/components/View.js.map +0 -1
  156. package/dist/data/components/index.d.ts +0 -38
  157. package/dist/data/components/index.d.ts.map +0 -1
  158. package/dist/data/components/index.js +0 -113
  159. package/dist/data/components/index.js.map +0 -1
  160. package/dist/data/framework-guides.d.ts +0 -2
  161. package/dist/data/framework-guides.d.ts.map +0 -1
  162. package/dist/data/framework-guides.js +0 -1730
  163. package/dist/data/framework-guides.js.map +0 -1
  164. package/dist/data/icon-guide.d.ts +0 -2
  165. package/dist/data/icon-guide.d.ts.map +0 -1
  166. package/dist/data/icon-guide.js +0 -285
  167. package/dist/data/icon-guide.js.map +0 -1
  168. package/dist/data/icons.json +0 -7452
  169. package/dist/data/navigation-guides.d.ts +0 -2
  170. package/dist/data/navigation-guides.d.ts.map +0 -1
  171. package/dist/data/navigation-guides.js +0 -2144
  172. package/dist/data/navigation-guides.js.map +0 -1
  173. package/dist/data/packages.d.ts +0 -39
  174. package/dist/data/packages.d.ts.map +0 -1
  175. package/dist/data/packages.js +0 -550
  176. package/dist/data/packages.js.map +0 -1
  177. package/dist/data/recipes.d.ts +0 -36
  178. package/dist/data/recipes.d.ts.map +0 -1
  179. package/dist/data/recipes.js +0 -2945
  180. package/dist/data/recipes.js.map +0 -1
  181. package/dist/data/storage-guides.d.ts +0 -2
  182. package/dist/data/storage-guides.d.ts.map +0 -1
  183. package/dist/data/storage-guides.js +0 -418
  184. package/dist/data/storage-guides.js.map +0 -1
  185. package/dist/data/translate-guides.d.ts +0 -2
  186. package/dist/data/translate-guides.d.ts.map +0 -1
  187. package/dist/data/translate-guides.js +0 -1030
  188. package/dist/data/translate-guides.js.map +0 -1
  189. package/dist/index.d.ts.map +0 -1
  190. package/dist/tools/get-types.d.ts +0 -37
  191. package/dist/tools/get-types.d.ts.map +0 -1
  192. package/dist/tools/get-types.js +0 -148
  193. package/dist/tools/get-types.js.map +0 -1
@@ -1,2144 +0,0 @@
1
- export const navigationGuides = {
2
- "idealyst://navigation/overview": `# Navigation System Overview
3
-
4
- The Idealyst navigation system provides a unified API for both React Native and web applications, handling routing seamlessly across platforms.
5
-
6
- ## Core Concepts
7
-
8
- ### Cross-Platform Routing
9
- - **Mobile (React Native)**: Uses React Navigation for native navigation patterns
10
- - **Web**: Uses React Router for browser-based routing
11
- - **Unified API**: Same code works on both platforms
12
-
13
- ### Route Types
14
-
15
- There are two fundamental route types:
16
-
17
- #### 1. Screen Routes
18
- Renders a component directly to the screen:
19
- \`\`\`tsx
20
- {
21
- path: "profile",
22
- type: 'screen',
23
- component: ProfileScreen
24
- }
25
- \`\`\`
26
-
27
- #### 2. Navigator Routes
28
- Wraps child routes with navigation structure:
29
- \`\`\`tsx
30
- {
31
- path: "/",
32
- type: 'navigator',
33
- layout: 'stack', // or 'tab', 'drawer', 'modal'
34
- routes: [
35
- { path: "home", type: 'screen', component: HomeScreen },
36
- { path: "settings", type: 'screen', component: SettingsScreen },
37
- ]
38
- }
39
- \`\`\`
40
-
41
- ## Setup
42
-
43
- ### Basic Setup
44
-
45
- \`\`\`tsx
46
- import { NavigatorProvider } from '@idealyst/navigation';
47
-
48
- function App() {
49
- return (
50
- <NavigatorProvider route={appRouter}>
51
- {/* Content managed by router */}
52
- </NavigatorProvider>
53
- );
54
- }
55
- \`\`\`
56
-
57
- ### Quick Start with Examples
58
-
59
- Use the pre-built example router for instant working navigation:
60
-
61
- \`\`\`tsx
62
- import { ExampleNavigationRouter } from '@idealyst/navigation/examples';
63
-
64
- <NavigatorProvider route={ExampleNavigationRouter} />
65
- \`\`\`
66
-
67
- The example router demonstrates:
68
- - Stack-based navigation structure
69
- - Custom web layouts with header and sidebar
70
- - Tab navigation for nested sections
71
-
72
- ## Platform Differences
73
-
74
- ### Mobile (React Native)
75
- - Stack navigator: Native stack navigation with animations
76
- - Tab navigator: Bottom tab bar
77
- - Drawer navigator: Side drawer with gestures
78
- - Modal navigator: Full-screen modal presentation
79
-
80
- ### Web
81
- - Stack navigator: Browser history-based routing
82
- - Tab navigator: Top or side tab navigation
83
- - All navigators: Use URL paths for routing
84
- - Custom layouts: Can add headers, sidebars, footers
85
-
86
- ## Key Features
87
-
88
- 1. **Type-Safe Navigation**: Full TypeScript support
89
- 2. **Path Parameters**: \`/user/:id\` support
90
- 3. **Nested Routes**: Unlimited nesting depth
91
- 4. **Custom Layouts**: Web-specific layout components
92
- 5. **Theme Integration**: Works with @idealyst/theme
93
- 6. **Cross-Platform**: Write once, run everywhere
94
- `,
95
- "idealyst://navigation/route-configuration": `# Route Configuration
96
-
97
- Learn how to define and structure routes in your Idealyst application.
98
-
99
- ## Route Definition
100
-
101
- ### Screen Route
102
-
103
- The simplest route type - renders a component:
104
-
105
- \`\`\`tsx
106
- import { RouteParam } from '@idealyst/navigation';
107
-
108
- const route: RouteParam = {
109
- path: "profile",
110
- type: 'screen',
111
- component: ProfileScreen,
112
- options: {
113
- title: "User Profile",
114
- headerShown: true
115
- }
116
- };
117
- \`\`\`
118
-
119
- ### Navigator Route
120
-
121
- A route that contains child routes:
122
-
123
- \`\`\`tsx
124
- const route: RouteParam = {
125
- path: "/",
126
- type: 'navigator',
127
- layout: 'stack',
128
- routes: [
129
- { path: "home", type: 'screen', component: HomeScreen },
130
- { path: "about", type: 'screen', component: AboutScreen },
131
- ],
132
- options: {
133
- headerShown: false
134
- }
135
- };
136
- \`\`\`
137
-
138
- ## Path Configuration
139
-
140
- ### Basic Paths
141
-
142
- \`\`\`tsx
143
- { path: "home", ... } // Relative: /parent/home
144
- { path: "/home", ... } // Absolute: /home
145
- { path: "", ... } // Index route
146
- \`\`\`
147
-
148
- ### Path Parameters
149
-
150
- \`\`\`tsx
151
- {
152
- path: "user/:id",
153
- type: 'screen',
154
- component: UserScreen
155
- }
156
-
157
- // Navigate with:
158
- navigator.navigate({
159
- path: "/user/:id",
160
- vars: { id: "123" }
161
- });
162
-
163
- // Access in component:
164
- import { useParams } from '@idealyst/navigation';
165
-
166
- const params = useParams();
167
- const userId = params.id;
168
- \`\`\`
169
-
170
- ### Optional Parameters
171
-
172
- \`\`\`tsx
173
- { path: "search/:query?" } // query is optional
174
- \`\`\`
175
-
176
- ## Screen Options
177
-
178
- ### Common Options
179
-
180
- \`\`\`tsx
181
- type ScreenOptions = {
182
- title?: string; // Screen title
183
- headerShown?: boolean; // Show/hide header (mobile)
184
- };
185
- \`\`\`
186
-
187
- ### Tab-Specific Options
188
-
189
- For tab navigators:
190
-
191
- \`\`\`tsx
192
- type TabBarScreenOptions = {
193
- tabBarIcon?: (props: {
194
- focused: boolean;
195
- color: string;
196
- size: string | number
197
- }) => React.ReactElement;
198
-
199
- tabBarLabel?: string; // Tab label
200
- tabBarBadge?: string | number; // Badge count
201
- tabBarVisible?: boolean; // Show/hide tab
202
- } & ScreenOptions;
203
- \`\`\`
204
-
205
- Example:
206
- \`\`\`tsx
207
- {
208
- path: "home",
209
- type: 'screen',
210
- component: HomeScreen,
211
- options: {
212
- tabBarLabel: "Home",
213
- tabBarIcon: ({ focused, color }) => (
214
- <Icon name="home" color={color} />
215
- ),
216
- tabBarBadge: 5
217
- }
218
- }
219
- \`\`\`
220
-
221
- ## Navigator Options
222
-
223
- Options for navigator routes:
224
-
225
- \`\`\`tsx
226
- type NavigatorOptions = {
227
- headerTitle?: React.ComponentType | React.ReactElement | string;
228
- headerLeft?: React.ComponentType | React.ReactElement;
229
- headerBackVisible?: boolean;
230
- headerRight?: React.ComponentType | React.ReactElement;
231
- headerShown?: boolean;
232
- };
233
- \`\`\`
234
-
235
- Example:
236
- \`\`\`tsx
237
- {
238
- path: "/app",
239
- type: 'navigator',
240
- layout: 'stack',
241
- routes: [...],
242
- options: {
243
- headerTitle: "My App",
244
- headerShown: true,
245
- headerRight: <UserMenu />
246
- }
247
- }
248
- \`\`\`
249
-
250
- ## Invalid Route Handling
251
-
252
- Navigators can specify how to handle invalid routes:
253
-
254
- \`\`\`tsx
255
- import { NavigatorParam, NotFoundComponentProps } from '@idealyst/navigation';
256
-
257
- const NotFoundPage = ({ path, params }: NotFoundComponentProps) => (
258
- <View>
259
- <Text>Page not found: {path}</Text>
260
- </View>
261
- );
262
-
263
- const routes: NavigatorParam = {
264
- path: "/",
265
- type: 'navigator',
266
- layout: 'stack',
267
- // Component to show when route is invalid
268
- notFoundComponent: NotFoundPage,
269
- // Handler to redirect or show 404
270
- onInvalidRoute: (path) => {
271
- if (path.startsWith('/old-')) {
272
- return { path: '/new-section', replace: true };
273
- }
274
- return undefined; // Show notFoundComponent
275
- },
276
- routes: [...]
277
- };
278
- \`\`\`
279
-
280
- See the **Invalid Route Handling** guide for complete documentation.
281
-
282
- ## Nested Routes
283
-
284
- Create hierarchical navigation:
285
-
286
- \`\`\`tsx
287
- const appRouter: RouteParam = {
288
- path: "/",
289
- type: 'navigator',
290
- layout: 'stack',
291
- routes: [
292
- {
293
- path: "dashboard",
294
- type: 'navigator',
295
- layout: 'tab',
296
- routes: [
297
- { path: "overview", type: 'screen', component: OverviewScreen },
298
- { path: "analytics", type: 'screen', component: AnalyticsScreen },
299
- { path: "reports", type: 'screen', component: ReportsScreen },
300
- ]
301
- },
302
- {
303
- path: "settings",
304
- type: 'screen',
305
- component: SettingsScreen
306
- },
307
- ]
308
- };
309
- \`\`\`
310
-
311
- ## Full Path Resolution
312
-
313
- Full paths are automatically computed:
314
- - \`/dashboard/overview\`
315
- - \`/dashboard/analytics\`
316
- - \`/dashboard/reports\`
317
- - \`/settings\`
318
-
319
- ## Best Practices
320
-
321
- 1. **Use relative paths** for child routes
322
- 2. **Keep nesting shallow** (3 levels max recommended)
323
- 3. **Group related screens** in navigators
324
- 4. **Use meaningful paths** that reflect content
325
- 5. **Define index routes** with empty path
326
- 6. **Set appropriate options** for each screen type
327
- `,
328
- "idealyst://navigation/navigator-types": `# Navigator Types
329
-
330
- Idealyst supports four navigator types: stack, tab, drawer, and modal. Each provides different navigation patterns.
331
-
332
- ## Stack Navigator
333
-
334
- Linear, hierarchical navigation - the most common pattern.
335
-
336
- ### Configuration
337
-
338
- \`\`\`tsx
339
- {
340
- path: "/app",
341
- type: 'navigator',
342
- layout: 'stack',
343
- routes: [
344
- { path: "", type: 'screen', component: HomeScreen },
345
- { path: "profile", type: 'screen', component: ProfileScreen },
346
- { path: "settings", type: 'screen', component: SettingsScreen },
347
- ]
348
- }
349
- \`\`\`
350
-
351
- ### Platform Behavior
352
-
353
- **Mobile:**
354
- - Native stack navigation with slide animations
355
- - Hardware back button support
356
- - Swipe-to-go-back gesture
357
-
358
- **Web:**
359
- - Browser history integration
360
- - URL updates on navigation
361
- - Back/forward browser buttons work
362
-
363
- ### Use Cases
364
- - Main app navigation
365
- - Detail views
366
- - Settings flows
367
- - Onboarding sequences
368
-
369
- ## Tab Navigator
370
-
371
- Section-based navigation with a tab bar.
372
-
373
- ### Configuration
374
-
375
- \`\`\`tsx
376
- {
377
- path: "/",
378
- type: 'navigator',
379
- layout: 'tab',
380
- routes: [
381
- {
382
- path: "home",
383
- type: 'screen',
384
- component: HomeScreen,
385
- options: {
386
- tabBarLabel: "Home",
387
- tabBarIcon: ({ color }) => <Icon name="home" color={color} />
388
- }
389
- },
390
- {
391
- path: "search",
392
- type: 'screen',
393
- component: SearchScreen,
394
- options: {
395
- tabBarLabel: "Search",
396
- tabBarIcon: ({ color }) => <Icon name="search" color={color} />
397
- }
398
- },
399
- ]
400
- }
401
- \`\`\`
402
-
403
- ### Platform Behavior
404
-
405
- **Mobile:**
406
- - Bottom tab bar
407
- - Tab icons and labels
408
- - Badge support
409
- - Active tab highlighting
410
-
411
- **Web:**
412
- - Top or side tabs
413
- - Can use custom layout component
414
- - URL-based tab switching
415
-
416
- ### Use Cases
417
- - Main app sections
418
- - Content categories
419
- - Dashboard views
420
- - Multi-section interfaces
421
-
422
- ## Drawer Navigator
423
-
424
- Side menu navigation, primarily for desktop/tablet.
425
-
426
- ### Configuration
427
-
428
- \`\`\`tsx
429
- {
430
- path: "/",
431
- type: 'navigator',
432
- layout: 'drawer',
433
- routes: [
434
- { path: "dashboard", type: 'screen', component: DashboardScreen },
435
- { path: "users", type: 'screen', component: UsersScreen },
436
- { path: "settings", type: 'screen', component: SettingsScreen },
437
- ]
438
- }
439
- \`\`\`
440
-
441
- ### Platform Behavior
442
-
443
- **Mobile:**
444
- - Slide-out drawer
445
- - Swipe gesture to open
446
- - Overlay when open
447
-
448
- **Web:**
449
- - Sidebar navigation
450
- - Can be persistent or overlay
451
- - Responsive behavior
452
-
453
- ### Use Cases
454
- - Admin panels
455
- - Desktop applications
456
- - Content management systems
457
- - Multi-section apps
458
-
459
- ## Modal Navigator
460
-
461
- Overlay navigation for temporary screens.
462
-
463
- ### Configuration
464
-
465
- \`\`\`tsx
466
- {
467
- path: "/modals",
468
- type: 'navigator',
469
- layout: 'modal',
470
- routes: [
471
- { path: "new-post", type: 'screen', component: NewPostScreen },
472
- { path: "edit-profile", type: 'screen', component: EditProfileScreen },
473
- ]
474
- }
475
- \`\`\`
476
-
477
- ### Platform Behavior
478
-
479
- **Mobile:**
480
- - Full-screen modal presentation
481
- - Slide-up animation
482
- - Close gesture support
483
-
484
- **Web:**
485
- - Overlay modal
486
- - Background dimming
487
- - Click-outside to close
488
-
489
- ### Use Cases
490
- - Forms and data entry
491
- - Action confirmations
492
- - Image viewers
493
- - Temporary content
494
-
495
- ## Combining Navigator Types
496
-
497
- Navigators can be nested for complex flows:
498
-
499
- \`\`\`tsx
500
- const appRouter: RouteParam = {
501
- path: "/",
502
- type: 'navigator',
503
- layout: 'stack',
504
- routes: [
505
- {
506
- path: "main",
507
- type: 'navigator',
508
- layout: 'tab',
509
- routes: [
510
- { path: "feed", type: 'screen', component: FeedScreen },
511
- { path: "profile", type: 'screen', component: ProfileScreen },
512
- ]
513
- },
514
- {
515
- path: "modals",
516
- type: 'navigator',
517
- layout: 'modal',
518
- routes: [
519
- { path: "create", type: 'screen', component: CreateScreen },
520
- ]
521
- }
522
- ]
523
- };
524
- \`\`\`
525
-
526
- ## Choosing the Right Navigator
527
-
528
- | Navigator | Mobile | Desktop | Use Case |
529
- |-----------|--------|---------|----------|
530
- | **Stack** | ✅ Primary | ✅ Primary | Hierarchical navigation |
531
- | **Tab** | ✅ Primary | ✅ Secondary | Section-based navigation |
532
- | **Drawer** | ⚠️ Secondary | ✅ Primary | Menu-based navigation |
533
- | **Modal** | ✅ Common | ✅ Common | Temporary overlays |
534
-
535
- ## Best Practices
536
-
537
- 1. **Use stack for most flows** - It's the most universal pattern
538
- 2. **Limit tab count** - 3-5 tabs maximum for mobile
539
- 3. **Reserve drawers for complex apps** - Best on desktop
540
- 4. **Use modals sparingly** - For focused, temporary tasks
541
- 5. **Consider platform** - What works on mobile may not work on web
542
- 6. **Test navigation flow** - Ensure intuitive user experience
543
- `,
544
- "idealyst://navigation/custom-layouts": `# Custom Layouts (Web Only)
545
-
546
- On web, navigators can use custom layout components to add headers, sidebars, and other UI around route content.
547
-
548
- ## GeneralLayout Component
549
-
550
- The built-in \`GeneralLayout\` provides header and sidebar functionality:
551
-
552
- ### Basic Usage
553
-
554
- \`\`\`tsx
555
- import { GeneralLayout } from '@idealyst/navigation';
556
-
557
- <GeneralLayout
558
- header={{
559
- enabled: true,
560
- content: <Text>My App</Text>,
561
- }}
562
- sidebar={{
563
- enabled: true,
564
- content: <NavigationMenu />,
565
- }}
566
- >
567
- {children}
568
- </GeneralLayout>
569
- \`\`\`
570
-
571
- ### Header Configuration
572
-
573
- \`\`\`tsx
574
- type HeaderConfig = {
575
- enabled: boolean; // Show/hide header
576
- height?: number; // Header height (default: 64)
577
- content?: React.ReactNode; // Header content
578
- style?: ViewStyle; // Custom styles
579
- };
580
- \`\`\`
581
-
582
- Example:
583
- \`\`\`tsx
584
- header={{
585
- enabled: true,
586
- height: 80,
587
- content: (
588
- <View style={{ flexDirection: 'row', justifyContent: 'space-between', padding: 16 }}>
589
- <Text size="lg" weight="bold">Dashboard</Text>
590
- <UserMenu />
591
- </View>
592
- )
593
- }}
594
- \`\`\`
595
-
596
- ### Sidebar Configuration
597
-
598
- \`\`\`tsx
599
- type SidebarConfig = {
600
- enabled: boolean; // Show/hide sidebar
601
- collapsible?: boolean; // Allow collapse/expand
602
- position?: 'left' | 'right'; // Sidebar position
603
- initiallyExpanded?: boolean; // Initial state
604
- expandedWidth?: number; // Width when expanded (default: 240)
605
- collapsedWidth?: number; // Width when collapsed (default: 64)
606
- content?: React.ReactNode; // Sidebar content
607
- style?: ViewStyle; // Custom styles
608
- };
609
- \`\`\`
610
-
611
- Example:
612
- \`\`\`tsx
613
- sidebar={{
614
- enabled: true,
615
- collapsible: true,
616
- position: 'left',
617
- initiallyExpanded: true,
618
- expandedWidth: 280,
619
- collapsedWidth: 72,
620
- content: <NavigationSidebar />
621
- }}
622
- \`\`\`
623
-
624
- ## Using Layouts with Navigators
625
-
626
- ### Stack Navigator with Layout
627
-
628
- \`\`\`tsx
629
- import { CustomStackLayout } from './layouts/CustomStackLayout';
630
-
631
- const router: NavigatorParam = {
632
- path: "/",
633
- type: 'navigator',
634
- layout: 'stack',
635
- layoutComponent: CustomStackLayout, // Web only!
636
- routes: [
637
- { path: "", type: 'screen', component: HomeScreen },
638
- { path: "about", type: 'screen', component: AboutScreen },
639
- ]
640
- };
641
- \`\`\`
642
-
643
- ### Tab Navigator with Layout
644
-
645
- \`\`\`tsx
646
- import { CustomTabLayout } from './layouts/CustomTabLayout';
647
-
648
- const router: NavigatorParam = {
649
- path: "/",
650
- type: 'navigator',
651
- layout: 'tab',
652
- layoutComponent: CustomTabLayout, // Web only!
653
- routes: [
654
- { path: "feed", type: 'screen', component: FeedScreen },
655
- { path: "profile", type: 'screen', component: ProfileScreen },
656
- ]
657
- };
658
- \`\`\`
659
-
660
- ## Creating Custom Layouts
661
-
662
- ### Stack Layout Component
663
-
664
- \`\`\`tsx
665
- import { GeneralLayout } from '@idealyst/navigation';
666
- import type { StackLayoutProps } from '@idealyst/navigation';
667
-
668
- export const CustomStackLayout: React.FC<StackLayoutProps> = ({
669
- children,
670
- options,
671
- routes,
672
- currentPath
673
- }) => {
674
- return (
675
- <GeneralLayout
676
- header={{
677
- enabled: true,
678
- content: (
679
- <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
680
- <Text>{options?.headerTitle || 'My App'}</Text>
681
- {options?.headerRight}
682
- </View>
683
- )
684
- }}
685
- sidebar={{
686
- enabled: true,
687
- collapsible: true,
688
- content: (
689
- <NavigationMenu routes={routes} currentPath={currentPath} />
690
- )
691
- }}
692
- >
693
- {children}
694
- </GeneralLayout>
695
- );
696
- };
697
- \`\`\`
698
-
699
- ### Tab Layout Component
700
-
701
- \`\`\`tsx
702
- import type { TabLayoutProps } from '@idealyst/navigation';
703
-
704
- export const CustomTabLayout: React.FC<TabLayoutProps> = ({
705
- children,
706
- routes,
707
- currentPath
708
- }) => {
709
- const navigator = useNavigator();
710
-
711
- return (
712
- <View style={{ flex: 1 }}>
713
- {/* Custom tab bar */}
714
- <View style={{ flexDirection: 'row', borderBottom: '1px solid #ccc' }}>
715
- {routes.map(route => (
716
- <Pressable
717
- key={route.path}
718
- onPress={() => navigator.navigate({ path: route.fullPath, vars: {} })}
719
- style={{
720
- padding: 16,
721
- borderBottom: currentPath === route.fullPath ? '2px solid blue' : 'none'
722
- }}
723
- >
724
- <Text>{route.options?.tabBarLabel || route.path}</Text>
725
- </Pressable>
726
- ))}
727
- </View>
728
-
729
- {/* Content */}
730
- <View style={{ flex: 1 }}>
731
- {children}
732
- </View>
733
- </View>
734
- );
735
- };
736
- \`\`\`
737
-
738
- ## Layout Props Reference
739
-
740
- ### StackLayoutProps
741
-
742
- \`\`\`tsx
743
- type StackLayoutProps = {
744
- options?: NavigatorOptions; // Navigator options
745
- routes: RouteWithFullPath[]; // All routes with full paths
746
- currentPath: string; // Current active path
747
- children?: React.ReactNode; // Route content
748
- };
749
- \`\`\`
750
-
751
- ### TabLayoutProps
752
-
753
- \`\`\`tsx
754
- type TabLayoutProps = {
755
- options?: NavigatorOptions; // Navigator options
756
- routes: RouteWithFullPath<TabBarScreenOptions>[]; // Tab routes
757
- currentPath: string; // Current active path
758
- children?: React.ReactNode; // Route content
759
- };
760
- \`\`\`
761
-
762
- ## Real-World Examples
763
-
764
- ### Dashboard Layout
765
-
766
- \`\`\`tsx
767
- export const DashboardLayout: React.FC<StackLayoutProps> = ({
768
- children,
769
- routes,
770
- currentPath
771
- }) => {
772
- return (
773
- <GeneralLayout
774
- header={{
775
- enabled: true,
776
- height: 72,
777
- content: (
778
- <View style={{
779
- flexDirection: 'row',
780
- justifyContent: 'space-between',
781
- alignItems: 'center',
782
- padding: 16
783
- }}>
784
- <Text size="xl" weight="bold">Dashboard</Text>
785
- <View style={{ flexDirection: 'row', gap: 16 }}>
786
- <NotificationBell />
787
- <UserAvatar />
788
- </View>
789
- </View>
790
- )
791
- }}
792
- sidebar={{
793
- enabled: true,
794
- collapsible: true,
795
- position: 'left',
796
- expandedWidth: 260,
797
- content: <DashboardSidebar routes={routes} currentPath={currentPath} />
798
- }}
799
- >
800
- {children}
801
- </GeneralLayout>
802
- );
803
- };
804
- \`\`\`
805
-
806
- ### Admin Panel Layout
807
-
808
- \`\`\`tsx
809
- export const AdminLayout: React.FC<StackLayoutProps> = ({ children }) => {
810
- return (
811
- <GeneralLayout
812
- header={{
813
- enabled: true,
814
- content: <AdminHeader />,
815
- style: { backgroundColor: '#1a1a1a', color: '#fff' }
816
- }}
817
- sidebar={{
818
- enabled: true,
819
- collapsible: true,
820
- position: 'left',
821
- initiallyExpanded: true,
822
- content: <AdminNavigationMenu />,
823
- style: { backgroundColor: '#2a2a2a' }
824
- }}
825
- >
826
- <View style={{ padding: 24 }}>
827
- {children}
828
- </View>
829
- </GeneralLayout>
830
- );
831
- };
832
- \`\`\`
833
-
834
- ## Best Practices
835
-
836
- 1. **Use GeneralLayout as base** - Don't reinvent header/sidebar logic
837
- 2. **Keep layouts simple** - Complex logic belongs in screens
838
- 3. **Make responsive** - Consider different screen sizes
839
- 4. **Theme-aware** - Use theme colors and spacing
840
- 5. **Accessible** - Ensure keyboard and screen reader support
841
- 6. **Performance** - Memoize layout components when possible
842
- 7. **Consistent** - Use same layout for similar sections
843
-
844
- ## Platform Considerations
845
-
846
- - **Web only**: Layout components only apply to web
847
- - **Mobile**: Uses native navigation components
848
- - **Conditional rendering**: Check platform if needed
849
- - **Testing**: Test both platforms separately
850
- `,
851
- "idealyst://navigation/use-navigator": `# useNavigator Hook
852
-
853
- The \`useNavigator\` hook provides navigation functionality and route information within your components.
854
-
855
- ## Basic Usage
856
-
857
- \`\`\`tsx
858
- import { useNavigator } from '@idealyst/navigation';
859
-
860
- function MyComponent() {
861
- const navigator = useNavigator();
862
-
863
- return (
864
- <Button onPress={() => navigator.navigate({ path: '/profile', vars: {} })}>
865
- Go to Profile
866
- </Button>
867
- );
868
- }
869
- \`\`\`
870
-
871
- ## API Reference
872
-
873
- ### navigator.navigate()
874
-
875
- Navigate to a route:
876
-
877
- \`\`\`tsx
878
- navigator.navigate({
879
- path: string;
880
- vars?: Record<string, string>;
881
- replace?: boolean; // Replace history entry instead of push
882
- });
883
- \`\`\`
884
-
885
- Examples:
886
- \`\`\`tsx
887
- // Simple navigation
888
- navigator.navigate({ path: '/home' });
889
-
890
- // With path parameters
891
- navigator.navigate({
892
- path: '/user/:id',
893
- vars: { id: '123' }
894
- });
895
-
896
- // With query parameters
897
- navigator.navigate({
898
- path: '/search',
899
- vars: { q: 'react', category: 'tutorial' }
900
- });
901
-
902
- // Replace current history entry (no back navigation to current page)
903
- navigator.navigate({
904
- path: '/dashboard',
905
- replace: true
906
- });
907
- \`\`\`
908
-
909
- ### Replace vs Push Navigation
910
-
911
- By default, navigation pushes a new entry onto the history stack. Use \`replace: true\` when you want to replace the current entry instead:
912
-
913
- \`\`\`tsx
914
- // After login, replace login page in history
915
- navigator.navigate({ path: '/dashboard', replace: true });
916
-
917
- // Redirect without adding to history
918
- navigator.navigate({ path: '/new-location', replace: true });
919
- \`\`\`
920
-
921
- **Use cases for replace:**
922
- - Post-login redirects (user shouldn't go back to login)
923
- - After form submission redirects
924
- - URL canonicalization/normalization
925
- - Redirect from deprecated routes
926
-
927
- ## useParams Hook
928
-
929
- Access current route path parameters:
930
-
931
- \`\`\`tsx
932
- import { useParams } from '@idealyst/navigation';
933
-
934
- function UserScreen() {
935
- const params = useParams();
936
- const userId = params.id; // Path param from /user/:id
937
-
938
- return <Text>User ID: {userId}</Text>;
939
- }
940
- \`\`\`
941
-
942
- ## useNavigationState Hook
943
-
944
- Access navigation state passed via the \`state\` property:
945
-
946
- \`\`\`tsx
947
- import { useNavigationState } from '@idealyst/navigation';
948
-
949
- // When navigating:
950
- navigator.navigate({
951
- path: '/recording',
952
- state: { autostart: true, source: 'home' }
953
- });
954
-
955
- // In destination screen:
956
- function RecordingScreen() {
957
- const { autostart, source } = useNavigationState<{
958
- autostart?: boolean;
959
- source?: string;
960
- }>();
961
-
962
- // autostart = true, source = 'home'
963
- }
964
- \`\`\`
965
-
966
- ### Consuming State (Web)
967
-
968
- Remove state from URL after reading:
969
-
970
- \`\`\`tsx
971
- // URL: /recording?autostart=true
972
- const { autostart } = useNavigationState<{ autostart?: boolean }>({
973
- consume: ['autostart']
974
- });
975
- // autostart = true, URL becomes: /recording (param removed)
976
- \`\`\`
977
-
978
- ## useLocation (React Router)
979
-
980
- Get the current route path on web:
981
-
982
- \`\`\`tsx
983
- import { useLocation } from '@idealyst/navigation';
984
-
985
- function MyComponent() {
986
- const location = useLocation();
987
- console.log(location.pathname); // "/user/123"
988
- }
989
- \`\`\`
990
-
991
- ### navigator.canGoBack()
992
-
993
- Check if back navigation is available:
994
-
995
- \`\`\`tsx
996
- const navigator = useNavigator();
997
-
998
- if (navigator.canGoBack()) {
999
- // Show back button
1000
- }
1001
- \`\`\`
1002
-
1003
- **Platform behavior:**
1004
- - **Web**: Returns \`true\` if there's a valid parent route in the route hierarchy (e.g., \`/users/123\` can go back to \`/users\`)
1005
- - **Native**: Uses React Navigation's \`canGoBack()\` to check navigation stack
1006
-
1007
- ### navigator.goBack()
1008
-
1009
- Navigate back in the route hierarchy:
1010
-
1011
- \`\`\`tsx
1012
- <Button onPress={() => navigator.goBack()}>
1013
- Go Back
1014
- </Button>
1015
- \`\`\`
1016
-
1017
- **Platform behavior:**
1018
- - **Web**: Navigates to the parent route (e.g., \`/users/123/edit\` → \`/users/123\` → \`/users\` → \`/\`). Does NOT use browser history - navigates up the route tree.
1019
- - **Native**: Uses React Navigation's \`goBack()\` to pop the navigation stack
1020
-
1021
- **Important**: On web, this is NOT browser history back. It navigates to the parent path in the route hierarchy. Use this for "up" navigation within your app structure.
1022
-
1023
- ## Path Parameters
1024
-
1025
- ### Defining Parameters
1026
-
1027
- In route configuration:
1028
- \`\`\`tsx
1029
- {
1030
- path: "user/:id",
1031
- type: 'screen',
1032
- component: UserScreen
1033
- }
1034
- \`\`\`
1035
-
1036
- ### Accessing Parameters
1037
-
1038
- In the screen component:
1039
- \`\`\`tsx
1040
- import { useParams } from '@idealyst/navigation';
1041
-
1042
- function UserScreen() {
1043
- const params = useParams();
1044
- const userId = params.id;
1045
-
1046
- return <Text>User ID: {userId}</Text>;
1047
- }
1048
- \`\`\`
1049
-
1050
- ### Multiple Parameters
1051
-
1052
- \`\`\`tsx
1053
- import { useParams } from '@idealyst/navigation';
1054
-
1055
- // Route: "post/:postId/comment/:commentId"
1056
-
1057
- // Navigate:
1058
- navigator.navigate({
1059
- path: '/post/:postId/comment/:commentId',
1060
- vars: { postId: '42', commentId: '7' }
1061
- });
1062
-
1063
- // Access:
1064
- const params = useParams();
1065
- const postId = params.postId;
1066
- const commentId = params.commentId;
1067
- \`\`\`
1068
-
1069
- ## Query Parameters
1070
-
1071
- ### Passing Query Params
1072
-
1073
- \`\`\`tsx
1074
- navigator.navigate({
1075
- path: '/search',
1076
- vars: {
1077
- q: 'typescript',
1078
- category: 'tutorial',
1079
- sort: 'recent'
1080
- }
1081
- });
1082
-
1083
- // Results in: /search?q=typescript&category=tutorial&sort=recent
1084
- \`\`\`
1085
-
1086
- ### Reading Query Params
1087
-
1088
- \`\`\`tsx
1089
- import { useNavigationState } from '@idealyst/navigation';
1090
-
1091
- function SearchScreen() {
1092
- // Query params are accessed via useNavigationState on web
1093
- const { q, category, sort } = useNavigationState<{
1094
- q?: string;
1095
- category?: string;
1096
- sort?: string;
1097
- }>();
1098
-
1099
- // Use params...
1100
- }
1101
- \`\`\`
1102
-
1103
- ## Navigation Patterns
1104
-
1105
- ### Programmatic Navigation
1106
-
1107
- Navigate based on conditions:
1108
-
1109
- \`\`\`tsx
1110
- function LoginScreen() {
1111
- const navigator = useNavigator();
1112
-
1113
- const handleLogin = async () => {
1114
- const success = await login(credentials);
1115
-
1116
- if (success) {
1117
- navigator.navigate({ path: '/dashboard', vars: {} });
1118
- }
1119
- };
1120
-
1121
- return <Button onPress={handleLogin}>Login</Button>;
1122
- }
1123
- \`\`\`
1124
-
1125
- ### Navigation with Data
1126
-
1127
- Pass data through navigation:
1128
-
1129
- \`\`\`tsx
1130
- // List screen
1131
- <Button onPress={() => {
1132
- navigator.navigate({
1133
- path: '/product/:id',
1134
- vars: { id: product.id }
1135
- });
1136
- }}>
1137
- View Product
1138
- </Button>
1139
-
1140
- // Detail screen
1141
- function ProductScreen() {
1142
- const params = useParams();
1143
- const productId = params.id;
1144
-
1145
- const product = useProduct(productId);
1146
- // Render product...
1147
- }
1148
- \`\`\`
1149
-
1150
- ### Conditional Navigation
1151
-
1152
- Navigate based on user state:
1153
-
1154
- \`\`\`tsx
1155
- function ProtectedScreen() {
1156
- const navigator = useNavigator();
1157
- const { user } = useAuth();
1158
-
1159
- useEffect(() => {
1160
- if (!user) {
1161
- navigator.navigate({ path: '/login', vars: {} });
1162
- }
1163
- }, [user]);
1164
-
1165
- return user ? <Content /> : null;
1166
- }
1167
- \`\`\`
1168
-
1169
- ## Advanced Usage
1170
-
1171
- ### Navigation Guards
1172
-
1173
- \`\`\`tsx
1174
- import { useNavigator, useLocation } from '@idealyst/navigation';
1175
-
1176
- function useAuthGuard() {
1177
- const { navigate } = useNavigator();
1178
- const location = useLocation();
1179
- const { isAuthenticated } = useAuth();
1180
-
1181
- useEffect(() => {
1182
- if (!isAuthenticated && location.pathname !== '/login') {
1183
- navigate({ path: '/login' });
1184
- }
1185
- }, [isAuthenticated, location.pathname]);
1186
- }
1187
-
1188
- function App() {
1189
- useAuthGuard();
1190
- return <AppContent />;
1191
- }
1192
- \`\`\`
1193
-
1194
- ### Deep Linking
1195
-
1196
- Handle deep links:
1197
-
1198
- \`\`\`tsx
1199
- useEffect(() => {
1200
- const handleDeepLink = (url: string) => {
1201
- // Parse URL and navigate
1202
- const path = parseDeepLink(url);
1203
- navigator.navigate({ path, vars: {} });
1204
- };
1205
-
1206
- // Add deep link listener
1207
- const subscription = Linking.addEventListener('url', ({ url }) => {
1208
- handleDeepLink(url);
1209
- });
1210
-
1211
- return () => subscription.remove();
1212
- }, []);
1213
- \`\`\`
1214
-
1215
- ### Navigation History
1216
-
1217
- Track navigation history:
1218
-
1219
- \`\`\`tsx
1220
- import { useLocation } from '@idealyst/navigation';
1221
-
1222
- function useNavigationHistory() {
1223
- const location = useLocation();
1224
- const [history, setHistory] = useState<string[]>([]);
1225
-
1226
- useEffect(() => {
1227
- setHistory(prev => [...prev, location.pathname]);
1228
- }, [location.pathname]);
1229
-
1230
- return history;
1231
- }
1232
- \`\`\`
1233
-
1234
- ## TypeScript Support
1235
-
1236
- Type-safe navigation:
1237
-
1238
- \`\`\`tsx
1239
- type NavigateParams = {
1240
- path: string;
1241
- vars: Record<string, string>;
1242
- };
1243
-
1244
- // Strongly typed vars
1245
- type UserRouteVars = {
1246
- id: string;
1247
- tab?: 'posts' | 'comments' | 'likes';
1248
- };
1249
-
1250
- // Usage
1251
- navigator.navigate({
1252
- path: '/user/:id',
1253
- vars: { id: '123', tab: 'posts' } as UserRouteVars
1254
- });
1255
- \`\`\`
1256
-
1257
- ## Platform Differences
1258
-
1259
- ### React Native
1260
- - \`canGoBack()\` checks React Navigation stack
1261
- - \`goBack()\` uses native navigation stack
1262
- - Hardware back button supported
1263
- - Gesture-based navigation
1264
-
1265
- ### Web
1266
- - \`canGoBack()\` checks for valid parent route in hierarchy
1267
- - \`goBack()\` navigates to parent route (NOT browser history)
1268
- - Browser back/forward buttons still work for browser history
1269
- - URL updates automatically
1270
- - Bookmarkable URLs
1271
-
1272
- ## Best Practices
1273
-
1274
- 1. **Always provide vars** - Even if empty: \`{}\`
1275
- 2. **Type your routes** - Use TypeScript for safety
1276
- 3. **Handle errors** - Check if navigation succeeded
1277
- 4. **Avoid navigation in render** - Use effects or handlers
1278
- 5. **Clean up listeners** - Remove event listeners on unmount
1279
- 6. **Test navigation flow** - Verify all paths work
1280
- 7. **Use path params for IDs** - Not query params
1281
- 8. **Use query params for filters** - Not path params
1282
-
1283
- ## Common Patterns
1284
-
1285
- ### Back Navigation
1286
-
1287
- \`\`\`tsx
1288
- // Conditionally show back button
1289
- const { canGoBack, goBack } = useNavigator();
1290
-
1291
- {canGoBack() && (
1292
- <Button
1293
- icon="arrow-left"
1294
- onPress={goBack}
1295
- >
1296
- Back
1297
- </Button>
1298
- )}
1299
- \`\`\`
1300
-
1301
- ### Tab Navigation
1302
-
1303
- \`\`\`tsx
1304
- import { useNavigator, useLocation } from '@idealyst/navigation';
1305
-
1306
- const tabs = ['feed', 'search', 'profile'];
1307
-
1308
- function TabBar() {
1309
- const { navigate } = useNavigator();
1310
- const location = useLocation();
1311
-
1312
- return (
1313
- <View>
1314
- {tabs.map(tab => (
1315
- <Button
1316
- key={tab}
1317
- onPress={() => navigate({ path: \`/\${tab}\` })}
1318
- type={location.pathname === \`/\${tab}\` ? 'contained' : 'outlined'}
1319
- >
1320
- {tab}
1321
- </Button>
1322
- ))}
1323
- </View>
1324
- );
1325
- }
1326
- \`\`\`
1327
-
1328
- ### Modal Navigation
1329
-
1330
- \`\`\`tsx
1331
- <Button onPress={() => {
1332
- navigator.navigate({
1333
- path: '/modal/create-post',
1334
- vars: {}
1335
- });
1336
- }}>
1337
- Create Post
1338
- </Button>
1339
-
1340
- // In modal:
1341
- <Button onPress={() => navigator.goBack()}>
1342
- Close
1343
- </Button>
1344
- \`\`\`
1345
- `,
1346
- "idealyst://navigation/invalid-route-handling": `# Invalid Route Handling
1347
-
1348
- Handle 404 pages and invalid routes with customizable redirect logic and fallback components.
1349
-
1350
- ## Overview
1351
-
1352
- The navigation system provides two mechanisms for handling invalid routes:
1353
-
1354
- 1. **\`onInvalidRoute\`** - A handler function that can redirect to a different route
1355
- 2. **\`notFoundComponent\`** - A fallback component to render when no redirect is specified
1356
-
1357
- These can be configured at each navigator level and support bubbling up to parent navigators.
1358
-
1359
- ## Basic Setup
1360
-
1361
- ### Adding a 404 Page
1362
-
1363
- \`\`\`tsx
1364
- import { NavigatorParam, NotFoundComponentProps } from '@idealyst/navigation';
1365
-
1366
- // 404 Component receives path and params
1367
- const NotFoundPage = ({ path, params }: NotFoundComponentProps) => (
1368
- <Screen>
1369
- <View style={{ alignItems: 'center', padding: 24 }}>
1370
- <Icon name="alert-circle" size={64} color="red" />
1371
- <Text size="xl">Page Not Found</Text>
1372
- <Text color="secondary">The path "{path}" doesn't exist.</Text>
1373
- {params && Object.keys(params).length > 0 && (
1374
- <Text size="sm">Params: {JSON.stringify(params)}</Text>
1375
- )}
1376
- </View>
1377
- </Screen>
1378
- );
1379
-
1380
- const routes: NavigatorParam = {
1381
- path: "/",
1382
- type: 'navigator',
1383
- layout: 'stack',
1384
- notFoundComponent: NotFoundPage,
1385
- routes: [
1386
- { path: "", type: 'screen', component: HomeScreen },
1387
- { path: "about", type: 'screen', component: AboutScreen },
1388
- ]
1389
- };
1390
- \`\`\`
1391
-
1392
- ## NotFoundComponentProps
1393
-
1394
- The 404 component receives information about the attempted route:
1395
-
1396
- \`\`\`tsx
1397
- type NotFoundComponentProps = {
1398
- /** The full path that was attempted */
1399
- path: string;
1400
- /** Any route parameters that were parsed from the path */
1401
- params?: Record<string, string>;
1402
- };
1403
- \`\`\`
1404
-
1405
- Example usage:
1406
- \`\`\`tsx
1407
- const NotFoundPage = ({ path, params }: NotFoundComponentProps) => {
1408
- const { navigate } = useNavigator();
1409
-
1410
- return (
1411
- <Screen>
1412
- <View style={{ padding: 16, gap: 24 }}>
1413
- <Text size="xl">404 - Page Not Found</Text>
1414
- <Text>Attempted: {path}</Text>
1415
- {params?.id && <Text>User ID: {params.id}</Text>}
1416
- <Button onPress={() => navigate({ path: '/', replace: true })}>
1417
- Go Home
1418
- </Button>
1419
- </View>
1420
- </Screen>
1421
- );
1422
- };
1423
- \`\`\`
1424
-
1425
- ## Redirect Handler
1426
-
1427
- Use \`onInvalidRoute\` to redirect certain invalid paths:
1428
-
1429
- \`\`\`tsx
1430
- const routes: NavigatorParam = {
1431
- path: "/",
1432
- type: 'navigator',
1433
- layout: 'stack',
1434
- notFoundComponent: NotFoundPage,
1435
- onInvalidRoute: (invalidPath) => {
1436
- // Redirect old URLs to new locations
1437
- if (invalidPath.startsWith('/old-blog')) {
1438
- return { path: '/blog', replace: true };
1439
- }
1440
-
1441
- // Redirect legacy paths
1442
- if (invalidPath === '/legacy-dashboard') {
1443
- return { path: '/dashboard', replace: true };
1444
- }
1445
-
1446
- // Return undefined to show notFoundComponent
1447
- return undefined;
1448
- },
1449
- routes: [...]
1450
- };
1451
- \`\`\`
1452
-
1453
- ### Handler Return Values
1454
-
1455
- The \`onInvalidRoute\` handler can return:
1456
-
1457
- - **\`NavigateParams\`** - Redirect to a different route
1458
- - **\`undefined\`** - Show the \`notFoundComponent\` (or bubble up)
1459
-
1460
- \`\`\`tsx
1461
- type NavigateParams = {
1462
- path: string;
1463
- vars?: Record<string, string>;
1464
- replace?: boolean; // Recommended: true for redirects
1465
- };
1466
-
1467
- onInvalidRoute: (path: string) => NavigateParams | undefined
1468
- \`\`\`
1469
-
1470
- ## Scoped Handlers (Nested Navigators)
1471
-
1472
- Each navigator can have its own 404 handling:
1473
-
1474
- \`\`\`tsx
1475
- // Settings-specific 404 page
1476
- const SettingsNotFound = ({ path }: NotFoundComponentProps) => (
1477
- <Screen>
1478
- <View style={{ alignItems: 'center' }}>
1479
- <Icon name="cog-off" size={48} color="orange" />
1480
- <Text>Settings page not found: {path}</Text>
1481
- </View>
1482
- </Screen>
1483
- );
1484
-
1485
- // Nested settings navigator with its own handler
1486
- const SettingsNavigator: NavigatorParam = {
1487
- path: "settings",
1488
- type: 'navigator',
1489
- layout: 'stack',
1490
- notFoundComponent: SettingsNotFound,
1491
- onInvalidRoute: (path) => {
1492
- // Redirect deprecated settings paths
1493
- if (path.includes('legacy-setting')) {
1494
- return { path: '/settings/general', replace: true };
1495
- }
1496
- return undefined; // Show SettingsNotFound
1497
- },
1498
- routes: [
1499
- { path: "", type: 'screen', component: SettingsHome },
1500
- { path: "general", type: 'screen', component: GeneralSettings },
1501
- { path: "account", type: 'screen', component: AccountSettings },
1502
- ]
1503
- };
1504
-
1505
- // Root navigator with global 404
1506
- const AppRouter: NavigatorParam = {
1507
- path: "/",
1508
- type: 'navigator',
1509
- layout: 'stack',
1510
- notFoundComponent: GlobalNotFound, // Fallback for non-settings routes
1511
- routes: [
1512
- { path: "", type: 'screen', component: HomeScreen },
1513
- SettingsNavigator, // Has its own 404 handling
1514
- { path: "about", type: 'screen', component: AboutScreen },
1515
- ]
1516
- };
1517
- \`\`\`
1518
-
1519
- ## Handler Bubbling
1520
-
1521
- Invalid routes bubble up through the navigator hierarchy:
1522
-
1523
- \`\`\`
1524
- Invalid route detected: /settings/invalid-page
1525
-
1526
- Check /settings navigator's onInvalidRoute
1527
-
1528
- ┌─────────────────────────────────────────┐
1529
- │ Returns NavigateParams? │
1530
- │ YES → Redirect to that route │
1531
- │ NO (undefined) → Check notFoundComponent │
1532
- └─────────────────────────────────────────┘
1533
-
1534
- Has notFoundComponent?
1535
- YES → Render it with { path, params }
1536
- NO → Bubble up to parent navigator
1537
-
1538
- No parent handles it?
1539
- → console.warn("No handler for invalid route")
1540
- \`\`\`
1541
-
1542
- ## Platform Behavior
1543
-
1544
- ### Web
1545
-
1546
- - Invalid routes trigger the catch-all route at each navigator level
1547
- - The \`onInvalidRoute\` handler is called when the 404 route is rendered
1548
- - If handler returns \`NavigateParams\`, navigation uses \`replace: true\` by default
1549
- - URL stays at the invalid path when showing \`notFoundComponent\`
1550
-
1551
- ### Mobile (React Native)
1552
-
1553
- - Invalid routes trigger navigation to a hidden 404 screen
1554
- - The handler is called during the \`navigate()\` function
1555
- - If handler returns \`NavigateParams\`, redirects to that route
1556
- - If no handler/component, logs a warning
1557
-
1558
- ## Complete Example
1559
-
1560
- \`\`\`tsx
1561
- import { NavigatorParam, NotFoundComponentProps } from '@idealyst/navigation';
1562
- import { Screen, View, Text, Button, Icon, Card } from '@idealyst/components';
1563
-
1564
- // Global 404 - detailed error page
1565
- const GlobalNotFound = ({ path, params }: NotFoundComponentProps) => {
1566
- const { navigate } = useNavigator();
1567
-
1568
- return (
1569
- <Screen>
1570
- <View style={{ padding: 24, gap: 24, alignItems: 'center', flex: 1, justifyContent: 'center' }}>
1571
- <Icon name="alert-circle-outline" size={64} color="red" />
1572
- <Text size="xl" weight="bold">Page Not Found</Text>
1573
- <Text color="secondary">The page you're looking for doesn't exist.</Text>
1574
-
1575
- <Card style={{ marginTop: 16, padding: 16 }}>
1576
- <Text size="sm" weight="semibold">Attempted path:</Text>
1577
- <Text size="sm" color="secondary">{path}</Text>
1578
- {params && Object.keys(params).length > 0 && (
1579
- <>
1580
- <Text size="sm" weight="semibold" style={{ marginTop: 8 }}>Params:</Text>
1581
- <Text size="sm" color="secondary">{JSON.stringify(params)}</Text>
1582
- </>
1583
- )}
1584
- </Card>
1585
-
1586
- <Button style={{ marginTop: 24 }} onPress={() => navigate({ path: '/', replace: true })}>
1587
- Go Home
1588
- </Button>
1589
- </View>
1590
- </Screen>
1591
- );
1592
- };
1593
-
1594
- // Admin section 404 - simpler style
1595
- const AdminNotFound = ({ path }: NotFoundComponentProps) => {
1596
- const { navigate } = useNavigator();
1597
-
1598
- return (
1599
- <Screen>
1600
- <View padding={16} style={{ alignItems: 'center' }}>
1601
- <Icon name="shield-off" size={48} color="orange" />
1602
- <Text size="lg">Admin page not found</Text>
1603
- <Button
1604
- type="outlined"
1605
- size="sm"
1606
- onPress={() => navigate({ path: '/admin', replace: true })}
1607
- >
1608
- Back to Admin
1609
- </Button>
1610
- </View>
1611
- </Screen>
1612
- );
1613
- };
1614
-
1615
- // Admin navigator with redirect logic
1616
- const AdminNavigator: NavigatorParam = {
1617
- path: "admin",
1618
- type: 'navigator',
1619
- layout: 'stack',
1620
- notFoundComponent: AdminNotFound,
1621
- onInvalidRoute: (path) => {
1622
- // Redirect old admin paths
1623
- if (path.includes('old-users')) {
1624
- return { path: '/admin/users', replace: true };
1625
- }
1626
- if (path.includes('deprecated')) {
1627
- return { path: '/admin', replace: true };
1628
- }
1629
- return undefined; // Show AdminNotFound
1630
- },
1631
- routes: [
1632
- { path: "", type: 'screen', component: AdminDashboard },
1633
- { path: "users", type: 'screen', component: AdminUsers },
1634
- { path: "settings", type: 'screen', component: AdminSettings },
1635
- ]
1636
- };
1637
-
1638
- // Root app router
1639
- const AppRouter: NavigatorParam = {
1640
- path: "/",
1641
- type: 'navigator',
1642
- layout: 'drawer',
1643
- notFoundComponent: GlobalNotFound,
1644
- onInvalidRoute: (path) => {
1645
- // Global redirects
1646
- if (path === '/home') {
1647
- return { path: '/', replace: true };
1648
- }
1649
- return undefined;
1650
- },
1651
- routes: [
1652
- { path: "", type: 'screen', component: HomeScreen },
1653
- AdminNavigator,
1654
- { path: "about", type: 'screen', component: AboutScreen },
1655
- { path: "contact", type: 'screen', component: ContactScreen },
1656
- ]
1657
- };
1658
-
1659
- export default AppRouter;
1660
- \`\`\`
1661
-
1662
- ## Best Practices
1663
-
1664
- 1. **Always provide a root notFoundComponent** - Ensures all invalid routes are handled
1665
- 2. **Use scoped handlers for sections** - Different 404 styles for different app areas
1666
- 3. **Redirect deprecated URLs** - Use \`onInvalidRoute\` to maintain URL compatibility
1667
- 4. **Include helpful information** - Show the attempted path and suggest alternatives
1668
- 5. **Provide navigation options** - Add buttons to go home or back
1669
- 6. **Use \`replace: true\` for redirects** - Prevents invalid routes in browser history
1670
- 7. **Log unhandled routes** - Monitor for missing pages in production
1671
-
1672
- ## TypeScript Types
1673
-
1674
- \`\`\`tsx
1675
- import {
1676
- NavigatorParam,
1677
- NotFoundComponentProps,
1678
- NavigateParams
1679
- } from '@idealyst/navigation';
1680
-
1681
- // NotFoundComponentProps
1682
- type NotFoundComponentProps = {
1683
- path: string;
1684
- params?: Record<string, string>;
1685
- };
1686
-
1687
- // Handler signature
1688
- type InvalidRouteHandler = (invalidPath: string) => NavigateParams | undefined;
1689
-
1690
- // NavigateParams (used by handler return and navigate function)
1691
- type NavigateParams = {
1692
- path: string;
1693
- vars?: Record<string, string>;
1694
- replace?: boolean;
1695
- };
1696
- \`\`\`
1697
- `,
1698
- "idealyst://navigation/web-mobile-parity": `# Web/Mobile Navigation Parity
1699
-
1700
- Understanding how to achieve the same navigation experience on web that you get for free on mobile.
1701
-
1702
- ## The Core Problem
1703
-
1704
- On **React Native**, you get native navigation UI for free:
1705
- - Stack navigator gives you headers with back buttons
1706
- - Tab navigator gives you a bottom tab bar with icons
1707
- - Drawer navigator gives you a slide-out menu
1708
-
1709
- On **Web**, you get nothing but URL routing. No headers, no tab bars, no drawers. You must build these yourself using **layout components**.
1710
-
1711
- ## The Solution: Layout Components
1712
-
1713
- The \`layoutComponent\` prop on navigators is how you achieve parity. It wraps your route content and provides the navigation UI.
1714
-
1715
- \`\`\`tsx
1716
- {
1717
- path: "/",
1718
- type: 'navigator',
1719
- layout: 'stack',
1720
- layoutComponent: MyStackLayout, // <-- This is the key!
1721
- routes: [...]
1722
- }
1723
- \`\`\`
1724
-
1725
- ## What Layout Components Receive
1726
-
1727
- Every layout component receives these props:
1728
-
1729
- \`\`\`tsx
1730
- type LayoutProps = {
1731
- children: React.ReactNode; // The route content (renders via <Outlet />)
1732
- options?: NavigatorOptions; // headerTitle, headerLeft, headerRight, etc.
1733
- routes: RouteWithFullPath[]; // All child routes with their full paths
1734
- currentPath: string; // Currently active route path
1735
- };
1736
- \`\`\`
1737
-
1738
- **This gives you everything you need to build any navigation UI:**
1739
- - \`options\` - What to show in headers
1740
- - \`routes\` - What tabs/menu items to render
1741
- - \`currentPath\` - Which one is active
1742
- - \`children\` - Where to render the screen content
1743
-
1744
- ## Stack Navigator Parity
1745
-
1746
- ### What Native Gives You
1747
- - Header bar with title
1748
- - Back button (automatic)
1749
- - Right-side actions
1750
- - Smooth transitions
1751
-
1752
- ### Web Implementation
1753
-
1754
- \`\`\`tsx
1755
- import { Outlet } from 'react-router-dom';
1756
- import { View, Text, IconButton, Pressable } from '@idealyst/components';
1757
- import { useNavigator } from '@idealyst/navigation';
1758
- import type { StackLayoutProps } from '@idealyst/navigation';
1759
-
1760
- export function StackLayout({ options, currentPath }: StackLayoutProps) {
1761
- const { canGoBack, goBack } = useNavigator();
1762
-
1763
- return (
1764
- <View style={{ flex: 1 }}>
1765
- {/* Header - mimics native stack header */}
1766
- {options?.headerShown !== false && (
1767
- <View style={{
1768
- height: 56,
1769
- flexDirection: 'row',
1770
- alignItems: 'center',
1771
- paddingHorizontal: 16,
1772
- borderBottomWidth: 1,
1773
- borderBottomColor: '#e0e0e0',
1774
- backgroundColor: '#fff',
1775
- }}>
1776
- {/* Back button - like native */}
1777
- {options?.headerBackVisible !== false && canGoBack() && (
1778
- <IconButton
1779
- icon="arrow-left"
1780
- onPress={goBack}
1781
- style={{ marginRight: 8 }}
1782
- />
1783
- )}
1784
-
1785
- {/* Left slot */}
1786
- {options?.headerLeft && (
1787
- <View style={{ marginRight: 16 }}>
1788
- {typeof options.headerLeft === 'function'
1789
- ? options.headerLeft({})
1790
- : options.headerLeft}
1791
- </View>
1792
- )}
1793
-
1794
- {/* Title - centered or left-aligned */}
1795
- <View style={{ flex: 1 }}>
1796
- {typeof options?.headerTitle === 'string' ? (
1797
- <Text variant="title">{options.headerTitle}</Text>
1798
- ) : (
1799
- options?.headerTitle
1800
- )}
1801
- </View>
1802
-
1803
- {/* Right slot */}
1804
- {options?.headerRight && (
1805
- <View>
1806
- {typeof options.headerRight === 'function'
1807
- ? options.headerRight({})
1808
- : options.headerRight}
1809
- </View>
1810
- )}
1811
- </View>
1812
- )}
1813
-
1814
- {/* Content area */}
1815
- <View style={{ flex: 1 }}>
1816
- <Outlet />
1817
- </View>
1818
- </View>
1819
- );
1820
- }
1821
- \`\`\`
1822
-
1823
- ## Tab Navigator Parity
1824
-
1825
- ### What Native Gives You
1826
- - Bottom tab bar
1827
- - Icons for each tab
1828
- - Labels
1829
- - Badge counts
1830
- - Active state highlighting
1831
-
1832
- ### Web Implementation
1833
-
1834
- \`\`\`tsx
1835
- import { Outlet } from 'react-router-dom';
1836
- import { View, Text, Pressable, Icon, Badge } from '@idealyst/components';
1837
- import { useNavigator } from '@idealyst/navigation';
1838
- import type { TabLayoutProps } from '@idealyst/navigation';
1839
-
1840
- export function TabLayout({ routes, currentPath }: TabLayoutProps) {
1841
- const { navigate } = useNavigator();
1842
-
1843
- return (
1844
- <View style={{ flex: 1 }}>
1845
- {/* Content area */}
1846
- <View style={{ flex: 1 }}>
1847
- <Outlet />
1848
- </View>
1849
-
1850
- {/* Bottom tab bar - mimics native */}
1851
- <View style={{
1852
- height: 56,
1853
- flexDirection: 'row',
1854
- borderTopWidth: 1,
1855
- borderTopColor: '#e0e0e0',
1856
- backgroundColor: '#fff',
1857
- }}>
1858
- {routes.map((route) => {
1859
- const isActive = currentPath === route.fullPath;
1860
- const options = route.options;
1861
-
1862
- return (
1863
- <Pressable
1864
- key={route.fullPath}
1865
- onPress={() => navigate({ path: route.fullPath })}
1866
- style={{
1867
- flex: 1,
1868
- alignItems: 'center',
1869
- justifyContent: 'center',
1870
- paddingVertical: 8,
1871
- }}
1872
- >
1873
- {/* Icon with optional badge */}
1874
- <View style={{ position: 'relative' }}>
1875
- {options?.tabBarIcon?.({
1876
- focused: isActive,
1877
- color: isActive ? '#007AFF' : '#8E8E93',
1878
- size: 24,
1879
- })}
1880
- {options?.tabBarBadge && (
1881
- <Badge
1882
- count={options.tabBarBadge}
1883
- style={{ position: 'absolute', top: -4, right: -8 }}
1884
- />
1885
- )}
1886
- </View>
1887
-
1888
- {/* Label */}
1889
- {options?.tabBarLabel && (
1890
- <Text
1891
- size="xs"
1892
- style={{
1893
- marginTop: 4,
1894
- color: isActive ? '#007AFF' : '#8E8E93',
1895
- }}
1896
- >
1897
- {options.tabBarLabel}
1898
- </Text>
1899
- )}
1900
- </Pressable>
1901
- );
1902
- })}
1903
- </View>
1904
- </View>
1905
- );
1906
- }
1907
- \`\`\`
1908
-
1909
- ## Drawer Navigator Parity
1910
-
1911
- ### What Native Gives You
1912
- - Slide-out drawer from edge
1913
- - Overlay when open
1914
- - Gesture to open/close
1915
- - Menu items
1916
-
1917
- ### Web Implementation
1918
-
1919
- On web, drawers are typically persistent sidebars. Here's how to build both:
1920
-
1921
- \`\`\`tsx
1922
- import { Outlet } from 'react-router-dom';
1923
- import { View, Text, Pressable, Icon } from '@idealyst/components';
1924
- import { useNavigator } from '@idealyst/navigation';
1925
- import type { StackLayoutProps } from '@idealyst/navigation';
1926
-
1927
- export function DrawerLayout({ routes, currentPath, options }: StackLayoutProps) {
1928
- const { navigate } = useNavigator();
1929
- const [isCollapsed, setIsCollapsed] = useState(false);
1930
-
1931
- return (
1932
- <View style={{ flex: 1, flexDirection: 'row' }}>
1933
- {/* Sidebar - always visible on web */}
1934
- <View style={{
1935
- width: isCollapsed ? 64 : 240,
1936
- borderRightWidth: 1,
1937
- borderRightColor: '#e0e0e0',
1938
- backgroundColor: '#f8f8f8',
1939
- transition: 'width 0.2s',
1940
- }}>
1941
- {/* Logo/Header */}
1942
- <View style={{ height: 56, justifyContent: 'center', paddingHorizontal: 16 }}>
1943
- {!isCollapsed && <Text variant="title">My App</Text>}
1944
- </View>
1945
-
1946
- {/* Menu Items */}
1947
- {routes.map((route) => {
1948
- const isActive = currentPath.startsWith(route.fullPath);
1949
- return (
1950
- <Pressable
1951
- key={route.fullPath}
1952
- onPress={() => navigate({ path: route.fullPath })}
1953
- style={{
1954
- flexDirection: 'row',
1955
- alignItems: 'center',
1956
- padding: 12,
1957
- backgroundColor: isActive ? 'rgba(0,0,0,0.08)' : 'transparent',
1958
- }}
1959
- >
1960
- <Icon
1961
- name={route.options?.icon || 'circle'}
1962
- size={24}
1963
- color={isActive ? '#007AFF' : '#666'}
1964
- />
1965
- {!isCollapsed && (
1966
- <Text style={{ marginLeft: 12, color: isActive ? '#007AFF' : '#333' }}>
1967
- {route.options?.title || route.path}
1968
- </Text>
1969
- )}
1970
- </Pressable>
1971
- );
1972
- })}
1973
-
1974
- {/* Collapse toggle */}
1975
- <Pressable
1976
- onPress={() => setIsCollapsed(!isCollapsed)}
1977
- style={{ padding: 12, marginTop: 'auto' }}
1978
- >
1979
- <Icon name={isCollapsed ? 'chevron-right' : 'chevron-left'} size={24} />
1980
- </Pressable>
1981
- </View>
1982
-
1983
- {/* Content */}
1984
- <View style={{ flex: 1 }}>
1985
- <Outlet />
1986
- </View>
1987
- </View>
1988
- );
1989
- }
1990
- \`\`\`
1991
-
1992
- ## Using GeneralLayout Helper
1993
-
1994
- The \`GeneralLayout\` component simplifies building layouts:
1995
-
1996
- \`\`\`tsx
1997
- import { GeneralLayout } from '@idealyst/navigation';
1998
-
1999
- export function AppLayout({ options, routes, currentPath, children }: StackLayoutProps) {
2000
- return (
2001
- <GeneralLayout
2002
- header={{
2003
- enabled: true,
2004
- height: 56,
2005
- content: (
2006
- <View style={{ flexDirection: 'row', alignItems: 'center', flex: 1 }}>
2007
- <Text variant="title">{options?.headerTitle || 'App'}</Text>
2008
- <View style={{ marginLeft: 'auto' }}>{options?.headerRight}</View>
2009
- </View>
2010
- ),
2011
- }}
2012
- sidebar={{
2013
- enabled: true,
2014
- collapsible: true,
2015
- expandedWidth: 240,
2016
- collapsedWidth: 64,
2017
- content: <SidebarMenu routes={routes} currentPath={currentPath} />,
2018
- }}
2019
- >
2020
- {children}
2021
- </GeneralLayout>
2022
- );
2023
- }
2024
- \`\`\`
2025
-
2026
- ## Putting It All Together
2027
-
2028
- Here's a complete router setup with web layouts:
2029
-
2030
- \`\`\`tsx
2031
- import { NavigatorParam } from '@idealyst/navigation';
2032
- import { StackLayout } from './layouts/StackLayout';
2033
- import { TabLayout } from './layouts/TabLayout';
2034
-
2035
- const appRouter: NavigatorParam = {
2036
- path: "/",
2037
- type: 'navigator',
2038
- layout: 'stack',
2039
- layoutComponent: StackLayout, // Web header
2040
- options: {
2041
- headerTitle: "My App",
2042
- headerRight: <UserMenu />,
2043
- },
2044
- routes: [
2045
- {
2046
- path: "main",
2047
- type: 'navigator',
2048
- layout: 'tab',
2049
- layoutComponent: TabLayout, // Web tab bar
2050
- routes: [
2051
- {
2052
- path: "home",
2053
- type: 'screen',
2054
- component: HomeScreen,
2055
- options: {
2056
- tabBarLabel: "Home",
2057
- tabBarIcon: ({ color }) => <Icon name="home" color={color} />,
2058
- },
2059
- },
2060
- {
2061
- path: "search",
2062
- type: 'screen',
2063
- component: SearchScreen,
2064
- options: {
2065
- tabBarLabel: "Search",
2066
- tabBarIcon: ({ color }) => <Icon name="magnify" color={color} />,
2067
- },
2068
- },
2069
- ],
2070
- },
2071
- {
2072
- path: "settings",
2073
- type: 'screen',
2074
- component: SettingsScreen,
2075
- },
2076
- ],
2077
- };
2078
- \`\`\`
2079
-
2080
- ## Key Insights
2081
-
2082
- 1. **layoutComponent is web-only** - Native ignores it and uses native navigators
2083
- 2. **Same route config, different UI** - Your routes stay the same, layouts differ
2084
- 3. **Options are your data source** - headerTitle, tabBarIcon, etc. drive your layout
2085
- 4. **routes array is navigation menu** - Use it to build sidebars, tab bars, menus
2086
- 5. **currentPath enables active states** - Compare to highlight current item
2087
- 6. **Outlet renders children** - From react-router-dom, this is where screen content goes
2088
-
2089
- ## Common Patterns
2090
-
2091
- ### Responsive Layout
2092
- \`\`\`tsx
2093
- function ResponsiveLayout(props: StackLayoutProps) {
2094
- const { width } = useWindowDimensions();
2095
- const isMobile = width < 768;
2096
-
2097
- // Tabs on mobile, drawer on desktop
2098
- return isMobile
2099
- ? <TabLayout {...props} />
2100
- : <DrawerLayout {...props} />;
2101
- }
2102
- \`\`\`
2103
-
2104
- ### Nested Headers
2105
- \`\`\`tsx
2106
- // Parent navigator has app header
2107
- // Child navigator has section header
2108
- {
2109
- path: "/",
2110
- layoutComponent: AppHeaderLayout,
2111
- routes: [{
2112
- path: "admin",
2113
- layoutComponent: AdminSectionHeader, // Adds another header
2114
- routes: [...]
2115
- }]
2116
- }
2117
- \`\`\`
2118
-
2119
- ### Hiding Navigation
2120
- \`\`\`tsx
2121
- function ConditionalLayout(props: StackLayoutProps) {
2122
- // Hide navigation on certain routes
2123
- if (props.currentPath.includes('/fullscreen')) {
2124
- return <Outlet />; // No chrome
2125
- }
2126
- return <FullLayout {...props} />;
2127
- }
2128
- \`\`\`
2129
-
2130
- ## Summary
2131
-
2132
- | Native Gets | Web Needs |
2133
- |-------------|-----------|
2134
- | Stack header | \`layoutComponent\` with header UI |
2135
- | Tab bar | \`layoutComponent\` with tab buttons |
2136
- | Drawer | \`layoutComponent\` with sidebar |
2137
- | Back button | \`canGoBack()\` + \`goBack()\` |
2138
- | Active states | Compare \`currentPath\` to \`route.fullPath\` |
2139
- | Screen options | Access via \`options\` and \`route.options\` |
2140
-
2141
- The key to web/mobile parity is understanding that **layout components give web everything native navigators provide automatically**.
2142
- `,
2143
- };
2144
- //# sourceMappingURL=navigation-guides.js.map