@momo-kits/foundation 0.92.6-rc.9 → 0.92.7

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.
@@ -46,8 +46,6 @@ type Options = {
46
46
  };
47
47
 
48
48
  const DEFAULT_MAX_TAB_ITEM_WIDTH = 125;
49
- const DEFAULT_TABBAR_HEIGHT = 49;
50
- const COMPACT_TABBAR_HEIGHT = 32;
51
49
 
52
50
  const shouldUseHorizontalLabels = ({
53
51
  state,
@@ -87,38 +85,6 @@ const shouldUseHorizontalLabels = ({
87
85
  const getPaddingBottom = (insets: EdgeInsets) =>
88
86
  Math.max(insets.bottom - Platform.select({ios: 4, default: 0}), 0);
89
87
 
90
- export const getTabBarHeight = ({
91
- dimensions,
92
- insets,
93
- style,
94
- ...rest
95
- }: Options & {
96
- insets: EdgeInsets;
97
- style: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
98
- }) => {
99
- // @ts-ignore
100
- const customHeight = StyleSheet.flatten(style)?.height;
101
-
102
- if (typeof customHeight === 'number') {
103
- return customHeight;
104
- }
105
-
106
- const isLandscape = dimensions.width > dimensions.height;
107
- const horizontalLabels = shouldUseHorizontalLabels({dimensions, ...rest});
108
- const paddingBottom = getPaddingBottom(insets);
109
-
110
- if (
111
- Platform.OS === 'ios' &&
112
- !Platform.isPad &&
113
- isLandscape &&
114
- horizontalLabels
115
- ) {
116
- return COMPACT_TABBAR_HEIGHT + paddingBottom;
117
- }
118
-
119
- return DEFAULT_TABBAR_HEIGHT + paddingBottom;
120
- };
121
-
122
88
  export default function BottomTabBar({
123
89
  state,
124
90
  navigation,
@@ -327,21 +293,19 @@ export default function BottomTabBar({
327
293
  );
328
294
 
329
295
  const renderCenterComponent = useCallback(() => {
330
- const renderFloatingButton = () => {
331
- if (typeof floatingButton?.icon == 'string') {
332
- return <Icon source={floatingButton?.icon} size={32} color={'white'} />;
333
- } else if (floatingButton?.icon) {
334
- return floatingButton?.icon;
335
- } else {
336
- return null;
337
- }
338
- };
339
296
  if (floatingButton?.icon && state.routes.length !== 4) {
340
297
  console.warn(
341
298
  "Can't render floating button with icon when routes length is not 4",
342
299
  );
343
300
  return null;
344
301
  }
302
+ const renderFloatingButton = () => {
303
+ if (typeof floatingButton?.icon == 'string') {
304
+ return <Icon source={floatingButton?.icon} size={32} color={'white'} />;
305
+ }
306
+ return floatingButton?.icon;
307
+ };
308
+
345
309
  return (
346
310
  <View style={{flex: 1}}>
347
311
  <TouchableOpacity
@@ -404,12 +368,10 @@ export default function BottomTabBar({
404
368
  }),
405
369
  },
406
370
  ],
407
- // Absolutely position the tab bar so that the content is below it
408
- // This is needed to avoid gap at bottom when the tab bar is hidden
409
371
  position: isTabBarHidden ? 'absolute' : (null as any),
410
372
  },
411
373
  {
412
- paddingBottom: paddingBottom - 0,
374
+ paddingBottom: paddingBottom,
413
375
  paddingHorizontal: Math.max(insets.left, insets.right),
414
376
  },
415
377
  style,
@@ -464,9 +426,8 @@ const styles = StyleSheet.create({
464
426
  justifyContent: 'center',
465
427
  height: 48,
466
428
  width: 48,
467
-
468
- borderRadius: 100,
469
- top: -12,
429
+ borderRadius: 24,
430
+ top: -10,
470
431
  },
471
432
  indicator: {
472
433
  height: 2,
@@ -10,21 +10,26 @@ import {Text} from '../../Text';
10
10
  import {ApplicationContext} from '../index';
11
11
  import StackScreen from '../StackScreen';
12
12
  import {BottomTabProps, NavigationScreenProps} from '../types';
13
- import {getStackOptions} from '../utils';
13
+ import {getOptions, getStackOptions} from '../utils';
14
14
  import BottomTabBar from './BottomTabBar';
15
15
 
16
16
  const Tab = createBottomTabNavigator();
17
17
  const Stack = createStackNavigator();
18
18
 
19
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
+ }
20
25
  if (route.params?.nested) {
21
26
  return (
22
27
  <Stack.Navigator>
23
28
  <Stack.Screen
24
29
  name="Stack"
25
30
  component={StackScreen}
26
- initialParams={{screen: route.params?.screen}}
27
- options={{...getStackOptions(), ...route.params?.options}}
31
+ initialParams={{screen: route.params?.screen, options}}
32
+ options={{...getStackOptions()}}
28
33
  />
29
34
  </Stack.Navigator>
30
35
  );
@@ -35,7 +40,7 @@ const TabScreen: React.FC<NavigationScreenProps> = ({route}) => {
35
40
  <Stack.Screen
36
41
  name="Stack"
37
42
  component={StackScreen}
38
- initialParams={{screen: route.params?.screen}}
43
+ initialParams={{screen: route.params?.screen, options}}
39
44
  options={{...getStackOptions(), ...route.params?.options}}
40
45
  />
41
46
  </Stack.Navigator>
@@ -75,14 +80,16 @@ const BottomTab: React.FC<BottomTabProps> = ({
75
80
  )}
76
81
  tabBarOptions={{
77
82
  style: {
78
- height: 68 + Math.min(insets.bottom, 21),
83
+ height: 64 + Math.min(insets.bottom, 21),
79
84
  },
80
85
  tabStyle: {
81
86
  height: 68,
82
- paddingVertical: 8,
87
+ paddingVertical: 10,
83
88
  },
84
89
  }}>
85
90
  {tabs.map(item => {
91
+ const isNum = !isNaN(parseInt(`${item?.badgeLabel}`));
92
+ const isDot = typeof item?.badgeLabel == 'string' && !item?.badgeLabel;
86
93
  return (
87
94
  <Tab.Screen
88
95
  key={item.name}
@@ -113,12 +120,9 @@ const BottomTab: React.FC<BottomTabProps> = ({
113
120
  fontWeight: 'bold',
114
121
  alignSelf: 'center',
115
122
  fontFamily: `${theme.font}-Bold`,
116
- left: item?.showDot ? 8 : 4,
117
- top: item?.showDot ? -2 : -6,
118
- backgroundColor:
119
- typeof item?.badgeLabel === 'string'
120
- ? Colors.orange_03
121
- : Colors.red_03,
123
+ left: isDot ? 8 : 4,
124
+ top: isDot ? -2 : -6,
125
+ backgroundColor: isNum ? Colors.red_03 : Colors.orange_03,
122
126
  },
123
127
  tabBarIcon: ({color}) => (
124
128
  <Icon source={item.icon} color={color} size={28} />
@@ -183,15 +183,18 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
183
183
 
184
184
  const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
185
185
  image,
186
- backgroundColor,
187
186
  animatedValue,
188
- useBorder = false,
187
+ useSurface = false,
189
188
  }) => {
190
189
  const {theme} = useContext(ApplicationContext);
191
190
  let headerImage = theme.assets?.headerBackground;
191
+ let backgroundColor;
192
192
  if (image === null) {
193
193
  headerImage = undefined;
194
194
  }
195
+ if (useSurface) {
196
+ backgroundColor = theme.colors.background.surface;
197
+ }
195
198
  const opacity = animatedValue?.interpolate({
196
199
  inputRange: [0, 114],
197
200
  outputRange: [0, 1],
@@ -203,9 +206,9 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
203
206
  style={[
204
207
  Styles.flex,
205
208
  {
206
- backgroundColor: backgroundColor ?? theme.colors.background.default,
209
+ backgroundColor,
207
210
  overflow: 'hidden',
208
- borderBottomWidth: useBorder ? 1 : 0,
211
+ borderBottomWidth: useSurface ? 1 : 0,
209
212
  borderColor: theme.colors.border.default,
210
213
  opacity,
211
214
  },
@@ -297,14 +300,14 @@ const HeaderToolkitAction: React.FC<any> = ({
297
300
  runtimeTools = [],
298
301
  preventClose,
299
302
  }) => {
300
- const {theme, navigator} = useContext(ApplicationContext);
303
+ const {navigator} = useContext(ApplicationContext);
301
304
  const miniContext = useContext<any>(MiniAppContext);
302
305
  const [toolConfig, setToolConfig] = useState<any>();
303
306
  const {onAction} = toolConfig ?? {};
304
307
 
305
308
  let backgroundColor;
306
309
  if (tintColor != Colors.black_01) {
307
- backgroundColor = theme.colors.background.surface;
310
+ backgroundColor = 'transparent';
308
311
  }
309
312
 
310
313
  useEffect(() => {
@@ -503,7 +506,7 @@ const styles = StyleSheet.create({
503
506
  flexDirection: 'row',
504
507
  justifyContent: 'center',
505
508
  alignItems: 'center',
506
- paddingRight: Spacing.S,
509
+ paddingRight: Spacing.M,
507
510
  },
508
511
  toolkitContainer: {
509
512
  marginLeft: Spacing.S,
@@ -85,11 +85,14 @@ const Modal: React.FC<ModalParams> = props => {
85
85
 
86
86
  const BottomSheet: React.FC<BottomSheetParams> = props => {
87
87
  const {theme, navigator} = useContext(ApplicationContext);
88
+ const insets = useSafeAreaInsets();
89
+
88
90
  const {
89
91
  screen: Screen,
90
92
  options,
91
93
  footerComponent,
92
- flatListProps,
94
+ scrollViewProps,
95
+ surface,
93
96
  }: BottomSheetParams = props.route.params;
94
97
 
95
98
  const navigation = new Navigation(props.navigation, theme);
@@ -100,14 +103,62 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
100
103
  };
101
104
 
102
105
  delete params.screen;
103
- const modalizeRef = useRef<Modalize>(null);
106
+ const modalRef = useRef<Modalize>(null);
104
107
 
105
108
  useEffect(() => {
106
- modalizeRef.current?.open();
109
+ modalRef.current?.open();
107
110
  }, []);
108
111
 
109
112
  const handleClose = () => {
110
- modalizeRef?.current?.close();
113
+ modalRef?.current?.close();
114
+ };
115
+
116
+ /**
117
+ * build header
118
+ * @param title
119
+ * @param onClose
120
+ */
121
+ const headerDefault = (title: string, onClose: () => void) => {
122
+ return (
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.textOption}
134
+ numberOfLines={1}
135
+ typography="header_default_bold">
136
+ {title ?? ''}
137
+ </Text>
138
+ <TouchableOpacity style={styles.iconButton} onPress={onClose}>
139
+ <Icon source="navigation_close" />
140
+ </TouchableOpacity>
141
+ </View>
142
+ );
143
+ };
144
+
145
+ /**
146
+ * build footer
147
+ * @param footerComponent
148
+ */
149
+ const footerDefault = (footerComponent: React.ReactNode) => {
150
+ if (footerComponent) {
151
+ return (
152
+ <View
153
+ style={{
154
+ marginBottom: Math.min(insets.bottom, 21),
155
+ paddingHorizontal: Spacing.M,
156
+ paddingVertical: Spacing.S,
157
+ }}>
158
+ {footerComponent}
159
+ </View>
160
+ );
161
+ }
111
162
  };
112
163
 
113
164
  return (
@@ -120,13 +171,21 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
120
171
  onClosed={() => {
121
172
  navigator?.pop();
122
173
  }}
123
- ref={modalizeRef}
124
- withHandle={false}
125
- flatListProps={flatListProps}
174
+ handlePosition="inside"
175
+ withHandle={true}
176
+ disableScrollIfPossible={true}
177
+ modalTopOffset={insets.top}
178
+ ref={modalRef}
179
+ scrollViewProps={scrollViewProps}
126
180
  HeaderComponent={headerDefault(
127
181
  options?.title ?? options?.customTitle?.title ?? '',
128
182
  handleClose,
129
183
  )}
184
+ modalStyle={{
185
+ backgroundColor: surface
186
+ ? theme.colors.background.surface
187
+ : theme.colors.background.default,
188
+ }}
130
189
  FooterComponent={footerDefault(footerComponent)}>
131
190
  <View style={styles.bottomSheetContainer}>
132
191
  <Screen {...props} {...params} />
@@ -136,36 +195,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
136
195
  );
137
196
  };
138
197
 
139
- const headerDefault = (title: string, onClose: () => void) => {
140
- return (
141
- <View style={styles.header}>
142
- <Text
143
- style={styles.textOption}
144
- numberOfLines={2}
145
- typography="header_default_bold">
146
- {title ?? ''}
147
- </Text>
148
- <TouchableOpacity style={styles.buttonClose} onPress={onClose}>
149
- <Icon source="navigation_close" />
150
- </TouchableOpacity>
151
- </View>
152
- );
153
- };
154
-
155
- const footerDefault = (footerComponent: React.ReactNode) => {
156
- const insets = useSafeAreaInsets();
157
- if (!footerComponent) return;
158
- return (
159
- <View
160
- style={{
161
- marginBottom: Math.min(insets.bottom, 21) + Spacing.S,
162
- paddingHorizontal: 12,
163
- }}>
164
- {footerComponent}
165
- </View>
166
- );
167
- };
168
-
169
198
  const styles = StyleSheet.create({
170
199
  container: {flex: 1, flexDirection: 'row'},
171
200
  modalContent: {
@@ -204,34 +233,28 @@ const styles = StyleSheet.create({
204
233
  },
205
234
  bottomSheetContainer: {
206
235
  width: '100%',
207
- backgroundColor: 'white',
208
- borderTopRightRadius: 16,
209
- borderTopLeftRadius: 16,
210
236
  },
211
237
  header: {
212
- height: 50,
238
+ marginTop: 10,
239
+ height: 56,
213
240
  width: '100%',
214
241
  alignItems: 'center',
215
- justifyContent: 'center',
242
+ flexDirection: 'row',
216
243
  },
217
244
  textOption: {
218
245
  fontSize: 16,
219
246
  color: '#303233',
220
247
  textAlign: 'center',
221
248
  fontWeight: 'bold',
222
- paddingHorizontal: 60,
223
- },
224
- imageOption: {
225
- height: 1,
226
- backgroundColor: '#ebebeb',
227
- width: '100%',
228
- position: 'absolute',
229
- bottom: 1,
249
+ paddingHorizontal: Spacing.L,
250
+ flex: 1,
230
251
  },
231
- buttonClose: {
232
- padding: 12,
233
- position: 'absolute',
234
- right: 0,
252
+ iconButton: {
253
+ width: 24,
254
+ height: 24,
255
+ alignItems: 'center',
256
+ justifyContent: 'center',
257
+ marginHorizontal: Spacing.M,
235
258
  },
236
259
  icon: {
237
260
  width: 24,
@@ -62,6 +62,7 @@ const StackScreen: React.FC<ScreenParams> = props => {
62
62
  tracked.current.timeoutInteraction = setTimeout(() => {
63
63
  onScreenInteraction();
64
64
  }, 5000);
65
+
65
66
  return () => {
66
67
  onScreenLoad();
67
68
  onScreenInteraction();
@@ -74,13 +75,14 @@ const StackScreen: React.FC<ScreenParams> = props => {
74
75
  const onScreenLoad = () => {
75
76
  clearTimeout(tracked.current.timeoutLoad);
76
77
  if (!tracked.current?.releaseLoad) {
77
- navigator?.maxApi?.dispatchFunction?.('autoTracking', {
78
+ context.autoTracking?.({
78
79
  appId: context.appId,
79
80
  code: context.code,
81
+ buildNumber: context.buildNumber,
80
82
  screenName,
81
83
  componentName: 'Screen',
82
84
  state: 'load',
83
- time: timeLoad.current,
85
+ duration: timeLoad.current,
84
86
  });
85
87
  navigator?.maxApi?.showToastDebug?.({
86
88
  appId: `auto - ${context.appId}`,
@@ -96,13 +98,14 @@ const StackScreen: React.FC<ScreenParams> = props => {
96
98
  const onScreenInteraction = () => {
97
99
  clearTimeout(tracked.current.timeoutInteraction);
98
100
  if (!tracked.current?.releaseInteraction) {
99
- navigator?.maxApi?.dispatchFunction?.('autoTracking', {
101
+ context.autoTracking?.({
100
102
  appId: context.appId,
101
103
  code: context.code,
104
+ buildNumber: context.buildNumber,
102
105
  screenName,
103
106
  componentName: 'Screen',
104
107
  state: 'interaction',
105
- time: timeInteraction.current,
108
+ duration: timeInteraction.current - timeLoad.current,
106
109
  });
107
110
 
108
111
  navigator?.maxApi?.showToastDebug?.({
@@ -16,8 +16,8 @@ import {defaultContext} from '../Consts';
16
16
  const Context = createContext({});
17
17
  const ApplicationContext = createContext(defaultContext);
18
18
 
19
- const MiniAppContext = (Platform as any).MiniAppContext ?? Context;
20
- const ScreenContext = (Platform as any).ScreenContext ?? Context;
19
+ const MiniAppContext = (Platform as any).MiniAppContext ?? createContext({});
20
+ const ScreenContext = (Platform as any).ScreenContext ?? createContext({});
21
21
  const ComponentContext = (Platform as any).ComponentContext ?? Context;
22
22
 
23
23
  export {
@@ -1,6 +1,6 @@
1
1
  import {StackNavigationOptions} from '@react-navigation/stack';
2
2
  import React from 'react';
3
- import {Animated, FlatListProps, ViewProps} from 'react-native';
3
+ import {Animated, ScrollViewProps, ViewProps} from 'react-native';
4
4
  import {PopupNotifyProps} from '../Popup/types';
5
5
  import Navigation from './Navigation';
6
6
  import Navigator from './Navigator';
@@ -119,8 +119,9 @@ export interface ModalParams extends ScreenParams {
119
119
  export interface BottomSheetParams extends ScreenParams {
120
120
  [key: string]: any;
121
121
  onDismiss?: () => void;
122
+ surface?: boolean;
122
123
  footerComponent?: React.ReactNode;
123
- flatListProps?: FlatListProps<any>;
124
+ scrollViewProps?: ScrollViewProps;
124
125
  }
125
126
 
126
127
  export type NavigationButtonProps = {
@@ -165,9 +166,8 @@ export interface HeaderBackProps extends NavigationButtonProps {
165
166
 
166
167
  export type HeaderBackgroundProps = {
167
168
  image?: string | null;
168
- backgroundColor?: string | null;
169
169
  animatedValue?: Animated.Value;
170
- useBorder?: boolean;
170
+ useSurface?: boolean;
171
171
  };
172
172
 
173
173
  export type TitleCustomProps = {
@@ -223,4 +223,5 @@ export type FloatingButtonProps = {
223
223
  export type AnimatedHeader = {
224
224
  type: 'default' | 'surface';
225
225
  component?: (props?: any) => React.ReactElement;
226
+ headerTitle?: (props?: any) => React.ReactElement;
226
227
  };
@@ -12,8 +12,7 @@ import {
12
12
  } from './Components';
13
13
  import {NavigationOptions, Theme} from './types';
14
14
  import {Colors} from '../Consts';
15
- import {Platform} from 'react-native';
16
- const isIOS = Platform.OS === 'ios';
15
+
17
16
  /**
18
17
  * default options for stack screen
19
18
  */
@@ -101,11 +100,7 @@ const getOptions = (params: NavigationOptions, theme: Theme) => {
101
100
  ...options,
102
101
  headerTintColor: theme.colors.text.default,
103
102
  headerBackground: () => (
104
- <HeaderBackground
105
- image={null}
106
- backgroundColor={theme.colors.background.surface}
107
- useBorder={true}
108
- />
103
+ <HeaderBackground image={null} useSurface={true} />
109
104
  ),
110
105
  };
111
106
  } else {
@@ -142,22 +137,6 @@ const getOptions = (params: NavigationOptions, theme: Theme) => {
142
137
  };
143
138
  };
144
139
 
145
- const getAccessibilityID = (
146
- key = '',
147
- override: string | undefined = undefined,
148
- ) => {
149
- if (isIOS) {
150
- return {
151
- accessible: true,
152
- testID: override || key,
153
- };
154
- } else {
155
- return {
156
- accessibilityLabel: override || key,
157
- };
158
- }
159
- };
160
-
161
140
  const formatNameLength = (name = '') => {
162
141
  let formattedName = name;
163
142
  if (formattedName.length > 100) {
@@ -171,5 +150,4 @@ export {
171
150
  getModalOptions,
172
151
  getOptions,
173
152
  formatNameLength,
174
- getAccessibilityID,
175
153
  };
package/Button/index.tsx CHANGED
@@ -58,7 +58,7 @@ export interface ButtonProps extends TouchableOpacityProps {
58
58
  /**
59
59
  * If true, the icon of button will use the tint color provided by its container.
60
60
  */
61
- useTintColor?: boolean;
61
+ tintColor?: string;
62
62
 
63
63
  /**
64
64
  * If true, the icon of button will use be show indicator loading.
@@ -76,7 +76,7 @@ export interface ButtonProps extends TouchableOpacityProps {
76
76
  const Button: FC<ButtonProps> = ({
77
77
  type = 'primary',
78
78
  size = 'large',
79
- useTintColor = true,
79
+ tintColor = null,
80
80
  full = true,
81
81
  iconRight,
82
82
  iconLeft,
@@ -231,7 +231,7 @@ const Button: FC<ButtonProps> = ({
231
231
  const renderLeading = () => {
232
232
  const iconSize = getIconSize();
233
233
  const marginRight = getIconSpace();
234
- const color = useTintColor ? getTextColor() : undefined;
234
+ const color = tintColor ?? getTextColor();
235
235
 
236
236
  if (loading) {
237
237
  return (
@@ -245,7 +245,7 @@ const Button: FC<ButtonProps> = ({
245
245
  borderRadius: Spacing.M,
246
246
  },
247
247
  ]}>
248
- <Loader type="spinner" color={color} />
248
+ <Loader type="spinner" color={getTextColor()} />
249
249
  </View>
250
250
  );
251
251
  }
@@ -266,7 +266,7 @@ const Button: FC<ButtonProps> = ({
266
266
  * render trailing
267
267
  */
268
268
  const renderTrailing = () => {
269
- const color = useTintColor ? getTextColor() : undefined;
269
+ const color = tintColor ?? getTextColor();
270
270
  const marginLeft = getIconSpace();
271
271
 
272
272
  if (iconRight) {
package/Icon/index.tsx CHANGED
@@ -33,6 +33,7 @@ const Icon: React.FC<IconProps> = ({
33
33
  loading={false}
34
34
  source={getIconSource()}
35
35
  style={[style, {width: size, height: size}]}
36
+ resizeMode='contain'
36
37
  {...tintColorProps}
37
38
  />
38
39
  );
@@ -76,7 +76,6 @@ const InputMoney = forwardRef(
76
76
  clear: onClearText,
77
77
  focus: () => inputRef.current?.focus(),
78
78
  blur: () => inputRef.current?.blur(),
79
- changeText: (text: string) => _onChangeText(text),
80
79
  setText: _onChangeText,
81
80
  };
82
81
  });
package/Input/common.tsx CHANGED
@@ -16,13 +16,6 @@ type FloatingViewProps = {
16
16
  style?: ViewStyle;
17
17
  };
18
18
 
19
- export interface InputRef {
20
- focus: () => void;
21
- blur: () => void;
22
- clear: () => void;
23
- setText: (text: string) => void;
24
- }
25
-
26
19
  export const DEFAULT_HEIGHT = scaleSize(104);
27
20
  export const MAX_LENGTH = 300;
28
21
 
package/Input/index.tsx CHANGED
@@ -6,6 +6,13 @@ import InputOTP from './InputOTP';
6
6
  import InputSearch from './InputSearch';
7
7
  import InputTextArea from './InputTextArea';
8
8
 
9
+ export interface InputRef {
10
+ focus: () => void;
11
+ blur: () => void;
12
+ clear: () => void;
13
+ setText: (text: string) => void;
14
+ }
15
+
9
16
  export interface InputProps extends TextInputProps {
10
17
  /**
11
18
  * Optional. Defines the size of the Input component.
@@ -4,10 +4,10 @@ import {
4
4
  LayoutChangeEvent,
5
5
  StyleSheet,
6
6
  TouchableOpacity,
7
+ View,
7
8
  } from 'react-native';
8
9
  import {ApplicationContext} from '../Application';
9
10
  import {Icon} from '../Icon';
10
- import {Text} from '../Text';
11
11
 
12
12
  export interface FloatingButtonProps {
13
13
  label?: string;
@@ -18,9 +18,11 @@ export interface FloatingButtonProps {
18
18
  size?: 'small' | 'large';
19
19
  animatedValue?: Animated.Value;
20
20
  bottom?: number;
21
+ renderComponent?: () => React.ReactNode;
21
22
  }
22
23
 
23
24
  export const FloatingButton: React.FC<FloatingButtonProps> = ({
25
+ renderComponent,
24
26
  label = '',
25
27
  onPress = () => {},
26
28
  position = 'right',
@@ -33,33 +35,52 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
33
35
  const {theme} = useContext(ApplicationContext);
34
36
  const maxWidth = useRef(0);
35
37
  const minWidth = size == 'small' ? 36 : 48;
38
+ const [opacityAnimated] = useState(new Animated.Value(0)); // Initial opacity set to 0
36
39
  const [widthAnimated, setWidthAnimated] = useState<Animated.Value>();
37
40
  const lastOffset = useRef(0);
38
- const debounce = useRef<any>();
41
+ const lastDirection = useRef<string>();
39
42
  const [showText, setShowText] = React.useState(true);
40
43
 
41
44
  useEffect(() => {
42
45
  if (!label) return;
43
46
 
47
+ Animated.timing(opacityAnimated, {
48
+ toValue: 1,
49
+ duration: 100,
50
+ useNativeDriver: true,
51
+ }).start();
52
+
44
53
  const listener = animatedValue?.addListener(({value}) => {
45
- clearTimeout(debounce.current);
46
- debounce.current = setTimeout(() => {
47
- const down = value > lastOffset.current;
54
+ if (value !== lastOffset.current && value > 0) {
55
+ const direction = value > lastOffset.current ? 'down' : 'up';
48
56
  lastOffset.current = value;
49
- if (down) {
50
- Animated.timing(widthAnimated!, {
51
- toValue: minWidth,
52
- duration: 300,
53
- useNativeDriver: false,
54
- }).start(() => setShowText(false));
55
- } else {
56
- Animated.timing(widthAnimated!, {
57
- toValue: maxWidth.current,
58
- duration: 300,
59
- useNativeDriver: false,
60
- }).start(() => setShowText(true));
57
+ if (lastDirection.current !== direction) {
58
+ lastDirection.current = direction;
59
+ if (direction === 'down') {
60
+ Animated.timing(opacityAnimated, {
61
+ toValue: 0,
62
+ duration: 100,
63
+ useNativeDriver: true,
64
+ }).start();
65
+ Animated.timing(widthAnimated!, {
66
+ toValue: minWidth,
67
+ duration: 100,
68
+ useNativeDriver: false,
69
+ }).start(() => setShowText(false));
70
+ } else {
71
+ Animated.timing(opacityAnimated, {
72
+ toValue: 1,
73
+ duration: 100,
74
+ useNativeDriver: true,
75
+ }).start();
76
+ Animated.timing(widthAnimated!, {
77
+ toValue: maxWidth.current,
78
+ duration: 100,
79
+ useNativeDriver: false,
80
+ }).start(() => setShowText(true));
81
+ }
61
82
  }
62
- }, 500);
83
+ }
63
84
  });
64
85
 
65
86
  return () => {
@@ -76,6 +97,23 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
76
97
  setWidthAnimated(new Animated.Value(layout.width));
77
98
  };
78
99
 
100
+ if (renderComponent) {
101
+ return (
102
+ <View
103
+ onLayout={handleLayout}
104
+ style={[
105
+ styles.container,
106
+ {
107
+ right: position === 'right' ? 12 : undefined,
108
+ alignSelf: position === 'center' ? 'center' : 'flex-end',
109
+ bottom,
110
+ },
111
+ ]}>
112
+ {renderComponent()}
113
+ </View>
114
+ );
115
+ }
116
+
79
117
  return (
80
118
  <Animated.View
81
119
  onLayout={handleLayout}
@@ -99,12 +137,11 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
99
137
  color={iconColor}
100
138
  />
101
139
  {showText && !!label && (
102
- <Text
103
- style={styles.label}
104
- color="white"
105
- typography="action_default_bold">
140
+ <Animated.Text
141
+ style={[styles.label, {opacity: opacityAnimated, color: 'white'}]}
142
+ numberOfLines={1}>
106
143
  {label}
107
- </Text>
144
+ </Animated.Text>
108
145
  )}
109
146
  </TouchableOpacity>
110
147
  </Animated.View>
@@ -124,5 +161,8 @@ const styles = StyleSheet.create({
124
161
  },
125
162
  label: {
126
163
  marginLeft: 8,
164
+ fontSize: 16,
165
+ lineHeight: 22,
166
+ fontWeight: '700',
127
167
  },
128
168
  });
package/Layout/Screen.tsx CHANGED
@@ -89,6 +89,10 @@ export interface ScreenProps extends ViewProps {
89
89
  */
90
90
  floatingButtonProps?: Omit<FloatingButtonProps, 'animatedValue' | 'bottom'>;
91
91
 
92
+ /**
93
+ * optional animated value
94
+ */
95
+ animatedValue?: Animated.Value;
92
96
  /**
93
97
  * Optional. If `true`, the screen content is using grid layout by span.
94
98
  */
@@ -111,10 +115,13 @@ const Screen = forwardRef(
111
115
  headerType = 'default',
112
116
  floatingButtonProps,
113
117
  useGridLayout = true,
118
+ animatedValue: animated,
114
119
  }: ScreenProps,
115
120
  ref,
116
121
  ) => {
117
- const animatedValue = useRef<Animated.Value>();
122
+ const animatedValue = useRef<Animated.Value>(
123
+ animated ?? new Animated.Value(0),
124
+ );
118
125
  const {theme} = useContext(ApplicationContext);
119
126
  const insets = useSafeAreaInsets();
120
127
  const heightHeader = useHeaderHeight();
@@ -151,18 +158,27 @@ const Screen = forwardRef(
151
158
  };
152
159
  if (animatedHeader.type == 'surface') {
153
160
  styleAnimatedHeader = {
154
- headerTransparent: true,
161
+ ...styleAnimatedHeader,
155
162
  headerBackground: (props: any) => (
156
163
  <HeaderBackground
157
164
  {...props}
158
165
  image={null}
159
- backgroundColor={theme.colors.background.surface}
160
- useBorder={true}
166
+ useSurface={true}
161
167
  animatedValue={animatedValue.current}
162
168
  />
163
169
  ),
164
170
  };
165
171
  }
172
+ if (animatedHeader.headerTitle) {
173
+ styleAnimatedHeader = {
174
+ ...styleAnimatedHeader,
175
+ headerTitle: (props: any) =>
176
+ animatedHeader.headerTitle?.({
177
+ ...props,
178
+ animatedValue: animatedValue.current,
179
+ }),
180
+ };
181
+ }
166
182
  navigation?.setOptions(styleAnimatedHeader);
167
183
  }
168
184
  }, [animatedHeader]);
@@ -185,12 +201,7 @@ const Screen = forwardRef(
185
201
  headerShown: true,
186
202
  headerTintColor: theme.colors.text.default,
187
203
  headerBackground: (props: any) => (
188
- <HeaderBackground
189
- {...props}
190
- image={null}
191
- backgroundColor={theme.colors.background.surface}
192
- useBorder={true}
193
- />
204
+ <HeaderBackground {...props} image={null} useSurface={true} />
194
205
  ),
195
206
  headerTitle: (props: any) => <HeaderTitle {...props} />,
196
207
  };
@@ -234,8 +245,9 @@ const Screen = forwardRef(
234
245
  },
235
246
  ],
236
247
  {
237
- useNativeDriver: false,
248
+ useNativeDriver: true,
238
249
  listener: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
250
+ scrollViewProps?.onScroll?.(e);
239
251
  if (animatedHeader?.type == 'surface') {
240
252
  const offsetY = e.nativeEvent.contentOffset.y;
241
253
  let color = Colors.black_01;
@@ -199,16 +199,20 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
199
199
  {title}
200
200
  </Text>
201
201
  <Description style={[styles.description, descriptionStyle]}>
202
- <Text
203
- typography={'body_default_regular'}
204
- onTextLayout={({nativeEvent: {lines}}) => {
205
- const enableScroll = lines.length > 3;
206
- if (enableScroll != scrollContent) {
207
- setScrollContent(enableScroll);
208
- }
209
- }}>
210
- {description}
211
- </Text>
202
+ {typeof description === 'string' ? (
203
+ <Text
204
+ typography={'body_default_regular'}
205
+ onTextLayout={({nativeEvent: {lines}}) => {
206
+ const enableScroll = lines.length > 3;
207
+ if (enableScroll != scrollContent) {
208
+ setScrollContent(enableScroll);
209
+ }
210
+ }}>
211
+ {description}
212
+ </Text>
213
+ ) : (
214
+ description
215
+ )}
212
216
  </Description>
213
217
  {!!error && (
214
218
  <View style={styles.information}>
package/index.ts CHANGED
@@ -4,12 +4,11 @@ import {
4
4
  useSafeAreaInsets,
5
5
  } from 'react-native-safe-area-context';
6
6
  import LinearGradient from 'react-native-linear-gradient';
7
- import {getAccessibilityID} from "./Application/utils";
8
7
 
9
8
  /**
10
9
  * native export
11
10
  */
12
- export {SafeAreaView, useSafeAreaInsets, SafeAreaProvider, LinearGradient, getAccessibilityID};
11
+ export {SafeAreaView, useSafeAreaInsets, SafeAreaProvider, LinearGradient};
13
12
 
14
13
  /**
15
14
  * foundation component
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.92.6-rc.9",
3
+ "version": "0.92.7",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},