@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.
- package/Application/BottomTab.tsx +9 -2
- package/Application/Components.tsx +46 -3
- package/Application/ModalScreen.tsx +46 -2
- package/Application/Navigation.ts +15 -4
- package/Application/NavigationContainer.tsx +16 -1
- package/Application/Navigator.ts +40 -26
- package/Application/StackScreen.tsx +28 -2
- package/Application/types.ts +11 -1
- package/Application/utils.tsx +12 -0
- package/Consts/colors+spacing+radius.ts +3 -3
- package/Image/index.tsx +9 -8
- package/Input/InputSearch.tsx +1 -1
- package/Layout/Card.tsx +1 -0
- package/Layout/ItemSectionList.tsx +39 -0
- package/Layout/Section.tsx +11 -13
- package/Layout/index.ts +2 -0
- package/Layout/styles.ts +0 -12
- package/Popup/PopupNotify.tsx +21 -5
- package/Title/index.tsx +17 -4
- package/Title/styles.ts +10 -2
- package/Title/types.ts +1 -1
- package/package.json +1 -1
|
@@ -8,7 +8,12 @@ import {ApplicationContext} from './index';
|
|
|
8
8
|
|
|
9
9
|
const Tab = createBottomTabNavigator();
|
|
10
10
|
|
|
11
|
-
const BottomTab: React.FC<BottomTabProps> = ({
|
|
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
|
|
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?.
|
|
312
|
-
setToolConfig(
|
|
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}
|
|
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
|
-
|
|
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
|
|
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},
|
package/Application/Navigator.ts
CHANGED
|
@@ -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} />
|
package/Application/types.ts
CHANGED
|
@@ -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
|
|
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;
|
package/Application/utils.tsx
CHANGED
|
@@ -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
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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;
|
package/Input/InputSearch.tsx
CHANGED
package/Layout/Card.tsx
CHANGED
|
@@ -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;
|
package/Layout/Section.tsx
CHANGED
|
@@ -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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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',
|
package/Popup/PopupNotify.tsx
CHANGED
|
@@ -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
|
-
<
|
|
147
|
-
<Text
|
|
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
|
-
</
|
|
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 {
|
|
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
|
-
|
|
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: '
|
|
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.
|