@idealyst/navigation 1.0.62 → 1.0.64
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 +3 -3
- package/src/examples/ExampleStackRouter.tsx +0 -27
- package/src/layouts/index.ts +0 -2
- package/src/routing/types.ts +5 -0
- package/src/layouts/DefaultTabLayout.web.tsx +0 -105
- package/src/layouts/GeneralLayout/GeneralLayout.styles.tsx +0 -55
- package/src/layouts/GeneralLayout/GeneralLayout.tsx +0 -143
- package/src/layouts/GeneralLayout/README.md +0 -498
- package/src/layouts/GeneralLayout/index.ts +0 -2
- package/src/layouts/GeneralLayout/types.ts +0 -99
- package/src/layouts/TabBarLayout/TabBarLayout.native.tsx +0 -283
- package/src/layouts/TabBarLayout/TabBarLayout.styles.tsx +0 -142
- package/src/layouts/TabBarLayout/TabBarLayout.web.tsx +0 -286
- package/src/layouts/TabBarLayout/index.native.ts +0 -2
- package/src/layouts/TabBarLayout/index.ts +0 -2
- package/src/layouts/TabBarLayout/index.web.ts +0 -2
- package/src/layouts/TabBarLayout/types.ts +0 -176
- package/src/routing_old/README.md +0 -421
- package/src/routing_old/index.native.tsx +0 -2
- package/src/routing_old/index.ts +0 -2
- package/src/routing_old/index.web.tsx +0 -2
- package/src/routing_old/router.native.tsx +0 -192
- package/src/routing_old/router.web.tsx +0 -366
- package/src/routing_old/types.ts +0 -74
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
|
4
|
-
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
|
|
5
|
-
import { createDrawerNavigator } from "@react-navigation/drawer";
|
|
6
|
-
|
|
7
|
-
import { RouteParam, ScreenOptions } from "./types"
|
|
8
|
-
import { TypedNavigator } from "@react-navigation/native";
|
|
9
|
-
import { Icon } from '@idealyst/components';
|
|
10
|
-
|
|
11
|
-
export const buildRouter = (routeParam: RouteParam, path: string = '') => {
|
|
12
|
-
return () => buildNativeRouter(routeParam, path)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Convert ScreenOptions to React Navigation screen options
|
|
17
|
-
*/
|
|
18
|
-
const convertScreenOptions = (screenOptions?: ScreenOptions) => {
|
|
19
|
-
if (!screenOptions) return {};
|
|
20
|
-
|
|
21
|
-
const options: any = {};
|
|
22
|
-
|
|
23
|
-
if (screenOptions.title) {
|
|
24
|
-
options.title = screenOptions.title;
|
|
25
|
-
// Set default headerTitle to title, but allow headerTitle to override
|
|
26
|
-
if (!screenOptions.headerTitle) {
|
|
27
|
-
options.headerTitle = screenOptions.title;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (screenOptions.tabBarLabel) {
|
|
32
|
-
options.tabBarLabel = screenOptions.tabBarLabel;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (screenOptions.tabBarIcon) {
|
|
36
|
-
if (typeof screenOptions.tabBarIcon === 'string') {
|
|
37
|
-
options.tabBarIcon = ({ focused }: { focused: boolean; color: string; size: number }) => (
|
|
38
|
-
<Icon
|
|
39
|
-
name={screenOptions.tabBarIcon as any}
|
|
40
|
-
color={focused ? 'primary' : 'secondary'}
|
|
41
|
-
/>
|
|
42
|
-
);
|
|
43
|
-
} else if (typeof screenOptions.tabBarIcon === 'function') {
|
|
44
|
-
options.tabBarIcon = screenOptions.tabBarIcon
|
|
45
|
-
} else {
|
|
46
|
-
options.tabBarIcon = screenOptions.tabBarIcon;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (screenOptions.tabBarBadge !== undefined) {
|
|
51
|
-
options.tabBarBadge = screenOptions.tabBarBadge;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (screenOptions.tabBarVisible !== undefined) {
|
|
55
|
-
options.tabBarStyle = screenOptions.tabBarVisible ? {} : { display: 'none' };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// headerTitle should override the default title in the header
|
|
59
|
-
if (screenOptions.headerTitle) {
|
|
60
|
-
options.headerTitle = screenOptions.headerTitle;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (screenOptions.headerBackVisible !== undefined) {
|
|
64
|
-
options.headerBackVisible = screenOptions.headerBackVisible;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (screenOptions.headerLeft) {
|
|
68
|
-
options.headerLeft = screenOptions.headerLeft;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (screenOptions.headerRight) {
|
|
72
|
-
options.headerRight = screenOptions.headerRight;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (screenOptions.platformOptions?.native) {
|
|
76
|
-
Object.assign(options, screenOptions.platformOptions.native);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return options;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Create the router supporting React Navigation
|
|
84
|
-
* @param routeParam
|
|
85
|
-
* @param path
|
|
86
|
-
* @param LastNavigator
|
|
87
|
-
* @returns
|
|
88
|
-
*/
|
|
89
|
-
const buildNativeRouter = (routeParam: RouteParam, path: string = '', LastNavigator?: TypedNavigator<any>): React.ReactElement => {
|
|
90
|
-
const nextPath = (routeParam.path ? path + routeParam.path : path) || '';
|
|
91
|
-
const type = routeParam.layout?.type;
|
|
92
|
-
const screenOptions = convertScreenOptions(routeParam.screenOptions);
|
|
93
|
-
|
|
94
|
-
function buildComponent() {
|
|
95
|
-
switch (type) {
|
|
96
|
-
case 'stack':
|
|
97
|
-
const Stack = createNativeStackNavigator();
|
|
98
|
-
return (
|
|
99
|
-
<Stack.Navigator
|
|
100
|
-
screenOptions={{
|
|
101
|
-
// Disable screen optimization to ensure theme updates
|
|
102
|
-
freezeOnBlur: false,
|
|
103
|
-
}}
|
|
104
|
-
>
|
|
105
|
-
<Stack.Screen
|
|
106
|
-
name={nextPath}
|
|
107
|
-
component={routeParam.component}
|
|
108
|
-
options={screenOptions}
|
|
109
|
-
/>
|
|
110
|
-
{routeParam.routes?.map((route) => buildNativeRouter(route, nextPath, Stack))}
|
|
111
|
-
</Stack.Navigator>
|
|
112
|
-
)
|
|
113
|
-
case 'tab':
|
|
114
|
-
const Tab = createBottomTabNavigator();
|
|
115
|
-
return (
|
|
116
|
-
<Tab.Navigator
|
|
117
|
-
screenOptions={{
|
|
118
|
-
// Disable screen optimization to ensure theme updates
|
|
119
|
-
lazy: false,
|
|
120
|
-
freezeOnBlur: false,
|
|
121
|
-
}}
|
|
122
|
-
>
|
|
123
|
-
<Tab.Screen
|
|
124
|
-
name={nextPath}
|
|
125
|
-
component={routeParam.component}
|
|
126
|
-
options={screenOptions}
|
|
127
|
-
/>
|
|
128
|
-
{routeParam.routes?.map((route) => buildNativeRouter(route, nextPath, Tab))}
|
|
129
|
-
</Tab.Navigator>
|
|
130
|
-
)
|
|
131
|
-
case 'drawer':
|
|
132
|
-
const Drawer = createDrawerNavigator();
|
|
133
|
-
return (
|
|
134
|
-
<Drawer.Navigator
|
|
135
|
-
screenOptions={{
|
|
136
|
-
// Disable screen optimization to ensure theme updates
|
|
137
|
-
lazy: false,
|
|
138
|
-
freezeOnBlur: false,
|
|
139
|
-
}}
|
|
140
|
-
>
|
|
141
|
-
<Drawer.Screen
|
|
142
|
-
name={nextPath}
|
|
143
|
-
component={routeParam.component}
|
|
144
|
-
options={screenOptions}
|
|
145
|
-
/>
|
|
146
|
-
{routeParam.routes?.map((route) => buildNativeRouter(route, nextPath, Drawer))}
|
|
147
|
-
</Drawer.Navigator>
|
|
148
|
-
)
|
|
149
|
-
case 'modal':
|
|
150
|
-
if (!LastNavigator) {
|
|
151
|
-
throw new Error('LastNavigator is required for modal layout');
|
|
152
|
-
}
|
|
153
|
-
return (
|
|
154
|
-
<>
|
|
155
|
-
<LastNavigator.Screen
|
|
156
|
-
options={{ headerShown: false, presentation: 'modal', ...screenOptions }}
|
|
157
|
-
name={nextPath}
|
|
158
|
-
component={routeParam.component}
|
|
159
|
-
/>
|
|
160
|
-
{routeParam.routes?.map((route) => buildNativeRouter(route, nextPath, LastNavigator))}
|
|
161
|
-
</>
|
|
162
|
-
)
|
|
163
|
-
case undefined:
|
|
164
|
-
if (!LastNavigator) {
|
|
165
|
-
throw new Error('LastNavigator is required for undefined layout - ' + routeParam.path);
|
|
166
|
-
}
|
|
167
|
-
return (
|
|
168
|
-
<>
|
|
169
|
-
<LastNavigator.Screen
|
|
170
|
-
name={nextPath}
|
|
171
|
-
component={routeParam.component}
|
|
172
|
-
options={screenOptions}
|
|
173
|
-
/>
|
|
174
|
-
{routeParam.routes?.map((route) => buildNativeRouter(route, nextPath, LastNavigator))}
|
|
175
|
-
</>
|
|
176
|
-
)
|
|
177
|
-
default:
|
|
178
|
-
throw new Error(`Unknown layout type: ${type}`);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
const Component = buildComponent();
|
|
182
|
-
if (LastNavigator) {
|
|
183
|
-
return (
|
|
184
|
-
<LastNavigator.Screen
|
|
185
|
-
name={nextPath}
|
|
186
|
-
component={() => Component}
|
|
187
|
-
options={screenOptions}
|
|
188
|
-
/>
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
return Component;
|
|
192
|
-
}
|
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Routes, Route, Outlet, useLocation, useNavigate } from "react-router-dom";
|
|
3
|
-
import { RouteParam, ScreenOptions } from "./types";
|
|
4
|
-
import { GeneralLayout } from "../layouts/GeneralLayout";
|
|
5
|
-
import { View, Text, Icon, Pressable } from '@idealyst/components';
|
|
6
|
-
|
|
7
|
-
// Types for TabButton
|
|
8
|
-
interface TabRoute {
|
|
9
|
-
id: string;
|
|
10
|
-
path: string;
|
|
11
|
-
label: string;
|
|
12
|
-
icon?: React.ComponentType<{ focused: boolean; color: string; size: string }>
|
|
13
|
-
| React.ReactElement
|
|
14
|
-
| ((props: { focused: boolean; color: string; size: string }) => React.ReactElement)
|
|
15
|
-
| string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface TabButtonProps {
|
|
19
|
-
tab: TabRoute;
|
|
20
|
-
isActive: boolean;
|
|
21
|
-
onPress: () => void;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Tab Button Component
|
|
25
|
-
const TabButton: React.FC<TabButtonProps> = ({ tab, isActive, onPress }) => {
|
|
26
|
-
// Render icon - supports React elements, functions, and string names
|
|
27
|
-
const renderIcon = (icon: TabRoute['icon']) => {
|
|
28
|
-
if (!icon) return null;
|
|
29
|
-
|
|
30
|
-
if (typeof icon === 'function') {
|
|
31
|
-
// Function-based icon that receives state - pass explicit colors
|
|
32
|
-
const IconComponent = icon as (props: { focused: boolean; color: string; size: string }) => React.ReactElement;
|
|
33
|
-
return IconComponent({
|
|
34
|
-
focused: isActive,
|
|
35
|
-
color: isActive ? 'blue' : 'black.900',
|
|
36
|
-
size: 'sm'
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (React.isValidElement(icon)) {
|
|
41
|
-
return icon;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (typeof icon === 'string') {
|
|
45
|
-
// Fallback for string icons (though this breaks transpiler support)
|
|
46
|
-
return <Icon name={icon as any} size="md" color={isActive ? 'white' : 'secondary'} />;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Handle React.ComponentType
|
|
50
|
-
if (typeof icon === 'object' && 'type' in icon) {
|
|
51
|
-
const IconComponent = icon as React.ComponentType<{ focused: boolean; color: string; size: string }>;
|
|
52
|
-
return <IconComponent focused={isActive} color={isActive ? 'white' : 'secondary'} size="sm" />;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return null;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<Pressable onPress={onPress}>
|
|
60
|
-
<View
|
|
61
|
-
style={{
|
|
62
|
-
paddingHorizontal: 12,
|
|
63
|
-
paddingVertical: 8,
|
|
64
|
-
borderRadius: 6,
|
|
65
|
-
flexDirection: 'row',
|
|
66
|
-
alignItems: 'center',
|
|
67
|
-
}}
|
|
68
|
-
>
|
|
69
|
-
{tab.icon ? renderIcon(tab.icon) : null}
|
|
70
|
-
{tab.label && (
|
|
71
|
-
<Text
|
|
72
|
-
size="small"
|
|
73
|
-
color={isActive ? 'blue' : 'black.900'}
|
|
74
|
-
style={{
|
|
75
|
-
marginLeft: tab.icon ? 8 : 0,
|
|
76
|
-
}}
|
|
77
|
-
>
|
|
78
|
-
{tab.label}
|
|
79
|
-
</Text>
|
|
80
|
-
)}
|
|
81
|
-
</View>
|
|
82
|
-
</Pressable>
|
|
83
|
-
);
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
// Types for SimpleTabLayout
|
|
87
|
-
interface SimpleTabLayoutProps {
|
|
88
|
-
routeParam: RouteParam;
|
|
89
|
-
webScreenOptions: {
|
|
90
|
-
title?: string;
|
|
91
|
-
headerTitle?: React.ComponentType | React.ReactElement | string;
|
|
92
|
-
headerLeft?: React.ComponentType | React.ReactElement;
|
|
93
|
-
headerRight?: React.ComponentType | React.ReactElement;
|
|
94
|
-
tabBarLabel?: string;
|
|
95
|
-
tabBarIcon?: TabRoute['icon'];
|
|
96
|
-
[key: string]: any;
|
|
97
|
-
};
|
|
98
|
-
currentPath: string;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Simple Tab Layout Component
|
|
102
|
-
const SimpleTabLayout: React.FC<SimpleTabLayoutProps> = ({ routeParam, webScreenOptions }) => {
|
|
103
|
-
const location = useLocation();
|
|
104
|
-
const navigate = useNavigate();
|
|
105
|
-
|
|
106
|
-
// Build tab links from routes with screen options
|
|
107
|
-
const tabRoutes: TabRoute[] = [
|
|
108
|
-
// Main route (home/index)
|
|
109
|
-
{
|
|
110
|
-
id: 'home',
|
|
111
|
-
path: '',
|
|
112
|
-
label: webScreenOptions.tabBarLabel || webScreenOptions.title || 'Home',
|
|
113
|
-
icon: webScreenOptions.tabBarIcon,
|
|
114
|
-
},
|
|
115
|
-
// Child routes
|
|
116
|
-
...routeParam.routes!.map((route): TabRoute => {
|
|
117
|
-
const routeOptions = convertScreenOptionsForWeb(route.screenOptions);
|
|
118
|
-
return {
|
|
119
|
-
id: route.path || '',
|
|
120
|
-
path: route.path || '',
|
|
121
|
-
label: routeOptions.tabBarLabel || routeOptions.title || route.path || '',
|
|
122
|
-
icon: routeOptions.tabBarIcon,
|
|
123
|
-
};
|
|
124
|
-
}),
|
|
125
|
-
];
|
|
126
|
-
|
|
127
|
-
// Determine active tab based on current location
|
|
128
|
-
const getActiveTab = () => {
|
|
129
|
-
const path = location.pathname.replace(/^\//, ''); // Remove leading slash
|
|
130
|
-
const activeTabId = path || 'home';
|
|
131
|
-
return activeTabId;
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const activeTab = getActiveTab();
|
|
135
|
-
|
|
136
|
-
// Helper to render header element (component, element, or string)
|
|
137
|
-
const renderHeaderElement = (element: React.ComponentType | React.ReactElement | string | undefined) => {
|
|
138
|
-
if (!element) return null;
|
|
139
|
-
if (React.isValidElement(element)) return element;
|
|
140
|
-
if (typeof element === 'string') return <Text size="large" weight="semibold">{element}</Text>;
|
|
141
|
-
if (typeof element === 'function') {
|
|
142
|
-
const Component = element as React.ComponentType;
|
|
143
|
-
return <Component />;
|
|
144
|
-
}
|
|
145
|
-
return null;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
// Create simple header navigation
|
|
149
|
-
const headerContent = (
|
|
150
|
-
<View style={{
|
|
151
|
-
flexDirection: 'row',
|
|
152
|
-
alignItems: 'center',
|
|
153
|
-
justifyContent: 'space-between',
|
|
154
|
-
width: '100%',
|
|
155
|
-
flex: 1
|
|
156
|
-
}}>
|
|
157
|
-
{/* Left side */}
|
|
158
|
-
<View style={{ flexDirection: 'row', alignItems: 'center', flex: 1 }}>
|
|
159
|
-
{webScreenOptions.headerLeft && (
|
|
160
|
-
<View style={{ marginRight: 12 }}>
|
|
161
|
-
{renderHeaderElement(webScreenOptions.headerLeft)}
|
|
162
|
-
</View>
|
|
163
|
-
)}
|
|
164
|
-
{renderHeaderElement(
|
|
165
|
-
webScreenOptions.headerTitle ||
|
|
166
|
-
webScreenOptions.title ||
|
|
167
|
-
'Navigation'
|
|
168
|
-
)}
|
|
169
|
-
</View>
|
|
170
|
-
|
|
171
|
-
{/* Tab Navigation */}
|
|
172
|
-
<View style={{
|
|
173
|
-
flexDirection: 'row',
|
|
174
|
-
alignItems: 'center',
|
|
175
|
-
gap: 8,
|
|
176
|
-
}}>
|
|
177
|
-
{tabRoutes.map((tab) => (
|
|
178
|
-
<TabButton
|
|
179
|
-
key={tab.id}
|
|
180
|
-
tab={tab}
|
|
181
|
-
isActive={activeTab === tab.id}
|
|
182
|
-
onPress={() => {
|
|
183
|
-
let targetPath;
|
|
184
|
-
if (tab.id === 'home') {
|
|
185
|
-
targetPath = '/';
|
|
186
|
-
} else {
|
|
187
|
-
targetPath = `/${tab.path}`;
|
|
188
|
-
}
|
|
189
|
-
navigate(targetPath);
|
|
190
|
-
}}
|
|
191
|
-
/>
|
|
192
|
-
))}
|
|
193
|
-
</View>
|
|
194
|
-
|
|
195
|
-
{/* Right side */}
|
|
196
|
-
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end', flex: 1 }}>
|
|
197
|
-
{renderHeaderElement(webScreenOptions.headerRight)}
|
|
198
|
-
</View>
|
|
199
|
-
</View>
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
return (
|
|
203
|
-
<GeneralLayout
|
|
204
|
-
header={{
|
|
205
|
-
enabled: true,
|
|
206
|
-
content: headerContent,
|
|
207
|
-
}}
|
|
208
|
-
sidebar={{
|
|
209
|
-
enabled: false,
|
|
210
|
-
}}
|
|
211
|
-
>
|
|
212
|
-
<Outlet />
|
|
213
|
-
</GeneralLayout>
|
|
214
|
-
);
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
export const buildRouter = (routeParam: RouteParam, path: string = '') => {
|
|
218
|
-
return () => (
|
|
219
|
-
<Routes>
|
|
220
|
-
{buildWebRoutes(routeParam, path)}
|
|
221
|
-
</Routes>
|
|
222
|
-
);
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Convert ScreenOptions to web-compatible props for layout components
|
|
227
|
-
*/
|
|
228
|
-
const convertScreenOptionsForWeb = (screenOptions?: ScreenOptions) => {
|
|
229
|
-
if (!screenOptions) return {};
|
|
230
|
-
|
|
231
|
-
const webOptions: any = {};
|
|
232
|
-
|
|
233
|
-
// Basic screen info
|
|
234
|
-
if (screenOptions.title) {
|
|
235
|
-
webOptions.title = screenOptions.title;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (screenOptions.tabBarLabel) {
|
|
239
|
-
webOptions.tabBarLabel = screenOptions.tabBarLabel;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (screenOptions.tabBarIcon) {
|
|
243
|
-
webOptions.tabBarIcon = screenOptions.tabBarIcon;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (screenOptions.tabBarBadge !== undefined) {
|
|
247
|
-
webOptions.tabBarBadge = screenOptions.tabBarBadge;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (screenOptions.tabBarVisible !== undefined) {
|
|
251
|
-
webOptions.tabBarVisible = screenOptions.tabBarVisible;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (screenOptions.headerTitle) {
|
|
255
|
-
webOptions.headerTitle = screenOptions.headerTitle;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (screenOptions.headerBackVisible !== undefined) {
|
|
259
|
-
webOptions.headerBackVisible = screenOptions.headerBackVisible;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (screenOptions.headerLeft) {
|
|
263
|
-
webOptions.headerLeft = screenOptions.headerLeft;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (screenOptions.headerRight) {
|
|
267
|
-
webOptions.headerRight = screenOptions.headerRight;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (screenOptions.platformOptions?.web) {
|
|
271
|
-
Object.assign(webOptions, screenOptions.platformOptions.web);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return webOptions;
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Create React Router routes from RouteParam configuration
|
|
279
|
-
* @param routeParam The route parameter configuration
|
|
280
|
-
* @param parentPath The parent path for nested routes
|
|
281
|
-
* @returns Array of React Router Route elements
|
|
282
|
-
*/
|
|
283
|
-
const buildWebRoutes = (routeParam: RouteParam, parentPath: string = ''): React.ReactElement[] => {
|
|
284
|
-
const routes: React.ReactElement[] = [];
|
|
285
|
-
const currentPath = routeParam.path ? `${parentPath}${routeParam.path}` : parentPath;
|
|
286
|
-
|
|
287
|
-
// Handle layout wrapping
|
|
288
|
-
const LayoutComponent = routeParam.layout?.component;
|
|
289
|
-
const RouteComponent = routeParam.component;
|
|
290
|
-
const webScreenOptions = convertScreenOptionsForWeb(routeParam.screenOptions);
|
|
291
|
-
const isTabLayout = routeParam.layout?.type === 'tab';
|
|
292
|
-
|
|
293
|
-
if (isTabLayout && routeParam.routes) {
|
|
294
|
-
// Create simple header-based tab navigation using GeneralLayout
|
|
295
|
-
const SimpleTabLayoutWrapper: React.FC = () => {
|
|
296
|
-
return <SimpleTabLayout routeParam={routeParam} webScreenOptions={webScreenOptions} currentPath={currentPath} />;
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
// Create parent route with simple tab layout
|
|
300
|
-
const layoutRoute = (
|
|
301
|
-
<Route
|
|
302
|
-
key={`simple-tab-layout-${currentPath || 'root'}`}
|
|
303
|
-
path={currentPath || '/'}
|
|
304
|
-
element={<SimpleTabLayoutWrapper />}
|
|
305
|
-
>
|
|
306
|
-
{/* Add index route for the main component */}
|
|
307
|
-
<Route
|
|
308
|
-
index
|
|
309
|
-
element={<RouteComponent {...webScreenOptions} />}
|
|
310
|
-
/>
|
|
311
|
-
{/* Add nested routes */}
|
|
312
|
-
{routeParam.routes.reduce((acc, nestedRoute) => {
|
|
313
|
-
return acc.concat(buildWebRoutes(nestedRoute, currentPath));
|
|
314
|
-
}, [] as React.ReactElement[])}
|
|
315
|
-
</Route>
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
routes.push(layoutRoute);
|
|
319
|
-
} else if (LayoutComponent && routeParam.routes) {
|
|
320
|
-
// Create a wrapper component that renders the layout with Outlet and screen options
|
|
321
|
-
const LayoutWrapper: React.FC = () => (
|
|
322
|
-
<LayoutComponent {...webScreenOptions}>
|
|
323
|
-
<Outlet />
|
|
324
|
-
</LayoutComponent>
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
// Create parent route with layout
|
|
328
|
-
const layoutRoute = (
|
|
329
|
-
<Route
|
|
330
|
-
key={`layout-${currentPath || 'root'}`}
|
|
331
|
-
path={currentPath || '/'}
|
|
332
|
-
element={<LayoutWrapper />}
|
|
333
|
-
>
|
|
334
|
-
{/* Add index route for the main component */}
|
|
335
|
-
<Route
|
|
336
|
-
index
|
|
337
|
-
element={<RouteComponent {...webScreenOptions} />}
|
|
338
|
-
/>
|
|
339
|
-
{/* Add nested routes */}
|
|
340
|
-
{routeParam.routes.reduce((acc, nestedRoute) => {
|
|
341
|
-
return acc.concat(buildWebRoutes(nestedRoute, currentPath));
|
|
342
|
-
}, [] as React.ReactElement[])}
|
|
343
|
-
</Route>
|
|
344
|
-
);
|
|
345
|
-
|
|
346
|
-
routes.push(layoutRoute);
|
|
347
|
-
} else {
|
|
348
|
-
// Simple route without layout
|
|
349
|
-
routes.push(
|
|
350
|
-
<Route
|
|
351
|
-
key={currentPath || 'root'}
|
|
352
|
-
path={currentPath || '/'}
|
|
353
|
-
element={<RouteComponent {...webScreenOptions} />}
|
|
354
|
-
/>
|
|
355
|
-
);
|
|
356
|
-
|
|
357
|
-
// Handle nested routes without layout
|
|
358
|
-
if (routeParam.routes) {
|
|
359
|
-
routeParam.routes.forEach(nestedRoute => {
|
|
360
|
-
routes.push(...buildWebRoutes(nestedRoute, currentPath));
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return routes;
|
|
366
|
-
};
|
package/src/routing_old/types.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
export type ScreenOptions = {
|
|
4
|
-
/**
|
|
5
|
-
* Screen title for navigation headers
|
|
6
|
-
*/
|
|
7
|
-
title?: string;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Icon component for tab/drawer navigation (React.ComponentType, React.ReactElement, function, or string)
|
|
11
|
-
*/
|
|
12
|
-
tabBarIcon?: React.ComponentType<{ focused: boolean; color: string; size: number }>
|
|
13
|
-
| React.ReactElement
|
|
14
|
-
| ((props: { focused: boolean; color: string; size: string }) => React.ReactElement)
|
|
15
|
-
| string;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Label for tab/drawer navigation
|
|
19
|
-
*/
|
|
20
|
-
tabBarLabel?: string;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Badge for tab navigation
|
|
24
|
-
*/
|
|
25
|
-
tabBarBadge?: string | number;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Whether to show the tab bar for this screen
|
|
29
|
-
*/
|
|
30
|
-
tabBarVisible?: boolean;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Custom header title component or string
|
|
34
|
-
*/
|
|
35
|
-
headerTitle?: React.ComponentType | React.ReactElement | string;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Custom header left component (overrides back button)
|
|
39
|
-
*/
|
|
40
|
-
headerLeft?: React.ComponentType | React.ReactElement;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Whether to show header back button
|
|
44
|
-
*/
|
|
45
|
-
headerBackVisible?: boolean;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Custom header right component
|
|
49
|
-
*/
|
|
50
|
-
headerRight?: React.ComponentType | React.ReactElement;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Additional platform-specific options
|
|
54
|
-
*/
|
|
55
|
-
platformOptions?: {
|
|
56
|
-
native?: Record<string, any>;
|
|
57
|
-
web?: Record<string, any>;
|
|
58
|
-
};
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export type RouteParam = {
|
|
62
|
-
path?: string;
|
|
63
|
-
routes?: RouteParam[];
|
|
64
|
-
component: React.ComponentType;
|
|
65
|
-
layout?: LayoutParam;
|
|
66
|
-
screenOptions?: ScreenOptions;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export type LayoutType = 'stack' | 'tab' | 'drawer' | 'modal';
|
|
70
|
-
|
|
71
|
-
export type LayoutParam = {
|
|
72
|
-
type: LayoutType;
|
|
73
|
-
component?: React.ComponentType<{ children?: React.ReactNode }>;
|
|
74
|
-
}
|