@momo-kits/foundation 0.92.1-rc.1 → 0.92.1-rc.10

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.
@@ -1,16 +1,15 @@
1
- import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
2
- import {NavigationContainer} from '@react-navigation/native';
3
- import {createStackNavigator} from '@react-navigation/stack';
4
1
  import React, {useContext, useEffect} from 'react';
5
2
  import {Platform} from 'react-native';
3
+ import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
4
+ import {createStackNavigator} from '@react-navigation/stack';
6
5
  import {useSafeAreaInsets} from 'react-native-safe-area-context';
7
- import BottomTabBar from './BottomTabBar';
8
- import {Icon} from '../../Icon';
9
- import {Text} from '../../Text';
10
- import {ApplicationContext} from '../index';
11
- import {getAccessibilityID, getStackOptions} from '../utils';
12
- import StackScreen from '../StackScreen';
6
+ import {NavigationContainer} from '@react-navigation/native';
13
7
  import {BottomTabProps, NavigationScreenProps} from '../types';
8
+ import StackScreen from '../StackScreen';
9
+ import {getAccessibilityID, getStackOptions} from '../utils';
10
+ import {ApplicationContext} from '../index';
11
+ import {Text} from '../../Text';
12
+ import {Icon} from '../../Icon';
14
13
 
15
14
  const Tab = createBottomTabNavigator();
16
15
  const Stack = createStackNavigator();
@@ -48,7 +47,6 @@ const BottomTab: React.FC<BottomTabProps> = ({
48
47
  listeners,
49
48
  navigation,
50
49
  initialRouteName,
51
- floatingButton,
52
50
  }) => {
53
51
  const {theme} = useContext(ApplicationContext);
54
52
  const insets = useSafeAreaInsets();
@@ -66,9 +64,6 @@ const BottomTab: React.FC<BottomTabProps> = ({
66
64
  {...getAccessibilityID('tbr_main_bottom')}
67
65
  initialRouteName={initialRouteName}
68
66
  backBehavior={'firstRoute'}
69
- tabBar={props => (
70
- <BottomTabBar {...props} floatingButton={floatingButton} />
71
- )}
72
67
  tabBarOptions={{
73
68
  style: {
74
69
  height: 68 + Math.min(insets.bottom, 21),
@@ -158,6 +158,13 @@ const getAccessibilityID = (
158
158
  }
159
159
  };
160
160
 
161
+ const cleanNameForTracking = (word = ''): string => {
162
+ return word
163
+ .normalize('NFD')
164
+ .replace(/[\u0300-\u036f |\s]/g, '')
165
+ .toLowerCase();
166
+ };
167
+
161
168
  const formatComponentName = (word = '') => {
162
169
  const accentsMap = {
163
170
  a: 'á|à|ả|ã|ạ|â|ấ|ầ|ẩ|ẫ|ậ|ă|ắ|ằ|ẳ|ẵ|ặ',
@@ -201,4 +208,5 @@ export {
201
208
  formatComponentName,
202
209
  formatNameLength,
203
210
  getAccessibilityID,
211
+ cleanNameForTracking,
204
212
  };
package/Button/index.tsx CHANGED
@@ -14,7 +14,11 @@ import {Colors, Spacing} from '../Consts';
14
14
  import styles from './styles';
15
15
  import {Icon} from '../Icon';
16
16
  import {Loader} from '../Loader';
17
- import {formatComponentName, getAccessibilityID} from '../Application/utils';
17
+ import {
18
+ cleanNameForTracking,
19
+ formatComponentName,
20
+ getAccessibilityID,
21
+ } from '../Application/utils';
18
22
 
19
23
  const {
20
24
  ScreenContext,
@@ -323,7 +327,9 @@ const Button: FC<ButtonProps> = ({
323
327
  <TouchableOpacity
324
328
  {...rest}
325
329
  {...getAccessibilityID(
326
- `${appId}/${code}/${screenInfo.screenName}/btn_${title}`,
330
+ `${appId}/${code}/${screenInfo.screenName}/btn_${cleanNameForTracking(
331
+ title,
332
+ )}`,
327
333
  accessibilityLabel,
328
334
  )}
329
335
  activeOpacity={activeOpacity}
@@ -5,7 +5,11 @@ import styles from './styles';
5
5
  import {ApplicationContext} from '../Application';
6
6
  import {Text} from '../Text';
7
7
  import {Icon} from '../Icon';
8
- import {formatNameLength, getAccessibilityID} from '../Application/utils';
8
+ import {
9
+ cleanNameForTracking,
10
+ formatNameLength,
11
+ getAccessibilityID,
12
+ } from '../Application/utils';
9
13
 
10
14
  const {
11
15
  ScreenContext,
@@ -22,7 +26,7 @@ const CheckBox: FC<CheckBoxProps> = ({
22
26
  params,
23
27
  componentId,
24
28
  }) => {
25
- const {appId} = useContext<any>(MiniAppContext);
29
+ const {appId, code} = useContext<any>(MiniAppContext);
26
30
  const {theme} = useContext(ApplicationContext);
27
31
  const screenInfo = useContext<any>(ScreenContext);
28
32
  const haveValue = value || indeterminate;
@@ -45,7 +49,9 @@ const CheckBox: FC<CheckBoxProps> = ({
45
49
  }
46
50
 
47
51
  const accessibilityID = getAccessibilityID(
48
- `${appId}/${screenInfo.screenName}/cbx_${label}_${value}`,
52
+ `${appId}/${code}/${screenInfo.screenName}/cbx_${cleanNameForTracking(
53
+ label,
54
+ )}_${indeterminate ? 'na' : value}`,
49
55
  );
50
56
 
51
57
  return (
package/Input/Input.tsx CHANGED
@@ -14,7 +14,11 @@ import {InputProps} from './index';
14
14
  import {Icon} from '../Icon';
15
15
  import {checkTyping} from './utils';
16
16
  import {Loader} from '../Loader';
17
- import {formatComponentName, getAccessibilityID} from '../Application/utils';
17
+ import {
18
+ formatComponentName,
19
+ getAccessibilityID,
20
+ cleanNameForTracking,
21
+ } from '../Application/utils';
18
22
 
19
23
  const {
20
24
  ScreenContext,
@@ -120,14 +124,16 @@ const Input = forwardRef(
120
124
  let iconTintColor = iconColor;
121
125
  const accessibilityID = disabled
122
126
  ? getAccessibilityID(
123
- `${appId}/${code}/${screenInfo.screenName}/input_${
124
- floatingValue ?? placeholder
125
- }_disabled`,
127
+ `${appId}/${code}/${
128
+ screenInfo.screenName
129
+ }/input_${cleanNameForTracking(
130
+ floatingValue ?? placeholder,
131
+ )}_disabled`,
126
132
  )
127
133
  : getAccessibilityID(
128
- `${appId}/${code}/${screenInfo.screenName}/input_${
129
- floatingValue ?? placeholder
130
- }`,
134
+ `${appId}/${code}/${
135
+ screenInfo.screenName
136
+ }/input_${cleanNameForTracking(floatingValue ?? placeholder)}`,
131
137
  );
132
138
 
133
139
  if (disabled) {
@@ -7,7 +7,11 @@ import {InputDropDownProps} from './index';
7
7
  import {Icon} from '../Icon';
8
8
  import {Loader} from '../Loader';
9
9
  import {Styles} from '../Consts';
10
- import {formatComponentName, getAccessibilityID} from '../Application/utils';
10
+ import {
11
+ cleanNameForTracking,
12
+ formatComponentName,
13
+ getAccessibilityID,
14
+ } from '../Application/utils';
11
15
 
12
16
  const {
13
17
  ScreenContext,
@@ -57,14 +61,16 @@ const InputDropDown = ({
57
61
  let iconTintColor = iconColor;
58
62
  const accessibilityID = disabled
59
63
  ? getAccessibilityID(
60
- `${appId}/${code}/${screenInfo.screenName}/input_${
61
- floatingValue ?? placeholder
62
- }_disabled`,
64
+ `${appId}/${code}/${
65
+ screenInfo.screenName
66
+ }/input_${cleanNameForTracking(
67
+ floatingValue ?? placeholder,
68
+ )}_disabled`,
63
69
  )
64
70
  : getAccessibilityID(
65
- `${appId}/${code}/${screenInfo.screenName}/input_${
66
- floatingValue ?? placeholder
67
- }`,
71
+ `${appId}/${code}/${
72
+ screenInfo.screenName
73
+ }/input_${cleanNameForTracking(floatingValue ?? placeholder)}`,
68
74
  );
69
75
 
70
76
  if (disabled) {
@@ -18,7 +18,11 @@ import {ErrorView, FloatingView, getBorderColor, getSizeStyle} from './common';
18
18
  import {InputMoneyProps} from './index';
19
19
  import {Icon} from '../Icon';
20
20
  import {formatMoneyToNumber, formatNumberToMoney} from './utils';
21
- import {formatComponentName, getAccessibilityID} from '../Application/utils';
21
+ import {
22
+ cleanNameForTracking,
23
+ formatComponentName,
24
+ getAccessibilityID,
25
+ } from '../Application/utils';
22
26
 
23
27
  const {
24
28
  ScreenContext,
@@ -101,10 +105,14 @@ const InputMoney = forwardRef(
101
105
  let iconTintColor = iconColor;
102
106
  const accessibilityID = disabled
103
107
  ? getAccessibilityID(
104
- `${appId}/${code}/${screenInfo.screenName}/input_${floatingValue}_disabled`,
108
+ `${appId}/${code}/${
109
+ screenInfo.screenName
110
+ }/input_${cleanNameForTracking(floatingValue)}_disabled`,
105
111
  )
106
112
  : getAccessibilityID(
107
- `${appId}/${code}/${screenInfo.screenName}/input_${floatingValue}`,
113
+ `${appId}/${code}/${
114
+ screenInfo.screenName
115
+ }/input_${cleanNameForTracking(floatingValue)}`,
108
116
  );
109
117
 
110
118
  if (disabled) {
@@ -21,7 +21,11 @@ import styles from './styles';
21
21
  import {ErrorView, getBorderColor} from './common';
22
22
  import {ApplicationContext} from '../Application';
23
23
  import {Spacing} from '../Consts';
24
- import {formatComponentName, getAccessibilityID} from '../Application/utils';
24
+ import {
25
+ cleanNameForTracking,
26
+ formatComponentName,
27
+ getAccessibilityID,
28
+ } from '../Application/utils';
25
29
 
26
30
  const {
27
31
  ScreenContext,
@@ -192,7 +196,9 @@ const InputOTP = forwardRef(
192
196
  };
193
197
 
194
198
  const accessibilityID = getAccessibilityID(
195
- `${appId}/${code}/${screenInfo.screenName}/input_${floatingValue}`,
199
+ `${appId}/${code}/${screenInfo.screenName}/input_${cleanNameForTracking(
200
+ floatingValue,
201
+ )}`,
196
202
  );
197
203
 
198
204
  const renderInputView = () => {
@@ -34,9 +34,9 @@ const InputSearch = forwardRef(
34
34
  style,
35
35
  defaultValue,
36
36
  onPressButtonText,
37
- hasColorBG = true,
38
37
  params,
39
38
  componentId,
39
+ hasColorBG = false,
40
40
  ...props
41
41
  }: InputSearchProps,
42
42
  ref,
@@ -18,7 +18,7 @@ import {
18
18
  import {InputTextAreaProps} from './index';
19
19
  import {Icon} from '../Icon';
20
20
  import {ApplicationContext} from '../Application';
21
- import {getAccessibilityID} from '../Application/utils';
21
+ import {cleanNameForTracking, getAccessibilityID} from '../Application/utils';
22
22
 
23
23
  const {
24
24
  ScreenContext,
@@ -98,14 +98,16 @@ const InputTextArea = forwardRef(
98
98
 
99
99
  const accessibilityID = disabled
100
100
  ? getAccessibilityID(
101
- `${appId}/${code}/${screenInfo.screenName}/input_${
102
- floatingValue ?? placeholder
103
- }_disabled`,
101
+ `${appId}/${code}/${
102
+ screenInfo.screenName
103
+ }/input_${cleanNameForTracking(
104
+ floatingValue ?? placeholder,
105
+ )}_disabled`,
104
106
  )
105
107
  : getAccessibilityID(
106
- `${appId}/${code}/${screenInfo.screenName}/input_${
107
- floatingValue ?? placeholder
108
- }`,
108
+ `${appId}/${code}/${
109
+ screenInfo.screenName
110
+ }/input_${cleanNameForTracking(floatingValue ?? placeholder)}`,
109
111
  );
110
112
 
111
113
  return (
package/Radio/index.tsx CHANGED
@@ -6,7 +6,11 @@ import {Text} from '../Text';
6
6
  import styles from './styles';
7
7
  import {ApplicationContext} from '../Application';
8
8
  import {Spacing} from '../Consts';
9
- import {formatNameLength, getAccessibilityID} from '../Application/utils';
9
+ import {
10
+ cleanNameForTracking,
11
+ formatNameLength,
12
+ getAccessibilityID,
13
+ } from '../Application/utils';
10
14
 
11
15
  const {
12
16
  ScreenContext,
@@ -48,7 +52,9 @@ const Radio: FC<RadioProps> = ({
48
52
  }
49
53
 
50
54
  const accessibilityID = getAccessibilityID?.(
51
- `${appId}/${code}/${screenInfo.screenName}/rdo_${label}_${value}`,
55
+ `${appId}/${code}/${screenInfo.screenName}/rdo_${cleanNameForTracking(
56
+ label,
57
+ )}_${value}`,
52
58
  );
53
59
 
54
60
  return (
package/Text/styles.ts CHANGED
@@ -5,7 +5,7 @@ export default {
5
5
  header_s_semibold: {fontSize: 14, lineHeight: 20, fontWeight: '600'},
6
6
  header_xs_semibold: {fontSize: 12, lineHeight: 18, fontWeight: '600'},
7
7
  body_default_regular: {fontSize: 14, lineHeight: 20, fontWeight: '400'},
8
- body_regularstrikethrought: {
8
+ body_default_regularstrikethrought: {
9
9
  fontSize: 14,
10
10
  lineHeight: 20,
11
11
  fontWeight: '400',
package/Text/types.ts CHANGED
@@ -20,7 +20,7 @@ export type Typography =
20
20
  | 'header_s_semibold'
21
21
  | 'header_xs_semibold'
22
22
  | 'body_default_regular'
23
- | 'body_regularstrikethrought'
23
+ | 'body_default_regularstrikethrought'
24
24
  | 'description_default_regular'
25
25
  | 'description_default_regularstrikethrought'
26
26
  | 'description_xs_regular'
package/Title/index.tsx CHANGED
@@ -4,7 +4,6 @@ import styles from './styles';
4
4
  import {Icon} from '../Icon';
5
5
  import {scaleSize, Text} from '../Text';
6
6
  import {ApplicationContext} from '../Application';
7
- import {Colors, Spacing} from '../Consts';
8
7
  import {Badge} from '../Badge';
9
8
  import {Typography} from '../Text/types';
10
9
  import {TitleProps} from './types';
@@ -25,6 +24,7 @@ const Title: FC<TitleProps> = ({
25
24
  buttonSize = 'small',
26
25
  }) => {
27
26
  const {theme} = useContext(ApplicationContext);
27
+
28
28
  const styleSheet: {
29
29
  [key: string]: any;
30
30
  } = styles;
@@ -33,10 +33,6 @@ const Title: FC<TitleProps> = ({
33
33
  const numberOfLines = showLeftAction || !!badgeLabel ? 1 : 2;
34
34
  const buttonTypo: Typography =
35
35
  buttonSize === 'small' ? 'action_xs_bold' : 'action_s_bold';
36
- const widthStyle =
37
- !showLeftAction && !showRightAction && !badgeLabel
38
- ? {width: '100%'}
39
- : {width: '75%'};
40
36
 
41
37
  const renderIcon = () => {
42
38
  if (!icon) return null;
@@ -62,23 +58,16 @@ const Title: FC<TitleProps> = ({
62
58
 
63
59
  const renderContent = () => {
64
60
  return (
65
- <View style={{flex: 1}}>
66
- <View style={[styles.iconLeftView, widthStyle]}>
61
+ <View style={styles.contentView}>
62
+ <View style={[styles.iconLeftView]}>
67
63
  <RNText
68
64
  numberOfLines={numberOfLines}
69
- style={[
70
- styleSheet[typography],
71
- {
72
- fontWeight: 'bold',
73
- marginBottom: Spacing.XS,
74
- },
75
- ]}>
65
+ style={[styleSheet[typography], styles.title]}>
76
66
  {title}
77
67
  </RNText>
68
+
78
69
  <View style={{alignItems: 'center'}}>
79
- {badgeLabel && (
80
- <Badge style={{marginLeft: Spacing.XS}} label={badgeLabel} />
81
- )}
70
+ {badgeLabel && <Badge style={styles.badge} label={badgeLabel} />}
82
71
  </View>
83
72
  {renderActionLeft()}
84
73
  </View>
@@ -95,7 +84,7 @@ const Title: FC<TitleProps> = ({
95
84
  return (
96
85
  <View style={styles.iconLeftView}>
97
86
  {showLeftAction && !showRightAction && (
98
- <View style={[styles.iconLeft, {backgroundColor: Colors.black_06}]}>
87
+ <View style={styles.iconLeft}>
99
88
  <Icon
100
89
  source={'arrow_chevron_right_small'}
101
90
  size={scaleSize(18)}
package/Title/styles.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import {StyleSheet} from 'react-native';
2
2
  import {scaleSize} from '../Text';
3
- import {Radius, Spacing} from '../Consts';
3
+ import {Colors, Radius, Spacing} from '../Consts';
4
4
 
5
5
  export default StyleSheet.create({
6
6
  card_small: {
@@ -39,10 +39,6 @@ export default StyleSheet.create({
39
39
  wrapper: {
40
40
  flexDirection: 'row',
41
41
  },
42
- textContent: {
43
- marginRight: Spacing.S,
44
- flex: 1,
45
- },
46
42
  margin: {
47
43
  marginTop: Spacing.S,
48
44
  },
@@ -64,9 +60,19 @@ export default StyleSheet.create({
64
60
  alignItems: 'center',
65
61
  justifyContent: 'center',
66
62
  marginHorizontal: Spacing.XS,
63
+ backgroundColor: Colors.black_06,
67
64
  },
68
65
  iconLeftView: {
69
66
  flexDirection: 'row',
70
67
  alignItems: 'center',
71
68
  },
69
+ contentView: {marginRight: Spacing.S, flex: 1},
70
+ title: {
71
+ fontWeight: 'bold',
72
+ marginBottom: Spacing.XS,
73
+ flex: 1,
74
+ },
75
+ badge: {
76
+ marginLeft: Spacing.XS,
77
+ },
72
78
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.92.1-rc.1",
3
+ "version": "0.92.1-rc.10",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {
@@ -21,7 +21,6 @@
21
21
  "@react-navigation/native": "5.9.8",
22
22
  "@react-navigation/routers": "5.7.4",
23
23
  "lottie-react-native": "3.3.2",
24
- "d3-shape": "1.3.7",
25
24
  "@react-navigation/stack": "git+https://gitlab.com/momo-platform/react-navigation-stack.git#main"
26
25
  },
27
26
  "peerDependencies": {
@@ -1,507 +0,0 @@
1
- import {BottomTabBarProps} from '@react-navigation/bottom-tabs';
2
- import {LabelPosition} from '@react-navigation/bottom-tabs/src/types';
3
- import BottomTabBarHeightCallbackContext from '@react-navigation/bottom-tabs/src/utils/BottomTabBarHeightCallbackContext';
4
- import BottomTabBarItem from '@react-navigation/bottom-tabs/src/views/BottomTabItem';
5
- import {
6
- CommonActions,
7
- NavigationContext,
8
- NavigationRouteContext,
9
- ParamListBase,
10
- TabNavigationState,
11
- useLinkBuilder,
12
- useTheme,
13
- } from '@react-navigation/native';
14
- import React, {useCallback} from 'react';
15
- import {
16
- Animated,
17
- Dimensions,
18
- LayoutChangeEvent,
19
- Platform,
20
- StyleProp,
21
- StyleSheet,
22
- TouchableOpacity,
23
- useWindowDimensions,
24
- View,
25
- ViewStyle,
26
- } from 'react-native';
27
- import {EdgeInsets, useSafeAreaInsets} from 'react-native-safe-area-context';
28
- import {Radius} from '../../Consts';
29
- import {Icon} from '../../Icon';
30
- import {Text} from '../../Text';
31
- import {CurvedViewComponent} from './CurvedView';
32
- import {getPathUp} from './path';
33
- import {FloatingButtonProps} from '../types';
34
- const {width} = Dimensions.get('window');
35
-
36
- type Props = BottomTabBarProps & {
37
- activeTintColor?: string;
38
- inactiveTintColor?: string;
39
- floatingButton?: FloatingButtonProps;
40
- };
41
-
42
- type Options = {
43
- state: TabNavigationState<ParamListBase>;
44
- layout: {height: number; width: number};
45
- dimensions: {height: number; width: number};
46
- tabStyle: StyleProp<ViewStyle>;
47
- labelPosition: LabelPosition | undefined;
48
- adaptive: boolean | undefined;
49
- };
50
-
51
- const DEFAULT_MAX_TAB_ITEM_WIDTH = 125;
52
- const DEFAULT_TABBAR_HEIGHT = 49;
53
- const COMPACT_TABBAR_HEIGHT = 32;
54
-
55
- const shouldUseHorizontalLabels = ({
56
- state,
57
- layout,
58
- dimensions,
59
- adaptive = true,
60
- labelPosition,
61
- tabStyle,
62
- }: Options) => {
63
- if (labelPosition) {
64
- return labelPosition === 'beside-icon';
65
- }
66
-
67
- if (!adaptive) {
68
- return false;
69
- }
70
-
71
- if (layout.width >= 768) {
72
- // Screen size matches a tablet
73
- let maxTabItemWidth = DEFAULT_MAX_TAB_ITEM_WIDTH;
74
-
75
- const flattenedStyle = StyleSheet.flatten(tabStyle);
76
-
77
- if (flattenedStyle) {
78
- if (typeof flattenedStyle.width === 'number') {
79
- maxTabItemWidth = flattenedStyle.width;
80
- } else if (typeof flattenedStyle.maxWidth === 'number') {
81
- maxTabItemWidth = flattenedStyle.maxWidth;
82
- }
83
- }
84
-
85
- return state.routes.length * maxTabItemWidth <= layout.width;
86
- } else {
87
- return dimensions.width > dimensions.height;
88
- }
89
- };
90
- const getPaddingBottom = (insets: EdgeInsets) =>
91
- Math.max(insets.bottom - Platform.select({ios: 4, default: 0}), 0);
92
-
93
- export const getTabBarHeight = ({
94
- dimensions,
95
- insets,
96
- style,
97
- ...rest
98
- }: Options & {
99
- insets: EdgeInsets;
100
- style: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
101
- }) => {
102
- // @ts-ignore
103
- const customHeight = StyleSheet.flatten(style)?.height;
104
-
105
- if (typeof customHeight === 'number') {
106
- return customHeight;
107
- }
108
-
109
- const isLandscape = dimensions.width > dimensions.height;
110
- const horizontalLabels = shouldUseHorizontalLabels({dimensions, ...rest});
111
- const paddingBottom = getPaddingBottom(insets);
112
-
113
- if (
114
- Platform.OS === 'ios' &&
115
- !Platform.isPad &&
116
- isLandscape &&
117
- horizontalLabels
118
- ) {
119
- return COMPACT_TABBAR_HEIGHT + paddingBottom;
120
- }
121
-
122
- return DEFAULT_TABBAR_HEIGHT + paddingBottom;
123
- };
124
-
125
- export default function BottomTabBar({
126
- state,
127
- navigation,
128
- descriptors,
129
- activeBackgroundColor,
130
- activeTintColor,
131
- adaptive,
132
- allowFontScaling,
133
- inactiveBackgroundColor,
134
- inactiveTintColor,
135
- keyboardHidesTabBar = false,
136
- labelPosition,
137
- labelStyle,
138
- iconStyle,
139
- safeAreaInsets,
140
- showLabel,
141
- style,
142
- tabStyle,
143
- floatingButton,
144
- }: Props) {
145
- const {routes} = state;
146
- const dimensions = useWindowDimensions();
147
- const isFloating = floatingButton?.icon && state.routes.length == 4;
148
- const itemWidth = isFloating
149
- ? dimensions.width / 5
150
- : dimensions.width / state.routes.length;
151
-
152
- const [layout, setLayout] = React.useState({
153
- height: 0,
154
- width: dimensions.width,
155
- });
156
- const buildLink = useLinkBuilder();
157
- const {colors} = useTheme();
158
-
159
- const hasHorizontalLabels = shouldUseHorizontalLabels({
160
- state,
161
- dimensions,
162
- layout,
163
- adaptive,
164
- labelPosition,
165
- tabStyle,
166
- });
167
- const focusedRoute = state.routes[state.index] as any;
168
- const focusedDescriptor = descriptors[focusedRoute.key] as any;
169
- const focusedOptions = focusedDescriptor.options;
170
- const shouldShowTabBar =
171
- focusedOptions.tabBarVisible !== false && !keyboardHidesTabBar;
172
- const [visible] = React.useState(
173
- () => new Animated.Value(shouldShowTabBar ? 1 : 0),
174
- );
175
-
176
- const indicatorAnimated = React.useRef(new Animated.Value(0)).current;
177
-
178
- const visibilityAnimationConfigRef = React.useRef(
179
- focusedOptions.tabBarVisibilityAnimationConfig,
180
- );
181
- const onHeightChange = React.useContext(BottomTabBarHeightCallbackContext);
182
- const [isTabBarHidden, setIsTabBarHidden] = React.useState(!shouldShowTabBar);
183
- React.useEffect(() => {
184
- visibilityAnimationConfigRef.current =
185
- focusedOptions.tabBarVisibilityAnimationConfig;
186
- });
187
-
188
- const handleLayout = (e: LayoutChangeEvent) => {
189
- const {height, width} = e.nativeEvent.layout;
190
-
191
- onHeightChange?.(height);
192
-
193
- setLayout(layout => {
194
- if (height === layout.height && width === layout.width) {
195
- return layout;
196
- } else {
197
- return {
198
- height,
199
- width,
200
- };
201
- }
202
- });
203
- };
204
-
205
- React.useEffect(() => {
206
- const visibilityAnimationConfig = visibilityAnimationConfigRef.current;
207
-
208
- if (shouldShowTabBar) {
209
- const animation =
210
- visibilityAnimationConfig?.show?.animation === 'spring'
211
- ? Animated.spring
212
- : Animated.timing;
213
-
214
- animation(visible, {
215
- toValue: 1,
216
- useNativeDriver: true,
217
- duration: 250,
218
- ...visibilityAnimationConfig?.show?.config,
219
- }).start(({finished}) => {
220
- if (finished) {
221
- setIsTabBarHidden(false);
222
- }
223
- });
224
- } else {
225
- setIsTabBarHidden(true);
226
-
227
- const animation =
228
- visibilityAnimationConfig?.hide?.animation === 'spring'
229
- ? Animated.spring
230
- : Animated.timing;
231
-
232
- animation(visible, {
233
- toValue: 0,
234
- useNativeDriver: true,
235
- duration: 200,
236
- ...visibilityAnimationConfig?.hide?.config,
237
- }).start();
238
- }
239
- }, [visible, shouldShowTabBar]);
240
- const defaultInsets = useSafeAreaInsets();
241
-
242
- const insets = {
243
- top: safeAreaInsets?.top ?? defaultInsets.top,
244
- right: safeAreaInsets?.right ?? defaultInsets.right,
245
- bottom: safeAreaInsets?.bottom ?? defaultInsets.bottom,
246
- left: safeAreaInsets?.left ?? defaultInsets.left,
247
- };
248
- const paddingBottom = getPaddingBottom(insets);
249
-
250
- const leftArr = routes.slice(0, 2);
251
- const rightArr = routes.slice(2, routes.length);
252
- const focusedTab = state?.routes[state.index]?.name || routes[0]?.params;
253
- const tabBarHeight = getTabBarHeight({
254
- state,
255
- insets,
256
- dimensions,
257
- layout,
258
- adaptive,
259
- labelPosition,
260
- tabStyle,
261
- style,
262
- });
263
-
264
- const renderTabItem = useCallback(
265
- (data: any[]) => {
266
- return data?.map((route, index) => {
267
- const focused = route.params.name === focusedTab;
268
- const {options} = descriptors[route.key] as any;
269
- const onPress = () => {
270
- const event = navigation.emit({
271
- type: 'tabPress',
272
- target: route.key,
273
- canPreventDefault: true,
274
- });
275
-
276
- if (!focused && !event.defaultPrevented) {
277
- navigation.dispatch({
278
- ...CommonActions.navigate(route.name),
279
- target: state.key,
280
- });
281
- }
282
- };
283
-
284
- const onLongPress = () => {
285
- navigation.emit({
286
- type: 'tabLongPress',
287
- target: route.key,
288
- });
289
- };
290
-
291
- const label =
292
- options.tabBarLabel !== undefined
293
- ? options.tabBarLabel
294
- : options.title !== undefined
295
- ? options.title
296
- : route.name;
297
-
298
- const accessibilityLabel =
299
- options.tabBarAccessibilityLabel !== undefined
300
- ? options.tabBarAccessibilityLabel
301
- : typeof label === 'string'
302
- ? `${label}, tab, ${index + 1} of ${routes.length}`
303
- : undefined;
304
-
305
- return (
306
- <NavigationContext.Provider
307
- key={route.key}
308
- value={(descriptors as any)[route.key].navigation}>
309
- <NavigationRouteContext.Provider value={route}>
310
- <BottomTabBarItem
311
- route={route}
312
- focused={focused}
313
- horizontal={hasHorizontalLabels}
314
- onPress={onPress}
315
- onLongPress={onLongPress}
316
- accessibilityLabel={accessibilityLabel}
317
- to={buildLink(route.name, route.params)}
318
- testID={options.tabBarTestID}
319
- allowFontScaling={allowFontScaling}
320
- activeTintColor={activeTintColor}
321
- inactiveTintColor={inactiveTintColor}
322
- activeBackgroundColor={activeBackgroundColor}
323
- inactiveBackgroundColor={inactiveBackgroundColor}
324
- button={options.tabBarButton}
325
- icon={options.tabBarIcon}
326
- badge={options.tabBarBadge}
327
- badgeStyle={options.tabBarBadgeStyle}
328
- label={label}
329
- showLabel={showLabel}
330
- labelStyle={labelStyle}
331
- iconStyle={iconStyle}
332
- style={tabStyle}
333
- />
334
- </NavigationRouteContext.Provider>
335
- </NavigationContext.Provider>
336
- );
337
- });
338
- },
339
- [JSON.stringify(routes), state, descriptors, focusedTab],
340
- );
341
-
342
- const renderCenterComponent = useCallback(() => {
343
- const renderFloatingButton = () => {
344
- if (typeof floatingButton?.icon == 'string') {
345
- return <Icon source={floatingButton?.icon} size={32} color={'white'} />;
346
- } else if (floatingButton?.icon) {
347
- return floatingButton?.icon;
348
- } else {
349
- return null;
350
- }
351
- };
352
- if (floatingButton?.icon && state.routes.length !== 4) {
353
- console.warn(
354
- "Can't render floating button with icon when routes length is not 4",
355
- );
356
- return null;
357
- }
358
- return (
359
- <View style={{flex: 1}}>
360
- <TouchableOpacity
361
- onPress={floatingButton?.onPress}
362
- style={styles.floatingContainer}>
363
- <View
364
- style={{
365
- ...styles.floatingContent,
366
- backgroundColor: activeTintColor ?? colors.primary,
367
- }}>
368
- {renderFloatingButton()}
369
- </View>
370
- <View
371
- style={{
372
- top: -6,
373
- backgroundColor: activeTintColor ?? colors.primary,
374
- borderRadius: Radius.XL,
375
- paddingHorizontal: 6,
376
- }}>
377
- <Text
378
- numberOfLines={1}
379
- typography={'description_xs_regular'}
380
- color={'white'}>
381
- {floatingButton?.label}
382
- </Text>
383
- </View>
384
- </TouchableOpacity>
385
- </View>
386
- );
387
- }, [floatingButton]);
388
-
389
- React.useEffect(() => {
390
- Animated.timing(indicatorAnimated, {
391
- toValue:
392
- isFloating && state.index > 1
393
- ? itemWidth * (state.index + 1)
394
- : itemWidth * state.index,
395
- useNativeDriver: false,
396
- duration: 200,
397
- }).start();
398
- }, [state.index, itemWidth, indicatorAnimated, isFloating]);
399
-
400
- return (
401
- <Animated.View
402
- style={[
403
- styles.tabBar,
404
- {
405
- backgroundColor: colors.card,
406
- borderTopColor: colors.border,
407
- },
408
- {
409
- transform: [
410
- {
411
- translateY: visible.interpolate({
412
- inputRange: [0, 1],
413
- outputRange: [
414
- layout.height + paddingBottom + StyleSheet.hairlineWidth,
415
- 0,
416
- ],
417
- }),
418
- },
419
- ],
420
- // Absolutely position the tab bar so that the content is below it
421
- // This is needed to avoid gap at bottom when the tab bar is hidden
422
- position: isTabBarHidden ? 'absolute' : (null as any),
423
- },
424
- {
425
- // height: tabBarHeight,
426
- paddingBottom: paddingBottom - 0,
427
- paddingHorizontal: Math.max(insets.left, insets.right),
428
- },
429
- style,
430
- ]}
431
- pointerEvents={isTabBarHidden ? 'none' : 'auto'}
432
- onLayout={handleLayout}>
433
- <View style={styles.content}>
434
- {isFloating && (
435
- <CurvedViewComponent
436
- width={width}
437
- height={61 + Math.min(insets.bottom, 21)}
438
- style={styles.curved}
439
- bgColor="white"
440
- path={getPathUp(width, tabBarHeight, 50, false, 'CENTER')}
441
- />
442
- )}
443
- <Animated.View
444
- style={{
445
- width: itemWidth,
446
- position: 'absolute',
447
- top: 0,
448
- transform: [{translateX: indicatorAnimated}],
449
- }}>
450
- <View
451
- style={[
452
- styles.indicator,
453
- {backgroundColor: activeTintColor ?? colors.primary},
454
- ]}
455
- />
456
- </Animated.View>
457
-
458
- {renderTabItem(leftArr)}
459
- {renderCenterComponent()}
460
- {renderTabItem(rightArr)}
461
- </View>
462
- </Animated.View>
463
- );
464
- }
465
-
466
- const styles = StyleSheet.create({
467
- curved: {
468
- position: 'absolute',
469
- bottom: 0,
470
- // backgroundColor: 'red',
471
- },
472
- content: {
473
- flex: 1,
474
- flexDirection: 'row',
475
- // backgroundColor: 'red',
476
- },
477
- tabBar: {
478
- left: 0,
479
- right: 0,
480
- bottom: 0,
481
- borderTopWidth: StyleSheet.hairlineWidth,
482
- elevation: 8,
483
- },
484
- floatingContainer: {
485
- position: 'absolute',
486
- alignItems: 'center',
487
- left: 0,
488
- right: 0,
489
- bottom: -6,
490
- },
491
- floatingContent: {
492
- alignItems: 'center',
493
- justifyContent: 'center',
494
- height: 48,
495
- width: 48,
496
-
497
- borderRadius: 100,
498
- top: -12,
499
- },
500
- indicator: {
501
- height: 2,
502
- width: 44,
503
- alignSelf: 'center',
504
- borderBottomLeftRadius: 1,
505
- borderBottomRightRadius: 1,
506
- },
507
- });
@@ -1,27 +0,0 @@
1
- import React from 'react';
2
- import {StyleProp, View, ViewStyle} from 'react-native';
3
- import {Path, Svg} from 'react-native-svg';
4
-
5
- interface ICurvedView {
6
- style?: StyleProp<ViewStyle>;
7
- width: number;
8
- height: number;
9
- bgColor: string;
10
- path: any;
11
- }
12
-
13
- export type CurvedView = React.FC<ICurvedView>;
14
-
15
- const CurvedContainer: CurvedView = props => {
16
- const {style, width, height, bgColor, path} = props;
17
-
18
- return (
19
- <View style={style} pointerEvents="none">
20
- <Svg width={width} height={height}>
21
- <Path fill={bgColor} {...{d: path}} />
22
- </Svg>
23
- </View>
24
- );
25
- };
26
-
27
- export const CurvedViewComponent: CurvedView = React.memo(CurvedContainer);
@@ -1,202 +0,0 @@
1
- import * as shape from 'd3-shape';
2
- import {Platform} from 'react-native';
3
-
4
- export const TABBAR_ITEM_TOP = Platform.OS === 'ios' ? 8 : 4;
5
- export const CIRCLE_SIZE = 50;
6
- export const TABBAR_ITEM_ICON_SIZE = 28;
7
- export const CIRCLE_POSITION_TOP =
8
- CIRCLE_SIZE - TABBAR_ITEM_ICON_SIZE - TABBAR_ITEM_TOP;
9
- export const CIRCLE_OVER_SIZE = 10;
10
- export const CIRCLE_OVER_TOP = CIRCLE_POSITION_TOP + CIRCLE_OVER_SIZE;
11
-
12
- export const scale = (number: number) => {
13
- return number;
14
- };
15
-
16
- const line = (width: number, height: number) => {
17
- const path = (shape as any)
18
- .line()
19
- .x((d: {x: any}) => d.x)
20
- .y((d: {y: any}) => d.y)([
21
- {x: width / 2, y: scale(CIRCLE_OVER_TOP)},
22
- {x: 0, y: scale(CIRCLE_OVER_TOP)},
23
- {x: 0, y: height},
24
- {x: width, y: height},
25
- {x: width, y: scale(CIRCLE_OVER_TOP)},
26
- {x: width / 2, y: scale(CIRCLE_OVER_TOP)},
27
- ]);
28
- //M195,30L0,30L0,106L390,106L390,30L195,30
29
- // MaxApi.copyToClipboard(path, 'copied');
30
- return path;
31
- };
32
-
33
- //** Path Line Border Left Right Up */
34
- const lineBorder = (width: number, height: number) => {
35
- const border = (shape as any)
36
- .line()
37
- .x((d: {x: any}) => d.x)
38
- .y((d: {y: any}) => d.y)
39
- .curve(shape.curveBasis)([
40
- // right
41
- {x: width / 2, y: scale(30)},
42
- {x: width - scale(20), y: scale(30)},
43
- {x: width - scale(10), y: scale(32)},
44
- {x: width - scale(2), y: scale(40)},
45
- {x: width, y: scale(50)},
46
- {x: width, y: height},
47
- {x: width, y: height},
48
- // bottom
49
- {x: width, y: height},
50
- {x: 0, y: height},
51
- // left
52
- {x: 0, y: height},
53
- {x: 0, y: height},
54
- {x: 0, y: scale(50)},
55
- {x: 0 + scale(2), y: scale(40)},
56
- {x: 0 + scale(10), y: scale(32)},
57
- {x: 0 + scale(20), y: scale(30)},
58
- {x: width / 2, y: scale(30)},
59
- ]);
60
-
61
- return border;
62
- };
63
-
64
- //** Path Line Border Left Right Down */
65
- const lineBorderLeft = (width: number, height: number) => {
66
- const border = (shape as any)
67
- .line()
68
- .x((d: {x: any}) => d.x)
69
- .y((d: {y: any}) => d.y)
70
- .curve(shape.curveBasis)([
71
- // right
72
- {x: width, y: scale(30)},
73
- {x: width, y: height},
74
- {x: width, y: height},
75
- // bottom
76
- {x: width, y: height},
77
- {x: 0, y: height},
78
- // left
79
- {x: 0, y: height},
80
- {x: 0, y: height},
81
- {x: 0, y: scale(50)},
82
- {x: 0 + scale(2), y: scale(40)},
83
- {x: 0 + scale(10), y: scale(32)},
84
- {x: 0 + scale(20), y: scale(30)},
85
- {x: width / 2, y: scale(30)},
86
- ]);
87
-
88
- return border;
89
- };
90
-
91
- //** Path Line Border Left Right Down */
92
- const lineBorderRight = (width: number, height: number) => {
93
- const border = (shape as any)
94
- .line()
95
- .x((d: {x: any}) => d.x)
96
- .y((d: {y: any}) => d.y)
97
- .curve(shape.curveBasis)([
98
- // right
99
- {x: width / 2, y: scale(30)},
100
- {x: width - scale(20), y: scale(30)},
101
- {x: width - scale(10), y: scale(32)},
102
- {x: width - scale(2), y: scale(40)},
103
- {x: width, y: scale(50)},
104
- {x: width, y: height},
105
- {x: width, y: height},
106
- // bottom
107
- {x: width, y: height},
108
- {x: 0, y: height},
109
- // left
110
- {x: 0, y: height},
111
- {x: 0, y: height},
112
- {x: 0, y: scale(30)},
113
- ]);
114
-
115
- return border;
116
- };
117
-
118
- //** Path Curved Up */
119
- const lineCurved = (position: number, circle: number) => {
120
- const circleR = circle / 2;
121
- const circleRightPos = circle + position;
122
- const circleOverR = circle / 2 + CIRCLE_OVER_SIZE;
123
- const centerX = (position + circleRightPos) / 2; //195
124
- const centerY = CIRCLE_OVER_SIZE + circleR; //33
125
- // {"circle":50,"position":170,"centerX":195,"circleWidth":220}
126
- // WIDTH_SCREEN: 390
127
-
128
- const curved = (shape as any)
129
- .line()
130
- .x((d: {x: any}) => d.x)
131
- .y((d: {y: any}) => d.y)
132
- .curve(shape.curveBundle.beta(1))([
133
- {x: centerX - circleOverR - scale(50), y: CIRCLE_OVER_TOP},
134
- {x: centerX - circleOverR, y: CIRCLE_OVER_TOP}, //y 20
135
- {
136
- x: centerX + circleOverR * Math.cos(-Math.PI / 2 - Math.PI / 4),
137
- y: centerY + circleOverR * Math.sin(-Math.PI / 2 - Math.PI / 4),
138
- },
139
-
140
- {x: centerX, y: 0},
141
-
142
- {
143
- x: centerX + circleOverR * Math.cos(-Math.PI / 2 + Math.PI / 4),
144
- y: centerY + circleOverR * Math.sin(-Math.PI / 2 + Math.PI / 4),
145
- },
146
- {x: centerX + circleOverR, y: CIRCLE_OVER_TOP}, //y 20
147
- {x: centerX + circleOverR + scale(50), y: CIRCLE_OVER_TOP},
148
- ]);
149
-
150
- return curved;
151
- };
152
-
153
- export const getPathUp = (
154
- width: number,
155
- iHeight: number,
156
- centerWidth = 50,
157
- borderTopLeftRight = false,
158
- position: 'CENTER' | 'LEFT' | 'RIGHT',
159
- ) => {
160
- const height = scale(iHeight);
161
- const circleWidth = scale(centerWidth);
162
-
163
- if (borderTopLeftRight && position === 'LEFT') {
164
- return `${lineBorderRight(width, height)} ${lineCurved(
165
- circleWidth / 1.7,
166
- circleWidth,
167
- )}`;
168
- }
169
-
170
- if (borderTopLeftRight && position === 'RIGHT') {
171
- return `${lineBorderLeft(width, height)} ${lineCurved(
172
- width - circleWidth * 1.6,
173
- circleWidth,
174
- )}`;
175
- }
176
-
177
- if (borderTopLeftRight && position === 'CENTER') {
178
- return `${lineBorder(width, height)} ${lineCurved(
179
- width / 2 - circleWidth / 2,
180
- circleWidth,
181
- )}`;
182
- }
183
-
184
- if (position === 'LEFT') {
185
- return `${line(width, height)} ${lineCurved(
186
- circleWidth / 1.7,
187
- circleWidth,
188
- )}`;
189
- }
190
-
191
- if (position === 'RIGHT') {
192
- return `${line(width, height)} ${lineCurved(
193
- width - circleWidth * 1.6,
194
- circleWidth,
195
- )}`;
196
- }
197
-
198
- return `${line(width, height)} ${lineCurved(
199
- width / 2 - circleWidth / 2,
200
- circleWidth,
201
- )}`;
202
- };