@umituz/react-native-design-system 2.3.14 → 2.3.16

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.
Files changed (93) hide show
  1. package/package.json +19 -2
  2. package/src/index.ts +105 -0
  3. package/src/layouts/ScreenLayout/ScreenLayout.example.tsx +2 -2
  4. package/src/layouts/ScreenLayout/ScreenLayout.tsx +1 -1
  5. package/src/molecules/animation/core/AnimationCore.ts +29 -0
  6. package/src/molecules/animation/domain/entities/Animation.ts +81 -0
  7. package/src/molecules/animation/domain/entities/Fireworks.ts +44 -0
  8. package/src/molecules/animation/domain/entities/Theme.ts +76 -0
  9. package/src/molecules/animation/index.ts +146 -0
  10. package/src/molecules/animation/infrastructure/services/AnimationConfigService.ts +35 -0
  11. package/src/molecules/animation/infrastructure/services/SpringAnimationConfigService.ts +67 -0
  12. package/src/molecules/animation/infrastructure/services/TimingAnimationConfigService.ts +57 -0
  13. package/src/molecules/animation/infrastructure/services/__tests__/SpringAnimationConfigService.test.ts +114 -0
  14. package/src/molecules/animation/infrastructure/services/__tests__/TimingAnimationConfigService.test.ts +105 -0
  15. package/src/molecules/animation/presentation/components/Fireworks.tsx +126 -0
  16. package/src/molecules/animation/presentation/components/__tests__/Fireworks.test.tsx +189 -0
  17. package/src/molecules/animation/presentation/hooks/__tests__/useAnimation.integration.test.ts +216 -0
  18. package/src/molecules/animation/presentation/hooks/__tests__/useFireworks.test.ts +242 -0
  19. package/src/molecules/animation/presentation/hooks/__tests__/useGesture.test.ts +111 -0
  20. package/src/molecules/animation/presentation/hooks/__tests__/useSpringAnimation.test.ts +131 -0
  21. package/src/molecules/animation/presentation/hooks/__tests__/useTimingAnimation.test.ts +175 -0
  22. package/src/molecules/animation/presentation/hooks/__tests__/useTransformAnimation.test.ts +137 -0
  23. package/src/molecules/animation/presentation/hooks/useAnimation.ts +77 -0
  24. package/src/molecules/animation/presentation/hooks/useFireworks.ts +141 -0
  25. package/src/molecules/animation/presentation/hooks/useGesture.ts +61 -0
  26. package/src/molecules/animation/presentation/hooks/useGestureCreators.ts +163 -0
  27. package/src/molecules/animation/presentation/hooks/useGestureState.ts +53 -0
  28. package/src/molecules/animation/presentation/hooks/useIconAnimations.ts +119 -0
  29. package/src/molecules/animation/presentation/hooks/useModalAnimations.ts +124 -0
  30. package/src/molecules/animation/presentation/hooks/useReanimatedReady.ts +60 -0
  31. package/src/molecules/animation/presentation/hooks/useSpringAnimation.ts +69 -0
  32. package/src/molecules/animation/presentation/hooks/useTimingAnimation.ts +111 -0
  33. package/src/molecules/animation/presentation/hooks/useTransformAnimation.ts +57 -0
  34. package/src/molecules/animation/presentation/providers/AnimationThemeProvider.tsx +62 -0
  35. package/src/molecules/animation/presentation/providers/__tests__/AnimationThemeProvider.test.tsx +165 -0
  36. package/src/molecules/animation/types/global.d.ts +97 -0
  37. package/src/molecules/celebration/domain/entities/CelebrationConfig.ts +17 -0
  38. package/src/molecules/celebration/domain/entities/FireworksConfig.ts +32 -0
  39. package/src/molecules/celebration/index.ts +93 -0
  40. package/src/molecules/celebration/infrastructure/services/FireworksConfigService.ts +49 -0
  41. package/src/molecules/celebration/presentation/components/CelebrationFireworksOverlay.tsx +33 -0
  42. package/src/molecules/celebration/presentation/components/CelebrationModal.tsx +78 -0
  43. package/src/molecules/celebration/presentation/components/CelebrationModalContent.tsx +90 -0
  44. package/src/molecules/celebration/presentation/hooks/useCelebrationModalAnimation.ts +49 -0
  45. package/src/molecules/celebration/presentation/hooks/useCelebrationState.ts +45 -0
  46. package/src/molecules/celebration/presentation/styles/CelebrationModalStyles.ts +65 -0
  47. package/src/molecules/countdown/components/Countdown.tsx +128 -0
  48. package/src/molecules/countdown/components/CountdownHeader.tsx +84 -0
  49. package/src/molecules/countdown/components/TimeUnit.tsx +73 -0
  50. package/src/molecules/countdown/hooks/useCountdown.ts +107 -0
  51. package/src/molecules/countdown/index.ts +25 -0
  52. package/src/molecules/countdown/types/CountdownTypes.ts +31 -0
  53. package/src/molecules/countdown/utils/TimeCalculator.ts +46 -0
  54. package/src/molecules/emoji/domain/entities/Emoji.ts +129 -0
  55. package/src/molecules/emoji/index.ts +177 -0
  56. package/src/molecules/emoji/presentation/components/EmojiPicker.tsx +102 -0
  57. package/src/molecules/emoji/presentation/hooks/useEmojiPicker.ts +171 -0
  58. package/src/molecules/index.ts +21 -0
  59. package/src/molecules/long-press-menu/domain/entities/MenuAction.ts +37 -0
  60. package/src/molecules/long-press-menu/index.ts +16 -0
  61. package/src/molecules/navigation/StackNavigator.tsx +75 -0
  62. package/src/molecules/navigation/TabsNavigator.tsx +94 -0
  63. package/src/molecules/navigation/components/FabButton.tsx +45 -0
  64. package/src/molecules/navigation/components/TabLabel.tsx +47 -0
  65. package/src/molecules/navigation/createStackNavigator.ts +20 -0
  66. package/src/molecules/navigation/createTabNavigator.ts +20 -0
  67. package/src/molecules/navigation/hooks/useTabBarStyles.ts +54 -0
  68. package/src/molecules/navigation/index.ts +37 -0
  69. package/src/molecules/navigation/types.ts +118 -0
  70. package/src/molecules/navigation/utils/AppNavigation.ts +101 -0
  71. package/src/molecules/navigation/utils/IconRenderer.ts +50 -0
  72. package/src/molecules/navigation/utils/LabelProcessor.ts +70 -0
  73. package/src/molecules/navigation/utils/NavigationCleanup.ts +62 -0
  74. package/src/molecules/navigation/utils/NavigationTheme.ts +21 -0
  75. package/src/molecules/navigation/utils/NavigationValidator.ts +61 -0
  76. package/src/molecules/navigation/utils/ScreenFactory.ts +115 -0
  77. package/src/molecules/navigation/utils/__tests__/IconRenderer.getIconName.test.ts +109 -0
  78. package/src/molecules/navigation/utils/__tests__/IconRenderer.renderIcon.test.ts +116 -0
  79. package/src/molecules/navigation/utils/__tests__/LabelProcessor.processLabel.test.ts +116 -0
  80. package/src/molecules/navigation/utils/__tests__/LabelProcessor.processTitle.test.ts +59 -0
  81. package/src/molecules/navigation/utils/__tests__/NavigationCleanup.test.ts +271 -0
  82. package/src/molecules/navigation/utils/__tests__/NavigationValidator.test.ts +252 -0
  83. package/src/molecules/swipe-actions/domain/entities/SwipeAction.ts +194 -0
  84. package/src/molecules/swipe-actions/index.ts +6 -0
  85. package/src/molecules/swipe-actions/presentation/components/SwipeActionButton.tsx +131 -0
  86. package/src/theme/hooks/useResponsiveDesignTokens.ts +1 -1
  87. package/src/utilities/clipboard/ClipboardUtils.ts +71 -0
  88. package/src/utilities/clipboard/index.ts +5 -0
  89. package/src/utilities/index.ts +6 -0
  90. package/src/utilities/sharing/domain/entities/Share.ts +210 -0
  91. package/src/utilities/sharing/index.ts +205 -0
  92. package/src/utilities/sharing/infrastructure/services/SharingService.ts +165 -0
  93. package/src/utilities/sharing/presentation/hooks/useSharing.ts +154 -0
@@ -0,0 +1,75 @@
1
+ import React, { useMemo } from "react";
2
+ import { createStackNavigator as createRNStackNavigator } from "@react-navigation/stack";
3
+ import type { ParamListBase } from "@react-navigation/native";
4
+ import type { StackNavigatorConfig, StackScreen } from "./types";
5
+ import { NavigationValidator } from "./utils/NavigationValidator";
6
+ import { createStackScreen } from "./utils/ScreenFactory";
7
+ import { useAppDesignTokens } from "../../theme";
8
+
9
+ // Create the navigator instance ONCE outside the component
10
+ const Stack = createRNStackNavigator();
11
+
12
+ export interface StackNavigatorProps<T extends ParamListBase> {
13
+ config: StackNavigatorConfig<T>;
14
+ }
15
+
16
+ /**
17
+ * StackNavigator Component
18
+ *
19
+ * Reusable Stack Navigator component that handles configuration and rendering.
20
+ * Integrates with design tokens for consistent themed styling.
21
+ */
22
+ export function StackNavigator<T extends ParamListBase>({ config }: StackNavigatorProps<T>) {
23
+ const tokens = useAppDesignTokens();
24
+
25
+ // Validate configuration
26
+ if (__DEV__) {
27
+ try {
28
+ NavigationValidator.validateScreens(config.screens, "stack");
29
+ NavigationValidator.validateInitialRoute(config.initialRouteName, config.screens);
30
+ } catch (error) {
31
+ if (__DEV__) console.error('[StackNavigator] Configuration validation failed:', error);
32
+ }
33
+ }
34
+
35
+ const { screens } = config;
36
+
37
+ // Default screen options using design tokens
38
+ const defaultScreenOptions = useMemo(() => ({
39
+ headerStyle: {
40
+ backgroundColor: tokens.colors.surface,
41
+ elevation: 0,
42
+ shadowOpacity: 0,
43
+ borderBottomWidth: 1,
44
+ borderBottomColor: tokens.colors.surfaceVariant,
45
+ },
46
+ headerTitleStyle: {
47
+ color: tokens.colors.onSurface,
48
+ fontSize: 18,
49
+ fontWeight: "600" as any,
50
+ },
51
+ headerTintColor: tokens.colors.primary,
52
+ cardStyle: {
53
+ backgroundColor: tokens.colors.background,
54
+ },
55
+ }), [tokens]);
56
+
57
+ if (screens.length === 0) {
58
+ if (__DEV__) console.warn('[StackNavigator] No screens found');
59
+ return null;
60
+ }
61
+
62
+ return (
63
+ <Stack.Navigator
64
+ initialRouteName={config.initialRouteName as string}
65
+ screenOptions={{
66
+ ...defaultScreenOptions,
67
+ ...(config.screenOptions as any),
68
+ }}
69
+ >
70
+ {screens.map((screen) =>
71
+ createStackScreen(screen as StackScreen<any>, config, Stack)
72
+ )}
73
+ </Stack.Navigator>
74
+ );
75
+ }
@@ -0,0 +1,94 @@
1
+ import React, { useMemo } from "react";
2
+ import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
3
+ import type { ParamListBase } from "@react-navigation/native";
4
+ import type { TabNavigatorConfig, TabScreen } from "./types";
5
+ import { NavigationValidator } from "./utils/NavigationValidator";
6
+ import { createTabScreen } from "./utils/ScreenFactory";
7
+ import { useAppDesignTokens } from "../../theme";
8
+
9
+ // Create the navigator instance ONCE outside the component
10
+ const Tab = createBottomTabNavigator();
11
+
12
+ export interface TabsNavigatorProps<T extends ParamListBase> {
13
+ config: TabNavigatorConfig<T>;
14
+ }
15
+
16
+ /**
17
+ * TabsNavigator Component
18
+ *
19
+ * Reusable Bottom Tab Navigator component that handles configuration and rendering.
20
+ * Integrates with design tokens for consistent themed styling.
21
+ */
22
+ export function TabsNavigator<T extends ParamListBase>({
23
+ config,
24
+ }: TabsNavigatorProps<T>) {
25
+ const tokens = useAppDesignTokens();
26
+
27
+ // Validate configuration
28
+ if (__DEV__) {
29
+ try {
30
+ NavigationValidator.validateScreens(config.screens, "tab");
31
+ NavigationValidator.validateInitialRoute(
32
+ config.initialRouteName,
33
+ config.screens
34
+ );
35
+ } catch (error) {
36
+ if (__DEV__)
37
+ console.error("[TabsNavigator] Configuration validation failed:", error);
38
+ }
39
+ }
40
+
41
+ // Memoize filtered screens
42
+ const visibleScreens = useMemo(
43
+ () => config.screens.filter((screen) => screen.visible !== false),
44
+ [config.screens]
45
+ );
46
+
47
+ // Default screen options using design tokens
48
+ const defaultScreenOptions = useMemo(
49
+ () => ({
50
+ tabBarActiveTintColor: tokens.colors.primary,
51
+ tabBarInactiveTintColor: tokens.colors.onSurface,
52
+ tabBarStyle: {
53
+ backgroundColor: tokens.colors.surface,
54
+ borderTopColor: tokens.colors.surfaceVariant,
55
+ height: (tokens.spacing as any).tabBarHeight || 60,
56
+ paddingBottom: 8,
57
+ paddingTop: 8,
58
+ },
59
+ headerStyle: {
60
+ backgroundColor: tokens.colors.surface,
61
+ elevation: 0,
62
+ shadowOpacity: 0,
63
+ borderBottomWidth: 1,
64
+ borderBottomColor: tokens.colors.surfaceVariant,
65
+ },
66
+ headerTitleStyle: {
67
+ color: tokens.colors.onSurface,
68
+ fontSize: 18,
69
+ fontWeight: "600" as any,
70
+ },
71
+ headerTintColor: tokens.colors.primary,
72
+ }),
73
+ [tokens]
74
+ );
75
+
76
+ if (visibleScreens.length === 0) {
77
+ if (__DEV__) console.warn("[TabsNavigator] No visible screens found");
78
+ return null;
79
+ }
80
+
81
+ return (
82
+ <Tab.Navigator
83
+ initialRouteName={config.initialRouteName as string}
84
+ screenOptions={{
85
+ ...defaultScreenOptions,
86
+ ...(config.screenOptions as any),
87
+ }}
88
+ >
89
+ {visibleScreens.map((screen) =>
90
+ createTabScreen(screen as TabScreen<any>, config, Tab)
91
+ )}
92
+ </Tab.Navigator>
93
+ );
94
+ }
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import { StyleSheet, View, type ViewStyle } from 'react-native';
3
+ import { useAppDesignTokens } from '../../../theme';
4
+
5
+ export interface FabButtonProps {
6
+ /** Content to render inside the FAB */
7
+ children: React.ReactNode;
8
+ /** Container style overrides */
9
+ style?: ViewStyle;
10
+ }
11
+
12
+ /**
13
+ * Standard FAB container component.
14
+ * Provides a centered, elevated circular container using design tokens.
15
+ */
16
+ export const FabButton: React.FC<FabButtonProps> = ({
17
+ children,
18
+ style,
19
+ }) => {
20
+ const tokens = useAppDesignTokens();
21
+
22
+ return (
23
+ <View style={[
24
+ styles.container,
25
+ {
26
+ backgroundColor: tokens.colors.primary,
27
+ borderColor: tokens.colors.onPrimary,
28
+ shadowColor: tokens.colors.onSurface,
29
+ },
30
+ style
31
+ ]}>
32
+ {children}
33
+ </View>
34
+ );
35
+ };
36
+
37
+ const styles = StyleSheet.create({
38
+ container: {
39
+ alignItems: 'center',
40
+ justifyContent: 'center',
41
+ borderWidth: 1,
42
+ borderColor: "rgba(255, 255, 255, 0.1)",
43
+ zIndex: 10,
44
+ },
45
+ });
@@ -0,0 +1,47 @@
1
+ import React, { useMemo } from 'react';
2
+ import { AtomicText } from '../../../atoms';
3
+
4
+ export interface TabLabelProps {
5
+ label: string;
6
+ focused: boolean;
7
+ color?: string;
8
+ focusedColor?: string;
9
+ unfocusedColor?: string;
10
+ fontSize?: number;
11
+ focusedWeight?: '400' | '500' | '600' | '700' | '800' | '900';
12
+ unfocusedWeight?: '400' | '500' | '600' | '700' | '800' | '900';
13
+ textStyle?: any;
14
+ textType?: 'labelSmall' | 'labelMedium' | 'labelLarge' | 'bodySmall';
15
+ }
16
+
17
+ export const TabLabel: React.FC<TabLabelProps> = React.memo(({
18
+ label,
19
+ focused,
20
+ color,
21
+ focusedColor,
22
+ unfocusedColor,
23
+ fontSize,
24
+ focusedWeight = '600',
25
+ unfocusedWeight = '500',
26
+ textStyle,
27
+ textType = 'labelSmall',
28
+ }) => {
29
+ const textStyleMemo = useMemo(() => [
30
+ {
31
+ color: color || (focused ? focusedColor : unfocusedColor),
32
+ textAlign: 'center' as const,
33
+ fontSize,
34
+ fontWeight: focused ? focusedWeight : unfocusedWeight,
35
+ },
36
+ textStyle,
37
+ ], [color, focused, focusedColor, unfocusedColor, fontSize, focusedWeight, unfocusedWeight, textStyle]);
38
+
39
+ return (
40
+ <AtomicText
41
+ type={textType}
42
+ style={textStyleMemo}
43
+ >
44
+ {label}
45
+ </AtomicText>
46
+ );
47
+ });
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ import type { ParamListBase } from "@react-navigation/native";
3
+ import type { StackNavigatorConfig } from "./types";
4
+ import { StackNavigator } from "./StackNavigator";
5
+
6
+ /**
7
+ * Creates a Stack Navigator component based on configuration.
8
+ *
9
+ * @deprecated Use <StackNavigator config={...} /> instead to prevent re-mounting issues.
10
+ * This wrapper is maintained for backward compatibility but using the component directly is recommended.
11
+ */
12
+ export function createStackNavigator<T extends ParamListBase>(
13
+ config: StackNavigatorConfig<T>
14
+ ): React.ComponentType {
15
+ // Return a component that renders the new StackNavigator
16
+ // Using React.memo to prevent unnecessary re-renders of the wrapper
17
+ return React.memo(function StackNavigatorWrapper() {
18
+ return React.createElement(StackNavigator as any, { config });
19
+ });
20
+ }
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ import type { ParamListBase } from "@react-navigation/native";
3
+ import type { TabNavigatorConfig } from "./types";
4
+ import { TabsNavigator } from "./TabsNavigator";
5
+
6
+ /**
7
+ * Creates a Tab Navigator component based on configuration.
8
+ *
9
+ * @deprecated Use <TabsNavigator config={...} /> instead to prevent re-mounting issues.
10
+ * This wrapper is maintained for backward compatibility but using the component directly is recommended.
11
+ */
12
+ export function createTabNavigator<T extends ParamListBase>(
13
+ config: TabNavigatorConfig<T>
14
+ ): React.ComponentType {
15
+ // Return a component that renders the new TabsNavigator
16
+ // Using React.memo to prevent unnecessary re-renders of the wrapper
17
+ return React.memo(function TabNavigatorWrapper() {
18
+ return React.createElement(TabsNavigator as any, { config });
19
+ });
20
+ }
@@ -0,0 +1,54 @@
1
+ import { Platform } from 'react-native';
2
+ import { useMemo } from 'react';
3
+ import { useAppDesignTokens } from '../../../theme';
4
+
5
+ export interface TabBarConfig {
6
+ backgroundColor?: string;
7
+ borderTopColor?: string;
8
+ borderTopWidth?: number;
9
+ paddingTop?: number;
10
+ paddingBottom?: number;
11
+ minHeight?: number;
12
+ activeTintColor?: string;
13
+ inactiveTintColor?: string;
14
+ labelFontSize?: number;
15
+ labelFontWeight?: string;
16
+ labelMarginTop?: number;
17
+ labelMarginBottom?: number;
18
+ }
19
+
20
+ export function useTabBarStyles(config: TabBarConfig = {}) {
21
+ const tokens = useAppDesignTokens();
22
+
23
+ const tabBarStyle = useMemo(() => ({
24
+ backgroundColor: config.backgroundColor || tokens.colors.surface,
25
+ borderTopColor: config.borderTopColor || tokens.colors.borderLight,
26
+ borderTopWidth: config.borderTopWidth ?? 1,
27
+ paddingTop: config.paddingTop ?? 12,
28
+ paddingBottom: config.paddingBottom ?? (Platform.OS === 'ios' ? 24 : 12),
29
+ minHeight: config.minHeight ?? (Platform.OS === 'ios' ? 80 : 70),
30
+ }), [config.backgroundColor, config.borderTopColor, config.borderTopWidth,
31
+ config.paddingTop, config.paddingBottom, config.minHeight, tokens.colors.surface,
32
+ tokens.colors.borderLight]);
33
+
34
+ const screenOptions = useMemo(() => ({
35
+ headerShown: false,
36
+ tabBarLabelStyle: {
37
+ fontSize: config.labelFontSize ?? 12,
38
+ fontWeight: (config.labelFontWeight ?? '600') as '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' | 'normal' | 'bold',
39
+ marginTop: config.labelMarginTop ?? 12,
40
+ marginBottom: config.labelMarginBottom ?? 4,
41
+ },
42
+ tabBarActiveTintColor: config.activeTintColor || tokens.colors.primary,
43
+ tabBarInactiveTintColor: config.inactiveTintColor || tokens.colors.textSecondary,
44
+ tabBarStyle,
45
+ }), [config.labelFontSize, config.labelFontWeight, config.labelMarginTop,
46
+ config.labelMarginBottom, config.activeTintColor, config.inactiveTintColor,
47
+ tabBarStyle, tokens.colors.primary, tokens.colors.textSecondary]);
48
+
49
+ return useMemo(() => ({
50
+ tokens,
51
+ screenOptions,
52
+ tabBarStyle,
53
+ }), [tokens, screenOptions, tabBarStyle]);
54
+ }
@@ -0,0 +1,37 @@
1
+ export { createTabNavigator } from "./createTabNavigator";
2
+ export { createStackNavigator } from "./createStackNavigator";
3
+ export { TabsNavigator, type TabsNavigatorProps } from "./TabsNavigator";
4
+ export { StackNavigator, type StackNavigatorProps } from "./StackNavigator";
5
+ export { FabButton, type FabButtonProps } from "./components/FabButton";
6
+
7
+ export type {
8
+ TabScreen,
9
+ TabNavigatorConfig,
10
+ StackScreen,
11
+ StackNavigatorConfig,
12
+ BaseScreen,
13
+ BaseNavigatorConfig,
14
+ IconRendererProps,
15
+ LabelProcessorProps,
16
+ FabConfig,
17
+ } from "./types";
18
+
19
+ export { DEFAULT_FAB_CONFIG } from "./types";
20
+
21
+ export { NavigationCleanupManager } from "./utils/NavigationCleanup";
22
+ export type { NavigationCleanup } from "./utils/NavigationCleanup";
23
+
24
+ export type {
25
+ BottomTabNavigationOptions,
26
+ BottomTabScreenProps,
27
+ } from "@react-navigation/bottom-tabs";
28
+
29
+ export type { StackNavigationOptions } from "@react-navigation/stack";
30
+
31
+ // Navigation Utilities
32
+ export { AppNavigation } from "./utils/AppNavigation";
33
+ export { TabLabel, type TabLabelProps } from "./components/TabLabel";
34
+ export { useTabBarStyles, type TabBarConfig } from "./hooks/useTabBarStyles";
35
+
36
+ // Navigation Theme
37
+ export { createNavigationTheme } from "./utils/NavigationTheme";
@@ -0,0 +1,118 @@
1
+ import type { ParamListBase } from "@react-navigation/native";
2
+ import type {
3
+ BottomTabNavigationOptions,
4
+ BottomTabScreenProps,
5
+ } from "@react-navigation/bottom-tabs";
6
+ import type { StackNavigationOptions } from "@react-navigation/stack";
7
+ import type { IconName } from "../../atoms/AtomicIcon";
8
+
9
+ export type NavigationParams = Record<string, unknown>;
10
+
11
+ /**
12
+ * FAB (Floating Action Button) configuration for center tab
13
+ */
14
+ export interface FabConfig {
15
+ /** FAB button size (default: 56) */
16
+ size?: number;
17
+ /** Vertical offset from tab bar (default: -20) */
18
+ offsetY?: number;
19
+ /** Border radius (default: size / 2) */
20
+ borderRadius?: number;
21
+ /** Border width when inactive (default: 3) */
22
+ borderWidth?: number;
23
+ }
24
+
25
+ /**
26
+ * Base interface for all screen configurations
27
+ * @template T - Type of navigation parameters for the screen
28
+ */
29
+ export interface BaseScreen<T extends ParamListBase = ParamListBase> {
30
+ /** Unique name identifier for the screen */
31
+ name: string;
32
+ /** React component to render for this screen */
33
+ component: React.ComponentType<T>;
34
+ }
35
+
36
+ /**
37
+ * Configuration for a tab navigation screen
38
+ * @template T - Type of navigation parameters for the screen
39
+ */
40
+ export interface TabScreen<T extends ParamListBase = ParamListBase>
41
+ extends BaseScreen<T> {
42
+ /** Display label for the tab */
43
+ label: string;
44
+ /** Icon identifier for the tab */
45
+ icon?: IconName;
46
+ /** Mark this tab as a FAB (center floating button) */
47
+ isFab?: boolean;
48
+ /** Additional navigation options for the tab */
49
+ options?:
50
+ | BottomTabNavigationOptions
51
+ | ((props: BottomTabScreenProps<T>) => BottomTabNavigationOptions);
52
+ /** Whether the tab should be visible */
53
+ visible?: boolean;
54
+ }
55
+
56
+ /**
57
+ * Configuration for a stack navigation screen
58
+ * @template T - Type of navigation parameters for the screen
59
+ */
60
+ export interface StackScreen<T extends ParamListBase = ParamListBase>
61
+ extends BaseScreen<T> {
62
+ /** Additional navigation options for the stack screen */
63
+ options?:
64
+ | StackNavigationOptions
65
+ | ((props: { navigation: unknown; route: unknown }) => StackNavigationOptions);
66
+ }
67
+
68
+ export interface BaseNavigatorConfig<T extends ParamListBase = ParamListBase> {
69
+ screens: TabScreen[] | StackScreen[];
70
+ initialRouteName?: Extract<keyof T, string>;
71
+ getLabel?: (label: string) => string;
72
+ }
73
+
74
+ export interface TabNavigatorConfig<T extends ParamListBase = ParamListBase>
75
+ extends BaseNavigatorConfig<T> {
76
+ screens: TabScreen[];
77
+ /** Custom icon renderer function */
78
+ renderIcon?: (
79
+ iconName: string,
80
+ focused: boolean,
81
+ routeName: string,
82
+ isFab: boolean
83
+ ) => React.ReactElement;
84
+ /** Get icon name for a tab */
85
+ getTabIcon?: (routeName: string, focused: boolean) => IconName;
86
+ /** Screen options for all tabs */
87
+ screenOptions?:
88
+ | BottomTabNavigationOptions
89
+ | ((props: BottomTabScreenProps<T>) => BottomTabNavigationOptions);
90
+ /** FAB configuration for center button styling */
91
+ fabConfig?: FabConfig;
92
+ }
93
+
94
+ export interface StackNavigatorConfig<T extends ParamListBase = ParamListBase>
95
+ extends BaseNavigatorConfig<T> {
96
+ screens: StackScreen[];
97
+ screenOptions?: StackNavigationOptions;
98
+ }
99
+
100
+ export interface IconRendererProps {
101
+ iconName: IconName;
102
+ focused: boolean;
103
+ routeName: string;
104
+ isFab: boolean;
105
+ }
106
+
107
+ export interface LabelProcessorProps {
108
+ label: string;
109
+ getLabel?: (label: string) => string;
110
+ }
111
+
112
+ /** Default FAB configuration values */
113
+ export const DEFAULT_FAB_CONFIG: Required<FabConfig> = {
114
+ size: 56,
115
+ offsetY: -20,
116
+ borderRadius: 28,
117
+ borderWidth: 3,
118
+ };
@@ -0,0 +1,101 @@
1
+ import { NavigationContainerRef, CommonActions, StackActions } from '@react-navigation/native';
2
+
3
+ export class AppNavigation {
4
+ private static navigationRef: NavigationContainerRef<any> | null = null;
5
+
6
+ /**
7
+ * Set the global navigation reference
8
+ */
9
+ static setRef(ref: NavigationContainerRef<any> | null): void {
10
+ this.navigationRef = ref;
11
+ }
12
+
13
+ /**
14
+ * Set the global navigation reference (alias for setRef)
15
+ * @deprecated Use setRef instead
16
+ */
17
+ static setNavigationRef(ref: NavigationContainerRef<any> | null): void {
18
+ this.setRef(ref);
19
+ }
20
+
21
+ /**
22
+ * Get the global navigation reference
23
+ */
24
+ static getRef(): NavigationContainerRef<any> | null {
25
+ return this.navigationRef;
26
+ }
27
+
28
+ /**
29
+ * Navigate to a route
30
+ */
31
+ static navigate(name: string, params?: object): void {
32
+ if (this.navigationRef?.isReady()) {
33
+ this.navigationRef.navigate(name, params);
34
+ } else if (__DEV__) {
35
+ console.warn('[AppNavigation] Navigation ref is not ready. Call setRef() first.');
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Push a route onto the stack
41
+ */
42
+ static push(name: string, params?: object): void {
43
+ if (this.navigationRef?.isReady()) {
44
+ this.navigationRef.dispatch(StackActions.push(name, params));
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Go back to the previous screen
50
+ */
51
+ static goBack(): void {
52
+ if (this.navigationRef?.isReady() && this.navigationRef.canGoBack()) {
53
+ this.navigationRef.goBack();
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Reset the navigation state
59
+ */
60
+ static reset(name: string, params?: object): void {
61
+ if (this.navigationRef?.isReady()) {
62
+ this.navigationRef.dispatch(
63
+ CommonActions.reset({
64
+ index: 0,
65
+ routes: [{ name, params }],
66
+ })
67
+ );
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Replace the current route
73
+ */
74
+ static replace(name: string, params?: object): void {
75
+ if (this.navigationRef?.isReady()) {
76
+ this.navigationRef.dispatch(StackActions.replace(name, params));
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Navigate to a screen in a nested navigator (e.g. Tab > Stack > Screen)
82
+ */
83
+ static navigateToNested(parentParams: { screen: string; params?: any }): void {
84
+ // This is a simplified wrapper for navigating to nested stacks which usually looks like:
85
+ // navigate('ParentStack', { screen: 'ChildScreen', params: { ... } })
86
+ // But React Navigation handle this quite well with basic .navigate too if hierarchy is clear.
87
+ // Kept simple for now.
88
+ }
89
+
90
+ /**
91
+ * Navigate to a screen in the parent navigator
92
+ */
93
+ static navigateToParent(name: string, params?: object): void {
94
+ if (this.navigationRef?.isReady()) {
95
+ const parent = this.navigationRef.getParent();
96
+ if (parent) {
97
+ parent.navigate(name, params);
98
+ }
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,50 @@
1
+ import React from "react";
2
+ import type { IconRendererProps } from "../types";
3
+ import { AtomicIcon } from "../../../atoms/AtomicIcon";
4
+ import type { IconName } from "../../../atoms/AtomicIcon";
5
+
6
+ type RenderIconFn = (
7
+ iconName: IconName,
8
+ focused: boolean,
9
+ routeName: string,
10
+ isFab: boolean
11
+ ) => React.ReactElement;
12
+
13
+ export class IconRenderer {
14
+ static renderIcon(
15
+ props: IconRendererProps,
16
+ renderIcon?: RenderIconFn
17
+ ): React.ReactElement | null {
18
+ const { iconName, focused, routeName, isFab } = props;
19
+
20
+ if (renderIcon) {
21
+ try {
22
+ const result = renderIcon(iconName, focused, routeName, isFab);
23
+ if (React.isValidElement(result)) {
24
+ return result;
25
+ }
26
+ } catch {
27
+ // Fallback to default
28
+ }
29
+ }
30
+
31
+ if (!iconName) {
32
+ return null;
33
+ }
34
+
35
+ return React.createElement(AtomicIcon, {
36
+ name: iconName,
37
+ size: isFab ? "lg" : "md",
38
+ color: focused ? "primary" : "onSurface",
39
+ });
40
+ }
41
+
42
+ static getIconName(
43
+ routeName: string,
44
+ focused: boolean,
45
+ icon: IconName,
46
+ getTabIcon?: (routeName: string, focused: boolean) => IconName
47
+ ): IconName {
48
+ return getTabIcon ? getTabIcon(routeName, focused) : icon;
49
+ }
50
+ }