@@ -2,15 +2,16 @@ import { Controller } from '@hotwired/stimulus'
22import TomSelect from 'tom-select'
33
44export default class extends Controller {
5- static targets = [ 'select' , 'option' , 'item' ]
5+ static targets = [ 'select' , 'option' , 'item' , 'hiddenItem' , 'selectAllOption' ]
66 static values = {
77 options : Array ,
88 selectedItems : Array ,
99 withOptions : Boolean ,
1010 placeholderTerm : {
1111 type : String ,
1212 default : 'contact(s)'
13- }
13+ } ,
14+ showAllOption : Boolean
1415 }
1516
1617 connect ( ) {
@@ -37,11 +38,44 @@ export default class extends Controller {
3738 const itemTemplate = this . itemTarget . innerHTML
3839 const placeholder = `Select or search ${ this . placeholderTermValue } `
3940
41+ const showAllOptionCheck = this . showAllOptionValue
42+ const hiddenItemTemplate = showAllOptionCheck && this . hiddenItemTarget && this . hiddenItemTarget . innerHTML
43+ const showAllOptionTemplate = showAllOptionCheck && this . selectAllOptionTarget && this . selectAllOptionTarget . innerHTML
44+
45+ // orderedOptionVals is of type (" " | number)[] - the " " could appear
46+ // because using it as the value for the select/unselect all option
47+ let orderedOptionVals = this . optionsValue . map ( opt => opt . value )
48+ if ( showAllOptionCheck ) {
49+ // using " " as value instead of "" bc tom-select doesn't init the "" in the item list
50+ orderedOptionVals = [ ' ' ] . concat ( orderedOptionVals )
51+ }
52+
53+ const hasInitialItems = Array . isArray ( this . selectedItemsValue ) && this . selectedItemsValue . length
54+ // initItems: number[], possibly empty
55+ let initItems = this . selectedItemsValue
56+ if ( showAllOptionCheck ) {
57+ const emptyItem = [ ' ' ]
58+ initItems = hasInitialItems ? emptyItem . concat ( this . selectedItemsValue ) : orderedOptionVals
59+ }
60+
61+ const dropdownOptions = showAllOptionCheck
62+ ? [ { text : 'Select/Unselect all' , subtext : '' , value : ' ' , group : '' } ] . concat ( this . optionsValue )
63+ : this . optionsValue
64+
4065 /* eslint-disable no-new */
4166 new TomSelect ( this . selectTarget , {
42- onItemAdd : function ( ) {
67+ onItemRemove : function ( value ) {
68+ if ( value === ' ' ) {
69+ this . clear ( )
70+ }
71+ } ,
72+ onItemAdd : function ( value ) {
4373 this . setTextboxValue ( '' )
4474 this . refreshOptions ( )
75+
76+ if ( value === ' ' ) {
77+ this . addItems ( orderedOptionVals )
78+ }
4579 } ,
4680 plugins : {
4781 remove_button : {
@@ -54,19 +88,25 @@ export default class extends Controller {
5488 uncheckedClassNames : [ 'form-check-input' ]
5589 }
5690 } ,
57- options : this . optionsValue ,
58- items : this . selectedItemsValue ,
91+ options : dropdownOptions ,
92+ items : initItems ,
5993 placeholder,
6094 hidePlaceholder : true ,
6195 searchField : [ 'text' , 'group' ] ,
6296 render : {
6397 option : function ( data , escape ) {
64- let html = optionTemplate . replace ( / D A T A _ L A B E L / g, escape ( data . text ) )
65- html = html . replace ( / D A T A _ S U B _ T E X T / g, escape ( data . subtext ) )
98+ let html
99+
100+ if ( showAllOptionCheck && data && data . value === ' ' ) {
101+ html = showAllOptionTemplate . replace ( / D A T A _ L A B E L / g, escape ( data . text ) )
102+ } else {
103+ html = optionTemplate . replace ( / D A T A _ L A B E L / g, escape ( data . text ) )
104+ html = html . replace ( / D A T A _ S U B _ T E X T / g, escape ( data . subtext ) )
105+ }
66106 return html
67107 } ,
68108 item : function ( data , escape ) {
69- return itemTemplate . replace ( / D A T A _ L A B E L / g, escape ( data . text ) )
109+ return showAllOptionCheck && data . value === ' ' ? hiddenItemTemplate : itemTemplate . replace ( / D A T A _ L A B E L / g, escape ( data . text ) )
70110 }
71111 }
72112 } )
0 commit comments