@momo-kits/foundation 0.77.8-beta.3 → 0.77.8-beta.5

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.
@@ -8,17 +8,27 @@ import {ApplicationContext} from './index';
8
8
 
9
9
  const Tab = createBottomTabNavigator();
10
10
 
11
- const BottomTab: React.FC<BottomTabProps> = ({tabs, listeners}) => {
11
+ const BottomTab: React.FC<BottomTabProps> = ({tabs, listeners, navigation}) => {
12
12
  const {theme} = useContext(ApplicationContext);
13
+
14
+ const defaultListener = {
15
+ focus: (item: any) => {
16
+ const focusItem = tabs.find(i => item.target.includes(i.name));
17
+ if (focusItem?.options) {
18
+ navigation?.setOptions?.(focusItem.options);
19
+ }
20
+ },
21
+ };
22
+
13
23
  return (
14
- <Tab.Navigator>
24
+ <Tab.Navigator backBehavior={'firstRoute'}>
15
25
  {tabs.map(item => {
16
26
  return (
17
27
  <Tab.Screen
18
28
  key={item.name}
19
29
  name={item.name}
20
30
  component={item.screen}
21
- listeners={listeners}
31
+ listeners={listeners ?? defaultListener}
22
32
  options={{
23
33
  tabBarLabel: ({color, focused}) => {
24
34
  return (
@@ -1,12 +1,13 @@
1
- import React, {useContext, useEffect, useMemo, useRef} from 'react';
1
+ import React, {useContext, useEffect, useRef} from 'react';
2
2
  import {
3
+ Dimensions,
3
4
  KeyboardAvoidingView,
4
5
  Platform,
5
6
  Pressable,
6
7
  StyleSheet,
7
8
  View,
8
9
  } from 'react-native';
9
- import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
10
+ import {SafeAreaView} from 'react-native-safe-area-context';
10
11
  import {BottomSheetBackdrop, BottomSheetModal} from '@gorhom/bottom-sheet';
11
12
  import Navigation from './Navigation';
12
13
  import {BottomSheetParams, ModalParams} from './types';
@@ -84,7 +85,12 @@ const Modal: React.FC<ModalParams> = props => {
84
85
 
85
86
  const BottomSheet: React.FC<BottomSheetParams> = props => {
86
87
  const {theme, navigator} = useContext(ApplicationContext);
87
- const {screen, options}: BottomSheetParams = props.route.params;
88
+ const {height} = Dimensions.get('window');
89
+ const {
90
+ screen,
91
+ options,
92
+ snapPoints = [height * 0.5, height * 0.9],
93
+ }: BottomSheetParams = props.route.params;
88
94
  const Component = useRef(screen).current;
89
95
  const navigation = new Navigation(props.navigation, theme);
90
96
  const params = {
@@ -94,11 +100,14 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
94
100
 
95
101
  delete params.screen;
96
102
 
97
- const snapPoints = useMemo(() => ['50%', '90%'], []);
98
- const {bottom} = useSafeAreaInsets();
99
103
  const bottomSheetRef = useRef<any>(null);
100
104
  const mountedRef = useRef(true);
101
105
 
106
+ let backgroundColor = theme.colors.background.default;
107
+ if (options?.surface) {
108
+ backgroundColor = theme.colors.background.surface;
109
+ }
110
+
102
111
  useEffect(() => {
103
112
  bottomSheetRef.current?.present();
104
113
  return () => {
@@ -114,16 +123,11 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
114
123
  />
115
124
  );
116
125
 
117
- let backgroundColor = theme.colors.background.default;
118
- if (options?.surface) {
119
- backgroundColor = theme.colors.background.surface;
120
- }
121
-
122
126
  return (
123
127
  <SafeAreaView style={Styles.flex}>
124
128
  <BottomSheetModal
125
129
  ref={bottomSheetRef}
126
- snapPoints={snapPoints}
130
+ snapPoints={snapPoints?.map(item => item + 68)}
127
131
  onDismiss={() => {
128
132
  if (mountedRef.current) {
129
133
  navigator?.pop();
@@ -131,9 +135,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
131
135
  }}
132
136
  handleComponent={null}
133
137
  backdropComponent={backdropComponent}>
134
- <View
135
- style={{paddingBottom: bottom}}
136
- accessibilityLabel={'BottomSheet'}>
138
+ <View accessibilityLabel={'BottomSheet'}>
137
139
  <View style={[styles.sheetContainer, {backgroundColor}]}>
138
140
  <View style={styles.indicatorContainer}>
139
141
  <View
@@ -108,6 +108,7 @@ export interface ModalParams extends ScreenParams {
108
108
  export interface BottomSheetParams extends ScreenParams {
109
109
  [key: string]: any;
110
110
  onDismiss?: () => void;
111
+ snapPoints?: ReadonlyArray<number>;
111
112
  }
112
113
 
113
114
  export type NavigationButtonProps = {
@@ -171,10 +172,12 @@ export type BottomTabItemProps = {
171
172
  showDot?: boolean;
172
173
  badgeLabel?: string;
173
174
  screen: Screen;
175
+ options: NavigationOptions;
174
176
  };
175
177
 
176
178
  export type BottomTabProps = {
177
179
  tabs: BottomTabItemProps[];
180
+ navigation: Navigation;
178
181
  listeners?: {
179
182
  tabPress?: any;
180
183
  focus?: any;
package/Button/index.tsx CHANGED
@@ -11,6 +11,7 @@ import {Typography} from '../Text/types';
11
11
  import {Colors} from '../Consts';
12
12
  import styles from './styles';
13
13
  import {Icon} from '../Icon';
14
+ import {Loader} from '../Loader';
14
15
 
15
16
  export interface ButtonProps extends TouchableOpacityProps {
16
17
  /**
@@ -55,6 +56,11 @@ export interface ButtonProps extends TouchableOpacityProps {
55
56
  */
56
57
  useTintColor?: boolean;
57
58
 
59
+ /**
60
+ * If true, the icon of button will use be show indicator loading.
61
+ */
62
+ loading?: boolean;
63
+
58
64
  /**
59
65
  * A callback function that is called when the button is pressed.
60
66
  */
@@ -68,6 +74,7 @@ const Button: FC<ButtonProps> = ({
68
74
  full = true,
69
75
  iconRight,
70
76
  iconLeft,
77
+ loading = false,
71
78
  title = 'Button',
72
79
  onPress,
73
80
  ...rest
@@ -223,6 +230,19 @@ const Button: FC<ButtonProps> = ({
223
230
  const renderTrailing = () => {
224
231
  const marginLeft = getIconSpace();
225
232
  const color = useTintColor ? getTextColor() : undefined;
233
+ if (loading) {
234
+ const iconSize = getIconSize();
235
+ const color = getTextColor();
236
+ return (
237
+ <View
238
+ style={[
239
+ styles.trailing,
240
+ {width: iconSize, height: iconSize, marginLeft},
241
+ ]}>
242
+ <Loader type="spinner" color={color} />
243
+ </View>
244
+ );
245
+ }
226
246
  if (iconRight) {
227
247
  const iconSize = getIconSize();
228
248
  return (
@@ -36,7 +36,7 @@ const CheckBox: FC<CheckBoxProps> = ({
36
36
 
37
37
  return (
38
38
  <TouchableOpacity
39
- accessibilityLabel={`CheckBox/${label}`}
39
+ accessibilityLabel={`CheckBox/${label}/${value}`}
40
40
  activeOpacity={0.8}
41
41
  onPress={() => onChange?.(!value)}
42
42
  disabled={disabled}
@@ -11,5 +11,5 @@ export default StyleSheet.create({
11
11
  justifyContent: 'center',
12
12
  alignItems: 'center',
13
13
  },
14
- container: {flexDirection: 'row'},
14
+ container: {flexDirection: 'row', alignItems: 'center'},
15
15
  });
package/Input/Input.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, {FC, useContext, useEffect, useRef, useState} from 'react';
1
+ import React, {FC, forwardRef, useContext, useRef, useState} from 'react';
2
2
  import {
3
3
  NativeSyntheticEvent,
4
4
  TextInput,
@@ -12,124 +12,144 @@ import {ErrorView, FloatingView, getBorderColor, getSizeStyle} from './common';
12
12
  import {InputProps} from './index';
13
13
  import {Icon} from '../Icon';
14
14
  import {checkTyping} from './utils';
15
+ import {Loader} from '../Loader';
15
16
 
16
- const Input: FC<InputProps> = ({
17
- value,
18
- onChangeText,
19
- floatingValue,
20
- floatingIcon,
21
- size,
22
- placeholder,
23
- onBlur,
24
- onFocus,
25
- errorMessage,
26
- icon,
27
- disabled,
28
- floatingIconColor,
29
- iconColor,
30
- required,
31
- ...props
32
- }) => {
33
- const {theme} = useContext(ApplicationContext);
34
- const [focused, setFocused] = useState(false);
35
- const [haveValue, setHaveValue] = useState(!!value || !!props.defaultValue);
17
+ const Input: FC<InputProps> = forwardRef(
18
+ (
19
+ {
20
+ value,
21
+ onChangeText,
22
+ floatingValue,
23
+ floatingIcon,
24
+ size,
25
+ placeholder,
26
+ onBlur,
27
+ onFocus,
28
+ errorMessage,
29
+ icon,
30
+ disabled,
31
+ floatingIconColor,
32
+ iconColor,
33
+ required,
34
+ errorSpacing,
35
+ loading,
36
+ ...props
37
+ },
38
+ ref,
39
+ ) => {
40
+ const {theme} = useContext(ApplicationContext);
41
+ const [focused, setFocused] = useState(false);
42
+ const [haveValue, setHaveValue] = useState(!!value || !!props.defaultValue);
43
+ const inputRef = (ref as React.RefObject<TextInput>) ?? useRef<TextInput>();
36
44
 
37
- const inputRef = useRef<any>(null);
45
+ const onClearText = () => {
46
+ inputRef?.current?.clear();
47
+ _onChangeText('');
48
+ };
38
49
 
39
- const onClearText = () => {
40
- inputRef?.current?.clear();
41
- _onChangeText('');
42
- };
50
+ const _onChangeText = (text: string) => {
51
+ checkTyping(text, haveValue, setHaveValue);
52
+ onChangeText?.(text);
53
+ };
43
54
 
44
- const _onChangeText = (text: string) => {
45
- checkTyping(text, haveValue, setHaveValue);
46
- onChangeText?.(text);
47
- };
55
+ const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
56
+ setFocused(true);
57
+ onFocus?.(e);
58
+ };
48
59
 
49
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
50
- setFocused(true);
51
- onFocus?.(e);
52
- };
60
+ const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
61
+ setFocused(false);
62
+ onBlur?.(e);
63
+ };
53
64
 
54
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
55
- setFocused(false);
56
- onBlur?.(e);
57
- };
65
+ const renderIcon = (color: string | undefined) => {
66
+ if (loading) {
67
+ return <Loader type={'spinner'} color={color} />;
68
+ }
69
+ if (icon) {
70
+ return <Icon color={color} source={icon} size={24} />;
71
+ }
72
+ };
58
73
 
59
- const renderInputView = () => {
60
- const disabledColor = theme.colors.text.disable;
61
- let textColor = theme.colors.text.default;
62
- let placeholderColor = theme.colors.text.hint;
63
- let iconTintColor = iconColor;
74
+ const renderInputView = () => {
75
+ const disabledColor = theme.colors.text.disable;
76
+ let textColor = theme.colors.text.default;
77
+ let placeholderColor = theme.colors.text.hint;
78
+ let iconTintColor = iconColor;
64
79
 
65
- if (disabled) {
66
- textColor = disabledColor;
67
- placeholderColor = disabledColor;
68
- iconTintColor = disabledColor;
69
- }
80
+ if (disabled) {
81
+ textColor = disabledColor;
82
+ placeholderColor = disabledColor;
83
+ iconTintColor = disabledColor;
84
+ }
70
85
 
71
- return (
72
- <View
73
- style={[
74
- getSizeStyle(size),
75
- getBorderColor(focused, errorMessage, disabled),
76
- styles.inputWrapper,
77
- {backgroundColor: theme.colors.background.surface},
78
- ]}>
79
- <FloatingView
80
- floatingValue={floatingValue}
81
- floatingIconColor={floatingIconColor}
82
- disabled={disabled}
83
- required={required}
84
- floatingIcon={floatingIcon}
85
- />
86
- <View style={styles.inputView}>
87
- <TextInput
88
- {...props}
89
- accessibilityLabel={`TextInput`}
90
- editable={!disabled}
91
- textAlignVertical="top"
92
- ref={inputRef}
93
- style={[
94
- styles.input,
95
- {
96
- color: textColor,
97
- fontFamily: `${theme.font}-Regular`,
98
- },
99
- ]}
100
- textBreakStrategy="highQuality"
101
- value={value}
102
- onChangeText={_onChangeText}
103
- onFocus={_onFocus}
104
- onBlur={_onBlur}
105
- placeholder={placeholder}
106
- selectionColor={theme.colors.primary}
107
- placeholderTextColor={placeholderColor}
86
+ return (
87
+ <View
88
+ style={[
89
+ styles.inputWrapper,
90
+ {backgroundColor: theme.colors.background.surface},
91
+ getSizeStyle(size),
92
+ getBorderColor(theme, focused, errorMessage, disabled),
93
+ ]}>
94
+ <FloatingView
95
+ floatingValue={floatingValue}
96
+ floatingIconColor={floatingIconColor}
97
+ disabled={disabled}
98
+ required={required}
99
+ floatingIcon={floatingIcon}
108
100
  />
101
+ <View style={styles.inputView}>
102
+ <TextInput
103
+ {...props}
104
+ accessibilityLabel={`TextInput`}
105
+ editable={!disabled}
106
+ textAlignVertical="top"
107
+ ref={inputRef}
108
+ style={[
109
+ styles.input,
110
+ {
111
+ color: textColor,
112
+ fontFamily: `${theme.font}-Regular`,
113
+ },
114
+ ]}
115
+ textBreakStrategy="highQuality"
116
+ value={value}
117
+ onChangeText={_onChangeText}
118
+ onFocus={_onFocus}
119
+ onBlur={_onBlur}
120
+ placeholder={placeholder}
121
+ selectionColor={theme.colors.primary}
122
+ placeholderTextColor={placeholderColor}
123
+ />
124
+ </View>
125
+ <View style={styles.iconView}>
126
+ {focused && haveValue && (
127
+ <TouchableOpacity
128
+ style={styles.iconWrapper}
129
+ onPress={onClearText}>
130
+ <Icon
131
+ source="24_navigation_close_circle_full"
132
+ size={16}
133
+ color={theme.colors.text.hint}
134
+ />
135
+ </TouchableOpacity>
136
+ )}
137
+ {renderIcon(iconTintColor)}
138
+ </View>
109
139
  </View>
110
- <View style={styles.iconView}>
111
- {focused && haveValue && (
112
- <TouchableOpacity style={styles.iconWrapper} onPress={onClearText}>
113
- <Icon
114
- source="24_navigation_close_circle_full"
115
- size={16}
116
- color={theme.colors.text.hint}
117
- />
118
- </TouchableOpacity>
119
- )}
120
- {!!icon && <Icon color={iconTintColor} source={icon} size={24} />}
121
- </View>
140
+ );
141
+ };
142
+
143
+ return (
144
+ <View
145
+ style={styles.wrapper}
146
+ accessibilityLabel={`Input/${floatingValue}`}>
147
+ {renderInputView()}
148
+ <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
122
149
  </View>
123
150
  );
124
- };
125
-
126
- return (
127
- <View style={styles.wrapper} accessibilityLabel={`Input/${floatingValue}`}>
128
- {renderInputView()}
129
- <ErrorView errorMessage={errorMessage} />
130
- </View>
131
- );
132
- };
151
+ },
152
+ );
133
153
 
134
154
  Input.defaultProps = {
135
155
  size: 'small',