@@ -3,22 +3,23 @@ import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled';
33import { getInputClassName } from './Input' ;
44import PropTypes from '../_util/vue-types' ;
55import { 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' ;
98import { tuple } from '../_util/type' ;
9+ import type { Direction , SizeType } from '../config-provider' ;
10+ import type { MouseEventHandler } from '../_util/EventInterface' ;
1011
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 ) ;
1718}
1819
1920const ClearableInputType = [ 'text' , 'input' ] ;
2021
21- const ClearableLabeledInput = defineComponent ( {
22+ export default defineComponent ( {
2223 name : 'ClearableLabeledInput' ,
2324 inheritAttrs : false ,
2425 props : {
@@ -27,93 +28,125 @@ const ClearableLabeledInput = defineComponent({
2728 value : PropTypes . any ,
2829 defaultValue : PropTypes . any ,
2930 allowClear : PropTypes . looseBool ,
30- element : PropTypes . VNodeChild ,
31+ element : PropTypes . any ,
3132 handleReset : PropTypes . func ,
3233 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 ,
3840 readonly : PropTypes . looseBool ,
39- isFocused : PropTypes . looseBool ,
41+ focused : PropTypes . looseBool ,
42+ bordered : PropTypes . looseBool ,
43+ triggerFocus : { type : Function as PropType < ( ) => void > } ,
4044 } ,
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 ;
4455 if ( ! allowClear ) {
4556 return null ;
4657 }
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` ;
5360 return (
5461 < CloseCircleFilled
5562 onClick = { handleReset }
56- class = { classNames ( className , {
57- [ `${ className } -hidden` ] : ! showClearIcon ,
58- } ) }
63+ class = { classNames (
64+ {
65+ [ `${ className } -hidden` ] : ! needClear ,
66+ } ,
67+ className ,
68+ ) }
5969 role = "button"
6070 />
6171 ) ;
62- } ,
72+ } ;
6373
64- renderSuffix ( prefixCls : string ) {
65- const { suffix, allowClear } = this . $ props;
74+ const renderSuffix = ( prefixCls : string ) => {
75+ const { suffix = slots . suffix ?. ( ) , allowClear } = props ;
6676 if ( suffix || allowClear ) {
6777 return (
6878 < span class = { `${ prefixCls } -suffix` } >
69- { this . renderClearIcon ( prefixCls ) }
79+ { renderClearIcon ( prefixCls ) }
7080 { suffix }
7181 </ span >
7282 ) ;
7383 }
7484 return null ;
75- } ,
85+ } ;
7686
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 } ) ) {
82104 return cloneElement ( element , {
83- value : props . value ,
105+ value,
84106 } ) ;
85107 }
86108
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 ;
90110
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 ,
98122 } ) ;
99123 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 }
102131 { cloneElement ( element , {
103132 style : null ,
104- value : props . value ,
105- class : getInputClassName ( prefixCls , props . size , props . disabled ) ,
133+ value,
134+ class : getInputClassName ( prefixCls , bordered , size , disabled ) ,
106135 } ) }
107- { suffix }
136+ { suffixNode }
108137 </ span >
109- ) as VNode ;
110- } ,
138+ ) ;
139+ } ;
111140
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 ;
115148 // Not wrap when there is not addons
116- if ( ! addonBefore && ! addonAfter ) {
149+ if ( ! hasAddon ( { addonBefore, addonAfter } ) ) {
117150 return labeledElement ;
118151 }
119152
@@ -124,61 +157,74 @@ const ClearableLabeledInput = defineComponent({
124157 ) : null ;
125158 const addonAfterNode = addonAfter ? < span class = { addonClassName } > { addonAfter } </ span > : null ;
126159
127- const mergedWrapperClassName = classNames ( `${ prefixCls } -wrapper` , {
128- [ wrapperClassName ] : addonBefore || addonAfter ,
160+ const mergedWrapperClassName = classNames ( `${ prefixCls } -wrapper` , wrapperClassName , {
161+ [ ` ${ wrapperClassName } -rtl` ] : direction === 'rtl' ,
129162 } ) ;
130163
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+ ) ;
135173
136174 // Need another wrapper for changing display:table to display:inline-block
137175 // and put style prop in wrapper
138176 return (
139- < span class = { mergedGroupClassName } style = { style } >
177+ < span class = { mergedGroupClassName } style = { attrs . style } >
140178 < span class = { mergedWrapperClassName } >
141179 { addonBeforeNode }
142180 { cloneElement ( labeledElement , { style : null } ) }
143181 { addonAfterNode }
144182 </ span >
145183 </ span >
146184 ) ;
147- } ,
185+ } ;
148186
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 ;
152196 if ( ! allowClear ) {
153- return cloneElement ( element , { value } ) ;
197+ return cloneElement ( element , {
198+ value,
199+ } ) ;
154200 }
155201 const affixWrapperCls = classNames (
156- className ,
157202 `${ prefixCls } -affix-wrapper` ,
158203 `${ 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+ } ,
159210 ) ;
160211 return (
161- < span class = { affixWrapperCls } style = { style } >
212+ < span class = { affixWrapperCls } style = { attrs . style } >
162213 { cloneElement ( element , {
163214 style : null ,
164215 value,
165216 } ) }
166- { this . renderClearIcon ( prefixCls ) }
217+ { renderClearIcon ( prefixCls ) }
167218 </ span >
168219 ) ;
169- } ,
220+ } ;
170221
171- renderClearableLabeledInput ( ) {
172- const { prefixCls, inputType, element } = this . $props as any ;
222+ return ( ) => {
223+ const { prefixCls, inputType, element = slots . element ?. ( ) } = props ;
173224 if ( inputType === ClearableInputType [ 0 ] ) {
174- return this . renderTextAreaWithClearIcon ( prefixCls , element ) ;
225+ return renderTextAreaWithClearIcon ( prefixCls , element ) ;
175226 }
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+ } ;
181229 } ,
182230} ) ;
183-
184- export default ClearableLabeledInput ;
0 commit comments