@idealyst/navigation 1.0.83 → 1.0.84
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 +10 -3
- package/src/context/NavigatorContext.native.tsx +37 -6
- package/src/context/NavigatorContext.web.tsx +3 -1
- package/src/context/types.ts +2 -0
- package/src/examples/CustomStackLayout.tsx +1 -3
- package/src/examples/CustomTabLayout.tsx +6 -5
- package/src/examples/ExampleNavigationRouter.tsx +111 -0
- package/src/examples/ExampleSearchDialog.tsx +134 -0
- package/src/examples/ExampleSidebar.tsx +134 -0
- package/src/examples/ExampleWebLayout.tsx +107 -0
- package/src/examples/HeaderRight.tsx +27 -0
- package/src/examples/index.ts +3 -5
- package/src/examples/unistyles.ts +6 -12
- package/src/index.native.ts +4 -1
- package/src/layouts/DefaultStackLayout.tsx +5 -3
- package/src/layouts/DefaultTabLayout.tsx +10 -5
- package/src/routing/DrawerContentWrapper.native.tsx +25 -0
- package/src/routing/HeaderWrapper.native.tsx +19 -0
- package/src/routing/router.native.tsx +133 -23
- package/src/routing/router.web.tsx +1 -2
- package/src/routing/types.ts +40 -12
- package/CLAUDE.md +0 -417
- package/LLM-ACCESS-GUIDE.md +0 -166
- package/src/examples/ExampleDrawerRouter.tsx +0 -196
- package/src/examples/ExampleHybridRouter.tsx +0 -62
- package/src/examples/ExampleStackRouter.tsx +0 -276
- package/src/examples/ExampleTabRouter.tsx +0 -318
- package/src/examples/README.md +0 -394
- package/src/examples/highContrastThemes.ts +0 -583
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/navigation",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.84",
|
|
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.0.
|
|
47
|
-
"@idealyst/theme": "^1.0.
|
|
46
|
+
"@idealyst/components": "^1.0.84",
|
|
47
|
+
"@idealyst/theme": "^1.0.84",
|
|
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,8 +60,15 @@
|
|
|
60
60
|
"react-router-dom": ">=6.0.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
+
"@idealyst/components": "^1.0.84",
|
|
64
|
+
"@idealyst/datagrid": "^1.0.83",
|
|
65
|
+
"@idealyst/datepicker": "^1.0.83",
|
|
66
|
+
"@idealyst/theme": "^1.0.84",
|
|
63
67
|
"@types/react": "^19.1.8",
|
|
64
68
|
"@types/react-dom": "^19.1.6",
|
|
69
|
+
"react": "^19.1.0",
|
|
70
|
+
"react-native": "^0.80.1",
|
|
71
|
+
"react-native-unistyles": "^3.0.10",
|
|
65
72
|
"react-router": "7.9.1",
|
|
66
73
|
"typescript": "^5.0.0"
|
|
67
74
|
},
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import React, { createContext, memo, useContext, useMemo } from 'react';
|
|
1
|
+
import React, { createContext, memo, use, useContext, useMemo } from 'react';
|
|
2
2
|
import { NavigateParams, NavigatorProviderProps, NavigatorContextValue } from './types';
|
|
3
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
7
|
const NavigatorContext = createContext<NavigatorContextValue>({
|
|
8
|
+
route: undefined,
|
|
9
|
+
navigate: () => {},
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const DrawerNavigatorContext = createContext<NavigatorContextValue>({
|
|
13
|
+
route: undefined,
|
|
8
14
|
navigate: () => {},
|
|
9
15
|
});
|
|
10
16
|
|
|
@@ -77,7 +83,7 @@ const parseParameterizedPath = (path: string, rootRoute: any): { routeName: stri
|
|
|
77
83
|
const UnwrappedNavigatorProvider = ({ route }: NavigatorProviderProps) => {
|
|
78
84
|
|
|
79
85
|
const navigation = useNavigation();
|
|
80
|
-
|
|
86
|
+
|
|
81
87
|
const navigate = (params: NavigateParams) => {
|
|
82
88
|
// Parse parameterized path for mobile
|
|
83
89
|
const parsed = parseParameterizedPath(params.path, route);
|
|
@@ -94,9 +100,10 @@ const UnwrappedNavigatorProvider = ({ route }: NavigatorProviderProps) => {
|
|
|
94
100
|
// Memoize the navigator to prevent unnecessary re-renders
|
|
95
101
|
return memo(buildNavigator(route));
|
|
96
102
|
}, [route]);
|
|
97
|
-
|
|
103
|
+
|
|
98
104
|
return (
|
|
99
|
-
<NavigatorContext.Provider value={{
|
|
105
|
+
<NavigatorContext.Provider value={{
|
|
106
|
+
route,
|
|
100
107
|
navigate,
|
|
101
108
|
}}>
|
|
102
109
|
<RouteComponent />
|
|
@@ -116,9 +123,33 @@ const NavigatorProvider = ({ route }: NavigatorProviderProps) => {
|
|
|
116
123
|
)
|
|
117
124
|
};
|
|
118
125
|
|
|
119
|
-
|
|
126
|
+
const DrawerNavigatorProvider = ({ navigation, route, children }: { navigation: any, route: any, children: React.ReactNode }) => {
|
|
127
|
+
|
|
128
|
+
const navigate = (params: NavigateParams) => {
|
|
129
|
+
// Parse parameterized path for mobile
|
|
130
|
+
const parsed = parseParameterizedPath(params.path, route);
|
|
131
|
+
if (parsed) {
|
|
132
|
+
// Navigate to the pattern route with extracted parameters
|
|
133
|
+
navigation.navigate(parsed.routeName as never, parsed.params as never);
|
|
134
|
+
} else {
|
|
135
|
+
// Fallback to direct navigation
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<DrawerNavigatorContext.Provider value={{ navigate, route }}>
|
|
141
|
+
{children}
|
|
142
|
+
</DrawerNavigatorContext.Provider>
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export { NavigatorProvider, DrawerNavigatorProvider };
|
|
120
147
|
|
|
121
148
|
|
|
122
149
|
export const useNavigator = () => {
|
|
123
|
-
|
|
150
|
+
const drawerContext = useContext(DrawerNavigatorContext);
|
|
151
|
+
if (!drawerContext) {
|
|
152
|
+
return useContext(NavigatorContext)
|
|
153
|
+
}
|
|
154
|
+
return drawerContext;
|
|
124
155
|
};
|
|
@@ -5,6 +5,7 @@ import { buildNavigator } from '../routing';
|
|
|
5
5
|
|
|
6
6
|
const NavigatorContext = createContext<NavigatorContextValue>({
|
|
7
7
|
navigate: () => {},
|
|
8
|
+
route: undefined,
|
|
8
9
|
});
|
|
9
10
|
|
|
10
11
|
export const NavigatorProvider = ({
|
|
@@ -40,7 +41,8 @@ export const NavigatorProvider = ({
|
|
|
40
41
|
}, [route]);
|
|
41
42
|
|
|
42
43
|
return (
|
|
43
|
-
<NavigatorContext.Provider value={{
|
|
44
|
+
<NavigatorContext.Provider value={{
|
|
45
|
+
route,
|
|
44
46
|
navigate: navigateFunction,
|
|
45
47
|
}}>
|
|
46
48
|
<RouteComponent />
|
package/src/context/types.ts
CHANGED
|
@@ -10,11 +10,13 @@ export type NavigateParams = {
|
|
|
10
10
|
|
|
11
11
|
export type NavigatorProviderProps = {
|
|
12
12
|
route: NavigatorParam;
|
|
13
|
+
_overrideNavigation?: any; // Used in the drawer navigator which has to provide its own navigation object
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Context value that includes navigation function and current route parameters
|
|
17
18
|
*/
|
|
18
19
|
export type NavigatorContextValue = {
|
|
20
|
+
route: NavigatorParam | undefined;
|
|
19
21
|
navigate: (params: NavigateParams) => void;
|
|
20
22
|
};
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { Button, Text, View } from "@idealyst/components";
|
|
2
|
-
import { StackLayoutProps } from "
|
|
2
|
+
import { StackLayoutProps } from "../routing/types";
|
|
3
3
|
import React, { useMemo } from "react";
|
|
4
4
|
|
|
5
5
|
export default function CustomStackLayout({
|
|
6
6
|
routes,
|
|
7
7
|
options,
|
|
8
|
-
ContentComponent,
|
|
9
|
-
onNavigate,
|
|
10
8
|
currentPath
|
|
11
9
|
}: StackLayoutProps) {
|
|
12
10
|
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Button, View, Text, Pressable } from "@idealyst/components";
|
|
3
|
-
import { RouteParam, TabBarScreenOptions, TabLayoutProps } from "
|
|
3
|
+
import { RouteParam, TabBarScreenOptions, TabLayoutProps } from "../routing";
|
|
4
|
+
import { useNavigator } from "../context";
|
|
4
5
|
|
|
5
6
|
export default function CustomTabLayout({
|
|
6
7
|
routes,
|
|
7
8
|
options,
|
|
8
|
-
ContentComponent,
|
|
9
|
-
onNavigate,
|
|
10
9
|
currentPath
|
|
11
10
|
}: TabLayoutProps) {
|
|
12
|
-
|
|
11
|
+
|
|
12
|
+
const navigator = useNavigator();
|
|
13
|
+
|
|
13
14
|
return (
|
|
14
15
|
<View>
|
|
15
16
|
<View>
|
|
@@ -28,7 +29,7 @@ export default function CustomTabLayout({
|
|
|
28
29
|
<TabButton
|
|
29
30
|
key={route.path}
|
|
30
31
|
route={route}
|
|
31
|
-
onNavigate={
|
|
32
|
+
onNavigate={(path) => navigator.navigate({ path: route.fullPath })}
|
|
32
33
|
currentPath={currentPath}
|
|
33
34
|
/>
|
|
34
35
|
))}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AvatarExamples, BadgeExamples, ButtonExamples, CardExamples, CheckboxExamples, DialogExamples, DividerExamples, IconExamples, InputExamples, PopoverExamples, ScreenExamples, SelectExamples, SliderExamples, SVGImageExamples, TextExamples, ViewExamples, ThemeExtensionExamples, SwitchExamples, RadioButtonExamples, ProgressExamples, TextAreaExamples, TabBarExamples, TooltipExamples, AccordionExamples, ListExamples, TableExamples, MenuExamples, ImageExamples, VideoExamples, AlertExamples, SkeletonExamples, ChipExamples, BreadcrumbExamples } from '@idealyst/components/examples';
|
|
3
|
+
import { DataGridShowcase } from '@idealyst/datagrid/examples';
|
|
4
|
+
import { DatePickerExamples } from '@idealyst/datepicker/examples';
|
|
5
|
+
import { Text, View, Card, Screen } from '@idealyst/components';
|
|
6
|
+
import { NavigatorParam, RouteParam } from '../routing';
|
|
7
|
+
import { ExampleWebLayout } from './ExampleWebLayout';
|
|
8
|
+
import ExampleSidebar from './ExampleSidebar';
|
|
9
|
+
import HeaderRight from './HeaderRight';
|
|
10
|
+
|
|
11
|
+
const HomeScreen = () => {
|
|
12
|
+
return (
|
|
13
|
+
<Screen>
|
|
14
|
+
<View spacing='lg' padding={12}>
|
|
15
|
+
<Text size="xl" weight="bold">
|
|
16
|
+
Welcome to Idealyst Components
|
|
17
|
+
</Text>
|
|
18
|
+
|
|
19
|
+
<Card>
|
|
20
|
+
<View spacing="md" style={{ padding: 16 }}>
|
|
21
|
+
<Text size="lg" weight="semibold">
|
|
22
|
+
Getting Started
|
|
23
|
+
</Text>
|
|
24
|
+
<Text>
|
|
25
|
+
Explore the component library using the sidebar navigation. Each component includes examples
|
|
26
|
+
and demonstrations of various props and configurations.
|
|
27
|
+
</Text>
|
|
28
|
+
</View>
|
|
29
|
+
</Card>
|
|
30
|
+
|
|
31
|
+
<Card>
|
|
32
|
+
<View spacing="md" style={{ padding: 16 }}>
|
|
33
|
+
<Text size="lg" weight="semibold">
|
|
34
|
+
Features
|
|
35
|
+
</Text>
|
|
36
|
+
<View spacing="sm">
|
|
37
|
+
<Text>• Cross-platform components for React and React Native</Text>
|
|
38
|
+
<Text>• Comprehensive theming system with light/dark modes</Text>
|
|
39
|
+
<Text>• High contrast accessibility support</Text>
|
|
40
|
+
<Text>• Responsive design with Unistyles</Text>
|
|
41
|
+
<Text>• Type-safe component APIs</Text>
|
|
42
|
+
</View>
|
|
43
|
+
</View>
|
|
44
|
+
</Card>
|
|
45
|
+
|
|
46
|
+
<Card>
|
|
47
|
+
<View spacing="md" style={{ padding: 16 }}>
|
|
48
|
+
<Text size="lg" weight="semibold">
|
|
49
|
+
Theme Controls
|
|
50
|
+
</Text>
|
|
51
|
+
<Text>
|
|
52
|
+
Use the theme controls in the header to switch between light/dark themes and
|
|
53
|
+
toggle high contrast mode for better accessibility.
|
|
54
|
+
</Text>
|
|
55
|
+
</View>
|
|
56
|
+
</Card>
|
|
57
|
+
</View>
|
|
58
|
+
</Screen>
|
|
59
|
+
)
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const ExampleNavigationRouter: NavigatorParam = {
|
|
63
|
+
path: "/",
|
|
64
|
+
type: 'navigator',
|
|
65
|
+
layout: 'drawer',
|
|
66
|
+
sidebarComponent: ExampleSidebar,
|
|
67
|
+
layoutComponent: ExampleWebLayout,
|
|
68
|
+
options: {
|
|
69
|
+
headerRight: HeaderRight,
|
|
70
|
+
},
|
|
71
|
+
routes: [
|
|
72
|
+
{ path: "/", type: 'screen', component: HomeScreen, options: { title: "Home" } },
|
|
73
|
+
{ path: "avatar", type: 'screen', component: AvatarExamples, options: { title: "Avatar" } },
|
|
74
|
+
{ path: "badge", type: 'screen', component: BadgeExamples, options: { title: "Badge" } },
|
|
75
|
+
{ path: "button", type: 'screen', component: ButtonExamples, options: { title: "Button" } },
|
|
76
|
+
{ path: "card", type: 'screen', component: CardExamples, options: { title: "Card" } },
|
|
77
|
+
{ path: "checkbox", type: 'screen', component: CheckboxExamples, options: { title: "Checkbox" } },
|
|
78
|
+
{ path: "divider", type: 'screen', component: DividerExamples, options: { title: "Divider" } },
|
|
79
|
+
{ path: "input", type: 'screen', component: InputExamples, options: { title: "Input" } },
|
|
80
|
+
{ path: "text", type: 'screen', component: TextExamples, options: { title: "Text" } },
|
|
81
|
+
{ path: "view", type: 'screen', component: ViewExamples, options: { title: "View" } },
|
|
82
|
+
{ path: "screen", type: 'screen', component: ScreenExamples, options: { title: "Screen" } },
|
|
83
|
+
{ path: "icon", type: 'screen', component: IconExamples, options: { title: "Icon" } },
|
|
84
|
+
{ path: "svg-image", type: 'screen', component: SVGImageExamples, options: { title: "SVG Image" } },
|
|
85
|
+
{ path: "dialog", type: 'screen', component: DialogExamples, options: { title: "Dialog" } },
|
|
86
|
+
{ path: "popover", type: 'screen', component: PopoverExamples, options: { title: "Popover" } },
|
|
87
|
+
{ path: "select", type: 'screen', component: SelectExamples, options: { title: "Select" } },
|
|
88
|
+
{ path: "slider", type: 'screen', component: SliderExamples, options: { title: "Slider" } },
|
|
89
|
+
{ path: "switch", type: 'screen', component: SwitchExamples, options: { title: "Switch" } },
|
|
90
|
+
{ path: "radio-button", type: 'screen', component: RadioButtonExamples, options: { title: "Radio Button" } },
|
|
91
|
+
{ path: "progress", type: 'screen', component: ProgressExamples, options: { title: "Progress" } },
|
|
92
|
+
{ path: "textarea", type: 'screen', component: TextAreaExamples, options: { title: "Text Area" } },
|
|
93
|
+
{ path: "tooltip", type: 'screen', component: TooltipExamples, options: { title: "Tooltip" } },
|
|
94
|
+
{ path: "accordion", type: 'screen', component: AccordionExamples, options: { title: "Accordion" } },
|
|
95
|
+
{ path: "menu", type: 'screen', component: MenuExamples, options: { title: "Menu" } },
|
|
96
|
+
{ path: "list", type: 'screen', component: ListExamples, options: { title: "List" } },
|
|
97
|
+
{ path: "table", type: 'screen', component: TableExamples, options: { title: "Table" } },
|
|
98
|
+
{ path: "tabbar", type: 'screen', component: TabBarExamples, options: { title: "Tab Bar" } },
|
|
99
|
+
{ path: "alert", type: 'screen', component: AlertExamples, options: { title: "Alert" } },
|
|
100
|
+
{ path: "skeleton", type: 'screen', component: SkeletonExamples, options: { title: "Skeleton" } },
|
|
101
|
+
{ path: "chip", type: 'screen', component: ChipExamples, options: { title: "Chip" } },
|
|
102
|
+
{ path: "breadcrumb", type: 'screen', component: BreadcrumbExamples, options: { title: "Breadcrumb" } },
|
|
103
|
+
{ path: "image", type: 'screen', component: ImageExamples, options: { title: "Image" } },
|
|
104
|
+
{ path: "video", type: 'screen', component: VideoExamples, options: { title: "Video" } },
|
|
105
|
+
{ path: "datagrid", type: 'screen', component: DataGridShowcase, options: { title: "Data Grid" } },
|
|
106
|
+
{ path: "datepicker", type: 'screen', component: DatePickerExamples, options: { title: "Date Picker" } },
|
|
107
|
+
{ path: "theme-extension", type: 'screen', component: ThemeExtensionExamples, options: { title: "Theme Extension" } },
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export default ExampleNavigationRouter;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import React, { useState, useMemo } from 'react';
|
|
2
|
+
import { Dialog, View, Text, Input, ListItem, List } from '@idealyst/components';
|
|
3
|
+
import { useNavigator } from '../context';
|
|
4
|
+
import type { IconName } from '@idealyst/components';
|
|
5
|
+
|
|
6
|
+
// Icon mapping for routes
|
|
7
|
+
const routeIcons: Record<string, IconName> = {
|
|
8
|
+
'/': 'home',
|
|
9
|
+
'button': 'gesture-tap',
|
|
10
|
+
'checkbox': 'checkbox-marked',
|
|
11
|
+
'input': 'form-textbox',
|
|
12
|
+
'radio-button': 'radiobox-marked',
|
|
13
|
+
'select': 'form-dropdown',
|
|
14
|
+
'slider': 'tune',
|
|
15
|
+
'switch': 'toggle-switch',
|
|
16
|
+
'textarea': 'note-text',
|
|
17
|
+
'accordion': 'chevron-down',
|
|
18
|
+
'alert': 'information',
|
|
19
|
+
'avatar': 'account-circle',
|
|
20
|
+
'badge': 'label',
|
|
21
|
+
'card': 'credit-card',
|
|
22
|
+
'chip': 'label',
|
|
23
|
+
'icon': 'emoticon',
|
|
24
|
+
'list': 'format-list-bulleted',
|
|
25
|
+
'skeleton': 'timer-sand-empty',
|
|
26
|
+
'text': 'format-text',
|
|
27
|
+
'divider': 'minus',
|
|
28
|
+
'screen': 'cellphone',
|
|
29
|
+
'view': 'view-compact',
|
|
30
|
+
'breadcrumb': 'chevron-right',
|
|
31
|
+
'tabbar': 'tab',
|
|
32
|
+
'dialog': 'picture-in-picture-bottom-right',
|
|
33
|
+
'menu': 'menu',
|
|
34
|
+
'popover': 'message',
|
|
35
|
+
'tooltip': 'help',
|
|
36
|
+
'progress': 'timer-sand-empty',
|
|
37
|
+
'image': 'image',
|
|
38
|
+
'svg-image': 'image',
|
|
39
|
+
'video': 'video',
|
|
40
|
+
'datagrid': 'grid',
|
|
41
|
+
'datepicker': 'calendar-today',
|
|
42
|
+
'table': 'table',
|
|
43
|
+
'theme-extension': 'palette',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Get all routes recursively
|
|
47
|
+
const getAllRoutes = (routes: any[]): Array<{ path: string; title: string; icon?: IconName }> => {
|
|
48
|
+
const result: Array<{ path: string; title: string; icon?: IconName }> = [];
|
|
49
|
+
|
|
50
|
+
routes.forEach((route) => {
|
|
51
|
+
if (route.type === 'screen' && route.options?.title) {
|
|
52
|
+
result.push({
|
|
53
|
+
path: route.path,
|
|
54
|
+
title: route.options.title,
|
|
55
|
+
icon: routeIcons[route.path],
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (route.routes) {
|
|
59
|
+
result.push(...getAllRoutes(route.routes));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
interface ExampleSearchDialogProps {
|
|
67
|
+
open: boolean;
|
|
68
|
+
onOpenChange: (open: boolean) => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default function ExampleSearchDialog({ open, onOpenChange }: ExampleSearchDialogProps) {
|
|
72
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
73
|
+
const navigator = useNavigator();
|
|
74
|
+
|
|
75
|
+
// Get all available pages
|
|
76
|
+
const allPages = useMemo(() => {
|
|
77
|
+
if (!navigator.route?.routes) return [];
|
|
78
|
+
return getAllRoutes(navigator.route.routes);
|
|
79
|
+
}, [navigator.route]);
|
|
80
|
+
|
|
81
|
+
// Filter pages based on search query
|
|
82
|
+
const filteredPages = useMemo(() => {
|
|
83
|
+
if (!searchQuery.trim()) return allPages;
|
|
84
|
+
|
|
85
|
+
const query = searchQuery.toLowerCase();
|
|
86
|
+
return allPages.filter((page) =>
|
|
87
|
+
page.title.toLowerCase().includes(query)
|
|
88
|
+
);
|
|
89
|
+
}, [searchQuery, allPages]);
|
|
90
|
+
|
|
91
|
+
const handleNavigate = (path: string) => {
|
|
92
|
+
navigator.navigate({ path: `/${path}` });
|
|
93
|
+
onOpenChange(false);
|
|
94
|
+
setSearchQuery('');
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Reset search when dialog closes
|
|
98
|
+
const handleOpenChange = (newOpen: boolean) => {
|
|
99
|
+
if (!newOpen) {
|
|
100
|
+
setSearchQuery('');
|
|
101
|
+
}
|
|
102
|
+
onOpenChange(newOpen);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<Dialog open={open} onOpenChange={handleOpenChange} title='Search Pages'>
|
|
107
|
+
<View spacing='md'>
|
|
108
|
+
<Input
|
|
109
|
+
placeholder='Search for a page...'
|
|
110
|
+
value={searchQuery}
|
|
111
|
+
onChangeText={setSearchQuery}
|
|
112
|
+
leftIcon='magnify'
|
|
113
|
+
/>
|
|
114
|
+
{filteredPages.length > 0 ? (
|
|
115
|
+
<List scrollable maxHeight={400}>
|
|
116
|
+
{filteredPages.map((page) => (
|
|
117
|
+
<ListItem
|
|
118
|
+
size='sm'
|
|
119
|
+
key={page.path}
|
|
120
|
+
label={page.title}
|
|
121
|
+
leading={page.icon}
|
|
122
|
+
onPress={() => handleNavigate(page.path)}
|
|
123
|
+
/>
|
|
124
|
+
))}
|
|
125
|
+
</List>
|
|
126
|
+
) : (
|
|
127
|
+
<Text color='tertiary' style={{ textAlign: 'center', padding: 16 }}>
|
|
128
|
+
No pages found
|
|
129
|
+
</Text>
|
|
130
|
+
)}
|
|
131
|
+
</View>
|
|
132
|
+
</Dialog>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { ListItem, View, Text, Divider, Screen, List } from "@idealyst/components";
|
|
2
|
+
import { useNavigator } from "../context";
|
|
3
|
+
import type { DrawerSidebarProps } from "../routing/types";
|
|
4
|
+
|
|
5
|
+
interface ComponentGroup {
|
|
6
|
+
title: string;
|
|
7
|
+
items: Array<{
|
|
8
|
+
label: string;
|
|
9
|
+
path: string;
|
|
10
|
+
icon?: string;
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const componentGroups: ComponentGroup[] = [
|
|
15
|
+
{
|
|
16
|
+
title: 'Form',
|
|
17
|
+
items: [
|
|
18
|
+
{ label: 'Button', path: '/button', icon: 'gesture-tap' },
|
|
19
|
+
{ label: 'Checkbox', path: '/checkbox', icon: 'checkbox-marked' },
|
|
20
|
+
{ label: 'Input', path: '/input', icon: 'form-textbox' },
|
|
21
|
+
{ label: 'RadioButton', path: '/radio-button', icon: 'radiobox-marked' },
|
|
22
|
+
{ label: 'Select', path: '/select', icon: 'form-dropdown' },
|
|
23
|
+
{ label: 'Slider', path: '/slider', icon: 'tune' },
|
|
24
|
+
{ label: 'Switch', path: '/switch', icon: 'toggle-switch' },
|
|
25
|
+
{ label: 'TextArea', path: '/textarea', icon: 'note-text' },
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
title: 'Display',
|
|
30
|
+
items: [
|
|
31
|
+
{ label: 'Accordion', path: '/accordion', icon: 'chevron-down' },
|
|
32
|
+
{ label: 'Alert', path: '/alert', icon: 'information' },
|
|
33
|
+
{ label: 'Avatar', path: '/avatar', icon: 'account-circle' },
|
|
34
|
+
{ label: 'Badge', path: '/badge', icon: 'label' },
|
|
35
|
+
{ label: 'Card', path: '/card', icon: 'credit-card' },
|
|
36
|
+
{ label: 'Chip', path: '/chip', icon: 'label' },
|
|
37
|
+
{ label: 'Icon', path: '/icon', icon: 'emoticon' },
|
|
38
|
+
{ label: 'List', path: '/list', icon: 'format-list-bulleted' },
|
|
39
|
+
{ label: 'Skeleton', path: '/skeleton', icon: 'timer-sand-empty' },
|
|
40
|
+
{ label: 'Text', path: '/text', icon: 'format-text' },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
title: 'Layout',
|
|
45
|
+
items: [
|
|
46
|
+
{ label: 'Divider', path: '/divider', icon: 'minus' },
|
|
47
|
+
{ label: 'Screen', path: '/screen', icon: 'cellphone' },
|
|
48
|
+
{ label: 'View', path: '/view', icon: 'view-compact' },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
title: 'Navigation',
|
|
53
|
+
items: [
|
|
54
|
+
{ label: 'Breadcrumb', path: '/breadcrumb', icon: 'chevron-right' },
|
|
55
|
+
{ label: 'TabBar', path: '/tabbar', icon: 'tab' },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
title: 'Overlay',
|
|
60
|
+
items: [
|
|
61
|
+
{ label: 'Dialog', path: '/dialog', icon: 'picture-in-picture-bottom-right' },
|
|
62
|
+
{ label: 'Menu', path: '/menu', icon: 'menu' },
|
|
63
|
+
{ label: 'Popover', path: '/popover', icon: 'message' },
|
|
64
|
+
{ label: 'Tooltip', path: '/tooltip', icon: 'help' },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
title: 'Feedback',
|
|
69
|
+
items: [
|
|
70
|
+
{ label: 'Progress', path: '/progress', icon: 'timer-sand-empty' },
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
title: 'Media',
|
|
75
|
+
items: [
|
|
76
|
+
{ label: 'Image', path: '/image', icon: 'image' },
|
|
77
|
+
{ label: 'SVG Image', path: '/svg-image', icon: 'image' },
|
|
78
|
+
{ label: 'Video', path: '/video', icon: 'video' },
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
title: 'Data',
|
|
83
|
+
items: [
|
|
84
|
+
{ label: 'DataGrid', path: '/datagrid', icon: 'grid' },
|
|
85
|
+
{ label: 'DatePicker', path: '/datepicker', icon: 'calendar-today' },
|
|
86
|
+
{ label: 'Table', path: '/table', icon: 'table' },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
title: 'Theme',
|
|
91
|
+
items: [
|
|
92
|
+
{ label: 'Theme Extension', path: '/theme-extension', icon: 'palette' },
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
export default function ExampleSidebar({ insets }: DrawerSidebarProps) {
|
|
98
|
+
const navigator = useNavigator();
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<Screen
|
|
102
|
+
style={{
|
|
103
|
+
height: '100%',
|
|
104
|
+
padding: 16,
|
|
105
|
+
}}
|
|
106
|
+
contentInset={insets}
|
|
107
|
+
>
|
|
108
|
+
{componentGroups.map((group, groupIndex) => (
|
|
109
|
+
<List key={group.title}>
|
|
110
|
+
<Text
|
|
111
|
+
size="sm"
|
|
112
|
+
weight="bold"
|
|
113
|
+
color="secondary"
|
|
114
|
+
style={{ marginBottom: 8, marginLeft: 8 }}
|
|
115
|
+
>
|
|
116
|
+
{group.title}
|
|
117
|
+
</Text>
|
|
118
|
+
{group.items.map((item) => (
|
|
119
|
+
<ListItem
|
|
120
|
+
key={item.path}
|
|
121
|
+
label={item.label}
|
|
122
|
+
leading={item.icon}
|
|
123
|
+
size="sm"
|
|
124
|
+
onPress={() => navigator.navigate({ path: item.path, vars: {} })}
|
|
125
|
+
/>
|
|
126
|
+
))}
|
|
127
|
+
{groupIndex < componentGroups.length - 1 && (
|
|
128
|
+
<Divider spacing="sm" style={{ marginTop: 8 }} />
|
|
129
|
+
)}
|
|
130
|
+
</List>
|
|
131
|
+
))}
|
|
132
|
+
</Screen>
|
|
133
|
+
)
|
|
134
|
+
}
|