import { forwardRef, useId, useCallback, useRef, useEffect, type TextareaHTMLAttributes } from 'react' import { cn } from '@/lib/utils' export interface TextareaProps extends TextareaHTMLAttributes { label: string description?: string hint?: string error?: string variant?: 'outlined' | 'stacked' resize?: 'vertical' | 'horizontal' | 'both' | 'none' autoResize?: boolean } export const Textarea = forwardRef( ( { label, description, hint, error, variant = 'outlined', resize = 'vertical', autoResize = false, disabled, readOnly, maxLength, value, defaultValue, rows = 3, className, id: idProp, onChange, ...props }, ref, ) => { const autoId = useId() const id = idProp ?? autoId const descriptionId = `${id}-description` const hintId = `${id}-hint` const hasError = !!error const supportiveText = error || hint const isStacked = variant === 'stacked' const internalRef = useRef(null) const currentLength = maxLength != null && typeof value === 'string' ? value.length : undefined const describedBy = [ description && isStacked ? descriptionId : undefined, supportiveText ? hintId : undefined, ] .filter(Boolean) .join(' ') || undefined const resizeClass: Record = { vertical: 'resize-y', horizontal: 'resize-x', both: 'resize', none: 'resize-none', } const adjustHeight = useCallback(() => { const el = internalRef.current if (!el || !autoResize) return el.style.height = 'auto' el.style.height = `${el.scrollHeight}px` }, [autoResize]) useEffect(() => { adjustHeight() }, [adjustHeight, value]) const setRefs = useCallback( (node: HTMLTextAreaElement | null) => { internalRef.current = node if (typeof ref === 'function') ref(node) else if (ref) ref.current = node }, [ref], ) return (
{isStacked && (
{description && (

{description}

)}
)}
{!isStacked && ( )}