117 lines
2.9 KiB
TypeScript
117 lines
2.9 KiB
TypeScript
import * as React from "react";
|
|
import { Pressable, Text, type PressableProps, type ViewStyle } from "react-native";
|
|
import { LinearGradient } from "expo-linear-gradient";
|
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
|
|
import { cn } from "../../lib/utils";
|
|
|
|
const buttonVariants = cva(
|
|
"flex-row items-center justify-center rounded-md active:opacity-70",
|
|
{
|
|
variants: {
|
|
variant: {
|
|
default: "bg-primary",
|
|
destructive: "bg-destructive",
|
|
outline: "border border-input bg-background",
|
|
secondary: "bg-secondary",
|
|
ghost: "active:bg-accent",
|
|
link: "",
|
|
gradient: "",
|
|
},
|
|
size: {
|
|
default: "h-10 px-4 py-2",
|
|
sm: "h-9 px-3",
|
|
lg: "h-11 px-8",
|
|
icon: "h-10 w-10",
|
|
},
|
|
},
|
|
defaultVariants: {
|
|
variant: "default",
|
|
size: "default",
|
|
},
|
|
}
|
|
);
|
|
|
|
const buttonTextVariants = cva("text-sm font-medium text-center", {
|
|
variants: {
|
|
variant: {
|
|
default: "text-primary-foreground",
|
|
destructive: "text-destructive-foreground",
|
|
outline: "text-foreground",
|
|
secondary: "text-secondary-foreground",
|
|
ghost: "text-foreground",
|
|
link: "text-primary",
|
|
gradient: "text-white",
|
|
},
|
|
},
|
|
defaultVariants: {
|
|
variant: "default",
|
|
},
|
|
});
|
|
|
|
export interface ButtonProps
|
|
extends Omit<PressableProps, "style">,
|
|
VariantProps<typeof buttonVariants> {
|
|
children?: React.ReactNode;
|
|
style?: ViewStyle;
|
|
}
|
|
|
|
const Button = React.forwardRef<
|
|
React.ElementRef<typeof Pressable>,
|
|
ButtonProps
|
|
>(({ className, variant, size, children, disabled, style, ...props }, ref) => {
|
|
const isGradient = variant === "gradient";
|
|
|
|
const content = typeof children === "string" ? (
|
|
<Text className={cn(buttonTextVariants({ variant }))}>{children}</Text>
|
|
) : (
|
|
children
|
|
);
|
|
|
|
// 合并样式,确保 borderRadius 正确应用
|
|
const borderRadius = style?.borderRadius ?? (isGradient ? 12 : undefined);
|
|
const mergedStyle = borderRadius
|
|
? [style, { borderRadius }]
|
|
: style;
|
|
|
|
return (
|
|
<Pressable
|
|
ref={ref}
|
|
className={cn(
|
|
buttonVariants({ variant, size }),
|
|
disabled && "opacity-50",
|
|
isGradient && "overflow-hidden",
|
|
className
|
|
)}
|
|
disabled={disabled}
|
|
style={mergedStyle}
|
|
{...props}
|
|
>
|
|
{isGradient ? (
|
|
<LinearGradient
|
|
colors={["#9966FF", "#FF6699", "#FF9966"]}
|
|
locations={[0.0015, 0.4985, 0.9956]}
|
|
start={{ x: 1, y: 0 }}
|
|
end={{ x: 0, y: 0 }}
|
|
style={{
|
|
flex: 1,
|
|
alignSelf: "stretch",
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
borderRadius: borderRadius ?? 12,
|
|
}}
|
|
>
|
|
{content}
|
|
</LinearGradient>
|
|
) : (
|
|
content
|
|
)}
|
|
</Pressable>
|
|
);
|
|
});
|
|
|
|
Button.displayName = "Button";
|
|
|
|
export { Button, buttonVariants };
|