@momo-kits/foundation 0.168.1-beta.37 → 0.168.1-beta.39

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 (131) hide show
  1. package/Application/BottomSheet.hooks.ts +8 -8
  2. package/Application/BottomSheet.parts.tsx +44 -34
  3. package/Application/BottomSheet.tsx +35 -26
  4. package/Application/BottomTab/ActiveIndicator.tsx +15 -14
  5. package/Application/BottomTab/BottomTabBar.tsx +49 -62
  6. package/Application/BottomTab/BottomTabInset.tsx +8 -4
  7. package/Application/BottomTab/CurvedContainer.tsx +14 -12
  8. package/Application/BottomTab/CustomBottomTabItem.tsx +13 -13
  9. package/Application/BottomTab/DuoBarCompression.tsx +13 -19
  10. package/Application/BottomTab/DuoRailTabBar.tsx +20 -24
  11. package/Application/BottomTab/DuoTabBar.tsx +8 -10
  12. package/Application/BottomTab/TabBarIcon.tsx +15 -29
  13. package/Application/BottomTab/TabScreen.tsx +1 -5
  14. package/Application/BottomTab/index.tsx +33 -30
  15. package/Application/BottomTab/metrics.ts +23 -23
  16. package/Application/BottomTab/tabItems.ts +1 -1
  17. package/Application/BottomTab/usePressLift.ts +2 -2
  18. package/Application/Components/BackgroundImageView.tsx +4 -0
  19. package/Application/Components/DuoGlassBackground.tsx +5 -4
  20. package/Application/Components/HeaderAnimated.tsx +40 -12
  21. package/Application/Components/HeaderBackground.tsx +19 -2
  22. package/Application/Components/HeaderExtendHeader.tsx +51 -37
  23. package/Application/Components/HeaderLeft.tsx +5 -18
  24. package/Application/Components/HeaderRight.styles.ts +15 -13
  25. package/Application/Components/HeaderRight.tsx +2 -1
  26. package/Application/Components/HeaderTitle.shared.ts +17 -16
  27. package/Application/Components/HeaderTitle.tsx +4 -3
  28. package/Application/Components/HeaderToolkitAction.tsx +29 -57
  29. package/Application/Components/NavigationButton.tsx +10 -6
  30. package/Application/Components/SearchHeader.tsx +37 -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 +17 -32
  35. package/Application/Localize.ts +7 -0
  36. package/Application/ModalScreen.tsx +9 -3
  37. package/Application/Navigation.ts +14 -16
  38. package/Application/NavigationContainer.tsx +19 -16
  39. package/Application/Navigator.ts +20 -48
  40. package/Application/ScaleSizeProvider.tsx +13 -6
  41. package/Application/StackScreen.tracking.ts +11 -26
  42. package/Application/StackScreen.tsx +17 -35
  43. package/Application/TooltipPortal.tsx +12 -5
  44. package/Application/WidgetContainer.tsx +10 -4
  45. package/Application/headerLayout.ts +158 -0
  46. package/Application/index.ts +11 -1
  47. package/Application/modalLayout.ts +13 -14
  48. package/Application/navigationOptions.tsx +27 -32
  49. package/Application/types.header.ts +7 -2
  50. package/Application/types.ts +7 -0
  51. package/Badge/Badge.tsx +5 -0
  52. package/Badge/BadgeDot.tsx +1 -0
  53. package/Badge/BadgeDotAnimation.tsx +15 -7
  54. package/Badge/BadgeRibbon.tsx +46 -38
  55. package/Badge/Shape.tsx +2 -0
  56. package/Badge/types.ts +2 -2
  57. package/Button/index.tsx +5 -16
  58. package/Button/utils.ts +9 -15
  59. package/CheckBox/index.tsx +4 -0
  60. package/Consts/theme.ts +10 -10
  61. package/Context/index.ts +5 -1
  62. package/Divider/DashDivider.tsx +1 -0
  63. package/Divider/index.tsx +4 -0
  64. package/FoundationList/index.tsx +4 -0
  65. package/Icon/index.tsx +6 -6
  66. package/Icon/types.ts +1 -1
  67. package/IconButton/index.tsx +4 -12
  68. package/Image/index.tsx +9 -7
  69. package/Input/Input.tsx +8 -9
  70. package/Input/InputDropDown.tsx +10 -12
  71. package/Input/InputMoney.tsx +12 -8
  72. package/Input/InputMoneyView.tsx +14 -7
  73. package/Input/InputOTP.tsx +35 -33
  74. package/Input/InputPhoneNumber.tsx +8 -9
  75. package/Input/InputSearch.tsx +22 -8
  76. package/Input/InputTextArea.tsx +4 -0
  77. package/Input/SearchTextTyping.tsx +4 -0
  78. package/Input/SystemTextInput.tsx +7 -2
  79. package/Input/TextTyping.tsx +4 -0
  80. package/Input/common.tsx +26 -12
  81. package/Input/index.tsx +24 -21
  82. package/Input/moneyUtils.ts +10 -4
  83. package/Input/styles.ts +0 -5
  84. package/Input/utils.ts +14 -12
  85. package/Layout/Card.tsx +7 -4
  86. package/Layout/FloatingButton.tsx +4 -0
  87. package/Layout/Item.tsx +6 -2
  88. package/Layout/ItemList.tsx +9 -7
  89. package/Layout/ItemSectionList.tsx +2 -1
  90. package/Layout/ReservedRegions.tsx +25 -25
  91. package/Layout/Screen.tsx +59 -58
  92. package/Layout/Section.tsx +8 -5
  93. package/Layout/TrackingScope.tsx +1 -0
  94. package/Layout/screenContent.tsx +4 -2
  95. package/Layout/screenFooter.tsx +28 -18
  96. package/Layout/screenHeader.tsx +88 -82
  97. package/Layout/screenQuickReturn.tsx +17 -20
  98. package/Layout/screenScroll.ts +4 -7
  99. package/Layout/screenTypes.ts +13 -4
  100. package/Layout/types.ts +2 -4
  101. package/Layout/utils.ts +31 -37
  102. package/Loader/DotLoader.tsx +4 -0
  103. package/Loader/Loader.tsx +4 -0
  104. package/Loader/ProgressBar.tsx +1 -0
  105. package/Loader/Spinner.tsx +4 -0
  106. package/Loader/utils.ts +11 -7
  107. package/Pagination/Dot.tsx +4 -0
  108. package/Pagination/PaginationDot.tsx +1 -0
  109. package/Pagination/PaginationNumber.tsx +5 -3
  110. package/Pagination/PaginationScroll.tsx +5 -0
  111. package/Pagination/PaginationWhiteDot.tsx +1 -0
  112. package/Pagination/index.tsx +4 -0
  113. package/Pagination/types.ts +2 -2
  114. package/Popup/PopupNotify.tsx +44 -48
  115. package/Popup/PopupPromotion.tsx +4 -7
  116. package/Popup/types.ts +4 -4
  117. package/Popup/utils.ts +4 -0
  118. package/Radio/index.tsx +4 -0
  119. package/Skeleton/index.tsx +8 -0
  120. package/Switch/index.tsx +1 -0
  121. package/Tag/index.tsx +5 -1
  122. package/Tag/types.ts +3 -3
  123. package/Text/index.tsx +14 -3
  124. package/Text/styles.ts +1 -1
  125. package/Text/types.ts +1 -6
  126. package/Text/utils.ts +20 -4
  127. package/Title/index.tsx +41 -32
  128. package/ViewBoundary/index.tsx +4 -2
  129. package/index.ts +0 -6
  130. package/package.json +37 -37
  131. package/Application/__tests__/modalLayout.test.ts +0 -128
package/Layout/Screen.tsx CHANGED
@@ -37,6 +37,10 @@ import { useScreenQuickReturn } from './screenQuickReturn';
37
37
 
38
38
  export type { ScreenProps };
39
39
 
40
+ /**
41
+ * Screen container: sets the navigation header, lays children on the grid, and adds the
42
+ * scroll view, floating button and footer, with iPhone Duo side bar and tab bar insets.
43
+ */
40
44
  const Screen = forwardRef(
41
45
  (
42
46
  {
@@ -63,6 +67,7 @@ const Screen = forwardRef(
63
67
  headerBackground,
64
68
  gradientColor,
65
69
  trackingParams,
70
+ contentLayout,
66
71
  }: ScreenProps,
67
72
  ref: any,
68
73
  ) => {
@@ -108,14 +113,12 @@ const Screen = forwardRef(
108
113
  const isIOS = Platform.OS === 'ios';
109
114
  const bottomInset = isIOS ? Math.min(insets.bottom, 21) : insets.bottom;
110
115
  const hasSideBar = !!layout.sideBar;
111
- const useDuoGlass =
112
- hasSideBar &&
113
- (headerType === 'default' || headerType === 'extended') &&
114
- !animatedHeader &&
115
- !inputSearchProps;
116
+ const isPlainHeader = headerType === 'default' || headerType === 'extended';
117
+ const hasCustomHeader = !!animatedHeader || !!inputSearchProps;
118
+ const useDuoGlass = hasSideBar && isPlainHeader && !hasCustomHeader;
116
119
 
117
- const useQuickReturn =
118
- hasSideBar && scrollable && (useDuoGlass || !!animatedHeader);
120
+ const hasOverlayHeader = useDuoGlass || !!animatedHeader;
121
+ const useQuickReturn = hasSideBar && scrollable && hasOverlayHeader;
119
122
  const { quickReturnOptions, snapQuickReturnHeader } = useScreenQuickReturn(
120
123
  sharedValue,
121
124
  useQuickReturn,
@@ -123,18 +126,10 @@ const Screen = forwardRef(
123
126
  );
124
127
 
125
128
  let keyboardOffset = heightHeader - bottomInset;
126
- if (
127
- headerType === 'extended' ||
128
- animatedHeader ||
129
- inputSearchProps ||
130
- useDuoGlass
131
- ) {
129
+ if (headerType === 'extended' || hasCustomHeader || useDuoGlass) {
132
130
  keyboardOffset = -bottomInset;
133
131
  }
134
132
 
135
- /**
136
- * inject params for screen tracking
137
- */
138
133
  screen?.onSetParams?.(trackingParams);
139
134
 
140
135
  const { setSearchHeader, currentTint } = useScreenHeader({
@@ -150,11 +145,10 @@ const Screen = forwardRef(
150
145
  gradientColor,
151
146
  headerBackground,
152
147
  layoutOffset,
148
+ contentLayout,
153
149
  });
154
150
 
155
- /**
156
- * expose ref for screen
157
- */
151
+ // mutates the content node so callers keep its methods plus the header setters
158
152
  useImperativeHandle(ref, () => {
159
153
  return Object.assign(screenRef.current || {}, {
160
154
  setOptions: (params: NavigationOptions) => {
@@ -165,19 +159,18 @@ const Screen = forwardRef(
165
159
  });
166
160
  });
167
161
 
168
- const { handleScroll, handleScrollEnd, handleMomentumScrollEnd } =
169
- createScrollHandlers({
170
- scrollable,
171
- animatedValue: animatedValue.current,
172
- sharedValue,
173
- scrollViewProps,
174
- animatedHeader,
175
- inputSearchProps,
176
- currentTint,
177
- navigation,
178
- snapQuickReturnHeader,
179
- ref,
180
- });
162
+ const scrollHandlers = createScrollHandlers({
163
+ scrollable,
164
+ animatedValue: animatedValue.current,
165
+ sharedValue,
166
+ scrollViewProps,
167
+ animatedHeader,
168
+ inputSearchProps,
169
+ currentTint,
170
+ navigation,
171
+ snapQuickReturnHeader,
172
+ ref,
173
+ });
181
174
  const Component: any = scrollable ? Animated.ScrollView : View;
182
175
 
183
176
  const getHeight = () => {
@@ -207,6 +200,36 @@ const Screen = forwardRef(
207
200
  floatingButtonBottom = 12 + duoTabInset;
208
201
  }
209
202
 
203
+ const renderHeaderLayer = () => {
204
+ if (useDuoGlass) {
205
+ return (
206
+ <DuoScrollGradient
207
+ animatedValue={sharedValue}
208
+ gradientColor={gradientColor}
209
+ headerBackground={headerBackground}
210
+ heightHeader={heightHeader}
211
+ extended={headerType === 'extended'}
212
+ followScroll={useQuickReturn}
213
+ />
214
+ );
215
+ }
216
+ return (
217
+ <HeaderExtendHeader
218
+ headerType={headerType}
219
+ heightHeader={heightHeader}
220
+ headerRightWidth={headerRightWidth}
221
+ animatedValue={sharedValue}
222
+ inputSearchProps={inputSearchProps}
223
+ navigation={navigation}
224
+ inputSearchRef={inputSearchRef}
225
+ useShadowHeader={useShadowHeader}
226
+ gradientColor={gradientColor}
227
+ headerBackground={headerBackground}
228
+ contentLayout={contentLayout}
229
+ />
230
+ );
231
+ };
232
+
210
233
  return (
211
234
  <View
212
235
  style={[
@@ -217,29 +240,7 @@ const Screen = forwardRef(
217
240
  },
218
241
  ]}
219
242
  >
220
- {useDuoGlass ? (
221
- <DuoScrollGradient
222
- animatedValue={sharedValue}
223
- gradientColor={gradientColor}
224
- headerBackground={headerBackground}
225
- heightHeader={heightHeader}
226
- extended={headerType === 'extended'}
227
- followScroll={useQuickReturn}
228
- />
229
- ) : (
230
- <HeaderExtendHeader
231
- headerType={headerType}
232
- heightHeader={heightHeader}
233
- headerRightWidth={headerRightWidth}
234
- animatedValue={sharedValue}
235
- inputSearchProps={inputSearchProps}
236
- navigation={navigation}
237
- inputSearchRef={inputSearchRef}
238
- useShadowHeader={useShadowHeader}
239
- gradientColor={gradientColor}
240
- headerBackground={headerBackground}
241
- />
242
- )}
243
+ {renderHeaderLayer()}
243
244
 
244
245
  <KeyboardAvoidingView
245
246
  style={[
@@ -259,9 +260,9 @@ const Screen = forwardRef(
259
260
  {...scrollViewProps}
260
261
  ref={screenRef}
261
262
  showsVerticalScrollIndicator={false}
262
- onScroll={handleScroll}
263
- onScrollEndDrag={handleScrollEnd}
264
- onMomentumScrollEnd={handleMomentumScrollEnd}
263
+ onScroll={scrollHandlers.handleScroll}
264
+ onScrollEndDrag={scrollHandlers.handleScrollEnd}
265
+ onMomentumScrollEnd={scrollHandlers.handleMomentumScrollEnd}
265
266
  scrollEventThrottle={16}
266
267
  style={[
267
268
  Styles.flex,
@@ -15,8 +15,8 @@ export interface SectionProps extends ViewProps {
15
15
  expanded?: boolean;
16
16
 
17
17
  /**
18
- * Optional. If `true`, margin will be applied to the Section component.
19
- * Defaults to `false` if not provided.
18
+ * Optional. If `true`, 12pt side margins are applied to the Section component.
19
+ * Defaults to `true`.
20
20
  */
21
21
  useMargin?: boolean;
22
22
 
@@ -26,6 +26,10 @@ export interface SectionProps extends ViewProps {
26
26
  backgroundImage?: string;
27
27
  }
28
28
 
29
+ /**
30
+ * Content-wide grid row (12 or 24 columns) with optional side margins; child Items size
31
+ * themselves from its GridContext.
32
+ */
29
33
  const Section: React.FC<SectionProps> = ({
30
34
  children,
31
35
  useMargin = true,
@@ -43,9 +47,8 @@ const Section: React.FC<SectionProps> = ({
43
47
  }
44
48
  const widthSection = contentWidth - margin * 2;
45
49
 
46
- const sizePerSpan =
47
- (widthSection - gutterSize * (numberOfColumns - 1)) / numberOfColumns -
48
- 1 / numberOfColumns;
50
+ const columnsWidth = widthSection - gutterSize * (numberOfColumns - 1);
51
+ const sizePerSpan = columnsWidth / numberOfColumns - 1 / numberOfColumns;
49
52
 
50
53
  const gridContext: GridContextProps = {
51
54
  numberOfColumns,
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { TrackingScopeContext } from '../Context';
3
3
 
4
+ /** Names a tracking scope; components inside prefix their component ids with it. */
4
5
  const TrackingScope = ({
5
6
  scopeName,
6
7
  children,
@@ -9,7 +9,8 @@ import { AnimatedHeader } from '../Application/types';
9
9
  type ContentInset = { insetLeft: number; insetRight: number };
10
10
 
11
11
  /**
12
- * build content for screen for grid rule
12
+ * Wraps each screen child (Fragments flattened) in a view with the screen's top spacing
13
+ * and side insets; a child with `useMargin: false` gets no top spacing.
13
14
  */
14
15
  export const renderContent = (content: any, layout: ContentInset): any => {
15
16
  const results = validateChildren(content, [undefined]);
@@ -64,7 +65,8 @@ export const renderContent = (content: any, layout: ContentInset): any => {
64
65
  };
65
66
 
66
67
  /**
67
- * render top navigation banner
68
+ * Animated banner from `animatedHeader.component`, clipped to the banner height plus
69
+ * `layoutOffset`. Nothing without a component.
68
70
  */
69
71
  export const renderAnimatedHeader = (
70
72
  animatedHeader: AnimatedHeader | undefined,
@@ -16,6 +16,10 @@ type ScreenFooterProps = {
16
16
  surfaceColor: string;
17
17
  };
18
18
 
19
+ /**
20
+ * Screen footer on the surface color with a top shadow, padded above the home indicator,
21
+ * or above the floating tab bar on a tab screen.
22
+ */
19
23
  export const ScreenFooter = ({
20
24
  footer,
21
25
  marginFooter,
@@ -26,28 +30,34 @@ export const ScreenFooter = ({
26
30
  duoTabInset,
27
31
  bottomInset,
28
32
  surfaceColor,
29
- }: ScreenFooterProps) => (
30
- <View
31
- pointerEvents={'box-none'}
32
- style={[
33
- hasContentInset && styles.footerClip,
34
- {
35
- marginLeft: insetLeft,
36
- marginRight: insetRight,
37
- },
38
- ]}
39
- >
33
+ }: ScreenFooterProps) => {
34
+ let bottomSpace = bottomInset;
35
+ if (isTab && duoTabInset) {
36
+ bottomSpace = duoTabInset;
37
+ }
38
+
39
+ return (
40
40
  <View
41
+ pointerEvents={'box-none'}
41
42
  style={[
42
- styles.shadow,
43
+ hasContentInset && styles.footerClip,
43
44
  {
44
- paddingBottom:
45
- (isTab && duoTabInset ? duoTabInset : bottomInset) + Spacing.S,
46
- backgroundColor: surfaceColor,
45
+ marginLeft: insetLeft,
46
+ marginRight: insetRight,
47
47
  },
48
48
  ]}
49
49
  >
50
- <Section useMargin={marginFooter}>{footer}</Section>
50
+ <View
51
+ style={[
52
+ styles.shadow,
53
+ {
54
+ paddingBottom: bottomSpace + Spacing.S,
55
+ backgroundColor: surfaceColor,
56
+ },
57
+ ]}
58
+ >
59
+ <Section useMargin={marginFooter}>{footer}</Section>
60
+ </View>
51
61
  </View>
52
- </View>
53
- );
62
+ );
63
+ };
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useEffect, useRef } from 'react';
2
2
  import { StatusBar, StatusBarStyle } from 'react-native';
3
3
  import { StackNavigationOptions } from '@react-navigation/stack';
4
+ import type { SharedValue } from 'react-native-reanimated';
4
5
  import Navigation from '../Application/Navigation';
5
6
  import { AnimatedHeader, SearchHeaderProps } from '../Application/types';
6
7
  import { exportHeaderTitle } from '../Application/navigationOptions';
@@ -12,6 +13,11 @@ import {
12
13
  SearchHeader,
13
14
  } from '../Application';
14
15
  import { DuoGlassBackground } from '../Application/Components/DuoGlassBackground';
16
+ import {
17
+ type HeaderContentLayout,
18
+ useHeaderFrameOptions,
19
+ useHeaderLayout,
20
+ } from '../Application/headerLayout';
15
21
  import { getHeaderMargin } from './utils';
16
22
  import { ANIMATED_BANNER_HEIGHT } from './screenTypes';
17
23
  import { HeaderType } from './types';
@@ -29,8 +35,37 @@ type ScreenHeaderParams = {
29
35
  gradientColor?: string;
30
36
  headerBackground?: string;
31
37
  layoutOffset: number;
38
+ contentLayout?: HeaderContentLayout;
39
+ };
40
+
41
+ /**
42
+ * Transparent header for an `inputSearchProps` Screen: its title fades out over the
43
+ * first 50pt of scroll while the search collapses into the bar.
44
+ */
45
+ const getSearchTitleOptions = (
46
+ sharedValue: SharedValue<number>,
47
+ ): StackNavigationOptions => {
48
+ return {
49
+ headerShown: true,
50
+ headerTransparent: true,
51
+ headerBackground: () => null,
52
+ headerTitle: (props: any) => (
53
+ <HeaderTitle
54
+ {...props}
55
+ interpolate={{
56
+ inputRange: [0, 50],
57
+ outputRange: [1, 0],
58
+ }}
59
+ animatedValue={sharedValue}
60
+ />
61
+ ),
62
+ };
32
63
  };
33
64
 
65
+ /**
66
+ * Applies the Screen's navigation header (plain, extended, animated banner or search) and
67
+ * re-applies it when those props change. Returns the search header setter and the tint.
68
+ */
34
69
  export const useScreenHeader = ({
35
70
  navigation,
36
71
  layout,
@@ -44,14 +79,14 @@ export const useScreenHeader = ({
44
79
  gradientColor,
45
80
  headerBackground,
46
81
  layoutOffset,
82
+ contentLayout,
47
83
  }: ScreenHeaderParams) => {
48
84
  const currentTint = useRef<string | undefined>(undefined);
49
85
  const appliedHeaderMode = useRef<string | undefined>(undefined);
86
+ const headerLayout = useHeaderLayout(contentLayout);
87
+ const frameOptions = useHeaderFrameOptions(contentLayout);
50
88
 
51
- /**
52
- * export options for screen
53
- * @param type
54
- */
89
+ // keepTitle leaves a title the screen set itself when the same type is re-applied
55
90
  const setHeaderType = useCallback(
56
91
  (type: HeaderType, keepTitle = false) => {
57
92
  let options: StackNavigationOptions;
@@ -69,28 +104,16 @@ export const useScreenHeader = ({
69
104
  ...quickReturnOptions,
70
105
  headerShown: true,
71
106
  headerTransparent: true,
72
- headerBackground: () =>
73
- hasSideBar ? (
74
- <DuoGlassBackground animatedValue={sharedValue} />
75
- ) : null,
107
+ headerBackground: () => {
108
+ if (!hasSideBar) {
109
+ return null;
110
+ }
111
+ return <DuoGlassBackground animatedValue={sharedValue} />;
112
+ },
76
113
  headerTitle: (props: any) => <HeaderTitle {...props} />,
77
114
  };
78
115
  if (inputSearchProps) {
79
- options = {
80
- headerShown: true,
81
- headerTransparent: true,
82
- headerBackground: () => null,
83
- headerTitle: (props: any) => (
84
- <HeaderTitle
85
- {...props}
86
- interpolate={{
87
- inputRange: [0, 50],
88
- outputRange: [1, 0],
89
- }}
90
- animatedValue={sharedValue}
91
- />
92
- ),
93
- };
116
+ options = getSearchTitleOptions(sharedValue);
94
117
  }
95
118
  break;
96
119
 
@@ -124,21 +147,7 @@ export const useScreenHeader = ({
124
147
  headerTitle: (props: any) => <HeaderTitle {...props} />,
125
148
  };
126
149
  if (inputSearchProps) {
127
- options = {
128
- headerShown: true,
129
- headerTransparent: true,
130
- headerBackground: () => null,
131
- headerTitle: (props: any) => (
132
- <HeaderTitle
133
- {...props}
134
- interpolate={{
135
- inputRange: [0, 50],
136
- outputRange: [1, 0],
137
- }}
138
- animatedValue={sharedValue}
139
- />
140
- ),
141
- };
150
+ options = getSearchTitleOptions(sharedValue);
142
151
  }
143
152
  }
144
153
 
@@ -146,7 +155,11 @@ export const useScreenHeader = ({
146
155
  delete options.headerTitle;
147
156
  }
148
157
 
149
- navigation?.instance?.setOptions({ header: undefined, ...options });
158
+ navigation?.instance?.setOptions({
159
+ header: undefined,
160
+ ...frameOptions,
161
+ ...options,
162
+ });
150
163
  },
151
164
  [
152
165
  gradientColor,
@@ -157,13 +170,11 @@ export const useScreenHeader = ({
157
170
  useShadowHeader,
158
171
  hasSideBar,
159
172
  quickReturnOptions,
173
+ frameOptions,
160
174
  ],
161
175
  );
162
176
 
163
- /**
164
- * export animatedHeader options for screen
165
- * @param nextAnimatedHeader
166
- */
177
+ // the first tint the animated header sets is kept for later scroll updates
167
178
  const setAnimatedHeader = useCallback(
168
179
  (nextAnimatedHeader: AnimatedHeader) => {
169
180
  let options: StackNavigationOptions;
@@ -175,13 +186,16 @@ export const useScreenHeader = ({
175
186
  ...quickReturnOptions,
176
187
  headerTintColor: currentTint.current ?? Colors.black_17,
177
188
  headerTransparent: true,
178
- headerBackground: (props: any) =>
179
- hasSideBar ? (
180
- <DuoGlassBackground
181
- animatedValue={sharedValue}
182
- bannerBottom={ANIMATED_BANNER_HEIGHT + layoutOffset}
183
- />
184
- ) : (
189
+ headerBackground: (props: any) => {
190
+ if (hasSideBar) {
191
+ return (
192
+ <DuoGlassBackground
193
+ animatedValue={sharedValue}
194
+ bannerBottom={ANIMATED_BANNER_HEIGHT + layoutOffset}
195
+ />
196
+ );
197
+ }
198
+ return (
185
199
  <HeaderBackground
186
200
  {...props}
187
201
  animatedValue={sharedValue}
@@ -190,7 +204,8 @@ export const useScreenHeader = ({
190
204
  headerBackground={headerBackground}
191
205
  gradientColor={gradientColor}
192
206
  />
193
- ),
207
+ );
208
+ },
194
209
  headerTitle: (props: any) => {
195
210
  return <HeaderTitle {...props} />;
196
211
  },
@@ -203,7 +218,7 @@ export const useScreenHeader = ({
203
218
  };
204
219
  }
205
220
 
206
- navigation?.instance?.setOptions(options);
221
+ navigation?.instance?.setOptions({ ...frameOptions, ...options });
207
222
  },
208
223
  [
209
224
  gradientColor,
@@ -214,47 +229,34 @@ export const useScreenHeader = ({
214
229
  hasSideBar,
215
230
  layoutOffset,
216
231
  quickReturnOptions,
232
+ frameOptions,
217
233
  ],
218
234
  );
219
235
 
220
- /**
221
- * export animatedHeader options for screen
222
- */
223
236
  const setAnimatedSearch = useCallback(() => {
224
- const options: StackNavigationOptions = {
225
- headerShown: true,
226
- headerTransparent: true,
227
- headerBackground: () => null,
228
- headerTitle: (props: any) => (
229
- <HeaderTitle
230
- {...props}
231
- interpolate={{
232
- inputRange: [0, 50],
233
- outputRange: [1, 0],
234
- }}
235
- animatedValue={sharedValue}
236
- />
237
- ),
238
- };
239
-
240
- navigation?.instance?.setOptions({ header: undefined, ...options });
241
- }, [navigation?.instance, sharedValue]);
237
+ const options = getSearchTitleOptions(sharedValue);
238
+ navigation?.instance?.setOptions({
239
+ header: undefined,
240
+ ...frameOptions,
241
+ ...options,
242
+ });
243
+ }, [navigation?.instance, sharedValue, frameOptions]);
242
244
 
243
- /**
244
- * export search header
245
- */
245
+ // the search field spans the content (or the stretch frame), clear of the side bar
246
+ // and status bar clusters
246
247
  const setSearchHeader = (params: SearchHeaderProps) => {
247
- let searchWidth = layout.width - 24;
248
+ let searchWidth = headerLayout.frameWidth;
248
249
  let searchLeft = Spacing.M;
249
250
  if (layout.sideBar) {
250
251
  searchWidth = layout.contentWidth - Spacing.M * 2;
251
252
  searchLeft = Spacing.M - getHeaderMargin(layout.width);
252
253
  }
253
- if (layout.statusBarClearance) {
254
- searchWidth -=
255
- layout.statusBarClearance.left + layout.statusBarClearance.right;
254
+ const clearance = layout.statusBarClearance;
255
+ if (clearance) {
256
+ searchWidth -= clearance.left + clearance.right;
256
257
  }
257
258
  const options: StackNavigationOptions = {
259
+ ...frameOptions,
258
260
  headerRight: undefined,
259
261
  headerTitleContainerStyle: {
260
262
  width: searchWidth,
@@ -267,8 +269,12 @@ export const useScreenHeader = ({
267
269
  margin: 0,
268
270
  left: searchLeft,
269
271
  },
270
- headerLeft: (props: any) =>
271
- params?.hiddenBack ? null : <HeaderLeft {...props} floating />,
272
+ headerLeft: (props: any) => {
273
+ if (params?.hiddenBack) {
274
+ return null;
275
+ }
276
+ return <HeaderLeft {...props} floating />;
277
+ },
272
278
  headerTitle: () => (
273
279
  <SearchHeader {...params} animatedValue={sharedValue} />
274
280
  ),
@@ -8,6 +8,10 @@ import {
8
8
  } from 'react-native-reanimated';
9
9
  import { DuoQuickReturnHeader } from '../Application/Components/DuoQuickReturnHeader';
10
10
 
11
+ /**
12
+ * iPhone Duo quick return header: it slides out while the content scrolls down and back
13
+ * on scroll up. Returns the header options and a snap to call when scrolling ends.
14
+ */
11
15
  export const useScreenQuickReturn = (
12
16
  sharedValue: SharedValue<number>,
13
17
  useQuickReturn: boolean,
@@ -33,29 +37,22 @@ export const useScreenQuickReturn = (
33
37
  [useQuickReturn, heightHeader],
34
38
  );
35
39
 
36
- const quickReturnOptions: StackNavigationOptions = useMemo(
37
- () =>
38
- useQuickReturn
39
- ? {
40
- header: (props: any) => (
41
- <DuoQuickReturnHeader {...props} offset={headerHide} />
42
- ),
43
- }
44
- : { header: undefined },
45
- [useQuickReturn, headerHide],
46
- );
40
+ const quickReturnOptions: StackNavigationOptions = useMemo(() => {
41
+ if (!useQuickReturn) {
42
+ return { header: undefined };
43
+ }
44
+ return {
45
+ header: (props: any) => (
46
+ <DuoQuickReturnHeader {...props} offset={headerHide} />
47
+ ),
48
+ };
49
+ }, [useQuickReturn, headerHide]);
47
50
 
48
- /**
49
- * a half-shown quick return header settles fully in or out
50
- */
51
+ // a half-shown quick return header settles fully in or out
51
52
  const snapQuickReturnHeader = (offsetY: number) => {
52
53
  const hide = headerHide.value;
53
- if (
54
- !useQuickReturn ||
55
- offsetY < heightHeader ||
56
- hide <= 0 ||
57
- hide >= heightHeader
58
- ) {
54
+ const isSettled = hide <= 0 || hide >= heightHeader;
55
+ if (!useQuickReturn || offsetY < heightHeader || isSettled) {
59
56
  return;
60
57
  }
61
58
  headerHide.value = withTiming(hide > heightHeader / 2 ? heightHeader : 0, {
@@ -25,6 +25,10 @@ type ScrollHandlerParams = {
25
25
  ref: any;
26
26
  };
27
27
 
28
+ /**
29
+ * Scroll handlers for Screen: feed the header animated values, retint an animated header
30
+ * past 50pt, snap the quick return header and reset a search screen scrolled under 100pt.
31
+ */
28
32
  export const createScrollHandlers = ({
29
33
  scrollable,
30
34
  animatedValue,
@@ -39,9 +43,6 @@ export const createScrollHandlers = ({
39
43
  }: ScrollHandlerParams) => {
40
44
  let handleScroll;
41
45
 
42
- /**
43
- * animated when use scroll && animated value
44
- */
45
46
  if (scrollable) {
46
47
  handleScroll = Animated.event(
47
48
  [
@@ -80,10 +81,6 @@ export const createScrollHandlers = ({
80
81
  );
81
82
  }
82
83
 
83
- /**
84
- * handle scroll end
85
- * @param e
86
- */
87
84
  const handleMomentumScrollEnd = (
88
85
  e: NativeSyntheticEvent<NativeScrollEvent>,
89
86
  ) => {