holoviz / panel

Panel: The powerful data exploration & web app framework for Python
https://panel.holoviz.org
BSD 3-Clause "New" or "Revised" License
4.73k stars 516 forks source link

Sort and Filter MultiSelect widget #392

Open jsignell opened 5 years ago

jsignell commented 5 years ago

I am trying to set up a new composite widget consisting of a MultiSelect with a sort button and a filter input at the top. I was hoping that it would be able to work in js, but that doesn't quite seem to be the case.

import panel as pn
pn.extension()

labels = [t[0] + t[1] + t[2] for t in zip('abcdefghijklmnopqrstuvwxyz','bcdefghijklmnopqrstuvwxyza', 'cdefghijklmnopqrstuvwxyzab')]
options = dict(zip(labels, range(len(labels))))

multi = pn.widgets.MultiSelect(options=options)
sort = pn.widgets.Button(name='▼', width=40)
filt = pn.widgets.AutocompleteInput(options=list(options.keys()), placeholder='Filter')

sort.jslink(multi, code={'clicks': """
source.label = source.clicks % 2 ? '▼' : '▲';
target.options = source.clicks % 2 ? target.options.sort() : target.options.reverse();
console.log(target.options[0]);
console.log(source.clicks);
"""})

filt.jslink(multi, code={'value': """
target.value = target.options.filter(label => label.includes(source.value));
console.log(target.value);
"""})

result = pn.pane.Str('')
multi.link(result, value='object')

pn.Column(pn.Row(sort, filt, margin=0), multi, result)

This seems to work fairly well except for the options don't update.

For comparison, here is the same widget implemented with python linking

multi = pn.widgets.MultiSelect(options=options)
sort = pn.widgets.Button(name='▼', width=40)
filt = pn.widgets.AutocompleteInput(options=list(options.keys()), placeholder='Filter')

def sort_options(event):
    reverse = not event.new % 2
    multi.options = {k: multi.options[k] for k in sorted(multi.options.keys(), reverse=reverse)}
    multi.param.trigger('options')
    sort.name = '▼' if not reverse else '▲'

def filter_values(event):
    multi.value = [v for k,v in multi.options.items() if event.new in k]

sort.param.watch(sort_options, 'clicks')
filt.param.watch(filter_values, 'value')

result = pn.pane.Str()
multi.link(result, value='object')

pn.Column(pn.Row(sort, filt, margin=0), multi, result)
jsignell commented 5 years ago
Screen Shot 2019-04-19 at 3 10 02 PM
xavArtley commented 5 years ago

you can add target.properties.options.change.emit() to make it works in javascript

sort.jslink(multi, code={'clicks': """
source.label = source.clicks % 2 ? '▼' : '▲';
target.options = source.clicks % 2 ? target.options.sort() : target.options.reverse();
console.log(target.options[0]);
console.log(source.clicks);
target.properties.options.change.emit()
"""})