@momo-kits/foundation 0.92.8-rc.5 → 0.92.8-rc.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/BottomSheet.tsx +90 -110
- package/Application/StackScreen.tsx +3 -2
- package/Application/types.ts +1 -2
- package/Layout/Screen.tsx +12 -11
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {useContext, useEffect, useRef
|
|
1
|
+
import React, {useCallback, useContext, useEffect, useRef} from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Animated,
|
|
4
4
|
Dimensions,
|
|
@@ -26,19 +26,21 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
26
26
|
options,
|
|
27
27
|
surface,
|
|
28
28
|
barrierDismissible = false,
|
|
29
|
-
snapPoint = 'content',
|
|
30
29
|
draggable = true,
|
|
31
|
-
dragOnContent = false,
|
|
32
30
|
useBottomInset = true,
|
|
31
|
+
keyboardVerticalOffset,
|
|
33
32
|
}: BottomSheetParams = props.route.params;
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const pan = useRef(
|
|
34
|
+
new Animated.ValueXY({
|
|
35
|
+
y: heightDevice,
|
|
36
|
+
x: 0,
|
|
37
|
+
}),
|
|
38
|
+
).current;
|
|
37
39
|
const panResponder = useRef(
|
|
38
40
|
PanResponder.create({
|
|
39
41
|
onStartShouldSetPanResponder: () => draggable,
|
|
40
42
|
onMoveShouldSetPanResponder: (_, gestureState) => {
|
|
41
|
-
return draggable &&
|
|
43
|
+
return draggable && gestureState.dy > 0;
|
|
42
44
|
},
|
|
43
45
|
onPanResponderMove: (e, gestureState) => {
|
|
44
46
|
gestureState.dy > 0 &&
|
|
@@ -59,141 +61,119 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
59
61
|
},
|
|
60
62
|
}),
|
|
61
63
|
).current;
|
|
62
|
-
const [estimate, setEstimate] = useState(0.9);
|
|
63
|
-
const timeoutMount = useRef<any>();
|
|
64
64
|
|
|
65
65
|
let backgroundColor = theme.colors.background.default;
|
|
66
66
|
if (surface) {
|
|
67
67
|
backgroundColor = theme.colors.background.surface;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/**
|
|
71
|
-
* handle open & emit dismiss event
|
|
72
|
-
*/
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
clearTimeout(timeoutMount.current);
|
|
75
|
-
timeoutMount.current = setTimeout(() => {
|
|
76
|
-
const mapSnapPoint: any = {
|
|
77
|
-
'30%': 0.3,
|
|
78
|
-
'50%': 0.5,
|
|
79
|
-
'70%': 0.7,
|
|
80
|
-
'90%': 0.9,
|
|
81
|
-
};
|
|
82
|
-
const point = mapSnapPoint[snapPoint] ?? estimate;
|
|
83
|
-
setModalVisible(true);
|
|
84
|
-
Animated.timing(animatedHeight, {
|
|
85
|
-
useNativeDriver: false,
|
|
86
|
-
toValue: heightDevice * point,
|
|
87
|
-
duration: 150,
|
|
88
|
-
}).start();
|
|
89
|
-
}, 50);
|
|
90
|
-
}, [estimate]);
|
|
91
|
-
|
|
92
70
|
/**
|
|
93
71
|
* emit dismiss event
|
|
94
72
|
*/
|
|
95
73
|
useEffect(() => {
|
|
74
|
+
Animated.timing(pan, {
|
|
75
|
+
toValue: {x: 0, y: 0},
|
|
76
|
+
useNativeDriver: false,
|
|
77
|
+
duration: 150,
|
|
78
|
+
}).start();
|
|
96
79
|
return () => {
|
|
97
80
|
props.route.params?.onDismiss?.();
|
|
98
81
|
};
|
|
99
82
|
}, []);
|
|
83
|
+
|
|
100
84
|
/**
|
|
101
85
|
* handler dismiss
|
|
102
86
|
*/
|
|
103
|
-
const onDismiss = (force = false) => {
|
|
87
|
+
const onDismiss = (force = false, callback?: () => void) => {
|
|
104
88
|
if (barrierDismissible && !force) {
|
|
105
89
|
return;
|
|
106
90
|
}
|
|
107
|
-
Animated.timing(
|
|
91
|
+
Animated.timing(pan, {
|
|
92
|
+
toValue: {x: 0, y: heightDevice},
|
|
108
93
|
useNativeDriver: false,
|
|
109
|
-
|
|
110
|
-
duration: 200,
|
|
94
|
+
duration: 100,
|
|
111
95
|
}).start(() => {
|
|
112
|
-
pan.setValue({x: 0, y: 0});
|
|
113
96
|
navigator?.pop();
|
|
97
|
+
callback?.();
|
|
114
98
|
});
|
|
115
99
|
};
|
|
116
100
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
101
|
+
/**
|
|
102
|
+
* manual close
|
|
103
|
+
*/
|
|
104
|
+
const requestClose = useCallback((callback?: () => void) => {
|
|
105
|
+
onDismiss(true, callback);
|
|
106
|
+
}, []);
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* render header
|
|
110
|
+
*/
|
|
111
|
+
const renderHeader = useCallback(() => {
|
|
112
|
+
return (
|
|
113
|
+
<View
|
|
114
|
+
style={{
|
|
115
|
+
backgroundColor,
|
|
116
|
+
borderTopLeftRadius: Radius.M,
|
|
117
|
+
borderTopRightRadius: Radius.M,
|
|
118
|
+
}}
|
|
119
|
+
{...panResponder.panHandlers}>
|
|
120
|
+
<View style={{height: 16}}>
|
|
121
|
+
<View style={styles.indicator} />
|
|
122
|
+
</View>
|
|
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.flex, {textAlign: 'center'}]}
|
|
134
|
+
numberOfLines={1}
|
|
135
|
+
typography="header_default_bold">
|
|
136
|
+
{options.title ?? ''}
|
|
137
|
+
</Text>
|
|
127
138
|
<TouchableOpacity
|
|
128
|
-
style={
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
]}>
|
|
157
|
-
<View style={styles.iconButton} />
|
|
158
|
-
<Text
|
|
159
|
-
style={[Styles.flex, {textAlign: 'center'}]}
|
|
160
|
-
numberOfLines={1}
|
|
161
|
-
typography="header_default_bold">
|
|
162
|
-
{options.title ?? ''}
|
|
163
|
-
</Text>
|
|
164
|
-
<TouchableOpacity
|
|
165
|
-
style={styles.iconButton}
|
|
166
|
-
onPress={() => onDismiss(true)}>
|
|
167
|
-
<Icon source="navigation_close" />
|
|
168
|
-
</TouchableOpacity>
|
|
169
|
-
</View>
|
|
170
|
-
</View>
|
|
171
|
-
)}
|
|
172
|
-
<Screen {...props} {...props.route.params} />
|
|
139
|
+
style={styles.iconButton}
|
|
140
|
+
onPress={() => onDismiss(true)}>
|
|
141
|
+
<Icon source="navigation_close" />
|
|
142
|
+
</TouchableOpacity>
|
|
143
|
+
</View>
|
|
144
|
+
</View>
|
|
145
|
+
);
|
|
146
|
+
}, []);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<Modal transparent visible={true} onRequestClose={() => onDismiss()}>
|
|
150
|
+
<KeyboardAvoidingView
|
|
151
|
+
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
152
|
+
keyboardVerticalOffset={keyboardVerticalOffset ?? -20}
|
|
153
|
+
style={[Styles.flex, {justifyContent: 'flex-end'}]}>
|
|
154
|
+
<TouchableOpacity
|
|
155
|
+
style={Styles.flex}
|
|
156
|
+
activeOpacity={1}
|
|
157
|
+
onPress={() => onDismiss()}
|
|
158
|
+
/>
|
|
159
|
+
<Animated.View style={{transform: pan.getTranslateTransform()}}>
|
|
160
|
+
{renderHeader()}
|
|
161
|
+
<View style={{backgroundColor: backgroundColor}}>
|
|
162
|
+
<Screen
|
|
163
|
+
{...props}
|
|
164
|
+
{...props.route.params}
|
|
165
|
+
requestClose={requestClose}
|
|
166
|
+
/>
|
|
173
167
|
{useBottomInset && (
|
|
174
168
|
<View style={{height: Math.min(insets.bottom, 21)}} />
|
|
175
169
|
)}
|
|
176
|
-
</
|
|
177
|
-
</
|
|
178
|
-
</
|
|
179
|
-
|
|
180
|
-
<View
|
|
181
|
-
style={styles.estimate}
|
|
182
|
-
onLayout={e => {
|
|
183
|
-
const current = (e.nativeEvent.layout.height + 72) / heightDevice;
|
|
184
|
-
if (current != estimate) {
|
|
185
|
-
setEstimate(current);
|
|
186
|
-
}
|
|
187
|
-
}}>
|
|
188
|
-
<Screen {...props} {...props.route.params} />
|
|
189
|
-
{useBottomInset && (
|
|
190
|
-
<View style={{height: Math.min(insets.bottom, 21)}} />
|
|
191
|
-
)}
|
|
192
|
-
</View>
|
|
193
|
-
)}
|
|
194
|
-
</>
|
|
170
|
+
</View>
|
|
171
|
+
</Animated.View>
|
|
172
|
+
</KeyboardAvoidingView>
|
|
173
|
+
</Modal>
|
|
195
174
|
);
|
|
196
175
|
};
|
|
176
|
+
|
|
197
177
|
export default BottomSheet;
|
|
198
178
|
|
|
199
179
|
const styles = StyleSheet.create({
|
|
@@ -50,10 +50,11 @@ const StackScreen: React.FC<ScreenParams> = props => {
|
|
|
50
50
|
* tracking for screen
|
|
51
51
|
*/
|
|
52
52
|
useEffect(() => {
|
|
53
|
-
|
|
53
|
+
if (__DEV__ && (!screenName || screenName == 'Invalid')) {
|
|
54
|
+
Alert.alert('Design system', `Unvalidated screen name "${screenName}" !`);
|
|
55
|
+
}
|
|
54
56
|
|
|
55
57
|
navigator?.maxApi?.of?.({screenName});
|
|
56
|
-
|
|
57
58
|
tracked.current.timeoutLoad = setTimeout(() => {
|
|
58
59
|
onScreenLoad();
|
|
59
60
|
}, 5000);
|
package/Application/types.ts
CHANGED
|
@@ -114,13 +114,12 @@ export type BottomSheetParams = {
|
|
|
114
114
|
title: string;
|
|
115
115
|
surface?: boolean;
|
|
116
116
|
};
|
|
117
|
-
snapPoint?: '30%' | '50%' | '70%' | '90%' | 'content';
|
|
118
117
|
surface?: boolean;
|
|
119
118
|
onDismiss?: (type?: string) => void;
|
|
120
119
|
barrierDismissible?: boolean;
|
|
121
120
|
draggable?: boolean;
|
|
122
|
-
dragOnContent?: boolean;
|
|
123
121
|
useBottomInset?: boolean;
|
|
122
|
+
keyboardVerticalOffset?: number;
|
|
124
123
|
};
|
|
125
124
|
|
|
126
125
|
export type NavigationButtonProps = {
|
package/Layout/Screen.tsx
CHANGED
|
@@ -33,6 +33,9 @@ import styles from './styles';
|
|
|
33
33
|
import {HeaderType} from './types';
|
|
34
34
|
|
|
35
35
|
export interface ScreenProps extends ViewProps {
|
|
36
|
+
/**
|
|
37
|
+
* ReactNode representing the content of the screen.
|
|
38
|
+
*/
|
|
36
39
|
children: React.ReactNode;
|
|
37
40
|
/**
|
|
38
41
|
* Optional. Navigation object for handling navigation actions.
|
|
@@ -90,9 +93,10 @@ export interface ScreenProps extends ViewProps {
|
|
|
90
93
|
floatingButtonProps?: Omit<FloatingButtonProps, 'animatedValue' | 'bottom'>;
|
|
91
94
|
|
|
92
95
|
/**
|
|
93
|
-
*
|
|
96
|
+
* Optional. Keyboard vertical offset.
|
|
94
97
|
*/
|
|
95
|
-
|
|
98
|
+
keyboardVerticalOffset?: number;
|
|
99
|
+
|
|
96
100
|
/**
|
|
97
101
|
* Optional. If `true`, the screen content is using grid layout by span.
|
|
98
102
|
*/
|
|
@@ -115,13 +119,11 @@ const Screen = forwardRef(
|
|
|
115
119
|
headerType = 'default',
|
|
116
120
|
floatingButtonProps,
|
|
117
121
|
useGridLayout = true,
|
|
118
|
-
|
|
122
|
+
keyboardVerticalOffset,
|
|
119
123
|
}: ScreenProps,
|
|
120
124
|
ref,
|
|
121
125
|
) => {
|
|
122
|
-
const animatedValue = useRef<Animated.Value>(
|
|
123
|
-
animated ?? new Animated.Value(0),
|
|
124
|
-
);
|
|
126
|
+
const animatedValue = useRef<Animated.Value>(new Animated.Value(0));
|
|
125
127
|
const {theme} = useContext(ApplicationContext);
|
|
126
128
|
const insets = useSafeAreaInsets();
|
|
127
129
|
const heightHeader = useHeaderHeight();
|
|
@@ -132,10 +134,9 @@ const Screen = forwardRef(
|
|
|
132
134
|
let handleScroll;
|
|
133
135
|
let headerBackground: string | undefined = undefined;
|
|
134
136
|
let Component: any = View;
|
|
135
|
-
let
|
|
136
|
-
|
|
137
|
+
let keyboardOffset = heightHeader - 20;
|
|
137
138
|
if (headerType == 'extended' || animatedHeader) {
|
|
138
|
-
|
|
139
|
+
keyboardOffset = -20;
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
if (headerType == 'extended') {
|
|
@@ -235,7 +236,7 @@ const Screen = forwardRef(
|
|
|
235
236
|
if (scrollable) {
|
|
236
237
|
Component = Animated.ScrollView;
|
|
237
238
|
handleScroll = scrollViewProps?.onScroll;
|
|
238
|
-
if (animatedHeader || floatingButtonProps) {
|
|
239
|
+
if (animatedHeader || floatingButtonProps?.icon) {
|
|
239
240
|
handleScroll = Animated.event(
|
|
240
241
|
[
|
|
241
242
|
{
|
|
@@ -339,7 +340,7 @@ const Screen = forwardRef(
|
|
|
339
340
|
<KeyboardAvoidingView
|
|
340
341
|
style={Styles.flex}
|
|
341
342
|
enabled={enableKeyboardAvoidingView}
|
|
342
|
-
keyboardVerticalOffset={keyboardVerticalOffset}
|
|
343
|
+
keyboardVerticalOffset={keyboardVerticalOffset ?? keyboardOffset}
|
|
343
344
|
behavior={Platform.select({
|
|
344
345
|
ios: 'padding',
|
|
345
346
|
android: undefined,
|