UNPKG

react-native-modern-elements

Version:

A modern, customizable UI component library for React Native

123 lines (122 loc) 6.04 kB
import React, { memo, useEffect, useRef, useState } from "react"; import { Dimensions, ScrollView, StyleSheet, Text, TouchableOpacity, View, } from "react-native"; import PagerView from "react-native-pager-view"; import Animated, { useAnimatedStyle, withTiming, } from "react-native-reanimated"; import { verticalScale } from "../utils/styling"; const screenWidth = Dimensions.get("window").width; // --------------------------------------------- // CHILD COMPONENT // --------------------------------------------- const TabItem = ({ tab, isActive, onPress, onLayout, activeColor, inactiveColor, variant, tooltipActiveColor, tooltipInactiveColor, buttonStyle, buttonTextStyle, buttonContainerStyle, customRender, }) => { const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: withTiming(isActive ? 1.06 : 1, { duration: 200 }) }], })); // If custom render exists, use it if (customRender) { return (React.createElement(View, { onLayout: (e) => { const { x, width } = e.nativeEvent.layout; onLayout({ x, width }); } }, customRender({ tab, isActive, onPress }))); } return (React.createElement(TouchableOpacity, { disabled: tab.disabled, onPress: onPress, onLayout: (e) => { const { x, width } = e.nativeEvent.layout; onLayout({ x, width }); }, style: [buttonContainerStyle] }, React.createElement(Animated.View, { style: [ styles.button, { backgroundColor: isActive ? activeColor : inactiveColor, borderRadius: variant === "pill" ? 30 : 5, }, buttonStyle, animatedStyle, ] }, tab.icon, React.createElement(Text, { style: [ { fontWeight: "600", color: isActive ? "white" : "black", }, buttonTextStyle, ] }, tab.label), tab.tooltip !== undefined && (React.createElement(Text, { style: { marginLeft: 5, paddingHorizontal: 6, fontSize: verticalScale(11), borderRadius: 20, backgroundColor: isActive ? tooltipActiveColor : tooltipInactiveColor, color: isActive ? "black" : tooltipActiveColor, } }, tab.tooltip))))); }; // --------------------------------------------- // MAIN COMPONENT // --------------------------------------------- const TabSlider = ({ tabs, children, initialPage = 0, activeColor = "green", inactiveColor = "white", variant = "pill", tooltipActiveColor = "white", tooltipInactiveColor = "green", /** NEW */ alignTabs = "left", buttonStyle, buttonTextStyle, buttonContainerStyle, renderTabItem, header, footer, }) => { const pagerRef = useRef(null); const scrollRef = useRef(null); const [activeIndex, setActiveIndex] = useState(initialPage); const [layouts, setLayouts] = useState(Array(tabs.length).fill({ x: 0, width: 0 })); const goToPage = (index, disabled) => { var _a; if (disabled) return; (_a = pagerRef.current) === null || _a === void 0 ? void 0 : _a.setPage(index); }; const centerActiveTab = () => { var _a; const layout = layouts[activeIndex]; if (!layout) return; const offset = layout.x + layout.width / 2 - screenWidth / 2; (_a = scrollRef.current) === null || _a === void 0 ? void 0 : _a.scrollTo({ x: Math.max(0, offset), animated: true, }); }; useEffect(() => { centerActiveTab(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [activeIndex, layouts]); // --------------------------------------------- // NEW: Tab Alignment System // --------------------------------------------- const alignmentStyle = { justifyContent: alignTabs === "center" ? "center" : alignTabs === "right" ? "flex-end" : "flex-start", }; return (React.createElement(View, { style: { flex: 1 } }, React.createElement(View, { style: { height: 60 } }, React.createElement(ScrollView, { horizontal: true, ref: scrollRef, showsHorizontalScrollIndicator: false, contentContainerStyle: [ { alignItems: "center", flexGrow: 1 }, alignmentStyle, ] }, header, tabs.map((tab, index) => (React.createElement(TabItem, { key: index, tab: tab, index: index, isActive: activeIndex === index, onPress: () => goToPage(index, tab.disabled), onLayout: (layout) => { const updated = [...layouts]; updated[index] = layout; setLayouts(updated); }, activeColor: activeColor, inactiveColor: inactiveColor, variant: variant, tooltipActiveColor: tooltipActiveColor, tooltipInactiveColor: tooltipInactiveColor, buttonStyle: buttonStyle, buttonTextStyle: buttonTextStyle, buttonContainerStyle: buttonContainerStyle, customRender: renderTabItem }))), footer)), React.createElement(View, { style: { flex: 1 } }, React.createElement(PagerView, { ref: pagerRef, initialPage: initialPage, style: { flex: 1, borderWidth: 1 }, onPageSelected: (e) => setActiveIndex(e.nativeEvent.position) }, children.map((child, idx) => (React.createElement(View, { key: idx, style: { flex: 1 } }, child))))))); }; // --------------------------------------------- // STYLES // --------------------------------------------- const styles = StyleSheet.create({ button: { paddingVertical: 10, paddingHorizontal: 20, marginHorizontal: 5, flexDirection: "row", alignItems: "center", }, }); export default memo(TabSlider);