UNPKG

11.5 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = BottomTabBar;
7exports.getTabBarHeight = void 0;
8
9var _elements = require("@react-navigation/elements");
10
11var _native = require("@react-navigation/native");
12
13var _react = _interopRequireDefault(require("react"));
14
15var _reactNative = require("react-native");
16
17var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
18
19var _BottomTabBarHeightCallbackContext = _interopRequireDefault(require("../utils/BottomTabBarHeightCallbackContext"));
20
21var _useIsKeyboardShown = _interopRequireDefault(require("../utils/useIsKeyboardShown"));
22
23var _BottomTabItem = _interopRequireDefault(require("./BottomTabItem"));
24
25function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26
27const DEFAULT_TABBAR_HEIGHT = 49;
28const COMPACT_TABBAR_HEIGHT = 32;
29const DEFAULT_MAX_TAB_ITEM_WIDTH = 125;
30const useNativeDriver = _reactNative.Platform.OS !== 'web';
31
32const 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 // Screen size matches a tablet
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
78const getPaddingBottom = insets => Math.max(insets.bottom - _reactNative.Platform.select({
79 ios: 4,
80 default: 0
81}), 0);
82
83const getTabBarHeight = _ref2 => {
84 var _StyleSheet$flatten;
85
86 let {
87 state,
88 descriptors,
89 dimensions,
90 insets,
91 style,
92 ...rest
93 } = _ref2;
94 // @ts-ignore
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
117exports.getTabBarHeight = getTabBarHeight;
118
119function 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 /*#__PURE__*/_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 // Absolutely position the tab bar so that the content is below it
253 // This is needed to avoid gap at bottom when the tab bar is hidden
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 }, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
263 pointerEvents: "none",
264 style: _reactNative.StyleSheet.absoluteFill
265 }, tabBarBackgroundElement), /*#__PURE__*/_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 /*#__PURE__*/_react.default.createElement(_native.NavigationContext.Provider, {
303 key: route.key,
304 value: descriptors[route.key].navigation
305 }, /*#__PURE__*/_react.default.createElement(_native.NavigationRouteContext.Provider, {
306 value: route
307 }, /*#__PURE__*/_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 /*#__PURE__*/_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
343const 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//# sourceMappingURL=BottomTabBar.js.map
\No newline at end of file