0
0
mirror of https://github.com/wagtail/wagtail.git synced 2024-12-01 11:41:20 +01:00

Move FilteredSelect to wagtail.admin.widgets

This commit is contained in:
Matt Westcott 2020-04-30 17:33:59 +01:00 committed by Matt Westcott
parent 35c55091af
commit 36780a9f56
3 changed files with 87 additions and 85 deletions

View File

@ -1,10 +1,8 @@
import django_filters
from django import forms
from django.utils.translation import gettext_lazy as _
from django_filters.widgets import SuffixedMultiWidget
from wagtail.admin.staticfiles import versioned_static
from wagtail.admin.widgets import AdminDateInput, BooleanButtonSelect, ButtonSelect
from wagtail.admin.widgets import AdminDateInput, BooleanButtonSelect, ButtonSelect, FilteredSelect
from wagtail.core.models import Page, Task, TaskState, Workflow, WorkflowState
@ -25,88 +23,6 @@ class DateRangePickerWidget(SuffixedMultiWidget):
return [None, None]
class FilteredSelect(forms.Select):
"""
A select box variant that adds 'data-' attributes to the <select> and <option> elements
to allow the options to be dynamically filtered by another select box.
See wagtailadmin/js/filtered-select.js for an example of how these attributes are configured.
"""
def __init__(self, attrs=None, choices=(), filter_field=''):
super().__init__(attrs, choices)
self.filter_field = filter_field
def build_attrs(self, base_attrs, extra_attrs=None):
my_attrs = {
'data-widget': 'filtered-select',
'data-filter-field': self.filter_field,
}
if extra_attrs:
my_attrs.update(extra_attrs)
return super().build_attrs(base_attrs, my_attrs)
def optgroups(self, name, value, attrs=None):
# copy of Django's Select.optgroups, modified to accept filter_value as a
# third item in the tuple and expose that as a data-filter-value attribute
# on the final <option>
groups = []
has_selected = False
for index, choice in enumerate(self.choices):
try:
(option_value, option_label, filter_value) = choice
except ValueError:
# *ChoiceField will still output blank options as a 2-tuple,
# so need to handle that too
(option_value, option_label) = choice
filter_value = None
if option_value is None:
option_value = ''
subgroup = []
if isinstance(option_label, (list, tuple)):
group_name = option_value
subindex = 0
choices = option_label
else:
group_name = None
subindex = None
choices = [(option_value, option_label)]
groups.append((group_name, subgroup, index))
for subvalue, sublabel in choices:
selected = (
str(subvalue) in value
and (not has_selected or self.allow_multiple_selected)
)
has_selected |= selected
subgroup.append(self.create_option(
name, subvalue, sublabel, selected, index, subindex=subindex,
filter_value=filter_value
))
if subindex is not None:
subindex += 1
return groups
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None, filter_value=None):
option = super().create_option(
name, value, label, selected, index, subindex=subindex, attrs=attrs
)
if filter_value is not None:
option['attrs']['data-filter-value'] = filter_value
return option
@property
def media(self):
return forms.Media(js=[
versioned_static('wagtailadmin/js/filtered-select.js'),
])
class FilteredModelChoiceIterator(django_filters.fields.ModelChoiceIterator):
"""
A variant of Django's ModelChoiceIterator that, instead of yielding (value, label) tuples,

View File

@ -3,4 +3,5 @@ from wagtail.admin.widgets.button import * # NOQA
from wagtail.admin.widgets.button_select import * # NOQA
from wagtail.admin.widgets.chooser import * # NOQA
from wagtail.admin.widgets.datetime import * # NOQA
from wagtail.admin.widgets.filtered_select import * # NOQA
from wagtail.admin.widgets.tags import * # NOQA

View File

@ -0,0 +1,85 @@
from django import forms
from wagtail.admin.staticfiles import versioned_static
class FilteredSelect(forms.Select):
"""
A select box variant that adds 'data-' attributes to the <select> and <option> elements
to allow the options to be dynamically filtered by another select box.
See wagtailadmin/js/filtered-select.js for an example of how these attributes are configured.
"""
def __init__(self, attrs=None, choices=(), filter_field=''):
super().__init__(attrs, choices)
self.filter_field = filter_field
def build_attrs(self, base_attrs, extra_attrs=None):
my_attrs = {
'data-widget': 'filtered-select',
'data-filter-field': self.filter_field,
}
if extra_attrs:
my_attrs.update(extra_attrs)
return super().build_attrs(base_attrs, my_attrs)
def optgroups(self, name, value, attrs=None):
# copy of Django's Select.optgroups, modified to accept filter_value as a
# third item in the tuple and expose that as a data-filter-value attribute
# on the final <option>
groups = []
has_selected = False
for index, choice in enumerate(self.choices):
try:
(option_value, option_label, filter_value) = choice
except ValueError:
# *ChoiceField will still output blank options as a 2-tuple,
# so need to handle that too
(option_value, option_label) = choice
filter_value = None
if option_value is None:
option_value = ''
subgroup = []
if isinstance(option_label, (list, tuple)):
group_name = option_value
subindex = 0
choices = option_label
else:
group_name = None
subindex = None
choices = [(option_value, option_label)]
groups.append((group_name, subgroup, index))
for subvalue, sublabel in choices:
selected = (
str(subvalue) in value
and (not has_selected or self.allow_multiple_selected)
)
has_selected |= selected
subgroup.append(self.create_option(
name, subvalue, sublabel, selected, index, subindex=subindex,
filter_value=filter_value
))
if subindex is not None:
subindex += 1
return groups
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None, filter_value=None):
option = super().create_option(
name, value, label, selected, index, subindex=subindex, attrs=attrs
)
if filter_value is not None:
option['attrs']['data-filter-value'] = filter_value
return option
@property
def media(self):
return forms.Media(js=[
versioned_static('wagtailadmin/js/filtered-select.js'),
])