@momo-kits/foundation 0.167.1-duo.8 → 0.167.1-sizescale.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 (41) hide show
  1. package/Application/BottomSheet.tsx +14 -115
  2. package/Application/BottomTab/BottomTabBar.tsx +3 -6
  3. package/Application/BottomTab/index.tsx +134 -155
  4. package/Application/Components/HeaderExtendHeader.tsx +27 -40
  5. package/Application/Components/HeaderLeft.tsx +0 -23
  6. package/Application/Components/HeaderRight.tsx +9 -104
  7. package/Application/Components/HeaderTitle.tsx +7 -29
  8. package/Application/Components/SearchHeader.tsx +14 -15
  9. package/Application/ModalScreen.tsx +2 -16
  10. package/Application/NavigationContainer.tsx +70 -22
  11. package/Application/Navigator.ts +3 -1
  12. package/Application/StackScreen.tsx +5 -2
  13. package/Application/types.ts +2 -10
  14. package/Application/utils.tsx +3 -6
  15. package/Consts/theme.ts +4 -3
  16. package/Input/Input.tsx +4 -3
  17. package/Input/InputMoney.tsx +8 -5
  18. package/Input/InputPhoneNumber.tsx +4 -3
  19. package/Input/InputSearch.tsx +4 -3
  20. package/Input/InputTextArea.tsx +4 -3
  21. package/Layout/Card.tsx +26 -8
  22. package/Layout/FloatingButton.tsx +3 -3
  23. package/Layout/GridSystem.tsx +137 -0
  24. package/Layout/Item.tsx +10 -1
  25. package/Layout/Screen.tsx +43 -225
  26. package/Layout/Section.tsx +18 -5
  27. package/Layout/index.ts +4 -4
  28. package/Layout/utils.ts +5 -348
  29. package/Popup/PopupNotify.tsx +22 -33
  30. package/Popup/PopupPromotion.tsx +8 -20
  31. package/Text/utils.ts +2 -1
  32. package/package.json +2 -3
  33. package/Application/BottomTab/DuoBarCompression.tsx +0 -135
  34. package/Application/BottomTab/DuoTabBar.tsx +0 -462
  35. package/Application/Components/DuoGlassBackground.tsx +0 -179
  36. package/Application/Components/DuoQuickReturnHeader.tsx +0 -42
  37. package/Application/Components/OptionalGlassView.ts +0 -11
  38. package/Application/Components/SideBarButton.tsx +0 -128
  39. package/Application/device.ts +0 -29
  40. package/Layout/ReservedRegions.tsx +0 -47
  41. package/Popup/utils.ts +0 -32
@@ -1,24 +1,14 @@
1
- import React, {
2
- useCallback,
3
- useContext,
4
- useEffect,
5
- useRef,
6
- useState,
7
- } from 'react';
1
+ import React, { useCallback, useContext, useEffect, useRef } from 'react';
8
2
  import {
9
3
  BackHandler,
10
4
  Dimensions,
11
- Keyboard,
12
5
  KeyboardAvoidingView,
13
- KeyboardEvent,
14
- LayoutAnimation,
15
6
  Modal,
16
7
  PanResponder,
17
8
  Platform,
18
9
  Pressable,
19
10
  StyleSheet,
20
11
  TouchableOpacity,
21
- useWindowDimensions,
22
12
  View,
23
13
  } from 'react-native';
24
14
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -32,38 +22,22 @@ import Animated, {
32
22
  Easing,
33
23
  Extrapolation,
34
24
  interpolate,
25
+ runOnJS,
35
26
  useAnimatedStyle,
36
27
  useSharedValue,
37
28
  withSpring,
38
29
  withTiming,
39
30
  } from 'react-native-reanimated';
40
- import { scheduleOnRN } from 'react-native-worklets';
41
31
  import layoutStyles from '../Layout/styles';
42
- import { getDuoTopInset, isIPhoneDuo } from './device';
43
-
44
- // iPhone Duo inner display: center the sheet instead of spanning the width
45
- const DUO_REGULAR_MIN_WIDTH = 600;
46
32
 
47
33
  const BottomSheet: React.FC<BottomSheetParams> = props => {
48
34
  const { theme, navigator } = useContext(ApplicationContext);
49
35
  const context = useContext<any>(MiniAppContext);
50
- const windowSize = useWindowDimensions();
51
- const isDuo = isIPhoneDuo();
52
- const isDuoCentered = isDuo && windowSize.width >= DUO_REGULAR_MIN_WIDTH;
53
- const isPortrait = windowSize.height > windowSize.width;
54
- // 'screen' is the outer display on iPhone Duo, even when unfolded
55
- const heightDevice = isDuo
56
- ? windowSize.height
57
- : Dimensions.get('screen').height;
36
+ const heightDevice = Dimensions.get('screen').height;
58
37
  const action = useRef<undefined | string>(undefined);
59
38
  const insets = useSafeAreaInsets();
60
39
  const heightHeader = useHeaderHeight();
61
- const topGap = Math.max(
62
- getDuoTopInset(insets, windowSize.width, windowSize.height),
63
- Spacing.XL,
64
- );
65
- const bottomInset =
66
- Platform.OS === 'ios' ? Math.min(insets.bottom, 21) : insets.bottom;
40
+ const bottomInset = Platform.OS === 'ios' ? Math.min(insets.bottom, 21) : insets.bottom;
67
41
  const keyboardOffset = heightHeader - bottomInset;
68
42
 
69
43
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
@@ -84,43 +58,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
84
58
  }: BottomSheetParams = props.route.params;
85
59
 
86
60
  const translateY = useSharedValue(heightDevice);
87
- const [keyboardLift, setKeyboardLift] = useState(0);
88
-
89
- // KeyboardAvoidingView lifts the whole sheet past the top on iPhone Duo,
90
- // so lift it here and shrink maxHeight by the same amount instead
91
- useEffect(() => {
92
- if (!isDuo || !useKeyboardAvoidingView) {
93
- return;
94
- }
95
- const onKeyboardChange = (e: KeyboardEvent) => {
96
- const overlap = windowSize.height - e.endCoordinates.screenY;
97
- const lift = Math.max(
98
- overlap - bottomInset - (keyboardVerticalOffset ?? 0),
99
- 0,
100
- );
101
- if (e.duration) {
102
- LayoutAnimation.configureNext({
103
- duration: e.duration,
104
- update: {
105
- duration: e.duration,
106
- type: LayoutAnimation.Types[e.easing] || 'keyboard',
107
- },
108
- });
109
- }
110
- setKeyboardLift(lift);
111
- };
112
- const subscriptions = [
113
- Keyboard.addListener('keyboardWillChangeFrame', onKeyboardChange),
114
- Keyboard.addListener('keyboardWillHide', onKeyboardChange),
115
- ];
116
- return () => subscriptions.forEach(sub => sub.remove());
117
- }, [
118
- bottomInset,
119
- isDuo,
120
- keyboardVerticalOffset,
121
- useKeyboardAvoidingView,
122
- windowSize.height,
123
- ]);
124
61
 
125
62
  const openAnimation = useCallback(() => {
126
63
  translateY.value = withTiming(0, {
@@ -137,7 +74,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
137
74
  duration: 200,
138
75
  easing: Easing.bezier(0.3, 0.0, 0.8, 0.15),
139
76
  },
140
- () => scheduleOnRN(callback),
77
+ () => runOnJS(callback)(),
141
78
  );
142
79
  },
143
80
  [heightDevice, translateY],
@@ -200,9 +137,9 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
200
137
  }
201
138
  closeAnimation(() => {
202
139
  navigator?.pop();
203
- scheduleOnRN(() => {
140
+ runOnJS(() => {
204
141
  callback?.();
205
- });
142
+ })();
206
143
  });
207
144
  },
208
145
  [barrierDismissible, closeAnimation, navigator],
@@ -311,23 +248,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
311
248
  };
312
249
  });
313
250
 
314
- // landscape side buttons span the full height, so inset the content; in
315
- // portrait the side status bar only covers the top corner
316
- const sideInsetStyle = isDuo &&
317
- !isDuoCentered &&
318
- !isPortrait && {
319
- paddingLeft: insets.left,
320
- paddingRight: insets.right,
321
- };
322
-
323
- const screen = (
324
- <Screen
325
- {...props}
326
- {...props.route.params}
327
- onRequestClose={onRequestClose}
328
- />
329
- );
330
-
331
251
  return (
332
252
  <Container
333
253
  transparent
@@ -344,7 +264,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
344
264
  android: undefined,
345
265
  })}
346
266
  keyboardVerticalOffset={keyboardVerticalOffset ?? keyboardOffset}
347
- enabled={useKeyboardAvoidingView && !isDuo}
267
+ enabled={useKeyboardAvoidingView}
348
268
  contentContainerStyle={styles.container}
349
269
  style={styles.container}
350
270
  >
@@ -367,34 +287,15 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
367
287
  borderTopRightRadius: Radius.M,
368
288
  overflow: 'hidden',
369
289
  },
370
- isDuo && {
371
- maxHeight: windowSize.height - topGap - keyboardLift,
372
- marginBottom: keyboardLift,
373
- },
374
- isDuoCentered && {
375
- // keeps the portrait width in landscape, like system sheets
376
- width: Math.min(
377
- Math.min(windowSize.width, windowSize.height) - Spacing.L * 2,
378
- windowSize.width -
379
- Math.max(insets.left, insets.right, Spacing.L) * 2,
380
- ),
381
- alignSelf: 'center',
382
- },
383
290
  showBaseLineDebug && styles.debugBaseLine,
384
291
  ]}
385
292
  >
386
- {isDuo ? (
387
- // content shrinks so a tall sheet keeps its header and footer
388
- <View style={[styles.shrink, sideInsetStyle]}>
389
- {renderHeader()}
390
- <View style={styles.shrink}>{screen}</View>
391
- </View>
392
- ) : (
393
- <>
394
- {renderHeader()}
395
- {screen}
396
- </>
397
- )}
293
+ {renderHeader()}
294
+ <Screen
295
+ {...props}
296
+ {...props.route.params}
297
+ onRequestClose={onRequestClose}
298
+ />
398
299
  {footerComponent && (
399
300
  <View
400
301
  style={[
@@ -403,7 +304,6 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
403
304
  paddingBottom: Spacing.S,
404
305
  backgroundColor: theme.colors.background.surface,
405
306
  },
406
- sideInsetStyle,
407
307
  ]}
408
308
  >
409
309
  {footerComponent}
@@ -453,7 +353,6 @@ const styles = StyleSheet.create({
453
353
  justifyContent: 'center',
454
354
  marginHorizontal: Spacing.M,
455
355
  },
456
- shrink: { flexShrink: 1 },
457
356
  debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
458
357
  });
459
358
 
@@ -58,13 +58,11 @@ export default function BottomTabBar({
58
58
  const { theme } = useContext(ApplicationContext);
59
59
  const { routes } = state;
60
60
  const widthDevice = useWindowDimensions()?.width;
61
- const insets = useSafeAreaInsets();
62
61
  const useFloating = floatingButton && state.routes.length % 2 === 0;
63
- const itemWidth =
64
- (widthDevice - insets.left - insets.right) /
65
- (state.routes.length + (useFloating ? 1 : 0));
62
+ const itemWidth = widthDevice / (state.routes.length + (useFloating ? 1 : 0));
66
63
  // const buildLink = useLinkBuilder();
67
64
  const indicatorAnimated = useSharedValue(0);
65
+ const insets = useSafeAreaInsets();
68
66
 
69
67
  const indicatorStyle = useAnimatedStyle(() => ({
70
68
  width: itemWidth,
@@ -248,8 +246,7 @@ export default function BottomTabBar({
248
246
  },
249
247
  {
250
248
  paddingBottom: paddingBottom,
251
- paddingLeft: insets.left,
252
- paddingRight: insets.right,
249
+ paddingHorizontal: Math.max(insets.left, insets.right),
253
250
  },
254
251
  style,
255
252
  ]}
@@ -27,10 +27,7 @@ import StackScreen from '../StackScreen';
27
27
  import { BottomTabProps } from '../types';
28
28
  import { getOptions, getStackOptions, useAppState } from '../utils';
29
29
  import BottomTabBar from './BottomTabBar';
30
- import { DuoTabBar } from './DuoTabBar';
31
- import { DuoBarCompressionProvider } from './DuoBarCompression';
32
30
  import { ApplicationContext, MiniAppContext } from '../../Context';
33
- import { useScreenLayout } from '../../Layout';
34
31
 
35
32
  const Tab = createBottomTabNavigator();
36
33
  const Stack = createStackNavigator();
@@ -39,13 +36,7 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
39
36
  const { navigator } = useContext(ApplicationContext);
40
37
  const context = useContext<any>(MiniAppContext);
41
38
  const { nested, options, screen, initialParams } = route?.params;
42
- const { headerHeight, headerStatusBarHeight } = useScreenLayout();
43
- const headerStyleOptions = {
44
- headerStyle: {
45
- height: headerHeight,
46
- },
47
- headerStatusBarHeight,
48
- };
39
+ const insets = useSafeAreaInsets();
49
40
 
50
41
  const opacityValue = 0.3;
51
42
  const scaleValue = 0.97;
@@ -148,7 +139,11 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
148
139
  return (
149
140
  <Animated.View style={[styles.container, tabAnimatedStyle]}>
150
141
  <Stack.Navigator
151
- screenOptions={headerStyleOptions}
142
+ screenOptions={{
143
+ headerStyle: {
144
+ height: 52 + insets.top,
145
+ },
146
+ }}
152
147
  >
153
148
  <Stack.Screen
154
149
  name="Stack"
@@ -179,7 +174,11 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
179
174
  <NavigationIndependentTree>
180
175
  <NavigationContainer>
181
176
  <Stack.Navigator
182
- screenOptions={headerStyleOptions}
177
+ screenOptions={{
178
+ headerStyle: {
179
+ height: 52 + insets.top,
180
+ },
181
+ }}
183
182
  >
184
183
  <Stack.Screen
185
184
  name="Stack"
@@ -206,10 +205,8 @@ const BottomTab: React.FC<BottomTabProps> = ({
206
205
  navigation,
207
206
  initialRouteName,
208
207
  floatingButton,
209
- compressionBehavior,
210
208
  }) => {
211
209
  const { theme, navigator } = useContext(ApplicationContext);
212
- const { isIPhoneDuo } = useScreenLayout();
213
210
  const dimensions = useWindowDimensions();
214
211
  const insets = useSafeAreaInsets();
215
212
  const initialIndex = tabs.findIndex(i => i.name === initialRouteName);
@@ -283,156 +280,138 @@ const BottomTab: React.FC<BottomTabProps> = ({
283
280
  };
284
281
 
285
282
  return (
286
- <DuoBarCompressionProvider
287
- behavior={compressionBehavior}
288
- tabCount={tabs.length}
289
- hasQr={!!floatingButton}
283
+ <Tab.Navigator
284
+ screenOptions={{
285
+ headerShown: false,
286
+ headerTransparent: true,
287
+ headerBackground: undefined,
288
+ tabBarLabelPosition: 'below-icon',
289
+ tabBarBackground: () => (
290
+ <View
291
+ style={[
292
+ styles.container,
293
+ {
294
+ backgroundColor: theme.colors.background.surface,
295
+ },
296
+ ]}
297
+ >
298
+ <Animated.View style={[styles.indicatorContainer, indicatorStyle]}>
299
+ <View
300
+ style={[
301
+ styles.indicator,
302
+ { backgroundColor: theme.colors.primary },
303
+ ]}
304
+ />
305
+ </Animated.View>
306
+ </View>
307
+ ),
308
+ }}
309
+ tabBar={props => (
310
+ <BottomTabBar
311
+ {...props}
312
+ floatingButton={floatingButton}
313
+ inactiveTintColor={theme.colors.text.hint}
314
+ activeTintColor={theme.colors.primary}
315
+ style={[
316
+ {
317
+ height:
318
+ 64 +
319
+ (Platform.OS === 'ios'
320
+ ? Math.min(insets.bottom, 21)
321
+ : insets.bottom),
322
+ },
323
+ ]}
324
+ />
325
+ )}
326
+ initialRouteName={initialRouteName}
290
327
  >
291
- <Tab.Navigator
292
- screenOptions={{
293
- headerShown: false,
294
- headerTransparent: true,
295
- headerBackground: undefined,
296
- tabBarLabelPosition: 'below-icon',
297
- tabBarBackground: () => (
298
- <View
299
- style={[
300
- styles.container,
301
- {
302
- backgroundColor: theme.colors.background.surface,
303
- },
304
- ]}
305
- >
306
- <Animated.View
307
- style={[styles.indicatorContainer, indicatorStyle]}
308
- >
309
- <View
310
- style={[
311
- styles.indicator,
312
- { backgroundColor: theme.colors.primary },
313
- ]}
314
- />
315
- </Animated.View>
316
- </View>
317
- ),
318
- }}
319
- tabBar={props =>
320
- isIPhoneDuo ? (
321
- <DuoTabBar
322
- {...props}
323
- floatingButton={floatingButton}
324
- inactiveTintColor={theme.colors.text.default}
325
- activeTintColor={theme.colors.primary}
326
- />
327
- ) : (
328
- <BottomTabBar
329
- {...props}
330
- floatingButton={floatingButton}
331
- inactiveTintColor={theme.colors.text.hint}
332
- activeTintColor={theme.colors.primary}
333
- style={[
334
- {
335
- height:
336
- 64 +
337
- (Platform.OS === 'ios'
338
- ? Math.min(insets.bottom, 21)
339
- : insets.bottom),
340
- },
341
- ]}
342
- />
343
- )
328
+ {tabs.map((item, index) => {
329
+ const isNum = !isNaN(parseInt(`${item?.badgeLabel}`, 10));
330
+ const isDot = typeof item?.badgeLabel === 'string' && !item?.badgeLabel;
331
+ const isNumZero = isNum && Number(item.badgeLabel) === 0;
332
+
333
+ let badgeLabel = item?.badgeLabel;
334
+ if (isNumZero) {
335
+ badgeLabel = undefined;
344
336
  }
345
- initialRouteName={initialRouteName}
346
- >
347
- {tabs.map((item, index) => {
348
- const isNum = !isNaN(parseInt(`${item?.badgeLabel}`, 10));
349
- const isDot =
350
- typeof item?.badgeLabel === 'string' && !item?.badgeLabel;
351
- const isNumZero = isNum && Number(item.badgeLabel) === 0;
352
337
 
353
- let badgeLabel = item?.badgeLabel;
354
- if (isNumZero) {
355
- badgeLabel = undefined;
356
- }
338
+ let badgeStyle: {
339
+ marginRight: number;
340
+ top: number;
341
+ backgroundColor: string;
342
+ width?: number;
343
+ } = {
344
+ marginRight: -8,
345
+ top: -4,
346
+ backgroundColor: Colors.orange_03,
347
+ };
357
348
 
358
- let badgeStyle: {
359
- marginRight: number;
360
- top: number;
361
- backgroundColor: string;
362
- width?: number;
363
- } = {
364
- marginRight: -8,
349
+ if (isNum) {
350
+ badgeStyle = {
351
+ marginRight: -4,
365
352
  top: -4,
353
+ backgroundColor: Colors.red_03,
354
+ };
355
+ }
356
+ if (isDot) {
357
+ badgeStyle = {
358
+ marginRight: -2,
359
+ top: -2,
366
360
  backgroundColor: Colors.orange_03,
361
+ width: 12,
367
362
  };
363
+ }
368
364
 
369
- if (isNum) {
370
- badgeStyle = {
371
- marginRight: -4,
372
- top: -4,
373
- backgroundColor: Colors.red_03,
374
- };
375
- }
376
- if (isDot) {
377
- badgeStyle = {
378
- marginRight: -2,
379
- top: -2,
380
- backgroundColor: Colors.orange_03,
381
- width: 12,
382
- };
383
- }
384
-
385
- return (
386
- <Tab.Screen
387
- key={`BottomTab-${item.name}`}
388
- name={item.name}
389
- component={TabScreen}
390
- initialParams={{ ...item, nested, index }}
391
- listeners={handler}
392
- options={{
393
- tabBarLabel: ({ focused, color }) => {
394
- return (
395
- <Text
396
- numberOfLines={1}
397
- typography={
398
- focused ? 'label_xs_medium' : 'description_xs_regular'
399
- }
400
- color={color}
401
- >
402
- {item.label}
403
- </Text>
404
- );
365
+ return (
366
+ <Tab.Screen
367
+ key={`BottomTab-${item.name}`}
368
+ name={item.name}
369
+ component={TabScreen}
370
+ initialParams={{ ...item, nested, index }}
371
+ listeners={handler}
372
+ options={{
373
+ tabBarLabel: ({ focused, color }) => {
374
+ return (
375
+ <Text
376
+ numberOfLines={1}
377
+ typography={
378
+ focused ? 'label_xs_medium' : 'description_xs_regular'
379
+ }
380
+ color={color}
381
+ >
382
+ {item.label}
383
+ </Text>
384
+ );
385
+ },
386
+ title: item.label,
387
+ tabBarStyle: [
388
+ {
389
+ height: 64,
405
390
  },
406
- title: item.label,
407
- tabBarStyle: [
408
- {
409
- height: 64,
410
- },
411
- styles.tabStyle,
412
- ],
413
- tabBarBadge: badgeLabel,
414
- tabBarBadgeStyle: [
415
- {
416
- borderColor: 'white',
417
- borderWidth: 2,
418
- fontSize: 10,
419
- lineHeight: 14,
420
- fontWeight: 'bold',
421
- alignSelf: 'center',
422
- fontFamily: exportFontFamily('bold'),
423
- },
424
- badgeStyle,
425
- ],
426
- tabBarIcon: ({ color }) => (
427
- <Icon source={item.icon} color={color} size={28} />
428
- ),
429
- ...item,
430
- }}
431
- />
432
- );
433
- })}
434
- </Tab.Navigator>
435
- </DuoBarCompressionProvider>
391
+ styles.tabStyle,
392
+ ],
393
+ tabBarBadge: badgeLabel,
394
+ tabBarBadgeStyle: [
395
+ {
396
+ borderColor: 'white',
397
+ borderWidth: 2,
398
+ fontSize: 10,
399
+ lineHeight: 14,
400
+ fontWeight: 'bold',
401
+ alignSelf: 'center',
402
+ fontFamily: exportFontFamily('bold'),
403
+ },
404
+ badgeStyle,
405
+ ],
406
+ tabBarIcon: ({ color }) => (
407
+ <Icon source={item.icon} color={color} size={28} />
408
+ ),
409
+ ...item,
410
+ }}
411
+ />
412
+ );
413
+ })}
414
+ </Tab.Navigator>
436
415
  );
437
416
  };
438
417