@momo-kits/foundation 0.80.8 → 0.81.1-beta.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.
Files changed (87) hide show
  1. package/Application/BottomSheet.tsx +212 -0
  2. package/Application/BottomTab/BottomTabBar.tsx +278 -0
  3. package/Application/BottomTab/index.tsx +143 -0
  4. package/Application/Components.tsx +384 -213
  5. package/Application/ModalScreen.tsx +14 -178
  6. package/Application/Navigation.ts +15 -19
  7. package/Application/NavigationContainer.tsx +66 -63
  8. package/Application/Navigator.ts +106 -53
  9. package/Application/StackScreen.tsx +123 -9
  10. package/Application/index.ts +19 -6
  11. package/Application/types.ts +83 -26
  12. package/Application/utils.tsx +62 -74
  13. package/Assets/icon.json +83 -5
  14. package/Assets/icon_bank.json +506 -0
  15. package/Assets/language.json +3 -1
  16. package/Badge/Badge.tsx +56 -0
  17. package/Badge/BadgeDot.tsx +11 -0
  18. package/Badge/BadgeRibbon.tsx +125 -0
  19. package/Badge/Shape.tsx +29 -0
  20. package/Badge/index.tsx +7 -0
  21. package/Badge/styles.ts +58 -0
  22. package/Badge/types.ts +63 -0
  23. package/Button/index.tsx +64 -28
  24. package/Button/styles.ts +19 -0
  25. package/CheckBox/index.tsx +37 -27
  26. package/CheckBox/types.ts +5 -3
  27. package/Consts/colors+spacing+radius.ts +18 -0
  28. package/Icon/index.tsx +14 -9
  29. package/Icon/types.ts +4 -3
  30. package/IconButton/index.tsx +24 -15
  31. package/Image/index.tsx +62 -47
  32. package/Image/styles.ts +1 -9
  33. package/Image/types.ts +5 -1
  34. package/Input/Input.tsx +104 -25
  35. package/Input/InputDropDown.tsx +115 -110
  36. package/Input/InputMoney.tsx +79 -18
  37. package/Input/InputOTP.tsx +190 -149
  38. package/Input/InputSearch.tsx +96 -44
  39. package/Input/InputTextArea.tsx +33 -17
  40. package/Input/common.tsx +26 -17
  41. package/Input/index.tsx +108 -41
  42. package/Input/styles.ts +5 -7
  43. package/Input/utils.ts +1 -1
  44. package/Layout/AnimatedInputSearch.tsx +44 -0
  45. package/Layout/Card.tsx +37 -13
  46. package/Layout/FloatingButton.tsx +168 -0
  47. package/Layout/GridSystem.tsx +2 -3
  48. package/Layout/Item.tsx +1 -2
  49. package/Layout/ItemList.tsx +55 -0
  50. package/Layout/ItemSectionList.tsx +39 -0
  51. package/Layout/Screen.tsx +404 -181
  52. package/Layout/Section.tsx +22 -22
  53. package/Layout/TextTyping.tsx +114 -0
  54. package/Layout/index.ts +12 -2
  55. package/Layout/styles.ts +46 -13
  56. package/Layout/types.ts +13 -5
  57. package/Layout/utils.ts +7 -12
  58. package/Loader/index.tsx +2 -0
  59. package/Pagination/PaginationDot.tsx +6 -1
  60. package/Pagination/PaginationNumber.tsx +7 -4
  61. package/Pagination/PaginationScroll.tsx +0 -1
  62. package/Pagination/PaginationWhiteDot.tsx +6 -1
  63. package/Pagination/styles.ts +3 -0
  64. package/Pagination/types.ts +2 -0
  65. package/Popup/PopupNotify.tsx +108 -40
  66. package/Popup/PopupPromotion.tsx +45 -10
  67. package/Popup/types.ts +13 -10
  68. package/Radio/index.tsx +35 -21
  69. package/Radio/types.ts +7 -5
  70. package/Skeleton/index.tsx +1 -1
  71. package/Switch/index.tsx +32 -19
  72. package/Switch/types.ts +5 -3
  73. package/Tag/index.tsx +2 -4
  74. package/Text/index.tsx +26 -6
  75. package/Text/styles.ts +38 -0
  76. package/Text/types.ts +25 -13
  77. package/Title/index.tsx +177 -64
  78. package/Title/styles.ts +48 -15
  79. package/Title/types.ts +16 -7
  80. package/code-scanner.js +125 -16
  81. package/index.ts +9 -0
  82. package/migrate_typo.js +56 -0
  83. package/package.json +8 -7
  84. package/publish.sh +21 -16
  85. package/tsconfig.json +1 -2
  86. package/verify.js +24 -6
  87. package/Application/BottomTab.tsx +0 -65
@@ -0,0 +1,212 @@
1
+ import React, {useCallback, useContext, useEffect, useRef} from 'react';
2
+ import {
3
+ Animated,
4
+ Dimensions,
5
+ KeyboardAvoidingView,
6
+ Modal,
7
+ PanResponder,
8
+ Platform,
9
+ StyleSheet,
10
+ TouchableOpacity,
11
+ View,
12
+ } from 'react-native';
13
+ import {ApplicationContext} from './index';
14
+ import {BottomSheetParams} from './types';
15
+ import {useSafeAreaInsets} from 'react-native-safe-area-context';
16
+ import {Colors, Radius, Spacing, Styles} from '../Consts';
17
+ import {Text} from '../Text';
18
+ import {Icon} from '../Icon';
19
+
20
+ const BottomSheet: React.FC<BottomSheetParams> = props => {
21
+ const {theme, navigator} = useContext(ApplicationContext);
22
+ const heightDevice = Dimensions.get('screen').height;
23
+ const insets = useSafeAreaInsets();
24
+ const {
25
+ screen: Screen,
26
+ options,
27
+ surface,
28
+ barrierDismissible = false,
29
+ draggable = true,
30
+ useBottomInset = true,
31
+ keyboardVerticalOffset,
32
+ }: BottomSheetParams = props.route.params;
33
+ const pan = useRef(
34
+ new Animated.ValueXY({
35
+ y: heightDevice,
36
+ x: 0,
37
+ })
38
+ ).current;
39
+ const panResponder = useRef(
40
+ PanResponder.create({
41
+ onStartShouldSetPanResponder: () => draggable,
42
+ onMoveShouldSetPanResponder: (_, gestureState) => {
43
+ return draggable && gestureState.dy > 0;
44
+ },
45
+ onPanResponderMove: (e, gestureState) => {
46
+ gestureState.dy > 0 &&
47
+ Animated.event([null, {dy: pan.y}], {useNativeDriver: false})(
48
+ e,
49
+ gestureState
50
+ );
51
+ },
52
+ onPanResponderRelease: (_, gestureState) => {
53
+ if (gestureState.dy > 100) {
54
+ onDismiss();
55
+ } else {
56
+ Animated.spring(pan, {
57
+ toValue: {x: 0, y: 0},
58
+ useNativeDriver: false,
59
+ }).start();
60
+ }
61
+ },
62
+ })
63
+ ).current;
64
+
65
+ let backgroundColor = theme.colors.background.default;
66
+ if (surface) {
67
+ backgroundColor = theme.colors.background.surface;
68
+ }
69
+
70
+ /**
71
+ * emit dismiss event
72
+ */
73
+ useEffect(() => {
74
+ Animated.timing(pan, {
75
+ toValue: {x: 0, y: 0},
76
+ useNativeDriver: false,
77
+ duration: 150,
78
+ }).start();
79
+ return () => {
80
+ props.route.params?.onDismiss?.();
81
+ };
82
+ }, []);
83
+
84
+ /**
85
+ * handler dismiss
86
+ */
87
+ const onDismiss = (force = false, callback?: () => void) => {
88
+ if (barrierDismissible && !force) {
89
+ return;
90
+ }
91
+ Animated.timing(pan, {
92
+ toValue: {x: 0, y: heightDevice},
93
+ useNativeDriver: false,
94
+ duration: 100,
95
+ }).start(() => {
96
+ navigator?.pop();
97
+ callback?.();
98
+ });
99
+ };
100
+
101
+ /**
102
+ * manual close
103
+ */
104
+ const requestClose = useCallback((callback?: () => void) => {
105
+ onDismiss(true, callback);
106
+ }, []);
107
+
108
+ /**
109
+ * render header
110
+ */
111
+ const renderHeader = useCallback(() => {
112
+ return (
113
+ <View
114
+ style={{
115
+ backgroundColor,
116
+ borderTopLeftRadius: Radius.M,
117
+ borderTopRightRadius: Radius.M,
118
+ }}
119
+ {...panResponder.panHandlers}>
120
+ <View style={{height: 16}}>
121
+ <View style={styles.indicator} />
122
+ </View>
123
+ <View
124
+ style={[
125
+ styles.header,
126
+ surface && {
127
+ borderBottomColor: theme.colors.border.default,
128
+ borderBottomWidth: 1,
129
+ },
130
+ ]}>
131
+ <View style={styles.iconButton} />
132
+ <Text
133
+ style={[Styles.flex, {textAlign: 'center'}]}
134
+ numberOfLines={1}
135
+ typography="header_default_bold">
136
+ {options.title ?? ''}
137
+ </Text>
138
+ <TouchableOpacity
139
+ style={styles.iconButton}
140
+ onPress={() => onDismiss(true)}>
141
+ <Icon source="navigation_close" />
142
+ </TouchableOpacity>
143
+ </View>
144
+ </View>
145
+ );
146
+ }, []);
147
+
148
+ return (
149
+ <Modal transparent visible={true} onRequestClose={() => onDismiss()}>
150
+ <KeyboardAvoidingView
151
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
152
+ keyboardVerticalOffset={keyboardVerticalOffset ?? -20}
153
+ style={[Styles.flex, {justifyContent: 'flex-end'}]}>
154
+ <TouchableOpacity
155
+ style={Styles.flex}
156
+ activeOpacity={1}
157
+ onPress={() => onDismiss()}
158
+ />
159
+ <Animated.View style={{transform: pan.getTranslateTransform()}}>
160
+ {renderHeader()}
161
+ <View style={{backgroundColor: backgroundColor}}>
162
+ <Screen
163
+ {...props}
164
+ {...props.route.params}
165
+ requestClose={requestClose}
166
+ />
167
+ {useBottomInset && (
168
+ <View style={{height: Math.min(insets.bottom, 21)}} />
169
+ )}
170
+ </View>
171
+ </Animated.View>
172
+ </KeyboardAvoidingView>
173
+ </Modal>
174
+ );
175
+ };
176
+
177
+ export default BottomSheet;
178
+
179
+ const styles = StyleSheet.create({
180
+ container: {
181
+ width: '100%',
182
+ height: 0,
183
+ overflow: 'hidden',
184
+ borderTopLeftRadius: Radius.M,
185
+ borderTopRightRadius: Radius.M,
186
+ },
187
+ draggableContainer: {
188
+ width: '100%',
189
+ alignItems: 'center',
190
+ },
191
+ indicator: {
192
+ width: 40,
193
+ height: 4,
194
+ borderRadius: Radius.S,
195
+ backgroundColor: Colors.black_06,
196
+ position: 'absolute',
197
+ alignSelf: 'center',
198
+ top: Spacing.S,
199
+ },
200
+ header: {
201
+ height: 56,
202
+ flexDirection: 'row',
203
+ alignItems: 'center',
204
+ },
205
+ iconButton: {
206
+ width: 24,
207
+ height: 24,
208
+ alignItems: 'center',
209
+ justifyContent: 'center',
210
+ marginHorizontal: Spacing.M,
211
+ },
212
+ });
@@ -0,0 +1,278 @@
1
+ import {BottomTabBarProps} from '@react-navigation/bottom-tabs';
2
+ import BottomTabBarItem from '@react-navigation/bottom-tabs/src/views/BottomTabItem';
3
+ import {
4
+ CommonActions,
5
+ NavigationContext,
6
+ NavigationRouteContext,
7
+ useLinkBuilder,
8
+ } from '@react-navigation/native';
9
+ import React, {useCallback, useContext} from 'react';
10
+ import {
11
+ Animated,
12
+ Platform,
13
+ StyleSheet,
14
+ TouchableOpacity,
15
+ useWindowDimensions,
16
+ View,
17
+ } from 'react-native';
18
+ import {EdgeInsets, useSafeAreaInsets} from 'react-native-safe-area-context';
19
+ import {Radius} from '../../Consts';
20
+ import {Icon} from '../../Icon';
21
+ import {Text} from '../../Text';
22
+ import {FloatingButtonProps} from '../types';
23
+ import {ApplicationContext} from '../index';
24
+
25
+ type Props = BottomTabBarProps & {
26
+ activeTintColor?: string;
27
+ inactiveTintColor?: string;
28
+ floatingButton?: FloatingButtonProps;
29
+ };
30
+
31
+ const getPaddingBottom = (insets: EdgeInsets) => {
32
+ return Math.max(insets.bottom - Platform.select({ios: 4, default: 0}), 0);
33
+ };
34
+
35
+ export default function BottomTabBar({
36
+ state,
37
+ navigation,
38
+ descriptors,
39
+ activeBackgroundColor,
40
+ activeTintColor,
41
+ allowFontScaling,
42
+ inactiveBackgroundColor,
43
+ inactiveTintColor,
44
+ labelStyle,
45
+ iconStyle,
46
+ showLabel,
47
+ style,
48
+ tabStyle,
49
+ floatingButton,
50
+ }: Props) {
51
+ const {theme} = useContext(ApplicationContext);
52
+ const {routes} = state;
53
+ const widthDevice = useWindowDimensions()?.width;
54
+ const useFloating = floatingButton && state.routes.length % 2 === 0;
55
+ const itemWidth = widthDevice / (state.routes.length + (useFloating ? 1 : 0));
56
+ const buildLink = useLinkBuilder();
57
+ const indicatorAnimated = React.useRef(new Animated.Value(0)).current;
58
+ const insets = useSafeAreaInsets();
59
+ const paddingBottom = getPaddingBottom(insets);
60
+ const focusedTab = state?.routes[state.index]?.name || routes[0]?.params;
61
+
62
+ /**
63
+ * render tab item
64
+ */
65
+ const renderTabItem = useCallback(
66
+ (data: any[]) => {
67
+ return data?.map((route, _) => {
68
+ const focused = route.params.name === focusedTab;
69
+ const {options} = descriptors[route?.key] as any;
70
+ const onPress = () => {
71
+ const event = navigation.emit({
72
+ type: 'tabPress',
73
+ target: route.key,
74
+ canPreventDefault: true,
75
+ });
76
+
77
+ if (!focused && !event.defaultPrevented) {
78
+ navigation.dispatch({
79
+ ...CommonActions.navigate(route.name),
80
+ target: state.key,
81
+ });
82
+ }
83
+ };
84
+
85
+ const onLongPress = () => {
86
+ navigation.emit({
87
+ type: 'tabLongPress',
88
+ target: route.key,
89
+ });
90
+ };
91
+
92
+ const label = options.tabBarLabel || options.title || route.name;
93
+
94
+ return (
95
+ <NavigationContext.Provider
96
+ key={route.key}
97
+ value={(descriptors as any)[route.key].navigation}>
98
+ <NavigationRouteContext.Provider value={route}>
99
+ <BottomTabBarItem
100
+ route={route}
101
+ focused={focused}
102
+ horizontal={false}
103
+ onPress={onPress}
104
+ onLongPress={onLongPress}
105
+ accessibilityLabel={`tab_item_${route.name}`}
106
+ to={buildLink(route.name, route.params)}
107
+ testID={options.tabBarTestID}
108
+ allowFontScaling={allowFontScaling}
109
+ activeTintColor={activeTintColor}
110
+ inactiveTintColor={inactiveTintColor}
111
+ activeBackgroundColor={activeBackgroundColor}
112
+ inactiveBackgroundColor={inactiveBackgroundColor}
113
+ button={options.tabBarButton}
114
+ icon={options.tabBarIcon}
115
+ badge={options.tabBarBadge}
116
+ badgeStyle={options.tabBarBadgeStyle}
117
+ label={label}
118
+ showLabel={showLabel}
119
+ labelStyle={labelStyle}
120
+ iconStyle={iconStyle}
121
+ style={tabStyle}
122
+ />
123
+ </NavigationRouteContext.Provider>
124
+ </NavigationContext.Provider>
125
+ );
126
+ });
127
+ },
128
+ [JSON.stringify(routes), state, descriptors, focusedTab],
129
+ );
130
+
131
+ /**
132
+ * render floating button
133
+ */
134
+ const renderFloatingButton = useCallback(() => {
135
+ const renderFloatingButton = () => {
136
+ if (typeof floatingButton?.icon === 'string') {
137
+ return <Icon source={floatingButton?.icon} size={32} color={'white'} />;
138
+ }
139
+ return floatingButton?.icon;
140
+ };
141
+
142
+ return (
143
+ <View style={{flex: 1}}>
144
+ <TouchableOpacity
145
+ onPress={floatingButton?.onPress}
146
+ style={styles.floatingContainer}>
147
+ <View
148
+ style={{
149
+ ...styles.floatingContent,
150
+ backgroundColor: activeTintColor ?? theme.colors.primary,
151
+ }}>
152
+ {renderFloatingButton()}
153
+ </View>
154
+ <View
155
+ style={{
156
+ top: -6,
157
+ backgroundColor: activeTintColor ?? theme.colors.primary,
158
+ borderRadius: Radius.XL,
159
+ paddingHorizontal: 6,
160
+ }}>
161
+ <Text
162
+ numberOfLines={1}
163
+ typography={'description_xs_regular'}
164
+ color={'white'}>
165
+ {floatingButton?.label}
166
+ </Text>
167
+ </View>
168
+ </TouchableOpacity>
169
+ </View>
170
+ );
171
+ }, [useFloating]);
172
+
173
+ /**
174
+ * animated indicator
175
+ */
176
+ React.useEffect(() => {
177
+ let index = state.index;
178
+ if (useFloating && index > (state.routes.length - 1) / 2) {
179
+ index += 1;
180
+ }
181
+ Animated.timing(indicatorAnimated, {
182
+ toValue: itemWidth * index,
183
+ useNativeDriver: true,
184
+ duration: 200,
185
+ }).start();
186
+ }, [state.index, itemWidth, indicatorAnimated, useFloating]);
187
+
188
+ /**
189
+ * render tab bar
190
+ */
191
+ const renderTabBar = () => {
192
+ if (useFloating) {
193
+ const middleIndex = routes.length / 2;
194
+ const leftArr = routes.slice(0, middleIndex);
195
+ const rightArr = routes.slice(middleIndex);
196
+ return (
197
+ <>
198
+ {renderTabItem(leftArr)}
199
+ {renderFloatingButton()}
200
+ {renderTabItem(rightArr)}
201
+ </>
202
+ );
203
+ }
204
+ return renderTabItem(routes);
205
+ };
206
+
207
+ return (
208
+ <Animated.View
209
+ style={[
210
+ styles.tabBar,
211
+ {
212
+ backgroundColor: theme.colors.background.surface,
213
+ borderTopColor: theme.colors.border.default,
214
+ },
215
+ {
216
+ paddingBottom: paddingBottom,
217
+ paddingHorizontal: Math.max(insets.left, insets.right),
218
+ },
219
+ style,
220
+ ]}
221
+ pointerEvents={'auto'}>
222
+ <View style={styles.content} accessibilityLabel={'bottom_tab_bar'}>
223
+ {useFloating && floatingButton?.container}
224
+ <Animated.View
225
+ style={{
226
+ width: itemWidth,
227
+ position: 'absolute',
228
+ top: 0,
229
+ transform: [{translateX: indicatorAnimated}],
230
+ }}>
231
+ <View
232
+ style={[
233
+ styles.indicator,
234
+ {backgroundColor: activeTintColor ?? theme.colors.primary},
235
+ ]}
236
+ />
237
+ </Animated.View>
238
+ {renderTabBar()}
239
+ </View>
240
+ </Animated.View>
241
+ );
242
+ }
243
+
244
+ const styles = StyleSheet.create({
245
+ content: {
246
+ flex: 1,
247
+ flexDirection: 'row',
248
+ },
249
+ tabBar: {
250
+ left: 0,
251
+ right: 0,
252
+ bottom: 0,
253
+ borderTopWidth: StyleSheet.hairlineWidth,
254
+ elevation: 8,
255
+ },
256
+ floatingContainer: {
257
+ position: 'absolute',
258
+ alignItems: 'center',
259
+ left: 0,
260
+ right: 0,
261
+ bottom: Platform.OS === 'ios' ? -6 : 0,
262
+ },
263
+ floatingContent: {
264
+ alignItems: 'center',
265
+ justifyContent: 'center',
266
+ height: 48,
267
+ width: 48,
268
+ borderRadius: 24,
269
+ top: -10,
270
+ },
271
+ indicator: {
272
+ height: 2,
273
+ width: 44,
274
+ alignSelf: 'center',
275
+ borderBottomLeftRadius: 1,
276
+ borderBottomRightRadius: 1,
277
+ },
278
+ });
@@ -0,0 +1,143 @@
1
+ import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
2
+ import {NavigationContainer} from '@react-navigation/native';
3
+ import {createStackNavigator} from '@react-navigation/stack';
4
+ import React, {useContext, useEffect} from 'react';
5
+ import {Platform} from 'react-native';
6
+ import {useSafeAreaInsets} from 'react-native-safe-area-context';
7
+ import {Colors} from '../../Consts';
8
+ import {Icon} from '../../Icon';
9
+ import {Text} from '../../Text';
10
+ import {ApplicationContext} from '../index';
11
+ import StackScreen from '../StackScreen';
12
+ import {BottomTabProps, NavigationScreenProps} from '../types';
13
+ import {getOptions, getStackOptions} from '../utils';
14
+ import BottomTabBar from './BottomTabBar';
15
+
16
+ const Tab = createBottomTabNavigator();
17
+ const Stack = createStackNavigator();
18
+
19
+ const TabScreen: React.FC<NavigationScreenProps> = ({route}) => {
20
+ const {theme} = useContext(ApplicationContext);
21
+ let options = {};
22
+ if (route.params?.options) {
23
+ options = getOptions(route.params?.options, theme);
24
+ }
25
+
26
+ if (route.params?.nested) {
27
+ return (
28
+ <Stack.Navigator>
29
+ <Stack.Screen
30
+ name="Stack"
31
+ component={StackScreen}
32
+ initialParams={{
33
+ screen: route.params?.screen,
34
+ options,
35
+ initialParams: route.params?.initialParams,
36
+ }}
37
+ options={{...getStackOptions()}}
38
+ />
39
+ </Stack.Navigator>
40
+ );
41
+ }
42
+ return (
43
+ <NavigationContainer independent={true}>
44
+ <Stack.Navigator>
45
+ <Stack.Screen
46
+ name="Stack"
47
+ component={StackScreen}
48
+ initialParams={{
49
+ screen: route.params?.screen,
50
+ options,
51
+ initialParams: route.params?.initialParams,
52
+ }}
53
+ options={{...getStackOptions(), ...route.params?.options}}
54
+ />
55
+ </Stack.Navigator>
56
+ </NavigationContainer>
57
+ );
58
+ };
59
+
60
+ const BottomTab: React.FC<BottomTabProps> = ({
61
+ nested,
62
+ tabs,
63
+ listeners,
64
+ navigation,
65
+ initialRouteName,
66
+ floatingButton,
67
+ }) => {
68
+ const {theme} = useContext(ApplicationContext);
69
+ const insets = useSafeAreaInsets();
70
+
71
+ useEffect(() => {
72
+ navigation?.setOptions({
73
+ headerShown: false,
74
+ headerTransparent: true,
75
+ headerBackground: undefined,
76
+ });
77
+ }, []);
78
+
79
+ return (
80
+ <Tab.Navigator
81
+ initialRouteName={initialRouteName}
82
+ backBehavior={'firstRoute'}
83
+ tabBar={props => (
84
+ <BottomTabBar {...props} floatingButton={floatingButton} />
85
+ )}
86
+ tabBarOptions={{
87
+ style: {
88
+ height: 64 + Math.min(insets.bottom, 21),
89
+ },
90
+ tabStyle: {
91
+ height: 68,
92
+ paddingVertical: 10,
93
+ },
94
+ }}>
95
+ {tabs.map(item => {
96
+ const isNum = !isNaN(parseInt(`${item?.badgeLabel}`));
97
+ const isDot = typeof item?.badgeLabel === 'string' && !item?.badgeLabel;
98
+ return (
99
+ <Tab.Screen
100
+ key={item.name}
101
+ name={item.name}
102
+ component={TabScreen}
103
+ initialParams={{...item, nested}}
104
+ listeners={listeners}
105
+ options={{
106
+ tabBarLabel: ({color, focused}) => {
107
+ return (
108
+ <Text
109
+ numberOfLines={1}
110
+ typography={
111
+ focused ? 'label_xs_medium' : 'description_xs_regular'
112
+ }
113
+ color={color}
114
+ style={{width: '90%', textAlign: 'center'}}>
115
+ {item.label}
116
+ </Text>
117
+ );
118
+ },
119
+ tabBarBadge: item?.badgeLabel,
120
+ tabBarBadgeStyle: {
121
+ borderColor: 'white',
122
+ borderWidth: 2,
123
+ fontSize: 10,
124
+ lineHeight: Platform.OS === 'android' ? 17 : 14,
125
+ fontWeight: 'bold',
126
+ alignSelf: 'center',
127
+ fontFamily: `${theme.font}-Bold`,
128
+ left: isDot ? 8 : 4,
129
+ top: isDot ? -2 : -6,
130
+ backgroundColor: isNum ? Colors.red_03 : Colors.orange_03,
131
+ },
132
+ tabBarIcon: ({color}) => (
133
+ <Icon source={item.icon} color={color} size={28} />
134
+ ),
135
+ }}
136
+ />
137
+ );
138
+ })}
139
+ </Tab.Navigator>
140
+ );
141
+ };
142
+
143
+ export default BottomTab;