@momo-kits/foundation 0.81.27 → 0.81.28

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,13 +1,29 @@
1
- import React, {useContext} from 'react';
1
+ import React, {useContext, useEffect} from 'react';
2
2
  import {View} from 'react-native';
3
- import {BottomTabProps} from './types';
3
+ import {BottomTabProps, NavigationScreenProps} from './types';
4
4
  import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
5
+ import {createStackNavigator} from '@react-navigation/stack';
5
6
  import {Icon} from '../Icon';
6
7
  import {Text} from '../Text';
7
8
  import {ApplicationContext} from './index';
9
+ import {getStackOptions} from './utils';
10
+ import StackScreen from './StackScreen';
8
11
 
9
12
  const Tab = createBottomTabNavigator();
13
+ const Stack = createStackNavigator();
10
14
 
15
+ const TabScreen: React.FC<NavigationScreenProps> = ({route}) => {
16
+ return (
17
+ <Stack.Navigator>
18
+ <Stack.Screen
19
+ name="Stack"
20
+ component={StackScreen}
21
+ initialParams={{screen: route.params?.screen}}
22
+ options={{...getStackOptions(), ...route.params?.options}}
23
+ />
24
+ </Stack.Navigator>
25
+ );
26
+ };
11
27
  const BottomTab: React.FC<BottomTabProps> = ({
12
28
  tabs,
13
29
  listeners,
@@ -16,14 +32,13 @@ const BottomTab: React.FC<BottomTabProps> = ({
16
32
  }) => {
17
33
  const {theme} = useContext(ApplicationContext);
18
34
 
19
- const defaultListener = {
20
- focus: (item: any) => {
21
- const focusItem = tabs.find(i => item.target.includes(i.name));
22
- if (focusItem?.options) {
23
- navigation?.setOptions?.(focusItem.options);
24
- }
25
- },
26
- };
35
+ useEffect(() => {
36
+ navigation?.setOptions({
37
+ headerShown: false,
38
+ headerTransparent: true,
39
+ headerBackground: undefined,
40
+ });
41
+ }, []);
27
42
 
28
43
  return (
29
44
  <Tab.Navigator
@@ -34,8 +49,9 @@ const BottomTab: React.FC<BottomTabProps> = ({
34
49
  <Tab.Screen
35
50
  key={item.name}
36
51
  name={item.name}
37
- component={item.screen}
38
- listeners={listeners ?? defaultListener}
52
+ component={TabScreen}
53
+ initialParams={item}
54
+ listeners={listeners}
39
55
  options={{
40
56
  tabBarLabel: ({color, focused}) => {
41
57
  return (
@@ -94,8 +94,8 @@ const styles = StyleSheet.create({
94
94
  },
95
95
  badge: {
96
96
  position: 'absolute',
97
- top: -6,
98
- right: -6,
97
+ top: -8,
98
+ right: -8,
99
99
  },
100
100
  });
101
101
 
package/Badge/Badge.tsx CHANGED
@@ -4,7 +4,7 @@ import {View} from 'react-native';
4
4
  import {ApplicationContext} from '../Application';
5
5
  import {BadgeProps} from './types';
6
6
  import styles from './styles';
7
- import {Colors} from '../Consts';
7
+ import {Colors, Spacing} from '../Consts';
8
8
  import {Text} from '../Text';
9
9
 
10
10
  const Badge: FC<BadgeProps> = ({label = 'Label', style, backgroundColor}) => {
package/Badge/styles.ts CHANGED
@@ -4,15 +4,13 @@ import {scaleSize} from '../Text';
4
4
 
5
5
  export default StyleSheet.create({
6
6
  badge: {
7
- paddingHorizontal: Spacing.XS,
8
7
  borderRadius: Radius.M,
9
8
  justifyContent: 'center',
10
- alignItems: 'center',
11
9
  height: scaleSize(16),
12
10
  flexDirection: 'row',
13
- borderWidth: 1,
14
11
  borderColor: Colors.black_01,
15
- alignSelf: 'baseline',
12
+ paddingHorizontal: Spacing.XS,
13
+ alignItems: 'center',
16
14
  },
17
15
  dot: {
18
16
  width: 16,
package/Input/Input.tsx CHANGED
@@ -36,6 +36,7 @@ const Input = forwardRef(
36
36
  leadingIcon,
37
37
  leadingIconColor,
38
38
  fontWeight = 'Regular',
39
+ onPressIcon,
39
40
  style,
40
41
  ...props
41
42
  }: InputProps,
@@ -72,7 +73,11 @@ const Input = forwardRef(
72
73
  return <Loader type={'spinner'} color={color} />;
73
74
  }
74
75
  if (icon) {
75
- return <Icon color={color} source={icon} size={24} />;
76
+ return (
77
+ <TouchableOpacity onPress={onPressIcon} disabled={!onPressIcon}>
78
+ <Icon color={color} source={icon} size={24} />
79
+ </TouchableOpacity>
80
+ );
76
81
  }
77
82
  };
78
83
 
@@ -1,4 +1,12 @@
1
- import React, {FC, useContext, useEffect, useRef, useState} from 'react';
1
+ import React, {
2
+ FC,
3
+ forwardRef,
4
+ useContext,
5
+ useEffect,
6
+ useImperativeHandle,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
2
10
  import {
3
11
  Animated,
4
12
  NativeSyntheticEvent,
@@ -56,156 +64,169 @@ const OTPCaret: FC<CaretProps> = ({index, length}) => {
56
64
  );
57
65
  };
58
66
 
59
- const InputOTP: FC<InputOTPProps> = ({
60
- length,
61
- errorMessage,
62
- errorSpacing,
63
- floatingValue,
64
- floatingIconColor,
65
- floatingIcon,
66
- placeholder,
67
- onChangeText,
68
- onFocus,
69
- onBlur,
70
- dataType = 'number',
71
- style,
72
- ...props
73
- }) => {
74
- const MAX_LENGTH = 10;
75
- const [value, setValue] = useState('');
76
- const [focused, setFocused] = useState(false);
77
- const inputRef = useRef<TextInput>(null);
78
- const {theme} = useContext(ApplicationContext);
79
- const accessibilityLabel = `InputOTP/${floatingValue ?? placeholder}`;
80
-
81
- const onFocusInput = () => {
82
- setFocused(true);
83
- inputRef.current?.focus();
84
- };
85
-
86
- const onBlurInput = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
87
- setFocused(false);
88
- onBlur?.(e);
89
- };
90
-
91
- const _onChangeText = (text: string) => {
92
- if (text.length > MAX_LENGTH) return;
93
- if (dataType === 'number' && isNaN(Number(text))) return;
94
- if (
95
- !isNaN(Number(text)) &&
96
- value.length >= Number(length) &&
97
- text.length >= value.length
98
- )
99
- return;
100
-
101
- setValue(text);
102
- onChangeText?.(text);
103
- };
104
-
105
- const renderInputs = (length: number) => {
106
- const TextInputs: React.ReactNode[] = [];
107
- for (let i = 0; i < length; i++) {
108
- TextInputs.push(
109
- <View>
110
- {focused && value.length === i ? (
111
- <OTPCaret index={i} length={length} />
112
- ) : (
113
- <Text
114
- style={i !== length - 1 && {marginRight: Spacing.L}}
115
- color={
116
- !!value[i] ? theme.colors.text.default : theme.colors.text.hint
117
- }
118
- typography={!!value[i] ? 'title_s' : 'paragraph_default'}>
119
- {value[i] || '-'}
120
- </Text>
121
- )}
122
- </View>,
123
- );
124
- }
125
- return TextInputs;
126
- };
67
+ const InputOTP = forwardRef(
68
+ (
69
+ {
70
+ length,
71
+ errorMessage,
72
+ errorSpacing,
73
+ floatingValue,
74
+ floatingIconColor,
75
+ floatingIcon,
76
+ placeholder,
77
+ onChangeText,
78
+ onFocus,
79
+ onBlur,
80
+ dataType = 'number',
81
+ style,
82
+ ...props
83
+ }: InputOTPProps,
84
+ ref,
85
+ ) => {
86
+ const MAX_LENGTH = 10;
87
+ const [value, setValue] = useState('');
88
+ const [focused, setFocused] = useState(false);
89
+ const inputRef = useRef<TextInput>(null);
90
+ const {theme} = useContext(ApplicationContext);
91
+ const accessibilityLabel = `InputOTP/${floatingValue ?? placeholder}`;
127
92
 
128
- const renderUnidentifiedInputs = () => {
129
- const splitValue = value.split('');
93
+ useImperativeHandle(ref, () => ({
94
+ onChangeText: (text: string) => {
95
+ _onChangeText(text);
96
+ },
97
+ }));
130
98
 
131
- if (!focused && value.length === 0 && !!placeholder) {
132
- return (
133
- <Text color={theme.colors.text.hint} typography={'description_s'}>
134
- {placeholder}
135
- </Text>
136
- );
137
- }
99
+ const onFocusInput = () => {
100
+ setFocused(true);
101
+ inputRef.current?.focus();
102
+ };
138
103
 
139
- return (
140
- <View style={{flexDirection: 'row'}}>
141
- {splitValue.map((letter, index) => {
142
- return (
143
- <Text
144
- style={
145
- (index !== value.length - 1 ||
146
- (focused && value.length !== MAX_LENGTH)) && {
147
- marginRight: Spacing.L,
104
+ const onBlurInput = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
105
+ setFocused(false);
106
+ onBlur?.(e);
107
+ };
108
+
109
+ const _onChangeText = (text: string) => {
110
+ if (text.length > MAX_LENGTH) return;
111
+ if (dataType === 'number' && isNaN(Number(text))) return;
112
+ if (
113
+ !isNaN(Number(text)) &&
114
+ value.length >= Number(length) &&
115
+ text.length >= value.length
116
+ )
117
+ return;
118
+
119
+ setValue(text);
120
+ onChangeText?.(text);
121
+ };
122
+
123
+ const renderInputs = (length: number) => {
124
+ const TextInputs: React.ReactNode[] = [];
125
+ for (let i = 0; i < length; i++) {
126
+ TextInputs.push(
127
+ <View>
128
+ {focused && value.length === i ? (
129
+ <OTPCaret index={i} length={length} />
130
+ ) : (
131
+ <Text
132
+ style={i !== length - 1 && {marginRight: Spacing.L}}
133
+ color={
134
+ !!value[i]
135
+ ? theme.colors.text.default
136
+ : theme.colors.text.hint
148
137
  }
149
- }
150
- typography={'title_s'}>
151
- {letter}
152
- </Text>
153
- );
154
- })}
155
- {focused && value.length !== MAX_LENGTH && <OTPCaret />}
156
- </View>
157
- );
158
- };
138
+ typography={!!value[i] ? 'title_s' : 'paragraph_default'}>
139
+ {value[i] || '-'}
140
+ </Text>
141
+ )}
142
+ </View>,
143
+ );
144
+ }
145
+ return TextInputs;
146
+ };
159
147
 
160
- const renderInputView = () => {
161
- return (
162
- <View
163
- style={[
164
- styles.otpInput,
165
- {backgroundColor: theme.colors.background.surface},
166
- getBorderColor(theme, focused, errorMessage),
167
- ]}>
168
- {!!floatingValue && (
169
- <View
170
- style={[
171
- styles.otpFloatingView,
172
- {backgroundColor: theme.colors.background.surface},
173
- ]}>
174
- <Text typography={'description_s'}>{floatingValue}</Text>
175
- </View>
176
- )}
177
- <View style={styles.otpInputsView}>
178
- {length ? renderInputs(length) : renderUnidentifiedInputs()}
148
+ const renderUnidentifiedInputs = () => {
149
+ const splitValue = value.split('');
150
+
151
+ if (!focused && value.length === 0 && !!placeholder) {
152
+ return (
153
+ <Text color={theme.colors.text.hint} typography={'description_s'}>
154
+ {placeholder}
155
+ </Text>
156
+ );
157
+ }
158
+
159
+ return (
160
+ <View style={{flexDirection: 'row'}}>
161
+ {splitValue.map((letter, index) => {
162
+ return (
163
+ <Text
164
+ style={
165
+ (index !== value.length - 1 ||
166
+ (focused && value.length !== MAX_LENGTH)) && {
167
+ marginRight: Spacing.L,
168
+ }
169
+ }
170
+ typography={'title_s'}>
171
+ {letter}
172
+ </Text>
173
+ );
174
+ })}
175
+ {focused && value.length !== MAX_LENGTH && <OTPCaret />}
179
176
  </View>
180
- <View style={styles.inputView}>
181
- <TextInput
182
- {...props}
183
- accessibilityLabel={accessibilityLabel}
184
- ref={inputRef}
185
- value={value}
186
- onChangeText={_onChangeText}
187
- keyboardType={dataType === 'number' ? 'number-pad' : 'default'}
188
- onFocus={onFocusInput}
189
- onBlur={onBlurInput}
190
- style={{opacity: 0}}
191
- selectionColor={theme.colors.primary}
192
- placeholderTextColor={theme.colors.text.hint}
193
- />
177
+ );
178
+ };
179
+
180
+ const renderInputView = () => {
181
+ return (
182
+ <View
183
+ style={[
184
+ styles.otpInput,
185
+ {backgroundColor: theme.colors.background.surface},
186
+ getBorderColor(theme, focused, errorMessage),
187
+ ]}>
188
+ {!!floatingValue && (
189
+ <View
190
+ style={[
191
+ styles.otpFloatingView,
192
+ {backgroundColor: theme.colors.background.surface},
193
+ ]}>
194
+ <Text typography={'description_s'}>{floatingValue}</Text>
195
+ </View>
196
+ )}
197
+ <View style={styles.otpInputsView}>
198
+ {length ? renderInputs(length) : renderUnidentifiedInputs()}
199
+ </View>
200
+ <View style={styles.inputView}>
201
+ <TextInput
202
+ {...props}
203
+ accessibilityLabel={accessibilityLabel}
204
+ ref={inputRef}
205
+ value={value}
206
+ onChangeText={_onChangeText}
207
+ keyboardType={dataType === 'number' ? 'number-pad' : 'default'}
208
+ onFocus={onFocusInput}
209
+ onBlur={onBlurInput}
210
+ style={{opacity: 0}}
211
+ selectionColor={theme.colors.primary}
212
+ placeholderTextColor={theme.colors.text.hint}
213
+ />
214
+ </View>
194
215
  </View>
195
- </View>
196
- );
197
- };
216
+ );
217
+ };
198
218
 
199
- return (
200
- <TouchableOpacity
201
- activeOpacity={1}
202
- onPress={onFocusInput}
203
- style={[style, styles.otpWrapper]}
204
- accessibilityLabel={accessibilityLabel}>
205
- {renderInputView()}
206
- <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
207
- </TouchableOpacity>
208
- );
209
- };
219
+ return (
220
+ <TouchableOpacity
221
+ activeOpacity={1}
222
+ onPress={onFocusInput}
223
+ style={[style, styles.otpWrapper]}
224
+ accessibilityLabel={accessibilityLabel}>
225
+ {renderInputView()}
226
+ <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
227
+ </TouchableOpacity>
228
+ );
229
+ },
230
+ );
210
231
 
211
232
  export default InputOTP;
package/Input/index.tsx CHANGED
@@ -78,6 +78,8 @@ export interface InputProps extends TextInputProps {
78
78
  leadingIconColor?: string;
79
79
 
80
80
  fontWeight?: 'Regular' | 'Bold';
81
+
82
+ onPressIcon?: () => void;
81
83
  }
82
84
 
83
85
  type InputPropsWithoutSizeAndIcon = Omit<
package/Layout/Screen.tsx CHANGED
@@ -28,6 +28,7 @@ import Navigation from '../Application/Navigation';
28
28
  import styles from './styles';
29
29
  import {AnimatedHeader, NavigationOptions} from '../Application/types';
30
30
  import {Image} from '../Image';
31
+ import {HeaderType} from './types';
31
32
 
32
33
  export interface ScreenProps extends ViewProps {
33
34
  children: React.ReactNode;
@@ -47,9 +48,9 @@ export interface ScreenProps extends ViewProps {
47
48
  scrollable?: boolean;
48
49
 
49
50
  /**
50
- * Optional. If `true`, use an extended header for the screen.
51
+ * Optional. Set type of header.
51
52
  */
52
- useExtendedHeader?: boolean;
53
+ headerType?: HeaderType;
53
54
 
54
55
  /**
55
56
  * Optional. Props for the animated component or bool used top navigation animated.
@@ -89,13 +90,13 @@ const Screen = forwardRef(
89
90
  enableKeyboardAvoidingView = false,
90
91
  scrollable = false,
91
92
  navigation,
92
- useExtendedHeader,
93
93
  animatedHeader,
94
94
  scrollViewProps,
95
95
  headerComponent: Header,
96
96
  footerComponent: Footer,
97
97
  layoutOffset = -56,
98
98
  backgroundColor,
99
+ headerType = 'default',
99
100
  }: ScreenProps,
100
101
  ref,
101
102
  ) => {
@@ -111,23 +112,14 @@ const Screen = forwardRef(
111
112
  let keyboardVerticalOffset = heightHeader - 20;
112
113
  animatedValue.current = undefined;
113
114
 
114
- if (useExtendedHeader || animatedHeader) {
115
+ if (headerType == 'extended' || animatedHeader) {
115
116
  keyboardVerticalOffset = -20;
116
117
  }
117
118
 
118
- if (useExtendedHeader) {
119
+ if (headerType == 'extended') {
119
120
  headerBackground = theme.assets?.headerBackground;
120
121
  }
121
122
 
122
- useEffect(() => {
123
- if (useExtendedHeader) {
124
- navigation?.setOptions({
125
- headerTransparent: true,
126
- headerBackground: () => null,
127
- });
128
- }
129
- }, [useExtendedHeader]);
130
-
131
123
  useEffect(() => {
132
124
  if (animatedHeader) {
133
125
  styleAnimatedHeader = {
@@ -168,6 +160,69 @@ const Screen = forwardRef(
168
160
  }
169
161
  }, [animatedHeader]);
170
162
 
163
+ useEffect(() => {
164
+ if (!animatedHeader) {
165
+ let headerOptions: NavigationOptions = {};
166
+ switch (headerType) {
167
+ case 'none':
168
+ headerOptions = {
169
+ headerTransparent: true,
170
+ headerBackground: () => null,
171
+ headerShown: false,
172
+ };
173
+ break;
174
+
175
+ case 'surface':
176
+ headerOptions = {
177
+ headerTransparent: false,
178
+ headerShown: true,
179
+ headerTintColor: theme.colors.text.default,
180
+ headerBackground: (props: any) => (
181
+ <HeaderBackground
182
+ {...props}
183
+ image={null}
184
+ backgroundColor={theme.colors.background.surface}
185
+ useBorder={true}
186
+ animatedValue={animatedValue.current}
187
+ />
188
+ ),
189
+ headerTitle: (props: any) => (
190
+ <HeaderTitle {...props} animatedValue={animatedValue.current} />
191
+ ),
192
+ };
193
+ break;
194
+
195
+ case 'extended':
196
+ headerOptions = {
197
+ headerShown: true,
198
+ headerTransparent: true,
199
+ headerTintColor: Colors.black_01,
200
+ headerBackground: () => null,
201
+ headerTitle: (props: any) => <HeaderTitle {...props} />,
202
+ };
203
+ break;
204
+
205
+ default:
206
+ headerOptions = {
207
+ headerTransparent: false,
208
+ headerShown: true,
209
+ headerTintColor: Colors.black_01,
210
+ headerBackground: (props: any) => <HeaderBackground {...props} />,
211
+ };
212
+ }
213
+
214
+ navigation?.setOptions(headerOptions);
215
+ const current: any = navigation;
216
+ const parent = current?.getParent?.();
217
+ if (parent) {
218
+ return current?.addListener?.('focus', () => {
219
+ const stackNavigation = new Navigation(parent, theme);
220
+ stackNavigation?.setOptions(headerOptions);
221
+ });
222
+ }
223
+ }
224
+ }, [headerType]);
225
+
171
226
  /**
172
227
  * animated when use scroll && animated value
173
228
  */
@@ -292,11 +347,13 @@ const Screen = forwardRef(
292
347
  </Component>
293
348
  {Footer && (
294
349
  <View
295
- style={{
296
- paddingTop: Spacing.S,
297
- paddingBottom: Math.min(insets.bottom, 20) + Spacing.S,
298
- backgroundColor: theme.colors.background.surface,
299
- }}>
350
+ style={[
351
+ styles.footerContainer,
352
+ {
353
+ paddingBottom: Math.min(insets.bottom, 21) + Spacing.S,
354
+ backgroundColor: theme.colors.background.surface,
355
+ },
356
+ ]}>
300
357
  <Section>{Footer}</Section>
301
358
  </View>
302
359
  )}
package/Layout/styles.ts CHANGED
@@ -1,5 +1,5 @@
1
- import {StyleSheet} from 'react-native';
2
- import {Radius} from '../Consts';
1
+ import {Platform, StyleSheet} from 'react-native';
2
+ import {Colors, Radius, Spacing} from '../Consts';
3
3
 
4
4
  export default StyleSheet.create({
5
5
  extendedHeader: {
@@ -45,7 +45,6 @@ export default StyleSheet.create({
45
45
  flexDirection: 'row',
46
46
  flexWrap: 'wrap',
47
47
  borderRadius: Radius.M,
48
-
49
48
  padding: 12,
50
49
  },
51
50
  item: {
@@ -81,4 +80,18 @@ export default StyleSheet.create({
81
80
  paddingHorizontal: undefined,
82
81
  paddingVertical: undefined,
83
82
  },
83
+ footerContainer: {
84
+ paddingTop: Spacing.S,
85
+ ...Platform.select({
86
+ android: {
87
+ elevation: 4,
88
+ },
89
+ ios: {
90
+ shadowColor: Colors.black_20,
91
+ shadowOffset: {width: 0, height: -2},
92
+ shadowOpacity: 0.1,
93
+ shadowRadius: 3,
94
+ },
95
+ }),
96
+ },
84
97
  });
package/Layout/types.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export type HeaderType = 'default' | 'surface' | 'extended' | 'none';
2
+
1
3
  export type GridContextProps = {
2
4
  /**
3
5
  * Represents the number of columns in the grid.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.81.27",
3
+ "version": "0.81.28",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {