@momo-kits/foundation 0.92.21-beta.3 → 0.92.21-beta.5

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
- );
273
- }
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
- });
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);
286
297
  }
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,150 @@ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
470
502
  );
471
503
  };
472
504
 
505
+ /**
506
+ * Header extended with background image
507
+ * @constructor
508
+ */
509
+ const HeaderExtendBackground: 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
+ hasColorBG
555
+ animatedValue={animatedValue}
556
+ navigation={navigation}
557
+ {...inputSearchProps}
558
+ />
559
+ </Animated.View>
560
+ </View>
561
+ );
562
+ }
563
+
564
+ if (headerType === 'extended') {
565
+ return (
566
+ <>
567
+ <Image
568
+ source={{
569
+ uri: theme.assets?.headerBackground,
570
+ }}
571
+ style={styles.extendedHeader}
572
+ />
573
+ <Animated.View
574
+ style={{
575
+ justifyContent: 'flex-end',
576
+ height: heightAnimated.current.interpolate({
577
+ inputRange: [0, 100],
578
+ outputRange: [heightHeader + 52, heightHeader],
579
+ extrapolate: 'clamp',
580
+ }),
581
+ }}>
582
+ <AnimatedInputSearch
583
+ animatedValue={animatedValue}
584
+ navigation={navigation}
585
+ {...inputSearchProps}
586
+ />
587
+ </Animated.View>
588
+ </>
589
+ );
590
+ }
591
+
592
+ return (
593
+ <>
594
+ <View
595
+ style={{
596
+ height: heightHeader + 52,
597
+ width: '100%',
598
+ position: 'absolute',
599
+ overflow: 'hidden',
600
+ }}>
601
+ <Image
602
+ source={{
603
+ uri: theme.assets?.headerBackground,
604
+ }}
605
+ style={{
606
+ width: '100%',
607
+ height: undefined,
608
+ position: 'absolute',
609
+ aspectRatio: 1.75,
610
+ }}
611
+ />
612
+ </View>
613
+
614
+ <Animated.View
615
+ style={{
616
+ justifyContent: 'flex-end',
617
+ height: heightAnimated.current.interpolate({
618
+ inputRange: [0, 100],
619
+ outputRange: [heightHeader + 52, heightHeader],
620
+ extrapolate: 'clamp',
621
+ }),
622
+ }}>
623
+ <AnimatedInputSearch
624
+ animatedValue={animatedValue}
625
+ navigation={navigation}
626
+ {...inputSearchProps}
627
+ />
628
+ </Animated.View>
629
+ </>
630
+ );
631
+ }
632
+
633
+ if (headerType === 'extended') {
634
+ return (
635
+ <View style={{minHeight: heightHeader}}>
636
+ <Image
637
+ source={{
638
+ uri: theme.assets?.headerBackground,
639
+ }}
640
+ style={styles.extendedHeader}
641
+ />
642
+ </View>
643
+ );
644
+ }
645
+
646
+ return <View />;
647
+ };
648
+
473
649
  const styles = StyleSheet.create({
474
650
  navigationButton: {
475
651
  height: 28,
@@ -509,7 +685,6 @@ const styles = StyleSheet.create({
509
685
  paddingRight: Spacing.M,
510
686
  },
511
687
  toolkitContainer: {
512
- marginLeft: Spacing.S,
513
688
  padding: Spacing.XS,
514
689
  height: 28,
515
690
  borderRadius: 14,
@@ -541,6 +716,12 @@ const styles = StyleSheet.create({
541
716
  top: -Spacing.XS,
542
717
  right: -Spacing.XS,
543
718
  },
719
+ extendedHeader: {
720
+ aspectRatio: 1.75,
721
+ position: 'absolute',
722
+ width: '100%',
723
+ height: 210,
724
+ },
544
725
  });
545
726
 
546
727
  export {
@@ -548,8 +729,9 @@ export {
548
729
  HeaderTitle,
549
730
  HeaderLeft,
550
731
  HeaderBackground,
551
- HeaderRightAction,
552
732
  HeaderToolkitAction,
553
733
  HeaderCustom,
554
734
  HeaderAnimated,
735
+ HeaderRight,
736
+ HeaderExtendBackground,
555
737
  };
@@ -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
+ rightHeaderWidth?: number;
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
  }
@@ -1,13 +1,94 @@
1
1
  import React, {useContext, useEffect, useLayoutEffect, useRef} from 'react';
2
+ import {useHeaderHeight} from '@react-navigation/stack';
3
+ import {InteractionManager, Linking, View} from 'react-native';
2
4
  import {ScreenParams} from './types';
3
5
  import Navigation from './Navigation';
4
6
  import {ApplicationContext, MiniAppContext, ScreenContext} from './index';
5
7
  import {GridSystem} from '../Layout';
6
- import {useHeaderHeight} from '@react-navigation/stack';
7
- import {Alert, InteractionManager} from 'react-native';
8
+ import {Text} from '../Text';
9
+ import {Colors, Radius, Spacing} from '../Consts';
10
+
11
+ /**
12
+ * Empty screen for unvalidated screen name
13
+ * @constructor
14
+ */
15
+ const EmptyScreen: React.FC = () => {
16
+ return (
17
+ <View
18
+ style={{
19
+ backgroundColor: Colors.black_01,
20
+ padding: Spacing.M,
21
+ borderRadius: Radius.M,
22
+ }}>
23
+ <Text typography={'header_m_bold'}>Unvalidated screen name</Text>
24
+ <Text
25
+ typography={'description_default_regular'}
26
+ style={{paddingVertical: Spacing.S}}>
27
+ Your screen has not been rendered because Platform has not detected the
28
+ screen name. Please migrate to support this feature.
29
+ </Text>
30
+ <Text typography={'header_default'} style={{width: '100%'}}>
31
+ Possible fixes:
32
+ </Text>
33
+ <View style={{marginLeft: Spacing.S}}>
34
+ <Text typography={'body_default_regular'}>1. Off webpack config</Text>
35
+ <Text typography={'body_default_regular'}>
36
+ 2. Screen name refactoring
37
+ </Text>
38
+ <View style={{marginLeft: Spacing.S}}>
39
+ <Text
40
+ typography={'description_default_regular'}
41
+ style={{marginBottom: Spacing.S}}>
42
+ - Với các screen push/import dạng arrow function ={'>'} đổi thành
43
+ named function
44
+ </Text>
45
+ <Text
46
+ typography={'description_default_regular'}
47
+ style={{marginBottom: Spacing.S}}>
48
+ - Với các screen import dạng High Order Component ={'>'} rename
49
+ generic function thành named function
50
+ </Text>
51
+ <Text
52
+ typography={'description_default_regular'}
53
+ style={{marginBottom: Spacing.S}}>
54
+ - Với Tab Navigator, Platform sẽ lấy Tab Container làm screen name.
55
+ Trong trường hợp miniapp muốn track riêng action cho từng tab, tham
56
+ khảo theo docs
57
+ </Text>
58
+ </View>
59
+ </View>
60
+ <Text typography={'header_s_semibold'}>More information: </Text>
61
+ <Text
62
+ typography={'body_default_regular'}
63
+ color={Colors.blue_01}
64
+ onPress={() => {
65
+ Linking.openURL(
66
+ 'https://gitlab.mservice.com.vn/momo-platform/mini-app/-/tree/vn.momo.uikits/dev/app'
67
+ );
68
+ }}>
69
+ Mini App: vn.momo.uikits/dev
70
+ </Text>
71
+ <Text
72
+ typography={'body_default_regular'}
73
+ color={Colors.blue_01}
74
+ onPress={() => {
75
+ Linking.openURL(
76
+ 'https://docs.google.com/presentation/d/1bIFoh8gRXb6hsnGJz4O1Kxg4_oNikB1KuuEt2wULCDM/edit?usp=sharing'
77
+ );
78
+ }}>
79
+ https://docs.google.com/presentation/d/1bIFoh8gRXb6hsnGJz4O1Kxg4_oNikB1KuuEt2wULCDM/edit?usp=sharing
80
+ </Text>
81
+ </View>
82
+ );
83
+ };
8
84
 
85
+ /**
86
+ * container for stack screen
87
+ * @param props
88
+ * @constructor
89
+ */
9
90
  const StackScreen: React.FC<ScreenParams> = props => {
10
- const {theme, showGrid, navigator} = useContext(ApplicationContext);
91
+ const {showGrid, navigator} = useContext(ApplicationContext);
11
92
  const startTime = useRef(Date.now());
12
93
  const endTime = useRef(Date.now());
13
94
  const timeLoad = useRef(0);
@@ -23,7 +104,7 @@ const StackScreen: React.FC<ScreenParams> = props => {
23
104
  const context = useContext<any>(MiniAppContext);
24
105
 
25
106
  const {screen: Component, options, initialParams} = props.route.params;
26
- const navigation = new Navigation(props.navigation, theme);
107
+ const navigation = new Navigation(props.navigation);
27
108
  const heightHeader = useHeaderHeight();
28
109
 
29
110
  const data = {
@@ -50,8 +131,10 @@ const StackScreen: React.FC<ScreenParams> = props => {
50
131
  * tracking for screen
51
132
  */
52
133
  useEffect(() => {
53
- if (__DEV__ && (!screenName || screenName == 'Invalid')) {
54
- Alert.alert('Design system', `Unvalidated screen name "${screenName}" !`);
134
+ if (['Invalid', 'screen'].includes(screenName) && context.enableAutoId) {
135
+ setTimeout(() => {
136
+ navigator?.showModal({screen: EmptyScreen});
137
+ }, 300);
55
138
  }
56
139
 
57
140
  navigator?.maxApi?.of?.({screenName});
@@ -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';
@@ -144,19 +145,29 @@ export type RuntimeToolType = {
144
145
  key: string;
145
146
  };
146
147
 
147
- export interface NavigationOptions extends StackNavigationOptions {
148
+ export interface NavigationOptions
149
+ extends Omit<StackNavigationOptions, 'headerRight'> {
148
150
  preventBack?: PopupNotifyProps;
149
151
  onPressLeftHeader?: () => void;
150
152
  surface?: boolean;
151
153
  hiddenBack?: boolean;
152
154
  customTitle?: TitleCustomProps;
153
- toolkitParams?: {
154
- pinnedTool?: PinnedToolType;
155
- runtimeTools?: RuntimeToolType[];
156
- preventClose?: PopupNotifyProps;
157
- };
155
+ headerRight?: HeaderRightToolkit | HeaderRightActions | any;
158
156
  }
159
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
+
160
171
  export interface HeaderBackProps extends NavigationButtonProps {
161
172
  preventBack?: PopupNotifyProps;
162
173
  onPressLeftHeader?: () => void;
@@ -198,6 +209,11 @@ export type BottomTabItemProps = {
198
209
  screen: Screen;
199
210
  options: NavigationOptions;
200
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
+ };
201
217
  };
202
218
 
203
219
  export type BottomTabProps = {
@@ -206,8 +222,9 @@ export type BottomTabProps = {
206
222
  initialRouteName?: string;
207
223
  navigation: Navigation;
208
224
  listeners?: {
209
- tabPress?: any;
210
- 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;
211
228
  };
212
229
  floatingButton?: FloatingButtonProps;
213
230
  };