1
- import type { VNode } from 'vue' ;
1
+ import type { VNode , CSSProperties } from 'vue' ;
2
2
import {
3
- onMounted ,
3
+ computed ,
4
+ watchEffect ,
4
5
getCurrentInstance ,
5
6
watch ,
6
7
onBeforeUnmount ,
7
8
ref ,
8
- nextTick ,
9
9
defineComponent ,
10
10
withDirectives ,
11
11
} from 'vue' ;
12
12
import ResizeObserver from '../vc-resize-observer' ;
13
13
import classNames from '../_util/classNames' ;
14
- import calculateNodeHeight from './calculateNodeHeight' ;
15
14
import raf from '../_util/raf' ;
16
15
import warning from '../_util/warning' ;
17
16
import antInput from '../_util/antInputDirective' ;
18
17
import omit from '../_util/omit' ;
19
18
import { textAreaProps } from './inputProps' ;
19
+ import calculateAutoSizeStyle from './calculateNodeHeight' ;
20
20
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 ;
24
24
25
25
const ResizableTextArea = defineComponent ( {
26
26
compatConfig : { MODE : 3 } ,
@@ -32,7 +32,7 @@ const ResizableTextArea = defineComponent({
32
32
let resizeFrameId : any ;
33
33
const textAreaRef = ref ( ) ;
34
34
const textareaStyles = ref ( { } ) ;
35
- const resizeStatus = ref ( RESIZE_STATUS_NONE ) ;
35
+ const resizeStatus = ref ( RESIZE_STABLE ) ;
36
36
onBeforeUnmount ( ( ) => {
37
37
raf . cancel ( nextFrameActionId ) ;
38
38
raf . cancel ( resizeFrameId ) ;
@@ -44,57 +44,100 @@ const ResizableTextArea = defineComponent({
44
44
if ( document . activeElement === textAreaRef . value ) {
45
45
const currentStart = textAreaRef . value . selectionStart ;
46
46
const currentEnd = textAreaRef . value . selectionEnd ;
47
+ const scrollTop = textAreaRef . value . scrollTop ;
47
48
textAreaRef . value . setSelectionRange ( currentStart , currentEnd ) ;
49
+ textAreaRef . value . scrollTop = scrollTop ;
48
50
}
49
51
} catch ( e ) {
50
52
// Fix error in Chrome:
51
53
// Failed to read the 'selectionStart' property from 'HTMLInputElement'
52
54
// http://stackoverflow.com/q/21177489/3040605
53
55
}
54
56
} ;
55
-
56
- const resizeTextarea = ( ) => {
57
+ const minRows = ref < number > ( ) ;
58
+ const maxRows = ref < number > ( ) ;
59
+ watchEffect ( ( ) => {
57
60
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 ;
60
67
}
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 ;
72
72
} ;
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 ) ;
77
108
} ;
109
+ const onInternalResize = ( size : { width : number ; height : number } ) => {
110
+ if ( resizeStatus . value === RESIZE_STABLE ) {
111
+ emit ( 'resize' , size ) ;
78
112
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
+ }
88
119
}
89
120
} ;
121
+ onBeforeUnmount ( ( ) => {
122
+ cleanRaf ( ) ;
123
+ } ) ;
124
+ const resizeTextarea = ( ) => {
125
+ startResize ( ) ;
126
+ } ;
127
+
128
+ expose ( {
129
+ resizeTextarea,
130
+ textArea : textAreaRef ,
131
+ instance,
132
+ } ) ;
90
133
warning (
91
134
props . autosize === undefined ,
92
135
'Input.TextArea' ,
93
136
'autosize is deprecated, please use autoSize instead.' ,
94
137
) ;
95
138
96
139
const renderTextArea = ( ) => {
97
- const { prefixCls, autoSize , autosize , disabled } = props ;
140
+ const { prefixCls, disabled } = props ;
98
141
const otherProps = omit ( props , [
99
142
'prefixCls' ,
100
143
'onPressEnter' ,
@@ -110,54 +153,35 @@ const ResizableTextArea = defineComponent({
110
153
const cls = classNames ( prefixCls , attrs . class , {
111
154
[ `${ prefixCls } -disabled` ] : disabled ,
112
155
} ) ;
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 ] ;
120
158
const textareaProps : any = {
121
159
...otherProps ,
122
160
...attrs ,
123
161
style,
124
162
class : cls ,
125
163
} ;
164
+ if ( resizeStatus . value === RESIZE_START || resizeStatus . value === RESIZE_MEASURING ) {
165
+ style . push ( {
166
+ overflowX : 'hidden' ,
167
+ overflowY : 'hidden' ,
168
+ } ) ;
169
+ }
126
170
if ( ! textareaProps . autofocus ) {
127
171
delete textareaProps . autofocus ;
128
172
}
129
173
if ( textareaProps . rows === 0 ) {
130
174
delete textareaProps . rows ;
131
175
}
132
176
return (
133
- < ResizeObserver onResize = { handleResize } disabled = { ! ( autoSize || autosize ) } >
177
+ < ResizeObserver onResize = { onInternalResize } disabled = { ! needAutoSize . value } >
134
178
{ withDirectives ( ( < textarea { ...textareaProps } ref = { textAreaRef } /> ) as VNode , [
135
179
[ antInput ] ,
136
180
] ) }
137
181
</ ResizeObserver >
138
182
) ;
139
183
} ;
140
184
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
-
161
185
return ( ) => {
162
186
return renderTextArea ( ) ;
163
187
} ;
0 commit comments