Spaces:
Sleeping
Sleeping
| "use client"; | |
| import { useState, useRef, useCallback } from "react"; | |
| import { SendHorizontal, Square } from "lucide-react"; | |
| import { cn } from "@/lib/utils"; | |
| import { useChatStore } from "@/stores/chatStore"; | |
| import { getChatbotConfig } from "@/lib/chatbotConfig"; | |
| interface ChatInputProps { | |
| onSend: (message: string) => void; | |
| disabled?: boolean; | |
| } | |
| export function ChatInput({ onSend, disabled }: ChatInputProps) { | |
| const [value, setValue] = useState(""); | |
| const textareaRef = useRef<HTMLTextAreaElement>(null); | |
| const { isSending } = useChatStore(); | |
| const [chatbotConfig] = useState(() => getChatbotConfig()); | |
| const autoResize = useCallback(() => { | |
| const ta = textareaRef.current; | |
| if (!ta) return; | |
| ta.style.height = "auto"; | |
| ta.style.height = Math.min(ta.scrollHeight, 180) + "px"; | |
| }, []); | |
| function handleChange(e: React.ChangeEvent<HTMLTextAreaElement>) { | |
| setValue(e.target.value); | |
| autoResize(); | |
| } | |
| function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>) { | |
| if (e.key === "Enter" && !e.shiftKey) { | |
| e.preventDefault(); | |
| handleSubmit(); | |
| } | |
| } | |
| function handleSubmit() { | |
| const trimmed = value.trim(); | |
| if (!trimmed || disabled || isSending) return; | |
| onSend(trimmed); | |
| setValue(""); | |
| if (textareaRef.current) { | |
| textareaRef.current.style.height = "auto"; | |
| } | |
| } | |
| const isDisabled = disabled || isSending; | |
| const isEmpty = !value.trim(); | |
| return ( | |
| <div | |
| className={cn( | |
| "flex items-end gap-3 bg-white rounded-2xl border shadow-sm px-4 py-3 transition-all duration-200", | |
| isDisabled | |
| ? "border-neutral-200 opacity-80" | |
| : "border-neutral-200 focus-within:border-brand-green focus-within:shadow-md focus-within:shadow-brand-green/10" | |
| )} | |
| > | |
| <textarea | |
| ref={textareaRef} | |
| value={value} | |
| onChange={handleChange} | |
| onKeyDown={handleKeyDown} | |
| disabled={isDisabled} | |
| placeholder={ | |
| isSending | |
| ? `${chatbotConfig.name} sedang menjawab...` | |
| : "Tanya langsung di sini... (Enter untuk kirim, Shift+Enter baris baru)" | |
| } | |
| rows={1} | |
| className={cn( | |
| "flex-1 resize-none bg-transparent text-sm text-neutral-900 placeholder:text-neutral-400 outline-none leading-6", | |
| "disabled:cursor-not-allowed min-h-[24px] max-h-[180px] overflow-y-auto" | |
| )} | |
| /> | |
| <div className="flex items-center gap-2 flex-shrink-0 pb-0.5"> | |
| {/* Character hint */} | |
| {value.length > 500 && ( | |
| <span className="text-xs text-neutral-400">{value.length}</span> | |
| )} | |
| {/* Send / Stop button */} | |
| <button | |
| onClick={handleSubmit} | |
| disabled={isEmpty || isDisabled} | |
| className={cn( | |
| "w-9 h-9 rounded-xl flex items-center justify-center transition-all duration-200", | |
| isEmpty || isDisabled | |
| ? "bg-neutral-100 text-neutral-300 cursor-not-allowed" | |
| : "bg-gradient-to-br from-brand-green-light to-brand-green text-white shadow-md shadow-brand-green/25 hover:brightness-105" | |
| )} | |
| aria-label="Kirim pesan" | |
| > | |
| {isSending ? ( | |
| <Square className="h-3.5 w-3.5" /> | |
| ) : ( | |
| <SendHorizontal className="h-4 w-4" /> | |
| )} | |
| </button> | |
| </div> | |
| </div> | |
| ); | |
| } | |