File size: 4,804 Bytes
a94ab76 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | import React, { useState, KeyboardEvent, useRef } from 'react';
import { Send, Loader2, Plus, X } from 'lucide-react';
interface ChatInputProps {
onSend: (message: string) => void;
disabled?: boolean;
isLoading?: boolean;
onImageSelect?: (image: string | null) => void;
currentImage?: string | null;
supportsImages?: boolean;
}
export const ChatInput: React.FC<ChatInputProps> = ({
onSend,
disabled = false,
isLoading = false,
onImageSelect,
currentImage,
supportsImages = false
}) => {
const [input, setInput] = useState('');
const [preview, setPreview] = useState<string | null>(currentImage || null);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleSend = () => {
if (input.trim() && !disabled && !isLoading) {
onSend(input.trim());
setInput('');
}
};
const handleKeyPress = (e: KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSend();
}
};
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
const result = reader.result as string;
setPreview(result);
onImageSelect?.(result);
};
reader.readAsDataURL(file);
}
};
const handleRemoveImage = () => {
setPreview(null);
onImageSelect?.(null);
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
};
// Update preview when currentImage changes
React.useEffect(() => {
setPreview(currentImage || null);
}, [currentImage]);
return (
<div className="w-full max-w-4xl mx-auto">
{/* Image Preview */}
{preview && (
<div className="mb-3 relative inline-block">
<img
src={preview}
alt="Preview"
className="max-w-xs h-32 object-contain rounded-lg border border-gray-300 dark:border-gray-700"
/>
<button
onClick={handleRemoveImage}
className="absolute -top-2 -right-2 p-1 bg-red-500 hover:bg-red-600 text-white rounded-full transition-all shadow-lg"
>
<X size={14} />
</button>
</div>
)}
<div className="relative flex items-center gap-2 bg-white dark:bg-gray-800 rounded-3xl shadow-lg border border-gray-200 dark:border-gray-700 px-3 py-3 transition-all focus-within:shadow-xl focus-within:border-primary-500 dark:focus-within:border-primary-600">
{/* Hidden File Input */}
<input
ref={fileInputRef}
type="file"
accept="image/*"
onChange={handleFileChange}
className="hidden"
disabled={!supportsImages}
/>
{/* Plus Button for Image Upload */}
<button
onClick={() => fileInputRef.current?.click()}
disabled={!supportsImages}
className={`flex-shrink-0 w-8 h-8 rounded-lg flex items-center justify-center transition-all ${
supportsImages
? 'hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400'
: 'text-gray-300 dark:text-gray-600 cursor-not-allowed'
}`}
title={supportsImages ? "Resim yükle" : "Bu model resim desteklemiyor"}
>
<Plus size={20} />
</button>
<textarea
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="Herhangi bir şey sor"
rows={1}
disabled={disabled || isLoading}
className="flex-1 bg-transparent text-gray-900 dark:text-gray-100 resize-none border-none focus:outline-none focus:ring-0 placeholder:text-gray-400 dark:placeholder:text-gray-500 disabled:cursor-not-allowed"
style={{ minHeight: '24px', maxHeight: '120px' }}
onInput={(e) => {
const target = e.target as HTMLTextAreaElement;
target.style.height = 'auto';
target.style.height = Math.min(target.scrollHeight, 120) + 'px';
}}
/>
<button
onClick={handleSend}
disabled={!input.trim() || disabled || isLoading}
className="flex-shrink-0 w-8 h-8 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 disabled:bg-gray-100 dark:disabled:bg-gray-800 disabled:cursor-not-allowed rounded-lg flex items-center justify-center transition-all"
>
{isLoading ? (
<Loader2 size={18} className="animate-spin text-gray-600 dark:text-gray-400" />
) : (
<Send size={18} className="text-gray-600 dark:text-gray-300" />
)}
</button>
</div>
</div>
);
};
|