@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.
- package/Application/BottomTab/BottomTabBar.tsx +10 -49
- package/Application/BottomTab/index.tsx +16 -12
- package/Application/Components.tsx +10 -7
- package/Application/ModalScreen.tsx +77 -54
- package/Application/StackScreen.tsx +7 -4
- package/Application/index.ts +2 -2
- package/Application/types.ts +5 -4
- package/Application/utils.tsx +2 -24
- package/Button/index.tsx +5 -5
- package/Icon/index.tsx +1 -0
- package/Input/InputMoney.tsx +0 -1
- package/Input/common.tsx +0 -7
- package/Input/index.tsx +7 -0
- package/Layout/FloatingButton.tsx +63 -23
- package/Layout/Screen.tsx +23 -11
- package/Popup/PopupNotify.tsx +14 -10
- package/index.ts +1 -2
- package/package.json +1 -1
|
@@ -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
|
|
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
|
-
|
|
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()
|
|
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:
|
|
83
|
+
height: 64 + Math.min(insets.bottom, 21),
|
|
79
84
|
},
|
|
80
85
|
tabStyle: {
|
|
81
86
|
height: 68,
|
|
82
|
-
paddingVertical:
|
|
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:
|
|
117
|
-
top:
|
|
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
|
-
|
|
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
|
|
209
|
+
backgroundColor,
|
|
207
210
|
overflow: 'hidden',
|
|
208
|
-
borderBottomWidth:
|
|
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 {
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
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
|
|
106
|
+
const modalRef = useRef<Modalize>(null);
|
|
104
107
|
|
|
105
108
|
useEffect(() => {
|
|
106
|
-
|
|
109
|
+
modalRef.current?.open();
|
|
107
110
|
}, []);
|
|
108
111
|
|
|
109
112
|
const handleClose = () => {
|
|
110
|
-
|
|
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
|
-
|
|
124
|
-
withHandle={
|
|
125
|
-
|
|
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
|
-
|
|
238
|
+
marginTop: 10,
|
|
239
|
+
height: 56,
|
|
213
240
|
width: '100%',
|
|
214
241
|
alignItems: 'center',
|
|
215
|
-
|
|
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:
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
108
|
+
duration: timeInteraction.current - timeLoad.current,
|
|
106
109
|
});
|
|
107
110
|
|
|
108
111
|
navigator?.maxApi?.showToastDebug?.({
|
package/Application/index.ts
CHANGED
|
@@ -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 ??
|
|
20
|
-
const ScreenContext = (Platform as any).ScreenContext ??
|
|
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 {
|
package/Application/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {StackNavigationOptions} from '@react-navigation/stack';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import {Animated,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
};
|
package/Application/utils.tsx
CHANGED
|
@@ -12,8 +12,7 @@ import {
|
|
|
12
12
|
} from './Components';
|
|
13
13
|
import {NavigationOptions, Theme} from './types';
|
|
14
14
|
import {Colors} from '../Consts';
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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={
|
|
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 =
|
|
269
|
+
const color = tintColor ?? getTextColor();
|
|
270
270
|
const marginLeft = getIconSpace();
|
|
271
271
|
|
|
272
272
|
if (iconRight) {
|
package/Icon/index.tsx
CHANGED
package/Input/InputMoney.tsx
CHANGED
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
|
|
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
|
-
|
|
46
|
-
|
|
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 (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
-
|
|
161
|
+
...styleAnimatedHeader,
|
|
155
162
|
headerBackground: (props: any) => (
|
|
156
163
|
<HeaderBackground
|
|
157
164
|
{...props}
|
|
158
165
|
image={null}
|
|
159
|
-
|
|
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:
|
|
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;
|
package/Popup/PopupNotify.tsx
CHANGED
|
@@ -199,16 +199,20 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
199
199
|
{title}
|
|
200
200
|
</Text>
|
|
201
201
|
<Description style={[styles.description, descriptionStyle]}>
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
|
11
|
+
export {SafeAreaView, useSafeAreaInsets, SafeAreaProvider, LinearGradient};
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* foundation component
|