@momo-kits/foundation 0.92.6-beta.5 → 0.92.6-rc.10

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.
@@ -10,7 +10,7 @@ import {
10
10
  import {Modalize} from 'react-native-modalize';
11
11
  import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
12
12
 
13
- import {ApplicationContext} from '.';
13
+ import {ApplicationContext} from './index';
14
14
  import {Radius, Spacing, Styles} from '../Consts';
15
15
  import {Icon} from '../Icon';
16
16
  import {Text} from '../Text';
@@ -6,8 +6,8 @@ import {getOptions} from './utils';
6
6
  import {HeaderToolkitAction} from './Components';
7
7
 
8
8
  class Navigation {
9
- private instance: NavigationProp<any>;
10
- private readonly theme: Theme;
9
+ instance: NavigationProp<any>;
10
+ readonly theme: Theme;
11
11
 
12
12
  constructor(instance: any, theme: Theme) {
13
13
  this.instance = instance;
@@ -27,8 +27,8 @@ const StackScreen: React.FC<ScreenParams> = props => {
27
27
  const heightHeader = useHeaderHeight();
28
28
 
29
29
  const data = {
30
- ...props.route.params,
31
30
  ...initialParams,
31
+ ...props.route.params,
32
32
  navigation,
33
33
  };
34
34
 
package/Image/index.tsx CHANGED
@@ -2,7 +2,11 @@ import React, {useContext, useEffect, useRef, useState} from 'react';
2
2
  import {StyleSheet, View} from 'react-native';
3
3
  import FastImage, {Source} from 'react-native-fast-image';
4
4
  import styles from './styles';
5
- import {ApplicationContext, ComponentContext, MiniAppContext} from '../Application';
5
+ import {
6
+ ApplicationContext,
7
+ ComponentContext,
8
+ MiniAppContext,
9
+ } from '../Application';
6
10
  import {Skeleton} from '../Skeleton';
7
11
  import {Icon} from '../Icon';
8
12
  import {Styles} from '../Consts';
@@ -30,9 +34,9 @@ const Image: React.FC<ImageProps> = ({
30
34
  };
31
35
 
32
36
  useEffect(() => {
33
- if(app.enableAutoId){
37
+ if (app.enableAutoId) {
34
38
  component?.addChildren?.(test.accessibilityLabel, 'add');
35
- return () => component?.addChildren(test.accessibilityLabel, 'remove');
39
+ return () => component?.addChildren?.(test.accessibilityLabel, 'remove');
36
40
  }
37
41
  }, []);
38
42
 
@@ -57,37 +61,37 @@ const Image: React.FC<ImageProps> = ({
57
61
  };
58
62
 
59
63
  return (
60
- <FastImage
61
- {...rest}
62
- {...test}
63
- source={source}
64
- style={[styles.container, style]}
65
- onProgress={() => {
66
- error.current = false;
67
- if (status !== 'loading' && loading) {
68
- setStatus('loading');
69
- }
70
- }}
71
- onLoadStart={() => {
72
- error.current = false;
73
- }}
74
- onLoad={() => {
75
- error.current = false;
76
- }}
77
- onLoadEnd={() => {
78
- let current: Status = 'success';
79
- if (error.current) {
80
- current = 'error';
81
- }
82
- if (status !== current) {
83
- setStatus(current);
84
- }
85
- }}
86
- onError={() => {
87
- error.current = true;
88
- }}>
89
- {renderContent()}
90
- </FastImage>
64
+ <FastImage
65
+ {...rest}
66
+ {...test}
67
+ source={source}
68
+ style={[styles.container, style]}
69
+ onProgress={() => {
70
+ error.current = false;
71
+ if (status !== 'loading' && loading) {
72
+ setStatus('loading');
73
+ }
74
+ }}
75
+ onLoadStart={() => {
76
+ error.current = false;
77
+ }}
78
+ onLoad={() => {
79
+ error.current = false;
80
+ }}
81
+ onLoadEnd={() => {
82
+ let current: Status = 'success';
83
+ if (error.current) {
84
+ current = 'error';
85
+ }
86
+ if (status !== current) {
87
+ setStatus(current);
88
+ }
89
+ }}
90
+ onError={() => {
91
+ error.current = true;
92
+ }}>
93
+ {renderContent()}
94
+ </FastImage>
91
95
  );
92
96
  };
93
97
 
package/Input/Input.tsx CHANGED
@@ -50,6 +50,7 @@ const Input = forwardRef(
50
50
  params,
51
51
  hintText,
52
52
  accessibilityLabel,
53
+ editable = true,
53
54
  ...props
54
55
  }: InputProps,
55
56
  ref,
@@ -168,7 +169,7 @@ const Input = forwardRef(
168
169
  <TextInput
169
170
  {...props}
170
171
  accessibilityLabel={accessibilityLabel}
171
- editable={props.editable && !disabled}
172
+ editable={editable && !disabled}
172
173
  secureTextEntry={secure}
173
174
  textAlignVertical="center"
174
175
  ref={inputRef}
@@ -0,0 +1,128 @@
1
+ import {default as React, useContext, useEffect, useRef, useState} from 'react';
2
+ import {
3
+ Animated,
4
+ LayoutChangeEvent,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ } from 'react-native';
8
+ import {ApplicationContext} from '../Application';
9
+ import {Icon} from '../Icon';
10
+ import {Text} from '../Text';
11
+
12
+ export interface FloatingButtonProps {
13
+ label?: string;
14
+ onPress?: () => void;
15
+ position?: 'center' | 'right';
16
+ icon?: string;
17
+ iconColor?: string;
18
+ size?: 'small' | 'large';
19
+ animatedValue?: Animated.Value;
20
+ bottom?: number;
21
+ }
22
+
23
+ export const FloatingButton: React.FC<FloatingButtonProps> = ({
24
+ label = '',
25
+ onPress = () => {},
26
+ position = 'right',
27
+ icon = '',
28
+ iconColor = '',
29
+ size = 'small',
30
+ animatedValue,
31
+ bottom = 12,
32
+ }: FloatingButtonProps) => {
33
+ const {theme} = useContext(ApplicationContext);
34
+ const maxWidth = useRef(0);
35
+ const minWidth = size == 'small' ? 36 : 48;
36
+ const [widthAnimated, setWidthAnimated] = useState<Animated.Value>();
37
+ const lastOffset = useRef(0);
38
+ const debounce = useRef<any>();
39
+ const [showText, setShowText] = React.useState(true);
40
+
41
+ useEffect(() => {
42
+ if (!label) return;
43
+
44
+ const listener = animatedValue?.addListener(({value}) => {
45
+ clearTimeout(debounce.current);
46
+ debounce.current = setTimeout(() => {
47
+ const down = value > lastOffset.current;
48
+ lastOffset.current = value;
49
+ if (down) {
50
+ Animated.timing(widthAnimated!, {
51
+ toValue: minWidth,
52
+ duration: 300,
53
+ useNativeDriver: false,
54
+ }).start(() => setShowText(false));
55
+ } else {
56
+ Animated.timing(widthAnimated!, {
57
+ toValue: maxWidth.current,
58
+ duration: 300,
59
+ useNativeDriver: false,
60
+ }).start(() => setShowText(true));
61
+ }
62
+ }, 500);
63
+ });
64
+
65
+ return () => {
66
+ if (listener != null) {
67
+ animatedValue?.removeListener(listener);
68
+ }
69
+ };
70
+ }, [label, widthAnimated]);
71
+
72
+ const handleLayout = (event: LayoutChangeEvent) => {
73
+ const layout = event.nativeEvent.layout;
74
+ if (widthAnimated) return;
75
+ maxWidth.current = layout.width;
76
+ setWidthAnimated(new Animated.Value(layout.width));
77
+ };
78
+
79
+ return (
80
+ <Animated.View
81
+ onLayout={handleLayout}
82
+ style={[
83
+ styles.container,
84
+ {
85
+ right: position === 'right' ? 12 : undefined,
86
+ alignSelf: position === 'center' ? 'center' : 'flex-end',
87
+ width: widthAnimated,
88
+ height: size == 'small' ? 36 : 48,
89
+ backgroundColor: theme.colors.primary,
90
+ bottom,
91
+ },
92
+ ]}>
93
+ <TouchableOpacity
94
+ style={{flexDirection: 'row', alignItems: 'center'}}
95
+ onPress={onPress}>
96
+ <Icon
97
+ source={icon}
98
+ size={size === 'small' ? 12 : 24}
99
+ color={iconColor}
100
+ />
101
+ {showText && !!label && (
102
+ <Text
103
+ style={styles.label}
104
+ color="white"
105
+ typography="action_default_bold">
106
+ {label}
107
+ </Text>
108
+ )}
109
+ </TouchableOpacity>
110
+ </Animated.View>
111
+ );
112
+ };
113
+
114
+ const styles = StyleSheet.create({
115
+ container: {
116
+ flexDirection: 'row',
117
+ alignContent: 'center',
118
+ alignItems: 'center',
119
+ position: 'absolute',
120
+ borderRadius: 100,
121
+ paddingHorizontal: 12,
122
+ flexWrap: 'wrap',
123
+ alignSelf: 'flex-end',
124
+ },
125
+ label: {
126
+ marginLeft: 8,
127
+ },
128
+ });
package/Layout/Screen.tsx CHANGED
@@ -3,16 +3,13 @@ import React, {
3
3
  forwardRef,
4
4
  Fragment,
5
5
  ReactNode,
6
- useCallback,
7
6
  useContext,
8
7
  useEffect,
9
8
  useRef,
10
9
  } from 'react';
11
10
  import {
12
11
  Animated,
13
- Easing,
14
12
  KeyboardAvoidingView,
15
- LayoutChangeEvent,
16
13
  NativeScrollEvent,
17
14
  NativeSyntheticEvent,
18
15
  Platform,
@@ -29,15 +26,11 @@ import {
29
26
  import Navigation from '../Application/Navigation';
30
27
  import {AnimatedHeader, NavigationOptions} from '../Application/types';
31
28
  import {Colors, Spacing, Styles} from '../Consts';
32
- import {
33
- FloatingButton,
34
- FloatingButtonProps,
35
- } from '../FloatingButton/FloatingButton';
29
+ import {FloatingButton, FloatingButtonProps} from './FloatingButton';
36
30
  import {Image} from '../Image';
37
31
  import {Card, Section, validateChildren} from './index';
38
32
  import styles from './styles';
39
33
  import {HeaderType} from './types';
40
- import {debounce} from './utils';
41
34
 
42
35
  export interface ScreenProps extends ViewProps {
43
36
  children: React.ReactNode;
@@ -94,7 +87,12 @@ export interface ScreenProps extends ViewProps {
94
87
  /**
95
88
  * Optional. Props for the floating action button.
96
89
  */
97
- floatingButtonProps?: Omit<FloatingButtonProps, 'animatedValue'>;
90
+ floatingButtonProps?: Omit<FloatingButtonProps, 'animatedValue' | 'bottom'>;
91
+
92
+ /**
93
+ * Optional. If `true`, the screen content is using grid layout by span.
94
+ */
95
+ useGridLayout?: boolean;
98
96
  }
99
97
 
100
98
  const Screen = forwardRef(
@@ -112,24 +110,22 @@ const Screen = forwardRef(
112
110
  backgroundColor,
113
111
  headerType = 'default',
114
112
  floatingButtonProps,
113
+ useGridLayout = true,
115
114
  }: ScreenProps,
116
115
  ref,
117
116
  ) => {
118
- const animatedValue = useRef<Animated.Value | undefined>(undefined);
119
- const lastScrollPosition = useRef(0);
120
- const animatedFABValue = useRef(new Animated.Value(0)).current;
117
+ const animatedValue = useRef<Animated.Value>(new Animated.Value(0));
121
118
  const {theme} = useContext(ApplicationContext);
122
119
  const insets = useSafeAreaInsets();
123
120
  const heightHeader = useHeaderHeight();
124
- const layoutHeight = useRef(0);
125
- const contentHeight = useRef(0);
126
121
  const currentTint = useRef(Colors.black_01);
122
+ const isTab = navigation?.instance?.getState?.()?.type == 'tab';
123
+
127
124
  let styleAnimatedHeader: NavigationOptions;
128
125
  let handleScroll;
129
126
  let headerBackground: string | undefined = undefined;
130
127
  let Component: any = View;
131
128
  let keyboardVerticalOffset = heightHeader - 20;
132
- animatedValue.current = undefined;
133
129
 
134
130
  if (headerType == 'extended' || animatedHeader) {
135
131
  keyboardVerticalOffset = -20;
@@ -194,12 +190,9 @@ const Screen = forwardRef(
194
190
  image={null}
195
191
  backgroundColor={theme.colors.background.surface}
196
192
  useBorder={true}
197
- animatedValue={animatedValue.current}
198
193
  />
199
194
  ),
200
- headerTitle: (props: any) => (
201
- <HeaderTitle {...props} animatedValue={animatedValue.current} />
202
- ),
195
+ headerTitle: (props: any) => <HeaderTitle {...props} />,
203
196
  };
204
197
  break;
205
198
 
@@ -232,8 +225,6 @@ const Screen = forwardRef(
232
225
  Component = Animated.ScrollView;
233
226
  handleScroll = scrollViewProps?.onScroll;
234
227
  if (animatedHeader || floatingButtonProps) {
235
- animatedValue.current = new Animated.Value(0);
236
-
237
228
  handleScroll = Animated.event(
238
229
  [
239
230
  {
@@ -258,46 +249,12 @@ const Screen = forwardRef(
258
249
  });
259
250
  }
260
251
  }
261
- if (floatingButtonProps) {
262
- const currentScrollPosition = e.nativeEvent.contentOffset.y;
263
- const isScrollingDown =
264
- currentScrollPosition > lastScrollPosition.current;
265
- scrollViewProps?.onScroll?.(e);
266
-
267
- if (currentScrollPosition < 0) {
268
- return;
269
- }
270
- const isAtBottom =
271
- currentScrollPosition +
272
- layoutHeight.current +
273
- Math.min(insets.bottom, 21) +
274
- Spacing.S >=
275
- contentHeight.current;
276
- if (isAtBottom) {
277
- return;
278
- }
279
- animateFAB(isScrollingDown);
280
-
281
- lastScrollPosition.current = currentScrollPosition;
282
- }
283
252
  },
284
253
  },
285
254
  );
286
255
  }
287
256
  }
288
257
 
289
- const animateFAB = useCallback(
290
- debounce((isScrollingDown: boolean) => {
291
- Animated.timing(animatedFABValue, {
292
- toValue: isScrollingDown ? 1 : 0,
293
- duration: 100,
294
- useNativeDriver: false,
295
- easing: Easing.linear,
296
- }).start();
297
- }, 300),
298
- [],
299
- );
300
-
301
258
  /**
302
259
  * render top navigation banner
303
260
  */
@@ -351,6 +308,7 @@ const Screen = forwardRef(
351
308
  );
352
309
  }
353
310
  };
311
+
354
312
  return (
355
313
  <View style={[Styles.flex, {backgroundColor}]}>
356
314
  {!!headerBackground && (
@@ -373,39 +331,32 @@ const Screen = forwardRef(
373
331
  android: undefined,
374
332
  })}>
375
333
  {Header}
334
+
376
335
  <Component
377
336
  {...scrollViewProps}
378
337
  ref={ref}
379
338
  showsVerticalScrollIndicator={false}
380
339
  onScroll={handleScroll}
381
- onContentSizeChange={(_: number, height: number) => {
382
- contentHeight.current = height;
383
- }}
384
- onLayout={(event: LayoutChangeEvent) => {
385
- layoutHeight.current = event.nativeEvent.layout.height;
386
- }}
387
340
  style={Styles.flex}>
388
341
  <View
389
342
  style={[styles.screenBanner, {maxHeight: 210 + layoutOffset}]}>
390
343
  {renderHeader()}
391
344
  </View>
392
- {renderContent(children)}
345
+ {useGridLayout ? renderContent(children) : children}
393
346
  </Component>
394
- <View>
395
- {floatingButtonProps && (
347
+
348
+ {floatingButtonProps && (
349
+ <View>
396
350
  <FloatingButton
397
- label={floatingButtonProps?.label}
398
- onPress={floatingButtonProps?.onPress}
399
- size={floatingButtonProps?.size}
400
- position={floatingButtonProps?.position}
401
- icon={floatingButtonProps?.icon}
402
- iconColor={floatingButtonProps?.iconColor}
403
- isBottomTab={floatingButtonProps?.isBottomTab}
404
- animatedValue={animatedFABValue}
405
- isFooter={!!Footer}
351
+ {...floatingButtonProps}
352
+ animatedValue={animatedValue.current}
353
+ bottom={
354
+ Footer || isTab ? 12 : Math.min(insets.bottom, 21) + Spacing.S
355
+ }
406
356
  />
407
- )}
408
- </View>
357
+ </View>
358
+ )}
359
+
409
360
  {Footer && (
410
361
  <View
411
362
  style={[
package/Layout/utils.ts CHANGED
@@ -51,20 +51,5 @@ const useGridSystem = () => {
51
51
 
52
52
  return {numberOfColumns};
53
53
  };
54
- function debounce<T extends (...args: any[]) => void>(
55
- func: T,
56
- wait: number,
57
- ): (...args: Parameters<T>) => void {
58
- let timeout: NodeJS.Timeout | null = null;
59
54
 
60
- return function (...args: Parameters<T>) {
61
- if (timeout !== null) {
62
- clearTimeout(timeout);
63
- }
64
- timeout = setTimeout(() => {
65
- func(...args);
66
- }, wait);
67
- };
68
- }
69
-
70
- export {debounce, useGridSystem, validateChildren};
55
+ export {useGridSystem, validateChildren};
package/package.json CHANGED
@@ -1,11 +1,9 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.92.6-beta.5",
3
+ "version": "0.92.6-rc.10",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
- "scripts": {
7
- "postinstall": "node verify.js"
8
- },
6
+ "scripts": {},
9
7
  "keywords": [
10
8
  "@momo-kits/foundation"
11
9
  ],
@@ -1,154 +0,0 @@
1
- import {
2
- default as React,
3
- useCallback,
4
- useContext,
5
- useEffect,
6
- useRef,
7
- useState,
8
- } from 'react';
9
- import {
10
- Animated,
11
- LayoutChangeEvent,
12
- StyleSheet,
13
- TouchableOpacity,
14
- } from 'react-native';
15
- import {useSafeAreaInsets} from 'react-native-safe-area-context';
16
- import {ApplicationContext} from '../Application';
17
- import {Spacing} from '../Consts';
18
- import {Icon} from '../Icon';
19
- import {Text} from '../Text';
20
-
21
- export interface FloatingButtonProps {
22
- label?: string;
23
- onPress?: () => void;
24
- position?: 'center' | 'right';
25
- icon?: string;
26
- iconColor?: string;
27
- size?: 'small' | 'large';
28
- animatedValue: Animated.Value;
29
- isBottomTab?: boolean;
30
- isFooter?: boolean;
31
- }
32
-
33
- export const FloatingButton: React.FC<FloatingButtonProps> = ({
34
- label = '',
35
- onPress = () => {},
36
- position = 'right',
37
- icon = '',
38
- iconColor = '',
39
- size = 'small',
40
- animatedValue,
41
- isBottomTab = false,
42
- isFooter = false,
43
- }: FloatingButtonProps) => {
44
- const {theme} = useContext(ApplicationContext);
45
- const insets = useSafeAreaInsets();
46
- const [isExpanded, setIsExpanded] = React.useState(true);
47
- const [init, setInit] = useState(true);
48
- const width = size == 'small' ? 36 : 48;
49
- const widthAnim = useRef(new Animated.Value(width)).current;
50
- const [textWidth, setTextWidth] = useState(0);
51
-
52
- useEffect(() => {
53
- if (!label) return;
54
-
55
- const listener = animatedValue.addListener(({value}) => {
56
- if (value === 1) {
57
- setIsExpanded(false);
58
- collapseFab();
59
- } else {
60
- setIsExpanded(true);
61
- expandFab();
62
- }
63
- });
64
-
65
- return () => {
66
- animatedValue?.removeListener(listener);
67
- };
68
- }, [textWidth, label, init]);
69
-
70
- // If the text width is greater than 0 and it's not initialized then expand the fab
71
- useEffect(() => {
72
- if (textWidth > 0 && !init) {
73
- expandFab();
74
- }
75
- }, [textWidth]);
76
-
77
- const handleLayout = (event: LayoutChangeEvent) => {
78
- const {width} = event.nativeEvent.layout;
79
-
80
- if (init) {
81
- setTextWidth(width);
82
- setInit(false);
83
- }
84
- };
85
-
86
- const expandFab = useCallback(() => {
87
- Animated.timing(widthAnim, {
88
- toValue: width + textWidth + 8,
89
- duration: 200,
90
- useNativeDriver: false,
91
- }).start();
92
- }, [textWidth, widthAnim]);
93
-
94
- const collapseFab = useCallback(() => {
95
- Animated.timing(widthAnim, {
96
- toValue: width,
97
- duration: 200,
98
- useNativeDriver: false,
99
- }).start();
100
- }, [widthAnim]);
101
-
102
- return (
103
- <Animated.View
104
- style={[
105
- styles.container,
106
- {
107
- right: position === 'right' ? 12 : undefined,
108
- alignSelf: position === 'center' ? 'center' : 'flex-end',
109
- width: init ? undefined : widthAnim,
110
- height: width,
111
- backgroundColor: theme.colors.primary,
112
- bottom:
113
- isFooter || isBottomTab
114
- ? 12
115
- : Math.min(insets.bottom, 21) + Spacing.S,
116
- },
117
- ]}>
118
- <TouchableOpacity
119
- style={{flexDirection: 'row', alignItems: 'center'}}
120
- onPress={onPress}>
121
- <Icon
122
- source={icon}
123
- size={size === 'small' ? 12 : 24}
124
- color={iconColor}
125
- />
126
- {isExpanded && !!label && (
127
- <Text
128
- onLayout={handleLayout}
129
- style={styles.label}
130
- color="white"
131
- typography="action_default_bold">
132
- Hello
133
- </Text>
134
- )}
135
- </TouchableOpacity>
136
- </Animated.View>
137
- );
138
- };
139
-
140
- const styles = StyleSheet.create({
141
- container: {
142
- flexDirection: 'row',
143
- alignContent: 'center',
144
- alignItems: 'center',
145
- position: 'absolute',
146
- borderRadius: 100,
147
- paddingHorizontal: 12,
148
- flexWrap: 'wrap',
149
- alignSelf: 'flex-end',
150
- },
151
- label: {
152
- marginLeft: 8,
153
- },
154
- });