@momo-kits/foundation 0.167.2-beta.1 → 0.168.1-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 (53) hide show
  1. package/Application/BottomSheet.tsx +113 -14
  2. package/Application/BottomTab/BottomTabBar.tsx +6 -3
  3. package/Application/BottomTab/CustomBottomTabItem.tsx +3 -5
  4. package/Application/BottomTab/DuoBarCompression.tsx +135 -0
  5. package/Application/BottomTab/DuoTabBar.tsx +537 -0
  6. package/Application/BottomTab/index.tsx +145 -134
  7. package/Application/Components/DuoGlassBackground.tsx +158 -0
  8. package/Application/Components/DuoQuickReturnHeader.tsx +42 -0
  9. package/Application/Components/HeaderAnimated.tsx +9 -3
  10. package/Application/Components/HeaderExtendHeader.tsx +70 -28
  11. package/Application/Components/HeaderLeft.tsx +38 -0
  12. package/Application/Components/HeaderRight.tsx +144 -9
  13. package/Application/Components/HeaderTitle.tsx +43 -7
  14. package/Application/Components/SearchHeader.tsx +20 -15
  15. package/Application/Components/SideBarButton.tsx +134 -0
  16. package/Application/ModalScreen.tsx +16 -2
  17. package/Application/NavigationContainer.tsx +22 -38
  18. package/Application/Navigator.ts +1 -3
  19. package/Application/StackScreen.tsx +2 -5
  20. package/Application/device.ts +29 -0
  21. package/Application/types.ts +10 -1
  22. package/Application/utils.tsx +10 -7
  23. package/Badge/Badge.tsx +2 -3
  24. package/Badge/BadgeDotAnimation.tsx +3 -3
  25. package/Button/index.tsx +11 -11
  26. package/Consts/theme.ts +3 -4
  27. package/Input/Input.tsx +3 -4
  28. package/Input/InputMoney.tsx +8 -12
  29. package/Input/InputOTP.tsx +9 -12
  30. package/Input/InputPhoneNumber.tsx +3 -4
  31. package/Input/InputSearch.tsx +17 -14
  32. package/Input/InputTextArea.tsx +3 -4
  33. package/Input/SystemTextInput.tsx +2 -3
  34. package/Input/TextTyping.tsx +11 -6
  35. package/Input/utils.ts +2 -3
  36. package/Layout/Card.tsx +8 -26
  37. package/Layout/FloatingButton.tsx +3 -3
  38. package/Layout/Item.tsx +1 -10
  39. package/Layout/ReservedRegions.tsx +78 -0
  40. package/Layout/Screen.tsx +242 -48
  41. package/Layout/Section.tsx +5 -18
  42. package/Layout/index.ts +4 -4
  43. package/Layout/styles.ts +5 -0
  44. package/Layout/utils.ts +477 -5
  45. package/Pagination/Dot.tsx +3 -6
  46. package/Popup/PopupNotify.tsx +33 -22
  47. package/Popup/PopupPromotion.tsx +21 -8
  48. package/Popup/utils.ts +32 -0
  49. package/Radio/index.tsx +2 -3
  50. package/Text/utils.ts +1 -2
  51. package/Title/index.tsx +8 -11
  52. package/package.json +5 -2
  53. package/Layout/GridSystem.tsx +0 -137
@@ -6,10 +6,9 @@ import React, {
6
6
  useState,
7
7
  } from 'react';
8
8
  import {
9
- NativeSyntheticEvent,
10
9
  StyleSheet,
11
10
  TextInput,
12
- TextInputFocusEventData,
11
+ TextInputFocusEvent,
13
12
  TouchableOpacity,
14
13
  View,
15
14
  } from 'react-native';
@@ -93,12 +92,12 @@ const InputPhoneNumber = forwardRef(
93
92
  onChangeText?.(text);
94
93
  };
95
94
 
96
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
95
+ const _onFocus = (e: TextInputFocusEvent) => {
97
96
  setFocused(true);
98
97
  onFocus?.(e);
99
98
  };
100
99
 
101
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
100
+ const _onBlur = (e: TextInputFocusEvent) => {
102
101
  setFocused(false);
103
102
  onBlur?.(e);
104
103
  };
@@ -10,9 +10,8 @@ import React, {
10
10
  useState,
11
11
  } from 'react';
12
12
  import {
13
- NativeSyntheticEvent,
14
13
  TextInput,
15
- TextInputFocusEventData,
14
+ TextInputFocusEvent,
16
15
  TouchableOpacity,
17
16
  View,
18
17
  } from 'react-native';
@@ -59,9 +58,11 @@ const TextTyping: FC<any> = ({
59
58
  listChar.length !== 0 && showChar(listChar?.[currentCharIndex.current]);
60
59
  };
61
60
 
62
- currentIndex.current >= data?.length
63
- ? handleResetList()
64
- : handleAnimation();
61
+ if (currentIndex.current >= data?.length) {
62
+ handleResetList();
63
+ } else {
64
+ handleAnimation();
65
+ }
65
66
  };
66
67
 
67
68
  const showChar = (char?: string) => {
@@ -82,9 +83,12 @@ const TextTyping: FC<any> = ({
82
83
  }, timeDelayNextChar);
83
84
  };
84
85
 
85
- currentCharIndex.current >= (data?.[currentIndex.current]?.length || 0)
86
- ? handleResetString()
87
- : handleShowChar();
86
+ const textLength = data?.[currentIndex.current]?.length || 0;
87
+ if (currentCharIndex.current >= textLength) {
88
+ handleResetString();
89
+ } else {
90
+ handleShowChar();
91
+ }
88
92
  };
89
93
 
90
94
  const reset = () => {
@@ -166,12 +170,12 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
166
170
  props.accessibilityLabel,
167
171
  );
168
172
 
169
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
173
+ const _onFocus = (e: TextInputFocusEvent) => {
170
174
  setFocused(true);
171
175
  onFocus?.(e);
172
176
  };
173
177
 
174
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
178
+ const _onBlur = (e: TextInputFocusEvent) => {
175
179
  setFocused(false);
176
180
  onBlur?.(e);
177
181
  };
@@ -303,6 +307,8 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
303
307
  inputState = 'filled';
304
308
  }
305
309
 
310
+ const inputBackground = backgroundColor ?? theme.colors.background.surface;
311
+
306
312
  return (
307
313
  <ComponentContext.Provider
308
314
  value={{
@@ -322,10 +328,7 @@ const InputSearch: ForwardRefRenderFunction<InputRef, InputSearchProps> = (
322
328
  <View
323
329
  style={[
324
330
  styles.searchInputWrapper,
325
- {
326
- backgroundColor:
327
- backgroundColor ?? theme.colors.background.surface,
328
- },
331
+ { backgroundColor: inputBackground },
329
332
  ]}
330
333
  >
331
334
  <Icon
@@ -7,9 +7,8 @@ import React, {
7
7
  useState,
8
8
  } from 'react';
9
9
  import {
10
- NativeSyntheticEvent,
11
10
  TextInput,
12
- TextInputFocusEventData,
11
+ TextInputFocusEvent,
13
12
  TouchableOpacity,
14
13
  View,
15
14
  } from 'react-native';
@@ -79,12 +78,12 @@ const InputTextArea = forwardRef(
79
78
  setInputValue(text);
80
79
  };
81
80
 
82
- const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
81
+ const _onFocus = (e: TextInputFocusEvent) => {
83
82
  setFocused(true);
84
83
  onFocus?.(e);
85
84
  };
86
85
 
87
- const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
86
+ const _onBlur = (e: TextInputFocusEvent) => {
88
87
  setFocused(false);
89
88
  onBlur?.(e);
90
89
  };
@@ -17,11 +17,10 @@ const SystemTextInput: FC<SystemTextInputProps> = props => {
17
17
  const needsAndroidWrapper =
18
18
  Platform.OS === 'android' && !editable && (onPressIn || onPressOut);
19
19
 
20
+ const noPressHandlers = { onPressIn: undefined, onPressOut: undefined };
20
21
  const inputProps: TextInputProps = {
21
22
  ...props,
22
- ...(needsAndroidWrapper
23
- ? { onPressIn: undefined, onPressOut: undefined }
24
- : null),
23
+ ...(needsAndroidWrapper ? noPressHandlers : null),
25
24
  ...(editable === false ? { accessibilityState: undefined } : null),
26
25
  };
27
26
 
@@ -36,9 +36,11 @@ const TextTyping: FC<TextTypingProps> = ({
36
36
  listChar.length !== 0 && showChar(listChar?.[currentCharIndex.current]);
37
37
  };
38
38
 
39
- currentIndex.current >= data?.length
40
- ? handleResetList()
41
- : handleAnimation();
39
+ if (currentIndex.current >= data?.length) {
40
+ handleResetList();
41
+ } else {
42
+ handleAnimation();
43
+ }
42
44
  };
43
45
 
44
46
  const showChar = (char?: string) => {
@@ -59,9 +61,12 @@ const TextTyping: FC<TextTypingProps> = ({
59
61
  }, timeDelayNextChar);
60
62
  };
61
63
 
62
- currentCharIndex.current >= (data?.[currentIndex.current]?.length || 0)
63
- ? handleResetString()
64
- : handleShowChar();
64
+ const textLength = data?.[currentIndex.current]?.length || 0;
65
+ if (currentCharIndex.current >= textLength) {
66
+ handleResetString();
67
+ } else {
68
+ handleShowChar();
69
+ }
65
70
  };
66
71
 
67
72
  const reset = () => {
package/Input/utils.ts CHANGED
@@ -15,9 +15,8 @@ const formatNumberToMoney = (number: number, currency = '') => {
15
15
 
16
16
  const numberString = Math.abs(number).toString();
17
17
  if (numberString.length < 3) {
18
- return isNegative
19
- ? `-${numberString}${currency}`
20
- : `${numberString}${currency}`;
18
+ const sign = isNegative ? '-' : '';
19
+ return `${sign}${numberString}${currency}`;
21
20
  }
22
21
 
23
22
  let count = 0;
package/Layout/Card.tsx CHANGED
@@ -1,12 +1,12 @@
1
1
  import React, { useContext } from 'react';
2
- import { Dimensions, View, ViewProps } from 'react-native';
3
- import { useGridSystem } from './utils';
2
+ import { View, ViewProps } from 'react-native';
3
+ import { useGridSystem, useScreenLayout } from './utils';
4
4
  import { GridContextProps } from './types';
5
- import { GridContext, GridSystem } from './index';
5
+ import { GridContext } from './index';
6
6
  import { Image } from '../Image';
7
- import { ApplicationContext, MiniAppContext } from '../Context';
7
+ import { ApplicationContext } from '../Context';
8
8
  import styles from './styles';
9
- import { Colors, Radius, Shadow, Spacing } from '../Consts';
9
+ import { Radius, Shadow, Spacing } from '../Consts';
10
10
 
11
11
  export interface CardProps extends ViewProps {
12
12
  /**
@@ -27,12 +27,13 @@ const Card: React.FC<CardProps> = ({
27
27
  useShadow = false,
28
28
  ...props
29
29
  }) => {
30
- const { showGrid, theme } = useContext(ApplicationContext);
30
+ const { theme } = useContext(ApplicationContext);
31
31
  const { numberOfColumns } = useGridSystem();
32
+ const { contentWidth } = useScreenLayout();
32
33
 
33
34
  const gutterSize = 8;
34
35
  const margin = 12;
35
- const widthSection = Dimensions.get('window').width - margin * 2;
36
+ const widthSection = contentWidth - margin * 2;
36
37
  let sizePerSpan =
37
38
  (widthSection - margin * 2 - gutterSize * (numberOfColumns - 1)) /
38
39
  numberOfColumns -
@@ -47,23 +48,6 @@ const Card: React.FC<CardProps> = ({
47
48
  },
48
49
  };
49
50
 
50
- /**
51
- * render overlay only dev mode
52
- */
53
- const renderOverlay = () => {
54
- return (
55
- <>
56
- <GridSystem
57
- width={widthSection}
58
- gutterSize={Spacing.S}
59
- screenGrid={false}
60
- gridColor={Colors.red_01}
61
- />
62
- <View pointerEvents={'none'} style={styles.boxGridCard} />
63
- </>
64
- );
65
- };
66
-
67
51
  return (
68
52
  <GridContext.Provider value={gridContext}>
69
53
  <View
@@ -95,9 +79,7 @@ const Card: React.FC<CardProps> = ({
95
79
  ]}
96
80
  />
97
81
  )}
98
- {/* Render only allowed children as valid React elements */}
99
82
  {children}
100
- {showGrid && renderOverlay()}
101
83
  </View>
102
84
  </GridContext.Provider>
103
85
  );
@@ -10,8 +10,8 @@ import Animated, {
10
10
  useAnimatedStyle,
11
11
  useSharedValue,
12
12
  withTiming,
13
- runOnJS,
14
13
  } from 'react-native-reanimated';
14
+ import { scheduleOnRN } from 'react-native-worklets';
15
15
  import { ApplicationContext } from '../Context';
16
16
  import { Icon } from '../Icon';
17
17
  import { useScaleSize, exportFontFamily } from '../Text';
@@ -78,7 +78,7 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
78
78
  minWidth,
79
79
  { duration: 100 },
80
80
  finished => {
81
- if (finished) runOnJS(setShowText)(false);
81
+ if (finished) scheduleOnRN(setShowText, false);
82
82
  },
83
83
  );
84
84
  } else {
@@ -87,7 +87,7 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
87
87
  maxWidth.value,
88
88
  { duration: 100 },
89
89
  finished => {
90
- if (finished) runOnJS(setShowText)(true);
90
+ if (finished) scheduleOnRN(setShowText, true);
91
91
  },
92
92
  );
93
93
  }
package/Layout/Item.tsx CHANGED
@@ -1,7 +1,7 @@
1
1
  import React, { useContext } from 'react';
2
2
  import { View, ViewProps } from 'react-native';
3
3
  import { GridContext } from './index';
4
- import { ApplicationContext, MiniAppContext } from '../Context';
4
+ import { MiniAppContext } from '../Context';
5
5
  import styles from './styles';
6
6
  import { SpanNumber } from './types';
7
7
 
@@ -23,19 +23,11 @@ const Item: React.FC<ItemProps> = ({
23
23
  children,
24
24
  style,
25
25
  }) => {
26
- const { showGrid } = useContext(ApplicationContext);
27
26
  const context = useContext<any>(MiniAppContext);
28
27
  const grid = useContext(GridContext);
29
28
 
30
29
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
31
30
 
32
- /**
33
- * render overlay view only dev mode
34
- */
35
- const renderOverlay = () => {
36
- return <View pointerEvents={'none'} style={styles.boxGrid} />;
37
- };
38
-
39
31
  return (
40
32
  <View
41
33
  style={[
@@ -49,7 +41,6 @@ const Item: React.FC<ItemProps> = ({
49
41
  ]}
50
42
  >
51
43
  {children}
52
- {showGrid && renderOverlay()}
53
44
  </View>
54
45
  );
55
46
  };
@@ -0,0 +1,78 @@
1
+ import React, { createContext, ReactNode, useMemo } from 'react';
2
+ import { Platform, StyleSheet } from 'react-native';
3
+
4
+ export type ReservedRegion = {
5
+ x: number;
6
+ y: number;
7
+ width: number;
8
+ height: number;
9
+ };
10
+
11
+ type NativeReservedRegions = typeof import('react-native-reserved-regions');
12
+
13
+ // native lib is optional so apps without react-native-reserved-regions still bundle
14
+ let nativeLib: NativeReservedRegions | undefined;
15
+ try {
16
+ nativeLib = require('react-native-reserved-regions');
17
+ } catch {
18
+ nativeLib = undefined;
19
+ }
20
+
21
+ /**
22
+ * screen regions the system currently occludes, relative to the provider, which fills
23
+ * the root view so they line up with window coordinates
24
+ */
25
+ const ReservedRegionsContext = createContext<ReservedRegion[]>([]);
26
+
27
+ /**
28
+ * keeps the occlusion regions reported by the native provider, the only kind layout reads
29
+ */
30
+ const OcclusionRegions: React.FC<{
31
+ useRegions: NativeReservedRegions['useReservedRegions'];
32
+ children: ReactNode;
33
+ }> = ({ useRegions, children }) => {
34
+ const regions = useRegions();
35
+ const occlusion = useMemo(
36
+ () =>
37
+ regions
38
+ .filter(region => region.kind === 'occlusion')
39
+ .map(region => region.frame),
40
+ [regions],
41
+ );
42
+
43
+ return (
44
+ <ReservedRegionsContext.Provider value={occlusion}>
45
+ {children}
46
+ </ReservedRegionsContext.Provider>
47
+ );
48
+ };
49
+
50
+ /**
51
+ * tracks the system occlusion regions (iOS 27.1+), e.g. the iPhone Duo status bar
52
+ * cluster growing for a call or a Live Activity
53
+ */
54
+ const ReservedRegionsProvider: React.FC<{ children: ReactNode }> = ({
55
+ children,
56
+ }) => {
57
+ if (Platform.OS !== 'ios' || !nativeLib) {
58
+ return <>{children}</>;
59
+ }
60
+
61
+ const { ReservedRegionsProvider: NativeProvider, useReservedRegions } =
62
+ nativeLib;
63
+ return (
64
+ <NativeProvider style={styles.container}>
65
+ <OcclusionRegions useRegions={useReservedRegions}>
66
+ {children}
67
+ </OcclusionRegions>
68
+ </NativeProvider>
69
+ );
70
+ };
71
+
72
+ const styles = StyleSheet.create({
73
+ container: {
74
+ flex: 1,
75
+ },
76
+ });
77
+
78
+ export { ReservedRegionsContext, ReservedRegionsProvider };