@idealyst/navigation 1.0.78 → 1.0.79
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 +7 -5
- package/src/context/NavigatorContext.native.tsx +78 -10
- package/src/context/NavigatorContext.web.tsx +20 -9
- package/src/context/types.ts +8 -0
- package/src/index.native.ts +4 -0
- package/src/index.ts +1 -5
- package/src/index.web.ts +5 -0
- package/src/layouts/DefaultStackLayout.tsx +2 -2
- package/src/layouts/DefaultTabLayout.tsx +2 -2
- package/src/routing/index.native.tsx +5 -1
- package/src/routing/index.web.tsx +3 -1
- package/src/routing/router.web.tsx +56 -201
- package/src/routing/types.ts +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/navigation",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.79",
|
|
4
4
|
"description": "Cross-platform navigation library for React and React Native",
|
|
5
5
|
"readme": "README.md",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -18,8 +18,10 @@
|
|
|
18
18
|
},
|
|
19
19
|
"exports": {
|
|
20
20
|
".": {
|
|
21
|
-
"
|
|
22
|
-
"
|
|
21
|
+
"react-native": "./src/index.native.ts",
|
|
22
|
+
"default": "./src/index.web.ts",
|
|
23
|
+
"import": "./src/index.web.ts",
|
|
24
|
+
"require": "./src/index.web.ts",
|
|
23
25
|
"types": "./src/index.ts"
|
|
24
26
|
},
|
|
25
27
|
"./examples": {
|
|
@@ -38,8 +40,8 @@
|
|
|
38
40
|
"publish:npm": "npm publish"
|
|
39
41
|
},
|
|
40
42
|
"peerDependencies": {
|
|
41
|
-
"@idealyst/components": "^1.0.
|
|
42
|
-
"@idealyst/theme": "^1.0.
|
|
43
|
+
"@idealyst/components": "^1.0.79",
|
|
44
|
+
"@idealyst/theme": "^1.0.79",
|
|
43
45
|
"@react-navigation/bottom-tabs": "^7.0.0",
|
|
44
46
|
"@react-navigation/drawer": "^7.0.0",
|
|
45
47
|
"@react-navigation/native": "^7.0.0",
|
|
@@ -1,33 +1,101 @@
|
|
|
1
1
|
import React, { createContext, memo, useContext, useMemo } from 'react';
|
|
2
|
-
import { NavigateParams, NavigatorProviderProps } from './types';
|
|
3
|
-
import { useNavigation, useNavigationState, DarkTheme, DefaultTheme, NavigationContainer } from '@react-navigation/native';
|
|
2
|
+
import { NavigateParams, NavigatorProviderProps, NavigatorContextValue } from './types';
|
|
3
|
+
import { useNavigation, useNavigationState, DarkTheme, DefaultTheme, NavigationContainer, useRoute } from '@react-navigation/native';
|
|
4
4
|
import { buildNavigator } from '../routing';
|
|
5
5
|
import { useUnistyles } from 'react-native-unistyles';
|
|
6
6
|
|
|
7
|
-
const NavigatorContext = createContext<{
|
|
8
|
-
navigate: (params: NavigateParams) => void;
|
|
9
|
-
}>({
|
|
7
|
+
const NavigatorContext = createContext<NavigatorContextValue>({
|
|
10
8
|
navigate: () => {},
|
|
9
|
+
params: {},
|
|
11
10
|
});
|
|
12
11
|
|
|
12
|
+
// Utility function to parse path with parameters and find matching route
|
|
13
|
+
const parseParameterizedPath = (path: string, routes: any[]): { routeName: string, params: Record<string, string> } | null => {
|
|
14
|
+
// Handle absolute paths like /event/123
|
|
15
|
+
if (path.startsWith('/')) {
|
|
16
|
+
const pathSegments = path.split('/').filter(Boolean);
|
|
17
|
+
|
|
18
|
+
// Find matching route by checking patterns like /event/:id
|
|
19
|
+
const findMatchingRoute = (routeList: any[], segments: string[], startIndex = 0): any => {
|
|
20
|
+
for (const route of routeList) {
|
|
21
|
+
const routeSegments = route.path.split('/').filter(Boolean);
|
|
22
|
+
|
|
23
|
+
if (routeSegments.length === segments.length - startIndex) {
|
|
24
|
+
let isMatch = true;
|
|
25
|
+
const extractedParams: Record<string, string> = {};
|
|
26
|
+
|
|
27
|
+
for (let i = 0; i < routeSegments.length; i++) {
|
|
28
|
+
const routeSegment = routeSegments[i];
|
|
29
|
+
const pathSegment = segments[startIndex + i];
|
|
30
|
+
|
|
31
|
+
if (routeSegment.startsWith(':')) {
|
|
32
|
+
// Parameter segment - extract value
|
|
33
|
+
const paramName = routeSegment.slice(1);
|
|
34
|
+
extractedParams[paramName] = pathSegment;
|
|
35
|
+
} else if (routeSegment !== pathSegment) {
|
|
36
|
+
// Literal segment must match exactly
|
|
37
|
+
isMatch = false;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (isMatch) {
|
|
43
|
+
return { route, params: extractedParams };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check nested routes
|
|
48
|
+
if (route.routes) {
|
|
49
|
+
const nestedMatch = findMatchingRoute(route.routes, segments, startIndex + route.path.split('/').filter(Boolean).length);
|
|
50
|
+
if (nestedMatch) {
|
|
51
|
+
return nestedMatch;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const match = findMatchingRoute([route], pathSegments);
|
|
59
|
+
if (match) {
|
|
60
|
+
return {
|
|
61
|
+
routeName: match.route.path,
|
|
62
|
+
params: match.params
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return null;
|
|
68
|
+
};
|
|
69
|
+
|
|
13
70
|
const UnwrappedNavigatorProvider = ({ route }: NavigatorProviderProps) => {
|
|
14
71
|
|
|
15
72
|
const navigation = useNavigation();
|
|
73
|
+
const currentRoute = useRoute();
|
|
16
74
|
|
|
17
75
|
const navigate = (params: NavigateParams) => {
|
|
18
|
-
|
|
19
|
-
|
|
76
|
+
|
|
77
|
+
// Parse parameterized path for mobile
|
|
78
|
+
const parsed = parseParameterizedPath(params.path, [route]);
|
|
79
|
+
|
|
80
|
+
if (parsed) {
|
|
81
|
+
// Navigate to the pattern route with extracted parameters
|
|
82
|
+
navigation.navigate(parsed.routeName as never, parsed.params as never);
|
|
83
|
+
} else {
|
|
84
|
+
// Fallback to direct navigation
|
|
85
|
+
navigation.navigate(params.path as never, params.vars as never);
|
|
86
|
+
}
|
|
20
87
|
};
|
|
21
88
|
|
|
22
89
|
const RouteComponent = useMemo(() => {
|
|
23
90
|
// Memoize the navigator to prevent unnecessary re-renders
|
|
24
91
|
return memo(buildNavigator(route));
|
|
25
92
|
}, [route]);
|
|
26
|
-
|
|
27
|
-
console.log('UnwrappedNavigatorProvider render', RouteComponent);
|
|
28
93
|
|
|
29
94
|
return (
|
|
30
|
-
<NavigatorContext.Provider value={{
|
|
95
|
+
<NavigatorContext.Provider value={{
|
|
96
|
+
navigate,
|
|
97
|
+
params: currentRoute.params || {}
|
|
98
|
+
}}>
|
|
31
99
|
<RouteComponent />
|
|
32
100
|
</NavigatorContext.Provider>
|
|
33
101
|
)
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import React, { createContext, memo, useContext, useMemo } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useNavigate, useParams } from 'react-router';
|
|
3
|
+
import { NavigateParams, NavigatorProviderProps, NavigatorContextValue } from './types';
|
|
3
4
|
import { buildNavigator } from '../routing';
|
|
4
5
|
|
|
5
|
-
const NavigatorContext = createContext<{
|
|
6
|
-
navigate: (params: NavigateParams) => void;
|
|
7
|
-
}>({
|
|
6
|
+
const NavigatorContext = createContext<NavigatorContextValue>({
|
|
8
7
|
navigate: () => {},
|
|
8
|
+
params: {},
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
export const NavigatorProvider = ({
|
|
12
12
|
route,
|
|
13
13
|
}: NavigatorProviderProps) => {
|
|
14
|
+
const reactRouterNavigate = useNavigate()
|
|
15
|
+
const params = useParams()
|
|
16
|
+
|
|
14
17
|
const navigateFunction = (params: NavigateParams) => {
|
|
15
18
|
if (params.path) {
|
|
16
19
|
// Normalize path - convert empty string to '/'
|
|
@@ -21,10 +24,15 @@ export const NavigatorProvider = ({
|
|
|
21
24
|
path = `/${path}`
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
// Substitute variables in the path if provided
|
|
28
|
+
if (params.vars) {
|
|
29
|
+
Object.entries(params.vars).forEach(([key, value]) => {
|
|
30
|
+
path = path.replace(`:${key}`, value);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Use React Router's navigate function
|
|
35
|
+
reactRouterNavigate(path);
|
|
28
36
|
}
|
|
29
37
|
};
|
|
30
38
|
|
|
@@ -34,7 +42,10 @@ export const NavigatorProvider = ({
|
|
|
34
42
|
}, [route]);
|
|
35
43
|
|
|
36
44
|
return (
|
|
37
|
-
<NavigatorContext.Provider value={{
|
|
45
|
+
<NavigatorContext.Provider value={{
|
|
46
|
+
navigate: navigateFunction,
|
|
47
|
+
params: params || {}
|
|
48
|
+
}}>
|
|
38
49
|
<RouteComponent />
|
|
39
50
|
</NavigatorContext.Provider>
|
|
40
51
|
);
|
package/src/context/types.ts
CHANGED
|
@@ -10,4 +10,12 @@ export type NavigateParams = {
|
|
|
10
10
|
|
|
11
11
|
export type NavigatorProviderProps = {
|
|
12
12
|
route: NavigatorParam;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Context value that includes navigation function and current route parameters
|
|
17
|
+
*/
|
|
18
|
+
export type NavigatorContextValue = {
|
|
19
|
+
navigate: (params: NavigateParams) => void;
|
|
20
|
+
params: Record<string, string>;
|
|
13
21
|
};
|
package/src/index.ts
CHANGED
package/src/index.web.ts
ADDED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { View, Text } from '@idealyst/components'
|
|
3
3
|
import { StackLayoutProps } from '../routing/types'
|
|
4
|
+
import { Outlet } from 'react-router'
|
|
4
5
|
|
|
5
6
|
export interface DefaultStackLayoutProps extends StackLayoutProps {
|
|
6
7
|
onNavigate: (path: string) => void
|
|
@@ -14,7 +15,6 @@ export interface DefaultStackLayoutProps extends StackLayoutProps {
|
|
|
14
15
|
export const DefaultStackLayout: React.FC<DefaultStackLayoutProps> = ({
|
|
15
16
|
options,
|
|
16
17
|
routes,
|
|
17
|
-
ContentComponent,
|
|
18
18
|
onNavigate,
|
|
19
19
|
currentPath
|
|
20
20
|
}) => {
|
|
@@ -50,7 +50,7 @@ export const DefaultStackLayout: React.FC<DefaultStackLayoutProps> = ({
|
|
|
50
50
|
|
|
51
51
|
{/* Content Area */}
|
|
52
52
|
<View style={{ flex: 1, padding: 20 }}>
|
|
53
|
-
<
|
|
53
|
+
<Outlet />
|
|
54
54
|
</View>
|
|
55
55
|
</View>
|
|
56
56
|
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { View, Text, Button, Icon } from '@idealyst/components'
|
|
3
3
|
import { TabLayoutProps } from '../routing/types'
|
|
4
|
+
import { Outlet } from 'react-router'
|
|
4
5
|
|
|
5
6
|
export interface DefaultTabLayoutProps extends TabLayoutProps {}
|
|
6
7
|
|
|
@@ -11,7 +12,6 @@ export interface DefaultTabLayoutProps extends TabLayoutProps {}
|
|
|
11
12
|
export const DefaultTabLayout: React.FC<DefaultTabLayoutProps> = ({
|
|
12
13
|
options,
|
|
13
14
|
routes,
|
|
14
|
-
ContentComponent,
|
|
15
15
|
onNavigate,
|
|
16
16
|
currentPath
|
|
17
17
|
}) => {
|
|
@@ -94,7 +94,7 @@ export const DefaultTabLayout: React.FC<DefaultTabLayoutProps> = ({
|
|
|
94
94
|
|
|
95
95
|
{/* Content Area */}
|
|
96
96
|
<View style={{ flex: 1, padding: 20 }}>
|
|
97
|
-
<
|
|
97
|
+
<Outlet />
|
|
98
98
|
</View>
|
|
99
99
|
</View>
|
|
100
100
|
)
|
|
@@ -1,223 +1,78 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
+
import { Routes, Route } from 'react-router'
|
|
2
3
|
import { DefaultStackLayout } from '../layouts/DefaultStackLayout'
|
|
3
4
|
import { DefaultTabLayout } from '../layouts/DefaultTabLayout'
|
|
4
|
-
import { NavigatorParam,
|
|
5
|
+
import { NavigatorParam, RouteParam } from './types'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
* Build the Web navigator using
|
|
8
|
+
* Build the Web navigator using React Router v7 nested routes
|
|
8
9
|
* @param params Navigator configuration
|
|
9
10
|
* @param parentPath Parent route path for nested routing
|
|
10
11
|
* @returns React Router component
|
|
11
12
|
*/
|
|
12
13
|
export const buildNavigator = (params: NavigatorParam, parentPath = '') => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return <StackNavigator params={params} parentPath={parentPath} />
|
|
19
|
-
default:
|
|
20
|
-
throw new Error(`Unsupported navigator layout: ${(params as any).layout}`)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return NavigatorComponent
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Normalize path for navigation - convert empty string to '/'
|
|
29
|
-
*/
|
|
30
|
-
const normalizePath = (path: string): string => {
|
|
31
|
-
if (path === '' || path === '/') {
|
|
32
|
-
return '/'
|
|
33
|
-
}
|
|
34
|
-
return path.startsWith('/') ? path : `/${path}`
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Build full path by combining parent path with child path
|
|
39
|
-
*/
|
|
40
|
-
const buildFullPath = (parentPath: string, childPath: string): string => {
|
|
41
|
-
if (childPath === '/') {
|
|
42
|
-
return parentPath || '/'
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const normalizedParent = parentPath === '/' ? '' : parentPath
|
|
46
|
-
const normalizedChild = normalizePath(childPath)
|
|
47
|
-
|
|
48
|
-
return `${normalizedParent}${normalizedChild}`
|
|
14
|
+
return () => (
|
|
15
|
+
<Routes>
|
|
16
|
+
{params.routes.map((child, index) => buildRoute(child, index))}
|
|
17
|
+
</Routes>
|
|
18
|
+
)
|
|
49
19
|
}
|
|
50
20
|
|
|
51
|
-
/**
|
|
52
|
-
* Check if current path matches a route, considering parent path
|
|
53
|
-
*/
|
|
54
|
-
const pathMatches = (currentPath: string, routePath: string, parentPath: string): boolean => {
|
|
55
|
-
const fullRoutePath = buildFullPath(parentPath, routePath)
|
|
56
|
-
return currentPath === fullRoutePath
|
|
57
|
-
}
|
|
58
21
|
|
|
59
22
|
/**
|
|
60
|
-
*
|
|
23
|
+
* Build Route - handles both screens and nested navigators
|
|
24
|
+
* @param params Route configuration
|
|
25
|
+
* @param index Route index for key generation
|
|
26
|
+
* @param isNested Whether this is a nested route (should strip leading slash)
|
|
27
|
+
* @returns React Router Route element
|
|
61
28
|
*/
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const getCurrentPath = () => {
|
|
68
|
-
return window.location.pathname
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const [currentPath, setCurrentPath] = React.useState(getCurrentPath)
|
|
72
|
-
|
|
73
|
-
// Listen for navigation changes
|
|
74
|
-
React.useEffect(() => {
|
|
75
|
-
const handlePopState = () => {
|
|
76
|
-
setCurrentPath(getCurrentPath())
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
window.addEventListener('popstate', handlePopState)
|
|
80
|
-
return () => window.removeEventListener('popstate', handlePopState)
|
|
81
|
-
}, [])
|
|
82
|
-
|
|
83
|
-
// Navigate function
|
|
84
|
-
const navigateToRoute = (routePath: string) => {
|
|
85
|
-
const fullPath = buildFullPath(parentPath, routePath)
|
|
86
|
-
window.history.pushState({}, '', fullPath)
|
|
87
|
-
setCurrentPath(fullPath)
|
|
88
|
-
}
|
|
29
|
+
const buildRoute = (params: RouteParam, index: number, isNested = false) => {
|
|
30
|
+
// For nested routes, strip leading slash to make path relative
|
|
31
|
+
const routePath = isNested && params.path.startsWith('/')
|
|
32
|
+
? params.path.slice(1)
|
|
33
|
+
: params.path;
|
|
89
34
|
|
|
90
|
-
|
|
91
|
-
const getCurrentRoute = () => {
|
|
92
|
-
// Find matching route
|
|
93
|
-
const currentRoute = params.routes.find(route => {
|
|
94
|
-
return pathMatches(currentPath, route.path, parentPath)
|
|
95
|
-
}) || params.routes[0] // fallback to first route
|
|
96
|
-
|
|
97
|
-
if (!currentRoute || currentRoute.type !== 'screen') {
|
|
98
|
-
return () => React.createElement('div', {}, `Route not found: ${currentPath}`)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return currentRoute.component
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Transform routes to include full paths for layout component
|
|
105
|
-
const routesWithFullPaths = params.routes.map(route => ({
|
|
106
|
-
...route,
|
|
107
|
-
fullPath: buildFullPath(parentPath, route.path)
|
|
108
|
-
}))
|
|
109
|
-
|
|
110
|
-
// Use custom layout component or default
|
|
111
|
-
const LayoutComponent = params.layoutComponent || DefaultTabLayout
|
|
112
|
-
|
|
113
|
-
return (
|
|
114
|
-
<LayoutComponent
|
|
115
|
-
options={params.options}
|
|
116
|
-
routes={routesWithFullPaths}
|
|
117
|
-
ContentComponent={getCurrentRoute()}
|
|
118
|
-
onNavigate={navigateToRoute}
|
|
119
|
-
currentPath={currentPath}
|
|
120
|
-
/>
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Stack Navigator Component for web using custom layout components
|
|
126
|
-
*/
|
|
127
|
-
const StackNavigator: React.FC<{ params: StackNavigatorParam; parentPath: string }> = ({
|
|
128
|
-
params,
|
|
129
|
-
parentPath
|
|
130
|
-
}) => {
|
|
131
|
-
// Get current path from window location
|
|
132
|
-
const getCurrentPath = () => {
|
|
133
|
-
return window.location.pathname
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const [currentPath, setCurrentPath] = React.useState(getCurrentPath)
|
|
137
|
-
|
|
138
|
-
// Listen for navigation changes
|
|
139
|
-
React.useEffect(() => {
|
|
140
|
-
const handlePopState = () => {
|
|
141
|
-
setCurrentPath(getCurrentPath())
|
|
142
|
-
}
|
|
35
|
+
if (params.type === 'screen') {
|
|
143
36
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}, [])
|
|
147
|
-
|
|
148
|
-
// Navigate function
|
|
149
|
-
const navigateToRoute = (routePath: string) => {
|
|
150
|
-
const fullPath = buildFullPath(parentPath, routePath)
|
|
151
|
-
window.history.pushState({}, '', fullPath)
|
|
152
|
-
setCurrentPath(fullPath)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Get current route component
|
|
156
|
-
const getCurrentRoute = () => {
|
|
157
|
-
// Find matching route
|
|
158
|
-
const currentRoute = params.routes.find(route => {
|
|
159
|
-
return pathMatches(currentPath, route.path, parentPath)
|
|
160
|
-
})
|
|
37
|
+
// If the route path is empty (root route in navigator), make it an index route
|
|
38
|
+
const routeProps = routePath === '' ? { index: true } : { path: routePath };
|
|
161
39
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return () => {
|
|
174
|
-
const NestedNavigator = buildNavigator(navigatorRoute, buildFullPath(parentPath, navigatorRoute.path))
|
|
175
|
-
return React.createElement(NestedNavigator)
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
40
|
+
return (
|
|
41
|
+
<Route
|
|
42
|
+
key={`${params.path}-${index}`}
|
|
43
|
+
{...routeProps}
|
|
44
|
+
element={React.createElement(params.component)}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
} else if (params.type === 'navigator') {
|
|
48
|
+
// Get the layout component directly
|
|
49
|
+
const LayoutComponent = params.layoutComponent ||
|
|
50
|
+
(params.layout === 'tab' ? DefaultTabLayout : DefaultStackLayout);
|
|
179
51
|
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
} else if (fallbackRoute.type === 'navigator') {
|
|
186
|
-
const NestedNavigator = buildNavigator(fallbackRoute, buildFullPath(parentPath, fallbackRoute.path))
|
|
187
|
-
return () => React.createElement(NestedNavigator)
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (currentRoute) {
|
|
192
|
-
if (currentRoute.type === 'screen') {
|
|
193
|
-
return currentRoute.component
|
|
194
|
-
} else if (currentRoute.type === 'navigator') {
|
|
195
|
-
// Nested navigator
|
|
196
|
-
const NestedNavigator = buildNavigator(currentRoute, buildFullPath(parentPath, currentRoute.path))
|
|
197
|
-
return () => React.createElement(NestedNavigator)
|
|
198
|
-
}
|
|
199
|
-
}
|
|
52
|
+
// Transform routes to include full paths for layout component
|
|
53
|
+
const routesWithFullPaths = params.routes.map(route => ({
|
|
54
|
+
...route,
|
|
55
|
+
fullPath: route.path
|
|
56
|
+
}));
|
|
200
57
|
|
|
201
|
-
|
|
202
|
-
|
|
58
|
+
return (
|
|
59
|
+
<Route
|
|
60
|
+
key={`${params.path}-${index}`}
|
|
61
|
+
path={routePath}
|
|
62
|
+
element={
|
|
63
|
+
<LayoutComponent
|
|
64
|
+
options={params.options}
|
|
65
|
+
routes={routesWithFullPaths}
|
|
66
|
+
onNavigate={() => {}} // Layout components can use their own navigation logic
|
|
67
|
+
currentPath=""
|
|
68
|
+
/>
|
|
69
|
+
}
|
|
70
|
+
>
|
|
71
|
+
{params.routes.map((child, childIndex) => buildRoute(child, childIndex, true))}
|
|
72
|
+
</Route>
|
|
73
|
+
);
|
|
203
74
|
}
|
|
75
|
+
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
204
78
|
|
|
205
|
-
// Transform routes to include full paths for layout component
|
|
206
|
-
const routesWithFullPaths = params.routes.map(route => ({
|
|
207
|
-
...route,
|
|
208
|
-
fullPath: buildFullPath(parentPath, route.path)
|
|
209
|
-
}))
|
|
210
|
-
|
|
211
|
-
// Use custom layout component or default
|
|
212
|
-
const LayoutComponent = params.layoutComponent || DefaultStackLayout
|
|
213
|
-
|
|
214
|
-
return (
|
|
215
|
-
<LayoutComponent
|
|
216
|
-
options={params.options}
|
|
217
|
-
routes={routesWithFullPaths}
|
|
218
|
-
ContentComponent={getCurrentRoute()}
|
|
219
|
-
onNavigate={navigateToRoute}
|
|
220
|
-
currentPath={currentPath}
|
|
221
|
-
/>
|
|
222
|
-
)
|
|
223
|
-
}
|
package/src/routing/types.ts
CHANGED
|
@@ -102,7 +102,6 @@ export type RouteWithFullPath<T = ScreenOptions> = RouteParam<T> & {
|
|
|
102
102
|
export type TabLayoutProps = {
|
|
103
103
|
options?: NavigatorOptions
|
|
104
104
|
routes: RouteWithFullPath<TabBarScreenOptions>[]
|
|
105
|
-
ContentComponent: React.ComponentType
|
|
106
105
|
onNavigate: (path: string) => void
|
|
107
106
|
currentPath: string
|
|
108
107
|
}
|
|
@@ -110,7 +109,6 @@ export type TabLayoutProps = {
|
|
|
110
109
|
export type StackLayoutProps = {
|
|
111
110
|
options?: NavigatorOptions
|
|
112
111
|
routes: RouteWithFullPath<ScreenOptions>[]
|
|
113
|
-
ContentComponent: React.ComponentType
|
|
114
112
|
onNavigate: (path: string) => void
|
|
115
113
|
currentPath: string
|
|
116
114
|
}
|