@momo-kits/foundation 0.92.21-beta.10 → 0.92.21-beta.12

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.
@@ -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) {
@@ -1,8 +1,8 @@
1
1
  import React, {
2
- Fragment,
3
2
  useCallback,
4
3
  useContext,
5
4
  useEffect,
5
+ useRef,
6
6
  useState,
7
7
  } from 'react';
8
8
  import {
@@ -29,7 +29,14 @@ import {scaleSize, Text} from '../Text';
29
29
  import {Icon} from '../Icon';
30
30
  import {PopupNotify} from '../Popup';
31
31
  import {Badge, BadgeDot} from '../Badge';
32
+ import {HeaderType} from '../Layout/types';
33
+ import Navigation from './Navigation';
34
+ import {InputSearchProps} from '../Input';
35
+ import AnimatedInputSearch from '../Layout/AnimatedInputSearch';
32
36
 
37
+ /**
38
+ * default navigation button used header nav
39
+ */
33
40
  const NavigationButton: React.FC<NavigationButtonProps> = ({
34
41
  icon,
35
42
  tintColor,
@@ -81,13 +88,18 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
81
88
  );
82
89
  };
83
90
 
91
+ /**
92
+ * default header title used for nav
93
+ */
84
94
  const HeaderTitle: React.FC<any> = props => {
85
95
  const {theme} = useContext(ApplicationContext);
86
- const opacity = props.animatedValue?.interpolate({
87
- inputRange: [0, 200],
88
- outputRange: [0, 1],
89
- extrapolate: 'clamp',
90
- });
96
+ const opacity = props.animatedValue?.interpolate(
97
+ props.interpolate ?? {
98
+ inputRange: [0, 200],
99
+ outputRange: [0, 1],
100
+ extrapolate: 'clamp',
101
+ }
102
+ );
91
103
 
92
104
  return (
93
105
  <Animated.Text
@@ -101,6 +113,9 @@ const HeaderTitle: React.FC<any> = props => {
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,
@@ -113,7 +128,7 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
113
128
  useEffect(() => {
114
129
  const backHandler = BackHandler.addEventListener(
115
130
  'hardwareBackPress',
116
- goBackSafe,
131
+ goBackSafe
117
132
  );
118
133
 
119
134
  return () => backHandler.remove();
@@ -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,37 +286,42 @@ const HeaderCustom: React.FC<TitleCustomProps> = ({
263
286
  return <View style={styles.headerTitleContainer}>{content ?? header}</View>;
264
287
  };
265
288
 
266
- const HeaderRightAction: React.FC<any> = ({children, ...restProps}) => {
267
- const validateType = (child: React.ReactElement) => {
268
- const valid = child?.type === NavigationButton || child?.type === Fragment;
269
- if (__DEV__ && !valid) {
270
- console.warn(
271
- 'HeaderRightAction contains element type of NavigationButton, Please check again.',
272
- );
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 || [];
295
+ if (buttons?.length > 3) {
296
+ buttons = buttons.slice(0, 3);
273
297
  }
274
- return child;
275
- };
276
-
277
- const renderAction = () => {
278
- if (Array.isArray(children)) {
279
- return children.map((child, index) => {
280
- return (
281
- <View key={`HeaderRightAction ${index}`} style={styles.headerButton}>
282
- {React.cloneElement(validateType(child), {...restProps})}
283
- </View>
284
- );
285
- });
286
- }
287
-
288
298
  return (
289
- <View style={styles.headerButton}>
290
- {React.cloneElement(validateType(children), {...restProps})}
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
+ })}
291
311
  </View>
292
312
  );
293
- };
294
- return <View style={styles.headerRightButton}>{renderAction()}</View>;
313
+ }
314
+
315
+ return (
316
+ <View style={styles.headerRightButton} onLayout={onLayout}>
317
+ {children ?? <HeaderToolkitAction {...props} />}
318
+ </View>
319
+ );
295
320
  };
296
321
 
322
+ /**
323
+ * Header toolkit action
324
+ */
297
325
  const HeaderToolkitAction: React.FC<any> = ({
298
326
  tintColor,
299
327
  pinnedTool,
@@ -327,7 +355,7 @@ const HeaderToolkitAction: React.FC<any> = ({
327
355
  (config: any) => {
328
356
  navigator.toolkitConfig = config;
329
357
  setToolConfig(navigator?.toolkitConfig);
330
- },
358
+ }
331
359
  );
332
360
  };
333
361
 
@@ -340,7 +368,7 @@ const HeaderToolkitAction: React.FC<any> = ({
340
368
  const {item} = res;
341
369
  navigator?.toolkitCallback?.(item);
342
370
  getToolkitConfig();
343
- },
371
+ }
344
372
  );
345
373
  };
346
374
 
@@ -357,7 +385,7 @@ const HeaderToolkitAction: React.FC<any> = ({
357
385
  navigator?.maxApi?.dispatchFunction?.(
358
386
  'dismiss',
359
387
  undefined,
360
- undefined,
388
+ undefined
361
389
  );
362
390
  },
363
391
  }}
@@ -388,7 +416,7 @@ const HeaderToolkitAction: React.FC<any> = ({
388
416
  [pinTool?.key],
389
417
  () => {
390
418
  getToolkitConfig();
391
- },
419
+ }
392
420
  );
393
421
  navigator?.toolkitCallback?.(pinTool.key);
394
422
  }}
@@ -397,7 +425,7 @@ const HeaderToolkitAction: React.FC<any> = ({
397
425
  };
398
426
  if (toolConfig) {
399
427
  return (
400
- <View style={styles.headerRightButton}>
428
+ <View style={Styles.row}>
401
429
  {renderPinnedTool()}
402
430
  <View
403
431
  style={[
@@ -405,6 +433,7 @@ const HeaderToolkitAction: React.FC<any> = ({
405
433
  {
406
434
  backgroundColor: backgroundColor ?? '#00000066',
407
435
  borderWidth: backgroundColor ? 0.5 : 0,
436
+ marginLeft: renderPinnedTool() ? Spacing.S : 0,
408
437
  },
409
438
  ]}>
410
439
  <TouchableOpacity
@@ -430,6 +459,9 @@ const HeaderToolkitAction: React.FC<any> = ({
430
459
  return <View />;
431
460
  };
432
461
 
462
+ /**
463
+ * default header banner for header animated
464
+ */
433
465
  const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
434
466
  animatedValue,
435
467
  image,
@@ -470,6 +502,152 @@ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
470
502
  );
471
503
  };
472
504
 
505
+ /**
506
+ * Header extended with background image
507
+ * @constructor
508
+ */
509
+ const HeaderExtendHeader: React.FC<{
510
+ headerType?: HeaderType;
511
+ animatedValue: Animated.Value;
512
+ heightHeader: number;
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
+
524
+ const heightAnimated = useRef(new Animated.Value(0));
525
+
526
+ useEffect(() => {
527
+ const listener = animatedValue.addListener(({value}) => {
528
+ heightAnimated.current.setValue(value);
529
+ });
530
+ return () => {
531
+ animatedValue?.removeListener(listener);
532
+ };
533
+ }, []);
534
+
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 />;
649
+ };
650
+
473
651
  const styles = StyleSheet.create({
474
652
  navigationButton: {
475
653
  height: 28,
@@ -509,7 +687,6 @@ const styles = StyleSheet.create({
509
687
  paddingRight: Spacing.M,
510
688
  },
511
689
  toolkitContainer: {
512
- marginLeft: Spacing.S,
513
690
  padding: Spacing.XS,
514
691
  height: 28,
515
692
  borderRadius: 14,
@@ -541,6 +718,17 @@ const styles = StyleSheet.create({
541
718
  top: -Spacing.XS,
542
719
  right: -Spacing.XS,
543
720
  },
721
+ extendedHeader: {
722
+ aspectRatio: 1.75,
723
+ position: 'absolute',
724
+ width: '100%',
725
+ height: 210,
726
+ },
727
+ headerBox: {
728
+ width: '100%',
729
+ position: 'absolute',
730
+ overflow: 'hidden',
731
+ },
544
732
  });
545
733
 
546
734
  export {
@@ -548,8 +736,9 @@ export {
548
736
  HeaderTitle,
549
737
  HeaderLeft,
550
738
  HeaderBackground,
551
- HeaderRightAction,
552
739
  HeaderToolkitAction,
553
740
  HeaderCustom,
554
741
  HeaderAnimated,
742
+ HeaderRight,
743
+ HeaderExtendHeader,
555
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,17 +1,13 @@
1
- import React from 'react';
2
1
  import {NavigationProp} from '@react-navigation/native';
3
- import {NavigationOptions, Theme} from './types';
4
- import {HeaderRightAction} from './index';
2
+ import {NavigationOptions} from './types';
5
3
  import {getOptions} from './utils';
6
- import {HeaderToolkitAction} from './Components';
7
4
 
8
5
  class Navigation {
9
6
  instance: NavigationProp<any>;
10
- readonly theme: Theme;
7
+ onHeaderRightChange?: (width: number) => void;
11
8
 
12
- constructor(instance: any, theme: Theme) {
9
+ constructor(instance: any) {
13
10
  this.instance = instance;
14
- this.theme = theme;
15
11
  }
16
12
 
17
13
  /**
@@ -19,16 +15,6 @@ class Navigation {
19
15
  * @param params
20
16
  */
21
17
  private filterParams = (params: NavigationOptions) => {
22
- if (params.headerRight) {
23
- const headerRight = params.headerRight?.({}) as React.ReactElement;
24
- const type = headerRight?.type;
25
- if (type !== HeaderRightAction && type !== HeaderToolkitAction) {
26
- console.warn(
27
- 'headerRight contains element type of HeaderRightAction | HeaderToolkitAction, Please check again.',
28
- );
29
- }
30
- }
31
-
32
18
  if (params.headerLeft) {
33
19
  console.warn('headerLeft not allow override render by design system!');
34
20
  delete params.headerLeft;
@@ -42,7 +28,7 @@ class Navigation {
42
28
  * @param params
43
29
  */
44
30
  setOptions = (params: NavigationOptions) => {
45
- params = getOptions(this.filterParams(params), this.theme);
31
+ params = getOptions(this.filterParams(params), this);
46
32
  this.instance.setOptions(params);
47
33
  };
48
34
  }
@@ -78,7 +78,7 @@ class Navigator {
78
78
  StackActions.push('Modal', {
79
79
  ...params,
80
80
  isBottomSheet: true,
81
- }),
81
+ })
82
82
  );
83
83
  }
84
84
  };
@@ -103,7 +103,7 @@ class Navigator {
103
103
  CommonActions.navigate({
104
104
  name,
105
105
  params,
106
- }),
106
+ })
107
107
  );
108
108
  }
109
109
  };
@@ -124,7 +124,7 @@ class Navigator {
124
124
  params,
125
125
  },
126
126
  ],
127
- }),
127
+ })
128
128
  );
129
129
  }
130
130
  };
@@ -142,10 +142,14 @@ class Navigator {
142
142
  * @param callback
143
143
  */
144
144
  setToolkitCallback = (
145
- callback: (item: {key: string; action: string}) => void,
145
+ callback: (item: {key: string; action: string}) => void
146
146
  ) => {
147
147
  this.toolkitCallback = callback;
148
148
  };
149
+
150
+ setHeaderWidth = (width: number) => {
151
+ alert(width);
152
+ };
149
153
  }
150
154
 
151
155
  export default Navigator;
@@ -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
  };
@@ -145,19 +145,29 @@ export type RuntimeToolType = {
145
145
  key: string;
146
146
  };
147
147
 
148
- export interface NavigationOptions extends StackNavigationOptions {
148
+ export interface NavigationOptions
149
+ extends Omit<StackNavigationOptions, 'headerRight'> {
149
150
  preventBack?: PopupNotifyProps;
150
151
  onPressLeftHeader?: () => void;
151
152
  surface?: boolean;
152
153
  hiddenBack?: boolean;
153
154
  customTitle?: TitleCustomProps;
154
- toolkitParams?: {
155
- pinnedTool?: PinnedToolType;
156
- runtimeTools?: RuntimeToolType[];
157
- preventClose?: PopupNotifyProps;
158
- };
155
+ headerRight?: HeaderRightToolkit | HeaderRightActions | any;
159
156
  }
160
157
 
158
+ export type HeaderRightToolkit = {
159
+ type?: 'toolkit';
160
+ pinnedTool?: PinnedToolType;
161
+ runtimeTools?: RuntimeToolType[];
162
+ preventClose?: PopupNotifyProps;
163
+ useMore?: boolean;
164
+ };
165
+
166
+ export type HeaderRightActions = {
167
+ type?: 'icon';
168
+ buttons: NavigationButtonProps[];
169
+ };
170
+
161
171
  export interface HeaderBackProps extends NavigationButtonProps {
162
172
  preventBack?: PopupNotifyProps;
163
173
  onPressLeftHeader?: () => void;