@momo-kits/foundation 0.92.21-beta.1 → 0.92.21-beta.11

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.
@@ -88,13 +88,16 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
88
88
  if (barrierDismissible && !force) {
89
89
  return;
90
90
  }
91
+ setTimeout(() => {
92
+ callback?.();
93
+ }, 350);
94
+
91
95
  Animated.timing(pan, {
92
96
  toValue: {x: 0, y: heightDevice},
93
97
  useNativeDriver: false,
94
98
  duration: 100,
95
99
  }).start(() => {
96
100
  navigator?.pop();
97
- callback?.();
98
101
  });
99
102
  };
100
103
 
@@ -125,7 +125,7 @@ export default function BottomTabBar({
125
125
  );
126
126
  });
127
127
  },
128
- [JSON.stringify(routes), state, descriptors, focusedTab],
128
+ [JSON.stringify(routes), state, descriptors, focusedTab]
129
129
  );
130
130
 
131
131
  /**
@@ -153,7 +153,7 @@ export default function BottomTabBar({
153
153
  </View>
154
154
  <View
155
155
  style={{
156
- top: -6,
156
+ top: -4,
157
157
  backgroundColor: activeTintColor ?? theme.colors.primary,
158
158
  borderRadius: Radius.XL,
159
159
  paddingHorizontal: 6,
@@ -17,10 +17,9 @@ const Tab = createBottomTabNavigator();
17
17
  const Stack = createStackNavigator();
18
18
 
19
19
  const TabScreen: React.FC<NavigationScreenProps> = ({route}) => {
20
- const {theme} = useContext(ApplicationContext);
21
20
  let options = {};
22
21
  if (route.params?.options) {
23
- options = getOptions(route.params?.options, theme);
22
+ options = getOptions(route.params?.options);
24
23
  }
25
24
 
26
25
  if (route.params?.nested) {
@@ -101,7 +100,7 @@ const BottomTab: React.FC<BottomTabProps> = ({
101
100
  name={item.name}
102
101
  component={TabScreen}
103
102
  initialParams={{...item, nested}}
104
- listeners={listeners}
103
+ listeners={item.listeners ?? listeners}
105
104
  options={{
106
105
  tabBarLabel: ({color, focused}) => {
107
106
  return (
@@ -1,5 +1,4 @@
1
1
  import React, {
2
- FC,
3
2
  useCallback,
4
3
  useContext,
5
4
  useEffect,
@@ -30,7 +29,14 @@ import {scaleSize, Text} from '../Text';
30
29
  import {Icon} from '../Icon';
31
30
  import {PopupNotify} from '../Popup';
32
31
  import {Badge, BadgeDot} from '../Badge';
33
-
32
+ import {HeaderType} from '../Layout/types';
33
+ import Navigation from './Navigation';
34
+ import {InputSearchProps} from '../Input';
35
+ import AnimatedInputSearch from '../Layout/AnimatedInputSearch';
36
+
37
+ /**
38
+ * default navigation button used header nav
39
+ */
34
40
  const NavigationButton: React.FC<NavigationButtonProps> = ({
35
41
  icon,
36
42
  tintColor,
@@ -82,8 +88,18 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
82
88
  );
83
89
  };
84
90
 
91
+ /**
92
+ * default header title used for nav
93
+ */
85
94
  const HeaderTitle: React.FC<any> = props => {
86
95
  const {theme} = useContext(ApplicationContext);
96
+ const opacity = props.animatedValue?.interpolate(
97
+ props.interpolate ?? {
98
+ inputRange: [0, 200],
99
+ outputRange: [0, 1],
100
+ extrapolate: 'clamp',
101
+ }
102
+ );
87
103
 
88
104
  return (
89
105
  <Animated.Text
@@ -91,16 +107,15 @@ const HeaderTitle: React.FC<any> = props => {
91
107
  accessibilityLabel={'title_navigation_header'}
92
108
  style={[
93
109
  styles.title,
94
- {
95
- fontFamily: `${theme.font}-Bold`,
96
- color: props.tintColor,
97
- opacity: props.titleOpacity,
98
- },
110
+ {fontFamily: `${theme.font}-Bold`, opacity, color: props.tintColor},
99
111
  ]}
100
112
  />
101
113
  );
102
114
  };
103
115
 
116
+ /**
117
+ * default header left used for nav
118
+ */
104
119
  const HeaderLeft: React.FC<HeaderBackProps> = ({
105
120
  tintColor,
106
121
  preventBack,
@@ -168,6 +183,7 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
168
183
  }
169
184
  return true;
170
185
  };
186
+
171
187
  return (
172
188
  <View style={styles.headerLeft}>
173
189
  <NavigationButton
@@ -181,6 +197,9 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
181
197
  );
182
198
  };
183
199
 
200
+ /**
201
+ * header background for default
202
+ */
184
203
  const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
185
204
  image,
186
205
  animatedValue,
@@ -229,6 +248,10 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
229
248
  );
230
249
  };
231
250
 
251
+ /**
252
+ * Header custom with image, title, subtitle
253
+ * @constructor
254
+ */
232
255
  const HeaderCustom: React.FC<TitleCustomProps> = ({
233
256
  title,
234
257
  subTitle,
@@ -263,42 +286,42 @@ const HeaderCustom: React.FC<TitleCustomProps> = ({
263
286
  return <View style={styles.headerTitleContainer}>{content ?? header}</View>;
264
287
  };
265
288
 
266
- const HeaderRight: FC<any> = ({type, ...props}) => {
267
- let buttons = props?.buttons || [];
268
- if (!type) {
269
- return <View />;
270
- }
271
-
272
- if (type === 'iconButton') {
289
+ /**
290
+ * main component for header right
291
+ */
292
+ const HeaderRight: React.FC<any> = ({type, children, onLayout, ...props}) => {
293
+ if (type === 'icon') {
294
+ let buttons = props?.buttons || [];
273
295
  if (buttons?.length > 3) {
274
296
  buttons = buttons.slice(0, 3);
275
297
  }
276
298
  return (
277
- <View style={styles.headerRightButton}>
278
- {buttons?.map?.(
279
- ({icon, onPress}: NavigationButtonProps, index: number) => {
280
- return (
281
- <View
282
- key={`HeaderRightAction ${index}`}
283
- style={{
284
- marginLeft: index !== 0 ? Spacing.S : 0,
285
- }}>
286
- <NavigationButton
287
- icon={icon}
288
- onPress={onPress}
289
- tintColor={props?.tintColor}
290
- />
291
- </View>
292
- );
293
- }
294
- )}
299
+ <View style={styles.headerRightButton} onLayout={onLayout}>
300
+ {buttons?.map?.((item: NavigationButtonProps, index: number) => {
301
+ return (
302
+ <View
303
+ key={`HeaderRightAction ${index}`}
304
+ style={{
305
+ marginLeft: index !== 0 ? Spacing.S : 0,
306
+ }}>
307
+ <NavigationButton {...item} tintColor={props?.tintColor} />
308
+ </View>
309
+ );
310
+ })}
295
311
  </View>
296
312
  );
297
313
  }
298
314
 
299
- return <HeaderToolkitAction {...props} />;
315
+ return (
316
+ <View style={styles.headerRightButton} onLayout={onLayout}>
317
+ {children ?? <HeaderToolkitAction {...props} />}
318
+ </View>
319
+ );
300
320
  };
301
321
 
322
+ /**
323
+ * Header toolkit action
324
+ */
302
325
  const HeaderToolkitAction: React.FC<any> = ({
303
326
  tintColor,
304
327
  pinnedTool,
@@ -402,7 +425,7 @@ const HeaderToolkitAction: React.FC<any> = ({
402
425
  };
403
426
  if (toolConfig) {
404
427
  return (
405
- <View style={styles.headerRightButton}>
428
+ <View style={Styles.row}>
406
429
  {renderPinnedTool()}
407
430
  <View
408
431
  style={[
@@ -436,6 +459,9 @@ const HeaderToolkitAction: React.FC<any> = ({
436
459
  return <View />;
437
460
  };
438
461
 
462
+ /**
463
+ * default header banner for header animated
464
+ */
439
465
  const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
440
466
  animatedValue,
441
467
  image,
@@ -476,12 +502,27 @@ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
476
502
  );
477
503
  };
478
504
 
479
- const ExtendBackground: FC<{
480
- headerBackground: string;
505
+ /**
506
+ * Header extended with background image
507
+ * @constructor
508
+ */
509
+ const HeaderExtendHeader: React.FC<{
510
+ headerType?: HeaderType;
481
511
  animatedValue: Animated.Value;
482
512
  heightHeader: number;
483
- }> = ({headerBackground, animatedValue, heightHeader}) => {
513
+ inputSearchProps?: InputSearchProps;
514
+ navigation?: Navigation;
515
+ }> = ({
516
+ headerType = 'default',
517
+ animatedValue,
518
+ heightHeader,
519
+ inputSearchProps,
520
+ navigation,
521
+ }) => {
522
+ const {theme} = useContext(ApplicationContext);
523
+
484
524
  const heightAnimated = useRef(new Animated.Value(0));
525
+
485
526
  useEffect(() => {
486
527
  const listener = animatedValue.addListener(({value}) => {
487
528
  heightAnimated.current.setValue(value);
@@ -491,25 +532,120 @@ const ExtendBackground: FC<{
491
532
  };
492
533
  }, []);
493
534
 
494
- return (
495
- <>
496
- <Image
497
- source={{
498
- uri: headerBackground,
499
- }}
500
- style={styles.extendedHeader}
501
- />
502
- <Animated.View
503
- style={{
504
- height: heightAnimated.current.interpolate({
505
- inputRange: [0, 100],
506
- outputRange: [heightHeader, heightHeader - 52],
507
- extrapolate: 'clamp',
508
- }),
509
- }}
510
- />
511
- </>
512
- );
535
+ if (inputSearchProps) {
536
+ if (headerType === 'surface') {
537
+ return (
538
+ <View
539
+ style={{
540
+ backgroundColor: theme.colors.background.surface,
541
+ borderBottomWidth: 1,
542
+ borderColor: theme.colors.border.default,
543
+ }}>
544
+ <Animated.View
545
+ style={{
546
+ justifyContent: 'flex-end',
547
+ height: heightAnimated.current.interpolate({
548
+ inputRange: [0, 100],
549
+ outputRange: [heightHeader + 52, heightHeader],
550
+ extrapolate: 'clamp',
551
+ }),
552
+ }}>
553
+ <AnimatedInputSearch
554
+ animatedValue={animatedValue}
555
+ navigation={navigation}
556
+ {...inputSearchProps}
557
+ hasColorBG={true}
558
+ />
559
+ </Animated.View>
560
+ </View>
561
+ );
562
+ }
563
+
564
+ if (headerType === 'extended') {
565
+ return (
566
+ <>
567
+ <Animated.View
568
+ style={[
569
+ styles.headerBox,
570
+ {
571
+ height: heightAnimated.current.interpolate({
572
+ inputRange: [0, 100],
573
+ outputRange: [heightHeader + 52, heightHeader],
574
+ extrapolate: 'clamp',
575
+ }),
576
+ },
577
+ ]}>
578
+ <Image
579
+ source={{
580
+ uri: theme.assets?.headerBackground,
581
+ }}
582
+ style={styles.headerBackground}
583
+ />
584
+ </Animated.View>
585
+
586
+ <Animated.View
587
+ style={{
588
+ justifyContent: 'flex-end',
589
+ height: heightAnimated.current.interpolate({
590
+ inputRange: [0, 100],
591
+ outputRange: [heightHeader + 52, heightHeader],
592
+ extrapolate: 'clamp',
593
+ }),
594
+ }}>
595
+ <AnimatedInputSearch
596
+ animatedValue={animatedValue}
597
+ navigation={navigation}
598
+ {...inputSearchProps}
599
+ />
600
+ </Animated.View>
601
+ </>
602
+ );
603
+ }
604
+
605
+ return (
606
+ <>
607
+ <View style={[styles.headerBox, {height: heightHeader}]}>
608
+ <Image
609
+ source={{
610
+ uri: theme.assets?.headerBackground,
611
+ }}
612
+ style={styles.headerBackground}
613
+ />
614
+ </View>
615
+
616
+ <Animated.View
617
+ style={{
618
+ justifyContent: 'flex-end',
619
+ height: heightAnimated.current.interpolate({
620
+ inputRange: [0, 100],
621
+ outputRange: [heightHeader + 52, heightHeader],
622
+ extrapolate: 'clamp',
623
+ }),
624
+ }}>
625
+ <AnimatedInputSearch
626
+ animatedValue={animatedValue}
627
+ navigation={navigation}
628
+ {...inputSearchProps}
629
+ />
630
+ </Animated.View>
631
+ </>
632
+ );
633
+ }
634
+
635
+ if (headerType === 'extended') {
636
+ return (
637
+ <View style={{minHeight: heightHeader}}>
638
+ <Image
639
+ source={{
640
+ uri: theme.assets?.headerBackground,
641
+ }}
642
+ style={styles.extendedHeader}
643
+ />
644
+ </View>
645
+ );
646
+ }
647
+
648
+ return <View />;
513
649
  };
514
650
 
515
651
  const styles = StyleSheet.create({
@@ -588,6 +724,11 @@ const styles = StyleSheet.create({
588
724
  width: '100%',
589
725
  height: 210,
590
726
  },
727
+ headerBox: {
728
+ width: '100%',
729
+ position: 'absolute',
730
+ overflow: 'hidden',
731
+ },
591
732
  });
592
733
 
593
734
  export {
@@ -599,5 +740,5 @@ export {
599
740
  HeaderCustom,
600
741
  HeaderAnimated,
601
742
  HeaderRight,
602
- ExtendBackground,
743
+ HeaderExtendHeader,
603
744
  };
@@ -21,10 +21,10 @@ const ModalScreen: React.FC<any> = props => {
21
21
  };
22
22
 
23
23
  const Modal: React.FC<ModalParams> = props => {
24
- const {theme, navigator} = useContext(ApplicationContext);
24
+ const {navigator} = useContext(ApplicationContext);
25
25
  const {screen, barrierDismissible} = props.route.params;
26
26
  const Component = useRef(screen).current;
27
- const navigation = new Navigation(props.navigation, theme);
27
+ const navigation = new Navigation(props.navigation);
28
28
  const params = {
29
29
  ...props.route.params,
30
30
  navigation,
@@ -1,14 +1,13 @@
1
1
  import {NavigationProp} from '@react-navigation/native';
2
- import {NavigationOptions, Theme} from './types';
2
+ import {NavigationOptions} from './types';
3
3
  import {getOptions} from './utils';
4
4
 
5
5
  class Navigation {
6
6
  instance: NavigationProp<any>;
7
- readonly theme: Theme;
7
+ rightHeaderWidth?: number;
8
8
 
9
- constructor(instance: any, theme: Theme) {
9
+ constructor(instance: any) {
10
10
  this.instance = instance;
11
- this.theme = theme;
12
11
  }
13
12
 
14
13
  /**
@@ -29,7 +28,7 @@ class Navigation {
29
28
  * @param params
30
29
  */
31
30
  setOptions = (params: NavigationOptions) => {
32
- params = getOptions(this.filterParams(params), this.theme);
31
+ params = getOptions(this.filterParams(params), this);
33
32
  this.instance.setOptions(params);
34
33
  };
35
34
  }
@@ -88,7 +88,7 @@ const EmptyScreen: React.FC = () => {
88
88
  * @constructor
89
89
  */
90
90
  const StackScreen: React.FC<ScreenParams> = props => {
91
- const {theme, showGrid, navigator} = useContext(ApplicationContext);
91
+ const {showGrid, navigator} = useContext(ApplicationContext);
92
92
  const startTime = useRef(Date.now());
93
93
  const endTime = useRef(Date.now());
94
94
  const timeLoad = useRef(0);
@@ -104,7 +104,7 @@ const StackScreen: React.FC<ScreenParams> = props => {
104
104
  const context = useContext<any>(MiniAppContext);
105
105
 
106
106
  const {screen: Component, options, initialParams} = props.route.params;
107
- const navigation = new Navigation(props.navigation, theme);
107
+ const navigation = new Navigation(props.navigation);
108
108
  const heightHeader = useHeaderHeight();
109
109
 
110
110
  const data = {
@@ -5,7 +5,6 @@ import {
5
5
  HeaderAnimated,
6
6
  HeaderBackground,
7
7
  HeaderCustom,
8
- HeaderRightAction,
9
8
  HeaderTitle,
10
9
  NavigationButton,
11
10
  } from './Components';
@@ -31,7 +30,6 @@ export {
31
30
  NavigationButton,
32
31
  HeaderTitle,
33
32
  HeaderBackground,
34
- HeaderRightAction,
35
33
  HeaderCustom,
36
34
  HeaderAnimated,
37
35
  };
@@ -1,4 +1,5 @@
1
1
  import {StackNavigationOptions} from '@react-navigation/stack';
2
+ import {EventArg} from '@react-navigation/core';
2
3
  import React from 'react';
3
4
  import {Animated, ViewProps} from 'react-native';
4
5
  import {PopupNotifyProps} from '../Popup/types';
@@ -151,7 +152,7 @@ export interface NavigationOptions
151
152
  surface?: boolean;
152
153
  hiddenBack?: boolean;
153
154
  customTitle?: TitleCustomProps;
154
- headerRight?: HeaderRightToolkit | HeaderRightActions | Element;
155
+ headerRight?: HeaderRightToolkit | HeaderRightActions | any;
155
156
  }
156
157
 
157
158
  export type HeaderRightToolkit = {
@@ -163,7 +164,7 @@ export type HeaderRightToolkit = {
163
164
  };
164
165
 
165
166
  export type HeaderRightActions = {
166
- type?: 'iconButton';
167
+ type?: 'icon';
167
168
  buttons: NavigationButtonProps[];
168
169
  };
169
170
 
@@ -208,6 +209,11 @@ export type BottomTabItemProps = {
208
209
  screen: Screen;
209
210
  options: NavigationOptions;
210
211
  initialParams?: any;
212
+ listeners?: {
213
+ tabPress?: (e: EventArg<'tabPress', true, undefined>) => void;
214
+ focus?: (e: EventArg<'focus', false, unknown>) => void;
215
+ blur?: (e: EventArg<'blur', false, unknown>) => void;
216
+ };
211
217
  };
212
218
 
213
219
  export type BottomTabProps = {
@@ -216,8 +222,9 @@ export type BottomTabProps = {
216
222
  initialRouteName?: string;
217
223
  navigation: Navigation;
218
224
  listeners?: {
219
- tabPress?: any;
220
- focus?: any;
225
+ tabPress?: (e: EventArg<'tabPress', true, undefined>) => void;
226
+ focus?: (e: EventArg<'focus', false, unknown>) => void;
227
+ blur?: (e: EventArg<'blur', false, unknown>) => void;
221
228
  };
222
229
  floatingButton?: FloatingButtonProps;
223
230
  };
@@ -7,11 +7,13 @@ import {
7
7
  HeaderBackground,
8
8
  HeaderCustom,
9
9
  HeaderLeft,
10
+ HeaderRight,
10
11
  HeaderTitle,
11
- HeaderToolkitAction,
12
12
  } from './Components';
13
- import {NavigationOptions, Theme} from './types';
13
+ import {NavigationOptions} from './types';
14
14
  import {Colors} from '../Consts';
15
+ import Navigation from './Navigation';
16
+ import {LayoutChangeEvent} from 'react-native';
15
17
 
16
18
  /**
17
19
  * default options for stack screen
@@ -22,7 +24,7 @@ const getStackOptions = (): StackNavigationOptions => {
22
24
  headerTitle: HeaderTitle,
23
25
  headerBackground: (props: any) => <HeaderBackground {...props} />,
24
26
  headerLeft: (props: any) => <HeaderLeft {...props} />,
25
- headerRight: (props: any) => <HeaderToolkitAction {...props} />,
27
+ headerRight: (props: any) => <HeaderRight {...props} />,
26
28
  headerTintColor: Colors.black_01,
27
29
  };
28
30
  };
@@ -72,33 +74,42 @@ const getModalOptions = (): StackNavigationOptions => {
72
74
  /**
73
75
  * build react-navigation options
74
76
  */
75
- const getOptions = (params: NavigationOptions, theme: Theme) => {
77
+ const getOptions = (params: NavigationOptions, navigation?: Navigation) => {
76
78
  let options = {};
77
79
 
80
+ /**
81
+ * left header factory
82
+ */
78
83
  if (
79
84
  typeof params.onPressLeftHeader === 'function' ||
80
85
  params.preventBack !== undefined ||
81
86
  typeof params.hiddenBack === 'boolean'
82
87
  ) {
88
+ let headerLeft: any = (props: any) => (
89
+ <HeaderLeft
90
+ {...props}
91
+ preventBack={params.preventBack}
92
+ onPressLeftHeader={params.onPressLeftHeader}
93
+ />
94
+ );
95
+ if (params.hiddenBack) {
96
+ headerLeft = null;
97
+ }
83
98
  options = {
84
99
  ...options,
85
- headerLeft: params.hiddenBack
86
- ? null
87
- : (props: any) => (
88
- <HeaderLeft
89
- {...props}
90
- preventBack={params.preventBack}
91
- onPressLeftHeader={params.onPressLeftHeader}
92
- />
93
- ),
100
+ headerLeft,
94
101
  };
95
102
  }
96
103
 
104
+ /**
105
+ * surface header style
106
+ */
97
107
  if (typeof params.surface === 'boolean') {
108
+ console.warn('surface options is deprecated, headerType instead in screen');
98
109
  if (params.surface) {
99
110
  options = {
100
111
  ...options,
101
- headerTintColor: theme.colors.text.default,
112
+ headerTintColor: Colors.black_17,
102
113
  headerBackground: () => (
103
114
  <HeaderBackground image={null} useSurface={true} />
104
115
  ),
@@ -112,7 +123,13 @@ const getOptions = (params: NavigationOptions, theme: Theme) => {
112
123
  }
113
124
  }
114
125
 
126
+ /**
127
+ * custom title
128
+ */
115
129
  if (params.customTitle) {
130
+ console.warn(
131
+ 'customTitle options is deprecated, please used headerTitle instead'
132
+ );
116
133
  options = {
117
134
  ...options,
118
135
  headerTitleAlign: 'left',
@@ -122,6 +139,44 @@ const getOptions = (params: NavigationOptions, theme: Theme) => {
122
139
  };
123
140
  }
124
141
 
142
+ /**
143
+ * header right
144
+ */
145
+ if (params.headerRight) {
146
+ let headerRight: any = (props: any) => {
147
+ return (
148
+ <HeaderRight
149
+ {...props}
150
+ {...params.headerRight}
151
+ onLayout={(e: LayoutChangeEvent) => {
152
+ if (navigation) {
153
+ navigation.rightHeaderWidth = e.nativeEvent.layout.width;
154
+ }
155
+ }}
156
+ />
157
+ );
158
+ };
159
+ if (typeof params.headerRight === 'function') {
160
+ headerRight = (props: any) => {
161
+ return (
162
+ <HeaderRight
163
+ {...props}
164
+ onLayout={(e: LayoutChangeEvent) => {
165
+ if (navigation) {
166
+ navigation.rightHeaderWidth = e.nativeEvent.layout.width;
167
+ }
168
+ }}>
169
+ {params.headerRight(props)}
170
+ </HeaderRight>
171
+ );
172
+ };
173
+ }
174
+ options = {
175
+ ...options,
176
+ headerRight,
177
+ };
178
+ }
179
+
125
180
  return {
126
181
  ...params,
127
182
  ...options,