@momo-kits/foundation 0.169.7-beta.1 → 0.169.7-beta.10
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 +34 -44
- package/Application/BottomSheet.tsx +37 -40
- package/Application/BottomTab/ActiveIndicator.tsx +14 -15
- package/Application/BottomTab/BottomTabBar.styles.ts +1 -18
- package/Application/BottomTab/BottomTabBar.tsx +72 -57
- package/Application/BottomTab/BottomTabInset.tsx +4 -8
- package/Application/BottomTab/CurvedContainer.tsx +14 -22
- package/Application/BottomTab/CustomBottomTabItem.tsx +15 -15
- package/Application/BottomTab/DuoBarCompression.tsx +19 -13
- package/Application/BottomTab/DuoRailTabBar.tsx +24 -20
- package/Application/BottomTab/DuoTabBar.tsx +13 -23
- package/Application/BottomTab/TabBarIcon.tsx +29 -15
- package/Application/BottomTab/TabScreen.tsx +5 -1
- package/Application/BottomTab/index.tsx +30 -33
- package/Application/BottomTab/metrics.ts +26 -46
- package/Application/BottomTab/tabItems.ts +1 -1
- package/Application/BottomTab/usePressLift.ts +2 -2
- package/Application/Components/BackgroundImageView.tsx +0 -4
- package/Application/Components/DuoGlassBackground.tsx +4 -5
- package/Application/Components/HeaderAnimated.tsx +0 -4
- package/Application/Components/HeaderBackground.tsx +0 -4
- package/Application/Components/HeaderExtendHeader.tsx +10 -11
- package/Application/Components/HeaderLeft.tsx +1 -2
- package/Application/Components/HeaderRight.tsx +1 -2
- package/Application/Components/HeaderTitle.shared.ts +9 -11
- package/Application/Components/HeaderTitle.tsx +3 -4
- package/Application/Components/HeaderToolkitAction.tsx +20 -17
- package/Application/Components/NavigationButton.tsx +1 -2
- package/Application/Components/SearchHeader.tsx +10 -17
- 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 +32 -17
- package/Application/KitAdoption/AdoptionBadge.tsx +119 -0
- package/Application/KitAdoption/AdoptionProbe.tsx +166 -0
- package/Application/KitAdoption/AdoptionStore.ts +80 -0
- package/Application/KitAdoption/Authorship/utils.ts +137 -0
- package/Application/KitAdoption/Slots/utils.ts +68 -0
- package/Application/KitAdoption/constants.ts +120 -0
- package/Application/KitAdoption/index.ts +12 -0
- package/Application/KitAdoption/styles.ts +55 -0
- package/Application/KitAdoption/types.ts +61 -0
- package/Application/KitAdoption/utils.ts +376 -0
- package/Application/Localize.ts +0 -7
- package/Application/ModalScreen.tsx +10 -10
- package/Application/Navigation.ts +16 -14
- package/Application/NavigationContainer.tsx +18 -19
- package/Application/Navigator.ts +48 -20
- package/Application/ScaleSizeProvider.tsx +6 -13
- package/Application/StackScreen.tracking.ts +33 -11
- package/Application/StackScreen.tsx +54 -10
- package/Application/TooltipPortal.tsx +5 -12
- package/Application/WidgetContainer.tsx +4 -10
- package/Application/__tests__/KitAdoption.integration.test.tsx +253 -0
- package/Application/__tests__/KitAdoption.test.ts +283 -0
- package/Application/__tests__/KitAdoptionAuthorship.test.ts +132 -0
- package/Application/__tests__/KitAdoptionStore.test.ts +110 -0
- package/Application/__tests__/KitAdoptionStructural.test.ts +193 -0
- package/Application/__tests__/modalLayout.test.ts +128 -0
- package/Application/modalLayout.ts +14 -13
- package/Application/navigationOptions.tsx +17 -21
- package/Application/types.tabs.ts +1 -0
- package/Badge/Badge.tsx +0 -5
- package/Badge/BadgeDot.tsx +0 -1
- package/Badge/BadgeDotAnimation.tsx +7 -15
- package/Badge/BadgeRibbon.tsx +38 -46
- package/Badge/Shape.tsx +0 -2
- package/Badge/types.ts +2 -2
- package/Button/index.tsx +16 -5
- package/Button/utils.ts +15 -9
- package/CheckBox/index.tsx +0 -4
- package/Consts/theme.ts +10 -10
- package/Context/index.ts +1 -5
- package/Divider/DashDivider.tsx +0 -1
- package/Divider/index.tsx +0 -4
- package/FoundationList/index.tsx +0 -4
- package/Icon/index.tsx +6 -6
- package/Icon/types.ts +1 -1
- package/IconButton/index.tsx +12 -4
- package/Image/index.tsx +7 -9
- package/Input/Input.tsx +9 -8
- package/Input/InputDropDown.tsx +12 -10
- package/Input/InputMoney.tsx +8 -12
- package/Input/InputMoneyView.tsx +7 -14
- package/Input/InputOTP.tsx +33 -35
- package/Input/InputPhoneNumber.tsx +9 -8
- package/Input/InputSearch.tsx +6 -4
- package/Input/InputTextArea.tsx +0 -4
- package/Input/SearchTextTyping.tsx +0 -4
- package/Input/SystemTextInput.tsx +2 -7
- package/Input/TextTyping.tsx +0 -4
- package/Input/common.tsx +12 -26
- package/Input/index.tsx +20 -17
- package/Input/moneyUtils.ts +4 -10
- package/Input/styles.ts +5 -0
- package/Input/utils.ts +12 -14
- package/Layout/Card.tsx +13 -16
- package/Layout/FloatingButton.tsx +0 -4
- package/Layout/Item.tsx +2 -6
- package/Layout/ItemList.tsx +7 -9
- package/Layout/ItemSectionList.tsx +1 -2
- package/Layout/ReservedRegions.tsx +25 -25
- package/Layout/Screen.tsx +58 -56
- package/Layout/Section.tsx +13 -17
- package/Layout/TrackingScope.tsx +0 -1
- package/Layout/constants.ts +19 -0
- package/Layout/screenContent.tsx +2 -4
- package/Layout/screenFooter.tsx +18 -28
- package/Layout/screenHeader.tsx +31 -34
- package/Layout/screenQuickReturn.tsx +20 -17
- package/Layout/screenScroll.ts +7 -4
- package/Layout/screenTypes.ts +2 -3
- package/Layout/types.ts +41 -3
- package/Layout/useGridSystem.ts +49 -0
- package/Layout/utils.ts +37 -38
- package/Loader/DotLoader.tsx +0 -4
- package/Loader/Loader.tsx +0 -4
- package/Loader/ProgressBar.tsx +0 -1
- package/Loader/Spinner.tsx +0 -4
- package/Loader/utils.ts +7 -11
- package/Pagination/Dot.tsx +0 -4
- package/Pagination/PaginationDot.tsx +0 -1
- package/Pagination/PaginationNumber.tsx +3 -5
- package/Pagination/PaginationScroll.tsx +0 -5
- package/Pagination/PaginationWhiteDot.tsx +0 -1
- package/Pagination/index.tsx +0 -4
- package/Pagination/types.ts +2 -2
- package/Popup/PopupNotify.tsx +48 -44
- package/Popup/PopupPromotion.tsx +7 -4
- package/Popup/types.ts +4 -4
- package/Popup/utils.ts +0 -4
- package/Radio/index.tsx +0 -4
- package/Skeleton/index.tsx +0 -8
- package/Switch/index.tsx +0 -1
- package/Tag/index.tsx +1 -5
- package/Tag/types.ts +3 -3
- package/Text/index.tsx +3 -14
- package/Text/styles.ts +1 -1
- package/Text/types.ts +6 -1
- package/Text/utils.ts +4 -20
- package/Title/index.tsx +32 -41
- package/ViewBoundary/index.tsx +2 -4
- package/index.ts +6 -0
- package/package.json +1 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
BottomTabBarProps,
|
|
3
2
|
BottomTabNavigationOptions,
|
|
4
3
|
createBottomTabNavigator,
|
|
5
4
|
} from '@react-navigation/bottom-tabs';
|
|
@@ -14,7 +13,6 @@ import {
|
|
|
14
13
|
} from 'react-native';
|
|
15
14
|
import { Icon } from '../../Icon';
|
|
16
15
|
import { BottomTabProps } from '../types';
|
|
17
|
-
import ScaleSizeProvider from '../ScaleSizeProvider';
|
|
18
16
|
import BottomTabBar, { hasCenterAction } from './BottomTabBar';
|
|
19
17
|
import { TabBarMetrics } from './metrics';
|
|
20
18
|
import { getTabBadge } from './TabBarIcon';
|
|
@@ -25,8 +23,6 @@ import TabScreen from './TabScreen';
|
|
|
25
23
|
import { ApplicationContext } from '../../Context';
|
|
26
24
|
import { useScreenLayout } from '../../Layout';
|
|
27
25
|
|
|
28
|
-
type SceneInterpolator = BottomTabNavigationOptions['sceneStyleInterpolator'];
|
|
29
|
-
|
|
30
26
|
const Tab = createBottomTabNavigator();
|
|
31
27
|
|
|
32
28
|
// the tab screens slide in index order over 280ms, CSS `ease`
|
|
@@ -36,11 +32,12 @@ const TAB_SWITCH_SPEC = {
|
|
|
36
32
|
};
|
|
37
33
|
|
|
38
34
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
35
|
+
* scene progress is -1 for tabs before the active one, 1 for tabs after it: they wait a full
|
|
36
|
+
* width off screen on their side
|
|
41
37
|
*/
|
|
42
|
-
const getTabSlide =
|
|
43
|
-
|
|
38
|
+
const getTabSlide =
|
|
39
|
+
(width: number): BottomTabNavigationOptions['sceneStyleInterpolator'] =>
|
|
40
|
+
({ current }) => ({
|
|
44
41
|
sceneStyle: {
|
|
45
42
|
transform: [
|
|
46
43
|
{
|
|
@@ -52,7 +49,6 @@ const getTabSlide = (width: number): SceneInterpolator => {
|
|
|
52
49
|
],
|
|
53
50
|
},
|
|
54
51
|
});
|
|
55
|
-
};
|
|
56
52
|
|
|
57
53
|
/**
|
|
58
54
|
* App tab navigator: the floating bar on phones, the rail or floating bar on
|
|
@@ -71,11 +67,11 @@ const BottomTab: React.FC<BottomTabProps> = ({
|
|
|
71
67
|
const { isIPhoneDuo } = useScreenLayout();
|
|
72
68
|
const { width: windowWidth } = useWindowDimensions();
|
|
73
69
|
const barInset = useDuoTabBarInset();
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
// the center action's button rises above the bar, tab content leaves room for it too
|
|
71
|
+
const tabBarInset =
|
|
72
|
+
barInset && hasCenterAction(floatingButton, tabs.length)
|
|
73
|
+
? barInset + TabBarMetrics.ctaLift
|
|
74
|
+
: barInset;
|
|
79
75
|
const initialIndex = tabs.findIndex(i => i.name === initialRouteName);
|
|
80
76
|
const activeIndex = React.useRef(initialIndex > 0 ? initialIndex : 0);
|
|
81
77
|
|
|
@@ -87,15 +83,15 @@ const BottomTab: React.FC<BottomTabProps> = ({
|
|
|
87
83
|
});
|
|
88
84
|
}, [navigation]);
|
|
89
85
|
|
|
90
|
-
// Focus, not mount
|
|
86
|
+
// Focus, not mount: pushing a screen from a tab leaves BottomTab mounted but blurred, and
|
|
87
|
+
// onFocus only fires on tab changes.
|
|
91
88
|
useFocusEffect(
|
|
92
89
|
React.useCallback(() => {
|
|
93
90
|
NativeModules?.MiniAppModule?.setShouldUseFloatingArrowBack?.(
|
|
94
91
|
activeIndex.current !== 0,
|
|
95
92
|
);
|
|
96
|
-
return () =>
|
|
93
|
+
return () =>
|
|
97
94
|
NativeModules?.MiniAppModule?.setShouldUseFloatingArrowBack?.(false);
|
|
98
|
-
};
|
|
99
95
|
}, []),
|
|
100
96
|
);
|
|
101
97
|
|
|
@@ -132,21 +128,6 @@ const BottomTab: React.FC<BottomTabProps> = ({
|
|
|
132
128
|
},
|
|
133
129
|
};
|
|
134
130
|
|
|
135
|
-
// One cap for the whole bar (labels, CTA, custom content) so it fits the fixed bar and notch.
|
|
136
|
-
const renderTabBar = (props: BottomTabBarProps) => {
|
|
137
|
-
const TabBar = isIPhoneDuo ? DuoTabBar : BottomTabBar;
|
|
138
|
-
return (
|
|
139
|
-
<ScaleSizeProvider scaleSizeMaxRate={TabBarMetrics.labelMaxScale}>
|
|
140
|
-
<TabBar
|
|
141
|
-
{...props}
|
|
142
|
-
floatingButton={floatingButton}
|
|
143
|
-
inactiveTintColor={theme.colors.text.secondary}
|
|
144
|
-
activeTintColor={theme.colors.primary}
|
|
145
|
-
/>
|
|
146
|
-
</ScaleSizeProvider>
|
|
147
|
-
);
|
|
148
|
-
};
|
|
149
|
-
|
|
150
131
|
return (
|
|
151
132
|
<DuoBarCompressionProvider
|
|
152
133
|
behavior={compressionBehavior}
|
|
@@ -164,7 +145,23 @@ const BottomTab: React.FC<BottomTabProps> = ({
|
|
|
164
145
|
transitionSpec: TAB_SWITCH_SPEC,
|
|
165
146
|
sceneStyleInterpolator: getTabSlide(windowWidth),
|
|
166
147
|
}}
|
|
167
|
-
tabBar={
|
|
148
|
+
tabBar={props =>
|
|
149
|
+
isIPhoneDuo ? (
|
|
150
|
+
<DuoTabBar
|
|
151
|
+
{...props}
|
|
152
|
+
floatingButton={floatingButton}
|
|
153
|
+
inactiveTintColor={theme.colors.text.secondary}
|
|
154
|
+
activeTintColor={theme.colors.primary}
|
|
155
|
+
/>
|
|
156
|
+
) : (
|
|
157
|
+
<BottomTabBar
|
|
158
|
+
{...props}
|
|
159
|
+
floatingButton={floatingButton}
|
|
160
|
+
inactiveTintColor={theme.colors.text.secondary}
|
|
161
|
+
activeTintColor={theme.colors.primary}
|
|
162
|
+
/>
|
|
163
|
+
)
|
|
164
|
+
}
|
|
168
165
|
initialRouteName={initialRouteName}
|
|
169
166
|
>
|
|
170
167
|
{tabs.map((item, index) => (
|
|
@@ -3,41 +3,34 @@ import { Easing } from 'react-native-reanimated';
|
|
|
3
3
|
import { Colors, Radius, Spacing } from '../../Consts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Navigation bar measurements (Figma "Specs Navigation", DS-984), in sync with
|
|
7
|
-
* `TabBarMetrics
|
|
8
|
-
*
|
|
6
|
+
* Navigation bar measurements (Figma "Specs Navigation", DS-984), kept in sync with
|
|
7
|
+
* momo-native-kits `TabBarMetrics`
|
|
8
|
+
* - horizontal: floating container with items split evenly, center action in a notch
|
|
9
|
+
* - vertical: iPhone Duo rail, center action under the container
|
|
9
10
|
*/
|
|
10
11
|
const TabBarMetrics = {
|
|
11
|
-
height:
|
|
12
|
+
height: 68,
|
|
12
13
|
margin: Spacing.S,
|
|
13
|
-
paddingVertical: Spacing.
|
|
14
|
+
paddingVertical: Spacing.S,
|
|
14
15
|
paddingHorizontal: Spacing.M,
|
|
15
|
-
itemGap:
|
|
16
|
-
// gap between the icon slot and the label inside a tab (Figma: none)
|
|
17
|
-
itemIconLabelGap: 0,
|
|
18
|
-
// .Nav/Background: no backdrop blur, so use the Figma Blur=Off spec (opacity 95%, no blur) plus the 1px
|
|
19
|
-
// border — nearly solid, matching the frosted sample; surface keeps dark-mode support
|
|
20
|
-
backgroundOpacity: 0.95,
|
|
21
|
-
// white 60% hairline around the center action button (Figma Icon Button border)
|
|
22
|
-
ctaBorderOpacity: 0.6,
|
|
16
|
+
itemGap: Spacing.XS,
|
|
23
17
|
iconSize: 24,
|
|
24
18
|
// active indicator behind the icon: 24 icon + XS·M padding
|
|
25
19
|
indicatorPaddingVertical: Spacing.XS,
|
|
26
20
|
indicatorPaddingHorizontal: Spacing.M,
|
|
27
21
|
indicatorRadius: Radius.XL,
|
|
28
22
|
indicatorHeight: 32,
|
|
29
|
-
|
|
30
|
-
labelHeight: 18,
|
|
31
|
-
railLabelHeight: 14,
|
|
23
|
+
// home indicator / gesture bar area under the container
|
|
32
24
|
systemIndicatorHeight: 24,
|
|
25
|
+
// unfolded foldables (short side from 600, the smallest in the spec) use 65% of the short
|
|
26
|
+
// side, centered; phones and folded outer displays fill the window
|
|
33
27
|
foldableMinShortSide: 600,
|
|
34
28
|
foldableWidthRatio: 0.65,
|
|
35
|
-
// a 2-tab bar doesn't stretch to the full width: each item is this fixed width and the bar is
|
|
36
|
-
// sized to its content, centered (Figma Navigation · 2 Tab: item 100, bar ~225)
|
|
37
|
-
twoTabItemWidth: 100,
|
|
38
29
|
ctaSlotWidth: 92,
|
|
39
30
|
ctaSize: 48,
|
|
31
|
+
// the icon sits 10 inside the 48 button (prototype .cta: 28 icon), 12 inside the 52 rail one
|
|
40
32
|
ctaIconSize: 28,
|
|
33
|
+
// the button rises 22 above the container top edge
|
|
41
34
|
ctaLift: 22,
|
|
42
35
|
ctaLabelGap: Spacing.M,
|
|
43
36
|
ctaLabelHeight: 18,
|
|
@@ -58,9 +51,10 @@ const TabBarMetrics = {
|
|
|
58
51
|
};
|
|
59
52
|
|
|
60
53
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
54
|
+
* motion of the navigation bar (MoMo Navigation Prototype):
|
|
55
|
+
* - one active indicator slides to the selected item, overshooting a little
|
|
56
|
+
* - icons cross-fade and the label color follows
|
|
57
|
+
* - the center action shrinks while pressed
|
|
64
58
|
*/
|
|
65
59
|
const TabBarMotion = {
|
|
66
60
|
indicator: { duration: 550, easing: Easing.bezier(0.34, 1.4, 0.5, 1) },
|
|
@@ -91,7 +85,7 @@ const CTA_SHADOW: BoxShadowValue[] = [
|
|
|
91
85
|
];
|
|
92
86
|
|
|
93
87
|
/**
|
|
94
|
-
*
|
|
88
|
+
* inner light and tint of the active indicator: white 12% from below, primary 12% from above
|
|
95
89
|
*/
|
|
96
90
|
const getIndicatorShadow = (primary: string): BoxShadowValue[] => [
|
|
97
91
|
{
|
|
@@ -105,9 +99,9 @@ const getIndicatorShadow = (primary: string): BoxShadowValue[] => [
|
|
|
105
99
|
];
|
|
106
100
|
|
|
107
101
|
/**
|
|
108
|
-
*
|
|
102
|
+
* notch dipped into the container top edge under the center action (Figma "Backgroud" path):
|
|
109
103
|
* starts NOTCH_HALF_WIDTH left of the center, then cubic segments as [control1, control2, end],
|
|
110
|
-
* each point [dx from the center, dy below the top edge]
|
|
104
|
+
* each point [dx from the center, dy below the top edge]
|
|
111
105
|
*/
|
|
112
106
|
const NOTCH_HALF_WIDTH = 50.758;
|
|
113
107
|
const NOTCH_CURVES: [number, number][][] = [
|
|
@@ -134,25 +128,15 @@ const NOTCH_CURVES: [number, number][][] = [
|
|
|
134
128
|
];
|
|
135
129
|
|
|
136
130
|
/**
|
|
137
|
-
*
|
|
138
|
-
* window minus the side safe areas and margins
|
|
131
|
+
* width of the horizontal bar: 65% of the short side on an unfolded foldable, otherwise the
|
|
132
|
+
* window minus the side safe areas and margins
|
|
139
133
|
*/
|
|
140
134
|
const getHorizontalBarWidth = (
|
|
141
135
|
width: number,
|
|
142
136
|
height: number,
|
|
143
137
|
insetLeft: number,
|
|
144
138
|
insetRight: number,
|
|
145
|
-
itemCount: number,
|
|
146
|
-
hasCta: boolean,
|
|
147
139
|
) => {
|
|
148
|
-
// 2 tabs (no center action) stay content-sized and centered instead of filling the window
|
|
149
|
-
if (itemCount === 2 && !hasCta) {
|
|
150
|
-
return (
|
|
151
|
-
TabBarMetrics.twoTabItemWidth * 2 +
|
|
152
|
-
TabBarMetrics.paddingHorizontal * 2 +
|
|
153
|
-
TabBarMetrics.itemGap
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
140
|
const shortSide = Math.min(width, height);
|
|
157
141
|
if (shortSide >= TabBarMetrics.foldableMinShortSide) {
|
|
158
142
|
return shortSide * TabBarMetrics.foldableWidthRatio;
|
|
@@ -161,16 +145,12 @@ const getHorizontalBarWidth = (
|
|
|
161
145
|
};
|
|
162
146
|
|
|
163
147
|
/**
|
|
164
|
-
*
|
|
148
|
+
* height of the vertical rail container, plus the center action under it
|
|
165
149
|
*/
|
|
166
|
-
const getRailHeight = (tabCount: number, hasCta: boolean) =>
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
ctaHeight = TabBarMetrics.railCtaGap + TabBarMetrics.railCtaHeight;
|
|
171
|
-
}
|
|
172
|
-
return itemsHeight + TabBarMetrics.railPaddingBottom + ctaHeight;
|
|
173
|
-
};
|
|
150
|
+
const getRailHeight = (tabCount: number, hasCta: boolean) =>
|
|
151
|
+
tabCount * TabBarMetrics.railItemHeight +
|
|
152
|
+
TabBarMetrics.railPaddingBottom +
|
|
153
|
+
(hasCta ? TabBarMetrics.railCtaGap + TabBarMetrics.railCtaHeight : 0);
|
|
174
154
|
|
|
175
155
|
export {
|
|
176
156
|
TabBarMetrics,
|
|
@@ -2,7 +2,7 @@ import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
|
|
|
2
2
|
import { CommonActions, Route } from '@react-navigation/native';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* navigation items of the tab bar, with the press handlers of the system tab bar
|
|
6
6
|
*/
|
|
7
7
|
const getTabItems = ({ state, descriptors, navigation }: BottomTabBarProps) => {
|
|
8
8
|
return state.routes.map((route: Route<string>, index: number) => {
|
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
} from 'react-native-reanimated';
|
|
7
7
|
|
|
8
8
|
const SPRING = { damping: 18, stiffness: 220, mass: 0.8 };
|
|
9
|
+
// the button grows by a tenth while held
|
|
9
10
|
const PRESSED_SCALE = 1.1;
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
*
|
|
13
|
-
* unless the OS asks for reduced motion.
|
|
13
|
+
* lift for a single button, e.g. the compressed rail
|
|
14
14
|
*/
|
|
15
15
|
const usePressLift = () => {
|
|
16
16
|
const reduceMotion = useReducedMotion();
|
|
@@ -17,10 +17,6 @@ type BackgroundImageViewProps = {
|
|
|
17
17
|
animatedValue: SharedValue<number>;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
* Header surface (shadow or divider) and image, fading in over the first 52pt of
|
|
22
|
-
* scroll. iOS keeps the surface out of the clipped view so its shadow isn't cut.
|
|
23
|
-
*/
|
|
24
20
|
const BackgroundImageView: FC<BackgroundImageViewProps> = ({
|
|
25
21
|
useShadowHeader = true,
|
|
26
22
|
headerBackground = null,
|
|
@@ -30,7 +30,6 @@ const DuoGlassBackground: React.FC<{
|
|
|
30
30
|
const { headerHeight } = useScreenLayout();
|
|
31
31
|
const sharedValue = useNormalizedSharedValue(animatedValue);
|
|
32
32
|
const fadeStart = Math.max(bannerBottom - headerHeight, 0);
|
|
33
|
-
const translucentColor = theme.colors.background.surface + TRANSLUCENT_ALPHA;
|
|
34
33
|
|
|
35
34
|
const fadeStyle = useAnimatedStyle(() => ({
|
|
36
35
|
opacity: interpolate(
|
|
@@ -48,7 +47,8 @@ const DuoGlassBackground: React.FC<{
|
|
|
48
47
|
StyleSheet.absoluteFill,
|
|
49
48
|
styles.translucentBar,
|
|
50
49
|
{
|
|
51
|
-
backgroundColor:
|
|
50
|
+
backgroundColor:
|
|
51
|
+
theme.colors.background.surface + TRANSLUCENT_ALPHA,
|
|
52
52
|
borderBottomColor: theme.colors.border.default,
|
|
53
53
|
},
|
|
54
54
|
fadeStyle,
|
|
@@ -59,9 +59,8 @@ const DuoGlassBackground: React.FC<{
|
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
63
|
-
* header-high,
|
|
64
|
-
* scrolls it away with the content instead of fading it.
|
|
62
|
+
* pink header gradient drawn under the content, fading out on scroll as main's extended
|
|
63
|
+
* header does: header-high for default headers, 52 taller (collapsing) for extended
|
|
65
64
|
*/
|
|
66
65
|
const DuoScrollGradient: React.FC<{
|
|
67
66
|
animatedValue?: AnimatedCompatValue;
|
|
@@ -19,10 +19,6 @@ const ASPECT_RATIOS: Record<
|
|
|
19
19
|
'1/1': 1 / 1,
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* Parallax header image (or `children`): stretches when pulled down and fades out
|
|
24
|
-
* as the content scrolls up.
|
|
25
|
-
*/
|
|
26
22
|
const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
|
|
27
23
|
animatedValue,
|
|
28
24
|
image,
|
|
@@ -9,10 +9,6 @@ import { Image } from '../../Image';
|
|
|
9
9
|
import BackgroundImageView from './BackgroundImageView';
|
|
10
10
|
import { useNormalizedSharedValue } from '../hooks';
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* Header background: surface and image that fade in on scroll, with an optional
|
|
14
|
-
* gradient (custom or theme) on top. Outlined when `showBaseLineDebug` is on.
|
|
15
|
-
*/
|
|
16
12
|
const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
|
|
17
13
|
animatedValue,
|
|
18
14
|
useGradient = true,
|
|
@@ -23,10 +23,6 @@ const SCREEN_PADDING = 12;
|
|
|
23
23
|
const BACK_WIDTH = 28;
|
|
24
24
|
const TOOLKIT_WIDTH = 73;
|
|
25
25
|
|
|
26
|
-
/**
|
|
27
|
-
* Extended header background: a gradient that collapses on scroll and, with
|
|
28
|
-
* `inputSearchProps`, a full-width search that slides into the bar beside back/toolkit.
|
|
29
|
-
*/
|
|
30
26
|
const HeaderExtendHeader: React.FC<{
|
|
31
27
|
headerType?: HeaderType;
|
|
32
28
|
animatedValue: AnimatedCompatValue;
|
|
@@ -68,15 +64,18 @@ const HeaderExtendHeader: React.FC<{
|
|
|
68
64
|
const contentLeft = layout.insetLeft + SCREEN_PADDING;
|
|
69
65
|
const contentRight = layout.insetLeft + layout.contentWidth;
|
|
70
66
|
// collapsed on the status bar row, the search stays between its clusters
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
const collapsedRight = contentRight - clearanceRight - headerRightWidth;
|
|
67
|
+
const collapsedLeft =
|
|
68
|
+
layout.insetLeft + leftPosition + (layout.statusBarClearance?.left ?? 0);
|
|
69
|
+
const collapsedRight = contentRight - (layout.statusBarClearance?.right ?? 0);
|
|
75
70
|
const searchWidth = layout.contentWidth - SCREEN_PADDING * 2;
|
|
76
|
-
const collapsedSearchWidth =
|
|
71
|
+
const collapsedSearchWidth =
|
|
72
|
+
collapsedRight - collapsedLeft - SCREEN_PADDING - headerRightWidth;
|
|
77
73
|
// a row taller than barHeight (iPhone Duo) centers the collapsed search on the title
|
|
78
|
-
const
|
|
79
|
-
|
|
74
|
+
const collapsedRaise =
|
|
75
|
+
(layout.headerHeight -
|
|
76
|
+
layout.headerStatusBarHeight -
|
|
77
|
+
HeaderMetrics.barHeight) /
|
|
78
|
+
2;
|
|
80
79
|
|
|
81
80
|
let useShadowHeader = useShadowHeaderProp;
|
|
82
81
|
if (inputSearchProps && Platform.OS === 'android') {
|
|
@@ -10,8 +10,7 @@ import { useScreenLayout, useSideBarItemStyle } from '../../Layout/utils';
|
|
|
10
10
|
import { useSideBarCounterStyle } from './DuoQuickReturnHeader';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
* confirms in a popup first; the iPhone Duo layouts use the 48pt side bar button.
|
|
13
|
+
* default header left used for nav
|
|
15
14
|
*/
|
|
16
15
|
const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
17
16
|
tintColor,
|
|
@@ -9,8 +9,7 @@ import { HeaderToolkitAction } from './HeaderToolkitAction';
|
|
|
9
9
|
import { styles } from './HeaderRight.styles';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
* button, or the toolkit (stacked vertically in the floating iPhone Duo side bar).
|
|
12
|
+
* main component for header right
|
|
14
13
|
*/
|
|
15
14
|
const HeaderRight: React.FC<any> = props => {
|
|
16
15
|
const { translate } = useContext(ApplicationContext);
|
|
@@ -4,10 +4,6 @@ import { Spacing } from '../../Consts';
|
|
|
4
4
|
import { isWhiteListDesignSystem } from '../utils';
|
|
5
5
|
import { useScreenLayout } from '../../Layout';
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* Header title font: MoMoTrustDisplay for design-system whitelisted mini apps,
|
|
9
|
-
* SFProText otherwise.
|
|
10
|
-
*/
|
|
11
7
|
const useHeaderFontFamily = (): string => {
|
|
12
8
|
const context = useContext<any>(MiniAppContext);
|
|
13
9
|
return isWhiteListDesignSystem(context) ? 'MoMoTrustDisplay' : 'SFProText';
|
|
@@ -24,22 +20,24 @@ const HEADER_ACTIONS_WIDTH = 172;
|
|
|
24
20
|
const STATUS_BAR_ROW_ACTIONS_EXTRA = 55;
|
|
25
21
|
|
|
26
22
|
/**
|
|
27
|
-
*
|
|
28
|
-
* the bar, so the title gets the content width; otherwise it shares the bar with them.
|
|
23
|
+
* max title width, the iPhone Duo side bar takes back and toolkit out of the bar
|
|
29
24
|
*/
|
|
30
25
|
const useTitleMaxWidth = () => {
|
|
31
|
-
const
|
|
32
|
-
|
|
26
|
+
const { width, contentWidth, sideBar, statusBarClearance } =
|
|
27
|
+
useScreenLayout();
|
|
33
28
|
let clearance = 0;
|
|
34
|
-
let rowActionsExtra = 0;
|
|
35
29
|
if (statusBarClearance) {
|
|
36
30
|
clearance = statusBarClearance.left + statusBarClearance.right;
|
|
37
|
-
rowActionsExtra = STATUS_BAR_ROW_ACTIONS_EXTRA;
|
|
38
31
|
}
|
|
39
32
|
if (sideBar) {
|
|
40
33
|
return contentWidth - Spacing.M * 2 - clearance;
|
|
41
34
|
}
|
|
42
|
-
return
|
|
35
|
+
return (
|
|
36
|
+
width -
|
|
37
|
+
HEADER_ACTIONS_WIDTH -
|
|
38
|
+
(statusBarClearance ? STATUS_BAR_ROW_ACTIONS_EXTRA : 0) -
|
|
39
|
+
clearance
|
|
40
|
+
);
|
|
43
41
|
};
|
|
44
42
|
|
|
45
43
|
export { useHeaderFontFamily, HEADER_ACTIONS_WIDTH, useTitleMaxWidth };
|
|
@@ -33,8 +33,7 @@ type HeaderTitleExtraProps = {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
* scroll through `interpolate` (0 to 1 over the first 200pt by default).
|
|
36
|
+
* default header title used for nav
|
|
38
37
|
*/
|
|
39
38
|
const HeaderTitle: React.FC<
|
|
40
39
|
HeaderTitleExtraProps & { [key: string]: any }
|
|
@@ -101,8 +100,8 @@ const HeaderTitle: React.FC<
|
|
|
101
100
|
};
|
|
102
101
|
|
|
103
102
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
103
|
+
* Title custom for header
|
|
104
|
+
* @constructor
|
|
106
105
|
*/
|
|
107
106
|
const TitleCustom: React.FC<any> = ({ type, data, ...props }) => {
|
|
108
107
|
switch (type) {
|
|
@@ -14,8 +14,7 @@ const CLOSE_ICON = '16_navigation_close_circle';
|
|
|
14
14
|
const HOME_ICON = '16_basic_home';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Header toolkit
|
|
18
|
-
* `vertical` (iPhone Duo side bar) it reports its height and may fold into one button.
|
|
17
|
+
* Header toolkit action
|
|
19
18
|
*/
|
|
20
19
|
const HeaderToolkitAction: React.FC<any> = ({
|
|
21
20
|
tintColor,
|
|
@@ -28,9 +27,8 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
28
27
|
vertical = false,
|
|
29
28
|
}) => {
|
|
30
29
|
const context = useContext<any>(MiniAppContext);
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
const { expandToolbar, setToolbarHeight } = compression;
|
|
30
|
+
const { toolbarCompressed, toolbarHidden, expandToolbar, setToolbarHeight } =
|
|
31
|
+
useDuoBarCompression();
|
|
34
32
|
const { statusBarClearance } = useScreenLayout();
|
|
35
33
|
const {
|
|
36
34
|
isLoading,
|
|
@@ -58,22 +56,18 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
58
56
|
|
|
59
57
|
const useHelpCenter = context.appId !== 'vn.momo.helpcenter';
|
|
60
58
|
const closeIcon = useCloseIcon ? CLOSE_ICON : HOME_ICON;
|
|
61
|
-
let toolkitWidthStyle: ViewStyle = styles.toolkitNarrow;
|
|
62
|
-
if (useHelpCenter) {
|
|
63
|
-
toolkitWidthStyle = styles.toolkitWide;
|
|
64
|
-
}
|
|
65
59
|
|
|
66
60
|
const groupCount = useHelpCenter ? 2 : 1;
|
|
67
61
|
let capsuleHeight = DuoMetrics.controlSize;
|
|
68
62
|
if (groupCount > 1) {
|
|
69
63
|
capsuleHeight = groupCount * DuoMetrics.itemPitch;
|
|
70
64
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
// Back slot, the optional shortcut, then the help/home capsule
|
|
66
|
+
const fullToolbarHeight =
|
|
67
|
+
DuoMetrics.controlSize +
|
|
68
|
+
Spacing.M +
|
|
69
|
+
(useShortcut ? DuoMetrics.controlSize + Spacing.M : 0) +
|
|
70
|
+
capsuleHeight;
|
|
77
71
|
|
|
78
72
|
useEffect(() => {
|
|
79
73
|
if (vertical) {
|
|
@@ -86,7 +80,8 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
86
80
|
return null;
|
|
87
81
|
}
|
|
88
82
|
|
|
89
|
-
// HIG "Toolbar compressed":
|
|
83
|
+
// HIG "Toolbar compressed": tools fold into one button; tapping it expands the
|
|
84
|
+
// toolbar and compresses the tab bar instead. Not the useMore icon, to tell them apart
|
|
90
85
|
if (vertical && toolbarCompressed) {
|
|
91
86
|
return (
|
|
92
87
|
<SideBarButton
|
|
@@ -175,7 +170,15 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
175
170
|
showBadge={showBadge}
|
|
176
171
|
/>
|
|
177
172
|
)}
|
|
178
|
-
<View
|
|
173
|
+
<View
|
|
174
|
+
style={[
|
|
175
|
+
styles.toolkitContainer,
|
|
176
|
+
buttonStyle,
|
|
177
|
+
context.appId !== 'vn.momo.helpcenter'
|
|
178
|
+
? styles.toolkitWide
|
|
179
|
+
: styles.toolkitNarrow,
|
|
180
|
+
]}
|
|
181
|
+
>
|
|
179
182
|
{useHelpCenter && (
|
|
180
183
|
<>
|
|
181
184
|
<TouchableOpacity
|
|
@@ -7,8 +7,7 @@ import { Icon } from '../../Icon';
|
|
|
7
7
|
import { BadgeDot } from '../../Badge';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
* dot; switches to a dark fill under a white tint so the icon stays readable.
|
|
10
|
+
* default navigation button used header nav
|
|
12
11
|
*/
|
|
13
12
|
const NavigationButton: React.FC<NavigationButtonProps> = ({
|
|
14
13
|
icon,
|
|
@@ -10,10 +10,6 @@ import { useNormalizedSharedValue } from '../hooks';
|
|
|
10
10
|
import { useScreenLayout } from '../../Layout';
|
|
11
11
|
import { DuoMetrics } from '../../Layout/utils';
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* Header search bar whose fill turns from surface to default once scrolled, with
|
|
15
|
-
* a cancel button that goes back (or dismisses the mini app) unless `renderButtons`.
|
|
16
|
-
*/
|
|
17
13
|
const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
18
14
|
(
|
|
19
15
|
{
|
|
@@ -40,9 +36,9 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
40
36
|
if (layout.sideBar) {
|
|
41
37
|
searchMaxWidth = layout.contentWidth - Spacing.M * 2 - headerRightWidth;
|
|
42
38
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
if (layout.statusBarClearance) {
|
|
40
|
+
searchMaxWidth -=
|
|
41
|
+
layout.statusBarClearance.left + layout.statusBarClearance.right;
|
|
46
42
|
}
|
|
47
43
|
|
|
48
44
|
const backgroundStyle = useAnimatedStyle(() => {
|
|
@@ -80,15 +76,6 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
80
76
|
|
|
81
77
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
82
78
|
|
|
83
|
-
let buttons: React.ReactNode = (
|
|
84
|
-
<TouchableOpacity onPress={goBackSafe} style={styles.searchAction}>
|
|
85
|
-
<Text typography={'action_default_bold'}>{buttonText}</Text>
|
|
86
|
-
</TouchableOpacity>
|
|
87
|
-
);
|
|
88
|
-
if (typeof renderButtons === 'function') {
|
|
89
|
-
buttons = renderButtons();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
79
|
return (
|
|
93
80
|
<View
|
|
94
81
|
style={[styles.container, showBaseLineDebug && styles.debugBaseLine]}
|
|
@@ -110,7 +97,13 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
110
97
|
/>
|
|
111
98
|
</Animated.View>
|
|
112
99
|
</View>
|
|
113
|
-
{
|
|
100
|
+
{renderButtons && typeof renderButtons === 'function' ? (
|
|
101
|
+
renderButtons()
|
|
102
|
+
) : (
|
|
103
|
+
<TouchableOpacity onPress={goBackSafe} style={styles.searchAction}>
|
|
104
|
+
<Text typography={'action_default_bold'}>{buttonText}</Text>
|
|
105
|
+
</TouchableOpacity>
|
|
106
|
+
)}
|
|
114
107
|
</View>
|
|
115
108
|
);
|
|
116
109
|
},
|
|
@@ -14,8 +14,8 @@ import {
|
|
|
14
14
|
} from './HeaderTitle.shared';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Header title
|
|
18
|
-
*
|
|
17
|
+
* Header user title
|
|
18
|
+
* @constructor
|
|
19
19
|
*/
|
|
20
20
|
const TitleJourney: React.FC<TitleJourneyProps> = ({
|
|
21
21
|
start,
|
|
@@ -9,8 +9,8 @@ import { styles } from './HeaderTitle.styles';
|
|
|
9
9
|
import { useHeaderFontFamily, useTitleMaxWidth } from './HeaderTitle.shared';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Header title
|
|
13
|
-
*
|
|
12
|
+
* Header user title
|
|
13
|
+
* @constructor
|
|
14
14
|
*/
|
|
15
15
|
const TitleLocation: React.FC<TitleLocationProps> = ({
|
|
16
16
|
tintColor,
|
|
@@ -10,8 +10,8 @@ import { styles } from './HeaderTitle.styles';
|
|
|
10
10
|
import { useHeaderFontFamily, useTitleMaxWidth } from './HeaderTitle.shared';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Header
|
|
14
|
-
*
|
|
13
|
+
* Header user title
|
|
14
|
+
* @constructor
|
|
15
15
|
*/
|
|
16
16
|
const TitleUser: React.FC<TitleUserProps> = ({
|
|
17
17
|
title,
|