react-native-beauty-tabs
Version:
A customizable and animated tab component for React Native with beautiful sliding indicators and swipeable content.
142 lines (141 loc) • 5.76 kB
JavaScript
import React, { useRef, useState, useEffect } from "react";
import { View, Text, TouchableOpacity, StyleSheet, Dimensions, ScrollView, Animated as RNAnimated, } from "react-native";
const NativeTabs = ({ Tabs = [], ContainerStyle, Tabcontainer, TabTextStyle, TabContentStyle, IndicatorStyle, ActiveTextColors = "#fff", InActiveTextColors = "#007aff", TabsRowStyle, PageContentStyle, widthRatio = 0.9, // ✅ Default to 90% width
}) => {
const scrollRef = useRef(null);
const [active, setActive] = useState(0);
const [screenWidth, setScreenWidth] = useState(Dimensions.get("window").width);
const indicatorTranslateX = useRef(new RNAnimated.Value(0)).current;
const isTabClick = useRef(false);
const tabsContainerWidth = screenWidth * widthRatio;
const handleTabPress = (index) => {
var _a;
isTabClick.current = true;
(_a = scrollRef.current) === null || _a === void 0 ? void 0 : _a.scrollTo({ x: screenWidth * index, animated: true });
setActive(index);
animateIndicator(index, screenWidth);
setTimeout(() => {
isTabClick.current = false;
}, 0);
};
const animateIndicator = (index, width) => {
const containerWidth = width * widthRatio;
RNAnimated.spring(indicatorTranslateX, {
toValue: (containerWidth / Tabs.length) * index,
useNativeDriver: false,
}).start();
};
const handleMomentumScrollEnd = (event) => {
if (isTabClick.current)
return;
const offsetX = event.nativeEvent.contentOffset.x;
const index = Math.round(offsetX / screenWidth);
if (index !== active) {
setActive(index);
animateIndicator(index, screenWidth);
}
};
useEffect(() => {
const onChange = ({ window }) => {
setScreenWidth(window.width);
animateIndicator(active, window.width);
};
const subscription = Dimensions.addEventListener("change", onChange);
return () => subscription.remove();
}, [active]);
return (React.createElement(View, { style: [ContainerStyle ? ContainerStyle : styles.ContainerStyle] },
React.createElement(View, { style: [
{ width: tabsContainerWidth },
Tabcontainer ? Tabcontainer : styles.TabsContainer,
] },
React.createElement(View, { style: [styles.tabsRow, TabsRowStyle ? TabsRowStyle : null] },
React.createElement(RNAnimated.View, { style: [
styles.Indicator,
{
width: `${100 / Tabs.length}%`,
transform: [{ translateX: indicatorTranslateX }],
},
IndicatorStyle ? IndicatorStyle : null,
] }),
Tabs.map((tab, index) => (React.createElement(TouchableOpacity, { key: index, style: styles.tabButton, onPress: () => handleTabPress(index), activeOpacity: 0.8 },
React.createElement(View, { style: [
TabContentStyle ? TabContentStyle : styles.TabContentStyle,
] },
(tab === null || tab === void 0 ? void 0 : tab.Icons) &&
React.cloneElement(tab.Icons, {
color: active === index ? ActiveTextColors : InActiveTextColors,
}),
React.createElement(Text, { style: [
TabTextStyle ? TabTextStyle : styles.TabTextStyle,
{
color: active === index
? ActiveTextColors
: InActiveTextColors,
},
] }, tab.label))))))),
React.createElement(ScrollView, { ref: scrollRef, horizontal: true, pagingEnabled: true, onMomentumScrollEnd: handleMomentumScrollEnd, scrollEventThrottle: 16, showsHorizontalScrollIndicator: false, contentContainerStyle: { flexGrow: 1 } }, Tabs.map((tab, index) => (React.createElement(View, { key: index, style: [
PageContentStyle ? PageContentStyle : styles.PageContentStyle,
{ width: screenWidth },
] }, tab.Content))))));
};
const styles = StyleSheet.create({
ContainerStyle: {
flex: 1,
alignSelf: "center",
backgroundColor: "#fff",
gap: 10,
},
TabsContainer: {
alignSelf: "center",
marginTop: 10,
},
tabsRow: {
flexDirection: "row",
position: "relative",
borderRadius: 6,
// overflow: "hidden",
borderWidth: 1,
borderColor: "#007aff",
backgroundColor: "#fff",
// ✅ Android shadow
elevation: 5,
// ✅ iOS shadow
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.2,
shadowRadius: 4,
},
TabTextStyle: {
fontWeight: "600",
fontSize: 20,
},
TabContentStyle: {
flexDirection: "column",
gap: 4,
alignItems: "center",
justifyContent: "center",
},
tabButton: {
flex: 1,
paddingVertical: 10,
alignItems: "center",
justifyContent: "center",
zIndex: 1,
},
Indicator: {
position: "absolute",
height: "100%",
backgroundColor: "#007aff",
top: 0,
left: 0,
zIndex: 0,
borderRadius: 6,
},
PageContentStyle: {
padding: 20,
},
});
export default NativeTabs;