@momo-kits/foundation 0.169.4 → 0.169.5
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.
- package/Application/BottomSheet.hooks.ts +8 -8
- package/Application/BottomSheet.parts.tsx +44 -34
- package/Application/BottomSheet.tsx +35 -26
- package/Application/BottomTab/ActiveIndicator.tsx +15 -14
- package/Application/BottomTab/BottomTabBar.tsx +43 -34
- package/Application/BottomTab/BottomTabInset.tsx +8 -4
- package/Application/BottomTab/CurvedContainer.tsx +14 -12
- package/Application/BottomTab/CustomBottomTabItem.tsx +10 -6
- package/Application/BottomTab/DuoBarCompression.tsx +13 -19
- package/Application/BottomTab/DuoRailTabBar.tsx +13 -13
- package/Application/BottomTab/DuoTabBar.tsx +8 -9
- package/Application/BottomTab/TabBarIcon.tsx +13 -8
- package/Application/BottomTab/TabScreen.tsx +1 -5
- package/Application/BottomTab/index.tsx +38 -30
- package/Application/BottomTab/metrics.ts +21 -20
- package/Application/BottomTab/tabItems.ts +1 -1
- package/Application/BottomTab/usePressLift.ts +2 -2
- package/Application/Components/BackgroundImageView.tsx +4 -0
- package/Application/Components/DuoGlassBackground.tsx +5 -4
- package/Application/Components/HeaderAnimated.tsx +4 -0
- package/Application/Components/HeaderBackground.tsx +4 -0
- package/Application/Components/HeaderExtendHeader.tsx +11 -10
- package/Application/Components/HeaderLeft.tsx +2 -1
- package/Application/Components/HeaderRight.tsx +2 -1
- package/Application/Components/HeaderTitle.shared.ts +11 -9
- package/Application/Components/HeaderTitle.tsx +4 -3
- package/Application/Components/HeaderToolkitAction.tsx +17 -20
- package/Application/Components/NavigationButton.tsx +2 -1
- package/Application/Components/SearchHeader.tsx +17 -10
- package/Application/Components/TitleJourney.tsx +2 -2
- package/Application/Components/TitleLocation.tsx +2 -2
- package/Application/Components/TitleUser.tsx +2 -2
- package/Application/Components/useToolkitActions.tsx +17 -32
- package/Application/Localize.ts +7 -0
- package/Application/ModalScreen.tsx +9 -3
- package/Application/Navigation.ts +14 -16
- package/Application/NavigationContainer.tsx +19 -16
- package/Application/Navigator.ts +20 -48
- package/Application/StackScreen.tracking.ts +11 -26
- package/Application/StackScreen.tsx +8 -35
- package/Application/TooltipPortal.tsx +12 -5
- package/Application/WidgetContainer.tsx +10 -4
- package/Application/modalLayout.ts +11 -12
- package/Application/navigationOptions.tsx +21 -17
- package/Badge/Badge.tsx +5 -0
- package/Badge/BadgeDot.tsx +1 -0
- package/Badge/BadgeDotAnimation.tsx +15 -7
- package/Badge/BadgeRibbon.tsx +46 -38
- package/Badge/Shape.tsx +2 -0
- package/Badge/types.ts +2 -2
- package/Button/index.tsx +5 -16
- package/Button/utils.ts +9 -15
- package/CheckBox/index.tsx +4 -0
- package/Consts/theme.ts +10 -10
- package/Context/index.ts +1 -0
- package/Divider/DashDivider.tsx +1 -0
- package/Divider/index.tsx +4 -0
- package/FoundationList/index.tsx +4 -0
- package/Icon/index.tsx +6 -6
- package/Icon/types.ts +1 -1
- package/IconButton/index.tsx +4 -12
- package/Image/index.tsx +9 -7
- package/Input/Input.tsx +8 -9
- package/Input/InputDropDown.tsx +10 -12
- package/Input/InputMoney.tsx +12 -8
- package/Input/InputMoneyView.tsx +14 -7
- package/Input/InputOTP.tsx +35 -33
- package/Input/InputPhoneNumber.tsx +8 -9
- package/Input/InputSearch.tsx +4 -6
- package/Input/InputTextArea.tsx +4 -0
- package/Input/SearchTextTyping.tsx +4 -0
- package/Input/SystemTextInput.tsx +7 -2
- package/Input/TextTyping.tsx +4 -0
- package/Input/common.tsx +26 -12
- package/Input/index.tsx +17 -20
- package/Input/moneyUtils.ts +10 -4
- package/Input/styles.ts +0 -5
- package/Input/utils.ts +14 -12
- package/Layout/Card.tsx +7 -4
- package/Layout/FloatingButton.tsx +4 -0
- package/Layout/Item.tsx +6 -2
- package/Layout/ItemList.tsx +9 -7
- package/Layout/ItemSectionList.tsx +2 -1
- package/Layout/ReservedRegions.tsx +25 -25
- package/Layout/Screen.tsx +56 -58
- package/Layout/Section.tsx +8 -5
- package/Layout/TrackingScope.tsx +1 -0
- package/Layout/screenContent.tsx +4 -2
- package/Layout/screenFooter.tsx +28 -18
- package/Layout/screenHeader.tsx +34 -31
- package/Layout/screenQuickReturn.tsx +17 -20
- package/Layout/screenScroll.ts +4 -7
- package/Layout/screenTypes.ts +3 -2
- package/Layout/types.ts +2 -4
- package/Layout/utils.ts +31 -37
- package/Loader/DotLoader.tsx +4 -0
- package/Loader/Loader.tsx +4 -0
- package/Loader/ProgressBar.tsx +1 -0
- package/Loader/Spinner.tsx +4 -0
- package/Loader/utils.ts +11 -7
- package/Pagination/Dot.tsx +4 -0
- package/Pagination/PaginationDot.tsx +1 -0
- package/Pagination/PaginationNumber.tsx +5 -3
- package/Pagination/PaginationScroll.tsx +5 -0
- package/Pagination/PaginationWhiteDot.tsx +1 -0
- package/Pagination/index.tsx +4 -0
- package/Pagination/types.ts +2 -2
- package/Popup/PopupNotify.tsx +39 -39
- package/Popup/PopupPromotion.tsx +4 -7
- package/Popup/types.ts +4 -4
- package/Popup/utils.ts +4 -0
- package/Radio/index.tsx +4 -0
- package/Skeleton/index.tsx +8 -0
- package/Switch/index.tsx +1 -0
- package/Tag/index.tsx +5 -1
- package/Tag/types.ts +3 -3
- package/Text/index.tsx +14 -3
- package/Text/styles.ts +1 -1
- package/Text/types.ts +1 -6
- package/Text/utils.ts +9 -0
- package/Title/index.tsx +41 -32
- package/ViewBoundary/index.tsx +4 -2
- package/index.ts +0 -6
- package/package.json +1 -1
- package/Application/__tests__/modalLayout.test.ts +0 -128
|
@@ -19,43 +19,43 @@ try {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
* the root view so they line up with window coordinates
|
|
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
24
|
*/
|
|
25
25
|
const ReservedRegionsContext = createContext<ReservedRegion[]>([]);
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
* half folded. `undefined` while the display is flat or the system reports none
|
|
28
|
+
* Vertical seam splitting the display into left and right halves, e.g. iPhone Duo
|
|
29
|
+
* half folded. `undefined` while the display is flat or the system reports none.
|
|
30
30
|
*/
|
|
31
31
|
const HingeContext = createContext<ReservedRegion | undefined>(undefined);
|
|
32
32
|
|
|
33
|
+
/** Frame of the vertical hinge (see HingeContext), `undefined` while the display is flat. */
|
|
33
34
|
const useHinge = () => useContext(HingeContext);
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
|
-
*
|
|
37
|
+
* Provides the occlusion regions and the vertical division (hinge) reported by the native
|
|
38
|
+
* provider.
|
|
37
39
|
*/
|
|
38
40
|
const OcclusionRegions: React.FC<{
|
|
39
41
|
useRegions: NativeReservedRegions['useReservedRegions'];
|
|
40
42
|
children: ReactNode;
|
|
41
43
|
}> = ({ useRegions, children }) => {
|
|
42
44
|
const regions = useRegions();
|
|
43
|
-
const occlusion = useMemo(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
[regions],
|
|
58
|
-
);
|
|
45
|
+
const occlusion = useMemo(() => {
|
|
46
|
+
return regions
|
|
47
|
+
.filter(region => region.kind === 'occlusion')
|
|
48
|
+
.map(region => region.frame);
|
|
49
|
+
}, [regions]);
|
|
50
|
+
const hinge = useMemo(() => {
|
|
51
|
+
const division = regions.find(region => {
|
|
52
|
+
if (region.kind !== 'division') {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return region.frame.height > region.frame.width;
|
|
56
|
+
});
|
|
57
|
+
return division?.frame;
|
|
58
|
+
}, [regions]);
|
|
59
59
|
|
|
60
60
|
return (
|
|
61
61
|
<ReservedRegionsContext.Provider value={occlusion}>
|
|
@@ -65,8 +65,8 @@ const OcclusionRegions: React.FC<{
|
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
*
|
|
69
|
-
* cluster growing for a call or a Live Activity
|
|
68
|
+
* Tracks the system occlusion regions (iOS 27.1+), e.g. the iPhone Duo status bar
|
|
69
|
+
* cluster growing for a call or a Live Activity.
|
|
70
70
|
*/
|
|
71
71
|
const ReservedRegionsProvider: React.FC<{ children: ReactNode }> = ({
|
|
72
72
|
children,
|
|
@@ -75,8 +75,8 @@ const ReservedRegionsProvider: React.FC<{ children: ReactNode }> = ({
|
|
|
75
75
|
return <>{children}</>;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
const
|
|
79
|
-
|
|
78
|
+
const NativeProvider = nativeLib.ReservedRegionsProvider;
|
|
79
|
+
const { useReservedRegions } = nativeLib;
|
|
80
80
|
return (
|
|
81
81
|
<NativeProvider style={styles.container}>
|
|
82
82
|
<OcclusionRegions useRegions={useReservedRegions}>
|
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
|
{
|
|
@@ -108,14 +112,12 @@ const Screen = forwardRef(
|
|
|
108
112
|
const isIOS = Platform.OS === 'ios';
|
|
109
113
|
const bottomInset = isIOS ? Math.min(insets.bottom, 21) : insets.bottom;
|
|
110
114
|
const hasSideBar = !!layout.sideBar;
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
!animatedHeader &&
|
|
115
|
-
!inputSearchProps;
|
|
115
|
+
const isPlainHeader = headerType === 'default' || headerType === 'extended';
|
|
116
|
+
const hasCustomHeader = !!animatedHeader || !!inputSearchProps;
|
|
117
|
+
const useDuoGlass = hasSideBar && isPlainHeader && !hasCustomHeader;
|
|
116
118
|
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
+
const hasOverlayHeader = useDuoGlass || !!animatedHeader;
|
|
120
|
+
const useQuickReturn = hasSideBar && scrollable && hasOverlayHeader;
|
|
119
121
|
const { quickReturnOptions, snapQuickReturnHeader } = useScreenQuickReturn(
|
|
120
122
|
sharedValue,
|
|
121
123
|
useQuickReturn,
|
|
@@ -123,18 +125,10 @@ const Screen = forwardRef(
|
|
|
123
125
|
);
|
|
124
126
|
|
|
125
127
|
let keyboardOffset = heightHeader - bottomInset;
|
|
126
|
-
if (
|
|
127
|
-
headerType === 'extended' ||
|
|
128
|
-
animatedHeader ||
|
|
129
|
-
inputSearchProps ||
|
|
130
|
-
useDuoGlass
|
|
131
|
-
) {
|
|
128
|
+
if (headerType === 'extended' || hasCustomHeader || useDuoGlass) {
|
|
132
129
|
keyboardOffset = -bottomInset;
|
|
133
130
|
}
|
|
134
131
|
|
|
135
|
-
/**
|
|
136
|
-
* inject params for screen tracking
|
|
137
|
-
*/
|
|
138
132
|
screen?.onSetParams?.(trackingParams);
|
|
139
133
|
|
|
140
134
|
const { setSearchHeader, currentTint } = useScreenHeader({
|
|
@@ -152,9 +146,7 @@ const Screen = forwardRef(
|
|
|
152
146
|
layoutOffset,
|
|
153
147
|
});
|
|
154
148
|
|
|
155
|
-
|
|
156
|
-
* expose ref for screen
|
|
157
|
-
*/
|
|
149
|
+
// mutates the content node so callers keep its methods plus the header setters
|
|
158
150
|
useImperativeHandle(ref, () => {
|
|
159
151
|
return Object.assign(screenRef.current || {}, {
|
|
160
152
|
setOptions: (params: NavigationOptions) => {
|
|
@@ -165,19 +157,18 @@ const Screen = forwardRef(
|
|
|
165
157
|
});
|
|
166
158
|
});
|
|
167
159
|
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
});
|
|
160
|
+
const scrollHandlers = createScrollHandlers({
|
|
161
|
+
scrollable,
|
|
162
|
+
animatedValue: animatedValue.current,
|
|
163
|
+
sharedValue,
|
|
164
|
+
scrollViewProps,
|
|
165
|
+
animatedHeader,
|
|
166
|
+
inputSearchProps,
|
|
167
|
+
currentTint,
|
|
168
|
+
navigation,
|
|
169
|
+
snapQuickReturnHeader,
|
|
170
|
+
ref,
|
|
171
|
+
});
|
|
181
172
|
const Component: any = scrollable ? Animated.ScrollView : View;
|
|
182
173
|
|
|
183
174
|
const getHeight = () => {
|
|
@@ -207,6 +198,35 @@ const Screen = forwardRef(
|
|
|
207
198
|
floatingButtonBottom = 12 + duoTabInset;
|
|
208
199
|
}
|
|
209
200
|
|
|
201
|
+
const renderHeaderLayer = () => {
|
|
202
|
+
if (useDuoGlass) {
|
|
203
|
+
return (
|
|
204
|
+
<DuoScrollGradient
|
|
205
|
+
animatedValue={sharedValue}
|
|
206
|
+
gradientColor={gradientColor}
|
|
207
|
+
headerBackground={headerBackground}
|
|
208
|
+
heightHeader={heightHeader}
|
|
209
|
+
extended={headerType === 'extended'}
|
|
210
|
+
followScroll={useQuickReturn}
|
|
211
|
+
/>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
return (
|
|
215
|
+
<HeaderExtendHeader
|
|
216
|
+
headerType={headerType}
|
|
217
|
+
heightHeader={heightHeader}
|
|
218
|
+
headerRightWidth={headerRightWidth}
|
|
219
|
+
animatedValue={sharedValue}
|
|
220
|
+
inputSearchProps={inputSearchProps}
|
|
221
|
+
navigation={navigation}
|
|
222
|
+
inputSearchRef={inputSearchRef}
|
|
223
|
+
useShadowHeader={useShadowHeader}
|
|
224
|
+
gradientColor={gradientColor}
|
|
225
|
+
headerBackground={headerBackground}
|
|
226
|
+
/>
|
|
227
|
+
);
|
|
228
|
+
};
|
|
229
|
+
|
|
210
230
|
return (
|
|
211
231
|
<View
|
|
212
232
|
style={[
|
|
@@ -217,29 +237,7 @@ const Screen = forwardRef(
|
|
|
217
237
|
},
|
|
218
238
|
]}
|
|
219
239
|
>
|
|
220
|
-
{
|
|
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
|
-
)}
|
|
240
|
+
{renderHeaderLayer()}
|
|
243
241
|
|
|
244
242
|
<KeyboardAvoidingView
|
|
245
243
|
style={[
|
|
@@ -259,9 +257,9 @@ const Screen = forwardRef(
|
|
|
259
257
|
{...scrollViewProps}
|
|
260
258
|
ref={screenRef}
|
|
261
259
|
showsVerticalScrollIndicator={false}
|
|
262
|
-
onScroll={handleScroll}
|
|
263
|
-
onScrollEndDrag={handleScrollEnd}
|
|
264
|
-
onMomentumScrollEnd={handleMomentumScrollEnd}
|
|
260
|
+
onScroll={scrollHandlers.handleScroll}
|
|
261
|
+
onScrollEndDrag={scrollHandlers.handleScrollEnd}
|
|
262
|
+
onMomentumScrollEnd={scrollHandlers.handleMomentumScrollEnd}
|
|
265
263
|
scrollEventThrottle={16}
|
|
266
264
|
style={[
|
|
267
265
|
Styles.flex,
|
package/Layout/Section.tsx
CHANGED
|
@@ -15,8 +15,8 @@ export interface SectionProps extends ViewProps {
|
|
|
15
15
|
expanded?: boolean;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* Optional. If `true`,
|
|
19
|
-
* Defaults to `
|
|
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
|
|
47
|
-
|
|
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,
|
package/Layout/TrackingScope.tsx
CHANGED
package/Layout/screenContent.tsx
CHANGED
|
@@ -9,7 +9,8 @@ import { AnimatedHeader } from '../Application/types';
|
|
|
9
9
|
type ContentInset = { insetLeft: number; insetRight: number };
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
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
|
-
*
|
|
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,
|
package/Layout/screenFooter.tsx
CHANGED
|
@@ -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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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.
|
|
43
|
+
hasContentInset && styles.footerClip,
|
|
43
44
|
{
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
backgroundColor: surfaceColor,
|
|
45
|
+
marginLeft: insetLeft,
|
|
46
|
+
marginRight: insetRight,
|
|
47
47
|
},
|
|
48
48
|
]}
|
|
49
49
|
>
|
|
50
|
-
<
|
|
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
|
-
|
|
53
|
-
|
|
62
|
+
);
|
|
63
|
+
};
|
package/Layout/screenHeader.tsx
CHANGED
|
@@ -31,6 +31,10 @@ type ScreenHeaderParams = {
|
|
|
31
31
|
layoutOffset: number;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Applies the Screen's navigation header (plain, extended, animated banner or search) and
|
|
36
|
+
* re-applies it when those props change. Returns the search header setter and the tint.
|
|
37
|
+
*/
|
|
34
38
|
export const useScreenHeader = ({
|
|
35
39
|
navigation,
|
|
36
40
|
layout,
|
|
@@ -48,10 +52,7 @@ export const useScreenHeader = ({
|
|
|
48
52
|
const currentTint = useRef<string | undefined>(undefined);
|
|
49
53
|
const appliedHeaderMode = useRef<string | undefined>(undefined);
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
* export options for screen
|
|
53
|
-
* @param type
|
|
54
|
-
*/
|
|
55
|
+
// keepTitle leaves a title the screen set itself when the same type is re-applied
|
|
55
56
|
const setHeaderType = useCallback(
|
|
56
57
|
(type: HeaderType, keepTitle = false) => {
|
|
57
58
|
let options: StackNavigationOptions;
|
|
@@ -69,10 +70,12 @@ export const useScreenHeader = ({
|
|
|
69
70
|
...quickReturnOptions,
|
|
70
71
|
headerShown: true,
|
|
71
72
|
headerTransparent: true,
|
|
72
|
-
headerBackground: () =>
|
|
73
|
-
hasSideBar
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
headerBackground: () => {
|
|
74
|
+
if (!hasSideBar) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return <DuoGlassBackground animatedValue={sharedValue} />;
|
|
78
|
+
},
|
|
76
79
|
headerTitle: (props: any) => <HeaderTitle {...props} />,
|
|
77
80
|
};
|
|
78
81
|
if (inputSearchProps) {
|
|
@@ -160,10 +163,7 @@ export const useScreenHeader = ({
|
|
|
160
163
|
],
|
|
161
164
|
);
|
|
162
165
|
|
|
163
|
-
|
|
164
|
-
* export animatedHeader options for screen
|
|
165
|
-
* @param nextAnimatedHeader
|
|
166
|
-
*/
|
|
166
|
+
// the first tint the animated header sets is kept for later scroll updates
|
|
167
167
|
const setAnimatedHeader = useCallback(
|
|
168
168
|
(nextAnimatedHeader: AnimatedHeader) => {
|
|
169
169
|
let options: StackNavigationOptions;
|
|
@@ -175,13 +175,16 @@ export const useScreenHeader = ({
|
|
|
175
175
|
...quickReturnOptions,
|
|
176
176
|
headerTintColor: currentTint.current ?? Colors.black_17,
|
|
177
177
|
headerTransparent: true,
|
|
178
|
-
headerBackground: (props: any) =>
|
|
179
|
-
hasSideBar
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
178
|
+
headerBackground: (props: any) => {
|
|
179
|
+
if (hasSideBar) {
|
|
180
|
+
return (
|
|
181
|
+
<DuoGlassBackground
|
|
182
|
+
animatedValue={sharedValue}
|
|
183
|
+
bannerBottom={ANIMATED_BANNER_HEIGHT + layoutOffset}
|
|
184
|
+
/>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
return (
|
|
185
188
|
<HeaderBackground
|
|
186
189
|
{...props}
|
|
187
190
|
animatedValue={sharedValue}
|
|
@@ -190,7 +193,8 @@ export const useScreenHeader = ({
|
|
|
190
193
|
headerBackground={headerBackground}
|
|
191
194
|
gradientColor={gradientColor}
|
|
192
195
|
/>
|
|
193
|
-
)
|
|
196
|
+
);
|
|
197
|
+
},
|
|
194
198
|
headerTitle: (props: any) => {
|
|
195
199
|
return <HeaderTitle {...props} />;
|
|
196
200
|
},
|
|
@@ -217,9 +221,6 @@ export const useScreenHeader = ({
|
|
|
217
221
|
],
|
|
218
222
|
);
|
|
219
223
|
|
|
220
|
-
/**
|
|
221
|
-
* export animatedHeader options for screen
|
|
222
|
-
*/
|
|
223
224
|
const setAnimatedSearch = useCallback(() => {
|
|
224
225
|
const options: StackNavigationOptions = {
|
|
225
226
|
headerShown: true,
|
|
@@ -240,9 +241,7 @@ export const useScreenHeader = ({
|
|
|
240
241
|
navigation?.instance?.setOptions({ header: undefined, ...options });
|
|
241
242
|
}, [navigation?.instance, sharedValue]);
|
|
242
243
|
|
|
243
|
-
|
|
244
|
-
* export search header
|
|
245
|
-
*/
|
|
244
|
+
// the search field spans the content, clear of the side bar and status bar clusters
|
|
246
245
|
const setSearchHeader = (params: SearchHeaderProps) => {
|
|
247
246
|
let searchWidth = layout.width - 24;
|
|
248
247
|
let searchLeft = Spacing.M;
|
|
@@ -250,9 +249,9 @@ export const useScreenHeader = ({
|
|
|
250
249
|
searchWidth = layout.contentWidth - Spacing.M * 2;
|
|
251
250
|
searchLeft = Spacing.M - getHeaderMargin(layout.width);
|
|
252
251
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
252
|
+
const clearance = layout.statusBarClearance;
|
|
253
|
+
if (clearance) {
|
|
254
|
+
searchWidth -= clearance.left + clearance.right;
|
|
256
255
|
}
|
|
257
256
|
const options: StackNavigationOptions = {
|
|
258
257
|
headerRight: undefined,
|
|
@@ -267,8 +266,12 @@ export const useScreenHeader = ({
|
|
|
267
266
|
margin: 0,
|
|
268
267
|
left: searchLeft,
|
|
269
268
|
},
|
|
270
|
-
headerLeft: (props: any) =>
|
|
271
|
-
params?.hiddenBack
|
|
269
|
+
headerLeft: (props: any) => {
|
|
270
|
+
if (params?.hiddenBack) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
return <HeaderLeft {...props} floating />;
|
|
274
|
+
},
|
|
272
275
|
headerTitle: () => (
|
|
273
276
|
<SearchHeader {...params} animatedValue={sharedValue} />
|
|
274
277
|
),
|
|
@@ -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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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, {
|
package/Layout/screenScroll.ts
CHANGED
|
@@ -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
|
) => {
|
package/Layout/screenTypes.ts
CHANGED
|
@@ -38,7 +38,7 @@ export interface ScreenProps extends ViewProps {
|
|
|
38
38
|
headerType?: HeaderType;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Optional.
|
|
41
|
+
* Optional. Animated banner header: its component, tint and title.
|
|
42
42
|
*/
|
|
43
43
|
animatedHeader?: AnimatedHeader;
|
|
44
44
|
|
|
@@ -71,7 +71,8 @@ export interface ScreenProps extends ViewProps {
|
|
|
71
71
|
backgroundColor?: string;
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
* Optional.
|
|
74
|
+
* Optional. Shift of the content against the animated header banner bottom; negative
|
|
75
|
+
* values overlap the banner.
|
|
75
76
|
*/
|
|
76
77
|
layoutOffset?: -8 | -24 | -56 | number;
|
|
77
78
|
|
package/Layout/types.ts
CHANGED
|
@@ -4,14 +4,12 @@ export type HeaderType = 'default' | 'extended' | 'none';
|
|
|
4
4
|
|
|
5
5
|
export type ScreenRef = {
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
* @param params
|
|
7
|
+
* Applies navigation options to the screen's header.
|
|
9
8
|
*/
|
|
10
9
|
setOptions: (params: NavigationOptions) => void;
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
|
-
*
|
|
14
|
-
* @param params
|
|
12
|
+
* Replaces the header title with a search field.
|
|
15
13
|
*/
|
|
16
14
|
setSearchHeader: (params: SearchHeaderProps) => void;
|
|
17
15
|
};
|