@umituz/react-native-design-system 2.5.32 → 2.5.33
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.33",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive and safe area utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -3,21 +3,38 @@ import { View } from "react-native";
|
|
|
3
3
|
import type { ParamListBase } from "@react-navigation/native";
|
|
4
4
|
import { AtomicIcon } from "../../../atoms/AtomicIcon";
|
|
5
5
|
import { useAppDesignTokens } from "../../../theme";
|
|
6
|
-
import {
|
|
6
|
+
import { useResponsive } from "../../../responsive";
|
|
7
7
|
import type { TabNavigatorConfig, TabScreen } from "../types";
|
|
8
8
|
|
|
9
9
|
export interface UseTabConfigProps<T extends ParamListBase> {
|
|
10
10
|
config: TabNavigatorConfig<T>;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Get the appropriate icon name based on focus state
|
|
15
|
+
* Ionicons convention: filled for active, outline for inactive
|
|
16
|
+
*/
|
|
17
|
+
const getIconNameForState = (baseName: string, focused: boolean): string => {
|
|
18
|
+
if (!baseName) return "help-circle-outline";
|
|
19
|
+
|
|
20
|
+
const hasOutlineSuffix = baseName.endsWith("-outline");
|
|
21
|
+
const hasSharpSuffix = baseName.endsWith("-sharp");
|
|
22
|
+
|
|
23
|
+
if (hasOutlineSuffix || hasSharpSuffix) {
|
|
24
|
+
return baseName;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return focused ? baseName : `${baseName}-outline`;
|
|
28
|
+
};
|
|
29
|
+
|
|
13
30
|
export function useTabConfig<T extends ParamListBase>(props: UseTabConfigProps<T>): TabNavigatorConfig<T> {
|
|
14
31
|
const { config } = props;
|
|
15
32
|
const tokens = useAppDesignTokens();
|
|
16
|
-
const insets =
|
|
33
|
+
const { tabBarConfig, insets } = useResponsive();
|
|
17
34
|
|
|
18
35
|
const finalConfig: TabNavigatorConfig<T> = useMemo(() => {
|
|
19
36
|
const screens = config.screens as TabScreen<T>[];
|
|
20
|
-
|
|
37
|
+
|
|
21
38
|
return {
|
|
22
39
|
...config,
|
|
23
40
|
renderIcon: (
|
|
@@ -34,7 +51,7 @@ export function useTabConfig<T extends ParamListBase>(props: UseTabConfigProps<T
|
|
|
34
51
|
const fab = config.fabConfig;
|
|
35
52
|
|
|
36
53
|
if (isFab) {
|
|
37
|
-
const fabSize = fab?.size ??
|
|
54
|
+
const fabSize = fab?.size ?? tabBarConfig.fabSize;
|
|
38
55
|
return React.createElement(View, {
|
|
39
56
|
style: {
|
|
40
57
|
width: fabSize,
|
|
@@ -43,7 +60,7 @@ export function useTabConfig<T extends ParamListBase>(props: UseTabConfigProps<T
|
|
|
43
60
|
backgroundColor: tokens.colors.primary,
|
|
44
61
|
justifyContent: "center",
|
|
45
62
|
alignItems: "center",
|
|
46
|
-
marginTop: fab?.offsetY ??
|
|
63
|
+
marginTop: fab?.offsetY ?? tabBarConfig.fabOffsetY,
|
|
47
64
|
}
|
|
48
65
|
}, React.createElement(AtomicIcon, {
|
|
49
66
|
name: iconName,
|
|
@@ -53,25 +70,28 @@ export function useTabConfig<T extends ParamListBase>(props: UseTabConfigProps<T
|
|
|
53
70
|
}));
|
|
54
71
|
}
|
|
55
72
|
|
|
73
|
+
const resolvedIconName = getIconNameForState(iconName, focused);
|
|
74
|
+
|
|
56
75
|
return React.createElement(AtomicIcon, {
|
|
57
|
-
name:
|
|
76
|
+
name: resolvedIconName,
|
|
58
77
|
customColor: focused ? tokens.colors.primary : tokens.colors.textSecondary,
|
|
59
|
-
|
|
78
|
+
customSize: tabBarConfig.iconSize,
|
|
60
79
|
});
|
|
61
80
|
},
|
|
62
81
|
screenOptions: {
|
|
63
82
|
tabBarActiveTintColor: tokens.colors.primary,
|
|
64
83
|
tabBarInactiveTintColor: tokens.colors.textSecondary,
|
|
65
84
|
tabBarShowLabel: false,
|
|
66
|
-
|
|
67
|
-
|
|
85
|
+
tabBarItemStyle: {
|
|
86
|
+
paddingVertical: tabBarConfig.paddingTop,
|
|
68
87
|
},
|
|
69
88
|
tabBarStyle: {
|
|
70
89
|
backgroundColor: tokens.colors.surface,
|
|
71
|
-
borderTopColor: tokens.colors.borderLight,
|
|
72
90
|
borderTopWidth: 0,
|
|
73
|
-
paddingBottom:
|
|
74
|
-
|
|
91
|
+
paddingBottom: tabBarConfig.paddingBottom,
|
|
92
|
+
paddingTop: tabBarConfig.paddingTop,
|
|
93
|
+
height: tabBarConfig.height,
|
|
94
|
+
elevation: 0,
|
|
75
95
|
},
|
|
76
96
|
headerStyle: {
|
|
77
97
|
backgroundColor: tokens.colors.surface,
|
|
@@ -89,7 +109,7 @@ export function useTabConfig<T extends ParamListBase>(props: UseTabConfigProps<T
|
|
|
89
109
|
: {}),
|
|
90
110
|
},
|
|
91
111
|
};
|
|
92
|
-
}, [tokens, config, insets]);
|
|
112
|
+
}, [tokens, config, tabBarConfig, insets]);
|
|
93
113
|
|
|
94
114
|
return finalConfig;
|
|
95
115
|
}
|
package/src/responsive/index.ts
CHANGED
|
@@ -16,6 +16,9 @@ export {
|
|
|
16
16
|
getResponsiveHorizontalPadding,
|
|
17
17
|
getResponsiveBottomPosition,
|
|
18
18
|
getResponsiveFABPosition,
|
|
19
|
+
getResponsiveTabBarHeight,
|
|
20
|
+
getResponsiveTabBarConfig,
|
|
21
|
+
type ResponsiveTabBarConfig,
|
|
19
22
|
getResponsiveModalMaxHeight,
|
|
20
23
|
getResponsiveMinModalHeight,
|
|
21
24
|
getResponsiveModalWidth,
|
|
@@ -21,6 +21,9 @@ export {
|
|
|
21
21
|
getResponsiveHorizontalPadding,
|
|
22
22
|
getResponsiveBottomPosition,
|
|
23
23
|
getResponsiveFABPosition,
|
|
24
|
+
getResponsiveTabBarHeight,
|
|
25
|
+
getResponsiveTabBarConfig,
|
|
26
|
+
type ResponsiveTabBarConfig,
|
|
24
27
|
} from './responsiveLayout';
|
|
25
28
|
|
|
26
29
|
// Responsive modal utilities
|
|
@@ -96,3 +96,98 @@ export const getResponsiveFABPosition = (
|
|
|
96
96
|
return { bottom: 90, right: 20 };
|
|
97
97
|
}
|
|
98
98
|
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Tab bar height constants
|
|
102
|
+
*/
|
|
103
|
+
const TAB_BAR_CONSTANTS = {
|
|
104
|
+
BASE_HEIGHT_PHONE: 60,
|
|
105
|
+
BASE_HEIGHT_TABLET: 70,
|
|
106
|
+
MIN_PADDING_BOTTOM: 8,
|
|
107
|
+
MIN_PADDING_TOP: 8,
|
|
108
|
+
ICON_SIZE_PHONE: 24,
|
|
109
|
+
ICON_SIZE_TABLET: 28,
|
|
110
|
+
FAB_SIZE_PHONE: 64,
|
|
111
|
+
FAB_SIZE_TABLET: 72,
|
|
112
|
+
FAB_OFFSET_Y_PHONE: -24,
|
|
113
|
+
FAB_OFFSET_Y_TABLET: -28,
|
|
114
|
+
} as const;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Responsive tab bar configuration
|
|
118
|
+
*/
|
|
119
|
+
export interface ResponsiveTabBarConfig {
|
|
120
|
+
height: number;
|
|
121
|
+
paddingBottom: number;
|
|
122
|
+
paddingTop: number;
|
|
123
|
+
iconSize: number;
|
|
124
|
+
fabSize: number;
|
|
125
|
+
fabOffsetY: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get responsive tab bar height based on device and safe area
|
|
130
|
+
*/
|
|
131
|
+
export const getResponsiveTabBarHeight = (
|
|
132
|
+
insets: { bottom?: number } = { bottom: 0 }
|
|
133
|
+
): number => {
|
|
134
|
+
try {
|
|
135
|
+
validateSafeAreaInsets(insets);
|
|
136
|
+
const { width } = getScreenDimensions();
|
|
137
|
+
const { bottom = 0 } = insets;
|
|
138
|
+
|
|
139
|
+
const baseHeight = width >= DEVICE_BREAKPOINTS.SMALL_TABLET
|
|
140
|
+
? TAB_BAR_CONSTANTS.BASE_HEIGHT_TABLET
|
|
141
|
+
: TAB_BAR_CONSTANTS.BASE_HEIGHT_PHONE;
|
|
142
|
+
|
|
143
|
+
const bottomPadding = Math.max(bottom, TAB_BAR_CONSTANTS.MIN_PADDING_BOTTOM);
|
|
144
|
+
|
|
145
|
+
return baseHeight + bottomPadding;
|
|
146
|
+
} catch {
|
|
147
|
+
return TAB_BAR_CONSTANTS.BASE_HEIGHT_PHONE + TAB_BAR_CONSTANTS.MIN_PADDING_BOTTOM;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get complete responsive tab bar configuration
|
|
153
|
+
*/
|
|
154
|
+
export const getResponsiveTabBarConfig = (
|
|
155
|
+
insets: { bottom?: number } = { bottom: 0 }
|
|
156
|
+
): ResponsiveTabBarConfig => {
|
|
157
|
+
try {
|
|
158
|
+
validateSafeAreaInsets(insets);
|
|
159
|
+
const { width } = getScreenDimensions();
|
|
160
|
+
const { bottom = 0 } = insets;
|
|
161
|
+
const isTabletSize = width >= DEVICE_BREAKPOINTS.SMALL_TABLET;
|
|
162
|
+
|
|
163
|
+
const baseHeight = isTabletSize
|
|
164
|
+
? TAB_BAR_CONSTANTS.BASE_HEIGHT_TABLET
|
|
165
|
+
: TAB_BAR_CONSTANTS.BASE_HEIGHT_PHONE;
|
|
166
|
+
|
|
167
|
+
const paddingBottom = Math.max(bottom, TAB_BAR_CONSTANTS.MIN_PADDING_BOTTOM);
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
height: baseHeight + paddingBottom,
|
|
171
|
+
paddingBottom,
|
|
172
|
+
paddingTop: TAB_BAR_CONSTANTS.MIN_PADDING_TOP,
|
|
173
|
+
iconSize: isTabletSize
|
|
174
|
+
? TAB_BAR_CONSTANTS.ICON_SIZE_TABLET
|
|
175
|
+
: TAB_BAR_CONSTANTS.ICON_SIZE_PHONE,
|
|
176
|
+
fabSize: isTabletSize
|
|
177
|
+
? TAB_BAR_CONSTANTS.FAB_SIZE_TABLET
|
|
178
|
+
: TAB_BAR_CONSTANTS.FAB_SIZE_PHONE,
|
|
179
|
+
fabOffsetY: isTabletSize
|
|
180
|
+
? TAB_BAR_CONSTANTS.FAB_OFFSET_Y_TABLET
|
|
181
|
+
: TAB_BAR_CONSTANTS.FAB_OFFSET_Y_PHONE,
|
|
182
|
+
};
|
|
183
|
+
} catch {
|
|
184
|
+
return {
|
|
185
|
+
height: TAB_BAR_CONSTANTS.BASE_HEIGHT_PHONE + TAB_BAR_CONSTANTS.MIN_PADDING_BOTTOM,
|
|
186
|
+
paddingBottom: TAB_BAR_CONSTANTS.MIN_PADDING_BOTTOM,
|
|
187
|
+
paddingTop: TAB_BAR_CONSTANTS.MIN_PADDING_TOP,
|
|
188
|
+
iconSize: TAB_BAR_CONSTANTS.ICON_SIZE_PHONE,
|
|
189
|
+
fabSize: TAB_BAR_CONSTANTS.FAB_SIZE_PHONE,
|
|
190
|
+
fabOffsetY: TAB_BAR_CONSTANTS.FAB_OFFSET_Y_PHONE,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
};
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
getResponsiveHorizontalPadding,
|
|
20
20
|
getResponsiveBottomPosition,
|
|
21
21
|
getResponsiveFABPosition,
|
|
22
|
+
getResponsiveTabBarConfig,
|
|
22
23
|
getResponsiveModalMaxHeight,
|
|
23
24
|
getResponsiveMinModalHeight,
|
|
24
25
|
getResponsiveModalLayout,
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
type ResponsiveModalLayout,
|
|
32
33
|
type ResponsiveBottomSheetLayout,
|
|
33
34
|
type ResponsiveDialogLayout,
|
|
35
|
+
type ResponsiveTabBarConfig,
|
|
34
36
|
} from './responsive';
|
|
35
37
|
import {
|
|
36
38
|
isSmallPhone,
|
|
@@ -82,6 +84,9 @@ export interface UseResponsiveReturn {
|
|
|
82
84
|
bottomSheetLayout: ResponsiveBottomSheetLayout;
|
|
83
85
|
dialogLayout: ResponsiveDialogLayout;
|
|
84
86
|
|
|
87
|
+
// Tab bar configuration
|
|
88
|
+
tabBarConfig: ResponsiveTabBarConfig;
|
|
89
|
+
|
|
85
90
|
// Utility functions
|
|
86
91
|
getLogoSize: (baseSize?: number) => number;
|
|
87
92
|
getInputHeight: (baseHeight?: number) => number;
|
|
@@ -143,6 +148,9 @@ export const useResponsive = (): UseResponsiveReturn => {
|
|
|
143
148
|
bottomSheetLayout: getResponsiveBottomSheetLayout(),
|
|
144
149
|
dialogLayout: getResponsiveDialogLayout(),
|
|
145
150
|
|
|
151
|
+
// Tab bar configuration
|
|
152
|
+
tabBarConfig: getResponsiveTabBarConfig(insets),
|
|
153
|
+
|
|
146
154
|
// Utility functions (memoized)
|
|
147
155
|
getLogoSize,
|
|
148
156
|
getInputHeight,
|