@@ -101,7 +101,8 @@ const Default = {
101
101
selectAllLabel : 'Select all options' ,
102
102
selectionType : 'tags' ,
103
103
selectionTypeCounterText : 'item(s) selected' ,
104
- valid : false
104
+ valid : false ,
105
+ value : null
105
106
}
106
107
107
108
const DefaultType = {
@@ -123,7 +124,8 @@ const DefaultType = {
123
124
selectAllLabel : 'string' ,
124
125
selectionType : 'string' ,
125
126
selectionTypeCounterText : 'string' ,
126
- valid : 'boolean'
127
+ valid : 'boolean' ,
128
+ value : '(string|array|null)'
127
129
}
128
130
129
131
/**
@@ -136,6 +138,7 @@ class MultiSelect extends BaseComponent {
136
138
constructor ( element , config ) {
137
139
super ( element , config )
138
140
141
+ this . _configureNativeSelect ( )
139
142
this . _indicatorElement = null
140
143
this . _selectAllElement = null
141
144
this . _selectionElement = null
@@ -146,13 +149,13 @@ class MultiSelect extends BaseComponent {
146
149
147
150
this . _clone = null
148
151
this . _menu = null
152
+ this . _selected = [ ]
149
153
this . _options = this . _getOptions ( )
150
154
this . _popper = null
151
155
this . _search = ''
152
- this . _selected = this . _getSelectedOptions ( this . _options )
153
156
154
157
if ( this . _config . options . length > 0 ) {
155
- this . _createNativeSelect ( this . _config . options )
158
+ this . _createNativeOptions ( this . _element , this . _config . options )
156
159
}
157
160
158
161
this . _createSelect ( )
@@ -240,8 +243,8 @@ class MultiSelect extends BaseComponent {
240
243
241
244
update ( config ) {
242
245
this . _config = this . _getConfig ( config )
246
+ this . _selected = [ ]
243
247
this . _options = this . _getOptions ( )
244
- this . _selected = this . _getSelectedOptions ( this . _options )
245
248
this . _menu . remove ( )
246
249
this . _clone . remove ( )
247
250
this . _element . innerHTML = ''
@@ -386,61 +389,81 @@ class MultiSelect extends BaseComponent {
386
389
return this . _element . classList . value . split ( ' ' )
387
390
}
388
391
389
- _getOptions ( node = this . _element ) {
392
+ _getOptions ( ) {
390
393
if ( this . _config . options ) {
391
- return this . _config . options
394
+ return this . _getOptionsFromConfig ( )
392
395
}
393
396
397
+ return this . _getOptionsFromElement ( )
398
+ }
399
+
400
+ _getOptionsFromConfig ( options = this . _config . options ) {
401
+ const _options = [ ]
402
+ for ( const option of options ) {
403
+ if ( option . options && Array . isArray ( option . options ) ) {
404
+ _options . push ( {
405
+ label : option . label ,
406
+ options : this . _getOptionsFromConfig ( option . options )
407
+ } )
408
+ continue
409
+ }
410
+
411
+ const value = String ( option . value )
412
+ const isSelected = option . selected || ( this . _config . value && this . _config . value . includes ( value ) )
413
+
414
+ _options . push ( {
415
+ ...option ,
416
+ value,
417
+ ...isSelected && { selected : true } ,
418
+ ...option . disabled && { disabled : true }
419
+ } )
420
+
421
+ if ( isSelected ) {
422
+ this . _selected . push ( {
423
+ value : String ( option . value ) ,
424
+ text : option . text
425
+ } )
426
+ }
427
+ }
428
+
429
+ return _options
430
+ }
431
+
432
+ _getOptionsFromElement ( node = this . _element ) {
394
433
const nodes = Array . from ( node . childNodes ) . filter ( element => element . nodeName === 'OPTION' || element . nodeName === 'OPTGROUP' )
395
434
const options = [ ]
396
435
397
436
for ( const node of nodes ) {
398
437
if ( node . nodeName === 'OPTION' && node . value ) {
438
+ const isSelected = node . selected || ( this . _config . value && this . _config . value . includes ( node . value ) )
399
439
options . push ( {
400
440
value : node . value ,
401
441
text : node . innerHTML ,
402
- selected : node . selected ,
442
+ selected : isSelected ,
403
443
disabled : node . disabled
404
444
} )
445
+
446
+ if ( node . selected || isSelected ) {
447
+ this . _selected . push ( {
448
+ value : node . value ,
449
+ text : node . innerHTML ,
450
+ ...node . disabled && { disabled : true }
451
+ } )
452
+ }
405
453
}
406
454
407
455
if ( node . nodeName === 'OPTGROUP' ) {
408
456
options . push ( {
409
457
label : node . label ,
410
- options : this . _getOptions ( node )
458
+ options : this . _getOptionsFromElement ( node )
411
459
} )
412
460
}
413
461
}
414
462
415
463
return options
416
464
}
417
465
418
- _getSelectedOptions ( options ) {
419
- const selected = [ ]
420
-
421
- for ( const option of options ) {
422
- if ( typeof option . value === 'undefined' ) {
423
- this . _getSelectedOptions ( option . options )
424
- continue
425
- }
426
-
427
- if ( option . selected ) {
428
- // Add only the last option if single select
429
- if ( ! this . _config . multiple ) {
430
- selected . length = 0
431
- }
432
-
433
- selected . push ( {
434
- value : String ( option . value ) ,
435
- text : option . text
436
- } )
437
- }
438
- }
439
-
440
- return selected
441
- }
442
-
443
- _createNativeSelect ( data ) {
466
+ _configureNativeSelect ( ) {
444
467
this . _element . classList . add ( CLASS_NAME_SELECT )
445
468
446
469
if ( this . _config . multiple ) {
@@ -450,8 +473,6 @@ class MultiSelect extends BaseComponent {
450
473
if ( this . _config . required ) {
451
474
this . _element . setAttribute ( 'required' , true )
452
475
}
453
-
454
- this . _createNativeOptions ( this . _element , data )
455
476
}
456
477
457
478
_createNativeOptions ( parentElement , options ) {
@@ -681,13 +702,13 @@ class MultiSelect extends BaseComponent {
681
702
}
682
703
}
683
704
684
- _createTag ( value , text ) {
705
+ _createTag ( value , text , disabled ) {
685
706
const tag = document . createElement ( 'div' )
686
707
tag . classList . add ( CLASS_NAME_TAG )
687
708
tag . dataset . value = value
688
709
tag . innerHTML = text
689
710
690
- if ( ! this . _config . disabled ) {
711
+ if ( ! this . _config . disabled && disabled !== true ) {
691
712
const closeBtn = document . createElement ( 'button' )
692
713
closeBtn . type = 'button'
693
714
closeBtn . classList . add ( CLASS_NAME_TAG_DELETE )
@@ -732,7 +753,7 @@ class MultiSelect extends BaseComponent {
732
753
733
754
_findOptionByValue ( value , options = this . _options ) {
734
755
for ( const option of options ) {
735
- if ( option . value === value ) {
756
+ if ( String ( option . value ) === value ) {
736
757
return option
737
758
}
738
759
@@ -781,8 +802,7 @@ class MultiSelect extends BaseComponent {
781
802
}
782
803
783
804
_deselectOption ( value ) {
784
- const selected = this . _selected . filter ( option => option . value !== String ( value ) )
785
- this . _selected = selected
805
+ this . _selected = this . _selected . filter ( option => option . value !== String ( value ) )
786
806
787
807
SelectorEngine . findOne ( `option[value="${ value } "]` , this . _element ) . selected = false
788
808
@@ -803,8 +823,10 @@ class MultiSelect extends BaseComponent {
803
823
804
824
_deselectLastOption ( ) {
805
825
if ( this . _selected . length > 0 ) {
806
- const last = this . _selected . pop ( )
807
- this . _deselectOption ( last . value )
826
+ const last = this . _selected . findLast ( option => option . disabled !== true )
827
+ if ( last ) {
828
+ this . _deselectOption ( last . value )
829
+ }
808
830
}
809
831
}
810
832
@@ -825,7 +847,7 @@ class MultiSelect extends BaseComponent {
825
847
selection . innerHTML = ''
826
848
827
849
for ( const option of this . _selected ) {
828
- selection . append ( this . _createTag ( option . value , option . text ) )
850
+ selection . append ( this . _createTag ( option . value , option . text , option . disabled ) )
829
851
}
830
852
}
831
853
@@ -1004,6 +1026,14 @@ class MultiSelect extends BaseComponent {
1004
1026
config . container = getElement ( config . container )
1005
1027
}
1006
1028
1029
+ if ( typeof config . value === 'number' ) {
1030
+ config . value = [ String ( config . value ) ]
1031
+ }
1032
+
1033
+ if ( typeof config . value === 'string' ) {
1034
+ config . value = config . value . split ( / , \s * / ) . map ( String )
1035
+ }
1036
+
1007
1037
return config
1008
1038
}
1009
1039
0 commit comments