按钮
Block Button
Button component with multiple variants, colors, and states
Button 组件
配置按钮属性,预览即时更新
按钮配置
预览
"use client";
import React, { useState } from "react";
import { Button } from "@/components/composed/block-button/block-button";
import {
Send,
Download,
ArrowRight,
ArrowLeft,
Save,
Trash2,
Plus,
RefreshCw,
Settings,
Eye,
EyeOff,
Home,
User,
Star,
Heart,
Mail,
Search,
Bell,
PlusCircle,
MinusCircle,
Check,
X,
} from "lucide-react";
// 可选图标列表
const ICONS = {
none: null,
Send,
Download,
ArrowRight,
ArrowLeft,
Save,
Trash2,
Plus,
RefreshCw,
Settings,
Eye,
EyeOff,
Home,
User,
Star,
Heart,
Mail,
Search,
Bell,
PlusCircle,
MinusCircle,
Check,
X,
};
const VARIANTS = [
{ value: "solid", label: "实心" },
{ value: "text", label: "文字" },
{ value: "outline", label: "边框" },
{ value: "link", label: "链接" },
];
const COLORS = [
{ value: "primary", label: "主色" },
{ value: "secondary", label: "次要" },
{ value: "danger", label: "危险" },
];
const SIZES = [
{ value: "sm", label: "小" },
{ value: "md", label: "中" },
{ value: "lg", label: "大" },
];
const ICON_OPTIONS = Object.keys(ICONS).map((key) => ({
value: key,
label: key.charAt(0).toUpperCase() + key.slice(1).replace(/([A-Z])/g, " $1"),
}));
export function ButtonDemo() {
// 按钮配置状态
const [config, setConfig] = useState({
variant: "solid",
color: "primary",
size: "md",
disabled: false,
loading: false,
block: false,
icon: "none",
iconRight: "none",
text: "按钮",
});
const updateConfig = (key: string, value: unknown) => {
setConfig((prev) => ({ ...prev, [key]: value }));
};
const SelectedIcon = ICONS[config.icon as keyof typeof ICONS];
const SelectedIconRight = ICONS[config.iconRight as keyof typeof ICONS];
// 生成代码
const generateCode = () => {
const props = [];
if (config.variant !== "solid") props.push(`variant="${config.variant}"`);
if (config.color !== "primary") props.push(`color="${config.color}"`);
if (config.size !== "md") props.push(`size="${config.size}"`);
if (config.disabled) props.push("disabled");
if (config.loading) props.push("loading");
if (config.block) props.push("block");
if (config.icon !== "none") props.push(`icon={${config.icon}}`);
if (config.iconRight !== "none")
props.push(`iconRight={${config.iconRight}}`);
const propsString = props.length > 0 ? "\n " + props.join("\n ") : "";
return `<Button${propsString}>\n ${config.text}\n</Button>`;
};
const [showCode, setShowCode] = useState(false);
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto p-6">
{/* 标题 */}
<div className="mb-8">
<h1 className="text-2xl font-bold text-gray-900 mb-2">Button 组件</h1>
<p className="text-gray-600">配置按钮属性,预览即时更新</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* 配置面板 */}
<div className="lg:col-span-1 bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<h2 className="text-lg font-semibold text-gray-900 mb-6">
按钮配置
</h2>
{/* 变体类型 */}
<div className="mb-6">
<label className="block text-sm font-medium text-gray-700 mb-2">
按钮类型
</label>
<div className="grid grid-cols-2 gap-2">
{VARIANTS.map((v) => (
<button
key={v.value}
onClick={() => updateConfig("variant", v.value)}
className={`px-3 py-2 text-sm rounded-md border transition-all ${
config.variant === v.value
? "border-blue-500 bg-blue-50 text-blue-700 font-medium"
: "border-gray-300 text-gray-700 hover:bg-gray-50"
}`}
>
{v.label}
</button>
))}
</div>
</div>
{/* 颜色 */}
<div className="mb-6">
<label className="block text-sm font-medium text-gray-700 mb-2">
颜色
</label>
<div className="flex gap-2">
{COLORS.map((c) => (
<button
key={c.value}
onClick={() => updateConfig("color", c.value)}
className={`flex-1 px-3 py-2 text-sm rounded-md border transition-all ${
config.color === c.value
? "border-blue-500 bg-blue-50 text-blue-700 font-medium"
: "border-gray-300 text-gray-700 hover:bg-gray-50"
}`}
>
{c.label}
</button>
))}
</div>
</div>
{/* 尺寸 */}
<div className="mb-6">
<label className="block text-sm font-medium text-gray-700 mb-2">
尺寸
</label>
<div className="flex gap-2">
{SIZES.map((s) => (
<button
key={s.value}
onClick={() => updateConfig("size", s.value)}
className={`flex-1 px-3 py-2 text-sm rounded-md border transition-all ${
config.size === s.value
? "border-blue-500 bg-blue-50 text-blue-700 font-medium"
: "border-gray-300 text-gray-700 hover:bg-gray-50"
}`}
>
{s.label}
</button>
))}
</div>
</div>
{/* 图标配置 */}
<div className="mb-6">
<label className="block text-sm font-medium text-gray-700 mb-2">
左侧图标
</label>
<select
value={config.icon}
onChange={(e) => updateConfig("icon", e.target.value)}
className="w-full px-3 py-2 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white"
>
{ICON_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
<div className="mb-6">
<label className="block text-sm font-medium text-gray-700 mb-2">
右侧图标
</label>
<select
value={config.iconRight}
onChange={(e) => updateConfig("iconRight", e.target.value)}
className="w-full px-3 py-2 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white"
>
{ICON_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
{/* 按钮文字 */}
<div className="mb-6">
<label className="block text-sm font-medium text-gray-700 mb-2">
按钮文字
</label>
<input
type="text"
value={config.text}
onChange={(e) => updateConfig("text", e.target.value)}
className="w-full px-3 py-2 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="输入按钮文字"
/>
</div>
{/* 开关选项 */}
<div className="mb-2">
<label className="block text-sm font-medium text-gray-700 mb-3">
选项
</label>
<div className="space-y-3">
<label className="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={config.disabled}
onChange={(e) => updateConfig("disabled", e.target.checked)}
className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500"
/>
<span className="text-sm text-gray-700">禁用</span>
</label>
<label className="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={config.loading}
onChange={(e) => updateConfig("loading", e.target.checked)}
className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500"
/>
<span className="text-sm text-gray-700">加载中</span>
</label>
<label className="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={config.block}
onChange={(e) => updateConfig("block", e.target.checked)}
className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500"
/>
<span className="text-sm text-gray-700">全宽</span>
</label>
</div>
</div>
</div>
{/* 预览区域 */}
<div className="lg:col-span-2 space-y-6">
{/* 主要预览 */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-8">
<h2 className="text-lg font-semibold text-gray-900 mb-6">预览</h2>
<div
className={`flex ${
config.block ? "flex-col" : "flex-row"
} items-center justify-center gap-6 min-h-[140px]`}
>
<Button
variant={
config.variant as "solid" | "text" | "outline" | "link"
}
color={config.color as "primary" | "secondary" | "danger"}
size={config.size as "sm" | "md" | "lg"}
disabled={config.disabled}
loading={config.loading}
block={config.block}
icon={
config.icon !== "none"
? (SelectedIcon as React.ComponentType<
React.SVGProps<SVGSVGElement>
>)
: undefined
}
iconRight={
config.iconRight !== "none"
? (SelectedIconRight as React.ComponentType<
React.SVGProps<SVGSVGElement>
>)
: undefined
}
>
{config.text}
</Button>
</div>
</div>
{/* 代码展示 */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
<button
onClick={() => setShowCode(!showCode)}
className="w-full px-6 py-4 flex items-center justify-between bg-gray-50 hover:bg-gray-100 transition-colors"
>
<span className="text-sm font-medium text-gray-700">代码</span>
<span className="text-sm text-gray-500">
{showCode ? "收起" : "展开"}
</span>
</button>
{showCode && (
<pre className="p-6 text-sm overflow-x-auto bg-gray-900 text-gray-100">
<code>{generateCode()}</code>
</pre>
)}
</div>
</div>
</div>
</div>
</div>
);
}
Button 组件支持多种类型、颜色和状态,适用于各种交互场景。
概述
- 四种按钮类型:solid(实心)、text(文字)、outline(边框)、link(链接)
- 三种颜色:primary(主色)、secondary(次要色)、danger(危险色)
- 三种尺寸:sm(小)、md(中)、lg(大)
- 六种状态:default、hover、pressed、disabled、loading、progress
- 图标支持:支持前置图标和后置图标
- 进度条:支持进度状态显示
- CSS Token:使用 CSS 变量实现主题化
快速开始
import { Button } from "@/registry/wuhan/blocks/block-button/block-button";
export function Example() {
return (
<Button variant="solid" color="primary">
按钮文字
</Button>
);
}特性
- 多种变体:满足不同视觉权重需求
- 完整状态:覆盖所有交互状态
- 图标支持:灵活的图标配置
- 进度显示:支持上传/下载等场景
- 主题化:基于 CSS 变量,易于定制
安装
代码演示
按钮类型
按钮支持四种类型:实心(solid)、文字(text)、边框(outline)和链接(link)。
"use client";
import { Button } from "@/components/composed/block-button/block-button";
export function ButtonVariantsDemo() {
return (
<div className="flex items-center gap-3">
<Button variant="solid" color="primary">
实心按钮
</Button>
<Button variant="text" color="primary">
文字按钮
</Button>
<Button variant="outline" color="primary">
边框按钮
</Button>
<Button variant="link" color="primary">
链接按钮
</Button>
</div>
);
}
按钮颜色
按钮支持三种颜色:主色(primary)、次要色(secondary)和危险色(danger)。
"use client";
import { Button } from "@/components/composed/block-button/block-button";
export function ButtonColorsDemo() {
return (
<div className="flex items-center gap-3">
<Button variant="solid" color="primary">
主色
</Button>
<Button variant="solid" color="secondary">
次要色
</Button>
<Button variant="solid" color="danger">
危险
</Button>
</div>
);
}
按钮尺寸
按钮支持三种尺寸:小(sm)、中(md)、大(lg)。
"use client";
import { Button } from "@/components/composed/block-button/block-button";
export function ButtonSizesDemo() {
return (
<div className="flex items-center gap-3">
<Button size="sm" color="primary">
小按钮
</Button>
<Button size="md" color="primary">
中按钮
</Button>
<Button size="lg" color="primary">
大按钮
</Button>
</div>
);
}
带图标按钮
支持在按钮前后添加图标。
"use client";
import { Button } from "@/components/composed/block-button/block-button";
import { ArrowRight, Download, Send } from "lucide-react";
export function ButtonWithIconDemo() {
return (
<div className="flex items-center gap-3">
<Button
variant="solid"
color="primary"
icon={<Send className="size-4" />}
>
发送
</Button>
<Button
variant="outline"
color="primary"
icon={<Download className="size-4" />}
>
下载
</Button>
<Button
variant="text"
color="primary"
iconRight={<ArrowRight className="size-4" />}
>
下一步
</Button>
</div>
);
}
纯图标按钮
当 children 为空时,只传图标,自动移除间距,适合纯图标按钮场景。
纯图标按钮(不同尺寸)
纯图标按钮(不同变体)
纯图标按钮(不同颜色)
纯图标按钮(禁用状态)
"use client";
import { Button } from "@/components/composed/block-button/block-button";
import { Download, Heart, Search, Settings, Star, Trash2 } from "lucide-react";
export function ButtonIconOnlyDemo() {
return (
<div className="space-y-6">
<div className="space-y-4">
<h3 className="text-sm font-medium text-[var(--Text-text-primary)]">
纯图标按钮(不同尺寸)
</h3>
<div className="flex items-center gap-3">
<Button
variant="solid"
color="primary"
size="sm"
icon={<Search className="size-4" />}
/>
<Button
variant="solid"
color="primary"
size="md"
icon={<Heart className="size-4" />}
/>
<Button
variant="solid"
color="primary"
size="lg"
icon={<Star className="size-5" />}
/>
</div>
</div>
<div className="space-y-4">
<h3 className="text-sm font-medium text-[var(--Text-text-primary)]">
纯图标按钮(不同变体)
</h3>
<div className="flex items-center gap-3">
<Button
variant="solid"
color="primary"
icon={<Download className="size-4" />}
/>
<Button
variant="outline"
color="primary"
icon={<Download className="size-4" />}
/>
<Button
variant="text"
color="primary"
icon={<Download className="size-4" />}
/>
</div>
</div>
<div className="space-y-4">
<h3 className="text-sm font-medium text-[var(--Text-text-primary)]">
纯图标按钮(不同颜色)
</h3>
<div className="flex items-center gap-3">
<Button
variant="solid"
color="primary"
icon={<Settings className="size-4" />}
/>
<Button
variant="solid"
color="secondary"
icon={<Settings className="size-4" />}
/>
<Button
variant="solid"
color="danger"
icon={<Trash2 className="size-4" />}
/>
</div>
</div>
<div className="space-y-4">
<h3 className="text-sm font-medium text-[var(--Text-text-primary)]">
纯图标按钮(禁用状态)
</h3>
<div className="flex items-center gap-3">
<Button
variant="solid"
color="primary"
icon={<Settings className="size-4" />}
disabled
/>
<Button
variant="outline"
color="primary"
icon={<Settings className="size-4" />}
disabled
/>
<Button
variant="text"
color="primary"
icon={<Settings className="size-4" />}
disabled
/>
</div>
</div>
</div>
);
}
加载状态
通过 loading 属性显示加载状态。
"use client";
import { Button } from "@/components/composed/block-button/block-button";
import { useState } from "react";
export function ButtonLoadingDemo() {
const [loading, setLoading] = useState(false);
const handleClick = () => {
setLoading(true);
setTimeout(() => setLoading(false), 2000);
};
return (
<div className="flex items-center gap-3">
<Button
variant="solid"
color="primary"
loading={loading}
onClick={handleClick}
>
{loading ? "加载中..." : "点击加载"}
</Button>
</div>
);
}
进度状态
通过 progress 和 progressValue 显示进度状态,适用于上传、下载等场景。
"use client";
import { Button } from "@/components/composed/block-button/block-button";
import { useState } from "react";
export function ButtonProgressDemo() {
const [progress, setProgress] = useState(0);
const handleClick = () => {
setProgress(0);
const interval = setInterval(() => {
setProgress((prev) => {
if (prev >= 100) {
clearInterval(interval);
return 100;
}
return prev + 10;
});
}, 200);
};
return (
<div className="flex items-center gap-3">
<Button
variant="outline"
color="primary"
progress={progress < 100}
progressValue={progress}
onClick={handleClick}
>
{progress === 0
? "开始上传"
: progress < 100
? `上传中 ${progress}%`
: "完成"}
</Button>
</div>
);
}
禁用状态
"use client";
import { Button } from "@/components/composed/block-button/block-button";
export function ButtonDisabledDemo() {
return (
<div className="flex items-center gap-3">
<Button variant="solid" color="primary" disabled>
禁用实心
</Button>
<Button variant="outline" color="primary" disabled>
禁用边框
</Button>
<Button variant="text" color="primary" disabled>
禁用文字
</Button>
</div>
);
}
全宽按钮
通过 block 属性使按钮占满父容器宽度。
"use client";
import { Button } from "@/components/composed/block-button/block-button";
export function ButtonBlockDemo() {
return (
<div className="space-y-3">
<Button variant="solid" color="primary" block>
全宽按钮
</Button>
</div>
);
}
API
Button Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| variant | 按钮类型 | 'solid' | 'text' | 'outline' | 'link' | 'solid' |
| color | 按钮颜色 | 'primary' | 'secondary' | 'danger' | 'primary' |
| size | 按钮尺寸 | 'sm' | 'md' | 'lg' | 'md' |
| loading | 是否加载中 | boolean | false |
| disabled | 是否禁用 | boolean | false |
| progress | 是否显示进度 | boolean | false |
| progressValue | 进度值 0-100 | number | - |
| icon | 前置图标 | React.ComponentType<SVGProps<SVGSVGElement>> | - |
| iconRight | 后置图标 | React.ComponentType<SVGProps<SVGSVGElement>> | - |
| block | 是否全宽 | boolean | false |
| asChild | 作为子组件渲染 | boolean | false |
| children | 按钮内容 | React.ReactNode | - |
| onClick | 点击事件 | () => void | - |
设计规范
尺寸规范
| 尺寸 | 高度 | 水平内边距 | 字号 | 行高 |
|---|---|---|---|---|
| sm | 28px (h-7) | 12px (px-3) | 12px (text-xs) | 16px |
| md | 32px (h-8) | 16px (px-4) | 14px (text-sm) | 20px |
| lg | 40px (h-10) | 24px (px-6) | 16px (text-base) | 24px |
基础样式
| 变体 | 圆角 | 间距 | 字体 |
|---|---|---|---|
| solid/text/outline | 8px (radius-md) | 8px (gap-md) | font-family-cn |
| link | - | 4px (gap-xs) | font-family-cn |
颜色规范
Primary 主色
| 状态 | Solid | Text | Outline | Link |
|---|---|---|---|---|
| default | bg-brand, text-inverse | bg-container, border-brand, text-brand | bg-container, text-brand | bg-transparent, text-brand |
| hover | bg-brand-hover, text-inverse | bg-brand-light, text-brand | bg-container, text-brand-hover | bg-transparent, text-brand-hover |
| pressed | bg-brand-active, text-inverse | bg-brand-light-active, text-brand | bg-container, text-brand-active | bg-transparent, text-brand-active |
| disabled | opacity-50 | bg-container-disable, text-disable | bg-container-disable, text-disable | bg-transparent, text-disable |
| loading | opacity-50 | bg-container-disable, text-disable | bg-container-disable, text-disable | bg-transparent, text-disable |
| progress | bg-brand, text-inverse | 带 border-brand | bg-container, text-brand | bg-transparent, text-brand |
Secondary 次要色
| 状态 | Solid | Text | Outline | Link |
|---|---|---|---|---|
| default | bg-secondary, text-inverse | bg-container, border-secondary, text-secondary | bg-container, text-secondary | bg-transparent, text-secondary |
| hover | bg-secondary-hover, text-inverse | bg-secondary-light, text-secondary-hover | bg-container, text-secondary-hover | bg-transparent, text-secondary-hover |
| pressed | bg-secondary-active, text-inverse | bg-secondary-light-active, text-secondary-active | bg-container, text-secondary-active | bg-transparent, text-secondary-active |
| disabled | opacity-50 | bg-container-disable, text-disable | bg-container-disable, text-disable | bg-transparent, text-disable |
| loading | opacity-50 | bg-container-disable, text-disable | bg-container-disable, text-disable | bg-transparent, text-disable |
| progress | bg-secondary, text-inverse | 带 border-secondary | bg-container, text-secondary | bg-transparent, text-secondary |
Danger 危险色
| 状态 | Solid | Text | Outline | Link |
|---|---|---|---|---|
| default | bg-error, text-inverse | bg-container, border-error, text-error | bg-container, text-error | bg-transparent, text-error |
| hover | bg-error-hover, text-inverse | bg-error-light, text-error-hover | bg-container, text-error-hover | bg-transparent, text-error-hover |
| pressed | bg-error-active, text-inverse | bg-error-light-active, text-error-active | bg-container, text-error-active | bg-transparent, text-error-active |
| disabled | opacity-50 | bg-container-disable, text-disable | bg-container-disable, text-disable | bg-transparent, text-disable |
| loading | opacity-50 | bg-container-disable, text-disable | bg-container-disable, text-disable | bg-transparent, text-disable |
| progress | bg-error, text-inverse | 带 border-error | bg-container, text-error | bg-transparent, text-error |
注意事项
- CSS 变量:组件使用 CSS 变量实现主题化,需要在全局样式中定义对应的变量
- 加载状态:加载状态时会禁用按钮并显示加载图标
- 进度状态:进度状态和加载状态互斥,不能同时使用
- 禁用状态:禁用状态会覆盖加载和进度状态
交互规范
- 实心按钮:用于主要操作,视觉权重最高
- 文字/边框按钮:用于次要操作,视觉权重较低
- 危险按钮:用于删除、移除等危险操作
- 尺寸选择:小按钮用于紧凑场景,大按钮用于主要操作
- 加载状态:加载状态时禁止重复点击
- 危险操作:建议使用危险颜色
- 主要操作:使用实心主色按钮