@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
|
@@ -51,10 +51,8 @@ export const useBottomSheetLayout = () => {
|
|
|
51
51
|
};
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
* the whole sheet past the top; the sheet lifts by it and shrinks its maxHeight.
|
|
57
|
-
*/
|
|
54
|
+
// KeyboardAvoidingView lifts the whole sheet past the top on iPhone Duo,
|
|
55
|
+
// so lift it here and shrink maxHeight by the same amount instead
|
|
58
56
|
export const useDuoKeyboardLift = (
|
|
59
57
|
isDuo: boolean,
|
|
60
58
|
useKeyboardAvoidingView: boolean,
|
|
@@ -138,10 +136,6 @@ type SheetDismissParams = {
|
|
|
138
136
|
action: MutableRefObject<undefined | string>;
|
|
139
137
|
};
|
|
140
138
|
|
|
141
|
-
/**
|
|
142
|
-
* Close handlers of a bottom sheet. A non-forced dismiss (scrim tap, hardware
|
|
143
|
-
* back) is ignored while `barrierDismissible` is set.
|
|
144
|
-
*/
|
|
145
139
|
export const useSheetDismiss = ({
|
|
146
140
|
barrierDismissible,
|
|
147
141
|
closeAnimation,
|
|
@@ -149,6 +143,9 @@ export const useSheetDismiss = ({
|
|
|
149
143
|
leftOptions,
|
|
150
144
|
action,
|
|
151
145
|
}: SheetDismissParams) => {
|
|
146
|
+
/**
|
|
147
|
+
* handler dismiss
|
|
148
|
+
*/
|
|
152
149
|
const onDismiss = useCallback(
|
|
153
150
|
(force = false, callback?: () => void) => {
|
|
154
151
|
if (barrierDismissible && !force) {
|
|
@@ -164,6 +161,9 @@ export const useSheetDismiss = ({
|
|
|
164
161
|
[barrierDismissible, closeAnimation, navigator],
|
|
165
162
|
);
|
|
166
163
|
|
|
164
|
+
/**
|
|
165
|
+
* on request close
|
|
166
|
+
*/
|
|
167
167
|
const onRequestClose = useCallback(
|
|
168
168
|
(callback?: () => void) => {
|
|
169
169
|
onDismiss(true, callback);
|
|
@@ -34,52 +34,42 @@ export const renderSheetHeader = ({
|
|
|
34
34
|
iconBack,
|
|
35
35
|
options,
|
|
36
36
|
leftOptions,
|
|
37
|
-
}: SheetHeaderParams) =>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
>
|
|
55
|
-
{options.title ?? ''}
|
|
56
|
-
</Text>
|
|
57
|
-
</>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<View {...panHandlers}>
|
|
63
|
-
<View style={{ height: INDICATOR_HEIGHT }}>
|
|
64
|
-
<View style={styles.indicator} />
|
|
65
|
-
</View>
|
|
66
|
-
<View
|
|
67
|
-
style={[
|
|
68
|
-
styles.header,
|
|
69
|
-
useDivider && styles.headerDivider,
|
|
70
|
-
useDivider && {
|
|
71
|
-
borderBottomColor: theme.colors.border.default,
|
|
72
|
-
},
|
|
73
|
-
]}
|
|
74
|
-
>
|
|
75
|
-
{backButton}
|
|
37
|
+
}: SheetHeaderParams) => (
|
|
38
|
+
<View {...panHandlers}>
|
|
39
|
+
<View style={{ height: INDICATOR_HEIGHT }}>
|
|
40
|
+
<View style={styles.indicator} />
|
|
41
|
+
</View>
|
|
42
|
+
<View
|
|
43
|
+
style={[
|
|
44
|
+
styles.header,
|
|
45
|
+
useDivider && styles.headerDivider,
|
|
46
|
+
useDivider && {
|
|
47
|
+
borderBottomColor: theme.colors.border.default,
|
|
48
|
+
},
|
|
49
|
+
]}
|
|
50
|
+
>
|
|
51
|
+
{showBack ? (
|
|
52
|
+
<ModalIconButton icon={iconBack} onPress={onPressBack} />
|
|
53
|
+
) : null}
|
|
76
54
|
|
|
77
|
-
|
|
78
|
-
<
|
|
79
|
-
|
|
55
|
+
{options.header ? (
|
|
56
|
+
<View style={Styles.flex}>{options.header}</View>
|
|
57
|
+
) : (
|
|
58
|
+
<>
|
|
59
|
+
{!leftOptions ? <View style={styles.iconButtonSpace} /> : null}
|
|
60
|
+
<Text
|
|
61
|
+
style={[Styles.flex, styles.title]}
|
|
62
|
+
numberOfLines={1}
|
|
63
|
+
typography="header_default_bold"
|
|
64
|
+
>
|
|
65
|
+
{options.title ?? ''}
|
|
66
|
+
</Text>
|
|
67
|
+
</>
|
|
68
|
+
)}
|
|
69
|
+
<ModalIconButton icon="navigation_close" onPress={onPressClose} />
|
|
80
70
|
</View>
|
|
81
|
-
|
|
82
|
-
|
|
71
|
+
</View>
|
|
72
|
+
);
|
|
83
73
|
|
|
84
74
|
/**
|
|
85
75
|
* Footer pinned under the sheet content, `null` when the sheet has none.
|
|
@@ -18,6 +18,7 @@ import Animated, {
|
|
|
18
18
|
} from 'react-native-reanimated';
|
|
19
19
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
20
20
|
import { DUO_ORIENTATIONS, styles } from './BottomSheet.styles';
|
|
21
|
+
import { AdoptionProbe } from './KitAdoption';
|
|
21
22
|
import {
|
|
22
23
|
useBottomSheetLayout,
|
|
23
24
|
useDuoKeyboardLift,
|
|
@@ -30,10 +31,6 @@ import {
|
|
|
30
31
|
renderSheetHeader,
|
|
31
32
|
} from './BottomSheet.parts';
|
|
32
33
|
|
|
33
|
-
/**
|
|
34
|
-
* Bottom sheet route of the modal stack: slides the given screen up under a
|
|
35
|
-
* drag handle and header, and reports how it was closed through `onDismiss`.
|
|
36
|
-
*/
|
|
37
34
|
const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
38
35
|
const { theme, navigator } = useContext(ApplicationContext);
|
|
39
36
|
const context = useContext<any>(MiniAppContext);
|
|
@@ -102,9 +99,8 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
102
99
|
const panResponder = useRef(
|
|
103
100
|
PanResponder.create({
|
|
104
101
|
onStartShouldSetPanResponder: () => draggable,
|
|
105
|
-
onMoveShouldSetPanResponder: (_, gestureState) =>
|
|
106
|
-
|
|
107
|
-
},
|
|
102
|
+
onMoveShouldSetPanResponder: (_, gestureState) =>
|
|
103
|
+
draggable && gestureState.dy > 0,
|
|
108
104
|
onPanResponderMove: (_, gestureState) => {
|
|
109
105
|
if (gestureState.dy > 0) {
|
|
110
106
|
translateY.value = gestureState.dy;
|
|
@@ -136,7 +132,10 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
136
132
|
backgroundColor = theme.colors.background.surface;
|
|
137
133
|
}
|
|
138
134
|
|
|
139
|
-
|
|
135
|
+
/**
|
|
136
|
+
* emit dismiss event, once: folding or rotating iPhone Duo changes the
|
|
137
|
+
* window height while the sheet stays open
|
|
138
|
+
*/
|
|
140
139
|
useEffect(() => {
|
|
141
140
|
openAnimation();
|
|
142
141
|
return () => {
|
|
@@ -144,14 +143,14 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
144
143
|
};
|
|
145
144
|
}, [openAnimation]);
|
|
146
145
|
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
146
|
+
const { onDismiss, onRequestClose, onPressBack, onPressClose } =
|
|
147
|
+
useSheetDismiss({
|
|
148
|
+
barrierDismissible,
|
|
149
|
+
closeAnimation,
|
|
150
|
+
navigator,
|
|
151
|
+
leftOptions,
|
|
152
|
+
action,
|
|
153
|
+
});
|
|
155
154
|
|
|
156
155
|
const showBack = !!leftOptions?.onPressIconLeft;
|
|
157
156
|
const iconBack = leftOptions?.iconLeft ?? 'ic_back';
|
|
@@ -175,31 +174,18 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
175
174
|
);
|
|
176
175
|
|
|
177
176
|
const screen = (
|
|
178
|
-
<
|
|
179
|
-
{
|
|
180
|
-
{
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
<AdoptionProbe
|
|
178
|
+
screenName={Screen?.name || 'Invalid'}
|
|
179
|
+
navigation={props.navigation}
|
|
180
|
+
>
|
|
181
|
+
<Screen
|
|
182
|
+
{...props}
|
|
183
|
+
{...props.route.params}
|
|
184
|
+
onRequestClose={onRequestClose}
|
|
185
|
+
/>
|
|
186
|
+
</AdoptionProbe>
|
|
183
187
|
);
|
|
184
188
|
|
|
185
|
-
let content: React.ReactNode;
|
|
186
|
-
if (isFoldable) {
|
|
187
|
-
// Content shrinks so a tall sheet keeps its header and footer.
|
|
188
|
-
content = (
|
|
189
|
-
<View style={styles.shrink}>
|
|
190
|
-
{renderSheetHeader(headerParams)}
|
|
191
|
-
<View style={styles.shrink}>{screen}</View>
|
|
192
|
-
</View>
|
|
193
|
-
);
|
|
194
|
-
} else {
|
|
195
|
-
content = (
|
|
196
|
-
<>
|
|
197
|
-
{renderSheetHeader(headerParams)}
|
|
198
|
-
{screen}
|
|
199
|
-
</>
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
189
|
return (
|
|
204
190
|
<Container
|
|
205
191
|
transparent
|
|
@@ -245,7 +231,18 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
245
231
|
}),
|
|
246
232
|
]}
|
|
247
233
|
>
|
|
248
|
-
{
|
|
234
|
+
{isFoldable ? (
|
|
235
|
+
// content shrinks so a tall sheet keeps its header and footer
|
|
236
|
+
<View style={styles.shrink}>
|
|
237
|
+
{renderSheetHeader(headerParams)}
|
|
238
|
+
<View style={styles.shrink}>{screen}</View>
|
|
239
|
+
</View>
|
|
240
|
+
) : (
|
|
241
|
+
<>
|
|
242
|
+
{renderSheetHeader(headerParams)}
|
|
243
|
+
{screen}
|
|
244
|
+
</>
|
|
245
|
+
)}
|
|
249
246
|
{renderSheetFooter({ footerComponent, theme })}
|
|
250
247
|
{useBottomInset && (
|
|
251
248
|
<View
|
|
@@ -19,8 +19,8 @@ type Props = {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* one active indicator per bar (Background/tonal with its inner shadows) sliding to the
|
|
23
|
+
* selected item; it lands in place on the first measure and slides from then on
|
|
24
24
|
*/
|
|
25
25
|
const ActiveIndicator: React.FC<Props> = ({
|
|
26
26
|
axis,
|
|
@@ -49,19 +49,18 @@ const ActiveIndicator: React.FC<Props> = ({
|
|
|
49
49
|
position.value = withTiming(offset, TabBarMotion.indicator);
|
|
50
50
|
}, [offset, extent, position, size, visible]);
|
|
51
51
|
|
|
52
|
-
const animatedStyle = useAnimatedStyle(() =>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
52
|
+
const animatedStyle = useAnimatedStyle(() =>
|
|
53
|
+
axis === 'x'
|
|
54
|
+
? {
|
|
55
|
+
opacity: visible.value,
|
|
56
|
+
width: size.value,
|
|
57
|
+
transform: [{ translateX: position.value }],
|
|
58
|
+
}
|
|
59
|
+
: {
|
|
60
|
+
opacity: visible.value,
|
|
61
|
+
transform: [{ translateY: position.value }],
|
|
62
|
+
},
|
|
63
|
+
);
|
|
65
64
|
|
|
66
65
|
return (
|
|
67
66
|
<Animated.View
|
|
@@ -2,20 +2,6 @@ import { StyleSheet } from 'react-native';
|
|
|
2
2
|
import { Colors, Spacing } from '../../Consts';
|
|
3
3
|
import { CTA_SHADOW, TabBarMetrics } from './metrics';
|
|
4
4
|
|
|
5
|
-
const ITEM_HEIGHT =
|
|
6
|
-
TabBarMetrics.indicatorHeight +
|
|
7
|
-
TabBarMetrics.itemIconLabelGap +
|
|
8
|
-
TabBarMetrics.labelHeight;
|
|
9
|
-
const CONTENT_HEIGHT = TabBarMetrics.height - 2 * TabBarMetrics.paddingVertical;
|
|
10
|
-
// the items are centered in the content, so their label starts this far below the content top
|
|
11
|
-
const ITEM_LABEL_TOP =
|
|
12
|
-
(CONTENT_HEIGHT - ITEM_HEIGHT) / 2 +
|
|
13
|
-
TabBarMetrics.indicatorHeight +
|
|
14
|
-
TabBarMetrics.itemIconLabelGap;
|
|
15
|
-
// the center action column is placed so its label starts on the same line as the tab labels
|
|
16
|
-
const CTA_TOP =
|
|
17
|
-
ITEM_LABEL_TOP - TabBarMetrics.ctaLabelGap - TabBarMetrics.ctaSize;
|
|
18
|
-
|
|
19
5
|
export const styles = StyleSheet.create({
|
|
20
6
|
bar: {
|
|
21
7
|
position: 'absolute',
|
|
@@ -51,7 +37,7 @@ export const styles = StyleSheet.create({
|
|
|
51
37
|
position: 'absolute',
|
|
52
38
|
left: 0,
|
|
53
39
|
right: 0,
|
|
54
|
-
top:
|
|
40
|
+
top: -TabBarMetrics.ctaLift - TabBarMetrics.paddingVertical,
|
|
55
41
|
alignItems: 'center',
|
|
56
42
|
gap: TabBarMetrics.ctaLabelGap,
|
|
57
43
|
},
|
|
@@ -60,9 +46,6 @@ export const styles = StyleSheet.create({
|
|
|
60
46
|
width: TabBarMetrics.ctaSize,
|
|
61
47
|
height: TabBarMetrics.ctaSize,
|
|
62
48
|
borderRadius: TabBarMetrics.ctaSize / 2,
|
|
63
|
-
// Figma Icon Button border: white 60% hairline
|
|
64
|
-
borderWidth: 1,
|
|
65
|
-
borderColor: `rgba(255, 255, 255, ${TabBarMetrics.ctaBorderOpacity})`,
|
|
66
49
|
alignItems: 'center',
|
|
67
50
|
justifyContent: 'center',
|
|
68
51
|
},
|
|
@@ -17,6 +17,7 @@ import { CurvedContainer } from './CurvedContainer';
|
|
|
17
17
|
import { ActiveIndicator } from './ActiveIndicator';
|
|
18
18
|
import { getHorizontalBarWidth, TabBarMetrics, TabBarMotion } from './metrics';
|
|
19
19
|
import { getTabItems, TabItem } from './tabItems';
|
|
20
|
+
import { FixedScaleText } from './TabBarIcon';
|
|
20
21
|
import { styles } from './BottomTabBar.styles';
|
|
21
22
|
|
|
22
23
|
type Props = BottomTabBarProps & {
|
|
@@ -26,28 +27,24 @@ type Props = BottomTabBarProps & {
|
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
|
-
*
|
|
30
|
-
* Special type takes 2 or 4 tabs.
|
|
30
|
+
* the center action needs a tab on each side: Special type takes 2 or 4 tabs
|
|
31
31
|
*/
|
|
32
|
-
const hasCenterAction = (floatingButton: unknown, tabCount: number) =>
|
|
33
|
-
|
|
34
|
-
};
|
|
32
|
+
const hasCenterAction = (floatingButton: unknown, tabCount: number) =>
|
|
33
|
+
!!floatingButton && tabCount % 2 === 0;
|
|
35
34
|
|
|
36
35
|
/**
|
|
37
|
-
*
|
|
38
|
-
* least `Spacing.S`; iOS caps it at the indicator height.
|
|
36
|
+
* gap under the container: the home indicator / gesture bar area
|
|
39
37
|
*/
|
|
40
|
-
const getSystemIndicatorHeight = (bottomInset: number) =>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
const getSystemIndicatorHeight = (bottomInset: number) =>
|
|
39
|
+
Math.max(
|
|
40
|
+
Platform.OS === 'ios'
|
|
41
|
+
? Math.min(bottomInset, TabBarMetrics.systemIndicatorHeight)
|
|
42
|
+
: bottomInset,
|
|
43
|
+
Spacing.S,
|
|
44
|
+
);
|
|
47
45
|
|
|
48
46
|
/**
|
|
49
|
-
*
|
|
50
|
-
* stays still.
|
|
47
|
+
* center action press: the button shrinks while held; the label stays plain text
|
|
51
48
|
*/
|
|
52
49
|
const useCenterActionPress = () => {
|
|
53
50
|
const scale = useSharedValue(1);
|
|
@@ -67,27 +64,26 @@ const useCenterActionPress = () => {
|
|
|
67
64
|
};
|
|
68
65
|
|
|
69
66
|
/**
|
|
70
|
-
*
|
|
71
|
-
* (growing downward when scaled).
|
|
67
|
+
* center action (.Nav/CTA): button rising out of the notch, label under it
|
|
72
68
|
*/
|
|
73
|
-
const CenterAction: React.FC<{
|
|
74
|
-
floatingButton
|
|
75
|
-
|
|
69
|
+
const CenterAction: React.FC<{
|
|
70
|
+
floatingButton: FloatingButtonProps;
|
|
71
|
+
fixedFontScale?: boolean;
|
|
72
|
+
}> = ({ floatingButton, fixedFontScale }) => {
|
|
76
73
|
const { theme } = useContext(ApplicationContext);
|
|
77
74
|
const context = useContext<any>(MiniAppContext);
|
|
78
75
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
79
76
|
const { icon, label, onPress, labelContent } = floatingButton;
|
|
80
77
|
const { buttonStyle, onPressIn, onPressOut } = useCenterActionPress();
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
78
|
+
const labelText = (
|
|
79
|
+
<Text
|
|
80
|
+
numberOfLines={1}
|
|
81
|
+
typography={'label_s_medium'}
|
|
82
|
+
color={theme.colors.text.secondary}
|
|
83
|
+
>
|
|
84
|
+
{label}
|
|
85
|
+
</Text>
|
|
86
|
+
);
|
|
91
87
|
|
|
92
88
|
return (
|
|
93
89
|
<View style={[styles.ctaSlot, showBaseLineDebug && styles.debugBaseLine]}>
|
|
@@ -106,16 +102,22 @@ const CenterAction: React.FC<{ floatingButton: FloatingButtonProps }> = ({
|
|
|
106
102
|
buttonStyle,
|
|
107
103
|
]}
|
|
108
104
|
>
|
|
109
|
-
{
|
|
105
|
+
{typeof icon === 'string' ? (
|
|
106
|
+
<Icon
|
|
107
|
+
source={icon}
|
|
108
|
+
size={TabBarMetrics.ctaIconSize}
|
|
109
|
+
color={Colors.black_01}
|
|
110
|
+
/>
|
|
111
|
+
) : (
|
|
112
|
+
icon
|
|
113
|
+
)}
|
|
110
114
|
</Animated.View>
|
|
111
115
|
<View style={styles.ctaLabel}>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
{label}
|
|
118
|
-
</Text>
|
|
116
|
+
{fixedFontScale ? (
|
|
117
|
+
<FixedScaleText>{labelText}</FixedScaleText>
|
|
118
|
+
) : (
|
|
119
|
+
labelText
|
|
120
|
+
)}
|
|
119
121
|
{labelContent && (
|
|
120
122
|
<View pointerEvents={'none'} style={styles.ctaLabelOverlay}>
|
|
121
123
|
{labelContent}
|
|
@@ -128,8 +130,8 @@ const CenterAction: React.FC<{ floatingButton: FloatingButtonProps }> = ({
|
|
|
128
130
|
};
|
|
129
131
|
|
|
130
132
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
+
* horizontal navigation bar: floating container, items split evenly, Special type puts
|
|
134
|
+
* the center action in a notch in the middle
|
|
133
135
|
*/
|
|
134
136
|
const HorizontalTabBar: React.FC<{
|
|
135
137
|
items: TabItem[];
|
|
@@ -137,15 +139,23 @@ const HorizontalTabBar: React.FC<{
|
|
|
137
139
|
activeTintColor: string;
|
|
138
140
|
inactiveTintColor: string;
|
|
139
141
|
floatingButton?: FloatingButtonProps;
|
|
140
|
-
|
|
142
|
+
fixedFontScale?: boolean;
|
|
143
|
+
}> = ({
|
|
144
|
+
items,
|
|
145
|
+
width,
|
|
146
|
+
activeTintColor,
|
|
147
|
+
inactiveTintColor,
|
|
148
|
+
floatingButton,
|
|
149
|
+
fixedFontScale,
|
|
150
|
+
}) => {
|
|
141
151
|
const withCta = hasCenterAction(floatingButton, items.length);
|
|
142
152
|
const [layouts, setLayouts] = useState<
|
|
143
153
|
Record<string, { x: number; y: number; width: number }>
|
|
144
154
|
>({});
|
|
145
155
|
const selected = layouts[items.find(item => item.focused)?.route.key ?? ''];
|
|
146
156
|
|
|
147
|
-
const renderItems = (data: TabItem[]) =>
|
|
148
|
-
|
|
157
|
+
const renderItems = (data: TabItem[]) =>
|
|
158
|
+
data.map(item => (
|
|
149
159
|
<CustomBottomTabItem
|
|
150
160
|
key={item.route.key}
|
|
151
161
|
onLayout={e => {
|
|
@@ -163,9 +173,9 @@ const HorizontalTabBar: React.FC<{
|
|
|
163
173
|
onLongPress={item.onLongPress}
|
|
164
174
|
activeTintColor={activeTintColor}
|
|
165
175
|
inactiveTintColor={inactiveTintColor}
|
|
176
|
+
fixedFontScale={fixedFontScale}
|
|
166
177
|
/>
|
|
167
178
|
));
|
|
168
|
-
};
|
|
169
179
|
|
|
170
180
|
const renderContent = () => {
|
|
171
181
|
if (withCta && floatingButton) {
|
|
@@ -173,7 +183,10 @@ const HorizontalTabBar: React.FC<{
|
|
|
173
183
|
return (
|
|
174
184
|
<>
|
|
175
185
|
{renderItems(items.slice(0, middle))}
|
|
176
|
-
<CenterAction
|
|
186
|
+
<CenterAction
|
|
187
|
+
floatingButton={floatingButton}
|
|
188
|
+
fixedFontScale={fixedFontScale}
|
|
189
|
+
/>
|
|
177
190
|
{renderItems(items.slice(middle))}
|
|
178
191
|
</>
|
|
179
192
|
);
|
|
@@ -181,17 +194,21 @@ const HorizontalTabBar: React.FC<{
|
|
|
181
194
|
return renderItems(items);
|
|
182
195
|
};
|
|
183
196
|
|
|
197
|
+
const customContainer = withCta && floatingButton?.container;
|
|
198
|
+
|
|
184
199
|
return (
|
|
185
200
|
<View
|
|
186
201
|
style={[styles.container, { width }]}
|
|
187
202
|
accessibilityLabel={'bottom_tab_bar'}
|
|
188
203
|
>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
204
|
+
{customContainer || (
|
|
205
|
+
<CurvedContainer
|
|
206
|
+
width={width}
|
|
207
|
+
height={TabBarMetrics.height}
|
|
208
|
+
radius={TabBarMetrics.height / 2}
|
|
209
|
+
notchCenterX={withCta ? width / 2 : undefined}
|
|
210
|
+
/>
|
|
211
|
+
)}
|
|
195
212
|
<View style={styles.content}>
|
|
196
213
|
<ActiveIndicator
|
|
197
214
|
axis={'x'}
|
|
@@ -210,9 +227,9 @@ const HorizontalTabBar: React.FC<{
|
|
|
210
227
|
};
|
|
211
228
|
|
|
212
229
|
/**
|
|
213
|
-
*
|
|
214
|
-
* so content shows around it and through the notch. The transparent strip
|
|
215
|
-
* the rising center action touchable
|
|
230
|
+
* phone tab bar: the horizontal navigation bar floating over the tab content above the home
|
|
231
|
+
* indicator, so the content shows around it and through the notch. The transparent strip
|
|
232
|
+
* above it keeps the rising center action touchable
|
|
216
233
|
*/
|
|
217
234
|
export default function BottomTabBar(props: Props) {
|
|
218
235
|
const { state, floatingButton, activeTintColor, inactiveTintColor } = props;
|
|
@@ -224,8 +241,6 @@ export default function BottomTabBar(props: Props) {
|
|
|
224
241
|
window.height,
|
|
225
242
|
insets.left,
|
|
226
243
|
insets.right,
|
|
227
|
-
state.routes.length,
|
|
228
|
-
withCta,
|
|
229
244
|
);
|
|
230
245
|
|
|
231
246
|
return (
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import { createContext } from 'react';
|
|
2
2
|
|
|
3
3
|
type BottomTabInset = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* indicator area, plus the rising center action when shown); 0 on the iPhone Duo rail.
|
|
7
|
-
*/
|
|
4
|
+
// space the floating tab bar takes over the bottom of a tab's content (bar + home
|
|
5
|
+
// indicator area, plus the rising center action when shown); 0 on the iPhone Duo rail
|
|
8
6
|
inset: number;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* its scroll view, or above the bar when static), so the tab stops padding its content.
|
|
12
|
-
*/
|
|
7
|
+
// a Screen in the tab keeps its content clear of the bar itself (inside its scroll view,
|
|
8
|
+
// or above the bar when static), so the tab stops padding its content
|
|
13
9
|
claim?: () => void;
|
|
14
10
|
};
|
|
15
11
|
|