I'm having some hard time solving this one. I have built a Retool app and used several multiselect listbox components. Items are both selected manually and programmatically in ordered lists that are often long.
To improve the overall readability of the app and get as quickly as possible which items have been selected, I'd like to push those selected items to the top of the multiselect listbox components.
Hey @jeremy! So I read this and thought it would be a good challenge but certainly feasible. I set out by writing a JS transformer to sort an array of options by the array of selected items in the multi-select listbox:
let optionsArray = {{query1.data.options}};
let selectionsArray = {{multiselectListbox.selectedItems}}
let sortedArray = optionsArray.sort((a, b) => {
const aIndex = selectionsArray.indexOf(a);
const bIndex = selectionsArray.indexOf(b);
if (aIndex === -1) {
return 1;
}
if (bIndex === -1) {
return -1;
}
return aIndex - bIndex;
});
return sortedArray
Then I set the listbox's data source to this transformer. Unfortunately, I'd not recognised that this would of course result in a circular dependency, whereby selected items are sorting the data source, but the data source is sorting the items, in general.
As far as I can tell, there isn't a way to get around circular dependency, but I'd be very happy to be proven wrong!
I think the lovely people at Retool would need to add this option to multi-select components so that selected items are rendered at the top of the options list, without actually changing the order of the options array under-the-hood.
The problem is I'm not sure I can set a new value in selectedIndexes to move the selected item to the top. And even if I could, what happens when I'll unselect the option? I feel like it might exist a workaround with a temporary state to store previous & new indexes.
Just filed this as a feature request internally! Thank you for flagging @jeremy and thank you for sharing such a clever workaround @ryanm.
As a worse and less functional workaround, depending on your use case, you could add another Multiselect Listbox and set its value to {{multiselectListbox1.selectedItems}} and its default value to {{multiselectListbox2.values}} to select all values by default.
This is not a great workaround, but I wanted to share juuust in case it helps