@momo-kits/foundation 0.168.1-rn862.1 → 0.169.2
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 +195 -0
- package/Application/BottomSheet.parts.tsx +147 -0
- package/Application/BottomSheet.styles.ts +43 -0
- package/Application/BottomSheet.tsx +76 -279
- package/Application/BottomTab/ActiveIndicator.tsx +90 -0
- package/Application/BottomTab/BottomTabBar.styles.ts +65 -0
- package/Application/BottomTab/BottomTabBar.tsx +229 -276
- package/Application/BottomTab/BottomTabInset.tsx +14 -0
- package/Application/BottomTab/CurvedContainer.tsx +153 -0
- package/Application/BottomTab/CustomBottomTabItem.tsx +132 -108
- package/Application/BottomTab/DuoBarCompression.tsx +4 -12
- package/Application/BottomTab/DuoRailTabBar.tsx +227 -0
- package/Application/BottomTab/DuoTabBar.styles.ts +81 -0
- package/Application/BottomTab/DuoTabBar.tsx +29 -495
- package/Application/BottomTab/TabBarIcon.tsx +160 -72
- package/Application/BottomTab/TabScreen.tsx +164 -0
- package/Application/BottomTab/index.tsx +84 -351
- package/Application/BottomTab/metrics.ts +164 -0
- package/Application/BottomTab/tabItems.ts +46 -0
- package/Application/BottomTab/usePressLift.ts +32 -0
- package/Application/Components/DuoGlassBackground.tsx +7 -9
- package/Application/Components/HeaderAnimated.tsx +1 -1
- package/Application/Components/HeaderBackground.tsx +1 -1
- package/Application/Components/HeaderExtendHeader.tsx +17 -11
- package/Application/Components/HeaderRight.styles.ts +144 -0
- package/Application/Components/HeaderRight.tsx +6 -528
- package/Application/Components/HeaderTitle.shared.ts +43 -0
- package/Application/Components/HeaderTitle.styles.ts +125 -0
- package/Application/Components/HeaderTitle.tsx +12 -576
- package/Application/Components/HeaderToolkitAction.tsx +208 -0
- package/Application/Components/NavigationButton.tsx +9 -6
- package/Application/Components/SearchHeader.tsx +1 -1
- package/Application/Components/SwipeBackHaptic.tsx +95 -0
- package/Application/Components/TitleJourney.tsx +110 -0
- package/Application/Components/TitleLocation.tsx +73 -0
- package/Application/Components/TitleUser.tsx +304 -0
- package/Application/Components/useToolkitActions.tsx +233 -0
- package/Application/ModalIconButton.tsx +66 -0
- package/Application/ModalScreen.tsx +39 -18
- package/Application/Navigation.ts +1 -1
- package/Application/NavigationContainer.tsx +7 -8
- package/Application/StackScreen.tracking.ts +158 -0
- package/Application/StackScreen.tsx +10 -137
- package/Application/WidgetContainer.tsx +10 -3
- package/Application/__tests__/modalLayout.test.ts +128 -0
- package/Application/hooks.ts +137 -0
- package/Application/index.ts +13 -1
- package/Application/modalLayout.ts +177 -0
- package/Application/navigationOptions.tsx +217 -0
- package/Application/types.header.ts +116 -0
- package/Application/types.tabs.ts +48 -0
- package/Application/types.theme.ts +87 -0
- package/Application/types.ts +12 -244
- package/Application/utils.ts +107 -0
- package/Badge/BadgeRibbon.tsx +3 -3
- package/Badge/styles.ts +2 -0
- package/Button/index.tsx +16 -136
- package/Button/styles.ts +1 -1
- package/Button/utils.ts +139 -0
- package/CheckBox/index.tsx +1 -6
- package/Divider/DashDivider.tsx +10 -11
- package/Divider/index.tsx +18 -8
- package/Image/index.tsx +1 -46
- package/Input/Input.tsx +7 -8
- package/Input/InputDropDown.tsx +3 -4
- package/Input/InputMoney.tsx +38 -164
- package/Input/InputMoneyView.tsx +187 -0
- package/Input/InputOTP.tsx +1 -1
- package/Input/InputPhoneNumber.tsx +5 -1
- package/Input/InputSearch.tsx +2 -108
- package/Input/InputTextArea.tsx +1 -5
- package/Input/SearchTextTyping.tsx +104 -0
- package/Input/common.tsx +4 -4
- package/Input/moneyUtils.ts +17 -0
- package/Input/styles.ts +26 -2
- package/Layout/FloatingButton.tsx +22 -21
- package/Layout/ReservedRegions.tsx +21 -4
- package/Layout/Screen.tsx +74 -670
- package/Layout/Section.tsx +1 -2
- package/Layout/index.ts +10 -4
- package/Layout/layoutMetrics.ts +58 -0
- package/Layout/layoutTypes.ts +86 -0
- package/Layout/screenContent.tsx +90 -0
- package/Layout/screenFooter.tsx +53 -0
- package/Layout/screenHeader.tsx +315 -0
- package/Layout/screenQuickReturn.tsx +67 -0
- package/Layout/screenScroll.ts +110 -0
- package/Layout/screenTypes.ts +134 -0
- package/Layout/styles.ts +2 -0
- package/Layout/utils.ts +10 -144
- package/Loader/DotLoader.tsx +7 -3
- package/Loader/ProgressBar.tsx +2 -6
- package/Loader/Spinner.tsx +7 -3
- package/Loader/styles.ts +4 -0
- package/Popup/PopupNotify.tsx +70 -64
- package/Popup/PopupPromotion.tsx +5 -4
- package/Popup/types.ts +7 -0
- package/Popup/utils.ts +46 -24
- package/Radio/index.tsx +2 -10
- package/Radio/styles.ts +6 -0
- package/Skeleton/index.tsx +1 -9
- package/Skeleton/styles.ts +1 -0
- package/Tag/index.tsx +3 -3
- package/Text/index.tsx +11 -2
- package/Title/index.tsx +2 -9
- package/Title/styles.ts +6 -0
- package/package.json +21 -22
- package/Application/device.ts +0 -29
- package/Application/utils.tsx +0 -497
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import Svg, { Defs, FeGaussianBlur, Filter, Path } from 'react-native-svg';
|
|
4
|
+
import { Colors } from '../../Consts';
|
|
5
|
+
import { ApplicationContext } from '../../Context';
|
|
6
|
+
import { NOTCH_CURVES, NOTCH_HALF_WIDTH } from './metrics';
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
radius: number;
|
|
12
|
+
// x of the center action, the top edge is cut around it
|
|
13
|
+
notchCenterX?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// room for the floating shadow around the shape: the key shadow reaches 12 + 3 × 16 below it
|
|
17
|
+
const SHADOW_PADDING = 64;
|
|
18
|
+
// Figma Shadow/floating: 12% blur 24, plus 4% blur 32 spread -16 offset 12; svg blur takes
|
|
19
|
+
// half the CSS blur radius, the spread shrinks the shape the key shadow is cast from
|
|
20
|
+
const AMBIENT_BLUR = 12;
|
|
21
|
+
const KEY_BLUR = 16;
|
|
22
|
+
const KEY_SPREAD = 16;
|
|
23
|
+
const KEY_OFFSET_Y = 12;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* rounded container path placed at (`x`, `y`) on the canvas, with the notch dipped into the
|
|
27
|
+
* top edge around `notchCenterX`
|
|
28
|
+
*/
|
|
29
|
+
const getContainerPath = (
|
|
30
|
+
width: number,
|
|
31
|
+
height: number,
|
|
32
|
+
radius: number,
|
|
33
|
+
x: number,
|
|
34
|
+
y: number,
|
|
35
|
+
notchCenterX?: number,
|
|
36
|
+
) => {
|
|
37
|
+
const r = Math.max(Math.min(radius, height / 2, width / 2), 0);
|
|
38
|
+
const left = x;
|
|
39
|
+
const top = y;
|
|
40
|
+
const right = x + width;
|
|
41
|
+
const bottom = y + height;
|
|
42
|
+
let notch = '';
|
|
43
|
+
if (notchCenterX !== undefined) {
|
|
44
|
+
const cx = x + notchCenterX;
|
|
45
|
+
const point = ([dx, dy]: [number, number]) => `${cx + dx} ${top + dy}`;
|
|
46
|
+
notch =
|
|
47
|
+
` L ${cx - NOTCH_HALF_WIDTH} ${top}` +
|
|
48
|
+
NOTCH_CURVES.map(
|
|
49
|
+
([c1, c2, end]) => ` C ${point(c1)} ${point(c2)} ${point(end)}`,
|
|
50
|
+
).join('');
|
|
51
|
+
}
|
|
52
|
+
return (
|
|
53
|
+
`M ${left + r} ${top}` +
|
|
54
|
+
notch +
|
|
55
|
+
` L ${right - r} ${top}` +
|
|
56
|
+
` A ${r} ${r} 0 0 1 ${right} ${top + r}` +
|
|
57
|
+
` L ${right} ${bottom - r}` +
|
|
58
|
+
` A ${r} ${r} 0 0 1 ${right - r} ${bottom}` +
|
|
59
|
+
` L ${left + r} ${bottom}` +
|
|
60
|
+
` A ${r} ${r} 0 0 1 ${left} ${bottom - r}` +
|
|
61
|
+
` L ${left} ${top + r}` +
|
|
62
|
+
` A ${r} ${r} 0 0 1 ${left + r} ${top} Z`
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* navigation bar container: surface fill with the floating shadow, drawn as a shape so
|
|
68
|
+
* the shadow follows the notch
|
|
69
|
+
*/
|
|
70
|
+
const CurvedContainer: React.FC<Props> = ({
|
|
71
|
+
width,
|
|
72
|
+
height,
|
|
73
|
+
radius,
|
|
74
|
+
notchCenterX,
|
|
75
|
+
}) => {
|
|
76
|
+
const { theme } = useContext(ApplicationContext);
|
|
77
|
+
if (width <= 0 || height <= 0) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const canvasWidth = width + 2 * SHADOW_PADDING;
|
|
81
|
+
const canvasHeight = height + 2 * SHADOW_PADDING;
|
|
82
|
+
const path = getContainerPath(
|
|
83
|
+
width,
|
|
84
|
+
height,
|
|
85
|
+
radius,
|
|
86
|
+
SHADOW_PADDING,
|
|
87
|
+
SHADOW_PADDING,
|
|
88
|
+
notchCenterX,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<Svg
|
|
93
|
+
pointerEvents={'none'}
|
|
94
|
+
width={canvasWidth}
|
|
95
|
+
height={canvasHeight}
|
|
96
|
+
style={styles.canvas}
|
|
97
|
+
>
|
|
98
|
+
<Defs>
|
|
99
|
+
<Filter
|
|
100
|
+
id={'ambientShadow'}
|
|
101
|
+
filterUnits={'userSpaceOnUse'}
|
|
102
|
+
x={0}
|
|
103
|
+
y={0}
|
|
104
|
+
width={canvasWidth}
|
|
105
|
+
height={canvasHeight}
|
|
106
|
+
>
|
|
107
|
+
<FeGaussianBlur in={'SourceAlpha'} stdDeviation={AMBIENT_BLUR} />
|
|
108
|
+
</Filter>
|
|
109
|
+
<Filter
|
|
110
|
+
id={'keyShadow'}
|
|
111
|
+
filterUnits={'userSpaceOnUse'}
|
|
112
|
+
x={0}
|
|
113
|
+
y={0}
|
|
114
|
+
width={canvasWidth}
|
|
115
|
+
height={canvasHeight}
|
|
116
|
+
>
|
|
117
|
+
<FeGaussianBlur in={'SourceAlpha'} stdDeviation={KEY_BLUR} />
|
|
118
|
+
</Filter>
|
|
119
|
+
</Defs>
|
|
120
|
+
<Path
|
|
121
|
+
d={path}
|
|
122
|
+
fill={Colors.black_20}
|
|
123
|
+
fillOpacity={0.12}
|
|
124
|
+
filter={'url(#ambientShadow)'}
|
|
125
|
+
/>
|
|
126
|
+
{width > 2 * KEY_SPREAD && height > 2 * KEY_SPREAD && (
|
|
127
|
+
<Path
|
|
128
|
+
d={getContainerPath(
|
|
129
|
+
width - 2 * KEY_SPREAD,
|
|
130
|
+
height - 2 * KEY_SPREAD,
|
|
131
|
+
radius - KEY_SPREAD,
|
|
132
|
+
SHADOW_PADDING + KEY_SPREAD,
|
|
133
|
+
SHADOW_PADDING + KEY_SPREAD + KEY_OFFSET_Y,
|
|
134
|
+
)}
|
|
135
|
+
fill={Colors.black_20}
|
|
136
|
+
fillOpacity={0.04}
|
|
137
|
+
filter={'url(#keyShadow)'}
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
<Path d={path} fill={theme.colors.background.surface} />
|
|
141
|
+
</Svg>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const styles = StyleSheet.create({
|
|
146
|
+
canvas: {
|
|
147
|
+
position: 'absolute',
|
|
148
|
+
left: -SHADOW_PADDING,
|
|
149
|
+
top: -SHADOW_PADDING,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
export { CurvedContainer };
|
|
@@ -1,136 +1,142 @@
|
|
|
1
|
-
import React, { useContext } from 'react';
|
|
2
|
-
import { TouchableOpacity, StyleSheet } from 'react-native';
|
|
1
|
+
import React, { useContext, useEffect } from 'react';
|
|
3
2
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
LayoutChangeEvent,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
TouchableOpacity,
|
|
6
|
+
View,
|
|
7
|
+
} from 'react-native';
|
|
8
|
+
import Animated, {
|
|
9
|
+
useAnimatedStyle,
|
|
10
|
+
useSharedValue,
|
|
11
|
+
withTiming,
|
|
12
|
+
} from 'react-native-reanimated';
|
|
13
|
+
import { FixedScaleText, TabBarIcon } from './TabBarIcon';
|
|
9
14
|
import { Text } from '../../Text';
|
|
10
15
|
import { MiniAppContext } from '../../Context';
|
|
11
|
-
import { Colors } from '../../Consts';
|
|
16
|
+
import { Colors, Spacing } from '../../Consts';
|
|
17
|
+
import { TabBarMetrics, TabBarMotion } from './metrics';
|
|
12
18
|
|
|
13
|
-
type
|
|
19
|
+
type IconRenderer = (props: {
|
|
20
|
+
focused: boolean;
|
|
21
|
+
color: string;
|
|
22
|
+
size: number;
|
|
23
|
+
}) => React.ReactNode;
|
|
14
24
|
|
|
15
|
-
|
|
16
|
-
|
|
25
|
+
type TabItemContentProps = {
|
|
26
|
+
label: string;
|
|
17
27
|
focused: boolean;
|
|
18
|
-
label: LabelType;
|
|
19
|
-
onPress: () => void;
|
|
20
|
-
onLongPress: () => void;
|
|
21
28
|
activeTintColor: string;
|
|
22
29
|
inactiveTintColor: string;
|
|
23
|
-
|
|
24
|
-
inactiveBackgroundColor?: string;
|
|
25
|
-
icon: any;
|
|
30
|
+
icon?: IconRenderer;
|
|
26
31
|
badge?: string | number;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
button?: (props: BottomTabBarButtonProps) => React.ReactNode;
|
|
34
|
-
/**
|
|
35
|
-
* The accessibility label for the tab.
|
|
36
|
-
*/
|
|
37
|
-
accessibilityLabel?: string;
|
|
38
|
-
/**
|
|
39
|
-
* An unique ID for testing for the tab.
|
|
40
|
-
*/
|
|
41
|
-
testID?: string;
|
|
42
|
-
}
|
|
32
|
+
// rail item (.Nav/Duo/Item) instead of the horizontal one (.Nav/itemTab)
|
|
33
|
+
vertical?: boolean;
|
|
34
|
+
// keep the label at its design size, the Duo displays otherwise scale it up
|
|
35
|
+
fixedFontScale?: boolean;
|
|
36
|
+
hideLabel?: boolean;
|
|
37
|
+
};
|
|
43
38
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
/**
|
|
40
|
+
* icon in its indicator slot, label under it. The bar slides one shared active indicator
|
|
41
|
+
* behind the slots; the icon and label tint cross-fade on focus
|
|
42
|
+
*/
|
|
43
|
+
const TabItemContent: React.FC<TabItemContentProps> = ({
|
|
47
44
|
label,
|
|
48
|
-
|
|
49
|
-
onLongPress,
|
|
45
|
+
focused,
|
|
50
46
|
activeTintColor,
|
|
51
47
|
inactiveTintColor,
|
|
52
|
-
activeBackgroundColor,
|
|
53
|
-
inactiveBackgroundColor,
|
|
54
48
|
icon,
|
|
55
49
|
badge,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
style,
|
|
60
|
-
badgeStyle,
|
|
61
|
-
allowFontScaling,
|
|
50
|
+
vertical,
|
|
51
|
+
fixedFontScale,
|
|
52
|
+
hideLabel,
|
|
62
53
|
}) => {
|
|
63
|
-
const
|
|
64
|
-
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
54
|
+
const progress = useSharedValue(focused ? 1 : 0);
|
|
65
55
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
progress.value = withTiming(focused ? 1 : 0, TabBarMotion.tint);
|
|
58
|
+
}, [focused, progress]);
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
|
|
60
|
+
const activeLabelStyle = useAnimatedStyle(() => ({
|
|
61
|
+
opacity: progress.value,
|
|
62
|
+
}));
|
|
73
63
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
renderIcon={icon}
|
|
85
|
-
style={iconStyle}
|
|
86
|
-
/>
|
|
87
|
-
);
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const renderLabel = ({ focused }: { focused: boolean }) => {
|
|
91
|
-
if (showLabel === false) {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
64
|
+
const renderText = (color: string) => (
|
|
65
|
+
<Text
|
|
66
|
+
numberOfLines={1}
|
|
67
|
+
typography={vertical ? 'label_xs_medium' : 'label_s_medium'}
|
|
68
|
+
color={color}
|
|
69
|
+
style={!vertical && styles.label}
|
|
70
|
+
>
|
|
71
|
+
{label}
|
|
72
|
+
</Text>
|
|
73
|
+
);
|
|
94
74
|
|
|
95
|
-
|
|
75
|
+
// the active label fades in over the inactive one
|
|
76
|
+
const text = (
|
|
77
|
+
<View style={!vertical && styles.labelBox}>
|
|
78
|
+
{renderText(inactiveTintColor)}
|
|
79
|
+
<Animated.View
|
|
80
|
+
pointerEvents={'none'}
|
|
81
|
+
style={[StyleSheet.absoluteFill, activeLabelStyle]}
|
|
82
|
+
>
|
|
83
|
+
{renderText(activeTintColor)}
|
|
84
|
+
</Animated.View>
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
96
87
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
88
|
+
return (
|
|
89
|
+
<>
|
|
90
|
+
<View style={vertical ? styles.railSlot : styles.slot}>
|
|
91
|
+
<TabBarIcon
|
|
92
|
+
focused={focused}
|
|
93
|
+
badge={badge}
|
|
94
|
+
vertical={vertical}
|
|
95
|
+
activeTintColor={activeTintColor}
|
|
96
|
+
inactiveTintColor={inactiveTintColor}
|
|
97
|
+
renderIcon={icon}
|
|
98
|
+
/>
|
|
99
|
+
</View>
|
|
100
|
+
{!hideLabel &&
|
|
101
|
+
(fixedFontScale ? <FixedScaleText>{text}</FixedScaleText> : text)}
|
|
102
|
+
</>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
110
105
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
106
|
+
type CustomBottomTabItemProps = TabItemContentProps & {
|
|
107
|
+
onPress: () => void;
|
|
108
|
+
onLongPress: () => void;
|
|
109
|
+
onLayout?: (e: LayoutChangeEvent) => void;
|
|
110
|
+
accessibilityLabel?: string;
|
|
111
|
+
testID?: string;
|
|
112
|
+
};
|
|
118
113
|
|
|
119
|
-
|
|
114
|
+
/**
|
|
115
|
+
* horizontal navigation item (.Nav/itemTab): fills an even share of the container
|
|
116
|
+
*/
|
|
117
|
+
const CustomBottomTabItem: React.FC<CustomBottomTabItemProps> = ({
|
|
118
|
+
onPress,
|
|
119
|
+
onLongPress,
|
|
120
|
+
onLayout,
|
|
121
|
+
accessibilityLabel,
|
|
122
|
+
testID,
|
|
123
|
+
...content
|
|
124
|
+
}) => {
|
|
125
|
+
const context = useContext<any>(MiniAppContext);
|
|
126
|
+
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
120
127
|
|
|
121
128
|
return (
|
|
122
129
|
<TouchableOpacity
|
|
130
|
+
accessibilityRole={'tab'}
|
|
131
|
+
accessibilityLabel={accessibilityLabel ?? content.label}
|
|
132
|
+
accessibilityState={{ selected: content.focused }}
|
|
133
|
+
testID={testID}
|
|
123
134
|
onPress={onPress}
|
|
124
135
|
onLongPress={onLongPress}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
{ backgroundColor: background },
|
|
128
|
-
style,
|
|
129
|
-
showBaseLineDebug && styles.debugBaseLine,
|
|
130
|
-
]}
|
|
136
|
+
onLayout={onLayout}
|
|
137
|
+
style={[styles.container, showBaseLineDebug && styles.debugBaseLine]}
|
|
131
138
|
>
|
|
132
|
-
{
|
|
133
|
-
{renderLabel({ focused })}
|
|
139
|
+
<TabItemContent {...content} />
|
|
134
140
|
</TouchableOpacity>
|
|
135
141
|
);
|
|
136
142
|
};
|
|
@@ -138,16 +144,34 @@ const CustomBottomTabItem: React.FC<CustomBottomTabItemProps> = ({
|
|
|
138
144
|
const styles = StyleSheet.create({
|
|
139
145
|
container: {
|
|
140
146
|
flex: 1,
|
|
147
|
+
minWidth: 1,
|
|
148
|
+
alignItems: 'center',
|
|
149
|
+
justifyContent: 'center',
|
|
150
|
+
gap: Spacing.XXS,
|
|
151
|
+
},
|
|
152
|
+
slot: {
|
|
153
|
+
height: TabBarMetrics.indicatorHeight,
|
|
154
|
+
alignSelf: 'stretch',
|
|
141
155
|
alignItems: 'center',
|
|
142
156
|
justifyContent: 'center',
|
|
143
|
-
paddingVertical:
|
|
144
|
-
|
|
157
|
+
paddingVertical: TabBarMetrics.indicatorPaddingVertical,
|
|
158
|
+
paddingHorizontal: TabBarMetrics.indicatorPaddingHorizontal,
|
|
159
|
+
},
|
|
160
|
+
railSlot: {
|
|
161
|
+
height: TabBarMetrics.railIndicatorHeight,
|
|
162
|
+
alignItems: 'center',
|
|
163
|
+
justifyContent: 'center',
|
|
164
|
+
paddingVertical: TabBarMetrics.railIndicatorPaddingVertical,
|
|
165
|
+
paddingHorizontal: TabBarMetrics.indicatorPaddingHorizontal,
|
|
166
|
+
},
|
|
167
|
+
labelBox: {
|
|
168
|
+
alignSelf: 'stretch',
|
|
145
169
|
},
|
|
146
170
|
label: {
|
|
147
|
-
|
|
148
|
-
fontWeight: '600',
|
|
171
|
+
textAlign: 'center',
|
|
149
172
|
},
|
|
150
173
|
debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
|
|
151
174
|
});
|
|
152
175
|
|
|
176
|
+
export { TabItemContent };
|
|
153
177
|
export default CustomBottomTabItem;
|
|
@@ -8,6 +8,7 @@ import React, {
|
|
|
8
8
|
import { Spacing } from '../../Consts';
|
|
9
9
|
import { useScreenLayout } from '../../Layout';
|
|
10
10
|
import { DuoMetrics } from '../../Layout/utils';
|
|
11
|
+
import { getRailHeight, TabBarMetrics } from './metrics';
|
|
11
12
|
|
|
12
13
|
export type DuoBarCompressionBehavior = 'toolbar' | 'tabBar';
|
|
13
14
|
|
|
@@ -25,8 +26,6 @@ type DuoBarCompression = {
|
|
|
25
26
|
setToolbarHeight?: (height: number) => void;
|
|
26
27
|
};
|
|
27
28
|
|
|
28
|
-
const RAIL_PADDING = 6;
|
|
29
|
-
const RAIL_GAP = Spacing.XXS;
|
|
30
29
|
// space kept between the top and the bottom groups (HIG Vertical controls)
|
|
31
30
|
const GROUP_GAP = Spacing.M;
|
|
32
31
|
// Back, then the right toolkit: help + home capsule
|
|
@@ -43,15 +42,6 @@ const NO_COMPRESSION: DuoBarCompression = {
|
|
|
43
42
|
const DuoBarCompressionContext =
|
|
44
43
|
createContext<DuoBarCompression>(NO_COMPRESSION);
|
|
45
44
|
|
|
46
|
-
/**
|
|
47
|
-
* height of the vertical tab rail, plus the QR button under it
|
|
48
|
-
*/
|
|
49
|
-
const getRailHeight = (tabCount: number, hasQr: boolean) =>
|
|
50
|
-
2 * RAIL_PADDING +
|
|
51
|
-
tabCount * DuoMetrics.controlSize +
|
|
52
|
-
Math.max(tabCount - 1, 0) * RAIL_GAP +
|
|
53
|
-
(hasQr ? Spacing.M + DuoMetrics.controlSize : 0);
|
|
54
|
-
|
|
55
45
|
/**
|
|
56
46
|
* keeps the side bar's toolbar and tab bar clear of each other (HIG, Designing for
|
|
57
47
|
* iPhone Duo: Vertical controls):
|
|
@@ -81,7 +71,9 @@ const DuoBarCompressionProvider: React.FC<{
|
|
|
81
71
|
}
|
|
82
72
|
const room = height - sideBar.bottom - sideBar.top - GROUP_GAP;
|
|
83
73
|
const tabBar = getRailHeight(tabCount, hasQr);
|
|
84
|
-
const compressedTabBar =
|
|
74
|
+
const compressedTabBar =
|
|
75
|
+
DuoMetrics.controlSize +
|
|
76
|
+
(hasQr ? TabBarMetrics.railCtaGap + TabBarMetrics.railCtaHeight : 0);
|
|
85
77
|
const fits = (toolbar: number, tabs: number) => toolbar + tabs <= room;
|
|
86
78
|
|
|
87
79
|
if (fits(toolbarHeight, tabBar)) {
|