@momo-kits/foundation 0.167.1-duotest.2 → 0.167.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 (43) hide show
  1. package/Application/BottomSheet.tsx +14 -115
  2. package/Application/BottomTab/BottomTabBar.tsx +3 -6
  3. package/Application/BottomTab/index.tsx +135 -162
  4. package/Application/Components/HeaderExtendHeader.tsx +28 -75
  5. package/Application/Components/HeaderLeft.tsx +0 -38
  6. package/Application/Components/HeaderRight.tsx +9 -142
  7. package/Application/Components/HeaderTitle.tsx +7 -43
  8. package/Application/Components/SearchHeader.tsx +15 -22
  9. package/Application/ModalScreen.tsx +2 -16
  10. package/Application/NavigationContainer.tsx +38 -22
  11. package/Application/Navigator.ts +3 -1
  12. package/Application/StackScreen.tsx +9 -63
  13. package/Application/types.ts +1 -27
  14. package/Application/utils.tsx +6 -242
  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 +52 -199
  26. package/Layout/Section.tsx +18 -5
  27. package/Layout/index.ts +4 -4
  28. package/Layout/styles.ts +0 -5
  29. package/Layout/utils.ts +5 -465
  30. package/Popup/PopupNotify.tsx +22 -33
  31. package/Popup/PopupPromotion.tsx +8 -20
  32. package/Text/utils.ts +2 -1
  33. package/package.json +2 -3
  34. package/Application/BottomTab/DuoBarCompression.tsx +0 -135
  35. package/Application/BottomTab/DuoTabBar.tsx +0 -462
  36. package/Application/Components/DuoGlassBackground.tsx +0 -179
  37. package/Application/Components/DuoQuickReturnHeader.tsx +0 -42
  38. package/Application/Components/OptionalGlassView.ts +0 -11
  39. package/Application/Components/OptionalScrollObserver.ts +0 -12
  40. package/Application/Components/SideBarButton.tsx +0 -139
  41. package/Application/device.ts +0 -29
  42. package/Layout/ReservedRegions.tsx +0 -47
  43. package/Popup/utils.ts +0 -32
package/Layout/Screen.tsx CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  ScrollViewProps,
22
22
  StatusBar,
23
23
  StatusBarStyle,
24
+ useWindowDimensions,
24
25
  View,
25
26
  ViewProps,
26
27
  } from 'react-native';
@@ -42,12 +43,8 @@ import { FloatingButton, FloatingButtonProps } from './FloatingButton';
42
43
  import styles from './styles';
43
44
  import { HeaderType } from './types';
44
45
  import { InputRef } from '../Input';
45
- import {
46
- exportHeaderTitle,
47
- getOptions,
48
- useDuoQuickReturn,
49
- } from '../Application/utils';
50
- import { getHeaderMargin, useScreenLayout, validateChildren } from './utils';
46
+ import { exportHeaderTitle, getOptions } from '../Application/utils';
47
+ import { validateChildren } from './utils';
51
48
  import Section from './Section';
52
49
  import {
53
50
  HeaderBackground,
@@ -56,11 +53,6 @@ import {
56
53
  HeaderTitle,
57
54
  SearchHeader,
58
55
  } from '../Application';
59
- import {
60
- DuoGlassBackground,
61
- DuoScrollGradient,
62
- } from '../Application/Components/DuoGlassBackground';
63
- import { useDuoTabBarInset } from '../Application/BottomTab/DuoTabBar';
64
56
 
65
57
  export interface ScreenProps extends ViewProps {
66
58
  /**
@@ -126,8 +118,7 @@ export interface ScreenProps extends ViewProps {
126
118
  layoutOffset?: -8 | -24 | -56 | number;
127
119
 
128
120
  /**
129
- * Optional. Width of the right header. Defaults to the toolkit width (73), or 0 when
130
- * iPhone Duo floats the toolkit into its side bar.
121
+ * Optional. Width of the right header.
131
122
  */
132
123
  headerRightWidth?: 0 | 73 | 110 | number;
133
124
 
@@ -196,7 +187,7 @@ const Screen = forwardRef(
196
187
  footerComponent: Footer,
197
188
  marginFooter = true,
198
189
  layoutOffset = -56,
199
- headerRightWidth,
190
+ headerRightWidth = 73,
200
191
  backgroundColor,
201
192
  headerType = 'default',
202
193
  floatingButtonProps,
@@ -213,8 +204,8 @@ const Screen = forwardRef(
213
204
  ref: any,
214
205
  ) => {
215
206
  const screenRef = useRef<View | ScrollView>(null);
216
- const layout = useScreenLayout();
217
- const { theme } = useContext(ApplicationContext);
207
+ const { width: widthDevice } = useWindowDimensions();
208
+ const { theme, navigator } = useContext(ApplicationContext);
218
209
  const screen: any = useContext(ScreenContext);
219
210
  const insets = useSafeAreaInsets();
220
211
  const frame = useSafeAreaFrame();
@@ -254,37 +245,15 @@ const Screen = forwardRef(
254
245
  }, [customAnimatedValue, internalShared]);
255
246
 
256
247
  const currentTint = useRef<string | undefined>(undefined);
257
-
248
+
258
249
  const isTab = !!screen?.bottomTab;
259
- // Duo floating tab bar overlays the tab content instead of taking layout space
260
- const duoTabInset = useDuoTabBarInset();
261
250
 
262
251
  let handleScroll;
263
252
  let Component: any = View;
264
253
 
265
- const bottomInset =
266
- Platform.OS === 'ios' ? Math.min(insets.bottom, 21) : insets.bottom;
267
- const hasSideBar = !!layout.sideBar;
268
- // iPhone Duo: content scrolls under a glass header instead of starting below it
269
- const useDuoGlass =
270
- hasSideBar &&
271
- (headerType === 'default' || headerType === 'extended') &&
272
- !animatedHeader &&
273
- !inputSearchProps;
274
-
275
- // iPhone Duo: the header leaves with the content and returns on a reverse scroll
276
- const useQuickReturn =
277
- hasSideBar && scrollable && (useDuoGlass || !!animatedHeader);
278
- const { quickReturnOptions, snap: snapQuickReturnHeader } =
279
- useDuoQuickReturn(sharedValue, useQuickReturn, heightHeader);
280
-
254
+ const bottomInset = Platform.OS === 'ios' ? Math.min(insets.bottom, 21) : insets.bottom;
281
255
  let keyboardOffset = heightHeader - bottomInset;
282
- if (
283
- headerType === 'extended' ||
284
- animatedHeader ||
285
- inputSearchProps ||
286
- useDuoGlass
287
- ) {
256
+ if (headerType === 'extended' || animatedHeader || inputSearchProps) {
288
257
  keyboardOffset = -bottomInset;
289
258
  }
290
259
 
@@ -292,12 +261,6 @@ const Screen = forwardRef(
292
261
  * inject params for screen tracking
293
262
  */
294
263
  screen?.onSetParams?.(trackingParams);
295
- // the screen drives its header, the stack screen's scroll observer stays off
296
- screen?.claimHeader?.();
297
- useEffect(() => {
298
- screen?.syncHeader?.();
299
- // eslint-disable-next-line react-hooks/exhaustive-deps
300
- }, []);
301
264
 
302
265
  /**
303
266
  * export options for screen
@@ -317,13 +280,9 @@ const Screen = forwardRef(
317
280
 
318
281
  case 'extended':
319
282
  options = {
320
- ...quickReturnOptions,
321
283
  headerShown: true,
322
284
  headerTransparent: true,
323
- headerBackground: () =>
324
- hasSideBar ? (
325
- <DuoGlassBackground animatedValue={sharedValue} />
326
- ) : null,
285
+ headerBackground: () => null,
327
286
  headerTitle: (props: any) => <HeaderTitle {...props} />,
328
287
  };
329
288
  if (inputSearchProps) {
@@ -346,19 +305,6 @@ const Screen = forwardRef(
346
305
  break;
347
306
 
348
307
  default:
349
- if (hasSideBar && !inputSearchProps) {
350
- options = {
351
- ...quickReturnOptions,
352
- headerTransparent: true,
353
- headerTintColor: Colors.black_17,
354
- headerShown: true,
355
- headerBackground: () => (
356
- <DuoGlassBackground animatedValue={sharedValue} />
357
- ),
358
- headerTitle: (props: any) => <HeaderTitle {...props} />,
359
- };
360
- break;
361
- }
362
308
  options = {
363
309
  headerTransparent: false,
364
310
  headerTintColor: Colors.black_17,
@@ -393,7 +339,7 @@ const Screen = forwardRef(
393
339
  }
394
340
  }
395
341
 
396
- navigation?.instance?.setOptions({ header: undefined, ...options });
342
+ navigation?.instance?.setOptions(options);
397
343
  },
398
344
  [
399
345
  gradientColor,
@@ -402,8 +348,6 @@ const Screen = forwardRef(
402
348
  navigation?.instance,
403
349
  sharedValue,
404
350
  useShadowHeader,
405
- hasSideBar,
406
- quickReturnOptions,
407
351
  ],
408
352
  );
409
353
 
@@ -419,26 +363,18 @@ const Screen = forwardRef(
419
363
  }
420
364
 
421
365
  options = {
422
- ...quickReturnOptions,
423
366
  headerTintColor: currentTint.current ?? Colors.black_17,
424
367
  headerTransparent: true,
425
- headerBackground: (props: any) =>
426
- hasSideBar ? (
427
- // glass shows once the banner has scrolled under the header
428
- <DuoGlassBackground
429
- animatedValue={sharedValue}
430
- fadeStart={Math.max(210 + layoutOffset - heightHeader, 0)}
431
- />
432
- ) : (
433
- <HeaderBackground
434
- {...props}
435
- animatedValue={sharedValue}
436
- useGradient={false}
437
- useShadowHeader={useShadowHeader}
438
- headerBackground={headerBackground}
439
- gradientColor={gradientColor}
440
- />
441
- ),
368
+ headerBackground: (props: any) => (
369
+ <HeaderBackground
370
+ {...props}
371
+ animatedValue={sharedValue}
372
+ useGradient={false}
373
+ useShadowHeader={useShadowHeader}
374
+ headerBackground={headerBackground}
375
+ gradientColor={gradientColor}
376
+ />
377
+ ),
442
378
  headerTitle: (props: any) => {
443
379
  return <HeaderTitle {...props} />;
444
380
  },
@@ -459,10 +395,6 @@ const Screen = forwardRef(
459
395
  navigation?.instance,
460
396
  sharedValue,
461
397
  useShadowHeader,
462
- hasSideBar,
463
- layoutOffset,
464
- heightHeader,
465
- quickReturnOptions,
466
398
  ],
467
399
  );
468
400
 
@@ -486,24 +418,14 @@ const Screen = forwardRef(
486
418
  ),
487
419
  };
488
420
 
489
- navigation?.instance?.setOptions({ header: undefined, ...options });
421
+ navigation?.instance?.setOptions(options);
490
422
  }, [navigation?.instance, sharedValue]);
491
423
 
492
424
  /**
493
425
  * export search header
494
426
  */
495
427
  const setSearchHeader = (params: SearchHeaderProps) => {
496
- let searchWidth = layout.width - 24;
497
- let searchLeft = Spacing.M;
498
- if (layout.sideBar) {
499
- // back floats in the side bar: the search spans the content width
500
- searchWidth = layout.contentWidth - Spacing.M * 2;
501
- searchLeft = Spacing.M - getHeaderMargin(layout.width);
502
- }
503
- if (layout.statusBarClearance) {
504
- searchWidth -=
505
- layout.statusBarClearance.left + layout.statusBarClearance.right;
506
- }
428
+ const searchWidth = widthDevice - 24;
507
429
  const options: StackNavigationOptions = {
508
430
  headerRight: undefined,
509
431
  headerTitleContainerStyle: {
@@ -515,10 +437,10 @@ const Screen = forwardRef(
515
437
  marginHorizontal: 0,
516
438
  marginVertical: 0,
517
439
  margin: 0,
518
- left: searchLeft,
440
+ left: Spacing.M,
519
441
  },
520
442
  headerLeft: (props: any) =>
521
- params?.hiddenBack ? null : <HeaderLeft {...props} floating />,
443
+ params?.hiddenBack ? null : <HeaderLeft {...props} />,
522
444
  headerTitle: () => (
523
445
  <SearchHeader {...params} animatedValue={sharedValue} />
524
446
  ),
@@ -614,16 +536,12 @@ const Screen = forwardRef(
614
536
  );
615
537
  }
616
538
 
617
- const handleMomentumScrollEnd = (
618
- e: NativeSyntheticEvent<NativeScrollEvent>,
619
- ) => {
620
- snapQuickReturnHeader(e.nativeEvent.contentOffset.y);
621
- scrollViewProps?.onMomentumScrollEnd?.(e);
622
- };
623
-
539
+ /**
540
+ * handle scroll end
541
+ * @param e
542
+ */
624
543
  const handleScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
625
544
  const offsetY = e.nativeEvent.contentOffset.y;
626
- snapQuickReturnHeader(offsetY);
627
545
  if (inputSearchProps && offsetY < 100 && offsetY > 0) {
628
546
  Animated.timing(animatedValue.current, {
629
547
  toValue: 0,
@@ -673,8 +591,6 @@ const Screen = forwardRef(
673
591
  key={`ScreenItem${index}`}
674
592
  style={{
675
593
  marginTop: space,
676
- marginLeft: layout.insetLeft,
677
- marginRight: layout.insetRight,
678
594
  flex: item.props?.expanded ? 1 : undefined,
679
595
  }}
680
596
  onLayout={item.props?.onLayout}
@@ -695,8 +611,6 @@ const Screen = forwardRef(
695
611
  key={`ScreenItem`}
696
612
  style={{
697
613
  marginTop: space,
698
- marginLeft: layout.insetLeft,
699
- marginRight: layout.insetRight,
700
614
  flex: item?.props?.expanded ? 1 : undefined,
701
615
  }}
702
616
  onLayout={item?.props?.onLayout}
@@ -723,15 +637,6 @@ const Screen = forwardRef(
723
637
  return newHeight;
724
638
  };
725
639
 
726
- // without the grid, the content container keeps clear of the horizontal insets.
727
- // Only set when there are insets, so a caller's paddingHorizontal is never overridden.
728
- const hasContentInset = layout.insetLeft > 0 || layout.insetRight > 0;
729
- const contentInsetStyle = !useGridLayout &&
730
- hasContentInset && {
731
- paddingLeft: layout.insetLeft,
732
- paddingRight: layout.insetRight,
733
- };
734
-
735
640
  return (
736
641
  <View
737
642
  style={[
@@ -744,36 +649,21 @@ const Screen = forwardRef(
744
649
  },
745
650
  ]}
746
651
  >
747
- {useDuoGlass ? (
748
- <DuoScrollGradient
749
- animatedValue={sharedValue}
750
- gradientColor={gradientColor}
751
- headerBackground={headerBackground}
752
- heightHeader={heightHeader}
753
- extended={headerType === 'extended'}
754
- followScroll={useQuickReturn}
755
- />
756
- ) : (
757
- <HeaderExtendHeader
758
- headerType={headerType}
759
- heightHeader={heightHeader}
760
- headerRightWidth={headerRightWidth}
761
- animatedValue={sharedValue}
762
- inputSearchProps={inputSearchProps}
763
- navigation={navigation}
764
- inputSearchRef={inputSearchRef}
765
- useShadowHeader={useShadowHeader}
766
- gradientColor={gradientColor}
767
- headerBackground={headerBackground}
768
- />
769
- )}
652
+ <HeaderExtendHeader
653
+ headerType={headerType}
654
+ heightHeader={heightHeader}
655
+ headerRightWidth={headerRightWidth}
656
+ animatedValue={sharedValue}
657
+ inputSearchProps={inputSearchProps}
658
+ navigation={navigation}
659
+ inputSearchRef={inputSearchRef}
660
+ useShadowHeader={useShadowHeader}
661
+ gradientColor={gradientColor}
662
+ headerBackground={headerBackground}
663
+ />
770
664
 
771
665
  <KeyboardAvoidingView
772
- style={[
773
- Styles.flex,
774
- useDuoGlass &&
775
- (!scrollable || !!Header) && { paddingTop: heightHeader },
776
- ]}
666
+ style={Styles.flex}
777
667
  enabled={enableKeyboardAvoidingView}
778
668
  keyboardVerticalOffset={keyboardVerticalOffset ?? keyboardOffset}
779
669
  behavior={Platform.select({
@@ -789,24 +679,10 @@ const Screen = forwardRef(
789
679
  showsVerticalScrollIndicator={false}
790
680
  onScroll={handleScroll}
791
681
  onScrollEndDrag={handleScrollEnd}
792
- onMomentumScrollEnd={handleMomentumScrollEnd}
793
682
  scrollEventThrottle={16}
794
- style={[
795
- Styles.flex,
796
- !scrollable && contentInsetStyle,
797
- !scrollable &&
798
- !Footer &&
799
- isTab &&
800
- !!duoTabInset && { paddingBottom: duoTabInset },
801
- ]}
683
+ style={[Styles.flex]}
802
684
  contentContainerStyle={[
803
685
  scrollable && !Footer && !isTab && { paddingBottom: bottomInset },
804
- scrollable &&
805
- !Footer &&
806
- isTab &&
807
- !!duoTabInset && { paddingBottom: duoTabInset },
808
- scrollable && contentInsetStyle,
809
- useDuoGlass && !Header && { paddingTop: heightHeader },
810
686
  scrollViewProps?.contentContainerStyle,
811
687
  ]}
812
688
  >
@@ -816,51 +692,28 @@ const Screen = forwardRef(
816
692
  </Component>
817
693
 
818
694
  {floatingButtonProps && (
819
- <View
820
- style={{
821
- marginLeft: layout.insetLeft,
822
- marginRight: layout.insetRight,
823
- }}
824
- >
695
+ <View>
825
696
  <FloatingButton
826
697
  {...floatingButtonProps}
827
698
  animatedValue={sharedValue}
828
- bottom={
829
- Footer || isTab
830
- ? 12 + (Footer ? 0 : duoTabInset)
831
- : bottomInset + Spacing.S
832
- }
699
+ bottom={Footer || isTab ? 12 : bottomInset + Spacing.S}
833
700
  />
834
701
  </View>
835
702
  )}
836
703
 
837
704
  {Footer && (
838
- // with side insets, the clip keeps the shadow off the side bar, only the top one shows
839
705
  <View
840
- pointerEvents={'box-none'}
841
706
  style={[
842
- hasContentInset && styles.footerClip,
707
+ styles.shadow,
843
708
  {
844
- marginLeft: layout.insetLeft,
845
- marginRight: layout.insetRight,
709
+ paddingBottom: bottomInset + Spacing.S,
710
+ backgroundColor: theme.colors.background.surface,
846
711
  },
847
712
  ]}
848
713
  >
849
- <View
850
- style={[
851
- styles.shadow,
852
- {
853
- paddingBottom:
854
- (isTab && duoTabInset ? duoTabInset : bottomInset) +
855
- Spacing.S,
856
- backgroundColor: theme.colors.background.surface,
857
- },
858
- ]}
859
- >
860
- {/* The footer inset is a caller decision, not a platform one — a footer
861
- that carries its own horizontal padding passes `marginFooter={false}`. */}
862
- <Section useMargin={marginFooter}>{Footer}</Section>
863
- </View>
714
+ {/* The footer inset is a caller decision, not a platform one — a footer
715
+ that carries its own horizontal padding passes `marginFooter={false}`. */}
716
+ <Section useMargin={marginFooter}>{Footer}</Section>
864
717
  </View>
865
718
  )}
866
719
  </KeyboardAvoidingView>
@@ -1,8 +1,9 @@
1
- import React from 'react';
2
- import { View, ViewProps } from 'react-native';
3
- import { useGridSystem, useScreenLayout } from './utils';
1
+ import React, { useContext } from 'react';
2
+ import { Dimensions, View, ViewProps } from 'react-native';
3
+ import { useGridSystem } from './utils';
4
4
  import { GridContextProps } from './types';
5
5
  import { GridContext } from './index';
6
+ import { ApplicationContext, MiniAppContext } from '../Context';
6
7
  import styles from './styles';
7
8
  import { Image } from '../Image';
8
9
  import { Spacing } from '../Consts';
@@ -33,15 +34,18 @@ const Section: React.FC<SectionProps> = ({
33
34
  style,
34
35
  ...props
35
36
  }) => {
37
+ const { showGrid } = useContext(ApplicationContext);
36
38
  const { numberOfColumns } = useGridSystem();
37
- const { contentWidth } = useScreenLayout();
38
39
 
39
40
  const gutterSize = 12;
40
41
  let margin = 0;
41
42
  if (useMargin) {
42
43
  margin = 12;
43
44
  }
44
- const widthSection = contentWidth - margin * 2;
45
+ let widthSection = Dimensions.get('window').width;
46
+ if (useMargin) {
47
+ widthSection = widthSection - margin * 2;
48
+ }
45
49
 
46
50
  const sizePerSpan =
47
51
  (widthSection - gutterSize * (numberOfColumns - 1)) / numberOfColumns -
@@ -56,6 +60,13 @@ const Section: React.FC<SectionProps> = ({
56
60
  },
57
61
  };
58
62
 
63
+ /**
64
+ * render overlay only dev mode
65
+ */
66
+ const renderOverlay = () => {
67
+ return <View pointerEvents={'none'} style={styles.boxGridSection} />;
68
+ };
69
+
59
70
  return (
60
71
  <GridContext.Provider value={gridContext}>
61
72
  <View
@@ -79,7 +90,9 @@ const Section: React.FC<SectionProps> = ({
79
90
  style={styles.imageBackground}
80
91
  />
81
92
  )}
93
+ {/* Render only allowed children as valid React elements */}
82
94
  {children}
95
+ {showGrid && renderOverlay()}
83
96
  </View>
84
97
  </GridContext.Provider>
85
98
  );
package/Layout/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import Card from './Card';
2
2
  import Section from './Section';
3
- import { useScreenLayout, validateChildren } from './utils';
4
- import type { ScreenLayout, SideBarEdge, SideBarLayout } from './utils';
3
+ import GridSystem from './GridSystem';
4
+ import { validateChildren } from './utils';
5
5
  import { createContext } from 'react';
6
6
  import { GridContextProps } from './types';
7
7
  import Item from './Item';
@@ -18,14 +18,14 @@ const GridContext = createContext<GridContextProps>({
18
18
  getSizeSpan: () => 0,
19
19
  });
20
20
 
21
- export type { ScreenProps, ScreenLayout, SideBarEdge, SideBarLayout };
21
+ export type { ScreenProps };
22
22
 
23
23
  export {
24
24
  GridContext,
25
25
  Card,
26
26
  Section,
27
+ GridSystem,
27
28
  validateChildren,
28
- useScreenLayout,
29
29
  Item,
30
30
  ItemList,
31
31
  ItemSectionList,
package/Layout/styles.ts CHANGED
@@ -91,10 +91,5 @@ export default StyleSheet.create({
91
91
  },
92
92
  }),
93
93
  },
94
- footerClip: {
95
- marginTop: -Spacing.XL,
96
- paddingTop: Spacing.XL,
97
- overflow: 'hidden',
98
- },
99
94
  debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
100
95
  });