@@ -3,22 +3,23 @@ import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled';
3
3
import { getInputClassName } from './Input' ;
4
4
import PropTypes from '../_util/vue-types' ;
5
5
import { cloneElement } from '../_util/vnode' ;
6
- import { getComponent } from '../_util/props-util' ;
7
- import type { VNode } from 'vue' ;
8
- import { defineComponent } from 'vue' ;
6
+ import type { PropType , VNode } from 'vue' ;
7
+ import { ref , defineComponent } from 'vue' ;
9
8
import { tuple } from '../_util/type' ;
9
+ import type { Direction , SizeType } from '../config-provider' ;
10
+ import type { MouseEventHandler } from '../_util/EventInterface' ;
10
11
11
- export function hasPrefixSuffix ( instance : any ) {
12
- return ! ! (
13
- getComponent ( instance , 'prefix' ) ||
14
- getComponent ( instance , 'suffix' ) ||
15
- instance . $props . allowClear
16
- ) ;
12
+ export function hasPrefixSuffix ( propsAndSlots : any ) {
13
+ return ! ! ( propsAndSlots . prefix || propsAndSlots . suffix || propsAndSlots . allowClear ) ;
14
+ }
15
+
16
+ function hasAddon ( propsAndSlots : any ) {
17
+ return ! ! ( propsAndSlots . addonBefore || propsAndSlots . addonAfter ) ;
17
18
}
18
19
19
20
const ClearableInputType = [ 'text' , 'input' ] ;
20
21
21
- const ClearableLabeledInput = defineComponent ( {
22
+ export default defineComponent ( {
22
23
name : 'ClearableLabeledInput' ,
23
24
inheritAttrs : false ,
24
25
props : {
@@ -27,93 +28,125 @@ const ClearableLabeledInput = defineComponent({
27
28
value : PropTypes . any ,
28
29
defaultValue : PropTypes . any ,
29
30
allowClear : PropTypes . looseBool ,
30
- element : PropTypes . VNodeChild ,
31
+ element : PropTypes . any ,
31
32
handleReset : PropTypes . func ,
32
33
disabled : PropTypes . looseBool ,
33
- size : PropTypes . oneOf ( tuple ( 'small' , 'large' , 'default' ) ) ,
34
- suffix : PropTypes . VNodeChild ,
35
- prefix : PropTypes . VNodeChild ,
36
- addonBefore : PropTypes . VNodeChild ,
37
- addonAfter : PropTypes . VNodeChild ,
34
+ direction : { type : String as PropType < Direction > } ,
35
+ size : { type : String as PropType < SizeType > } ,
36
+ suffix : PropTypes . any ,
37
+ prefix : PropTypes . any ,
38
+ addonBefore : PropTypes . any ,
39
+ addonAfter : PropTypes . any ,
38
40
readonly : PropTypes . looseBool ,
39
- isFocused : PropTypes . looseBool ,
41
+ focused : PropTypes . looseBool ,
42
+ bordered : PropTypes . looseBool ,
43
+ triggerFocus : { type : Function as PropType < ( ) => void > } ,
40
44
} ,
41
- methods : {
42
- renderClearIcon ( prefixCls : string ) {
43
- const { allowClear, value, disabled, readonly, inputType, handleReset } = this . $props ;
45
+ setup ( props , { slots, attrs } ) {
46
+ const containerRef = ref ( ) ;
47
+ const onInputMouseUp : MouseEventHandler = e => {
48
+ if ( containerRef . value ?. contains ( e . target as Element ) ) {
49
+ const { triggerFocus } = props ;
50
+ triggerFocus ?.( ) ;
51
+ }
52
+ } ;
53
+ const renderClearIcon = ( prefixCls : string ) => {
54
+ const { allowClear, value, disabled, readonly, handleReset } = props ;
44
55
if ( ! allowClear ) {
45
56
return null ;
46
57
}
47
- const showClearIcon =
48
- ! disabled && ! readonly && value !== undefined && value !== null && value !== '' ;
49
- const className =
50
- inputType === ClearableInputType [ 0 ]
51
- ? `${ prefixCls } -textarea-clear-icon`
52
- : `${ prefixCls } -clear-icon` ;
58
+ const needClear = ! disabled && ! readonly && value ;
59
+ const className = `${ prefixCls } -clear-icon` ;
53
60
return (
54
61
< CloseCircleFilled
55
62
onClick = { handleReset }
56
- class = { classNames ( className , {
57
- [ `${ className } -hidden` ] : ! showClearIcon ,
58
- } ) }
63
+ class = { classNames (
64
+ {
65
+ [ `${ className } -hidden` ] : ! needClear ,
66
+ } ,
67
+ className ,
68
+ ) }
59
69
role = "button"
60
70
/>
61
71
) ;
62
- } ,
72
+ } ;
63
73
64
- renderSuffix ( prefixCls : string ) {
65
- const { suffix, allowClear } = this . $ props;
74
+ const renderSuffix = ( prefixCls : string ) => {
75
+ const { suffix = slots . suffix ?. ( ) , allowClear } = props ;
66
76
if ( suffix || allowClear ) {
67
77
return (
68
78
< span class = { `${ prefixCls } -suffix` } >
69
- { this . renderClearIcon ( prefixCls ) }
79
+ { renderClearIcon ( prefixCls ) }
70
80
{ suffix }
71
81
</ span >
72
82
) ;
73
83
}
74
84
return null ;
75
- } ,
85
+ } ;
76
86
77
- renderLabeledIcon ( prefixCls : string , element : VNode ) : VNode {
78
- const props = this . $props ;
79
- const { style } = this . $attrs ;
80
- const suffix = this . renderSuffix ( prefixCls ) ;
81
- if ( ! hasPrefixSuffix ( this ) ) {
87
+ const renderLabeledIcon = ( prefixCls : string , element : VNode ) => {
88
+ const {
89
+ focused,
90
+ value,
91
+ prefix = slots . prefix ?.( ) ,
92
+ size,
93
+ suffix = slots . suffix ?.( ) ,
94
+ disabled,
95
+ allowClear,
96
+ direction,
97
+ readonly,
98
+ bordered,
99
+ addonAfter = slots . addonAfter ,
100
+ addonBefore = slots . addonBefore ,
101
+ } = props ;
102
+ const suffixNode = renderSuffix ( prefixCls ) ;
103
+ if ( ! hasPrefixSuffix ( { prefix, suffix, allowClear } ) ) {
82
104
return cloneElement ( element , {
83
- value : props . value ,
105
+ value,
84
106
} ) ;
85
107
}
86
108
87
- const prefix = props . prefix ? (
88
- < span class = { `${ prefixCls } -prefix` } > { props . prefix } </ span >
89
- ) : null ;
109
+ const prefixNode = prefix ? < span class = { `${ prefixCls } -prefix` } > { prefix } </ span > : null ;
90
110
91
- const affixWrapperCls = classNames ( this . $attrs ?. class , `${ prefixCls } -affix-wrapper` , {
92
- [ `${ prefixCls } -affix-wrapper-focused` ] : props . isFocused ,
93
- [ `${ prefixCls } -affix-wrapper-disabled` ] : props . disabled ,
94
- [ `${ prefixCls } -affix-wrapper-sm` ] : props . size === 'small' ,
95
- [ `${ prefixCls } -affix-wrapper-lg` ] : props . size === 'large' ,
96
- [ `${ prefixCls } -affix-wrapper-input-with-clear-btn` ] :
97
- props . suffix && props . allowClear && this . $props . value ,
111
+ const affixWrapperCls = classNames ( `${ prefixCls } -affix-wrapper` , {
112
+ [ `${ prefixCls } -affix-wrapper-focused` ] : focused ,
113
+ [ `${ prefixCls } -affix-wrapper-disabled` ] : disabled ,
114
+ [ `${ prefixCls } -affix-wrapper-sm` ] : size === 'small' ,
115
+ [ `${ prefixCls } -affix-wrapper-lg` ] : size === 'large' ,
116
+ [ `${ prefixCls } -affix-wrapper-input-with-clear-btn` ] : suffix && allowClear && value ,
117
+ [ `${ prefixCls } -affix-wrapper-rtl` ] : direction === 'rtl' ,
118
+ [ `${ prefixCls } -affix-wrapper-readonly` ] : readonly ,
119
+ [ `${ prefixCls } -affix-wrapper-borderless` ] : ! bordered ,
120
+ // className will go to addon wrapper
121
+ [ `${ attrs . class } ` ] : ! hasAddon ( { addonAfter, addonBefore } ) && attrs . class ,
98
122
} ) ;
99
123
return (
100
- < span class = { affixWrapperCls } style = { style } >
101
- { prefix }
124
+ < span
125
+ ref = { containerRef }
126
+ class = { affixWrapperCls }
127
+ style = { attrs . style }
128
+ onMouseup = { onInputMouseUp }
129
+ >
130
+ { prefixNode }
102
131
{ cloneElement ( element , {
103
132
style : null ,
104
- value : props . value ,
105
- class : getInputClassName ( prefixCls , props . size , props . disabled ) ,
133
+ value,
134
+ class : getInputClassName ( prefixCls , bordered , size , disabled ) ,
106
135
} ) }
107
- { suffix }
136
+ { suffixNode }
108
137
</ span >
109
- ) as VNode ;
110
- } ,
138
+ ) ;
139
+ } ;
111
140
112
- renderInputWithLabel ( prefixCls : string , labeledElement : VNode ) {
113
- const { addonBefore, addonAfter, size } = this . $props ;
114
- const { style, class : className } = this . $attrs ;
141
+ const renderInputWithLabel = ( prefixCls : string , labeledElement : VNode ) => {
142
+ const {
143
+ addonBefore = slots . addonBefore ?.( ) ,
144
+ addonAfter = slots . addonAfter ?.( ) ,
145
+ size,
146
+ direction,
147
+ } = props ;
115
148
// Not wrap when there is not addons
116
- if ( ! addonBefore && ! addonAfter ) {
149
+ if ( ! hasAddon ( { addonBefore, addonAfter } ) ) {
117
150
return labeledElement ;
118
151
}
119
152
@@ -124,61 +157,74 @@ const ClearableLabeledInput = defineComponent({
124
157
) : null ;
125
158
const addonAfterNode = addonAfter ? < span class = { addonClassName } > { addonAfter } </ span > : null ;
126
159
127
- const mergedWrapperClassName = classNames ( `${ prefixCls } -wrapper` , {
128
- [ wrapperClassName ] : addonBefore || addonAfter ,
160
+ const mergedWrapperClassName = classNames ( `${ prefixCls } -wrapper` , wrapperClassName , {
161
+ [ ` ${ wrapperClassName } -rtl` ] : direction === 'rtl' ,
129
162
} ) ;
130
163
131
- const mergedGroupClassName = classNames ( className , `${ prefixCls } -group-wrapper` , {
132
- [ `${ prefixCls } -group-wrapper-sm` ] : size === 'small' ,
133
- [ `${ prefixCls } -group-wrapper-lg` ] : size === 'large' ,
134
- } ) ;
164
+ const mergedGroupClassName = classNames (
165
+ `${ prefixCls } -group-wrapper` ,
166
+ {
167
+ [ `${ prefixCls } -group-wrapper-sm` ] : size === 'small' ,
168
+ [ `${ prefixCls } -group-wrapper-lg` ] : size === 'large' ,
169
+ [ `${ prefixCls } -group-wrapper-rtl` ] : direction === 'rtl' ,
170
+ } ,
171
+ attrs . class ,
172
+ ) ;
135
173
136
174
// Need another wrapper for changing display:table to display:inline-block
137
175
// and put style prop in wrapper
138
176
return (
139
- < span class = { mergedGroupClassName } style = { style } >
177
+ < span class = { mergedGroupClassName } style = { attrs . style } >
140
178
< span class = { mergedWrapperClassName } >
141
179
{ addonBeforeNode }
142
180
{ cloneElement ( labeledElement , { style : null } ) }
143
181
{ addonAfterNode }
144
182
</ span >
145
183
</ span >
146
184
) ;
147
- } ,
185
+ } ;
148
186
149
- renderTextAreaWithClearIcon ( prefixCls : string , element : VNode ) {
150
- const { value, allowClear } = this . $props ;
151
- const { style, class : className } = this . $attrs ;
187
+ const renderTextAreaWithClearIcon = ( prefixCls : string , element : VNode ) => {
188
+ const {
189
+ value,
190
+ allowClear,
191
+ direction,
192
+ bordered,
193
+ addonAfter = slots . addonAfter ,
194
+ addonBefore = slots . addonBefore ,
195
+ } = props ;
152
196
if ( ! allowClear ) {
153
- return cloneElement ( element , { value } ) ;
197
+ return cloneElement ( element , {
198
+ value,
199
+ } ) ;
154
200
}
155
201
const affixWrapperCls = classNames (
156
- className ,
157
202
`${ prefixCls } -affix-wrapper` ,
158
203
`${ prefixCls } -affix-wrapper-textarea-with-clear-btn` ,
204
+ {
205
+ [ `${ prefixCls } -affix-wrapper-rtl` ] : direction === 'rtl' ,
206
+ [ `${ prefixCls } -affix-wrapper-borderless` ] : ! bordered ,
207
+ // className will go to addon wrapper
208
+ [ `${ attrs . class } ` ] : ! hasAddon ( { addonAfter, addonBefore } ) && attrs . class ,
209
+ } ,
159
210
) ;
160
211
return (
161
- < span class = { affixWrapperCls } style = { style } >
212
+ < span class = { affixWrapperCls } style = { attrs . style } >
162
213
{ cloneElement ( element , {
163
214
style : null ,
164
215
value,
165
216
} ) }
166
- { this . renderClearIcon ( prefixCls ) }
217
+ { renderClearIcon ( prefixCls ) }
167
218
</ span >
168
219
) ;
169
- } ,
220
+ } ;
170
221
171
- renderClearableLabeledInput ( ) {
172
- const { prefixCls, inputType, element } = this . $props as any ;
222
+ return ( ) => {
223
+ const { prefixCls, inputType, element = slots . element ?. ( ) } = props ;
173
224
if ( inputType === ClearableInputType [ 0 ] ) {
174
- return this . renderTextAreaWithClearIcon ( prefixCls , element ) ;
225
+ return renderTextAreaWithClearIcon ( prefixCls , element ) ;
175
226
}
176
- return this . renderInputWithLabel ( prefixCls , this . renderLabeledIcon ( prefixCls , element ) ) ;
177
- } ,
178
- } ,
179
- render ( ) {
180
- return this . renderClearableLabeledInput ( ) ;
227
+ return renderInputWithLabel ( prefixCls , renderLabeledIcon ( prefixCls , element ) ) ;
228
+ } ;
181
229
} ,
182
230
} ) ;
183
-
184
- export default ClearableLabeledInput ;
0 commit comments