输入控件
Sender Responsive
响应式 Sender 组件:输入框和按钮默认单行布局,当内容溢出时自动切换为多行布局。
"use client";
import { useState } from "react";
import { ResponsiveSender } from "@/components/composed/sender/responsive-sender";
import {
SenderResponsiveButtonGroup,
SenderResponsiveSendButton,
} from "@/components/wuhan/blocks/sender/sender-responsive-01";
export function SenderResponsiveDemo() {
const [value, setValue] = useState("");
const [overflowStatus, setOverflowStatus] = useState(false);
const canSend = value.trim().length > 0;
return (
<ResponsiveSender
value={value}
onChange={setValue}
placeholder="输入内容..."
getCanSend={({ value: v }) => v.trim().length > 0}
sendDisabled={!canSend}
submitOnEnter
onOverflowChange={setOverflowStatus}
onSend={() => setValue("")}
buttonGroupChildren={
<SenderResponsiveButtonGroup isOverflow={overflowStatus}>
<div className="pr-[var(--Gap-gap-sm)] pl-[var(--Gap-gap-sm)] gap-[var(--Gap-gap-sm)] rounded-[var(--radius-sm)] bg-[var(--Container-bg-neutral-light)]">
<span className="font-size-1 text-[var(--Text-text-primary)]">
0个数据源
</span>
</div>
<SenderResponsiveSendButton type="submit" disabled={!canSend} />
</SenderResponsiveButtonGroup>
}
/>
);
}
核心特性
- 自动溢出检测:使用
ResizeObserver监听内容宽度变化 - 平滑过渡动画:CSS 实现流畅布局切换
- 灵活控制:
forceSingleLine强制单行,onOverflowChange监听状态
安装
布局行为
单行模式 (默认)
═══════════════════════════════════════════════════
│ [Textarea:flex-1] [附件] [发送按钮] │
│ ↑ 自适应宽度 ↑ 最右边 │
═══════════════════════════════════════════════════
多行模式 (溢出时)
═══════════════════════════════════════════════════
│ [Textarea:w-full] │
│ ───────────────────────────────────────────── │
│ [附件] [发送按钮] │ ← 右对齐
═══════════════════════════════════════════════════Usage
基础用法
import { useState } from "react";
import { ResponsiveSender } from "@/registry/wuhan/composed/sender/responsive-sender";
export function Example() {
const [value, setValue] = useState("");
const [isOverflow, setIsOverflow] = useState(false);
return (
<ResponsiveSender
value={value}
onChange={setValue}
onSend={() => console.log("send", value)}
placeholder="输入你的需求..."
onOverflowChange={setIsOverflow}
/>
);
}响应式原语(Blocks 层)
"use client";
import {
SenderResponsiveContainer,
SenderResponsiveTextarea,
SenderResponsiveInputRow,
SenderResponsiveButtonGroup,
SenderResponsiveAttachmentButton,
SenderResponsiveSendButton,
} from "@/components/wuhan/blocks/sender/sender-responsive-01";
import { Paperclip } from "lucide-react";
import { useState, useRef, useEffect } from "react";
export function SenderResponsiveDefault() {
const [value, setValue] = useState("");
const [isOverflow, setIsOverflow] = useState(false);
const containerRef = useRef<HTMLFormElement>(null);
// 检测溢出的简单示例
useEffect(() => {
const container = containerRef.current;
if (!container) return;
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const { scrollWidth, clientWidth } = entry.target as HTMLElement;
setIsOverflow(scrollWidth > clientWidth);
}
});
resizeObserver.observe(container);
return () => resizeObserver.disconnect();
}, []);
return (
<SenderResponsiveContainer
ref={containerRef}
className="max-w-2xl"
onOverflowChange={setIsOverflow}
>
<SenderResponsiveInputRow isOverflow={isOverflow}>
<SenderResponsiveTextarea
placeholder={isOverflow ? "输入内容..." : "输入消息..."}
value={value}
onChange={(e) => setValue(e.target.value)}
isOverflow={isOverflow}
/>
<SenderResponsiveButtonGroup isOverflow={isOverflow}>
<SenderResponsiveAttachmentButton
type="button"
onClick={() => alert("点击附件")}
aria-label="Attach file"
>
<Paperclip className="size-4" />
</SenderResponsiveAttachmentButton>
<SenderResponsiveSendButton
type="submit"
onClick={() => {
if (value.trim()) {
alert(`发送: ${value}`);
setValue("");
}
}}
disabled={!value.trim()}
/>
</SenderResponsiveButtonGroup>
</SenderResponsiveInputRow>
</SenderResponsiveContainer>
);
}
import {
SenderResponsiveContainer,
SenderResponsiveTextarea,
SenderResponsiveInputRow,
SenderResponsiveButtonGroup,
SenderResponsiveAttachmentButton,
SenderResponsiveSendButton,
} from "@/registry/wuhan/blocks/sender/sender-responsive-01";
export function Example() {
const [value, setValue] = useState("");
const [isOverflow, setIsOverflow] = useState(false);
return (
<SenderResponsiveContainer
onOverflowChange={setIsOverflow}
>
<SenderResponsiveInputRow isOverflow={isOverflow}>
<SenderResponsiveTextarea
value={value}
onChange={(e) => setValue(e.target.value)}
isOverflow={isOverflow}
/>
<SenderResponsiveButtonGroup isOverflow={isOverflow}>
<SenderResponsiveAttachmentButton type="button">
<Paperclip className="size-4" />
</SenderResponsiveAttachmentButton>
<SenderResponsiveSendButton
type="submit"
disabled={!value.trim()}
/>
</SenderResponsiveButtonGroup>
</SenderResponsiveInputRow>
</SenderResponsiveContainer>
);
}API
ResponsiveSender (Composed)
布局控制
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
forceSingleLine | boolean | false | 强制使用单行布局,禁用响应式切换 |
onOverflowChange | (isOverflow) => void | - | 溢出状态变化回调 |
maxWidth | string | "100%" | 容器最大宽度 |
输入
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string | - | 受控输入值 |
onChange | (value) => void | - | 输入变化回调 |
placeholder | string | - | 占位符文字 |
inputDisabled | boolean | false | 禁用输入框 |
submitOnEnter | boolean | false | 按 Enter 发送 |
附件
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
attachments | T[] | [] | 附件数组 |
attachmentAdapter | (item) => AttachmentItem | - | 数据适配器 |
onAttachmentRemove | (id) => void | - | 删除回调 |
maxAttachments | number | - | 最大附件数 |
发送
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
onSend | () => void | - | 发送回调 |
sendDisabled | boolean | - | 禁用发送 |
generating | boolean | false | 生成中状态 |
getCanSend | (ctx) => boolean | - | 自定义发送规则 |
Blocks 层组件
SenderResponsiveContainer
响应式容器,负责检测溢出状态。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
maxWidth | string | "100%" | 最大宽度阈值 |
forceSingleLine | boolean | false | 强制单行模式 |
onOverflowChange | (isOverflow) => void | - | 溢出状态回调 |
SenderResponsiveInputRow
响应式输入行,始终保持 flex-row 布局。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
isOverflow | boolean | - | 当前是否处于溢出状态 |
SenderResponsiveTextarea
响应式文本域,根据溢出状态调整宽度。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
isOverflow | boolean | - | 当前是否处于溢出状态 |
SenderResponsiveButtonGroup
响应式按钮组,根据溢出状态切换对齐方式。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
isOverflow | boolean | - | 当前是否处于溢出状态 |
SenderResponsiveAttachmentButton
响应式附件按钮。
SenderResponsiveSendButton
响应式发送按钮。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
generating | boolean | false | 生成中状态 |
generatingContent | ReactNode | - | 自定义加载内容 |