Skip to content

Commit 6424bd4

Browse files
zavarocksmnandre
authored andcommitted
[Autocomplete] Fix grouped options order
1 parent 18a4b4e commit 6424bd4

File tree

2 files changed

+84
-12
lines changed

2 files changed

+84
-12
lines changed

src/Autocomplete/assets/dist/controller.js

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,9 @@ class default_1 extends Controller {
195195
}
196196
createOptionsDataStructure(selectElement) {
197197
return Array.from(selectElement.options).map((option) => {
198-
const optgroup = option.closest('optgroup');
199198
return {
200199
value: option.value,
201200
text: option.text,
202-
group: optgroup ? optgroup.label : null,
203201
};
204202
});
205203
}
@@ -216,7 +214,7 @@ class default_1 extends Controller {
216214
if (filteredOriginalOptions.length !== filteredNewOptions.length) {
217215
return false;
218216
}
219-
const normalizeOption = (option) => `${option.value}-${option.text}-${option.group}`;
217+
const normalizeOption = (option) => `${option.value}-${option.text}`;
220218
const originalOptionsSet = new Set(filteredOriginalOptions.map(normalizeOption));
221219
const newOptionsSet = new Set(filteredNewOptions.map(normalizeOption));
222220
return (originalOptionsSet.size === newOptionsSet.size &&
@@ -250,6 +248,40 @@ _default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _def
250248
this.tomSelect.setTextboxValue('');
251249
},
252250
closeAfterSelect: true,
251+
onOptionAdd: (value, data) => {
252+
let parentElement = this.tomSelect.input;
253+
let optgroupData = null;
254+
const optgroup = data[this.tomSelect.settings.optgroupField];
255+
if (optgroup && this.tomSelect.optgroups) {
256+
optgroupData = this.tomSelect.optgroups[optgroup];
257+
if (optgroupData) {
258+
const optgroupElement = parentElement.querySelector(`optgroup[label="${optgroupData.label}"]`);
259+
if (optgroupElement) {
260+
parentElement = optgroupElement;
261+
}
262+
}
263+
}
264+
const optionElement = document.createElement('option');
265+
optionElement.value = value;
266+
optionElement.text = data[this.tomSelect.settings.labelField];
267+
const optionOrder = data['$order'];
268+
let orderedOption = null;
269+
for (const [, tomSelectOption] of Object.entries(this.tomSelect.options)) {
270+
if (tomSelectOption['$order'] === optionOrder) {
271+
orderedOption = parentElement.querySelector(`:scope > option[value="${tomSelectOption[this.tomSelect.settings.valueField]}"]`);
272+
break;
273+
}
274+
}
275+
if (orderedOption) {
276+
orderedOption.insertAdjacentElement('afterend', optionElement);
277+
}
278+
else if (optionOrder >= 0) {
279+
parentElement.append(optionElement);
280+
}
281+
else {
282+
parentElement.prepend(optionElement);
283+
}
284+
},
253285
};
254286
if (!this.selectElement && !this.urlValue) {
255287
config.shouldLoad = () => false;

src/Autocomplete/assets/src/controller.ts

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ export interface AutocompleteConnectOptions {
1717
tomSelect: TomSelect;
1818
options: any;
1919
}
20+
interface OptionDataStructure {
21+
value: string;
22+
text: string;
23+
}
2024

2125
export default class extends Controller {
2226
static values = {
@@ -47,7 +51,7 @@ export default class extends Controller {
4751
private mutationObserver: MutationObserver;
4852
private isObserving = false;
4953
private hasLoadedChoicesPreviously = false;
50-
private originalOptions: Array<{ value: string; text: string; group: string | null }> = [];
54+
private originalOptions: Array<OptionDataStructure> = [];
5155

5256
initialize() {
5357
if (!this.mutationObserver) {
@@ -158,6 +162,47 @@ export default class extends Controller {
158162
this.tomSelect.setTextboxValue('');
159163
},
160164
closeAfterSelect: true,
165+
// fix positioning (in the dropdown) of options added through addOption()
166+
onOptionAdd: (value: string, data: { [key: string]: any }) => {
167+
let parentElement = this.tomSelect.input as Element;
168+
let optgroupData = null;
169+
170+
const optgroup = data[this.tomSelect.settings.optgroupField];
171+
if (optgroup && this.tomSelect.optgroups) {
172+
optgroupData = this.tomSelect.optgroups[optgroup];
173+
if (optgroupData) {
174+
const optgroupElement = parentElement.querySelector(`optgroup[label="${optgroupData.label}"]`);
175+
if (optgroupElement) {
176+
parentElement = optgroupElement;
177+
}
178+
}
179+
}
180+
181+
const optionElement = document.createElement('option');
182+
optionElement.value = value;
183+
optionElement.text = data[this.tomSelect.settings.labelField];
184+
185+
const optionOrder = data['$order'];
186+
let orderedOption = null;
187+
188+
for (const [, tomSelectOption] of Object.entries(this.tomSelect.options)) {
189+
if (tomSelectOption['$order'] === optionOrder) {
190+
orderedOption = parentElement.querySelector(
191+
`:scope > option[value="${tomSelectOption[this.tomSelect.settings.valueField]}"]`
192+
);
193+
194+
break;
195+
}
196+
}
197+
198+
if (orderedOption) {
199+
orderedOption.insertAdjacentElement('afterend', optionElement);
200+
} else if (optionOrder >= 0) {
201+
parentElement.append(optionElement);
202+
} else {
203+
parentElement.prepend(optionElement);
204+
}
205+
},
161206
};
162207

163208
// for non-autocompleting input elements, avoid the "No results" message that always shows
@@ -414,20 +459,16 @@ export default class extends Controller {
414459
}
415460
}
416461

417-
private createOptionsDataStructure(
418-
selectElement: HTMLSelectElement
419-
): Array<{ value: string; text: string; group: string | null }> {
462+
private createOptionsDataStructure(selectElement: HTMLSelectElement): Array<OptionDataStructure> {
420463
return Array.from(selectElement.options).map((option) => {
421-
const optgroup = option.closest('optgroup');
422464
return {
423465
value: option.value,
424466
text: option.text,
425-
group: optgroup ? optgroup.label : null,
426467
};
427468
});
428469
}
429470

430-
private areOptionsEquivalent(newOptions: Array<{ value: string; text: string; group: string | null }>): boolean {
471+
private areOptionsEquivalent(newOptions: Array<OptionDataStructure>): boolean {
431472
// remove the empty option, which is added by TomSelect so may be missing from new options
432473
const filteredOriginalOptions = this.originalOptions.filter((option) => option.value !== '');
433474
const filteredNewOptions = newOptions.filter((option) => option.value !== '');
@@ -447,8 +488,7 @@ export default class extends Controller {
447488
return false;
448489
}
449490

450-
const normalizeOption = (option: { value: string; text: string; group: string | null }) =>
451-
`${option.value}-${option.text}-${option.group}`;
491+
const normalizeOption = (option: OptionDataStructure) => `${option.value}-${option.text}`;
452492
const originalOptionsSet = new Set(filteredOriginalOptions.map(normalizeOption));
453493
const newOptionsSet = new Set(filteredNewOptions.map(normalizeOption));
454494

0 commit comments

Comments
 (0)