1
- // Copied from Vuex’s util.js
2
- function deepClone ( obj ) {
3
- if ( Array . isArray ( obj ) ) {
4
- return obj . map ( deepClone )
5
- } else if ( obj && typeof obj === 'object' ) {
6
- var cloned = { }
7
- var keys = Object . keys ( obj )
8
- for ( var i = 0 , l = keys . length ; i < l ; i ++ ) {
9
- var key = keys [ i ]
10
- cloned [ key ] = deepClone ( obj [ key ] )
11
- }
12
- return cloned
13
- } else {
14
- return obj
15
- }
16
- }
1
+ import { deepClone } from './utils'
17
2
18
3
module . exports = {
19
4
data ( ) {
20
5
return {
21
6
search : '' ,
22
7
isOpen : false ,
23
- value : [ ] ,
24
- loading : false
8
+ value : [ ]
25
9
}
26
10
} ,
27
11
props : {
@@ -126,29 +110,6 @@ module.exports = {
126
110
type : Boolean ,
127
111
default : true
128
112
} ,
129
- /**
130
- * Callback function to call after this.value changes
131
- * @callback onChange
132
- * @default false
133
- * @param {Array||Object||String||Integer } Current this.value
134
- * @param {Integer } $index of current selection
135
- * @type {Function }
136
- */
137
- onChange : {
138
- type : Function ,
139
- default : false
140
- } ,
141
- /**
142
- * Callback function to call after this.search changes
143
- * @callback onSearchChange
144
- * @default false
145
- * @param {String } Pass current search String
146
- * @type {Function }
147
- */
148
- onSearchChange : {
149
- type : Function ,
150
- default : false
151
- } ,
152
113
/**
153
114
* Value that indicates if the dropdown has been used.
154
115
* Useful for validation.
@@ -197,19 +158,6 @@ module.exports = {
197
158
type : Boolean ,
198
159
default : false
199
160
} ,
200
- /**
201
- * Callback function to run when attemting to add a tag
202
- * @default suitable for primitive values
203
- * @param {String } Tag string to build a tag
204
- * @type {Function }
205
- */
206
- onTag : {
207
- type : Function ,
208
- default : function ( tag ) {
209
- this . options . push ( tag )
210
- this . value . push ( tag )
211
- }
212
- } ,
213
161
/**
214
162
* String to show when highlighting a potential tag
215
163
* @default 'Press enter to create a tag'
@@ -228,9 +176,14 @@ module.exports = {
228
176
type : Number ,
229
177
default : false
230
178
} ,
231
- async : {
232
- type : Boolean ,
233
- default : false
179
+ /**
180
+ * Will be passed with all events as second param.
181
+ * Useful for identifying events origin.
182
+ * @default null
183
+ * @type {String|Integer }
184
+ */
185
+ id : {
186
+ default : null
234
187
}
235
188
} ,
236
189
created ( ) {
@@ -239,9 +192,7 @@ module.exports = {
239
192
} else {
240
193
this . value = deepClone ( this . selected )
241
194
}
242
- if ( this . searchable && ! this . multiple ) {
243
- this . search = this . getOptionLabel ( this . value )
244
- }
195
+ if ( this . searchable ) this . adjustSearch ( )
245
196
} ,
246
197
computed : {
247
198
filteredOptions ( ) {
@@ -272,33 +223,18 @@ module.exports = {
272
223
} ,
273
224
watch : {
274
225
'value' ( ) {
275
- if ( this . onChange && JSON . stringify ( this . value ) !== JSON . stringify ( this . selected ) ) {
276
- this . onChange ( deepClone ( this . value ) )
277
- } else {
278
- this . $set ( 'selected' , deepClone ( this . value ) )
279
- }
280
226
if ( this . resetAfter ) {
281
227
this . $set ( 'value' , null )
282
228
this . $set ( 'search' , null )
283
229
this . $set ( 'selected' , null )
284
230
}
285
- if ( ! this . multiple && this . searchable && this . clearOnSelect ) {
286
- this . search = this . getOptionLabel ( this . value )
287
- }
231
+ this . adjustSearch ( )
288
232
} ,
289
233
'search' ( ) {
290
- this . $emit ( 'on-search-change' , this . search )
291
- this . loading = true
292
- if ( this . async ) {
293
- }
294
- } ,
295
- 'options' ( ) {
296
- this . onSearchChange && ( this . loading = false )
234
+ this . $emit ( 'search-change' , this . search , this . id )
297
235
} ,
298
236
'selected' ( newVal , oldVal ) {
299
- if ( JSON . stringify ( newVal ) !== JSON . stringify ( oldVal ) ) {
300
- this . value = deepClone ( this . selected )
301
- }
237
+ this . value = deepClone ( this . selected )
302
238
}
303
239
} ,
304
240
methods : {
@@ -374,28 +310,31 @@ module.exports = {
374
310
select ( option ) {
375
311
if ( this . max && this . multiple && this . value . length === this . max ) return
376
312
if ( option . isTag ) {
377
- this . onTag ( option . label )
313
+ this . $emit ( 'tag' , option . label , this . id )
378
314
this . search = ''
379
315
} else {
380
316
if ( this . multiple ) {
381
317
if ( ! this . isNotSelected ( option ) ) {
382
318
this . removeElement ( option )
383
319
} else {
384
320
this . value . push ( option )
385
- if ( this . clearOnSelect ) { this . search = '' }
321
+
322
+ this . $emit ( 'select' , deepClone ( option ) , this . id )
323
+ this . $emit ( 'update' , deepClone ( this . value ) , this . id )
386
324
}
387
325
} else {
388
- this . $set ( 'value' ,
389
- ! this . isNotSelected ( option ) && this . allowEmpty
390
- ? null
391
- : option
392
- )
393
- if ( this . closeOnSelect ) {
394
- this . searchable
395
- ? this . $els . search . blur ( )
396
- : this . $el . blur ( )
397
- }
326
+ const isSelected = this . isSelected ( option )
327
+
328
+ /* istanbul ignore else */
329
+ if ( isSelected && ! this . allowEmpty ) return
330
+
331
+ this . value = isSelected ? null : option
332
+
333
+ this . $emit ( 'select' , deepClone ( option ) , this . id )
334
+ this . $emit ( 'update' , deepClone ( this . value ) , this . id )
398
335
}
336
+
337
+ if ( this . closeOnSelect ) this . deactivate ( )
399
338
}
400
339
} ,
401
340
/**
@@ -408,14 +347,16 @@ module.exports = {
408
347
*/
409
348
removeElement ( option ) {
410
349
/* istanbul ignore else */
411
- if ( this . allowEmpty || this . value . length > 1 ) {
412
- if ( this . multiple && typeof option === 'object' ) {
413
- const index = this . valueKeys . indexOf ( option [ this . key ] )
414
- this . value . splice ( index , 1 )
415
- } else {
416
- this . value . $remove ( option )
417
- }
350
+ if ( ! this . allowEmpty && this . value . length <= 1 ) return
351
+
352
+ if ( this . multiple && typeof option === 'object' ) {
353
+ const index = this . valueKeys . indexOf ( option [ this . key ] )
354
+ this . value . splice ( index , 1 )
355
+ } else {
356
+ this . value . $remove ( option )
418
357
}
358
+ this . $emit ( 'remove' , deepClone ( option ) , this . id )
359
+ this . $emit ( 'update' , deepClone ( this . value ) , this . id )
419
360
} ,
420
361
/**
421
362
* Calls this.removeElement() with the last element
@@ -452,20 +393,29 @@ module.exports = {
452
393
*/
453
394
deactivate ( ) {
454
395
/* istanbul ignore else */
455
- if ( this . isOpen ) {
456
- this . isOpen = false
457
- this . touched = true
458
- /* istanbul ignore else */
459
- if ( this . searchable ) {
460
- this . $els . search . blur ( )
461
- this . search = this . multiple
462
- ? ''
463
- : this . getOptionLabel ( this . value )
464
- } else {
465
- this . $el . blur ( )
466
- }
396
+ if ( ! this . isOpen ) return
397
+
398
+ this . isOpen = false
399
+ this . touched = true
400
+ /* istanbul ignore else */
401
+ if ( this . searchable ) {
402
+ this . $els . search . blur ( )
403
+ this . adjustSearch ( )
404
+ } else {
405
+ this . $el . blur ( )
467
406
}
468
407
} ,
408
+ /**
409
+ * Adjusts the Search property to equal the correct value
410
+ * depending on the selected value.
411
+ */
412
+ adjustSearch ( ) {
413
+ if ( ! this . searchable || ! this . clearOnSelect ) return
414
+
415
+ this . search = this . multiple
416
+ ? ''
417
+ : this . getOptionLabel ( this . value )
418
+ } ,
469
419
/**
470
420
* Call this.activate() or this.deactivate()
471
421
* depending on this.isOpen value.
0 commit comments