@momo-kits/foundation 0.168.1-rn862.1 → 0.169.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 +215 -0
- package/Application/BottomSheet.parts.tsx +231 -0
- package/Application/BottomSheet.styles.ts +59 -0
- package/Application/BottomSheet.tsx +114 -265
- package/Application/BottomTab/BottomTabBar.styles.ts +60 -0
- package/Application/BottomTab/BottomTabBar.tsx +7 -60
- package/Application/BottomTab/CustomBottomTabItem.tsx +5 -5
- package/Application/BottomTab/DuoRailTabBar.tsx +149 -0
- package/Application/BottomTab/DuoTabBar.parts.tsx +202 -0
- package/Application/BottomTab/DuoTabBar.styles.ts +123 -0
- package/Application/BottomTab/DuoTabBar.tsx +78 -464
- package/Application/BottomTab/TabScreen.tsx +177 -0
- package/Application/BottomTab/badge.ts +41 -0
- package/Application/BottomTab/hooks.ts +358 -0
- package/Application/BottomTab/index.styles.ts +30 -0
- package/Application/BottomTab/index.tsx +9 -236
- 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/ModalScreen.tsx +14 -10
- 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/hooks.ts +137 -0
- package/Application/index.ts +3 -1
- package/Application/navigationOptions.tsx +217 -0
- package/Application/types.header.ts +116 -0
- package/Application/types.tabs.ts +43 -0
- package/Application/types.theme.ts +87 -0
- package/Application/types.ts +12 -244
- package/Application/utils.ts +143 -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/Screen.tsx +62 -667
- package/Layout/Section.tsx +1 -2
- package/Layout/index.ts +8 -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 +47 -11
- package/Popup/types.ts +7 -0
- package/Popup/utils.ts +61 -14
- 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 +20 -22
- package/Application/device.ts +0 -29
- package/Application/utils.tsx +0 -497
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { MutableRefObject, useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Extrapolation,
|
|
4
|
+
interpolate,
|
|
5
|
+
SharedValue,
|
|
6
|
+
useAnimatedStyle,
|
|
7
|
+
} from 'react-native-reanimated';
|
|
8
|
+
import {
|
|
9
|
+
BackHandler,
|
|
10
|
+
Dimensions,
|
|
11
|
+
Keyboard,
|
|
12
|
+
KeyboardEvent,
|
|
13
|
+
LayoutAnimation,
|
|
14
|
+
Platform,
|
|
15
|
+
useWindowDimensions,
|
|
16
|
+
} from 'react-native';
|
|
17
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
18
|
+
import { useHeaderHeight } from '@react-navigation/elements';
|
|
19
|
+
import { Spacing } from '../Consts';
|
|
20
|
+
import { DuoMetrics, useScreenLayout } from '../Layout/utils';
|
|
21
|
+
import { scheduleOnRN } from 'react-native-worklets';
|
|
22
|
+
import { BottomSheetParams, NavigatorProps } from './types';
|
|
23
|
+
import { getDuoTopInset, isIPhoneDuo } from './utils';
|
|
24
|
+
import { DUO_REGULAR_MIN_WIDTH, getSideInset } from './BottomSheet.styles';
|
|
25
|
+
|
|
26
|
+
export const useBottomSheetLayout = () => {
|
|
27
|
+
const windowSize = useWindowDimensions();
|
|
28
|
+
const isDuo = isIPhoneDuo();
|
|
29
|
+
const isDuoCentered =
|
|
30
|
+
isDuo &&
|
|
31
|
+
Math.min(windowSize.width, windowSize.height) >= DUO_REGULAR_MIN_WIDTH;
|
|
32
|
+
const isDuoFloating = isDuo && !isDuoCentered;
|
|
33
|
+
const { sideBar } = useScreenLayout();
|
|
34
|
+
const sideColumnWidth = (sideBar?.width ?? 0) - Spacing.M;
|
|
35
|
+
const sideColumn =
|
|
36
|
+
isDuoFloating &&
|
|
37
|
+
sideColumnWidth >= DuoMetrics.controlSize + Spacing.M &&
|
|
38
|
+
sideBar
|
|
39
|
+
? sideBar
|
|
40
|
+
: undefined;
|
|
41
|
+
const hasSideColumn = !!sideColumn;
|
|
42
|
+
// 'screen' is the outer display on iPhone Duo, even when unfolded
|
|
43
|
+
const screenHeight = Dimensions.get('screen').height;
|
|
44
|
+
const heightDevice = isDuo ? windowSize.height : screenHeight;
|
|
45
|
+
const insets = useSafeAreaInsets();
|
|
46
|
+
const heightHeader = useHeaderHeight();
|
|
47
|
+
const topGap = Math.max(
|
|
48
|
+
sideColumn
|
|
49
|
+
? insets.top
|
|
50
|
+
: getDuoTopInset(insets, windowSize.width, windowSize.height),
|
|
51
|
+
Spacing.XL,
|
|
52
|
+
);
|
|
53
|
+
const sideInsetLeft = isDuoFloating ? getSideInset(insets.left) : 0;
|
|
54
|
+
const sideInsetRight = isDuoFloating ? getSideInset(insets.right) : 0;
|
|
55
|
+
const indicatorShift = (sideInsetRight - sideInsetLeft) / 2;
|
|
56
|
+
const isIOS = Platform.OS === 'ios';
|
|
57
|
+
const bottomInset = isIOS ? Math.min(insets.bottom, 21) : insets.bottom;
|
|
58
|
+
const keyboardOffset = heightHeader - bottomInset;
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
windowSize,
|
|
62
|
+
isDuo,
|
|
63
|
+
isDuoCentered,
|
|
64
|
+
isDuoFloating,
|
|
65
|
+
sideColumn,
|
|
66
|
+
sideColumnWidth,
|
|
67
|
+
hasSideColumn,
|
|
68
|
+
heightDevice,
|
|
69
|
+
insets,
|
|
70
|
+
topGap,
|
|
71
|
+
sideInsetLeft,
|
|
72
|
+
sideInsetRight,
|
|
73
|
+
indicatorShift,
|
|
74
|
+
bottomInset,
|
|
75
|
+
keyboardOffset,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// KeyboardAvoidingView lifts the whole sheet past the top on iPhone Duo,
|
|
80
|
+
// so lift it here and shrink maxHeight by the same amount instead
|
|
81
|
+
export const useDuoKeyboardLift = (
|
|
82
|
+
isDuo: boolean,
|
|
83
|
+
useKeyboardAvoidingView: boolean,
|
|
84
|
+
windowHeight: number,
|
|
85
|
+
bottomInset: number,
|
|
86
|
+
keyboardVerticalOffset?: number,
|
|
87
|
+
) => {
|
|
88
|
+
const [keyboardLift, setKeyboardLift] = useState(0);
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!isDuo || !useKeyboardAvoidingView) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const onKeyboardChange = (e: KeyboardEvent) => {
|
|
95
|
+
const overlap = windowHeight - e.endCoordinates.screenY;
|
|
96
|
+
const lift = Math.max(
|
|
97
|
+
overlap - bottomInset - (keyboardVerticalOffset ?? 0),
|
|
98
|
+
0,
|
|
99
|
+
);
|
|
100
|
+
if (e.duration) {
|
|
101
|
+
LayoutAnimation.configureNext({
|
|
102
|
+
duration: e.duration,
|
|
103
|
+
update: {
|
|
104
|
+
duration: e.duration,
|
|
105
|
+
type: LayoutAnimation.Types[e.easing] || 'keyboard',
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
setKeyboardLift(lift);
|
|
110
|
+
};
|
|
111
|
+
const subscriptions = [
|
|
112
|
+
Keyboard.addListener('keyboardWillChangeFrame', onKeyboardChange),
|
|
113
|
+
Keyboard.addListener('keyboardWillHide', onKeyboardChange),
|
|
114
|
+
];
|
|
115
|
+
return () => subscriptions.forEach(sub => sub.remove());
|
|
116
|
+
}, [
|
|
117
|
+
bottomInset,
|
|
118
|
+
isDuo,
|
|
119
|
+
keyboardVerticalOffset,
|
|
120
|
+
useKeyboardAvoidingView,
|
|
121
|
+
windowHeight,
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
return keyboardLift;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export const useSheetAnimatedStyles = (
|
|
128
|
+
translateY: SharedValue<number>,
|
|
129
|
+
heightDevice: number,
|
|
130
|
+
) => {
|
|
131
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
132
|
+
return {
|
|
133
|
+
transform: [{ translateY: translateY.value }],
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const animatedOverlayStyle = useAnimatedStyle(() => {
|
|
138
|
+
return {
|
|
139
|
+
opacity: interpolate(
|
|
140
|
+
translateY.value,
|
|
141
|
+
[0, heightDevice],
|
|
142
|
+
[1, 0],
|
|
143
|
+
Extrapolation.CLAMP,
|
|
144
|
+
),
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return { animatedStyle, animatedOverlayStyle };
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
type SheetDismissParams = {
|
|
152
|
+
barrierDismissible: boolean;
|
|
153
|
+
closeAnimation: (callback?: () => void) => void;
|
|
154
|
+
navigator?: NavigatorProps;
|
|
155
|
+
leftOptions: BottomSheetParams['leftOptions'];
|
|
156
|
+
action: MutableRefObject<undefined | string>;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export const useSheetDismiss = ({
|
|
160
|
+
barrierDismissible,
|
|
161
|
+
closeAnimation,
|
|
162
|
+
navigator,
|
|
163
|
+
leftOptions,
|
|
164
|
+
action,
|
|
165
|
+
}: SheetDismissParams) => {
|
|
166
|
+
/**
|
|
167
|
+
* handler dismiss
|
|
168
|
+
*/
|
|
169
|
+
const onDismiss = useCallback(
|
|
170
|
+
(force = false, callback?: () => void) => {
|
|
171
|
+
if (barrierDismissible && !force) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
closeAnimation(() => {
|
|
175
|
+
navigator?.pop();
|
|
176
|
+
scheduleOnRN(() => {
|
|
177
|
+
callback?.();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
},
|
|
181
|
+
[barrierDismissible, closeAnimation, navigator],
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* on request close
|
|
186
|
+
*/
|
|
187
|
+
const onRequestClose = useCallback(
|
|
188
|
+
(callback?: () => void) => {
|
|
189
|
+
onDismiss(true, callback);
|
|
190
|
+
},
|
|
191
|
+
[onDismiss],
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
const backHandler = BackHandler.addEventListener(
|
|
196
|
+
'hardwareBackPress',
|
|
197
|
+
() => {
|
|
198
|
+
onDismiss();
|
|
199
|
+
return true;
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
return () => backHandler.remove();
|
|
203
|
+
}, [barrierDismissible, onDismiss]);
|
|
204
|
+
|
|
205
|
+
const onPressBack = useCallback(() => {
|
|
206
|
+
onDismiss(true, leftOptions?.onPressIconLeft);
|
|
207
|
+
}, [leftOptions, onDismiss]);
|
|
208
|
+
|
|
209
|
+
const onPressClose = useCallback(() => {
|
|
210
|
+
action.current = 'icon_close';
|
|
211
|
+
onDismiss(true);
|
|
212
|
+
}, [action, onDismiss]);
|
|
213
|
+
|
|
214
|
+
return { onDismiss, onRequestClose, onPressBack, onPressClose };
|
|
215
|
+
};
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
PanResponderInstance,
|
|
4
|
+
ScaledSize,
|
|
5
|
+
TouchableOpacity,
|
|
6
|
+
View,
|
|
7
|
+
} from 'react-native';
|
|
8
|
+
import type { EdgeInsets } from 'react-native-safe-area-context';
|
|
9
|
+
import { Radius, Spacing, Styles } from '../Consts';
|
|
10
|
+
import layoutStyles from '../Layout/styles';
|
|
11
|
+
import { Text } from '../Text';
|
|
12
|
+
import { Icon } from '../Icon';
|
|
13
|
+
import { InnerEdgeStyles, type SideBarLayout } from '../Layout/utils';
|
|
14
|
+
import { SideBarButton } from './Components/SideBarButton';
|
|
15
|
+
import {
|
|
16
|
+
INDICATOR_HEIGHT,
|
|
17
|
+
SIDE_COLUMN_OFFSET,
|
|
18
|
+
styles,
|
|
19
|
+
} from './BottomSheet.styles';
|
|
20
|
+
import { BottomSheetParams, Theme } from './types';
|
|
21
|
+
|
|
22
|
+
type SheetPartsParams = {
|
|
23
|
+
panHandlers: PanResponderInstance['panHandlers'];
|
|
24
|
+
indicatorShift: number;
|
|
25
|
+
useDivider: boolean;
|
|
26
|
+
theme: Theme;
|
|
27
|
+
hasSideColumn: boolean;
|
|
28
|
+
showBack: boolean;
|
|
29
|
+
onPressBack: () => void;
|
|
30
|
+
onPressClose: () => void;
|
|
31
|
+
iconBack: string;
|
|
32
|
+
options: BottomSheetParams['options'];
|
|
33
|
+
leftOptions: BottomSheetParams['leftOptions'];
|
|
34
|
+
sideColumn?: SideBarLayout;
|
|
35
|
+
windowHeight: number;
|
|
36
|
+
sheetTop: number;
|
|
37
|
+
sideColumnHeight: number;
|
|
38
|
+
sideColumnWidth: number;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const renderSheetHeader = ({
|
|
42
|
+
panHandlers,
|
|
43
|
+
indicatorShift,
|
|
44
|
+
useDivider,
|
|
45
|
+
theme,
|
|
46
|
+
hasSideColumn,
|
|
47
|
+
showBack,
|
|
48
|
+
onPressBack,
|
|
49
|
+
onPressClose,
|
|
50
|
+
iconBack,
|
|
51
|
+
options,
|
|
52
|
+
leftOptions,
|
|
53
|
+
}: SheetPartsParams) => {
|
|
54
|
+
const showButtons = !hasSideColumn;
|
|
55
|
+
return (
|
|
56
|
+
<View {...panHandlers}>
|
|
57
|
+
<View style={{ height: INDICATOR_HEIGHT }}>
|
|
58
|
+
<View
|
|
59
|
+
style={[
|
|
60
|
+
styles.indicator,
|
|
61
|
+
indicatorShift !== 0 && {
|
|
62
|
+
transform: [{ translateX: indicatorShift }],
|
|
63
|
+
},
|
|
64
|
+
]}
|
|
65
|
+
/>
|
|
66
|
+
</View>
|
|
67
|
+
<View
|
|
68
|
+
style={[
|
|
69
|
+
styles.header,
|
|
70
|
+
useDivider && styles.headerDivider,
|
|
71
|
+
useDivider && {
|
|
72
|
+
borderBottomColor: theme.colors.border.default,
|
|
73
|
+
},
|
|
74
|
+
]}
|
|
75
|
+
>
|
|
76
|
+
{showButtons && showBack ? (
|
|
77
|
+
<TouchableOpacity style={styles.iconButton} onPress={onPressBack}>
|
|
78
|
+
<Icon source={iconBack} />
|
|
79
|
+
</TouchableOpacity>
|
|
80
|
+
) : null}
|
|
81
|
+
|
|
82
|
+
{options.header ? (
|
|
83
|
+
<View style={Styles.flex}>{options.header}</View>
|
|
84
|
+
) : (
|
|
85
|
+
<>
|
|
86
|
+
{showButtons && !leftOptions ? (
|
|
87
|
+
<View style={styles.iconButton} />
|
|
88
|
+
) : null}
|
|
89
|
+
<Text
|
|
90
|
+
style={[
|
|
91
|
+
Styles.flex,
|
|
92
|
+
styles.title,
|
|
93
|
+
!showButtons && styles.titleInset,
|
|
94
|
+
]}
|
|
95
|
+
numberOfLines={1}
|
|
96
|
+
typography="header_default_bold"
|
|
97
|
+
>
|
|
98
|
+
{options.title ?? ''}
|
|
99
|
+
</Text>
|
|
100
|
+
</>
|
|
101
|
+
)}
|
|
102
|
+
{showButtons ? (
|
|
103
|
+
<TouchableOpacity style={styles.iconButton} onPress={onPressClose}>
|
|
104
|
+
<Icon source="navigation_close" />
|
|
105
|
+
</TouchableOpacity>
|
|
106
|
+
) : null}
|
|
107
|
+
</View>
|
|
108
|
+
</View>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* iPhone Duo folded: back and close move to the side bar column, drawn on
|
|
114
|
+
* the part of the sheet under the side safe area. They line up with the
|
|
115
|
+
* header row, stay below the status bar cluster on a tall sheet and above a
|
|
116
|
+
* camera at the bottom end of the strip.
|
|
117
|
+
*/
|
|
118
|
+
export const renderSheetSideColumn = ({
|
|
119
|
+
sideColumn,
|
|
120
|
+
windowHeight,
|
|
121
|
+
sheetTop,
|
|
122
|
+
sideColumnHeight,
|
|
123
|
+
sideColumnWidth,
|
|
124
|
+
showBack,
|
|
125
|
+
iconBack,
|
|
126
|
+
onPressBack,
|
|
127
|
+
onPressClose,
|
|
128
|
+
}: SheetPartsParams) => {
|
|
129
|
+
if (!sideColumn) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const lowestTop =
|
|
133
|
+
windowHeight - sideColumn.bottom - sideColumnHeight - sheetTop;
|
|
134
|
+
const top = Math.min(
|
|
135
|
+
Math.max(sideColumn.top - sheetTop, SIDE_COLUMN_OFFSET),
|
|
136
|
+
lowestTop,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<View
|
|
141
|
+
pointerEvents={'box-none'}
|
|
142
|
+
style={[
|
|
143
|
+
styles.sideColumn,
|
|
144
|
+
sideColumn.edge === 'right' ? styles.alignRight : styles.alignLeft,
|
|
145
|
+
InnerEdgeStyles[sideColumn.edge],
|
|
146
|
+
{ top, width: sideColumnWidth },
|
|
147
|
+
]}
|
|
148
|
+
>
|
|
149
|
+
{showBack ? (
|
|
150
|
+
<SideBarButton icon={iconBack} onPress={onPressBack} />
|
|
151
|
+
) : null}
|
|
152
|
+
<SideBarButton icon="navigation_close" onPress={onPressClose} />
|
|
153
|
+
</View>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export const renderSheetFooter = ({
|
|
158
|
+
footerComponent,
|
|
159
|
+
theme,
|
|
160
|
+
sideInsetStyle,
|
|
161
|
+
}: {
|
|
162
|
+
footerComponent: BottomSheetParams['footerComponent'];
|
|
163
|
+
theme: Theme;
|
|
164
|
+
sideInsetStyle: any;
|
|
165
|
+
}) =>
|
|
166
|
+
footerComponent && (
|
|
167
|
+
<View
|
|
168
|
+
style={[
|
|
169
|
+
layoutStyles.shadow,
|
|
170
|
+
{
|
|
171
|
+
paddingBottom: Spacing.S,
|
|
172
|
+
backgroundColor: theme.colors.background.surface,
|
|
173
|
+
},
|
|
174
|
+
sideInsetStyle,
|
|
175
|
+
]}
|
|
176
|
+
>
|
|
177
|
+
{footerComponent}
|
|
178
|
+
</View>
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
type SheetStyleParams = {
|
|
182
|
+
backgroundColor: string;
|
|
183
|
+
isDuo: boolean;
|
|
184
|
+
isDuoFloating: boolean;
|
|
185
|
+
isDuoCentered: boolean;
|
|
186
|
+
windowSize: ScaledSize;
|
|
187
|
+
topGap: number;
|
|
188
|
+
keyboardLift: number;
|
|
189
|
+
sideColumn?: SideBarLayout;
|
|
190
|
+
sideColumnHeight: number;
|
|
191
|
+
insets: EdgeInsets;
|
|
192
|
+
showBaseLineDebug: boolean;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export const getSheetStyle = ({
|
|
196
|
+
backgroundColor,
|
|
197
|
+
isDuo,
|
|
198
|
+
isDuoFloating,
|
|
199
|
+
isDuoCentered,
|
|
200
|
+
windowSize,
|
|
201
|
+
topGap,
|
|
202
|
+
keyboardLift,
|
|
203
|
+
sideColumn,
|
|
204
|
+
sideColumnHeight,
|
|
205
|
+
insets,
|
|
206
|
+
showBaseLineDebug,
|
|
207
|
+
}: SheetStyleParams) => [
|
|
208
|
+
styles.sheetContainer,
|
|
209
|
+
{
|
|
210
|
+
backgroundColor,
|
|
211
|
+
borderTopLeftRadius: isDuo ? Radius.XL : Radius.M,
|
|
212
|
+
borderTopRightRadius: isDuo ? Radius.XL : Radius.M,
|
|
213
|
+
},
|
|
214
|
+
isDuo && {
|
|
215
|
+
maxHeight: windowSize.height - topGap - keyboardLift,
|
|
216
|
+
marginBottom: keyboardLift,
|
|
217
|
+
},
|
|
218
|
+
isDuoFloating && { marginHorizontal: Spacing.M },
|
|
219
|
+
sideColumn && {
|
|
220
|
+
minHeight: SIDE_COLUMN_OFFSET + sideColumnHeight + sideColumn.bottom,
|
|
221
|
+
},
|
|
222
|
+
isDuoCentered && styles.centered,
|
|
223
|
+
isDuoCentered && {
|
|
224
|
+
// keeps the portrait width in landscape, like system sheets
|
|
225
|
+
width: Math.min(
|
|
226
|
+
Math.min(windowSize.width, windowSize.height) - Spacing.L * 2,
|
|
227
|
+
windowSize.width - Math.max(insets.left, insets.right, Spacing.L) * 2,
|
|
228
|
+
),
|
|
229
|
+
},
|
|
230
|
+
showBaseLineDebug && styles.debugBaseLine,
|
|
231
|
+
];
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { Colors, Radius, Spacing } from '../Consts';
|
|
3
|
+
import { DuoMetrics } from '../Layout/utils';
|
|
4
|
+
|
|
5
|
+
// iPhone Duo inner display: center the sheet instead of spanning the width
|
|
6
|
+
export const DUO_REGULAR_MIN_WIDTH = 600;
|
|
7
|
+
|
|
8
|
+
export const INDICATOR_HEIGHT = 16;
|
|
9
|
+
export const HEADER_HEIGHT = 56;
|
|
10
|
+
export const DUO_ORIENTATIONS = ['portrait', 'landscape'];
|
|
11
|
+
export const SIDE_COLUMN_OFFSET =
|
|
12
|
+
INDICATOR_HEIGHT + (HEADER_HEIGHT - DuoMetrics.controlSize) / 2;
|
|
13
|
+
|
|
14
|
+
export const getSideInset = (inset: number) => Math.max(inset - Spacing.M, 0);
|
|
15
|
+
|
|
16
|
+
export const styles = StyleSheet.create({
|
|
17
|
+
container: {
|
|
18
|
+
flex: 1,
|
|
19
|
+
justifyContent: 'flex-end',
|
|
20
|
+
},
|
|
21
|
+
overlay: {
|
|
22
|
+
flex: 1,
|
|
23
|
+
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
|
24
|
+
},
|
|
25
|
+
indicator: {
|
|
26
|
+
width: 40,
|
|
27
|
+
height: 4,
|
|
28
|
+
borderRadius: Radius.S,
|
|
29
|
+
backgroundColor: Colors.black_06,
|
|
30
|
+
position: 'absolute',
|
|
31
|
+
alignSelf: 'center',
|
|
32
|
+
top: Spacing.S,
|
|
33
|
+
},
|
|
34
|
+
header: {
|
|
35
|
+
height: HEADER_HEIGHT,
|
|
36
|
+
flexDirection: 'row',
|
|
37
|
+
alignItems: 'center',
|
|
38
|
+
},
|
|
39
|
+
title: { textAlign: 'center' },
|
|
40
|
+
titleInset: { paddingHorizontal: Spacing.M },
|
|
41
|
+
iconButton: {
|
|
42
|
+
width: 24,
|
|
43
|
+
height: 24,
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
justifyContent: 'center',
|
|
46
|
+
marginHorizontal: Spacing.M,
|
|
47
|
+
},
|
|
48
|
+
shrink: { flexShrink: 1 },
|
|
49
|
+
sideColumn: {
|
|
50
|
+
position: 'absolute',
|
|
51
|
+
gap: Spacing.M,
|
|
52
|
+
},
|
|
53
|
+
alignRight: { right: 0 },
|
|
54
|
+
alignLeft: { left: 0 },
|
|
55
|
+
debugBaseLine: { borderWidth: 1, borderColor: Colors.green_06 },
|
|
56
|
+
headerDivider: { borderBottomWidth: 1 },
|
|
57
|
+
sheetContainer: { overflow: 'hidden' },
|
|
58
|
+
centered: { alignSelf: 'center' },
|
|
59
|
+
});
|