1
- import type { App , PropType , VNodeTypes , Plugin , ExtractPropTypes } from 'vue' ;
2
- import { defineComponent , inject , nextTick } from 'vue' ;
1
+ import type { App , PropType , Plugin , ExtractPropTypes } from 'vue' ;
2
+ import { watch } from 'vue' ;
3
+ import { ref , onMounted } from 'vue' ;
4
+ import { defineComponent , nextTick } from 'vue' ;
3
5
import classNames from '../_util/classNames' ;
4
6
import omit from 'omit.js' ;
5
7
import PropTypes from '../_util/vue-types' ;
6
8
import VcMentions from '../vc-mentions' ;
7
9
import { mentionsProps as baseMentionsProps } from '../vc-mentions/src/mentionsProps' ;
8
- import Spin from '../spin' ;
9
- import BaseMixin from '../_util/BaseMixin' ;
10
- import { defaultConfigProvider } from '../config-provider' ;
11
- import { getOptionProps , getComponent , getSlot } from '../_util/props-util' ;
12
- import type { RenderEmptyHandler } from '../config-provider/renderEmpty' ;
10
+ import useConfigInject from '../_util/hooks/useConfigInject' ;
11
+ import { flattenChildren , getOptionProps } from '../_util/props-util' ;
13
12
14
13
const { Option } = VcMentions ;
15
14
@@ -20,23 +19,26 @@ interface MentionsConfig {
20
19
21
20
export interface MentionsOptionProps {
22
21
value : string ;
23
- disabled : boolean ;
24
- children : VNodeTypes ;
22
+ disabled ? : boolean ;
23
+ label ?: string | number | ( ( o : MentionsOptionProps ) => any ) ;
25
24
[ key : string ] : any ;
26
25
}
27
26
28
- function loadingFilterOption ( ) {
29
- return true ;
27
+ interface MentionsEntity {
28
+ prefix : string ;
29
+ value : string ;
30
30
}
31
31
32
- function getMentions ( value = '' , config : MentionsConfig ) {
32
+ export type MentionPlacement = 'top' | 'bottom' ;
33
+
34
+ const getMentions = ( value = '' , config : MentionsConfig ) : MentionsEntity [ ] => {
33
35
const { prefix = '@' , split = ' ' } = config || { } ;
34
- const prefixList = Array . isArray ( prefix ) ? prefix : [ prefix ] ;
36
+ const prefixList : string [ ] = Array . isArray ( prefix ) ? prefix : [ prefix ] ;
35
37
36
38
return value
37
39
. split ( split )
38
- . map ( ( str = '' ) => {
39
- let hitPrefix = null ;
40
+ . map ( ( str = '' ) : MentionsEntity | null => {
41
+ let hitPrefix : string | null = null ;
40
42
41
43
prefixList . some ( prefixStr => {
42
44
const startStr = str . slice ( 0 , prefixStr . length ) ;
@@ -50,13 +52,13 @@ function getMentions(value = '', config: MentionsConfig) {
50
52
if ( hitPrefix !== null ) {
51
53
return {
52
54
prefix : hitPrefix ,
53
- value : str . slice ( hitPrefix . length ) ,
55
+ value : str . slice ( ( hitPrefix as string ) . length ) ,
54
56
} ;
55
57
}
56
58
return null ;
57
59
} )
58
- . filter ( entity => ! ! entity && ! ! entity . value ) ;
59
- }
60
+ . filter ( ( entity ) : entity is MentionsEntity => ! ! entity && ! ! entity . value ) ;
61
+ } ;
60
62
61
63
const mentionsProps = {
62
64
...baseMentionsProps ,
@@ -73,145 +75,144 @@ const mentionsProps = {
73
75
onChange : {
74
76
type : Function as PropType < ( text : string ) => void > ,
75
77
} ,
78
+ notFoundContent : PropTypes . any ,
79
+ defaultValue : String ,
76
80
} ;
77
81
78
82
export type MentionsProps = Partial < ExtractPropTypes < typeof mentionsProps > > ;
79
83
80
84
const Mentions = defineComponent ( {
81
85
name : 'AMentions' ,
82
- mixins : [ BaseMixin ] ,
83
86
inheritAttrs : false ,
84
- Option : { ...Option , name : 'AMentionsOption' } ,
85
- getMentions,
86
87
props : mentionsProps ,
87
- emits : [ 'update:value' , 'change' , 'focus' , 'blur' , 'select' ] ,
88
- setup ( ) {
89
- return {
90
- configProvider : inject ( 'configProvider' , defaultConfigProvider ) ,
88
+ getMentions,
89
+ Option,
90
+ emits : [ 'update:value' , 'change' , 'focus' , 'blur' , 'select' , 'pressenter' ] ,
91
+ slots : [ 'notFoundContent' , 'option' ] ,
92
+ setup ( props , { slots, emit, attrs, expose } ) {
93
+ const { prefixCls, renderEmpty, direction } = useConfigInject ( 'mentions' , props ) ;
94
+ const focused = ref ( false ) ;
95
+ const vcMentions = ref ( null ) ;
96
+ const value = ref ( props . value ?? props . defaultValue ?? '' ) ;
97
+ watch (
98
+ ( ) => props . value ,
99
+ val => {
100
+ value . value = val ;
101
+ } ,
102
+ ) ;
103
+ const handleFocus = ( e : FocusEvent ) => {
104
+ focused . value = true ;
105
+ emit ( 'focus' , e ) ;
91
106
} ;
92
- } ,
93
- data ( ) {
94
- return {
95
- focused : false ,
107
+
108
+ const handleBlur = ( e : FocusEvent ) => {
109
+ focused . value = false ;
110
+ emit ( 'blur' , e ) ;
96
111
} ;
97
- } ,
98
- mounted ( ) {
99
- nextTick ( ( ) => {
100
- if ( process . env . NODE_ENV === 'test' ) {
101
- if ( this . autofocus ) {
102
- this . focus ( ) ;
103
- }
112
+
113
+ const handleSelect = ( ...args : [ MentionsOptionProps , string ] ) => {
114
+ emit ( 'select' , ...args ) ;
115
+ focused . value = true ;
116
+ } ;
117
+
118
+ const handleChange = ( val : string ) => {
119
+ if ( props . value === undefined ) {
120
+ value . value = val ;
104
121
}
105
- } ) ;
106
- } ,
107
- methods : {
108
- handleFocus ( e : FocusEvent ) {
109
- this . $emit ( 'focus' , e ) ;
110
- this . setState ( {
111
- focused : true ,
112
- } ) ;
113
- } ,
114
- handleBlur ( e : FocusEvent ) {
115
- this . $emit ( 'blur' , e ) ;
116
- this . setState ( {
117
- focused : false ,
118
- } ) ;
119
- } ,
120
- handleSelect ( ...args : [ MentionsOptionProps , string ] ) {
121
- this . $emit ( 'select' , ...args ) ;
122
- this . setState ( {
123
- focused : true ,
124
- } ) ;
125
- } ,
126
- handleChange ( val : string ) {
127
- this . $emit ( 'update:value' , val ) ;
128
- this . $emit ( 'change' , val ) ;
129
- } ,
130
- getNotFoundContent ( renderEmpty : RenderEmptyHandler ) {
131
- const notFoundContent = getComponent ( this , 'notFoundContent' ) ;
122
+ emit ( 'update:value' , val ) ;
123
+ emit ( 'change' , val ) ;
124
+ } ;
125
+
126
+ const getNotFoundContent = ( ) => {
127
+ const notFoundContent = props . notFoundContent ;
132
128
if ( notFoundContent !== undefined ) {
133
129
return notFoundContent ;
134
130
}
135
-
136
- return renderEmpty ( 'Select' ) ;
137
- } ,
138
- getOptions ( ) {
139
- const { loading } = this . $props ;
140
- const children = getSlot ( this ) ;
141
-
142
- if ( loading ) {
143
- return (
144
- < Option value = "ANTD_SEARCHING" disabled >
145
- < Spin size = "small" />
146
- </ Option >
147
- ) ;
148
- }
149
- return children ;
150
- } ,
151
- getFilterOption ( ) {
152
- const { filterOption, loading } = this . $props ;
153
- if ( loading ) {
154
- return loadingFilterOption ;
131
+ if ( slots . notFoundContent ) {
132
+ return slots . notFoundContent ( ) ;
155
133
}
156
- return filterOption ;
157
- } ,
158
- focus ( ) {
159
- ( this . $refs . vcMentions as HTMLTextAreaElement ) . focus ( ) ;
160
- } ,
161
- blur ( ) {
162
- ( this . $refs . vcMentions as HTMLTextAreaElement ) . blur ( ) ;
163
- } ,
164
- } ,
165
- render ( ) {
166
- const { focused } = this . $data ;
167
- const { getPrefixCls, renderEmpty } = this . configProvider ;
168
- const {
169
- prefixCls : customizePrefixCls ,
170
- disabled,
171
- getPopupContainer,
172
- ...restProps
173
- } = getOptionProps ( this ) as any ;
174
- const { class : className , ...otherAttrs } = this . $attrs ;
175
- const prefixCls = getPrefixCls ( 'mentions' , customizePrefixCls ) ;
176
- const otherProps = omit ( restProps , [ 'loading' , 'onUpdate:value' ] ) ;
177
-
178
- const mergedClassName = classNames ( className , {
179
- [ `${ prefixCls } -disabled` ] : disabled ,
180
- [ `${ prefixCls } -focused` ] : focused ,
181
- } ) ;
134
+ return renderEmpty . value ( 'Select' ) ;
135
+ } ;
182
136
183
- const mentionsProps = {
184
- prefixCls,
185
- notFoundContent : this . getNotFoundContent ( renderEmpty ) ,
186
- ...otherProps ,
187
- disabled,
188
- filterOption : this . getFilterOption ( ) ,
189
- getPopupContainer,
190
- children : this . getOptions ( ) ,
191
- class : mergedClassName ,
192
- rows : 1 ,
193
- ...otherAttrs ,
194
- onChange : this . handleChange ,
195
- onSelect : this . handleSelect ,
196
- onFocus : this . handleFocus ,
197
- onBlur : this . handleBlur ,
198
- ref : 'vcMentions' ,
137
+ const getOptions = ( ) => {
138
+ return flattenChildren ( slots . default ?.( ) || [ ] ) . map ( item => {
139
+ return { ...getOptionProps ( item ) , label : ( item . children as any ) ?. default ?.( ) } ;
140
+ } ) ;
199
141
} ;
200
142
201
- return < VcMentions { ...mentionsProps } /> ;
143
+ const focus = ( ) => {
144
+ ( vcMentions . value as HTMLTextAreaElement ) . focus ( ) ;
145
+ } ;
146
+
147
+ const blur = ( ) => {
148
+ ( vcMentions . value as HTMLTextAreaElement ) . blur ( ) ;
149
+ } ;
150
+
151
+ expose ( { focus, blur } ) ;
152
+
153
+ onMounted ( ( ) => {
154
+ nextTick ( ( ) => {
155
+ if ( process . env . NODE_ENV === 'test' ) {
156
+ if ( props . autofocus ) {
157
+ focus ( ) ;
158
+ }
159
+ }
160
+ } ) ;
161
+ } ) ;
162
+
163
+ return ( ) => {
164
+ const { disabled, getPopupContainer, rows = 1 , ...restProps } = props ;
165
+ const { class : className , ...otherAttrs } = attrs ;
166
+ const otherProps = omit ( restProps , [ 'defaultValue' , 'onUpdate:value' , 'prefixCls' ] ) ;
167
+
168
+ const mergedClassName = classNames ( className , {
169
+ [ `${ prefixCls . value } -disabled` ] : disabled ,
170
+ [ `${ prefixCls . value } -focused` ] : focused . value ,
171
+ [ `${ prefixCls . value } -rtl` ] : direction . value === 'rtl' ,
172
+ } ) ;
173
+
174
+ const mentionsProps = {
175
+ prefixCls : prefixCls . value ,
176
+ ...otherProps ,
177
+ disabled,
178
+ direction : direction . value ,
179
+ filterOption : props . filterOption ,
180
+ getPopupContainer,
181
+ options : props . options || getOptions ( ) ,
182
+ class : mergedClassName ,
183
+ ...otherAttrs ,
184
+ rows,
185
+ onChange : handleChange ,
186
+ onSelect : handleSelect ,
187
+ onFocus : handleFocus ,
188
+ onBlur : handleBlur ,
189
+ ref : vcMentions ,
190
+ value : value . value ,
191
+ } ;
192
+ return (
193
+ < VcMentions
194
+ { ...mentionsProps }
195
+ v-slots = { { notFoundContent : getNotFoundContent , option : slots . option } }
196
+ > </ VcMentions >
197
+ ) ;
198
+ } ;
202
199
} ,
203
200
} ) ;
204
201
202
+ export const MentionsOption = {
203
+ ...Option ,
204
+ name : 'AMentionsOption' ,
205
+ } ;
206
+
205
207
/* istanbul ignore next */
206
208
Mentions . install = function ( app : App ) {
207
209
app . component ( Mentions . name , Mentions ) ;
208
- app . component ( Mentions . Option . name , Mentions . Option ) ;
210
+ app . component ( MentionsOption . name , MentionsOption ) ;
209
211
return app ;
210
212
} ;
211
213
212
- export const MentionsOption = Mentions . Option ;
213
-
214
214
export default Mentions as typeof Mentions &
215
215
Plugin & {
216
+ getMentions : typeof getMentions ;
216
217
readonly Option : typeof Option ;
217
218
} ;
0 commit comments