130 lines
2.7 KiB
TypeScript
130 lines
2.7 KiB
TypeScript
import type { Href } from "expo-router";
|
|
import { usePathname, useRouter } from "expo-router";
|
|
import { useEffect, useState } from "react";
|
|
import { Pressable, Text, View } from "react-native";
|
|
import Animated, {
|
|
useAnimatedStyle,
|
|
useSharedValue,
|
|
withSequence,
|
|
withTiming,
|
|
} from "react-native-reanimated";
|
|
import * as icons from "lucide-react-native/icons";
|
|
import { BlurView } from "expo-blur";
|
|
|
|
export interface TabbarItem {
|
|
name: string;
|
|
href: Href;
|
|
icon: keyof typeof icons;
|
|
label: string;
|
|
onPress?: () => void;
|
|
}
|
|
|
|
interface AppTabbarProps {
|
|
data: TabbarItem[];
|
|
}
|
|
|
|
function TabButton({
|
|
iconName,
|
|
label,
|
|
isFocused = false,
|
|
onPress,
|
|
}: {
|
|
iconName: keyof typeof icons;
|
|
label: string;
|
|
isFocused?: boolean;
|
|
onPress?: () => void;
|
|
}) {
|
|
const scale = useSharedValue(1);
|
|
|
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
transform: [{ scale: scale.value }],
|
|
}));
|
|
|
|
useEffect(() => {
|
|
if (isFocused) {
|
|
scale.value = withSequence(
|
|
withTiming(0.9, { duration: 120 }),
|
|
withTiming(1, { duration: 160 }),
|
|
);
|
|
}
|
|
}, [isFocused]);
|
|
|
|
const LucideIcon = icons[iconName];
|
|
|
|
return (
|
|
<Pressable
|
|
onPress={onPress}
|
|
style={{
|
|
flex: 1,
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
}}
|
|
>
|
|
<Animated.View style={animatedStyle}>
|
|
<LucideIcon size={22} color={isFocused ? "#111" : "#999"} strokeWidth={2} />
|
|
|
|
<Text
|
|
style={{
|
|
fontSize: 10,
|
|
marginTop: 2,
|
|
color: isFocused ? "#111" : "#999",
|
|
}}
|
|
>
|
|
{label}
|
|
</Text>
|
|
</Animated.View>
|
|
</Pressable>
|
|
);
|
|
}
|
|
|
|
export default function CustomTabbar({ data }: AppTabbarProps) {
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
|
|
const [activeTab, setActiveTab] = useState("");
|
|
|
|
useEffect(() => {
|
|
const match = data.find((item) => item.href === pathname);
|
|
if (match) {
|
|
setActiveTab(match.name);
|
|
}
|
|
}, [pathname, data]);
|
|
|
|
const handlePressTab = (item: TabbarItem) => {
|
|
setActiveTab(item.name);
|
|
router.push(item.href);
|
|
item.onPress?.();
|
|
};
|
|
|
|
return (
|
|
<View
|
|
style={{
|
|
position: "absolute",
|
|
bottom: 0,
|
|
left: 0,
|
|
right: 0,
|
|
}}
|
|
>
|
|
<BlurView
|
|
intensity={90}
|
|
tint="light"
|
|
blurMethod="dimezisBlurView"
|
|
style={{
|
|
flexDirection: "row",
|
|
height: 56,
|
|
}}
|
|
>
|
|
{data.map((item) => (
|
|
<TabButton
|
|
key={item.name}
|
|
iconName={item.icon}
|
|
label={item.label}
|
|
isFocused={activeTab === item.name}
|
|
onPress={() => handlePressTab(item)}
|
|
/>
|
|
))}
|
|
</BlurView>
|
|
</View>
|
|
);
|
|
}
|