@momo-kits/foundation 0.169.5 → 0.169.6-beta.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.
Files changed (127) hide show
  1. package/Application/BottomSheet.hooks.ts +8 -8
  2. package/Application/BottomSheet.parts.tsx +34 -44
  3. package/Application/BottomSheet.tsx +26 -35
  4. package/Application/BottomTab/ActiveIndicator.tsx +14 -15
  5. package/Application/BottomTab/BottomTabBar.styles.ts +18 -1
  6. package/Application/BottomTab/BottomTabBar.tsx +40 -63
  7. package/Application/BottomTab/BottomTabInset.tsx +4 -8
  8. package/Application/BottomTab/CurvedContainer.tsx +20 -16
  9. package/Application/BottomTab/CustomBottomTabItem.tsx +13 -16
  10. package/Application/BottomTab/DuoBarCompression.tsx +19 -13
  11. package/Application/BottomTab/DuoRailTabBar.tsx +13 -17
  12. package/Application/BottomTab/DuoTabBar.tsx +24 -12
  13. package/Application/BottomTab/TabBarIcon.tsx +8 -13
  14. package/Application/BottomTab/TabScreen.tsx +5 -1
  15. package/Application/BottomTab/index.tsx +30 -38
  16. package/Application/BottomTab/metrics.ts +46 -24
  17. package/Application/BottomTab/tabItems.ts +1 -1
  18. package/Application/BottomTab/usePressLift.ts +2 -2
  19. package/Application/Components/BackgroundImageView.tsx +0 -4
  20. package/Application/Components/DuoGlassBackground.tsx +4 -5
  21. package/Application/Components/HeaderAnimated.tsx +0 -4
  22. package/Application/Components/HeaderBackground.tsx +0 -4
  23. package/Application/Components/HeaderExtendHeader.tsx +10 -11
  24. package/Application/Components/HeaderLeft.tsx +1 -2
  25. package/Application/Components/HeaderRight.tsx +1 -2
  26. package/Application/Components/HeaderTitle.shared.ts +9 -11
  27. package/Application/Components/HeaderTitle.tsx +3 -4
  28. package/Application/Components/HeaderToolkitAction.tsx +20 -17
  29. package/Application/Components/NavigationButton.tsx +1 -2
  30. package/Application/Components/SearchHeader.tsx +10 -17
  31. package/Application/Components/TitleJourney.tsx +2 -2
  32. package/Application/Components/TitleLocation.tsx +2 -2
  33. package/Application/Components/TitleUser.tsx +2 -2
  34. package/Application/Components/useToolkitActions.tsx +32 -17
  35. package/Application/Localize.ts +0 -7
  36. package/Application/ModalScreen.tsx +3 -9
  37. package/Application/Navigation.ts +16 -14
  38. package/Application/NavigationContainer.tsx +16 -19
  39. package/Application/Navigator.ts +48 -20
  40. package/Application/StackScreen.tracking.ts +26 -11
  41. package/Application/StackScreen.tsx +35 -8
  42. package/Application/TooltipPortal.tsx +5 -12
  43. package/Application/WidgetContainer.tsx +4 -10
  44. package/Application/__tests__/modalLayout.test.ts +128 -0
  45. package/Application/modalLayout.ts +14 -13
  46. package/Application/navigationOptions.tsx +17 -21
  47. package/Application/types.tabs.ts +0 -1
  48. package/Badge/Badge.tsx +0 -5
  49. package/Badge/BadgeDot.tsx +0 -1
  50. package/Badge/BadgeDotAnimation.tsx +7 -15
  51. package/Badge/BadgeRibbon.tsx +38 -46
  52. package/Badge/Shape.tsx +0 -2
  53. package/Badge/types.ts +2 -2
  54. package/Button/index.tsx +16 -5
  55. package/Button/utils.ts +15 -9
  56. package/CheckBox/index.tsx +0 -4
  57. package/Consts/theme.ts +10 -10
  58. package/Context/index.ts +0 -1
  59. package/Divider/DashDivider.tsx +0 -1
  60. package/Divider/index.tsx +0 -4
  61. package/FoundationList/index.tsx +0 -4
  62. package/Icon/index.tsx +6 -6
  63. package/Icon/types.ts +1 -1
  64. package/IconButton/index.tsx +12 -4
  65. package/Image/index.tsx +7 -9
  66. package/Input/Input.tsx +9 -8
  67. package/Input/InputDropDown.tsx +12 -10
  68. package/Input/InputMoney.tsx +8 -12
  69. package/Input/InputMoneyView.tsx +7 -14
  70. package/Input/InputOTP.tsx +33 -35
  71. package/Input/InputPhoneNumber.tsx +9 -8
  72. package/Input/InputSearch.tsx +6 -4
  73. package/Input/InputTextArea.tsx +0 -4
  74. package/Input/SearchTextTyping.tsx +0 -4
  75. package/Input/SystemTextInput.tsx +2 -7
  76. package/Input/TextTyping.tsx +0 -4
  77. package/Input/common.tsx +12 -26
  78. package/Input/index.tsx +20 -17
  79. package/Input/moneyUtils.ts +4 -10
  80. package/Input/styles.ts +5 -0
  81. package/Input/utils.ts +12 -14
  82. package/Layout/Card.tsx +4 -7
  83. package/Layout/FloatingButton.tsx +0 -4
  84. package/Layout/Item.tsx +2 -6
  85. package/Layout/ItemList.tsx +7 -9
  86. package/Layout/ItemSectionList.tsx +1 -2
  87. package/Layout/ReservedRegions.tsx +25 -25
  88. package/Layout/Screen.tsx +58 -56
  89. package/Layout/Section.tsx +5 -8
  90. package/Layout/TrackingScope.tsx +0 -1
  91. package/Layout/screenContent.tsx +2 -4
  92. package/Layout/screenFooter.tsx +18 -28
  93. package/Layout/screenHeader.tsx +31 -34
  94. package/Layout/screenQuickReturn.tsx +20 -17
  95. package/Layout/screenScroll.ts +7 -4
  96. package/Layout/screenTypes.ts +2 -3
  97. package/Layout/types.ts +4 -2
  98. package/Layout/utils.ts +37 -31
  99. package/Loader/DotLoader.tsx +0 -4
  100. package/Loader/Loader.tsx +0 -4
  101. package/Loader/ProgressBar.tsx +0 -1
  102. package/Loader/Spinner.tsx +0 -4
  103. package/Loader/utils.ts +7 -11
  104. package/Pagination/Dot.tsx +0 -4
  105. package/Pagination/PaginationDot.tsx +0 -1
  106. package/Pagination/PaginationNumber.tsx +3 -5
  107. package/Pagination/PaginationScroll.tsx +0 -5
  108. package/Pagination/PaginationWhiteDot.tsx +0 -1
  109. package/Pagination/index.tsx +0 -4
  110. package/Pagination/types.ts +2 -2
  111. package/Popup/PopupNotify.tsx +48 -44
  112. package/Popup/PopupPromotion.tsx +7 -4
  113. package/Popup/types.ts +4 -4
  114. package/Popup/utils.ts +0 -4
  115. package/Radio/index.tsx +0 -4
  116. package/Skeleton/index.tsx +0 -8
  117. package/Switch/index.tsx +0 -1
  118. package/Tag/index.tsx +1 -5
  119. package/Tag/types.ts +3 -3
  120. package/Text/index.tsx +3 -14
  121. package/Text/styles.ts +1 -1
  122. package/Text/types.ts +6 -1
  123. package/Text/utils.ts +0 -9
  124. package/Title/index.tsx +32 -41
  125. package/ViewBoundary/index.tsx +2 -4
  126. package/index.ts +6 -0
  127. package/package.json +1 -1
@@ -27,10 +27,6 @@ import { isIPhoneDuo } from './utils';
27
27
  import { useModalLayout } from './modalLayout';
28
28
  import { scheduleOnRN } from 'react-native-worklets';
29
29
 
30
- /**
31
- * Screen of the `Modal` route: a bottom sheet when pushed by
32
- * `showBottomSheet`, otherwise a centered popup.
33
- */
34
30
  const ModalScreen: React.FC<any> = props => {
35
31
  if (props.route?.params?.isBottomSheet) {
36
32
  return <BottomSheet {...props} />;
@@ -38,10 +34,6 @@ const ModalScreen: React.FC<any> = props => {
38
34
  return <Modal {...props} />;
39
35
  };
40
36
 
41
- /**
42
- * Centered popup that fades and scales in over a scrim. On foldables it is
43
- * centered in the modal placement area, at most a popup wide.
44
- */
45
37
  const Modal: React.FC<ModalParams> = props => {
46
38
  const { navigator } = useContext(ApplicationContext);
47
39
  const context = useContext<any>(MiniAppContext);
@@ -58,7 +50,9 @@ const Modal: React.FC<ModalParams> = props => {
58
50
  const isDuo = isIPhoneDuo();
59
51
  const { isFoldable, placement, popup, scrimOpacity } = useModalLayout();
60
52
 
61
- // Auto-tracks the popup as displayed on the current screen.
53
+ /**
54
+ * Fire auto_popup_displayed tracking event on mount
55
+ */
62
56
  useEffect(() => {
63
57
  navigator?.maxApi?.getDataObserver?.('current_screen', (curScreen: any) => {
64
58
  context.autoTracking({
@@ -11,10 +11,6 @@ export const WHITE_LIST = [
11
11
  'vn.momo.ekyc',
12
12
  ];
13
13
 
14
- /**
15
- * Screen-level navigation handle given to features; `setOptions` enforces the
16
- * design system's header rules before applying React Navigation options.
17
- */
18
14
  class Navigation {
19
15
  instance: NavigationProp<any>;
20
16
  context: any;
@@ -25,33 +21,39 @@ class Navigation {
25
21
  }
26
22
 
27
23
  /**
28
- * Drops header overrides the design system forbids: any `headerLeft`, and
29
- * render-function `headerRight`/`headerTitle` outside `WHITE_LIST` apps.
24
+ * navigation options filter by design system rule
25
+ * @param params
30
26
  */
31
27
  private filterParams = (params: NavigationOptions) => {
32
- const isWhiteListed = WHITE_LIST.includes(this.context?.appId);
33
- const rightType = typeof params.headerRight;
34
- const titleType = typeof params.headerTitle;
35
- const isTitleRenderer = titleType !== 'string' && titleType !== 'object';
36
-
37
28
  if (params.headerLeft) {
38
29
  console.warn('headerLeft not allow override render by design system!');
39
30
  delete params.headerLeft;
40
31
  }
41
32
 
42
- if (params.headerRight && rightType !== 'object' && !isWhiteListed) {
33
+ if (
34
+ params.headerRight &&
35
+ typeof params.headerRight !== 'object' &&
36
+ !WHITE_LIST.includes(this.context?.appId)
37
+ ) {
43
38
  console.warn('headerRight not allow type func render by design system!');
44
39
  delete params.headerRight;
45
40
  }
46
41
 
47
- if (params.headerTitle && isTitleRenderer && !isWhiteListed) {
42
+ if (
43
+ params.headerTitle &&
44
+ !['string', 'object'].includes(typeof params.headerTitle) &&
45
+ !WHITE_LIST.includes(this.context?.appId)
46
+ ) {
48
47
  console.warn('headerTitle not allow type func render by design system!');
49
48
  delete params.headerTitle;
50
49
  }
51
50
  return params;
52
51
  };
53
52
 
54
- /** Applies kit `NavigationOptions` to the screen after `filterParams`. */
53
+ /**
54
+ * navigation setOptions support props NavigationOptions base on react-navigation options
55
+ * @param params
56
+ */
55
57
  setOptions = (params: NavigationOptions) => {
56
58
  const options = getOptions(this.filterParams(params));
57
59
  this.instance.setOptions(options as StackNavigationOptions);
@@ -33,10 +33,6 @@ import { ReservedRegionsProvider } from '../Layout/ReservedRegions';
33
33
 
34
34
  const Stack = createStackNavigator();
35
35
 
36
- /**
37
- * Root of a mini app: safe area, reserved regions and the merged mini app
38
- * context around the kit's navigator.
39
- */
40
36
  const NavigationContainer: React.FC<NavigationContainerProps> = ({
41
37
  screen,
42
38
  theme = defaultTheme,
@@ -96,10 +92,6 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
96
92
  );
97
93
  };
98
94
 
99
- /**
100
- * Stack navigator with its `Stack`, `Dialog` and `Modal` routes, under an
101
- * `ApplicationContext` carrying the navigator, themed fonts and `translate`.
102
- */
103
95
  const Navigation: React.FC<any> = ({
104
96
  screen,
105
97
  theme = defaultTheme,
@@ -117,10 +109,9 @@ const Navigation: React.FC<any> = ({
117
109
  const syncCanGoBack = useNativeCanGoBack();
118
110
  const isWhiteList = isWhiteListDesignSystem(context);
119
111
  const fontFamily = isWhiteList ? 'MomoTrustSans' : 'SFProText';
120
- let navigationFontFamily = 'SFProText';
121
- if (isWhiteList) {
122
- navigationFontFamily = 'MomoTrustSans-Regular';
123
- }
112
+ const navigationFontFamily = isWhiteList
113
+ ? 'MomoTrustSans-Regular'
114
+ : 'SFProText';
124
115
  let headerBackground = context?.designSystemConfig?.config?.headerBar;
125
116
  let headerGradient = theme.colors?.gradient;
126
117
 
@@ -131,9 +122,15 @@ const Navigation: React.FC<any> = ({
131
122
  headerGradient = context?.designSystemConfig?.config?.headerGradient;
132
123
  }
133
124
 
125
+ /**
126
+ * inject data for navigator
127
+ */
134
128
  navigator.current.maxApi = maxApi;
135
129
 
136
- /** Text for a localization key, or the current-language side of `{ vi, en }`. */
130
+ /**
131
+ * translate context method
132
+ * @param data
133
+ */
137
134
  const translate = (data?: string | { vi: string; en: string }) => {
138
135
  if (data && typeof data !== 'string') {
139
136
  return localize?.translateData(data);
@@ -231,12 +228,12 @@ const Navigation: React.FC<any> = ({
231
228
  backgroundColor: theme.colors.background.default,
232
229
  },
233
230
  headerLeft: (props: any) => <HeaderLeft {...props} floating />,
234
- headerBackground: (props: any) => {
235
- return <HeaderBackground {...props} />;
236
- },
237
- headerRight: (props: any) => {
238
- return <HeaderRight {...props} floating />;
239
- },
231
+ headerBackground: (props: any) => (
232
+ <HeaderBackground {...props} />
233
+ ),
234
+ headerRight: (props: any) => (
235
+ <HeaderRight {...props} floating />
236
+ ),
240
237
  headerTintColor: Colors.black_17,
241
238
  ...TransitionPresets.ModalTransition,
242
239
  }}
@@ -1,10 +1,6 @@
1
1
  import {CommonActions, StackActions} from '@react-navigation/native';
2
2
  import {BottomSheetParams, ModalParams, ScreenParams} from './types';
3
3
 
4
- /**
5
- * Imperative navigation API exposed to features through `ApplicationContext`.
6
- * Every action is a no-op until the container reports ready.
7
- */
8
4
  class Navigator {
9
5
  ref?: any;
10
6
  isReady?: any;
@@ -18,28 +14,40 @@ class Navigator {
18
14
  this.isWidget = isWidget;
19
15
  }
20
16
 
21
- /** Pushes a new stack screen. */
17
+ /**
18
+ * push new stack screen
19
+ * @param params
20
+ */
22
21
  push = (params: ScreenParams) => {
23
22
  if (this.isReady.current) {
24
23
  this.ref.current?.dispatch?.(StackActions.push('Stack', params));
25
24
  }
26
25
  };
27
26
 
28
- /** Replaces the current screen with a new stack screen. */
27
+ /**
28
+ * replace current screen with new screen
29
+ * @param params
30
+ */
29
31
  replace = (params: ScreenParams) => {
30
32
  if (this.isReady.current) {
31
33
  this.ref.current?.dispatch?.(StackActions.replace('Stack', params));
32
34
  }
33
35
  };
34
36
 
35
- /** Pops `count` screens, 1 by default. */
37
+ /**
38
+ * pop to dismiss a screen
39
+ * @param count
40
+ */
36
41
  pop = (count?: number) => {
37
42
  if (this.isReady.current) {
38
43
  this.ref.current?.dispatch?.(StackActions.pop(count ?? 1));
39
44
  }
40
45
  };
41
46
 
42
- /** Presents a screen as a dialog (slides up from the bottom on iOS). */
47
+ /**
48
+ * present a new screen, show from bottom iOS
49
+ * @param params
50
+ */
43
51
  present = (params: ScreenParams) => {
44
52
  if (this.isReady.current) {
45
53
  this.ref.current?.dispatch?.(StackActions.push('Dialog', params));
@@ -47,8 +55,9 @@ class Navigator {
47
55
  };
48
56
 
49
57
  /**
50
- * Shows a modal popup; inside a widget it always uses the native modal.
51
- * Failures are reported through `onError` instead of throwing.
58
+ * show a modal popup
59
+ * @param params
60
+ * @param onError
52
61
  */
53
62
  showModal = (params: ModalParams, onError?: (error: string) => void) => {
54
63
  try {
@@ -67,7 +76,11 @@ class Navigator {
67
76
  }
68
77
  };
69
78
 
70
- /** Shows a bottom sheet through the modal route; failures go to `onError`. */
79
+ /**
80
+ * show a bottom sheet
81
+ * @param params
82
+ * @param onError
83
+ */
71
84
  showBottomSheet = (
72
85
  params: BottomSheetParams,
73
86
  onError?: (error: string) => void
@@ -89,21 +102,31 @@ class Navigator {
89
102
  }
90
103
  };
91
104
 
92
- /** Pops back to the first screen of the stack. */
105
+ /**
106
+ * pop all screen route
107
+ */
93
108
  popToTop = () => {
94
109
  if (this.isReady.current) {
95
110
  this.ref.current?.dispatch?.(StackActions.popToTop());
96
111
  }
97
112
  };
98
113
 
99
- /** Navigates to a route by name; meant for bottom tab routes only. */
114
+ /**
115
+ * navigate route name bottom tab only
116
+ * @param name
117
+ */
100
118
  navigate = (name: string) => {
101
119
  if (this.isReady.current) {
102
- this.ref.current?.dispatch?.(CommonActions.navigate(name));
120
+ this.ref.current?.dispatch?.(
121
+ CommonActions.navigate(name)
122
+ );
103
123
  }
104
124
  };
105
125
 
106
- /** Resets the flow to a single new stack screen. */
126
+ /**
127
+ * reset a navigation flow with new screen
128
+ * @param params
129
+ */
107
130
  reset = (params: ScreenParams) => {
108
131
  if (this.isReady.current) {
109
132
  this.ref.current?.dispatch?.(
@@ -121,21 +144,26 @@ class Navigator {
121
144
  }
122
145
  };
123
146
 
124
- /** Sets the payload the header passes to `maxApi.dismiss` on close. */
147
+ /**
148
+ * dismiss a feature data
149
+ * @param data
150
+ */
125
151
  setDismissData = (data: any) => {
126
152
  this.dismissData = data;
127
153
  };
128
154
 
129
155
  /**
130
- * No-op placeholder; `NavigationContainer` and `WidgetContainer` replace it
131
- * with their context state setter.
156
+ * set app context using for features of kit
157
+ * @param context
132
158
  */
133
- setCurrentContext = (_context: {
159
+ setCurrentContext = (context: {
134
160
  code?: string;
135
161
  name?: {vi: string; en: string};
136
162
  description?: {vi: string; en: string};
137
163
  icon?: string;
138
- }) => {};
164
+ }) => {
165
+ console.log(context);
166
+ };
139
167
  }
140
168
 
141
169
  export default Navigator;
@@ -8,17 +8,15 @@ type ScreenTrackingDeps = {
8
8
  screenName: string;
9
9
  };
10
10
 
11
- /**
12
- * Auto-tracking reporters of a stack screen. Each event fires at most once per
13
- * screen, guarded by the `release*` flags on `tracking.current`.
14
- */
15
11
  export const createScreenTracking = ({
16
12
  tracking,
17
13
  context,
18
14
  navigator,
19
15
  screenName,
20
16
  }: ScreenTrackingDeps) => {
21
- /** Reports the screen load time. */
17
+ /**
18
+ * tracking for screen load
19
+ */
22
20
  const onScreenLoad = () => {
23
21
  if (!tracking.current?.releaseLoad) {
24
22
  let timeLoad = tracking.current.timeLoad;
@@ -44,22 +42,29 @@ export const createScreenTracking = ({
44
42
  );
45
43
  tracking.current.releaseLoad = true;
46
44
 
45
+ /**
46
+ * debug
47
+ */
47
48
  navigator?.maxApi?.showToastDebug?.({
48
49
  appId: context.appId,
49
50
  message: `${screenName} screen_load_time ${timeLoad}`,
50
51
  type: 'ERROR',
51
52
  });
52
- const lastChildren = tracking.current.lastElement?.children?.current;
53
- if (__DEV__ && lastChildren?.length > 0) {
53
+ if (
54
+ __DEV__ &&
55
+ tracking.current.lastElement?.children?.current?.length > 0
56
+ ) {
54
57
  Alert.alert(
55
58
  `${screenName}- load ${timeLoad}ms`,
56
- JSON.stringify(lastChildren),
59
+ JSON.stringify(tracking.current.lastElement?.children?.current),
57
60
  );
58
61
  }
59
62
  }
60
63
  };
61
64
 
62
- /** Reports the time until the screen became interactive. */
65
+ /**
66
+ * tracking for screen load
67
+ */
63
68
  const onScreenInteraction = () => {
64
69
  if (!tracking.current?.releaseInteraction) {
65
70
  let timeLoad = tracking.current.timeLoad;
@@ -87,6 +92,9 @@ export const createScreenTracking = ({
87
92
  );
88
93
  tracking.current.releaseInteraction = true;
89
94
 
95
+ /**
96
+ * debug toast
97
+ */
90
98
  navigator?.maxApi?.showToastDebug?.({
91
99
  appId: context.appId,
92
100
  message: `${screenName} screen_interaction_time ${tracking.current.timeInteraction}`,
@@ -95,7 +103,9 @@ export const createScreenTracking = ({
95
103
  }
96
104
  };
97
105
 
98
- /** Reports the user's first interaction with the screen. */
106
+ /**
107
+ * tracking for first interaction by user
108
+ */
99
109
  const onFirstInteraction = (action: string) => {
100
110
  if (!tracking.current?.releaseUserInteraction) {
101
111
  const timeLoad = tracking.current.endTime - tracking.current.startTime;
@@ -109,6 +119,9 @@ export const createScreenTracking = ({
109
119
  });
110
120
  tracking.current.releaseUserInteraction = true;
111
121
 
122
+ /**
123
+ * debug
124
+ */
112
125
  navigator?.maxApi?.showToastDebug?.({
113
126
  appId: context.appId,
114
127
  message: `${screenName} user_interaction ${timeLoad}`,
@@ -117,7 +130,9 @@ export const createScreenTracking = ({
117
130
  }
118
131
  };
119
132
 
120
- /** Reports how long the skeleton loading state lasted. */
133
+ /**
134
+ * tracking for loading screen
135
+ */
121
136
  const onScreenLoading = () => {
122
137
  const start = tracking.current.timeStartLoading;
123
138
  const end = tracking.current.timeEndLoading;
@@ -12,8 +12,9 @@ import { TooltipPortalHost, TooltipPortalProvider } from './TooltipPortal';
12
12
  const runAfterInteractions = InteractionManager.runAfterInteractions;
13
13
 
14
14
  /**
15
- * Hosts a feature screen in the stack: applies its header options, provides
16
- * `ScreenContext` and reports navigation, load and interaction tracking.
15
+ * container for stack screen
16
+ * @param props
17
+ * @constructor
17
18
  */
18
19
  const StackScreen: React.FC<any> = props => {
19
20
  const { navigator } = useContext(ApplicationContext);
@@ -63,12 +64,18 @@ const StackScreen: React.FC<any> = props => {
63
64
 
64
65
  const screenName = Component?.name || Component?.type?.name || 'Invalid';
65
66
 
67
+ /**
68
+ * set options for screen
69
+ */
66
70
  useLayoutEffect(() => {
67
71
  if (options) {
68
72
  navigation.setOptions(options);
69
73
  }
70
74
  }, [navigation, options]);
71
75
 
76
+ /**
77
+ * tracking for screen
78
+ */
72
79
  useEffect(() => {
73
80
  let focusScreen: any;
74
81
  let onFocusApp: any;
@@ -142,7 +149,6 @@ const StackScreen: React.FC<any> = props => {
142
149
  // eslint-disable-next-line react-hooks/exhaustive-deps
143
150
  }, []);
144
151
 
145
- /** Reports a navigation to this screen: `back` if it was shown before. */
146
152
  const onScreenNavigated = (pre: string, current: string) => {
147
153
  if (!isBackgroundToForeground) {
148
154
  const item: any = {
@@ -160,6 +166,9 @@ const StackScreen: React.FC<any> = props => {
160
166
 
161
167
  tracking.current.mounted = true;
162
168
 
169
+ /**
170
+ * debug toast
171
+ */
163
172
  navigator?.maxApi?.showToastDebug?.({
164
173
  appId: context.appId,
165
174
  message: `${screenName} screen_navigated`,
@@ -181,7 +190,9 @@ const StackScreen: React.FC<any> = props => {
181
190
  screenName,
182
191
  bottomTab: !!bottomTab,
183
192
  onElementLoad: (item: any) => {
184
- // Widgets record their own load span and don't end the screen load.
193
+ /**
194
+ * widget handle
195
+ */
185
196
  if (item?.componentName === 'Widget') {
186
197
  const index = widgets.current.findIndex(
187
198
  (element: any) => element.componentId === item.componentId,
@@ -205,25 +216,35 @@ const StackScreen: React.FC<any> = props => {
205
216
  return;
206
217
  }
207
218
 
219
+ /**
220
+ * tracking for element screen load
221
+ */
208
222
  clearTimeout(tracking.current.timeoutLoad);
209
223
  tracking.current.endTime = Date.now();
210
224
  tracking.current.timeoutInteraction?.cancel?.();
211
225
  tracking.current.timeoutInteraction = runAfterInteractions(() => {
212
- const elapsed = Date.now() - tracking.current.startTime;
213
- tracking.current.timeInteraction = elapsed;
226
+ tracking.current.timeInteraction =
227
+ Date.now() - tracking.current.startTime;
214
228
  });
215
229
 
230
+ /**
231
+ * support for debug last element
232
+ */
216
233
  if (item?.componentName) {
217
234
  tracking.current.lastElement = item;
218
235
  }
219
236
 
220
- // A user interaction settles load and interaction tracking at once.
237
+ /**
238
+ * for stop tracking when user interaction
239
+ */
221
240
  if (item?.interaction) {
222
241
  onScreenLoad();
223
242
  onScreenInteraction();
224
243
  onFirstInteraction(item?.action);
225
244
  }
226
- // Reports once no element has loaded for 2s.
245
+ /**
246
+ * timeout for handle tracking screen
247
+ */
227
248
  tracking.current.timeoutLoad = setTimeout(() => {
228
249
  const time = tracking.current.endTime - tracking.current.startTime;
229
250
  tracking.current.widgets = widgets.current;
@@ -241,10 +262,16 @@ const StackScreen: React.FC<any> = props => {
241
262
  tracking.current.timeStartLoading = Date.now();
242
263
  }
243
264
 
265
+ /**
266
+ * tracking for loading screen
267
+ */
244
268
  if (!loading) {
245
269
  tracking.current.timeEndLoading = Date.now();
246
270
  }
247
271
 
272
+ /**
273
+ * timeout for handle tracking screen
274
+ */
248
275
  clearTimeout(tracking.current.timeoutLoading);
249
276
  tracking.current.timeoutLoading = setTimeout(() => {
250
277
  onScreenLoading();
@@ -21,14 +21,10 @@ const TooltipPortalContext = createContext<TooltipPortalContextValue | null>(
21
21
  null,
22
22
  );
23
23
 
24
- /**
25
- * Registry of tooltip contents that `TooltipPortalHost` renders in one layer
26
- * above the screen, so tooltips aren't clipped by their anchor's parents.
27
- */
28
24
  export const TooltipPortalProvider: React.FC<{ children: ReactNode }> = ({
29
25
  children,
30
26
  }) => {
31
- // The ref map updates synchronously; the reducer only triggers re-renders.
27
+ // Use ref for synchronous updates + reducer for manual re-renders
32
28
  const portalsRef = useRef<Map<string, ReactNode>>(new Map());
33
29
  const hostRef = useRef<View>(null);
34
30
  const [updateCounter, forceUpdate] = useReducer((x: number) => x + 1, 0);
@@ -36,15 +32,16 @@ export const TooltipPortalProvider: React.FC<{ children: ReactNode }> = ({
36
32
 
37
33
  const register = useCallback((id: string, content: ReactNode) => {
38
34
  portalsRef.current.set(id, content);
39
- forceUpdate();
35
+ forceUpdate(); // Trigger re-render to show new content
40
36
  }, []);
41
37
 
42
38
  const unregister = useCallback((id: string, immediate = false) => {
43
39
  const hasItem = portalsRef.current.has(id);
44
- if (!hasItem) return;
40
+ if (!hasItem) return; // Already removed
45
41
 
46
42
  portalsRef.current.delete(id);
47
43
 
44
+ // Clear any pending updates
48
45
  if (pendingUpdateRef.current) {
49
46
  clearTimeout(pendingUpdateRef.current);
50
47
  pendingUpdateRef.current = null;
@@ -75,7 +72,6 @@ export const TooltipPortalProvider: React.FC<{ children: ReactNode }> = ({
75
72
  );
76
73
  };
77
74
 
78
- /** Full-screen, touch-through layer that renders every registered tooltip. */
79
75
  export const TooltipPortalHost: React.FC = () => {
80
76
  const context = useContext(TooltipPortalContext);
81
77
 
@@ -99,10 +95,6 @@ export const TooltipPortalHost: React.FC = () => {
99
95
  );
100
96
  };
101
97
 
102
- /**
103
- * Tooltip portal API. Outside a provider it returns no-ops (and warns in dev)
104
- * so a tooltip degrades instead of crashing.
105
- */
106
98
  export const useTooltipPortal = () => {
107
99
  const context = useContext(TooltipPortalContext);
108
100
 
@@ -112,6 +104,7 @@ export const useTooltipPortal = () => {
112
104
  'useTooltipPortal must be used within TooltipPortalProvider. Tooltips may not appear correctly.',
113
105
  );
114
106
  }
107
+ // Return no-op functions if context is missing
115
108
  return {
116
109
  register: () => { },
117
110
  unregister: () => { },
@@ -18,7 +18,6 @@ import { StyleSheet, View } from 'react-native';
18
18
 
19
19
  const Stack = createStackNavigator();
20
20
 
21
- /** Widget screen that reports its laid-out height to size the container. */
22
21
  const WidgetContent: React.FC = ({ route }: any) => {
23
22
  const Component = route?.params?.screen;
24
23
  return (
@@ -32,10 +31,6 @@ const WidgetContent: React.FC = ({ route }: any) => {
32
31
  );
33
32
  };
34
33
 
35
- /**
36
- * Root of an embedded widget: its own navigator with the widget screen and the
37
- * `Modal` route, sized to the widget's content height.
38
- */
39
34
  const WidgetContainer: React.FC<WidgetContainerProps> = ({
40
35
  widget,
41
36
  theme = defaultTheme,
@@ -66,9 +61,11 @@ const WidgetContainer: React.FC<WidgetContainerProps> = ({
66
61
  headerGradient = context?.designConfig?.headerGradient;
67
62
  }
68
63
 
64
+ /**
65
+ * inject data for navigator
66
+ */
69
67
  navigator.current.maxApi = maxApi;
70
68
 
71
- /** Text for a localization key, or the current-language side of `{ vi, en }`. */
72
69
  const translate = (data?: string | { vi: string; en: string }) => {
73
70
  if (data && typeof data !== 'string') {
74
71
  return localize?.translateData(data);
@@ -84,10 +81,7 @@ const WidgetContainer: React.FC<WidgetContainerProps> = ({
84
81
  };
85
82
  const isWhiteList = isWhiteListDesignSystem(mergedContext);
86
83
  const fontFamily = isWhiteList ? 'MomoTrustSans' : 'SFProText';
87
- let navigationFontFamily = 'SFProText';
88
- if (isWhiteList) {
89
- navigationFontFamily = 'MomoTrustSans-Regular';
90
- }
84
+ const navigationFontFamily = isWhiteList ? 'MomoTrustSans-Regular' : 'SFProText';
91
85
 
92
86
  return (
93
87
  <View style={[styles.container, { height: height ?? '100%' }]}>