ar08's picture
Upload 1040 files
246d201 verified
import React from "react";
import TextareaAutosize from "react-textarea-autosize";
import { useTranslation } from "react-i18next";
import { I18nKey } from "#/i18n/declaration";
import { cn } from "#/utils/utils";
import { SubmitButton } from "#/components/shared/buttons/submit-button";
import { StopButton } from "#/components/shared/buttons/stop-button";
interface ChatInputProps {
name?: string;
button?: "submit" | "stop";
disabled?: boolean;
showButton?: boolean;
value?: string;
maxRows?: number;
onSubmit: (message: string) => void;
onStop?: () => void;
onChange?: (message: string) => void;
onFocus?: () => void;
onBlur?: () => void;
onImagePaste?: (files: File[]) => void;
className?: React.HTMLAttributes<HTMLDivElement>["className"];
buttonClassName?: React.HTMLAttributes<HTMLButtonElement>["className"];
}
export function ChatInput({
name,
button = "submit",
disabled,
showButton = true,
value,
maxRows = 4,
onSubmit,
onStop,
onChange,
onFocus,
onBlur,
onImagePaste,
className,
buttonClassName,
}: ChatInputProps) {
const { t } = useTranslation();
const textareaRef = React.useRef<HTMLTextAreaElement>(null);
const [isDraggingOver, setIsDraggingOver] = React.useState(false);
const handlePaste = (event: React.ClipboardEvent<HTMLTextAreaElement>) => {
// Only handle paste if we have an image paste handler and there are files
if (onImagePaste && event.clipboardData.files.length > 0) {
const files = Array.from(event.clipboardData.files).filter((file) =>
file.type.startsWith("image/"),
);
// Only prevent default if we found image files to handle
if (files.length > 0) {
event.preventDefault();
onImagePaste(files);
}
}
// For text paste, let the default behavior handle it
};
const handleDragOver = (event: React.DragEvent<HTMLTextAreaElement>) => {
event.preventDefault();
if (event.dataTransfer.types.includes("Files")) {
setIsDraggingOver(true);
}
};
const handleDragLeave = (event: React.DragEvent<HTMLTextAreaElement>) => {
event.preventDefault();
setIsDraggingOver(false);
};
const handleDrop = (event: React.DragEvent<HTMLTextAreaElement>) => {
event.preventDefault();
setIsDraggingOver(false);
if (onImagePaste && event.dataTransfer.files.length > 0) {
const files = Array.from(event.dataTransfer.files).filter((file) =>
file.type.startsWith("image/"),
);
if (files.length > 0) {
onImagePaste(files);
}
}
};
const handleSubmitMessage = () => {
const message = value || textareaRef.current?.value || "";
if (message.trim()) {
onSubmit(message);
onChange?.("");
if (textareaRef.current) {
textareaRef.current.value = "";
}
}
};
const handleKeyPress = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (
event.key === "Enter" &&
!event.shiftKey &&
!disabled &&
!event.nativeEvent.isComposing
) {
event.preventDefault();
handleSubmitMessage();
}
};
const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
onChange?.(event.target.value);
};
return (
<div
data-testid="chat-input"
className="flex items-end justify-end grow gap-1 min-h-6 w-full"
>
<TextareaAutosize
ref={textareaRef}
name={name}
placeholder={t(I18nKey.SUGGESTIONS$WHAT_TO_BUILD)}
onKeyDown={handleKeyPress}
onChange={handleChange}
onFocus={onFocus}
onBlur={onBlur}
onPaste={handlePaste}
onDrop={handleDrop}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
value={value}
minRows={1}
maxRows={maxRows}
data-dragging-over={isDraggingOver}
className={cn(
"grow text-sm self-center placeholder:text-neutral-400 text-white resize-none outline-none ring-0",
"transition-all duration-200 ease-in-out",
isDraggingOver
? "bg-neutral-600/50 rounded-lg px-2"
: "bg-transparent",
className,
)}
/>
{showButton && (
<div className={buttonClassName}>
{button === "submit" && (
<SubmitButton isDisabled={disabled} onClick={handleSubmitMessage} />
)}
{button === "stop" && (
<StopButton isDisabled={disabled} onClick={onStop} />
)}
</div>
)}
</div>
);
}