'use client'; import React, { useLayoutEffect, useEffect, useRef, useCallback } from 'react'; import { FormFieldComponent, AutomationProps } from '../ui-reflection/types'; import { useAutomationIdAndRegister } from '../ui-reflection/useAutomationIdAndRegister'; import { cn } from '../lib/utils'; type TextAreaSize = 'sm' | 'md' | 'lg'; const textAreaSizeClasses: Record = { sm: 'py-1 px-2 text-xs', md: 'py-2 px-3', lg: 'py-3 px-4 text-base', }; interface TextAreaProps extends Omit, 'id'> { label?: string; /** Unique identifier for UI reflection system */ id?: string; /** Whether the textarea is required */ required?: boolean; /** Ref for the textarea element */ ref?: React.Ref; /** Optional wrapper class overrides */ wrapperClassName?: string; /** Size variant */ size?: TextAreaSize; } export function TextArea({ label, onChange, className, value = '', id, disabled, required, size = 'md', ref: forwardedRef, wrapperClassName, "data-automation-id": dataAutomationId, ...props }: TextAreaProps & AutomationProps) { const textareaRef = useRef(null); const mergedRef = useCallback( (node: HTMLTextAreaElement | null) => { textareaRef.current = node; if (typeof forwardedRef === 'function') { forwardedRef(node); } else if (forwardedRef) { forwardedRef.current = node; } }, [forwardedRef] ); const adjustHeight = (element: HTMLTextAreaElement) => { // Temporarily collapse to get the minimum height element.style.height = 'auto'; // Get the computed line height to ensure proper minimum height const computedStyle = window.getComputedStyle(element); const lineHeight = parseInt(computedStyle.lineHeight); // Calculate height based on content const newHeight = Math.max( element.scrollHeight, lineHeight * 1.5 // Minimum height of ~1.5 lines ); // Set the new height element.style.height = `${newHeight}px`; }; // Initial setup and content-based adjustment useEffect(() => { if (textareaRef.current) { const element = textareaRef.current; // Ensure proper initial display element.style.height = 'auto'; element.style.overflow = 'hidden'; // Force a reflow and adjust height void element.offsetHeight; adjustHeight(element); } }, []); // Handle value changes useLayoutEffect(() => { if (textareaRef.current) { adjustHeight(textareaRef.current); } }, [value]); // Use provided data-automation-id or register normally const { automationIdProps: textAreaProps, updateMetadata } = useAutomationIdAndRegister({ type: 'formField', fieldType: 'textField', id, label, value: typeof value === 'string' ? value : undefined, disabled, required }, true, dataAutomationId); // Always use the generated automation props (which include our override ID if provided) const finalAutomationProps = textAreaProps; // Update metadata when field props change useEffect(() => { if (updateMetadata && typeof value === 'string') { updateMetadata({ value, label, disabled, required }); } }, [value, updateMetadata, label, disabled, required]); const handleInput = (e: React.ChangeEvent) => { if (textareaRef.current) { adjustHeight(textareaRef.current); } if (onChange) { onChange(e); } }; return (
{label && ( )}