@momo-kits/foundation 0.169.5 → 0.169.6-beta.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.
- package/Application/BottomSheet.hooks.ts +8 -8
- package/Application/BottomSheet.parts.tsx +34 -44
- package/Application/BottomSheet.tsx +26 -35
- package/Application/BottomTab/ActiveIndicator.tsx +14 -15
- package/Application/BottomTab/BottomTabBar.styles.ts +18 -1
- package/Application/BottomTab/BottomTabBar.tsx +40 -63
- package/Application/BottomTab/BottomTabInset.tsx +4 -8
- package/Application/BottomTab/CurvedContainer.tsx +20 -16
- package/Application/BottomTab/CustomBottomTabItem.tsx +13 -16
- package/Application/BottomTab/DuoBarCompression.tsx +19 -13
- package/Application/BottomTab/DuoRailTabBar.tsx +13 -17
- package/Application/BottomTab/DuoTabBar.tsx +24 -12
- package/Application/BottomTab/TabBarIcon.tsx +8 -13
- package/Application/BottomTab/TabScreen.tsx +5 -1
- package/Application/BottomTab/index.tsx +30 -38
- package/Application/BottomTab/metrics.ts +46 -24
- 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/Localize.ts +0 -7
- package/Application/ModalScreen.tsx +3 -9
- package/Application/Navigation.ts +16 -14
- package/Application/NavigationContainer.tsx +16 -19
- package/Application/Navigator.ts +48 -20
- package/Application/StackScreen.tracking.ts +26 -11
- package/Application/StackScreen.tsx +35 -8
- package/Application/TooltipPortal.tsx +5 -12
- package/Application/WidgetContainer.tsx +4 -10
- 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 +0 -1
- 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 +0 -1
- 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 +4 -7
- 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 +5 -8
- package/Layout/TrackingScope.tsx +0 -1
- 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 +4 -2
- package/Layout/utils.ts +37 -31
- 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 +0 -9
- package/Title/index.tsx +32 -41
- package/ViewBoundary/index.tsx +2 -4
- package/index.ts +6 -0
- package/package.json +1 -1
|
@@ -3,27 +3,42 @@ 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: 60,
|
|
12
13
|
margin: Spacing.S,
|
|
13
|
-
paddingVertical: Spacing.
|
|
14
|
+
paddingVertical: Spacing.XS,
|
|
14
15
|
paddingHorizontal: Spacing.M,
|
|
15
|
-
itemGap:
|
|
16
|
+
itemGap: 1,
|
|
17
|
+
// gap between the icon slot and the label inside a tab (Figma: none)
|
|
18
|
+
itemIconLabelGap: 0,
|
|
19
|
+
// .Nav/Background: no backdrop blur, so use the Figma Blur=Off spec (opacity 95%, no blur) plus the 1px
|
|
20
|
+
// border — nearly solid, matching the frosted sample; surface keeps dark-mode support
|
|
21
|
+
backgroundOpacity: 0.95,
|
|
22
|
+
// white 60% hairline around the center action button (Figma Icon Button border)
|
|
23
|
+
ctaBorderOpacity: 0.6,
|
|
16
24
|
iconSize: 24,
|
|
17
25
|
// active indicator behind the icon: 24 icon + XS·M padding
|
|
18
26
|
indicatorPaddingVertical: Spacing.XS,
|
|
19
27
|
indicatorPaddingHorizontal: Spacing.M,
|
|
20
28
|
indicatorRadius: Radius.XL,
|
|
21
29
|
indicatorHeight: 32,
|
|
30
|
+
// label_s_medium line height: the label box keeps this height so a scaled label grows
|
|
31
|
+
// downward instead of pushing the icon up
|
|
32
|
+
labelHeight: 18,
|
|
22
33
|
// home indicator / gesture bar area under the container
|
|
23
34
|
systemIndicatorHeight: 24,
|
|
24
|
-
//
|
|
35
|
+
// unfolded foldables (short side from 600, the smallest in the spec) use 65% of the short
|
|
36
|
+
// side, centered; phones and folded outer displays fill the window
|
|
25
37
|
foldableMinShortSide: 600,
|
|
26
38
|
foldableWidthRatio: 0.65,
|
|
39
|
+
// a 2-tab bar doesn't stretch to the full width: each item is this fixed width and the bar is
|
|
40
|
+
// sized to its content, centered (Figma Navigation · 2 Tab: item 100, bar ~225)
|
|
41
|
+
twoTabItemWidth: 100,
|
|
27
42
|
ctaSlotWidth: 92,
|
|
28
43
|
ctaSize: 48,
|
|
29
44
|
// the icon sits 10 inside the 48 button (prototype .cta: 28 icon), 12 inside the 52 rail one
|
|
@@ -49,9 +64,10 @@ const TabBarMetrics = {
|
|
|
49
64
|
};
|
|
50
65
|
|
|
51
66
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
67
|
+
* motion of the navigation bar (MoMo Navigation Prototype):
|
|
68
|
+
* - one active indicator slides to the selected item, overshooting a little
|
|
69
|
+
* - icons cross-fade and the label color follows
|
|
70
|
+
* - the center action shrinks while pressed
|
|
55
71
|
*/
|
|
56
72
|
const TabBarMotion = {
|
|
57
73
|
indicator: { duration: 550, easing: Easing.bezier(0.34, 1.4, 0.5, 1) },
|
|
@@ -82,7 +98,7 @@ const CTA_SHADOW: BoxShadowValue[] = [
|
|
|
82
98
|
];
|
|
83
99
|
|
|
84
100
|
/**
|
|
85
|
-
*
|
|
101
|
+
* inner light and tint of the active indicator: white 12% from below, primary 12% from above
|
|
86
102
|
*/
|
|
87
103
|
const getIndicatorShadow = (primary: string): BoxShadowValue[] => [
|
|
88
104
|
{
|
|
@@ -96,9 +112,9 @@ const getIndicatorShadow = (primary: string): BoxShadowValue[] => [
|
|
|
96
112
|
];
|
|
97
113
|
|
|
98
114
|
/**
|
|
99
|
-
*
|
|
115
|
+
* notch dipped into the container top edge under the center action (Figma "Backgroud" path):
|
|
100
116
|
* starts NOTCH_HALF_WIDTH left of the center, then cubic segments as [control1, control2, end],
|
|
101
|
-
* each point [dx from the center, dy below the top edge]
|
|
117
|
+
* each point [dx from the center, dy below the top edge]
|
|
102
118
|
*/
|
|
103
119
|
const NOTCH_HALF_WIDTH = 50.758;
|
|
104
120
|
const NOTCH_CURVES: [number, number][][] = [
|
|
@@ -125,15 +141,25 @@ const NOTCH_CURVES: [number, number][][] = [
|
|
|
125
141
|
];
|
|
126
142
|
|
|
127
143
|
/**
|
|
128
|
-
*
|
|
129
|
-
* window minus the side safe areas and margins
|
|
144
|
+
* width of the horizontal bar: 65% of the short side on an unfolded foldable, otherwise the
|
|
145
|
+
* window minus the side safe areas and margins
|
|
130
146
|
*/
|
|
131
147
|
const getHorizontalBarWidth = (
|
|
132
148
|
width: number,
|
|
133
149
|
height: number,
|
|
134
150
|
insetLeft: number,
|
|
135
151
|
insetRight: number,
|
|
152
|
+
itemCount: number,
|
|
153
|
+
hasCta: boolean,
|
|
136
154
|
) => {
|
|
155
|
+
// 2 tabs (no center action) stay content-sized and centered instead of filling the window
|
|
156
|
+
if (itemCount === 2 && !hasCta) {
|
|
157
|
+
return (
|
|
158
|
+
TabBarMetrics.twoTabItemWidth * 2 +
|
|
159
|
+
TabBarMetrics.paddingHorizontal * 2 +
|
|
160
|
+
TabBarMetrics.itemGap
|
|
161
|
+
);
|
|
162
|
+
}
|
|
137
163
|
const shortSide = Math.min(width, height);
|
|
138
164
|
if (shortSide >= TabBarMetrics.foldableMinShortSide) {
|
|
139
165
|
return shortSide * TabBarMetrics.foldableWidthRatio;
|
|
@@ -142,16 +168,12 @@ const getHorizontalBarWidth = (
|
|
|
142
168
|
};
|
|
143
169
|
|
|
144
170
|
/**
|
|
145
|
-
*
|
|
171
|
+
* height of the vertical rail container, plus the center action under it
|
|
146
172
|
*/
|
|
147
|
-
const getRailHeight = (tabCount: number, hasCta: boolean) =>
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
ctaHeight = TabBarMetrics.railCtaGap + TabBarMetrics.railCtaHeight;
|
|
152
|
-
}
|
|
153
|
-
return itemsHeight + TabBarMetrics.railPaddingBottom + ctaHeight;
|
|
154
|
-
};
|
|
173
|
+
const getRailHeight = (tabCount: number, hasCta: boolean) =>
|
|
174
|
+
tabCount * TabBarMetrics.railItemHeight +
|
|
175
|
+
TabBarMetrics.railPaddingBottom +
|
|
176
|
+
(hasCta ? TabBarMetrics.railCtaGap + TabBarMetrics.railCtaHeight : 0);
|
|
155
177
|
|
|
156
178
|
export {
|
|
157
179
|
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,
|
|
@@ -15,8 +15,7 @@ type ToolkitActionsParams = {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
* with the home), single tool or "more" shortcut, help center and close.
|
|
18
|
+
* state and handlers behind the header toolkit buttons
|
|
20
19
|
*/
|
|
21
20
|
const useToolkitActions = ({
|
|
22
21
|
preventClose,
|
|
@@ -32,15 +31,20 @@ const useToolkitActions = ({
|
|
|
32
31
|
const [isFavorite, setIsFavorite] = useState<boolean>(false);
|
|
33
32
|
const [isLoading, setIsLoading] = useState(false);
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
/**
|
|
35
|
+
* check app is favorite
|
|
36
|
+
*/
|
|
37
|
+
const checkAppIsFavorite = useCallback(
|
|
38
|
+
() =>
|
|
39
|
+
navigator?.maxApi?.dispatchFunction?.(
|
|
40
|
+
'isFavoriteApp',
|
|
41
|
+
{ code: context?.code },
|
|
42
|
+
(result: boolean) => {
|
|
43
|
+
setIsFavorite(result);
|
|
44
|
+
},
|
|
45
|
+
),
|
|
46
|
+
[context?.code, navigator?.maxApi],
|
|
47
|
+
);
|
|
44
48
|
|
|
45
49
|
useEffect(() => {
|
|
46
50
|
if (useShortcut) {
|
|
@@ -71,6 +75,9 @@ const useToolkitActions = ({
|
|
|
71
75
|
}
|
|
72
76
|
};
|
|
73
77
|
|
|
78
|
+
/**
|
|
79
|
+
* close navigation container
|
|
80
|
+
*/
|
|
74
81
|
const onClose = () => {
|
|
75
82
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
76
83
|
context?.autoTracking?.({
|
|
@@ -100,6 +107,9 @@ const useToolkitActions = ({
|
|
|
100
107
|
}
|
|
101
108
|
};
|
|
102
109
|
|
|
110
|
+
/**
|
|
111
|
+
* on press shortcut
|
|
112
|
+
*/
|
|
103
113
|
const onPressShortcut = () => {
|
|
104
114
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
105
115
|
const expected = !isFavorite;
|
|
@@ -129,6 +139,9 @@ const useToolkitActions = ({
|
|
|
129
139
|
}, 1000);
|
|
130
140
|
};
|
|
131
141
|
|
|
142
|
+
/**
|
|
143
|
+
* on press help center
|
|
144
|
+
*/
|
|
132
145
|
const onPressHelpCenter = () => {
|
|
133
146
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
134
147
|
context?.autoTracking?.({
|
|
@@ -141,6 +154,9 @@ const useToolkitActions = ({
|
|
|
141
154
|
navigator?.maxApi?.dispatchFunction?.('showHelpCenter', context);
|
|
142
155
|
};
|
|
143
156
|
|
|
157
|
+
/**
|
|
158
|
+
* on press icon more
|
|
159
|
+
*/
|
|
144
160
|
const onPressIconMore = () => {
|
|
145
161
|
const currentRoute = navigator?.ref?.current?.getCurrentRoute?.();
|
|
146
162
|
context?.autoTracking?.({
|
|
@@ -184,10 +200,9 @@ const useToolkitActions = ({
|
|
|
184
200
|
iconShortcut = 'navigation_more_icon';
|
|
185
201
|
shortcutOnPress = onPressIconMore;
|
|
186
202
|
} else if (isHaveOneTool) {
|
|
187
|
-
const
|
|
203
|
+
const singleTool = tools.find(
|
|
188
204
|
(group: ToolGroup) => group?.items?.length === 1,
|
|
189
|
-
);
|
|
190
|
-
const singleTool = singleToolGroup?.items[0];
|
|
205
|
+
)?.items[0];
|
|
191
206
|
iconShortcut = singleTool?.icon;
|
|
192
207
|
shortcutOnPress = () => {
|
|
193
208
|
singleTool?.onPress?.();
|
|
@@ -201,9 +216,9 @@ const useToolkitActions = ({
|
|
|
201
216
|
};
|
|
202
217
|
}
|
|
203
218
|
|
|
204
|
-
const showBadge = tools.some((group: ToolGroup) =>
|
|
205
|
-
|
|
206
|
-
|
|
219
|
+
const showBadge = tools.some((group: ToolGroup) =>
|
|
220
|
+
group?.items?.some?.(tool => tool?.showBadge),
|
|
221
|
+
);
|
|
207
222
|
|
|
208
223
|
return {
|
|
209
224
|
isLoading,
|
package/Application/Localize.ts
CHANGED
|
@@ -2,10 +2,6 @@ import { NativeModules } from 'react-native';
|
|
|
2
2
|
import { LocalizationObject } from './types';
|
|
3
3
|
import defaultLanguage from '../Assets/language.json';
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* String table for kit and feature copy. The language is read once from the
|
|
7
|
-
* native `RNResource` module at construction; Vietnamese is the default.
|
|
8
|
-
*/
|
|
9
5
|
class Localize {
|
|
10
6
|
private assets: LocalizationObject;
|
|
11
7
|
private readonly currentLanguage: 'en' | 'vi';
|
|
@@ -29,17 +25,14 @@ class Localize {
|
|
|
29
25
|
return this.assets;
|
|
30
26
|
}
|
|
31
27
|
|
|
32
|
-
/** Text for `key` in the current language, or the key itself if missing. */
|
|
33
28
|
translate(key: string) {
|
|
34
29
|
return this.assets[this.currentLanguage]?.[key] || key;
|
|
35
30
|
}
|
|
36
31
|
|
|
37
|
-
/** Picks the current-language text of a `{ vi, en }` pair, else its JSON. */
|
|
38
32
|
translateData(data: { vi: string; en: string }) {
|
|
39
33
|
return data[this.currentLanguage] ?? JSON.stringify(data);
|
|
40
34
|
}
|
|
41
35
|
|
|
42
|
-
/** Merges extra translations; keys that already exist keep their text. */
|
|
43
36
|
addTranslations(translations: LocalizationObject) {
|
|
44
37
|
this.assets = {
|
|
45
38
|
vi: { ...translations?.vi, ...this.assets.vi },
|