@idealyst/navigation 1.1.6 → 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.
|
|
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.
|
|
47
|
-
"@idealyst/theme": "^1.1.
|
|
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.
|
|
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.
|
|
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(
|
|
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,
|
|
200
|
+
const handler = findInvalidRouteHandler(route, normalizedPath);
|
|
175
201
|
if (handler) {
|
|
176
|
-
const redirectParams = handler(
|
|
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:
|
|
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 "${
|
|
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(
|
|
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,
|
|
300
|
+
const handler = findInvalidRouteHandler(route, normalizedPath);
|
|
273
301
|
if (handler) {
|
|
274
|
-
const redirectParams = handler(
|
|
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:
|
|
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 "${
|
|
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
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
}
|