react-native-tab-view
Version:
Tab view component for React Native
216 lines (215 loc) • 6.35 kB
JavaScript
"use strict";
import * as React from 'react';
import { Animated, Platform, StyleSheet, View } from 'react-native';
import useLatestCallback from 'use-latest-callback';
import { PlatformPressable } from "./PlatformPressable.js";
import { TabBarItemLabel } from "./TabBarItemLabel.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const DEFAULT_ACTIVE_COLOR = 'rgba(255, 255, 255, 1)';
const DEFAULT_INACTIVE_COLOR = 'rgba(255, 255, 255, 0.7)';
const ICON_SIZE = 24;
const getActiveOpacity = (position, routesLength, tabIndex) => {
if (routesLength > 1) {
const inputRange = Array.from({
length: routesLength
}, (_, i) => i);
return position.interpolate({
inputRange,
outputRange: inputRange.map(i => i === tabIndex ? 1 : 0)
});
} else {
return 1;
}
};
const getInactiveOpacity = (position, routesLength, tabIndex) => {
if (routesLength > 1) {
const inputRange = Array.from({
length: routesLength
}, (_, i) => i);
return position.interpolate({
inputRange,
outputRange: inputRange.map(i => i === tabIndex ? 0 : 1)
});
} else {
return 0;
}
};
const ANDROID_RIPPLE_DEFAULT = {
borderless: true
};
const TabBarItemInternal = ({
accessibilityLabel,
accessible,
label: customlabel,
testID,
onLongPress,
onPress,
isFocused,
position,
style,
inactiveColor: inactiveColorCustom,
activeColor: activeColorCustom,
labelStyle,
onLayout,
index: tabIndex,
pressColor,
pressOpacity,
defaultTabWidth,
icon: customIcon,
badge: customBadge,
href,
labelText,
routesLength,
android_ripple = ANDROID_RIPPLE_DEFAULT,
labelAllowFontScaling,
route
}) => {
const labelColorFromStyle = StyleSheet.flatten(labelStyle || {}).color;
const activeColor = activeColorCustom !== undefined ? activeColorCustom : typeof labelColorFromStyle === 'string' ? labelColorFromStyle : DEFAULT_ACTIVE_COLOR;
const inactiveColor = inactiveColorCustom !== undefined ? inactiveColorCustom : typeof labelColorFromStyle === 'string' ? labelColorFromStyle : DEFAULT_INACTIVE_COLOR;
const activeOpacity = getActiveOpacity(position, routesLength, tabIndex);
const inactiveOpacity = getInactiveOpacity(position, routesLength, tabIndex);
const icon = React.useMemo(() => {
if (!customIcon) {
return null;
}
const inactiveIcon = customIcon({
focused: false,
color: inactiveColor,
size: ICON_SIZE,
route
});
const activeIcon = customIcon({
focused: true,
color: activeColor,
size: ICON_SIZE,
route
});
return /*#__PURE__*/_jsxs(View, {
style: styles.icon,
children: [/*#__PURE__*/_jsx(Animated.View, {
style: {
opacity: inactiveOpacity
},
children: inactiveIcon
}), /*#__PURE__*/_jsx(Animated.View, {
style: [StyleSheet.absoluteFill, {
opacity: activeOpacity
}],
children: activeIcon
})]
});
}, [activeColor, activeOpacity, customIcon, inactiveColor, inactiveOpacity, route]);
const renderLabel = React.useCallback(focused => customlabel ? customlabel({
focused,
color: focused ? activeColor : inactiveColor,
style: labelStyle,
labelText,
allowFontScaling: labelAllowFontScaling,
route
}) : /*#__PURE__*/_jsx(TabBarItemLabel, {
color: focused ? activeColor : inactiveColor,
icon: icon,
label: labelText,
style: labelStyle
}), [customlabel, activeColor, labelStyle, labelText, labelAllowFontScaling, route, inactiveColor, icon]);
const tabStyle = StyleSheet.flatten(style);
const isWidthSet = tabStyle?.width !== undefined;
const tabContainerStyle = isWidthSet ? null : {
width: defaultTabWidth
};
const ariaLabel = typeof accessibilityLabel !== 'undefined' ? accessibilityLabel : labelText;
return /*#__PURE__*/_jsx(PlatformPressable, {
android_ripple: android_ripple,
testID: testID,
accessible: accessible,
role: "tab",
"aria-label": ariaLabel,
"aria-selected": isFocused,
pressColor: pressColor,
pressOpacity: pressOpacity,
unstable_pressDelay: 0,
onLayout: onLayout,
onPress: onPress,
onLongPress: onLongPress,
href: href,
style: [styles.pressable, tabContainerStyle],
children: /*#__PURE__*/_jsxs(View, {
pointerEvents: "none",
style: [styles.item, tabStyle],
children: [icon, /*#__PURE__*/_jsxs(View, {
children: [/*#__PURE__*/_jsx(Animated.View, {
style: {
opacity: inactiveOpacity
},
children: renderLabel(false)
}), /*#__PURE__*/_jsx(Animated.View, {
style: [StyleSheet.absoluteFill, {
opacity: activeOpacity
}],
children: renderLabel(true)
})]
}), customBadge != null ? /*#__PURE__*/_jsx(View, {
style: styles.badge,
children: customBadge({
route
})
}) : null]
})
});
};
const MemoizedTabBarItemInternal = /*#__PURE__*/React.memo(TabBarItemInternal);
export function TabBarItem(props) {
const {
onPress,
onLongPress,
onLayout,
navigationState,
route,
...rest
} = props;
const onPressLatest = useLatestCallback(onPress);
const onLongPressLatest = useLatestCallback(onLongPress);
const onLayoutLatest = useLatestCallback(onLayout ? onLayout : () => {});
const tabIndex = navigationState.routes.indexOf(route);
return /*#__PURE__*/_jsx(MemoizedTabBarItemInternal, {
...rest,
onPress: onPressLatest,
onLayout: onLayoutLatest,
onLongPress: onLongPressLatest,
isFocused: navigationState.index === tabIndex,
route: route,
index: tabIndex,
routesLength: navigationState.routes.length
});
}
const styles = StyleSheet.create({
icon: {
margin: 2
},
item: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 10,
minHeight: 48
},
badge: {
position: 'absolute',
top: 0,
end: 0
},
pressable: {
// The label is not pressable on Windows
// Adding backgroundColor: 'transparent' seems to fix it
backgroundColor: 'transparent',
...Platform.select({
// Roundness for iPad hover effect
ios: {
borderRadius: 10
},
default: null
})
}
});
//# sourceMappingURL=TabBarItem.js.map