83 lines
2.6 KiB
TypeScript
83 lines
2.6 KiB
TypeScript
"use client";
|
||
/**
|
||
* ChatInputTextField 输入框
|
||
*
|
||
*
|
||
*
|
||
* 行为:
|
||
* - 多行输入(min 1 行 / max 5 行 ≈ max-height 120px)
|
||
* - Enter 直接发送(无 Shift/Ctrl/Meta)
|
||
* - Shift+Enter / Ctrl+Enter / Meta+Enter 换行
|
||
* - 外层白底圆角容器
|
||
*
|
||
* 通过 `forwardRef` 暴露 textarea 节点,父组件可在 send 后调用 `.focus()`
|
||
* 通过 `onFocusChange` 回调通知父组件焦点状态,用于外层容器的聚焦样式切换。
|
||
*/
|
||
import {
|
||
forwardRef,
|
||
type FormEvent,
|
||
type KeyboardEvent,
|
||
useImperativeHandle,
|
||
useRef,
|
||
} from "react";
|
||
|
||
export interface ChatInputTextFieldProps {
|
||
value: string;
|
||
onChange: (value: string) => void;
|
||
onSubmit: () => void;
|
||
/** 焦点变化回调(用于父组件 ChatInputBar 切换聚焦态视觉) */
|
||
onFocusChange?: (focused: boolean) => void;
|
||
placeholder?: string;
|
||
disabled?: boolean;
|
||
autoFocus?: boolean;
|
||
}
|
||
|
||
export const ChatInputTextField = forwardRef<
|
||
HTMLTextAreaElement,
|
||
ChatInputTextFieldProps
|
||
>(function ChatInputTextField(
|
||
{
|
||
value,
|
||
onChange,
|
||
onSubmit,
|
||
onFocusChange,
|
||
placeholder = "Say anything that’s on your mind ^U^…",
|
||
disabled = false,
|
||
autoFocus = false,
|
||
},
|
||
ref,
|
||
) {
|
||
const innerRef = useRef<HTMLTextAreaElement>(null);
|
||
// 对外暴露与 forwardRef 同名的 ref(指向内部 textarea 节点)
|
||
useImperativeHandle(ref, () => innerRef.current as HTMLTextAreaElement, []);
|
||
|
||
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
||
if (e.key !== "Enter") return;
|
||
if (e.shiftKey || e.ctrlKey || e.metaKey) return; // 修饰键 → 换行
|
||
e.preventDefault();
|
||
onSubmit();
|
||
};
|
||
|
||
return (
|
||
<div className="flex min-w-0 flex-auto items-center rounded-full bg-transparent px-[clamp(10px,3vw,14px)]">
|
||
<textarea
|
||
ref={innerRef}
|
||
className="min-h-(--chat-send-button-size,40px) max-h-[min(30vh,120px)] w-full min-w-0 flex-auto resize-none border-0 bg-transparent pb-0 pl-0 pr-0 pt-[clamp(5px,1.111vw,6px)] font-[inherit] text-[16px] leading-[clamp(22px,4.444vw,24px)] text-(--color-text-foreground,#000) caret-accent outline-none placeholder:text-(--color-text-hint,#757575)"
|
||
value={value}
|
||
onChange={(e: FormEvent<HTMLTextAreaElement>) =>
|
||
onChange(e.currentTarget.value)
|
||
}
|
||
onKeyDown={handleKeyDown}
|
||
onFocus={() => onFocusChange?.(true)}
|
||
onBlur={() => onFocusChange?.(false)}
|
||
placeholder={placeholder}
|
||
disabled={disabled}
|
||
maxLength={4000}
|
||
autoFocus={autoFocus}
|
||
rows={1}
|
||
aria-label="Message"
|
||
/>
|
||
</div>
|
||
);
|
||
});
|