1- import type { VNode } from 'vue' ;
1+ import type { VNode , CSSProperties } from 'vue' ;
22import {
3- onMounted ,
3+ computed ,
4+ watchEffect ,
45 getCurrentInstance ,
56 watch ,
67 onBeforeUnmount ,
78 ref ,
8- nextTick ,
99 defineComponent ,
1010 withDirectives ,
1111} from 'vue' ;
1212import ResizeObserver from '../vc-resize-observer' ;
1313import classNames from '../_util/classNames' ;
14- import calculateNodeHeight from './calculateNodeHeight' ;
1514import raf from '../_util/raf' ;
1615import warning from '../_util/warning' ;
1716import antInput from '../_util/antInputDirective' ;
1817import omit from '../_util/omit' ;
1918import { textAreaProps } from './inputProps' ;
19+ import calculateAutoSizeStyle from './calculateNodeHeight' ;
2020
21- const RESIZE_STATUS_NONE = 0 ;
22- const RESIZE_STATUS_RESIZING = 1 ;
23- const RESIZE_STATUS_RESIZED = 2 ;
21+ const RESIZE_START = 0 ;
22+ const RESIZE_MEASURING = 1 ;
23+ const RESIZE_STABLE = 2 ;
2424
2525const ResizableTextArea = defineComponent ( {
2626 compatConfig : { MODE : 3 } ,
@@ -32,7 +32,7 @@ const ResizableTextArea = defineComponent({
3232 let resizeFrameId : any ;
3333 const textAreaRef = ref ( ) ;
3434 const textareaStyles = ref ( { } ) ;
35- const resizeStatus = ref ( RESIZE_STATUS_NONE ) ;
35+ const resizeStatus = ref ( RESIZE_STABLE ) ;
3636 onBeforeUnmount ( ( ) => {
3737 raf . cancel ( nextFrameActionId ) ;
3838 raf . cancel ( resizeFrameId ) ;
@@ -44,57 +44,100 @@ const ResizableTextArea = defineComponent({
4444 if ( document . activeElement === textAreaRef . value ) {
4545 const currentStart = textAreaRef . value . selectionStart ;
4646 const currentEnd = textAreaRef . value . selectionEnd ;
47+ const scrollTop = textAreaRef . value . scrollTop ;
4748 textAreaRef . value . setSelectionRange ( currentStart , currentEnd ) ;
49+ textAreaRef . value . scrollTop = scrollTop ;
4850 }
4951 } catch ( e ) {
5052 // Fix error in Chrome:
5153 // Failed to read the 'selectionStart' property from 'HTMLInputElement'
5254 // http://stackoverflow.com/q/21177489/3040605
5355 }
5456 } ;
55-
56- const resizeTextarea = ( ) => {
57+ const minRows = ref < number > ( ) ;
58+ const maxRows = ref < number > ( ) ;
59+ watchEffect ( ( ) => {
5760 const autoSize = props . autoSize || props . autosize ;
58- if ( ! autoSize || ! textAreaRef . value ) {
59- return ;
61+ if ( autoSize ) {
62+ minRows . value = autoSize . minRows ;
63+ maxRows . value = autoSize . maxRows ;
64+ } else {
65+ minRows . value = undefined ;
66+ maxRows . value = undefined ;
6067 }
61- const { minRows, maxRows } = autoSize ;
62- textareaStyles . value = calculateNodeHeight ( textAreaRef . value , false , minRows , maxRows ) ;
63- resizeStatus . value = RESIZE_STATUS_RESIZING ;
64- raf . cancel ( resizeFrameId ) ;
65- resizeFrameId = raf ( ( ) => {
66- resizeStatus . value = RESIZE_STATUS_RESIZED ;
67- resizeFrameId = raf ( ( ) => {
68- resizeStatus . value = RESIZE_STATUS_NONE ;
69- fixFirefoxAutoScroll ( ) ;
70- } ) ;
71- } ) ;
68+ } ) ;
69+ const needAutoSize = computed ( ( ) => ! ! ( props . autoSize || props . autosize ) ) ;
70+ const startResize = ( ) => {
71+ resizeStatus . value = RESIZE_START ;
7272 } ;
73-
74- const resizeOnNextFrame = ( ) => {
75- raf . cancel ( nextFrameActionId ) ;
76- nextFrameActionId = raf ( resizeTextarea ) ;
73+ watch (
74+ [ ( ) => props . value , minRows , maxRows , needAutoSize ] ,
75+ ( ) => {
76+ if ( needAutoSize . value ) {
77+ startResize ( ) ;
78+ }
79+ } ,
80+ { immediate : true , flush : 'post' } ,
81+ ) ;
82+ const autoSizeStyle = ref < CSSProperties > ( ) ;
83+ watch (
84+ [ resizeStatus , textAreaRef ] ,
85+ ( ) => {
86+ if ( ! textAreaRef . value ) return ;
87+ if ( resizeStatus . value === RESIZE_START ) {
88+ resizeStatus . value = RESIZE_MEASURING ;
89+ } else if ( resizeStatus . value === RESIZE_MEASURING ) {
90+ const textareaStyles = calculateAutoSizeStyle (
91+ textAreaRef . value ,
92+ false ,
93+ minRows . value ,
94+ maxRows . value ,
95+ ) ;
96+ resizeStatus . value = RESIZE_STABLE ;
97+ autoSizeStyle . value = textareaStyles ;
98+ } else {
99+ fixFirefoxAutoScroll ( ) ;
100+ }
101+ } ,
102+ { immediate : true , flush : 'post' } ,
103+ ) ;
104+ const instance = getCurrentInstance ( ) ;
105+ const resizeRafRef = ref ( ) ;
106+ const cleanRaf = ( ) => {
107+ raf . cancel ( resizeRafRef . value ) ;
77108 } ;
109+ const onInternalResize = ( size : { width : number ; height : number } ) => {
110+ if ( resizeStatus . value === RESIZE_STABLE ) {
111+ emit ( 'resize' , size ) ;
78112
79- const handleResize = ( size : { width : number ; height : number } ) => {
80- if ( resizeStatus . value !== RESIZE_STATUS_NONE ) {
81- return ;
82- }
83- emit ( 'resize' , size ) ;
84-
85- const autoSize = props . autoSize || props . autosize ;
86- if ( autoSize ) {
87- resizeOnNextFrame ( ) ;
113+ if ( needAutoSize . value ) {
114+ cleanRaf ( ) ;
115+ resizeRafRef . value = raf ( ( ) => {
116+ startResize ( ) ;
117+ } ) ;
118+ }
88119 }
89120 } ;
121+ onBeforeUnmount ( ( ) => {
122+ cleanRaf ( ) ;
123+ } ) ;
124+ const resizeTextarea = ( ) => {
125+ startResize ( ) ;
126+ } ;
127+
128+ expose ( {
129+ resizeTextarea,
130+ textArea : textAreaRef ,
131+ instance,
132+ } ) ;
90133 warning (
91134 props . autosize === undefined ,
92135 'Input.TextArea' ,
93136 'autosize is deprecated, please use autoSize instead.' ,
94137 ) ;
95138
96139 const renderTextArea = ( ) => {
97- const { prefixCls, autoSize , autosize , disabled } = props ;
140+ const { prefixCls, disabled } = props ;
98141 const otherProps = omit ( props , [
99142 'prefixCls' ,
100143 'onPressEnter' ,
@@ -110,54 +153,35 @@ const ResizableTextArea = defineComponent({
110153 const cls = classNames ( prefixCls , attrs . class , {
111154 [ `${ prefixCls } -disabled` ] : disabled ,
112155 } ) ;
113- const style = [
114- attrs . style ,
115- textareaStyles . value ,
116- resizeStatus . value === RESIZE_STATUS_RESIZING
117- ? { overflowX : 'hidden' , overflowY : 'hidden' }
118- : null ,
119- ] ;
156+ const mergedAutoSizeStyle = needAutoSize . value ? autoSizeStyle . value : null ;
157+ const style = [ attrs . style , textareaStyles . value , mergedAutoSizeStyle ] ;
120158 const textareaProps : any = {
121159 ...otherProps ,
122160 ...attrs ,
123161 style,
124162 class : cls ,
125163 } ;
164+ if ( resizeStatus . value === RESIZE_START || resizeStatus . value === RESIZE_MEASURING ) {
165+ style . push ( {
166+ overflowX : 'hidden' ,
167+ overflowY : 'hidden' ,
168+ } ) ;
169+ }
126170 if ( ! textareaProps . autofocus ) {
127171 delete textareaProps . autofocus ;
128172 }
129173 if ( textareaProps . rows === 0 ) {
130174 delete textareaProps . rows ;
131175 }
132176 return (
133- < ResizeObserver onResize = { handleResize } disabled = { ! ( autoSize || autosize ) } >
177+ < ResizeObserver onResize = { onInternalResize } disabled = { ! needAutoSize . value } >
134178 { withDirectives ( ( < textarea { ...textareaProps } ref = { textAreaRef } /> ) as VNode , [
135179 [ antInput ] ,
136180 ] ) }
137181 </ ResizeObserver >
138182 ) ;
139183 } ;
140184
141- watch (
142- ( ) => props . value ,
143- ( ) => {
144- nextTick ( ( ) => {
145- resizeTextarea ( ) ;
146- } ) ;
147- } ,
148- ) ;
149- onMounted ( ( ) => {
150- nextTick ( ( ) => {
151- resizeTextarea ( ) ;
152- } ) ;
153- } ) ;
154- const instance = getCurrentInstance ( ) ;
155- expose ( {
156- resizeTextarea,
157- textArea : textAreaRef ,
158- instance,
159- } ) ;
160-
161185 return ( ) => {
162186 return renderTextArea ( ) ;
163187 } ;
0 commit comments