@momo-kits/foundation 0.81.7 → 0.81.8-beta.2

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,7 +8,12 @@ import {ApplicationContext} from './index';
8
8
 
9
9
  const Tab = createBottomTabNavigator();
10
10
 
11
- const BottomTab: React.FC<BottomTabProps> = ({tabs, listeners, navigation}) => {
11
+ const BottomTab: React.FC<BottomTabProps> = ({
12
+ tabs,
13
+ listeners,
14
+ navigation,
15
+ initialRouteName,
16
+ }) => {
12
17
  const {theme} = useContext(ApplicationContext);
13
18
 
14
19
  const defaultListener = {
@@ -21,7 +26,9 @@ const BottomTab: React.FC<BottomTabProps> = ({tabs, listeners, navigation}) => {
21
26
  };
22
27
 
23
28
  return (
24
- <Tab.Navigator backBehavior={'firstRoute'}>
29
+ <Tab.Navigator
30
+ initialRouteName={initialRouteName}
31
+ backBehavior={'firstRoute'}>
25
32
  {tabs.map(item => {
26
33
  return (
27
34
  <Tab.Screen
@@ -1,6 +1,7 @@
1
1
  import React, {Fragment, useContext, useEffect, useState} from 'react';
2
2
  import {
3
3
  Animated,
4
+ BackHandler,
4
5
  DeviceEventEmitter,
5
6
  StatusBar,
6
7
  StyleSheet,
@@ -149,8 +150,31 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
149
150
  accessibilityLabel = '',
150
151
  }) => {
151
152
  const {navigator} = useContext(ApplicationContext);
153
+
154
+ useEffect(() => {
155
+ const backHandler = BackHandler.addEventListener(
156
+ 'hardwareBackPress',
157
+ goBack,
158
+ );
159
+
160
+ return () => backHandler.remove();
161
+ }, []);
162
+
152
163
  const goBack = () => {
153
164
  const canGoBack = navigator?.ref.current?.canGoBack();
165
+ const currentRoute = navigator?.ref?.current.getCurrentRoute();
166
+
167
+ if (navigator?.trackingInfo) {
168
+ navigator?.maxApi?.trackEvent('service_icon_clicked', {
169
+ feature_code: navigator.trackingInfo.code,
170
+ app_id: navigator.trackingInfo.appId,
171
+ miniapp_version: navigator.trackingInfo.buildNumber,
172
+ screen_name: currentRoute?.params?.screen?.name ?? currentRoute?.name,
173
+ trigger_id: '111154000000000000',
174
+ icon_name: 'back',
175
+ });
176
+ }
177
+
154
178
  if (canGoBack) {
155
179
  if (preventBack) {
156
180
  navigator?.showModal({
@@ -170,12 +194,15 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
170
194
  } else {
171
195
  navigator?.ref.current?.goBack();
172
196
  }
197
+ } else if (navigator?.maxApi) {
198
+ navigator?.maxApi?.dismiss(navigator.dismissData);
173
199
  } else {
174
200
  const currentTime = new Date().getTime();
175
201
  const requestId = `navigationBackPress#${currentTime}`;
176
202
  DeviceEventEmitter.emit('dismiss', {requestId});
177
203
  }
178
204
  onPressLeftHeader?.();
205
+ return true;
179
206
  };
180
207
 
181
208
  return (
@@ -308,9 +335,25 @@ const HeaderToolkitAction: React.FC<any> = ({tintColor}) => {
308
335
  }
309
336
 
310
337
  useEffect(() => {
311
- navigator?.getToolkit().then((toolConfig: any) => {
312
- setToolConfig(toolConfig);
313
- });
338
+ if (navigator?.toolkitConfig) {
339
+ setToolConfig(navigator?.toolkitConfig);
340
+ } else {
341
+ navigator?.requestApi('getToolkit', [undefined], (result: boolean) => {
342
+ if (result) {
343
+ navigator?.requestApi(
344
+ 'getToolkitConfig',
345
+ [undefined],
346
+ (config: any) => {
347
+ navigator.toolkitConfig = {
348
+ ...config?.utilityToolConfig,
349
+ appCode: config?.appCode,
350
+ };
351
+ setToolConfig(navigator?.toolkitConfig);
352
+ },
353
+ );
354
+ }
355
+ });
356
+ }
314
357
  }, []);
315
358
 
316
359
  useEffect(() => {
@@ -26,6 +26,9 @@ import {
26
26
  import {HeaderBackground, HeaderLeft, HeaderTitle} from './Components';
27
27
 
28
28
  const ModalScreen: React.FC<any> = props => {
29
+ /**
30
+ * dismiss callback when modal | bottom sheet dismissed
31
+ */
29
32
  useEffect(() => {
30
33
  return () => {
31
34
  props.route.params?.onDismiss?.();
@@ -40,7 +43,7 @@ const ModalScreen: React.FC<any> = props => {
40
43
 
41
44
  const Modal: React.FC<ModalParams> = props => {
42
45
  const {theme, navigator} = useContext(ApplicationContext);
43
- const {screen, barrierDismissible}: ModalParams = props.route.params;
46
+ const {screen, barrierDismissible, modalName} = props.route.params;
44
47
  const Component = useRef(screen).current;
45
48
  const navigation = new Navigation(props.navigation, theme);
46
49
  const params = {
@@ -50,6 +53,45 @@ const Modal: React.FC<ModalParams> = props => {
50
53
 
51
54
  delete params.screen;
52
55
 
56
+ /**
57
+ * tracking
58
+ */
59
+ useEffect(() => {
60
+ if (navigator?.trackingInfo) {
61
+ const navigationRoutes = props.navigation.getState()?.routes || [];
62
+ const routesLength = navigationRoutes.length;
63
+ let screen_name = navigationRoutes?.[0]?.params?.screen?.name;
64
+ if (routesLength > 1) {
65
+ screen_name = navigationRoutes[routesLength - 2]?.params?.screen?.name;
66
+ }
67
+ const error_code = (screen as Function)?.()?.props?.information;
68
+ navigator?.maxApi?.trackEvent('service_component_displayed', {
69
+ feature_code: navigator.trackingInfo.code,
70
+ app_id: navigator.trackingInfo.appId,
71
+ miniapp_version: navigator.trackingInfo.buildNumber,
72
+ screen_name,
73
+ trigger_id: '111012100000000000',
74
+ component_type: 'popup',
75
+ component_name: modalName,
76
+ error_code,
77
+ });
78
+
79
+ return () => {
80
+ navigator?.maxApi?.trackEvent('service_icon_clicked', {
81
+ feature_code: navigator?.trackingInfo?.code,
82
+ app_id: navigator?.trackingInfo?.appId,
83
+ miniapp_version: navigator?.trackingInfo?.buildNumber,
84
+ screen_name,
85
+ trigger_id: '111154100000000001',
86
+ component_type: 'popup',
87
+ component_name: modalName,
88
+ error_code,
89
+ icon_name: 'close',
90
+ });
91
+ };
92
+ }
93
+ }, []);
94
+
53
95
  const onDismiss = () => {
54
96
  if (barrierDismissible) {
55
97
  return;
@@ -181,6 +223,8 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
181
223
  />
182
224
  </View>
183
225
  <NavigationContainer
226
+ trackingInfo={navigator?.trackingInfo}
227
+ maxApi={navigator?.maxApi}
184
228
  theme={{
185
229
  ...theme,
186
230
  assets: {...theme.assets, headerBackground: undefined},
@@ -223,7 +267,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
223
267
  icon="24_navigation_close"
224
268
  backgroundColor="transparent"
225
269
  useBorder={false}
226
- onPress={bottomSheetRef.current?.dismiss}
270
+ onPress={() => bottomSheetRef.current?.dismiss()}
227
271
  accessibilityLabel={'BottomSheet/Close'}
228
272
  />
229
273
  </HeaderRightAction>
@@ -14,6 +14,10 @@ class Navigation {
14
14
  this.theme = theme;
15
15
  }
16
16
 
17
+ /**
18
+ * navigation options filter by design system rule
19
+ * @param params
20
+ */
17
21
  private filterParams = (params: NavigationOptions) => {
18
22
  if (params.headerRight) {
19
23
  const headerRight = params.headerRight?.({}) as React.ReactElement;
@@ -24,14 +28,21 @@ class Navigation {
24
28
  );
25
29
  }
26
30
  }
31
+
32
+ if (params.headerLeft) {
33
+ console.warn('headerLeft not allow override render by design system!');
34
+ delete params.headerLeft;
35
+ }
36
+
27
37
  return params;
28
38
  };
29
39
 
40
+ /**
41
+ * navigation setOptions support props NavigationOptions base on react-navigation options
42
+ * @param params
43
+ */
30
44
  setOptions = (params: NavigationOptions) => {
31
- if (__DEV__) {
32
- params = this.filterParams(params);
33
- }
34
- params = getOptions(params, this.theme);
45
+ params = getOptions(this.filterParams(params), this.theme);
35
46
  this.instance.setOptions(params);
36
47
  };
37
48
  }
@@ -23,12 +23,16 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
23
23
  options,
24
24
  maxApi,
25
25
  localization,
26
+ trackingInfo,
26
27
  }) => {
27
28
  const navigationRef = React.useRef<NavigationContainerRef>(null);
28
29
  const navigator = useRef(new Navigator(navigationRef));
29
30
  const [showGrid, setShowGrid] = useState(false);
30
31
  const [language, setLanguage] = useState<'vi' | 'en'>('vi');
31
32
 
33
+ /**
34
+ * engine only shake to enable grid view
35
+ */
32
36
  useEffect(() => {
33
37
  const subscription = DeviceEventEmitter.addListener(
34
38
  'onChangeGrid',
@@ -37,10 +41,13 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
37
41
  },
38
42
  );
39
43
  return () => {
40
- subscription.remove();
44
+ subscription?.remove?.();
41
45
  };
42
46
  }, []);
43
47
 
48
+ /**
49
+ * handle mini language & listen change
50
+ */
44
51
  useEffect(() => {
45
52
  maxApi?.getLanguage?.((lang: 'vi' | 'en') => {
46
53
  setLanguage(lang);
@@ -53,8 +60,16 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
53
60
  };
54
61
  }, []);
55
62
 
63
+ /**
64
+ * inject data for navigator
65
+ */
56
66
  navigator.current.maxApi = maxApi;
67
+ navigator.current.trackingInfo = trackingInfo;
57
68
 
69
+ /**
70
+ * translate
71
+ * @param key
72
+ */
58
73
  const translate = (key: string) => {
59
74
  const mergedLocalization: LocalizationObject = {
60
75
  vi: {...localization?.vi, ...kitLocalization.vi},
@@ -4,37 +4,63 @@ import {
4
4
  HeaderToolkitProps,
5
5
  ModalParams,
6
6
  ScreenParams,
7
+ TrackingInfo,
7
8
  } from './types';
8
9
 
9
10
  class Navigator {
10
11
  ref?: any;
11
12
  toolkitConfig?: HeaderToolkitProps;
12
13
  maxApi?: any;
14
+ trackingInfo?: TrackingInfo;
15
+ dismissData?: any;
13
16
 
14
17
  constructor(navigation: any) {
15
18
  this.ref = navigation;
16
19
  }
17
20
 
21
+ /**
22
+ * push new stack screen
23
+ * @param params
24
+ */
18
25
  push = (params: ScreenParams) => {
19
26
  this.ref.current.dispatch(StackActions.push('Stack', params));
20
27
  };
21
28
 
29
+ /**
30
+ * replace current screen with new screen
31
+ * @param params
32
+ */
22
33
  replace = (params: ScreenParams) => {
23
34
  this.ref.current.dispatch(StackActions.replace('Stack', params));
24
35
  };
25
-
36
+ /**
37
+ * pop to dismiss a screen
38
+ * @param count
39
+ */
26
40
  pop = (count?: number) => {
27
41
  this.ref.current.dispatch(StackActions.pop(count ?? 1));
28
42
  };
29
43
 
44
+ /**
45
+ * present a new screen, show from bottom iOS
46
+ * @param params
47
+ */
30
48
  present = (params: ScreenParams) => {
31
49
  this.ref.current.dispatch(StackActions.push('Dialog', params));
32
50
  };
33
51
 
52
+ /**
53
+ * show a modal popup
54
+ * @param params
55
+ */
34
56
  showModal = (params: ModalParams) => {
35
57
  this.ref.current.dispatch(StackActions.push('Modal', params));
36
58
  };
37
59
 
60
+ /**
61
+ * show a bottom sheet
62
+ * @param params
63
+ */
38
64
  showBottomSheet = (params: BottomSheetParams) => {
39
65
  this.ref.current.dispatch(
40
66
  StackActions.push('Modal', {
@@ -44,10 +70,17 @@ class Navigator {
44
70
  );
45
71
  };
46
72
 
73
+ /**
74
+ * pop all screen route
75
+ */
47
76
  popToTop = () => {
48
77
  this.ref.current.dispatch(StackActions.popToTop());
49
78
  };
50
79
 
80
+ /**
81
+ * reset a navigation flow with new screen
82
+ * @param params
83
+ */
51
84
  reset = (params: ScreenParams) => {
52
85
  this.ref.current.dispatch(
53
86
  CommonActions.reset({
@@ -63,36 +96,17 @@ class Navigator {
63
96
  );
64
97
  };
65
98
 
99
+ /**
100
+ * request max api for mini app
101
+ * @param func
102
+ * @param args
103
+ * @param callback
104
+ */
66
105
  requestApi = (func: any, args: any = [], callback: any) => {
67
106
  if (this.maxApi) {
68
107
  this.maxApi[func]?.(...args, callback);
69
- } else {
70
- const instance: any = global;
71
- instance?.miniAppApi?.dispatch?.(func, ...args, callback);
72
108
  }
73
109
  };
74
-
75
- getToolkit = async (): Promise<HeaderToolkitProps | undefined> => {
76
- return new Promise(resolve => {
77
- if (this.toolkitConfig) {
78
- resolve(this.toolkitConfig);
79
- return;
80
- }
81
- this.requestApi('getToolkit', [undefined], (result: boolean) => {
82
- if (result) {
83
- this.requestApi('getToolkitConfig', [undefined], (config: any) => {
84
- this.toolkitConfig = {
85
- ...config?.utilityToolConfig,
86
- appCode: config?.appCode,
87
- };
88
- resolve(this.toolkitConfig);
89
- });
90
- } else {
91
- resolve(undefined);
92
- }
93
- });
94
- });
95
- };
96
110
  }
97
111
 
98
112
  export default Navigator;
@@ -1,4 +1,4 @@
1
- import React, {useContext, useLayoutEffect} from 'react';
1
+ import React, {useContext, useEffect, useLayoutEffect} from 'react';
2
2
  import {ScreenParams} from './types';
3
3
  import Navigation from './Navigation';
4
4
  import {ApplicationContext} from './index';
@@ -6,7 +6,7 @@ import {GridSystem} from '../Layout';
6
6
 
7
7
  const StackScreen: React.FC<ScreenParams> = props => {
8
8
  const {screen: Component, options}: ScreenParams = props.route.params;
9
- const {theme, showGrid} = useContext(ApplicationContext);
9
+ const {theme, showGrid, navigator} = useContext(ApplicationContext);
10
10
  const navigation = new Navigation(props.navigation, theme);
11
11
 
12
12
  const params = {
@@ -16,12 +16,38 @@ const StackScreen: React.FC<ScreenParams> = props => {
16
16
 
17
17
  delete params.screen;
18
18
 
19
+ /**
20
+ * set options for screen
21
+ */
19
22
  useLayoutEffect(() => {
20
23
  if (options) {
21
24
  navigation.setOptions(options);
22
25
  }
23
26
  }, [options]);
24
27
 
28
+ /**
29
+ * tracking for screen
30
+ */
31
+ useEffect(() => {
32
+ if (navigator?.trackingInfo) {
33
+ const navigationRoutes = props.navigation.getState()?.routes || [];
34
+ const routesLength = navigationRoutes.length;
35
+ let pre_screen_name = navigationRoutes?.[0]?.params?.screen?.name;
36
+ if (routesLength > 1) {
37
+ pre_screen_name =
38
+ navigationRoutes[routesLength - 2]?.params?.screen?.name;
39
+ }
40
+ navigator?.maxApi?.trackEvent('service_frame_displayed', {
41
+ feature_code: navigator.trackingInfo.code,
42
+ app_id: navigator.trackingInfo.appId,
43
+ miniapp_version: navigator.trackingInfo.buildNumber,
44
+ screen_name: Component.name,
45
+ pre_screen_name,
46
+ trigger_id: '111378000000000000',
47
+ });
48
+ }
49
+ }, []);
50
+
25
51
  return (
26
52
  <>
27
53
  <Component {...params} />
@@ -79,12 +79,20 @@ export type LocalizationObject = {
79
79
  };
80
80
  };
81
81
 
82
+ export type TrackingInfo = {
83
+ [key: string]: any;
84
+ code: string;
85
+ appId: string;
86
+ buildNumber: number;
87
+ };
88
+
82
89
  export type NavigationContainerProps = {
83
90
  screen: Screen;
84
91
  options?: NavigationOptions;
85
92
  theme: Theme;
86
- maxApi?: any;
93
+ maxApi: any;
87
94
  localization?: LocalizationObject;
95
+ trackingInfo?: TrackingInfo;
88
96
  };
89
97
 
90
98
  export type NavigationScreenProps = {
@@ -101,6 +109,7 @@ export type ScreenParams = {
101
109
 
102
110
  export interface ModalParams extends ScreenParams {
103
111
  [key: string]: any;
112
+ modalName?: string;
104
113
  onDismiss?: () => void;
105
114
  barrierDismissible?: boolean;
106
115
  }
@@ -177,6 +186,7 @@ export type BottomTabItemProps = {
177
186
 
178
187
  export type BottomTabProps = {
179
188
  tabs: BottomTabItemProps[];
189
+ initialRouteName?: string;
180
190
  navigation: Navigation;
181
191
  listeners?: {
182
192
  tabPress?: any;
@@ -13,6 +13,9 @@ import {
13
13
  import {NavigationOptions, Theme} from './types';
14
14
  import {Colors} from '../Consts';
15
15
 
16
+ /**
17
+ * default options for stack screen
18
+ */
16
19
  const getStackOptions = (): StackNavigationOptions => {
17
20
  return {
18
21
  headerTitleAlign: 'left',
@@ -24,6 +27,9 @@ const getStackOptions = (): StackNavigationOptions => {
24
27
  };
25
28
  };
26
29
 
30
+ /**
31
+ * default options for dialog screen
32
+ */
27
33
  const getDialogOptions = (): StackNavigationOptions => {
28
34
  return {
29
35
  headerTitleAlign: 'left',
@@ -35,6 +41,9 @@ const getDialogOptions = (): StackNavigationOptions => {
35
41
  };
36
42
  };
37
43
 
44
+ /**
45
+ * default options for modal screen
46
+ */
38
47
  const getModalOptions = (): StackNavigationOptions => {
39
48
  return {
40
49
  headerShown: false,
@@ -60,6 +69,9 @@ const getModalOptions = (): StackNavigationOptions => {
60
69
  };
61
70
  };
62
71
 
72
+ /**
73
+ * build react-navigation options
74
+ */
63
75
  const getOptions = (params: NavigationOptions, theme: Theme) => {
64
76
  let options = {};
65
77
 
@@ -153,9 +153,9 @@ const Spacing = {
153
153
  L: 16,
154
154
  XL: 24,
155
155
  XXL: 32,
156
- Size48: 48,
157
- Size56: 56,
158
- Size64: 64,
156
+ Size3XL: 48,
157
+ Size4XL: 56,
158
+ Size5XL: 64,
159
159
  };
160
160
 
161
161
  const Radius = {
package/Image/index.tsx CHANGED
@@ -20,13 +20,12 @@ const Image: React.FC<ImageProps> = ({
20
20
  style,
21
21
  source,
22
22
  accessibilityLabel,
23
+ loading,
23
24
  ...rest
24
25
  }) => {
25
26
  const {theme} = useContext(ApplicationContext);
26
27
  const error = useRef(false);
27
- const [status, setStatus] = useState<Status>(
28
- rest.loading ? 'loading' : 'success',
29
- );
28
+ const [status, setStatus] = useState<Status>('success');
30
29
 
31
30
  /**
32
31
  * render content loading | fail | rendered
@@ -65,16 +64,18 @@ const Image: React.FC<ImageProps> = ({
65
64
  <FastImage
66
65
  {...rest}
67
66
  source={source}
67
+ fallback={false}
68
68
  style={styles.image}
69
+ onProgress={() => {
70
+ if (status != 'loading' && loading) setStatus('loading');
71
+ }}
69
72
  onLoadStart={() => {
70
73
  error.current = false;
71
74
  }}
72
75
  onLoadEnd={() => {
73
- if (error.current) {
74
- setStatus('error');
75
- } else {
76
- setStatus('success');
77
- }
76
+ let current: Status = 'success';
77
+ if (error.current) current = 'error';
78
+ if (status != current) setStatus(current);
78
79
  }}
79
80
  onError={() => {
80
81
  error.current = true;
@@ -64,7 +64,7 @@ const InputSearch = forwardRef(
64
64
  <TextInput
65
65
  {...props}
66
66
  accessibilityLabel={accessibilityLabel}
67
- textAlignVertical="top"
67
+ textAlignVertical="center"
68
68
  ref={inputRef}
69
69
  style={[
70
70
  styles.searchInput,
package/Layout/Card.tsx CHANGED
@@ -145,6 +145,7 @@ const Card: React.FC<CardProps> = ({
145
145
  accessibilityLabel={`Card`}
146
146
  style={[
147
147
  style,
148
+ styles.protectedStyle,
148
149
  styles.card,
149
150
  {
150
151
  width: widthSection,
@@ -0,0 +1,39 @@
1
+ import React, {FC, useContext} from 'react';
2
+ import {SectionList, SectionListProps, View} from 'react-native';
3
+ import Item, {SpanNumber} from './Item';
4
+ import {GridContext} from './index';
5
+ import styles from './styles';
6
+
7
+ export interface ItemSectionListProps extends SectionListProps<any> {
8
+ /**
9
+ * Represents the width of the Card component in terms of span numbers (1-12).
10
+ */
11
+ widthSpan?: SpanNumber;
12
+ }
13
+
14
+ const ItemSectionList: FC<ItemSectionListProps> = ({
15
+ renderItem,
16
+ widthSpan,
17
+ style,
18
+ contentContainerStyle,
19
+ ...props
20
+ }) => {
21
+ const {gutterSize, numberOfColumns} = useContext(GridContext);
22
+ const widthItem = (widthSpan ?? numberOfColumns) as SpanNumber;
23
+ const _renderItem = (item: any) => {
24
+ return <Item widthSpan={widthItem}>{renderItem?.(item)}</Item>;
25
+ };
26
+
27
+ return (
28
+ <SectionList
29
+ {...props}
30
+ renderItem={_renderItem}
31
+ horizontal={false}
32
+ ItemSeparatorComponent={() => <View style={{height: gutterSize}} />}
33
+ style={[style, styles.protectedStyle]}
34
+ contentContainerStyle={[contentContainerStyle, styles.protectedStyle]}
35
+ />
36
+ );
37
+ };
38
+
39
+ export default ItemSectionList;
@@ -22,11 +22,6 @@ export interface SectionProps extends ViewProps {
22
22
  */
23
23
  useMargin?: boolean;
24
24
 
25
- /**
26
- * Optional. Background color for the Section component.
27
- */
28
- backgroundColor?: string;
29
-
30
25
  /**
31
26
  * Optional. Background image for the Section component.
32
27
  */
@@ -36,8 +31,8 @@ export interface SectionProps extends ViewProps {
36
31
  const Section: React.FC<SectionProps> = ({
37
32
  children,
38
33
  useMargin = true,
39
- backgroundColor,
40
34
  backgroundImage,
35
+ style,
41
36
  }) => {
42
37
  const {showGrid} = useContext(ApplicationContext);
43
38
  const {numberOfColumns} = useGridSystem();
@@ -156,13 +151,16 @@ const Section: React.FC<SectionProps> = ({
156
151
  }}>
157
152
  <View
158
153
  accessibilityLabel={`Section`}
159
- style={{
160
- width: widthSection,
161
- marginHorizontal: gridContext.margin,
162
- flexDirection: 'row',
163
- flexWrap: 'wrap',
164
- backgroundColor: backgroundColor,
165
- }}>
154
+ style={[
155
+ style,
156
+ styles.protectedStyle,
157
+ {
158
+ width: widthSection,
159
+ marginHorizontal: gridContext.margin,
160
+ flexDirection: 'row',
161
+ flexWrap: 'wrap',
162
+ },
163
+ ]}>
166
164
  {!!backgroundImage && (
167
165
  <Image
168
166
  source={{uri: backgroundImage}}
package/Layout/index.ts CHANGED
@@ -6,6 +6,7 @@ import {createContext} from 'react';
6
6
  import {GridContextProps} from './types';
7
7
  import Item from './Item';
8
8
  import ItemList from './ItemList';
9
+ import ItemSectionList from './ItemSectionList';
9
10
 
10
11
  const GridContext = createContext<GridContextProps>({
11
12
  numberOfColumns: 12,
@@ -23,4 +24,5 @@ export {
23
24
  validateChildren,
24
25
  Item,
25
26
  ItemList,
27
+ ItemSectionList,
26
28
  };
package/Layout/styles.ts CHANGED
@@ -45,19 +45,7 @@ export default StyleSheet.create({
45
45
  flexDirection: 'row',
46
46
  flexWrap: 'wrap',
47
47
  borderRadius: Radius.M,
48
- paddingTop: undefined,
49
- paddingBottom: undefined,
50
- paddingLeft: undefined,
51
- paddingRight: undefined,
52
- paddingVertical: undefined,
53
- paddingHorizontal: undefined,
54
48
  padding: 12,
55
- margin: undefined,
56
- marginTop: undefined,
57
- marginBottom: undefined,
58
- marginLeft: undefined,
59
- marginRight: undefined,
60
- marginVertical: undefined,
61
49
  },
62
50
  item: {
63
51
  overflow: 'hidden',
@@ -1,5 +1,5 @@
1
- import React, {useContext} from 'react';
2
- import {StyleSheet, TouchableOpacity, View} from 'react-native';
1
+ import React, {useContext, useState} from 'react';
2
+ import {ScrollView, StyleSheet, TouchableOpacity, View} from 'react-native';
3
3
  import {PopupNotifyProps} from './types';
4
4
  import {Radius, Spacing, Styles} from '../Consts';
5
5
  import {Image} from '../Image';
@@ -19,6 +19,14 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
19
19
  onIconClose,
20
20
  }) => {
21
21
  const {theme, navigator} = useContext(ApplicationContext);
22
+ const [scrollContent, setScrollContent] = useState(false);
23
+
24
+ let descriptionStyle = {};
25
+ let Description: any = View;
26
+ if (scrollContent) {
27
+ Description = ScrollView;
28
+ descriptionStyle = {height: 80};
29
+ }
22
30
 
23
31
  /**
24
32
  * on action popup
@@ -143,11 +151,19 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
143
151
  <Text typography={'title_xs'} numberOfLines={1}>
144
152
  {title}
145
153
  </Text>
146
- <View style={styles.description}>
147
- <Text typography={'paragraph_default'} numberOfLines={3}>
154
+ <Description style={[styles.description, descriptionStyle]}>
155
+ <Text
156
+ typography={'paragraph_default'}
157
+ numberOfLines={4}
158
+ onTextLayout={({nativeEvent: {lines}}) => {
159
+ const enableScroll = lines.length > 3;
160
+ if (enableScroll != scrollContent) {
161
+ setScrollContent(enableScroll);
162
+ }
163
+ }}>
148
164
  {description}
149
165
  </Text>
150
- </View>
166
+ </Description>
151
167
  {!!information && (
152
168
  <View style={styles.information}>
153
169
  <Text
package/Title/index.tsx CHANGED
@@ -1,5 +1,10 @@
1
- import React, {FC, useContext} from 'react';
2
- import {Text as RNText, TouchableOpacity, View} from 'react-native';
1
+ import React, {FC, useContext, useState} from 'react';
2
+ import {
3
+ LayoutChangeEvent,
4
+ Text as RNText,
5
+ TouchableOpacity,
6
+ View,
7
+ } from 'react-native';
3
8
  import {TitleProps} from './types';
4
9
  import {Icon} from '../Icon';
5
10
  import styles from './styles';
@@ -19,6 +24,7 @@ const Title: FC<TitleProps> = ({
19
24
  description,
20
25
  }) => {
21
26
  const {theme} = useContext(ApplicationContext);
27
+ const [containerWidth, setContainerWidth] = useState(0);
22
28
  const flexStyle = titleFlex ? {flex: 1} : {};
23
29
  const typeStyle =
24
30
  type === 'screen' ? styles.screen : styles[`${type}_${size}`];
@@ -31,7 +37,7 @@ const Title: FC<TitleProps> = ({
31
37
  const actionSize = titleFlex ? scaleSize(22) : scaleSize(18);
32
38
  const titleWeight = type === 'screen' ? 'Semibold' : 'Bold';
33
39
  const marginStyle = type === 'section' ? {marginTop: Spacing.M} : {};
34
-
40
+ const numberOfLines = type === 'section' ? 1 : 2;
35
41
  const renderAction = () => {
36
42
  return !!actionTitle && titleFlex ? (
37
43
  <TouchableOpacity onPress={onPressAction}>
@@ -56,19 +62,26 @@ const Title: FC<TitleProps> = ({
56
62
  );
57
63
  };
58
64
 
65
+ const onLayout = (e: LayoutChangeEvent) => {
66
+ setContainerWidth(e.nativeEvent.layout.width);
67
+ };
68
+
59
69
  return (
60
- <View style={styles.wrapper}>
70
+ <View onLayout={onLayout} style={styles.wrapper}>
61
71
  <View style={[styles.container, marginStyle]}>
62
72
  {!!icon && type !== 'screen' && (
63
73
  <Icon style={styles.icon} source={icon} />
64
74
  )}
65
75
  <RNText
76
+ numberOfLines={numberOfLines}
66
77
  style={[
67
78
  typeStyle,
68
79
  flexStyle,
69
80
  styles.title,
70
81
  {
71
82
  fontFamily: `${theme.font}-${titleWeight}`,
83
+ color: theme.colors.text.default,
84
+ maxWidth: containerWidth - scaleSize(22),
72
85
  },
73
86
  ]}>
74
87
  {title}
package/Title/styles.ts CHANGED
@@ -4,6 +4,10 @@ import {Radius, Spacing} from '../Consts';
4
4
 
5
5
  export default StyleSheet.create({
6
6
  card_small: {
7
+ fontSize: scaleSize(14),
8
+ lineHeight: scaleSize(20),
9
+ },
10
+ card_medium: {
7
11
  fontSize: scaleSize(16),
8
12
  lineHeight: scaleSize(22),
9
13
  },
@@ -11,10 +15,14 @@ export default StyleSheet.create({
11
15
  fontSize: scaleSize(18),
12
16
  lineHeight: scaleSize(26),
13
17
  },
14
- section_small: {
18
+ section_medium: {
15
19
  fontSize: scaleSize(20),
16
20
  lineHeight: scaleSize(28),
17
21
  },
22
+ section_small: {
23
+ fontSize: scaleSize(16),
24
+ lineHeight: scaleSize(22),
25
+ },
18
26
  section_large: {
19
27
  fontSize: scaleSize(24),
20
28
  lineHeight: scaleSize(34),
@@ -28,7 +36,7 @@ export default StyleSheet.create({
28
36
  },
29
37
  container: {
30
38
  flexDirection: 'row',
31
- alignItems: 'center',
39
+ alignItems: 'baseline',
32
40
  width: '100%',
33
41
  },
34
42
  actionIcon: {
package/Title/types.ts CHANGED
@@ -22,7 +22,7 @@ export type TitleProps = {
22
22
  * 'large' - A larger, more prominent title.
23
23
  * 'small' - A smaller, less prominent title.
24
24
  */
25
- size?: 'large' | 'small';
25
+ size?: 'large' | 'medium' | 'small';
26
26
 
27
27
  /**
28
28
  * Optional. Represents the name or key of the icon to be displayed alongside the title.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.81.7",
3
+ "version": "0.81.8-beta.2",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {