@idealyst/navigation 1.0.61 → 1.0.63

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.0.61",
3
+ "version": "1.0.63",
4
4
  "description": "Cross-platform navigation library for React and React Native",
5
5
  "readme": "README.md",
6
6
  "main": "src/index.ts",
@@ -38,8 +38,8 @@
38
38
  "publish:npm": "npm publish"
39
39
  },
40
40
  "peerDependencies": {
41
- "@idealyst/components": "^1.0.61",
42
- "@idealyst/theme": "^1.0.61",
41
+ "@idealyst/components": "^1.0.63",
42
+ "@idealyst/theme": "^1.0.63",
43
43
  "@react-navigation/bottom-tabs": "^7.0.0",
44
44
  "@react-navigation/drawer": "^7.0.0",
45
45
  "@react-navigation/native": "^7.0.0",
@@ -1,7 +1,7 @@
1
1
  import React, { createContext, memo, useContext, useMemo } from 'react';
2
2
  import { NavigateParams, NavigatorProviderProps } from './types';
3
3
  import { useNavigation, useNavigationState, DarkTheme, DefaultTheme, NavigationContainer } from '@react-navigation/native';
4
- import { buildRouter } from '../routing';
4
+ import { buildNavigator } from '../routing';
5
5
  import { useUnistyles } from 'react-native-unistyles';
6
6
 
7
7
  const NavigatorContext = createContext<{
@@ -20,9 +20,11 @@ const UnwrappedNavigatorProvider = ({ route }: NavigatorProviderProps) => {
20
20
  };
21
21
 
22
22
  const RouteComponent = useMemo(() => {
23
- // Memoize the router to prevent unnecessary re-renders
24
- return memo(buildRouter(route));
23
+ // Memoize the navigator to prevent unnecessary re-renders
24
+ return memo(buildNavigator(route));
25
25
  }, [route]);
26
+
27
+ console.log('UnwrappedNavigatorProvider render', RouteComponent);
26
28
 
27
29
  return (
28
30
  <NavigatorContext.Provider value={{ navigate }}>
@@ -1,7 +1,6 @@
1
1
  import React, { createContext, memo, useContext, useMemo } from 'react';
2
2
  import { NavigateParams, NavigatorProviderProps } from './types';
3
- import { useNavigate } from "react-router-dom";
4
- import { buildRouter } from '../routing';
3
+ import { buildNavigator } from '../routing';
5
4
 
6
5
  const NavigatorContext = createContext<{
7
6
  navigate: (params: NavigateParams) => void;
@@ -12,17 +11,26 @@ const NavigatorContext = createContext<{
12
11
  export const NavigatorProvider = ({
13
12
  route,
14
13
  }: NavigatorProviderProps) => {
15
- const routerNavigate = useNavigate();
16
-
17
14
  const navigateFunction = (params: NavigateParams) => {
18
15
  if (params.path) {
19
- routerNavigate(params.path);
16
+ // Normalize path - convert empty string to '/'
17
+ let path = params.path
18
+ if (path === '' || path === '/') {
19
+ path = '/'
20
+ } else if (!path.startsWith('/')) {
21
+ path = `/${path}`
22
+ }
23
+
24
+ // Use HTML5 history API for proper navigation without hash
25
+ window.history.pushState({}, '', path);
26
+ // Trigger a popstate event to update any listening components
27
+ window.dispatchEvent(new PopStateEvent('popstate'));
20
28
  }
21
29
  };
22
30
 
23
31
  const RouteComponent = useMemo(() => {
24
32
  // Memoize the router to prevent unnecessary re-renders
25
- return memo(buildRouter(route));
33
+ return memo(buildNavigator(route));
26
34
  }, [route]);
27
35
 
28
36
  return (
@@ -1,4 +1,4 @@
1
- import { RouteParam } from "../routing";
1
+ import { NavigatorParam } from "../routing";
2
2
 
3
3
  /**
4
4
  * When navigating to a new route, specify the path and the variables to be used in the route.
@@ -9,5 +9,5 @@ export type NavigateParams = {
9
9
  };
10
10
 
11
11
  export type NavigatorProviderProps = {
12
- route: RouteParam;
12
+ route: NavigatorParam;
13
13
  };
@@ -0,0 +1,63 @@
1
+ import { Button, Text, View } from "@idealyst/components";
2
+ import { StackLayoutProps } from "src";
3
+ import React, { useMemo } from "react";
4
+
5
+ export default function CustomStackLayout({
6
+ routes,
7
+ options,
8
+ ContentComponent,
9
+ onNavigate,
10
+ currentPath
11
+ }: StackLayoutProps) {
12
+
13
+ const headerTitle = useMemo(() => {
14
+ if (!options?.headerTitle) return <Text>{currentPath}</Text>;
15
+ if (typeof options.headerTitle === 'string') {
16
+ return <Text>{options.headerTitle}</Text>;
17
+ }
18
+ const HeaderComponent = options.headerTitle as React.ComponentType;
19
+ return <HeaderComponent />;
20
+ }, [options?.headerTitle, currentPath]);
21
+
22
+ return (
23
+ <View style={{ height: '100vh' }}>
24
+ {/* Header */}
25
+ <View style={{ padding: 16, borderBottom: '1px solid #e0e0e0' }}>
26
+ {headerTitle}
27
+ </View>
28
+
29
+ {/* Main content area with sidebar and content */}
30
+ <View style={{ display: 'flex', flexDirection: 'row', flex: 1 }}>
31
+ {/* Left sidebar with routes */}
32
+ <View style={{
33
+ width: 250,
34
+ padding: 16,
35
+ borderRight: '1px solid #e0e0e0',
36
+ backgroundColor: '#f5f5f5',
37
+ display: 'flex',
38
+ flexDirection: 'column',
39
+ gap: 8
40
+ }}>
41
+ {routes.map(route => (
42
+ <Button
43
+ variant={currentPath === route.fullPath ? 'contained' : 'outlined'}
44
+ key={route.path}
45
+ onPress={() => onNavigate(route.path)}
46
+ style={{
47
+ justifyContent: 'flex-start',
48
+ textAlign: 'left'
49
+ }}
50
+ >
51
+ {route.fullPath === '/' ? 'Home' : route.fullPath}
52
+ </Button>
53
+ ))}
54
+ </View>
55
+
56
+ {/* Main content area */}
57
+ <View style={{ flex: 1, padding: 16 }}>
58
+ <ContentComponent />
59
+ </View>
60
+ </View>
61
+ </View>
62
+ )
63
+ }
@@ -0,0 +1,44 @@
1
+ import React from "react";
2
+ import { Button, View, Text } from "@idealyst/components";
3
+ import { TabLayoutProps } from "src/routing";
4
+
5
+ export default function CustomTabLayout({
6
+ routes,
7
+ options,
8
+ ContentComponent,
9
+ onNavigate,
10
+ currentPath
11
+ }: TabLayoutProps) {
12
+
13
+ return (
14
+ <View>
15
+ <View>
16
+ {options?.headerTitle ? (
17
+ typeof options.headerTitle === 'string' ? (
18
+ <Text>{options.headerTitle}</Text>
19
+ ) : (
20
+ React.createElement(options.headerTitle as React.ComponentType)
21
+ )
22
+ ) : (
23
+ <Text>Custom Tab Layout</Text>
24
+ )}
25
+ </View>
26
+ <View style={{ flexDirection: 'row' }}>
27
+ {routes.map(route => (
28
+ <Button
29
+ variant={currentPath === route.fullPath ? 'contained' : 'outlined'}
30
+ key={route.path}
31
+ onPress={() => onNavigate(route.path)}
32
+ style={{ margin: 4 }}
33
+ >
34
+ {route.fullPath === '/' ? 'Home' : route.fullPath}
35
+ </Button>
36
+ ))}
37
+ </View>
38
+ <View>
39
+ <ContentComponent />
40
+ </View>
41
+ </View>
42
+ )
43
+
44
+ }
@@ -1,7 +1,9 @@
1
- import { RouteParam } from '../routing';
1
+ import { NavigatorParam, RouteParam } from '../routing';
2
2
  import React from 'react';
3
3
  import { Button, Screen, Text } from '../../../components/src';
4
4
  import { useNavigator } from '../context';
5
+ import CustomTabLayout from './CustomTabLayout';
6
+ import CustomStackLayout from './CustomStackLayout';
5
7
 
6
8
  const RootScreen = () => {
7
9
 
@@ -10,50 +12,50 @@ const RootScreen = () => {
10
12
  return <Screen>
11
13
  <Text>Root Screen</Text>
12
14
  <Button title="Go to Tab" onPress={() => navigator.navigate({
13
- path: '/a',
15
+ path: '/tab',
14
16
  vars: {},
15
17
  })} />
16
18
  </Screen>
17
19
  }
18
20
 
19
- const ExampleHybridRouter: RouteParam = {
21
+ const ExampleHybridRouter: NavigatorParam = {
20
22
  path: '/',
21
- layout: {
22
- type: 'stack',
23
- },
24
- component: RootScreen,
25
- screenOptions: {
26
- title: 'Example',
27
- headerTitle: 'Example Header',
28
- tabBarLabel: 'Example',
29
- tabBarIcon: 'example-icon',
30
- },
23
+ type: 'navigator',
24
+ layout: 'stack',
25
+ layoutComponent: CustomStackLayout,
31
26
  routes: [
32
27
  {
33
- path: 'a',
34
- component: () => <Text>Nested Tab Example</Text>,
35
- layout: {
36
- type: 'tab',
37
- },
38
- screenOptions: {
39
- title: 'Tab Example',
40
- headerTitle: 'Tab Header',
41
- tabBarLabel: 'Tab',
42
- tabBarIcon: 'tab-icon',
28
+ type: 'screen',
29
+ path: '/',
30
+ component: RootScreen,
31
+ options: {
32
+ title: 'Example',
43
33
  },
34
+ },
35
+ {
36
+ type: 'navigator',
37
+ path: '/tab',
38
+ layout: 'tab',
39
+ layoutComponent: CustomTabLayout,
44
40
  routes: [
45
41
  {
46
- path: 'nested',
47
- component: () => <Text>Tab 2</Text>,
48
- screenOptions: {
49
- title: 'Nested Tab Example',
50
- headerTitle: 'Nested Tab Header',
51
- tabBarLabel: 'Nested Tab',
52
- tabBarIcon: 'nested-tab-icon',
42
+ type: 'screen',
43
+ path: '/a',
44
+ component: () => <Text>Tab A Example</Text>,
45
+ options: {
46
+ title: 'Tab Example',
47
+ },
48
+ },
49
+ {
50
+ type: 'screen',
51
+ path: '/b',
52
+ component: () => <Text>Tab B Example</Text>,
53
+ options: {
54
+ title: 'B',
53
55
  },
54
- }
56
+ },
55
57
  ]
56
- }
58
+ },
57
59
  ]
58
60
  }
59
61
 
@@ -6,7 +6,7 @@ import { Button, Divider, Screen, Text, View } from "../../../components/src";
6
6
  import { useNavigator } from "../context";
7
7
  import { UnistylesRuntime } from 'react-native-unistyles';
8
8
  import { GeneralLayout } from '../layouts/GeneralLayout';
9
- import { RouteParam } from '../routing';
9
+ import { NavigatorParam, RouteParam } from '../routing';
10
10
  import { getNextTheme, getThemeDisplayName, isHighContrastTheme } from './unistyles';
11
11
 
12
12
  const HomeScreen = () => {
@@ -264,31 +264,29 @@ const WrappedGeneralLayout = (props: any) => {
264
264
  )
265
265
  }
266
266
 
267
- const StackRouter: RouteParam = {
267
+ const StackRouter: NavigatorParam = {
268
268
  path: "/",
269
- component: HomeScreen,
270
- layout: {
271
- type: "stack",
272
- component: WrappedGeneralLayout,
273
- },
269
+ type: 'navigator',
270
+ layout: 'stack',
274
271
  routes: [
275
- { path: "avatar", component: AvatarExamples},
276
- { path: "badge", component: BadgeExamples},
277
- { path: "button", component: ButtonExamples},
278
- { path: "card", component: CardExamples},
279
- { path: "checkbox", component: CheckboxExamples},
280
- { path: "divider", component: DividerExamples},
281
- { path: "input", component: InputExamples},
282
- { path: "text", component: TextExamples},
283
- { path: "view", component: ViewExamples},
284
- { path: "screen", component: ScreenExamples},
285
- { path: "icon", component: IconExamples},
286
- { path: "svg-image", component: SVGImageExamples},
287
- { path: "dialog", component: DialogExamples},
288
- { path: "popover", component: PopoverExamples},
289
- { path: "datagrid", component: DataGridShowcase},
290
- { path: "datepicker", component: DatePickerExamples},
291
- { path: "theme-extension", component: ThemeExtensionExamples},
272
+ { path: "/", type: 'screen', component: HomeScreen },
273
+ { path: "avatar", type: 'screen', component: AvatarExamples},
274
+ { path: "badge", type: 'screen', component: BadgeExamples},
275
+ { path: "button", type: 'screen', component: ButtonExamples},
276
+ { path: "card", type: 'screen', component: CardExamples},
277
+ { path: "checkbox", type: 'screen', component: CheckboxExamples},
278
+ { path: "divider", type: 'screen', component: DividerExamples},
279
+ { path: "input", type: 'screen', component: InputExamples},
280
+ { path: "text", type: 'screen', component: TextExamples},
281
+ { path: "view", type: 'screen', component: ViewExamples},
282
+ { path: "screen", type: 'screen', component: ScreenExamples},
283
+ { path: "icon", type: 'screen', component: IconExamples},
284
+ { path: "svg-image", type: 'screen', component: SVGImageExamples},
285
+ { path: "dialog", type: 'screen', component: DialogExamples},
286
+ { path: "popover", type: 'screen', component: PopoverExamples},
287
+ { path: "datagrid", type: 'screen', component: DataGridShowcase},
288
+ { path: "datepicker", type: 'screen', component: DatePickerExamples},
289
+ { path: "theme-extension", type: 'screen', component: ThemeExtensionExamples},
292
290
  ],
293
291
  };
294
292
 
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { ButtonExamples, CardExamples, IconExamples, SVGImageExamples, ThemeExtensionExamples } from "../../../components/src/examples";
3
3
  import { Screen, Text, View, Button, Icon } from "../../../components/src";
4
4
  import { UnistylesRuntime } from 'react-native-unistyles';
5
- import { RouteParam } from '../routing';
5
+ import { NavigatorParam, RouteParam } from '../routing';
6
6
  import { useNavigator } from '../context';
7
7
  import { getNextTheme, getThemeDisplayName, isHighContrastTheme } from './unistyles';
8
8
 
@@ -192,45 +192,48 @@ const ThemeTabScreen = () => (
192
192
  </Screen>
193
193
  );
194
194
 
195
- const TabRouter: RouteParam = {
195
+ const TabRouter: NavigatorParam = {
196
196
  path: "/",
197
- component: HomeTabScreen,
198
- layout: {
199
- type: "tab",
200
- },
201
- screenOptions: {
202
- title: 'Home',
203
- headerTitle: () => (
204
- <View style={{ flexDirection: 'row', alignItems: 'center' }}>
205
- <Icon name="home" size="md" style={{ marginRight: 8 }} />
206
- <Text size="large" weight="bold">Tab Demo</Text>
207
- </View>
208
- ),
209
- headerLeft: () => (
210
- <Button variant="text" size="small">
211
- <Icon name="menu" size="md" />
212
- </Button>
213
- ),
214
- headerRight: () => (
215
- <View style={{ flexDirection: 'row', alignItems: 'center' }}>
216
- <Button variant="text" size="small" style={{ marginRight: 8 }}>
217
- <Icon name="bell" size="md" />
218
- </Button>
219
- <Button variant="text" size="small">
220
- <Icon name="account" size="md" />
221
- </Button>
222
- </View>
223
- ),
224
- tabBarLabel: 'Home',
225
- tabBarIcon: ({ focused, size }) => {
226
- return <Icon name="home" color={focused ? 'blue' : 'black'} size={size} />
227
- },
228
- },
197
+ layout: 'tab',
229
198
  routes: [
199
+ {
200
+ path: '/',
201
+ type: 'screen',
202
+ component: HomeTabScreen,
203
+ options: {
204
+ title: 'Home',
205
+ headerTitle: () => (
206
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
207
+ <Icon name="home" size="md" style={{ marginRight: 8 }} />
208
+ <Text size="large" weight="bold">Tab Demo</Text>
209
+ </View>
210
+ ),
211
+ headerLeft: () => (
212
+ <Button variant="text" size="small">
213
+ <Icon name="menu" size="md" />
214
+ </Button>
215
+ ),
216
+ headerRight: () => (
217
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
218
+ <Button variant="text" size="small" style={{ marginRight: 8 }}>
219
+ <Icon name="bell" size="md" />
220
+ </Button>
221
+ <Button variant="text" size="small">
222
+ <Icon name="account" size="md" />
223
+ </Button>
224
+ </View>
225
+ ),
226
+ tabBarLabel: 'Home',
227
+ tabBarIcon: ({ focused, size }) => {
228
+ return <Icon name="home" color={focused ? 'blue' : 'black'} size={size} />
229
+ },
230
+ },
231
+ },
230
232
  {
231
233
  path: "components",
232
234
  component: ComponentsTabScreen,
233
- screenOptions: {
235
+ type: 'screen',
236
+ options: {
234
237
  title: 'Components',
235
238
  headerLeft: () => (
236
239
  <Button variant="text" size="small">
@@ -254,7 +257,8 @@ const TabRouter: RouteParam = {
254
257
  {
255
258
  path: "settings",
256
259
  component: SettingsTabScreen,
257
- screenOptions: {
260
+ type: 'screen',
261
+ options: {
258
262
  title: 'Settings',
259
263
  headerTitle: 'App Settings',
260
264
  headerLeft: () => (
@@ -286,7 +290,8 @@ const TabRouter: RouteParam = {
286
290
  {
287
291
  path: "theme",
288
292
  component: ThemeTabScreen,
289
- screenOptions: {
293
+ type: 'screen',
294
+ options: {
290
295
  title: 'Theme',
291
296
  headerTitle: () => (
292
297
  <View style={{ flexDirection: 'row', alignItems: 'center' }}>
@@ -0,0 +1,57 @@
1
+ import React from 'react'
2
+ import { View, Text } from '@idealyst/components'
3
+ import { StackLayoutProps } from '../routing/types'
4
+
5
+ export interface DefaultStackLayoutProps extends StackLayoutProps {
6
+ onNavigate: (path: string) => void
7
+ currentPath: string
8
+ }
9
+
10
+ /**
11
+ * Default Stack Layout Component for Web
12
+ * Provides a simple stack navigation interface using @idealyst/components
13
+ */
14
+ export const DefaultStackLayout: React.FC<DefaultStackLayoutProps> = ({
15
+ options,
16
+ routes,
17
+ ContentComponent,
18
+ onNavigate,
19
+ currentPath
20
+ }) => {
21
+ return (
22
+ <View style={{ height: '100vh', flexDirection: 'column' }}>
23
+ {/* Header */}
24
+ {(options?.headerTitle || options?.headerLeft || options?.headerRight) && (
25
+ <View style={{
26
+ padding: 16,
27
+ borderBottomWidth: 1,
28
+ borderBottomColor: '#e0e0e0',
29
+ backgroundColor: '#f8f9fa'
30
+ }}>
31
+ <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
32
+ <View style={{ flexDirection: 'row', alignItems: 'center', flex: 1 }}>
33
+ {options.headerLeft && React.createElement(options.headerLeft as any)}
34
+
35
+ {options.headerTitle && (
36
+ typeof options.headerTitle === 'string' ? (
37
+ <Text size="large" weight="bold" style={{ marginLeft: options.headerLeft ? 12 : 0 }}>
38
+ {options.headerTitle}
39
+ </Text>
40
+ ) : (
41
+ React.createElement(options.headerTitle as any)
42
+ )
43
+ )}
44
+ </View>
45
+
46
+ {options.headerRight && React.createElement(options.headerRight as any)}
47
+ </View>
48
+ </View>
49
+ )}
50
+
51
+ {/* Content Area */}
52
+ <View style={{ flex: 1, padding: 20 }}>
53
+ <ContentComponent />
54
+ </View>
55
+ </View>
56
+ )
57
+ }
@@ -0,0 +1,101 @@
1
+ import React from 'react'
2
+ import { View, Text, Button, Icon } from '@idealyst/components'
3
+ import { TabLayoutProps } from '../routing/types'
4
+
5
+ export interface DefaultTabLayoutProps extends TabLayoutProps {}
6
+
7
+ /**
8
+ * Default Tab Layout Component for Web
9
+ * Provides a simple tab navigation interface using @idealyst/components
10
+ */
11
+ export const DefaultTabLayout: React.FC<DefaultTabLayoutProps> = ({
12
+ options,
13
+ routes,
14
+ ContentComponent,
15
+ onNavigate,
16
+ currentPath
17
+ }) => {
18
+ return (
19
+ <View style={{ height: '100vh', flexDirection: 'column' }}>
20
+ {/* Header */}
21
+ {options?.headerTitle && (
22
+ <View style={{
23
+ padding: 16,
24
+ borderBottomWidth: 1,
25
+ borderBottomColor: '#e0e0e0',
26
+ backgroundColor: '#f8f9fa'
27
+ }}>
28
+ <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
29
+ <View style={{ flexDirection: 'row', alignItems: 'center', flex: 1 }}>
30
+ {options.headerLeft && React.createElement(options.headerLeft as any)}
31
+
32
+ {typeof options.headerTitle === 'string' ? (
33
+ <Text size="large" weight="bold" style={{ marginLeft: options.headerLeft ? 12 : 0 }}>
34
+ {options.headerTitle}
35
+ </Text>
36
+ ) : (
37
+ React.createElement(options.headerTitle as any)
38
+ )}
39
+ </View>
40
+
41
+ {options.headerRight && React.createElement(options.headerRight as any)}
42
+ </View>
43
+ </View>
44
+ )}
45
+
46
+ {/* Tab Navigation */}
47
+ <View style={{
48
+ padding: 12,
49
+ borderBottomWidth: 1,
50
+ borderBottomColor: '#e0e0e0',
51
+ backgroundColor: '#ffffff'
52
+ }}>
53
+ <View style={{ flexDirection: 'row', gap: 8 }}>
54
+ {routes.map((route) => {
55
+ if (route.type !== 'screen') return null
56
+
57
+ const isActive = currentPath === route.fullPath
58
+ const screenRoute = route as any
59
+ const label = screenRoute.options?.tabBarLabel || screenRoute.options?.title || (route.fullPath === '/' ? 'Home' : route.fullPath)
60
+ const icon = screenRoute.options?.tabBarIcon
61
+
62
+ return (
63
+ <Button
64
+ key={route.path}
65
+ variant={isActive ? 'contained' : 'outlined'}
66
+ intent={isActive ? 'primary' : undefined}
67
+ size="small"
68
+ onPress={() => onNavigate(route.path)}
69
+ style={{
70
+ flexDirection: 'row',
71
+ alignItems: 'center',
72
+ gap: 6
73
+ }}
74
+ >
75
+ {icon && typeof icon === 'string' && (
76
+ <Icon
77
+ name={icon as any}
78
+ size="sm"
79
+ color={isActive ? 'white' : 'primary'}
80
+ />
81
+ )}
82
+ <Text
83
+ size="small"
84
+ color={isActive ? 'white' : 'primary'}
85
+ weight={isActive ? 'semibold' : 'medium'}
86
+ >
87
+ {label}
88
+ </Text>
89
+ </Button>
90
+ )
91
+ })}
92
+ </View>
93
+ </View>
94
+
95
+ {/* Content Area */}
96
+ <View style={{ flex: 1, padding: 20 }}>
97
+ <ContentComponent />
98
+ </View>
99
+ </View>
100
+ )
101
+ }
@@ -1,2 +1,5 @@
1
1
  export * from './GeneralLayout';
2
- export * from './TabBarLayout';
2
+ export * from './TabBarLayout';
3
+ export { DefaultTabLayout } from './DefaultTabLayout'
4
+ export { DefaultStackLayout } from './DefaultStackLayout'
5
+ export type { TabLayoutProps, StackLayoutProps, TabLayoutComponent, StackLayoutComponent } from '../routing/types'