Source code for material_widgets.widgets
"""Customized form field widgets with templates styled with Material Components
for the Web.
References
----------
`Material Design for the Web <https://material.io/components/web/>`_
"""
# pylint: disable=invalid-name, too-few-public-methods
# pylint: disable=too-many-ancestors, too-many-arguments, too-many-lines
from django.forms import widgets, utils
from .settings import MATERIAL_CSS, MATERIAL_JS
__all__ = (
'MaterialCheckboxInput',
'MaterialCheckboxSelectMultiple',
'MaterialClearableFileInput',
'MaterialDateInput',
'MaterialDateTimeInput',
'MaterialEmailInput',
'MaterialFileInput',
'MaterialHiddenInput',
'MaterialMultipleHiddenInput',
'MaterialNullBooleanSelect',
'MaterialNumberInput',
'MaterialPasswordInput',
'MaterialRadioSelect',
'MaterialSelect',
'MaterialSelectDateWidget',
'MaterialSelectMultiple',
'MaterialSliderInput',
'MaterialSplitDateTimeWidget',
'MaterialSplitHiddenDateTimeWidget',
'MaterialSwitchInput',
'MaterialTextarea',
'MaterialTextInput',
'MaterialTimeInput',
'MaterialURLInput',
)
class MaterialComponent(widgets.Widget):
"""Superclass of Material widgets which adds attributes used by Material
Components.
Passes label and help_text to the widget context. Declares under Media the
Material CSS and JS components required to correctly display the widget.
Parameters
----------
label : str, optional
Displayed on the widget's field in the template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed under text field and textarea inputs, to the right of slider
labels, and as tool tips on labels for other inputs.
*args
**kwargs
"""
def __init__(self, label=None, help_text=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.label = label
self.help_text = help_text
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget'].update({
'label': self.label,
'help_text': self.help_text,
})
return context
@property
def media(self): # pylint: disable=missing-docstring
return widgets.Media(
css={'all': (
MATERIAL_CSS,
'material_widgets/css/material_error.css',
)},
js=(MATERIAL_JS,),
)
class MaterialMultiWidget(widgets.MultiWidget):
"""Superclass of Material split widgets which adds attributes used by
Material Components. Material split widgets consist of multiple Material
widgets.
Passes label to the widget context. Declares under Media the Material CSS
and JS components required to correctly display the widget.
Parameters
----------
multiwidgets : list or tuple of widget objects
Widgets to be included in the multiwidget.
attrs : dict, optional
HTML attributes to be included in the multiwidget's template.
label : str, optional
Displayed to the left of the split widget fields in the template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
*args
**kwargs
"""
template_name = "material_widgets/widgets/material_multiwidget.html"
def __init__(self, multiwidgets, attrs=None, label=None, *args, **kwargs):
super().__init__(multiwidgets, attrs, *args, **kwargs)
self.label = label
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget'].update({
'label': self.label,
})
return context
@property
def media(self): # pylint: disable=missing-docstring
return widgets.Media(
css={'all': (
MATERIAL_CSS,
'material_widgets/css/material_error.css',
'material_widgets/css/material_multiwidget.css',
)},
js=(MATERIAL_JS,),
)
def decompress(self, value):
"""Return a list of decompressed values for the given compressed value.
The given value can be assumed to be valid, but not necessarily
non-empty.
"""
raise NotImplementedError('Subclasses must implement this method.')
class MaterialCheckbox(MaterialComponent):
"""Superclass of Material widgets that make use of Material Checkbox
Components.
Declares under Media the Material JS component required to correctly
display the widget.
"""
class Media: # pylint: disable=missing-docstring
js = ('material_widgets/js/material_checkbox.js',)
class MaterialFileButton(MaterialComponent):
"""Superclass of Material widgets that make use of Material File Button
Components.
Declares under Media the Material CSS and JS components required to
correctly display the widget.
Parameters
----------
button : list or tuple of str, optional
Button style modifiers include 'compact', 'dense', 'raised', 'stroked',
and 'unelevated'. See `mdc-button CSS Classes <https://github.com\
/material-components/material-components-web/tree/master/packages\
/mdc-button#css-classes>`_ for details.
icon : str, optional
Icon to be displayed on the file button. See `Material Icons
<https://material.io/icons/>`_ for
choices.
*args
**kwargs
"""
def __init__(self, button=None, icon=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.button = button
self.icon = icon
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget'].update({
'button': self.button,
'icon': self.icon,
})
return context
class Media: # pylint: disable=missing-docstring
css = {
'all': (
'material_widgets/css/material_button.css',
'material_widgets/css/material_file_input.css',
)
}
js = (
'material_widgets/js/material_button.js',
'material_widgets/js/material_file_input.js',
)
class MaterialSelectMenu(MaterialComponent):
"""Superclass of Material widgets that make use of Material Select Menu
Components.
Declares under Media the Material CSS and JS components required to
correctly display the widget.
"""
class Media: # pylint: disable=missing-docstring
css = {
'all': (
'material_widgets/css/material_select.css',
)
}
js = ('material_widgets/js/material_select.js',)
class MaterialTextField(MaterialComponent):
"""Superclass of Material widgets that make use of Material TextField
Components.
Declares under Media the Material CSS and JS components required to
correctly display the widget.
Parameters
----------
persistent_help_text : bool, optional
Help text will be persistently displayed if True, else it will only be
visible when the field is active.
Defaults to False.
*args
**kwargs
"""
def __init__(self, persistent_help_text=False, *args, **kwargs):
super().__init__(*args, **kwargs)
self.persistent_help_text = persistent_help_text
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget'].update({
'persistent_help_text': self.persistent_help_text,
})
return context
class Media: # pylint: disable=missing-docstring
css = {
'all': (
'material_widgets/css/material_text_field.css',
)
}
js = ('material_widgets/js/material_text_field.js',)
[docs]class MaterialCheckboxInput(MaterialCheckbox, widgets.CheckboxInput):
"""Material CheckboxInput
Default widget for BooleanField under MaterialForm.
Parameters
----------
label : str, optional
Displayed to the right of the checkbox.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed as a tool tip on the label.
Examples
--------
Explicit widget declaration is unnecessary in a BooleanField under a
MaterialForm.
>>> checkbox = forms.BooleanField(
>>> label='Checkbox',
>>> help_text='This is a checkbox',
>>> #widget=MaterialCheckboxInput(),
>>> )
"""
template_name = 'material_widgets/widgets/material_checkbox.html'
[docs]class MaterialCheckboxSelectMultiple(
MaterialCheckbox,
widgets.CheckboxSelectMultiple):
"""Material CheckboxSelectMultiple
Parameters
----------
label : str, optional
Displayed above the checkbox group.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : list or tuple of str, optional
Displayed as tool tips on the respective checkbox labels.
choices : list or tuple of (value, label) pairs
Each pair will be displayed as a checkbox.
Other Parameters
----------------
is_vertical : bool, optional
Layout of checkboxes will be vertical if True, or horizontal otherwise.
Defaults to True.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> checkbox_select_multiple = forms.MultipleChoiceField(
>>> label='Multiple Checkboxes',
>>> help_text=(
>>> 'This is checkbox 1',
>>> 'This is checkbox 2',
>>> 'This is checkbox 3',
>>> ),
>>> widget=MaterialCheckboxSelectMultiple(
>>> is_vertical=False,
>>> ),
>>> choices=(
>>> ('checkbox_1', 'Checkbox 1'),
>>> ('checkbox_2', 'Checkbox 2'),
>>> ('checkbox_3', 'Checkbox 3'),
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_checkbox_select.html'
option_template_name = 'material_widgets/widgets/material_checkbox_option.html'
def __init__(self, is_vertical=True, *args, **kwargs):
super().__init__(*args, **kwargs)
self.is_vertical = is_vertical
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget'].update({
'is_vertical': self.is_vertical,
})
return context
def create_option(self,
name, value, label, selected, index,
subindex=None, attrs=None):
option = super().create_option(
name, value, label, selected, index, subindex, attrs
)
option['help_text'] = self.help_text[index]
return option
[docs]class MaterialClearableFileInput(
MaterialFileButton,
widgets.ClearableFileInput):
"""Material ClearableFileInput
Default widget for FileField and ImageField under MaterialForm.
Parameters
----------
label : str, optional
Displayed on the button.
Defaults to widget's capitalized field name with underscores converted
to spaces.
Label dynamically changes to selected file name, or file count if
the 'multiple' attrs is True.
help_text : str, optional
Displayed as a tool tip on the button.
Other Parameters
----------------
button : list or tuple of str, optional
Button style modifiers include 'compact', 'dense', 'raised', 'stroked',
and 'unelevated'. See `mdc-button CSS Classes <https://github.com\
/material-components/material-components-web/tree/master/packages\
/mdc-button#css-classes>`_ for details.
icon : str, optional
Icon to be displayed on the file button. See `Material Icons
<https://material.io/icons/>`_ for
choices.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> clearable_file_input = forms.FileField(
>>> ### Blank the label to only display the icon
>>> label='',
>>> help_text='Choose 1 or more files',
>>> widget=MaterialClearableFileInput(
>>> ### compact, dense, raised, stroked, unelevated
>>> button=('compact', 'dense', 'raised'),
>>> ### From material-icons; https://material.io/icons/
>>> icon='file_upload',
>>> attrs={
>>> 'multiple': True,
>>> },
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_clearable_file_input.html'
class Media: # pylint: disable=missing-docstring
css = {
'all': (
'material_widgets/css/material_button.css',
'material_widgets/css/material_file_input.css',
)
}
js = (
'material_widgets/js/material_button.js',
'material_widgets/js/material_checkbox.js',
'material_widgets/js/material_file_input.js',
)
[docs]class MaterialDateInput(MaterialTextField, widgets.DateInput):
"""Material DateInput
Default widget for DateField under MaterialForm.
Parameters
----------
label : str, optional
Displayed on the widget's field in the template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed under the input field.
Other Parameters
----------------
persistent_help_text : bool, optional
Help text will be persistently displayed if True, else it will only be
visible when the field is active.
Defaults to False.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> date_field = forms.DateField(
>>> label='Date',
>>> help_text='YYYY-MM-DD',
>>> widget=MaterialDateInput(
>>> persistent_help_text=True,
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_date.html'
[docs]class MaterialDateTimeInput(MaterialTextField, widgets.DateTimeInput):
"""Material DateTimeInput
Default widget for DateTimeField under MaterialForm.
Parameters
----------
label : str, optional
Displayed on the widget's field in the template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed under the input field.
Other Parameters
----------------
persistent_help_text : bool, optional
Help text will be persistently displayed if True, else it will only be
visible when the field is active.
Defaults to False.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> datetime_field = forms.DateTimeField(
>>> label='Date and Time',
>>> help_text='YYYY-MM-DD HH:MM:SS',
>>> widget=MaterialDateTimeInput(
>>> persistent_help_text=True,
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_datetime.html'
[docs]class MaterialEmailInput(MaterialTextField, widgets.EmailInput):
"""Material EmailInput
Default widget for EmailField under MaterialForm.
Parameters
----------
label : str, optional
Displayed on the widget's field in the template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed under the input field.
Other Parameters
----------------
persistent_help_text : bool, optional
Help text will be persistently displayed if True, else it will only be
visible when the field is active.
Defaults to False.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> email = forms.EmailField(
>>> label='Email Address',
>>> help_text='your_email@example.com',
>>> widget=MaterialEmailInput(
>>> persistent_help_text=True,
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_email.html'
[docs]class MaterialFileInput(MaterialFileButton, widgets.FileInput):
"""Material FileInput
Parameters
----------
label : str, optional
Displayed on the button.
Defaults to widget's capitalized field name with underscores converted
to spaces.
Label dynamically changes to selected file name, or file count if
the 'multiple' attrs is True.
help_text : str, optional
Displayed as a tool tip on the button.
Other Parameters
----------------
button : list or tuple of str, optional
Button style modifiers include 'compact', 'dense', 'raised', 'stroked',
and 'unelevated'. See `mdc-button CSS Classes <https://github.com\
/material-components/material-components-web/tree/master/packages\
/mdc-button#css-classes>`_ for details.
icon : str, optional
Icon to be displayed on the file button. See `Material Icons
<https://material.io/icons/>`_ for
choices.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> file_input = forms.FileField(
>>> ### Blank the label to only display the icon
>>> label='',
>>> help_text='Choose 1 or more files',
>>> widget=MaterialFileInput(
>>> ### compact, dense, raised, stroked, unelevated
>>> button=('compact', 'dense', 'raised'),
>>> ### From material-icons; https://material.io/icons/
>>> icon='attachment',
>>> attrs={
>>> 'multiple': True,
>>> },
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_file.html'
[docs]class MaterialHiddenInput(MaterialComponent, widgets.HiddenInput):
"""Material HiddenInput
Parameters
----------
initial : varies depending on field, optional
Initial value to populate the hidden input in the form.
Examples
--------
>>> hidden_input = forms.CharField(
>>> initial='hidden_value',
>>> widget=widgets.HiddenInput(),
>>> )
"""
template_name = 'material_widgets/widgets/material_hidden.html'
[docs]class MaterialMultipleHiddenInput(
MaterialComponent,
widgets.MultipleHiddenInput):
"""Material MultipleHiddenInput
Parameters
----------
initial : varies depending on field, optional
Initial Value to populate the multiple hidden input in the form.
Examples
--------
>>> multiple_hidden_input = forms.MultipleChoiceField(
>>> initial=['hidden_1', 'hidden_2', 'hidden_3',],
>>> widget=widgets.MultipleHiddenInput(),
>>> choices=(
>>> ('hidden_1', 'Hidden 1'),
>>> ('hidden_2', 'Hidden 2'),
>>> ('hidden_3', 'Hidden 3'),
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_multiple_hidden.html'
[docs]class MaterialNullBooleanSelect(MaterialSelectMenu, widgets.NullBooleanSelect):
"""Material NullBooleanSelect
Default widget for NullBooleanField under MaterialForm.
Parameters
----------
label : str, optional
Displayed to the left of the select menu.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed as a tool tip on the label.
Examples
--------
Explicit widget declaration is unnecessary in a NullBooleanField under a
MaterialForm.
>>> null_boolean_select = forms.NullBooleanField(
>>> label='Is this a null boolean select?',
>>> help_text='This is a null boolean select',
>>> #widget=MaterialNullBooleanSelect(),
>>> )
"""
template_name = 'material_widgets/widgets/material_select.html'
option_template_name = 'material_widgets/widgets/material_select_option.html'
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
attrs_class = (
context['widget']['attrs'].get('class', '')
+ ' mdc-select'
).strip()
context['widget']['attrs'].update({
'class': attrs_class,
})
return context
[docs]class MaterialNumberInput(MaterialTextField, widgets.NumberInput):
"""Material NumberInput
Default widget for DecimalField, FloatField, and IntegerField under
MaterialForm when Field.localize is False.
Parameters
----------
label : str, optional
Displayed on the widget's field in the template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed under the input field.
Other Parameters
----------------
persistent_help_text : bool, optional
Help text will be persistently displayed if True, else it will only be
visible when the field is active.
Defaults to False.
Examples
--------
Explicit widget declaration is unnecessary in a DecimalField, FloatField,
or IntegerField under MaterialForm.
>>> decimal = forms.DecimalField(
>>> label='Decimal Number',
>>> help_text='To 2 d.p.',
>>> min_value=0.0,
>>> max_value=10.0,
>>> decimal_places=2,
>>> )
Declare widget explicity to use custom widget parameters.
>>> integer = forms.IntegerField(
>>> label='Odd Number',
>>> help_text='No even numbers!',
>>> min_value=1,
>>> max_value=9,
>>> widget=widgets.NumberInput(
>>> persistent_help_text=True,
>>> attrs={
>>> 'step': '2',
>>> },
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_number.html'
[docs]class MaterialPasswordInput(MaterialTextField, widgets.PasswordInput):
"""Material PasswordInput
Parameters
----------
label : str, optional
Displayed on the widget's field in the template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed under the input field.
Other Parameters
----------------
persistent_help_text : bool, optional
Help text will be persistently displayed if True, else it will only be
visible when the field is active.
Defaults to False.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> password = forms.CharField(
>>> label="Secret Password",
>>> help_text='Please enter at least 8 characters',
>>> min_length=8,
>>> widget=MaterialPasswordInput(
>>> persistent_help_text=True,
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_password.html'
[docs]class MaterialRadioSelect(MaterialComponent, widgets.RadioSelect):
"""Material RadioSelect
Parameters
----------
label : str, optional
Displayed above the radio group.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : list or tuple of str, optional
Displayed as tool tips on the respective radio option labels.
choices : list or tuple of (value, label) pairs
Each pair will be displayed as a radio option.
Other Parameters
----------------
is_vertical : bool, optional
Layout of checkboxes will be vertical if True, or horizontal otherwise.
Defaults to False.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> radio_select = forms.ChoiceField(
>>> label='Radio Select',
>>> help_text=(
>>> 'This is radio 1',
>>> 'This is radio 2',
>>> 'This is radio 3',
>>> ),
>>> widget=widgets.RadioSelect(
>>> is_vertical=True,
>>> ),
>>> choices=(
>>> ('radio_select_1', 'Radio 1'),
>>> ('radio_select_2', 'Radio 2'),
>>> ('radio_select_3', 'Radio 3'),
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_radio.html'
option_template_name = 'material_widgets/widgets/material_radio_option.html'
def __init__(self, is_vertical=False, *args, **kwargs):
super().__init__(*args, **kwargs)
self.is_vertical = is_vertical
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget'].update({
'is_vertical': self.is_vertical,
})
return context
def create_option(self,
name, value, label, selected, index,
subindex=None, attrs=None):
option = super().create_option(
name, value, label, selected, index, subindex, attrs
)
option['help_text'] = self.help_text[index]
return option
[docs] class Media:
"""Material radio JS component."""
js = ('material_widgets/js/material_radio.js',)
[docs]class MaterialSelect(MaterialSelectMenu, widgets.Select):
"""Material Select
Default widget for ChoiceField, FilePathField, ModelChoiceField, and
TypedChoiceField under MaterialForm.
Parameters
----------
label : str, optional
Displayed to the left of the select menu.
If explicitly declared blank, the first item in the select menu will
act as an unselectable choice label.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed as a tool tip on the label.
Examples
--------
Explicit widget declaration is unnecessary in ChoiceField, FilePathField,
ModelChoiceField, and TypedChoiceField under MaterialForm.
>>> select = forms.ChoiceField(
>>> label='',
>>> #widget=MaterialSelect(),
>>> choices=(
>>> ('', 'Select'),
>>> ('choice_1', 'Choice 1'),
>>> ('choice_2', 'Choice 2'),
>>> ('choice_3', 'Choice 3'),
>>> ),
>>> )
Choices can be grouped.
>>> select_with_groups = forms.ChoiceField(
>>> label='',
>>> required=False,
>>> choices=(
>>> ('', 'Select with Groups'),
>>> ('Group 1', (
>>> ('group_1_choice_1', 'Group 1 Choice 1 '),
>>> ('group_1_choice_2', 'Group 1 Choice 2'),
>>> ('group_1_choice_3', 'Group 1 Choice 3'),
>>> )
>>> ),
>>> ('Group 2', (
>>> ('group_2_choice_4', 'Group 2 Choice 4'),
>>> ('group_2_choice_5', 'Group 2 Choice 5'),
>>> ('group_2_choice_6', 'Group 2 Choice 6'),
>>> )
>>> ),
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_select.html'
option_template_name = (
'material_widgets/widgets/'
'material_select_option.html'
)
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
attrs_class = (
context['widget']['attrs'].get('class', '')
+ ' mdc-select'
).strip()
context['widget']['attrs'].update({
'class': attrs_class,
})
return context
[docs]class MaterialSelectDateWidget(MaterialSelectMenu, widgets.SelectDateWidget):
"""Material SelectDateWidget
Parameters
----------
label : str, optional
Displayed to the left of the select menu.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed as a tool tip on the label.
Other Parameters
----------------
various : various, optional
See `django.forms.widgets.SelectDateWidget <https://docs.djangoproject.com\
/en/dev/ref/forms/widgets/#selectdatewidget>`_ for optional parameters.
Examples
--------
Widget can be declared as Django's SelectDateWidget or as
MaterialSelectDateWidget.
>>> date_select = forms.DateField(
>>> label="Date",
>>> help_text='This is a select date',
>>> widget=widgets.SelectDateWidget(
>>> years=[year for year in range(
>>> datetime.now().year, datetime.now().year-100, -1)
>>> ],
>>> empty_label=("Year", "Month", "Day"),
>>> )
>>> )
"""
template_name = 'material_widgets/widgets/material_select_date.html'
select_widget = MaterialSelect
[docs] class Media:
"""Material multiwidget CSS component."""
css = {
'all': (
'material_widgets/css/material_multiwidget.css',
)
}
[docs]class MaterialSelectMultiple(MaterialSelectMenu, widgets.SelectMultiple):
"""Material SelectMultiple
Default widget for MultipleChoiceField, ModelMultipleChoiceField, and
TypedMultipleChoiceField under MaterialForm.
Parameters
----------
label : str, optional
Displayed to the left of the select menu.
If explicitly declared blank, the first item in the select menu will
act as an unselectable choice label.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed as a tool tip on the label.
Examples
--------
Explicit widget declaration is unnecessary in MultipleChoiceField,
ModelMultipleChoiceField, and TypedMultipleChoiceField under MaterialForm.
>>> select_multiple = forms.MultipleChoiceField(
>>> label='Choose 1 or more',
>>> help_text='Ctrl + Click to unselect',
>>> #widget=MaterialSelect(),
>>> choices=(
>>> ('', 'Select'),
>>> ('multiple_choice_1', 'Multiple Choice 1'),
>>> ('multiple_choice_2', 'Multiple Choice 2'),
>>> ('multiple_choice_3', 'Multiple Choice 3'),
>>> ),
>>> )
Choices can be grouped.
>>> select_multiple_with_groups = forms.MultipleChoiceField(
>>> label='',
>>> #help_text='A label is needed to show the help text tooltip',
>>> choices=(
>>> ('Select Multiple 1', (
>>> ('multiple_choice_1', 'Multiple Choice 1'),
>>> ('multiple_choice_2', 'Multiple Choice 2'),
>>> ('multiple_choice_3', 'Multiple Choice 3'),
>>> )
>>> ),
>>> ('Select Multiple 2', (
>>> ('multiple_choice_4', 'Multiple Choice 4'),
>>> ('multiple_choice_5', 'Multiple Choice 5'),
>>> ('multiple_choice_6', 'Multiple Choice 6'),
>>> )
>>> ),
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_select_multiple.html'
option_template_name = (
'material_widgets/widgets/material_select_option_nojs.html'
)
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
attrs_class = (
context['widget']['attrs'].get('class', '')
+ ' mdc-multi-select mdc-list'
).strip()
context['widget']['attrs'].update({
'class': attrs_class,
})
return context
[docs]class MaterialSliderInput(MaterialComponent, widgets.NumberInput):
"""Material SliderInput
Parameters
----------
label : str, optional
Displayed above the widget in the template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed to the right of the label.
Other Parameters
----------------
persistent_help_text : bool, optional
Help text will be persistently displayed if True, else it will only be
visible when the field is active.
Defaults to True.
is_discrete : bool, optional
Slider will have discrete steps in its interface if True, else its
interface will be continuous.
Defaults to False.
display_markers : bool, optional
Value markers appear on slider interaction if True.
Defaults to False.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> slider = forms.IntegerField(
>>> label='Slider',
>>> ### help_text in sliders are persistent by default
>>> help_text='Even Number with Markers',
>>> required=False,
>>> min_value=0,
>>> max_value=10,
>>> initial=6,
>>> widget=MaterialSliderInput(
>>> is_discrete=True,
>>> display_markers=True,
>>> attrs={
>>> 'step': '2',
>>> },
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_slider.html'
def __init__(self,
display_markers=False,
is_discrete=False,
persistent_help_text=True,
*args, **kwargs):
super().__init__(*args, **kwargs)
self.display_markers = display_markers
self.is_discrete = is_discrete
self.persistent_help_text = persistent_help_text
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget'].update({
'display_markers': self.display_markers,
'is_discrete': self.is_discrete,
'persistent_help_text': self.persistent_help_text,
})
return context
class Media: # pylint: disable=missing-docstring
js = ('material_widgets/js/material_slider.js',)
[docs]class MaterialSplitDateTimeWidget(MaterialMultiWidget):
"""Material SplitDateTimeWidget
Splits datetime input into two MaterialTextFields.
Default widget for SplitDateTimeField under MaterialForm.
Parameters
----------
label : str, optional
Displayed to the left of the widget fields in the template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
Other Parameters
----------------
attrs : dict, optional
HTML attributes to be included in the multiwidget's template.
date_format : str, optional
Format to display initial field value. Defaults to first format found
in Django's DATE_INPUT_FORMATS.
time_format : str, optional
Format to display initial field value. Defaults to first format found
in Django's TIME_INPUT_FORMATS.
label : str, optional
Passes value through from field parameter.
date_label : str, optional
Displayed on the Date widget's field in the template.
Defaults to 'Date'.
date_help_text : str, optional
Displayed under the Date input field.
Defaults to 'YYYY-MM-DD'.
date_persistent_help_text : bool, optional
Help text for Date widget will be persistently displayed if True, else
it will only be visible when the field is active.
Defaults to False.
time_label : str, optional
Displayed on the Time widget's field in the template.
Defaults to 'Time'.
time_help_text : str, optional
Displayed under the Time input field.
Defaults to 'HH:MM:SS'.
time_persistent_help_text : bool, optional
Help text for Time widget will be persistently displayed if True, else
it will only be visible when the field is active.
Defaults to False.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> split_datetime = forms.SplitDateTimeField(
>>> required=False,
>>> widget=MaterialSplitDateTimeWidget(
>>> date_label='Birthdate',
>>> date_help_text='YYYY-MM-DD',
>>> date_persistent_help_text=True,
>>> time_label='Birthtime',
>>> time_help_text='HH:MM:SS',
>>> time_persistent_help_text=True,
>>> ),
>>> )
"""
supports_microseconds = False
template_name = 'material_widgets/widgets/material_splitdatetime.html'
def __init__(self,
attrs=None, date_format=None, time_format=None,
label=None,
date_label='Date', date_help_text='YYYY-MM-DD',
date_persistent_help_text=False,
time_label='Time', time_help_text='HH:MM:SS',
time_persistent_help_text=False,
*args, **kwargs):
multiwidgets = (
MaterialDateInput(
label=date_label,
help_text=date_help_text,
persistent_help_text=date_persistent_help_text,
attrs=attrs,
format=date_format,
),
MaterialTimeInput(
label=time_label,
help_text=time_help_text,
persistent_help_text=time_persistent_help_text,
attrs=attrs,
format=time_format,
),
)
super().__init__(multiwidgets, attrs, label, *args, **kwargs)
def decompress(self, value):
if value:
value = utils.to_current_timezone(value)
return [value.date(), value.time().replace(microsecond=0)]
return [None, None]
[docs]class MaterialSplitHiddenDateTimeWidget(MaterialSplitDateTimeWidget):
"""Material SplitHiddenDateTimeWidget
Parameters
----------
initial : list or tuple of str, optional
Initial values to populate the hidden date and time fields in the form.
Examples
--------
>>> split_hidden_datetime = forms.SplitDateTimeField(
>>> initial=['1965-08-09', '12:00:00'],
>>> widget=widgets.SplitHiddenDateTimeWidget(),
>>> )
"""
input_type = 'hidden'
template_name = 'material_widgets/widgets/material_splithiddendatetime.html'
def __init__(self, attrs=None, date_format=None, time_format=None):
super().__init__(attrs, date_format, time_format, label='',
date_label='', date_help_text='',
date_persistent_help_text=False,
time_label='', time_help_text='',
time_persistent_help_text=False,
)
for widget in self.widgets:
widget.__class__ = MaterialHiddenInput
widget.input_type = 'hidden'
[docs]class MaterialSwitchInput(MaterialComponent, widgets.CheckboxInput):
"""Material SwitchInput
Parameters
----------
label : str, optional
Displayed to the left of the switch.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed as a tool tip on the label.
Examples
--------
Declare widget explicity in a BooleanField to use widget.
>>> boolean_switch = forms.BooleanField(
>>> label='Boolean Switch',
>>> help_text='This is a switch',
>>> widget=MaterialSwitchInput(),
>>> )
"""
template_name = 'material_widgets/widgets/material_switch.html'
class Media: # pylint: disable=missing-docstring
css = {
'all': (
'material_widgets/css/material_switch.css',
)
}
[docs]class MaterialTextarea(MaterialTextField, widgets.Textarea):
"""Material Textarea
Parameters
----------
label : str, optional
Displayed in the widget's field to the upper left of the box in the
template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed under the input field.
Other Parameters
----------------
persistent_help_text : bool, optional
Help text will be persistently displayed if True, else it will only be
visible when the field is active.
Defaults to False.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> textarea = forms.CharField(
>>> label="Captain's Log",
>>> help_text='Write as much as you want',
>>> widget=widgets.MaterialTextarea(
>>> persistent_help_text=True,
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_textarea.html'
[docs]class MaterialTextInput(MaterialTextField, widgets.TextInput):
"""Material TextInput
Default widget for CharField and various other fields under MaterialForm.
Parameters
----------
label : str, optional
Displayed on the widget's field in the template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed under the input field.
Other Parameters
----------------
persistent_help_text : bool, optional
Help text will be persistently displayed if True, else it will only be
visible when the field is active.
Defaults to False.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> username = forms.CharField(
>>> label='Choose a Username',
>>> min_length=3,
>>> max_length=32,
>>> help_text='3-32 characters required',
>>> widget=widgets.TextInput(
>>> persistent_help_text=True,
>>> attrs={
>>> 'autofocus': "autofocus",
>>> },
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_text.html'
[docs]class MaterialTimeInput(MaterialTextField, widgets.TimeInput):
"""Material TimeInput
Parameters
----------
label : str, optional
Displayed on the widget's field in the template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed under the input field.
Other Parameters
----------------
persistent_help_text : bool, optional
Help text will be persistently displayed if True, else it will only be
visible when the field is active.
Defaults to False.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> time_field = forms.TimeField(
>>> label='Time',
>>> help_text='HH:MM:SS',
>>> widget=MaterialTimeInput(
>>> persistent_help_text=True,
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_time.html'
[docs]class MaterialURLInput(MaterialTextField, widgets.URLInput):
"""Material URLInput
Default widget for URLField under MaterialForm.
Parameters
----------
label : str, optional
Displayed on the widget's field in the template.
Defaults to widget's capitalized field name with underscores converted
to spaces.
help_text : str, optional
Displayed under the input field.
Other Parameters
----------------
persistent_help_text : bool, optional
Help text will be persistently displayed if True, else it will only be
visible when the field is active.
Defaults to False.
Examples
--------
Declare widget explicity to use custom widget parameters.
>>> url = forms.URLField(
>>> label='URL',
>>> help_text='Website',
>>> initial='http://github.com/ooknosi',
>>> widget=widgets.MaterialTextInput(
>>> persistent_help_text=True,
>>> ),
>>> )
"""
template_name = 'material_widgets/widgets/material_url.html'