unnamed-ui
气泡/容器

Quote Content

Composed quote content display above input field

基础用法

这是一段引用内容

带关闭功能

点击右侧关闭按钮可以关闭这条引用

长文本溢出处理

这是一段非常长的引用内容,当内容超出容器宽度时会自动截断并显示省略号,确保布局不会被破坏

自定义内容

可以引用消息、回复或其他内容

无关闭按钮

不提供关闭按钮时,引用内容会一直显示
"use client";

import { useState } from "react";
import { QuoteContentComposed } from "@/components/composed/quote-content/quote-content";

export function QuoteContentDemo() {
  const [showQuote1, setShowQuote1] = useState(true);
  const [showQuote2, setShowQuote2] = useState(true);
  const [showQuote3, setShowQuote3] = useState(true);

  return (
    <div className="w-full max-w-2xl space-y-4">
      {/* 基础用法 */}
      <div className="space-y-2">
        <h3 className="text-sm font-medium text-[var(--Text-text-primary)]">
          基础用法
        </h3>
        <QuoteContentComposed
          content="这是一段引用内容"
          onClose={() => {
            console.log("关闭引用");
          }}
        />
      </div>

      {/* 带关闭功能 */}
      <div className="space-y-2">
        <h3 className="text-sm font-medium text-[var(--Text-text-primary)]">
          带关闭功能
        </h3>
        {showQuote1 && (
          <QuoteContentComposed
            content="点击右侧关闭按钮可以关闭这条引用"
            onClose={() => setShowQuote1(false)}
          />
        )}
        {!showQuote1 && (
          <button
            onClick={() => setShowQuote1(true)}
            className="appearance-none border-0 bg-transparent p-0 text-sm text-[var(--Text-text-secondary)] hover:text-[var(--Text-text-primary)]"
          >
            重新显示引用
          </button>
        )}
      </div>

      {/* 长文本溢出 */}
      <div className="space-y-2">
        <h3 className="text-sm font-medium text-[var(--Text-text-primary)]">
          长文本溢出处理
        </h3>
        {showQuote2 && (
          <QuoteContentComposed
            content="这是一段非常长的引用内容,当内容超出容器宽度时会自动截断并显示省略号,确保布局不会被破坏"
            onClose={() => setShowQuote2(false)}
          />
        )}
      </div>

      {/* 自定义内容 */}
      <div className="space-y-2">
        <h3 className="text-sm font-medium text-[var(--Text-text-primary)]">
          自定义内容
        </h3>
        {showQuote3 && (
          <QuoteContentComposed
            content={<span>可以引用消息、回复或其他内容</span>}
            onClose={() => setShowQuote3(false)}
          />
        )}
      </div>

      {/* 无关闭按钮 */}
      <div className="space-y-2">
        <h3 className="text-sm font-medium text-[var(--Text-text-primary)]">
          无关闭按钮
        </h3>
        <QuoteContentComposed content="不提供关闭按钮时,引用内容会一直显示" />
      </div>
    </div>
  );
}

Quote Content 组件用于在输入框上方显示引用内容,包含图标、内容与关闭动作。适用于聊天场景中的引用消息、回复内容等场景。

概述

  • 引用内容展示:图标 + 内容 + 关闭按钮的完整布局
  • 业务数据适配:支持泛型数据对象,通过 adapter 映射到组件属性
  • 灵活定制:支持自定义图标、关闭按钮和插槽渲染
  • 类型安全:完整的 TypeScript 类型定义
  • 开箱即用:默认样式即可满足常见场景

快速开始

import { QuoteContentComposed } from "@/registry/wuhan/composed/quote-content/quote-content";

export function Example() {
  return (
    <QuoteContentComposed content="这是一条引用内容" />
  );
}

特性

  • 默认样式:直接使用即可看到完整 UI,无需额外配置
  • 自定义图标:前缀图标和关闭图标均可自定义
  • 数据适配器:通过 quoteAdapter 适配业务数据到组件属性
  • 插槽渲染:支持 renderLeadingrenderContentrenderClose 等插槽
  • 可控关闭:支持 closable 控制是否显示关闭按钮
  • 回调支持:提供 onCloseonCloseQuote 两种关闭回调

安装

pnpm dlx shadcn@latest add http://localhost:3000/r/wuhan/quote-content.json

代码演示

基本用法

最小示例,只传入引用内容。

import { QuoteContentComposed } from "@/registry/wuhan/composed/quote-content/quote-content";

<QuoteContentComposed content="这是一条引用内容" />
这是一条引用内容
"use client";

import { QuoteContentComposed } from "@/components/composed/quote-content/quote-content";

export function QuoteContentDefault() {
  return (
    <div className="w-full max-w-2xl">
      <QuoteContentComposed content="这是一条引用内容" />
    </div>
  );
}

自定义图标

自定义前缀图标与关闭图标。

<QuoteContentComposed
  content="自定义图标"
  icon={<MyIcon />}
  closeIcon={<MyCloseIcon />}
  onClose={() => console.log("close")}
/>
自定义图标与关闭按钮
"use client";

import { useState } from "react";
import { QuoteContentComposed } from "@/components/composed/quote-content/quote-content";
import { Reply, XCircle } from "lucide-react";

export function QuoteContentCustomIcon() {
  const [visible, setVisible] = useState(true);

  return (
    <div className="w-full max-w-2xl">
      {visible ? (
        <QuoteContentComposed
          content="自定义图标与关闭按钮"
          icon={<Reply className="w-4 h-4 text-slate-500" />}
          closeIcon={<XCircle className="w-4 h-4 text-slate-500" />}
          onClose={() => setVisible(false)}
        />
      ) : (
        <button
          type="button"
          onClick={() => setVisible(true)}
          className="appearance-none border-0 bg-transparent p-0 text-sm text-muted-foreground hover:text-[var(--Text-text-primary)]"
        >
          重新显示引用
        </button>
      )}
    </div>
  );
}

数据适配(推荐)

业务数据适配,通过 quoteAdapter 将业务数据映射到组件属性。

const quote = { id: "q-1", text: "来自业务数据的引用内容" };

<QuoteContentComposed
  quote={quote}
  quoteAdapter={(item) => ({ content: item.text })}
  onCloseQuote={(item) => console.log("close", item.id)}
/>
来自业务数据的引用内容
"use client";

import { useState } from "react";
import { QuoteContentComposed } from "@/components/composed/quote-content/quote-content";

type QuoteData = {
  id: string;
  text: string;
};

export function QuoteContentAdapter() {
  const [quote, setQuote] = useState<QuoteData | null>({
    id: "q-1",
    text: "来自业务数据的引用内容",
  });

  return (
    <div className="w-full max-w-2xl">
      {quote ? (
        <QuoteContentComposed
          quote={quote}
          quoteAdapter={(item) => ({ content: item.text })}
          onCloseQuote={() => setQuote(null)}
        />
      ) : (
        <button
          type="button"
          onClick={() => setQuote({ id: "q-2", text: "重新添加的引用内容" })}
          className="appearance-none border-0 bg-transparent p-0 text-sm text-muted-foreground hover:text-[var(--Text-text-primary)]"
        >
          重新添加引用
        </button>
      )}
    </div>
  );
}

自定义渲染(高级)

使用插槽完全自定义渲染结构。

<QuoteContentComposed
  content="自定义结构"
  renderLeading={({ icon }) => <div>{icon}</div>}
  renderContent={({ content }) => <div>{content}</div>}
  renderClose={({ onClose }) => <button onClick={onClose}>关闭</button>}
/>
自定义渲染结构
"use client";

import { QuoteContentComposed } from "@/components/composed/quote-content/quote-content";
import { MessageSquareText } from "lucide-react";

export function QuoteContentCustomRender() {
  return (
    <div className="w-full max-w-2xl">
      <QuoteContentComposed
        content="自定义渲染结构"
        renderLeading={({ icon }) => (
          <div className="flex items-center text-slate-500">
            {icon ?? <MessageSquareText className="w-4 h-4" />}
          </div>
        )}
        renderContent={({ content }) => (
          <div className="flex-1 text-sm text-slate-700">{content}</div>
        )}
        renderClose={({ onClose }) => (
          <button
            type="button"
            onClick={(event) => {
              event.stopPropagation();
              onClose?.();
            }}
            className="appearance-none border-0 bg-transparent p-0 text-xs text-slate-500 hover:text-slate-900"
          >
            关闭
          </button>
        )}
        onClose={() => undefined}
      />
    </div>
  );
}

API

QuoteContentComposed

引用内容组件,支持基础属性和高级定制。

Props

PropTypeDefaultDescription
contentReactNode-引用内容(最常用)
quoteT-业务数据对象(泛型支持)
quoteAdapter(quote: T) => AdaptedProps-业务数据转组件属性的适配器
iconReactNode-前缀图标
closeIconReactNode-关闭图标
onClose() => void-关闭回调
onCloseQuote(quote: T) => void-关闭回调(带业务数据)
closablebooleantrue是否显示关闭按钮
renderQuote(ctx: RenderContext) => ReactNode-覆盖整体渲染
renderLeading(ctx: RenderContext) => ReactNode-覆盖图标区域
renderContent(ctx: RenderContext) => ReactNode-覆盖内容区域
renderClose(ctx: RenderContext) => ReactNode-覆盖关闭按钮
classNamestring-外层容器样式

Example

import { QuoteContentComposed } from "@/registry/wuhan/composed/quote-content/quote-content";

function ChatInput() {
  const [quote, setQuote] = useState(null);
  
  return (
    <div className="flex flex-col gap-2">
      {/* 基础用法 */}
      <QuoteContentComposed content="这是一条引用内容" />
      
      {/* 数据适配 */}
      {quote && (
        <QuoteContentComposed
          quote={quote}
          quoteAdapter={(item) => ({
            content: item.text,
            icon: <ReplyIcon />
          })}
          onCloseQuote={(item) => setQuote(null)}
        />
      )}
      
      {/* 完全自定义 */}
      <QuoteContentComposed
        content="高级定制"
        renderContent={({ content }) => (
          <div className="text-sm font-medium">{content}</div>
        )}
      />
    </div>
  );
}

AdaptedProps

适配器返回的属性类型。

type AdaptedProps = {
  content?: ReactNode;
  icon?: ReactNode;
  closeIcon?: ReactNode;
};

RenderContext

渲染函数的上下文参数。

type RenderContext = {
  content: ReactNode;
  icon?: ReactNode;
  closeIcon?: ReactNode;
  onClose?: () => void;
  closable: boolean;
};

使用场景

  • 聊天引用:在聊天输入框上方显示引用的消息内容
  • 回复消息:论坛、评论系统中的回复功能
  • 编辑预览:显示正在编辑或引用的内容
  • 引用卡片:文章、文档中的引用块展示
  • 消息转发:显示转发的消息来源和内容

最佳实践

  1. 优先使用数据适配器:当组件与业务数据绑定时,使用 quoteAdapter 可以保持类型安全和代码清晰
  2. 合理使用插槽:只在需要深度定制布局时使用 render* 系列插槽
  3. 控制内容长度:引用内容过长时应截断或限制行数,避免影响 UI 布局
  4. 回调处理:使用 onCloseQuote 而非 onClose 可以直接获取业务数据,减少状态管理
  5. 图标一致性:保持图标风格与整体设计系统一致

注意事项

  • quoteAdapter 优先级高于直接传入的属性(如 contenticon 等)
  • 使用 renderQuote 会完全覆盖组件默认结构,需自行实现所有布局
  • onCloseQuote 只在提供 quote 属性时才会被调用
  • closable={false} 会隐藏关闭按钮,此时 onCloseonCloseQuote 不会被触发
  • 建议为引用内容设置最大宽度或截断策略,避免布局问题