Files
workspace/code/app/app-rn4/src/components/layout/navbar.tsx
T
2026-04-21 17:29:49 +08:00

202 lines
4.9 KiB
TypeScript

import React from "react";
import { View, Text, StyleProp, ViewStyle, Pressable, StyleSheet } from "react-native";
import { cn } from "@/utils/cn";
import Animated, { useSharedValue, useAnimatedStyle, withSpring } from "react-native-reanimated";
import { Shadow } from "react-native-shadow-2";
import { ChevronLeft, SlidersHorizontal, CirclePower, LucideIcon } from "lucide-react-native";
interface NavbarProps {
title?: string;
titleIcon?: LucideIcon;
showBack?: boolean;
onBack?: () => void;
onClose?: () => void;
className?: string;
style?: StyleProp<ViewStyle>;
}
const AnimatedView = Animated.View;
export default function Navbar({
title,
titleIcon,
showBack = true,
onBack,
onClose,
className,
style,
}: NavbarProps) {
const TitleIcon = titleIcon;
// 返回按钮
const scale = useSharedValue(1);
const backAnimatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));
// 右侧按钮动画
const leftScale = useSharedValue(1);
const rightScale = useSharedValue(1);
const leftStyle = useAnimatedStyle(() => ({
transform: [{ scale: leftScale.value }],
opacity: leftScale.value === 1 ? 1 : 0.7,
}));
const rightStyle = useAnimatedStyle(() => ({
transform: [{ scale: rightScale.value }],
opacity: rightScale.value === 1 ? 1 : 0.7,
}));
return (
<View
className={cn("flex-row gap-3 items-center justify-between mx-3", className)}
style={[
{
...style,
},
]}
>
<View className="flex-1 flex-row gap-3">
{showBack && (
<Pressable
onPress={onBack}
onPressIn={() => {
scale.value = withSpring(0.8);
}}
onPressOut={() => {
scale.value = withSpring(1);
}}
>
<AnimatedView style={backAnimatedStyle}>
<Shadow
distance={20}
startColor="rgba(248,248,248,1)"
endColor="rgba(248,248,248,0)"
offset={[0, 0]}
paintInside={false}
safeRender
style={styles.iconButtonShadow}
>
<ChevronLeft size={24} />
</Shadow>
</AnimatedView>
</Pressable>
)}
{title && (
<Shadow
distance={20}
startColor="rgba(248,248,248,1)"
endColor="rgba(248,248,248,0)"
offset={[0, 0]}
paintInside={false}
safeRender
style={{
...styles.titleShadown,
alignContent: "center",
justifyContent: "center",
maxWidth: 180,
gap: 4,
}}
>
{TitleIcon && <TitleIcon size={18} color="#000" />}
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{
fontSize: 18,
fontWeight: "600",
color: "#111",
}}
>
{title}
</Text>
</Shadow>
)}
</View>
<Shadow
distance={20}
startColor="rgba(248,248,248,1)"
endColor="rgba(248,248,248,0)"
offset={[0, 0]}
paintInside={false}
safeRender
style={styles.groupShadow}
>
<Pressable
onPressIn={() => {
leftScale.value = withSpring(0.8);
}}
onPressOut={() => {
leftScale.value = withSpring(1);
}}
>
<AnimatedView style={leftStyle} className="w-14 items-center justify-center">
<SlidersHorizontal size={20} />
</AnimatedView>
</Pressable>
<View className="w-px h-4 bg-black/10" />
<Pressable
onPress={onClose}
onPressIn={() => {
rightScale.value = withSpring(0.8);
}}
onPressOut={() => {
rightScale.value = withSpring(1);
}}
>
<AnimatedView style={rightStyle} className="w-14 items-center justify-center">
<CirclePower size={20} />
</AnimatedView>
</Pressable>
</Shadow>
</View>
);
}
const styles = StyleSheet.create({
iconButtonShadow: {
borderRadius: 999,
width: 36,
height: 36,
alignItems: "center",
justifyContent: "center",
borderWidth: 0.5,
borderColor: "rgba(255,255,255,0.6)",
backgroundColor: "#fff",
},
groupShadow: {
borderRadius: 999,
height: 36,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
borderWidth: 0.5,
borderColor: "rgba(255,255,255,0.6)",
backgroundColor: "#fff",
},
titleShadown: {
borderRadius: 999,
height: 36,
flexDirection: "row",
alignItems: "center",
justifyContent: "flex-start",
borderWidth: 0.5,
borderColor: "rgba(255,255,255,0.6)",
paddingHorizontal: 16,
backgroundColor: "#fff",
},
});