ChatInput.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import { useState, useRef, useEffect, useCallback } from 'react';
  2. interface ChatInputProps {
  3. onSendMessage: (text: string) => void;
  4. onStopTask: () => void;
  5. disabled: boolean;
  6. showStopButton: boolean;
  7. setContent?: (setter: (text: string) => void) => void;
  8. isDarkMode?: boolean;
  9. }
  10. export default function ChatInput({
  11. onSendMessage,
  12. onStopTask,
  13. disabled,
  14. showStopButton,
  15. setContent,
  16. isDarkMode = false,
  17. }: ChatInputProps) {
  18. const [text, setText] = useState('');
  19. const textareaRef = useRef<HTMLTextAreaElement>(null);
  20. // Handle text changes and resize textarea
  21. const handleTextChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
  22. const newText = e.target.value;
  23. setText(newText);
  24. // Resize textarea
  25. const textarea = textareaRef.current;
  26. if (textarea) {
  27. textarea.style.height = 'auto';
  28. textarea.style.height = `${Math.min(textarea.scrollHeight, 100)}px`;
  29. }
  30. };
  31. // Expose a method to set content from outside
  32. useEffect(() => {
  33. if (setContent) {
  34. setContent(setText);
  35. }
  36. }, [setContent]);
  37. // Initial resize when component mounts
  38. useEffect(() => {
  39. const textarea = textareaRef.current;
  40. if (textarea) {
  41. textarea.style.height = 'auto';
  42. textarea.style.height = `${Math.min(textarea.scrollHeight, 100)}px`;
  43. }
  44. }, []);
  45. const handleSubmit = useCallback(
  46. (e: React.FormEvent) => {
  47. e.preventDefault();
  48. if (text.trim()) {
  49. onSendMessage(text);
  50. setText('');
  51. }
  52. },
  53. [text, onSendMessage],
  54. );
  55. const handleKeyDown = useCallback(
  56. (e: React.KeyboardEvent) => {
  57. if (e.key === 'Enter' && !e.shiftKey) {
  58. e.preventDefault();
  59. handleSubmit(e);
  60. }
  61. },
  62. [handleSubmit],
  63. );
  64. return (
  65. <form
  66. onSubmit={handleSubmit}
  67. className={`overflow-hidden rounded-lg border transition-colors focus-within:border-sky-400 hover:border-sky-400 ${isDarkMode ? 'border-slate-700' : ''}`}
  68. aria-label="Chat input form">
  69. <div className="flex flex-col">
  70. <textarea
  71. ref={textareaRef}
  72. value={text}
  73. onChange={handleTextChange}
  74. onKeyDown={handleKeyDown}
  75. disabled={disabled}
  76. rows={4}
  77. className={`w-full resize-none border-none p-2 focus:outline-none ${
  78. disabled
  79. ? isDarkMode
  80. ? 'bg-slate-800 text-gray-400'
  81. : 'bg-gray-100 text-gray-500'
  82. : isDarkMode
  83. ? 'bg-slate-800 text-gray-200'
  84. : 'bg-white'
  85. }`}
  86. placeholder="What can I help with?"
  87. aria-label="Message input"
  88. />
  89. <div
  90. className={`flex items-center justify-between px-2 py-1.5 ${
  91. disabled ? (isDarkMode ? 'bg-slate-800' : 'bg-gray-100') : isDarkMode ? 'bg-slate-800' : 'bg-white'
  92. }`}>
  93. <div className="flex gap-2 text-gray-500">{/* Icons can go here */}</div>
  94. {showStopButton ? (
  95. <button
  96. type="button"
  97. onClick={onStopTask}
  98. className="rounded-md bg-red-500 px-3 py-1 text-white transition-colors hover:bg-red-600">
  99. Stop
  100. </button>
  101. ) : (
  102. <button
  103. type="submit"
  104. disabled={disabled}
  105. className={`rounded-md bg-[#19C2FF] px-3 py-1 text-white transition-colors hover:bg-[#0073DC] ${disabled ? 'opacity-50' : ''}`}>
  106. Send
  107. </button>
  108. )}
  109. </div>
  110. </div>
  111. </form>
  112. );
  113. }