@@ -7,7 +7,18 @@ import styles from './text-area.module.css'
77
88interface TextAreaProps
99 extends Omit < FieldComponentProps < HTMLTextAreaElement > , 'characterCountPosition' > ,
10- Omit < BaseFieldVariantProps , 'supportsStartAndEndSlots' | 'endSlot' | 'endSlotPosition' > {
10+ Omit <
11+ BaseFieldVariantProps ,
12+ 'supportsStartAndEndSlots' | 'endSlot' | 'endSlotPosition' | 'value'
13+ > {
14+ /**
15+ * The value of the text area.
16+ *
17+ * If this prop is not specified, the text area will be uncontrolled and the component will
18+ * manage its own state.
19+ */
20+ value ?: string
21+
1122 /**
1223 * The number of visible text lines for the text area.
1324 *
@@ -62,39 +73,13 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(function T
6273 const internalRef = React . useRef < HTMLTextAreaElement > ( null )
6374 const combinedRef = useMergeRefs ( [ ref , internalRef ] )
6475
76+ useAutoExpand ( { value, autoExpand, containerRef, internalRef } )
77+
6578 const textAreaClassName = classNames ( [
6679 autoExpand ? styles . disableResize : null ,
6780 disableResize ? styles . disableResize : null ,
6881 ] )
6982
70- React . useEffect (
71- function setupAutoExpand ( ) {
72- const containerElement = containerRef . current
73-
74- function handleAutoExpand ( value : string ) {
75- if ( containerElement ) {
76- containerElement . dataset . replicatedValue = value
77- }
78- }
79-
80- function handleInput ( event : Event ) {
81- handleAutoExpand ( ( event . currentTarget as HTMLTextAreaElement ) . value )
82- }
83-
84- const textAreaElement = internalRef . current
85- if ( ! textAreaElement || ! autoExpand ) {
86- return undefined
87- }
88-
89- // Apply change initially, in case the text area has a non-empty initial value
90- handleAutoExpand ( textAreaElement . value )
91-
92- textAreaElement . addEventListener ( 'input' , handleInput )
93- return ( ) => textAreaElement . removeEventListener ( 'input' , handleInput )
94- } ,
95- [ autoExpand ] ,
96- )
97-
9883 return (
9984 < BaseField
10085 variant = { variant }
@@ -139,5 +124,60 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(function T
139124 )
140125} )
141126
127+ function useAutoExpand ( {
128+ value,
129+ autoExpand,
130+ containerRef,
131+ internalRef,
132+ } : {
133+ value : string | undefined
134+ autoExpand : boolean
135+ containerRef : React . RefObject < HTMLDivElement >
136+ internalRef : React . RefObject < HTMLTextAreaElement >
137+ } ) {
138+ const isControlled = value !== undefined
139+
140+ React . useEffect (
141+ function setupAutoExpandWhenUncontrolled ( ) {
142+ const textAreaElement = internalRef . current
143+ if ( ! textAreaElement || ! autoExpand || isControlled ) {
144+ return undefined
145+ }
146+
147+ const containerElement = containerRef . current
148+
149+ function handleAutoExpand ( value : string ) {
150+ if ( containerElement ) {
151+ containerElement . dataset . replicatedValue = value
152+ }
153+ }
154+
155+ function handleInput ( event : Event ) {
156+ handleAutoExpand ( ( event . currentTarget as HTMLTextAreaElement ) . value )
157+ }
158+
159+ // Apply change initially, in case the text area has a non-empty initial value
160+ handleAutoExpand ( textAreaElement . value )
161+ textAreaElement . addEventListener ( 'input' , handleInput )
162+ return ( ) => textAreaElement . removeEventListener ( 'input' , handleInput )
163+ } ,
164+ [ autoExpand , containerRef , internalRef , isControlled ] ,
165+ )
166+
167+ React . useEffect (
168+ function setupAutoExpandWhenControlled ( ) {
169+ if ( ! isControlled ) {
170+ return
171+ }
172+
173+ const containerElement = containerRef . current
174+ if ( containerElement ) {
175+ containerElement . dataset . replicatedValue = value
176+ }
177+ } ,
178+ [ value , containerRef , isControlled ] ,
179+ )
180+ }
181+
142182export { TextArea }
143183export type { TextAreaProps }
0 commit comments