| // src/components/threads/ThreadReplyInput.tsx | |
| import { useRef, useEffect } from "react"; | |
| import { Send, Sparkles, Loader2, Paperclip } from "lucide-react"; | |
| interface ThreadReplyInputProps { | |
| value: string; | |
| onChange: (val: string) => void; | |
| onSend: () => void; | |
| sending: boolean; | |
| onAiAsk: () => void; | |
| placeholder?: string; | |
| disabled?: boolean; | |
| } | |
| export function ThreadReplyInput({ | |
| value, | |
| onChange, | |
| onSend, | |
| sending, | |
| onAiAsk, | |
| placeholder = "Reply in thread...", | |
| disabled = false, | |
| }: ThreadReplyInputProps) { | |
| const textareaRef = useRef<HTMLTextAreaElement>(null); | |
| const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { | |
| onChange(e.target.value); | |
| // Auto-resize | |
| e.target.style.height = "auto"; | |
| e.target.style.height = `${Math.min(e.target.scrollHeight, 120)}px`; | |
| }; | |
| const handleKeyDown = (e: React.KeyboardEvent) => { | |
| if (e.key === "Enter" && !e.shiftKey) { | |
| e.preventDefault(); | |
| onSend(); | |
| } | |
| }; | |
| // Focus on mount | |
| useEffect(() => { | |
| textareaRef.current?.focus(); | |
| }, []); | |
| return ( | |
| <div className="border-t border-gray-100 bg-white p-3 dark:border-gray-700 dark:bg-gray-800"> | |
| <div className="flex items-end gap-2"> | |
| <button | |
| onClick={onAiAsk} | |
| disabled={disabled || sending} | |
| className="rounded-lg p-2 text-purple-400 transition hover:bg-purple-50 hover:text-purple-600 dark:hover:bg-purple-900/20 disabled:opacity-50" | |
| title="Ask AI for help in this thread" | |
| aria-label="Ask AI" | |
| > | |
| <Sparkles className="h-5 w-5" /> | |
| </button> | |
| <button | |
| disabled={disabled || sending} | |
| className="rounded-lg p-2 text-gray-400 transition hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-gray-700 disabled:opacity-50" | |
| title="Attach file (coming soon)" | |
| aria-label="Attach file" | |
| > | |
| <Paperclip className="h-5 w-5" /> | |
| </button> | |
| <textarea | |
| ref={textareaRef} | |
| value={value} | |
| onChange={handleChange} | |
| onKeyDown={handleKeyDown} | |
| placeholder={placeholder} | |
| disabled={disabled || sending} | |
| rows={1} | |
| className="flex-1 resize-none rounded-xl bg-gray-100 px-3 py-2 text-sm text-gray-800 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary dark:bg-gray-700 dark:text-gray-100" | |
| style={{ minHeight: "38px", maxHeight: "120px" }} | |
| /> | |
| <button | |
| onClick={onSend} | |
| disabled={!value.trim() || sending || disabled} | |
| className="rounded-xl bg-primary p-2 text-white transition hover:bg-primary/90 disabled:opacity-50" | |
| aria-label="Send reply" | |
| > | |
| {sending ? <Loader2 className="h-5 w-5 animate-spin" /> : <Send className="h-5 w-5" />} | |
| </button> | |
| </div> | |
| </div> | |
| ); | |
| } |
Xet Storage Details
- Size:
- 2.87 kB
- Xet hash:
- ce1e4b1924cdde173aa68c96eb06de4672a9a560138ca284c35b1a2f39fd8e9b
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.