import { forwardRef, ReactNode, useCallback, useEffect, useImperativeHandle, useRef, useState, } from 'react'; import classnames from 'classnames'; import styles from './VRTextArea.css'; import boxStyles from '../Box.css'; import FormErrorMessage from '../sharedSubcomponents/FormErrorMessage'; import FormHelperText from '../sharedSubcomponents/FormHelperText'; import { MaxLength } from '../TextField'; import TextUI from '../TextUI'; import useInteractiveStates from '../utils/useInteractiveStates'; type Props = { // REQUIRED id: string; onChange: (arg1: { event: React.ChangeEvent; value: string }) => void; // OPTIONAL dataTestId?: string; disabled?: boolean; errorMessage?: ReactNode; hasError?: boolean; helperText?: string; label?: string; labelDisplay?: 'visible' | 'hidden'; maxLength?: MaxLength | null | undefined; name?: string; onBlur?: (arg1: { event: React.FocusEvent; value: string }) => void; onClick?: (arg1: { event: React.MouseEvent; value: string }) => void; onFocus?: (arg1: { event: React.FocusEvent; value: string }) => void; onKeyDown?: (arg1: { event: React.KeyboardEvent; value: string }) => void; placeholder?: string; readOnly?: boolean; rows?: number; value?: string; }; const TextAreaWithForwardRef = forwardRef(function TextArea( { dataTestId, disabled = false, errorMessage, hasError = false, helperText, id, label, labelDisplay, maxLength, name, onBlur, onChange, onClick, onFocus, onKeyDown, placeholder, readOnly, rows = 2, value, }: Props, ref, ) { const innerRef = useRef(null); const labelRef = useRef(null); const { handleOnBlur, handleOnFocus, handleOnMouseEnter, handleOnMouseLeave, isHovered, isFocused, } = useInteractiveStates(); // @ts-expect-error - TS2322 - Type 'HTMLDivElement | HTMLTextAreaElement | null' is not assignable to type 'HTMLTextAreaElement'. useImperativeHandle(ref, () => innerRef.current); const hasErrorMessage = Boolean(errorMessage); const isLabelVisible = labelDisplay === 'visible'; // ==== STATE ==== const [currentLength, setCurrentLength] = useState(value?.length ?? 0); const [ellipsisActive, setEllipsisActive] = useState(false); // ==== A11Y ==== let ariaDescribedby; if (hasErrorMessage) { ariaDescribedby = `${id}-error`; } if (helperText || maxLength) { ariaDescribedby = `${id}-helperText`; } const isEllipsisActive = (element: HTMLElement) => element.offsetHeight < element.scrollHeight || element.offsetWidth < element.scrollWidth; const checkEllipsisActive = useCallback(() => { if (labelRef.current && !ellipsisActive && isEllipsisActive(labelRef?.current)) { setEllipsisActive(true); } else if (labelRef.current && ellipsisActive && !isEllipsisActive(labelRef?.current)) { setEllipsisActive(false); } }, [ellipsisActive]); useEffect(() => { if (!label) return () => {}; checkEllipsisActive(); if (typeof window !== 'undefined') window.addEventListener('resize', checkEllipsisActive); return () => { if (typeof window !== 'undefined') window?.removeEventListener('resize', checkEllipsisActive); }; }, [label, checkEllipsisActive]); return (
{label && ( )}