@@ -3,6 +3,10 @@ import {
33 forwardRef ,
44 TextareaHTMLAttributes ,
55 ForwardedRef ,
6+ useEffect ,
7+ useRef ,
8+ useImperativeHandle ,
9+ useCallback ,
610} from 'react' ;
711import styled , { css } from 'styled-components' ;
812import { spacing } from '../../spacing' ;
@@ -12,13 +16,20 @@ type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & {
1216 variant ?: TextAreaVariant ;
1317 width ?: CSSProperties [ 'width' ] ;
1418 height ?: CSSProperties [ 'height' ] ;
19+ /**
20+ * Automatically adjust height to fit content
21+ * When enabled, the textarea will grow/shrink to show all content
22+ * It disables the resize property
23+ */
24+ autoGrow ?: boolean ;
1525} ;
1626type RefType = HTMLTextAreaElement | null ;
1727
1828const TextAreaContainer = styled . textarea < {
1929 variant : TextAreaVariant ;
2030 width ?: CSSProperties [ 'width' ] ;
2131 height ?: CSSProperties [ 'height' ] ;
32+ autoGrow ?: boolean ;
2233} > `
2334 padding: ${ spacing . r12 } ${ spacing . r8 } ;
2435 border-radius: 4px;
@@ -46,6 +57,13 @@ const TextAreaContainer = styled.textarea<{
4657 height : ${ props . height } ;
4758 ` }
4859
60+ ${ ( props ) =>
61+ props . autoGrow &&
62+ css `
63+ resize : none;
64+ overflow : hidden;
65+ ` }
66+
4967 &:placeholder-shown {
5068 font-style: italic;
5169 }
@@ -77,18 +95,55 @@ const TextAreaContainer = styled.textarea<{
7795` ;
7896
7997function TextAreaElement (
80- { rows = 3 , cols = 20 , width, height, variant = 'code' , ...rest } : Props ,
98+ {
99+ rows = 3 ,
100+ cols = 20 ,
101+ width,
102+ height,
103+ variant = 'code' ,
104+ autoGrow = false ,
105+ value,
106+ defaultValue,
107+ onChange,
108+ ...rest
109+ } : Props ,
81110 ref : ForwardedRef < RefType > ,
82111) {
112+ const internalRef = useRef < HTMLTextAreaElement > ( null ) ;
113+
114+ // Expose the textarea element to parent components via forwarded ref
115+ useImperativeHandle ( ref , ( ) => internalRef . current as HTMLTextAreaElement ) ;
116+
117+ const adjustHeight = useCallback ( ( ) => {
118+ const textarea = internalRef . current ;
119+ if ( ! textarea || ! autoGrow ) return ;
120+
121+ // Reset height to auto to get the correct scrollHeight
122+ textarea . style . height = 'auto' ;
123+
124+ // Set the height to match the content
125+ const newHeight = textarea . scrollHeight ;
126+ textarea . style . height = `${ newHeight } px` ;
127+ } , [ autoGrow ] ) ;
128+
129+ // Adjust height on mount to fit initial content
130+ useEffect ( ( ) => {
131+ adjustHeight ( ) ;
132+ } , [ ] ) ;
133+
83134 if ( width || height ) {
84135 return (
85136 < TextAreaContainer
86137 className = "sc-textarea"
87138 width = { width }
88139 height = { height }
89140 variant = { variant }
141+ autoGrow = { autoGrow }
142+ value = { value }
143+ defaultValue = { defaultValue }
144+ onChange = { onChange }
90145 { ...rest }
91- ref = { ref }
146+ ref = { internalRef }
92147 />
93148 ) ;
94149 }
@@ -99,8 +154,12 @@ function TextAreaElement(
99154 rows = { rows }
100155 cols = { cols }
101156 variant = { variant }
157+ autoGrow = { autoGrow }
158+ value = { value }
159+ defaultValue = { defaultValue }
160+ onChange = { onChange }
102161 { ...rest }
103- ref = { ref }
162+ ref = { internalRef }
104163 />
105164 ) ;
106165}
0 commit comments