| import React, { useEffect, useLayoutEffect, useRef } from "react"; |
|
|
| interface AutoResizeTextareaProps |
| extends React.TextareaHTMLAttributes<HTMLTextAreaElement> { |
| value: string; |
| onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void; |
| className: string; |
| minHeight?: string; |
| maxHeight?: string; |
| } |
|
|
| const AutoResizeTextarea: React.FC<AutoResizeTextareaProps> = ({ |
| value, |
| onChange, |
| className, |
| minHeight = "30px", |
| maxHeight = "120px", |
| ...props |
| }) => { |
| const textareaRef = useRef<HTMLTextAreaElement>(null); |
| const observerRef = useRef<ResizeObserver | null>(null); |
|
|
| const adjustHeight = () => { |
| const textarea = textareaRef.current; |
| if (!textarea) return; |
|
|
| |
| textarea.style.height = minHeight; |
|
|
| |
| const minHeightPx = parseInt(minHeight); |
| const maxHeightPx = parseInt(maxHeight); |
|
|
| |
| const desiredHeight = Math.min( |
| Math.max(minHeightPx, textarea.scrollHeight), |
| maxHeightPx |
| ); |
| textarea.style.height = `${desiredHeight}px`; |
|
|
| |
| textarea.style.overflowY = |
| textarea.scrollHeight > maxHeightPx ? "auto" : "hidden"; |
| }; |
|
|
| |
| useLayoutEffect(() => { |
| adjustHeight(); |
| }, []); |
|
|
| |
| useEffect(() => { |
| adjustHeight(); |
| }, [value]); |
|
|
| |
| useEffect(() => { |
| const textarea = textareaRef.current; |
| if (!textarea) return; |
|
|
| |
| observerRef.current = new ResizeObserver(() => { |
| adjustHeight(); |
| }); |
|
|
| |
| observerRef.current.observe(textarea); |
| if (textarea.parentElement) { |
| observerRef.current.observe(textarea.parentElement); |
| } |
|
|
| |
| const handleResize = () => adjustHeight(); |
| window.addEventListener("resize", handleResize); |
|
|
| |
| const intersectionObserver = new IntersectionObserver( |
| (entries) => { |
| entries.forEach((entry) => { |
| if (entry.isIntersecting) { |
| adjustHeight(); |
| } |
| }); |
| }, |
| { threshold: 0.1 } |
| ); |
|
|
| intersectionObserver.observe(textarea); |
|
|
| return () => { |
| window.removeEventListener("resize", handleResize); |
| if (observerRef.current) { |
| observerRef.current.disconnect(); |
| } |
| intersectionObserver.disconnect(); |
| }; |
| }, []); |
|
|
| return ( |
| <textarea |
| ref={textareaRef} |
| value={value} |
| onChange={onChange} |
| className={className} |
| style={{ |
| minHeight, |
| maxHeight, |
| overflowY: "auto", |
| resize: "none", |
| ...props.style, |
| }} |
| {...props} |
| /> |
| ); |
| }; |
|
|
| export default AutoResizeTextarea; |
|
|