@momo-kits/foundation 0.92.34 → 0.102.1

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.
@@ -0,0 +1,113 @@
1
+ import React, {FC, memo, useEffect, useRef} from 'react';
2
+ import {Platform, StyleSheet, TextInput, TextStyle} from 'react-native';
3
+ import {Colors} from '../Consts';
4
+ import {scaleSize} from '../Text';
5
+
6
+ export interface TextTypingProps {
7
+ data: string[];
8
+ timeDelayEnd?: number;
9
+ timeDelayNextChar?: number;
10
+ inputStyle?: TextStyle;
11
+ }
12
+
13
+ const TextTyping: FC<TextTypingProps> = ({
14
+ data = [],
15
+ timeDelayEnd = 2000,
16
+ timeDelayNextChar = 20,
17
+ inputStyle = {},
18
+ }) => {
19
+ const textRef = useRef<TextInput>(null);
20
+
21
+ const currentIndex = useRef<number>(0);
22
+ const currentCharIndex = useRef<number>(0);
23
+ const currentText = useRef('');
24
+
25
+ useEffect(() => {
26
+ setTimeout(() => {
27
+ playAnimation();
28
+ }, 1000);
29
+ }, []);
30
+
31
+ const playAnimation = () => {
32
+ const handleResetList = () => {
33
+ currentIndex.current = 0;
34
+ handleAnimation();
35
+ };
36
+
37
+ const handleAnimation = () => {
38
+ const listChar = data?.[currentIndex.current]?.split('') || [];
39
+ listChar.length !== 0 && showChar(listChar?.[currentCharIndex.current]);
40
+ };
41
+
42
+ currentIndex.current >= data?.length
43
+ ? handleResetList()
44
+ : handleAnimation();
45
+ };
46
+
47
+ const showChar = (char?: string) => {
48
+ const handleResetString = () => {
49
+ setTimeout(() => {
50
+ reset();
51
+ }, timeDelayEnd);
52
+ };
53
+
54
+ const handleShowChar = () => {
55
+ setTimeout(() => {
56
+ textRef.current?.setNativeProps?.({
57
+ text: currentText.current + char,
58
+ });
59
+ currentText.current += char;
60
+ currentCharIndex.current++;
61
+ playAnimation();
62
+ }, timeDelayNextChar);
63
+ };
64
+
65
+ currentCharIndex.current >= (data?.[currentIndex.current]?.length || 0)
66
+ ? handleResetString()
67
+ : handleShowChar();
68
+ };
69
+
70
+ const reset = () => {
71
+ const handleNextString = () => {
72
+ currentIndex.current++;
73
+ playAnimation();
74
+ };
75
+
76
+ const handleReset = () => {
77
+ setTimeout(() => {
78
+ textRef.current?.setNativeProps?.({
79
+ text: currentText.current.slice(0, -1),
80
+ });
81
+ currentText.current = currentText.current.slice(0, -1);
82
+ currentCharIndex.current--;
83
+ reset();
84
+ }, timeDelayNextChar);
85
+ };
86
+
87
+ currentCharIndex.current <= 0 ? handleNextString() : handleReset();
88
+ };
89
+
90
+ return (
91
+ <TextInput
92
+ style={[styles.inputStyle, inputStyle]}
93
+ ref={textRef}
94
+ editable={false}
95
+ autoCorrect={false}
96
+ pointerEvents={'none'}
97
+ numberOfLines={1}
98
+ />
99
+ );
100
+ };
101
+
102
+ export default memo(TextTyping);
103
+
104
+ const styles = StyleSheet.create({
105
+ inputStyle: {
106
+ color: Colors.black_12,
107
+ fontSize: scaleSize(12),
108
+ height: Platform.select({
109
+ android: 60,
110
+ ios: 36,
111
+ }),
112
+ },
113
+ });
package/Input/index.tsx CHANGED
@@ -187,6 +187,17 @@ export interface InputSearchProps extends TextInputProps {
187
187
  * Optional. Represents the style of the InputSearch component.
188
188
  */
189
189
  params?: any;
190
+
191
+ /**
192
+ * Optional. Represents the data for typing animation.
193
+ */
194
+ typingData?: string[];
195
+
196
+ /**
197
+ * Optional. Represents the callback function to be called when the search button is pressed.
198
+ * @param e
199
+ */
200
+ onPress?: (e: GestureResponderEvent) => void;
190
201
  }
191
202
 
192
203
  export interface InputMoneyProps extends Omit<InputProps, 'placeholder'> {}
@@ -265,4 +276,11 @@ export interface InputDropDownProps
265
276
  multiline?: boolean;
266
277
  }
267
278
 
279
+ export type InputRef = {
280
+ clear: () => void;
281
+ focus: () => void | undefined;
282
+ blur: () => void | undefined;
283
+ setText: (text: string) => void;
284
+ };
285
+
268
286
  export {Input, InputDropDown, InputMoney, InputOTP, InputSearch, InputTextArea};
package/Input/styles.ts CHANGED
@@ -1,5 +1,5 @@
1
- import {StyleSheet} from 'react-native';
2
- import {Radius, Spacing} from '../Consts';
1
+ import {Platform, StyleSheet} from 'react-native';
2
+ import {Colors, Radius, Spacing} from '../Consts';
3
3
  import {scaleSize} from '../Text';
4
4
 
5
5
  export default StyleSheet.create({
@@ -133,20 +133,27 @@ export default StyleSheet.create({
133
133
  paddingVertical: Spacing.S,
134
134
  fontWeight: 'bold',
135
135
  },
136
-
136
+ inputStyle: {
137
+ color: Colors.black_12,
138
+ fontSize: scaleSize(12),
139
+ height: Platform.select({
140
+ android: 60,
141
+ ios: 36,
142
+ }),
143
+ },
137
144
  //OTP
138
145
  otpWrapper: {
139
146
  width: '100%',
140
147
  marginTop: Spacing.M,
141
148
  },
142
149
  otpInput: {
143
- height: scaleSize(56),
150
+ height: 56,
144
151
  borderRadius: Radius.S,
145
152
  borderWidth: 1,
146
153
  },
147
154
  otpFloatingView: {
148
155
  position: 'absolute',
149
- top: scaleSize(-Spacing.M + 2),
156
+ top: -Spacing.M + 2,
150
157
  alignSelf: 'center',
151
158
  paddingHorizontal: Spacing.S,
152
159
  },
package/Layout/Screen.tsx CHANGED
@@ -3,6 +3,7 @@ import React, {
3
3
  forwardRef,
4
4
  Fragment,
5
5
  ReactNode,
6
+ Ref,
6
7
  useContext,
7
8
  useEffect,
8
9
  useRef,
@@ -27,10 +28,11 @@ import Navigation from '../Application/Navigation';
27
28
  import {AnimatedHeader, NavigationOptions} from '../Application/types';
28
29
  import {Colors, Spacing, Styles} from '../Consts';
29
30
  import {FloatingButton, FloatingButtonProps} from './FloatingButton';
30
- import {Image} from '../Image';
31
31
  import {Card, Section, validateChildren} from './index';
32
32
  import styles from './styles';
33
33
  import {HeaderType} from './types';
34
+ import {InputRef, InputSearchProps} from '../Input';
35
+ import {HeaderExtendHeader} from '../Application/Components';
34
36
 
35
37
  export interface ScreenProps extends ViewProps {
36
38
  /**
@@ -101,6 +103,13 @@ export interface ScreenProps extends ViewProps {
101
103
  * Optional. If `true`, the screen content is using grid layout by span.
102
104
  */
103
105
  useGridLayout?: boolean;
106
+
107
+ /**
108
+ * Optional. specs for input search.
109
+ */
110
+ inputSearchProps?: InputSearchProps;
111
+
112
+ inputSearchRef?: Ref<InputRef>;
104
113
  }
105
114
 
106
115
  const Screen = forwardRef(
@@ -120,32 +129,133 @@ const Screen = forwardRef(
120
129
  floatingButtonProps,
121
130
  useGridLayout = true,
122
131
  keyboardVerticalOffset,
132
+ inputSearchProps,
133
+ inputSearchRef,
123
134
  }: ScreenProps,
124
135
  ref
125
136
  ) => {
126
- const animatedValue = useRef<Animated.Value>(new Animated.Value(0));
127
137
  const {theme} = useContext(ApplicationContext);
128
138
  const insets = useSafeAreaInsets();
129
139
  const heightHeader = useHeaderHeight();
140
+ const animatedValue = useRef<Animated.Value>(new Animated.Value(0));
141
+ const scrollViewRef = useRef<any>(ref);
130
142
  const currentTint = useRef(Colors.black_01);
131
143
  const isTab = navigation?.instance?.getState?.()?.type === 'tab';
132
144
 
133
- let styleAnimatedHeader: NavigationOptions;
134
145
  let handleScroll;
135
- let headerBackground: string | undefined = undefined;
136
146
  let Component: any = View;
137
147
  let keyboardOffset = heightHeader - 20;
138
- if (headerType === 'extended' || animatedHeader) {
148
+ if (headerType === 'extended' || animatedHeader || inputSearchProps) {
139
149
  keyboardOffset = -20;
140
150
  }
141
151
 
142
- if (headerType === 'extended') {
143
- headerBackground = theme.assets?.headerBackground;
144
- }
145
-
146
152
  useEffect(() => {
153
+ let options: NavigationOptions;
154
+ /**
155
+ * handle for basic header type
156
+ */
157
+ switch (headerType) {
158
+ case 'none':
159
+ options = {
160
+ headerTransparent: true,
161
+ headerBackground: () => null,
162
+ headerShown: false,
163
+ };
164
+ break;
165
+
166
+ case 'surface':
167
+ options = {
168
+ headerTransparent: false,
169
+ headerShown: true,
170
+ headerTintColor: theme.colors.text.default,
171
+ headerBackground: (props: any) => (
172
+ <HeaderBackground {...props} image={null} useSurface={true} />
173
+ ),
174
+ headerTitle: (props: any) => <HeaderTitle {...props} />,
175
+ };
176
+ if (inputSearchProps) {
177
+ options = {
178
+ headerShown: true,
179
+ headerTransparent: true,
180
+ headerTintColor: theme.colors.text.default,
181
+ headerBackground: () => null,
182
+ headerTitle: (props: any) => (
183
+ <HeaderTitle
184
+ {...props}
185
+ interpolate={{
186
+ inputRange: [0, 50],
187
+ outputRange: [1, 0],
188
+ extrapolate: 'clamp',
189
+ }}
190
+ animatedValue={animatedValue.current}
191
+ />
192
+ ),
193
+ };
194
+ }
195
+ break;
196
+
197
+ case 'extended':
198
+ options = {
199
+ headerShown: true,
200
+ headerTransparent: true,
201
+ headerTintColor: Colors.black_01,
202
+ headerBackground: () => null,
203
+ headerTitle: (props: any) => <HeaderTitle {...props} />,
204
+ };
205
+ if (inputSearchProps) {
206
+ options = {
207
+ headerShown: true,
208
+ headerTransparent: true,
209
+ headerTintColor: Colors.black_01,
210
+ headerBackground: () => null,
211
+ headerTitle: (props: any) => (
212
+ <HeaderTitle
213
+ {...props}
214
+ interpolate={{
215
+ inputRange: [0, 50],
216
+ outputRange: [1, 0],
217
+ extrapolate: 'clamp',
218
+ }}
219
+ animatedValue={animatedValue.current}
220
+ />
221
+ ),
222
+ };
223
+ }
224
+ break;
225
+
226
+ default:
227
+ options = {
228
+ headerTransparent: false,
229
+ headerShown: true,
230
+ headerTintColor: Colors.black_01,
231
+ headerBackground: (props: any) => <HeaderBackground {...props} />,
232
+ };
233
+ if (inputSearchProps) {
234
+ options = {
235
+ headerShown: true,
236
+ headerTransparent: true,
237
+ headerTintColor: Colors.black_01,
238
+ headerBackground: () => null,
239
+ headerTitle: (props: any) => (
240
+ <HeaderTitle
241
+ {...props}
242
+ interpolate={{
243
+ inputRange: [0, 50],
244
+ outputRange: [1, 0],
245
+ extrapolate: 'clamp',
246
+ }}
247
+ animatedValue={animatedValue.current}
248
+ />
249
+ ),
250
+ };
251
+ }
252
+ }
253
+
254
+ /**
255
+ * override options for animated header
256
+ */
147
257
  if (animatedHeader) {
148
- styleAnimatedHeader = {
258
+ options = {
149
259
  headerTransparent: true,
150
260
  headerBackground: (props: any) => (
151
261
  <HeaderBackground
@@ -158,8 +268,8 @@ const Screen = forwardRef(
158
268
  ),
159
269
  };
160
270
  if (animatedHeader.type === 'surface') {
161
- styleAnimatedHeader = {
162
- ...styleAnimatedHeader,
271
+ options = {
272
+ ...options,
163
273
  headerBackground: (props: any) => (
164
274
  <HeaderBackground
165
275
  {...props}
@@ -171,8 +281,8 @@ const Screen = forwardRef(
171
281
  };
172
282
  }
173
283
  if (animatedHeader.headerTitle) {
174
- styleAnimatedHeader = {
175
- ...styleAnimatedHeader,
284
+ options = {
285
+ ...options,
176
286
  headerTitle: (props: any) =>
177
287
  animatedHeader.headerTitle?.({
178
288
  ...props,
@@ -180,55 +290,10 @@ const Screen = forwardRef(
180
290
  }),
181
291
  };
182
292
  }
183
- navigation?.setOptions(styleAnimatedHeader);
184
293
  }
185
- }, [animatedHeader]);
186
294
 
187
- useEffect(() => {
188
- if (!animatedHeader) {
189
- let headerOptions: NavigationOptions;
190
- switch (headerType) {
191
- case 'none':
192
- headerOptions = {
193
- headerTransparent: true,
194
- headerBackground: () => null,
195
- headerShown: false,
196
- };
197
- break;
198
-
199
- case 'surface':
200
- headerOptions = {
201
- headerTransparent: false,
202
- headerShown: true,
203
- headerTintColor: theme.colors.text.default,
204
- headerBackground: (props: any) => (
205
- <HeaderBackground {...props} image={null} useSurface={true} />
206
- ),
207
- headerTitle: (props: any) => <HeaderTitle {...props} />,
208
- };
209
- break;
210
-
211
- case 'extended':
212
- headerOptions = {
213
- headerShown: true,
214
- headerTransparent: true,
215
- headerTintColor: Colors.black_01,
216
- headerBackground: () => null,
217
- headerTitle: (props: any) => <HeaderTitle {...props} />,
218
- };
219
- break;
220
-
221
- default:
222
- headerOptions = {
223
- headerTransparent: false,
224
- headerShown: true,
225
- headerTintColor: Colors.black_01,
226
- headerBackground: (props: any) => <HeaderBackground {...props} />,
227
- };
228
- }
229
- navigation?.setOptions(headerOptions);
230
- }
231
- }, [headerType]);
295
+ navigation?.setOptions(options);
296
+ }, [headerType, animatedHeader, inputSearchProps]);
232
297
 
233
298
  /**
234
299
  * animated when use scroll && animated value
@@ -236,7 +301,7 @@ const Screen = forwardRef(
236
301
  if (scrollable) {
237
302
  Component = Animated.ScrollView;
238
303
  handleScroll = scrollViewProps?.onScroll;
239
- if (animatedHeader || floatingButtonProps?.icon) {
304
+ if (animatedHeader || floatingButtonProps?.icon || inputSearchProps) {
240
305
  handleScroll = Animated.event(
241
306
  [
242
307
  {
@@ -268,19 +333,40 @@ const Screen = forwardRef(
268
333
  }
269
334
  }
270
335
 
336
+ /**
337
+ * handle scroll end
338
+ * @param e
339
+ */
340
+ const handleScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
341
+ const offsetY = e.nativeEvent.contentOffset.y;
342
+ if (inputSearchProps && offsetY < 100 && offsetY > 0) {
343
+ Animated.timing(animatedValue.current, {
344
+ toValue: 0,
345
+ useNativeDriver: true,
346
+ duration: 300,
347
+ }).start();
348
+ scrollViewRef.current?.scrollTo({y: 0, animated: true});
349
+ }
350
+ scrollViewProps?.onScrollEndDrag?.(e);
351
+ };
352
+
271
353
  /**
272
354
  * render top navigation banner
273
355
  */
274
- const renderHeader = () => {
356
+ const renderAnimatedHeader = () => {
275
357
  if (typeof animatedHeader?.component === 'function') {
276
- return animatedHeader?.component({
277
- animatedValue: animatedValue.current,
278
- });
358
+ return (
359
+ <View style={[styles.screenBanner, {maxHeight: 210 + layoutOffset}]}>
360
+ {animatedHeader?.component({
361
+ animatedValue: animatedValue.current,
362
+ })}
363
+ </View>
364
+ );
279
365
  }
280
366
  };
281
367
 
282
368
  /**
283
- * build content for screen
369
+ * build content for screen for grid rule
284
370
  */
285
371
  const renderContent = (children: any): any => {
286
372
  const results = validateChildren(children, [Section, Card, undefined]);
@@ -326,17 +412,15 @@ const Screen = forwardRef(
326
412
 
327
413
  return (
328
414
  <View style={[Styles.flex, {backgroundColor}]}>
329
- {!!headerBackground && (
330
- <>
331
- <Image
332
- source={{
333
- uri: headerBackground,
334
- }}
335
- style={styles.extendedHeader}
336
- />
337
- <View style={{height: heightHeader}} />
338
- </>
339
- )}
415
+ <HeaderExtendHeader
416
+ headerType={headerType}
417
+ heightHeader={heightHeader}
418
+ animatedValue={animatedValue.current}
419
+ inputSearchProps={inputSearchProps}
420
+ navigation={navigation}
421
+ inputSearchRef={inputSearchRef}
422
+ />
423
+
340
424
  <KeyboardAvoidingView
341
425
  style={Styles.flex}
342
426
  enabled={enableKeyboardAvoidingView}
@@ -349,14 +433,14 @@ const Screen = forwardRef(
349
433
 
350
434
  <Component
351
435
  {...scrollViewProps}
352
- ref={ref}
436
+ ref={scrollViewRef}
353
437
  showsVerticalScrollIndicator={false}
354
438
  onScroll={handleScroll}
439
+ onScrollEndDrag={handleScrollEnd}
440
+ scrollEventThrottle={16}
355
441
  style={Styles.flex}>
356
- <View
357
- style={[styles.screenBanner, {maxHeight: 210 + layoutOffset}]}>
358
- {renderHeader()}
359
- </View>
442
+ {renderAnimatedHeader()}
443
+
360
444
  {useGridLayout ? renderContent(children) : children}
361
445
  </Component>
362
446
 
package/Layout/styles.ts CHANGED
@@ -2,12 +2,6 @@ import {Platform, StyleSheet} from 'react-native';
2
2
  import {Colors, Radius, Spacing} from '../Consts';
3
3
 
4
4
  export default StyleSheet.create({
5
- extendedHeader: {
6
- aspectRatio: 1.75,
7
- position: 'absolute',
8
- width: '100%',
9
- height: 210,
10
- },
11
5
  screenBanner: {
12
6
  width: '100%',
13
7
  },
package/Layout/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type HeaderType = 'default' | 'surface' | 'extended' | 'none';
1
+ export type HeaderType = 'default' | 'surface' | 'extended' | 'search' | 'none';
2
2
 
3
3
  export type GridContextProps = {
4
4
  /**
@@ -42,7 +42,7 @@ const PaginationScroll: FC<ScrollIndicatorProps> = ({
42
42
  },
43
43
  },
44
44
  ],
45
- {useNativeDriver: true},
45
+ {useNativeDriver: true}
46
46
  )}
47
47
  alwaysBounceHorizontal={false}
48
48
  showsHorizontalScrollIndicator={false}
@@ -80,7 +80,7 @@ const PaginationScroll: FC<ScrollIndicatorProps> = ({
80
80
  return (
81
81
  <View style={[style, styles.scrollContainer]}>
82
82
  {renderScrollView()}
83
- {renderIndicator()}
83
+ {scrollContentWidth > scrollViewWidth && renderIndicator()}
84
84
  </View>
85
85
  );
86
86
  };
@@ -29,7 +29,6 @@ export default StyleSheet.create({
29
29
  justifyContent: 'center',
30
30
  },
31
31
  scrollContainer: {
32
- alignItems: 'center',
33
32
  width: '100%',
34
33
  },
35
34
  scrollView: {
@@ -42,6 +41,7 @@ export default StyleSheet.create({
42
41
  borderRadius: Radius.XS,
43
42
  },
44
43
  indicatorContainer: {
44
+ alignSelf: 'center',
45
45
  width: 72,
46
46
  height: 4,
47
47
  borderRadius: Radius.XS,
@@ -195,7 +195,7 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
195
195
  ]}>
196
196
  {!!image && <Image source={{uri: image}} style={styles.image} />}
197
197
  <View style={styles.content}>
198
- <Text typography={'header_default_bold'} numberOfLines={1}>
198
+ <Text typography={'header_default_bold'} numberOfLines={3}>
199
199
  {title}
200
200
  </Text>
201
201
  <Description style={[styles.description, descriptionStyle]}>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.92.34",
3
+ "version": "0.102.1",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},
package/publish.sh CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/bin/bash
2
2
 
3
3
  if [ "$1" == "stable" ]; then
4
- npm version $(npm view @momo-kits/foundation@stable version)
5
- npm version patch
4
+ #npm version $(npm view @momo-kits/foundation@stable version)
5
+ #npm version patch
6
6
  npm publish --tag stable --access=public
7
7
  elif [ "$1" == "latest" ]; then
8
- npm version $(npm view @momo-kits/foundation@latest version)
9
- npm version prerelease --preid=rc
8
+ #npm version $(npm view @momo-kits/foundation@latest version)
9
+ #npm version prerelease --preid=rc
10
10
  npm publish --tag latest --access=public
11
11
  else
12
- npm version $(npm view @momo-kits/foundation@beta version)
13
- npm version prerelease --preid=beta
12
+ #npm version $(npm view @momo-kits/foundation@beta version)
13
+ #npm version prerelease --preid=beta
14
14
  npm publish --tag beta --access=public
15
15
  fi
16
16