按钮
Suggestion
Composed suggestion panel for next step guidance and user onboarding
接下来你可以
选择一个操作继续
"use client";
import {
SuggestionButton,
SuggestionGroup,
} from "@/components/composed/suggestion/suggestion";
export function SuggestionDemo() {
return (
<div className="flex flex-col gap-6 w-full">
<div className="text-center">
<h3 className="text-lg font-medium text-[var(--Text-text-primary)] mb-2">
接下来你可以
</h3>
<p className="text-sm text-[var(--Text-text-secondary)]">
选择一个操作继续
</p>
</div>
<SuggestionGroup>
<SuggestionButton>
介绍一下人工智能对互联网行业发展的影响
</SuggestionButton>
<SuggestionButton>
介绍一下人工智能对互联网行业发展的影响,并给出学习计划
</SuggestionButton>
<SuggestionButton>
介绍一下人工智能对互联网行业发展的影响,并给出学习计划
</SuggestionButton>
</SuggestionGroup>
</div>
);
}
Suggestion 组件用于展示下一步操作建议,支持标题、描述和建议按钮列表,适用于聊天界面的空状态引导、用户引导、操作建议等场景。
概述
- 标题描述:可选的标题和描述文本,提供上下文说明
- 图标支持:每个建议按钮可配置独立图标
- 数据驱动:通过 items 数组快速生成建议列表
- 灵活组合:SuggestionGroup、Button、Panel 可独立或组合使用
- 网格布局:自动网格布局,响应式设计
- 交互友好:清晰的悬停和点击状态反馈
快速开始
import { SuggestionPanel } from "@/registry/wuhan/composed/suggestion";
import { MessageSquare, FileText, Code } from "lucide-react";
export function Example() {
const suggestions = [
{ id: "1", label: "开始新对话", icon: <MessageSquare /> },
{ id: "2", label: "查看文档", icon: <FileText /> },
{ id: "3", label: "示例代码", icon: <Code /> },
];
return (
<SuggestionPanel
title="开始使用"
description="选择一个建议快速开始"
items={suggestions}
/>
);
}特性
- 标题和描述:可选的标题和描述,为建议提供上下文
- 图标配置:每个建议可配置不同的图标组件
- 数据驱动:通过数组数据快速生成建议按钮
- 独立组件:SuggestionButton 可单独使用
- 网格布局:建议按钮自动排列为网格,响应式适配
- 点击回调:每个建议支持独立的点击处理函数
安装
代码演示
基本
基础的建议按钮列表。
"use client";
import {
SuggestionButton,
SuggestionGroup,
} from "@/components/composed/suggestion/suggestion";
export function SuggestionDefault() {
return (
<SuggestionGroup>
<SuggestionButton onClick={() => alert("继续编辑")}>
继续编辑
</SuggestionButton>
<SuggestionButton onClick={() => alert("查看详情")}>
查看详情
</SuggestionButton>
<SuggestionButton onClick={() => alert("分享结果")}>
分享结果
</SuggestionButton>
</SuggestionGroup>
);
}
自定义图标
带自定义图标的建议按钮。
"use client";
import {
SuggestionButton,
SuggestionGroup,
} from "@/components/composed/suggestion/suggestion";
import { ArrowRight, ExternalLink, Download } from "lucide-react";
export function SuggestionCustomIcon() {
return (
<SuggestionGroup>
<SuggestionButton icon={<ArrowRight />} onClick={() => alert("继续")}>
继续下一步
</SuggestionButton>
<SuggestionButton
icon={<ExternalLink />}
onClick={() => alert("外部链接")}
>
在新窗口打开
</SuggestionButton>
<SuggestionButton icon={<Download />} onClick={() => alert("下载")}>
下载文件
</SuggestionButton>
</SuggestionGroup>
);
}
API
SuggestionPanel
建议面板组件,数据驱动的完整解决方案。
Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | - | 面板标题(可选) |
description | ReactNode | - | 面板描述文本(可选) |
items | SuggestionItem[] | - | 建议项数组(必填) |
className | string | - | 额外的样式类名 |
SuggestionItem Type
interface SuggestionItem {
id: string; // 唯一标识符,用于 React key
label: ReactNode; // 建议按钮显示的文本或内容
icon?: ReactNode; // 可选的图标元素
onClick?: () => void; // 点击建议按钮时的回调函数
}Example
import { SuggestionPanel } from "@/registry/wuhan/composed/suggestion";
import { MessageSquare, FileText, Code, Settings } from "lucide-react";
function Suggestions() {
const suggestions = [
{
id: "1",
label: "开始新对话",
icon: <MessageSquare className="w-5 h-5" />,
onClick: () => console.log("New chat")
},
{
id: "2",
label: "查看文档",
icon: <FileText className="w-5 h-5" />,
onClick: () => window.open("/docs")
},
{
id: "3",
label: "示例代码",
icon: <Code className="w-5 h-5" />
},
{
id: "4",
label: "设置选项",
icon: <Settings className="w-5 h-5" />
},
];
return (
<SuggestionPanel
title="开始使用"
description="选择一个建议快速开始,探索更多功能"
items={suggestions}
/>
);
}SuggestionButton
单个建议按钮组件。
Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | 按钮文本内容(必填) |
icon | ReactNode | - | 可选的图标元素 |
onClick | () => void | - | 点击事件处理函数 |
className | string | - | 额外的样式类名 |
Example
import { SuggestionButton } from "@/registry/wuhan/composed/suggestion";
import { Sparkles } from "lucide-react";
function SingleSuggestion() {
return (
<SuggestionButton
icon={<Sparkles className="w-5 h-5" />}
onClick={() => console.log("clicked")}
>
尝试这个功能
</SuggestionButton>
);
}SuggestionGroup
建议按钮组容器。
Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | 子元素(通常是 SuggestionButton) |
className | string | - | 额外的样式类名 |
Example
import { SuggestionGroup, SuggestionButton } from "@/registry/wuhan/composed/suggestion";
function SuggestionList() {
return (
<SuggestionGroup>
<SuggestionButton>建议 1</SuggestionButton>
<SuggestionButton>建议 2</SuggestionButton>
<SuggestionButton>建议 3</SuggestionButton>
</SuggestionGroup>
);
}使用场景
- 空状态引导:新用户首次使用时展示功能建议
- 聊天界面:对话开始前的话题建议或常见问题
- 下一步操作:完成某个步骤后的后续操作建议
- 功能发现:帮助用户发现产品功能和特性
- 快捷入口:提供常用功能的快速访问入口
- 学习路径:引导用户按步骤学习使用产品
最佳实践
- 标题描述:使用简洁的标题和描述,说明建议的目的
- 建议数量:建议 4-8 个选项,避免选择过载
- 图标使用:为每个建议配置相关图标,增强识别性
- 文本清晰:建议文本应明确,让用户知道点击后会发生什么
- 分组展示:相关建议可以分组展示,提升组织性
- 响应式:确保在移动端和桌面端都有良好的显示效果
注意事项
title和description都是可选的,可以只使用其中之一- 每个
SuggestionItem必须有唯一的id - 建议按钮会自动排列为网格布局,响应式适配不同屏幕
- 图标大小建议使用
w-5 h-5(20px)保持一致性 - 标题和描述使用 CSS 变量
--text-primary和--text-secondary
原语组件
Suggestion 基于以下原语组件构建:
SuggestionGroupPrimitive- 按钮组容器原语SuggestionButtonPrimitive- 单个按钮原语
原语组件提供了基础的样式和结构,可以在 registry/wuhan/blocks/suggestion/suggestion-01.tsx 中找到。
样式定制
组件使用 Tailwind CSS,可以通过以下方式定制:
// 自定义按钮样式
<SuggestionButton
className="bg-gradient-to-r from-purple-500 to-pink-500 text-white"
>
特殊样式
</SuggestionButton>
// 自定义面板样式
<SuggestionPanel
items={items}
className="p-8 bg-gradient-to-b from-background to-muted/50"
/>扩展示例
聊天空状态
import { SuggestionPanel } from "@/registry/wuhan/composed/suggestion";
import { MessageSquare, Lightbulb, Code, BookOpen } from "lucide-react";
function ChatEmptyState() {
const suggestions = [
{
id: "1",
label: "帮我写一段代码",
icon: <Code className="w-5 h-5" />,
onClick: () => console.log("Code request")
},
{
id: "2",
label: "解释一个概念",
icon: <Lightbulb className="w-5 h-5" />,
onClick: () => console.log("Explain concept")
},
{
id: "3",
label: "学习新技术",
icon: <BookOpen className="w-5 h-5" />,
onClick: () => console.log("Learn tech")
},
{
id: "4",
label: "开始对话",
icon: <MessageSquare className="w-5 h-5" />,
onClick: () => console.log("Start chat")
},
];
return (
<div className="flex items-center justify-center min-h-screen">
<div className="max-w-2xl">
<SuggestionPanel
title="你好!我是 AI 助手"
description="选择一个话题开始对话,或者直接在下方输入你的问题"
items={suggestions}
/>
</div>
</div>
);
}分类建议
import { SuggestionPanel } from "@/registry/wuhan/composed/suggestion";
import { Code, FileText, MessageSquare, Settings } from "lucide-react";
function CategorizedSuggestions() {
const categories = [
{
title: "编程相关",
suggestions: [
{ id: "code-1", label: "如何学习 React?", icon: <Code className="w-5 h-5" /> },
{ id: "code-2", label: "TypeScript 最佳实践", icon: <Code className="w-5 h-5" /> },
],
},
{
title: "文档编写",
suggestions: [
{ id: "doc-1", label: "写一篇技术博客", icon: <FileText className="w-5 h-5" /> },
{ id: "doc-2", label: "API 文档模板", icon: <FileText className="w-5 h-5" /> },
],
},
];
return (
<div className="space-y-8">
{categories.map((category, index) => (
<SuggestionPanel
key={index}
title={category.title}
items={category.suggestions}
/>
))}
</div>
);
}动态建议
import { SuggestionPanel } from "@/registry/wuhan/composed/suggestion";
import { useState, useEffect } from "react";
import { Sparkles } from "lucide-react";
function DynamicSuggestions() {
const [suggestions, setSuggestions] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
// 模拟 API 调用获取个性化建议
setTimeout(() => {
setSuggestions([
{
id: "1",
label: "基于你的历史,推荐学习 Next.js",
icon: <Sparkles className="w-5 h-5" />
},
{
id: "2",
label: "继续上次的对话",
icon: <Sparkles className="w-5 h-5" />
},
{
id: "3",
label: "探索新功能",
icon: <Sparkles className="w-5 h-5" />
},
]);
setLoading(false);
}, 1000);
}, []);
if (loading) {
return <div className="text-center p-4">加载建议中...</div>;
}
return (
<SuggestionPanel
title="为你推荐"
description="基于你的使用习惯,我们为你推荐以下内容"
items={suggestions}
/>
);
}引导流程
import { SuggestionPanel } from "@/registry/wuhan/composed/suggestion";
import { useState } from "react";
import { ArrowRight, Upload, Edit, Share } from "lucide-react";
function OnboardingFlow() {
const [step, setStep] = useState(0);
const steps = [
{
title: "欢迎使用",
description: "让我们开始你的第一个项目",
suggestions: [
{
id: "start",
label: "开始新项目",
icon: <ArrowRight className="w-5 h-5" />,
onClick: () => setStep(1)
},
],
},
{
title: "上传文件",
description: "选择要处理的文件",
suggestions: [
{
id: "upload",
label: "从电脑上传",
icon: <Upload className="w-5 h-5" />,
onClick: () => setStep(2)
},
{
id: "create",
label: "创建新文件",
icon: <Edit className="w-5 h-5" />,
onClick: () => setStep(2)
},
],
},
{
title: "分享作品",
description: "完成后分享给其他人",
suggestions: [
{
id: "share",
label: "生成分享链接",
icon: <Share className="w-5 h-5" />
},
],
},
];
const currentStep = steps[step];
return (
<div className="max-w-2xl mx-auto p-6">
<div className="mb-4 text-sm text-muted-foreground">
步骤 {step + 1} / {steps.length}
</div>
<SuggestionPanel
title={currentStep.title}
description={currentStep.description}
items={currentStep.suggestions}
/>
</div>
);
}与输入框集成
import { SuggestionPanel } from "@/registry/wuhan/composed/suggestion";
import { useState } from "react";
import { Send } from "lucide-react";
function ChatWithSuggestions() {
const [input, setInput] = useState("");
const [showSuggestions, setShowSuggestions] = useState(true);
const suggestions = [
{ id: "1", label: "如何学习编程?" },
{ id: "2", label: "推荐一本技术书籍" },
{ id: "3", label: "解释什么是算法" },
];
const handleSuggestionClick = (label: string) => {
setInput(label);
setShowSuggestions(false);
};
const handleSend = () => {
if (!input.trim()) return;
console.log("Send:", input);
setInput("");
setShowSuggestions(false);
};
return (
<div className="max-w-2xl mx-auto">
{showSuggestions && (
<SuggestionPanel
title="试试这些问题"
items={suggestions.map(s => ({
...s,
onClick: () => handleSuggestionClick(s.label)
}))}
className="mb-6"
/>
)}
<div className="flex gap-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onFocus={() => !input && setShowSuggestions(true)}
onKeyPress={(e) => e.key === "Enter" && handleSend()}
placeholder="输入你的问题..."
className="flex-1 px-4 py-2 border rounded-lg"
/>
<button
onClick={handleSend}
className="px-4 py-2 bg-primary text-primary-foreground rounded-lg"
>
<Send className="w-4 h-4" />
</button>
</div>
</div>
);
}