UNPKG

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
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;