@idealyst/navigation 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/navigation",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "Cross-platform navigation library for React and React Native",
5
5
  "readme": "README.md",
6
6
  "main": "src/index.ts",
@@ -43,8 +43,8 @@
43
43
  "publish:npm": "npm publish"
44
44
  },
45
45
  "peerDependencies": {
46
- "@idealyst/components": "^1.1.5",
47
- "@idealyst/theme": "^1.1.5",
46
+ "@idealyst/components": "^1.1.7",
47
+ "@idealyst/theme": "^1.1.7",
48
48
  "@react-navigation/bottom-tabs": ">=7.0.0",
49
49
  "@react-navigation/drawer": ">=7.0.0",
50
50
  "@react-navigation/native": ">=7.0.0",
@@ -60,10 +60,10 @@
60
60
  "react-router-dom": ">=6.0.0"
61
61
  },
62
62
  "devDependencies": {
63
- "@idealyst/components": "^1.1.5",
63
+ "@idealyst/components": "^1.1.7",
64
64
  "@idealyst/datagrid": "^1.0.93",
65
65
  "@idealyst/datepicker": "^1.0.93",
66
- "@idealyst/theme": "^1.1.5",
66
+ "@idealyst/theme": "^1.1.7",
67
67
  "@types/react": "^19.1.8",
68
68
  "@types/react-dom": "^19.1.6",
69
69
  "react": "^19.1.0",
@@ -89,6 +89,29 @@ function hasNotFoundComponent(route: NavigatorParam): boolean {
89
89
  return false;
90
90
  }
91
91
 
92
+ /**
93
+ * Normalize a path and substitute variables
94
+ */
95
+ function normalizePath(path: string, vars?: Record<string, string>): string {
96
+ let normalizedPath = path;
97
+
98
+ // Convert empty string to '/'
99
+ if (normalizedPath === '' || normalizedPath === '/') {
100
+ normalizedPath = '/';
101
+ } else if (!normalizedPath.startsWith('/')) {
102
+ normalizedPath = `/${normalizedPath}`;
103
+ }
104
+
105
+ // Substitute variables in the path if provided
106
+ if (vars) {
107
+ Object.entries(vars).forEach(([key, value]) => {
108
+ normalizedPath = normalizedPath.replace(`:${key}`, value);
109
+ });
110
+ }
111
+
112
+ return normalizedPath;
113
+ }
114
+
92
115
  // Utility function to parse path with parameters and find matching route
93
116
  const parseParameterizedPath = (path: string, rootRoute: any): { routeName: string, params: Record<string, string> } | null => {
94
117
  // Handle absolute paths like /event/123
@@ -166,14 +189,17 @@ const UnwrappedNavigatorProvider = ({ route }: NavigatorProviderProps) => {
166
189
  return;
167
190
  }
168
191
 
192
+ // Normalize path and substitute variables (e.g., /visit/:id with vars { id: "123" } becomes /visit/123)
193
+ const normalizedPath = normalizePath(params.path, params.vars);
194
+
169
195
  // Parse parameterized path for mobile
170
- const parsed = parseParameterizedPath(params.path, route);
196
+ const parsed = parseParameterizedPath(normalizedPath, route);
171
197
 
172
198
  if (!parsed) {
173
199
  // Invalid route - try to find a handler
174
- const handler = findInvalidRouteHandler(route, params.path);
200
+ const handler = findInvalidRouteHandler(route, normalizedPath);
175
201
  if (handler) {
176
- const redirectParams = handler(params.path);
202
+ const redirectParams = handler(normalizedPath);
177
203
  if (redirectParams) {
178
204
  // Handler returned NavigateParams - redirect
179
205
  return navigate(
@@ -187,14 +213,13 @@ const UnwrappedNavigatorProvider = ({ route }: NavigatorProviderProps) => {
187
213
  // Navigate to 404 screen if configured
188
214
  if (route.notFoundComponent) {
189
215
  navigation.navigate(NOT_FOUND_SCREEN_NAME as never, {
190
- path: params.path,
191
- params: params.vars
216
+ path: normalizedPath,
192
217
  } as never);
193
218
  return;
194
219
  }
195
220
 
196
221
  // No handler and no 404 screen - log warning
197
- console.warn(`Navigation: Invalid route "${params.path}" and no handler or notFoundComponent configured.`);
222
+ console.warn(`Navigation: Invalid route "${normalizedPath}" and no handler or notFoundComponent configured.`);
198
223
  return;
199
224
  }
200
225
 
@@ -245,7 +270,7 @@ const UnwrappedNavigatorProvider = ({ route }: NavigatorProviderProps) => {
245
270
 
246
271
  const NavigatorProvider = ({ route }: NavigatorProviderProps) => {
247
272
  const {rt} = useUnistyles()
248
-
273
+
249
274
  const isDarkMode = rt.themeName === 'dark';
250
275
 
251
276
  return (
@@ -264,14 +289,17 @@ const DrawerNavigatorProvider = ({ navigation, route, children }: { navigation:
264
289
  return;
265
290
  }
266
291
 
292
+ // Normalize path and substitute variables (e.g., /visit/:id with vars { id: "123" } becomes /visit/123)
293
+ const normalizedPath = normalizePath(params.path, params.vars);
294
+
267
295
  // Parse parameterized path for mobile
268
- const parsed = parseParameterizedPath(params.path, route);
296
+ const parsed = parseParameterizedPath(normalizedPath, route);
269
297
 
270
298
  if (!parsed) {
271
299
  // Invalid route - try to find a handler
272
- const handler = findInvalidRouteHandler(route, params.path);
300
+ const handler = findInvalidRouteHandler(route, normalizedPath);
273
301
  if (handler) {
274
- const redirectParams = handler(params.path);
302
+ const redirectParams = handler(normalizedPath);
275
303
  if (redirectParams) {
276
304
  // Handler returned NavigateParams - redirect
277
305
  return navigate(
@@ -285,14 +313,13 @@ const DrawerNavigatorProvider = ({ navigation, route, children }: { navigation:
285
313
  // Navigate to 404 screen if configured
286
314
  if (route.notFoundComponent) {
287
315
  navigation.navigate(NOT_FOUND_SCREEN_NAME as never, {
288
- path: params.path,
289
- params: params.vars
316
+ path: normalizedPath,
290
317
  } as never);
291
318
  return;
292
319
  }
293
320
 
294
321
  // No handler and no 404 screen - log warning
295
- console.warn(`Navigation: Invalid route "${params.path}" and no handler or notFoundComponent configured.`);
322
+ console.warn(`Navigation: Invalid route "${normalizedPath}" and no handler or notFoundComponent configured.`);
296
323
  return;
297
324
  }
298
325
 
@@ -213,24 +213,64 @@ const buildScreen = (params: RouteParam, Navigator: TypedNavigator, parentPath =
213
213
  component = buildNavigator(params, fullPath);
214
214
  }
215
215
 
216
- // Build screen options, adding fullScreen presentation if specified
217
- let screenOptions = params.options;
218
- if (params.type === 'screen' && params.options?.fullScreen) {
219
- screenOptions = {
220
- ...params.options,
221
- // Use fullScreenModal presentation to bypass parent navigator chrome
222
- presentation: 'fullScreenModal',
223
- // Hide header for true fullScreen experience
224
- headerShown: params.options.headerShown ?? false,
225
- } as ScreenOptions;
226
- }
216
+ // Build screen options
217
+ // React Navigation expects headerLeft/headerRight to be functions returning elements
218
+ const buildScreenOptions = (navProps: any) => {
219
+ let options = params.options || {};
220
+
221
+ // Handle fullScreen presentation
222
+ if (params.type === 'screen' && options?.fullScreen) {
223
+ options = {
224
+ ...options,
225
+ presentation: 'fullScreenModal',
226
+ headerShown: options.headerShown ?? false,
227
+ };
228
+ }
229
+
230
+ // Wrap headerLeft if it's a component
231
+ if (options.headerLeft) {
232
+ const HeaderLeftContent = options.headerLeft as React.ComponentType<any>;
233
+ options = {
234
+ ...options,
235
+ headerLeft: () => (
236
+ <HeaderWrapper
237
+ content={HeaderLeftContent}
238
+ route={params as NavigatorParam}
239
+ navigation={navProps.navigation}
240
+ />
241
+ ),
242
+ };
243
+ }
244
+
245
+ // Wrap headerRight if it's a component
246
+ if (options.headerRight) {
247
+ const HeaderRightContent = options.headerRight as React.ComponentType<any>;
248
+ options = {
249
+ ...options,
250
+ headerRight: () => (
251
+ <HeaderWrapper
252
+ content={HeaderRightContent}
253
+ route={params as NavigatorParam}
254
+ navigation={navProps.navigation}
255
+ />
256
+ ),
257
+ };
258
+ }
259
+
260
+ return options;
261
+ };
262
+
263
+ // Use function form of options to access navigation props for header wrappers
264
+ const screenOptions = (params.options?.headerLeft || params.options?.headerRight)
265
+ ? buildScreenOptions
266
+ : params.options;
227
267
 
228
268
  return (
229
269
  <Navigator.Screen
230
270
  key={`${fullPath}-${index}`}
231
271
  name={fullPath}
232
272
  component={component}
233
- options={screenOptions}
273
+ options={screenOptions as any}
234
274
  />
235
275
  )
236
276
  }