Skip to content

Commit

Permalink
feat: Added support for disabled attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
lucbelliveau committed Dec 13, 2022
1 parent 44d15c7 commit c29bde1
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 44 deletions.
6 changes: 6 additions & 0 deletions autocomplete/autocomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ class Meta:
# If set to True the HTML control will be marked as required
required = False

# If true the component will be disabled
disabled = False

# If enabled an indicator will be displayed while waiting for a network response
indicator = False

Expand Down Expand Up @@ -462,6 +465,7 @@ def put(self, request, method):
"name": self.name,
"search": "",
"indicator": self.indicator,
"placeholder": self.placeholder,
"required": self.required,
"no_result_text": self.no_result_text,
"narrow_search_text": self.narrow_search_text,
Expand Down Expand Up @@ -530,6 +534,7 @@ def get(self, request, method):
template.render(
{
"name": self.name,
"disabled": self.disabled,
"required": self.required,
"indicator": self.indicator,
"route_name": self.get_route_name(),
Expand Down Expand Up @@ -561,6 +566,7 @@ def get(self, request, method):
{
"name": self.name,
"required": self.required,
"placeholder": self.placeholder,
"indicator": self.indicator,
"no_result_text": self.no_result_text,
"narrow_search_text": self.narrow_search_text,
Expand Down
10 changes: 10 additions & 0 deletions autocomplete/static/autocomplete/css/autocomplete.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
appearance: none;
}

.phac_aspc_form_autocomplete.disabled {
background-color: #e9ecef;
padding: .375rem 0.75rem 0.375rem 0.75rem;
background-image: none;
}

.phac_aspc_form_autocomplete:focus {
border-color: #86b7fe;
outline: 0;
Expand Down Expand Up @@ -69,6 +75,10 @@
cursor: default;
}

.phac_aspc_form_autocomplete.disabled ul.ac_container li.chip {
padding: 3px 5px 3px 5px;
}

.phac_aspc_form_autocomplete ul.ac_container li.chip span {
word-wrap: break-word;
}
Expand Down
2 changes: 2 additions & 0 deletions autocomplete/templates/autocomplete/chip.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

<li class="chip">
<span>{{ item.label }}</span>
{% if not disabled %}
<a
hx-put="{% url route_name method='toggle' %}"
hx-params="{{ route_name }},item,remove"
Expand All @@ -18,4 +19,5 @@
</path>
</svg>
</a>
{% endif %}
</li>
2 changes: 1 addition & 1 deletion autocomplete/templates/autocomplete/component.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{% endcomment %}
<div
id="{{ route_name }}__container"
class="phac_aspc_form_autocomplete"
class="phac_aspc_form_autocomplete {{ disabled|yesno:'disabled,' }}"
>
{# Hidden input elements used to maintain the component's state #}
{# and used when submitting forms #}
Expand Down
9 changes: 7 additions & 2 deletions autocomplete/templates/autocomplete/textinput.html
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
{% if not disabled or not multiselect %}
<input
class="textinput"
id="{{ route_name }}__textinput"
{% if disabled %}
disabled
{% endif %}
onkeydown="return phac_aspc_autocomplete_keydown_handler(event)"
name="search"
class="form-control col-auto"
onblur="return phac_aspc_autocomplete_blur_handler(event, '{{ route_name }}', {{ multiselect|yesno:'false,true' }})"
{% if placeholder %}
{% if placeholder and selected_items|length == 0 %}
placeholder="{{ placeholder }}"
{% endif %}
type="search"
{% if not multiselect and selected_items|length == 1 %}
value="{{ selected_items.0.label }}"
{% endif %}
{% if selected_items|length == 0 %}
{% if required and selected_items|length == 0 %}
required
{% endif %}
hx-get="{% url route_name method='items' %}"
Expand All @@ -27,3 +31,4 @@
hx-indicator="#{{ route_name }}__container .htmx-indicator"
{% endif %}
>
{% endif %}
84 changes: 43 additions & 41 deletions autocomplete/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@

from .autocomplete import HTMXAutoComplete


class Autocomplete(Widget):
"""
Django forms compatible autocomplete widget
Parameters:
name (str): The name of the component (must be unique)
name (str): The name of the component (must be unique)
attrs (dict): disabled and required attributes are supported
options (dict): See [autocomplete.py](../autocomplete.py) for more info
label (str)
required (bool) Defaults to false
indicator (bool) Defaults to false
placeholder (str)
no_result_text (str) Defaults to "No results found."
Expand All @@ -30,8 +31,8 @@ class Autocomplete(Widget):
get_items (func)
"""
template_name = 'autocomplete/component.html'

template_name = "autocomplete/component.html"

def __init__(
self,
Expand All @@ -43,75 +44,76 @@ def __init__(

super().__init__(attrs)
config = {
'name': name,
'required': opts.get('required', None),
'indicator': opts.get('indicator', None),
'route_name': opts.get('route_name', None),
'label': opts.get('label', None),
'placeholder': opts.get('placeholder', None),
'no_result_text': opts.get('no_result_text', 'No results found.'),
'narrow_search_text': opts.get(
'narrow_search_text',
'Narrow your search for more results.'
"name": name,
"disabled": attrs.get("disabled", False) if attrs else False,
"required": attrs.get("required", False) if attrs else False,
"indicator": opts.get("indicator", None),
"route_name": opts.get("route_name", None),
"label": opts.get("label", None),
"placeholder": opts.get("placeholder", None),
"no_result_text": opts.get("no_result_text", "No results found."),
"narrow_search_text": opts.get(
"narrow_search_text", "Narrow your search for more results."
),
'max_results': opts.get('max_results', None),
'minimum_search_length' : opts.get('minimum_search_length', 3),
'multiselect': opts.get('multiselect', False),
"max_results": opts.get("max_results", None),
"minimum_search_length": opts.get("minimum_search_length", 3),
"multiselect": opts.get("multiselect", False),
}

if model := opts.get('model', None):
if model := opts.get("model", None):
mdl_config = {"model": model}
if item_value := opts.get('item_value', None):
if item_value := opts.get("item_value", None):
mdl_config["item_value"] = item_value
if item_label := opts.get('item_label', None):
if item_label := opts.get("item_label", None):
mdl_config["item_label"] = item_label
if lookup := opts.get('lookup', None):
if lookup := opts.get("lookup", None):
mdl_config["lookup"] = lookup

config["Meta"] = type("Meta", (object,), mdl_config)
else:
config["get_items"] = opts.get(
'get_items', HTMXAutoComplete.get_items
)
config["get_items"] = opts.get("get_items", HTMXAutoComplete.get_items)

self.a_c = type(f"HtmxAc__{name}", (HTMXAutoComplete,), config)


def value_from_datadict(self, data, files, name):
try:
getter = data.getlist
except AttributeError:
getter = data.get
return getter(name)


def value_omitted_from_data(self, data, files, name):
# An unselected <select multiple> doesn't appear in POST data, so it's
# never known if the value is actually omitted.
return []


def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)

items_selected = [] if value is None else \
[value] if not isinstance(value, list) else value
items_selected = (
[] if value is None else [value] if not isinstance(value, list) else value
)
selected_options = self.a_c.map_items(
self.a_c,
self.a_c.get_items(
self.a_c,
values=[str(x) for x in items_selected]
)
self.a_c.get_items(self.a_c, values=[str(x) for x in items_selected]),
)

context['name'] = self.a_c.name
context['indicator'] = self.a_c.indicator
context['required'] = self.a_c.required
context['route_name'] = self.a_c.get_route_name()
context['label'] = self.a_c.label
context['placeholder'] = self.a_c.placeholder
context['multiselect'] = self.a_c.multiselect
context['values'] = list(self.a_c.item_values(self.a_c, selected_options))
context['selected_items'] = list(selected_options)
context["name"] = self.a_c.name
context["disabled"] = attrs.get(
"disabled", self.attrs.get("disabled", self.a_c.disabled)
)
context["indicator"] = self.a_c.indicator
context["required"] = attrs.get(
"required", self.attrs.get("required", self.a_c.required)
)
context["route_name"] = self.a_c.get_route_name()
context["label"] = self.a_c.label
context["placeholder"] = self.a_c.placeholder
context["multiselect"] = self.a_c.multiselect
context["values"] = list(self.a_c.item_values(self.a_c, selected_options))
context["selected_items"] = list(selected_options)

self.a_c.required = context["required"]
self.a_c.disabled = context["disabled"]

return context

0 comments on commit c29bde1

Please sign in to comment.