Files
Genarrative/src/components/image-editor/EditorAgentConversation/EditorAgentDraftTextarea.tsx
T
k88936 e145d031c0
Project CI / Repository checks (pull_request) Failing after 10s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Successful in 1m40s
Project CI / Native shell tests (pull_request) Successful in 2m12s
fix: IME 判断增加 keyCode !== 229
2026-07-28 12:05:45 +08:00

114 lines
3.2 KiB
TypeScript

import {
type ClipboardEventHandler,
type KeyboardEvent,
useLayoutEffect,
useRef,
} from 'react';
const DRAFT_MIN_HEIGHT_PX = 40;
const DRAFT_MAX_HEIGHT_PX = 128;
// for fallback on some browser
function supportsNativeFieldSizing() {
return (
typeof CSS !== 'undefined' &&
typeof CSS.supports === 'function' &&
CSS.supports('field-sizing', 'content')
);
}
function resizeFallbackTextarea(textarea: HTMLTextAreaElement) {
textarea.style.height = 'auto';
const contentHeight = textarea.scrollHeight;
const borderHeight = Math.max(
0,
textarea.offsetHeight - textarea.clientHeight,
);
const borderBoxContentHeight = contentHeight + borderHeight;
const nextHeight = Math.min(
Math.max(borderBoxContentHeight, DRAFT_MIN_HEIGHT_PX),
DRAFT_MAX_HEIGHT_PX,
);
textarea.style.height = `${nextHeight}px`;
textarea.style.overflowY =
borderBoxContentHeight > DRAFT_MAX_HEIGHT_PX ? 'auto' : 'hidden';
}
export function EditorAgentDraftTextarea({
value,
onChange,
onPaste,
}: {
value: string;
onChange: (value: string) => void;
onPaste?: ClipboardEventHandler<HTMLTextAreaElement>;
}) {
const textareaRef = useRef<HTMLTextAreaElement>(null);
useLayoutEffect(() => {
const textarea = textareaRef.current;
if (!textarea || supportsNativeFieldSizing()) {
return;
}
resizeFallbackTextarea(textarea);
}, [value]);
useLayoutEffect(() => {
const textarea = textareaRef.current;
if (!textarea || supportsNativeFieldSizing()) {
return undefined;
}
let previousWidth = textarea.getBoundingClientRect().width;
const resizeWhenWidthChanges = (nextWidth: number) => {
if (Math.abs(nextWidth - previousWidth) < 0.5) {
return;
}
previousWidth = nextWidth;
resizeFallbackTextarea(textarea);
};
if (typeof ResizeObserver !== 'undefined') {
const observer = new ResizeObserver((entries) => {
const entry = entries[0];
if (entry) {
resizeWhenWidthChanges(entry.contentRect.width);
}
});
observer.observe(textarea);
return () => observer.disconnect();
}
const handleWindowResize = () => {
resizeWhenWidthChanges(textarea.getBoundingClientRect().width);
};
window.addEventListener('resize', handleWindowResize);
return () => window.removeEventListener('resize', handleWindowResize);
}, []);
const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {
if (
event.key === 'Enter' &&
!event.shiftKey &&
!event.nativeEvent.isComposing &&
event.nativeEvent.keyCode !== 229
) {
event.preventDefault();
event.currentTarget.form?.requestSubmit();
}
};
return (
<textarea
ref={textareaRef}
className="editor-agent-conversation__draft-input max-h-32 min-h-10 min-w-0 flex-1 resize-none overflow-x-hidden overflow-y-auto overscroll-contain rounded-3xl border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-800 outline-none [field-sizing:content] focus:border-slate-400"
aria-label="发送给画布 Agent"
value={value}
rows={1}
onChange={(event) => onChange(event.currentTarget.value)}
onPaste={onPaste}
onKeyDown={handleKeyDown}
/>
);
}