@umituz/react-native-design-system 4.27.35 → 4.28.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "Universal design system for React Native apps
|
|
3
|
+
"version": "4.28.0",
|
|
4
|
+
"description": "Universal design system for React Native apps with safe navigation hooks that work outside NavigationContainer",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -1,19 +1,42 @@
|
|
|
1
|
-
import { useFocusEffect } from "@react-navigation/native";
|
|
1
|
+
import { useFocusEffect as useRNFocusEffect } from "@react-navigation/native";
|
|
2
|
+
import { useEffect } from "react";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* useAppFocusEffect Hook
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
7
|
+
* Safe wrapper around React Navigation's useFocusEffect.
|
|
8
|
+
* Only runs the effect if navigation is ready.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* @param effect - Callback to run when screen is focused
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* useAppFocusEffect(
|
|
15
|
+
* useCallback(() => {
|
|
16
|
+
* // Runs when screen is focused
|
|
17
|
+
* return () => {
|
|
18
|
+
* // Cleanup when screen is unfocused
|
|
19
|
+
* };
|
|
20
|
+
* }, [dependency])
|
|
21
|
+
* );
|
|
22
|
+
* ```
|
|
16
23
|
*/
|
|
17
24
|
export function useAppFocusEffect(effect: () => void | (() => void)): void {
|
|
18
|
-
|
|
25
|
+
try {
|
|
26
|
+
// Try to use React Navigation's useFocusEffect
|
|
27
|
+
useRNFocusEffect(effect);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
// Navigation not ready - run effect once and cleanup
|
|
30
|
+
if (__DEV__) {
|
|
31
|
+
console.warn('[useAppFocusEffect] Navigation not ready. Running effect once instead.');
|
|
32
|
+
}
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const cleanup = effect();
|
|
35
|
+
return () => {
|
|
36
|
+
if (typeof cleanup === 'function') {
|
|
37
|
+
cleanup();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}, [effect]);
|
|
41
|
+
}
|
|
19
42
|
}
|
|
@@ -1,11 +1,28 @@
|
|
|
1
|
-
import { useIsFocused } from "@react-navigation/native";
|
|
1
|
+
import { useIsFocused as useRNIsFocused } from "@react-navigation/native";
|
|
2
|
+
import { useMemo } from "react";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* useAppIsFocused Hook
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
7
|
+
* Safe wrapper around React Navigation's useIsFocused.
|
|
8
|
+
* Returns false if called outside NavigationContainer.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const isFocused = useAppIsFocused();
|
|
13
|
+
* if (isFocused) {
|
|
14
|
+
* // Screen is currently focused
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
8
17
|
*/
|
|
9
18
|
export function useAppIsFocused(): boolean {
|
|
10
|
-
|
|
19
|
+
try {
|
|
20
|
+
return useRNIsFocused();
|
|
21
|
+
} catch (error) {
|
|
22
|
+
// Navigation not ready - return false
|
|
23
|
+
if (__DEV__) {
|
|
24
|
+
console.warn('[useAppIsFocused] Navigation not ready. Returning false.');
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
11
28
|
}
|
|
@@ -16,6 +16,55 @@ export interface AppNavigationResult {
|
|
|
16
16
|
canGoBack: () => boolean;
|
|
17
17
|
getState: () => ReturnType<NavigationProp<ParamListBase>["getState"]>;
|
|
18
18
|
getParent: () => NavigationProp<ParamListBase> | undefined;
|
|
19
|
+
/** Whether navigation is available (inside NavigationContainer) */
|
|
20
|
+
isReady: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates a no-op navigation object for use outside NavigationContainer
|
|
25
|
+
*/
|
|
26
|
+
function createNoOpNavigation(): AppNavigationResult {
|
|
27
|
+
return {
|
|
28
|
+
navigate: () => {
|
|
29
|
+
if (__DEV__) {
|
|
30
|
+
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
push: () => {
|
|
34
|
+
if (__DEV__) {
|
|
35
|
+
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
goBack: () => {
|
|
39
|
+
if (__DEV__) {
|
|
40
|
+
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
reset: () => {
|
|
44
|
+
if (__DEV__) {
|
|
45
|
+
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
replace: () => {
|
|
49
|
+
if (__DEV__) {
|
|
50
|
+
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
pop: () => {
|
|
54
|
+
if (__DEV__) {
|
|
55
|
+
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
popToTop: () => {
|
|
59
|
+
if (__DEV__) {
|
|
60
|
+
console.warn('[useAppNavigation] Navigation not ready. Component must be inside NavigationContainer.');
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
canGoBack: () => false,
|
|
64
|
+
getState: () => ({ key: 'root', index: 0, routeNames: [], history: [], routes: [], type: 'nav', stale: false }),
|
|
65
|
+
getParent: () => undefined,
|
|
66
|
+
isReady: false,
|
|
67
|
+
};
|
|
19
68
|
}
|
|
20
69
|
|
|
21
70
|
/**
|
|
@@ -23,82 +72,93 @@ export interface AppNavigationResult {
|
|
|
23
72
|
*
|
|
24
73
|
* Clean navigation API without complex type casting.
|
|
25
74
|
* Uses navigation.navigate() directly for proper nested navigator support.
|
|
75
|
+
*
|
|
76
|
+
* Safe to use outside NavigationContainer - returns no-op functions.
|
|
77
|
+
*
|
|
26
78
|
* Use: const navigation = useAppNavigation();
|
|
27
|
-
* navigation.
|
|
79
|
+
* if (navigation.isReady) {
|
|
80
|
+
* navigation.navigate("ScreenName", { param: value });
|
|
81
|
+
* }
|
|
28
82
|
*/
|
|
29
83
|
export function useAppNavigation(): AppNavigationResult {
|
|
30
|
-
|
|
84
|
+
try {
|
|
85
|
+
const navigation = useNavigation<NavigationProp<ParamListBase>>();
|
|
31
86
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
87
|
+
const navigate = useCallback(
|
|
88
|
+
(screen: string, params?: Record<string, unknown>) => {
|
|
89
|
+
// Dynamic navigation: use CommonActions for type-safe arbitrary screen navigation
|
|
90
|
+
navigation.dispatch(
|
|
91
|
+
CommonActions.navigate({
|
|
92
|
+
name: screen,
|
|
93
|
+
params,
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
},
|
|
97
|
+
[navigation]
|
|
98
|
+
);
|
|
44
99
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
100
|
+
const push = useCallback(
|
|
101
|
+
(screen: string, params?: Record<string, unknown>) => {
|
|
102
|
+
navigation.dispatch(StackActions.push(screen, params));
|
|
103
|
+
},
|
|
104
|
+
[navigation]
|
|
105
|
+
);
|
|
51
106
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
107
|
+
const goBack = useCallback(() => {
|
|
108
|
+
if (navigation.canGoBack()) {
|
|
109
|
+
navigation.goBack();
|
|
110
|
+
}
|
|
111
|
+
}, [navigation]);
|
|
57
112
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
113
|
+
const reset = useCallback(
|
|
114
|
+
(screen: string, params?: Record<string, unknown>) => {
|
|
115
|
+
navigation.reset({ index: 0, routes: [{ name: screen, params }] });
|
|
116
|
+
},
|
|
117
|
+
[navigation]
|
|
118
|
+
);
|
|
64
119
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
120
|
+
const replace = useCallback(
|
|
121
|
+
(screen: string, params?: Record<string, unknown>) => {
|
|
122
|
+
navigation.dispatch(StackActions.replace(screen, params));
|
|
123
|
+
},
|
|
124
|
+
[navigation]
|
|
125
|
+
);
|
|
71
126
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
127
|
+
const pop = useCallback(
|
|
128
|
+
(count = 1) => {
|
|
129
|
+
navigation.dispatch(StackActions.pop(count));
|
|
130
|
+
},
|
|
131
|
+
[navigation]
|
|
132
|
+
);
|
|
78
133
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
134
|
+
const popToTop = useCallback(() => {
|
|
135
|
+
navigation.dispatch(StackActions.popToTop());
|
|
136
|
+
}, [navigation]);
|
|
82
137
|
|
|
83
|
-
|
|
138
|
+
const canGoBack = useCallback(() => navigation.canGoBack(), [navigation]);
|
|
84
139
|
|
|
85
|
-
|
|
140
|
+
const getState = useCallback(() => navigation.getState(), [navigation]);
|
|
86
141
|
|
|
87
|
-
|
|
142
|
+
const getParent = useCallback(() => navigation.getParent(), [navigation]);
|
|
88
143
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
144
|
+
return useMemo(
|
|
145
|
+
() => ({
|
|
146
|
+
navigate,
|
|
147
|
+
push,
|
|
148
|
+
goBack,
|
|
149
|
+
reset,
|
|
150
|
+
replace,
|
|
151
|
+
pop,
|
|
152
|
+
popToTop,
|
|
153
|
+
canGoBack,
|
|
154
|
+
getState,
|
|
155
|
+
getParent,
|
|
156
|
+
isReady: true,
|
|
157
|
+
}),
|
|
158
|
+
[navigate, push, goBack, reset, replace, pop, popToTop, canGoBack, getState, getParent]
|
|
159
|
+
);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
// Navigation not ready - return no-op navigation
|
|
162
|
+
return createNoOpNavigation();
|
|
163
|
+
}
|
|
104
164
|
}
|
|
@@ -1,14 +1,50 @@
|
|
|
1
1
|
import { useRoute } from "@react-navigation/native";
|
|
2
2
|
import type { RouteProp, ParamListBase } from "@react-navigation/native";
|
|
3
|
+
import { useMemo } from "react";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* useAppRoute Hook
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* Safe wrapper around React Navigation's useRoute hook.
|
|
9
|
+
* Returns empty route params if called outside NavigationContainer.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const route = useAppRoute();
|
|
14
|
+
* if (route.isReady) {
|
|
15
|
+
* const params = route.params;
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
9
18
|
*/
|
|
10
|
-
export function useAppRoute<
|
|
11
|
-
|
|
19
|
+
export function useAppRoute<
|
|
20
|
+
T extends ParamListBase = ParamListBase,
|
|
21
|
+
RouteName extends keyof T = keyof T
|
|
22
|
+
>(): RouteProp<T, RouteName> & { isReady: boolean } {
|
|
23
|
+
try {
|
|
24
|
+
const route = useRoute<RouteProp<T, RouteName>>();
|
|
25
|
+
return useMemo(
|
|
26
|
+
() => ({
|
|
27
|
+
...route,
|
|
28
|
+
isReady: true,
|
|
29
|
+
}),
|
|
30
|
+
[route]
|
|
31
|
+
);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
// Route not ready - return empty route
|
|
34
|
+
if (__DEV__) {
|
|
35
|
+
console.warn('[useAppRoute] Route not ready. Component must be inside NavigationContainer.');
|
|
36
|
+
}
|
|
37
|
+
return useMemo(
|
|
38
|
+
() => ({
|
|
39
|
+
key: '',
|
|
40
|
+
name: '' as RouteName,
|
|
41
|
+
params: undefined,
|
|
42
|
+
path: undefined,
|
|
43
|
+
isReady: false,
|
|
44
|
+
} as RouteProp<T, RouteName> & { isReady: boolean }),
|
|
45
|
+
[]
|
|
46
|
+
);
|
|
47
|
+
}
|
|
12
48
|
}
|
|
13
49
|
|
|
14
50
|
export type { RouteProp };
|