react-native-modern-elements
Version:
A modern, customizable UI component library for React Native
123 lines (122 loc) • 6.04 kB
JavaScript
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);