import React, { useState, useRef, useEffect } from 'react'; import cx from 'classnames'; import Button from '../ui/Button'; import Expand from '../icons/Expand'; import FullscreenExit from '../icons/FullscreenExit'; import { useTranslation } from 'react-i18next'; import { isMobileOrTablet } from '../../helpers/utils'; export interface Props { disabled?: boolean; value: string; onChange: (value: string) => void; onPressEnter?: (e: React.KeyboardEvent) => void; onPaste?: (e: React.ClipboardEvent) => void; onFocus?: (e: React.FocusEvent) => void; onBlur?: (e: React.FocusEvent) => void; onExpandedChange?: (expanded: boolean) => void; /** When set, shows a character counter (e.g. "0 / 500") above the textarea and enforces the max length. */ maxTextareaCharacters?: number; } const ChatTextArea: React.FC = ({ disabled = false, value, onChange, onPressEnter, onPaste, onFocus, onBlur, onExpandedChange, maxTextareaCharacters, }) => { const { t } = useTranslation(); const textareaRef = useRef(null); const innerRef = useRef(null); const MIN_HEIGHT = 36; const MAX_HEIGHT = 208; // Auto-expand textarea based on content (only when not manually expanded) useEffect(() => { const textarea = textareaRef.current; const inner = innerRef.current; if (textarea && inner) { if (value.length === 0) { textarea.style.height = `${MIN_HEIGHT}px`; inner.style.height = `${MIN_HEIGHT}px`; if (onExpandedChange) { onExpandedChange(false); } //reset the padding bottom of the chat const chat = document.getElementsByClassName('memori-chat--content'); if (chat) { const lastChild = chat[chat.length - 1]; if (lastChild) { (lastChild as HTMLElement).style.paddingBottom = '0px'; } } } else { textarea.style.height = 'auto'; const scrollHeight = textarea.scrollHeight; const newHeight = Math.min(Math.max(scrollHeight, MIN_HEIGHT), MAX_HEIGHT); textarea.style.height = `${newHeight}px`; inner.style.height = `${newHeight}px`; //set the padding bottom to the chat in order to keep the whole chat visible // take last child of chat wrapper and set the padding bottom to the height of the textarea const chat = document.getElementsByClassName('memori-chat--content'); if (chat) { const lastChild = chat[chat.length - 1]; if (lastChild) { // (lastChild as HTMLElement).style.paddingBottom = `${newHeight}px`; //then scroll to the bottom of the chat (chat[0] as HTMLElement).scrollTo({ top: (chat[0] as HTMLElement).scrollHeight, behavior: 'smooth' }); } } } } }, [value]); const displayValue = maxTextareaCharacters != null ? value.slice(0, maxTextareaCharacters) : value; // Keep parent state in sync when value exceeds limit (e.g. paste or prop change) useEffect(() => { if ( maxTextareaCharacters != null && value.length > maxTextareaCharacters ) { onChange(value.slice(0, maxTextareaCharacters)); } }, [maxTextareaCharacters, value, onChange]); return (
{maxTextareaCharacters != null && (
{displayValue.length} / {maxTextareaCharacters}
)}