@idealyst/navigation 1.0.0

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 ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@idealyst/navigation",
3
+ "version": "1.0.0",
4
+ "description": "Cross-platform navigation library for React and React Native",
5
+ "main": "src/index.ts",
6
+ "module": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/your-username/idealyst-framework.git",
11
+ "directory": "packages/navigation"
12
+ },
13
+ "author": "Nicholas Mercier <nicho.mercier@gmail.com>",
14
+ "license": "MIT",
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "exports": {
19
+ ".": {
20
+ "import": "./src/index.ts",
21
+ "require": "./src/index.ts",
22
+ "types": "./src/index.ts"
23
+ },
24
+ "./examples": {
25
+ "import": "./src/examples/index.ts",
26
+ "require": "./src/examples/index.ts",
27
+ "types": "./src/examples/index.ts"
28
+ },
29
+ "./examples/unistyles": {
30
+ "import": "./src/examples/unistyles.ts",
31
+ "require": "./src/examples/unistyles.ts",
32
+ "types": "./src/examples/unistyles.ts"
33
+ }
34
+ },
35
+ "scripts": {
36
+ "build": "rollup -c",
37
+ "dev": "rollup -c -w",
38
+ "prepublishOnly": "echo 'Publishing TypeScript source directly'",
39
+ "publish:npm": "npm publish"
40
+ },
41
+ "dependencies": {
42
+ "@idealyst/components": "^1.0.0"
43
+ },
44
+ "peerDependencies": {
45
+ "@react-navigation/bottom-tabs": "^7.0.0",
46
+ "@react-navigation/drawer": "^7.0.0",
47
+ "@react-navigation/native": "^7.0.0",
48
+ "@react-navigation/native-stack": "^7.0.0",
49
+ "react": ">=16.8.0",
50
+ "react-native": ">=0.60.0",
51
+ "react-native-gesture-handler": "*",
52
+ "react-native-reanimated": "*",
53
+ "react-native-safe-area-context": "*",
54
+ "react-native-screens": "*",
55
+ "react-native-unistyles": "3.0.0-rc.5",
56
+ "react-router": "^7.6.3",
57
+ "react-router-dom": "^7.6.3"
58
+ },
59
+ "devDependencies": {
60
+ "@types/react": "^19.1.8",
61
+ "@types/react-dom": "^19.1.6",
62
+ "rollup": "^3.20.0",
63
+ "rollup-plugin-typescript2": "^0.34.0",
64
+ "typescript": "^5.0.0"
65
+ },
66
+ "files": [
67
+ "src"
68
+ ],
69
+ "keywords": [
70
+ "react",
71
+ "react-native",
72
+ "navigation",
73
+ "cross-platform",
74
+ "router"
75
+ ]
76
+ }
@@ -0,0 +1,51 @@
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';
4
+ import { buildRouter } from '../routing';
5
+ import { useUnistyles } from 'react-native-unistyles';
6
+
7
+ const NavigatorContext = createContext<{
8
+ navigate: (params: NavigateParams) => void;
9
+ }>({
10
+ navigate: () => {},
11
+ });
12
+
13
+ const UnwrappedNavigatorProvider = ({ route }: NavigatorProviderProps) => {
14
+
15
+ const navigation = useNavigation();
16
+
17
+ const navigate = (params: NavigateParams) => {
18
+ console.log('navigate', params);
19
+ navigation.navigate(params.path as never);
20
+ };
21
+
22
+ const RouteComponent = useMemo(() => {
23
+ // Memoize the router to prevent unnecessary re-renders
24
+ return memo(buildRouter(route));
25
+ }, [route]);
26
+
27
+ return (
28
+ <NavigatorContext.Provider value={{ navigate }}>
29
+ <RouteComponent />
30
+ </NavigatorContext.Provider>
31
+ )
32
+ };
33
+
34
+ const NavigatorProvider = ({ route }: NavigatorProviderProps) => {
35
+ const {rt} = useUnistyles()
36
+
37
+ const isDarkMode = rt.themeName === 'dark';
38
+
39
+ return (
40
+ <NavigationContainer theme={isDarkMode ? DarkTheme : DefaultTheme}>
41
+ <UnwrappedNavigatorProvider route={route} />
42
+ </NavigationContainer>
43
+ )
44
+ };
45
+
46
+ export { NavigatorProvider };
47
+
48
+
49
+ export const useNavigator = () => {
50
+ return useContext(NavigatorContext);
51
+ };
@@ -0,0 +1,37 @@
1
+ import React, { createContext, memo, useContext, useMemo } from 'react';
2
+ import { NavigateParams, NavigatorProviderProps } from './types';
3
+ import { useNavigate } from "react-router-dom";
4
+ import { buildRouter } from '../routing';
5
+
6
+ const NavigatorContext = createContext<{
7
+ navigate: (params: NavigateParams) => void;
8
+ }>({
9
+ navigate: () => {},
10
+ });
11
+
12
+ export const NavigatorProvider = ({
13
+ route,
14
+ }: NavigatorProviderProps) => {
15
+ const routerNavigate = useNavigate();
16
+
17
+ const navigateFunction = (params: NavigateParams) => {
18
+ if (params.path) {
19
+ routerNavigate(params.path);
20
+ }
21
+ };
22
+
23
+ const RouteComponent = useMemo(() => {
24
+ // Memoize the router to prevent unnecessary re-renders
25
+ return memo(buildRouter(route));
26
+ }, [route]);
27
+
28
+ return (
29
+ <NavigatorContext.Provider value={{ navigate: navigateFunction }}>
30
+ <RouteComponent />
31
+ </NavigatorContext.Provider>
32
+ );
33
+ };
34
+
35
+ export const useNavigator = () => {
36
+ return useContext(NavigatorContext);
37
+ };
@@ -0,0 +1,2 @@
1
+ export * from './NavigatorContext.native';
2
+ export * from './types';
@@ -0,0 +1,2 @@
1
+ export * from './NavigatorContext.web';
2
+ export * from './types';
@@ -0,0 +1,2 @@
1
+ export * from './NavigatorContext.web';
2
+ export * from './types';
@@ -0,0 +1,13 @@
1
+ import { RouteParam } from "../routing";
2
+
3
+ /**
4
+ * When navigating to a new route, specify the path and the variables to be used in the route.
5
+ */
6
+ export type NavigateParams = {
7
+ path: string;
8
+ vars: Record<string, string>;
9
+ };
10
+
11
+ export type NavigatorProviderProps = {
12
+ route: RouteParam;
13
+ };
@@ -0,0 +1,159 @@
1
+ import React from 'react';
2
+ import { AvatarExamples, BadgeExamples, ButtonExamples, CardExamples, CheckboxExamples, DividerExamples, IconExamples, InputExamples, TextExamples, ViewExamples, ThemeExtensionExamples } from "../../../components/src/examples";
3
+ import { Screen, Text, View, Button } from "../../../components/src";
4
+ import { UnistylesRuntime } from 'react-native-unistyles';
5
+ import { RouteParam } from '../routing';
6
+ import { getNextTheme, getThemeDisplayName, isHighContrastTheme } from './unistyles';
7
+
8
+ const HomeDrawerScreen = () => {
9
+ const currentTheme = UnistylesRuntime.themeName || 'light';
10
+
11
+ const cycleTheme = () => {
12
+ const nextTheme = getNextTheme(currentTheme);
13
+ UnistylesRuntime.setTheme(nextTheme as any);
14
+ console.log('Theme changed to:', nextTheme);
15
+ };
16
+
17
+ const toggleHighContrast = () => {
18
+ const currentTheme = UnistylesRuntime.themeName || 'light';
19
+ let newTheme: string;
20
+
21
+ if (isHighContrastTheme(currentTheme)) {
22
+ // Switch to standard variant
23
+ newTheme = currentTheme.includes('dark') ? 'dark' : 'light';
24
+ } else {
25
+ // Switch to high contrast variant
26
+ newTheme = currentTheme.includes('dark') ? 'darkHighContrast' : 'lightHighContrast';
27
+ }
28
+
29
+ UnistylesRuntime.setTheme(newTheme as any);
30
+ console.log('Theme toggled to:', newTheme);
31
+ };
32
+
33
+ return (
34
+ <Screen>
35
+ <View style={{ gap: 16 }}>
36
+ <Text size="large" weight="bold">
37
+ Welcome to the Component Library
38
+ </Text>
39
+ <Text size="medium">
40
+ Use the drawer menu to explore different components
41
+ </Text>
42
+
43
+ <View style={{ gap: 12, padding: 16, backgroundColor: 'rgba(128, 128, 128, 0.1)', borderRadius: 8, marginTop: 8 }}>
44
+ <Text size="medium" weight="semibold">
45
+ Theme Controls
46
+ </Text>
47
+ <Text size="small">
48
+ Current Theme: {getThemeDisplayName(currentTheme)}
49
+ </Text>
50
+
51
+ <View style={{ gap: 8 }}>
52
+ <Button variant="outlined" onPress={cycleTheme}>
53
+ 🔄 Cycle Theme (Light → Dark → Light HC → Dark HC)
54
+ </Button>
55
+
56
+ <Button variant="outlined" onPress={toggleHighContrast}>
57
+ ♿ Toggle High Contrast
58
+ </Button>
59
+ </View>
60
+
61
+ {isHighContrastTheme(currentTheme) && (
62
+ <Text size="small" style={{ fontStyle: 'italic' }}>
63
+ ♿ High contrast mode is active for better accessibility
64
+ </Text>
65
+ )}
66
+ </View>
67
+ </View>
68
+ </Screen>
69
+ );
70
+ };
71
+
72
+ const AvatarDrawerScreen = () => (
73
+ <Screen>
74
+ <AvatarExamples />
75
+ </Screen>
76
+ );
77
+
78
+ const BadgeDrawerScreen = () => (
79
+ <Screen>
80
+ <BadgeExamples />
81
+ </Screen>
82
+ );
83
+
84
+ const ButtonDrawerScreen = () => (
85
+ <Screen>
86
+ <ButtonExamples />
87
+ </Screen>
88
+ );
89
+
90
+ const CardDrawerScreen = () => (
91
+ <Screen>
92
+ <CardExamples />
93
+ </Screen>
94
+ );
95
+
96
+ const CheckboxDrawerScreen = () => (
97
+ <Screen>
98
+ <CheckboxExamples />
99
+ </Screen>
100
+ );
101
+
102
+ const DividerDrawerScreen = () => (
103
+ <Screen>
104
+ <DividerExamples />
105
+ </Screen>
106
+ );
107
+
108
+ const InputDrawerScreen = () => (
109
+ <Screen>
110
+ <InputExamples />
111
+ </Screen>
112
+ );
113
+
114
+ const TextDrawerScreen = () => (
115
+ <Screen>
116
+ <TextExamples />
117
+ </Screen>
118
+ );
119
+
120
+ const ViewDrawerScreen = () => (
121
+ <Screen>
122
+ <ViewExamples />
123
+ </Screen>
124
+ );
125
+
126
+ const IconDrawerScreen = () => (
127
+ <Screen>
128
+ <IconExamples />
129
+ </Screen>
130
+ );
131
+
132
+ const ThemeExtensionDrawerScreen = () => (
133
+ <Screen>
134
+ <ThemeExtensionExamples />
135
+ </Screen>
136
+ );
137
+
138
+ const DrawerRouter: RouteParam = {
139
+ path: "/",
140
+ component: HomeDrawerScreen,
141
+ layout: {
142
+ type: "drawer",
143
+ },
144
+ routes: [
145
+ { path: "avatar", component: AvatarDrawerScreen },
146
+ { path: "badge", component: BadgeDrawerScreen },
147
+ { path: "button", component: ButtonDrawerScreen },
148
+ { path: "card", component: CardDrawerScreen },
149
+ { path: "checkbox", component: CheckboxDrawerScreen },
150
+ { path: "divider", component: DividerDrawerScreen },
151
+ { path: "input", component: InputDrawerScreen },
152
+ { path: "text", component: TextDrawerScreen },
153
+ { path: "view", component: ViewDrawerScreen },
154
+ { path: "icon", component: IconDrawerScreen },
155
+ { path: "theme-extension", component: ThemeExtensionDrawerScreen },
156
+ ],
157
+ };
158
+
159
+ export default DrawerRouter;
@@ -0,0 +1,245 @@
1
+ import React, { useState } from 'react';
2
+ import { AvatarExamples, BadgeExamples, ButtonExamples, CardExamples, CheckboxExamples, DividerExamples, IconExamples, InputExamples, ScreenExamples, TextExamples, ViewExamples, ThemeExtensionExamples } from "../../../components/src/examples";
3
+ import { Button, Divider, Screen, Text, View } from "../../../components/src";
4
+ import { useNavigator } from "../context";
5
+ import { UnistylesRuntime, StyleSheet } from 'react-native-unistyles';
6
+ import { GeneralLayout } from '../layouts/GeneralLayout';
7
+ import { RouteParam } from '../routing';
8
+ import { getNextTheme, getThemeDisplayName, isHighContrastTheme } from './unistyles';
9
+
10
+ const HomeScreen = () => {
11
+ const navigator = useNavigator();
12
+ const currentTheme = UnistylesRuntime.themeName || 'light';
13
+
14
+ const cycleTheme = () => {
15
+ const nextTheme = getNextTheme(currentTheme);
16
+ UnistylesRuntime.setTheme(nextTheme as any);
17
+ console.log('Theme changed to:', nextTheme);
18
+ };
19
+
20
+ const toggleHighContrast = () => {
21
+ const currentTheme = UnistylesRuntime.themeName || 'light';
22
+ let newTheme: string;
23
+
24
+ if (isHighContrastTheme(currentTheme)) {
25
+ // Switch to standard variant
26
+ newTheme = currentTheme.includes('dark') ? 'dark' : 'light';
27
+ } else {
28
+ // Switch to high contrast variant
29
+ newTheme = currentTheme.includes('dark') ? 'darkHighContrast' : 'lightHighContrast';
30
+ }
31
+
32
+ UnistylesRuntime.setTheme(newTheme as any);
33
+ console.log('Theme toggled to:', newTheme);
34
+ };
35
+
36
+ return (
37
+ <Screen>
38
+ <View style={{ maxWidth: 800, width: '100%', gap: 10, marginHorizontal: 'auto' }}>
39
+ {/* Theme Controls Section */}
40
+ <View style={{ gap: 12, padding: 16, backgroundColor: 'rgba(128, 128, 128, 0.1)', borderRadius: 8 }}>
41
+ <Text size="medium" weight="bold">
42
+ Theme Controls
43
+ </Text>
44
+ <Text size="small">
45
+ Current Theme: {getThemeDisplayName(currentTheme)}
46
+ </Text>
47
+
48
+ <View style={{ flexDirection: 'row', gap: 12, flexWrap: 'wrap' }}>
49
+ <Button
50
+ variant="outlined"
51
+ intent="primary"
52
+ size="medium"
53
+ onPress={cycleTheme}
54
+ >
55
+ 🔄 Cycle Theme
56
+ </Button>
57
+
58
+ <Button
59
+ variant="outlined"
60
+ intent="neutral"
61
+ size="medium"
62
+ onPress={toggleHighContrast}
63
+ >
64
+ ♿ Toggle High Contrast
65
+ </Button>
66
+ </View>
67
+
68
+ {isHighContrastTheme(currentTheme) && (
69
+ <Text size="small" style={{ fontStyle: 'italic', color: '#666' }}>
70
+ ♿ High contrast mode is active for better accessibility
71
+ </Text>
72
+ )}
73
+ </View>
74
+
75
+ {/* Component Navigation Buttons */}
76
+ <View style={{ gap: 10 }}>
77
+ <Button
78
+ onPress={() => {
79
+ navigator.navigate({
80
+ path: "/avatar",
81
+ vars: {},
82
+ });
83
+ }}>
84
+ Avatar
85
+ </Button>
86
+ <Button
87
+ onPress={() => {
88
+ navigator.navigate({
89
+ path: "/badge",
90
+ vars: {},
91
+ });
92
+ }}>
93
+ Badge
94
+ </Button>
95
+ <Button
96
+ onPress={() => {
97
+ navigator.navigate({
98
+ path: "/button",
99
+ vars: {},
100
+ });
101
+ }}>
102
+ Button
103
+ </Button>
104
+ <Button
105
+ onPress={() => {
106
+ navigator.navigate({
107
+ path: "/card",
108
+ vars: {},
109
+ });
110
+ }}>
111
+ Card
112
+ </Button>
113
+ <Button
114
+ onPress={() => {
115
+ navigator.navigate({
116
+ path: "/checkbox",
117
+ vars: {},
118
+ });
119
+ }}>
120
+ Checkbox
121
+ </Button>
122
+ <Button
123
+ onPress={() => {
124
+ navigator.navigate({
125
+ path: "/divider",
126
+ vars: {},
127
+ });
128
+ }}>
129
+ Divider
130
+ </Button>
131
+ <Button
132
+ onPress={() => {
133
+ navigator.navigate({
134
+ path: "/input",
135
+ vars: {},
136
+ });
137
+ }}>
138
+ Input
139
+ </Button>
140
+ <Button
141
+ onPress={() => {
142
+ navigator.navigate({
143
+ path: "/text",
144
+ vars: {},
145
+ });
146
+ }}>
147
+ Text
148
+ </Button>
149
+ <Button
150
+ onPress={() => {
151
+ navigator.navigate({
152
+ path: "/view",
153
+ vars: {},
154
+ });
155
+ }}>
156
+ View
157
+ </Button>
158
+ <Button
159
+ onPress={() => {
160
+ navigator.navigate({
161
+ path: "/screen",
162
+ vars: {},
163
+ });
164
+ }}>
165
+ Screen
166
+ </Button>
167
+ <Button
168
+ onPress={() => {
169
+ navigator.navigate({
170
+ path: "/icon",
171
+ vars: {},
172
+ });
173
+ }}>
174
+ Icon
175
+ </Button>
176
+
177
+ <Divider spacing="medium" />
178
+ <Text size="small" weight="semibold" color="secondary">Theme System</Text>
179
+ <Button
180
+ variant="outlined"
181
+ intent="success"
182
+ onPress={() => {
183
+ navigator.navigate({
184
+ path: "/theme-extension",
185
+ vars: {},
186
+ });
187
+ }}>
188
+ 🎨 Theme Extension Demo
189
+ </Button>
190
+ </View>
191
+ </View>
192
+ </Screen>
193
+ )
194
+ };
195
+
196
+ const WrappedGeneralLayout = (props: any) => {
197
+
198
+ const navigator = useNavigator();
199
+
200
+ return (
201
+ <GeneralLayout
202
+ header={{
203
+ content: <Text style={{ marginHorizontal: 'auto' }} color="inverse">Stack Router Demo App</Text>,
204
+ }}
205
+ sidebar={{
206
+ enabled: true,
207
+ collapsible: true,
208
+ position: 'left',
209
+ initiallyExpanded: false,
210
+ content: <Button onPress={() => {
211
+ navigator.navigate({
212
+ path: "/",
213
+ vars: {},
214
+ });
215
+ }}>Home</Button>,
216
+ }}
217
+ {...props}>
218
+ </GeneralLayout>
219
+ )
220
+ }
221
+
222
+ const StackRouter: RouteParam = {
223
+ path: "/",
224
+ component: HomeScreen,
225
+ layout: {
226
+ type: "stack",
227
+ component: WrappedGeneralLayout,
228
+ },
229
+ routes: [
230
+ { path: "avatar", component: AvatarExamples},
231
+ { path: "badge", component: BadgeExamples},
232
+ { path: "button", component: ButtonExamples},
233
+ { path: "card", component: CardExamples},
234
+ { path: "checkbox", component: CheckboxExamples},
235
+ { path: "divider", component: DividerExamples},
236
+ { path: "input", component: InputExamples},
237
+ { path: "text", component: TextExamples},
238
+ { path: "view", component: ViewExamples},
239
+ { path: "screen", component: ScreenExamples},
240
+ { path: "icon", component: IconExamples},
241
+ { path: "theme-extension", component: ThemeExtensionExamples},
242
+ ],
243
+ };
244
+
245
+ export default StackRouter;