@@ -35,7 +35,6 @@ const SELECTOR_OPTION = '.form-multi-select-option'
3535const SELECTOR_OPTIONS = '.form-multi-select-options'
3636const SELECTOR_OPTIONS_EMPTY = '.form-multi-select-options-empty'
3737const SELECTOR_SELECT = '.form-multi-select'
38- const SELECTOR_SELECTED = '.form-multi-selected'
3938const SELECTOR_SELECTION = '.form-multi-select-selection'
4039const SELECTOR_SELECTION_CLEANER = '.form-multi-select-selection-cleaner'
4140
@@ -53,8 +52,10 @@ const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`
5352const EVENT_LOAD_DATA_API = `load${ EVENT_KEY } ${ DATA_API_KEY } `
5453
5554const CLASS_NAME_SELECT = 'form-multi-select'
55+ const CLASS_NAME_SELECT_DROPDOWN = 'form-multi-select-dropdown'
5656const CLASS_NAME_SELECT_MULTIPLE = 'form-multi-select-multiple'
5757const CLASS_NAME_SELECT_WITH_CLEANER = 'form-multi-select-with-cleaner'
58+ const CLASS_NAME_SELECT_ALL = 'form-multi-select-all'
5859const CLASS_NAME_OPTGROUP = 'form-multi-select-optgroup'
5960const CLASS_NAME_OPTGROUP_LABEL = 'form-multi-select-optgroup-label'
6061const CLASS_NAME_OPTION = 'form-multi-select-option'
@@ -73,14 +74,16 @@ const CLASS_NAME_TAG_DELETE = 'form-multi-select-tag-delete'
7374const CLASS_NAME_LABEL = 'label'
7475
7576const Default = {
76- cleaner : false ,
77+ cleaner : true ,
7778 multiple : true ,
7879 placeholder : 'Select...' ,
7980 options : false ,
8081 optionsMaxHeight : 'auto' ,
81- optionsStyle : 'default ' ,
82+ optionsStyle : 'checkbox ' ,
8283 search : false ,
8384 searchNoResultsLabel : 'No results found' ,
85+ selectAll : true ,
86+ selectAllLabel : 'Select all options' ,
8487 selectionType : 'tag' ,
8588 selectionTypeCounterText : 'item(s) selected'
8689}
@@ -94,6 +97,8 @@ const DefaultType = {
9497 optionsStyle : 'string' ,
9598 search : 'boolean' ,
9699 searchNoResultsLabel : 'string' ,
100+ selectAll : 'boolean' ,
101+ selectAllLabel : 'string' ,
97102 selectionType : 'string' ,
98103 selectionTypeCounterText : 'string'
99104}
@@ -108,6 +113,7 @@ class MultiSelect extends BaseComponent {
108113 constructor ( element , config ) {
109114 super ( element )
110115
116+ this . _selectAllElement = null
111117 this . _selectionElement = null
112118 this . _selectionCleanerElement = null
113119 this . _searchElement = null
@@ -181,6 +187,28 @@ class MultiSelect extends BaseComponent {
181187 this . _addEventListeners ( )
182188 }
183189
190+ selectAll ( options = this . _options ) {
191+ options . forEach ( option => {
192+ if ( option . label ) {
193+ this . selectAll ( option . options )
194+ return
195+ }
196+
197+ this . _selectOption ( option . value , option . text )
198+ } )
199+ }
200+
201+ deselectAll ( selection = this . _selection ) {
202+ selection . forEach ( option => {
203+ if ( option . label ) {
204+ this . deselectAll ( option . options )
205+ return
206+ }
207+
208+ this . _deselectOption ( option . value )
209+ } )
210+ }
211+
184212 getValue ( ) {
185213 return this . _selection
186214 }
@@ -200,10 +228,16 @@ class MultiSelect extends BaseComponent {
200228 const key = event . keyCode || event . charCode
201229
202230 if ( ( key === 8 || key === 46 ) && event . target . value . length === 0 ) {
203- this . _selectionDeleteLast ( )
231+ this . _deselectLastOption ( )
204232 }
205233 } )
206234
235+ EventHandler . on ( this . _selectAllElement , EVENT_CLICK , event => {
236+ event . preventDefault ( )
237+ event . stopPropagation ( )
238+ this . selectAll ( )
239+ } )
240+
207241 EventHandler . on ( this . _optionsElement , EVENT_CLICK , event => {
208242 event . preventDefault ( )
209243 event . stopPropagation ( )
@@ -213,11 +247,7 @@ class MultiSelect extends BaseComponent {
213247 EventHandler . on ( this . _selectionCleanerElement , EVENT_CLICK , event => {
214248 event . preventDefault ( )
215249 event . stopPropagation ( )
216- this . _selectionClear ( )
217- this . _updateSelection ( )
218- this . _updateSelectionCleaner ( )
219- this . _updateSearch ( )
220- this . _updateSearchSize ( )
250+ this . deselectAll ( )
221251 } )
222252
223253 EventHandler . on ( this . _optionsElement , EVENT_KEYDOWN , event => {
@@ -382,7 +412,7 @@ class MultiSelect extends BaseComponent {
382412 }
383413
384414 _createSelectionCleaner ( ) {
385- if ( this . _config . cleaner ) {
415+ if ( this . _config . cleaner && this . _config . multiple ) {
386416 const cleaner = document . createElement ( 'button' )
387417 cleaner . classList . add ( CLASS_NAME_SELECTION_CLEANER )
388418 this . _clone . append ( cleaner )
@@ -404,18 +434,33 @@ class MultiSelect extends BaseComponent {
404434 }
405435
406436 _createOptionsContainer ( ) {
407- const div = document . createElement ( 'div' )
408- div . classList . add ( CLASS_NAME_OPTIONS )
437+ const dropdownDiv = document . createElement ( 'div' )
438+ dropdownDiv . classList . add ( CLASS_NAME_SELECT_DROPDOWN )
439+
440+ if ( this . _config . selectAll && this . _config . multiple ) {
441+ const selectAll = document . createElement ( 'button' )
442+ selectAll . classList . add ( CLASS_NAME_SELECT_ALL )
443+ selectAll . innerHTML = this . _config . selectAllLabel
444+
445+ this . _selectAllElement = selectAll
446+
447+ dropdownDiv . append ( selectAll )
448+ }
449+
450+ const optionsDiv = document . createElement ( 'div' )
451+ optionsDiv . classList . add ( CLASS_NAME_OPTIONS )
409452
410453 if ( this . _config . optionsMaxHeight !== 'auto' ) {
411- div . style . maxHeight = `${ this . _config . optionsMaxHeight } px`
412- div . style . overflow = 'scroll'
454+ optionsDiv . style . maxHeight = `${ this . _config . optionsMaxHeight } px`
455+ optionsDiv . style . overflow = 'scroll'
413456 }
414457
415- this . _clone . append ( div )
458+ dropdownDiv . append ( optionsDiv )
459+
460+ this . _clone . append ( dropdownDiv )
416461
417- this . _createOptions ( div , this . _options )
418- this . _optionsElement = div
462+ this . _createOptions ( optionsDiv , this . _options )
463+ this . _optionsElement = optionsDiv
419464 }
420465
421466 _createOptions ( parentElement , options ) {
@@ -466,7 +511,7 @@ class MultiSelect extends BaseComponent {
466511 event . stopPropagation ( )
467512
468513 tag . remove ( )
469- this . _selectionDelete ( value )
514+ this . _deselectOption ( value )
470515 this . _updateOptionsList ( )
471516 this . _updateSearch ( )
472517 } )
@@ -483,22 +528,17 @@ class MultiSelect extends BaseComponent {
483528 const text = element . textContent
484529
485530 if ( this . _config . multiple && element . classList . contains ( CLASS_NAME_SELECTED ) ) {
486- this . _selectionDelete ( value )
531+ this . _deselectOption ( value )
487532 } else if ( this . _config . multiple && ! element . classList . contains ( CLASS_NAME_SELECTED ) ) {
488- this . _selectionAdd ( value , text )
533+ this . _selectOption ( value , text )
489534 } else if ( ! this . _config . multiple ) {
490- this . _selectionAdd ( value , text )
535+ this . _selectOption ( value , text )
491536 }
492-
493- this . _updateSelection ( )
494- this . _updateSelectionCleaner ( )
495- this . _updateSearch ( )
496- this . _updateSearchSize ( )
497537 }
498538
499- _selectionAdd ( value , text ) {
539+ _selectOption ( value , text ) {
500540 if ( ! this . _config . multiple ) {
501- this . _selectionClear ( )
541+ this . deselectAll ( )
502542 }
503543
504544 if ( this . _selection . filter ( e => e . value === value ) . length === 0 ) {
@@ -508,27 +548,48 @@ class MultiSelect extends BaseComponent {
508548 } )
509549 }
510550
511- this . _selectOption ( value )
512- }
551+ SelectorEngine . findOne ( `option[value="${ value } "]` , this . _element ) . selected = true
513552
514- _selectionClear ( ) {
515- this . _selection . length = 0
516- this . _clearOptions ( )
553+ const option = SelectorEngine . findOne ( `[data-value="${ value } "]` , this . _optionsElement )
554+ if ( option ) {
555+ option . classList . add ( CLASS_NAME_SELECTED )
556+ }
557+
558+ EventHandler . trigger ( this . _element , EVENT_CHANGED , {
559+ value : this . _selection
560+ } )
561+
562+ this . _updateSelection ( )
563+ this . _updateSelectionCleaner ( )
564+ this . _updateSearch ( )
565+ this . _updateSearchSize ( )
517566 }
518567
519- _selectionDelete ( value ) {
568+ _deselectOption ( value ) {
520569 const selected = this . _selection . filter ( e => e . value !== value )
521570 this . _selection = selected
522- this . _unSelectOption ( value )
571+
572+ SelectorEngine . findOne ( `option[value="${ value } "]` , this . _element ) . selected = false
573+
574+ const option = SelectorEngine . findOne ( `[data-value="${ value } "]` , this . _optionsElement )
575+ if ( option ) {
576+ option . classList . remove ( CLASS_NAME_SELECTED )
577+ }
578+
579+ EventHandler . trigger ( this . _element , EVENT_CHANGED , {
580+ value : this . _selection
581+ } )
582+
583+ this . _updateSelection ( )
584+ this . _updateSelectionCleaner ( )
585+ this . _updateSearch ( )
586+ this . _updateSearchSize ( )
523587 }
524588
525- _selectionDeleteLast ( ) {
589+ _deselectLastOption ( ) {
526590 if ( this . _selection . length > 0 ) {
527591 const last = this . _selection . pop ( )
528- this . _selectionDelete ( last . value )
529- this . _updateSelection ( )
530- this . _updateSelectionCleaner ( )
531- this . _updateSearch ( )
592+ this . _deselectOption ( last . value )
532593 }
533594 }
534595
@@ -559,7 +620,7 @@ class MultiSelect extends BaseComponent {
559620 }
560621
561622 _updateSelectionCleaner ( ) {
562- if ( this . _config . cleaner === false || this . _selectionCleanerElement === null ) {
623+ if ( ! this . _config . cleaner || this . _selectionCleanerElement === null ) {
563624 return
564625 }
565626
@@ -617,41 +678,6 @@ class MultiSelect extends BaseComponent {
617678 }
618679 }
619680
620- _selectOption ( value ) {
621- SelectorEngine . findOne ( `option[value="${ value } "]` , this . _element ) . selected = true
622-
623- const option = SelectorEngine . findOne ( `[data-value="${ value } "]` , this . _optionsElement )
624- if ( option ) {
625- option . classList . add ( CLASS_NAME_SELECTED )
626- }
627-
628- EventHandler . trigger ( this . _element , EVENT_CHANGED , {
629- value : this . _selection
630- } )
631- }
632-
633- _unSelectOption ( value ) {
634- SelectorEngine . findOne ( `option[value="${ value } "]` , this . _element ) . selected = false
635-
636- const option = SelectorEngine . findOne ( `[data-value="${ value } "]` , this . _optionsElement )
637- if ( option ) {
638- option . classList . remove ( CLASS_NAME_SELECTED )
639- }
640-
641- EventHandler . trigger ( this . _element , EVENT_CHANGED , {
642- value : this . _selection
643- } )
644- }
645-
646- _clearOptions ( ) {
647- this . _element . value = null
648- SelectorEngine . find ( SELECTOR_SELECTED , this . _clone ) . forEach ( element => {
649- element . classList . remove ( CLASS_NAME_SELECTED )
650- } )
651- }
652-
653- // eslint-disable-next-line no-warning-comments
654- // TODO: poprawić tą nazwę
655681 _onSearchChange ( element ) {
656682 if ( element ) {
657683 this . search ( element . value )
@@ -674,10 +700,6 @@ class MultiSelect extends BaseComponent {
674700 } )
675701 }
676702
677- _isHidden ( element ) {
678- return element . offsetParent === null
679- }
680-
681703 _isVisible ( element ) {
682704 const style = window . getComputedStyle ( element )
683705 return ( style . display !== 'none' )
0 commit comments