import { TextareaHTMLAttributes, useCallback, useEffect, useRef } from "react"; export function AutoHeightTextarea({ minRows = 1, ...props }: TextareaHTMLAttributes & { minRows?: number }) { const ref = useRef(null); const calculateAndSetHeight = useCallback(() => { if (!ref.current) { return; } const { borderBottomWidth, borderTopWidth, boxSizing, lineHeight, paddingBottom, paddingTop, } = window.getComputedStyle(ref.current); ref.current.style.minHeight = lineHeight; // set height temporarily to a single row to obtain scrollHeight, disregarding empty space after text (otherwise, scrollHeight would be equal to the height of the element) - this solves auto-shrinking of the textarea (it's not needed for auto-growing it) const { scrollHeight } = ref.current; // scrollHeight = content height + padding top + padding bottom if (boxSizing === "border-box") { const minHeight = parseFloat(lineHeight) * minRows + parseFloat(paddingTop) + parseFloat(paddingBottom) + parseFloat(borderTopWidth) + parseFloat(borderBottomWidth); const allTextHeight = scrollHeight + parseFloat(borderTopWidth) + parseFloat(borderBottomWidth); ref.current.style.minHeight = `${Math.max(minHeight, allTextHeight)}px`; } else if (boxSizing === "content-box") { const minHeight = parseFloat(lineHeight) * minRows; const allTextHeight = scrollHeight - parseFloat(paddingTop) - parseFloat(paddingBottom); ref.current.style.minHeight = `${Math.max(minHeight, allTextHeight)}px`; } else { console.error("Unknown box-sizing value."); } }, [minRows]); useEffect(() => { calculateAndSetHeight(); }, [calculateAndSetHeight]); const handleChange: React.ChangeEventHandler = (e) => { calculateAndSetHeight(); if (props.onChange) { props.onChange(e); } }; calculateAndSetHeight(); return