1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.default = BottomTabBar;
|
7 | exports.getTabBarHeight = void 0;
|
8 |
|
9 | var _elements = require("@react-navigation/elements");
|
10 |
|
11 | var _native = require("@react-navigation/native");
|
12 |
|
13 | var _react = _interopRequireDefault(require("react"));
|
14 |
|
15 | var _reactNative = require("react-native");
|
16 |
|
17 | var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
|
18 |
|
19 | var _BottomTabBarHeightCallbackContext = _interopRequireDefault(require("../utils/BottomTabBarHeightCallbackContext"));
|
20 |
|
21 | var _useIsKeyboardShown = _interopRequireDefault(require("../utils/useIsKeyboardShown"));
|
22 |
|
23 | var _BottomTabItem = _interopRequireDefault(require("./BottomTabItem"));
|
24 |
|
25 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
26 |
|
27 | const DEFAULT_TABBAR_HEIGHT = 49;
|
28 | const COMPACT_TABBAR_HEIGHT = 32;
|
29 | const DEFAULT_MAX_TAB_ITEM_WIDTH = 125;
|
30 | const useNativeDriver = _reactNative.Platform.OS !== 'web';
|
31 |
|
32 | const shouldUseHorizontalLabels = _ref => {
|
33 | let {
|
34 | state,
|
35 | descriptors,
|
36 | layout,
|
37 | dimensions
|
38 | } = _ref;
|
39 | const {
|
40 | tabBarLabelPosition
|
41 | } = descriptors[state.routes[state.index].key].options;
|
42 |
|
43 | if (tabBarLabelPosition) {
|
44 | switch (tabBarLabelPosition) {
|
45 | case 'beside-icon':
|
46 | return true;
|
47 |
|
48 | case 'below-icon':
|
49 | return false;
|
50 | }
|
51 | }
|
52 |
|
53 | if (layout.width >= 768) {
|
54 |
|
55 | const maxTabWidth = state.routes.reduce((acc, route) => {
|
56 | const {
|
57 | tabBarItemStyle
|
58 | } = descriptors[route.key].options;
|
59 |
|
60 | const flattenedStyle = _reactNative.StyleSheet.flatten(tabBarItemStyle);
|
61 |
|
62 | if (flattenedStyle) {
|
63 | if (typeof flattenedStyle.width === 'number') {
|
64 | return acc + flattenedStyle.width;
|
65 | } else if (typeof flattenedStyle.maxWidth === 'number') {
|
66 | return acc + flattenedStyle.maxWidth;
|
67 | }
|
68 | }
|
69 |
|
70 | return acc + DEFAULT_MAX_TAB_ITEM_WIDTH;
|
71 | }, 0);
|
72 | return maxTabWidth <= layout.width;
|
73 | } else {
|
74 | return dimensions.width > dimensions.height;
|
75 | }
|
76 | };
|
77 |
|
78 | const getPaddingBottom = insets => Math.max(insets.bottom - _reactNative.Platform.select({
|
79 | ios: 4,
|
80 | default: 0
|
81 | }), 0);
|
82 |
|
83 | const getTabBarHeight = _ref2 => {
|
84 | var _StyleSheet$flatten;
|
85 |
|
86 | let {
|
87 | state,
|
88 | descriptors,
|
89 | dimensions,
|
90 | insets,
|
91 | style,
|
92 | ...rest
|
93 | } = _ref2;
|
94 |
|
95 | const customHeight = (_StyleSheet$flatten = _reactNative.StyleSheet.flatten(style)) === null || _StyleSheet$flatten === void 0 ? void 0 : _StyleSheet$flatten.height;
|
96 |
|
97 | if (typeof customHeight === 'number') {
|
98 | return customHeight;
|
99 | }
|
100 |
|
101 | const isLandscape = dimensions.width > dimensions.height;
|
102 | const horizontalLabels = shouldUseHorizontalLabels({
|
103 | state,
|
104 | descriptors,
|
105 | dimensions,
|
106 | ...rest
|
107 | });
|
108 | const paddingBottom = getPaddingBottom(insets);
|
109 |
|
110 | if (_reactNative.Platform.OS === 'ios' && !_reactNative.Platform.isPad && isLandscape && horizontalLabels) {
|
111 | return COMPACT_TABBAR_HEIGHT + paddingBottom;
|
112 | }
|
113 |
|
114 | return DEFAULT_TABBAR_HEIGHT + paddingBottom;
|
115 | };
|
116 |
|
117 | exports.getTabBarHeight = getTabBarHeight;
|
118 |
|
119 | function BottomTabBar(_ref3) {
|
120 | let {
|
121 | state,
|
122 | navigation,
|
123 | descriptors,
|
124 | insets,
|
125 | style
|
126 | } = _ref3;
|
127 | const {
|
128 | colors
|
129 | } = (0, _native.useTheme)();
|
130 | const buildLink = (0, _native.useLinkBuilder)();
|
131 | const focusedRoute = state.routes[state.index];
|
132 | const focusedDescriptor = descriptors[focusedRoute.key];
|
133 | const focusedOptions = focusedDescriptor.options;
|
134 | const {
|
135 | tabBarShowLabel,
|
136 | tabBarHideOnKeyboard = false,
|
137 | tabBarVisibilityAnimationConfig,
|
138 | tabBarStyle,
|
139 | tabBarBackground,
|
140 | tabBarActiveTintColor,
|
141 | tabBarInactiveTintColor,
|
142 | tabBarActiveBackgroundColor,
|
143 | tabBarInactiveBackgroundColor
|
144 | } = focusedOptions;
|
145 | const dimensions = (0, _reactNativeSafeAreaContext.useSafeAreaFrame)();
|
146 | const isKeyboardShown = (0, _useIsKeyboardShown.default)();
|
147 |
|
148 | const onHeightChange = _react.default.useContext(_BottomTabBarHeightCallbackContext.default);
|
149 |
|
150 | const shouldShowTabBar = !(tabBarHideOnKeyboard && isKeyboardShown);
|
151 |
|
152 | const visibilityAnimationConfigRef = _react.default.useRef(tabBarVisibilityAnimationConfig);
|
153 |
|
154 | _react.default.useEffect(() => {
|
155 | visibilityAnimationConfigRef.current = tabBarVisibilityAnimationConfig;
|
156 | });
|
157 |
|
158 | const [isTabBarHidden, setIsTabBarHidden] = _react.default.useState(!shouldShowTabBar);
|
159 |
|
160 | const [visible] = _react.default.useState(() => new _reactNative.Animated.Value(shouldShowTabBar ? 1 : 0));
|
161 |
|
162 | _react.default.useEffect(() => {
|
163 | const visibilityAnimationConfig = visibilityAnimationConfigRef.current;
|
164 |
|
165 | if (shouldShowTabBar) {
|
166 | var _visibilityAnimationC, _visibilityAnimationC2;
|
167 |
|
168 | const animation = (visibilityAnimationConfig === null || visibilityAnimationConfig === void 0 ? void 0 : (_visibilityAnimationC = visibilityAnimationConfig.show) === null || _visibilityAnimationC === void 0 ? void 0 : _visibilityAnimationC.animation) === 'spring' ? _reactNative.Animated.spring : _reactNative.Animated.timing;
|
169 | animation(visible, {
|
170 | toValue: 1,
|
171 | useNativeDriver,
|
172 | duration: 250,
|
173 | ...(visibilityAnimationConfig === null || visibilityAnimationConfig === void 0 ? void 0 : (_visibilityAnimationC2 = visibilityAnimationConfig.show) === null || _visibilityAnimationC2 === void 0 ? void 0 : _visibilityAnimationC2.config)
|
174 | }).start(_ref4 => {
|
175 | let {
|
176 | finished
|
177 | } = _ref4;
|
178 |
|
179 | if (finished) {
|
180 | setIsTabBarHidden(false);
|
181 | }
|
182 | });
|
183 | } else {
|
184 | var _visibilityAnimationC3, _visibilityAnimationC4;
|
185 |
|
186 | setIsTabBarHidden(true);
|
187 | const animation = (visibilityAnimationConfig === null || visibilityAnimationConfig === void 0 ? void 0 : (_visibilityAnimationC3 = visibilityAnimationConfig.hide) === null || _visibilityAnimationC3 === void 0 ? void 0 : _visibilityAnimationC3.animation) === 'spring' ? _reactNative.Animated.spring : _reactNative.Animated.timing;
|
188 | animation(visible, {
|
189 | toValue: 0,
|
190 | useNativeDriver,
|
191 | duration: 200,
|
192 | ...(visibilityAnimationConfig === null || visibilityAnimationConfig === void 0 ? void 0 : (_visibilityAnimationC4 = visibilityAnimationConfig.hide) === null || _visibilityAnimationC4 === void 0 ? void 0 : _visibilityAnimationC4.config)
|
193 | }).start();
|
194 | }
|
195 |
|
196 | return () => visible.stopAnimation();
|
197 | }, [visible, shouldShowTabBar]);
|
198 |
|
199 | const [layout, setLayout] = _react.default.useState({
|
200 | height: 0,
|
201 | width: dimensions.width
|
202 | });
|
203 |
|
204 | const handleLayout = e => {
|
205 | const {
|
206 | height,
|
207 | width
|
208 | } = e.nativeEvent.layout;
|
209 | onHeightChange === null || onHeightChange === void 0 ? void 0 : onHeightChange(height);
|
210 | setLayout(layout => {
|
211 | if (height === layout.height && width === layout.width) {
|
212 | return layout;
|
213 | } else {
|
214 | return {
|
215 | height,
|
216 | width
|
217 | };
|
218 | }
|
219 | });
|
220 | };
|
221 |
|
222 | const {
|
223 | routes
|
224 | } = state;
|
225 | const paddingBottom = getPaddingBottom(insets);
|
226 | const tabBarHeight = getTabBarHeight({
|
227 | state,
|
228 | descriptors,
|
229 | insets,
|
230 | dimensions,
|
231 | layout,
|
232 | style: [tabBarStyle, style]
|
233 | });
|
234 | const hasHorizontalLabels = shouldUseHorizontalLabels({
|
235 | state,
|
236 | descriptors,
|
237 | dimensions,
|
238 | layout
|
239 | });
|
240 | const tabBarBackgroundElement = tabBarBackground === null || tabBarBackground === void 0 ? void 0 : tabBarBackground();
|
241 | return _react.default.createElement(_reactNative.Animated.View, {
|
242 | style: [styles.tabBar, {
|
243 | backgroundColor: tabBarBackgroundElement != null ? 'transparent' : colors.card,
|
244 | borderTopColor: colors.border
|
245 | }, {
|
246 | transform: [{
|
247 | translateY: visible.interpolate({
|
248 | inputRange: [0, 1],
|
249 | outputRange: [layout.height + paddingBottom + _reactNative.StyleSheet.hairlineWidth, 0]
|
250 | })
|
251 | }],
|
252 |
|
253 |
|
254 | position: isTabBarHidden ? 'absolute' : null
|
255 | }, {
|
256 | height: tabBarHeight,
|
257 | paddingBottom,
|
258 | paddingHorizontal: Math.max(insets.left, insets.right)
|
259 | }, tabBarStyle],
|
260 | pointerEvents: isTabBarHidden ? 'none' : 'auto',
|
261 | onLayout: handleLayout
|
262 | }, _react.default.createElement(_reactNative.View, {
|
263 | pointerEvents: "none",
|
264 | style: _reactNative.StyleSheet.absoluteFill
|
265 | }, tabBarBackgroundElement), _react.default.createElement(_reactNative.View, {
|
266 | accessibilityRole: "tablist",
|
267 | style: styles.content
|
268 | }, routes.map((route, index) => {
|
269 | var _options$tabBarIcon;
|
270 |
|
271 | const focused = index === state.index;
|
272 | const {
|
273 | options
|
274 | } = descriptors[route.key];
|
275 |
|
276 | const onPress = () => {
|
277 | const event = navigation.emit({
|
278 | type: 'tabPress',
|
279 | target: route.key,
|
280 | canPreventDefault: true
|
281 | });
|
282 |
|
283 | if (!focused && !event.defaultPrevented) {
|
284 | navigation.dispatch({ ..._native.CommonActions.navigate({
|
285 | name: route.name,
|
286 | merge: true
|
287 | }),
|
288 | target: state.key
|
289 | });
|
290 | }
|
291 | };
|
292 |
|
293 | const onLongPress = () => {
|
294 | navigation.emit({
|
295 | type: 'tabLongPress',
|
296 | target: route.key
|
297 | });
|
298 | };
|
299 |
|
300 | const label = options.tabBarLabel !== undefined ? options.tabBarLabel : options.title !== undefined ? options.title : route.name;
|
301 | const accessibilityLabel = options.tabBarAccessibilityLabel !== undefined ? options.tabBarAccessibilityLabel : typeof label === 'string' && _reactNative.Platform.OS === 'ios' ? `${label}, tab, ${index + 1} of ${routes.length}` : undefined;
|
302 | return _react.default.createElement(_native.NavigationContext.Provider, {
|
303 | key: route.key,
|
304 | value: descriptors[route.key].navigation
|
305 | }, _react.default.createElement(_native.NavigationRouteContext.Provider, {
|
306 | value: route
|
307 | }, _react.default.createElement(_BottomTabItem.default, {
|
308 | route: route,
|
309 | focused: focused,
|
310 | horizontal: hasHorizontalLabels,
|
311 | onPress: onPress,
|
312 | onLongPress: onLongPress,
|
313 | accessibilityLabel: accessibilityLabel,
|
314 | to: buildLink(route.name, route.params),
|
315 | testID: options.tabBarTestID,
|
316 | allowFontScaling: options.tabBarAllowFontScaling,
|
317 | activeTintColor: tabBarActiveTintColor,
|
318 | inactiveTintColor: tabBarInactiveTintColor,
|
319 | activeBackgroundColor: tabBarActiveBackgroundColor,
|
320 | inactiveBackgroundColor: tabBarInactiveBackgroundColor,
|
321 | button: options.tabBarButton,
|
322 | icon: (_options$tabBarIcon = options.tabBarIcon) !== null && _options$tabBarIcon !== void 0 ? _options$tabBarIcon : _ref5 => {
|
323 | let {
|
324 | color,
|
325 | size
|
326 | } = _ref5;
|
327 | return _react.default.createElement(_elements.MissingIcon, {
|
328 | color: color,
|
329 | size: size
|
330 | });
|
331 | },
|
332 | badge: options.tabBarBadge,
|
333 | badgeStyle: options.tabBarBadgeStyle,
|
334 | label: label,
|
335 | showLabel: tabBarShowLabel,
|
336 | labelStyle: options.tabBarLabelStyle,
|
337 | iconStyle: options.tabBarIconStyle,
|
338 | style: options.tabBarItemStyle
|
339 | })));
|
340 | })));
|
341 | }
|
342 |
|
343 | const styles = _reactNative.StyleSheet.create({
|
344 | tabBar: {
|
345 | left: 0,
|
346 | right: 0,
|
347 | bottom: 0,
|
348 | borderTopWidth: _reactNative.StyleSheet.hairlineWidth,
|
349 | elevation: 8
|
350 | },
|
351 | content: {
|
352 | flex: 1,
|
353 | flexDirection: 'row'
|
354 | }
|
355 | });
|
356 |
|
\ | No newline at end of file |