202 lines
4.9 KiB
TypeScript
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",
|
|
},
|
|
});
|