一、button
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
disabled:
"disabled-foreground bg-disabled text-disabled-foreground",
ghost: "hover:bg-accent focus-visible:ring-0 focus-visible:ring-offset-0",
link: "hover:text-primary underline-offset-4",
icon: "border border-input",
},
size: {
default: "h-8 px-5 py-1.5",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-6 w-6",
iconSm: "h-8 w-8",
ssm: "h-6",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
},
)
Button.displayName = "Button"
export { Button, buttonVariants }
这段代码定义了一个可变样式的按钮组件Button
,使用了多个工具和库。我们将逐步解释各部分代码的作用。
1. 导入部分
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
React
: 导入React库。Slot
: 从@radix-ui/react-slot
库中导入Slot
组件,用于支持“asChild”属性。cva
和VariantProps
: 从class-variance-authority
库中导入,用于定义可变样式。cn
: 从项目中的utils
工具库导入cn
函数,用于合并CSS类名。
2. 定义按钮的样式变体
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
disabled: "disabled-foreground bg-disabled text-disabled-foreground",
ghost: "hover:bg-accent focus-visible:ring-0 focus-visible:ring-offset-0",
link: "hover:text-primary underline-offset-4",
icon: "border border-input",
},
size: {
default: "h-8 px-5 py-1.5",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-6 w-6",
iconSm: "h-8 w-8",
ssm: "h-6",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
)
buttonVariants
: 使用cva
函数定义了按钮的样式变体。这个对象包含了两个主要部分:variants
: 定义了不同的变体选项,如variant
和size
,每个选项又包含不同的具体样式。defaultVariants
: 定义了默认的变体值。
当然,让我们逐一解释variants
对象中的每个属性及其对应的CSS属性值。
1. variant
variant
属性定义了按钮的多种视觉风格,每种风格都对应一组CSS类名。
default
bg-primary { background-color: var(--primary-color); } text-primary-foreground { color: var(--primary-foreground-color); } hover:bg-primary/90 { background-color: var(--primary-color); opacity: 0.9; }
destructive
bg-destructive { background-color: var(--destructive-color); } text-destructive-foreground { color: var(--destructive-foreground-color); } hover:bg-destructive/90 { background-color: var(--destructive-color); opacity: 0.9; }
outline
border border-input { border: 1px solid var(--input-border-color); } bg-background { background-color: var(--background-color); } hover:bg-accent { background-color: var(--accent-color); } hover:text-accent-foreground { color: var(--accent-foreground-color); }
secondary
bg-secondary { background-color: var(--secondary-color); } text-secondary-foreground { color: var(--secondary-foreground-color); } hover:bg-secondary/80 { background-color: var(--secondary-color); opacity: 0.8; }
disabled
disabled-foreground { color: var(--disabled-foreground-color); } bg-disabled { background-color: var(--disabled-color); } text-disabled-foreground { color: var(--disabled-foreground-color); }
ghost
hover:bg-accent { background-color: var(--accent-color); } focus-visible:ring-0 { outline: none; box-shadow: none; } focus-visible:ring-offset-0 { box-shadow: none; }
link
hover:text-primary { color: var(--primary-color); } underline-offset-4 { text-underline-offset: 4px; }
icon
border border-input { border: 1px solid var(--input-border-color); }
2. size
size
属性定义了按钮的不同尺寸,每个尺寸都对应一组CSS类名。
default
h-8 { height: 2rem; /* 32px */ } px-5 { padding-left: 1.25rem; /* 20px */ padding-right: 1.25rem; /* 20px */ } py-1.5 { padding-top: 0.375rem; /* 6px */ padding-bottom: 0.375rem; /* 6px */ }
sm
h-9 { height: 2.25rem; /* 36px */ } rounded-md { border-radius: 0.375rem; /* 6px */ } px-3 { padding-left: 0.75rem; /* 12px */ padding-right: 0.75rem; /* 12px */ }
lg
h-11 { height: 2.75rem; /* 44px */ } rounded-md { border-radius: 0.375rem; /* 6px */ } px-8 { padding-left: 2rem; /* 32px */ padding-right: 2rem; /* 32px */ }
icon
h-6 { height: 1.5rem; /* 24px */ } w-6 { width: 1.5rem; /* 24px */ }
iconSm
h-8 { height: 2rem; /* 32px */ } w-8 { width: 2rem; /* 32px */ }
ssm
h-6 { height: 1.5rem; /* 24px */ }
总结
这些variants
属性提供了丰富的样式变体,使得按钮组件可以根据不同的需求应用不同的外观和尺寸,通过简单的属性传递实现了多样化的视觉效果。
3. 定义按钮的属性类型
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
ButtonProps
: 定义了按钮组件的属性接口,扩展了React.ButtonHTMLAttributes
和VariantProps
。另外,还增加了asChild
属性,用于指定是否将按钮作为子组件渲染。
4. 定义按钮组件
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
},
)
Button.displayName = "Button"
Button
: 使用React.forwardRef
定义一个带有转发引用(ref)的按钮组件。Comp
: 根据asChild
属性,动态决定使用Slot
组件还是button
元素。cn(buttonVariants({ variant, size, className }))
: 使用cn
函数合并传入的className
和通过buttonVariants
生成的变体样式。
5. 导出组件和样式变体
export { Button, buttonVariants }
Button
和buttonVariants
均被导出,允许在其他模块中使用。
总结
这个组件使用了class-variance-authority
库来管理按钮的样式变体,通过React的forwardRef
和条件渲染实现了灵活的按钮组件。这样,开发者可以通过简单的属性传递来改变按钮的外观和行为。