@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.27.35",
4
- "description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, onboarding, and loading utilities - TanStack persistence and expo-image-manipulator now lazy loaded",
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
- * Wrapper around React Navigation's useFocusEffect.
7
- * Pass a useCallback-wrapped effect to avoid re-running on every render.
7
+ * Safe wrapper around React Navigation's useFocusEffect.
8
+ * Only runs the effect if navigation is ready.
8
9
  *
9
- * Usage:
10
- * useAppFocusEffect(
11
- * useCallback(() => {
12
- * doSomething();
13
- * return () => cleanup();
14
- * }, [deps])
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
- useFocusEffect(effect);
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
- * A wrapper around React Navigation's useIsFocused hook.
7
- * Standardizes API usage across all packages and apps.
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
- return useIsFocused();
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.navigate("ScreenName", { param: value });
79
+ * if (navigation.isReady) {
80
+ * navigation.navigate("ScreenName", { param: value });
81
+ * }
28
82
  */
29
83
  export function useAppNavigation(): AppNavigationResult {
30
- const navigation = useNavigation<NavigationProp<ParamListBase>>();
84
+ try {
85
+ const navigation = useNavigation<NavigationProp<ParamListBase>>();
31
86
 
32
- const navigate = useCallback(
33
- (screen: string, params?: Record<string, unknown>) => {
34
- // Dynamic navigation: use CommonActions for type-safe arbitrary screen navigation
35
- navigation.dispatch(
36
- CommonActions.navigate({
37
- name: screen,
38
- params,
39
- })
40
- );
41
- },
42
- [navigation]
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
- const push = useCallback(
46
- (screen: string, params?: Record<string, unknown>) => {
47
- navigation.dispatch(StackActions.push(screen, params));
48
- },
49
- [navigation]
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
- const goBack = useCallback(() => {
53
- if (navigation.canGoBack()) {
54
- navigation.goBack();
55
- }
56
- }, [navigation]);
107
+ const goBack = useCallback(() => {
108
+ if (navigation.canGoBack()) {
109
+ navigation.goBack();
110
+ }
111
+ }, [navigation]);
57
112
 
58
- const reset = useCallback(
59
- (screen: string, params?: Record<string, unknown>) => {
60
- navigation.reset({ index: 0, routes: [{ name: screen, params }] });
61
- },
62
- [navigation]
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
- const replace = useCallback(
66
- (screen: string, params?: Record<string, unknown>) => {
67
- navigation.dispatch(StackActions.replace(screen, params));
68
- },
69
- [navigation]
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
- const pop = useCallback(
73
- (count = 1) => {
74
- navigation.dispatch(StackActions.pop(count));
75
- },
76
- [navigation]
77
- );
127
+ const pop = useCallback(
128
+ (count = 1) => {
129
+ navigation.dispatch(StackActions.pop(count));
130
+ },
131
+ [navigation]
132
+ );
78
133
 
79
- const popToTop = useCallback(() => {
80
- navigation.dispatch(StackActions.popToTop());
81
- }, [navigation]);
134
+ const popToTop = useCallback(() => {
135
+ navigation.dispatch(StackActions.popToTop());
136
+ }, [navigation]);
82
137
 
83
- const canGoBack = useCallback(() => navigation.canGoBack(), [navigation]);
138
+ const canGoBack = useCallback(() => navigation.canGoBack(), [navigation]);
84
139
 
85
- const getState = useCallback(() => navigation.getState(), [navigation]);
140
+ const getState = useCallback(() => navigation.getState(), [navigation]);
86
141
 
87
- const getParent = useCallback(() => navigation.getParent(), [navigation]);
142
+ const getParent = useCallback(() => navigation.getParent(), [navigation]);
88
143
 
89
- return useMemo(
90
- () => ({
91
- navigate,
92
- push,
93
- goBack,
94
- reset,
95
- replace,
96
- pop,
97
- popToTop,
98
- canGoBack,
99
- getState,
100
- getParent,
101
- }),
102
- [navigate, push, goBack, reset, replace, pop, popToTop, canGoBack, getState, getParent]
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
- * A wrapper around React Navigation's useRoute hook.
8
- * Standardizes route usage across all packages and apps.
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<T extends ParamListBase, RouteName extends keyof T = string>(): RouteProp<T, RouteName> {
11
- return useRoute<RouteProp<T, RouteName>>();
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 };