@@ -29,26 +29,25 @@ class default_1 extends Controller {
29
29
_default_1_instances . add ( this ) ;
30
30
this . isObserving = false ;
31
31
this . hasLoadedChoicesPreviously = false ;
32
+ this . originalOptions = [ ] ;
32
33
}
33
34
initialize ( ) {
34
- if ( this . requiresLiveIgnore ( ) ) {
35
- this . element . setAttribute ( 'data-live-ignore' , '' ) ;
36
- if ( this . element . id ) {
37
- const label = document . querySelector ( `label[for="${ this . element . id } "]` ) ;
38
- if ( label ) {
39
- label . setAttribute ( 'data-live-ignore' , '' ) ;
40
- }
41
- }
42
- }
43
- else {
44
- if ( ! this . mutationObserver ) {
45
- this . mutationObserver = new MutationObserver ( ( mutations ) => {
46
- this . onMutations ( mutations ) ;
47
- } ) ;
48
- }
35
+ if ( ! this . mutationObserver ) {
36
+ this . mutationObserver = new MutationObserver ( ( mutations ) => {
37
+ this . onMutations ( mutations ) ;
38
+ } ) ;
49
39
}
50
40
}
51
41
connect ( ) {
42
+ if ( this . selectElement ) {
43
+ this . originalOptions = this . createOptionsDataStructure ( this . selectElement ) ;
44
+ }
45
+ this . initializeTomSelect ( ) ;
46
+ }
47
+ initializeTomSelect ( ) {
48
+ if ( this . selectElement ) {
49
+ this . selectElement . setAttribute ( 'data-skip-morph' , '' ) ;
50
+ }
52
51
if ( this . urlValue ) {
53
52
this . tomSelect = __classPrivateFieldGet ( this , _default_1_instances , "m" , _default_1_createAutocompleteWithRemoteData ) . call ( this , this . urlValue , this . hasMinCharactersValue ? this . minCharactersValue : null ) ;
54
53
return ;
@@ -97,9 +96,12 @@ class default_1 extends Controller {
97
96
resetTomSelect ( ) {
98
97
if ( this . tomSelect ) {
99
98
this . stopMutationObserver ( ) ;
100
- this . tomSelect . clearOptions ( ) ;
101
- this . tomSelect . settings . maxOptions = this . getMaxOptions ( ) ;
102
- this . tomSelect . sync ( ) ;
99
+ const currentHtml = this . element . innerHTML ;
100
+ const currentValue = this . tomSelect . getValue ( ) ;
101
+ this . tomSelect . destroy ( ) ;
102
+ this . element . innerHTML = currentHtml ;
103
+ this . initializeTomSelect ( ) ;
104
+ this . tomSelect . setValue ( currentValue ) ;
103
105
this . startMutationObserver ( ) ;
104
106
}
105
107
}
@@ -113,22 +115,6 @@ class default_1 extends Controller {
113
115
}
114
116
this . startMutationObserver ( ) ;
115
117
}
116
- updateTomSelectPlaceholder ( ) {
117
- const input = this . element ;
118
- let placeholder = input . getAttribute ( 'placeholder' ) || input . getAttribute ( 'data-placeholder' ) ;
119
- if ( ! placeholder && ! this . tomSelect . allowEmptyOption ) {
120
- const option = input . querySelector ( 'option[value=""]' ) ;
121
- if ( option ) {
122
- placeholder = option . textContent ;
123
- }
124
- }
125
- if ( placeholder ) {
126
- this . stopMutationObserver ( ) ;
127
- this . tomSelect . settings . placeholder = placeholder ;
128
- this . tomSelect . control_input . setAttribute ( 'placeholder' , placeholder ) ;
129
- this . startMutationObserver ( ) ;
130
- }
131
- }
132
118
startMutationObserver ( ) {
133
119
if ( ! this . isObserving && this . mutationObserver ) {
134
120
this . mutationObserver . observe ( this . element , {
@@ -147,73 +133,51 @@ class default_1 extends Controller {
147
133
}
148
134
}
149
135
onMutations ( mutations ) {
150
- const addedOptionElements = [ ] ;
151
- const removedOptionElements = [ ] ;
152
- let hasAnOptionChanged = false ;
153
136
let changeDisabledState = false ;
154
- let changePlaceholder = false ;
137
+ let requireReset = false ;
155
138
mutations . forEach ( ( mutation ) => {
156
139
switch ( mutation . type ) {
157
- case 'childList' :
158
- if ( mutation . target instanceof HTMLOptionElement ) {
159
- if ( mutation . target . value === '' ) {
160
- changePlaceholder = true ;
161
- break ;
162
- }
163
- hasAnOptionChanged = true ;
164
- break ;
165
- }
166
- mutation . addedNodes . forEach ( ( node ) => {
167
- if ( node instanceof HTMLOptionElement ) {
168
- if ( removedOptionElements . includes ( node ) ) {
169
- removedOptionElements . splice ( removedOptionElements . indexOf ( node ) , 1 ) ;
170
- return ;
171
- }
172
- addedOptionElements . push ( node ) ;
173
- }
174
- } ) ;
175
- mutation . removedNodes . forEach ( ( node ) => {
176
- if ( node instanceof HTMLOptionElement ) {
177
- if ( addedOptionElements . includes ( node ) ) {
178
- addedOptionElements . splice ( addedOptionElements . indexOf ( node ) , 1 ) ;
179
- return ;
180
- }
181
- removedOptionElements . push ( node ) ;
182
- }
183
- } ) ;
184
- break ;
185
140
case 'attributes' :
186
- if ( mutation . target instanceof HTMLOptionElement ) {
187
- hasAnOptionChanged = true ;
188
- break ;
189
- }
190
141
if ( mutation . target === this . element && mutation . attributeName === 'disabled' ) {
191
142
changeDisabledState = true ;
192
143
break ;
193
144
}
194
- break ;
195
- case 'characterData' :
196
- if ( mutation . target instanceof Text && mutation . target . parentElement instanceof HTMLOptionElement ) {
197
- if ( mutation . target . parentElement . value === '' ) {
198
- changePlaceholder = true ;
199
- break ;
200
- }
201
- hasAnOptionChanged = true ;
145
+ if ( mutation . target === this . element && mutation . attributeName === 'multiple' ) {
146
+ requireReset = true ;
147
+ break ;
202
148
}
149
+ break ;
203
150
}
204
151
} ) ;
205
- if ( hasAnOptionChanged || addedOptionElements . length > 0 || removedOptionElements . length > 0 ) {
152
+ const newOptions = this . selectElement ? this . createOptionsDataStructure ( this . selectElement ) : [ ] ;
153
+ const areOptionsEquivalent = this . areOptionsEquivalent ( newOptions ) ;
154
+ if ( ! areOptionsEquivalent || requireReset ) {
155
+ this . originalOptions = newOptions ;
206
156
this . resetTomSelect ( ) ;
207
157
}
208
158
if ( changeDisabledState ) {
209
159
this . changeTomSelectDisabledState ( this . formElement . disabled ) ;
210
160
}
211
- if ( changePlaceholder ) {
212
- this . updateTomSelectPlaceholder ( ) ;
213
- }
214
161
}
215
- requiresLiveIgnore ( ) {
216
- return this . element instanceof HTMLSelectElement && this . element . multiple ;
162
+ createOptionsDataStructure ( selectElement ) {
163
+ return Array . from ( selectElement . options ) . map ( ( option ) => {
164
+ const optgroup = option . closest ( 'optgroup' ) ;
165
+ return {
166
+ value : option . value ,
167
+ text : option . text ,
168
+ group : optgroup ? optgroup . label : null ,
169
+ } ;
170
+ } ) ;
171
+ }
172
+ areOptionsEquivalent ( newOptions ) {
173
+ if ( this . originalOptions . length !== newOptions . length ) {
174
+ return false ;
175
+ }
176
+ const normalizeOption = ( option ) => `${ option . value } -${ option . text } -${ option . group } ` ;
177
+ const originalOptionsSet = new Set ( this . originalOptions . map ( normalizeOption ) ) ;
178
+ const newOptionsSet = new Set ( newOptions . map ( normalizeOption ) ) ;
179
+ return ( originalOptionsSet . size === newOptionsSet . size &&
180
+ [ ...originalOptionsSet ] . every ( ( option ) => newOptionsSet . has ( option ) ) ) ;
217
181
}
218
182
}
219
183
_default_1_instances = new WeakSet ( ) , _default_1_getCommonConfig = function _default_1_getCommonConfig ( ) {
@@ -233,19 +197,12 @@ _default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _def
233
197
return `<div class="no-results">${ this . noResultsFoundTextValue } </div>` ;
234
198
} ,
235
199
} ;
236
- const requiresLiveIgnore = this . requiresLiveIgnore ( ) ;
237
200
const config = {
238
201
render,
239
202
plugins,
240
203
onItemAdd : ( ) => {
241
204
this . tomSelect . setTextboxValue ( '' ) ;
242
205
} ,
243
- onInitialize : function ( ) {
244
- if ( requiresLiveIgnore ) {
245
- const tomSelect = this ;
246
- tomSelect . wrapper . setAttribute ( 'data-live-ignore' , '' ) ;
247
- }
248
- } ,
249
206
closeAfterSelect : true ,
250
207
} ;
251
208
if ( ! this . selectElement && ! this . urlValue ) {
0 commit comments